@jaybeeuu/agent-cortex 1.39.0 → 1.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/.agents/skills/style-versioning/SKILL.md +1 -1
  2. package/.github/workflows/ci.yml +47 -14
  3. package/AGENTS.md +19 -5
  4. package/CHANGELOG.md +12 -0
  5. package/README.md +111 -74
  6. package/agents/README.md +12 -11
  7. package/bin/agent-cortex.mjs +23 -10
  8. package/bin/installers/claude.mjs +518 -168
  9. package/bin/installers/copilot.mjs +22 -12
  10. package/bin/installers/pi.mjs +37 -34
  11. package/docs/claude-hooks.md +3 -3
  12. package/extensions/agent-modes/README.md +3 -2
  13. package/extensions/agent-modes/discover.test.ts +30 -7
  14. package/extensions/agent-modes/discover.ts +1 -1
  15. package/extensions/skill-stats/index.test.ts +3 -2
  16. package/lib/cli.mjs +22 -10
  17. package/package.json +4 -4
  18. package/plugin.json +1 -1
  19. package/scripts/build-copilot-agents.mjs +1 -1
  20. package/scripts/check-no-sync.mjs +49 -0
  21. package/scripts/lib/compose-agent.mjs +32 -14
  22. package/skills/engineering/grill-with-docs/SKILL.md +1 -1
  23. package/skills/workflow/hitl-collab/SKILL.md +1 -1
  24. package/test/claude-installer.test.mjs +199 -139
  25. package/test/cli.test.mjs +40 -30
  26. package/test/copilot-installer.test.mjs +92 -82
  27. package/test/helpers/fake-claude.mjs +152 -0
  28. package/test/installer.test.mjs +309 -57
  29. package/test/pi-installer.test.mjs +115 -99
  30. package/token-map.README.md +21 -12
  31. package/token-map.json +7 -6
  32. package/.claude-plugin/marketplace.json +0 -16
  33. package/claude/.claude-plugin/plugin.json +0 -11
  34. package/claude/agents/plan.md +0 -58
  35. package/claude/agents/ralph-plan.md +0 -121
  36. package/claude/agents/ralph.md +0 -193
  37. package/claude/agents/strategy.md +0 -111
  38. package/claude/hooks/scripts/notify.mjs +0 -54
  39. package/claude/hooks.json +0 -38
  40. /package/{claude → claude-extras}/.mcp.json +0 -0
  41. /package/{claude → claude-extras}/scripts/statusline-command.sh +0 -0
@@ -64,7 +64,7 @@ Fix status bead showing wrong priority after update
64
64
  The `release` job in `.github/workflows/ci.yml` handles everything automatically:
65
65
 
66
66
  1. **Detects new changesets** on main
67
- 2. **Creates a "Version Packages" PR** — the `version` step runs `pnpm version-packages`, which bumps `package.json`, syncs `plugin.json` (via `scripts/sync-plugin-version.sh`), updates `CHANGELOG.md`, and regenerates the committed generated output (`pnpm build:claude`, `pnpm build:copilot`) so the drift gates never fail on a version bump
67
+ 2. **Creates a "Version Packages" PR** — the `version` step runs `pnpm version-packages`, which bumps `package.json`, syncs `plugin.json` (via `scripts/sync-plugin-version.sh`), updates `CHANGELOG.md`, and regenerates the committed Copilot output (`pnpm build:copilot`; the Claude plugin is materialised at install time with the package version, so it has no committed output to regenerate) so the drift gates never fail on a version bump
68
68
  3. **When "Version Packages" is merged**:
69
69
  - Creates a git tag
70
70
  - Publishes to npm — `pnpm publish-package` packs with `pnpm pack` and publishes with `npm publish --provenance --access public`. Authenticates via npm Trusted Publishing (OIDC): the release job's `id-token` permission lets npm exchange a GitHub OIDC token for a short-lived npm token at publish time, so no `NPM_TOKEN` secret or `.npmrc` auth config is required — only the one-time npm-side Trusted Publishing setup on npmjs.com (package → Access → Trusted Publishing → GitHub repo `jaybeeuu/agent-cortex` + workflow `.github/workflows/ci.yml`)
@@ -146,7 +146,7 @@ jobs:
146
146
  run: pnpm test
147
147
 
148
148
  claude-plugin-check:
149
- name: Claude plugin check
149
+ name: Claude plugin materialiser check
150
150
  needs: setup
151
151
  runs-on: ubuntu-latest
152
152
  steps:
@@ -175,16 +175,48 @@ jobs:
175
175
  - name: Install dependencies
176
176
  run: pnpm install --frozen-lockfile
177
177
 
178
- # `pnpm build:claude` is an alias of `agent-cortex install claude` (both run
179
- # bin/installers/claude.mjs), so this drift check proves the committed subtree
180
- # equals the installer's output — stale output or hand-edits to generated
181
- # files fail the build. The subtree stays committed because the marketplace
182
- # install flow reads it straight from the working tree (fresh clone installs
183
- # as-is).
184
- - name: Check generated Claude plugin is up to date
178
+ # The claude/ subtree is retired — the installer is the only generator
179
+ # (plain `agent-cortex install claude` materialises ~/.agent-cortex/claude;
180
+ # `--output <dir>` is generate-only for tests). There is no committed
181
+ # output to diff, so this job validates the materialiser itself: it must
182
+ # produce a complete, structural plugin (generated + hand-authored pieces),
183
+ # no symlinks, no literal {{TOOL:/{{PATH:/{{SECTION: tokens, and a
184
+ # plugin.json version tracking the package — exactly what a real install
185
+ # would ship.
186
+ - name: Materialise the Claude plugin and run structural checks
185
187
  run: |
