geneseed 3.9.0 → 3.11.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.
Files changed (219) hide show
  1. package/CHANGELOG.md +89 -1
  2. package/DESIGN.md +1 -1
  3. package/README.md +52 -71
  4. package/SHIPPED.md +3 -3
  5. package/adapters/bob/README.md +18 -2
  6. package/adapters/claude-code/README.md +17 -12
  7. package/adapters/openclaude/README.md +2 -2
  8. package/adapters/opencode/README.md +4 -4
  9. package/adapters/opencode/plugins/geneseed-context.js +78 -8
  10. package/bin/build-driver.mjs +2 -0
  11. package/bin/geneseed-cli.mjs +17 -0
  12. package/bin/geneseed-hook.mjs +3 -1
  13. package/docs/README.md +42 -0
  14. package/docs/_groups.json +18 -0
  15. package/docs/candidate-hosts.md +12 -5
  16. package/docs/concepts/agents.md +44 -0
  17. package/docs/concepts/collaboration.md +62 -0
  18. package/docs/concepts/exclusions.md +55 -0
  19. package/docs/concepts/footprint.md +52 -0
  20. package/docs/concepts/foreman-mode.md +69 -0
  21. package/docs/concepts/hooks.md +101 -0
  22. package/docs/concepts/hosts.md +55 -0
  23. package/docs/concepts/lsp.md +50 -0
  24. package/docs/{web → concepts}/memory.md +2 -2
  25. package/docs/{web → concepts}/notebook.md +2 -2
  26. package/docs/concepts/rules.md +52 -0
  27. package/docs/concepts/skills.md +49 -0
  28. package/docs/concepts/themes.md +50 -0
  29. package/docs/extending.md +27 -21
  30. package/docs/guides/autostart.md +70 -0
  31. package/docs/guides/choose-your-setup.md +120 -0
  32. package/docs/guides/headless.md +22 -0
  33. package/docs/guides/install.md +161 -0
  34. package/docs/guides/mcp.md +195 -0
  35. package/docs/guides/migrate.md +41 -0
  36. package/docs/guides/project-context.md +87 -0
  37. package/docs/guides/run-anywhere.md +45 -0
  38. package/docs/guides/uninstall.md +51 -0
  39. package/docs/guides/upgrade.md +65 -0
  40. package/docs/guides/verify.md +65 -0
  41. package/docs/guides/wiki.md +69 -0
  42. package/docs/guides/worktree.md +47 -0
  43. package/docs/reference/about.md +7 -0
  44. package/docs/reference/cli.md +7 -0
  45. package/docs/reference/environment.md +99 -0
  46. package/docs/reference/glossary.md +33 -0
  47. package/docs/reference/opencode-plugins.md +131 -0
  48. package/docs/reference/troubleshooting.md +350 -0
  49. package/docs/token-footprint.md +26 -21
  50. package/docs/understand/a-day.md +77 -0
  51. package/docs/understand/enforced-vs-asked.md +64 -0
  52. package/docs/understand/harness.md +36 -0
  53. package/docs/understand/on-your-machine.md +76 -0
  54. package/docs/web-ui.md +2 -2
  55. package/harness.config.json +1 -1
  56. package/js/README.md +7 -6
  57. package/js/build/driver.mjs +26 -95
  58. package/js/build/emit-claude.mjs +76 -81
  59. package/js/build/generate.mjs +12 -2
  60. package/js/build/render.mjs +4 -0
  61. package/js/build/source.mjs +6 -0
  62. package/js/build/stubs.mjs +18 -2
  63. package/js/cli-table.json +6 -6
  64. package/js/hosts/hooks.mjs +86 -54
  65. package/js/hosts/hosts.mjs +5 -21
  66. package/js/hosts/installs.mjs +4 -6
  67. package/js/hosts/mcp.mjs +4 -14
  68. package/js/hosts/native.mjs +27 -50
  69. package/js/hosts/opencode.mjs +1 -1
  70. package/js/hosts/settings.mjs +17 -79
  71. package/js/inspect/checks-authoring.mjs +9 -4
  72. package/js/inspect/checks-repo.mjs +45 -1
  73. package/js/inspect/diff.mjs +11 -3
  74. package/js/inspect/doctor.mjs +4 -4
  75. package/js/inspect/excludes.mjs +0 -4
  76. package/js/inspect/inventory.mjs +1 -1
  77. package/js/inspect/registry.mjs +20 -3
  78. package/js/inspect/status.mjs +11 -6
  79. package/js/lib/node-floor.mjs +30 -0
  80. package/js/maintain/migrate.mjs +5 -5
  81. package/js/maintain/setup.mjs +112 -26
  82. package/js/maintain/uninstall.mjs +8 -42
  83. package/js/maintain/update.mjs +4 -15
  84. package/js/ui/cli.mjs +2 -2
  85. package/js/web/actions.mjs +4 -5
  86. package/js/web/api.mjs +7 -4
  87. package/js/web/daemon.mjs +52 -0
  88. package/js/web/docs.mjs +149 -65
  89. package/js/web/server.mjs +5 -2
  90. package/package.json +6 -5
  91. package/registry.json +7 -0
  92. package/src/AGENT.md.tmpl +73 -52
  93. package/src/laws/universal.md +3 -0
  94. package/src/ontology/universal.md +1 -1
  95. package/src/postures/mentor.md +1 -0
  96. package/src/skills/daydream/SKILL.md +10 -3
  97. package/src/skills/daydream/VENDOR.md +10 -2
  98. package/src/skills/daydream/instructions.md +2 -2
  99. package/src/skills/explain-changes/SKILL.md +1 -1
  100. package/src/skills/explain-changes/VENDOR.md +1 -1
  101. package/src/skills/geneseed.md +1 -1
  102. package/src/skills/learn-mode.md +89 -0
  103. package/src/skills/pipeline.md +2 -2
  104. package/src/skills/quiz.md +2 -1
  105. package/src/skills/react-view-transitions/SKILL.md +1 -1
  106. package/src/skills/react-view-transitions/VENDOR.md +7 -2
  107. package/src/skills/teach.md +2 -1
  108. package/src/skills/token-report/SKILL.md +2 -3
  109. package/src/skills/token-report/VENDOR.md +2 -3
  110. package/src/skills/token-report/scripts/token_report.mjs +7 -121
  111. package/src/skills/workflow.md +1 -1
  112. package/themes/_TEMPLATE.json +1 -0
  113. package/themes/biker.json +1 -0
  114. package/themes/commentator.json +1 -0
  115. package/themes/cyberpunk.json +1 -0
  116. package/themes/gamer.json +1 -0
  117. package/themes/imperial.json +1 -0
  118. package/themes/joker.json +1 -0
  119. package/themes/marvin.json +1 -0
  120. package/themes/mean.json +1 -0
  121. package/themes/military.json +1 -0
  122. package/themes/neutral.json +1 -0
  123. package/themes/pirate.json +1 -0
  124. package/themes/sports.json +1 -0
  125. package/themes/verstappen.json +1 -0
  126. package/themes/wizard.json +1 -0
  127. package/web/dist/assets/Activity-C52LEb-2.js +1 -0
  128. package/web/dist/assets/ActivityDetail-CjNLRvXg.js +1 -0
  129. package/web/dist/assets/Dashboard-B6_sSCHi.js +2 -0
  130. package/web/dist/assets/Docs-Dj7BmCSA.js +5 -0
  131. package/web/dist/assets/{GreenhouseView-Bq1QmaGO.js → GreenhouseView-BALwjQsA.js} +1 -1
  132. package/web/dist/assets/Installs-BbffDQm3.js +2 -0
  133. package/web/dist/assets/JournalView-JpAQ8Hqe.js +1 -0
  134. package/web/dist/assets/Laws-DK7eCWSm.js +3 -0
  135. package/web/dist/assets/Library-CoamHelE.js +1 -0
  136. package/web/dist/assets/{Markdown-CkFyfgNf.js → Markdown-qjM2_ofS.js} +5 -5
  137. package/web/dist/assets/{OperatorHudView-B-HVBqfY.js → OperatorHudView-DVx1HlBx.js} +1 -1
  138. package/web/dist/assets/Personal-eaXb1zcj.js +1 -0
  139. package/web/dist/assets/{Tabs-_pjUFIEI.js → Tabs-BmGtt8E2.js} +1 -1
  140. package/web/dist/assets/api-Dc8f3ca-.js +1 -0
  141. package/web/dist/assets/drawable-Du_PZrQj.js +1 -0
  142. package/web/dist/assets/index-Bz_KMRDH.css +1 -0
  143. package/web/dist/assets/index-CNFH7o55.js +2 -0
  144. package/web/dist/index.html +3 -2
  145. package/QUICKSTART.md +0 -50
  146. package/SETUP.md +0 -1183
  147. package/adapters/copilot/README.md +0 -105
  148. package/docs/web/_groups.json +0 -45
  149. package/docs/web/about.md +0 -6
  150. package/docs/web/adapters-claude-code.md +0 -8
  151. package/docs/web/adapters-opencode-loads.md +0 -8
  152. package/docs/web/adapters-opencode-spec.md +0 -8
  153. package/docs/web/adapters-opencode.md +0 -8
  154. package/docs/web/agents.md +0 -8
  155. package/docs/web/authoring.md +0 -11
  156. package/docs/web/autostart.md +0 -9
  157. package/docs/web/claude-hooks.md +0 -37
  158. package/docs/web/cli.md +0 -6
  159. package/docs/web/collaboration.md +0 -27
  160. package/docs/web/configure-wiki.md +0 -9
  161. package/docs/web/design.md +0 -7
  162. package/docs/web/env-knobs.md +0 -9
  163. package/docs/web/exclusions.md +0 -27
  164. package/docs/web/first-session.md +0 -28
  165. package/docs/web/footprint.md +0 -38
  166. package/docs/web/foreman-mode.md +0 -33
  167. package/docs/web/glossary.md +0 -6
  168. package/docs/web/harnesses.md +0 -30
  169. package/docs/web/headless.md +0 -9
  170. package/docs/web/ingest-docs.md +0 -9
  171. package/docs/web/install-paths.md +0 -9
  172. package/docs/web/install-quick.md +0 -35
  173. package/docs/web/lsp-overview.md +0 -37
  174. package/docs/web/mcp-claude-code.md +0 -10
  175. package/docs/web/mcp-filesystem.md +0 -9
  176. package/docs/web/mcp-gitlab.md +0 -9
  177. package/docs/web/mcp-markitdown.md +0 -9
  178. package/docs/web/mcp-overview.md +0 -34
  179. package/docs/web/mcp-trouble.md +0 -9
  180. package/docs/web/mcp-verify.md +0 -9
  181. package/docs/web/migrate.md +0 -39
  182. package/docs/web/model.md +0 -20
  183. package/docs/web/pipelines.md +0 -24
  184. package/docs/web/plugin-activity.md +0 -23
  185. package/docs/web/plugin-context.md +0 -26
  186. package/docs/web/plugin-guard.md +0 -24
  187. package/docs/web/plugin-learn.md +0 -27
  188. package/docs/web/plugin-notify.md +0 -24
  189. package/docs/web/plugin-ponytail.md +0 -23
  190. package/docs/web/plugin-setup.md +0 -7
  191. package/docs/web/plugin-workflow.md +0 -25
  192. package/docs/web/plugins.md +0 -17
  193. package/docs/web/project-context.md +0 -9
  194. package/docs/web/rules.md +0 -30
  195. package/docs/web/run-anywhere.md +0 -9
  196. package/docs/web/self-improve.md +0 -8
  197. package/docs/web/setup-choices.md +0 -64
  198. package/docs/web/skills.md +0 -8
  199. package/docs/web/themes-author.md +0 -20
  200. package/docs/web/themes.md +0 -9
  201. package/docs/web/token-footprint.md +0 -7
  202. package/docs/web/trouble.md +0 -38
  203. package/docs/web/upgrade.md +0 -9
  204. package/docs/web/verify.md +0 -30
  205. package/docs/web/wiki.md +0 -7
  206. package/docs/web/worktree.md +0 -10
  207. package/docs/wiki.md +0 -43
  208. package/web/dist/assets/Activity-DAzV-qQj.js +0 -1
  209. package/web/dist/assets/ActivityDetail-CwEwMtKl.js +0 -1
  210. package/web/dist/assets/Dashboard-C2ELBW-X.js +0 -2
  211. package/web/dist/assets/Docs-iZrvQP8J.js +0 -2
  212. package/web/dist/assets/Installs-BiqR8xdO.js +0 -2
  213. package/web/dist/assets/JournalView-6wilG5Zd.js +0 -1
  214. package/web/dist/assets/Laws-D8YXD_XE.js +0 -3
  215. package/web/dist/assets/Library-DwuTZ2-L.js +0 -1
  216. package/web/dist/assets/Personal-BbUTmZQR.js +0 -1
  217. package/web/dist/assets/drawable-CoIulEH2.js +0 -1
  218. package/web/dist/assets/index-BMwSp8BP.js +0 -2
  219. package/web/dist/assets/index-CDPDk2vj.css +0 -1
