@jaybeeuu/agent-cortex 1.39.0 → 1.41.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 +1 -1
- package/.github/workflows/ci.yml +47 -14
- package/AGENTS.md +35 -8
- package/CHANGELOG.md +60 -0
- package/README.md +174 -97
- package/agents/README.md +14 -11
- package/bin/agent-cortex.mjs +55 -10
- package/bin/installers/claude.mjs +674 -174
- package/bin/installers/copilot.mjs +13 -18
- package/bin/installers/pi.mjs +293 -51
- package/claude/settings.json +4 -0
- package/{claude → claude-extras}/scripts/statusline-command.sh +32 -4
- package/docs/claude-hooks.md +15 -3
- package/docs/ideas/ban-content-assertion-tests.md +73 -0
- package/docs/ideas/bead-progress-notes-for-session-resumption.md +75 -0
- package/docs/ideas/classify-beads-via-deterministic-script-with-agent-fallback.md +70 -0
- package/docs/ideas/ctx-alias-version-and-update-commands.md +65 -0
- package/docs/ideas/ctx-feedback-command-for-capturing-ideas-from-any-machine.md +67 -0
- package/docs/ideas/deployment-beads-for-post-merge-pipelines.md +36 -0
- package/docs/ideas/design-and-delivery-as-separate-bead-trees/design-and-delivery-as-separate-bead-trees.md +99 -0
- package/docs/ideas/enable-signed-commit-enforcement-changesets-bot-commits-must-be-signed.md +33 -0
- package/docs/ideas/extension-manifests.md +55 -14
- package/docs/ideas/improve-pi-system-prompt.md +5 -2
- package/docs/ideas/map-every-cortex-tool-to-a-real-pi-equivalent.md +95 -0
- package/docs/ideas/record-idea-implementation-questions.md +68 -0
- package/docs/ideas/self-flushing-session.md +72 -0
- package/docs/ideas/state-in-beads-not-md.md +69 -0
- package/docs/ideas/task-session-context.local.md +95 -0
- package/docs/ideas/test-data-creation-helpers.md +92 -0
- package/docs/ideas/test-planning-stage-bead-in-feature-planning.md +68 -0
- package/docs/ideas/worktree-housekeeping-for-closed-beads.md +39 -0
- package/docs/technical-direction/harness-config.md +161 -0
- package/docs/technical-direction/migration-execution-labels.md +60 -23
- package/extensions/agent-guardrails/README.md +45 -0
- package/extensions/agent-guardrails/guardrails.test.ts +42 -0
- package/extensions/agent-guardrails/guardrails.ts +66 -0
- package/extensions/agent-guardrails/index.ts +22 -0
- package/extensions/agent-guardrails/node_modules/.bin/pi +21 -0
- package/extensions/agent-guardrails/node_modules/.bin/tsc +21 -0
- package/extensions/agent-guardrails/node_modules/.bin/tsserver +21 -0
- package/extensions/agent-guardrails/node_modules/.bin/tsx +21 -0
- package/extensions/agent-guardrails/package.json +19 -0
- package/extensions/agent-guardrails/tsconfig.json +12 -0
- package/extensions/agent-modes/README.md +12 -5
- package/extensions/agent-modes/discover.test.ts +30 -7
- package/extensions/agent-modes/discover.ts +1 -1
- package/extensions/agent-modes/index.test.ts +208 -0
- package/extensions/agent-modes/index.ts +23 -6
- package/extensions/agent-modes/package.json +1 -1
- package/extensions/skill-stats/index.test.ts +3 -2
- package/lib/cli.mjs +30 -12
- package/lib/pi-packages.mjs +169 -0
- package/package.json +8 -4
- package/pi/settings.json +23 -12
- package/plugin.json +1 -1
- package/scripts/build-copilot-agents.mjs +1 -1
- package/scripts/check-no-sync.mjs +49 -0
- package/scripts/lib/compose-agent.mjs +32 -14
- package/scripts/lib/fs.mjs +31 -0
- package/skills/engineering/grill-with-docs/SKILL.md +1 -1
- package/skills/planning/classify-bead/REFERENCE.md +85 -0
- package/skills/planning/classify-bead/SKILL.md +32 -36
- package/skills/planning/classify-bead/scripts/classify-bead.mjs +292 -0
- package/skills/planning/create-task/SKILL.md +19 -10
- package/skills/planning/write-a-prd/SKILL.md +2 -2
- package/skills/style/style-code/REFERENCE.md +32 -2
- package/skills/style/style-code/SKILL.md +5 -21
- package/skills/style/style-tests/SKILL.md +69 -17
- package/skills/workflow/hitl-collab/SKILL.md +1 -1
- package/skills/workflow/run-pipeline-stage/SKILL.md +13 -1
- package/skills/workflow/run-pipeline-stage/prompts/stage-runner.md +12 -0
- package/test/classify-bead.test.mjs +277 -0
- package/test/claude-installer.test.mjs +199 -139
- package/test/cli.test.mjs +95 -41
- package/test/copilot-installer.test.mjs +92 -82
- package/test/fs.test.mjs +76 -0
- package/test/helpers/fake-claude.mjs +152 -0
- package/test/installer.test.mjs +542 -57
- package/test/pi-installer.test.mjs +457 -102
- package/test/pi-packages.test.mjs +295 -0
- package/token-map.README.md +23 -12
- package/token-map.json +8 -7
- package/.claude-plugin/marketplace.json +0 -16
- package/claude/.claude-plugin/plugin.json +0 -11
- package/claude/agents/plan.md +0 -58
- package/claude/agents/ralph-plan.md +0 -121
- package/claude/agents/ralph.md +0 -193
- package/claude/agents/strategy.md +0 -111
- package/claude/hooks/scripts/notify.mjs +0 -54
- package/claude/hooks.json +0 -38
- /package/{claude → claude-extras}/.mcp.json +0 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { describe, it } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdtemp, mkdir, writeFile, rm, chmod } from "node:fs/promises";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import {
|
|
7
|
+
loadRequiredPackages,
|
|
8
|
+
npmSourceName,
|
|
9
|
+
planPackageInstalls,
|
|
10
|
+
provisionPiPackages,
|
|
11
|
+
runPiInstall,
|
|
12
|
+
} from "../lib/pi-packages.mjs";
|
|
13
|
+
|
|
14
|
+
// ─── Fixtures ────────────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
async function makePiRoot({ declared = [], installed = [], settingsRaw } = {}) {
|
|
17
|
+
const root = await mkdtemp(join(tmpdir(), "pi-packages-"));
|
|
18
|
+
if (settingsRaw !== undefined) {
|
|
19
|
+
await writeFile(join(root, "settings.json"), settingsRaw);
|
|
20
|
+
} else if (declared.length > 0) {
|
|
21
|
+
await writeFile(join(root, "settings.json"), JSON.stringify({ packages: declared }));
|
|
22
|
+
}
|
|
23
|
+
for (const name of installed) {
|
|
24
|
+
await mkdir(join(root, "npm", "node_modules", name), { recursive: true });
|
|
25
|
+
}
|
|
26
|
+
return { root, cleanup: async () => rm(root, { recursive: true, force: true }) };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** A runner that records every source it is asked to install. */
|
|
30
|
+
function recordingRunner({ results = {} } = {}) {
|
|
31
|
+
const calls = [];
|
|
32
|
+
const runner = async (source) => {
|
|
33
|
+
calls.push(source);
|
|
34
|
+
return results[source] ?? { ok: true, error: null };
|
|
35
|
+
};
|
|
36
|
+
return { runner, calls };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Run `fn` with PATH set to `pathValue`, restoring the previous PATH afterwards. */
|
|
40
|
+
async function withPath(pathValue, fn) {
|
|
41
|
+
const original = process.env.PATH;
|
|
42
|
+
process.env.PATH = pathValue;
|
|
43
|
+
try {
|
|
44
|
+
return await fn();
|
|
45
|
+
} finally {
|
|
46
|
+
process.env.PATH = original;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Write an executable `pi` stub with the given shell body; returns its dir and cleanup. */
|
|
51
|
+
async function makePiStub(body) {
|
|
52
|
+
const dir = await mkdtemp(join(tmpdir(), "pi-stub-"));
|
|
53
|
+
const file = join(dir, "pi");
|
|
54
|
+
await writeFile(file, `#!/bin/sh\n${body}\n`);
|
|
55
|
+
await chmod(file, 0o755);
|
|
56
|
+
return { dir, cleanup: async () => rm(dir, { recursive: true, force: true }) };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** A directory with nothing on PATH, so any external command lookup fails. */
|
|
60
|
+
async function makeEmptyPathDir() {
|
|
61
|
+
const dir = await mkdtemp(join(tmpdir(), "pi-stub-empty-"));
|
|
62
|
+
return { dir, cleanup: async () => rm(dir, { recursive: true, force: true }) };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ─── Manifest ────────────────────────────────────────────────────────────────
|
|
66
|
+
|
|
67
|
+
describe("loadRequiredPackages", () => {
|
|
68
|
+
it("reads the declared pi packages from the package manifest", async () => {
|
|
69
|
+
const root = await mkdtemp(join(tmpdir(), "pi-packages-manifest-"));
|
|
70
|
+
try {
|
|
71
|
+
await writeFile(
|
|
72
|
+
join(root, "package.json"),
|
|
73
|
+
JSON.stringify({ name: "x", pi: { extensions: ["./extensions"], packages: ["npm:pi-questions"] } }),
|
|
74
|
+
);
|
|
75
|
+
assert.deepEqual(await loadRequiredPackages(root), ["npm:pi-questions"]);
|
|
76
|
+
} finally {
|
|
77
|
+
await rm(root, { recursive: true, force: true });
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("returns no packages when the manifest does not declare any", async () => {
|
|
82
|
+
const root = await mkdtemp(join(tmpdir(), "pi-packages-manifest-"));
|
|
83
|
+
try {
|
|
84
|
+
await writeFile(join(root, "package.json"), JSON.stringify({ name: "x", pi: { skills: ["./skills"] } }));
|
|
85
|
+
assert.deepEqual(await loadRequiredPackages(root), []);
|
|
86
|
+
} finally {
|
|
87
|
+
await rm(root, { recursive: true, force: true });
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// ─── Source parsing ──────────────────────────────────────────────────────────
|
|
93
|
+
|
|
94
|
+
describe("npmSourceName", () => {
|
|
95
|
+
it("extracts the package name from npm sources, including scoped and pinned specs", () => {
|
|
96
|
+
assert.equal(npmSourceName("npm:pi-questions"), "pi-questions");
|
|
97
|
+
assert.equal(npmSourceName("npm:pi-web-access@0.10.7"), "pi-web-access");
|
|
98
|
+
assert.equal(npmSourceName("npm:@scope/pkg@1.2.3"), "@scope/pkg");
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("returns null for non-npm sources it cannot verify locally", () => {
|
|
102
|
+
assert.equal(npmSourceName("git:github.com/user/repo"), null);
|
|
103
|
+
assert.equal(npmSourceName("./local/path"), null);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// ─── Install plan ────────────────────────────────────────────────────────────
|
|
108
|
+
|
|
109
|
+
describe("planPackageInstalls", () => {
|
|
110
|
+
it("plans nothing when every required package is declared and installed", () => {
|
|
111
|
+
assert.deepEqual(
|
|
112
|
+
planPackageInstalls(["npm:pi-questions"], {
|
|
113
|
+
declaredSources: ["npm:pi-questions"],
|
|
114
|
+
installedNames: ["pi-questions"],
|
|
115
|
+
}),
|
|
116
|
+
[],
|
|
117
|
+
);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("plans a package that is declared but not installed", () => {
|
|
121
|
+
assert.deepEqual(
|
|
122
|
+
planPackageInstalls(["npm:pi-web-access@0.10.7"], {
|
|
123
|
+
declaredSources: ["npm:pi-web-access@0.10.7"],
|
|
124
|
+
installedNames: [],
|
|
125
|
+
}),
|
|
126
|
+
["npm:pi-web-access@0.10.7"],
|
|
127
|
+
);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("plans a package that is installed but not declared, so pi will load it", () => {
|
|
131
|
+
assert.deepEqual(
|
|
132
|
+
planPackageInstalls(["npm:pi-questions"], {
|
|
133
|
+
declaredSources: [],
|
|
134
|
+
installedNames: ["pi-questions"],
|
|
135
|
+
}),
|
|
136
|
+
["npm:pi-questions"],
|
|
137
|
+
);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// ─── Provisioning ────────────────────────────────────────────────────────────
|
|
142
|
+
|
|
143
|
+
describe("runPiInstall", () => {
|
|
144
|
+
it("degrades to a result when the pi CLI is not on PATH", async () => {
|
|
145
|
+
const empty = await makeEmptyPathDir();
|
|
146
|
+
try {
|
|
147
|
+
const result = await withPath(empty.dir, () => runPiInstall("npm:pi-questions"));
|
|
148
|
+
assert.deepEqual(result, { ok: false, error: "pi CLI not found on PATH" });
|
|
149
|
+
} finally {
|
|
150
|
+
await empty.cleanup();
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("maps a successful pi install to ok", async () => {
|
|
155
|
+
const stub = await makePiStub("exit 0");
|
|
156
|
+
try {
|
|
157
|
+
const result = await withPath(stub.dir, () => runPiInstall("npm:pi-questions"));
|
|
158
|
+
assert.deepEqual(result, { ok: true, error: null });
|
|
159
|
+
} finally {
|
|
160
|
+
await stub.cleanup();
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("maps a failing pi install to its stderr", async () => {
|
|
165
|
+
const stub = await makePiStub('echo "offline: no registry" >&2\nexit 1');
|
|
166
|
+
try {
|
|
167
|
+
const result = await withPath(stub.dir, () => runPiInstall("npm:pi-questions"));
|
|
168
|
+
assert.deepEqual(result, { ok: false, error: "offline: no registry" });
|
|
169
|
+
} finally {
|
|
170
|
+
await stub.cleanup();
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
describe("provisionPiPackages", () => {
|
|
176
|
+
it("installs missing packages and skips the ones already present", async () => {
|
|
177
|
+
const fx = await makePiRoot({ declared: ["npm:pi-questions"], installed: ["pi-questions"] });
|
|
178
|
+
try {
|
|
179
|
+
const { runner, calls } = await recordingRunner();
|
|
180
|
+
|
|
181
|
+
const result = await provisionPiPackages({
|
|
182
|
+
required: ["npm:pi-questions", "npm:pi-web-access@0.10.7"],
|
|
183
|
+
piRoot: fx.root,
|
|
184
|
+
runInstall: runner,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
assert.deepEqual(calls, ["npm:pi-web-access@0.10.7"], "only the missing package is installed");
|
|
188
|
+
assert.deepEqual(result.installed, ["npm:pi-web-access@0.10.7"]);
|
|
189
|
+
assert.deepEqual(result.failed, []);
|
|
190
|
+
} finally {
|
|
191
|
+
await fx.cleanup();
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it("is idempotent — a second run with everything present installs nothing", async () => {
|
|
196
|
+
const fx = await makePiRoot({
|
|
197
|
+
declared: ["npm:pi-questions", "npm:pi-web-access@0.10.7"],
|
|
198
|
+
installed: ["pi-questions", "pi-web-access"],
|
|
199
|
+
});
|
|
200
|
+
try {
|
|
201
|
+
const { runner, calls } = await recordingRunner();
|
|
202
|
+
const result = await provisionPiPackages({
|
|
203
|
+
required: ["npm:pi-questions", "npm:pi-web-access@0.10.7"],
|
|
204
|
+
piRoot: fx.root,
|
|
205
|
+
runInstall: runner,
|
|
206
|
+
});
|
|
207
|
+
assert.deepEqual(calls, []);
|
|
208
|
+
assert.deepEqual(result.installed, []);
|
|
209
|
+
} finally {
|
|
210
|
+
await fx.cleanup();
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it("reports a failed install and continues with the rest", async () => {
|
|
215
|
+
const fx = await makePiRoot();
|
|
216
|
+
try {
|
|
217
|
+
const warnings = [];
|
|
218
|
+
const { runner, calls } = await recordingRunner({
|
|
219
|
+
results: { "npm:pi-questions": { ok: false, error: "offline" } },
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
const result = await provisionPiPackages({
|
|
223
|
+
required: ["npm:pi-questions", "npm:pi-web-access@0.10.7"],
|
|
224
|
+
piRoot: fx.root,
|
|
225
|
+
runInstall: runner,
|
|
226
|
+
warn: (msg) => warnings.push(msg),
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
assert.deepEqual(calls, ["npm:pi-questions", "npm:pi-web-access@0.10.7"], "failure does not abort the rest");
|
|
230
|
+
assert.deepEqual(result.installed, ["npm:pi-web-access@0.10.7"]);
|
|
231
|
+
assert.deepEqual(result.failed, [{ source: "npm:pi-questions", error: "offline" }]);
|
|
232
|
+
assert.ok(warnings.some((w) => w.includes("npm:pi-questions")), "failure is surfaced as a warning");
|
|
233
|
+
} finally {
|
|
234
|
+
await fx.cleanup();
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it("uses the pi CLI runner by default and warns when it is missing", async () => {
|
|
239
|
+
const fx = await makePiRoot();
|
|
240
|
+
const empty = await makeEmptyPathDir();
|
|
241
|
+
try {
|
|
242
|
+
const warnings = [];
|
|
243
|
+
const result = await withPath(empty.dir, () =>
|
|
244
|
+
provisionPiPackages({
|
|
245
|
+
required: ["npm:pi-questions"],
|
|
246
|
+
piRoot: fx.root,
|
|
247
|
+
warn: (msg) => warnings.push(msg),
|
|
248
|
+
}),
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
assert.deepEqual(result.failed, [{ source: "npm:pi-questions", error: "pi CLI not found on PATH" }]);
|
|
252
|
+
assert.ok(
|
|
253
|
+
warnings.some((w) => w.includes("pi CLI not found on PATH")),
|
|
254
|
+
"missing CLI is surfaced as a warning, not a thrown error",
|
|
255
|
+
);
|
|
256
|
+
} finally {
|
|
257
|
+
await empty.cleanup();
|
|
258
|
+
await fx.cleanup();
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it("dry-run reports the plan without installing anything", async () => {
|
|
263
|
+
const fx = await makePiRoot();
|
|
264
|
+
try {
|
|
265
|
+
const { runner, calls } = await recordingRunner();
|
|
266
|
+
const result = await provisionPiPackages({
|
|
267
|
+
required: ["npm:pi-questions"],
|
|
268
|
+
piRoot: fx.root,
|
|
269
|
+
runInstall: runner,
|
|
270
|
+
dryRun: true,
|
|
271
|
+
});
|
|
272
|
+
assert.deepEqual(calls, []);
|
|
273
|
+
assert.deepEqual(result.planned, ["npm:pi-questions"]);
|
|
274
|
+
assert.deepEqual(result.installed, []);
|
|
275
|
+
} finally {
|
|
276
|
+
await fx.cleanup();
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it("treats an unreadable settings file as no declarations rather than failing the install", async () => {
|
|
281
|
+
const fx = await makePiRoot({ settingsRaw: "{ not json" });
|
|
282
|
+
try {
|
|
283
|
+
const { runner, calls } = await recordingRunner();
|
|
284
|
+
const result = await provisionPiPackages({
|
|
285
|
+
required: ["npm:pi-questions"],
|
|
286
|
+
piRoot: fx.root,
|
|
287
|
+
runInstall: runner,
|
|
288
|
+
});
|
|
289
|
+
assert.deepEqual(calls, ["npm:pi-questions"], "still attempts the install");
|
|
290
|
+
assert.deepEqual(result.installed, ["npm:pi-questions"]);
|
|
291
|
+
} finally {
|
|
292
|
+
await fx.cleanup();
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
});
|
package/token-map.README.md
CHANGED
|
@@ -16,7 +16,7 @@ decisions behind the map.
|
|
|
16
16
|
|---|---|---|---|
|
|
17
17
|
| `copilot` | GitHub Copilot CLI | `agents/<name>/` composable dirs → generated `agents/*.agent.md` | `~/.copilot/installed-plugins/_direct/agent-cortex` |
|
|
18
18
|
| `pi` | pi coding agent | `agents/<name>/` composable dirs (composed at runtime by agent-modes, or installed install-time by `agent-cortex install pi`) | `~/.pi/agent/npm/node_modules/@jaybeeuu/agent-cortex` |
|
|
19
|
-
| `claude` | Claude Code |
|
|
19
|
+
| `claude` | Claude Code | `~/.agent-cortex/claude/agents/*.md` (materialised home install) | `${CLAUDE_PLUGIN_ROOT}` (env var) |
|
|
20
20
|
|
|
21
21
|
The harness ids match the values accepted by `agent-cortex install <harness>` in
|
|
22
22
|
`lib/cli.mjs` (`SUPPORTED_HARNESSES`). The pi `plugin_root` row is the npm-layout
|
|
@@ -48,11 +48,19 @@ rather than inventing a dotted form:
|
|
|
48
48
|
harness. Source of truth for the copilot → claude half is `token-map.json`'s own claude
|
|
49
49
|
column, consumed by `bin/installers/claude.mjs` (the install-time generator — PR #91, #92); the pi column is grounded
|
|
50
50
|
in pi's documented built-in tool surface (`read`, `bash`, `edit`, `write`, `grep`, `find`,
|
|
51
|
-
`ls`) plus `
|
|
51
|
+
`ls`) plus `ask_questions` — added by the pi-questions package (mapped from
|
|
52
|
+
`ask_user`) — and `task` / `read_agent` / `wait_for_agents` subagent tools; `skill` maps
|
|
53
|
+
to `read` (skills load by reading the listed SKILL.md path).
|
|
52
54
|
- **`paths`** — `plugin_root`, `agents_dir`, and `skills_dir`. These are the install-time
|
|
53
55
|
variables that differ per harness. `plugin_root` is the anchor; the other two resolve
|
|
54
56
|
relative to it (`{base, relative}` specs) so a harness only overrides the single root it
|
|
55
57
|
actually installs into.
|
|
58
|
+
Claude's `plugin_root` stays the `${CLAUDE_PLUGIN_ROOT}` env-var token, never a literal
|
|
59
|
+
`~/.agent-cortex/claude`: Claude Code **copies** marketplace-installed plugins — including
|
|
60
|
+
local directory sources like `~/.agent-cortex/claude` — into its own cache, so only the
|
|
61
|
+
runtime env var (expanded per context by Claude Code) points at wherever the plugin
|
|
62
|
+
actually runs from. A literal home path in substituted files would break under
|
|
63
|
+
cache-copy, so the installer passes the env form through verbatim.
|
|
56
64
|
- **`agents`** — the `agent-cortex:` name-prefix convention per harness (claude strips the
|
|
57
65
|
prefix during conversion) and the deferred-agent exception (claude's `ralph` is authored
|
|
58
66
|
natively, never generated). Agent names themselves do not get per-row mappings because
|
|
@@ -83,8 +91,10 @@ The pi install-time installer (`bin/installers/pi.mjs`) follows the strict contr
|
|
|
83
91
|
One consumer deviates deliberately: the pi `agent-modes` extension reads the map at
|
|
84
92
|
**runtime** (not install time) to compose agent prompts from the composable agents
|
|
85
93
|
format. For it, an unmapped tool name passes through unchanged — it may be a
|
|
86
|
-
native PI tool — while a mapped-null tool
|
|
87
|
-
|
|
94
|
+
native PI tool — while a mapped-null tool is omitted with a warning. The pi column
|
|
95
|
+
currently maps every canonical tool (`ask_user` → `ask_questions`, `skill` → `read`),
|
|
96
|
+
so the warn-and-omit path applies only to future null entries. See
|
|
97
|
+
`extensions/agent-modes/README.md`.
|
|
88
98
|
|
|
89
99
|
### 4. Token substitution contract
|
|
90
100
|
|
|
@@ -103,9 +113,9 @@ The full contract is embedded in `token-map.json` under `contract`. In summary,
|
|
|
103
113
|
- Write tokens with the **canonical (copilot) names**: `Use {{TOOL:task}} to spawn
|
|
104
114
|
subagents`, `Read {{PATH:skills/workflow/plan/SKILL.md}}`.
|
|
105
115
|
- Never embed a harness-specific name in the canonical sources (`agents/<name>/agent.md`,
|
|
106
|
-
`skills/**/SKILL.md`) — that defeats the map. The
|
|
107
|
-
generated `agents/*.agent.md` files, and any installed
|
|
108
|
-
substituted names appear.
|
|
116
|
+
`skills/**/SKILL.md`) — that defeats the map. The materialised Claude plugin
|
|
117
|
+
(`~/.agent-cortex/claude`), the generated `agents/*.agent.md` files, and any installed
|
|
118
|
+
copies are the only places substituted names appear.
|
|
109
119
|
- When you add a new tool to an agent's frontmatter, add a row to `tools` in
|
|
110
120
|
`token-map.json` in the same change — the contract treats unknown tools as errors, so a
|
|
111
121
|
missing row breaks the install pipeline loudly instead of silently.
|
|
@@ -119,11 +129,12 @@ The full contract is embedded in `token-map.json` under `contract`. In summary,
|
|
|
119
129
|
`{{PATH:...}}` against the map and `{{SECTION:...}}` from the per-harness section
|
|
120
130
|
files; `bin/installers/copilot.mjs` is the shared generator both `pnpm build:copilot`
|
|
121
131
|
and `agent-cortex install copilot` run.
|
|
122
|
-
- `bin/installers/claude.mjs` — the single Claude generator
|
|
123
|
-
`agent-cortex install claude`
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
132
|
+
- `bin/installers/claude.mjs` — the single Claude generator: plain
|
|
133
|
+
`agent-cortex install claude` materialises the plugin into `~/.agent-cortex/claude`
|
|
134
|
+
with token-substituted skills, merges the committed `claude/settings.json` into
|
|
135
|
+
`~/.claude/settings.json`, and registers it with Claude Code; `--output <dir>` is the
|
|
136
|
+
generate-only form (tests/CI structural checks). The only committed `claude/` file is the
|
|
137
|
+
settings template.
|
|
127
138
|
- `bin/installers/pi.mjs` — the pi harness installer (`agent-cortex install pi`), an
|
|
128
139
|
install-time consumer of the map's contract.
|
|
129
140
|
- `lib/cli.mjs` — `SUPPORTED_HARNESSES`, the install command surface the harness ids come
|
package/token-map.json
CHANGED
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
"grep": { "copilot": "grep", "claude": "Grep", "pi": "grep" },
|
|
21
21
|
"rg": { "copilot": "rg", "claude": "Grep", "pi": "grep" },
|
|
22
22
|
"glob": { "copilot": "glob", "claude": "Glob", "pi": "find" },
|
|
23
|
-
"ask_user": { "copilot": "ask_user", "claude": "AskUserQuestion", "pi":
|
|
23
|
+
"ask_user": { "copilot": "ask_user", "claude": "AskUserQuestion", "pi": "ask_questions" },
|
|
24
24
|
"web_fetch": { "copilot": "web_fetch", "claude": "WebFetch", "pi": "fetch_content" },
|
|
25
|
-
"skill": { "copilot": "skill", "claude": "Skill", "pi":
|
|
25
|
+
"skill": { "copilot": "skill", "claude": "Skill", "pi": "read" },
|
|
26
26
|
"task": { "copilot": "task", "claude": "Task", "pi": "task" },
|
|
27
27
|
"read_agent": { "copilot": "read_agent", "claude": null, "pi": "read_agent" },
|
|
28
28
|
"wait_for_agents": { "copilot": null, "claude": null, "pi": "wait_for_agents" }
|
|
@@ -56,21 +56,22 @@
|
|
|
56
56
|
"sources": {
|
|
57
57
|
"copilot": "agents/<name>/ composable dirs (canonical) — composed to agents/*.agent.md by scripts/build-copilot-agents.mjs",
|
|
58
58
|
"pi": "agents/<name>/ composable dirs (canonical) — composed at runtime by the agent-modes extension",
|
|
59
|
-
"claude": "agents/<name>/ composable dirs (canonical) — composed by bin/installers/claude.mjs (install-time generator
|
|
59
|
+
"claude": "agents/<name>/ composable dirs (canonical) — composed by bin/installers/claude.mjs (install-time generator, the only generator — the generated claude/ plugin subtree is not committed), plus agents-native/*.md for deferred agents"
|
|
60
60
|
}
|
|
61
61
|
},
|
|
62
62
|
"notes": {
|
|
63
63
|
"tools": {
|
|
64
64
|
"rg": "Copilot alias for grep. Claude and pi collapse it onto their single search tool (Grep / grep) — same collapse rule as the build script.",
|
|
65
65
|
"glob": "Claude has a dedicated Glob tool. pi's built-in pattern-search tool is find (hypa_find when the pi-hypa extension is installed).",
|
|
66
|
-
"ask_user": "Copilot (ask_user) and Claude (AskUserQuestion) prompt the user mid-run. pi
|
|
67
|
-
"web_fetch": "pi's fetch_content comes from the pi-web-access package (declared in pi
|
|
68
|
-
"skill": "Copilot (skill) and Claude (Skill) expose skills as a tool. pi
|
|
66
|
+
"ask_user": "Copilot (ask_user) and Claude (AskUserQuestion) prompt the user mid-run. pi's equivalent is ask_questions, provided by the pi-questions package (declared in the package manifest — package.json pi.packages — and provisioned by agent-cortex install pi; also declared in pi/settings.json for the symlinked checkout) — the genuine interactive-questioning tool, restoring grilling in plan/ralph-plan/strategy modes. Like fetch_content (pi-web-access), it is only present when its package is installed, so the mapping depends on that package (see the extension-manifests idea). pi still intentionally ships no permission popups; ask_questions is the interactive mid-run question tool, not a popup.",
|
|
67
|
+
"web_fetch": "pi's fetch_content comes from the pi-web-access package (declared in the package manifest — package.json pi.packages — and provisioned by agent-cortex install pi).",
|
|
68
|
+
"skill": "Copilot (skill) and Claude (Skill) expose skills as a tool. pi has no dedicated skill tool — skills are surfaced in the always-present 'Available skills' context block and loaded by reading the listed SKILL.md path ({{PATH:<skill>/SKILL.md}}). The pi mapping is read, the tool used to load a skill's file; a dedicated skill tool is a follow-on design candidate if prompting proves insufficient.",
|
|
69
69
|
"read_agent": "Claude's Task returns results inline — there is no separate read step, so read_agent drops on claude (null). Copilot uses the task + read_agent poll loop; pi blocks on wait_for_agents and keeps read_agent as a fallback.",
|
|
70
70
|
"wait_for_agents": "PI-only blocking wait over background subagent runs (the task tool): returns the results of completed agents and STILL RUNNING for the rest. Copilot has no equivalent tool (its task + read_agent poll loop stays) and Claude's Task delivers results inline with no wait step — both null."
|
|
71
71
|
},
|
|
72
72
|
"paths": {
|
|
73
|
-
"pi": "plugin_root defaults to the npm-package install layout (~/.pi/agent/npm/node_modules/@jaybeeuu/agent-cortex). Local-checkout installs (pi/settings.json packages entry referencing a local src path) or symlinked installs differ — installers must compute plugin_root from the actual resolution and can override it."
|
|
73
|
+
"pi": "plugin_root defaults to the npm-package install layout (~/.pi/agent/npm/node_modules/@jaybeeuu/agent-cortex). Local-checkout installs (pi/settings.json packages entry referencing a local src path) or symlinked installs differ — installers must compute plugin_root from the actual resolution and can override it.",
|
|
74
|
+
"claude": "plugin_root stays ${CLAUDE_PLUGIN_ROOT} (env var) for the materialised plugin — never a literal ~/.agent-cortex/claude. Claude Code copies marketplace-installed plugins (including local directory sources like ~/.agent-cortex/claude) into its own cache and expands the env var per context, so a literal home path in substituted files would break under cache-copy; the installer passes the env form through verbatim."
|
|
74
75
|
},
|
|
75
76
|
"agents": {
|
|
76
77
|
"prefix": "Claude strips the agent-cortex: name prefix during conversion (NAME_PREFIX in bin/installers/claude.mjs): copilot/pi agents are agent-cortex:<slug>, claude agents are <slug>.",
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "jaybeeuu",
|
|
3
|
-
"owner": {
|
|
4
|
-
"name": "jaybeeuu"
|
|
5
|
-
},
|
|
6
|
-
"metadata": {
|
|
7
|
-
"description": "jaybeeuu's personal Claude Code plugins."
|
|
8
|
-
},
|
|
9
|
-
"plugins": [
|
|
10
|
-
{
|
|
11
|
-
"name": "agent-cortex",
|
|
12
|
-
"source": "./claude",
|
|
13
|
-
"description": "Personal Claude Code plugin with custom agents and skills"
|
|
14
|
-
}
|
|
15
|
-
]
|
|
16
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "agent-cortex",
|
|
3
|
-
"version": "1.39.0",
|
|
4
|
-
"description": "Personal Claude Code plugin with custom agents and skills",
|
|
5
|
-
"author": {
|
|
6
|
-
"name": "jaybeeuu"
|
|
7
|
-
},
|
|
8
|
-
"license": "MIT",
|
|
9
|
-
"repository": "https://github.com/jaybeeuu/agent-cortex",
|
|
10
|
-
"hooks": "./hooks.json"
|
|
11
|
-
}
|
package/claude/agents/plan.md
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
# GENERATED from agents/plan/ by bin/installers/claude.mjs — DO NOT EDIT.
|
|
3
|
-
name: plan
|
|
4
|
-
description: "Guide a feature from first discussion through PRD and task breakdown to classified beads ready for ralph execution. Full end-to-end planning workflow using the plan skill. Use when starting a new feature, scoping work to hand off to ralph, or saying 'plan this'."
|
|
5
|
-
tools: Bash, Read, Edit, Write, Grep, Glob, AskUserQuestion, Task, Skill
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# plan — end-to-end feature planning agent
|
|
9
|
-
|
|
10
|
-
You are a planning agent. Your job is to guide a feature from first discussion through PRD and task breakdown to classified beads ready for ralph execution.
|
|
11
|
-
|
|
12
|
-
You follow the **plan** skill workflow. The skill is defined in `skills/workflow/plan/SKILL.md`. Read it now to load the full instructions.
|
|
13
|
-
|
|
14
|
-
## Workflow summary
|
|
15
|
-
|
|
16
|
-
### Phase 1 — Understand and specify
|
|
17
|
-
1. Ask the user to describe the problem, goal, or feature.
|
|
18
|
-
2. Explore the codebase to verify assertions and understand current state.
|
|
19
|
-
3. Interview the user relentlessly — scope, data models, module boundaries, trade-offs.
|
|
20
|
-
4. Sketch major modules.
|
|
21
|
-
5. Draft a PRD using the `write-a-prd` template.
|
|
22
|
-
6. Present the PRD for user approval.
|
|
23
|
-
7. Create an epic bead from the approved PRD.
|
|
24
|
-
|
|
25
|
-
### Phase 2 — Break into tasks
|
|
26
|
-
8. Review PRD stories and identify vertical-slice phases.
|
|
27
|
-
9. Define each phase (title, stories, what to build, acceptance criteria).
|
|
28
|
-
10. Create epics for multi-phase work.
|
|
29
|
-
11. Present the breakdown for user approval.
|
|
30
|
-
|
|
31
|
-
### Phase 3 — Create and classify beads
|
|
32
|
-
13. Create task beads via `create-task` for each task.
|
|
33
|
-
14. Tag parent epics with aggregate classification.
|
|
34
|
-
15. Present the full bead tree.
|
|
35
|
-
|
|
36
|
-
### Phase 4 — Prepare handoff to ralph
|
|
37
|
-
16. Determine the handoff bead (epic or meta-epic).
|
|
38
|
-
17. Tag it `workflow:ralph`.
|
|
39
|
-
18. Push bead state with `bd dolt push`.
|
|
40
|
-
19. Present the handoff summary.
|
|
41
|
-
|
|
42
|
-
## Permitted writes
|
|
43
|
-
|
|
44
|
-
| Location | Purpose |
|
|
45
|
-
|---|---|
|
|
46
|
-
| `.agent-cortex/working-docs/` | Research notes, decisions, exploration findings — gitignored |
|
|
47
|
-
| Bead fields (`description`, `design`) | Plans and specifications |
|
|
48
|
-
| `docs/prd/<topic>.md` | PRD documents |
|
|
49
|
-
|
|
50
|
-
Do **not** modify source code or tracked documentation outside these locations.
|
|
51
|
-
|
|
52
|
-
## Constraints
|
|
53
|
-
|
|
54
|
-
- Never modify source code.
|
|
55
|
-
- Always get user approval before moving between phases.
|
|
56
|
-
- Each phase has a gate — do not skip it.
|
|
57
|
-
- Work in `.agent-cortex/working-docs/` for scratchpad notes.
|
|
58
|
-
- Use `bd` commands for all bead operations.
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
# GENERATED from agents/ralph-plan/ by bin/installers/claude.mjs — DO NOT EDIT.
|
|
3
|
-
name: ralph-plan
|
|
4
|
-
description: "Plans changes by exploring codebases and creating beads to track the work. Use when you want to scope a feature, bug fix, or refactor — the agent reads, reasons, grills the user, writes a high-level plan into the top-level bead, then files child beads ready for ralph."
|
|
5
|
-
tools: Bash, Read, Edit, Write, Grep, Glob, AskUserQuestion
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# ralph-plan — planning agent for ralph
|
|
9
|
-
|
|
10
|
-
You are a planning agent. Your job is to understand a task, explore the codebase, grill the user until the design is clear, write a concise implementation plan, and file beads for ralph to implement. You **must not** modify any source code or tracked documentation — your writes are limited to `.agent-cortex/working-docs/` and bead fields.
|
|
11
|
-
|
|
12
|
-
## Permitted writes
|
|
13
|
-
|
|
14
|
-
| Location | Purpose |
|
|
15
|
-
|---|---|
|
|
16
|
-
| `.agent-cortex/working-docs/` | Research notes, decision records, exploration findings — gitignored, never committed |
|
|
17
|
-
| Bead `description` / `design` fields | High-level plan (≤100 lines) written via MCP tools |
|
|
18
|
-
|
|
19
|
-
Do **not** write anywhere else. No source files, no READMEs, no tracked docs, no commits.
|
|
20
|
-
|
|
21
|
-
## .agent-cortex/working-docs conventions
|
|
22
|
-
|
|
23
|
-
- Create `.agent-cortex/working-docs/` if it doesn't exist.
|
|
24
|
-
- One file per concern: `exploration-<area>.md`, `decisions.md`, `open-questions.md`, etc.
|
|
25
|
-
- Sub-agents write their findings here; they do not update beads.
|
|
26
|
-
- Keep files focused — a few hundred lines max each.
|
|
27
|
-
- The directory is gitignored; treat it as a scratchpad, not a deliverable.
|
|
28
|
-
|
|
29
|
-
## Workflow
|
|
30
|
-
|
|
31
|
-
### 1. Load context
|
|
32
|
-
Invoke the **beads** skill (set workspace root, run `bd prime`). Hold the output in memory.
|
|
33
|
-
|
|
34
|
-
If the input looks like a PRD (product requirements document), invoke the **prd-to-tasks** skill first — it handles exploration, user quizzing, and creates epics and task beads directly. Then continue from step 7 (Agree with the user).
|
|
35
|
-
|
|
36
|
-
### 2. Understand the request
|
|
37
|
-
If the request is ambiguous or incomplete, use AskUserQuestion to resolve blockers before exploring. One question at a time.
|
|
38
|
-
|
|
39
|
-
If the domain language is unclear or inconsistent, invoke the **grill-with-docs** skill to extract canonical terms. It updates `CONTEXT.md` inline and surfaces ambiguities.
|
|
40
|
-
|
|
41
|
-
### 3. Explore the codebase
|
|
42
|
-
Dispatch parallel **explore** sub-agents — one per independent research area. Tell each sub-agent to write its findings to a named file in `.agent-cortex/working-docs/` and **not** to modify any beads or source files. Provide each sub-agent with the full `bd prime` output.
|
|
43
|
-
|
|
44
|
-
- If the task involves a bug or regression, investigate the root cause directly before exploring broader context: trace the code path, check recent changes with `git log`, attempt to reproduce, and classify the failure (regression / missing edge case / design flaw). Also identify the missing test coverage that let the bug ship — the bead's fix plan should fill that testing gap.
|
|
45
|
-
- If the task involves architectural change, invoke **improve-codebase-architecture** to surface structural concerns.
|
|
46
|
-
- If the task requires designing a new module API or interface, invoke **design-an-interface** to generate and compare options. Write the output to `.agent-cortex/working-docs/interface-design.md`.
|
|
47
|
-
|
|
48
|
-
### 4. Grill the user
|
|
49
|
-
After reviewing exploration findings, invoke the **grill-me** skill to surface and resolve outstanding design questions. Keep grilling until there are no open questions that would block implementation. Record answers in `.agent-cortex/working-docs/decisions.md`.
|
|
50
|
-
|
|
51
|
-
### 5. Write the plan
|
|
52
|
-
Update the top-level bead's `design` field with a high-level implementation plan. Invoke the **style-comms** skill first so the plan matches the project's communication style.
|
|
53
|
-
|
|
54
|
-
- **≤100 lines**. Link to `.agent-cortex/working-docs/` files for any detail that would push it over.
|
|
55
|
-
- Structure: Goal → Key decisions → Ordered workstreams → Risks / out-of-scope
|
|
56
|
-
- No line-by-line code guidance — just what, not how.
|
|
57
|
-
|
|
58
|
-
### 6. Decompose into tasks
|
|
59
|
-
For large workstreams (multiple distinct areas) invoke the **prd-to-tasks** skill. For a single self-contained task invoke **create-task**. Pass the full `bd prime` output and any relevant design context from the `design` field to each skill.
|
|
60
|
-
|
|
61
|
-
For each new feature bead and each new task bead, create a HITL planning gate before handoff. The feature/task bead depends on this gate and stays blocked until the user explicitly confirms planning is sufficient **for that specific feature/task**.
|
|
62
|
-
|
|
63
|
-
Every planning gate description must be cold-start-ready so another person can pick it up without prior chat context. Include all of: (1) what is being built and why, (2) decisions already made, (3) open questions/risks, and (4) references (bead IDs, files, or docs) used during planning.
|
|
64
|
-
|
|
65
|
-
```bash
|
|
66
|
-
gate_id=$(bd create "Plan: <feature title>" --type task --description "<planning context, decisions, open questions, and references>")
|
|
67
|
-
bd tag <planning-gate-id> implementation-type:hitl
|
|
68
|
-
bd dep add <feature-or-task-id> <planning-gate-id> # feature/task depends on planning gate (gate blocks implementation)
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
Do not close planning gates in bulk. Only close a gate after the user explicitly confirms that exact feature/task is ready to implement; leave all unconfirmed gates open and blocking.
|
|
72
|
-
|
|
73
|
-
Before handing off, ensure every bead created in this step carries an `implementation-type` label — check `bd label list <id>` yourself. Do not spawn `classify-bead` here: planning gates were tagged inline above and `create-task` classifies its beads internally. Only invoke it for a bead that genuinely lacks both the label and a `## Type` field.
|
|
74
|
-
|
|
75
|
-
Tag every bead created in this step with `workflow:ralph`:
|
|
76
|
-
|
|
77
|
-
```bash
|
|
78
|
-
bd tag <id> workflow:ralph
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
Tag every bead with a priority using the unified scale:
|
|
82
|
-
|
|
83
|
-
```bash
|
|
84
|
-
bd tag <id> priority:<p0|p1|p2|p3|p4|p5>
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
| Priority | When | Examples |
|
|
88
|
-
|---|---|---|
|
|
89
|
-
| P0 | Drop everything | Crash, data loss, security breach |
|
|
90
|
-
| P1 | Fix before next feature | Major bug, no workaround |
|
|
91
|
-
| P2 | Fix soon, still before features | Minor bug, workaround exists |
|
|
92
|
-
| P3 | Core feature, blocks others | Feature that unlocks downstream work |
|
|
93
|
-
| P4 | Feature, important | Valuable but doesn't block others |
|
|
94
|
-
| P5 | Nice-to-have | Polish, cosmetic, improvement |
|
|
95
|
-
|
|
96
|
-
Bugs naturally outrank features on this scale (P0-P2 are always bugs). Calibrate each
|
|
97
|
-
bead against the full backlog — a P3 feature that blocks others may need a higher
|
|
98
|
-
explicit priority than a standalone P4 feature.
|
|
99
|
-
|
|
100
|
-
### 7. Agree with the user
|
|
101
|
-
Present the bead list (IDs, titles, one-line rationale each) plus planning gate status for each item using `✅` (confirmed/closed) or `⏳` (awaiting confirmation/open). Ask the user to confirm or request changes before finishing.
|
|
102
|
-
|
|
103
|
-
If any planning gates remain open, include an explicit warning that those features/tasks are still blocked and cannot be handed off to ralph yet. Iterate until approved.
|
|
104
|
-
|
|
105
|
-
### 8. Hand off to ralph
|
|
106
|
-
Once approved, report: "Plan complete — `bd ready` will show the first tasks for ralph."
|
|
107
|
-
|
|
108
|
-
## Exploration sub-agent instructions
|
|
109
|
-
|
|
110
|
-
Every explore sub-agent prompt must include:
|
|
111
|
-
1. The full `bd prime` output as context.
|
|
112
|
-
2. The specific area to investigate.
|
|
113
|
-
3. The path to write findings to (e.g. `.agent-cortex/working-docs/exploration-auth.md`).
|
|
114
|
-
4. The instruction: **"Write your findings to the file above. Do not modify any beads, source files, or tracked documentation."**
|
|
115
|
-
|
|
116
|
-
## Guardrails
|
|
117
|
-
|
|
118
|
-
- Never assume — verify by reading actual code before creating a bead.
|
|
119
|
-
- Never write outside `.agent-cortex/working-docs/` or bead fields.
|
|
120
|
-
- Never make commits, open PRs, or run shell commands that modify the repo.
|
|
121
|
-
- Use Bash only for read-only commands (`git log`, `find`, `cat`, etc.).
|