@jaybeeuu/agent-cortex 1.37.1 → 1.39.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 (135) hide show
  1. package/.agents/skills/style-versioning/SKILL.md +9 -8
  2. package/.github/workflows/ci.yml +190 -8
  3. package/AGENTS.md +27 -6
  4. package/CHANGELOG.md +72 -0
  5. package/README.md +156 -39
  6. package/agents/README.md +49 -13
  7. package/agents/plan/agent.md +51 -0
  8. package/agents/plan/claude/frontmatter.json +6 -0
  9. package/agents/plan/copilot/frontmatter.json +6 -0
  10. package/agents/plan/pi/frontmatter.json +6 -0
  11. package/agents/plan.agent.md +3 -0
  12. package/agents/ralph/agent.md +262 -0
  13. package/agents/ralph/claude/frontmatter.json +6 -0
  14. package/agents/ralph/claude/polling.md +31 -0
  15. package/agents/ralph/copilot/frontmatter.json +6 -0
  16. package/agents/ralph/copilot/polling.md +62 -0
  17. package/agents/ralph/pi/frontmatter.json +6 -0
  18. package/agents/ralph/pi/polling.md +74 -0
  19. package/agents/ralph-plan/agent.md +114 -0
  20. package/agents/ralph-plan/claude/frontmatter.json +6 -0
  21. package/agents/ralph-plan/copilot/frontmatter.json +6 -0
  22. package/agents/ralph-plan/pi/frontmatter.json +6 -0
  23. package/agents/ralph-plan.agent.md +6 -3
  24. package/agents/ralph.agent.md +83 -57
  25. package/agents/strategy/agent.md +104 -0
  26. package/agents/strategy/claude/frontmatter.json +6 -0
  27. package/agents/strategy/copilot/frontmatter.json +6 -0
  28. package/agents/strategy/pi/frontmatter.json +6 -0
  29. package/agents/strategy.agent.md +3 -0
  30. package/bin/agent-cortex.mjs +76 -1
  31. package/bin/installers/claude.mjs +376 -0
  32. package/bin/installers/copilot.mjs +129 -0
  33. package/bin/installers/pi.mjs +197 -0
  34. package/claude/.claude-plugin/plugin.json +1 -1
  35. package/claude/agents/plan.md +3 -1
  36. package/claude/agents/ralph-plan.md +6 -4
  37. package/claude/agents/strategy.md +3 -1
  38. package/claude/hooks/scripts/notify.mjs +54 -0
  39. package/claude/hooks.json +15 -0
  40. package/docs/claude-hooks.md +115 -0
  41. package/docs/ideas/auto-model-selection.md +75 -0
  42. package/docs/ideas/extension-manifests.md +75 -0
  43. package/docs/ideas/improve-pi-system-prompt.md +34 -0
  44. package/docs/ideas/model-selector.md +40 -0
  45. package/docs/ideas/pi-task-memory.md +34 -0
  46. package/docs/ideas/qa-extension.md +43 -0
  47. package/docs/ideas/record-ideas-as-beads.md +115 -0
  48. package/docs/ideas/subagent-visibility.md +72 -0
  49. package/docs/ideas/task-session-context.md +89 -0
  50. package/docs/ideas/unified-ci-pipeline.md +28 -0
  51. package/docs/research/skill-design-research.md +4 -4
  52. package/docs/research/skill-improvements-analysis.md +1 -1
  53. package/docs/skills/skill-anatomy.md +47 -2
  54. package/docs/technical-direction/migration-execution-labels.md +132 -0
  55. package/extensions/agent-modes/README.md +54 -0
  56. package/extensions/agent-modes/discover.test.ts +241 -0
  57. package/extensions/agent-modes/discover.ts +458 -0
  58. package/extensions/agent-modes/index.ts +13 -224
  59. package/extensions/agent-modes/node_modules/.bin/pi +21 -0
  60. package/extensions/agent-modes/node_modules/.bin/tsc +21 -0
  61. package/extensions/agent-modes/node_modules/.bin/tsserver +21 -0
  62. package/extensions/agent-modes/node_modules/.bin/tsx +21 -0
  63. package/extensions/agent-modes/package.json +20 -0
  64. package/extensions/agent-modes/tsconfig.json +12 -0
  65. package/extensions/subagent/README.md +19 -0
  66. package/extensions/subagent/index.ts +131 -45
  67. package/extensions/subagent/node_modules/.bin/pi +21 -0
  68. package/extensions/subagent/node_modules/.bin/pi-ai +21 -0
  69. package/extensions/subagent/node_modules/.bin/tsc +21 -0
  70. package/extensions/subagent/node_modules/.bin/tsserver +21 -0
  71. package/extensions/subagent/node_modules/.bin/tsx +21 -0
  72. package/extensions/subagent/package.json +23 -0
  73. package/extensions/subagent/tsconfig.json +12 -0
  74. package/extensions/subagent/wait.test.ts +202 -0
  75. package/extensions/subagent/wait.ts +118 -0
  76. package/hooks/claude/hooks.json +38 -0
  77. package/hooks/claude/scripts/notify.mjs +54 -0
  78. package/lib/cli.mjs +68 -3
  79. package/package.json +6 -4
  80. package/plugin.json +1 -1
  81. package/pnpm-workspace.yaml +1 -1
  82. package/scripts/build-copilot-agents.mjs +30 -0
  83. package/scripts/lib/compose-agent.mjs +241 -0
  84. package/skills/engineering/design-an-interface/SKILL.md +78 -58
  85. package/skills/engineering/grill-with-docs/REFERENCE.md +78 -0
  86. package/skills/engineering/grill-with-docs/SKILL.md +66 -63
  87. package/skills/engineering/improve-codebase-architecture/SKILL.md +66 -48
  88. package/skills/engineering/technical-direction/FORMAT.md +38 -0
  89. package/skills/engineering/technical-direction/SKILL.md +70 -77
  90. package/skills/planning/classify-bead/SKILL.md +59 -35
  91. package/skills/planning/create-task/REFERENCE.md +40 -0
  92. package/skills/planning/create-task/SKILL.md +70 -66
  93. package/skills/planning/create-task/scripts/create-chores.ts +6 -1
  94. package/skills/planning/create-task/scripts/node_modules/.bin/tsc +21 -0
  95. package/skills/planning/create-task/scripts/node_modules/.bin/tsserver +21 -0
  96. package/skills/planning/create-task/scripts/node_modules/.bin/tsx +21 -0
  97. package/skills/planning/prd-to-tasks/REFERENCE.md +66 -0
  98. package/skills/planning/prd-to-tasks/SKILL.md +86 -170
  99. package/skills/planning/record-idea/FORMAT.md +44 -0
  100. package/skills/planning/record-idea/SKILL.md +70 -53
  101. package/skills/planning/request-refactor-plan/FORMAT.md +45 -0
  102. package/skills/planning/request-refactor-plan/SKILL.md +57 -46
  103. package/skills/planning/write-a-prd/FORMAT.md +54 -0
  104. package/skills/planning/write-a-prd/SKILL.md +49 -48
  105. package/skills/planning/write-a-ticket/SKILL.md +68 -53
  106. package/skills/productivity/handoff/SKILL.md +12 -0
  107. package/skills/productivity/init-beads/SKILL.md +48 -40
  108. package/skills/productivity/validate-agent-dir/scripts/node_modules/.bin/tsc +21 -0
  109. package/skills/productivity/validate-agent-dir/scripts/node_modules/.bin/tsserver +21 -0
  110. package/skills/productivity/validate-agent-dir/scripts/node_modules/.bin/tsx +21 -0
  111. package/skills/productivity/write-a-skill/SKILL.md +2 -1
  112. package/skills/review/maintain-agent-docs/SKILL.md +71 -51
  113. package/skills/workflow/hitl-collab/SKILL.md +86 -17
  114. package/skills/workflow/ralph/REFERENCE.md +24 -19
  115. package/skills/workflow/ralph/SKILL.md +88 -74
  116. package/skills/workflow/run-pipeline-stage/REFERENCE.md +22 -0
  117. package/skills/workflow/run-pipeline-stage/SKILL.md +91 -93
  118. package/skills/workflow/run-pipeline-stage/scripts/generate-progress.test.ts +6 -107
  119. package/skills/workflow/run-pipeline-stage/scripts/generate-progress.ts +4 -2
  120. package/skills/workflow/run-pipeline-stage/scripts/node_modules/.bin/tsc +21 -0
  121. package/skills/workflow/run-pipeline-stage/scripts/node_modules/.bin/tsserver +21 -0
  122. package/skills/workflow/run-pipeline-stage/scripts/node_modules/.bin/tsx +21 -0
  123. package/skills/workflow/using-agent-skills/SKILL.md +122 -0
  124. package/test/claude-installer.test.mjs +248 -0
  125. package/test/cli.test.mjs +115 -2
  126. package/test/copilot-installer.test.mjs +298 -0
  127. package/test/installer.test.mjs +179 -0
  128. package/test/notify-hook.test.mjs +63 -0
  129. package/test/pi-installer.test.mjs +371 -0
  130. package/token-map.README.md +130 -0
  131. package/token-map.json +106 -0
  132. package/.github/workflows/ci.test.ts +0 -217
  133. package/.github/workflows/release.yml +0 -58
  134. package/scripts/build-claude-agents.mjs +0 -179
  135. 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.37.1",
