@dadado/agent-kit-cli 5.10.3 → 5.12.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
@@ -10,12 +10,15 @@ import { defineCommand } from "citty";
10
10
  import { copyFile, mkdir as mkdir3, readFile as readFile3 } from "fs/promises";
11
11
  import path4 from "path";
12
12
 
13
+ // src/utils/kit-paths.ts
14
+ var HANDOFF_REL = ".cursor/HANDOFF.md";
15
+
13
16
  // src/manifest/types.ts
14
17
  var MANIFEST_FILENAME = "agent-kit.json";
15
18
  var MANIFEST_RELATIVE_PATH = `.cursor/${MANIFEST_FILENAME}`;
16
19
  var MANIFEST_SCHEMA_VERSION = 1;
17
20
  var DEFAULT_PROTECTED_PATHS = [
18
- ".cursor/HANDOFF.md",
21
+ HANDOFF_REL,
19
22
  ".cursor/plans/**",
20
23
  ".cursor/memory/**",
21
24
  ".cursor/context/config.json",
@@ -342,7 +345,16 @@ var KNOWN_SHIPPED_OVERLAY_HASHES = /* @__PURE__ */ new Set([
342
345
  "18426bc3bc8fdf0ab7577d92e31310faf3b83001f8e6e6fe5ef1a1043230e178",
343
346
  "24cc918e7ffa54df7f6007989fe62b83af1035c9bf6071cac8c4f848e28b04ca",
344
347
  "76757eae42daccc0d82c9630eab31041c6d6ea8fe4b4a47843574a6057dd2a4b",
345
- "88db17beb9ce2b95b2ce2c1df753b96933024a464cc847770d3f05cd7de2872c"
348
+ "88db17beb9ce2b95b2ce2c1df753b96933024a464cc847770d3f05cd7de2872c",
349
+ "71fff34e9392692d58f3dc92ed65d070f0b476446e0a183a8fbad66d46f3a785",
350
+ "73e3ca429cb522b0877f0d6d46bedbaf270f94bc63bb87f4e0fc7c512bf36e9c",
351
+ "9a61f7432cf66be53d42b5d6f3c67ea71dd5efa1cc18d77d996f08ffea873373",
352
+ "fd2a8867de62f83c89ae11295cae5b5699360b5205f9726c2c47c064318bd7a4",
353
+ "364126473e089ed09a71e3f9eed80b1d602b35b4baa669b0a4f4be0519a787f8",
354
+ "cb14c0162e66ac565679089a566a725b26989db3536b7dba89bff77cb6010969",
355
+ "ebf490086ea45a7b342b3ec7408c9a82b22031daaefddf4425b6e6e931f6d6c8",
356
+ "3077e013297c7dd1e32eb2fa86e1ffa1de7d8740058652782bb8d391782eed71",
357
+ "7e0fa1bb73c8b6f1358cae19477304d85edfb7d4d9a7faf37c00e0578ed79edf"
346
358
  ]);
347
359
 
348
360
  // src/lifecycle/paths.ts
@@ -713,6 +725,11 @@ import { mkdir as mkdir4, readFile as readFile4, rename, rm, stat, writeFile as
713
725
  import { homedir } from "os";
714
726
  import path5 from "path";
715
727
  import { promisify } from "util";
728
+
729
+ // src/registry/paths.ts
730
+ var REGISTRY_INDEX_REL = "registry/registry.json";
731
+
732
+ // src/registry/resolve.ts
716
733
  var execFileAsync = promisify(execFile);
717
734
  var LOCK_TIMEOUT_MS = 6e4;
718
735
  var LOCK_RETRY_MS = 200;
@@ -871,7 +888,7 @@ function cacheKey(url, ref) {
871
888
  return createHash2("sha256").update(`${url}@${ref}`).digest("hex").slice(0, 16);
872
889
  }
873
890
  async function hasRegistryIndex(root) {
874
- return fileExists(path5.join(root, "registry", "registry.json"));
891
+ return fileExists(path5.join(root, REGISTRY_INDEX_REL));
875
892
  }
876
893
  async function cloneRegistry(url, ref, dest) {
877
894
  const { mkdir: mkdir9 } = await import("fs/promises");
@@ -919,7 +936,7 @@ async function resolveRegistryRoot(options) {
919
936
  if (options.registryPath) {
920
937
  const root = path5.resolve(options.registryPath);
921
938
  if (!await hasRegistryIndex(root)) {
922
- throw new Error(`No registry/registry.json under --registry ${root}`);
939
+ throw new Error(`No ${REGISTRY_INDEX_REL} under --registry ${root}`);
923
940
  }
924
941
  return { root, source: "flag" };
925
942
  }
@@ -927,7 +944,7 @@ async function resolveRegistryRoot(options) {
927
944
  if (envPath) {
928
945
  const root = path5.resolve(envPath);
929
946
  if (!await hasRegistryIndex(root)) {
930
- throw new Error(`No registry/registry.json under AGENT_KIT_REGISTRY=${root}`);
947
+ throw new Error(`No ${REGISTRY_INDEX_REL} under AGENT_KIT_REGISTRY=${root}`);
931
948
  }
932
949
  return { root, source: "env", url: options.registryUrl, ref: options.registryRef };
933
950
  }
@@ -951,7 +968,7 @@ async function resolveRegistryRoot(options) {
951
968
  } else {
952
969
  await cloneRegistry(url, ref, dest);
953
970
  if (!await hasRegistryIndex(dest)) {
954
- throw new Error(`Cloned ${url}@${ref} but registry/registry.json is missing`);
971
+ throw new Error(`Cloned ${url}@${ref} but ${REGISTRY_INDEX_REL} is missing`);
955
972
  }
956
973
  }
957
974
  ownershipTransferred = true;
@@ -1201,7 +1218,7 @@ async function loadAgentKitManifest(cwd) {
1201
1218
  // src/registry/client.ts
1202
1219
  import path7 from "path";
1203
1220
  async function loadRegistry(rootDir) {
1204
- const indexPath = path7.join(rootDir, "registry", "registry.json");
1221
+ const indexPath = path7.join(rootDir, REGISTRY_INDEX_REL);
1205
1222
  const index = await readJson(indexPath);
1206
1223
  if (!index) throw new Error(`Registry not found at ${indexPath}`);
1207
1224
  return index;
@@ -1318,7 +1335,7 @@ async function installSkill(registryRoot, projectRoot, skill, options = {}) {
1318
1335
  return stats;
1319
1336
  }
1320
1337
  async function installSkillsByIds(registryRoot, projectRoot, skillIds, options = {}) {
1321
- const index = await readJson(path8.join(registryRoot, "registry", "registry.json"));
1338
+ const index = await readJson(path8.join(registryRoot, REGISTRY_INDEX_REL));
1322
1339
  const stats = emptyStats();
1323
1340
  if (!index) return stats;
1324
1341
  const pool = allSkills(index);
@@ -2117,22 +2134,40 @@ import { defineCommand as defineCommand3 } from "citty";
2117
2134
 
2118
2135
  // src/lifecycle/cursor-update-awareness.ts
2119
2136
  import { access as access2, readFile as readFile7 } from "fs/promises";
2137
+ import path11 from "path";
2138
+
2139
+ // src/lifecycle/context-config.ts
2120
2140
  import path10 from "path";
2141
+ var CONTEXT_CONFIG_REL = path10.join(".cursor", "context", "config.json");
2142
+ function contextConfigPath(cwd) {
2143
+ return path10.join(cwd, CONTEXT_CONFIG_REL);
2144
+ }
2145
+ async function loadContextConfig(cwd) {
2146
+ return readJson(contextConfigPath(cwd));
2147
+ }
2148
+ function intervalElapsed(lastCheckedAt, intervalDays) {
2149
+ if (!lastCheckedAt) return true;
2150
+ const last = Date.parse(lastCheckedAt);
2151
+ if (Number.isNaN(last)) return true;
2152
+ return Date.now() - last >= intervalDays * 24 * 60 * 60 * 1e3;
2153
+ }
2154
+
2155
+ // src/lifecycle/cursor-update-awareness.ts
2121
2156
  var DEFAULT_CURSOR_CHANGELOG_URL = "https://cursor.com/changelog";
2122
2157
  var CHANGELOG_FETCH_TIMEOUT_MS = 15e3;
2123
2158
  var CURSOR_VERSION_MAJOR_MAX = 20;
2124
- var INVENTORY_REL = path10.join("docs", "cursor-native-audit.md");
2125
- var FEATURES_REL = path10.join("docs", "cursor-3-features.md");
2159
+ var INVENTORY_REL = path11.join("docs", "cursor-native-audit.md");
2160
+ var FEATURES_REL = path11.join("docs", "cursor-3-features.md");
2126
2161
  async function resolveInventoryRoot(cwd) {
2127
- let dir = path10.resolve(cwd);
2162
+ let dir = path11.resolve(cwd);
2128
2163
  for (; ; ) {
2129
2164
  try {
2130
- await access2(path10.join(dir, INVENTORY_REL));
2165
+ await access2(path11.join(dir, INVENTORY_REL));
2131
2166
  return dir;
2132
2167
  } catch {
2133
2168
  }
2134
- if (await fileExists(path10.join(dir, ".git"))) break;
2135
- const parent = path10.dirname(dir);
2169
+ if (await fileExists(path11.join(dir, ".git"))) break;
2170
+ const parent = path11.dirname(dir);
2136
2171
  if (parent === dir) break;
2137
2172
  dir = parent;
2138
2173
  }
@@ -2168,18 +2203,7 @@ function readCursorUpdateCheckPrefs(config) {
2168
2203
  changelogUrl
2169
2204
  };
2170
2205
  }
2171
- function intervalElapsed(lastCheckedAt, intervalDays) {
2172
- if (!lastCheckedAt) return true;
2173
- const last = Date.parse(lastCheckedAt);
2174
- if (Number.isNaN(last)) return true;
2175
- return Date.now() - last >= intervalDays * 24 * 60 * 60 * 1e3;
2176
- }
2177
- async function loadContextConfig(cwd) {
2178
- const configPath = path10.join(cwd, ".cursor", "context", "config.json");
2179
- return readJson(configPath);
2180
- }
2181
2206
  async function stampCursorUpdateCheck(cwd, patch) {
2182
- const configPath = path10.join(cwd, ".cursor", "context", "config.json");
2183
2207
  const existing = await loadContextConfig(cwd) ?? {};
2184
2208
  const prev = existing.cursorUpdateCheck && typeof existing.cursorUpdateCheck === "object" ? { ...existing.cursorUpdateCheck } : {};
2185
2209
  const nextSeen = patch.lastSeenCursorVersion !== void 0 ? patch.lastSeenCursorVersion : void 0;
@@ -2189,7 +2213,7 @@ async function stampCursorUpdateCheck(cwd, patch) {
2189
2213
  ...prev,
2190
2214
  lastCheckedAt: (/* @__PURE__ */ new Date()).toISOString()
2191
2215
  };
2192
- await writeJson(configPath, existing);
2216
+ await writeJson(contextConfigPath(cwd), existing);
2193
2217
  return;
2194
2218
  }
2195
2219
  existing.cursorUpdateCheck = {
@@ -2198,7 +2222,7 @@ async function stampCursorUpdateCheck(cwd, patch) {
2198
2222
  lastCheckedAt: (/* @__PURE__ */ new Date()).toISOString(),
2199
2223
  ...nextSeen !== void 0 ? { lastSeenCursorVersion: nextSeen } : {}
2200
2224
  };
2201
- await writeJson(configPath, existing);
2225
+ await writeJson(contextConfigPath(cwd), existing);
2202
2226
  }
2203
2227
  function isPlausibleCursorVersion(version) {
2204
2228
  const parts = String(version).split(".");
@@ -2448,8 +2472,8 @@ async function checkCursorUpdateAwareness(cwd, options = {}) {
2448
2472
  message: missingInventoryMessage()
2449
2473
  });
2450
2474
  }
2451
- const inventoryPath = path10.join(inventoryRoot, INVENTORY_REL);
2452
- const featuresPath = path10.join(inventoryRoot, FEATURES_REL);
2475
+ const inventoryPath = path11.join(inventoryRoot, INVENTORY_REL);
2476
+ const featuresPath = path11.join(inventoryRoot, FEATURES_REL);
2453
2477
  let inventoryMd;
2454
2478
  try {
2455
2479
  inventoryMd = await readFile7(inventoryPath, "utf8");
@@ -2662,14 +2686,14 @@ var cursorAwarenessCommand = defineCommand3({
2662
2686
  // src/commands/dashboard-broadcast.ts
2663
2687
  import { spawn as spawn3 } from "child_process";
2664
2688
  import { access as access4 } from "fs/promises";
2665
- import path12 from "path";
2689
+ import path13 from "path";
2666
2690
  import { defineCommand as defineCommand5 } from "citty";
2667
2691
 
2668
2692
  // src/commands/dashboard.ts
2669
2693
  import { execFileSync, spawn as spawn2 } from "child_process";
2670
2694
  import { existsSync, readFileSync as readFileSync2, realpathSync } from "fs";
2671
2695
  import { access as access3 } from "fs/promises";
2672
- import path11 from "path";
2696
+ import path12 from "path";
2673
2697
  import { fileURLToPath } from "url";
2674
2698
  import { defineCommand as defineCommand4 } from "citty";
2675
2699
 
@@ -2679,7 +2703,7 @@ import { networkInterfaces } from "os";
2679
2703
  import { resolve } from "path";
2680
2704
  var REPO_ROOT_ENV = "MISSION_CONTROL_REPO_ROOT";
2681
2705
  var KIT_ROOT_ENV_KEYS = Object.freeze(["MISSION_CONTROL_KIT_ROOT", "AGENT_KIT_HOME"]);
2682
- var CONTEXT_CONFIG_REL = ".cursor/context/config.json";
2706
+ var CONTEXT_CONFIG_REL2 = ".cursor/context/config.json";
2683
2707
  var CONFIG_PERSONA_IDS = Object.freeze(["autopilot", "night-shift", "ghost-runner"]);
2684
2708
  var CONFIG_PERSONA_MODES = Object.freeze(["continue-plan", "run-plan", "cli-run-plan"]);
2685
2709
  var CONFIG_REVIEW_BACKENDS = Object.freeze(["auto", "claude", "cursor"]);
@@ -2692,7 +2716,7 @@ function resolveContextConfigPath(repoRoot, fsHooks = {}) {
2692
2716
  if (typeof repoRoot !== "string" || !repoRoot) {
2693
2717
  return { ok: false, error: "invalid repo root" };
2694
2718
  }
2695
- const abs = resolve(repoRoot, CONTEXT_CONFIG_REL);
2719
+ const abs = resolve(repoRoot, CONTEXT_CONFIG_REL2);
2696
2720
  const contextDir = resolve(repoRoot, ".cursor", "context");
2697
2721
  const absNorm = abs.replace(/\\/g, "/");
2698
2722
  if (!absNorm.endsWith("/.cursor/context/config.json")) {
@@ -2758,7 +2782,7 @@ function readPreferredBrowserFromConfig(configPath, fsHooks = {}) {
2758
2782
 
2759
2783
  // src/commands/dashboard.ts
2760
2784
  function readPreferredBrowserFromWorkspace(cwd, readFile31 = readFileSync2) {
2761
- const resolved = resolveContextConfigPath(path11.resolve(cwd), {
2785
+ const resolved = resolveContextConfigPath(path12.resolve(cwd), {
2762
2786
  existsSync,
2763
2787
  realpathSync
2764
2788
  });
@@ -2780,61 +2804,61 @@ function applyDashboardOpenEnv(env, opts) {
2780
2804
  return next;
2781
2805
  }
2782
2806
  function dashboardSpawnEnv(env, snapshotRoot, opts) {
2783
- const root = path11.resolve(snapshotRoot);
2807
+ const root = path12.resolve(snapshotRoot);
2784
2808
  return applyDashboardOpenEnv({ ...env, MISSION_CONTROL_REPO_ROOT: root }, { ...opts, cwd: root });
2785
2809
  }
2786
2810
  function dashboardProcessTitle(snapshotRoot) {
2787
- const base = path11.basename(path11.resolve(snapshotRoot)) || "workspace";
2811
+ const base = path12.basename(path12.resolve(snapshotRoot)) || "workspace";
2788
2812
  return `Mission Control \xB7 ${base}`;
2789
2813
  }
2790
2814
  function bundledDashboardCandidates(filename, moduleUrl = import.meta.url) {
2791
- const here = path11.dirname(fileURLToPath(moduleUrl));
2815
+ const here = path12.dirname(fileURLToPath(moduleUrl));
2792
2816
  return [
2793
2817
  // Published / built: <pkg>/dist/index.js -> <pkg>/dashboard/
2794
- path11.join(here, "..", "dashboard", filename),
2818
+ path12.join(here, "..", "dashboard", filename),
2795
2819
  // Dev source: packages/cli/src/commands/*.ts -> packages/cli/dashboard/ (sync copy)
2796
- path11.join(here, "..", "..", "dashboard", filename),
2820
+ path12.join(here, "..", "..", "dashboard", filename),
2797
2821
  // From src/commands: packages/dashboard/ (unused layout); from dist: two levels above package
2798
- path11.join(here, "..", "..", "..", "dashboard", filename)
2822
+ path12.join(here, "..", "..", "..", "dashboard", filename)
2799
2823
  ];
2800
2824
  }
2801
2825
  async function firstExisting(candidates) {
2802
2826
  for (const candidate2 of candidates) {
2803
2827
  try {
2804
2828
  await access3(candidate2);
2805
- return path11.resolve(candidate2);
2829
+ return path12.resolve(candidate2);
2806
2830
  } catch {
2807
2831
  }
2808
2832
  }
2809
2833
  return null;
2810
2834
  }
2811
2835
  async function findDashboardStart(cwd, env = process.env, options = {}) {
2812
- let dir = path11.resolve(cwd);
2836
+ let dir = path12.resolve(cwd);
2813
2837
  for (; ; ) {
2814
- const candidate2 = path11.join(dir, "dashboard", "start.mjs");
2838
+ const candidate2 = path12.join(dir, "dashboard", "start.mjs");
2815
2839
  try {
2816
2840
  await access3(candidate2);
2817
2841
  return candidate2;
2818
2842
  } catch {
2819
2843
  }
2820
- const parent = path11.dirname(dir);
2844
+ const parent = path12.dirname(dir);
2821
2845
  if (parent === dir) break;
2822
2846
  dir = parent;
2823
2847
  }
2824
2848
  for (const key of ["MISSION_CONTROL_KIT_ROOT", "AGENT_KIT_HOME"]) {
2825
2849
  const base = env[key];
2826
2850
  if (!base || typeof base !== "string" || !base.trim()) continue;
2827
- const candidate2 = path11.join(path11.resolve(base.trim()), "dashboard", "start.mjs");
2851
+ const candidate2 = path12.join(path12.resolve(base.trim()), "dashboard", "start.mjs");
2828
2852
  try {
2829
2853
  await access3(candidate2);
2830
2854
  return candidate2;
2831
2855
  } catch {
2832
2856
  }
2833
2857
  }
2834
- const sibling = path11.join(path11.resolve(cwd), "..", "agent-kit", "dashboard", "start.mjs");
2858
+ const sibling = path12.join(path12.resolve(cwd), "..", "agent-kit", "dashboard", "start.mjs");
2835
2859
  try {
2836
2860
  await access3(sibling);
2837
- return path11.resolve(sibling);
2861
+ return path12.resolve(sibling);
2838
2862
  } catch {
2839
2863
  }
2840
2864
  return firstExisting(
@@ -2842,13 +2866,13 @@ async function findDashboardStart(cwd, env = process.env, options = {}) {
2842
2866
  );
2843
2867
  }
2844
2868
  function resolveDashboardSnapshotRoot(cwd) {
2845
- const abs = path11.resolve(cwd);
2869
+ const abs = path12.resolve(cwd);
2846
2870
  let dir = abs;
2847
2871
  for (; ; ) {
2848
- if (existsSync(path11.join(dir, ".cursor", "agent-kit.json"))) {
2872
+ if (existsSync(path12.join(dir, ".cursor", "agent-kit.json"))) {
2849
2873
  return dir;
2850
2874
  }
2851
- const parent = path11.dirname(dir);
2875
+ const parent = path12.dirname(dir);
2852
2876
  if (parent === dir) break;
2853
2877
  dir = parent;
2854
2878
  }
@@ -2858,7 +2882,7 @@ function resolveDashboardSnapshotRoot(cwd) {
2858
2882
  timeout: 5e3,
2859
2883
  stdio: ["ignore", "pipe", "ignore"]
2860
2884
  }).trim();
2861
- if (top) return path11.resolve(top);
2885
+ if (top) return path12.resolve(top);
2862
2886
  } catch {
2863
2887
  }
2864
2888
  return abs;
@@ -2866,7 +2890,7 @@ function resolveDashboardSnapshotRoot(cwd) {
2866
2890
  function runStartScript(startPath, env) {
2867
2891
  return new Promise((resolve3, reject) => {
2868
2892
  const child = spawn2(process.execPath, [startPath], {
2869
- cwd: path11.dirname(path11.dirname(startPath)),
2893
+ cwd: path12.dirname(path12.dirname(startPath)),
2870
2894
  env,
2871
2895
  stdio: "inherit"
2872
2896
  });
@@ -2934,30 +2958,30 @@ var dashboardCommand = defineCommand4({
2934
2958
 
2935
2959
  // src/commands/dashboard-broadcast.ts
2936
2960
  async function findDashboardBroadcastStart(cwd, env = process.env, options = {}) {
2937
- let dir = path12.resolve(cwd);
2961
+ let dir = path13.resolve(cwd);
2938
2962
  for (; ; ) {
2939
- const candidate2 = path12.join(dir, "dashboard", "start-broadcast.mjs");
2963
+ const candidate2 = path13.join(dir, "dashboard", "start-broadcast.mjs");
2940
2964
  try {
2941
2965
  await access4(candidate2);
2942
2966
  return candidate2;
2943
2967
  } catch {
2944
2968
  }
2945
- const parent = path12.dirname(dir);
2969
+ const parent = path13.dirname(dir);
2946
2970
  if (parent === dir) break;
2947
2971
  dir = parent;
2948
2972
  }
2949
2973
  for (const key of ["MISSION_CONTROL_KIT_ROOT", "AGENT_KIT_HOME"]) {
2950
2974
  const base = env[key];
2951
2975
  if (!base || typeof base !== "string" || !base.trim()) continue;
2952
- const candidate2 = path12.join(path12.resolve(base.trim()), "dashboard", "start-broadcast.mjs");
2976
+ const candidate2 = path13.join(path13.resolve(base.trim()), "dashboard", "start-broadcast.mjs");
2953
2977
  try {
2954
2978
  await access4(candidate2);
2955
2979
  return candidate2;
2956
2980
  } catch {
2957
2981
  }
2958
2982
  }
2959
- const sibling = path12.join(
2960
- path12.resolve(cwd),
2983
+ const sibling = path13.join(
2984
+ path13.resolve(cwd),
2961
2985
  "..",
2962
2986
  "agent-kit",
2963
2987
  "dashboard",
@@ -2965,7 +2989,7 @@ async function findDashboardBroadcastStart(cwd, env = process.env, options = {})
2965
2989
  );
2966
2990
  try {
2967
2991
  await access4(sibling);
2968
- return path12.resolve(sibling);
2992
+ return path13.resolve(sibling);
2969
2993
  } catch {
2970
2994
  }
2971
2995
  for (const candidate2 of bundledDashboardCandidates(
@@ -2974,7 +2998,7 @@ async function findDashboardBroadcastStart(cwd, env = process.env, options = {})
2974
2998
  )) {
2975
2999
  try {
2976
3000
  await access4(candidate2);
2977
- return path12.resolve(candidate2);
3001
+ return path13.resolve(candidate2);
2978
3002
  } catch {
2979
3003
  }
2980
3004
  }
@@ -2983,7 +3007,7 @@ async function findDashboardBroadcastStart(cwd, env = process.env, options = {})
2983
3007
  function runStartScript2(startPath, env) {
2984
3008
  return new Promise((resolve3, reject) => {
2985
3009
  const child = spawn3(process.execPath, [startPath], {
2986
- cwd: path12.dirname(path12.dirname(startPath)),
3010
+ cwd: path13.dirname(path13.dirname(startPath)),
2987
3011
  env,
2988
3012
  stdio: "inherit"
2989
3013
  });
@@ -3038,7 +3062,7 @@ import { defineCommand as defineCommand6 } from "citty";
3038
3062
 
3039
3063
  // src/lifecycle/diff.ts
3040
3064
  import { readFile as readFile8 } from "fs/promises";
3041
- import path13 from "path";
3065
+ import path14 from "path";
3042
3066
  async function readIfExists(abs) {
3043
3067
  try {
3044
3068
  return await readFile8(abs, "utf8");
@@ -3047,7 +3071,7 @@ async function readIfExists(abs) {
3047
3071
  }
3048
3072
  }
3049
3073
  async function comparePair(registryRoot, projectRoot, sourceRel, targetRel, protectedGlobs) {
3050
- const posixTarget = targetRel.split(path13.sep).join("/");
3074
+ const posixTarget = targetRel.split(path14.sep).join("/");
3051
3075
  if (isProtectedPath(posixTarget, protectedGlobs)) {
3052
3076
  return { path: posixTarget, status: "protected" };
3053
3077
  }
@@ -3077,7 +3101,7 @@ async function diffAgainstRegistry(registryRoot, projectRoot, manifest) {
3077
3101
  const entries = [];
3078
3102
  const seen = /* @__PURE__ */ new Set();
3079
3103
  const pushUnique = async (sourceRel, targetRel) => {
3080
- const key = targetRel.split(path13.sep).join("/");
3104
+ const key = targetRel.split(path14.sep).join("/");
3081
3105
  if (seen.has(key)) return;
3082
3106
  seen.add(key);
3083
3107
  entries.push(
@@ -3182,14 +3206,116 @@ var diffCommand = defineCommand6({
3182
3206
  });
3183
3207
 
3184
3208
  // src/commands/doctor.ts
3185
- import path26 from "path";
3209
+ import path28 from "path";
3186
3210
  import { defineCommand as defineCommand7 } from "citty";
3187
3211
 
3188
3212
  // src/invariants/hooks-health.ts
3189
3213
  import { execFile as execFile2 } from "child_process";
3190
- import { constants as constants2, access as access5, readFile as readFile9, readdir as readdir4, stat as stat2 } from "fs/promises";
3191
- import path14 from "path";
3214
+ import { constants as constants2, access as access5, readFile as readFile10, readdir as readdir4, stat as stat2 } from "fs/promises";
3215
+ import path16 from "path";
3192
3216
  import { promisify as promisify2 } from "util";
3217
+
3218
+ // src/generator/claude-session-start-hook.ts
3219
+ import { readFile as readFile9, writeFile as writeFile5 } from "fs/promises";
3220
+ import path15 from "path";
3221
+ var CLAUDE_SETTINGS_REL = ".claude/settings.json";
3222
+ var RESOLVE_AGENT_KIT_REL = ".cursor/hooks/agent/resolve-agent-kit.sh";
3223
+ var SESSION_START_HOOK_MARKER = "hook session-start --format claude";
3224
+ var SESSION_START_DEGRADED_TEXT = "Agent Kit hooks are running in degraded fail-open mode: the agent-kit CLI could not be resolved (checked AGENT_KIT_HOOK_BIN, PATH, node_modules/.bin/agent-kit). Slash command adapters still work; session-context injection is inactive. Fix: install the CLI (npm i -D @dadado/agent-kit-cli) or set AGENT_KIT_HOOK_BIN.";
3225
+ function buildSessionStartHookCommand() {
3226
+ return `. "\${CLAUDE_PROJECT_DIR}/${RESOLVE_AGENT_KIT_REL}" 2>/dev/null && resolve_agent_kit && exec $AGENT_KIT_RESOLVED hook session-start --format claude; printf '%s' ${shellSingleQuote(SESSION_START_DEGRADED_TEXT)}`;
3227
+ }
3228
+ function shellSingleQuote(text) {
3229
+ return `'${text.replace(/'/g, "'\\''")}'`;
3230
+ }
3231
+ function buildSessionStartHookEntry() {
3232
+ return {
3233
+ type: "command",
3234
+ command: buildSessionStartHookCommand(),
3235
+ timeout: 15,
3236
+ statusMessage: "Loading Agent Kit session context"
3237
+ };
3238
+ }
3239
+ function isPlainObject2(value) {
3240
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3241
+ }
3242
+ function groupHasMarker(group) {
3243
+ if (!isPlainObject2(group) || !Array.isArray(group.hooks)) return false;
3244
+ return group.hooks.some(
3245
+ (h) => isPlainObject2(h) && typeof h.command === "string" && h.command.includes(SESSION_START_HOOK_MARKER)
3246
+ );
3247
+ }
3248
+ function mergeSessionStartHookIntoSettings(existingRaw) {
3249
+ const entry = buildSessionStartHookEntry();
3250
+ const newGroup = { hooks: [entry] };
3251
+ let root = {};
3252
+ if (existingRaw !== null && existingRaw.trim() !== "") {
3253
+ try {
3254
+ const parsed = JSON.parse(existingRaw);
3255
+ if (!isPlainObject2(parsed)) throw new Error("root is not an object");
3256
+ root = parsed;
3257
+ } catch {
3258
+ return {
3259
+ content: null,
3260
+ status: "unavailable",
3261
+ instructions: instructionsBlock(entry)
3262
+ };
3263
+ }
3264
+ }
3265
+ const hooks = isPlainObject2(root.hooks) ? { ...root.hooks } : {};
3266
+ const sessionStart = Array.isArray(hooks.SessionStart) ? [...hooks.SessionStart] : [];
3267
+ const existingIndex = sessionStart.findIndex((g) => groupHasMarker(g));
3268
+ let status;
3269
+ if (existingIndex === -1) {
3270
+ sessionStart.push(newGroup);
3271
+ status = "applied";
3272
+ } else {
3273
+ const current = sessionStart[existingIndex];
3274
+ if (JSON.stringify(current) === JSON.stringify(newGroup)) {
3275
+ status = "unchanged";
3276
+ } else {
3277
+ sessionStart[existingIndex] = newGroup;
3278
+ status = "refreshed";
3279
+ }
3280
+ }
3281
+ hooks.SessionStart = sessionStart;
3282
+ root.hooks = hooks;
3283
+ return { content: `${JSON.stringify(root, null, 2)}
3284
+ `, status };
3285
+ }
3286
+ function instructionsBlock(entry) {
3287
+ return [
3288
+ `Could not parse the existing ${CLAUDE_SETTINGS_REL} as JSON, so Agent Kit did not touch it.`,
3289
+ "Add this hook by hand under hooks.SessionStart (create the arrays if they do not exist):",
3290
+ "",
3291
+ JSON.stringify(entry, null, 2)
3292
+ ].join("\n");
3293
+ }
3294
+ async function writeClaudeSessionStartHook(rootDir) {
3295
+ const abs = path15.join(rootDir, CLAUDE_SETTINGS_REL);
3296
+ let existing = null;
3297
+ try {
3298
+ existing = await readFile9(abs, "utf8");
3299
+ } catch {
3300
+ existing = null;
3301
+ }
3302
+ const merged = mergeSessionStartHookIntoSettings(existing);
3303
+ if (merged.status === "unavailable" || merged.content === null) {
3304
+ return {
3305
+ relativePath: CLAUDE_SETTINGS_REL,
3306
+ status: "unavailable",
3307
+ instructions: merged.instructions
3308
+ };
3309
+ }
3310
+ if (merged.status === "unchanged") {
3311
+ return { relativePath: CLAUDE_SETTINGS_REL, status: "unchanged" };
3312
+ }
3313
+ await ensureDir(path15.dirname(abs));
3314
+ await writeFile5(abs, merged.content, "utf8");
3315
+ return { relativePath: CLAUDE_SETTINGS_REL, status: merged.status };
3316
+ }
3317
+
3318
+ // src/invariants/hooks-health.ts
3193
3319
  var execFileAsync2 = promisify2(execFile2);
3194
3320
  var GIT_HOOK_CANONICAL_NAMES = ["pre-commit", "pre-push", "prepare-commit-msg"];
3195
3321
  var EXPECTED_EVENTS = [
@@ -3216,10 +3342,10 @@ async function isExecutable(p) {
3216
3342
  }
3217
3343
  }
3218
3344
  async function resolveAgentKitCli(rootDir) {
3219
- const root = path14.resolve(rootDir);
3345
+ const root = path16.resolve(rootDir);
3220
3346
  const candidates = [
3221
- path14.join(root, "node_modules", ".bin", "agent-kit"),
3222
- path14.join(root, "packages", "cli", "dist", "index.js")
3347
+ path16.join(root, "node_modules", ".bin", "agent-kit"),
3348
+ path16.join(root, "packages", "cli", "dist", "index.js")
3223
3349
  ];
3224
3350
  for (const c of candidates) {
3225
3351
  if (await exists(c)) return c;
@@ -3239,8 +3365,8 @@ function commandLooksLikeAdapter(command) {
3239
3365
  return m?.[1] ?? null;
3240
3366
  }
3241
3367
  async function assessGitHooksInstallDrift(rootDir) {
3242
- const root = path14.resolve(rootDir);
3243
- const canonicalDir = path14.join(root, "git-hooks");
3368
+ const root = path16.resolve(rootDir);
3369
+ const canonicalDir = path16.join(root, "git-hooks");
3244
3370
  if (!await exists(canonicalDir)) return [];
3245
3371
  let hooksPathCfg = "";
3246
3372
  try {
@@ -3253,12 +3379,12 @@ async function assessGitHooksInstallDrift(rootDir) {
3253
3379
  } catch {
3254
3380
  }
3255
3381
  if (hooksPathCfg) {
3256
- const resolved = path14.isAbsolute(hooksPathCfg) ? path14.normalize(hooksPathCfg) : path14.normalize(path14.join(root, hooksPathCfg));
3257
- if (resolved === path14.normalize(canonicalDir)) {
3382
+ const resolved = path16.isAbsolute(hooksPathCfg) ? path16.normalize(hooksPathCfg) : path16.normalize(path16.join(root, hooksPathCfg));
3383
+ if (resolved === path16.normalize(canonicalDir)) {
3258
3384
  return [];
3259
3385
  }
3260
3386
  }
3261
- const gitDir = path14.join(root, ".git");
3387
+ const gitDir = path16.join(root, ".git");
3262
3388
  if (!await exists(gitDir)) return [];
3263
3389
  try {
3264
3390
  const st = await stat2(gitDir);
@@ -3266,7 +3392,7 @@ async function assessGitHooksInstallDrift(rootDir) {
3266
3392
  } catch {
3267
3393
  return [];
3268
3394
  }
3269
- const installedDir = path14.join(gitDir, "hooks");
3395
+ const installedDir = path16.join(gitDir, "hooks");
3270
3396
  const advisories = [];
3271
3397
  const installHint = "Install or refresh with: `cp git-hooks/<name> .git/hooks/<name> && chmod +x .git/hooks/<name>` (see git-hooks/README.md)";
3272
3398
  let names = [...GIT_HOOK_CANONICAL_NAMES];
@@ -3279,8 +3405,8 @@ async function assessGitHooksInstallDrift(rootDir) {
3279
3405
  } catch {
3280
3406
  }
3281
3407
  for (const name of names) {
3282
- const canonical = path14.join(canonicalDir, name);
3283
- const installed = path14.join(installedDir, name);
3408
+ const canonical = path16.join(canonicalDir, name);
3409
+ const installed = path16.join(installedDir, name);
3284
3410
  if (!await exists(canonical)) continue;
3285
3411
  if (!await exists(installed)) {
3286
3412
  advisories.push(
@@ -3289,7 +3415,7 @@ async function assessGitHooksInstallDrift(rootDir) {
3289
3415
  continue;
3290
3416
  }
3291
3417
  try {
3292
- const [a, b] = await Promise.all([readFile9(canonical, "utf8"), readFile9(installed, "utf8")]);
3418
+ const [a, b] = await Promise.all([readFile10(canonical, "utf8"), readFile10(installed, "utf8")]);
3293
3419
  if (a !== b) {
3294
3420
  advisories.push(
3295
3421
  `git-hooks drift: \`.git/hooks/${name}\` differs from \`git-hooks/${name}\`. ${installHint}`
@@ -3304,9 +3430,9 @@ async function assessGitHooksInstallDrift(rootDir) {
3304
3430
  return advisories;
3305
3431
  }
3306
3432
  async function assessHooksHealth(rootDir) {
3307
- const root = path14.resolve(rootDir);
3433
+ const root = path16.resolve(rootDir);
3308
3434
  const hooksJsonPath = ".cursor/hooks.json";
3309
- const hooksJsonAbs = path14.join(root, hooksJsonPath);
3435
+ const hooksJsonAbs = path16.join(root, hooksJsonPath);
3310
3436
  const reasons = [];
3311
3437
  const wiredEvents = [];
3312
3438
  const advisories = await assessGitHooksInstallDrift(root);
@@ -3322,7 +3448,7 @@ async function assessHooksHealth(rootDir) {
3322
3448
  }
3323
3449
  let parsed;
3324
3450
  try {
3325
- parsed = JSON.parse(await readFile9(hooksJsonAbs, "utf8"));
3451
+ parsed = JSON.parse(await readFile10(hooksJsonAbs, "utf8"));
3326
3452
  } catch {
3327
3453
  return {
3328
3454
  status: "degraded",
@@ -3352,14 +3478,14 @@ async function assessHooksHealth(rootDir) {
3352
3478
  reasons.push(`missing hook event: ${event}`);
3353
3479
  }
3354
3480
  }
3355
- const resolveLib = path14.join(root, ".cursor", "hooks", "agent", "resolve-agent-kit.sh");
3481
+ const resolveLib = path16.join(root, RESOLVE_AGENT_KIT_REL);
3356
3482
  if (!await exists(resolveLib)) {
3357
- reasons.push("missing `.cursor/hooks/agent/resolve-agent-kit.sh` (thin adapter resolver)");
3483
+ reasons.push(`missing \`${RESOLVE_AGENT_KIT_REL}\` (thin adapter resolver)`);
3358
3484
  } else if (!await isExecutable(resolveLib)) {
3359
- reasons.push("`.cursor/hooks/agent/resolve-agent-kit.sh` is not executable (chmod +x)");
3485
+ reasons.push(`\`${RESOLVE_AGENT_KIT_REL}\` is not executable (chmod +x)`);
3360
3486
  }
3361
3487
  for (const rel of adapterRels) {
3362
- const abs = path14.join(root, rel);
3488
+ const abs = path16.join(root, rel);
3363
3489
  if (!await exists(abs)) {
3364
3490
  reasons.push(`missing adapter script: \`${rel}\``);
3365
3491
  continue;
@@ -3400,7 +3526,7 @@ async function assessHooksHealth(rootDir) {
3400
3526
 
3401
3527
  // src/lifecycle/check-updates.ts
3402
3528
  import { execFile as execFile3 } from "child_process";
3403
- import path15 from "path";
3529
+ import path17 from "path";
3404
3530
  import { promisify as promisify3 } from "util";
3405
3531
  var execFileAsync3 = promisify3(execFile3);
3406
3532
  var SEMVER_CORE = /^v?(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/i;
@@ -3500,31 +3626,19 @@ function readUpdateCheckPrefs(config) {
3500
3626
  lastCheckedAt: typeof uc.lastCheckedAt === "string" ? uc.lastCheckedAt : null
3501
3627
  };
3502
3628
  }
3503
- function intervalElapsed2(lastCheckedAt, intervalDays) {
3504
- if (!lastCheckedAt) return true;
3505
- const last = Date.parse(lastCheckedAt);
3506
- if (Number.isNaN(last)) return true;
3507
- const ms = intervalDays * 24 * 60 * 60 * 1e3;
3508
- return Date.now() - last >= ms;
3509
- }
3510
- async function loadContextConfig2(cwd) {
3511
- const configPath = path15.join(cwd, ".cursor", "context", "config.json");
3512
- return readJson(configPath);
3513
- }
3514
3629
  async function stampLastCheckedAt(cwd) {
3515
- const configPath = path15.join(cwd, ".cursor", "context", "config.json");
3516
- const existing = await loadContextConfig2(cwd) ?? {};
3630
+ const existing = await loadContextConfig(cwd) ?? {};
3517
3631
  const prev = existing.updateCheck && typeof existing.updateCheck === "object" ? { ...existing.updateCheck } : {};
3518
3632
  existing.updateCheck = {
3519
3633
  ...DEFAULT_UPDATE_CHECK,
3520
3634
  ...prev,
3521
3635
  lastCheckedAt: (/* @__PURE__ */ new Date()).toISOString()
3522
3636
  };
3523
- await writeJson(configPath, existing);
3637
+ await writeJson(contextConfigPath(cwd), existing);
3524
3638
  }
3525
3639
  async function readLocalKitVersion(registryRoot) {
3526
3640
  for (const rel of ["packages/cli/package.json", "package.json"]) {
3527
- const data = await readJson(path15.join(registryRoot, rel));
3641
+ const data = await readJson(path17.join(registryRoot, rel));
3528
3642
  if (data && typeof data.version === "string" && data.version.length > 0) {
3529
3643
  return data.version;
3530
3644
  }
@@ -3545,7 +3659,7 @@ async function checkAgainstLocalRegistry(cwd, manifest, options) {
3545
3659
  status: "error",
3546
3660
  installedVersion: manifest.version,
3547
3661
  latestVersion: null,
3548
- registryUrl: path15.resolve(registryPath),
3662
+ registryUrl: path17.resolve(registryPath),
3549
3663
  registryRef: "local",
3550
3664
  applyRecommended: false,
3551
3665
  message: `Failed to resolve --registry: ${msg}`
@@ -3663,7 +3777,7 @@ async function checkForUpdates(cwd, options = {}) {
3663
3777
  };
3664
3778
  }
3665
3779
  if (options.respectPrefs) {
3666
- const config = await loadContextConfig2(cwd);
3780
+ const config = await loadContextConfig(cwd);
3667
3781
  const prefs = readUpdateCheckPrefs(config);
3668
3782
  if (!prefs.enabled) {
3669
3783
  return {
@@ -3676,7 +3790,7 @@ async function checkForUpdates(cwd, options = {}) {
3676
3790
  message: "updateCheck.enabled is false (opt-in). Set true in .cursor/context/config.json to nudge."
3677
3791
  };
3678
3792
  }
3679
- if (!intervalElapsed2(prefs.lastCheckedAt, prefs.intervalDays)) {
3793
+ if (!intervalElapsed(prefs.lastCheckedAt, prefs.intervalDays)) {
3680
3794
  return {
3681
3795
  status: "skipped-interval",
3682
3796
  installedVersion: manifest.version,
@@ -3773,7 +3887,7 @@ function runningCliBehindMessage(running, latest) {
3773
3887
  async function checkRunningCliVsNpmLatest(cwd, options = {}) {
3774
3888
  const running = normalizeSemver(KIT_VERSION) ?? KIT_VERSION;
3775
3889
  if (options.respectPrefs) {
3776
- const config = await loadContextConfig2(cwd);
3890
+ const config = await loadContextConfig(cwd);
3777
3891
  const prefs = readUpdateCheckPrefs(config);
3778
3892
  if (!prefs.enabled) {
3779
3893
  return {
@@ -3783,7 +3897,7 @@ async function checkRunningCliVsNpmLatest(cwd, options = {}) {
3783
3897
  message: "updateCheck.enabled is false (opt-in). Set true in .cursor/context/config.json to nudge."
3784
3898
  };
3785
3899
  }
3786
- if (!intervalElapsed2(prefs.lastCheckedAt, prefs.intervalDays)) {
3900
+ if (!intervalElapsed(prefs.lastCheckedAt, prefs.intervalDays)) {
3787
3901
  return {
3788
3902
  status: "skipped-interval",
3789
3903
  runningVersion: running,
@@ -3882,11 +3996,11 @@ async function warnIfRunningCliBehindNpm(cwd, options = {}) {
3882
3996
  }
3883
3997
 
3884
3998
  // src/readiness/env-checks.ts
3885
- import { constants as constants3, access as access6, readFile as readFile10, stat as stat3 } from "fs/promises";
3999
+ import { constants as constants3, access as access6, readFile as readFile11, stat as stat3 } from "fs/promises";
3886
4000
  import { homedir as homedir2 } from "os";
3887
- import path16 from "path";
4001
+ import path18 from "path";
3888
4002
  var MIN_NODE_MAJOR = 20;
3889
- var CLI_PACKAGE_JSON_REL = path16.join(
4003
+ var CLI_PACKAGE_JSON_REL = path18.join(
3890
4004
  "lib",
3891
4005
  "node_modules",
3892
4006
  "@dadado",
@@ -3907,7 +4021,7 @@ function expandBinDirVars(raw, binDir) {
3907
4021
  return raw.replace(/\$\{basedir\}/g, binDir).replace(/\$basedir/g, binDir);
3908
4022
  }
3909
4023
  function packageJsonFromDistIndex(distIndex) {
3910
- return path16.join(path16.dirname(distIndex), "..", "package.json");
4024
+ return path18.join(path18.dirname(distIndex), "..", "package.json");
3911
4025
  }
3912
4026
  function parsePackageVersion(contents) {
3913
4027
  try {
@@ -3918,23 +4032,23 @@ function parsePackageVersion(contents) {
3918
4032
  }
3919
4033
  }
3920
4034
  async function readBinVersion(binPath, options = {}) {
3921
- const readFileImpl = options.readFileImpl ?? ((filePath) => readFile10(filePath, "utf8"));
3922
- const binDir = path16.dirname(binPath);
4035
+ const readFileImpl = options.readFileImpl ?? ((filePath) => readFile11(filePath, "utf8"));
4036
+ const binDir = path18.dirname(binPath);
3923
4037
  const candidates = [
3924
- path16.join(binDir, "..", CLI_PACKAGE_JSON_REL),
3925
- path16.join(binDir, "..", "node_modules", "@dadado", "agent-kit-cli", "package.json")
4038
+ path18.join(binDir, "..", CLI_PACKAGE_JSON_REL),
4039
+ path18.join(binDir, "..", "node_modules", "@dadado", "agent-kit-cli", "package.json")
3926
4040
  ];
3927
4041
  try {
3928
4042
  const contents = await readFileImpl(binPath);
3929
4043
  for (const raw of extractDistIndexFromBinScript(contents)) {
3930
- const distIndex = path16.resolve(binDir, expandBinDirVars(raw, binDir));
4044
+ const distIndex = path18.resolve(binDir, expandBinDirVars(raw, binDir));
3931
4045
  candidates.push(packageJsonFromDistIndex(distIndex));
3932
4046
  }
3933
4047
  } catch {
3934
4048
  }
3935
4049
  const seen = /* @__PURE__ */ new Set();
3936
4050
  for (const candidate2 of candidates) {
3937
- const resolved = path16.resolve(candidate2);
4051
+ const resolved = path18.resolve(candidate2);
3938
4052
  if (seen.has(resolved)) continue;
3939
4053
  seen.add(resolved);
3940
4054
  try {
@@ -3948,11 +4062,11 @@ async function readBinVersion(binPath, options = {}) {
3948
4062
  async function resolveBinOnPath(binName, env, platform) {
3949
4063
  const pathVar = env.PATH ?? env.Path ?? "";
3950
4064
  if (!pathVar) return null;
3951
- const dirs = pathVar.split(path16.delimiter).filter(Boolean);
4065
+ const dirs = pathVar.split(path18.delimiter).filter(Boolean);
3952
4066
  const candidates = platform === "win32" ? [binName, `${binName}.cmd`, `${binName}.exe`, `${binName}.bat`] : [binName];
3953
4067
  for (const dir of dirs) {
3954
4068
  for (const candidate2 of candidates) {
3955
- const abs = path16.join(dir, candidate2);
4069
+ const abs = path18.join(dir, candidate2);
3956
4070
  try {
3957
4071
  await access6(abs, platform === "win32" ? void 0 : constants3.X_OK);
3958
4072
  return abs;
@@ -3976,13 +4090,13 @@ function detectShellName(env, platform) {
3976
4090
  }
3977
4091
  const shellPath = env.SHELL;
3978
4092
  if (!shellPath) return null;
3979
- const base = path16.basename(shellPath).trim();
4093
+ const base = path18.basename(shellPath).trim();
3980
4094
  return base || null;
3981
4095
  }
3982
4096
  function detectShellProfile(env, platform, homeDir) {
3983
4097
  const shellName = detectShellName(env, platform);
3984
- if (shellName === "zsh") return path16.join(homeDir, ".zshrc");
3985
- if (shellName === "bash") return path16.join(homeDir, ".bashrc");
4098
+ if (shellName === "zsh") return path18.join(homeDir, ".zshrc");
4099
+ if (shellName === "bash") return path18.join(homeDir, ".bashrc");
3986
4100
  return null;
3987
4101
  }
3988
4102
  function parseNpmrcPrefix(content, homeDir) {
@@ -3991,12 +4105,12 @@ function parseNpmrcPrefix(content, homeDir) {
3991
4105
  if (!captured) return null;
3992
4106
  let value = captured.trim().replace(/^["']|["']$/g, "");
3993
4107
  if (value.startsWith("~")) {
3994
- value = path16.join(homeDir, value.slice(1));
4108
+ value = path18.join(homeDir, value.slice(1));
3995
4109
  }
3996
4110
  return value || null;
3997
4111
  }
3998
4112
  function heuristicPrefixFromExecPath(execPath, platform) {
3999
- const p = platform === "win32" ? path16.win32 : path16.posix;
4113
+ const p = platform === "win32" ? path18.win32 : path18.posix;
4000
4114
  return platform === "win32" ? p.dirname(execPath) : p.dirname(p.dirname(execPath));
4001
4115
  }
4002
4116
  async function detectNpmPrefix(options = {}) {
@@ -4004,12 +4118,12 @@ async function detectNpmPrefix(options = {}) {
4004
4118
  const platform = options.platform ?? process.platform;
4005
4119
  const homeDir = options.homeDir ?? homedir2();
4006
4120
  const execPath = options.execPath ?? process.execPath;
4007
- const readFileImpl = options.readFileImpl ?? ((filePath) => readFile10(filePath, "utf8"));
4121
+ const readFileImpl = options.readFileImpl ?? ((filePath) => readFile11(filePath, "utf8"));
4008
4122
  const envPrefix = env.npm_config_prefix ?? env.NPM_CONFIG_PREFIX;
4009
4123
  if (envPrefix?.trim()) {
4010
4124
  return { prefix: envPrefix.trim(), source: "env" };
4011
4125
  }
4012
- const userconfigPath = env.NPM_CONFIG_USERCONFIG ?? path16.join(homeDir, ".npmrc");
4126
+ const userconfigPath = env.NPM_CONFIG_USERCONFIG ?? path18.join(homeDir, ".npmrc");
4013
4127
  try {
4014
4128
  const content = await readFileImpl(userconfigPath);
4015
4129
  const npmrcPrefix = parseNpmrcPrefix(content, homeDir);
@@ -4336,12 +4450,17 @@ function createReadinessReport(scan, options) {
4336
4450
  }
4337
4451
 
4338
4452
  // src/scanner/safe-fixes.ts
4339
- import { readFile as readFile15, writeFile as writeFile5 } from "fs/promises";
4340
- import path24 from "path";
4453
+ import { readFile as readFile16, writeFile as writeFile6 } from "fs/promises";
4454
+ import path26 from "path";
4455
+
4456
+ // src/scanner/detect-repository.ts
4457
+ import { readFile as readFile12 } from "fs/promises";
4458
+ import path19 from "path";
4459
+
4460
+ // src/scanner/paths.ts
4461
+ var REPOSITORY_PROFILE_REL = ".cursor/agent-kit.config.json";
4341
4462
 
4342
4463
  // src/scanner/detect-repository.ts
4343
- import { readFile as readFile11 } from "fs/promises";
4344
- import path17 from "path";
4345
4464
  var CONTEXT_PATHS = [
4346
4465
  ["README.md", "README"],
4347
4466
  ["README", "README"],
@@ -4360,7 +4479,7 @@ var CONTEXT_PATHS = [
4360
4479
  async function existingEvidence(rootDir, candidates) {
4361
4480
  const evidence = await Promise.all(
4362
4481
  candidates.map(
4363
- async ([relativePath, label]) => await fileExists(path17.join(rootDir, relativePath)) ? { source: "file", value: `${relativePath}:${label}` } : void 0
4482
+ async ([relativePath, label]) => await fileExists(path19.join(rootDir, relativePath)) ? { source: "file", value: `${relativePath}:${label}` } : void 0
4364
4483
  )
4365
4484
  );
4366
4485
  return evidence.flatMap((item) => item ? [item] : []);
@@ -4394,7 +4513,7 @@ function isRepositoryPurpose(value) {
4394
4513
  }
4395
4514
  async function readConfirmedPurpose(rootDir) {
4396
4515
  const configuration = await readJson(
4397
- path17.join(rootDir, ".cursor", "agent-kit.config.json")
4516
+ path19.join(rootDir, REPOSITORY_PROFILE_REL)
4398
4517
  );
4399
4518
  const configuredPurpose = configuration?.purpose;
4400
4519
  if (!configuredPurpose || !isRepositoryPurpose(configuredPurpose.value) || configuredPurpose.value === "unknown") {
@@ -4406,7 +4525,7 @@ async function readConfirmedPurpose(rootDir) {
4406
4525
  value: configuredPurpose.value,
4407
4526
  categories: categories.length > 0 ? categories : [configuredPurpose.value],
4408
4527
  confidence: "high",
4409
- evidence: evidence.length > 0 ? evidence : [{ source: "configuration", value: ".cursor/agent-kit.config.json#purpose.value" }]
4528
+ evidence: evidence.length > 0 ? evidence : [{ source: "configuration", value: `${REPOSITORY_PROFILE_REL}#purpose.value` }]
4410
4529
  };
4411
4530
  }
4412
4531
  async function detectPurpose(rootDir, stack) {
@@ -4414,7 +4533,7 @@ async function detectPurpose(rootDir, stack) {
4414
4533
  if (confirmed) return confirmed;
4415
4534
  const entries = await listDirectory(rootDir);
4416
4535
  const lowerEntries = entries.map((entry) => entry.toLowerCase());
4417
- const packageJson = await readJson(path17.join(rootDir, "package.json"));
4536
+ const packageJson = await readJson(path19.join(rootDir, "package.json"));
4418
4537
  const categories = [];
4419
4538
  const evidence = [];
4420
4539
  const add = (category, value2) => {
@@ -4454,16 +4573,16 @@ async function detectPurpose(rootDir, stack) {
4454
4573
  }
4455
4574
  async function detectAgentKit(rootDir) {
4456
4575
  const manifestRelativePath = ".cursor/agent-kit.json";
4457
- const manifestPath = path17.join(rootDir, manifestRelativePath);
4576
+ const manifestPath = path19.join(rootDir, manifestRelativePath);
4458
4577
  const installed = await fileExists(manifestPath);
4459
4578
  const manifest = installed ? await readJson(manifestPath) : null;
4460
4579
  return {
4461
4580
  installed,
4462
4581
  manifestPath: installed ? manifestRelativePath : void 0,
4463
4582
  version: manifest?.version,
4464
- hasPlans: await fileExists(path17.join(rootDir, ".cursor/plans")),
4465
- hasHandoff: await fileExists(path17.join(rootDir, ".cursor/HANDOFF.md")),
4466
- hasMemory: await fileExists(path17.join(rootDir, ".cursor/memory"))
4583
+ hasPlans: await fileExists(path19.join(rootDir, ".cursor/plans")),
4584
+ hasHandoff: await fileExists(path19.join(rootDir, ".cursor/HANDOFF.md")),
4585
+ hasMemory: await fileExists(path19.join(rootDir, ".cursor/memory"))
4467
4586
  };
4468
4587
  }
4469
4588
  var REQUIRED_SECRET_PATTERNS = [
@@ -4484,9 +4603,9 @@ var KIT_OWNED_IGNORE_PATTERNS = [
4484
4603
  ".cursor/context/mission-timing.json"
4485
4604
  ];
4486
4605
  async function detectSafety(rootDir, trackedFiles) {
4487
- const gitignorePath = path17.join(rootDir, ".gitignore");
4606
+ const gitignorePath = path19.join(rootDir, ".gitignore");
4488
4607
  const hasGitignore = await fileExists(gitignorePath);
4489
- const gitignore = hasGitignore ? await readFile11(gitignorePath, "utf8") : "";
4608
+ const gitignore = hasGitignore ? await readFile12(gitignorePath, "utf8") : "";
4490
4609
  const lines = gitignore.split(/\r?\n/).map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
4491
4610
  const ignoredSecretPatterns = REQUIRED_SECRET_PATTERNS.filter(
4492
4611
  (pattern) => lines.includes(pattern)
@@ -4495,7 +4614,7 @@ async function detectSafety(rootDir, trackedFiles) {
4495
4614
  (file) => /(^|\/)(\.env(\..+)?|.*\.(key|pem|p12|pfx)|.*credentials.*\.json)$/i.test(file)
4496
4615
  );
4497
4616
  const hookPaths = [".husky", ".git/hooks/pre-commit", "git-hooks/pre-commit"];
4498
- const hasHooks = (await Promise.all(hookPaths.map((item) => fileExists(path17.join(rootDir, item))))).some(Boolean);
4617
+ const hasHooks = (await Promise.all(hookPaths.map((item) => fileExists(path19.join(rootDir, item))))).some(Boolean);
4499
4618
  const guardCandidates = [
4500
4619
  ".husky/pre-commit",
4501
4620
  ".husky/pre-push",
@@ -4504,7 +4623,7 @@ async function detectSafety(rootDir, trackedFiles) {
4504
4623
  ];
4505
4624
  const guardContents = await Promise.all(
4506
4625
  guardCandidates.map(
4507
- async (item) => await fileExists(path17.join(rootDir, item)) ? readFile11(path17.join(rootDir, item), "utf8") : ""
4626
+ async (item) => await fileExists(path19.join(rootDir, item)) ? readFile12(path19.join(rootDir, item), "utf8") : ""
4508
4627
  )
4509
4628
  );
4510
4629
  return {
@@ -4522,11 +4641,11 @@ async function detectSafety(rootDir, trackedFiles) {
4522
4641
  }
4523
4642
 
4524
4643
  // src/scanner/scan.ts
4525
- import path23 from "path";
4644
+ import path25 from "path";
4526
4645
 
4527
4646
  // src/scanner/detect-git.ts
4528
4647
  import { execFile as execFile4 } from "child_process";
4529
- import path18 from "path";
4648
+ import path20 from "path";
4530
4649
  import { promisify as promisify4 } from "util";
4531
4650
  var exec = promisify4(execFile4);
4532
4651
  function remoteHostname(remoteUrl) {
@@ -4550,7 +4669,7 @@ function sanitizeRemoteUrl(remoteUrl) {
4550
4669
  }
4551
4670
  async function detectProvider(rootDir, remoteUrl) {
4552
4671
  const configuration = await readJson(
4553
- path18.join(rootDir, ".cursor", "agent-kit.config.json")
4672
+ path20.join(rootDir, REPOSITORY_PROFILE_REL)
4554
4673
  );
4555
4674
  const configuredProvider = configuration?.git?.provider;
4556
4675
  if (configuredProvider) {
@@ -4559,7 +4678,7 @@ async function detectProvider(rootDir, remoteUrl) {
4559
4678
  provider: configuredProvider,
4560
4679
  providerKind: configuredKind,
4561
4680
  confidence: "high",
4562
- evidence: [{ source: "configuration", value: ".cursor/agent-kit.config.json#git.provider" }]
4681
+ evidence: [{ source: "configuration", value: `${REPOSITORY_PROFILE_REL}#git.provider` }]
4563
4682
  };
4564
4683
  }
4565
4684
  if (!remoteUrl) {
@@ -4607,7 +4726,7 @@ async function detectProvider(rootDir, remoteUrl) {
4607
4726
  evidence: remoteEvidence
4608
4727
  };
4609
4728
  }
4610
- if (await fileExists(path18.join(rootDir, ".gitlab-ci.yml"))) {
4729
+ if (await fileExists(path20.join(rootDir, ".gitlab-ci.yml"))) {
4611
4730
  return {
4612
4731
  provider: "gitlab",
4613
4732
  providerKind: "gitlab-self-hosted",
@@ -4697,11 +4816,11 @@ async function detectGit(rootDir) {
4697
4816
  }
4698
4817
 
4699
4818
  // src/scanner/detect-ide.ts
4700
- import path19 from "path";
4819
+ import path21 from "path";
4701
4820
  async function detectIde(rootDir) {
4702
- const hasCursor = await fileExists(path19.join(rootDir, ".cursor"));
4703
- const hasVSCode = await fileExists(path19.join(rootDir, ".vscode"));
4704
- const hasWindsurf = await fileExists(path19.join(rootDir, ".windsurfrules"));
4821
+ const hasCursor = await fileExists(path21.join(rootDir, ".cursor"));
4822
+ const hasVSCode = await fileExists(path21.join(rootDir, ".vscode"));
4823
+ const hasWindsurf = await fileExists(path21.join(rootDir, ".windsurfrules"));
4705
4824
  if (hasCursor) return { ide: "cursor", plan: "cursor-pro" };
4706
4825
  if (hasVSCode) return { ide: "vscode", plan: "vscode-pro" };
4707
4826
  if (hasWindsurf) return { ide: "windsurf", plan: "windsurf" };
@@ -4709,8 +4828,8 @@ async function detectIde(rootDir) {
4709
4828
  }
4710
4829
 
4711
4830
  // src/scanner/detect-infra.ts
4712
- import { readFile as readFile12, readdir as readdir5 } from "fs/promises";
4713
- import path20 from "path";
4831
+ import { readFile as readFile13, readdir as readdir5 } from "fs/promises";
4832
+ import path22 from "path";
4714
4833
 
4715
4834
  // src/types.ts
4716
4835
  var GIT_PLATFORM_META = {
@@ -4790,7 +4909,7 @@ function extractRunCommands(yaml) {
4790
4909
  return Array.from(new Set(commands));
4791
4910
  }
4792
4911
  async function detectCiRunCommands(rootDir) {
4793
- const workflowsDir = path20.join(rootDir, ".github", "workflows");
4912
+ const workflowsDir = path22.join(rootDir, ".github", "workflows");
4794
4913
  if (!await fileExists(workflowsDir)) return [];
4795
4914
  let entries;
4796
4915
  try {
@@ -4802,7 +4921,7 @@ async function detectCiRunCommands(rootDir) {
4802
4921
  const perFile = await Promise.all(
4803
4922
  workflowFiles.map(async (name) => {
4804
4923
  try {
4805
- return extractRunCommands(await readFile12(path20.join(workflowsDir, name), "utf8"));
4924
+ return extractRunCommands(await readFile13(path22.join(workflowsDir, name), "utf8"));
4806
4925
  } catch {
4807
4926
  return [];
4808
4927
  }
@@ -4811,12 +4930,12 @@ async function detectCiRunCommands(rootDir) {
4811
4930
  return Array.from(new Set(perFile.flat()));
4812
4931
  }
4813
4932
  async function detectInfra(rootDir) {
4814
- const docker = await fileExists(path20.join(rootDir, "Dockerfile")) || await fileExists(path20.join(rootDir, "docker-compose.yml")) || await fileExists(path20.join(rootDir, "docker-compose.yaml"));
4815
- const kubernetes = await fileExists(path20.join(rootDir, "k8s")) || await fileExists(path20.join(rootDir, "kubernetes"));
4933
+ const docker = await fileExists(path22.join(rootDir, "Dockerfile")) || await fileExists(path22.join(rootDir, "docker-compose.yml")) || await fileExists(path22.join(rootDir, "docker-compose.yaml"));
4934
+ const kubernetes = await fileExists(path22.join(rootDir, "k8s")) || await fileExists(path22.join(rootDir, "kubernetes"));
4816
4935
  let ci = "none";
4817
4936
  const ciFiles = [];
4818
4937
  for (const [platform, filePath] of Object.entries(CI_PLATFORM_FILES)) {
4819
- if (await fileExists(path20.join(rootDir, filePath))) {
4938
+ if (await fileExists(path22.join(rootDir, filePath))) {
4820
4939
  if (ci === "none") ci = platform;
4821
4940
  ciFiles.push(filePath);
4822
4941
  }
@@ -4841,12 +4960,12 @@ async function detectInfra(rootDir) {
4841
4960
  ];
4842
4961
  const infrastructureFiles = (await Promise.all(
4843
4962
  infrastructureCandidates.map(
4844
- async (file) => await fileExists(path20.join(rootDir, file)) ? file : void 0
4963
+ async (file) => await fileExists(path22.join(rootDir, file)) ? file : void 0
4845
4964
  )
4846
4965
  )).filter((file) => file !== void 0);
4847
4966
  const deploymentFiles = (await Promise.all(
4848
4967
  deploymentCandidates.map(
4849
- async (file) => await fileExists(path20.join(rootDir, file)) ? file : void 0
4968
+ async (file) => await fileExists(path22.join(rootDir, file)) ? file : void 0
4850
4969
  )
4851
4970
  )).filter((file) => file !== void 0);
4852
4971
  const ciRunCommands = ci === "github-actions" ? await detectCiRunCommands(rootDir) : [];
@@ -4854,18 +4973,18 @@ async function detectInfra(rootDir) {
4854
4973
  }
4855
4974
 
4856
4975
  // src/scanner/detect-services.ts
4857
- import { readFile as readFile13 } from "fs/promises";
4858
- import path21 from "path";
4976
+ import { readFile as readFile14 } from "fs/promises";
4977
+ import path23 from "path";
4859
4978
  async function detectProjectManagement(rootDir) {
4860
4979
  const tools = [];
4861
4980
  const mcpConfigPaths = [
4862
- path21.join(rootDir, ".cursor", "mcp.json"),
4863
- path21.join(rootDir, "mcp.json")
4981
+ path23.join(rootDir, ".cursor", "mcp.json"),
4982
+ path23.join(rootDir, "mcp.json")
4864
4983
  ];
4865
4984
  for (const configPath of mcpConfigPaths) {
4866
4985
  if (!await fileExists(configPath)) continue;
4867
4986
  try {
4868
- const raw = await readFile13(configPath, "utf8");
4987
+ const raw = await readFile14(configPath, "utf8");
4869
4988
  const lower = raw.toLowerCase();
4870
4989
  if (lower.includes("clickup")) tools.push("clickup");
4871
4990
  if (lower.includes("jira") || lower.includes("atlassian")) tools.push("jira");
@@ -4876,20 +4995,20 @@ async function detectProjectManagement(rootDir) {
4876
4995
  } catch {
4877
4996
  }
4878
4997
  }
4879
- if (await fileExists(path21.join(rootDir, ".github", "ISSUE_TEMPLATE"))) {
4998
+ if (await fileExists(path23.join(rootDir, ".github", "ISSUE_TEMPLATE"))) {
4880
4999
  tools.push("github-issues");
4881
5000
  }
4882
- if (await fileExists(path21.join(rootDir, ".github", "projects"))) {
5001
+ if (await fileExists(path23.join(rootDir, ".github", "projects"))) {
4883
5002
  tools.push("github-projects");
4884
5003
  }
4885
5004
  return [...new Set(tools)];
4886
5005
  }
4887
5006
  async function detectServices(rootDir) {
4888
- const hasPrisma = await fileExists(path21.join(rootDir, "prisma/schema.prisma"));
4889
- const hasSequelize = await fileExists(path21.join(rootDir, "sequelize"));
4890
- const hasDrizzle = await fileExists(path21.join(rootDir, "drizzle.config.ts"));
4891
- const hasKnex = await fileExists(path21.join(rootDir, "knexfile.ts"));
4892
- const hasTypeorm = await fileExists(path21.join(rootDir, "ormconfig.json"));
5007
+ const hasPrisma = await fileExists(path23.join(rootDir, "prisma/schema.prisma"));
5008
+ const hasSequelize = await fileExists(path23.join(rootDir, "sequelize"));
5009
+ const hasDrizzle = await fileExists(path23.join(rootDir, "drizzle.config.ts"));
5010
+ const hasKnex = await fileExists(path23.join(rootDir, "knexfile.ts"));
5011
+ const hasTypeorm = await fileExists(path23.join(rootDir, "ormconfig.json"));
4893
5012
  const database = hasPrisma || hasSequelize || hasDrizzle || hasKnex || hasTypeorm ? "postgresql" : void 0;
4894
5013
  const orm = hasPrisma ? "prisma" : hasDrizzle ? "drizzle" : hasSequelize ? "sequelize" : hasKnex ? "knex" : hasTypeorm ? "typeorm" : void 0;
4895
5014
  const projectManagement = await detectProjectManagement(rootDir);
@@ -4901,8 +5020,8 @@ async function detectServices(rootDir) {
4901
5020
  }
4902
5021
 
4903
5022
  // src/scanner/detect-stack.ts
4904
- import { readFile as readFile14, readdir as readdir6 } from "fs/promises";
4905
- import path22 from "path";
5023
+ import { readFile as readFile15, readdir as readdir6 } from "fs/promises";
5024
+ import path24 from "path";
4906
5025
  var PROJECT_MARKERS = [
4907
5026
  "package.json",
4908
5027
  "requirements.txt",
@@ -4928,7 +5047,7 @@ async function scanSubdirectoryMarkers(rootDir) {
4928
5047
  const candidates = dirs.flatMap((dir) => PROJECT_MARKERS.map((marker) => ({ dir, marker })));
4929
5048
  const found = await Promise.all(
4930
5049
  candidates.map(async (candidate2) => {
4931
- const exists2 = await fileExists(path22.join(rootDir, candidate2.dir, candidate2.marker));
5050
+ const exists2 = await fileExists(path24.join(rootDir, candidate2.dir, candidate2.marker));
4932
5051
  return exists2 ? candidate2 : null;
4933
5052
  })
4934
5053
  );
@@ -4944,7 +5063,7 @@ var DART_VALIDATION_COMMANDS = [
4944
5063
  async function detectDartFlavor(pubspecDir) {
4945
5064
  let content = "";
4946
5065
  try {
4947
- content = await readFile14(path22.join(pubspecDir, "pubspec.yaml"), "utf8");
5066
+ content = await readFile15(path24.join(pubspecDir, "pubspec.yaml"), "utf8");
4948
5067
  } catch {
4949
5068
  }
4950
5069
  return { isFlutter: isFlutterPubspec(content) };
@@ -4957,7 +5076,7 @@ async function detectStackFromSubdirHits(rootDir, hits) {
4957
5076
  const dartHits = hits.filter((hit) => hit.marker === "pubspec.yaml");
4958
5077
  if (dartHits.length > 0) {
4959
5078
  const flavors = await Promise.all(
4960
- dartHits.map((hit) => detectDartFlavor(path22.join(rootDir, hit.dir)))
5079
+ dartHits.map((hit) => detectDartFlavor(path24.join(rootDir, hit.dir)))
4961
5080
  );
4962
5081
  const testCommands = Array.from(
4963
5082
  new Set(flavors.map((flavor) => flavor.isFlutter ? "flutter test" : "dart test"))
@@ -4974,8 +5093,8 @@ async function detectStackFromSubdirHits(rootDir, hits) {
4974
5093
  }
4975
5094
  const nodeHit = hits.find((hit) => hit.marker === "package.json");
4976
5095
  if (nodeHit) {
4977
- const subRoot = path22.join(rootDir, nodeHit.dir);
4978
- const packageJson = await readJson(path22.join(subRoot, "package.json")) ?? {};
5096
+ const subRoot = path24.join(rootDir, nodeHit.dir);
5097
+ const packageJson = await readJson(path24.join(subRoot, "package.json")) ?? {};
4979
5098
  const scripts = packageJson.scripts ?? {};
4980
5099
  const packageManager = await detectPackageManager(subRoot, packageJson);
4981
5100
  const commands = commandsForScripts(scripts, packageManager.packageManager);
@@ -5032,7 +5151,7 @@ function isDetectionEvidenceArray(value) {
5032
5151
  }
5033
5152
  async function readConfirmedStack(rootDir) {
5034
5153
  const configuration = await readJson(
5035
- path22.join(rootDir, ".cursor", "agent-kit.config.json")
5154
+ path24.join(rootDir, REPOSITORY_PROFILE_REL)
5036
5155
  );
5037
5156
  const configured = configuration?.stack;
5038
5157
  const language = configured?.language;
@@ -5046,7 +5165,7 @@ async function readConfirmedStack(rootDir) {
5046
5165
  const scripts = configured.scripts && typeof configured.scripts === "object" && !Array.isArray(configured.scripts) ? configured.scripts : void 0;
5047
5166
  const packageManagerEvidence = isDetectionEvidenceArray(
5048
5167
  configured.packageManagerEvidence
5049
- ) ? configured.packageManagerEvidence : [{ source: "configuration", value: ".cursor/agent-kit.config.json#stack.language" }];
5168
+ ) ? configured.packageManagerEvidence : [{ source: "configuration", value: `${REPOSITORY_PROFILE_REL}#stack.language` }];
5050
5169
  return {
5051
5170
  language,
5052
5171
  framework: typeof configured.framework === "string" ? configured.framework : void 0,
@@ -5075,7 +5194,7 @@ async function detectPackageManager(rootDir, packageJson) {
5075
5194
  };
5076
5195
  }
5077
5196
  for (const [lockfile, packageManager] of LOCKFILES) {
5078
- if (await fileExists(path22.join(rootDir, lockfile))) {
5197
+ if (await fileExists(path24.join(rootDir, lockfile))) {
5079
5198
  return {
5080
5199
  packageManager,
5081
5200
  evidence: [{ source: "file", value: lockfile }]
@@ -5094,33 +5213,33 @@ function commandsForScripts(scripts, packageManager) {
5094
5213
  async function detectStack(rootDir) {
5095
5214
  const confirmed = await readConfirmedStack(rootDir);
5096
5215
  if (confirmed) return confirmed;
5097
- const hasAnyProjectMarker = (await Promise.all(PROJECT_MARKERS.map((item) => fileExists(path22.join(rootDir, item))))).some(Boolean);
5216
+ const hasAnyProjectMarker = (await Promise.all(PROJECT_MARKERS.map((item) => fileExists(path24.join(rootDir, item))))).some(Boolean);
5098
5217
  if (!hasAnyProjectMarker) {
5099
5218
  const subdirHits = await scanSubdirectoryMarkers(rootDir);
5100
5219
  if (subdirHits.length > 0) {
5101
5220
  return detectStackFromSubdirHits(rootDir, subdirHits);
5102
5221
  }
5103
5222
  }
5104
- const hasPackageJson = await fileExists(path22.join(rootDir, "package.json"));
5223
+ const hasPackageJson = await fileExists(path24.join(rootDir, "package.json"));
5105
5224
  if (hasPackageJson) {
5106
- const packageJson = await readJson(path22.join(rootDir, "package.json")) ?? {};
5225
+ const packageJson = await readJson(path24.join(rootDir, "package.json")) ?? {};
5107
5226
  const scripts = packageJson.scripts ?? {};
5108
5227
  const packageManager = await detectPackageManager(rootDir, packageJson);
5109
5228
  const commands = commandsForScripts(scripts, packageManager.packageManager);
5110
- const hasNextConfig = await fileExists(path22.join(rootDir, "next.config.js")) || await fileExists(path22.join(rootDir, "next.config.mjs")) || await fileExists(path22.join(rootDir, "next.config.ts"));
5111
- const hasNestConfig = await fileExists(path22.join(rootDir, "nest-cli.json"));
5229
+ const hasNextConfig = await fileExists(path24.join(rootDir, "next.config.js")) || await fileExists(path24.join(rootDir, "next.config.mjs")) || await fileExists(path24.join(rootDir, "next.config.ts"));
5230
+ const hasNestConfig = await fileExists(path24.join(rootDir, "nest-cli.json"));
5112
5231
  return {
5113
5232
  language: "node",
5114
5233
  framework: hasNextConfig ? "nextjs" : hasNestConfig ? "nestjs" : "node",
5115
5234
  packageManager: packageManager.packageManager,
5116
5235
  packageManagerEvidence: packageManager.evidence,
5117
5236
  scripts,
5118
- workspaces: packageJson.workspaces !== void 0 || await fileExists(path22.join(rootDir, "pnpm-workspace.yaml")),
5237
+ workspaces: packageJson.workspaces !== void 0 || await fileExists(path24.join(rootDir, "pnpm-workspace.yaml")),
5119
5238
  ...commands,
5120
5239
  hasProjectFiles: hasAnyProjectMarker
5121
5240
  };
5122
5241
  }
5123
- if (await fileExists(path22.join(rootDir, "pyproject.toml"))) {
5242
+ if (await fileExists(path24.join(rootDir, "pyproject.toml"))) {
5124
5243
  return {
5125
5244
  language: "python",
5126
5245
  framework: "python",
@@ -5130,7 +5249,7 @@ async function detectStack(rootDir) {
5130
5249
  hasProjectFiles: hasAnyProjectMarker
5131
5250
  };
5132
5251
  }
5133
- if (await fileExists(path22.join(rootDir, "go.mod"))) {
5252
+ if (await fileExists(path24.join(rootDir, "go.mod"))) {
5134
5253
  return {
5135
5254
  language: "go",
5136
5255
  framework: "go",
@@ -5140,7 +5259,7 @@ async function detectStack(rootDir) {
5140
5259
  hasProjectFiles: hasAnyProjectMarker
5141
5260
  };
5142
5261
  }
5143
- if (await fileExists(path22.join(rootDir, "Cargo.toml"))) {
5262
+ if (await fileExists(path24.join(rootDir, "Cargo.toml"))) {
5144
5263
  return {
5145
5264
  language: "rust",
5146
5265
  framework: "rust",
@@ -5150,7 +5269,7 @@ async function detectStack(rootDir) {
5150
5269
  hasProjectFiles: hasAnyProjectMarker
5151
5270
  };
5152
5271
  }
5153
- if (await fileExists(path22.join(rootDir, "composer.json"))) {
5272
+ if (await fileExists(path24.join(rootDir, "composer.json"))) {
5154
5273
  return {
5155
5274
  language: "php",
5156
5275
  framework: "php",
@@ -5160,7 +5279,7 @@ async function detectStack(rootDir) {
5160
5279
  hasProjectFiles: hasAnyProjectMarker
5161
5280
  };
5162
5281
  }
5163
- if (await fileExists(path22.join(rootDir, "pubspec.yaml"))) {
5282
+ if (await fileExists(path24.join(rootDir, "pubspec.yaml"))) {
5164
5283
  const { isFlutter } = await detectDartFlavor(rootDir);
5165
5284
  return {
5166
5285
  language: "dart",
@@ -5194,7 +5313,7 @@ function isGreenfieldByEntries(entries) {
5194
5313
  return meaningful.length === 0;
5195
5314
  }
5196
5315
  async function runScanner(rootDir) {
5197
- const normalizedRoot = path23.resolve(rootDir);
5316
+ const normalizedRoot = path25.resolve(rootDir);
5198
5317
  const entries = await listDirectory(normalizedRoot);
5199
5318
  const stack = await detectStack(normalizedRoot);
5200
5319
  const purpose = await detectPurpose(normalizedRoot, stack);
@@ -5232,8 +5351,7 @@ async function runScanner(rootDir) {
5232
5351
  }
5233
5352
 
5234
5353
  // src/scanner/safe-fixes.ts
5235
- var PROFILE_RELATIVE_PATH = ".cursor/agent-kit.config.json";
5236
- var CONTEXT_CONFIG_RELATIVE_PATH = ".cursor/context/config.json";
5354
+ var CONTEXT_CONFIG_REL_POSIX = CONTEXT_CONFIG_REL.replace(/\\/g, "/");
5237
5355
  var ESSENTIAL_DIRECTORIES = [
5238
5356
  ".cursor",
5239
5357
  ".cursor/context",
@@ -5418,7 +5536,7 @@ async function refreshRepositoryProfile(rootDir, options) {
5418
5536
  generatedAt
5419
5537
  });
5420
5538
  const desiredProfile = createProfile(scan, report, generatedAt);
5421
- const profilePath = path24.join(scan.rootDir, PROFILE_RELATIVE_PATH);
5539
+ const profilePath = path26.join(scan.rootDir, REPOSITORY_PROFILE_REL);
5422
5540
  const existingProfile = await readJson(profilePath) ?? {};
5423
5541
  const reconciled = reconcileFreshOverExisting(desiredProfile, existingProfile);
5424
5542
  const meaningfulChange = !jsonEqual(
@@ -5441,7 +5559,7 @@ async function executeSafeReadinessFixes(rootDir, options) {
5441
5559
  });
5442
5560
  const changes = [];
5443
5561
  for (const relativePath of ESSENTIAL_DIRECTORIES) {
5444
- const absolutePath = path24.join(beforeScan.rootDir, relativePath);
5562
+ const absolutePath = path26.join(beforeScan.rootDir, relativePath);
5445
5563
  const exists2 = await fileExists(absolutePath);
5446
5564
  if (!exists2 && !dryRun) await ensureDir(absolutePath);
5447
5565
  recordChange(
@@ -5454,14 +5572,14 @@ async function executeSafeReadinessFixes(rootDir, options) {
5454
5572
  );
5455
5573
  }
5456
5574
  const gitignoreRelativePath = ".gitignore";
5457
- const gitignorePath = path24.join(beforeScan.rootDir, gitignoreRelativePath);
5458
- const existingGitignore = await fileExists(gitignorePath) ? await readFile15(gitignorePath, "utf8") : "";
5575
+ const gitignorePath = path26.join(beforeScan.rootDir, gitignoreRelativePath);
5576
+ const existingGitignore = await fileExists(gitignorePath) ? await readFile16(gitignorePath, "utf8") : "";
5459
5577
  const gitignoreAfterSecrets = mergeSecretIgnores(existingGitignore);
5460
5578
  const secretsChanged = gitignoreAfterSecrets !== existingGitignore;
5461
5579
  const mergedGitignore = mergeKitOwnedIgnores(gitignoreAfterSecrets);
5462
5580
  const gitignoreChanged = mergedGitignore !== existingGitignore;
5463
5581
  const kitOwnedChanged = mergedGitignore !== gitignoreAfterSecrets;
5464
- if (gitignoreChanged && !dryRun) await writeFile5(gitignorePath, mergedGitignore, "utf8");
5582
+ if (gitignoreChanged && !dryRun) await writeFile6(gitignorePath, mergedGitignore, "utf8");
5465
5583
  recordChange(
5466
5584
  changes,
5467
5585
  "merge-secret-ignores",
@@ -5484,7 +5602,7 @@ async function executeSafeReadinessFixes(rootDir, options) {
5484
5602
  kitOwnedChanged ? "kit-owned session/derived paths are missing" : "kit-owned session/derived paths are present"
5485
5603
  )
5486
5604
  );
5487
- const profilePath = path24.join(beforeScan.rootDir, PROFILE_RELATIVE_PATH);
5605
+ const profilePath = path26.join(beforeScan.rootDir, REPOSITORY_PROFILE_REL);
5488
5606
  const existingProfile = await readJson(profilePath) ?? {};
5489
5607
  const desiredProfile = createProfile(beforeScan, before, generatedAt);
5490
5608
  const mergedProfile = mergeMissing(existingProfile, desiredProfile);
@@ -5493,11 +5611,11 @@ async function executeSafeReadinessFixes(rootDir, options) {
5493
5611
  recordChange(
5494
5612
  changes,
5495
5613
  "merge-repository-profile",
5496
- PROFILE_RELATIVE_PATH,
5614
+ REPOSITORY_PROFILE_REL,
5497
5615
  profileChanged,
5498
5616
  dryRun,
5499
5617
  relativeEvidence(
5500
- PROFILE_RELATIVE_PATH,
5618
+ REPOSITORY_PROFILE_REL,
5501
5619
  profileChanged ? "missing scanner-derived facts" : "existing facts preserved"
5502
5620
  )
5503
5621
  );
@@ -5506,22 +5624,22 @@ async function executeSafeReadinessFixes(rootDir, options) {
5506
5624
  generatorVersion: options.generatorVersion,
5507
5625
  generatedAt
5508
5626
  });
5509
- const contextConfigPath = path24.join(beforeScan.rootDir, CONTEXT_CONFIG_RELATIVE_PATH);
5510
- const existingContextConfig = await readJson(contextConfigPath) ?? {};
5627
+ const configPath = contextConfigPath(beforeScan.rootDir);
5628
+ const existingContextConfig = await readJson(configPath) ?? {};
5511
5629
  const onboarding = reconcileOnboardingState(evidenceReport, existingContextConfig, generatedAt);
5512
5630
  const defaults = preferenceDefaults(onboarding, existingContextConfig.onboarded);
5513
5631
  const mergedContextConfig = mergeMissing(existingContextConfig, defaults);
5514
5632
  mergedContextConfig.onboarding = onboarding;
5515
5633
  const contextConfigChanged = !jsonEqual(existingContextConfig, mergedContextConfig);
5516
- if (contextConfigChanged && !dryRun) await writeJson(contextConfigPath, mergedContextConfig);
5634
+ if (contextConfigChanged && !dryRun) await writeJson(configPath, mergedContextConfig);
5517
5635
  recordChange(
5518
5636
  changes,
5519
5637
  "merge-onboarding-state",
5520
- CONTEXT_CONFIG_RELATIVE_PATH,
5638
+ CONTEXT_CONFIG_REL_POSIX,
5521
5639
  contextConfigChanged,
5522
5640
  dryRun,
5523
5641
  relativeEvidence(
5524
- CONTEXT_CONFIG_RELATIVE_PATH,
5642
+ CONTEXT_CONFIG_REL_POSIX,
5525
5643
  contextConfigChanged ? "missing preferences or onboarding state" : "state already merged"
5526
5644
  )
5527
5645
  );
@@ -5535,10 +5653,10 @@ async function executeSafeReadinessFixes(rootDir, options) {
5535
5653
  }
5536
5654
 
5537
5655
  // src/scanner/snapshot.ts
5538
- import path25 from "path";
5656
+ import path27 from "path";
5539
5657
  var READINESS_SNAPSHOT_RELATIVE_PATH = ".cursor/context/readiness.json";
5540
5658
  async function writeReadinessSnapshot(rootDir, report) {
5541
- const snapshotPath = path25.join(rootDir, READINESS_SNAPSHOT_RELATIVE_PATH);
5659
+ const snapshotPath = path27.join(rootDir, READINESS_SNAPSHOT_RELATIVE_PATH);
5542
5660
  await writeJson(snapshotPath, report);
5543
5661
  return snapshotPath;
5544
5662
  }
@@ -5730,7 +5848,7 @@ async function withCliProgress(label, fn, opts) {
5730
5848
  // src/commands/doctor.ts
5731
5849
  async function readDeferredEssentials(rootDir, report) {
5732
5850
  const config = await readJson(
5733
- path26.join(rootDir, ".cursor", "context", "config.json")
5851
+ path28.join(rootDir, ".cursor", "context", "config.json")
5734
5852
  );
5735
5853
  const deferredItems = config?.onboarding?.deferredItems;
5736
5854
  if (!Array.isArray(deferredItems)) return [];
@@ -5742,7 +5860,7 @@ async function readDeferredEssentials(rootDir, report) {
5742
5860
  ).map((item) => ({ checkId: item.checkId, reason: item.reason }));
5743
5861
  }
5744
5862
  async function runDoctor(cwd, options = {}) {
5745
- const rootDir = path26.resolve(cwd);
5863
+ const rootDir = path28.resolve(cwd);
5746
5864
  const hooks = await assessHooksHealth(rootDir);
5747
5865
  const env = await assessEnvironment();
5748
5866
  if (options.fixSafe) {
@@ -6270,8 +6388,8 @@ var guardCommand = defineCommand8({
6270
6388
 
6271
6389
  // src/commands/handoff.ts
6272
6390
  import { spawn as spawn4 } from "child_process";
6273
- import { appendFile, readFile as readFile16, readdir as readdir7, writeFile as writeFile6 } from "fs/promises";
6274
- import path27 from "path";
6391
+ import { appendFile, readFile as readFile17, readdir as readdir7, writeFile as writeFile7 } from "fs/promises";
6392
+ import path29 from "path";
6275
6393
  import { defineCommand as defineCommand9 } from "citty";
6276
6394
 
6277
6395
  // src/invariants/handoff-prune.ts
@@ -6412,12 +6530,12 @@ function pruneArchiveTimestamp(now2 = /* @__PURE__ */ new Date()) {
6412
6530
  return `${now2.getFullYear()}-${pad(now2.getMonth() + 1)}-${pad(now2.getDate())}-${pad(now2.getHours())}${pad(now2.getMinutes())}`;
6413
6531
  }
6414
6532
  async function runPrune(cwd, keep) {
6415
- const handoffPath = path27.join(cwd, ".cursor", "HANDOFF.md");
6533
+ const handoffPath = path29.join(cwd, ".cursor", "HANDOFF.md");
6416
6534
  if (!await fileExists(handoffPath)) {
6417
6535
  logger.warn("No .cursor/HANDOFF.md to prune.");
6418
6536
  return;
6419
6537
  }
6420
- const original = await readFile16(handoffPath, "utf8");
6538
+ const original = await readFile17(handoffPath, "utf8");
6421
6539
  const result = pruneHandoffText(original, keep);
6422
6540
  if (!result.ok) {
6423
6541
  logger.warn(`Refusing to prune: ${result.reason}`);
@@ -6430,9 +6548,9 @@ async function runPrune(cwd, keep) {
6430
6548
  );
6431
6549
  return;
6432
6550
  }
6433
- const archiveDir = path27.join(cwd, ".cursor", "context", "archive");
6551
+ const archiveDir = path29.join(cwd, ".cursor", "context", "archive");
6434
6552
  await ensureDir(archiveDir);
6435
- const archivePath = path27.join(archiveDir, `handoff-${pruneArchiveTimestamp()}.md`);
6553
+ const archivePath = path29.join(archiveDir, `handoff-${pruneArchiveTimestamp()}.md`);
6436
6554
  const header = [
6437
6555
  `<!-- Pruned from .cursor/HANDOFF.md at ${(/* @__PURE__ */ new Date()).toISOString()} -->`,
6438
6556
  `<!-- ${result.archived.length} narrative ${result.archived.length === 1 ? "entry" : "entries"} archived, keep=${keep} -->`,
@@ -6444,11 +6562,11 @@ async function runPrune(cwd, keep) {
6444
6562
  await appendFile(archivePath, `
6445
6563
  ${header}${body}`, "utf8");
6446
6564
  } else {
6447
- await writeFile6(archivePath, `${header}${body}`, "utf8");
6565
+ await writeFile7(archivePath, `${header}${body}`, "utf8");
6448
6566
  }
6449
- await writeFile6(handoffPath, result.text ?? original, "utf8");
6567
+ await writeFile7(handoffPath, result.text ?? original, "utf8");
6450
6568
  logger.success(
6451
- `HANDOFF.md pruned: kept ${result.keptCount} narrative ${result.keptCount === 1 ? "entry" : "entries"}, archived ${result.archived.length} to ${path27.relative(cwd, archivePath)}.`
6569
+ `HANDOFF.md pruned: kept ${result.keptCount} narrative ${result.keptCount === 1 ? "entry" : "entries"}, archived ${result.archived.length} to ${path29.relative(cwd, archivePath)}.`
6452
6570
  );
6453
6571
  }
6454
6572
  function parsePlanFrontmatter(raw) {
@@ -6469,19 +6587,19 @@ async function findActivePlan(plansDir) {
6469
6587
  if (!await fileExists(plansDir)) return null;
6470
6588
  const files = (await readdir7(plansDir)).filter((f) => f.endsWith(".plan.md")).sort().reverse();
6471
6589
  for (const file of files) {
6472
- const raw = await readFile16(path27.join(plansDir, file), "utf8");
6590
+ const raw = await readFile17(path29.join(plansDir, file), "utf8");
6473
6591
  const fm = parsePlanFrontmatter(raw);
6474
6592
  if (fm?.todos?.some((t) => t.status !== "completed" && t.status !== "cancelled")) {
6475
6593
  return { file, raw };
6476
6594
  }
6477
6595
  }
6478
- return files[0] ? { file: files[0], raw: await readFile16(path27.join(plansDir, files[0]), "utf8") } : null;
6596
+ return files[0] ? { file: files[0], raw: await readFile17(path29.join(plansDir, files[0]), "utf8") } : null;
6479
6597
  }
6480
6598
  function now() {
6481
6599
  return (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 16);
6482
6600
  }
6483
6601
  async function loadProfile(rootDir) {
6484
- const configPath = path27.join(rootDir, ".cursor", "agent-kit.config.json");
6602
+ const configPath = path29.join(rootDir, ".cursor", "agent-kit.config.json");
6485
6603
  try {
6486
6604
  return await readJson(configPath);
6487
6605
  } catch {
@@ -6596,15 +6714,15 @@ var handoffCommand = defineCommand9({
6596
6714
  return;
6597
6715
  }
6598
6716
  const profile = await loadProfile(args.cwd);
6599
- const plansDir = path27.join(args.cwd, ".cursor", "plans");
6600
- const handoffPath = path27.join(args.cwd, ".cursor", "HANDOFF.md");
6717
+ const plansDir = path29.join(args.cwd, ".cursor", "plans");
6718
+ const handoffPath = path29.join(args.cwd, ".cursor", "HANDOFF.md");
6601
6719
  const plan = await findActivePlan(plansDir);
6602
6720
  if (plan) {
6603
6721
  const fm = parsePlanFrontmatter(plan.raw);
6604
6722
  if (fm) {
6605
- await ensureDir(path27.join(args.cwd, ".cursor"));
6723
+ await ensureDir(path29.join(args.cwd, ".cursor"));
6606
6724
  const content = buildHandoff(plan.file, fm, profile);
6607
- await writeFile6(handoffPath, content, "utf8");
6725
+ await writeFile7(handoffPath, content, "utf8");
6608
6726
  logger.success("HANDOFF.md updated: .cursor/HANDOFF.md");
6609
6727
  logger.info(
6610
6728
  `Plan: ${plan.file} (${fm.todos?.filter((t) => t.status === "completed").length}/${fm.todos?.length} completed)`
@@ -6622,7 +6740,7 @@ var handoffCommand = defineCommand9({
6622
6740
  }
6623
6741
  logger.warn(`Plan ${plan.file} without valid frontmatter; trying legacy flow.`);
6624
6742
  }
6625
- const scriptPath = path27.join(args.cwd, "cursor-handoff");
6743
+ const scriptPath = path29.join(args.cwd, "cursor-handoff");
6626
6744
  if (!await fileExists(scriptPath)) {
6627
6745
  printV3Guidance();
6628
6746
  return;
@@ -6647,7 +6765,7 @@ var handoffCommand = defineCommand9({
6647
6765
  });
6648
6766
 
6649
6767
  // src/commands/hook.ts
6650
- import path30 from "path";
6768
+ import path32 from "path";
6651
6769
  import { defineCommand as defineCommand10 } from "citty";
6652
6770
 
6653
6771
  // src/hooks/format-session-start.ts
@@ -6673,8 +6791,8 @@ function buildPreCompactUserMessage(payload = {}) {
6673
6791
 
6674
6792
  // src/hooks/session-start.ts
6675
6793
  import { spawn as spawn5 } from "child_process";
6676
- import { access as access7, readFile as readFile18, stat as stat4 } from "fs/promises";
6677
- import path29 from "path";
6794
+ import { readFile as readFile19, stat as stat4 } from "fs/promises";
6795
+ import path31 from "path";
6678
6796
 
6679
6797
  // src/invariants/handoff-schema.ts
6680
6798
  var MACHINE_LIST_CHECKS = [
@@ -6716,8 +6834,8 @@ function validateHandoffText(text) {
6716
6834
  }
6717
6835
 
6718
6836
  // src/plan-index/plan-index.ts
6719
- import { readFile as readFile17, writeFile as writeFile7 } from "fs/promises";
6720
- import path28 from "path";
6837
+ import { readFile as readFile18, writeFile as writeFile8 } from "fs/promises";
6838
+ import path30 from "path";
6721
6839
  var PLAN_INDEX_REL = ".cursor/context/plan-index.json";
6722
6840
  var NONE = /^(none|n\/a|empty|nil)$/i;
6723
6841
  var OPEN_STATUSES = /* @__PURE__ */ new Set(["pending", "in_progress"]);
@@ -6725,14 +6843,14 @@ function defaultPlanIndexIo() {
6725
6843
  return {
6726
6844
  async readFile(absPath) {
6727
6845
  try {
6728
- return await readFile17(absPath, "utf8");
6846
+ return await readFile18(absPath, "utf8");
6729
6847
  } catch {
6730
6848
  return null;
6731
6849
  }
6732
6850
  },
6733
6851
  async writeFile(absPath, body) {
6734
- await ensureDir(path28.dirname(absPath));
6735
- await writeFile7(absPath, body, "utf8");
6852
+ await ensureDir(path30.dirname(absPath));
6853
+ await writeFile8(absPath, body, "utf8");
6736
6854
  }
6737
6855
  };
6738
6856
  }
@@ -6757,11 +6875,11 @@ function collectNamedPlanBasenames(named) {
6757
6875
  return out;
6758
6876
  }
6759
6877
  function namedPlanCandidatePaths(root, basename) {
6760
- const safe = path28.basename(basename);
6878
+ const safe = path30.basename(basename);
6761
6879
  if (!/^[A-Za-z0-9._-]+\.plan\.md$/i.test(safe)) return [];
6762
6880
  return [
6763
- path28.join(root, ".cursor", "plans", safe),
6764
- path28.join(root, ".cursor", "plans", "archive", safe)
6881
+ path30.join(root, ".cursor", "plans", safe),
6882
+ path30.join(root, ".cursor", "plans", "archive", safe)
6765
6883
  ];
6766
6884
  }
6767
6885
  function classifyPlanRole(file, named, openTodos) {
@@ -6793,7 +6911,7 @@ function parsePlanFrontmatter2(raw) {
6793
6911
  return { name, todos };
6794
6912
  }
6795
6913
  async function buildPlanIndex(root, io = defaultPlanIndexIo(), now2 = () => /* @__PURE__ */ new Date()) {
6796
- const handoffPath = path28.join(root, ".cursor", "HANDOFF.md");
6914
+ const handoffPath = path30.join(root, HANDOFF_REL);
6797
6915
  const handoffText = await io.readFile(handoffPath) ?? "";
6798
6916
  const named = extractHandoffNamedPlans(handoffText);
6799
6917
  const plans = [];
@@ -6822,7 +6940,7 @@ async function buildPlanIndex(root, io = defaultPlanIndexIo(), now2 = () => /* @
6822
6940
  }
6823
6941
  async function writePlanIndex(root, io = defaultPlanIndexIo(), now2 = () => /* @__PURE__ */ new Date()) {
6824
6942
  const index = await buildPlanIndex(root, io, now2);
6825
- const dest = path28.join(root, PLAN_INDEX_REL);
6943
+ const dest = path30.join(root, PLAN_INDEX_REL);
6826
6944
  const write = io.writeFile ?? defaultPlanIndexIo().writeFile;
6827
6945
  if (write) {
6828
6946
  await write(dest, `${JSON.stringify(index, null, 2)}
@@ -7034,11 +7152,11 @@ var HARD_RULES = `# Agent Kit session hard rules (manual mode default)
7034
7152
  2. **Do not burn the window.** Never run an entire multi-phase plan in one conversation unless the user explicitly ran \`/run-plan\` (or a deprecated alias \`/run-plan-loop\` / \`/run-plan-orchestrated\`).
7035
7153
  3. **Context questions are not optional.** If the user asks about context / contexto / window size, run the context-guardian protocol: warn, offer handoff, do NOT dismiss with "it's fine" and keep coding.
7036
7154
  4. **Read HANDOFF first** when resuming. Do not restart the plan from scratch.
7037
- 5. **Git:** suggest \`/git-staging\` after a phase with a diff; never \`/git-prod\` without explicit confirmation.
7155
+ 5. **Git:** suggest \`/git-staging\` after a phase with a diff; never \`/git-prod\` without explicit confirmation. A \`/run-plan-all\` queue confirm with Ship auth \`per-plan-release\` is that confirmation for one ship per completed plan.
7038
7156
  6. **HITL slash commands win.** When waiting for confirmation on \`/git-staging\` or \`/git-prod\` (or similar), do not divert to continue-plan / phase-boundary chatter; stay on that routine until the user answers.
7039
7157
  7. **\`/start-project\` is plan bootstrap, not execute.** Broad Intake Review first, then two gates: (A) single composite question (with active plan: backlog+write / park+write / modify / cancel; without: write / write+backlog (\`Write plan and add to backlog\`) / modify / cancel): approve/write the plan file only, (B) approve the first unit. Goal text in the same message is NOT permission to edit product files. Never "create plan and start Phase 1" in one turn. If HANDOFF already has an active plan, disposition is merged into Gate A composite options; never park silently. Gates use Ask questions per \`.cursor/skills/core/hitl-gates/SKILL.md\` and \`.cursor/rules/hitl-ask-questions.mdc\`. Fallback: numbered list is path 1.
7040
7158
  8. **\`/continue-plan\` waits for yes.** Summarize next \`[to-do-id]\`, then stop until the user confirms before editing.
7041
- 9. **\`/run-plan-all\` is a pure orchestrator.** After the confirm queue Ask, dispatch one Task subagent per plan (run the \`/run-plan\` tick contract inside it); never implement to-dos, run tests, or write changelogs in the orchestrator window.
7159
+ 9. **\`/run-plan-all\` is a pure orchestrator.** After the confirm queue Ask, dispatch one Task subagent per plan (run the \`/run-plan\` tick contract inside it). Do not implement that plan's to-dos in the orchestrator window. When Ship auth is \`per-plan-release\`, this window ships one release after each completed plan and waits until it is Done before the next plan. \`Run plans only\` does not ship. A red public sync stops the queue.
7042
7160
  10. **Backlog CRUD never activates.** \`/backlog-add\` enqueues (Broad Intake + write Ask + plan file + HANDOFF Backlog) without park, activate, or Gate B. \`/backlog-edit\` / \`/backlog-delete\` / \`/backlog-cancel\` require Ask confirm before mutate; delete archives from Backlog, cancel is soft in place. No Field Report cards for routine backlog CRUD.
7043
7161
  11. **HANDOFF machine fields are bullet fields, not \`##\` headings.** Mission Control parses \`- **Plan:**\`, \`- **Backlog plans:**\`, \`- **Parked plans:**\`, \`- **Run queue:**\` (etc.). Canonical Plan: \`- **Plan:** \\\`name.plan.md\\\`\` or \`none\`. Nested backlog/parked rows: \`- \\\`other.plan.md\\\`\`. Never invent \`## Backlog plans\` / \`## Parked plans\` / \`## Run queue\` headings in place of those fields (Checklist / Current mission go empty or idle).
7044
7162
  12. **Plans inventory is index + HANDOFF.** \`/continue-plan\`, \`/run-plan-all\`, and Broad Intake read \`.cursor/context/plan-index.json\` and \`.cursor/HANDOFF.md\` only. Do not glob \`.cursor/plans/*.plan.md\`. Named single-file reads of a known basename remain OK. Delegation stays (ADR \`decisions/2026-07-26_command-orchestration-delegation-pattern.md\`).`;
@@ -7061,19 +7179,11 @@ var NONE_PLACEHOLDERS = /* @__PURE__ */ new Set(["none", "n/a", "empty", "nil"])
7061
7179
  var CURSOR_AWARENESS_SPAWN_TIMEOUT_MS = CHANGELOG_FETCH_TIMEOUT_MS + 3e3;
7062
7180
  async function readFull(filePath) {
7063
7181
  try {
7064
- return await readFile18(filePath, "utf8");
7182
+ return await readFile19(filePath, "utf8");
7065
7183
  } catch {
7066
7184
  return "";
7067
7185
  }
7068
7186
  }
7069
- async function fileExists2(p) {
7070
- try {
7071
- await access7(p);
7072
- return true;
7073
- } catch {
7074
- return false;
7075
- }
7076
- }
7077
7187
  function isMarkdownTableSeparator(line) {
7078
7188
  const stripped = line.trim();
7079
7189
  if (!stripped.startsWith("|")) return false;
@@ -7135,8 +7245,7 @@ function extractUnprocessedDogfoodLine(line) {
7135
7245
  return raw;
7136
7246
  }
7137
7247
  async function l0Present(root) {
7138
- const cursor = path29.join(root, ".cursor");
7139
- return await fileExists2(path29.join(cursor, "agent-kit.json")) || await fileExists2(path29.join(cursor, "commands", "agent-kit-onboard.md")) || await fileExists2(path29.join(cursor, "commands", "start-project.md"));
7248
+ return await fileExists(path31.join(root, MANIFEST_RELATIVE_PATH)) || await fileExists(path31.join(root, ".cursor", "commands", "agent-kit-onboard.md")) || await fileExists(path31.join(root, ".cursor", "commands", "start-project.md"));
7140
7249
  }
7141
7250
  function checkLabelAndRecommendation(check2) {
7142
7251
  const checkId = check2.id;
@@ -7175,10 +7284,10 @@ function unresolvedReadinessChecks(data) {
7175
7284
  return { essential, nonessential };
7176
7285
  }
7177
7286
  async function readinessSection(root) {
7178
- const snapshotPath = path29.join(root, ".cursor", "context", "readiness.json");
7287
+ const snapshotPath = path31.join(root, READINESS_SNAPSHOT_RELATIVE_PATH);
7179
7288
  let data;
7180
7289
  try {
7181
- data = JSON.parse(await readFile18(snapshotPath, "utf8"));
7290
+ data = JSON.parse(await readFile19(snapshotPath, "utf8"));
7182
7291
  } catch {
7183
7292
  return null;
7184
7293
  }
@@ -7209,13 +7318,13 @@ Optional readiness item: \`${first.id}\`. ${first.recommendation} This does not
7209
7318
  }
7210
7319
  async function dogfoodInboxSection(root) {
7211
7320
  const candidateReadmes = [
7212
- path29.join(root, "dogfood", "README.md"),
7213
- path29.join(root, ".cursor", "dogfood", "README.md")
7321
+ path31.join(root, "dogfood", "README.md"),
7322
+ path31.join(root, ".cursor", "dogfood", "README.md")
7214
7323
  ];
7215
7324
  for (const readme of candidateReadmes) {
7216
- if (!await fileExists2(readme)) continue;
7325
+ if (!await fileExists(readme)) continue;
7217
7326
  try {
7218
- const text = await readFile18(readme, "utf8");
7327
+ const text = await readFile19(readme, "utf8");
7219
7328
  if (parseUnprocessedDogfoodItems(text).length) return DOGFOOD_INBOX_HINT;
7220
7329
  } catch {
7221
7330
  }
@@ -7223,18 +7332,13 @@ async function dogfoodInboxSection(root) {
7223
7332
  return null;
7224
7333
  }
7225
7334
  async function loadUpdateCheckPrefs(root) {
7226
- try {
7227
- const data = JSON.parse(
7228
- await readFile18(path29.join(root, ".cursor", "context", "config.json"), "utf8")
7229
- );
7230
- const uc = data.updateCheck;
7231
- if (!uc || typeof uc !== "object" || uc.enabled !== true) {
7232
- return null;
7233
- }
7234
- return uc;
7235
- } catch {
7335
+ const data = await loadContextConfig(root);
7336
+ if (!data) return null;
7337
+ const uc = data.updateCheck;
7338
+ if (!uc || typeof uc !== "object" || uc.enabled !== true) {
7236
7339
  return null;
7237
7340
  }
7341
+ return uc;
7238
7342
  }
7239
7343
  function runUpdateCheckJson(root) {
7240
7344
  return new Promise((resolve3) => {
@@ -7301,18 +7405,13 @@ async function updateCheckSection(root) {
7301
7405
  return UPDATE_CHECK_NUDGE.replace("{installed}", installed).replace("{latest}", latest);
7302
7406
  }
7303
7407
  async function loadCursorUpdateCheckPrefs(root) {
7304
- try {
7305
- const data = JSON.parse(
7306
- await readFile18(path29.join(root, ".cursor", "context", "config.json"), "utf8")
7307
- );
7308
- const uc = data.cursorUpdateCheck;
7309
- if (!uc || typeof uc !== "object" || uc.enabled !== true) {
7310
- return null;
7311
- }
7312
- return uc;
7313
- } catch {
7408
+ const data = await loadContextConfig(root);
7409
+ if (!data) return null;
7410
+ const uc = data.cursorUpdateCheck;
7411
+ if (!uc || typeof uc !== "object" || uc.enabled !== true) {
7314
7412
  return null;
7315
7413
  }
7414
+ return uc;
7316
7415
  }
7317
7416
  function runCursorAwarenessJson(root) {
7318
7417
  return new Promise((resolve3) => {
@@ -7447,7 +7546,7 @@ function parseScreenDetachedAuditSessions(listing) {
7447
7546
  const [, pid, name, marker] = m;
7448
7547
  if (!name || !name.startsWith(AUDIT_SESSION_NS_PREFIX)) continue;
7449
7548
  if (marker && /[Aa]ttached/.test(marker)) continue;
7450
- entries.push({ name, socketPath: sockdir ? path29.join(sockdir, `${pid}.${name}`) : null });
7549
+ entries.push({ name, socketPath: sockdir ? path31.join(sockdir, `${pid}.${name}`) : null });
7451
7550
  }
7452
7551
  return entries;
7453
7552
  }
@@ -7495,8 +7594,8 @@ ${sessions.length} detached \`agent-kit-audit-*\` PTY ${noun} on this host (${ol
7495
7594
  }
7496
7595
  }
7497
7596
  async function buildSessionStartAdditionalContext(rootDir, _payload = {}) {
7498
- const root = path29.resolve(rootDir);
7499
- const handoffPath = path29.join(root, ".cursor", "HANDOFF.md");
7597
+ const root = path31.resolve(rootDir);
7598
+ const handoffPath = path31.join(root, HANDOFF_REL);
7500
7599
  const handoffFull = await readFull(handoffPath);
7501
7600
  const handoff = capHandoffExcerpt(handoffFull);
7502
7601
  const parts = [HARD_RULES];
@@ -7563,7 +7662,7 @@ async function runSessionStartHook(cwd, formatArg, deps = {}) {
7563
7662
  const readStdin = deps.readStdin ?? readStdinJson;
7564
7663
  const buildContext = deps.buildContext ?? buildSessionStartAdditionalContext;
7565
7664
  const payload = await readStdin();
7566
- const root = resolveSessionRoot(payload, path30.resolve(cwd));
7665
+ const root = resolveSessionRoot(payload, path32.resolve(cwd));
7567
7666
  const out = await buildContext(root, payload);
7568
7667
  return formatSessionStartOutput(out.additional_context, format);
7569
7668
  } catch {
@@ -7616,12 +7715,12 @@ import { defineCommand as defineCommand12 } from "citty";
7616
7715
 
7617
7716
  // src/lifecycle/path-cli.ts
7618
7717
  import { spawn as spawn6 } from "child_process";
7619
- import path32 from "path";
7718
+ import path34 from "path";
7620
7719
 
7621
7720
  // src/utils/terminal.ts
7622
7721
  import { readdir as readdir8 } from "fs/promises";
7623
7722
  import { homedir as homedir3 } from "os";
7624
- import path31 from "path";
7723
+ import path33 from "path";
7625
7724
  import { confirm, isCancel } from "@clack/prompts";
7626
7725
  function isNonInteractive() {
7627
7726
  if (process.env.CI === "true" || process.env.CI === "1") return true;
@@ -7645,14 +7744,14 @@ async function findNestedRepoChildren(resolved) {
7645
7744
  if (!entry.isDirectory()) continue;
7646
7745
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
7647
7746
  scanned += 1;
7648
- if (await fileExists(path31.join(resolved, entry.name, ".git"))) {
7747
+ if (await fileExists(path33.join(resolved, entry.name, ".git"))) {
7649
7748
  nested.push(entry.name);
7650
7749
  }
7651
7750
  }
7652
7751
  return nested;
7653
7752
  }
7654
7753
  async function validateProjectRoot(resolved) {
7655
- const home = path31.resolve(homedir3());
7754
+ const home = path33.resolve(homedir3());
7656
7755
  if (resolved === "/" || resolved === home) {
7657
7756
  return {
7658
7757
  ok: false,
@@ -7660,12 +7759,12 @@ async function validateProjectRoot(resolved) {
7660
7759
  recovery: "Change into a project directory and re-run. Agent Kit installs per project."
7661
7760
  };
7662
7761
  }
7663
- const hasGit = await fileExists(path31.join(resolved, ".git"));
7664
- const hasManifest = await fileExists(path31.join(resolved, ".cursor", "agent-kit.json"));
7762
+ const hasGit = await fileExists(path33.join(resolved, ".git"));
7763
+ const hasManifest = await fileExists(path33.join(resolved, MANIFEST_RELATIVE_PATH));
7665
7764
  if (!hasGit && !hasManifest) {
7666
7765
  return {
7667
7766
  ok: false,
7668
- reason: `Refused ${resolved}: no .git and no .cursor/agent-kit.json.`,
7767
+ reason: `Refused ${resolved}: no .git and no ${MANIFEST_RELATIVE_PATH}.`,
7669
7768
  recovery: [
7670
7769
  "Starting from an empty folder? Pick one of these:",
7671
7770
  " 1. git init - then re-run. Recommended: readiness and the",
@@ -7694,7 +7793,7 @@ async function validateProjectRoot(resolved) {
7694
7793
  return { ok: true };
7695
7794
  }
7696
7795
  async function confirmProjectRoot(cwd, opts) {
7697
- const resolved = path31.resolve(cwd);
7796
+ const resolved = path33.resolve(cwd);
7698
7797
  if (opts.forceRoot) {
7699
7798
  return resolved;
7700
7799
  }
@@ -7808,9 +7907,9 @@ function npxPinned(runtimeVersion, subcommand) {
7808
7907
  return `npx -y ${pinnedCliSpec(runtimeVersion)} ${subcommand}`;
7809
7908
  }
7810
7909
  function isBinUnderPrefix(binPath, prefix) {
7811
- const bin = path32.resolve(binPath);
7812
- const root = path32.resolve(prefix);
7813
- return bin === root || bin.startsWith(`${root}${path32.sep}`);
7910
+ const bin = path34.resolve(binPath);
7911
+ const root = path34.resolve(prefix);
7912
+ return bin === root || bin.startsWith(`${root}${path34.sep}`);
7814
7913
  }
7815
7914
  function pathCliStatus(env, runtimeVersion) {
7816
7915
  if (!env.binOnPath || !env.binPath) return "missing";
@@ -7908,17 +8007,17 @@ async function syncPathCliToRuntime(options = {}) {
7908
8007
  }
7909
8008
 
7910
8009
  // src/commands/install.ts
7911
- import path39 from "path";
8010
+ import path40 from "path";
7912
8011
  import { defineCommand as defineCommand11 } from "citty";
7913
8012
  import { bold, cyan as cyan3, green as green2, options as koloristOptions2 } from "kolorist";
7914
8013
 
7915
8014
  // src/generator/personalization.ts
7916
8015
  import { readFile as readFile21, writeFile as writeFile12 } from "fs/promises";
7917
- import path37 from "path";
8016
+ import path38 from "path";
7918
8017
 
7919
8018
  // src/generator/claude-command-adapters.ts
7920
- import { readFile as readFile19, readdir as readdir9, writeFile as writeFile8 } from "fs/promises";
7921
- import path33 from "path";
8019
+ import { readFile as readFile20, readdir as readdir9, writeFile as writeFile9 } from "fs/promises";
8020
+ import path35 from "path";
7922
8021
  var CURSOR_COMMANDS_DIR_REL = ".cursor/commands";
7923
8022
  var CLAUDE_COMMANDS_DIR_REL = ".claude/commands";
7924
8023
  var RESERVED_ADAPTER_NAMES = /* @__PURE__ */ new Set(["agent-kit"]);
@@ -7947,7 +8046,7 @@ Adapter rules (Claude Code CLI):
7947
8046
  `;
7948
8047
  }
7949
8048
  async function discoverInstalledCommands(rootDir) {
7950
- const dir = path33.join(rootDir, CURSOR_COMMANDS_DIR_REL);
8049
+ const dir = path35.join(rootDir, CURSOR_COMMANDS_DIR_REL);
7951
8050
  let entries;
7952
8051
  try {
7953
8052
  entries = (await readdir9(dir)).filter((f) => f.endsWith(".md"));
@@ -7959,7 +8058,7 @@ async function discoverInstalledCommands(rootDir) {
7959
8058
  const name = file.slice(0, -3);
7960
8059
  if (RESERVED_ADAPTER_NAMES.has(name)) continue;
7961
8060
  try {
7962
- const raw = await readFile19(path33.join(dir, file), "utf8");
8061
+ const raw = await readFile20(path35.join(dir, file), "utf8");
7963
8062
  const parsed = parseCommandFrontmatter(name, raw);
7964
8063
  if (parsed) commands.push(parsed);
7965
8064
  } catch {
@@ -7974,18 +8073,18 @@ async function generateClaudeCommandAdapters(rootDir) {
7974
8073
  const results = [];
7975
8074
  let ledgerDirty = false;
7976
8075
  for (const command of commands) {
7977
- const relPath = path33.posix.join(CLAUDE_COMMANDS_DIR_REL, `${command.name}.md`);
8076
+ const relPath = path35.posix.join(CLAUDE_COMMANDS_DIR_REL, `${command.name}.md`);
7978
8077
  const rendered = renderClaudeCommandAdapter(command);
7979
- const abs = path33.join(rootDir, relPath);
8078
+ const abs = path35.join(rootDir, relPath);
7980
8079
  if (!await fileExists(abs)) {
7981
- await ensureDir(path33.dirname(abs));
7982
- await writeFile8(abs, rendered, "utf8");
8080
+ await ensureDir(path35.dirname(abs));
8081
+ await writeFile9(abs, rendered, "utf8");
7983
8082
  ledger.hashes[relPath] = contentHash(rendered);
7984
8083
  ledgerDirty = true;
7985
8084
  results.push({ relativePath: relPath, status: "applied" });
7986
8085
  continue;
7987
8086
  }
7988
- const localContent = await readFile19(abs, "utf8");
8087
+ const localContent = await readFile20(abs, "utf8");
7989
8088
  if (localContent === rendered) {
7990
8089
  if (ledger.hashes[relPath] !== contentHash(rendered)) {
7991
8090
  ledger.hashes[relPath] = contentHash(rendered);
@@ -7998,7 +8097,7 @@ async function generateClaudeCommandAdapters(rootDir) {
7998
8097
  results.push({ relativePath: relPath, status: "preserved-customized" });
7999
8098
  continue;
8000
8099
  }
8001
- await writeFile8(abs, rendered, "utf8");
8100
+ await writeFile9(abs, rendered, "utf8");
8002
8101
  ledger.hashes[relPath] = contentHash(rendered);
8003
8102
  ledgerDirty = true;
8004
8103
  results.push({ relativePath: relPath, status: "refreshed" });
@@ -8008,8 +8107,8 @@ async function generateClaudeCommandAdapters(rootDir) {
8008
8107
  }
8009
8108
 
8010
8109
  // src/generator/claude-kit-load.ts
8011
- import { writeFile as writeFile9 } from "fs/promises";
8012
- import path34 from "path";
8110
+ import { writeFile as writeFile10 } from "fs/promises";
8111
+ import path36 from "path";
8013
8112
  var CLAUDE_MD_REL = "CLAUDE.md";
8014
8113
  var AGENT_KIT_COMMAND_REL = ".claude/commands/agent-kit.md";
8015
8114
  function renderClaudeMd() {
@@ -8065,12 +8164,12 @@ Non-goals: not audits / \`/plan-external-review\`, not a second tick dialect (\`
8065
8164
  `;
8066
8165
  }
8067
8166
  async function writeUnlessExists(rootDir, relativePath, content) {
8068
- const target = path34.join(rootDir, relativePath);
8167
+ const target = path36.join(rootDir, relativePath);
8069
8168
  if (await fileExists(target)) {
8070
8169
  return { relativePath, status: "skipped-customized" };
8071
8170
  }
8072
- await ensureDir(path34.dirname(target));
8073
- await writeFile9(target, content, "utf8");
8171
+ await ensureDir(path36.dirname(target));
8172
+ await writeFile10(target, content, "utf8");
8074
8173
  return { relativePath, status: "applied" };
8075
8174
  }
8076
8175
  async function generateClaudeKitLoadArtifacts(rootDir) {
@@ -8080,109 +8179,9 @@ async function generateClaudeKitLoadArtifacts(rootDir) {
8080
8179
  ]);
8081
8180
  }
8082
8181
 
8083
- // src/generator/claude-session-start-hook.ts
8084
- import { readFile as readFile20, writeFile as writeFile10 } from "fs/promises";
8085
- import path35 from "path";
8086
- var CLAUDE_SETTINGS_REL = ".claude/settings.json";
8087
- var RESOLVE_AGENT_KIT_REL = ".cursor/hooks/agent/resolve-agent-kit.sh";
8088
- var SESSION_START_HOOK_MARKER = "hook session-start --format claude";
8089
- var SESSION_START_DEGRADED_TEXT = "Agent Kit hooks are running in degraded fail-open mode: the agent-kit CLI could not be resolved (checked AGENT_KIT_HOOK_BIN, PATH, node_modules/.bin/agent-kit). Slash command adapters still work; session-context injection is inactive. Fix: install the CLI (npm i -D @dadado/agent-kit-cli) or set AGENT_KIT_HOOK_BIN.";
8090
- function buildSessionStartHookCommand() {
8091
- return `. "\${CLAUDE_PROJECT_DIR}/${RESOLVE_AGENT_KIT_REL}" 2>/dev/null && resolve_agent_kit && exec $AGENT_KIT_RESOLVED hook session-start --format claude; printf '%s' ${shellSingleQuote(SESSION_START_DEGRADED_TEXT)}`;
8092
- }
8093
- function shellSingleQuote(text) {
8094
- return `'${text.replace(/'/g, "'\\''")}'`;
8095
- }
8096
- function buildSessionStartHookEntry() {
8097
- return {
8098
- type: "command",
8099
- command: buildSessionStartHookCommand(),
8100
- timeout: 15,
8101
- statusMessage: "Loading Agent Kit session context"
8102
- };
8103
- }
8104
- function isPlainObject2(value) {
8105
- return typeof value === "object" && value !== null && !Array.isArray(value);
8106
- }
8107
- function groupHasMarker(group) {
8108
- if (!isPlainObject2(group) || !Array.isArray(group.hooks)) return false;
8109
- return group.hooks.some(
8110
- (h) => isPlainObject2(h) && typeof h.command === "string" && h.command.includes(SESSION_START_HOOK_MARKER)
8111
- );
8112
- }
8113
- function mergeSessionStartHookIntoSettings(existingRaw) {
8114
- const entry = buildSessionStartHookEntry();
8115
- const newGroup = { hooks: [entry] };
8116
- let root = {};
8117
- if (existingRaw !== null && existingRaw.trim() !== "") {
8118
- try {
8119
- const parsed = JSON.parse(existingRaw);
8120
- if (!isPlainObject2(parsed)) throw new Error("root is not an object");
8121
- root = parsed;
8122
- } catch {
8123
- return {
8124
- content: null,
8125
- status: "unavailable",
8126
- instructions: instructionsBlock(entry)
8127
- };
8128
- }
8129
- }
8130
- const hooks = isPlainObject2(root.hooks) ? { ...root.hooks } : {};
8131
- const sessionStart = Array.isArray(hooks.SessionStart) ? [...hooks.SessionStart] : [];
8132
- const existingIndex = sessionStart.findIndex((g) => groupHasMarker(g));
8133
- let status;
8134
- if (existingIndex === -1) {
8135
- sessionStart.push(newGroup);
8136
- status = "applied";
8137
- } else {
8138
- const current = sessionStart[existingIndex];
8139
- if (JSON.stringify(current) === JSON.stringify(newGroup)) {
8140
- status = "unchanged";
8141
- } else {
8142
- sessionStart[existingIndex] = newGroup;
8143
- status = "refreshed";
8144
- }
8145
- }
8146
- hooks.SessionStart = sessionStart;
8147
- root.hooks = hooks;
8148
- return { content: `${JSON.stringify(root, null, 2)}
8149
- `, status };
8150
- }
8151
- function instructionsBlock(entry) {
8152
- return [
8153
- `Could not parse the existing ${CLAUDE_SETTINGS_REL} as JSON, so Agent Kit did not touch it.`,
8154
- "Add this hook by hand under hooks.SessionStart (create the arrays if they do not exist):",
8155
- "",
8156
- JSON.stringify(entry, null, 2)
8157
- ].join("\n");
8158
- }
8159
- async function writeClaudeSessionStartHook(rootDir) {
8160
- const abs = path35.join(rootDir, CLAUDE_SETTINGS_REL);
8161
- let existing = null;
8162
- try {
8163
- existing = await readFile20(abs, "utf8");
8164
- } catch {
8165
- existing = null;
8166
- }
8167
- const merged = mergeSessionStartHookIntoSettings(existing);
8168
- if (merged.status === "unavailable" || merged.content === null) {
8169
- return {
8170
- relativePath: CLAUDE_SETTINGS_REL,
8171
- status: "unavailable",
8172
- instructions: merged.instructions
8173
- };
8174
- }
8175
- if (merged.status === "unchanged") {
8176
- return { relativePath: CLAUDE_SETTINGS_REL, status: "unchanged" };
8177
- }
8178
- await ensureDir(path35.dirname(abs));
8179
- await writeFile10(abs, merged.content, "utf8");
8180
- return { relativePath: CLAUDE_SETTINGS_REL, status: merged.status };
8181
- }
8182
-
8183
8182
  // src/generator/vscode.ts
8184
8183
  import { writeFile as writeFile11 } from "fs/promises";
8185
- import path36 from "path";
8184
+ import path37 from "path";
8186
8185
 
8187
8186
  // src/generator/platform.ts
8188
8187
  function gitProviderLabel(profile) {
@@ -8206,10 +8205,10 @@ function prTerminology(profile) {
8206
8205
  // src/generator/vscode.ts
8207
8206
  async function generateVSCodeArtifacts(profile) {
8208
8207
  const results = [];
8209
- const vscodeDir = path36.join(profile.rootDir, ".vscode");
8210
- const githubDir = path36.join(profile.rootDir, ".github");
8208
+ const vscodeDir = path37.join(profile.rootDir, ".vscode");
8209
+ const githubDir = path37.join(profile.rootDir, ".github");
8211
8210
  await Promise.all([ensureDir(vscodeDir), ensureDir(githubDir)]);
8212
- const settingsPath = path36.join(vscodeDir, "settings.json");
8211
+ const settingsPath = path37.join(vscodeDir, "settings.json");
8213
8212
  if (await fileExists(settingsPath)) {
8214
8213
  results.push({ relativePath: ".vscode/settings.json", status: "skipped-customized" });
8215
8214
  } else {
@@ -8233,7 +8232,7 @@ async function generateVSCodeArtifacts(profile) {
8233
8232
  }
8234
8233
  const provider = gitProviderLabel(profile);
8235
8234
  const prTerm = prTerminology(profile);
8236
- const copilotPath = path36.join(githubDir, "copilot-instructions.md");
8235
+ const copilotPath = path37.join(githubDir, "copilot-instructions.md");
8237
8236
  if (await fileExists(copilotPath)) {
8238
8237
  results.push({ relativePath: ".github/copilot-instructions.md", status: "skipped-customized" });
8239
8238
  } else {
@@ -8251,7 +8250,7 @@ async function generateVSCodeArtifacts(profile) {
8251
8250
  results.push({ relativePath: ".github/copilot-instructions.md", status: "applied" });
8252
8251
  }
8253
8252
  if (profile.ide.plan === "vscode-pro") {
8254
- const securityPath = path36.join(vscodeDir, "security-review.agent.md");
8253
+ const securityPath = path37.join(vscodeDir, "security-review.agent.md");
8255
8254
  if (await fileExists(securityPath)) {
8256
8255
  results.push({
8257
8256
  relativePath: ".vscode/security-review.agent.md",
@@ -8463,7 +8462,7 @@ function renderProjectContext(profile, skillItems = []) {
8463
8462
  `;
8464
8463
  }
8465
8464
  async function createOwnedFile(rootDir, relativePath, content, evidence) {
8466
- const target = path37.join(rootDir, relativePath);
8465
+ const target = path38.join(rootDir, relativePath);
8467
8466
  if (await fileExists(target)) {
8468
8467
  return {
8469
8468
  kind: "file",
@@ -8473,7 +8472,7 @@ async function createOwnedFile(rootDir, relativePath, content, evidence) {
8473
8472
  evidence
8474
8473
  };
8475
8474
  }
8476
- await ensureDir(path37.dirname(target));
8475
+ await ensureDir(path38.dirname(target));
8477
8476
  await writeFile12(target, content, "utf8");
8478
8477
  return {
8479
8478
  kind: "file",
@@ -8490,7 +8489,7 @@ async function packTargets(registryRoot, packId) {
8490
8489
  async function existingTargets(projectRoot, targets) {
8491
8490
  const checks = await Promise.all(
8492
8491
  targets.map(
8493
- async (target) => await fileExists(path37.join(projectRoot, target)) ? target : null
8492
+ async (target) => await fileExists(path38.join(projectRoot, target)) ? target : null
8494
8493
  )
8495
8494
  );
8496
8495
  return checks.filter((target) => target !== null);
@@ -8512,14 +8511,14 @@ async function applyPersonalization(input) {
8512
8511
  componentResults.push({ ...item, status: "unavailable" });
8513
8512
  continue;
8514
8513
  }
8515
- const target = path37.posix.join(
8514
+ const target = path38.posix.join(
8516
8515
  ".cursor",
8517
8516
  "skills",
8518
8517
  skill.path.includes("/core/") ? "core" : "community",
8519
8518
  skill.id,
8520
8519
  "SKILL.md"
8521
8520
  );
8522
- if (await fileExists(path37.join(input.rootDir, target))) {
8521
+ if (await fileExists(path38.join(input.rootDir, target))) {
8523
8522
  componentResults.push({ ...item, status: "skipped-customized", path: target });
8524
8523
  protectedPaths.add(target);
8525
8524
  continue;
@@ -8664,7 +8663,7 @@ async function applyPersonalization(input) {
8664
8663
  protectedPaths: [...protectedPaths].sort(),
8665
8664
  ...claudeSessionStartInstructions ? { claudeSessionStartInstructions } : {}
8666
8665
  };
8667
- await writeJson(path37.join(input.rootDir, RESULT_PATH), result);
8666
+ await writeJson(path38.join(input.rootDir, RESULT_PATH), result);
8668
8667
  return {
8669
8668
  result,
8670
8669
  manifest: {
@@ -8682,7 +8681,7 @@ async function applyPersonalization(input) {
8682
8681
  };
8683
8682
  }
8684
8683
  async function readRepositoryProfile(rootDir) {
8685
- const target = path37.join(rootDir, ".cursor/agent-kit.config.json");
8684
+ const target = path38.join(rootDir, REPOSITORY_PROFILE_REL);
8686
8685
  if (!await fileExists(target)) return null;
8687
8686
  return JSON.parse(await readFile21(target, "utf8"));
8688
8687
  }
@@ -8690,16 +8689,16 @@ async function readRepositoryProfile(rootDir) {
8690
8689
  // src/lifecycle/onboard-migration.ts
8691
8690
  import { createHash as createHash4 } from "crypto";
8692
8691
  import { readFile as readFile22, unlink } from "fs/promises";
8693
- import path38 from "path";
8692
+ import path39 from "path";
8694
8693
  var LEGACY_ONBOARD_PATH = ".cursor/commands/onboard.md";
8695
8694
  var NAMESPACED_ONBOARD_PATH = ".cursor/commands/agent-kit-onboard.md";
8696
8695
  var MANAGED_LEGACY_HASHES = /* @__PURE__ */ new Set([
8697
8696
  "b274a68941813f19b185893cb7c5561dff027f53270890029992f208e24992fe"
8698
8697
  ]);
8699
8698
  async function migrateLegacyOnboardCommand(projectRoot, managedHashes = MANAGED_LEGACY_HASHES) {
8700
- const legacyPath = path38.join(projectRoot, LEGACY_ONBOARD_PATH);
8699
+ const legacyPath = path39.join(projectRoot, LEGACY_ONBOARD_PATH);
8701
8700
  if (!await fileExists(legacyPath)) return "absent";
8702
- const namespacedPath = path38.join(projectRoot, NAMESPACED_ONBOARD_PATH);
8701
+ const namespacedPath = path39.join(projectRoot, NAMESPACED_ONBOARD_PATH);
8703
8702
  if (!await fileExists(namespacedPath)) return "preserved-customized";
8704
8703
  const content = await readFile22(legacyPath);
8705
8704
  const hash = createHash4("sha256").update(content).digest("hex");
@@ -8751,6 +8750,21 @@ async function syncFromManifest(registryRoot, projectRoot, manifest) {
8751
8750
  return stats;
8752
8751
  }
8753
8752
 
8753
+ // src/commands/shared-args.ts
8754
+ var NON_INTERACTIVE_ROOT_ARGS = {
8755
+ yes: {
8756
+ type: "boolean",
8757
+ alias: "y",
8758
+ description: "Skip interactive prompts; use defaults (IDE-agnostic non-interactive mode)",
8759
+ default: false
8760
+ },
8761
+ "force-root": {
8762
+ type: "boolean",
8763
+ description: "Bypass the ambiguous-root guard (use with caution)",
8764
+ default: false
8765
+ }
8766
+ };
8767
+
8754
8768
  // src/commands/install.ts
8755
8769
  function parsePackList(raw) {
8756
8770
  if (!raw?.trim()) return [];
@@ -8835,7 +8849,7 @@ async function printPostInstallSummary(result) {
8835
8849
  printInstallEpilogue(sync.env, { runtimeVersion: KIT_VERSION });
8836
8850
  }
8837
8851
  async function performInstall(options) {
8838
- const projectRoot = path39.resolve(options.cwd);
8852
+ const projectRoot = path40.resolve(options.cwd);
8839
8853
  const packs = parsePackList(options.pack);
8840
8854
  const existing = await loadAgentKitManifest(projectRoot);
8841
8855
  const registry = await resolveRegistryFromCli({
@@ -8912,17 +8926,7 @@ var installCommand = defineCommand11({
8912
8926
  type: "string",
8913
8927
  description: `Comma-separated L1 pack ids (e.g. cybersec,devops). Known: ${DOMAIN_PACK_IDS.join(", ")}`
8914
8928
  },
8915
- yes: {
8916
- type: "boolean",
8917
- alias: "y",
8918
- description: "Skip interactive prompts; use defaults (IDE-agnostic non-interactive mode)",
8919
- default: false
8920
- },
8921
- "force-root": {
8922
- type: "boolean",
8923
- description: "Bypass the ambiguous-root guard (use with caution)",
8924
- default: false
8925
- },
8929
+ ...NON_INTERACTIVE_ROOT_ARGS,
8926
8930
  claude: {
8927
8931
  type: "boolean",
8928
8932
  description: "Opt-in: generate .claude/commands/*.md thin pointer adapters for the installed .cursor/commands set, and merge a SessionStart context hook into .claude/settings.json (default install behavior is unchanged without this flag)",
@@ -9017,17 +9021,7 @@ var initCommand = defineCommand12({
9017
9021
  description: "Project root directory",
9018
9022
  default: process.cwd()
9019
9023
  },
9020
- yes: {
9021
- type: "boolean",
9022
- alias: "y",
9023
- description: "Skip interactive prompts; use defaults (IDE-agnostic non-interactive mode)",
9024
- default: false
9025
- },
9026
- "force-root": {
9027
- type: "boolean",
9028
- description: "Bypass the ambiguous-root guard (use with caution)",
9029
- default: false
9030
- }
9024
+ ...NON_INTERACTIVE_ROOT_ARGS
9031
9025
  },
9032
9026
  async run({ args }) {
9033
9027
  const nonInteractive = args.yes || isNonInteractive();
@@ -9561,8 +9555,8 @@ function renderMcLive(view, opts = {}) {
9561
9555
 
9562
9556
  // src/mission-control/snapshot.ts
9563
9557
  import { execFile as execFile6 } from "child_process";
9564
- import { access as access8 } from "fs/promises";
9565
- import path40 from "path";
9558
+ import { access as access7 } from "fs/promises";
9559
+ import path41 from "path";
9566
9560
 
9567
9561
  // ../../dashboard/lib/live-refresh.mjs
9568
9562
  import { join, resolve as resolve2 } from "path";
@@ -9594,8 +9588,8 @@ function dataScriptTimeoutMs(env = process.env) {
9594
9588
  async function firstExisting2(candidates) {
9595
9589
  for (const candidate2 of candidates) {
9596
9590
  try {
9597
- await access8(candidate2);
9598
- return path40.resolve(candidate2);
9591
+ await access7(candidate2);
9592
+ return path41.resolve(candidate2);
9599
9593
  } catch {
9600
9594
  }
9601
9595
  }
@@ -9604,9 +9598,9 @@ async function firstExisting2(candidates) {
9604
9598
  async function findDashboardDataScript(cwd, env = process.env, options = {}) {
9605
9599
  const startPath = await findDashboardStart(cwd, env, options);
9606
9600
  if (startPath) {
9607
- const sibling = path40.join(path40.dirname(startPath), "dashboard-data.mjs");
9601
+ const sibling = path41.join(path41.dirname(startPath), "dashboard-data.mjs");
9608
9602
  try {
9609
- await access8(sibling);
9603
+ await access7(sibling);
9610
9604
  return sibling;
9611
9605
  } catch {
9612
9606
  }
@@ -9619,7 +9613,7 @@ async function loadDashboardSnapshot(opts) {
9619
9613
  const env = opts.env ?? process.env;
9620
9614
  const timeout = dataScriptTimeoutMs(env);
9621
9615
  const run = opts.execFileFn ?? execFile6;
9622
- const snapshotRoot = path40.resolve(opts.snapshotRoot);
9616
+ const snapshotRoot = path41.resolve(opts.snapshotRoot);
9623
9617
  return new Promise((resolve3) => {
9624
9618
  run(
9625
9619
  process.execPath,
@@ -9942,13 +9936,13 @@ var missionControlCommand = defineCommand13({
9942
9936
  });
9943
9937
 
9944
9938
  // src/commands/monitors.ts
9945
- import path42 from "path";
9939
+ import path43 from "path";
9946
9940
  import { defineCommand as defineCommand14 } from "citty";
9947
9941
 
9948
9942
  // src/invariants/monitors-untriaged.ts
9949
9943
  import { execFile as execFile7 } from "child_process";
9950
9944
  import { readFile as readFile23, readdir as readdir10, stat as stat5 } from "fs/promises";
9951
- import path41 from "path";
9945
+ import path42 from "path";
9952
9946
  import { promisify as promisify6 } from "util";
9953
9947
 
9954
9948
  // src/invariants/triage-heading.ts
@@ -9989,7 +9983,7 @@ async function gitFreshMonitorNames(rootDir) {
9989
9983
  for (const line of stdout.split("\n")) {
9990
9984
  if (!line.trim()) continue;
9991
9985
  const file = line.slice(3).trim().replace(/^.* -> /, "");
9992
- const base = path41.basename(file);
9986
+ const base = path42.basename(file);
9993
9987
  if (base.startsWith("plan-monitor-") && base.endsWith(".md")) {
9994
9988
  names.add(base);
9995
9989
  }
@@ -10012,13 +10006,13 @@ function monitorSlugFromName(fileName) {
10012
10006
  return fileName.replace(/^plan-monitor-/, "").replace(/\.md$/, "").toLowerCase();
10013
10007
  }
10014
10008
  async function selectUntriagedMonitors(rootDir) {
10015
- const root = path41.resolve(rootDir);
10016
- const memoryDir = path41.join(root, ".cursor", "memory");
10009
+ const root = path42.resolve(rootDir);
10010
+ const memoryDir = path42.join(root, ".cursor", "memory");
10017
10011
  const allNames = await listMonitorFiles(memoryDir);
10018
10012
  const selectionOrder = ["git-fresh", "handoff-aligned", "untriaged-scan"];
10019
10013
  const byName = /* @__PURE__ */ new Map();
10020
10014
  for (const name of allNames) {
10021
- const abs = path41.join(memoryDir, name);
10015
+ const abs = path42.join(memoryDir, name);
10022
10016
  try {
10023
10017
  const [content, st] = await Promise.all([readFile23(abs, "utf8"), stat5(abs)]);
10024
10018
  byName.set(name, { content, mtimeMs: st.mtimeMs });
@@ -10033,7 +10027,7 @@ async function selectUntriagedMonitors(rootDir) {
10033
10027
  const gitFreshSet = [...gitFresh].filter(untriaged).sort();
10034
10028
  let handoff = "";
10035
10029
  try {
10036
- handoff = await readFile23(path41.join(root, ".cursor", "HANDOFF.md"), "utf8");
10030
+ handoff = await readFile23(path42.join(root, ".cursor", "HANDOFF.md"), "utf8");
10037
10031
  } catch {
10038
10032
  handoff = "";
10039
10033
  }
@@ -10053,8 +10047,8 @@ async function selectUntriagedMonitors(rootDir) {
10053
10047
  const row = byName.get(name);
10054
10048
  if (!row) continue;
10055
10049
  entries.push({
10056
- path: path41.join(memoryDir, name),
10057
- relativePath: path41.relative(root, path41.join(memoryDir, name)).split(path41.sep).join("/"),
10050
+ path: path42.join(memoryDir, name),
10051
+ relativePath: path42.relative(root, path42.join(memoryDir, name)).split(path42.sep).join("/"),
10058
10052
  mtimeMs: row.mtimeMs,
10059
10053
  hasTriageHeading: false,
10060
10054
  hasOpenGaps: hasOpenGaps(row.content),
@@ -10100,7 +10094,7 @@ var monitorsCommand = defineCommand14({
10100
10094
  process.exitCode = 2;
10101
10095
  return;
10102
10096
  }
10103
- const result = await selectUntriagedMonitors(path42.resolve(args.cwd));
10097
+ const result = await selectUntriagedMonitors(path43.resolve(args.cwd));
10104
10098
  if (args.json) {
10105
10099
  console.log(JSON.stringify(result, null, 2));
10106
10100
  return;
@@ -10116,7 +10110,7 @@ var monitorsCommand = defineCommand14({
10116
10110
  });
10117
10111
 
10118
10112
  // src/commands/plan-index.ts
10119
- import path43 from "path";
10113
+ import path44 from "path";
10120
10114
  import { defineCommand as defineCommand15 } from "citty";
10121
10115
  var planIndexCommand = defineCommand15({
10122
10116
  meta: {
@@ -10135,7 +10129,7 @@ var planIndexCommand = defineCommand15({
10135
10129
  }
10136
10130
  },
10137
10131
  async run({ args }) {
10138
- const root = path43.resolve(args.cwd);
10132
+ const root = path44.resolve(args.cwd);
10139
10133
  const index = await writePlanIndex(root);
10140
10134
  if (args.json) {
10141
10135
  console.log(JSON.stringify(index, null, 2));
@@ -10151,14 +10145,14 @@ var planIndexCommand = defineCommand15({
10151
10145
  });
10152
10146
 
10153
10147
  // src/commands/run-plan.ts
10154
- import path50 from "path";
10148
+ import path51 from "path";
10155
10149
  import { defineCommand as defineCommand16 } from "citty";
10156
10150
 
10157
10151
  // src/plan-loop/backends.ts
10158
10152
  import { execFileSync as execFileSync2, spawn as spawn7 } from "child_process";
10159
10153
  import { createWriteStream } from "fs";
10160
- import { access as access9 } from "fs/promises";
10161
- import path44 from "path";
10154
+ import { access as access8 } from "fs/promises";
10155
+ import path45 from "path";
10162
10156
  import { StringDecoder as StringDecoder3 } from "string_decoder";
10163
10157
 
10164
10158
  // src/plan-loop/hitl-relay.ts
@@ -11186,26 +11180,32 @@ function createRelay(hitl, child, redactedError) {
11186
11180
  }
11187
11181
  };
11188
11182
  }
11183
+ function cursorAgentHeadlessArgs(opts) {
11184
+ const args = [
11185
+ "-p",
11186
+ "--force",
11187
+ "--sandbox",
11188
+ "disabled",
11189
+ "--output-format",
11190
+ "stream-json",
11191
+ "--workspace",
11192
+ opts.workspace
11193
+ ];
11194
+ if (opts.model) args.push("--model", opts.model);
11195
+ args.push(opts.prompt);
11196
+ return args;
11197
+ }
11189
11198
  var cursorAgentBackend = {
11190
11199
  id: "cursor-agent",
11191
11200
  async resolve() {
11192
11201
  return whichBinary("cursor-agent");
11193
11202
  },
11194
11203
  async run(opts) {
11195
- const args = [
11196
- "-p",
11197
- "--force",
11198
- "--sandbox",
11199
- "disabled",
11200
- "--output-format",
11201
- "stream-json",
11202
- "--workspace",
11203
- opts.workspace
11204
- ];
11205
- if (opts.model) {
11206
- args.push("--model", opts.model);
11207
- }
11208
- args.push(opts.prompt);
11204
+ const args = cursorAgentHeadlessArgs({
11205
+ workspace: opts.workspace,
11206
+ prompt: opts.prompt,
11207
+ model: opts.model
11208
+ });
11209
11209
  const env = opts.env ? mergeChildEnv(opts.env) : void 0;
11210
11210
  return spawnLogged("cursor-agent", args, opts.logPath, {
11211
11211
  spawnFn: opts.spawnFn,
@@ -11292,9 +11292,9 @@ function claudeHeadlessArgs(opts = {}) {
11292
11292
  async function missingClaudeAdapter(workspace, prompt) {
11293
11293
  const m = /^\/([A-Za-z0-9][A-Za-z0-9_-]*)/.exec(prompt.trimStart());
11294
11294
  if (!m?.[1]) return null;
11295
- const rel = path44.join(".claude", "commands", `${m[1]}.md`);
11295
+ const rel = path45.join(".claude", "commands", `${m[1]}.md`);
11296
11296
  try {
11297
- await access9(path44.join(workspace, rel));
11297
+ await access8(path45.join(workspace, rel));
11298
11298
  return null;
11299
11299
  } catch {
11300
11300
  return rel;
@@ -11417,15 +11417,15 @@ async function detectAgentBackend(requested, whichFn = whichBinary) {
11417
11417
 
11418
11418
  // src/plan-loop/run-loop.ts
11419
11419
  import { mkdir as mkdir6, readFile as readFile27, rm as rm2, unlink as unlink2 } from "fs/promises";
11420
- import path49 from "path";
11420
+ import path50 from "path";
11421
11421
 
11422
11422
  // src/mission-control/live-run.ts
11423
11423
  import { readFile as readFile25 } from "fs/promises";
11424
- import path46 from "path";
11424
+ import path47 from "path";
11425
11425
 
11426
11426
  // src/plan-loop/plan-state.ts
11427
11427
  import { readFile as readFile24, readdir as readdir11 } from "fs/promises";
11428
- import path45 from "path";
11428
+ import path46 from "path";
11429
11429
  function countPendingTodos(raw) {
11430
11430
  const lines = raw.split(/\r?\n/);
11431
11431
  let inFront = 0;
@@ -11453,7 +11453,7 @@ function countPendingTodos(raw) {
11453
11453
  async function findActivePlanFile(plansDir) {
11454
11454
  if (!await fileExists(plansDir)) return null;
11455
11455
  const files = (await readdir11(plansDir)).filter((f) => f.endsWith(".plan.md")).sort();
11456
- return files[0] ? path45.join(plansDir, files[0]) : null;
11456
+ return files[0] ? path46.join(plansDir, files[0]) : null;
11457
11457
  }
11458
11458
  async function readPlan(planPath) {
11459
11459
  return readFile24(planPath, "utf8");
@@ -11641,7 +11641,7 @@ function firstWord(raw) {
11641
11641
  }
11642
11642
  async function readLiveMission(root, io = defaultMissionIo()) {
11643
11643
  const mission = emptyLiveMission();
11644
- const handoff = await io.readFile(path46.join(root, ".cursor", "HANDOFF.md")) ?? "";
11644
+ const handoff = await io.readFile(path47.join(root, ".cursor", "HANDOFF.md")) ?? "";
11645
11645
  const named = extractHandoffNamedPlans(handoff);
11646
11646
  mission.mode = firstWord(MODE_RE.exec(handoff)?.[1]);
11647
11647
  let cursorIndex = null;
@@ -11677,13 +11677,13 @@ async function readLiveMission(root, io = defaultMissionIo()) {
11677
11677
  state: finished || cursor !== null && i < cursor ? "done" : i === cursor ? "current" : "pending"
11678
11678
  }));
11679
11679
  }
11680
- const plansDir = path46.join(root, ".cursor", "plans");
11680
+ const plansDir = path47.join(root, ".cursor", "plans");
11681
11681
  const planBase = (mission.queue && !finished ? mission.queue.current : null) ?? named.active;
11682
- const planPath = planBase ? path46.join(plansDir, planBase) : await io.findActivePlan(plansDir);
11682
+ const planPath = planBase ? path47.join(plansDir, planBase) : await io.findActivePlan(plansDir);
11683
11683
  if (planPath) {
11684
11684
  const raw = await io.readFile(planPath);
11685
11685
  if (raw !== null) {
11686
- mission.planFile = path46.basename(planPath);
11686
+ mission.planFile = path47.basename(planPath);
11687
11687
  const { todos } = parsePlanFrontmatter2(raw);
11688
11688
  const open = todos.filter((t) => t.status !== "cancelled");
11689
11689
  mission.todos = {
@@ -11734,7 +11734,7 @@ async function withLiveTui(opts, run) {
11734
11734
  columns,
11735
11735
  now: now2,
11736
11736
  backend: opts.backend,
11737
- logPath: path46.relative(opts.root, opts.logPath) || opts.logPath
11737
+ logPath: path47.relative(opts.root, opts.logPath) || opts.logPath
11738
11738
  });
11739
11739
  opts.onFeed?.(feed);
11740
11740
  const missionLoader = opts.missionLoader ?? createLiveMissionLoader(opts.root, { now: now2 });
@@ -11871,9 +11871,9 @@ async function withLiveTui(opts, run) {
11871
11871
 
11872
11872
  // src/plan-loop/external-review.ts
11873
11873
  import { spawn as spawn8 } from "child_process";
11874
- import path47 from "path";
11875
- var CANONICAL_REL = path47.join(".cursor", "scripts", "plan-external-review.sh");
11876
- var FALLBACK_REL = path47.join("scripts", "plan-external-review.sh");
11874
+ import path48 from "path";
11875
+ var CANONICAL_REL = path48.join(".cursor", "scripts", "plan-external-review.sh");
11876
+ var FALLBACK_REL = path48.join("scripts", "plan-external-review.sh");
11877
11877
  function isPlanExhaustedReason(reason) {
11878
11878
  const r = reason.trim().toLowerCase();
11879
11879
  if (!r) return false;
@@ -11895,8 +11895,8 @@ async function armExternalPlanReview(root, options = {}) {
11895
11895
  const existsFn = options.existsFn ?? fileExists;
11896
11896
  const log = options.log ?? ((line) => console.log(line));
11897
11897
  const force = options.force === true;
11898
- const canonicalPath = path47.join(root, CANONICAL_REL);
11899
- const fallbackPath = path47.join(root, FALLBACK_REL);
11898
+ const canonicalPath = path48.join(root, CANONICAL_REL);
11899
+ const fallbackPath = path48.join(root, FALLBACK_REL);
11900
11900
  let scriptPath = null;
11901
11901
  let scriptRel = CANONICAL_REL;
11902
11902
  if (await existsFn(canonicalPath)) {
@@ -11949,7 +11949,7 @@ async function armExternalPlanReview(root, options = {}) {
11949
11949
  }
11950
11950
 
11951
11951
  // src/plan-loop/persona-banners.ts
11952
- import path48 from "path";
11952
+ import path49 from "path";
11953
11953
  import {
11954
11954
  blue,
11955
11955
  cyan as cyan4,
@@ -11985,7 +11985,7 @@ function resolveColor(name, fallback) {
11985
11985
  async function resolveCliPersonaId(root) {
11986
11986
  try {
11987
11987
  const cfg = await readJson(
11988
- path48.join(root, ".cursor", "context", "config.json")
11988
+ path49.join(root, ".cursor", "context", "config.json")
11989
11989
  );
11990
11990
  const modes = cfg?.agentPersona?.modes ?? cfg?.workspaceSkin?.modes;
11991
11991
  const id = modes?.[CLI_RUN_PLAN_MODE];
@@ -11996,7 +11996,7 @@ async function resolveCliPersonaId(root) {
11996
11996
  }
11997
11997
  async function loadPersonaPack(root, personaId) {
11998
11998
  try {
11999
- const personaPath = path48.join(root, "registry", "personas", "core", personaId, "persona.json");
11999
+ const personaPath = path49.join(root, "registry", "personas", "core", personaId, "persona.json");
12000
12000
  const pack = await readJson(personaPath);
12001
12001
  if (!pack || typeof pack.id !== "string") return null;
12002
12002
  return pack;
@@ -12175,9 +12175,9 @@ function sleep(ms) {
12175
12175
  return new Promise((r) => setTimeout(r, ms));
12176
12176
  }
12177
12177
  async function runPlanLoop(opts) {
12178
- const plansDir = path49.join(opts.root, ".cursor", "plans");
12179
- const stopFile = path49.join(opts.root, ".cursor", "loop.stop");
12180
- const logDir = path49.join(opts.root, ".cursor", "loop-logs");
12178
+ const plansDir = path50.join(opts.root, ".cursor", "plans");
12179
+ const stopFile = path50.join(opts.root, ".cursor", "loop.stop");
12180
+ const logDir = path50.join(opts.root, ".cursor", "loop-logs");
12181
12181
  const planPath = await findActivePlanFile(plansDir);
12182
12182
  if (!planPath) {
12183
12183
  logger.error("No active plan in .cursor/plans/");
@@ -12198,7 +12198,7 @@ async function runPlanLoop(opts) {
12198
12198
  try {
12199
12199
  const persona = await loadCliRunPlanPersona(opts.root);
12200
12200
  const banners = createPersonaBannerPrinter(persona);
12201
- console.log(`Active plan: ${path49.basename(planPath)}`);
12201
+ console.log(`Active plan: ${path50.basename(planPath)}`);
12202
12202
  console.log(`Pending to-dos: ${await pending()} | max ticks: ${opts.maxTicks}`);
12203
12203
  console.log(`Backend: ${opts.backend.id}`);
12204
12204
  if (persona) {
@@ -12242,8 +12242,8 @@ async function runPlanLoop(opts) {
12242
12242
  planExhausted = true;
12243
12243
  break;
12244
12244
  }
12245
- const logPath = path49.join(logDir, `tick-${stamp()}.log`);
12246
- const relLog = path49.relative(opts.root, logPath);
12245
+ const logPath = path50.join(logDir, `tick-${stamp()}.log`);
12246
+ const relLog = path50.relative(opts.root, logPath);
12247
12247
  console.log("");
12248
12248
  const tickLine = `=== tick ${tick}/${opts.maxTicks} - pending: ${before} - log: ${relLog} ===`;
12249
12249
  if (banners) banners.tickStart(tickLine);
@@ -12351,7 +12351,7 @@ async function runPlanLoop(opts) {
12351
12351
  const finishDetail = `after ${tick} tick(s); pending: ${pendingNow}`;
12352
12352
  if (banners) banners.phaseComplete(finishDetail);
12353
12353
  console.log(
12354
- `Loop finished after ${tick} tick(s). Pending now: ${pendingNow}. Logs in ${path49.relative(opts.root, logDir)}/`
12354
+ `Loop finished after ${tick} tick(s). Pending now: ${pendingNow}. Logs in ${path50.relative(opts.root, logDir)}/`
12355
12355
  );
12356
12356
  if (planExhausted || shouldArmExternalPlanReview({ pending: pendingNow, stopReason })) {
12357
12357
  await armExternalPlanReview(opts.root);
@@ -12443,7 +12443,7 @@ var runPlanCommand = defineCommand16({
12443
12443
  return;
12444
12444
  }
12445
12445
  const code = await runPlanLoop({
12446
- root: path50.resolve(args.cwd),
12446
+ root: path51.resolve(args.cwd),
12447
12447
  maxTicks,
12448
12448
  sleepSeconds,
12449
12449
  model: args.model ? String(args.model) : void 0,
@@ -12458,12 +12458,12 @@ var runPlanCommand = defineCommand16({
12458
12458
 
12459
12459
  // src/commands/run.ts
12460
12460
  import { homedir as homedir4 } from "os";
12461
- import path52 from "path";
12461
+ import path53 from "path";
12462
12462
  import { defineCommand as defineCommand17 } from "citty";
12463
12463
 
12464
12464
  // src/plan-loop/dispatch.ts
12465
12465
  import { mkdir as mkdir7, readFile as readFile28 } from "fs/promises";
12466
- import path51 from "path";
12466
+ import path52 from "path";
12467
12467
  var RUN_CATALOG = [
12468
12468
  "run-plan",
12469
12469
  "run-plan-all",
@@ -12477,7 +12477,7 @@ var RUN_CATALOG = [
12477
12477
  "kit-staging"
12478
12478
  ];
12479
12479
  var FIRST_CLASS_SLASH_COMMANDS = ["run-plan", "run-plan-all"];
12480
- var RUN_PROMOTE_BLOCKED = ["git-prod", "kit-prod"];
12480
+ var RUN_PROMOTE_BLOCKED = RESERVED_GATE_IDS;
12481
12481
  function normalizeSlashName(raw) {
12482
12482
  return raw.replace(/^\//, "").replace(/\.md$/i, "").trim();
12483
12483
  }
@@ -12507,7 +12507,7 @@ function rewriteRootArgvToRun(argv) {
12507
12507
  return next;
12508
12508
  }
12509
12509
  function commandMarkdownPath(root, name) {
12510
- return path51.join(root, ".cursor", "commands", `${normalizeSlashName(name)}.md`);
12510
+ return path52.join(root, ".cursor", "commands", `${normalizeSlashName(name)}.md`);
12511
12511
  }
12512
12512
  function buildDispatchPrompt(commandBody) {
12513
12513
  return [
@@ -12541,19 +12541,7 @@ function stamp2() {
12541
12541
  return `${d.getFullYear()}${p(d.getMonth() + 1)}${p(d.getDate())}-${p(d.getHours())}${p(d.getMinutes())}${p(d.getSeconds())}${p(d.getMilliseconds(), 3)}`;
12542
12542
  }
12543
12543
  function cursorAgentDispatchArgs(opts) {
12544
- const args = [
12545
- "-p",
12546
- "--force",
12547
- "--sandbox",
12548
- "disabled",
12549
- "--output-format",
12550
- "stream-json",
12551
- "--workspace",
12552
- opts.workspace
12553
- ];
12554
- if (opts.model) args.push("--model", opts.model);
12555
- args.push(opts.prompt);
12556
- return args;
12544
+ return cursorAgentHeadlessArgs(opts);
12557
12545
  }
12558
12546
  function claudeDispatchArgs(opts = {}) {
12559
12547
  return claudeHeadlessArgs(opts);
@@ -12607,20 +12595,20 @@ async function runHeadlessDispatch(opts) {
12607
12595
  });
12608
12596
  }
12609
12597
  async function ensureDispatchLogPath(root) {
12610
- const logDir = path51.join(root, ".cursor", "loop-logs");
12598
+ const logDir = path52.join(root, ".cursor", "loop-logs");
12611
12599
  await mkdir7(logDir, { recursive: true });
12612
- return path51.join(logDir, `run-${stamp2()}.log`);
12600
+ return path52.join(logDir, `run-${stamp2()}.log`);
12613
12601
  }
12614
12602
 
12615
12603
  // src/commands/run.ts
12616
12604
  function displayPath(target, home = homedir4()) {
12617
- const resolvedHome = path52.resolve(home);
12605
+ const resolvedHome = path53.resolve(home);
12618
12606
  if (target === resolvedHome) return "~";
12619
- const prefix = resolvedHome.endsWith(path52.sep) ? resolvedHome : `${resolvedHome}${path52.sep}`;
12620
- return target.startsWith(prefix) ? `~${path52.sep}${target.slice(prefix.length)}` : target;
12607
+ const prefix = resolvedHome.endsWith(path53.sep) ? resolvedHome : `${resolvedHome}${path53.sep}`;
12608
+ return target.startsWith(prefix) ? `~${path53.sep}${target.slice(prefix.length)}` : target;
12621
12609
  }
12622
12610
  async function executeRun(input) {
12623
- const root = path52.resolve(input.cwd);
12611
+ const root = path53.resolve(input.cwd);
12624
12612
  const slash = normalizeSlashName(input.slash);
12625
12613
  if (!slash) {
12626
12614
  return { exitCode: 1, error: "Provide a slash: agent-kit run <slash>" };
@@ -12671,7 +12659,7 @@ async function executeRun(input) {
12671
12659
  const stdout = [
12672
12660
  "--dry-run: no agent will be started.",
12673
12661
  `Slash: ${slash}`,
12674
- `Command file: ${path52.relative(root, file.path)}`,
12662
+ `Command file: ${path53.relative(root, file.path)}`,
12675
12663
  `Backend: ${detected.id} (${displayPath(detected.bin)})`,
12676
12664
  "Prompt:",
12677
12665
  prompt
@@ -12680,8 +12668,8 @@ async function executeRun(input) {
12680
12668
  }
12681
12669
  const logPath = await ensureDispatchLogPath(root);
12682
12670
  console.log(`Dispatch: ${slash} via ${detected.id}`);
12683
- console.log(`Command file: ${path52.relative(root, file.path)}`);
12684
- console.log(`Log: ${path52.relative(root, logPath)}`);
12671
+ console.log(`Command file: ${path53.relative(root, file.path)}`);
12672
+ console.log(`Log: ${path53.relative(root, logPath)}`);
12685
12673
  try {
12686
12674
  const dispatch = {
12687
12675
  backendId: detected.id,
@@ -12838,7 +12826,7 @@ var scanCommand = defineCommand18({
12838
12826
  import { spawn as spawn9 } from "child_process";
12839
12827
  import { appendFile as appendFile2, mkdir as mkdir8, readFile as readFile29, writeFile as writeFile13 } from "fs/promises";
12840
12828
  import { homedir as homedir5 } from "os";
12841
- import path53 from "path";
12829
+ import path54 from "path";
12842
12830
  import { confirm as confirm2, isCancel as isCancel2 } from "@clack/prompts";
12843
12831
  import { defineCommand as defineCommand19 } from "citty";
12844
12832
  import { cyan as cyan5, green as green4, yellow as yellow3 } from "kolorist";
@@ -12847,9 +12835,9 @@ var SETUP_GLOBAL_MARKER = "# agent-kit setup-global";
12847
12835
  function planSetupGlobalSteps(env, options = {}) {
12848
12836
  const homeDir = options.homeDir ?? homedir5();
12849
12837
  const packageSpec = options.packageSpec ?? pinnedCliSpec();
12850
- const npmGlobalDir = path53.join(homeDir, NPM_GLOBAL_DIR_NAME);
12851
- const npmGlobalBin = path53.join(npmGlobalDir, "bin");
12852
- const npmrcPath = path53.join(homeDir, ".npmrc");
12838
+ const npmGlobalDir = path54.join(homeDir, NPM_GLOBAL_DIR_NAME);
12839
+ const npmGlobalBin = path54.join(npmGlobalDir, "bin");
12840
+ const npmrcPath = path54.join(homeDir, ".npmrc");
12853
12841
  const npmrcPrefixValue = `~/${NPM_GLOBAL_DIR_NAME}`;
12854
12842
  const pathExportLine = `export PATH="${npmGlobalBin}:$PATH"`;
12855
12843
  const shellProfile = env.shellProfile;
@@ -13148,7 +13136,7 @@ var setupGlobalCommand = defineCommand19({
13148
13136
  });
13149
13137
 
13150
13138
  // src/commands/status.ts
13151
- import path54 from "path";
13139
+ import path55 from "path";
13152
13140
  import { defineCommand as defineCommand20 } from "citty";
13153
13141
  function profileStatus(profile) {
13154
13142
  if (!profile) return { origin: "none", evidence: [], profile: null };
@@ -13179,11 +13167,11 @@ var statusCommand = defineCommand20({
13179
13167
  }
13180
13168
  },
13181
13169
  async run({ args }) {
13182
- const rootDir = path54.resolve(args.cwd);
13170
+ const rootDir = path55.resolve(args.cwd);
13183
13171
  const [manifest, rawProfile, scan, env] = await Promise.all([
13184
13172
  loadAgentKitManifest(rootDir),
13185
13173
  readJson(
13186
- path54.join(rootDir, ".cursor", "agent-kit.config.json")
13174
+ path55.join(rootDir, ".cursor", "agent-kit.config.json")
13187
13175
  ),
13188
13176
  runScanner(rootDir),
13189
13177
  assessEnvironment()
@@ -13291,17 +13279,7 @@ var updateCommand = defineCommand21({
13291
13279
  description: "Apply even when this CLI is older than the registry it is syncing from; the manifest is then stamped with this CLI's version, not the registry's (factory/dev only)",
13292
13280
  default: false
13293
13281
  },
13294
- yes: {
13295
- type: "boolean",
13296
- alias: "y",
13297
- description: "Skip interactive prompts; use defaults (IDE-agnostic non-interactive mode)",
13298
- default: false
13299
- },
13300
- "force-root": {
13301
- type: "boolean",
13302
- description: "Bypass the ambiguous-root guard (use with caution)",
13303
- default: false
13304
- },
13282
+ ...NON_INTERACTIVE_ROOT_ARGS,
13305
13283
  ...REGISTRY_CLI_ARGS
13306
13284
  },
13307
13285
  async run({ args }) {
@@ -13429,7 +13407,7 @@ ${err.recovery}
13429
13407
 
13430
13408
  // src/commands/validate.ts
13431
13409
  import { readFile as readFile30 } from "fs/promises";
13432
- import path55 from "path";
13410
+ import path56 from "path";
13433
13411
  import { defineCommand as defineCommand22 } from "citty";
13434
13412
 
13435
13413
  // src/invariants/plan-schema.ts
@@ -13476,7 +13454,7 @@ function validatePlanFrontmatterText(text) {
13476
13454
  // src/commands/validate.ts
13477
13455
  async function resolveEditedPath(cwd, explicit) {
13478
13456
  if (explicit) {
13479
- const filePath2 = path55.resolve(cwd, explicit);
13457
+ const filePath2 = path56.resolve(cwd, explicit);
13480
13458
  try {
13481
13459
  return { filePath: filePath2, content: await readFile30(filePath2, "utf8") };
13482
13460
  } catch {
@@ -13486,7 +13464,7 @@ async function resolveEditedPath(cwd, explicit) {
13486
13464
  const payload = await readStdinJson();
13487
13465
  const rel = typeof payload.file_path === "string" && payload.file_path || typeof payload.path === "string" && payload.path || typeof payload.file === "string" && payload.file || "";
13488
13466
  if (!rel) return null;
13489
- const filePath = path55.isAbsolute(rel) ? rel : path55.resolve(cwd, rel);
13467
+ const filePath = path56.isAbsolute(rel) ? rel : path56.resolve(cwd, rel);
13490
13468
  try {
13491
13469
  return { filePath, content: await readFile30(filePath, "utf8") };
13492
13470
  } catch {
@@ -13516,7 +13494,7 @@ var validateCommand = defineCommand22({
13516
13494
  async run({ args }) {
13517
13495
  const cwd = typeof args.cwd === "string" ? args.cwd : process.cwd();
13518
13496
  const fileArg = typeof args.file === "string" ? args.file : void 0;
13519
- const filePath = fileArg ? path55.resolve(cwd, fileArg) : path55.join(path55.resolve(cwd), ".cursor", "HANDOFF.md");
13497
+ const filePath = fileArg ? path56.resolve(cwd, fileArg) : path56.join(path56.resolve(cwd), ".cursor", "HANDOFF.md");
13520
13498
  let content = "";
13521
13499
  try {
13522
13500
  content = await readFile30(filePath, "utf8");
@@ -13549,7 +13527,7 @@ var validateCommand = defineCommand22({
13549
13527
  process.exitCode = 2;
13550
13528
  return;
13551
13529
  }
13552
- const filePath = path55.resolve(cwd, fileArg);
13530
+ const filePath = path56.resolve(cwd, fileArg);
13553
13531
  let content = "";
13554
13532
  try {
13555
13533
  content = await readFile30(filePath, "utf8");
@@ -13571,7 +13549,7 @@ var validateCommand = defineCommand22({
13571
13549
  },
13572
13550
  async run({ args }) {
13573
13551
  const cwd = typeof args.cwd === "string" ? args.cwd : process.cwd();
13574
- const resolved = await resolveEditedPath(path55.resolve(cwd));
13552
+ const resolved = await resolveEditedPath(path56.resolve(cwd));
13575
13553
  if (!resolved) {
13576
13554
  console.log(JSON.stringify({}));
13577
13555
  return;