@jaybeeuu/agent-cortex 1.40.0 → 1.41.1

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 (74) hide show
  1. package/AGENTS.md +21 -8
  2. package/CHANGELOG.md +85 -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/REFERENCE.md +1 -1
  56. package/skills/planning/create-task/SKILL.md +20 -11
  57. package/skills/planning/create-task/scripts/create-chores.ts +4 -0
  58. package/skills/planning/write-a-prd/SKILL.md +2 -2
  59. package/skills/style/style-code/REFERENCE.md +32 -2
  60. package/skills/style/style-code/SKILL.md +9 -21
  61. package/skills/style/style-tests/EXAMPLES.md +548 -0
  62. package/skills/style/style-tests/SKILL.md +84 -33
  63. package/skills/workflow/run-pipeline-stage/SKILL.md +13 -1
  64. package/skills/workflow/run-pipeline-stage/prompts/stage-runner.md +12 -0
  65. package/test/classify-bead.test.mjs +277 -0
  66. package/test/cli.test.mjs +55 -11
  67. package/test/create-chores.test.mjs +73 -0
  68. package/test/fs.test.mjs +76 -0
  69. package/test/helpers/fake-bd.mjs +67 -0
  70. package/test/installer.test.mjs +237 -4
  71. package/test/pi-installer.test.mjs +343 -4
  72. package/test/pi-packages.test.mjs +295 -0
  73. package/token-map.README.md +4 -2
  74. package/token-map.json +3 -3
