Skip to content

fix(skill): keep the §0 bootstrap intact when Claude Code loads the skill with arguments - #642

Merged
Ark0N merged 1 commit into
masterfrom
fix/skill-positional-args
Oct 16, 2026
Merged

Ark0N merged 1 commit into
masterfrom
fix/skill-positional-args

Conversation

@Ark0N

@Ark0N Ark0N commented Oct 15, 2026

Copy link
Copy Markdown
Owner

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 as skills/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 and codeman skill install users 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" (arguments start 5 claude workers, ...), the pasted helpers therefore:

  • asked about session 5 instead of the worker (sessions/5/wait-output, sessions/5/terminal, sessions/5/last-response),
  • waited with timeout=claude,
  • and looked up 5= instead of CODEMAN_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 / $2 become ${1} / ${2} in those nine lines, in both copies (preamble.sh and the §0 heredoc in SKILL.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.
  • Preamble stamp CODEMAN_PREAMBLE 1.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.
  • A three-line comment at the top of the preamble stating the rule.
  • plugins/codeman/ re-synced with node scripts/sync-plugin.mjs (byte copy).
  • New test in test/agent-skill.test.ts: applies Claude Code's substitution rules (as of 2.1.300) to SKILL.md with sample arguments and requires the body to come back unchanged and the pasted heredoc to equal preamble.sh. On master it fails with exactly the sessions/5/wait-output and timeout=claude lines.
  • A patch changeset.

No server, API or UI code changes.

How it was verified

Real Claude Code 2.1.300. A throwaway copy of each SKILL.md was invoked as /<skill> start 5 claude workers, one per TODO item (all CODEMAN_* variables stripped so the skill refuses to act, Haiku, no tools), and the transcript shows what Claude Code actually delivered:

§0 heredoc as delivered vs preamble.sh
master 9 lines differ (sessions/5/..., timeout=claude, 5=)
this branch byte-identical

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-42 with a 30 s timeout, with the password only in the data dir's .env:

requests credentials
master sessions/5/..., timeout=claude none (password not found)
this branch sessions/worker-sid-42/..., timeout=30000 sent

Plus npm test, npm run typecheck and npm run check:plugin.

Reviewing the diff

Most of the diff is mechanical: 18 $N to ${N} edits (9 per copy), 9 stamp lines, and the plugins/codeman/ mirror, a byte copy of skills/codeman/. The parts worth reading are the new test (about 25 lines) and the comment at the top of preamble.sh.

Risk

  • The stamp bump makes each running agent session re-run the full §0 block once, because its short loader now reports the cached preamble as stale. That is the existing upgrade path and costs one extra Bash call.
  • The test reimplements Claude Code's substitution rules as read from 2.1.300. If a later Claude Code widens the pattern (say, to braced ${1}), the test would not notice; the real-CLI check above is the evidence for today's behaviour.

…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
Ark0N merged commit 16bbd26 into master Oct 16, 2026
2 checks passed
Ark0N pushed a commit that referenced this pull request Oct 16, 2026
One release changeset for #616, #617, #621, #622, #623, #624, #625, #628, #632, #634, #635 and #642. The changesets carried by #616, #617 and #642 are folded into it and removed (#617's was minor; 1.41.2 is a patch release).

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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants