@jaybeeuu/agent-cortex 1.43.0 → 2.1.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 (58) hide show
  1. package/.agents/skills/style-versioning/SKILL.md +5 -6
  2. package/.github/workflows/ci.yml +4 -11
  3. package/AGENTS.md +13 -17
  4. package/CHANGELOG.md +66 -0
  5. package/README.md +57 -57
  6. package/agents/README.md +9 -23
  7. package/agents/ralph/agent.md +13 -17
  8. package/agents/ralph/claude/polling.md +1 -1
  9. package/agents-native/ralph.md +15 -5
  10. package/bin/agent-cortex.mjs +0 -16
  11. package/bin/installers/claude.mjs +82 -11
  12. package/docs/claude-hooks.md +16 -0
  13. package/docs/ideas/improve-pi-system-prompt.md +9 -3
  14. package/docs/research/disable-model-invocation.md +5 -26
  15. package/docs/skills/skill-anatomy.md +5 -3
  16. package/docs/technical-direction/harness-config.md +6 -14
  17. package/docs/user-preferences.md +2 -2
  18. package/extensions/agent-modes/README.md +3 -3
  19. package/extensions/agent-modes/discover.ts +6 -6
  20. package/extensions/output-style/README.md +58 -0
  21. package/extensions/output-style/index.ts +23 -0
  22. package/extensions/output-style/output-style.md +43 -0
  23. package/extensions/output-style/output-style.test.ts +50 -0
  24. package/extensions/output-style/output-style.ts +32 -0
  25. package/extensions/output-style/package.json +19 -0
  26. package/extensions/output-style/tsconfig.json +12 -0
  27. package/extensions/subagent/README.md +2 -2
  28. package/extensions/subagent/agents.ts +1 -2
  29. package/lib/cli.mjs +5 -7
  30. package/package.json +5 -6
  31. package/scripts/check-skill-frontmatter.mjs +21 -0
  32. package/scripts/lib/compose-agent.mjs +10 -10
  33. package/scripts/lib/skill-frontmatter.mjs +135 -0
  34. package/skills/workflow/ralph/REFERENCE.md +34 -11
  35. package/skills/workflow/ralph/SKILL.md +10 -10
  36. package/skills/workflow/ralph/scripts/pending-gates.mjs +134 -0
  37. package/test/cli.test.mjs +11 -10
  38. package/test/installer.test.mjs +143 -1
  39. package/test/pending-gates.test.mjs +147 -0
  40. package/test/skill-frontmatter.test.mjs +137 -0
  41. package/token-map.README.md +9 -12
  42. package/token-map.json +26 -31
  43. package/agents/plan/copilot/frontmatter.json +0 -6
  44. package/agents/plan.agent.md +0 -59
  45. package/agents/ralph/copilot/frontmatter.json +0 -6
  46. package/agents/ralph/copilot/polling.md +0 -62
  47. package/agents/ralph-plan/copilot/frontmatter.json +0 -6
  48. package/agents/ralph-plan.agent.md +0 -126
  49. package/agents/ralph.agent.md +0 -331
  50. package/agents/strategy/copilot/frontmatter.json +0 -6
  51. package/agents/strategy.agent.md +0 -112
  52. package/bin/installers/copilot.mjs +0 -124
  53. package/hooks.json +0 -10
  54. package/plugin.json +0 -35
  55. package/scripts/build-copilot-agents.mjs +0 -30
  56. package/scripts/install-instructions.sh +0 -82
  57. package/scripts/sync-plugin-version.sh +0 -17
  58. package/test/copilot-installer.test.mjs +0 -308
@@ -42,7 +42,7 @@ Run once at startup:
42
42
  5. Create Ralph's workspace directory: `mkdir -p .agent-cortex/ralph`
43
43
  6. Create `.agent-cortex/ralph/state.json` with initial content:
44
44
  ```json
45
- { "timerShellId": null, "inflight": [] }
45
+ { "inflight": [] }
46
46
  ```
47
47
  Then generate the progress file:
48
48
  ```bash
@@ -59,7 +59,7 @@ Run once at startup:
59
59
  b. Claim the parent bead: `bd update <parent-id> --claim`.
60
60
  c. Create the first stage chore bead and dispatch it (see _Creating and dispatching a stage chore_ below).
61
61
  8. Record each launched agent in `.agent-cortex/ralph/state.json` (see format below).
62
- 9. Hand over to the completion-detection mechanism in the polling section: it starts the Copilot poll loop, blocks with `wait_for_agents` (PI), or waits for event-driven wake-ups (Claude), and proceeds to **HITL Pause** directly when no agent work was dispatched and HITL gate beads are pending.
62
+ 9. Hand over to the completion-detection mechanism in the polling section: it blocks with `wait_for_agents` (PI) or waits for event-driven wake-ups (Claude), and proceeds to **HITL Pause** directly when no agent work was dispatched and `skills/workflow/ralph/scripts/pending-gates.mjs` reports a non-empty `pendingHumanAction` — an **unblocked** `lifecycle:feature-pr` gate or an epic tagged `awaiting-epic-pr-merge`. Gates still parked behind their own pipeline do not count (REFERENCE.md, _Gate predicate_).
63
63
 
64
64
  ---
65
65
 
@@ -101,7 +101,6 @@ All orchestration state is derived from beads:
101
101
 
102
102
  ```json
