@dadado/agent-kit-cli 5.0.0 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -72,8 +72,11 @@ var KNOWN_SHIPPED_OVERLAY_HASHES = /* @__PURE__ */ new Set([
72
72
  "004d98f557942677264d053dcd8335f2bbcf19c5591123ec16a7a2697cb88932",
73
73
  "05ee6ecde4215e33ace0ee152ffbf2cd885d2bd4b032900f0f0bb001d350ef0f",
74
74
  "082bdbc584be3f3d8aa6e9d7b9f4e076450f3ff48f9147a1deb557e31475036b",
75
+ "09850afc201eccb3e700cb934400d49c9cbe7510d4a546ee84ee519771a8200e",
75
76
  "0b5fe4729c3c68654434837630d775b847c082418f25b0b2c581700fe84b797a",
76
77
  "1106b279558b6a20f87951102f172a680976f3e6530855d5dad083e1f89b9ba3",
78
+ "12bed56b332406477cf546d9e8360d3cab129bd82a638bea356726d00a6847a3",
79
+ "13f6a0711a2fcadcc7f1b751772448517ce4871cdef6a0269fc26dad51ebe26d",
77
80
  "15566765ec95dad3bc3160f20ac910e01a13471df536e6d990f4b263700784a9",
78
81
  "1681e6cf7b80a67cd999b94b8b6e16eb8caed0e11b32905848ba733da956e902",
79
82
  "17818e47e06349ee35c1d8e1cc16e8bcbbbcc89c664014e9096e1f08c8567807",
@@ -83,6 +86,7 @@ var KNOWN_SHIPPED_OVERLAY_HASHES = /* @__PURE__ */ new Set([
83
86
  "1c5c1af87951367ad04bb6fa5b1d6ebaaefbc0ee95989b624cabf406a1869907",
84
87
  "1ded5c7b7eefc355fc1d17feb65ec472e35ed96076e47ce95f7bff5c1b84746c",
85
88
  "1e58e0d4f2e8459b95a40d1b1e72c5c85a87230cbde945840a84073fe3a20422",
89
+ "2280e3ca96129ab2de8a85a301df7c2b15ffaa6204b734ed3b1f303e0055c41d",
86
90
  "285668d08f1b15d96286d27e29cc4a67e126e097fb8a940bdde33ccf25bd2bcd",
87
91
  "2acdb148b07f3a4872b896da419af796fd3fefaace7ac79cdee0aab447004a83",
88
92
  "2cd7fc018a384b0188737259ecf593ce25b4b75cc38b3e410063ac91745894b9",
@@ -95,6 +99,7 @@ var KNOWN_SHIPPED_OVERLAY_HASHES = /* @__PURE__ */ new Set([
95
99
  "46a1dd4a07a2190ab9fb09abc05a3962277209f8e0c28033bb41450301474c86",
96
100
  "48a0d55e21132a305946b195db7f6b75da78d44a1d32166fb0c69bef4a5ffbe2",
97
101
  "4b62a46c74e5fa6a80283f21e8e6711958fbcd577051dc73ff4050643a031475",
102
+ "4ba6e319ac79455a7d2713674f64fabbc9cd401b5d26b1c0edbd19c27a52297e",
98
103
  "4bf23acbc9bd4e0f8468e61f4970e182600b72d9025d29e81ddbd83cb7b1710a",
99
104
  "4c5167d0ebde685f266dce32c516a7c43876e0520647b93f07f2e2147e6a7b2f",
100
105
  "4dc4efcd1b43c643d3889fb349df8d4964d0867c3a67eb67e2ec168fbb70fc75",
@@ -113,11 +118,16 @@ var KNOWN_SHIPPED_OVERLAY_HASHES = /* @__PURE__ */ new Set([
113
118
  "6994595e3a62edb6c518006a6bece6848c7077977c041439cf7c822b89304a14",
114
119
  "6a42e76fc038ca69ddad18541b8df161770c58980fefaf9e5e80d87407074bec",
115
120
  "6a9af9e8a95dbb279347166c43dcee245ca673f9687841086b49911c54944c26",
121
+ "99fdd85310e658a1c1b0e8f92257493409c28c3d693f24deeaa665861788e1e4",
122
+ // public registry SoT n8n-workflows/SKILL.md (em dash description)
116
123
  "6b66ed7747ea2d19cb457f5af656e3c07f9f563f7f32b3951fa4ae5f27f0ac46",
117
124
  "6dede0588274f5b44d833bcc696ec4776eb978ed24a20efdcc9c38caf3df1576",
118
125
  "71991a10972b8b956cae93c88517e31741c95e351814082df65e44f813590210",
126
+ "75ae3018a70ed838428c0e63e8b6901ac05bf1f1cf1845faa6f73c3940d22e4a",
119
127
  "75e591bfde61ca1ca3ca1303cc9bc487197af2f713c68f346a46e94ca9432fd5",
120
128
  "76ae85bbc336c6f435d9653e8f254ae49774b4ebaecf7335697115933eb8797c",
129
+ "76c4361e70a73276771e841e2c939253b4124d690a0fd25aff2a3ac636d8f4b0",
130
+ // mission-kit-comms/SKILL.md
121
131
  "79878f0f6f59d1a0b65d94011c0cafe7b6f8e03e89f568aa638b8ba7a2c34076",
122
132
  "8108b202c829627c3c2df33575016ebe942df42acf4f1cc393900be3b82b402c",
123
133
  "819b7bd3ba9e8bf12dcd4f81a76c98e48261b34c397f339b36d36eb364d9c3f4",
@@ -126,6 +136,7 @@ var KNOWN_SHIPPED_OVERLAY_HASHES = /* @__PURE__ */ new Set([
126
136
  "8a11c80b0fbf6c4aabf60eec05b8cca7dccf5bca472787a8afa3028fd8fbedfa",
127
137
  "8b25659727063013e8bf6d7925f815b2630c2919b21e6fdac354362bf122bfab",
128
138
  "8c7edbacc74b1431fbecb04c9857c0e7a9cb1578fc6ca6367be888dbcec3c98f",
139
+ "8d8d60e929ebf717eed3c1a29ee0de7c71ae917d125eb1b220a77220597281ed",
129
140
  "8ffc181bb152c68a3353309bfdc87c42bdb925b98675cdba959c0cdb90b2adea",
130
141
  "91ce0c933e3c8ee81927b8224f944ec5ce5445098a2ad154773092e3541dbf69",
131
142
  "9309af07a0a99a56143386b3ddd2a35fae29378fba3f6b4c9297a5d1c02a9724",
@@ -137,6 +148,7 @@ var KNOWN_SHIPPED_OVERLAY_HASHES = /* @__PURE__ */ new Set([
137
148
  "9ccf52fe53c6fa4d731650ab4928ce50e201fa34f3e174fee70e3d29867fef2a",
138
149
  "9d905aca0123f3018c81c579217104f1749d8da802f3837b2407fbad8f2d803f",
139
150
  "9e324c52c1a9d1ceee738bccf47da92910aca9f7a5a21cc41738d9573c414830",
151
+ "9eb457654167df562481637b25ecd91f0ff3c9282ba79409089d644d3b09b926",
140
152
  "a013a6a476160183d6457ff6c1c51ec228bdca359b11eb15cd9b7fc177a705db",
141
153
  "a08d04f8efa121ad498f96873778c22630c9172c07c216839e348cd0bb7c2292",
142
154
  "a7cd7058bcb6fbd962c37317fa16893bc86a270d0164af5a0e6f3f026aa408ad",
@@ -145,6 +157,8 @@ var KNOWN_SHIPPED_OVERLAY_HASHES = /* @__PURE__ */ new Set([
145
157
  "b5c7a10df90d209be07159b7ff9bfa24e869538a0b6026bb5ce79e1cdd5b2528",
146
158
  "bd9fd1efacbd132c5dafc12cd0cf2b651a2b1fab7b2d7a9ba4a3e44128097f48",
147
159
  "be964ff33a40280e13ce6071fb3e30ce87c374527efbb74c25c9d4534084a634",
160
+ "c245035b539e41e822fab8c74fcde3b18c45f4b9f26f1c8583fc9e7254249698",
161
+ "c794accc6668d74a739c982c1b63985a8be4be08afe3766214ab1e08d25ea95c",
148
162
  "c96d522be440fbb638dbe953cebf1bb31185c431ac2e14446369cfb7d155a951",
149
163
  "cf2bd11891b934484171fa705f470de284e3874402e1e487750735dbc7692857",
150
164
  "d0e3bf82efbe32c09761689802ffea7838046dbdd4803b88ead5ec3dfc7efb7e",
@@ -174,11 +188,15 @@ var KNOWN_SHIPPED_OVERLAY_HASHES = /* @__PURE__ */ new Set([
174
188
  "f061432a2606049717c0cbf098ef07b1da4df2b136c4cb9cdaa7ea44c876bcd6",
175
189
  "f061d36a1180293519fbb560bbf659c621bd90885e995fca6eaf2eda9e418a50",
176
190
  "f28731a82f4b394a5765fdefe2d1b667810831f6dcea400ca62bc66e1d65371c",
191
+ "f43a5484e39aca4e6af86e7f8d2941773cdc1c960838ad88ae3540c49a1097df",
177
192
  "f46c108457e4fe12953dca75b409e06fca9cb264baffabe0c0d04c89af471e56",
178
193
  "f6bc17efa746680a44bbc49be67ac3f924d9c306c6d937f9959c57a1d3c5b9af",
179
194
  "f981764422d468567b5aff31148dc659aeee22cb13dc0ecd873d737deaf07372",
180
195
  "fa306a0cdb0f40c817e32164cd03b946564a02b4b7f7c3f4f0b9513096584d28",
181
- "fa5cf460eb314437081f7cea30dc8041c0bd6fc3f560a74bc2d7be1bf07384b0"
196
+ "fa5cf460eb314437081f7cea30dc8041c0bd6fc3f560a74bc2d7be1bf07384b0",
197
+ "fc39ec6d8a22498697f968ffc0fe5f717bed97afd68f922c76a80ed1f10d6579",
198
+ "a8756742197c3a2e1a6d64f4bde2a88db48abb66a0f449d625cd2e1c7b8d0cb4",
199
+ "6a5f8795a4a26b419f167e249576b798781c95308dc1ea50958351de713231d4"
182
200
  ]);
183
201
 
184
202
  // src/lifecycle/paths.ts
@@ -572,18 +590,36 @@ async function readLockOwner(lockDir) {
572
590
  }
573
591
  async function writeLockOwner(lockDir, owner) {
574
592
  const finalPath = lockOwnerPath(lockDir);
575
- const tmpPath = path5.join(lockDir, `owner.${owner.uuid}.tmp`);
593
+ const tmpPath = path5.join(lockDir, `owner.${owner.uuid}.${randomUUID()}.tmp`);
576
594
  await writeFile3(tmpPath, JSON.stringify(owner), "utf8");
577
595
  await rename(tmpPath, finalPath);
578
596
  }
579
597
  async function refreshLockOwner(lockDir, uuid) {
580
598
  const owner = await readLockOwner(lockDir);
581
- if (!owner || owner.uuid !== uuid) return;
582
- await writeLockOwner(lockDir, { ...owner, updatedAt: Date.now() });
599
+ if (owner && owner.uuid !== uuid) return;
600
+ await writeLockOwner(lockDir, { pid: process.pid, uuid, updatedAt: Date.now() });
583
601
  }
584
602
  async function releaseCacheLock(lockDir, uuid) {
585
- const owner = await readLockOwner(lockDir);
586
- if (!owner || owner.uuid !== uuid) {
603
+ const claimPath = path5.join(lockDir, `releasing.${uuid}`);
604
+ try {
605
+ await rename(lockOwnerPath(lockDir), claimPath);
606
+ } catch {
607
+ return;
608
+ }
609
+ let claimed = null;
610
+ try {
611
+ const raw = await readFile4(claimPath, "utf8");
612
+ const parsed = JSON.parse(raw);
613
+ if (typeof parsed.pid === "number" && typeof parsed.uuid === "string") {
614
+ claimed = parsed;
615
+ }
616
+ } catch {
617
+ }
618
+ if (!claimed || claimed.uuid !== uuid) {
619
+ try {
620
+ await rename(claimPath, lockOwnerPath(lockDir));
621
+ } catch {
622
+ }
587
623
  return;
588
624
  }
589
625
  try {
@@ -622,12 +658,18 @@ async function acquireCacheLock(cacheDir) {
622
658
  const uuid = randomUUID();
623
659
  const owner = { pid: process.pid, uuid, updatedAt: Date.now() };
624
660
  await mkdir4(path5.dirname(lockDir), { recursive: true });
661
+ let lastErrorCode;
625
662
  while (Date.now() < deadline) {
626
663
  try {
627
664
  await mkdir4(lockDir, { recursive: false });
628
665
  await writeLockOwner(lockDir, owner);
666
+ let refreshing = false;
629
667
  const refreshInterval = setInterval(() => {
668
+ if (refreshing) return;
669
+ refreshing = true;
630
670
  refreshLockOwner(lockDir, uuid).catch(() => {
671
+ }).finally(() => {
672
+ refreshing = false;
631
673
  });
632
674
  }, LOCK_REFRESH_MS);
633
675
  return async () => {
@@ -636,6 +678,7 @@ async function acquireCacheLock(cacheDir) {
636
678
  };
637
679
  } catch (err) {
638
680
  const code = err.code;
681
+ lastErrorCode = code;
639
682
  if (code === "EEXIST") {
640
683
  if (await tryReclaimStaleLock(lockDir)) {
641
684
  continue;
@@ -644,12 +687,16 @@ async function acquireCacheLock(cacheDir) {
644
687
  continue;
645
688
  }
646
689
  if (code === "ENOENT") {
690
+ await mkdir4(path5.dirname(lockDir), { recursive: true });
691
+ await new Promise((r) => setTimeout(r, LOCK_RETRY_MS + Math.random() * 100));
647
692
  continue;
648
693
  }
649
694
  throw err;
650
695
  }
651
696
  }
652
- throw new Error(`Timed out waiting for cache lock on ${cacheDir}. Another install may be stuck.`);
697
+ throw new Error(
698
+ lastErrorCode === "ENOENT" ? `Timed out acquiring cache lock on ${cacheDir}: the lock parent directory kept vanishing (concurrent cache clear?).` : `Timed out waiting for cache lock on ${cacheDir}. Another install may be stuck.`
699
+ );
653
700
  }
654
701
  var DEFAULT_REGISTRY_URL = "https://github.com/agent-kit-startup/agent-kit";
655
702
  var DEFAULT_REGISTRY_REF = "main";
@@ -1835,6 +1882,7 @@ async function resolveInventoryRoot(cwd) {
1835
1882
  return dir;
1836
1883
  } catch {
1837
1884
  }
1885
+ if (await fileExists(path10.join(dir, ".git"))) break;
1838
1886
  const parent = path10.dirname(dir);
1839
1887
  if (parent === dir) break;
1840
1888
  dir = parent;
@@ -2011,12 +2059,16 @@ function baseResult(partial) {
2011
2059
  };
2012
2060
  }
2013
2061
  async function checkCursorUpdateAwareness(cwd, options = {}) {
2014
- const prefs = readCursorUpdateCheckPrefs(await loadContextConfig(cwd));
2062
+ const inventoryRoot = await resolveInventoryRoot(cwd);
2063
+ const prefs = readCursorUpdateCheckPrefs(
2064
+ inventoryRoot ? await loadContextConfig(inventoryRoot) : null
2065
+ );
2015
2066
  const changelogUrl = options.changelogUrl ?? prefs.changelogUrl;
2016
2067
  if (options.respectPrefs) {
2017
2068
  if (!prefs.enabled) {
2018
2069
  return baseResult({
2019
2070
  status: "skipped-disabled",
2071
+ inventoryRoot,
2020
2072
  inventoryPath: INVENTORY_REL,
2021
2073
  featuresPath: FEATURES_REL,
2022
2074
  changelogUrl,
@@ -2031,6 +2083,7 @@ async function checkCursorUpdateAwareness(cwd, options = {}) {
2031
2083
  if (!intervalElapsed(prefs.lastCheckedAt, prefs.intervalDays)) {
2032
2084
  return baseResult({
2033
2085
  status: "skipped-interval",
2086
+ inventoryRoot,
2034
2087
  inventoryPath: INVENTORY_REL,
2035
2088
  featuresPath: FEATURES_REL,
2036
2089
  changelogUrl,
@@ -2043,10 +2096,10 @@ async function checkCursorUpdateAwareness(cwd, options = {}) {
2043
2096
  });
2044
2097
  }
2045
2098
  }
2046
- const inventoryRoot = await resolveInventoryRoot(cwd);
2047
2099
  if (!inventoryRoot) {
2048
2100
  return baseResult({
2049
2101
  status: "error",
2102
+ inventoryRoot: null,
2050
2103
  inventoryPath: INVENTORY_REL,
2051
2104
  featuresPath: FEATURES_REL,
2052
2105
  changelogUrl,
@@ -2066,6 +2119,7 @@ async function checkCursorUpdateAwareness(cwd, options = {}) {
2066
2119
  } catch {
2067
2120
  return baseResult({
2068
2121
  status: "error",
2122
+ inventoryRoot,
2069
2123
  inventoryPath: INVENTORY_REL,
2070
2124
  featuresPath: FEATURES_REL,
2071
2125
  changelogUrl,
@@ -2150,6 +2204,7 @@ async function checkCursorUpdateAwareness(cwd, options = {}) {
2150
2204
  const msg = err instanceof Error ? err.message : String(err);
2151
2205
  return baseResult({
2152
2206
  status: "error",
2207
+ inventoryRoot,
2153
2208
  inventoryPath: INVENTORY_REL,
2154
2209
  featuresPath: FEATURES_REL,
2155
2210
  changelogUrl,
@@ -2162,8 +2217,8 @@ async function checkCursorUpdateAwareness(cwd, options = {}) {
2162
2217
  });
2163
2218
  }
2164
2219
  }
2165
- if (options.stamp) {
2166
- await stampCursorUpdateCheck(cwd, {
2220
+ if (options.stamp && inventoryRoot) {
2221
+ await stampCursorUpdateCheck(inventoryRoot, {
2167
2222
  lastSeenCursorVersion: latestCursorVersion ?? prefs.lastSeenCursorVersion
2168
2223
  });
2169
2224
  }
@@ -2171,6 +2226,7 @@ async function checkCursorUpdateAwareness(cwd, options = {}) {
2171
2226
  const message = status === "current" ? "No advisory Cursor-update gaps vs inventory (check-only)." : `Found ${gaps.length} advisory gap(s). ${CONVEYOR_HINT}`;
2172
2227
  return baseResult({
2173
2228
  status,
2229
+ inventoryRoot,
2174
2230
  inventoryPath: INVENTORY_REL,
2175
2231
  featuresPath: FEATURES_REL,
2176
2232
  changelogUrl: options.offline ? null : changelogUrl,
@@ -2270,7 +2326,7 @@ var KIT_ROOT_ENV_KEYS = Object.freeze(["MISSION_CONTROL_KIT_ROOT", "AGENT_KIT_HO
2270
2326
  var CONTEXT_CONFIG_REL = ".cursor/context/config.json";
2271
2327
  var CONFIG_PERSONA_IDS = Object.freeze(["autopilot", "night-shift", "ghost-runner"]);
2272
2328
  var CONFIG_PERSONA_MODES = Object.freeze(["continue-plan", "run-plan", "cli-run-plan"]);
2273
- var CONFIG_REVIEW_BACKENDS = Object.freeze(["claude"]);
2329
+ var CONFIG_REVIEW_BACKENDS = Object.freeze(["auto", "claude", "cursor"]);
2274
2330
  var CONFIG_REVIEW_MODES = Object.freeze(["paste", "autonomous"]);
2275
2331
  var CONFIG_REVIEW_PREFLIGHT = Object.freeze(["off", "warn", "block"]);
2276
2332
  function resolveContextConfigPath(repoRoot, fsHooks = {}) {
@@ -2367,6 +2423,14 @@ function applyDashboardOpenEnv(env, opts) {
2367
2423
  }
2368
2424
  return next;
2369
2425
  }
2426
+ function dashboardSpawnEnv(env, snapshotRoot, opts) {
2427
+ const root = path11.resolve(snapshotRoot);
2428
+ return applyDashboardOpenEnv({ ...env, MISSION_CONTROL_REPO_ROOT: root }, { ...opts, cwd: root });
2429
+ }
2430
+ function dashboardProcessTitle(snapshotRoot) {
2431
+ const base = path11.basename(path11.resolve(snapshotRoot)) || "workspace";
2432
+ return `Mission Control \xB7 ${base}`;
2433
+ }
2370
2434
  function bundledDashboardCandidates(filename, moduleUrl = import.meta.url) {
2371
2435
  const here = path11.dirname(fileURLToPath(moduleUrl));
2372
2436
  return [
@@ -2501,10 +2565,11 @@ var dashboardCommand = defineCommand4({
2501
2565
  process.exitCode = 1;
2502
2566
  return;
2503
2567
  }
2504
- const env = applyDashboardOpenEnv(
2505
- { ...process.env, MISSION_CONTROL_REPO_ROOT: snapshotRoot },
2506
- { noOpen: Boolean(args["no-open"]), browser: args.browser, cwd: snapshotRoot }
2507
- );
2568
+ const env = dashboardSpawnEnv({ ...process.env }, snapshotRoot, {
2569
+ noOpen: Boolean(args["no-open"]),
2570
+ browser: args.browser
2571
+ });
2572
+ process.title = dashboardProcessTitle(snapshotRoot);
2508
2573
  const code = await runStartScript(startPath, env);
2509
2574
  if (code !== 0) process.exitCode = code;
2510
2575
  }
@@ -2601,10 +2666,11 @@ var dashboardBroadcastCommand = defineCommand5({
2601
2666
  return;
2602
2667
  }
2603
2668
  const snapshotRoot = resolveDashboardSnapshotRoot(args.cwd);
2604
- const env = applyDashboardOpenEnv(
2605
- { ...process.env },
2606
- { noOpen: Boolean(args["no-open"]), browser: args.browser, cwd: snapshotRoot }
2607
- );
2669
+ const env = dashboardSpawnEnv({ ...process.env }, snapshotRoot, {
2670
+ noOpen: Boolean(args["no-open"]),
2671
+ browser: args.browser
2672
+ });
2673
+ process.title = dashboardProcessTitle(snapshotRoot);
2608
2674
  const code = await runStartScript2(startPath, env);
2609
2675
  if (code !== 0) process.exitCode = code;
2610
2676
  }
@@ -4115,6 +4181,169 @@ async function writeReadinessSnapshot(rootDir, report) {
4115
4181
  return snapshotPath;
4116
4182
  }
4117
4183
 
4184
+ // src/welcome/visual-kit.ts
4185
+ import { cyan as cyan2, gray, options as koloristOptions } from "kolorist";
4186
+ var HELMET_OUTLINE = "#e2e8f0";
4187
+ var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
4188
+ var SPACE_MARKS = {
4189
+ star: "\u2726",
4190
+ tick: "\xB7",
4191
+ diamond: "\u2727",
4192
+ visor: "( )"
4193
+ };
4194
+ var KIT_TIPS = [
4195
+ "Try agent-kit doctor for repository readiness.",
4196
+ "HITL gates stay in Cursor slash commands.",
4197
+ "/run-plan never promotes to production.",
4198
+ "Mission Control is the dashboard, not the CLI name.",
4199
+ "NO_COLOR and CI keep this output static.",
4200
+ "agent-kit --help groups SETUP, MISSION, DASHBOARD, INTEGRITY."
4201
+ ];
4202
+ function shouldUseWelcomeColor(opts = {}) {
4203
+ if (opts.color === false) return false;
4204
+ if (process.env.NO_COLOR) return false;
4205
+ if (process.env.NODE_DISABLE_COLORS) return false;
4206
+ if (process.env.FORCE_COLOR === "0") return false;
4207
+ if (process.env.CI != null && process.env.CI !== "") return false;
4208
+ if (opts.color === true) return true;
4209
+ const tty = opts.stdoutIsTTY ?? Boolean(process.stdout.isTTY);
4210
+ return tty;
4211
+ }
4212
+ function shouldUseVisualMotion(opts = {}) {
4213
+ if (process.env.AGENT_KIT_REDUCED_MOTION === "1") return false;
4214
+ return shouldUseWelcomeColor(opts);
4215
+ }
4216
+ function spinnerFrame(index) {
4217
+ const n = SPINNER_FRAMES.length;
4218
+ return SPINNER_FRAMES[(index % n + n) % n] ?? SPINNER_FRAMES[0];
4219
+ }
4220
+ function tipAt(index) {
4221
+ const n = KIT_TIPS.length;
4222
+ return KIT_TIPS[(index % n + n) % n] ?? KIT_TIPS[0];
4223
+ }
4224
+ function wrapNarrow(text, columns) {
4225
+ const width = Math.max(8, Number.isFinite(columns) ? Math.floor(columns) : 80);
4226
+ const words = text.trim().split(/\s+/).filter(Boolean);
4227
+ if (words.length === 0) return "";
4228
+ const lines = [];
4229
+ let current = "";
4230
+ for (const word of words) {
4231
+ if (word.length > width) {
4232
+ if (current) {
4233
+ lines.push(current);
4234
+ current = "";
4235
+ }
4236
+ for (let i = 0; i < word.length; i += width) {
4237
+ const chunk = word.slice(i, i + width);
4238
+ if (i + width >= word.length) current = chunk;
4239
+ else lines.push(chunk);
4240
+ }
4241
+ continue;
4242
+ }
4243
+ const next = current ? `${current} ${word}` : word;
4244
+ if (next.length > width) {
4245
+ lines.push(current);
4246
+ current = word;
4247
+ } else {
4248
+ current = next;
4249
+ }
4250
+ }
4251
+ if (current) lines.push(current);
4252
+ return lines.join("\n");
4253
+ }
4254
+ function renderProgressLine(opts) {
4255
+ const motion = opts.motion ?? shouldUseVisualMotion();
4256
+ const color = opts.color ?? shouldUseWelcomeColor();
4257
+ const mark = motion ? spinnerFrame(opts.frameIndex) : SPACE_MARKS.tick;
4258
+ const tip = tipAt(opts.tipIndex);
4259
+ const label = opts.label?.trim() ?? "";
4260
+ const raw = label ? `${mark} ${label} ${SPACE_MARKS.star} ${tip}` : `${mark} ${tip}`;
4261
+ const cols = opts.columns ?? process.stdout.columns ?? 80;
4262
+ const wrapped = wrapNarrow(raw, cols);
4263
+ if (!color) return wrapped;
4264
+ const prevEnabled = koloristOptions.enabled;
4265
+ koloristOptions.enabled = true;
4266
+ try {
4267
+ return wrapped.split("\n").map((line, i) => i === 0 ? cyan2(line) : gray(line)).join("\n");
4268
+ } finally {
4269
+ koloristOptions.enabled = prevEnabled;
4270
+ }
4271
+ }
4272
+ var TtySpinner = class {
4273
+ timer = null;
4274
+ frame = 0;
4275
+ startedAt = 0;
4276
+ label = "";
4277
+ write;
4278
+ columns;
4279
+ motion;
4280
+ color;
4281
+ intervalMs;
4282
+ now;
4283
+ constructor(opts = {}) {
4284
+ this.write = opts.write ?? ((chunk) => process.stdout.write(chunk));
4285
+ this.columns = opts.columns ?? (() => process.stdout.columns ?? 80);
4286
+ this.motion = opts.motion ?? shouldUseVisualMotion();
4287
+ this.color = opts.color ?? shouldUseWelcomeColor();
4288
+ this.intervalMs = opts.intervalMs ?? 80;
4289
+ this.now = opts.now ?? (() => Date.now());
4290
+ }
4291
+ start(label = "") {
4292
+ this.stop();
4293
+ this.label = label;
4294
+ this.frame = 0;
4295
+ this.startedAt = this.now();
4296
+ this.paint(false);
4297
+ if (!this.motion) return;
4298
+ this.timer = setInterval(() => {
4299
+ this.frame += 1;
4300
+ this.paint(true);
4301
+ }, this.intervalMs);
4302
+ this.timer.unref?.();
4303
+ }
4304
+ stop(finalLine) {
4305
+ if (this.timer) {
4306
+ clearInterval(this.timer);
4307
+ this.timer = null;
4308
+ }
4309
+ if (this.motion) this.write("\r\x1B[K");
4310
+ if (finalLine) this.write(`${finalLine}
4311
+ `);
4312
+ }
4313
+ paint(overwrite) {
4314
+ const elapsed = this.now() - this.startedAt;
4315
+ const tipIndex = Math.floor(elapsed / 4e3);
4316
+ const line = renderProgressLine({
4317
+ frameIndex: this.frame,
4318
+ tipIndex,
4319
+ columns: this.columns(),
4320
+ label: this.label,
4321
+ motion: this.motion,
4322
+ color: this.color
4323
+ }).split("\n")[0] ?? "";
4324
+ if (overwrite && this.motion) this.write(`\r\x1B[K${line}`);
4325
+ else if (this.motion) this.write(line);
4326
+ else this.write(`${line}
4327
+ `);
4328
+ }
4329
+ };
4330
+ async function withCliProgress(label, fn, opts) {
4331
+ const motion = opts?.motion ?? shouldUseVisualMotion();
4332
+ if (!motion) {
4333
+ return fn();
4334
+ }
4335
+ const spinner = new TtySpinner({ ...opts, motion: true });
4336
+ spinner.start(label);
4337
+ try {
4338
+ const result = await fn();
4339
+ spinner.stop();
4340
+ return result;
4341
+ } catch (err) {
4342
+ spinner.stop();
4343
+ throw err;
4344
+ }
4345
+ }
4346
+
4118
4347
  // src/commands/doctor.ts
4119
4348
  async function runDoctor(cwd, options = {}) {
4120
4349
  const rootDir = path24.resolve(cwd);
@@ -4188,7 +4417,8 @@ var doctorCommand = defineCommand7({
4188
4417
  }
4189
4418
  },
4190
4419
  async run({ args }) {
4191
- const result = await runDoctor(args.cwd, { fixSafe: args["fix-safe"] });
4420
+ const run = () => runDoctor(args.cwd, { fixSafe: args["fix-safe"] });
4421
+ const result = args.json ? await run() : await withCliProgress("doctor", run);
4192
4422
  if (args.json) {
4193
4423
  console.log(JSON.stringify(result, null, 2));
4194
4424
  return;
@@ -4818,11 +5048,19 @@ async function fileExists2(p) {
4818
5048
  return false;
4819
5049
  }
4820
5050
  }
5051
+ function isMarkdownTableSeparator(line) {
5052
+ const stripped = line.trim();
5053
+ if (!stripped.startsWith("|")) return false;
5054
+ const parts = stripped.replace(/^\|/, "").replace(/\|$/, "").split("|").map((cell) => cell.trim());
5055
+ return parts.length > 0 && parts.every((cell) => /^:?-+:?$/.test(cell));
5056
+ }
4821
5057
  function parseUnprocessedDogfoodItems(readmeText) {
4822
5058
  const items = [];
4823
5059
  let inSection = false;
4824
5060
  let sectionLevel = 0;
4825
- for (const line of readmeText.split(/\r?\n/)) {
5061
+ const lines = readmeText.split(/\r?\n/);
5062
+ for (let i = 0; i < lines.length; i++) {
5063
+ const line = lines[i] ?? "";
4826
5064
  const unprocessedMatch = /^(#{2,3})\s+Unprocessed Files\b/.exec(line);
4827
5065
  if (unprocessedMatch) {
4828
5066
  const hashes = unprocessedMatch[1];
@@ -4834,11 +5072,14 @@ function parseUnprocessedDogfoodItems(readmeText) {
4834
5072
  if (!inSection) continue;
4835
5073
  const headingMatch = /^(#{1,6})\s+/.exec(line);
4836
5074
  if (headingMatch) {
4837
- if (/\bProcessed Files\b/.test(line)) break;
5075
+ if (/^#{1,6}\s+Processed(?:\s+Files)?\b/.test(line)) break;
4838
5076
  const hashes = headingMatch[1];
4839
5077
  if (hashes && hashes.length <= sectionLevel) break;
4840
5078
  continue;
4841
5079
  }
5080
+ if (line.trim().startsWith("|") && isMarkdownTableSeparator(lines[i + 1] ?? "")) {
5081
+ continue;
5082
+ }
4842
5083
  const body = extractUnprocessedDogfoodLine(line);
4843
5084
  if (!body) continue;
4844
5085
  items.push(body);
@@ -4856,13 +5097,10 @@ function extractUnprocessedDogfoodLine(line) {
4856
5097
  if (numbered?.[2]) {
4857
5098
  raw = numbered[2].trim();
4858
5099
  } else if (stripped.startsWith("|")) {
5100
+ if (isMarkdownTableSeparator(stripped)) return null;
4859
5101
  const parts = stripped.replace(/^\|/, "").replace(/\|$/, "").split("|").map((cell) => cell.trim());
4860
5102
  if (parts.length === 0) return null;
4861
- if (parts.every((cell) => /^:?-+:?$/.test(cell))) return null;
4862
- const first = parts[0] ?? "";
4863
- const headerish = first.toLowerCase().replace(/[*_`]/g, "").trim();
4864
- if (/^(note|file|entrada|title|name|item|path)$/.test(headerish)) return null;
4865
- raw = first.trim();
5103
+ raw = (parts[0] ?? "").trim();
4866
5104
  }
4867
5105
  }
4868
5106
  if (!raw) return null;
@@ -5340,16 +5578,83 @@ function classifyInstallError(err) {
5340
5578
  }
5341
5579
 
5342
5580
  // src/commands/install.ts
5343
- import path32 from "path";
5581
+ import path33 from "path";
5344
5582
  import { defineCommand as defineCommand11 } from "citty";
5345
5583
 
5346
5584
  // src/generator/personalization.ts
5347
- import { readFile as readFile15, writeFile as writeFile8 } from "fs/promises";
5348
- import path30 from "path";
5585
+ import { readFile as readFile15, writeFile as writeFile9 } from "fs/promises";
5586
+ import path31 from "path";
5349
5587
 
5350
- // src/generator/vscode.ts
5588
+ // src/generator/claude-kit-load.ts
5351
5589
  import { writeFile as writeFile7 } from "fs/promises";
5352
5590
  import path29 from "path";
5591
+ var CLAUDE_MD_REL = "CLAUDE.md";
5592
+ var AGENT_KIT_COMMAND_REL = ".claude/commands/agent-kit.md";
5593
+ function renderClaudeMd() {
5594
+ return `# Agent Kit (Claude Code)
5595
+
5596
+ This repository uses Agent Kit / Mission Control. Before rediscovering the tree, read the shared sources of truth (paths below are pointers; do not treat this file as a second rulebook).
5597
+
5598
+ ## Read first
5599
+
5600
+ 1. \`AGENTS.md\` - cross-IDE contract
5601
+ 2. \`.cursor/project-context.md\` - verified repository facts (derived; prefer code, tests, SHAs when docs conflict)
5602
+ 3. \`.cursor/HANDOFF.md\` - if present: active plan, next to-do, queue fields
5603
+ 4. \`.cursor/commands/\` - slash catalog (Cursor). Follow the same HITL contracts here.
5604
+
5605
+ Mid-session refresh: \`/agent-kit\`.
5606
+
5607
+ ## HITL
5608
+
5609
+ Cursor Ask questions is not available in this CLI. When a command requires a choice, list the same labels as a numbered list and wait. Skip or cancel means stop. Never \`/git-prod\` without an explicit operator yes.
5610
+
5611
+ ## Non-goals
5612
+
5613
+ - Not Action A7 (Windsurf / VS Code generator parity)
5614
+ - Not Claude external plan-review audits (\`/plan-external-review\`)
5615
+ - Not \`--backend claude\` plan-loop ticks
5616
+ - Not a copy of Cursor \`sessionStart\` / other IDE hooks
5617
+ `;
5618
+ }
5619
+ function renderAgentKitCommand() {
5620
+ return `---
5621
+ description: Load Agent Kit session context (HANDOFF, project-context, commands). Manual refresh only.
5622
+ disable-model-invocation: true
5623
+ ---
5624
+
5625
+ Read these files if they exist, then summarize the active plan, next to-do, and any Gaps. Do not scan the whole repository first.
5626
+
5627
+ 1. \`AGENTS.md\`
5628
+ 2. \`.cursor/project-context.md\`
5629
+ 3. \`.cursor/HANDOFF.md\`
5630
+ 4. The plan file named in HANDOFF \`- **Plan:**\` under \`.cursor/plans/\`
5631
+
5632
+ If HANDOFF is missing, say so and point at \`/agent-kit-onboard\` or \`/start-project\` rather than inventing a plan.
5633
+
5634
+ HITL: numbered-list fallback for Ask questions labels. Never \`/git-prod\` from this skill.
5635
+
5636
+ Non-goals: not audits / \`/plan-external-review\`, not \`--backend claude\` ticks, not A7, not Cursor hook clones.
5637
+ `;
5638
+ }
5639
+ async function writeUnlessExists(rootDir, relativePath, content) {
5640
+ const target = path29.join(rootDir, relativePath);
5641
+ if (await fileExists(target)) {
5642
+ return { relativePath, status: "skipped-customized" };
5643
+ }
5644
+ await ensureDir(path29.dirname(target));
5645
+ await writeFile7(target, content, "utf8");
5646
+ return { relativePath, status: "applied" };
5647
+ }
5648
+ async function generateClaudeKitLoadArtifacts(rootDir) {
5649
+ return Promise.all([
5650
+ writeUnlessExists(rootDir, CLAUDE_MD_REL, renderClaudeMd()),
5651
+ writeUnlessExists(rootDir, AGENT_KIT_COMMAND_REL, renderAgentKitCommand())
5652
+ ]);
5653
+ }
5654
+
5655
+ // src/generator/vscode.ts
5656
+ import { writeFile as writeFile8 } from "fs/promises";
5657
+ import path30 from "path";
5353
5658
 
5354
5659
  // src/generator/platform.ts
5355
5660
  function gitProviderLabel(profile) {
@@ -5373,14 +5678,14 @@ function prTerminology(profile) {
5373
5678
  // src/generator/vscode.ts
5374
5679
  async function generateVSCodeArtifacts(profile) {
5375
5680
  const results = [];
5376
- const vscodeDir = path29.join(profile.rootDir, ".vscode");
5377
- const githubDir = path29.join(profile.rootDir, ".github");
5681
+ const vscodeDir = path30.join(profile.rootDir, ".vscode");
5682
+ const githubDir = path30.join(profile.rootDir, ".github");
5378
5683
  await Promise.all([ensureDir(vscodeDir), ensureDir(githubDir)]);
5379
- const settingsPath = path29.join(vscodeDir, "settings.json");
5684
+ const settingsPath = path30.join(vscodeDir, "settings.json");
5380
5685
  if (await fileExists(settingsPath)) {
5381
5686
  results.push({ relativePath: ".vscode/settings.json", status: "skipped-customized" });
5382
5687
  } else {
5383
- await writeFile7(
5688
+ await writeFile8(
5384
5689
  settingsPath,
5385
5690
  `${JSON.stringify(
5386
5691
  {
@@ -5400,11 +5705,11 @@ async function generateVSCodeArtifacts(profile) {
5400
5705
  }
5401
5706
  const provider = gitProviderLabel(profile);
5402
5707
  const prTerm = prTerminology(profile);
5403
- const copilotPath = path29.join(githubDir, "copilot-instructions.md");
5708
+ const copilotPath = path30.join(githubDir, "copilot-instructions.md");
5404
5709
  if (await fileExists(copilotPath)) {
5405
5710
  results.push({ relativePath: ".github/copilot-instructions.md", status: "skipped-customized" });
5406
5711
  } else {
5407
- await writeFile7(
5712
+ await writeFile8(
5408
5713
  copilotPath,
5409
5714
  `# Copilot Instructions
5410
5715
 
@@ -5418,14 +5723,14 @@ async function generateVSCodeArtifacts(profile) {
5418
5723
  results.push({ relativePath: ".github/copilot-instructions.md", status: "applied" });
5419
5724
  }
5420
5725
  if (profile.ide.plan === "vscode-pro") {
5421
- const securityPath = path29.join(vscodeDir, "security-review.agent.md");
5726
+ const securityPath = path30.join(vscodeDir, "security-review.agent.md");
5422
5727
  if (await fileExists(securityPath)) {
5423
5728
  results.push({
5424
5729
  relativePath: ".vscode/security-review.agent.md",
5425
5730
  status: "skipped-customized"
5426
5731
  });
5427
5732
  } else {
5428
- await writeFile7(
5733
+ await writeFile8(
5429
5734
  securityPath,
5430
5735
  "# Security Review Agent\n\nSpecialized mode for security review.\n",
5431
5736
  "utf8"
@@ -5561,7 +5866,40 @@ function buildPersonalizationPlan(profile, report, registry) {
5561
5866
  (item) => componentAvailable(registry, item) ? item : { ...item, status: "unavailable" }
5562
5867
  ).sort((left, right) => `${left.kind}:${left.id}`.localeCompare(`${right.kind}:${right.id}`));
5563
5868
  }
5564
- function renderProjectContext(profile) {
5869
+ var INSTALLED_SKILL_STATUSES = /* @__PURE__ */ new Set(["applied", "skipped-customized"]);
5870
+ function installedSkillItems(items, installedIds = []) {
5871
+ const rows = [];
5872
+ const seen = /* @__PURE__ */ new Set();
5873
+ for (const item of items) {
5874
+ if (item.kind !== "skill" || !INSTALLED_SKILL_STATUSES.has(item.status)) continue;
5875
+ if (seen.has(item.id)) continue;
5876
+ seen.add(item.id);
5877
+ rows.push(item);
5878
+ }
5879
+ for (const id of installedIds) {
5880
+ if (seen.has(id)) continue;
5881
+ seen.add(id);
5882
+ rows.push({
5883
+ kind: "skill",
5884
+ id,
5885
+ status: "applied",
5886
+ evidence: [{ source: "configuration", value: `.cursor/agent-kit.json skills[]:${id}` }]
5887
+ });
5888
+ }
5889
+ return rows.sort((left, right) => left.id.localeCompare(right.id));
5890
+ }
5891
+ function relevantSkillTableRows(skills) {
5892
+ if (skills.length === 0) {
5893
+ return ["| (none yet) | No installed or project-owned skills detected | \u2014 |"];
5894
+ }
5895
+ return skills.map((skill) => {
5896
+ const label = skill.path ?? skill.id;
5897
+ const role = skill.status === "skipped-customized" ? "Already present (customized)" : "Installed by personalization";
5898
+ const evidence = skill.path ?? skill.evidence[0]?.value ?? "personalization";
5899
+ return `| ${label} | ${role} | ${evidence} |`;
5900
+ });
5901
+ }
5902
+ function renderProjectContext(profile, skillItems = []) {
5565
5903
  const sections = ["# Project Context", "", "Verified repository facts:"];
5566
5904
  const purpose = purposeEvidence(profile);
5567
5905
  if (purpose.length > 0 && profile.purpose.value !== "unknown") {
@@ -5588,7 +5926,7 @@ function renderProjectContext(profile) {
5588
5926
  "",
5589
5927
  "| Skill / path | Role | Evidence |",
5590
5928
  "|--------------|------|----------|",
5591
- "| (none yet) | Add rows when `/agent-kit-onboard` scaffolds domain skills or personalization installs packs | \u2014 |"
5929
+ ...relevantSkillTableRows(installedSkillItems(skillItems))
5592
5930
  );
5593
5931
  if (profile.context.sources.length > 0) {
5594
5932
  sections.push("", "## Sources", ...profile.context.sources.map((item) => `- ${item.value}`));
@@ -5597,7 +5935,7 @@ function renderProjectContext(profile) {
5597
5935
  `;
5598
5936
  }
5599
5937
  async function createOwnedFile(rootDir, relativePath, content, evidence) {
5600
- const target = path30.join(rootDir, relativePath);
5938
+ const target = path31.join(rootDir, relativePath);
5601
5939
  if (await fileExists(target)) {
5602
5940
  return {
5603
5941
  kind: "file",
@@ -5607,8 +5945,8 @@ async function createOwnedFile(rootDir, relativePath, content, evidence) {
5607
5945
  evidence
5608
5946
  };
5609
5947
  }
5610
- await ensureDir(path30.dirname(target));
5611
- await writeFile8(target, content, "utf8");
5948
+ await ensureDir(path31.dirname(target));
5949
+ await writeFile9(target, content, "utf8");
5612
5950
  return {
5613
5951
  kind: "file",
5614
5952
  id: relativePath,
@@ -5624,7 +5962,7 @@ async function packTargets(registryRoot, packId) {
5624
5962
  async function existingTargets(projectRoot, targets) {
5625
5963
  const checks = await Promise.all(
5626
5964
  targets.map(
5627
- async (target) => await fileExists(path30.join(projectRoot, target)) ? target : null
5965
+ async (target) => await fileExists(path31.join(projectRoot, target)) ? target : null
5628
5966
  )
5629
5967
  );
5630
5968
  return checks.filter((target) => target !== null);
@@ -5646,14 +5984,14 @@ async function applyPersonalization(input) {
5646
5984
  componentResults.push({ ...item, status: "unavailable" });
5647
5985
  continue;
5648
5986
  }
5649
- const target = path30.posix.join(
5987
+ const target = path31.posix.join(
5650
5988
  ".cursor",
5651
5989
  "skills",
5652
5990
  skill.path.includes("/core/") ? "core" : "community",
5653
5991
  skill.id,
5654
5992
  "SKILL.md"
5655
5993
  );
5656
- if (await fileExists(path30.join(input.rootDir, target))) {
5994
+ if (await fileExists(path31.join(input.rootDir, target))) {
5657
5995
  componentResults.push({ ...item, status: "skipped-customized", path: target });
5658
5996
  protectedPaths.add(target);
5659
5997
  continue;
@@ -5689,7 +6027,7 @@ async function applyPersonalization(input) {
5689
6027
  createOwnedFile(
5690
6028
  input.rootDir,
5691
6029
  CONTEXT_PATH,
5692
- renderProjectContext(input.profile),
6030
+ renderProjectContext(input.profile, installedSkillItems(componentResults, skills)),
5693
6031
  profileEvidence
5694
6032
  ),
5695
6033
  createOwnedFile(
@@ -5701,6 +6039,17 @@ async function applyPersonalization(input) {
5701
6039
  ]);
5702
6040
  protectedPaths.add(CONTEXT_PATH);
5703
6041
  protectedPaths.add(AGENTS_PATH);
6042
+ const claudeResults = await generateClaudeKitLoadArtifacts(input.rootDir);
6043
+ const claudeItems = claudeResults.map((artifact) => {
6044
+ protectedPaths.add(artifact.relativePath);
6045
+ return {
6046
+ kind: "file",
6047
+ id: artifact.relativePath,
6048
+ path: artifact.relativePath,
6049
+ status: artifact.status,
6050
+ evidence: profileEvidence
6051
+ };
6052
+ });
5704
6053
  const ideDetection = await detectIde(input.rootDir);
5705
6054
  if (ideDetection.ide === "vscode" || ideDetection.ide === "other") {
5706
6055
  const git = {
@@ -5748,10 +6097,10 @@ async function applyPersonalization(input) {
5748
6097
  contractVersion: PERSONALIZATION_CONTRACT_VERSION,
5749
6098
  generatorVersion: input.generatorVersion,
5750
6099
  repositoryFingerprint: input.report.repositoryFingerprint,
5751
- items: [...fileResults, ...componentResults],
6100
+ items: [...fileResults, ...claudeItems, ...componentResults],
5752
6101
  protectedPaths: [...protectedPaths].sort()
5753
6102
  };
5754
- await writeJson(path30.join(input.rootDir, RESULT_PATH), result);
6103
+ await writeJson(path31.join(input.rootDir, RESULT_PATH), result);
5755
6104
  return {
5756
6105
  result,
5757
6106
  manifest: {
@@ -5769,7 +6118,7 @@ async function applyPersonalization(input) {
5769
6118
  };
5770
6119
  }
5771
6120
  async function readRepositoryProfile(rootDir) {
5772
- const target = path30.join(rootDir, ".cursor/agent-kit.config.json");
6121
+ const target = path31.join(rootDir, ".cursor/agent-kit.config.json");
5773
6122
  if (!await fileExists(target)) return null;
5774
6123
  return JSON.parse(await readFile15(target, "utf8"));
5775
6124
  }
@@ -5777,16 +6126,16 @@ async function readRepositoryProfile(rootDir) {
5777
6126
  // src/lifecycle/onboard-migration.ts
5778
6127
  import { createHash as createHash4 } from "crypto";
5779
6128
  import { readFile as readFile16, unlink } from "fs/promises";
5780
- import path31 from "path";
6129
+ import path32 from "path";
5781
6130
  var LEGACY_ONBOARD_PATH = ".cursor/commands/onboard.md";
5782
6131
  var NAMESPACED_ONBOARD_PATH = ".cursor/commands/agent-kit-onboard.md";
5783
6132
  var MANAGED_LEGACY_HASHES = /* @__PURE__ */ new Set([
5784
6133
  "b274a68941813f19b185893cb7c5561dff027f53270890029992f208e24992fe"
5785
6134
  ]);
5786
6135
  async function migrateLegacyOnboardCommand(projectRoot, managedHashes = MANAGED_LEGACY_HASHES) {
5787
- const legacyPath = path31.join(projectRoot, LEGACY_ONBOARD_PATH);
6136
+ const legacyPath = path32.join(projectRoot, LEGACY_ONBOARD_PATH);
5788
6137
  if (!await fileExists(legacyPath)) return "absent";
5789
- const namespacedPath = path31.join(projectRoot, NAMESPACED_ONBOARD_PATH);
6138
+ const namespacedPath = path32.join(projectRoot, NAMESPACED_ONBOARD_PATH);
5790
6139
  if (!await fileExists(namespacedPath)) return "preserved-customized";
5791
6140
  const content = await readFile16(legacyPath);
5792
6141
  const hash = createHash4("sha256").update(content).digest("hex");
@@ -5861,7 +6210,7 @@ function printReadinessNarrative(result) {
5861
6210
  );
5862
6211
  }
5863
6212
  async function performInstall(options) {
5864
- const projectRoot = path32.resolve(options.cwd);
6213
+ const projectRoot = path33.resolve(options.cwd);
5865
6214
  const packs = parsePackList(options.pack);
5866
6215
  const existing = await loadAgentKitManifest(projectRoot);
5867
6216
  const registry = await resolveRegistryFromCli({
@@ -5979,15 +6328,18 @@ var installCommand = defineCommand11({
5979
6328
  }
5980
6329
  }
5981
6330
  try {
5982
- const result = await performInstall({
5983
- cwd: projectRoot,
5984
- profile: args.profile,
5985
- pack: args.pack,
5986
- registry: args.registry,
5987
- url: args.url,
5988
- ref: args.ref,
5989
- refresh: args.refresh
5990
- });
6331
+ const result = await withCliProgress(
6332
+ "install",
6333
+ () => performInstall({
6334
+ cwd: projectRoot,
6335
+ profile: args.profile,
6336
+ pack: args.pack,
6337
+ registry: args.registry,
6338
+ url: args.url,
6339
+ ref: args.ref,
6340
+ refresh: args.refresh
6341
+ })
6342
+ );
5991
6343
  logApplyStats(result.stats);
5992
6344
  logger.success(`Manifest written: ${result.manifestPath}`);
5993
6345
  logger.success("Readiness snapshot written: .cursor/context/readiness.json");
@@ -5998,7 +6350,8 @@ var installCommand = defineCommand11({
5998
6350
  console.error(`
5999
6351
  ${hint.recovery}
6000
6352
  `);
6001
- process.exit(1);
6353
+ process.exitCode = 1;
6354
+ return;
6002
6355
  }
6003
6356
  }
6004
6357
  });
@@ -6028,7 +6381,7 @@ var initCommand = defineCommand12({
6028
6381
  }
6029
6382
  logger.info("init now uses the canonical install and readiness workflow.");
6030
6383
  try {
6031
- const result = await runInitCompatibility(args.cwd);
6384
+ const result = await withCliProgress("init", () => runInitCompatibility(args.cwd));
6032
6385
  const pending = result.readiness.pendingActions.length;
6033
6386
  logger.success(`L0 and readiness prepared in ${result.projectRoot}`);
6034
6387
  const nextStep = pending > 0 ? "Next: run /agent-kit-onboard in Cursor to resolve the first pending action." : "Next: run /start-project in Cursor when you have a deliverable.";
@@ -6049,13 +6402,13 @@ ${hint.recovery}
6049
6402
  });
6050
6403
 
6051
6404
  // src/commands/monitors.ts
6052
- import path34 from "path";
6405
+ import path35 from "path";
6053
6406
  import { defineCommand as defineCommand13 } from "citty";
6054
6407
 
6055
6408
  // src/invariants/monitors-untriaged.ts
6056
6409
  import { execFile as execFile5 } from "child_process";
6057
6410
  import { readFile as readFile17, readdir as readdir5, stat as stat3 } from "fs/promises";
6058
- import path33 from "path";
6411
+ import path34 from "path";
6059
6412
  import { promisify as promisify5 } from "util";
6060
6413
 
6061
6414
  // src/invariants/triage-heading.ts
@@ -6096,7 +6449,7 @@ async function gitFreshMonitorNames(rootDir) {
6096
6449
  for (const line of stdout.split("\n")) {
6097
6450
  if (!line.trim()) continue;
6098
6451
  const file = line.slice(3).trim().replace(/^.* -> /, "");
6099
- const base = path33.basename(file);
6452
+ const base = path34.basename(file);
6100
6453
  if (base.startsWith("plan-monitor-") && base.endsWith(".md")) {
6101
6454
  names.add(base);
6102
6455
  }
@@ -6119,13 +6472,13 @@ function monitorSlugFromName(fileName) {
6119
6472
  return fileName.replace(/^plan-monitor-/, "").replace(/\.md$/, "").toLowerCase();
6120
6473
  }
6121
6474
  async function selectUntriagedMonitors(rootDir) {
6122
- const root = path33.resolve(rootDir);
6123
- const memoryDir = path33.join(root, ".cursor", "memory");
6475
+ const root = path34.resolve(rootDir);
6476
+ const memoryDir = path34.join(root, ".cursor", "memory");
6124
6477
  const allNames = await listMonitorFiles(memoryDir);
6125
6478
  const selectionOrder = ["git-fresh", "handoff-aligned", "untriaged-scan"];
6126
6479
  const byName = /* @__PURE__ */ new Map();
6127
6480
  for (const name of allNames) {
6128
- const abs = path33.join(memoryDir, name);
6481
+ const abs = path34.join(memoryDir, name);
6129
6482
  try {
6130
6483
  const [content, st] = await Promise.all([readFile17(abs, "utf8"), stat3(abs)]);
6131
6484
  byName.set(name, { content, mtimeMs: st.mtimeMs });
@@ -6140,7 +6493,7 @@ async function selectUntriagedMonitors(rootDir) {
6140
6493
  const gitFreshSet = [...gitFresh].filter(untriaged).sort();
6141
6494
  let handoff = "";
6142
6495
  try {
6143
- handoff = await readFile17(path33.join(root, ".cursor", "HANDOFF.md"), "utf8");
6496
+ handoff = await readFile17(path34.join(root, ".cursor", "HANDOFF.md"), "utf8");
6144
6497
  } catch {
6145
6498
  handoff = "";
6146
6499
  }
@@ -6160,8 +6513,8 @@ async function selectUntriagedMonitors(rootDir) {
6160
6513
  const row = byName.get(name);
6161
6514
  if (!row) continue;
6162
6515
  entries.push({
6163
- path: path33.join(memoryDir, name),
6164
- relativePath: path33.relative(root, path33.join(memoryDir, name)).split(path33.sep).join("/"),
6516
+ path: path34.join(memoryDir, name),
6517
+ relativePath: path34.relative(root, path34.join(memoryDir, name)).split(path34.sep).join("/"),
6165
6518
  mtimeMs: row.mtimeMs,
6166
6519
  hasTriageHeading: false,
6167
6520
  hasOpenGaps: hasOpenGaps(row.content),
@@ -6207,7 +6560,7 @@ var monitorsCommand = defineCommand13({
6207
6560
  process.exitCode = 2;
6208
6561
  return;
6209
6562
  }
6210
- const result = await selectUntriagedMonitors(path34.resolve(args.cwd));
6563
+ const result = await selectUntriagedMonitors(path35.resolve(args.cwd));
6211
6564
  if (args.json) {
6212
6565
  console.log(JSON.stringify(result, null, 2));
6213
6566
  return;
@@ -6223,7 +6576,7 @@ var monitorsCommand = defineCommand13({
6223
6576
  });
6224
6577
 
6225
6578
  // src/commands/run-plan.ts
6226
- import path39 from "path";
6579
+ import path40 from "path";
6227
6580
  import { defineCommand as defineCommand14 } from "citty";
6228
6581
 
6229
6582
  // src/plan-loop/backends.ts
@@ -6309,13 +6662,13 @@ function listBackendIds() {
6309
6662
 
6310
6663
  // src/plan-loop/run-loop.ts
6311
6664
  import { mkdir as mkdir6, readFile as readFile20, rm as rm2, unlink as unlink2 } from "fs/promises";
6312
- import path38 from "path";
6665
+ import path39 from "path";
6313
6666
 
6314
6667
  // src/plan-loop/external-review.ts
6315
6668
  import { spawn as spawn7 } from "child_process";
6316
- import path35 from "path";
6317
- var CANONICAL_REL = path35.join(".cursor", "scripts", "plan-external-review.sh");
6318
- var FALLBACK_REL = path35.join("scripts", "plan-external-review.sh");
6669
+ import path36 from "path";
6670
+ var CANONICAL_REL = path36.join(".cursor", "scripts", "plan-external-review.sh");
6671
+ var FALLBACK_REL = path36.join("scripts", "plan-external-review.sh");
6319
6672
  function isPlanExhaustedReason(reason) {
6320
6673
  const r = reason.trim().toLowerCase();
6321
6674
  if (!r) return false;
@@ -6337,8 +6690,8 @@ async function armExternalPlanReview(root, options = {}) {
6337
6690
  const existsFn = options.existsFn ?? fileExists;
6338
6691
  const log = options.log ?? ((line) => console.log(line));
6339
6692
  const force = options.force === true;
6340
- const canonicalPath = path35.join(root, CANONICAL_REL);
6341
- const fallbackPath = path35.join(root, FALLBACK_REL);
6693
+ const canonicalPath = path36.join(root, CANONICAL_REL);
6694
+ const fallbackPath = path36.join(root, FALLBACK_REL);
6342
6695
  let scriptPath = null;
6343
6696
  let scriptRel = CANONICAL_REL;
6344
6697
  if (await existsFn(canonicalPath)) {
@@ -6391,11 +6744,11 @@ async function armExternalPlanReview(root, options = {}) {
6391
6744
  }
6392
6745
 
6393
6746
  // src/plan-loop/persona-banners.ts
6394
- import path36 from "path";
6747
+ import path37 from "path";
6395
6748
  import {
6396
6749
  blue,
6397
- cyan as cyan2,
6398
- gray,
6750
+ cyan as cyan3,
6751
+ gray as gray2,
6399
6752
  green as green2,
6400
6753
  lightGray,
6401
6754
  lightGreen,
@@ -6408,9 +6761,9 @@ var CLI_RUN_PLAN_MODE = "cli-run-plan";
6408
6761
  var DEFAULT_CLI_PERSONA_ID = "ghost-runner";
6409
6762
  var COLORS = {
6410
6763
  white,
6411
- gray,
6764
+ gray: gray2,
6412
6765
  green: green2,
6413
- cyan: cyan2,
6766
+ cyan: cyan3,
6414
6767
  magenta,
6415
6768
  yellow: yellow2,
6416
6769
  red: red2,
@@ -6427,7 +6780,7 @@ function resolveColor(name, fallback) {
6427
6780
  async function resolveCliPersonaId(root) {
6428
6781
  try {
6429
6782
  const cfg = await readJson(
6430
- path36.join(root, ".cursor", "context", "config.json")
6783
+ path37.join(root, ".cursor", "context", "config.json")
6431
6784
  );
6432
6785
  const modes = cfg?.agentPersona?.modes ?? cfg?.workspaceSkin?.modes;
6433
6786
  const id = modes?.[CLI_RUN_PLAN_MODE];
@@ -6438,7 +6791,7 @@ async function resolveCliPersonaId(root) {
6438
6791
  }
6439
6792
  async function loadPersonaPack(root, personaId) {
6440
6793
  try {
6441
- const personaPath = path36.join(root, "registry", "personas", "core", personaId, "persona.json");
6794
+ const personaPath = path37.join(root, "registry", "personas", "core", personaId, "persona.json");
6442
6795
  const pack = await readJson(personaPath);
6443
6796
  if (!pack || typeof pack.id !== "string") return null;
6444
6797
  return pack;
@@ -6455,7 +6808,7 @@ function createPersonaBannerPrinter(persona) {
6455
6808
  const banners = persona.cliBanners;
6456
6809
  if (!banners.tickStart && !banners.tickEnd && !banners.phaseComplete) return null;
6457
6810
  const primary = resolveColor(persona.ansiPalette?.primary, white);
6458
- const secondary = resolveColor(persona.ansiPalette?.secondary, gray);
6811
+ const secondary = resolveColor(persona.ansiPalette?.secondary, gray2);
6459
6812
  const accent = resolveColor(persona.ansiPalette?.accent, green2);
6460
6813
  return {
6461
6814
  tickStart(detail) {
@@ -6488,7 +6841,7 @@ function createPersonaBannerPrinter(persona) {
6488
6841
 
6489
6842
  // src/plan-loop/plan-state.ts
6490
6843
  import { readFile as readFile18, readdir as readdir6 } from "fs/promises";
6491
- import path37 from "path";
6844
+ import path38 from "path";
6492
6845
  function countPendingTodos(raw) {
6493
6846
  const lines = raw.split(/\r?\n/);
6494
6847
  let inFront = 0;
@@ -6516,7 +6869,7 @@ function countPendingTodos(raw) {
6516
6869
  async function findActivePlanFile(plansDir) {
6517
6870
  if (!await fileExists(plansDir)) return null;
6518
6871
  const files = (await readdir6(plansDir)).filter((f) => f.endsWith(".plan.md")).sort();
6519
- return files[0] ? path37.join(plansDir, files[0]) : null;
6872
+ return files[0] ? path38.join(plansDir, files[0]) : null;
6520
6873
  }
6521
6874
  async function readPlan(planPath) {
6522
6875
  return readFile18(planPath, "utf8");
@@ -6593,9 +6946,9 @@ function sleep(ms) {
6593
6946
  return new Promise((r) => setTimeout(r, ms));
6594
6947
  }
6595
6948
  async function runPlanLoop(opts) {
6596
- const plansDir = path38.join(opts.root, ".cursor", "plans");
6597
- const stopFile = path38.join(opts.root, ".cursor", "loop.stop");
6598
- const logDir = path38.join(opts.root, ".cursor", "loop-logs");
6949
+ const plansDir = path39.join(opts.root, ".cursor", "plans");
6950
+ const stopFile = path39.join(opts.root, ".cursor", "loop.stop");
6951
+ const logDir = path39.join(opts.root, ".cursor", "loop-logs");
6599
6952
  const planPath = await findActivePlanFile(plansDir);
6600
6953
  if (!planPath) {
6601
6954
  logger.error("No active plan in .cursor/plans/");
@@ -6616,7 +6969,7 @@ async function runPlanLoop(opts) {
6616
6969
  try {
6617
6970
  const persona = await loadCliRunPlanPersona(opts.root);
6618
6971
  const banners = createPersonaBannerPrinter(persona);
6619
- console.log(`Active plan: ${path38.basename(planPath)}`);
6972
+ console.log(`Active plan: ${path39.basename(planPath)}`);
6620
6973
  console.log(`Pending to-dos: ${await pending()} | max ticks: ${opts.maxTicks}`);
6621
6974
  console.log(`Backend: ${opts.backend.id}`);
6622
6975
  if (persona) {
@@ -6659,20 +7012,23 @@ async function runPlanLoop(opts) {
6659
7012
  planExhausted = true;
6660
7013
  break;
6661
7014
  }
6662
- const logPath = path38.join(logDir, `tick-${stamp()}.log`);
6663
- const relLog = path38.relative(opts.root, logPath);
7015
+ const logPath = path39.join(logDir, `tick-${stamp()}.log`);
7016
+ const relLog = path39.relative(opts.root, logPath);
6664
7017
  console.log("");
6665
7018
  const tickLine = `=== tick ${tick}/${opts.maxTicks} - pending: ${before} - log: ${relLog} ===`;
6666
7019
  if (banners) banners.tickStart(tickLine);
6667
7020
  else console.log(tickLine);
6668
7021
  let agentExit = 0;
6669
7022
  try {
6670
- const result = await opts.backend.run({
6671
- workspace: opts.root,
6672
- prompt: TICK_PROMPT,
6673
- model: opts.model,
6674
- logPath
6675
- });
7023
+ const result = await withCliProgress(
7024
+ `tick ${tick}`,
7025
+ () => opts.backend.run({
7026
+ workspace: opts.root,
7027
+ prompt: TICK_PROMPT,
7028
+ model: opts.model,
7029
+ logPath
7030
+ })
7031
+ );
6676
7032
  agentExit = result.exitCode;
6677
7033
  } catch (err) {
6678
7034
  logger.error(String(err));
@@ -6728,7 +7084,7 @@ async function runPlanLoop(opts) {
6728
7084
  }
6729
7085
  if (opts.sleepSeconds > 0) {
6730
7086
  if (banners) banners.sleep(opts.sleepSeconds);
6731
- await sleep(opts.sleepSeconds * 1e3);
7087
+ await withCliProgress(`sleep ${opts.sleepSeconds}s`, () => sleep(opts.sleepSeconds * 1e3));
6732
7088
  }
6733
7089
  }
6734
7090
  const pendingNow = await pending();
@@ -6736,7 +7092,7 @@ async function runPlanLoop(opts) {
6736
7092
  const finishDetail = `after ${tick} tick(s); pending: ${pendingNow}`;
6737
7093
  if (banners) banners.phaseComplete(finishDetail);
6738
7094
  console.log(
6739
- `Loop finished after ${tick} tick(s). Pending now: ${pendingNow}. Logs in ${path38.relative(opts.root, logDir)}/`
7095
+ `Loop finished after ${tick} tick(s). Pending now: ${pendingNow}. Logs in ${path39.relative(opts.root, logDir)}/`
6740
7096
  );
6741
7097
  if (planExhausted || shouldArmExternalPlanReview({ pending: pendingNow, stopReason })) {
6742
7098
  await armExternalPlanReview(opts.root);
@@ -6807,7 +7163,7 @@ var runPlanCommand = defineCommand14({
6807
7163
  return;
6808
7164
  }
6809
7165
  const code = await runPlanLoop({
6810
- root: path39.resolve(args.cwd),
7166
+ root: path40.resolve(args.cwd),
6811
7167
  maxTicks,
6812
7168
  sleepSeconds,
6813
7169
  model: args.model ? String(args.model) : void 0,
@@ -6841,7 +7197,7 @@ var scanCommand = defineCommand15({
6841
7197
  });
6842
7198
 
6843
7199
  // src/commands/status.ts
6844
- import path40 from "path";
7200
+ import path41 from "path";
6845
7201
  import { defineCommand as defineCommand16 } from "citty";
6846
7202
  function profileStatus(profile) {
6847
7203
  if (!profile) return { origin: "none", evidence: [], profile: null };
@@ -6872,11 +7228,11 @@ var statusCommand = defineCommand16({
6872
7228
  }
6873
7229
  },
6874
7230
  async run({ args }) {
6875
- const rootDir = path40.resolve(args.cwd);
7231
+ const rootDir = path41.resolve(args.cwd);
6876
7232
  const [manifest, rawProfile, scan] = await Promise.all([
6877
7233
  loadAgentKitManifest(rootDir),
6878
7234
  readJson(
6879
- path40.join(rootDir, ".cursor", "agent-kit.config.json")
7235
+ path41.join(rootDir, ".cursor", "agent-kit.config.json")
6880
7236
  ),
6881
7237
  runScanner(rootDir)
6882
7238
  ]);
@@ -6935,7 +7291,7 @@ import { defineCommand as defineCommand17 } from "citty";
6935
7291
 
6936
7292
  // src/lifecycle/check-updates.ts
6937
7293
  import { execFile as execFile6 } from "child_process";
6938
- import path41 from "path";
7294
+ import path42 from "path";
6939
7295
  import { promisify as promisify6 } from "util";
6940
7296
  var execFileAsync5 = promisify6(execFile6);
6941
7297
  var SEMVER_CORE = /^v?(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/i;
@@ -7026,11 +7382,11 @@ function intervalElapsed2(lastCheckedAt, intervalDays) {
7026
7382
  return Date.now() - last >= ms;
7027
7383
  }
7028
7384
  async function loadContextConfig2(cwd) {
7029
- const configPath = path41.join(cwd, ".cursor", "context", "config.json");
7385
+ const configPath = path42.join(cwd, ".cursor", "context", "config.json");
7030
7386
  return readJson(configPath);
7031
7387
  }
7032
7388
  async function stampLastCheckedAt(cwd) {
7033
- const configPath = path41.join(cwd, ".cursor", "context", "config.json");
7389
+ const configPath = path42.join(cwd, ".cursor", "context", "config.json");
7034
7390
  const existing = await loadContextConfig2(cwd) ?? {};
7035
7391
  const prev = existing.updateCheck && typeof existing.updateCheck === "object" ? { ...existing.updateCheck } : {};
7036
7392
  existing.updateCheck = {
@@ -7040,6 +7396,117 @@ async function stampLastCheckedAt(cwd) {
7040
7396
  };
7041
7397
  await writeJson(configPath, existing);
7042
7398
  }
7399
+ async function readLocalKitVersion(registryRoot) {
7400
+ for (const rel of ["packages/cli/package.json", "package.json"]) {
7401
+ const data = await readJson(path42.join(registryRoot, rel));
7402
+ if (data && typeof data.version === "string" && data.version.length > 0) {
7403
+ return data.version;
7404
+ }
7405
+ }
7406
+ return null;
7407
+ }
7408
+ async function checkAgainstLocalRegistry(cwd, manifest, options) {
7409
+ const registryPath = options.registryPath;
7410
+ if (!registryPath) {
7411
+ throw new Error("checkAgainstLocalRegistry requires registryPath");
7412
+ }
7413
+ let resolved;
7414
+ try {
7415
+ resolved = await resolveRegistryRoot({ cwd, registryPath });
7416
+ } catch (err) {
7417
+ const msg = err instanceof Error ? err.message : String(err);
7418
+ return {
7419
+ status: "error",
7420
+ installedVersion: manifest.version,
7421
+ latestVersion: null,
7422
+ registryUrl: path42.resolve(registryPath),
7423
+ registryRef: "local",
7424
+ applyRecommended: false,
7425
+ message: `Failed to resolve --registry: ${msg}`
7426
+ };
7427
+ }
7428
+ const registryUrl = resolved.root;
7429
+ const registryRef = "local";
7430
+ try {
7431
+ let latest;
7432
+ if (options.latestVersion !== void 0) {
7433
+ latest = options.latestVersion;
7434
+ } else {
7435
+ const raw = await readLocalKitVersion(resolved.root);
7436
+ latest = raw ? normalizeSemver(raw) : null;
7437
+ }
7438
+ const summary = summarizeDiff(await diffAgainstRegistry(resolved.root, cwd, manifest));
7439
+ const hasFileDrift = summary.drift > 0 || summary["missing-local"] > 0;
7440
+ const installed = normalizeSemver(manifest.version);
7441
+ if (!installed) {
7442
+ return {
7443
+ status: "error",
7444
+ installedVersion: manifest.version,
7445
+ latestVersion: latest,
7446
+ registryUrl,
7447
+ registryRef,
7448
+ applyRecommended: false,
7449
+ message: `Installed version "${manifest.version}" is not valid semver.`
7450
+ };
7451
+ }
7452
+ if (!latest) {
7453
+ return {
7454
+ status: "error",
7455
+ installedVersion: installed,
7456
+ latestVersion: null,
7457
+ registryUrl,
7458
+ registryRef,
7459
+ applyRecommended: false,
7460
+ message: "No semver found in the local --registry checkout (packages/cli/package.json or package.json)."
7461
+ };
7462
+ }
7463
+ const cmp = compareSemver(installed, latest);
7464
+ if (hasFileDrift) {
7465
+ return {
7466
+ status: "update-available",
7467
+ installedVersion: installed,
7468
+ latestVersion: latest,
7469
+ registryUrl,
7470
+ registryRef,
7471
+ applyRecommended: false,
7472
+ message: `Local registry has drift vs this install (drift=${summary.drift}, missing-local=${summary["missing-local"]}; source v${latest}). Run /update --registry <path> (Ask confirm) to apply; never silent.`
7473
+ };
7474
+ }
7475
+ if (cmp === 0) {
7476
+ return {
7477
+ status: "up-to-date",
7478
+ installedVersion: installed,
7479
+ latestVersion: latest,
7480
+ registryUrl,
7481
+ registryRef,
7482
+ applyRecommended: false,
7483
+ message: `Installed v${installed} matches local registry v${latest}.`
7484
+ };
7485
+ }
7486
+ if (cmp < 0) {
7487
+ return {
7488
+ status: "update-available",
7489
+ installedVersion: installed,
7490
+ latestVersion: latest,
7491
+ registryUrl,
7492
+ registryRef,
7493
+ applyRecommended: false,
7494
+ message: `Update available from local registry: v${installed} \u2192 v${latest}. Run /update --registry <path> (Ask confirm) to apply; never silent.`
7495
+ };
7496
+ }
7497
+ return {
7498
+ status: "ahead-of-public",
7499
+ installedVersion: installed,
7500
+ latestVersion: latest,
7501
+ registryUrl,
7502
+ registryRef,
7503
+ applyRecommended: false,
7504
+ message: `Installed v${installed} is ahead of local registry v${latest}.`
7505
+ };
7506
+ } finally {
7507
+ await resolved.unlock?.();
7508
+ }
7509
+ }
7043
7510
  async function checkForUpdates(cwd, options = {}) {
7044
7511
  const manifest = await loadAgentKitManifest(cwd);
7045
7512
  if (!manifest) {
@@ -7053,7 +7520,10 @@ async function checkForUpdates(cwd, options = {}) {
7053
7520
  message: "No .cursor/agent-kit.json \u2014 run agent-kit install first."
7054
7521
  };
7055
7522
  }
7056
- const registryUrl = manifest.registry?.url ?? null;
7523
+ if (options.registryPath) {
7524
+ return checkAgainstLocalRegistry(cwd, manifest, options);
7525
+ }
7526
+ const registryUrl = options.publicRegistryUrl ?? manifest.registry?.url ?? null;
7057
7527
  const registryRef = manifest.registry?.ref ?? null;
7058
7528
  if (isFactoryOrDevRegistry(registryUrl, registryRef)) {
7059
7529
  return {
@@ -7183,7 +7653,7 @@ var updateCommand = defineCommand17({
7183
7653
  },
7184
7654
  check: {
7185
7655
  type: "boolean",
7186
- description: "Check-only: compare installed version to latest public tag; never apply L0 writes",
7656
+ description: "Check-only: compare installed version to the resolved registry (public tags, or --registry local checkout); never apply L0 writes",
7187
7657
  default: false
7188
7658
  },
7189
7659
  json: {
@@ -7224,7 +7694,8 @@ var updateCommand = defineCommand17({
7224
7694
  const result = await checkForUpdates(args.cwd, {
7225
7695
  respectPrefs: Boolean(args["respect-prefs"]),
7226
7696
  stamp: Boolean(args.stamp),
7227
- publicRegistryUrl: typeof args.url === "string" && args.url ? args.url : void 0
7697
+ publicRegistryUrl: typeof args.url === "string" && args.url ? args.url : void 0,
7698
+ registryPath: typeof args.registry === "string" && args.registry ? args.registry : void 0
7228
7699
  });
7229
7700
  if (args.json) {
7230
7701
  console.log(JSON.stringify(result, null, 2));
@@ -7287,7 +7758,10 @@ var updateCommand = defineCommand17({
7287
7758
  await seedManagedHashLedger(projectRoot);
7288
7759
  logger.info("Seeded managed-hash ledger from current local overlay files.");
7289
7760
  }
7290
- const stats = await syncFromManifest(registry.root, projectRoot, next);
7761
+ const stats = await withCliProgress(
7762
+ "update",
7763
+ () => syncFromManifest(registry.root, projectRoot, next)
7764
+ );
7291
7765
  await saveManifest(projectRoot, next);
7292
7766
  logApplyStats(stats);
7293
7767
  logger.success("Update complete (L3 protected paths left untouched).");
@@ -7299,7 +7773,7 @@ var updateCommand = defineCommand17({
7299
7773
 
7300
7774
  // src/commands/validate.ts
7301
7775
  import { readFile as readFile21 } from "fs/promises";
7302
- import path42 from "path";
7776
+ import path43 from "path";
7303
7777
  import { defineCommand as defineCommand18 } from "citty";
7304
7778
 
7305
7779
  // src/invariants/plan-schema.ts
@@ -7346,7 +7820,7 @@ function validatePlanFrontmatterText(text) {
7346
7820
  // src/commands/validate.ts
7347
7821
  async function resolveEditedPath(cwd, explicit) {
7348
7822
  if (explicit) {
7349
- const filePath2 = path42.resolve(cwd, explicit);
7823
+ const filePath2 = path43.resolve(cwd, explicit);
7350
7824
  try {
7351
7825
  return { filePath: filePath2, content: await readFile21(filePath2, "utf8") };
7352
7826
  } catch {
@@ -7356,7 +7830,7 @@ async function resolveEditedPath(cwd, explicit) {
7356
7830
  const payload = await readStdinJson();
7357
7831
  const rel = typeof payload.file_path === "string" && payload.file_path || typeof payload.path === "string" && payload.path || typeof payload.file === "string" && payload.file || "";
7358
7832
  if (!rel) return null;
7359
- const filePath = path42.isAbsolute(rel) ? rel : path42.resolve(cwd, rel);
7833
+ const filePath = path43.isAbsolute(rel) ? rel : path43.resolve(cwd, rel);
7360
7834
  try {
7361
7835
  return { filePath, content: await readFile21(filePath, "utf8") };
7362
7836
  } catch {
@@ -7386,7 +7860,7 @@ var validateCommand = defineCommand18({
7386
7860
  async run({ args }) {
7387
7861
  const cwd = typeof args.cwd === "string" ? args.cwd : process.cwd();
7388
7862
  const fileArg = typeof args.file === "string" ? args.file : void 0;
7389
- const filePath = fileArg ? path42.resolve(cwd, fileArg) : path42.join(path42.resolve(cwd), ".cursor", "HANDOFF.md");
7863
+ const filePath = fileArg ? path43.resolve(cwd, fileArg) : path43.join(path43.resolve(cwd), ".cursor", "HANDOFF.md");
7390
7864
  let content = "";
7391
7865
  try {
7392
7866
  content = await readFile21(filePath, "utf8");
@@ -7413,7 +7887,7 @@ var validateCommand = defineCommand18({
7413
7887
  process.exitCode = 2;
7414
7888
  return;
7415
7889
  }
7416
- const filePath = path42.resolve(cwd, fileArg);
7890
+ const filePath = path43.resolve(cwd, fileArg);
7417
7891
  let content = "";
7418
7892
  try {
7419
7893
  content = await readFile21(filePath, "utf8");
@@ -7435,7 +7909,7 @@ var validateCommand = defineCommand18({
7435
7909
  },
7436
7910
  async run({ args }) {
7437
7911
  const cwd = typeof args.cwd === "string" ? args.cwd : process.cwd();
7438
- const resolved = await resolveEditedPath(path42.resolve(cwd));
7912
+ const resolved = await resolveEditedPath(path43.resolve(cwd));
7439
7913
  if (!resolved) {
7440
7914
  console.log(JSON.stringify({}));
7441
7915
  return;
@@ -7478,19 +7952,18 @@ var validateCommand = defineCommand18({
7478
7952
  });
7479
7953
 
7480
7954
  // src/welcome/help-groups.ts
7481
- import { bold, gray as gray3, underline } from "kolorist";
7955
+ import { bold, gray as gray4, underline } from "kolorist";
7482
7956
 
7483
7957
  // src/welcome/screen.ts
7484
7958
  import {
7485
7959
  blue as blue2,
7486
- cyan as cyan3,
7487
- gray as gray2,
7488
- options as koloristOptions,
7960
+ cyan as cyan4,
7961
+ gray as gray3,
7962
+ options as koloristOptions2,
7489
7963
  lightCyan,
7490
7964
  trueColor,
7491
7965
  white as white2
7492
7966
  } from "kolorist";
7493
- var HELMET_OUTLINE = "#e2e8f0";
7494
7967
  var KOLORIST_TRUECOLOR = 3;
7495
7968
  var HELMET_ASCII = [
7496
7969
  " ____",
@@ -7515,30 +7988,20 @@ function outlineAnsi(line) {
7515
7988
  return trueColor(r, g, b)(line);
7516
7989
  }
7517
7990
  function withKoloristColor(fn) {
7518
- const prevEnabled = koloristOptions.enabled;
7519
- const prevLevel = koloristOptions.supportLevel;
7520
- koloristOptions.enabled = true;
7521
- koloristOptions.supportLevel = KOLORIST_TRUECOLOR;
7991
+ const prevEnabled = koloristOptions2.enabled;
7992
+ const prevLevel = koloristOptions2.supportLevel;
7993
+ koloristOptions2.enabled = true;
7994
+ koloristOptions2.supportLevel = KOLORIST_TRUECOLOR;
7522
7995
  try {
7523
7996
  return fn();
7524
7997
  } finally {
7525
- koloristOptions.enabled = prevEnabled;
7526
- koloristOptions.supportLevel = prevLevel;
7998
+ koloristOptions2.enabled = prevEnabled;
7999
+ koloristOptions2.supportLevel = prevLevel;
7527
8000
  }
7528
8001
  }
7529
8002
  function hasCliSubcommand(rawArgs) {
7530
8003
  return Boolean(rawArgs?.some((arg) => !arg.startsWith("-")));
7531
8004
  }
7532
- function shouldUseWelcomeColor(opts = {}) {
7533
- if (opts.color === false) return false;
7534
- if (process.env.NO_COLOR) return false;
7535
- if (process.env.NODE_DISABLE_COLORS) return false;
7536
- if (process.env.FORCE_COLOR === "0") return false;
7537
- if (process.env.CI != null && process.env.CI !== "") return false;
7538
- if (opts.color === true) return true;
7539
- const tty = opts.stdoutIsTTY ?? Boolean(process.stdout.isTTY);
7540
- return tty;
7541
- }
7542
8005
  function renderHelmetAscii(color) {
7543
8006
  if (!color) return HELMET_ASCII.join("\n");
7544
8007
  return withKoloristColor(
@@ -7560,8 +8023,8 @@ function renderWelcomeScreen(opts = {}) {
7560
8023
  const version = opts.version ?? KIT_VERSION;
7561
8024
  const color = shouldUseWelcomeColor(opts);
7562
8025
  const title = color ? white2("Mission Kit") : "Mission Kit";
7563
- const product = color ? cyan3("agent-kit") : "agent-kit";
7564
- const muted = (s) => color ? gray2(s) : s;
8026
+ const product = color ? cyan4("agent-kit") : "agent-kit";
8027
+ const muted = (s) => color ? gray3(s) : s;
7565
8028
  const lines = [
7566
8029
  renderHelmetAscii(color),
7567
8030
  "",
@@ -7573,13 +8036,18 @@ function renderWelcomeScreen(opts = {}) {
7573
8036
  const width = Math.max(...WELCOME_UTILITY_HINTS.map(({ cmd }) => cmd.length));
7574
8037
  return WELCOME_UTILITY_HINTS.map(({ cmd, hint }) => {
7575
8038
  const pad = " ".repeat(width - cmd.length + 2);
7576
- const left = color ? cyan3(` ${cmd}`) : ` ${cmd}`;
8039
+ const left = color ? cyan4(` ${cmd}`) : ` ${cmd}`;
7577
8040
  return `${left}${pad}${muted(hint)}`;
7578
8041
  });
7579
8042
  })(),
7580
8043
  "",
7581
8044
  muted(
7582
8045
  "Chat HITL (start-project, git-staging/prod, run-plan-all) stays in Cursor slash commands."
8046
+ ),
8047
+ muted(
8048
+ `${shouldUseVisualMotion(opts) ? SPACE_MARKS.star : SPACE_MARKS.tick} ${tipAt(
8049
+ [...version].reduce((n, c) => n + c.charCodeAt(0), 0)
8050
+ )}`
7583
8051
  )
7584
8052
  ];
7585
8053
  return `${lines.join("\n")}
@@ -7630,7 +8098,7 @@ async function resolveSubMeta(subCommands) {
7630
8098
  async function renderGroupedRootHelp(cmd) {
7631
8099
  const color = shouldUseWelcomeColor();
7632
8100
  const u = (s) => color ? underline(bold(s)) : s;
7633
- const g = (s) => color ? gray3(s) : s;
8101
+ const g = (s) => color ? gray4(s) : s;
7634
8102
  const meta = await resolveCommandMeta(cmd.meta);
7635
8103
  const name = meta?.name ?? "agent-kit";
7636
8104
  const version = meta?.version ?? KIT_VERSION;
@@ -7660,11 +8128,14 @@ async function renderGroupedRootHelp(cmd) {
7660
8128
  }
7661
8129
  lines.push("");
7662
8130
  }
8131
+ const tipMark = shouldUseVisualMotion() ? SPACE_MARKS.star : SPACE_MARKS.tick;
8132
+ const tip = tipAt([...version].reduce((n, c) => n + c.charCodeAt(0), 0));
7663
8133
  lines.push(
7664
8134
  g(`Use \`${name} <command> --help\` for more information about a command.`),
7665
8135
  g(
7666
8136
  "Chat-only HITL (start-project, backlog, git-staging/prod, run-plan-all) is not a CLI surface."
7667
8137
  ),
8138
+ g(`${tipMark} ${tip}`),
7668
8139
  ""
7669
8140
  );
7670
8141
  return lines.join("\n");