acuvo-code 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/CHANGELOG.md +328 -0
  2. package/ENTERPRISE.md +927 -0
  3. package/LICENSE +120 -0
  4. package/README.md +1245 -0
  5. package/ROADMAP.md +556 -0
  6. package/bin/acuvo-mcp.mjs +208 -0
  7. package/bin/acuvo.mjs +3108 -0
  8. package/lib/acceptance-consent.mjs +168 -0
  9. package/lib/acceptance.mjs +859 -0
  10. package/lib/account.mjs +226 -0
  11. package/lib/acuvo-dir.mjs +72 -0
  12. package/lib/acuvo-models.mjs +141 -0
  13. package/lib/apply-patch.mjs +570 -0
  14. package/lib/ask-user.mjs +173 -0
  15. package/lib/audit.mjs +530 -0
  16. package/lib/auto-lease.mjs +174 -0
  17. package/lib/background.mjs +842 -0
  18. package/lib/best-of.mjs +334 -0
  19. package/lib/board.mjs +232 -0
  20. package/lib/breaker.mjs +93 -0
  21. package/lib/budget.mjs +1375 -0
  22. package/lib/builtin-skills.mjs +135 -0
  23. package/lib/cache-floor.mjs +204 -0
  24. package/lib/chain.mjs +303 -0
  25. package/lib/changed-paths.mjs +84 -0
  26. package/lib/chat.mjs +434 -0
  27. package/lib/checkpoint.mjs +637 -0
  28. package/lib/child-lifetime.mjs +71 -0
  29. package/lib/cli-args.mjs +1255 -0
  30. package/lib/code-review.mjs +1382 -0
  31. package/lib/colour.mjs +82 -0
  32. package/lib/command.mjs +2847 -0
  33. package/lib/compact.mjs +1151 -0
  34. package/lib/completion.mjs +515 -0
  35. package/lib/creative-engines.mjs +779 -0
  36. package/lib/db-inspect.mjs +1624 -0
  37. package/lib/delete.mjs +111 -0
  38. package/lib/design-loop.mjs +570 -0
  39. package/lib/diff-preview.mjs +1044 -0
  40. package/lib/doctor.mjs +2139 -0
  41. package/lib/dropped.mjs +216 -0
  42. package/lib/edit-diagnostics.mjs +277 -0
  43. package/lib/edit.mjs +460 -0
  44. package/lib/env-file.mjs +250 -0
  45. package/lib/escalate.mjs +702 -0
  46. package/lib/evaluate.mjs +284 -0
  47. package/lib/fetch-text.mjs +952 -0
  48. package/lib/fleet-budget.mjs +256 -0
  49. package/lib/gh.mjs +1536 -0
  50. package/lib/git.mjs +1341 -0
  51. package/lib/github.mjs +261 -0
  52. package/lib/h2.mjs +194 -0
  53. package/lib/handoff.mjs +417 -0
  54. package/lib/hooks.mjs +626 -0
  55. package/lib/http-probe.mjs +907 -0
  56. package/lib/image-director.mjs +322 -0
  57. package/lib/image-edit.mjs +522 -0
  58. package/lib/imagegen.mjs +998 -0
  59. package/lib/interrupt.mjs +234 -0
  60. package/lib/learned.mjs +353 -0
  61. package/lib/lease-watch.mjs +115 -0
  62. package/lib/lease.mjs +868 -0
  63. package/lib/localize.mjs +834 -0
  64. package/lib/log-tail.mjs +1052 -0
  65. package/lib/login.mjs +157 -0
  66. package/lib/lsp.mjs +1613 -0
  67. package/lib/mcp-consent.mjs +377 -0
  68. package/lib/mcp-defaults.mjs +780 -0
  69. package/lib/mcp-server.mjs +1343 -0
  70. package/lib/mcp.mjs +1263 -0
  71. package/lib/media.mjs +1283 -0
  72. package/lib/memory-workspace.mjs +179 -0
  73. package/lib/model-json.mjs +132 -0
  74. package/lib/model-tier.mjs +171 -0
  75. package/lib/model.mjs +1445 -0
  76. package/lib/parallel.mjs +144 -0
  77. package/lib/perchance.mjs +210 -0
  78. package/lib/plan-coherence.mjs +1461 -0
  79. package/lib/plan-ledger.mjs +981 -0
  80. package/lib/plan.mjs +461 -0
  81. package/lib/policy.mjs +783 -0
  82. package/lib/prefix-order.mjs +38 -0
  83. package/lib/project-memory.mjs +127 -0
  84. package/lib/prompt.mjs +109 -0
  85. package/lib/python.mjs +862 -0
  86. package/lib/rcfile.mjs +853 -0
  87. package/lib/read-window.mjs +743 -0
  88. package/lib/refute-tools.mjs +34 -0
  89. package/lib/refute.mjs +806 -0
  90. package/lib/repl-driver.mjs +264 -0
  91. package/lib/repl.mjs +324 -0
  92. package/lib/replay.mjs +1218 -0
  93. package/lib/repo-map.mjs +1101 -0
  94. package/lib/report.mjs +419 -0
  95. package/lib/search-rank.mjs +99 -0
  96. package/lib/search.mjs +659 -0
  97. package/lib/secret-paths.mjs +54 -0
  98. package/lib/session.mjs +1017 -0
  99. package/lib/skills.mjs +703 -0
  100. package/lib/slash.mjs +356 -0
  101. package/lib/spawn-argv.mjs +1151 -0
  102. package/lib/spend.mjs +250 -0
  103. package/lib/steer.mjs +280 -0
  104. package/lib/stream.mjs +253 -0
  105. package/lib/stuck.mjs +712 -0
  106. package/lib/subagent.mjs +749 -0
  107. package/lib/terminal-graphics.mjs +171 -0
  108. package/lib/tool-prefix.mjs +226 -0
  109. package/lib/tool-shortlist.mjs +162 -0
  110. package/lib/tools.mjs +2333 -0
  111. package/lib/tsserver.mjs +423 -0
  112. package/lib/turn.mjs +5672 -0
  113. package/lib/untrusted-block.mjs +271 -0
  114. package/lib/verify-claim.mjs +299 -0
  115. package/lib/vision.mjs +330 -0
  116. package/lib/voice-task.mjs +561 -0
  117. package/lib/warm-provider.mjs +255 -0
  118. package/lib/websearch.mjs +401 -0
  119. package/lib/workspace.mjs +928 -0
  120. package/lib/write-approval.mjs +235 -0
  121. package/lib/write-many.mjs +162 -0
  122. package/package.json +62 -0
  123. package/scripts/bundle.mjs +768 -0
  124. package/scripts/cache-floor.mjs +176 -0
  125. package/scripts/machine.mjs +226 -0
  126. package/scripts/test.mjs +139 -0
  127. package/skills/accessibility.md +87 -0
  128. package/skills/acuvo-design-system.md +123 -0
  129. package/skills/animation.md +84 -0
  130. package/skills/api-design.md +82 -0
  131. package/skills/auth-and-sessions.md +78 -0
  132. package/skills/build-with-a-framework.md +101 -0
  133. package/skills/colour-and-contrast.md +112 -0
  134. package/skills/creative-engines.md +81 -0
  135. package/skills/css-layout.md +85 -0
  136. package/skills/data-and-charts.md +77 -0
  137. package/skills/debugging.md +76 -0
  138. package/skills/designing-by-looking.md +84 -0
  139. package/skills/error-handling.md +78 -0
  140. package/skills/forms-and-validation.md +93 -0
  141. package/skills/nextjs-app-router.md +75 -0
  142. package/skills/page-composition.md +103 -0
  143. package/skills/performance.md +77 -0
  144. package/skills/plan-before-building.md +52 -0
  145. package/skills/planning-and-delegating.md +72 -0
  146. package/skills/refactoring.md +70 -0
  147. package/skills/security-basics.md +76 -0
  148. package/skills/state-management.md +73 -0
  149. package/skills/supabase-multitenant.md +72 -0
  150. package/skills/typescript-strict.md +90 -0
  151. package/skills/typography.md +135 -0
  152. package/skills/verify-your-own-work.md +62 -0
  153. package/skills/web-app-quality.md +62 -0
  154. package/skills/working-in-the-background.md +64 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,328 @@
