geneseed 3.2.0 → 3.4.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 +150 -0
- package/README.md +24 -14
- package/SETUP.md +38 -7
- package/SHIPPED.md +6 -5
- package/adapters/bob/README.md +40 -10
- package/adapters/claude-code/README.md +5 -0
- package/adapters/claude-code/settings.json +8 -1
- package/adapters/copilot/README.md +52 -25
- package/adapters/opencode/README.md +11 -3
- package/adapters/opencode/plugins/geneseed-guard.js +40 -6
- package/bin/build-driver.mjs +16 -10
- package/bin/geneseed-cli.mjs +55 -56
- package/bin/geneseed-hook.mjs +21 -14
- package/docs/candidate-hosts.md +74 -0
- package/docs/extending.md +10 -8
- package/docs/token-footprint.md +32 -21
- package/docs/web/agents.md +1 -1
- package/docs/web/first-session.md +1 -1
- package/docs/web/skills.md +1 -1
- package/docs/web-ui.md +5 -13
- package/harness.config.json +1 -1
- package/js/README.md +5 -6
- package/js/build/emit-claude.mjs +22 -7
- package/js/build/render.mjs +37 -19
- package/js/build/source.mjs +22 -2
- package/js/cli-table.json +84 -0
- package/js/hosts/hooks.mjs +240 -78
- package/js/hosts/hosts.mjs +56 -2
- package/js/hosts/link.mjs +1 -1
- package/js/hosts/mcp.mjs +3 -1
- package/js/hosts/native.mjs +86 -15
- package/js/hosts/opencode.mjs +14 -0
- package/js/hosts/settings.mjs +173 -5
- package/js/inspect/excludes.mjs +1 -1
- package/js/inspect/inventory.mjs +13 -8
- package/js/inspect/status.mjs +50 -2
- package/js/maintain/uninstall.mjs +38 -11
- package/js/maintain/update.mjs +2 -2
- package/js/web/actions.mjs +8 -8
- package/js/web/api.mjs +5 -10
- package/js/web/docs.mjs +9 -31
- package/js/web/handler.mjs +20 -30
- package/js/web/jobs.mjs +5 -26
- package/js/web/routes.mjs +18 -84
- package/js/web/server.mjs +3 -3
- package/package.json +1 -1
- package/registry.json +99 -78
- package/src/AGENT.md.tmpl +69 -28
- package/src/agents/architect.md +4 -1
- package/src/agents/explorer.md +3 -1
- package/src/agents/operator.md +13 -0
- package/src/agents/pragmatist.md +12 -0
- package/src/agents/researcher.md +72 -0
- package/src/agents/security.md +7 -1
- package/src/agents/skeptic.md +2 -1
- package/src/agents/tester.md +5 -1
- package/src/agents/user-advocate.md +13 -0
- package/src/doctrines/README.md +3 -3
- package/src/doctrines/rigor.md +14 -1
- package/src/laws/universal.md +98 -20
- package/src/ontology/universal.md +38 -13
- package/src/skills/_self-improvement.md +19 -0
- package/src/skills/_template.md +6 -13
- package/src/skills/brainstorm.md +37 -19
- package/src/skills/bruno-collection-generator.md +52 -0
- package/src/skills/bruno-test-writer.md +51 -0
- package/src/skills/ci-fix.md +49 -0
- package/src/skills/codebase-design.md +1 -12
- package/src/skills/commit.md +1 -12
- package/src/skills/consolidate-memory.md +50 -0
- package/src/skills/council.md +1 -12
- package/src/skills/debug.md +1 -12
- package/src/skills/deps-audit.md +46 -0
- package/src/skills/develop.md +38 -20
- package/src/skills/document-project.md +1 -12
- package/src/skills/domain-modeling.md +1 -12
- package/src/skills/forge-mcp.md +2 -13
- package/src/skills/fresh-eyes.md +1 -12
- package/src/skills/frontend-design.md +1 -12
- package/src/skills/geneseed-code-review.md +1 -12
- package/src/skills/geneseed.md +1 -12
- package/src/skills/git-archaeology.md +1 -12
- package/src/skills/git-rescue.md +1 -12
- package/src/skills/handoff.md +1 -12
- package/src/skills/herdr.md +2 -12
- package/src/skills/ingest.md +1 -12
- package/src/skills/migrate.md +1 -12
- package/src/skills/opencode-theme.md +2 -12
- package/src/skills/parallel-agents.md +1 -12
- package/src/skills/pipeline.md +2 -13
- package/src/skills/plan.md +1 -12
- package/src/skills/ponytail.md +1 -12
- package/src/skills/profile.md +1 -12
- package/src/skills/prose.md +1 -12
- package/src/skills/prototype.md +1 -12
- package/src/skills/quiz.md +43 -0
- package/src/skills/refactor.md +1 -12
- package/src/skills/release.md +1 -12
- package/src/skills/repo-map.md +1 -12
- package/src/skills/research.md +6 -14
- package/src/skills/review-response.md +1 -12
- package/src/skills/roast-me.md +1 -12
- package/src/skills/rule.md +1 -12
- package/src/skills/security-audit.md +51 -0
- package/src/skills/ship.md +1 -12
- package/src/skills/skill-forge.md +59 -0
- package/src/skills/teach.md +50 -0
- package/src/skills/tickets.md +1 -12
- package/src/skills/wayfinder.md +1 -12
- package/src/skills/wiki.md +1 -12
- package/src/skills/workflow.md +74 -26
- package/src/skills/worktree.md +52 -0
- package/themes/_TEMPLATE.json +12 -8
- package/themes/biker.json +12 -8
- package/themes/commentator.json +12 -8
- package/themes/cyberpunk.json +12 -8
- package/themes/gamer.json +12 -8
- package/themes/imperial.json +12 -8
- package/themes/joker.json +12 -8
- package/themes/marvin.json +12 -8
- package/themes/mean.json +12 -8
- package/themes/military.json +12 -8
- package/themes/neutral.json +12 -8
- package/themes/pirate.json +12 -8
- package/themes/sports.json +12 -8
- package/themes/verstappen.json +12 -8
- package/themes/wizard.json +12 -8
- package/web/dist/assets/Activity-DMGhhjQc.js +1 -0
- package/web/dist/assets/ActivityDetail-CeQcDA7e.js +1 -0
- package/web/dist/assets/{CatalogRow-DqS4e5XM.js → CatalogRow-CnSbDPyg.js} +1 -1
- package/web/dist/assets/{Diff-CIrGbiqm.js → Diff-D_ksMBD1.js} +1 -1
- package/web/dist/assets/{Doctor-CpI53oHY.js → Doctor-Brb3_oCT.js} +1 -1
- package/web/dist/assets/FilterInput-DbRWPhtT.js +1 -0
- package/web/dist/assets/GreenhouseView-F3VNFr73.js +2 -0
- package/web/dist/assets/{Harness-vRYs11kX.js → Harness-DJSwfYeF.js} +1 -1
- package/web/dist/assets/JournalView-BLRYRnB3.js +1 -0
- package/web/dist/assets/Laws-CKYns9qe.js +3 -0
- package/web/dist/assets/Library-BK0YloSv.js +1 -0
- package/web/dist/assets/{Markdown-kgpg9EQM.js → Markdown-BzTRzc7a.js} +1 -1
- package/web/dist/assets/OperatorHudView-D3OQSGp5.js +2 -0
- package/web/dist/assets/{Profile-CHr-PPf6.js → Profile-CPA2u_0g.js} +1 -1
- package/web/dist/assets/{Rules-CsC6hfqk.js → Rules-Lukel-Ru.js} +1 -1
- package/web/dist/assets/Skills-BKRLHH7m.js +1 -0
- package/web/dist/assets/{StatusBadge-gRrak0nO.js → StatusBadge-DI3weRKd.js} +1 -1
- package/web/dist/assets/{activity-B0-FXZCj.js → activity-C_H2Bx1P.js} +1 -1
- package/web/dist/assets/{index-Bx3I7cRW.js → index-B7_Wga23.js} +1 -1
- package/web/dist/assets/index-DzdQjvM1.css +1 -0
- package/web/dist/assets/{index-DJOhOwwR.js → index-N8dTrVro.js} +1 -1
- package/web/dist/assets/index-Py_IQjgK.js +10 -0
- package/web/dist/assets/jobBuckets-DfTUJUxR.js +1 -0
- package/web/dist/assets/lawCats-DUS9UTH4.js +1 -0
- package/web/dist/index.html +2 -2
- package/web/src/pages/Laws.jsx +82 -64
- package/js/web/graph.mjs +0 -0
- package/src/skills/clarify.md +0 -28
- package/src/skills/crash-course.md +0 -28
- package/src/skills/decode.md +0 -28
- package/src/skills/drill.md +0 -28
- package/src/skills/feynman.md +0 -26
- package/src/skills/gap-detector.md +0 -27
- package/src/skills/learning-path.md +0 -26
- package/src/skills/tdd.md +0 -31
- package/web/dist/assets/Activity-BTCG2gMl.js +0 -1
- package/web/dist/assets/ActivityDetail-julsmPDB.js +0 -1
- package/web/dist/assets/Graph-CS4tA7YS.js +0 -1
- package/web/dist/assets/Laws-BjbHJrJt.js +0 -3
- package/web/dist/assets/Library-CjC7BAWN.js +0 -1
- package/web/dist/assets/Skills-BvatJGfe.js +0 -1
- package/web/dist/assets/index-BnRWQbZb.js +0 -12
- package/web/dist/assets/index-D0f9tJlA.css +0 -1
- package/web/dist/assets/roman-DkYN0LX9.js +0 -1
package/docs/token-footprint.md
CHANGED
|
@@ -4,10 +4,11 @@ What a deployed harness costs in context-window tokens, per host. Token counts a
|
|
|
4
4
|
chars/4 estimates and shift a little with theme and version.
|
|
5
5
|
|
|
6
6
|
**Treat every number below as a floor, not a reading.** They were measured on a
|
|
7
|
-
`--footprint
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
`--footprint lean` build (the default) of the neutral theme when the tree carried
|
|
8
|
+
47 skills and 17 agents, and nothing re-measures this page automatically. For a
|
|
9
|
+
live figure on your own install, ask the agent for the `token-report` skill —
|
|
10
|
+
that is what it is for. A `--footprint full` build runs roughly 5k tokens
|
|
11
|
+
heavier on the root file.
|
|
11
12
|
|
|
12
13
|
## Always-on cost per session
|
|
13
14
|
|
|
@@ -15,40 +16,50 @@ The context every host injects at session start, before you type anything:
|
|
|
15
16
|
|
|
16
17
|
| Component | Claude Code | OpenCode | Bob | Copilot |
|
|
17
18
|
|---|---|---|---|---|
|
|
18
|
-
| Root instruction file | ~
|
|
19
|
-
| Skill metadata (name + description) | ~1.
|
|
20
|
-
| Agent metadata | ~0.
|
|
19
|
+
| Root instruction file | ~6.4k (`CLAUDE.md`) | ~6.4k (`AGENT.md`) | ~8.3k (`AGENTS.md`) | ~8.3k (`AGENTS.md`) |
|
|
20
|
+
| Skill metadata (name + description) | ~1.7k | ~1.7k | ~1.7k | ~1.7k |
|
|
21
|
+
| Agent metadata | ~0.5k | ~0.55k | ~0.5k | ~0.5k |
|
|
21
22
|
| Eager memory/notebook injection | ~1.2k (SessionStart hook) | ~1.2k (context plugin) | ~1.2k (SessionStart hook) | — (no hooks; read on demand) |
|
|
22
|
-
| **Total** | **~
|
|
23
|
+
| **Total** | **~9.8k** | **~9.9k** | **~11.7k** | **~10.5k** |
|
|
23
24
|
|
|
24
|
-
The emits are at parity by design: ~
|
|
25
|
-
window
|
|
26
|
-
|
|
25
|
+
The emits are at parity by design: ~10–12k tokens, about 5–6% of a 200k
|
|
26
|
+
window. Bob and Copilot carry the §3/§4 catalogue tables inline because their
|
|
27
|
+
hosts expose no native skill/agent inventory; Claude Code and OpenCode ship a
|
|
28
|
+
pointer to the host's own inventory instead, ~1.9k tokens lighter. Copilot also
|
|
29
|
+
runs without eager injection — it has no hook mechanism, so the
|
|
30
|
+
memory/notebook indexes load when the agent reads them, not eagerly. The
|
|
27
31
|
eager-injection path is budget-capped identically everywhere
|
|
28
32
|
(16 KB per file, 48 KB total ≈ 12k tokens ceiling), so growing Memory degrades
|
|
29
33
|
every host the same way instead of one silently falling behind.
|
|
30
34
|
|
|
31
35
|
## Where the tokens go
|
|
32
36
|
|
|
33
|
-
- **The root file is ~
|
|
37
|
+
- **The root file is ~65% of the bill**, and the constitution inside it is the
|
|
34
38
|
bulk of that — no longer one section but three: the Ontology, the nine Rules,
|
|
35
39
|
and the doctrine packs this install built in. **Two** levers move it, not one.
|
|
36
|
-
The [footprint dial](#/docs/footprint) (`lean`, the default,
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
The [footprint dial](#/docs/footprint) (`lean`, the default, swaps each Rule,
|
|
41
|
+
the Ontology and the longest prose sections — the preamble, §6 Notebook, §8
|
|
42
|
+
Wiki — for their hand-written condensations, and cuts each doctrine rule to its
|
|
43
|
+
heading plus first line; `full` inlines every rationale). And the
|
|
39
44
|
[doctrine packs](#/docs/setup-choices) — `geneseed-build --doctrines craft,rigor`
|
|
40
45
|
drops whole blocks of §2 at build time, which removes far more than the
|
|
41
46
|
footprint dial trims. Anything else is noise.
|
|
42
47
|
|
|
43
|
-
The
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
The lean condensations are authored, not generated. An earlier design shipped
|
|
49
|
+
the Ontology *whole* under `lean` on the grounds that truncating four flowing
|
|
50
|
+
sections to four orphan sentences would ship a worldview nobody could read —
|
|
51
|
+
the `LEAN:` blocks answer that by hand-writing the short form instead
|
|
52
|
+
(measured on `neutral`, the root file at `lean` went from 29,966 bytes to
|
|
53
|
+
25,614 with them).
|
|
48
54
|
- **Skill bodies (~53k) and agent bodies (~10k) are lazy** on every host —
|
|
49
55
|
loaded only when invoked. A typical skill costs ≤3k per invocation; the
|
|
50
56
|
heaviest (`react-view-transitions`, ~17.5k with its reference files) loads
|
|
51
57
|
its references progressively.
|
|
58
|
+
- **User-only skills leave the catalogue.** A skill whose source carries
|
|
59
|
+
`<!-- invocation: user -->` renders `disable-model-invocation: true`; Claude
|
|
60
|
+
Code and Bob then drop its description from the always-on skill metadata and
|
|
61
|
+
only `/name` opens it. Seven skills carry it today (the five teaching drills,
|
|
62
|
+
`herdr`, `opencode-theme`), roughly a seventh of the ~1.7k skill-metadata line.
|
|
52
63
|
- Plugin JavaScript (OpenCode, ~26k on disk) runs in the host runtime and
|
|
53
64
|
never enters the context window.
|
|
54
65
|
|
|
@@ -73,7 +84,7 @@ token counter also includes, none of which Geneseed controls:
|
|
|
73
84
|
per-repo install, a personal `~/.claude/CLAUDE.md`, or MCP servers each add
|
|
74
85
|
their own share.
|
|
75
86
|
|
|
76
|
-
So a ~
|
|
87
|
+
So a ~30k session on a work repo is normal arithmetic: ~10k harness +
|
|
77
88
|
~10k repo docs + ~5–10k host overhead. Set `GENESEED_DEBUG=1` (OpenCode) to
|
|
78
89
|
log exactly which files the context plugin injected, or run the SessionStart
|
|
79
90
|
command by hand to see the hook's payload.
|
package/docs/web/agents.md
CHANGED
|
@@ -5,4 +5,4 @@ title: "Agents"
|
|
|
5
5
|
kind: "concept"
|
|
6
6
|
link: {"hash": "#/section/agents", "label": "Browse the catalog →"}
|
|
7
7
|
---
|
|
8
|
-
{N_AGENTS} capability specialists — `reviewer`, `tester`, `architect`, `developer`, `docs`, `security`, `explorer`, plus a debate **council** the [[council]] skill convenes (`advocate`, `skeptic`, `pragmatist`, `steward`, `visionary`, `user-advocate`, `framer`, `empiricist`, `operator`, `historian`). You delegate by capability, not by folder: the harness picks the right specialist from the request.
|
|
8
|
+
{N_AGENTS} capability specialists — `reviewer`, `tester`, `architect`, `developer`, `docs`, `security`, `explorer`, `researcher`, plus a debate **council** the [[council]] skill convenes (`advocate`, `skeptic`, `pragmatist`, `steward`, `visionary`, `user-advocate`, `framer`, `empiricist`, `operator`, `historian`). You delegate by capability, not by folder: the harness picks the right specialist from the request.
|
|
@@ -16,7 +16,7 @@ Once installed, the agent doesn't change *how* you talk to your tool — it chan
|
|
|
16
16
|
### Try these prompts
|
|
17
17
|
|
|
18
18
|
- *"Use the **profile** skill"* — a short interview that drafts `PROFILE.md` (seeded empty at install), so the agent knows who you are and how you like answers pitched.
|
|
19
|
-
- *"Use the **
|
|
19
|
+
- *"Use the **brainstorm** skill on this feature request."*
|
|
20
20
|
- *"Delegate to the **reviewer** agent on the staged diff."*
|
|
21
21
|
- *"Use **brainstorm** then **plan** for how to add X."*
|
|
22
22
|
- *"Use **council** to debate whether we should ship Y now."*
|
package/docs/web/skills.md
CHANGED
|
@@ -5,4 +5,4 @@ title: "Skills"
|
|
|
5
5
|
kind: "concept"
|
|
6
6
|
link: {"hash": "#/section/skills", "label": "Browse the catalog →"}
|
|
7
7
|
---
|
|
8
|
-
{N_SKILLS} repeatable workflows the agent can invoke by name — from [[brainstorm]], [[
|
|
8
|
+
{N_SKILLS} repeatable workflows the agent can invoke by name — from [[brainstorm]], [[plan]], [[develop]] and [[debug]] through [[council]], [[research]] and [[ship]] to [[roast-me]]. A skill is a markdown playbook under `src/skills/`; the agent reads it before acting. The catalog link lists every one, live.
|
package/docs/web-ui.md
CHANGED
|
@@ -51,7 +51,7 @@ The left rail mirrors the harness's own shape:
|
|
|
51
51
|
| Group | Views |
|
|
52
52
|
| --- | --- |
|
|
53
53
|
| *(ungrouped)* | Dashboard |
|
|
54
|
-
| **📖 Codex** | Constitution · Rules · Profile · Skills · Agents · Library · Docs
|
|
54
|
+
| **📖 Codex** | Constitution · Rules · Profile · Skills · Agents · Library · Docs |
|
|
55
55
|
| **🩺 Care** | Activity · Changes · Doctor |
|
|
56
56
|
| **🎨 Setup** | Harness · Settings |
|
|
57
57
|
|
|
@@ -72,9 +72,10 @@ streams the output of background jobs and keeps their history across reloads.
|
|
|
72
72
|
### 🧬 Harness
|
|
73
73
|
|
|
74
74
|
- **Dashboard** (`#/`) — a live readout of what's deployed. Three lenses: **Status**
|
|
75
|
-
(readiness ring, a KPI band, a genome grid, recent activity), **Lineage** (
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
(readiness ring, a KPI band, a genome grid, recent activity), **Lineage** (the
|
|
76
|
+
source → render → deployed timeline and the genome-by-volume strand chart), and
|
|
77
|
+
**Operator** (a searchable table of every deployed capability). Headlines reflect the
|
|
78
|
+
active theme's voice.
|
|
78
79
|
|
|
79
80
|
The KPI band reads **Agents · Skills · Invariants · Local edits**. That third tile is
|
|
80
81
|
labelled *Invariants* and not *Constitution* on purpose: its value is the invariant count
|
|
@@ -107,15 +108,6 @@ streams the output of background jobs and keeps their history across reloads.
|
|
|
107
108
|
**Promote to rule** control turns a recurring lesson into a trial rule in
|
|
108
109
|
`user-rules.md` — provenance recorded, the source fact deleted so it isn't loaded
|
|
109
110
|
twice — landing you on the Rules page.
|
|
110
|
-
- **Graph** (`#/graph`) — the cross-link constellation across agents, skills and the
|
|
111
|
-
**invariants only**: every `[[wikilink]]` between agents/skills, plus every `Rule N`
|
|
112
|
-
mention that lands on a real invariant. ⚠ The reference pattern matches the tier noun
|
|
113
|
-
followed by a **Roman numeral** — `Rule`, `Law`, or whatever the active theme calls one,
|
|
114
|
-
which is how `Dictate III` still resolves under `imperial`. A doctrine address is
|
|
115
|
-
`<pack> <n>` and matches nothing, so **doctrine cross-links are not drawn**; the graph is
|
|
116
|
-
a view of the invariant backbone, not of the whole constitution. Hover to isolate a
|
|
117
|
-
neighbourhood, scroll to zoom, drag to pan, search to highlight; orphans dim out. Click a
|
|
118
|
-
node to open its spec.
|
|
119
111
|
|
|
120
112
|
### 📚 Learn
|
|
121
113
|
|
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.4.0"
|
|
6
6
|
}
|
package/js/README.md
CHANGED
|
@@ -13,7 +13,7 @@ a module that does not exist, is a test failure. `geneseed doctor` reports the s
|
|
|
13
13
|
|
|
14
14
|
```
|
|
15
15
|
bin/geneseed-cli.mjs the `geneseed` CLI — 20+ verbs, may NOT spawn
|
|
16
|
-
bin/geneseed-hook.mjs the hook entry —
|
|
16
|
+
bin/geneseed-hook.mjs the hook entry — 5 verbs, loads on EVERY tool call (~14 ms)
|
|
17
17
|
bin/build-driver.mjs `geneseed-build` — the generator's flags and emit targets
|
|
18
18
|
|
|
19
19
|
js/build/ src/ + themes/ → rendered text → a bundle → a per-host install
|
|
@@ -66,7 +66,7 @@ user co-owns, and the hook verbs those emitted configs then run.
|
|
|
66
66
|
| module | owns |
|
|
67
67
|
|---|---|
|
|
68
68
|
| `hosts.mjs` | The four host config dirs (opencode/claude/bob/copilot), plus `resolvePath`/`expanduser` |
|
|
69
|
-
| `hooks.mjs` | The verbs the emitted hooks run every session: `context`, `git-gate`, `rule-gate`, `learn` |
|
|
69
|
+
| `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 |
|
|
70
70
|
| `settings.mjs` | Merges into user-owned `settings.json`/`opencode.json`; owns the hook shim and the managed blocks |
|
|
71
71
|
| `native.mjs` | Capability specs → host-native subagents and skills. The impure half of the emit |
|
|
72
72
|
| `opencode.mjs` | OpenCode-only extras: colour themes, primary agent, `/commands`, plugins, overrides stub |
|
|
@@ -168,13 +168,12 @@ the deployed harness as JSON.
|
|
|
168
168
|
| `daemon.mjs` | Detached web start/stop/status/restart, the `.geneseed-web.json` record, and `openUrl` |
|
|
169
169
|
| `jobs.mjs` | `JobManager` and the action→argv table. The only module here that spawns job children |
|
|
170
170
|
| `docs.mjs` | The Docs tab: the `docs/web/` page registry, `?harness=` filtering, the five page kinds |
|
|
171
|
-
| `graph.mjs` | `/api/graph` alone — the agent/skill/law cross-link matrix built from rendered bodies |
|
|
172
171
|
| `activity.mjs` | The `activity/` session files: pid-liveness prune, list, detail, and the on/off flag |
|
|
173
172
|
|
|
174
173
|
**Before editing:** `makeHandler(state, jm, token, dist, holder)`'s arity is load-bearing — three
|
|
175
|
-
test call sites construct one directly and drive it without a socket.
|
|
176
|
-
|
|
177
|
-
`
|
|
174
|
+
test call sites construct one directly and drive it without a socket. `routes.mjs`'s tables
|
|
175
|
+
and inline lists are held against a surface list written out in
|
|
176
|
+
`tests/unit/web_server.test.mjs` — growing or shrinking the API edits both, in one commit.
|
|
178
177
|
|
|
179
178
|
---
|
|
180
179
|
|
package/js/build/emit-claude.mjs
CHANGED
|
@@ -10,8 +10,8 @@ import { VERSION_MARKER } from '../hosts/hosts.mjs';
|
|
|
10
10
|
import { loadAgentOverrides, writeNativeLayer } from '../hosts/native.mjs';
|
|
11
11
|
import { ensureAgentOverridesStub } from '../hosts/opencode.mjs';
|
|
12
12
|
import {
|
|
13
|
-
managedBlockRemove, managedBlockWrite, mergeClaudeSettings,
|
|
14
|
-
unwireClaudeSettings, wireClaudeExcludes,
|
|
13
|
+
managedBlockRemove, managedBlockWrite, mergeClaudeSettings, mergeCopilotSettings,
|
|
14
|
+
unwireClaudeExcludes, unwireClaudeSettings, wireClaudeExcludes,
|
|
15
15
|
} from '../hosts/settings.mjs';
|
|
16
16
|
import { writeText } from '../lib/fs.mjs';
|
|
17
17
|
import { isTruthy } from '../lib/json.mjs';
|
|
@@ -225,12 +225,19 @@ function claudeWire(job, claudeMdText, hasAgentText, doctrines = null, excludeRu
|
|
|
225
225
|
// settings.local.json — the personal, untracked file — never the team-shared
|
|
226
226
|
// settings.json, which would hand every teammate failing hooks pointing at this machine's
|
|
227
227
|
// node and this machine's checkout. (Bob documents no local variant, so it keeps
|
|
228
|
-
// settings.json.) Copilot
|
|
229
|
-
//
|
|
230
|
-
//
|
|
228
|
+
// settings.json.) Copilot's settings are a different shape entirely (one command per
|
|
229
|
+
// event, no matcher groups) and take the `else` branch below; its PROJECT scope wires
|
|
230
|
+
// nothing, because the CLI documents hooks in `~/.copilot/settings.json` only and a
|
|
231
|
+
// machine-absolute hook committed into `.github/` would fail on every teammate's machine.
|
|
231
232
|
if (!isCopilot) {
|
|
232
|
-
|
|
233
|
+
// Bob GLOBAL: its hooks doc puts the global file at `~/.bob/settings/settings.json`
|
|
234
|
+
// (nested), where an older emit wrote `~/.bob/settings.json`; the `oldSf !==
|
|
235
|
+
// settingsName` branch below unwires the old file, so a re-emit migrates. Bob's project
|
|
236
|
+
// file stays `.bob/settings.json`.
|
|
237
|
+
const settingsName = scope === 'project' && !isBob ? 'settings.local.json'
|
|
238
|
+
: isBob && scope === 'global' ? path.join('settings', 'settings.json') : 'settings.json';
|
|
233
239
|
const settingsPath = path.join(cfgDir, settingsName);
|
|
240
|
+
mkdirSync(path.dirname(settingsPath), { recursive: true });
|
|
234
241
|
managed.settings_file = settingsName;
|
|
235
242
|
// Migration: an older install wired hooks/excludes into a different file — unwire the
|
|
236
243
|
// recorded claims there, or they linger (and run) forever.
|
|
@@ -247,7 +254,7 @@ function claudeWire(job, claudeMdText, hasAgentText, doctrines = null, excludeRu
|
|
|
247
254
|
// DECIDES the install's packs, so the marker on disk is still the previous build's.
|
|
248
255
|
const [, managedHooks] = mergeClaudeSettings(
|
|
249
256
|
settingsPath, scope, oldSf === settingsName ? get(old, 'settings_hooks') : null, hookOpts,
|
|
250
|
-
doctrines, excludeRules,
|
|
257
|
+
doctrines, excludeRules, host,
|
|
251
258
|
);
|
|
252
259
|
managed.settings_hooks = managedHooks;
|
|
253
260
|
|
|
@@ -280,6 +287,14 @@ function claudeWire(job, claudeMdText, hasAgentText, doctrines = null, excludeRu
|
|
|
280
287
|
} else if (priorExcl.length) {
|
|
281
288
|
managed.settings_excludes = priorExcl;
|
|
282
289
|
}
|
|
290
|
+
} else if (scope === 'global') {
|
|
291
|
+
// `copilot_hooks`, not `settings_hooks`: a distinct key so the Claude-shaped unwire and
|
|
292
|
+
// integrity code, which walks `settings_hooks` as event→group arrays, never sees a
|
|
293
|
+
// Copilot record it would misread.
|
|
294
|
+
const settingsPath = path.join(cfgDir, 'settings.json');
|
|
295
|
+
managed.settings_file = 'settings.json';
|
|
296
|
+
const [, claims] = mergeCopilotSettings(settingsPath, get(old, 'copilot_hooks'), hookOpts);
|
|
297
|
+
managed.copilot_hooks = claims;
|
|
283
298
|
}
|
|
284
299
|
return managed;
|
|
285
300
|
}
|
package/js/build/render.mjs
CHANGED
|
@@ -34,7 +34,7 @@ import { readText } from '../lib/fs.mjs';
|
|
|
34
34
|
import { parseJson } from '../lib/json.mjs';
|
|
35
35
|
import { normcase, comparePaths } from '../lib/paths.mjs';
|
|
36
36
|
// `js/build/source.mjs` imports nothing but node builtins, so this direction cannot cycle.
|
|
37
|
-
import { PACK_ORDER } from './source.mjs';
|
|
37
|
+
import { PACK_ORDER, readPackText } from './source.mjs';
|
|
38
38
|
|
|
39
39
|
/** Document STRUCTURE is theme-INDEPENDENT — mirrors `_build_render.STRUCTURE`. */
|
|
40
40
|
export const STRUCTURE = {
|
|
@@ -69,6 +69,16 @@ const TOKEN_RE = /\{\{([A-Z_][A-Z0-9_]*)\}\}/g;
|
|
|
69
69
|
const INCLUDE_RE = /^[ \t]*<!--[ \t]*INCLUDE:[ \t]*(?<path>[^ \t]+)[ \t]*-->[ \t]*$/gm;
|
|
70
70
|
const CATALOG_BLOCK_RE =
|
|
71
71
|
/[ \t]*<!-- CATALOG:begin -->\n(?<table>[\s\S]*?)[ \t]*<!-- CATALOG:else -->\n(?<pointer>[\s\S]*?)[ \t]*<!-- CATALOG:end -->\n/g;
|
|
72
|
+
// The footprint twin of CATALOG. Before it existed, `lean` could only reach the two
|
|
73
|
+
// include-driven corpora (`laws/universal.md` and the doctrine packs, both truncated
|
|
74
|
+
// mechanically to heading + first sentence); every other section of the root template was
|
|
75
|
+
// byte-identical across footprints, which is how ~1.5k tokens of §-level prose rode into
|
|
76
|
+
// every lean install. A LEAN block ships a HAND-WRITTEN condensation instead of a
|
|
77
|
+
// truncation — the objection `docs/token-footprint.md` raised against terse-ing the
|
|
78
|
+
// Ontology ("four orphan sentences") is answered by authoring the short form, not
|
|
79
|
+
// generating it. Same marker grammar as CATALOG so the two stay learnable as one idea.
|
|
80
|
+
const LEAN_BLOCK_RE =
|
|
81
|
+
/[ \t]*<!-- LEAN:begin -->\n(?<full>[\s\S]*?)[ \t]*<!-- LEAN:else -->\n(?<lean>[\s\S]*?)[ \t]*<!-- LEAN:end -->\n/g;
|
|
72
82
|
|
|
73
83
|
/**
|
|
74
84
|
* `PurePath.suffix`. Not `path.extname`: Python returns '' for a name whose only dot is
|
|
@@ -147,6 +157,11 @@ function resolveCatalogs(text, nativeCatalog) {
|
|
|
147
157
|
return text.replace(CATALOG_BLOCK_RE, (_m, table, pointer) => (nativeCatalog ? pointer : table));
|
|
148
158
|
}
|
|
149
159
|
|
|
160
|
+
/** LEAN blocks — full text or the hand-written condensation, by footprint. */
|
|
161
|
+
function resolveLean(text, footprint) {
|
|
162
|
+
return text.replace(LEAN_BLOCK_RE, (_m, full, lean) => (footprint === 'lean' ? lean : full));
|
|
163
|
+
}
|
|
164
|
+
|
|
150
165
|
/**
|
|
151
166
|
* `re.split(r"(?m)^(?=### )", text)`.
|
|
152
167
|
*
|
|
@@ -168,15 +183,15 @@ function splitAtLawHeadings(text) {
|
|
|
168
183
|
}
|
|
169
184
|
|
|
170
185
|
/**
|
|
171
|
-
*
|
|
186
|
+
* Heading + first sentence, per `### ` block. DOCTRINES ONLY, now.
|
|
172
187
|
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
188
|
+
* The laws used to go through this too, and it made a law's lean footprint a function of its
|
|
189
|
+
* punctuation: Law II shipped as one 36-character sentence and lost its stop-and-ask
|
|
190
|
+
* mechanism, while a law written as one colon-chained run-on shipped nearly whole. The laws
|
|
191
|
+
* now carry authored LEAN blocks in `src/laws/universal.md`, the way the ontology already did,
|
|
192
|
+
* and `resolveLean` picks the half. Doctrine rules stay machine-cut: there are ~23 of them
|
|
193
|
+
* across four packs, and a pack is a practice catalogue whose first sentence is written to
|
|
194
|
+
* stand alone.
|
|
180
195
|
*/
|
|
181
196
|
function terseBlocks(text) {
|
|
182
197
|
const blocks = splitAtLawHeadings(text);
|
|
@@ -197,16 +212,14 @@ function terseBlocks(text) {
|
|
|
197
212
|
return out.join('\n\n');
|
|
198
213
|
}
|
|
199
214
|
|
|
200
|
-
/**
|
|
201
|
-
export function
|
|
215
|
+
/** The pointer under §1 at lean. The laws' lean text itself is authored in LEAN blocks. */
|
|
216
|
+
export function lawsPointer(theme, lawsPrefix = '') {
|
|
202
217
|
const law = theme.LAW ?? 'Law';
|
|
203
218
|
const lawsDir = theme.DIR_LAWS ?? 'laws';
|
|
204
|
-
|
|
205
|
-
`> Each ${law} above is given in brief — the rule, not its reasoning. The `
|
|
219
|
+
return `> Each ${law} above is given in brief — the rule, not its reasoning. The `
|
|
206
220
|
+ `complete, binding text of every ${law} is in \`${lawsPrefix}${lawsDir}/universal.md\`; `
|
|
207
221
|
+ `read it whenever a ${law}'s application is unclear, and before any act touching `
|
|
208
|
-
+
|
|
209
|
-
return terseBlocks(text) + '\n\n' + pointer;
|
|
222
|
+
+ 'secrets, deletion, git history, scope, or untrusted content.';
|
|
210
223
|
}
|
|
211
224
|
|
|
212
225
|
/** `_build_render.render_file`. */
|
|
@@ -225,12 +238,13 @@ export function renderFile(cfg, filePath, theme, footprint = 'full', lawsPrefix
|
|
|
225
238
|
new Set(visiting).add(normcase(here)), nativeCatalog)
|
|
226
239
|
.replace(/\n+$/, ''); // `.rstrip("\n")`
|
|
227
240
|
if (footprint === 'lean' && rel === 'laws/universal.md') {
|
|
228
|
-
inner =
|
|
241
|
+
inner = `${inner}\n\n${lawsPointer(theme, lawsPrefix)}`;
|
|
229
242
|
}
|
|
230
243
|
return inner;
|
|
231
244
|
});
|
|
232
245
|
|
|
233
246
|
text = resolveCatalogs(text, nativeCatalog);
|
|
247
|
+
text = resolveLean(text, footprint);
|
|
234
248
|
return substitute(text, theme);
|
|
235
249
|
}
|
|
236
250
|
|
|
@@ -291,7 +305,7 @@ function activeDoctrines(cfg) {
|
|
|
291
305
|
// `Active packs:` marker a later reader parses back — agrees with what was rendered.
|
|
292
306
|
const excluded = new Set(cfg.excludeRules ?? []);
|
|
293
307
|
const survives = (pack) => {
|
|
294
|
-
const ids = [...
|
|
308
|
+
const ids = [...readPackText(path.join(dir, `${pack}.md`))
|
|
295
309
|
.matchAll(/^### \{\{DOCTRINE\}\} ([a-z]+) (\d+)\b/gm)]
|
|
296
310
|
.map((m) => `${m[1]}.${Number(m[2])}`);
|
|
297
311
|
return ids.some((id) => !excluded.has(id));
|
|
@@ -340,7 +354,7 @@ function doctrinesBody(cfg, theme, active, footprint, lawsPrefix) {
|
|
|
340
354
|
if (!active.length) return '';
|
|
341
355
|
const excluded = new Set(cfg.excludeRules ?? []);
|
|
342
356
|
const parts = active.map((name) => {
|
|
343
|
-
const raw = dropExcludedRules(
|
|
357
|
+
const raw = dropExcludedRules(readPackText(path.join(cfg.src, 'doctrines', `${name}.md`)),
|
|
344
358
|
name, excluded);
|
|
345
359
|
const body = substitute(raw, theme).trim();
|
|
346
360
|
return footprint === 'lean' ? terseBlocks(body) : body;
|
|
@@ -440,8 +454,12 @@ export function renderAll(cfg, themeName, {
|
|
|
440
454
|
for (const file of sortedSourceFiles(cfg.src)) {
|
|
441
455
|
const rel = path.relative(cfg.src, file);
|
|
442
456
|
const outRel = destRel(themedRel(rel, theme)).split(path.sep).join('/'); // as_posix()
|
|
457
|
+
// The on-disk `laws/` and `ontology/` are the "complete, binding text" the lean pointer
|
|
458
|
+
// promises. They render at full whatever the footprint; only what AGENT.md INLINES leans.
|
|
459
|
+
// Keyed on the SOURCE rel, not the themed one, so a DIR_* rename cannot un-exempt them.
|
|
460
|
+
const fp = /^(?:laws|ontology)[\\/]/.test(rel) ? 'full' : footprint;
|
|
443
461
|
items.push(TEXT_SUFFIXES.has(suffixOf(path.basename(file)))
|
|
444
|
-
? { rel: outRel, text: renderFile(cfg, file, theme,
|
|
462
|
+
? { rel: outRel, text: renderFile(cfg, file, theme, fp, lawsPrefix, new Set(), nativeCatalog), src: file }
|
|
445
463
|
: { rel: outRel, text: null, src: file });
|
|
446
464
|
}
|
|
447
465
|
return { theme, items };
|
package/js/build/source.mjs
CHANGED
|
@@ -24,9 +24,29 @@
|
|
|
24
24
|
* over 259 cells and compares the tree byte-for-byte, and every one of them builds a cfg
|
|
25
25
|
* from these paths. A depth error or a renamed key fails 259 cells, not zero.
|
|
26
26
|
*/
|
|
27
|
-
import { readdirSync, readFileSync } from 'node:fs';
|
|
27
|
+
import { readdirSync, readFileSync, statSync } from 'node:fs';
|
|
28
28
|
import path from 'node:path';
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* One doctrine pack file used to be read three times per render — the survives gate and the
|
|
32
|
+
* body in `js/build/render.mjs`, plus `knownRuleIds` below — and the golden matrix multiplies
|
|
33
|
+
* that by 261 cells. Keyed on mtime, not just path, because the one long-lived process (the
|
|
34
|
+
* web daemon) renders previews of a `src/` the user may be editing between requests; a
|
|
35
|
+
* path-only memo would serve yesterday's pack forever. Newlines come back folded to LF, the
|
|
36
|
+
* same normalisation `readText` gave the render callers — `knownRuleIds` matched identically
|
|
37
|
+
* on raw or folded text, so one behaviour serves both.
|
|
38
|
+
*/
|
|
39
|
+
const _packTexts = new Map();
|
|
40
|
+
export function readPackText(file) {
|
|
41
|
+
const key = path.resolve(file);
|
|
42
|
+
const mtime = statSync(file).mtimeMs;
|
|
43
|
+
const hit = _packTexts.get(key);
|
|
44
|
+
if (hit && hit.mtime === mtime) return hit.text;
|
|
45
|
+
const text = readFileSync(file, 'utf8').replace(/\r\n?/g, '\n');
|
|
46
|
+
_packTexts.set(key, { mtime, text });
|
|
47
|
+
return text;
|
|
48
|
+
}
|
|
49
|
+
|
|
30
50
|
/**
|
|
31
51
|
* `_build_core.ROOT` — the checkout, from this file's own location (`js/..`).
|
|
32
52
|
*
|
|
@@ -104,7 +124,7 @@ export function knownRuleIds() {
|
|
|
104
124
|
for (const pack of PACK_ORDER) {
|
|
105
125
|
const file = path.join(SRC, 'doctrines', `${pack}.md`);
|
|
106
126
|
let text = '';
|
|
107
|
-
try { text =
|
|
127
|
+
try { text = readPackText(file); } catch { continue; }
|
|
108
128
|
for (const m of text.matchAll(/^### \{\{DOCTRINE\}\} ([a-z]+) (\d+)\b/gm)) {
|
|
109
129
|
if (m[1] === pack) out.push(`${pack}.${Number(m[2])}`);
|
|
110
130
|
}
|
package/js/cli-table.json
CHANGED
|
@@ -155,6 +155,21 @@
|
|
|
155
155
|
"is_flag": false,
|
|
156
156
|
"type": null,
|
|
157
157
|
"hidden": false
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"names": [
|
|
161
|
+
"--host"
|
|
162
|
+
],
|
|
163
|
+
"dest": "host",
|
|
164
|
+
"metavar": null,
|
|
165
|
+
"help": "the host that reads the verdict (set by the emitted hook): 'claude' (default) asks; 'copilot' has no ask tier, so Laws I/IV block and everything else is a stderr warning",
|
|
166
|
+
"choices": null,
|
|
167
|
+
"default": null,
|
|
168
|
+
"required": false,
|
|
169
|
+
"nargs": null,
|
|
170
|
+
"is_flag": false,
|
|
171
|
+
"type": null,
|
|
172
|
+
"hidden": false
|
|
158
173
|
}
|
|
159
174
|
],
|
|
160
175
|
"mutex": []
|
|
@@ -356,6 +371,21 @@
|
|
|
356
371
|
"is_flag": false,
|
|
357
372
|
"type": null,
|
|
358
373
|
"hidden": false
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
"names": [
|
|
377
|
+
"--host"
|
|
378
|
+
],
|
|
379
|
+
"dest": "host",
|
|
380
|
+
"metavar": null,
|
|
381
|
+
"help": "the host that reads the verdict (set by the emitted hook): 'claude' (default) asks; 'copilot' has no ask tier, so Laws I/IV block and everything else is a stderr warning",
|
|
382
|
+
"choices": null,
|
|
383
|
+
"default": null,
|
|
384
|
+
"required": false,
|
|
385
|
+
"nargs": null,
|
|
386
|
+
"is_flag": false,
|
|
387
|
+
"type": null,
|
|
388
|
+
"hidden": false
|
|
359
389
|
}
|
|
360
390
|
],
|
|
361
391
|
"mutex": []
|
|
@@ -604,6 +634,21 @@
|
|
|
604
634
|
"is_flag": false,
|
|
605
635
|
"type": null,
|
|
606
636
|
"hidden": false
|
|
637
|
+
},
|
|
638
|
+
{
|
|
639
|
+
"names": [
|
|
640
|
+
"--host"
|
|
641
|
+
],
|
|
642
|
+
"dest": "host",
|
|
643
|
+
"metavar": null,
|
|
644
|
+
"help": "the host that reads the verdict (set by the emitted hook): 'claude' (default) asks; 'copilot' has no ask tier, so Laws I/IV block and everything else is a stderr warning",
|
|
645
|
+
"choices": null,
|
|
646
|
+
"default": null,
|
|
647
|
+
"required": false,
|
|
648
|
+
"nargs": null,
|
|
649
|
+
"is_flag": false,
|
|
650
|
+
"type": null,
|
|
651
|
+
"hidden": false
|
|
607
652
|
}
|
|
608
653
|
],
|
|
609
654
|
"mutex": []
|
|
@@ -764,6 +809,45 @@
|
|
|
764
809
|
]
|
|
765
810
|
]
|
|
766
811
|
},
|
|
812
|
+
{
|
|
813
|
+
"name": "tool-gate",
|
|
814
|
+
"help": "toolCall hook (Copilot): git-gate and rule-gate fused behind one command, dispatched on the payload",
|
|
815
|
+
"description": "",
|
|
816
|
+
"positionals": [],
|
|
817
|
+
"options": [
|
|
818
|
+
{
|
|
819
|
+
"names": [
|
|
820
|
+
"--root"
|
|
821
|
+
],
|
|
822
|
+
"dest": "root",
|
|
823
|
+
"metavar": null,
|
|
824
|
+
"help": "the install's own dir (set by the emitted hook); enables the sovereign-repo bypass from <root>/excludes.json",
|
|
825
|
+
"choices": null,
|
|
826
|
+
"default": null,
|
|
827
|
+
"required": false,
|
|
828
|
+
"nargs": null,
|
|
829
|
+
"is_flag": false,
|
|
830
|
+
"type": null,
|
|
831
|
+
"hidden": false
|
|
832
|
+
},
|
|
833
|
+
{
|
|
834
|
+
"names": [
|
|
835
|
+
"--host"
|
|
836
|
+
],
|
|
837
|
+
"dest": "host",
|
|
838
|
+
"metavar": null,
|
|
839
|
+
"help": "the host that reads the verdict (set by the emitted hook): 'claude' (default) asks; 'copilot' has no ask tier, so Laws I/IV block and everything else is a stderr warning",
|
|
840
|
+
"choices": null,
|
|
841
|
+
"default": null,
|
|
842
|
+
"required": false,
|
|
843
|
+
"nargs": null,
|
|
844
|
+
"is_flag": false,
|
|
845
|
+
"type": null,
|
|
846
|
+
"hidden": false
|
|
847
|
+
}
|
|
848
|
+
],
|
|
849
|
+
"mutex": []
|
|
850
|
+
},
|
|
767
851
|
{
|
|
768
852
|
"name": "tui",
|
|
769
853
|
"help": "print the harness at a glance (interactive terminals only)",
|