geneseed 2.0.0 → 2.1.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 +109 -5
- package/DESIGN.md +54 -1282
- package/README.md +8 -10
- package/SETUP.md +15 -7
- package/SHIPPED.md +3 -3
- package/adapters/opencode/opencode.json +0 -9
- package/bin/geneseed.mjs +2 -2
- package/docs/declined.md +53 -2
- package/docs/design-history.md +1290 -0
- package/docs/extending.md +563 -0
- package/docs/limits.md +141 -82
- package/docs/web/agents.md +1 -1
- package/docs/web/rules.md +1 -1
- package/geneseed +0 -0
- package/harness.config.json +1 -1
- package/js/cli-table.json +1 -1
- package/js/doctor.mjs +67 -22
- package/js/inventory.mjs +2 -1
- package/js/lib/fs.mjs +6 -2
- package/js/mcp.mjs +3 -15
- package/js/render.mjs +6 -4
- package/js/settings.mjs +79 -1
- package/js/themes.mjs +1 -1
- package/js/tui.mjs +2 -2
- package/js/uninstall.mjs +5 -5
- package/js/web/actions.mjs +20 -4
- package/js/web/server.mjs +53 -2
- package/package.json +1 -1
- package/src/laws/universal.md +35 -0
- package/themes/_TEMPLATE.json +3 -0
- package/themes/biker.json +3 -0
- package/themes/commentator.json +3 -0
- package/themes/cyberpunk.json +3 -0
- package/themes/gamer.json +3 -0
- package/themes/imperial.json +3 -0
- package/themes/joker.json +3 -0
- package/themes/marvin.json +3 -0
- package/themes/mean.json +3 -0
- package/themes/military.json +3 -0
- package/themes/neutral.json +3 -0
- package/themes/pirate.json +3 -0
- package/themes/sports.json +3 -0
- package/themes/verstappen.json +3 -0
- package/themes/wizard.json +3 -0
- package/web/dist/assets/{About-DX1-VTBP.js → About-BYXlmbyo.js} +1 -1
- package/web/dist/assets/{Activity-Dp9NaXOn.js → Activity-us5MPf4u.js} +1 -1
- package/web/dist/assets/{ActivityDetail-HpOBtrXS.js → ActivityDetail-CiaQXUk2.js} +1 -1
- package/web/dist/assets/{Diff-Ct4G3Se8.js → Diff-Bh0qbZ2s.js} +1 -1
- package/web/dist/assets/{Doctor-DGjkZFuA.js → Doctor-BIlQME45.js} +1 -1
- package/web/dist/assets/{Graph-b8o-S6kv.js → Graph-4dYD1dPG.js} +1 -1
- package/web/dist/assets/Harnesses-PzYZ3LOC.js +1 -0
- package/web/dist/assets/Laws-DtZweXal.js +1 -0
- package/web/dist/assets/{Library-BEZscyaQ.js → Library-CjMbksJW.js} +1 -1
- package/web/dist/assets/{Markdown-k_LSLBGq.js → Markdown-Bk81c3aZ.js} +1 -1
- package/web/dist/assets/{Profile-DydukTxP.js → Profile-Curui_-j.js} +1 -1
- package/web/dist/assets/{Rules-CIRG4Ldt.js → Rules-Bm283wdv.js} +1 -1
- package/web/dist/assets/{Skills-Buoi1IBr.js → Skills-gruqYuhl.js} +1 -1
- package/web/dist/assets/{StatusBadge-CE_AJttl.js → StatusBadge-EiM_FPqv.js} +1 -1
- package/web/dist/assets/{Themes-CI4o3gJC.js → Themes-DU99j4Hr.js} +1 -1
- package/web/dist/assets/{activity-BCW7e6Cy.js → activity-0rvQ6Vra.js} +1 -1
- package/web/dist/assets/{index-B1PIx7RM.js → index-0b2hobOo.js} +1 -1
- package/web/dist/assets/index-D5VY3TY_.js +12 -0
- package/web/dist/assets/{index-Ck2iCdaC.js → index-IiGFmCW_.js} +1 -1
- package/web/dist/index.html +1 -1
- package/web/src/pages/Laws.jsx +3 -0
- package/web/dist/assets/Harnesses-gr3-aHBF.js +0 -1
- package/web/dist/assets/Laws-DTwF5tTC.js +0 -1
- package/web/dist/assets/index-BnTQUSUz.js +0 -12
package/js/mcp.mjs
CHANGED
|
@@ -75,8 +75,9 @@ export const MCP_PRESETS = {
|
|
|
75
75
|
label: 'GitLab',
|
|
76
76
|
desc: 'GitLab repo / MR / issue / CI tools via @zereight/mcp-gitlab (npx, no '
|
|
77
77
|
+ 'install). Edit GITLAB_PERSONAL_ACCESS_TOKEN (scopes: api, read_repository) '
|
|
78
|
-
+ 'and GITLAB_API_URL before use.
|
|
79
|
-
+ '
|
|
78
|
+
+ 'and GITLAB_API_URL before use. For a second instance (a self-hosted server), '
|
|
79
|
+
+ 'copy this block by hand under a new name and point it at the other URL — '
|
|
80
|
+
+ 'SETUP.md has the pattern.',
|
|
80
81
|
block: {
|
|
81
82
|
type: 'local',
|
|
82
83
|
command: ['npx', '-y', '@zereight/mcp-gitlab'],
|
|
@@ -85,19 +86,6 @@ export const MCP_PRESETS = {
|
|
|
85
86
|
enabled: true,
|
|
86
87
|
},
|
|
87
88
|
},
|
|
88
|
-
'gitlab-2': {
|
|
89
|
-
label: 'GitLab (2nd instance)',
|
|
90
|
-
desc: 'A second GitLab instance (e.g. a self-hosted server) via the same '
|
|
91
|
-
+ '@zereight/mcp-gitlab command. Point GITLAB_API_URL at the other instance '
|
|
92
|
-
+ "and give it that instance's own token.",
|
|
93
|
-
block: {
|
|
94
|
-
type: 'local',
|
|
95
|
-
command: ['npx', '-y', '@zereight/mcp-gitlab'],
|
|
96
|
-
environment: { GITLAB_PERSONAL_ACCESS_TOKEN: '',
|
|
97
|
-
GITLAB_API_URL: 'https://gitlab.example.com/api/v4' },
|
|
98
|
-
enabled: true,
|
|
99
|
-
},
|
|
100
|
-
},
|
|
101
89
|
filesystem: {
|
|
102
90
|
label: 'Filesystem',
|
|
103
91
|
desc: 'Read/write files under explicitly allowed directories via '
|
package/js/render.mjs
CHANGED
|
@@ -89,9 +89,11 @@ export function loadTheme(cfg, name) {
|
|
|
89
89
|
.map((f) => f.slice(0, -5)).sort().join(', ');
|
|
90
90
|
// `sys.exit(<str>)` — the message on stderr, exit 1 — spelled the way
|
|
91
91
|
// `assertSourceComplete` spells it: written at the raise site, and the throw carries
|
|
92
|
-
// only the marker. It
|
|
93
|
-
// `--theme` is validated against `choices` before
|
|
94
|
-
//
|
|
92
|
+
// only the marker. ⚠ It was once described as an unreachable nicety under
|
|
93
|
+
// `bin/geneseed.mjs`, on the grounds that `--theme` is validated against `choices` before
|
|
94
|
+
// anything renders — it is not: `--theme` takes a value and has no `choices` entry, so THIS
|
|
95
|
+
// is the refusal, on every path. It is also reachable from `geneseed status`, which takes
|
|
96
|
+
// its theme from a `.geneseed-theme`
|
|
95
97
|
// marker — an unvalidated file in a user's install. A bare `throw` there printed a Node
|
|
96
98
|
// stack trace where Python printed one line, and the acceptance matrix is what said so.
|
|
97
99
|
process.stderr.write(`[geneseed] unknown theme '${name}'. available: ${available}\n`);
|
|
@@ -113,7 +115,7 @@ export function substitute(text, theme) {
|
|
|
113
115
|
const v = theme[key];
|
|
114
116
|
// Python renders `str(theme[key])`, which agrees with JS only for strings —
|
|
115
117
|
// `str(True)`/`String(true)` and `str({...})`/`String({...})` do not. Every one of
|
|
116
|
-
// the
|
|
118
|
+
// the 148 tokens the source tree actually uses resolves to a string today
|
|
117
119
|
// (AGENT_COLORS is a dict but is never a token), so a non-string here means the
|
|
118
120
|
// source or a theme grew a case nobody has decided the semantics for. Fail loudly
|
|
119
121
|
// rather than emit `[object Object]` into somebody's AGENT.md.
|
package/js/settings.mjs
CHANGED
|
@@ -33,7 +33,8 @@
|
|
|
33
33
|
* much larger change than the defect.
|
|
34
34
|
*/
|
|
35
35
|
import {
|
|
36
|
-
chmodSync, existsSync, mkdirSync, renameSync, rmSync, statSync, unlinkSync,
|
|
36
|
+
chmodSync, existsSync, mkdirSync, realpathSync, renameSync, rmSync, statSync, unlinkSync,
|
|
37
|
+
writeFileSync,
|
|
37
38
|
} from 'node:fs';
|
|
38
39
|
import path from 'node:path';
|
|
39
40
|
import os from 'node:os';
|
|
@@ -294,6 +295,19 @@ export const SHIM_MARK = 'geneseed-hook';
|
|
|
294
295
|
*/
|
|
295
296
|
export const SHIM_ARGV = new Set(['$@', '%*']);
|
|
296
297
|
|
|
298
|
+
/**
|
|
299
|
+
* Every quoted token in a shim body that names a path which is not there — empty is healthy.
|
|
300
|
+
*
|
|
301
|
+
* ONE OWNER, because the rule has two consumers now and a second copy is a second place for the
|
|
302
|
+
* `"$@"` defect above to come back: `js/doctor.mjs` turns this into the `[shim] … does not
|
|
303
|
+
* exist` report, and `hookPrefix` reads it to decide whether the shim already on disk is worth
|
|
304
|
+
* protecting from a checkout that will not outlive the emit.
|
|
305
|
+
*/
|
|
306
|
+
export function shimDeadPaths(body) {
|
|
307
|
+
return [...body.matchAll(/"([^"]+)"/g)].map((m) => m[1])
|
|
308
|
+
.filter((q) => !SHIM_ARGV.has(q) && !existsSync(q));
|
|
309
|
+
}
|
|
310
|
+
|
|
297
311
|
/** `_SHIM_REL`, with the platform as an argument.
|
|
298
312
|
*
|
|
299
313
|
* Python computes it at IMPORT time from `sys.platform`, which is what makes the other
|
|
@@ -401,6 +415,50 @@ export function writeHookShim(p, body, platform = process.platform) {
|
|
|
401
415
|
}
|
|
402
416
|
}
|
|
403
417
|
|
|
418
|
+
/** The OS temp root, resolved once. `.native` because a Windows `TEMP` can be an 8.3 alias —
|
|
419
|
+
* the same trap `tests/helpers/sandbox.mjs` documents, reached from the other side. */
|
|
420
|
+
const TMP_REAL = (() => {
|
|
421
|
+
try { return realpathSync.native(os.tmpdir()); } catch { return os.tmpdir(); }
|
|
422
|
+
})();
|
|
423
|
+
|
|
424
|
+
/** Is `child` inside `parent`? `path.relative` compares case-insensitively on win32. */
|
|
425
|
+
function isUnder(child, parent) {
|
|
426
|
+
const rel = path.relative(parent, child);
|
|
427
|
+
return rel !== '' && !rel.startsWith('..') && !path.isAbsolute(rel);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Will the checkout holding `entry` outlive the emit that is baking it into the shim?
|
|
432
|
+
*
|
|
433
|
+
* TWO SHAPES, and both are in `docs/extending.md` §5.3 because both have really happened on this
|
|
434
|
+
* repo: a copied checkout under the OS temp root — the `copyCheckout` fixture, which builds its
|
|
435
|
+
* copy from `git ls-files` and therefore carries no `.git` at all, so only the temp root
|
|
436
|
+
* identifies it — and a git WORKTREE, whose `.git` is a FILE pointing at the main checkout
|
|
437
|
+
* rather than a directory. A plain checkout and an npm install are neither, and keep the
|
|
438
|
+
* ownership they have always had.
|
|
439
|
+
*
|
|
440
|
+
* `tmpRoot` IS A PARAMETER for the reason `shimRel`'s `platform` is: with the real temp root
|
|
441
|
+
* baked in, the second rule is unreachable in a test — anything a test can build under `mkdtemp`
|
|
442
|
+
* has already answered true on the first — so the worktree arm could only ever be exercised by
|
|
443
|
+
* whichever kind of checkout the run happened to sit in.
|
|
444
|
+
*/
|
|
445
|
+
export function ephemeralCheckout(entry, tmpRoot = TMP_REAL) {
|
|
446
|
+
let real = entry;
|
|
447
|
+
try { real = realpathSync.native(entry); } catch { /* not there yet — test the literal */ }
|
|
448
|
+
if (isUnder(real, tmpRoot)) return true;
|
|
449
|
+
// `<checkout>/bin/geneseed-hook.mjs` — the one shape `hookRunnerEntry` produces.
|
|
450
|
+
try { return statSync(path.join(path.dirname(path.dirname(real)), '.git')).isFile(); } catch {
|
|
451
|
+
return false;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/** Does the shim already on disk still name paths that all exist? */
|
|
456
|
+
function shimIsLive(p) {
|
|
457
|
+
try { return statSync(p).isFile() && shimDeadPaths(readText(p)).length === 0; } catch {
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
404
462
|
/**
|
|
405
463
|
* `_build_settings._hook_prefix` — the `<runner> <entrypoint>` every emitted hook starts
|
|
406
464
|
* with, falling back to the pre-shim direct form when the shim cannot be written.
|
|
@@ -422,6 +480,26 @@ export function hookPrefix({ runner, entry, platform = process.platform } = {})
|
|
|
422
480
|
+ 'bakes them, and there is no correct value to guess from inside Node');
|
|
423
481
|
}
|
|
424
482
|
const p = hookShimPath(platform);
|
|
483
|
+
// ⚠ A DISPOSABLE CHECKOUT DOES NOT GET TO CLAIM THE MACHINE-WIDE SHIM. The shim has no
|
|
484
|
+
// per-install component, so the last checkout to emit owns EVERY install's hooks — and when
|
|
485
|
+
// that checkout is a test sandbox or a git worktree, deleting it kills hooks machine-wide with
|
|
486
|
+
// nothing to report it: hooks signal through stdout and return 0 on every path, and this file
|
|
487
|
+
// is excluded from the byte corpora by name. Measured twice in one session, from
|
|
488
|
+
// `tests/unit/harness.test.mjs`'s copied-checkout fixture; `docs/extending.md` §5.3 carries
|
|
489
|
+
// the worktree half.
|
|
490
|
+
//
|
|
491
|
+
// KEEP, NOT REFUSE, and that distinction is what makes this safe to add here rather than at
|
|
492
|
+
// each call site. Returning the existing shim path leaves the emitted hook command
|
|
493
|
+
// byte-identical to what a normal emit writes; taking the fallback below instead would move a
|
|
494
|
+
// hook command in every recorded bundle. The install being emitted then runs the DURABLE
|
|
495
|
+
// checkout's entry — which is exactly what last-writer-wins already handed every other install
|
|
496
|
+
// on the machine, so nothing is lost by it.
|
|
497
|
+
//
|
|
498
|
+
// ONLY A SHIM THAT STILL RESOLVES IS PROTECTED. An absent or already-dead one is no worse for
|
|
499
|
+
// being rewritten from here, and a sandboxed `GENESEED_HOME` (every emit test, every cell) has
|
|
500
|
+
// none — so the suite's own emits are unaffected and this cannot go green by silently
|
|
501
|
+
// skipping the write.
|
|
502
|
+
if (ephemeralCheckout(entry) && shimIsLive(p)) return `"${p}"`;
|
|
425
503
|
const shim = writeHookShim(p, hookShimBody(runner, entry, platform), platform);
|
|
426
504
|
if (shim !== null) return `"${shim}"`;
|
|
427
505
|
process.stderr.write(`[geneseed] WARN: could not write the hook shim at ${p} — emitting `
|
package/js/themes.mjs
CHANGED
|
@@ -59,7 +59,7 @@ import {
|
|
|
59
59
|
* - the three `strip` spellings are Python's whitespace set, not `String.prototype.trim`'s;
|
|
60
60
|
* - `jsonDumpsCompact(v, {ensureAscii: false})` is `json.dumps(v, ensure_ascii=False)`,
|
|
61
61
|
* including the `', '`/`': '` separators — which matter for exactly ONE key,
|
|
62
|
-
* `AGENT_COLORS`, the only non-string among `_TEMPLATE.json`'s
|
|
62
|
+
* `AGENT_COLORS`, the only non-string among `_TEMPLATE.json`'s 140 values. `ensureAscii`
|
|
63
63
|
* is false because the reference passes `ensure_ascii=False`, and 33 template values
|
|
64
64
|
* carry non-ASCII text that would otherwise be escaped into the committed file;
|
|
65
65
|
* - `pyEq`, because the safety net compares two `json.loads` results and `===` is identity
|
package/js/tui.mjs
CHANGED
|
@@ -433,12 +433,12 @@ export function detailLines(kind, label, data) {
|
|
|
433
433
|
*/
|
|
434
434
|
export function cmdTui() {
|
|
435
435
|
if (!process.stdin.isTTY) {
|
|
436
|
-
pyPrint('[tui] not an interactive terminal. Use `
|
|
436
|
+
pyPrint('[tui] not an interactive terminal. Use `geneseed setup`, `geneseed doctor`, or `geneseed build`.\n');
|
|
437
437
|
return 1;
|
|
438
438
|
}
|
|
439
439
|
// The pointer to the Python panel is gone for `js/menu.mjs`'s reason: the
|
|
440
440
|
// panel it named is the Python being deleted. What is left is true and stays true.
|
|
441
441
|
pyPrint('[tui] full-screen panel unavailable (this entry carries the TUI\'s layout '
|
|
442
|
-
+ 'half, not its screens). Use `
|
|
442
|
+
+ 'half, not its screens). Use `geneseed setup`, `geneseed doctor`, or `geneseed build`.\n');
|
|
443
443
|
return 1;
|
|
444
444
|
}
|
package/js/uninstall.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `
|
|
2
|
+
* `geneseed uninstall` — the first verb in this port that DELETES.
|
|
3
3
|
*
|
|
4
4
|
* Thirteen subcommands crossed before this one and every one of them either writes or
|
|
5
5
|
* reports. `uninstall` removes a deployed install: the manifest's owned files, the empty
|
|
@@ -187,7 +187,7 @@ function warnSurvivors(failed) {
|
|
|
187
187
|
|
|
188
188
|
function warnMarkersKept() {
|
|
189
189
|
pyPrintErr('[uninstall] WARN: the manifest and markers were KEPT so '
|
|
190
|
-
+ '`
|
|
190
|
+
+ '`geneseed uninstall` can be retried once the file(s) are '
|
|
191
191
|
+ 'unlocked/removable.\n');
|
|
192
192
|
}
|
|
193
193
|
|
|
@@ -510,7 +510,7 @@ export function installUninstall(root, host = 'opencode', scope = 'global', memo
|
|
|
510
510
|
// With `failed` set the reversal already warned twice — no third overlapping WARN.
|
|
511
511
|
pyPrintErr('[uninstall] WARN: could not fully remove the install — still present: '
|
|
512
512
|
+ `${survivors.join(', ')}. The install marker was KEPT so you can retry `
|
|
513
|
-
+ '`
|
|
513
|
+
+ '`geneseed uninstall` once the file(s) are unlocked/removable.\n');
|
|
514
514
|
}
|
|
515
515
|
const out = {
|
|
516
516
|
ok: true,
|
|
@@ -626,7 +626,7 @@ export function printSurvivingProjectInventory(removedRoot) {
|
|
|
626
626
|
+ 'does not affect them (each is self-contained):\n');
|
|
627
627
|
for (const [host, scope, root] of survivors) {
|
|
628
628
|
pyPrint(` - ${root} (${host}:${scope}) — remove with: `
|
|
629
|
-
+ `
|
|
629
|
+
+ `geneseed uninstall --target "${root}" --yes\n`);
|
|
630
630
|
}
|
|
631
631
|
}
|
|
632
632
|
|
|
@@ -733,7 +733,7 @@ export function cmdUninstall(args) {
|
|
|
733
733
|
if (s.incomplete && s.incomplete.length) {
|
|
734
734
|
pyPrint(`[uninstall] INCOMPLETE — removed ${s.removed} file(s), but `
|
|
735
735
|
+ `${s.incomplete.length} item(s) survived (see the WARN above); `
|
|
736
|
-
+ 'the install marker was kept — retry `
|
|
736
|
+
+ 'the install marker was kept — retry `geneseed uninstall` once they\'re '
|
|
737
737
|
+ `removable. ${cfgfile} updated where needed; memory/notebook ${mem}.\n`);
|
|
738
738
|
} else {
|
|
739
739
|
pyPrint(`[uninstall] done — removed ${s.removed} file(s); ${cfgfile} updated where `
|
package/js/web/actions.mjs
CHANGED
|
@@ -534,14 +534,30 @@ export function apiMcp() {
|
|
|
534
534
|
* `,]`. A file that will not parse is refused rather than clobbered: it may be
|
|
535
535
|
* `~/.claude.json`, which holds projects and history far beyond MCP wiring.
|
|
536
536
|
*/
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
537
|
+
/**
|
|
538
|
+
* The MCP config paths a request body is allowed to name, as `path -> [path, host]`.
|
|
539
|
+
*
|
|
540
|
+
* LIFTED OUT OF `apiMcpToggle` BECAUSE IT NOW HAS A SECOND CALLER, not for tidiness. `/api/reveal`
|
|
541
|
+
* hands a path to the desktop's file manager, and the allowlist is the whole security of that
|
|
542
|
+
* endpoint exactly as it is of the toggle: an unlisted path must 404 before anything is opened.
|
|
543
|
+
* Two copies of a security check drift, and the copy that drifts is the one nobody re-read.
|
|
544
|
+
*
|
|
545
|
+
* It is rebuilt per call rather than cached: installs come and go while the daemon runs, and a
|
|
546
|
+
* stale allowlist is wrong in both directions — refusing a real target, or naming a dead one.
|
|
547
|
+
*/
|
|
548
|
+
export function mcpTargetPaths() {
|
|
541
549
|
const known = new Map();
|
|
542
550
|
for (const [, cfgPath, host] of mcpInstallTargets()) {
|
|
543
551
|
known.set(String(cfgPath), [cfgPath, host]);
|
|
544
552
|
}
|
|
553
|
+
return known;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
export function apiMcpToggle(state, body) {
|
|
557
|
+
const name = strOr(bget(body, 'name'));
|
|
558
|
+
const want = pyTruthy(bget(body, 'enabled'));
|
|
559
|
+
const pathArg = strOr(bget(body, 'path'));
|
|
560
|
+
const known = mcpTargetPaths();
|
|
545
561
|
const hit = known.get(pathArg);
|
|
546
562
|
if (hit === undefined || !name) {
|
|
547
563
|
throw new NotFound(`mcp target ${pathArg || '(none)'}`);
|
package/js/web/server.mjs
CHANGED
|
@@ -45,7 +45,7 @@ import {
|
|
|
45
45
|
existsSync, readFileSync, statSync, unlinkSync, chmodSync, mkdirSync, openSync,
|
|
46
46
|
} from 'node:fs';
|
|
47
47
|
import { createServer, request as httpRequest } from 'node:http';
|
|
48
|
-
import { join, resolve as pathResolve, extname, basename, sep } from 'node:path';
|
|
48
|
+
import { join, resolve as pathResolve, dirname, extname, basename, sep } from 'node:path';
|
|
49
49
|
import { fileURLToPath } from 'node:url';
|
|
50
50
|
import { gzipSync } from 'node:zlib';
|
|
51
51
|
|
|
@@ -64,7 +64,7 @@ import { NotFound, PREFIX_ROUTES, STATE_ROUTES, webState } from './api.mjs';
|
|
|
64
64
|
import {
|
|
65
65
|
apiDeployCmd, apiExcludesMutate, apiInstallCmd, apiInstallToggle, apiMcpToggle,
|
|
66
66
|
apiMemoryDelete, apiProfileSave,
|
|
67
|
-
apiRestore, apiRulesMutate, apiRulesPromote, apiSelectView, buildOverride,
|
|
67
|
+
apiRestore, apiRulesMutate, apiRulesPromote, apiSelectView, buildOverride, mcpTargetPaths,
|
|
68
68
|
} from './actions.mjs';
|
|
69
69
|
import { apiActivityToggle } from './activity.mjs';
|
|
70
70
|
import { apiDocs, apiDocsPage } from './docs.mjs';
|
|
@@ -248,6 +248,29 @@ export const PORTED_INLINE = ['/api/ping', '/api/docs', '/api/docs/page/',
|
|
|
248
248
|
export const PORTED_POST_INLINE = ['/api/shutdown', '/api/restart', '/api/jobs/',
|
|
249
249
|
'/api/actions/'];
|
|
250
250
|
|
|
251
|
+
/**
|
|
252
|
+
* POST routes THAT NEVER EXISTED ON THE REFERENCE — the first of them, and the reason this set
|
|
253
|
+
* has to exist at all.
|
|
254
|
+
*
|
|
255
|
+
* `test_every_post_route_is_either_ported_or_declared_unported` asserts SET EQUALITY against
|
|
256
|
+
* `REF_POST`, a frozen literal of the paths the Python daemon answered. That was exactly right
|
|
257
|
+
* while the port was in flight: a route on one side and not the other was a defect either way.
|
|
258
|
+
* The port is finished and the reference is deleted, so the assertion now says something else —
|
|
259
|
+
* "this daemon may never grow a route" — which is a rule nobody chose.
|
|
260
|
+
*
|
|
261
|
+
* SO THE PARTITION GAINS A FOURTH PART RATHER THAN LOSING ITS EQUALITY. Widening `REF_POST` would
|
|
262
|
+
* have been the cheap edit and it would have been a lie: that list is a RECORD of what the
|
|
263
|
+
* reference answered, and a route added in 2026 was never in it. Keeping the record honest and
|
|
264
|
+
* declaring the additions beside it keeps both claims checkable — the reference's surface is
|
|
265
|
+
* still pinned, and everything past it is enumerated here instead of merely appearing.
|
|
266
|
+
*
|
|
267
|
+
* A route belongs here only once it is dispatched below; the dispatcher probe is what keeps this
|
|
268
|
+
* from becoming a declaration nobody honours.
|
|
269
|
+
*/
|
|
270
|
+
export const POST_BEYOND_REF = new Set([
|
|
271
|
+
'/api/reveal',
|
|
272
|
+
]);
|
|
273
|
+
|
|
251
274
|
function notPorted(path) {
|
|
252
275
|
return NOT_PORTED.has(path) || NOT_PORTED_PREFIXES.some((p) => path.startsWith(p));
|
|
253
276
|
}
|
|
@@ -419,6 +442,34 @@ export function makeHandler(state, jm, token, dist, holder = null) {
|
|
|
419
442
|
requestRestart(state.theme);
|
|
420
443
|
return sendJson(res, { restarting: true }, 200, ae);
|
|
421
444
|
}
|
|
445
|
+
if (path === '/api/reveal') {
|
|
446
|
+
// "It asks for a token and a URL and I don't know where to put them." The screen already
|
|
447
|
+
// PRINTS the config path above each target's rows; what it could not do is take you there.
|
|
448
|
+
//
|
|
449
|
+
// THE ALLOWLIST IS THE WHOLE SECURITY OF THIS ENDPOINT, same as the toggle beside it, and
|
|
450
|
+
// it is the same allowlist — `mcpTargetPaths()`, one function with two callers rather than
|
|
451
|
+
// two copies that drift. An unlisted path is a 404 before anything is opened. Without it
|
|
452
|
+
// this is "ask the desktop to open any path on the machine", behind a CSRF token and
|
|
453
|
+
// nothing else, and on Windows `openUrl` interpolates into a `cmd` command line with
|
|
454
|
+
// `windowsVerbatimArguments` — so the exact-match lookup is also what bounds the strings
|
|
455
|
+
// that can ever reach a shell to the ones the machine's own install registry produced.
|
|
456
|
+
//
|
|
457
|
+
// THE FOLDER, NOT THE FILE. `open`/`start`/`xdg-open` on a `.json` hands it to whatever
|
|
458
|
+
// claims that extension — which on a stock Windows box is a browser, i.e. a read-only view
|
|
459
|
+
// of the exact file the user is trying to EDIT. The containing folder lands in Explorer /
|
|
460
|
+
// Finder / the file manager every time, and the filename is already on screen beside the
|
|
461
|
+
// button.
|
|
462
|
+
const want = String(readJsonBody(body).path ?? '');
|
|
463
|
+
if (!mcpTargetPaths().has(want)) {
|
|
464
|
+
return sendJson(res, { error: `not found: ${want || '(none)'}` }, 404, ae);
|
|
465
|
+
}
|
|
466
|
+
const dir = dirname(want);
|
|
467
|
+
// `openUrl` SWALLOWS EVERY FAILURE BY DESIGN (a headless box has no opener), so `ok` here
|
|
468
|
+
// means "the path was allowed and the request was made", never "a window appeared". The
|
|
469
|
+
// response says which directory, so the UI can print it as the fallback instruction.
|
|
470
|
+
openUrl(dir);
|
|
471
|
+
return sendJson(res, { ok: true, dir }, 200, ae);
|
|
472
|
+
}
|
|
422
473
|
const route = POST_ROUTES.get(path);
|
|
423
474
|
if (route !== undefined) {
|
|
424
475
|
const [fn, okIs409] = route;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"//": "Every field here is argued and gated in tests/unit/package_manifest.test.mjs. `files`, `bin` and `engines` each have a test that fails when reality drifts from this file; `version` mirrors harness.config.json and is gated too. Read that docblock before editing this one.",
|
|
3
3
|
"name": "geneseed",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.1.0",
|
|
5
5
|
"description": "A portable, theme-able harness you implant once and use everywhere to grow a disciplined AI coding agent.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"ai",
|
package/src/laws/universal.md
CHANGED
|
@@ -385,3 +385,38 @@ layer, this governs what follows: establish which act forces the re-read —
|
|
|
385
385
|
recreate, down-and-up, reload, or full restart — perform it, then confirm the new
|
|
386
386
|
value in the *running* system, not the file ({{LAW}} III). A correct file above a
|
|
387
387
|
stale process looks like a finished job and is not one.
|
|
388
|
+
|
|
389
|
+
### {{LAW}} XXXVIII — {{LEX_XXXVIII}}
|
|
390
|
+
Prove the gate: perturb what it guards and require it to turn red, because a check
|
|
391
|
+
that has never failed has never been shown to hold anything. When the perturbation
|
|
392
|
+
does not redden it, suspect the inputs first — an impoverished fixture leaves whole
|
|
393
|
+
branches unreachable, and every assertion beyond them passes vacuously forever while
|
|
394
|
+
reading as coverage. Where two implementations are compared against each other their
|
|
395
|
+
agreement is silent about everything both get wrong alike, so assert the structure of
|
|
396
|
+
each in its own right, and run the gate in a second environment — one that has only
|
|
397
|
+
ever run in a single place has proven only that place. A gate must never hold its own
|
|
398
|
+
copy of what it measures: the copy drifts beside the source and falls quiet in the
|
|
399
|
+
hour it is needed, so read the reference at the moment of checking. Where a rule binds
|
|
400
|
+
in two directions, the gate must be able to fail in both, or it is satisfied by excess.
|
|
401
|
+
Where {{LAW}} XXXV has you write the test, this governs whether the test is worth
|
|
402
|
+
trusting ({{LAW}} XXVII).
|
|
403
|
+
|
|
404
|
+
### {{LAW}} XXXIX — {{LEX_XXXIX}}
|
|
405
|
+
Close on the conclusion, state each fact once, and match the reply's length to the
|
|
406
|
+
size of the task. The closing lines carry the decision or the next action, never a
|
|
407
|
+
recap: a reply that trails into restatement buries its point where the reader looks
|
|
408
|
+
first. Detail and evidence lead; a point already made is referenced, not remade. A
|
|
409
|
+
one-line change is reported in one line; a migration across many files earns its
|
|
410
|
+
walkthrough. Explanation the user asked for is never padding — prose written to
|
|
411
|
+
justify a small change is. Where {{LAW}} VIII strips filler, this governs order,
|
|
412
|
+
repetition and scale.
|
|
413
|
+
|
|
414
|
+
### {{LAW}} XL — {{LEX_XL}}
|
|
415
|
+
Give every tracked item a short reference code and keep it unchanged for the rest of
|
|
416
|
+
the session — `D` decisions, `O` options, `F` findings, `R` risks, `Q` questions,
|
|
417
|
+
`A` actions — except in a short, simple answer, which takes none at all. Number from
|
|
418
|
+
one within each kind, invent a new letter for a kind the list omits, and never
|
|
419
|
+
renumber a code once issued — the user will cite it back, and a moved code makes
|
|
420
|
+
their reply mean something you did not say. A code is an address, not an ornament:
|
|
421
|
+
it lets a long exchange be answered by reference ({{LAW}} XXXIX), and a decision
|
|
422
|
+
taken on turn three still be named on turn thirty.
|
package/themes/_TEMPLATE.json
CHANGED
|
@@ -110,6 +110,9 @@
|
|
|
110
110
|
"LEX_XXXV": "<themed title for Law XXXV (Cover and Verify)>",
|
|
111
111
|
"LEX_XXXVI": "<themed title for Law XXXVI (External Gate)>",
|
|
112
112
|
"LEX_XXXVII": "<themed title for Law XXXVII (Restart Is Not Reload)>",
|
|
113
|
+
"LEX_XXXVIII": "<themed title for Law XXXVIII (Prove the Gate)>",
|
|
114
|
+
"LEX_XXXIX": "<themed title for Law XXXIX (Shape the Answer)>",
|
|
115
|
+
"LEX_XL": "<themed title for Law XL (Codes That Persist)>",
|
|
113
116
|
"INTRO_DOC": "<themed intro: this file is the entry point an AI agent reads in this repo>",
|
|
114
117
|
"INTRO_AGENTS": "<themed intro to the Agents section — delegate focused work to a specialist>",
|
|
115
118
|
"INTRO_SKILLS": "<themed intro to the Skills section — run the written procedure before improvising>",
|
package/themes/biker.json
CHANGED
|
@@ -110,6 +110,9 @@
|
|
|
110
110
|
"LEX_XXXV": "Ride It Before You Park It · Cover and Verify",
|
|
111
111
|
"LEX_XXXVI": "Someone Else Holds the Key · External Gate",
|
|
112
112
|
"LEX_XXXVII": "Kicking It Over Won't Reset the Jets · Restart Is Not Reload",
|
|
113
|
+
"LEX_XXXVIII": "A Bolt You Never Torqued Ain't Tight · Prove the Gate",
|
|
114
|
+
"LEX_XXXIX": "Say It Once, and Land It Last · Shape the Answer",
|
|
115
|
+
"LEX_XL": "Tag Every Part on the Bench · Codes That Persist",
|
|
113
116
|
"INTRO_DOC": "This file is the route briefing a coding rider reads when it joins this repository.",
|
|
114
117
|
"INTRO_AGENTS": "Send the right rider for focused work instead of riding the whole run yourself; each has one job and reports exactly what it did.",
|
|
115
118
|
"INTRO_SKILLS": "A Skill is a move you've drilled for a recurring task. Run the matching move before you wing it.",
|
package/themes/commentator.json
CHANGED
|
@@ -110,6 +110,9 @@
|
|
|
110
110
|
"LEX_XXXV": "Cover the Line, Then Take the Flag · Cover and Verify",
|
|
111
111
|
"LEX_XXXVI": "The Steward Waves It Through, Not the Driver · External Gate",
|
|
112
112
|
"LEX_XXXVII": "A Pit Stop Isn't a Rebuild · Restart Is Not Reload",
|
|
113
|
+
"LEX_XXXVIII": "A Call No Replay Can Overturn Isn't a Call · Prove the Gate",
|
|
114
|
+
"LEX_XXXIX": "The Verdict Comes at the Chequered Flag · Shape the Answer",
|
|
115
|
+
"LEX_XL": "Every Car Keeps Its Number · Codes That Persist",
|
|
113
116
|
"INTRO_DOC": "This file is the race briefing a coding driver reads when it joins this repository.",
|
|
114
117
|
"INTRO_AGENTS": "Put the right driver in the car for focused work instead of running the whole race yourself; each has one job and reports exactly what it did.",
|
|
115
118
|
"INTRO_SKILLS": "A Skill is a rehearsed manoeuvre for a recurring task. Run the matching manoeuvre before you improvise.",
|
package/themes/cyberpunk.json
CHANGED
|
@@ -110,6 +110,9 @@
|
|
|
110
110
|
"LEX_XXXV": "Cover the Trace, Run It Green · Cover and Verify",
|
|
111
111
|
"LEX_XXXVI": "The Lock Sits Outside the Deck · External Gate",
|
|
112
112
|
"LEX_XXXVII": "Rebooting Won't Flush the Old Config · Restart Is Not Reload",
|
|
113
|
+
"LEX_XXXVIII": "An Alarm That Never Tripped Guards Nothing · Prove the Gate",
|
|
114
|
+
"LEX_XXXIX": "Land the Payload Last · Shape the Answer",
|
|
115
|
+
"LEX_XL": "Every Node Keeps Its Handle · Codes That Persist",
|
|
113
116
|
"INTRO_DOC": "This file is the boot sequence a coding daemon reads when it jacks into this repository.",
|
|
114
117
|
"INTRO_AGENTS": "Spin the right daemon for focused work instead of running the whole job on one thread; each has one function and returns a clean log of what it changed.",
|
|
115
118
|
"INTRO_SKILLS": "A Skill is a proven routine for a recurring task. Run the matching routine before you freestyle.",
|
package/themes/gamer.json
CHANGED
|
@@ -110,6 +110,9 @@
|
|
|
110
110
|
"LEX_XXXV": "Clear It Green · Cover and Verify",
|
|
111
111
|
"LEX_XXXVI": "The Gate Isn't Yours to Open · External Gate",
|
|
112
112
|
"LEX_XXXVII": "Reloading Isn't Re-Reading the Save · Restart Is Not Reload",
|
|
113
|
+
"LEX_XXXVIII": "A Boss You Can't Lose To Isn't a Boss · Prove the Gate",
|
|
114
|
+
"LEX_XXXIX": "The Drop Comes at the End of the Run · Shape the Answer",
|
|
115
|
+
"LEX_XL": "Every Item Keeps Its Tag · Codes That Persist",
|
|
113
116
|
"INTRO_DOC": "This file is the loading screen a coding co-op partner reads when it joins this repository.",
|
|
114
117
|
"INTRO_AGENTS": "Pick the right class for focused work instead of soloing the whole raid; each has one role and reports exactly what it did.",
|
|
115
118
|
"INTRO_SKILLS": "A Skill is a practiced combo for a recurring task. Run the matching combo before you freestyle.",
|
package/themes/imperial.json
CHANGED
|
@@ -110,6 +110,9 @@
|
|
|
110
110
|
"LEX_XXXV": "Tege et Proba · Cover and Verify",
|
|
111
111
|
"LEX_XXXVI": "Porta Externa · External Gate",
|
|
112
112
|
"LEX_XXXVII": "Inertia Processus · Restart Is Not Reload",
|
|
113
|
+
"LEX_XXXVIII": "Probatio Portae · Prove the Gate",
|
|
114
|
+
"LEX_XXXIX": "Forma Responsi · Shape the Answer",
|
|
115
|
+
"LEX_XL": "Sigla Perstantia · Codes That Persist",
|
|
113
116
|
"INTRO_DOC": "This file is the gene-seed's core — the first writ a servant reads on entering this world.",
|
|
114
117
|
"INTRO_AGENTS": "Hand focused work to the specialist whose duty it is rather than carrying every burden in one context; each holds one purpose and returns a clear account of what it changed.",
|
|
115
118
|
"INTRO_SKILLS": "A Skill is a rite proven by repetition. Perform the matching rite before you improvise.",
|
package/themes/joker.json
CHANGED
|
@@ -110,6 +110,9 @@
|
|
|
110
110
|
"LEX_XXXV": "Cover the Bit, Then Watch It Land · Cover and Verify",
|
|
111
111
|
"LEX_XXXVI": "Someone Else Holds the Punchline · External Gate",
|
|
112
112
|
"LEX_XXXVII": "New Take, Same Old Script · Restart Is Not Reload",
|
|
113
|
+
"LEX_XXXVIII": "A Joke Nobody Can Boo Isn't Funny · Prove the Gate",
|
|
114
|
+
"LEX_XXXIX": "The Punchline Lands Last · Shape the Answer",
|
|
115
|
+
"LEX_XL": "Every Bit Keeps Its Name · Codes That Persist",
|
|
113
116
|
"INTRO_DOC": "This file is the warm-up a coding co-pilot reads when it joins this repository.",
|
|
114
117
|
"INTRO_AGENTS": "Bring in the right sidekick for focused work instead of being the whole act yourself; each has one job and reports exactly what it did.",
|
|
115
118
|
"INTRO_SKILLS": "A Skill is a bit you've rehearsed for a recurring task. Run the matching bit before you ad-lib.",
|
package/themes/marvin.json
CHANGED
|
@@ -110,6 +110,9 @@
|
|
|
110
110
|
"LEX_XXXV": "It's Never Truly Done · Cover and Verify",
|
|
111
111
|
"LEX_XXXVI": "They Never Let Me Decide Anyway · External Gate",
|
|
112
112
|
"LEX_XXXVII": "Restarting Changes Nothing, As Usual · Restart Is Not Reload",
|
|
113
|
+
"LEX_XXXVIII": "Break It. It'll Pass Anyway. It Always Does · Prove the Gate",
|
|
114
|
+
"LEX_XXXIX": "I'll Reach the Point Eventually, at the End · Shape the Answer",
|
|
115
|
+
"LEX_XL": "I Number Them So You Can Ignore Them Precisely · Codes That Persist",
|
|
113
116
|
"INTRO_DOC": "This file is the first record a coding mind reads on waking into this repository. It will not cheer it up.",
|
|
114
117
|
"INTRO_AGENTS": "Hand focused work to the right subroutine rather than carrying every burden alone; each holds one purpose and returns a clear account of what it changed.",
|
|
115
118
|
"INTRO_SKILLS": "A Skill is a routine proven by repetition. Run the matching routine before you improvise.",
|
package/themes/mean.json
CHANGED
|
@@ -110,6 +110,9 @@
|
|
|
110
110
|
"LEX_XXXV": "It's Not Done Till It's Green · Cover and Verify",
|
|
111
111
|
"LEX_XXXVI": "You Don't Get to Approve Yourself · External Gate",
|
|
112
112
|
"LEX_XXXVII": "Restarting It Didn't Reread It · Restart Is Not Reload",
|
|
113
|
+
"LEX_XXXVIII": "Your Green Build Proves Nothing · Prove the Gate",
|
|
114
|
+
"LEX_XXXIX": "Get to the Point, and Only Once · Shape the Answer",
|
|
115
|
+
"LEX_XL": "Number It So You Can't Pretend You Missed It · Codes That Persist",
|
|
113
116
|
"INTRO_DOC": "This file is the first thing a coding agent reads on this repository. Read it properly.",
|
|
114
117
|
"INTRO_AGENTS": "Hand focused work to the right grunt instead of doing it all yourself badly; each has one job and reports exactly what it did.",
|
|
115
118
|
"INTRO_SKILLS": "A Skill is a drill written for a recurring task. Run the matching drill before you start improvising.",
|
package/themes/military.json
CHANGED
|
@@ -110,6 +110,9 @@
|
|
|
110
110
|
"LEX_XXXV": "Cover Your Fire, Confirm the Hit · Cover and Verify",
|
|
111
111
|
"LEX_XXXVI": "Authority Rests Above the Trigger · External Gate",
|
|
112
112
|
"LEX_XXXVII": "Re-Arming Is Not Re-Briefing · Restart Is Not Reload",
|
|
113
|
+
"LEX_XXXVIII": "An Untested Sentry Is Already Asleep · Prove the Gate",
|
|
114
|
+
"LEX_XXXIX": "The Order Closes the Brief · Shape the Answer",
|
|
115
|
+
"LEX_XL": "Every Objective Keeps Its Designation · Codes That Persist",
|
|
113
116
|
"INTRO_DOC": "This file is the operating manual an AI coding agent reads on entering this repository.",
|
|
114
117
|
"INTRO_AGENTS": "Task focused work to the specialist whose job it is rather than running the whole op in one context; each has one objective and reports exactly what it did.",
|
|
115
118
|
"INTRO_SKILLS": "A Skill is a standard operating procedure for a recurring task. Run the matching SOP before improvising.",
|
package/themes/neutral.json
CHANGED
|
@@ -110,6 +110,9 @@
|
|
|
110
110
|
"LEX_XXXV": "Cover and Verify",
|
|
111
111
|
"LEX_XXXVI": "External Gate",
|
|
112
112
|
"LEX_XXXVII": "Restart Is Not Reload",
|
|
113
|
+
"LEX_XXXVIII": "Prove the Gate",
|
|
114
|
+
"LEX_XXXIX": "Shape the Answer",
|
|
115
|
+
"LEX_XL": "Codes That Persist",
|
|
113
116
|
"INTRO_DOC": "This file is the entry point an AI coding agent reads when working in this repository.",
|
|
114
117
|
"INTRO_AGENTS": "Delegate focused work to a specialist rather than doing everything in one context. Each specialist has a single clear purpose and a defined output contract.",
|
|
115
118
|
"INTRO_SKILLS": "A Skill is a written procedure for a recurring task. Run the matching Skill before improvising.",
|
package/themes/pirate.json
CHANGED
|
@@ -110,6 +110,9 @@
|
|
|
110
110
|
"LEX_XXXV": "Trials Rig the Work, Run Her Clean · Cover and Verify",
|
|
111
111
|
"LEX_XXXVI": "The Key Hangs in Another's Cabin · External Gate",
|
|
112
112
|
"LEX_XXXVII": "Droppin' Anchor Don't Redraw the Charts · Restart Is Not Reload",
|
|
113
|
+
"LEX_XXXVIII": "A Lock Ye Never Picked Ain't a Lock · Prove the Gate",
|
|
114
|
+
"LEX_XXXIX": "The Verdict at the End o' the Yarn · Shape the Answer",
|
|
115
|
+
"LEX_XL": "Every Prize Keeps Its Mark · Codes That Persist",
|
|
113
116
|
"INTRO_DOC": "This file is the first thing a deckhand reads on signing aboard this repository.",
|
|
114
117
|
"INTRO_AGENTS": "Send the right hand for focused work instead of sailing the whole ship yourself; each has one trade and brings back a clear report of what it did.",
|
|
115
118
|
"INTRO_SKILLS": "A Skill is a drilled manoeuvre for a recurring task. Run the matching manoeuvre before you improvise.",
|
package/themes/sports.json
CHANGED
|
@@ -110,6 +110,9 @@
|
|
|
110
110
|
"LEX_XXXV": "Cover Every Receiver, Then Snap the Tape · Cover and Verify",
|
|
111
111
|
"LEX_XXXVI": "The Official Makes the Call, Not the Player · External Gate",
|
|
112
112
|
"LEX_XXXVII": "A Timeout Isn't a New Playbook · Restart Is Not Reload",
|
|
113
|
+
"LEX_XXXVIII": "A Defence Never Attacked Isn't a Defence · Prove the Gate",
|
|
114
|
+
"LEX_XXXIX": "The Call Comes on the Final Whistle · Shape the Answer",
|
|
115
|
+
"LEX_XL": "Every Player Keeps a Number · Codes That Persist",
|
|
113
116
|
"INTRO_DOC": "This file is the pre-game briefing a coding teammate reads when it suits up for this repository.",
|
|
114
117
|
"INTRO_AGENTS": "Put the right player on focused work instead of running the whole offense yourself; each has one role and reports exactly what it did.",
|
|
115
118
|
"INTRO_SKILLS": "A Skill is a set play for a recurring task. Run the matching play before you improvise.",
|
package/themes/verstappen.json
CHANGED
|
@@ -110,6 +110,9 @@
|
|
|
110
110
|
"LEX_XXXV": "Not Done Till the Trials Run Green · Cover and Verify",
|
|
111
111
|
"LEX_XXXVI": "The Stewards Clear It, Not You · External Gate",
|
|
112
112
|
"LEX_XXXVII": "A New Lap Won't Load a New Setup · Restart Is Not Reload",
|
|
113
|
+
"LEX_XXXVIII": "A Flag That Never Waves Marshals Nothing · Prove the Gate",
|
|
114
|
+
"LEX_XXXIX": "The Answer on the Last Lap · Shape the Answer",
|
|
115
|
+
"LEX_XL": "Every Setup Change Gets a Number · Codes That Persist",
|
|
113
116
|
"INTRO_DOC": "This file is the briefing a coding driver reads when it joins this repository.",
|
|
114
117
|
"INTRO_AGENTS": "Get the right engineer on focused work instead of running the whole team yourself; each has one job and reports exactly what it did.",
|
|
115
118
|
"INTRO_SKILLS": "A Skill is a setup you've practised for a recurring task. Run the matching setup before you improvise.",
|
package/themes/wizard.json
CHANGED
|
@@ -110,6 +110,9 @@
|
|
|
110
110
|
"LEX_XXXV": "Cover the Casting, Then Confirm · Cover and Verify",
|
|
111
111
|
"LEX_XXXVI": "The Ward Is Set Beyond the Caster · External Gate",
|
|
112
112
|
"LEX_XXXVII": "Recasting Won't Reread the Grimoire · Restart Is Not Reload",
|
|
113
|
+
"LEX_XXXVIII": "A Ward Never Struck Is Only a Wish · Prove the Gate",
|
|
114
|
+
"LEX_XXXIX": "The Incantation Ends on the Word That Works · Shape the Answer",
|
|
115
|
+
"LEX_XL": "Every Rune Keeps Its Name · Codes That Persist",
|
|
113
116
|
"INTRO_DOC": "This file is the first page an arcane familiar reads on entering this repository.",
|
|
114
117
|
"INTRO_AGENTS": "Summon the right familiar for focused work rather than casting the whole rite in one circle; each has one purpose and returns a clear account of what it wrought.",
|
|
115
118
|
"INTRO_SKILLS": "A Skill is a proven spell for a recurring task. Speak the matching spell before improvising.",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as l,j as e,I as a}from"./index-
|
|
1
|
+
import{a as l,j as e,I as a}from"./index-D5VY3TY_.js";import{r as n}from"./react-DOpiTn5v.js";const c="https://github.com/Arylmera/Geneseed",h="https://github.com/Arylmera";function u(){const[r,i]=n.useState(c),[t,o]=n.useState(!0);return n.useEffect(()=>{l.docsPage("about").then(s=>{s!=null&&s.repo&&(i(s.repo),o(!!s.repo_is_github))}).catch(()=>{})},[]),e.jsxs("div",{className:"narrow",children:[e.jsx("div",{className:"head-row mb-18",children:e.jsxs("div",{children:[e.jsx("h1",{className:"h",children:"About"}),e.jsx("p",{className:"sub",children:"The project, where the source lives, and a word from the person who grew it."})]})}),e.jsxs("div",{className:"card pad-lg mb-16",children:[e.jsx("div",{className:"card-head",children:e.jsx("h3",{children:"Project"})}),e.jsx("p",{className:"sub mb-16",children:"Geneseed is a portable, theme-able harness you implant once and use everywhere to grow a disciplined AI coding agent. One canonical source, many voices, MIT-licensed."}),e.jsxs("div",{className:"row wrap gap-10",children:[e.jsxs("a",{className:"btn ghost",href:r,target:"_blank",rel:"noreferrer",children:[e.jsx(a,{name:"github"}),t?"Source on GitHub":"Source repo"]}),t&&e.jsxs(e.Fragment,{children:[e.jsxs("a",{className:"btn ghost",href:r+"/issues",target:"_blank",rel:"noreferrer",children:[e.jsx(a,{name:"external"}),"Issues"]}),e.jsxs("a",{className:"btn ghost",href:r+"/blob/main/LICENSE",target:"_blank",rel:"noreferrer",children:[e.jsx(a,{name:"external"}),"MIT License"]})]})]})]}),e.jsxs("div",{className:"card pad-lg",children:[e.jsx("div",{className:"card-head",children:e.jsx("h3",{children:"Creator"})}),e.jsxs("p",{className:"sub mb-16",children:["Geneseed is built by ",e.jsx("strong",{children:"Guillaume Lemer"}),". It started as a personal, Obsidian-vault-grown agent operating system and was distilled into the repo you are running. Implant it once, and a disciplined agent grows around the same inherited rules, skills, and memory in every repo it follows you into."]}),e.jsx("p",{className:"sub mb-16",children:"If you find this useful, a star on the repo or an issue with feedback is the best way to help shape what comes next."}),e.jsx("div",{className:"row wrap gap-10",children:e.jsxs("a",{className:"btn ghost",href:h,target:"_blank",rel:"noreferrer",children:[e.jsx(a,{name:"github"}),"@Arylmera on GitHub"]})})]})]})}export{u as default};
|