@jaybeeuu/agent-cortex 1.43.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/.agents/skills/style-versioning/SKILL.md +5 -6
  2. package/.github/workflows/ci.yml +4 -11
  3. package/AGENTS.md +13 -17
  4. package/CHANGELOG.md +66 -0
  5. package/README.md +57 -57
  6. package/agents/README.md +9 -23
  7. package/agents/ralph/agent.md +13 -17
  8. package/agents/ralph/claude/polling.md +1 -1
  9. package/agents-native/ralph.md +15 -5
  10. package/bin/agent-cortex.mjs +0 -16
  11. package/bin/installers/claude.mjs +82 -11
  12. package/docs/claude-hooks.md +16 -0
  13. package/docs/ideas/improve-pi-system-prompt.md +9 -3
  14. package/docs/research/disable-model-invocation.md +5 -26
  15. package/docs/skills/skill-anatomy.md +5 -3
  16. package/docs/technical-direction/harness-config.md +6 -14
  17. package/docs/user-preferences.md +2 -2
  18. package/extensions/agent-modes/README.md +3 -3
  19. package/extensions/agent-modes/discover.ts +6 -6
  20. package/extensions/output-style/README.md +58 -0
  21. package/extensions/output-style/index.ts +23 -0
  22. package/extensions/output-style/output-style.md +43 -0
  23. package/extensions/output-style/output-style.test.ts +50 -0
  24. package/extensions/output-style/output-style.ts +32 -0
  25. package/extensions/output-style/package.json +19 -0
  26. package/extensions/output-style/tsconfig.json +12 -0
  27. package/extensions/subagent/README.md +2 -2
  28. package/extensions/subagent/agents.ts +1 -2
  29. package/lib/cli.mjs +5 -7
  30. package/package.json +5 -6
  31. package/scripts/check-skill-frontmatter.mjs +21 -0
  32. package/scripts/lib/compose-agent.mjs +10 -10
  33. package/scripts/lib/skill-frontmatter.mjs +135 -0
  34. package/skills/workflow/ralph/REFERENCE.md +34 -11
  35. package/skills/workflow/ralph/SKILL.md +10 -10
  36. package/skills/workflow/ralph/scripts/pending-gates.mjs +134 -0
  37. package/test/cli.test.mjs +11 -10
  38. package/test/installer.test.mjs +143 -1
  39. package/test/pending-gates.test.mjs +147 -0
  40. package/test/skill-frontmatter.test.mjs +137 -0
  41. package/token-map.README.md +9 -12
  42. package/token-map.json +26 -31
  43. package/agents/plan/copilot/frontmatter.json +0 -6
  44. package/agents/plan.agent.md +0 -59
  45. package/agents/ralph/copilot/frontmatter.json +0 -6
  46. package/agents/ralph/copilot/polling.md +0 -62
  47. package/agents/ralph-plan/copilot/frontmatter.json +0 -6
  48. package/agents/ralph-plan.agent.md +0 -126
  49. package/agents/ralph.agent.md +0 -331
  50. package/agents/strategy/copilot/frontmatter.json +0 -6
  51. package/agents/strategy.agent.md +0 -112
  52. package/bin/installers/copilot.mjs +0 -124
  53. package/hooks.json +0 -10
  54. package/plugin.json +0 -35
  55. package/scripts/build-copilot-agents.mjs +0 -30
  56. package/scripts/install-instructions.sh +0 -82
  57. package/scripts/sync-plugin-version.sh +0 -17
  58. package/test/copilot-installer.test.mjs +0 -308
