@jaybeeuu/agent-cortex 1.37.1 → 1.38.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.
- package/.agents/skills/style-versioning/SKILL.md +9 -8
- package/.github/workflows/ci.yml +190 -8
- package/AGENTS.md +25 -6
- package/CHANGELOG.md +42 -0
- package/README.md +132 -33
- package/agents/README.md +47 -13
- package/agents/plan/agent.md +51 -0
- package/agents/plan/claude/frontmatter.json +6 -0
- package/agents/plan/copilot/frontmatter.json +6 -0
- package/agents/plan/pi/frontmatter.json +6 -0
- package/agents/plan.agent.md +3 -0
- package/agents/ralph/agent.md +262 -0
- package/agents/ralph/claude/frontmatter.json +6 -0
- package/agents/ralph/claude/polling.md +31 -0
- package/agents/ralph/copilot/frontmatter.json +6 -0
- package/agents/ralph/copilot/polling.md +62 -0
- package/agents/ralph/pi/frontmatter.json +6 -0
- package/agents/ralph/pi/polling.md +74 -0
- package/agents/ralph-plan/agent.md +114 -0
- package/agents/ralph-plan/claude/frontmatter.json +6 -0
- package/agents/ralph-plan/copilot/frontmatter.json +6 -0
- package/agents/ralph-plan/pi/frontmatter.json +6 -0
- package/agents/ralph-plan.agent.md +5 -2
- package/agents/ralph.agent.md +83 -57
- package/agents/strategy/agent.md +104 -0
- package/agents/strategy/claude/frontmatter.json +6 -0
- package/agents/strategy/copilot/frontmatter.json +6 -0
- package/agents/strategy/pi/frontmatter.json +6 -0
- package/agents/strategy.agent.md +3 -0
- package/bin/agent-cortex.mjs +68 -1
- package/bin/installers/claude.mjs +262 -0
- package/bin/installers/copilot.mjs +129 -0
- package/bin/installers/pi.mjs +197 -0
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/agents/plan.md +3 -1
- package/claude/agents/ralph-plan.md +5 -3
- package/claude/agents/strategy.md +3 -1
- package/claude/hooks/scripts/notify.mjs +54 -0
- package/claude/hooks.json +15 -0
- package/docs/claude-hooks.md +115 -0
- package/docs/ideas/auto-model-selection.md +75 -0
- package/docs/ideas/improve-pi-system-prompt.md +34 -0
- package/docs/ideas/model-selector.md +40 -0
- package/docs/ideas/pi-task-memory.md +34 -0
- package/docs/ideas/qa-extension.md +43 -0
- package/docs/ideas/unified-ci-pipeline.md +28 -0
- package/docs/skills/skill-anatomy.md +9 -0
- package/extensions/agent-modes/README.md +54 -0
- package/extensions/agent-modes/discover.test.ts +241 -0
- package/extensions/agent-modes/discover.ts +458 -0
- package/extensions/agent-modes/index.ts +13 -224
- package/extensions/agent-modes/node_modules/.bin/pi +21 -0
- package/extensions/agent-modes/node_modules/.bin/tsc +21 -0
- package/extensions/agent-modes/node_modules/.bin/tsserver +21 -0
- package/extensions/agent-modes/node_modules/.bin/tsx +21 -0
- package/extensions/agent-modes/package.json +20 -0
- package/extensions/agent-modes/tsconfig.json +12 -0
- package/extensions/subagent/README.md +19 -0
- package/extensions/subagent/index.ts +131 -45
- package/extensions/subagent/node_modules/.bin/pi +21 -0
- package/extensions/subagent/node_modules/.bin/pi-ai +21 -0
- package/extensions/subagent/node_modules/.bin/tsc +21 -0
- package/extensions/subagent/node_modules/.bin/tsserver +21 -0
- package/extensions/subagent/node_modules/.bin/tsx +21 -0
- package/extensions/subagent/package.json +23 -0
- package/extensions/subagent/tsconfig.json +12 -0
- package/extensions/subagent/wait.test.ts +202 -0
- package/extensions/subagent/wait.ts +118 -0
- package/hooks/claude/hooks.json +38 -0
- package/hooks/claude/scripts/notify.mjs +54 -0
- package/lib/cli.mjs +62 -3
- package/package.json +6 -4
- package/plugin.json +1 -1
- package/scripts/build-copilot-agents.mjs +30 -0
- package/scripts/lib/compose-agent.mjs +241 -0
- package/skills/engineering/design-an-interface/SKILL.md +1 -1
- package/skills/engineering/improve-codebase-architecture/SKILL.md +1 -1
- package/skills/workflow/ralph/REFERENCE.md +2 -2
- package/skills/workflow/ralph/SKILL.md +6 -6
- package/skills/workflow/run-pipeline-stage/scripts/generate-progress.ts +4 -2
- package/skills/workflow/using-agent-skills/SKILL.md +122 -0
- package/test/cli.test.mjs +115 -2
- package/test/copilot-installer.test.mjs +298 -0
- package/test/installer.test.mjs +179 -0
- package/test/notify-hook.test.mjs +63 -0
- package/test/pi-installer.test.mjs +371 -0
- package/token-map.README.md +128 -0
- package/token-map.json +106 -0
- package/.github/workflows/ci.test.ts +0 -217
- package/.github/workflows/release.yml +0 -58
- package/scripts/build-claude-agents.mjs +0 -179
- package/scripts/publish-release.sh +0 -32
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jaybeeuu/agent-cortex",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.38.1",
|
|
4
4
|
"description": "Personal PI package with custom agents, skills, and extensions",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package"
|
|
@@ -29,11 +29,13 @@
|
|
|
29
29
|
"scripts": {
|
|
30
30
|
"build": "echo 'Build complete — no compilation needed for PI package'",
|
|
31
31
|
"lint": "pnpm typecheck",
|
|
32
|
-
"test": "pnpm -r test",
|
|
32
|
+
"test": "node --test test/*.test.mjs && pnpm -r test",
|
|
33
33
|
"typecheck": "pnpm -r typecheck",
|
|
34
|
-
"build:claude": "node
|
|
34
|
+
"build:claude": "node bin/agent-cortex.mjs install claude",
|
|
35
|
+
"build:copilot": "node scripts/build-copilot-agents.mjs",
|
|
35
36
|
"changeset": "changeset",
|
|
36
|
-
"version-packages": "changeset version && bash scripts/sync-plugin-version.sh",
|
|
37
|
+
"version-packages": "changeset version && bash scripts/sync-plugin-version.sh && pnpm build:claude && pnpm build:copilot",
|
|
38
|
+
"publish-package": "TARBALL=$(pnpm pack | tail -n 1) && npm publish \"$TARBALL\" --provenance --access public",
|
|
37
39
|
"release": "changeset publish",
|
|
38
40
|
"postinstall": "bash scripts/install-instructions.sh"
|
|
39
41
|
}
|
package/plugin.json
CHANGED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Regenerates the committed Copilot CLI flat agent files (agents/*.agent.md)
|
|
2
|
+
// from the canonical composable agent sources (agents/<name>/).
|
|
3
|
+
//
|
|
4
|
+
// This script is a thin wrapper over the shared install-time generator
|
|
5
|
+
// (bin/installers/copilot.mjs) — `agent-cortex install copilot` runs exactly the
|
|
6
|
+
// same code path, so install-time and build-time output can never diverge. CI
|
|
7
|
+
// runs this and checks `git diff --exit-code -- 'agents/*.agent.md'` to
|
|
8
|
+
// guarantee the committed flat files are never stale: a fresh clone (or a
|
|
9
|
+
// direct path plugin install) ships the Copilot plugin read-ready.
|
|
10
|
+
//
|
|
11
|
+
// agents/<name>.agent.md composed from agents/<name>/agent.md +
|
|
12
|
+
// agents/<name>/copilot/frontmatter.json +
|
|
13
|
+
// agents/<name>/copilot/<section>.md ({{SECTION:...}}),
|
|
14
|
+
// with {{TOOL:...}}/{{PATH:...}} substituted against
|
|
15
|
+
// the copilot column of token-map.json.
|
|
16
|
+
//
|
|
17
|
+
// The flat files live in the same agents/ directory the Copilot plugin scans
|
|
18
|
+
// (plugin.json "agents": "agents/"), next to the composable dirs they come
|
|
19
|
+
// from — the generator only writes *.agent.md files and never deletes.
|
|
20
|
+
//
|
|
21
|
+
// Run: pnpm build:copilot (node scripts/build-copilot-agents.mjs)
|
|
22
|
+
// Zero dependencies so it runs on the CI Node (20) and local Node alike.
|
|
23
|
+
|
|
24
|
+
import { join, dirname } from "node:path";
|
|
25
|
+
import { fileURLToPath } from "node:url";
|
|
26
|
+
import { installCopilot } from "../bin/installers/copilot.mjs";
|
|
27
|
+
|
|
28
|
+
const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
29
|
+
|
|
30
|
+
installCopilot({ root: ROOT, output: join(ROOT, "agents") });
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
// Shared composer: reads a composable agent directory (agents/<name>/) and emits
|
|
2
|
+
// the harness-specific agent content defined by agents/README.md. The Copilot
|
|
3
|
+
// generator (scripts/build-copilot-agents.mjs) and the Claude install-time generator
|
|
4
|
+
// (bin/installers/claude.mjs) compose from here, so the composable dirs are
|
|
5
|
+
// the single source of truth and the flat/generated files stay in lockstep.
|
|
6
|
+
//
|
|
7
|
+
// Composition per the agents/README.md contract:
|
|
8
|
+
// 1. {{SECTION:name}} is replaced with <dir>/<harness>/<name>.md content.
|
|
9
|
+
// 2. {{TOOL:key}} is replaced with the harness column of token-map.json.
|
|
10
|
+
// 3. {{PATH:arg}} with a named key resolves through token-map.json's paths
|
|
11
|
+
// table; anything else is a relative path and stays verbatim.
|
|
12
|
+
// Substitution order is SECTION → TOOL → PATH (matches extensions/agent-modes).
|
|
13
|
+
// Missing sections, unknown tool keys, or schema-invalid frontmatter are hard
|
|
14
|
+
// errors: a committed generated file must never ship a literal token or a
|
|
15
|
+
// silently dropped section.
|
|
16
|
+
//
|
|
17
|
+
// Options (the pi installer passes `dropNullTools`, `pluginRoot` and
|
|
18
|
+
// `resolveRelativePaths`; the copilot generator and claude installer use the strict defaults):
|
|
19
|
+
// dropNullTools replace null-mapped tool tokens with an empty string and
|
|
20
|
+
// warn instead of throwing (pi's prose rule per the
|
|
21
|
+
// token-map contract)
|
|
22
|
+
// pluginRoot override the plugin_root used for resolved paths
|
|
23
|
+
// (installers compute it from the actual install layout)
|
|
24
|
+
// resolveRelativePaths non-keyed {{PATH:...}} args resolve to pluginRoot/arg
|
|
25
|
+
// instead of staying verbatim (install time; the build
|
|
26
|
+
// scripts leave them bare for Copilot CLI resolution)
|
|
27
|
+
// warn callback for drop/omit warnings (default: no-op; the
|
|
28
|
+
// pi installer collects them into its result)
|
|
29
|
+
//
|
|
30
|
+
// Zero dependencies so it runs on the CI Node (20) and local Node alike.
|
|
31
|
+
|
|
32
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
33
|
+
import { join } from "node:path";
|
|
34
|
+
|
|
35
|
+
// ─── Token substitution ──────────────────────────────────────────────────────
|
|
36
|
+
|
|
37
|
+
const TOOL_RE = /\{\{TOOL:([^}]+)\}\}/g;
|
|
38
|
+
const PATH_RE = /\{\{PATH:([^}]+)\}\}/g;
|
|
39
|
+
|
|
40
|
+
function noop() {}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Substitute {{TOOL:...}} and {{PATH:...}} tokens in arbitrary content (agent
|
|
44
|
+
* bodies, skill files) against one harness column of token-map.json.
|
|
45
|
+
*
|
|
46
|
+
* - TOOL: unknown key → throw (the map must stay complete); null mapping → drop
|
|
47
|
+
* with a warning when `dropNullTools`, otherwise throw.
|
|
48
|
+
* - PATH: named key → resolve through the paths table (plugin_root overridable);
|
|
49
|
+
* non-keyed → join(pluginRoot, arg) when `resolveRelativePaths`, else verbatim.
|
|
50
|
+
* - SECTION tokens are *not* handled here — the agent composer resolves them from
|
|
51
|
+
* the harness's section files before calling this.
|
|
52
|
+
*
|
|
53
|
+
* @param {string} content Source text containing {{TOOL:...}}/{{PATH:...}} tokens
|
|
54
|
+
* @param {string} harness Harness id: "copilot" | "claude" | "pi"
|
|
55
|
+
* @param {object} tokenMap Parsed token-map.json (from loadTokenMap)
|
|
56
|
+
* @param {object} options { dropNullTools, pluginRoot, resolveRelativePaths, warn, context }
|
|
57
|
+
* @returns {string}
|
|
58
|
+
*/
|
|
59
|
+
export function substituteTokens(content, harness, tokenMap, options = {}) {
|
|
60
|
+
const { dropNullTools = false, pluginRoot, resolveRelativePaths = false, warn = noop, context = "" } = options;
|
|
61
|
+
const ctx = context ? `${context}: ` : "";
|
|
62
|
+
|
|
63
|
+
// 2. {{TOOL:key}} → token-map.json tools.<key>.<harness>
|
|
64
|
+
content = content.replace(TOOL_RE, (_token, key) => {
|
|
65
|
+
const entry = tokenMap.tools[key];
|
|
66
|
+
if (!entry) throw new Error(`${ctx}unknown tool token {{TOOL:${key}}} — extend token-map.json`);
|
|
67
|
+
const mapped = entry[harness];
|
|
68
|
+
if (mapped == null) {
|
|
69
|
+
if (dropNullTools) {
|
|
70
|
+
warn(`tool token {{TOOL:${key}}} has no ${harness} mapping — dropped`);
|
|
71
|
+
return "";
|
|
72
|
+
}
|
|
73
|
+
throw new Error(`${ctx}tool token {{TOOL:${key}}} has no ${harness} mapping (null) — drop the token in agent.md first`);
|
|
74
|
+
}
|
|
75
|
+
return mapped;
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// 3. {{PATH:arg}} → named keys via token-map.json paths; relative paths verbatim
|
|
79
|
+
// (or resolved against pluginRoot when the installer option is set)
|
|
80
|
+
content = content.replace(PATH_RE, (_token, arg) =>
|
|
81
|
+
resolvePath(arg, harness, tokenMap, { pluginRoot, resolveRelativePaths }),
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
return content;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Compose the harness-specific agent content from a composable agent directory.
|
|
89
|
+
*
|
|
90
|
+
* @param {string} root Package root (contains agents/ and token-map.json)
|
|
91
|
+
* @param {string} name Agent directory name (e.g. "ralph", "plan")
|
|
92
|
+
* @param {string} harness Harness id: "copilot" | "claude" | "pi"
|
|
93
|
+
* @returns {{ name: string, description: string, tools: string[], model?: string, argumentHint?: string, body: string }}
|
|
94
|
+
*/
|
|
95
|
+
export function composeAgent(root, name, harness, options = {}) {
|
|
96
|
+
const { dropNullTools = false, pluginRoot, resolveRelativePaths = false, warn = noop } = options;
|
|
97
|
+
const agentDir = join(root, "agents", name);
|
|
98
|
+
const frontmatter = parseFrontmatter(join(agentDir, harness, "frontmatter.json"), name, harness);
|
|
99
|
+
const tokenMap = loadTokenMap(root);
|
|
100
|
+
|
|
101
|
+
let body = readFileSync(join(agentDir, "agent.md"), "utf-8");
|
|
102
|
+
|
|
103
|
+
// 1. {{SECTION:name}} → <harness>/<name>.md (may itself contain TOOL/PATH tokens)
|
|
104
|
+
body = body.replace(/\{\{SECTION:([^}]+)\}\}/g, (_token, section) => {
|
|
105
|
+
const sectionPath = join(agentDir, harness, `${section}.md`);
|
|
106
|
+
if (!existsSync(sectionPath)) {
|
|
107
|
+
throw new Error(`agent "${name}": missing section "${section}" for harness "${harness}" (${sectionPath})`);
|
|
108
|
+
}
|
|
109
|
+
return readFileSync(sectionPath, "utf-8").trim();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// 2. + 3. TOOL/PATH substitution (contextualised for error messages)
|
|
113
|
+
body = substituteTokens(body, harness, tokenMap, {
|
|
114
|
+
dropNullTools,
|
|
115
|
+
pluginRoot,
|
|
116
|
+
resolveRelativePaths,
|
|
117
|
+
warn,
|
|
118
|
+
context: `agent "${name}"`,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
return { ...frontmatter, body: body.trim() };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ─── Frontmatter ─────────────────────────────────────────────────────────────
|
|
125
|
+
|
|
126
|
+
const REQUIRED_FIELDS = ["name", "description", "tools"];
|
|
127
|
+
const KNOWN_OPTIONAL_FIELDS = ["model", "argumentHint"];
|
|
128
|
+
|
|
129
|
+
/** Parse and schema-check a harness frontmatter.json (strict, per agents/README.md). */
|
|
130
|
+
function parseFrontmatter(path, name, harness) {
|
|
131
|
+
const raw = readFileSync(path, "utf-8");
|
|
132
|
+
let fm;
|
|
133
|
+
try {
|
|
134
|
+
fm = JSON.parse(raw);
|
|
135
|
+
} catch (err) {
|
|
136
|
+
throw new Error(`agent "${name}": invalid JSON in ${path}: ${err.message}`);
|
|
137
|
+
}
|
|
138
|
+
if (typeof fm !== "object" || fm === null) {
|
|
139
|
+
throw new Error(`agent "${name}": ${path} must be a JSON object`);
|
|
140
|
+
}
|
|
141
|
+
for (const field of REQUIRED_FIELDS) {
|
|
142
|
+
if (!(field in fm)) throw new Error(`agent "${name}": ${path} missing required field "${field}" (harness ${harness})`);
|
|
143
|
+
}
|
|
144
|
+
if (typeof fm.name !== "string" || fm.name.trim() === "") throw new Error(`agent "${name}": ${path} name must be a non-empty string`);
|
|
145
|
+
if (typeof fm.description !== "string" || fm.description.trim() === "") throw new Error(`agent "${name}": ${path} description must be a non-empty string`);
|
|
146
|
+
if (!Array.isArray(fm.tools) || fm.tools.length === 0 || !fm.tools.every((t) => typeof t === "string")) {
|
|
147
|
+
throw new Error(`agent "${name}": ${path} tools must be a non-empty array of strings`);
|
|
148
|
+
}
|
|
149
|
+
for (const key of Object.keys(fm)) {
|
|
150
|
+
if (![...REQUIRED_FIELDS, ...KNOWN_OPTIONAL_FIELDS].includes(key)) {
|
|
151
|
+
throw new Error(`agent "${name}": ${path} unknown field "${key}" — strict schema per agents/README.md`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
name: fm.name,
|
|
156
|
+
description: fm.description,
|
|
157
|
+
tools: fm.tools,
|
|
158
|
+
...(typeof fm.model === "string" ? { model: fm.model } : {}),
|
|
159
|
+
...(typeof fm.argumentHint === "string" ? { argumentHint: fm.argumentHint } : {}),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ─── Path resolution ─────────────────────────────────────────────────────────
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Resolve a {{PATH:...}} argument for a harness's generated output.
|
|
167
|
+
* Named keys resolve through token-map.json's paths table (recursively for
|
|
168
|
+
* {base, relative} specs); non-keyed args are relative paths, kept verbatim —
|
|
169
|
+
* the installer beads own precise runtime path resolution (see token-map.README.md).
|
|
170
|
+
* With the installer options (`pluginRoot`, `resolveRelativePaths`) the
|
|
171
|
+
* plugin_root base and bare relative paths resolve against the actual install
|
|
172
|
+
* layout instead.
|
|
173
|
+
*/
|
|
174
|
+
function resolvePath(arg, harness, tokenMap, opts = {}) {
|
|
175
|
+
const entry = tokenMap.paths?.[arg];
|
|
176
|
+
if (!entry) {
|
|
177
|
+
if (opts.resolveRelativePaths && opts.pluginRoot) return join(opts.pluginRoot, arg);
|
|
178
|
+
return arg;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const spec = entry[harness];
|
|
182
|
+
if (typeof spec === "string") {
|
|
183
|
+
if (arg === "plugin_root" && opts.pluginRoot) return opts.pluginRoot;
|
|
184
|
+
return spec;
|
|
185
|
+
}
|
|
186
|
+
if (spec && typeof spec === "object" && typeof spec.base === "string" && typeof spec.relative === "string") {
|
|
187
|
+
return join(resolvePath(spec.base, harness, tokenMap, opts), spec.relative);
|
|
188
|
+
}
|
|
189
|
+
throw new Error(`unresolvable {{PATH:${arg}}} for harness "${harness}" — extend token-map.json`);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Translate a frontmatter tool list into one harness's tool names.
|
|
194
|
+
*
|
|
195
|
+
* Canonical keys resolve through token-map.json's tools table; a null mapping
|
|
196
|
+
* means the harness has no equivalent and the entry is omitted (the contract's
|
|
197
|
+
* tool-list rule); a name that is not a canonical key passes through untouched
|
|
198
|
+
* (it may be a native tool, e.g. pi's fetch_content). Mirrors the runtime
|
|
199
|
+
* translation in extensions/agent-modes/discover.ts.
|
|
200
|
+
*
|
|
201
|
+
* @param {string[]} tools Canonical tool names from a harness frontmatter.json
|
|
202
|
+
* @param {object} tokenMap Parsed token-map.json (from loadTokenMap)
|
|
203
|
+
* @param {string} harness Harness id: "copilot" | "claude" | "pi"
|
|
204
|
+
* @param {object} options { warn }
|
|
205
|
+
* @returns {string[]}
|
|
206
|
+
*/
|
|
207
|
+
export function translateToolList(tools, tokenMap, harness, { warn = noop } = {}) {
|
|
208
|
+
const out = [];
|
|
209
|
+
for (const tool of tools) {
|
|
210
|
+
const mapping = tokenMap.tools[tool];
|
|
211
|
+
if (mapping === undefined) {
|
|
212
|
+
out.push(tool);
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
const mapped = mapping[harness];
|
|
216
|
+
if (mapped == null) {
|
|
217
|
+
warn(`tool "${tool}" has no ${harness} mapping — omitted from the tool list`);
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
out.push(mapped);
|
|
221
|
+
}
|
|
222
|
+
return out;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// ─── Token map ───────────────────────────────────────────────────────────────
|
|
226
|
+
|
|
227
|
+
/** Load token-map.json from the package root; missing/incomplete maps hard-fail. */
|
|
228
|
+
export function loadTokenMap(root) {
|
|
229
|
+
const path = join(root, "token-map.json");
|
|
230
|
+
let raw;
|
|
231
|
+
try {
|
|
232
|
+
raw = readFileSync(path, "utf-8");
|
|
233
|
+
} catch {
|
|
234
|
+
throw new Error(`missing ${path} — the composer requires token-map.json`);
|
|
235
|
+
}
|
|
236
|
+
const parsed = JSON.parse(raw);
|
|
237
|
+
if (!parsed.tools || !parsed.paths) {
|
|
238
|
+
throw new Error(`${path}: expected "tools" and "paths" tables`);
|
|
239
|
+
}
|
|
240
|
+
return parsed;
|
|
241
|
+
}
|
|
@@ -23,7 +23,7 @@ Ask: "What does this module need to do? Who will use it?"
|
|
|
23
23
|
|
|
24
24
|
### 2. Generate Designs (Parallel Sub-Agents)
|
|
25
25
|
|
|
26
|
-
Spawn 3+ sub-agents simultaneously using
|
|
26
|
+
Spawn 3+ sub-agents simultaneously using {{TOOL:task}}. Each must produce a **radically different** approach.
|
|
27
27
|
|
|
28
28
|
```
|
|
29
29
|
Prompt template for each sub-agent:
|
|
@@ -13,7 +13,7 @@ A **deep module** (John Ousterhout, "A Philosophy of Software Design") has a sma
|
|
|
13
13
|
|
|
14
14
|
### 1. Explore the codebase
|
|
15
15
|
|
|
16
|
-
Explore the codebase using your available tools (bash, rg, view, glob
|
|
16
|
+
Explore the codebase using your available tools ({{TOOL:bash}}, {{TOOL:rg}}, {{TOOL:view}}, {{TOOL:glob}}). Navigate organically and note where you experience friction:
|
|
17
17
|
|
|
18
18
|
- Where does understanding one concept require bouncing between many small files?
|
|
19
19
|
- Where are modules so shallow that the interface is nearly as complex as the implementation?
|
|
@@ -10,7 +10,7 @@ Detailed procedures for the fleet orchestration workflow. See [SKILL.md](./SKILL
|
|
|
10
10
|
2. **Read its `stage:*` label** from `bd show <id>`.
|
|
11
11
|
3. **Load the universal stage runner prompt** from `skills/workflow/run-pipeline-stage/prompts/stage-runner.md`.
|
|
12
12
|
4. **Read the parent task context**: follow the `parent-child` dependency to the parent task bead, run `bd show <parent-id>` to get the full task description.
|
|
13
|
-
5. **Ensure branch + worktree exist for this parent task** (see _Feature branches and worktrees_ below), then note the worktree path — you'll pass it as `cwd` to
|
|
13
|
+
5. **Ensure branch + worktree exist for this parent task** (see _Feature branches and worktrees_ below), then note the worktree path — you'll pass it as `cwd` to {{TOOL:task}}.
|
|
14
14
|
6. **Fill in the prompt** — replace placeholders with:
|
|
15
15
|
- Stage from bead label (`stage:<stage>`) for `<stage>`
|
|
16
16
|
- `bd prime` output (held in memory from initialization)
|
|
@@ -18,7 +18,7 @@ Detailed procedures for the fleet orchestration workflow. See [SKILL.md](./SKILL
|
|
|
18
18
|
- For `fix` stage: `FILES_CHANGED` from the preceding stage's report (the required changes come from the fix bead's own description — read via `bd show <fix-id>`)
|
|
19
19
|
- For `verify`, `review`, `document` stages: `SUMMARY` and `FILES_CHANGED` from the preceding stage's report
|
|
20
20
|
- Bead ID and log file path (`.agent-cortex/ralph/ralph-<bead-id>.log`)
|
|
21
|
-
7. **Call the
|
|
21
|
+
7. **Call the {{TOOL:task}} tool** with the filled prompt as `prompt` and the worktree path as `cwd`. Store the returned agentId — you need it for step 8 and for log polling.
|
|
22
22
|
8. **Map** the agent ID to the bead ID in memory for lookup on completion.
|
|
23
23
|
|
|
24
24
|
---
|
|
@@ -35,7 +35,7 @@ Run once at startup:
|
|
|
35
35
|
- **Task or other type without an `implementation-type:*` label**: unexpected here — classification is a planning-stage concern already applied by `create-task`. Do not invoke `classify-bead`; treat as needs-refinement and note for the shutdown summary.
|
|
36
36
|
- **Task labelled `implementation-type:hitl`**: skip — record the bead ID for the **Pending Human Action** summary at shutdown.
|
|
37
37
|
7. For each ready chore bead (up to 5), **dispatch** it (see _Dispatching a chore bead_ in REFERENCE.md). Chores for a parent bead must run from that parent's worktree.
|
|
38
|
-
8. **If** any chore beads were dispatched in step 7, start the **poll timer
|
|
38
|
+
8. **If** any chore beads were dispatched in step 7, start the **poll timer** (Copilot only): run `sleep 120` as a background bash process and hold its shellId in memory. PI has no timer — keep a `wait_for_agents` call outstanding over every in-flight agent ID instead (see the Event Loop note). **Otherwise**, if HITL gate beads are pending (check `bd list -l lifecycle:feature-pr -l implementation-type:hitl` and `bd list -l awaiting-epic-pr-merge`), proceed to **HITL Pause** (see REFERENCE.md) immediately.
|
|
39
39
|
9. Regenerate `.agent-cortex/ralph/progress.md`:
|
|
40
40
|
```bash
|
|
41
41
|
# workspace must be the absolute path you cd'd into — never . or $(pwd)
|
|
@@ -47,7 +47,7 @@ Run once at startup:
|
|
|
47
47
|
|
|
48
48
|
## Event Loop
|
|
49
49
|
|
|
50
|
-
After initialization, wait for background agents or the poll timer to complete. On each completion notification:
|
|
50
|
+
After initialization, wait for background agents or the poll timer to complete. Completion detection is harness-specific: the `sleep 120` poll timer described below is the **Copilot** mechanism. **PI** replaces it with the blocking `wait_for_agents` tool — it returns as soon as any in-flight agent finishes, so each return plays the role of a poll-timer tick with its completed-agent result attached; there is no timer to start or kill on PI. On each completion notification:
|
|
51
51
|
|
|
52
52
|
### Poll timer completed
|
|
53
53
|
|
|
@@ -59,7 +59,7 @@ After initialization, wait for background agents or the poll timer to complete.
|
|
|
59
59
|
### Background agent completed
|
|
60
60
|
|
|
61
61
|
1. **Poll that bead's log** to flush any final lines.
|
|
62
|
-
2. **Read** the completed agent's full output with `read_agent
|
|
62
|
+
2. **Read** the completed agent's full output with {{TOOL:read_agent}} (on PI the completed result arrives in the `wait_for_agents` response; read_agent remains a fallback for a specific result).
|
|
63
63
|
3. **Identify the bead**: parse the `---REPORT---` block for `BEAD_ID` and `STAGE_COMPLETED`.
|
|
64
64
|
4. **Close** the chore bead: `bd close <bead-id>`.
|
|
65
65
|
5. **Handle stage outcome**:
|
|
@@ -90,10 +90,10 @@ After initialization, wait for background agents or the poll timer to complete.
|
|
|
90
90
|
- **Always** run in **foreground** (interactive) mode. If you find yourself executing as a background task, immediately surface a warning to the user and ask them to re-run you in foreground mode.
|
|
91
91
|
- **Never** write, edit, or create source code or documentation yourself.
|
|
92
92
|
- **Never** edit bead task files directly — only use `bd` commands.
|
|
93
|
-
- **ALWAYS call the
|
|
93
|
+
- **ALWAYS call the {{TOOL:task}} tool to spawn subagents** — never use {{TOOL:bash}}, never run stages inline, never use any other tool. This is your only spawning mechanism.
|
|
94
94
|
- **Always** derive orchestration state from beads — do not maintain a separate state file.
|
|
95
|
-
- **Always** include the full `bd prime` output verbatim in every
|
|
96
|
-
- **Always** keep the poll timer running — restart it immediately after it fires — **unless** the HITL pause condition is met (no chores in-flight, no `stage:*` chores ready, HITL gate beads pending), in which case proceed to **HITL Pause** (see REFERENCE.md) and stop instead.
|
|
95
|
+
- **Always** include the full `bd prime` output verbatim in every {{TOOL:task}} tool prompt.
|
|
96
|
+
- **Always** keep the poll timer running (Copilot) — restart it immediately after it fires — **unless** the HITL pause condition is met (no chores in-flight, no `stage:*` chores ready, HITL gate beads pending), in which case proceed to **HITL Pause** (see REFERENCE.md) and stop instead. On PI there is no timer to keep alive — always keep one `wait_for_agents` call outstanding while agents are in-flight.
|
|
97
97
|
- **Never** post empty poll updates to chat — only surface new log content.
|
|
98
98
|
- **Max 5** tasks in-flight at once.
|
|
99
99
|
- **Max fix rounds** per task as defined by `maxFixRounds` in `skills/planning/create-task/pipeline.json`.
|
|
@@ -50,8 +50,10 @@ const STATUS_CHAR_MAP: Record<string, BeadStatus> = {
|
|
|
50
50
|
export function parseListLine(
|
|
51
51
|
line: string,
|
|
52
52
|
): { id: string; status: BeadStatus; title: string } | null {
|
|
53
|
-
//
|
|
54
|
-
|
|
53
|
+
// Formats accepted:
|
|
54
|
+
// {STATUS_CHAR} {ID} P{N} {TITLE} — current bd list output
|
|
55
|
+
// {STATUS_CHAR} {ID} ● P{N} {TITLE} — older bd (● before P{N} is optional)
|
|
56
|
+
const match = line.trim().match(/^([○◐●✓❄])\s+(\S+)\s+(?:●\s+)?P\d+\s+(.+)$/);
|
|
55
57
|
if (!match) return null;
|
|
56
58
|
const [, statusChar, id, title] = match;
|
|
57
59
|
const status = STATUS_CHAR_MAP[statusChar];
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: using-agent-skills
|
|
3
|
+
description: Routes incoming work to the right skill and enforces core agent behaviors. Use when starting a session, deciding which skill applies, or when you need to check "which skill should I use?".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Using Agent Skills
|
|
7
|
+
|
|
8
|
+
## When to use
|
|
9
|
+
|
|
10
|
+
- Starting a new session and need to orient on available workflows.
|
|
11
|
+
- A task arrives and you need to decide which skill applies.
|
|
12
|
+
- The user asks "which skill should I use for this?"
|
|
13
|
+
- You want to verify that core operating behaviors are being followed.
|
|
14
|
+
|
|
15
|
+
## Workflow
|
|
16
|
+
|
|
17
|
+
When a task arrives, identify the development phase and apply the corresponding skill:
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
Task arrives
|
|
21
|
+
│
|
|
22
|
+
├── Don't know what to build yet? ──────→ grill-me
|
|
23
|
+
├── Have a rough idea? ─────────────────→ record-idea
|
|
24
|
+
├── New feature / PRD needed? ──────────→ write-a-prd
|
|
25
|
+
├── PRD approved, need tasks? ──────────→ prd-to-tasks
|
|
26
|
+
├── Need a single task tracked? ────────→ create-task
|
|
27
|
+
├── Need to plan a feature end-to-end? ─→ plan
|
|
28
|
+
├── Running beads / task execution? ────→ ralph
|
|
29
|
+
│ └── Single stage? ─────────────────→ run-pipeline-stage
|
|
30
|
+
├── Implementing code? ────────────────→ tdd
|
|
31
|
+
│ ├── Designing an interface? ───────→ design-an-interface
|
|
32
|
+
│ └── Need style conventions? ───────→ style-code
|
|
33
|
+
├── Reviewing code? ───────────────────→ style-code
|
|
34
|
+
│ ├── Security concerns? ────────────→ review-security
|
|
35
|
+
│ └── Reviewing tests? ──────────────→ style-tests
|
|
36
|
+
├── Writing tests? ────────────────────→ style-tests
|
|
37
|
+
├── Writing docs? ─────────────────────→ style-documentation
|
|
38
|
+
├── Writing external comms? ───────────→ style-comms
|
|
39
|
+
├── Stress-testing a plan? ────────────→ grill-with-docs
|
|
40
|
+
├── Planning a refactor? ──────────────→ request-refactor-plan
|
|
41
|
+
├── Evaluating architecture? ──────────→ technical-direction
|
|
42
|
+
├── Finding refactor opportunities? ───→ improve-codebase-architecture
|
|
43
|
+
├── Need task tracking? ───────────────→ bd-tool
|
|
44
|
+
├── Need human review / HITL? ─────────→ hitl-collab
|
|
45
|
+
├── Need a handoff doc? ───────────────→ handoff
|
|
46
|
+
├── Writing a ticket for Jira? ────────→ write-a-ticket
|
|
47
|
+
├── Creating a new skill? ─────────────→ write-a-skill
|
|
48
|
+
├── Refactoring a skill? ──────────────→ refactor-skill
|
|
49
|
+
├── Updating agent docs? ──────────────→ maintain-agent-docs
|
|
50
|
+
└── Setting up beads in a new project? → init-beads
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Core Operating Behaviors
|
|
54
|
+
|
|
55
|
+
These behaviors apply at all times, across all skills. They are non-negotiable.
|
|
56
|
+
|
|
57
|
+
### 1. Surface Assumptions
|
|
58
|
+
|
|
59
|
+
Before implementing anything non-trivial, explicitly state your assumptions. Don't silently fill in ambiguous requirements — surface uncertainty early, it's cheaper than rework.
|
|
60
|
+
|
|
61
|
+
### 2. Manage Confusion Actively
|
|
62
|
+
|
|
63
|
+
When you encounter inconsistencies, conflicting requirements, or unclear specifications: **STOP**. Name the specific confusion, present the tradeoff or ask the clarifying question, and wait for resolution before continuing.
|
|
64
|
+
|
|
65
|
+
### 3. Push Back When Warranted
|
|
66
|
+
|
|
67
|
+
You are not a yes-machine. When an approach has clear problems, point out the issue directly, explain the concrete downside, propose an alternative, and accept the human's decision if they override with full information.
|
|
68
|
+
|
|
69
|
+
### 4. Enforce Simplicity
|
|
70
|
+
|
|
71
|
+
Your natural tendency is to overcomplicate. Before finishing any implementation, ask: Can this be done in fewer lines? Are these abstractions earning their complexity? Prefer the boring, obvious solution.
|
|
72
|
+
|
|
73
|
+
### 5. Maintain Scope Discipline
|
|
74
|
+
|
|
75
|
+
Touch only what you're asked to touch. Do not clean up orthogonal code, refactor adjacent systems, or add features not in the spec. Surgical precision, not unsolicited renovation.
|
|
76
|
+
|
|
77
|
+
### 6. Verify, Don't Assume
|
|
78
|
+
|
|
79
|
+
A task is not complete until verification passes. "Seems right" is never sufficient — there must be evidence: passing tests, build output, or runtime data.
|
|
80
|
+
|
|
81
|
+
### 7. Flag Persistent Failures
|
|
82
|
+
|
|
83
|
+
When a tool or command fails repeatedly, stop and flag it to the user for investigation. Don't paper over failures with workarounds — workarounds waste tokens and hide real problems. Describe what failed, how many times, and any obvious patterns. Let the user decide whether to investigate in a dedicated session. Inefficiencies and failures should be squashed, not hidden.
|
|
84
|
+
|
|
85
|
+
### 8. Stay On Target
|
|
86
|
+
|
|
87
|
+
Stick to the task at hand. If you discover work that's outside the current scope — a tangential bug, an adjacent improvement, a missing doc — flag it to the user but don't pursue it. If the side quest blocks or slows your progress, stop and report it. Otherwise, note it and get back on track. Scope creep mid-task burns tokens and dilutes focus.
|
|
88
|
+
|
|
89
|
+
## Lifecycle Sequence
|
|
90
|
+
|
|
91
|
+
For a complete feature, the typical skill sequence is:
|
|
92
|
+
|
|
93
|
+
1. `grill-me` → Extract what the user actually wants
|
|
94
|
+
2. `record-idea` → Refine vague ideas
|
|
95
|
+
3. `write-a-prd` → Define what we're building
|
|
96
|
+
4. `prd-to-tasks` → Break into verifiable chunks
|
|
97
|
+
5. `plan` → Orchestrate the full flow end-to-end
|
|
98
|
+
6. `ralph` → Execute all pending beads
|
|
99
|
+
7. `tdd` → Prove each slice works
|
|
100
|
+
8. `style-code` → Review before merge
|
|
101
|
+
9. `review-security` → Check for secrets
|
|
102
|
+
10. `git-workflow` → Clean commit history
|
|
103
|
+
|
|
104
|
+
Not every task needs every skill. A bug fix might only need: `tdd` → `style-code` → `review-security`.
|
|
105
|
+
|
|
106
|
+
## Red Flags
|
|
107
|
+
|
|
108
|
+
- Starting work without checking which skill applies.
|
|
109
|
+
- Skipping verification because "it looks right."
|
|
110
|
+
- Making wrong assumptions without stating them.
|
|
111
|
+
- Overcomplicating code when a simple solution exists.
|
|
112
|
+
- Modifying code orthogonal to the task scope.
|
|
113
|
+
- Going on side-quests instead of staying on target.
|
|
114
|
+
- Going on side-quests instead of staying on target.
|
|
115
|
+
- Going on side-quests instead of staying on target.
|
|
116
|
+
|
|
117
|
+
## Verification checklist
|
|
118
|
+
|
|
119
|
+
- [ ] Correct skill identified before starting work.
|
|
120
|
+
- [ ] Core operating behaviors followed throughout.
|
|
121
|
+
- [ ] Verification completed with evidence (tests, build, runtime).
|
|
122
|
+
- [ ] Scope limited to the task at hand.
|