geneseed 3.7.0 → 3.9.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 +64 -0
- package/DESIGN.md +13 -10
- package/README.md +5 -5
- package/SETUP.md +2 -2
- package/SHIPPED.md +3 -3
- package/adapters/opencode/GLOBAL-HARNESS-SPEC.md +1 -1
- package/adapters/opencode/README.md +3 -3
- package/adapters/opencode/plugins/geneseed-guard.js +9 -9
- package/adapters/opencode/plugins/geneseed-workflow.js +1 -1
- package/adapters/opencode/workflows/_runtime.js +2 -2
- package/adapters/opencode/workflows/dispatch.js +1 -1
- package/docs/extending.md +72 -40
- package/docs/web/harnesses.md +2 -2
- package/docs/web/plugin-guard.md +2 -2
- package/docs/web/rules.md +1 -1
- package/docs/web/themes-author.md +2 -2
- package/docs/web-ui.md +1 -1
- package/harness.config.json +1 -1
- package/js/README.md +4 -3
- package/js/build/driver.mjs +14 -5
- package/js/build/generate.mjs +90 -41
- package/js/build/render.mjs +6 -3
- package/js/build/source.mjs +105 -1
- package/js/build/stubs.mjs +7 -2
- package/js/cli-table.json +154 -5
- package/js/hosts/hooks.mjs +5 -5
- package/js/hosts/native.mjs +1 -1
- package/js/inspect/checks-authoring.mjs +149 -116
- package/js/inspect/inventory.mjs +1 -2
- package/js/inspect/status.mjs +40 -4
- package/js/ui/tui.mjs +1 -1
- package/js/web/api.mjs +6 -1
- package/package.json +1 -1
- package/registry.json +7 -0
- package/src/AGENT.md.tmpl +19 -15
- package/src/agents/_template.md +3 -3
- package/src/agents/advocate.md +3 -3
- package/src/agents/architect.md +3 -3
- package/src/agents/developer.md +4 -4
- package/src/agents/docs.md +3 -3
- package/src/agents/empiricist.md +4 -4
- package/src/agents/explorer.md +4 -4
- package/src/agents/framer.md +3 -3
- package/src/agents/historian.md +3 -3
- package/src/agents/operator.md +3 -3
- package/src/agents/pragmatist.md +3 -3
- package/src/agents/researcher.md +7 -7
- package/src/agents/reviewer.md +3 -3
- package/src/agents/security.md +3 -3
- package/src/agents/skeptic.md +3 -3
- package/src/agents/steward.md +3 -3
- package/src/agents/tester.md +3 -3
- package/src/agents/user-advocate.md +3 -3
- package/src/agents/visionary.md +3 -3
- package/src/doctrines/README.md +4 -3
- package/src/doctrines/comms.md +4 -4
- package/src/doctrines/craft.md +20 -20
- package/src/doctrines/ops.md +19 -19
- package/src/doctrines/process.md +20 -20
- package/src/doctrines/rigor.md +22 -22
- package/src/laws/universal.md +23 -44
- package/src/memory/README.md +3 -3
- package/src/modes/foreman.md +1 -1
- package/src/notebook/README.md +1 -1
- package/src/ontology/universal.md +7 -9
- package/src/postures/artisan.md +2 -2
- package/src/skills/_self-improvement.md +4 -4
- package/src/skills/_template.md +1 -1
- package/src/skills/brainstorm.md +3 -3
- package/src/skills/bruno-collection-generator.md +6 -6
- package/src/skills/bruno-test-writer.md +5 -5
- package/src/skills/ci-fix.md +4 -4
- package/src/skills/commit.md +11 -10
- package/src/skills/consolidate-memory.md +4 -4
- package/src/skills/council.md +2 -2
- package/src/skills/debug.md +4 -4
- package/src/skills/deps-audit.md +3 -3
- package/src/skills/develop.md +7 -7
- package/src/skills/document-project.md +6 -6
- package/src/skills/explain-changes/SKILL.md +69 -0
- package/src/skills/explain-changes/VENDOR.md +20 -0
- package/src/skills/explain-changes/scripts/render_changes.mjs +558 -0
- package/src/skills/forge-mcp.md +4 -4
- package/src/skills/fresh-eyes.md +4 -4
- package/src/skills/frontend-design.md +1 -1
- package/src/skills/geneseed.md +9 -8
- package/src/skills/git-archaeology.md +5 -5
- package/src/skills/git-rescue.md +7 -7
- package/src/skills/handoff.md +3 -3
- package/src/skills/ingest.md +5 -5
- package/src/skills/migrate.md +8 -8
- package/src/skills/parallel-agents.md +2 -2
- package/src/skills/pipeline.md +2 -2
- package/src/skills/plan.md +3 -3
- package/src/skills/ponytail.md +2 -2
- package/src/skills/profile.md +2 -2
- package/src/skills/prose.md +1 -1
- package/src/skills/prototype.md +1 -1
- package/src/skills/refactor.md +3 -3
- package/src/skills/release.md +5 -5
- package/src/skills/repo-map.md +4 -4
- package/src/skills/research.md +9 -9
- package/src/skills/review-response.md +4 -4
- package/src/skills/rule.md +3 -3
- package/src/skills/security-audit.md +5 -5
- package/src/skills/ship.md +5 -5
- package/src/skills/skill-forge.md +4 -4
- package/src/skills/wiki.md +5 -5
- package/src/skills/workflow.md +2 -3
- package/src/skills/worktree.md +4 -4
- package/themes/_TEMPLATE.json +38 -40
- package/themes/biker.json +38 -40
- package/themes/commentator.json +38 -40
- package/themes/cyberpunk.json +38 -40
- package/themes/gamer.json +38 -40
- package/themes/imperial.json +38 -40
- package/themes/joker.json +38 -40
- package/themes/marvin.json +38 -40
- package/themes/mean.json +38 -40
- package/themes/military.json +38 -40
- package/themes/neutral.json +38 -40
- package/themes/pirate.json +38 -40
- package/themes/sports.json +38 -40
- package/themes/verstappen.json +38 -40
- package/themes/wizard.json +38 -40
- package/web/dist/assets/Activity-DAzV-qQj.js +1 -0
- package/web/dist/assets/ActivityDetail-CwEwMtKl.js +1 -0
- package/web/dist/assets/Dashboard-C2ELBW-X.js +2 -0
- package/web/dist/assets/{Docs-DrJNDPwI.js → Docs-iZrvQP8J.js} +2 -2
- package/web/dist/assets/FilterInput-CVyqmYWM.js +1 -0
- package/web/dist/assets/GreenhouseView-Bq1QmaGO.js +2 -0
- package/web/dist/assets/Installs-BiqR8xdO.js +2 -0
- package/web/dist/assets/JournalView-6wilG5Zd.js +1 -0
- package/web/dist/assets/Laws-D8YXD_XE.js +3 -0
- package/web/dist/assets/Library-DwuTZ2-L.js +1 -0
- package/web/dist/assets/{Markdown-BQAvN8xx.js → Markdown-CkFyfgNf.js} +14 -14
- package/web/dist/assets/OperatorHudView-B-HVBqfY.js +2 -0
- package/web/dist/assets/Personal-BbUTmZQR.js +1 -0
- package/web/dist/assets/Tabs-_pjUFIEI.js +1 -0
- package/web/dist/assets/drawable-CoIulEH2.js +1 -0
- package/web/dist/assets/geist-mono-latin-400-normal-DTRLJnHl.woff2 +0 -0
- package/web/dist/assets/geist-mono-latin-500-normal-YINYabwD.woff2 +0 -0
- package/web/dist/assets/index-BMwSp8BP.js +2 -0
- package/web/dist/assets/index-CDPDk2vj.css +1 -0
- package/web/dist/assets/{react-DbkvxWG5.js → react-B9Z_tXRV.js} +1 -1
- package/web/dist/assets/sections-GalMS0Zv.js +1 -0
- package/web/dist/assets/usePoll-O0sJEw1Y.js +1 -0
- package/web/dist/index.html +4 -4
- package/web/src/pages/Laws.jsx +616 -361
- package/web/dist/assets/Activity-CH-7cPIM.js +0 -1
- package/web/dist/assets/ActivityDetail-DZvz0xS5.js +0 -1
- package/web/dist/assets/CatalogRow-DftXpZHY.js +0 -1
- package/web/dist/assets/Diff-DvwdG0L9.js +0 -2
- package/web/dist/assets/Doctor-OTmwmYZg.js +0 -1
- package/web/dist/assets/FilterInput-BakJLvaV.js +0 -1
- package/web/dist/assets/GreenhouseView-XxrAdriX.js +0 -2
- package/web/dist/assets/Harness-C_LYNk5c.js +0 -1
- package/web/dist/assets/JournalView-BcuyeEaR.js +0 -1
- package/web/dist/assets/Laws-CWHrTKY4.js +0 -3
- package/web/dist/assets/Library-CA9IHghn.js +0 -1
- package/web/dist/assets/OperatorHudView-BMRY1Tt4.js +0 -2
- package/web/dist/assets/Profile-wceET1JY.js +0 -1
- package/web/dist/assets/Rules-BEXvY4Tx.js +0 -1
- package/web/dist/assets/Settings-DS2zF0Ez.js +0 -1
- package/web/dist/assets/Skills-DahlOJuI.js +0 -1
- package/web/dist/assets/StatusBadge-Cg6j-NJL.js +0 -1
- package/web/dist/assets/activity-iWaWVtsR.js +0 -1
- package/web/dist/assets/audiowide-latin-400-normal-6GFCX7ni.woff2 +0 -0
- package/web/dist/assets/drawable-BTHjTzF7.js +0 -1
- package/web/dist/assets/geist-mono-latin-400-normal-DKaoCDn5.woff2 +0 -0
- package/web/dist/assets/geist-mono-latin-500-normal-C3sF8Y1B.woff2 +0 -0
- package/web/dist/assets/geist-mono-latin-600-normal-BHzjB6_C.woff2 +0 -0
- package/web/dist/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
- package/web/dist/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
- package/web/dist/assets/ibm-plex-mono-latin-600-normal-BgSNZQsw.woff2 +0 -0
- package/web/dist/assets/ibm-plex-serif-latin-400-normal-BIGslYFI.woff2 +0 -0
- package/web/dist/assets/ibm-plex-serif-latin-500-normal-REKAfBVG.woff2 +0 -0
- package/web/dist/assets/ibm-plex-serif-latin-600-normal-DAxitmSu.woff2 +0 -0
- package/web/dist/assets/index-AN5Emnrg.js +0 -2
- package/web/dist/assets/index-Ruh93A3A.css +0 -1
- package/web/dist/assets/inter-latin-400-normal-C38fXH4l.woff2 +0 -0
- package/web/dist/assets/inter-latin-500-normal-Cerq10X2.woff2 +0 -0
- package/web/dist/assets/inter-latin-600-normal-LgqL8muc.woff2 +0 -0
- package/web/dist/assets/inter-latin-700-normal-Yt3aPRUw.woff2 +0 -0
- package/web/dist/assets/lawCats-Btjp3Hk_.js +0 -1
- package/web/dist/assets/oswald-latin-400-normal-BAFo8sP8.woff2 +0 -0
- package/web/dist/assets/oswald-latin-500-normal-B-eyT0Dw.woff2 +0 -0
- package/web/dist/assets/oswald-latin-600-normal-plFmSr5g.woff2 +0 -0
- package/web/dist/assets/oswald-latin-700-normal-vy6hmKSr.woff2 +0 -0
- package/web/dist/assets/poppins-latin-400-normal-cpxAROuN.woff2 +0 -0
- package/web/dist/assets/poppins-latin-500-normal-C8OXljZJ.woff2 +0 -0
- package/web/dist/assets/poppins-latin-600-normal-zEkxB9Mr.woff2 +0 -0
- package/web/dist/assets/poppins-latin-700-normal-Qrb0O0WB.woff2 +0 -0
- package/web/dist/assets/rolldown-runtime-CbXtAM7H.js +0 -1
package/docs/web-ui.md
CHANGED
|
@@ -160,7 +160,7 @@ streams the output of background jobs and keeps their history across reloads.
|
|
|
160
160
|
global harness goes dormant (hooks stay silent, the global preamble never loads);
|
|
161
161
|
add or remove one here, the web mirror of `harness exclude add|remove|list`.
|
|
162
162
|
- **Settings** (`#/settings`) — a **Console direction** picker (the visual flavour of the
|
|
163
|
-
console) with a **Dashboard layout** control (Auto follows each theme's designed Status
|
|
163
|
+
console: Cultivar, Operator, Matrix or Atlas) with a **Dashboard layout** control (Auto follows each theme's designed Status
|
|
164
164
|
lens; Cultivar / Greenhouse / Operator / Journal force one regardless of skin), the
|
|
165
165
|
install snapshot (deployed/target/theme/version/memory store), a Maintenance card (PATH
|
|
166
166
|
link/unlink, uninstall), an offline package download, server controls (Stop), and the
|
package/harness.config.json
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
"_comment": "version is a human-readable release label only; the canonical identity of an installed harness is the source fingerprint stamped in .geneseed-version (see `harness version`), not this string.",
|
|
3
3
|
"theme": "neutral",
|
|
4
4
|
"product": "Geneseed",
|
|
5
|
-
"version": "3.
|
|
5
|
+
"version": "3.9.0"
|
|
6
6
|
}
|
package/js/README.md
CHANGED
|
@@ -37,7 +37,7 @@ as a per-host install at project or global scope.
|
|
|
37
37
|
|
|
38
38
|
| module | owns |
|
|
39
39
|
|---|---|
|
|
40
|
-
| `source.mjs` | Where the SOURCE is: `ROOT`/`SRC`/`THEMES`/`CONFIG`, `PACK_ORDER`, rule ids, the cfg object |
|
|
40
|
+
| `source.mjs` | Where the SOURCE is: `ROOT`/`SRC`/`THEMES`/`CONFIG`, `PACK_ORDER`, the rule canon (stable ids → positional numbers, citations → names), the cfg object |
|
|
41
41
|
| `render.mjs` | The pure text pipeline: theme load, `INCLUDE` inlining, CATALOG blocks, `{{TOKENS}}`, dest paths |
|
|
42
42
|
| `bundle.mjs` | `build` — render a theme into a bundle dir, plus the source-completeness refusal. Sits UNDER both emits |
|
|
43
43
|
| `emit-claude.mjs` | The Claude/Bob/Copilot/OpenClaude emit — the render half plus the settings + CLAUDE.md managed-block wire |
|
|
@@ -182,8 +182,9 @@ and inline lists are held against a surface list written out in
|
|
|
182
182
|
|
|
183
183
|
| I want to | open first | then |
|
|
184
184
|
|---|---|---|
|
|
185
|
-
| a doctrine rule | `src/doctrines/<pack>.md` —
|
|
186
|
-
| a law (invariant) | `src/laws/universal.md` —
|
|
185
|
+
| a doctrine rule | `src/doctrines/<pack>.md` — `### {{DOCTRINE:<id>}} <Principle>`; the number is its position | `docs/extending.md` §2a — five files, `--sync-themes`, restyle 14 voices by hand. Author BOTH LEAN halves |
|
|
186
|
+
| a law (invariant) | `src/laws/universal.md` — `### {{LAW:<id>}} <Principle>`; cite by id, never by number | `docs/extending.md` §2c. Author both LEAN halves: the lean footprint ships nothing else |
|
|
187
|
+
| to remove or retire a rule | `docs/extending.md` §2e | `RETIRED_RULE_IDS` in `js/build/source.mjs` — a retired id is never reused |
|
|
187
188
|
| a skill or an agent | `src/skills/<name>.md` or `src/agents/<name>.md` | `docs/extending.md` §3 — eight steps. Write the FILE before the table row or the build aborts |
|
|
188
189
|
| a doctor check | `js/inspect/checks-{build,repo,authoring}.mjs` | register with `ran(...)` in `doctor.mjs`, then a PLANTED FAULT in `tests/unit/harness.test.mjs` |
|
|
189
190
|
| a web endpoint | GET → `js/web/api.mjs`; POST → `js/web/actions.mjs` | declare it in `js/web/routes.mjs`; the partition test probes the real handler |
|
package/js/build/driver.mjs
CHANGED
|
@@ -166,7 +166,7 @@ const VALUED = {
|
|
|
166
166
|
'--theme': 'theme', '--posture': 'posture', '--mode': 'mode',
|
|
167
167
|
'--doctrines': 'doctrines', '--exclude-rules': 'excludeRules',
|
|
168
168
|
'--out': 'out', '--target': 'out', '--emit': 'emit',
|
|
169
|
-
'--footprint': 'footprint', '--root': 'root',
|
|
169
|
+
'--footprint': 'footprint', '--root': 'root', '--config-dir': 'cfgDir',
|
|
170
170
|
};
|
|
171
171
|
const FLAGS = {
|
|
172
172
|
'--sync-themes': 'syncThemes', '--validate-only': 'validateOnly',
|
|
@@ -1032,10 +1032,10 @@ function writeMarkers(markerDir, emit, footprint) {
|
|
|
1032
1032
|
* compares it file by file. The Python spells that `HOSTS[host]["emit_global"](theme,
|
|
1033
1033
|
* out=..., cfg=<tmp>, footprint=...)`, where `cfg` overrides the target the emit would
|
|
1034
1034
|
* otherwise resolve. This is that call, and the override is threaded as `args.cfgDir` through
|
|
1035
|
-
* the
|
|
1036
|
-
*
|
|
1037
|
-
* `
|
|
1038
|
-
*
|
|
1035
|
+
* the `emit*Global` bodies. The same key is also `--config-dir` on the command line: a global
|
|
1036
|
+
* emit never writes to `--out` (there `out` is the legacy bundle a memory store migrates FROM,
|
|
1037
|
+
* and `update` passes it for exactly that), so without the flag there was no way to preview a
|
|
1038
|
+
* global install anywhere but over the live one.
|
|
1039
1039
|
*
|
|
1040
1040
|
* WHICH SIDE OF THE BOUNDARY THIS SITS ON. P3c's rule was that a render CHILD must never
|
|
1041
1041
|
* resolve a config dir, because a child that did would write 135 files into the developer's
|
|
@@ -1187,6 +1187,15 @@ function run(argv) {
|
|
|
1187
1187
|
}
|
|
1188
1188
|
|
|
1189
1189
|
const out = resolveOut(args.out);
|
|
1190
|
+
// Refused rather than ignored on a per-repo emit: an ignored target flag is how a preview
|
|
1191
|
+
// lands on a live install, which is the bug this flag exists to close.
|
|
1192
|
+
if (args.cfgDir !== undefined) {
|
|
1193
|
+
if (!args.emit.endsWith('-global')) {
|
|
1194
|
+
die(2, `argument --config-dir: only applies to a -global emit (got --emit ${args.emit}); `
|
|
1195
|
+
+ 'a per-repo emit writes under --out/--root');
|
|
1196
|
+
}
|
|
1197
|
+
args.cfgDir = resolveOut(args.cfgDir);
|
|
1198
|
+
}
|
|
1190
1199
|
const cfg = makeCfg(args);
|
|
1191
1200
|
|
|
1192
1201
|
// The marker directory is the emit's TARGET, which for a global emit is the config dir it
|
package/js/build/generate.mjs
CHANGED
|
@@ -80,12 +80,28 @@ function sysExit(msg) {
|
|
|
80
80
|
* Calling the driver in-process keeps that: `resolveOut` resolves against `process.cwd()`
|
|
81
81
|
* the same way. `build/the-bundle-follows-cwd-not-the-checkout` is the cell.
|
|
82
82
|
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
83
|
+
* `geneseed build` used to forward `--theme` alone, which made `setup`'s own off-TTY hint
|
|
84
|
+
* (`geneseed build --emit … --theme …`) a usage error and left every other axis reachable
|
|
85
|
+
* only through the undocumented `geneseed-build`. Values are forwarded unchecked: the driver
|
|
86
|
+
* owns the choices, so the table does not repeat them.
|
|
87
|
+
*
|
|
88
|
+
* `--validate-only` is NOT one of these: it lives on `geneseed validate` only — that verb's
|
|
89
|
+
* source-tree half is the doctor, which starts a process, and this command's whole closure
|
|
90
|
+
* (`bin/geneseed-cli.mjs` → here → `driverMain`) is under the same transitive `child_process`
|
|
91
|
+
* ban as `bin/build-driver.mjs` itself. `driver.mjs`'s `run()` refuses the flag unconditionally
|
|
92
|
+
* for exactly that reason.
|
|
86
93
|
*/
|
|
94
|
+
const BUILD_FORWARD = [
|
|
95
|
+
['theme', '--theme'], ['emit', '--emit'], ['footprint', '--footprint'],
|
|
96
|
+
['posture', '--posture'], ['mode', '--mode'], ['doctrines', '--doctrines'],
|
|
97
|
+
['excludeRules', '--exclude-rules'], ['out', '--out'], ['root', '--root'],
|
|
98
|
+
['configDir', '--config-dir'],
|
|
99
|
+
];
|
|
100
|
+
|
|
87
101
|
export function cmdBuild(args) {
|
|
88
|
-
|
|
102
|
+
const argv = [];
|
|
103
|
+
for (const [key, flag] of BUILD_FORWARD) if (args[key] != null) argv.push(flag, args[key]);
|
|
104
|
+
return driverMain(argv);
|
|
89
105
|
}
|
|
90
106
|
|
|
91
107
|
// --------------------------------------------------------------------------------------
|
|
@@ -161,6 +177,66 @@ export const DEFAULT_EMIT = new Map([
|
|
|
161
177
|
['openclaude global', 'openclaude-global'], ['openclaude project', 'openclaude'],
|
|
162
178
|
]);
|
|
163
179
|
|
|
180
|
+
/**
|
|
181
|
+
* Everything `rebuild-all` reads back off one install, and the argv that re-emits it unchanged.
|
|
182
|
+
*
|
|
183
|
+
* EXTRACTED SO `status` SHOWS WHAT A REBUILD WOULD USE, not a second reading of the same
|
|
184
|
+
* markers that could drift from this one. An agent changing one setting takes `argv`, edits one
|
|
185
|
+
* flag and runs it; every flag it did not touch has to be the install's own, or the "one
|
|
186
|
+
* change" silently resets the rest — which is exactly the failure the seven read-backs below
|
|
187
|
+
* exist to prevent.
|
|
188
|
+
*
|
|
189
|
+
* THE MARKER IS TRUSTED ONLY FOR ITS OWN HOST. There is one `.geneseed-emit` per root and the
|
|
190
|
+
* last deploy wins, so in a dual-host repo (`.opencode/` and `.claude/` in one cwd) the other
|
|
191
|
+
* host's row would otherwise rebuild with the WRONG emit — turning a Claude install into an
|
|
192
|
+
* OpenCode one. `rebuild-all/a-dual-host-repo-rebuilds-each-row-in-its-own-emit` is the cell.
|
|
193
|
+
*/
|
|
194
|
+
export function installProfile(host, scope, root) {
|
|
195
|
+
const raw = readMaybe(path.join(root, '.geneseed-emit'));
|
|
196
|
+
let marker = raw === null ? '' : raw.trim();
|
|
197
|
+
if (marker && (EMIT_HOST_SCOPE.get(marker) ?? ['', ''])[0] !== host) marker = '';
|
|
198
|
+
const emit = marker || DEFAULT_EMIT.get(`${host} ${scope}`) || 'opencode-global';
|
|
199
|
+
const theme = themeOfDir(root) || defaultTheme();
|
|
200
|
+
const footprint = footprintOfDir(root);
|
|
201
|
+
const posture = postureOfDir(root) || defaultPosture();
|
|
202
|
+
const mode = modeOfDir(root) || defaultMode();
|
|
203
|
+
// A rebuild that defaulted the pack selection would re-emit an install into a constitution
|
|
204
|
+
// its owner did not choose — and in one direction that is not merely surprising: dropping
|
|
205
|
+
// the process pack takes the commit/push consent RULES out of AGENT.md while
|
|
206
|
+
// `claudeHookGroups` keeps the gate wired, or the reverse. ⚠ AND THE FALLBACK IS NOT
|
|
207
|
+
// `harness.config.json`: a pre-migration carrier has no marker, and resolving that silence
|
|
208
|
+
// out of a machine-wide config narrows every such install on its first upgrade.
|
|
209
|
+
// `doctrinesForBuild` resolves unknown to ALL packs — see its docblock.
|
|
210
|
+
const doctrines = doctrinesForBuild(root);
|
|
211
|
+
// The per-rule axis is preserved for the same reason and with sharper teeth: an upgrade that
|
|
212
|
+
// dropped it would quietly hand back every rule its owner switched off, and `process 5` is
|
|
213
|
+
// one of them. `null` (no marker) stays `null` so the argv omits the flag, as it always did.
|
|
214
|
+
const excludeRules = excludedRulesOfDir(root);
|
|
215
|
+
const out = scope === 'global' ? null : root;
|
|
216
|
+
const argv = setupBuildArgs(theme, emit, out, out, footprint, posture, mode, doctrines,
|
|
217
|
+
PACK_ORDER, excludeRules);
|
|
218
|
+
// A GLOBAL ROW NAMES ITS OWN DIR. `root` came from the host's config-dir resolver, which
|
|
219
|
+
// honours env overrides (`OPENCODE_CONFIG_DIR`, `CLAUDE_CONFIG_DIR`…); without the flag the
|
|
220
|
+
// same argv run from a shell lacking that variable re-emits into the DEFAULT dir — a second
|
|
221
|
+
// install beside the one it was meant to rebuild. Appended, so the argv's head is unchanged.
|
|
222
|
+
if (scope === 'global') argv.push('--config-dir', root);
|
|
223
|
+
return {
|
|
224
|
+
host, scope, root, state: installState(root, host, scope),
|
|
225
|
+
emit, theme, footprint, posture, mode, doctrines, excludeRules, argv,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* `argv` as one pasteable command, using the `geneseed build` launcher rather than the raw
|
|
231
|
+
* generator: `geneseed-build` is only on PATH for an npm global install, while a git-clone
|
|
232
|
+
* install has only the `geneseed` launcher, and `geneseed build` now forwards every flag
|
|
233
|
+
* `setupBuildArgs` produces. Only whitespace is quoted — a path holding shell metacharacters
|
|
234
|
+
* (`$`, `&`) would need quoting by hand.
|
|
235
|
+
*/
|
|
236
|
+
export function rebuildCommand(argv) {
|
|
237
|
+
return ['geneseed', 'build', ...argv.map((a) => (/\s/.test(a) ? `"${a}"` : a))].join(' ');
|
|
238
|
+
}
|
|
239
|
+
|
|
164
240
|
/**
|
|
165
241
|
* `_harness_build.cmd_rebuild_all` — re-emit every ACTIVE install in place, best-effort.
|
|
166
242
|
*
|
|
@@ -172,16 +248,12 @@ export const DEFAULT_EMIT = new Map([
|
|
|
172
248
|
* `main` converts, and `rebuild-all/one-broken-install-does-not-stop-the-rest` is the cell
|
|
173
249
|
* that fails if either half is undone.
|
|
174
250
|
*
|
|
175
|
-
*
|
|
176
|
-
* come from markers; posture and mode are detected from the `**<Name>**`
|
|
177
|
-
* deployed carrier's prose, because they were never markered. A rebuild that
|
|
178
|
-
* of them would silently re-emit an install into something its owner did not
|
|
179
|
-
* is the same failure mode `_footprint_of_dir`'s WARN exists to prevent, at a
|
|
180
|
-
*
|
|
181
|
-
* THE MARKER IS TRUSTED ONLY FOR ITS OWN HOST. There is one `.geneseed-emit` per root and the
|
|
182
|
-
* last deploy wins, so in a dual-host repo (`.opencode/` and `.claude/` in one cwd) the other
|
|
183
|
-
* host's row would otherwise rebuild with the WRONG emit — turning a Claude install into an
|
|
184
|
-
* OpenCode one. `rebuild-all/a-dual-host-repo-rebuilds-each-row-in-its-own-emit` is the cell.
|
|
251
|
+
* SEVEN VALUES ARE READ BACK OFF EACH INSTALL, not defaulted — see `installProfile`. Theme and
|
|
252
|
+
* emit and footprint come from markers; posture and mode are detected from the `**<Name>**`
|
|
253
|
+
* lead in the deployed carrier's prose, because they were never markered. A rebuild that
|
|
254
|
+
* defaulted any of them would silently re-emit an install into something its owner did not
|
|
255
|
+
* choose — which is the same failure mode `_footprint_of_dir`'s WARN exists to prevent, at a
|
|
256
|
+
* larger scale.
|
|
185
257
|
*/
|
|
186
258
|
export function cmdRebuildAll() {
|
|
187
259
|
const targets = installTargets().filter(([h, s, r]) => installState(r, h, s) === 'active');
|
|
@@ -191,34 +263,11 @@ export function cmdRebuildAll() {
|
|
|
191
263
|
}
|
|
192
264
|
const failures = [];
|
|
193
265
|
for (const [host, scope, root] of targets) {
|
|
194
|
-
const
|
|
195
|
-
let marker = raw === null ? '' : raw.trim();
|
|
196
|
-
if (marker && (EMIT_HOST_SCOPE.get(marker) ?? ['', ''])[0] !== host) marker = '';
|
|
197
|
-
const emit = marker || DEFAULT_EMIT.get(`${host} ${scope}`) || 'opencode-global';
|
|
198
|
-
const theme = themeOfDir(root) || defaultTheme();
|
|
199
|
-
const footprint = footprintOfDir(root);
|
|
200
|
-
const posture = postureOfDir(root) || defaultPosture();
|
|
201
|
-
const mode = modeOfDir(root) || defaultMode();
|
|
202
|
-
const out = scope === 'global' ? null : root;
|
|
203
|
-
// SIX values now, not five. A rebuild that defaulted the pack selection would re-emit an
|
|
204
|
-
// install into a constitution its owner did not choose — and in one direction that is not
|
|
205
|
-
// merely surprising: dropping the process pack takes the commit/push consent RULES out of
|
|
206
|
-
// AGENT.md while `claudeHookGroups` keeps the gate wired, or the reverse. ⚠ AND THE
|
|
207
|
-
// FALLBACK IS NOT `harness.config.json`: a pre-migration carrier has no marker, and
|
|
208
|
-
// resolving that silence out of a machine-wide config narrows every such install on its
|
|
209
|
-
// first upgrade. `doctrinesForBuild` resolves unknown to ALL packs — see its docblock.
|
|
210
|
-
const doctrines = doctrinesForBuild(root);
|
|
211
|
-
// SEVEN. The per-rule axis is preserved for the same reason and with sharper teeth: an
|
|
212
|
-
// upgrade that dropped it would quietly hand back every rule its owner switched off, and
|
|
213
|
-
// `process 5` is one of them — the consent gate would return to a harness that had been
|
|
214
|
-
// built without it. No `doctrinesForBuild` rescue is needed here; see `apiInstallCmd`.
|
|
215
|
-
const excludeRules = excludedRulesOfDir(root);
|
|
216
|
-
const argv = setupBuildArgs(theme, emit, out, out, footprint, posture, mode, doctrines,
|
|
217
|
-
PACK_ORDER, excludeRules);
|
|
266
|
+
const p = installProfile(host, scope, root);
|
|
218
267
|
const label = `${host}:${scope} (${root})`;
|
|
219
|
-
printOut(`[rebuild-all] ${label}: theme=${theme} emit=${emit} footprint=${footprint} `
|
|
220
|
-
+ `posture=${posture} mode=${mode}\n`);
|
|
221
|
-
const rc = driverMain(argv);
|
|
268
|
+
printOut(`[rebuild-all] ${label}: theme=${p.theme} emit=${p.emit} footprint=${p.footprint} `
|
|
269
|
+
+ `posture=${p.posture} mode=${p.mode}\n`);
|
|
270
|
+
const rc = driverMain(p.argv);
|
|
222
271
|
if (rc !== 0) {
|
|
223
272
|
failures.push(label);
|
|
224
273
|
printErr(`[rebuild-all] FAILED ${label} (exit ${rc})\n`);
|
package/js/build/render.mjs
CHANGED
|
@@ -34,7 +34,7 @@ import { readText } from '../lib/fs.mjs';
|
|
|
34
34
|
import { parseJson } from '../lib/json.mjs';
|
|
35
35
|
import { normcase, comparePaths } from '../lib/paths.mjs';
|
|
36
36
|
// `js/build/source.mjs` imports nothing but node builtins, so this direction cannot cycle.
|
|
37
|
-
import { PACK_ORDER, readPackText } from './source.mjs';
|
|
37
|
+
import { PACK_ORDER, readPackText, resolveRuleIds } from './source.mjs';
|
|
38
38
|
|
|
39
39
|
/** Document STRUCTURE is theme-INDEPENDENT — mirrors `_build_render.STRUCTURE`. */
|
|
40
40
|
export const STRUCTURE = {
|
|
@@ -216,7 +216,8 @@ function readSource(cfg, p) {
|
|
|
216
216
|
let seen = SRC_TEXT.get(cfg);
|
|
217
217
|
if (!seen) SRC_TEXT.set(cfg, (seen = new Map()));
|
|
218
218
|
let text = seen.get(p);
|
|
219
|
-
|
|
219
|
+
// Rule ids resolve here, once per file, so every stage after sees the addressed form.
|
|
220
|
+
if (text === undefined) seen.set(p, (text = resolveRuleIds(readText(p), cfg.src)));
|
|
220
221
|
return text;
|
|
221
222
|
}
|
|
222
223
|
|
|
@@ -250,7 +251,9 @@ export function renderFile(cfg, filePath, theme, footprint = 'full', lawsPrefix
|
|
|
250
251
|
function registerBody(cfg, theme, dir, selected, fallback) {
|
|
251
252
|
for (const name of [selected, fallback]) {
|
|
252
253
|
const p = path.join(cfg.src, dir, `${name}.md`);
|
|
253
|
-
if (existsSync(p) && statSync(p).isFile())
|
|
254
|
+
if (existsSync(p) && statSync(p).isFile()) {
|
|
255
|
+
return substitute(resolveRuleIds(readText(p), cfg.src), theme).trim();
|
|
256
|
+
}
|
|
254
257
|
}
|
|
255
258
|
return '';
|
|
256
259
|
}
|
package/js/build/source.mjs
CHANGED
|
@@ -37,7 +37,7 @@ import path from 'node:path';
|
|
|
37
37
|
* on raw or folded text, so one behaviour serves both.
|
|
38
38
|
*/
|
|
39
39
|
const _packTexts = new Map();
|
|
40
|
-
export function
|
|
40
|
+
export function readRawText(file) {
|
|
41
41
|
const key = path.resolve(file);
|
|
42
42
|
const mtime = statSync(file).mtimeMs;
|
|
43
43
|
const hit = _packTexts.get(key);
|
|
@@ -47,6 +47,110 @@ export function readPackText(file) {
|
|
|
47
47
|
return text;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* A doctrine pack as the render sees it: ids resolved, so the heading reads
|
|
52
|
+
* `### {{DOCTRINE}} process 5 — {{DOC_CONSENT_BEFORE_PUSH}}` again. Every reader that keys on the
|
|
53
|
+
* `<pack> <n>` address (`knownRuleIds`, the exclusion filter, the survives gate) goes through
|
|
54
|
+
* here and keeps working unchanged — the address is computed, only the source stopped typing it.
|
|
55
|
+
*/
|
|
56
|
+
export function readPackText(file) {
|
|
57
|
+
return resolveRuleIds(readRawText(file), path.dirname(path.dirname(file)));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* STABLE RULE IDS. Every law and doctrine rule is DECLARED by its heading —
|
|
62
|
+
* `### {{LAW:verify-before-asserting}} Verify Before Asserting` — and CITED by its id —
|
|
63
|
+
* `{{LAW:verify-before-asserting}}`. The id is permanent; the number is not stored anywhere. It
|
|
64
|
+
* is the rule's position (Roman in `laws/universal.md`, Arabic within its pack), computed here
|
|
65
|
+
* at render time, so removing or reordering a rule renumbers every heading after it and rewires
|
|
66
|
+
* nothing: no citation ever named the number.
|
|
67
|
+
*
|
|
68
|
+
* WHY CITATIONS RENDER AS THE NAME, NOT THE NUMBER. A citation is the one place a number would
|
|
69
|
+
* leak into text that outlives the build — an agent reads "Law III", writes "Law III" into its
|
|
70
|
+
* memory, and the memory is wrong the day the canon moves. The rendered harness models what it
|
|
71
|
+
* asks of the agent: a rule is cited by its principle, in English, identical in every voice. The
|
|
72
|
+
* number survives only where it is structural — the rule's own heading.
|
|
73
|
+
*
|
|
74
|
+
* Retired ids live in `RETIRED_RULE_IDS` so the doctor can refuse their reuse: an id that meant
|
|
75
|
+
* one thing in someone's notes must never come back meaning another.
|
|
76
|
+
*/
|
|
77
|
+
const DECL_RE = /^### \{\{(LAW|DOCTRINE):([a-z0-9]+(?:-[a-z0-9]+)*)\}\}[ \t]+(\S[^\n]*?)[ \t]*$/gm;
|
|
78
|
+
const CITE_RE = /\{\{(LAW|DOCTRINE):([a-z0-9-]+)\}\}/g;
|
|
79
|
+
export const RULE_ID_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Ids that no longer name a live rule, and must never be declared again. `liveAs` names the one
|
|
83
|
+
* tier that may still declare it — `external-gate` was Law IX before it became a rigor rule, so
|
|
84
|
+
* the doctrine keeps it and `{{LAW:external-gate}}` is refused; `null` means gone for good.
|
|
85
|
+
*/
|
|
86
|
+
export const RETIRED_RULE_IDS = {
|
|
87
|
+
'external-gate': { was: 'Law IX', liveAs: 'DOCTRINE', note: 'moved to the rigor pack' },
|
|
88
|
+
'absence-is-a-claim': { was: 'Law XI', liveAs: null, note: 'folded into verify-before-asserting' },
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const ROMAN = [[1000, 'M'], [900, 'CM'], [500, 'D'], [400, 'CD'], [100, 'C'], [90, 'XC'],
|
|
92
|
+
[50, 'L'], [40, 'XL'], [10, 'X'], [9, 'IX'], [5, 'V'], [4, 'IV'], [1, 'I']];
|
|
93
|
+
export function toRoman(n) {
|
|
94
|
+
let out = '';
|
|
95
|
+
for (const [v, r] of ROMAN) while (n >= v) { out += r; n -= v; }
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** `sealed-secrets` -> `LEX_SEALED_SECRETS`; a doctrine rule's title key is `DOC_<ID>`. */
|
|
100
|
+
export const titleKey = (kind, id) =>
|
|
101
|
+
`${kind === 'LAW' ? 'LEX' : 'DOC'}_${id.toUpperCase().replaceAll('-', '_')}`;
|
|
102
|
+
|
|
103
|
+
/** The files that may declare rules, in canon order: `[kind, pack|null, file]`. */
|
|
104
|
+
export function canonFiles(src = SRC) {
|
|
105
|
+
return [['LAW', null, path.join(src, 'laws', 'universal.md')],
|
|
106
|
+
...PACK_ORDER.map((p) => ['DOCTRINE', p, path.join(src, 'doctrines', `${p}.md`)])];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const _canons = new Map();
|
|
110
|
+
/**
|
|
111
|
+
* Every declared rule, in render order: `{ kind, pack, id, name, n, label }`, where `label` is
|
|
112
|
+
* the address the heading renders (`III`, `process 5`). Memoised on the raw texts' identity —
|
|
113
|
+
* `readRawText` hands back the same string until a file's mtime moves.
|
|
114
|
+
*/
|
|
115
|
+
export function ruleCanon(src = SRC) {
|
|
116
|
+
const files = canonFiles(src);
|
|
117
|
+
const texts = files.map(([, , f]) => { try { return readRawText(f); } catch { return ''; } });
|
|
118
|
+
const key = path.resolve(src);
|
|
119
|
+
const hit = _canons.get(key);
|
|
120
|
+
if (hit && hit.texts.every((t, i) => t === texts[i])) return hit.canon;
|
|
121
|
+
const rules = [];
|
|
122
|
+
files.forEach(([kind, pack], i) => {
|
|
123
|
+
let n = 0;
|
|
124
|
+
for (const m of texts[i].matchAll(DECL_RE)) {
|
|
125
|
+
if (m[1] !== kind) continue; // a wrong-tier declaration is the doctor's to name
|
|
126
|
+
n += 1;
|
|
127
|
+
rules.push({ kind, pack, id: m[2], name: m[3], n, label: kind === 'LAW' ? toRoman(n) : `${pack} ${n}` });
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
const byId = new Map();
|
|
131
|
+
for (const r of rules) if (!byId.has(r.id)) byId.set(r.id, r);
|
|
132
|
+
const canon = { rules, byId };
|
|
133
|
+
_canons.set(key, { texts, canon });
|
|
134
|
+
return canon;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Resolve every declaration and citation in `text` against the canon under `src`. An unknown id
|
|
139
|
+
* is left exactly as written — visible in the output, and named by the doctor — rather than
|
|
140
|
+
* guessed at.
|
|
141
|
+
*/
|
|
142
|
+
export function resolveRuleIds(text, src = SRC) {
|
|
143
|
+
if (!text.includes('{{LAW:') && !text.includes('{{DOCTRINE:')) return text;
|
|
144
|
+
const { byId } = ruleCanon(src);
|
|
145
|
+
const known = (kind, id) => { const r = byId.get(id); return r && r.kind === kind ? r : null; };
|
|
146
|
+
return text
|
|
147
|
+
.replace(DECL_RE, (whole, kind, id) => {
|
|
148
|
+
const r = known(kind, id);
|
|
149
|
+
return r ? `### {{${kind}}} ${r.label} — {{${titleKey(kind, id)}}}` : whole;
|
|
150
|
+
})
|
|
151
|
+
.replace(CITE_RE, (whole, kind, id) => { const r = known(kind, id); return r ? `*${r.name}*` : whole; });
|
|
152
|
+
}
|
|
153
|
+
|
|
50
154
|
/**
|
|
51
155
|
* `_build_core.ROOT` — the checkout, from this file's own location (`js/..`).
|
|
52
156
|
*
|
package/js/build/stubs.mjs
CHANGED
|
@@ -137,8 +137,13 @@ the project's own. Follow the root \`AGENTS.md\`.
|
|
|
137
137
|
/** `_build_render.PROFILE_FILE`. */
|
|
138
138
|
const PROFILE_FILE = 'PROFILE.md';
|
|
139
139
|
|
|
140
|
-
/**
|
|
141
|
-
|
|
140
|
+
/**
|
|
141
|
+
* `_build_render.PROFILE_STUB`.
|
|
142
|
+
*
|
|
143
|
+
* Exported for the web console: `/api/profile` answers `seeded: true` while PROFILE.md is
|
|
144
|
+
* still byte-for-byte this text, which is how the Overview knows to ask for it to be filled in.
|
|
145
|
+
*/
|
|
146
|
+
export const PROFILE_STUB = `\
|
|
142
147
|
# Your profile
|
|
143
148
|
|
|
144
149
|
Who you are and how you like to work — so the agent can meet you where you are
|
package/js/cli-table.json
CHANGED
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
},
|
|
55
55
|
{
|
|
56
56
|
"name": "build",
|
|
57
|
-
"help": "render
|
|
57
|
+
"help": "render the harness (same flags as geneseed-build; an omitted flag takes the generator default, not the install's — see geneseed status --json for an install's own; dry run: geneseed validate)",
|
|
58
58
|
"description": "",
|
|
59
59
|
"positionals": [],
|
|
60
60
|
"options": [
|
|
@@ -72,6 +72,141 @@
|
|
|
72
72
|
"is_flag": false,
|
|
73
73
|
"type": null,
|
|
74
74
|
"hidden": false
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"names": [
|
|
78
|
+
"--emit"
|
|
79
|
+
],
|
|
80
|
+
"dest": "emit",
|
|
81
|
+
"metavar": null,
|
|
82
|
+
"help": "",
|
|
83
|
+
"choices": null,
|
|
84
|
+
"default": null,
|
|
85
|
+
"required": false,
|
|
86
|
+
"nargs": null,
|
|
87
|
+
"is_flag": false,
|
|
88
|
+
"type": null,
|
|
89
|
+
"hidden": false
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"names": [
|
|
93
|
+
"--footprint"
|
|
94
|
+
],
|
|
95
|
+
"dest": "footprint",
|
|
96
|
+
"metavar": null,
|
|
97
|
+
"help": "",
|
|
98
|
+
"choices": null,
|
|
99
|
+
"default": null,
|
|
100
|
+
"required": false,
|
|
101
|
+
"nargs": null,
|
|
102
|
+
"is_flag": false,
|
|
103
|
+
"type": null,
|
|
104
|
+
"hidden": false
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"names": [
|
|
108
|
+
"--posture"
|
|
109
|
+
],
|
|
110
|
+
"dest": "posture",
|
|
111
|
+
"metavar": null,
|
|
112
|
+
"help": "",
|
|
113
|
+
"choices": null,
|
|
114
|
+
"default": null,
|
|
115
|
+
"required": false,
|
|
116
|
+
"nargs": null,
|
|
117
|
+
"is_flag": false,
|
|
118
|
+
"type": null,
|
|
119
|
+
"hidden": false
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"names": [
|
|
123
|
+
"--mode"
|
|
124
|
+
],
|
|
125
|
+
"dest": "mode",
|
|
126
|
+
"metavar": null,
|
|
127
|
+
"help": "",
|
|
128
|
+
"choices": null,
|
|
129
|
+
"default": null,
|
|
130
|
+
"required": false,
|
|
131
|
+
"nargs": null,
|
|
132
|
+
"is_flag": false,
|
|
133
|
+
"type": null,
|
|
134
|
+
"hidden": false
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
"names": [
|
|
138
|
+
"--doctrines"
|
|
139
|
+
],
|
|
140
|
+
"dest": "doctrines",
|
|
141
|
+
"metavar": null,
|
|
142
|
+
"help": "",
|
|
143
|
+
"choices": null,
|
|
144
|
+
"default": null,
|
|
145
|
+
"required": false,
|
|
146
|
+
"nargs": null,
|
|
147
|
+
"is_flag": false,
|
|
148
|
+
"type": null,
|
|
149
|
+
"hidden": false
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"names": [
|
|
153
|
+
"--exclude-rules"
|
|
154
|
+
],
|
|
155
|
+
"dest": "exclude_rules",
|
|
156
|
+
"metavar": null,
|
|
157
|
+
"help": "",
|
|
158
|
+
"choices": null,
|
|
159
|
+
"default": null,
|
|
160
|
+
"required": false,
|
|
161
|
+
"nargs": null,
|
|
162
|
+
"is_flag": false,
|
|
163
|
+
"type": null,
|
|
164
|
+
"hidden": false
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"names": [
|
|
168
|
+
"--out"
|
|
169
|
+
],
|
|
170
|
+
"dest": "out",
|
|
171
|
+
"metavar": null,
|
|
172
|
+
"help": "",
|
|
173
|
+
"choices": null,
|
|
174
|
+
"default": null,
|
|
175
|
+
"required": false,
|
|
176
|
+
"nargs": null,
|
|
177
|
+
"is_flag": false,
|
|
178
|
+
"type": null,
|
|
179
|
+
"hidden": false
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"names": [
|
|
183
|
+
"--root"
|
|
184
|
+
],
|
|
185
|
+
"dest": "root",
|
|
186
|
+
"metavar": null,
|
|
187
|
+
"help": "",
|
|
188
|
+
"choices": null,
|
|
189
|
+
"default": null,
|
|
190
|
+
"required": false,
|
|
191
|
+
"nargs": null,
|
|
192
|
+
"is_flag": false,
|
|
193
|
+
"type": null,
|
|
194
|
+
"hidden": false
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
"names": [
|
|
198
|
+
"--config-dir"
|
|
199
|
+
],
|
|
200
|
+
"dest": "config_dir",
|
|
201
|
+
"metavar": null,
|
|
202
|
+
"help": "the config dir a -global emit writes to (default: the host's resolved one)",
|
|
203
|
+
"choices": null,
|
|
204
|
+
"default": null,
|
|
205
|
+
"required": false,
|
|
206
|
+
"nargs": null,
|
|
207
|
+
"is_flag": false,
|
|
208
|
+
"type": null,
|
|
209
|
+
"hidden": false
|
|
75
210
|
}
|
|
76
211
|
],
|
|
77
212
|
"mutex": []
|
|
@@ -353,7 +488,7 @@
|
|
|
353
488
|
},
|
|
354
489
|
{
|
|
355
490
|
"name": "git-gate",
|
|
356
|
-
"help": "PreToolUse hook: ASK before a history-discarding git act (
|
|
491
|
+
"help": "PreToolUse hook: ASK before a history-discarding git act (Deletion Is Deliberate) and before every git commit/push (Consent Before Push backstop)",
|
|
357
492
|
"description": "",
|
|
358
493
|
"positionals": [],
|
|
359
494
|
"options": [
|
|
@@ -393,7 +528,7 @@
|
|
|
393
528
|
],
|
|
394
529
|
"dest": "no_consent",
|
|
395
530
|
"metavar": null,
|
|
396
|
-
"help": "skip the
|
|
531
|
+
"help": "skip the Consent Before Push commit/push ask (set by the emit when the process pack or that rule is off); Deletion Is Deliberate still asks",
|
|
397
532
|
"choices": null,
|
|
398
533
|
"default": false,
|
|
399
534
|
"required": false,
|
|
@@ -631,7 +766,7 @@
|
|
|
631
766
|
},
|
|
632
767
|
{
|
|
633
768
|
"name": "rule-gate",
|
|
634
|
-
"help": "PreToolUse hook: force an ASK before any write to user-rules.md or memory (
|
|
769
|
+
"help": "PreToolUse hook: force an ASK before any write to user-rules.md or memory (Persist Insight backstop)",
|
|
635
770
|
"description": "",
|
|
636
771
|
"positionals": [],
|
|
637
772
|
"options": [
|
|
@@ -681,7 +816,21 @@
|
|
|
681
816
|
"help": "print the install dashboard as text (theme, mode, counts, memory, version)",
|
|
682
817
|
"description": "",
|
|
683
818
|
"positionals": [],
|
|
684
|
-
"options": [
|
|
819
|
+
"options": [
|
|
820
|
+
{
|
|
821
|
+
"names": ["--json"],
|
|
822
|
+
"dest": "json",
|
|
823
|
+
"metavar": null,
|
|
824
|
+
"help": "print the status payload as JSON — every install with its settings and the geneseed build command that rebuilds it",
|
|
825
|
+
"choices": null,
|
|
826
|
+
"default": false,
|
|
827
|
+
"required": false,
|
|
828
|
+
"nargs": "0",
|
|
829
|
+
"is_flag": true,
|
|
830
|
+
"type": null,
|
|
831
|
+
"hidden": false
|
|
832
|
+
}
|
|
833
|
+
],
|
|
685
834
|
"mutex": []
|
|
686
835
|
},
|
|
687
836
|
{
|
package/js/hosts/hooks.mjs
CHANGED
|
@@ -700,14 +700,14 @@ function gitDecide(args, payload) {
|
|
|
700
700
|
const command = ((payload && payload.tool_input) || {}).command;
|
|
701
701
|
if (typeof command !== 'string') return 0;
|
|
702
702
|
if (DESTRUCTIVE_GIT_RE.test(command)) {
|
|
703
|
-
return ask(args, 'git-gate', 'law-4', 'Geneseed
|
|
703
|
+
return ask(args, 'git-gate', 'law-4', 'Geneseed (Deletion Is Deliberate) \u2014 a history-rewriting or '
|
|
704
704
|
+ 'discarding git act needs confirmation bound to this specific command');
|
|
705
705
|
}
|
|
706
706
|
// `--no-consent`: the process pack (or process 5 alone) is off, so the commit/push ask has
|
|
707
707
|
// no rule behind it — but Law IV above is universal and has already run.
|
|
708
708
|
if (args.noConsent || !GIT_GATE_RE.test(command)) return 0;
|
|
709
|
-
return ask(args, 'git-gate', 'process-5', 'Geneseed
|
|
710
|
-
+ 'commit/push needs explicit approval');
|
|
709
|
+
return ask(args, 'git-gate', 'process-5', 'Geneseed (Consent Before Push) \u2014 every git '
|
|
710
|
+
+ 'commit/push needs explicit approval; to see the change first, use the explain-changes skill');
|
|
711
711
|
}
|
|
712
712
|
export const cmdGitGate = guardGate(gitGate, 'git-gate');
|
|
713
713
|
|
|
@@ -764,13 +764,13 @@ function ruleDecide(args, payload) {
|
|
|
764
764
|
const body = [ti.content, ti.new_string, ti.file_text, ti.new_str]
|
|
765
765
|
.find((v) => typeof v === 'string') || '';
|
|
766
766
|
if (body && !DOTENV_RE.test(p) && SECRET_RE.test(body)) {
|
|
767
|
-
return ask(args, 'rule-gate', 'law-1', `Geneseed
|
|
767
|
+
return ask(args, 'rule-gate', 'law-1', `Geneseed (Sealed Secrets) — ${p} would carry a `
|
|
768
768
|
+ 'credential-shaped string. Secrets live in .env or a secret manager, never in a '
|
|
769
769
|
+ 'tracked file.');
|
|
770
770
|
}
|
|
771
771
|
const target = ruleGateTarget(p, args.root);
|
|
772
772
|
if (!target) return 0;
|
|
773
|
-
return ask(args, 'rule-gate', 'process-1', `Geneseed
|
|
773
|
+
return ask(args, 'rule-gate', 'process-1', `Geneseed (Persist Insight) \u2014 writing to `
|
|
774
774
|
+ `${target}: a standing rule, or a fact to remember? That choice is the user's. Run the `
|
|
775
775
|
+ 'rule skill first.');
|
|
776
776
|
}
|
package/js/hosts/native.mjs
CHANGED
|
@@ -34,7 +34,7 @@ import { writeText, copyFile, readText } from '../lib/fs.mjs';
|
|
|
34
34
|
import { jsonDumps, parseJson, formatValue, formatRepr, isTruthy, isDict } from '../lib/json.mjs';
|
|
35
35
|
|
|
36
36
|
/** Mirrors `_build_core.VENDORED_SKILL_DIRS`. */
|
|
37
|
-
export const VENDORED_SKILL_DIRS = new Set(['react-view-transitions', 'daydream', 'token-report']);
|
|
37
|
+
export const VENDORED_SKILL_DIRS = new Set(['react-view-transitions', 'daydream', 'token-report', 'explain-changes']);
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
40
|
* `PurePath(rel).parts` — split on EITHER separator, empties dropped.
|