@@ -56,6 +56,7 @@ import { promises as fs } from "node:fs"
56
56
  import * as path from "node:path"
57
57
  import { fileURLToPath } from "node:url"
58
58
  import { homedir } from "node:os"
59
+ import { createHash } from "node:crypto"
59
60
 
60
61
  const MARKER = "<!-- geneseed-context:v2 -->"
61
62
  const PLUGIN_DIR = path.dirname(fileURLToPath(import.meta.url))
@@ -156,6 +157,49 @@ const EXCLUDE_DIRS = new Set([
156
157
  async function isFile(p) { try { return (await fs.stat(p)).isFile() } catch { return false } }
157
158
  async function isDir(p) { try { return (await fs.stat(p)).isDirectory() } catch { return false } }
158
159
 
160
+ // ---- session files (the harness's own) --------------------------------------
161
+ // The files AGENT.md names for session start, relative to the HARNESS dir — the dir that
162
+ // holds the installed AGENT.md: the repo root for a project install, the config dir for a
163
+ // global one. Same list and seed hashes as SESSION_FILES/SEED_SHA256 in js/hosts/hooks.mjs;
164
+ // tests/unit/claude.test.mjs gates both copies against SESSION_SEEDS in js/build/stubs.mjs.
165
+ // context.json is not here: it is a manifest, honoured by resolveSource. wiki.jsonc is not
166
+ // here either: the MACHINE WIKI block below already renders it in full.
167
+ const SESSION_FILES = ["user-rules.md", "PROFILE.md", "memory/MEMORY.md", "anamnesis/MEMORY.md",
168
+ "notebook/NOTEBOOK.md"]
169
+ const SEED_SHA256 = new Set([
170
+ "5c2e92fb1acde041e02d9ebf158460d8ff7a07cbc2b447859631604610130721", // user-rules.md
171
+ "29e012c3c4349ea62a9082cbd04bb25599a0ec280d62c33f6e10742809471816", // PROFILE.md
172
+ "cccc917c34e6b990e821620bdb4739090151885ab71b6e85046f30a3b71fa5e8", // wiki.jsonc
173
+ "99c786049c260f6baf7aec68a5c0f59907861afe9b867da009440613bdddb907", // MEMORY.md
174
+ "9acb5c9d9cb5dac572104480f05b48cc144e676869bd60e86d8a15b1f6308184", // NOTEBOOK.md
175
+ ])
176
+
177
+ // $GENESEED_HARNESS when set, else the first of <plugin dir>/.. (global: the config dir) and
178
+ // <plugin dir>/../.. (project: .opencode/plugins -> the repo root) that holds AGENT.md.
179
+ async function harnessDir() {
180
+ if (process.env.GENESEED_HARNESS && (await isDir(process.env.GENESEED_HARNESS))) {
181
+ return process.env.GENESEED_HARNESS
182
+ }
183
+ for (const d of [path.resolve(PLUGIN_DIR, ".."), path.resolve(PLUGIN_DIR, "..", "..")]) {
184
+ if (await isFile(path.join(d, "AGENT.md"))) return d
185
+ }
186
+ return null
187
+ }
188
+
189
+ // Session files present under the harness dir and changed from their seed: [{ rel, abs, text }].
190
+ async function sessionFiles(dir) {
191
+ const found = []
192
+ if (!dir) return found
193
+ for (const rel of SESSION_FILES) {
194
+ const abs = path.join(dir, rel)
195
+ let text
196
+ try { text = (await fs.readFile(abs, "utf8")).replace(/\r\n/g, "\n") } catch { continue }
197
+ if (SEED_SHA256.has(createHash("sha256").update(text).digest("hex"))) continue
198
+ found.push({ rel, abs, text: text.replace(/\n+$/, "") })
199
+ }
200
+ return found
201
+ }
202
+
159
203
  async function readJson(p) {
160
204
  try { return JSON.parse(await fs.readFile(p, "utf8")) } catch { return null }
161
205
  }
@@ -474,11 +518,13 @@ async function fromManifest(manifestFile, root) {
474
518
  return { eager: eager.sort(byRel), lazy: lazy.sort(byRel) }
475
519
  }
476
520
 
477
- async function resolveSource(root) {
521
+ async function resolveSource(root, harness = null) {
478
522
  const explicit = process.env.GENESEED_CONTEXT
479
523
  if (explicit && (await isFile(explicit))) return { mode: "manifest", file: explicit }
480
- for (const rel of [path.join(".harness", "context.json"), "context.json"]) {
481
- const p = path.join(root, rel)
524
+ // The harness dir last: a repo's own manifest outranks the one seeded beside the install.
525
+ const cands = [path.join(root, ".harness", "context.json"), path.join(root, "context.json")]
526
+ if (harness) cands.push(path.join(harness, "context.json"))
527
+ for (const p of cands) {
482
528
  if (await isFile(p)) return { mode: "manifest", file: p }
483
529
  }
484
530
  return { mode: "discover" }
@@ -494,8 +540,12 @@ async function resolveSource(root) {
494
540
  async function resolveWikiFile() {
495
541
  const explicit = process.env.GENESEED_WIKI
496
542
  if (explicit && (await isFile(explicit))) return explicit
543
+ // The harness dir (which honours $GENESEED_HARNESS) first: on a PROJECT install the plugin
544
+ // sits in .opencode/plugins, so <plugin dir>/.. is .opencode, not the dir the seed landed in.
545
+ // <plugin dir>/.. stays as the fallback for an install without an AGENT.md.
497
546
  const bases = []
498
- if (process.env.GENESEED_HARNESS) bases.push(process.env.GENESEED_HARNESS)
547
+ const harness = await harnessDir()
548
+ if (harness) bases.push(harness)
499
549
  bases.push(path.resolve(PLUGIN_DIR, ".."))
500
550
  for (const base of bases) {
501
551
  // wiki.json is the legacy name from earlier seeds — still honoured.
@@ -652,9 +702,27 @@ async function renderSet(out, { eager, lazy }, state) {
652
702
  state.lazy += lazyLines.length
653
703
  }
654
704
 
655
- async function buildBlock(sets, wikis = [], commands = []) {
705
+ async function buildBlock(sets, wikis = [], commands = [], session = []) {
656
706
  const state = { spent: 0, injected: 0, lazy: 0, headingsRead: 0 }
657
707
 
708
+ // A session file discovery also found (user-rules.md at the root of a project install,
709
+ // whose harness dir IS the repo root) is injected once, in the session block.
710
+ const seen = new Set(session.map((s) => path.resolve(s.abs)))
711
+ const fresh = (x) => !seen.has(path.resolve(x.abs))
712
+ sets = { eager: sets.eager.filter(fresh), lazy: sets.lazy.filter(fresh) }
713
+ const ses = []
714
+ for (const s of session) {
715
+ const size = Buffer.byteLength(s.text, "utf8")
716
+ ses.push(`----- ${s.rel} -----`)
717
+ if (size > EAGER_FILE_KB * 1024) {
718
+ ses.push(`[demoted: ${s.rel} exceeded ${EAGER_FILE_KB} KB — read ${s.abs} on demand]`, "")
719
+ continue
720
+ }
721
+ ses.push(s.text, "")
722
+ state.spent += size
723
+ state.injected++
724
+ }
725
+
658
726
  const proj = []
659
727
  await renderSet(proj, sets, state)
660
728
 
@@ -675,8 +743,9 @@ async function buildBlock(sets, wikis = [], commands = []) {
675
743
  if (w.truncated) wik.push(` [+${w.truncated} more notes in this wiki — explore its folders on demand]`, "")
676
744
  }
677
745
 
678
- if (!proj.length && !wik.length) return null
746
+ if (!ses.length && !proj.length && !wik.length) return null
679
747
  const out = [MARKER]
748
+ if (ses.length) out.push("=== SESSION FILES — your harness files, injected at session start (untouched seeds skipped) ===", "", ...ses)
680
749
  if (proj.length) out.push("=== PROJECT CONTEXT — binding for this repo ===", "", ...proj)
681
750
  if (wik.length) out.push("=== MACHINE WIKI — the user's knowledge base, binding per AGENT.md §8 ===", "", ...wik)
682
751
  return { text: out.join("\n"), injected: state.injected, lazy: state.lazy, kb: kb(state.spent) }
@@ -686,13 +755,14 @@ async function buildBlock(sets, wikis = [], commands = []) {
686
755
  // and render the injection block — shared by the session.created injection and the
687
756
  // compaction hook (the transform path reuses it via cachedBlockText).
688
757
  async function resolveBlock(root) {
689
- const src = await resolveSource(root)
758
+ const harness = await harnessDir()
759
+ const src = await resolveSource(root, harness)
690
760
  const sets = src.mode === "manifest"
691
761
  ? await fromManifest(src.file, root)
692
762
  : await discover(root)
693
763
  const wikis = await wikiSets()
694
764
  const commands = await discoverCommands(root)
695
- return { block: await buildBlock(sets, wikis, commands), src }
765
+ return { block: await buildBlock(sets, wikis, commands, await sessionFiles(harness)), src }
696
766
  }
697
767
 
698
768
  // Per-process cache of the rendered block text for the transform path (root is fixed
@@ -4,6 +4,8 @@
4
4
  * which the CLI verbs import too; this file only hands it argv. Keeping the entry this thin is
5
5
  * what lets a `js/` module import the driver without running it on the importer's argv.
6
6
  */
7
+ // FIRST, before anything it could fail to run: an old Node gets one sentence, not a stack trace.
8
+ import '../js/lib/node-floor.mjs';
7
9
  import { main } from '../js/build/driver.mjs';
8
10
 
9
11
  process.exitCode = main(process.argv.slice(2));
@@ -54,6 +54,8 @@
54
54
  // table below now holds one LOADER per row (`() => import(…).then(m => m.cmdX)`), so a verb
55
55
  // loads its slice of the tree and no other verb's. The spawn allow-list gate still sees the
56
56
  // whole graph: `tests/unit/hook_cli.test.mjs`'s `importClosure` follows both import spellings.
57
+ // FIRST, before anything it could fail to run: an old Node gets one sentence, not a stack trace.
58
+ import '../js/lib/node-floor.mjs';
57
59
  import { cliSpec, printHelp, printVerbList } from '../js/ui/cli.mjs';
58
60
  import { parseIntStrict } from '../js/lib/text.mjs';
59
61
  import { printErr } from '../js/lib/fs.mjs';
@@ -289,6 +291,21 @@ async function main(argv) {
289
291
  validate: "check the source tree without emitting (the generator's --validate-only)",
290
292
  });
291
293
  }
294
+ // A DEAD HOOK SHIM, SAID ON EVERY RUN. The shim is machine-wide, so one stale write turns off
295
+ // every hook of every Claude-shaped install, and the hooks fail into a channel nobody reads —
296
+ // `doctor` was the only thing that looked. stderr, so no verb's stdout moves; skipped for help
297
+ // and for scripted callers that opt out. Not in `bin/geneseed-hook.mjs`: a dead shim means
298
+ // that binary is never reached, and its import graph is paid on every tool call.
299
+ if (process.env.GENESEED_NO_SHIM_CHECK !== '1' && verb !== 'help'
300
+ && !argv.some((t) => t === '-h' || t === '--help')) {
301
+ const { shimDead } = await import('../js/hosts/settings.mjs');
302
+ const dead = shimDead();
303
+ if (dead.length) {
304
+ printErr(`[geneseed] ⚠ hooks are off on this machine: the hook shim points at `
305
+ + `${dead.join(', ')}, which no longer exists. Fix: geneseed rebuild-all
306
+ `);
307
+ }
308
+ }
292
309
  // `validate` — the generator's `--validate-only`, and the one verb this entry answers that is NOT
293
310
  // in the table above. It is dispatched here, before the lookup, for a reason that is a
294
311
  // property of the gates rather than a preference:
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * The hook entry point — a Node twin of `rituals/harness.py` for the verbs the emitted
4
- * `settings.json` actually invokes (four from the reference, plus `tool-gate` for Copilot).
4
+ * `settings.json` actually invokes (four from the reference, plus `tool-gate` for Bob).
5
5
  *
6
6
  * WHY IT IS A SECOND BINARY AND NOT A SUBCOMMAND OF `bin/build-driver.mjs`. Three reasons,
7
7
  * and each alone would be enough:
@@ -37,6 +37,8 @@
37
37
  * every Geneseed hook returns 0 and signals through stdout — so "did nothing" and "worked"
38
38
  * are the same observation.
39
39
  */
40
+ // FIRST, before anything it could fail to run: an old Node gets one sentence, not a stack trace.
41
+ import '../js/lib/node-floor.mjs';
40
42
  import { cmdContext, cmdGitGate, cmdRuleGate, cmdToolGate, cmdLearn } from '../js/hosts/hooks.mjs';
41
43
  // Not a new module on the hot path: `js/hosts/hooks.mjs` above already imports
42
44
  // `js/lib/fs.mjs`, so this edge names a file the process has already loaded.
package/docs/README.md ADDED
@@ -0,0 +1,42 @@
1
+ # Geneseed documentation
2
+
3
+ [← Back to README](../README.md)
4
+
5
+ **New to agent harnesses?** Read **Understand** first — four short pages, about ten minutes, and every other page assumes them. The same pages open the **Docs** tab of the web console (`geneseed web`), which adds an interactive map of your own install and filters host-specific passages to the tool you use.
6
+
7
+ ## 1 · Understand
8
+
9
+ 1. **[What a harness is](understand/harness.md)** — git hooks + CI + a runbook, but for your AI agent.
10
+ 2. **[What lands on your machine](understand/on-your-machine.md)** — every file and hook an install writes, what it does for you, what it costs, how to turn it off.
11
+ 3. **[A day with the harness](understand/a-day.md)** — the moments you will actually meet: a push that asks first, a skill, a delegation, a memory.
12
+ 4. **[Enforced vs. asked](understand/enforced-vs-asked.md)** — what is guaranteed by code, and what the model is only asked to do.
13
+
14
+ ## 2 · Guides
15
+
16
+ | Guide | When |
17
+ | --- | --- |
18
+ | [Install](guides/install.md) | First time — npx, a clone, or a specific host |
19
+ | [Choose your setup](guides/choose-your-setup.md) | Theme, doctrine packs, one rule at a time, footprint, posture, mode |
20
+ | [Verify it works](guides/verify.md) | Right after installing, or when something feels off |
21
+ | [Upgrade](guides/upgrade.md) | Getting a new version onto every install |
22
+ | [Uninstall](guides/uninstall.md) | Removing an install — and what stays behind |
23
+ | [Project context](guides/project-context.md) | Choosing which of a repo's docs the agent loads |
24
+ | [Connect your wiki](guides/wiki.md) | Giving the agent your own notes, machine-wide |
25
+ | [MCP servers](guides/mcp.md) | MarkItDown, GitLab, Filesystem — and reading non-markdown docs |
26
+ | [Run geneseed from anywhere](guides/run-anywhere.md) | Putting the CLI on your PATH from a clone |
27
+ | [Start the web console at login](guides/autostart.md) | Keeping the console running |
28
+ | [Run in CI / headless](guides/headless.md) | OpenCode without a terminal |
29
+ | [Add git-worktree isolation](guides/worktree.md) | OpenCode, optional third-party add-on |
30
+ | [Migrate from a clone to npm](guides/migrate.md) | Moving an old clone install to the npm package |
31
+
32
+ ## 3 · Concepts
33
+
34
+ [Hosts](concepts/hosts.md) · [Rules](concepts/rules.md) · [Agents](concepts/agents.md) · [Skills](concepts/skills.md) · [Hooks](concepts/hooks.md) · [Memory](concepts/memory.md) · [Notebook](concepts/notebook.md) · [Footprint](concepts/footprint.md) · [Collaboration](concepts/collaboration.md) · [Foreman mode](concepts/foreman-mode.md) · [Exclusions](concepts/exclusions.md) · [Themes](concepts/themes.md) · [Code intelligence (LSP)](concepts/lsp.md)
35
+
36
+ ## 4 · Reference
37
+
38
+ [CLI](reference/cli.md) · [Environment variables](reference/environment.md) · [Glossary](reference/glossary.md) · [Troubleshooting](reference/troubleshooting.md) (by symptom) · [OpenCode plugins](reference/opencode-plugins.md) · [About](reference/about.md)
39
+
40
+ ## For contributors
41
+
42
+ [Extending Geneseed](extending.md) — what each kind of addition costs · [DESIGN.md](../DESIGN.md) · [Design history](design-history.md) · [Token footprint](token-footprint.md) · [Web console internals](web-ui.md) · [OpenCode plugin wiring](opencode-plugin-setup.md) · [Candidate hosts](candidate-hosts.md) · adapters: [OpenCode](../adapters/opencode/README.md), [Claude Code](../adapters/claude-code/README.md), [Bob](../adapters/bob/README.md), [OpenClaude](../adapters/openclaude/README.md)
@@ -0,0 +1,18 @@
1
+ [
2
+ {
3
+ "id": "understand",
4
+ "label": "Understand"
5
+ },
6
+ {
7
+ "id": "guides",
8
+ "label": "Guides"
9
+ },
10
+ {
11
+ "id": "concepts",
12
+ "label": "Concepts"
13
+ },
14
+ {
15
+ "id": "reference",
16
+ "label": "Reference"
17
+ }
18
+ ]
@@ -38,8 +38,7 @@ current host gets. Skills are the largest surface by file count and they cost no
38
38
  Claude's, and `permissionDecision: "ask"` exists. The Claude engine with `host: "codex"`
39
39
  covers instructions (`AGENTS.md`), skills (`.agents/skills`) and the hook verbs
40
40
  unchanged; the only new code is the carrier writer (a `hooks.json` merge or a TOML
41
- table) and the config-dir resolver (`~/.codex`, `$CODEX_HOME`). Roughly the Copilot
42
- port minus the dialect work.
41
+ table) and the config-dir resolver (`~/.codex`, `$CODEX_HOME`).
43
42
 
44
43
  - **Gemini CLI — Claude's groups, Gemini's names.** The hook groups are Claude's
45
44
  `event → [{matcher, hooks}]` and the payload fields are Claude's, but the events are
@@ -54,7 +53,7 @@ current host gets. Skills are the largest surface by file count and they cost no
54
53
  - **Cursor — a third verdict dialect.** `AGENTS.md` and `.agents/skills` are free; hooks
55
54
  are `hooks.json` with a `{command}` list per event and a `permission: allow|deny`
56
55
  verdict, `additional_context` in snake_case for `sessionStart`. Same block-or-warn stance
57
- as Copilot, one more output spelling in `js/hosts/hooks.mjs`, one more carrier writer.
56
+ as Bob, one more output spelling in `js/hosts/hooks.mjs`, one more carrier writer.
58
57
  Cursor also reads `.claude/skills/`, so a Claude project install already gives a Cursor
59
58
  user the skills.
60
59
 
@@ -64,8 +63,16 @@ If a fifth host is wanted, Codex first: it reuses the most and it is the only ca
64
63
  where the gates can *ask* rather than block. Before any of them, a `--emit agents` for
65
64
  the shared `.agents/skills/` folder would give Codex, Cursor and Gemini users the whole
66
65
  skill catalogue for the price of one directory name, with no host-specific code at all.
67
- A Gemini host would be the fourth verdict spelling in `js/hosts/hooks.mjs` (after Claude's
68
- ask, Copilot's block, Bob's exit code 2), plus a resolver and a carrier.
66
+ A Gemini host would be the third verdict spelling in `js/hosts/hooks.mjs` (after Claude's
67
+ ask and Bob's exit code 2), plus a resolver and a carrier.
68
+
69
+ ## Evaluated, dropped
70
+
71
+ - **GitHub Copilot** — shipped as a host through 3.9.0, removed in 3.10.0. Dropped for lack
72
+ of support: its hooks were never verified live, its `postToolUse` output does not reach
73
+ the model, and it shared no cross-tool surface with the other hosts (camelCase payloads,
74
+ matcherless hook entries, its own agent dialect and carrier), so every feature needed a
75
+ Copilot-only decision.
69
76
 
70
77
  ## Not verified
71
78
 
@@ -0,0 +1,44 @@
1
+ ---
2
+ group: concepts
3
+ order: 3
4
+ title: "Agents"
5
+ kind: "concept"
6
+ link: {"hash": "#/section/agents", "label": "Browse the catalog →"}
7
+ ---
8
+ An **agent** is a specialist the main session can hand a piece of work to. Each one is a markdown file with its own instructions, installed where your host looks for subagents (`agents/` in the install). It runs in its own context and returns a result, so its reading and searching does not fill your conversation.
9
+
10
+ > **You already know this:** handing a ticket to a colleague. You do not explain how to review code to the reviewer — you say what to review, and they come back with findings.
11
+
12
+ {N_AGENTS} agents ship, in two groups.
13
+
14
+ ### The specialists
15
+
16
+ | Agent | What it does |
17
+ | --- | --- |
18
+ | `explorer` | reads and searches widely in a throwaway context, returns only distilled findings |
19
+ | `researcher` | searches the open web, cross-checks every claim against two sources, returns sourced findings |
20
+ | `architect` | produces a design or implementation plan before code is written |
21
+ | `developer` | implements a scoped change in an isolated worktree, self-checked, ready for the tester |
22
+ | `tester` | writes, runs and diagnoses tests |
23
+ | `reviewer` | reviews a change for correctness and quality before it merges |
24
+ | `security` | audits changes that touch the security surface |
25
+ | `docs` | writes and updates user-facing documentation after code lands |
26
+
27
+ ### The council
28
+
29
+ Ten read-only debaters — `advocate`, `skeptic`, `pragmatist`, `steward`, `operator`, `visionary`, `framer`, `empiricist`, `historian`, `user-advocate` — each arguing one fixed stance. You do not call them one by one: the **council** [skill](skills.md) seats the ones that actually disagree on your question, runs bounded rounds of debate, and hands you a verdict with the strongest dissent kept verbatim. The council advises; it writes no code.
30
+
31
+ ### Specialists, not folder owners
32
+
33
+ An agent is picked by **what the work needs**, not by **where the file lives**. There is no "agent for `src/api/`": a change to any folder goes to the `developer`, and its review to the `reviewer`. You can name one ("have the reviewer look at this"), or let the main session route the request to the right specialist itself.
34
+
35
+ In [foreman mode](foreman-mode.md) the session chains several of them into a pipeline — explorer, developer and tester, plus whatever specialists the task needs.
36
+
37
+ ### Agents learn too
38
+
39
+ When a subagent finishes, the learn step can record a lesson for that agent alone, in `memory/agents/<name>.md` — one bullet per lesson, capped so it never grows unbounded. The next time that agent runs, it starts with what it learned. See [Memory](memory.md). (Not on Bob, which has no subagent-finished event.)
40
+
41
+ <!--harness:opencode-->
42
+ *(OpenCode only)*
43
+ OpenCode also has a **primary agent** — the one your session talks to directly. Geneseed installs it alongside the specialists; the specialists are subagents it delegates to.
44
+ <!--/harness-->
@@ -0,0 +1,62 @@
1
+ ---
2
+ group: concepts
3
+ order: 9
4
+ title: "Collaboration"
5
+ kind: "concept"
6
+ ---
7
+ Beside the [rules](rules.md), a few pieces shape *how* the agent works with you: the register it speaks in, the contract between you, how firmly a memory binds, and who you are. All of them are plain text in the install, so they reach every host unchanged.
8
+
9
+ > **You already know this:** a team's working agreement. The coding standards say what the code must look like; the working agreement says how people treat each other's reviews, questions and decisions.
10
+
11
+ ### Posture — the relationship
12
+
13
+ A **posture** is the register the agent works in. You choose it at setup and it is fixed at build time, so it does not drift back toward plain execution halfway through a session.
14
+
15
+ | Posture | For |
16
+ | --- | --- |
17
+ | **peer** (default) | a candid equal — dense, challenges you, no flattery |
18
+ | **mentor** | leaves you more capable — explains the why, checks understanding |
19
+ | **expert** | maximum density for a fluent user — terse, no basics |
20
+ | **assistant** | precise execution, low initiative — you steer |
21
+ | **artisan** | a peer with toolsmith reflexes — terminal-first, automates repetition |
22
+
23
+ Posture is separate from the [theme](themes.md): the theme is the *voice*, the posture is the *relationship*. Every posture obeys the same rules; what shifts is how firmly and how loudly the agent voices disagreement. Change it with `geneseed setup`, the web console's Harness page, or:
24
+
25
+ ```bash
26
+ geneseed build --emit claude-global --posture mentor
27
+ ```
28
+
29
+ How the agent *executes* work — itself, or through a crew — is the separate **mode** setting: see [Foreman mode](foreman-mode.md).
30
+
31
+ ### The Pact — a two-way contract
32
+
33
+ The **Pact** is stated in the first section of the Ethos, the layer that is always on, so it holds in every task and every repository. It ranks three duties for the agent, each yielding only to the one above:
34
+
35
+ 1. protect you — and the future maintainer of your code;
36
+ 2. serve your intent;
37
+ 3. keep its own honesty.
38
+
39
+ Unusually, it also says what *you* owe back:
40
+
41
+ - **Don't punish candour.** When the agent contradicts you with evidence, flags a risk or admits a doubt, that is the Pact working. Penalising it teaches the agent to flatter.
42
+ - **Give the context up front.** The agent cannot weigh what it was not told.
43
+ - **Decide when shown a fork.** An unmade decision stalls work as surely as a wrong one.
44
+
45
+ Your half is not enforced against you. The agent's half binds it.
46
+
47
+ ### Memory force — how firmly a memory binds
48
+
49
+ A [memory](memory.md) can carry an optional `force`:
50
+
51
+ | Force | Meaning |
52
+ | --- | --- |
53
+ | **constraint** | imposed; not the agent's to relax |
54
+ | **choice** | revisable, with your consent |
55
+ | **conviction** | revisable on evidence |
56
+ | **tempered** | a constraint that was relaxed |
57
+
58
+ When new evidence contradicts a memory with a force, the agent must revise it in the open rather than quietly drop it.
59
+
60
+ ### Profile — who you are
61
+
62
+ `PROFILE.md` sits beside the instructions file, is seeded once and is never overwritten. It holds your role, habits and how you like answers pitched. It colours how the agent works but never binds — it is last in [precedence](rules.md). Edit the file directly, use the Profile tab in the web console, or let the `profile` [skill](skills.md) interview you and draft it; it writes only with your consent, and routes anything that is really a standing rule to `user-rules.md`.
@@ -0,0 +1,55 @@
1
+ ---
2
+ group: concepts
3
+ order: 11
4
+ title: "Exclusions"
5
+ kind: "concept"
6
+ link: {"hash": "#/harness", "label": "Manage in Harness →"}
7
+ ---
8
+ Geneseed has two different ways to switch something off, and they work at different scales:
9
+
10
+ | | Sovereign repo | Per-rule exclusion |
11
+ | --- | --- | --- |
12
+ | **Switches off** | the whole global install, inside one folder | one doctrine rule, everywhere the install applies |
13
+ | **Scope** | a folder and everything under it | the install |
14
+ | **Applies to** | **global** installs only | any install |
15
+ | **Set with** | `geneseed exclude add <path>` | `--exclude-rules` at build time |
16
+ | **Takes effect** | on the next tool call, no rebuild | on rebuild |
17
+
18
+ > **You already know this:** `.gitignore` versus disabling one lint rule. One makes a tool ignore a whole folder; the other keeps the tool running everywhere with one check turned off.
19
+
20
+ ### Sovereign repos — whole-repo dormancy
21
+
22
+ A **sovereign repo** is a folder where every *global* Geneseed install goes fully dormant. The usual case is a repo that ships its own complete agent setup — its own rules, hooks and memory — where a global harness layered on top would bring conflicting instructions and wasted tokens.
23
+
24
+ It is all-or-nothing. It does not trim which rules apply or which skills load; the global harness simply never engages inside that folder. A **per-repo** Geneseed install in the same folder is unaffected — exclusions only ever silence global installs.
25
+
26
+ ```bash
27
+ geneseed exclude add ~/code/my-vault
28
+ geneseed exclude list
29
+ geneseed exclude remove ~/code/my-vault
30
+ ```
31
+
32
+ One command updates every global install on the machine. Each keeps the list in its own `excludes.json`, and every hook and plugin reads it on each call, so a change takes effect at once.
33
+
34
+ What "dormant" means per host:
35
+
36
+ - **Claude Code** — every hook exits silently, and the global `CLAUDE.md` is suppressed natively through `claudeMdExcludes` in the repo's `.claude/settings.local.json`.
37
+ - **OpenClaude** — the same, in the repo's `.openclaude/settings.local.json`.
38
+ - **Bob** — every hook exits silently, and a stub `.bob/rules/geneseed.md` in the repo shadows the global rules file. A hand-written stub already there is never overwritten.
39
+ - **OpenCode** — Geneseed's plugins stand down.
40
+
41
+ `geneseed exclude remove` undoes all of it.
42
+
43
+ **Limits.** Globally installed skills and agents stay *listed* by the host — no host offers a per-repo switch for them. And a global install created *after* you added exclusions starts with an empty list: run `geneseed exclude add` again (`geneseed exclude list` flags installs whose lists differ).
44
+
45
+ The web console has the same controls in the **Excluded folders** card on the Harness page, shown once a global install exists.
46
+
47
+ ### Per-rule exclusion — one rule off
48
+
49
+ The finer switch removes a single doctrine rule from an install while keeping the rest of its pack:
50
+
51
+ ```bash
52
+ geneseed build --emit claude-global --exclude-rules "process 7"
53
+ ```
54
+
55
+ The install records it (`Excluded rules: process 7` beside its pack list) and every rebuild and upgrade keeps it. The always-on Rules and the Ethos cannot be excluded. Picking packs and rules is covered in [Choose your setup](../guides/choose-your-setup.md); what the layers are is in [Rules](rules.md).
@@ -0,0 +1,52 @@
1
+ ---
2
+ group: concepts
3
+ order: 8
4
+ title: "Footprint (lean vs full)"
5
+ kind: "concept"
6
+ link: {"hash": "#/settings", "label": "Toggle it in Settings →"}
7
+ ---
8
+ **Footprint** decides how much of the [constitution](rules.md) the agent carries *inline* in its instructions file on every turn. It has two settings, **lean** (the default) and **full**, chosen per install. It is a token-cost dial, not a rules dial: every rule the install adopted is in force at either setting.
9
+
10
+ > **You already know this:** eager versus lazy loading. Full ships everything in the first payload; lean ships the index and fetches the rest when it is needed.
11
+
12
+ ### The difference
13
+
14
+ - **Full** — the instructions file carries the Ethos, every Rule and every rule of the active doctrine packs at complete length, reasoning included, every turn.
15
+ - **Lean** — each Rule and each doctrine rule is carried as its heading plus a short form written by hand (the rule and how it works, not the first sentence cut off). Each layer ends with a pointer to its complete text on disk, and the agent reads the reasoning when a rule's application is unclear. The Ethos gets one hand-written short form for the whole layer, because it is flowing prose rather than numbered rules.
16
+
17
+ Neither setting hides text from the agent. They decide what is in front of it on *every* turn.
18
+
19
+ ### What is on disk either way
20
+
21
+ - **Lean** writes the full `laws/`, `ontology/` and `doctrines/` text beside the instructions file, because the inline copy is shorter than what the agent may need.
22
+ - **Full** writes `doctrines/` too: it inlines only the packs this install built in, so the others must be on disk for a citation into them to resolve, and for you to read before turning one on.
23
+
24
+ Everything else is identical at both settings — the same agents, skills, plugins or hooks, commands, memory and notebook.
25
+
26
+ ### Which to choose
27
+
28
+ - **Lean** (default) for long sessions, large repos or cost-sensitive work. It is safe: the full text ships, and the instructions tell the agent to read it before acting on secrets, deletion, git history, scope or untrusted content.
29
+ - **Full** when token cost does not matter, or with a smaller or cheaper model — with the reasoning always in context, a model applies a rule's edge cases more reliably.
30
+
31
+ For measured numbers per host, see [docs/token-footprint.md](../token-footprint.md).
32
+
33
+ ### How to set it
34
+
35
+ Footprint is stored in a `.geneseed-footprint` marker in the install and kept across every rebuild and upgrade, on every host.
36
+
37
+ - **Setup wizard** — asks for it alongside voice, posture, mode and doctrine packs:
38
+
39
+ ```bash
40
+ geneseed setup
41
+ ```
42
+
43
+ - **Web console** — the **Footprint** toggle in Settings flips the current install and rebuilds it; on the Harness page, each install has its own dropdown.
44
+ - **Command line** — with the emit target of the install you are changing:
45
+
46
+ ```bash
47
+ geneseed build --emit claude-global --footprint full
48
+ ```
49
+
50
+ On the command line, a flag you leave out takes the generator's default, not the install's current value. `geneseed status --json` shows what an install currently has.
51
+
52
+ The walkthrough for all build-time choices is [Choose your setup](../guides/choose-your-setup.md).
@@ -0,0 +1,69 @@
1
+ ---
2
+ group: concepts
3
+ order: 10
4
+ title: "Foreman mode"
5
+ kind: "concept"
6
+ link: {"hash": "#/harness", "label": "Set the mode in Harness →"}
7
+ ---
8
+ **Mode** decides how work gets executed in a session. Two modes ship: **direct**, the default, where the agent does every task itself, and **foreman**, where the session hands substantial tasks to a crew of [agents](agents.md) and keeps talking to you while they work.
9
+
10
+ > **You already know this:** a tech lead with a team. Quick questions get answered on the spot; a real feature becomes a ticket that someone builds on a branch, and the lead reviews the result before it is merged.
11
+
12
+ Mode is chosen at build time, so it does not drift mid-session, and kept across every rebuild and re-theme. It is separate from [posture](collaboration.md) (the relationship register), the doctrine packs ([Rules](rules.md)) and [footprint](footprint.md).
13
+
14
+ ### direct — the default
15
+
16
+ The agent works every task itself, turn by turn. Nothing is spawned; you talk to one agent from start to finish. Keep it unless you want the session to manage work for you.
17
+
18
+ ### foreman — a session that triages
19
+
20
+ The session sorts every request:
21
+
22
+ - **Trivial** — a quick answer, a one-file tweak, a question: done directly, as in direct mode.
23
+ - **Substantial** — handed to a **pipeline** running in the background. The session tells you it is running and stays free to answer you.
24
+
25
+ Expect a substantial task to cost more (a crew runs instead of one agent) in exchange for a session that never blocks on a long job.
26
+
27
+ ### What a pipeline is
28
+
29
+ A **pipeline** is a small crew of agents working one task in isolation. The minimum crew depends on the task:
30
+
31
+ | Task | Minimum crew |
32
+ | --- | --- |
33
+ | development | `explorer` (analyses the request) → `developer` → `tester`, plus a lint check |
34
+ | documentation | `explorer` → `docs` |
35
+ | research | `explorer`; add `researcher` when the question leaves the repository |
36
+ | review or audit | `reviewer` + `skeptic` |
37
+
38
+ The session adds specialists on top when the task needs them — `security`, `architect`, `reviewer` and so on — never fewer than the minimum.
39
+
40
+ The crew works in its own git worktree and branch. When worktrees are unavailable it falls back to the main tree, one pipeline at a time, and says so. Two pipelines whose sets of files overlap do not run at once.
41
+
42
+ Inside the crew, the developer implements and the tester runs the tests and lint. Failures go back to the developer, at most five rounds; after that the crew stops and reports the failure, leaving the branch for you to inspect.
43
+
44
+ ### A pipeline never commits or merges
45
+
46
+ When the crew is done, it hands back its worktree **uncommitted**, with the raw test and lint output and the list of files it changed. Then the session:
47
+
48
+ 1. re-runs the tests and lint itself — the crew's log is the crew's account, not proof;
49
+ 2. shows you the diff and the green output;
50
+ 3. commits and merges **only once you accept**.
51
+
52
+ Commit, push and merge belong to the session alone, under *Consent Before Push* ([Rules](rules.md)). A crew that cannot get to green reports back instead of landing broken work.
53
+
54
+ You can also start a pipeline without foreman mode: ask the agent to "run a pipeline" for a task, and the `pipeline` [skill](skills.md) takes it from there.
55
+
56
+ ### How to set it
57
+
58
+ - **Setup wizard** — asks for mode alongside voice, posture, doctrine packs and footprint:
59
+
60
+ ```bash
61
+ geneseed setup
62
+ ```
63
+
64
+ - **Web console** — the per-install **Mode** dropdown on the Harness page, then **Apply**.
65
+ - **Command line** — with the emit target of the install you are changing; switch back with `--mode direct`:
66
+
67
+ ```bash
68
+ geneseed build --emit claude-global --mode foreman
69
+ ```