Repository navigation
fix(skill): keep the §0 bootstrap intact when Claude Code loads the skill with arguments - #642
Merged
Merged
Conversation
…kill with arguments
Claude Code rewrites a skill body that it loads with arguments: every bare
$1, $2 ... becomes the matching word of the request (0-based). Nine lines of
the agent preamble used bare positional parameters, so an agent that pasted
the §0 bootstrap (every session without a pre-seeded preamble: Agent Skill
injection off, the default, or a remote or Docker session) wrote helpers
that queried session "5" with timeout=claude and looked up "5=" instead of
CODEMAN_PASSWORD= in the .env, sending every call unauthenticated.
- $1/$2 -> ${1}/${2} in preamble.sh and the identical §0 heredoc in
SKILL.md; same bash, and Claude Code's pattern (\$\d+(?!\w)) skips it.
- CODEMAN_PREAMBLE 1.33.4 -> 1.41.2, so a cache file written by the broken
bootstrap (which carries the old stamp) is rewritten on the next run.
- A comment at the top of the preamble states the rule.
- New test: Claude Code 2.1.300's substitution applied to SKILL.md with
sample arguments must leave the body, and the pasted heredoc, unchanged.
It fails on the old file with the exact sessions/5 and timeout=claude lines.
- plugins/codeman re-synced; patch changeset.
Verified with real Claude Code 2.1.300 (the delivered heredoc is now
byte-identical to preamble.sh; before, 9 lines differed) and by running the
pasted helpers against a local recording server.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Ark0N
pushed a commit
that referenced
this pull request
Oct 16, 2026
#642 stamped the preamble with the version it expected to ship in, 1.41.2. The release is 1.42.0, so the stamp would name a version that never existed. Any new value makes cached preambles from the broken version stale, which is what the stamp is for. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this fixes
The codeman skill (
skills/codeman/SKILL.md) starts with a §0 bootstrap: a bash block that writes the skill's helper functions (the same code asskills/codeman/preamble.sh) into a per-session cache file, which every later step sources. Codeman pre-seeds that file at spawn only when Agent Skill injection is on (off by default) and only for local sessions. Everywhere else (plugin andcodeman skill installusers with injection off, and every remote or Docker session) the agent pastes the §0 block from the skill text it was given.Claude Code rewrites that text when it loads a skill with arguments, which is what happens when the model calls the Skill tool with the user's request: every bare
$1,$2, ... becomes the matching argument word (0-based). Nine lines of the preamble used bare positional parameters. For a request like "use codeman to start 5 claude workers, one per TODO item" (argumentsstart 5 claude workers, ...), the pasted helpers therefore:5instead of the worker (sessions/5/wait-output,sessions/5/terminal,sessions/5/last-response),timeout=claude,5=instead ofCODEMAN_PASSWORD=in the data dir's.env, so on a password-protected server every call went out unauthenticated.This showed up in three real lead transcripts during recorded demo runs.
What changed
$1/$2become${1}/${2}in those nine lines, in both copies (preamble.shand the §0 heredoc inSKILL.md, which an existing test keeps byte-identical). It is the same bash; Claude Code's pattern (\$\d+(?!\w)) does not match the braced form, and no other CLI substitutes anything.CODEMAN_PREAMBLE1.33.4 to 1.41.2. The §0 block rewrites the cache file only when its stamp differs, so without the bump a file already written by the broken bootstrap would be kept and reused.plugins/codeman/re-synced withnode scripts/sync-plugin.mjs(byte copy).test/agent-skill.test.ts: applies Claude Code's substitution rules (as of 2.1.300) toSKILL.mdwith sample arguments and requires the body to come back unchanged and the pasted heredoc to equalpreamble.sh. On master it fails with exactly thesessions/5/wait-outputandtimeout=claudelines.No server, API or UI code changes.
How it was verified
Real Claude Code 2.1.300. A throwaway copy of each
SKILL.mdwas invoked as/<skill> start 5 claude workers, one per TODO item(allCODEMAN_*variables stripped so the skill refuses to act, Haiku, no tools), and the transcript shows what Claude Code actually delivered:preamble.shsessions/5/...,timeout=claude,5=)The pasted helpers, run. The delivered heredoc was sourced in a clean shell against a local server that records each request, then every changed helper was called for worker
worker-sid-42with a 30 s timeout, with the password only in the data dir's.env:sessions/5/...,timeout=claudesessions/worker-sid-42/...,timeout=30000Plus
npm test,npm run typecheckandnpm run check:plugin.Reviewing the diff
Most of the diff is mechanical: 18
$Nto${N}edits (9 per copy), 9 stamp lines, and theplugins/codeman/mirror, a byte copy ofskills/codeman/. The parts worth reading are the new test (about 25 lines) and the comment at the top ofpreamble.sh.Risk
${1}), the test would not notice; the real-CLI check above is the evidence for today's behaviour.