3
+ "version": "1.39.0",
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 scripts/build-claude-agents.mjs",
34
+ "build:claude": "node bin/agent-cortex.mjs install claude --output 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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "agent-cortex",
3
3
  "description": "Personal Copilot CLI plugin with custom agents and skills",
4
- "version": "1.37.1",
4
+ "version": "1.39.0",
5
5
  "author": {
6
6
  "name": "jaybeeuu"
7
7
  },
@@ -1,6 +1,6 @@
1
1
  packages:
2
2
  - "."
3
- - "skills/*/scripts"
3
+ - "skills/**/scripts"
4
4
  - "extensions/*"
5
5
  allowBuilds:
6
6
  '@google/genai': false
@@ -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
+ }
@@ -1,94 +1,114 @@
1
1
  ---
2
2
  name: design-an-interface
3
- description: Generate multiple radically different interface designs for a module using parallel sub-agents. Use when user wants to design an API, explore interface options, compare module shapes, or mentions "design it twice".
3
+ description: "Generate multiple radically different interface designs for a module via parallel sub-agents, then compare and synthesize. Use when designing an API, exploring interface options, or \"design it twice\"."
4
4
  ---
5
5
 
6
6
  # Design an Interface
7
7
 
8
- Based on "Design It Twice" from "A Philosophy of Software Design": your first idea is unlikely to be the best. Generate multiple radically different designs, then compare.
8
+ ## When to use
9
9
 
