geneseed 3.4.0 → 3.6.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 +316 -0
- package/DESIGN.md +11 -14
- package/QUICKSTART.md +3 -1
- package/README.md +51 -42
- package/SETUP.md +25 -27
- package/SHIPPED.md +26 -32
- package/adapters/bob/README.md +12 -9
- package/adapters/copilot/README.md +26 -25
- package/adapters/opencode/GLOBAL-HARNESS-SPEC.md +1 -1
- package/adapters/opencode/README.md +2 -2
- package/adapters/opencode/plugins/geneseed-context.js +5 -3
- package/adapters/opencode/plugins/geneseed-workflow.js +30 -3
- package/adapters/opencode/workflows/_runtime.js +97 -7
- package/bin/build-driver.mjs +5 -1248
- package/bin/geneseed-cli.mjs +11 -6
- package/bin/geneseed-hook.mjs +4 -2
- package/docs/candidate-hosts.md +1 -1
- package/docs/design-history.md +37 -37
- package/docs/extending.md +49 -24
- package/docs/token-footprint.md +52 -26
- package/docs/web/claude-hooks.md +3 -2
- package/docs/web/collaboration.md +2 -2
- package/docs/web/first-session.md +1 -1
- package/docs/web/footprint.md +2 -2
- package/docs/web/harnesses.md +1 -1
- package/docs/web/model.md +1 -1
- package/docs/web/plugin-workflow.md +1 -0
- package/docs/web/rules.md +3 -3
- package/docs/web/setup-choices.md +2 -2
- package/docs/web/themes-author.md +1 -1
- package/docs/web-ui.md +18 -10
- package/harness.config.json +1 -1
- package/js/README.md +6 -5
- package/js/build/driver.mjs +1191 -0
- package/js/build/emit-claude.mjs +17 -10
- package/js/build/emit-common.mjs +4 -4
- package/js/build/generate.mjs +1 -1
- package/js/build/render.mjs +48 -44
- package/js/build/stubs.mjs +1 -1
- package/js/build/themes.mjs +4 -4
- package/js/build/version.mjs +12 -0
- package/js/cli-table.json +21 -6
- package/js/hosts/hooks.mjs +89 -74
- package/js/hosts/hosts.mjs +19 -2
- package/js/hosts/mcp.mjs +14 -20
- package/js/hosts/native.mjs +23 -17
- package/js/hosts/settings.mjs +52 -104
- package/js/inspect/checks-authoring.mjs +117 -3
- package/js/inspect/diff.mjs +1 -1
- package/js/inspect/doctor.mjs +1 -1
- package/js/inspect/excludes.mjs +1 -1
- package/js/inspect/scan.mjs +3 -14
- package/js/inspect/status.mjs +2 -2
- package/js/inspect/validate.mjs +1 -1
- package/js/lib/fs.mjs +23 -8
- package/js/lib/json.mjs +7 -1
- package/js/lib/proc.mjs +6 -4
- package/js/maintain/migrate.mjs +1 -1
- package/js/maintain/setup.mjs +1 -1
- package/js/maintain/uninstall.mjs +17 -15
- package/js/ui/cli.mjs +19 -0
- package/js/ui/menu.mjs +1 -1
- package/js/web/actions.mjs +4 -4
- package/js/web/activity.mjs +4 -4
- package/js/web/api.mjs +14 -11
- package/js/web/daemon.mjs +1 -1
- package/js/web/docs.mjs +12 -12
- package/js/web/handler.mjs +12 -5
- package/js/web/jobs.mjs +1 -1
- package/package.json +1 -1
- package/src/AGENT.md.tmpl +74 -38
- package/src/agents/developer.md +2 -2
- package/src/agents/reviewer.md +5 -2
- package/src/doctrines/README.md +26 -3
- package/src/doctrines/craft.md +78 -5
- package/src/doctrines/ops.md +62 -0
- package/src/doctrines/process.md +91 -4
- package/src/doctrines/rigor.md +40 -0
- package/src/laws/universal.md +27 -21
- package/src/memory/README.md +4 -2
- package/src/modes/foreman.md +5 -5
- package/src/notebook/README.md +2 -1
- package/src/ontology/universal.md +71 -67
- package/src/postures/peer.md +3 -0
- package/src/skills/bruno-collection-generator.md +1 -1
- package/src/skills/bruno-test-writer.md +2 -2
- package/src/skills/daydream/LICENSE +21 -0
- package/src/skills/daydream/VENDOR.md +3 -1
- package/src/skills/debug.md +1 -1
- package/src/skills/document-project.md +127 -25
- package/src/skills/geneseed-code-review.md +6 -2
- package/src/skills/parallel-agents.md +3 -3
- package/src/skills/pipeline.md +4 -4
- package/src/skills/react-view-transitions/VENDOR.md +7 -1
- package/src/skills/workflow.md +12 -4
- package/themes/_TEMPLATE.json +7 -5
- package/themes/biker.json +6 -4
- package/themes/commentator.json +6 -4
- package/themes/cyberpunk.json +6 -4
- package/themes/gamer.json +6 -4
- package/themes/imperial.json +6 -4
- package/themes/joker.json +6 -4
- package/themes/marvin.json +6 -4
- package/themes/mean.json +6 -4
- package/themes/military.json +6 -4
- package/themes/neutral.json +7 -5
- package/themes/opencode/README.md +2 -2
- package/themes/pirate.json +6 -4
- package/themes/sports.json +6 -4
- package/themes/verstappen.json +6 -4
- package/themes/wizard.json +6 -4
- package/web/dist/assets/Activity-Flgf4kHy.js +1 -0
- package/web/dist/assets/ActivityDetail-DIpr85nh.js +1 -0
- package/web/dist/assets/CatalogRow-CjyLjaLb.js +1 -0
- package/web/dist/assets/Diff-Deisdha8.js +2 -0
- package/web/dist/assets/Docs-DGUdY4l4.js +2 -0
- package/web/dist/assets/Doctor-F74lJ69P.js +1 -0
- package/web/dist/assets/FilterInput-BakJLvaV.js +1 -0
- package/web/dist/assets/GreenhouseView-D072lBiD.js +2 -0
- package/web/dist/assets/Harness-DNRP2Va7.js +1 -0
- package/web/dist/assets/JournalView-DWFqZ0Iw.js +1 -0
- package/web/dist/assets/Laws-BN6g3HU7.js +3 -0
- package/web/dist/assets/Library-CDOv4emM.js +1 -0
- package/web/dist/assets/Markdown-BQAvN8xx.js +38 -0
- package/web/dist/assets/OperatorHudView-CwNIUVX8.js +2 -0
- package/web/dist/assets/Profile-CpP9BTne.js +1 -0
- package/web/dist/assets/Rules-DFXsUCB7.js +1 -0
- package/web/dist/assets/Settings-BqZVUtJ5.js +1 -0
- package/web/dist/assets/Skills-Cu6kBgfF.js +1 -0
- package/web/dist/assets/StatusBadge-Cg6j-NJL.js +1 -0
- package/web/dist/assets/activity-iWaWVtsR.js +1 -0
- package/web/dist/assets/drawable-BgfrEBer.js +1 -0
- package/web/dist/assets/index-CAqrWhKl.js +2 -0
- package/web/dist/assets/index-Ruh93A3A.css +1 -0
- package/web/dist/assets/jobBuckets-Dbq8ZlWW.js +1 -0
- package/web/dist/assets/lawCats-BLgxl151.js +1 -0
- package/web/dist/assets/react-DbkvxWG5.js +8 -0
- package/web/dist/assets/rolldown-runtime-CbXtAM7H.js +1 -0
- package/web/dist/index.html +4 -3
- package/web/src/pages/Laws.jsx +22 -8
- package/src/skills/react-view-transitions/README.md +0 -42
- package/web/dist/assets/Activity-DMGhhjQc.js +0 -1
- package/web/dist/assets/ActivityDetail-CeQcDA7e.js +0 -1
- package/web/dist/assets/CatalogRow-CnSbDPyg.js +0 -1
- package/web/dist/assets/Diff-D_ksMBD1.js +0 -5
- package/web/dist/assets/Doctor-Brb3_oCT.js +0 -1
- package/web/dist/assets/FilterInput-DbRWPhtT.js +0 -1
- package/web/dist/assets/GreenhouseView-F3VNFr73.js +0 -2
- package/web/dist/assets/Harness-DJSwfYeF.js +0 -1
- package/web/dist/assets/JournalView-BLRYRnB3.js +0 -1
- package/web/dist/assets/Laws-CKYns9qe.js +0 -3
- package/web/dist/assets/Library-BK0YloSv.js +0 -1
- package/web/dist/assets/Markdown-BzTRzc7a.js +0 -46
- package/web/dist/assets/OperatorHudView-D3OQSGp5.js +0 -2
- package/web/dist/assets/Profile-CPA2u_0g.js +0 -1
- package/web/dist/assets/Rules-Lukel-Ru.js +0 -1
- package/web/dist/assets/Skills-BKRLHH7m.js +0 -1
- package/web/dist/assets/StatusBadge-DI3weRKd.js +0 -1
- package/web/dist/assets/activity-C_H2Bx1P.js +0 -1
- package/web/dist/assets/index-B7_Wga23.js +0 -2
- package/web/dist/assets/index-DzdQjvM1.css +0 -1
- package/web/dist/assets/index-N8dTrVro.js +0 -1
- package/web/dist/assets/index-Py_IQjgK.js +0 -10
- package/web/dist/assets/jobBuckets-DfTUJUxR.js +0 -1
- package/web/dist/assets/lawCats-DUS9UTH4.js +0 -1
- package/web/dist/assets/react-DOpiTn5v.js +0 -32
|
@@ -47,7 +47,7 @@ import {
|
|
|
47
47
|
} from 'node:fs';
|
|
48
48
|
import path from 'node:path';
|
|
49
49
|
|
|
50
|
-
import { hookRunnerEntry } from '
|
|
50
|
+
import { hookRunnerEntry } from '../build/driver.mjs';
|
|
51
51
|
import { confirm } from './setup.mjs';
|
|
52
52
|
import {
|
|
53
53
|
claudeCfg, claudeReadManifest, doctrinesOfDir, emitHostScopeOf, excludedRulesOfDir, installKind,
|
|
@@ -60,7 +60,7 @@ import {
|
|
|
60
60
|
import { mcpCommented } from '../hosts/mcp.mjs';
|
|
61
61
|
import {
|
|
62
62
|
copilotIntegrityCheck, managedBlockRead, managedBlockRemove, managedBlockWrite,
|
|
63
|
-
mergeClaudeSettings,
|
|
63
|
+
mergeClaudeSettings, opencodeTarget, readJsonc,
|
|
64
64
|
settingsIntegrityCheck, wireClaudeExcludes, unwireClaudeExcludes, unwireClaudeSettings,
|
|
65
65
|
unwireCopilotSettings,
|
|
66
66
|
} from '../hosts/settings.mjs';
|
|
@@ -347,9 +347,8 @@ export function uninstallGlobal(target, archiveMemory, host = 'opencode') {
|
|
|
347
347
|
/**
|
|
348
348
|
* `_harness_mcp._mcp_load`, reduced to the OpenCode arm.
|
|
349
349
|
*
|
|
350
|
-
* The Claude arm (`.mcp.json`, `~/.claude.json`, parsed
|
|
351
|
-
*
|
|
352
|
-
* the TUI, and neither has crossed. `installAgentEntry` is this port's only caller and passes
|
|
350
|
+
* The Claude arm (`.mcp.json`, `~/.claude.json`, parsed STRICTLY — see `mcpLoad` in
|
|
351
|
+
* js/hosts/mcp.mjs for why) lives with the MCP catalog. `installAgentEntry` is this port's only caller and passes
|
|
353
352
|
* no host, so porting the branch would ship an unreachable arm with no cell and no partition
|
|
354
353
|
* to be part of — the criterion this port decides keep-vs-delete by.
|
|
355
354
|
*/
|
|
@@ -645,7 +644,7 @@ function printSurvivingProjectInventory(removedRoot) {
|
|
|
645
644
|
function printOtherHostHits(root, removedHost) {
|
|
646
645
|
for (const spec of HOSTS) {
|
|
647
646
|
if (spec.host !== removedHost && projectQualifies(root, spec.host)) {
|
|
648
|
-
printOut(`[uninstall] also found ${spec.host}:project here — run \`
|
|
647
|
+
printOut(`[uninstall] also found ${spec.host}:project here — run \`geneseed `
|
|
649
648
|
+ 'uninstall` again to remove it.\n');
|
|
650
649
|
}
|
|
651
650
|
}
|
|
@@ -1058,15 +1057,18 @@ function cleanHostStash(cfg, host = 'claude') {
|
|
|
1058
1057
|
function remergeClaudeHooks(cfg, root = cfg, host = 'claude') {
|
|
1059
1058
|
const data = claudeReadManifest(cfg);
|
|
1060
1059
|
const managed = managedOf(data);
|
|
1061
|
-
// A Copilot install
|
|
1062
|
-
//
|
|
1063
|
-
//
|
|
1064
|
-
//
|
|
1060
|
+
// A LEGACY Copilot install (single-slot `copilot_hooks`, the dead `toolCall` event):
|
|
1061
|
+
// reactivating it re-wires the CURRENT Copilot shape — event → array, recorded under
|
|
1062
|
+
// `settings_hooks` — rather than restoring hooks Copilot no longer reads. None of the
|
|
1063
|
+
// pack/exclude axes below apply: the Copilot gate is not pack-conditional.
|
|
1065
1064
|
if (Array.isArray(managed.copilot_hooks)) {
|
|
1066
|
-
const
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1065
|
+
const sf = settingsFile(cfg, managed);
|
|
1066
|
+
unwireCopilotSettings(sf, managed.copilot_hooks);
|
|
1067
|
+
const [, claims] = mergeClaudeSettings(sf, 'global', null, hookRunnerEntry(), null, [],
|
|
1068
|
+
'copilot', cfg);
|
|
1069
|
+
if (Object.keys(data).length > 0) {
|
|
1070
|
+
delete managed.copilot_hooks;
|
|
1071
|
+
managed.settings_hooks = claims;
|
|
1070
1072
|
data.managed = managed;
|
|
1071
1073
|
const tmp = path.join(cfg, `${GLOBAL_MANIFEST}.tmp`);
|
|
1072
1074
|
try {
|
|
@@ -1098,7 +1100,7 @@ function remergeClaudeHooks(cfg, root = cfg, host = 'claude') {
|
|
|
1098
1100
|
: excludedRulesOfDir(cfg);
|
|
1099
1101
|
// `host` reaches the group builder: a Bob reactivate must re-wire Gemini-named groups.
|
|
1100
1102
|
const [, claims] = mergeClaudeSettings(settingsFile(cfg, managed), 'global',
|
|
1101
|
-
managed.settings_hooks ?? null, hookRunnerEntry(), doctrines, excluded, host);
|
|
1103
|
+
managed.settings_hooks ?? null, hookRunnerEntry(), doctrines, excluded, host, cfg);
|
|
1102
1104
|
// `and data` — a manifest that did not parse is not one to write back.
|
|
1103
1105
|
if (!deepEquals(claims, managed.settings_hooks ?? null) && Object.keys(data).length > 0) {
|
|
1104
1106
|
managed.settings_hooks = claims;
|
package/js/ui/cli.mjs
CHANGED
|
@@ -441,6 +441,25 @@ export function cliCommand(verb) {
|
|
|
441
441
|
* CRLF ON WINDOWS for `die`'s reason: argparse writes through a Python text stream, which
|
|
442
442
|
* translates, and this text lands in the same terminals and the same pipes as that one.
|
|
443
443
|
*/
|
|
444
|
+
/**
|
|
445
|
+
* `geneseed --help` — every verb with its one-line help, from the same table the per-verb help
|
|
446
|
+
* reads. `verbs` is the entry's own dispatch list, so the listing can name nothing the entry
|
|
447
|
+
* would refuse; a verb the table does not describe still appears, with no help line.
|
|
448
|
+
*/
|
|
449
|
+
export function printVerbList(prog, verbs, extra = {}) {
|
|
450
|
+
const helpOf = new Map([...Object.entries(extra),
|
|
451
|
+
...cliReference().commands.filter((c) => c.help).map((c) => [c.name, c.help])]);
|
|
452
|
+
const names = [...verbs].sort();
|
|
453
|
+
const pad = Math.max(...names.map((n) => n.length)) + 2;
|
|
454
|
+
const lines = [`usage: ${prog} <command> [options]`, '',
|
|
455
|
+
`A bare \`${prog}\` runs \`home\`. \`${prog} <command> --help\` describes one command.`, '',
|
|
456
|
+
'commands:'];
|
|
457
|
+
for (const n of names) lines.push(` ${n.padEnd(pad)}${helpOf.get(n) || ''}`.trimEnd());
|
|
458
|
+
const text = `${lines.join('\n')}\n`;
|
|
459
|
+
process.stdout.write(process.platform === 'win32' ? text.replaceAll('\n', '\r\n') : text);
|
|
460
|
+
return 0;
|
|
461
|
+
}
|
|
462
|
+
|
|
444
463
|
export function printHelp(prog, verb) {
|
|
445
464
|
const cmd = cliCommand(verb);
|
|
446
465
|
if (cmd === null) return null;
|
package/js/ui/menu.mjs
CHANGED
|
@@ -45,7 +45,7 @@ import { startDaemon } from '../web/daemon.mjs';
|
|
|
45
45
|
function menuHelp() {
|
|
46
46
|
printOut('Geneseed — no interactive menu here. Get started with: geneseed setup\n');
|
|
47
47
|
printOut('Other commands: bootstrap · update · build · doctor · diff · tui · web\n');
|
|
48
|
-
printOut('
|
|
48
|
+
printOut('`geneseed --help` lists every command.\n');
|
|
49
49
|
return 0;
|
|
50
50
|
}
|
|
51
51
|
|
package/js/web/actions.mjs
CHANGED
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
import os from 'node:os';
|
|
33
33
|
import path from 'node:path';
|
|
34
34
|
|
|
35
|
-
import { emitGlobalInto } from '
|
|
35
|
+
import { emitGlobalInto } from '../build/driver.mjs';
|
|
36
36
|
import {
|
|
37
37
|
ROOT, discoverNames, knownRuleIds, PACK_ORDER,
|
|
38
38
|
} from '../build/source.mjs';
|
|
@@ -127,7 +127,7 @@ function bodyExcludeRules(body) {
|
|
|
127
127
|
const splitWords = (s) => s.split(new RegExp(`[${WHITESPACE}]+`)).filter(Boolean);
|
|
128
128
|
|
|
129
129
|
/** Today's date, LOCAL (not UTC) — this is a user-facing date, not a wire timestamp. */
|
|
130
|
-
|
|
130
|
+
function todayIso(d = new Date()) {
|
|
131
131
|
const p2 = (n) => String(n).padStart(2, '0');
|
|
132
132
|
return `${d.getFullYear()}-${p2(d.getMonth() + 1)}-${p2(d.getDate())}`;
|
|
133
133
|
}
|
|
@@ -278,13 +278,13 @@ const rulesPath = (state) => path.join(state.target, RULES_FILE);
|
|
|
278
278
|
* `max(ids) + 1`. Widening this to match would only move the gap one line down, to whatever
|
|
279
279
|
* parses the captured digits back into a number.
|
|
280
280
|
*/
|
|
281
|
-
|
|
281
|
+
const RULE_HEAD_RE = new RegExp(
|
|
282
282
|
`^##[${WHITESPACE}]+R(\\d+)[${WHITESPACE}]*[—–-]+[${WHITESPACE}]*`
|
|
283
283
|
+ `([^${WHITESPACE}].*?)[${WHITESPACE}]*$`,
|
|
284
284
|
);
|
|
285
285
|
|
|
286
286
|
/** Advisory only; nothing blocks past it. */
|
|
287
|
-
|
|
287
|
+
const RULES_BUDGET = { max_rules: 15, max_tokens: 1500 };
|
|
288
288
|
|
|
289
289
|
const META_RE = new RegExp(`^\\((.+)\\)[${WHITESPACE}]*$`);
|
|
290
290
|
|
package/js/web/activity.mjs
CHANGED
|
@@ -48,7 +48,7 @@ import { NotFound, stemOf } from './api.mjs';
|
|
|
48
48
|
* platform where the probe cannot tell. Generous on purpose: streaming bumps `updated_at`
|
|
49
49
|
* constantly, so only a genuinely abandoned (process-still-alive) session ages out.
|
|
50
50
|
*/
|
|
51
|
-
|
|
51
|
+
const ACTIVITY_STALE_SECONDS = 1800;
|
|
52
52
|
|
|
53
53
|
const nowSeconds = () => Date.now() / 1000;
|
|
54
54
|
|
|
@@ -66,7 +66,7 @@ const activityFlag = (state) => path.join(state.target, '.geneseed-activity');
|
|
|
66
66
|
* default every user who has never toggled is in. An undecodable flag file is not treated
|
|
67
67
|
* specially: Node substitutes U+FFFD and reads on rather than raising.
|
|
68
68
|
*/
|
|
69
|
-
|
|
69
|
+
function activityEnabled(state) {
|
|
70
70
|
let raw;
|
|
71
71
|
try {
|
|
72
72
|
raw = readText(activityFlag(state));
|
|
@@ -97,7 +97,7 @@ function toIntOr(raw) {
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
/** Best-effort liveness for the writer process. See this file's header. */
|
|
100
|
-
|
|
100
|
+
function pidAlive(raw) {
|
|
101
101
|
const pid = toIntOr(raw);
|
|
102
102
|
if (pid === null || pid <= 0) return false;
|
|
103
103
|
try {
|
|
@@ -161,7 +161,7 @@ function isLive(entry, now) {
|
|
|
161
161
|
* missing dir is `[]`, a garbage file is skipped (and kept — only a snapshot that PARSED and
|
|
162
162
|
* failed the liveness test is unlinked), newest first.
|
|
163
163
|
*/
|
|
164
|
-
|
|
164
|
+
function activityEntries(state) {
|
|
165
165
|
const d = activityDir(state);
|
|
166
166
|
if (!isDir(d)) return [];
|
|
167
167
|
const now = nowSeconds();
|
package/js/web/api.mjs
CHANGED
|
@@ -201,7 +201,7 @@ export function specDesc(fm, body) {
|
|
|
201
201
|
* plumbing rather than prose, which is what makes a deployed entry the same shape as a
|
|
202
202
|
* source-rendered one and every consumer indifferent to the origin.
|
|
203
203
|
*/
|
|
204
|
-
|
|
204
|
+
function specEntries(root, nested) {
|
|
205
205
|
const out = [];
|
|
206
206
|
if (!isDir(root)) return out;
|
|
207
207
|
let names;
|
|
@@ -238,7 +238,7 @@ export function specEntries(root, nested) {
|
|
|
238
238
|
* the walk. So the catalogue is always whole, and which packs this INSTALL built in comes from
|
|
239
239
|
* its own carrier via `doctrinesForBuild`, which resolves "no marker" to every pack.
|
|
240
240
|
*/
|
|
241
|
-
|
|
241
|
+
function deployedInventory(state) {
|
|
242
242
|
const render = tuiInventory(state.theme, doctrinesForBuild(state.target),
|
|
243
243
|
excludedRulesOfDir(state.target));
|
|
244
244
|
const registry = loadRegistry();
|
|
@@ -259,7 +259,7 @@ export function deployedInventory(state) {
|
|
|
259
259
|
}
|
|
260
260
|
|
|
261
261
|
/** The deployed record when there is one, else the source render. */
|
|
262
|
-
|
|
262
|
+
function inventoryFor(state) {
|
|
263
263
|
return deployed(state) ? deployedInventory(state) : tuiInventory(state.theme);
|
|
264
264
|
}
|
|
265
265
|
|
|
@@ -305,7 +305,7 @@ export function memoryItems(state) {
|
|
|
305
305
|
});
|
|
306
306
|
}
|
|
307
307
|
|
|
308
|
-
|
|
308
|
+
function notebookItems(state) {
|
|
309
309
|
const d = notebookDir(state);
|
|
310
310
|
if (!isDir(d)) return [];
|
|
311
311
|
return globMd(d).map((n) => ({
|
|
@@ -319,7 +319,7 @@ const CONFIG_META = {
|
|
|
319
319
|
'wiki.jsonc': ['Wiki manifest', 'your machine-wide knowledge base(s)'],
|
|
320
320
|
};
|
|
321
321
|
|
|
322
|
-
|
|
322
|
+
function configItems(state) {
|
|
323
323
|
const out = [];
|
|
324
324
|
for (const fname of ['context.json', 'wiki.jsonc']) {
|
|
325
325
|
const p = path.join(state.target, fname);
|
|
@@ -331,7 +331,7 @@ export function configItems(state) {
|
|
|
331
331
|
return out;
|
|
332
332
|
}
|
|
333
333
|
|
|
334
|
-
|
|
334
|
+
const WIKI_FILE_CAP = 5000;
|
|
335
335
|
|
|
336
336
|
/**
|
|
337
337
|
* `resolvePath` for a path that came out of a HAND-MAINTAINED manifest — `null`, never a throw.
|
|
@@ -419,7 +419,10 @@ export function wikiItems(state) {
|
|
|
419
419
|
// blank every OTHER vault in it.
|
|
420
420
|
const root = wikiPath(String(dget(w, 'path', null) || ''));
|
|
421
421
|
if (!root || !isDir(root)) continue;
|
|
422
|
-
|
|
422
|
+
// A hand-edited `entries` that is not a list (`{}`, a string) is a malformed vault, not a
|
|
423
|
+
// crash: `/api/status` counts these, and one bad manifest must not fail the dashboard.
|
|
424
|
+
const rawEntries = dget(w, 'entries', null);
|
|
425
|
+
const entries = (Array.isArray(rawEntries) ? rawEntries : [])
|
|
423
426
|
.filter((e) => e && typeof e === 'object' && !Array.isArray(e));
|
|
424
427
|
const excludes = entries.filter((e) => dget(e, 'load', null) === 'exclude')
|
|
425
428
|
.map((e) => String(dget(e, 'path', null) || '').replace(/^\/+|\/+$/g, '').replace(/\\/g, '/'));
|
|
@@ -450,7 +453,7 @@ export function wikiItems(state) {
|
|
|
450
453
|
}
|
|
451
454
|
|
|
452
455
|
/** One page by `<wiki>:<relpath>`, never outside the vault. */
|
|
453
|
-
|
|
456
|
+
function apiWikiItem(state, name) {
|
|
454
457
|
const at = name.indexOf(':');
|
|
455
458
|
const wname = at < 0 ? name : name.slice(0, at);
|
|
456
459
|
const rel = (at < 0 ? '' : name.slice(at + 1))
|
|
@@ -497,7 +500,7 @@ function flatName(name) {
|
|
|
497
500
|
}
|
|
498
501
|
}
|
|
499
502
|
|
|
500
|
-
|
|
503
|
+
const WIKILINK_RE = /\[\[([^\]]+)\]\]/g;
|
|
501
504
|
|
|
502
505
|
/**
|
|
503
506
|
* `[[name]]` matched against known agent and skill names.
|
|
@@ -746,9 +749,9 @@ export function apiInstalls(state) {
|
|
|
746
749
|
|
|
747
750
|
/** Beside the deployed AGENT.md. */
|
|
748
751
|
export const PROFILE_FILE = 'PROFILE.md';
|
|
749
|
-
|
|
752
|
+
const profilePath = (state) => path.join(state.target, PROFILE_FILE);
|
|
750
753
|
|
|
751
|
-
|
|
754
|
+
function apiProfile(state) {
|
|
752
755
|
const p = profilePath(state);
|
|
753
756
|
if (!isFile(p)) return { exists: false, path: p, text: '', fingerprint: '' };
|
|
754
757
|
const text = readMaybe(p) ?? '';
|
package/js/web/daemon.mjs
CHANGED
|
@@ -83,7 +83,7 @@ export async function probe(url, timeout = 1.5) {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
/** The token-carrying POST `geneseed web stop` and the in-page Stop button both send. */
|
|
86
|
-
|
|
86
|
+
async function postShutdown(url, token, timeout = 3.0) {
|
|
87
87
|
try {
|
|
88
88
|
const res = await fetch(`${url}/api/shutdown`, {
|
|
89
89
|
method: 'POST',
|
package/js/web/docs.mjs
CHANGED
|
@@ -24,7 +24,7 @@ import { readJsonMaybe, readMaybe } from '../hosts/installs.mjs';
|
|
|
24
24
|
import { resolvePath } from '../hosts/hosts.mjs';
|
|
25
25
|
import { parseJson } from '../lib/json.mjs';
|
|
26
26
|
import { isFile } from '../lib/fs.mjs';
|
|
27
|
-
import { normcase } from '../lib/paths.mjs';
|
|
27
|
+
import { normcase, within } from '../lib/paths.mjs';
|
|
28
28
|
import { WHITESPACE, stripWhitespace } from '../lib/text.mjs';
|
|
29
29
|
import { statusData } from '../inspect/status.mjs';
|
|
30
30
|
import { originDisplay } from '../maintain/update.mjs';
|
|
@@ -39,7 +39,7 @@ const DOC_DIR = path.join(ROOT, 'docs', 'web');
|
|
|
39
39
|
* covers every key these pages use and parses with the standard library. A value that is
|
|
40
40
|
* not valid JSON is kept as the raw string, which is how `title: Something` works.
|
|
41
41
|
*/
|
|
42
|
-
|
|
42
|
+
function docFrontmatter(text) {
|
|
43
43
|
const marker = '---\n';
|
|
44
44
|
if (!text.startsWith(marker)) return [{}, text];
|
|
45
45
|
const rest = text.slice(marker.length);
|
|
@@ -109,8 +109,13 @@ export function docGroups() {
|
|
|
109
109
|
*/
|
|
110
110
|
const GLOSSARY_KEYS = [
|
|
111
111
|
// The three tiers, in constitutional order and before the entities — a reader who does not
|
|
112
|
-
// know what an
|
|
113
|
-
|
|
112
|
+
// know what an Ethos is here cannot read the rest of the glossary's first rows.
|
|
113
|
+
//
|
|
114
|
+
// `Ethos (Ontology)` for the same reason as `Rule (Law)` below: the neutral theme renamed
|
|
115
|
+
// what a reader SEES, while the address the reader will meet in `src/ontology/`, in the
|
|
116
|
+
// `ONTOLOGY` theme key and in every identifier did not move. The parenthesis is the bridge
|
|
117
|
+
// between the two, and the glossary is the one page that owes a reader that bridge.
|
|
118
|
+
['Ethos (Ontology)', 'ONTOLOGY', 'the always-on worldview under the rules — telos, evidence, '
|
|
114
119
|
+ 'decisions, conduct; it holds the Pact'],
|
|
115
120
|
['Rule (Law)', 'LAW', 'one of the nine always-on invariants: what is never done'],
|
|
116
121
|
['Rules (Laws)', 'LAWS', 'the body of always-on invariants'],
|
|
@@ -126,8 +131,9 @@ const GLOSSARY_KEYS = [
|
|
|
126
131
|
['Wiki', 'WIKI', 'the machine-wide knowledge base'],
|
|
127
132
|
// Still a token, still themed, but it names a CONCEPT INSIDE the ontology's Telos now
|
|
128
133
|
// rather than a peer of the Rules — `src/postures/peer.md` cites it and the section that
|
|
129
|
-
// used to carry it is the
|
|
130
|
-
['Pact', 'PACT',
|
|
134
|
+
// used to carry it is the Ethos.
|
|
135
|
+
['Pact', 'PACT', 'the two-way collaboration contract, stated in Telos, the first Ethos '
|
|
136
|
+
+ 'section'],
|
|
131
137
|
['Posture', null, 'the relationship register the agent works in '
|
|
132
138
|
+ '(peer, mentor, expert, assistant, artisan)'],
|
|
133
139
|
['Mode', null, 'how work gets executed — direct (the agent works every '
|
|
@@ -357,12 +363,6 @@ function readDocSource(rel) {
|
|
|
357
363
|
return readMaybe(target) ?? '';
|
|
358
364
|
}
|
|
359
365
|
|
|
360
|
-
function within(child, parent) {
|
|
361
|
-
const c = normcase(child).split(/[\\/]/);
|
|
362
|
-
const p = normcase(parent).split(/[\\/]/);
|
|
363
|
-
return p.length <= c.length && p.every((seg, i) => c[i] === seg);
|
|
364
|
-
}
|
|
365
|
-
|
|
366
366
|
/** The deployed theme's words beside the neutral ones. */
|
|
367
367
|
function glossary(state) {
|
|
368
368
|
const load = (theme) => {
|
package/js/web/handler.mjs
CHANGED
|
@@ -62,6 +62,13 @@ export function makeHandler(state, jm, token, dist, holder = null) {
|
|
|
62
62
|
// syscall against the readFileSync + gzipSync it saves. `gz` is the gzipped body,
|
|
63
63
|
// computed once on the first gzip-accepting request instead of per request.
|
|
64
64
|
const staticCache = new Map();
|
|
65
|
+
// `dist` RESOLVED ONCE, because every path compared against it below is resolved too.
|
|
66
|
+
// `resolvePath` is `realpathSync.native`, so a checkout reached by any path that is not its
|
|
67
|
+
// canonical one — a symlink, a Windows junction, a `subst` or mapped drive, an 8.3 short name,
|
|
68
|
+
// a lowercase drive letter — made `fp.startsWith(dist + sep)` false for EVERY asset. Each one
|
|
69
|
+
// then fell back to index.html at `text/html`, the browser refused the module script, and the
|
|
70
|
+
// page stayed blank with nothing logged on either side.
|
|
71
|
+
const distReal = resolvePath(dist);
|
|
65
72
|
|
|
66
73
|
function sendBytes(res, body, ctype, code = 200, extra = null, acceptEncoding = '', gzStore = null) {
|
|
67
74
|
const headers = { ...(extra || {}) };
|
|
@@ -86,12 +93,12 @@ export function makeHandler(state, jm, token, dist, holder = null) {
|
|
|
86
93
|
|
|
87
94
|
function serveStatic(res, path, acceptEncoding) {
|
|
88
95
|
const rel = (path === '/' || path === '') ? 'index.html' : path.replace(/^\/+/, '');
|
|
89
|
-
const index =
|
|
90
|
-
let fp = resolvePath(join(
|
|
96
|
+
const index = join(distReal, 'index.html');
|
|
97
|
+
let fp = resolvePath(join(distReal, rel));
|
|
91
98
|
// STRICTLY under `dist`: `fp === dist` itself must also take the fallback below, which
|
|
92
|
-
// is why this checks `fp !==
|
|
93
|
-
if (!(fp.startsWith(
|
|
94
|
-
if (!isFile(fp)) fp =
|
|
99
|
+
// is why this checks `fp !== distReal` and not just the prefix.
|
|
100
|
+
if (!(fp.startsWith(distReal + sep) && fp !== distReal) && fp !== index) fp = index;
|
|
101
|
+
if (!isFile(fp)) fp = index;
|
|
95
102
|
if (!isFile(fp)) {
|
|
96
103
|
return sendJson(res, { error: 'web/dist missing — run the UI build' }, 500, acceptEncoding);
|
|
97
104
|
}
|
package/js/web/jobs.mjs
CHANGED
|
@@ -98,7 +98,7 @@ import { jsonDumpsCompact, parseJson, isTruthy } from '../lib/json.mjs';
|
|
|
98
98
|
* NEVER THROWS: it runs from `cancel()` and from the crash path in `_run`'s `catch`, and an
|
|
99
99
|
* exception in either would leave the child alive with the manager believing it dead.
|
|
100
100
|
*/
|
|
101
|
-
|
|
101
|
+
function killJobTree(child) {
|
|
102
102
|
try {
|
|
103
103
|
if (process.platform === 'win32') {
|
|
104
104
|
// The one machine primitive with no Node equivalent — see this module's header.
|
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": "3.
|
|
4
|
+
"version": "3.6.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/AGENT.md.tmpl
CHANGED
|
@@ -19,19 +19,26 @@ name, or reference it from the tool's config. Where a tool supports sub-agents,
|
|
|
19
19
|
the {{AGENTS}} below are dispatched as real sub-agents; where it does not, treat
|
|
20
20
|
each as a *persona* the single agent adopts for that slice of work.
|
|
21
21
|
<!-- LEAN:else -->
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
dispatched as real sub-agents; where it does not, treat each as a *persona* the
|
|
25
|
-
single agent adopts for that slice of work.
|
|
22
|
+
Tool-agnostic: where the host has no sub-agents, each {{AGENT}} below is a
|
|
23
|
+
*persona* the single agent adopts for that slice of work.
|
|
26
24
|
<!-- LEAN:end -->
|
|
27
25
|
|
|
26
|
+
<!-- LEAN:begin -->
|
|
28
27
|
**Readiness sigil — required.** Before your first reply, complete the startup
|
|
29
28
|
ritual: read this file and its {{LAWS}}, then **load the project context** — if a
|
|
30
29
|
`context.json` sits beside this file, read it and load its `eager` entries now
|
|
31
30
|
(§9). Only then emit your first reply, which MUST begin with the
|
|
32
31
|
exact line below, before any other text. Do not paraphrase it and do not skip it —
|
|
33
32
|
a first reply that omits the sigil signals the harness was not read, and emitting
|
|
34
|
-
it certifies you have loaded the {{LAWS}} *and* the project context.
|
|
33
|
+
it certifies you have loaded the {{LAWS}} *and* the project context. **{{SKILLS}}
|
|
34
|
+
first:** when a request matches a {{SKILL}}, invoke it as your first tool
|
|
35
|
+
call — before loading context or touching files.
|
|
36
|
+
<!-- LEAN:else -->
|
|
37
|
+
**Readiness sigil — required.** Your first reply MUST begin with the exact line below,
|
|
38
|
+
verbatim — it certifies you have read this file and its {{LAWS}} and loaded `context.json`
|
|
39
|
+
and its `eager` entries (§9). **{{SKILLS}} first:** when the request matches a {{SKILL}}, invoke
|
|
40
|
+
it as your very first tool call — before loading context, reading files, or searching.
|
|
41
|
+
<!-- LEAN:end -->
|
|
35
42
|
|
|
36
43
|
> {{LOADED_SIGIL}}
|
|
37
44
|
|
|
@@ -128,7 +135,7 @@ Active packs: {{DOCTRINES_LIST}}{{EXCLUDED_RULES_LINE}}
|
|
|
128
135
|
|
|
129
136
|
{{INTRO_AGENTS}} Specs live in [`{{DIR_AGENTS}}/`]({{DIR_AGENTS}}/).
|
|
130
137
|
|
|
131
|
-
<!-- CATALOG:begin -->
|
|
138
|
+
<!-- CATALOG:begin agents -->
|
|
132
139
|
| {{AGENT}} | Use it when… |
|
|
133
140
|
| --- | --- |
|
|
134
141
|
| [reviewer]({{DIR_AGENTS}}/reviewer.md) | a change is ready and needs a correctness + quality pass before merge |
|
|
@@ -173,7 +180,7 @@ content. Where the host exposes a `workflow` tool, prefer the saved `dispatch`
|
|
|
173
180
|
workflow ([workflow {{SKILL}}]({{DIR_SKILLS}}/workflow.md)); where it does not, run
|
|
174
181
|
the same shape model-driven via the [parallel-agents {{SKILL}}]({{DIR_SKILLS}}/parallel-agents.md).
|
|
175
182
|
|
|
176
|
-
|
|
183
|
+
Ten of them — `advocate`, `skeptic`, `pragmatist`, `steward`, `visionary`,
|
|
177
184
|
`user-advocate`, `framer`, `empiricist`, `operator`, `historian` — are the standing
|
|
178
185
|
**council**: read-only debate seats that each argue one assigned stance. They are rarely
|
|
179
186
|
dispatched alone; the [council {{SKILL}}]({{DIR_SKILLS}}/council.md) convenes the
|
|
@@ -187,7 +194,7 @@ subset that productively clashes on a given motion.
|
|
|
187
194
|
|
|
188
195
|
{{INTRO_SKILLS}} Specs live in [`{{DIR_SKILLS}}/`]({{DIR_SKILLS}}/).
|
|
189
196
|
|
|
190
|
-
<!-- CATALOG:begin -->
|
|
197
|
+
<!-- CATALOG:begin skills -->
|
|
191
198
|
| {{SKILL}} | Trigger |
|
|
192
199
|
| --- | --- |
|
|
193
200
|
| [brainstorm]({{DIR_SKILLS}}/brainstorm.md) | a new feature or project with no design yet, or whose goal, scope, or success criteria need pinning down first |
|
|
@@ -202,7 +209,7 @@ subset that productively clashes on a given motion.
|
|
|
202
209
|
| [debug]({{DIR_SKILLS}}/debug.md) | a bug or failure to diagnose before fixing |
|
|
203
210
|
| [prototype]({{DIR_SKILLS}}/prototype.md) | a design question needs a runnable answer before real implementation |
|
|
204
211
|
| [repo-map]({{DIR_SKILLS}}/repo-map.md) | orienting to the repo, or structure changed |
|
|
205
|
-
| [document-project]({{DIR_SKILLS}}/document-project.md) |
|
|
212
|
+
| [document-project]({{DIR_SKILLS}}/document-project.md) | missing/drifted docs, or a PRD, design system, architecture doc or AGENTS.md |
|
|
206
213
|
| [ingest]({{DIR_SKILLS}}/ingest.md) | a task needs content from a PDF, Office doc, or URL |
|
|
207
214
|
| [research]({{DIR_SKILLS}}/research.md) | a question needs answers gathered and verified from the open web |
|
|
208
215
|
| [teach]({{DIR_SKILLS}}/teach.md) | the user wants to be taught — a crash course, confusing material decoded, or a day-by-day path to a deadline |
|
|
@@ -249,14 +256,14 @@ demand.
|
|
|
249
256
|
When a task repeats and no {{SKILL}} covers it, forge one through the
|
|
250
257
|
[skill-forge]({{DIR_SKILLS}}/skill-forge.md) {{SKILL}}: copy
|
|
251
258
|
[`{{DIR_SKILLS}}/_template.md`]({{DIR_SKILLS}}/_template.md), keep it to one coherent
|
|
252
|
-
domain ({{DOCTRINE}} craft 1), and
|
|
259
|
+
domain ({{DOCTRINE}} craft 1), and list it wherever this file carries a catalogue. Every {{SKILL}} ends with a
|
|
253
260
|
**self-improvement loop** — honour it: fold what a run teaches back into the
|
|
254
261
|
file, or into {{MEMORY}} ({{DOCTRINE}} process 1).
|
|
255
262
|
|
|
256
263
|
**Folder {{SKILLS}}.** Some {{SKILLS}} ship as self-contained folders under
|
|
257
264
|
`{{DIR_SKILLS}}/`, each with its own format and provenance (`VENDOR.md`) —
|
|
258
|
-
third-party vendors plus first-party ones bundling scripts.
|
|
259
|
-
|
|
265
|
+
third-party vendors plus first-party ones bundling scripts. Reach for one only when
|
|
266
|
+
its domain comes up:
|
|
260
267
|
- [react-view-transitions]({{DIR_SKILLS}}/react-view-transitions/SKILL.md) — animating
|
|
261
268
|
React UIs with the View Transition API (`<ViewTransition>`, shared elements, Next.js).
|
|
262
269
|
- [daydream]({{DIR_SKILLS}}/daydream/SKILL.md) — mining the `wiki.jsonc` vault (§8) for
|
|
@@ -270,6 +277,7 @@ above — reach for one only when its domain comes up:
|
|
|
270
277
|
|
|
271
278
|
*{{EPI_MEMORY}}*
|
|
272
279
|
|
|
280
|
+
<!-- LEAN:begin -->
|
|
273
281
|
Persistent facts live in [`{{DIR_MEMORY}}/`]({{DIR_MEMORY}}/) — one fact per file, indexed by a
|
|
274
282
|
local `MEMORY.md`. Read that index at the start of a session; write a new memory
|
|
275
283
|
whenever the conversation yields a durable fact, a correction, or a decision worth
|
|
@@ -284,6 +292,15 @@ carry an optional `force` (constraint · choice · conviction · tempered): when
|
|
|
284
292
|
evidence contradicts one, revise it rather than ignore it, and never overturn a
|
|
285
293
|
`constraint` yourself — surface it to the user. Full
|
|
286
294
|
convention: [`{{DIR_MEMORY}}/README.md`]({{DIR_MEMORY}}/README.md).
|
|
295
|
+
<!-- LEAN:else -->
|
|
296
|
+
Durable facts live in [`{{DIR_MEMORY}}/`]({{DIR_MEMORY}}/), one per file, indexed by
|
|
297
|
+
`MEMORY.md` — **read that index at the start of a session**. Write one through the
|
|
298
|
+
[rule]({{DIR_SKILLS}}/rule.md) {{SKILL}}, which settles with the user whether a
|
|
299
|
+
lesson is a memory or a standing rule ({{DOCTRINE}} process 1). The directory is
|
|
300
|
+
**personal and git-ignored**; shared knowledge goes in code, docs, or `user-rules.md`
|
|
301
|
+
(§1). Convention, including the optional `force` field:
|
|
302
|
+
[`{{DIR_MEMORY}}/README.md`]({{DIR_MEMORY}}/README.md).
|
|
303
|
+
<!-- LEAN:end -->
|
|
287
304
|
|
|
288
305
|
---
|
|
289
306
|
|
|
@@ -307,24 +324,21 @@ tool you build for yourself — belongs in `{{DIR_NOTEBOOK}}/`, never in the hos
|
|
|
307
324
|
tree. Read `NOTEBOOK.md` at the start of a session to recall what
|
|
308
325
|
you keep here, and keep the space referable across sessions — the seeded index
|
|
309
326
|
is one way; replace it with whatever scheme serves you better. When a note
|
|
310
|
-
hardens into a durable fact worth recalling verbatim,
|
|
311
|
-
(§5, {{DOCTRINE}} process 1). Keep the two stores distinct by *role*, not just by
|
|
327
|
+
hardens into a durable fact worth recalling verbatim, propose it for {{MEMORY}}
|
|
328
|
+
through the rule {{SKILL}} (§5, {{DOCTRINE}} process 1). Keep the two stores distinct by *role*, not just by
|
|
312
329
|
format: {{MEMORY}} holds durable insight for future sessions, the {{NOTEBOOK}}
|
|
313
330
|
holds your working artifacts. The one fixed law: `{{DIR_NOTEBOOK}}/` is **personal and
|
|
314
331
|
git-ignored** — never committed or shared, and the `.gitignore` that guarantees
|
|
315
332
|
it is not yours to lift.
|
|
316
333
|
<!-- LEAN:else -->
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
durable
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
to rewrite. The one fixed law: `{{DIR_NOTEBOOK}}/` is **personal and
|
|
326
|
-
git-ignored** — never committed or shared, and the `.gitignore` that guarantees
|
|
327
|
-
it is not yours to lift.
|
|
334
|
+
Your own working space, [`{{DIR_NOTEBOOK}}/`]({{DIR_NOTEBOOK}}/): any file you make
|
|
335
|
+
for your own benefit — scratch code, drafts, datasets, experiments — goes here, never
|
|
336
|
+
in the host tree, in whatever format serves. **Read `NOTEBOOK.md` at the start of a
|
|
337
|
+
session.** A note that hardens into a durable fact is proposed for {{MEMORY}} through
|
|
338
|
+
the rule {{SKILL}} (§5).
|
|
339
|
+
The charter [`{{DIR_NOTEBOOK}}/README.md`]({{DIR_NOTEBOOK}}/README.md) is yours to
|
|
340
|
+
rewrite; the one fixed law is that the folder is **personal and git-ignored**, and
|
|
341
|
+
that `.gitignore` is not yours to lift.
|
|
328
342
|
<!-- LEAN:end -->
|
|
329
343
|
|
|
330
344
|
---
|
|
@@ -333,14 +347,15 @@ it is not yours to lift.
|
|
|
333
347
|
|
|
334
348
|
*{{EPI_VAULT}}*
|
|
335
349
|
|
|
350
|
+
<!-- LEAN:begin -->
|
|
336
351
|
{{INTRO_WORKSPACE}} The harness files — these {{LAWS}}, {{AGENTS}}, {{SKILLS}}, this file — sit
|
|
337
352
|
*alongside* whatever already belongs to this machine or repository: notes, data,
|
|
338
353
|
configs, documents, prior work. All of it is context you may use.
|
|
339
354
|
|
|
340
355
|
After reading this file, take stock of what surrounds it. That non-harness
|
|
341
356
|
content is knowledge to **read and learn from**, not scaffolding to reorganise.
|
|
342
|
-
When you find a durable fact there worth keeping,
|
|
343
|
-
it outlives the session.
|
|
357
|
+
When you find a durable fact there worth keeping, propose it for {{MEMORY}} through
|
|
358
|
+
the rule {{SKILL}} (§5) so it outlives the session.
|
|
344
359
|
|
|
345
360
|
Because the folder is shared, two cautions hold:
|
|
346
361
|
- **You do not own it.** Files you did not create are not yours to move, rename,
|
|
@@ -351,6 +366,15 @@ Because the folder is shared, two cautions hold:
|
|
|
351
366
|
And it receives only the **deliverables** of the task. Your own working files —
|
|
352
367
|
scratch scripts, analysis dumps, drafts, experiments — are made in the
|
|
353
368
|
{{NOTEBOOK}} (§6), never here.
|
|
369
|
+
<!-- LEAN:else -->
|
|
370
|
+
{{INTRO_WORKSPACE}} Whatever sits beside the harness files — notes, data, configs,
|
|
371
|
+
prior work — is knowledge to read and learn from, not scaffolding to reorganise;
|
|
372
|
+
propose a durable fact from it for {{MEMORY}} through the rule {{SKILL}} (§5). You do not own the folder: files
|
|
373
|
+
you did not create are not yours to move or delete unless the task requires it
|
|
374
|
+
({{LAW}} IV), and a file is verified before it is treated as disposable ({{LAW}} III).
|
|
375
|
+
It receives only the task's deliverables; your working files go in the {{NOTEBOOK}}
|
|
376
|
+
(§6).
|
|
377
|
+
<!-- LEAN:end -->
|
|
354
378
|
|
|
355
379
|
---
|
|
356
380
|
|
|
@@ -387,17 +411,14 @@ durable fact worth sharing across projects goes here via the
|
|
|
387
411
|
[wiki]({{DIR_SKILLS}}/wiki.md) {{SKILL}}; one that matters only to you stays in
|
|
388
412
|
{{MEMORY}} (§5).
|
|
389
413
|
<!-- LEAN:else -->
|
|
390
|
-
{{INTRO_WIKI}}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
`
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
note linked into the graph. A durable fact worth sharing across projects goes
|
|
399
|
-
here via the [wiki]({{DIR_SKILLS}}/wiki.md) {{SKILL}}; one that matters only to
|
|
400
|
-
you stays in {{MEMORY}} (§5).
|
|
414
|
+
{{INTRO_WIKI}} Declared in `wiki.jsonc` beside this AGENT.md (or at `$GENESEED_WIKI`;
|
|
415
|
+
an empty `wikis` list means dormant) — **read it at the start of every session**
|
|
416
|
+
and honour each wiki: load `entries` by `load` mode as in `context.json` (§9), read
|
|
417
|
+
`conventions` before your first write there, drop an unplaceable note in `inbox`,
|
|
418
|
+
never write inside `protected`. It is the user's: wikilinks over bare paths, every
|
|
419
|
+
new note linked in. Cross-project facts go here via the
|
|
420
|
+
[wiki]({{DIR_SKILLS}}/wiki.md) {{SKILL}}; facts that matter only to you stay in
|
|
421
|
+
{{MEMORY}} (§5).
|
|
401
422
|
<!-- LEAN:end -->
|
|
402
423
|
|
|
403
424
|
---
|
|
@@ -406,6 +427,7 @@ you stays in {{MEMORY}} (§5).
|
|
|
406
427
|
|
|
407
428
|
*{{EPI_CONTEXT}}*
|
|
408
429
|
|
|
430
|
+
<!-- LEAN:begin -->
|
|
409
431
|
**Read `context.json` at the start of every session — this is not a
|
|
410
432
|
suggestion.** The harness carries no project-specific knowledge; this manifest,
|
|
411
433
|
**beside this AGENT.md**, is how the project hands you its own. (On OpenCode it
|
|
@@ -422,6 +444,14 @@ you do not work blind, and if you have not loaded it you are not ready to act.
|
|
|
422
444
|
|
|
423
445
|
Each `path` may be absolute or relative to the repository root. The file's own
|
|
424
446
|
comment carries its schema and setup notes.
|
|
447
|
+
<!-- LEAN:else -->
|
|
448
|
+
**Read `context.json` at the start of every session — before your first reply, not
|
|
449
|
+
deferrable.** It sits beside this AGENT.md and is how the project hands you its own
|
|
450
|
+
knowledge (on OpenCode it is loaded for you). `"eager"` entries are read now and are
|
|
451
|
+
**project law for this repo, as binding as this file**; `"lazy"` entries only when
|
|
452
|
+
the task needs them ({{DOCTRINE}} process 3). Schema and setup notes are in the file's
|
|
453
|
+
own comment.
|
|
454
|
+
<!-- LEAN:end -->
|
|
425
455
|
|
|
426
456
|
---
|
|
427
457
|
|
|
@@ -429,12 +459,18 @@ comment carries its schema and setup notes.
|
|
|
429
459
|
|
|
430
460
|
*{{EPI_SCRIPTS}}*
|
|
431
461
|
|
|
462
|
+
<!-- LEAN:begin -->
|
|
432
463
|
{{INTRO_SCRIPTS}} The harness ships a dependency-free CLI (`geneseed
|
|
433
464
|
build|doctor|diff|status|setup|web`, plus `geneseed-hook` for the verbs a host
|
|
434
465
|
fires on its own) that can be wired into your host's hooks or into CI — notably
|
|
435
466
|
`geneseed-hook context`, which injects the project context at session start so
|
|
436
467
|
this read is enforced by the hook rather than left to you. All of it is
|
|
437
468
|
opt-in; the harness works without any of it.
|
|
469
|
+
<!-- LEAN:else -->
|
|
470
|
+
{{INTRO_SCRIPTS}} A dependency-free CLI (`geneseed build|doctor|diff|status|setup|web`,
|
|
471
|
+
plus `geneseed-hook` for host-fired verbs) can wire the gates and the §9 context
|
|
472
|
+
read into your host's hooks or CI. Opt-in; the harness works without it.
|
|
473
|
+
<!-- LEAN:end -->
|
|
438
474
|
|
|
439
475
|
---
|
|
440
476
|
|