From claude-secrets
When the user needs a credential to make an API call, store it once via a system popup, then reference it by name in any command. The value lives only in the macOS Keychain and the broker subprocess; it never enters this conversation.
How this skill is triggered — by the user, by Claude, or both
Slash command
/claude-secrets:claude-secretsThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
This skill provides a safe way to use credentials without ever reading them. The user types a value into a native system popup, the broker stores it in macOS Keychain, and you reference it by name when running commands. The actual value never crosses into your context.
This skill provides a safe way to use credentials without ever reading them. The user types a value into a native system popup, the broker stores it in macOS Keychain, and you reference it by name when running commands. The actual value never crosses into your context.
When the user asks to set up a credential, run:
claude-secrets prompt NAME --json
Replace NAME with a clear identifier (e.g., STRIPE_KEY, GITHUB_TOKEN, AWS_ACCESS_KEY). A system popup appears on the user's screen — they type the value there. You see only:
{"status":"ok","name":"STRIPE_KEY","action":"stored"}
If the user cancels: {"status":"cancelled","name":"..."}. If they don't type within 2 minutes: {"status":"timeout","name":"..."}. Handle gracefully.
When you need to use a stored credential, run:
claude-secrets run --inject NAME=ENV_VAR -- <command>
The broker reads the value from Keychain, exec's your command with ENV_VAR set, captures stdout and stderr, runs them through a sanitizer that redacts any byte-for-byte appearance of the value, and returns:
{"status":"ok","exit_code":0,"stdout":"...","stderr":"..."}
Example — testing a Stripe API call:
claude-secrets run --inject STRIPE_KEY=K -- bash -c '
curl -s -H "Authorization: Bearer $K" https://api.stripe.com/v1/charges
'
Critical: wrap the command in bash -c '...'. The broker exec's argv directly (no shell), so $VAR in argv is passed as a literal string. The bash -c wrapper opens a shell inside the subprocess where the env var actually expands. Without bash -c, your curl would send Authorization: Bearer $K literally and get a 401.
| Command | Purpose |
|---|---|
claude-secrets list --json | List stored names + metadata (never values) |
claude-secrets rotate NAME --json | Replace the value (pops dialog again) |
claude-secrets rm NAME --json | Delete a stored credential |
claude-secrets status --json | Check if the broker is working |
When the user asks you to do something that needs an API key or token:
claude-secrets list --json first to check if it's already stored.<service> key safely so I never see it — should I trigger the popup?"claude-secrets prompt NAME --json.Never run any of these. They defeat the entire point.
claude-secrets get NAME — that subcommand does not exist; the broker has no read-to-stdout path on purpose.cat ~/.config/claude-secrets/manifest.json to "look at" the secret — the manifest stores names only, but reading it suggests you don't trust the design.echo $STRIPE_KEY to "verify" a value works. The whole architecture exists so you cannot do this.run invocation's output into chat. The sanitizer will redact it, but the intent is wrong.prompt instead.sk-*, ghp_*, JWT, AKIA*, etc.). It does NOT catch base64-encoded, URL-encoded, or otherwise transformed appearances of a value. If a command base64-encodes the credential and prints it, the encoded form will reach the user.prompt will fail; tell them to run claude-secrets prompt NAME locally on their machine.npx claudepluginhub robertnowell/claude-secrets --plugin claude-secretsCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.