@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.
- package/.agents/skills/style-versioning/SKILL.md +9 -8
- package/.github/workflows/ci.yml +190 -8
- package/AGENTS.md +27 -6
- package/CHANGELOG.md +72 -0
- package/README.md +156 -39
- package/agents/README.md +49 -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 +6 -3
- 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 +76 -1
- package/bin/installers/claude.mjs +376 -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 +6 -4
- 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/extension-manifests.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/record-ideas-as-beads.md +115 -0
- package/docs/ideas/subagent-visibility.md +72 -0
- package/docs/ideas/task-session-context.md +89 -0
- package/docs/ideas/unified-ci-pipeline.md +28 -0
- package/docs/research/skill-design-research.md +4 -4
- package/docs/research/skill-improvements-analysis.md +1 -1
- package/docs/skills/skill-anatomy.md +47 -2
- package/docs/technical-direction/migration-execution-labels.md +132 -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 +68 -3
- package/package.json +6 -4
- package/plugin.json +1 -1
- package/pnpm-workspace.yaml +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 +78 -58
- package/skills/engineering/grill-with-docs/REFERENCE.md +78 -0
- package/skills/engineering/grill-with-docs/SKILL.md +66 -63
- package/skills/engineering/improve-codebase-architecture/SKILL.md +66 -48
- package/skills/engineering/technical-direction/FORMAT.md +38 -0
- package/skills/engineering/technical-direction/SKILL.md +70 -77
- package/skills/planning/classify-bead/SKILL.md +59 -35
- package/skills/planning/create-task/REFERENCE.md +40 -0
- package/skills/planning/create-task/SKILL.md +70 -66
- package/skills/planning/create-task/scripts/create-chores.ts +6 -1
- package/skills/planning/create-task/scripts/node_modules/.bin/tsc +21 -0
- package/skills/planning/create-task/scripts/node_modules/.bin/tsserver +21 -0
- package/skills/planning/create-task/scripts/node_modules/.bin/tsx +21 -0
- package/skills/planning/prd-to-tasks/REFERENCE.md +66 -0
- package/skills/planning/prd-to-tasks/SKILL.md +86 -170
- package/skills/planning/record-idea/FORMAT.md +44 -0
- package/skills/planning/record-idea/SKILL.md +70 -53
- package/skills/planning/request-refactor-plan/FORMAT.md +45 -0
- package/skills/planning/request-refactor-plan/SKILL.md +57 -46
- package/skills/planning/write-a-prd/FORMAT.md +54 -0
- package/skills/planning/write-a-prd/SKILL.md +49 -48
- package/skills/planning/write-a-ticket/SKILL.md +68 -53
- package/skills/productivity/handoff/SKILL.md +12 -0
- package/skills/productivity/init-beads/SKILL.md +48 -40
- package/skills/productivity/validate-agent-dir/scripts/node_modules/.bin/tsc +21 -0
- package/skills/productivity/validate-agent-dir/scripts/node_modules/.bin/tsserver +21 -0
- package/skills/productivity/validate-agent-dir/scripts/node_modules/.bin/tsx +21 -0
- package/skills/productivity/write-a-skill/SKILL.md +2 -1
- package/skills/review/maintain-agent-docs/SKILL.md +71 -51
- package/skills/workflow/hitl-collab/SKILL.md +86 -17
- package/skills/workflow/ralph/REFERENCE.md +24 -19
- package/skills/workflow/ralph/SKILL.md +88 -74
- package/skills/workflow/run-pipeline-stage/REFERENCE.md +22 -0
- package/skills/workflow/run-pipeline-stage/SKILL.md +91 -93
- package/skills/workflow/run-pipeline-stage/scripts/generate-progress.test.ts +6 -107
- package/skills/workflow/run-pipeline-stage/scripts/generate-progress.ts +4 -2
- package/skills/workflow/run-pipeline-stage/scripts/node_modules/.bin/tsc +21 -0
- package/skills/workflow/run-pipeline-stage/scripts/node_modules/.bin/tsserver +21 -0
- package/skills/workflow/run-pipeline-stage/scripts/node_modules/.bin/tsx +21 -0
- package/skills/workflow/using-agent-skills/SKILL.md +122 -0
- package/test/claude-installer.test.mjs +248 -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 +130 -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.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
|
|
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
package/pnpm-workspace.yaml
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
|
+
}
|
|
@@ -1,94 +1,114 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: design-an-interface
|
|
3
|
-
description: Generate multiple radically different interface designs for a module
|
|
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
|
-
|
|
8
|
+
## When to use
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
15
|
+
## When NOT to use
|
|
15
16
|
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
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
|
-
|
|
21
|
+
## Philosophy / rationale
|
|
23
22
|
|
|
24
|
-
|
|
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
|
-
|
|
27
|
+
## Workflow
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
36
|
+
Ask: "What does this module need to do? Who will use it?"
|
|
32
37
|
|
|
33
|
-
|
|
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
|
-
|
|
36
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
69
|
+
Highlight where the designs diverge most — that divergence is where the decision lives.
|
|
61
70
|
|
|
62
|
-
|
|
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
|
-
|
|
73
|
+
## Red Flags
|
|
69
74
|
|
|
70
|
-
|
|
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
|
-
|
|
80
|
+
## Common Rationalizations
|
|
73
81
|
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
##
|
|
88
|
+
## Phase-gate checklists
|
|
78
89
|
|
|
79
|
-
|
|
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
|
-
|
|
96
|
+
## Cross-skill references
|
|
82
97
|
|
|
83
|
-
|
|
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
|
-
|
|
101
|
+
## Examples
|
|
86
102
|
|
|
87
|
-
|
|
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
|
-
##
|
|
106
|
+
## Verification checklist
|
|
90
107
|
|
|
91
|
-
-
|
|
92
|
-
-
|
|
93
|
-
-
|
|
94
|
-
-
|
|
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.
|