geneseed 3.20.0 → 4.0.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 (88) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +9 -1
  3. package/SHIPPED.md +1 -1
  4. package/adapters/bob/README.md +31 -12
  5. package/adapters/claude-code/README.md +24 -17
  6. package/adapters/claude-code/settings.json +4 -6
  7. package/adapters/openclaude/README.md +13 -5
  8. package/adapters/opencode/plugins/geneseed-guard.js +291 -174
  9. package/bin/geneseed-hook.mjs +36 -5
  10. package/docs/README.md +3 -1
  11. package/docs/candidate-hosts.md +0 -8
  12. package/docs/concepts/enforcement-table.md +66 -0
  13. package/docs/concepts/hook-gates.md +2 -2
  14. package/docs/concepts/hooks.md +6 -6
  15. package/docs/concepts/host-matrix.md +4 -3
  16. package/docs/concepts/loop-branch.md +6 -1
  17. package/docs/concepts/rules.md +1 -1
  18. package/docs/design-history.md +59 -0
  19. package/docs/extending.md +11 -3
  20. package/docs/guides/install-opencode.md +1 -1
  21. package/docs/guides/loop-running.md +1 -1
  22. package/docs/reference/env-context.md +3 -2
  23. package/docs/reference/environment.md +2 -1
  24. package/docs/reference/plugin-guard.md +3 -3
  25. package/docs/reference/troubleshoot-hooks.md +34 -0
  26. package/docs/understand/design-choices-engineering.md +29 -0
  27. package/docs/understand/design-choices-gates.md +23 -0
  28. package/docs/understand/design-choices-hosts.md +21 -0
  29. package/docs/understand/design-choices-install.md +19 -0
  30. package/docs/understand/design-choices-shape.md +21 -0
  31. package/docs/understand/design-choices.md +34 -0
  32. package/docs/understand/enforced-vs-asked.md +5 -4
  33. package/docs/understand/machine-bob.md +1 -1
  34. package/docs/understand/machine-opencode.md +2 -2
  35. package/harness.config.json +1 -1
  36. package/js/build/driver.mjs +42 -16
  37. package/js/build/emit-claude.mjs +24 -8
  38. package/js/build/emit-opencode.mjs +33 -15
  39. package/js/build/generate.mjs +38 -9
  40. package/js/build/source.mjs +39 -0
  41. package/js/cli-table.json +6 -6
  42. package/js/hosts/gitref.mjs +3 -3
  43. package/js/hosts/hooks-context.mjs +11 -30
  44. package/js/hosts/hooks-learn.mjs +89 -8
  45. package/js/hosts/hooks.mjs +212 -18
  46. package/js/hosts/hosts.mjs +217 -13
  47. package/js/hosts/installs.mjs +259 -46
  48. package/js/hosts/native.mjs +48 -12
  49. package/js/hosts/opencode.mjs +47 -17
  50. package/js/hosts/settings.mjs +366 -42
  51. package/js/hosts/shim.mjs +37 -0
  52. package/js/inspect/checks-authoring.mjs +81 -1
  53. package/js/inspect/checks-constitution.mjs +2 -1
  54. package/js/inspect/checks-repo.mjs +68 -4
  55. package/js/inspect/doctor.mjs +40 -9
  56. package/js/inspect/scan.mjs +5 -4
  57. package/js/inspect/status.mjs +62 -12
  58. package/js/lib/text.mjs +23 -0
  59. package/js/loop/catalog.mjs +7 -2
  60. package/js/maintain/migrate.mjs +42 -5
  61. package/js/maintain/uninstall.mjs +150 -38
  62. package/js/maintain/update.mjs +22 -2
  63. package/js/web/actions.mjs +28 -10
  64. package/js/web/api.mjs +5 -3
  65. package/js/web/state.mjs +28 -15
  66. package/package.json +1 -1
  67. package/src/agents/_template.md +1 -1
  68. package/src/bricks/_template.md +1 -1
  69. package/src/doctrines/ops.md +9 -7
  70. package/src/laws/universal.md +1 -1
  71. package/src/skills/_template.md +3 -3
  72. package/src/skills/herdr.md +1 -1
  73. package/web/dist/assets/{Activity-IbwdHHyx.js → Activity-BCtqxjfy.js} +1 -1
  74. package/web/dist/assets/{ActivityDetail-B5uivdWI.js → ActivityDetail-Ba3Oo4dE.js} +1 -1
  75. package/web/dist/assets/{Dashboard-v6WQ37bD.js → Dashboard-DpHza68-.js} +2 -2
  76. package/web/dist/assets/{Docs-CypIfDBX.js → Docs-Bw_-UK0O.js} +1 -1
  77. package/web/dist/assets/{GreenhouseView-ClxP0hQG.js → GreenhouseView-DnJ1Ggp1.js} +1 -1
  78. package/web/dist/assets/{Installs-CPkQeFjO.js → Installs-CWoNKFBk.js} +1 -1
  79. package/web/dist/assets/{JournalView-BeEInhUf.js → JournalView-g6-bw4c8.js} +1 -1
  80. package/web/dist/assets/{Laws-Ds6U5xBQ.js → Laws-DkVWR1tb.js} +1 -1
  81. package/web/dist/assets/{Library-BraUKPyQ.js → Library-CK9HZzjV.js} +1 -1
  82. package/web/dist/assets/{Loops-DWcy3mwK.js → Loops-BP98SMOH.js} +1 -1
  83. package/web/dist/assets/{OperatorHudView-CQyrJQ9m.js → OperatorHudView-DI_n12M9.js} +1 -1
  84. package/web/dist/assets/{Personal-YWqs6qFL.js → Personal-Bk89f13a.js} +1 -1
  85. package/web/dist/assets/{Tabs-EhzEC-Xh.js → Tabs-3WuYIE1b.js} +1 -1
  86. package/web/dist/assets/{drawable-CEKPNV7X.js → drawable-oCUeq9e5.js} +1 -1
  87. package/web/dist/assets/{index-CPi22b7y.js → index-CiXwChvf.js} +2 -2
  88. package/web/dist/index.html +1 -1
