Skill tool's args does bash-style $N positional substitution against SKILL.md prose, corrupting literal dollar amounts

Status Open
Maintainer reply None cached
Activity 0 comments · opened Jul 21, 2026

Summary

Calling the Skill tool with a non-empty args string causes the harness to scan the rendered SKILL.md body for bare $<digit> tokens and replace each with the Nth (0-indexed) whitespace-split word of args — bash/slash-command-style positional-parameter substitution applied to skill markdown that was never authored with placeholders in mind. This silently corrupts any literal dollar amount in the skill body whose $N happens to land on a valid word index.

Repro

Skill file contains (among other rows):

| AR balance >$5,000 and 30+ days | ...
| AR balance >$0 and 90+ days | ...
| Rent PSF below $2.50/SF (under-market flag) | ...

Invocation:

Skill({ skill: "exception-detection", args: "Targeted scan: Category 2 tenant exceptions only, with emphasis on verifying the 3 new v1.2 cross-checks against HubSpot Lease Renewal deals..." })

args split on whitespace: index 0 = "Targeted", index 2 = "Category", index 5 = "exceptions".

Content actually returned to the model:

| AR balance >exceptions,000 and 30+ days | ...
| AR balance >Targeted and 90+ days | ...
| Rent PSF below Category.50/SF (under-market flag) | ...

$5,000 → "exceptions,000" (word[5]), $0 → "Targeted" (word[0]), $2.50 → "Category.50" (word[2]) — an exact 0-indexed match.

Re-tested with a controlled args string (args="zero one two three four five six seven") against the same skill file: same three tokens corrupted predictably ($5,000→"five,000", $0→"zero", $2.50→"two.50"). But a separate literal $5K/mo elsewhere in the same file was left untouched — the substitution appears to require the digit run be followed by a non-letter (space/comma/period qualify, a letter does not), i.e. there's a partial guard against clobbering shorthand like $5K/$10M, but it does not protect comma-formatted or decimal dollar amounts, which is extremely common in ordinary prose.

Skills whose body has no $<digit> substring are unaffected — in that case args is instead appended verbatim as a trailing ARGUMENTS: <raw string> line, which is the correct/safe behavior.

Why this matters

Any skill whose Markdown body contains dollar figures (financial thresholds, pricing, cost references — extremely common in real-world skill content) is silently corrupted in-context whenever it's invoked with an args string whose word count happens to land on those $N positions. The corruption is invisible unless someone happens to diff the rendered output against the source file — the model just silently reasons over garbage numbers.

Suggested fix

args should never be pattern-matched/interpolated against the skill body at all — it should always be passed through as inert appended context (exactly what already happens today for skills with no $N matches). If per-skill templating is intentionally wanted in the future, it should require an explicit, collision-proof placeholder syntax (e.g. {{arg1}} / {{ARGUMENTS}}) that skill authors opt into via frontmatter, rather than a blanket regex applied to every skill's prose regardless of whether the author ever intended a placeholder.

Environment

  • @anthropic-ai/claude-code v2.1.214
  • Windows 11 (win32-x64 build)

View original on GitHub ↗