[FEATURE] AskUserBash tool — let agents prompt for secrets without exposing them to the model
Preflight Checklist
- [x] I have searched existing requests — see Related issues below.
- [x] This is a single feature request.
Related issues
- #29910 — Built-in secrets management with optional third-party integrations (open). This proposal is meaningfully smaller in scope: a single tool primitive Claude can call, not a full vault subsystem. It is a viable first step that unblocks the same security goal ("secrets should never appear in chat context") without committing to the larger architecture, and could serve as the underlying input primitive for #29910.
- #38797 — Secure/masked input for sensitive data entry (closed as duplicate of #29910). This proposal differs by being a tool Claude calls during its agentic flow (not a slash command the user invokes) and by piping the value directly into a local bash command rather than storing it.
Problem statement
Today, when a user wants Claude Code to do anything that needs a secret — a GitHub PAT for gh auth login, an OPENAI_API_KEY for .env.local, an MFA code, a private SSH key — the only path inside the agentic loop is to paste the secret into chat. The moment that happens:
- The value enters the model's context window.
- It's transmitted to the API.
- It's persisted in
~/.claude/projects/*.jsonltranscripts. - It's potentially captured in screen recordings, terminal scrollback, and IDE clipboards.
The user's only alternative is to leave Claude Code, handle the secret in a separate terminal, and come back — defeating the point of an agentic CLI.
Proposed solution
A new built-in tool, alongside AskUserQuestion:
AskUserBash({
label: string, // displayed above the input field
preview: string, // human-readable description of what the bash command will do
bashCmd: string, // template; user input is substituted for $ARG1
})
When Claude calls this tool, Claude Code renders an inline secure-input form. The user types or pastes their value. On submit:
- The input is substituted for
$ARG1(and only$ARG1) inbashCmd. - The resolved command is executed locally via the user's shell.
- Only an exit-code-equivalent boolean is returned to the model — never the input, never the resolved command, never stdout/stderr.
The model sees { ok: true } or { ok: false } and proceeds.
Demos
Two screenshots, both showing the same tool rendered with different (label, preview, bashCmd) payloads.
Demo 1 — GitHub PAT into gh auth login:
AskUserBash({ label: "GitHub Personal Access Token", preview: "Pipe token into gh CLI — never sent to the model", bashCmd: "echo $ARG1 | gh auth login --with-token" })
<!-- drag screenshot 1 here: ~/Desktop/askuserbash-feature-request.png -->
<img width="2000" height="1125" alt="Image" src="https://github.com/user-attachments/assets/8ef25e56-12dc-4c69-a56b-9ef6397aaf0d" />
Demo 2 — OpenAI key into .env.local:
AskUserBash({ label: "OPENAI_API_KEY", preview: "Append to .env.local without touching the model context", bashCmd: "printf 'OPENAI_API_KEY=%s\n' \"$ARG1\" >> .env.local" })
<!-- drag screenshot 2 here: ~/Desktop/askuserbash-feature-request-2.png -->
<img width="2000" height="1125" alt="Image" src="https://github.com/user-attachments/assets/ef3235ad-c963-45ea-b4b3-16ce2d5443f5" />
Security model
| Channel | Visible to model? |
|---|---|
| label, preview, bashCmd template | Yes — the model authored these |
| User input (the value typed into the form) | No |
| Resolved command (after $ARG1 substitution) | No |
| stdout / stderr | No |
| Exit-code-equivalent boolean | Yes |
The user sees the bashCmd template before typing — they always know exactly what will run with their input. This preserves Claude Code's existing trust model (user inspects → user approves → Claude runs) while closing the secret-shaped hole.
Use cases this unblocks
echo $ARG1 | gh auth login --with-tokenprintf 'OPENAI_API_KEY=%s\n' "$ARG1" >> .env.localecho "$ARG1" | wrangler secret put STRIPE_WEBHOOK_SECRETop item edit "Anthropic API" credential="$ARG1"aws-vault exec prod --mfa-token=$ARG1 -- aws s3 lsprintf '%s' "$ARG1" | ssh-add -kubectl create secret generic api --from-literal=token=$ARG1- Anything else a user would otherwise paste into chat and immediately regret.
Why a tool primitive, not a full secrets manager
AskUserBash is composable with whatever secrets infrastructure the user already has — 1Password, Doppler, AWS Secrets Manager, plain .env, Cloudflare bindings, system keychain. It's the input edge: the missing primitive that lets Claude help with secret-handling workflows without ever seeing the secret.
- Users with no secrets infra can use it to write to
.env.local. - Users with sophisticated infra can pipe into
op item edit,wrangler secret put,vault kv put, etc.
This makes AskUserBash implementable as a small, well-scoped feature on top of the existing tool / form infrastructure, while moving the security needle today. It does not preclude #29910's larger proposal — it could be the underlying primitive that powers it.
Alternative API shapes considered
AskUserSecureInput(label) → value— returns the raw value to the model. Defeats the purpose; the secret still enters context.- A
/secretslash command — user-driven only; doesn't fit Claude's agentic flow where Claude recognizes that a secret is needed and asks for it. Tool form is the right shape. bashCmdwith arbitrary$ARG1..N— multi-input would be a small extension; single input keeps v1 surface minimal.- Returning stdout to the model — tempting, but a single error message can leak the secret. Keep the return channel boolean-only; let the model retry with
{ ok: false }.
Priority
High — this is the single highest-leverage tactical security feature for users today, and it's small enough to ship as an MVP.
Feature category
Tools / Interactive mode (TUI) / Security
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