Commit Graph

1 Commits

Author SHA1 Message Date
Auree Aubert 1cb3ee755b Claude Code plugin channel, cargo-ai approval hook, and CLI version pin (#51)
* Add Claude Code plugin channel and cargo-ai auto-approve hook

- .claude-plugin/marketplace.json + plugin.json: the repo root doubles as
  a Claude Code plugin (skills scan root "./" discovers all 15 top-level
  skill dirs with no restructuring; plugin version mirrors the router's,
  now linter-enforced).
- hooks/approve-cli.sh: allow-only PreToolUse hook that auto-approves
  cargo-ai / npx @cargo-ai/cli calls and read-only pipe helpers after a
  quote-aware structural pass. Never auto-approves: login/logout,
  workspaceManagement token/report, cdk deploy/destroy, or any
  remove/delete/destroy token. Verified with a 49-case harness plus a
  live end-to-end test (clean permissions: whoami auto-approved, token
  list fell through to prompt).
- README: plugin install path + pick-one-channel note; prerequisites.md
  gains a "Permission prompts" section. cargo 1.9.0, plugin 1.9.0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Pin the CLI version in cargo/cli-version

- New cargo/cli-version (1.0.29): single-line semver pin, the source of
  truth for which @cargo-ai/cli these skills were written against. Lives
  inside the router skill so it ships with skills add and the ClawHub
  bundle; session hooks read it locally with a latest fallback, so the
  pin can never block an install.
- Linter asserts the pin exists and is bare semver, and that openclaw
  install blocks stay @cargo-ai/cli@latest (bootstrap only; the session
  hook converges to the pin).
- Docs: router session job 1 reordered (skills add -> pinned npm
  install) with a "why the pin" note; prerequisites.md and README
  refresh snippets use the pinned form. cargo 1.10.0, plugin 1.10.0.

The producer side (CLI release pipeline PRs pin bumps here; install.sh
consumes the raw pin URL) lands in getcargohq/cargo separately.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(approve-cli): position-independent gating closes token-shift bypass

Review finding: gated checks only examined the first two non-flag tokens
after the binary/package. Valid invocation prefixes shift the real
subcommand out of those slots - `npx -p @cargo-ai/cli cargo-ai login`
puts a literal cargo-ai before login (confirmed auto-approved), and a
global flag with a value would shift the domain word the same way.

Fix: normalize the cargo segment's token stream once, drop leading
`cargo-ai` binary tokens, and gate position-independently - credential
tokens (login/logout), destructive verbs (remove/delete/destroy), and
gated adjacent pairs (workspaceManagement token/report, cdk
deploy/destroy) anywhere in the segment refuse approval. Accepted cost,
documented: a gated word as a literal inside a quoted argument (SQL
containing "login") now falls through to the prompt - the safe
direction for an allow-only hook.

Harness extended to 62 cases: all -p/token-shift/flag-shift bypass
shapes prompt; safe -p usage still auto-approves.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 18:30:56 -07:00