@agent_cli/cli
v0.2.4
Published
Primary command-line entry point for dev-agent.
Readme
@agent_cli/cli
Primary CLI entry point. The package has two supported invocation modes:
- workspace development through
pnpm clifrom this repository; - an installed
dev-agentbinary from any project directory after@agent_cli/cliis published or installed from a local tarball.
Workspace development
Run from the repo root:
pnpm cli --version
pnpm cli --once "list files in the current directory"
pnpm cli --session docs --once "answer in code"
pnpm cli --session docs --reset-memory --once "start over"
pnpm cli --cleanup-evidence --remove-rolled-back --json
pnpm cli --session docs --export-evidence --audit-max-bytes 1048576
pnpm cli --session docs --preview-evidence --status failed
pnpm cli setup --provider ollama --model qwen3:4b-instruct
pnpm cli mcp add --project-state --name files --command npx \
--arg -y --arg @modelcontextprotocol/server-filesystem --arg /tmp
pnpm cli mcp list --project-state --json
pnpm cli --tools
DEV_AGENT_MODEL_PROVIDER=ollama pnpm cliInteractive terminal layout
The Ink interface keeps welcome, conversation, and informational panels in one
managed scroll area. Wheel/PageUp/PageDown work from startup; Home/End (with an
empty draft) jump to the beginning/latest output. Back to bottom uses the current
painted layout, including composer growth and terminal resize. The composer keeps
a bounded four-line draft window; the full draft is preserved for submission.
Approval input remains beside the composer. When a draft starts with : or /, the command palette supports ↑/↓ selection, highlights the active row, uses Tab to insert that command, and uses Esc to close the palette without cancelling the session.
Below the composer, the active provider/model and context status replace the repeated workspace/session footer. Context tokens are the provider-reported input count for the last request, not cumulative session usage or a live estimate of the next request. Before telemetry arrives (and after switching models/sessions), usage is unknown. The model context-window limit is currently unknown; no utilization percentage is inferred from session budgets or character limits. The executor being local does not imply a local model.
Routine route/state/timing/token diagnostics are hidden by default. Use :route,
:trace, and :budget for explicit details, or DEV_AGENT_TUI_DEBUG=1 for verbose
run summaries and automatic route notices. Notices are deduplicated and retain
the latest 50; pending approvals and retry state are stored separately. Errors,
safety decisions, and explicit command results remain visible in the scroll area.
External editor composition
While the Ink session is idle, :editor (also /editor) suspends the TUI and
opens the configured editor on a private temp file:
:editor- The editor is resolved from
$VISUALfirst, then$EDITOR, thenvi. The value must name a single executable; it runs through an argument array withshell: falseand an explicit--before the temp file path, and any value containing shell metacharacters or extra tokens is refused (use a wrapper script for flags such ascode -w). - Saving content and exiting the editor loads it into the composer draft for
review; nothing is submitted automatically. Ink 7's native
suspendTerminallifecycle clears the TUI, hands the terminal to the editor, and forces a full redraw when it resumes. Content is normalized (CRLF → LF, one trailing newline stripped) and capped at 8,000 characters with an explicit truncation notice. Closing the editor with an empty buffer is a quiet no-op. - The command is available only while the session is idle; piped, JSON, and
--onceruns are unaffected. A Ctrl-C that terminates the editor does not cancel the CLI session. Temp files are private (0600) and always removed.
Interactive skills
The interactive CLI discovers bounded Markdown skills from:
<working-directory>/.dev-agent/skills/<name>/SKILL.mdfor project skills;~/.dev-agent/skills/<name>/SKILL.mdfor user skills.
Project skills take precedence when both scopes define the same name. Skills are listed without loading their instructions into the base Prompt. In a rich TTY, use either colon or slash aliases:
:skills
:skill review
:skill offOnly the explicitly activated skill is added to later model requests. Skill instructions are bounded during discovery and remain session-local; activating one does not modify files or publish package state.
Specialist Agent definitions
The CLI can discover bounded Markdown Specialist Agents from:
<working-directory>/.dev-agent/agents/<id>/AGENT.mdfor project agents;~/.dev-agent/agents/<id>/AGENT.mdfor user agents.
Project definitions shadow user definitions with the same lowercase id. The
file body is the specialist instruction block. Optional front matter supports
name, description, provider, model, toolAllowlist, maxTurns,
maxTokens, maxDurationMs, and maxOutputChars. Front matter is deliberately
not a general YAML runtime: unknown fields, duplicate tools, invalid selectors,
unsupported provider IDs, unavailable tool names, and invalid budgets cause
that optional definition to be ignored. Use --tools to inspect the active
tool registry. Files and
instruction bodies are bounded during discovery. Omit toolAllowlist to inherit
only the caller-owned tool ceiling, or set toolAllowlist: [] to request no
tools for that specialist. Discovery caps each file at 128 KiB, retains at
most 16,384 instruction characters per definition, and loads at most 256
agents. Oversized or malformed optional files are skipped.
For example, save this as .dev-agent/agents/doc-maintainer/AGENT.md:
---
name: doc-maintainer
description: Review documentation against the implementation
toolAllowlist: [filesystem, search]
maxTurns: 2
---
Inspect the changed behavior and identify missing documentation.Use these read-only commands to inspect the definitions:
:agents
:agent <id>The /agents and /agent aliases are also accepted. Listing and inspection
show only safe metadata; the instruction body and source path are not printed.
Review project agent files before using :team: their Markdown is sent as
specialist instructions and can affect behavior within the permitted tools.
A project definition may declare a preferred provider and model, but it
cannot silently move the current session to another provider: when the
requested provider differs from the active session provider, both selectors are
ignored and the specialist inherits the session model. :agent <id> reports
this explicitly. A matching project provider may still select its declared
model. User-scoped Markdown definitions and explicit JSON
collaboration.roles remain caller-controlled configuration and keep their
existing model-selection behavior.
Discovered definitions augment the built-in :team roles. A JSON
collaboration.roles entry with the same id wins over the Markdown definition.
Before execution, every role still goes through the active tool registry and
collaboration.toolAllowlist intersection. Agent Markdown never executes code,
starts MCP servers, changes approval policy, or bypasses the Rust sandbox.
Extension discovery
The CLI discovers bounded metadata manifests from:
<working-directory>/.dev-agent/extensions/<id>/extension.jsonfor project extensions;~/.dev-agent/extensions/<id>/extension.jsonfor user extensions.
Project extensions shadow user extensions with the same id. Use these interactive commands in Ink mode:
:extensions
:extension <id>The /extensions and /extension aliases are also accepted. Discovery only
reads and validates the manifest, then reports the extension name, version,
scope, description, and counts for tools, commands, skills, MCP servers,
configuration keys, and resources. It does not execute manifest-declared
commands, load JavaScript, start MCP servers, or change tool policy.
Conversation checkpoints
Interactive Ink sessions support bounded conversation-history checkpoints:
:checkpoint
:checkpoints
:rewind <checkpointId>The /checkpoint, /checkpoints, and /rewind aliases are also accepted.
Rewind truncates persisted conversation entries at the selected anchor and
removes later checkpoints. It never restores before-images, rolls back files,
or changes applied change-set evidence; the CLI prints workspace unchanged
after a successful rewind.
Local task scheduler
Each interactive session owns a local, in-process Scheduler with concurrency
1. A normal prompt becomes one task only when the existing prompt queue
hands it to the runner, so prompts submitted during an active request remain
queued by Ink and cannot receive another task or another answer's output.
The Scheduler records only bounded lifecycle metadata:
queued -> running -> waiting-for-confirmation -> running -> completed
└───────> failed/cancelled
queued ────────────────────────────────────────> cancelledAn active task enters waiting-for-confirmation while a tool approval or
sandbox expansion decision is pending, then returns to running when that
decision resolves.
Inspect it from the Ink interactive mode:
:tasks
:task <id>The /tasks and /task <id> aliases are also accepted. Task history retains
at most 64 terminal snapshots, supports Ctrl-C/Escape cancellation, and never
persists prompt text, model output, tool arguments, paths, environment values,
or credentials. This is a session-local scheduling boundary, not a detached
background service or a cross-session job queue. Use detached background jobs below when work
must continue after the current CLI process exits.
Detached background jobs
In an interactive session, start and manage an isolated Git worktree task with:
:job start <request>
:jobs
:job <id>
:job cancel <id>
:job resume <id>Jobs require a Git repository and an unattended approval policy: select
--approval deny-dangerous or --approval allow before starting one. A job
runs in its own worktree and continues after the originating CLI exits. Its
request is stored locally in a user-private directory (0700 directory,
0600 files); public job listings contain lifecycle metadata, not the request
or model output. Requests and session history may contain sensitive content,
so they remain on disk until the job state is removed.
Resume is an explicit continuation, not restoration of an interrupted tool
call. The CLI validates the recorded worktree and session first, refuses a
transcript with unresolved tool calls, and asks the agent to inspect existing
work before continuing. Job changes remain in the isolated worktree; they are
not automatically merged into the source checkout. Review the path shown by
:job <id> before using or cleaning up the result.
Local run trace
Interactive sessions accept :trace and /trace after a run. The command
prints a bounded, metadata-only summary of the latest model/tool run, including
queue wait, time to the first answer token, aggregate model-call and tool time,
total elapsed time, token counts, and the largest measured latency stage. The
first-token measurement is from prompt submission to the first answer delta; it
includes queue wait, which is also reported separately. Per-span details name
only the model-call turn or sanitized tool name, never its input or output.
The trace never prints prompt text, assistant output, tool arguments/results,
paths, environment values, or credentials. It is available in interactive
sessions and is not added to JSON or protocol output. See
docs/trace-observability.md for the
Desktop endpoint and retention contract.
Security Center and local Skill Marketplace
The CLI includes two bounded, read-first surfaces:
:security
:security history
:security clear
:marketplace
:marketplace search docs
:marketplace install docs-helper
:marketplace disable docs-helper
:marketplace enable docs-helper:security scans a bounded set of text files for secret-like material, flags sensitive filenames, reviews shell-based MCP entries and secret-like MCP environment keys, and reports symlinks that leave the workspace. It never prints secret values, runs commands, edits files, or performs automatic remediation. :security clear deletes the recorded audit history only after the explicit :security clear confirm follow-up.
:security history shows the most recent scan metadata recorded by this workspace's Security Center: scan id, time, status, duration, scanned file/byte counts, and aggregate finding counts by severity and category. History is metadata-only — finding locations, summaries, secret values, paths, commands, environment values, and errors are never persisted or displayed. The bounded history lives next to the session state and is capped in record count and size. Audit history is a bookkeeping aid, not proof of runtime security.
The local marketplace reads .dev-agent/skill-marketplace.json. Entries declare a name, version, local source file, and permission labels. Install, disable, and enable require confirmation where they mutate files; remote URLs, path escapes, oversized skill files, malformed entries, and unconfirmed mutations are rejected. Skills are instruction-only and never execute code. Restart the CLI after install/enable so bounded skill discovery reloads them.
Adaptive model routing and session budgets
Interactive sessions default to conservative local routing: standalone greetings use fast, ordinary requests use balanced, and requests that mention debugging, migrations, concurrency, security, refactors, or other multi-step work use deep. The decision uses only the current prompt and is not persisted.
:route # show the current policy
:route manual # keep the current tier for later prompts
:route auto # return to automatic routing
:mode fast|balanced|deep # choose a tier and switch to manual routing
:budget # show session usage and limits
:budget tokens 20000
:budget cost 0.50
:budget duration 120000
:budget tokens offBudgets are process-local guardrails. Provider-reported tokens and configured model prices are accounted per request, including fallback calls. If a configured cost limit cannot be verified because the model has no price entry or a provider omits usage, the next request stops rather than reporting an unsafe zero cost. The ledger never stores prompts, responses, tool payloads, credentials, or filesystem paths.
Speed modes and latency benchmark
Use :mode to inspect the active mode, or select one for subsequent model calls:
:mode fast
:mode balanced
:mode deepfast requests the lowest reasoning/thinking level supported by the selected
model; deep requests a higher level. balanced leaves the provider at its
default. Unsupported models are left unchanged and the command says when a
model-specific control is unavailable. The Ink status line shows the active
mode.
Use :bench hi (or :bench for the same default prompt) to make one isolated
provider request and measure first-answer-token and total provider latency. A
benchmark is not a normal agent turn: it does not use tools, print or save the
model response, or add the prompt to conversation history. The check caps
completion at 32 tokens and may still incur provider usage charges. It reports
only model/mode and elapsed-time metadata. For Ollama, :trace also reports
the server's model-load, prompt-evaluation, and generation durations when
available, so a slow model stage can be narrowed to cold loading, a large
prompt, or token generation. The output labels this
scope=provider-only: it excludes the agent system prompt, conversation
history, and tool schemas, so use :trace after a real turn to diagnose
end-to-end latency. Standalone greetings (hi, hello, 你好, and similar)
are sent without workspace tool schemas; every other request retains the full
tool set. If a provider returns an unadvertised tool call in that narrow mode,
the CLI fails closed and does not execute it.
Human-readable runs also show a timing line after each turn:
[timing] queue=3ms first-token=418ms model=690ms tool=12ms total=962msIn --no-stream mode, the first-token value is n/a by design because there
is no answer delta before the completed response. :trace remains available to
identify whether queue wait, model calls, tools, or other runtime work accounts
for a slow run.
Session history
Interactive Ink sessions also support the read-only :history [count] command:
:history
:history 20The /history [count] alias is accepted. History shows the newest 10 entries
by default, accepts at most 50 entries, and bounds each displayed entry. It
sanitizes terminal control sequences and redacts credential-shaped values
before rendering; it does not change the model context, workspace, or
persisted session.
Use :search <query> (or /search <query>) for a case-insensitive search
across persisted conversation and tool entries. Ink renders history and search
results in a bounded panel.
Session browser and resume
Use :sessions (or /sessions) to browse the newest saved sessions. Add a
query to filter by session id or the bounded last-message preview:
:sessions
:sessions deployment
:resume deploymentIn Ink mode, :resume <query> opens a picker. Use Up/Down to move, Enter to
continue the selected session, and Escape to close the picker. Switching is
available only while idle; a running request, pending approval, or queued
prompt keeps the current session active. The picker reads only the current
session directory, shows at most 256 newest files, truncates previews, and
marks malformed or oversized files unavailable.
For a new process, --resume <session-id> starts from an existing session:
dev-agent --resume other-project
dev-agent --resume other-project --once "continue the previous task"--resume cannot be combined with --session; choose one explicit session id.
Existing --session, --session-list, delete, rename, and memory-file formats
remain compatible.
In Ink mode, :theme lists the built-in signal, mono, and ember palettes,
and :theme <name> switches the presentation without changing the session.
Assistant answers use bounded streaming Markdown, while approval cards show a
sanitized, truncated unified diff when a reviewed file change is available.
Set INK_SCREEN_READER=true to run the Ink TUI in screen-reader mode: the
frame renders unthrottled as flattened text, decorative spinner glyphs are
omitted, animation timers freeze into deterministic status labels (one
announcement instead of a stream of near-identical frames), and pending
approval cards and running tool cards announce themselves with an explicit
(busy) state prefix.
The TUI keeps its animation cheap: the status line, thinking glyph, thought
line, command palette, and approval pulse all ride one shared 180ms clock
(src/ink/animation-clock.ts) that starts on the first subscriber and stops
when the last one leaves, instead of one OS timer per component. Set
DEV_AGENT_TUI_RENDER_METRICS=1 to count renders exceeding 1000 / 15 ms
(about 66.67ms). After normal TUI unmount, at most one stderr line reports
[tui-render] slow frames: …; max: …ms; budget: 66.67ms. No line is emitted
without slow samples. Collection uses constant memory and performs no I/O
while Ink owns the terminal: stderr writes during onRender can trigger Ink
frame restoration. This opt-in diagnostic measures Ink render time, not
end-to-end latency or achieved FPS; abrupt termination may lose the summary.
No runtime performance improvement has been benchmarked.
Set DEV_AGENT_TUI_ALT_SCREEN=1 to opt into the terminal's alternate screen buffer
for interactive Ink sessions. When stdout is a TTY, Ink 7.1.1 enters the
alternate buffer before the first frame and restores the primary buffer after
unmount, preserving the user's normal scrollback while the full-screen TUI is
active. The default is off, and pipes/non-interactive modes never enter the
alternate screen. Normal shutdown restores the primary screen; uncatchable hard
termination such as SIGKILL cannot run cleanup and cannot guarantee
restoration. The repository still keeps src/ink/alternate-screen.ts as a
bounded compatibility/test helper, but production runtime ownership now stays
with Ink's native alternateScreen render option.
Mouse wheel and click support targets the SGR 1006 protocol that modern
terminals answer. Ink strips the leading ESC byte from legacy X10 mouse
reports, which makes a bare [M prefix indistinguishable from composer text;
that ambiguous compatibility path is therefore off by default. Terminals that
only implement X10 reports can set DEV_AGENT_LEGACY_MOUSE_X10=1 to re-enable
it; the parser still guards text with a button-byte whitelist, same-event
text preservation, and a bounded pending timeout. While :editor suspends the
TUI for an external editor, mouse tracking is disabled for the duration and
re-enabled after Ink reclaims the terminal, so the editor never receives
stray mouse report bytes.
Installed CLI and external projects
After installing the package (or after placing a local package tarball in an npm prefix), run the binary directly from the target project:
npm install -g @agent_cli/cli
cd /path/to/other-project
dev-agent --version
dev-agent --tools
dev-agent --cwd /path/to/other-project --index . --json
dev-agent --session other-project --once "list files in the current directory"
dev-agent --acp --cwd /path/to/other-project
dev-agent init --cwd /path/to/other-project --gitignore
dev-agent config validate --cwd /path/to/other-project --project-state --json
dev-agent config show --cwd /path/to/other-project --project-state --json
# Explicitly inspect and install the managed Rust runtime when sandboxing is needed
dev-agent runtime status --runtime-version 0.2.0 --json
dev-agent runtime install --runtime-version 0.2.0 --runtime-release 0.2.2
dev-agent --executor rust-sandbox --runtime-version 0.2.0 --once "list files"
# Provider-free CI workflows
dev-agent review --cwd /path/to/other-project --json --non-interactive
dev-agent plan --cwd /path/to/other-project --changes-file changes.json --plan-file plan.json --session ci --json --non-interactive
dev-agent apply --cwd /path/to/other-project --changes-file changes.json --plan-file plan.json --session ci --json --non-interactive
# Provider/model and project-index management are provider-free
dev-agent providers status --cwd /path/to/other-project --json
dev-agent models current --cwd /path/to/other-project --json
dev-agent mcp list --cwd /path/to/other-project --project-state --json
dev-agent mcp status --cwd /path/to/other-project --project-state --json
dev-agent index refresh --cwd /path/to/other-project --json
dev-agent index status --cwd /path/to/other-project --jsonindex status accepts both current v1 file signatures and legacy signatures
that contain only mtimeMs and size. New index writes include ctimeMs;
legacy entries without ctimeMs remain readable but are not reused as cache
hits unless a content fingerprint is available.
The managed runtime is opt-in. runtime status, runtime path, and runtime remove
only inspect or change the local cache; they do not load a model provider or connect to
MCP. runtime install downloads only the fixed release manifest and the matching
macOS/Linux artifact, verifies its SHA-256 and runtime protocol/version, then installs
atomically under ~/.dev-agent/runtimes/<version>/<target>. The manifest response is
read with a streamed bounded reader at 1 MiB and the archive response with a streamed
bounded reader at 16 MiB; either over-limit response is rejected with a structured
DOWNLOAD_FAILED result before staging or extraction. The decompressed tar payload is
bounded at 32 MiB before extraction. Install
and remove for the same runtime version are serialized, so a concurrent remove waits until
the active installation completes before acting. It
never runs from npm postinstall and never silently falls back to local. Windows,
Linux musl, and unknown platforms return a structured unsupported result.
dev-agent review is read-only and provider-free. It reports changed-file metadata from the working tree; use --base <ref> --head <ref> for a Git range. dev-agent plan accepts a JSON array of filesystem mutation inputs, writes only a metadata-only plan document, and never changes the workspace. dev-agent apply requires both the plan document and the original --changes-file; it rechecks the workspace preimage before applying. Plan and apply input files have a fixed 16 MiB read limit: both commands check the changes file size with stat before parsing, and apply also checks the plan file size before reading. Oversized files return a stable invalid_input_size config error. Use --event-stream for newline-delimited, versioned, redacted workflow events. CI workflows use stable exit codes: 0 success, 2 findings, 3 policy denied, 4 config error, 5 runtime unavailable, 6 execution error, and 64 usage error.
dev-agent init creates .dev-agent/config.json and .dev-agent/sessions
without overwriting existing files. Add --gitignore only when you want the
command to append an idempotent .dev-agent/ rule to the project .gitignore;
add --dry-run to preview the initialization. config validate checks the
JSON structure and supported provider, approval, validation, pricing, and MCP
fields without loading a provider or executing an MCP command. config show
prints the effective project configuration with sensitive-looking values
redacted. config validate/show check the file size with stat before reading;
a file above the fixed 1 MiB limit returns a stable config_read_error
without loading its bytes. Both config commands support stable --json output.
pnpm cli is a workspace-only developer command; it is not required by an
installed user. See docs/release-cli-npm.md
for clean-install checks, npm scope/version preflight, config/session isolation,
and the optional Rust sandbox boundary.
Options:
--once <prompt>- run a single prompt and exit; persisted history is bounded to 4,000 characters by default and individual tool results to 6,000 characters so one-shot runs do not overflow small model contexts. SetDEV_AGENT_MAX_CONTEXT_CHARSormaxContextCharsto override the history bound. One-shot runs also stop after the same tool failure repeats twice, returningtool_loop_detectedinstead of spending the remaining turns on an unchanged call. If the turn limit is reached normally, the CLI makes one no-tools finalization call so the answer can report verified evidence instead of ending with a bare budget error.--cwd <path>- use an explicit working directory for filesystem, shell, git, search, code-search, MCP, and validation operations; the directory must exist. Filesystem, search, code-search, and MCP child-process paths are bounded to this project directory; shell and git remain command-level operations with the host permissions of the selected executor--config <path>- read a config file, resolving a relative path from the final working directory selected by--cwd--project-state- opt in to project-scoped defaults: config uses<final-cwd>/.dev-agent/config.jsonand sessions use<final-cwd>/.dev-agent/sessionsunless an explicit config/session path is set; this does not migrate or rename existing user-level sessions--session <id>- use a separate persisted memory session--resume <id>- continue an existing persisted session; it is rejected when combined with--session--reset-memory- clear the selected memory before running--tools- list registered tools and exit--metadata- print metadata for the selected session and exit, including the accumulated token usage and a metadata-onlyevidenceSummarywhen the session has any--session-list- list saved sessions, newest first. The JSON form is an object withsessions,truncated, andtotal; it keeps the newest 256.jsonsessions, counts all matching entries, and includes each session's accumulatedusage(nullwhen it never reported tokens) and its metadata-onlyevidenceSummary--cleanup-evidence- explicitly prune metadata-only evidence for the selected session and exit. Optional--max-validations <n>,--max-change-sets <n>, and--remove-rolled-backcontrol the cleanup; limits must be positive integers no greater than 10000. Applied change-set guards are always protected. The command never reads or changes workspace files, and--jsonreturns removal, protection, remaining-count, andevidenceSummaryfields--export-evidence- print a versioned, read-only metadata-only audit snapshot as JSON without loading a model provider or MCP server. Optional--change-set-id <id>,--validation-id <id>, and--status passed|failed|skipped|blockedfilter the snapshot. Optional rejection-only limits are--audit-max-validations <n>,--audit-max-change-sets <n>,--audit-max-files <n>, and--audit-max-bytes <n>. The limits must be positive integers within the agent-core caps (10,000 validations, 10,000 change sets, 100,000 files, and 10 MiB / 10,485,760 bytes); an over-limit complete snapshot exits non-zero and writes only a structured metadata error to stderr, never a partial JSON snapshot. The projection excludes commands, arguments, cwd, output, errors, diffs, patches, file bytes, before-images, and absolute working-directory paths;--jsonmay be combined for scripting consistency--preview-evidence- print a separate, metadata-only preflight JSON object without loading a model provider or MCP server. It accepts the same--change-set-id <id>,--validation-id <id>, and--status passed|failed|skipped|blockedfilters as--export-evidence, and returns onlyschemaVersion,sessionId,generatedAt,validationCount,changeSetCount,fileCount, and canonical UTF-8serializedBytes. It is for choosing a later export limit; it does not return evidence records, commands, paths, output/errors, file bytes, before-images, pagination fields, or recovery/execution authority. It cannot be combined with--export-evidence,--cleanup-evidence,--audit-max-*, or another command operation such as--once,--index,--compact,--session-delete,--session-rename,--reset-memory,--mcp-server,--tools,--doctor, or--check-rust.--compact <n>- compact the selected session, keeping thenmost recent turns--no-stream- print only the final answer instead of streaming tokens--executor <local|rust-sandbox>- explicitly select the local executor or an already-installed managed Rust runtime;rust-sandboxfails if no matching runtime is installed--runtime-version <version>- select the managed Rust runtime identity when used with--executor rust-sandbox--runtime-release <version>- select the GitHub release that carries the runtime manifest and archive duringruntime install. The published 0.2.2 carrier is0.2.2and is the built-in default when the flag is omitted; it may differ from--runtime-version, and the local cache remains keyed by the runtime identity--runtime-dir <path>- override the managed runtime cache directory for the current invocationruntime status|install|path|remove- inspect, install, locate, or remove a managed Rust runtime; add--target <target>to scope lifecycle operations--rust-executor <path>- run tools through the Rust sandbox runtime binary--check-rust [path]- send a health check to the Rust runtime binary--mcp-server- run as an MCP server over stdio instead of starting the agent, exposing the built-in tools, thedev-agent://sessionanddev-agent://workspaceresources, and thereview-changes/explain-codebaseprompts to a host agent (no model provider needed).--approval(and the config file'sapprovalsection) also applies here: withdeny-dangerousa flagged call comes back asisErrorwith the reason instead of running,askbehaves the same because MCP has no prompt channel, andreview-writesprepares the same change set but refuses filesystem mutations because stdio has no interactive reviewer.--acp- serve the single-agent runtime through ACP v1 over newline-delimited JSON-RPC on stdio. It requires the selected model provider, keeps stdout protocol-only, sends diagnostics to stderr, and supportsinitialize,session/new, text-onlysession/prompt,session/cancel, streamedsession/update, and client-side permission requests. Keep the transport open until the response for each request arrives.--a2a- serve the single-agent runtime through A2A v1.0 over HTTP. The default loopback endpoint ishttp://127.0.0.1:4320/;--hostand--portoverride the bind address. Discovery is available at/.well-known/agent-card.json. The JSON-RPC methods areSendMessage,SendStreamingMessage,GetTask,CancelTask, andListTasks; streaming uses SSE. A2A mode keeps stdout empty, writes only a short startup diagnostic to stderr, reuses the selected provider, tools, memory, approval, validation, MCP context, and executor, and does not start Ink rendering. Requests must includeA2A-Version: 1.0; loopback is unauthenticated, while a non-loopback--hostrequiresDEV_AGENT_A2A_TOKENandAuthorization: Bearer <token>. Provider reasoning is not forwarded to A2A clients.--approval <mode>- tool approval policy:allow(default, everything runs),deny-dangerous(block the built-in dangerous command patterns and writes outside the working directory),ask(same detection, but confirm withy/N/afirst:yruns once,aruns and remembers the command + subcommand key for the rest of the session (npm testalso coversnpm test -- --watch), orreview-writes(show the real filesystem diff and apply it only after an explicity; dangerous shell/git calls keep their existing approval rules; EOF or a read failure denies it). Approval input before the first newline is capped at4 KiBof UTF-8 bytes; an oversized answer denies the tool without echoing the answer and closes a non-TTY input stream.--json- machine-readable output for--once,--tools,--metadata,--session-list,--compact, and--cleanup-evidence; implies--no-streamso nothing else is written to stdout. Prompt results include structuredreviews,validations,changeSets, and metadata-onlyevidenceSummaryfields. Argument-validation, provider-startup, and agent-run failures (whether returned asstatus: "error"or raised before a result exists) emit one{ "error": "..." }document on stdout and exit1; human-readable invocations keep their existing stderr errors. Evidence preview/export keep option errors on stderr so a successful JSON artifact can never be mixed with an error document.--doctor- check the environment (Node version,rg,protoc, the Rust runtime binary, the provider API key,~/.dev-agent/config.json, and the session directory); a missing config is fine, while malformed JSON is reported as a warning instead of being silently ignored. Exits 1 when any check fails. Combine with--jsonfor{ checks, summary }. The runtime metadata reports the selected source and, without paths, a successful runtime identity or a stable managed-cache diagnosis (target/state/reason). Doctor caps external command-version output at64 KiB, Rust probe frames at8 MiB, and Rust probe stderr at16 KiB.--doctor --check-update- add a bounded, read-only npm registry check for@agent_cli/cli. It reports whether the installed CLI is current or a newer version is available; network or registry failures become a warning, and it never installs or changes npm configuration. Combine with--jsonto keep the result machine-readable.--session-delete <id>- delete a stored session file; a missing session is reported (deleted: falsewith--json) without failing--session-rename <old> <new>- rename a stored session; refuses to overwrite an existing one and reports{ from, to, renamed }with--json. Renaming a session to its own name is a no-op when the session exists ("already has that name") and still reports "not found" when it does not--index <path>- scan a directory and write a symbol index to<path>/.dev-agent/index.json; scans TypeScript/JavaScript/Python/Rust up to depth 8 and skips vendored, build, cache, and virtual-environment directories. Root.gitignoreand.ignorerules are applied before explicit--excludepaths. Unchanged files are reused from the previous index instead of re-read.--jsonreports counts, languages, cache reuse, warnings, and update time.providers list|status|test- list supported providers, inspect credential/base-URL presence, or run an explicitly requested bounded connectivity probe.testnever runs by default from a normal agent request and reports stable reasons without returning keys or response bodies.models list|current- show configured model candidates and the effective provider/model selection without starting a provider.--profile <name>and--alias <name>select project profiles; explicit--provider/--modelflags win.setup- write the selected provider and model to the user config, or to the project config with--project-state. It preserves unrelated settings and never stores provider credentials; use the corresponding environment variable for API keys.mcp list|validate|status|test|health- inspect MCP metadata and configuration.listandvalidatenever start a server;status,test, andhealthperform a bounded capability probe and return only names, counts, latency, and stable failure reasons. No install script is run automatically.mcp add --name <name> --command <command> [--arg <value>] [--env KEY=VALUE]- add a named stdio server to the selected config.
--arg,--env, and--timeout-msare supported; the config is written atomically with restrictive permissions and environment values never appear in output.
- add a named stdio server to the selected config.
mcp remove --name <name>- remove one named server without starting it. Removing an unknown name returns a stable nonzero config result.mcp enable|disable --name <name>- toggle one named server without changing its launch settings. Disabled entries stay visible inlistbut are not probed.mcp templates- list bounded, safe MCP launcher templates.index status|refresh|clear- inspect a metadata-only index status, refresh the project index (optionally with--index-fileand repeated--exclude), or clear one index file.clearrequires--confirm;--dry-runnever deletes.--profile <name>/--alias <name>- choose a configured model profile or alias for a normal agent run. Fallback changes only whenfallback.enabledis exactlytrue.--max-turns <n>,--max-tokens <n>,--max-duration-ms <n>,--max-output-chars <n>- fail closed before the next model/tool call when the selected per-run budget is exhausted.--version/-v- print the CLI version
Arguments are validated before anything else runs: an unknown flag, a flag that
is missing its value, and a stray positional argument all exit 1 instead of
being ignored. Human-readable invocations receive the message on stderr;
--json invocations receive one parseable { "error": "..." } document on
stdout. This matters because a typo used to fall through to interactive mode
(--nope), send the next flag as the prompt (--once --json), or consume it
as a session id (--session --once).
Running without --once starts an interactive session. Each prompt continues
from the previous run, so [state=… turns=…] counts the whole session and
[usage] accumulates instead of reporting one prompt at a time. In a real terminal
(where both stdin and stdout are TTYs), the CLI uses a rich presentation with a
welcome panel, Signal Loom mark, provider/model/executor status, a bordered
multiline editor, separate user and assistant sections, live Markdown-aware
streaming, command completion, and live tool cards. The launch state is always
READY; transport capability (streaming) is shown separately from the active
run state. During a request, the status rail moves through THINKING,
STREAMING, tool-running, approval, and validation states, and rapid tokens are
coalesced into bounded live redraws.
The only rich renderer is Ink 7.1.1 with React 19. Interactive TTY input, cursor
movement, queueing, and redraws all go through the Ink composer; there is no
second raw-ANSI editor path.
Prompts submitted while a run is active stay in a passive waiting queue; only
the active composer accepts editing input, and each streamed answer remains
attached to the prompt that produced it.
When the transcript is longer than the active terminal viewport, use the mouse
wheel or PageUp/PageDown to browse turns, Home for the oldest content,
and End to return to live output. While browsing, the latest task prompt is
kept as a one-line header at the top, and a ↓ Back to bottom · End latest
affordance stays directly above the status, composer, and working-directory
footer. New streamed content stays below the current view and is marked as new
output; submitting a prompt returns to the bottom automatically.
Rich interactive commands accept either / or : prefixes:
/help/:help, /clear/:clear, /model/:model,
/history [count]/:history [count],
/search <query>/:search <query>, /theme [name]/:theme [name],
/export [markdown|json]/:export [markdown|json], and :retry in Ink mode,
/sessions [query]/:sessions [query], /resume <query>/:resume <query>,
/cards/:cards, /collapse/:collapse, /expand/:expand,
/validate <changeSetId>/:validate <changeSetId>,
/autofix [1-3]/:autofix [1-3], and /quit/:quit;
exit and quit remain accepted aliases. Tab completes a command or selects a
workspace path after @, Shift+Enter
inserts a newline, Escape dismisses the palette or exits an idle session when the
palette is closed, Ctrl-L redraws the rich surface, and Ctrl-C cancels the
request in flight or exits an idle session. Terminals that render Ctrl-C as
visible ^C are handled as cancellation input as well.
Prompts can attach bounded workspace context without changing the visible
request: @src/index.ts includes one file, @src includes a bounded directory
snapshot, and @git diff includes the current Git diff. References are resolved
only inside the selected working directory; missing or unsafe references are
reported instead of being sent to the model. Context is passed as a separate
reference-data block, with file, file-count, and total-size limits.
Path completion only scans bounded, workspace-relative entries and skips
.git, .dev-agent, node_modules, caches, and unsafe symlinks. It never
reads a file merely to show a completion.
Use :plan <request> (or /plan <request>) to run a read-only planning pass.
Plan mode permits action-aware built-in inspection and explicitly classified
read-only tools; unclassified tools fail closed before execution. MCP actions
always require confirmation, while the local resource and prompt read wrappers
are classified separately from server-provided action tools.
Use :apply (or /apply) to review a confirmation prompt and run the same
request normally; declining keeps the plan and changes nothing. :clear
discards any pending plan.
Use :team <request> (or /team <request>) to execute a bounded specialist
task graph in isolated Git worktrees. Independent tasks run in parallel,
dependent tasks wait for their prerequisites, and the Ink task board shows
queued, running, retrying, completed, failed, blocked, and cancelled states.
The current working tree is not changed during execution.
Use :team plan <request> for the original read-only specialist planning flow.
After a successful execution review, inspect the task diff summary and use
:team apply to confirm and merge it into the current working tree.
Use :team retry <taskId> to rerun one failed task and :team cancel [taskId]
to cancel one task or the whole team run. Merge conflicts fail closed and never
fall back to a forceful apply.
Before each :team execution, the CLI shows every normalized task and asks you
to select its exact tool names. Enter all, none, or a comma-separated list;
blank input or Escape cancels the run. A final summary asks you to confirm the
complete plan and all scopes before any task workspace is created. Retrying a
task starts a fresh authorization review. Planner-provided capability fields
are ignored.
An optional collaboration.toolAllowlist config value narrows the tools you
may choose for every :team task. It must list exact, currently registered
tool names (built-in or configured MCP tools), with at most 256 unique entries.
Omit it to make all registered tools available as candidates; every task still
requires explicit authorization. An empty list leaves none as the only
choice. This ceiling does not bypass approval or sandbox policy. The review is
limited to 8 tasks and a 128 KiB aggregate prompt budget. Invalid selections
are retried at most four times, then fail closed. The deprecated
collaboration.reviewTaskToolScopes setting is accepted for compatibility but
ignored; setting it to false does not disable per-task authorization.
collaboration.roles- optional list of 1–12 named specialists for:team plan, replacing the built-in architect/reviewer/tester roles. Each role has a stable lowercaseid, boundedinstructions, optionalprovider/model, an optional exact-nametoolAllowlist, and optionalbudgetlimits (maxTurns,maxTokens,maxDurationMs,maxOutputChars). A role's tools are always intersected withcollaboration.toolAllowlistand the active tool registry; role config cannot bypass plan-mode restrictions, approval, or the executor sandbox. Planner-generated task role labels do not select grants. Invalid role config is rejected before the agent starts.:exportwrites the persisted session to.dev-agent/exports/<session>-<timestamp>.md; use:export jsonfor a machine-readable export. Export content is bounded, sanitized, and redacted. When an Ink run fails, the workbench shows a retry card: pressr, use:retry, or press Escape to dismiss it. Retrying reuses the last prepared prompt without putting a duplicate prompt into the composer. MCP tools that report progress update one live card in place with a progress bar and percentage when a total is available.:theme signal|mono|emberchanges the Ink palette and persists it. By default the preference is stored in~/.dev-agent/config.json; with--project-stateit is stored in<final-cwd>/.dev-agent/config.json.DEV_AGENT_THEMEtakes precedence over the saved value.:cardsprints the latest live card, while:collapseand:expandappend a compact folded or expanded view of the latest card without changing the underlying session state. The rich presentation is intentionally disabled for pipes, CI,--json,--once,--mcp-server, and other non-TTY paths, which keep the stable line-oriented or JSON contracts. Use:validate <changeSetId>for a guarded validation rerun, or:autofix [1-3]to repair the latest failed/blocked validation with a bounded reviewed agent loop. The default is two attempts; each repair is followed by a new automatic validation when the repair creates a change set, otherwise the original change set is rerun through the trusted guard. The loop never replays the original user prompt, never includes raw tool output or absolute paths in the repair prompt, and stops after the requested attempt budget. Use:cleanup [--remove-rolled-back] [--max-validations N] [--max-change-sets N]for explicit metadata-only evidence cleanup. Cleanup reports removed validations, removed change sets, protected applied guards, remaining counts, and the current retention summary; it never executes a command or touches workspace files.
Guarded GitHub workflow
Interactive sessions include a small, argument-safe Git workflow for the common branch/commit/push/pull-request path:
:branch
:branch create feature/parser-fix
:commit --all "fix parser validation"
:push origin feature/parser-fix
:pr --base main "Fix parser validation"/branch, /commit, /push, and /pr aliases are accepted as well. :branch
only reads the current branch, changed-file count, and the origin remote.
Branch creation, commits, pushes, and pull requests always ask for explicit
confirmation. Commits run git diff --check, refuse worktrees containing
secret-looking files such as .env or private keys, and only stage the whole
workspace when --all is present. Push and PR commands require a clean worktree;
PR creation additionally requires an authenticated GitHub CLI session and an
upstream branch. The CLI never adds --force, never sends shell command strings
to a shell, and does not print gh auth status output or tokens.
Confirmed project memory
Project-scoped memory is explicit and separate from the conversation transcript:
:memory
:memory add --source "team docs" --confidence high "Run focused tests before the full suite"
:memory search "focused tests"
:memory forget memory-1234The slash aliases are accepted. Listing and searching are read-only. Adding or
forgetting a record asks for confirmation; each saved record has a bounded note,
source, confidence (low, medium, or high), creation time, update time, and
a short opaque id. Notes are redacted for credential-shaped values before they
are stored or displayed, and the store never saves raw tool output or the full
conversation prompt. By default the store is kept in the user state directory
under a project hash; --project-state stores it at
.dev-agent/project-memory.json, and DEV_AGENT_PROJECT_MEMORY_FILE can select
a bounded test or deployment location.
Ctrl-C cancels the request that is in flight (through the same abort path the
desktop uses) and exits with status 130; it also exits immediately when the CLI
is idle at the prompt.
When a request is still running, a prompt submitted with Enter is retained in the session's waiting queue. It is displayed as a passive message block rather than another active editor, and the next model request starts only after the current response has completed. The active blue editor and its working-directory footer remain at the bottom, while each streamed answer stays attached to its own submitted prompt. As soon as a prompt is submitted, its user message is committed to terminal scrollback; the live repaint region is reserved for the current answer and status, so the prompt is not duplicated or left in a tall blank viewport.
For a deterministic plain-text transcript with no terminal control sequences, use
a pipe, --once, or --json. Human-readable model/tool text is sanitized before it
reaches the terminal and obvious credential-shaped values are shown as [redacted];
--json preserves the original successful model data and relies on JSON escaping for control
characters. Provider error response bodies are treated as untrusted input: credential-shaped
fields are redacted and the diagnostic body is bounded before it reaches agent memory or a JSON
error document. Error response reads stop at 16 KiB; an over-limit error response is
reported as stable metadata and its stream is cancelled.
Non-streaming provider success JSON is read with a streamed bounded JSON reader at 16 MiB;
an over-limit response is rejected and its reader is cancelled before provider schemas are parsed.
Provider streaming lines are buffered with a fixed 1 MiB limit; an over-limit line
cancels its stream and rejects before the unbounded buffer can grow.
Across one streamed provider response, text, reasoning, and accumulated tool-call
fragments share a 16 MiB UTF-8 budget. The provider reader is cancelled before
the next fragment can exceed that budget, and the caller receives a stable
stream_output_limit error.
When a provider emits an explicit reasoning channel, rich Ink mode renders it as a
separate Thinking transcript entry while the status rail animates the blue
breathing thinking marker. This displays provider-emitted deltas only; it does not infer or
reconstruct hidden reasoning. Ollama requests enable think: true, while
OpenAI-compatible, Anthropic, and Gemini streams use their native reasoning
fields when the selected model returns them.
Filesystem writes, edits, patches, and reviewed postimages are capped at
16 MiB of UTF-8 content before a file is changed or a diff is retained.
NO_COLOR=1 disables color ANSI in Ink TTY mode, but cursor movement and clear-line
sequences required for live redraw remain.
Managed Rust runtime
The npm package contains the JavaScript CLI only. Runtime installation is explicit:
dev-agent runtime status --runtime-version 0.2.0 --json
dev-agent runtime install --runtime-version 0.2.0 --runtime-release 0.2.2 --json
dev-agent runtime path --runtime-version 0.2.0 --json
dev-agent runtime remove --runtime-version 0.2.0 --jsonThe runtime identity and the release that carries its manifest/archive are
normally separate concepts. When adding a new runtime identity without a new
GitHub release, pass both flags so installation can find the manifest in the
known release while storing the installed binary under the requested runtime
version. In this repository, pnpm runtime:smoke also runs the additional
package-install and isolated runtime lifecycle check.
If the host routes GitHub traffic through HTTP_PROXY/HTTPS_PROXY, enable
Node's environment proxy support before downloading:
NODE_USE_ENV_PROXY=1 pnpm runtime:smoke -- --skip-build--skip-build is appropriate only after the workspace has already been built.
A normal run remains local unless --executor rust-sandbox or the legacy
DEV_AGENT_RUST_BINARY/--rust-executor path selects the Rust executor. Managed
runtime selection is fail-closed: an absent, corrupt, unsupported, or protocol/version
mismatched runtime stops before the model provider or tools start.
Optional manifest signature verification
Managed runtime installs can additionally verify the release's Ed25519
manifest signature. Set DEV_AGENT_RUNTIME_MANIFEST_PUBLIC_KEY to the
release signer's SPKI PEM public key; every downloaded manifest must then
match its dev-agent-runtime-manifest.sig sidecar (signature over the
canonical manifest form), and installs fail closed when the signature is
missing, corrupt, or produced by a different key. Without the variable, the
previous checksum-only trust model applies unchanged. The signing private
key stays with the release process; the CLI never accepts it at runtime.
The full key ceremony lives in
docs/runtime-manifest-signing.md.
When Rust sandbox execution is selected, built-in Shell and Git calls use a
workspace-write profile limited to the current working directory, while Search
uses a read-only profile with network disabled and no writable paths. The same
profiles apply to direct built-in tool calls exposed by --mcp-server.
Filesystem mutations continue to use the existing path, approval, and change-set
guards. Local execution remains the default, and Rust runtime child processes
are closed when the CLI session ends.
Human-readable agent runs print the resolved runtime before the first prompt or
--once request:
[runtime] provider=ollama model=qwen3:4b-instruct streaming=enabledAfter each request, the CLI prints the time to the first visible token and the total agent-run duration:
[timing] queue=3ms first-token=418ms model=690ms tool=12ms total=962ms--no-stream reports first-token=n/a because it intentionally waits for the
final answer. --json does not add these display lines to stdout; it continues
to emit one parseable JSON value.
When an MCP tool reports progress, human-readable runs print one line per
update, for example [tool-progress] files:download 4/10 (or just the current
number when the server omits total). Machine-readable --json runs never
mix these lines into stdout; they continue to emit one parseable JSON value.
MCP server mode speaks newline-delimited JSON-RPC on stdio; every frame on stdout is a protocol message, so logs (if any) go to stderr:
node apps/cli/dist/cli-entry.js --mcp-serverHosts may cancel an active tools/call by sending the MCP
notifications/cancelled notification with its request id. The CLI forwards
that cooperative cancellation signal to built-in tools, including the shell
executor, so a stopped host request does not remain blocked on a running
command.
ACP mode uses the same stdio boundary for agent sessions rather than exposing tools directly:
dev-agent --acp --cwd /path/to/other-projectThe ACP host owns session/new and prompt sequencing. Each ACP session gets
its own AgentLoop context and memory handle, while provider streaming,
tool progress, usage, approval requests, cancellation, and terminal stop
reasons are emitted as ACP updates. ACP mode does not render the Signal Loom
TTY surface, and it does not add A2A or a second configuration format.
A2A mode is an HTTP task boundary rather than an IDE session boundary:
dev-agent --a2a --cwd /path/to/projectAnother local agent can discover the card, send a text task, subscribe to
streaming task updates, query a bounded task record, or cancel an active task.
The first implementation is intentionally local-first: it provides bearer
authentication for explicit non-loopback binds but does not provide OAuth,
push notifications, signed cards, gRPC, or multi-agent delegation. Include
A2A-Version: 1.0 on JSON-RPC requests. Task metadata excludes provider
errors, workspace paths, raw tool inputs, credentials, and provider reasoning.
Reviewed filesystem writes
Use review-writes when a human should see the exact file changes before the
agent writes them:
pnpm cli --approval review-writes --once "update the README"The CLI prepares a change set before approval and prints its id, file paths,
addition/deletion totals, and unified diff to stderr. y applies the prepared
change set; any other answer leaves the original bytes unchanged. A reviewed
write is always decided per change set, so it never inherits the ask mode's
"always allow" memory. With --json, stdout remains one parseable JSON object
and the reviews field records the structured review outcome. In
--mcp-server mode there is no reviewer channel, so --approval review-writes
denies filesystem mutations safely rather than applying them.
Filesystem reads, write/edit snapshots, edit, and patch use a fixed 16 MiB
whole-file read limit. An oversized file rejects before its bytes enter memory,
and mutation targets remain unchanged.
Persisted memory files use the same fixed 16 MiB read limit; oversized session
files are rejected before their bytes enter memory. Memory file writes enforce
the same 16 MiB write limit; an oversized write is rejected before the file
changes, so a session cannot outgrow its read limit.
Rollback checks created directories with early-exit opendir() iteration. It
stops at the first unexpected entry, preserves the existing postimage conflict
message, and leaves the unexpected file untouched when rollback is denied.
Change-set validation
After an approved review-writes apply, the CLI derives safe checks from the
actual changed paths and runs them through the same executor as the built-in
tools. Human mode reports each check with its status, id, duration, and
structured command summary:
[validation] passed: validation passed: 1 passed
[passed] workspace:diff-check (12ms) — git diff --check -- target.mdA failing check means the change was applied, but validation failed. The
CLI keeps the changed bytes on disk and does not automatically roll them back.
Use the review's change-set id with a caller that supports the guarded rollback
operation if an explicit Undo is wanted. Non-Git workspaces and unknown-only
changes are reported as skipped instead of attempting an unsafe Git check.
With --json, stdout remains one JSON value and includes the complete
validations DTO plus a changeSets evidence summary. The change-set summary
is metadata-only: it includes identity, session/workdir binding, file hashes and
stats, existence, state, and timestamps, but never includes a diff, command, or
file bytes.
{
"validations": [
{
"validationId": "validation:<change-set-id>",
"changeSetId": "<change-set-id>",
"status": "passed",
"checks": [
{
"id": "workspace:diff-check",
"status": "passed",
"durationMs": 12,
"command": {
"executable": "git",
"args": ["diff", "--check", "--", "target.md"],
"cwd": "/workspace",
"timeoutMs": 30000
}
}
],
"durationMs": 12,
"summary": "validation passed: 1 passed"
}
],
"changeSets": [
{
"changeSetId": "<change-set-id>",
"sessionId": "docs",
"workingDirectory": "/workspace",
"state": "applied",
"files": [
{
"path": "target.md",
"kind": "file",
"exists": true,
"beforeSha256": "…",
"afterSha256": "…",
"size": 42
}
]
}
]
}Validation failures do not turn a successful apply into a CLI error state;
validation status and apply status remain separate. Ctrl-C still cancels the
active model/tool/validation path through the shared abort signal. MCP server
mode has no validation runner and continues to deny review-writes mutations
when no interactive reviewer is available.
In interactive mode, :validate <changeSetId> explicitly reruns the trusted
checks for an applied change set. The command accepts only the change-set id;
the planner recreates the structured commands and assigns a fresh validation
attempt id. A rerun is serialized with Undo, checks the postimage before and
after execution, and never overwrites or rolls back user bytes. On startup and
before an explicit rerun, the CLI restores persisted applied evidence only after
rechecking the session id, canonical working directory, safe relative paths,
file kinds, existence, and postimage hashes. A valid cross-process restore is a
read-only validation guard, so it can be rerun but cannot be used for Undo;
conflicts or session/workdir mismatches are reported as blocked and leave the
workspace unchanged. Older session memory without changeSets remains readable
and simply reports an empty evidence array.
Configuration is read from the environment:
DEV_AGENT_WORKING_DIRECTORY- default working directory when--cwdis not supplied; precedence is--cwd > DEV_AGENT_WORKING_DIRECTORY > INIT_CWD > process.cwd()DEV_AGENT_CONFIG_FILE- config file path when--configis not supplied; relative paths resolve from the final working directory. With--project-stateand no explicit config path, the project default is<final-cwd>/.dev-agent/config.json; otherwise the legacy user default remains~/.dev-agent/config.json.DEV_AGENT_MODEL_PROVIDER-ollama(default),openai,anthropic, orgeminiDEV_AGENT_MODEL- model name; defaults toqwen3:4b-instructfor OllamaOLLAMA_BASE_URL- optional Ollama base URL overrideOPENAI_API_KEY/DEV_AGENT_OPENAI_API_KEY- required for OpenAIOPENAI_BASE_URL- optional OpenAI-compatible base URL overrideANTHROPIC_API_KEY/DEV_AGENT_ANTHROPIC_API_KEY- required for AnthropicANTHROPIC_BASE_URL- optional Anthropic base URL overrideGEMINI_API_KEY/DEV_AGENT_GEMINI_API_KEY- required for GeminiGEMINI_BASE_URL- optional Gemini base URL overrideDEV_AGENT_MEMORY_FILE- optional JSON memory file path; when set, it bypasses session files. Without it, memory defaults to~/.dev-agent/sessions/default.jsonand--session <id>maps to~/.dev-agent/sessions/<id>.jsonDEV_AGENT_SESSION_DIR- optional directory holding session files; defaults to~/.dev-agent/sessions, or to<final-cwd>/.dev-agent/sessionswhen--project-stateis present. An explicitDEV_AGENT_SESSION_DIRalways wins. Used by--session,--metadata,--compact, and--session-listalike, so sessions written by the CLI are the ones listed.DEV_AGENT_MAX_CONTEXT_CHARS- optional character budget for the conversation
