Skip to main content
ElastiCORE 3.0 is a release candidate: 3.0.261022-rc1 on Maven Central. Projects on the 2.2 line: see the 2.2 documentation.
Version: 3.0

The hooks

The hooks run on every edit and every Gradle run, in bash (hooks/elasticore-hook.sh), and answer Claude Code in JSON. They watch the edit tools (Edit, Write, MultiEdit) and commands through Bash and PowerShell, including failed commands, and the moment Claude is about to stop.

WhenWhat happens
Claude uses the edit tools on a generated file - first line //ecd:, no // Initially generated by ElastiCORE; line, no preserved blockThe edit is refused, with where the change belongs instead. Adding one whole preserved block (// !---------- ... // ------------!) goes through
Claude edits a file ElastiCORE rewrites on every generation - blueprint/schema.yml, the copied guides, the default i18n/<domain>-messages.properties, a generated orm.xmlRefused
Claude edits a generated file that has a preserved blockAllowed, with a reminder that only the block survives regeneration
Claude runs Gradle with -Pforce, or sets the property another way (-P force, ORG_GRADLE_PROJECT_force, ...)Refused: it overwrites hand edits. You decide
Claude changes the model under src/main/resources/blueprint/<domain>/Told to run ./gradlew compileJava and fix the warnings before finishing; on a build without the elasticore { } block, also told that the 2.x generator ignores access rules, lifecycles and derived values
A Gradle build - successful or failed - prints DSL validation warnings, a YAML file it could not read, or hand-edited generated filesTold what that means and what to do
Claude is about to finish while no successful build has generated and compiled its DSL changeSent back once to build. A build in the background, cut short or filtered, or one that only generated, does not count

Details that matter​

  • -Pforce is recognised as "-Pforce", -P force, --project-prop force, -Dorg.gradle.project.force and ORG_GRADLE_PROJECT_force=, through & ".\gradlew.bat", cmd /c, bash -c, env or time. The command is read as a shell reads it, so a commit message that mentions -Pforce is not refused.
  • "Compiled" means compiled: generation alone (elcore3, elcore3ts, a build with -x on the compiling tasks) does not end a DSL change, nor does a command in which one Gradle run failed and another passed.
  • The model files are always editable, even when the ElastiCORE wizard wrote # Generated by ElastiCORE Framework at their top.
  • Long output: a successful command's full output is read from the file Claude Code saves it to; of a failed one Claude Code passes on only the first 10,000 characters, so in a very large project the warnings may be past what the hook sees.
  • An edit made another way - a script through a shell - is not stopped; the skill tells Claude not to make one.
  • In opencode the kit's plugin runs the same script: refusals become tool errors, notes are added to the tool's output, and an idle session with an uncompiled DSL change is sent back once.

Switching them off for a project​

.claude/settings.local.json
{ "env": { "ELASTICORE_ALLOW_GENERATED_EDITS": "1" } }

ELASTICORE_ALLOW_FORCE=1 allows -Pforce. On Windows the hooks need Git for Windows (Git Bash); without it, install it or disable the plugin.

The hook script is tested on Linux, macOS (also its own /bin/bash 3.2) and Windows on every change, with the JSON Claude Code really sends (tests/hooks/test-hooks.sh).