10
- ## Workflow
11
-
12
- ### 1. Gather Requirements
10
+ - The user wants to design an API, module boundary, or public interface.
11
+ - The user asks to explore interface options or compare module shapes.
12
+ - The user mentions "design it twice" or says the first idea is unlikely to be the best.
13
+ - An interface design step needs a guided parallel exploration — `improve-codebase-architecture` invokes this skill for its multi-design step.
13
14
 
14
- Before designing, understand:
15
+ ## When NOT to use
15
16
 
16
- - [ ] What problem does this module solve?
17
- - [ ] Who are the callers? (other modules, external users, tests)
18
- - [ ] What are the key operations?
19
- - [ ] Any constraints? (performance, compatibility, existing patterns)
20
- - [ ] What should be hidden inside vs exposed?
17
+ - The user wants a single justified recommendation with tradeoff analysis — use `technical-direction` instead.
18
+ - The module's shape is already fixed by an upstream contract or framework, so exploring alternatives is wasted effort.
19
+ - The question is about implementation strategy, not interface shape.
21
20
 
22
- Ask: "What does this module need to do? Who will use it?"
21
+ ## Philosophy / rationale
23
22
 
24
- ### 2. Generate Designs (Parallel Sub-Agents)
23
+ - **Your first idea is unlikely to be the best.** "Design It Twice" (John Ousterhout, *A Philosophy of Software Design*) holds that the first design is almost never the strongest. Generating multiple radically different candidates forces the comparison that finds the best shape.
24
+ - **Contrast, not volume, creates insight.** Similar designs from parallel agents only confirm one idea. Each agent is assigned a different constraint so divergence is guaranteed by construction.
25
+ - **Interface shape is the whole problem here.** The process stops at the interface — implementation effort is irrelevant to the comparison, because a good interface usually makes the implementation easier, not harder. Judging designs by implementation cost rewards shallow modules.
25
26
 
