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.
- package/CHANGELOG.md +328 -0
- package/ENTERPRISE.md +927 -0
- package/LICENSE +120 -0
- package/README.md +1245 -0
- package/ROADMAP.md +556 -0
- package/bin/acuvo-mcp.mjs +208 -0
- package/bin/acuvo.mjs +3108 -0
- package/lib/acceptance-consent.mjs +168 -0
- package/lib/acceptance.mjs +859 -0
- package/lib/account.mjs +226 -0
- package/lib/acuvo-dir.mjs +72 -0
- package/lib/acuvo-models.mjs +141 -0
- package/lib/apply-patch.mjs +570 -0
- package/lib/ask-user.mjs +173 -0
- package/lib/audit.mjs +530 -0
- package/lib/auto-lease.mjs +174 -0
- package/lib/background.mjs +842 -0
- package/lib/best-of.mjs +334 -0
- package/lib/board.mjs +232 -0
- package/lib/breaker.mjs +93 -0
- package/lib/budget.mjs +1375 -0
- package/lib/builtin-skills.mjs +135 -0
- package/lib/cache-floor.mjs +204 -0
- package/lib/chain.mjs +303 -0
- package/lib/changed-paths.mjs +84 -0
- package/lib/chat.mjs +434 -0
- package/lib/checkpoint.mjs +637 -0
- package/lib/child-lifetime.mjs +71 -0
- package/lib/cli-args.mjs +1255 -0
- package/lib/code-review.mjs +1382 -0
- package/lib/colour.mjs +82 -0
- package/lib/command.mjs +2847 -0
- package/lib/compact.mjs +1151 -0
- package/lib/completion.mjs +515 -0
- package/lib/creative-engines.mjs +779 -0
- package/lib/db-inspect.mjs +1624 -0
- package/lib/delete.mjs +111 -0
- package/lib/design-loop.mjs +570 -0
- package/lib/diff-preview.mjs +1044 -0
- package/lib/doctor.mjs +2139 -0
- package/lib/dropped.mjs +216 -0
- package/lib/edit-diagnostics.mjs +277 -0
- package/lib/edit.mjs +460 -0
- package/lib/env-file.mjs +250 -0
- package/lib/escalate.mjs +702 -0
- package/lib/evaluate.mjs +284 -0
- package/lib/fetch-text.mjs +952 -0
- package/lib/fleet-budget.mjs +256 -0
- package/lib/gh.mjs +1536 -0
- package/lib/git.mjs +1341 -0
- package/lib/github.mjs +261 -0
- package/lib/h2.mjs +194 -0
- package/lib/handoff.mjs +417 -0
- package/lib/hooks.mjs +626 -0
- package/lib/http-probe.mjs +907 -0
- package/lib/image-director.mjs +322 -0
- package/lib/image-edit.mjs +522 -0
- package/lib/imagegen.mjs +998 -0
- package/lib/interrupt.mjs +234 -0
- package/lib/learned.mjs +353 -0
- package/lib/lease-watch.mjs +115 -0
- package/lib/lease.mjs +868 -0
- package/lib/localize.mjs +834 -0
- package/lib/log-tail.mjs +1052 -0
- package/lib/login.mjs +157 -0
- package/lib/lsp.mjs +1613 -0
- package/lib/mcp-consent.mjs +377 -0
- package/lib/mcp-defaults.mjs +780 -0
- package/lib/mcp-server.mjs +1343 -0
- package/lib/mcp.mjs +1263 -0
- package/lib/media.mjs +1283 -0
- package/lib/memory-workspace.mjs +179 -0
- package/lib/model-json.mjs +132 -0
- package/lib/model-tier.mjs +171 -0
- package/lib/model.mjs +1445 -0
- package/lib/parallel.mjs +144 -0
- package/lib/perchance.mjs +210 -0
- package/lib/plan-coherence.mjs +1461 -0
- package/lib/plan-ledger.mjs +981 -0
- package/lib/plan.mjs +461 -0
- package/lib/policy.mjs +783 -0
- package/lib/prefix-order.mjs +38 -0
- package/lib/project-memory.mjs +127 -0
- package/lib/prompt.mjs +109 -0
- package/lib/python.mjs +862 -0
- package/lib/rcfile.mjs +853 -0
- package/lib/read-window.mjs +743 -0
- package/lib/refute-tools.mjs +34 -0
- package/lib/refute.mjs +806 -0
- package/lib/repl-driver.mjs +264 -0
- package/lib/repl.mjs +324 -0
- package/lib/replay.mjs +1218 -0
- package/lib/repo-map.mjs +1101 -0
- package/lib/report.mjs +419 -0
- package/lib/search-rank.mjs +99 -0
- package/lib/search.mjs +659 -0
- package/lib/secret-paths.mjs +54 -0
- package/lib/session.mjs +1017 -0
- package/lib/skills.mjs +703 -0
- package/lib/slash.mjs +356 -0
- package/lib/spawn-argv.mjs +1151 -0
- package/lib/spend.mjs +250 -0
- package/lib/steer.mjs +280 -0
- package/lib/stream.mjs +253 -0
- package/lib/stuck.mjs +712 -0
- package/lib/subagent.mjs +749 -0
- package/lib/terminal-graphics.mjs +171 -0
- package/lib/tool-prefix.mjs +226 -0
- package/lib/tool-shortlist.mjs +162 -0
- package/lib/tools.mjs +2333 -0
- package/lib/tsserver.mjs +423 -0
- package/lib/turn.mjs +5672 -0
- package/lib/untrusted-block.mjs +271 -0
- package/lib/verify-claim.mjs +299 -0
- package/lib/vision.mjs +330 -0
- package/lib/voice-task.mjs +561 -0
- package/lib/warm-provider.mjs +255 -0
- package/lib/websearch.mjs +401 -0
- package/lib/workspace.mjs +928 -0
- package/lib/write-approval.mjs +235 -0
- package/lib/write-many.mjs +162 -0
- package/package.json +62 -0
- package/scripts/bundle.mjs +768 -0
- package/scripts/cache-floor.mjs +176 -0
- package/scripts/machine.mjs +226 -0
- package/scripts/test.mjs +139 -0
- package/skills/accessibility.md +87 -0
- package/skills/acuvo-design-system.md +123 -0
- package/skills/animation.md +84 -0
- package/skills/api-design.md +82 -0
- package/skills/auth-and-sessions.md +78 -0
- package/skills/build-with-a-framework.md +101 -0
- package/skills/colour-and-contrast.md +112 -0
- package/skills/creative-engines.md +81 -0
- package/skills/css-layout.md +85 -0
- package/skills/data-and-charts.md +77 -0
- package/skills/debugging.md +76 -0
- package/skills/designing-by-looking.md +84 -0
- package/skills/error-handling.md +78 -0
- package/skills/forms-and-validation.md +93 -0
- package/skills/nextjs-app-router.md +75 -0
- package/skills/page-composition.md +103 -0
- package/skills/performance.md +77 -0
- package/skills/plan-before-building.md +52 -0
- package/skills/planning-and-delegating.md +72 -0
- package/skills/refactoring.md +70 -0
- package/skills/security-basics.md +76 -0
- package/skills/state-management.md +73 -0
- package/skills/supabase-multitenant.md +72 -0
- package/skills/typescript-strict.md +90 -0
- package/skills/typography.md +135 -0
- package/skills/verify-your-own-work.md +62 -0
- package/skills/web-app-quality.md +62 -0
- 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.
|