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,173 @@
1
+ /**
2
+ * ── ⭐⭐ THE AGENT COULD NOT ASK A QUESTION, AND THAT IS THE GAP ─────────────
3
+ *
4
+ * Of the 47 tools this package offers, not one lets the model say "I need a
5
+ * decision from you". It reads, writes, runs, searches, renders and remembers —
6
+ * and when the task is ambiguous it GUESSES, because guessing is the only thing
7
+ * it can do.
8
+ *
9
+ * ⭐ Asked what Claude Code and Codex CLI do that matters most and is easiest to
10
+ * underestimate, an independent model answered: *"the UX polish on ambiguity
11
+ * handling — asking one clarifying question instead of guessing wrong is the
12
+ * difference between a tool you trust and a toy you supervise."* That matches
13
+ * what this package's own failure log says: the expensive failures are not bad
14
+ * code, they are correct code built to the wrong requirement.
15
+ *
16
+ * ── ⚠️ AND YET AN AGENT THAT ASKS IS EASILY WORSE THAN ONE THAT GUESSES ─────
17
+ *
18
+ * A tool like this fails in three directions, and all three are designed
19
+ * against here rather than hoped away:
20
+ *
21
+ * 1. **It hangs.** In CI, a pipe or a task runner there is nobody to answer.
22
+ * An agent blocked on input that will never arrive fails by TIMEOUT — the
23
+ * least legible failure available, burning a whole job to say nothing.
24
+ * ⭐ Handled by NOT OFFERING the tool at all when there is no terminal
25
+ * (`prompt.mjs`'s `createAsker` returns null), and by the asker resolving
26
+ * `null` rather than pending when a stream ends. A tool that is absent
27
+ * cannot be called; that is stronger than a tool that refuses.
28
+ *
29
+ * 2. **It interrogates.** An agent that asks five questions has moved the work
30
+ * back onto the person and is worse than useless — they could have done it
31
+ * themselves. ⭐ Handled by a hard per-run allowance (`MAX_QUESTIONS`), and
32
+ * by the allowance being SPENT even on a refusal, so a model cannot burn
33
+ * rounds retrying a question it was already told to stop asking.
34
+ *
35
+ * 3. **It asks what it could have looked up.** "What framework is this?" is
36
+ * not a question for a human in a repository the agent can read. ⭐ Handled
37
+ * in the description, which is the only place a model's behaviour is
38
+ * actually steered, and which spends its words on WHEN NOT TO CALL THIS.
39
+ *
40
+ * ── ⚠️ THE ANSWER IS UNTRUSTED TEXT, LIKE EVERY OTHER TOOL RESULT ───────────
41
+ *
42
+ * Whatever the user types goes straight into the prompt. It is clamped like any
43
+ * other result: a pasted logfile must not become the context window.
44
+ */
45
+
46
+ import { clampOutput } from './command.mjs';
47
+
48
+ /**
49
+ * ⚠️ THREE, AND THE NUMBER IS AN OPINION WORTH DEFENDING.
50
+ *
51
+ * One is too few — a genuinely ambiguous task often has a second question that
52
+ * only becomes visible after the first is answered. Five is an interview. Three
53
+ * is enough for "which of these two designs", "is this data real", "may I
54
+ * delete this" in one run, and few enough that a model which has started
55
+ * interrogating is stopped before the person gives up on it.
56
+ */
57
+ export const MAX_QUESTIONS = 3;
58
+
59
+ /** A question longer than this is a paragraph, and a paragraph is not a question. */
60
+ export const MAX_QUESTION_CHARS = 400;
61
+
62
+ /** Enough for a considered sentence or a pasted path; not enough for a logfile. */
63
+ export const MAX_ANSWER_CHARS = 2_000;
64
+
65
+ export function askUserToolSchemas() {
66
+ return [
67
+ {
68
+ type: 'function',
69
+ function: {
70
+ name: 'ask_user',
71
+ /**
72
+ * ⚠️ MOST OF THIS DESCRIPTION IS ABOUT WHEN **NOT** TO CALL IT.
73
+ *
74
+ * The description is the only lever on a model's behaviour that exists
75
+ * at call time — there is no runtime check that can tell a good
76
+ * question from a lazy one. So the words are spent on the failure mode
77
+ * (asking instead of looking) rather than on restating the obvious
78
+ * capability.
79
+ */
80
+ description: [
81
+ 'Ask the person running you ONE short question, and wait for their answer.',
82
+ 'Use this ONLY when the task is genuinely ambiguous and the wrong choice would waste real work —',
83
+ 'two reasonable interpretations that lead somewhere different, a destructive step you want confirmed,',
84
+ 'or a missing fact that exists only in their head.',
85
+ 'DO NOT use it for anything you can find out yourself: read the files, look at the tests,',
86
+ 'check the config, run the command. Asking what you could have looked up is worse than not asking.',
87
+ 'DO NOT use it to check in, to report progress, or to ask permission to continue — just continue.',
88
+ `You may ask at most ${MAX_QUESTIONS} questions in a whole run, so spend them on decisions, not details.`,
89
+ 'If nobody answers, you will be told so, and you must then make the most reasonable choice',
90
+ 'and say plainly in your final message which assumption you took.',
91
+ ].join(' '),
92
+ parameters: {
93
+ type: 'object',
94
+ properties: {
95
+ question: {
96
+ type: 'string',
97
+ description: 'The question, in one sentence. Include the options if there are options.',
98
+ },
99
+ },
100
+ required: ['question'],
101
+ },
102
+ },
103
+ },
104
+ ];
105
+ }
106
+
107
+ /**
108
+ * Wrap a raw asker in the per-run allowance.
109
+ *
110
+ * ⚠️ THE STATE LIVES HERE, NOT IN THE DISPATCHER. `executeToolCall` is a pure
111
+ * switch over one call and has no memory of the round before it — giving it a
112
+ * counter would mean threading mutable state through every tool in the file for
113
+ * the sake of one. The turn loop already owns per-run state, so it owns this
114
+ * too, and the dispatcher keeps receiving a plain function.
115
+ *
116
+ * @param {null | ((q: string) => Promise<string|null>)} ask
117
+ * @param {{ max?: number }} [opts]
118
+ * @returns {null | ((q: string) => Promise<{ok: true, answer: string} | {ok: false, error: string}>)}
119
+ */
120
+ export function budgetedAsker(ask, { max = MAX_QUESTIONS } = {}) {
121
+ if (typeof ask !== 'function') return null;
122
+ let used = 0;
123
+ return async (question) => {
124
+ const q = String(question ?? '').trim();
125
+ if (!q) return { ok: false, error: 'ask_user needs a question — an empty one cannot be answered' };
126
+ if (q.length > MAX_QUESTION_CHARS) {
127
+ return { ok: false, error: `that question is ${q.length} characters; keep it under ${MAX_QUESTION_CHARS}. One sentence.` };
128
+ }
129
+
130
+ /**
131
+ * ⚠️ THE ALLOWANCE IS SPENT BEFORE THE ANSWER ARRIVES, DELIBERATELY.
132
+ * Counting only successful answers would let a model that keeps asking
133
+ * unanswerable questions loop forever at the user's expense. The budget is
134
+ * on ASKING, which is the thing being rationed.
135
+ */
136
+ if (used >= max) {
137
+ return {
138
+ ok: false,
139
+ error: `you have used all ${max} of your questions for this run. Make the most reasonable choice now, `
140
+ + 'act on it, and state the assumption you took in your final message.',
141
+ };
142
+ }
143
+ used += 1;
144
+
145
+ const answer = await ask(`\n ${q}\n > `);
146
+ if (answer === null) {
147
+ /**
148
+ * ⭐ "NOBODY ANSWERED" IS AN ANSWER. It is `ok: true` on purpose: the tool
149
+ * did its job, the run is not broken, and the model needs to proceed
150
+ * rather than treat this as an error worth retrying. The instruction to
151
+ * carry on is in the payload because a model reads the result, not this
152
+ * comment.
153
+ */
154
+ return {
155
+ ok: true,
156
+ answer: '(no answer — the terminal closed or nobody was there). '
157
+ + 'Make the most reasonable choice, continue, and state the assumption you took in your final message.',
158
+ answered: false,
159
+ };
160
+ }
161
+ if (answer === '') {
162
+ return {
163
+ ok: true,
164
+ answer: '(the user pressed enter without answering, which usually means "you decide"). '
165
+ + 'Take the most reasonable option and say which one you took.',
166
+ answered: false,
167
+ };
168
+ }
169
+ // `clampOutput` returns {text, truncated, omitted} and marks the omission
170
+ // inside the text itself, so a truncated answer never silently reads whole.
171
+ return { ok: true, answer: clampOutput(answer, MAX_ANSWER_CHARS).text, answered: true };
172
+ };
173
+ }