186
- pnpm build:claude
187
- git diff --exit-code claude/agents/ claude/skills/ claude/.claude-plugin/plugin.json claude/hooks.json claude/hooks/
188
+ set -euo pipefail
189
+ OUT="$RUNNER_TEMP/claude-plugin-check"
190
+ node bin/agent-cortex.mjs install claude --output "$OUT"
191
+
192
+ # structure: generated + hand-authored pieces all present
193
+ test -f "$OUT/.claude-plugin/plugin.json"
194
+ test -f "$OUT/agents/plan.md"
195
+ test -f "$OUT/agents/ralph.md"
196
+ test -f "$OUT/skills/tdd/SKILL.md"
197
+ test -f "$OUT/hooks.json"
198
+ test -f "$OUT/hooks/scripts/notify.mjs"
199
+ test -f "$OUT/.mcp.json"
200
+ test -f "$OUT/scripts/statusline-command.sh"
201
+
202
+ # flat-copy guarantee: no symlinks in the materialised tree
203
+ if [ -n "$(find "$OUT" -type l)" ]; then
204
+ echo "::error::symlinks found in materialised Claude plugin"
205
+ exit 1
206
+ fi
207
+
208
+ # token substitution: no literal tokens survive materialisation
209
+ TOKENS=$(grep -rlE '\{\{(TOOL|PATH|SECTION):' "$OUT" || true)
210
+ if [ -n "$TOKENS" ]; then
211
+ echo "::error::literal substitution tokens in materialised Claude plugin:"
212
+ echo "$TOKENS"
213
+ exit 1
214
+ fi
215
+
216
+ # plugin version tracks the package (never stale)
217
+ PKG_VERSION=$(node -p "require('./package.json').version")
218
+ PLUGIN_VERSION=$(node -p "require('$OUT/.claude-plugin/plugin.json').version")
219
+ test "$PKG_VERSION" = "$PLUGIN_VERSION"
188
220
 
189
221
  - name: Check generated Copilot agent files are up to date
190
222
  run: |
@@ -239,10 +271,11 @@ jobs:
239
271
  # the string is split by argStringToArray on whitespace (single quotes
240
272
  # survive literally), so each must be a single unquoted command token
241
273
  # that runs a pnpm script. version-packages bumps versions, syncs
242
- # plugin.json, THEN regenerates the committed generated output
243
- # (build:claude writes claude/.claude-plugin/plugin.json with the NEW
244
- # version, build:copilot re-derives agents/*.agent.md) before the action
245
- # commits, so the Version Packages PR always passes the drift gates.
274
+ # plugin.json, THEN regenerates the committed Copilot output
275
+ # (build:copilot re-derives agents/*.agent.md; the Claude plugin is
276
+ # materialised at install time with the package version, so nothing
277
+ # committed needs regenerating) before the action commits, so the
278
+ # Version Packages PR always passes the drift gates.
246
279
  version: pnpm version-packages
247
280
  publish: pnpm publish-package
248
281
  commit: "chore: version packages"
package/AGENTS.md CHANGED
@@ -33,7 +33,10 @@ agent-cortex/
33
33
  │ ├── README.md # Installation & usage
34
34
  │ └── index.ts # Extension entrypoint
35
35
  ├── hooks/ # Canonical Claude Code hooks (hooks.json + scripts — see docs/claude-hooks.md)
36
- │ └── claude/ # generated into claude/hooks.json + claude/hooks/ by bin/installers/claude.mjs
36
+ │ └── claude/ # generated into the plugin's hooks.json + hooks/ by bin/installers/claude.mjs
37
+ ├── claude-extras/ # Hand-authored Claude plugin extras (no committed claude/ output)
38
+ │ ├── .mcp.json # MCP servers (context7, github) — copied into installs
39
+ │ └── scripts/ # statusline-command.sh — copied into installs (executable)
37
40
  └── package.json # PI package manifest (pi: { extensions, skills })