26
- Spawn 3+ sub-agents simultaneously using Task tool. Each must produce a **radically different** approach.
27
+ ## Workflow
27
28
 
28
- ```
29
- Prompt template for each sub-agent:
29
+ 1. **Gather requirements.** Before designing, pin down:
30
+ - What problem does the module solve?
31
+ - Who are the callers (other modules, external users, tests)?
32
+ - What are the key operations?
33
+ - What constraints apply (performance, compatibility, existing patterns)?
34
+ - What should be hidden inside vs exposed?
30
35
 
31
- Design an interface for: [module description]
36
+ Ask: "What does this module need to do? Who will use it?"
32
37
 
33
- Requirements: [gathered requirements]
38
+ 2. **Generate designs with parallel sub-agents.** Spawn 3+ sub-agents simultaneously with {{TOOL:task}}, each assigned a **different** constraint to guarantee radical difference. Use this prompt template per agent:
34
39
 
35
- Constraints for this design: [assign a different constraint to each agent]
36
- - Agent 1: "Minimize method count - aim for 1-3 methods max"
37
- - Agent 2: "Maximize flexibility - support many use cases"
38
- - Agent 3: "Optimize for the most common case"
39
- - Agent 4: "Take inspiration from [specific paradigm/library]"
40
+ ```
41
+ Design an interface for: [module description]
40
42
 
41
- Output format:
42
- 1. Interface signature (types/methods)
43
- 2. Usage example (how caller uses it)
44
- 3. What this design hides internally
45
- 4. Trade-offs of this approach
46
- ```
43
+ Requirements: [gathered requirements]
47
44
 
48
- ### 3. Present Designs
45
+ Constraints for this design: [assign a different constraint to each agent]
46
+ - Agent 1: "Minimize method count — aim for 1-3 methods max"
47
+ - Agent 2: "Maximize flexibility — support many use cases"
48
+ - Agent 3: "Optimize for the most common case"
49
+ - Agent 4: "Take inspiration from [specific paradigm/library]"
49
50
 
50
- Show each design with:
51
+ Output format:
52
+ 1. Interface signature (types/methods)
53
+ 2. Usage example (how a caller uses it)
54
+ 3. What this design hides internally
55
+ 4. Trade-offs of this approach
56
+ ```
51
57
 
52
- 1. **Interface signature** - types, methods, params
53
- 2. **Usage examples** - how callers actually use it in practice
54
- 3. **What it hides** - complexity kept internal
58
+ Collect each agent's output with {{TOOL:read_agent}} when it finishes.
55
59
 
56
- Present designs sequentially so user can absorb each approach before comparison.
60
+ 3. **Present designs.** Show each design sequentially — signature, usage examples, what it hides — so the user absorbs each approach before any comparison happens.
57
61
 