@@ -1,82 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Installs ~/.copilot/copilot-instructions.md with proactive skill directives.
3
- # Run after cloning this plugin: pnpm install (or bash scripts/install-instructions.sh)
4
- # Safe to re-run — replaces the agent-cortex section only.
5
-
6
- set -euo pipefail
7
-
8
- INSTRUCTIONS_FILE="$HOME/.copilot/copilot-instructions.md"
9
- MARKER_START="<!-- agent-cortex:start -->"
10
- MARKER_END="<!-- agent-cortex:end -->"
11
-
12
- BLOCK="$MARKER_START
13
- <!-- This block is managed by agent-cortex. Manual edits inside will be overwritten. -->
14
-
15
- ## Proactive skill use (agent-cortex plugin)
16
-
17
- You have a set of custom skills available. Apply them automatically whenever they are relevant — the user should not have to invoke them by name.
18
-
19
- ### Code & tests
20
-
21
- - Apply **style-code** whenever writing, editing, or reviewing code. It defines naming, module boundaries, and architectural conventions.
22
- - Apply **style-tests** whenever writing, editing, or reviewing tests. It defines assertion strategy, mock discipline, and what to test.
23
- - Apply **style-comms** whenever writing external-facing communication: tickets, RFCs, PRDs, proposals, or reports.
24
- - Apply **style-documentation** whenever deciding what to document or reviewing existing docs.
25
- - Use **tdd** (red-green-refactor loop) whenever building a new feature or fixing a bug where correctness matters.
26
- - Run **review-security** before committing any changes that touch secrets, auth, or credentials.
27
-
28
- ### Planning & design
29
-
30
- - Use **write-a-prd** when the user wants to plan a new feature or product requirement.
31
- - Use **prd-to-tasks** after a PRD is approved to break it into phased epics and task beads.
32
- - Use **create-task** when creating a new trackable task bead.
33
- - Use **record-idea** when the user shares an early-stage idea that is not yet ready to implement.
34
- - Use **request-refactor-plan** when the user wants to plan a refactor with safe incremental commits.
35
- - Use **write-a-ticket** when writing a ticket for an external issue tracker (e.g. Jira).
36
- - Use **design-an-interface** when the user wants to explore multiple API or module designs.
37
- - Use **technical-direction** when evaluating architecture or technology choices with tradeoff analysis.
38
- - Use **grill-me** or **grill-with-docs** when the user wants to stress-test a plan or design.
39
- - Use **improve-codebase-architecture** when the user wants to find architectural improvement opportunities.
40
-
41
- ### Task execution
42
-
43
- - Use **bd-tool** whenever the user mentions beads, bd, tasks, or asks what work is available.
44
- - Use **ralph** to run all pending beads end-to-end with review gates.
45
- - Use **run-pipeline-stage** to work through individual pipeline stages inline.
46
- - Use **hitl-collab** when a task requires human review, manual steps, or a PR handoff.
47
- - Use **classify-bead** when a bead is missing its implementation-type label.
48
-
49
- ### Repo & plugin maintenance
50
-
51
- - Use **maintain-agent-docs** when reviewing or updating AGENTS.md or agent-consumed docs.
52
- - Use **refactor-skill** when restructuring or reviewing an existing skill.
53
- - Use **write-a-skill** when creating a new skill.
54
- - Use **init-beads** when setting up the bd task-tracker in a new project.
55
-
56
- $MARKER_END"
57
-
58
- mkdir -p "$(dirname "$INSTRUCTIONS_FILE")"
59
-
60
- if [ ! -f "$INSTRUCTIONS_FILE" ]; then
61
- printf '%s\n' "$BLOCK" > "$INSTRUCTIONS_FILE"
62
- echo "✓ Created $INSTRUCTIONS_FILE"
63
- elif grep -qF "$MARKER_START" "$INSTRUCTIONS_FILE"; then
64
- # Write replacement block to a temp file so awk can read it with getline
65
- BLOCK_FILE=$(mktemp)
66
- printf '%s\n' "$BLOCK" > "$BLOCK_FILE"
67
- awk -v start="$MARKER_START" -v end="$MARKER_END" -v blockfile="$BLOCK_FILE" '
68
- $0 == start {
69
- while ((getline line < blockfile) > 0) print line
70
- close(blockfile)
71
- skip=1; next
72
- }
73
- $0 == end { skip=0; next }
74
- !skip { print }
75
- ' "$INSTRUCTIONS_FILE" > "${INSTRUCTIONS_FILE}.tmp"
76
- mv "${INSTRUCTIONS_FILE}.tmp" "$INSTRUCTIONS_FILE"
77
- rm -f "$BLOCK_FILE"
78
- echo "✓ Updated agent-cortex section in $INSTRUCTIONS_FILE"
79
- else
80
- printf '\n%s\n' "$BLOCK" >> "$INSTRUCTIONS_FILE"
81
- echo "✓ Appended agent-cortex section to $INSTRUCTIONS_FILE"
82
- fi
@@ -1,17 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Sync plugin.json version with package.json
3
- # Called by changesets after versioning to keep them in lockstep
4
-
5
- set -euo pipefail
6
-
7
- VERSION=$(node -p "require('./package.json').version")
8
-
9
- # Update plugin.json version field
10
- node -e "
11
- const fs = require('fs');
12
- const plugin = JSON.parse(fs.readFileSync('plugin.json', 'utf8'));
13
- plugin.version = '$VERSION';
14
- fs.writeFileSync('plugin.json', JSON.stringify(plugin, null, 2) + '\n');
15
- "
16
-
17
- echo "✓ Synced plugin.json to version $VERSION"
@@ -1,308 +0,0 @@
1
- // Integration tests for `agent-cortex install copilot`: the install-time
2
- // generator for the Copilot CLI flat agent files (agents/*.agent.md). All tests
3
- // install into mkdtemp dirs so the real repo agents/ is never touched.
4
-
5
- import { describe, it } from "node:test";
6
- import assert from "node:assert/strict";
7
- import { execFile } from "node:child_process";
8
- import { mkdtemp, mkdir, writeFile, rm, readFile, readdir, stat } from "node:fs/promises";
9
- import { tmpdir } from "node:os";
10
- import { fileURLToPath } from "node:url";
11
- import { join, dirname } from "node:path";
12
- import { installCopilot } from "../bin/installers/copilot.mjs";
13
-
14
- const __dirname = dirname(fileURLToPath(import.meta.url));
15
- const ROOT = join(__dirname, "..");
16
- const CLI_PATH = join(ROOT, "bin", "agent-cortex.mjs");
17
-
18
- /** True when a path exists (stat succeeds). */
19
- async function pathExists(p) {
20
- try {
21
- await stat(p);
22
- return true;
23
- } catch {
24
- return false;
25
- }
26
- }
27
-
28
- function runCli(args) {
29
- return new Promise((resolve) => {
30
- execFile(process.execPath, [CLI_PATH, ...args], (error, stdout, stderr) => {
31
- resolve({ exitCode: error ? error.code ?? 1 : 0, stdout, stderr });
32
- });
33
- });
34
- }
35
-
36
- // ─── Fixtures ────────────────────────────────────────────────────────────────
37
-
38
- function tokenMap() {
39
- return {
40
- name: "fixture token map",
41
- version: 1,
42
- harnesses: ["copilot"],
43
- tools: {
44
- bash: { copilot: "bash" },
45
- view: { copilot: "view" },
46
- task: { copilot: "task" },
47
- ask_user: { copilot: "ask_user" },
48
- },
49
- paths: {
50
- plugin_root: { copilot: "~/.copilot/installed-plugins/_direct/agent-cortex" },
51
- agents_dir: { copilot: { base: "plugin_root", relative: "agents" } },
52
- },
53
- };
54
- }
55
-
56
- async function makeFixture() {
57
- const root = await mkdtemp(join(tmpdir(), "copilot-installer-"));
58
- return {
59
- root,
60
- output: join(root, "generated"),
61
- cleanup: async () => rm(root, { recursive: true, force: true }),
62
- };
63
- }
64
-
65
- async function writeFixture(fx, relPath, content) {
66
- const p = join(fx.root, relPath);
67
- await mkdir(dirname(p), { recursive: true });
68
- await writeFile(p, content);
69
- }
70
-
71
- /** Seed a minimal but realistic package tree (agents + token-map). */
72
- async function seedCopilotPackage(fx, opts = {}) {
73
- const { agents = {} } = opts;
74
- await writeFixture(fx, "token-map.json", JSON.stringify(tokenMap()));
75
- for (const [name, def] of Object.entries(agents)) {
76
- await writeFixture(fx, `agents/${name}/agent.md`, def.body ?? `# ${name}\nBody of ${name}.`);
77
- await writeFixture(
78
- fx,
79
- `agents/${name}/copilot/frontmatter.json`,
80
- JSON.stringify(
81
- def.frontmatter ?? {
82
- name: `agent-cortex:${name}`,
83
- description: `Description of ${name}`,
84
- tools: ["bash", "view"],
85
- argumentHint: `Hint for ${name}`,
86
- },
87
- ),
88
- );
89
- for (const [section, content] of Object.entries(def.sections ?? {})) {
90
- await writeFixture(fx, `agents/${name}/copilot/${section}.md`, content);
91
- }
92
- }
93
- }
94
-
95
- /** Deterministic snapshot of a directory: relative paths + byte content. */
96
- async function snapshot(dir) {
97
- const entries = [];
98
- const walk = async (p) => {
99
- for (const e of (await readdir(p, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
100
- const full = join(p, e.name);
101
- if (e.isDirectory()) {
102
- entries.push([e.name, "dir"]);
103
- await walk(full);
104
- } else entries.push([e.name, "file", await readFile(full, "utf-8")]);
105
- }
106
- };
107
- await walk(dir);
108
- return entries;
109
- }
110
-
111
- // ─── Agent composition ───────────────────────────────────────────────────────
112
-
113
- describe("installCopilot — agent composition", () => {
114
- it("writes one flat <name>.agent.md per composable agent dir with copilot frontmatter and no leftover tokens", async () => {
115
- const fx = await makeFixture();
116
- try {
117
- await seedCopilotPackage(fx, {
118
- agents: {
119
- alpha: {
120
- body: "# alpha\nUse {{TOOL:view}} to read.\n{{SECTION:polling}}\nRead {{PATH:agents_dir}}.",
121
- sections: { polling: "Spawn with {{TOOL:task}}." },
122
- frontmatter: {
123
- name: "agent-cortex:alpha",
124
- description: "Reads things.",
125
- tools: ["bash", "view"],
126
- argumentHint: "Read something",
127
- },
128
- },
129
- },
130
- });
131
-
132
- const result = await installCopilot({ root: fx.root, output: fx.output });
133
-
134
- const file = join(fx.output, "alpha.agent.md");
135
- assert.ok(await pathExists(file), "flat agent file written");
136
- const content = await readFile(file, "utf-8");
137
- assert.ok(content.startsWith("---\n"), "yaml frontmatter");
138
- assert.ok(content.includes("# GENERATED from agents/alpha/ by scripts/build-copilot-agents.mjs — DO NOT EDIT."));
139
- // Committed flat-file field order: description, name, tools (YAML array), argument-hint.
140
- const order = ["description:", "name: \"agent-cortex:alpha\"", "tools: [", "argument-hint:"].map((s) => content.indexOf(s));
141
- assert.ok(order.every((i) => i !== -1), "all frontmatter fields present");
142
- assert.deepEqual([...order].sort((a, b) => a - b), order, "frontmatter fields in committed order");
143
- assert.match(content, /tools: \["bash", "view"\]/, "copilot tools kept as a YAML array (no translation)");
144
- assert.ok(content.includes("Use view to read."), "{{TOOL:view}} substituted");
145
- assert.ok(content.includes("Spawn with task."), "{{SECTION:polling}} composed from copilot/polling.md");
146
- assert.ok(!/{{/.test(content), "no literal tokens remain");
147
- assert.equal(result.agents.length, 1);
148
- assert.deepEqual(result.agents, ["alpha"]);
149
- assert.equal(result.output, fx.output);
150
- } finally {
151
- await fx.cleanup();
152
- }
153
- });
154
-
155
- it("skips non-composable entries in agents/ and never clobbers composable sources", async () => {
156
- const fx = await makeFixture();
157
- try {
158
- await seedCopilotPackage(fx, { agents: { alpha: { body: "# alpha" } } });
159
- await writeFixture(fx, "agents/README.md", "# agents dir docs\n");
160
-
161
- const result = await installCopilot({ root: fx.root, output: fx.output });
162
-
163
- assert.deepEqual((await readdir(fx.output)).sort(), ["alpha.agent.md"], "no README, no subdirectories written");
164
- assert.deepEqual(result.agents, ["alpha"]);
165
- // The composable source dir next to the generated file stays untouched.
166
- assert.ok(await pathExists(join(fx.root, "agents", "alpha", "agent.md")), "composable source preserved");
167
- assert.ok(await pathExists(join(fx.root, "agents", "alpha", "copilot", "frontmatter.json")), "harness dir preserved");
168
- } finally {
169
- await fx.cleanup();
170
- }
171
- });
172
-
173
- it("throws when a composable dir lacks copilot/frontmatter.json", async () => {
174
- const fx = await makeFixture();
175
- try {
176
- await seedCopilotPackage(fx, { agents: { alpha: { body: "# alpha" } } });
177
- await rm(join(fx.root, "agents", "alpha", "copilot"), { recursive: true, force: true });
178
- await assert.rejects(
179
- installCopilot({ root: fx.root, output: fx.output }),
180
- /without copilot\/frontmatter\.json/,
181
- );
182
- } finally {
183
- await fx.cleanup();
184
- }
185
- });
186
-
187
- it("defaults to the plugin-scanned agents/ dir of the package root (plugin.json \"agents\": \"agents/\")", async () => {
188
- const fx = await makeFixture();
189
- try {
190
- await seedCopilotPackage(fx, { agents: { alpha: { body: "# alpha" } } });
191
-
192
- const result = await installCopilot({ root: fx.root });
193
-
194
- assert.equal(result.output, join(fx.root, "agents"), "default output is the dir plugin.json scans");
195
- assert.ok(await pathExists(join(fx.root, "agents", "alpha.agent.md")), "flat file lands next to the composable dir");
196
- assert.ok(await pathExists(join(fx.root, "agents", "alpha", "agent.md")), "composable source untouched");
197
- } finally {
198
- await fx.cleanup();
199
- }
200
- });
201
-
202
- it("throws on an unknown tool token", async () => {
203
- const fx = await makeFixture();
204
- try {
205
- await seedCopilotPackage(fx, { agents: { alpha: { body: "# alpha\nUse {{TOOL:nope}} now." } } });
206
- await assert.rejects(installCopilot({ root: fx.root, output: fx.output }), /unknown tool token {{TOOL:nope}}/);
207
- } finally {
208
- await fx.cleanup();
209
- }
210
- });
211
- });
212
-
213
- // ─── Output / dry-run ────────────────────────────────────────────────────────
214
-
215
- describe("installCopilot — output & dry-run", () => {
216
- it("dry-run reports the plan without writing anything", async () => {
217
- const fx = await makeFixture();
218
- try {
219
- await seedCopilotPackage(fx, { agents: { alpha: { body: "# alpha" } } });
220
-
221
- const result = await installCopilot({ root: fx.root, output: fx.output, dryRun: true });
222
-
223
- assert.equal(result.dryRun, true);
224
- assert.equal(await pathExists(fx.output), false, "no output dir created in dry-run");
225
- assert.deepEqual(result.agents, ["alpha"], "planned files still reported");
226
- } finally {
227
- await fx.cleanup();
228
- }
229
- });
230
-
231
- it("is deterministic across repeated installs into the same output", async () => {
232
- const fx = await makeFixture();
233
- try {
234
- await seedCopilotPackage(fx, {
235
- agents: {
236
- alpha: { body: "# alpha\n{{SECTION:polling}}", sections: { polling: "Poll with {{TOOL:task}}." } },
237
- beta: { body: "# beta" },
238
- },
239
- });
240
-
241
- await installCopilot({ root: fx.root, output: fx.output });
242
- const first = await snapshot(fx.output);
243
- await installCopilot({ root: fx.root, output: fx.output });
244
- assert.deepEqual(await snapshot(fx.output), first);
245
- } finally {
246
- await fx.cleanup();
247
- }
248
- });
249
- });
250
-
251
- // ─── Integration against the real repo ───────────────────────────────────────
252
-
253
- describe("installCopilot — real repo integration", () => {
254
- it("regenerates byte-identical copies of the committed agents/*.agent.md files", async () => {
255
- const fx = await makeFixture();
256
- try {
257
- const result = await installCopilot({ root: ROOT, output: fx.output });
258
-
259
- assert.deepEqual(result.agents.sort(), ["plan", "ralph", "ralph-plan", "strategy"]);
260
- for (const name of result.agents) {
261
- const generated = await readFile(join(fx.output, `${name}.agent.md`), "utf-8");
262
- const committed = await readFile(join(ROOT, "agents", `${name}.agent.md`), "utf-8");
263
- assert.equal(generated, committed, `${name}.agent.md must match the committed flat file (CI drift check)`);
264
- }
265
- } finally {
266
- await fx.cleanup();
267
- }
268
- });
269
- });
270
-
271
- // ─── CLI wiring ──────────────────────────────────────────────────────────────
272
-
273
- describe("CLI integration", () => {
274
- it("exits 0 for install copilot --output <tmp> and writes the flat files", async () => {
275
- const fx = await makeFixture();
276
- try {
277
- const { exitCode, stdout } = await runCli(["install", "copilot", "--output", fx.output]);
278
- assert.equal(exitCode, 0);
279
- assert.ok(stdout.includes("copilot"), "prints the harness name");
280
- assert.ok(stdout.includes("Generated"), "prints the generation summary");
281
- assert.ok(await pathExists(join(fx.output, "ralph.agent.md")), "agent files written");
282
- assert.ok(await pathExists(join(fx.output, "plan.agent.md")));
283
- } finally {
284
- await fx.cleanup();
285
- }
286
- });
287
-
288
- it("exits 0 for install copilot --dry-run and writes nothing", async () => {
289
- const fx = await makeFixture();
290
- try {
291
- const { exitCode, stdout } = await runCli(["install", "copilot", "--output", fx.output, "--dry-run"]);
292
- assert.equal(exitCode, 0);
293
- assert.ok(/dry-run/i.test(stdout));
294
- assert.equal(await pathExists(fx.output), false, "no files written in dry-run");
295
- } finally {
296
- await fx.cleanup();
297
- }
298
- });
299
-
300
- it("exits 0 for install copilot (default output) without touching committed files", async () => {
301
- const before = await readFile(join(ROOT, "agents", "ralph.agent.md"), "utf-8");
302
- const { exitCode } = await runCli(["install", "copilot"]);
303
- assert.equal(exitCode, 0);
304
- // Default output regenerates the shared agents/ dir in place — output must
305
- // stay byte-identical to what is committed.
306
- assert.equal(await readFile(join(ROOT, "agents", "ralph.agent.md"), "utf-8"), before);
307
- });
308
- });