Skip to content

Pre-approve safe commands and run shell inline

With the rename done, the next task is to harden the new capturePayment path with a few more tests, and that means the agent runs npm test over and over as it iterates. In accept-edits mode the edits fly, but every single test run still stops and asks - because accept-edits deliberately doesn’t cover arbitrary shell commands. The mode was a bulk loosening; what you want now is a surgical one: silence this one trusted command and nothing else.

A permission rule moves a specific action from ask to allow permanently, written into your settings so it survives restarts. The quickest way to create one is at the moment the prompt appears - Claude Code offers to remember your answer:

⏵ Run npm test
1. Yes
2. Yes, and don't ask again for `npm test` ← creates the allow rule
3. No
> 2
✓ Added allow rule: Bash(npm test). Won't ask again.

From here on, npm test runs silently in any mode - the agent can lean on your test suite as a feedback loop without interrupting you for permission each lap. The rule lands in your settings file under a permissions.allow list, so you can also write it by hand or commit it to share with the team:

.claude/settings.json
{
"permissions": {
"allow": ["Bash(npm test)", "Bash(npm run build)"]
}
}

The discipline that keeps this safe is to allow narrowly. Bash(npm test) is a precise, low-risk command and a fine thing to silence. A blanket Bash(*) that green-lights any shell command is how you hand the agent an unsupervised root shell - exactly the thing the permission system exists to prevent. Allow the specific commands you’d run a hundred times without thinking; leave everything else to prompt. Rule patterns support more than exact strings - prefixes, arguments, and tool-specific forms - and the current syntax lives in the permissions docs.

Where that discipline actually gets tested isn’t the rule you write calmly into a settings file - it’s the prompt that arrives mid-flow, after a streak of easy yeses. The session below walks into exactly that moment; when playback pauses, the prompt is yours, and each answer edits a different amount of standing policy.

One work session, played back with its permission policy x-rayed beside it. Every approve? prompt is an edit to that policy, not a yes/no - watch the ledger on the right, and when playback pauses, the prompt is yours.

agent session - orders-api
agent cli · new session · repo: orders-api · permissions: default
what runs without asking

    The prompt wears different clothes across tools - allow/always/deny buttons, allow rules in a settings file, approval modes, sandbox flags - but underneath every one is this same ledger: a standing policy you edit one decision at a time. Grants sized to an exact command age well. Grants sized to a wildcard, approved under fatigue, are how an agent ends up with more rope than anyone remembers giving it.

    There’s a neighbor to this worth knowing, because it solves a related friction: sometimes you want to run a command and put its output in front of the agent - check what’s failing, look at git status, see a directory listing - without leaving the session or waiting for the agent to do it. In the CLI, prefix a line with ! and it runs in your shell immediately, with the result dropped into the conversation:

    > !npm test 2>&1 | tail -20
    ⎿ FAIL test/capture.test.js - expected 200, got 500
    (output added to context)
    > that 500 is the bug - the capture path is missing the auth header. fix it.

    That ! is you running the command, not the agent asking to - so there’s no permission prompt at all; it’s your shell, your call. It’s the fastest way to feed the agent a concrete fact instead of describing it, and it pairs naturally with allow rules: you reach for ! when you want to look yourself, and allow rules for when you want the agent to stop asking about the looking it does on your behalf. (This inline ! is a CLI feature; the graphical editor extension doesn’t carry it - run such commands in the integrated terminal there instead.)

    You’ve now loosened the leash about as far as is comfortable while still reviewing the direction: edits auto-apply, trusted commands run silently, and you can drop in a command whenever you want. Which makes this the right moment to do the opposite - because the next task touches a secrets directory, and “loose by default” is precisely when you need something the agent cannot do, no matter how relaxed the rest of the session gets. Time to lock it out.