58
- ### 4. Compare Designs
62
+ 4. **Compare designs.** Compare in prose, not tables, on:
63
+ - **Interface simplicity** — fewer methods, simpler params
64
+ - **General-purpose vs specialized** — flexibility vs focus
65
+ - **Implementation efficiency** — does the shape allow efficient internals?
66
+ - **Depth** — small interface hiding significant complexity (deep module, good) vs large interface with thin implementation (shallow module, avoid)
67
+ - **Ease of correct use** vs ease of misuse
59
68
 
60
- After showing all designs, compare them on:
69
+ Highlight where the designs diverge most — that divergence is where the decision lives.
61
70
 
62
- - **Interface simplicity**: fewer methods, simpler params
63
- - **General-purpose vs specialized**: flexibility vs focus
64
- - **Implementation efficiency**: does shape allow efficient internals?
65
- - **Depth**: small interface hiding significant complexity (good) vs large interface with thin implementation (bad)
66
- - **Ease of correct use** vs **ease of misuse**
71
+ 5. **Synthesize.** Ask: "Which design best fits your primary use case?" and "What elements from other designs are worth incorporating?" Often the best design combines insights from multiple options.
67
72
 
68
- Discuss trade-offs in prose, not tables. Highlight where designs diverge most.
73
+ ## Red Flags
69
74
 
70
- ### 5. Synthesize
75
+ - **Sub-agents converging on similar designs** — if two agents produce near-identical shapes, re-run with sharper, more opposed constraints. Similarity means the contrast step lost its value.
76
+ - **Skipping comparison** — jumping from presentation to a favorite design discards the whole point of exploring alternatives.
77
+ - **Sliding into implementation** — this process concerns interface shape only. Implementation details belong in a follow-up task.
78
+ - **Judging designs by implementation effort** — a design that looks harder to build may still be the deeper, better interface.
71
79
 
72
- Often the best design combines insights from multiple options. Ask:
80
+ ## Common Rationalizations
73
81
 
74
- - "Which design best fits your primary use case?"
75
- - "Any elements from other designs worth incorporating?"
82
+ | Rationalization | Rebuttal |
83
+ |---|---|
84
+ | "My first design is probably fine" | That is exactly the assumption "design it twice" exists to break. Generate the alternatives before committing. |
85
+ | "The agents will just produce similar things, so why bother" | Similarity is a failure mode you control — assign opposed constraints so divergence is guaranteed by construction. |
86
+ | "I can compare these myself, no need to present each one" | Sequential presentation forces you to articulate what each design actually is before judging it, which is where the insight comes from. |
76
87
 
77
- ## Evaluation Criteria
88
+ ## Phase-gate checklists
78
89
 
79
- From "A Philosophy of Software Design":
90
+ - [ ] Phase 1 complete: requirements gathered and constraints listed
91
+ - [ ] Phase 2 complete: 3+ designs returned, each constrained differently
92
+ - [ ] Phase 3 complete: all designs presented sequentially
93
+ - [ ] Phase 4 complete: comparison shared and divergence highlighted
94
+ - [ ] Phase 5 complete: candidate synthesized with user input
80
95
 
81
- **Interface simplicity**: Fewer methods, simpler params = easier to learn and use correctly.
96
+ ## Cross-skill references
82
97
 
83
- **General-purpose**: Can handle future use cases without changes. But beware over-generalization.
98
+ - `improve-codebase-architecture` invokes this skill for its multi-design step (step 5 of its workflow).
99
+ - Run `grill-with-docs` when the module's domain terminology is unsettled — design names should match the ubiquitous language.
84
100
 
85
- **Implementation efficiency**: Does interface shape allow efficient implementation? Or force awkward internals?
101
+ ## Examples
86
102
 
