@jaybeeuu/agent-cortex 1.40.0 → 1.41.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 (69) hide show
  1. package/AGENTS.md +21 -8
  2. package/CHANGELOG.md +48 -0
  3. package/README.md +78 -38
  4. package/agents/README.md +5 -3
  5. package/bin/agent-cortex.mjs +36 -4
  6. package/bin/installers/claude.mjs +171 -21
  7. package/bin/installers/copilot.mjs +2 -17
  8. package/bin/installers/pi.mjs +261 -22
  9. package/claude/settings.json +4 -0
  10. package/claude-extras/scripts/statusline-command.sh +32 -4
  11. package/docs/claude-hooks.md +12 -0
  12. package/docs/ideas/ban-content-assertion-tests.md +73 -0
  13. package/docs/ideas/bead-progress-notes-for-session-resumption.md +75 -0
  14. package/docs/ideas/classify-beads-via-deterministic-script-with-agent-fallback.md +70 -0
  15. package/docs/ideas/ctx-alias-version-and-update-commands.md +65 -0
  16. package/docs/ideas/ctx-feedback-command-for-capturing-ideas-from-any-machine.md +67 -0
  17. package/docs/ideas/deployment-beads-for-post-merge-pipelines.md +36 -0
  18. package/docs/ideas/design-and-delivery-as-separate-bead-trees/design-and-delivery-as-separate-bead-trees.md +99 -0
  19. package/docs/ideas/enable-signed-commit-enforcement-changesets-bot-commits-must-be-signed.md +33 -0
  20. package/docs/ideas/extension-manifests.md +55 -14
  21. package/docs/ideas/improve-pi-system-prompt.md +5 -2
  22. package/docs/ideas/map-every-cortex-tool-to-a-real-pi-equivalent.md +95 -0
  23. package/docs/ideas/record-idea-implementation-questions.md +68 -0
  24. package/docs/ideas/self-flushing-session.md +72 -0
  25. package/docs/ideas/state-in-beads-not-md.md +69 -0
  26. package/docs/ideas/task-session-context.local.md +95 -0
  27. package/docs/ideas/test-data-creation-helpers.md +92 -0
  28. package/docs/ideas/test-planning-stage-bead-in-feature-planning.md +68 -0
  29. package/docs/ideas/worktree-housekeeping-for-closed-beads.md +39 -0
  30. package/docs/technical-direction/harness-config.md +161 -0
  31. package/docs/technical-direction/migration-execution-labels.md +60 -23
  32. package/extensions/agent-guardrails/README.md +45 -0
  33. package/extensions/agent-guardrails/guardrails.test.ts +42 -0
  34. package/extensions/agent-guardrails/guardrails.ts +66 -0
  35. package/extensions/agent-guardrails/index.ts +22 -0
  36. package/extensions/agent-guardrails/node_modules/.bin/pi +21 -0
  37. package/extensions/agent-guardrails/node_modules/.bin/tsc +21 -0
  38. package/extensions/agent-guardrails/node_modules/.bin/tsserver +21 -0
  39. package/extensions/agent-guardrails/node_modules/.bin/tsx +21 -0
  40. package/extensions/agent-guardrails/package.json +19 -0
  41. package/extensions/agent-guardrails/tsconfig.json +12 -0
  42. package/extensions/agent-modes/README.md +9 -3
  43. package/extensions/agent-modes/index.test.ts +208 -0
  44. package/extensions/agent-modes/index.ts +23 -6
  45. package/extensions/agent-modes/package.json +1 -1
  46. package/lib/cli.mjs +8 -2
  47. package/lib/pi-packages.mjs +169 -0
  48. package/package.json +5 -1
  49. package/pi/settings.json +23 -12
  50. package/plugin.json +1 -1
  51. package/scripts/lib/fs.mjs +31 -0
  52. package/skills/planning/classify-bead/REFERENCE.md +85 -0
  53. package/skills/planning/classify-bead/SKILL.md +32 -36
  54. package/skills/planning/classify-bead/scripts/classify-bead.mjs +292 -0
  55. package/skills/planning/create-task/SKILL.md +19 -10
  56. package/skills/planning/write-a-prd/SKILL.md +2 -2
  57. package/skills/style/style-code/REFERENCE.md +32 -2
  58. package/skills/style/style-code/SKILL.md +5 -21
  59. package/skills/style/style-tests/SKILL.md +69 -17
  60. package/skills/workflow/run-pipeline-stage/SKILL.md +13 -1
  61. package/skills/workflow/run-pipeline-stage/prompts/stage-runner.md +12 -0
  62. package/test/classify-bead.test.mjs +277 -0
  63. package/test/cli.test.mjs +55 -11
  64. package/test/fs.test.mjs +76 -0
  65. package/test/installer.test.mjs +237 -4
  66. package/test/pi-installer.test.mjs +343 -4
  67. package/test/pi-packages.test.mjs +295 -0
  68. package/token-map.README.md +4 -2
  69. package/token-map.json +3 -3
