geneseed 3.14.0 → 3.15.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 +54 -0
- package/SHIPPED.md +1 -1
- package/adapters/opencode/plugins/geneseed-context.js +23 -9
- package/adapters/opencode/plugins/geneseed-guard.js +16 -4
- package/adapters/opencode/plugins/geneseed-learn.js +37 -11
- package/adapters/opencode/plugins/geneseed-notify.js +37 -19
- package/adapters/opencode/plugins/geneseed-workflow.js +3 -1
- package/adapters/opencode/workflows/_runtime.js +16 -3
- package/bin/geneseed-cli.mjs +22 -20
- package/bin/geneseed-hook.mjs +6 -3
- package/harness.config.json +1 -1
- package/js/README.md +9 -5
- package/js/build/args.mjs +4 -3
- package/js/build/bundle.mjs +10 -10
- package/js/build/driver.mjs +8 -7
- package/js/build/emit-claude.mjs +3 -2
- package/js/build/emit-common.mjs +2 -14
- package/js/build/emit-opencode.mjs +12 -8
- package/js/build/generate.mjs +15 -11
- package/js/build/render.mjs +1 -1
- package/js/build/source.mjs +6 -6
- package/js/build/stubs.mjs +6 -5
- package/js/build/version.mjs +21 -2
- package/js/hosts/hooks-context.mjs +2 -1
- package/js/hosts/hooks-prims.mjs +2 -2
- package/js/hosts/hosts.mjs +24 -26
- package/js/hosts/installs.mjs +33 -14
- package/js/hosts/link.mjs +75 -26
- package/js/hosts/memory-files.mjs +13 -4
- package/js/hosts/native.mjs +106 -48
- package/js/hosts/opencode.mjs +44 -36
- package/js/hosts/shim.mjs +4 -2
- package/js/inspect/diff.mjs +5 -6
- package/js/inspect/excludes.mjs +10 -5
- package/js/inspect/status.mjs +18 -21
- package/js/inspect/validate.mjs +2 -2
- package/js/lib/fs.mjs +12 -11
- package/js/lib/json.mjs +4 -4
- package/js/lib/paths.mjs +27 -33
- package/js/lib/proc.mjs +3 -3
- package/js/lib/prompt.mjs +132 -0
- package/js/lib/text.mjs +18 -4
- package/js/lib/udiff.mjs +3 -2
- package/js/maintain/memory.mjs +5 -6
- package/js/maintain/migrate.mjs +4 -3
- package/js/maintain/setup.mjs +9 -131
- package/js/maintain/uninstall.mjs +10 -9
- package/js/maintain/update.mjs +115 -34
- package/js/ui/anim.mjs +8 -12
- package/js/ui/cli.mjs +29 -35
- package/js/ui/menu.mjs +7 -13
- package/js/ui/tui.mjs +14 -123
- package/js/web/actions.mjs +82 -386
- package/js/web/activity.mjs +1 -1
- package/js/web/api.mjs +33 -717
- package/js/web/catalog.mjs +527 -0
- package/js/web/daemon.mjs +21 -3
- package/js/web/handler.mjs +51 -27
- package/js/web/jobs.mjs +24 -2
- package/js/web/routes.mjs +61 -6
- package/js/web/server.mjs +4 -4
- package/js/web/state.mjs +138 -0
- package/js/web/user-files.mjs +339 -0
- package/package.json +1 -1
- package/web/dist/assets/{Activity-CjRqZnr-.js → Activity-kpWxd70F.js} +1 -1
- package/web/dist/assets/{ActivityDetail-OQTxKBj2.js → ActivityDetail-Ccr942VL.js} +1 -1
- package/web/dist/assets/Dashboard-D1lE7jcg.js +2 -0
- package/web/dist/assets/{Docs-CvOZ8akL.js → Docs-BHyaggFy.js} +3 -3
- package/web/dist/assets/{GreenhouseView-6KDjX8da.js → GreenhouseView-Sc_UDMOE.js} +1 -1
- package/web/dist/assets/Installs-Gw-pJ3Zi.js +2 -0
- package/web/dist/assets/JournalView-_gxpsxXi.js +1 -0
- package/web/dist/assets/{Laws-Du0-Zyp2.js → Laws-S9fq1r9T.js} +1 -1
- package/web/dist/assets/{Library-DOkO0TtB.js → Library-MBxadRzn.js} +1 -1
- package/web/dist/assets/{Loops-Dv_aLByJ.js → Loops-Da_JhlzM.js} +2 -2
- package/web/dist/assets/{OperatorHudView-BPP1dtLz.js → OperatorHudView--TkvDybH.js} +1 -1
- package/web/dist/assets/{Personal-26VPYkK0.js → Personal-CTXKHVbV.js} +1 -1
- package/web/dist/assets/Tabs-B0cdf8k6.js +1 -0
- package/web/dist/assets/api-CxgfYu18.js +1 -0
- package/web/dist/assets/drawable-BbWQxe82.js +1 -0
- package/web/dist/assets/index-ujKG1ZNf.js +2 -0
- package/web/dist/index.html +2 -2
- package/web/dist/assets/Dashboard-J-xCe_Gk.js +0 -2
- package/web/dist/assets/Installs-MWuhNjSm.js +0 -2
- package/web/dist/assets/JournalView-C8MrOz2c.js +0 -1
- package/web/dist/assets/Tabs-BzKuFskD.js +0 -1
- package/web/dist/assets/api-D_ixdyEY.js +0 -1
- package/web/dist/assets/drawable-zVVDbsyw.js +0 -1
- package/web/dist/assets/index-KHt3wE_K.js +0 -2
package/js/ui/tui.mjs
CHANGED
|
@@ -21,13 +21,11 @@
|
|
|
21
21
|
* deleted the day the real one crossed, would be a second user interface and not a port.
|
|
22
22
|
*
|
|
23
23
|
* WHAT DOES CROSS is the alignment contract — the functions below that decide what a row
|
|
24
|
-
* looks like
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* this port has shipped code before whose absence nothing would have noticed, and these are
|
|
30
|
-
* not that.
|
|
24
|
+
* looks like: display width, fitting, glyph tiers, the catalogue's entries and detail lines.
|
|
25
|
+
* Each has a caller outside this file (the catalogue, the status panel, the web console).
|
|
26
|
+
* The panel itself was REJECTED, not deferred, so the helpers that existed only for it —
|
|
27
|
+
* the spinner, the wordmark, scroll clamping, the progress bar and the theme preview/flair
|
|
28
|
+
* readers — were deleted rather than kept waiting for a screen that will not come.
|
|
31
29
|
*
|
|
32
30
|
* WHAT IS DELIBERATELY NOT HERE: `_bx` (its non-ASCII arm returned `curses.ACS_*` chtype
|
|
33
31
|
* ints, which belonged with the window) and every `(stdscr, curses, pal)` drawing helper.
|
|
@@ -37,14 +35,10 @@
|
|
|
37
35
|
* **DO NOT WRITE A SECOND LINE BREAKER.** This block used to argue that `textwrap.wrap` was
|
|
38
36
|
* "a second `difflib` — a stdlib payload with no Node twin". It has one since P1:
|
|
39
37
|
* `wrapText` in `js/ui/cli.mjs`, written because `--help` was the caller that finally
|
|
40
|
-
* required it,
|
|
38
|
+
* required it, pinned by the written-out rows in `tests/unit/text_layout.test.mjs`.
|
|
41
39
|
* Anything that needs wrapping wraps THAT. `_parse_laws`, `load_registry`, `entity_status` and
|
|
42
40
|
* `_tui_inventory` crossed in P6c and live in `js/inspect/inventory.mjs`.
|
|
43
41
|
*/
|
|
44
|
-
import path from 'node:path';
|
|
45
|
-
|
|
46
|
-
import { THEMES } from '../build/source.mjs';
|
|
47
|
-
import { readJsonMaybe } from '../hosts/installs.mjs';
|
|
48
42
|
import { splitLines } from '../lib/udiff.mjs';
|
|
49
43
|
import { printOut } from '../lib/fs.mjs';
|
|
50
44
|
|
|
@@ -58,7 +52,6 @@ import { printOut } from '../lib/fs.mjs';
|
|
|
58
52
|
const TUI_ASCII = !!process.env.GENESEED_TUI_ASCII;
|
|
59
53
|
const TUI_PLAIN = !!process.env.GENESEED_TUI_PLAIN;
|
|
60
54
|
const TUI_EMOJI = !(TUI_ASCII || TUI_PLAIN);
|
|
61
|
-
const TUI_ANIM = TUI_EMOJI;
|
|
62
55
|
|
|
63
56
|
/** `_glyphs` — the one glyph table for the whole TUI, unicode or ASCII stand-ins. */
|
|
64
57
|
export function glyphs(asciiMode) {
|
|
@@ -269,119 +262,18 @@ export function mark(kind) {
|
|
|
269
262
|
return TUI_ASCII ? asc : (TUI_EMOJI ? emoji : sym);
|
|
270
263
|
}
|
|
271
264
|
|
|
272
|
-
const SPIN = '⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏';
|
|
273
|
-
|
|
274
|
-
/** `_spin` — one spinner frame for tick `i`; a static dot in the non-animated tiers. */
|
|
275
|
-
export function spin(i) {
|
|
276
|
-
if (!TUI_ANIM) return TUI_ASCII ? '-' : '·';
|
|
277
|
-
const frames = TUI_ASCII ? '|/-\\' : SPIN;
|
|
278
|
-
// `frames[i % len]` on the reference indexes CODE POINTS; the braille frames are BMP so
|
|
279
|
-
// UTF-16 indexing agrees, but spelling it through `[...frames]` keeps that an argument
|
|
280
|
-
// about the data rather than a coincidence. Python's `%` on a negative `i` returns a
|
|
281
|
-
// non-negative remainder and JavaScript's does not, so the modulo is corrected.
|
|
282
|
-
const f = [...frames];
|
|
283
|
-
return f[((i % f.length) + f.length) % f.length];
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
const LOGO_FONT = {
|
|
287
|
-
G: [' ### ', '# ', '# ## ', '# # ', ' ### '],
|
|
288
|
-
E: ['#### ', '# ', '### ', '# ', '#### '],
|
|
289
|
-
N: ['# #', '## #', '# # #', '# ##', '# #'],
|
|
290
|
-
S: [' ####', '# ', ' ### ', ' #', '#### '],
|
|
291
|
-
D: ['### ', '# # ', '# #', '# # ', '### '],
|
|
292
|
-
};
|
|
293
|
-
|
|
294
|
-
/** `_logo_lines` — the GENESEED wordmark as 5 text rows. */
|
|
295
|
-
export function logoLines() {
|
|
296
|
-
const ink = TUI_ASCII ? '#' : '█';
|
|
297
|
-
const rows = [];
|
|
298
|
-
for (let r = 0; r < 5; r += 1) {
|
|
299
|
-
rows.push([...'GENESEED'].map((c) => LOGO_FONT[c][r].replaceAll('#', ink)).join(' '));
|
|
300
|
-
}
|
|
301
|
-
return rows;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// ---- layout maths --------------------------------------------------------------------
|
|
305
|
-
|
|
306
|
-
/** `_clamp` — clamp a scroll offset so [top, top+viewH) stays inside `total`. */
|
|
307
|
-
export function clamp(top, total, viewH) {
|
|
308
|
-
return Math.max(0, Math.min(top, Math.max(0, total - viewH)));
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
const BAR_EIGHTHS = ' ▏▎▍▌▋▊▉█';
|
|
312
|
-
|
|
313
|
-
/** `_progress_bar` — a determinate bar exactly `width` display columns wide. */
|
|
314
|
-
export function progressBar(frac, width = 24) {
|
|
315
|
-
const f = Math.max(0.0, Math.min(1.0, frac));
|
|
316
|
-
if (TUI_ASCII) {
|
|
317
|
-
const filled = roundHalfEven(f * width);
|
|
318
|
-
return '#'.repeat(filled) + '-'.repeat(width - filled);
|
|
319
|
-
}
|
|
320
|
-
const eighths = roundHalfEven(f * width * 8);
|
|
321
|
-
const full = Math.floor(eighths / 8);
|
|
322
|
-
const rem = eighths % 8;
|
|
323
|
-
if (full >= width) return '█'.repeat(width);
|
|
324
|
-
return '█'.repeat(full) + [...BAR_EIGHTHS][rem] + ' '.repeat(width - full - 1);
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* `round()` — Python rounds HALF TO EVEN and `Math.round` rounds half UP, and this is the
|
|
329
|
-
* one place in the file where that shows: a bar at exactly 0.5 of a cell is the frontier
|
|
330
|
-
* case the eighths resolution exists to draw, and `round(0.5)` is 0 there and 1 here.
|
|
331
|
-
*/
|
|
332
|
-
function roundHalfEven(x) {
|
|
333
|
-
const f = Math.floor(x);
|
|
334
|
-
const d = x - f;
|
|
335
|
-
if (d > 0.5) return f + 1;
|
|
336
|
-
if (d < 0.5) return f;
|
|
337
|
-
return f % 2 === 0 ? f : f + 1;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// ---- theme reads ---------------------------------------------------------------------
|
|
341
|
-
|
|
342
|
-
/** `_theme_preview` — right-panel preview rows for a theme, as (kind, text) pairs. */
|
|
343
|
-
export function themePreview(key) {
|
|
344
|
-
const data = readJsonMaybe(path.join(THEMES, `${key}.json`));
|
|
345
|
-
if (!data || typeof data !== 'object') return [['dim', '(no preview available)']];
|
|
346
|
-
const lines = [['title', key], ['', '']];
|
|
347
|
-
if (data.TAGLINE) lines.push(['dim', data.TAGLINE], ['', '']);
|
|
348
|
-
if (data.LOADED_SIGIL) lines.push(['ok', data.LOADED_SIGIL], ['', '']);
|
|
349
|
-
if (data.VOICE) lines.push(['', `Voice — ${data.VOICE}`], ['', '']);
|
|
350
|
-
if (data.LEX_SEALED_SECRETS) lines.push(['', `e.g. Rule I — ${data.LEX_SEALED_SECRETS}`]);
|
|
351
|
-
if (data.BENEDICTION) lines.push(['', ''], ['dim', data.BENEDICTION]);
|
|
352
|
-
return lines;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
/** `_theme_flair` — the voice elements the setup chrome speaks in once a theme is chosen. */
|
|
356
|
-
export function themeFlair(theme) {
|
|
357
|
-
const data = readJsonMaybe(path.join(THEMES, `${theme}.json`)) || {};
|
|
358
|
-
const banner = data.BANNER ?? '';
|
|
359
|
-
return {
|
|
360
|
-
accent: data.ACCENT ?? 'cyan',
|
|
361
|
-
tagline: data.TAGLINE ?? '',
|
|
362
|
-
sigil: data.LOADED_SIGIL ?? '',
|
|
363
|
-
// `str.splitlines()` — `js/lib/udiff.mjs`' twin, already gated. It answers `[]` for
|
|
364
|
-
// the empty string where `''.split('\n')` answers `['']`, which would put a blank row
|
|
365
|
-
// in the banner of every theme that omits one; and its boundary set is Python's rather
|
|
366
|
-
// than `\s`, which P6d measured apart.
|
|
367
|
-
banner: splitLines(banner),
|
|
368
|
-
benediction: data.BENEDICTION ?? '',
|
|
369
|
-
};
|
|
370
|
-
}
|
|
371
|
-
|
|
372
265
|
// ---- the inventory's two consumers ----------------------------------------------------
|
|
373
266
|
|
|
374
267
|
/**
|
|
375
268
|
* `_tui_entries` — the ordered (kind, label, data) rows for the left list. `head` is a
|
|
376
269
|
* section divider and is not selectable.
|
|
377
270
|
*
|
|
378
|
-
* ⚠ THE TWO NEW TIERS ARE GUARDED, AND THE GUARD IS NOT DEFENSIVENESS.
|
|
379
|
-
* recorded cases per platform in
|
|
380
|
-
*
|
|
381
|
-
* `
|
|
382
|
-
*
|
|
383
|
-
*
|
|
384
|
-
* gets all three tiers.
|
|
271
|
+
* ⚠ THE TWO NEW TIERS ARE GUARDED, AND THE GUARD IS NOT DEFENSIVENESS. When they landed, this
|
|
272
|
+
* function had two recorded cases per platform in the former Python suite's corpus, and their
|
|
273
|
+
* argument was a synthetic inventory carrying exactly `{agents, skills, laws, theme}`. Reading
|
|
274
|
+
* `inv.doctrines.length` on such an inventory THROWS; emitting a head unconditionally changes
|
|
275
|
+
* the result array. Keyed on presence, an inventory without the tiers renders as it always did
|
|
276
|
+
* and a real inventory gets all three tiers.
|
|
385
277
|
*
|
|
386
278
|
* The order is CONSTITUTIONAL and not alphabetical: ontology, then invariants, then doctrines
|
|
387
279
|
* — the order they are read in, the order AGENT.md renders them, and the order the console's
|
|
@@ -435,12 +327,11 @@ export function tuiEntries(inv) {
|
|
|
435
327
|
* because line 554 moves `sel` onto the first SELECTABLE row — but line 555 is
|
|
436
328
|
* `if not selectable: sel = 0`, and row 0 is always the AGENTS header. So an inventory with
|
|
437
329
|
* no agents, no skills AND no laws crashes the panel on its first frame instead of drawing
|
|
438
|
-
* an empty state. `_TUI_INV_EMPTY` in
|
|
330
|
+
* an empty state. `_TUI_INV_EMPTY` in the former Python parity suite is the corpus
|
|
439
331
|
* entry that surfaced it. **P7c must not reproduce the crash when it ports `_tui_loop`** —
|
|
440
332
|
* the fix is one `or []` at the call site, and it belongs in the same change as the port.
|
|
441
333
|
*
|
|
442
|
-
* Its only consumer with direct assertions is `tests/unit/setup.test.mjs
|
|
443
|
-
* also dispatches it to exercise wizard/web_first test scenarios.
|
|
334
|
+
* Its only consumer with direct assertions is `tests/unit/setup.test.mjs`.
|
|
444
335
|
*/
|
|
445
336
|
export function detailLines(kind, label, data) {
|
|
446
337
|
// The three constitutional tiers share one treatment — the label, a blank, then the body —
|