@jaybeeuu/agent-cortex 1.38.1 → 1.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/.agents/skills/style-versioning/SKILL.md +1 -1
  2. package/.github/workflows/ci.yml +47 -14
  3. package/AGENTS.md +19 -3
  4. package/CHANGELOG.md +42 -0
  5. package/README.md +118 -63
  6. package/agents/README.md +13 -10
  7. package/agents/ralph-plan/agent.md +1 -1
  8. package/agents/ralph-plan.agent.md +1 -1
  9. package/bin/agent-cortex.mjs +29 -8
  10. package/bin/installers/claude.mjs +569 -105
  11. package/bin/installers/copilot.mjs +22 -12
  12. package/bin/installers/pi.mjs +37 -34
  13. package/docs/claude-hooks.md +3 -3
  14. package/docs/ideas/extension-manifests.md +75 -0
  15. package/docs/ideas/record-ideas-as-beads.md +115 -0
  16. package/docs/ideas/subagent-visibility.md +72 -0
  17. package/docs/ideas/task-session-context.md +89 -0
  18. package/docs/research/skill-design-research.md +4 -4
  19. package/docs/research/skill-improvements-analysis.md +1 -1
  20. package/docs/skills/skill-anatomy.md +38 -2
  21. package/docs/technical-direction/migration-execution-labels.md +132 -0
  22. package/extensions/agent-modes/README.md +3 -2
  23. package/extensions/agent-modes/discover.test.ts +30 -7
  24. package/extensions/agent-modes/discover.ts +1 -1
  25. package/extensions/skill-stats/index.test.ts +3 -2
  26. package/lib/cli.mjs +24 -6
  27. package/package.json +4 -4
  28. package/plugin.json +1 -1
  29. package/pnpm-workspace.yaml +1 -1
  30. package/scripts/build-copilot-agents.mjs +1 -1
  31. package/scripts/check-no-sync.mjs +49 -0
  32. package/scripts/lib/compose-agent.mjs +32 -14
  33. package/skills/engineering/design-an-interface/SKILL.md +78 -58
  34. package/skills/engineering/grill-with-docs/REFERENCE.md +78 -0
  35. package/skills/engineering/grill-with-docs/SKILL.md +66 -63
  36. package/skills/engineering/improve-codebase-architecture/SKILL.md +66 -48
  37. package/skills/engineering/technical-direction/FORMAT.md +38 -0
  38. package/skills/engineering/technical-direction/SKILL.md +70 -77
  39. package/skills/planning/classify-bead/SKILL.md +59 -35
  40. package/skills/planning/create-task/REFERENCE.md +40 -0
  41. package/skills/planning/create-task/SKILL.md +70 -66
  42. package/skills/planning/create-task/scripts/create-chores.ts +6 -1
  43. package/skills/planning/create-task/scripts/node_modules/.bin/tsc +21 -0
  44. package/skills/planning/create-task/scripts/node_modules/.bin/tsserver +21 -0
  45. package/skills/planning/create-task/scripts/node_modules/.bin/tsx +21 -0
  46. package/skills/planning/prd-to-tasks/REFERENCE.md +66 -0
  47. package/skills/planning/prd-to-tasks/SKILL.md +86 -170
  48. package/skills/planning/record-idea/FORMAT.md +44 -0
  49. package/skills/planning/record-idea/SKILL.md +70 -53
  50. package/skills/planning/request-refactor-plan/FORMAT.md +45 -0
  51. package/skills/planning/request-refactor-plan/SKILL.md +57 -46
  52. package/skills/planning/write-a-prd/FORMAT.md +54 -0
  53. package/skills/planning/write-a-prd/SKILL.md +49 -48
  54. package/skills/planning/write-a-ticket/SKILL.md +68 -53
  55. package/skills/productivity/handoff/SKILL.md +12 -0
  56. package/skills/productivity/init-beads/SKILL.md +48 -40
  57. package/skills/productivity/validate-agent-dir/scripts/node_modules/.bin/tsc +21 -0
  58. package/skills/productivity/validate-agent-dir/scripts/node_modules/.bin/tsserver +21 -0
  59. package/skills/productivity/validate-agent-dir/scripts/node_modules/.bin/tsx +21 -0
  60. package/skills/productivity/write-a-skill/SKILL.md +2 -1
  61. package/skills/review/maintain-agent-docs/SKILL.md +71 -51
  62. package/skills/workflow/hitl-collab/SKILL.md +86 -17
  63. package/skills/workflow/ralph/REFERENCE.md +22 -17
  64. package/skills/workflow/ralph/SKILL.md +88 -74
  65. package/skills/workflow/run-pipeline-stage/REFERENCE.md +22 -0
  66. package/skills/workflow/run-pipeline-stage/SKILL.md +91 -93
  67. package/skills/workflow/run-pipeline-stage/scripts/generate-progress.test.ts +6 -107
  68. package/skills/workflow/run-pipeline-stage/scripts/node_modules/.bin/tsc +21 -0
  69. package/skills/workflow/run-pipeline-stage/scripts/node_modules/.bin/tsserver +21 -0
  70. package/skills/workflow/run-pipeline-stage/scripts/node_modules/.bin/tsx +21 -0
  71. package/test/claude-installer.test.mjs +308 -0
  72. package/test/cli.test.mjs +40 -30
  73. package/test/copilot-installer.test.mjs +92 -82
  74. package/test/helpers/fake-claude.mjs +152 -0
  75. package/test/installer.test.mjs +309 -57
  76. package/test/pi-installer.test.mjs +115 -99
  77. package/token-map.README.md +21 -10
  78. package/token-map.json +7 -6
  79. package/.claude-plugin/marketplace.json +0 -16
  80. package/claude/.claude-plugin/plugin.json +0 -11
  81. package/claude/agents/plan.md +0 -58
  82. package/claude/agents/ralph-plan.md +0 -121
  83. package/claude/agents/ralph.md +0 -193
  84. package/claude/agents/strategy.md +0 -111
  85. package/claude/hooks/scripts/notify.mjs +0 -54
  86. package/claude/hooks.json +0 -38
  87. /package/{claude → claude-extras}/.mcp.json +0 -0
  88. /package/{claude → claude-extras}/scripts/statusline-command.sh +0 -0