package/AGENTS.md CHANGED
@@ -13,7 +13,7 @@ available in the author's coding sessions.
13
13
 
14
14
  ```
15
15
  agent-cortex/
16
- ├── package.json # PI package manifest (pi: { extensions, skills })
16
+ ├── package.json # PI package manifest (pi: { extensions, skills, packages })
17
17
  ├── plugin.json # GitHub Copilot plugin manifest — agents, skills, MCP servers
18
18
  ├── agents/ # Custom agents — canonical composable <name>/ dirs
19
19
  │ ├── *.agent.md # generated from <name>/ by scripts/build-copilot-agents.mjs
@@ -34,10 +34,11 @@ agent-cortex/
34
34
  │ └── index.ts # Extension entrypoint
35
35
  ├── hooks/ # Canonical Claude Code hooks (hooks.json + scripts — see docs/claude-hooks.md)
36
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)
37
+ ├── claude/ # Committed Claude user-settings template — merged into ~/.claude/settings.json on install
38
+ ├── claude-extras/ # Hand-authored Claude plugin extras
38
39
  │ ├── .mcp.json # MCP servers (context7, github) — copied into installs
39
40
  │ └── scripts/ # statusline-command.sh — copied into installs (executable)
40
- └── package.json # PI package manifest (pi: { extensions, skills })
41
+ └── package.json # PI package manifest (pi: { extensions, skills, packages })
41
42
  ```
42
43
 
43
44
  ## Versioning
@@ -56,12 +57,18 @@ Generated automatically by changesets. Do not edit `CHANGELOG.md` manually.
56
57
  Tooling code in `bin/`, `scripts/`, `lib/`, `hooks/`, and `test/` must be async —
57
58
  enforced by `scripts/check-no-sync.mjs` (runs via `pnpm lint`). Skill-owned
58
59
  scripts (`skills/*/scripts`) and `extensions/` are the remaining sync surfaces.
60
+ - **Share generic tooling helpers in `scripts/lib/`**: tooling code (installers under
61
+ `bin/installers/`, hooks, other scripts) imports shared utilities from `scripts/lib/`
62
+ (e.g. `compose-agent.mjs`, `fs.mjs`) rather than redefining them inline.
59
63
 
60
64
  ## Skill Conventions
61
65
 
62
66
  - `SKILL.md` must include a YAML front-matter block with `name` and `description`.
63
67
  - `description` must be ≤ 1024 characters. First sentence: what it does. Second: "Use when…".
64
- - Keep `SKILL.md` under ~100 lines. Overflow into `REFERENCE.md` for rarely-needed detail.
68
+ - Keep `SKILL.md` near the ~150-line target in `docs/skills/skill-anatomy.md`, overflowing into
69
+ purpose-named support files (`REFERENCE.md`, `EXAMPLES.md`, `FORMAT.md`). `style-tests`
70
+ deliberately sits at the top of that range rather than splitting — its principles must load
71
+ automatically with the skill.
65
72
  - Add utility scripts to `scripts/` when an operation is deterministic and would otherwise be
66
73
  regenerated each time (e.g. install scripts, scanners).
