The user wants to set the pi-delegate mode to: $ARGUMENTS
If $ARGUMENTS is empty, just show the current mode and policy — do nothing else:
node "${CLAUDE_PLUGIN_ROOT}/bin/pi-mode"
For off and soft, set it directly and report the result:
node "${CLAUDE_PLUGIN_ROOT}/bin/pi-mode" <mode>
What the three modes do:
off— no intervention at all. Suitable for a project pi should never touch.soft— nudges when existing product code is touched (default).strict— blocks edits to existing product code, requiringpi_dispatchinstead.
The mode is recorded against the project root and applied by the project root of the file being edited — not by wherever the session happens to be sitting. A strict project therefore stays strict when edited from another project's session, or from one of its own subdirectories. Report the path the command prints, so the user can see which project the mode actually landed on.
Setting strict: survey the project first
strict is the mode with teeth, so it needs to know what to protect. Without a policy it
falls back to a built-in heuristic that only fits one shape — a src/ directory holding
.ts/.tsx/.js/.jsx/.mjs/.svelte/.py. On a Go, Rust, Elixir, or <pkg>/-layout Python
tree that heuristic protects nothing, and says nothing about it: strict looks switched
on and is not. So work out this project's real answer.
1. Look at the project. Read the manifest (go.mod, Cargo.toml, pyproject.toml,
package.json, mix.exs, …) and list the top-level directories. You are answering one
question: which paths hold product source that should be written by pi rather than by
hand?
Typical answers, as a starting point and not a lookup table — the repo in front of you wins over any of them:
| Layout | Usually protect |
|---|---|
| Node / TS | src/** |
| Go | internal/**, cmd/**, pkg/** |
| Rust | src/**/*.rs |
Python (<pkg>/) |
<pkg>/**/*.py |
| Elixir | lib/** |
Test files are NOT an exception. Do not put them in allow. They are the single
biggest block of characters in most repos, and dispatching them is the plugin's whole
premise — the skill's rule is structural, not about judgment: is this a character that
gets committed? then pi writes it, implementation and tests alike. Exempting tests looks
reasonable ("the test is the spec, so I should write it") and quietly hands the largest
share of the work straight back to hand-typing. That is the exact leak strict exists to
close: with a written rule in place and no enforcement, roughly 80% of committed characters
were still typed by the main model.
If you want to fix the contract before pi implements it, the contract belongs in the task
book — prose you write, not a file that gets committed. And an acceptance test you run
yourself to judge the result is fine too, because it is a throwaway you never commit. What
must not happen is src/**/*.test.ts landing in the repo from your own keyboard.
allow is for things that are not hand-written source at all: generated code
(*.pb.go, *_pb2.py, src/generated/**, snapshots) and framework shells that are
config in disguise (src/app.d.ts, src/app.html, a vitest setup file). Getting these
wrong is what makes someone switch strict off entirely rather than live with it.
Paths that are simply outside the protected globs — docs, config, migrations you hand-write
by policy, fixtures, scripts — need no allow entry at all; they were never protected.
2. Show the user what you propose, and why. List the protect globs and the allow
globs, each with a one-line reason, and name anything you were unsure about. Patterns are
relative to the project root; ** crosses directories, * stays inside one segment, and
a trailing / means "everything under here".
3. Wait for their answer. Do not write the policy until they approve it. They may edit the lists — use what they say, not what you proposed. This confirmation step is the whole reason this is safe: otherwise the model editing the files is also the model deciding which files it may edit.
4. Then write it:
node "${CLAUDE_PLUGIN_ROOT}/bin/pi-mode" strict \
--protect "internal/**,cmd/**/*.go" \
--allow "**/*.pb.go"
Note what is and is not in that --allow: generated protobuf output, yes; **/*_test.go,
no. Tests are dispatched like everything else.
--allow is optional — a policy with no exceptions at all is the common case, and the
better default. Omit it unless the repo actually contains generated or shell files inside
the protected globs.
The command prints back the mode, the project root, and the stored policy. Show that output — it is the user's confirmation that what landed is what they agreed to.
To go back to the built-in heuristic, --clear-policy removes the stored lists. The policy
survives a switch to soft and back, so nobody has to redo the survey to toggle the mode.
Correcting a policy already in place: re-run the command with the full lists you want.
--protect and --allow replace what is stored, they do not merge — so to drop an
exception, simply re-issue the policy without it (--allow "" clears the exceptions while
keeping the protected globs).
What strict can and cannot do
Be straight with the user about the limits, at least the first time they enable it:
- Only
WriteandEditare intercepted. The same edit made throughBash(sed -i, a heredoc,python - <<EOF) is never seen. This is a discipline rail against your own habit, not a security boundary. - Brand-new files are always allowed, whatever the policy says — writing a file from scratch is the shape pi is best at.
/pi-delegate:probegrants a one-time bypass for a deliberate hand-edit.- The policy lives in
~/.claude/pi-delegate/modes.json, outside any project, so it is not protected by the guard it configures.