@dadado/agent-kit-cli 4.7.1 → 4.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +619 -235
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { defineCommand as defineCommand13, runMain } from "citty";
4
+ import { defineCommand as defineCommand14, runMain } from "citty";
5
5
 
6
6
  // src/commands/add.ts
7
7
  import { defineCommand } from "citty";
@@ -974,6 +974,22 @@ var L0_ARTIFACTS = [
974
974
  source: ".cursor/commands/start-project.md",
975
975
  target: ".cursor/commands/start-project.md"
976
976
  },
977
+ {
978
+ source: ".cursor/commands/backlog-add.md",
979
+ target: ".cursor/commands/backlog-add.md"
980
+ },
981
+ {
982
+ source: ".cursor/commands/backlog-edit.md",
983
+ target: ".cursor/commands/backlog-edit.md"
984
+ },
985
+ {
986
+ source: ".cursor/commands/backlog-delete.md",
987
+ target: ".cursor/commands/backlog-delete.md"
988
+ },
989
+ {
990
+ source: ".cursor/commands/backlog-cancel.md",
991
+ target: ".cursor/commands/backlog-cancel.md"
992
+ },
977
993
  {
978
994
  source: ".cursor/commands/agent-kit-onboard.md",
979
995
  target: ".cursor/commands/agent-kit-onboard.md"
@@ -986,6 +1002,14 @@ var L0_ARTIFACTS = [
986
1002
  source: ".cursor/commands/run-plan.md",
987
1003
  target: ".cursor/commands/run-plan.md"
988
1004
  },
1005
+ {
1006
+ source: ".cursor/commands/hotfix.md",
1007
+ target: ".cursor/commands/hotfix.md"
1008
+ },
1009
+ {
1010
+ source: ".cursor/commands/run-plan-all.md",
1011
+ target: ".cursor/commands/run-plan-all.md"
1012
+ },
989
1013
  // Deprecated aliases of /run-plan (kept so existing references keep working)
990
1014
  {
991
1015
  source: ".cursor/commands/run-plan-loop.md",
@@ -1014,6 +1038,10 @@ var L0_ARTIFACTS = [
1014
1038
  source: ".cursor/commands/plan-review-triage.md",
1015
1039
  target: ".cursor/commands/plan-review-triage.md"
1016
1040
  },
1041
+ {
1042
+ source: ".cursor/commands/field-report-resolve.md",
1043
+ target: ".cursor/commands/field-report-resolve.md"
1044
+ },
1017
1045
  // Context (templates + example config; private config.json is not L0)
1018
1046
  {
1019
1047
  source: ".cursor/context/templates/plan-external-review-prompt.md",
@@ -1047,11 +1075,19 @@ var L0_ARTIFACTS = [
1047
1075
  source: ".cursor/context/config.example.json",
1048
1076
  target: ".cursor/context/config.example.json"
1049
1077
  },
1050
- // Scripts (canonical launcher; consumers never receive repo-root scripts/)
1078
+ // Scripts (canonical launchers; consumers never receive repo-root scripts/)
1051
1079
  {
1052
1080
  source: ".cursor/scripts/plan-external-review.sh",
1053
1081
  target: ".cursor/scripts/plan-external-review.sh"
1054
1082
  },
1083
+ {
1084
+ source: ".cursor/scripts/field-report-cadence-bump.sh",
1085
+ target: ".cursor/scripts/field-report-cadence-bump.sh"
1086
+ },
1087
+ {
1088
+ source: ".cursor/scripts/run-plan-all-consolidate.sh",
1089
+ target: ".cursor/scripts/run-plan-all-consolidate.sh"
1090
+ },
1055
1091
  // Secrets gate (structural)
1056
1092
  {
1057
1093
  source: ".cursor/hooks/pre-commit/check-secrets.sh",
@@ -1354,6 +1390,12 @@ var contributeCommand = defineCommand2({
1354
1390
  }
1355
1391
  });
1356
1392
 
1393
+ // src/commands/dashboard-broadcast.ts
1394
+ import { spawn as spawn2 } from "child_process";
1395
+ import { access as access3 } from "fs/promises";
1396
+ import path10 from "path";
1397
+ import { defineCommand as defineCommand4 } from "citty";
1398
+
1357
1399
  // src/commands/dashboard.ts
1358
1400
  import { spawn } from "child_process";
1359
1401
  import { access as access2 } from "fs/promises";
@@ -1417,12 +1459,72 @@ var dashboardCommand = defineCommand3({
1417
1459
  }
1418
1460
  });
1419
1461
 
1462
+ // src/commands/dashboard-broadcast.ts
1463
+ async function findDashboardBroadcastStart(cwd) {
1464
+ let dir = path10.resolve(cwd);
1465
+ for (; ; ) {
1466
+ const candidate2 = path10.join(dir, "dashboard", "start-broadcast.mjs");
1467
+ try {
1468
+ await access3(candidate2);
1469
+ return candidate2;
1470
+ } catch {
1471
+ }
1472
+ const parent = path10.dirname(dir);
1473
+ if (parent === dir) return null;
1474
+ dir = parent;
1475
+ }
1476
+ }
1477
+ function runStartScript2(startPath, env) {
1478
+ return new Promise((resolve, reject) => {
1479
+ const child = spawn2(process.execPath, [startPath], {
1480
+ cwd: path10.dirname(path10.dirname(startPath)),
1481
+ env,
1482
+ stdio: "inherit"
1483
+ });
1484
+ child.on("error", reject);
1485
+ child.on("close", (code) => resolve(code ?? 1));
1486
+ });
1487
+ }
1488
+ var dashboardBroadcastCommand = defineCommand4({
1489
+ meta: {
1490
+ name: "dashboard-broadcast",
1491
+ description: "Opt-in LAN Mission Control (token-gated). Terminal counterpart to /dashboard-broadcast. Does not change loopback /dashboard."
1492
+ },
1493
+ args: {
1494
+ cwd: {
1495
+ type: "string",
1496
+ default: process.cwd(),
1497
+ description: "Directory to search upward for dashboard/start-broadcast.mjs"
1498
+ },
1499
+ "no-open": {
1500
+ type: "boolean",
1501
+ default: false,
1502
+ description: "Do not open a browser; only ensure the server is up and print LAN URL + token"
1503
+ }
1504
+ },
1505
+ async run({ args }) {
1506
+ const startPath = await findDashboardBroadcastStart(args.cwd);
1507
+ if (!startPath) {
1508
+ const loopback = await findDashboardStart(args.cwd);
1509
+ logger.error(
1510
+ loopback ? "No dashboard/start-broadcast.mjs found beside dashboard/start.mjs. Update the agent-kit tree, then retry." : "No dashboard/start-broadcast.mjs found. Mission Control ships with the agent-kit repo; run from that tree."
1511
+ );
1512
+ process.exitCode = 1;
1513
+ return;
1514
+ }
1515
+ const env = { ...process.env };
1516
+ if (args["no-open"]) env.MISSION_CONTROL_NO_OPEN = "1";
1517
+ const code = await runStartScript2(startPath, env);
1518
+ if (code !== 0) process.exitCode = code;
1519
+ }
1520
+ });
1521
+
1420
1522
  // src/commands/diff.ts
1421
- import { defineCommand as defineCommand4 } from "citty";
1523
+ import { defineCommand as defineCommand5 } from "citty";
1422
1524
 
1423
1525
  // src/lifecycle/diff.ts
1424
1526
  import { readFile as readFile5 } from "fs/promises";
1425
- import path10 from "path";
1527
+ import path11 from "path";
1426
1528
  async function readIfExists(abs) {
1427
1529
  try {
1428
1530
  return await readFile5(abs, "utf8");
@@ -1431,7 +1533,7 @@ async function readIfExists(abs) {
1431
1533
  }
1432
1534
  }
1433
1535
  async function comparePair(registryRoot, projectRoot, sourceRel, targetRel, protectedGlobs) {
1434
- const posixTarget = targetRel.split(path10.sep).join("/");
1536
+ const posixTarget = targetRel.split(path11.sep).join("/");
1435
1537
  if (isProtectedPath(posixTarget, protectedGlobs)) {
1436
1538
  return { path: posixTarget, status: "protected" };
1437
1539
  }
@@ -1461,7 +1563,7 @@ async function diffAgainstRegistry(registryRoot, projectRoot, manifest) {
1461
1563
  const entries = [];
1462
1564
  const seen = /* @__PURE__ */ new Set();
1463
1565
  const pushUnique = async (sourceRel, targetRel) => {
1464
- const key = targetRel.split(path10.sep).join("/");
1566
+ const key = targetRel.split(path11.sep).join("/");
1465
1567
  if (seen.has(key)) return;
1466
1568
  seen.add(key);
1467
1569
  entries.push(
@@ -1488,8 +1590,8 @@ async function diffAgainstRegistry(registryRoot, projectRoot, manifest) {
1488
1590
  continue;
1489
1591
  }
1490
1592
  const category = skill.path.includes("/core/") ? "core" : "community";
1491
- const sourceRel = path10.posix.join(skill.path, "SKILL.md");
1492
- const targetRel = path10.posix.join(".cursor", "skills", category, skill.id, "SKILL.md");
1593
+ const sourceRel = path11.posix.join(skill.path, "SKILL.md");
1594
+ const targetRel = path11.posix.join(".cursor", "skills", category, skill.id, "SKILL.md");
1493
1595
  await pushUnique(sourceRel, targetRel);
1494
1596
  }
1495
1597
  }
@@ -1508,7 +1610,7 @@ function summarizeDiff(entries) {
1508
1610
  }
1509
1611
 
1510
1612
  // src/commands/diff.ts
1511
- var diffCommand = defineCommand4({
1613
+ var diffCommand = defineCommand5({
1512
1614
  meta: {
1513
1615
  name: "diff",
1514
1616
  description: "Compare installed kit artifacts to the registry (respects L3 protected)."
@@ -1557,8 +1659,8 @@ var diffCommand = defineCommand4({
1557
1659
  });
1558
1660
 
1559
1661
  // src/commands/doctor.ts
1560
- import path20 from "path";
1561
- import { defineCommand as defineCommand5 } from "citty";
1662
+ import path21 from "path";
1663
+ import { defineCommand as defineCommand6 } from "citty";
1562
1664
 
1563
1665
  // src/scanner/readiness.ts
1564
1666
  import { createHash as createHash2 } from "crypto";
@@ -1808,11 +1910,11 @@ function createReadinessReport(scan, options) {
1808
1910
 
1809
1911
  // src/scanner/safe-fixes.ts
1810
1912
  import { readFile as readFile8, writeFile as writeFile3 } from "fs/promises";
1811
- import path18 from "path";
1913
+ import path19 from "path";
1812
1914
 
1813
1915
  // src/scanner/detect-repository.ts
1814
1916
  import { readFile as readFile6 } from "fs/promises";
1815
- import path11 from "path";
1917
+ import path12 from "path";
1816
1918
  var CONTEXT_PATHS = [
1817
1919
  ["README.md", "README"],
1818
1920
  ["README", "README"],
@@ -1831,7 +1933,7 @@ var CONTEXT_PATHS = [
1831
1933
  async function existingEvidence(rootDir, candidates) {
1832
1934
  const evidence = await Promise.all(
1833
1935
  candidates.map(
1834
- async ([relativePath, label]) => await fileExists(path11.join(rootDir, relativePath)) ? { source: "file", value: `${relativePath}:${label}` } : void 0
1936
+ async ([relativePath, label]) => await fileExists(path12.join(rootDir, relativePath)) ? { source: "file", value: `${relativePath}:${label}` } : void 0
1835
1937
  )
1836
1938
  );
1837
1939
  return evidence.flatMap((item) => item ? [item] : []);
@@ -1852,7 +1954,7 @@ async function detectContext(rootDir) {
1852
1954
  async function detectPurpose(rootDir, stack) {
1853
1955
  const entries = await listDirectory(rootDir);
1854
1956
  const lowerEntries = entries.map((entry) => entry.toLowerCase());
1855
- const packageJson = await readJson(path11.join(rootDir, "package.json"));
1957
+ const packageJson = await readJson(path12.join(rootDir, "package.json"));
1856
1958
  const categories = [];
1857
1959
  const evidence = [];
1858
1960
  const add = (category, value2) => {
@@ -1892,16 +1994,16 @@ async function detectPurpose(rootDir, stack) {
1892
1994
  }
1893
1995
  async function detectAgentKit(rootDir) {
1894
1996
  const manifestRelativePath = ".cursor/agent-kit.json";
1895
- const manifestPath = path11.join(rootDir, manifestRelativePath);
1997
+ const manifestPath = path12.join(rootDir, manifestRelativePath);
1896
1998
  const installed = await fileExists(manifestPath);
1897
1999
  const manifest = installed ? await readJson(manifestPath) : null;
1898
2000
  return {
1899
2001
  installed,
1900
2002
  manifestPath: installed ? manifestRelativePath : void 0,
1901
2003
  version: manifest?.version,
1902
- hasPlans: await fileExists(path11.join(rootDir, ".cursor/plans")),
1903
- hasHandoff: await fileExists(path11.join(rootDir, ".cursor/HANDOFF.md")),
1904
- hasMemory: await fileExists(path11.join(rootDir, ".cursor/memory"))
2004
+ hasPlans: await fileExists(path12.join(rootDir, ".cursor/plans")),
2005
+ hasHandoff: await fileExists(path12.join(rootDir, ".cursor/HANDOFF.md")),
2006
+ hasMemory: await fileExists(path12.join(rootDir, ".cursor/memory"))
1905
2007
  };
1906
2008
  }
1907
2009
  var REQUIRED_SECRET_PATTERNS = [
@@ -1915,7 +2017,7 @@ var REQUIRED_SECRET_PATTERNS = [
1915
2017
  "*service-account*.json"
1916
2018
  ];
1917
2019
  async function detectSafety(rootDir, trackedFiles) {
1918
- const gitignorePath = path11.join(rootDir, ".gitignore");
2020
+ const gitignorePath = path12.join(rootDir, ".gitignore");
1919
2021
  const hasGitignore = await fileExists(gitignorePath);
1920
2022
  const gitignore = hasGitignore ? await readFile6(gitignorePath, "utf8") : "";
1921
2023
  const lines = gitignore.split(/\r?\n/).map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
@@ -1926,7 +2028,7 @@ async function detectSafety(rootDir, trackedFiles) {
1926
2028
  (file) => /(^|\/)(\.env(\..+)?|.*\.(key|pem|p12|pfx)|.*credentials.*\.json)$/i.test(file)
1927
2029
  );
1928
2030
  const hookPaths = [".husky", ".git/hooks/pre-commit", "git-hooks/pre-commit"];
1929
- const hasHooks = (await Promise.all(hookPaths.map((item) => fileExists(path11.join(rootDir, item))))).some(Boolean);
2031
+ const hasHooks = (await Promise.all(hookPaths.map((item) => fileExists(path12.join(rootDir, item))))).some(Boolean);
1930
2032
  const guardCandidates = [
1931
2033
  ".husky/pre-commit",
1932
2034
  ".husky/pre-push",
@@ -1935,7 +2037,7 @@ async function detectSafety(rootDir, trackedFiles) {
1935
2037
  ];
1936
2038
  const guardContents = await Promise.all(
1937
2039
  guardCandidates.map(
1938
- async (item) => await fileExists(path11.join(rootDir, item)) ? readFile6(path11.join(rootDir, item), "utf8") : ""
2040
+ async (item) => await fileExists(path12.join(rootDir, item)) ? readFile6(path12.join(rootDir, item), "utf8") : ""
1939
2041
  )
1940
2042
  );
1941
2043
  return {
@@ -1953,11 +2055,11 @@ async function detectSafety(rootDir, trackedFiles) {
1953
2055
  }
1954
2056
 
1955
2057
  // src/scanner/scan.ts
1956
- import path17 from "path";
2058
+ import path18 from "path";
1957
2059
 
1958
2060
  // src/scanner/detect-git.ts
1959
2061
  import { execFile as execFile2 } from "child_process";
1960
- import path12 from "path";
2062
+ import path13 from "path";
1961
2063
  import { promisify as promisify2 } from "util";
1962
2064
  var exec = promisify2(execFile2);
1963
2065
  function remoteHostname(remoteUrl) {
@@ -1981,7 +2083,7 @@ function sanitizeRemoteUrl(remoteUrl) {
1981
2083
  }
1982
2084
  async function detectProvider(rootDir, remoteUrl) {
1983
2085
  const configuration = await readJson(
1984
- path12.join(rootDir, ".cursor", "agent-kit.config.json")
2086
+ path13.join(rootDir, ".cursor", "agent-kit.config.json")
1985
2087
  );
1986
2088
  const configuredProvider = configuration?.git?.provider;
1987
2089
  if (configuredProvider) {
@@ -2038,7 +2140,7 @@ async function detectProvider(rootDir, remoteUrl) {
2038
2140
  evidence: remoteEvidence
2039
2141
  };
2040
2142
  }
2041
- if (await fileExists(path12.join(rootDir, ".gitlab-ci.yml"))) {
2143
+ if (await fileExists(path13.join(rootDir, ".gitlab-ci.yml"))) {
2042
2144
  return {
2043
2145
  provider: "gitlab",
2044
2146
  providerKind: "gitlab-self-hosted",
@@ -2127,11 +2229,11 @@ async function detectGit(rootDir) {
2127
2229
  }
2128
2230
 
2129
2231
  // src/scanner/detect-ide.ts
2130
- import path13 from "path";
2232
+ import path14 from "path";
2131
2233
  async function detectIde(rootDir) {
2132
- const hasCursor = await fileExists(path13.join(rootDir, ".cursor"));
2133
- const hasVSCode = await fileExists(path13.join(rootDir, ".vscode"));
2134
- const hasWindsurf = await fileExists(path13.join(rootDir, ".windsurfrules"));
2234
+ const hasCursor = await fileExists(path14.join(rootDir, ".cursor"));
2235
+ const hasVSCode = await fileExists(path14.join(rootDir, ".vscode"));
2236
+ const hasWindsurf = await fileExists(path14.join(rootDir, ".windsurfrules"));
2135
2237
  if (hasCursor) return { ide: "cursor", plan: "cursor-pro" };
2136
2238
  if (hasVSCode) return { ide: "vscode", plan: "vscode-pro" };
2137
2239
  if (hasWindsurf) return { ide: "windsurf", plan: "windsurf" };
@@ -2139,7 +2241,7 @@ async function detectIde(rootDir) {
2139
2241
  }
2140
2242
 
2141
2243
  // src/scanner/detect-infra.ts
2142
- import path14 from "path";
2244
+ import path15 from "path";
2143
2245
 
2144
2246
  // src/types.ts
2145
2247
  var CI_PLATFORM_FILES = {
@@ -2168,12 +2270,12 @@ var PM_TOOL_LABELS = {
2168
2270
 
2169
2271
  // src/scanner/detect-infra.ts
2170
2272
  async function detectInfra(rootDir) {
2171
- const docker = await fileExists(path14.join(rootDir, "Dockerfile")) || await fileExists(path14.join(rootDir, "docker-compose.yml")) || await fileExists(path14.join(rootDir, "docker-compose.yaml"));
2172
- const kubernetes = await fileExists(path14.join(rootDir, "k8s")) || await fileExists(path14.join(rootDir, "kubernetes"));
2273
+ const docker = await fileExists(path15.join(rootDir, "Dockerfile")) || await fileExists(path15.join(rootDir, "docker-compose.yml")) || await fileExists(path15.join(rootDir, "docker-compose.yaml"));
2274
+ const kubernetes = await fileExists(path15.join(rootDir, "k8s")) || await fileExists(path15.join(rootDir, "kubernetes"));
2173
2275
  let ci = "none";
2174
2276
  const ciFiles = [];
2175
2277
  for (const [platform, filePath] of Object.entries(CI_PLATFORM_FILES)) {
2176
- if (await fileExists(path14.join(rootDir, filePath))) {
2278
+ if (await fileExists(path15.join(rootDir, filePath))) {
2177
2279
  if (ci === "none") ci = platform;
2178
2280
  ciFiles.push(filePath);
2179
2281
  }
@@ -2198,12 +2300,12 @@ async function detectInfra(rootDir) {
2198
2300
  ];
2199
2301
  const infrastructureFiles = (await Promise.all(
2200
2302
  infrastructureCandidates.map(
2201
- async (file) => await fileExists(path14.join(rootDir, file)) ? file : void 0
2303
+ async (file) => await fileExists(path15.join(rootDir, file)) ? file : void 0
2202
2304
  )
2203
2305
  )).filter((file) => file !== void 0);
2204
2306
  const deploymentFiles = (await Promise.all(
2205
2307
  deploymentCandidates.map(
2206
- async (file) => await fileExists(path14.join(rootDir, file)) ? file : void 0
2308
+ async (file) => await fileExists(path15.join(rootDir, file)) ? file : void 0
2207
2309
  )
2208
2310
  )).filter((file) => file !== void 0);
2209
2311
  return { docker, kubernetes, ci, ciFiles, infrastructureFiles, deploymentFiles };
@@ -2211,12 +2313,12 @@ async function detectInfra(rootDir) {
2211
2313
 
2212
2314
  // src/scanner/detect-services.ts
2213
2315
  import { readFile as readFile7 } from "fs/promises";
2214
- import path15 from "path";
2316
+ import path16 from "path";
2215
2317
  async function detectProjectManagement(rootDir) {
2216
2318
  const tools = [];
2217
2319
  const mcpConfigPaths = [
2218
- path15.join(rootDir, ".cursor", "mcp.json"),
2219
- path15.join(rootDir, "mcp.json")
2320
+ path16.join(rootDir, ".cursor", "mcp.json"),
2321
+ path16.join(rootDir, "mcp.json")
2220
2322
  ];
2221
2323
  for (const configPath of mcpConfigPaths) {
2222
2324
  if (!await fileExists(configPath)) continue;
@@ -2232,20 +2334,20 @@ async function detectProjectManagement(rootDir) {
2232
2334
  } catch {
2233
2335
  }
2234
2336
  }
2235
- if (await fileExists(path15.join(rootDir, ".github", "ISSUE_TEMPLATE"))) {
2337
+ if (await fileExists(path16.join(rootDir, ".github", "ISSUE_TEMPLATE"))) {
2236
2338
  tools.push("github-issues");
2237
2339
  }
2238
- if (await fileExists(path15.join(rootDir, ".github", "projects"))) {
2340
+ if (await fileExists(path16.join(rootDir, ".github", "projects"))) {
2239
2341
  tools.push("github-projects");
2240
2342
  }
2241
2343
  return [...new Set(tools)];
2242
2344
  }
2243
2345
  async function detectServices(rootDir) {
2244
- const hasPrisma = await fileExists(path15.join(rootDir, "prisma/schema.prisma"));
2245
- const hasSequelize = await fileExists(path15.join(rootDir, "sequelize"));
2246
- const hasDrizzle = await fileExists(path15.join(rootDir, "drizzle.config.ts"));
2247
- const hasKnex = await fileExists(path15.join(rootDir, "knexfile.ts"));
2248
- const hasTypeorm = await fileExists(path15.join(rootDir, "ormconfig.json"));
2346
+ const hasPrisma = await fileExists(path16.join(rootDir, "prisma/schema.prisma"));
2347
+ const hasSequelize = await fileExists(path16.join(rootDir, "sequelize"));
2348
+ const hasDrizzle = await fileExists(path16.join(rootDir, "drizzle.config.ts"));
2349
+ const hasKnex = await fileExists(path16.join(rootDir, "knexfile.ts"));
2350
+ const hasTypeorm = await fileExists(path16.join(rootDir, "ormconfig.json"));
2249
2351
  const database = hasPrisma || hasSequelize || hasDrizzle || hasKnex || hasTypeorm ? "postgresql" : void 0;
2250
2352
  const orm = hasPrisma ? "prisma" : hasDrizzle ? "drizzle" : hasSequelize ? "sequelize" : hasKnex ? "knex" : hasTypeorm ? "typeorm" : void 0;
2251
2353
  const projectManagement = await detectProjectManagement(rootDir);
@@ -2257,7 +2359,7 @@ async function detectServices(rootDir) {
2257
2359
  }
2258
2360
 
2259
2361
  // src/scanner/detect-stack.ts
2260
- import path16 from "path";
2362
+ import path17 from "path";
2261
2363
  var PROJECT_MARKERS = [
2262
2364
  "package.json",
2263
2365
  "requirements.txt",
@@ -2286,7 +2388,7 @@ async function detectPackageManager(rootDir, packageJson) {
2286
2388
  };
2287
2389
  }
2288
2390
  for (const [lockfile, packageManager] of LOCKFILES) {
2289
- if (await fileExists(path16.join(rootDir, lockfile))) {
2391
+ if (await fileExists(path17.join(rootDir, lockfile))) {
2290
2392
  return {
2291
2393
  packageManager,
2292
2394
  evidence: [{ source: "file", value: lockfile }]
@@ -2303,27 +2405,27 @@ function commandsForScripts(scripts, packageManager) {
2303
2405
  return { testCommands, validationCommands };
2304
2406
  }
2305
2407
  async function detectStack(rootDir) {
2306
- const hasAnyProjectMarker = (await Promise.all(PROJECT_MARKERS.map((item) => fileExists(path16.join(rootDir, item))))).some(Boolean);
2307
- const hasPackageJson = await fileExists(path16.join(rootDir, "package.json"));
2408
+ const hasAnyProjectMarker = (await Promise.all(PROJECT_MARKERS.map((item) => fileExists(path17.join(rootDir, item))))).some(Boolean);
2409
+ const hasPackageJson = await fileExists(path17.join(rootDir, "package.json"));
2308
2410
  if (hasPackageJson) {
2309
- const packageJson = await readJson(path16.join(rootDir, "package.json")) ?? {};
2411
+ const packageJson = await readJson(path17.join(rootDir, "package.json")) ?? {};
2310
2412
  const scripts = packageJson.scripts ?? {};
2311
2413
  const packageManager = await detectPackageManager(rootDir, packageJson);
2312
2414
  const commands = commandsForScripts(scripts, packageManager.packageManager);
2313
- const hasNextConfig = await fileExists(path16.join(rootDir, "next.config.js")) || await fileExists(path16.join(rootDir, "next.config.mjs")) || await fileExists(path16.join(rootDir, "next.config.ts"));
2314
- const hasNestConfig = await fileExists(path16.join(rootDir, "nest-cli.json"));
2415
+ const hasNextConfig = await fileExists(path17.join(rootDir, "next.config.js")) || await fileExists(path17.join(rootDir, "next.config.mjs")) || await fileExists(path17.join(rootDir, "next.config.ts"));
2416
+ const hasNestConfig = await fileExists(path17.join(rootDir, "nest-cli.json"));
2315
2417
  return {
2316
2418
  language: "node",
2317
2419
  framework: hasNextConfig ? "nextjs" : hasNestConfig ? "nestjs" : "node",
2318
2420
  packageManager: packageManager.packageManager,
2319
2421
  packageManagerEvidence: packageManager.evidence,
2320
2422
  scripts,
2321
- workspaces: packageJson.workspaces !== void 0 || await fileExists(path16.join(rootDir, "pnpm-workspace.yaml")),
2423
+ workspaces: packageJson.workspaces !== void 0 || await fileExists(path17.join(rootDir, "pnpm-workspace.yaml")),
2322
2424
  ...commands,
2323
2425
  hasProjectFiles: hasAnyProjectMarker
2324
2426
  };
2325
2427
  }
2326
- if (await fileExists(path16.join(rootDir, "pyproject.toml"))) {
2428
+ if (await fileExists(path17.join(rootDir, "pyproject.toml"))) {
2327
2429
  return {
2328
2430
  language: "python",
2329
2431
  framework: "python",
@@ -2333,7 +2435,7 @@ async function detectStack(rootDir) {
2333
2435
  hasProjectFiles: hasAnyProjectMarker
2334
2436
  };
2335
2437
  }
2336
- if (await fileExists(path16.join(rootDir, "go.mod"))) {
2438
+ if (await fileExists(path17.join(rootDir, "go.mod"))) {
2337
2439
  return {
2338
2440
  language: "go",
2339
2441
  framework: "go",
@@ -2343,7 +2445,7 @@ async function detectStack(rootDir) {
2343
2445
  hasProjectFiles: hasAnyProjectMarker
2344
2446
  };
2345
2447
  }
2346
- if (await fileExists(path16.join(rootDir, "Cargo.toml"))) {
2448
+ if (await fileExists(path17.join(rootDir, "Cargo.toml"))) {
2347
2449
  return {
2348
2450
  language: "rust",
2349
2451
  framework: "rust",
@@ -2353,7 +2455,7 @@ async function detectStack(rootDir) {
2353
2455
  hasProjectFiles: hasAnyProjectMarker
2354
2456
  };
2355
2457
  }
2356
- if (await fileExists(path16.join(rootDir, "composer.json"))) {
2458
+ if (await fileExists(path17.join(rootDir, "composer.json"))) {
2357
2459
  return {
2358
2460
  language: "php",
2359
2461
  framework: "php",
@@ -2386,7 +2488,7 @@ function isGreenfieldByEntries(entries) {
2386
2488
  return meaningful.length === 0;
2387
2489
  }
2388
2490
  async function runScanner(rootDir) {
2389
- const normalizedRoot = path17.resolve(rootDir);
2491
+ const normalizedRoot = path18.resolve(rootDir);
2390
2492
  const entries = await listDirectory(normalizedRoot);
2391
2493
  const stack = await detectStack(normalizedRoot);
2392
2494
  const purpose = await detectPurpose(normalizedRoot, stack);
@@ -2537,10 +2639,13 @@ function preferenceDefaults(onboarding, onboarded) {
2537
2639
  enabled: false,
2538
2640
  backend: "claude",
2539
2641
  autoRemediate: false,
2540
- offerOnExhausted: true
2642
+ offerOnExhausted: true,
2643
+ mode: "paste",
2644
+ midBatchAudits: false,
2645
+ preflight: "off"
2541
2646
  },
2542
2647
  autoHandoff: false,
2543
- workspaceSkin: {
2648
+ agentPersona: {
2544
2649
  default: "autopilot",
2545
2650
  modes: {
2546
2651
  "continue-plan": "autopilot",
@@ -2587,7 +2692,7 @@ async function executeSafeReadinessFixes(rootDir, options) {
2587
2692
  });
2588
2693
  const changes = [];
2589
2694
  for (const relativePath of ESSENTIAL_DIRECTORIES) {
2590
- const absolutePath = path18.join(beforeScan.rootDir, relativePath);
2695
+ const absolutePath = path19.join(beforeScan.rootDir, relativePath);
2591
2696
  const exists = await fileExists(absolutePath);
2592
2697
  if (!exists && !dryRun) await ensureDir(absolutePath);
2593
2698
  recordChange(
@@ -2600,7 +2705,7 @@ async function executeSafeReadinessFixes(rootDir, options) {
2600
2705
  );
2601
2706
  }
2602
2707
  const gitignoreRelativePath = ".gitignore";
2603
- const gitignorePath = path18.join(beforeScan.rootDir, gitignoreRelativePath);
2708
+ const gitignorePath = path19.join(beforeScan.rootDir, gitignoreRelativePath);
2604
2709
  const existingGitignore = await fileExists(gitignorePath) ? await readFile8(gitignorePath, "utf8") : "";
2605
2710
  const mergedGitignore = mergeSecretIgnores(existingGitignore);
2606
2711
  const gitignoreChanged = mergedGitignore !== existingGitignore;
@@ -2616,7 +2721,7 @@ async function executeSafeReadinessFixes(rootDir, options) {
2616
2721
  gitignoreChanged ? "required secret patterns are missing" : "required patterns are present"
2617
2722
  )
2618
2723
  );
2619
- const profilePath = path18.join(beforeScan.rootDir, PROFILE_RELATIVE_PATH);
2724
+ const profilePath = path19.join(beforeScan.rootDir, PROFILE_RELATIVE_PATH);
2620
2725
  const existingProfile = await readJson(profilePath) ?? {};
2621
2726
  const desiredProfile = createProfile(beforeScan, before, generatedAt);
2622
2727
  const mergedProfile = mergeMissing(existingProfile, desiredProfile);
@@ -2638,7 +2743,7 @@ async function executeSafeReadinessFixes(rootDir, options) {
2638
2743
  generatorVersion: options.generatorVersion,
2639
2744
  generatedAt
2640
2745
  });
2641
- const contextConfigPath = path18.join(beforeScan.rootDir, CONTEXT_CONFIG_RELATIVE_PATH);
2746
+ const contextConfigPath = path19.join(beforeScan.rootDir, CONTEXT_CONFIG_RELATIVE_PATH);
2642
2747
  const existingContextConfig = await readJson(contextConfigPath) ?? {};
2643
2748
  const onboarding = reconcileOnboardingState(evidenceReport, existingContextConfig, generatedAt);
2644
2749
  const defaults = preferenceDefaults(onboarding, existingContextConfig.onboarded);
@@ -2667,17 +2772,17 @@ async function executeSafeReadinessFixes(rootDir, options) {
2667
2772
  }
2668
2773
 
2669
2774
  // src/scanner/snapshot.ts
2670
- import path19 from "path";
2775
+ import path20 from "path";
2671
2776
  var READINESS_SNAPSHOT_RELATIVE_PATH = ".cursor/context/readiness.json";
2672
2777
  async function writeReadinessSnapshot(rootDir, report) {
2673
- const snapshotPath = path19.join(rootDir, READINESS_SNAPSHOT_RELATIVE_PATH);
2778
+ const snapshotPath = path20.join(rootDir, READINESS_SNAPSHOT_RELATIVE_PATH);
2674
2779
  await writeJson(snapshotPath, report);
2675
2780
  return snapshotPath;
2676
2781
  }
2677
2782
 
2678
2783
  // src/commands/doctor.ts
2679
2784
  async function runDoctor(cwd, options = {}) {
2680
- const rootDir = path20.resolve(cwd);
2785
+ const rootDir = path21.resolve(cwd);
2681
2786
  if (options.fixSafe) {
2682
2787
  const execution = await executeSafeReadinessFixes(rootDir, {
2683
2788
  generatorVersion: KIT_VERSION,
@@ -2708,7 +2813,7 @@ function printDoctorSummary(result) {
2708
2813
  nextAction ? `Next: ${nextAction.recommendation}` : "Next: repository readiness checks are complete"
2709
2814
  );
2710
2815
  }
2711
- var doctorCommand = defineCommand5({
2816
+ var doctorCommand = defineCommand6({
2712
2817
  meta: {
2713
2818
  name: "doctor",
2714
2819
  description: "Diagnose repository readiness and optionally apply safe local fixes."
@@ -2740,10 +2845,10 @@ var doctorCommand = defineCommand5({
2740
2845
  });
2741
2846
 
2742
2847
  // src/commands/handoff.ts
2743
- import { spawn as spawn2 } from "child_process";
2848
+ import { spawn as spawn3 } from "child_process";
2744
2849
  import { readFile as readFile9, readdir as readdir2, writeFile as writeFile4 } from "fs/promises";
2745
- import path21 from "path";
2746
- import { defineCommand as defineCommand6 } from "citty";
2850
+ import path22 from "path";
2851
+ import { defineCommand as defineCommand7 } from "citty";
2747
2852
  function parsePlanFrontmatter(raw) {
2748
2853
  const match = raw.match(/^---\n([\s\S]*?)\n---/);
2749
2854
  if (!match?.[1]) return null;
@@ -2762,19 +2867,19 @@ async function findActivePlan(plansDir) {
2762
2867
  if (!await fileExists(plansDir)) return null;
2763
2868
  const files = (await readdir2(plansDir)).filter((f) => f.endsWith(".plan.md")).sort().reverse();
2764
2869
  for (const file of files) {
2765
- const raw = await readFile9(path21.join(plansDir, file), "utf8");
2870
+ const raw = await readFile9(path22.join(plansDir, file), "utf8");
2766
2871
  const fm = parsePlanFrontmatter(raw);
2767
2872
  if (fm?.todos?.some((t) => t.status !== "completed" && t.status !== "cancelled")) {
2768
2873
  return { file, raw };
2769
2874
  }
2770
2875
  }
2771
- return files[0] ? { file: files[0], raw: await readFile9(path21.join(plansDir, files[0]), "utf8") } : null;
2876
+ return files[0] ? { file: files[0], raw: await readFile9(path22.join(plansDir, files[0]), "utf8") } : null;
2772
2877
  }
2773
2878
  function now() {
2774
2879
  return (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 16);
2775
2880
  }
2776
2881
  async function loadProfile(rootDir) {
2777
- const configPath = path21.join(rootDir, ".cursor", "agent-kit.config.json");
2882
+ const configPath = path22.join(rootDir, ".cursor", "agent-kit.config.json");
2778
2883
  try {
2779
2884
  return await readJson(configPath);
2780
2885
  } catch {
@@ -2852,7 +2957,7 @@ function printV3Guidance() {
2852
2957
  }
2853
2958
  function runCursorHandoff(scriptPath, cwd) {
2854
2959
  return new Promise((resolve, reject) => {
2855
- const child = spawn2("sh", [scriptPath, "handoff"], {
2960
+ const child = spawn3("sh", [scriptPath, "handoff"], {
2856
2961
  cwd,
2857
2962
  stdio: "inherit"
2858
2963
  });
@@ -2860,7 +2965,7 @@ function runCursorHandoff(scriptPath, cwd) {
2860
2965
  child.on("close", (code) => resolve(code ?? 1));
2861
2966
  });
2862
2967
  }
2863
- var handoffCommand = defineCommand6({
2968
+ var handoffCommand = defineCommand7({
2864
2969
  meta: {
2865
2970
  name: "handoff",
2866
2971
  description: "Write .cursor/HANDOFF.md from the active Cursor plan, or run ./cursor-handoff handoff when no plan exists."
@@ -2874,13 +2979,13 @@ var handoffCommand = defineCommand6({
2874
2979
  },
2875
2980
  async run({ args }) {
2876
2981
  const profile = await loadProfile(args.cwd);
2877
- const plansDir = path21.join(args.cwd, ".cursor", "plans");
2878
- const handoffPath = path21.join(args.cwd, ".cursor", "HANDOFF.md");
2982
+ const plansDir = path22.join(args.cwd, ".cursor", "plans");
2983
+ const handoffPath = path22.join(args.cwd, ".cursor", "HANDOFF.md");
2879
2984
  const plan = await findActivePlan(plansDir);
2880
2985
  if (plan) {
2881
2986
  const fm = parsePlanFrontmatter(plan.raw);
2882
2987
  if (fm) {
2883
- await ensureDir(path21.join(args.cwd, ".cursor"));
2988
+ await ensureDir(path22.join(args.cwd, ".cursor"));
2884
2989
  const content = buildHandoff(plan.file, fm, profile);
2885
2990
  await writeFile4(handoffPath, content, "utf8");
2886
2991
  logger.success("HANDOFF.md updated: .cursor/HANDOFF.md");
@@ -2900,7 +3005,7 @@ var handoffCommand = defineCommand6({
2900
3005
  }
2901
3006
  logger.warn(`Plan ${plan.file} without valid frontmatter; trying legacy flow.`);
2902
3007
  }
2903
- const scriptPath = path21.join(args.cwd, "cursor-handoff");
3008
+ const scriptPath = path22.join(args.cwd, "cursor-handoff");
2904
3009
  if (!await fileExists(scriptPath)) {
2905
3010
  printV3Guidance();
2906
3011
  return;
@@ -2926,15 +3031,15 @@ var handoffCommand = defineCommand6({
2926
3031
 
2927
3032
  // src/commands/init.ts
2928
3033
  import { intro, outro } from "@clack/prompts";
2929
- import { defineCommand as defineCommand8 } from "citty";
3034
+ import { defineCommand as defineCommand9 } from "citty";
2930
3035
 
2931
3036
  // src/commands/install.ts
2932
- import path24 from "path";
2933
- import { defineCommand as defineCommand7 } from "citty";
3037
+ import path25 from "path";
3038
+ import { defineCommand as defineCommand8 } from "citty";
2934
3039
 
2935
3040
  // src/generator/personalization.ts
2936
3041
  import { readFile as readFile10, writeFile as writeFile5 } from "fs/promises";
2937
- import path22 from "path";
3042
+ import path23 from "path";
2938
3043
  var PERSONALIZATION_CONTRACT_VERSION = 1;
2939
3044
  var CONTEXT_PATH = ".cursor/project-context.md";
2940
3045
  var AGENTS_PATH = "AGENTS.md";
@@ -3085,7 +3190,7 @@ function renderProjectContext(profile) {
3085
3190
  `;
3086
3191
  }
3087
3192
  async function createOwnedFile(rootDir, relativePath, content, evidence) {
3088
- const target = path22.join(rootDir, relativePath);
3193
+ const target = path23.join(rootDir, relativePath);
3089
3194
  if (await fileExists(target)) {
3090
3195
  return {
3091
3196
  kind: "file",
@@ -3095,7 +3200,7 @@ async function createOwnedFile(rootDir, relativePath, content, evidence) {
3095
3200
  evidence
3096
3201
  };
3097
3202
  }
3098
- await ensureDir(path22.dirname(target));
3203
+ await ensureDir(path23.dirname(target));
3099
3204
  await writeFile5(target, content, "utf8");
3100
3205
  return {
3101
3206
  kind: "file",
@@ -3112,7 +3217,7 @@ async function packTargets(registryRoot, packId) {
3112
3217
  async function existingTargets(projectRoot, targets) {
3113
3218
  const checks = await Promise.all(
3114
3219
  targets.map(
3115
- async (target) => await fileExists(path22.join(projectRoot, target)) ? target : null
3220
+ async (target) => await fileExists(path23.join(projectRoot, target)) ? target : null
3116
3221
  )
3117
3222
  );
3118
3223
  return checks.filter((target) => target !== null);
@@ -3134,14 +3239,14 @@ async function applyPersonalization(input) {
3134
3239
  componentResults.push({ ...item, status: "unavailable" });
3135
3240
  continue;
3136
3241
  }
3137
- const target = path22.posix.join(
3242
+ const target = path23.posix.join(
3138
3243
  ".cursor",
3139
3244
  "skills",
3140
3245
  skill.path.includes("/core/") ? "core" : "community",
3141
3246
  skill.id,
3142
3247
  "SKILL.md"
3143
3248
  );
3144
- if (await fileExists(path22.join(input.rootDir, target))) {
3249
+ if (await fileExists(path23.join(input.rootDir, target))) {
3145
3250
  componentResults.push({ ...item, status: "skipped-customized", path: target });
3146
3251
  protectedPaths.add(target);
3147
3252
  continue;
@@ -3196,7 +3301,7 @@ async function applyPersonalization(input) {
3196
3301
  items: [...fileResults, ...componentResults],
3197
3302
  protectedPaths: [...protectedPaths].sort()
3198
3303
  };
3199
- await writeJson(path22.join(input.rootDir, RESULT_PATH), result);
3304
+ await writeJson(path23.join(input.rootDir, RESULT_PATH), result);
3200
3305
  return {
3201
3306
  result,
3202
3307
  manifest: {
@@ -3214,7 +3319,7 @@ async function applyPersonalization(input) {
3214
3319
  };
3215
3320
  }
3216
3321
  async function readRepositoryProfile(rootDir) {
3217
- const target = path22.join(rootDir, ".cursor/agent-kit.config.json");
3322
+ const target = path23.join(rootDir, ".cursor/agent-kit.config.json");
3218
3323
  if (!await fileExists(target)) return null;
3219
3324
  return JSON.parse(await readFile10(target, "utf8"));
3220
3325
  }
@@ -3222,16 +3327,16 @@ async function readRepositoryProfile(rootDir) {
3222
3327
  // src/lifecycle/onboard-migration.ts
3223
3328
  import { createHash as createHash3 } from "crypto";
3224
3329
  import { readFile as readFile11, unlink } from "fs/promises";
3225
- import path23 from "path";
3330
+ import path24 from "path";
3226
3331
  var LEGACY_ONBOARD_PATH = ".cursor/commands/onboard.md";
3227
3332
  var NAMESPACED_ONBOARD_PATH = ".cursor/commands/agent-kit-onboard.md";
3228
3333
  var MANAGED_LEGACY_HASHES = /* @__PURE__ */ new Set([
3229
3334
  "b274a68941813f19b185893cb7c5561dff027f53270890029992f208e24992fe"
3230
3335
  ]);
3231
3336
  async function migrateLegacyOnboardCommand(projectRoot, managedHashes = MANAGED_LEGACY_HASHES) {
3232
- const legacyPath = path23.join(projectRoot, LEGACY_ONBOARD_PATH);
3337
+ const legacyPath = path24.join(projectRoot, LEGACY_ONBOARD_PATH);
3233
3338
  if (!await fileExists(legacyPath)) return "absent";
3234
- const namespacedPath = path23.join(projectRoot, NAMESPACED_ONBOARD_PATH);
3339
+ const namespacedPath = path24.join(projectRoot, NAMESPACED_ONBOARD_PATH);
3235
3340
  if (!await fileExists(namespacedPath)) return "preserved-customized";
3236
3341
  const content = await readFile11(legacyPath);
3237
3342
  const hash = createHash3("sha256").update(content).digest("hex");
@@ -3302,7 +3407,7 @@ function printReadinessNarrative(result) {
3302
3407
  );
3303
3408
  }
3304
3409
  async function performInstall(options) {
3305
- const projectRoot = path24.resolve(options.cwd);
3410
+ const projectRoot = path25.resolve(options.cwd);
3306
3411
  const packs = parsePackList(options.pack);
3307
3412
  const existing = await loadAgentKitManifest(projectRoot);
3308
3413
  const registry = await resolveRegistryFromCli({
@@ -3356,7 +3461,7 @@ async function performInstall(options) {
3356
3461
  safeChanges: readinessExecution.changes
3357
3462
  };
3358
3463
  }
3359
- var installCommand = defineCommand7({
3464
+ var installCommand = defineCommand8({
3360
3465
  meta: {
3361
3466
  name: "install",
3362
3467
  description: "Bootstrap L0 (+ optional packs) from the registry and write agent-kit.json."
@@ -3378,7 +3483,7 @@ var installCommand = defineCommand7({
3378
3483
  ...REGISTRY_CLI_ARGS
3379
3484
  },
3380
3485
  async run({ args }) {
3381
- const projectRoot = path24.resolve(args.cwd);
3486
+ const projectRoot = path25.resolve(args.cwd);
3382
3487
  logger.info(`Installing into: ${projectRoot}`);
3383
3488
  const packs = parsePackList(args.pack);
3384
3489
  for (const id of packs) {
@@ -3406,7 +3511,7 @@ var installCommand = defineCommand7({
3406
3511
  async function runInitCompatibility(cwd, installer = performInstall) {
3407
3512
  return installer({ cwd });
3408
3513
  }
3409
- var initCommand = defineCommand8({
3514
+ var initCommand = defineCommand9({
3410
3515
  meta: {
3411
3516
  name: "init",
3412
3517
  description: "Guided compatibility entry point for install and repository readiness."
@@ -3431,11 +3536,11 @@ var initCommand = defineCommand8({
3431
3536
  });
3432
3537
 
3433
3538
  // src/commands/run-plan.ts
3434
- import path29 from "path";
3435
- import { defineCommand as defineCommand9 } from "citty";
3539
+ import path30 from "path";
3540
+ import { defineCommand as defineCommand10 } from "citty";
3436
3541
 
3437
3542
  // src/plan-loop/backends.ts
3438
- import { execFileSync, spawn as spawn3 } from "child_process";
3543
+ import { execFileSync, spawn as spawn4 } from "child_process";
3439
3544
  import { createWriteStream } from "fs";
3440
3545
  async function which(bin) {
3441
3546
  try {
@@ -3448,7 +3553,7 @@ async function which(bin) {
3448
3553
  function spawnLogged(command, args, logPath) {
3449
3554
  return new Promise((resolve, reject) => {
3450
3555
  const out = createWriteStream(logPath, { flags: "w" });
3451
- const child = spawn3(command, args, {
3556
+ const child = spawn4(command, args, {
3452
3557
  stdio: ["ignore", "pipe", "pipe"]
3453
3558
  });
3454
3559
  const onData = (chunk) => {
@@ -3517,13 +3622,13 @@ function listBackendIds() {
3517
3622
 
3518
3623
  // src/plan-loop/run-loop.ts
3519
3624
  import { mkdir as mkdir4, readFile as readFile14, rm, unlink as unlink2 } from "fs/promises";
3520
- import path28 from "path";
3625
+ import path29 from "path";
3521
3626
 
3522
3627
  // src/plan-loop/external-review.ts
3523
- import { spawn as spawn4 } from "child_process";
3524
- import path25 from "path";
3525
- var CANONICAL_REL = path25.join(".cursor", "scripts", "plan-external-review.sh");
3526
- var FALLBACK_REL = path25.join("scripts", "plan-external-review.sh");
3628
+ import { spawn as spawn5 } from "child_process";
3629
+ import path26 from "path";
3630
+ var CANONICAL_REL = path26.join(".cursor", "scripts", "plan-external-review.sh");
3631
+ var FALLBACK_REL = path26.join("scripts", "plan-external-review.sh");
3527
3632
  function isPlanExhaustedReason(reason) {
3528
3633
  const r = reason.trim().toLowerCase();
3529
3634
  if (!r) return false;
@@ -3541,12 +3646,12 @@ function shouldArmExternalPlanReview(input) {
3541
3646
  return false;
3542
3647
  }
3543
3648
  async function armExternalPlanReview(root, options = {}) {
3544
- const spawnFn = options.spawnFn ?? spawn4;
3649
+ const spawnFn = options.spawnFn ?? spawn5;
3545
3650
  const existsFn = options.existsFn ?? fileExists;
3546
3651
  const log = options.log ?? ((line) => console.log(line));
3547
3652
  const force = options.force === true;
3548
- const canonicalPath = path25.join(root, CANONICAL_REL);
3549
- const fallbackPath = path25.join(root, FALLBACK_REL);
3653
+ const canonicalPath = path26.join(root, CANONICAL_REL);
3654
+ const fallbackPath = path26.join(root, FALLBACK_REL);
3550
3655
  let scriptPath = null;
3551
3656
  let scriptRel = CANONICAL_REL;
3552
3657
  if (await existsFn(canonicalPath)) {
@@ -3563,11 +3668,11 @@ async function armExternalPlanReview(root, options = {}) {
3563
3668
  return { invoked: false, exitCode: null, output: "" };
3564
3669
  }
3565
3670
  log("Plan exhausted: arming optional external plan review (opt-in handled by script)...");
3566
- const args = force ? [scriptPath, "--force"] : [scriptPath];
3671
+ const args = force ? [scriptPath, "--force", "--print"] : [scriptPath, "--print"];
3567
3672
  return new Promise((resolve) => {
3568
3673
  const child = spawnFn("bash", args, {
3569
3674
  cwd: root,
3570
- env: process.env
3675
+ env: { ...process.env, AGENT_KIT_HEADLESS: "1" }
3571
3676
  });
3572
3677
  let output = "";
3573
3678
  const append = (chunk) => {
@@ -3598,9 +3703,105 @@ async function armExternalPlanReview(root, options = {}) {
3598
3703
  });
3599
3704
  }
3600
3705
 
3706
+ // src/plan-loop/persona-banners.ts
3707
+ import path27 from "path";
3708
+ import {
3709
+ blue,
3710
+ cyan as cyan2,
3711
+ gray,
3712
+ green as green2,
3713
+ lightGray,
3714
+ lightGreen,
3715
+ magenta,
3716
+ red as red2,
3717
+ white,
3718
+ yellow as yellow2
3719
+ } from "kolorist";
3720
+ var CLI_RUN_PLAN_MODE = "cli-run-plan";
3721
+ var DEFAULT_CLI_PERSONA_ID = "ghost-runner";
3722
+ var COLORS = {
3723
+ white,
3724
+ gray,
3725
+ green: green2,
3726
+ cyan: cyan2,
3727
+ magenta,
3728
+ yellow: yellow2,
3729
+ red: red2,
3730
+ blue,
3731
+ "light-green": lightGreen,
3732
+ lightgreen: lightGreen,
3733
+ "light-gray": lightGray,
3734
+ lightgray: lightGray
3735
+ };
3736
+ function resolveColor(name, fallback) {
3737
+ if (!name) return fallback;
3738
+ return COLORS[name.toLowerCase()] ?? fallback;
3739
+ }
3740
+ async function resolveCliPersonaId(root) {
3741
+ try {
3742
+ const cfg = await readJson(
3743
+ path27.join(root, ".cursor", "context", "config.json")
3744
+ );
3745
+ const modes = cfg?.agentPersona?.modes ?? cfg?.workspaceSkin?.modes;
3746
+ const id = modes?.[CLI_RUN_PLAN_MODE];
3747
+ if (typeof id === "string" && id.trim()) return id.trim();
3748
+ } catch {
3749
+ }
3750
+ return DEFAULT_CLI_PERSONA_ID;
3751
+ }
3752
+ async function loadPersonaPack(root, personaId) {
3753
+ try {
3754
+ const personaPath = path27.join(root, "registry", "personas", "core", personaId, "persona.json");
3755
+ const pack = await readJson(personaPath);
3756
+ if (!pack || typeof pack.id !== "string") return null;
3757
+ return pack;
3758
+ } catch {
3759
+ return null;
3760
+ }
3761
+ }
3762
+ async function loadCliRunPlanPersona(root) {
3763
+ const id = await resolveCliPersonaId(root);
3764
+ return loadPersonaPack(root, id);
3765
+ }
3766
+ function createPersonaBannerPrinter(persona) {
3767
+ if (!persona?.cliBanners) return null;
3768
+ const banners = persona.cliBanners;
3769
+ if (!banners.tickStart && !banners.tickEnd && !banners.phaseComplete) return null;
3770
+ const primary = resolveColor(persona.ansiPalette?.primary, white);
3771
+ const secondary = resolveColor(persona.ansiPalette?.secondary, gray);
3772
+ const accent = resolveColor(persona.ansiPalette?.accent, green2);
3773
+ return {
3774
+ tickStart(detail) {
3775
+ if (banners.tickStart) {
3776
+ console.log(`${accent(banners.tickStart)} ${primary(detail)}`);
3777
+ } else {
3778
+ console.log(primary(detail));
3779
+ }
3780
+ },
3781
+ tickEnd(detail) {
3782
+ if (!banners.tickEnd) return;
3783
+ const line = detail ? `${banners.tickEnd} ${detail}` : banners.tickEnd;
3784
+ console.log(secondary(line));
3785
+ },
3786
+ phaseComplete(detail) {
3787
+ if (!banners.phaseComplete) return;
3788
+ const line = detail ? `${banners.phaseComplete} ${detail}` : banners.phaseComplete;
3789
+ console.log(accent(line));
3790
+ },
3791
+ sleep(seconds) {
3792
+ console.log(secondary(`sleep ${seconds}s`));
3793
+ },
3794
+ stop(reason) {
3795
+ if (banners.tickEnd) {
3796
+ console.log(secondary(`${banners.tickEnd} ${reason}`));
3797
+ }
3798
+ }
3799
+ };
3800
+ }
3801
+
3601
3802
  // src/plan-loop/plan-state.ts
3602
3803
  import { readFile as readFile12, readdir as readdir3 } from "fs/promises";
3603
- import path26 from "path";
3804
+ import path28 from "path";
3604
3805
  function countPendingTodos(raw) {
3605
3806
  const lines = raw.split(/\r?\n/);
3606
3807
  let inFront = 0;
@@ -3628,7 +3829,7 @@ function countPendingTodos(raw) {
3628
3829
  async function findActivePlanFile(plansDir) {
3629
3830
  if (!await fileExists(plansDir)) return null;
3630
3831
  const files = (await readdir3(plansDir)).filter((f) => f.endsWith(".plan.md")).sort();
3631
- return files[0] ? path26.join(plansDir, files[0]) : null;
3832
+ return files[0] ? path28.join(plansDir, files[0]) : null;
3632
3833
  }
3633
3834
  async function readPlan(planPath) {
3634
3835
  return readFile12(planPath, "utf8");
@@ -3694,101 +3895,6 @@ function formatSentinelLine(sentinel) {
3694
3895
  return "";
3695
3896
  }
3696
3897
 
3697
- // src/plan-loop/skin-banners.ts
3698
- import path27 from "path";
3699
- import {
3700
- blue,
3701
- cyan as cyan2,
3702
- gray,
3703
- green as green2,
3704
- lightGray,
3705
- lightGreen,
3706
- magenta,
3707
- red as red2,
3708
- white,
3709
- yellow as yellow2
3710
- } from "kolorist";
3711
- var CLI_RUN_PLAN_MODE = "cli-run-plan";
3712
- var DEFAULT_CLI_SKIN_ID = "ghost-runner";
3713
- var COLORS = {
3714
- white,
3715
- gray,
3716
- green: green2,
3717
- cyan: cyan2,
3718
- magenta,
3719
- yellow: yellow2,
3720
- red: red2,
3721
- blue,
3722
- "light-green": lightGreen,
3723
- lightgreen: lightGreen,
3724
- "light-gray": lightGray,
3725
- lightgray: lightGray
3726
- };
3727
- function resolveColor(name, fallback) {
3728
- if (!name) return fallback;
3729
- return COLORS[name.toLowerCase()] ?? fallback;
3730
- }
3731
- async function resolveCliSkinId(root) {
3732
- try {
3733
- const cfg = await readJson(
3734
- path27.join(root, ".cursor", "context", "config.json")
3735
- );
3736
- const id = cfg?.workspaceSkin?.modes?.[CLI_RUN_PLAN_MODE];
3737
- if (typeof id === "string" && id.trim()) return id.trim();
3738
- } catch {
3739
- }
3740
- return DEFAULT_CLI_SKIN_ID;
3741
- }
3742
- async function loadSkinPack(root, skinId) {
3743
- try {
3744
- const skinPath = path27.join(root, "registry", "skins", "core", skinId, "skin.json");
3745
- const pack = await readJson(skinPath);
3746
- if (!pack || typeof pack.id !== "string") return null;
3747
- return pack;
3748
- } catch {
3749
- return null;
3750
- }
3751
- }
3752
- async function loadCliRunPlanSkin(root) {
3753
- const id = await resolveCliSkinId(root);
3754
- return loadSkinPack(root, id);
3755
- }
3756
- function createSkinBannerPrinter(skin) {
3757
- if (!skin?.cliBanners) return null;
3758
- const banners = skin.cliBanners;
3759
- if (!banners.tickStart && !banners.tickEnd && !banners.phaseComplete) return null;
3760
- const primary = resolveColor(skin.ansiPalette?.primary, white);
3761
- const secondary = resolveColor(skin.ansiPalette?.secondary, gray);
3762
- const accent = resolveColor(skin.ansiPalette?.accent, green2);
3763
- return {
3764
- tickStart(detail) {
3765
- if (banners.tickStart) {
3766
- console.log(`${accent(banners.tickStart)} ${primary(detail)}`);
3767
- } else {
3768
- console.log(primary(detail));
3769
- }
3770
- },
3771
- tickEnd(detail) {
3772
- if (!banners.tickEnd) return;
3773
- const line = detail ? `${banners.tickEnd} ${detail}` : banners.tickEnd;
3774
- console.log(secondary(line));
3775
- },
3776
- phaseComplete(detail) {
3777
- if (!banners.phaseComplete) return;
3778
- const line = detail ? `${banners.phaseComplete} ${detail}` : banners.phaseComplete;
3779
- console.log(accent(line));
3780
- },
3781
- sleep(seconds) {
3782
- console.log(secondary(`sleep ${seconds}s`));
3783
- },
3784
- stop(reason) {
3785
- if (banners.tickEnd) {
3786
- console.log(secondary(`${banners.tickEnd} ${reason}`));
3787
- }
3788
- }
3789
- };
3790
- }
3791
-
3792
3898
  // src/plan-loop/run-loop.ts
3793
3899
  var TICK_PROMPT = '/run-plan - single tick from the headless runner (agent-kit run-plan). Read .cursor/HANDOFF.md and the active plan in .cursor/plans/. Mark the next to-do as in_progress in the frontmatter, execute ONLY that to-do, mark completed and update HANDOFF. If there is a commitable diff: run /git-staging without asking for confirmation. NEVER /git-prod. Do NOT re-arm an internal Loop skill - the external runner starts the next agent. End the response with exactly one line: "LOOP_TICK_RESULT: continue" if implementable to-dos remain, or "LOOP_TICK_RESULT: stop - <reason>" (plan exhausted, external blocker, or human decision needed).';
3794
3900
  function stamp() {
@@ -3800,9 +3906,9 @@ function sleep(ms) {
3800
3906
  return new Promise((r) => setTimeout(r, ms));
3801
3907
  }
3802
3908
  async function runPlanLoop(opts) {
3803
- const plansDir = path28.join(opts.root, ".cursor", "plans");
3804
- const stopFile = path28.join(opts.root, ".cursor", "loop.stop");
3805
- const logDir = path28.join(opts.root, ".cursor", "loop-logs");
3909
+ const plansDir = path29.join(opts.root, ".cursor", "plans");
3910
+ const stopFile = path29.join(opts.root, ".cursor", "loop.stop");
3911
+ const logDir = path29.join(opts.root, ".cursor", "loop-logs");
3806
3912
  const planPath = await findActivePlanFile(plansDir);
3807
3913
  if (!planPath) {
3808
3914
  logger.error("No active plan in .cursor/plans/");
@@ -3821,13 +3927,13 @@ async function runPlanLoop(opts) {
3821
3927
  };
3822
3928
  process.on("SIGINT", onSigInt);
3823
3929
  try {
3824
- const skin = await loadCliRunPlanSkin(opts.root);
3825
- const banners = createSkinBannerPrinter(skin);
3826
- console.log(`Active plan: ${path28.basename(planPath)}`);
3930
+ const persona = await loadCliRunPlanPersona(opts.root);
3931
+ const banners = createPersonaBannerPrinter(persona);
3932
+ console.log(`Active plan: ${path29.basename(planPath)}`);
3827
3933
  console.log(`Pending to-dos: ${await pending()} | max ticks: ${opts.maxTicks}`);
3828
3934
  console.log(`Backend: ${opts.backend.id}`);
3829
- if (skin) {
3830
- console.log(`CLI skin: ${skin.displayName ?? skin.id}`);
3935
+ if (persona) {
3936
+ console.log(`CLI persona: ${persona.displayName ?? persona.id}`);
3831
3937
  }
3832
3938
  if (opts.dryRun) {
3833
3939
  console.log("--dry-run: no agent will be started. Tick prompt:");
@@ -3866,8 +3972,8 @@ async function runPlanLoop(opts) {
3866
3972
  planExhausted = true;
3867
3973
  break;
3868
3974
  }
3869
- const logPath = path28.join(logDir, `tick-${stamp()}.log`);
3870
- const relLog = path28.relative(opts.root, logPath);
3975
+ const logPath = path29.join(logDir, `tick-${stamp()}.log`);
3976
+ const relLog = path29.relative(opts.root, logPath);
3871
3977
  console.log("");
3872
3978
  const tickLine = `=== tick ${tick}/${opts.maxTicks} - pending: ${before} - log: ${relLog} ===`;
3873
3979
  if (banners) banners.tickStart(tickLine);
@@ -3943,7 +4049,7 @@ async function runPlanLoop(opts) {
3943
4049
  const finishDetail = `after ${tick} tick(s); pending: ${pendingNow}`;
3944
4050
  if (banners) banners.phaseComplete(finishDetail);
3945
4051
  console.log(
3946
- `Loop finished after ${tick} tick(s). Pending now: ${pendingNow}. Logs in ${path28.relative(opts.root, logDir)}/`
4052
+ `Loop finished after ${tick} tick(s). Pending now: ${pendingNow}. Logs in ${path29.relative(opts.root, logDir)}/`
3947
4053
  );
3948
4054
  if (planExhausted || shouldArmExternalPlanReview({ pending: pendingNow, stopReason })) {
3949
4055
  await armExternalPlanReview(opts.root);
@@ -3955,7 +4061,7 @@ async function runPlanLoop(opts) {
3955
4061
  }
3956
4062
 
3957
4063
  // src/commands/run-plan.ts
3958
- var runPlanCommand = defineCommand9({
4064
+ var runPlanCommand = defineCommand10({
3959
4065
  meta: {
3960
4066
  name: "run-plan",
3961
4067
  description: "Headless continuous plan runner: one fresh agent per tick (LOOP_TICK_RESULT contract). Never git-prod."
@@ -4014,7 +4120,7 @@ var runPlanCommand = defineCommand9({
4014
4120
  return;
4015
4121
  }
4016
4122
  const code = await runPlanLoop({
4017
- root: path29.resolve(args.cwd),
4123
+ root: path30.resolve(args.cwd),
4018
4124
  maxTicks,
4019
4125
  sleepSeconds,
4020
4126
  model: args.model ? String(args.model) : void 0,
@@ -4026,8 +4132,8 @@ var runPlanCommand = defineCommand9({
4026
4132
  });
4027
4133
 
4028
4134
  // src/commands/scan.ts
4029
- import { defineCommand as defineCommand10 } from "citty";
4030
- var scanCommand = defineCommand10({
4135
+ import { defineCommand as defineCommand11 } from "citty";
4136
+ var scanCommand = defineCommand11({
4031
4137
  meta: {
4032
4138
  name: "scan",
4033
4139
  description: "Scan the current repository and print detected profile."
@@ -4048,8 +4154,8 @@ var scanCommand = defineCommand10({
4048
4154
  });
4049
4155
 
4050
4156
  // src/commands/status.ts
4051
- import path30 from "path";
4052
- import { defineCommand as defineCommand11 } from "citty";
4157
+ import path31 from "path";
4158
+ import { defineCommand as defineCommand12 } from "citty";
4053
4159
  function profileStatus(profile) {
4054
4160
  if (!profile) return { origin: "none", evidence: [], profile: null };
4055
4161
  if ("detection" in profile && profile.detection && typeof profile.detection === "object") {
@@ -4062,7 +4168,7 @@ function profileStatus(profile) {
4062
4168
  }
4063
4169
  return { origin: "legacy-wizard", evidence: [], profile };
4064
4170
  }
4065
- var statusCommand = defineCommand11({
4171
+ var statusCommand = defineCommand12({
4066
4172
  meta: {
4067
4173
  name: "status",
4068
4174
  description: "Show Agent Kit distribution status (manifest + optional wizard profile)."
@@ -4079,11 +4185,11 @@ var statusCommand = defineCommand11({
4079
4185
  }
4080
4186
  },
4081
4187
  async run({ args }) {
4082
- const rootDir = path30.resolve(args.cwd);
4188
+ const rootDir = path31.resolve(args.cwd);
4083
4189
  const [manifest, rawProfile, scan] = await Promise.all([
4084
4190
  loadAgentKitManifest(rootDir),
4085
4191
  readJson(
4086
- path30.join(rootDir, ".cursor", "agent-kit.config.json")
4192
+ path31.join(rootDir, ".cursor", "agent-kit.config.json")
4087
4193
  ),
4088
4194
  runScanner(rootDir)
4089
4195
  ]);
@@ -4138,20 +4244,297 @@ var statusCommand = defineCommand11({
4138
4244
  });
4139
4245
 
4140
4246
  // src/commands/update.ts
4141
- import { defineCommand as defineCommand12 } from "citty";
4142
- var updateCommand = defineCommand12({
4247
+ import { defineCommand as defineCommand13 } from "citty";
4248
+
4249
+ // src/lifecycle/check-updates.ts
4250
+ import { execFile as execFile3 } from "child_process";
4251
+ import path32 from "path";
4252
+ import { promisify as promisify3 } from "util";
4253
+ var execFileAsync2 = promisify3(execFile3);
4254
+ var SEMVER_CORE = /^v?(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/i;
4255
+ var FACTORY_URL_MARKERS = ["agent-kit-dev"];
4256
+ var FACTORY_REFS = /* @__PURE__ */ new Set(["staging", "homologacao", "develop", "dev"]);
4257
+ var DEFAULT_UPDATE_CHECK = {
4258
+ enabled: false,
4259
+ intervalDays: 7,
4260
+ lastCheckedAt: null
4261
+ };
4262
+ function gitEnv2() {
4263
+ return { ...process.env, GIT_ALLOW_PROTOCOL: "https" };
4264
+ }
4265
+ function compareSemver(a, b) {
4266
+ const pa = parseSemverParts(a);
4267
+ const pb = parseSemverParts(b);
4268
+ if (!pa || !pb) {
4269
+ throw new Error(`Invalid semver for compare: "${a}" vs "${b}"`);
4270
+ }
4271
+ for (let i = 0; i < 3; i++) {
4272
+ const d = (pa[i] ?? 0) - (pb[i] ?? 0);
4273
+ if (d !== 0) return d < 0 ? -1 : 1;
4274
+ }
4275
+ return 0;
4276
+ }
4277
+ function parseSemverParts(version) {
4278
+ const m = SEMVER_CORE.exec(version.trim());
4279
+ if (!m) return null;
4280
+ return [Number(m[1]), Number(m[2]), Number(m[3])];
4281
+ }
4282
+ function normalizeSemver(version) {
4283
+ const parts = parseSemverParts(version);
4284
+ if (!parts) return null;
4285
+ return `${parts[0]}.${parts[1]}.${parts[2]}`;
4286
+ }
4287
+ function isFactoryOrDevRegistry(url, ref) {
4288
+ if (url) {
4289
+ const lower = url.toLowerCase();
4290
+ if (FACTORY_URL_MARKERS.some((m) => lower.includes(m))) return true;
4291
+ }
4292
+ if (ref && FACTORY_REFS.has(ref.toLowerCase())) return true;
4293
+ return false;
4294
+ }
4295
+ function pickLatestSemverTag(lsRemoteStdout) {
4296
+ let best = null;
4297
+ for (const line of lsRemoteStdout.split("\n")) {
4298
+ const trimmed = line.trim();
4299
+ if (!trimmed) continue;
4300
+ const ref = trimmed.split(/\s+/)[1];
4301
+ if (!ref?.startsWith("refs/tags/")) continue;
4302
+ if (ref.endsWith("^{}")) continue;
4303
+ const tag = ref.slice("refs/tags/".length);
4304
+ const norm = normalizeSemver(tag);
4305
+ if (!norm) continue;
4306
+ if (!best || compareSemver(norm, best) > 0) best = norm;
4307
+ }
4308
+ return best;
4309
+ }
4310
+ async function fetchLatestPublicVersion(registryUrl = DEFAULT_REGISTRY_URL) {
4311
+ assertSafeRegistrySource(registryUrl, "main");
4312
+ const { stdout } = await execFileAsync2("git", ["ls-remote", "--tags", "--", registryUrl], {
4313
+ env: gitEnv2(),
4314
+ timeout: 2e4
4315
+ });
4316
+ return pickLatestSemverTag(stdout);
4317
+ }
4318
+ function readUpdateCheckPrefs(config) {
4319
+ if (!config || typeof config !== "object" || Array.isArray(config)) {
4320
+ return { ...DEFAULT_UPDATE_CHECK };
4321
+ }
4322
+ const raw = config.updateCheck;
4323
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
4324
+ return { ...DEFAULT_UPDATE_CHECK };
4325
+ }
4326
+ const uc = raw;
4327
+ const intervalDays = typeof uc.intervalDays === "number" && Number.isInteger(uc.intervalDays) && uc.intervalDays >= 1 ? uc.intervalDays : DEFAULT_UPDATE_CHECK.intervalDays;
4328
+ return {
4329
+ enabled: uc.enabled === true,
4330
+ intervalDays,
4331
+ lastCheckedAt: typeof uc.lastCheckedAt === "string" ? uc.lastCheckedAt : null
4332
+ };
4333
+ }
4334
+ function intervalElapsed(lastCheckedAt, intervalDays) {
4335
+ if (!lastCheckedAt) return true;
4336
+ const last = Date.parse(lastCheckedAt);
4337
+ if (Number.isNaN(last)) return true;
4338
+ const ms = intervalDays * 24 * 60 * 60 * 1e3;
4339
+ return Date.now() - last >= ms;
4340
+ }
4341
+ async function loadContextConfig(cwd) {
4342
+ const configPath = path32.join(cwd, ".cursor", "context", "config.json");
4343
+ return readJson(configPath);
4344
+ }
4345
+ async function stampLastCheckedAt(cwd) {
4346
+ const configPath = path32.join(cwd, ".cursor", "context", "config.json");
4347
+ const existing = await loadContextConfig(cwd) ?? {};
4348
+ const prev = existing.updateCheck && typeof existing.updateCheck === "object" ? { ...existing.updateCheck } : {};
4349
+ existing.updateCheck = {
4350
+ ...DEFAULT_UPDATE_CHECK,
4351
+ ...prev,
4352
+ lastCheckedAt: (/* @__PURE__ */ new Date()).toISOString()
4353
+ };
4354
+ await writeJson(configPath, existing);
4355
+ }
4356
+ async function checkForUpdates(cwd, options = {}) {
4357
+ const manifest = await loadAgentKitManifest(cwd);
4358
+ if (!manifest) {
4359
+ return {
4360
+ status: "skipped-no-manifest",
4361
+ installedVersion: null,
4362
+ latestVersion: null,
4363
+ registryUrl: null,
4364
+ registryRef: null,
4365
+ applyRecommended: false,
4366
+ message: "No .cursor/agent-kit.json \u2014 run agent-kit install first."
4367
+ };
4368
+ }
4369
+ const registryUrl = manifest.registry?.url ?? null;
4370
+ const registryRef = manifest.registry?.ref ?? null;
4371
+ if (isFactoryOrDevRegistry(registryUrl, registryRef)) {
4372
+ return {
4373
+ status: "skipped-factory",
4374
+ installedVersion: manifest.version,
4375
+ latestVersion: null,
4376
+ registryUrl,
4377
+ registryRef,
4378
+ applyRecommended: false,
4379
+ message: "Factory/dev registry detected \u2014 skipping public update check. Use /update only with explicit consumer intent."
4380
+ };
4381
+ }
4382
+ if (options.respectPrefs) {
4383
+ const config = await loadContextConfig(cwd);
4384
+ const prefs = readUpdateCheckPrefs(config);
4385
+ if (!prefs.enabled) {
4386
+ return {
4387
+ status: "skipped-disabled",
4388
+ installedVersion: manifest.version,
4389
+ latestVersion: null,
4390
+ registryUrl,
4391
+ registryRef,
4392
+ applyRecommended: false,
4393
+ message: "updateCheck.enabled is false (opt-in). Set true in .cursor/context/config.json to nudge."
4394
+ };
4395
+ }
4396
+ if (!intervalElapsed(prefs.lastCheckedAt, prefs.intervalDays)) {
4397
+ return {
4398
+ status: "skipped-interval",
4399
+ installedVersion: manifest.version,
4400
+ latestVersion: null,
4401
+ registryUrl,
4402
+ registryRef,
4403
+ applyRecommended: false,
4404
+ message: `Within updateCheck.intervalDays (${prefs.intervalDays}); last check ${prefs.lastCheckedAt}.`
4405
+ };
4406
+ }
4407
+ }
4408
+ let latest;
4409
+ try {
4410
+ latest = options.latestVersion !== void 0 ? options.latestVersion : await fetchLatestPublicVersion(options.publicRegistryUrl ?? DEFAULT_REGISTRY_URL);
4411
+ } catch (err) {
4412
+ const msg = err instanceof Error ? err.message : String(err);
4413
+ return {
4414
+ status: "error",
4415
+ installedVersion: manifest.version,
4416
+ latestVersion: null,
4417
+ registryUrl,
4418
+ registryRef,
4419
+ applyRecommended: false,
4420
+ message: `Failed to fetch public tags: ${msg}`
4421
+ };
4422
+ }
4423
+ if (options.stamp) {
4424
+ try {
4425
+ await stampLastCheckedAt(cwd);
4426
+ } catch {
4427
+ }
4428
+ }
4429
+ const installed = normalizeSemver(manifest.version);
4430
+ if (!installed) {
4431
+ return {
4432
+ status: "error",
4433
+ installedVersion: manifest.version,
4434
+ latestVersion: latest,
4435
+ registryUrl,
4436
+ registryRef,
4437
+ applyRecommended: false,
4438
+ message: `Installed version "${manifest.version}" is not valid semver.`
4439
+ };
4440
+ }
4441
+ if (!latest) {
4442
+ return {
4443
+ status: "error",
4444
+ installedVersion: installed,
4445
+ latestVersion: null,
4446
+ registryUrl,
4447
+ registryRef,
4448
+ applyRecommended: false,
4449
+ message: "No semver tags found on the public registry."
4450
+ };
4451
+ }
4452
+ const cmp = compareSemver(installed, latest);
4453
+ if (cmp === 0) {
4454
+ return {
4455
+ status: "up-to-date",
4456
+ installedVersion: installed,
4457
+ latestVersion: latest,
4458
+ registryUrl,
4459
+ registryRef,
4460
+ applyRecommended: false,
4461
+ message: `Installed v${installed} matches latest public v${latest}.`
4462
+ };
4463
+ }
4464
+ if (cmp < 0) {
4465
+ return {
4466
+ status: "update-available",
4467
+ installedVersion: installed,
4468
+ latestVersion: latest,
4469
+ registryUrl,
4470
+ registryRef,
4471
+ applyRecommended: false,
4472
+ message: `Update available: v${installed} \u2192 v${latest}. Run /update (Ask confirm) to apply; never silent.`
4473
+ };
4474
+ }
4475
+ return {
4476
+ status: "ahead-of-public",
4477
+ installedVersion: installed,
4478
+ latestVersion: latest,
4479
+ registryUrl,
4480
+ registryRef,
4481
+ applyRecommended: false,
4482
+ message: `Installed v${installed} is ahead of latest public v${latest} (dev/local build).`
4483
+ };
4484
+ }
4485
+
4486
+ // src/commands/update.ts
4487
+ var updateCommand = defineCommand13({
4143
4488
  meta: {
4144
4489
  name: "update",
4145
- description: "Re-apply L0/packs/skills from the registry; never overwrites L3 protected paths."
4490
+ description: "Re-apply L0/packs/skills from the registry; never overwrites L3 protected paths. Use --check for notify-only."
4146
4491
  },
4147
4492
  args: {
4148
4493
  cwd: {
4149
4494
  type: "string",
4150
4495
  default: process.cwd()
4151
4496
  },
4497
+ check: {
4498
+ type: "boolean",
4499
+ description: "Check-only: compare installed version to latest public tag; never apply L0 writes",
4500
+ default: false
4501
+ },
4502
+ json: {
4503
+ type: "boolean",
4504
+ description: "Print machine-readable JSON (with --check)",
4505
+ default: false
4506
+ },
4507
+ "respect-prefs": {
4508
+ type: "boolean",
4509
+ description: "Honor updateCheck.enabled and intervalDays from .cursor/context/config.json (hooks)",
4510
+ default: false
4511
+ },
4512
+ stamp: {
4513
+ type: "boolean",
4514
+ description: "Persist updateCheck.lastCheckedAt after a network check",
4515
+ default: false
4516
+ },
4152
4517
  ...REGISTRY_CLI_ARGS
4153
4518
  },
4154
4519
  async run({ args }) {
4520
+ if (args.check) {
4521
+ const result = await checkForUpdates(args.cwd, {
4522
+ respectPrefs: Boolean(args["respect-prefs"]),
4523
+ stamp: Boolean(args.stamp),
4524
+ publicRegistryUrl: typeof args.url === "string" && args.url ? args.url : void 0
4525
+ });
4526
+ if (args.json) {
4527
+ console.log(JSON.stringify(result, null, 2));
4528
+ } else {
4529
+ const line = `[${result.status}] ${result.message}`;
4530
+ if (result.status === "error") logger.error(line);
4531
+ else if (result.status === "update-available") logger.warn(line);
4532
+ else if (result.status.startsWith("skipped-")) logger.warn(line);
4533
+ else logger.info(line);
4534
+ }
4535
+ if (result.status === "error") process.exitCode = 2;
4536
+ return;
4537
+ }
4155
4538
  const existing = await loadAgentKitManifest(args.cwd);
4156
4539
  if (!existing) {
4157
4540
  logger.warn("No .cursor/agent-kit.json \u2014 run agent-kit install first.");
@@ -4184,7 +4567,7 @@ var updateCommand = defineCommand12({
4184
4567
  });
4185
4568
 
4186
4569
  // src/index.ts
4187
- var main = defineCommand13({
4570
+ var main = defineCommand14({
4188
4571
  meta: {
4189
4572
  name: "agent-kit",
4190
4573
  description: "HITL framework for AI-assisted IDEs"
@@ -4201,7 +4584,8 @@ var main = defineCommand13({
4201
4584
  contribute: contributeCommand,
4202
4585
  handoff: handoffCommand,
4203
4586
  "run-plan": runPlanCommand,
4204
- dashboard: dashboardCommand
4587
+ dashboard: dashboardCommand,
4588
+ "dashboard-broadcast": dashboardBroadcastCommand
4205
4589
  }
4206
4590
  });
4207
4591
  runMain(main);