From client-admin
Create or update the encrypted credentials file (credentials.env.age) in the client's harness repo. Use when setting up API keys for the first time or adding/rotating a key. Run by the client admin.
How this skill is triggered — by the user, by Claude, or both
Slash command
/client-admin:manage-credentialsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
**Execute this skill directly. Do not enter plan mode. Do not expose internal derivation steps to the user.** The user should see only: what you found, what you need from them, and the finished result.
Execute this skill directly. Do not enter plan mode. Do not expose internal derivation steps to the user. The user should see only: what you found, what you need from them, and the finished result.
credentials.env.age yet in their harness repo).age is not installed on your machine. Install it first: brew install age (Mac) or winget install FiloSottile.age (Windows).This skill is run by the client admin on their own machine. The consultant's role ends at /onboard-client; credential population happens admin-side so the admin controls their own API keys and their own passphrase.
The consultant may run this skill from the master repo for demos, drills, or emergency rotations — that's not the canonical path, and the pre-flight gate below permits both.
The admin chooses how to distribute the passphrase + installer to employees. Options in order of security:
The skill does not prescribe a channel. Use whatever matches your org's existing secrets-distribution conventions. Do not paste the passphrase in the same message as the installer one-liner unless the channel is end-to-end encrypted.
This skill manages secrets and must only run on admin/consultant machines. Matches the /client-admin:generate-installer gate: clients.json is the only automatic signal that survives; everything else falls back to a single conversational question.
# Signals:
# - clients.json in $PWD → consultant (certain)
# - .claude-plugin/marketplace.json in $PWD → admin OR consultant
# (admin working inside their harness checkout, or consultant inside a client repo)
# - Neither present → unknown — ask the user
if [ -f clients.json ]; then
ROLE=consultant
elif [ -f .claude-plugin/marketplace.json ]; then
ROLE=admin_or_consultant
else
ROLE=unknown
fi
If ROLE=unknown, ask the user ONE question (no multi-step triage):
Quick check before I manage credentials — who's running this: admin, consultant, or employee?
Employee refusal text:
This skill manages the encrypted credentials file for the client harness — it's for the admin or consultant, not team members. Contact your admin if you need a key added or rotated.
Context A — Master repo (consultant): clients.json exists in $PWD. List active clients by human name — titlecase the name field (e.g., test-client → "Test Client"). Ask which client if more than one.
Context B — Client repo (admin): .claude-plugin/marketplace.json exists in $PWD. Extract <org>/<repo> from the git remote URL.
Context C — Installed harness (admin's own machine): Run this exact bash to build the candidate list — only client harness repos have a credentials/ directory; marketplace catalogs do not:
for name in $(jq -r 'keys[]' ~/.claude/plugins/known_marketplaces.json 2>/dev/null); do
[ -d "$HOME/.claude/plugins/marketplaces/$name/credentials" ] && echo "$name"
done
Each line of output is a valid client harness name. If no lines are output, fall through to the prompt below. For each candidate, read the human-friendly name from ~/.claude/plugins/marketplaces/<name>/.claude-plugin/marketplace.json → .name field, titlecased. If exactly one candidate, use it automatically. If multiple, ask which.
If none of A/B/C produces a repo, ask: "I couldn't detect a client repo. Are you running this from your master repo, from inside a client's harness repo, or on a machine with the harness installed?"
Passphrase input is handled by age natively — it opens /dev/tty directly and does not echo. Never ask for a passphrase as a chat message. Never include any passphrase value in tool output, responses, chain-of-thought, or subagent prompts. The passphrase never enters Claude's context.
Admin machines have gh authenticated — use gh repo clone so no token is embedded in a URL or echoed.
TMP_WORK=$(mktemp -d)
trap 'rm -rf "$TMP_WORK"' EXIT
gh repo clone "${CLIENT_REPO}" "$TMP_WORK/repo" -- --depth 1
If gh repo clone fails (e.g., gh not installed), fall back to:
git clone --depth 1 "https://github.com/${CLIENT_REPO}.git" "$TMP_WORK/repo"
(The credential helper configured by bootstrap will handle auth. Do NOT construct a token-in-URL string or echo any token value.)
If $TMP_WORK/repo/credentials/credentials.env.age exists, ask the user in chat:
I found an existing credentials file. Do you know the current passphrase?
- Yes — enter it now to load and review the current keys before editing
- No / Start fresh — replace the file with new credentials (existing keys will be lost)
If Yes: execute via Bash tool — age will prompt for the passphrase at the terminal (no echo):
age --decrypt -o "$TMP_WORK/credentials.env" "$TMP_WORK/repo/credentials/credentials.env.age"
Display the current keys, masking values:
Current keys:
TAVILY_API_KEY = ****
AVOMA_API_KEY = ****
If No / Start fresh: skip decryption, start with an empty credentials set. Tell the user: "Starting fresh — existing credentials will be replaced when you save."
If no existing file: start with an empty credentials set silently.
Ask the user which API keys they want to add or update. For each key they name, output a copy-paste terminal command in triple backticks so the value is entered securely (not visible, not in chat, not in shell history). Use printf for the prompt and read -rs without -p — this works in both bash and zsh (-p in zsh means "read from coprocess", not "print prompt"):
printf "TAVILY_API_KEY: " && read -rs v && echo && echo "TAVILY_API_KEY=$v" >> "$TMP_WORK/credentials.env"
Replace TAVILY_API_KEY with the actual key name. Provide one command block per key. After the admin pastes and runs each command, ask "Any other keys? (say done when finished)".
To remove a key (start-fresh path or explicit removal), omit it — don't append it to credentials.env.
The final $TMP_WORK/credentials.env must never be printed or logged.
Run via Bash tool. age will prompt for the passphrase and confirmation at the terminal — no echo, never enters Claude's context. The admin picks the passphrase from their password manager and types it when prompted.
mkdir -p "$TMP_WORK/repo/credentials"
age --encrypt --passphrase -o "$TMP_WORK/repo/credentials/credentials.env.age" "$TMP_WORK/credentials.env"
GITIGNORE="$TMP_WORK/repo/.gitignore"
if ! grep -Fq "credentials.env" "$GITIGNORE" 2>/dev/null; then
printf '\ncredentials.env\n' >> "$GITIGNORE"
fi
cd "$TMP_WORK/repo"
git add credentials/credentials.env.age .gitignore
git commit -m "chore: update credentials"
git push
Print this as the final output. DO NOT print the passphrase — admin already has it in their password manager.
Credentials updated and pushed to <CLIENT_REPO>/credentials/credentials.env.age.
Next step: run `/client-admin:generate-installer` to emit the decrypt-only one-liner. Distribute the one-liner + the passphrase (from your password manager) to employees via your chosen encrypted channel (1Password shared item, encrypted DM, etc.).
If rotating an existing key (same passphrase), employees pick up the new credentials.env.age at their next Claude Desktop marketplace sync — no new passphrase distribution needed.
If rotating the passphrase itself, re-run `/client-admin:generate-installer` after this skill and redistribute both the one-liner and the new passphrase; employees re-run the one-liner to decrypt under the new passphrase.
| Scenario | Behavior |
|---|---|
age not installed | Fail immediately: "age is not installed. Run: brew install age (Mac) or winget install FiloSottile.age (Windows)." |
| Wrong existing passphrase | age returns non-zero; fail with: "Wrong passphrase — the existing credentials file could not be decrypted." |
| Clone fails | Fail with: "Could not clone $CLIENT_REPO. Check that gh is authenticated and has repo access." |
| Push fails | Fail with git error; leave temp files for inspection. |
age — install via brew install age (Mac) or winget install FiloSottile.age (Windows)gitgh — authenticated as a user with write access to the client harness repo (gh auth status succeeds)Guides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.
npx claudepluginhub palisades-labs/marketplace-admin --plugin client-admin