67
74
  - Skill names use kebab-case and are grouped by prefix where related (e.g. `review-security`,
@@ -80,10 +87,13 @@ Generated automatically by changesets. Do not edit `CHANGELOG.md` manually.
80
87
  `bin/installers/claude.mjs`: a plain `agent-cortex install claude` copies it into
81
88
  `~/.agent-cortex/claude` (skills copied flat with `{{TOOL:...}}`/`{{PATH:...}}` substituted,
82
89
  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`
90
+ `~/.agent-cortex/.claude-plugin/marketplace.json`, merges the committed `claude/settings.json`
91
+ template into `~/.claude/settings.json`, and registers it with Claude Code via the
92
+ `claude plugin` CLI. The settings merge preserves personal config: the CLI owns exactly
93
+ `enabledPlugins` + `extraKnownMarketplaces`, and every other key is left untouched. Only
94
+ that settings template is committed under `claude/` — the generated plugin subtree is not;
95
+ hand-authored plugin extras live in `claude-extras/` (`.mcp.json`, `scripts/`), and
96
+ `--output <dir>` is the generate-only form (tests/CI materialiser checks); `ralph`
87
97
  is authored natively in `agents-native/` and copied verbatim; the other agents compose from their
88
98
  `claude/` harness dirs).
89
99
  - Keep orchestration logic in the agent file; extract shared per-task workflow into a skill
@@ -96,6 +106,9 @@ Generated automatically by changesets. Do not edit `CHANGELOG.md` manually.
96
106
  - Extensions are auto-discovered when the repo is installed as a PI package via `pi install`.
97
107
  - Keep extensions lightweight: no internal LLM calls. Extensions observe events and provide
98
108
  commands — they should not add token overhead.
109
+ - Register CLI flags from the extension factory (load time), never from a `session_start`
110
+ handler: PI matches parsed CLI flags against the extensions loaded at startup, so late
111
+ registration makes the flag fail with `Unknown option`.
99
112
  - Extensions that persist data should write to `~/.pi/agent-cortex/` (global, cross-project)
100
113
  and tag records with the project path for per-project slicing.
101
114
  - PI extensions with Claude Code equivalents are ported to `hooks/claude/`; the port/reject
package/CHANGELOG.md CHANGED
@@ -1,5 +1,53 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.41.0
4
+
5
+ ### Minor Changes
6
+
7
+ - a6b1929: `agent-cortex install pi` now materialises the pi config files as real CLI-managed files, replacing the legacy symlinks into the repo's `pi/` directory. `settings.json` is merged — the committed template supplies defaults, values already present (personal config and anything pi wrote) win, and the CLI owns the `packages` list with the repo path entry resolved against the settings file — so personal config survives re-install. `keybindings.json` is written from the template, refreshed when the template changes, and left alone once edited by hand. The generated header lives in a top-level `"//"` key because pi parses both files with a bare `JSON.parse`. `--dry-run` prints the plan (including "would remove symlink …") and `--output <dir>` keeps the generate-only semantics. Two caveats: because the live value wins, a changed default in the committed template only reaches fresh installs (edit the live file to adopt it); and `packages` is rebuilt from the template, so any `npm:` package added to the live file with `pi install npm:<pkg>` is dropped on the next install — declare long-lived packages in the repo template. An unparseable live `settings.json` is warned about and overwritten.
8
+ - d6ac5a5: Provision the pi packages agent-cortex needs (pi-questions for `ask_questions`, pi-web-access for `fetch_content`) on `agent-cortex install pi`. The package manifest now declares them under `pi.packages`, the installer installs any that the pi user scope is missing via the pi CLI (idempotent, warns and continues on failure), and `--no-provision` opts an offline machine out. The token-map `ask_user` / `web_fetch` mappings are no longer no-ops on a clean `~/.pi`.
9
+ - daa77c8: Encode test-quality rules into `style-tests`: the full agreed principle set (behaviour over
10
+ implementation, mocking only at external edges, module-boundary units, realistic
11
+ LocalStack/TestContainers integration, test independence, CI-only truth, spec-by-test, and
12
+ more), a blanket ban on asserting the static contents of a file (config, markdown, terraform,
13
+ snapshots) with the behaviour-not-inventory distinction, and test-data construction rules —
14
+ inline data, no shared mutable top-level fixtures, and small configurable factories over
15
+ repeated full literals. The content-assertion ban and the test-data rules land as red-flag rows
16
+ plus verification-checklist items. `style-tests/SKILL.md` deliberately occupies the full
17
+ ~150-line `SKILL.md` budget so the principles load automatically, rather than splitting into
18
+ `REFERENCE.md`.
19
+
20
+ ### Patch Changes
21
+
22
+ - 3e2d149: Add the `agent-guardrails` pi extension: it appends behavioural circuit-breakers to the PI
23
+ system prompt once per user prompt — two-strikes retry limits, explicit check-in triggers, and
24
+ atomic-change discipline (first slice of `docs/ideas/improve-pi-system-prompt.md`).
25
+ - a6b1929: The bundled `agent-modes` pi extension now registers its `--agent <name>` CLI flag while the extension loads, instead of from its `session_start` handler. PI validates parsed CLI flags against the extensions present at startup, so the late registration meant `pi --agent <name>` failed with `Unknown option: --agent`. Registration is extracted into a `registerAgentFlag(pi, agents)` helper called directly by the extension factory.
26
+ - a6b1929: Materialise `~/.claude/settings.json` from a new committed `claude/settings.json` template when
27
+ `agent-cortex install claude` runs (parity with the pi installer's merge-keep-personal contract).
28
+ The template supplies defaults and personal values win; the installer owns exactly
29
+ `enabledPlugins` + `extraKnownMarketplaces`, so `permissions`, `hooks`, `env`, `statusLine` and
30
+ unknown keys are never touched. A legacy symlinked settings file is replaced with a real file,
31
+ `--dry-run` plans without writing, and `--output <dir>` leaves the user's Claude config alone.
32
+ - 140d9d4: Add a deterministic classifier to the `classify-bead` skill
33
+ (`skills/planning/classify-bead/scripts/classify-bead.mjs`). One command resolves an
34
+ existing `implementation-type` label, the legacy `## Type` field, then conservative
35
+ heuristic signals, and applies the label itself; only beads with no deterministic signal
36
+ escalate to the rubric subagent. `create-task` and `write-a-prd` now run the classifier
37
+ before spawning a subagent, and `--audit` reports the deterministic-vs-rubric split
38
+ over recent beads.
39
+ - e055123: Extract the generic async `isFile`/`isDirectory` stat wrappers into a shared `scripts/lib/fs.mjs` and import them from the claude, copilot, and pi installers instead of redefining them inline.
40
+ - 720d678: Add a milestone-notes discipline to the `run-pipeline-stage` skill: the stage-runner prompt and
41
+ SKILL.md now instruct agents to append a short checkpoint to the bead's notes (`bd update <id>
42
+ --append-notes`) at stage start and at each milestone, so a fresh session can resume from
43
+ `bd show <id>` alone if the original session dies mid-stage.
44
+ - 515b75e: Document why the pi installer's substituted skills are the sole pi skill source (the local pi/settings.json packages filter `{ source, "skills": [] }` keeps raw package skills out) and update pi/settings.json to the current runtime config (models, packages, compaction, TUI prefs).
45
+ - 11f9a36: Add a class-over-factory rule to `style-code`: when a module holds mutable state and exposes
46
+ methods that transform or manage it, model it as a class with public methods and native
47
+ `#private` fields rather than a factory function returning a closure of methods (TypeScript's
48
+ `private` modifier is compile-time only). Existing functional guidance for stateless components,
49
+ hooks, and pure transforms is unchanged.
50
+
3
51
  ## 1.40.0
4
52
 
5
53
  ### Minor Changes
package/README.md CHANGED
@@ -25,21 +25,24 @@ agent-cortex/
25
25
  │ ├── style/ # style-code, style-tests, style-comms, style-documentation
26
26
  │ └── workflow/ # ralph, run-pipeline-stage, create-task, …
27
27
  ├── extensions/ # pi extensions (pi only)
28
+ │ ├── agent-guardrails/ # behavioural circuit-breakers in the system prompt
28
29
  │ ├── agent-modes/ # switchable agent modes (reads composable agents/)
29
30
  │ ├── skill-stats/
30
31
  │ └── notify/
31
- ├── pi/ # Global pi configuration (see below)
32
- │ └── settings.json
32
+ ├── pi/ # Committed pi config templates (see below)
33
+ │ ├── settings.json
34
+ │ └── keybindings.json
33
35
  ├── token-map.json # canonical tool/path/agent names per harness (install-time token substitution)
34
36
  ├── token-map.README.md # design decisions behind token-map.json
35
37
  ├── bin/
36
38
  │ ├── agent-cortex.mjs # CLI entrypoint
37
39
  │ └── installers/
38
40
  │ ├── copilot.mjs # shared generator: agent-cortex install copilot + scripts/build-copilot-agents.mjs
39
- │ └── claude.mjs # materialises ~/.agent-cortex/claude + registers with Claude Code (--output <dir> = generate-only form)
41
+ │ └── claude.mjs # materialises ~/.agent-cortex/claude + ~/.claude/settings.json, registers with Claude Code (--output <dir> = generate-only)
40
42
  ├── scripts/
41
43
  │ └── build-copilot-agents.mjs # thin wrapper over bin/installers/copilot.mjs (regenerates agents/*.agent.md)
42
- └── claude-extras/ # Hand-authored Claude plugin extras (no committed claude/ output)
44
+ ├── claude/ # Committed Claude user-settings template (settings.json) — merged into ~/.claude/settings.json
45
+ └── claude-extras/ # Hand-authored Claude plugin extras
43
46
  ├── .mcp.json # MCP servers (context7, github) — copied into installs
44
47
  └── scripts/ # statusline-command.sh — copied into installs (executable)
45
48
  ```
@@ -61,9 +64,10 @@ plain `agent-cortex install claude` copies the plugin into the home install root
61
64
  Claude Code by driving the `claude plugin` CLI — state-checked and idempotent (a fresh
62
65
  install adds the marketplace + installs the plugin; a re-run updates what state says is
63
66
  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
+ warns and prints the manual registration commands instead of failing). The generated
68
+ `claude/` plugin subtree is never committed (only the `claude/settings.json` template and
69
+ the hand-authored `claude-extras/` are), so CI validates the materialiser itself — a
70
+ temp-dir install plus structural checks:
67
71
 
68
72
  - **Skills** stay single-source — the installer copies each `skills/<group>/<name>/` dir
69
73
  flat into `skills/<name>/` (Claude discovers skills only one level deep) with
@@ -87,7 +91,7 @@ materialiser itself — a temp-dir install plus structural checks:
87
91
  the plugin's `hooks/` so hook commands can reach them via `$CLAUDE_PLUGIN_ROOT`. Hand-authored
88
92
  extras — `.mcp.json` and `scripts/` — are copied into every install from the `claude-extras/`
89
93
  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).
94
+ extension→hook mapping and the rejections (auto-name, agent-guardrails, skill-stats, subagent, agent-modes).
91
95
 
92
96
  Edit the sources (`agents/<name>/` composable dirs, `agents-native/*.md`, `skills/**`,
93
97
  `hooks/claude/`, `claude-extras/`, `package.json`), never the generated
@@ -101,7 +105,7 @@ does its own checkout and `pnpm install` rather than sharing build artifacts fro
101
105
  the `setup` job — pnpm workspace symlinks don't survive artifact upload/download,
102
106
  so artifact sharing would break the workspace resolution that the build depends on.
103
107
 
104
- The repo commits no generated `claude/` output, so the `claude-plugin-check` job
108
+ The repo commits no generated `claude/` plugin output, so the `claude-plugin-check` job
105
109
  validates the Claude plugin **materialiser** instead of diffing a committed mirror:
106
110
  it runs `node bin/agent-cortex.mjs install claude --output <tmp dir>` and checks the
107
111
  result structurally — `plugin.json` version tracks `package.json`, every generated and
@@ -130,30 +134,51 @@ are sourced entirely from `main`.
130
134
 
131
135
  ## Installation
132
136
 
133
- ### Symlink as global pi config
134
-
135
- This repo's `pi/settings.json` is symlinked to `~/.pi/agent/settings.json`,
136
- making it the canonical store for personal pi agent configuration:
137
-
138
- ```sh
139
- ~/.pi/agent/settings.json -> /path/to/agent-cortex/pi/settings.json
140
- ```
141
-
142
- All `pi install` / `pi remove` commands write to this file, and changes are
143
- committed to git. On a fresh machine:
144
-
145
- ```sh
146
- git clone https://github.com/jaybeeuu/agent-cortex
147
- ln -sf "$PWD/agent-cortex/pi/settings.json" ~/.pi/agent/settings.json
148
- ```
137
+ ### Pi config files (`settings.json` + `keybindings.json`)
138
+
139
+ `agent-cortex install pi` materialises `~/.pi/agent/settings.json` and
140
+ `~/.pi/agent/keybindings.json` as real CLI-managed files — the legacy symlinks
141
+ into this repo's `pi/` directory are gone. `pi/settings.json` and
142
+ `pi/keybindings.json` are the committed **templates**; the live files exist only
143
+ on the machine, so re-run the installer after pulling changes.
144
+
145
+ `settings.json` is merged. The template supplies defaults for keys the live file
146
+ lacks, every value already present in the live file wins, and keys the template
147
+ does not know about are preserved — so personal config (and anything pi itself
148
+ wrote) survives re-install. The one key the CLI owns outright is `packages`: it
149
+ is rebuilt from the template with the repo path entry resolved against the live
150
+ settings file.
151
+
152
+ Two consequences follow from that precedence:
153
+
154
+ - **Template changes to non-`packages` keys never reach an existing install.**
155
+ Because the live value always wins, editing a default in `pi/settings.json`
156
+ only affects a fresh install (or a manually deleted live file) — edit the live
157
+ file to adopt a new default.
158
+ - **`pi install npm:<pkg>` entries do not survive re-install.** `packages` is
159
+ rebuilt from the template, so any `npm:` package pi added to the live file is
160
+ dropped on the next `agent-cortex install pi`. Declare long-lived packages in
161
+ the repo template instead.
162
+
163
+ An unparseable live `settings.json` is warned about and overwritten.
164
+ `keybindings.json` follows a checksum rule: written from the template, refreshed
165
+ when the template changes, and left untouched once you edit it by hand (delete
166
+ it to re-adopt the template).
149
167
 
150
168
  ### Pi package dependencies
151
169
 
152
- These packages are declared in `pi/settings.json` and auto-installed by pi:
170
+ These packages are declared in the package manifest (`package.json` → `pi.packages`) and
171
+ provisioned by `agent-cortex install pi`, so a clean `~/.pi` gets the tools they provide
172
+ without a manual `pi install`. They are also declared in the template `pi/settings.json`,
173
+ so the materialised `settings.json` registers them with pi.
153
174
 
154
175
  | Package | Version | Purpose |
155
176
  |---|---|---|
156
- | [`pi-web-access`](https://www.npmjs.com/package/pi-web-access) | 0.10.7 | Web search, URL fetching, GitHub repo access, PDF/YouTube/video analysis |
177
+ | [`pi-web-access`](https://www.npmjs.com/package/pi-web-access) | 0.10.7 | Web search, URL fetching, GitHub repo access, PDF/YouTube/video analysis (`fetch_content`) |
178
+ | [`pi-questions`](https://www.npmjs.com/package/pi-questions) | latest | Structured interactive questions (`ask_questions`) |
179
+
180
+ Pass `--no-provision` to skip the package install step (e.g. offline machines); a
181
+ missing `pi` CLI or failed install warns and leaves the rest of the install intact.
157
182
 
158
183
  Desktop notifications are handled by the local `extensions/notify/` extension
159
184
  (replaces the former `pi-notify` dependency). It sends an OSC desktop
@@ -171,6 +196,7 @@ composed agents and token-substituted skills into pi's user scope:
171
196
  agent-cortex install pi
172
197
  # → ~/.pi/agent/agents/<name>.agent.md (ralph, plan, ralph-plan, strategy)
173
198
  # → ~/.pi/agent/skills/ (token-substituted skill tree)
199
+ # → ~/.pi/agent/settings.json + keybindings.json (merged from the repo templates)
174
200
  ```
175
201
 
176
202
  Flags:
@@ -178,8 +204,9 @@ Flags:
178
204
  | Flag | Meaning |
179
205
  | --- | --- |
180
206
  | `--dry-run` | Show what would be installed without writing anything |
181
- | `--output <dir>` | Install into `<dir>/agents` and `<dir>/skills` (default `~/.pi/agent`) |
207
+ | `--output <dir>` | Install into `<dir>/agents`, `<dir>/skills` and the config files (default `~/.pi/agent`) |
182
208
  | `--plugin-root <dir>` | Override the plugin root used for `{{PATH:...}}` tokens (default: token-map.json's pi value — use it for checkout or symlinked installs) |
209
+ | `--no-provision` | Skip provisioning the third-party pi packages declared in `package.json` `pi.packages` (default: install them via the `pi` CLI) |
183
210
 
184
211
  Re-run whenever you pull changes (`git pull` + reinstall, or after `pnpm build:copilot`).
185
212
 
@@ -202,9 +229,13 @@ root `~/.agent-cortex/claude` — **4 agents** (`strategy`, `plan`, `ralph-plan`
202
229
  **29 skills** (copied flat per skill, `{{TOOL:...}}` / `{{PATH:...}}` token-substituted — no
203
230
  symlinks), `SessionStart` + `Notification` hooks, and 2 MCP servers — writes a marketplace
204
231
  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.
232
+ registers it with Claude Code, and merges the committed `claude/settings.json` template into
233
+ `~/.claude/settings.json`: the template supplies defaults, personal values win, and the
234
+ installer owns exactly `enabledPlugins` + `extraKnownMarketplaces` — every other key
235
+ (`permissions`, `hooks`, `env`, `statusLine`, unknown keys) is preserved. The only committed
236
+ Claude config is that template plus the hand-authored extras in `claude-extras/`
237
+ (`.mcp.json`, `scripts/`); the plugin subtree is generated at install time — there is nothing
238
+ to drift.
208
239
 
209
240
  The generate-only `--output <dir>` form is for previewing and CI validation; the
210
241
  documented path is the plain install to `~/.agent-cortex/claude`:
@@ -258,8 +289,16 @@ agent-cortex install claude --require-register # register or fail the inst
258
289
  ```
259
290
 
260
291
  `--dry-run` prints the full plan without spawning the claude CLI or writing anything;
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
292
+ `--output <dir>` generates only, with no marketplace manifest and no registration.
293
+
294
+ The install also merges the committed `claude/settings.json` template into
295
+ `~/.claude/settings.json` (the settings file Claude Code reads at user scope). The template
296
+ supplies defaults and personal values win; the installer owns exactly `enabledPlugins` +
297
+ `extraKnownMarketplaces`, so `permissions`, `hooks`, `env`, `statusLine` and unknown keys are
298
+ never touched. A legacy symlinked `settings.json` is replaced with a real file rather than
299
+ written through, and `--output` leaves the user's Claude config alone.
300
+
301
+ The equivalent manual registration adds a marketplace root by **absolute path** (a bare `.` is
263
302
  rejected) — the home install root (`~/.agent-cortex`) is the marketplace root written by
264
303
  the plain install; the repo checkout ships no manifest:
265
304
 
@@ -337,7 +376,7 @@ extension→hook audit.
337
376
  #### Not ported / follow-ups
338
377
 
339
378
  - Only `session-start` and `notify` had Claude equivalents (both ported to hooks); the other
340
- pi `extensions/` (auto-name, skill-stats, subagent, agent-modes) have none — the audit and
379
+ pi `extensions/` (auto-name, agent-guardrails, skill-stats, subagent, agent-modes) have none — the audit and
341
380
  rejection rationale live in `docs/claude-hooks.md`.
342
381
  - Ralph follow-ups: multi-feature epic branches, and a GitHub-trigger routine to auto-resume
343
382
  after a PR merge (instead of manual re-invocation).
@@ -350,12 +389,13 @@ Edit the **sources** — the composable `agents/<name>/` directories (shared `ag
350
389
  per-harness frontmatter/sections, auto-composed by `scripts/build-copilot-agents.mjs` and
351
390
  `bin/installers/claude.mjs`), `agents-native/*.md` (Claude-only
352
391
  agents like `ralph` — the canonical bodies the installer copies verbatim), `skills/**`,
353
- `hooks/claude/hooks.json` (hook config), `claude-extras/` (hand-authored `.mcp.json` and
392
+ `hooks/claude/hooks.json` (hook config), `claude/settings.json` (the user-settings template
393
+ merged into `~/.claude/settings.json`), `claude-extras/` (hand-authored `.mcp.json` and
354
394
  `scripts/`), and `package.json` (plugin
355
395
  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
396
+ `agent-cortex install claude` (re-materialises the home plugin, merges settings, and
397
+ re-registers it; `--output <dir>` generates a preview only). The only committed file under
398
+ `claude/` is the settings template; `~/.agent-cortex/claude` is generated in full
359
399
  (`.claude-plugin/plugin.json`, `agents/`, `skills/`, `hooks.json`, `.mcp.json`, `scripts/`)
360
400
  so never edit anything under it. The generated `agents/*.agent.md` files are still
361
401
  drift-checked by CI (`git diff --exit-code -- 'agents/*.agent.md'`), and CI validates the
package/agents/README.md CHANGED
@@ -222,9 +222,11 @@ flat `agents/*.agent.md` files are built from them:
222
222
  Never edit the generated flat materialised files by hand — edit the composable directory.
223
223
  Generation is install-time: `claude` ships
224
224
  via `bin/installers/claude.mjs`, the generator behind plain `agent-cortex install claude`
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
225
+ (which materialises into `~/.agent-cortex/claude`, merges the committed
226
+ `claude/settings.json` into `~/.claude/settings.json`, and registers the plugin with Claude
227
+ Code); the only committed `claude/` file is that settings template (hand-authored extras live
228
+ in `claude-extras/`), and CI validates the materialiser with structural checks on a temp-dir
229
+ install; `copilot` ships via
228
230
  `bin/installers/copilot.mjs`, the shared code path behind both `agent-cortex install
229
231
  copilot` and `scripts/build-copilot-agents.mjs`; `pi` ships via
230
232
  `bin/installers/pi.mjs`, which materialises composed agents + token-substituted skills
@@ -32,6 +32,10 @@ if (parsed.command === "install") {
32
32
  try {
33
33
  const result = await installPi({
34
34
  dryRun: parsed.dryRun ?? false,
35
+ // Third-party pi packages are only provisioned for a real install:
36
+ // --output is the generate-only form, --no-provision opts an offline
37
+ // machine out explicitly.
38
+ provisionPackages: parsed.output === undefined && !parsed.noProvision,
35
39
  warn: () => {}, // warnings surface once in the printed summary
36
40
  ...(parsed.output ? { output: parsed.output } : {}),
37
41
  ...(parsed.pluginRoot ? { pluginRoot: parsed.pluginRoot } : {}),
@@ -49,10 +53,12 @@ if (parsed.command === "install") {
49
53
  // claude is the first wired harness: a plain `agent-cortex install claude`
50
54
  // materialises the plugin into the home install root (~/.agent-cortex/claude)
51
55
  // 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
+ // ~/.agent-cortex/.claude-plugin/marketplace.json, merges the committed
57
+ // claude/settings.json template into ~/.claude/settings.json (owning only
58
+ // enabledPlugins + extraKnownMarketplaces), and registers it with Claude Code
59
+ // (marketplace add → install → update); `--output <dir>` is the generate-only
60
+ // form (tests/CI validation), and the generated claude/ plugin subtree is
61
+ // never committed — only the settings template is.
56
62
  if (parsed.harness === "claude") {
57
63
  // Avoid loading the installer on the help/summary paths and for other harnesses.
58
64
  const { installClaude, registerClaude } = await import("./installers/claude.mjs");
@@ -106,10 +112,36 @@ function printPiInstall(result) {
106
112
  process.stdout.write(` ✓ ${agent.name}.agent.md → ${agent.filePath}\n`);
107
113
  }
108
114
  process.stdout.write(` ✓ Substituted ${result.skills.md} markdown file(s) across ${result.skills.skills} skill(s) → ${result.skills.dir}\n`);
115
+ printManagedConfig("settings.json", result.settings);
116
+ printManagedConfig("keybindings.json", result.keybindings);
117
+ // A dry run installs nothing, so report the plan — otherwise the documented
118
+ // "show what would be installed" contract prints no packages at all.
119
+ const packages = result.packages ?? { planned: [], installed: [] };
120
+ for (const source of result.dryRun ? packages.planned : packages.installed) {
121
+ process.stdout.write(` ✓ ${result.dryRun ? "Would install" : "Installed"} pi package ${source}\n`);
122
+ }
109
123
  for (const warning of result.warnings) {
110
124
  process.stdout.write(` ⚠ ${warning}\n`);
111
125
  }
112
126
  if (result.dryRun) {
113
127
  process.stdout.write("(dry-run — nothing written)\n");
114
128
  }
129
+ }
130
+
131
+ /** Report one managed pi config file (settings.json / keybindings.json). */
132
+ function printManagedConfig(label, info) {
133
+ if (!info) return;
134
+ if (info.action === "would-remove-symlink") {
135
+ process.stdout.write(` → would remove symlink ${info.path} and write ${label}\n`);
136
+ } else if (info.action === "would-write") {
137
+ process.stdout.write(` → would write ${label} → ${info.path}\n`);
138
+ } else if (info.action === "unchanged") {
139
+ process.stdout.write(` · ${label} → ${info.path} (unchanged)\n`);
140
+ } else if (info.action === "skipped") {
141
+ process.stdout.write(` ⚠ ${label} modified after install — left untouched → ${info.path}\n`);
142
+ } else if (info.action === "removed-symlink") {
143
+ process.stdout.write(` ✓ ${label} → ${info.path} (removed legacy symlink)\n`);
144
+ } else {
145
+ process.stdout.write(` ✓ ${label} → ${info.path}\n`);
146
+ }
115
147
  }