Skip to content

Use {platform_home_dir} template for bare script invocations in skill content #607

Description

@williamthorsen

Description

Skill instructions tell agents to invoke helper scripts as bare commands (e.g., Run `resolve-frontmatter.sh --skill orchestrate --interactive false` via Bash), but the scripts are installed under ~/.claude/scripts/ (or ~/.codex/scripts/, etc.) and that directory is not on PATH. Only the launcher set (check-project-staleness.sh, claude.sh, rovo.sh) is symlinked into /usr/local/bin by scripts/install-launchers.sh.

When an agent shells out, the bare command does not resolve. The agent then guesses a path. The _data/ subtree under skills/ is a plausible-but-wrong guess because it does hold shared skill artifacts (artifact-conventions.md, pr-resolution.md, etc.), so the model conflates "shared skill data" with "skill-adjacent scripts." Each wasted attempt costs at least one tool round trip plus permission overhead.

Observed example (orchestrate-dev run):

Bash(/Users/william/.claude/skills/_data/resolve-frontmatter.sh --skill orchestrate --interactive false)
  Error: Exit code 127 — no such file or directory
Bash(/Users/william/.claude/scripts/resolve-frontmatter.sh --skill orchestrate --interactive false)
  ---
  provenance:
    skill: orchestrate
  ...

Context

  • Affected scripts: resolve-frontmatter.sh, describe-change.sh, get-ticket-id.sh, resolve-merge-options.sh, resolve-reviewer-context.sh (everything in packages/agents/content/scripts/ that ends up at ~/{platform_home}/scripts/).
  • 25 sites across skill and subagent files invoke these scripts as bare commands. Sample sites: packages/agents/content/skills/orchestrate/SKILL.md:718, packages/agents/content/subagents/orchestrated-architect.md:118, and the rest under packages/agents/content/skills/**/SKILL.md and packages/agents/content/subagents/*.md.
  • One additional site, packages/agents/content/skills/orchestrate/modules/review-cycle.md:62, invokes resolve-reviewer-context.sh via {repo-root}/packages/agents/content/scripts/... rather than as a bare command. This invocation is broken in the same way (it assumes the user's working repo is the codeassembly repo) and is included in scope.
  • packages/agents/src/lib/path-rewriter.ts already supports a {platform_home_dir} template variable that the install pipeline expands to ~/.claude (or ~/.codex, ~/.opencode, etc.) per target platform.
  • Skill content already uses {platform_home_dir} for cross-platform Markdown link targets; bare-command script invocations bypass it.

Considerations

  • Alternatives considered:
    • Symlink ~/.claude/scripts/*.sh into /usr/local/bin from install-launchers.sh. Rejected: pollutes a global bin directory with names that could collide with user scripts, and only helps after the launcher install has run.
    • Leave as-is. Rejected: drift is observable in every orchestrated run.
  • Cross-platform: the template var already expands per platform, so the change works for .claude, .codex, .opencode, etc., without further changes.
  • Backward compatibility: rewriting bare invocations is purely additive. The resulting command is an absolute path that resolves whether or not the script is also on PATH.
  • Out of scope: changing how scripts are installed or distributed; rewriting pure-prose mentions of script names (these are not executed and do not need a prefix).

Proposed solution

Rewrite bare-command script invocations in skill and subagent content to use the existing {platform_home_dir} template variable, so the install-time path rewriter produces an explicit, platform-correct absolute path:

Before:

Run `resolve-frontmatter.sh --skill orchestrate --interactive false` via Bash.

After:

Run `{platform_home_dir}/scripts/resolve-frontmatter.sh --skill orchestrate --interactive false` via Bash.

At install time this becomes Run ~/.claude/scripts/resolve-frontmatter.sh ... on the Claude target, ~/.codex/scripts/... on the Codex target, and so on.

Apply the same treatment to every bare-command invocation of the affected scripts across packages/agents/content/skills/**/*.md and packages/agents/content/subagents/*.md.

For the one related site in packages/agents/content/skills/orchestrate/modules/review-cycle.md:62, replace {repo-root}/packages/agents/content/scripts/resolve-reviewer-context.sh with {platform_home_dir}/scripts/resolve-reviewer-context.sh so it resolves to the installed location instead of the user's working repository.

Add a regression test to catch future drift. The test walks packages/agents/content/skills/**/*.md and packages/agents/content/subagents/*.md and, for each known helper script name, scans for occurrences immediately followed by CLI-argument syntax ( --, \, "$@", etc.). Any such occurrence not immediately preceded by {platform_home_dir}/scripts/ is reported as a violation with the offending file:line.

Add a short packages/agents/content/scripts/README.md documenting the convention for maintainers (this file is not loaded by agents, so it does not impose token cost on agent invocations).

Acceptance criteria

Must have

  • Every bare-command invocation of resolve-frontmatter.sh, describe-change.sh, get-ticket-id.sh, resolve-merge-options.sh, and resolve-reviewer-context.sh in packages/agents/content/skills/**/*.md and packages/agents/content/subagents/*.md is prefixed with {platform_home_dir}/scripts/.
  • The {repo-root}/packages/agents/content/scripts/resolve-reviewer-context.sh invocation in packages/agents/content/skills/orchestrate/modules/review-cycle.md:62 is replaced with {platform_home_dir}/scripts/resolve-reviewer-context.sh.
  • After running the install pipeline, the rendered skill/subagent files under ~/.claude/skills/ and ~/.claude/agents/ contain explicit absolute paths (e.g., ~/.claude/scripts/resolve-frontmatter.sh ...) at every previously-bare invocation site.
  • The same rendering works for at least one non-Claude platform target (e.g., ~/.codex/scripts/... on a Codex install) without manual fixup.
  • A regression test at packages/agents/src/__tests__/script-invocation-conventions.test.ts walks the skill and subagent trees, detects bare-command invocations of any known helper script, and fails listing each offending file:line.
  • All existing tests pass, including packages/agents/src/lib/__tests__/path-rewriter.test.ts.

Should have

  • A short packages/agents/content/scripts/README.md documents the {platform_home_dir}/scripts/ invocation convention, the list of helper scripts it applies to, and a pointer to the regression test that enforces it.

Nice to have

(No additional work identified. The audit during design confirmed the five scripts in packages/agents/content/scripts/ are the complete set of helper scripts referenced as bare commands.)

Metadata

Metadata

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions