Runtime Preflight breaks in Claude Code when the topic has 2+ words (`$1` in SKILL.md is replaced by skill arguments)
What happens
Claude Code substitutes positional placeholders in a skill's SKILL.md before the model reads it. When the skill is invoked with arguments, $0, $1, $2, ... become the argument words, counting from zero. This includes text inside fenced code blocks.
/last30days always receives the topic as arguments, so the Runtime Preflight in v3.25.0 (084662b) reaches the model already rewritten. With /last30days AI video tools:
# SKILL.md on disk
try_last30days_python() {
candidate="$1"
# what the model receives
try_last30days_python() {
candidate="video"windows_path_to_unix gets the same treatment (path="$1"). Every interpreter candidate is then tested as the literal word "video", so the preflight fails to resolve Python on any topic longer than one word. The awk fallback in the version badge instruction (gsub(/"/,"",$2); print $2) is affected the same way once the topic has three or more words; it only runs when plugin.json is missing.
Repro
- Put a skill in
~/.claude/skills/probe/SKILL.mdwhose body containsrate $0.035and`echo "$1 and $2"`. - Invoke it with the arguments
alpha beta. - The model receives
rate alpha.035and`echo "beta and $2"`.
Indexes with no matching argument are left alone, and nothing changes when the skill is invoked without arguments. That's why a one-word topic works and the bug is easy to miss.
Tested on Claude Code 2.1.281 (desktop) on macOS.
Fix
The braced form means the same thing in POSIX shell, and awk accepts $(n). Claude Code leaves both alone (checked with the probe above):
- candidate="$1"
+ candidate="${1}"- path="$1"
+ path="${1}"-awk '/^version:/{gsub(/"/,"",$2); print $2; exit}'
+awk '/^version:/{gsub(/"/,"",$(2)); print $(2); exit}'With this patch the existing contract and preflight tests pass locally (test_onboarding_contract, test_plugin_contract, test_preflight, test_doc_flag_contract, test_codex_host_contract, test_diagnose_compat, test_env_unsubstituted_template, test_save_dir_env_fallback: 147 passed), and the extracted preflight resolves python3.14.
A plain-text price such as $24K in SKILL.md is technically exposed too, but only when the topic has that many words. \$ also survives substitution if you'd rather escape than brace.
Suggestion
A contract test that fails on (?<!\\)\$\d in SKILL.md would keep this from coming back. I'm happy to open a PR with the fix and the test.
Source: mvanhorn/last30days-skill