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
@@ -0,0 +1,174 @@
1
+ /**
2
+ * ── ⭐⭐ `--lease` IS A DECLARATION; THIS IS THE GUARANTEE ────────────────────
3
+ *
4
+ * `bin/acuvo.mjs` states the limit honestly at the import of `lease.mjs`:
5
+ *
6
+ * "a coding agent does not know which files it will write until it writes
7
+ * them, so `--lease a.ts --lease b.ts` is a DECLARATION, not a guarantee.
8
+ * It protects exactly the paths named. The complete fix is one `acquire()`
9
+ * call inside the executor's write path."
10
+ *
11
+ * This is that call. Every write and every delete claims its own path first, so
12
+ * protection no longer depends on the user having correctly predicted, in
13
+ * advance, what the model was going to do.
14
+ *
15
+ * ⚠️ WHY IT MATTERS MORE THAN IT SOUNDS. The product direction is seven
16
+ * terminals against one checkout. Today two of them writing the same file is
17
+ * SILENT DATA LOSS that surfaces later as "the agent undid my change" — the
18
+ * worst class of bug this package can ship, because the evidence is gone by the
19
+ * time anyone notices. `parallel.mjs` has `detectConflicts`, but read what it
20
+ * does: it walks the results AFTER both tasks have finished and reports which
21
+ * paths two of them wrote. Reporting a collision is not preventing one.
22
+ *
23
+ * ── ⚠️⚠️ THE JUDGEMENT THAT MATTERS: WHEN TO REFUSE ─────────────────────────
24
+ *
25
+ * A guard on the write path can destroy this tool. If it refuses wrongly, the
26
+ * agent cannot write files, which is the entire product. So the two failures
27
+ * are split deliberately and they are NOT treated the same:
28
+ *
29
+ * · **A known conflict — another terminal holds this exact path — REFUSES.**
30
+ * We have positive evidence someone else owns the file. Refusing is the
31
+ * whole feature, and the refusal names the holder so it can be acted on.
32
+ *
33
+ * · **An infrastructure failure — the lease directory is unwritable, the disk
34
+ * is full, a record is corrupt — ALLOWS THE WRITE and marks it degraded.**
35
+ * ⭐ This is the important half. Yesterday there was no protection at all,
36
+ * so a broken `.acuvo/leases` directory that BLOCKED every write would be a
37
+ * strictly worse product than the one that shipped without leases. "A check
38
+ * that fails correct work is worse than no check" is the rule this package
39
+ * has been burned by four times in one day, and a guard on the write path is
40
+ * where it would hurt most. We refuse only what we can prove.
41
+ *
42
+ * `heldBy` is the discriminator: `lease.mjs` sets it only when a live record
43
+ * belonging to somebody else is what stopped the acquire.
44
+ *
45
+ * ── ⭐ RE-ENTRANT, BECAUSE A RUN WRITES THE SAME FILE MANY TIMES ─────────────
46
+ *
47
+ * A write → run → fix loop rewrites one file three or four times. Claiming it
48
+ * once and remembering that is not an optimisation, it is correctness: the
49
+ * second acquire would be refused by our OWN lease, and the agent would be
50
+ * blocked by itself, which is the most confusing possible failure.
51
+ *
52
+ * ⭐ A repeat claim RENEWS instead. That is a free keepalive on exactly the
53
+ * files being worked on: a run that outlives the TTL keeps what it is actively
54
+ * using and lets go of what it touched once and abandoned, which is the
55
+ * behaviour you would design on purpose.
56
+ */
57
+
58
+ import { acquire, release, renew, DEFAULT_TTL_MS } from './lease.mjs';
59
+
60
+ /**
61
+ * @param {string} root
62
+ * @param {object} [opts]
63
+ * @param {string} [opts.holder] who to record as holding. Defaults to the pid.
64
+ * @param {number} [opts.ttlMs]
65
+ * @param {Function} [opts.acquireImpl] injected for tests — no disk required
66
+ * @param {Function} [opts.releaseImpl]
67
+ * @param {Function} [opts.renewImpl]
68
+ */
69
+ export function createPathClaimer(root, {
70
+ holder = `pid-${process.pid}`,
71
+ ttlMs = DEFAULT_TTL_MS,
72
+ acquireImpl = acquire,
73
+ releaseImpl = release,
74
+ renewImpl = renew,
75
+ } = {}) {
76
+ /** @type {Map<string, any>} relative path -> the lease handle we hold */
77
+ const mine = new Map();
78
+ /** Paths we could not protect, so the caller can say so once at the end. */
79
+ const degraded = new Set();
80
+
81
+ return {
82
+ /**
83
+ * Claim `relPath` for writing.
84
+ *
85
+ * @returns {{ok: true, degraded?: boolean} | {ok: false, error: string, heldBy: string}}
86
+ */
87
+ claim(relPath) {
88
+ const path = String(relPath ?? '').trim();
89
+ if (!path) return { ok: true };
90
+
91
+ // Already ours: renew and carry on. Never re-acquire — see the header.
92
+ const existing = mine.get(path);
93
+ if (existing) {
94
+ try { renewImpl(existing, { ttlMs }); } catch { /* a failed keepalive is not a reason to block a write */ }
95
+ return { ok: true };
96
+ }
97
+
98
+ let got;
99
+ try {
100
+ got = acquireImpl(root, { path, holder, pid: process.pid, ttlMs });
101
+ } catch (err) {
102
+ /**
103
+ * ⚠️ A THROW IS INFRASTRUCTURE, NEVER A CONFLICT. `lease.mjs` returns
104
+ * its refusals; it throws only when something underneath it broke. Let
105
+ * the write through rather than making a filesystem problem look like a
106
+ * colleague holding the file.
107
+ */
108
+ degraded.add(path);
109
+ return { ok: true, degraded: true, reason: err?.message ?? String(err) };
110
+ }
111
+
112
+ if (got?.ok) {
113
+ /**
114
+ * ⚠️ `got.lease`, NOT `got`. `acquire` returns
115
+ * `{ok, lease, warnings}` and the HANDLE — the thing `release` and
116
+ * `renew` need, carrying `file` and `token` — is nested inside it.
117
+ * Storing the result instead of the handle made `release` a silent
118
+ * no-op: it type-checks its argument and returns
119
+ * `{ok:true, released:false, reason:'not a lease handle'}`, so every
120
+ * path stayed locked for its full TTL and nothing said so.
121
+ *
122
+ * ⭐ Caught by the round-trip test (claim → release → another terminal
123
+ * claims), never by the unit tests — their stub `acquireImpl` returned
124
+ * a shape the real module does not use. A stub that does not match
125
+ * reality tests the stub.
126
+ */
127
+ mine.set(path, got.lease ?? got);
128
+ return { ok: true };
129
+ }
130
+
131
+ if (got?.heldBy) {
132
+ return {
133
+ ok: false,
134
+ heldBy: got.heldBy,
135
+ error:
136
+ `${path} is being written by another terminal (${got.heldBy}) right now, so this write was refused `
137
+ + 'rather than silently overwriting their work. Wait for them to finish, work on a different file, or run '
138
+ + '`acuvo leases` to see who holds what.',
139
+ };
140
+ }
141
+
142
+ /**
143
+ * Refused for a reason that is not a conflict — an unwritable lease
144
+ * directory, a corrupt record, a path the lease layer will not normalise.
145
+ * The write proceeds unprotected, which is exactly what happened on every
146
+ * run before this module existed.
147
+ */
148
+ degraded.add(path);
149
+ return { ok: true, degraded: true, reason: got?.error ?? 'the lease could not be taken' };
150
+ },
151
+
152
+ /** Paths written without protection, if any. For one honest line at the end. */
153
+ unprotected() {
154
+ return [...degraded];
155
+ },
156
+
157
+ held() {
158
+ return [...mine.keys()];
159
+ },
160
+
161
+ /**
162
+ * ⚠️ Release must never throw. It runs from an `exit` handler, where a
163
+ * throw is an ugly crash on the way out of a run that already succeeded —
164
+ * and every lease has a TTL, so the worst case of a missed release is a
165
+ * path that frees itself shortly afterwards.
166
+ */
167
+ releaseAll() {
168
+ for (const handle of mine.values()) {
169
+ try { releaseImpl(handle); } catch { /* the TTL will clear it */ }
170
+ }
171
+ mine.clear();
172
+ },
173
+ };
174
+ }