feat(tui): auto permission classifier - a middle tier between build and yolo - #137
Draft
wesleymatosdev wants to merge 2 commits into
Draft
feat(tui): auto permission classifier - a middle tier between build and yolo#137wesleymatosdev wants to merge 2 commits into
wesleymatosdev wants to merge 2 commits into
Conversation
Adds a middle permission tier to the terminal client: before rendering the permission dialog, requestToolPermission() consults a static policy (read-only command prefixes and tools are allowed; rm -rf, sudo, curl|sh, force pushes and credential paths are denied with a reason; everything else falls through to the normal dialog). The classifier returns the same response objects the dialog produces, so runtime behavior is unchanged; verdicts are surfaced as TUI notices for transparency. Policy defaults live in code and can be overridden point-wise via ZCODE_AUTO_PERMISSIONS_CONFIG (unmatched defaults to ask, so the classifier is strictly fail-open toward the human dialog). Verified: bun test (unit), typecheck and biome clean, plus a live A/B over a PTY against the real runtime: the stock bundle renders the write dialog and writes nothing; the patched bundle auto-approves the covered write with a visible notice and still defers uncovered writes.
The runtime's mode enum has no auto value (its reserved one denies everything), so auto ships as a client-side overlay: Shift+Tab and the /mode picker cycle a client list (build, edit, auto, yolo, plan) where selecting auto pins the runtime to build and routes permission prompts through the classifier. One-shot runs can boot into the overlay via ZCODE_CLIENT_MODE=auto (only over a build runtime). Typed /mode commands still execute in the runtime and exit the overlay; runtime mode echoes during a session no longer clear it. Classification is gated on the overlay being active, so build/edit users see exactly the behavior they chose; AskUserQuestion and plan approvals are never auto-answered.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feat(tui): auto permission classifier — a middle tier between build and yolo
Why
Claude Code ships an
autopermission mode: a classifier answers most prompts automatically and only escalates the gray zone to the user. ZCode's terminal client currently offers only two poles —build(prompt for everything) andyolo(prompt for nothing). The runtime reserves anautomode value but currently denies all tools in it (Auto mode is reserved but not implemented yet), so there is no way to run "safe things automatically, ask me about the rest."This PR adds that middle tier in the TUI, where the permission UX lives, without touching the vendored runtime.
What it does
Classifier at the dialog seam.
requestToolPermission()consults a static policy before rendering the choice dialog:git status/log/diff/show/branch,ls,pwd,cat,head,tail,wc,rg,grep,find,which,file,stat, plus theRead/Glob/Grep/TodoRead/WebSearchtoolsrm -rf,sudo,curl|sh,git push --force,git reset --hard(soft), and credential paths (.env,.ssh,.aws,.gnupg,.kube,.netrc,.npmrc,*.pem,id_rsa, …) even for readsnull→ the normal human dialog, unchangedA verdict returns the same response object the dialog returns (
{ decision, reason }), so runtime semantics are untouched. Every auto-decision prints a transcript notice (auto-permissions · ALLOW · Write · …) so users can always see what was decided and why.Opt-in
autoclient mode. Shift+Tab and the/modepicker now cycle a client-side list —build → edit → auto → yolo → plan. Selectingautopins the runtime tobuild(so prompts still reach the client) while the footer showsautoand the classifier decides prompts. Typed/modecommands still execute in the runtime (which owns its enum) and exit the overlay. One-shot runs can boot into the overlay withZCODE_CLIENT_MODE=auto, honored only over a build runtime.Policy is data.
ZCODE_AUTO_PERMISSIONS_CONFIG=<path>overrides the built-in rules point-wise (allow/softDeny/hardDenyrule arrays withtool,commandPrefix,commandRegex,pathPrefix,pathRegex,note;defaults.unmatched: ask|allow|deny). Malformed or missing files fall back to built-ins. Defaults are conservative: unmatched → ask.Safety properties (structural, not disciplinary)
yoloor affect non-prompting paths.AskUserQuestionand plan-approval flows are never auto-answered (explicit gate).Testing
test/auto-permissions.test.ts(8) — classification semantics, precedence, credential paths, config load/fallback, dialog-response mappingtest/shortcuts-auto.test.ts(9) — client mode cycle (officialmodes/nextModecontract unchanged), runtime-vs-client normalization, boot gating, classification gatebun run typecheckclean;biome checkclean on all touched files; full suite delta vsmainverified zero (pre-existing env failures identical on a clean-main worktree)HOME, identical prompt, only the TUI bundle swapped): stock renders the write dialog and writes nothing; the patched client inautoprints the allow notice, executes the covered write with no dialog, and still renders the dialog for an uncovered write. This run also caught a real integration bug (runtime mode echoes clearing the overlay), now fixed and covered.