103
103
  {
104
- "timerShellId": "shell-abc",
105
104
  "inflight": [
106
105
  { "choreId": "xyz-456", "parentId": "abc-123", "title": "Add auth", "agentId": "agent-xyz-456", "logLine": 4 },
107
106
  { "choreId": "def-789", "parentId": "ghi-101", "title": "Fix cache", "agentId": "agent-def-789", "logLine": 7 }
@@ -109,7 +108,6 @@ All orchestration state is derived from beads:
109
108
  }
110
109
  ```
111
110
 
112
- - **timerShellId**: the shellId of the active poll-timer bash process used for completion polling (Copilot; stays `null` on PI, which blocks with `wait_for_agents` instead, and on event-driven harnesses such as Claude).
113
111
  - **inflight[].choreId**: the chore bead currently being worked by the subagent.
114
112
  - **inflight[].parentId**: the parent feature task bead. Log file is `.agent-cortex/ralph/ralph-<parentId>.log`.
115
113
  - **inflight[].logLine**: the next line number to read from the parent's log file (1-based; start at 1). Updated after every poll.
@@ -127,12 +125,12 @@ Update `.agent-cortex/ralph/state.json` after **every** agent completion or disp
127
125
 
128
126
  ## Event loop
129
127
 
130
- After initialization, Ralph waits for background agents to complete. Completion notifications are delivered by the mechanism documented in the polling section — PI blocks with `wait_for_agents` until a worker completes and its result is returned inline; Copilot uses an active poll timer plus log polling; Claude is event-driven (you are re-invoked automatically when a worker completes, with its result delivered inline). On each completion notification:
128
+ After initialization, Ralph waits for background agents to complete. Completion notifications are delivered by the mechanism documented in the polling section — PI blocks with `wait_for_agents` until a worker completes and its result is returned inline; Claude is event-driven (you are re-invoked automatically when a worker completes, with its result delivered inline). On each completion notification:
131
129
 
132
130
  ### If a background agent completed
133
131
 
134
- 1. **Flush** that bead's log using the log-polling procedure in the polling section to pick up any final lines for the chat summary (PI: flush new log lines as part of handling the `wait_for_agents` result; Copilot: flush to detect completion; Claude: nothing to flush — the result is delivered with the wake-up).
135
- 2. **Read** the completed agent's full output (PI: the result arrives in the `wait_for_agents` response, with `read_agent` as a fallback for a specific result; Copilot: `read_agent`; Claude: the result was delivered inline).
132
+ 1. **Flush** that bead's log using the log-polling procedure in the polling section to pick up any final lines for the chat summary (PI: flush new log lines as part of handling the `wait_for_agents` result; Claude: nothing to flush — the result is delivered with the wake-up).
133
+ 2. **Read** the completed agent's full output (PI: the result arrives in the `wait_for_agents` response, with `read_agent` as a fallback for a specific result; Claude: the result was delivered inline).
136
134
  3. **Re-read** `.agent-cortex/ralph/state.json` to identify the chore bead and parent for that agent ID.
137
135
  4. **Parse** the agent's `---REPORT---` block (see report format in the `run-pipeline-stage` skill).
138
136
  5. **Close** the completed chore bead: `bd close <chore-id>`.
@@ -142,13 +140,13 @@ After initialization, Ralph waits for background agents to complete. Completion
142
140
  - **Failure paths** (verify BLOCKED, review CHANGES_REQUESTED): check loop cap (see below), then create a feedback bead per the _Feedback Beads_ section in `run-pipeline-stage`. Do **not** dispatch immediately — the feedback bead appears in `bd ready` and is picked up in step 7. Remove the inflight entry for this chore; the feedback bead will create a new entry when dispatched.
143
141
  7. **Check for newly ready beads**: run `bd ready`. For each bead not yet tracked:
144
142
  - **Chore bead with `stage:*` label** (a feedback bead): find its parent via `bd show <chore-id>` (follow the `parent-child` dep). Check loop cap (see below). If under cap: dispatch it via _Creating and dispatching a stage chore_. If at cap: block it (`bd update <chore-id> --status blocked --notes "<cap> cap reached"`) and record for shutdown.
145
- - **Task bead, AFK, in-flight count < 5, no open feature PR HITL gate**: claim the parent, create its first code chore, dispatch it.
143
+ - **Task bead, AFK, in-flight count < 5, no pending human action**: claim the parent, create its first code chore, dispatch it. Check `pendingHumanAction` with `node skills/workflow/ralph/scripts/pending-gates.mjs` — an unblocked `lifecycle:feature-pr` gate means a feature is awaiting a human merge, so hold new parent features. Gates still blocked behind their own pipeline are parked and do not count.
146
144
  - **Task bead, AFK, in-flight count at 5**: hold in memory as Waiting.
147
145
  - **HITL task bead**: note for **Pending Human Action** shutdown summary.
148
146
  - **NEEDS-REFINEMENT bead**: note for **Needs Refinement** shutdown summary.
149
147
  When a parent is removed from `inflight` (completed or failed), immediately promote the first Waiting AFK parent.
150
148
  8. **If no tasks remain in-flight** and `bd ready` returns no chore beads with `stage:*` labels and no AFK task beads:
151
- - If HITL gate beads are pending (`bd list -l lifecycle:feature-pr -l implementation-type:hitl` or `bd list -l awaiting-epic-pr-merge` returns results), proceed to **HITL Pause** (see below).
149
+ - If `pending-gates.mjs` reports a non-empty `pendingHumanAction` (an unblocked `lifecycle:feature-pr` gate, or an epic tagged `awaiting-epic-pr-merge`), proceed to **HITL Pause** (see below).
152
150
  - Otherwise, proceed to **Shutdown** (see below).
153
151
 
154
152
  ### Loop cap check
@@ -167,15 +165,14 @@ Block command: `bd update <parent-id> --status blocked --notes "<cap> cap reache
167
165
 
168
166
  ## HITL Pause
169
167
 
170
- Proceed here when no chore beads are in-flight, no `stage:*` chore beads are ready, and HITL gate beads are pending (open `lifecycle:feature-pr` beads or epics tagged `awaiting-epic-pr-merge`). Ralph stops rather than burning requests on idle polls.
168
+ Proceed here when no chore beads are in-flight, no `stage:*` chore beads are ready, and `pending-gates.mjs` reports a non-empty `pendingHumanAction` — an **unblocked** `lifecycle:feature-pr` gate (a feature genuinely awaiting human merge) or an epic tagged `awaiting-epic-pr-merge`. Gates still parked behind their own pipeline do not count. Ralph stops rather than burning requests on idle polls.
171
169
 
172
170
  1. Regenerate `.agent-cortex/ralph/progress.md` one final time — do not delete it.
173
171
  2. For each **multi-feature** epic whose feature beads are all closed but not yet tagged `awaiting-epic-pr-merge`, open/update an epic PR to main, then tag the epic `awaiting-epic-pr-merge`. Skip single-feature epics — their feature branch PRs already target `main` directly.
174
172
  3. Run `bd dolt push`.
175
- 4. Collect pending HITL gate beads and their PR URLs:
176
- - Run `bd list -l lifecycle:feature-pr -l implementation-type:hitl`
177
- - Run `bd list -l awaiting-epic-pr-merge`
178
- - For each bead, run `bd show <id>` to retrieve the PR URL from bead notes.
173
+ 4. Collect the table rows from the script's `pendingHumanAction` list:
174
+ - Run `node skills/workflow/ralph/scripts/pending-gates.mjs`
175
+ - For each entry, run `bd show <id>` to retrieve the PR URL from bead notes. `kind: "feature-pr"` rows read "review and merge the feature PR"; `kind: "epic-pr"` rows read "review and merge the epic PR into main".
179
176
  5. Output the **Pending Human Action** summary:
180
177
  ```
181
178
  ⏸️ Ralph is paused — human action required before work can continue.
@@ -188,7 +185,7 @@ Proceed here when no chore beads are in-flight, no `stage:*` chore beads are rea
188
185
 
189
186
  When you've completed the above actions, prompt me to continue.
190
187
  ```
191
- 6. **Stop completely.** Stop the polling mechanism documented in the polling section (Copilot: kill any running timer; PI and Claude: nothing to stop — there is no timer). Do **not** restart or schedule anything. Do **not** start any background processes. Do **not** check bead status again. Do **not** continue the event loop. Output **nothing further** after the summary above. Ralph is now fully idle — it must not act again until the user explicitly re-prompts.
188
+ 6. **Stop completely.** Stop the polling mechanism documented in the polling section (PI and Claude: nothing to stop — there is no timer). Do **not** restart or schedule anything. Do **not** start any background processes. Do **not** check bead status again. Do **not** continue the event loop. Output **nothing further** after the summary above. Ralph is now fully idle — it must not act again until the user explicitly re-prompts.
192
189
 
193
190
  ---
194
191
 
@@ -258,5 +255,4 @@ All beads complete.
258
255
  - **Never** auto-merge feature or epic PRs — merge decisions are human-controlled.
259
256
  - **Never** continue past feature completion until PR `feature/<parent-id> -> epic/<epic-id>` is merged and the child HITL PR gate bead is closed by a human.
260
257
  - **Never** continue past epic completion until PR `epic/<epic-id> -> main` is merged.
261
- - **When a feature hits the HITL PR gate, push and open the PR immediately and report the URL — do not wait for explicit approval to push or create the PR.**
262
- - **Always** bump the patch version in `plugin.json` as part of any commit that changes agent or skill files.
258
+ - **When a feature hits the HITL PR gate, push and open the PR immediately and report the URL — do not wait for explicit approval to push or create the PR.**
@@ -20,7 +20,7 @@ Rules:
20
20
 
21
21
  Claude Code **re-invokes you automatically when a background worker completes** — the worker's result is delivered to you inline on the wake-up. **Do not poll and do not `sleep`.** React to completions as they arrive.
22
22
 
23
- - There is **no poll timer on Claude**: skip the PI/Copilot timer start and restart steps in initialization, and there is nothing to kill in **HITL Pause** — when you stop, you simply stop. The harness wakes you again when the user re-invokes you.
23
+ - There is **no poll timer anywhere in this pipeline**: nothing in initialization starts one, and there is nothing to kill in **HITL Pause** — when you stop, you simply stop. The harness wakes you again when the user re-invokes you.
24
24
  - There is **no log tailing on Claude**: workers' progress lines (`.agent-cortex/ralph/ralph-<parentId>.log`) are written by the workers themselves and surfaced by the harness with the completion — nothing to flush, nothing to poll. Produce the shared chat summaries from the delivered result instead.
25
25
  - On each completion, apply the shared handling in the **Event loop** section of agent.md (steps 3–8: correlate the task ID to its bead via `state.json`, parse the REPORT, close the chore, advance the parent, check for newly ready beads, run the shutdown check). `state.json` `logLine` tracking is not used on Claude.
26
26
 
@@ -35,7 +35,8 @@ created, so by the time a bead reaches `bd ready` here it should already carry a
35
35
  `classify-bead` from the ralph loop:
36
36
 
37
37
  - `implementation-type:afk` present → **AFK**. Done.
38
- - `implementation-type:hitl` present → **HITL**. Skip; record for the pending-action summary.
38
+ - `execution:hitl` (or the legacy `implementation-type:hitl`) present → **HITL**. Skip; record for the
39
+ pending-action summary.
39
40
  - Neither present → treat as **NEEDS-REFINEMENT**: skip; record for the summary. A missing label
40
41
  here means the bead skipped planning-stage classification — flag it, don't classify inline.
41
42
 
@@ -52,8 +53,8 @@ Run once at startup:
52
53
  5. Read `maxFixRounds` from `${CLAUDE_PLUGIN_ROOT}/skills/create-task/pipeline.json` if present;
53
54
  otherwise default to **4**.
54
55
  6. Dispatch up to 5 AFK features (see below). Then end your turn — you will be woken on the
55
- first completion. If there are zero AFK features but HITL/PR gates are pending, go straight
56
- to **Idle**.
56
+ first completion. If there are zero AFK features but an open `lifecycle:feature-pr` gate is
57
+ pending (see **Idle**), go straight to **Idle**.
57
58
 
58
59
  ## Dispatching a feature
59
60
 
@@ -105,10 +106,19 @@ beads per **Classifying a bead**, and promote AFK features into free slots.
105
106
 
106
107
  When no workers are running and no AFK features remain:
107
108
 
108
- - **PR gates pending** (open `lifecycle:feature-pr` beads): run `bd dolt push`, then print a
109
+ - **PR gates pending** (open `lifecycle:feature-pr` beads on either HITL axis):
110
+ `bd list -l lifecycle:feature-pr --label-any execution:hitl,implementation-type:hitl --limit 0`.
111
+ This harness keys the pause on a gate being **open**, not unblocked, because its gates stay
112
+ blocked for their whole life: `create-task` blocks each gate behind the feature's `document`
113
+ chore, which no Claude ralph claims or closes, and the native route in _When a reviewer
114
+ worker completes_ step 2 deps the gate onto the still-open feature bead. So never assume a gate
115
+ here is blocked only by its own pipeline, and never treat "no unblocked gate" as "no human
116
+ action owed" — that clears the loop with PRs unmerged. Run `bd dolt push`, then print a
109
117
  **Pending Human Action** table (bead id, title, PR URL, "review & merge, then `bd close <gate>`"),
110
118
  and **stop completely** — output nothing further. Do not poll. The user re-invokes you after
111
- merging; you will resume from `bd ready`.
119
+ merging; you will resume from `bd ready`. The pi harness uses the unblocked-gate predicate
120
+ (`skills/workflow/ralph/scripts/pending-gates.mjs`); F10 (agnt-ctx-s5xd.10) unifies ralph's
121
+ agent files — reconcile the two predicates there rather than half-migrating this one.
112
122
  - **Nothing pending**: run `bd dolt push`, print any recorded HITL / needs-refinement / blocked
113
123
  beads as tables, then print **All beads complete.**
114
124
 
@@ -112,22 +112,6 @@ if (parsed.command === "install") {
112
112
  process.exit(1);
113
113
  }
114
114
  }
