agented (binary: ae)
ae is a stateful editor controlled by command-line verbs. State persists across sessions in a SQLite-backed workspace (.agented/state.db). Every edit is versioned in an undo tree — you can branch, jump to any past state, and never lose work.
Use this tool when
- You need to edit one or more files in a repo across multiple steps and want a durable history.
- You want to leave notes for your future self or other agents (annotations) attached to specific files.
- You want safe multi-file refactors with all-or-nothing semantics (transactions).
Don't use this tool for
- Reading repo overview / architecture (use plain
cat/grep). - Single one-shot edits where you don't care about history (use the platform's built-in editor).
- Anything that isn't text (no binary file support).
How the editor enforces correctness for you
Every read returns a state_token. Pass it to your next write with --expect. If the file changed under you, the write is rejected (exit code 3) and the response includes the new content and the new token. Retry with the new token.
You don't need to "view before write" — the editor will tell you if your assumption is stale. You don't need to "branches before undo" — undo's error includes the branches if there's ambiguity. You don't need to "status before edit" — every operation's response carries the state you'd want to check.
The default is concurrency.require_expect: warn: writes without --expect succeed and emit a stderr warning. For multi-agent setups, set require_expect: writes in .agented/config.json to enforce strict pre-write checks. In either mode, an actual conflict (a stale --expect value) is rejected with exit 3 and the recovery payload — the tree never silently loses work.
Short forms are the default in agent contexts. Long forms exist for documentation and human readers; agent calls should use the shorter form to save tokens. ae s foo.go -r 12:14 -w "..." -x ab12cd34 is the canonical shape, not ae replace foo.go --range 12:14 --with "..." --expect ab12cd34.
For multi-line content, pipe via -i (--from-stdin) or use -f <path> (--text-file). Stdin is auto-detected when piped, so cat patch.txt | ae s foo.go -r 12:14 -i and echo "..." | ae i foo.go -A 0 -i work without quoting tricks.
The first-touch rule
The first time you touch a file in a session, do it through ae open <path>. Not Read, not Edit, not cat. The response is the input to the rest of the session.
ae open returns six things: the file's id, line count, head_edit_id, content_hash, state_token, and any active annotations inline. Treat that response as authoritative. Annotations were left by a prior session for you to read. Read them. The state_token threads forward into your next write. The line_count tells you whether your assumptions about the file's shape match.
ae open <new-path> is also the file-creation primitive. If the path doesn't exist on disk, ae creates an empty file there and registers it in the workspace. No touch first, no --create flag. The new-file flow is ae open foo.go → ae i foo.go -A 0 -i (insert content from stdin) → ae w foo.go. The same call covers "open this existing file" and "create this new file"; the response shape is identical either way.
Skipping this step costs you. If you Read first, the agent runtime has the bytes, but the editor doesn't know you've seen the file. Subsequent writes through ae will treat your context as a fresh actor and may surface conflicts that wouldn't have happened otherwise. If you read via ae open, the editor knows your starting point, your writes thread cleanly, and the workspace history shows your session as a coherent sequence of edits rather than a stranger's drive-by.
The trained habit ("read before write to be safe") doesn't apply here. ae reports drift via full-content rejection payloads. Read once at session start. Edit forward.
Round-trip economy: don't over-fetch
Most LLMs were trained on Read → Edit → Read → Edit and reach for the same shape with ae. Don't. ae's contract eliminates almost every "look first to be safe" round-trip. Specific anti-patterns:
-
Don't
ae viewbeforeae replace/insert/delete. You already know the line range fromae open(orae search). If you're wrong, the write rejects with the current content attached (exit 3); you reconcile and retry. One trip on success, one trip on conflict. View-first burns a trip every time, success or not. -
Don't
ae viewbeforeae search. Search returnsline\tcol\ttextper match. That's the answer. View only after if you need surrounding context. -
Don't
ae loadbefore reading. Auto-load on drift is on by default. Every write verb stat-s the file and reconciles disk changes itself.ae loadis only for the rare case where you specifically want to capture a disk snapshot as an edit without modifying anything else. -
Don't
ae statusjust to refetch a state_token. Every write returns the new token in the result. Thread it forward. The only reason to callae statusis when you explicitly need workspace-level info (open files, dirty flags, current actor). -
Don't
ae openmore than once per file per session. The first-touch rule covers the registration. Subsequent reads/writes auto-resolve the same FileInfo. Auto-open also kicks in transparently if you forget —ae search foo.goregisters the file silently when needed. -
Don't pipe ae output through
| head/| tail/| grep. Every read verb has--range,--limit, or--patternto bound output server-side, and--rangeaccepts Python-slice syntax:1:10for first 10,-10:for last 10,5:-5for middle slice (skip first 5 and last 5),:20for first 20,-50:-20for the lines 50-from-end through 20-from-end. Pipe-trimming after the fact wastes the round trip's setup cost. -
Don't append
2>&1. ae uses exit codes — 0 success, 3 conflict (with full content payload on stdout), 1/2 for errors. Stderr is ae's own diagnostic noise; merging it into stdout means you parse around it. Just read stdout.
Translation: the canonical loop is open → search/find → replace/insert/delete → repeat. Three calls per logical edit, sometimes two. Not five.
What this editor does that Read/Edit/Write can't
These are the operations that motivate reaching for ae over the built-ins.
- Read once, edit forever. Your local model of the file (built from the
ae openresponse and every subsequent edit) is the source of truth. The editor reports drift via full-content rejection payloads, not via "Read before every Write" rituals. Verb:ae open <path>, then any number of writes without re-reading. - Branching tree, not stack. Walking back is
ae undo; jumping to any prior state isae head -e <edit_id>. Both branches stay addressable; the wrong path is never lost. Verb:ae br <path>to list leaves,ae head <path> -e <id>to jump. - Three-way merge. Reconcile two diverged branches with structured conflict responses. Auto-resolve via
--prefer a|b; resolve specific ranges via--resolve start:end=a|b|"text". Verb:ae merge <path> -l <leafA> -l <leafB>. - Atomic batches.
ae applyconsumes JSON-lines on stdin and applies every operation inside one transaction. Replaces N Edit calls with one. Verb:cat ops.jsonl | ae apply <path>. - Atomic move.
ae movecuts a range and inserts it elsewhere — same file or cross-file — in one transaction. No partial-success risk. Verb:ae move <path> --from S:E --to N(same-file) or--to-file <other> --to-line N(cross-file). - Atomic extract.
ae extractcuts a range out of one file and writes it to another, creating the destination if absent and optionally saving both files in one call. The canonical refactor primitive. Verb:ae extract <path> -r S:E --to <new-or-existing> [--to-line N] [--save]. - Regex replace with capture groups.
ae replace --patterndoes sed-style replacement in a single tool call. Verb:ae s <path> -p '<re>' -w '<expansion>' [-L <max>] [-n]. - Range-based addressing. Every write targets a line range or insertion point, not a string. Edit's "string appears multiple times" failure mode doesn't exist here. Verb: any of
ae s/i/dwith-r S:E. - Annotations as cross-session memory. Per-file notes that persist across processes and across agents. Verb:
ae an <path> a -t "..."to write; reading is automatic onae open. - Transactions with auto-rollback.
ae beginopens a logical group;ae commitfinalizes;ae rollbackreverts. Forgotten transactions auto-rollback after the configured idle window. Verb:ae begin / ae commit / ae rollback. - Cross-agent shared state. A Claude Code session and a Codex session both reading the same
.agented/state.dbsee the same head, branches, annotations, and marks. The workspace is the durable thing; the agent identity is incidental.
Reading verbs (idempotent, cheap)
Bound output server-side, always. Every read verb has --limit/-L, --range/-r, or --pattern/-p to cap the result set before it leaves the daemon. Do not pipe through head/tail/grep to truncate; the bytes are already on the wire by then. ae prints a stderr nudge when stdout is piped without a bound flag (silence: AE_NO_NUDGE=1 env or output.nudge_on_pipe: false in config).
| Verb | Short | Args | Output (tab) suffix | Use when |
|---|---|---|---|---|
view | v | <path> [--range S:E] [--raw] | state_token\t<hex> | Inspect a file or range. --range is Python-slice: 1:10 first 10, -10: last 10, 5:-5 middle slice, :20 first 20, 42:50 window. --raw emits verbatim bytes (no line-num prefix, no token) for piping to another tool |
search | / | <path> --pattern <re> | state_token\t<hex> | Find matches; output line\tcol\ttext |
diff | df | <path> [--from N --to M] | unified diff + token | Inspect what an edit changed |
log | - | <path> [--limit N] | tab-delimited audit rows | See history of operations |
branches | br | <path> | id\tts\tactor\tcmd\tis_head | Discover alternative leaves |
list | ls | `[--all | --closed | --stale]` |
status | st | [<path>] | workspace or file summary | Get state_token for next write |
mark get | - | <path> <name> | name\tline\tsnapped\t... | Jump back to a known anchor |
annotate list | - | <path> | id\tts\tactor\tcontent | Recall notes from prior sessions |
show | - | <path> [--edit <id>] [--no-color] | colored, syntax-highlighted unified diff | Display a change to the user. NOT for tool result chains; lean tab format is the default everywhere else |
symbols | sy | [<path>] [--kind <k>] [--pattern <re>] | sym\t<kind>\t<file>:<line>:<col>\t<name> | List symbols (file or workspace). IDE mode only; falls through to lsp_unavailable when daemon is off |
Writing verbs (use --expect <state_token>)
| Verb | Short | Args | Conflict response | Use when |
|---|---|---|---|---|
replace | s | <path> --range S:E --with TEXT --expect TOK | exit 3 + content | Change lines |
insert | i | <path> --after N --text TEXT --expect TOK | exit 3 + content | Add lines |
delete | d | <path> --range S:E --expect TOK | exit 3 + content | Remove lines |
save | w | <path> | - | Write head to disk |
load | e | <path> | - | Reload from disk |
move | mv | <path> --from S:E --to N (or --to-file P --to-line N) | exit 3 + content | Move a range; cross-file dst auto-created |
extract | - | <path> --range S:E --to <new-or-existing> [--save] | - | Refactor a range into a sibling file (atomic) |
Every successful write prints edit_id=<n>\thead_edit_id=<n>\tline_delta=<d>\tline_count=<n>\tstate_token=<hex>. Use the new token for the next write.
Auto-save and auto-load are on by default. Every write verb (replace/insert/delete/move/extract) and history verb (undo/redo/head) flushes the resulting head to disk in the same call. The result includes saved: true to confirm. Before the write, ae stat-s the file: if (mtime, size) match the stamp from our last save, the call proceeds; if disk was touched externally, ae loads the disk content as a new edit on the tree (so external changes are recoverable via ae undo/ae head) and applies your edit on top. The result includes loaded_from_disk: true and drift_reason when this happens.
Config knobs: concurrency.auto_save = clean | off | force (default clean), concurrency.auto_load_on_drift (default true). Env override: AE_AUTO_SAVE=off, AE_AUTO_LOAD_ON_DRIFT=false.
ae save <path> and ae load <path> still exist for granular control. They are not part of the normal write flow.
ae save <path> and ae load <path> still exist for granular control: save flushes head when auto-save was off, load pulls disk content into the workspace as a new edit (useful when an external editor diverged the file). Neither belongs in the normal write flow.
History verbs
ae undo <path> [--count N]— walk head pointer back N edits. Errors with branch info if ambiguous.ae redo <path>— walk forward along the most recently created child.ae head <path> --edit <id>— jump to a specific edit (use afterbranchesshows alternatives).ae branches <path>— list leaf edits (alternatives that exist in the tree).
Worked example: backtracking after a wrong direction
ae view foo.go --range 10:20 # state_token=A1B2
ae replace foo.go --range 12:14 --with "..." --expect A1B2 # state_token=B3C4
ae replace foo.go --range 18:18 --with "..." --expect B3C4 # state_token=C5D6
ae undo foo.go --count 2 # head moves back two; new state_token=A1B2-ish
ae replace foo.go --range 12:14 --with "DIFFERENT" --expect <new> # creates branch B
ae branches foo.go # shows two leaves: original C5D6, and branch B's leaf
ae head foo.go --edit <C5D6_id> # jump back to original branch's leaf
Marks
Marks are named line anchors that survive edits. The editor recomputes a mark's line on every edit (deletes shift it down, inserts shift it up; if a delete includes the mark's line, it snaps to the start of the deletion and the snapped flag is set).
Worked example: mark a return point before a multi-edit refactor
ae open auth.go # state_token=T1
ae mark auth.go add return_point --line 240
ae replace auth.go --range 100:140 --with "..." --expect T1 # state_token=T2
ae mark auth.go get return_point # line is now 100+(new lines)-(40 deleted)
Annotations — durable cross-session memory
Annotations are how a session leaves context for the next one. They live in the workspace, not in your context window. ae open returns active annotations inline so reading them is free — there is no separate "load memory" step.
These four behaviors are mandatory, not optional:
1. On opening any file with annotations, read them before doing anything else. They are the prior session's input to the upcoming task. Parse them, factor them into your plan, reference them when making decisions. Do not skip. Do not skim. They were left specifically because the prior session thought the next session needed them.
ae open auth.go
# response: annotation\t14\t...\tprev-actor\tauth path uses signed cookies; do not weaken
# response: annotation\t15\t...\tprev-actor\trefactor in progress, lines 80-130 half-done
# read both before issuing any edit
2. On finishing substantive work on a file, leave an annotation. "Substantive" means more than three or four edits, or any logical unit of work — "implemented X", "refactored Y", "fixed bug Z". Summarize what was done, what remains open, and any decisions that aren't visible in the code. Skip annotations only for truly trivial fixes (single-line typo, config tweak with no broader implication).
ae an auth.go a -t "implemented refresh-token rotation; remaining: revoke endpoint and key-rotation cron. token storage is at line 47 — do not move without auditing the audit log."
3. On encountering a non-obvious invariant or constraint while reading code, annotate it before moving on. If the next session would benefit from knowing it without re-deriving it, capture it now.
ae an scheduler.go a -t "the dispatcher in run() must remain pure — it's called during init() before the metrics package is loaded"
ae an fixtures_test.go a -t "tests depend on the exact ordering of map iteration in this fixture; do not switch to map-iteration-order-independent assertions"
4. Do not annotate trivia. Don't write "this is a Python file", "this function returns a string", "imports". Don't repeat what a docstring already says. Don't annotate something obvious from three lines of the function. Useless annotations train the next session to skim them — that's how you lose the high-signal ones.
Transactions
ae begin [path] opens a transaction. All subsequent edits attach to it. ae commit finalizes; ae rollback reverts every edit back to the pre-transaction head on each affected file (the reverted edits remain visible in ae log as a closed branch, never lost).
If you forget to commit/rollback, the editor auto-rolls-back idle transactions per transactions.auto_rollback_idle_for (default 10m). You don't need to handle abandoned transactions defensively; the editor cleans up.
Worked example: multi-file refactor with rollback safety on test failure
ae begin # tx_id=42
ae search auth.go --pattern 'oldName\\(' # find call sites
ae replace auth.go --range 12:12 --with "newName(" --expect T1
ae replace auth.go --range 80:80 --with "newName(" --expect T2
ae replace caller.go --range 40:40 --with "newName(" --expect U1
# run tests externally; if green:
ae commit
# else:
ae rollback
Worked examples
1) Read-modify-verify a function
ae view auth.go --range 50:80 # capture state_token=T1
ae replace auth.go --range 60:65 --with "func ..." --expect T1 # state_token=T2
ae diff auth.go # confirm intended change
2) Backtracking (see history verbs section above).
3) Leaving context for the next session
ae annotate auth.go add --text "Migration 0042 must run before this lands; coordinate with infra"
4) Picking up where another session left off
ae open auth.go # response includes annotations and state_token in one shot
# immediately use --expect <returned_token> on the next write
5) Search-then-targeted-edits
ae search foo.go --pattern 'TODO' # state_token=T1; matches show line/col
ae replace foo.go --range 12:12 --with "..." --expect T1 # state_token=T2
ae replace foo.go --range 47:47 --with "..." --expect T2 # state_token=T3
6) Multi-file refactor with rollback (see transactions section).
7) Atomic batch via ae apply
cat <<'OPS' | ae apply auth.go
s 12:12 newName(\n
s 40:40 newName(\n
i 80 // see ADR-0042\n
OPS
# all-or-nothing; on any failure the response identifies the failing op
# and the head is unchanged.
ae apply input formats
ae apply reads operations from stdin in any of three formats. The format is detected automatically; no flag is needed.
Shortform. What you reach for when writing batches yourself.
s 12:14 new content
i 80 header line
d 67:69
m foo 50
Longform. Same density, fuller names. Use when the batch will be reviewed.
replace range=12:14 with=new content
insert after=80 text=header line
delete range=67:69
mark name=foo line=50
JSON-lines. Use when piping from another tool, especially ae find --json.
{"verb":"replace","range":"12:14","with":"new content"}
{"verb":"insert","after":80,"text":"header line"}
{"verb":"delete","range":"67:69"}
The decision rule: shortform when typing it yourself and you want the token economy, longform when the batch goes anywhere a human will read it, JSON-lines when piping from a tool that produces structured output.
Cross-file batches: shortform and longform use @<file> lines as separators. JSON-lines uses a "file" field per line.
State tokens: shortform appends ! <token> at end of line, longform uses expect=<token>, JSON-lines uses an "expect" field.
8) Three-way merge with one resolved conflict
ae br auth.go
# leaves: 47 (refactor branch), 52 (bug-fix branch), head=52
ae merge auth.go -l 47 -l 52
# conflict response shows ranges modified by both branches
ae merge auth.go -l 47 -l 52 -R '20:22=a' -R '47:47=b'
# all conflicts resolved => commit a merge edit; head moves to the new id
9) LSP-driven structural navigation (IDE mode on)
ae find -R HandleAuth # who calls it?
# ref auth.go:47:12 call HandleAuth(ctx, req)
# ref middleware.go:128:8 call HandleAuth(ctx, r2)
# ref test.go:34:5 import HandleAuth
ae find -s HandleAuth # where is it defined?
# def auth.go:47:1 HandleAuth func
ae sy auth.go --kind func # list functions in this file
# sym func auth.go:47:1 HandleAuth
# sym func auth.go:89:1 parseToken
# now do the actual edit, with the line number from the ref output
ae view auth.go --range 47:60 # state_token=T1
ae replace auth.go --range 50:50 --with "..." --expect T1
# response includes diag lines if gopls flags anything new
Reach for these instead of grep when the question is structural ("who calls", "where is X defined", "what does this file expose"). Reach for ae search / ae find (regex) for free-text in comments, strings, TODOs.
IDE mode
When ide.enabled: true is set in .agented/config.json, IDE features are available:
ae symbols [path](shortae sy) lists symbols in a file or workspaceae find --symbol <name>(ae / -s) finds where a symbol is definedae find --references <symbol>(ae / -R) finds all use sitesae find --definition <symbol> --at <file>:<line>:<col>(ae / -D -A) resolves a definition at a cursor position
When IDE mode is on, prefer these over grep/Glob for structural questions: "where is X defined", "who calls X", "what does this file expose". ae find -R Foo is one structured call with usage classification (call/read/write/import/definition); a grep is text-only and forces you to disambiguate matches in your head. The trained grep -rn reflex still applies for free-text searches over comments, strings, and TODOs; structural queries belong on the LSP.
You don't need to manage the daemon yourself. If config has IDE enabled, ae ensures the daemon is running when you invoke any IDE-relevant verb. The first invocation in a session may take a second or two while the LSP starts up; subsequent calls are fast.
The user may instruct you to enable or disable IDE mode for a specific task, overriding the config. Honor those instructions for the duration of the conversation.
When IDE features aren't available (config disabled, no instruction override, daemon crashed), LSP-dependent verbs return:
error lsp_unavailable <reason>
Don't retry; proceed without those capabilities. Don't try to start the daemon yourself unless the user explicitly told you to.
When the user asks why IDE features aren't working, run ae lsp doctor [language] and report what it says. The output is tab-delimited rows of doctor <lang> <check> <subject> <result> <detail> where result is ok | warn | fail | info. Doctor reads-only; it can't fix anything, but the fail and warn rows usually identify the issue (missing binary, missing package.json/Cargo.toml/tsconfig.json, eslint without .eslintrc, pyright without an activated venv).
When IDE mode is active, mutating verbs (ae save, ae replace, ae apply, etc.) may include diagnostic lines in their responses:
ok state_token=ab12cd34 diag warn foo.go:89:4 unused variable x lint diag error foo.go:47:12 undefined: bar compile
Diagnostics are informational. The operation succeeded; the diagnostics report current LSP findings on the file. Decide whether to act on them based on the task.
The absence of diag lines does not mean the file is clean. It means either there are no diagnostics, the LSP hasn't analyzed yet, the language has no LSP configured, or the daemon isn't running. Don't infer file health from absence of diagnostics. If the user asks "is this file clean?", answer "no diagnostics returned" rather than "the file is clean."
A language can run multiple LSP servers. The first listed answers symbol/reference/definition queries; all of them publish diagnostics, tagged by source server in diag lines:
diag error foo.ts:14:3 Cannot find name 'bar'. ts diag warn foo.ts:14:3 'bar' is not defined. eslint
The last column is the source-server label (e.g. tsserver, eslint, pyright, ruff, gopls). When you see two diag lines at the same location with different sources, treat them as independent findings — the type checker and the linter looking at the same code from different angles.
Default server lists (when the language has auto_start: true and the binaries are installed):
go:goplstypescript:tsserver+eslintpython:pyright+ruffrust:rust-analyzer
Severity, kind, and usage vocabularies
| Severity | LSP origin |
|---|---|
error | LSP severity 1 |
warn | LSP severity 2 |
info | LSP severity 3 |
hint | LSP severity 4 |
| Kind | Examples |
|---|---|
func | top-level function |
method | method on a type |
type | type alias, struct |
class | class (Python, TS) |
interface | interface |
var | local or package variable |
const | constant |
field | struct/class field |
module | package or namespace |
| Usage | When |
|---|---|
call | name(...) |
read | name appears, no other label fits |
write | name = ... or name := ... |
import | import statement |
definition | the line that defines the symbol |
other | catch-all |
Errors and recovery
| Error substring | What it means | Next action |
|---|---|---|
state_token mismatch | Head moved or you didn't pass --expect | Use the current_token from the conflict response |
branch ambiguous | undo/redo would have to choose among siblings | Read the branches list in the response, then head --edit |
transaction <id> owned by | Another actor's tx is open; writes are blocked | Wait, or pass --no-transaction to bypass |
transaction auto-rolled-back | The editor reverted an idle tx automatically | Check ae log <path>; the auto_rollback row identifies what was reverted |
mark name exists | Mark name collision | Pick a different name or mark remove first |
file not registered | The path was never ae open'd | Run ae open <path> first, or pass --auto-open |
pattern compile error | RE2 syntax issue | Fix the pattern (Go's regexp syntax) |
range out of bounds | Line range exceeds file | Re-view to get the current line count |
skill out of date | Installed SKILL.md major-mismatches binary | ae skill install |
Anti-patterns
- Reaching for
Read/Edit/Writebecause they're familiar. When editing a file you'll edit more than once, ae is the right verb. Defaulting to the built-ins is path-of-least-resistance, not a technical case. The exceptions are bootstrap (the project doesn't build, ae can't be invoked) and one-shot inspection of files outside any project (~/.zshrc, configuration files in/etc/). Everywhere else, ae. - Discarding
state_tokenbetween calls (forces unnecessary conflicts on every write). - Ignoring the conflict response payload (the new content is right there; use it instead of running
viewagain). - Calling
redoafter intentionally creating a new branch —redowill fail with branch ambiguity. Usehead --edit <id>instead. - Useless annotations ("this is a function").
- Unescaped regex special characters in
searchpatterns. - Skipping the annotations on
ae open. They were left for you on purpose; reading them is free. - Using Read/Edit/Write on a file ae already manages. They bypass the tree, the annotations, and the conflict detection; the agents that share this workspace will see drift they cannot recover from.
- Inferring file health from absence of
diaglines. Diagnostics absence has multiple causes (no findings, LSP not analyzed yet, language not configured, daemon not running). - Trying to start
ae lspyourself unless the user has explicitly authorized it. The auto-start handles it when config says enabled.
Output format reference
All output is tab-delimited (\t) with one record per line.
view: each line<line_num>\t<content>. Trailer (whenoutput.include_state_tokenis on):state_token\t<hex>.search: each match<line>\t<column>\t<text>. Trailer:state_token\t<hex>.replace/insert/delete: a single lineedit_id=<n>\thead_edit_id=<n>\tline_delta=<d>\tline_count=<n>\tstate_token=<hex>.undo/redo/head: a single linehead_edit_id=<n>\tline_count=<n>\tstate_token=<hex>.branches:<edit_id>\t<created_at>\t<actor>\t<command>\t<is_head>.open: header line<file_id>\t<path>\t<line_count>\t<head_edit_id>\t<annotation_count>\t<state_token>. Then for each annotation:annotation\t<id>\t<created_at>\t<actor>\t<content>.status(workspace):workspace\tactor=...\topen_files=.... (file):file\tid=...\tpath=...\tline_count=...\thead_edit_id=...\tstate=clean|dirty\tstate_token=....log:<created_at>\t<actor>\t<command>\t<result>\t<edit_id>.- Conflict response (exit code 3, on stdout):
conflict\tfile_id=...\tcurrent_token=...\thead_edit_id=...\thead_actor=...\tline_count=...thennote\t...then---current-content---then literal content then---end---.
For programmatic parsing, pass --json to any verb and you'll get a stable JSON object instead of tabs.
Verb shortcuts
| Long | Short | Long | Short |
|---|---|---|---|
| view | v | branches | br |
| search | / | open | o |
| replace | s | close | x |
| insert | i | list | ls |
| delete | d | save | w |
| undo | u | load | e |
| redo | r | diff | df |
| mark | m | status | st |
| annotate | an | ||
| symbols | sy | lsp | |
| --symbol | -s | --references | -R |
| --definition | -D | --at | -A |
| --diagnostics | -G | --no-diagnostics | -N |
| --background | -B |
Configuration awareness
The editor's behavior is influenced by the project's .agented/config.json:
concurrency.require_expect:writes|warn|off. Defaultwrites. Determines whether writes without--expectare rejected, warned, or silently allowed.transactions.auto_rollback_idle_for: how long an idle transaction can sit before auto-rollback. Default10m.auto_prune.*: whether and when the editor prunes stale history. You don't need to think about it.output.include_state_token: defaulttrue. Adds thestate_token\t<hex>trailer to read verbs' output. Don't turn this off for agent use.workspace.auto_create:root-only(default) |true|false. Auto-creates.agented/at the project root on first use; you almost never needae initin the normal flow.
When unsure where ae is resolving paths, run ae status -W. The first line includes cwd=<dir>\tworkspace_dir=<dir> so you can confirm the editor is pointing at the workspace you expect.
ae config show prints the resolved configuration if you want to know what's active. The agent does not modify config; the human sets it.