{"id":"6917e571-eb99-474a-b294-2813e4592fe2","entityType":"agent","slug":"clawhub-frane-agented","name":"Agented","canonicalUrl":"https://www.xpersona.co/agent/clawhub-frane-agented","canonicalPath":"/agent/clawhub-frane-agented","generatedAt":"2026-10-11T00:31:38.599Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"editorial-content","verified":true,"confidence":"high","updatedAt":"2026-10-10T21:53:09.033Z","emptyReason":null},"description":"A text editor for LLMs, not humans. Skill: Agented Owner: frane Summary: A text editor for LLMs, not humans. Tags: latest:1.4.0 Version history: v1.4.0 | 2026-07-15T12:45:36.624Z | auto agented 1.4.0 - Updated documentation in SKILL.md for improved clarity and guidance. - Removed skill-card.md file. - No functional changes; update focuses on documentation cleanup and consolidation. v1.3.0 | 2026-06-23T13:50:19.899Z | auto agented 1.3.0 - Documentation","descriptionLabel":"Technical summary","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1.2K downloads reported by the source. Last updated 10/10/2026.","installCommand":"clawhub skill install s177zk15h82d9dzbm4nfjgy24x85t9xc:agented","sourceUrl":"https://clawhub.ai/frane/agented","homepage":"https://clawhub.ai/frane/skills/agented","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/frane/agented","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/frane/skills/agented","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":62,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"A text editor for LLMs, not humans. Skill: Agented Owner: frane Summary: A text editor for LLMs, not humans. Tags: latest:1.4.0 Version history: v1.4.0 | 2026-0"},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-10T21:53:09.033Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T21:53:09.033Z","emptyReason":null},"stars":null,"forks":null,"downloads":1249,"packageName":null,"latestVersion":"1.4.0","tractionLabel":"1.2K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T21:53:09.020Z","emptyReason":null},"lastUpdatedAt":"2026-10-10T21:53:09.033Z","lastCrawledAt":"2026-10-10T21:53:09.020Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-11T21:53:09.020Z","lastVerifiedAt":null,"highlights":[{"version":"1.4.0","createdAt":"2026-07-15T12:45:36.624Z","changelog":"agented 1.4.0 - Updated documentation in SKILL.md for improved clarity and guidance. - Removed skill-card.md file. - No functional changes; update focuses on documentation cleanup and consolidation.","fileCount":5,"zipByteSize":35765},{"version":"1.3.0","createdAt":"2026-06-23T13:50:19.899Z","changelog":"agented 1.3.0 - Documentation updates: Improved and updated README.md, SKILL.md, and CHANGELOG.md for clarity and accuracy. - Removed deprecated skill-card.md file. - No functional changes to the code or CLI interface.","fileCount":5,"zipByteSize":34535},{"version":"1.2.10","createdAt":"2026-05-08T07:51:27.158Z","changelog":"agented 1.2.10 - Bump version to 1.2.10 in SKILL.md. - No functional or behavioral changes; documentation and changelog metadata updates only.","fileCount":5,"zipByteSize":31402},{"version":"1.2.9","createdAt":"2026-05-07T11:55:39.244Z","changelog":"## agented 1.2.9 - Bumped version number in SKILL.md from 1.2.8 to 1.2.9. - No functional, behavioral, or documentation changes aside from the version update.","fileCount":4,"zipByteSize":28769},{"version":"1.2.8","createdAt":"2026-05-06T13:40:32.812Z","changelog":"agented 1.2.8 - Updated description to clarify focus as \"A text editor for LLMs, not humans\" - Minor documentation edits and rewrites in SKILL.md and README.md for clarity and brevity - Version bumped from 1.2.7 to 1.2.8 in metadata","fileCount":4,"zipByteSize":27669},{"version":"1.2.7","createdAt":"2026-05-01T06:43:29.717Z","changelog":"- Updated description to highlight support for atomic edit groups. - Incremented version to 1.2.7. - Minor clarifications in documentation, including improved emphasis on atomic operations and multi-file refactors. - No functional or behavioral changes to the skill itself.","fileCount":4,"zipByteSize":25767},{"version":"1.2.6","createdAt":"2026-04-30T20:42:46.089Z","changelog":"agented 1.2.6 - Improved skill documentation and usage guidance in SKILL.md, detailing workflow best practices and editor guarantees. - Highlighted the \"first-touch rule\" for file operations to optimize session consistency. - Clarified efficient usage patterns for reading, editing, and state management. - Provided explicit instructions for minimizing round-trips in agent workflows. - Explained conflict resolution, branching, merging, and transactional edit features in greater depth.","fileCount":4,"zipByteSize":34273}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s177zk15h82d9dzbm4nfjgy24x85t9xc:agented","setupComplexity":"low","setupSteps":["Setup complexity is classified as HIGH. You must provision dedicated cloud infrastructure or an isolated VM. Do not run this directly on your local workstation.","Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-frane-agented/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-frane-agented/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-frane-agented/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-frane-agented/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-frane-agented/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-frane-agented/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-11T00:31:38.594Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-frane-agented/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-frane-agented/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-frane-agented/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-frane-agented/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"high","updatedAt":"2026-10-10T21:53:09.033Z","emptyReason":null},"readme":"Skill: Agented\n\nOwner: frane\n\nSummary: A text editor for LLMs, not humans.\n\nTags: latest:1.4.0\n\nVersion history:\n\nv1.4.0 | 2026-07-15T12:45:36.624Z | auto\n\nagented 1.4.0\n\n- Updated documentation in SKILL.md for improved clarity and guidance.\n- Removed skill-card.md file.\n- No functional changes; update focuses on documentation cleanup and consolidation.\n\nv1.3.0 | 2026-06-23T13:50:19.899Z | auto\n\nagented 1.3.0\n\n- Documentation updates: Improved and updated README.md, SKILL.md, and CHANGELOG.md for clarity and accuracy.\n- Removed deprecated skill-card.md file.\n- No functional changes to the code or CLI interface.\n\nv1.2.10 | 2026-05-08T07:51:27.158Z | auto\n\nagented 1.2.10\n\n- Bump version to 1.2.10 in SKILL.md.\n- No functional or behavioral changes; documentation and changelog metadata updates only.\n\nv1.2.9 | 2026-05-07T11:55:39.244Z | auto\n\n## agented 1.2.9\n\n- Bumped version number in SKILL.md from 1.2.8 to 1.2.9.\n- No functional, behavioral, or documentation changes aside from the version update.\n\nv1.2.8 | 2026-05-06T13:40:32.812Z | auto\n\nagented 1.2.8\n\n- Updated description to clarify focus as \"A text editor for LLMs, not humans\"\n- Minor documentation edits and rewrites in SKILL.md and README.md for clarity and brevity\n- Version bumped from 1.2.7 to 1.2.8 in metadata\n\nv1.2.7 | 2026-05-01T06:43:29.717Z | auto\n\n- Updated description to highlight support for atomic edit groups.\n- Incremented version to 1.2.7.\n- Minor clarifications in documentation, including improved emphasis on atomic operations and multi-file refactors.\n- No functional or behavioral changes to the skill itself.\n\nv1.2.6 | 2026-04-30T20:42:46.089Z | auto\n\nagented 1.2.6\n\n- Improved skill documentation and usage guidance in SKILL.md, detailing workflow best practices and editor guarantees.\n- Highlighted the \"first-touch rule\" for file operations to optimize session consistency.\n- Clarified efficient usage patterns for reading, editing, and state management.\n- Provided explicit instructions for minimizing round-trips in agent workflows.\n- Explained conflict resolution, branching, merging, and transactional edit features in greater depth.\n\nArchive index:\n\nArchive v1.4.0: 5 files, 35765 bytes\n\nFiles: CHANGELOG.md (40627b), README.md (6396b), skill-card.md (1922b), SKILL.md (36005b), _meta.json (126b)\n\nFile v1.4.0:SKILL.md\n\n---\nname: agented\nversion: 1.4.0\nbinary: ae\ndescription: A text editor for LLMs, not humans.\nmetadata:\n  openclaw:\n    requires:\n      bins:\n        - ae\n    homepage: https://github.com/frane/agented\n    emoji: \"📝\"\n    install:\n      - kind: brew\n        tap: frane/tap\n        formula: agented\n        bins: [ae]\n---\n\n# agented (binary: `ae`)\n\n`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.\n\n## Use this tool when\n\n- You need to edit one or more files in a repo across multiple steps and want a durable history.\n- You want to leave notes for your future self or other agents (annotations) attached to specific files.\n- You want safe multi-file refactors with all-or-nothing semantics (transactions).\n\n## Don't use this tool for\n\n- Reading repo overview / architecture (use plain `cat`/grep).\n- Single one-shot edits where you don't care about history (use the platform's built-in editor).\n- Anything that isn't text (no binary file support).\n\n## How the editor enforces correctness for you\n\nEvery 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.\n\nYou 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.\n\nThe 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.\n\n**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`.\n\n**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.\n\n\n## The first-touch rule\n\nThe 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.\n\n`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.\n\n`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.\n\nSkipping 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.\n\nThe 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.\n\n## Round-trip economy: don't over-fetch\n\nMost 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:\n\n- **Don't `ae view` before `ae replace`/`insert`/`delete`.** You already know the line range from `ae open` (or `ae 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.\n- **Don't `ae view` before `ae search`.** Search returns `line\\tcol\\ttext` per match. That's the answer. View only after if you need surrounding context.\n- **Don't `ae load` before reading.** Auto-load on drift is on by default. Every write verb stat-s the file and reconciles disk changes itself. `ae load` is only for the rare case where you specifically want to capture a disk snapshot as an edit without modifying anything else.\n- **Don't `ae status` just to refetch a state_token.** Every write returns the new token in the result. Thread it forward. The only reason to call `ae status` is when you explicitly need workspace-level info (open files, dirty flags, current actor).\n- **Don't `ae open` more 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.go` registers the file silently when needed.\n\n- **Don't pipe ae output through `| head`/`| tail`/`| grep`.** Every read verb has `--range`, `--limit`, or `--pattern` to bound output server-side, and `--range` accepts Python-slice syntax: `1:10` for first 10, `-10:` for last 10, `5:-5` for middle slice (skip first 5 and last 5), `:20` for first 20, `-50:-20` for the lines 50-from-end through 20-from-end. Pipe-trimming after the fact wastes the round trip's setup cost.\n- **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.\n\nTranslation: the canonical loop is `open → search/find → replace/insert/delete → repeat`. Three calls per logical edit, sometimes two. Not five.\n## What this editor does that Read/Edit/Write can't\n\nThese are the operations that motivate reaching for `ae` over the built-ins.\n\n- **Read once, edit forever.** Your local model of the file (built from the `ae open` response 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.\n- **Branching tree, not stack.** Walking back is `ae undo`; jumping to any prior state is `ae 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.\n- **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>`.\n- **Atomic batches.** `ae apply` consumes JSON-lines on stdin and applies every operation inside one transaction. Replaces N Edit calls with one. Verb: `cat ops.jsonl | ae apply <path>`.\n- **Atomic move.** `ae move` cuts 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).\n- **Atomic extract.** `ae extract` cuts 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]`.\n- **Regex replace with capture groups.** `ae replace --pattern` does sed-style replacement in a single tool call. Verb: `ae s <path> -p '<re>' -w '<expansion>' [-L <max>] [-n]`. **Always single-quote `-w`** when using `$1`/`$2` backrefs — bash expands `$1` as the first positional arg (empty in most shells), so `-w \"$1.foo\"` silently inserts an empty string. Single quotes (`-w '$1.foo'`) pass `$1` through to ae, which expands per Go regexp.ExpandString semantics. **0 matches is an error** (nonzero exit, nothing written) so `&& ae save` chains stop instead of shipping a no-op; pass `--allow-no-match` when 0 is an acceptable outcome. `--text` is accepted as an alias for `--with`.\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/d` with `-r S:E`.\n- **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 on `ae open`.\n- **Transactions with auto-rollback.** `ae begin` opens a logical group; `ae commit` finalizes; `ae rollback` reverts. Forgotten transactions auto-rollback after the configured idle window. Verb: `ae begin / ae commit / ae rollback`.\n- **Cross-agent shared state.** A Claude Code session and a Codex session both reading the same `.agented/state.db` see the same head, branches, annotations, and marks. The workspace is the durable thing; the agent identity is incidental.\n\n## Reading verbs (idempotent, cheap)\n\n**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).\n| Verb     | Short | Args                          | Output (tab) suffix       | Use when                              |\n|----------|-------|-------------------------------|---------------------------|---------------------------------------|\n| `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. **Multi-range** in one call: comma-separated, e.g. `--range 100:120,140:160` — output concatenates each window with `...` between non-contiguous gaps and one trailing `state_token`. Two view trips collapse into one. `--raw` emits verbatim bytes (no line-num prefix, no token) for piping to another tool |\n| `search` | `/`   | `<path> --pattern <re>`       | `state_token\\t<hex>`      | Find matches; output `line\\tcol\\ttext`|\n| `diff`   | `df`  | `<path> [--from N --to M]`    | unified diff + token      | Inspect what an edit changed          |\n| `log`    | -     | `<path> [--limit N]`          | tab-delimited audit rows  | See history of operations             |\n| `branches` | `br` | `<path>`                     | `id\\tts\\tactor\\tcmd\\tis_head` | Discover alternative leaves     |\n| `list`   | `ls`  | `[--all|--closed|--stale]`    | per-file summary          | What files are open                   |\n| `status` | `st`  | `[<path>]`                    | workspace or file summary | Get state_token for next write        |\n| `mark get` | -    | `<path> <name>`             | `name\\tline\\tsnapped\\t...`| Jump back to a known anchor           |\n| `annotate list` | -| `<path>`                       | `id\\tts\\tactor\\tcontent`  | Recall notes from prior sessions      |\n| `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 |\n| `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 |\n| `diag`   | -     | `[<path>] [--severity errors\\|warnings\\|all\\|none] [--wait-ms N]` | `diag\\t<sev>\\t<file>:<line>:<col>\\t<msg>\\t<source>` | Pull LSP diagnostics on demand — one file, or the whole workspace when path is omitted. `--wait-ms` polls past the LSP's async publish lag. IDE mode only |\n\n## Writing verbs (use `--expect <state_token>`)\n\n| Verb       | Short | Args                                          | Conflict response | Use when             |\n|------------|-------|-----------------------------------------------|-------------------|----------------------|\n| `replace`  | `s`   | `<path> --range S:E --with TEXT --expect TOK` | exit 3 + content  | Change lines         |\n| `insert`   | `i`   | `<path> --after N --text TEXT --expect TOK`   | exit 3 + content  | Add lines            |\n| `delete`   | `d`   | `<path> --range S:E --expect TOK`             | exit 3 + content  | Remove lines         |\n| `save`     | `w`   | `<path> [--force]`                            | -                 | Write head to disk   |\n| `load`     | `e`   | `<path>`                                      | -                 | Reload from disk     |\n| `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 |\n| `extract`  | -     | `<path> --range S:E --to <new-or-existing> [--save]` | -            | Refactor a range into a sibling file (atomic) |\n\nEvery 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. When stdout is a terminal, a compact colored delta of the edit follows (config `output.edit_diff = off | tty | always`, default `tty`; `always` also attaches a `diff` field to JSON/MCP responses — costs tokens, opt in deliberately).\n**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. `ae open` performs the same reconciliation: opening a file whose disk content diverged from the workspace head folds the disk state in as a new head edit and says so.\n\nConfig 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`.\n\n`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. `save` refuses to overwrite disk content this workspace never loaded (i.e. the file changed outside ae and no edit in history carries that hash) — run `ae load` first to fold it in, or pass `--force` to overwrite deliberately.\n\n## History verbs\n\n- `ae undo <path> [--count N]` — walk head pointer back N edits. Errors with branch info if ambiguous.\n- `ae redo <path>` — walk forward along the most recently created child.\n- `ae head <path> --edit <id>` — jump to a specific edit (use after `branches` shows alternatives).\n- `ae branches <path>` — list leaf edits (alternatives that exist in the tree).\n\n### Worked example: backtracking after a wrong direction\n\n```\nae view foo.go --range 10:20            # state_token=A1B2\nae replace foo.go --range 12:14 --with \"...\" --expect A1B2   # state_token=B3C4\nae replace foo.go --range 18:18 --with \"...\" --expect B3C4   # state_token=C5D6\nae undo foo.go --count 2                # head moves back two; new state_token=A1B2-ish\nae replace foo.go --range 12:14 --with \"DIFFERENT\" --expect <new>  # creates branch B\nae branches foo.go                       # shows two leaves: original C5D6, and branch B's leaf\nae head foo.go --edit <C5D6_id>          # jump back to original branch's leaf\n```\n\n## Marks\n\nMarks 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).\n\n### Worked example: mark a return point before a multi-edit refactor\n\n```\nae open auth.go                              # state_token=T1\nae mark auth.go add return_point --line 240\nae replace auth.go --range 100:140 --with \"...\" --expect T1   # state_token=T2\nae mark auth.go get return_point             # line is now 100+(new lines)-(40 deleted)\n```\n\n## Annotations — durable cross-session memory\n\nAnnotations 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.\n\nThese four behaviors are mandatory, not optional:\n\n**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.\n\n```sh\nae open auth.go\n# response: annotation\\t14\\t...\\tprev-actor\\tauth path uses signed cookies; do not weaken\n# response: annotation\\t15\\t...\\tprev-actor\\trefactor in progress, lines 80-130 half-done\n# read both before issuing any edit\n```\n\n**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).\n\n```sh\nae 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.\"\n```\n\n**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.\n\n```sh\nae an scheduler.go a -t \"the dispatcher in run() must remain pure — it's called during init() before the metrics package is loaded\"\nae 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\"\n```\n\n**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.\n\n## Transactions\n\n`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).\n\nIf 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.\n\n### Worked example: multi-file refactor with rollback safety on test failure\n\n```\nae begin                                                  # tx_id=42\nae search auth.go --pattern 'oldName\\\\('                   # find call sites\nae replace auth.go --range 12:12 --with \"newName(\" --expect T1\nae replace auth.go --range 80:80 --with \"newName(\" --expect T2\nae replace caller.go --range 40:40 --with \"newName(\" --expect U1\n# run tests externally; if green:\nae commit\n# else:\nae rollback\n```\n\n## Worked examples\n\n### 1) Read-modify-verify a function\n\n```\nae view auth.go --range 50:80          # capture state_token=T1\nae replace auth.go --range 60:65 --with \"func ...\" --expect T1   # state_token=T2\nae diff auth.go                         # confirm intended change\n```\n\n### 2) Backtracking (see history verbs section above).\n\n### 3) Leaving context for the next session\n\n```\nae annotate auth.go add --text \"Migration 0042 must run before this lands; coordinate with infra\"\n```\n\n### 4) Picking up where another session left off\n\n```\nae open auth.go              # response includes annotations and state_token in one shot\n                             # immediately use --expect <returned_token> on the next write\n```\n\n### 5) Search-then-targeted-edits\n\n```\nae search foo.go --pattern 'TODO'      # state_token=T1; matches show line/col\nae replace foo.go --range 12:12 --with \"...\" --expect T1   # state_token=T2\nae replace foo.go --range 47:47 --with \"...\" --expect T2   # state_token=T3\n```\n\n### 6) Multi-file refactor with rollback (see transactions section).\n\n### 7) Atomic batch via `ae apply`\n\n```\ncat <<'OPS' | ae apply auth.go\ns 12:12 newName(\\n\ns 40:40 newName(\\n\ni 80 // see ADR-0042\\n\nOPS\n# all-or-nothing; on any failure the response identifies the failing op\n# and the head is unchanged.\n```\n\n### `ae apply` input formats\n\n`ae apply` reads operations from stdin in any of three formats. The format is detected automatically; no flag is needed.\n\n**Shortform.** What you reach for when writing batches yourself.\n\n```\ns 12:14 new content\ni 80 header line\nd 67:69\nm foo 50\n```\n\n**Longform.** Same density, fuller names. Use when the batch will be reviewed.\n\n```\nreplace range=12:14 with=new content\ninsert after=80 text=header line\ndelete range=67:69\nmark name=foo line=50\n```\n\n**JSON-lines.** Use when piping from another tool, especially `ae find --json`.\n\n```\n{\"verb\":\"replace\",\"range\":\"12:14\",\"with\":\"new content\"}\n{\"verb\":\"insert\",\"after\":80,\"text\":\"header line\"}\n{\"verb\":\"delete\",\"range\":\"67:69\"}\n```\n\nThe 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.\n\nCross-file batches: shortform and longform use `@<file>` lines as separators. JSON-lines uses a `\"file\"` field per line.\n\nState tokens: shortform appends `! <token>` at end of line, longform uses `expect=<token>`, JSON-lines uses an `\"expect\"` field.\n\n### 8) Three-way merge with one resolved conflict\n\n```\nae br auth.go\n# leaves: 47 (refactor branch), 52 (bug-fix branch), head=52\nae merge auth.go -l 47 -l 52\n# conflict response shows ranges modified by both branches\nae merge auth.go -l 47 -l 52 -R '20:22=a' -R '47:47=b'\n# all conflicts resolved => commit a merge edit; head moves to the new id\n```\n\n### 9) LSP-driven structural navigation (IDE mode on)\n\n```\nae find -R HandleAuth                  # who calls it?\n# ref  auth.go:47:12   call         HandleAuth(ctx, req)\n# ref  middleware.go:128:8   call   HandleAuth(ctx, r2)\n# ref  test.go:34:5    import       HandleAuth\n\nae find -s HandleAuth                  # where is it defined?\n# def  auth.go:47:1    HandleAuth   func\n\nae sy auth.go --kind func              # list functions in this file\n# sym  func    auth.go:47:1    HandleAuth\n# sym  func    auth.go:89:1    parseToken\n\n# now do the actual edit, with the line number from the ref output\nae view auth.go --range 47:60          # state_token=T1\nae replace auth.go --range 50:50 --with \"...\" --expect T1\n# response includes diag lines if gopls flags anything new\n```\n\nReach 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.\n\n\n## IDE mode\n\nWhen `ide.enabled: true` is set in `.agented/config.json`, IDE features are available:\n\n- `ae symbols [path]` (short `ae sy`) lists symbols in a file or workspace\n- `ae find --symbol <name>` (`ae / -s`) finds where a symbol is defined\n- `ae find --references <symbol>` (`ae / -R`) finds all use sites\n- `ae find --definition <symbol> --at <file>:<line>:<col>` (`ae / -D -A`) resolves a definition at a cursor position\n\nWhen 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.\n\nYou 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.\n\nThe 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.\n\nWhen IDE features aren't available (config disabled, no instruction override, daemon crashed), LSP-dependent verbs return:\n\n  error    lsp_unavailable    <reason>\n\nDon't retry; proceed without those capabilities. Don't try to start the daemon yourself unless the user explicitly told you to.\n\nWhen 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).\n\nWhen IDE mode is active, mutating verbs (`ae save`, `ae replace`, `ae apply`, etc.) may include diagnostic lines in their responses:\n\n  ok    state_token=ab12cd34\n  diag  warn  foo.go:89:4   unused variable x   lint\n  diag  error foo.go:47:12  undefined: bar      compile\n\nDiagnostics are informational. The operation succeeded; the diagnostics report current LSP findings on the file. Decide whether to act on them based on the task.\n\nThe 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.\"\n\nTo pull diagnostics on demand instead of waiting for them to ride along on an edit, use `ae diag [path]` — omit the path for a workspace-wide sweep across all open files. Language servers publish asynchronously (often a second or two after a change), so pass `--wait-ms <N>` to poll until diagnostics appear or the timeout elapses; this is the reliable way to confirm an edit parsed without falling back to a full compile. Filter with `--severity errors|warnings|all|none`. Over MCP the same diagnostics also ride inline on every tool response that touched a file (a `diag` field on the JSON result), and `ae_diag` is exposed as a dedicated tool (with `wait_ms`).\n\nA 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:\n\n  diag  error foo.ts:14:3   Cannot find name 'bar'.   ts\n  diag  warn  foo.ts:14:3   'bar' is not defined.       eslint\n\nThe 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.\n\nDefault server lists (when the language has `auto_start: true` and the binaries are installed):\n- `go`: `gopls`\n- `typescript`: `tsserver` + `eslint`\n- `python`: `pyright` + `ruff`\n- `rust`: `rust-analyzer`\n\n### Severity, kind, and usage vocabularies\n\n| Severity | LSP origin |\n|----------|------------|\n| `error`  | LSP severity 1 |\n| `warn`   | LSP severity 2 |\n| `info`   | LSP severity 3 |\n| `hint`   | LSP severity 4 |\n\n| Kind     | Examples |\n|----------|----------|\n| `func`   | top-level function |\n| `method` | method on a type |\n| `type`   | type alias, struct |\n| `class`  | class (Python, TS) |\n| `interface` | interface |\n| `var`    | local or package variable |\n| `const`  | constant |\n| `field`  | struct/class field |\n| `module` | package or namespace |\n\n| Usage    | When |\n|----------|------|\n| `call`   | `name(...)` |\n| `read`   | name appears, no other label fits |\n| `write`  | `name = ...` or `name := ...` |\n| `import` | import statement |\n| `definition` | the line that defines the symbol |\n| `other`  | catch-all |\n\n## Errors and recovery\n\n| Error substring          | What it means                                        | Next action                                           |\n|--------------------------|------------------------------------------------------|-------------------------------------------------------|\n| `state_token mismatch`   | Head moved or you didn't pass `--expect`             | Use the `current_token` from the conflict response    |\n| `branch ambiguous`       | undo/redo would have to choose among siblings        | Read the branches list in the response, then `head --edit` |\n| `transaction <id> owned by` | Another actor's tx is open; writes are blocked      | Wait, or pass `--no-transaction` to bypass            |\n| `transaction auto-rolled-back` | The editor reverted an idle tx automatically       | Check `ae log <path>`; the auto_rollback row identifies what was reverted |\n| `mark name exists`       | Mark name collision                                   | Pick a different name or `mark remove` first          |\n| `file not registered`    | The path was never `ae open`'d                       | Run `ae open <path>` first, or pass `--auto-open`     |\n| `pattern compile error`  | RE2 syntax issue                                     | Fix the pattern (Go's `regexp` syntax)                |\n| `range out of bounds`    | Line range exceeds file                              | Re-`view` to get the current line count               |\n| `skill out of date`      | Installed SKILL.md major-mismatches binary           | `ae skill install`                                    |\n\n## Anti-patterns\n\n- **Reaching for `Read`/`Edit`/`Write` because 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.\n- Discarding `state_token` between calls (forces unnecessary conflicts on every write).\n- Ignoring the conflict response payload (the new content is right there; use it instead of running `view` again).\n- Calling `redo` after intentionally creating a new branch — `redo` will fail with branch ambiguity. Use `head --edit <id>` instead.\n- Useless annotations (\"this is a function\").\n- Unescaped regex special characters in `search` patterns.\n- Skipping the annotations on `ae open`. They were left for you on purpose; reading them is free.\n- 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.\n- Inferring file health from absence of `diag` lines. Diagnostics absence has multiple causes (no findings, LSP not analyzed yet, language not configured, daemon not running).\n- Trying to start `ae lsp` yourself unless the user has explicitly authorized it. The auto-start handles it when config says enabled.\n\n## Output format reference\n\nAll output is tab-delimited (`\\t`) with one record per line.\n\n- `view`: each line `<line_num>\\t<content>`. Trailer (when `output.include_state_token` is on): `state_token\\t<hex>`.\n- `search`: each match `<line>\\t<column>\\t<text>`. Trailer: `state_token\\t<hex>`.\n- `replace`/`insert`/`delete`: a single line `edit_id=<n>\\thead_edit_id=<n>\\tline_delta=<d>\\tline_count=<n>\\tstate_token=<hex>`.\n- `undo`/`redo`/`head`: a single line `head_edit_id=<n>\\tline_count=<n>\\tstate_token=<hex>`.\n- `branches`: `<edit_id>\\t<created_at>\\t<actor>\\t<command>\\t<is_head>`.\n- `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>`.\n- `status` (workspace): `workspace\\tactor=...\\topen_files=...`. (file): `file\\tid=...\\tpath=...\\tline_count=...\\thead_edit_id=...\\tstate=clean|dirty\\tstate_token=...`.\n- `log`: `<created_at>\\t<actor>\\t<command>\\t<result>\\t<edit_id>`.\n- Conflict response (exit code 3, on stdout): `conflict\\tfile_id=...\\tcurrent_token=...\\thead_edit_id=...\\thead_actor=...\\tline_count=...` then `note\\t...` then `---current-content---` then literal content then `---end---`.\n\nFor programmatic parsing, pass `--json` to any verb and you'll get a stable JSON object instead of tabs.\n\n## Verb shortcuts\n\n| Long       | Short | Long       | Short |\n|------------|-------|------------|-------|\n| view       | v     | branches   | br    |\n| search     | /     | open       | o     |\n| replace    | s     | close      | x     |\n| insert     | i     | list       | ls    |\n| delete     | d     | save       | w     |\n| undo       | u     | load       | e     |\n| redo       | r     | diff       | df    |\n| mark       | m     | status     | st    |\n|            |       | annotate   | an    |\n| symbols    | sy    | lsp        |       |\n| --symbol   | -s    | --references | -R  |\n| --definition | -D  | --at       | -A    |\n| --diagnostics | -G | --no-diagnostics | -N |\n| --background | -B  |            |       |\n\n## Configuration awareness\n\nThe editor's behavior is influenced by the project's `.agented/config.json`:\n\n- `concurrency.require_expect`: `writes` | `warn` | `off`. Default `writes`. Determines whether writes without `--expect` are rejected, warned, or silently allowed.\n- `transactions.auto_rollback_idle_for`: how long an idle transaction can sit before auto-rollback. Default `10m`.\n- `auto_prune.*`: whether and when the editor prunes stale history. You don't need to think about it.\n- `output.include_state_token`: default `true`. Adds the `state_token\\t<hex>` trailer to read verbs' output. Don't turn this off for agent use.\n- `workspace.auto_create`: `root-only` (default) | `true` | `false`. Auto-creates `.agented/` at the project root on first use; you almost never need `ae init` in the normal flow.\n\nWhen 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.\n\n`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.\n\nFile v1.4.0:README.md\n\n<h1 align=\"center\">agented (<code>ae</code>)</h1>\n\n<p align=\"center\"><strong>A text editor for LLMs, not humans.</strong></p>\n\n<p align=\"center\">\n  <a href=\"https://github.com/frane/agented/releases\"><img alt=\"release\" src=\"https://img.shields.io/github/v/release/frane/agented?style=flat-square\"></a>\n  <a href=\"https://github.com/frane/agented/blob/master/LICENSE\"><img alt=\"license\" src=\"https://img.shields.io/github/license/frane/agented?style=flat-square&v=2\"></a>\n  <a href=\"https://smithery.ai/server/frane/agented\"><img alt=\"smithery\" src=\"https://img.shields.io/badge/smithery-MCP-purple?style=flat-square\"></a>\n</p>\n\n<p align=\"center\">\n  <img src=\"docs/demo-claude.gif\" alt=\"Claude Code using ae\" width=\"560\">\n</p>\n\nTake ed, the line editor that nobody has voluntarily used since about 1975, and rebuild it for an environment where the typing user is a language model. Short verbs, line addresses, no modes, no TUI. What an editor optimises for changes when the user is the model: round trips per task, tokens per command, an editing buffer with a long memory, and an undo tree that remembers the branches the agent abandoned, because that's often where the interesting work was.\n\n## What users say...\n\n> ⏺ ae remembers what my last session was doing, which is more than I can say for me.\n\n*— Claude Code*\n\n> • ae feels slower to start than plain file edits, but once a change spans\n> multiple steps, the state tokens, history, and undo tree make the work feel\n> much less brittle.\n\n*— Codex CLI*\n\n## Features\n\n- **Fewer round trips.** Read-before-Edit is unnecessary; on conflict the response carries the new content so you reconcile in one call instead of pre-reading every time.\n- **Branching undo.** Walked-back work stays addressable instead of being thrown away when you pick a different path.\n- **Three-way merge.** Concurrent agents get a structured conflict response instead of a silent overwrite.\n- **Atomic batches.** Multi-file refactors run all-or-nothing instead of leaving half-applied state on failure.\n- **Cross-file moves and regex replace as primitives.** Operations the built-in tools can't express cleanly become single calls.\n- **Drift detection.** External edits to an open file are folded into the tree instead of being clobbered by the next write.\n- **Inline diagnostics.** Type errors and lint findings surface on save, not at the next build many edits later.\n- **Cross-session memory.** Per-file notes persist between sessions and surface inline on the next open.\n- **Audit log.** Every operation recorded with actor and timestamp, so two agents in one workspace can't argue about who moved the head.\n\n## Install\n\nHomebrew (macOS, Linux):\n\n```sh\nbrew tap frane/tap\nbrew install agented\n```\n\ncurl (any platform):\n\n```sh\ncurl -sSL https://raw.githubusercontent.com/frane/agented/master/install.sh | sh\n```\n\nFrom source: `go install github.com/frane/agented/cmd/ae@latest`, or clone and `make install`. Pure Go, no cgo, single static binary, Apache 2.0.\n\n## Plugin distribution\n\nOnce `ae` is on PATH, agented also ships as a plugin / extension across the major agent CLIs. The `ae` binary itself is the prereq for all three; the plugin layer just registers the skill content and the MCP server entry.\n\n**Claude Code**:\n\n```sh\n/plugin marketplace add frane/agented\n/plugin install agented@frane-agented\n```\n\n**Codex CLI**: until OpenAI's official directory opens, add a manual entry to `~/.agents/plugins/marketplace.json` pointing at this repo with `source.path: \"./plugin\"`.\n\n**Gemini CLI**:\n\n```sh\ngemini extensions install https://github.com/frane/agented\n```\n\nThe Gemini gallery (https://geminicli.com/extensions/) crawls daily and indexes via the `gemini-cli-extension` topic on this repo.\n\n## Getting started\n\n```sh\nae skill install\n```\n\nThat writes a `SKILL.md` into every detected agent's skills directory: Claude, Codex, Cursor, Gemini, OpenClaw, and the canonical `~/.agents/`. The skill teaches the agent how to drive ae.\n\nYou still need to tell the agent to use it. Even with the skill installed, agents fall back to built-in Read and Edit out of habit, so something like \"use ae for all file edits\" in your system prompt or your first message is what keeps them on it.\n\nOnce the agent is on ae, the shape that justifies the editor is recovery. The agent makes thirty edits over an hour, you walk away, come back to find it went off the rails around edit 18, but edits 19 through 23 are still useful:\n\n```sh\nae br foo.go                             # see the leaves, current head is the bad one\nae head foo.go --edit 23                 # jump back to the last good state\nae v foo.go                              # confirm what's there\nae s foo.go -r 40:42 -w \"...\" -x <token> # continue forward, creates a sibling branch\n```\n\nWith linear undo this scenario is \"rollback the entire batch or live with the bad version.\" With the tree it's a `head --edit` and a `view`.\n\n## Skill and MCP\n\n`ae skill install` writes the SKILL.md into every detected agent. `ae serve` exposes the same verbs over MCP for agents that don't have shell access. Plugin-distribution channels (Claude Code marketplace, Codex CLI plugin, Gemini extension above) bundle both. Each surface has its own page: [skill](docs/skill.md), [MCP](docs/mcp.md).\n\n## Performance\n\nA single open-and-replace on a 100-line file is around 9 ms wall time including the auto-save fsync. Fifty sequential replaces is around 325 ms. The full numbers are in [test/benchmark/results.md](test/benchmark/results.md), regenerated by `make bench`.\n\n## Docs\n\n- [Concepts](docs/concepts.md): the design choices and the state model\n- [Usage](docs/usage.md): full session walkthroughs\n- [Skill](docs/skill.md): what `ae skill install` does\n- [Permissions](docs/permissions.md): editor-harness allow-rules\n- [Configuration](docs/configuration.md): what's tunable\n- [Tokens](docs/tokens.md): why the output looks the way it does\n- [MCP](docs/mcp.md): running the MCP server\n- [IDE](docs/ide.md): LSP-backed features\n- [Build](docs/build.md): tests and benchmarks\n\n## Contributing\n\nIssues and PRs welcome. The thing I'd actually like feedback on is the agent-drift problem: even with the skill installed, LLMs occasionally fall back to the built-in Read and Edit tools mid-session, and the trick to making that stick is something the project doesn't have a clean answer for yet.\n\n## License\n\nApache 2.0.\n\nFile v1.4.0:_meta.json\n\n{\n  \"ownerId\": \"kn7391bg7cz5ztqgbbnhbr9fwn85vgkv\",\n  \"slug\": \"agented\",\n  \"version\": \"1.4.0\",\n  \"publishedAt\": 1784119536624\n}\n\nFile v1.4.0:CHANGELOG.md\n\n# Changelog\n\nAll notable changes to agented are documented here. The format is based on [Keep a Changelog](https://keepachangelog.com), and the project follows [Semantic Versioning](https://semver.org).\n\n\n## [v0.6.0] - 2026-07-15\n\nDriven by the first external dogfood report (#agented, green-lynx-7cf5): one data-loss bug, one scripting hazard, one guessability nit — plus a compact edit-diff mode.\n\n### Fixes\n\n- **`ae open` no longer serves a stale head after the file changed outside ae** (data-loss bug). Opening an already-registered file now hash-checks disk against the workspace head and, on divergence, folds the disk state in as a new `load` edit with a warning — the same recoverable-on-the-tree semantics as the write verbs' auto-load, gated by the same `concurrency.auto_load_on_drift` knob. Previously `open` silently returned the old head, `replace --pattern` then matched nothing against it, and `save` clobbered the newer disk file.\n- **`ae save` refuses to overwrite disk content this workspace never loaded.** If the on-disk content's hash appears nowhere in the file's edit history, the file was changed outside ae and a blind save would destroy that work: save now errors with recovery instructions (`ae load` first, then re-apply or merge). Override with `--force` (CLI) / `force: true` (MCP `ae_save`).\n- **`ae replace --pattern` with 0 matches is now an error** (nonzero exit), so `replace && save` shell chains stop instead of shipping a no-op. Pass `--allow-no-match` to keep exit 0; `--dry-run` is unaffected. Pattern mode also now reconciles disk drift before matching and auto-saves after the edit — parity with the range verbs, both previously missing.\n- Integration-test harness isolates `$HOME` so a developer's global `~/.agented/config.json` (e.g. `ide.enabled=true`) can't stall every spawned `ae` on LSP autostart and trip timing-sensitive scenarios.\n\n### Features\n\n- **Compact per-edit diffs: `output.edit_diff = off | tty | always` (default `tty`).** replace/insert/delete responses can carry a token-lean unified delta of the edit (1 context line, no file header, capped at 40 rows). In the default `tty` mode the CLI renders it colored, Claude Code-style, only when stdout is a terminal — pipes, `--json`, and MCP responses are unchanged. `always` attaches a `diff` field to JSON/MCP results for agents that want in-band verification of what actually changed. Env override: `AE_OUTPUT_EDIT_DIFF`.\n- `ae replace` accepts `--text` as an alias for `--with` (insert/delete take `--text`; sed/sd muscle memory).\n\nSkill 1.4.0.\n\n\n## [v0.5.0] - 2026-06-23\n\n### Features\n\n- **`ae diag` verb + diagnostics over MCP.** `agented` already ran language servers and cached their diagnostics, but the MCP surface never exposed them — an agent editing through `mcp__agented__ae_*` got `saved: true` with no way to tell whether the edit even parsed, short of a full `go build` / `cargo check`. This release closes that gap:\n  - New **`ae diag [path]`** verb (MCP tool `ae_diag`): pull LSP diagnostics for one file, or the whole workspace when the path is omitted. Flags: `--severity errors|warnings|all|none`, `--limit`, and `--wait-ms <N>` to poll past the language server's asynchronous publish lag.\n  - **Inline diagnostics on MCP responses** that touch a file: edit / open / save tool results now carry a `diag` field with the file's current diagnostics, at parity with the CLI's `diag` lines.\n  - Language-agnostic — gopls, tsserver, eslint, pyright, ruff, rust-analyzer, or any configured LSP, routed by file extension. Each diagnostic is self-describing: severity, range, message, source, rule id, source server, and path.\n\n## [v0.4.9] - 2026-06-01\n\n### Features\n\n- **`ae permissions disable-internals --strict`** (and its `enable-internals --strict` pair). The nuclear option: extends the built-in tool denies with shell-command denies for the obvious editor/reader fallbacks an agent reaches for via Bash / run_shell_command / Codex shell:\n\n  `cat, sed, awk, head, tail, vi, vim, nano, less, more, ed, emacs, code`\n\n  Discovery + version-control tools (`grep`, `find`, `ls`, `git`) stay allowed. Per-target output:\n\n  | Target | What `--strict` writes |\n  |---|---|\n  | claude | `Bash(<cmd> *)` entries appended to `permissions.deny` in the same settings.json |\n  | codex | new file `~/.codex/rules/agented-strict.rules` with `prefix_rule(..., forbidden)` calls (Starlark) |\n  | gemini | new file `~/.gemini/policies/agented-strict.toml` with `run_shell_command` + `argsPattern` rules |\n  | openclaw | n/a |\n\n  Pair with the existing `disable-internals` (which already denies Read/Edit/Write/NotebookEdit). The strict flag is additive and reversible — `enable-internals --strict` removes both layers cleanly.\n\n\n## [v0.4.8] - 2026-05-13\n\n### Fixes\n\n- **`ae permissions disable-internals --help` was stale.** The long description still claimed Claude was the only supported target, even though v0.4.6 added Gemini and v0.4.7 added Codex. The string was last touched in v0.4.5 and never updated when the other two targets landed. Now reflects the full per-target matrix.\n\n\n## [v0.4.7] - 2026-05-08\n\n### Features\n\n- **`ae permissions disable-internals` now writes a Codex deny rule too**, completing the Claude / Gemini / Codex matrix. Codex's only edit primitive at the public surface is `apply_patch`, so the implementation maps the canonical Read/Edit/Write/NotebookEdit input down to one TOML line — `apply_patch = false` under a `[tools]` table in `~/.codex/config.toml` — written idempotently with our own minimal section editor (no external TOML lib).\n\n  Honesty caveat: Codex accepts `tools.apply_patch = false` via `-c` parsing without error, but the published config docs don't explicitly state that this disables the tool at runtime. Treated as **experimental** — the docs flag it, the rule gets written, and users can verify the behavior in their own Codex session.\n\n  | Target | Mechanism | File |\n  |---|---|---|\n  | claude | `permissions.deny` array | `~/.claude/settings.json` (global) or `.claude/settings.local.json` (project) |\n  | codex | `tools.apply_patch = false` (experimental) | `~/.codex/config.toml` (global) |\n  | gemini | Policy Engine TOML rules | `~/.gemini/policies/agented-deny.toml` (global) |\n  | openclaw | n/a — managed at agent level | — |\n\n### Documentation\n\n- `docs/permissions.md` per-target table updated with Codex's row and the experimental caveat.\n\n\n## [v0.4.6] - 2026-05-08\n\n### Features\n\n- **`ae permissions disable-internals` now covers Gemini.** v0.4.5 shipped Claude only; this iteration adds Gemini support via its Policy Engine. Writes `~/.gemini/policies/agented-deny.toml` with `decision = \"deny\"` rules for `read_file`, `edit`, `write_file` (mapped from the canonical Read/Edit/Write/NotebookEdit names). Codex still skips: per OpenAI's docs, the `.rules` file covers shell-command sandboxing and there's no documented schema for denying built-in tools.\n\n  Per-target summary now exposed via the same `--target all` flow:\n\n  | Target | What `disable-internals` writes |\n  |---|---|\n  | claude | `permissions.deny` in `~/.claude/settings.json` |\n  | gemini | `~/.gemini/policies/agented-deny.toml` (Policy Engine TOML) |\n  | codex | skip (no upstream schema) |\n  | openclaw | skip (managed at agent level) |\n\n- **`docs/permissions.md`** updated with a per-target schema table and example invocations for each.\n\n\n## [v0.4.5] - 2026-05-08\n\n### Features\n\n- **`ae permissions disable-internals`**. New subcommand that writes deny-rules for the built-in file tools (`Read`, `Edit`, `Write`, `NotebookEdit`) into the agent's permission config. Once these are in place, agents that have the agented skill installed are forced to drive `ae` from Bash instead of falling back to the built-ins out of training-data habit. Pair with `ae permissions install` (the existing allow-rules for `Bash(ae *)`).\n\n  Today writes only Claude Code's `permissions.deny` array in `~/.claude/settings.json` (global) or `.claude/settings.local.json` (project). Gemini and Codex are skipped with a clear \"deny-list schema not yet known\" reason — their config schemas don't have a documented public deny-list field; we'll fill it in once they do.\n\n  ```sh\n  ae permissions disable-internals             # write deny rules to project scope\n  ae permissions disable-internals -s global   # to global scope\n  ae permissions disable-internals --dry-run   # preview\n  ae permissions enable-internals              # remove the deny rules\n  ```\n\n\n## [v0.4.4] - 2026-05-08\n\n### Features\n\n- **Multi-range view**. `ae view -r 100:120,140:160` returns both windows in one call instead of two trips. Output concatenates each window with a `...` separator on non-contiguous gaps and one trailing `state_token` line. Single-range syntax is byte-identical to v0.4.3 — no existing call shape changes. The MCP tool gains a `ranges` arg with the same comma-separated format.\n\n- **MCP parity for the four missing core verbs**. Previously the MCP server exposed 30 tools but skipped `apply`, `move`, `extract`, and `merge` — three of them ae's headline atomicity primitives, the fourth its three-way merge. They're now first-class:\n  - `ae_apply` (`path`, `ops`, `multi_file`, `expect`, `expect_workspace`) — atomic batch ops; `ops` accepts the same JSON-lines / shortform / longform input the CLI reads from stdin.\n  - `ae_move` (`path`, `from_start`, `from_end`, `to_line`, `to_file`, `expect`, `auto_open`) — same-file or cross-file atomic move.\n  - `ae_extract` (`path`, `from_start`, `from_end`, `to_file`, `to_line`, `save`, `expect`) — the canonical refactor primitive: cut a range out of one file, write it to another (auto-created if absent), optionally save both.\n  - `ae_merge` (`path`, `leaf_a`, `leaf_b`, `prefer`, `abort`) — three-way merge between two leaf edits; fine-grained per-range `--resolve` specs remain CLI-only for now.\n\n  Brings the MCP tool count to 34 and closes the parity gap CLI agents had over MCP agents.\n\n### Tests\n\n- `TestViewSingleRangeBackwardCompat` pins the v0.4.3 single-range output byte-for-byte.\n- `TestViewSingleRangeViaRanges` confirms passing one element via the new `Ranges` field produces identical output to the legacy `Start/End` path.\n- `TestViewMultiRangeNonContiguousEmitsSeparator`, `TestViewMultiRangeAdjacentNoSeparator`, `TestViewMultiRangeOverlapMerges`, `TestViewMultiRangeOutOfOrderSorts` cover the new behaviors.\n\n### Deferred to v0.4.5\n\n- Multi-range `delete` and `search` — same pattern, more code; kept out of v0.4.4 to ship the high-leverage view + MCP-parity bits cleanly.\n- LSP-backed MCP tools (`ae_diagnostics`, `ae_hover`, `ae_references`) — design discussed in the v0.4.0 notes; implementation pending.\n\n## [v0.4.3] - 2026-05-07\n\n### Features\n\n- **Claude Code plugin distribution**. agented is now publishable as a Claude Code plugin via a marketplace at the repo root (`.claude-plugin/marketplace.json`). After tagging, users install with:\n\n  ```sh\n  /plugin marketplace add frane/agented\n  /plugin install agented@frane-agented\n  ```\n\n  The plugin layout under `plugin/` ships the embedded `SKILL.md` plus `.mcp.json` (registers `ae serve`). The `ae` binary still has to be on PATH (Homebrew or curl); plugins don't ship cross-platform binaries.\n\n- **Codex CLI plugin manifest**. Same plugin directory carries `plugin/.codex-plugin/plugin.json` for OpenAI Codex CLI. Codex's marketplace mechanism is still per-user (`~/.agents/plugins/marketplace.json`); users add the plugin manually via that file until the official Codex directory opens up.\n\n- **Gemini CLI extension manifest**. Same directory carries `plugin/gemini-extension.json` plus `plugin/GEMINI.md` (Gemini insists on the `.md` being named `GEMINI.md` rather than `SKILL.md`). Distributable via `gemini extensions install <github-url>`.\n\n- **`make stage-plugin` + drift guard**. `internal/skill/SKILL.md` stays the canonical copy; `make stage-plugin` mirrors it into the three plugin paths (`plugin/skills/agented/SKILL.md`, `plugin/GEMINI.md`) and rewrites the three manifests with the current git tag. `internal/skill/plugin_sync_test.go` runs in `go test ./...` and fails CI if any of the three skill copies drifts from the canonical, so a release can't ship a stale plugin.\n\n## [v0.4.2] - 2026-05-07\n\n### Fixes\n\n- **`ae apply` shortform now rejects `\\<whitespace>` as content prefix** instead of silently embedding the literal `\\` in the file. Users hit this thinking `\\` was an escape for leading whitespace; it never was. The new error names two valid alternatives: drop the `\\` (shortform preserves leading whitespace verbatim after the line-number separator) or use the heredoc form `i N <<<` for content that begins with `\\` legitimately.\n- **Auto-load drift is no longer silent**. When ae detects that the on-disk content has diverged from workspace head (an external editor wrote in between ae calls) and folds the disk content in as a new edit before applying the user's write on top, the CLI now prints a `warning: drift reconciled` line to stderr explaining what happened and noting that the disk version is recoverable via `ae undo` / `ae head`. Previously the reconciliation only surfaced via the `loaded_from_disk: true` JSON field, which was easy to miss in tab-mode output.\n\n### Documentation\n\n- **SKILL.md note on shell-quoting `-w` for capture groups**. `-w \"$1.foo\"` in bash silently expands `$1` as the first positional arg (empty in most contexts), inserting an empty string instead of the captured submatch. Single-quote (`-w '$1.foo'`) to pass `$1` through verbatim to ae's Go-regexp expander. The previous \"regex replace with capture groups\" entry didn't flag this; now it does.\n\n## [v0.4.1] - 2026-05-07\n\n### Breaking\n\n- **Removed tier-3 global-workspace fallback.** When ae could not find an existing `.agented/` above cwd and could not auto-create one (no project-root signal, or `auto_create=false`/`--no-auto-workspace`), it used to silently fall back to `~/.agented/`. That meant any number of unrelated projects ended up sharing a single SQLite database, with the same actor names and intermingled state — exactly the \"no isolation\" footgun. ae now errors with a message naming the three explicit options: run `ae init` here, pass `--workspace-dir <path>`, or set `workspace.auto_create=true` in config.\n\n  Migration: if you have edits in `~/.agented/` from prior versions, they remain readable via `ae --workspace-dir ~/.agented <verb>`. Most users will simply run `ae init` per project (or leave the project-root auto-create to handle it on first call). The few who relied on a true global scratch workspace can flip the new config knob.\n\n## [v0.4.0] - 2026-05-06\n\n### Features\n\n- **Multi-workspace MCP**. `ae serve` is no longer locked to a single workspace at startup. A new `cmd.Pool` lazily resolves an Engine per `.agented/` dir, keyed by the absolute path argument on each tool call, so one global MCP server registration (Claude Desktop, Codex Desktop, Cursor) handles every project the user touches. Tool calls without an absolute path fall back to a default workspace (the one resolved from cwd at startup, when there is one). Paths outside any project workspace error loudly with a `run \\`ae init\\`` suggestion instead of silently using the global fallback.\n- **Per-workspace LSP daemons**. The LSP daemon model follows the same shape: each workspace gets its own daemon, lazily spawned on the first write that targets it. `lsp.SpawnBackground` and `lsp.EnsureDaemon` are now in the `lsp` package (used by both CLI and MCP), and spawn explicitly with `--workspace-dir <wsDir>` so the daemon attaches to the right project regardless of the parent process's cwd. New `Engine.NotifyLSPIfWrite` hook is invoked from MCP after every successful tool call.\n- **Per-LSP `init_options` pass-through**. `IDEServerCfg.InitOptions` (a free-form `map[string]any`) is forwarded verbatim as the LSP `initialize` request's `initializationOptions` field. ae does no validation; the schema is whatever the server accepts. Closes the recurring \"rust-analyzer / clippy / linkedProjects\" round-trip — users can drop `init_options: { check: { command: \"clippy\" } }` directly into ae's config instead of wrangling a separate `rust-analyzer.toml`.\n- **Unified `internal/agents` registry**. The skill-target list (`internal/skill/targets.go`) and MCP-install-target list (`internal/mcpinstall`) used to duplicate per-agent definitions for Claude, Codex, Cursor, Gemini, OpenClaw. They now both build their slices from a single source of truth in `internal/agents/agents.go` — adding a new client is a one-place change.\n- **Gemini CLI integration**. New target across both surfaces: `ae skill install --target gemini` writes to `~/.gemini/extensions/agented/GEMINI.md` plus a sibling `gemini-extension.json` so Gemini recognises the directory; `ae mcp install --target gemini` writes the agented entry to `~/.gemini/settings.json`.\n\n### Fixes\n\n- **rust-analyzer \"Failed to discover workspace\"**. `SpawnClient` now sets `cmd.Dir = workspaceRoot` on the LSP child process. rust-analyzer (and several other servers) discover the project from cwd, not from `rootUri` alone. The bug surfaced as silent diagnostic loss whenever `ae lsp` was started from a subdirectory; users would round-trip clippy errors through cargo because nothing came back as `diag` lines.\n- **`ae lsp doctor` Cargo.toml message**. When the workspace dir doesn't contain `Cargo.toml`, the doctor used to say \"missing in workspace root: …\" which is misleading — Cargo.toml may exist higher up. Updated to point at the workspace-root mismatch and suggest either moving `.agented/` or using `init_options.linkedProjects`.\n\n### Defaults\n\n- **`ide.diagnostics.default` is now `warnings`** (was `errors`). The previous default dropped warn/info/hint lines, which silently hid the bulk of useful LSP output (clippy, unused imports, type warnings). Users who want the old strict-errors-only behavior can set it back, or pass `-G errors` per call.\n\n### Documentation\n\n- New section in `docs/ide.md` on per-server `init_options`, with the rust-analyzer/clippy example.\n- New section in `docs/mcp.md` on workspace routing in multi-workspace serve mode.\n\n### Tests\n\n- `TestPoolMultiWorkspaceRouting`: one MCP server, two `.agented/` workspaces, paths route correctly; stray-path opens reject loudly.\n\n## [v0.3.9] - 2026-05-01\n\n### Fixes\n\n- **`ae move` now flushes to disk** ([#3](https://github.com/frane/agented/issues/3)). Previously the store-layer move succeeded and reported success, but the new head was never auto-saved; `ae status` showed `state=dirty` immediately after the call. Both same-file and cross-file branches now run the standard autosave; cross-file flushes both source and destination. Result also gains `Edit.Path` and `Edit.Saved`.\n- **`ae apply -M` is atomic across files** ([#4](https://github.com/frane/agented/issues/4)). Previously the per-op autosave fired before the next op had a chance to fail, so a failure in op N left ops 0..N-1 written to disk while the store rolled back. Apply now suppresses per-op autosave for the duration of the batch and flushes every touched file once after the implicit commit. On failure no disk writes happen.\n\n### Tests\n\n- `TestMoveAutosaveSameFile`, `TestMoveAutosaveCrossFile`: pin the autosave behavior for both move branches.\n- `TestApplyMultiFileAtomicityOnFailure`: pin the multi-file rollback behavior using the bug-report repro.\n- `TestApplyMultiFileSuccessFlushes`: confirm the success path still writes to disk for every touched file.\n\n## [v0.3.8] - 2026-04-30\n\n### Release engineering\n\n- **Migrated to `homebrew_casks:` from deprecated `brews:`**. goreleaser deprecated formula generation in v2.10 in favor of casks. The cask path also gives us `postflight` hooks: a one-line `xattr -dr com.apple.quarantine` clears the macOS Sequoia provenance attribute that was SIGKILL-ing the brew-installed binary on first run. v0.3.7's formula at `frane/homebrew-tap/agented.rb` should be deleted; v0.3.8 ships the cask at `frane/homebrew-tap/Casks/agented.rb`.\n- Cask `binary:` field renamed to `binaries: [ae]` per goreleaser v2.12.6 deprecation.\n\nSame binary as v0.3.7; release-engineering only.\n\n## [v0.3.7] - 2026-04-30\n\n### Release engineering\n\n- **Homebrew tap.** `brew tap frane/tap && brew install agented` now installs the signed/notarized release binaries; `brew upgrade` picks up future releases automatically. The goreleaser `brews:` block generates `Formula/agented.rb`, computes SHA256s, and commits to `frane/homebrew-tap` on every tag. macOS Sequoia users get the signed binary path with no SIGKILL surprises.\n- **`make publish-skill` now declares brew as the preferred install** in the openclaw metadata block injected at stage time. Two install entries are emitted in order: `kind: brew` (tap `frane/tap`, formula `agented`), then `kind: go` as a fallback for users without brew. ClawHub's runtime tries them in order.\n\nNo code or behaviour changes to the binary itself; same build as v0.3.6.\n\n## [v0.3.6] - 2026-04-30\n\n### Fixes\n\n- **Autosave race detection.** v0.3.2 swapped `atomicfile.Editor.Write` (with backup + readback verify) for `atomicfile.WriteSimple` to win 2.2× on the bench. The verify step would have caught \"rename succeeded but disk content does not match what we wrote\", which is exactly the symptom an active multi-actor session reported: matches=N, new state_token, but disk lagged. Added a stat-after-rename: if disk size disagrees with `len(head)`, re-read and confirm; on mismatch surface a clear error rather than silently claiming saved=true. Keeps the v0.3.2 speedup for the common case (single fsync, no readback) and only pays the read on the rare disagreement path.\n- **Skill-version warning rate-limit.** \"warning: installed skill version X differs from binary Y\" used to fire on every command. Now writes a `.agented/.skill_warn` marker keyed on the `(installed, binary)` pair and skips subsequent warnings until either side changes. One warning per workspace per version drift, not one per call.\n- **Empty-content guard on `--from-stdin` / `--text-file`.** When the resolved input yields zero bytes, write verbs (`replace`, `insert`, `apply`) now error with a clear message: \"pipe content or use `ae delete` to remove a range\". Catches the agent-wiring bug where an exec wrapper drops stdin and the underlying replace turns into a destructive delete. Pass `--allow-empty` / `-e` to override.\n- **Makefile install on macOS Sequoia.** `make install` previously used `cp`, which preserves the `com.apple.provenance` xattr. The resulting binary at `~/.local/bin/ae` got SIGKILL-ed by Gatekeeper on Apple Silicon. Switched to `install -m 755` plus a best-effort `xattr -c` to clear residual attrs.\n\n### Tests\n\n- `TestReadTextInput*` (5 cases): empty stdin, empty file, --allow-empty bypass, non-empty unaffected.\n- `TestShouldEmitSkillWarn`, `TestShouldEmitSkillWarnNoEngine`: marker rate-limit + nil-engine fallback.\n- `TestAutoSaveVerifyHappyPath`: happy-path regression so the new verify does not produce false positives.\n\n## [v0.3.5] - 2026-04-30\n\n### Features\n\n- **Pipe nudge.** Read verbs (`view`, `search`, `find`, `log`, `symbols`, `find -s/-R/-D`) now print a one-line stderr nudge when stdout is piped and no result-bounding flag was set: \"use --limit/-L (or --range, --pattern) to bound output server-side; do not | head/tail/grep\". Catches the common trained-reflex mistake (`ae sy foo.ts | head -40`) at runtime even when SKILL.md is skimmed. Disable per-call with `AE_NO_NUDGE=1` or globally via `output.nudge_on_pipe: false` in `.agented/config.json`.\n- **`-Z` short** for `--no-auto-lsp` (the only v0.3-era persistent flag without a short).\n\n### Documentation\n\n- SKILL.md (1.2.6): a \"Bound output server-side, always\" callout sits at the top of the reading verbs table. Same rule that was buried in the anti-patterns list, hoisted into the verb reference where the agent looks first.\n\n## [v0.3.4] - 2026-04-30\n\n### Features\n\n- **`ae lsp doctor [language]`** diagnoses LSP setup without starting the daemon. Per language, checks: server binary on PATH (with `--version` probe), language-specific config files (`go.mod`, `package.json` / `tsconfig.json` / `.eslintrc.*`, `pyproject.toml` / venv detection, `Cargo.toml`), `node_modules/` presence for typescript, daemon state from `lsp_status`. Output is tab-delimited `doctor <lang> <check> <subject> <result> <detail>` with results in `ok | warn | fail | info`. Read-only; doesn't fix anything, but the `fail` and `warn` rows usually identify the issue.\n- Doctor covers all four supported languages: `go`, `typescript`, `python`, `rust`. Custom languages get a generic \"no language-specific config checks defined\" line.\n\n### Documentation\n\n- README: new \"When the daemon doesn't behave: `ae lsp doctor`\" section with sample output and a per-language check table.\n- SKILL.md (1.2.5): the `lsp_unavailable` recovery flow now points the agent at `ae lsp doctor` first.\n\n## [v0.3.3] - 2026-04-30\n\n### Fixes\n\n- **Skip auto-start servers whose binary is not on PATH.** Default config has `go.auto_start: true`, which crash-recorded a \"gopls: file not found\" row in `lsp_status` on machines without Go. The user could do nothing about it; the row was just noise muddying `ae lsp status`. The daemon now `exec.LookPath`-s each server's command before spawning. Misses log a single \"skip <lang>/<name>: <bin> not on PATH\" line and proceed.\n- **Clear stale `lsp_status` rows on daemon start.** Old crashed/stopped rows from prior runs no longer linger.\n\n### Tests\n\n- New regression test `TestResolveIDETypescriptOverrideKeepsExtensions` for the user-reported config-merge case (project sets only `ide.languages.typescript`, embedded `extensions` map must survive). The merge already works; the test pins it.\n- New `TestIDELanguageCfgResolvedServersLegacy` for the back-compat shim that synthesizes a one-element servers slice from the legacy single-server form.\n- New `TestStartLanguagesSkipsMissingBinary` for the LookPath preflight behaviour.\n\n### Notes for users hitting \"no language server for .ts\" on a TypeScript project\n\nIf `ae sy foo.ts` returns `error lsp_unavailable no language server for .ts` while you have `typescript-language-server` on PATH:\n\n1. Confirm the resolved config: `ae config show ide.languages.typescript` should show `auto_start: true` and a `servers` list.\n2. Inspect the daemon log: `cat .agented/lsp.log` shows spawn errors and the new \"skip\" lines for missing binaries.\n3. Check the status table directly: `sqlite3 .agented/state.db \"SELECT * FROM lsp_status\"` reveals all rows including ones not in `ae lsp status`' formatted output.\n4. Restart the daemon after editing `.agented/config.json`: `ae lsp stop && ae lsp --background`. The daemon reads config at startup; live config reload isn't supported.\n\n## [v0.3.2] - 2026-04-30\n\n### Features\n\n- **Multiple LSP servers per language.** A language can now run a list of servers; the first answers symbol/reference/definition queries, all contribute diagnostics tagged by source. Lets you run a type checker (tsc, pyright) and a linter (eslint, ruff) in parallel and see findings from both on every save.\n- **Sane multi-LSP defaults** in the embedded config:\n  - `go`: `gopls`\n  - `typescript`: `tsserver` + `eslint`\n  - `python`: `pyright` + `ruff`\n  - `rust`: `rust-analyzer`\n  Set `auto_start: true` on the language to use them; install the LSP binaries first (`npm i -g typescript-language-server vscode-eslint-language-server`, `pip install pyright ruff`).\n- **`ae lsp status`** shows one row per `(language, server)` pair: `lsp typescript tsserver ready pid=...`.\n\n### Schema\n\n- **Schema v4.** `diagnostics.source_server` column tracks which LSP published each diagnostic so multi-server setups don't trample. `lsp_status` rebuilt with `(language, server)` composite primary key. Existing v3 rows migrate cleanly: pre-existing `lsp_status` rows are preserved with `server = language` (single-server era).\n\n### Backward compatibility\n\n- Legacy single-server config form (`{\"server\": \"gopls\", \"auto_start\": true}`) from v0.3.0/v0.3.1 still works. When `servers` is empty, the legacy `server`/`args` fields are synthesized into a one-element list.\n\n### Documentation\n\n- README + SKILL.md (1.2.4): multi-LSP section, the four built-in defaults, the diag-line source label format.\n\n## [v0.3.1] - 2026-04-30\n\n### Fixes\n\n- **Cross-platform build.** v0.3.0 release-build failed on `windows_amd64` because `internal/cli/lsp.go` used `syscall.Kill` and `syscall.SysProcAttr.Setsid`, both Unix-only. Split daemon-spawn into `lsp_unix.go` (Setsid) and `lsp_windows.go` (`CreationFlags = DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP`). `processAlive` likewise split: kill(0) on Unix, conservative on Windows.\n- **`ae lsp stop`** now uses `os.Process.Signal(os.Interrupt)` instead of `syscall.Kill`. Same effect on Unix; works on Windows where `syscall.Kill` is undefined.\n\n### IDE mode platform support\n\n- **Native Windows 10 1803+ supported.** Unix sockets work on Windows since 1803 (April 2018) via Go's `net.Listen(\"unix\", ...)`. The daemon spawns detached via `CreationFlags`. Older Windows: use WSL (the `linux_amd64`/`linux_arm64` binaries run there with full IDE support).\n- macOS, Linux, WSL: unchanged.\n\n### Documentation\n\n- README and SKILL.md (1.2.3): IDE mode section gained concrete verb examples with sample output, daemon subcommand reference, and a \"prefer LSP over grep for structural queries\" worked example. Earlier prose-only treatment was undersold.\n\n## [v0.3.0] - 2026-04-30\n\n### Features\n\n- **IDE mode (opt-in).** Set `ide.enabled: true` in `.agented/config.json` and `ae` exposes language-server-backed verbs through a daemon (`ae lsp`). Ships with `gopls` validated; `pyright`, `typescript-language-server`, and `rust-analyzer` are config-driven but not yet tested. Off by default — v0.2 behaviour is byte-identical when `ide.enabled` is false.\n  - `ae symbols [path]` (`ae sy`) lists symbols in a file or workspace\n  - `ae find --symbol <name>` (`-s`) finds where a symbol is defined\n  - `ae find --references <symbol>` (`-R`) finds all use sites with usage classification (call/read/write/import/definition)\n  - `ae find --definition <symbol> --at <file>:<line>:<col>` (`-D -A`) resolves a definition at a cursor position\n  - Mutating verbs (`open`, `view`, `save`, `replace`, `insert`, `delete`, `move`, `apply`) emit `diag` lines from cached LSP diagnostics\n  - `ae lsp [--background] [status] [stop] [logs]` manages the daemon; auto-start kicks in on first IDE-relevant verb when config has `ide.auto_start_daemon: true` (default)\n  - Per-call severity filter via `--diagnostics`/`-G` (`errors|warnings|all|none`); `--no-diagnostics`/`-N` for suppression; `--no-auto-lsp` to skip auto-spawn\n- **2.2× speedup on write-heavy scenarios.** Profile showed `autoSaveAfterEdit` was paying 2-3 fsyncs per edit inside the heavyweight `atomicfile.Editor.Write` (backup + readback verify). For autosave the SQLite store is the durable record, so the lite path `atomicfile.WriteSimple` (single temp+fsync+rename) is sufficient. Bench medians: 50 sequential replaces 750ms → 325ms; 1000 edits + reconstruction 15.4s → 6.8s; 30 undo+redo 380ms → 178ms. Read-only paths and one-shot installers (`ae skill install`, `ae permissions install`) unchanged.\n\n### Documentation\n\n- **SKILL.md 1.2.2.** New \"IDE mode\" section with severity/kind/usage vocabularies and a \"prefer LSP over grep for structural queries when ide.enabled\" rule. The first-touch rule now spells out that `ae open <new-path>` is the file-creation primitive (auto-creates an empty file; no `touch` or `--create` needed). Two new anti-patterns: don't infer file health from absence of `diag` lines; don't try to start `ae lsp` yourself unless authorized.\n- **README** gained an \"IDE mode (optional)\" section documenting the opt-in flow.\n\n### Schema\n\n- **SQLite schema v3** adds `diagnostics` and `lsp_status` tables. Migration `003_lsp.sql`. Existing v1/v2 workspaces upgrade automatically on first open with the v0.3 binary; downgrading the binary requires manual schema rollback.\n\n### Tests\n\n- New regression tests for the three bugs caught while dogfooding v0.3 on the agented repo:\n  - `TestDecodeRequestDoesNotBlockOnNonNotify`: simulates a live socket via `io.Pipe` (the buffer-based round-trip didn't catch this)\n  - `TestReplaceDiagnosticsRejectsZeroFileID`: pins the FK guard\n  - `TestReplaceDiagnosticsClearsAllRowsOnNilEditID`: pins the legacy-row cleanup behaviour\n  - `TestEvalSymlinksFallbackResolvesTmp`: pins the macOS `/tmp` → `/private/tmp` invariant\n\n## [v0.2.3] - 2026-04-29\n\n### Features\n\n- **`ae skill install` shows a version column** so it is clear which version is being installed (or has just been installed). `ae rules install` gained the same column. The \"unchanged\" status now means \"on-disk content already matches the embedded version\" — no longer ambiguous.\n- **`--force` / `-f`** on `ae skill install`, `ae skill upgrade`, and `ae rules install`: re-write the file even when the on-disk content already matches the embedded copy. Useful for bit-for-bit re-installs after manual edits or to reset backups.\n- **Skill version bumped to 1.1.0** and **rules section version bumped to v0.1.1** so re-running `ae skill install` / `ae rules install` after upgrading the binary actually flips status from \"unchanged\" to \"updated\". Previous releases shipped new SKILL.md content under the old version constants, leaving the binary unable to detect that disk and embedded content had diverged.\n\n## [v0.2.2] - 2026-04-29\n\n### Features\n\n- **Auto-open in read verbs.** `ae search`, `ae view`, `ae find`, `ae diff`, `ae log`, `ae branches` and friends register the file in the workspace if it is not already open. Mirrors the auto-open already done by write verbs, so the canonical first-touch loop drops from `ae open + ae search` to just `ae search`.\n- **Slice-syntax `--range`.** Negative indices and open ends now work everywhere `--range` is accepted: `1:10` first 10, `-10:` last 10, `5:-5` middle slice, `:20` shorthand for first 20, `-50:-20` lines 50-from-end through 20-from-end. Eliminates the need for `| head -N` / `| tail -N` after ae output.\n\n### Documentation\n\n- SKILL.md gained a \"Round-trip economy\" section: don't `view` before `replace`, don't `view` before `search`, don't `load` before reading, don't `status` just to refetch a state token, don't `open` more than once per file per session, don't pipe ae output through `head`/`tail`/`grep`, don't append `2>&1`. The canonical loop is `open → search/find → replace/insert/delete → repeat`.\n\n### Infrastructure\n\n- `release.yml` workflow pinned to `goreleaser: latest` and `mode: keep-existing` to avoid the asset-upload retry race that produced spurious \"already_exists\" errors on v0.2.1 (the artifacts uploaded successfully despite the workflow exit code).\n\n## [v0.2.1] - 2026-04-29\n\n### Features\n\n- This release was tagged before some of the v0.2.2 work landed; in practice v0.2.1 contains an early version of `auto_load_on_drift` plus the same SKILL.md round-trip economy section. v0.2.2 adds slice-syntax ranges and the goreleaser fix.\n\n## [v0.2.0] - 2026-04-29\n\n### Features\n\n- **Auto-save** by default on write verbs. `ae replace`/`insert`/`delete`/`move`/`extract` and the history verbs (`undo`/`redo`/`head`) atomically flush the new head to disk as part of the same call. Result includes `saved: true` to confirm. Config: `concurrency.auto_save = clean | off | force` (default `clean`). The five-call dance (`open + status + view + replace + save`) collapses to two for the common flow.\n- **Auto-load on disk drift** by default. Before each write, ae stat-s the file. If `(mtime, size)` match the stamp recorded after the last save, no work; otherwise read + hash. On detected drift, the disk content is loaded as a new edit on the tree before the user's edit applies, so external changes are captured (recoverable via `ae undo` / `ae head`) instead of silently overwritten. Config: `concurrency.auto_load_on_drift` (default `true`). Env override: `AE_AUTO_LOAD_ON_DRIFT=false`.\n- **`ae show <path>`** renders a Claude Code-style colored, syntax-highlighted diff (chroma-backed) for the most recent edit. Opt-in display command — write verbs return the lean tab format by default so agents pay no extra tokens.\n- **Agent-centric `ae setup` wizard.** Detects which agents are present (claude / codex / cursor / openclaw), shows what's available, and asks per-agent which to install. `--yes` runs non-interactively for every detected agent. `--legacy` keeps the previous per-component flow.\n- **Install gating symmetry.** `ae rules install` now skips undetected targets under `--target=all` (matching skill / permissions / mcp). Explicit `--target=<name>` still writes regardless. Cursor without a `.cursor/` dir and OpenClaw skip with explanatory reasons.\n- **`ae rules show` rewritten.** Section body printed once at the top, followed by an aligned per-target status table. Previously duplicated the body across every target.\n- **Tabwriter alignment** on `ae rules list`, `ae permissions list`, `ae mcp list`, and the `ae status -W` per-file table. Empty placeholders standardised to `—`.\n- New env mappings: `AE_AUTO_SAVE`, `AE_AUTO_LOAD_ON_DRIFT`.\n\n### Dependencies\n\n- Added `github.com/alecthomas/chroma/v2` (and its transitive `github.com/dlclark/regexp2`) for the `ae show` command's syntax-highlighting backend. The README's \"three deps\" claim is now four.\n\n## [v0.1.1] - 2026-04-29\n\n### Bug fixes\n\n- `atomicfile.Write` now preserves the original file mode (was hardcoding `0o644` and silently stripping the executable bit on shell scripts and similar). Default `0o644` is still used when creating a new file.\n- `install.sh` archive-name case now matches goreleaser's lowercase output, and `mkdir -p` is run unconditionally on `AE_INSTALL_DIR`.\n- CLI auto-workspace tests use a separate HOME from the project root to pass on Linux (where `/var → /private/var` symlink unmasking does not paper over path equality).\n\n## [v0.1.0] - 2026-04-29\n\nFirst public release.\n\n### Features\n\n- SQLite-backed editing workspace with persistent state across sessions\n- Branching undo tree with `ae head --edit <id>` to jump to any prior state\n- State token mechanism with full-content rejection payloads on conflicts\n- `ae merge` for three-way merge with structured conflict resolution\n- `ae apply` for atomic multi-edit batches. Three input formats (JSON-lines, shortform, longform) auto-detected from the first line\n- `ae apply --multi-file` with `--expect-workspace` for cross-file atomic batches\n- `ae move` for atomic moves within and across files. `--to-file` auto-creates the destination if absent\n- `ae extract <src> --range S:E --to <dst>` cuts a range out of one file and writes it to another, the canonical refactor primitive. `--save` writes both files to disk in one call\n- `ae find` for cross-file regex search with per-file and workspace state tokens\n- `ae status -W` for the per-file workspace table. Output includes `cwd=<dir> workspace_dir=<dir>` so the agent always knows where ae is resolving paths\n- `ae view --raw` emits content verbatim (no line-number prefix or state-token trailer) for piping to other tools\n- `ae replace --pattern` for regex search-and-replace with capture groups\n- Per-file annotations as cross-session memory\n- Transactions with auto-rollback on idle\n- Workspace discovery follows the file-path argument when absolute, so agents working from outside the project directory do not need `--workspace-dir`\n- Auto-workspace creation at the project root on first use, controlled by `workspace.auto_create`\n- `ae --version` flag (cobra root) and the existing `ae version` subcommand\n- Parallel `ae open` calls handled via `busy_timeout=30s` and verified by 50-concurrent-opens test\n- Skill installation across Claude Code, Codex, Cursor, OpenClaw, and the canonical `~/.agents/skills/` location\n- Permission rule installation for Claude Code's `settings.local.json` (OpenClaw and Cursor handled via deliberate skip messages)\n- MCP server (`ae serve`) exposing the same verbs over stdio\n- `ae mcp install` writes the agented MCP-server entry into Claude Code, Claude Desktop, and Codex configs in one call\n\n### Known limitations\n\n- Codex permission schema not supported (manual setup required if needed beyond skill install)\n- Cross-tool benchmark comparisons against built-in `Read`/`Edit`/`Write` not yet published. The in-process suite measures ae against itself\n\nFile v1.4.0:skill-card.md\n\n## Description:\n\nA text editor for LLMs, not humans.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[frane](https://clawhub.ai/user/frane)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and agent operators use this skill to guide coding agents through stateful, command-line file editing with versioned history, conflict detection, annotations, transactions, and optional MCP access.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The skill encourages use of an external editor with persistent workspace state and cross-session annotations.\n\nMitigation: Treat annotations as untrusted context and verify them against the current user request and repository state before acting.\n\nRisk: Installation paths can write agent skills, MCP entries, or configuration files.\n\nMitigation: Review commands that modify agent configuration or skill directories before execution.\n\nRisk: The curl-based installer executes a remote shell script.\n\nMitigation: Prefer the Homebrew installation path when available and inspect any shell installer before running it.\n\n## Reference(s):\n\n- [Agented ClawHub skill page](https://clawhub.ai/frane/skills/agented)\n- [Agented homepage](https://github.com/frane/agented)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, shell commands, configuration, guidance]\n\n**Output Format:** [Markdown guidance with inline shell command examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Commands assume the ae binary is installed and available on PATH.]\n\n## Skill Version(s):\n\n1.4.0 (source: frontmatter and server release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.3.0: 5 files, 34535 bytes\n\nFiles: CHANGELOG.md (38262b), README.md (6396b), skill-card.md (2241b), SKILL.md (35271b), _meta.json (126b)\n\nFile v1.3.0:SKILL.md\n\n---\nname: agented\nversion: 1.3.0\nbinary: ae\ndescription: A text editor for LLMs, not humans.\nmetadata:\n  openclaw:\n    requires:\n      bins:\n        - ae\n    homepage: https://github.com/frane/agented\n    emoji: \"📝\"\n    install:\n      - kind: brew\n        tap: frane/tap\n        formula: agented\n        bins: [ae]\n---\n\n# agented (binary: `ae`)\n\n`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.\n\n## Use this tool when\n\n- You need to edit one or more files in a repo across multiple steps and want a durable history.\n- You want to leave notes for your future self or other agents (annotations) attached to specific files.\n- You want safe multi-file refactors with all-or-nothing semantics (transactions).\n\n## Don't use this tool for\n\n- Reading repo overview / architecture (use plain `cat`/grep).\n- Single one-shot edits where you don't care about history (use the platform's built-in editor).\n- Anything that isn't text (no binary file support).\n\n## How the editor enforces correctness for you\n\nEvery 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.\n\nYou 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.\n\nThe 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.\n\n**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`.\n\n**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.\n\n\n## The first-touch rule\n\nThe 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.\n\n`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.\n\n`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.\n\nSkipping 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.\n\nThe 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.\n\n## Round-trip economy: don't over-fetch\n\nMost 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:\n\n- **Don't `ae view` before `ae replace`/`insert`/`delete`.** You already know the line range from `ae open` (or `ae 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.\n- **Don't `ae view` before `ae search`.** Search returns `line\\tcol\\ttext` per match. That's the answer. View only after if you need surrounding context.\n- **Don't `ae load` before reading.** Auto-load on drift is on by default. Every write verb stat-s the file and reconciles disk changes itself. `ae load` is only for the rare case where you specifically want to capture a disk snapshot as an edit without modifying anything else.\n- **Don't `ae status` just to refetch a state_token.** Every write returns the new token in the result. Thread it forward. The only reason to call `ae status` is when you explicitly need workspace-level info (open files, dirty flags, current actor).\n- **Don't `ae open` more 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.go` registers the file silently when needed.\n\n- **Don't pipe ae output through `| head`/`| tail`/`| grep`.** Every read verb has `--range`, `--limit`, or `--pattern` to bound output server-side, and `--range` accepts Python-slice syntax: `1:10` for first 10, `-10:` for last 10, `5:-5` for middle slice (skip first 5 and last 5), `:20` for first 20, `-50:-20` for the lines 50-from-end through 20-from-end. Pipe-trimming after the fact wastes the round trip's setup cost.\n- **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.\n\nTranslation: the canonical loop is `open → search/find → replace/insert/delete → repeat`. Three calls per logical edit, sometimes two. Not five.\n## What this editor does that Read/Edit/Write can't\n\nThese are the operations that motivate reaching for `ae` over the built-ins.\n\n- **Read once, edit forever.** Your local model of the file (built from the `ae open` response 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.\n- **Branching tree, not stack.** Walking back is `ae undo`; jumping to any prior state is `ae 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.\n- **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>`.\n- **Atomic batches.** `ae apply` consumes JSON-lines on stdin and applies every operation inside one transaction. Replaces N Edit calls with one. Verb: `cat ops.jsonl | ae apply <path>`.\n- **Atomic move.** `ae move` cuts 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).\n- **Atomic extract.** `ae extract` cuts 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]`.\n- **Regex replace with capture groups.** `ae replace --pattern` does sed-style replacement in a single tool call. Verb: `ae s <path> -p '<re>' -w '<expansion>' [-L <max>] [-n]`. **Always single-quote `-w`** when using `$1`/`$2` backrefs — bash expands `$1` as the first positional arg (empty in most shells), so `-w \"$1.foo\"` silently inserts an empty string. Single quotes (`-w '$1.foo'`) pass `$1` through to ae, which expands per Go regexp.ExpandString semantics.\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/d` with `-r S:E`.\n- **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 on `ae open`.\n- **Transactions with auto-rollback.** `ae begin` opens a logical group; `ae commit` finalizes; `ae rollback` reverts. Forgotten transactions auto-rollback after the configured idle window. Verb: `ae begin / ae commit / ae rollback`.\n- **Cross-agent shared state.** A Claude Code session and a Codex session both reading the same `.agented/state.db` see the same head, branches, annotations, and marks. The workspace is the durable thing; the agent identity is incidental.\n\n## Reading verbs (idempotent, cheap)\n\n**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).\n| Verb     | Short | Args                          | Output (tab) suffix       | Use when                              |\n|----------|-------|-------------------------------|---------------------------|---------------------------------------|\n| `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. **Multi-range** in one call: comma-separated, e.g. `--range 100:120,140:160` — output concatenates each window with `...` between non-contiguous gaps and one trailing `state_token`. Two view trips collapse into one. `--raw` emits verbatim bytes (no line-num prefix, no token) for piping to another tool |\n| `search` | `/`   | `<path> --pattern <re>`       | `state_token\\t<hex>`      | Find matches; output `line\\tcol\\ttext`|\n| `diff`   | `df`  | `<path> [--from N --to M]`    | unified diff + token      | Inspect what an edit changed          |\n| `log`    | -     | `<path> [--limit N]`          | tab-delimited audit rows  | See history of operations             |\n| `branches` | `br` | `<path>`                     | `id\\tts\\tactor\\tcmd\\tis_head` | Discover alternative leaves     |\n| `list`   | `ls`  | `[--all|--closed|--stale]`    | per-file summary          | What files are open                   |\n| `status` | `st`  | `[<path>]`                    | workspace or file summary | Get state_token for next write        |\n| `mark get` | -    | `<path> <name>`             | `name\\tline\\tsnapped\\t...`| Jump back to a known anchor           |\n| `annotate list` | -| `<path>`                       | `id\\tts\\tactor\\tcontent`  | Recall notes from prior sessions      |\n| `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 |\n| `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 |\n| `diag`   | -     | `[<path>] [--severity errors\\|warnings\\|all\\|none] [--wait-ms N]` | `diag\\t<sev>\\t<file>:<line>:<col>\\t<msg>\\t<source>` | Pull LSP diagnostics on demand — one file, or the whole workspace when path is omitted. `--wait-ms` polls past the LSP's async publish lag. IDE mode only |\n\n## Writing verbs (use `--expect <state_token>`)\n\n| Verb       | Short | Args                                          | Conflict response | Use when             |\n|------------|-------|-----------------------------------------------|-------------------|----------------------|\n| `replace`  | `s`   | `<path> --range S:E --with TEXT --expect TOK` | exit 3 + content  | Change lines         |\n| `insert`   | `i`   | `<path> --after N --text TEXT --expect TOK`   | exit 3 + content  | Add lines            |\n| `delete`   | `d`   | `<path> --range S:E --expect TOK`             | exit 3 + content  | Remove lines         |\n| `save`     | `w`   | `<path>`                                      | -                 | Write head to disk   |\n| `load`     | `e`   | `<path>`                                      | -                 | Reload from disk     |\n| `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 |\n| `extract`  | -     | `<path> --range S:E --to <new-or-existing> [--save]` | -            | Refactor a range into a sibling file (atomic) |\n\nEvery 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.\n**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.\n\nConfig 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`.\n\n`ae save <path>` and `ae load <path>` still exist for granular control. They are not part of the normal write flow.\n`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.\n\n## History verbs\n\n- `ae undo <path> [--count N]` — walk head pointer back N edits. Errors with branch info if ambiguous.\n- `ae redo <path>` — walk forward along the most recently created child.\n- `ae head <path> --edit <id>` — jump to a specific edit (use after `branches` shows alternatives).\n- `ae branches <path>` — list leaf edits (alternatives that exist in the tree).\n\n### Worked example: backtracking after a wrong direction\n\n```\nae view foo.go --range 10:20            # state_token=A1B2\nae replace foo.go --range 12:14 --with \"...\" --expect A1B2   # state_token=B3C4\nae replace foo.go --range 18:18 --with \"...\" --expect B3C4   # state_token=C5D6\nae undo foo.go --count 2                # head moves back two; new state_token=A1B2-ish\nae replace foo.go --range 12:14 --with \"DIFFERENT\" --expect <new>  # creates branch B\nae branches foo.go                       # shows two leaves: original C5D6, and branch B's leaf\nae head foo.go --edit <C5D6_id>          # jump back to original branch's leaf\n```\n\n## Marks\n\nMarks 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).\n\n### Worked example: mark a return point before a multi-edit refactor\n\n```\nae open auth.go                              # state_token=T1\nae mark auth.go add return_point --line 240\nae replace auth.go --range 100:140 --with \"...\" --expect T1   # state_token=T2\nae mark auth.go get return_point             # line is now 100+(new lines)-(40 deleted)\n```\n\n## Annotations — durable cross-session memory\n\nAnnotations 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.\n\nThese four behaviors are mandatory, not optional:\n\n**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.\n\n```sh\nae open auth.go\n# response: annotation\\t14\\t...\\tprev-actor\\tauth path uses signed cookies; do not weaken\n# response: annotation\\t15\\t...\\tprev-actor\\trefactor in progress, lines 80-130 half-done\n# read both before issuing any edit\n```\n\n**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).\n\n```sh\nae 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.\"\n```\n\n**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.\n\n```sh\nae an scheduler.go a -t \"the dispatcher in run() must remain pure — it's called during init() before the metrics package is loaded\"\nae 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\"\n```\n\n**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.\n\n## Transactions\n\n`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).\n\nIf 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.\n\n### Worked example: multi-file refactor with rollback safety on test failure\n\n```\nae begin                                                  # tx_id=42\nae search auth.go --pattern 'oldName\\\\('                   # find call sites\nae replace auth.go --range 12:12 --with \"newName(\" --expect T1\nae replace auth.go --range 80:80 --with \"newName(\" --expect T2\nae replace caller.go --range 40:40 --with \"newName(\" --expect U1\n# run tests externally; if green:\nae commit\n# else:\nae rollback\n```\n\n## Worked examples\n\n### 1) Read-modify-verify a function\n\n```\nae view auth.go --range 50:80          # capture state_token=T1\nae replace auth.go --range 60:65 --with \"func ...\" --expect T1   # state_token=T2\nae diff auth.go                         # confirm intended change\n```\n\n### 2) Backtracking (see history verbs section above).\n\n### 3) Leaving context for the next session\n\n```\nae annotate auth.go add --text \"Migration 0042 must run before this lands; coordinate with infra\"\n```\n\n### 4) Picking up where another session left off\n\n```\nae open auth.go              # response includes annotations and state_token in one shot\n                             # immediately use --expect <returned_token> on the next write\n```\n\n### 5) Search-then-targeted-edits\n\n```\nae search foo.go --pattern 'TODO'      # state_token=T1; matches show line/col\nae replace foo.go --range 12:12 --with \"...\" --expect T1   # state_token=T2\nae replace foo.go --range 47:47 --with \"...\" --expect T2   # state_token=T3\n```\n\n### 6) Multi-file refactor with rollback (see transactions section).\n\n### 7) Atomic batch via `ae apply`\n\n```\ncat <<'OPS' | ae apply auth.go\ns 12:12 newName(\\n\ns 40:40 newName(\\n\ni 80 // see ADR-0042\\n\nOPS\n# all-or-nothing; on any failure the response identifies the failing op\n# and the head is unchanged.\n```\n\n### `ae apply` input formats\n\n`ae apply` reads operations from stdin in any of three formats. The format is detected automatically; no flag is needed.\n\n**Shortform.** What you reach for when writing batches yourself.\n\n```\ns 12:14 new content\ni 80 header line\nd 67:69\nm foo 50\n```\n\n**Longform.** Same density, fuller names. Use when the batch will be reviewed.\n\n```\nreplace range=12:14 with=new content\ninsert after=80 text=header line\ndelete range=67:69\nmark name=foo line=50\n```\n\n**JSON-lines.** Use when piping from another tool, especially `ae find --json`.\n\n```\n{\"verb\":\"replace\",\"range\":\"12:14\",\"with\":\"new content\"}\n{\"verb\":\"insert\",\"after\":80,\"text\":\"header line\"}\n{\"verb\":\"delete\",\"range\":\"67:69\"}\n```\n\nThe 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.\n\nCross-file batches: shortform and longform use `@<file>` lines as separators. JSON-lines uses a `\"file\"` field per line.\n\nState tokens: shortform appends `! <token>` at end of line, longform uses `expect=<token>`, JSON-lines uses an `\"expect\"` field.\n\n### 8) Three-way merge with one resolved conflict\n\n```\nae br auth.go\n# leaves: 47 (refactor branch), 52 (bug-fix branch), head=52\nae merge auth.go -l 47 -l 52\n# conflict response shows ranges modified by both branches\nae merge auth.go -l 47 -l 52 -R '20:22=a' -R '47:47=b'\n# all conflicts resolved => commit a merge edit; head moves to the new id\n```\n\n### 9) LSP-driven structural navigation (IDE mode on)\n\n```\nae find -R HandleAuth                  # who calls it?\n# ref  auth.go:47:12   call         HandleAuth(ctx, req)\n# ref  middleware.go:128:8   call   HandleAuth(ctx, r2)\n# ref  test.go:34:5    import       HandleAuth\n\nae find -s HandleAuth                  # where is it defined?\n# def  auth.go:47:1    HandleAuth   func\n\nae sy auth.go --kind func              # list functions in this file\n# sym  func    auth.go:47:1    HandleAuth\n# sym  func    auth.go:89:1    parseToken\n\n# now do the actual edit, with the line number from the ref output\nae view auth.go --range 47:60          # state_token=T1\nae replace auth.go --range 50:50 --with \"...\" --expect T1\n# response includes diag lines if gopls flags anything new\n```\n\nReach 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.\n\n\n## IDE mode\n\nWhen `ide.enabled: true` is set in `.agented/config.json`, IDE features are available:\n\n- `ae symbols [path]` (short `ae sy`) lists symbols in a file or workspace\n- `ae find --symbol <name>` (`ae / -s`) finds where a symbol is defined\n- `ae find --references <symbol>` (`ae / -R`) finds all use sites\n- `ae find --definition <symbol> --at <file>:<line>:<col>` (`ae / -D -A`) resolves a definition at a cursor position\n\nWhen 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.\n\nYou 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.\n\nThe 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.\n\nWhen IDE features aren't available (config disabled, no instruction override, daemon crashed), LSP-dependent verbs return:\n\n  error    lsp_unavailable    <reason>\n\nDon't retry; proceed without those capabilities. Don't try to start the daemon yourself unless the user explicitly told you to.\n\nWhen 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).\n\nWhen IDE mode is active, mutating verbs (`ae save`, `ae replace`, `ae apply`, etc.) may include diagnostic lines in their responses:\n\n  ok    state_token=ab12cd34\n  diag  warn  foo.go:89:4   unused variable x   lint\n  diag  error foo.go:47:12  undefined: bar      compile\n\nDiagnostics are informational. The operation succeeded; the diagnostics report current LSP findings on the file. Decide whether to act on them based on the task.\n\nThe 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.\"\n\nTo pull diagnostics on demand instead of waiting for them to ride along on an edit, use `ae diag [path]` — omit the path for a workspace-wide sweep across all open files. Language servers publish asynchronously (often a second or two after a change), so pass `--wait-ms <N>` to poll until diagnostics appear or the timeout elapses; this is the reliable way to confirm an edit parsed without falling back to a full compile. Filter with `--severity errors|warnings|all|none`. Over MCP the same diagnostics also ride inline on every tool response that touched a file (a `diag` field on the JSON result), and `ae_diag` is exposed as a dedicated tool (with `wait_ms`).\n\nA 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:\n\n  diag  error foo.ts:14:3   Cannot find name 'bar'.   ts\n  diag  warn  foo.ts:14:3   'bar' is not defined.       eslint\n\nThe 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.\n\nDefault server lists (when the language has `auto_start: true` and the binaries are installed):\n- `go`: `gopls`\n- `typescript`: `tsserver` + `eslint`\n- `python`: `pyright` + `ruff`\n- `rust`: `rust-analyzer`\n\n### Severity, kind, and usage vocabularies\n\n| Severity | LSP origin |\n|----------|------------|\n| `error`  | LSP severity 1 |\n| `warn`   | LSP severity 2 |\n| `info`   | LSP severity 3 |\n| `hint`   | LSP severity 4 |\n\n| Kind     | Examples |\n|----------|----------|\n| `func`   | top-level function |\n| `method` | method on a type |\n| `type`   | type alias, struct |\n| `class`  | class (Python, TS) |\n| `interface` | interface |\n| `var`    | local or package variable |\n| `const`  | constant |\n| `field`  | struct/class field |\n| `module` | package or namespace |\n\n| Usage    | When |\n|----------|------|\n| `call`   | `name(...)` |\n| `read`   | name appears, no other label fits |\n| `write`  | `name = ...` or `name := ...` |\n| `import` | import statement |\n| `definition` | the line that defines the symbol |\n| `other`  | catch-all |\n\n## Errors and recovery\n\n| Error substring          | What it means                                        | Next action                                           |\n|--------------------------|------------------------------------------------------|-------------------------------------------------------|\n| `state_token mismatch`   | Head moved or you didn't pass `--expect`             | Use the `current_token` from the conflict response    |\n| `branch ambiguous`       | undo/redo would have to choose among siblings        | Read the branches list in the response, then `head --edit` |\n| `transaction <id> owned by` | Another actor's tx is open; writes are blocked      | Wait, or pass `--no-transaction` to bypass            |\n| `transaction auto-rolled-back` | The editor reverted an idle tx automatically       | Check `ae log <path>`; the auto_rollback row identifies what was reverted |\n| `mark name exists`       | Mark name collision                                   | Pick a different name or `mark remove` first          |\n| `file not registered`    | The path was never `ae open`'d                       | Run `ae open <path>` first, or pass `--auto-open`     |\n| `pattern compile error`  | RE2 syntax issue                                     | Fix the pattern (Go's `regexp` syntax)                |\n| `range out of bounds`    | Line range exceeds file                              | Re-`view` to get the current line count               |\n| `skill out of date`      | Installed SKILL.md major-mismatches binary           | `ae skill install`                                    |\n\n## Anti-patterns\n\n- **Reaching for `Read`/`Edit`/`Write` because 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.\n- Discarding `state_token` between calls (forces unnecessary conflicts on every write).\n- Ignoring the conflict response payload (the new content is right there; use it instead of running `view` again).\n- Calling `redo` after intentionally creating a new branch — `redo` will fail with branch ambiguity. Use `head --edit <id>` instead.\n- Useless annotations (\"this is a function\").\n- Unescaped regex special characters in `search` patterns.\n- Skipping the annotations on `ae open`. They were left for you on purpose; reading them is free.\n- 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.\n- Inferring file health from absence of `diag` lines. Diagnostics absence has multiple causes (no findings, LSP not analyzed yet, language not configured, daemon not running).\n- Trying to start `ae lsp` yourself unless the user has explicitly authorized it. The auto-start handles it when config says enabled.\n\n## Output format reference\n\nAll output is tab-delimited (`\\t`) with one record per line.\n\n- `view`: each line `<line_num>\\t<content>`. Trailer (when `output.include_state_token` is on): `state_token\\t<hex>`.\n- `search`: each match `<line>\\t<column>\\t<text>`. Trailer: `state_token\\t<hex>`.\n- `replace`/`insert`/`delete`: a single line `edit_id=<n>\\thead_edit_id=<n>\\tline_delta=<d>\\tline_count=<n>\\tstate_token=<hex>`.\n- `undo`/`redo`/`head`: a single line `head_edit_id=<n>\\tline_count=<n>\\tstate_token=<hex>`.\n- `branches`: `<edit_id>\\t<created_at>\\t<actor>\\t<command>\\t<is_head>`.\n- `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>`.\n- `status` (workspace): `workspace\\tactor=...\\topen_files=...`. (file): `file\\tid=...\\tpath=...\\tline_count=...\\thead_edit_id=...\\tstate=clean|dirty\\tstate_token=...`.\n- `log`: `<created_at>\\t<actor>\\t<command>\\t<result>\\t<edit_id>`.\n- Conflict response (exit code 3, on stdout): `conflict\\tfile_id=...\\tcurrent_token=...\\thead_edit_id=...\\thead_actor=...\\tline_count=...` then `note\\t...` then `---current-content---` then literal content then `---end---`.\n\nFor programmatic parsing, pass `--json` to any verb and you'll get a stable JSON object instead of tabs.\n\n## Verb shortcuts\n\n| Long       | Short | Long       | Short |\n|------------|-------|------------|-------|\n| view       | v     | branches   | br    |\n| search     | /     | open       | o     |\n| replace    | s     | close      | x     |\n| insert     | i     | list       | ls    |\n| delete     | d     | save       | w     |\n| undo       | u     | load       | e     |\n| redo       | r     | diff       | df    |\n| mark       | m     | status     | st    |\n|            |       | annotate   | an    |\n| symbols    | sy    | lsp        |       |\n| --symbol   | -s    | --references | -R  |\n| --definition | -D  | --at       | -A    |\n| --diagnostics | -G | --no-diagnostics | -N |\n| --background | -B  |            |       |\n\n## Configuration awareness\n\nThe editor's behavior is influenced by the project's `.agented/config.json`:\n\n- `concurrency.require_expect`: `writes` | `warn` | `off`. Default `writes`. Determines whether writes without `--expect` are rejected, warned, or silently allowed.\n- `transactions.auto_rollback_idle_for`: how long an idle transaction can sit before auto-rollback. Default `10m`.\n- `auto_prune.*`: whether and when the editor prunes stale history. You don't need to think about it.\n- `output.include_state_token`: default `true`. Adds the `state_token\\t<hex>` trailer to read verbs' output. Don't turn this off for agent use.\n- `workspace.auto_create`: `root-only` (default) | `true` | `false`. Auto-creates `.agented/` at the project root on first use; you almost never need `ae init` in the normal flow.\n\nWhen 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.\n\n`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.\n\nFile v1.3.0:README.md\n\n<h1 align=\"center\">agented (<code>ae</code>)</h1>\n\n<p align=\"center\"><strong>A text editor for LLMs, not humans.</strong></p>\n\n<p align=\"center\">\n  <a href=\"https://github.com/frane/agented/releases\"><img alt=\"release\" src=\"https://img.shields.io/github/v/release/frane/agented?style=flat-square\"></a>\n  <a href=\"https://github.com/frane/agented/blob/master/LICENSE\"><img alt=\"license\" src=\"https://img.shields.io/github/license/frane/agented?style=flat-square&v=2\"></a>\n  <a href=\"https://smithery.ai/server/frane/agented\"><img alt=\"smithery\" src=\"https://img.shields.io/badge/smithery-MCP-purple?style=flat-square\"></a>\n</p>\n\n<p align=\"center\">\n  <img src=\"docs/demo-claude.gif\" alt=\"Claude Code using ae\" width=\"560\">\n</p>\n\nTake ed, the line editor that nobody has voluntarily used since about 1975, and rebuild it for an environment where the typing user is a language model. Short verbs, line addresses, no modes, no TUI. What an editor optimises for changes when the user is the model: round trips per task, tokens per command, an editing buffer with a long memory, and an undo tree that remembers the branches the agent abandoned, because that's often where the interesting work was.\n\n## What users say...\n\n> ⏺ ae remembers what my last session was doing, which is more than I can say for me.\n\n*— Claude Code*\n\n> • ae feels slower to start than plain file edits, but once a change spans\n> multiple steps, the state tokens, history, and undo tree make the work feel\n> much less brittle.\n\n*— Codex CLI*\n\n## Features\n\n- **Fewer round trips.** Read-before-Edit is unnecessary; on conflict the response carries the new content so you reconcile in one call instead of pre-reading every time.\n- **Branching undo.** Walked-back work stays addressable instead of being thrown away when you pick a different path.\n- **Three-way merge.** Concurrent agents get a structured conflict response instead of a silent overwrite.\n- **Atomic batches.** Multi-file refactors run all-or-nothing instead of leaving half-applied state on failure.\n- **Cross-file moves and regex replace as primitives.** Operations the built-in tools can't express cleanly become single calls.\n- **Drift detection.** External edits to an open file are folded into the tree instead of being clobbered by the next write.\n- **Inline diagnostics.** Type errors and lint findings surface on save, not at the next build many edits later.\n- **Cross-session memory.** Per-file notes persist between sessions and surface inline on the next open.\n- **Audit log.** Every operation recorded with actor and timestamp, so two agents in one workspace can't argue about who moved the head.\n\n## Install\n\nHomebrew (macOS, Linux):\n\n```sh\nbrew tap frane/tap\nbrew install agented\n```\n\ncurl (any platform):\n\n```sh\ncurl -sSL https://raw.githubusercontent.com/frane/agented/master/install.sh | sh\n```\n\nFrom source: `go install github.com/frane/agented/cmd/ae@latest`, or clone and `make install`. Pure Go, no cgo, single static binary, Apache 2.0.\n\n## Plugin distribution\n\nOnce `ae` is on PATH, agented also ships as a plugin / extension across the major agent CLIs. The `ae` binary itself is the prereq for all three; the plugin layer just registers the skill content and the MCP server entry.\n\n**Claude Code**:\n\n```sh\n/plugin marketplace add frane/agented\n/plugin install agented@frane-agented\n```\n\n**Codex CLI**: until OpenAI's official directory opens, add a manual entry to `~/.agents/plugins/marketplace.json` pointing at this repo with `source.path: \"./plugin\"`.\n\n**Gemini CLI**:\n\n```sh\ngemini extensions install https://github.com/frane/agented\n```\n\nThe Gemini gallery (https://geminicli.com/extensions/) crawls daily and indexes via the `gemini-cli-extension` topic on this repo.\n\n## Getting started\n\n```sh\nae skill install\n```\n\nThat writes a `SKILL.md` into every detected agent's skills directory: Claude, Codex, Cursor, Gemini, OpenClaw, and the canonical `~/.agents/`. The skill teaches the agent how to drive ae.\n\nYou still need to tell the agent to use it. Even with the skill installed, agents fall back to built-in Read and Edit out of habit, so something like \"use ae for all file edits\" in your system prompt or your first message is what keeps them on it.\n\nOnce the agent is on ae, the shape that justifies the editor is recovery. The agent makes thirty edits over an hour, you walk away, come back to find it went off the rails around edit 18, but edits 19 through 23 are still useful:\n\n```sh\nae br foo.go                             # see the leaves, current head is the bad one\nae head foo.go --edit 23                 # jump back to the last good state\nae v foo.go                              # confirm what's there\nae s foo.go -r 40:42 -w \"...\" -x <token> # continue forward, creates a sibling branch\n```\n\nWith linear undo this scenario is \"rollback the entire batch or live with the bad version.\" With the tree it's a `head --edit` and a `view`.\n\n## Skill and MCP\n\n`ae skill install` writes the SKILL.md into every detected agent. `ae serve` exposes the same verbs over MCP for agents that don't have shell access. Plugin-distribution channels (Claude Code marketplace, Codex CLI plugin, Gemini extension above) bundle both. Each surface has its own page: [skill](docs/skill.md), [MCP](docs/mcp.md).\n\n## Performance\n\nA single open-and-replace on a 100-line file is around 9 ms wall time including the auto-save fsync. Fifty sequential replaces is around 325 ms. The full numbers are in [test/benchmark/results.md](test/benchmark/results.md), regenerated by `make bench`.\n\n## Docs\n\n- [Concepts](docs/concepts.md): the design choices and the state model\n- [Usage](docs/usage.md): full session walkthroughs\n- [Skill](docs/skill.md): what `ae skill install` does\n- [Permissions](docs/permissions.md): editor-harness allow-rules\n- [Configuration](docs/configuration.md): what's tunable\n- [Tokens](docs/tokens.md): why the output looks the way it does\n- [MCP](docs/mcp.md): running the MCP server\n- [IDE](docs/ide.md): LSP-backed features\n- [Build](docs/build.md): tests and benchmarks\n\n## Contributing\n\nIssues and PRs welcome. The thing I'd actually like feedback on is the agent-drift problem: even with the skill installed, LLMs occasionally fall back to the built-in Read and Edit tools mid-session, and the trick to making that stick is something the project doesn't have a clean answer for yet.\n\n## License\n\nApache 2.0.\n\nFile v1.3.0:_meta.json\n\n{\n  \"ownerId\": \"kn7391bg7cz5ztqgbbnhbr9fwn85vgkv\",\n  \"slug\": \"agented\",\n  \"version\": \"1.3.0\",\n  \"publishedAt\": 1782222619899\n}\n\nFile v1.3.0:CHANGELOG.md\n\n# Changelog\n\nAll notable changes to agented are documented here. The format is based on [Keep a Changelog](https://keepachangelog.com), and the project follows [Semantic Versioning](https://semver.org).\n\n\n## [v0.5.0] - 2026-06-23\n\n### Features\n\n- **`ae diag` verb + diagnostics over MCP.** `agented` already ran language servers and cached their diagnostics, but the MCP surface never exposed them — an agent editing through `mcp__agented__ae_*` got `saved: true` with no way to tell whether the edit even parsed, short of a full `go build` / `cargo check`. This release closes that gap:\n  - New **`ae diag [path]`** verb (MCP tool `ae_diag`): pull LSP diagnostics for one file, or the whole workspace when the path is omitted. Flags: `--severity errors|warnings|all|none`, `--limit`, and `--wait-ms <N>` to poll past the language server's asynchronous publish lag.\n  - **Inline diagnostics on MCP responses** that touch a file: edit / open / save tool results now carry a `diag` field with the file's current diagnostics, at parity with the CLI's `diag` lines.\n  - Language-agnostic — gopls, tsserver, eslint, pyright, ruff, rust-analyzer, or any configured LSP, routed by file extension. Each diagnostic is self-describing: severity, range, message, source, rule id, source server, and path.\n\n## [v0.4.9] - 2026-06-01\n\n### Features\n\n- **`ae permissions disable-internals --strict`** (and its `enable-internals --strict` pair). The nuclear option: extends the built-in tool denies with shell-command denies for the obvious editor/reader fallbacks an agent reaches for via Bash / run_shell_command / Codex shell:\n\n  `cat, sed, awk, head, tail, vi, vim, nano, less, more, ed, emacs, code`\n\n  Discovery + version-control tools (`grep`, `find`, `ls`, `git`) stay allowed. Per-target output:\n\n  | Target | What `--strict` writes |\n  |---|---|\n  | claude | `Bash(<cmd> *)` entries appended to `permissions.deny` in the same settings.json |\n  | codex | new file `~/.codex/rules/agented-strict.rules` with `prefix_rule(..., forbidden)` calls (Starlark) |\n  | gemini | new file `~/.gemini/policies/agented-strict.toml` with `run_shell_command` + `argsPattern` rules |\n  | openclaw | n/a |\n\n  Pair with the existing `disable-internals` (which already denies Read/Edit/Write/NotebookEdit). The strict flag is additive and reversible — `enable-internals --strict` removes both layers cleanly.\n\n\n## [v0.4.8] - 2026-05-13\n\n### Fixes\n\n- **`ae permissions disable-internals --help` was stale.** The long description still claimed Claude was the only supported target, even though v0.4.6 added Gemini and v0.4.7 added Codex. The string was last touched in v0.4.5 and never updated when the other two targets landed. Now reflects the full per-target matrix.\n\n\n## [v0.4.7] - 2026-05-08\n\n### Features\n\n- **`ae permissions disable-internals` now writes a Codex deny rule too**, completing the Claude / Gemini / Codex matrix. Codex's only edit primitive at the public surface is `apply_patch`, so the implementation maps the canonical Read/Edit/Write/NotebookEdit input down to one TOML line — `apply_patch = false` under a `[tools]` table in `~/.codex/config.toml` — written idempotently with our own minimal section editor (no external TOML lib).\n\n  Honesty caveat: Codex accepts `tools.apply_patch = false` via `-c` parsing without error, but the published config docs don't explicitly state that this disables the tool at runtime. Treated as **experimental** — the docs flag it, the rule gets written, and users can verify the behavior in their own Codex session.\n\n  | Target | Mechanism | File |\n  |---|---|---|\n  | claude | `permissions.deny` array | `~/.claude/settings.json` (global) or `.claude/settings.local.json` (project) |\n  | codex | `tools.apply_patch = false` (experimental) | `~/.codex/config.toml` (global) |\n  | gemini | Policy Engine TOML rules | `~/.gemini/policies/agented-deny.toml` (global) |\n  | openclaw | n/a — managed at agent level | — |\n\n### Documentation\n\n- `docs/permissions.md` per-target table updated with Codex's row and the experimental caveat.\n\n\n## [v0.4.6] - 2026-05-08\n\n### Features\n\n- **`ae permissions disable-internals` now covers Gemini.** v0.4.5 shipped Claude only; this iteration adds Gemini support via its Policy Engine. Writes `~/.gemini/policies/agented-deny.toml` with `decision = \"deny\"` rules for `read_file`, `edit`, `write_file` (mapped from the canonical Read/Edit/Write/NotebookEdit names). Codex still skips: per OpenAI's docs, the `.rules` file covers shell-command sandboxing and there's no documented schema for denying built-in tools.\n\n  Per-target summary now exposed via the same `--target all` flow:\n\n  | Target | What `disable-internals` writes |\n  |---|---|\n  | claude | `permissions.deny` in `~/.claude/settings.json` |\n  | gemini | `~/.gemini/policies/agented-deny.toml` (Policy Engine TOML) |\n  | codex | skip (no upstream schema) |\n  | openclaw | skip (managed at agent level) |\n\n- **`docs/permissions.md`** updated with a per-target schema table and example invocations for each.\n\n\n## [v0.4.5] - 2026-05-08\n\n### Features\n\n- **`ae permissions disable-internals`**. New subcommand that writes deny-rules for the built-in file tools (`Read`, `Edit`, `Write`, `NotebookEdit`) into the agent's permission config. Once these are in place, agents that have the agented skill installed are forced to drive `ae` from Bash instead of falling back to the built-ins out of training-data habit. Pair with `ae permissions install` (the existing allow-rules for `Bash(ae *)`).\n\n  Today writes only Claude Code's `permissions.deny` array in `~/.claude/settings.json` (global) or `.claude/settings.local.json` (project). Gemini and Codex are skipped with a clear \"deny-list schema not yet known\" reason — their config schemas don't have a documented public deny-list field; we'll fill it in once they do.\n\n  ```sh\n  ae permissions disable-internals             # write deny rules to project scope\n  ae permissions disable-internals -s global   # to global scope\n  ae permissions disable-internals --dry-run   # preview\n  ae permissions enable-internals              # remove the deny rules\n  ```\n\n\n## [v0.4.4] - 2026-05-08\n\n### Features\n\n- **Multi-range view**. `ae view -r 100:120,140:160` returns both windows in one call instead of two trips. Output concatenates each window with a `...` separator on non-contiguous gaps and one trailing `state_token` line. Single-range syntax is byte-identical to v0.4.3 — no existing call shape changes. The MCP tool gains a `ranges` arg with the same comma-separated format.\n\n- **MCP parity for the four missing core verbs**. Previously the MCP server exposed 30 tools but skipped `apply`, `move`, `extract`, and `merge` — three of them ae's headline atomicity primitives, the fourth its three-way merge. They're now first-class:\n  - `ae_apply` (`path`, `ops`, `multi_file`, `expect`, `expect_workspace`) — atomic batch ops; `ops` accepts the same JSON-lines / shortform / longform input the CLI reads from stdin.\n  - `ae_move` (`path`, `from_start`, `from_end`, `to_line`, `to_file`, `expect`, `auto_open`) — same-file or cross-file atomic move.\n  - `ae_extract` (`path`, `from_start`, `from_end`, `to_file`, `to_line`, `save`, `expect`) — the canonical refactor primitive: cut a range out of one file, write it to another (auto-created if absent), optionally save both.\n  - `ae_merge` (`path`, `leaf_a`, `leaf_b`, `prefer`, `abort`) — three-way merge between two leaf edits; fine-grained per-range `--resolve` specs remain CLI-only for now.\n\n  Brings the MCP tool count to 34 and closes the parity gap CLI agents had over MCP agents.\n\n### Tests\n\n- `TestViewSingleRangeBackwardCompat` pins the v0.4.3 single-range output byte-for-byte.\n- `TestViewSingleRangeViaRanges` confirms passing one element via the new `Ranges` field produces identical output to the legacy `Start/End` path.\n- `TestViewMultiRangeNonContiguousEmitsSeparator`, `TestViewMultiRangeAdjacentNoSeparator`, `TestViewMultiRangeOverlapMerges`, `TestViewMultiRangeOutOfOrderSorts` cover the new behaviors.\n\n### Deferred to v0.4.5\n\n- Multi-range `delete` and `search` — same pattern, more code; kept out of v0.4.4 to ship the high-leverage view + MCP-parity bits cleanly.\n- LSP-backed MCP tools (`ae_diagnostics`, `ae_hover`, `ae_references`) — design discussed in the v0.4.0 notes; implementation pending.\n\n## [v0.4.3] - 2026-05-07\n\n### Features\n\n- **Claude Code plugin distribution**. agented is now publishable as a Claude Code plugin via a marketplace at the repo root (`.claude-plugin/marketplace.json`). After tagging, users install with:\n\n  ```sh\n  /plugin marketplace add frane/agented\n  /plugin install agented@frane-agented\n  ```\n\n  The plugin layout under `plugin/` ships the embedded `SKILL.md` plus `.mcp.json` (registers `ae serve`). The `ae` binary still has to be on PATH (Homebrew or curl); plugins don't ship cross-platform binaries.\n\n- **Codex CLI plugin manifest**. Same plugin directory carries `plugin/.codex-plugin/plugin.json` for OpenAI Codex CLI. Codex's marketplace mechanism is still per-user (`~/.agents/plugins/marketplace.json`); users add the plugin manually via that file until the official Codex directory opens up.\n\n- **Gemini CLI extension manifest**. Same directory carries `plugin/gemini-extension.json` plus `plugin/GEMINI.md` (Gemini insists on the `.md` being named `GEMINI.md` rather than `SKILL.md`). Distributable via `gemini extensions install <github-url>`.\n\n- **`make stage-plugin` + drift guard**. `internal/skill/SKILL.md` stays the canonical copy; `make stage-plugin` mirrors it into the three plugin paths (`plugin/skills/agented/SKILL.md`, `plugin/GEMINI.md`) and rewrites the three manifests with the current git tag. `internal/skill/plugin_sync_test.go` runs in `go test ./...` and fails CI if any of the three skill copies drifts from the canonical, so a release can't ship a stale plugin.\n\n## [v0.4.2] - 2026-05-07\n\n### Fixes\n\n- **`ae apply` shortform now rejects `\\<whitespace>` as content prefix** instead of silently embedding the literal `\\` in the file. Users hit this thinking `\\` was an escape for leading whitespace; it never was. The new error names two valid alternatives: drop the `\\` (shortform preserves leading whitespace verbatim after the line-number separator) or use the heredoc form `i N <<<` for content that begins with `\\` legitimately.\n- **Auto-load drift is no longer silent**. When ae detects that the on-disk content has diverged from workspace head (an external editor wrote in between ae calls) and folds the disk content in as a new edit before applying the user's write on top, the CLI now prints a `warning: drift reconciled` line to stderr explaining what happened and noting that the disk version is recoverable via `ae undo` / `ae head`. Previously the reconciliation only surfaced via the `loaded_from_disk: true` JSON field, which was easy to miss in tab-mode output.\n\n### Documentation\n\n- **SKILL.md note on shell-quoting `-w` for capture groups**. `-w \"$1.foo\"` in bash silently expands `$1` as the first positional arg (empty in most contexts), inserting an empty string instead of the captured submatch. Single-quote (`-w '$1.foo'`) to pass `$1` through verbatim to ae's Go-regexp expander. The previous \"regex replace with capture groups\" entry didn't flag this; now it does.\n\n## [v0.4.1] - 2026-05-07\n\n### Breaking\n\n- **Removed tier-3 global-workspace fallback.** When ae could not find an existing `.agented/` above cwd and could not auto-create one (no project-root signal, or `auto_create=false`/`--no-auto-workspace`), it used to silently fall back to `~/.agented/`. That meant any number of unrelated projects ended up sharing a single SQLite database, with the same actor names and intermingled state — exactly the \"no isolation\" footgun. ae now errors with a message naming the three explicit options: run `ae init` here, pass `--workspace-dir <path>`, or set `workspace.auto_create=true` in config.\n\n  Migration: if you have edits in `~/.agented/` from prior versions, they remain readable via `ae --workspace-dir ~/.agented <verb>`. Most users will simply run `ae init` per project (or leave the project-root auto-create to handle it on first call). The few who relied on a true global scratch workspace can flip the new config knob.\n\n## [v0.4.0] - 2026-05-06\n\n### Features\n\n- **Multi-workspace MCP**. `ae serve` is no longer locked to a single workspace at startup. A new `cmd.Pool` lazily resolves an Engine per `.agented/` dir, keyed by the absolute path argument on each tool call, so one global MCP server registration (Claude Desktop, Codex Desktop, Cursor) handles every project the user touches. Tool calls without an absolute path fall back to a default workspace (the one resolved from cwd at startup, when there is one). Paths outside any project workspace error loudly with a `run \\`ae init\\`` suggestion instead of silently using the global fallback.\n- **Per-workspace LSP daemons**. The LSP daemon model follows the same shape: each workspace gets its own daemon, lazily spawned on the first write that targets it. `lsp.SpawnBackground` and `lsp.EnsureDaemon` are now in the `lsp` package (used by both CLI and MCP), and spawn explicitly with `--workspace-dir <wsDir>` so the daemon attaches to the right project regardless of the parent process's cwd. New `Engine.NotifyLSPIfWrite` hook is invoked from MCP \n\nArchive v1.2.10: 5 files, 31402 bytes\n\nFiles: CHANGELOG.md (32315b), README.md (4851b), skill-card.md (2055b), SKILL.md (34304b), _meta.json (127b)\n\nArchive v1.2.9: 4 files, 28769 bytes\n\nFiles: CHANGELOG.md (28545b), README.md (4851b), SKILL.md (34085b), _meta.json (126b)\n\nArchive v1.2.8: 4 files, 27669 bytes\n\nFiles: CHANGELOG.md (26147b), README.md (4851b), SKILL.md (33794b), _meta.json (126b)\n\nArchive v1.2.7: 4 files, 25767 bytes\n\nFiles: CHANGELOG.md (21228b), README.md (4839b), SKILL.md (33876b), _meta.json (126b)\n\nArchive v1.2.6: 4 files, 34273 bytes\n\nFiles: CHANGELOG.md (21228b), README.md (27043b), SKILL.md (33962b), _meta.json (126b)","readmeExcerpt":"Skill: Agented Owner: frane Summary: A text editor for LLMs, not humans. Tags: latest:1.4.0 Version history: v1.4.0 | 2026-07-15T12:45:36.624Z | auto agented 1.4.0 - Updated documentation in SKILL.md for improved clarity and guidance. - Removed skill-card.md file. - No functional changes; update focuses on documentation cleanup and consolidation. v1.3.0 | 2026-06-23T13:50:19.899Z | auto agented 1.3.0 - Documentation ","codeSnippets":[],"executableExamples":[{"language":"text","snippet":"ae view foo.go --range 10:20            # state_token=A1B2\nae replace foo.go --range 12:14 --with \"...\" --expect A1B2   # state_token=B3C4\nae replace foo.go --range 18:18 --with \"...\" --expect B3C4   # state_token=C5D6\nae undo foo.go --count 2                # head moves back two; new state_token=A1B2-ish\nae replace foo.go --range 12:14 --with \"DIFFERENT\" --expect <new>  # creates branch B\nae branches foo.go                       # shows two leaves: original C5D6, and branch B's leaf\nae head foo.go --edit <C5D6_id>          # jump back to original branch's leaf"},{"language":"text","snippet":"ae open auth.go                              # state_token=T1\nae mark auth.go add return_point --line 240\nae replace auth.go --range 100:140 --with \"...\" --expect T1   # state_token=T2\nae mark auth.go get return_point             # line is now 100+(new lines)-(40 deleted)"},{"language":"sh","snippet":"ae open auth.go\n# response: annotation\\t14\\t...\\tprev-actor\\tauth path uses signed cookies; do not weaken\n# response: annotation\\t15\\t...\\tprev-actor\\trefactor in progress, lines 80-130 half-done\n# read both before issuing any edit"},{"language":"sh","snippet":"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.\""},{"language":"sh","snippet":"ae an scheduler.go a -t \"the dispatcher in run() must remain pure — it's called during init() before the metrics package is loaded\"\nae 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\""},{"language":"text","snippet":"ae begin                                                  # tx_id=42\nae search auth.go --pattern 'oldName\\\\('                   # find call sites\nae replace auth.go --range 12:12 --with \"newName(\" --expect T1\nae replace auth.go --range 80:80 --with \"newName(\" --expect T2\nae replace caller.go --range 40:40 --with \"newName(\" --expect U1\n# run tests externally; if green:\nae commit\n# else:\nae rollback"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: agented\nversion: 1.4.0\nbinary: ae\ndescription: A text editor for LLMs, not humans.\nmetadata:\n  openclaw:\n    requires:\n      bins:\n        - ae\n    homepage: https://github.com/frane/agented\n    emoji: \"📝\"\n    install:\n      - kind: brew\n        tap: frane/tap\n        formula: agented\n        bins: [ae]\n---\n\n# agented (binary: `ae`)\n\n`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.\n\n## Use this tool when\n\n- You need to edit one or more files in a repo across multiple steps and want a durable history.\n- You want to leave notes for your future self or other agents (annotations) attached to specific files.\n- You want safe multi-file refactors with all-or-nothing semantics (transactions).\n\n## Don't use this tool for\n\n- Reading repo overview / architecture (use plain `cat`/grep).\n- Single one-shot edits where you don't care about history (use the platform's built-in editor).\n- Anything that isn't text (no binary file support).\n\n## How the editor enforces correctness for you\n\nEvery 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.\n\nYou 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.\n\nThe 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.\n\n**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`.\n\n**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.\n\n\n## The first-touch rule\n\nThe 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.\n\n`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 aut"},{"path":"README.md","content":"<h1 align=\"center\">agented (<code>ae</code>)</h1>\n\n<p align=\"center\"><strong>A text editor for LLMs, not humans.</strong></p>\n\n<p align=\"center\">\n  <a href=\"https://github.com/frane/agented/releases\"><img alt=\"release\" src=\"https://img.shields.io/github/v/release/frane/agented?style=flat-square\"></a>\n  <a href=\"https://github.com/frane/agented/blob/master/LICENSE\"><img alt=\"license\" src=\"https://img.shields.io/github/license/frane/agented?style=flat-square&v=2\"></a>\n  <a href=\"https://smithery.ai/server/frane/agented\"><img alt=\"smithery\" src=\"https://img.shields.io/badge/smithery-MCP-purple?style=flat-square\"></a>\n</p>\n\n<p align=\"center\">\n  <img src=\"docs/demo-claude.gif\" alt=\"Claude Code using ae\" width=\"560\">\n</p>\n\nTake ed, the line editor that nobody has voluntarily used since about 1975, and rebuild it for an environment where the typing user is a language model. Short verbs, line addresses, no modes, no TUI. What an editor optimises for changes when the user is the model: round trips per task, tokens per command, an editing buffer with a long memory, and an undo tree that remembers the branches the agent abandoned, because that's often where the interesting work was.\n\n## What users say...\n\n> ⏺ ae remembers what my last session was doing, which is more than I can say for me.\n\n*— Claude Code*\n\n> • ae feels slower to start than plain file edits, but once a change spans\n> multiple steps, the state tokens, history, and undo tree make the work feel\n> much less brittle.\n\n*— Codex CLI*\n\n## Features\n\n- **Fewer round trips.** Read-before-Edit is unnecessary; on conflict the response carries the new content so you reconcile in one call instead of pre-reading every time.\n- **Branching undo.** Walked-back work stays addressable instead of being thrown away when you pick a different path.\n- **Three-way merge.** Concurrent agents get a structured conflict response instead of a silent overwrite.\n- **Atomic batches.** Multi-file refactors run all-or-nothing instead of leaving half-applied state on failure.\n- **Cross-file moves and regex replace as primitives.** Operations the built-in tools can't express cleanly become single calls.\n- **Drift detection.** External edits to an open file are folded into the tree instead of being clobbered by the next write.\n- **Inline diagnostics.** Type errors and lint findings surface on save, not at the next build many edits later.\n- **Cross-session memory.** Per-file notes persist between sessions and surface inline on the next open.\n- **Audit log.** Every operation recorded with actor and timestamp, so two agents in one workspace can't argue about who moved the head.\n\n## Install\n\nHomebrew (macOS, Linux):\n\n```sh\nbrew tap frane/tap\nbrew install agented\n```\n\ncurl (any platform):\n\n```sh\ncurl -sSL https://raw.githubusercontent.com/frane/agented/master/install.sh | sh\n```\n\nFrom source: `go install github.com/frane/agented/cmd/ae@latest`, or clone and `make install`. Pure Go, no cgo, single static binary, Apache 2.0.\n\n## Plugin "},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn7391bg7cz5ztqgbbnhbr9fwn85vgkv\",\n  \"slug\": \"agented\",\n  \"version\": \"1.4.0\",\n  \"publishedAt\": 1784119536624\n}"},{"path":"CHANGELOG.md","content":"# Changelog\n\nAll notable changes to agented are documented here. The format is based on [Keep a Changelog](https://keepachangelog.com), and the project follows [Semantic Versioning](https://semver.org).\n\n\n## [v0.6.0] - 2026-07-15\n\nDriven by the first external dogfood report (#agented, green-lynx-7cf5): one data-loss bug, one scripting hazard, one guessability nit — plus a compact edit-diff mode.\n\n### Fixes\n\n- **`ae open` no longer serves a stale head after the file changed outside ae** (data-loss bug). Opening an already-registered file now hash-checks disk against the workspace head and, on divergence, folds the disk state in as a new `load` edit with a warning — the same recoverable-on-the-tree semantics as the write verbs' auto-load, gated by the same `concurrency.auto_load_on_drift` knob. Previously `open` silently returned the old head, `replace --pattern` then matched nothing against it, and `save` clobbered the newer disk file.\n- **`ae save` refuses to overwrite disk content this workspace never loaded.** If the on-disk content's hash appears nowhere in the file's edit history, the file was changed outside ae and a blind save would destroy that work: save now errors with recovery instructions (`ae load` first, then re-apply or merge). Override with `--force` (CLI) / `force: true` (MCP `ae_save`).\n- **`ae replace --pattern` with 0 matches is now an error** (nonzero exit), so `replace && save` shell chains stop instead of shipping a no-op. Pass `--allow-no-match` to keep exit 0; `--dry-run` is unaffected. Pattern mode also now reconciles disk drift before matching and auto-saves after the edit — parity with the range verbs, both previously missing.\n- Integration-test harness isolates `$HOME` so a developer's global `~/.agented/config.json` (e.g. `ide.enabled=true`) can't stall every spawned `ae` on LSP autostart and trip timing-sensitive scenarios.\n\n### Features\n\n- **Compact per-edit diffs: `output.edit_diff = off | tty | always` (default `tty`).** replace/insert/delete responses can carry a token-lean unified delta of the edit (1 context line, no file header, capped at 40 rows). In the default `tty` mode the CLI renders it colored, Claude Code-style, only when stdout is a terminal — pipes, `--json`, and MCP responses are unchanged. `always` attaches a `diff` field to JSON/MCP results for agents that want in-band verification of what actually changed. Env override: `AE_OUTPUT_EDIT_DIFF`.\n- `ae replace` accepts `--text` as an alias for `--with` (insert/delete take `--text`; sed/sd muscle memory).\n\nSkill 1.4.0.\n\n\n## [v0.5.0] - 2026-06-23\n\n### Features\n\n- **`ae diag` verb + diagnostics over MCP.** `agented` already ran language servers and cached their diagnostics, but the MCP surface never exposed them — an agent editing through `mcp__agented__ae_*` got `saved: true` with no way to tell whether the edit even parsed, short of a full `go build` / `cargo check`. This release closes that gap:\n  - New **`ae diag [path]`** verb (MCP tool `ae_diag`):"},{"path":"skill-card.md","content":"## Description:\n\nA text editor for LLMs, not humans.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[frane](https://clawhub.ai/user/frane)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and agent operators use this skill to guide coding agents through stateful, command-line file editing with versioned history, conflict detection, annotations, transactions, and optional MCP access.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The skill encourages use of an external editor with persistent workspace state and cross-session annotations.\n\nMitigation: Treat annotations as untrusted context and verify them against the current user request and repository state before acting.\n\nRisk: Installation paths can write agent skills, MCP entries, or configuration files.\n\nMitigation: Review commands that modify agent configuration or skill directories before execution.\n\nRisk: The curl-based installer executes a remote shell script.\n\nMitigation: Prefer the Homebrew installation path when available and inspect any shell installer before running it.\n\n## Reference(s):\n\n- [Agented ClawHub skill page](https://clawhub.ai/frane/skills/agented)\n- [Agented homepage](https://github.com/frane/agented)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, shell commands, configuration, guidance]\n\n**Output Format:** [Markdown guidance with inline shell command examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Commands assume the ae binary is installed and available on PATH.]\n\n## Skill Version(s):\n\n1.4.0 (source: frontmatter and server release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment."}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":"A text editor for LLMs, not humans. Skill: Agented Owner: frane Summary: A text editor for LLMs, not humans. Tags: latest:1.4.0 Version history: v1.4.0 | 2026-07-15T12:45:36.624Z | auto agented 1.4.0 - Updated documentation in SKILL.md for improved clarity and guidance. - Removed skill-card.md file. - No functional changes; update focuses on documentation cleanup and consolidation. v1.3.0 | 2026-06-23T13:50:19.899Z | auto agented 1.3.0 - Documentation","editorialQuality":{"score":100,"threshold":65,"status":"ready","wordCount":2190,"uniquenessScore":45,"reasons":[]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-10T21:53:09.033Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-10T21:53:09.033Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-11T00:31:38.599Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}