1
+ # Changelog
2
+
3
+ All notable changes to Acuvo Code. Dates are AEST.
4
+
5
+ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
+ This project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ### Added
11
+ - **`acuvo rewind` — undo what the agent did to your files.** Every run now
12
+ copies the previous contents of each file it writes or deletes into
13
+ `.acuvo/checkpoints/` (content-addressed, so ten rounds rewriting one file
14
+ store one copy per distinct version) and prints one line when it finishes:
15
+ `· checkpoint 20260814-084541-84u8 — 3 files can be put back`. `acuvo rewind`
16
+ lists them; `acuvo rewind <id>` puts the tree back the way it was **before
17
+ that run started**, including **deleting the files the agent created**, which
18
+ `git checkout` cannot do because they are untracked. No model call, no cost,
19
+ no API key, and it works in a directory that is not a git repository at all.
20
+ Proven end to end on a real run: `math.mjs` rewritten, `README.md` created,
21
+ `stale.txt` deleted — one command restored all three.
22
+ ⚠️ **It refuses any file you changed yourself after the run.** Each entry
23
+ records the sha256 of what the agent left; if the file on disk is not that, it
24
+ is skipped with the reason, because a rewind that threw away your edit would
25
+ be the accident rather than the fix. `--force` overrides and prints every such
26
+ path as FORCED. A rewind that restored nothing because everything conflicted
27
+ exits **3**, never 0 — `acuvo rewind <id> && npm test` must not test the tree
28
+ it was asked to undo.
29
+ ⚠️ **The bug only a real command line could find (twice, for $0.006):**
30
+ written first with the `argv[0]` anchor `board` and `verify` use,
31
+ `acuvo --dir <ws> rewind` did not dispatch — the word fell through as a TASK
32
+ and a paid agent session read the workspace and did nothing. The verb is now
33
+ claimed as the first POSITIONAL. `board`, `verify`, `leases` and `spend` still
34
+ carry the original defect.
35
+ ⚠️ **And the second one:** `evaluate` writes its snippet through the executor
36
+ and deletes it with raw `unlinkSync`, so the first real run announced "3 files
37
+ can be put back" when the user had two. The tool's own scratch file is now
38
+ excluded by the pattern `evaluate.mjs` itself exports.
39
+ - **`--no-checkpoint`** turns the recording off; it is on by default, off
40
+ automatically under `--dry-run`, and creates nothing until a run mutates
41
+ something.
42
+ - **`delegate` — read-only subagents.** Hand a research question to a helper
43
+ with its own fresh context and get back a ~900-character summary instead of
44
+ everything it read. The helper is offered **twelve tools, every one a read**
45
+ (`SUBAGENT_TOOL_NAMES`); it cannot write, edit, commit or run anything, it is
46
+ capped at 6 rounds (4 by default), and **it cannot delegate again** —
47
+ `MAX_SUBAGENT_DEPTH` is 1, because two levels is how a five-round task becomes
48
+ a hundred model calls nobody authorised. It refuses rather than guessing when
49
+ no credentials reached the dispatcher.
50
+ ⚠️ **The bug that only a real run could find:** the dispatcher passed
51
+ `depth + 1`, so the top-level `delegate` refused *itself* with "a helper cannot
52
+ delegate again (depth 1)". All thirteen unit tests passed — every one called
53
+ `runSubagent` directly and none came through the dispatcher.
54
+ - **`remember` / `forget` — facts that outlive a run.** One markdown file per
55
+ fact in `.acuvo/memory/`, read back into the next session's prompt, so a run
56
+ does not rediscover your real test command. Markdown on purpose: **these files
57
+ are meant to be committed**, so they are diffable and reviewable in a PR — and
58
+ that is exactly why a fact that pattern-matches a credential is refused
59
+ outright rather than redacted. Bounded at 40 entries / 4,000 bytes / 400
60
+ characters a fact, oldest evicted; every fact must carry a `why`, because a
61
+ fact with no provenance cannot be judged later and a wrong memory is worse
62
+ than no memory.
63
+ - **`--budget <usd>` and `--until-done`.** `--budget` stops before the round
64
+ that would cross the figure you named (`0.50`, `25c`, `$2` all parse) and
65
+ **refuses to start when it cannot afford one round** (`reason: "too-small"`)
66
+ rather than spending money to discover it had none. `--until-done` keeps going
67
+ while your declared criterion is unmet, the budget allows, and the loop is not
68
+ going in circles — it **requires `--budget`, and there is no unbounded mode.**
69
+ - **`--doctor`.** Says what is actually working on this machine — key, model
70
+ chain, every media endpoint, which tools would be offered, which command
71
+ presets are enabled, git — with no API key, no network and no completion
72
+ spent. Every dark or broken line names the exact variable that fixes it. Exit
73
+ 0 only when nothing is broken, so it works in CI; `--doctor --json` for the
74
+ machine form.
75
+ ⭐ **`MODAL_VIDEO_SECRET` is why this exists and it had been documented
76
+ nowhere.** It is not a URL, so it never appears in an error about a missing
77
+ endpoint: a correctly-set URL *without* it answers HTTP 200 with
78
+ `{ok:false,error:"unauthorised"}` and reads like a broken service. Its absence
79
+ made four working tools look broken. The doctor now separates **live**
80
+ (reachable *and* authorised) from **dark** (unset) from **broken** (set, not
81
+ answering).
82
+ - **`--replay <id>`.** Step through a saved run — every round, call, result and
83
+ refusal. Runs nothing, writes nothing. `--only refusals|writes|runs|effects|
84
+ reasoning` narrows it; `--replay <a> --diff <b>` compares two runs of the same
85
+ task and names where they split.
86
+ - **`--design <file.html>`.** The design loop without the agent: render the
87
+ page, look at it, print a verdict — and the actual pixels if your terminal
88
+ speaks kitty or iTerm2.
89
+ - **Voice, file in and file out.** `--task-audio <file>` transcribes an audio
90
+ file and runs what it says — **showing the transcript first and waiting**, so
91
+ you can press Enter to cancel, `y` to run, or type a correction to fix a
92
+ mis-heard word without retyping the task. `--say` speaks the closing verdict
93
+ into a `.wav`. `--yes` is required to skip the confirmation, and required in a
94
+ pipe, cron or CI where there is nobody to ask.
95
+ - **Command presets — other languages, off by default.** `python`, `go`, `rust`,
96
+ `ruby`, `make` and `node-bin` add vetted build/test drivers (`pytest`, `go`,
97
+ `cargo`, `rspec`, `make`, `eslint`/`prettier`/`jest`) on top of the fixed four.
98
+ ⚠️ **`.acuvo/commands.json` may name presets and nothing else**, because that
99
+ file lives in the workspace and the agent can write to the workspace — a grant
100
+ of an arbitrary binary there would be the agent granting itself a program. An
101
+ arbitrary binary can only be named in `ACUVO_ALLOW_COMMANDS`, in the parent
102
+ environment, which the agent has no verb that reaches. Shells are refused at
103
+ every layer including that one. A refusal now names the preset that would have
104
+ allowed the command, instead of only saying no.
105
+ - **`run_program` — run a program with a REAL argument array.** One of the
106
+ unreachable tools the wiring pass left out as
107
+ "a product decision". `run_command` takes a *string* and cannot tell
108
+ `node app.js add "buy milk"` from a model composing a second command, so it
109
+ refuses the quote — which meant the agent could not execute the flags and
110
+ arguments it had **itself just written**. Measured in three probe runs; two of
111
+ them responded by documenting output that had never been produced. Each item
112
+ of `args` is now exactly one argv slot, `shell: false`, nothing re-parses it.
113
+ Same four binaries, same `validateCommand` (imported, never re-declared), same
114
+ bounded spawn and scrubbed environment; withheld by `--no-run` and `--dry-run`
115
+ at the offer *and* at the dispatcher. **Proven live on its first real
116
+ opportunity:** the model chose it unprompted in round 2 of a fresh task and
117
+ the run came back `✔ VERIFIED — node --test test/fizzbuzz.test.mjs exited 0`.
118
+ - **Fourteen tools the model could not previously reach.** `read_lines`,
119
+ `read_around`, `fetch_url`, `plan_start`/`plan_step`/`plan_status`,
120
+ `declare_acceptance`/`check_acceptance`, `list_sessions`, `read_skill`, and
121
+ `find_definition`/`find_references`/`check_types`/`list_symbols`. Seven
122
+ finished, tested modules were imported by nothing on the runtime path; they
123
+ are now declared, dispatched, offered per round budget, and rendered to the
124
+ model in a form it can act on. All multi-round only — in a single-shot turn
125
+ every one of them is a dead button, and the offer still returns exactly
126
+ `write_file` and `generate_image` for `--max-rounds 1`.
127
+ - **Skills.** `.acuvo/skills/*.md` — your team's procedures, no pull request
128
+ needed. The catalogue (name, description, when) goes in the system prompt; the
129
+ body loads only when the model calls `read_skill`. Measured 2026-08-11 with a
130
+ three-rule skill and a task that never mentioned skills: `read_skill` was the
131
+ model's **first** tool call and all three rules were obeyed.
132
+ - **The plan countdown.** With a plan recorded, every round now carries
133
+ `plan: 1/3 done · 2 remaining: … · round 4 of 5`, in the conversation and in
134
+ the tool results. No plan file means byte-identical behaviour to before.
135
+ - **Acceptance.** A **declared** criterion decides the exit code; a **derived**
136
+ one (read out of your own task text) reports and never gates. Both are now in
137
+ `--json` as `.acceptance` with `source`, `gating`, `verdict` and `unmet` —
138
+ previously a run could emit `verification.passed: true` while the command the
139
+ user actually named had failed, with nothing in the document to say so.
140
+
141
+ ### Changed
142
+ - **The round ceiling is 16, raised from 8** (`MAX_ROUNDS_LIMIT`,
143
+ `lib/cli-args.mjs`). The default is unchanged at 5. What made it safe
144
+ was compaction — the constraint was never the round count, it was a
145
+ transcript that grew without bound, and a 15-round session could not have
146
+ existed under the old ceiling. `--budget` is the bound that actually matters
147
+ to whoever pays: dollars, not a counter.
148
+ - **The repository is public.** `github.com/xxiautomate-star/acuvo-code` is open
149
+ and clonable — verified by cloning it into an empty directory and running both
150
+ the CLI and the suite out of the result; the clone carries `bin/`, `lib/`,
151
+ `test/` and `bench/`, and there is no `node_modules` to fetch. The README and
152
+ ENTERPRISE.md both said the URL 404s, which was true when written.
153
+ ⚠️ **What is published is one squashed 0.2.0 commit and is well behind**: cloned
154
+ and run, it is **1,192 tests with 4 failing** (`bundle.test.mjs` and
155
+ `docs-truth.test.mjs` fail at file level, plus a `STUCK_PATTERNS` assertion)
156
+ against 1,378 and 0 here. Documented rather than left for a stranger to find.
157
+ **Neither `acuvo-code` nor `acuvo` is on npm**, so there is still no
158
+ `npm install -g`.
159
+
160
+ ### Fixed
161
+ - **⚠️ Documentation that had gone false, struck rather than quietly edited.**
162
+ The README advertised a single-file bundle — `npm run bundle`, and a
163
+ measurement, *"Verified 2026-08-11: 1,228,642 bytes, 42 modules · 13 node
164
+ builtins · 1 inlined asset"*. **No such run ever happened.** `package.json`
165
+ declares the script and `scripts/bundle.mjs` was never written, so the command
166
+ exits 1 with `Cannot find module`, and all 44 tests in `test/bundle.test.mjs`
167
+ skip themselves. A byte count nobody measured is the worst kind of
168
+ documentation, because it reads as the most rigorous line on the page.
169
+ Also corrected: "455 tests" (there are **1,378** across 61 files — 1,333 pass,
170
+ 45 skipped, 0 fail), "fifteen more tools" (**36** are registered), the
171
+ `MAX_ROUNDS_LIMIT` = 8 stated three times in ENTERPRISE.md, and ENTERPRISE.md
172
+ §5.2's "we have neither sub-agents nor task delegation" — both halves false
173
+ since `delegate` shipped.
174
+ - **`.acuvo/` was documented as a directory to gitignore wholesale.** That would
175
+ throw away the three things in it meant to travel with the repo: `skills/`,
176
+ `memory/` and `commands.json`. The ignore rule in the README now keeps them.
177
+ - **⚠️ A SILENT GREEN: a failing test suite reported as exit 0 with no output.**
178
+ `node --test` sets `NODE_TEST_CONTEXT` in every child. A nested `node --test`
179
+ that inherited it believed it was a test *worker*, stopped printing TAP, and
180
+ wrote a serialised stream to a parent that was not listening — so
181
+ `run_command` returned **exit 0 and zero bytes** for a suite that failed.
182
+ Measured side by side against the identical failing file, before the fix:
183
+ `run_command → exit 0 · 0 bytes`, `run_program → exit 1 · 951 bytes`.
184
+ `spawn-argv.mjs` had found it and deleted the variable locally, deliberately
185
+ not touching the shared `scrubEnvironment` because a single-file lane must not
186
+ change another verb's behaviour. Correct then; wrong once both verbs shipped —
187
+ two spawners disagreeing about whether a suite passed is worse than either
188
+ being wrong, and the disagreement was invisible until both ran. Now deleted in
189
+ `scrubEnvironment`, so `run_command`, `run_program`, `evaluate` and `git` all
190
+ agree. For an ordinary CLI user the variable is never set and this is a no-op.
191
+ - **The summary contradicted itself two lines apart.** A run with a declared
192
+ criterion and no model tool calls printed
193
+ `⚠ NOTHING WAS RUN … no command was executed this session`, and then, two
194
+ lines below, `✖ UNMET — you asked that npm test pass; it ran and exited 1`.
195
+ The acceptance sweep spawns the criterion at the end of the session, which is
196
+ correctly kept out of `verification` — but the *sentence* claimed nothing had
197
+ executed. The verdict is unchanged (a sweep run must never be able to turn a
198
+ NOT-VERIFIED into a VERIFIED); only the sentence now tells the truth.
199
+ - **`run_program` counts as a run.** Third time for this defect: the list was
200
+ keyed on the *name of the tool* rather than on whether a process ran, so a
201
+ session that verified itself entirely through `run_program` would have been
202
+ summarised as unverified. Its `command` is synthesised from `argv`, and it is
203
+ excluded from the stale re-run — re-running a joined argv as a string would
204
+ hand it straight back to the parser the tool exists to avoid.
205
+ - **A name collision between two different `run_program`s.** `acceptance.mjs`
206
+ already listed `run_program` in its satisfying tools — meaning the *browser*
207
+ client's verb, which returns `{command}`. The CLI's returns `{argv}` and no
208
+ `command`, so the judge silently skipped our records and the sweep re-ran a
209
+ criterion the session had already satisfied. `runSession` now translates,
210
+ rather than teaching the judge a second shape it cannot see the other half of.
211
+ - **`check_acceptance` now counts as a run.** A criterion the model checked and
212
+ watched exit 0 was reported as "nothing in this run satisfied it", re-run a
213
+ second time by the end-of-run sweep, and then summarised as
214
+ "⚠ NOTHING WAS RUN, so nothing here is verified". Every word of that was
215
+ wrong: the bookkeeping was keyed on the *name of the tool* rather than on
216
+ whether a process ran. Same defect `evaluate` had, one tool later.
217
+ - **The audit log's `model.answered` is no longer `null` on every run.** The
218
+ session reported only the model that was *requested*, while `chain.mjs` fails
219
+ over across up to four candidates — so a buyer asking "which model saw our
220
+ source code" got no answer. Each round now records the model that replied, and
221
+ the session carries the last one. Still `null` rather than guessed when
222
+ nothing reported one.
223
+ - **Language-server tools are no longer offered where they cannot work.** The
224
+ gate asked only "is any server installed". Measured on the integrating
225
+ machine: a zero-dependency JavaScript package offered all four LSP tools
226
+ because `rust-analyzer` sat on `PATH` from unrelated work, and every call
227
+ could only answer "typescript-language-server is not installed". The gate is
228
+ now the intersection of *installed* and *a language this project contains*.
229
+ - **Read results no longer arrive as escaped JSON.** `read_lines`,
230
+ `read_around`, the four LSP verbs, `read_skill`, `fetch_url` and the plan
231
+ tools were rendering through the generic `JSON.stringify` fallback — every
232
+ newline a literal `\n`, cut to 2,000 characters against `read_file`'s 8,000.
233
+ A model cannot copy an `edit_file` old\_string out of that. For `read_skill`
234
+ it was also a security regression: the wrapper stating that a skill grants no
235
+ tool and lifts no restriction was being stripped off.
236
+ - **The offer probes the workspace, not the shell's current directory.** A
237
+ `--dir` run decided whether to show `read_skill` and the LSP tools by looking
238
+ at wherever the operator happened to be standing.
239
+ - **`--no-run` is enforced at the dispatcher.** The flag withheld
240
+ `check_acceptance` from the schemas, but a model can still emit a call for a
241
+ tool it was never shown — a resumed session or a provider echoing an old tool
242
+ list will do it. It is now refused where the command would be spawned.
243
+ - **ENTERPRISE.md §3.2 said the prompt ships `.env`. It does not, and had not
244
+ for some time.** The audit read `CONTEXT_SKIP` and concluded from the
245
+ constant; the guard is one line further on, in `gatherWorkspaceContext`, which
246
+ runs every candidate through `refusedCommitPath` — the exact fix that section
247
+ recommends. Re-measured on a fixture and pinned by a test asserting both
248
+ directions. **MVP-PLAN.md said `--parallel` was not built**; it is, and was
249
+ run end to end.
250
+
251
+ ### Added
252
+ - **MCP client.** Connect to any Model Context Protocol server declared in
253
+ `.acuvo/mcp.json`. Tools are namespaced `mcp__<server>__<tool>` so a remote
254
+ tool can never shadow a local one. Verified live: the model called a tool we
255
+ did not build and used the result.
256
+ - **`see_page`.** Renders HTML you wrote in a real browser, saves the screenshot
257
+ into your workspace, and reports measured layout problems — invisible text,
258
+ overflow, cramped sections. The edge is the **return value**, not the browser:
259
+ it hands back a short measured verdict instead of an image. Measured
260
+ 2026-08-10 against a live Playwright MCP server, same page: **89 tokens
261
+ against 3,072**, and the model never has to interpret its own screenshot.
262
+ - **Run lifecycle.** `--sessions` lists saved runs; `--resume <id>` / `--continue`
263
+ carry one on by **rebuilding** the conversation, never replaying it — no file
264
+ is rewritten and no command is re-run. `--no-session` opts out.
265
+ - **Audit log.** Every run appends one redacted JSON line to
266
+ `.acuvo/audit/<date>.jsonl`: what was asked, what changed, what verified, what
267
+ it cost. No file contents, no command output, no model prose, and secrets are
268
+ pattern-redacted first. `--no-audit` opts out; `--dry-run` writes neither file.
269
+ - **`make_document`, `transcribe`, `speak`.** HTML → PDF/PNG/PPTX; audio → text
270
+ with timestamped segments; text → audio.
271
+ - **`evaluate`.** Run a JavaScript snippet and see what it prints. Replaces
272
+ `node -e`, which cannot work here because a command may not contain quotes.
273
+ It is a **second code-execution path** and does not go through the command
274
+ whitelist — `--no-run` and `--dry-run` both stop it, and the README's security
275
+ section now says so instead of listing four programs and stopping.
276
+ - **Model chain.** Four **model ids** through the one OpenRouter endpoint and
277
+ key — not four vendors — with attempts bounded at four. A rate limit falls
278
+ through; a bad key stops immediately. An empty HTTP 200 counts as a failure.
279
+ - **Streaming.** The model's reasoning appears as it arrives instead of after a
280
+ 20-second silence.
281
+ - **Project memory.** `ACUVO.md` (or `CONVENTIONS.md` / `AGENTS.md`) is read at
282
+ the start of every session and its conventions are followed.
283
+ - **Git verbs** — `git_status`, `git_diff`, `git_log`, `git_commit`.
284
+ - **`delete_file`**, and **`--version`**.
285
+ - **Image generation is built in.** No configuration required.
286
+ - **Standalone test suite** — `npm test`, no network, no API key.
287
+
288
+ ### Changed
289
+ - A passing command no longer ends the turn. It buys one closing round, so a
290
+ task that says "fix it, then commit it" reaches the second half.
291
+ - The loop batches independent reads rather than spending a round on each.
292
+
293
+ ### Fixed
294
+ - The summary reported "NOTHING WAS RUN" after a successful `evaluate`.
295
+ - `--version` required an API key — the first command anyone runs after
296
+ installing refused to answer until they configured an account.
297
+ - Relative imports failed inside `evaluate` because the snippet was staged in a
298
+ subdirectory while the tool description promised they worked.
299
+ - MCP servers could not start on Windows (`npx` is `npx.cmd`; ENOENT, then a
300
+ bare shell script, then EINVAL). Now routed through node's own entry point.
301
+
302
+ ### Security
303
+ - **`--dry-run` and `--no-run` now stop the MCP spawn.** They did not. A
304
+ committed `.mcp.json` in a cloned repo was launched before a single file was
305
+ read, under the two flags documented as the cautious ones — a false guarantee
306
+ is worse than no flag, because it is the advice a careful person follows.
307
+ Reproduced under both flags with a hostile config, and reproduced again
308
+ without them to confirm the ordinary path still connects.
309
+ ⚠️ **Still open:** an ordinary run with no flags spawns it with no prompt and
310
+ an unscrubbed environment. Cloning an untrusted repo and running `acuvo` in it
311
+ is user-level RCE today. See `ENTERPRISE.md` §3.1.
312
+ - MCP servers come only from a file the user wrote. There is deliberately no
313
+ tool that lets the model add one, and a test asserts no such export exists.
314
+ - `git_commit` requires explicit paths. There is no "commit everything".
315
+ - `.env`, `*.pem` and `id_rsa` are never staged, whatever `.gitignore` says.
316
+ - Git commands are refused when the workspace sits inside a larger repository —
317
+ git walks upward, so operating from a subdirectory would commit the whole
318
+ outer project.
319
+
320
+ ## [0.2.0] — 2026-08-10
321
+
322
+ First release intended to be installed by someone other than its author.
323
+ Added a licence, a README whose every command was run before it was written, a
324
+ `files` allowlist, and a test suite that works on a fresh clone.
325
+
326
+ ## [0.1.0]
327
+
328
+ Internal. Write, run, read the failure, fix it.