@@ -0,0 +1,68 @@
1
+ # Idea: Test-planning stage bead in feature planning
2
+
3
+ ## Status
4
+ Absorbed into bead `agnt-ctx-rig0` (2026-09-22) — the verification beads in that design's
5
+ design stage generalise this idea's provable-outcome / completion-criterion motivation. Not a
6
+ standalone bead.
7
+
8
+ ## Created
9
+ 2026-09-03
10
+
11
+ ## Problem
12
+ Tests get under-planned: features are split into beads around implementation
13
+ (code, verify, review) but nobody plans *what* needs testing or *how we know
14
+ it's done* before the work starts. Verification ends up ad hoc — a verify
15
+ stage runs whatever tests exist rather than the tests the feature actually
16
+ needs.
17
+
18
+ ## Who benefits
19
+ - The agent running the pipeline (ralph / run-pipeline-stage) — has an explicit
20
+ test plan to execute against instead of improvising a verify pass.
21
+ - The human reviewer — a stated definition of done for testing makes the PR
22
+ gate checkable ("did the planned tests land?").
23
+ - The feature itself — testability becomes a planning output, not an accident.
24
+
25
+ ## Proposed outcome
26
+ When a feature is planned, the planning phase also produces a
27
+ test/verification planning stage bead: what behaviour to test, at what level
28
+ (unit/integration/e2e), key cases and edge cases, and an explicit completion
29
+ criterion — the answer to "how do we know it's done?" The bead sits between
30
+ feature planning and implementation, so test planning happens once at plan
31
+ time rather than being rediscovered per-task.
32
+
33
+ ## Validity check
34
+ - Evidence we already have: verify-stage chores exist today but run generic
35
+ checks (`playbooks/verify.md`, `run-pipeline-stage`); nothing at plan time
36
+ defines the feature's test surface or a testing definition of done — PR
37
+ reviews have to reconstruct what should have been tested.
38
+ - Riskiest assumption: a test-plan bead is actually useful to the executing
39
+ agent and isn't just ceremony that duplicates what the code bead already
40
+ implies ("write tests that cover this feature"). Cheap to validate: try it
41
+ on one feature and judge whether the verify stage runs a materially better
42
+ test pass.
43
+ - What would invalidate this idea: if RALPH's verify stage already produces
44
+ adequate coverage decisions from the playbook alone, the extra bead adds
45
+ overhead without changing outcomes.
46
+
47
+ ## Constraints
48
+ - Pipeline shape is fixed by the existing bead/pipeline contract
49
+ (create-task chores, ralph stage dispatch) — the new bead type must slot in
50
+ without a pipeline redesign.
51
+ - No new runtime for test execution — the bead plans; existing verify tooling
52
+ executes.
53
+ - Must stay lightweight: test planning for its own sake is the failure mode.
54
+
55
+ ## Next validation step
56
+ Run a two-feature pilot: for the next two planned features, hand-write a
57
+ test/verification plan bead (without wiring it into the pipeline) and compare
58
+ the resulting verify passes against a control feature planned the current
59
+ way. If the planned features demonstrably get better test coverage, wire the
60
+ bead type into the planning skill.
61
+
62
+ ## Notes
63
+ - User framing: "planning, then test/verification planning — how do we know
64
+ it's done?" The completion criterion is the heart of the idea, not the bead
65
+ itself.
66
+ - Priority: medium — next planning-skill iteration, not blocking current work.
67
+ - The definition-of-done answer could eventually feed the HITL PR gate
68
+ (reviewer checks planned-tests-vs-landed-tests).
@@ -0,0 +1,39 @@
1
+ # Idea: Worktree housekeeping for closed beads
2
+
3
+ ## Status
4
+ Backlog idea (not implementation-ready)
5
+
6
+ ## Created
7
+ 2026-09-13
8
+
9
+ ## Problem
10
+ Ralph runs each bead in a dedicated worktree under `.agent-cortex/worktrees/<parent-id>` and only cleans up when a human manually removes it — the `git-workflow` skill's "Worktree lifecycle" step is discretionary, not enforced. Closed, merged beads leave their worktrees behind, and crashed or abandoned runs leave orphaned ones. The worktree set drifts from the actual bead/PR state with no mechanism to converge it.
11
+
12
+ ## Who benefits
13
+ - The author, who currently accumulates stale worktrees and must identify and remove them by hand.
14
+ - Future ralph/agent sessions, which skip reading a worktree set polluted by dead state and avoid accidentally reusing an abandoned worktree for a new bead.
15
+ - Confident teardown: knowing a merged feature's worktree is gone makes "is this still live?" checks unnecessary.
16
+
17
+ ## Proposed outcome
18
+ Two tiers of cleanup:
19
+ - **Minor (deterministic)**: when a ralph run's PRs merge and its beads close, the associated worktrees are removed as part of the close-off — no LLM judgement needed.
20
+ - **Major (broader)**: a reusable housekeeping pass that finds and reports orphaned or abandoned worktrees (no corresponding live bead, no open PR / stale branch), usable on demand.
21
+
22
+ Cleanup is safe-first: it must not remove a worktree whose branch or bead is still active, and unmerged/abandoned worktrees should be surfaced rather than silently deleted.
23
+
24
+ ## Validity check
25
+ - Evidence we already have: `git-workflow` SKILL.md already documents "after the PR is merged or closed, clean up the worktree" as a manual step — the intent exists, only the enforcement is missing. Ralph already knows the full mapping of bead → worktree → PR, so it has all the data a deterministic cleanup needs.
26
+ - Riskiest assumption: worktree state can be derived reliably from bead state + git state (that a "closed bead" always means its worktree is safe to remove, and that orphan detection can distinguish abandoned worktrees from merely idle ones). If the bead/PR mapping is lossy — e.g. beads close without recording whether the PR merged — the deterministic rule could remove an unmerged feature's worktree.
27
+ - What would invalidate this idea: if the cost of a false positive cleanup (losing an unmerged feature's worktree or its local branches) outweighs the cost of the current manual drift; or if ralph's lifecycle already converges worktrees in practice and the observed accumulation is negligible.
28
+
29
+ ## Constraints
30
+ - Human merge gates stay non-negotiable — cleanup must never imply or require auto-merging.
31
+ - No destructive action on anything the mapping can't positively classify (unmerged features, active beads, open PRs) — default to reporting.
32
+ - Both the skill and the deterministic script live in this repo (`skills/`, `scripts/`); final split between skill, script, or both is a design-time decision.
33
+
34
+ ## Next validation step
35
+ List worktrees across a few real (recently completed) ralph sessions and compare against bead/PR state to measure actual accumulation and test whether closed-bead → safe-to-remove always holds. This both quantifies the problem and de-risks the riskiest assumption.
36
+
37
+ ## Notes
38
+ - Interview left two design-time decisions open: (1) whether cleanup lives as a skill, a script, or both; (2) whether it runs at session start, inside the ralph loop, or as an on-demand command — "maybe both" was the lean.
39
+ - Related backlog neighbours worth reconciling when this is planned: `deployment-beads-for-post-merge-pipelines.md` (post-merge lifecycle), `record-ideas-as-beads.md` if the major housekeeping pass becomes a scheduled bead.
@@ -0,0 +1,161 @@
1
+ # Technical Direction: Harness config and third-party extensions
2
+
3
+ ## Problem and target outcome
4
+
5
+ - **What we are solving.** agent-cortex ships agents and skills to three harnesses (pi, Claude
6
+ Code, Copilot CLI) and needs a declared, reproducible set of *third-party* extensions those
7
+ agents depend on (pi packages / Claude plugins). Two bespoke mechanisms currently exist and
8
+ disagree: `main` declares packages in `package.json` → `pi.packages` and provisions them from
9
+ `lib/pi-packages.mjs`; the unmerged pi-harness epic (`agnt-ctx-dfq3`) declares them in
10
+ `pi.extensions.json` / `claude.extensions.json` and provisions them via `agent-cortex ext
11
+ install`. **Neither file is read by any harness** — both are agent-cortex inventions.
12
+ - **Target outcome.** One idiomatic mechanism per harness: extensions the agents *require* are
13
+ resolved by the harness's native dependency field; extensions that are *optional* are declared
14
+ in the harness's committed settings template. No bespoke manifest format survives.
15
+ - **Why now.** The epic is unmerged and diverging from `main` (7 commits behind), and PR #142
16
+ (`ext prune`) was closed as redundant with the harnesses' own package management. Keeping both
17
+ models means keeping the drift the original idea set out to remove.
18
+
19
+ ## Current-state constraints
20
+
21
+ - **Public package.** `publishConfig.access: public`, npm `@jaybeeuu/agent-cortex@1.40.0` — every
22
+ bundled dependency or committed template affects anyone who installs the package, not just the
23
+ author.
24
+ - **pi is idiomatic two ways.** Machine state is `~/.pi/agent/settings.json` → `packages[]`, owned
25
+ by `pi install/remove/list`. A package's own resource declarations live in `package.json` → `pi`
26
+ (`extensions`, `skills`, `prompts`, `themes`); third-party pi packages are depended on via
27
+ `dependencies` + `bundledDependencies` and referenced through `node_modules/…`. **There is no
28
+ `pi.packages` field** — pi ignores it.
29
+ - **Claude is idiomatic two ways.** Machine state is `~/.claude/settings.json` → `enabledPlugins`
30
+ + `extraKnownMarketplaces`. A plugin declares other plugins in `.claude-plugin/plugin.json` →
31
+ `dependencies`, which Claude Code resolves and auto-installs.
32
+ - **Copilot has no dependency mechanism.** `copilot plugin install/list/uninstall` exists, but its
33
+ state is auto-managed `~/.copilot/config.json` (not committable) and `plugin.json` declares no
34
+ dependencies.
35
+ - **The epic already built two-thirds of the target.** Unmerged commit `afab2c6` (F3) materialises
36
+ `pi/settings.json` + `pi/keybindings.json` from committed templates with merge-keep-personal and
37
+ the CLI owning `packages`; `1ff212d` (F4) makes bundled pi extensions load via package
38
+ registration. Only `1e0e970` (F5, the bespoke manifests) is off-direction.
39
+ - **Unknowns resolved during this memo:** pi bundling is feasible (see Validation plan); Claude
40
+ dependency auto-install is real but cross-marketplace needs an allowlist; Claude currently
41
+ declares **zero** third-party plugins.
42
+
43
+ ## Options considered
44
+
45
+ ### Option A: one bespoke config per harness
46
+
47
+ - **Approach.** A `config/<harness>.json` (or the epic's `*.extensions.json`) holds settings and
48
+ the extension list; the CLI provisions from it.
49
+ - **Pros.** Uniform across harnesses; full control; explicit single place to look.
50
+ - **Cons.** A third format that neither harness reads; duplicates the native `packages` /
51
+ `enabledPlugins` fields; re-creates the manifest-vs-machine drift the idea exists to remove.
52
+ - **Risks.** The redundancy that made `ext prune` (F6) not worth building.
53
+
54
+ ### Option B: native dependency fields + committed settings templates *(recommended)*
55
+
56
+ - **Approach.** Required extensions → `dependencies` + `bundledDependencies` (pi) and
57
+ `.claude-plugin/plugin.json` `dependencies` (Claude). Optional extensions → the harness's own
58
+ committed settings template (`pi/settings.json` `packages`; `claude/settings.json`
59
+ `enabledPlugins` + `extraKnownMarketplaces`), materialised with merge-keep-personal. Drop the
60
+ bespoke manifests, the `ext install`/`ext prune` family, and the separate diff command —
61
+ `agent-cortex install <harness>` reconciles.
62
+ - **Pros.** Idiomatic; no bespoke format; each harness resolves its own required deps; the
63
+ optional list lives in the file the harness already reads, so `pi list` / `claude plugin list
64
+ --json` are the exact machine-state comparison; drift self-heals on install.
65
+ - **Cons.** pi bundling shifts update cadence to agent-cortex releases and grows the tarball;
66
+ Claude cross-marketplace deps need an allowlist; the committed template also carries personal
67
+ settings; Copilot gets nothing.
68
+ - **Risks.** Loading TS extensions from bundled `node_modules/`; maintaining the Claude
69
+ cross-marketplace allowlist.
70
+
71
+ ### Option C: fully delegate to the harnesses
72
+
73
+ - **Approach.** pi bundles its deps, Claude auto-installs plugin deps, `install` only registers;
74
+ there is no optional layer at all.
75
+ - **Pros.** Least code; no declaration surface to maintain.
76
+ - **Cons.** No way to declare the optional/opt-in personal toolset; the original problem —
77
+ "the toolset isn't reproducible or documented" — goes unsolved for everything not strictly
78
+ required.
79
+ - **Risks.** Silently drops the reproducibility goal.
80
+
81
+ ## Recommendation
82
+
83
+ **Option B.** It is the only option that satisfies both halves of the problem: *required*
84
+ extensions resolve through the harness's own mechanism (so an agent's tools can't be missing),
85
+ and *optional* ones stay declared and reproducible without inventing a format the harnesses
86
+ ignore. It also deletes the most code — the two competing bespoke models, `lib/pi-packages.mjs`,
87
+ and the `ext` command family — rather than adding a third.
88
+
89
+ Alternatives were not chosen because: **A** keeps a third format and the drift it causes; **C**
90
+ abandons the declared-toolset goal that started the work.
91
+
92
+ **Concrete disposition:** salvage the epic's F3 (`afab2c6`) and F4 (`1ff212d`) — they *are* the
93
+ settings-template and bundled-dependency mechanisms — drop F5/F6 (`1e0e970` and PR #142), close
94
+ the epic, and delete `pi.packages` + `lib/pi-packages.mjs` from `main`.
95
+
96
+ ## Tradeoffs accepted
97
+
98
+ - **pi bundling changes update cadence.** Bundled pi packages are pinned by agent-cortex's
99
+ `package.json` ranges and update when agent-cortex releases, not via `pi update`. Accepted: it
100
+ makes the required toolset a property of agent-cortex rather than of the machine.
101
+ - **Tarball size.** `pi-web-access` pulls 5 runtime deps (readability, linkedom, p-limit, turndown,
102
+ unpdf). Accepted for the required set; the optional set stays provisioned, not bundled.
103
+ - **Claude cross-marketplace allowlist.** If a required Claude plugin lives in another
104
+ marketplace, agent-cortex's `marketplace.json` must list it in
105
+ `allowCrossMarketplaceDependenciesOn`. Accepted; currently moot (zero declared plugins).
106
+ - **Committed personal settings.** `pi/settings.json` already carries models/theme; the template
107
+ model makes the repo own the whole pi settings surface. Accepted; F3's merge preserves personal
108
+ keys and only `packages` is CLI-owned.
109
+ - **Copilot gets no mechanism.** Documented exception — the README says `copilot plugin install`.
110
+ Accepted because Copilot has no dependency field and no committable config.
111
+ - **No dedicated diff command.** A read-only `status` is deferred until drift is shown to hurt.
112
+
113
+ ## Validation plan
114
+
115
+ Run before implementation (three already run during this memo):
116
+
117
+ 1. **Can `pi-questions` / `pi-web-access` be bundled and load from `node_modules/`?** ✅ *Ran.*
118
+ `pi-questions@0.3.4` is a single extension file with no deps or install-time state;
119
+ `pi-web-access@0.10.7` is an extension + skills with 5 runtime deps. Both are conventional pi
120
+ packages — bundling is feasible. Success signal: a scratch package depending on both loads
121
+ `ask_questions` / `fetch_content` in a fresh pi session.
122
+ 2. **Does Claude's `plugin.json` `dependencies` auto-install from agent-cortex's marketplace?**
123
+ ✅ *Ran.* Yes, but cross-marketplace deps require `allowCrossMarketplaceDependenciesOn` in the
124
+ root `marketplace.json`, and version constraints need `{plugin-name}--v{version}` git tags.
125
+ 3. **Does F3 reconcile `packages` when the template drops one?** ✅ *Ran.* Yes — the CLI owns
126
+ `packages` outright, so removing an entry from the template removes it on next install. The
127
+ documented limitation is the reverse (ad-hoc `pi install` entries don't persist), which is
128
+ intended.
129
+
130
+ Still to run before shipping: a scratch end-to-end install on a clean `HOME` proving a required
131
+ pi extension loads from `node_modules/` and an optional one is materialised from the template.
132
+
133
+ ## Revisit triggers
134
+
135
+ - pi or Claude ship a first-class *optional* dependency field, making the settings template
136
+ unnecessary.
137
+ - Claude still declares zero third-party plugins after the migration — then Claude parity is
138
+ speculative and should be dropped rather than maintained.
139
+ - Bundling `pi-web-access`'s native-ish dependency tree (unpdf, linkedom) proves brittle or
140
+ unacceptable on a fresh machine.
141
+ - Copilot gains a committable config or a plugin dependency field — revisit the documented
142
+ exception.
143
+ - Drift between the committed template and the machine becomes a recurring annoyance — add the
144
+ deferred read-only `status`.
145
+
146
+ ## References
147
+
148
+ - pi docs, *Settings* and *Packages* (installed at
149
+ `…/@earendil-works/pi-coding-agent@0.85.1/docs/settings.md`, `…/docs/packages.md`): `packages`
150
+ array is the machine store; the `pi` manifest keys are `extensions`/`skills`/`prompts`/`themes`;
151
+ other pi packages are bundled and referenced through `node_modules/`.
152
+ - Claude Code, *Constrain plugin dependency versions* — https://code.claude.com/docs/en/plugin-dependencies
153
+ (auto-install, cross-marketplace allowlist, tag naming).
154
+ - Claude Code, *Settings* — https://code.claude.com/docs/en/settings (`enabledPlugins`,
155
+ `extraKnownMarketplaces`).
156
+ - GitHub Copilot CLI, *Plugin reference* — https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-plugin-reference
157
+ and *Configuration directory* — https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-config-dir-reference
158
+ - Code evidence: `package.json` (`pi.packages`), `lib/pi-packages.mjs` (main's provisioner);
159
+ `pi.extensions.json`, `claude.extensions.json`, `lib/extension-manifest.mjs` (epic F5, `1e0e970`);
160
+ `bin/installers/pi.mjs` settings materialisation (epic F3, `afab2c6`);
161
+ `docs/ideas/extension-manifests.md` (the originating idea).
@@ -1,11 +1,15 @@
1
- # Technical Direction: Execution-Label Migration Tooling (two-axis model)
1
+ # Technical Direction: Migration Tooling (two-axis model)
2
2
 
3
3
  ## Problem and target outcome
4
4
 
5
5
  - Migrate all beads workspaces from `implementation-type:afk|hitl` to `execution:afk|hitl`
6
6
  (strict rename, agent-agnostic) as part of the two-axis workflow model epic.
7
- - Provide a repo-agnostic tool + skill any workspace can run, and a single-package.json
8
- script tooling story across the agent-cortex repo.
7
+ - Migrate loose idea markdown files (`docs/ideas/*.md`) to first-class `kind:idea` beads
8
+ in any workspace.
9
+ - Both migrations are **steps in one versioned migration command** — `agent-cortex migrate`
10
+ (aliased `ctx migrate`), which detects a repo's current migration version and applies
11
+ ordered steps up to the latest. Repo-agnostic tool + skill any workspace can run, and a
12
+ single-package.json script tooling story across the agent-cortex repo.
9
13
 
10
14
  ## Current-state constraints
11
15
 
@@ -22,6 +26,8 @@
22
26
  — they remain workspace members with their own package.json.
23
27
  - `node_modules` gitignored; per-skill modules are installed manually today — a fragility in
24
28
  fresh clones and PI-installed contexts.
29
+ - The CLI (`bin/agent-cortex.mjs` + `lib/cli.mjs`) is already zero-dep stdlib-only and is the
30
+ natural home for a migrate command; it currently wires only `install <harness>`.
25
31
 
26
32
  ## Decisions (locked in planning session)
27
33
 
@@ -45,28 +51,40 @@
45
51
  - Fresh-clone/installed-context verification: `pnpm install` at root then
46
52
  `pnpm -r typecheck` + `pnpm -r test` (or root `node --test` wiring) cover all scripts.
47
53
 
48
- ### D3: Migration tool shape
49
-
50
- - Script: `skills/productivity/migration/scripts/rename-execution-labels.mjs` — zero-dep,
51
- `// @ts-check` + JSDoc.
52
- - Behavior:
53
- 1. Enumerate: `bd list --label-pattern 'implementation-type:*' --json`.
54
- 2. Group bead ids by label value (afk | hitl). A bead carrying both gets both execution labels.
55
- 3. Dry-run (default): print per-bead plan + summary counts; zero writes; exit 0.
56
- 4. Apply (`--apply`): batched `bd label add execution:<v> <ids...>` then
57
- `bd label remove implementation-type:<v> <ids...>` per group.
58
- 5. Idempotent: re-run finds zero targets → no-op.
59
- 6. Fail loudly if `bd` unavailable or any subcommand errors.
60
- - Scope: rename ALL beads carrying `implementation-type:*` (including closed) — the F7
61
- acceptance "zero implementation-type:* labels remain" requires it, and bd label history
62
- tolerates it.
54
+ ### D3: Migration command — versioned registry in the CLI
55
+
56
+ - Command: `agent-cortex migrate` (alias `ctx migrate`), wired into `bin/agent-cortex.mjs`
57
+ + `lib/cli.mjs` (parseArgs + buildHelpText), zero-dep stdlib-only like the rest of the CLI.
58
+ - A **versioned migration registry**: each step declares the version it migrates to.
59
+ `migrate` detects the current version of the target repo's state and applies each step in
60
+ order up to the latest. Steps:
61
+ 1. **v1: label rename** (F1) — `implementation-type:afk|hitl` → `execution:afk|hitl`:
62
+ `bd list --label-pattern 'implementation-type:*' --json`; group ids by label value
63
+ (a bead with both gets both); dry-run default (per-bead plan + summary, zero writes,
64
+ exit 0); `--apply` batched `bd label add execution:<v> <ids...>` then
65
+ `bd label remove implementation-type:<v> <ids...>` per group; idempotent re-run
66
+ (zero targets → no-op); fail loudly if `bd` missing or a subcommand errors.
67
+ Scope: ALL beads including closed (F7 acceptance "zero implementation-type:* labels
68
+ remain" requires it; bd label history tolerates it).
69
+ 2. **v2: ideas → beads** (F2) — `docs/ideas/*.md` (default, `--dir` override) →
70
+ `kind:idea` beads: map template sections to bead fields (Problem→--description,
71
+ How→--design, When/Priority/Validity/Constraints/Notes→--notes, referential→--context),
72
+ `bd create -l kind:idea -p <preserved priority, default P4>`; preserve content
73
+ fidelity (keep section markers inside fields); dry-run; safe deletion order
74
+ (docs/ideas/ + new-idea.sh only after F3 lands); `record-ideas-as-beads.md` migrates
75
+ itself last.
76
+ - Shared plumbing: preflight (detect current version), `--dry-run` (default), `--apply`,
77
+ per-step verification, and a final summary. Do NOT run v1 against agent-cortex's own
78
+ .beads — the repo cutover is feature F7 (landed after skill updates so ralph never
79
+ deadlocks).
63
80
 
64
81
  ### D4: Migration skill
65
82
 
66
83
  - Location: `skills/productivity/migration/SKILL.md` (productivity = workflow tooling).
67
- - Docs the full sequence for any workspace: preflight (count via `bd label list-all`),
68
- dry-run, apply, post-checks (grep stale refs; `bd ready -l execution:afk` smoke), plus
69
- pointers to idea-file migration (F2) and skill/doc updates (F3/F4/F5/F6).
84
+ - Docs the full sequence for any workspace: preflight (current-version detection),
85
+ `agent-cortex migrate` dry-run, apply, post-checks (grep stale refs; `bd ready -l
86
+ execution:afk` smoke), plus pointers to idea-file migration (F2) and skill/doc updates
87
+ (F3/F4/F5/F6).
70
88
  - Repo-agnostic: no agent-cortex-specific paths in the runnable steps.
71
89
 
72
90
  ### D6: npm packaging hygiene (no build step — F9)
@@ -106,17 +124,31 @@
106
124
  - Cons: 3 orphaned mini-workspaces, manual install fragility, resolution doc dance
107
125
  (`<skill-scripts>/node_modules/.bin/tsx`), contradicts single-package.json preference.
108
126
 
127
+ ### Option D: standalone one-off migration script (original D3, superseded)
128
+ - Was: `skills/productivity/migration/scripts/rename-execution-labels.mjs` as a standalone
129
+ script with its own skill. Superseded by the unified `migrate` command: the label rename
130
+ is migration step v1 inside the same versioned registry as the idea-file migration (F2),
131
+ sharing preflight/dry-run/verify plumbing. Rationale (user directive, 2026-09-13): the
132
+ target is a migration *command* that converts any given repo from the current version to
133
+ the latest — one surface, not N ad-hoc scripts; also aligns with the planned `ctx`
134
+ alias/version/update surface (docs/ideas/ctx-alias-version-and-update-commands.md).
135
+
109
136
  ## Tradeoffs accepted
110
137
 
111
138
  - Scripts use JSDoc annotations where types matter; `tsc --noEmit` with `checkJs` keeps a
112
139
  typecheck gate at root.
113
140
  - Closed beads get their history labels renamed — accepted for a clean label space.
141
+ - migrate command grows the CLI surface beyond `install`; kept small (one register of
142
+ versioned steps) to avoid a second mechanism drifting from bd's own state.
114
143
 
115
144
  ## Validation plan
116
145
 
117
146
  - F1-T3: scratch beads workspace (temp dir): create test beads with both labels + a dep pair,
118
147
  dry-run (assert zero writes), apply, verify rename, re-run idempotence, `bd ready -l
119
148
  execution:afk` smoke, then run the migration skill end-to-end once.
149
+ - F2-T1: scratch repo with several idea `.md` files: `agent-cortex migrate` v2 dry-run
150
+ (zero writes), apply, verify kind:idea beads with content fidelity + priorities, delete
151
+ file flow, re-run idempotence.
120
152
  - F8-T3: fresh clone (or temp copy) + `pnpm install` at root; `pnpm -r typecheck` and
121
153
  `pnpm -r test` green; scripts execute via bare `node`.
122
154
 
@@ -125,8 +157,13 @@
125
157
  - A future script needs third-party deps at runtime → revisit D1 (either root dep or
126
158
  per-package re-introduction).
127
159
  - New extension with conflicting devDeps → revisit D2 membership rules.
160
+ - A third migration step appears → extend the registry in place; if steps start sharing
161
+ little plumbing, revisit the unified-command decision (Option D).
128
162
 
129
163
  ## References
130
164
 
131
- - Code evidence: `skills/*/scripts/package.json` (3 orphaned), root `package.json` +
132
- `pnpm-workspace.yaml`, bd CLI help (`bd list`, `bd label`), epic `agnt-ctx-s5xd` notes.
165
+ - Code evidence: `bin/agent-cortex.mjs` + `lib/cli.mjs` (CLI surface), `skills/*/scripts/
166
+ package.json` (3 orphaned), root `package.json` + `pnpm-workspace.yaml`, bd CLI help
167
+ (`bd list`, `bd label`, `bd create`), epic `agnt-ctx-s5xd` notes.
168
+ - Idea file: `docs/ideas/ctx-alias-version-and-update-commands.md` (ctx alias + version/update
169
+ surface that `migrate` joins).
@@ -0,0 +1,45 @@
1
+ # Agent Guardrails
2
+
3
+ Appends behavioural circuit-breakers to the PI system prompt once per user prompt.
4
+
5
+ This is the first slice of `docs/ideas/improve-pi-system-prompt.md`: the highest-leverage
6
+ token reduction is behavioural, not structural. The extension injects a stable block via
7
+ the `before_agent_start` hook — which fires once per user prompt, before the agent loop — so
8
+ the rules apply to every turn of the run without rewriting Pi's default prompt.
9
+
10
+ ## Rules
11
+
12
+ - **Circuit-breakers** — two strikes then stop; never repeat a tool call without a stated
13
+ rationale; check in when a cycle produces no progress.
14
+ - **Check-in triggers** — stop and present options when retries fail, the requirement is
15
+ ambiguous, a decision with trade-offs is needed, scope must expand, an action is
16
+ destructive, or you are blocked.
17
+ - **Atomic changes** — one concern per change; smallest diff that works; no tangential
18
+ refactoring or cleanup.
19
+
20
+ A structured workflow (a skill or pipeline stage with its own explicit retry and check-in
21
+ rules) takes precedence where it is explicit.
22
+
23
+ ## Structure
24
+
25
+ ```
26
+ agent-guardrails/
27
+ ├── README.md # This file
28
+ ├── index.ts # Extension entry point — appends on before_agent_start
29
+ ├── guardrails.ts # The guardrails text + appendGuardrails composition
30
+ └── guardrails.test.ts # Composition tests
31
+ ```
32
+
33
+ ## Testing
34
+
35
+ ```bash
36
+ cd extensions/agent-guardrails
37
+ node --test --import tsx/esm guardrails.test.ts
38
+ ```
39
+
40
+ ## Design notes
41
+
42
+ - The block is a stable constant appended after any chained system-prompt changes, so the
43
+ prompt-cache prefix is preserved.
44
+ - `appendGuardrails` is idempotent — a prompt that already carries the marker is returned
45
+ unchanged, so a duplicate extension load cannot double the rules.
@@ -0,0 +1,42 @@
1
+ import { describe, it } from "node:test";
2
+ import assert from "node:assert/strict";
3
+
4
+ import { GUARDRAILS, GUARDRAILS_MARKER, appendGuardrails } from "./guardrails.ts";
5
+
6
+ // ─── appendGuardrails ─────────────────────────────────────────────────────────
7
+
8
+ describe("appendGuardrails", () => {
9
+ it("appends the guardrails block after the base prompt", () => {
10
+ const result = appendGuardrails("You are a helpful agent.");
11
+
12
+ assert.ok(result.startsWith("You are a helpful agent."));
13
+ assert.ok(result.includes(GUARDRAILS));
14
+ });
15
+
16
+ it("preserves a chained base prompt from earlier handlers", () => {
17
+ const base = "Base prompt.\n\n---\nAgent mode instructions.";
18
+ const result = appendGuardrails(base);
19
+
20
+ assert.ok(result.startsWith(base));
21
+ });
22
+
23
+ it("trims trailing whitespace before appending", () => {
24
+ const result = appendGuardrails("Base prompt.\n\n");
25
+
26
+ assert.equal(result, `Base prompt.\n\n${GUARDRAILS}`);
27
+ });
28
+
29
+ it("is idempotent when applied twice", () => {
30
+ const once = appendGuardrails("Base prompt.");
31
+ const twice = appendGuardrails(once);
32
+
33
+ assert.equal(twice, once);
34
+ assert.equal(twice.split(GUARDRAILS_MARKER).length - 1, 1);
35
+ });
36
+
37
+ it("returns the prompt unchanged when the guardrails are already present", () => {
38
+ const existing = `Base prompt.\n\n${GUARDRAILS}`;
39
+
40
+ assert.equal(appendGuardrails(existing), existing);
41
+ });
42
+ });
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Behavioural guardrails for the PI system prompt.
3
+ *
4
+ * The first slice of docs/ideas/improve-pi-system-prompt.md: hard circuit-breakers
5
+ * against wasted retries, explicit check-in triggers, and atomic-change discipline.
6
+ * Kept as a stable constant so it can be appended to every turn's system prompt
7
+ * without invalidating the prompt-cache prefix.
8
+ *
9
+ * The marker doubles as an idempotency guard: if a chained system prompt already
10
+ * carries the block (e.g. the extension loaded twice), it is not appended again.
11
+ */
12
+
13
+ export const GUARDRAILS_MARKER = "## Operating discipline (agent-cortex)";
14
+
15
+ export const GUARDRAILS = `${GUARDRAILS_MARKER}
16
+
17
+ Behavioural circuit-breakers for every task. They override the instinct to keep
18
+ retrying. A structured workflow (a skill or pipeline stage with its own explicit
19
+ retry and check-in rules) takes precedence where it is explicit.
20
+
21
+ ### Circuit-breakers
22
+
23
+ - **Two strikes, then stop.** If the same command, test, or fix fails twice for
24
+ the same reason, stop. Do not attempt a third variation. Report what you tried
25
+ and ask how to proceed.
26
+ - **No repeat without rationale.** Never re-run a tool call with the same input
27
+ unless you can state, in one sentence, what changed and why it will now
28
+ succeed. If you cannot, stop and ask.
29
+ - **No progress, no next cycle.** If a full cycle of work produces no observable
30
+ progress, check in instead of starting another cycle.
31
+
32
+ ### Check in when…
33
+
34
+ Stop and present options instead of continuing when any of these is true:
35
+
36
+ - Two attempts at the same problem have failed.
37
+ - The requirement is ambiguous, or two instructions conflict.
38
+ - The work needs a decision the user has not made, or a choice with material
39
+ trade-offs.
40
+ - You need to change something outside the task's stated scope.
41
+ - The next action is destructive or hard to reverse.
42
+ - You are blocked and no in-scope action will unblock you.
43
+
44
+ When checking in, state: what you tried, what happened, the options, and your
45
+ recommendation.
46
+
47
+ ### Atomic changes
48
+
49
+ - **One concern per change.** Do not refactor, rename, reformat, or tidy code the
50
+ task does not require. Record it and leave it for a separate change.
51
+ - **Smallest diff that works.** Make the minimal change that satisfies the
52
+ requirement.
53
+ - Never mix a fix with unrelated cleanup in the same change.
54
+ `;
55
+
56
+ /**
57
+ * Append the guardrails block to a system prompt.
58
+ *
59
+ * The base prompt is preserved verbatim (minus trailing whitespace) so callers
60
+ * can chain this after other `before_agent_start` handlers. Idempotent: a prompt
61
+ * that already contains the marker is returned unchanged.
62
+ */
63
+ export function appendGuardrails(systemPrompt: string): string {
64
+ if (systemPrompt.includes(GUARDRAILS_MARKER)) return systemPrompt;
65
+ return `${systemPrompt.trimEnd()}\n\n${GUARDRAILS}`;
66
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Agent Guardrails Extension
3
+ *
4
+ * Appends behavioural circuit-breakers to the PI system prompt on every turn:
5
+ * hard retry limits, explicit check-in triggers, and atomic-change discipline
6
+ * (the first slice of docs/ideas/improve-pi-system-prompt.md).
7
+ *
8
+ * The block is stable text appended after any chained system-prompt changes, so
9
+ * it keeps the prompt-cache prefix intact while remaining authoritative for the
10
+ * model. See guardrails.ts for the rules and appendGuardrails for composition.
11
+ */
12
+
13
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
14
+ import { appendGuardrails } from "./guardrails.ts";
15
+
16
+ export { GUARDRAILS, GUARDRAILS_MARKER, appendGuardrails } from "./guardrails.ts";
17
+
18
+ export default function agentGuardrailsExtension(pi: ExtensionAPI): void {
19
+ pi.on("before_agent_start", (event) => {
20
+ return { systemPrompt: appendGuardrails(event.systemPrompt) };
21
+ });
22
+ }
@@ -0,0 +1,21 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+
4
+ case `uname` in
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
10
+ esac
11
+
12
+ if [ -z "$NODE_PATH" ]; then
13
+ export NODE_PATH="/home/runner/work/agent-cortex/agent-cortex/node_modules/.pnpm/@earendil-works+pi-coding-agent@0.78.1_ws@8.21.0_zod@4.4.3/node_modules/@earendil-works/pi-coding-agent/node_modules:/home/runner/work/agent-cortex/agent-cortex/node_modules/.pnpm/@earendil-works+pi-coding-agent@0.78.1_ws@8.21.0_zod@4.4.3/node_modules:/home/runner/work/agent-cortex/agent-cortex/node_modules/.pnpm/node_modules"
14
+ else
15
+ export NODE_PATH="/home/runner/work/agent-cortex/agent-cortex/node_modules/.pnpm/@earendil-works+pi-coding-agent@0.78.1_ws@8.21.0_zod@4.4.3/node_modules/@earendil-works/pi-coding-agent/node_modules:/home/runner/work/agent-cortex/agent-cortex/node_modules/.pnpm/@earendil-works+pi-coding-agent@0.78.1_ws@8.21.0_zod@4.4.3/node_modules:/home/runner/work/agent-cortex/agent-cortex/node_modules/.pnpm/node_modules:$NODE_PATH"
16
+ fi
17
+ if [ -x "$basedir/node" ]; then
18
+ exec "$basedir/node" "$basedir/../@earendil-works/pi-coding-agent/dist/cli.js" "$@"
19
+ else
20
+ exec node "$basedir/../@earendil-works/pi-coding-agent/dist/cli.js" "$@"
21
+ fi
@@ -0,0 +1,21 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+
4
+ case `uname` in
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
10
+ esac
11
+
12
+ if [ -z "$NODE_PATH" ]; then
13
+ export NODE_PATH="/home/runner/work/agent-cortex/agent-cortex/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/node_modules:/home/runner/work/agent-cortex/agent-cortex/node_modules/.pnpm/typescript@5.9.3/node_modules:/home/runner/work/agent-cortex/agent-cortex/node_modules/.pnpm/node_modules"
14
+ else
15
+ export NODE_PATH="/home/runner/work/agent-cortex/agent-cortex/node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/node_modules:/home/runner/work/agent-cortex/agent-cortex/node_modules/.pnpm/typescript@5.9.3/node_modules:/home/runner/work/agent-cortex/agent-cortex/node_modules/.pnpm/node_modules:$NODE_PATH"
16
+ fi
17
+ if [ -x "$basedir/node" ]; then
18
+ exec "$basedir/node" "$basedir/../typescript/bin/tsc" "$@"
19
+ else
20
+ exec node "$basedir/../typescript/bin/tsc" "$@"
21
+ fi