115
-
116
- // copilot regenerates the flat agents/*.agent.md files the Copilot plugin
117
- // loads via plugin.json ("agents": "agents/") — the same code path
118
- // `pnpm build:copilot` runs.
119
- if (parsed.harness === "copilot") {
120
- const { installCopilot } = await import("./installers/copilot.mjs");
121
- try {
122
- await installCopilot({ output: parsed.output, dryRun: parsed.dryRun });
123
- process.exit(0);
124
- } catch (err) {
125
- process.stderr.write(`Install failed: ${err.message}\n`);
126
- process.exit(1);
127
- }
128
- }
129
-
130
- process.exit(0);
131
115
  }
132
116
 
133
117
  function printPiSweep({ removed }) {
@@ -79,6 +79,14 @@ const DEFAULT_SETTINGS_PATH = join(homedir(), ".claude", "settings.json");
79
79
  // The committed user-settings template.
80
80
  const SETTINGS_TEMPLATE = "claude/settings.json";
81
81
 
82
+ // Claude user memory — the global prompt. The installer owns only the marked
83
+ // section; everything outside the markers is the user's own text.
84
+ const DEFAULT_INSTRUCTIONS_PATH = join(homedir(), ".claude", "CLAUDE.md");
85
+ const INSTRUCTIONS_BEGIN = "<!-- BEGIN agent-cortex:output-style -->";
86
+ const INSTRUCTIONS_END = "<!-- END agent-cortex:output-style -->";
87
+ // Canonical prose, shared with the pi extension (extensions/output-style).
88
+ const INSTRUCTIONS_SOURCE = join("extensions", "output-style", "output-style.md");
89
+
82
90
  // The only keys the installer owns outright: everything else in the live file
83
91
  // is personal config the merge must preserve.
84
92
  const SETTINGS_OWNED_KEYS = ["enabledPlugins", "extraKnownMarketplaces"];
@@ -97,8 +105,8 @@ const DEFAULT_WARN = (msg) => console.warn(`[claude-installer] ${msg}`);
97
105
  // section composition from the poll-loop sources.
98
106
  const DEFER = new Set(["ralph"]);
99
107
 
100
- // Ralph-coupled Copilot skills that don't apply to the lean Claude Ralph (and carry dead
101
- // ~/.copilot orchestration paths). Not copied into the Claude plugin.
108
+ // Ralph-coupled pipeline skills that don't apply to the lean Claude Ralph (its completion
109
+ // handling is event-driven, not the pi orchestrator's). Not copied into the Claude plugin.
102
110
  const SKILL_EXCLUDE = new Set(["ralph", "run-pipeline-stage"]);
103
111
 
104
112
  const NAME_PREFIX = "agent-cortex:";
@@ -324,7 +332,7 @@ async function installSettings({ root, target, dryRun, warn }) {
324
332
  }
325
333
 
326
334
  const content = `${JSON.stringify(merged, null, 2)}\n`;
327
- return writeManagedJson({ target, content, dryRun });
335
+ return writeManagedFile({ target, content, dryRun });
328
336
  }
