@appsforgood/next-supabase-kit 0.1.3 → 0.1.4
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 +9 -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 +627 -280
- package/dist/index.js.map +1 -1
- package/examples/next-supabase-installed/.agent-kit/manifest.json +3 -3
- package/examples/next-supabase-installed/.agent-kit/overrides.json +1 -7
- package/examples/next-supabase-installed/audit-output.json +9 -2
- package/package.json +1 -1
- package/templates/next-supabase/ASSISTANT_ADAPTERS.md +26 -2
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.4";
|
|
150
150
|
var DEFAULT_CONFIG = {
|
|
151
151
|
stack: "next-supabase",
|
|
152
152
|
projectType: "saas",
|
|
@@ -215,6 +215,20 @@ var CURSOR_ADAPTER_FILES = [
|
|
|
215
215
|
target: ".cursor/rules/cursor-model-selection.mdc"
|
|
216
216
|
}
|
|
217
217
|
];
|
|
218
|
+
var CURSOR_SCOPED_ADAPTER_FILES = [
|
|
219
|
+
{
|
|
220
|
+
source: "assistant-adapters/cursor-planner.mdc",
|
|
221
|
+
target: ".cursor/rules/cursor-planner.mdc"
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
source: "assistant-adapters/cursor-security.mdc",
|
|
225
|
+
target: ".cursor/rules/cursor-security.mdc"
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
source: "assistant-adapters/cursor-frontend.mdc",
|
|
229
|
+
target: ".cursor/rules/cursor-frontend.mdc"
|
|
230
|
+
}
|
|
231
|
+
];
|
|
218
232
|
var COPILOT_INSTRUCTION_FILES = [
|
|
219
233
|
{
|
|
220
234
|
source: "assistant-adapters/github-copilot-instructions.md",
|
|
@@ -345,8 +359,8 @@ function unique(values) {
|
|
|
345
359
|
}
|
|
346
360
|
|
|
347
361
|
// src/install/audit.ts
|
|
348
|
-
import { existsSync as
|
|
349
|
-
import { join as
|
|
362
|
+
import { existsSync as existsSync11, readFileSync as readFileSync8, statSync } from "fs";
|
|
363
|
+
import { join as join11 } from "path";
|
|
350
364
|
|
|
351
365
|
// src/config/contracts.ts
|
|
352
366
|
import { z } from "zod";
|
|
@@ -1100,11 +1114,15 @@ function onboardingStateExists(cwd) {
|
|
|
1100
1114
|
}
|
|
1101
1115
|
|
|
1102
1116
|
// src/install/install.ts
|
|
1117
|
+
import { existsSync as existsSync10, readFileSync as readFileSync7 } from "fs";
|
|
1118
|
+
import { join as join10 } from "path";
|
|
1119
|
+
|
|
1120
|
+
// src/install/ide-activate.ts
|
|
1103
1121
|
import { existsSync as existsSync9, readFileSync as readFileSync6 } from "fs";
|
|
1104
1122
|
import { join as join9 } from "path";
|
|
1105
1123
|
|
|
1106
|
-
// src/install/
|
|
1107
|
-
import { existsSync as existsSync8, readFileSync as readFileSync5 } from "fs";
|
|
1124
|
+
// src/install/roster-adapters.ts
|
|
1125
|
+
import { existsSync as existsSync8, readFileSync as readFileSync5, readdirSync as readdirSync2 } from "fs";
|
|
1108
1126
|
import { join as join8 } from "path";
|
|
1109
1127
|
|
|
1110
1128
|
// src/studio/wizard/roster.ts
|
|
@@ -1188,8 +1206,224 @@ function agentBriefFieldName(agentId) {
|
|
|
1188
1206
|
return `agentBrief_${agentId}`;
|
|
1189
1207
|
}
|
|
1190
1208
|
|
|
1191
|
-
// src/install/
|
|
1209
|
+
// src/install/assistant-adapters-table.ts
|
|
1210
|
+
function extractAssistantAdapterRow(adaptersDoc, toolLabel) {
|
|
1211
|
+
for (const line of adaptersDoc.split("\n")) {
|
|
1212
|
+
const trimmed = line.trim();
|
|
1213
|
+
if (!trimmed.startsWith("|")) continue;
|
|
1214
|
+
const cells = trimmed.split("|").map((cell) => cell.trim()).filter((cell) => cell.length > 0);
|
|
1215
|
+
if (cells[0] === toolLabel) return trimmed;
|
|
1216
|
+
}
|
|
1217
|
+
return null;
|
|
1218
|
+
}
|
|
1219
|
+
function assistantAdapterRowIsActive(adaptersDoc, toolLabel) {
|
|
1220
|
+
const row = extractAssistantAdapterRow(adaptersDoc, toolLabel);
|
|
1221
|
+
if (!row) return false;
|
|
1222
|
+
const cells = row.split("|").map((cell) => cell.trim()).filter((cell) => cell.length > 0);
|
|
1223
|
+
const instructionStatus = cells[2] ?? "";
|
|
1224
|
+
return /^Active/i.test(instructionStatus) && !/^TBD/i.test(instructionStatus);
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
// src/install/roster-adapters.ts
|
|
1192
1228
|
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`";
|
|
1229
|
+
function quoteYamlScalar(value) {
|
|
1230
|
+
return JSON.stringify(value);
|
|
1231
|
+
}
|
|
1232
|
+
function buildAgentHint(agentId, name) {
|
|
1233
|
+
if (agentId === "planner") return "Start with the Planner workflow.";
|
|
1234
|
+
if (agentId === "lead-architect") return "Convene council for core changes before implementation.";
|
|
1235
|
+
if (agentId === "frontend-design-lead") {
|
|
1236
|
+
return "Require brand/content intake, creative-direction rationale, and visual QA evidence for UI changes.";
|
|
1237
|
+
}
|
|
1238
|
+
if (agentId === "security-reviewer") {
|
|
1239
|
+
return "Review auth, RLS, data mutation, dependency, external-call, secret, and release-risk changes.";
|
|
1240
|
+
}
|
|
1241
|
+
return `Use for ${name.toLowerCase()} work defined in the roster.`;
|
|
1242
|
+
}
|
|
1243
|
+
function buildProactiveSuffix(agentId) {
|
|
1244
|
+
const suffixes = {
|
|
1245
|
+
planner: "Use proactively for planning, scope breakdown, ambiguous requests, and workflow routing.",
|
|
1246
|
+
"lead-architect": "Use proactively for core changes, architecture, and cross-layer decisions.",
|
|
1247
|
+
"security-reviewer": "Use proactively for auth, RLS, API, Server Action, data mutation, dependency, secret, and release-risk changes.",
|
|
1248
|
+
"frontend-design-lead": "Use proactively for UI, design system, accessibility, and visual QA work.",
|
|
1249
|
+
"qa-engineer": "Use proactively after behavior changes to add or verify tests and acceptance evidence.",
|
|
1250
|
+
"supabase-postgres-engineer": "Use proactively for schema, migrations, RLS, auth, and SQL changes.",
|
|
1251
|
+
"nextjs-engineer": "Use proactively for App Router, Server Components, route handlers, and UI state work.",
|
|
1252
|
+
"marketing-copy-lead": "Use proactively for public-facing copy, positioning, and conversion surfaces.",
|
|
1253
|
+
"docs-maintainer": "Use proactively after significant changes to update living documentation.",
|
|
1254
|
+
"deployment-observability-engineer": "Use proactively for release, env var, migration order, monitoring, and rollback work."
|
|
1255
|
+
};
|
|
1256
|
+
return suffixes[agentId] ?? "";
|
|
1257
|
+
}
|
|
1258
|
+
function buildSubagentDescription(agent, proactive) {
|
|
1259
|
+
const base = agent.roleSummary.length > 140 ? `${agent.roleSummary.slice(0, 137)}...` : agent.roleSummary;
|
|
1260
|
+
if (!proactive) return base;
|
|
1261
|
+
const suffix = buildProactiveSuffix(agent.id);
|
|
1262
|
+
return suffix ? `${base} ${suffix}` : base;
|
|
1263
|
+
}
|
|
1264
|
+
function buildSubagentMarkdown(agent, options = {}) {
|
|
1265
|
+
const description = buildSubagentDescription(agent, Boolean(options.proactive));
|
|
1266
|
+
const agentFile = agent.file ?? `.agent-kit/agents/${agent.id}.md`;
|
|
1267
|
+
const hint = buildAgentHint(agent.id, agent.name);
|
|
1268
|
+
return `---
|
|
1269
|
+
name: ${quoteYamlScalar(agent.id)}
|
|
1270
|
+
description: ${quoteYamlScalar(description)}
|
|
1271
|
+
---
|
|
1272
|
+
|
|
1273
|
+
Read ${CANONICAL_READ_LIST} before making routing or implementation decisions.
|
|
1274
|
+
|
|
1275
|
+
Also read \`${agentFile}\` for this role's detailed contract.
|
|
1276
|
+
|
|
1277
|
+
${hint}
|
|
1278
|
+
|
|
1279
|
+
For council work, delegate to this subagent instead of role-playing the council in the main thread.
|
|
1280
|
+
|
|
1281
|
+
Record meaningful decisions, risks, handoffs, human corrections, artifacts, evidence, and verification through \`agent-kit session checkpoint\` or individual \`agent-kit session ...\` commands when available.
|
|
1282
|
+
`;
|
|
1283
|
+
}
|
|
1284
|
+
function writeGeneratedAgentFile(cwd, relativePath, content, force, result) {
|
|
1285
|
+
const targetPath = join8(cwd, relativePath);
|
|
1286
|
+
if (!force && existsSync8(targetPath)) {
|
|
1287
|
+
const existing = readFileSync5(targetPath, "utf8");
|
|
1288
|
+
if (existing === content) {
|
|
1289
|
+
result.unchanged.push(relativePath);
|
|
1290
|
+
return;
|
|
1291
|
+
}
|
|
1292
|
+
const conflictPath = join8(cwd, ".agent-kit", "conflicts", relativePath.replace(/\//g, "__"));
|
|
1293
|
+
ensureDir(join8(cwd, ".agent-kit", "conflicts"));
|
|
1294
|
+
writeText(conflictPath, existing);
|
|
1295
|
+
result.conflicts.push(`${relativePath} -> ${conflictPath}`);
|
|
1296
|
+
return;
|
|
1297
|
+
}
|
|
1298
|
+
ensureDir(join8(cwd, relativePath.split("/").slice(0, -1).join("/")));
|
|
1299
|
+
writeText(targetPath, content);
|
|
1300
|
+
result.copied.push(relativePath);
|
|
1301
|
+
}
|
|
1302
|
+
function generateMarkdownSubagents(cwd, agentsDir, options) {
|
|
1303
|
+
ensureDir(join8(cwd, agentsDir));
|
|
1304
|
+
for (const agent of loadProjectRosterAgents(cwd)) {
|
|
1305
|
+
const relativePath = `${agentsDir}/${agent.id}.md`;
|
|
1306
|
+
writeGeneratedAgentFile(cwd, relativePath, buildSubagentMarkdown(agent, options), options.force, options.result);
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
var CURSOR_AGENTS_README = `# Cursor council subagents
|
|
1310
|
+
|
|
1311
|
+
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.
|
|
1312
|
+
|
|
1313
|
+
## Delegation
|
|
1314
|
+
|
|
1315
|
+
| Risk / work type | Subagent |
|
|
1316
|
+
| --- | --- |
|
|
1317
|
+
| Planning / scope | \`@planner\` |
|
|
1318
|
+
| Core architecture | \`@lead-architect\` |
|
|
1319
|
+
| Auth / RLS / secrets | \`@security-reviewer\` or Task \`security-review\` |
|
|
1320
|
+
| Frontend UI | \`@frontend-design-lead\` |
|
|
1321
|
+
| QA / tests | \`@qa-engineer\` |
|
|
1322
|
+
|
|
1323
|
+
Record handoffs with \`agent-kit session checkpoint --file <json>\` when the CLI is available.
|
|
1324
|
+
|
|
1325
|
+
Regenerate with \`agent-kit init --activate cursor\` after roster changes.
|
|
1326
|
+
`;
|
|
1327
|
+
function generateCursorSubagents(cwd, force, result) {
|
|
1328
|
+
generateMarkdownSubagents(cwd, ".cursor/agents", { proactive: true, force, result });
|
|
1329
|
+
writeGeneratedAgentFile(cwd, ".cursor/agents/README.md", CURSOR_AGENTS_README, force, result);
|
|
1330
|
+
}
|
|
1331
|
+
function loadAgentReasoningEffortMap(cwd) {
|
|
1332
|
+
const path = join8(cwd, ".agent-kit/model-routing.json");
|
|
1333
|
+
const map = /* @__PURE__ */ new Map();
|
|
1334
|
+
if (!existsSync8(path)) return map;
|
|
1335
|
+
try {
|
|
1336
|
+
const parsed = JSON.parse(readFileSync5(path, "utf8"));
|
|
1337
|
+
const profileEffort = /* @__PURE__ */ new Map();
|
|
1338
|
+
for (const profile of parsed.profiles ?? []) {
|
|
1339
|
+
if (profile.id && profile.reasoningEffort) {
|
|
1340
|
+
const effort = profile.reasoningEffort;
|
|
1341
|
+
if (effort === "low" || effort === "medium" || effort === "high") {
|
|
1342
|
+
profileEffort.set(profile.id, effort);
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
for (const binding of parsed.agentRoutes ?? []) {
|
|
1347
|
+
if (!binding.agentId) continue;
|
|
1348
|
+
const direct = binding.defaultEffort;
|
|
1349
|
+
if (direct === "low" || direct === "medium" || direct === "high") {
|
|
1350
|
+
map.set(binding.agentId, direct);
|
|
1351
|
+
continue;
|
|
1352
|
+
}
|
|
1353
|
+
if (binding.profileId && profileEffort.has(binding.profileId)) {
|
|
1354
|
+
map.set(binding.agentId, profileEffort.get(binding.profileId));
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
} catch {
|
|
1358
|
+
return map;
|
|
1359
|
+
}
|
|
1360
|
+
return map;
|
|
1361
|
+
}
|
|
1362
|
+
function escapeTomlString(value) {
|
|
1363
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
1364
|
+
}
|
|
1365
|
+
function buildCodexAgentToml(agent, effort) {
|
|
1366
|
+
const description = buildSubagentDescription(agent, true);
|
|
1367
|
+
const agentFile = agent.file ?? `.agent-kit/agents/${agent.id}.md`;
|
|
1368
|
+
const hint = buildAgentHint(agent.id, agent.name);
|
|
1369
|
+
const instructions = [
|
|
1370
|
+
`Read AGENTS.md, AGENT_ROSTER.md, .agent-kit/agent-roster.json, MODEL_ROUTING.md,`,
|
|
1371
|
+
`.agent-kit/model-routing.json, project context, corrections, COUNCIL.md, QUALITY_GATES.md,`,
|
|
1372
|
+
`and ${agentFile} before reviewing or implementing.`,
|
|
1373
|
+
"",
|
|
1374
|
+
hint,
|
|
1375
|
+
"",
|
|
1376
|
+
"Record meaningful decisions, risks, handoffs, and verification through agent-kit session checkpoint when available."
|
|
1377
|
+
].join("\n");
|
|
1378
|
+
return `name = "${agent.id}"
|
|
1379
|
+
description = "${escapeTomlString(description)}"
|
|
1380
|
+
# model = "gpt-5.5" # verify in your Codex environment; see MODEL_ROUTING.md
|
|
1381
|
+
model_reasoning_effort = "${effort}"
|
|
1382
|
+
|
|
1383
|
+
developer_instructions = """
|
|
1384
|
+
${instructions}
|
|
1385
|
+
"""
|
|
1386
|
+
`;
|
|
1387
|
+
}
|
|
1388
|
+
function generateCodexCustomAgents(cwd, force, result) {
|
|
1389
|
+
ensureDir(join8(cwd, ".codex/agents"));
|
|
1390
|
+
const effortMap = loadAgentReasoningEffortMap(cwd);
|
|
1391
|
+
for (const agent of loadProjectRosterAgents(cwd)) {
|
|
1392
|
+
const effort = effortMap.get(agent.id) ?? "medium";
|
|
1393
|
+
const relativePath = `.codex/agents/${agent.id}.toml`;
|
|
1394
|
+
writeGeneratedAgentFile(cwd, relativePath, buildCodexAgentToml(agent, effort), force, result);
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
function skillDescriptionFromMarkdown(text, skillId) {
|
|
1398
|
+
const useWhen = text.match(/## Use When\s*\n+\s*([^\n#]+)/);
|
|
1399
|
+
if (useWhen?.[1]) return useWhen[1].trim().slice(0, 200);
|
|
1400
|
+
const firstHeading = text.match(/^#\s+(.+)/m);
|
|
1401
|
+
if (firstHeading?.[1]) return `${firstHeading[1].trim()} \u2014 Agent Kit council skill.`;
|
|
1402
|
+
return `Agent Kit skill for ${skillId.replace(/-/g, " ")}.`;
|
|
1403
|
+
}
|
|
1404
|
+
function kitSkillToCursorSkill(skillId, kitMarkdown) {
|
|
1405
|
+
const description = skillDescriptionFromMarkdown(kitMarkdown, skillId);
|
|
1406
|
+
const body = kitMarkdown.replace(/^#\s+.+\n+/, "").trimStart();
|
|
1407
|
+
return `---
|
|
1408
|
+
name: ${quoteYamlScalar(skillId)}
|
|
1409
|
+
description: ${quoteYamlScalar(description)}
|
|
1410
|
+
---
|
|
1411
|
+
|
|
1412
|
+
${body.trim()}
|
|
1413
|
+
`;
|
|
1414
|
+
}
|
|
1415
|
+
function generateCursorSkillsFromKit(cwd, force, result) {
|
|
1416
|
+
const skillsRoot = join8(cwd, ".agent-kit/skills");
|
|
1417
|
+
if (!existsSync8(skillsRoot)) return;
|
|
1418
|
+
for (const file of readdirSync2(skillsRoot).filter((name) => name.endsWith(".md"))) {
|
|
1419
|
+
const skillId = file.replace(/\.md$/, "");
|
|
1420
|
+
const kitMarkdown = readFileSync5(join8(skillsRoot, file), "utf8");
|
|
1421
|
+
const relativePath = `.cursor/skills/${skillId}/SKILL.md`;
|
|
1422
|
+
writeGeneratedAgentFile(cwd, relativePath, kitSkillToCursorSkill(skillId, kitMarkdown), force, result);
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
// src/install/ide-activate.ts
|
|
1193
1427
|
function normalizeTargets(targets) {
|
|
1194
1428
|
const allowed = /* @__PURE__ */ new Set(["cursor", "claude", "codex", "copilot", "antigravity"]);
|
|
1195
1429
|
const normalized = /* @__PURE__ */ new Set();
|
|
@@ -1204,9 +1438,9 @@ function normalizeTargets(targets) {
|
|
|
1204
1438
|
return [...normalized];
|
|
1205
1439
|
}
|
|
1206
1440
|
function copyAdapterFile(cwd, packageRoot, source, target, force, result) {
|
|
1207
|
-
const copyResult = copyTextWithConflict(
|
|
1441
|
+
const copyResult = copyTextWithConflict(join9(packageRoot, source), cwd, target, {
|
|
1208
1442
|
force,
|
|
1209
|
-
conflictRoot:
|
|
1443
|
+
conflictRoot: join9(cwd, ".agent-kit", "conflicts")
|
|
1210
1444
|
});
|
|
1211
1445
|
if (copyResult.action === "created") result.copied.push(copyResult.target);
|
|
1212
1446
|
if (copyResult.action === "unchanged") result.unchanged.push(copyResult.target);
|
|
@@ -1215,53 +1449,18 @@ function copyAdapterFile(cwd, packageRoot, source, target, force, result) {
|
|
|
1215
1449
|
result.conflicts.push(`${copyResult.target} -> ${copyResult.conflictPath}`);
|
|
1216
1450
|
}
|
|
1217
1451
|
}
|
|
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
1452
|
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
|
-
}
|
|
1453
|
+
generateMarkdownSubagents(cwd, ".claude/agents", { proactive: false, force, result });
|
|
1255
1454
|
copyAdapterFile(cwd, packageRoot, CLAUDE_TEMPLATE, "CLAUDE.md", force, result);
|
|
1256
1455
|
}
|
|
1257
1456
|
function copyDirectoryAsConflicts(cwd, packageRoot, sourceDir, targetDir, force, result) {
|
|
1258
|
-
for (const file of listFilesRecursive(
|
|
1259
|
-
copyAdapterFile(cwd, packageRoot,
|
|
1457
|
+
for (const file of listFilesRecursive(join9(packageRoot, sourceDir))) {
|
|
1458
|
+
copyAdapterFile(cwd, packageRoot, join9(sourceDir, file), join9(targetDir, file).replace(/\\/g, "/"), force, result);
|
|
1260
1459
|
}
|
|
1261
1460
|
}
|
|
1262
1461
|
function installAntigravityAdapter(cwd, packageRoot, force, result) {
|
|
1263
|
-
ensureDir(
|
|
1264
|
-
ensureDir(
|
|
1462
|
+
ensureDir(join9(cwd, ".antigravity", "agent-kit", "commands"));
|
|
1463
|
+
ensureDir(join9(cwd, ".antigravity", "runtime-skills"));
|
|
1265
1464
|
for (const file of ANTIGRAVITY_PLUGIN_FILES) {
|
|
1266
1465
|
copyAdapterFile(cwd, packageRoot, file.source, file.target, force, result);
|
|
1267
1466
|
}
|
|
@@ -1283,38 +1482,38 @@ function installAntigravityAdapter(cwd, packageRoot, force, result) {
|
|
|
1283
1482
|
);
|
|
1284
1483
|
}
|
|
1285
1484
|
function updateAssistantAdaptersTable(cwd, activated) {
|
|
1286
|
-
const path =
|
|
1287
|
-
if (!
|
|
1288
|
-
let content =
|
|
1485
|
+
const path = join9(cwd, "ASSISTANT_ADAPTERS.md");
|
|
1486
|
+
if (!existsSync9(path)) return;
|
|
1487
|
+
let content = readFileSync6(path, "utf8");
|
|
1289
1488
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
1290
1489
|
if (activated.has("cursor") && content.includes("| Cursor |")) {
|
|
1291
1490
|
content = content.replace(
|
|
1292
1491
|
/\| Cursor \|[^|]*\|[^|]*\|[^|]*\|[^|]*\|[^|]*\|[^|]*\|/,
|
|
1293
|
-
|
|
1492
|
+
`| 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
1493
|
);
|
|
1295
1494
|
}
|
|
1296
1495
|
if (activated.has("copilot") && content.includes("| GitHub Copilot")) {
|
|
1297
1496
|
content = content.replace(
|
|
1298
1497
|
/\| GitHub Copilot[^|]*\|[^|]*\|[^|]*\|[^|]*\|[^|]*\|[^|]*\|[^|]*\|/,
|
|
1299
|
-
|
|
1498
|
+
`| 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
1499
|
);
|
|
1301
1500
|
}
|
|
1302
1501
|
if (activated.has("claude") && content.includes("| Claude Code |")) {
|
|
1303
1502
|
content = content.replace(
|
|
1304
1503
|
/\| Claude Code \|[^|]*\|[^|]*\|[^|]*\|[^|]*\|[^|]*\|[^|]*\|/,
|
|
1305
|
-
|
|
1504
|
+
`| 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
1505
|
);
|
|
1307
1506
|
}
|
|
1308
1507
|
if (activated.has("codex") && content.includes("| Codex / AGENTS.md-compatible tools |")) {
|
|
1309
1508
|
content = content.replace(
|
|
1310
1509
|
/\| Codex \/ AGENTS\.md-compatible tools \|[^|]*\|[^|]*\|[^|]*\|[^|]*\|[^|]*\|[^|]*\|/,
|
|
1311
|
-
|
|
1510
|
+
`| 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
1511
|
);
|
|
1313
1512
|
}
|
|
1314
1513
|
if (activated.has("antigravity") && content.includes("| Antigravity |")) {
|
|
1315
1514
|
content = content.replace(
|
|
1316
1515
|
/\| Antigravity \|[^|]*\|[^|]*\|[^|]*\|[^|]*\|[^|]*\|[^|]*\|/,
|
|
1317
|
-
|
|
1516
|
+
`| 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
1517
|
);
|
|
1319
1518
|
}
|
|
1320
1519
|
writeText(path, content);
|
|
@@ -1341,9 +1540,14 @@ function activateIdeTargets(options) {
|
|
|
1341
1540
|
for (const adapter2 of CURSOR_ADAPTER_FILES) {
|
|
1342
1541
|
copyAdapterFile(cwd, packageRoot, adapter2.source, adapter2.target, force, result);
|
|
1343
1542
|
}
|
|
1543
|
+
for (const adapter2 of CURSOR_SCOPED_ADAPTER_FILES) {
|
|
1544
|
+
copyAdapterFile(cwd, packageRoot, adapter2.source, adapter2.target, force, result);
|
|
1545
|
+
}
|
|
1546
|
+
generateCursorSubagents(cwd, force, result);
|
|
1547
|
+
generateCursorSkillsFromKit(cwd, force, result);
|
|
1344
1548
|
}
|
|
1345
1549
|
if (activated.has("copilot")) {
|
|
1346
|
-
ensureDir(
|
|
1550
|
+
ensureDir(join9(cwd, ".github", "instructions"));
|
|
1347
1551
|
for (const file of COPILOT_INSTRUCTION_FILES) {
|
|
1348
1552
|
copyAdapterFile(cwd, packageRoot, file.source, file.target, force, result);
|
|
1349
1553
|
}
|
|
@@ -1352,8 +1556,9 @@ function activateIdeTargets(options) {
|
|
|
1352
1556
|
generateClaudeSubagents(cwd, packageRoot, force, result);
|
|
1353
1557
|
}
|
|
1354
1558
|
if (activated.has("codex")) {
|
|
1355
|
-
ensureDir(
|
|
1559
|
+
ensureDir(join9(cwd, ".codex"));
|
|
1356
1560
|
copyAdapterFile(cwd, packageRoot, CODEX_CONFIG_SOURCE, ".codex/config.toml", force, result);
|
|
1561
|
+
generateCodexCustomAgents(cwd, force, result);
|
|
1357
1562
|
}
|
|
1358
1563
|
if (activated.has("antigravity")) {
|
|
1359
1564
|
installAntigravityAdapter(cwd, packageRoot, force, result);
|
|
@@ -1361,18 +1566,25 @@ function activateIdeTargets(options) {
|
|
|
1361
1566
|
updateAssistantAdaptersTable(cwd, activated);
|
|
1362
1567
|
return result;
|
|
1363
1568
|
}
|
|
1569
|
+
function ideSurfaceToActivateTarget(ideSurface) {
|
|
1570
|
+
const value = ideSurface.trim().toLowerCase();
|
|
1571
|
+
if (value === "cursor" || value === "claude" || value === "codex" || value === "copilot") {
|
|
1572
|
+
return value;
|
|
1573
|
+
}
|
|
1574
|
+
return null;
|
|
1575
|
+
}
|
|
1364
1576
|
|
|
1365
1577
|
// src/install/install.ts
|
|
1366
1578
|
function initProject(options) {
|
|
1367
1579
|
const cwd = options.cwd;
|
|
1368
1580
|
const stack = options.stack ?? DEFAULT_CONFIG.stack;
|
|
1369
1581
|
const packageRoot = findPackageRoot();
|
|
1370
|
-
const templateRoot =
|
|
1371
|
-
if (!
|
|
1582
|
+
const templateRoot = join10(packageRoot, "templates", stack);
|
|
1583
|
+
if (!existsSync10(templateRoot)) {
|
|
1372
1584
|
throw new Error(`Unsupported stack profile: ${stack}`);
|
|
1373
1585
|
}
|
|
1374
|
-
ensureDir(
|
|
1375
|
-
ensureDir(
|
|
1586
|
+
ensureDir(join10(cwd, ".agent-kit"));
|
|
1587
|
+
ensureDir(join10(cwd, ".agent-kit", "conflicts"));
|
|
1376
1588
|
const result = {
|
|
1377
1589
|
copied: [],
|
|
1378
1590
|
unchanged: [],
|
|
@@ -1382,11 +1594,11 @@ function initProject(options) {
|
|
|
1382
1594
|
};
|
|
1383
1595
|
const templateHashes = {};
|
|
1384
1596
|
for (const doc of ROOT_DOCS) {
|
|
1385
|
-
const templatePath =
|
|
1386
|
-
templateHashes[doc] = sha256(
|
|
1597
|
+
const templatePath = join10(templateRoot, doc);
|
|
1598
|
+
templateHashes[doc] = sha256(readFileSync7(templatePath, "utf8"));
|
|
1387
1599
|
const copyResult = copyTextWithConflict(templatePath, cwd, doc, {
|
|
1388
1600
|
force: Boolean(options.force),
|
|
1389
|
-
conflictRoot:
|
|
1601
|
+
conflictRoot: join10(cwd, ".agent-kit", "conflicts")
|
|
1390
1602
|
});
|
|
1391
1603
|
if (copyResult.action === "created") result.copied.push(copyResult.target);
|
|
1392
1604
|
if (copyResult.action === "unchanged") result.unchanged.push(copyResult.target);
|
|
@@ -1396,12 +1608,12 @@ function initProject(options) {
|
|
|
1396
1608
|
}
|
|
1397
1609
|
}
|
|
1398
1610
|
for (const folder of LIBRARY_FOLDERS) {
|
|
1399
|
-
copyDirectory(
|
|
1611
|
+
copyDirectory(join10(packageRoot, folder), join10(cwd, ".agent-kit", folder));
|
|
1400
1612
|
}
|
|
1401
1613
|
for (const adapter2 of CURSOR_ADAPTER_FILES) {
|
|
1402
|
-
const adapterCopy = copyTextWithConflict(
|
|
1614
|
+
const adapterCopy = copyTextWithConflict(join10(packageRoot, adapter2.source), cwd, adapter2.target, {
|
|
1403
1615
|
force: Boolean(options.force),
|
|
1404
|
-
conflictRoot:
|
|
1616
|
+
conflictRoot: join10(cwd, ".agent-kit", "conflicts")
|
|
1405
1617
|
});
|
|
1406
1618
|
if (adapterCopy.action === "created") result.copied.push(adapterCopy.target);
|
|
1407
1619
|
if (adapterCopy.action === "unchanged") result.unchanged.push(adapterCopy.target);
|
|
@@ -1410,17 +1622,17 @@ function initProject(options) {
|
|
|
1410
1622
|
result.conflicts.push(`${adapterCopy.target} -> ${adapterCopy.conflictPath}`);
|
|
1411
1623
|
}
|
|
1412
1624
|
}
|
|
1413
|
-
const rosterCopy = copyTextWithConflict(
|
|
1625
|
+
const rosterCopy = copyTextWithConflict(join10(packageRoot, DEFAULT_AGENT_ROSTER_SOURCE), cwd, DEFAULT_AGENT_ROSTER_TARGET, {
|
|
1414
1626
|
force: Boolean(options.force),
|
|
1415
|
-
conflictRoot:
|
|
1627
|
+
conflictRoot: join10(cwd, ".agent-kit", "conflicts")
|
|
1416
1628
|
});
|
|
1417
1629
|
if (rosterCopy.action === "created") result.copied.push(rosterCopy.target);
|
|
1418
1630
|
if (rosterCopy.action === "unchanged") result.unchanged.push(rosterCopy.target);
|
|
1419
1631
|
if (rosterCopy.action === "overwritten") result.overwritten.push(rosterCopy.target);
|
|
1420
1632
|
if (rosterCopy.action === "conflict") result.conflicts.push(`${rosterCopy.target} -> ${rosterCopy.conflictPath}`);
|
|
1421
|
-
const modelRoutingCopy = copyTextWithConflict(
|
|
1633
|
+
const modelRoutingCopy = copyTextWithConflict(join10(packageRoot, DEFAULT_MODEL_ROUTING_SOURCE), cwd, DEFAULT_MODEL_ROUTING_TARGET, {
|
|
1422
1634
|
force: Boolean(options.force),
|
|
1423
|
-
conflictRoot:
|
|
1635
|
+
conflictRoot: join10(cwd, ".agent-kit", "conflicts")
|
|
1424
1636
|
});
|
|
1425
1637
|
if (modelRoutingCopy.action === "created") result.copied.push(modelRoutingCopy.target);
|
|
1426
1638
|
if (modelRoutingCopy.action === "unchanged") result.unchanged.push(modelRoutingCopy.target);
|
|
@@ -1437,17 +1649,17 @@ function initProject(options) {
|
|
|
1437
1649
|
modelRouting: DEFAULT_MODEL_ROUTING_TARGET,
|
|
1438
1650
|
templateHashes
|
|
1439
1651
|
};
|
|
1440
|
-
writeText(
|
|
1652
|
+
writeText(join10(cwd, ".agent-kit", "manifest.json"), `${JSON.stringify(manifest, null, 2)}
|
|
1441
1653
|
`);
|
|
1442
|
-
writeText(
|
|
1654
|
+
writeText(join10(cwd, ".agent-kit", "config.json"), `${JSON.stringify(DEFAULT_CONFIG, null, 2)}
|
|
1443
1655
|
`);
|
|
1444
|
-
const overridesPath =
|
|
1445
|
-
if (!
|
|
1656
|
+
const overridesPath = join10(cwd, ".agent-kit", "overrides.json");
|
|
1657
|
+
if (!existsSync10(overridesPath)) writeText(overridesPath, `${JSON.stringify({ templates: {} }, null, 2)}
|
|
1446
1658
|
`);
|
|
1447
1659
|
for (const template of CI_TEMPLATE_FILES) {
|
|
1448
|
-
const ciCopy = copyTextWithConflict(
|
|
1660
|
+
const ciCopy = copyTextWithConflict(join10(packageRoot, template.source), cwd, template.target, {
|
|
1449
1661
|
force: Boolean(options.force),
|
|
1450
|
-
conflictRoot:
|
|
1662
|
+
conflictRoot: join10(cwd, ".agent-kit", "conflicts")
|
|
1451
1663
|
});
|
|
1452
1664
|
if (ciCopy.action === "created") result.copied.push(ciCopy.target);
|
|
1453
1665
|
if (ciCopy.action === "unchanged") result.unchanged.push(ciCopy.target);
|
|
@@ -1471,9 +1683,9 @@ function initProject(options) {
|
|
|
1471
1683
|
return result;
|
|
1472
1684
|
}
|
|
1473
1685
|
function readManifest(cwd) {
|
|
1474
|
-
const manifestPath =
|
|
1475
|
-
if (!
|
|
1476
|
-
return JSON.parse(
|
|
1686
|
+
const manifestPath = join10(cwd, ".agent-kit", "manifest.json");
|
|
1687
|
+
if (!existsSync10(manifestPath)) return null;
|
|
1688
|
+
return JSON.parse(readFileSync7(manifestPath, "utf8"));
|
|
1477
1689
|
}
|
|
1478
1690
|
|
|
1479
1691
|
// src/install/audit.ts
|
|
@@ -1542,24 +1754,24 @@ function includesAll(text, values) {
|
|
|
1542
1754
|
return values.every((value) => lower.includes(value.toLowerCase()));
|
|
1543
1755
|
}
|
|
1544
1756
|
function readDoc(cwd, file) {
|
|
1545
|
-
const path =
|
|
1546
|
-
return
|
|
1757
|
+
const path = join11(cwd, file);
|
|
1758
|
+
return existsSync11(path) ? readFileSync8(path, "utf8") : "";
|
|
1547
1759
|
}
|
|
1548
1760
|
function isPackageRepository(cwd) {
|
|
1549
|
-
const packagePath =
|
|
1550
|
-
if (!
|
|
1761
|
+
const packagePath = join11(cwd, "package.json");
|
|
1762
|
+
if (!existsSync11(packagePath)) return false;
|
|
1551
1763
|
try {
|
|
1552
|
-
const packageJson = JSON.parse(
|
|
1553
|
-
return packageJson.name === "@appsforgood/next-supabase-kit" &&
|
|
1764
|
+
const packageJson = JSON.parse(readFileSync8(packagePath, "utf8"));
|
|
1765
|
+
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
1766
|
} catch {
|
|
1555
1767
|
return false;
|
|
1556
1768
|
}
|
|
1557
1769
|
}
|
|
1558
1770
|
function readOverrides(cwd) {
|
|
1559
|
-
const path =
|
|
1560
|
-
if (!
|
|
1771
|
+
const path = join11(cwd, ".agent-kit", "overrides.json");
|
|
1772
|
+
if (!existsSync11(path)) return {};
|
|
1561
1773
|
try {
|
|
1562
|
-
const parsed = JSON.parse(
|
|
1774
|
+
const parsed = JSON.parse(readFileSync8(path, "utf8"));
|
|
1563
1775
|
const templates = parsed.templates ?? {};
|
|
1564
1776
|
return Object.fromEntries(
|
|
1565
1777
|
Object.entries(templates).map(([file, override]) => [
|
|
@@ -1572,8 +1784,8 @@ function readOverrides(cwd) {
|
|
|
1572
1784
|
}
|
|
1573
1785
|
}
|
|
1574
1786
|
function readTemplate(stack, file) {
|
|
1575
|
-
const path =
|
|
1576
|
-
return
|
|
1787
|
+
const path = join11(findPackageRoot(), "templates", stack, file);
|
|
1788
|
+
return existsSync11(path) ? readFileSync8(path, "utf8") : null;
|
|
1577
1789
|
}
|
|
1578
1790
|
function asStringArray(value) {
|
|
1579
1791
|
if (!Array.isArray(value)) return [];
|
|
@@ -1583,8 +1795,8 @@ function isRecord(value) {
|
|
|
1583
1795
|
return typeof value === "object" && value !== null;
|
|
1584
1796
|
}
|
|
1585
1797
|
function addAgentRosterFindings(cwd, findings, rosterRelativePath = DEFAULT_AGENT_ROSTER_TARGET) {
|
|
1586
|
-
const rosterPath =
|
|
1587
|
-
if (!
|
|
1798
|
+
const rosterPath = join11(cwd, rosterRelativePath);
|
|
1799
|
+
if (!existsSync11(rosterPath)) {
|
|
1588
1800
|
findings.push({
|
|
1589
1801
|
level: "fail",
|
|
1590
1802
|
area: "agents",
|
|
@@ -1595,7 +1807,7 @@ function addAgentRosterFindings(cwd, findings, rosterRelativePath = DEFAULT_AGEN
|
|
|
1595
1807
|
}
|
|
1596
1808
|
let roster;
|
|
1597
1809
|
try {
|
|
1598
|
-
const parsed = JSON.parse(
|
|
1810
|
+
const parsed = JSON.parse(readFileSync8(rosterPath, "utf8"));
|
|
1599
1811
|
if (!isRecord(parsed)) throw new Error("Roster must be a JSON object.");
|
|
1600
1812
|
const contractResult = AgentRosterContract.safeParse(parsed);
|
|
1601
1813
|
if (!contractResult.success) {
|
|
@@ -1782,15 +1994,15 @@ function addAgentRosterFindings(cwd, findings, rosterRelativePath = DEFAULT_AGEN
|
|
|
1782
1994
|
}
|
|
1783
1995
|
}
|
|
1784
1996
|
function addCouncilSessionRecordFindings(cwd, findings) {
|
|
1785
|
-
const sessionsRoot =
|
|
1786
|
-
if (!
|
|
1997
|
+
const sessionsRoot = join11(cwd, COUNCIL_SESSION_DIR);
|
|
1998
|
+
if (!existsSync11(sessionsRoot)) return;
|
|
1787
1999
|
const sessionFiles = listFilesRecursive(sessionsRoot).filter((file) => file.endsWith(".json") && !/[\\/]/.test(file));
|
|
1788
2000
|
if (sessionFiles.length === 0) return;
|
|
1789
2001
|
let invalidCount = 0;
|
|
1790
2002
|
for (const sessionFile of sessionFiles) {
|
|
1791
2003
|
const displayPath = `${COUNCIL_SESSION_DIR}/${sessionFile}`;
|
|
1792
2004
|
try {
|
|
1793
|
-
const parsed = JSON.parse(
|
|
2005
|
+
const parsed = JSON.parse(readFileSync8(join11(sessionsRoot, sessionFile), "utf8"));
|
|
1794
2006
|
const contractResult = CouncilSessionContract.safeParse(parsed);
|
|
1795
2007
|
if (!contractResult.success) {
|
|
1796
2008
|
invalidCount += 1;
|
|
@@ -1821,8 +2033,8 @@ function addCouncilSessionRecordFindings(cwd, findings) {
|
|
|
1821
2033
|
}
|
|
1822
2034
|
function addSchemaFindings(cwd, findings, schemaRootRelativePath = ".agent-kit/schemas") {
|
|
1823
2035
|
for (const schemaFile of REQUIRED_SCHEMA_FILES) {
|
|
1824
|
-
const schemaPath =
|
|
1825
|
-
if (!
|
|
2036
|
+
const schemaPath = join11(cwd, schemaRootRelativePath, schemaFile);
|
|
2037
|
+
if (!existsSync11(schemaPath)) {
|
|
1826
2038
|
findings.push({
|
|
1827
2039
|
level: "warn",
|
|
1828
2040
|
area: "agents",
|
|
@@ -1832,7 +2044,7 @@ function addSchemaFindings(cwd, findings, schemaRootRelativePath = ".agent-kit/s
|
|
|
1832
2044
|
continue;
|
|
1833
2045
|
}
|
|
1834
2046
|
try {
|
|
1835
|
-
const parsed = JSON.parse(
|
|
2047
|
+
const parsed = JSON.parse(readFileSync8(schemaPath, "utf8"));
|
|
1836
2048
|
if (!isRecord(parsed) || typeof parsed.$schema !== "string" || !isRecord(parsed.properties)) {
|
|
1837
2049
|
throw new Error("Schema file is missing JSON Schema metadata.");
|
|
1838
2050
|
}
|
|
@@ -1852,8 +2064,8 @@ function addSchemaFindings(cwd, findings, schemaRootRelativePath = ".agent-kit/s
|
|
|
1852
2064
|
}
|
|
1853
2065
|
}
|
|
1854
2066
|
function addAgentStudioFindings(cwd, findings) {
|
|
1855
|
-
const contextPath =
|
|
1856
|
-
if (!
|
|
2067
|
+
const contextPath = join11(cwd, CONTEXT_JSON);
|
|
2068
|
+
if (!existsSync11(contextPath)) {
|
|
1857
2069
|
findings.push({
|
|
1858
2070
|
level: "warn",
|
|
1859
2071
|
area: "studio",
|
|
@@ -1862,7 +2074,7 @@ function addAgentStudioFindings(cwd, findings) {
|
|
|
1862
2074
|
});
|
|
1863
2075
|
} else {
|
|
1864
2076
|
try {
|
|
1865
|
-
const parsed = JSON.parse(
|
|
2077
|
+
const parsed = JSON.parse(readFileSync8(contextPath, "utf8"));
|
|
1866
2078
|
const result = ProjectContextContract.safeParse(parsed);
|
|
1867
2079
|
if (!result.success) {
|
|
1868
2080
|
findings.push({
|
|
@@ -1926,8 +2138,8 @@ function addAgentStudioFindings(cwd, findings) {
|
|
|
1926
2138
|
});
|
|
1927
2139
|
}
|
|
1928
2140
|
}
|
|
1929
|
-
const contextMdPath =
|
|
1930
|
-
if (
|
|
2141
|
+
const contextMdPath = join11(cwd, CONTEXT_MD);
|
|
2142
|
+
if (existsSync11(contextPath) && !existsSync11(contextMdPath)) {
|
|
1931
2143
|
findings.push({
|
|
1932
2144
|
level: "warn",
|
|
1933
2145
|
area: "studio",
|
|
@@ -1936,10 +2148,10 @@ function addAgentStudioFindings(cwd, findings) {
|
|
|
1936
2148
|
});
|
|
1937
2149
|
}
|
|
1938
2150
|
for (const relativePath of [PROJECT_RULES_JSON, AGENT_RULES_JSON]) {
|
|
1939
|
-
const path =
|
|
1940
|
-
if (!
|
|
2151
|
+
const path = join11(cwd, relativePath);
|
|
2152
|
+
if (!existsSync11(path)) continue;
|
|
1941
2153
|
try {
|
|
1942
|
-
const parsed = JSON.parse(
|
|
2154
|
+
const parsed = JSON.parse(readFileSync8(path, "utf8"));
|
|
1943
2155
|
const result = CorrectionRulesContract.safeParse(parsed);
|
|
1944
2156
|
if (!result.success) {
|
|
1945
2157
|
findings.push({
|
|
@@ -1964,9 +2176,9 @@ function addAgentStudioFindings(cwd, findings) {
|
|
|
1964
2176
|
});
|
|
1965
2177
|
}
|
|
1966
2178
|
}
|
|
1967
|
-
const studioExportPath =
|
|
1968
|
-
if (
|
|
1969
|
-
const exportHtml =
|
|
2179
|
+
const studioExportPath = join11(cwd, STUDIO_EXPORT_HTML);
|
|
2180
|
+
if (existsSync11(studioExportPath)) {
|
|
2181
|
+
const exportHtml = readFileSync8(studioExportPath, "utf8");
|
|
1970
2182
|
if (containsLikelySecret(exportHtml)) {
|
|
1971
2183
|
findings.push({
|
|
1972
2184
|
level: "fail",
|
|
@@ -1989,8 +2201,8 @@ function addAgentStudioFindings(cwd, findings) {
|
|
|
1989
2201
|
});
|
|
1990
2202
|
}
|
|
1991
2203
|
}
|
|
1992
|
-
const sessionsRoot =
|
|
1993
|
-
if (!
|
|
2204
|
+
const sessionsRoot = join11(cwd, COUNCIL_SESSION_DIR);
|
|
2205
|
+
if (!existsSync11(sessionsRoot)) return;
|
|
1994
2206
|
const files = listFilesRecursive(sessionsRoot);
|
|
1995
2207
|
const studioSessionFiles = files.filter((file) => /[\\/]session\.json$/.test(file));
|
|
1996
2208
|
for (const sessionFile of studioSessionFiles) {
|
|
@@ -2001,10 +2213,10 @@ function addAgentStudioFindings(cwd, findings) {
|
|
|
2001
2213
|
const eventsRelative = `${COUNCIL_SESSION_DIR}/${normalizedSessionDir}/events.jsonl`;
|
|
2002
2214
|
const indexRelative = `${COUNCIL_SESSION_DIR}/${normalizedSessionDir}/index.md`;
|
|
2003
2215
|
const transcriptRelative = `${COUNCIL_SESSION_DIR}/${normalizedSessionDir}/transcript.md`;
|
|
2004
|
-
const sessionDirPath =
|
|
2216
|
+
const sessionDirPath = join11(sessionsRoot, sessionDir2);
|
|
2005
2217
|
let sessionResult = null;
|
|
2006
2218
|
try {
|
|
2007
|
-
sessionResult = StudioSessionContract.safeParse(JSON.parse(
|
|
2219
|
+
sessionResult = StudioSessionContract.safeParse(JSON.parse(readFileSync8(join11(sessionDirPath, "session.json"), "utf8")));
|
|
2008
2220
|
if (!sessionResult.success) {
|
|
2009
2221
|
findings.push({
|
|
2010
2222
|
level: "fail",
|
|
@@ -2023,8 +2235,8 @@ function addAgentStudioFindings(cwd, findings) {
|
|
|
2023
2235
|
});
|
|
2024
2236
|
continue;
|
|
2025
2237
|
}
|
|
2026
|
-
const eventsPath2 =
|
|
2027
|
-
if (!
|
|
2238
|
+
const eventsPath2 = join11(sessionDirPath, "events.jsonl");
|
|
2239
|
+
if (!existsSync11(eventsPath2)) {
|
|
2028
2240
|
findings.push({
|
|
2029
2241
|
level: "fail",
|
|
2030
2242
|
area: "studio",
|
|
@@ -2033,7 +2245,7 @@ function addAgentStudioFindings(cwd, findings) {
|
|
|
2033
2245
|
});
|
|
2034
2246
|
continue;
|
|
2035
2247
|
}
|
|
2036
|
-
const eventText =
|
|
2248
|
+
const eventText = readFileSync8(eventsPath2, "utf8");
|
|
2037
2249
|
if (containsLikelySecret(eventText)) {
|
|
2038
2250
|
findings.push({
|
|
2039
2251
|
level: "fail",
|
|
@@ -2068,7 +2280,7 @@ function addAgentStudioFindings(cwd, findings) {
|
|
|
2068
2280
|
});
|
|
2069
2281
|
}
|
|
2070
2282
|
}
|
|
2071
|
-
if (!
|
|
2283
|
+
if (!existsSync11(join11(sessionDirPath, "index.md")) || !existsSync11(join11(sessionDirPath, "transcript.md"))) {
|
|
2072
2284
|
findings.push({
|
|
2073
2285
|
level: "warn",
|
|
2074
2286
|
area: "studio",
|
|
@@ -2076,8 +2288,8 @@ function addAgentStudioFindings(cwd, findings) {
|
|
|
2076
2288
|
remediation: "Run agent-kit session render so humans can inspect the current agent transcript and handoffs."
|
|
2077
2289
|
});
|
|
2078
2290
|
} else {
|
|
2079
|
-
const indexText =
|
|
2080
|
-
const transcriptText =
|
|
2291
|
+
const indexText = readFileSync8(join11(sessionDirPath, "index.md"), "utf8");
|
|
2292
|
+
const transcriptText = readFileSync8(join11(sessionDirPath, "transcript.md"), "utf8");
|
|
2081
2293
|
if (containsLikelySecret(indexText) || containsLikelySecret(transcriptText)) {
|
|
2082
2294
|
findings.push({
|
|
2083
2295
|
level: "fail",
|
|
@@ -2086,7 +2298,7 @@ function addAgentStudioFindings(cwd, findings) {
|
|
|
2086
2298
|
remediation: "Regenerate Markdown after redacting sensitive values from the event log."
|
|
2087
2299
|
});
|
|
2088
2300
|
}
|
|
2089
|
-
if (statSync(eventsPath2).mtimeMs > statSync(
|
|
2301
|
+
if (statSync(eventsPath2).mtimeMs > statSync(join11(sessionDirPath, "index.md")).mtimeMs) {
|
|
2090
2302
|
findings.push({
|
|
2091
2303
|
level: "warn",
|
|
2092
2304
|
area: "studio",
|
|
@@ -2136,8 +2348,8 @@ function addCouncilDocFindings(cwd, findings) {
|
|
|
2136
2348
|
}
|
|
2137
2349
|
function addAssistantAdapterFindings(cwd, findings, adapterRootRelativePath = ".agent-kit/assistant-adapters", docsCwd = cwd) {
|
|
2138
2350
|
const adaptersDoc = readDoc(docsCwd, "ASSISTANT_ADAPTERS.md");
|
|
2139
|
-
const adapterRoot =
|
|
2140
|
-
if (!
|
|
2351
|
+
const adapterRoot = join11(cwd, adapterRootRelativePath);
|
|
2352
|
+
if (!existsSync11(adapterRoot)) {
|
|
2141
2353
|
findings.push({
|
|
2142
2354
|
level: "warn",
|
|
2143
2355
|
area: "agents",
|
|
@@ -2157,7 +2369,7 @@ function addAssistantAdapterFindings(cwd, findings, adapterRootRelativePath = ".
|
|
|
2157
2369
|
level: "warn",
|
|
2158
2370
|
area: "agents",
|
|
2159
2371
|
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."
|
|
2372
|
+
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
2373
|
});
|
|
2162
2374
|
} else {
|
|
2163
2375
|
findings.push({
|
|
@@ -2166,6 +2378,22 @@ function addAssistantAdapterFindings(cwd, findings, adapterRootRelativePath = ".
|
|
|
2166
2378
|
message: "ASSISTANT_ADAPTERS.md maps the council roster to tool-specific instruction surfaces."
|
|
2167
2379
|
});
|
|
2168
2380
|
}
|
|
2381
|
+
if (assistantAdapterRowIsActive(adaptersDoc, "Cursor") && !existsSync11(join11(cwd, ".cursor/agents/planner.md"))) {
|
|
2382
|
+
findings.push({
|
|
2383
|
+
level: "warn",
|
|
2384
|
+
area: "agents",
|
|
2385
|
+
message: "Cursor is marked Active but .cursor/agents/planner.md is missing.",
|
|
2386
|
+
remediation: "Run agent-kit init --activate cursor to generate council subagents from the roster."
|
|
2387
|
+
});
|
|
2388
|
+
}
|
|
2389
|
+
if (assistantAdapterRowIsActive(adaptersDoc, "Codex / AGENTS.md-compatible tools") && !existsSync11(join11(cwd, ".codex/agents/planner.toml"))) {
|
|
2390
|
+
findings.push({
|
|
2391
|
+
level: "warn",
|
|
2392
|
+
area: "agents",
|
|
2393
|
+
message: "Codex is marked Active but .codex/agents/planner.toml is missing.",
|
|
2394
|
+
remediation: "Run agent-kit init --activate codex to generate council custom agents from the roster."
|
|
2395
|
+
});
|
|
2396
|
+
}
|
|
2169
2397
|
if (!includesAll(adaptersDoc, ["model selection", "enforcement", "MODEL_ROUTING.md", "model-routing.json"])) {
|
|
2170
2398
|
findings.push({
|
|
2171
2399
|
level: "warn",
|
|
@@ -2206,8 +2434,8 @@ function addModelRoutingFindings(cwd, findings, routingRelativePath = DEFAULT_MO
|
|
|
2206
2434
|
message: "MODEL_ROUTING.md documents agent model profiles and IDE enforcement limits."
|
|
2207
2435
|
});
|
|
2208
2436
|
}
|
|
2209
|
-
const routingPath =
|
|
2210
|
-
if (!
|
|
2437
|
+
const routingPath = join11(cwd, routingRelativePath);
|
|
2438
|
+
if (!existsSync11(routingPath)) {
|
|
2211
2439
|
findings.push({
|
|
2212
2440
|
level: "warn",
|
|
2213
2441
|
area: "models",
|
|
@@ -2218,7 +2446,7 @@ function addModelRoutingFindings(cwd, findings, routingRelativePath = DEFAULT_MO
|
|
|
2218
2446
|
}
|
|
2219
2447
|
let routing;
|
|
2220
2448
|
try {
|
|
2221
|
-
routing = JSON.parse(
|
|
2449
|
+
routing = JSON.parse(readFileSync8(routingPath, "utf8"));
|
|
2222
2450
|
} catch {
|
|
2223
2451
|
findings.push({
|
|
2224
2452
|
level: "warn",
|
|
@@ -2276,11 +2504,11 @@ function addTemplateHashFindings(cwd, findings) {
|
|
|
2276
2504
|
if (!manifest) return;
|
|
2277
2505
|
const overrides = readOverrides(cwd);
|
|
2278
2506
|
for (const doc of ROOT_DOCS) {
|
|
2279
|
-
const targetPath =
|
|
2280
|
-
if (!
|
|
2507
|
+
const targetPath = join11(cwd, doc);
|
|
2508
|
+
if (!existsSync11(targetPath)) continue;
|
|
2281
2509
|
const currentTemplate = readTemplate(manifest.stack, doc);
|
|
2282
2510
|
if (!currentTemplate) continue;
|
|
2283
|
-
const targetHash = sha256(
|
|
2511
|
+
const targetHash = sha256(readFileSync8(targetPath, "utf8"));
|
|
2284
2512
|
const currentTemplateHash = sha256(currentTemplate);
|
|
2285
2513
|
const installedTemplateHash = manifest.templateHashes?.[doc];
|
|
2286
2514
|
const override = overrides[doc];
|
|
@@ -2584,7 +2812,7 @@ function auditProject(cwd) {
|
|
|
2584
2812
|
const manifest = readManifest(cwd);
|
|
2585
2813
|
const packageRepository = isPackageRepository(cwd);
|
|
2586
2814
|
const packageSourceMode = packageRepository && !manifest;
|
|
2587
|
-
const docsCwd = packageSourceMode ?
|
|
2815
|
+
const docsCwd = packageSourceMode ? join11(cwd, "templates", "next-supabase") : cwd;
|
|
2588
2816
|
if (!manifest) {
|
|
2589
2817
|
if (packageRepository) {
|
|
2590
2818
|
findings.push({
|
|
@@ -2611,13 +2839,13 @@ function auditProject(cwd) {
|
|
|
2611
2839
|
addAgentRosterFindings(cwd, findings, packageSourceMode ? "rosters/next-supabase-default-council.json" : DEFAULT_AGENT_ROSTER_TARGET);
|
|
2612
2840
|
addSchemaFindings(cwd, findings, packageSourceMode ? "schemas" : ".agent-kit/schemas");
|
|
2613
2841
|
addCouncilSessionRecordFindings(cwd, findings);
|
|
2614
|
-
if (!packageRepository ||
|
|
2842
|
+
if (!packageRepository || existsSync11(join11(cwd, CONTEXT_JSON)) || existsSync11(join11(cwd, COUNCIL_SESSION_DIR))) {
|
|
2615
2843
|
addAgentStudioFindings(cwd, findings);
|
|
2616
2844
|
}
|
|
2617
2845
|
for (const doc of ROOT_DOCS) {
|
|
2618
|
-
const docPath =
|
|
2846
|
+
const docPath = join11(docsCwd, doc);
|
|
2619
2847
|
const displayPath = packageSourceMode ? `templates/next-supabase/${doc}` : doc;
|
|
2620
|
-
if (
|
|
2848
|
+
if (existsSync11(docPath)) {
|
|
2621
2849
|
findings.push({ level: "pass", area: "docs", message: `${displayPath} exists.` });
|
|
2622
2850
|
} else {
|
|
2623
2851
|
findings.push({
|
|
@@ -2682,10 +2910,10 @@ function auditProject(cwd) {
|
|
|
2682
2910
|
return findings;
|
|
2683
2911
|
}
|
|
2684
2912
|
function readPackageJson2(cwd) {
|
|
2685
|
-
const path =
|
|
2686
|
-
if (!
|
|
2913
|
+
const path = join11(cwd, "package.json");
|
|
2914
|
+
if (!existsSync11(path)) return null;
|
|
2687
2915
|
try {
|
|
2688
|
-
return JSON.parse(
|
|
2916
|
+
return JSON.parse(readFileSync8(path, "utf8"));
|
|
2689
2917
|
} catch {
|
|
2690
2918
|
return null;
|
|
2691
2919
|
}
|
|
@@ -2699,8 +2927,8 @@ function containsLikelySecretForAudit(relativeFile, content) {
|
|
|
2699
2927
|
return containsLikelySecret(content);
|
|
2700
2928
|
}
|
|
2701
2929
|
function addProjectRealityFindings(cwd, findings, options = {}) {
|
|
2702
|
-
const migrationsDir =
|
|
2703
|
-
if (
|
|
2930
|
+
const migrationsDir = join11(cwd, "supabase", "migrations");
|
|
2931
|
+
if (existsSync11(migrationsDir)) {
|
|
2704
2932
|
const sqlFiles = listFilesRecursive(migrationsDir).filter((file) => file.endsWith(".sql"));
|
|
2705
2933
|
if (sqlFiles.length === 0) {
|
|
2706
2934
|
findings.push({
|
|
@@ -2711,7 +2939,7 @@ function addProjectRealityFindings(cwd, findings, options = {}) {
|
|
|
2711
2939
|
});
|
|
2712
2940
|
} else {
|
|
2713
2941
|
const rlsFiles = sqlFiles.filter((file) => {
|
|
2714
|
-
const content =
|
|
2942
|
+
const content = readFileSync8(join11(migrationsDir, file), "utf8");
|
|
2715
2943
|
return /enable\s+row\s+level\s+security/i.test(content);
|
|
2716
2944
|
});
|
|
2717
2945
|
if (rlsFiles.length === 0) {
|
|
@@ -2761,7 +2989,7 @@ function addProjectRealityFindings(cwd, findings, options = {}) {
|
|
|
2761
2989
|
return /\.(ts|tsx|js|jsx|env|json)$/.test(file);
|
|
2762
2990
|
});
|
|
2763
2991
|
const secretHits = trackedSourceFiles.map((file) => {
|
|
2764
|
-
const content =
|
|
2992
|
+
const content = readFileSync8(join11(cwd, file), "utf8");
|
|
2765
2993
|
return containsLikelySecretForAudit(file, content) ? file : null;
|
|
2766
2994
|
}).filter((file) => file !== null).slice(0, 5);
|
|
2767
2995
|
if (secretHits.length > 0) {
|
|
@@ -2784,7 +3012,7 @@ function addProjectRealityFindings(cwd, findings, options = {}) {
|
|
|
2784
3012
|
area: "project-reality",
|
|
2785
3013
|
message: "Package source repository mode does not require installed-project context files."
|
|
2786
3014
|
});
|
|
2787
|
-
} else if (!
|
|
3015
|
+
} else if (!existsSync11(join11(cwd, CONTEXT_JSON))) {
|
|
2788
3016
|
findings.push({
|
|
2789
3017
|
level: "warn",
|
|
2790
3018
|
area: "project-reality",
|
|
@@ -2850,7 +3078,7 @@ function report(target, findings) {
|
|
|
2850
3078
|
}
|
|
2851
3079
|
function readJson(path) {
|
|
2852
3080
|
try {
|
|
2853
|
-
return JSON.parse(
|
|
3081
|
+
return JSON.parse(readFileSync9(path, "utf8"));
|
|
2854
3082
|
} catch {
|
|
2855
3083
|
return null;
|
|
2856
3084
|
}
|
|
@@ -2864,24 +3092,24 @@ function isSafeRelativePath(path) {
|
|
|
2864
3092
|
return !normalized.startsWith("/") && !normalized.startsWith("../") && !normalized.includes("/../");
|
|
2865
3093
|
}
|
|
2866
3094
|
function findAntigravityLayout(cwd) {
|
|
2867
|
-
const sourcePlugin =
|
|
2868
|
-
if (
|
|
3095
|
+
const sourcePlugin = join12(cwd, "antigravity", "plugin.json");
|
|
3096
|
+
if (existsSync12(sourcePlugin)) {
|
|
2869
3097
|
return {
|
|
2870
3098
|
mode: "source",
|
|
2871
|
-
pluginRoot:
|
|
2872
|
-
commandsRoot:
|
|
2873
|
-
runtimeSkillsRoot:
|
|
2874
|
-
adapterDocPath:
|
|
3099
|
+
pluginRoot: join12(cwd, "antigravity"),
|
|
3100
|
+
commandsRoot: join12(cwd, ANTIGRAVITY_COMMANDS_SOURCE_DIR),
|
|
3101
|
+
runtimeSkillsRoot: join12(cwd, RUNTIME_SKILLS_SOURCE_DIR),
|
|
3102
|
+
adapterDocPath: join12(cwd, "assistant-adapters", "antigravity.md")
|
|
2875
3103
|
};
|
|
2876
3104
|
}
|
|
2877
|
-
const installedPlugin =
|
|
2878
|
-
if (
|
|
3105
|
+
const installedPlugin = join12(cwd, ".antigravity", "agent-kit", "plugin.json");
|
|
3106
|
+
if (existsSync12(installedPlugin)) {
|
|
2879
3107
|
return {
|
|
2880
3108
|
mode: "installed",
|
|
2881
|
-
pluginRoot:
|
|
2882
|
-
commandsRoot:
|
|
2883
|
-
runtimeSkillsRoot:
|
|
2884
|
-
adapterDocPath:
|
|
3109
|
+
pluginRoot: join12(cwd, ".antigravity", "agent-kit"),
|
|
3110
|
+
commandsRoot: join12(cwd, ANTIGRAVITY_COMMANDS_TARGET_DIR),
|
|
3111
|
+
runtimeSkillsRoot: join12(cwd, ANTIGRAVITY_RUNTIME_SKILLS_TARGET_DIR),
|
|
3112
|
+
adapterDocPath: join12(cwd, ".antigravity", "agent-kit", "README.md")
|
|
2885
3113
|
};
|
|
2886
3114
|
}
|
|
2887
3115
|
return null;
|
|
@@ -2905,8 +3133,8 @@ function validateAntigravityCommands(layout, findings) {
|
|
|
2905
3133
|
const commandNames = /* @__PURE__ */ new Set();
|
|
2906
3134
|
for (const command of REQUIRED_COMMANDS) {
|
|
2907
3135
|
const relativePath = `${command}.toml`;
|
|
2908
|
-
const path =
|
|
2909
|
-
if (!
|
|
3136
|
+
const path = join12(layout.commandsRoot, relativePath);
|
|
3137
|
+
if (!existsSync12(path)) {
|
|
2910
3138
|
findings.push({
|
|
2911
3139
|
level: "fail",
|
|
2912
3140
|
area: "commands",
|
|
@@ -2915,7 +3143,7 @@ function validateAntigravityCommands(layout, findings) {
|
|
|
2915
3143
|
});
|
|
2916
3144
|
continue;
|
|
2917
3145
|
}
|
|
2918
|
-
const text =
|
|
3146
|
+
const text = readFileSync9(path, "utf8");
|
|
2919
3147
|
addSecretFinding(relativePath, text, findings);
|
|
2920
3148
|
const name = commandField(text, "name");
|
|
2921
3149
|
const description = commandField(text, "description");
|
|
@@ -2972,7 +3200,7 @@ function validateAntigravityCommands(layout, findings) {
|
|
|
2972
3200
|
}
|
|
2973
3201
|
}
|
|
2974
3202
|
function validateAntigravityPlugin(layout, findings) {
|
|
2975
|
-
const pluginPath =
|
|
3203
|
+
const pluginPath = join12(layout.pluginRoot, "plugin.json");
|
|
2976
3204
|
const plugin = readJson(pluginPath);
|
|
2977
3205
|
if (!plugin || !isRecord2(plugin)) {
|
|
2978
3206
|
findings.push({
|
|
@@ -3012,8 +3240,8 @@ function validateAntigravityPlugin(layout, findings) {
|
|
|
3012
3240
|
});
|
|
3013
3241
|
continue;
|
|
3014
3242
|
}
|
|
3015
|
-
const resolved =
|
|
3016
|
-
if (!
|
|
3243
|
+
const resolved = join12(layout.pluginRoot, path);
|
|
3244
|
+
if (!existsSync12(resolved)) {
|
|
3017
3245
|
findings.push({
|
|
3018
3246
|
level: "fail",
|
|
3019
3247
|
area: "manifest",
|
|
@@ -3022,7 +3250,7 @@ function validateAntigravityPlugin(layout, findings) {
|
|
|
3022
3250
|
});
|
|
3023
3251
|
}
|
|
3024
3252
|
}
|
|
3025
|
-
const pluginText =
|
|
3253
|
+
const pluginText = readFileSync9(pluginPath, "utf8");
|
|
3026
3254
|
addSecretFinding("plugin.json", pluginText, findings);
|
|
3027
3255
|
if (Array.isArray(plugin.sourceOfTruth) && plugin.sourceOfTruth.includes("AGENTS.md") && plugin.sourceOfTruth.includes(".agent-kit/agent-roster.json")) {
|
|
3028
3256
|
findings.push({
|
|
@@ -3040,13 +3268,13 @@ function validateAntigravityPlugin(layout, findings) {
|
|
|
3040
3268
|
}
|
|
3041
3269
|
}
|
|
3042
3270
|
function validateRuntimeSkills(cwd, layout, findings) {
|
|
3043
|
-
const canonicalSkillsRoot =
|
|
3271
|
+
const canonicalSkillsRoot = existsSync12(join12(cwd, "skills")) ? join12(cwd, "skills") : join12(cwd, ".agent-kit", "skills");
|
|
3044
3272
|
const canonicalSkillNames = listFilesRecursive(canonicalSkillsRoot).filter((file) => file.endsWith(".md")).map((file) => file.replace(/\.md$/, ""));
|
|
3045
3273
|
const runtimeSkillFiles = listFilesRecursive(layout.runtimeSkillsRoot).filter((file) => file.endsWith("/SKILL.md") || file === "SKILL.md");
|
|
3046
3274
|
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
3275
|
for (const skillName of canonicalSkillNames) {
|
|
3048
|
-
const runtimePath =
|
|
3049
|
-
if (!
|
|
3276
|
+
const runtimePath = join12(layout.runtimeSkillsRoot, skillName, "SKILL.md");
|
|
3277
|
+
if (!existsSync12(runtimePath)) {
|
|
3050
3278
|
findings.push({
|
|
3051
3279
|
level: "fail",
|
|
3052
3280
|
area: "runtime-skills",
|
|
@@ -3055,7 +3283,7 @@ function validateRuntimeSkills(cwd, layout, findings) {
|
|
|
3055
3283
|
});
|
|
3056
3284
|
continue;
|
|
3057
3285
|
}
|
|
3058
|
-
const text =
|
|
3286
|
+
const text = readFileSync9(runtimePath, "utf8");
|
|
3059
3287
|
addSecretFinding(`${skillName}/SKILL.md`, text, findings);
|
|
3060
3288
|
if (!/^---\nname: .+\ndescription: .+\n---/m.test(text)) {
|
|
3061
3289
|
findings.push({
|
|
@@ -3083,7 +3311,7 @@ function validateRuntimeSkills(cwd, layout, findings) {
|
|
|
3083
3311
|
remediation: "Add canonical skills or remove orphan runtime wrappers."
|
|
3084
3312
|
});
|
|
3085
3313
|
}
|
|
3086
|
-
if (canonicalSkillNames.length > 0 && canonicalSkillNames.every((skillName) =>
|
|
3314
|
+
if (canonicalSkillNames.length > 0 && canonicalSkillNames.every((skillName) => existsSync12(join12(layout.runtimeSkillsRoot, skillName, "SKILL.md")))) {
|
|
3087
3315
|
findings.push({
|
|
3088
3316
|
level: "pass",
|
|
3089
3317
|
area: "runtime-skills",
|
|
@@ -3104,7 +3332,7 @@ function validateAntigravity(cwd) {
|
|
|
3104
3332
|
}
|
|
3105
3333
|
]);
|
|
3106
3334
|
}
|
|
3107
|
-
const adapterDoc =
|
|
3335
|
+
const adapterDoc = existsSync12(layout.adapterDocPath) ? readFileSync9(layout.adapterDocPath, "utf8") : "";
|
|
3108
3336
|
if (!adapterDoc) {
|
|
3109
3337
|
findings.push({
|
|
3110
3338
|
level: "fail",
|
|
@@ -3133,7 +3361,7 @@ function validateAntigravity(cwd) {
|
|
|
3133
3361
|
validateAntigravityCommands(layout, findings);
|
|
3134
3362
|
validateRuntimeSkills(cwd, layout, findings);
|
|
3135
3363
|
if (layout.mode === "source") {
|
|
3136
|
-
const packageJson = readJson(
|
|
3364
|
+
const packageJson = readJson(join12(cwd, "package.json"));
|
|
3137
3365
|
const files = isRecord2(packageJson) && Array.isArray(packageJson.files) ? packageJson.files : [];
|
|
3138
3366
|
for (const requiredFile of ["antigravity", "runtime-skills", "assistant-adapters"]) {
|
|
3139
3367
|
if (!files.includes(requiredFile)) {
|
|
@@ -3157,40 +3385,145 @@ function validateAntigravity(cwd) {
|
|
|
3157
3385
|
}
|
|
3158
3386
|
function validateBasicAdapter(cwd, target) {
|
|
3159
3387
|
const findings = [];
|
|
3160
|
-
const
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3388
|
+
const isPackageSource = existsSync12(join12(cwd, "package.json")) && existsSync12(join12(cwd, "src")) && existsSync12(join12(cwd, "templates"));
|
|
3389
|
+
if (isPackageSource) {
|
|
3390
|
+
const sourcePaths = {
|
|
3391
|
+
cursor: [
|
|
3392
|
+
"assistant-adapters/cursor-agent-kit.mdc",
|
|
3393
|
+
"assistant-adapters/model-selection/cursor-model-selection.mdc",
|
|
3394
|
+
"assistant-adapters/cursor-planner.mdc"
|
|
3395
|
+
],
|
|
3396
|
+
claude: ["assistant-adapters/claude-code-subagents.md"],
|
|
3397
|
+
codex: ["assistant-adapters/codex-agents.md", "assistant-adapters/model-selection/codex-config.example.toml"],
|
|
3398
|
+
copilot: ["assistant-adapters/github-copilot-instructions.md", "assistant-adapters/github-next-supabase.instructions.md"]
|
|
3399
|
+
};
|
|
3400
|
+
for (const relativePath of sourcePaths[target]) {
|
|
3401
|
+
const path = join12(cwd, relativePath);
|
|
3402
|
+
if (!existsSync12(path)) {
|
|
3403
|
+
findings.push({
|
|
3404
|
+
level: "fail",
|
|
3405
|
+
area: "adapter",
|
|
3406
|
+
message: `${relativePath} is missing.`,
|
|
3407
|
+
remediation: "Restore the adapter template or run agent-kit update."
|
|
3408
|
+
});
|
|
3409
|
+
continue;
|
|
3410
|
+
}
|
|
3411
|
+
const text = readFileSync9(path, "utf8");
|
|
3412
|
+
addSecretFinding(relativePath, text, findings);
|
|
3413
|
+
if (!text.includes("AGENTS.md") && !text.includes("MODEL_ROUTING.md")) {
|
|
3414
|
+
findings.push({
|
|
3415
|
+
level: "warn",
|
|
3416
|
+
area: "adapter",
|
|
3417
|
+
message: `${relativePath} does not clearly reference Agent Kit source-of-truth files.`,
|
|
3418
|
+
remediation: "Adapter templates should point back to AGENTS.md, MODEL_ROUTING.md, and the roster contract."
|
|
3419
|
+
});
|
|
3420
|
+
}
|
|
3421
|
+
}
|
|
3422
|
+
} else {
|
|
3423
|
+
findings.push(...validateInstalledIdeAdapter(cwd, target).findings);
|
|
3424
|
+
}
|
|
3425
|
+
if (findings.every((finding) => finding.level !== "fail")) {
|
|
3426
|
+
findings.push({
|
|
3427
|
+
level: "pass",
|
|
3428
|
+
area: "adapter",
|
|
3429
|
+
message: `${target} adapter ${isPackageSource ? "templates are present" : "activation assets are present"}.`
|
|
3430
|
+
});
|
|
3431
|
+
}
|
|
3432
|
+
return report(target, findings);
|
|
3433
|
+
}
|
|
3434
|
+
function readAssistantAdaptersDoc(cwd) {
|
|
3435
|
+
const path = join12(cwd, "ASSISTANT_ADAPTERS.md");
|
|
3436
|
+
return existsSync12(path) ? readFileSync9(path, "utf8") : "";
|
|
3437
|
+
}
|
|
3438
|
+
function adaptersRowIsActive(doc, toolLabel) {
|
|
3439
|
+
return assistantAdapterRowIsActive(doc, toolLabel);
|
|
3440
|
+
}
|
|
3441
|
+
function validateInstalledIdeAdapter(cwd, target) {
|
|
3442
|
+
const findings = [];
|
|
3443
|
+
const adaptersDoc = readAssistantAdaptersDoc(cwd);
|
|
3444
|
+
if (target === "cursor") {
|
|
3445
|
+
const rulesPath = join12(cwd, ".cursor/rules/cursor-agent-kit.mdc");
|
|
3446
|
+
if (!existsSync12(rulesPath)) {
|
|
3169
3447
|
findings.push({
|
|
3170
3448
|
level: "fail",
|
|
3171
3449
|
area: "adapter",
|
|
3172
|
-
message:
|
|
3173
|
-
remediation: "
|
|
3450
|
+
message: ".cursor/rules/cursor-agent-kit.mdc is missing.",
|
|
3451
|
+
remediation: "Run agent-kit init or agent-kit init --activate cursor."
|
|
3174
3452
|
});
|
|
3175
|
-
|
|
3453
|
+
} else {
|
|
3454
|
+
addSecretFinding(".cursor/rules/cursor-agent-kit.mdc", readFileSync9(rulesPath, "utf8"), findings);
|
|
3176
3455
|
}
|
|
3177
|
-
const
|
|
3178
|
-
|
|
3179
|
-
|
|
3456
|
+
const plannerAgent = join12(cwd, ".cursor/agents/planner.md");
|
|
3457
|
+
if (existsSync12(plannerAgent)) {
|
|
3458
|
+
findings.push({
|
|
3459
|
+
level: "pass",
|
|
3460
|
+
area: "adapter",
|
|
3461
|
+
message: ".cursor/agents/planner.md is installed."
|
|
3462
|
+
});
|
|
3463
|
+
} else if (adaptersRowIsActive(adaptersDoc, "Cursor")) {
|
|
3180
3464
|
findings.push({
|
|
3181
3465
|
level: "warn",
|
|
3182
3466
|
area: "adapter",
|
|
3183
|
-
message:
|
|
3184
|
-
remediation: "
|
|
3467
|
+
message: "Cursor is marked Active but .cursor/agents/planner.md is missing.",
|
|
3468
|
+
remediation: "Run agent-kit init --activate cursor to generate council subagents from the roster."
|
|
3469
|
+
});
|
|
3470
|
+
}
|
|
3471
|
+
const skillSample = join12(cwd, ".cursor/skills/planning-council/SKILL.md");
|
|
3472
|
+
if (existsSync12(skillSample)) {
|
|
3473
|
+
findings.push({
|
|
3474
|
+
level: "pass",
|
|
3475
|
+
area: "adapter",
|
|
3476
|
+
message: ".cursor/skills/*/SKILL.md project skills are installed."
|
|
3185
3477
|
});
|
|
3186
3478
|
}
|
|
3187
3479
|
}
|
|
3188
|
-
if (
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3480
|
+
if (target === "claude") {
|
|
3481
|
+
const plannerAgent = join12(cwd, ".claude/agents/planner.md");
|
|
3482
|
+
if (!existsSync12(plannerAgent)) {
|
|
3483
|
+
findings.push({
|
|
3484
|
+
level: "fail",
|
|
3485
|
+
area: "adapter",
|
|
3486
|
+
message: ".claude/agents/planner.md is missing.",
|
|
3487
|
+
remediation: "Run agent-kit init --activate claude."
|
|
3488
|
+
});
|
|
3489
|
+
}
|
|
3490
|
+
}
|
|
3491
|
+
if (target === "codex") {
|
|
3492
|
+
const configPath = join12(cwd, ".codex/config.toml");
|
|
3493
|
+
if (!existsSync12(configPath)) {
|
|
3494
|
+
findings.push({
|
|
3495
|
+
level: "fail",
|
|
3496
|
+
area: "adapter",
|
|
3497
|
+
message: ".codex/config.toml is missing.",
|
|
3498
|
+
remediation: "Run agent-kit init --activate codex."
|
|
3499
|
+
});
|
|
3500
|
+
}
|
|
3501
|
+
const plannerAgent = join12(cwd, ".codex/agents/planner.toml");
|
|
3502
|
+
if (existsSync12(plannerAgent)) {
|
|
3503
|
+
findings.push({
|
|
3504
|
+
level: "pass",
|
|
3505
|
+
area: "adapter",
|
|
3506
|
+
message: ".codex/agents/planner.toml is installed."
|
|
3507
|
+
});
|
|
3508
|
+
} else if (adaptersRowIsActive(adaptersDoc, "Codex / AGENTS.md-compatible tools")) {
|
|
3509
|
+
findings.push({
|
|
3510
|
+
level: "warn",
|
|
3511
|
+
area: "adapter",
|
|
3512
|
+
message: "Codex is marked Active but .codex/agents/planner.toml is missing.",
|
|
3513
|
+
remediation: "Run agent-kit init --activate codex to generate council custom agents from the roster."
|
|
3514
|
+
});
|
|
3515
|
+
}
|
|
3516
|
+
}
|
|
3517
|
+
if (target === "copilot") {
|
|
3518
|
+
const instructions = join12(cwd, ".github/copilot-instructions.md");
|
|
3519
|
+
if (!existsSync12(instructions)) {
|
|
3520
|
+
findings.push({
|
|
3521
|
+
level: "fail",
|
|
3522
|
+
area: "adapter",
|
|
3523
|
+
message: ".github/copilot-instructions.md is missing.",
|
|
3524
|
+
remediation: "Run agent-kit init --activate copilot."
|
|
3525
|
+
});
|
|
3526
|
+
}
|
|
3194
3527
|
}
|
|
3195
3528
|
return report(target, findings);
|
|
3196
3529
|
}
|
|
@@ -3207,7 +3540,7 @@ function validateAdapter(cwd, target = "antigravity") {
|
|
|
3207
3540
|
}
|
|
3208
3541
|
function validatePackage(cwd) {
|
|
3209
3542
|
const findings = [];
|
|
3210
|
-
const sourceMode =
|
|
3543
|
+
const sourceMode = existsSync12(join12(cwd, "package.json")) && existsSync12(join12(cwd, "src")) && existsSync12(join12(cwd, "templates"));
|
|
3211
3544
|
if (!sourceMode) {
|
|
3212
3545
|
return report("package", [
|
|
3213
3546
|
{
|
|
@@ -3220,8 +3553,8 @@ function validatePackage(cwd) {
|
|
|
3220
3553
|
}
|
|
3221
3554
|
findings.push(...validateAntigravity(cwd).findings);
|
|
3222
3555
|
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 =
|
|
3556
|
+
const path = join12(cwd, doc);
|
|
3557
|
+
const text = existsSync12(path) ? readFileSync9(path, "utf8") : "";
|
|
3225
3558
|
const lower = text.toLowerCase();
|
|
3226
3559
|
if (!lower.includes("antigravity") && !lower.includes("runtime command") && !lower.includes("runtime adapter")) {
|
|
3227
3560
|
findings.push({
|
|
@@ -3238,7 +3571,7 @@ function validatePackage(cwd) {
|
|
|
3238
3571
|
"examples/next-supabase-installed/.agent-kit/manifest.json",
|
|
3239
3572
|
"examples/next-supabase-installed/audit-output.json"
|
|
3240
3573
|
]) {
|
|
3241
|
-
if (!
|
|
3574
|
+
if (!existsSync12(join12(cwd, examplePath))) {
|
|
3242
3575
|
findings.push({
|
|
3243
3576
|
level: "fail",
|
|
3244
3577
|
area: "examples",
|
|
@@ -3273,17 +3606,17 @@ function validatePackage(cwd) {
|
|
|
3273
3606
|
}
|
|
3274
3607
|
|
|
3275
3608
|
// src/install/diff.ts
|
|
3276
|
-
import { existsSync as
|
|
3277
|
-
import { join as
|
|
3609
|
+
import { existsSync as existsSync13, readFileSync as readFileSync10 } from "fs";
|
|
3610
|
+
import { join as join13 } from "path";
|
|
3278
3611
|
function statusForTextFile(target, template) {
|
|
3279
|
-
if (!
|
|
3280
|
-
const targetHash = sha256(
|
|
3281
|
-
const templateHash = sha256(
|
|
3612
|
+
if (!existsSync13(target)) return "missing";
|
|
3613
|
+
const targetHash = sha256(readFileSync10(target, "utf8"));
|
|
3614
|
+
const templateHash = sha256(readFileSync10(template, "utf8"));
|
|
3282
3615
|
return targetHash === templateHash ? "unchanged" : "changed";
|
|
3283
3616
|
}
|
|
3284
3617
|
function diffProject(cwd, stack = "next-supabase") {
|
|
3285
3618
|
const packageRoot = findPackageRoot();
|
|
3286
|
-
const templateRoot =
|
|
3619
|
+
const templateRoot = join13(packageRoot, "templates", stack);
|
|
3287
3620
|
const libraryFolders = {
|
|
3288
3621
|
missing: [],
|
|
3289
3622
|
present: [],
|
|
@@ -3307,8 +3640,8 @@ function diffProject(cwd, stack = "next-supabase") {
|
|
|
3307
3640
|
}
|
|
3308
3641
|
};
|
|
3309
3642
|
for (const doc of ROOT_DOCS) {
|
|
3310
|
-
const target =
|
|
3311
|
-
const template =
|
|
3643
|
+
const target = join13(cwd, doc);
|
|
3644
|
+
const template = join13(templateRoot, doc);
|
|
3312
3645
|
const status = statusForTextFile(target, template);
|
|
3313
3646
|
if (status === "missing") {
|
|
3314
3647
|
result.missing.push(doc);
|
|
@@ -3321,7 +3654,7 @@ function diffProject(cwd, stack = "next-supabase") {
|
|
|
3321
3654
|
result.preview.wouldWriteConflicts.push(doc);
|
|
3322
3655
|
}
|
|
3323
3656
|
}
|
|
3324
|
-
result.agentRoster = statusForTextFile(
|
|
3657
|
+
result.agentRoster = statusForTextFile(join13(cwd, DEFAULT_AGENT_ROSTER_TARGET), join13(packageRoot, DEFAULT_AGENT_ROSTER_SOURCE));
|
|
3325
3658
|
if (result.agentRoster === "missing") {
|
|
3326
3659
|
result.preview.wouldCreate.push(DEFAULT_AGENT_ROSTER_TARGET);
|
|
3327
3660
|
result.preview.wouldCreateAgentRoster = true;
|
|
@@ -3330,7 +3663,7 @@ function diffProject(cwd, stack = "next-supabase") {
|
|
|
3330
3663
|
result.preview.wouldWriteConflicts.push(DEFAULT_AGENT_ROSTER_TARGET);
|
|
3331
3664
|
result.preview.wouldWriteAgentRosterConflict = true;
|
|
3332
3665
|
}
|
|
3333
|
-
result.modelRouting = statusForTextFile(
|
|
3666
|
+
result.modelRouting = statusForTextFile(join13(cwd, DEFAULT_MODEL_ROUTING_TARGET), join13(packageRoot, DEFAULT_MODEL_ROUTING_SOURCE));
|
|
3334
3667
|
if (result.modelRouting === "missing") {
|
|
3335
3668
|
result.preview.wouldCreate.push(DEFAULT_MODEL_ROUTING_TARGET);
|
|
3336
3669
|
result.preview.wouldCreateModelRouting = true;
|
|
@@ -3340,8 +3673,8 @@ function diffProject(cwd, stack = "next-supabase") {
|
|
|
3340
3673
|
result.preview.wouldWriteModelRoutingConflict = true;
|
|
3341
3674
|
}
|
|
3342
3675
|
for (const folder of LIBRARY_FOLDERS) {
|
|
3343
|
-
const target =
|
|
3344
|
-
if (
|
|
3676
|
+
const target = join13(cwd, ".agent-kit", folder);
|
|
3677
|
+
if (existsSync13(target)) libraryFolders.present.push(folder);
|
|
3345
3678
|
else libraryFolders.missing.push(folder);
|
|
3346
3679
|
}
|
|
3347
3680
|
return result;
|
|
@@ -3349,8 +3682,8 @@ function diffProject(cwd, stack = "next-supabase") {
|
|
|
3349
3682
|
|
|
3350
3683
|
// src/research/discover.ts
|
|
3351
3684
|
import { Octokit } from "@octokit/rest";
|
|
3352
|
-
import { readFileSync as
|
|
3353
|
-
import { join as
|
|
3685
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
3686
|
+
import { join as join14 } from "path";
|
|
3354
3687
|
|
|
3355
3688
|
// src/research/config.ts
|
|
3356
3689
|
import { z as z2 } from "zod";
|
|
@@ -3375,8 +3708,8 @@ var researchConfigSchema = z2.object({
|
|
|
3375
3708
|
// src/research/discover.ts
|
|
3376
3709
|
async function discoverRepos(options) {
|
|
3377
3710
|
const packageRoot = findPackageRoot();
|
|
3378
|
-
const configPath =
|
|
3379
|
-
const config = researchConfigSchema.parse(JSON.parse(
|
|
3711
|
+
const configPath = join14(packageRoot, "research", "scan-config.json");
|
|
3712
|
+
const config = researchConfigSchema.parse(JSON.parse(readFileSync11(configPath, "utf8")));
|
|
3380
3713
|
const token = options.token ?? process.env.GITHUB_TOKEN;
|
|
3381
3714
|
if (!token) {
|
|
3382
3715
|
throw new Error("GITHUB_TOKEN is required for GitHub API research discovery.");
|
|
@@ -3437,20 +3770,20 @@ async function discoverRepos(options) {
|
|
|
3437
3770
|
}
|
|
3438
3771
|
}
|
|
3439
3772
|
const candidates = [...deduped.values()].slice(0, maxRepos);
|
|
3440
|
-
const output = options.output ??
|
|
3773
|
+
const output = options.output ?? join14(options.cwd, "research", "repo-candidates.json");
|
|
3441
3774
|
writeText(output, `${JSON.stringify(candidates, null, 2)}
|
|
3442
3775
|
`);
|
|
3443
3776
|
return candidates;
|
|
3444
3777
|
}
|
|
3445
3778
|
|
|
3446
3779
|
// src/research/scan.ts
|
|
3447
|
-
import { existsSync as
|
|
3448
|
-
import { join as
|
|
3780
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync2, readFileSync as readFileSync13, rmSync } from "fs";
|
|
3781
|
+
import { join as join16 } from "path";
|
|
3449
3782
|
import { simpleGit } from "simple-git";
|
|
3450
3783
|
|
|
3451
3784
|
// src/research/analyze.ts
|
|
3452
|
-
import { existsSync as
|
|
3453
|
-
import { join as
|
|
3785
|
+
import { existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
|
|
3786
|
+
import { join as join15 } from "path";
|
|
3454
3787
|
function normalizeRelativePath(file) {
|
|
3455
3788
|
return file.replace(/\\/g, "/");
|
|
3456
3789
|
}
|
|
@@ -3458,8 +3791,8 @@ function hasFile(files, matcher) {
|
|
|
3458
3791
|
return files.some((file) => matcher.test(normalizeRelativePath(file)));
|
|
3459
3792
|
}
|
|
3460
3793
|
function fileText(root, file) {
|
|
3461
|
-
const path =
|
|
3462
|
-
return
|
|
3794
|
+
const path = join15(root, file);
|
|
3795
|
+
return existsSync15(path) ? readFileSync12(path, "utf8") : "";
|
|
3463
3796
|
}
|
|
3464
3797
|
function textIncludes(root, files, matcher, terms) {
|
|
3465
3798
|
const lowerTerms = terms.map((term) => term.toLowerCase());
|
|
@@ -3590,24 +3923,24 @@ ${finding.impactOnKit.map((item) => `- ${item}`).join("\n")}
|
|
|
3590
3923
|
`;
|
|
3591
3924
|
}
|
|
3592
3925
|
async function scanRepos(options) {
|
|
3593
|
-
const candidatesPath = options.candidatesPath ??
|
|
3594
|
-
if (!
|
|
3926
|
+
const candidatesPath = options.candidatesPath ?? join16(options.cwd, "research", "repo-candidates.json");
|
|
3927
|
+
if (!existsSync16(candidatesPath)) {
|
|
3595
3928
|
throw new Error(`Candidates file not found: ${candidatesPath}`);
|
|
3596
3929
|
}
|
|
3597
|
-
const candidates = JSON.parse(
|
|
3598
|
-
const workdir = options.workdir ??
|
|
3930
|
+
const candidates = JSON.parse(readFileSync13(candidatesPath, "utf8"));
|
|
3931
|
+
const workdir = options.workdir ?? join16(options.cwd, "research", "workdir");
|
|
3599
3932
|
mkdirSync2(workdir, { recursive: true });
|
|
3600
|
-
mkdirSync2(
|
|
3933
|
+
mkdirSync2(join16(options.cwd, "research", "findings"), { recursive: true });
|
|
3601
3934
|
const findings = [];
|
|
3602
3935
|
const git = simpleGit();
|
|
3603
3936
|
for (const candidate of candidates) {
|
|
3604
3937
|
const repoSlug = candidate.fullName.replace("/", "__");
|
|
3605
|
-
const repoPath =
|
|
3606
|
-
if (
|
|
3938
|
+
const repoPath = join16(workdir, repoSlug);
|
|
3939
|
+
if (existsSync16(repoPath)) rmSync(repoPath, { recursive: true, force: true });
|
|
3607
3940
|
await git.raw(["clone", "--depth", "1", candidate.htmlUrl, repoPath]);
|
|
3608
3941
|
const finding = analyzeRepository(candidate, repoPath);
|
|
3609
3942
|
findings.push(finding);
|
|
3610
|
-
writeText(
|
|
3943
|
+
writeText(join16(options.cwd, "research", "findings", `${repoSlug}.md`), findingToMarkdown(finding));
|
|
3611
3944
|
if (!options.keepClones) {
|
|
3612
3945
|
rmSync(repoPath, { recursive: true, force: true });
|
|
3613
3946
|
}
|
|
@@ -3616,8 +3949,8 @@ async function scanRepos(options) {
|
|
|
3616
3949
|
}
|
|
3617
3950
|
|
|
3618
3951
|
// src/research/summarize.ts
|
|
3619
|
-
import { existsSync as
|
|
3620
|
-
import { join as
|
|
3952
|
+
import { existsSync as existsSync17, readFileSync as readFileSync14, readdirSync as readdirSync3 } from "fs";
|
|
3953
|
+
import { join as join17 } from "path";
|
|
3621
3954
|
var SUMMARY_TARGETS = {
|
|
3622
3955
|
"nextjs-patterns": {
|
|
3623
3956
|
title: "Next.js Patterns",
|
|
@@ -3720,12 +4053,12 @@ function renderRepoList(findings, scoreKeys) {
|
|
|
3720
4053
|
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
4054
|
}
|
|
3722
4055
|
function summarizeFindings(cwd) {
|
|
3723
|
-
const findingsDir =
|
|
3724
|
-
if (!
|
|
4056
|
+
const findingsDir = join17(cwd, "research", "findings");
|
|
4057
|
+
if (!existsSync17(findingsDir)) {
|
|
3725
4058
|
throw new Error("No research/findings directory exists. Run agent-kit research scan first.");
|
|
3726
4059
|
}
|
|
3727
|
-
const findingFiles =
|
|
3728
|
-
const findings = findingFiles.map((file) => parseFinding(file,
|
|
4060
|
+
const findingFiles = readdirSync3(findingsDir).filter((file) => file.endsWith(".md"));
|
|
4061
|
+
const findings = findingFiles.map((file) => parseFinding(file, readFileSync14(join17(findingsDir, file), "utf8"))).filter((finding) => finding !== null);
|
|
3729
4062
|
const categoryCounts = countBy(findings.map((finding) => finding.category));
|
|
3730
4063
|
const outputs = [];
|
|
3731
4064
|
const overview = `# Research Scan Overview
|
|
@@ -3744,13 +4077,13 @@ ${countBy(findings.flatMap((finding) => finding.strongPractices)).slice(0, 12).m
|
|
|
3744
4077
|
## Most Repeated Gaps
|
|
3745
4078
|
${countBy(findings.flatMap((finding) => finding.weakPractices)).slice(0, 12).map(([practice, count]) => `- ${practice} (${count})`).join("\n")}
|
|
3746
4079
|
`;
|
|
3747
|
-
const overviewPath =
|
|
4080
|
+
const overviewPath = join17(cwd, "research", "summaries", "scan-overview.md");
|
|
3748
4081
|
writeText(overviewPath, overview);
|
|
3749
4082
|
outputs.push(overviewPath);
|
|
3750
4083
|
for (const [target, config] of Object.entries(SUMMARY_TARGETS)) {
|
|
3751
4084
|
const categories = config.categories;
|
|
3752
4085
|
const scopedFindings = findings.filter((finding) => categories.includes(finding.category));
|
|
3753
|
-
const path =
|
|
4086
|
+
const path = join17(cwd, "research", "summaries", `${target}.md`);
|
|
3754
4087
|
const summary2 = `# ${config.title}
|
|
3755
4088
|
|
|
3756
4089
|
Generated from ${scopedFindings.length} relevant repository findings.
|
|
@@ -3791,7 +4124,7 @@ Review the generated research summaries, then convert repeated best practices in
|
|
|
3791
4124
|
|
|
3792
4125
|
Do not copy source code from scanned repositories. Adopt only generalized practices with clear rationale.
|
|
3793
4126
|
`;
|
|
3794
|
-
const path =
|
|
4127
|
+
const path = join17(cwd, "research", "proposed-updates.md");
|
|
3795
4128
|
writeText(path, output);
|
|
3796
4129
|
return path;
|
|
3797
4130
|
}
|
|
@@ -3900,8 +4233,8 @@ function proposeCorrectionUpstream(cwd, id) {
|
|
|
3900
4233
|
}
|
|
3901
4234
|
|
|
3902
4235
|
// src/studio/session.ts
|
|
3903
|
-
import { existsSync as
|
|
3904
|
-
import { join as
|
|
4236
|
+
import { existsSync as existsSync18, readFileSync as readFileSync15, readdirSync as readdirSync4, statSync as statSync2 } from "fs";
|
|
4237
|
+
import { join as join18 } from "path";
|
|
3905
4238
|
function sessionDir(sessionId) {
|
|
3906
4239
|
return `${COUNCIL_SESSIONS_DIR}/${safeSlug(sessionId)}`;
|
|
3907
4240
|
}
|
|
@@ -3958,9 +4291,9 @@ function startSession(cwd, options) {
|
|
|
3958
4291
|
return { sessionId, sessionPath: sessionDir(sessionId) };
|
|
3959
4292
|
}
|
|
3960
4293
|
function listSessions(cwd) {
|
|
3961
|
-
const root =
|
|
3962
|
-
if (!
|
|
3963
|
-
return
|
|
4294
|
+
const root = join18(cwd, COUNCIL_SESSIONS_DIR);
|
|
4295
|
+
if (!existsSync18(root)) return [];
|
|
4296
|
+
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
4297
|
}
|
|
3965
4298
|
function getActiveSessionId(cwd) {
|
|
3966
4299
|
const active = readTextFile(cwd, ACTIVE_SESSION_FILE)?.trim();
|
|
@@ -4658,13 +4991,13 @@ function parseSetupFormPayload(raw) {
|
|
|
4658
4991
|
}
|
|
4659
4992
|
|
|
4660
4993
|
// src/studio/wizard/checklist.ts
|
|
4661
|
-
import { existsSync as
|
|
4662
|
-
import { join as
|
|
4994
|
+
import { existsSync as existsSync19, readFileSync as readFileSync16 } from "fs";
|
|
4995
|
+
import { join as join19 } from "path";
|
|
4663
4996
|
var IDE_PATHS = {
|
|
4664
|
-
cursor: ".cursor/
|
|
4997
|
+
cursor: ".cursor/agents/planner.md",
|
|
4665
4998
|
copilot: ".github/copilot-instructions.md",
|
|
4666
|
-
claude: ".claude/agents/",
|
|
4667
|
-
codex: "
|
|
4999
|
+
claude: ".claude/agents/planner.md",
|
|
5000
|
+
codex: ".codex/agents/planner.toml",
|
|
4668
5001
|
other: "ASSISTANT_ADAPTERS.md"
|
|
4669
5002
|
};
|
|
4670
5003
|
function saveIdeChecklist(cwd, ideSurface) {
|
|
@@ -4679,10 +5012,13 @@ function saveIdeChecklist(cwd, ideSurface) {
|
|
|
4679
5012
|
}
|
|
4680
5013
|
function detectIdeRulePresent(cwd, ideSurface) {
|
|
4681
5014
|
const rel = IDE_PATHS[ideSurface];
|
|
5015
|
+
if (ideSurface === "cursor") {
|
|
5016
|
+
return existsSync19(join19(cwd, rel)) || existsSync19(join19(cwd, ".cursor/rules/cursor-agent-kit.mdc"));
|
|
5017
|
+
}
|
|
4682
5018
|
if (rel.endsWith("/")) {
|
|
4683
|
-
return
|
|
5019
|
+
return existsSync19(join19(cwd, rel));
|
|
4684
5020
|
}
|
|
4685
|
-
return
|
|
5021
|
+
return existsSync19(join19(cwd, rel));
|
|
4686
5022
|
}
|
|
4687
5023
|
var VISUAL_QA_MARKER = "## Visual QA Tier";
|
|
4688
5024
|
var VISUAL_QA_BLOCKS = {
|
|
@@ -4710,11 +5046,11 @@ This project uses the **Mature** visual QA tier.
|
|
|
4710
5046
|
};
|
|
4711
5047
|
function writeVisualQaTier(cwd, tier) {
|
|
4712
5048
|
const path = "TESTING.md";
|
|
4713
|
-
const fullPath =
|
|
4714
|
-
if (!
|
|
5049
|
+
const fullPath = join19(cwd, path);
|
|
5050
|
+
if (!existsSync19(fullPath)) {
|
|
4715
5051
|
return { updated: false, path, reason: "TESTING.md not found in project root." };
|
|
4716
5052
|
}
|
|
4717
|
-
const current =
|
|
5053
|
+
const current = readFileSync16(fullPath, "utf8");
|
|
4718
5054
|
if (current.includes(VISUAL_QA_MARKER)) {
|
|
4719
5055
|
return {
|
|
4720
5056
|
updated: false,
|
|
@@ -4890,8 +5226,8 @@ function extractSetupFormFromWizardForm(form) {
|
|
|
4890
5226
|
}
|
|
4891
5227
|
|
|
4892
5228
|
// src/studio/wizard/drafts.ts
|
|
4893
|
-
import { existsSync as
|
|
4894
|
-
import { join as
|
|
5229
|
+
import { existsSync as existsSync20, readFileSync as readFileSync17 } from "fs";
|
|
5230
|
+
import { join as join20 } from "path";
|
|
4895
5231
|
var DESIGN_DRAFT_JSON = ".agent-kit/onboarding/design-draft.json";
|
|
4896
5232
|
var MESSAGING_DRAFT_JSON = ".agent-kit/onboarding/messaging-draft.json";
|
|
4897
5233
|
function loadDesignDraft(cwd) {
|
|
@@ -4936,11 +5272,11 @@ function previewMessagingMarkdown(draft) {
|
|
|
4936
5272
|
`;
|
|
4937
5273
|
}
|
|
4938
5274
|
function appendSectionToDoc(cwd, doc, sectionMarkdown) {
|
|
4939
|
-
const fullPath =
|
|
4940
|
-
if (!
|
|
5275
|
+
const fullPath = join20(cwd, doc);
|
|
5276
|
+
if (!existsSync20(fullPath)) {
|
|
4941
5277
|
return { target: doc, action: "missing" };
|
|
4942
5278
|
}
|
|
4943
|
-
const current =
|
|
5279
|
+
const current = readFileSync17(fullPath, "utf8");
|
|
4944
5280
|
if (current.includes("(wizard draft)")) {
|
|
4945
5281
|
return { target: doc, action: "conflict", conflictPath: `.agent-kit/conflicts/wizard-${doc}` };
|
|
4946
5282
|
}
|
|
@@ -4979,8 +5315,8 @@ function applyDrafts(cwd) {
|
|
|
4979
5315
|
}
|
|
4980
5316
|
|
|
4981
5317
|
// src/studio/office/render.ts
|
|
4982
|
-
import { readFileSync as
|
|
4983
|
-
import { join as
|
|
5318
|
+
import { readFileSync as readFileSync18 } from "fs";
|
|
5319
|
+
import { join as join21 } from "path";
|
|
4984
5320
|
|
|
4985
5321
|
// src/studio/office/map.ts
|
|
4986
5322
|
var MAP_WIDTH = 28;
|
|
@@ -5114,12 +5450,12 @@ var PRODUCT_CATEGORIES = [
|
|
|
5114
5450
|
var TENANT_MODELS = ["single-user", "team", "tenant", "marketplace", "admin", "public-content"];
|
|
5115
5451
|
function readOfficeAsset(name) {
|
|
5116
5452
|
const root = findPackageRoot();
|
|
5117
|
-
const distPath =
|
|
5118
|
-
const srcPath =
|
|
5453
|
+
const distPath = join21(root, "dist", "studio", "office", "assets", name);
|
|
5454
|
+
const srcPath = join21(root, "src", "studio", "office", "assets", name);
|
|
5119
5455
|
try {
|
|
5120
|
-
return
|
|
5456
|
+
return readFileSync18(distPath, "utf8");
|
|
5121
5457
|
} catch {
|
|
5122
|
-
return
|
|
5458
|
+
return readFileSync18(srcPath, "utf8");
|
|
5123
5459
|
}
|
|
5124
5460
|
}
|
|
5125
5461
|
function buildOfficeBootConfig(cwd, viewModel) {
|
|
@@ -5286,8 +5622,8 @@ function allAgentBriefsComplete(form, agentIds) {
|
|
|
5286
5622
|
}
|
|
5287
5623
|
|
|
5288
5624
|
// src/studio/wizard/render.ts
|
|
5289
|
-
import { readFileSync as
|
|
5290
|
-
import { join as
|
|
5625
|
+
import { readFileSync as readFileSync19 } from "fs";
|
|
5626
|
+
import { join as join22 } from "path";
|
|
5291
5627
|
var PRODUCT_CATEGORIES2 = [
|
|
5292
5628
|
"content-app",
|
|
5293
5629
|
"saas",
|
|
@@ -5304,12 +5640,12 @@ var PRODUCT_CATEGORIES2 = [
|
|
|
5304
5640
|
var TENANT_MODELS2 = ["single-user", "team", "tenant", "marketplace", "admin", "public-content"];
|
|
5305
5641
|
function readWizardAsset(name) {
|
|
5306
5642
|
const root = findPackageRoot();
|
|
5307
|
-
const distPath =
|
|
5308
|
-
const srcPath =
|
|
5643
|
+
const distPath = join22(root, "dist", "studio", "wizard", "assets", name);
|
|
5644
|
+
const srcPath = join22(root, "src", "studio", "wizard", "assets", name);
|
|
5309
5645
|
try {
|
|
5310
|
-
return
|
|
5646
|
+
return readFileSync19(distPath, "utf8");
|
|
5311
5647
|
} catch {
|
|
5312
|
-
return
|
|
5648
|
+
return readFileSync19(srcPath, "utf8");
|
|
5313
5649
|
}
|
|
5314
5650
|
}
|
|
5315
5651
|
function mergeWizardSteps(cwd) {
|
|
@@ -5574,9 +5910,20 @@ async function handleRequest(cwd, request, response) {
|
|
|
5574
5910
|
try {
|
|
5575
5911
|
const body = await readJsonBody(request);
|
|
5576
5912
|
if (!body.ideSurface) throw new Error("ideSurface is required.");
|
|
5913
|
+
const activateTarget = ideSurfaceToActivateTarget(body.ideSurface);
|
|
5914
|
+
let activation;
|
|
5915
|
+
if (activateTarget) {
|
|
5916
|
+
const activateResult = activateIdeTargets({ cwd, targets: [activateTarget] });
|
|
5917
|
+
activation = {
|
|
5918
|
+
activated: activateResult.activated,
|
|
5919
|
+
copied: activateResult.copied,
|
|
5920
|
+
unchanged: activateResult.unchanged,
|
|
5921
|
+
conflicts: activateResult.conflicts
|
|
5922
|
+
};
|
|
5923
|
+
}
|
|
5577
5924
|
const result = saveIdeChecklist(cwd, body.ideSurface);
|
|
5578
5925
|
markSectionComplete(cwd, "ide");
|
|
5579
|
-
sendJson(response, 200, { ...result, ...buildStatePayload(cwd) });
|
|
5926
|
+
sendJson(response, 200, { ...result, activation, ...buildStatePayload(cwd) });
|
|
5580
5927
|
} catch (error) {
|
|
5581
5928
|
sendJson(response, 400, { error: error instanceof Error ? error.message : String(error) });
|
|
5582
5929
|
}
|
|
@@ -5689,7 +6036,7 @@ async function startSetupServer(options) {
|
|
|
5689
6036
|
// src/studio/studio-server.ts
|
|
5690
6037
|
import { watch } from "fs";
|
|
5691
6038
|
import { createServer as createServer2 } from "http";
|
|
5692
|
-
import { join as
|
|
6039
|
+
import { join as join23 } from "path";
|
|
5693
6040
|
var DEFAULT_PORT2 = 9331;
|
|
5694
6041
|
var DEFAULT_HOST2 = "127.0.0.1";
|
|
5695
6042
|
var sseClients = /* @__PURE__ */ new Set();
|
|
@@ -5731,7 +6078,7 @@ function stopWatcher() {
|
|
|
5731
6078
|
}
|
|
5732
6079
|
}
|
|
5733
6080
|
function watchSessionEvents(cwd, sessionId) {
|
|
5734
|
-
const eventsPath2 =
|
|
6081
|
+
const eventsPath2 = join23(cwd, COUNCIL_SESSIONS_DIR, sessionId, "events.jsonl");
|
|
5735
6082
|
if (watchedEventsPath === eventsPath2 && activeWatcher) return;
|
|
5736
6083
|
stopWatcher();
|
|
5737
6084
|
watchedEventsPath = eventsPath2;
|
|
@@ -5887,8 +6234,8 @@ async function startStudioServer(options) {
|
|
|
5887
6234
|
}
|
|
5888
6235
|
|
|
5889
6236
|
// src/studio/session-checkpoint.ts
|
|
5890
|
-
import { existsSync as
|
|
5891
|
-
import { extname, join as
|
|
6237
|
+
import { existsSync as existsSync21, readFileSync as readFileSync20 } from "fs";
|
|
6238
|
+
import { extname, join as join24 } from "path";
|
|
5892
6239
|
function parseCheckpointMarkdown(content) {
|
|
5893
6240
|
const payload = { notes: [], decisions: [], handoffs: [], outputs: [] };
|
|
5894
6241
|
const sections = content.split(/^## /m).slice(1);
|
|
@@ -5944,7 +6291,7 @@ function parseCheckpointMarkdown(content) {
|
|
|
5944
6291
|
return payload;
|
|
5945
6292
|
}
|
|
5946
6293
|
function parseCheckpointFile(filePath) {
|
|
5947
|
-
const content =
|
|
6294
|
+
const content = readFileSync20(filePath, "utf8");
|
|
5948
6295
|
const ext = extname(filePath).toLowerCase();
|
|
5949
6296
|
if (ext === ".json") {
|
|
5950
6297
|
const parsed = JSON.parse(content);
|
|
@@ -6020,8 +6367,8 @@ function applySessionCheckpoint(cwd, payload) {
|
|
|
6020
6367
|
};
|
|
6021
6368
|
}
|
|
6022
6369
|
function checkpointSessionFromFile(cwd, filePath) {
|
|
6023
|
-
const absolute =
|
|
6024
|
-
if (!
|
|
6370
|
+
const absolute = join24(cwd, filePath);
|
|
6371
|
+
if (!existsSync21(absolute)) throw new Error(`Checkpoint file not found: ${filePath}`);
|
|
6025
6372
|
return applySessionCheckpoint(cwd, parseCheckpointFile(absolute));
|
|
6026
6373
|
}
|
|
6027
6374
|
|