acuvo-code 0.3.1 → 0.3.3
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/ENTERPRISE.md +2 -2
- package/bin/acuvo.mjs +57 -1
- package/lib/banner.mjs +97 -0
- package/package.json +1 -1
package/ENTERPRISE.md
CHANGED
|
@@ -188,8 +188,8 @@ copy, but it *is* a place a process starts, and this is a list of those. Six and
|
|
|
188
188
|
are the numbers to quote. Counting is the first thing a reviewer does.
|
|
189
189
|
|
|
190
190
|
⚠️ **This said "18 shipped files", then "41", then "90", then "101", then "108", and every
|
|
191
|
-
one went stale in turn.** The package ships **
|
|
192
|
-
`bin/` — about
|
|
191
|
+
one went stale in turn.** The package ships **119 files — 117 in `lib/`, 2 in
|
|
192
|
+
`bin/` — about 77552 lines**, with **234 test files** beside them (counted 2026-08-22).
|
|
193
193
|
|
|
194
194
|
⭐ **AND THE 108 WENT STALE IN THE MOST INSTRUCTIVE WAY POSSIBLE: THREE OF THE FILES IT
|
|
195
195
|
MISSED WERE REACHABLE FROM NOTHING.** `wiring-reach.test.mjs` was naming
|
package/bin/acuvo.mjs
CHANGED
|
@@ -1440,7 +1440,63 @@ ${formatBoard(listed)}
|
|
|
1440
1440
|
* exactly the fact a banner exists to state, so it stays, shortened and with
|
|
1441
1441
|
* any elision marked. See `shortenRoot`.
|
|
1442
1442
|
*/
|
|
1443
|
-
|
|
1443
|
+
/**
|
|
1444
|
+
* ── ⭐⭐⭐ WHO IS PAYING FOR THIS, ON THE LINE ABOVE THE FIRST SPEND ────────
|
|
1445
|
+
*
|
|
1446
|
+
* Roman opened a terminal, typed `acuvo`, and got a working prompt without
|
|
1447
|
+
* ever logging in — because a stray `OPENROUTER_API_KEY` in the project's
|
|
1448
|
+
* `.env.local` was picked up automatically. `resolveCredential` has the right
|
|
1449
|
+
* PRECEDENCE (an Acuvo account beats BYOK), but nothing on screen said which
|
|
1450
|
+
* one had won.
|
|
1451
|
+
*
|
|
1452
|
+
* ⚠️ THAT IS A BILLING FAILURE, NOT A COSMETIC ONE. A paying customer with a
|
|
1453
|
+
* leftover provider key in a project directory burns THEIR OWN credits while
|
|
1454
|
+
* believing their plan covers it, and the first evidence is somebody else's
|
|
1455
|
+
* invoice. The banner already states what the tool may RUN before it runs it;
|
|
1456
|
+
* stating what it will CHARGE before it charges is the same obligation.
|
|
1457
|
+
*/
|
|
1458
|
+
let billing = 'no key — run `acuvo --login`';
|
|
1459
|
+
try {
|
|
1460
|
+
const { resolveCredential } = await import('../lib/account.mjs');
|
|
1461
|
+
const cred = resolveCredential();
|
|
1462
|
+
billing =
|
|
1463
|
+
cred.mode === 'account' ? 'your Acuvo plan'
|
|
1464
|
+
: cred.mode === 'byok' ? 'YOUR OWN OpenRouter key (not your Acuvo plan)'
|
|
1465
|
+
: billing;
|
|
1466
|
+
} catch {
|
|
1467
|
+
// Never let a banner stop a run.
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
/**
|
|
1471
|
+
* ⚠️ THE SAFETY LINE SURVIVED THE REDESIGN, DELIBERATELY. Roman read
|
|
1472
|
+
* "may run: node, npm test, …" as the product describing itself and disliked
|
|
1473
|
+
* it — but a tool that can execute programs on your machine has to say so
|
|
1474
|
+
* above the first one it runs, and moving that into a README is how it stops
|
|
1475
|
+
* being read. It is relabelled `can run`, not removed.
|
|
1476
|
+
*/
|
|
1477
|
+
const pkgVersion = (() => {
|
|
1478
|
+
try {
|
|
1479
|
+
return JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version;
|
|
1480
|
+
} catch {
|
|
1481
|
+
return '';
|
|
1482
|
+
}
|
|
1483
|
+
})();
|
|
1484
|
+
|
|
1485
|
+
/**
|
|
1486
|
+
* ⚠️ `interactive: false` EVEN IN THE REPL, because `runChat` prints its own
|
|
1487
|
+
* "Type what you want done" line. Setting it here as well produced the
|
|
1488
|
+
* instruction twice, three lines apart — the banner owns the identity, the
|
|
1489
|
+
* chat loop owns the invitation, and neither should own both.
|
|
1490
|
+
*/
|
|
1491
|
+
const { openingScreen } = await import('../lib/banner.mjs');
|
|
1492
|
+
const banner = openingScreen({
|
|
1493
|
+
version: pkgVersion,
|
|
1494
|
+
workspace: shortenRoot(executor.root),
|
|
1495
|
+
model: config.model,
|
|
1496
|
+
billing,
|
|
1497
|
+
canRun: mode,
|
|
1498
|
+
interactive: false,
|
|
1499
|
+
});
|
|
1444
1500
|
if (opts.json) process.stderr.write(banner);
|
|
1445
1501
|
else process.stdout.write(banner);
|
|
1446
1502
|
|
package/lib/banner.mjs
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ── ⭐⭐⭐ WHAT YOU SEE WHEN YOU TYPE `acuvo` ────────────────────────────────
|
|
3
|
+
*
|
|
4
|
+
* Roman, 2026-08-22, having typed it: *"it's not opening as a typable terminal,
|
|
5
|
+
* with acuvo logo top left and details up top etc, like how claude code opens.
|
|
6
|
+
* have you actually designed the page?"*
|
|
7
|
+
*
|
|
8
|
+
* No — the previous version printed two dense lines of facts and a prompt. Every
|
|
9
|
+
* fact was correct and none of it was designed. This is the opening screen: a
|
|
10
|
+
* wordmark, one instruction, and the four things a person needs to know before
|
|
11
|
+
* they type anything.
|
|
12
|
+
*
|
|
13
|
+
* ── ⚠️ THE CONSTRAINTS THAT SHAPED IT, NOT PREFERENCES ──────────────────────
|
|
14
|
+
*
|
|
15
|
+
* · **Half-block glyphs only** (▀ ▄ █). Braille and fancy box-drawing render as
|
|
16
|
+
* tofu in cmd.exe and in a lot of CI log viewers, and a logo that renders as
|
|
17
|
+
* question marks is worse than no logo.
|
|
18
|
+
* · **No colour codes here.** The caller owns colour and already honours
|
|
19
|
+
* NO_COLOR; a module that hard-codes escapes produces garbage the moment its
|
|
20
|
+
* output is piped to a file.
|
|
21
|
+
* · **Every line under 80 columns**, because the narrowest terminal anyone
|
|
22
|
+
* actually uses is 80 and a wrapped banner looks broken rather than dense.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* ACUVO in half-blocks. Three rows, because five is a splash screen — it belongs
|
|
27
|
+
* to something you open once a day, not something you run forty times.
|
|
28
|
+
*/
|
|
29
|
+
const WORDMARK = [
|
|
30
|
+
'▄▀█ █▀▀ █ █ █ █ █▀█',
|
|
31
|
+
'█▀█ █▄▄ █▄█ ▀▄▀ █▄█',
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Build the opening screen.
|
|
36
|
+
*
|
|
37
|
+
* @param {object} o
|
|
38
|
+
* @param {string} o.version
|
|
39
|
+
* @param {string} o.workspace already shortened by the caller
|
|
40
|
+
* @param {string} o.model
|
|
41
|
+
* @param {string} o.billing who this run will charge
|
|
42
|
+
* @param {string} o.canRun what it may execute, or that it may not
|
|
43
|
+
* @param {boolean} [o.interactive] whether a prompt follows
|
|
44
|
+
* @returns {string}
|
|
45
|
+
*/
|
|
46
|
+
export function openingScreen({ version, workspace, model, billing, canRun, interactive = false }) {
|
|
47
|
+
/**
|
|
48
|
+
* ── ⚠️⚠️ ELIDED, BECAUSE THE VALUES ARE NOT OURS ────────────────────────────
|
|
49
|
+
*
|
|
50
|
+
* Every value here arrives at runtime: a deep monorepo path, a long model id,
|
|
51
|
+
* the shell-mode warning. The first version of this function assumed they
|
|
52
|
+
* would be short and its own test caught it wrapping at 80 columns on
|
|
53
|
+
* perfectly ordinary inputs.
|
|
54
|
+
*
|
|
55
|
+
* ⭐ ELIDED IN THE MIDDLE, NOT THE END. The informative parts of a path are the
|
|
56
|
+
* drive and the leaf; chopping the tail leaves `C:\Users\somebody\Projects\a-`
|
|
57
|
+
* which identifies nothing. Same for a model id, where the family is at the
|
|
58
|
+
* front and the variant at the back.
|
|
59
|
+
*/
|
|
60
|
+
const room = MAX_BANNER_COLUMNS - 2 - 11;
|
|
61
|
+
const fit = (v) => {
|
|
62
|
+
const s = String(v ?? '');
|
|
63
|
+
if (s.length <= room) return s;
|
|
64
|
+
const head = Math.ceil((room - 1) / 2);
|
|
65
|
+
const tail = room - 1 - head;
|
|
66
|
+
return `${s.slice(0, head)}…${s.slice(s.length - tail)}`;
|
|
67
|
+
};
|
|
68
|
+
const label = (k, v) => ` ${k.padEnd(11)}${fit(v)}`;
|
|
69
|
+
const lines = [
|
|
70
|
+
'',
|
|
71
|
+
` ${WORDMARK[0]}`,
|
|
72
|
+
` ${WORDMARK[1]} CODE${version ? ` ${version}` : ''}`,
|
|
73
|
+
'',
|
|
74
|
+
label('workspace', workspace),
|
|
75
|
+
label('model', model),
|
|
76
|
+
label('billing', billing),
|
|
77
|
+
label('can run', canRun),
|
|
78
|
+
'',
|
|
79
|
+
];
|
|
80
|
+
/**
|
|
81
|
+
* ⚠️ ONLY WHEN A PROMPT ACTUALLY FOLLOWS. Printing "type what you want done"
|
|
82
|
+
* above a one-shot run that has already been given its task is an instruction
|
|
83
|
+
* for something the user cannot do, on the screen of a thing already working.
|
|
84
|
+
*/
|
|
85
|
+
if (interactive) {
|
|
86
|
+
lines.push(' Type what you want done. /help for commands · exit to leave', '');
|
|
87
|
+
}
|
|
88
|
+
return lines.join('\n');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* ⚠️ EXPORTED SO A TEST CAN ASSERT THE WIDTH RULE RATHER THAN TRUST IT. The
|
|
93
|
+
* banner is assembled from values supplied at runtime — a long model id or a
|
|
94
|
+
* deep workspace path is what would push it over, and neither is visible from
|
|
95
|
+
* reading this file.
|
|
96
|
+
*/
|
|
97
|
+
export const MAX_BANNER_COLUMNS = 80;
|