Abhed — CLI & Product Surface
Status: Draft · 2026-09-02 · Evidence status: [E]
The architecture makes Abhed work; this document is what makes it feel like a tool an engineer wants to use. These decisions are not cosmetic — approval friction, output density, and interrupt latency determine whether people actually adopt the agent.
1. Interaction model
$ abhed
╭─ abhed ─────────────────────────────── qwen3-32b · ~/proj · main ─╮
│ │
│ > fix the failing auth tests │
│ │
│ ● Searching for test files │
│ grep "auth.*test" → 3 files │
│ ● Reading auth_test.go │
│ ● Running tests │
│ bash go test ./pkg/auth → FAIL (2 failures) │
│ │
│ TestLogin_ExpiredToken: expected 401, got 500 │
│ TestLogin_Refresh: nil pointer at auth.go:88 │
│ │
│ ● Reading auth.go │
│ ● Edit auth.go │
│ ╭────────────────────────────────────────────────╮ │
│ │ 86 if tok == nil { │ │
│ │ 87 - return nil, err │ │
│ │ 87 + return nil, ErrTokenMissing │ │
│ │ 88 } │ │
│ ╰────────────────────────────────────────────────╯ │
│ [a]ccept [r]eject [e]dit [A]lways for this file │
╰────────────────────────────────────────────────────────────────────╯
Design rules
- Show work as it happens. Streaming, not a spinner then a wall of text. The user must be able to interrupt early when the agent goes wrong — which it will.
- One line per tool call, expanded only on failure or when it carries information the user needs. Verbosity is the default failure mode of agent CLIs.
- Diffs before writes, always. The approval prompt shows exactly what changes with surrounding context. Never "Claude wants to edit auth.go — allow?"
- Esc interrupts immediately. The running tool is cancelled, partial work is preserved, and the session stays alive. A tool that can't be interrupted will be killed with Ctrl-C, losing the session.
- Errors are shown, not swallowed. A failing test is information, displayed plainly.
2. Permission prompts
Per P7, the ordered evaluation decides whether to ask; this is how to ask.
● bash npm install --save-dev vitest
Adds vitest as a dev dependency
[a]ccept [r]eject [A]lways allow `npm install *` [?] explain
- The
descriptionfield from the tool call (§06) is what makes this readable. Alwayscreates a scoped rule (npm install *), never a blanket tool allow.?shows why approval was required — which rule matched.- Rejection feeds a message back to the model so it can adapt rather than retry.
Batch related approvals. Five edits to one file in one plan = one prompt with five diffs. Prompt fatigue causes blanket-approve, which defeats the permission model entirely.
3. Modes
| Mode | Behavior | Use |
|---|---|---|
default | Ask before mutations | Everyday work |
accept-edits | Auto-approve file edits; ask for bash | Trusted refactors |
plan | Read-only; produce a plan, no mutations | Exploring an approach |
auto | Approve by classifier + rules; hard blocks stand | Batch/CI |
restricted | Managed policy, bypass disabled | Regulated tenants |
Deny rules and destructive-command blocks apply in every mode including auto (P7). bypass exists but is refusable by org policy and blocked when running as root.
4. Slash commands
| Command | Effect |
|---|---|
/mode <name> | Switch mode — implemented |
/compact [hint] | Compact now, optionally guided — implemented |
/cost | Tokens, cache hit rate, prefill saving, compactions — implemented |
/sessions | List recent sessions (durable store) — implemented |
/resume <id> | Replay a prior session's transcript — implemented |
/cwd | Show the workspace root — implemented |
/undo | Revert the last turn's file changes — implemented |
/diff | Files changed this session, with line counts — implemented |
/clear | Clear context, keep the workspace — implemented |
/memory | Show the ABHED.md files in effect — implemented |
/model [name] | Show or switch the configured provider — implemented |
/export [path] | Write the transcript to JSON — implemented |
/resume replays a session recorded by any process, including one from before a restart, because state is event-sourced and the store is durable.
/undo reverts a whole turn, not one file: a model that edits four files to make one change should undo as one change. Within a turn only the earliest checkpoint per file is applied, so repeated edits still revert to the pre-turn state. Undoing a file the agent created deletes it rather than emptying it.
Checkpointing hangs off tools.Session.Checkpoint rather than living in each tool, so a future mutating tool gets undo by construction and cannot forget to call it.
/model deliberately does not hot-swap mid-session: a different model means a different prompt prefix, which would invalidate the cache the whole context design depends on (P8).
5. Web console
Same event stream (P6), different affordances. Not a second implementation of the loop.
- Sessions — live and historical, filterable by user, repo, model, outcome.
- Replay — step through any session's events. Deterministic replay is why this works.
- Approvals — pending requests routed to a reviewer; enables headless runs with a human gate.
- Audit — searchable event log, exportable for compliance.
- Fleet — model health, cache hit rate, GPU utilization, queue depth.
- Eval — L3/L4 results over time; regression alarms (§08).
6. Headless and CI
abhed -p "fix failing tests" --output-format json --max-turns 30
abhed -p "review this diff" --mode plan --allow "bash(git *)"
-pnon-interactive; exits with a status code reflecting the terminal event.--output-format jsonemits the event stream for programmatic consumption.- Approvals:
--allow/--denyrules, or route to the web console and block. - Every terminal event maps to a distinct exit code so CI can distinguish "task failed" from "budget exhausted" from "policy denied" — they need different responses.
7. Latency budget
What the user actually feels:
| Interaction | Target | Why |
|---|---|---|
| Keystroke → echo | < 16 ms | Local; never blocked on the model |
| Submit → first token | < 1.5 s | Prefix cache hit makes this achievable (P8) |
| Tool call → result shown | < 100 ms overhead | Beyond the tool's own runtime |
| Esc → cancelled | < 200 ms | Trust depends on this |
| Session resume | < 500 ms | Event replay from store |
A prefix-cache miss shows up directly as time-to-first-token. The eval report's cache-hit metric (§08) is a user-experience metric, not just a capacity one.