@@ -30,7 +30,7 @@
30
30
  //
31
31
  // Zero dependencies so it runs on the CI Node and local Node alike.
32
32
 
33
- import { writeFileSync, mkdirSync, readdirSync, statSync, existsSync } from "node:fs";
33
+ import { writeFile, mkdir, readdir, stat } from "node:fs/promises";
34
34
  import { join, dirname } from "node:path";
35
35
  import { fileURLToPath } from "node:url";
36
36
  import { composeAgent } from "../../scripts/lib/compose-agent.mjs";
@@ -38,9 +38,17 @@ import { composeAgent } from "../../scripts/lib/compose-agent.mjs";
38
38
  const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
39
39
  const DEFAULT_ROOT = join(MODULE_DIR, "..", "..");
40
40
 
41
- function isDirectory(p) {
41
+ async function isDirectory(p) {
42
42
  try {
43
- return statSync(p).isDirectory();
43
+ return (await stat(p)).isDirectory();
44
+ } catch {
45
+ return false;
46
+ }
47
+ }
48
+
49
+ async function isFile(p) {
50
+ try {
51
+ return (await stat(p)).isFile();
44
52
  } catch {
45
53
  return false;
46
54
  }
@@ -71,19 +79,21 @@ function frontmatterYaml(name, fm) {
71
79
  }
72
80
 
73
81
  /** Compose agents from agents/<name>/; every composable dir must ship a copilot harness. */
74
- function buildAgents(root) {
82
+ async function buildAgents(root) {
75
83
  const files = [];
76
- const entries = readdirSync(join(root, "agents"), { withFileTypes: true }).sort((a, b) => byName(a.name, b.name));
84
+ const entries = (await readdir(join(root, "agents"), { withFileTypes: true })).sort((a, b) =>
85
+ byName(a.name, b.name),
86
+ );
77
87
 
78
88
  for (const entry of entries) {
79
89
  if (!entry.isDirectory()) continue; // skip generated *.agent.md flat files and README
80
90
  const dir = join(root, "agents", entry.name);
81
- if (!existsSync(join(dir, "agent.md"))) continue; // not a composable agent dir
82
- if (!isDirectory(join(dir, "copilot")) || !existsSync(join(dir, "copilot", "frontmatter.json"))) {
91
+ if (!(await isFile(join(dir, "agent.md")))) continue; // not a composable agent dir
92
+ if (!(await isDirectory(join(dir, "copilot"))) || !(await isFile(join(dir, "copilot", "frontmatter.json")))) {
83
93
  throw new Error(`agent "${entry.name}": composable directory without copilot/frontmatter.json — define the copilot harness or exclude the agent`);
84
94
  }
85
95
 
86
- const fm = composeAgent(root, entry.name, "copilot");
96
+ const fm = await composeAgent(root, entry.name, "copilot");
87
97
  files.push({
88
98
  name: entry.name,
89
99
  file: `${entry.name}.agent.md`,
@@ -105,9 +115,9 @@ function buildAgents(root) {
105
115
  * @param {boolean} [options.dryRun] Plan and report without writing anything.
106
116
  * @returns {{ output: string, agents: string[], dryRun: boolean }}
107
117
  */
108
- export function installCopilot({ root = DEFAULT_ROOT, output, dryRun = false } = {}) {
118
+ export async function installCopilot({ root = DEFAULT_ROOT, output, dryRun = false } = {}) {
109
119
  const out = output ?? join(root, "agents");
110
- const files = buildAgents(root);
120
+ const files = await buildAgents(root);
111
121
  const summary = {
112
122
  output: out,
113
123
  agents: files.map((f) => f.name),
@@ -120,9 +130,9 @@ export function installCopilot({ root = DEFAULT_ROOT, output, dryRun = false } =
120
130
  return summary;
121
131
  }
122
132
 
123
- mkdirSync(out, { recursive: true });
133
+ await mkdir(out, { recursive: true });
124
134
  for (const { file, content } of files) {
125
- writeFileSync(join(out, file), content);
135
+ await writeFile(join(out, file), content);
126
136
  }
127
137
  console.log(`Generated ${files.length} Copilot agent file(s): ${summary.agents.join(", ")}`);
128
138
  return summary;
@@ -8,8 +8,9 @@
8
8
  // Composition per the token-map.json contract:
9
9
  // 1. {{SECTION:name}} is resolved from the agent's pi/<name>.md section file
10
10
  // (shared composer, scripts/lib/compose-agent.mjs).
11
- // 2. {{TOOL:key}} is substituted with the pi column; a null mapping drops the
12
- // token from prose with a warning (pi has no ask_user/skill tool).
11
+ // 2. {{TOOL:key}} is substituted with the pi column (pi maps ask_user →
12
+ // ask_questions and skill → read); any (future) null pi mapping drops the
13
+ // token from prose with a warning.
13
14
  // 3. {{PATH:key}} named keys resolve through the paths table; bare relative
14
15
  // paths resolve against the plugin root (default: token-map.json's pi value,
15
16
  // overridable with --plugin-root for checkout/symlinked installs).
@@ -24,7 +25,7 @@
24
25
  //
25
26
  // Zero dependencies so it runs on the CI Node and local Node alike.
26
27
 
27
- import { readFileSync, writeFileSync, mkdirSync, copyFileSync, readdirSync, statSync } from "node:fs";
28
+ import { readFile, writeFile, mkdir, copyFile, readdir, stat } from "node:fs/promises";
28
29
  import { join, dirname } from "node:path";
29
30
  import { fileURLToPath } from "node:url";
30
31
  import { homedir } from "node:os";
@@ -37,22 +38,22 @@ const PI = "pi";
37
38
  // extended before this installer can trust it.
38
39
  const CONTRACT_VERSION = 1;
39
40
 
40
- const REPO_ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
41
+ const PACKAGE_ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
41
42
  const DEFAULT_OUTPUT = join(homedir(), ".pi", "agent");
42
43
 
43
44
  const DEFAULT_WARN = (msg) => console.warn(`[pi-installer] ${msg}`);
44
45
 
45
- function isDirectory(p) {
46
+ async function isDirectory(p) {
46
47
  try {
47
- return statSync(p).isDirectory();
48
+ return (await stat(p)).isDirectory();
48
49
  } catch {
49
50
  return false;
50
51
  }
51
52
  }
52
53
 
53
- function isFile(p) {
54
+ async function isFile(p) {
54
55
  try {
55
- return statSync(p).isFile();
56
+ return (await stat(p)).isFile();
56
57
  } catch {
57
58
  return false;
58
59
  }
@@ -73,8 +74,8 @@ function isFile(p) {
73
74
  * @returns {{ agents: {name:string, filePath:string}[], skills: {skills:number, md:number, files:number, dir:string},
74
75
  * warnings: string[], dryRun: boolean, agentsDir: string, skillsDir: string }}
75
76
  */
76
- export function installPi(options = {}) {
77
- const root = options.root ?? REPO_ROOT;
77
+ export async function installPi(options = {}) {
78
+ const root = options.root ?? PACKAGE_ROOT;
78
79
  const output = options.output ?? DEFAULT_OUTPUT;
79
80
  const dryRun = options.dryRun ?? false;
80
81
  const warnings = [];
@@ -83,7 +84,7 @@ export function installPi(options = {}) {
83
84
  (options.warn ?? DEFAULT_WARN)(msg);
84
85
  };
85
86
 
86
- const tokenMap = loadTokenMap(root);
87
+ const tokenMap = await loadTokenMap(root);
87
88
  if (typeof tokenMap.version === "number" && tokenMap.version > CONTRACT_VERSION) {
88
89
  throw new Error(
89
90
  `token-map.json version ${tokenMap.version} is newer than the contract version ${CONTRACT_VERSION} this installer implements — upgrade agent-cortex`,
@@ -94,26 +95,28 @@ export function installPi(options = {}) {
94
95
  const agentsDir = join(output, "agents");
95
96
  const skillsDir = join(output, "skills");
96
97
 
97
- const agents = installAgents({ root, agentsDir, dryRun, pluginRoot, tokenMap, warn });
98
- const skills = installSkills({ root, skillsDir, dryRun, pluginRoot, tokenMap, warn });
98
+ const agents = await installAgents({ root, agentsDir, dryRun, pluginRoot, tokenMap, warn });
99
+ const skills = await installSkills({ root, skillsDir, dryRun, pluginRoot, tokenMap, warn });
99
100
 
100
101
  return { agents, skills, warnings, dryRun, agentsDir, skillsDir };
101
102
  }
102
103
 
103
104
  // ─── Agents ──────────────────────────────────────────────────────────────────
104
105
 
105
- function installAgents({ root, agentsDir, dryRun, pluginRoot, tokenMap, warn }) {
106
+ async function installAgents({ root, agentsDir, dryRun, pluginRoot, tokenMap, warn }) {
106
107
  const written = [];
107
108
  const agentsSrc = join(root, "agents");
108
- for (const entry of readdirSync(agentsSrc, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
109
+ for (const entry of (await readdir(agentsSrc, { withFileTypes: true })).sort((a, b) =>
110
+ a.name.localeCompare(b.name),
111
+ )) {
109
112
  if (!entry.isDirectory()) continue; // skip generated *.agent.md flat files
110
113
  const dir = join(agentsSrc, entry.name);
111
- if (!isFile(join(dir, "agent.md"))) continue; // not a composable agent dir
112
- if (!isDirectory(join(dir, PI)) || !isFile(join(dir, PI, "frontmatter.json"))) {
114
+ if (!(await isFile(join(dir, "agent.md")))) continue; // not a composable agent dir
115
+ if (!(await isDirectory(join(dir, PI))) || !(await isFile(join(dir, PI, "frontmatter.json")))) {
113
116
  throw new Error(`agent "${entry.name}": composable directory without pi/frontmatter.json — define the pi harness or exclude the agent`);
114
117
  }
115
118
 
116
- const fm = composeAgent(root, entry.name, PI, {
119
+ const fm = await composeAgent(root, entry.name, PI, {
117
120
  dropNullTools: true,
118
121
  pluginRoot,
119
122
  resolveRelativePaths: true,
@@ -123,8 +126,8 @@ function installAgents({ root, agentsDir, dryRun, pluginRoot, tokenMap, warn })
123
126
  const content = renderPiAgent(entry.name, fm, tools);
124
127
  const filePath = join(agentsDir, `${entry.name}.agent.md`);
125
128
  if (!dryRun) {
126
- mkdirSync(agentsDir, { recursive: true });
127
- writeFileSync(filePath, content);
129
+ await mkdir(agentsDir, { recursive: true });
130
+ await writeFile(filePath, content);
128
131
  }
129
132
  written.push({ name: entry.name, filePath });
130
133
  }
@@ -147,24 +150,24 @@ function renderPiAgent(name, fm, tools) {
147
150
 
148
151
  // ─── Skills ──────────────────────────────────────────────────────────────────
149
152
 
150
- function installSkills({ root, skillsDir, dryRun, pluginRoot, tokenMap, warn }) {
153
+ async function installSkills({ root, skillsDir, dryRun, pluginRoot, tokenMap, warn }) {
151
154
  const skillsSrc = join(root, "skills");
152
- if (!isDirectory(skillsSrc)) return { skills: 0, md: 0, files: 0, dir: skillsDir };
155
+ if (!(await isDirectory(skillsSrc))) return { skills: 0, md: 0, files: 0, dir: skillsDir };
153
156
 
154
157
  const transform = (content) =>
155
158
  substituteTokens(content, PI, tokenMap, { dropNullTools: true, pluginRoot, resolveRelativePaths: true, warn });
156
159
 
157
- if (!dryRun) mkdirSync(skillsDir, { recursive: true });
160
+ if (!dryRun) await mkdir(skillsDir, { recursive: true });
158
161
  let skills = 0;
159
162
  let md = 0;
160
163
  let files = 0;
161
- for (const group of readdirSync(skillsSrc).sort()) {
164
+ for (const group of (await readdir(skillsSrc)).sort()) {
162
165
  const groupDir = join(skillsSrc, group);
163
- if (!isDirectory(groupDir)) continue;
164
- for (const name of readdirSync(groupDir).sort()) {
165
- if (!isFile(join(groupDir, name, "SKILL.md"))) continue;
166
+ if (!(await isDirectory(groupDir))) continue;
167
+ for (const name of (await readdir(groupDir)).sort()) {
168
+ if (!(await isFile(join(groupDir, name, "SKILL.md")))) continue;
166
169
  skills += 1;
167
- const stats = copyTree(join(groupDir, name), join(skillsDir, group, name), transform, dryRun);
170
+ const stats = await copyTree(join(groupDir, name), join(skillsDir, group, name), transform, dryRun);
168
171
  md += stats.md;
169
172
  files += stats.files;
170
173
  }
@@ -173,24 +176,24 @@ function installSkills({ root, skillsDir, dryRun, pluginRoot, tokenMap, warn })
173
176
  }
174
177
 
175
178
  /** Recursively copy a tree; transform .md file contents, copy everything else verbatim. */
176
- function copyTree(src, dest, transform, dryRun) {
179
+ async function copyTree(src, dest, transform, dryRun) {
177
180
  let md = 0;
178
181
  let files = 0;
179
- const entries = readdirSync(src, { withFileTypes: true });
182
+ const entries = await readdir(src, { withFileTypes: true });
180
183
  for (const entry of entries) {
181
184
  const from = join(src, entry.name);
182
185
  const to = join(dest, entry.name);
183
186
  if (entry.isDirectory()) {
184
- const stats = copyTree(from, to, transform, dryRun);
187
+ const stats = await copyTree(from, to, transform, dryRun);
185
188
  md += stats.md;
186
189
  files += stats.files;
187
190
  } else if (entry.isFile()) {
188
191
  files += 1;
189
192
  if (entry.name.endsWith(".md")) md += 1;
190
193
  if (dryRun) continue;
191
- mkdirSync(dest, { recursive: true });
192
- if (entry.name.endsWith(".md")) writeFileSync(to, transform(readFileSync(from, "utf-8")));
193
- else copyFileSync(from, to);
194
+ await mkdir(dest, { recursive: true });
195
+ if (entry.name.endsWith(".md")) await writeFile(to, transform(await readFile(from, "utf-8")));
196
+ else await copyFile(from, to);
194
197
  }
195
198
  }
196
199
  return { md, files };
@@ -2,8 +2,8 @@
2
2
 
3
3
  The pi extensions in `extensions/` were audited for Claude Code equivalents.
4
4
  Implemented ports live in `hooks/claude/hooks.json` (copied verbatim into the
5
- generated plugin as `claude/hooks.json`); support scripts live in
6
- `hooks/claude/scripts/` and are bundled into `claude/hooks/scripts/` by
5
+ materialised plugin as its `hooks.json`); support scripts live in
6
+ `hooks/claude/scripts/` and are bundled into the plugin's `hooks/scripts/` by
7
7
  `bin/installers/claude.mjs` (referenced from hooks.json via `$CLAUDE_PLUGIN_ROOT`).
8
8
 
9
9
  Claude Code's hook model differs from PI's: hooks are shell commands fed a JSON
@@ -100,7 +100,7 @@ for little signal. Rejected; Claude Code ships its own usage analytics
100
100
  ## `subagent/` → Reject (native)
101
101
 
102
102
  pi's `subagent` tool spawns isolated `pi --mode json` processes. Claude Code has
103
- native subagents — the plugin already ships `claude/agents/*.md` (including
103
+ native subagents — the plugin already ships `~/.agent-cortex/claude/agents/*.md` (including
104
104
  `ralph`, `plan`, `strategy`, `ralph-plan`) executed via the built-in Agent/Task
105
105
  tools with per-session context isolation — so a hook-based workaround would be
106
106
  strictly worse.
@@ -0,0 +1,75 @@
1
+ # Idea: extension-manifests
2
+
3
+ ## Status
4
+ Backlog idea (not implementation-ready)
5
+
6
+ ## Created
7
+ 2026-08-25
8
+
9
+ ## Problem
10
+ PI extensions and Claude/Copilot plugins are installed individually (`pi install <ext>`) and
11
+ live implicit in the machine's state (`~/.pi/agent/extensions/`) with no declared manifest of
12
+ what the author uses or where it came from. A fresh machine, clean `~/.pi`, or a failed update
13
+ leaves the environment without the expected extensions. The toolset isn't reproducible and
14
+ isn't documented anywhere in the author's repos. Experimenting with an extension (install, try,
15
+ remove) is also all manual, and there's no way to uninstall cleanly.
16
+
17
+ ## Who benefits
18
+ The author. Installing/updating agent-cortex becomes a reliable way to restore a known PI +
19
+ Claude environment (agent-cortex + declared extensions in one step). The manifests document and
20
+ reproduce the toolset across machines. A prune CLI makes extension experimentation fast and safe.
21
+
22
+ ## Proposed outcome
23
+ agent-cortex ships **committed extension manifests** (reviewed and stored on GitHub) declaring
24
+ which extensions to install — **per harness**: one manifest for PI extensions, one for
25
+ Claude/Copilot plugins. On install or update of agent-cortex, it reads the manifests and
26
+ installs the declared extensions. The manifests are the single source of truth.
27
+
28
+ Key decisions from the interview:
29
+
30
+ 1. **No monitoring** — nothing observes installs, and the CLI never writes the manifests.
31
+ Adding an extension = edit the manifest, commit it, ship it. This keeps the source of truth
32
+ in GitHub where it belongs (and gets the usual PR/review flow).
33
+ 2. **Per-harness manifests** — separate records for the pi harness vs the Claude harness, each
34
+ carrying the extension source (git URL / package name) for that harness's installer. The
35
+ install-on-update path handles each harness with its own mechanism.
36
+ 3. **Prune CLI with a TUI** — a CLI command (e.g. `agent-cortex ext prune`) opens an interactive
37
+ TUI listing locally-installed extensions; selecting one **uninstalls** it from the local
38
+ environment. Removing from the manifest is done manually on GitHub. This supports the
39
+ experimentation loop: install → try → prune locally, then curate the manifest separately.
40
+
41
+ agent-cortex's own bundled extensions (auto-discovered via `pi install`, e.g. `skill-stats`)
42
+ are deliberately excluded from the manifests — only third-party extensions are declared.
43
+
44
+ ## Validity check
45
+ - Evidence we already have: agent-cortex already has an install surface to hang this on
46
+ (`pi install` package discovery for the pi harness, plus the existing `bin/agent-cortex.mjs`
47
+ install command and `postinstall` script). The repo already carries a `plugin.json` (Claude
48
+ harness) alongside `package.json` (pi harness), showing the per-harness split is natural.
49
+ - Riskiest assumption: that an automated install-on-update of declared extensions is safe and
50
+ desirable (auto-installing on every update could surprise, or fail mid-flight on a new
51
+ machine lacking auth/network). Also, the TUI prune needs the local extension store's shape
52
+ (`~/.pi/agent/extensions/` for pi; whatever Claude uses) to be introspectable.
53
+ - What would invalidate this idea: if auto-install-on-update proves too disruptive (better as
54
+ an explicit `agent-cortex ext install` command), or if the pi/Claude installers don't expose
55
+ the store in a way that supports listing + uninstall for the TUI.
56
+
57
+ ## Constraints
58
+ - Manifests live in this repo, committed via the normal PR flow — the CLI never edits them.
59
+ - Install-on-update must be idempotent and safe to skip/fail gracefully (no hard breakage of
60
+ agent-cortex's own install on extension failure).
61
+ - Follow the repo's extension conventions where relevant (lightweight, no internal LLM calls).
62
+ - If any local data is persisted (e.g. prune history), write to `~/.pi/agent-cortex/`.
63
+
64
+ ## Next validation step
65
+ Confirm the install surface for each harness: how `pi install <source>` works for the pi side,
66
+ and how Claude/Copilot plugins get installed, plus whether each store can be listed and
67
+ uninstalled programmatically (needed for the prune TUI). Then prototype the manifest + one
68
+ harness's install-on-update path.
69
+
70
+ ## Notes
71
+ Recorded 2026-08-25. The idea was refined during the interview: initially framed as
72
+ "record the extensions I use" (observer-based), corrected to a **committed manifest** model —
73
+ recording happens by editing + committing, not by monitoring. Prune uninstalls locally only;
74
+ the manifest stays GitHub-managed. This idea is adjacent to `record-ideas-as-beads`: both make
75
+ agent-cortex the declared source of truth for the author's environment and workflow.
@@ -0,0 +1,115 @@
1
+ # Idea: record-ideas-as-beads
2
+
3
+ ## Status
4
+ Fleshed out 2026-08-25 — design decisions locked in an interview; not yet implemented.
5
+
6
+ ## Created
7
+ 2026-08-25
8
+
9
+ ## Problem
10
+ Ideas currently live as loose markdown files in `docs/ideas/`. This sits awkwardly alongside
11
+ the rest of the workflow, which is almost entirely bead-based (bd). Because ideas aren't beads,
12
+ they can't be searched with `bd label`, can't express relationships/dependencies to real tasks,
13
+ can't be given priority that surfaces alongside other work, and can't sync to remote via
14
+ `bd dolt push`. There's no clean promotion path from "idea" to "epic" to "executable task".
15
+
16
+ ## Who benefits
17
+ The author's daily planning loop. Ideas are the beginning of epics, so making them first-class
18
+ beads gives a single coherent backlog where an idea can grow relationships to the tasks it
19
+ spawns. The plan/agent-nexus agents benefit too: agent-nexus will eventually schedule work
20
+ through planning stages (not just execution), and a beads-native idea record gives it a clean
21
+ input to plan against.
22
+
23
+ ## Proposed outcome
24
+ Record ideas as beads labelled `kind:idea`, replacing `docs/ideas/*.md` entirely (bead-only —
25
+ files deleted after migration). `bd` already supports labels natively (`bd create -l`,
26
+ `bd label add/list`). An idea bead maps directly to an epic; promoting it means spawning task
27
+ beads as children and letting the idea bead become the epic node.
28
+
29
+ ## Fleshed-out design (decided 2026-08-25)
30
+
31
+ ### Label & identification
32
+ - **`kind:idea` label** marks idea beads. Ideas do NOT use `deferred` status — they stay
33
+ `open` so they're visible to idea tooling; hiding from actionable work is done by exclusion,
34
+ not status.
35
+ - **ralph excludes them**: ready queries use `bd ready --exclude-label kind:idea`
36
+ (verified: `bd ready` supports `--exclude-label`). Idea beads never appear as claimable work.
37
+ - Optional companion labels: `priority:<0-4>`, `epic:<id>` once promoted.
38
+
39
+ ### Flesh-out workflow (the skill that consumes them)
40
+ There should be a **flesh-out skill** (e.g. `flesh-out-idea`) that answers: *"which idea beads
41
+ are ready to be fleshed out?"* This is `bd ready -l kind:idea` — the blocker-aware ready query
42
+ scoped to ideas. Because `bd ready` already respects dependencies, an idea bead that
43
+ `depends-on` in-flight work (e.g. "needs unified-ci-pipeline landed first") naturally shows as
44
+ *not ready* until its deps close. That's exactly the semantics wanted:
45
+ - `bd ready -l kind:idea` → list & pick an idea ready for fleshing.
46
+ - The skill runs the record-idea-style interview (why / how / when / priority) at deeper depth,
47
+ updating the bead's description/design/notes as the idea ripens.
48
+ - Once fleshed out enough, the idea can be promoted (below).
49
+
50
+ ### Promotion path: idea → epic → tasks
51
+ Parent-child hierarchy (chosen over labels+deps only):
52
+ 1. Idea bead starts with `kind:idea`, type defaulted appropriately (still `task` type, or set
53
+ `epic` type on promotion).
54
+ 2. On promotion: `bd label add <idea> epic:<idea-id>` (or flip to an epic label), then spawn
55
+ task beads with `--parent <idea-id>` — children inherit the epic relationship natively.
56
+ 3. The idea bead becomes the epic node; its children are the executable tasks. Closing the
57
+ children resolves to the epic closing.
58
+
59
+ ### Content model (bead fields)
60
+ Map the current idea-template sections onto bd fields:
61
+ | md template field | bd field |
62
+ |---|---|
63
+ | Title | `--title` |
64
+ | Problem / Why | `--description` |
65
+ | How we might do it | `--design` |
66
+ | When / Priority / Validity / Constraints / Notes | `--notes` (structured, keep section markers) |
67
+ | anything referential | `--context` |
68
+
69
+ Multiple sections per field use explicit markers (`## Validity check`, etc.) inside the field
70
+ so the flesh-out interview can find/update them later.
71
+
72
+ ### Skill changes
73
+ - **`record-idea`**: replace scaffold-file flow (`new-idea.sh` → `docs/ideas/x.md`) with bead
74
+ creation: interview → `bd create "<title>" -l kind:idea -d "<why>" --design "<how>"
75
+ --notes "<when/priority/validity>"`. Keep the interview; drop the markdown scaffold script.
76
+ - New **`flesh-out-idea`** skill (see above) — validates readiness (`bd ready -l kind:idea`),
77
+ picks an idea, drives the deepening interview, updates the bead, optionally promotes.
78
+
79
+ ### Migration
80
+ - Bulk-migrate the existing `docs/ideas/*.md` records to beads: read each file, map sections to
81
+ bd fields, `bd create -l kind:idea`, preserve priority. Run as a one-off (manual or scripted).
82
+ - Then **delete `docs/ideas/`** — bead-only going forward.
83
+ - Update `AGENTS.md` + any stale references that point at `docs/ideas/`.
84
+ - The `record-idea` skill itself must unlearn the docs/ideas path.
85
+
86
+ ## Validity check
87
+ - Evidence we already have: `bd ready --exclude-label` and `bd ready -l` both exist and compose
88
+ (label filtering + blocker-aware ready semantics) — the two core mechanisms this idea needs
89
+ are native, no feature build. `--parent` exists for the hierarchy. 9-10 idea files exist to
90
+ migrate; the record-idea skill and its script are the only two consumers of the file flow.
91
+ - Riskiest assumption: that keeping ideas `open` (not `deferred`) + label exclusion is
92
+ discoverable enough — ralph, plan agents, and the author must remember the exclusion. The
93
+ `bd-tool` skill's labels table needs `kind:idea` documented with the exclusion convention.
94
+ - What would invalidate this idea: if label-based exclusion leaks idea beads into `bd ready`
95
+ via some path (e.g. `bd ready` without the exclude flag), or if losing the scannable
96
+ `docs/ideas/` directory hurts occasional review more than bead queries help it.
97
+
98
+ ## Constraints
99
+ - Ideas stay quick to scan: `bd list -l kind:idea` + `bd show <id>` should be a comfortable
100
+ replacement for opening the directory.
101
+ - Idea beads must never appear in ralph's actionable-ready view.
102
+ - `record-idea` and `bd-tool` skills stay consistent — no stale references to docs/ideas.
103
+ - Priority of ideas uses the same P0-P4 scale as tasks (P4 = backlog default for ideas).
104
+
105
+ ## Next validation step
106
+ Prototype one idea as a labelled bead: create it with `-l kind:idea`, confirm `bd ready`
107
+ excludes it by default and `bd ready -l kind:idea` surfaces it; then promote it (add epic label,
108
+ spawn a child task) to confirm the hierarchy reads correctly. If the prototype passes, do the
109
+ bulk migration + skill updates.
110
+
111
+ ## Notes
112
+ Interview on 2026-08-25 locked: labels over status (kind:idea + ralph exclusion), bead-only
113
+ content (files deleted), parent-child promotion, and record-idea skill rewritten to create
114
+ beads. Agent-nexus is the planned consumer. Once this lands, this very record becomes an
115
+ idea-bead (and then this file is deleted — the migration eats its own tail).
@@ -0,0 +1,72 @@
1
+ # Idea: subagent-visibility
2
+
3
+ ## Status
4
+ Backlog idea (not implementation-ready)
5
+
6
+ ## Created
7
+ 2026-08-25
8
+
9
+ ## Problem
10
+ The bundled `subagent` extension delegates work to separate `pi` processes with isolated
11
+ context windows. It streams output live, shows per-agent usage stats (turns, tokens, cost), and
12
+ supports abort — but after a run there's no retained record of what each subagent actually did
13
+ (files touched, decisions, blockers), and while a run is in flight there's no richer
14
+ dashboard-level view of what each agent is up to. There's also no way to influence a running
15
+ subagent: you can only watch or Ctrl+C the whole thing. For the ralph-style parallel dispatches
16
+ (up to 5 concurrent chores) this is a real blind spot.
17
+
18
+ ## Who benefits
19
+ The author, whenever running parallel subagent work (ralph pipeline chores, parallel research).
20
+ A live dashboard helps during runs; persisted per-run reports turn subagent work into a
21
+ reviewable after-action record (which also complements the HITL/summary flow). Mid-run steering
22
+ reduces wasted tokens from subagents heading the wrong way and having to restart.
23
+
24
+ ## Proposed outcome
25
+ Expand (or adopt, if a store extension fits) subagent tooling to give:
26
+
27
+ 1. **Live dashboard** — richer TUI during runs: per-agent status (phase, current tool call,
28
+ files touched), so you can see what each parallel subagent is doing without digging into
29
+ streamed output.
30
+ 2. **Persisted reports** — a per-run record per subagent (what it did, files changed,
31
+ decisions, blockers), stored for after-action review. Persistence should follow the repo
32
+ convention: write to `~/.pi/agent-cortex/`, tagged with project path for per-project slicing.
33
+ 3. **Mid-run steering** — send a message to a running subagent to change its direction
34
+ ("skip that, focus on X") rather than killing and restarting.
35
+
36
+ Decision from the interview: **evaluate the pi extension store first** — if a maintained
37
+ extension already covers dashboard + reports (and ideally steering), adopt it; otherwise expand
38
+ the bundled `subagent` extension. Building is a fallback, not the default.
39
+
40
+ ## Validity check
41
+ - Evidence we already have: the bundled `subagent` extension is a working base — it already
42
+ spawns `pi` subprocesses, streams, and collects usage data, so the hooks for a dashboard and
43
+ reports exist in some form. The repo has a data convention (`~/.pi/agent-cortex/`, per-project
44
+ tagging) that reports would slot into.
45
+ - Riskiest assumption: that mid-run steering is technically feasible — subagents run as separate
46
+ `pi` processes, so steering likely needs a message channel (e.g. a FIFO / IPC pipe into the
47
+ running process, or a PI-native way to inject input). If the process model doesn't allow input
48
+ mid-run, the interaction half collapses to "abort and re-dispatch with corrected instruction".
49
+ - What would invalidate this idea: if store extensions are all visibility-only (no steering) and
50
+ the process model rules out mid-run input, the remaining value is dashboard + reports only.
51
+ Also, if subagents use private pipes that block reading live state cheaply, the dashboard may
52
+ be constrained to what the streamer already shows.
53
+
54
+ ## Constraints
55
+ - Keep it lightweight and consistent with the repo's extension conventions (no internal LLM
56
+ calls in the extension itself).
57
+ - Should compose with ralph's parallel dispatch (up to 5 concurrent chores) without making the
58
+ TUI unusable.
59
+ - Steering must be safe: a message is advisory input to the subagent, not a force-kill;
60
+ abort stays available.
61
+
62
+ ## Next validation step
63
+ Two-part check before building: (1) search the pi extension store for subagent/dashboard/steering
64
+ extensions and evaluate against this wishlist; (2) verify whether a running `pi` subagent
65
+ process accepts input mid-run (checked against how the subagent extension spawns processes and
66
+ whether PI exposes any IPC/input channel) — this decides whether steering is scoped in.
67
+
68
+ ## Notes
69
+ Recorded 2026-08-25. Interview decisions: visibility = both live dashboard AND persisted
70
+ per-run reports; interaction = mid-run steering (not approve-gates, not pause/resume); path =
71
+ evaluate store first, fall back to expanding the bundled `subagent` extension. Complements
72
+ `unified-ci-pipeline` (pipeline visibility) and the ralph parallelism work in this repo.
@@ -0,0 +1,89 @@
1
+ # Idea: task-session-context
2
+
3
+ ## Status
4
+ Backlog idea (not implementation-ready)
5
+
6
+ ## Created
7
+ 2026-08-25
8
+
9
+ ## Problem
10
+ Coding sessions accumulate context turn by turn: every question, every tool call, every
11
+ tangent stays in the window until it's manually compacted or the session is restarted. Keeping
12
+ the context small and relevant is an active management burden — the author has to notice the
13
+ bloat, decide what matters, and intervene. Long conversations either burn tokens or require
14
+ frequent manual resets that drop useful memory.
15
+
16
+ ## Who benefits
17
+ The author, daily. Removing context management from the loop means sessions stay small and
18
+ focused no matter how long the overall thread runs. It also produces a durable, reviewable log
19
+ of work: each task's bead + summary memory is an audit trail of what was done and why.
20
+
21
+ ## Proposed outcome
22
+ A workflow organised around **tasks** instead of sessions. Whenever the user does something
23
+ discrete — asks a new question, records an idea, fleshes out a piece of work — it **begins a
24
+ task**:
25
+
26
+ 1. **Task = blank session with memory context.** The task starts a fresh, self-contained
27
+ session. It does NOT get every turn and tool call of the parent conversation — only access
28
+ to the accumulated **memories** of what came before (a summary/compressed view, not the raw
29
+ transcript).
30
+ 2. **Self-contained execution.** The task works on its own: it takes action, and **records
31
+ memories along the way** as it produces outcomes.
32
+ 3. **Single summary memory on completion.** The last thing a task does is write one summary
33
+ memory capturing its outcome. These memories are additive — the running memory store grows as
34
+ tasks complete.
35
+ 4. **Fresh session continues the thread.** Once the task completes, a new session starts with
36
+ access to the memories — including the new summary — so to the user it feels like one
37
+ continuous thread, but each underlying session stays small.
38
+
39
+ **Ties into beads (bd):** "the completion of a bead is the completion of a task". A bead becomes
40
+ the log of work completed: the task's outcome is recorded on/with the bead, the fresh session
41
+ starts, and the bead is accompanied by **referential memory** (a pointer/memory entry that links
42
+ the outcome to the bead). So the bead doubles as the durable record AND the context-boundary
43
+ marker: starting a bead is the trigger for task-session separation.
44
+
45
+ **The bead is the artifact — the session is disposable.** Once a task is complete, the whole
46
+ conversation context is no longer needed; the bead is the artifact. The current conversation is
47
+ the live example: recording this idea is a task, and when it's done the transcript of every turn
48
+ and tool call can be discarded — what remains is the record plus its summary/referential memory.
49
+ This is the load-bearing philosophical point of the idea: sessions exist to serve tasks, not the
50
+ other way around. Context is an implementation detail of a single task; the bead is the durable
51
+ unit of history.
52
+
53
+ The mechanism likely needs: a memory store (per-project, e.g. under `~/.pi/agent-cortex/` or a
54
+ records convention), a session-scoping rule (what memories a fresh task session is seeded with),
55
+ the summary-writing step, and the bead ↔ memory linkage.
56
+
57
+ ## Validity check
58
+ - Evidence we already have: the repo already runs on beads as the unit of work (bd), and the
59
+ ralph pipeline already treats beads as self-contained executable units — so task-per-bead
60
+ separation is a natural extension of an existing pattern. Existing ideas
61
+ (`record-ideas-as-beads`, `pi-task-memory`, `extension-manifests`) all push in the direction of
62
+ making agent-cortex the declared, memory-rich source of truth.
63
+ - Riskiest assumption: that a fresh session seeded with *memories only* (no raw transcript) is
64
+ sufficient context for a task — the compression of a conversation into memories must not lose
65
+ the specifics a task needs. Also: whether the harness (pi/Claude) even supports programmatic
66
+ session spawning with a seed context cleanly enough for this to feel seamless.
67
+ - What would invalidate this idea: if memory-as-context proves lossy in a way that keeps tasks
68
+ mis-executing (then the blank-session model needs the raw transcript after all, defeating the
69
+ point); or if the session boundary machinery (start/stop/spawn with seed) fights the harness.
70
+
71
+ ## Constraints
72
+ - Sessions must stay small — this is the whole point; memory is the interface, not the transcript.
73
+ - Memories are additive and durable; the summary memory is the handoff contract between tasks.
74
+ - The bead is the work log AND the context boundary: no task completion without its summary
75
+ memory and bead record.
76
+ - Must not break the existing task loops (ralph pipeline stages, HITL handoffs) — it should
77
+ compose with them, not fork them.
78
+
79
+ ## Next validation step
80
+ Flesh-out session (proposed by the author): nail down the trigger rules (what counts as "begin a
81
+ task"), the memory store schema and what a "memory" is vs a summary, how a fresh session gets
82
+ seeded, and how the bead carries the summary + referential memory. Then a spike: one task
83
+ completing with summary → fresh session seeded → verify the thread feels continuous.
84
+
85
+ ## Notes
86
+ Recorded 2026-08-25. The author explicitly wants to flesh this out later — this record captures
87
+ the working concept. It connects several existing ideas: `record-ideas-as-beads` (beads as the
88
+ unit of everything), `pi-task-memory` (memory as a first-class mechanism), and
89
+ `subagent-visibility` (visibility into what task sessions do).