@appsforgood/next-supabase-kit 0.1.3 → 0.1.5
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/CHANGELOG.md +16 -0
- package/DOGFOOD.md +15 -0
- package/LOOP_CODING.md +107 -0
- package/MAINTAINER_RELEASE.md +100 -0
- package/assistant-adapters/codex-agents.md +15 -0
- package/assistant-adapters/cursor-agent-kit.mdc +1 -0
- package/assistant-adapters/cursor-frontend.mdc +16 -0
- package/assistant-adapters/cursor-planner.mdc +14 -0
- package/assistant-adapters/cursor-security.mdc +18 -0
- package/assistant-adapters/model-selection/codex-config.example.toml +3 -0
- package/dist/index.js +976 -285
- package/dist/index.js.map +1 -1
- package/dist/studio/office/assets/office.css +79 -1
- package/dist/studio/office/assets/office.js +72 -1
- package/dist/studio/wizard/assets/wizard.css +52 -0
- package/dist/studio/wizard/assets/wizard.js +76 -5
- package/examples/next-supabase-installed/.agent-kit/manifest.json +7 -5
- package/examples/next-supabase-installed/.agent-kit/overrides.json +1 -7
- package/examples/next-supabase-installed/audit-output.json +25 -3
- package/examples/next-supabase-installed/tree.txt +1 -0
- package/package.json +8 -3
- package/research/summaries/agentic-engineering-maturity-levels.md +54 -0
- package/schemas/agentic-level.schema.json +47 -0
- package/schemas/onboarding-state.schema.json +4 -1
- package/templates/next-supabase/ASSISTANT_ADAPTERS.md +26 -2
- package/templates/next-supabase/DOCS.md +2 -0
- package/templates/next-supabase/LOOP_CODING.md +98 -0
- package/templates/next-supabase/TESTING.md +10 -0
package/dist/index.js
CHANGED
|
@@ -141,12 +141,12 @@ function addSkill(cwd, skillName, options = {}) {
|
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
// src/install/adapter-validate.ts
|
|
144
|
-
import { existsSync as
|
|
145
|
-
import { join as
|
|
144
|
+
import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
|
|
145
|
+
import { join as join12, normalize } from "path";
|
|
146
146
|
|
|
147
147
|
// src/config/defaults.ts
|
|
148
148
|
var PACKAGE_NAME = "@appsforgood/next-supabase-kit";
|
|
149
|
-
var PACKAGE_VERSION = "0.1.
|
|
149
|
+
var PACKAGE_VERSION = "0.1.5";
|
|
150
150
|
var DEFAULT_CONFIG = {
|
|
151
151
|
stack: "next-supabase",
|
|
152
152
|
projectType: "saas",
|
|
@@ -185,6 +185,7 @@ var ROOT_DOCS = [
|
|
|
185
185
|
"STYLE_GUIDE.md",
|
|
186
186
|
"SECURITY.md",
|
|
187
187
|
"TESTING.md",
|
|
188
|
+
"LOOP_CODING.md",
|
|
188
189
|
"DEPLOYMENT.md",
|
|
189
190
|
"UPGRADE.md"
|
|
190
191
|
];
|
|
@@ -215,6 +216,20 @@ var CURSOR_ADAPTER_FILES = [
|
|
|
215
216
|
target: ".cursor/rules/cursor-model-selection.mdc"
|
|
216
217
|
}
|
|
217
218
|
];
|
|
219
|
+
var CURSOR_SCOPED_ADAPTER_FILES = [
|
|
220
|
+
{
|
|
221
|
+
source: "assistant-adapters/cursor-planner.mdc",
|
|
222
|
+
target: ".cursor/rules/cursor-planner.mdc"
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
source: "assistant-adapters/cursor-security.mdc",
|
|
226
|
+
target: ".cursor/rules/cursor-security.mdc"
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
source: "assistant-adapters/cursor-frontend.mdc",
|
|
230
|
+
target: ".cursor/rules/cursor-frontend.mdc"
|
|
231
|
+
}
|
|
232
|
+
];
|
|
218
233
|
var COPILOT_INSTRUCTION_FILES = [
|
|
219
234
|
{
|
|
220
235
|
source: "assistant-adapters/github-copilot-instructions.md",
|
|
@@ -345,8 +360,8 @@ function unique(values) {
|
|
|
345
360
|
}
|
|
346
361
|
|
|
347
362
|
// src/install/audit.ts
|
|
348
|
-
import { existsSync as
|
|
349
|
-
import { join as
|
|
363
|
+
import { existsSync as existsSync11, readFileSync as readFileSync8, statSync } from "fs";
|
|
364
|
+
import { join as join11 } from "path";
|
|
350
365
|
|
|
351
366
|
// src/config/contracts.ts
|
|
352
367
|
import { z } from "zod";
|
|
@@ -615,6 +630,15 @@ var SessionEventContract = z.object({
|
|
|
615
630
|
context2.addIssue({ code: z.ZodIssueCode.custom, message: "required output events require outputName and outputStatus", path: ["outputName"] });
|
|
616
631
|
}
|
|
617
632
|
});
|
|
633
|
+
var AgenticLevelCore = z.union([z.literal(3), z.literal(4), z.literal(5), z.literal(6)]);
|
|
634
|
+
var AgenticLevelTarget = z.union([
|
|
635
|
+
z.literal(3),
|
|
636
|
+
z.literal(4),
|
|
637
|
+
z.literal(5),
|
|
638
|
+
z.literal(6),
|
|
639
|
+
z.literal(7),
|
|
640
|
+
z.literal(8)
|
|
641
|
+
]);
|
|
618
642
|
var OnboardingStateContract = z.object({
|
|
619
643
|
schemaVersion: z.literal(1),
|
|
620
644
|
depth: z.enum(["quick", "standard", "complete", "undecided"]),
|
|
@@ -628,7 +652,27 @@ var OnboardingStateContract = z.object({
|
|
|
628
652
|
wizardVersion: z.string(),
|
|
629
653
|
ideSurface: z.enum(["cursor", "copilot", "claude", "codex", "other"]).optional(),
|
|
630
654
|
ideVerifiedAt: z.string().datetime().optional(),
|
|
631
|
-
visualQaTier: z.enum(["baseline", "strong", "mature"]).optional()
|
|
655
|
+
visualQaTier: z.enum(["baseline", "strong", "mature"]).optional(),
|
|
656
|
+
targetAgenticLevel: AgenticLevelTarget.optional(),
|
|
657
|
+
lastAgenticLevel: AgenticLevelCore.optional(),
|
|
658
|
+
lastAgenticComputedAt: z.string().datetime().optional()
|
|
659
|
+
}).strict();
|
|
660
|
+
var AgenticLevelSignalContract = z.object({
|
|
661
|
+
id: z.string().min(1),
|
|
662
|
+
level: AgenticLevelCore,
|
|
663
|
+
label: z.string().min(1),
|
|
664
|
+
pass: z.boolean(),
|
|
665
|
+
evidence: z.string().min(1),
|
|
666
|
+
remediation: z.string().min(1)
|
|
667
|
+
}).strict();
|
|
668
|
+
var AgenticLevelContract = z.object({
|
|
669
|
+
currentLevel: AgenticLevelCore,
|
|
670
|
+
targetLevel: AgenticLevelTarget,
|
|
671
|
+
maintainerProfile: z.boolean(),
|
|
672
|
+
computedAt: z.string().datetime(),
|
|
673
|
+
maintainerNote: z.string().optional(),
|
|
674
|
+
signals: z.array(AgenticLevelSignalContract),
|
|
675
|
+
climbSteps: z.array(AgenticLevelSignalContract)
|
|
632
676
|
}).strict();
|
|
633
677
|
function formatContractIssues(error) {
|
|
634
678
|
return error.issues.map((issue) => {
|
|
@@ -1100,11 +1144,15 @@ function onboardingStateExists(cwd) {
|
|
|
1100
1144
|
}
|
|
1101
1145
|
|
|
1102
1146
|
// src/install/install.ts
|
|
1147
|
+
import { existsSync as existsSync10, readFileSync as readFileSync7 } from "fs";
|
|
1148
|
+
import { join as join10 } from "path";
|
|
1149
|
+
|
|
1150
|
+
// src/install/ide-activate.ts
|
|
1103
1151
|
import { existsSync as existsSync9, readFileSync as readFileSync6 } from "fs";
|
|
1104
1152
|
import { join as join9 } from "path";
|
|
1105
1153
|
|
|
1106
|
-
// src/install/
|
|
1107
|
-
import { existsSync as existsSync8, readFileSync as readFileSync5 } from "fs";
|
|
1154
|
+
// src/install/roster-adapters.ts
|
|
1155
|
+
import { existsSync as existsSync8, readFileSync as readFileSync5, readdirSync as readdirSync2 } from "fs";
|
|
1108
1156
|
import { join as join8 } from "path";
|
|
1109
1157
|
|
|
1110
1158
|
// src/studio/wizard/roster.ts
|
|
@@ -1188,8 +1236,224 @@ function agentBriefFieldName(agentId) {
|
|
|
1188
1236
|
return `agentBrief_${agentId}`;
|
|
1189
1237
|
}
|
|
1190
1238
|
|
|
1191
|
-
// src/install/
|
|
1239
|
+
// src/install/assistant-adapters-table.ts
|
|
1240
|
+
function extractAssistantAdapterRow(adaptersDoc, toolLabel) {
|
|
1241
|
+
for (const line of adaptersDoc.split("\n")) {
|
|
1242
|
+
const trimmed = line.trim();
|
|
1243
|
+
if (!trimmed.startsWith("|")) continue;
|
|
1244
|
+
const cells = trimmed.split("|").map((cell) => cell.trim()).filter((cell) => cell.length > 0);
|
|
1245
|
+
if (cells[0] === toolLabel) return trimmed;
|
|
1246
|
+
}
|
|
1247
|
+
return null;
|
|
1248
|
+
}
|
|
1249
|
+
function assistantAdapterRowIsActive(adaptersDoc, toolLabel) {
|
|
1250
|
+
const row = extractAssistantAdapterRow(adaptersDoc, toolLabel);
|
|
1251
|
+
if (!row) return false;
|
|
1252
|
+
const cells = row.split("|").map((cell) => cell.trim()).filter((cell) => cell.length > 0);
|
|
1253
|
+
const instructionStatus = cells[2] ?? "";
|
|
1254
|
+
return /^Active/i.test(instructionStatus) && !/^TBD/i.test(instructionStatus);
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
// src/install/roster-adapters.ts
|
|
1192
1258
|
var CANONICAL_READ_LIST = "`AGENTS.md`, `AGENT_ROSTER.md`, `.agent-kit/agent-roster.json`, `MODEL_ROUTING.md`, `.agent-kit/model-routing.json`, `.agent-kit/project-context.json`, `.agent-kit/project-context.md`, `.agent-kit/agent-briefs.md` when present, `.agent-kit/corrections/project-rules.json`, `.agent-kit/corrections/agent-rules.json`, `COUNCIL.md`, `.agent-kit/council-sessions/`, and `QUALITY_GATES.md`";
|
|
1259
|
+
function quoteYamlScalar(value) {
|
|
1260
|
+
return JSON.stringify(value);
|
|
1261
|
+
}
|
|
1262
|
+
function buildAgentHint(agentId, name) {
|
|
1263
|
+
if (agentId === "planner") return "Start with the Planner workflow.";
|
|
1264
|
+
if (agentId === "lead-architect") return "Convene council for core changes before implementation.";
|
|
1265
|
+
if (agentId === "frontend-design-lead") {
|
|
1266
|
+
return "Require brand/content intake, creative-direction rationale, and visual QA evidence for UI changes.";
|
|
1267
|
+
}
|
|
1268
|
+
if (agentId === "security-reviewer") {
|
|
1269
|
+
return "Review auth, RLS, data mutation, dependency, external-call, secret, and release-risk changes.";
|
|
1270
|
+
}
|
|
1271
|
+
return `Use for ${name.toLowerCase()} work defined in the roster.`;
|
|
1272
|
+
}
|
|
1273
|
+
function buildProactiveSuffix(agentId) {
|
|
1274
|
+
const suffixes = {
|
|
1275
|
+
planner: "Use proactively for planning, scope breakdown, ambiguous requests, and workflow routing.",
|
|
1276
|
+
"lead-architect": "Use proactively for core changes, architecture, and cross-layer decisions.",
|
|
1277
|
+
"security-reviewer": "Use proactively for auth, RLS, API, Server Action, data mutation, dependency, secret, and release-risk changes.",
|
|
1278
|
+
"frontend-design-lead": "Use proactively for UI, design system, accessibility, and visual QA work.",
|
|
1279
|
+
"qa-engineer": "Use proactively after behavior changes to add or verify tests and acceptance evidence.",
|
|
1280
|
+
"supabase-postgres-engineer": "Use proactively for schema, migrations, RLS, auth, and SQL changes.",
|
|
1281
|
+
"nextjs-engineer": "Use proactively for App Router, Server Components, route handlers, and UI state work.",
|
|
1282
|
+
"marketing-copy-lead": "Use proactively for public-facing copy, positioning, and conversion surfaces.",
|
|
1283
|
+
"docs-maintainer": "Use proactively after significant changes to update living documentation.",
|
|
1284
|
+
"deployment-observability-engineer": "Use proactively for release, env var, migration order, monitoring, and rollback work."
|
|
1285
|
+
};
|
|
1286
|
+
return suffixes[agentId] ?? "";
|
|
1287
|
+
}
|
|
1288
|
+
function buildSubagentDescription(agent, proactive) {
|
|
1289
|
+
const base = agent.roleSummary.length > 140 ? `${agent.roleSummary.slice(0, 137)}...` : agent.roleSummary;
|
|
1290
|
+
if (!proactive) return base;
|
|
1291
|
+
const suffix = buildProactiveSuffix(agent.id);
|
|
1292
|
+
return suffix ? `${base} ${suffix}` : base;
|
|
1293
|
+
}
|
|
1294
|
+
function buildSubagentMarkdown(agent, options = {}) {
|
|
1295
|
+
const description = buildSubagentDescription(agent, Boolean(options.proactive));
|
|
1296
|
+
const agentFile = agent.file ?? `.agent-kit/agents/${agent.id}.md`;
|
|
1297
|
+
const hint = buildAgentHint(agent.id, agent.name);
|
|
1298
|
+
return `---
|
|
1299
|
+
name: ${quoteYamlScalar(agent.id)}
|
|
1300
|
+
description: ${quoteYamlScalar(description)}
|
|
1301
|
+
---
|
|
1302
|
+
|
|
1303
|
+
Read ${CANONICAL_READ_LIST} before making routing or implementation decisions.
|
|
1304
|
+
|
|
1305
|
+
Also read \`${agentFile}\` for this role's detailed contract.
|
|
1306
|
+
|
|
1307
|
+
${hint}
|
|
1308
|
+
|
|
1309
|
+
For council work, delegate to this subagent instead of role-playing the council in the main thread.
|
|
1310
|
+
|
|
1311
|
+
Record meaningful decisions, risks, handoffs, human corrections, artifacts, evidence, and verification through \`agent-kit session checkpoint\` or individual \`agent-kit session ...\` commands when available.
|
|
1312
|
+
`;
|
|
1313
|
+
}
|
|
1314
|
+
function writeGeneratedAgentFile(cwd, relativePath, content, force, result) {
|
|
1315
|
+
const targetPath = join8(cwd, relativePath);
|
|
1316
|
+
if (!force && existsSync8(targetPath)) {
|
|
1317
|
+
const existing = readFileSync5(targetPath, "utf8");
|
|
1318
|
+
if (existing === content) {
|
|
1319
|
+
result.unchanged.push(relativePath);
|
|
1320
|
+
return;
|
|
1321
|
+
}
|
|
1322
|
+
const conflictPath = join8(cwd, ".agent-kit", "conflicts", relativePath.replace(/\//g, "__"));
|
|
1323
|
+
ensureDir(join8(cwd, ".agent-kit", "conflicts"));
|
|
1324
|
+
writeText(conflictPath, existing);
|
|
1325
|
+
result.conflicts.push(`${relativePath} -> ${conflictPath}`);
|
|
1326
|
+
return;
|
|
1327
|
+
}
|
|
1328
|
+
ensureDir(join8(cwd, relativePath.split("/").slice(0, -1).join("/")));
|
|
1329
|
+
writeText(targetPath, content);
|
|
1330
|
+
result.copied.push(relativePath);
|
|
1331
|
+
}
|
|
1332
|
+
function generateMarkdownSubagents(cwd, agentsDir, options) {
|
|
1333
|
+
ensureDir(join8(cwd, agentsDir));
|
|
1334
|
+
for (const agent of loadProjectRosterAgents(cwd)) {
|
|
1335
|
+
const relativePath = `${agentsDir}/${agent.id}.md`;
|
|
1336
|
+
writeGeneratedAgentFile(cwd, relativePath, buildSubagentMarkdown(agent, options), options.force, options.result);
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
var CURSOR_AGENTS_README = `# Cursor council subagents
|
|
1340
|
+
|
|
1341
|
+
Project subagents generated from \`.agent-kit/agent-roster.json\`. Use them for isolated specialist context instead of role-playing the whole council in one chat.
|
|
1342
|
+
|
|
1343
|
+
## Delegation
|
|
1344
|
+
|
|
1345
|
+
| Risk / work type | Subagent |
|
|
1346
|
+
| --- | --- |
|
|
1347
|
+
| Planning / scope | \`@planner\` |
|
|
1348
|
+
| Core architecture | \`@lead-architect\` |
|
|
1349
|
+
| Auth / RLS / secrets | \`@security-reviewer\` or Task \`security-review\` |
|
|
1350
|
+
| Frontend UI | \`@frontend-design-lead\` |
|
|
1351
|
+
| QA / tests | \`@qa-engineer\` |
|
|
1352
|
+
|
|
1353
|
+
Record handoffs with \`agent-kit session checkpoint --file <json>\` when the CLI is available.
|
|
1354
|
+
|
|
1355
|
+
Regenerate with \`agent-kit init --activate cursor\` after roster changes.
|
|
1356
|
+
`;
|
|
1357
|
+
function generateCursorSubagents(cwd, force, result) {
|
|
1358
|
+
generateMarkdownSubagents(cwd, ".cursor/agents", { proactive: true, force, result });
|
|
1359
|
+
writeGeneratedAgentFile(cwd, ".cursor/agents/README.md", CURSOR_AGENTS_README, force, result);
|
|
1360
|
+
}
|
|
1361
|
+
function loadAgentReasoningEffortMap(cwd) {
|
|
1362
|
+
const path = join8(cwd, ".agent-kit/model-routing.json");
|
|
1363
|
+
const map = /* @__PURE__ */ new Map();
|
|
1364
|
+
if (!existsSync8(path)) return map;
|
|
1365
|
+
try {
|
|
1366
|
+
const parsed = JSON.parse(readFileSync5(path, "utf8"));
|
|
1367
|
+
const profileEffort = /* @__PURE__ */ new Map();
|
|
1368
|
+
for (const profile of parsed.profiles ?? []) {
|
|
1369
|
+
if (profile.id && profile.reasoningEffort) {
|
|
1370
|
+
const effort = profile.reasoningEffort;
|
|
1371
|
+
if (effort === "low" || effort === "medium" || effort === "high") {
|
|
1372
|
+
profileEffort.set(profile.id, effort);
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
for (const binding of parsed.agentRoutes ?? []) {
|
|
1377
|
+
if (!binding.agentId) continue;
|
|
1378
|
+
const direct = binding.defaultEffort;
|
|
1379
|
+
if (direct === "low" || direct === "medium" || direct === "high") {
|
|
1380
|
+
map.set(binding.agentId, direct);
|
|
1381
|
+
continue;
|
|
1382
|
+
}
|
|
1383
|
+
if (binding.profileId && profileEffort.has(binding.profileId)) {
|
|
1384
|
+
map.set(binding.agentId, profileEffort.get(binding.profileId));
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
} catch {
|
|
1388
|
+
return map;
|
|
1389
|
+
}
|
|
1390
|
+
return map;
|
|
1391
|
+
}
|
|
1392
|
+
function escapeTomlString(value) {
|
|
1393
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
1394
|
+
}
|
|
1395
|
+
function buildCodexAgentToml(agent, effort) {
|
|
1396
|
+
const description = buildSubagentDescription(agent, true);
|
|
1397
|
+
const agentFile = agent.file ?? `.agent-kit/agents/${agent.id}.md`;
|
|
1398
|
+
const hint = buildAgentHint(agent.id, agent.name);
|
|
1399
|
+
const instructions = [
|
|
1400
|
+
`Read AGENTS.md, AGENT_ROSTER.md, .agent-kit/agent-roster.json, MODEL_ROUTING.md,`,
|
|
1401
|
+
`.agent-kit/model-routing.json, project context, corrections, COUNCIL.md, QUALITY_GATES.md,`,
|
|
1402
|
+
`and ${agentFile} before reviewing or implementing.`,
|
|
1403
|
+
"",
|
|
1404
|
+
hint,
|
|
1405
|
+
"",
|
|
1406
|
+
"Record meaningful decisions, risks, handoffs, and verification through agent-kit session checkpoint when available."
|
|
1407
|
+
].join("\n");
|
|
1408
|
+
return `name = "${agent.id}"
|
|
1409
|
+
description = "${escapeTomlString(description)}"
|
|
1410
|
+
# model = "gpt-5.5" # verify in your Codex environment; see MODEL_ROUTING.md
|
|
1411
|
+
model_reasoning_effort = "${effort}"
|
|
1412
|
+
|
|
1413
|
+
developer_instructions = """
|
|
1414
|
+
${instructions}
|
|
1415
|
+
"""
|
|
1416
|
+
`;
|
|
1417
|
+
}
|
|
1418
|
+
function generateCodexCustomAgents(cwd, force, result) {
|
|
1419
|
+
ensureDir(join8(cwd, ".codex/agents"));
|
|
1420
|
+
const effortMap = loadAgentReasoningEffortMap(cwd);
|
|
1421
|
+
for (const agent of loadProjectRosterAgents(cwd)) {
|
|
1422
|
+
const effort = effortMap.get(agent.id) ?? "medium";
|
|
1423
|
+
const relativePath = `.codex/agents/${agent.id}.toml`;
|
|
1424
|
+
writeGeneratedAgentFile(cwd, relativePath, buildCodexAgentToml(agent, effort), force, result);
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
function skillDescriptionFromMarkdown(text, skillId) {
|
|
1428
|
+
const useWhen = text.match(/## Use When\s*\n+\s*([^\n#]+)/);
|
|
1429
|
+
if (useWhen?.[1]) return useWhen[1].trim().slice(0, 200);
|
|
1430
|
+
const firstHeading = text.match(/^#\s+(.+)/m);
|
|
1431
|
+
if (firstHeading?.[1]) return `${firstHeading[1].trim()} \u2014 Agent Kit council skill.`;
|
|
1432
|
+
return `Agent Kit skill for ${skillId.replace(/-/g, " ")}.`;
|
|
1433
|
+
}
|
|
1434
|
+
function kitSkillToCursorSkill(skillId, kitMarkdown) {
|
|
1435
|
+
const description = skillDescriptionFromMarkdown(kitMarkdown, skillId);
|
|
1436
|
+
const body = kitMarkdown.replace(/^#\s+.+\n+/, "").trimStart();
|
|
1437
|
+
return `---
|
|
1438
|
+
name: ${quoteYamlScalar(skillId)}
|
|
1439
|
+
description: ${quoteYamlScalar(description)}
|
|
1440
|
+
---
|
|
1441
|
+
|
|
1442
|
+
${body.trim()}
|
|
1443
|
+
`;
|
|
1444
|
+
}
|
|
1445
|
+
function generateCursorSkillsFromKit(cwd, force, result) {
|
|
1446
|
+
const skillsRoot = join8(cwd, ".agent-kit/skills");
|
|
1447
|
+
if (!existsSync8(skillsRoot)) return;
|
|
1448
|
+
for (const file of readdirSync2(skillsRoot).filter((name) => name.endsWith(".md"))) {
|
|
1449
|
+
const skillId = file.replace(/\.md$/, "");
|
|
1450
|
+
const kitMarkdown = readFileSync5(join8(skillsRoot, file), "utf8");
|
|
1451
|
+
const relativePath = `.cursor/skills/${skillId}/SKILL.md`;
|
|
1452
|
+
writeGeneratedAgentFile(cwd, relativePath, kitSkillToCursorSkill(skillId, kitMarkdown), force, result);
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
// src/install/ide-activate.ts
|
|
1193
1457
|
function normalizeTargets(targets) {
|
|
1194
1458
|
const allowed = /* @__PURE__ */ new Set(["cursor", "claude", "codex", "copilot", "antigravity"]);
|
|
1195
1459
|
const normalized = /* @__PURE__ */ new Set();
|
|
@@ -1204,9 +1468,9 @@ function normalizeTargets(targets) {
|
|
|
1204
1468
|
return [...normalized];
|
|
1205
1469
|
}
|
|
1206
1470
|
function copyAdapterFile(cwd, packageRoot, source, target, force, result) {
|
|
1207
|
-
const copyResult = copyTextWithConflict(
|
|
1471
|
+
const copyResult = copyTextWithConflict(join9(packageRoot, source), cwd, target, {
|
|
1208
1472
|
force,
|
|
1209
|
-
conflictRoot:
|
|
1473
|
+
conflictRoot: join9(cwd, ".agent-kit", "conflicts")
|
|
1210
1474
|
});
|
|
1211
1475
|
if (copyResult.action === "created") result.copied.push(copyResult.target);
|
|
1212
1476
|
if (copyResult.action === "unchanged") result.unchanged.push(copyResult.target);
|
|
@@ -1215,53 +1479,18 @@ function copyAdapterFile(cwd, packageRoot, source, target, force, result) {
|
|
|
1215
1479
|
result.conflicts.push(`${copyResult.target} -> ${copyResult.conflictPath}`);
|
|
1216
1480
|
}
|
|
1217
1481
|
}
|
|
1218
|
-
function buildClaudeSubagentMarkdown(agentId, name, description) {
|
|
1219
|
-
const defaultForHint = agentId === "planner" ? "Start with the Planner workflow." : agentId === "lead-architect" ? "Convene council for core changes before implementation." : agentId === "frontend-design-lead" ? "Require brand/content intake, creative-direction rationale, and visual QA evidence for UI changes." : agentId === "security-reviewer" ? "Review auth, RLS, data mutation, dependency, external-call, secret, and release-risk changes." : `Use for ${name.toLowerCase()} work defined in the roster.`;
|
|
1220
|
-
return `---
|
|
1221
|
-
name: ${agentId}
|
|
1222
|
-
description: ${description}
|
|
1223
|
-
---
|
|
1224
|
-
|
|
1225
|
-
Read ${CANONICAL_READ_LIST} before making routing or implementation decisions.
|
|
1226
|
-
|
|
1227
|
-
${defaultForHint}
|
|
1228
|
-
|
|
1229
|
-
Record meaningful decisions, risks, handoffs, human corrections, artifacts, evidence, and verification through \`agent-kit session checkpoint\` or individual \`agent-kit session ...\` commands when available.
|
|
1230
|
-
`;
|
|
1231
|
-
}
|
|
1232
1482
|
function generateClaudeSubagents(cwd, packageRoot, force, result) {
|
|
1233
|
-
|
|
1234
|
-
const agents = loadProjectRosterAgents(cwd);
|
|
1235
|
-
for (const agent of agents) {
|
|
1236
|
-
const description = agent.roleSummary.length > 180 ? `${agent.roleSummary.slice(0, 177)}...` : agent.roleSummary;
|
|
1237
|
-
const target = `.claude/agents/${agent.id}.md`;
|
|
1238
|
-
const content = buildClaudeSubagentMarkdown(agent.id, agent.name, description);
|
|
1239
|
-
const targetPath = join8(cwd, target);
|
|
1240
|
-
if (!force && existsSync8(targetPath)) {
|
|
1241
|
-
const existing = readFileSync5(targetPath, "utf8");
|
|
1242
|
-
if (existing === content) {
|
|
1243
|
-
result.unchanged.push(target);
|
|
1244
|
-
continue;
|
|
1245
|
-
}
|
|
1246
|
-
const conflictPath = join8(cwd, ".agent-kit", "conflicts", target.replace(/\//g, "__"));
|
|
1247
|
-
ensureDir(join8(cwd, ".agent-kit", "conflicts"));
|
|
1248
|
-
writeText(conflictPath, existing);
|
|
1249
|
-
result.conflicts.push(`${target} -> ${conflictPath}`);
|
|
1250
|
-
continue;
|
|
1251
|
-
}
|
|
1252
|
-
writeText(targetPath, content);
|
|
1253
|
-
result.copied.push(target);
|
|
1254
|
-
}
|
|
1483
|
+
generateMarkdownSubagents(cwd, ".claude/agents", { proactive: false, force, result });
|
|
1255
1484
|
copyAdapterFile(cwd, packageRoot, CLAUDE_TEMPLATE, "CLAUDE.md", force, result);
|
|
1256
1485
|
}
|
|
1257
1486
|
function copyDirectoryAsConflicts(cwd, packageRoot, sourceDir, targetDir, force, result) {
|
|
1258
|
-
for (const file of listFilesRecursive(
|
|
1259
|
-
copyAdapterFile(cwd, packageRoot,
|
|
1487
|
+
for (const file of listFilesRecursive(join9(packageRoot, sourceDir))) {
|
|
1488
|
+
copyAdapterFile(cwd, packageRoot, join9(sourceDir, file), join9(targetDir, file).replace(/\\/g, "/"), force, result);
|
|
1260
1489
|
}
|
|
1261
1490
|
}
|
|
1262
1491
|
function installAntigravityAdapter(cwd, packageRoot, force, result) {
|
|
1263
|
-
ensureDir(
|
|
1264
|
-
ensureDir(
|
|
1492
|
+
ensureDir(join9(cwd, ".antigravity", "agent-kit", "commands"));
|
|
1493
|
+
ensureDir(join9(cwd, ".antigravity", "runtime-skills"));
|
|
1265
1494
|
for (const file of ANTIGRAVITY_PLUGIN_FILES) {
|
|
1266
1495
|
copyAdapterFile(cwd, packageRoot, file.source, file.target, force, result);
|
|
1267
1496
|
}
|
|
@@ -1283,38 +1512,38 @@ function installAntigravityAdapter(cwd, packageRoot, force, result) {
|
|
|
1283
1512
|
);
|
|
1284
1513
|
}
|
|
1285
1514
|
function updateAssistantAdaptersTable(cwd, activated) {
|
|
1286
|
-
const path =
|
|
1287
|
-
if (!
|
|
1288
|
-
let content =
|
|
1515
|
+
const path = join9(cwd, "ASSISTANT_ADAPTERS.md");
|
|
1516
|
+
if (!existsSync9(path)) return;
|
|
1517
|
+
let content = readFileSync6(path, "utf8");
|
|
1289
1518
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
1290
1519
|
if (activated.has("cursor") && content.includes("| Cursor |")) {
|
|
1291
1520
|
content = content.replace(
|
|
1292
1521
|
/\| Cursor \|[^|]*\|[^|]*\|[^|]*\|[^|]*\|[^|]*\|[^|]*\|/,
|
|
1293
|
-
|
|
1522
|
+
`| Cursor | \`.cursor/rules/*.mdc\`, \`.cursor/agents/*.md\`, \`.cursor/skills/*/SKILL.md\` | Active | Partial | Partial | \`agent-kit init --activate cursor\` on ${today}; verify subagents in Cursor. | Delegate to council subagents instead of role-playing; run \`agent-kit adapter validate cursor\`. |`
|
|
1294
1523
|
);
|
|
1295
1524
|
}
|
|
1296
1525
|
if (activated.has("copilot") && content.includes("| GitHub Copilot")) {
|
|
1297
1526
|
content = content.replace(
|
|
1298
1527
|
/\| GitHub Copilot[^|]*\|[^|]*\|[^|]*\|[^|]*\|[^|]*\|[^|]*\|[^|]*\|/,
|
|
1299
|
-
|
|
1528
|
+
`| GitHub Copilot / VS Code | \`.github/copilot-instructions.md\` and \`.github/instructions/next-supabase.instructions.md\` | Active | Advisory | Advisory | \`agent-kit init --activate copilot\` on ${today}. | Copilot loads repository and path-specific instructions automatically in VS Code. |`
|
|
1300
1529
|
);
|
|
1301
1530
|
}
|
|
1302
1531
|
if (activated.has("claude") && content.includes("| Claude Code |")) {
|
|
1303
1532
|
content = content.replace(
|
|
1304
1533
|
/\| Claude Code \|[^|]*\|[^|]*\|[^|]*\|[^|]*\|[^|]*\|[^|]*\|/,
|
|
1305
|
-
|
|
1534
|
+
`| Claude Code | \`.claude/agents/*.md\` and \`CLAUDE.md\` | Active | Partial | Partial | \`agent-kit init --activate claude\` generated subagents on ${today}. | Subagents generated from \`.agent-kit/agent-roster.json\`; verify in Claude Code project settings. |`
|
|
1306
1535
|
);
|
|
1307
1536
|
}
|
|
1308
1537
|
if (activated.has("codex") && content.includes("| Codex / AGENTS.md-compatible tools |")) {
|
|
1309
1538
|
content = content.replace(
|
|
1310
1539
|
/\| Codex \/ AGENTS\.md-compatible tools \|[^|]*\|[^|]*\|[^|]*\|[^|]*\|[^|]*\|[^|]*\|/,
|
|
1311
|
-
|
|
1540
|
+
`| Codex / AGENTS.md-compatible tools | \`AGENTS.md\`, \`.codex/config.toml\`, \`.codex/agents/*.toml\` | Active | Partial | Partial | \`agent-kit init --activate codex\` on ${today}. | Spawn council custom agents from \`.codex/agents/\`; run \`agent-kit adapter validate codex\`. |`
|
|
1312
1541
|
);
|
|
1313
1542
|
}
|
|
1314
1543
|
if (activated.has("antigravity") && content.includes("| Antigravity |")) {
|
|
1315
1544
|
content = content.replace(
|
|
1316
1545
|
/\| Antigravity \|[^|]*\|[^|]*\|[^|]*\|[^|]*\|[^|]*\|[^|]*\|/,
|
|
1317
|
-
|
|
1546
|
+
`| Antigravity | \`.antigravity/agent-kit/plugin.json\`, \`.antigravity/agent-kit/commands/*.toml\`, \`.antigravity/runtime-skills/*/SKILL.md\` | Active | Advisory | Advisory | \`agent-kit init --activate antigravity\` on ${today}; run \`agent-kit adapter validate antigravity\`. | Native commands wrap the Agent Kit council/session contract; runtime validation is structural unless \`agy\` is installed. |`
|
|
1318
1547
|
);
|
|
1319
1548
|
}
|
|
1320
1549
|
writeText(path, content);
|
|
@@ -1341,9 +1570,14 @@ function activateIdeTargets(options) {
|
|
|
1341
1570
|
for (const adapter2 of CURSOR_ADAPTER_FILES) {
|
|
1342
1571
|
copyAdapterFile(cwd, packageRoot, adapter2.source, adapter2.target, force, result);
|
|
1343
1572
|
}
|
|
1573
|
+
for (const adapter2 of CURSOR_SCOPED_ADAPTER_FILES) {
|
|
1574
|
+
copyAdapterFile(cwd, packageRoot, adapter2.source, adapter2.target, force, result);
|
|
1575
|
+
}
|
|
1576
|
+
generateCursorSubagents(cwd, force, result);
|
|
1577
|
+
generateCursorSkillsFromKit(cwd, force, result);
|
|
1344
1578
|
}
|
|
1345
1579
|
if (activated.has("copilot")) {
|
|
1346
|
-
ensureDir(
|
|
1580
|
+
ensureDir(join9(cwd, ".github", "instructions"));
|
|
1347
1581
|
for (const file of COPILOT_INSTRUCTION_FILES) {
|
|
1348
1582
|
copyAdapterFile(cwd, packageRoot, file.source, file.target, force, result);
|
|
1349
1583
|
}
|
|
@@ -1352,8 +1586,9 @@ function activateIdeTargets(options) {
|
|
|
1352
1586
|
generateClaudeSubagents(cwd, packageRoot, force, result);
|
|
1353
1587
|
}
|
|
1354
1588
|
if (activated.has("codex")) {
|
|
1355
|
-
ensureDir(
|
|
1589
|
+
ensureDir(join9(cwd, ".codex"));
|
|
1356
1590
|
copyAdapterFile(cwd, packageRoot, CODEX_CONFIG_SOURCE, ".codex/config.toml", force, result);
|
|
1591
|
+
generateCodexCustomAgents(cwd, force, result);
|
|
1357
1592
|
}
|
|
1358
1593
|
if (activated.has("antigravity")) {
|
|
1359
1594
|
installAntigravityAdapter(cwd, packageRoot, force, result);
|
|
@@ -1361,18 +1596,25 @@ function activateIdeTargets(options) {
|
|
|
1361
1596
|
updateAssistantAdaptersTable(cwd, activated);
|
|
1362
1597
|
return result;
|
|
1363
1598
|
}
|
|
1599
|
+
function ideSurfaceToActivateTarget(ideSurface) {
|
|
1600
|
+
const value = ideSurface.trim().toLowerCase();
|
|
1601
|
+
if (value === "cursor" || value === "claude" || value === "codex" || value === "copilot") {
|
|
1602
|
+
return value;
|
|
1603
|
+
}
|
|
1604
|
+
return null;
|
|
1605
|
+
}
|
|
1364
1606
|
|
|
1365
1607
|
// src/install/install.ts
|
|
1366
1608
|
function initProject(options) {
|
|
1367
1609
|
const cwd = options.cwd;
|
|
1368
1610
|
const stack = options.stack ?? DEFAULT_CONFIG.stack;
|
|
1369
1611
|
const packageRoot = findPackageRoot();
|
|
1370
|
-
const templateRoot =
|
|
1371
|
-
if (!
|
|
1612
|
+
const templateRoot = join10(packageRoot, "templates", stack);
|
|
1613
|
+
if (!existsSync10(templateRoot)) {
|
|
1372
1614
|
throw new Error(`Unsupported stack profile: ${stack}`);
|
|
1373
1615
|
}
|
|
1374
|
-
ensureDir(
|
|
1375
|
-
ensureDir(
|
|
1616
|
+
ensureDir(join10(cwd, ".agent-kit"));
|
|
1617
|
+
ensureDir(join10(cwd, ".agent-kit", "conflicts"));
|
|
1376
1618
|
const result = {
|
|
1377
1619
|
copied: [],
|
|
1378
1620
|
unchanged: [],
|
|
@@ -1382,11 +1624,11 @@ function initProject(options) {
|
|
|
1382
1624
|
};
|
|
1383
1625
|
const templateHashes = {};
|
|
1384
1626
|
for (const doc of ROOT_DOCS) {
|
|
1385
|
-
const templatePath =
|
|
1386
|
-
templateHashes[doc] = sha256(
|
|
1627
|
+
const templatePath = join10(templateRoot, doc);
|
|
1628
|
+
templateHashes[doc] = sha256(readFileSync7(templatePath, "utf8"));
|
|
1387
1629
|
const copyResult = copyTextWithConflict(templatePath, cwd, doc, {
|
|
1388
1630
|
force: Boolean(options.force),
|
|
1389
|
-
conflictRoot:
|
|
1631
|
+
conflictRoot: join10(cwd, ".agent-kit", "conflicts")
|
|
1390
1632
|
});
|
|
1391
1633
|
if (copyResult.action === "created") result.copied.push(copyResult.target);
|
|
1392
1634
|
if (copyResult.action === "unchanged") result.unchanged.push(copyResult.target);
|
|
@@ -1396,12 +1638,12 @@ function initProject(options) {
|
|
|
1396
1638
|
}
|
|
1397
1639
|
}
|
|
1398
1640
|
for (const folder of LIBRARY_FOLDERS) {
|
|
1399
|
-
copyDirectory(
|
|
1641
|
+
copyDirectory(join10(packageRoot, folder), join10(cwd, ".agent-kit", folder));
|
|
1400
1642
|
}
|
|
1401
1643
|
for (const adapter2 of CURSOR_ADAPTER_FILES) {
|
|
1402
|
-
const adapterCopy = copyTextWithConflict(
|
|
1644
|
+
const adapterCopy = copyTextWithConflict(join10(packageRoot, adapter2.source), cwd, adapter2.target, {
|
|
1403
1645
|
force: Boolean(options.force),
|
|
1404
|
-
conflictRoot:
|
|
1646
|
+
conflictRoot: join10(cwd, ".agent-kit", "conflicts")
|
|
1405
1647
|
});
|
|
1406
1648
|
if (adapterCopy.action === "created") result.copied.push(adapterCopy.target);
|
|
1407
1649
|
if (adapterCopy.action === "unchanged") result.unchanged.push(adapterCopy.target);
|
|
@@ -1410,17 +1652,17 @@ function initProject(options) {
|
|
|
1410
1652
|
result.conflicts.push(`${adapterCopy.target} -> ${adapterCopy.conflictPath}`);
|
|
1411
1653
|
}
|
|
1412
1654
|
}
|
|
1413
|
-
const rosterCopy = copyTextWithConflict(
|
|
1655
|
+
const rosterCopy = copyTextWithConflict(join10(packageRoot, DEFAULT_AGENT_ROSTER_SOURCE), cwd, DEFAULT_AGENT_ROSTER_TARGET, {
|
|
1414
1656
|
force: Boolean(options.force),
|
|
1415
|
-
conflictRoot:
|
|
1657
|
+
conflictRoot: join10(cwd, ".agent-kit", "conflicts")
|
|
1416
1658
|
});
|
|
1417
1659
|
if (rosterCopy.action === "created") result.copied.push(rosterCopy.target);
|
|
1418
1660
|
if (rosterCopy.action === "unchanged") result.unchanged.push(rosterCopy.target);
|
|
1419
1661
|
if (rosterCopy.action === "overwritten") result.overwritten.push(rosterCopy.target);
|
|
1420
1662
|
if (rosterCopy.action === "conflict") result.conflicts.push(`${rosterCopy.target} -> ${rosterCopy.conflictPath}`);
|
|
1421
|
-
const modelRoutingCopy = copyTextWithConflict(
|
|
1663
|
+
const modelRoutingCopy = copyTextWithConflict(join10(packageRoot, DEFAULT_MODEL_ROUTING_SOURCE), cwd, DEFAULT_MODEL_ROUTING_TARGET, {
|
|
1422
1664
|
force: Boolean(options.force),
|
|
1423
|
-
conflictRoot:
|
|
1665
|
+
conflictRoot: join10(cwd, ".agent-kit", "conflicts")
|
|
1424
1666
|
});
|
|
1425
1667
|
if (modelRoutingCopy.action === "created") result.copied.push(modelRoutingCopy.target);
|
|
1426
1668
|
if (modelRoutingCopy.action === "unchanged") result.unchanged.push(modelRoutingCopy.target);
|
|
@@ -1437,17 +1679,17 @@ function initProject(options) {
|
|
|
1437
1679
|
modelRouting: DEFAULT_MODEL_ROUTING_TARGET,
|
|
1438
1680
|
templateHashes
|
|
1439
1681
|
};
|
|
1440
|
-
writeText(
|
|
1682
|
+
writeText(join10(cwd, ".agent-kit", "manifest.json"), `${JSON.stringify(manifest, null, 2)}
|
|
1441
1683
|
`);
|
|
1442
|
-
writeText(
|
|
1684
|
+
writeText(join10(cwd, ".agent-kit", "config.json"), `${JSON.stringify(DEFAULT_CONFIG, null, 2)}
|
|
1443
1685
|
`);
|
|
1444
|
-
const overridesPath =
|
|
1445
|
-
if (!
|
|
1686
|
+
const overridesPath = join10(cwd, ".agent-kit", "overrides.json");
|
|
1687
|
+
if (!existsSync10(overridesPath)) writeText(overridesPath, `${JSON.stringify({ templates: {} }, null, 2)}
|
|
1446
1688
|
`);
|
|
1447
1689
|
for (const template of CI_TEMPLATE_FILES) {
|
|
1448
|
-
const ciCopy = copyTextWithConflict(
|
|
1690
|
+
const ciCopy = copyTextWithConflict(join10(packageRoot, template.source), cwd, template.target, {
|
|
1449
1691
|
force: Boolean(options.force),
|
|
1450
|
-
conflictRoot:
|
|
1692
|
+
conflictRoot: join10(cwd, ".agent-kit", "conflicts")
|
|
1451
1693
|
});
|
|
1452
1694
|
if (ciCopy.action === "created") result.copied.push(ciCopy.target);
|
|
1453
1695
|
if (ciCopy.action === "unchanged") result.unchanged.push(ciCopy.target);
|
|
@@ -1471,9 +1713,9 @@ function initProject(options) {
|
|
|
1471
1713
|
return result;
|
|
1472
1714
|
}
|
|
1473
1715
|
function readManifest(cwd) {
|
|
1474
|
-
const manifestPath =
|
|
1475
|
-
if (!
|
|
1476
|
-
return JSON.parse(
|
|
1716
|
+
const manifestPath = join10(cwd, ".agent-kit", "manifest.json");
|
|
1717
|
+
if (!existsSync10(manifestPath)) return null;
|
|
1718
|
+
return JSON.parse(readFileSync7(manifestPath, "utf8"));
|
|
1477
1719
|
}
|
|
1478
1720
|
|
|
1479
1721
|
// src/install/audit.ts
|
|
@@ -1523,7 +1765,8 @@ var REQUIRED_SCHEMA_FILES = [
|
|
|
1523
1765
|
"correction-rules.schema.json",
|
|
1524
1766
|
"session-event.schema.json",
|
|
1525
1767
|
"studio-session.schema.json",
|
|
1526
|
-
"onboarding-state.schema.json"
|
|
1768
|
+
"onboarding-state.schema.json",
|
|
1769
|
+
"agentic-level.schema.json"
|
|
1527
1770
|
];
|
|
1528
1771
|
var COUNCIL_SESSION_DIR = ".agent-kit/council-sessions";
|
|
1529
1772
|
var READINESS_ORDER = ["needs-setup", "baseline-setup", "needs-improvement", "best-practice-candidate"];
|
|
@@ -1542,24 +1785,24 @@ function includesAll(text, values) {
|
|
|
1542
1785
|
return values.every((value) => lower.includes(value.toLowerCase()));
|
|
1543
1786
|
}
|
|
1544
1787
|
function readDoc(cwd, file) {
|
|
1545
|
-
const path =
|
|
1546
|
-
return
|
|
1788
|
+
const path = join11(cwd, file);
|
|
1789
|
+
return existsSync11(path) ? readFileSync8(path, "utf8") : "";
|
|
1547
1790
|
}
|
|
1548
1791
|
function isPackageRepository(cwd) {
|
|
1549
|
-
const packagePath =
|
|
1550
|
-
if (!
|
|
1792
|
+
const packagePath = join11(cwd, "package.json");
|
|
1793
|
+
if (!existsSync11(packagePath)) return false;
|
|
1551
1794
|
try {
|
|
1552
|
-
const packageJson = JSON.parse(
|
|
1553
|
-
return packageJson.name === "@appsforgood/next-supabase-kit" &&
|
|
1795
|
+
const packageJson = JSON.parse(readFileSync8(packagePath, "utf8"));
|
|
1796
|
+
return packageJson.name === "@appsforgood/next-supabase-kit" && existsSync11(join11(cwd, "src", "cli", "index.ts")) && existsSync11(join11(cwd, "templates", "next-supabase")) && existsSync11(join11(cwd, "rosters", "next-supabase-default-council.json"));
|
|
1554
1797
|
} catch {
|
|
1555
1798
|
return false;
|
|
1556
1799
|
}
|
|
1557
1800
|
}
|
|
1558
1801
|
function readOverrides(cwd) {
|
|
1559
|
-
const path =
|
|
1560
|
-
if (!
|
|
1802
|
+
const path = join11(cwd, ".agent-kit", "overrides.json");
|
|
1803
|
+
if (!existsSync11(path)) return {};
|
|
1561
1804
|
try {
|
|
1562
|
-
const parsed = JSON.parse(
|
|
1805
|
+
const parsed = JSON.parse(readFileSync8(path, "utf8"));
|
|
1563
1806
|
const templates = parsed.templates ?? {};
|
|
1564
1807
|
return Object.fromEntries(
|
|
1565
1808
|
Object.entries(templates).map(([file, override]) => [
|
|
@@ -1572,8 +1815,8 @@ function readOverrides(cwd) {
|
|
|
1572
1815
|
}
|
|
1573
1816
|
}
|
|
1574
1817
|
function readTemplate(stack, file) {
|
|
1575
|
-
const path =
|
|
1576
|
-
return
|
|
1818
|
+
const path = join11(findPackageRoot(), "templates", stack, file);
|
|
1819
|
+
return existsSync11(path) ? readFileSync8(path, "utf8") : null;
|
|
1577
1820
|
}
|
|
1578
1821
|
function asStringArray(value) {
|
|
1579
1822
|
if (!Array.isArray(value)) return [];
|
|
@@ -1583,8 +1826,8 @@ function isRecord(value) {
|
|
|
1583
1826
|
return typeof value === "object" && value !== null;
|
|
1584
1827
|
}
|
|
1585
1828
|
function addAgentRosterFindings(cwd, findings, rosterRelativePath = DEFAULT_AGENT_ROSTER_TARGET) {
|
|
1586
|
-
const rosterPath =
|
|
1587
|
-
if (!
|
|
1829
|
+
const rosterPath = join11(cwd, rosterRelativePath);
|
|
1830
|
+
if (!existsSync11(rosterPath)) {
|
|
1588
1831
|
findings.push({
|
|
1589
1832
|
level: "fail",
|
|
1590
1833
|
area: "agents",
|
|
@@ -1595,7 +1838,7 @@ function addAgentRosterFindings(cwd, findings, rosterRelativePath = DEFAULT_AGEN
|
|
|
1595
1838
|
}
|
|
1596
1839
|
let roster;
|
|
1597
1840
|
try {
|
|
1598
|
-
const parsed = JSON.parse(
|
|
1841
|
+
const parsed = JSON.parse(readFileSync8(rosterPath, "utf8"));
|
|
1599
1842
|
if (!isRecord(parsed)) throw new Error("Roster must be a JSON object.");
|
|
1600
1843
|
const contractResult = AgentRosterContract.safeParse(parsed);
|
|
1601
1844
|
if (!contractResult.success) {
|
|
@@ -1782,15 +2025,15 @@ function addAgentRosterFindings(cwd, findings, rosterRelativePath = DEFAULT_AGEN
|
|
|
1782
2025
|
}
|
|
1783
2026
|
}
|
|
1784
2027
|
function addCouncilSessionRecordFindings(cwd, findings) {
|
|
1785
|
-
const sessionsRoot =
|
|
1786
|
-
if (!
|
|
2028
|
+
const sessionsRoot = join11(cwd, COUNCIL_SESSION_DIR);
|
|
2029
|
+
if (!existsSync11(sessionsRoot)) return;
|
|
1787
2030
|
const sessionFiles = listFilesRecursive(sessionsRoot).filter((file) => file.endsWith(".json") && !/[\\/]/.test(file));
|
|
1788
2031
|
if (sessionFiles.length === 0) return;
|
|
1789
2032
|
let invalidCount = 0;
|
|
1790
2033
|
for (const sessionFile of sessionFiles) {
|
|
1791
2034
|
const displayPath = `${COUNCIL_SESSION_DIR}/${sessionFile}`;
|
|
1792
2035
|
try {
|
|
1793
|
-
const parsed = JSON.parse(
|
|
2036
|
+
const parsed = JSON.parse(readFileSync8(join11(sessionsRoot, sessionFile), "utf8"));
|
|
1794
2037
|
const contractResult = CouncilSessionContract.safeParse(parsed);
|
|
1795
2038
|
if (!contractResult.success) {
|
|
1796
2039
|
invalidCount += 1;
|
|
@@ -1821,8 +2064,8 @@ function addCouncilSessionRecordFindings(cwd, findings) {
|
|
|
1821
2064
|
}
|
|
1822
2065
|
function addSchemaFindings(cwd, findings, schemaRootRelativePath = ".agent-kit/schemas") {
|
|
1823
2066
|
for (const schemaFile of REQUIRED_SCHEMA_FILES) {
|
|
1824
|
-
const schemaPath =
|
|
1825
|
-
if (!
|
|
2067
|
+
const schemaPath = join11(cwd, schemaRootRelativePath, schemaFile);
|
|
2068
|
+
if (!existsSync11(schemaPath)) {
|
|
1826
2069
|
findings.push({
|
|
1827
2070
|
level: "warn",
|
|
1828
2071
|
area: "agents",
|
|
@@ -1832,7 +2075,7 @@ function addSchemaFindings(cwd, findings, schemaRootRelativePath = ".agent-kit/s
|
|
|
1832
2075
|
continue;
|
|
1833
2076
|
}
|
|
1834
2077
|
try {
|
|
1835
|
-
const parsed = JSON.parse(
|
|
2078
|
+
const parsed = JSON.parse(readFileSync8(schemaPath, "utf8"));
|
|
1836
2079
|
if (!isRecord(parsed) || typeof parsed.$schema !== "string" || !isRecord(parsed.properties)) {
|
|
1837
2080
|
throw new Error("Schema file is missing JSON Schema metadata.");
|
|
1838
2081
|
}
|
|
@@ -1852,8 +2095,8 @@ function addSchemaFindings(cwd, findings, schemaRootRelativePath = ".agent-kit/s
|
|
|
1852
2095
|
}
|
|
1853
2096
|
}
|
|
1854
2097
|
function addAgentStudioFindings(cwd, findings) {
|
|
1855
|
-
const contextPath =
|
|
1856
|
-
if (!
|
|
2098
|
+
const contextPath = join11(cwd, CONTEXT_JSON);
|
|
2099
|
+
if (!existsSync11(contextPath)) {
|
|
1857
2100
|
findings.push({
|
|
1858
2101
|
level: "warn",
|
|
1859
2102
|
area: "studio",
|
|
@@ -1862,7 +2105,7 @@ function addAgentStudioFindings(cwd, findings) {
|
|
|
1862
2105
|
});
|
|
1863
2106
|
} else {
|
|
1864
2107
|
try {
|
|
1865
|
-
const parsed = JSON.parse(
|
|
2108
|
+
const parsed = JSON.parse(readFileSync8(contextPath, "utf8"));
|
|
1866
2109
|
const result = ProjectContextContract.safeParse(parsed);
|
|
1867
2110
|
if (!result.success) {
|
|
1868
2111
|
findings.push({
|
|
@@ -1926,8 +2169,8 @@ function addAgentStudioFindings(cwd, findings) {
|
|
|
1926
2169
|
});
|
|
1927
2170
|
}
|
|
1928
2171
|
}
|
|
1929
|
-
const contextMdPath =
|
|
1930
|
-
if (
|
|
2172
|
+
const contextMdPath = join11(cwd, CONTEXT_MD);
|
|
2173
|
+
if (existsSync11(contextPath) && !existsSync11(contextMdPath)) {
|
|
1931
2174
|
findings.push({
|
|
1932
2175
|
level: "warn",
|
|
1933
2176
|
area: "studio",
|
|
@@ -1936,10 +2179,10 @@ function addAgentStudioFindings(cwd, findings) {
|
|
|
1936
2179
|
});
|
|
1937
2180
|
}
|
|
1938
2181
|
for (const relativePath of [PROJECT_RULES_JSON, AGENT_RULES_JSON]) {
|
|
1939
|
-
const path =
|
|
1940
|
-
if (!
|
|
2182
|
+
const path = join11(cwd, relativePath);
|
|
2183
|
+
if (!existsSync11(path)) continue;
|
|
1941
2184
|
try {
|
|
1942
|
-
const parsed = JSON.parse(
|
|
2185
|
+
const parsed = JSON.parse(readFileSync8(path, "utf8"));
|
|
1943
2186
|
const result = CorrectionRulesContract.safeParse(parsed);
|
|
1944
2187
|
if (!result.success) {
|
|
1945
2188
|
findings.push({
|
|
@@ -1964,9 +2207,9 @@ function addAgentStudioFindings(cwd, findings) {
|
|
|
1964
2207
|
});
|
|
1965
2208
|
}
|
|
1966
2209
|
}
|
|
1967
|
-
const studioExportPath =
|
|
1968
|
-
if (
|
|
1969
|
-
const exportHtml =
|
|
2210
|
+
const studioExportPath = join11(cwd, STUDIO_EXPORT_HTML);
|
|
2211
|
+
if (existsSync11(studioExportPath)) {
|
|
2212
|
+
const exportHtml = readFileSync8(studioExportPath, "utf8");
|
|
1970
2213
|
if (containsLikelySecret(exportHtml)) {
|
|
1971
2214
|
findings.push({
|
|
1972
2215
|
level: "fail",
|
|
@@ -1989,8 +2232,8 @@ function addAgentStudioFindings(cwd, findings) {
|
|
|
1989
2232
|
});
|
|
1990
2233
|
}
|
|
1991
2234
|
}
|
|
1992
|
-
const sessionsRoot =
|
|
1993
|
-
if (!
|
|
2235
|
+
const sessionsRoot = join11(cwd, COUNCIL_SESSION_DIR);
|
|
2236
|
+
if (!existsSync11(sessionsRoot)) return;
|
|
1994
2237
|
const files = listFilesRecursive(sessionsRoot);
|
|
1995
2238
|
const studioSessionFiles = files.filter((file) => /[\\/]session\.json$/.test(file));
|
|
1996
2239
|
for (const sessionFile of studioSessionFiles) {
|
|
@@ -2001,10 +2244,10 @@ function addAgentStudioFindings(cwd, findings) {
|
|
|
2001
2244
|
const eventsRelative = `${COUNCIL_SESSION_DIR}/${normalizedSessionDir}/events.jsonl`;
|
|
2002
2245
|
const indexRelative = `${COUNCIL_SESSION_DIR}/${normalizedSessionDir}/index.md`;
|
|
2003
2246
|
const transcriptRelative = `${COUNCIL_SESSION_DIR}/${normalizedSessionDir}/transcript.md`;
|
|
2004
|
-
const sessionDirPath =
|
|
2247
|
+
const sessionDirPath = join11(sessionsRoot, sessionDir2);
|
|
2005
2248
|
let sessionResult = null;
|
|
2006
2249
|
try {
|
|
2007
|
-
sessionResult = StudioSessionContract.safeParse(JSON.parse(
|
|
2250
|
+
sessionResult = StudioSessionContract.safeParse(JSON.parse(readFileSync8(join11(sessionDirPath, "session.json"), "utf8")));
|
|
2008
2251
|
if (!sessionResult.success) {
|
|
2009
2252
|
findings.push({
|
|
2010
2253
|
level: "fail",
|
|
@@ -2023,8 +2266,8 @@ function addAgentStudioFindings(cwd, findings) {
|
|
|
2023
2266
|
});
|
|
2024
2267
|
continue;
|
|
2025
2268
|
}
|
|
2026
|
-
const eventsPath2 =
|
|
2027
|
-
if (!
|
|
2269
|
+
const eventsPath2 = join11(sessionDirPath, "events.jsonl");
|
|
2270
|
+
if (!existsSync11(eventsPath2)) {
|
|
2028
2271
|
findings.push({
|
|
2029
2272
|
level: "fail",
|
|
2030
2273
|
area: "studio",
|
|
@@ -2033,7 +2276,7 @@ function addAgentStudioFindings(cwd, findings) {
|
|
|
2033
2276
|
});
|
|
2034
2277
|
continue;
|
|
2035
2278
|
}
|
|
2036
|
-
const eventText =
|
|
2279
|
+
const eventText = readFileSync8(eventsPath2, "utf8");
|
|
2037
2280
|
if (containsLikelySecret(eventText)) {
|
|
2038
2281
|
findings.push({
|
|
2039
2282
|
level: "fail",
|
|
@@ -2068,7 +2311,7 @@ function addAgentStudioFindings(cwd, findings) {
|
|
|
2068
2311
|
});
|
|
2069
2312
|
}
|
|
2070
2313
|
}
|
|
2071
|
-
if (!
|
|
2314
|
+
if (!existsSync11(join11(sessionDirPath, "index.md")) || !existsSync11(join11(sessionDirPath, "transcript.md"))) {
|
|
2072
2315
|
findings.push({
|
|
2073
2316
|
level: "warn",
|
|
2074
2317
|
area: "studio",
|
|
@@ -2076,8 +2319,8 @@ function addAgentStudioFindings(cwd, findings) {
|
|
|
2076
2319
|
remediation: "Run agent-kit session render so humans can inspect the current agent transcript and handoffs."
|
|
2077
2320
|
});
|
|
2078
2321
|
} else {
|
|
2079
|
-
const indexText =
|
|
2080
|
-
const transcriptText =
|
|
2322
|
+
const indexText = readFileSync8(join11(sessionDirPath, "index.md"), "utf8");
|
|
2323
|
+
const transcriptText = readFileSync8(join11(sessionDirPath, "transcript.md"), "utf8");
|
|
2081
2324
|
if (containsLikelySecret(indexText) || containsLikelySecret(transcriptText)) {
|
|
2082
2325
|
findings.push({
|
|
2083
2326
|
level: "fail",
|
|
@@ -2086,7 +2329,7 @@ function addAgentStudioFindings(cwd, findings) {
|
|
|
2086
2329
|
remediation: "Regenerate Markdown after redacting sensitive values from the event log."
|
|
2087
2330
|
});
|
|
2088
2331
|
}
|
|
2089
|
-
if (statSync(eventsPath2).mtimeMs > statSync(
|
|
2332
|
+
if (statSync(eventsPath2).mtimeMs > statSync(join11(sessionDirPath, "index.md")).mtimeMs) {
|
|
2090
2333
|
findings.push({
|
|
2091
2334
|
level: "warn",
|
|
2092
2335
|
area: "studio",
|
|
@@ -2136,8 +2379,8 @@ function addCouncilDocFindings(cwd, findings) {
|
|
|
2136
2379
|
}
|
|
2137
2380
|
function addAssistantAdapterFindings(cwd, findings, adapterRootRelativePath = ".agent-kit/assistant-adapters", docsCwd = cwd) {
|
|
2138
2381
|
const adaptersDoc = readDoc(docsCwd, "ASSISTANT_ADAPTERS.md");
|
|
2139
|
-
const adapterRoot =
|
|
2140
|
-
if (!
|
|
2382
|
+
const adapterRoot = join11(cwd, adapterRootRelativePath);
|
|
2383
|
+
if (!existsSync11(adapterRoot)) {
|
|
2141
2384
|
findings.push({
|
|
2142
2385
|
level: "warn",
|
|
2143
2386
|
area: "agents",
|
|
@@ -2157,7 +2400,7 @@ function addAssistantAdapterFindings(cwd, findings, adapterRootRelativePath = ".
|
|
|
2157
2400
|
level: "warn",
|
|
2158
2401
|
area: "agents",
|
|
2159
2402
|
message: "ASSISTANT_ADAPTERS.md does not map the council roster to supported tool instruction surfaces.",
|
|
2160
|
-
remediation: "Document Codex/AGENTS.md, GitHub Copilot, Cursor rules, Claude Code subagents, and the source-of-truth rule for avoiding divergent agent instructions."
|
|
2403
|
+
remediation: "Document Codex/AGENTS.md, GitHub Copilot, Cursor rules and subagents, Claude Code subagents, and the source-of-truth rule for avoiding divergent agent instructions."
|
|
2161
2404
|
});
|
|
2162
2405
|
} else {
|
|
2163
2406
|
findings.push({
|
|
@@ -2166,6 +2409,22 @@ function addAssistantAdapterFindings(cwd, findings, adapterRootRelativePath = ".
|
|
|
2166
2409
|
message: "ASSISTANT_ADAPTERS.md maps the council roster to tool-specific instruction surfaces."
|
|
2167
2410
|
});
|
|
2168
2411
|
}
|
|
2412
|
+
if (assistantAdapterRowIsActive(adaptersDoc, "Cursor") && !existsSync11(join11(cwd, ".cursor/agents/planner.md"))) {
|
|
2413
|
+
findings.push({
|
|
2414
|
+
level: "warn",
|
|
2415
|
+
area: "agents",
|
|
2416
|
+
message: "Cursor is marked Active but .cursor/agents/planner.md is missing.",
|
|
2417
|
+
remediation: "Run agent-kit init --activate cursor to generate council subagents from the roster."
|
|
2418
|
+
});
|
|
2419
|
+
}
|
|
2420
|
+
if (assistantAdapterRowIsActive(adaptersDoc, "Codex / AGENTS.md-compatible tools") && !existsSync11(join11(cwd, ".codex/agents/planner.toml"))) {
|
|
2421
|
+
findings.push({
|
|
2422
|
+
level: "warn",
|
|
2423
|
+
area: "agents",
|
|
2424
|
+
message: "Codex is marked Active but .codex/agents/planner.toml is missing.",
|
|
2425
|
+
remediation: "Run agent-kit init --activate codex to generate council custom agents from the roster."
|
|
2426
|
+
});
|
|
2427
|
+
}
|
|
2169
2428
|
if (!includesAll(adaptersDoc, ["model selection", "enforcement", "MODEL_ROUTING.md", "model-routing.json"])) {
|
|
2170
2429
|
findings.push({
|
|
2171
2430
|
level: "warn",
|
|
@@ -2206,8 +2465,8 @@ function addModelRoutingFindings(cwd, findings, routingRelativePath = DEFAULT_MO
|
|
|
2206
2465
|
message: "MODEL_ROUTING.md documents agent model profiles and IDE enforcement limits."
|
|
2207
2466
|
});
|
|
2208
2467
|
}
|
|
2209
|
-
const routingPath =
|
|
2210
|
-
if (!
|
|
2468
|
+
const routingPath = join11(cwd, routingRelativePath);
|
|
2469
|
+
if (!existsSync11(routingPath)) {
|
|
2211
2470
|
findings.push({
|
|
2212
2471
|
level: "warn",
|
|
2213
2472
|
area: "models",
|
|
@@ -2218,7 +2477,7 @@ function addModelRoutingFindings(cwd, findings, routingRelativePath = DEFAULT_MO
|
|
|
2218
2477
|
}
|
|
2219
2478
|
let routing;
|
|
2220
2479
|
try {
|
|
2221
|
-
routing = JSON.parse(
|
|
2480
|
+
routing = JSON.parse(readFileSync8(routingPath, "utf8"));
|
|
2222
2481
|
} catch {
|
|
2223
2482
|
findings.push({
|
|
2224
2483
|
level: "warn",
|
|
@@ -2276,11 +2535,11 @@ function addTemplateHashFindings(cwd, findings) {
|
|
|
2276
2535
|
if (!manifest) return;
|
|
2277
2536
|
const overrides = readOverrides(cwd);
|
|
2278
2537
|
for (const doc of ROOT_DOCS) {
|
|
2279
|
-
const targetPath =
|
|
2280
|
-
if (!
|
|
2538
|
+
const targetPath = join11(cwd, doc);
|
|
2539
|
+
if (!existsSync11(targetPath)) continue;
|
|
2281
2540
|
const currentTemplate = readTemplate(manifest.stack, doc);
|
|
2282
2541
|
if (!currentTemplate) continue;
|
|
2283
|
-
const targetHash = sha256(
|
|
2542
|
+
const targetHash = sha256(readFileSync8(targetPath, "utf8"));
|
|
2284
2543
|
const currentTemplateHash = sha256(currentTemplate);
|
|
2285
2544
|
const installedTemplateHash = manifest.templateHashes?.[doc];
|
|
2286
2545
|
const override = overrides[doc];
|
|
@@ -2584,7 +2843,7 @@ function auditProject(cwd) {
|
|
|
2584
2843
|
const manifest = readManifest(cwd);
|
|
2585
2844
|
const packageRepository = isPackageRepository(cwd);
|
|
2586
2845
|
const packageSourceMode = packageRepository && !manifest;
|
|
2587
|
-
const docsCwd = packageSourceMode ?
|
|
2846
|
+
const docsCwd = packageSourceMode ? join11(cwd, "templates", "next-supabase") : cwd;
|
|
2588
2847
|
if (!manifest) {
|
|
2589
2848
|
if (packageRepository) {
|
|
2590
2849
|
findings.push({
|
|
@@ -2611,13 +2870,13 @@ function auditProject(cwd) {
|
|
|
2611
2870
|
addAgentRosterFindings(cwd, findings, packageSourceMode ? "rosters/next-supabase-default-council.json" : DEFAULT_AGENT_ROSTER_TARGET);
|
|
2612
2871
|
addSchemaFindings(cwd, findings, packageSourceMode ? "schemas" : ".agent-kit/schemas");
|
|
2613
2872
|
addCouncilSessionRecordFindings(cwd, findings);
|
|
2614
|
-
if (!packageRepository ||
|
|
2873
|
+
if (!packageRepository || existsSync11(join11(cwd, CONTEXT_JSON)) || existsSync11(join11(cwd, COUNCIL_SESSION_DIR))) {
|
|
2615
2874
|
addAgentStudioFindings(cwd, findings);
|
|
2616
2875
|
}
|
|
2617
2876
|
for (const doc of ROOT_DOCS) {
|
|
2618
|
-
const docPath =
|
|
2877
|
+
const docPath = join11(docsCwd, doc);
|
|
2619
2878
|
const displayPath = packageSourceMode ? `templates/next-supabase/${doc}` : doc;
|
|
2620
|
-
if (
|
|
2879
|
+
if (existsSync11(docPath)) {
|
|
2621
2880
|
findings.push({ level: "pass", area: "docs", message: `${displayPath} exists.` });
|
|
2622
2881
|
} else {
|
|
2623
2882
|
findings.push({
|
|
@@ -2682,10 +2941,10 @@ function auditProject(cwd) {
|
|
|
2682
2941
|
return findings;
|
|
2683
2942
|
}
|
|
2684
2943
|
function readPackageJson2(cwd) {
|
|
2685
|
-
const path =
|
|
2686
|
-
if (!
|
|
2944
|
+
const path = join11(cwd, "package.json");
|
|
2945
|
+
if (!existsSync11(path)) return null;
|
|
2687
2946
|
try {
|
|
2688
|
-
return JSON.parse(
|
|
2947
|
+
return JSON.parse(readFileSync8(path, "utf8"));
|
|
2689
2948
|
} catch {
|
|
2690
2949
|
return null;
|
|
2691
2950
|
}
|
|
@@ -2699,8 +2958,8 @@ function containsLikelySecretForAudit(relativeFile, content) {
|
|
|
2699
2958
|
return containsLikelySecret(content);
|
|
2700
2959
|
}
|
|
2701
2960
|
function addProjectRealityFindings(cwd, findings, options = {}) {
|
|
2702
|
-
const migrationsDir =
|
|
2703
|
-
if (
|
|
2961
|
+
const migrationsDir = join11(cwd, "supabase", "migrations");
|
|
2962
|
+
if (existsSync11(migrationsDir)) {
|
|
2704
2963
|
const sqlFiles = listFilesRecursive(migrationsDir).filter((file) => file.endsWith(".sql"));
|
|
2705
2964
|
if (sqlFiles.length === 0) {
|
|
2706
2965
|
findings.push({
|
|
@@ -2711,7 +2970,7 @@ function addProjectRealityFindings(cwd, findings, options = {}) {
|
|
|
2711
2970
|
});
|
|
2712
2971
|
} else {
|
|
2713
2972
|
const rlsFiles = sqlFiles.filter((file) => {
|
|
2714
|
-
const content =
|
|
2973
|
+
const content = readFileSync8(join11(migrationsDir, file), "utf8");
|
|
2715
2974
|
return /enable\s+row\s+level\s+security/i.test(content);
|
|
2716
2975
|
});
|
|
2717
2976
|
if (rlsFiles.length === 0) {
|
|
@@ -2761,7 +3020,7 @@ function addProjectRealityFindings(cwd, findings, options = {}) {
|
|
|
2761
3020
|
return /\.(ts|tsx|js|jsx|env|json)$/.test(file);
|
|
2762
3021
|
});
|
|
2763
3022
|
const secretHits = trackedSourceFiles.map((file) => {
|
|
2764
|
-
const content =
|
|
3023
|
+
const content = readFileSync8(join11(cwd, file), "utf8");
|
|
2765
3024
|
return containsLikelySecretForAudit(file, content) ? file : null;
|
|
2766
3025
|
}).filter((file) => file !== null).slice(0, 5);
|
|
2767
3026
|
if (secretHits.length > 0) {
|
|
@@ -2784,7 +3043,7 @@ function addProjectRealityFindings(cwd, findings, options = {}) {
|
|
|
2784
3043
|
area: "project-reality",
|
|
2785
3044
|
message: "Package source repository mode does not require installed-project context files."
|
|
2786
3045
|
});
|
|
2787
|
-
} else if (!
|
|
3046
|
+
} else if (!existsSync11(join11(cwd, CONTEXT_JSON))) {
|
|
2788
3047
|
findings.push({
|
|
2789
3048
|
level: "warn",
|
|
2790
3049
|
area: "project-reality",
|
|
@@ -2850,7 +3109,7 @@ function report(target, findings) {
|
|
|
2850
3109
|
}
|
|
2851
3110
|
function readJson(path) {
|
|
2852
3111
|
try {
|
|
2853
|
-
return JSON.parse(
|
|
3112
|
+
return JSON.parse(readFileSync9(path, "utf8"));
|
|
2854
3113
|
} catch {
|
|
2855
3114
|
return null;
|
|
2856
3115
|
}
|
|
@@ -2864,24 +3123,24 @@ function isSafeRelativePath(path) {
|
|
|
2864
3123
|
return !normalized.startsWith("/") && !normalized.startsWith("../") && !normalized.includes("/../");
|
|
2865
3124
|
}
|
|
2866
3125
|
function findAntigravityLayout(cwd) {
|
|
2867
|
-
const sourcePlugin =
|
|
2868
|
-
if (
|
|
3126
|
+
const sourcePlugin = join12(cwd, "antigravity", "plugin.json");
|
|
3127
|
+
if (existsSync12(sourcePlugin)) {
|
|
2869
3128
|
return {
|
|
2870
3129
|
mode: "source",
|
|
2871
|
-
pluginRoot:
|
|
2872
|
-
commandsRoot:
|
|
2873
|
-
runtimeSkillsRoot:
|
|
2874
|
-
adapterDocPath:
|
|
3130
|
+
pluginRoot: join12(cwd, "antigravity"),
|
|
3131
|
+
commandsRoot: join12(cwd, ANTIGRAVITY_COMMANDS_SOURCE_DIR),
|
|
3132
|
+
runtimeSkillsRoot: join12(cwd, RUNTIME_SKILLS_SOURCE_DIR),
|
|
3133
|
+
adapterDocPath: join12(cwd, "assistant-adapters", "antigravity.md")
|
|
2875
3134
|
};
|
|
2876
3135
|
}
|
|
2877
|
-
const installedPlugin =
|
|
2878
|
-
if (
|
|
3136
|
+
const installedPlugin = join12(cwd, ".antigravity", "agent-kit", "plugin.json");
|
|
3137
|
+
if (existsSync12(installedPlugin)) {
|
|
2879
3138
|
return {
|
|
2880
3139
|
mode: "installed",
|
|
2881
|
-
pluginRoot:
|
|
2882
|
-
commandsRoot:
|
|
2883
|
-
runtimeSkillsRoot:
|
|
2884
|
-
adapterDocPath:
|
|
3140
|
+
pluginRoot: join12(cwd, ".antigravity", "agent-kit"),
|
|
3141
|
+
commandsRoot: join12(cwd, ANTIGRAVITY_COMMANDS_TARGET_DIR),
|
|
3142
|
+
runtimeSkillsRoot: join12(cwd, ANTIGRAVITY_RUNTIME_SKILLS_TARGET_DIR),
|
|
3143
|
+
adapterDocPath: join12(cwd, ".antigravity", "agent-kit", "README.md")
|
|
2885
3144
|
};
|
|
2886
3145
|
}
|
|
2887
3146
|
return null;
|
|
@@ -2905,8 +3164,8 @@ function validateAntigravityCommands(layout, findings) {
|
|
|
2905
3164
|
const commandNames = /* @__PURE__ */ new Set();
|
|
2906
3165
|
for (const command of REQUIRED_COMMANDS) {
|
|
2907
3166
|
const relativePath = `${command}.toml`;
|
|
2908
|
-
const path =
|
|
2909
|
-
if (!
|
|
3167
|
+
const path = join12(layout.commandsRoot, relativePath);
|
|
3168
|
+
if (!existsSync12(path)) {
|
|
2910
3169
|
findings.push({
|
|
2911
3170
|
level: "fail",
|
|
2912
3171
|
area: "commands",
|
|
@@ -2915,7 +3174,7 @@ function validateAntigravityCommands(layout, findings) {
|
|
|
2915
3174
|
});
|
|
2916
3175
|
continue;
|
|
2917
3176
|
}
|
|
2918
|
-
const text =
|
|
3177
|
+
const text = readFileSync9(path, "utf8");
|
|
2919
3178
|
addSecretFinding(relativePath, text, findings);
|
|
2920
3179
|
const name = commandField(text, "name");
|
|
2921
3180
|
const description = commandField(text, "description");
|
|
@@ -2972,7 +3231,7 @@ function validateAntigravityCommands(layout, findings) {
|
|
|
2972
3231
|
}
|
|
2973
3232
|
}
|
|
2974
3233
|
function validateAntigravityPlugin(layout, findings) {
|
|
2975
|
-
const pluginPath =
|
|
3234
|
+
const pluginPath = join12(layout.pluginRoot, "plugin.json");
|
|
2976
3235
|
const plugin = readJson(pluginPath);
|
|
2977
3236
|
if (!plugin || !isRecord2(plugin)) {
|
|
2978
3237
|
findings.push({
|
|
@@ -3012,8 +3271,8 @@ function validateAntigravityPlugin(layout, findings) {
|
|
|
3012
3271
|
});
|
|
3013
3272
|
continue;
|
|
3014
3273
|
}
|
|
3015
|
-
const resolved =
|
|
3016
|
-
if (!
|
|
3274
|
+
const resolved = join12(layout.pluginRoot, path);
|
|
3275
|
+
if (!existsSync12(resolved)) {
|
|
3017
3276
|
findings.push({
|
|
3018
3277
|
level: "fail",
|
|
3019
3278
|
area: "manifest",
|
|
@@ -3022,7 +3281,7 @@ function validateAntigravityPlugin(layout, findings) {
|
|
|
3022
3281
|
});
|
|
3023
3282
|
}
|
|
3024
3283
|
}
|
|
3025
|
-
const pluginText =
|
|
3284
|
+
const pluginText = readFileSync9(pluginPath, "utf8");
|
|
3026
3285
|
addSecretFinding("plugin.json", pluginText, findings);
|
|
3027
3286
|
if (Array.isArray(plugin.sourceOfTruth) && plugin.sourceOfTruth.includes("AGENTS.md") && plugin.sourceOfTruth.includes(".agent-kit/agent-roster.json")) {
|
|
3028
3287
|
findings.push({
|
|
@@ -3040,13 +3299,13 @@ function validateAntigravityPlugin(layout, findings) {
|
|
|
3040
3299
|
}
|
|
3041
3300
|
}
|
|
3042
3301
|
function validateRuntimeSkills(cwd, layout, findings) {
|
|
3043
|
-
const canonicalSkillsRoot =
|
|
3302
|
+
const canonicalSkillsRoot = existsSync12(join12(cwd, "skills")) ? join12(cwd, "skills") : join12(cwd, ".agent-kit", "skills");
|
|
3044
3303
|
const canonicalSkillNames = listFilesRecursive(canonicalSkillsRoot).filter((file) => file.endsWith(".md")).map((file) => file.replace(/\.md$/, ""));
|
|
3045
3304
|
const runtimeSkillFiles = listFilesRecursive(layout.runtimeSkillsRoot).filter((file) => file.endsWith("/SKILL.md") || file === "SKILL.md");
|
|
3046
3305
|
const runtimeSkillNames = runtimeSkillFiles.map((file) => file.split(/[\\/]/)[0]).filter((value) => typeof value === "string" && value.length > 0).filter((value, index, values) => values.indexOf(value) === index);
|
|
3047
3306
|
for (const skillName of canonicalSkillNames) {
|
|
3048
|
-
const runtimePath =
|
|
3049
|
-
if (!
|
|
3307
|
+
const runtimePath = join12(layout.runtimeSkillsRoot, skillName, "SKILL.md");
|
|
3308
|
+
if (!existsSync12(runtimePath)) {
|
|
3050
3309
|
findings.push({
|
|
3051
3310
|
level: "fail",
|
|
3052
3311
|
area: "runtime-skills",
|
|
@@ -3055,7 +3314,7 @@ function validateRuntimeSkills(cwd, layout, findings) {
|
|
|
3055
3314
|
});
|
|
3056
3315
|
continue;
|
|
3057
3316
|
}
|
|
3058
|
-
const text =
|
|
3317
|
+
const text = readFileSync9(runtimePath, "utf8");
|
|
3059
3318
|
addSecretFinding(`${skillName}/SKILL.md`, text, findings);
|
|
3060
3319
|
if (!/^---\nname: .+\ndescription: .+\n---/m.test(text)) {
|
|
3061
3320
|
findings.push({
|
|
@@ -3083,7 +3342,7 @@ function validateRuntimeSkills(cwd, layout, findings) {
|
|
|
3083
3342
|
remediation: "Add canonical skills or remove orphan runtime wrappers."
|
|
3084
3343
|
});
|
|
3085
3344
|
}
|
|
3086
|
-
if (canonicalSkillNames.length > 0 && canonicalSkillNames.every((skillName) =>
|
|
3345
|
+
if (canonicalSkillNames.length > 0 && canonicalSkillNames.every((skillName) => existsSync12(join12(layout.runtimeSkillsRoot, skillName, "SKILL.md")))) {
|
|
3087
3346
|
findings.push({
|
|
3088
3347
|
level: "pass",
|
|
3089
3348
|
area: "runtime-skills",
|
|
@@ -3104,7 +3363,7 @@ function validateAntigravity(cwd) {
|
|
|
3104
3363
|
}
|
|
3105
3364
|
]);
|
|
3106
3365
|
}
|
|
3107
|
-
const adapterDoc =
|
|
3366
|
+
const adapterDoc = existsSync12(layout.adapterDocPath) ? readFileSync9(layout.adapterDocPath, "utf8") : "";
|
|
3108
3367
|
if (!adapterDoc) {
|
|
3109
3368
|
findings.push({
|
|
3110
3369
|
level: "fail",
|
|
@@ -3133,7 +3392,7 @@ function validateAntigravity(cwd) {
|
|
|
3133
3392
|
validateAntigravityCommands(layout, findings);
|
|
3134
3393
|
validateRuntimeSkills(cwd, layout, findings);
|
|
3135
3394
|
if (layout.mode === "source") {
|
|
3136
|
-
const packageJson = readJson(
|
|
3395
|
+
const packageJson = readJson(join12(cwd, "package.json"));
|
|
3137
3396
|
const files = isRecord2(packageJson) && Array.isArray(packageJson.files) ? packageJson.files : [];
|
|
3138
3397
|
for (const requiredFile of ["antigravity", "runtime-skills", "assistant-adapters"]) {
|
|
3139
3398
|
if (!files.includes(requiredFile)) {
|
|
@@ -3157,40 +3416,145 @@ function validateAntigravity(cwd) {
|
|
|
3157
3416
|
}
|
|
3158
3417
|
function validateBasicAdapter(cwd, target) {
|
|
3159
3418
|
const findings = [];
|
|
3160
|
-
const
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3419
|
+
const isPackageSource = existsSync12(join12(cwd, "package.json")) && existsSync12(join12(cwd, "src")) && existsSync12(join12(cwd, "templates"));
|
|
3420
|
+
if (isPackageSource) {
|
|
3421
|
+
const sourcePaths = {
|
|
3422
|
+
cursor: [
|
|
3423
|
+
"assistant-adapters/cursor-agent-kit.mdc",
|
|
3424
|
+
"assistant-adapters/model-selection/cursor-model-selection.mdc",
|
|
3425
|
+
"assistant-adapters/cursor-planner.mdc"
|
|
3426
|
+
],
|
|
3427
|
+
claude: ["assistant-adapters/claude-code-subagents.md"],
|
|
3428
|
+
codex: ["assistant-adapters/codex-agents.md", "assistant-adapters/model-selection/codex-config.example.toml"],
|
|
3429
|
+
copilot: ["assistant-adapters/github-copilot-instructions.md", "assistant-adapters/github-next-supabase.instructions.md"]
|
|
3430
|
+
};
|
|
3431
|
+
for (const relativePath of sourcePaths[target]) {
|
|
3432
|
+
const path = join12(cwd, relativePath);
|
|
3433
|
+
if (!existsSync12(path)) {
|
|
3434
|
+
findings.push({
|
|
3435
|
+
level: "fail",
|
|
3436
|
+
area: "adapter",
|
|
3437
|
+
message: `${relativePath} is missing.`,
|
|
3438
|
+
remediation: "Restore the adapter template or run agent-kit update."
|
|
3439
|
+
});
|
|
3440
|
+
continue;
|
|
3441
|
+
}
|
|
3442
|
+
const text = readFileSync9(path, "utf8");
|
|
3443
|
+
addSecretFinding(relativePath, text, findings);
|
|
3444
|
+
if (!text.includes("AGENTS.md") && !text.includes("MODEL_ROUTING.md")) {
|
|
3445
|
+
findings.push({
|
|
3446
|
+
level: "warn",
|
|
3447
|
+
area: "adapter",
|
|
3448
|
+
message: `${relativePath} does not clearly reference Agent Kit source-of-truth files.`,
|
|
3449
|
+
remediation: "Adapter templates should point back to AGENTS.md, MODEL_ROUTING.md, and the roster contract."
|
|
3450
|
+
});
|
|
3451
|
+
}
|
|
3452
|
+
}
|
|
3453
|
+
} else {
|
|
3454
|
+
findings.push(...validateInstalledIdeAdapter(cwd, target).findings);
|
|
3455
|
+
}
|
|
3456
|
+
if (findings.every((finding) => finding.level !== "fail")) {
|
|
3457
|
+
findings.push({
|
|
3458
|
+
level: "pass",
|
|
3459
|
+
area: "adapter",
|
|
3460
|
+
message: `${target} adapter ${isPackageSource ? "templates are present" : "activation assets are present"}.`
|
|
3461
|
+
});
|
|
3462
|
+
}
|
|
3463
|
+
return report(target, findings);
|
|
3464
|
+
}
|
|
3465
|
+
function readAssistantAdaptersDoc(cwd) {
|
|
3466
|
+
const path = join12(cwd, "ASSISTANT_ADAPTERS.md");
|
|
3467
|
+
return existsSync12(path) ? readFileSync9(path, "utf8") : "";
|
|
3468
|
+
}
|
|
3469
|
+
function adaptersRowIsActive(doc, toolLabel) {
|
|
3470
|
+
return assistantAdapterRowIsActive(doc, toolLabel);
|
|
3471
|
+
}
|
|
3472
|
+
function validateInstalledIdeAdapter(cwd, target) {
|
|
3473
|
+
const findings = [];
|
|
3474
|
+
const adaptersDoc = readAssistantAdaptersDoc(cwd);
|
|
3475
|
+
if (target === "cursor") {
|
|
3476
|
+
const rulesPath = join12(cwd, ".cursor/rules/cursor-agent-kit.mdc");
|
|
3477
|
+
if (!existsSync12(rulesPath)) {
|
|
3169
3478
|
findings.push({
|
|
3170
3479
|
level: "fail",
|
|
3171
3480
|
area: "adapter",
|
|
3172
|
-
message:
|
|
3173
|
-
remediation: "
|
|
3481
|
+
message: ".cursor/rules/cursor-agent-kit.mdc is missing.",
|
|
3482
|
+
remediation: "Run agent-kit init or agent-kit init --activate cursor."
|
|
3174
3483
|
});
|
|
3175
|
-
|
|
3484
|
+
} else {
|
|
3485
|
+
addSecretFinding(".cursor/rules/cursor-agent-kit.mdc", readFileSync9(rulesPath, "utf8"), findings);
|
|
3176
3486
|
}
|
|
3177
|
-
const
|
|
3178
|
-
|
|
3179
|
-
|
|
3487
|
+
const plannerAgent = join12(cwd, ".cursor/agents/planner.md");
|
|
3488
|
+
if (existsSync12(plannerAgent)) {
|
|
3489
|
+
findings.push({
|
|
3490
|
+
level: "pass",
|
|
3491
|
+
area: "adapter",
|
|
3492
|
+
message: ".cursor/agents/planner.md is installed."
|
|
3493
|
+
});
|
|
3494
|
+
} else if (adaptersRowIsActive(adaptersDoc, "Cursor")) {
|
|
3180
3495
|
findings.push({
|
|
3181
3496
|
level: "warn",
|
|
3182
3497
|
area: "adapter",
|
|
3183
|
-
message:
|
|
3184
|
-
remediation: "
|
|
3498
|
+
message: "Cursor is marked Active but .cursor/agents/planner.md is missing.",
|
|
3499
|
+
remediation: "Run agent-kit init --activate cursor to generate council subagents from the roster."
|
|
3500
|
+
});
|
|
3501
|
+
}
|
|
3502
|
+
const skillSample = join12(cwd, ".cursor/skills/planning-council/SKILL.md");
|
|
3503
|
+
if (existsSync12(skillSample)) {
|
|
3504
|
+
findings.push({
|
|
3505
|
+
level: "pass",
|
|
3506
|
+
area: "adapter",
|
|
3507
|
+
message: ".cursor/skills/*/SKILL.md project skills are installed."
|
|
3185
3508
|
});
|
|
3186
3509
|
}
|
|
3187
3510
|
}
|
|
3188
|
-
if (
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3511
|
+
if (target === "claude") {
|
|
3512
|
+
const plannerAgent = join12(cwd, ".claude/agents/planner.md");
|
|
3513
|
+
if (!existsSync12(plannerAgent)) {
|
|
3514
|
+
findings.push({
|
|
3515
|
+
level: "fail",
|
|
3516
|
+
area: "adapter",
|
|
3517
|
+
message: ".claude/agents/planner.md is missing.",
|
|
3518
|
+
remediation: "Run agent-kit init --activate claude."
|
|
3519
|
+
});
|
|
3520
|
+
}
|
|
3521
|
+
}
|
|
3522
|
+
if (target === "codex") {
|
|
3523
|
+
const configPath = join12(cwd, ".codex/config.toml");
|
|
3524
|
+
if (!existsSync12(configPath)) {
|
|
3525
|
+
findings.push({
|
|
3526
|
+
level: "fail",
|
|
3527
|
+
area: "adapter",
|
|
3528
|
+
message: ".codex/config.toml is missing.",
|
|
3529
|
+
remediation: "Run agent-kit init --activate codex."
|
|
3530
|
+
});
|
|
3531
|
+
}
|
|
3532
|
+
const plannerAgent = join12(cwd, ".codex/agents/planner.toml");
|
|
3533
|
+
if (existsSync12(plannerAgent)) {
|
|
3534
|
+
findings.push({
|
|
3535
|
+
level: "pass",
|
|
3536
|
+
area: "adapter",
|
|
3537
|
+
message: ".codex/agents/planner.toml is installed."
|
|
3538
|
+
});
|
|
3539
|
+
} else if (adaptersRowIsActive(adaptersDoc, "Codex / AGENTS.md-compatible tools")) {
|
|
3540
|
+
findings.push({
|
|
3541
|
+
level: "warn",
|
|
3542
|
+
area: "adapter",
|
|
3543
|
+
message: "Codex is marked Active but .codex/agents/planner.toml is missing.",
|
|
3544
|
+
remediation: "Run agent-kit init --activate codex to generate council custom agents from the roster."
|
|
3545
|
+
});
|
|
3546
|
+
}
|
|
3547
|
+
}
|
|
3548
|
+
if (target === "copilot") {
|
|
3549
|
+
const instructions = join12(cwd, ".github/copilot-instructions.md");
|
|
3550
|
+
if (!existsSync12(instructions)) {
|
|
3551
|
+
findings.push({
|
|
3552
|
+
level: "fail",
|
|
3553
|
+
area: "adapter",
|
|
3554
|
+
message: ".github/copilot-instructions.md is missing.",
|
|
3555
|
+
remediation: "Run agent-kit init --activate copilot."
|
|
3556
|
+
});
|
|
3557
|
+
}
|
|
3194
3558
|
}
|
|
3195
3559
|
return report(target, findings);
|
|
3196
3560
|
}
|
|
@@ -3207,7 +3571,7 @@ function validateAdapter(cwd, target = "antigravity") {
|
|
|
3207
3571
|
}
|
|
3208
3572
|
function validatePackage(cwd) {
|
|
3209
3573
|
const findings = [];
|
|
3210
|
-
const sourceMode =
|
|
3574
|
+
const sourceMode = existsSync12(join12(cwd, "package.json")) && existsSync12(join12(cwd, "src")) && existsSync12(join12(cwd, "templates"));
|
|
3211
3575
|
if (!sourceMode) {
|
|
3212
3576
|
return report("package", [
|
|
3213
3577
|
{
|
|
@@ -3220,8 +3584,8 @@ function validatePackage(cwd) {
|
|
|
3220
3584
|
}
|
|
3221
3585
|
findings.push(...validateAntigravity(cwd).findings);
|
|
3222
3586
|
for (const doc of ["README.md", "DOCS.md", "SPEC.md", "DECISIONS.md", "QUALITY_GATES.md", "TESTING.md", "UPGRADE.md"]) {
|
|
3223
|
-
const path =
|
|
3224
|
-
const text =
|
|
3587
|
+
const path = join12(cwd, doc);
|
|
3588
|
+
const text = existsSync12(path) ? readFileSync9(path, "utf8") : "";
|
|
3225
3589
|
const lower = text.toLowerCase();
|
|
3226
3590
|
if (!lower.includes("antigravity") && !lower.includes("runtime command") && !lower.includes("runtime adapter")) {
|
|
3227
3591
|
findings.push({
|
|
@@ -3238,7 +3602,7 @@ function validatePackage(cwd) {
|
|
|
3238
3602
|
"examples/next-supabase-installed/.agent-kit/manifest.json",
|
|
3239
3603
|
"examples/next-supabase-installed/audit-output.json"
|
|
3240
3604
|
]) {
|
|
3241
|
-
if (!
|
|
3605
|
+
if (!existsSync12(join12(cwd, examplePath))) {
|
|
3242
3606
|
findings.push({
|
|
3243
3607
|
level: "fail",
|
|
3244
3608
|
area: "examples",
|
|
@@ -3273,17 +3637,17 @@ function validatePackage(cwd) {
|
|
|
3273
3637
|
}
|
|
3274
3638
|
|
|
3275
3639
|
// src/install/diff.ts
|
|
3276
|
-
import { existsSync as
|
|
3277
|
-
import { join as
|
|
3640
|
+
import { existsSync as existsSync13, readFileSync as readFileSync10 } from "fs";
|
|
3641
|
+
import { join as join13 } from "path";
|
|
3278
3642
|
function statusForTextFile(target, template) {
|
|
3279
|
-
if (!
|
|
3280
|
-
const targetHash = sha256(
|
|
3281
|
-
const templateHash = sha256(
|
|
3643
|
+
if (!existsSync13(target)) return "missing";
|
|
3644
|
+
const targetHash = sha256(readFileSync10(target, "utf8"));
|
|
3645
|
+
const templateHash = sha256(readFileSync10(template, "utf8"));
|
|
3282
3646
|
return targetHash === templateHash ? "unchanged" : "changed";
|
|
3283
3647
|
}
|
|
3284
3648
|
function diffProject(cwd, stack = "next-supabase") {
|
|
3285
3649
|
const packageRoot = findPackageRoot();
|
|
3286
|
-
const templateRoot =
|
|
3650
|
+
const templateRoot = join13(packageRoot, "templates", stack);
|
|
3287
3651
|
const libraryFolders = {
|
|
3288
3652
|
missing: [],
|
|
3289
3653
|
present: [],
|
|
@@ -3307,8 +3671,8 @@ function diffProject(cwd, stack = "next-supabase") {
|
|
|
3307
3671
|
}
|
|
3308
3672
|
};
|
|
3309
3673
|
for (const doc of ROOT_DOCS) {
|
|
3310
|
-
const target =
|
|
3311
|
-
const template =
|
|
3674
|
+
const target = join13(cwd, doc);
|
|
3675
|
+
const template = join13(templateRoot, doc);
|
|
3312
3676
|
const status = statusForTextFile(target, template);
|
|
3313
3677
|
if (status === "missing") {
|
|
3314
3678
|
result.missing.push(doc);
|
|
@@ -3321,7 +3685,7 @@ function diffProject(cwd, stack = "next-supabase") {
|
|
|
3321
3685
|
result.preview.wouldWriteConflicts.push(doc);
|
|
3322
3686
|
}
|
|
3323
3687
|
}
|
|
3324
|
-
result.agentRoster = statusForTextFile(
|
|
3688
|
+
result.agentRoster = statusForTextFile(join13(cwd, DEFAULT_AGENT_ROSTER_TARGET), join13(packageRoot, DEFAULT_AGENT_ROSTER_SOURCE));
|
|
3325
3689
|
if (result.agentRoster === "missing") {
|
|
3326
3690
|
result.preview.wouldCreate.push(DEFAULT_AGENT_ROSTER_TARGET);
|
|
3327
3691
|
result.preview.wouldCreateAgentRoster = true;
|
|
@@ -3330,7 +3694,7 @@ function diffProject(cwd, stack = "next-supabase") {
|
|
|
3330
3694
|
result.preview.wouldWriteConflicts.push(DEFAULT_AGENT_ROSTER_TARGET);
|
|
3331
3695
|
result.preview.wouldWriteAgentRosterConflict = true;
|
|
3332
3696
|
}
|
|
3333
|
-
result.modelRouting = statusForTextFile(
|
|
3697
|
+
result.modelRouting = statusForTextFile(join13(cwd, DEFAULT_MODEL_ROUTING_TARGET), join13(packageRoot, DEFAULT_MODEL_ROUTING_SOURCE));
|
|
3334
3698
|
if (result.modelRouting === "missing") {
|
|
3335
3699
|
result.preview.wouldCreate.push(DEFAULT_MODEL_ROUTING_TARGET);
|
|
3336
3700
|
result.preview.wouldCreateModelRouting = true;
|
|
@@ -3340,8 +3704,8 @@ function diffProject(cwd, stack = "next-supabase") {
|
|
|
3340
3704
|
result.preview.wouldWriteModelRoutingConflict = true;
|
|
3341
3705
|
}
|
|
3342
3706
|
for (const folder of LIBRARY_FOLDERS) {
|
|
3343
|
-
const target =
|
|
3344
|
-
if (
|
|
3707
|
+
const target = join13(cwd, ".agent-kit", folder);
|
|
3708
|
+
if (existsSync13(target)) libraryFolders.present.push(folder);
|
|
3345
3709
|
else libraryFolders.missing.push(folder);
|
|
3346
3710
|
}
|
|
3347
3711
|
return result;
|
|
@@ -3349,8 +3713,8 @@ function diffProject(cwd, stack = "next-supabase") {
|
|
|
3349
3713
|
|
|
3350
3714
|
// src/research/discover.ts
|
|
3351
3715
|
import { Octokit } from "@octokit/rest";
|
|
3352
|
-
import { readFileSync as
|
|
3353
|
-
import { join as
|
|
3716
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
3717
|
+
import { join as join14 } from "path";
|
|
3354
3718
|
|
|
3355
3719
|
// src/research/config.ts
|
|
3356
3720
|
import { z as z2 } from "zod";
|
|
@@ -3375,8 +3739,8 @@ var researchConfigSchema = z2.object({
|
|
|
3375
3739
|
// src/research/discover.ts
|
|
3376
3740
|
async function discoverRepos(options) {
|
|
3377
3741
|
const packageRoot = findPackageRoot();
|
|
3378
|
-
const configPath =
|
|
3379
|
-
const config = researchConfigSchema.parse(JSON.parse(
|
|
3742
|
+
const configPath = join14(packageRoot, "research", "scan-config.json");
|
|
3743
|
+
const config = researchConfigSchema.parse(JSON.parse(readFileSync11(configPath, "utf8")));
|
|
3380
3744
|
const token = options.token ?? process.env.GITHUB_TOKEN;
|
|
3381
3745
|
if (!token) {
|
|
3382
3746
|
throw new Error("GITHUB_TOKEN is required for GitHub API research discovery.");
|
|
@@ -3437,20 +3801,20 @@ async function discoverRepos(options) {
|
|
|
3437
3801
|
}
|
|
3438
3802
|
}
|
|
3439
3803
|
const candidates = [...deduped.values()].slice(0, maxRepos);
|
|
3440
|
-
const output = options.output ??
|
|
3804
|
+
const output = options.output ?? join14(options.cwd, "research", "repo-candidates.json");
|
|
3441
3805
|
writeText(output, `${JSON.stringify(candidates, null, 2)}
|
|
3442
3806
|
`);
|
|
3443
3807
|
return candidates;
|
|
3444
3808
|
}
|
|
3445
3809
|
|
|
3446
3810
|
// src/research/scan.ts
|
|
3447
|
-
import { existsSync as
|
|
3448
|
-
import { join as
|
|
3811
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync2, readFileSync as readFileSync13, rmSync } from "fs";
|
|
3812
|
+
import { join as join16 } from "path";
|
|
3449
3813
|
import { simpleGit } from "simple-git";
|
|
3450
3814
|
|
|
3451
3815
|
// src/research/analyze.ts
|
|
3452
|
-
import { existsSync as
|
|
3453
|
-
import { join as
|
|
3816
|
+
import { existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
|
|
3817
|
+
import { join as join15 } from "path";
|
|
3454
3818
|
function normalizeRelativePath(file) {
|
|
3455
3819
|
return file.replace(/\\/g, "/");
|
|
3456
3820
|
}
|
|
@@ -3458,8 +3822,8 @@ function hasFile(files, matcher) {
|
|
|
3458
3822
|
return files.some((file) => matcher.test(normalizeRelativePath(file)));
|
|
3459
3823
|
}
|
|
3460
3824
|
function fileText(root, file) {
|
|
3461
|
-
const path =
|
|
3462
|
-
return
|
|
3825
|
+
const path = join15(root, file);
|
|
3826
|
+
return existsSync15(path) ? readFileSync12(path, "utf8") : "";
|
|
3463
3827
|
}
|
|
3464
3828
|
function textIncludes(root, files, matcher, terms) {
|
|
3465
3829
|
const lowerTerms = terms.map((term) => term.toLowerCase());
|
|
@@ -3590,24 +3954,24 @@ ${finding.impactOnKit.map((item) => `- ${item}`).join("\n")}
|
|
|
3590
3954
|
`;
|
|
3591
3955
|
}
|
|
3592
3956
|
async function scanRepos(options) {
|
|
3593
|
-
const candidatesPath = options.candidatesPath ??
|
|
3594
|
-
if (!
|
|
3957
|
+
const candidatesPath = options.candidatesPath ?? join16(options.cwd, "research", "repo-candidates.json");
|
|
3958
|
+
if (!existsSync16(candidatesPath)) {
|
|
3595
3959
|
throw new Error(`Candidates file not found: ${candidatesPath}`);
|
|
3596
3960
|
}
|
|
3597
|
-
const candidates = JSON.parse(
|
|
3598
|
-
const workdir = options.workdir ??
|
|
3961
|
+
const candidates = JSON.parse(readFileSync13(candidatesPath, "utf8"));
|
|
3962
|
+
const workdir = options.workdir ?? join16(options.cwd, "research", "workdir");
|
|
3599
3963
|
mkdirSync2(workdir, { recursive: true });
|
|
3600
|
-
mkdirSync2(
|
|
3964
|
+
mkdirSync2(join16(options.cwd, "research", "findings"), { recursive: true });
|
|
3601
3965
|
const findings = [];
|
|
3602
3966
|
const git = simpleGit();
|
|
3603
3967
|
for (const candidate of candidates) {
|
|
3604
3968
|
const repoSlug = candidate.fullName.replace("/", "__");
|
|
3605
|
-
const repoPath =
|
|
3606
|
-
if (
|
|
3969
|
+
const repoPath = join16(workdir, repoSlug);
|
|
3970
|
+
if (existsSync16(repoPath)) rmSync(repoPath, { recursive: true, force: true });
|
|
3607
3971
|
await git.raw(["clone", "--depth", "1", candidate.htmlUrl, repoPath]);
|
|
3608
3972
|
const finding = analyzeRepository(candidate, repoPath);
|
|
3609
3973
|
findings.push(finding);
|
|
3610
|
-
writeText(
|
|
3974
|
+
writeText(join16(options.cwd, "research", "findings", `${repoSlug}.md`), findingToMarkdown(finding));
|
|
3611
3975
|
if (!options.keepClones) {
|
|
3612
3976
|
rmSync(repoPath, { recursive: true, force: true });
|
|
3613
3977
|
}
|
|
@@ -3616,8 +3980,8 @@ async function scanRepos(options) {
|
|
|
3616
3980
|
}
|
|
3617
3981
|
|
|
3618
3982
|
// src/research/summarize.ts
|
|
3619
|
-
import { existsSync as
|
|
3620
|
-
import { join as
|
|
3983
|
+
import { existsSync as existsSync17, readFileSync as readFileSync14, readdirSync as readdirSync3 } from "fs";
|
|
3984
|
+
import { join as join17 } from "path";
|
|
3621
3985
|
var SUMMARY_TARGETS = {
|
|
3622
3986
|
"nextjs-patterns": {
|
|
3623
3987
|
title: "Next.js Patterns",
|
|
@@ -3720,12 +4084,12 @@ function renderRepoList(findings, scoreKeys) {
|
|
|
3720
4084
|
return findings.slice().sort((a, b) => scoreFor(b, scoreKeys) - scoreFor(a, scoreKeys) || b.totalScore - a.totalScore || b.stars - a.stars).slice(0, 12).map((finding) => `- ${finding.fullName} (${finding.category}) - focus score ${scoreFor(finding, scoreKeys)}, total ${finding.totalScore}/${maxTotalScore}`).join("\n");
|
|
3721
4085
|
}
|
|
3722
4086
|
function summarizeFindings(cwd) {
|
|
3723
|
-
const findingsDir =
|
|
3724
|
-
if (!
|
|
4087
|
+
const findingsDir = join17(cwd, "research", "findings");
|
|
4088
|
+
if (!existsSync17(findingsDir)) {
|
|
3725
4089
|
throw new Error("No research/findings directory exists. Run agent-kit research scan first.");
|
|
3726
4090
|
}
|
|
3727
|
-
const findingFiles =
|
|
3728
|
-
const findings = findingFiles.map((file) => parseFinding(file,
|
|
4091
|
+
const findingFiles = readdirSync3(findingsDir).filter((file) => file.endsWith(".md"));
|
|
4092
|
+
const findings = findingFiles.map((file) => parseFinding(file, readFileSync14(join17(findingsDir, file), "utf8"))).filter((finding) => finding !== null);
|
|
3729
4093
|
const categoryCounts = countBy(findings.map((finding) => finding.category));
|
|
3730
4094
|
const outputs = [];
|
|
3731
4095
|
const overview = `# Research Scan Overview
|
|
@@ -3744,13 +4108,13 @@ ${countBy(findings.flatMap((finding) => finding.strongPractices)).slice(0, 12).m
|
|
|
3744
4108
|
## Most Repeated Gaps
|
|
3745
4109
|
${countBy(findings.flatMap((finding) => finding.weakPractices)).slice(0, 12).map(([practice, count]) => `- ${practice} (${count})`).join("\n")}
|
|
3746
4110
|
`;
|
|
3747
|
-
const overviewPath =
|
|
4111
|
+
const overviewPath = join17(cwd, "research", "summaries", "scan-overview.md");
|
|
3748
4112
|
writeText(overviewPath, overview);
|
|
3749
4113
|
outputs.push(overviewPath);
|
|
3750
4114
|
for (const [target, config] of Object.entries(SUMMARY_TARGETS)) {
|
|
3751
4115
|
const categories = config.categories;
|
|
3752
4116
|
const scopedFindings = findings.filter((finding) => categories.includes(finding.category));
|
|
3753
|
-
const path =
|
|
4117
|
+
const path = join17(cwd, "research", "summaries", `${target}.md`);
|
|
3754
4118
|
const summary2 = `# ${config.title}
|
|
3755
4119
|
|
|
3756
4120
|
Generated from ${scopedFindings.length} relevant repository findings.
|
|
@@ -3791,7 +4155,7 @@ Review the generated research summaries, then convert repeated best practices in
|
|
|
3791
4155
|
|
|
3792
4156
|
Do not copy source code from scanned repositories. Adopt only generalized practices with clear rationale.
|
|
3793
4157
|
`;
|
|
3794
|
-
const path =
|
|
4158
|
+
const path = join17(cwd, "research", "proposed-updates.md");
|
|
3795
4159
|
writeText(path, output);
|
|
3796
4160
|
return path;
|
|
3797
4161
|
}
|
|
@@ -3900,8 +4264,8 @@ function proposeCorrectionUpstream(cwd, id) {
|
|
|
3900
4264
|
}
|
|
3901
4265
|
|
|
3902
4266
|
// src/studio/session.ts
|
|
3903
|
-
import { existsSync as
|
|
3904
|
-
import { join as
|
|
4267
|
+
import { existsSync as existsSync18, readFileSync as readFileSync15, readdirSync as readdirSync4, statSync as statSync2 } from "fs";
|
|
4268
|
+
import { join as join18 } from "path";
|
|
3905
4269
|
function sessionDir(sessionId) {
|
|
3906
4270
|
return `${COUNCIL_SESSIONS_DIR}/${safeSlug(sessionId)}`;
|
|
3907
4271
|
}
|
|
@@ -3958,9 +4322,9 @@ function startSession(cwd, options) {
|
|
|
3958
4322
|
return { sessionId, sessionPath: sessionDir(sessionId) };
|
|
3959
4323
|
}
|
|
3960
4324
|
function listSessions(cwd) {
|
|
3961
|
-
const root =
|
|
3962
|
-
if (!
|
|
3963
|
-
return
|
|
4325
|
+
const root = join18(cwd, COUNCIL_SESSIONS_DIR);
|
|
4326
|
+
if (!existsSync18(root)) return [];
|
|
4327
|
+
return readdirSync4(root).filter((entry) => entry !== "active").map((entry) => join18(root, entry, "session.json")).filter((path) => existsSync18(path) && statSync2(path).isFile()).map((path) => StudioSessionContract.parse(JSON.parse(readFileSync15(path, "utf8")))).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
3964
4328
|
}
|
|
3965
4329
|
function getActiveSessionId(cwd) {
|
|
3966
4330
|
const active = readTextFile(cwd, ACTIVE_SESSION_FILE)?.trim();
|
|
@@ -4658,13 +5022,13 @@ function parseSetupFormPayload(raw) {
|
|
|
4658
5022
|
}
|
|
4659
5023
|
|
|
4660
5024
|
// src/studio/wizard/checklist.ts
|
|
4661
|
-
import { existsSync as
|
|
4662
|
-
import { join as
|
|
5025
|
+
import { existsSync as existsSync19, readFileSync as readFileSync16 } from "fs";
|
|
5026
|
+
import { join as join19 } from "path";
|
|
4663
5027
|
var IDE_PATHS = {
|
|
4664
|
-
cursor: ".cursor/
|
|
5028
|
+
cursor: ".cursor/agents/planner.md",
|
|
4665
5029
|
copilot: ".github/copilot-instructions.md",
|
|
4666
|
-
claude: ".claude/agents/",
|
|
4667
|
-
codex: "
|
|
5030
|
+
claude: ".claude/agents/planner.md",
|
|
5031
|
+
codex: ".codex/agents/planner.toml",
|
|
4668
5032
|
other: "ASSISTANT_ADAPTERS.md"
|
|
4669
5033
|
};
|
|
4670
5034
|
function saveIdeChecklist(cwd, ideSurface) {
|
|
@@ -4679,10 +5043,13 @@ function saveIdeChecklist(cwd, ideSurface) {
|
|
|
4679
5043
|
}
|
|
4680
5044
|
function detectIdeRulePresent(cwd, ideSurface) {
|
|
4681
5045
|
const rel = IDE_PATHS[ideSurface];
|
|
5046
|
+
if (ideSurface === "cursor") {
|
|
5047
|
+
return existsSync19(join19(cwd, rel)) || existsSync19(join19(cwd, ".cursor/rules/cursor-agent-kit.mdc"));
|
|
5048
|
+
}
|
|
4682
5049
|
if (rel.endsWith("/")) {
|
|
4683
|
-
return
|
|
5050
|
+
return existsSync19(join19(cwd, rel));
|
|
4684
5051
|
}
|
|
4685
|
-
return
|
|
5052
|
+
return existsSync19(join19(cwd, rel));
|
|
4686
5053
|
}
|
|
4687
5054
|
var VISUAL_QA_MARKER = "## Visual QA Tier";
|
|
4688
5055
|
var VISUAL_QA_BLOCKS = {
|
|
@@ -4710,11 +5077,11 @@ This project uses the **Mature** visual QA tier.
|
|
|
4710
5077
|
};
|
|
4711
5078
|
function writeVisualQaTier(cwd, tier) {
|
|
4712
5079
|
const path = "TESTING.md";
|
|
4713
|
-
const fullPath =
|
|
4714
|
-
if (!
|
|
5080
|
+
const fullPath = join19(cwd, path);
|
|
5081
|
+
if (!existsSync19(fullPath)) {
|
|
4715
5082
|
return { updated: false, path, reason: "TESTING.md not found in project root." };
|
|
4716
5083
|
}
|
|
4717
|
-
const current =
|
|
5084
|
+
const current = readFileSync16(fullPath, "utf8");
|
|
4718
5085
|
if (current.includes(VISUAL_QA_MARKER)) {
|
|
4719
5086
|
return {
|
|
4720
5087
|
updated: false,
|
|
@@ -4890,8 +5257,8 @@ function extractSetupFormFromWizardForm(form) {
|
|
|
4890
5257
|
}
|
|
4891
5258
|
|
|
4892
5259
|
// src/studio/wizard/drafts.ts
|
|
4893
|
-
import { existsSync as
|
|
4894
|
-
import { join as
|
|
5260
|
+
import { existsSync as existsSync20, readFileSync as readFileSync17 } from "fs";
|
|
5261
|
+
import { join as join20 } from "path";
|
|
4895
5262
|
var DESIGN_DRAFT_JSON = ".agent-kit/onboarding/design-draft.json";
|
|
4896
5263
|
var MESSAGING_DRAFT_JSON = ".agent-kit/onboarding/messaging-draft.json";
|
|
4897
5264
|
function loadDesignDraft(cwd) {
|
|
@@ -4936,11 +5303,11 @@ function previewMessagingMarkdown(draft) {
|
|
|
4936
5303
|
`;
|
|
4937
5304
|
}
|
|
4938
5305
|
function appendSectionToDoc(cwd, doc, sectionMarkdown) {
|
|
4939
|
-
const fullPath =
|
|
4940
|
-
if (!
|
|
5306
|
+
const fullPath = join20(cwd, doc);
|
|
5307
|
+
if (!existsSync20(fullPath)) {
|
|
4941
5308
|
return { target: doc, action: "missing" };
|
|
4942
5309
|
}
|
|
4943
|
-
const current =
|
|
5310
|
+
const current = readFileSync17(fullPath, "utf8");
|
|
4944
5311
|
if (current.includes("(wizard draft)")) {
|
|
4945
5312
|
return { target: doc, action: "conflict", conflictPath: `.agent-kit/conflicts/wizard-${doc}` };
|
|
4946
5313
|
}
|
|
@@ -4979,8 +5346,8 @@ function applyDrafts(cwd) {
|
|
|
4979
5346
|
}
|
|
4980
5347
|
|
|
4981
5348
|
// src/studio/office/render.ts
|
|
4982
|
-
import { readFileSync as
|
|
4983
|
-
import { join as
|
|
5349
|
+
import { readFileSync as readFileSync18 } from "fs";
|
|
5350
|
+
import { join as join21 } from "path";
|
|
4984
5351
|
|
|
4985
5352
|
// src/studio/office/map.ts
|
|
4986
5353
|
var MAP_WIDTH = 28;
|
|
@@ -5114,12 +5481,12 @@ var PRODUCT_CATEGORIES = [
|
|
|
5114
5481
|
var TENANT_MODELS = ["single-user", "team", "tenant", "marketplace", "admin", "public-content"];
|
|
5115
5482
|
function readOfficeAsset(name) {
|
|
5116
5483
|
const root = findPackageRoot();
|
|
5117
|
-
const distPath =
|
|
5118
|
-
const srcPath =
|
|
5484
|
+
const distPath = join21(root, "dist", "studio", "office", "assets", name);
|
|
5485
|
+
const srcPath = join21(root, "src", "studio", "office", "assets", name);
|
|
5119
5486
|
try {
|
|
5120
|
-
return
|
|
5487
|
+
return readFileSync18(distPath, "utf8");
|
|
5121
5488
|
} catch {
|
|
5122
|
-
return
|
|
5489
|
+
return readFileSync18(srcPath, "utf8");
|
|
5123
5490
|
}
|
|
5124
5491
|
}
|
|
5125
5492
|
function buildOfficeBootConfig(cwd, viewModel) {
|
|
@@ -5182,14 +5549,21 @@ function renderOfficeHtml(boot, mode) {
|
|
|
5182
5549
|
</div>
|
|
5183
5550
|
<div class="header-actions">
|
|
5184
5551
|
<span class="progress-pill" id="progress-pill">${isStudio ? "Live" : "0% ready"}</span>
|
|
5552
|
+
${isStudio ? "" : '<span class="level-pill" id="level-pill" aria-live="polite">L3 \u2192 L5</span>'}
|
|
5185
5553
|
${isStudio ? '<span class="session-pill" id="session-pill">No session</span>' : '<a class="btn secondary" href="/wizard">Form view</a>'}
|
|
5186
5554
|
${isStudio ? "" : '<button type="button" class="btn primary" id="review-btn">Review & save</button>'}
|
|
5187
5555
|
</div>
|
|
5188
5556
|
</header>
|
|
5557
|
+
${isStudio ? "" : '<div class="iceberg-strip" id="iceberg-strip" aria-label="Agentic engineering levels L3 through L8"></div>'}
|
|
5189
5558
|
<main class="office-main${isStudio ? " studio-layout" : ""}">
|
|
5190
5559
|
<aside class="station-list${isStudio ? " hidden" : ""}" aria-label="Setup stations">
|
|
5191
5560
|
<h2>Stations</h2>
|
|
5192
5561
|
<p class="hint">Keyboard-friendly list \u2014 same actions as the office floor.</p>
|
|
5562
|
+
<div class="climb-panel" id="climb-panel" hidden>
|
|
5563
|
+
<h3>Climb checklist</h3>
|
|
5564
|
+
<ol id="climb-list"></ol>
|
|
5565
|
+
<button type="button" class="btn secondary climb-refresh" id="climb-refresh">Refresh level</button>
|
|
5566
|
+
</div>
|
|
5193
5567
|
<ul id="station-list"></ul>
|
|
5194
5568
|
</aside>
|
|
5195
5569
|
<div class="canvas-wrap">
|
|
@@ -5286,8 +5660,8 @@ function allAgentBriefsComplete(form, agentIds) {
|
|
|
5286
5660
|
}
|
|
5287
5661
|
|
|
5288
5662
|
// src/studio/wizard/render.ts
|
|
5289
|
-
import { readFileSync as
|
|
5290
|
-
import { join as
|
|
5663
|
+
import { readFileSync as readFileSync19 } from "fs";
|
|
5664
|
+
import { join as join22 } from "path";
|
|
5291
5665
|
var PRODUCT_CATEGORIES2 = [
|
|
5292
5666
|
"content-app",
|
|
5293
5667
|
"saas",
|
|
@@ -5304,12 +5678,12 @@ var PRODUCT_CATEGORIES2 = [
|
|
|
5304
5678
|
var TENANT_MODELS2 = ["single-user", "team", "tenant", "marketplace", "admin", "public-content"];
|
|
5305
5679
|
function readWizardAsset(name) {
|
|
5306
5680
|
const root = findPackageRoot();
|
|
5307
|
-
const distPath =
|
|
5308
|
-
const srcPath =
|
|
5681
|
+
const distPath = join22(root, "dist", "studio", "wizard", "assets", name);
|
|
5682
|
+
const srcPath = join22(root, "src", "studio", "wizard", "assets", name);
|
|
5309
5683
|
try {
|
|
5310
|
-
return
|
|
5684
|
+
return readFileSync19(distPath, "utf8");
|
|
5311
5685
|
} catch {
|
|
5312
|
-
return
|
|
5686
|
+
return readFileSync19(srcPath, "utf8");
|
|
5313
5687
|
}
|
|
5314
5688
|
}
|
|
5315
5689
|
function mergeWizardSteps(cwd) {
|
|
@@ -5363,6 +5737,7 @@ function renderSetupWizardHtml(boot) {
|
|
|
5363
5737
|
<div>
|
|
5364
5738
|
<div style="font-weight:600;color:#f8fafc">Setup progress</div>
|
|
5365
5739
|
<div style="font-size:13px;color:#94a3b8">Save anytime \u2014 resume with agent-kit setup</div>
|
|
5740
|
+
<div class="wizard-level-pill" id="wizard-level-pill" hidden aria-live="polite">L3 \u2192 L5</div>
|
|
5366
5741
|
</div>
|
|
5367
5742
|
</div>
|
|
5368
5743
|
<ul class="section-nav" id="section-nav"></ul>
|
|
@@ -5397,6 +5772,291 @@ function renderSetupWizardHtmlWithContext(cwd) {
|
|
|
5397
5772
|
return renderSetupWizardHtml({ ...boot, stackSignals: [...new Set(stackSignals)] });
|
|
5398
5773
|
}
|
|
5399
5774
|
|
|
5775
|
+
// src/studio/agentic-level.ts
|
|
5776
|
+
import { existsSync as existsSync21, readFileSync as readFileSync20 } from "fs";
|
|
5777
|
+
import { join as join23 } from "path";
|
|
5778
|
+
var CACHE_TTL_MS = 3e4;
|
|
5779
|
+
var cache = /* @__PURE__ */ new Map();
|
|
5780
|
+
function isMaintainerSourceRepo(cwd) {
|
|
5781
|
+
return existsSync21(join23(cwd, "package.json")) && existsSync21(join23(cwd, "src")) && existsSync21(join23(cwd, "templates"));
|
|
5782
|
+
}
|
|
5783
|
+
function signal(id, level, label, pass, evidence, remediation) {
|
|
5784
|
+
return { id, level, label, pass, evidence, remediation };
|
|
5785
|
+
}
|
|
5786
|
+
function detectIdePresent(cwd) {
|
|
5787
|
+
const onboarding = loadOnboardingState(cwd);
|
|
5788
|
+
if (onboarding.ideSurface && detectIdeRulePresent(cwd, onboarding.ideSurface)) {
|
|
5789
|
+
return { pass: true, evidence: `${onboarding.ideSurface} adapter configured` };
|
|
5790
|
+
}
|
|
5791
|
+
const surfaces = ["cursor", "copilot", "claude", "codex"];
|
|
5792
|
+
for (const surface of surfaces) {
|
|
5793
|
+
if (detectIdeRulePresent(cwd, surface)) {
|
|
5794
|
+
return { pass: true, evidence: `${surface} adapter files detected` };
|
|
5795
|
+
}
|
|
5796
|
+
}
|
|
5797
|
+
if (existsSync21(join23(cwd, ".cursor/rules/cursor-agent-kit.mdc"))) {
|
|
5798
|
+
return { pass: true, evidence: "Cursor council rules from init" };
|
|
5799
|
+
}
|
|
5800
|
+
return { pass: false, evidence: "No IDE adapter rules or subagents detected" };
|
|
5801
|
+
}
|
|
5802
|
+
function detectTierBSubagents(cwd) {
|
|
5803
|
+
const paths = [
|
|
5804
|
+
".cursor/agents/planner.md",
|
|
5805
|
+
".codex/agents/planner.toml",
|
|
5806
|
+
".claude/agents/planner.md",
|
|
5807
|
+
".github/copilot-instructions.md"
|
|
5808
|
+
];
|
|
5809
|
+
const found = paths.filter((rel) => existsSync21(join23(cwd, rel)));
|
|
5810
|
+
if (found.length > 0) {
|
|
5811
|
+
return { pass: true, evidence: `Specialist surface: ${found[0]}` };
|
|
5812
|
+
}
|
|
5813
|
+
return { pass: false, evidence: "No council subagents or Copilot instructions installed" };
|
|
5814
|
+
}
|
|
5815
|
+
function readDocSnippet(cwd, name, needles) {
|
|
5816
|
+
const path = join23(cwd, name);
|
|
5817
|
+
if (!existsSync21(path)) return false;
|
|
5818
|
+
const lower = readFileSync20(path, "utf8").toLowerCase();
|
|
5819
|
+
return needles.every((needle) => lower.includes(needle.toLowerCase()));
|
|
5820
|
+
}
|
|
5821
|
+
function adapterTargetForIde(ide) {
|
|
5822
|
+
if (ide === "cursor" || ide === "codex" || ide === "claude" || ide === "copilot") return ide;
|
|
5823
|
+
return null;
|
|
5824
|
+
}
|
|
5825
|
+
function buildSignals(cwd, maintainerProfile) {
|
|
5826
|
+
const signals = [];
|
|
5827
|
+
const ide = detectIdePresent(cwd);
|
|
5828
|
+
signals.push(
|
|
5829
|
+
signal(
|
|
5830
|
+
"l3-ide",
|
|
5831
|
+
3,
|
|
5832
|
+
"AI-native IDE or adapter rules",
|
|
5833
|
+
ide.pass,
|
|
5834
|
+
ide.evidence,
|
|
5835
|
+
"Run agent-kit init and complete the IDE station, or agent-kit init --activate cursor|codex"
|
|
5836
|
+
)
|
|
5837
|
+
);
|
|
5838
|
+
const context2 = scanProjectContext(cwd);
|
|
5839
|
+
const openQuestions = context2.openQuestions.length;
|
|
5840
|
+
const contextReady = Boolean(context2.productSummary.trim()) && Boolean(context2.primaryAudience.trim()) && Boolean(context2.authModel.trim()) && context2.primaryWorkflows.length > 0 && openQuestions === 0;
|
|
5841
|
+
signals.push(
|
|
5842
|
+
signal(
|
|
5843
|
+
"l4-agents-md",
|
|
5844
|
+
4,
|
|
5845
|
+
"Council contract (AGENTS.md)",
|
|
5846
|
+
existsSync21(join23(cwd, "AGENTS.md")),
|
|
5847
|
+
existsSync21(join23(cwd, "AGENTS.md")) ? "AGENTS.md installed" : "AGENTS.md missing",
|
|
5848
|
+
"Run agent-kit init --stack next-supabase"
|
|
5849
|
+
)
|
|
5850
|
+
);
|
|
5851
|
+
signals.push(
|
|
5852
|
+
signal(
|
|
5853
|
+
"l4-adapters-doc",
|
|
5854
|
+
4,
|
|
5855
|
+
"Assistant activation doc",
|
|
5856
|
+
existsSync21(join23(cwd, "ASSISTANT_ADAPTERS.md")),
|
|
5857
|
+
existsSync21(join23(cwd, "ASSISTANT_ADAPTERS.md")) ? "ASSISTANT_ADAPTERS.md installed" : "ASSISTANT_ADAPTERS.md missing",
|
|
5858
|
+
"Run agent-kit init or agent-kit update"
|
|
5859
|
+
)
|
|
5860
|
+
);
|
|
5861
|
+
signals.push(
|
|
5862
|
+
signal(
|
|
5863
|
+
"l4-roster",
|
|
5864
|
+
4,
|
|
5865
|
+
"Machine-readable council roster",
|
|
5866
|
+
existsSync21(join23(cwd, ".agent-kit/agent-roster.json")),
|
|
5867
|
+
existsSync21(join23(cwd, ".agent-kit/agent-roster.json")) ? ".agent-kit/agent-roster.json present" : "Roster missing",
|
|
5868
|
+
"Run agent-kit init or agent-kit update"
|
|
5869
|
+
)
|
|
5870
|
+
);
|
|
5871
|
+
signals.push(
|
|
5872
|
+
signal(
|
|
5873
|
+
"l4-project-context",
|
|
5874
|
+
4,
|
|
5875
|
+
"Project context without open questions",
|
|
5876
|
+
contextReady,
|
|
5877
|
+
contextReady ? "Core project context fields complete" : openQuestions > 0 ? `${openQuestions} open question(s) remain` : "Fill product, audience, auth, and workflows in setup",
|
|
5878
|
+
"Complete setup wizard or edit .agent-kit/project-context.json"
|
|
5879
|
+
)
|
|
5880
|
+
);
|
|
5881
|
+
const tierB = detectTierBSubagents(cwd);
|
|
5882
|
+
signals.push(
|
|
5883
|
+
signal(
|
|
5884
|
+
"l5-subagents",
|
|
5885
|
+
5,
|
|
5886
|
+
"Tier-B specialist activation",
|
|
5887
|
+
tierB.pass,
|
|
5888
|
+
tierB.evidence,
|
|
5889
|
+
"Run agent-kit init --activate cursor|codex|claude|copilot"
|
|
5890
|
+
)
|
|
5891
|
+
);
|
|
5892
|
+
const loopCoding = existsSync21(join23(cwd, "LOOP_CODING.md"));
|
|
5893
|
+
signals.push(
|
|
5894
|
+
signal(
|
|
5895
|
+
"l6-loop-coding",
|
|
5896
|
+
6,
|
|
5897
|
+
"Loop coding playbook",
|
|
5898
|
+
loopCoding,
|
|
5899
|
+
loopCoding ? "LOOP_CODING.md installed" : "LOOP_CODING.md missing",
|
|
5900
|
+
"Run agent-kit update or agent-kit init on a current kit version"
|
|
5901
|
+
)
|
|
5902
|
+
);
|
|
5903
|
+
let auditPass = false;
|
|
5904
|
+
let auditEvidence = "Audit not run";
|
|
5905
|
+
try {
|
|
5906
|
+
const audit = createAuditReport(cwd);
|
|
5907
|
+
auditPass = audit.summary.fail === 0 && audit.readiness.level !== "needs-setup";
|
|
5908
|
+
auditEvidence = `${audit.summary.pass} pass / ${audit.summary.warn} warn / ${audit.summary.fail} fail \xB7 ${audit.readiness.level}`;
|
|
5909
|
+
} catch (error) {
|
|
5910
|
+
auditEvidence = error instanceof Error ? error.message : String(error);
|
|
5911
|
+
}
|
|
5912
|
+
signals.push(
|
|
5913
|
+
signal(
|
|
5914
|
+
"l6-audit-gate",
|
|
5915
|
+
6,
|
|
5916
|
+
"Audit gate at baseline-setup or better",
|
|
5917
|
+
auditPass,
|
|
5918
|
+
auditEvidence,
|
|
5919
|
+
"Run agent-kit audit --min-readiness baseline-setup and fix failures"
|
|
5920
|
+
)
|
|
5921
|
+
);
|
|
5922
|
+
if (maintainerProfile) {
|
|
5923
|
+
const pkgPath = join23(cwd, "package.json");
|
|
5924
|
+
let releaseCheck = false;
|
|
5925
|
+
if (existsSync21(pkgPath)) {
|
|
5926
|
+
try {
|
|
5927
|
+
const pkg = JSON.parse(readFileSync20(pkgPath, "utf8"));
|
|
5928
|
+
releaseCheck = Boolean(pkg.scripts?.["release:check"]) && existsSync21(join23(cwd, "scripts/release-check.mjs"));
|
|
5929
|
+
} catch {
|
|
5930
|
+
releaseCheck = false;
|
|
5931
|
+
}
|
|
5932
|
+
}
|
|
5933
|
+
signals.push(
|
|
5934
|
+
signal(
|
|
5935
|
+
"l6-maintainer-release-check",
|
|
5936
|
+
6,
|
|
5937
|
+
"Maintainer release-check gate",
|
|
5938
|
+
releaseCheck,
|
|
5939
|
+
releaseCheck ? "npm run release:check wired in package.json" : "release:check script missing",
|
|
5940
|
+
"Use npm run release:check before merge; see MAINTAINER_RELEASE.md"
|
|
5941
|
+
)
|
|
5942
|
+
);
|
|
5943
|
+
const maintainerDocs = existsSync21(join23(cwd, "MAINTAINER_RELEASE.md")) || readDocSnippet(cwd, "DOCS.md", ["maintainer dogfood", "dogfood:init"]);
|
|
5944
|
+
signals.push(
|
|
5945
|
+
signal(
|
|
5946
|
+
"l6-maintainer-docs",
|
|
5947
|
+
6,
|
|
5948
|
+
"Maintainer dogfood and release evidence docs",
|
|
5949
|
+
maintainerDocs,
|
|
5950
|
+
maintainerDocs ? "Maintainer climb docs present" : "Add MAINTAINER_RELEASE.md / DOCS maintainer section",
|
|
5951
|
+
"Read MAINTAINER_RELEASE.md and run npm run dogfood:init locally"
|
|
5952
|
+
)
|
|
5953
|
+
);
|
|
5954
|
+
} else {
|
|
5955
|
+
const onboarding = loadOnboardingState(cwd);
|
|
5956
|
+
const adapterTarget = adapterTargetForIde(onboarding.ideSurface);
|
|
5957
|
+
let adapterPass = false;
|
|
5958
|
+
let adapterEvidence = "No IDE surface selected for validation";
|
|
5959
|
+
if (adapterTarget) {
|
|
5960
|
+
const report2 = validateAdapter(cwd, adapterTarget);
|
|
5961
|
+
adapterPass = report2.summary.fail === 0;
|
|
5962
|
+
adapterEvidence = `${adapterTarget}: ${report2.summary.pass} pass / ${report2.summary.warn} warn / ${report2.summary.fail} fail`;
|
|
5963
|
+
} else if (tierB.pass) {
|
|
5964
|
+
const report2 = validateAdapter(cwd, "cursor");
|
|
5965
|
+
adapterPass = report2.summary.fail === 0;
|
|
5966
|
+
adapterEvidence = `cursor (detected): ${report2.summary.pass} pass / ${report2.summary.fail} fail`;
|
|
5967
|
+
}
|
|
5968
|
+
signals.push(
|
|
5969
|
+
signal(
|
|
5970
|
+
"l6-adapter-validate",
|
|
5971
|
+
6,
|
|
5972
|
+
"Adapter validate for active IDE",
|
|
5973
|
+
adapterPass,
|
|
5974
|
+
adapterEvidence,
|
|
5975
|
+
"Run agent-kit adapter validate cursor|codex|all"
|
|
5976
|
+
)
|
|
5977
|
+
);
|
|
5978
|
+
const ciWorkflow = existsSync21(join23(cwd, ".github/workflows/agent-kit-audit.yml"));
|
|
5979
|
+
const testingEval = existsSync21(join23(cwd, "TESTING.md")) && readDocSnippet(cwd, "TESTING.md", ["agent-kit audit", "eval"]);
|
|
5980
|
+
const evalLoop = ciWorkflow || testingEval;
|
|
5981
|
+
signals.push(
|
|
5982
|
+
signal(
|
|
5983
|
+
"l6-eval-loop",
|
|
5984
|
+
6,
|
|
5985
|
+
"Eval-driven loop documented in CI or TESTING.md",
|
|
5986
|
+
evalLoop,
|
|
5987
|
+
ciWorkflow ? ".github/workflows/agent-kit-audit.yml present" : testingEval ? "TESTING.md documents eval/audit loop" : "No CI audit workflow or TESTING eval section",
|
|
5988
|
+
"Enable agent-kit-audit.yml or add eval loop section to TESTING.md (see LOOP_CODING.md)"
|
|
5989
|
+
)
|
|
5990
|
+
);
|
|
5991
|
+
}
|
|
5992
|
+
return signals;
|
|
5993
|
+
}
|
|
5994
|
+
function computeCurrentLevel(signals) {
|
|
5995
|
+
let current = 3;
|
|
5996
|
+
for (const level of [3, 4, 5, 6]) {
|
|
5997
|
+
const tier = signals.filter((item) => item.level === level);
|
|
5998
|
+
if (tier.length === 0) continue;
|
|
5999
|
+
if (tier.every((item) => item.pass)) {
|
|
6000
|
+
current = level;
|
|
6001
|
+
} else {
|
|
6002
|
+
break;
|
|
6003
|
+
}
|
|
6004
|
+
}
|
|
6005
|
+
return current;
|
|
6006
|
+
}
|
|
6007
|
+
function defaultTargetLevel(maintainerProfile) {
|
|
6008
|
+
return maintainerProfile ? 6 : 5;
|
|
6009
|
+
}
|
|
6010
|
+
function resolveTargetLevel(cwd, maintainerProfile) {
|
|
6011
|
+
const onboarding = loadOnboardingState(cwd);
|
|
6012
|
+
const raw = onboarding.targetAgenticLevel;
|
|
6013
|
+
if (raw && raw >= 3 && raw <= 8) return raw;
|
|
6014
|
+
return defaultTargetLevel(maintainerProfile);
|
|
6015
|
+
}
|
|
6016
|
+
function computeAgenticLevel(cwd, options = {}) {
|
|
6017
|
+
const cacheKey = cwd;
|
|
6018
|
+
const cached = cache.get(cacheKey);
|
|
6019
|
+
if (!options.forceRefresh && cached && Date.now() - cached.at < CACHE_TTL_MS) {
|
|
6020
|
+
return cached.report;
|
|
6021
|
+
}
|
|
6022
|
+
const maintainerProfile = isMaintainerSourceRepo(cwd);
|
|
6023
|
+
const signals = buildSignals(cwd, maintainerProfile);
|
|
6024
|
+
const currentLevel = computeCurrentLevel(signals);
|
|
6025
|
+
const targetLevel = resolveTargetLevel(cwd, maintainerProfile);
|
|
6026
|
+
const climbSteps = signals.filter((item) => !item.pass && item.level <= Math.min(targetLevel, 6)).slice(0, 5);
|
|
6027
|
+
const report2 = AgenticLevelContract.parse({
|
|
6028
|
+
currentLevel,
|
|
6029
|
+
targetLevel,
|
|
6030
|
+
maintainerProfile,
|
|
6031
|
+
computedAt: nowIso(),
|
|
6032
|
+
maintainerNote: maintainerProfile ? "Kit source repo \u2014 run npm run dogfood:init locally; overlay is gitignored." : void 0,
|
|
6033
|
+
signals,
|
|
6034
|
+
climbSteps
|
|
6035
|
+
});
|
|
6036
|
+
cache.set(cacheKey, { at: Date.now(), report: report2 });
|
|
6037
|
+
saveOnboardingState(cwd, {
|
|
6038
|
+
lastAgenticLevel: currentLevel,
|
|
6039
|
+
lastAgenticComputedAt: report2.computedAt
|
|
6040
|
+
});
|
|
6041
|
+
return report2;
|
|
6042
|
+
}
|
|
6043
|
+
function summarizeAdapterValidation(cwd, ideSurface) {
|
|
6044
|
+
const target = adapterTargetForIde(ideSurface);
|
|
6045
|
+
if (!target) {
|
|
6046
|
+
return { pass: 0, warn: 0, fail: 0, target: null };
|
|
6047
|
+
}
|
|
6048
|
+
const report2 = validateAdapter(cwd, target);
|
|
6049
|
+
return {
|
|
6050
|
+
pass: report2.summary.pass,
|
|
6051
|
+
warn: report2.summary.warn,
|
|
6052
|
+
fail: report2.summary.fail,
|
|
6053
|
+
target
|
|
6054
|
+
};
|
|
6055
|
+
}
|
|
6056
|
+
function invalidateAgenticLevelCache(cwd) {
|
|
6057
|
+
cache.delete(cwd);
|
|
6058
|
+
}
|
|
6059
|
+
|
|
5400
6060
|
// src/studio/setup-server.ts
|
|
5401
6061
|
var DEFAULT_PORT = 9321;
|
|
5402
6062
|
var DEFAULT_HOST = "127.0.0.1";
|
|
@@ -5440,7 +6100,7 @@ function sendHtml(response, html) {
|
|
|
5440
6100
|
});
|
|
5441
6101
|
response.end(html);
|
|
5442
6102
|
}
|
|
5443
|
-
function buildStatePayload(cwd) {
|
|
6103
|
+
function buildStatePayload(cwd, options = {}) {
|
|
5444
6104
|
ensureProjectContextForSetup(cwd);
|
|
5445
6105
|
const viewModel = getSetupFormViewModel(cwd);
|
|
5446
6106
|
const onboarding = loadOnboardingState(cwd);
|
|
@@ -5448,6 +6108,10 @@ function buildStatePayload(cwd) {
|
|
|
5448
6108
|
const designDraft = loadDesignDraft(cwd);
|
|
5449
6109
|
const messagingDraft = loadMessagingDraft(cwd);
|
|
5450
6110
|
const draft = loadWizardDraft(cwd);
|
|
6111
|
+
const agenticLevel = computeAgenticLevel(
|
|
6112
|
+
cwd,
|
|
6113
|
+
options.forceAgenticRefresh ? { forceRefresh: true } : {}
|
|
6114
|
+
);
|
|
5451
6115
|
return {
|
|
5452
6116
|
projectName: viewModel.projectName,
|
|
5453
6117
|
form: buildWizardFormState(cwd),
|
|
@@ -5458,6 +6122,7 @@ function buildStatePayload(cwd) {
|
|
|
5458
6122
|
hasSupabase: viewModel.hasSupabase,
|
|
5459
6123
|
onboarding,
|
|
5460
6124
|
progress,
|
|
6125
|
+
agenticLevel,
|
|
5461
6126
|
designDraft,
|
|
5462
6127
|
messagingDraft,
|
|
5463
6128
|
draftUpdatedAt: draft.updatedAt,
|
|
@@ -5519,8 +6184,10 @@ async function handleRequest(cwd, request, response) {
|
|
|
5519
6184
|
if (body.currentSection) patch.currentSection = String(body.currentSection);
|
|
5520
6185
|
if (typeof body.currentStep === "number") patch.currentStep = body.currentStep;
|
|
5521
6186
|
if (Array.isArray(body.completedSections)) patch.completedSections = body.completedSections;
|
|
6187
|
+
if (typeof body.targetAgenticLevel === "number") patch.targetAgenticLevel = body.targetAgenticLevel;
|
|
5522
6188
|
if (Object.keys(patch).length > 0) saveOnboardingState(cwd, patch);
|
|
5523
|
-
|
|
6189
|
+
invalidateAgenticLevelCache(cwd);
|
|
6190
|
+
sendJson(response, 200, buildStatePayload(cwd, { forceAgenticRefresh: true }));
|
|
5524
6191
|
} catch (error) {
|
|
5525
6192
|
sendJson(response, 400, { error: error instanceof Error ? error.message : String(error) });
|
|
5526
6193
|
}
|
|
@@ -5533,8 +6200,9 @@ async function handleRequest(cwd, request, response) {
|
|
|
5533
6200
|
const result = applySetupFormAnswers(cwd, payload);
|
|
5534
6201
|
saveAgentBriefs(cwd, extractAgentBriefsFromForm(raw));
|
|
5535
6202
|
markQuickPathComplete(cwd);
|
|
6203
|
+
invalidateAgenticLevelCache(cwd);
|
|
5536
6204
|
sendJson(response, 200, {
|
|
5537
|
-
...buildStatePayload(cwd),
|
|
6205
|
+
...buildStatePayload(cwd, { forceAgenticRefresh: true }),
|
|
5538
6206
|
saved: true,
|
|
5539
6207
|
contextPath: result.contextPath,
|
|
5540
6208
|
markdownPath: result.markdownPath,
|
|
@@ -5574,9 +6242,27 @@ async function handleRequest(cwd, request, response) {
|
|
|
5574
6242
|
try {
|
|
5575
6243
|
const body = await readJsonBody(request);
|
|
5576
6244
|
if (!body.ideSurface) throw new Error("ideSurface is required.");
|
|
6245
|
+
const activateTarget = ideSurfaceToActivateTarget(body.ideSurface);
|
|
6246
|
+
let activation;
|
|
6247
|
+
if (activateTarget) {
|
|
6248
|
+
const activateResult = activateIdeTargets({ cwd, targets: [activateTarget] });
|
|
6249
|
+
activation = {
|
|
6250
|
+
activated: activateResult.activated,
|
|
6251
|
+
copied: activateResult.copied,
|
|
6252
|
+
unchanged: activateResult.unchanged,
|
|
6253
|
+
conflicts: activateResult.conflicts
|
|
6254
|
+
};
|
|
6255
|
+
}
|
|
5577
6256
|
const result = saveIdeChecklist(cwd, body.ideSurface);
|
|
5578
6257
|
markSectionComplete(cwd, "ide");
|
|
5579
|
-
|
|
6258
|
+
invalidateAgenticLevelCache(cwd);
|
|
6259
|
+
const adapterValidation = summarizeAdapterValidation(cwd, body.ideSurface);
|
|
6260
|
+
sendJson(response, 200, {
|
|
6261
|
+
...result,
|
|
6262
|
+
activation,
|
|
6263
|
+
adapterValidation,
|
|
6264
|
+
...buildStatePayload(cwd, { forceAgenticRefresh: true })
|
|
6265
|
+
});
|
|
5580
6266
|
} catch (error) {
|
|
5581
6267
|
sendJson(response, 400, { error: error instanceof Error ? error.message : String(error) });
|
|
5582
6268
|
}
|
|
@@ -5634,6 +6320,11 @@ async function handleRequest(cwd, request, response) {
|
|
|
5634
6320
|
}
|
|
5635
6321
|
return;
|
|
5636
6322
|
}
|
|
6323
|
+
if (request.method === "POST" && url.pathname === "/api/agentic-level/refresh") {
|
|
6324
|
+
invalidateAgenticLevelCache(cwd);
|
|
6325
|
+
sendJson(response, 200, buildStatePayload(cwd, { forceAgenticRefresh: true }));
|
|
6326
|
+
return;
|
|
6327
|
+
}
|
|
5637
6328
|
sendJson(response, 404, { error: "Not found." });
|
|
5638
6329
|
}
|
|
5639
6330
|
function listen(server, host, port) {
|
|
@@ -5689,7 +6380,7 @@ async function startSetupServer(options) {
|
|
|
5689
6380
|
// src/studio/studio-server.ts
|
|
5690
6381
|
import { watch } from "fs";
|
|
5691
6382
|
import { createServer as createServer2 } from "http";
|
|
5692
|
-
import { join as
|
|
6383
|
+
import { join as join24 } from "path";
|
|
5693
6384
|
var DEFAULT_PORT2 = 9331;
|
|
5694
6385
|
var DEFAULT_HOST2 = "127.0.0.1";
|
|
5695
6386
|
var sseClients = /* @__PURE__ */ new Set();
|
|
@@ -5731,7 +6422,7 @@ function stopWatcher() {
|
|
|
5731
6422
|
}
|
|
5732
6423
|
}
|
|
5733
6424
|
function watchSessionEvents(cwd, sessionId) {
|
|
5734
|
-
const eventsPath2 =
|
|
6425
|
+
const eventsPath2 = join24(cwd, COUNCIL_SESSIONS_DIR, sessionId, "events.jsonl");
|
|
5735
6426
|
if (watchedEventsPath === eventsPath2 && activeWatcher) return;
|
|
5736
6427
|
stopWatcher();
|
|
5737
6428
|
watchedEventsPath = eventsPath2;
|
|
@@ -5887,8 +6578,8 @@ async function startStudioServer(options) {
|
|
|
5887
6578
|
}
|
|
5888
6579
|
|
|
5889
6580
|
// src/studio/session-checkpoint.ts
|
|
5890
|
-
import { existsSync as
|
|
5891
|
-
import { extname, join as
|
|
6581
|
+
import { existsSync as existsSync22, readFileSync as readFileSync21 } from "fs";
|
|
6582
|
+
import { extname, join as join25 } from "path";
|
|
5892
6583
|
function parseCheckpointMarkdown(content) {
|
|
5893
6584
|
const payload = { notes: [], decisions: [], handoffs: [], outputs: [] };
|
|
5894
6585
|
const sections = content.split(/^## /m).slice(1);
|
|
@@ -5944,7 +6635,7 @@ function parseCheckpointMarkdown(content) {
|
|
|
5944
6635
|
return payload;
|
|
5945
6636
|
}
|
|
5946
6637
|
function parseCheckpointFile(filePath) {
|
|
5947
|
-
const content =
|
|
6638
|
+
const content = readFileSync21(filePath, "utf8");
|
|
5948
6639
|
const ext = extname(filePath).toLowerCase();
|
|
5949
6640
|
if (ext === ".json") {
|
|
5950
6641
|
const parsed = JSON.parse(content);
|
|
@@ -6020,8 +6711,8 @@ function applySessionCheckpoint(cwd, payload) {
|
|
|
6020
6711
|
};
|
|
6021
6712
|
}
|
|
6022
6713
|
function checkpointSessionFromFile(cwd, filePath) {
|
|
6023
|
-
const absolute =
|
|
6024
|
-
if (!
|
|
6714
|
+
const absolute = join25(cwd, filePath);
|
|
6715
|
+
if (!existsSync22(absolute)) throw new Error(`Checkpoint file not found: ${filePath}`);
|
|
6025
6716
|
return applySessionCheckpoint(cwd, parseCheckpointFile(absolute));
|
|
6026
6717
|
}
|
|
6027
6718
|
|