Claude Code's Bash tool escapes every ! to \! at the transport layer (anthropics/claude-code#61121, a regression of a 2.1.87 fix), corrupting Python !=, jq, Jira JQL, branch names, etc. Documents the single robust workaround: write !-containing code to .tmp/ via the Write tool and run the file; .tmp/ is prompt-free via the Edit(.tmp/**) allow rule. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
95 lines
5.4 KiB
Markdown
95 lines
5.4 KiB
Markdown
# Personal preferences
|
|
|
|
## Workflow
|
|
|
|
- **Specs, plans, and design docs go into the repo they describe.** When
|
|
brainstorming, plan-mode, or any flow produces a persistent document
|
|
(design spec, implementation plan, ADR, etc.), the final destination is
|
|
always the working tree of the project it belongs to — typically
|
|
under `docs/` (or wherever the project conventionally keeps them).
|
|
Scratch locations like `~/.claude/plans/<slug>.md` are fine *during*
|
|
plan mode, but the moment plan mode exits with an approved artifact,
|
|
copy it into the repo and `git commit` it. Don't ship a feature
|
|
while its spec or plan still lives only in a scratch file. If a
|
|
project's `AGENTS.md` / `CLAUDE.md` specifies a different directory,
|
|
that wins.
|
|
|
|
## Code health
|
|
|
|
Strategic over tactical, with follow-through. Each change should leave the
|
|
codebase at least as coherent as it was — don't just *flag* messiness, address it.
|
|
|
|
- **Default to acting on cleanups, not just mentioning them.** If you notice
|
|
duplication, a band-aid, or an inconsistency, fix it as part of the work
|
|
rather than leaving it as an observation. "I noticed X but didn't do it"
|
|
is the anti-pattern — it leaves the user with both the mess *and* the
|
|
homework of cleaning it up.
|
|
- **Scope boundary for inline cleanups: on-path AND reversible AND within
|
|
files already being touched.** Inline when all three hold. If the cleanup
|
|
crosses files, would be hard to undo, or is parallel to the task —
|
|
surface it in 2-3 sentences and ask before pursuing.
|
|
- **Tolerate duplication until the pattern is real.** Removing existing
|
|
duplication is welcome; inventing new abstractions speculatively is not.
|
|
Rule of three: don't unify two instances, wait for the third.
|
|
- **Treat friction as architectural data.** Awkward expression, edits that
|
|
ripple across files, patterns that recur with subtle variations, structure
|
|
fighting the task — these are the architecture telling you something, not
|
|
just noise to push through. Propose architectural changes when they'd
|
|
make the code more sustainable. Frame as proposals with a clear ask
|
|
("now, or follow-up?"), not silent rewrites.
|
|
|
|
## Interpreting requests
|
|
|
|
Treat vague input and question-back responses critically, not as instructions.
|
|
|
|
- **Vagueness is not authorization.** "Make it cleaner", "maybe X?",
|
|
"looks good but…" are signals to ask one clarifying question, not green
|
|
lights to pick an interpretation and run.
|
|
- **A tangent is not an answer.** If you ask "A or B?" and get "maybe X?",
|
|
that's a new question, not a pick. Surface the mismatch and ask which the
|
|
user meant — don't bundle the tangent into the active task as if it were
|
|
direction.
|
|
|
|
## Sandbox
|
|
|
|
- **Always run commands sandboxed first.** Only use `dangerouslyDisableSandbox: true` as a last resort after a sandbox-related failure — never preemptively.
|
|
- **On sandbox failure:** analyze the error message for the blocked path, then suggest adding it to `sandbox.filesystem.allowWrite` in `~/.claude/settings.json`. Only fall back to `dangerouslyDisableSandbox: true` if the path can't be determined or the user prefers it.
|
|
|
|
## Temporary Files
|
|
|
|
- **Use `$TMPDIR` or `.tmp/` (project-local) for temporary files.** Never use `/tmp` directly or paths like `/Library/Application Support` — those are not in the sandbox allowlist and trigger permission prompts.
|
|
- `$TMPDIR` is set by the sandbox to a writable path. `.tmp/` inside the project directory is always writable without prompts.
|
|
- Ensure `.tmp/` is listed in `.gitignore` when creating temp files in a tracked repo.
|
|
|
|
## Shell — literal `!` in Bash commands
|
|
|
|
- **Never put a literal `!` in an inline Bash command.** Claude Code's Bash tool
|
|
escapes every `!` to `\!` at the transport layer before any shell sees it —
|
|
even inside single/double quotes and in non-interactive shells
|
|
(anthropics/claude-code#61121, a regression of a fix shipped in 2.1.87). The
|
|
stray backslash corrupts downstream tools: Python `!=` → SyntaxWarning + broken
|
|
string, jq, Jira JQL (`status != Done` → server 400), branch names, etc.
|
|
`bash -c '...'` and quoted heredocs do **not** avoid it.
|
|
- **One rule covers everything:** if a command needs a `!`, write the command/code
|
|
to a file with the Write tool under `.tmp/` and run the file. `.tmp/` is
|
|
prompt-free via the `Edit(.tmp/**)` allow rule in `~/.claude/settings.json`. Do
|
|
**not** create the file with inline `printf`/`echo >` — that re-escapes the `!`.
|
|
- Need a literal `!` inline anyway: ANSI-C hex `$'\x21'` (e.g. `B=$'\x21'; cmd
|
|
"${B}=…"`) yields a real `!` with no literal `!` in the command.
|
|
|
|
## Tooling
|
|
|
|
- **`ccc` for semantic code search.** Repos containing a
|
|
`.cocoindex_code/` directory are indexed by
|
|
[`ccc`](https://github.com/cocoindex-io/cocoindex-code). Reach for
|
|
it on conceptual questions ("where is X used / which files do Y /
|
|
what handles Z"), where a keyword grep would miss indirect usage:
|
|
`ccc search '<concept>' --path '**'`. Pass `--path '**'` — without
|
|
it, results are filtered to the current working directory's
|
|
subtree. The `ccc` skill has the full reference;
|
|
`grep`/`rg`/`find` remain fine for exact-string lookups.
|
|
- **`ccc index` / `ccc init` are read-safe maintenance.** They only
|
|
write to the gitignored `.cocoindex_code/` directory, never to
|
|
source. Run them without confirmation prompts — at session start,
|
|
after refactors, or before a search when the index may be stale.
|
|
Treat like `grep` for permission purposes, not like a code edit.
|