38
41
  ```
39
42
 
@@ -47,6 +50,13 @@ See the `style-versioning` skill for the full workflow.
47
50
 
48
51
  Generated automatically by changesets. Do not edit `CHANGELOG.md` manually.
49
52
 
53
+ ## Coding Conventions
54
+
55
+ - **Never block the main thread**: no `*Sync` functions (`readFileSync`, `spawnSync`, …).
56
+ Tooling code in `bin/`, `scripts/`, `lib/`, `hooks/`, and `test/` must be async —
57
+ enforced by `scripts/check-no-sync.mjs` (runs via `pnpm lint`). Skill-owned
58
+ scripts (`skills/*/scripts`) and `extensions/` are the remaining sync surfaces.
59
+
50
60
  ## Skill Conventions
51
61
 
52
62
  - `SKILL.md` must include a YAML front-matter block with `name` and `description`.
@@ -66,10 +76,14 @@ Generated automatically by changesets. Do not edit `CHANGELOG.md` manually.
66
76
  from the canonical directories by the shared `bin/installers/copilot.mjs` code path behind both
67
77
  `agent-cortex install copilot` and `scripts/build-copilot-agents.mjs` (thin wrapper), and
68
78
  committed for Copilot CLI (plugin.json `agents: "agents/"` scans for `*.agent.md`); never edit
69
- them by hand. The `claude/` subtree is generated by the install-time generator
70
- `bin/installers/claude.mjs` (a plain `agent-cortex install claude` also registers the plugin
71
- with Claude Code via the `claude plugin` CLI; `pnpm build:claude` is the generate-only alias,
72
- running `agent-cortex install claude --output claude`; `ralph`
79
+ them by hand. The Claude plugin is materialised by the install-time generator
80
+ `bin/installers/claude.mjs`: a plain `agent-cortex install claude` copies it into
81
+ `~/.agent-cortex/claude` (skills copied flat with `{{TOOL:...}}`/`{{PATH:...}}` substituted,
82
+ never symlinked), writes the marketplace manifest at
83
+ `~/.agent-cortex/.claude-plugin/marketplace.json`, and registers it with Claude Code via the
84
+ `claude plugin` CLI; the repo commits no `claude/` output — hand-authored plugin
85
+ extras live in `claude-extras/` (`.mcp.json`, `scripts/`), and `--output <dir>` is the
86
+ generate-only form (tests/CI materialiser checks); `ralph`
73
87
  is authored natively in `agents-native/` and copied verbatim; the other agents compose from their
74
88
  `claude/` harness dirs).
75
89
  - Keep orchestration logic in the agent file; extract shared per-task workflow into a skill
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.40.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 1b6b8b8: `agent-cortex install claude` now materialises the plugin: home-scoped install into `~/.agent-cortex/claude`, copied (token-substituted, no symlinks) skills, marketplace manifest, and automatic registration with Claude Code. Installer toolchain is fully async (no `*Sync` — never block the main thread), enforced by a `check-no-sync` lint scanner.
8
+ - 65119b8: Map `ask_user` → `ask_questions` (pi-questions package) and `skill` → `read` in the pi column of token-map.json: pi agent modes regain interactive questioning (`ask_questions`) instead of dropping the tool with a startup warning, and skill references resolve to reading their SKILL.md. Notes and docs updated to record that `ask_questions` is provided by the pi-questions package.
9
+ - 1b6b8b8: Retire the committed `claude/` plugin subtree: `agent-cortex install claude` into `~/.agent-cortex/claude` (with automatic marketplace registration) is now the only way to build the Claude Code plugin. Hand-authored extras moved to `claude-extras/`, the `build:claude` script is removed, and CI validates the installer instead of diffing committed output.
10
+
11
+ ### Patch Changes
12
+
13
+ - fcb16d7: Fix grill-with-docs failing to load: quote the front-matter description so skill parsers accept it.
14
+
3
15
  ## 1.39.0
4
16
 
5
17
  ### Minor Changes
package/README.md CHANGED
@@ -9,8 +9,6 @@ pi, and [Claude Code](https://code.claude.com/docs) (as a plugin).
9
9
  ```
10
10
  agent-cortex/
11
11
  ├── plugin.json # Copilot plugin manifest
12
- ├── .claude-plugin/
13
- │ └── marketplace.json # Claude marketplace (exposes ./claude as the "jaybeeuu" market)
14
12
  ├── agents/ # Canonical agents — composable <name>/ dirs (see agents/README.md)
15
13
  │ ├── *.agent.md # ralph, ralph-plan, plan, strategy — GENERATED from <name>/ by scripts/build-copilot-agents.mjs
16
14
  │ ├── ralph/ # composable form (shared agent.md + per-harness pi/, copilot/, claude/)
@@ -38,17 +36,12 @@ agent-cortex/
38
36
  │ ├── agent-cortex.mjs # CLI entrypoint
39
37
  │ └── installers/
40
38
  │ ├── copilot.mjs # shared generator: agent-cortex install copilot + scripts/build-copilot-agents.mjs
41
- │ └── claude.mjs # install-time generator + Claude Code registrar (pnpm build:claude = generate-only --output form)
39
+ │ └── claude.mjs # materialises ~/.agent-cortex/claude + registers with Claude Code (--output <dir> = generate-only form)
42
40
  ├── scripts/
43
41
  │ └── build-copilot-agents.mjs # thin wrapper over bin/installers/copilot.mjs (regenerates agents/*.agent.md)
44
- └── claude/ # Self-contained Claude Code plugin (GENERATED — do not hand-edit)
45
- ├── .claude-plugin/
46
- │ └── plugin.json
47
- ├── .mcp.json # MCP servers (context7, github) — hand-authored
48
- ├── hooks.json # SessionStart + Notification hooks — generated from hooks/claude/
49
- ├── hooks/ # hook scripts (e.g. scripts/notify.mjs) — generated from hooks/claude/
50
- ├── agents/ # composed from agents/<name>/claude/ + copied from agents-native/
51
- └── skills/ # symlinks to the grouped skills/ dirs (29 skills)
42
+ └── claude-extras/ # Hand-authored Claude plugin extras (no committed claude/ output)
43
+ ├── .mcp.json # MCP servers (context7, github) — copied into installs
44
+ └── scripts/ # statusline-command.sh — copied into installs (executable)
52
45
  ```
53
46
 
54
47
  The same `agents/` and `skills/` power three harnesses (Copilot, pi, Claude Code).
@@ -61,36 +54,44 @@ install-time and build-time output can never diverge) and committed so Copilot C
61
54
  The `{{TOOL:...}}` / `{{PATH:...}}` tokens written in agent and skill files are resolved
62
55
  per harness at install time from `token-map.json`, the single source of truth for
63
56
  canonical tool/path/agent names (see `token-map.README.md` and the `contract` section).
64
- The `claude/` subtree is **generated** by the install-time generator
65
- (`bin/installers/claude.mjs`). A plain `agent-cortex install claude` regenerates the
66
- subtree AND registers it with Claude Code by driving the `claude plugin` CLI (marketplace
67
- add → install → marketplace update → plugin update). `pnpm build:claude` is the pure,
68
- generate-only alias — it passes `--output claude`, so CI and release drift-checks never
69
- spawn the claude CLI or touch `~/.claude`. CI regenerates it and checks
70
- it is never stale:
71
-
72
- - **Skills** stay single-source — `claude/skills/<name>` are symlinks into the grouped
73
- `skills/<group>/<name>` dirs (Claude discovers skills only one level deep, so the
74
- grouping is flattened via links, not copies).
75
- - **Agents** can't be shared files (the frontmatter formats differ), so `claude/agents/*.md`
76
- are composed from the canonical `agents/<name>/` directories' `claude/` harness dirs
57
+ The Claude plugin is **materialised** at install time by `bin/installers/claude.mjs`. A
58
+ plain `agent-cortex install claude` copies the plugin into the home install root
59
+ (`~/.agent-cortex/claude`), writes a marketplace manifest at
60
+ `~/.agent-cortex/.claude-plugin/marketplace.json` exposing it, and registers it with
61
+ Claude Code by driving the `claude plugin` CLI — state-checked and idempotent (a fresh
62
+ install adds the marketplace + installs the plugin; a re-run updates what state says is
63
+ out of date; a repeat install at the same version is a no-op; a missing or pre-v2 CLI
64
+ warns and prints the manual registration commands instead of failing). The repo commits
65
+ no `claude/` output (hand-authored extras live in `claude-extras/`), so CI validates the
66
+ materialiser itself — a temp-dir install plus structural checks:
67
+
68
+ - **Skills** stay single-source — the installer copies each `skills/<group>/<name>/` dir
69
+ flat into `skills/<name>/` (Claude discovers skills only one level deep) with
70
+ `{{TOOL:...}}` / `{{PATH:...}}` tokens substituted against token-map.json's claude
71
+ column — no symlinks, so literal tokens never reach Claude.
72
+ - **Agents** can't be shared files (the frontmatter formats differ), so the materialised
73
+ plugin's `agents/*.md` are composed from the canonical `agents/<name>/` directories' `claude/`
74
+ harness dirs
77
75
  (frontmatter.json + section files), exactly like the Copilot flats are composed from their
78
76
  `copilot/` dirs. Claude only loads agents from a plugin's default `agents/` dir, so the plugin
79
- root is `claude/` — isolating it from the Copilot `.agent.md` files.
77
+ ships them at its own root (default `~/.agent-cortex/claude/agents/`) — isolating them from the
78
+ Copilot `.agent.md` files.
80
79
  - **Claude-native agents** that have no Copilot equivalent live in `agents-native/*.md` and are
81
- copied verbatim into `claude/agents/`. `ralph` is one: it is reimplemented for Claude around
80
+ copied verbatim into the materialised plugin's `agents/`. `ralph` is one: it is reimplemented
81
+ for Claude around
82
82
  background workers + an independent review gate (the Copilot Ralph's `task`/`read_agent`
83
83
  poll loop has no Claude equivalent), so it can't be mechanically converted.
84
- - **Manifests** are generated too: `claude/.claude-plugin/plugin.json` (its `version` tracks
85
- `package.json`, so it never goes stale) and `claude/hooks.json` (copied from the canonical
84
+ - **Manifests** are generated too: the plugin's `.claude-plugin/plugin.json` (its `version`
85
+ tracks `package.json`, so it never goes stale) and `hooks.json` (copied from the canonical
86
86
  `hooks/claude/hooks.json` source), plus any support files under `hooks/claude/` bundled into
87
- `claude/hooks/` so hook commands can reach them via `$CLAUDE_PLUGIN_ROOT`. `claude/.mcp.json`
88
- and `claude/scripts/` stay hand-authored. See `docs/claude-hooks.md` for the extension→hook
89
- mapping and the rejections (auto-name, skill-stats, subagent, agent-modes).
87
+ the plugin's `hooks/` so hook commands can reach them via `$CLAUDE_PLUGIN_ROOT`. Hand-authored
88
+ extras — `.mcp.json` and `scripts/` — are copied into every install from the `claude-extras/`
89
+ dir, which is their canonical store. See `docs/claude-hooks.md` for the
90
+ extension→hook mapping and the rejections (auto-name, skill-stats, subagent, agent-modes).
90
91
 
91
92
  Edit the sources (`agents/<name>/` composable dirs, `agents-native/*.md`, `skills/**`,
92
- `hooks/claude/`, `package.json`), never anything under `claude/` or the generated
93
- `agents/*.agent.md` files.
93
+ `hooks/claude/`, `claude-extras/`, `package.json`), never the generated
94
+ `agents/*.agent.md` files or anything under the materialised `~/.agent-cortex/claude`.
94
95
 
95
96
  ## CI
96
97
 
@@ -100,6 +101,15 @@ does its own checkout and `pnpm install` rather than sharing build artifacts fro
100
101
  the `setup` job — pnpm workspace symlinks don't survive artifact upload/download,
101
102
  so artifact sharing would break the workspace resolution that the build depends on.
102
103
 
104
+ The repo commits no generated `claude/` output, so the `claude-plugin-check` job
105
+ validates the Claude plugin **materialiser** instead of diffing a committed mirror:
106
+ it runs `node bin/agent-cortex.mjs install claude --output <tmp dir>` and checks the
107
+ result structurally — `plugin.json` version tracks `package.json`, every generated and
108
+ hand-authored piece is present, no literal `{{TOOL:...}}`/`{{PATH:...}}` tokens survive,
109
+ and no symlinks leak into the copied tree. The Copilot drift check
110
+ (`pnpm build:copilot` + `git diff --exit-code -- 'agents/*.agent.md'`) still guards the
111
+ committed flat agent files.
112
+
103
113
  A separate `changeset-check` job runs only on pull requests and fails any PR that
104
114
  touches a versioned path (`extensions/`, `skills/`, `agents/`, `package.json`, or
105
115
  `plugin.json`) without a changeset in `.changeset/`. Add one with `pnpm changeset` —
@@ -109,7 +119,9 @@ On pushes to `main`, a `release` job (gated on `lint`, `test`, and
109
119
  `claude-plugin-check`) runs changesets to open a `chore: version packages` PR when
110
120
  changesets are pending, then publishes to npm once it lands. The version step runs
111
121
  `pnpm version-packages` — bumping `package.json`, syncing `plugin.json`, and
112
- regenerating the committed generated output so the drift gates stay green; the
122
+ regenerating the committed Copilot agent files (the Claude plugin is materialised at
123
+ install time with the package version, so it has no committed output to regenerate)
124
+ so the drift gates stay green; the
113
125
  publish step runs `pnpm publish-package` (pack + provenance publish). Publish
114
126
  authenticates via npm Trusted Publishing (OIDC) — no npm token or GitHub
115
127
  secret is needed, only the one-time npm-side setup on npmjs.com (package →
@@ -185,32 +197,39 @@ copilot plugin install ./agent-cortex
185
197
 
186
198
  ### Claude Code plugin (separate)
187
199
 
188
- The Claude plugin is the self-contained `claude/` subtree (manifest at
189
- `claude/.claude-plugin/plugin.json`), containing **4 agents** (`strategy`, `plan`,
190
- `ralph-plan`, `ralph`), **29 skills**, `SessionStart` + `Notification` hooks, and 2 MCP servers.
200
+ A plain `agent-cortex install claude` **materialises** the plugin into the home install
201
+ root `~/.agent-cortex/claude` — **4 agents** (`strategy`, `plan`, `ralph-plan`, `ralph`),
202
+ **29 skills** (copied flat per skill, `{{TOOL:...}}` / `{{PATH:...}}` token-substituted — no
203
+ symlinks), `SessionStart` + `Notification` hooks, and 2 MCP servers — writes a marketplace
204
+ manifest at `~/.agent-cortex/.claude-plugin/marketplace.json` exposing `./claude`, and
205
+ registers it with Claude Code. The repo commits no `claude/` output: hand-authored extras
206
+ (`.mcp.json`, `scripts/`) are served from `claude-extras/`, and everything else is generated
207
+ at install time — there is nothing to drift.
191
208
 
192
- `claude/` is committed (the marketplace install flow reads it straight from the working
193
- tree, so a fresh clone installs as-is), and rebuilt via the install-time generator if you
194
- change the sources:
209
+ The generate-only `--output <dir>` form is for previewing and CI validation; the
210
+ documented path is the plain install to `~/.agent-cortex/claude`:
195
211
 
196
212
  ```sh
197
213
  pnpm build:copilot # or: node scripts/build-copilot-agents.mjs (regenerates agents/*.agent.md)
198
- pnpm build:claude # or: node bin/agent-cortex.mjs install claude --output claude (generate-only)
199
- # or the install-time entry (same generator, less typing):
200
214
  node bin/agent-cortex.mjs install copilot # regenerates agents/*.agent.md in place
201
215
  node bin/agent-cortex.mjs install copilot --dry-run # plan only, no writes
202
216
  node bin/agent-cortex.mjs install copilot --output /tmp/x # preview the flat files elsewhere
203
- node bin/agent-cortex.mjs install claude # regenerates ./claude AND registers it with Claude Code
217
+ node bin/agent-cortex.mjs install claude # materialises ~/.agent-cortex/claude + marketplace manifest AND registers it with Claude Code (user scope, idempotent)
204
218
  node bin/agent-cortex.mjs install claude --dry-run # plan generation + registration, no writes/spawns
205
- node bin/agent-cortex.mjs install claude --output /tmp/x # generate only — no registration
219
+ node bin/agent-cortex.mjs install claude --require-register # fail (non-zero exit) when the claude CLI can't drive registration
220
+ node bin/agent-cortex.mjs install claude --output /tmp/x # generate only (preview/CI) — no marketplace manifest, no registration
206
221
  ```
207
222
 
208
223
  #### Try it for one session
209
224
 
225
+ Generate a throwaway plugin and point Claude Code at it — no install or registration
226
+ needed:
227
+
210
228
  ```sh
211
- claude --plugin-dir /path/to/agent-cortex/claude
229
+ node bin/agent-cortex.mjs install claude --output /tmp/agent-cortex-claude
230
+ claude --plugin-dir /tmp/agent-cortex-claude
212
231
  # verify what loaded:
213
- claude --plugin-dir /path/to/agent-cortex/claude plugin details agent-cortex
232
+ claude --plugin-dir /tmp/agent-cortex-claude plugin details agent-cortex
214
233
  ```
215
234
 
216
235
  `SKILL.md` edits are picked up live in that session; agent, hook, and MCP changes need
@@ -218,24 +237,34 @@ claude --plugin-dir /path/to/agent-cortex/claude plugin details agent-cortex
218
237
 
219
238
  #### Install persistently (recommended)
220
239
 
221
- The repo ships a marketplace manifest (`.claude-plugin/marketplace.json`) that exposes the
222
- `claude/` subtree. A plain `agent-cortex install claude` does both halves in one step: it
223
- regenerates `claude/` and registers the plugin with Claude Code by driving the `claude
224
- plugin` CLI (marketplace add → install → marketplace update → plugin update), pointing it
225
- at this checkout's manifest. This requires the `claude` plugin CLI (Claude Code v2+):
240
+ The plain install does both halves in one step: it materialises the plugin into
241
+ `~/.agent-cortex/claude`, writes `~/.agent-cortex/.claude-plugin/marketplace.json` (the
242
+ home install root doubles as the marketplace root — the manifest exposes `./claude`), and
243
+ registers it with Claude Code by driving the `claude plugin` CLI against that root at
244
+ **user scope** (the `claude plugin install` default, so the plugin is available in every
245
+ session — the recommended persistent flow). Registration is idempotent by state, not by
246
+ exit code: `claude plugin marketplace list --json` picks add-vs-update and
247
+ `claude plugin list --json` picks install-vs-update against the materialised version. A
248
+ fresh install adds the marketplace and installs the plugin; a re-run is the update path
249
+ (`marketplace update`, plus `plugin update` only when a newer version is materialised); a
250
+ repeat install at the same version is a true no-op. It requires the `claude` plugin CLI
251
+ (Claude Code v2+): with a missing or pre-v2 CLI the installer warns and prints the manual
252
+ commands below, exiting 0 — `--require-register` makes registration mandatory and fails
253
+ non-zero when it can't run:
226
254
 
227
255
  ```sh
228
- git clone https://github.com/jaybeeuu/agent-cortex # first time (or git pull to update)
229
- agent-cortex install claude # regenerate + register (user scope)
256
+ agent-cortex install claude # materialise + register (user scope, idempotent)
257
+ agent-cortex install claude --require-register # register or fail the install
230
258
  ```
231
259
 
232
260
  `--dry-run` prints the full plan without spawning the claude CLI or writing anything;
233
- `--output <dir>` regenerates only, with no registration. The equivalent manual registration
234
- (e.g. on a Claude Code build without the plugin CLI) adds the checkout by **absolute path**
235
- (a bare `.` is rejected):
261
+ `--output <dir>` generates only, with no marketplace manifest and no registration. The
262
+ equivalent manual registration adds a marketplace root by **absolute path** (a bare `.` is
263
+ rejected) — the home install root (`~/.agent-cortex`) is the marketplace root written by
264
+ the plain install; the repo checkout ships no manifest:
236
265
 
237
266
  ```sh
238
- claude plugin marketplace add /path/to/agent-cortex
267
+ claude plugin marketplace add ~/.agent-cortex # after a plain install
239
268
  claude plugin install agent-cortex@jaybeeuu # every session (user scope)
240
269
  # or, for this project only:
241
270
  claude plugin install agent-cortex@jaybeeuu --scope local
@@ -246,17 +275,23 @@ flag, and Ralph is just `claude --agent agent-cortex:ralph`.
246
275
 
247
276
  #### Update
248
277
 
249
- The marketplace source is your local checkout, so after pulling changes re-run the installer —
250
- it rebuilds the `claude/` subtree and refreshes the registered plugin (marketplace update →
251
- plugin update) in one step:
278
+ The plain install re-materialises from the current sources, so after pulling changes
279
+ re-run it — it refreshes the materialised plugin and re-registers whatever the installed
280
+ state says is out of date (`marketplace update`, plus `plugin update` only when a newer
281
+ version is materialised; a repeat install at the same version is a no-op) in one step:
252
282
 
253
283
  ```sh
254
284
  git pull
255
- agent-cortex install claude # rebuild + refresh (restart Claude Code to apply)
285
+ agent-cortex install claude # re-materialise + refresh (restart Claude Code to apply)
256
286
  ```
257
287
 
258
- `pnpm build:claude` only regenerates the subtree (no registration), so on its own it won't
259
- refresh an installed plugin.
288
+ Registration matches the marketplace by **name**, not path: a marketplace previously added
289
+ from a different root is refreshed in place rather than re-pointed at the fresh home
290
+ install. Re-point it with
291
+ `claude plugin marketplace remove jaybeeuu` and re-run the install.
292
+
293
+ The plain install IS the update — there is no separate build step. (`--output <dir>` only
294
+ generates a preview and never refreshes an installed plugin.)
260
295
 
261
296
  #### Uninstall
262
297
 
@@ -281,7 +316,7 @@ The lean **Ralph** runs as the interactive main agent (not delegated — it must
281
316
  receive its workers' completions):
282
317
 
283
318
  ```sh
284
- claude --agent agent-cortex:ralph # (add --plugin-dir .../claude if not installed)
319
+ claude --agent agent-cortex:ralph # (omitted once installed via agent-cortex install claude; else --plugin-dir ~/.agent-cortex/claude)
285
320
  ```
286
321
 
287
322
  It finds ready beads, spawns parallel background workers (implement → independent review →
@@ -313,14 +348,16 @@ extension→hook audit.
313
348
 
314
349
  Edit the **sources** — the composable `agents/<name>/` directories (shared `agent.md` +
315
350
  per-harness frontmatter/sections, auto-composed by `scripts/build-copilot-agents.mjs` and
316
- the install-time generator `bin/installers/claude.mjs`), `agents-native/*.md` (Claude-only
351
+ `bin/installers/claude.mjs`), `agents-native/*.md` (Claude-only
317
352
  agents like `ralph` — the canonical bodies the installer copies verbatim), `skills/**`,
318
- `hooks/claude/hooks.json` (hook config), and `package.json` (plugin
319
- version) — then run `pnpm build:claude` (the generate-only alias of `agent-cortex install
320
- claude --output claude`; run the plain install instead if you also want the plugin
321
- re-registered with Claude Code). Never hand-edit
322
- anything under `claude/` except the hand-authored `claude/.mcp.json` and `claude/scripts/`
323
- files, or the generated `agents/*.agent.md` files, `claude/agents/`, `claude/skills/`,
324
- `claude/.claude-plugin/plugin.json`, and `claude/hooks.json`. CI runs
325
- `git diff --exit-code claude/agents/ claude/skills/ claude/.claude-plugin/plugin.json claude/hooks.json`
326
- and `git diff --exit-code -- 'agents/*.agent.md'` so the generated outputs can never drift.
353
+ `hooks/claude/hooks.json` (hook config), `claude-extras/` (hand-authored `.mcp.json` and
354
+ `scripts/`), and `package.json` (plugin
355
+ version — picked up at install time) — then run
356
+ `agent-cortex install claude` (re-materialises the home plugin and re-registers it;
357
+ `--output <dir>` generates a preview only). There is no committed
358
+ `claude/` output to hand-edit or regenerate: `~/.agent-cortex/claude` is generated in full
359
+ (`.claude-plugin/plugin.json`, `agents/`, `skills/`, `hooks.json`, `.mcp.json`, `scripts/`)
360
+ so never edit anything under it. The generated `agents/*.agent.md` files are still
361
+ drift-checked by CI (`git diff --exit-code -- 'agents/*.agent.md'`), and CI validates the
362
+ Claude materialiser (structural checks on a temp-dir install) so sources and installer can
363
+ never silently diverge.
package/agents/README.md CHANGED
@@ -143,8 +143,8 @@ When composing the final agent file for a specific harness:
143
143
  2. Replace `{{SECTION:name}}` with content from `<harness>/<name>.md`
144
144
  3. `{{TOOL:name}}` and `{{PATH:name}}` are resolved against `token-map.json` — the
145
145
  canonical tool/path/agent names per harness. `scripts/lib/compose-agent.mjs` (used by
146
- `build:copilot` / `build:claude` and by `bin/installers/pi.mjs`) resolves them when
147
- generating the flat files / installing for pi; pi's `agent-modes` extension does the
146
+ `build:copilot` and by `bin/installers/claude.mjs` / `bin/installers/pi.mjs`) resolves them when
147
+ generating the flat files / installing for claude and pi; pi's `agent-modes` extension does the
148
148
  same at runtime. See the `contract` section of `token-map.json` and
149
149
  `token-map.README.md` for the rules.
150
150
 
@@ -209,21 +209,22 @@ flat `agents/*.agent.md` files are built from them:
209
209
  - `scripts/build-copilot-agents.mjs` (`pnpm build:copilot`) composes `agents/*.agent.md` from
210
210
  each agent's `copilot/` harness dir — committed for Copilot CLI, which loads `*.agent.md`
211
211
  from `plugin.json`'s `agents: "agents/"` path.
212
- - `bin/installers/claude.mjs` (plain `agent-cortex install claude` also registers the plugin
213
- with Claude Code; `pnpm build:claude` is its generate-only alias, running `install claude
214
- --output claude`) composes `claude/agents/*.md` from each agent's `claude/` harness dir (except
215
- `ralph`, which ships natively from `agents-native/ralph.md`, copied verbatim).
212
+ - `bin/installers/claude.mjs` (plain `agent-cortex install claude` materialises the plugin
213
+ into `~/.agent-cortex/claude` and registers it with Claude Code; `--output <dir>` is its
214
+ generate-only form, used by tests/CI) composes the plugin's
215
+ `agents/*.md` from each agent's `claude/` harness dir (except `ralph`, which ships natively
216
+ from `agents-native/ralph.md`, copied verbatim).
216
217
  - The pi harness delivers two ways: the `agent-modes` extension composes `agent.md` +
217
218
  `pi/frontmatter.json` at runtime against `token-map.json`, and `agent-cortex install pi`
218
219
  materialises composed agents + token-substituted skills into `~/.pi/agent` at install
219
220
  time (see README "Pi harness agents & skills").
220
221
 
221
- Never edit the generated flat/claude files by hand — edit the composable directory. CI
222
- regenerates both outputs and fails on drift. Generation is install-time: `claude` ships
222
+ Never edit the generated flat materialised files by hand — edit the composable directory.
223
+ Generation is install-time: `claude` ships
223
224
  via `bin/installers/claude.mjs`, the generator behind plain `agent-cortex install claude`
224
- (which also registers the plugin with Claude Code) and its generate-only `pnpm
225
- build:claude` alias (`install claude --output claude`) — the committed `claude/` subtree
226
- is regenerated by it and drift-checked, so it can never be stale; `copilot` ships via
225
+ (which materialises into `~/.agent-cortex/claude` and registers the plugin with Claude
226
+ Code); the repo commits no `claude/` output (hand-authored extras live in `claude-extras/`),
227
+ and CI validates the materialiser with structural checks on a temp-dir install; `copilot` ships via
227
228
  `bin/installers/copilot.mjs`, the shared code path behind both `agent-cortex install
228
229
  copilot` and `scripts/build-copilot-agents.mjs`; `pi` ships via
229
230
  `bin/installers/pi.mjs`, which materialises composed agents + token-substituted skills
@@ -30,7 +30,7 @@ if (parsed.command === "install") {
30
30
 
31
31
  if (parsed.harness === "pi") {
32
32
  try {
33
- const result = installPi({
33
+ const result = await installPi({
34
34
  dryRun: parsed.dryRun ?? false,
35
35
  warn: () => {}, // warnings surface once in the printed summary
36
36
  ...(parsed.output ? { output: parsed.output } : {}),
@@ -47,21 +47,34 @@ if (parsed.command === "install") {
47
47
  process.stdout.write(`Installing agent-cortex for "${parsed.harness}" harness…\n`);
48
48
 
49
49
  // claude is the first wired harness: a plain `agent-cortex install claude`
50
- // regenerates the plugin subtree from the canonical sources (agents/,
51
- // agents-native/, skills/, hooks/claude/) via the shared installClaude
52
- // generator AND registers it with Claude Code (marketplace add → install →
53
- // update) — `pnpm build:claude` is the generate-only alias of this
54
- // invocation (it passes `--output claude`), so there is no separate
55
- // build-time code path and CI drift-checks never touch ~/.claude.
50
+ // materialises the plugin into the home install root (~/.agent-cortex/claude)
51
+ // with copied, token-substituted skills, writes the marketplace manifest at
52
+ // ~/.agent-cortex/.claude-plugin/marketplace.json, and registers it with
53
+ // Claude Code (marketplace add → install → update); `--output <dir>` is the
54
+ // generate-only form (tests/CI validation), and there is no committed claude/
55
+ // subtree in the repo anymore.
56
56
  if (parsed.harness === "claude") {
57
57
  // Avoid loading the installer on the help/summary paths and for other harnesses.
58
58
  const { installClaude, registerClaude } = await import("./installers/claude.mjs");
59
59
  try {
60
- installClaude({ output: parsed.output, dryRun: parsed.dryRun });
60
+ const result = await installClaude({
61
+ dryRun: parsed.dryRun ?? false,
62
+ warn: () => {}, // warnings surface once via result.warnings
63
+ ...(parsed.output ? { output: parsed.output } : {}),
64
+ });
65
+ for (const warning of result.warnings) {
66
+ process.stdout.write(` ⚠ ${warning}\n`);
67
+ }
61
68
  // Runtime registration happens only for the plain install — `--output`
62
69
  // keeps generating only (documented in --help).
63
70
  if (parsed.output === undefined) {
64
- registerClaude({ dryRun: parsed.dryRun });
71
+ await registerClaude({
72
+ root: result.marketplaceRoot,
73
+ manifest: result.marketplaceManifest,
74
+ pluginVersion: result.pluginVersion,
75
+ dryRun: parsed.dryRun ?? false,
76
+ requireRegister: parsed.requireRegister ?? false,
77
+ });
65
78
  }
66
79
  process.exit(0);
67
80
  } catch (err) {
@@ -76,7 +89,7 @@ if (parsed.command === "install") {
76
89
  if (parsed.harness === "copilot") {
77
90
  const { installCopilot } = await import("./installers/copilot.mjs");
78
91
  try {
79
- installCopilot({ output: parsed.output, dryRun: parsed.dryRun });
92
+ await installCopilot({ output: parsed.output, dryRun: parsed.dryRun });
80
93
  process.exit(0);
81
94
  } catch (err) {
82
95
  process.stderr.write(`Install failed: ${err.message}\n`);