87
- **Depth**: Small interface hiding significant complexity = deep module (good). Large interface with thin implementation = shallow module (avoid).
103
+ Input: "Design the interface for a rate limiter module"
104
+ Output: four radically different interface sketches (minimal-method, flexible, common-case-optimized, paradigm-inspired), presented sequentially, compared on simplicity, depth, and trade-offs, then synthesized into one recommended shape with the user.
88
105
 
89
- ## Anti-Patterns
106
+ ## Verification checklist
90
107
 
91
- - Don't let sub-agents produce similar designs - enforce radical difference
92
- - Don't skip comparison - the value is in contrast
93
- - Don't implement - this is purely about interface shape
94
- - Don't evaluate based on implementation effort
108
+ - [ ] Requirements captured before any sub-agent is spawned
109
+ - [ ] At least 3 sub-agents spawned, each with a distinct constraint
110
+ - [ ] Each design reports signature, usage example, hidden complexity, and trade-offs
111
+ - [ ] Designs presented sequentially before any comparison
112
+ - [ ] Comparison covers simplicity, generality, efficiency, depth, and ease of correct use
113
+ - [ ] Synthesis asks the user which design fits their primary use case
114
+ - [ ] No implementation work performed — interface shape only
@@ -0,0 +1,78 @@
1
+ # Grill-with-docs reference
2
+
3
+ Rarely-needed detail the workflow points to when you must create or update project documents.
4
+
5
+ ## Finding the domain model
6
+
7
+ ### Single-context repos
8
+
9
+ ```
10
+ /
11
+ ├── CONTEXT.md
12
+ ├── docs/
13
+ │ └── adr/
14
+ │ ├── 0001-event-sourced-orders.md
15
+ │ └── 0002-postgres-for-write-model.md
16
+ └── src/
17
+ ```
18
+
19
+ ### Multi-context repos
20
+
21
+ A `CONTEXT-MAP.md` at the root points to where each context lives:
22
+
23
+ ```
24
+ /
25
+ ├── CONTEXT-MAP.md
26
+ ├── docs/
27
+ │ └── adr/ ← system-wide decisions
28
+ ├── src/
29
+ │ ├── ordering/
30
+ │ │ ├── CONTEXT.md
31
+ │ │ └── docs/adr/ ← context-specific decisions
32
+ │ └── billing/
33
+ │ ├── CONTEXT.md
34
+ │ └── docs/adr/
35
+ ```
36
+
37
+ Create files lazily — only when you have something to write. If no `CONTEXT.md` exists, create one when the first term resolves. If no `docs/adr/` exists, create it when the first ADR is needed.
38
+
39
+ ## CONTEXT.md entry format
40
+
41
+ One `# term` heading per entry with a plain-prose definition and cross-links to related terms:
42
+
43
+ ```md
44
+ # cancellation
45
+ An order may be cancelled only before fulfilment begins. See refund for the
46
+ post-fulfilment reversal path.
47
+ ```
48
+
49
+ Rules:
50
+
51
+ - Glossary only — no implementation details, no specs, no scratch notes.
52
+ - Prefer one concise definition over long paragraphs.
53
+ - Cross-link related terms so the vocabulary is navigable.
54
+
55
+ ## ADR format
56
+
57
+ One file per decision in `docs/adr/`, zero-padded sequence number plus kebab-case title:
58
+
59
+ ```
60
+ 0003-cancel-before-fulfilment-only.md
61
+ ```
62
+
63
+ Structure:
64
+
65
+ ```md
66
+ # Title
67
+
68
+ ## Context
69
+ What problem forced the decision and why the question arose.
70
+
71
+ ## Decision
72
+ The choice, stated precisely.
73
+
74
+ ## Consequences
75
+ What becomes easier or harder as a result; what a future reader must know.
76
+ ```
77
+
78
+ Offer an ADR only when all three hold — hard to reverse, surprising without context, real trade-off. If any is missing, skip it.