geneseed 4.9.4 → 4.10.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 +21 -0
- package/README.md +13 -0
- package/SHIPPED.md +1 -1
- package/docs/README.md +1 -1
- package/docs/concepts/collaboration.md +1 -1
- package/docs/concepts/enforcement-table.md +1 -1
- package/docs/concepts/foreman-mode.md +1 -1
- package/docs/concepts/kits.md +79 -0
- package/docs/concepts/loop-branch.md +1 -1
- package/docs/concepts/loop-bricks.md +1 -1
- package/docs/concepts/loop-contracts.md +1 -1
- package/docs/concepts/loop-gates.md +1 -1
- package/docs/concepts/loop-notes.md +1 -1
- package/docs/concepts/loop-risk.md +1 -1
- package/docs/concepts/loop-templates.md +1 -1
- package/docs/concepts/loops.md +1 -1
- package/docs/concepts/lsp.md +1 -1
- package/docs/extending.md +108 -4
- package/docs/guides/install-scripted.md +1 -1
- package/docs/reference/glossary.md +1 -0
- package/docs/web-ui.md +4 -0
- package/harness.config.json +1 -1
- package/js/build/args.mjs +27 -6
- package/js/build/driver.mjs +29 -8
- package/js/build/emit-claude.mjs +3 -1
- package/js/build/generate.mjs +10 -5
- package/js/build/render.mjs +94 -11
- package/js/build/source.mjs +117 -4
- package/js/cli-table.json +15 -0
- package/js/hosts/installs.mjs +35 -2
- package/js/hosts/native.mjs +14 -4
- package/js/inspect/checks-authoring.mjs +6 -3
- package/js/inspect/checks-build.mjs +7 -1
- package/js/inspect/checks-counts.mjs +153 -6
- package/js/inspect/checks-repo.mjs +95 -20
- package/js/inspect/diff.mjs +3 -1
- package/js/inspect/inventory.mjs +1 -0
- package/js/inspect/status.mjs +44 -3
- package/js/inspect/validate.mjs +3 -3
- package/js/maintain/setup.mjs +30 -6
- package/js/maintain/update.mjs +1 -1
- package/js/web/actions.mjs +30 -6
- package/js/web/api.mjs +40 -3
- package/js/web/routes.mjs +2 -1
- package/package.json +1 -1
- package/registry.json +63 -0
- package/src/AGENT.md.tmpl +2 -2
- package/src/agents/devops-engineer.md +45 -0
- package/src/agents/docker-expert.md +45 -0
- package/src/agents/kubernetes-specialist.md +45 -0
- package/src/agents/reviewer.md +2 -0
- package/src/agents/spring-boot-engineer.md +48 -0
- package/src/agents/spring-reviewer.md +49 -0
- package/src/agents/spring-security.md +50 -0
- package/src/kits/spring-boot/rules.md +10 -0
- package/src/kits/spring-boot.json +23 -0
- package/src/licenses/mattpocock-skills.txt +21 -0
- package/src/licenses/piomin-spring-boot.txt +190 -0
- package/src/licenses/superpowers.txt +21 -0
- package/src/licenses/vibe-wise.txt +21 -0
- package/src/skills/brainstorm/builder-check.md +2 -0
- package/src/skills/brainstorm.md +4 -0
- package/src/skills/codebase-design.md +4 -0
- package/src/skills/daydream/VENDOR.md +7 -7
- package/src/skills/debug.md +5 -1
- package/src/skills/develop.md +2 -0
- package/src/skills/geneseed-code-review.md +6 -2
- package/src/skills/git-history.md +2 -0
- package/src/skills/handoff.md +2 -0
- package/src/skills/learn-mode.md +3 -2
- package/src/skills/parallel-agents.md +2 -0
- package/src/skills/pipeline.md +2 -0
- package/src/skills/plan.md +4 -0
- package/src/skills/prototype.md +2 -0
- package/src/skills/react-view-transitions/AGENTS.md +646 -221
- package/src/skills/react-view-transitions/SKILL.md +28 -16
- package/src/skills/react-view-transitions/VENDOR.md +10 -8
- package/src/skills/react-view-transitions/references/css-recipes.md +72 -2
- package/src/skills/react-view-transitions/references/implementation.md +14 -26
- package/src/skills/react-view-transitions/references/nextjs.md +94 -37
- package/src/skills/react-view-transitions/references/patterns.md +60 -48
- package/src/skills/react-view-transitions/references/troubleshooting.md +47 -0
- package/src/skills/research.md +2 -0
- package/src/skills/review-response.md +2 -0
- package/src/skills/rule.md +2 -0
- package/src/skills/ship.md +2 -0
- package/src/skills/skill-forge.md +1 -0
- package/src/skills/spring-boot/cloud.md +103 -0
- package/src/skills/spring-boot/java-modern.md +71 -0
- package/src/skills/spring-boot/logging.md +89 -0
- package/src/skills/spring-boot/security.md +112 -0
- package/src/skills/spring-boot/web.md +112 -0
- package/src/skills/spring-boot.md +57 -0
- package/src/skills/spring-data-jpa/patterns.md +68 -0
- package/src/skills/spring-data-jpa.md +81 -0
- package/src/skills/spring-testing.md +93 -0
- package/src/skills/wayfinder.md +2 -0
- package/src/upstreams.json +59 -0
- package/themes/_TEMPLATE.json +9 -0
- package/themes/biker.json +9 -0
- package/themes/commentator.json +9 -0
- package/themes/cyberpunk.json +9 -0
- package/themes/gamer.json +9 -0
- package/themes/imperial.json +9 -0
- package/themes/joker.json +9 -0
- package/themes/marvin.json +9 -0
- package/themes/mean.json +9 -0
- package/themes/military.json +9 -0
- package/themes/neutral.json +9 -0
- package/themes/pirate.json +9 -0
- package/themes/sports.json +9 -0
- package/themes/verstappen.json +9 -0
- package/themes/wizard.json +9 -0
- package/web/dist/assets/{Activity-C8sZVIUU.js → Activity-BjyJaf8e.js} +1 -1
- package/web/dist/assets/{ActivityDetail-Cc6vWUS4.js → ActivityDetail-Bo9nRDNV.js} +1 -1
- package/web/dist/assets/{Dashboard-C5ATa3ow.js → Dashboard-BQJGU7vE.js} +2 -2
- package/web/dist/assets/{Docs-tDW7i7mo.js → Docs-DLVckkew.js} +1 -1
- package/web/dist/assets/{GreenhouseView-zk2Uzc3A.js → GreenhouseView--DML7CW3.js} +1 -1
- package/web/dist/assets/{Installs-CB_rvbBP.js → Installs-CxCiln1F.js} +2 -2
- package/web/dist/assets/{JournalView-Bfj27XHC.js → JournalView-ymgZleHi.js} +1 -1
- package/web/dist/assets/{Laws-BA3CzUf5.js → Laws-BkKnZOBI.js} +1 -1
- package/web/dist/assets/Library-C8ercIZv.js +1 -0
- package/web/dist/assets/{Loops-DaPk-ckL.js → Loops-C95zhbFl.js} +1 -1
- package/web/dist/assets/{OperatorHudView-CyiGhiHk.js → OperatorHudView-B9qwc3w3.js} +1 -1
- package/web/dist/assets/{Personal-Bwd27CmT.js → Personal-DyR72AF-.js} +1 -1
- package/web/dist/assets/{Tabs-BOD7Kaz1.js → Tabs-ChCr_HqV.js} +1 -1
- package/web/dist/assets/api-BfpZmUMI.js +1 -0
- package/web/dist/assets/{drawable-CyLe1gtW.js → drawable-CgYi0Dtg.js} +1 -1
- package/web/dist/assets/index-B32EvfDT.js +2 -0
- package/web/dist/index.html +2 -2
- package/web/dist/assets/Library-BWwiy9Vb.js +0 -1
- package/web/dist/assets/api-CiXlkbe0.js +0 -1
- package/web/dist/assets/index-DQXG_mqo.js +0 -2
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,27 @@ label. For the capability ↔ spec map, see [SHIPPED.md](SHIPPED.md).
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
## [4.10.0] — 2026-10-20
|
|
12
|
+
|
|
13
|
+
Minor: opt-in kits (and a first one, `spring-boot`), credits for adapted content, a weekly upstream-drift check, and a web Kits tab.
|
|
14
|
+
|
|
15
|
+
### Upgrade notes
|
|
16
|
+
- Run `geneseed rebuild-all` once after updating. Default installs are unchanged (no kit is on); enable one with `geneseed build --kits spring-boot` or the new **Installs → Kits** tab.
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- **Kits: opt-in bundles of skills, agents and rules** (#343). `src/kits/<kit>.json` declares a kit; `--kits a,b` turns kits on (`none` turns all off), the install records `Active kits:` and `rebuild-all`, `update`, `migrate` and the web console keep it. A plain build warns before it drops a kit an install has; `status` lists kits and files you edited that were kept from a kit turned off. The doctor gates kit membership, links (kit → core only), registry rows and each kit's rules budget.
|
|
20
|
+
- **The `spring-boot` kit** (#349), adapted from piomin/claude-ai-spring-boot (Apache-2.0) and rewritten for Spring Boot 4: skills `spring-boot`, `spring-data-jpa`, `spring-testing`; agents `spring-boot-engineer`, `spring-reviewer` and `spring-security` (both read-only), `kubernetes-specialist`, `docker-expert`, `devops-engineer`; kit rules. A banned-API gate keeps removed Boot APIs (`@MockBean`, `javax.persistence`, …) out of kit files.
|
|
21
|
+
- **Credits for adapted content** (#342). `src/upstreams.json` records each upstream's repo, commit, author and licence; a `{{CREDIT:key:path}}` token renders an "Adapted from … modified." line and the skill ships the licence beside it. Superpowers, Matt Pocock's skills and vibe-wise ports are now credited; the README gains a Credits section, gated by the doctor.
|
|
22
|
+
- **Weekly upstream-drift check** (#344, #351). A CI job compares every pinned upstream with its HEAD on the files Geneseed uses and keeps one tracking issue per upstream; vendored skills are compared on their folder only, and `"frozen": true` entries are skipped.
|
|
23
|
+
- **Web console: Installs → Kits** (#350). One row per global and project install, a switch per kit, apply through the usual confirm and rebuild job.
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
- `react-view-transitions` re-vendored at upstream HEAD (Next.js no longer needs the `experimental.viewTransition` flag; back/forward navigation and shared-element guidance updated) (#351).
|
|
27
|
+
- `debug` proves a forced red landed by diffing against a pristine copy; `geneseed-code-review` searches the repo for its standards files (#351).
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
- The web install action no longer re-themes an install you are not viewing (#350).
|
|
31
|
+
|
|
11
32
|
## [4.9.4] — 2026-10-18
|
|
12
33
|
|
|
13
34
|
Patch: `git reset --hard` asks only when it would abandon existing commits, not over uncommitted work.
|
package/README.md
CHANGED
|
@@ -367,6 +367,19 @@ Three things that bite when you don't know them:
|
|
|
367
367
|
- **The version has one owner: `harness.config.json`.** `package.json` mirrors it and a test fails the fork. Never `npm version` — it edits one of the two.
|
|
368
368
|
- **Publishing is deliberate and manual.** `.github/workflows/publish.yml` uses npm trusted publishing (OIDC); there is no `NPM_TOKEN` in this repository and there must not be one. It runs only from Actions → publish → Run workflow, and the npm-side trusted publisher is keyed on that workflow's **filename** — renaming the file breaks publishing with no local symptom, which is why the file names itself in its own header and a test asserts the two agree.
|
|
369
369
|
|
|
370
|
+
## 🙏 Credits
|
|
371
|
+
|
|
372
|
+
Some skills and agents are adapted from other open-source skill collections, under the MIT or Apache-2.0 licence (named on each entry below). Each adapted skill names its source in its own text, and ships that upstream's licence beside it. `src/upstreams.json` pins every upstream to a commit.
|
|
373
|
+
|
|
374
|
+
- `daydream` — [glebis/claude-skills](https://github.com/glebis/claude-skills) by Gleb Kalinin (MIT): the vendored `daydream` skill.
|
|
375
|
+
- `react-view-transitions` — [vercel-labs/agent-skills](https://github.com/vercel-labs/agent-skills) by Vercel Engineering (MIT): the vendored `react-view-transitions` skill.
|
|
376
|
+
- `mattpocock-skills` — [mattpocock/skills](https://github.com/mattpocock/skills) by Matt Pocock (MIT): the vocabulary and procedures behind codebase-design, wayfinder, prototype, develop, handoff and research, the spec pass of geneseed-code-review, the question rounds of brainstorm, the slicing of plan, and the reproduction step of debug.
|
|
377
|
+
- `mattpocock-skills-frozen` — [mattpocock/skills](https://github.com/mattpocock/skills) by Matt Pocock (MIT), pinned at 391a2701, the last commit carrying `resolving-merge-conflicts` (deleted upstream since): the merge-conflict step of git-history, from a skill upstream has since deleted, so the weekly drift check skips it.
|
|
378
|
+
- `mattpocock-skills-2509` — [mattpocock/skills](https://github.com/mattpocock/skills) by Matt Pocock (MIT), a second key for the same repo, at the same pin as `mattpocock-skills` today (5beb35b0): the PR body of ship and the guardrail clause of rule.
|
|
379
|
+
- `superpowers` — [obra/superpowers](https://github.com/obra/superpowers) by Jesse Vincent (MIT): the core of debug, the size step and builder check of brainstorm, the plan rules of plan, the reviewer brief behind geneseed-code-review and the reviewer agent, the review-handling steps of review-response, and the ruling format of pipeline and parallel-agents.
|
|
380
|
+
- `vibe-wise` — [nykooi1/vibe-wise](https://github.com/nykooi1/vibe-wise) by Noah Kim (MIT): the state file, levels and checkpoints of learn-mode.
|
|
381
|
+
- `piomin-spring-boot` — [piomin/claude-ai-spring-boot](https://github.com/piomin/claude-ai-spring-boot) by Piotr Mińkowski (Apache-2.0): the Spring Boot kit's skills and agents, rewritten and condensed for Boot 4.
|
|
382
|
+
|
|
370
383
|
## 📄 License
|
|
371
384
|
|
|
372
385
|
[MIT](LICENSE) — built by [@Arylmera](https://github.com/Arylmera).
|
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: **4.
|
|
23
|
+
> string. Current label: **4.10.0**.
|
|
24
24
|
|
|
25
25
|
## Capabilities
|
|
26
26
|
|
package/docs/README.md
CHANGED
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
|
|
43
43
|
**Memory** — [Memory](concepts/memory.md) · [Notebook](concepts/notebook.md)
|
|
44
44
|
|
|
45
|
-
**Configuration** — [Footprint](concepts/footprint.md) · [Themes](concepts/themes.md) · [Exclusions](concepts/exclusions.md) · [Code intelligence (LSP)](concepts/lsp.md)
|
|
45
|
+
**Configuration** — [Footprint](concepts/footprint.md) · [Themes](concepts/themes.md) · [Exclusions](concepts/exclusions.md) · [Kits](concepts/kits.md) · [Code intelligence (LSP)](concepts/lsp.md)
|
|
46
46
|
|
|
47
47
|
**Working together** — [Collaboration](concepts/collaboration.md) · [Foreman mode](concepts/foreman-mode.md)
|
|
48
48
|
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
group: concepts
|
|
3
|
+
order: 13
|
|
4
|
+
title: "Kits"
|
|
5
|
+
kind: "concept"
|
|
6
|
+
section: "Configuration"
|
|
7
|
+
description: "Opt-in bundles of skills, agents and rules for one stack, off unless you name them."
|
|
8
|
+
---
|
|
9
|
+
A **kit** is an opt-in bundle of skills, agents and rules for one stack. Kits are off by default and switched on per install, project or global, on every host. An install without a kit pays nothing for it. Inside an install that has it on, a kit's skills and agents are ordinary: the model picks them as it picks any other.
|
|
10
|
+
|
|
11
|
+
> **You already know this:** an optional dependency group, like `pip install pkg[postgres]`. The core install stays small, and you opt into the extra parts for your stack.
|
|
12
|
+
|
|
13
|
+
The first kit is `spring-boot`, for Spring Boot 4 (Spring Framework 7, Java 21+) projects. It adds nine members:
|
|
14
|
+
|
|
15
|
+
| Member | Kind | What it is for |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| `spring-boot` | skill | Building a Spring Boot service: web layer, configuration, security, HTTP clients, logging, cloud-native packaging, and a table of Boot 4 pitfalls |
|
|
18
|
+
| `spring-data-jpa` | skill | Spring Data JPA and Hibernate: entities, repositories, transactions, N+1 and fetch problems, migrations |
|
|
19
|
+
| `spring-testing` | skill | Spring Boot tests: slices, `MockMvcTester`, `@MockitoBean`, Testcontainers with `@ServiceConnection` |
|
|
20
|
+
| `spring-boot-engineer` | agent | Implements a Spring Boot feature end to end |
|
|
21
|
+
| `spring-reviewer` | agent | Framework-aware review: layering, transactions, JPA, configuration, test slices |
|
|
22
|
+
| `spring-security` | agent | Security pass on a Spring Security change |
|
|
23
|
+
| `kubernetes-specialist` | agent | Kubernetes manifests, probes, resources and rollout for a Spring Boot service |
|
|
24
|
+
| `docker-expert` | agent | Dockerfile, layered or native image for a Spring Boot service |
|
|
25
|
+
| `devops-engineer` | agent | Build and delivery pipeline, on whatever CI the project already uses |
|
|
26
|
+
|
|
27
|
+
The kit is adapted from [piomin/claude-ai-spring-boot](https://github.com/piomin/claude-ai-spring-boot) by Piotr Mińkowski (Apache-2.0), rewritten and condensed for Boot 4. Each of its skill folders ships `LICENSE-piomin-spring-boot.txt`, and each agent carries a credit line.
|
|
28
|
+
|
|
29
|
+
### Switching kits on and off
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
geneseed build --emit claude-global --kits spring-boot
|
|
33
|
+
geneseed build --emit claude-global --kits none
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`--kits` takes a comma-separated list. `none` and an empty value both mean no kits. An unknown kit name is refused with exit code 2, and the error lists the kits this Geneseed ships. `none` is reserved and can never name a kit. The flag's row is in the [CLI reference](../reference/cli.md), under `build`.
|
|
37
|
+
|
|
38
|
+
A build does exactly what its flags say. A plain `build` without `--kits` therefore turns off a kit the install has on. Before it does, it warns: `this install has kit <k>; this build turns it off — pass --kits <k> to keep it`.
|
|
39
|
+
|
|
40
|
+
The setup wizard asks one question listing the kits, with the install's current kits as the default. It asks it only when at least one kit exists.
|
|
41
|
+
|
|
42
|
+
### What an install records
|
|
43
|
+
|
|
44
|
+
An install with a kit on records it in its instructions file, under the skill section: `Active kits: spring-boot`. The line is written only when at least one kit is on. A missing line means no kits, so an install built before kits existed reads as having none.
|
|
45
|
+
|
|
46
|
+
`geneseed rebuild-all`, `geneseed update` and `geneseed migrate` read the line back and rebuild with the same kits. `geneseed diff` and the web console read it too, so they compare against or rebuild that same set. `geneseed validate` does not read it: pass `--kits` to dry-run a kit build. A plain `build` does not read it; it only warns, as above. If the line names a kit this Geneseed no longer ships, the rebuild drops that kit with a notice and keeps the others. It never fails `rebuild-all`. A retired kit name is never reused for a new kit.
|
|
47
|
+
|
|
48
|
+
`geneseed status` shows `kits: spring-boot` on the install's row, and `status --json` carries the same list.
|
|
49
|
+
|
|
50
|
+
In the web console, **Installs → Kits** (`#/installs/kits`) lists every install (disabled ones read-only) with one switch per kit. Toggling stages the set; **Apply** asks for confirmation, then rebuilds that one install with exactly that set as a console job (replace, not replay). Files you edited in a kit that is now off are kept, and the row names them so you can delete them by hand.
|
|
51
|
+
|
|
52
|
+
### What a kit adds to an install
|
|
53
|
+
|
|
54
|
+
- **Its skills and agents.** They are written like any core skill or agent. The members of a kit that is off are not written at all.
|
|
55
|
+
- **A Kits block in the instructions file**, after the doctrine layer. It holds each active kit's rules, the same text at either [footprint](footprint.md).
|
|
56
|
+
- **Its catalogue rows**, as the last rows of the skills table and of the agents table, only where the host does not list that kind natively. The plain bundle (`--emit files`) gets both, Bob gets the agent rows (it lists skills natively), and Claude Code, OpenClaude and OpenCode get none: their instructions file keeps its pointer.
|
|
57
|
+
|
|
58
|
+
`--exclude-skills` still applies: it can drop one kit skill while the rest of the kit stays on, and the exclusion wins. A kit's links to its own skills do not stop that, but a kit file's link to a core skill still protects that core skill from `--exclude-skills`, as any link from the harness does. Kit agents cannot be left out one by one. See [Exclusions](exclusions.md).
|
|
59
|
+
|
|
60
|
+
### Turning a kit off
|
|
61
|
+
|
|
62
|
+
Rebuild with `--kits none`, or with a list that leaves the kit out. The kit's files are removed from the install, its block and catalogue rows go, and the `Active kits:` line goes when no kit is left.
|
|
63
|
+
|
|
64
|
+
A kit file you edited is kept, as with any file Geneseed wrote and you changed. `geneseed status` names it on a row of its own, `kept from inactive kit <kit>: <files>`, and `status --json` lists it under `keptKitFiles`. Delete it by hand if you no longer want it.
|
|
65
|
+
|
|
66
|
+
### Kits are not doctrine packs
|
|
67
|
+
|
|
68
|
+
A [doctrine pack](rules.md) is part of the constitution. Kits are not:
|
|
69
|
+
|
|
70
|
+
- A kit's rules are plain text. They have no rule ids, cannot be excluded one at a time with `--exclude-rules`, and have no row in the hook ledger. No hook gate enforces them.
|
|
71
|
+
- A kit is not a pack. A missing `Active packs:` line reads as every pack, but a missing `Active kits:` line reads as no kits, so an old install never turns a kit on by accident.
|
|
72
|
+
|
|
73
|
+
### What a kit costs
|
|
74
|
+
|
|
75
|
+
- **Hooks:** nothing. The hook binary does not read kits, so a kit adds no time to a tool call.
|
|
76
|
+
- **The instructions file:** the Kits block. Each kit has its own word budget for its rules, set in its manifest and checked by `doctor`, so a kit never eats into the core ceiling.
|
|
77
|
+
- **The skill listing:** one line per kit skill, every session, while the kit is on. That is the same price as a core skill.
|
|
78
|
+
|
|
79
|
+
The cost of authoring one, file by file, is in [Extending](../extending.md#3bis--adding-a-kit).
|
package/docs/concepts/loops.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
group: concepts
|
|
3
3
|
kind: "concept"
|
|
4
4
|
section: "Loops"
|
|
5
|
-
order:
|
|
5
|
+
order: 17
|
|
6
6
|
title: "Loops"
|
|
7
7
|
link: {"hash":"#/docs/loop-actions","label":"The geneseed loop actions →"}
|
|
8
8
|
description: "What a loop is, the ring of bricks it runs, and how it differs from foreman mode."
|
package/docs/concepts/lsp.md
CHANGED
package/docs/extending.md
CHANGED
|
@@ -389,10 +389,31 @@ emit leaves the prior install intact.
|
|
|
389
389
|
never fires. Hand-run, spends model calls on your own login, not part of the suite.
|
|
390
390
|
|
|
391
391
|
**A vendored skill folder** (`src/skills/<name>/SKILL.md`) is a different shape: add it to
|
|
392
|
-
`VENDORED_SKILL_DIRS` (`js/hosts/native.mjs:
|
|
393
|
-
`tests/unit/authoring_gates.test.mjs:
|
|
394
|
-
|
|
395
|
-
|
|
392
|
+
`VENDORED_SKILL_DIRS` (`js/hosts/native.mjs:48` — the literal is frozen in
|
|
393
|
+
`tests/unit/authoring_gates.test.mjs:421`), add a `src/upstreams.json` entry (`repo`, `commit` —
|
|
394
|
+
a 40-hex commit, never a moving branch — `author`, `license`, and `licenseFile`, a path under
|
|
395
|
+
`src/` or `null`; an optional `"frozen": true` marks text left at its pin on purpose: the weekly drift job skips it, `creditProblems` still validates it), write a `VENDOR.md` carrying `**Upstream key:** <that key>` and a
|
|
396
|
+
`{{CREDIT:<key>:<upstream path>}}` line, which renders the attribution into every install (the
|
|
397
|
+
folder ships its upstream's own LICENSE, so it gets no `LICENSE-<key>.txt` copy; a first-party
|
|
398
|
+
folder writes `**Upstream:** this repository` and a `**License:**` instead, and has no entry),
|
|
399
|
+
add the registry row — and **skip steps 2, 4, 6, 7**: the flat-spec globs only see `*.md` at the
|
|
400
|
+
top level. The doctor (`vendorPinProblems`, `creditProblems`) refuses a key with no entry, an
|
|
401
|
+
entry nothing credits, an entry missing `repo`/`author`/`license`, a bad commit, and a licence
|
|
402
|
+
file that is missing or outside `src/`.
|
|
403
|
+
|
|
404
|
+
**Porting a flat skill or an agent from another repository** is steps 1–9 plus a
|
|
405
|
+
`{{CREDIT:}}` token. Only an upstream not yet in `src/upstreams.json` needs a new entry, as
|
|
406
|
+
above, with the upstream's licence text at `src/licenses/<key>.txt` as its `licenseFile` (never
|
|
407
|
+
`null` here; two keys from one repo may share a file); a port from a listed upstream reuses its
|
|
408
|
+
key. Put
|
|
409
|
+
`{{CREDIT:<key>:<upstream path>}}` on its own line under the purpose blockquote; the render turns
|
|
410
|
+
it into `` *Adapted from [repo](url) (`path`) by author — license, modified.* ``, and for a skill the
|
|
411
|
+
emit adds `skills/<name>/LICENSE-<key>.txt` (an agent is one file, so it gets the line only). A
|
|
412
|
+
skill drawn from two upstreams carries two tokens, as `debug` does. A new entry also needs a
|
|
413
|
+
`` `<key>` `` bullet under README `## 🙏 Credits` — `proseMirrorProblems` gates that list both
|
|
414
|
+
ways. Do it in
|
|
415
|
+
the commit that brings the text in: no gate can find an unmarked port afterwards, because nothing
|
|
416
|
+
distinguishes it from first-party text.
|
|
396
417
|
|
|
397
418
|
Two traps worth knowing before you write the spec:
|
|
398
419
|
|
|
@@ -435,6 +456,89 @@ because a code span is not a link. Measured on 2026-10-08, with the neutral them
|
|
|
435
456
|
footprint: debug's SKILL.md went from 1.64k to 1.18k tokens and rule's from 2.33k to 1.83k
|
|
436
457
|
(chars/4).
|
|
437
458
|
|
|
459
|
+
---
|
|
460
|
+
|
|
461
|
+
## 3bis — Adding a KIT
|
|
462
|
+
|
|
463
|
+
A kit is an opt-in bundle of skills, agents and rules for one stack, off unless an install names
|
|
464
|
+
it with `--kits` ([Kits](concepts/kits.md)). Nothing in the mechanism is specific to one stack.
|
|
465
|
+
|
|
466
|
+
**The manifest is the sole writer of membership.** `src/kits/<kit>.json`:
|
|
467
|
+
|
|
468
|
+
```json
|
|
469
|
+
{
|
|
470
|
+
"description": "One line: what the kit is for.",
|
|
471
|
+
"skills": ["<skill>"],
|
|
472
|
+
"agents": ["<agent>"],
|
|
473
|
+
"rules": "kits/<kit>/rules.md",
|
|
474
|
+
"rulesWordBudget": 300,
|
|
475
|
+
"catalogue": {
|
|
476
|
+
"skills": ["| [<skill>]({{DIR_SKILLS}}/<skill>.md) | when to reach for it |"],
|
|
477
|
+
"agents": ["| [<agent>]({{DIR_AGENTS}}/<agent>.md) | when to delegate to it |"]
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
- `banned` (optional): strings no member file may contain, in the skill and agent specs, a skill's side files (flat, `skills/<m>/*.md`) and the kit's `rules.md`; `doctor` refuses each hit, naming file and line. A line carrying `<!-- banned: ok -->` is exempt (a rule that names the construct in order to forbid it).
|
|
483
|
+
|
|
484
|
+
`loadKits` (`js/build/source.mjs`) reads every `src/kits/*.json`; `src/kits/` itself never ships
|
|
485
|
+
(`NOT_BUNDLED` in `js/build/render.mjs`). The members stay **flat** in `src/skills/` and
|
|
486
|
+
`src/agents/`, never in a `src/kits/<kit>/skills/` folder: every host's native layer accepts only
|
|
487
|
+
`skills/<x>.md` and `agents/<x>.md` and would drop a nested file silently, and every check that
|
|
488
|
+
enumerates skills would miss it. `src/kits/<kit>/` holds only `rules.md`.
|
|
489
|
+
|
|
490
|
+
**Per-kit cost:**
|
|
491
|
+
|
|
492
|
+
1. **Each member is a skill or an agent first.** §3 applies to it, with these differences:
|
|
493
|
+
- **No row in `AGENT.md.tmpl`.** Its catalogue row, a table row of the same shape, goes in
|
|
494
|
+
the manifest's `catalogue`. With the kit on, the render inserts the skill rows as the last
|
|
495
|
+
rows of the §4 skills table (before the folder-skills list) and the agent rows at the end of
|
|
496
|
+
the §3 agents table, on a host that does not catalogue that kind natively.
|
|
497
|
+
`countTableProblems` checks the template's links and the kits' catalogue rows together
|
|
498
|
+
against the flat specs, and `kitProblems` refuses a catalogue row for a non-member.
|
|
499
|
+
- **No README badge and no `SHIPPED.md` count.** Core counts exclude kit members, so
|
|
500
|
+
skip §3 steps 6 and 7.
|
|
501
|
+
- **Still** a `DESC_<NAME>` in every theme, a `SKILL_CLASS` entry for a skill (the members are
|
|
502
|
+
flat, so `srcStems('skills')` sees them), and a `registry.json` row.
|
|
503
|
+
- **An agent's web glyph, required:** an entry in `GLYPHS`
|
|
504
|
+
(`web/src/components/AgentGlyph.jsx`), then rebuild and commit `web/dist`.
|
|
505
|
+
`web/src/__tests__/agentGlyph.test.jsx` gates every `src/agents/*.md`, kit members included.
|
|
506
|
+
2. **The manifest**, above.
|
|
507
|
+
3. **`src/kits/<kit>/rules.md`**, the kit's rules. The render puts it in the carrier's Kits block,
|
|
508
|
+
after §2, only while the kit is on. It is plain text, not doctrine machinery: no rule ids, no
|
|
509
|
+
per-rule exclusion, no hook-ledger row, and the same text at either footprint.
|
|
510
|
+
`doctor` holds it to the manifest's `rulesWordBudget` (300 when unset).
|
|
511
|
+
4. **Membership and links.** `doctor` (`kitProblems`) refuses a member listed in two kits, a
|
|
512
|
+
core skill listed in a kit, a member with no file or no registry row, a core file that links
|
|
513
|
+
to a kit member (it would dangle in an install without the kit), and a kit file that links to
|
|
514
|
+
another kit's member. Links run kit → core only. A kit's links to its own members do not
|
|
515
|
+
protect them from `--exclude-skills`; its link to a core skill still protects that skill.
|
|
516
|
+
5. **The setup wizard.** It asks its kits question only when at least one kit exists. The shipped
|
|
517
|
+
`spring-boot` kit therefore added one answer line to the stdin strings in
|
|
518
|
+
`tests/unit/wizard.test.mjs`, and the `wizard-no-kit` job holds that the question is skipped on a
|
|
519
|
+
tree with no kit.
|
|
520
|
+
6. **Golden cells**: the matrix (`tests/helpers/matrix/emit.*.json`) carries kit on for `claude` and
|
|
521
|
+
`opencode`, the kit-off baseline (`kits: []`, spelled `--kits none`), and three deletion cells
|
|
522
|
+
(kit on to off). One deletion cell sets `edit` (a map of sandbox path to appended text) so a member
|
|
523
|
+
is the user's own before the kit goes off, and `sovereign` excuses it: the prune keeps it (R6).
|
|
524
|
+
|
|
525
|
+
**What a kit costs an install** — what to weigh before adding one:
|
|
526
|
+
|
|
527
|
+
- **Hooks: zero.** The hook binary never reads a kit, so a kit adds no time to any tool call.
|
|
528
|
+
- **The carrier:** the Kits block. Each kit is measured against its own
|
|
529
|
+
`rulesWordBudget`, not the core carrier ceiling, which stays measured on core alone.
|
|
530
|
+
- **The skill listing:** one description per kit skill, every session, while the kit is on.
|
|
531
|
+
- **An install without the kit: nothing.** Its members are not written, and a default build
|
|
532
|
+
stays byte-identical, with no `Active kits:` line.
|
|
533
|
+
|
|
534
|
+
**Retiring a kit** puts its id in `RETIRED_KIT_IDS` (`js/build/source.mjs`), with the reason
|
|
535
|
+
beside it. An install whose `Active kits:` line names it drops it wherever that line is read back
|
|
536
|
+
(`rebuild-all`, `update`, `migrate`, `diff`, the web console), without failing; `rebuild-all`, `update` and `migrate` print a notice. `doctor` refuses a retired id on a new kit. `none` is
|
|
537
|
+
already there: `--kits none` means no kits.
|
|
538
|
+
|
|
539
|
+
**Kits are not packs.** Never add a kit to `PACK_ORDER`. A missing `Active packs:` line reads as
|
|
540
|
+
every pack, so a kit there would switch itself on in every install built before it existed.
|
|
541
|
+
|
|
438
542
|
|
|
439
543
|
---
|
|
440
544
|
|
|
@@ -12,7 +12,7 @@ description: "The build command to run in a script, a CI job, or a shell with no
|
|
|
12
12
|
geneseed build --emit opencode-global --theme neutral
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
`geneseed build` takes the same flags as the generator `geneseed-build`. `--emit` picks the host and its scope: `files`, `opencode`, `opencode-global`, `claude`, `claude-global`, `bob`, `bob-global`, `openclaude` or `openclaude-global`. A `-global` emit writes into the tool's own config directory. A project emit writes into the repo you name with `--out <repo> --root <repo>`. All the other flags (`--theme`, `--footprint`, `--posture`, `--mode`, `--trust`, `--doctrines`, `--exclude-rules`) are covered in [Choose your setup](choose-your-setup.md) and the two pages after it. `--exclude-skills` leaves skills you never use out of the install; see [Exclusions](../concepts/exclusions.md).
|
|
15
|
+
`geneseed build` takes the same flags as the generator `geneseed-build`. `--emit` picks the host and its scope: `files`, `opencode`, `opencode-global`, `claude`, `claude-global`, `bob`, `bob-global`, `openclaude` or `openclaude-global`. A `-global` emit writes into the tool's own config directory. A project emit writes into the repo you name with `--out <repo> --root <repo>`. All the other flags (`--theme`, `--footprint`, `--posture`, `--mode`, `--trust`, `--doctrines`, `--exclude-rules`) are covered in [Choose your setup](choose-your-setup.md) and the two pages after it. `--exclude-skills` leaves skills you never use out of the install; see [Exclusions](../concepts/exclusions.md). `--kits <kit>` switches on an opt-in kit, and `--kits none` switches every kit off; see [Kits](../concepts/kits.md).
|
|
16
16
|
|
|
17
17
|
To test a build before writing anything, use `geneseed validate` with the same flags. See [Verify](verify.md).
|
|
18
18
|
|
|
@@ -28,6 +28,7 @@ Every word Geneseed uses that you would not find in a general programming glossa
|
|
|
28
28
|
| Posture | — | the relationship register the agent works in (peer, mentor, expert, assistant, artisan) | pairing style |
|
|
29
29
|
| Mode | — | how work gets executed — direct (the agent works every task itself) or foreman (substantial tasks spawn an isolated pipeline) | solo vs. delegating to a sub-team |
|
|
30
30
|
| Footprint | — | how much of the Rules loads inline each turn (full vs lean) | eager vs. lazy loading |
|
|
31
|
+
| Kit | — | an opt-in bundle of skills, agents and rules for one stack, switched on per install with `--kits`; not a doctrine pack | an optional dependency group (`pkg[extra]`) |
|
|
31
32
|
| Loop | — | a requirement worked as small, validated, committed iterations on their own branch, driven by `geneseed loop` | a state machine over a ticket's steps |
|
|
32
33
|
| Iteration | — | one cycle of a loop back to its head; closes into its own commit | one sprint-of-one through a checklist |
|
|
33
34
|
| Brick | — | one node of a loop graph: a prompt for one agent or skill, reporting one of a declared set of outcomes | a step in a runbook |
|
package/docs/web-ui.md
CHANGED
|
@@ -194,6 +194,10 @@ streams the output of background jobs and keeps their history across reloads.
|
|
|
194
194
|
card (shown once a global install exists) manages sovereign repos — folders where every
|
|
195
195
|
global harness goes dormant (hooks stay silent, the global preamble never loads);
|
|
196
196
|
add or remove one here, the web mirror of `harness exclude add|remove|list`.
|
|
197
|
+
- **Kits** (`#/installs/kits`) — every install (disabled ones read-only) as a card with one switch per kit.
|
|
198
|
+
Toggling only stages the set; **Apply** asks first, then rebuilds that install with
|
|
199
|
+
exactly that set as a console job. Files you edited in a kit that is now off are kept,
|
|
200
|
+
and the row lists them.
|
|
197
201
|
- **Settings** (`#/settings`) — a **Console direction** picker (the visual flavour of the
|
|
198
202
|
console: Cultivar, Operator, Matrix or Atlas) with a **Dashboard layout** control (Auto follows each theme's designed Status
|
|
199
203
|
lens; Cultivar / Greenhouse / Operator / Journal force one regardless of skin), the
|
package/harness.config.json
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
"_comment": "version is a human-readable release label only; the canonical identity of an installed harness is the source fingerprint stamped in .geneseed-version (see `harness version`), not this string.",
|
|
3
3
|
"theme": "neutral",
|
|
4
4
|
"product": "Geneseed",
|
|
5
|
-
"version": "4.
|
|
5
|
+
"version": "4.10.0"
|
|
6
6
|
}
|
package/js/build/args.mjs
CHANGED
|
@@ -11,7 +11,8 @@ import { parseArgs as nodeParseArgs } from 'node:util';
|
|
|
11
11
|
import { withPlatformNewlines } from '../lib/fs.mjs';
|
|
12
12
|
import path from 'node:path';
|
|
13
13
|
import {
|
|
14
|
-
CONFIG, discoverNames, GATE_SWITCHES, PACK_ORDER, knownRuleIds, knownSkillIds, linkedSkillIds,
|
|
14
|
+
CONFIG, discoverNames, GATE_SWITCHES, PACK_ORDER, SRC, knownRuleIds, knownSkillIds, linkedSkillIds, loadKits,
|
|
15
|
+
resolveSkillNames, skillAliases,
|
|
15
16
|
} from './source.mjs';
|
|
16
17
|
// The loop engine's trust presets — the `--trust` choices. `score.mjs` imports nothing, which is
|
|
17
18
|
// what lets it sit inside this module's `child_process` ban.
|
|
@@ -32,7 +33,7 @@ const EMITS = ['files', 'opencode', 'opencode-global', 'claude', 'claude-global'
|
|
|
32
33
|
export function configDefaults() {
|
|
33
34
|
const d = {
|
|
34
35
|
theme: 'neutral', posture: 'peer', mode: 'direct', trust: DEFAULT_PRESET,
|
|
35
|
-
doctrines: [...PACK_ORDER], excludeRules: [], excludeSkills: [],
|
|
36
|
+
doctrines: [...PACK_ORDER], excludeRules: [], excludeSkills: [], kits: [],
|
|
36
37
|
};
|
|
37
38
|
if (!existsSync(CONFIG)) return d;
|
|
38
39
|
try {
|
|
@@ -96,6 +97,7 @@ export function configDefaults() {
|
|
|
96
97
|
const VALUED = {
|
|
97
98
|
'--theme': 'theme', '--posture': 'posture', '--mode': 'mode', '--trust': 'trust',
|
|
98
99
|
'--doctrines': 'doctrines', '--exclude-rules': 'excludeRules', '--exclude-skills': 'excludeSkills',
|
|
100
|
+
'--kits': 'kits',
|
|
99
101
|
'--out': 'out', '--target': 'out', '--emit': 'emit',
|
|
100
102
|
'--footprint': 'footprint', '--root': 'root', '--config-dir': 'cfgDir',
|
|
101
103
|
};
|
|
@@ -196,13 +198,13 @@ function parseExcludeRules(value) {
|
|
|
196
198
|
* through `resolveSkillNames` and leave one stderr line each; the linked refusal stays for a
|
|
197
199
|
* name typed as itself.
|
|
198
200
|
*/
|
|
199
|
-
function parseExcludeSkills(value) {
|
|
201
|
+
export function parseExcludeSkills(value, src = SRC) {
|
|
200
202
|
const s = value.trim();
|
|
201
203
|
if (s === 'none' || s === '') return [];
|
|
202
|
-
const known = knownSkillIds();
|
|
203
|
-
const linked = linkedSkillIds();
|
|
204
|
+
const known = knownSkillIds(src);
|
|
205
|
+
const linked = linkedSkillIds(src);
|
|
204
206
|
const names = s.split(',').map((x) => x.trim()).filter(Boolean);
|
|
205
|
-
const { ids, notices, unknown } = resolveSkillNames(names, { known, linked });
|
|
207
|
+
const { ids, notices, unknown } = resolveSkillNames(names, { known, linked, aliases: skillAliases(src) });
|
|
206
208
|
if (unknown.length) {
|
|
207
209
|
die(2, `argument --exclude-skills: unknown skill '${unknown[0]}' (choose from `
|
|
208
210
|
+ `${known.filter((k) => !linked.includes(k)).join(', ')}, or 'none')`);
|
|
@@ -216,6 +218,23 @@ function parseExcludeSkills(value) {
|
|
|
216
218
|
return ids;
|
|
217
219
|
}
|
|
218
220
|
|
|
221
|
+
/**
|
|
222
|
+
* `--kits a,b` / `--kits none` -> the cfg's kit list, sorted and deduped for the same marker
|
|
223
|
+
* stability as `--exclude-skills`. `none` and '' mean no kits; an unknown kit is refused, naming
|
|
224
|
+
* the known ones (spec R1). No `harness.config.json` key feeds it, for `excludeSkills`' reason.
|
|
225
|
+
*/
|
|
226
|
+
export function parseKits(value, known = [...loadKits().keys()]) {
|
|
227
|
+
const s = value.trim();
|
|
228
|
+
if (s === 'none' || s === '') return [];
|
|
229
|
+
const names = [...new Set(s.split(',').map((x) => x.trim()).filter(Boolean))].sort();
|
|
230
|
+
const unknown = names.find((n) => !known.includes(n));
|
|
231
|
+
if (unknown) {
|
|
232
|
+
const choices = known.length ? `${known.join(', ')}, or 'none'` : "'none'";
|
|
233
|
+
die(2, `argument --kits: unknown kit '${unknown}' (choose from ${choices})`);
|
|
234
|
+
}
|
|
235
|
+
return names;
|
|
236
|
+
}
|
|
237
|
+
|
|
219
238
|
/**
|
|
220
239
|
* `--doctrines craft,rigor` / `--doctrines none` -> the cfg's pack array.
|
|
221
240
|
*
|
|
@@ -313,6 +332,7 @@ export function parseArgs(argv, defaults) {
|
|
|
313
332
|
doctrines: defaults.doctrines,
|
|
314
333
|
excludeRules: defaults.excludeRules,
|
|
315
334
|
excludeSkills: defaults.excludeSkills ?? [],
|
|
335
|
+
kits: defaults.kits ?? [],
|
|
316
336
|
out: 'Harness', emit: 'files', footprint: 'lean', root: null,
|
|
317
337
|
syncThemes: false, validateOnly: false, verbose: false,
|
|
318
338
|
...Object.fromEntries(GATE_SWITCHES.map((g) => [g.arg, false])),
|
|
@@ -356,6 +376,7 @@ export function parseArgs(argv, defaults) {
|
|
|
356
376
|
if (typeof args.doctrines === 'string') args.doctrines = parseDoctrines(args.doctrines);
|
|
357
377
|
if (typeof args.excludeRules === 'string') args.excludeRules = parseExcludeRules(args.excludeRules);
|
|
358
378
|
if (typeof args.excludeSkills === 'string') args.excludeSkills = parseExcludeSkills(args.excludeSkills);
|
|
379
|
+
if (typeof args.kits === 'string') args.kits = parseKits(args.kits);
|
|
359
380
|
return args;
|
|
360
381
|
}
|
|
361
382
|
|
package/js/build/driver.mjs
CHANGED
|
@@ -65,6 +65,7 @@ import {
|
|
|
65
65
|
// registry to find every install it must re-emit, and a CLI verb may not reach into a driver
|
|
66
66
|
// for a reader. See js/inspect/registry.mjs.
|
|
67
67
|
import { registryRecord, registryRoots } from '../inspect/registry.mjs';
|
|
68
|
+
import { kitsOfDir } from '../hosts/installs.mjs';
|
|
68
69
|
|
|
69
70
|
// P2. `--sync-themes` crossed into its own module rather than into this file: it is 90 lines
|
|
70
71
|
// of textual surgery over committed files with a corpus of its own, and it is the half of the
|
|
@@ -651,12 +652,12 @@ const PROJECT_EMITS = {
|
|
|
651
652
|
* carry straight past the parameter default.
|
|
652
653
|
*/
|
|
653
654
|
function axesCfg({
|
|
654
|
-
posture, mode, trust, doctrines, excludeRules, excludeSkills, ...gates
|
|
655
|
+
posture, mode, trust, doctrines, excludeRules, excludeSkills, kits, ...gates
|
|
655
656
|
}) {
|
|
656
657
|
return makeCfg({
|
|
657
658
|
...(posture ? { posture } : {}), ...(mode ? { mode } : {}), ...(trust ? { trust } : {}),
|
|
658
659
|
...(doctrines ? { doctrines } : {}), ...(excludeRules ? { excludeRules } : {}),
|
|
659
|
-
...(excludeSkills ? { excludeSkills } : {}),
|
|
660
|
+
...(excludeSkills ? { excludeSkills } : {}), ...(kits ? { kits } : {}),
|
|
660
661
|
// The gate switches (`GATE_SWITCHES` keys): only an explicit `false` disarms; `null` (no
|
|
661
662
|
// opinion) keeps the gate.
|
|
662
663
|
...Object.fromEntries(GATE_SWITCHES.filter((g) => gates[g.key] === false).map((g) => [g.arg, true])),
|
|
@@ -665,7 +666,7 @@ function axesCfg({
|
|
|
665
666
|
|
|
666
667
|
export function emitProjectInto(host, {
|
|
667
668
|
theme, out, root, footprint = 'full', posture = null, mode = null, trust = null,
|
|
668
|
-
doctrines = null, excludeRules = null, excludeSkills = null, ...gates
|
|
669
|
+
doctrines = null, excludeRules = null, excludeSkills = null, kits = null, ...gates
|
|
669
670
|
}) {
|
|
670
671
|
const emit = PROJECT_EMITS[host];
|
|
671
672
|
// The axes ride the same rail into every `*Into`, and for the same reason: a drift reader
|
|
@@ -674,7 +675,7 @@ export function emitProjectInto(host, {
|
|
|
674
675
|
// checked an all-packs render nobody asked for.
|
|
675
676
|
return withPlatformNewlines(() => emit(
|
|
676
677
|
axesCfg({
|
|
677
|
-
posture, mode, trust, doctrines, excludeRules, excludeSkills, ...gates }),
|
|
678
|
+
posture, mode, trust, doctrines, excludeRules, excludeSkills, kits, ...gates }),
|
|
678
679
|
{ theme, footprint, root }, out,
|
|
679
680
|
));
|
|
680
681
|
}
|
|
@@ -689,16 +690,16 @@ export function emitProjectInto(host, {
|
|
|
689
690
|
*/
|
|
690
691
|
export function buildInto({
|
|
691
692
|
theme, out, footprint = 'lean', posture = null, mode = null, trust = null, doctrines = null,
|
|
692
|
-
excludeRules = null, excludeSkills = null, ...gates
|
|
693
|
+
excludeRules = null, excludeSkills = null, kits = null, ...gates
|
|
693
694
|
}) {
|
|
694
695
|
return withPlatformNewlines(() => build(axesCfg({
|
|
695
|
-
posture, mode, trust, doctrines, excludeRules, excludeSkills, ...gates }),
|
|
696
|
+
posture, mode, trust, doctrines, excludeRules, excludeSkills, kits, ...gates }),
|
|
696
697
|
theme, out, { footprint, nativeCatalog: false }));
|
|
697
698
|
}
|
|
698
699
|
|
|
699
700
|
export function emitGlobalInto(host, {
|
|
700
701
|
theme, out, cfgDir, footprint, posture = null, mode = null, doctrines = null, trust = null,
|
|
701
|
-
excludeRules = null, excludeSkills = null, ...gates
|
|
702
|
+
excludeRules = null, excludeSkills = null, kits = null, ...gates
|
|
702
703
|
}) {
|
|
703
704
|
// `build.HOSTS.get(host, build.HOSTS["opencode"])` — an unknown host falls back rather than
|
|
704
705
|
// raising, because the host comes from a marker file a user can edit.
|
|
@@ -715,7 +716,7 @@ export function emitGlobalInto(host, {
|
|
|
715
716
|
// the same scar the footprint left in `diffCollect`, one axis over each time.
|
|
716
717
|
return withPlatformNewlines(() => emit(
|
|
717
718
|
axesCfg({
|
|
718
|
-
posture, mode, trust, doctrines, excludeRules, excludeSkills, ...gates }),
|
|
719
|
+
posture, mode, trust, doctrines, excludeRules, excludeSkills, kits, ...gates }),
|
|
719
720
|
{ theme, footprint, root: null, cfgDir }, out,
|
|
720
721
|
));
|
|
721
722
|
}
|
|
@@ -749,6 +750,25 @@ export function main(argv) {
|
|
|
749
750
|
});
|
|
750
751
|
}
|
|
751
752
|
|
|
753
|
+
/**
|
|
754
|
+
* Spec B3: a build does what its flags say, so one without `--kits` turns off a kit the target
|
|
755
|
+
* install has on. Said BEFORE the emit rewrites the marker. The target is the emit's own: the
|
|
756
|
+
* config dir for a `-global` emit, `out` otherwise. `kitsOfDir` has already dropped a kit this
|
|
757
|
+
* checkout no longer ships, so the advice never names one `--kits` would refuse. Best-effort.
|
|
758
|
+
*/
|
|
759
|
+
function warnDroppedKits(args, out) {
|
|
760
|
+
try {
|
|
761
|
+
const global = args.emit.endsWith('-global');
|
|
762
|
+
const host = global ? args.emit.slice(0, -'-global'.length) : (args.emit === 'files' ? null : args.emit);
|
|
763
|
+
const target = global ? (args.cfgDir ?? HOSTS.find((h) => h.host === host).configDir()) : out;
|
|
764
|
+
for (const k of kitsOfDir(target, host).filter((x) => !args.kits.includes(x))) {
|
|
765
|
+
process.stderr.write(`[geneseed] WARN: this install has kit ${k}; this build turns it off — `
|
|
766
|
+
+ `pass --kits ${k} to keep it
|
|
767
|
+
`);
|
|
768
|
+
}
|
|
769
|
+
} catch { /* a warning never fails a build */ }
|
|
770
|
+
}
|
|
771
|
+
|
|
752
772
|
function run(argv) {
|
|
753
773
|
const args = parseArgs(argv, configDefaults());
|
|
754
774
|
|
|
@@ -783,6 +803,7 @@ function run(argv) {
|
|
|
783
803
|
args.cfgDir = resolveOut(args.cfgDir);
|
|
784
804
|
}
|
|
785
805
|
const cfg = makeCfg(args);
|
|
806
|
+
warnDroppedKits(args, out);
|
|
786
807
|
|
|
787
808
|
// The marker directory is the emit's TARGET, which for a global emit is the config dir it
|
|
788
809
|
// just rendered into and not `--out` at all — so the emit returns it rather than the
|