npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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 cli from this repository;
  • an installed dev-agent binary from any project directory after @agent_cli/cli is 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 cli

Interactive 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 $VISUAL first, then $EDITOR, then vi. The value must name a single executable; it runs through an argument array with shell: false and 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 as code -w).
  • Saving content and exiting the editor loads it into the composer draft for review; nothing is submitted automatically. Ink 7's native suspendTerminal lifecycle 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 --once runs 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.md for project skills;
  • ~/.dev-agent/skills/<name>/SKILL.md for 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 off

Only 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.md for project agents;
  • ~/.dev-agent/agents/<id>/AGENT.md for 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.json for project extensions;
  • ~/.dev-agent/extensions/<id>/extension.json for 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 ────────────────────────────────────────> cancelled

An 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 off

Budgets 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 deep

fast 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=962ms

In --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 20

The /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 deployment

In 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 --json

index 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. Set DEV_AGENT_MAX_CONTEXT_CHARS or maxContextChars to override the history bound. One-shot runs also stop after the same tool failure repeats twice, returning tool_loop_detected instead 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.json and sessions use <final-cwd>/.dev-agent/sessions unless 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-only evidenceSummary when the session has any
  • --session-list - list saved sessions, newest first. The JSON form is an object with sessions, truncated, and total; it keeps the newest 256 .json sessions, counts all matching entries, and includes each session's accumulated usage (null when it never reported tokens) and its metadata-only evidenceSummary
  • --cleanup-evidence - explicitly prune metadata-only evidence for the selected session and exit. Optional --max-validations <n>, --max-change-sets <n>, and --remove-rolled-back control 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 --json returns removal, protection, remaining-count, and evidenceSummary fields
  • --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|blocked filter 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; --json may 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|blocked filters as --export-evidence, and returns only schemaVersion, sessionId, generatedAt, validationCount, changeSetCount, fileCount, and canonical UTF-8 serializedBytes. 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 the n most 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-sandbox fails 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 during runtime install. The published 0.2.2 carrier is 0.2.2 and 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 invocation
  • runtime 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, the dev-agent://session and dev-agent://workspace resources, and the review-changes / explain-codebase prompts to a host agent (no model provider needed). --approval (and the config file's approval section) also applies here: with deny-dangerous a flagged call comes back as isError with the reason instead of running, ask behaves the same because MCP has no prompt channel, and review-writes prepares 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 supports initialize, session/new, text-only session/prompt, session/cancel, streamed session/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 is http://127.0.0.1:4320/; --host and --port override the bind address. Discovery is available at /.well-known/agent-card.json. The JSON-RPC methods are SendMessage, SendStreamingMessage, GetTask, CancelTask, and ListTasks; 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 include A2A-Version: 1.0; loopback is unauthenticated, while a non-loopback --host requires DEV_AGENT_A2A_TOKEN and Authorization: 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 with y/N/a first: y runs once, a runs and remembers the command + subcommand key for the rest of the session (npm test also covers npm test -- --watch), or review-writes (show the real filesystem diff and apply it only after an explicit y; dangerous shell/git calls keep their existing approval rules; EOF or a read failure denies it). Approval input before the first newline is capped at 4 KiB of 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-stream so nothing else is written to stdout. Prompt results include structured reviews, validations, changeSets, and metadata-only evidenceSummary fields. Argument-validation, provider-startup, and agent-run failures (whether returned as status: "error" or raised before a result exists) emit one { "error": "..." } document on stdout and exit 1; 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 --json for { 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 at 64 KiB, Rust probe frames at 8 MiB, and Rust probe stderr at 16 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 --json to keep the result machine-readable.
  • --session-delete <id> - delete a stored session file; a missing session is reported (deleted: false with --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 .gitignore and .ignore rules are applied before explicit --exclude paths. Unchanged files are reused from the previous index instead of re-read. --json reports 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. test never 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/--model flags 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. list and validate never start a server; status, test, and health perform 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-ms are supported; the config is written atomically with restrictive permissions and environment values never appear in output.
  • 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 in list but 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-file and repeated --exclude), or clear one index file. clear requires --confirm; --dry-run never deletes.
  • --profile <name> / --alias <name> - choose a configured model profile or alias for a normal agent run. Fallback changes only when fallback.enabled is exactly true.
  • --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 lowercase id, bounded instructions, optional provider/model, an optional exact-name toolAllowlist, and optional budget limits (maxTurns, maxTokens, maxDurationMs, maxOutputChars). A role's tools are always intersected with collaboration.toolAllowlist and 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. :export writes the persisted session to .dev-agent/exports/<session>-<timestamp>.md; use :export json for a machine-readable export. Export content is bounded, sanitized, and redacted. When an Ink run fails, the workbench shows a retry card: press r, 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|ember changes the Ink palette and persists it. By default the preference is stored in ~/.dev-agent/config.json; with --project-state it is stored in <final-cwd>/.dev-agent/config.json. DEV_AGENT_THEME takes precedence over the saved value. :cards prints the latest live card, while :collapse and :expand append 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-1234

The 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 --json

The 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=enabled

After 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-server

Hosts 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-project

The 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/project

Another 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.md

A 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 --cwd is not supplied; precedence is --cwd > DEV_AGENT_WORKING_DIRECTORY > INIT_CWD > process.cwd()
  • DEV_AGENT_CONFIG_FILE - config file path when --config is not supplied; relative paths resolve from the final working directory. With --project-state and 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, or gemini
  • DEV_AGENT_MODEL - model name; defaults to qwen3:4b-instruct for Ollama
  • OLLAMA_BASE_URL - optional Ollama base URL override
  • OPENAI_API_KEY / DEV_AGENT_OPENAI_API_KEY - required for OpenAI
  • OPENAI_BASE_URL - optional OpenAI-compatible base URL override
  • ANTHROPIC_API_KEY / DEV_AGENT_ANTHROPIC_API_KEY - required for Anthropic
  • ANTHROPIC_BASE_URL - optional Anthropic base URL override
  • GEMINI_API_KEY / DEV_AGENT_GEMINI_API_KEY - required for Gemini
  • GEMINI_BASE_URL - optional Gemini base URL override
  • DEV_AGENT_MEMORY_FILE - optional JSON memory file path; when set, it bypasses session files. Without it, memory defaults to ~/.dev-agent/sessions/default.json and --session <id> maps to ~/.dev-agent/sessions/<id>.json
  • DEV_AGENT_SESSION_DIR - optional directory holding session files; defaults to ~/.dev-agent/sessions, or to <final-cwd>/.dev-agent/sessions when --project-state is present. An explicit DEV_AGENT_SESSION_DIR always wins. Used by --session, --metadata, --compact, and --session-list alike, so sessions written by the CLI are the ones listed.
  • DEV_AGENT_MAX_CONTEXT_CHARS - optional character budget for the conversation