package/CHANGELOG.md CHANGED
@@ -8,6 +8,41 @@ label. For the capability ↔ spec map, see [SHIPPED.md](SHIPPED.md).
8
8
 
9
9
  ## [Unreleased]
10
10
 
11
+ ## [4.0.0] — 2026-10-11
12
+
13
+ Major: a host-compatibility pass against the current IBM Bob, Claude Code, OpenCode and OpenClaude docs (#240), the host opportunities and a root-scan guard (#241), and author notes kept out of what the agent reads (#242). It is a major because three behaviours change visibly: Claude Code gates now block when they cannot run, OpenCode no longer falls back to `~/.claude/CLAUDE.md`, and the silent *process 8* migration is gone.
14
+
15
+ ### Upgrade notes
16
+ - Run `geneseed rebuild-all` once after updating: nothing on a machine changes until an install is re-emitted.
17
+ - An install built before the comms pack (3.7.0) is now skipped by `rebuild-all`, `migrate` and `upgrade`; run `geneseed setup` for it.
18
+ - If a Claude Code session suddenly refuses every Bash or Write call, the machine-wide hook shim is missing or stale: run `! geneseed rebuild-all` (see docs/reference/troubleshoot-hooks.md).
19
+
20
+ ### Added
21
+ - A Claude Code plugin marketplace: `/plugin marketplace add Arylmera/Geneseed`, then `/plugin install geneseed@geneseed`. The plugin carries one skill, `/geneseed:setup`, which asks for scope and theme and runs the published CLI (`npm install -g geneseed`, `geneseed build --emit claude-global` or `--emit claude`). It carries no harness of its own, so nothing in it can drift from the build.
22
+ - Bob gets `/`-commands: each skill alias and each user-only skill is a `.bob/commands/<name>.md`, and a user-only skill writes no SKILL.md on Bob, so the model cannot start it there.
23
+ - OpenCode hides user-only skills from the model: each one gets a `permission.skill` deny in `opencode.json`, and `/name` still runs it. Geneseed records the denies it writes in its manifest (`skill_denies`); a rebuild removes a recorded deny once the skill is no longer user-only, and uninstall takes the recorded ones back. A deny you set yourself, even on a Geneseed skill, is never touched.
24
+ - A root-scan check in the shell gate: a recursive scan whose argument is a whole filesystem root (`find /`, `du -sh /`, `grep -r foo /`, `rg x C:\`, `Get-ChildItem C:\ -Recurse`) now asks on Claude Code and OpenClaude and is refused on Bob and by the OpenCode guard plugin, citing *Commands Must Return* (ops 2), which gains a sentence saying so in both halves. In Git Bash `/` mounts every drive and share: a `find / … | head -5` ran for 3.5 hours on 2026-10-10. A deeper path, `ls /` without `-R`, a quoted argument, a comment, and the remote half of an `ssh` command pass. It runs inside `git-gate`/`tool-gate`, so no emitted hook command changes. Separators inside quotes and heredoc bodies are data, so a commit message that names such a scan is not one.
25
+ - A dedicated author-note prefix: an HTML comment in `src/` whose body starts with `note:` (`<!-- note: ... -->`, single or multi-line) is stripped before any render sees it — `resolveRuleIds` in `js/build/source.mjs` is the one function every text file threads through (and `parseBrick` in `js/loop/catalog.mjs` calls the same `stripAuthorNotes` directly, since a brick body is handed to the model raw and never goes through `resolveRuleIds` at all), so the strip covers the carrier, the on-disk `laws/`/`doctrines/` copies, every skill, and every brick alike. A comment shown only as a literal example — inside a fenced code block or an inline code span — is left un-stripped, via `blankCodeSpans` (`js/lib/text.mjs`, shared with the doctor arm below so the two can never disagree about what counts as "shown as an example"). Maintainer-only prose that used to ship verbatim (`src/laws/universal.md`'s "Amend BOTH halves", `src/skills/herdr.md`'s "Authoring note") is migrated to the prefix and no longer reaches the agent; the full carrier gains ~176 characters of headroom against its ceiling (unchanged, not lowered), lean is unaffected. `js/inspect/checks-authoring.mjs`'s new `authorNoteProblems` (wired into `doctor --all`) flags any other bare HTML comment in `src/` that is not the `note:` prefix and not a known functional marker (`LEAN:`/`CATALOG:`/`INCLUDE:`, `invocation: user`, `aliases:`, `webfetch: allow`, `harness:` followed by one or more of OpenCode/Claude Code/OpenClaude/Bob's ids, `bash: allow`), the same fence/code-span exemption applied, and separately refuses a `note:` comment whose own prose quotes a literal `<!--`/`-->` — HTML comments do not nest, so such a comment closes at its own inner marker and leaks the rest as live text (the landmine `src/skills/_template.md` hit once, fixed there by rewording the example to prose).
26
+
27
+ ### Changed
28
+ - Claude Code gates now block the tool call when the gate itself fails: `git-gate` and `rule-gate` carry `onFailure: "block"`, so a dead or missing shim, a gate process that crashes or a timeout refuses every Bash, PowerShell, Write, Edit and NotebookEdit call instead of letting it through. This needs Claude Code v2.1.295 or later; whether an older version ignores the key or rejects the settings file is unverified. `doctor` and `status` now report a deleted shim. To recover from inside a blocked session, see [Troubleshoot hooks](docs/reference/troubleshoot-hooks.md). OpenClaude and Bob are unchanged.
29
+
30
+ ### Removed
31
+ - The automatic *process 8* migration. An install built before the comms pack (3.7.0, 2026-09-27) used to be silently rewritten on every rebuild: `doctrinesOfDir` added `comms` so the codes rule it carried inside `process` was not lost, and `excludedRulesOfDir` re-addressed any `process 7`/`process 8` exclusion. Both relied on sniffing `process 8` in the rendered carrier, which breaks the day the process pack ever regains an eighth rule for an unrelated reason — it would then fire on a current install and rewrite data nobody asked rewritten. 4.0.0 removes the rewrite: a pre-comms carrier is now read exactly as written, and reading it (`doctor`, `diff`, `status`, `uninstall`) prints a warning on stderr instead, once per process. `rebuild-all` (and `upgrade`, which shells into it) now **skips** a pre-comms install entirely rather than re-emitting it — re-emitting would have written a fresh carrier that still drops `comms` and still carries unaddressed `process 7`/`process 8` exclusions, except now permanently, with no `process 8` text left for anything to ever catch again. **Upgrading:** if your install predates 3.7.0 and still names `process 8` in its carrier, run `geneseed setup` for it — `rebuild-all`/`upgrade` will skip it and name it in their summary until you do.
32
+
33
+ ### Fixed
34
+ - Bob gates read Bob's own payload (`{event, tool, input}`); they used to read only `tool_input`, so on Bob no gate ever fired. Bob also scans `apply_diff` and `search_and_replace` bodies.
35
+ - The Claude Code and OpenClaude git gate also matches the PowerShell tool (`Bash|PowerShell`); on Windows a destructive git command run through PowerShell used to pass unchecked. Read-only agents deny PowerShell too.
36
+ - A hook argv error never exits 2: a stale machine-wide shim handed a newer flag used to block every tool call.
37
+ - Windows without Git Bash: Claude Code runs hooks under PowerShell, where the emitted command was a parse error. The emit now detects Git Bash and writes a PowerShell form when it is absent; `doctor` and `status` warn when the machine changes after the emit.
38
+ - `CLAUDE_CONFIG_DIR` is honoured. A global hook stands down only for a live project install in `$CLAUDE_PROJECT_DIR` whose settings file still runs the same hook, never for a parent folder, another global install, a deactivated install or a manifest alone.
39
+ - The destructive-git gate also covers `clean --force`, `restore`, `push --delete` and `:branch`, `checkout -f`, `switch -f`, `stash drop/clear`, `worktree remove --force`, `reflog expire` and `gc --prune`, in the hook, the OpenCode guard and OpenCode's permissions.
40
+ - OpenCode: the guard checks every path an `apply_patch` touches (the only edit tool of gpt-* models) and scans written content for secrets; per-repo builds honour `--footprint lean`; plugins, workflows and themes are claimed on create, so a same-named user file survives emit and uninstall; a one-line managed `AGENTS.md` stops OpenCode loading the harness a second time through `~/.claude/CLAUDE.md`; a shadowed install under `OPENCODE_CONFIG_DIR` is reported. The dead `permission.ask` loop exemption is removed; pushes to `loop/*` are allowed by a static rule, and force, delete, an extra refspec and commits still ask.
41
+ - Settings are read back per host, so `rebuild-all`, `upgrade` and the console keep an OpenClaude per-repo install's theme, posture, packs and exclusions, also in a repo shared with another host.
42
+ - Uninstalling one host from a shared repo hands the root marker to the surviving host instead of deleting it; `migrate` reads the settings file the install really wired.
43
+ - `learn` cannot recurse through `claude -p`, reads nothing when `GENESEED_LLM` is unset, and returns at once on Bob, which sends no transcript.
44
+ - `learn` on `SubagentStop` reads the subagent's own transcript (`agent_transcript_path`); it used to distil a lesson from the main session's. When the transcript cannot be read, or lags behind the final turn, it falls back to or appends `last_assistant_message`.
45
+
11
46
  ## [3.20.0] — 2026-10-09
12
47
 
13
48
  ### Changed
package/README.md CHANGED
@@ -90,6 +90,14 @@ npm install -g geneseed # then plain `geneseed <command>` from any directo
90
90
  npm install -g geneseed@latest # …and that is also the update
91
91
  ```
92
92
 
93
+ Already in Claude Code? Add the plugin marketplace and let Claude run the install:
94
+
95
+ ```
96
+ /plugin marketplace add Arylmera/Geneseed
97
+ /plugin install geneseed@geneseed
98
+ /geneseed:setup
99
+ ```
100
+
93
101
  Every other route (Claude Code, Bob, OpenClaude, plain `AGENT.md`, per-repo installs) is in the **[install guide](docs/guides/install.md)**.
94
102
 
95
103
  ### 🧬 The long way — a git checkout
@@ -238,7 +246,7 @@ is **identical on every host**. What differs is how much of it the host can
238
246
  | **Colour themes** | ✅ full palette | ➖ | ➖ | ➖ |
239
247
  | **LSP · workflow runner · primary-agent · `/`-commands** | ✅ | ➖ | ➖ | ➖ |
240
248
 
241
- <sub>✅ native support · ⚙️ OpenCode plugin · 🪝 `settings.json` hook · 📄 carried by preamble prose only · ➖ no host mechanism (harness discipline still applies) · ¹ Bob's own hook contract: global hooks in `~/.bob/settings/settings.json`, Claude's event names but stdout ignored on `PreToolUse` — a refusal is **exit code 2**. So Laws I/IV exit 2, the consent rules are a stderr line, `SessionStart` context is plain stdout, `Stop` runs learn; `SubagentStop`/`PreCompact` are not Bob events and are not written. Unverified live (no Bob install on the authoring machine); the harness still holds via the rules preamble. · ² OpenCode's `tool.execute.before` can only allow or throw, with no "ask the user" tier, so the rule gate is a one-shot speed bump there rather than a prompt. · ³ OpenClaude is a Claude Code fork: Claude's hook groups and verdicts verbatim, in `~/.openclaude/settings.json` (global) or `.openclaude/settings.local.json` (per repo). Unverified live (no OpenClaude install on the authoring machine).</sub>
249
+ <sub>✅ native support · ⚙️ OpenCode plugin · 🪝 `settings.json` hook · 📄 carried by preamble prose only · ➖ no host mechanism (harness discipline still applies) · ¹ Bob's own hook contract: global hooks in `~/.bob/settings/settings.json`, Claude's event names but stdout ignored on `PreToolUse` — a refusal is **exit code 2**. So Laws I/IV, rigor-5 (External Gate) and ops-2 (a recursive scan of a whole filesystem) exit 2, the consent rules are a stderr line, `SessionStart` context is plain stdout, `Stop` runs learn; `SubagentStop`/`PreCompact` are not Bob events and are not written. Unverified live (no Bob install on the authoring machine); the harness still holds via the rules preamble. · ² OpenCode's `tool.execute.before` can only allow or throw, with no "ask the user" tier, so the rule gate is a one-shot speed bump there rather than a prompt. · ³ OpenClaude is a Claude Code fork: Claude's hook groups and verdicts verbatim, in `~/.openclaude/settings.json` (global) or `.openclaude/settings.local.json` (per repo). Unverified live (no OpenClaude install on the authoring machine).</sub>
242
250
 
243
251
  **Reading the matrix.** Everything above the divider is at full parity — no host
244
252
  drops an Agent, Skill, or the memory convention. The asymmetry is entirely in
package/SHIPPED.md CHANGED
@@ -20,7 +20,7 @@ historical references, not files distributed with the repo.
20
20
  > **On version numbers.** [`harness.config.json`](harness.config.json)'s `version` is a
21
21
  > human-readable label only. The canonical identity of an installed harness is the
22
22
  > **source fingerprint** stamped in `.geneseed-version` (see `geneseed version`), not the
23
- > string. Current label: **3.20.0**.
23
+ > string. Current label: **4.0.0**.
24
24
 
25
25
  ## Capabilities
26
26
 
@@ -99,16 +99,22 @@ What the global emit writes into `~/.bob/settings/settings.json`:
99
99
  | event | verb | how the verdict travels |
100
100
  | --- | --- | --- |
101
101
  | `SessionStart` | `context --host bob` | plain stdout, as on Claude |
102
- | `PreToolUse` | `tool-gate --host bob` — one group, no matcher | Laws I/IV: **exit 2**, reason on stderr; process 1/5: a stderr line, exit 0 |
102
+ | `PreToolUse` | `tool-gate --host bob` — one group, no matcher | Laws I/IV, rigor-5 and ops-2: **exit 2**, reason on stderr; process 1/5: a stderr line, exit 0 |
103
103
  | `Stop` | `learn` | never a verdict |
104
104
 
105
- No matcher on `PreToolUse` because Bob's tool names are undocumented (its IDE and its
106
- Shell differ); `tool-gate` reads the payload's shape instead — a `command` field gets
107
- the git checks, a path gets the rule checks — and defers on anything it does not
108
- recognise. Bob has **no "ask the user" tier**, so the two Laws that admit no judgement
109
- call are refused outright and the two consent rules become a warning. Because that
110
- warning is all process 5 can be here, the `process` pack toggle changes nothing in
111
- Bob's hooks: the one group only ever exits 2 for Laws I and IV, which every build carries.
105
+ No matcher on `PreToolUse`, deliberately: Bob's tools page already drifts from its own
106
+ changelog (`web_fetch`, the 2.1.0 Office/IBM-docs tools are undocumented there), so an
107
+ allow-list built from it would fail open on the next tool Bob adds, and a deny-list needs a
108
+ regex dialect (lookahead) the docs never confirm — bob-verdict.md O1 rates the matcher's own
109
+ upside "low, performance only", not worth either risk. `tool-gate` reads the payload's shape
110
+ instead — a `command` field gets the git checks, a path gets the rule checks — and defers on
111
+ anything it does not recognise. Bob has **no "ask the user" tier**, so the Laws and checks
112
+ that admit no judgement call — Laws I and IV, rigor-5 (External Gate, when a project
113
+ opts in) and ops-2 (Commands Must Return: a recursive scan rooted at a whole filesystem) —
114
+ are refused outright, and the two consent rules become a warning. Because that
115
+ warning is all process 5 can be here, the `process` pack toggle changes nothing in Bob's
116
+ hooks: the one group only ever exits 2 for Laws I and IV, rigor-5 and ops-2, which every build
117
+ carries.
112
118
 
113
119
  **Migration.** A re-emit over an older install unwires Geneseed's groups from the flat
114
120
  `~/.bob/settings.json` (your own keys there are kept) and writes the new set to the
@@ -140,10 +146,23 @@ MCP servers are wired at runtime (web console, listed by `geneseed mcp`) into th
140
146
  `~/.bob/settings/mcp.json` global — never `settings.json`, which Bob does not read
141
147
  servers from ([Bob docs](https://bob.ibm.com/docs/ide/configuration/mcp/mcp-in-bob)).
142
148
 
149
+ ## Slash commands
150
+
151
+ Bob documents `.bob/commands/*.md` (project) and `~/.bob/commands/*.md` (global), with
152
+ `description` frontmatter (bob-docs.md §6 — no use for `argument-hint` yet: no Geneseed
153
+ skill takes positional arguments). Geneseed writes one command per merged skill's OLD name
154
+ (`/old-name`, the same alias problem OpenCode's `command/` layer solves) and one per
155
+ genuinely user-only skill (host-compat O2/R1): Bob's skills page documents only
156
+ `name`/`description`, no model-invocation key, so a user-only skill gets **no** SKILL.md on
157
+ Bob at all — `/its-name` is the only way to run it, and the model never sees it in its
158
+ `use_skill` catalogue. Claim-on-create and manifest-owned like every other native file;
159
+ uninstall removes them the same way.
160
+
143
161
  ## What Bob does not get
144
162
 
145
163
  Bob rides the Claude engine, so — like Claude — it has no OpenCode-only extras:
146
- colour themes, JS plugins, the saved-workflow runner, the primary-agent
147
- orchestrator, LSP enablement, or the `/`-command layer. Those capabilities have
148
- no analogue on a Claude-shaped host; the harness discipline is carried by the
149
- preamble and skills instead. See the [host matrix](../../README.md#-supported-harnesses).
164
+ colour themes, JS plugins, the saved-workflow runner, the primary-agent orchestrator,
165
+ LSP enablement, or OpenCode's full `/`-command layer (`COMMAND_SET`, `/ponytail`) — only
166
+ the narrow alias/user-only-skill commands above. Those capabilities have no analogue on a
167
+ Claude-shaped host; the harness discipline is carried by the preamble and skills instead.
168
+ See the [host matrix](../../README.md#-supported-harnesses).
@@ -14,7 +14,7 @@ bin/geneseed-hook.mjs …` — so at user scope they would fire and fail in ever
14
14
  repo that doesn't vendor the harness. A generated global install wires
15
15
  absolute-path hooks instead — `geneseed setup` does that for you.) It:
16
16
 
17
- - on **PreToolUse** (matcher `Bash`), runs `geneseed-hook git-gate` — the tool-boundary
17
+ - on **PreToolUse** (matcher `Bash|PowerShell`), runs `geneseed-hook git-gate` — the tool-boundary
18
18
  half of Doctrine process 5 (*consent before every commit and push*) and of the
19
19
  destructive-git Rule. Drop the `process` pack (or exclude process 5) and the hook stays,
20
20
  wired with `--no-consent`: commits and pushes pass without asking, while the
@@ -27,22 +27,27 @@ absolute-path hooks instead — `geneseed setup` does that for you.) It:
27
27
  next time regardless. Every other Bash command (and any unreadable payload) is
28
28
  deferred to the normal permission flow — the hook never blocks unrelated work.
29
29
 
30
- **Caveat — the GitHub MCP vector.** The hook matches the `Bash` tool only. If your
31
- session has the GitHub MCP server, an agent can commit/push via `push_files` /
32
- `create_or_update_file` / `merge_pull_request`, which bypass Bash. To gate those too,
33
- widen the matcher to `"Bash|mcp__github__.*"` — `git-gate` ignores any payload it
34
- doesn't recognise as a commit/push, so over-matching the tool surface is harmless.
35
- - on **PreToolUse** (matcher `Write|Edit|MultiEdit|NotebookEdit`), runs
30
+ **Caveat — the GitHub MCP vector.** The hook matches the `Bash` and `PowerShell` tools
31
+ only. If your session has the GitHub MCP server, an agent can commit/push via
32
+ `push_files` / `create_or_update_file` / `merge_pull_request`, which bypass both. To
33
+ gate those too, widen the matcher to `"Bash|PowerShell|mcp__github__.*"` —
34
+ `git-gate` ignores any payload it doesn't recognise as a commit/push, so
35
+ over-matching the tool surface is harmless.
36
+ - on **PreToolUse** (matcher `Write|Edit|NotebookEdit`), runs
36
37
  `geneseed-hook rule-gate` — refuses a write that would put a credential into a tracked
37
38
  file, and asks first on the first write to `user-rules.md` or a memory file (whether
38
39
  something you want kept is a rule or a fact is your call). Ordinary edits pass.
39
- - on **SessionStart** (`startup`/`clear` only — a fresh context), prints `AGENT.md`
40
- so the harness is in context from the first turn, then runs `geneseed-hook context` to
41
- **inject the project context** directly into the session — so the project-context load is
42
- by the hook, not left to the agent to remember (lazy entries are only listed). On
43
- **`resume`** it runs `geneseed-hook context` *without* re-`cat`ting `AGENT.md`: the resumed
44
- conversation already carries the harness, so re-injecting the static file each resume
45
- is pure token waste — only the (possibly changed) project context is refreshed;
40
+ (`MultiEdit` is not a Claude Code tool and is not in the matcher; the gate reads
41
+ `edits[].new_string` as a cheap guard regardless.)
42
+ - on **SessionStart**, every source (`startup`, `resume`, `clear`, `compact`, `fork`), runs
43
+ `geneseed-hook context` to **inject the project context** directly into the session — so
44
+ the project-context load is by the hook, not left to the agent to remember (lazy entries
45
+ are only listed). This is one matcher-less group, exactly as a generated install wires it:
46
+ a matcher list is one more place to miss a source (`fork` once was). A second group,
47
+ `startup`/`clear` only — a fresh context — prints `AGENT.md` so the vendored harness is in
48
+ context from the first turn. A generated install has no such group: its root file is
49
+ `CLAUDE.md`, which Claude Code loads by itself. A resumed or forked conversation already
50
+ carries the harness, so re-printing the static file there is pure token waste;
46
51
  - on **Stop**, runs `geneseed-hook learn` over the session to capture durable memories.
47
52
  Claude Code pipes the hook payload (with the session's `transcript_path`) to the
48
53
  command on stdin; `learn` reads that, flattens the transcript, distils new
@@ -59,12 +64,14 @@ absolute-path hooks instead — `geneseed setup` does that for you.) It:
59
64
  - on **PreCompact**, runs the same `geneseed-hook learn` once more. `learn` distils the
60
65
  *tail* of the transcript, so per-turn Stop is a sliding window over the session —
61
66
  and auto-compaction is the moment that window is about to be summarised away.
62
- This captures memory *before* the summary; the SessionStart `compact` matcher
63
- re-seeds context *after* it.
67
+ This captures memory *before* the summary; the SessionStart `context` hook (which
68
+ also fires on the `compact` source) re-seeds context *after* it.
64
69
 
65
70
  This step is **opt-in on a model CLI**: set `GENESEED_LLM` (e.g. `claude -p`,
66
71
  `llm`, `ollama run …`) for `learn` to actually distil. With it unset the hook is
67
- a harmless no-op that just prints the prompt. Geneseed never embeds an API key.
72
+ a harmless no-op — it returns immediately rather than reading the transcript for
73
+ nothing; running `learn` by hand with notes piped in still prints the prompt it
74
+ would have sent. Geneseed never embeds an API key.
68
75
  If your bundle's `memory/` is not at `./memory` or `./Harness/memory`, point at
69
76
  it with `GENESEED_MEMORY=/abs/path/to/memory`.
70
77
 
@@ -2,13 +2,13 @@
2
2
  "hooks": {
3
3
  "PreToolUse": [
4
4
  {
5
- "matcher": "Bash",
5
+ "matcher": "Bash|PowerShell",
6
6
  "hooks": [
7
7
  { "type": "command", "command": "node bin/geneseed-hook.mjs git-gate" }
8
8
  ]
9
9
  },
10
10
  {
11
- "matcher": "Write|Edit|MultiEdit|NotebookEdit",
11
+ "matcher": "Write|Edit|NotebookEdit",
12
12
  "hooks": [
13
13
  { "type": "command", "command": "node bin/geneseed-hook.mjs rule-gate" }
14
14
  ]
@@ -16,16 +16,14 @@
16
16
  ],
17
17
  "SessionStart": [
18
18
  {
19
- "matcher": "startup|clear",
20
19
  "hooks": [
21
- { "type": "command", "command": "node -e \"process.stdout.write(require('fs').readFileSync('AGENT.md','utf8'))\"" },
22
20
  { "type": "command", "command": "node bin/geneseed-hook.mjs context" }
23
21
  ]
24
22
  },
25
23
  {
26
- "matcher": "resume|compact",
24
+ "matcher": "startup|clear",
27
25
  "hooks": [
28
- { "type": "command", "command": "node bin/geneseed-hook.mjs context" }
26
+ { "type": "command", "command": "node -e \"process.stdout.write(require('fs').readFileSync('AGENT.md','utf8'))\"" }
29
27
  ]
30
28
  }
31
29
  ],
@@ -12,14 +12,21 @@ There is nothing to install by hand: `geneseed setup` (or `geneseed-build --emit
12
12
  openclaude` / `--emit openclaude-global`) writes everything.
13
13
 
14
14
  **OpenClaude never reads `~/.claude`, a project `.claude/`, or `$CLAUDE_CONFIG_DIR`.**
15
- A Claude Code install is invisible to it, so it gets its own. The two coexist on one
16
- machine and in one repo without touching each other.
15
+ A Claude Code install is invisible to it, so it gets its own, and neither emit
16
+ overwrites the other's files. On one machine the two coexist cleanly. In one
17
+ repo they do not fully: OpenClaude also reads the root `CLAUDE.md`/`AGENTS.md`
18
+ a Claude Code or Bob per-repo install already wrote there, on top of its own
19
+ `.openclaude/CLAUDE.md` — both harnesses load, and there is no clean exclusion
20
+ (excluding the root file would hide your own content in it too). `geneseed status`
21
+ and `geneseed doctor` warn when this is happening in your repo.
17
22
 
18
23
  ## What the emit writes
19
24
 
20
25
  ### Per-repo (`--emit openclaude`)
21
26
 
22
- Everything lives under **`.openclaude/`**. Nothing is written at the repo root.
27
+ Everything lives under **`.openclaude/`**, except the `.geneseed-emit`/
28
+ `.geneseed-footprint` markers and `.mcp.json` below, which the driver writes at the
29
+ repo root like every other host.
23
30
 
24
31
  - `CLAUDE.md`: the harness preamble as a delimited **managed block**. OpenClaude
25
32
  loads the root `AGENTS.md`, or the root `CLAUDE.md` only when `AGENTS.md` is
@@ -47,8 +54,9 @@ global config file.
47
54
 
48
55
  ## Hooks
49
56
 
50
- The Claude group set, unchanged: `PreToolUse` (git gate on `Bash`, rule gate on
51
- writes), `SessionStart` (context on `startup|clear` and `resume|compact`), `Stop`,
57
+ The Claude group set, unchanged: `PreToolUse` (git gate on `Bash|PowerShell`, rule gate on
58
+ writes), `SessionStart` (context on every source — one matcher-less group covers
59
+ `startup`, `resume`, `clear`, `compact` and `fork` alike), `Stop`,
52
60
  `SubagentStop` and `PreCompact` (learn). OpenClaude reads Claude's
53
61
  `hookSpecificOutput.permissionDecision`, so the gates **ask** where Claude asks.
54
62
  The context hook carries `--host openclaude` for one reason. It tells the hook