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/lib/ask-user.mjs
ADDED
|
@@ -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
|
+
}
|