geneseed 3.6.0 → 3.8.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 +74 -0
- package/DESIGN.md +6 -6
- package/QUICKSTART.md +1 -1
- package/README.md +33 -31
- package/SETUP.md +36 -10
- package/SHIPPED.md +3 -3
- package/adapters/claude-code/skills/geneseed/SKILL.md +1 -1
- package/adapters/openclaude/README.md +64 -0
- package/adapters/opencode/plugins/geneseed-workflow.js +1 -1
- package/adapters/opencode/workflows/_runtime.js +1 -1
- package/docs/extending.md +18 -6
- package/docs/web/exclusions.md +1 -0
- package/docs/web/footprint.md +1 -1
- package/docs/web/harnesses.md +14 -14
- package/docs/web/install-quick.md +1 -1
- package/docs/web/rules.md +1 -1
- package/docs/web/themes-author.md +1 -1
- package/docs/web-ui.md +1 -1
- package/harness.config.json +1 -1
- package/js/README.md +3 -3
- package/js/build/driver.mjs +64 -14
- package/js/build/emit-claude.mjs +1 -1
- package/js/build/generate.mjs +92 -42
- package/js/build/source.mjs +5 -4
- package/js/cli-table.json +151 -2
- package/js/hosts/hooks.mjs +11 -4
- package/js/hosts/hosts.mjs +17 -2
- package/js/hosts/installs.mjs +32 -2
- package/js/hosts/mcp.mjs +8 -1
- package/js/hosts/settings.mjs +4 -1
- package/js/inspect/diff.mjs +1 -1
- package/js/inspect/doctor.mjs +3 -3
- package/js/inspect/excludes.mjs +7 -5
- package/js/inspect/status.mjs +44 -8
- package/js/maintain/migrate.mjs +1 -1
- package/js/maintain/setup.mjs +7 -2
- package/js/maintain/uninstall.mjs +3 -3
- package/js/web/actions.mjs +7 -5
- package/js/web/api.mjs +2 -2
- package/js/web/docs.mjs +1 -1
- package/package.json +2 -1
- package/src/doctrines/README.md +3 -2
- package/src/doctrines/comms.md +53 -0
- package/src/doctrines/process.md +0 -26
- package/src/skills/geneseed.md +9 -8
- package/src/skills/parallel-agents.md +1 -1
- package/src/skills/pipeline.md +1 -1
- package/src/skills/token-report/SKILL.md +2 -1
- package/src/skills/token-report/VENDOR.md +3 -3
- package/src/skills/token-report/scripts/token_report.mjs +14 -7
- package/src/skills/workflow.md +2 -2
- package/themes/_TEMPLATE.json +5 -3
- package/themes/biker.json +5 -3
- package/themes/commentator.json +5 -3
- package/themes/cyberpunk.json +5 -3
- package/themes/gamer.json +5 -3
- package/themes/imperial.json +5 -3
- package/themes/joker.json +5 -3
- package/themes/marvin.json +5 -3
- package/themes/mean.json +5 -3
- package/themes/military.json +5 -3
- package/themes/neutral.json +5 -3
- package/themes/pirate.json +5 -3
- package/themes/sports.json +5 -3
- package/themes/verstappen.json +5 -3
- package/themes/wizard.json +5 -3
- package/web/dist/assets/{Activity-Flgf4kHy.js → Activity-CH-7cPIM.js} +1 -1
- package/web/dist/assets/{ActivityDetail-DIpr85nh.js → ActivityDetail-DZvz0xS5.js} +1 -1
- package/web/dist/assets/{CatalogRow-CjyLjaLb.js → CatalogRow-DftXpZHY.js} +1 -1
- package/web/dist/assets/{Diff-Deisdha8.js → Diff-DvwdG0L9.js} +1 -1
- package/web/dist/assets/{Docs-DGUdY4l4.js → Docs-DrJNDPwI.js} +1 -1
- package/web/dist/assets/{Doctor-F74lJ69P.js → Doctor-OTmwmYZg.js} +1 -1
- package/web/dist/assets/{GreenhouseView-D072lBiD.js → GreenhouseView-XxrAdriX.js} +1 -1
- package/web/dist/assets/Harness-C_LYNk5c.js +1 -0
- package/web/dist/assets/{JournalView-DWFqZ0Iw.js → JournalView-BcuyeEaR.js} +1 -1
- package/web/dist/assets/{Laws-BN6g3HU7.js → Laws-CWHrTKY4.js} +1 -1
- package/web/dist/assets/{Library-CDOv4emM.js → Library-CA9IHghn.js} +1 -1
- package/web/dist/assets/{OperatorHudView-CwNIUVX8.js → OperatorHudView-BMRY1Tt4.js} +1 -1
- package/web/dist/assets/{Profile-CpP9BTne.js → Profile-wceET1JY.js} +1 -1
- package/web/dist/assets/{Rules-DFXsUCB7.js → Rules-BEXvY4Tx.js} +1 -1
- package/web/dist/assets/{Settings-BqZVUtJ5.js → Settings-DS2zF0Ez.js} +1 -1
- package/web/dist/assets/{Skills-Cu6kBgfF.js → Skills-DahlOJuI.js} +1 -1
- package/web/dist/assets/{drawable-BgfrEBer.js → drawable-BTHjTzF7.js} +1 -1
- package/web/dist/assets/{index-CAqrWhKl.js → index-AN5Emnrg.js} +2 -2
- package/web/dist/assets/{lawCats-BLgxl151.js → lawCats-Btjp3Hk_.js} +1 -1
- package/web/dist/index.html +1 -1
- package/web/src/pages/Laws.jsx +6 -2
- package/web/dist/assets/Harness-DNRP2Va7.js +0 -1
package/docs/web/exclusions.md
CHANGED
|
@@ -14,6 +14,7 @@ Exclusion is all-or-nothing per folder — it doesn't trim which Rules apply or
|
|
|
14
14
|
### How it's wired, per host
|
|
15
15
|
|
|
16
16
|
- **Claude Code** — native suppression via `claudeMdExcludes` in the repo's `.claude/settings.local.json`.
|
|
17
|
+
- **OpenClaude** — the same `claudeMdExcludes`, in the repo's `.openclaude/settings.local.json`.
|
|
17
18
|
- **Bob** — a shadow `rules/geneseed.md` stub that shadows the global one (never overwrites a hand-written stub already there).
|
|
18
19
|
- **OpenCode** — the hook entry's sovereign-bypass guard and its twin in the plugins short-circuit at session start.
|
|
19
20
|
- **Copilot — documented limitation.** GitHub Copilot has no native per-repo suppression hook: the global `copilot-instructions.md` still loads even inside an excluded folder.
|
package/docs/web/footprint.md
CHANGED
|
@@ -26,7 +26,7 @@ Neither setting hides text from the agent; they hide it from *every turn*.
|
|
|
26
26
|
|
|
27
27
|
### How to set it
|
|
28
28
|
|
|
29
|
-
It's set-and-forget — stored in the `.geneseed-footprint` marker, preserved across every rebuild, identical on every host (OpenCode, Claude Code, Bob, Copilot). Changing it re-emits the install.
|
|
29
|
+
It's set-and-forget — stored in the `.geneseed-footprint` marker, preserved across every rebuild, identical on every host (OpenCode, Claude Code, Bob, Copilot, OpenClaude). Changing it re-emits the install.
|
|
30
30
|
|
|
31
31
|
- **Settings** — the **Footprint** toggle flips the current install (full ⇄ lean) and rebuilds it in place.
|
|
32
32
|
- **Harness tab** — on an active install, a per-harness dropdown sets it for that install independently, then **Apply**. On a host that is not installed yet, it is step 2 of **Install…**.
|
package/docs/web/harnesses.md
CHANGED
|
@@ -4,26 +4,26 @@ order: 4
|
|
|
4
4
|
title: "Supported harnesses"
|
|
5
5
|
kind: "concept"
|
|
6
6
|
---
|
|
7
|
-
One source, **
|
|
7
|
+
One source, **six emit targets** — each with a per-repo and a global (`-global`) variant, plus a portable `files` bundle any `AGENT.md`-aware tool can read. **OpenCode** runs its own engine (JS plugins, colour themes, LSP); **Claude Code**, **Bob**, **Copilot**, and **OpenClaude** share one Claude-shaped engine that diverges only by host dialect.
|
|
8
8
|
|
|
9
|
-
The harness *content* — the constitution (Ethos, Rules, Doctrines), Agents, Skills, Memory, and the preamble voice — is **identical on every host**: one build renders the same text for all
|
|
9
|
+
The harness *content* — the constitution (Ethos, Rules, Doctrines), Agents, Skills, Memory, and the preamble voice — is **identical on every host**: one build renders the same text for all six targets. What differs is how much of it the host can *automate* (via plugins or hooks) versus carry as preamble discipline.
|
|
10
10
|
|
|
11
|
-
| Capability | OpenCode | Claude | Bob | Copilot |
|
|
12
|
-
| --- | :---: | :---: | :---: | :---: |
|
|
13
|
-
| Agents · Skills · Memory | ✅ | ✅ | ✅ | ✅ |
|
|
14
|
-
| Context injection | ⚙️ plugin | 🪝 hook | 🪝 hook¹ | 📄 preamble |
|
|
15
|
-
| Memory write-back (learn) | ⚙️ plugin | 🪝 hook | 🪝 hook¹ | 📄 preamble |
|
|
16
|
-
| Git-gate consent (Doctrine process 5) | ⚙️ plugin | 🪝 hook | 🪝 hook¹ | 📄 preamble |
|
|
17
|
-
| Sovereign-repo excludes | ⚙️ plugin | ✅ | ✅ | ➖ |
|
|
18
|
-
| MCP server wiring | ✅ | ✅ | ✅ | ✅ |
|
|
19
|
-
| Colour themes | ✅ | ➖ | ➖ | ➖ |
|
|
20
|
-
| LSP · workflows · primary-agent · `/`-commands | ✅ | ➖ | ➖ | ➖ |
|
|
11
|
+
| Capability | OpenCode | Claude | Bob | Copilot | OpenClaude |
|
|
12
|
+
| --- | :---: | :---: | :---: | :---: | :---: |
|
|
13
|
+
| Agents · Skills · Memory | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
14
|
+
| Context injection | ⚙️ plugin | 🪝 hook | 🪝 hook¹ | 📄 preamble | 🪝 hook² |
|
|
15
|
+
| Memory write-back (learn) | ⚙️ plugin | 🪝 hook | 🪝 hook¹ | 📄 preamble | 🪝 hook² |
|
|
16
|
+
| Git-gate consent (Doctrine process 5) | ⚙️ plugin | 🪝 hook | 🪝 hook¹ | 📄 preamble | 🪝 hook² |
|
|
17
|
+
| Sovereign-repo excludes | ⚙️ plugin | ✅ | ✅ | ➖ | ✅ |
|
|
18
|
+
| MCP server wiring | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
19
|
+
| Colour themes | ✅ | ➖ | ➖ | ➖ | ➖ |
|
|
20
|
+
| LSP · workflows · primary-agent · `/`-commands | ✅ | ➖ | ➖ | ➖ | ➖ |
|
|
21
21
|
|
|
22
|
-
✅ native · ⚙️ OpenCode plugin · 🪝 `settings.json` hook · 📄 preamble prose only · ➖ no host mechanism (harness discipline still applies). ¹ Bob honours Claude-dialect hooks best-effort — inert if unsupported, harness still holds via the preamble.
|
|
22
|
+
✅ native · ⚙️ OpenCode plugin · 🪝 `settings.json` hook · 📄 preamble prose only · ➖ no host mechanism (harness discipline still applies). ¹ Bob honours Claude-dialect hooks best-effort — inert if unsupported, harness still holds via the preamble. ² OpenClaude is a Claude Code fork: Claude's hooks verbatim, under `~/.openclaude` or a repo's `.openclaude/`.
|
|
23
23
|
|
|
24
24
|
**The git-gate row is the one capability that is not always present**, on any host. It implements **Doctrine process 5**, so it is wired only on an install that built the **process** [doctrine pack](#/docs/setup-choices) in — drop that pack and the boundary drops with it, because a gate that keeps asking for a rule the install never adopted is a gate arguing with its own harness. One asymmetry: on OpenCode that holds for a *fresh* install, but a pack-off rebuild of an install whose `opencode.json` already carries a `git commit*` entry leaves it in place and reports it — Geneseed cannot tell its own entry from one you typed, so it removes neither ([doctrine packs](#/docs/setup-choices)). The invariant-territory refusals in the same neighbourhood (`rm -rf`, a force-push) are the Rules' territory, not the pack's, and stay in every build.
|
|
25
25
|
|
|
26
|
-
**No host drops an Agent, Skill, or the memory convention.** The asymmetry is otherwise entirely in *automation mechanism*: OpenCode's plugins and Claude/Bob's hooks enforce a few of these rules for you at the tool boundary, where **Copilot** (no hook mechanism) enforces them through preamble discipline. The OpenCode-only extras (themes, LSP, workflow runner, primary-agent) have no analogue on a Claude-shaped host.
|
|
26
|
+
**No host drops an Agent, Skill, or the memory convention.** The asymmetry is otherwise entirely in *automation mechanism*: OpenCode's plugins and Claude/Bob/OpenClaude's hooks enforce a few of these rules for you at the tool boundary, where **Copilot** (no hook mechanism) enforces them through preamble discipline. The OpenCode-only extras (themes, LSP, workflow runner, primary-agent) have no analogue on a Claude-shaped host.
|
|
27
27
|
|
|
28
28
|
---
|
|
29
29
|
|
|
@@ -18,7 +18,7 @@ To keep the command around: `npm install -g geneseed`, then plain `geneseed …`
|
|
|
18
18
|
|
|
19
19
|
### 2. Open your agent
|
|
20
20
|
|
|
21
|
-
Open OpenCode (or Claude Code, Bob, Copilot, or any `AGENT.md`-aware tool) in any repo. The first reply opens with the readiness sigil (`✅` neutral / `🧬` imperial / your theme's equivalent) and your project's docs are already in context.
|
|
21
|
+
Open OpenCode (or Claude Code, Bob, Copilot, OpenClaude, or any `AGENT.md`-aware tool) in any repo. The first reply opens with the readiness sigil (`✅` neutral / `🧬` imperial / your theme's equivalent) and your project's docs are already in context.
|
|
22
22
|
|
|
23
23
|
### Prefer a clone?
|
|
24
24
|
|
package/docs/web/rules.md
CHANGED
|
@@ -17,7 +17,7 @@ The agent's standing rules come in **three tiers**, and the tier decides how a r
|
|
|
17
17
|
|
|
18
18
|
### Doctrines — chosen at build time
|
|
19
19
|
|
|
20
|
-
Practices rather than principles: **craft** (how code is written), **rigor** (how work is proven), **ops** (how the machine is operated), **process** (how a task is run), one file each under `src/doctrines/`. A repository picks its set with `geneseed-build --doctrines <list>` or through the setup wizard — all {N_PACKS} are on by default. This install built in {N_PACKS_ACTIVE}, carrying {N_DOCTRINE_RULES} rules between them. Cited by pack and number — `Doctrine process 5`.
|
|
20
|
+
Practices rather than principles: **craft** (how code is written), **rigor** (how work is proven), **ops** (how the machine is operated), **process** (how a task is run), **comms** (how answers are presented), one file each under `src/doctrines/`. A repository picks its set with `geneseed-build --doctrines <list>` or through the setup wizard — all {N_PACKS} are on by default. This install built in {N_PACKS_ACTIVE}, carrying {N_DOCTRINE_RULES} rules between them. Cited by pack and number — `Doctrine process 5`.
|
|
21
21
|
|
|
22
22
|
A pack you leave out **still ships in the bundle**: every pack file lands under `doctrines/` beside `AGENT.md` whether or not it was built in. So a rule that cites one in an inactive pack still resolves on disk, and you can read the alternatives before turning one on. What an inactive pack loses is bindingness, not availability.
|
|
23
23
|
|
|
@@ -13,7 +13,7 @@ Every rule in the constitution gets its title from a theme key, and a theme that
|
|
|
13
13
|
|
|
14
14
|
- `LEX_I`…`LEX_IX` — one title per invariant. `doctor` holds this to an **equality**, not mere presence: a `LEX_*` outside `I`–`IX` is a title for a rule that does not exist and fails the check too.
|
|
15
15
|
- `DOC_*` — one title per doctrine rule, named after its address (`DOC_CRAFT_1`, `DOC_PROCESS_5`, …). `doctor` reads the pack files under `src/doctrines/` and requires exactly the set they name — a missing one is a blank heading, an extra one is a dead key.
|
|
16
|
-
- `PACK_*` — one name per pack (`PACK_CRAFT`, `PACK_RIGOR`, `PACK_OPS`, `PACK_PROCESS`), used for the pack's own sub-heading.
|
|
16
|
+
- `PACK_*` — one name per pack (`PACK_CRAFT`, `PACK_RIGOR`, `PACK_OPS`, `PACK_PROCESS`, `PACK_COMMS`), used for the pack's own sub-heading.
|
|
17
17
|
|
|
18
18
|
The Ethos section names are **not** theme keys: a citation reads `({{ONTOLOGY}}: {{ONT_TELOS}})`, token on both sides, so heading and reference always move together. They live in the build's fixed structure table and you neither declare nor rename them.
|
|
19
19
|
|
package/docs/web-ui.md
CHANGED
|
@@ -120,7 +120,7 @@ streams the output of background jobs and keeps their history across reloads.
|
|
|
120
120
|
- **Docs** (`#/docs`) — rendered documentation: markdown pages, concept pages, a CLI
|
|
121
121
|
reference (generated from the harness argument parser), and a glossary, grouped into
|
|
122
122
|
Get started / Core concepts / How-to / MCP servers / Plugins / Reference / Deeper. Filtered
|
|
123
|
-
for one host family (OpenCode, or Claude Code — which Bob and
|
|
123
|
+
for one host family (OpenCode, or Claude Code — which Bob, Copilot and OpenClaude share); with no
|
|
124
124
|
choice stored it follows the deployed install.
|
|
125
125
|
|
|
126
126
|
### 🩺 Care
|
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.8.0"
|
|
6
6
|
}
|
package/js/README.md
CHANGED
|
@@ -40,12 +40,12 @@ as a per-host install at project or global scope.
|
|
|
40
40
|
| `source.mjs` | Where the SOURCE is: `ROOT`/`SRC`/`THEMES`/`CONFIG`, `PACK_ORDER`, rule ids, 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
|
-
| `emit-claude.mjs` | The Claude/Bob/Copilot emit — the render half plus the settings + CLAUDE.md managed-block wire |
|
|
43
|
+
| `emit-claude.mjs` | The Claude/Bob/Copilot/OpenClaude emit — the render half plus the settings + CLAUDE.md managed-block wire |
|
|
44
44
|
| `emit-opencode.mjs` | The OpenCode emits — a project's `.opencode/` and the global config dir. Deliberate twin of the above |
|
|
45
45
|
| `emit-common.mjs` | Constants, tree walkers, and the three writers BOTH global emits share. Decides no host |
|
|
46
46
|
| `stubs.mjs` | Write-once seed files: context, wiki, rules, excludes, profile, `.gitignore`, memory/notebook indexes |
|
|
47
47
|
| `version.mjs` | The release marker: fingerprint the sources, read/compare/write an install's version, warn on downgrade |
|
|
48
|
-
| `driver.mjs` | The generator driver: `--emit`/`--theme`/`--footprint` flag parsing, the
|
|
48
|
+
| `driver.mjs` | The generator driver: `--emit`/`--theme`/`--footprint` flag parsing, the eleven emit targets, per-host emit orchestration, the prune and the manifest. `bin/build-driver.mjs` is only its entry |
|
|
49
49
|
| `generate.mjs` | CLI verbs `build`, `prompt`, `theme`, `rebuild-all` — the thin face over `driver.mjs` |
|
|
50
50
|
| `catalog.mjs` | `geneseed catalog` — prints the shipped roster. Classifies nothing itself |
|
|
51
51
|
| `themes.mjs` | Maintainer-only `--sync-themes`: inserts `_TEMPLATE.json`'s missing keys into the committed themes |
|
|
@@ -66,7 +66,7 @@ user co-owns, and the hook verbs those emitted configs then run.
|
|
|
66
66
|
|
|
67
67
|
| module | owns |
|
|
68
68
|
|---|---|
|
|
69
|
-
| `hosts.mjs` | The
|
|
69
|
+
| `hosts.mjs` | The five host config dirs (opencode/claude/bob/copilot/openclaude), plus `resolvePath`/`expanduser` |
|
|
70
70
|
| `hooks.mjs` | The verbs the emitted hooks run every session: `context`, `git-gate`, `rule-gate`, `tool-gate` (the two gates fused, for Copilot), `learn`. `--host` picks the verdict dialect |
|
|
71
71
|
| `settings.mjs` | Merges into user-owned `settings.json`/`opencode.json`; owns the hook shim and the managed blocks |
|
|
72
72
|
| `native.mjs` | Capability specs → host-native subagents and skills. The impure half of the emit |
|
package/js/build/driver.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The generator driver — flag parsing, the
|
|
2
|
+
* The generator driver — flag parsing, the eleven emit targets, and the per-host emit orchestration.
|
|
3
3
|
* `bin/build-driver.mjs` (`geneseed-build`) is only its entry; this module is what the CLI verbs
|
|
4
4
|
* (`build`, `setup`, `migrate`, `doctor`, `diff`, `validate`, the web console) import. It lived in
|
|
5
5
|
* `bin/` until 2026-09, which made nine `js/` modules import upward from a binary.
|
|
@@ -51,7 +51,7 @@ import { parseJson, jsonDumpsIndent } from '../lib/json.mjs';
|
|
|
51
51
|
// that should exist twice. golden.py's 259 cells are what made the move safe to attempt.
|
|
52
52
|
import {
|
|
53
53
|
GLOBAL_MANIFEST, resolvePath, opencodeConfigDir, claudeConfigDir, bobConfigDir,
|
|
54
|
-
copilotConfigDir, hostCatalogsNatively,
|
|
54
|
+
copilotConfigDir, openclaudeConfigDir, hostCatalogsNatively,
|
|
55
55
|
} from '../hosts/hosts.mjs';
|
|
56
56
|
|
|
57
57
|
// P5d moved these out of this file for the reason P5c moved the host resolvers: `harness
|
|
@@ -71,9 +71,9 @@ import { registryRecord, registryRoots } from '../inspect/registry.mjs';
|
|
|
71
71
|
// maintainer pair that needs nothing this driver is banned from having.
|
|
72
72
|
import { syncThemes } from './themes.mjs';
|
|
73
73
|
|
|
74
|
-
/** The
|
|
74
|
+
/** The eleven `--emit` choices: build.py:337-338's nine, then OpenClaude's pair. */
|
|
75
75
|
const EMITS = ['files', 'opencode', 'opencode-global', 'claude', 'claude-global',
|
|
76
|
-
'bob', 'bob-global', 'copilot', 'copilot-global'];
|
|
76
|
+
'bob', 'bob-global', 'copilot', 'copilot-global', 'openclaude', 'openclaude-global'];
|
|
77
77
|
|
|
78
78
|
/** `_build_emit.PRIMARY_AGENT_SRC`. */
|
|
79
79
|
const PRIMARY_AGENT_SRC = path.join(ROOT, 'adapters', 'opencode', 'agents', 'orchestrator.md');
|
|
@@ -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',
|
|
@@ -509,9 +509,11 @@ export function hookRunnerEntry() {
|
|
|
509
509
|
* POSIX spelling, per the original: `claudeMdExcludes` entries are glob patterns, where a
|
|
510
510
|
* backslash is an escape, so the Windows-native form risks never matching.
|
|
511
511
|
*/
|
|
512
|
-
function preambleExclude(claudeMd) {
|
|
512
|
+
function preambleExclude(claudeMd, host) {
|
|
513
513
|
if (path.basename(claudeMd) !== 'CLAUDE.md') return null;
|
|
514
|
-
|
|
514
|
+
// OpenClaude inherited `claudeMdExcludes` from Claude Code; its global preamble is its own.
|
|
515
|
+
const globalDir = host === 'openclaude' ? openclaudeConfigDir() : claudeConfigDir();
|
|
516
|
+
return resolvePath(path.join(globalDir, 'CLAUDE.md')).split(path.sep).join('/');
|
|
515
517
|
}
|
|
516
518
|
|
|
517
519
|
/**
|
|
@@ -808,7 +810,7 @@ function emitClaudeCore(cfg, args, { cfgDir, claudeMd, scope, host, out, hookOpt
|
|
|
808
810
|
theme: args.theme, cfgDir, claudeMd, scope, host,
|
|
809
811
|
out: out === null ? null : out,
|
|
810
812
|
footprint: args.footprint, nativeCatalog: hostCatalogsNatively(host),
|
|
811
|
-
oldOwned, oldManaged, preambleExclude: preambleExclude(claudeMd),
|
|
813
|
+
oldOwned, oldManaged, preambleExclude: preambleExclude(claudeMd, host),
|
|
812
814
|
...(hookOpts ? { hookOpts } : {}),
|
|
813
815
|
});
|
|
814
816
|
const { owned, stats, memStatus, nbStatus, managed } = rendered;
|
|
@@ -930,6 +932,42 @@ function emitClaude(cfg, args, out) {
|
|
|
930
932
|
+ `settings.json), ${r.memStatus}, ${r.nbStatus}.\n`);
|
|
931
933
|
}
|
|
932
934
|
|
|
935
|
+
/**
|
|
936
|
+
* OpenClaude global — `~/.openclaude` (or `$OPENCLAUDE_CONFIG_DIR`). The Claude emit with
|
|
937
|
+
* another config dir: OpenClaude is a Claude Code fork that reads neither `~/.claude` nor
|
|
938
|
+
* `CLAUDE_CONFIG_DIR`, so a Claude install is invisible to it and it needs its own.
|
|
939
|
+
*/
|
|
940
|
+
function emitOpenclaudeGlobal(cfg, args, out) {
|
|
941
|
+
const cfgDir = args.cfgDir ?? openclaudeConfigDir();
|
|
942
|
+
const hookOpts = hookRunnerEntry();
|
|
943
|
+
const r = emitClaudeCore(cfg, args, {
|
|
944
|
+
cfgDir, claudeMd: path.join(cfgDir, 'CLAUDE.md'), scope: 'global', host: 'openclaude', out,
|
|
945
|
+
hookOpts,
|
|
946
|
+
});
|
|
947
|
+
process.stdout.write(`[geneseed] openclaude-global -> ${cfgDir}: ${r.nAgents} subagents, `
|
|
948
|
+
+ `${r.nSkills} skills, CLAUDE.md, ${r.nHooks} hook group(s), settings.json, `
|
|
949
|
+
+ `${r.memStatus}, ${r.nbStatus}. MCP servers go in .openclaude.json.\n`);
|
|
950
|
+
return cfgDir;
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
/**
|
|
954
|
+
* OpenClaude per-repo — everything under `.openclaude/`, the preamble included. OpenClaude
|
|
955
|
+
* reads the root CLAUDE.md only when the repo has no AGENTS.md, but `.openclaude/CLAUDE.md`
|
|
956
|
+
* always; keeping the root untouched also lets a Claude Code install share the repo.
|
|
957
|
+
*/
|
|
958
|
+
function emitOpenclaude(cfg, args, out) {
|
|
959
|
+
const root = args.root ? resolveOut(args.root) : out;
|
|
960
|
+
const cfgDir = path.join(root, '.openclaude');
|
|
961
|
+
const hookOpts = hookRunnerEntry();
|
|
962
|
+
const r = emitClaudeCore(cfg, args, {
|
|
963
|
+
cfgDir, claudeMd: path.join(cfgDir, 'CLAUDE.md'), scope: 'project', host: 'openclaude', out,
|
|
964
|
+
hookOpts,
|
|
965
|
+
});
|
|
966
|
+
process.stdout.write(`[geneseed] openclaude (folder) -> ${root}: .openclaude/ `
|
|
967
|
+
+ `(CLAUDE.md, ${r.nAgents} subagents, ${r.nSkills} skills, ${r.nHooks} hook group(s), `
|
|
968
|
+
+ `settings.local.json), ${r.memStatus}, ${r.nbStatus}.\n`);
|
|
969
|
+
}
|
|
970
|
+
|
|
933
971
|
/**
|
|
934
972
|
* `_build_global.emit_bob_global` — into Bob's global config dir (~/.bob).
|
|
935
973
|
*
|
|
@@ -994,10 +1032,10 @@ function writeMarkers(markerDir, emit, footprint) {
|
|
|
994
1032
|
* compares it file by file. The Python spells that `HOSTS[host]["emit_global"](theme,
|
|
995
1033
|
* out=..., cfg=<tmp>, footprint=...)`, where `cfg` overrides the target the emit would
|
|
996
1034
|
* otherwise resolve. This is that call, and the override is threaded as `args.cfgDir` through
|
|
997
|
-
* the
|
|
998
|
-
*
|
|
999
|
-
* `
|
|
1000
|
-
*
|
|
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.
|
|
1001
1039
|
*
|
|
1002
1040
|
* WHICH SIDE OF THE BOUNDARY THIS SITS ON. P3c's rule was that a render CHILD must never
|
|
1003
1041
|
* resolve a config dir, because a child that did would write 135 files into the developer's
|
|
@@ -1012,6 +1050,7 @@ const GLOBAL_EMITS = {
|
|
|
1012
1050
|
claude: emitClaudeGlobal,
|
|
1013
1051
|
bob: emitBobGlobal,
|
|
1014
1052
|
copilot: emitCopilotGlobal,
|
|
1053
|
+
openclaude: emitOpenclaudeGlobal,
|
|
1015
1054
|
};
|
|
1016
1055
|
|
|
1017
1056
|
/**
|
|
@@ -1033,7 +1072,7 @@ const GLOBAL_EMITS = {
|
|
|
1033
1072
|
* three-positional call leaves in place, inherited here rather than re-decided.
|
|
1034
1073
|
*/
|
|
1035
1074
|
const PROJECT_EMITS = {
|
|
1036
|
-
claude: emitClaude, bob: emitBob, copilot: emitCopilot,
|
|
1075
|
+
claude: emitClaude, bob: emitBob, copilot: emitCopilot, openclaude: emitOpenclaude,
|
|
1037
1076
|
// P2. `opencode` joins the three for `cmdValidate`, which has to be able to render EVERY
|
|
1038
1077
|
// `--emit` choice into its sandbox and not only the three doctor already scans. Its call
|
|
1039
1078
|
// shape is `emitClaude`'s exactly — `(cfg, args, out)` — so the row is the whole change.
|
|
@@ -1148,6 +1187,15 @@ function run(argv) {
|
|
|
1148
1187
|
}
|
|
1149
1188
|
|
|
1150
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
|
+
}
|
|
1151
1199
|
const cfg = makeCfg(args);
|
|
1152
1200
|
|
|
1153
1201
|
// The marker directory is the emit's TARGET, which for a global emit is the config dir it
|
|
@@ -1186,6 +1234,8 @@ function run(argv) {
|
|
|
1186
1234
|
// An ALLOW-LIST, not "everything that is not global": a plain `--emit files` dev build —
|
|
1187
1235
|
// the default — must never pollute the registry, and only the four per-repo host emits
|
|
1188
1236
|
// are ones `_EMIT_HOST_SCOPE` can map back to a row. Records `out`, where the marker is.
|
|
1189
|
-
if (['opencode', 'claude', 'bob', 'copilot'].includes(args.emit))
|
|
1237
|
+
if (['opencode', 'claude', 'bob', 'copilot', 'openclaude'].includes(args.emit)) {
|
|
1238
|
+
registryRecord(markerDir);
|
|
1239
|
+
}
|
|
1190
1240
|
return 0;
|
|
1191
1241
|
}
|
package/js/build/emit-claude.mjs
CHANGED
|
@@ -126,7 +126,7 @@ export function emitClaudeRender(cfg, job) {
|
|
|
126
126
|
// settings.local.json; Bob documents no local variant, so its settings.json is ignored.
|
|
127
127
|
if (scope === 'project') {
|
|
128
128
|
const gi = path.join(cfgDir, '.gitignore');
|
|
129
|
-
const giLines = (host === 'claude' ? ['settings.local.json']
|
|
129
|
+
const giLines = (host === 'claude' || host === 'openclaude' ? ['settings.local.json']
|
|
130
130
|
: host === 'bob' ? ['settings.json'] : [])
|
|
131
131
|
.concat(['wiki.jsonc', 'agent-overrides.json']);
|
|
132
132
|
if (!existsSync(gi)) {
|
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
|
// --------------------------------------------------------------------------------------
|
|
@@ -123,7 +139,7 @@ export function setupBuildArgs(theme, emit, out = null, root = null, footprint =
|
|
|
123
139
|
posture = 'peer', mode = 'direct', doctrines = null, allPacks = PACK_ORDER,
|
|
124
140
|
excludeRules = null) {
|
|
125
141
|
const argv = ['--theme', theme, '--emit', emit];
|
|
126
|
-
if (!
|
|
142
|
+
if (!emit.endsWith('-global')) {
|
|
127
143
|
if (out) argv.push('--out', out);
|
|
128
144
|
if (root) argv.push('--root', root);
|
|
129
145
|
}
|
|
@@ -158,8 +174,69 @@ export const DEFAULT_EMIT = new Map([
|
|
|
158
174
|
['claude global', 'claude-global'], ['claude project', 'claude'],
|
|
159
175
|
['bob global', 'bob-global'], ['bob project', 'bob'],
|
|
160
176
|
['copilot global', 'copilot-global'], ['copilot project', 'copilot'],
|
|
177
|
+
['openclaude global', 'openclaude-global'], ['openclaude project', 'openclaude'],
|
|
161
178
|
]);
|
|
162
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
|
+
|
|
163
240
|
/**
|
|
164
241
|
* `_harness_build.cmd_rebuild_all` — re-emit every ACTIVE install in place, best-effort.
|
|
165
242
|
*
|
|
@@ -171,16 +248,12 @@ export const DEFAULT_EMIT = new Map([
|
|
|
171
248
|
* `main` converts, and `rebuild-all/one-broken-install-does-not-stop-the-rest` is the cell
|
|
172
249
|
* that fails if either half is undone.
|
|
173
250
|
*
|
|
174
|
-
*
|
|
175
|
-
* come from markers; posture and mode are detected from the `**<Name>**`
|
|
176
|
-
* deployed carrier's prose, because they were never markered. A rebuild that
|
|
177
|
-
* of them would silently re-emit an install into something its owner did not
|
|
178
|
-
* is the same failure mode `_footprint_of_dir`'s WARN exists to prevent, at a
|
|
179
|
-
*
|
|
180
|
-
* THE MARKER IS TRUSTED ONLY FOR ITS OWN HOST. There is one `.geneseed-emit` per root and the
|
|
181
|
-
* last deploy wins, so in a dual-host repo (`.opencode/` and `.claude/` in one cwd) the other
|
|
182
|
-
* host's row would otherwise rebuild with the WRONG emit — turning a Claude install into an
|
|
183
|
-
* 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.
|
|
184
257
|
*/
|
|
185
258
|
export function cmdRebuildAll() {
|
|
186
259
|
const targets = installTargets().filter(([h, s, r]) => installState(r, h, s) === 'active');
|
|
@@ -190,34 +263,11 @@ export function cmdRebuildAll() {
|
|
|
190
263
|
}
|
|
191
264
|
const failures = [];
|
|
192
265
|
for (const [host, scope, root] of targets) {
|
|
193
|
-
const
|
|
194
|
-
let marker = raw === null ? '' : raw.trim();
|
|
195
|
-
if (marker && (EMIT_HOST_SCOPE.get(marker) ?? ['', ''])[0] !== host) marker = '';
|
|
196
|
-
const emit = marker || DEFAULT_EMIT.get(`${host} ${scope}`) || 'opencode-global';
|
|
197
|
-
const theme = themeOfDir(root) || defaultTheme();
|
|
198
|
-
const footprint = footprintOfDir(root);
|
|
199
|
-
const posture = postureOfDir(root) || defaultPosture();
|
|
200
|
-
const mode = modeOfDir(root) || defaultMode();
|
|
201
|
-
const out = scope === 'global' ? null : root;
|
|
202
|
-
// SIX values now, not five. A rebuild that defaulted the pack selection would re-emit an
|
|
203
|
-
// install into a constitution its owner did not choose — and in one direction that is not
|
|
204
|
-
// merely surprising: dropping the process pack takes the commit/push consent RULES out of
|
|
205
|
-
// AGENT.md while `claudeHookGroups` keeps the gate wired, or the reverse. ⚠ AND THE
|
|
206
|
-
// FALLBACK IS NOT `harness.config.json`: a pre-migration carrier has no marker, and
|
|
207
|
-
// resolving that silence out of a machine-wide config narrows every such install on its
|
|
208
|
-
// first upgrade. `doctrinesForBuild` resolves unknown to ALL packs — see its docblock.
|
|
209
|
-
const doctrines = doctrinesForBuild(root);
|
|
210
|
-
// SEVEN. The per-rule axis is preserved for the same reason and with sharper teeth: an
|
|
211
|
-
// upgrade that dropped it would quietly hand back every rule its owner switched off, and
|
|
212
|
-
// `process 5` is one of them — the consent gate would return to a harness that had been
|
|
213
|
-
// built without it. No `doctrinesForBuild` rescue is needed here; see `apiInstallCmd`.
|
|
214
|
-
const excludeRules = excludedRulesOfDir(root);
|
|
215
|
-
const argv = setupBuildArgs(theme, emit, out, out, footprint, posture, mode, doctrines,
|
|
216
|
-
PACK_ORDER, excludeRules);
|
|
266
|
+
const p = installProfile(host, scope, root);
|
|
217
267
|
const label = `${host}:${scope} (${root})`;
|
|
218
|
-
printOut(`[rebuild-all] ${label}: theme=${theme} emit=${emit} footprint=${footprint} `
|
|
219
|
-
+ `posture=${posture} mode=${mode}\n`);
|
|
220
|
-
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);
|
|
221
271
|
if (rc !== 0) {
|
|
222
272
|
failures.push(label);
|
|
223
273
|
printErr(`[rebuild-all] FAILED ${label} (exit ${rc})\n`);
|
package/js/build/source.mjs
CHANGED
|
@@ -92,17 +92,18 @@ export function discoverNames(dir, first) {
|
|
|
92
92
|
*
|
|
93
93
|
* DELIBERATELY NOT `discoverNames('doctrines', 'craft')`. That helper `.sort()`s, so it
|
|
94
94
|
* answers `craft, ops, process, rigor`; the constitution's order is a reading order —
|
|
95
|
-
* write it (craft), prove it (rigor), run it (ops), ship it (process)
|
|
96
|
-
* produces it.
|
|
95
|
+
* write it (craft), prove it (rigor), run it (ops), ship it (process), then say it
|
|
96
|
+
* (comms) — and no sort produces it. comms is LAST because presenting the answer is the last
|
|
97
|
+
* act of any task, and because appending left every earlier pack's position untouched. Discovery still runs, but as a GATE rather than as the order: `js/build/render.mjs`
|
|
97
98
|
* refuses to build when a pack file exists under `src/doctrines/` that is missing from this
|
|
98
|
-
* array, so a
|
|
99
|
+
* array, so a sixth pack cannot be silently dropped from every install.
|
|
99
100
|
*
|
|
100
101
|
* It lives here, beside `makeCfg` and `discoverNames`, because every consumer (the cfg
|
|
101
102
|
* default, the render loop, the `Active packs:` marker, the CLI flag, the wizard, the
|
|
102
103
|
* doctor) already imports this module and this module imports nothing of theirs — the one
|
|
103
104
|
* placement that cannot introduce a cycle.
|
|
104
105
|
*/
|
|
105
|
-
export const PACK_ORDER = ['craft', 'rigor', 'ops', 'process'];
|
|
106
|
+
export const PACK_ORDER = ['craft', 'rigor', 'ops', 'process', 'comms'];
|
|
106
107
|
|
|
107
108
|
/**
|
|
108
109
|
* Every doctrine rule address this checkout ships — `pack.n`, in render order.
|