329
337
 
330
338
  /**
@@ -350,12 +358,12 @@ function mergeSettings(template, existing) {
350
358
  }
351
359
 
352
360
  /**
353
- * Write the managed settings file. A legacy symlink is replaced with a real
354
- * file by writing a sibling temp file and renaming it over the target — rename
355
- * replaces the link itself, so the committed template is never written through.
361
+ * Write a managed file. A legacy symlink is replaced with a real file by writing
362
+ * a sibling temp file and renaming it over the target — rename replaces the link
363
+ * itself, so a committed template is never written through.
356
364
  * Returns the action taken so the CLI can report the plan (dry-run included).
357
365
  */
358
- async function writeManagedJson({ target, content, dryRun }) {
366
+ async function writeManagedFile({ target, content, dryRun }) {
359
367
  const symlink = await isSymlink(target);
360
368
  const current = await readFileMaybe(target);
361
369
 
@@ -382,6 +390,52 @@ async function writeManagedJson({ target, content, dryRun }) {
382
390
  return { path: target, action: symlink ? "removed-symlink" : "written", removedSymlink: symlink };
383
391
  }
384
392
 
393
+ // ─── Claude user memory (CLAUDE.md) ──────────
394
+
395
+ /**
396
+ * Materialise the output-style block into the managed section of ~/.claude/CLAUDE.md.
397
+ *
398
+ * The section is delimited by HTML comments, so a re-install replaces only the
399
+ * text between them and every other line of the user's memory file survives. A
400
+ * file with exactly one marker (hand-edited or truncated) is left untouched with
401
+ * a warning, never guessed at.
402
+ */
403
+ async function installInstructions({ root, target, dryRun, warn }) {
404
+ const block = await readFileMaybe(join(root, INSTRUCTIONS_SOURCE));
405
+ if (block === null) {
406
+ warn(`no ${INSTRUCTIONS_SOURCE} source in the package — skipping CLAUDE.md`);
407
+ return null;
408
+ }
409
+ const content = mergeManagedSection(await readFileMaybe(target), block);
410
+ if (content === null) {
411
+ warn(`${target} has an unterminated agent-cortex section — left unchanged; fix the markers by hand`);
412
+ return { path: target, action: "skipped-unmanaged", removedSymlink: false };
413
+ }
414
+ return writeManagedFile({ target, content, dryRun });
415
+ }
416
+
417
+ /**
418
+ * Return `existing` with the managed section replaced by `block`, or null when
419
+ * the file carries unmatched markers. A file without markers gets the section
420
+ * appended, so personal memory text is never disturbed.
421
+ */
422
+ function mergeManagedSection(existing, block) {
423
+ const managed = `${INSTRUCTIONS_BEGIN}\n${block.trimEnd()}\n${INSTRUCTIONS_END}`;
424
+ if (existing === null) return `${managed}\n`;
425
+
426
+ const start = existing.indexOf(INSTRUCTIONS_BEGIN);
427
+ const end = existing.indexOf(INSTRUCTIONS_END);
428
+ if (start === -1 && end === -1) {
429
+ const head = existing.replace(/\s*$/, "");
430
+ return head === "" ? `${managed}\n` : `${head}\n\n${managed}\n`;
431
+ }
432
+ if (start === -1 || end === -1 || end < start) return null;
433
+
434
+ const before = existing.slice(0, start).replace(/\s*$/, "");
435
+ const after = existing.slice(end + INSTRUCTIONS_END.length).replace(/^\s*/, "").replace(/\s*$/, "");
436
+ return `${[before, managed, after].filter((part) => part !== "").join("\n\n")}\n`;
437
+ }
438
+
385
439
  async function readTemplateJson(filePath, warn) {
386
440
  const raw = await readFileMaybe(filePath);
387
441
  if (raw === null) {
@@ -450,15 +504,20 @@ async function isSymlink(filePath) {
450
504
  * (default ~/.claude/settings.json). Passing it
451
505
  * explicitly materialises settings even with
452
506
  * --output — the test/CI seam.
507
+ * @param {string} [options.instructionsPath] Claude user-memory target
508
+ * (default ~/.claude/CLAUDE.md). Passing it
509
+ * explicitly materialises the section even
510
+ * with --output — the test/CI seam.
453
511
  * @param {(msg: string) => void} [options.warn] Warning sink, also collected in `warnings`.
454
512
  * @returns {{ output: string, marketplaceRoot: string|null, manifestPath: string|null,
455
513
  * marketplaceManifest: object|null, agents: string[], natives: string[],
456
514
  * skills: {names: string[], md: number, files: number, dir: string},
457
515
  * hooks: boolean, hookFiles: string[], handAuthored: string[],
458
516
  * settings: {path: string, action: string, removedSymlink: boolean}|null,
517
+ * instructions: {path: string, action: string, removedSymlink: boolean}|null,
459
518
  * warnings: string[], dryRun: boolean }}
460
519
  */
461
- export async function installClaude({ root = PACKAGE_ROOT, output, dryRun = false, pluginRoot, settingsPath, warn = DEFAULT_WARN } = {}) {
520
+ export async function installClaude({ root = PACKAGE_ROOT, output, dryRun = false, pluginRoot, settingsPath, instructionsPath, warn = DEFAULT_WARN } = {}) {
462
521
  const tokenMap = await loadTokenMap(root);
463
522
  if (typeof tokenMap.version === "number" && tokenMap.version > CONTRACT_VERSION) {
464
523
  throw new Error(
@@ -498,15 +557,22 @@ export async function installClaude({ root = PACKAGE_ROOT, output, dryRun = fals
498
557
  await mkdir(out, { recursive: true });
499
558
  }
500
559
 
501
- // The default install owns ~/.claude/settings.json; --output is the
502
- // generate-only form and leaves the user's Claude config alone. An explicit
503
- // settingsPath requests the write regardless (the test/CI seam).
560
+ // The default install owns ~/.claude/settings.json and the managed section of
561
+ // ~/.claude/CLAUDE.md; --output is the generate-only form and leaves the
562
+ // user's Claude config alone. Explicit paths request the write regardless
563
+ // (the test/CI seam).
504
564
  const settingsTarget = settingsPath ?? DEFAULT_SETTINGS_PATH;
505
565
  const settings =
506
566
  isDefaultInstall || settingsPath !== undefined
507
567
  ? await installSettings({ root, target: settingsTarget, dryRun, warn: warnSink })
508
568
  : null;
509
569
 
570
+ const instructionsTarget = instructionsPath ?? DEFAULT_INSTRUCTIONS_PATH;
571
+ const instructions =
572
+ isDefaultInstall || instructionsPath !== undefined
573
+ ? await installInstructions({ root, target: instructionsTarget, dryRun, warn: warnSink })
574
+ : null;
575
+
510
576
  const agents = await buildAgents(root);
511
577
  const natives = await buildNatives(root, join(root, "agents-native"));
512
578
  const skillOut = join(out, "skills");
@@ -530,6 +596,7 @@ export async function installClaude({ root = PACKAGE_ROOT, output, dryRun = fals
530
596
  hookFiles: hookFiles.map((f) => f.rel),
531
597
  handAuthored: handAuthored.map((f) => f.rel),
532
598
  settings,
599
+ instructions,
533
600
  warnings,
534
601
  dryRun,
535
602
  };
@@ -551,11 +618,15 @@ export async function installClaude({ root = PACKAGE_ROOT, output, dryRun = fals
551
618
  if (summary.handAuthored.length)
552
619
  console.log(` hand-authored files: ${summary.handAuthored.join(", ")} (copied from repo claude-extras/)`);
553
620
  if (summary.settings) console.log(` settings → ${summary.settings.path} (${summary.settings.action})`);
621
+ if (summary.instructions)
622
+ console.log(` CLAUDE.md → ${summary.instructions.path} (${summary.instructions.action})`);
554
623
  return summary;
555
624
  }
556
625
 
557
626
  // Write phase — the cleanup before buildSkills already cleared stale children.
558
627
  if (settings) console.log(`Materialised Claude settings → ${settings.path} (${settings.action})`);
628
+ if (instructions && instructions.action !== "skipped-unmanaged")
629
+ console.log(`Materialised CLAUDE.md section → ${instructions.path} (${instructions.action})`);
559
630
  const agentOut = join(out, "agents");
560
631
  await mkdir(agentOut, { recursive: true });
561
632
  for (const { file, content } of [...agents.files, ...natives]) {
@@ -17,6 +17,7 @@ UI surface. Each decision below is grounded in the Claude Code hooks reference.
17
17
  | `session-start/` | `SessionStart` hook commands | **Port** (extended) |
18
18
  | `auto-name.ts` | — | **Reject** — built-in |
19
19
  | `agent-guardrails/` | — | **Reject** — pi-only (no system-prompt hook) |
20
+ | `output-style/` | managed `CLAUDE.md` section | **Port** — user memory, not a hook |
20
21
  | `notify/` | `Notification` hook + `scripts/notify.mjs` | **Port** |
21
22
  | `skill-stats/` | — | **Reject** — no equivalent |
22
23
  | `subagent/` | — | **Reject** — native |
@@ -89,6 +90,21 @@ context rather than system instructions. The rules are harness-agnostic prose, s
89
90
  a future port could append them to the existing ported SessionStart hook — out of
90
91
  scope for this pi-first slice.
91
92
 
93
+ ## `output-style/` → Port (managed CLAUDE.md section)
94
+
95
+ pi appends the reply shape (action first, Simplified Technical English, restated
96
+ context) to the system prompt through `before_agent_start`. Claude Code cannot
97
+ modify the system prompt from a hook, so the installer writes the same block into
98
+ the managed section of `~/.claude/CLAUDE.md` — Claude's user memory, loaded every
99
+ session.
100
+
101
+ The content is not copied into a Claude-side template: `extensions/output-style/
102
+ output-style.md` is the single source, read by the pi extension at load and by the
103
+ installer at install time. Re-running the install replaces only the text between
104
+ the markers; personal memory text survives untouched. The installer never writes
105
+ the block by appending to a `SessionStart` hook — memory is the right surface
106
+ because the rules must hold for every turn, not just the first.
107
+
92
108
  ## `skill-stats/` → Reject (no equivalent)
93
109
 
94
110
  pi tracks skills loaded into the system prompt, `/skill:` invocations, SKILL.md
@@ -1,9 +1,11 @@
1
1
  # Idea: Improve PI System Prompt
2
2
 
3
3
  ## Status
4
- Backlog idea (not implementation-ready) — first slice (circuit-breakers, check-in triggers,
5
- atomic-change discipline) implemented as the `agent-guardrails` pi extension; the rest of the
6
- idea is unscoped.
4
+ Backlog idea (not implementation-ready) — two slices implemented as pi extensions:
5
+ `agent-guardrails` (circuit-breakers, check-in triggers, atomic-change discipline) and
6
+ `output-style` (action-first reply shape, Simplified Technical English, restated context,
7
+ shipped to pi via the system prompt and to Claude via a managed `CLAUDE.md` section).
8
+ The rest of the idea is unscoped.
7
9
 
8
10
  ## Why it might be useful
9
11
  PI's current system prompt is weak. Agents consistently get off track, spend excessive tokens spinning their wheels or going off on tangents, fail to check in when problems are hit, and generally behave like inexperienced code goblins. This wastes tokens, slows down task completion, and forces the user to constantly redirect the agent. A stronger system prompt would produce more focused, disciplined agents that know when to stop and ask.
@@ -30,6 +32,10 @@ High — after current work lands. Directly impacts daily productivity and token
30
32
  - Which guardrails from the Gemini starter are genuinely useful vs redundant with existing skills (e.g. `git-workflow` already covers destructive commands, `style-code` covers scope discipline)?
31
33
 
32
34
  ## Notes
35
+ - **Second slice (settled)**: reply shape is a separate extension from task discipline.
36
+ `agent-guardrails` decides whether work continues; `output-style` decides how the result
37
+ is reported (STE per ASD-STE100, action first, context restated — never a bead or phase
38
+ ID cited alone). Both are stable constants appended on `before_agent_start`.
33
39
  - **Precedence (settled by the first slice)**: the generic circuit-breakers yield to a structured
34
40
  workflow — a skill or pipeline stage with its own explicit retry and check-in rules wins.
35
41
  - **Gemini starter prompt** exists (see conversation from 2026-08-05) covering loop prevention, action scope, context management, execution workflow order, and working state maintenance. Useful starting material but needs heavy customisation.
@@ -3,8 +3,7 @@
3
3
  Decision record for the frontmatter field this repo uses to keep rare or destructive
4
4
  skills out of automatic model routing (bead `agnt-ctx-yf24`).
5
5
 
6
- **Verdict:** rely on it for pi and Claude Code. Do not rely on it in Copilot CLI as
7
- "invoke-only" — there it means "unreachable through the model-facing skill tool".
6
+ **Verdict:** rely on it for pi and Claude Code.
8
7
 
9
8
  ## What the field does
10
9
 
@@ -19,11 +18,10 @@ previously implied.
19
18
  | --- | --- | --- | --- |
20
19
  | pi 1.4.0 | Yes — byte-identical copy | Yes — hidden from the prompt, `/skill:name` still works | `bin/installers/pi.mjs:182-204`, `scripts/lib/compose-agent.mjs:69-95`; `dist/core/skills.js:262` (parse) and `:279` (filter); `docs/skills.md:53` |
21
20
  | Claude Code 2.1.294 | Yes — byte-identical copy | Yes — Claude cannot invoke it, `/skill-name` still works | `bin/installers/claude.mjs:160-201`, same token helper; [Claude Code skills docs](https://code.claude.com/docs/en/skills) ("Control who invokes a skill") |
22
- | Copilot CLI (1.0.55 locally, 1.0.80/1.0.83 upstream) | N/A — no copy; `plugin.json` `"skills": "skills/"` serves the repo tree in place | Auto-invocation suppressed, but explicit model invocation fails; `/skill-name` works | `plugin.json:12`; [github/copilot-cli#4438](https://github.com/github/copilot-cli/issues/4438) (open, retested 1.0.83) |
23
21
 
24
22
  Both materialisers copy `SKILL.md` as text and rewrite only `{{TOOL:...}}` /
25
23
  `{{PATH:...}}` tokens, so frontmatter keys they do not know pass through untouched —
26
- nothing re-serialises YAML. Verified by installing all three harnesses into
24
+ nothing re-serialises YAML. Verified by installing both harnesses into
27
25
  `/var/tmp/dmi-check` and diffing `skills/productivity/teach/SKILL.md` (the repo's
28
26
  only current user of the field): both copies are byte-identical to the source.
29
27
  `claude plugin validate /var/tmp/dmi-check/claude/skills` passes, i.e. Claude Code's
@@ -58,46 +56,27 @@ Two Claude-specific caveats:
58
56
  frontmatter field itself still applies there — only that settings-based alternative
59
57
  is unavailable.
60
58
 
61
- **Copilot CLI.** The field is not in GitHub's skill documentation, which lists only
62
- `name`, `description`, `license`, and `allowed-tools`. In practice the CLI filters
63
- flagged skills out of the `skill()` tool's resolution list, so the model cannot call
64
- them even when the user names one: `Skill not found`. Only the interactive
65
- `/skill-name` slash command reaches a flagged skill, and `-p` mode has no slash path.
66
- Upstream issue #4438 is open.
67
-
68
- Local probing was not possible: the installed CLI is 1.0.55 (no `copilot skill`
69
- subcommand), and prompt runs are refused by this machine's org policy. The upstream
70
- report is the evidence for Copilot, tested on 1.0.80 and 1.0.83 — newer than 1.0.55,
71
- so this may differ locally, in either direction.
72
-
73
59
  ## Options for "explicit invocation only", evaluated
74
60
 
75
61
  | Option | Portable? | Enforced? | Verdict |
76
62
  | --- | --- | --- | --- |
77
- | `disable-model-invocation: true` | pi + Claude Code yes; Copilot suppresses but breaks tool-based invocation | Yes on pi/Claude | **Recommended** where suppressing auto-routing is the point and the explicit path is a human typing `/name` |
63
+ | `disable-model-invocation: true` | pi + Claude Code yes | Yes | **Recommended** where suppressing auto-routing is the point and the explicit path is a human typing `/name` |
78
64
  | Naming convention (e.g. `manual-*`) | Yes | No — no harness reads names | Rejected: a human signal we already get from the body |
79
- | `metadata:` block | Yes (spec-legal) | No — pi and Claude read top-level keys only, Copilot undefined | Rejected: documentation that looks like enforcement |
65
+ | `metadata:` block | Yes (spec-legal) | No — pi and Claude read top-level keys only | Rejected: documentation that looks like enforcement |
80
66
  | Keep such skills out of `skills/` | Yes | Yes, by not shipping the skill | Rejected: gives up the per-harness install entirely |
81
67
 
82
68
  ## Recommendation
83
69
 
84
70
  Use the field when the danger is fuzzy automatic matching and the intended explicit
85
71
  path is a human typing `/skill-name`: that is the case for a migration skill whose
86
- `--apply` renames labels workspace-wide. Accept, and document, that on Copilot CLI it
87
- also blocks model-mediated invocation, including `-p` runs — if a workflow needs
88
- non-interactive access to such a skill, do not set the field and instead disable the
89
- skill in that user's Copilot config (`copilot skill disable <name>`, or the `/skills`
90
- toggle), which is harness-native and per-user.
72
+ `--apply` renames labels workspace-wide.
91
73
 
92
74
  ## Reproducing
93
75
 
94
76
  ```sh
95
77
  node bin/agent-cortex.mjs install claude --output /var/tmp/dmi-check/claude
96
78
  node bin/agent-cortex.mjs install pi --output /var/tmp/dmi-check/pi
97
- node bin/agent-cortex.mjs install copilot --output /var/tmp/dmi-check/copilot
98
79
  diff skills/productivity/teach/SKILL.md /var/tmp/dmi-check/claude/skills/teach/SKILL.md
99
80
  diff skills/productivity/teach/SKILL.md /var/tmp/dmi-check/pi/skills/productivity/teach/SKILL.md
100
81
  claude plugin validate /var/tmp/dmi-check/claude/skills
101
82
  ```
102
-
103
- The Copilot run writes only composed agent files — there is no skill copy to inspect.
@@ -9,13 +9,15 @@ Required fields:
9
9
  - `name`: kebab-case skill identifier.
10
10
  - `description`: max 1024 characters.
11
11
 
12
+ Frontmatter is machine-validated: `pnpm lint` runs `check:skills`, which fails on a
13
+ frontmatter block that would make pi silently drop the skill.
14
+
12
15
  Optional fields:
13
16
  - `argument-hint`: input prompt shown to users at invocation.
14
17
  - `disable-model-invocation`: hides the skill from automatic model routing; explicit
15
18
  `/skill-name` invocation still works. Set it when the danger is a fuzzy description
16
19
  match, e.g. a skill whose steps are destructive. Preserved and honoured by pi and
17
- Claude Code; Copilot CLI suppresses auto-routing but then cannot reach the skill
18
- through its model-facing tool. See `docs/research/disable-model-invocation.md`.
20
+ Claude Code. See `docs/research/disable-model-invocation.md`.
19
21
 
20
22
  ## Description format rules
21
23
 
@@ -35,7 +37,7 @@ Optional fields:
35
37
 
36
38
  ## Tool references
37
39
 
38
- Reference agent tools by `{{TOOL:name}}` token (canonical copilot name), never a
40
+ Reference agent tools by `{{TOOL:name}}` token (canonical authoring name), never a
39
41
  hardcoded harness-specific name such as `bash`, `task`, or `read_agent` — installers
40
42
  substitute the per-harness name from `token-map.json` at install time. Add any new tool
41
43
  to the map's `tools` section in the same change; unknown tokens are hard errors. See
@@ -20,13 +20,14 @@ below no longer hold:
20
20
  that both load.
21
21
 
22
22
  `pi.packages` and `lib/pi-packages.mjs` are deleted, so the bespoke-pi-mechanism problem this
23
- memo was written to settle is closed. The Claude and Copilot analysis stands.
23
+ memo was written to settle is closed. The Claude analysis stands; Copilot CLI is no longer a
24
+ deploy target (`agnt-ctx-2rl`).
24
25
 
25
26
  ## Problem and target outcome
26
27
 
27
- - **What we are solving.** agent-cortex ships agents and skills to three harnesses (pi, Claude
28
- Code, Copilot CLI) and needs a declared, reproducible set of *third-party* extensions those
29
- agents depend on (pi packages / Claude plugins). Two bespoke mechanisms currently exist and
28
+ - **What we are solving.** agent-cortex ships agents and skills to two harnesses (pi, Claude
29
+ Code) and needs a declared, reproducible set of *third-party* extensions those agents depend
30
+ on (pi packages / Claude plugins). Two bespoke mechanisms currently exist and
30
31
  disagree: `main` declares packages in `package.json` → `pi.packages` and provisions them from
31
32
  `lib/pi-packages.mjs`; the unmerged pi-harness epic (`agnt-ctx-dfq3`) declares them in
32
33
  `pi.extensions.json` / `claude.extensions.json` and provisions them via `agent-cortex ext
@@ -51,9 +52,6 @@ memo was written to settle is closed. The Claude and Copilot analysis stands.
51
52
  - **Claude is idiomatic two ways.** Machine state is `~/.claude/settings.json` → `enabledPlugins`
52
53
  + `extraKnownMarketplaces`. A plugin declares other plugins in `.claude-plugin/plugin.json` →
53
54
  `dependencies`, which Claude Code resolves and auto-installs.
54
- - **Copilot has no dependency mechanism.** `copilot plugin install/list/uninstall` exists, but its
55
- state is auto-managed `~/.copilot/config.json` (not committable) and `plugin.json` declares no
56
- dependencies.
57
55
  - **The epic already built two-thirds of the target.** Unmerged commit `afab2c6` (F3) materialises
58
56
  `pi/settings.json` + `pi/keybindings.json` from committed templates with merge-keep-personal and
59
57
  the CLI owning `packages`; `1ff212d` (F4) makes bundled pi extensions load via package
@@ -86,7 +84,7 @@ memo was written to settle is closed. The Claude and Copilot analysis stands.
86
84
  --json` are the exact machine-state comparison; drift self-heals on install.
87
85
  - **Cons.** pi bundling shifts update cadence to agent-cortex releases and grows the tarball;
88
86
  Claude cross-marketplace deps need an allowlist; the committed template also carries personal
89
- settings; Copilot gets nothing.
87
+ settings.
90
88
  - **Risks.** Loading TS extensions from bundled `node_modules/`; maintaining the Claude
91
89
  cross-marketplace allowlist.
92
90
 
@@ -128,8 +126,6 @@ the epic, and delete `pi.packages` + `lib/pi-packages.mjs` from `main`.
128
126
  - **Committed personal settings.** `pi/settings.json` already carries models/theme; the template
129
127
  model makes the repo own the whole pi settings surface. Accepted; F3's merge preserves personal
130
128
  keys and only `packages` is CLI-owned.
131
- - **Copilot gets no mechanism.** Documented exception — the README says `copilot plugin install`.
132
- Accepted because Copilot has no dependency field and no committable config.
133
129
  - **No dedicated diff command.** A read-only `status` is deferred until drift is shown to hurt.
134
130
 
135
131
  ## Validation plan
@@ -160,8 +156,6 @@ pi extension loads from `node_modules/` and an optional one is materialised from
160
156
  speculative and should be dropped rather than maintained.
161
157
  - Bundling `pi-web-access`'s native-ish dependency tree (unpdf, linkedom) proves brittle or
162
158
  unacceptable on a fresh machine.
163
- - Copilot gains a committable config or a plugin dependency field — revisit the documented
164
- exception.
165
159
  - Drift between the committed template and the machine becomes a recurring annoyance — add the
166
160
  deferred read-only `status`.
167
161
 
@@ -175,8 +169,6 @@ pi extension loads from `node_modules/` and an optional one is materialised from
175
169
  (auto-install, cross-marketplace allowlist, tag naming).
176
170
  - Claude Code, *Settings* — https://code.claude.com/docs/en/settings (`enabledPlugins`,
177
171
  `extraKnownMarketplaces`).
178
- - GitHub Copilot CLI, *Plugin reference* — https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-plugin-reference
179
- and *Configuration directory* — https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-config-dir-reference
180
172
  - Code evidence (as of 2026-09 — `pi.packages` and `lib/pi-packages.mjs` were removed in
181
173
  `agnt-ctx-7kru.1`, see Status): `package.json` (`pi.packages`), `lib/pi-packages.mjs` (main's
182
174
  provisioner);
@@ -1,8 +1,8 @@
1
1
  # User Preferences
2
2
 
3
3
  This file records personal development preferences for the author (**jaybeeuu**).
4
- Both Copilot CLI agents and PI coding agents should read this file for user-level
5
- context when making tooling and workflow decisions.
4
+ Both pi and Claude Code agents should read this file for user-level context when
5
+ making tooling and workflow decisions.
6
6
 
7
7
  ## Package manager
8
8
 
@@ -39,9 +39,9 @@ contract:
39
39
  from the tool set with a warning.
40
40
 
41
41
  Flat `agents/*.agent.md` files are parsed as a fallback when an agent has no
42
- composable directory — they are generated output (scripts/build-copilot-agents.mjs)
43
- but may exist for non-migrated agents. A composable directory always wins over its
44
- flat counterpart for the same agent id.
42
+ composable directory — a staged pi package carries flat renders, and a hand-written
43
+ agent may be a single flat file. A composable directory always wins over its flat
44
+ counterpart for the same agent id.
45
45
 
46
46
  ## Development
47
47