@dadado/agent-kit-cli 4.8.9 → 5.1.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
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { defineCommand as defineCommand19, runMain } from "citty";
4
+ import { defineCommand as defineCommand19, runMain, showUsage } from "citty";
5
5
 
6
6
  // src/commands/add.ts
7
7
  import { defineCommand } from "citty";
@@ -69,53 +69,132 @@ import path3 from "path";
69
69
 
70
70
  // src/lifecycle/overlay-known-hashes.ts
71
71
  var KNOWN_SHIPPED_OVERLAY_HASHES = /* @__PURE__ */ new Set([
72
+ "004d98f557942677264d053dcd8335f2bbcf19c5591123ec16a7a2697cb88932",
73
+ "05ee6ecde4215e33ace0ee152ffbf2cd885d2bd4b032900f0f0bb001d350ef0f",
72
74
  "082bdbc584be3f3d8aa6e9d7b9f4e076450f3ff48f9147a1deb557e31475036b",
75
+ "09850afc201eccb3e700cb934400d49c9cbe7510d4a546ee84ee519771a8200e",
76
+ "0b5fe4729c3c68654434837630d775b847c082418f25b0b2c581700fe84b797a",
77
+ "1106b279558b6a20f87951102f172a680976f3e6530855d5dad083e1f89b9ba3",
78
+ "12bed56b332406477cf546d9e8360d3cab129bd82a638bea356726d00a6847a3",
79
+ "13f6a0711a2fcadcc7f1b751772448517ce4871cdef6a0269fc26dad51ebe26d",
73
80
  "15566765ec95dad3bc3160f20ac910e01a13471df536e6d990f4b263700784a9",
74
81
  "1681e6cf7b80a67cd999b94b8b6e16eb8caed0e11b32905848ba733da956e902",
82
+ "17818e47e06349ee35c1d8e1cc16e8bcbbbcc89c664014e9096e1f08c8567807",
75
83
  "1974a5af0e4ba2bb0fc7e1872ef9ab70e323323ab216958c31731bcdd82c8241",
84
+ "1b384771cc6228215e31e6a9ab0943b13a5cdc463354a2641f1c0d85b4357e84",
85
+ "1b5f8c4197393dc9110d020f3b619b8502862adeb6522a6b9be4b49dd8794612",
86
+ "1c5c1af87951367ad04bb6fa5b1d6ebaaefbc0ee95989b624cabf406a1869907",
87
+ "1ded5c7b7eefc355fc1d17feb65ec472e35ed96076e47ce95f7bff5c1b84746c",
76
88
  "1e58e0d4f2e8459b95a40d1b1e72c5c85a87230cbde945840a84073fe3a20422",
89
+ "2280e3ca96129ab2de8a85a301df7c2b15ffaa6204b734ed3b1f303e0055c41d",
77
90
  "285668d08f1b15d96286d27e29cc4a67e126e097fb8a940bdde33ccf25bd2bcd",
91
+ "2acdb148b07f3a4872b896da419af796fd3fefaace7ac79cdee0aab447004a83",
92
+ "2cd7fc018a384b0188737259ecf593ce25b4b75cc38b3e410063ac91745894b9",
93
+ "2f6cedfa61cb62126efd6b845bbf4d8d7913e39223d5f799ac09ef64c0489824",
94
+ "3204a7449f3031916a8d57b37144e5d8ded6c94aa5659493fa5cc2c7321f4eab",
95
+ "33982aea21a074d21a638850475ce1fefa7d043ec035de3855889cc68d60cf35",
96
+ "38675aec695e9d4e7f16e8fbe3facc3280f47fb875bf9ac50bd6837b92b7a7fa",
97
+ "3ee94e2441218025c085856679d17b4a4fe7a6e3d0274423abe20a422a5ab05d",
78
98
  "3f3354825ff06ff9f9ae1e29b71784fef4250020c23c1b490940cf06b2be0f28",
99
+ "46a1dd4a07a2190ab9fb09abc05a3962277209f8e0c28033bb41450301474c86",
100
+ "48a0d55e21132a305946b195db7f6b75da78d44a1d32166fb0c69bef4a5ffbe2",
101
+ "4b62a46c74e5fa6a80283f21e8e6711958fbcd577051dc73ff4050643a031475",
102
+ "4ba6e319ac79455a7d2713674f64fabbc9cd401b5d26b1c0edbd19c27a52297e",
79
103
  "4bf23acbc9bd4e0f8468e61f4970e182600b72d9025d29e81ddbd83cb7b1710a",
80
104
  "4c5167d0ebde685f266dce32c516a7c43876e0520647b93f07f2e2147e6a7b2f",
81
105
  "4dc4efcd1b43c643d3889fb349df8d4964d0867c3a67eb67e2ec168fbb70fc75",
106
+ "4f133554d01cedc62280006f4cf6453e25a5cb41ef6dde4a43f9508cb36ef75d",
107
+ "4fe1e2bce8bd55a6decdd62c887a423cf85148b692f313aad4c777d0e50596b3",
82
108
  "4ff844cf789cef4b1e809a55ca006316cd964bcdb2557d91c916a08e30ad15f6",
83
109
  "533b41599c25698e5746166096a68fd4080efb5e16287b2a1ed5f73295cda446",
110
+ "54dda8addb74bc187ee23c31f8b7d17ad19e745a45169d29ffea094860eb18f2",
84
111
  "570c3f91fd31a4cc1e56e997c85feab5c6756f45fc73dc741afe573912111231",
112
+ "5cb36f5dab846b2551afd05eab92c14122853c147d7f02136d1c04e01e3c9def",
85
113
  "5d302d207521c53b9fa814ee0eba1376a5b2bfd2983c0373657c9fdf9884541b",
114
+ "5f5c125f7edf280029f214589ce3e088fc74bad717e30178405a3b537673c42f",
86
115
  "621bc6cecf7c710a97e93463ca9b8872a1fcab1325c358cc8be65b418eebb339",
116
+ "693c732658e31d6403cf59e85985518f24ab92bf7fcdc3c225e9a79e54597a43",
87
117
  "69715048da3963947aa8a39e7ac30ec01632fec862bfcb9559c46764b4833be0",
118
+ "6994595e3a62edb6c518006a6bece6848c7077977c041439cf7c822b89304a14",
88
119
  "6a42e76fc038ca69ddad18541b8df161770c58980fefaf9e5e80d87407074bec",
89
120
  "6a9af9e8a95dbb279347166c43dcee245ca673f9687841086b49911c54944c26",
121
+ "99fdd85310e658a1c1b0e8f92257493409c28c3d693f24deeaa665861788e1e4",
122
+ // public registry SoT n8n-workflows/SKILL.md (em dash description)
90
123
  "6b66ed7747ea2d19cb457f5af656e3c07f9f563f7f32b3951fa4ae5f27f0ac46",
124
+ "6dede0588274f5b44d833bcc696ec4776eb978ed24a20efdcc9c38caf3df1576",
125
+ "71991a10972b8b956cae93c88517e31741c95e351814082df65e44f813590210",
126
+ "75ae3018a70ed838428c0e63e8b6901ac05bf1f1cf1845faa6f73c3940d22e4a",
127
+ "75e591bfde61ca1ca3ca1303cc9bc487197af2f713c68f346a46e94ca9432fd5",
91
128
  "76ae85bbc336c6f435d9653e8f254ae49774b4ebaecf7335697115933eb8797c",
129
+ "76c4361e70a73276771e841e2c939253b4124d690a0fd25aff2a3ac636d8f4b0",
130
+ // mission-kit-comms/SKILL.md
131
+ "79878f0f6f59d1a0b65d94011c0cafe7b6f8e03e89f568aa638b8ba7a2c34076",
92
132
  "8108b202c829627c3c2df33575016ebe942df42acf4f1cc393900be3b82b402c",
133
+ "819b7bd3ba9e8bf12dcd4f81a76c98e48261b34c397f339b36d36eb364d9c3f4",
93
134
  "8619500b0c7024390d43e3aaefa2a2d731728d7c664aaf90e0acdd067f3397ce",
135
+ "875b0a8805280b2a2df43f2c42f9063fe0623a34fa19c3aa5d2ec48211f3ebae",
94
136
  "8a11c80b0fbf6c4aabf60eec05b8cca7dccf5bca472787a8afa3028fd8fbedfa",
95
137
  "8b25659727063013e8bf6d7925f815b2630c2919b21e6fdac354362bf122bfab",
96
138
  "8c7edbacc74b1431fbecb04c9857c0e7a9cb1578fc6ca6367be888dbcec3c98f",
139
+ "8d8d60e929ebf717eed3c1a29ee0de7c71ae917d125eb1b220a77220597281ed",
140
+ "8ffc181bb152c68a3353309bfdc87c42bdb925b98675cdba959c0cdb90b2adea",
141
+ "91ce0c933e3c8ee81927b8224f944ec5ce5445098a2ad154773092e3541dbf69",
142
+ "9309af07a0a99a56143386b3ddd2a35fae29378fba3f6b4c9297a5d1c02a9724",
97
143
  "9439da963acbe5dd88a3aa4d6ba4fb297db666ce8f2c9d3917dbc6d6e517f9aa",
144
+ "989e7e1bdfb29dedf850405f28648810d008885196efe7ae38c6e7554c7ce45d",
98
145
  "98d96e25f67aa9c234cb0c422f0d701c22a04bfe184fb4eb68295003d429552b",
99
146
  "99031145609af46e7e0f315006bf633f47002eccece8eb7af8b5efd762313ade",
100
147
  "9a3cea681399b42ef930a0720802378fe1b06ee73370df9ede9d0309c3c60ab2",
148
+ "9ccf52fe53c6fa4d731650ab4928ce50e201fa34f3e174fee70e3d29867fef2a",
101
149
  "9d905aca0123f3018c81c579217104f1749d8da802f3837b2407fbad8f2d803f",
150
+ "9e324c52c1a9d1ceee738bccf47da92910aca9f7a5a21cc41738d9573c414830",
151
+ "9eb457654167df562481637b25ecd91f0ff3c9282ba79409089d644d3b09b926",
152
+ "a013a6a476160183d6457ff6c1c51ec228bdca359b11eb15cd9b7fc177a705db",
102
153
  "a08d04f8efa121ad498f96873778c22630c9172c07c216839e348cd0bb7c2292",
154
+ "a7cd7058bcb6fbd962c37317fa16893bc86a270d0164af5a0e6f3f026aa408ad",
155
+ "a833a7131cdfc003b45a48cb26c6ce33dc218c1814156047db5211a79192e0a0",
156
+ "ac2f1f7d9e68f9c98fc4fe5ca348b1a90cf484141bbd116db871d63bdcc3c7c3",
103
157
  "b5c7a10df90d209be07159b7ff9bfa24e869538a0b6026bb5ce79e1cdd5b2528",
158
+ "bd9fd1efacbd132c5dafc12cd0cf2b651a2b1fab7b2d7a9ba4a3e44128097f48",
159
+ "be964ff33a40280e13ce6071fb3e30ce87c374527efbb74c25c9d4534084a634",
160
+ "c245035b539e41e822fab8c74fcde3b18c45f4b9f26f1c8583fc9e7254249698",
161
+ "c794accc6668d74a739c982c1b63985a8be4be08afe3766214ab1e08d25ea95c",
104
162
  "c96d522be440fbb638dbe953cebf1bb31185c431ac2e14446369cfb7d155a951",
105
163
  "cf2bd11891b934484171fa705f470de284e3874402e1e487750735dbc7692857",
164
+ "d0e3bf82efbe32c09761689802ffea7838046dbdd4803b88ead5ec3dfc7efb7e",
106
165
  "d4659e4bb6fd6022559fc632b17db6a39007c04bc0ded59017a0dacc013632ed",
107
166
  "d484243167e13c34f9df7ff378baf2967f7cbfcd88170d4d32e745efbcae7480",
167
+ "d67ac8fa1edec6e50c82aeca064335e799e408d0e4bbc95e1407cf7463a61de3",
168
+ "d7ad53f06a41bcb2f2b998fd4a4c99209a0cf287cf0d0d4acbcc36c73dd46e4a",
108
169
  "d96aa15dde45147aa40db64684688a4b5c9ad660dcd16c55156b31933a42c37e",
109
170
  "d9ecd90f80ebc25db73ebed27c32596b9cfce518b3026a3a4d5787321ba6324a",
110
171
  "dd01d051dbab257e16609b02c983bad6a576ed8473781b4f1df9ef2eb5f85795",
172
+ "dd9ff2ff8aa7bcb2cdfc59092ad73227631c1c3619b495131f7faee65224cb17",
111
173
  "de6efabde6a1d69357926b88b5a4ed3eec19e11b82597cb999f5365f952305b8",
174
+ "debc0758b3e39294df85ccb7eab3455e93a2b8c608602d309a7b0ebdb6618805",
112
175
  "e03b5764b908f9ed5c592108fbc11d5962d9256fd129a63ab4a39c8efaa2ef3e",
113
176
  "e102e77f9f520f938d26861978249e4d85a8c8755d48c8e79b21fef9423325ee",
177
+ "e2d89f780c6ae5393dc05818f9e3e9a8bf6629eac05702eea8f9a25b578e3ff1",
178
+ "e2f1e9108fd450817ab42570db656b1e43b20b5ba703b14027ce540e1b8fe502",
179
+ "e37c62695b5c70451ef1a0c90b31b73a9c442ebc55fb3fc3f79c294b993b99ea",
180
+ "e3c32aec17e8f728cbd13162a123a9debffb8bbcec527a00b1c2df30a1a20080",
114
181
  "e511e04064ffb8c429b659d2ae2e0f223e8cc51039ae816753095a5f7a160595",
182
+ "e5682d4696e874d09f12a1d65891fee5971201a29c654c08f5d3b90fb9a6a0b7",
183
+ "e57437bf790fda1d43b895e2b5b0a7d2d57635c319ac0e2439ba2222a825bcde",
184
+ "e8bb214c3e551c44f379db13c5962ef20698b505b0227d283d3ae632e1bc3626",
185
+ "ea36e74c9d592980cba5c08d04ee70fc59a6cd01e87a50bbfae4f668557df4b4",
115
186
  "ebd41f8df764a14b3460016fafcfdde98c4c3a59a3c17f773451ef061fc42f95",
187
+ "ef181a4e2bd9f82f45628637b8d66925ffa20801558808f1b03ea72b7cb82a72",
116
188
  "f061432a2606049717c0cbf098ef07b1da4df2b136c4cb9cdaa7ea44c876bcd6",
117
189
  "f061d36a1180293519fbb560bbf659c621bd90885e995fca6eaf2eda9e418a50",
118
- "f28731a82f4b394a5765fdefe2d1b667810831f6dcea400ca62bc66e1d65371c"
190
+ "f28731a82f4b394a5765fdefe2d1b667810831f6dcea400ca62bc66e1d65371c",
191
+ "f43a5484e39aca4e6af86e7f8d2941773cdc1c960838ad88ae3540c49a1097df",
192
+ "f46c108457e4fe12953dca75b409e06fca9cb264baffabe0c0d04c89af471e56",
193
+ "f6bc17efa746680a44bbc49be67ac3f924d9c306c6d937f9959c57a1d3c5b9af",
194
+ "f981764422d468567b5aff31148dc659aeee22cb13dc0ecd873d737deaf07372",
195
+ "fa306a0cdb0f40c817e32164cd03b946564a02b4b7f7c3f4f0b9513096584d28",
196
+ "fa5cf460eb314437081f7cea30dc8041c0bd6fc3f560a74bc2d7be1bf07384b0",
197
+ "fc39ec6d8a22498697f968ffc0fe5f717bed97afd68f922c76a80ed1f10d6579"
119
198
  ]);
120
199
 
121
200
  // src/lifecycle/paths.ts
@@ -475,11 +554,119 @@ function logApplyStats(stats) {
475
554
 
476
555
  // src/registry/resolve.ts
477
556
  import { execFile } from "child_process";
478
- import { createHash as createHash2 } from "crypto";
557
+ import { createHash as createHash2, randomUUID } from "crypto";
558
+ import { mkdir as mkdir4, readFile as readFile4, rename, rm, stat, writeFile as writeFile3 } from "fs/promises";
479
559
  import { homedir } from "os";
480
560
  import path5 from "path";
481
561
  import { promisify } from "util";
482
562
  var execFileAsync = promisify(execFile);
563
+ var LOCK_TIMEOUT_MS = 6e4;
564
+ var LOCK_RETRY_MS = 200;
565
+ var LOCK_STALE_MS = 5 * 6e4;
566
+ var LOCK_REFRESH_MS = 1e4;
567
+ function lockOwnerPath(lockDir) {
568
+ return path5.join(lockDir, "owner.json");
569
+ }
570
+ function isProcessAlive(pid) {
571
+ try {
572
+ process.kill(pid, 0);
573
+ return true;
574
+ } catch {
575
+ return false;
576
+ }
577
+ }
578
+ async function readLockOwner(lockDir) {
579
+ try {
580
+ const raw = await readFile4(lockOwnerPath(lockDir), "utf8");
581
+ const parsed = JSON.parse(raw);
582
+ if (typeof parsed.pid === "number" && typeof parsed.uuid === "string") {
583
+ return parsed;
584
+ }
585
+ } catch {
586
+ }
587
+ return null;
588
+ }
589
+ async function writeLockOwner(lockDir, owner) {
590
+ const finalPath = lockOwnerPath(lockDir);
591
+ const tmpPath = path5.join(lockDir, `owner.${owner.uuid}.tmp`);
592
+ await writeFile3(tmpPath, JSON.stringify(owner), "utf8");
593
+ await rename(tmpPath, finalPath);
594
+ }
595
+ async function refreshLockOwner(lockDir, uuid) {
596
+ const owner = await readLockOwner(lockDir);
597
+ if (!owner || owner.uuid !== uuid) return;
598
+ await writeLockOwner(lockDir, { ...owner, updatedAt: Date.now() });
599
+ }
600
+ async function releaseCacheLock(lockDir, uuid) {
601
+ const owner = await readLockOwner(lockDir);
602
+ if (!owner || owner.uuid !== uuid) {
603
+ return;
604
+ }
605
+ try {
606
+ await rm(lockDir, { recursive: true, force: true });
607
+ } catch {
608
+ }
609
+ }
610
+ async function tryReclaimStaleLock(lockDir) {
611
+ let owner;
612
+ let lockStat;
613
+ try {
614
+ lockStat = await stat(lockDir);
615
+ owner = await readLockOwner(lockDir);
616
+ } catch {
617
+ return false;
618
+ }
619
+ const now2 = Date.now();
620
+ if (owner) {
621
+ const ownerFresh = now2 - owner.updatedAt < LOCK_STALE_MS;
622
+ if (isProcessAlive(owner.pid) && ownerFresh) {
623
+ return false;
624
+ }
625
+ }
626
+ if (now2 - lockStat.mtimeMs > LOCK_STALE_MS) {
627
+ try {
628
+ await rm(lockDir, { recursive: true, force: true });
629
+ return true;
630
+ } catch {
631
+ }
632
+ }
633
+ return false;
634
+ }
635
+ async function acquireCacheLock(cacheDir) {
636
+ const lockDir = `${cacheDir}.lock`;
637
+ const deadline = Date.now() + LOCK_TIMEOUT_MS;
638
+ const uuid = randomUUID();
639
+ const owner = { pid: process.pid, uuid, updatedAt: Date.now() };
640
+ await mkdir4(path5.dirname(lockDir), { recursive: true });
641
+ while (Date.now() < deadline) {
642
+ try {
643
+ await mkdir4(lockDir, { recursive: false });
644
+ await writeLockOwner(lockDir, owner);
645
+ const refreshInterval = setInterval(() => {
646
+ refreshLockOwner(lockDir, uuid).catch(() => {
647
+ });
648
+ }, LOCK_REFRESH_MS);
649
+ return async () => {
650
+ clearInterval(refreshInterval);
651
+ await releaseCacheLock(lockDir, uuid);
652
+ };
653
+ } catch (err) {
654
+ const code = err.code;
655
+ if (code === "EEXIST") {
656
+ if (await tryReclaimStaleLock(lockDir)) {
657
+ continue;
658
+ }
659
+ await new Promise((r) => setTimeout(r, LOCK_RETRY_MS + Math.random() * 100));
660
+ continue;
661
+ }
662
+ if (code === "ENOENT") {
663
+ continue;
664
+ }
665
+ throw err;
666
+ }
667
+ }
668
+ throw new Error(`Timed out waiting for cache lock on ${cacheDir}. Another install may be stuck.`);
669
+ }
483
670
  var DEFAULT_REGISTRY_URL = "https://github.com/agent-kit-startup/agent-kit";
484
671
  var DEFAULT_REGISTRY_REF = "main";
485
672
  var SAFE_URL = /^https:\/\/[^\s]+$/;
@@ -504,15 +691,32 @@ async function hasRegistryIndex(root) {
504
691
  return fileExists(path5.join(root, "registry", "registry.json"));
505
692
  }
506
693
  async function cloneRegistry(url, ref, dest) {
507
- const { mkdir: mkdir6 } = await import("fs/promises");
508
- await mkdir6(path5.dirname(dest), { recursive: true });
694
+ const { mkdir: mkdir7 } = await import("fs/promises");
695
+ await mkdir7(path5.dirname(dest), { recursive: true });
509
696
  try {
510
697
  await execFileAsync("git", ["clone", "--depth", "1", "--branch", ref, "--", url, dest], {
511
698
  env: gitEnv()
512
699
  });
513
- } catch {
514
- await execFileAsync("git", ["clone", "--depth", "1", "--", url, dest], { env: gitEnv() });
515
- await execFileAsync("git", ["checkout", ref, "--"], { cwd: dest, env: gitEnv() });
700
+ } catch (firstErr) {
701
+ try {
702
+ await execFileAsync("git", ["clone", "--depth", "1", "--", url, dest], { env: gitEnv() });
703
+ await execFileAsync("git", ["checkout", ref, "--"], { cwd: dest, env: gitEnv() });
704
+ } catch (secondErr) {
705
+ const msg = secondErr instanceof Error ? secondErr.message : String(secondErr);
706
+ if (/403|Authentication|could not read Username/i.test(msg)) {
707
+ throw new Error(
708
+ `Registry clone failed (auth/access): ${url}@${ref}. Check git credentials or use --registry <local-path> for a local checkout.`
709
+ );
710
+ }
711
+ if (/Could not resolve host|Network is unreachable|Connection refused/i.test(msg)) {
712
+ throw new Error(
713
+ `Registry clone failed (network): ${url}@${ref}. Check network/proxy settings or use --registry <local-path>.`
714
+ );
715
+ }
716
+ throw new Error(
717
+ `Registry clone failed: ${url}@${ref}. ${msg}. Use --registry <local-path> if you have a local checkout.`
718
+ );
719
+ }
516
720
  }
517
721
  }
518
722
  async function refreshCache(cacheDir) {
@@ -556,15 +760,24 @@ async function resolveRegistryRoot(options) {
556
760
  const ref = options.registryRef ?? DEFAULT_REGISTRY_REF;
557
761
  assertSafeRegistrySource(url, ref);
558
762
  const dest = path5.join(homedir(), ".cache", "agent-kit", "registry", cacheKey(url, ref));
559
- if (await hasRegistryIndex(dest)) {
560
- await refreshCache(dest);
561
- return { root: dest, source: "remote-cache", url, ref };
562
- }
563
- await cloneRegistry(url, ref, dest);
564
- if (!await hasRegistryIndex(dest)) {
565
- throw new Error(`Cloned ${url}@${ref} but registry/registry.json is missing`);
763
+ const unlock = await acquireCacheLock(dest);
764
+ let ownershipTransferred = false;
765
+ try {
766
+ if (await hasRegistryIndex(dest)) {
767
+ await refreshCache(dest);
768
+ } else {
769
+ await cloneRegistry(url, ref, dest);
770
+ if (!await hasRegistryIndex(dest)) {
771
+ throw new Error(`Cloned ${url}@${ref} but registry/registry.json is missing`);
772
+ }
773
+ }
774
+ ownershipTransferred = true;
775
+ return { root: dest, source: "remote-cache", url, ref, unlock };
776
+ } finally {
777
+ if (!ownershipTransferred) {
778
+ await unlock();
779
+ }
566
780
  }
567
- return { root: dest, source: "remote-cache", url, ref };
568
781
  }
569
782
 
570
783
  // src/lifecycle/resolve-cli.ts
@@ -826,7 +1039,7 @@ async function findPack(rootDir, packId) {
826
1039
  }
827
1040
 
828
1041
  // src/registry/install.ts
829
- import { readFile as readFile4 } from "fs/promises";
1042
+ import { readFile as readFile5 } from "fs/promises";
830
1043
  import path8 from "path";
831
1044
  function targetForMember(member) {
832
1045
  switch (member.kind) {
@@ -945,7 +1158,7 @@ function packMemberTargets(member) {
945
1158
  var addCommand = defineCommand({
946
1159
  meta: {
947
1160
  name: "add",
948
- description: "Install a skill or L1 pack from the registry into the project."
1161
+ description: "Install a skill or L1 pack from the registry."
949
1162
  },
950
1163
  args: {
951
1164
  id: {
@@ -982,64 +1195,68 @@ var addCommand = defineCommand({
982
1195
  refresh: args.refresh,
983
1196
  manifest: existing
984
1197
  });
985
- const protectedGlobs = resolveProtectedGlobs(existing);
986
- const index = await loadRegistry(registry.root);
987
- const asSkill = allSkills(index).find((s) => s.id === args.id);
988
- const asPack = allPacks(index).find((p) => p.id === args.id);
989
- const base = existing ? { ...existing } : buildManifest({
990
- version: KIT_VERSION,
991
- profile: "default",
992
- registryUrl: registry.url,
993
- registryRef: registry.ref
994
- });
995
- const finish = async (kind, id) => {
996
- if (kind === "skill") base.skills = upsertIdList(base.skills, id);
997
- else base.packs = upsertIdList(base.packs, id);
998
- if (registry.url || registry.ref) {
999
- base.registry = {
1000
- url: registry.url ?? base.registry?.url,
1001
- ref: registry.ref ?? base.registry?.ref
1002
- };
1198
+ try {
1199
+ const protectedGlobs = resolveProtectedGlobs(existing);
1200
+ const index = await loadRegistry(registry.root);
1201
+ const asSkill = allSkills(index).find((s) => s.id === args.id);
1202
+ const asPack = allPacks(index).find((p) => p.id === args.id);
1203
+ const base = existing ? { ...existing } : buildManifest({
1204
+ version: KIT_VERSION,
1205
+ profile: "default",
1206
+ registryUrl: registry.url,
1207
+ registryRef: registry.ref
1208
+ });
1209
+ const finish = async (kind, id) => {
1210
+ if (kind === "skill") base.skills = upsertIdList(base.skills, id);
1211
+ else base.packs = upsertIdList(base.packs, id);
1212
+ if (registry.url || registry.ref) {
1213
+ base.registry = {
1214
+ url: registry.url ?? base.registry?.url,
1215
+ ref: registry.ref ?? base.registry?.ref
1216
+ };
1217
+ }
1218
+ base.version = KIT_VERSION;
1219
+ await saveManifest(args.cwd, base);
1220
+ };
1221
+ if (args.skill) {
1222
+ const skill = await findSkill(registry.root, args.id);
1223
+ const stats = await installSkill(registry.root, args.cwd, skill, { protectedGlobs });
1224
+ logApplyStats(stats);
1225
+ await finish("skill", skill.id);
1226
+ logger.success(`Skill '${skill.id}' added.`);
1227
+ return;
1003
1228
  }
1004
- base.version = KIT_VERSION;
1005
- await saveManifest(args.cwd, base);
1006
- };
1007
- if (args.skill) {
1008
- const skill = await findSkill(registry.root, args.id);
1009
- const stats = await installSkill(registry.root, args.cwd, skill, { protectedGlobs });
1010
- logApplyStats(stats);
1011
- await finish("skill", skill.id);
1012
- logger.success(`Skill '${skill.id}' added.`);
1013
- return;
1014
- }
1015
- if (args.pack) {
1016
- const pack = await findPack(registry.root, args.id);
1017
- const stats = await installPack(registry.root, args.cwd, pack.id, { protectedGlobs });
1018
- logApplyStats(stats);
1019
- await finish("pack", pack.id);
1020
- logger.success(`Pack '${pack.id}' added.`);
1021
- return;
1022
- }
1023
- if (asSkill && asPack) {
1024
- throw new Error(
1025
- `'${args.id}' is both a skill and a pack. Disambiguate: agent-kit add --skill ${args.id} | agent-kit add --pack ${args.id}`
1026
- );
1027
- }
1028
- if (asSkill) {
1029
- const stats = await installSkill(registry.root, args.cwd, asSkill, { protectedGlobs });
1030
- logApplyStats(stats);
1031
- await finish("skill", asSkill.id);
1032
- logger.success(`Skill '${asSkill.id}' added.`);
1033
- return;
1034
- }
1035
- if (asPack) {
1036
- const stats = await installPack(registry.root, args.cwd, asPack.id, { protectedGlobs });
1037
- logApplyStats(stats);
1038
- await finish("pack", asPack.id);
1039
- logger.success(`Pack '${asPack.id}' added.`);
1040
- return;
1229
+ if (args.pack) {
1230
+ const pack = await findPack(registry.root, args.id);
1231
+ const stats = await installPack(registry.root, args.cwd, pack.id, { protectedGlobs });
1232
+ logApplyStats(stats);
1233
+ await finish("pack", pack.id);
1234
+ logger.success(`Pack '${pack.id}' added.`);
1235
+ return;
1236
+ }
1237
+ if (asSkill && asPack) {
1238
+ throw new Error(
1239
+ `'${args.id}' is both a skill and a pack. Disambiguate: agent-kit add --skill ${args.id} | agent-kit add --pack ${args.id}`
1240
+ );
1241
+ }
1242
+ if (asSkill) {
1243
+ const stats = await installSkill(registry.root, args.cwd, asSkill, { protectedGlobs });
1244
+ logApplyStats(stats);
1245
+ await finish("skill", asSkill.id);
1246
+ logger.success(`Skill '${asSkill.id}' added.`);
1247
+ return;
1248
+ }
1249
+ if (asPack) {
1250
+ const stats = await installPack(registry.root, args.cwd, asPack.id, { protectedGlobs });
1251
+ logApplyStats(stats);
1252
+ await finish("pack", asPack.id);
1253
+ logger.success(`Pack '${asPack.id}' added.`);
1254
+ return;
1255
+ }
1256
+ throw new Error(`'${args.id}' not found as skill or pack in registry.`);
1257
+ } finally {
1258
+ await registry.unlock?.();
1041
1259
  }
1042
- throw new Error(`'${args.id}' not found as skill or pack in registry.`);
1043
1260
  }
1044
1261
  });
1045
1262
 
@@ -1047,7 +1264,7 @@ var addCommand = defineCommand({
1047
1264
  import { defineCommand as defineCommand2 } from "citty";
1048
1265
 
1049
1266
  // src/lifecycle/contribute.ts
1050
- import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile3 } from "fs/promises";
1267
+ import { mkdir as mkdir5, readFile as readFile6, writeFile as writeFile4 } from "fs/promises";
1051
1268
  import path9 from "path";
1052
1269
 
1053
1270
  // src/lifecycle/contribute-gate.ts
@@ -1240,6 +1457,9 @@ var L0_ARTIFACTS = [
1240
1457
  source: ".cursor/commands/dogfood.md",
1241
1458
  target: ".cursor/commands/dogfood.md"
1242
1459
  },
1460
+ // Factory-only: `.cursor/commands/public-issue-triage.md` is intentionally
1461
+ // omitted from L0 (and excluded from public-sync). See ADR
1462
+ // 2026-08-05_factory-only-public-issue-triage-command.md.
1243
1463
  {
1244
1464
  source: ".cursor/commands/cursor-update-awareness.md",
1245
1465
  target: ".cursor/commands/cursor-update-awareness.md"
@@ -1338,7 +1558,7 @@ var L0_ARTIFACTS = [
1338
1558
  // src/lifecycle/contribute.ts
1339
1559
  async function readLocal(projectRoot, rel) {
1340
1560
  try {
1341
- return await readFile5(resolveContained(projectRoot, rel), "utf8");
1561
+ return await readFile6(resolveContained(projectRoot, rel), "utf8");
1342
1562
  } catch {
1343
1563
  return null;
1344
1564
  }
@@ -1414,7 +1634,7 @@ async function planContribute(options) {
1414
1634
  }
1415
1635
  if (kind === "drift") {
1416
1636
  try {
1417
- const upstream = await readFile5(
1637
+ const upstream = await readFile6(
1418
1638
  resolveContained(options.registryRoot, registryPath),
1419
1639
  "utf8"
1420
1640
  );
@@ -1480,10 +1700,10 @@ async function writeContributeToRegistry(projectRoot, registryRoot, accepted) {
1480
1700
  const written = [];
1481
1701
  for (const c of accepted) {
1482
1702
  if (!c.registryPath) continue;
1483
- const content = await readFile5(resolveContained(projectRoot, c.projectPath), "utf8");
1703
+ const content = await readFile6(resolveContained(projectRoot, c.projectPath), "utf8");
1484
1704
  const dest = resolveContained(registryRoot, c.registryPath);
1485
- await mkdir4(path9.dirname(dest), { recursive: true });
1486
- await writeFile3(dest, content, "utf8");
1705
+ await mkdir5(path9.dirname(dest), { recursive: true });
1706
+ await writeFile4(dest, content, "utf8");
1487
1707
  written.push(c.registryPath);
1488
1708
  }
1489
1709
  return written;
@@ -1554,57 +1774,61 @@ var contributeCommand = defineCommand2({
1554
1774
  refresh: args.refresh,
1555
1775
  manifest
1556
1776
  });
1557
- if (registry.source !== "flag" && args.write) {
1558
- logger.warn(
1559
- "contribute --write needs a local kit checkout. Pass --registry /path/to/agent-kit."
1560
- );
1561
- return;
1562
- }
1563
- logger.info(`Registry: ${registry.root} (${registry.source})`);
1564
- const plan = await planContribute({
1565
- registryRoot: registry.root,
1566
- projectRoot: args.cwd,
1567
- manifest,
1568
- extraPaths: parsePaths(args.path),
1569
- includeDrift: args.drift
1570
- });
1571
- console.log(
1572
- `Contribute plan: accepted=${plan.accepted.length} rejected=${plan.rejected.length} (drift scan=${args.drift})`
1573
- );
1574
- if (plan.candidates.length === 0) {
1575
- logger.success("Nothing to contribute (no drift / no --path).");
1576
- return;
1577
- }
1578
- for (const c of plan.candidates) {
1579
- const mark = c.gateOk ? "ok" : "FAIL";
1777
+ try {
1778
+ if (registry.source !== "flag" && args.write) {
1779
+ logger.warn(
1780
+ "contribute --write needs a local kit checkout. Pass --registry /path/to/agent-kit."
1781
+ );
1782
+ return;
1783
+ }
1784
+ logger.info(`Registry: ${registry.root} (${registry.source})`);
1785
+ const plan = await planContribute({
1786
+ registryRoot: registry.root,
1787
+ projectRoot: args.cwd,
1788
+ manifest,
1789
+ extraPaths: parsePaths(args.path),
1790
+ includeDrift: args.drift
1791
+ });
1580
1792
  console.log(
1581
- `${mark.padEnd(4)} ${c.kind.padEnd(5)} ${c.projectPath} \u2192 ${c.registryPath || "(unmapped)"}`
1793
+ `Contribute plan: accepted=${plan.accepted.length} rejected=${plan.rejected.length} (drift scan=${args.drift})`
1582
1794
  );
1583
- for (const issue of c.issues) {
1584
- console.log(` [${issue.code}] ${issue.message}`);
1795
+ if (plan.candidates.length === 0) {
1796
+ logger.success("Nothing to contribute (no drift / no --path).");
1797
+ return;
1585
1798
  }
1799
+ for (const c of plan.candidates) {
1800
+ const mark = c.gateOk ? "ok" : "FAIL";
1801
+ console.log(
1802
+ `${mark.padEnd(4)} ${c.kind.padEnd(5)} ${c.projectPath} \u2192 ${c.registryPath || "(unmapped)"}`
1803
+ );
1804
+ for (const issue of c.issues) {
1805
+ console.log(` [${issue.code}] ${issue.message}`);
1806
+ }
1807
+ }
1808
+ if (plan.accepted.length === 0) {
1809
+ logger.warn("No files passed the contribute gate.");
1810
+ return;
1811
+ }
1812
+ console.log("\n--- Suggested PR body ---\n");
1813
+ console.log(formatPrBody(plan.accepted));
1814
+ if (!args.write) {
1815
+ logger.info(
1816
+ "Dry run. Re-run with --write --registry /path/to/agent-kit to copy accepted files into the kit checkout, then open a PR (gh pr create --base main)."
1817
+ );
1818
+ return;
1819
+ }
1820
+ const written = await writeContributeToRegistry(args.cwd, registry.root, plan.accepted);
1821
+ logger.success(`Wrote ${written.length} file(s) into ${registry.root}`);
1822
+ for (const p of written) console.log(` + ${p}`);
1823
+ console.log("\nNext (HITL \u2014 review before push):");
1824
+ console.log(` cd ${registry.root}`);
1825
+ console.log(" git checkout -b contribute/<short-topic>");
1826
+ console.log(' git add <files> && git commit -m "feat: contribute <topic> from consumer"');
1827
+ console.log(" git push -u origin HEAD");
1828
+ console.log(' gh pr create --base main --title "feat: contribute <topic>" --body-file -');
1829
+ } finally {
1830
+ await registry.unlock?.();
1586
1831
  }
1587
- if (plan.accepted.length === 0) {
1588
- logger.warn("No files passed the contribute gate.");
1589
- return;
1590
- }
1591
- console.log("\n--- Suggested PR body ---\n");
1592
- console.log(formatPrBody(plan.accepted));
1593
- if (!args.write) {
1594
- logger.info(
1595
- "Dry run. Re-run with --write --registry /path/to/agent-kit to copy accepted files into the kit checkout, then open a PR (gh pr create --base main)."
1596
- );
1597
- return;
1598
- }
1599
- const written = await writeContributeToRegistry(args.cwd, registry.root, plan.accepted);
1600
- logger.success(`Wrote ${written.length} file(s) into ${registry.root}`);
1601
- for (const p of written) console.log(` + ${p}`);
1602
- console.log("\nNext (HITL \u2014 review before push):");
1603
- console.log(` cd ${registry.root}`);
1604
- console.log(" git checkout -b contribute/<short-topic>");
1605
- console.log(' git add <files> && git commit -m "feat: contribute <topic> from consumer"');
1606
- console.log(" git push -u origin HEAD");
1607
- console.log(' gh pr create --base main --title "feat: contribute <topic>" --body-file -');
1608
1832
  }
1609
1833
  });
1610
1834
 
@@ -1612,13 +1836,30 @@ var contributeCommand = defineCommand2({
1612
1836
  import { defineCommand as defineCommand3 } from "citty";
1613
1837
 
1614
1838
  // src/lifecycle/cursor-update-awareness.ts
1615
- import { readFile as readFile6 } from "fs/promises";
1839
+ import { access as access2, readFile as readFile7 } from "fs/promises";
1616
1840
  import path10 from "path";
1617
1841
  var DEFAULT_CURSOR_CHANGELOG_URL = "https://cursor.com/changelog";
1618
1842
  var CHANGELOG_FETCH_TIMEOUT_MS = 15e3;
1619
1843
  var CURSOR_VERSION_MAJOR_MAX = 20;
1620
1844
  var INVENTORY_REL = path10.join("docs", "cursor-native-audit.md");
1621
1845
  var FEATURES_REL = path10.join("docs", "cursor-3-features.md");
1846
+ async function resolveInventoryRoot(cwd) {
1847
+ let dir = path10.resolve(cwd);
1848
+ for (; ; ) {
1849
+ try {
1850
+ await access2(path10.join(dir, INVENTORY_REL));
1851
+ return dir;
1852
+ } catch {
1853
+ }
1854
+ const parent = path10.dirname(dir);
1855
+ if (parent === dir) break;
1856
+ dir = parent;
1857
+ }
1858
+ return null;
1859
+ }
1860
+ function missingInventoryMessage() {
1861
+ return `Missing inventory at ${INVENTORY_REL}. Run from the kit/repo root that contains that file, or pass --cwd <path>.`;
1862
+ }
1622
1863
  var DEFAULT_PREFS = {
1623
1864
  enabled: false,
1624
1865
  intervalDays: 7,
@@ -1786,8 +2027,6 @@ function baseResult(partial) {
1786
2027
  };
1787
2028
  }
1788
2029
  async function checkCursorUpdateAwareness(cwd, options = {}) {
1789
- const inventoryPath = path10.join(cwd, INVENTORY_REL);
1790
- const featuresPath = path10.join(cwd, FEATURES_REL);
1791
2030
  const prefs = readCursorUpdateCheckPrefs(await loadContextConfig(cwd));
1792
2031
  const changelogUrl = options.changelogUrl ?? prefs.changelogUrl;
1793
2032
  if (options.respectPrefs) {
@@ -1820,9 +2059,26 @@ async function checkCursorUpdateAwareness(cwd, options = {}) {
1820
2059
  });
1821
2060
  }
1822
2061
  }
2062
+ const inventoryRoot = await resolveInventoryRoot(cwd);
2063
+ if (!inventoryRoot) {
2064
+ return baseResult({
2065
+ status: "error",
2066
+ inventoryPath: INVENTORY_REL,
2067
+ featuresPath: FEATURES_REL,
2068
+ changelogUrl,
2069
+ latestCursorVersion: null,
2070
+ lastSeenCursorVersion: prefs.lastSeenCursorVersion,
2071
+ inventoryRefreshed: null,
2072
+ openActionIds: [],
2073
+ gaps: [],
2074
+ message: missingInventoryMessage()
2075
+ });
2076
+ }
2077
+ const inventoryPath = path10.join(inventoryRoot, INVENTORY_REL);
2078
+ const featuresPath = path10.join(inventoryRoot, FEATURES_REL);
1823
2079
  let inventoryMd;
1824
2080
  try {
1825
- inventoryMd = await readFile6(inventoryPath, "utf8");
2081
+ inventoryMd = await readFile7(inventoryPath, "utf8");
1826
2082
  } catch {
1827
2083
  return baseResult({
1828
2084
  status: "error",
@@ -1834,7 +2090,7 @@ async function checkCursorUpdateAwareness(cwd, options = {}) {
1834
2090
  inventoryRefreshed: null,
1835
2091
  openActionIds: [],
1836
2092
  gaps: [],
1837
- message: `Missing inventory at ${INVENTORY_REL}.`
2093
+ message: missingInventoryMessage()
1838
2094
  });
1839
2095
  }
1840
2096
  const inventoryRefreshed = parseInventoryRefreshed(inventoryMd);
@@ -1865,7 +2121,7 @@ async function checkCursorUpdateAwareness(cwd, options = {}) {
1865
2121
  }
1866
2122
  }
1867
2123
  try {
1868
- const featuresMd = await readFile6(featuresPath, "utf8");
2124
+ const featuresMd = await readFile7(featuresPath, "utf8");
1869
2125
  if (parseFeatureMapNames(featuresMd).length === 0) {
1870
2126
  gaps.push({
1871
2127
  id: "features-map-empty",
@@ -1947,7 +2203,7 @@ async function checkCursorUpdateAwareness(cwd, options = {}) {
1947
2203
  var cursorAwarenessCommand = defineCommand3({
1948
2204
  meta: {
1949
2205
  name: "cursor-awareness",
1950
- description: "Opt-in advisory: diff Cursor changelog / native-audit inventory for gaps (never apply, never Field Reports)"
2206
+ description: "Opt-in advisory: Cursor changelog vs native-audit gaps (never apply)."
1951
2207
  },
1952
2208
  args: {
1953
2209
  cwd: {
@@ -2009,17 +2265,132 @@ var cursorAwarenessCommand = defineCommand3({
2009
2265
  });
2010
2266
 
2011
2267
  // src/commands/dashboard-broadcast.ts
2012
- import { spawn as spawn2 } from "child_process";
2013
- import { access as access3 } from "fs/promises";
2268
+ import { spawn as spawn3 } from "child_process";
2269
+ import { access as access4 } from "fs/promises";
2014
2270
  import path12 from "path";
2015
2271
  import { defineCommand as defineCommand5 } from "citty";
2016
2272
 
2017
2273
  // src/commands/dashboard.ts
2018
- import { execFileSync, spawn } from "child_process";
2019
- import { access as access2 } from "fs/promises";
2274
+ import { execFileSync, spawn as spawn2 } from "child_process";
2275
+ import { existsSync, readFileSync as readFileSync2, realpathSync } from "fs";
2276
+ import { access as access3 } from "fs/promises";
2020
2277
  import path11 from "path";
2021
2278
  import { fileURLToPath } from "url";
2022
2279
  import { defineCommand as defineCommand4 } from "citty";
2280
+
2281
+ // ../../dashboard/lib/guards.mjs
2282
+ import { randomBytes, timingSafeEqual } from "crypto";
2283
+ import { networkInterfaces } from "os";
2284
+ import { resolve } from "path";
2285
+ var KIT_ROOT_ENV_KEYS = Object.freeze(["MISSION_CONTROL_KIT_ROOT", "AGENT_KIT_HOME"]);
2286
+ var CONTEXT_CONFIG_REL = ".cursor/context/config.json";
2287
+ var CONFIG_PERSONA_IDS = Object.freeze(["autopilot", "night-shift", "ghost-runner"]);
2288
+ var CONFIG_PERSONA_MODES = Object.freeze(["continue-plan", "run-plan", "cli-run-plan"]);
2289
+ var CONFIG_REVIEW_BACKENDS = Object.freeze(["auto", "claude", "cursor"]);
2290
+ var CONFIG_REVIEW_MODES = Object.freeze(["paste", "autonomous"]);
2291
+ var CONFIG_REVIEW_PREFLIGHT = Object.freeze(["off", "warn", "block"]);
2292
+ function resolveContextConfigPath(repoRoot, fsHooks = {}) {
2293
+ const exists2 = fsHooks.existsSync;
2294
+ const realpath = fsHooks.realpathSync;
2295
+ const mkdir7 = fsHooks.mkdirSync;
2296
+ if (typeof repoRoot !== "string" || !repoRoot) {
2297
+ return { ok: false, error: "invalid repo root" };
2298
+ }
2299
+ const abs = resolve(repoRoot, CONTEXT_CONFIG_REL);
2300
+ const contextDir = resolve(repoRoot, ".cursor", "context");
2301
+ const absNorm = abs.replace(/\\/g, "/");
2302
+ if (!absNorm.endsWith("/.cursor/context/config.json")) {
2303
+ return { ok: false, error: "path escape" };
2304
+ }
2305
+ try {
2306
+ if (typeof mkdir7 === "function" && typeof exists2 === "function" && !exists2(contextDir)) {
2307
+ mkdir7(contextDir, { recursive: true });
2308
+ }
2309
+ if (typeof realpath === "function" && typeof exists2 === "function" && exists2(abs)) {
2310
+ const fileReal = String(realpath(abs)).replace(/\\/g, "/");
2311
+ const rootReal = String(realpath(repoRoot)).replace(/\\/g, "/");
2312
+ if (!fileReal.startsWith(`${rootReal}/`)) {
2313
+ return { ok: false, error: "path escape" };
2314
+ }
2315
+ if (!fileReal.endsWith("/.cursor/context/config.json")) {
2316
+ return { ok: false, error: "path escape" };
2317
+ }
2318
+ return { ok: true, path: realpath(abs) };
2319
+ }
2320
+ } catch {
2321
+ return { ok: false, error: "path escape" };
2322
+ }
2323
+ return { ok: true, path: abs };
2324
+ }
2325
+
2326
+ // ../../dashboard/lib/open-browser.mjs
2327
+ import { spawn, spawnSync } from "child_process";
2328
+ import { readFileSync } from "fs";
2329
+ import { platform as osPlatform } from "os";
2330
+ var OS_DEFAULT_TOKENS = /* @__PURE__ */ new Set(["", "default", "os", "ask"]);
2331
+ function isSafePreferredBrowser(value) {
2332
+ if (typeof value !== "string") return false;
2333
+ const s = value.trim();
2334
+ if (!s) return false;
2335
+ if (/[/\\]/.test(s)) return false;
2336
+ if (/[\0-\x1f\x7f]/.test(s)) return false;
2337
+ if (/[$`;&|<>(){}[\]!*?#~"'%^=,+]/.test(s)) return false;
2338
+ if (s.includes(":")) return false;
2339
+ if (/^-/.test(s)) return false;
2340
+ return true;
2341
+ }
2342
+ function normalizePreferredBrowser(value) {
2343
+ if (value == null) return null;
2344
+ const s = String(value).trim();
2345
+ if (!s || OS_DEFAULT_TOKENS.has(s.toLowerCase())) return null;
2346
+ if (!isSafePreferredBrowser(s)) return null;
2347
+ return s;
2348
+ }
2349
+ function readPreferredBrowserFromConfig(configPath, fsHooks = {}) {
2350
+ const read = fsHooks.readFileSync ?? readFileSync;
2351
+ try {
2352
+ const raw = read(configPath, "utf8");
2353
+ const data = JSON.parse(raw);
2354
+ if (!data || typeof data !== "object" || Array.isArray(data)) return null;
2355
+ const mc = data.missionControl;
2356
+ if (!mc || typeof mc !== "object" || Array.isArray(mc)) return null;
2357
+ return mc.preferredBrowser ?? null;
2358
+ } catch {
2359
+ return null;
2360
+ }
2361
+ }
2362
+
2363
+ // src/commands/dashboard.ts
2364
+ function readPreferredBrowserFromWorkspace(cwd, readFile22 = readFileSync2) {
2365
+ const resolved = resolveContextConfigPath(path11.resolve(cwd), {
2366
+ existsSync,
2367
+ realpathSync
2368
+ });
2369
+ if (!resolved.ok) return null;
2370
+ const value = readPreferredBrowserFromConfig(resolved.path, { readFileSync: readFile22 });
2371
+ return normalizePreferredBrowser(value);
2372
+ }
2373
+ function applyDashboardOpenEnv(env, opts) {
2374
+ const next = { ...env };
2375
+ if (opts.noOpen) next.MISSION_CONTROL_NO_OPEN = "1";
2376
+ const flag = opts.browser?.trim();
2377
+ if (flag) {
2378
+ const safe = normalizePreferredBrowser(flag);
2379
+ if (safe) next.MISSION_CONTROL_PREFERRED_BROWSER = safe;
2380
+ } else if (!next.MISSION_CONTROL_PREFERRED_BROWSER && opts.cwd) {
2381
+ const fromConfig = readPreferredBrowserFromWorkspace(opts.cwd);
2382
+ if (fromConfig) next.MISSION_CONTROL_PREFERRED_BROWSER = fromConfig;
2383
+ }
2384
+ return next;
2385
+ }
2386
+ function dashboardSpawnEnv(env, snapshotRoot, opts) {
2387
+ const root = path11.resolve(snapshotRoot);
2388
+ return applyDashboardOpenEnv({ ...env, MISSION_CONTROL_REPO_ROOT: root }, { ...opts, cwd: root });
2389
+ }
2390
+ function dashboardProcessTitle(snapshotRoot) {
2391
+ const base = path11.basename(path11.resolve(snapshotRoot)) || "workspace";
2392
+ return `Mission Control \xB7 ${base}`;
2393
+ }
2023
2394
  function bundledDashboardCandidates(filename, moduleUrl = import.meta.url) {
2024
2395
  const here = path11.dirname(fileURLToPath(moduleUrl));
2025
2396
  return [
@@ -2034,7 +2405,7 @@ function bundledDashboardCandidates(filename, moduleUrl = import.meta.url) {
2034
2405
  async function firstExisting(candidates) {
2035
2406
  for (const candidate2 of candidates) {
2036
2407
  try {
2037
- await access2(candidate2);
2408
+ await access3(candidate2);
2038
2409
  return path11.resolve(candidate2);
2039
2410
  } catch {
2040
2411
  }
@@ -2046,7 +2417,7 @@ async function findDashboardStart(cwd, env = process.env, options = {}) {
2046
2417
  for (; ; ) {
2047
2418
  const candidate2 = path11.join(dir, "dashboard", "start.mjs");
2048
2419
  try {
2049
- await access2(candidate2);
2420
+ await access3(candidate2);
2050
2421
  return candidate2;
2051
2422
  } catch {
2052
2423
  }
@@ -2059,14 +2430,14 @@ async function findDashboardStart(cwd, env = process.env, options = {}) {
2059
2430
  if (!base || typeof base !== "string" || !base.trim()) continue;
2060
2431
  const candidate2 = path11.join(path11.resolve(base.trim()), "dashboard", "start.mjs");
2061
2432
  try {
2062
- await access2(candidate2);
2433
+ await access3(candidate2);
2063
2434
  return candidate2;
2064
2435
  } catch {
2065
2436
  }
2066
2437
  }
2067
2438
  const sibling = path11.join(path11.resolve(cwd), "..", "agent-kit", "dashboard", "start.mjs");
2068
2439
  try {
2069
- await access2(sibling);
2440
+ await access3(sibling);
2070
2441
  return path11.resolve(sibling);
2071
2442
  } catch {
2072
2443
  }
@@ -2076,6 +2447,15 @@ async function findDashboardStart(cwd, env = process.env, options = {}) {
2076
2447
  }
2077
2448
  function resolveDashboardSnapshotRoot(cwd) {
2078
2449
  const abs = path11.resolve(cwd);
2450
+ let dir = abs;
2451
+ for (; ; ) {
2452
+ if (existsSync(path11.join(dir, ".cursor", "agent-kit.json"))) {
2453
+ return dir;
2454
+ }
2455
+ const parent = path11.dirname(dir);
2456
+ if (parent === dir) break;
2457
+ dir = parent;
2458
+ }
2079
2459
  try {
2080
2460
  const top = execFileSync("git", ["-C", abs, "rev-parse", "--show-toplevel"], {
2081
2461
  encoding: "utf8",
@@ -2087,46 +2467,69 @@ function resolveDashboardSnapshotRoot(cwd) {
2087
2467
  return abs;
2088
2468
  }
2089
2469
  function runStartScript(startPath, env) {
2090
- return new Promise((resolve, reject) => {
2091
- const child = spawn(process.execPath, [startPath], {
2470
+ return new Promise((resolve2, reject) => {
2471
+ const child = spawn2(process.execPath, [startPath], {
2092
2472
  cwd: path11.dirname(path11.dirname(startPath)),
2093
2473
  env,
2094
2474
  stdio: "inherit"
2095
2475
  });
2096
2476
  child.on("error", reject);
2097
- child.on("close", (code) => resolve(code ?? 1));
2477
+ child.on("close", (code) => resolve2(code ?? 1));
2098
2478
  });
2099
2479
  }
2100
2480
  var dashboardCommand = defineCommand4({
2101
2481
  meta: {
2102
2482
  name: "dashboard",
2103
- description: "Start Mission Control for this workspace (stable per-root port) and open the panel URL."
2483
+ description: "Start Mission Control for this workspace (loopback; opens panel URL)."
2104
2484
  },
2105
2485
  args: {
2106
2486
  cwd: {
2107
2487
  type: "string",
2108
2488
  default: process.cwd(),
2109
- description: "Workspace to snapshot (git root preferred); also searched upward for dashboard/start.mjs"
2489
+ description: "Workspace to snapshot (nearest .cursor/agent-kit.json, else git root); also searched upward for dashboard/start.mjs"
2110
2490
  },
2111
2491
  "no-open": {
2112
2492
  type: "boolean",
2113
2493
  default: false,
2114
2494
  description: "Do not open a browser; only ensure the server is up and print the URL"
2495
+ },
2496
+ browser: {
2497
+ type: "string",
2498
+ description: "Preferred browser app/binary for this launch (overrides config missionControl.preferredBrowser)"
2115
2499
  }
2116
2500
  },
2117
2501
  async run({ args }) {
2118
2502
  const snapshotRoot = resolveDashboardSnapshotRoot(args.cwd);
2119
2503
  const startPath = await findDashboardStart(args.cwd);
2120
2504
  if (!startPath) {
2121
- logger.error(
2122
- "No dashboard/start.mjs found. After a CLI publish that ships dashboard/ (Path C), reinstall @dadado/agent-kit-cli. Or set MISSION_CONTROL_KIT_ROOT / AGENT_KIT_HOME to an agent-kit checkout, place a sibling ../agent-kit tree, or run from that kit tree."
2505
+ logger.error("No dashboard/start.mjs found.");
2506
+ console.error(
2507
+ [
2508
+ "",
2509
+ "The dashboard runtime is not available in this workspace.",
2510
+ "L0 install provides the /dashboard command text but not the panel itself.",
2511
+ "",
2512
+ "Recovery (pick one):",
2513
+ " 1. Upgrade the CLI: npx @dadado/agent-kit-cli@latest dashboard",
2514
+ " (Path C ships dashboard/ from 4.8.2 onward)",
2515
+ " 2. Set an env var pointing to an agent-kit checkout:",
2516
+ " export MISSION_CONTROL_KIT_ROOT=/path/to/agent-kit",
2517
+ " agent-kit dashboard",
2518
+ " 3. Place an agent-kit sibling: ../agent-kit/dashboard/start.mjs",
2519
+ " 4. Run directly from a kit tree: node dashboard/start.mjs",
2520
+ "",
2521
+ "Works in Cursor, VS Code, and any Node.js terminal.",
2522
+ ""
2523
+ ].join("\n")
2123
2524
  );
2124
2525
  process.exitCode = 1;
2125
2526
  return;
2126
2527
  }
2127
- const env = { ...process.env };
2128
- env.MISSION_CONTROL_REPO_ROOT = snapshotRoot;
2129
- if (args["no-open"]) env.MISSION_CONTROL_NO_OPEN = "1";
2528
+ const env = dashboardSpawnEnv({ ...process.env }, snapshotRoot, {
2529
+ noOpen: Boolean(args["no-open"]),
2530
+ browser: args.browser
2531
+ });
2532
+ process.title = dashboardProcessTitle(snapshotRoot);
2130
2533
  const code = await runStartScript(startPath, env);
2131
2534
  if (code !== 0) process.exitCode = code;
2132
2535
  }
@@ -2138,7 +2541,7 @@ async function findDashboardBroadcastStart(cwd, env = process.env, options = {})
2138
2541
  for (; ; ) {
2139
2542
  const candidate2 = path12.join(dir, "dashboard", "start-broadcast.mjs");
2140
2543
  try {
2141
- await access3(candidate2);
2544
+ await access4(candidate2);
2142
2545
  return candidate2;
2143
2546
  } catch {
2144
2547
  }
@@ -2151,7 +2554,7 @@ async function findDashboardBroadcastStart(cwd, env = process.env, options = {})
2151
2554
  if (!base || typeof base !== "string" || !base.trim()) continue;
2152
2555
  const candidate2 = path12.join(path12.resolve(base.trim()), "dashboard", "start-broadcast.mjs");
2153
2556
  try {
2154
- await access3(candidate2);
2557
+ await access4(candidate2);
2155
2558
  return candidate2;
2156
2559
  } catch {
2157
2560
  }
@@ -2164,7 +2567,7 @@ async function findDashboardBroadcastStart(cwd, env = process.env, options = {})
2164
2567
  "start-broadcast.mjs"
2165
2568
  );
2166
2569
  try {
2167
- await access3(sibling);
2570
+ await access4(sibling);
2168
2571
  return path12.resolve(sibling);
2169
2572
  } catch {
2170
2573
  }
@@ -2173,7 +2576,7 @@ async function findDashboardBroadcastStart(cwd, env = process.env, options = {})
2173
2576
  options.moduleUrl ?? import.meta.url
2174
2577
  )) {
2175
2578
  try {
2176
- await access3(candidate2);
2579
+ await access4(candidate2);
2177
2580
  return path12.resolve(candidate2);
2178
2581
  } catch {
2179
2582
  }
@@ -2181,14 +2584,14 @@ async function findDashboardBroadcastStart(cwd, env = process.env, options = {})
2181
2584
  return null;
2182
2585
  }
2183
2586
  function runStartScript2(startPath, env) {
2184
- return new Promise((resolve, reject) => {
2185
- const child = spawn2(process.execPath, [startPath], {
2587
+ return new Promise((resolve2, reject) => {
2588
+ const child = spawn3(process.execPath, [startPath], {
2186
2589
  cwd: path12.dirname(path12.dirname(startPath)),
2187
2590
  env,
2188
2591
  stdio: "inherit"
2189
2592
  });
2190
2593
  child.on("error", reject);
2191
- child.on("close", (code) => resolve(code ?? 1));
2594
+ child.on("close", (code) => resolve2(code ?? 1));
2192
2595
  });
2193
2596
  }
2194
2597
  var dashboardBroadcastCommand = defineCommand5({
@@ -2205,7 +2608,11 @@ var dashboardBroadcastCommand = defineCommand5({
2205
2608
  "no-open": {
2206
2609
  type: "boolean",
2207
2610
  default: false,
2208
- description: "Do not open a browser; only ensure the server is up and print LAN URL + token"
2611
+ description: "Do not open a browser; only ensure the server is up and print Share URL (when masking is on) plus token / LAN lines"
2612
+ },
2613
+ browser: {
2614
+ type: "string",
2615
+ description: "Preferred browser app/binary for this launch (overrides config missionControl.preferredBrowser)"
2209
2616
  }
2210
2617
  },
2211
2618
  async run({ args }) {
@@ -2218,8 +2625,12 @@ var dashboardBroadcastCommand = defineCommand5({
2218
2625
  process.exitCode = 1;
2219
2626
  return;
2220
2627
  }
2221
- const env = { ...process.env };
2222
- if (args["no-open"]) env.MISSION_CONTROL_NO_OPEN = "1";
2628
+ const snapshotRoot = resolveDashboardSnapshotRoot(args.cwd);
2629
+ const env = dashboardSpawnEnv({ ...process.env }, snapshotRoot, {
2630
+ noOpen: Boolean(args["no-open"]),
2631
+ browser: args.browser
2632
+ });
2633
+ process.title = dashboardProcessTitle(snapshotRoot);
2223
2634
  const code = await runStartScript2(startPath, env);
2224
2635
  if (code !== 0) process.exitCode = code;
2225
2636
  }
@@ -2229,11 +2640,11 @@ var dashboardBroadcastCommand = defineCommand5({
2229
2640
  import { defineCommand as defineCommand6 } from "citty";
2230
2641
 
2231
2642
  // src/lifecycle/diff.ts
2232
- import { readFile as readFile7 } from "fs/promises";
2643
+ import { readFile as readFile8 } from "fs/promises";
2233
2644
  import path13 from "path";
2234
2645
  async function readIfExists(abs) {
2235
2646
  try {
2236
- return await readFile7(abs, "utf8");
2647
+ return await readFile8(abs, "utf8");
2237
2648
  } catch {
2238
2649
  return null;
2239
2650
  }
@@ -2347,19 +2758,23 @@ var diffCommand = defineCommand6({
2347
2758
  refresh: args.refresh,
2348
2759
  manifest
2349
2760
  });
2350
- logger.info(`Registry: ${registry.root} (${registry.source})`);
2351
- const entries = await diffAgainstRegistry(registry.root, args.cwd, manifest);
2352
- const summary = summarizeDiff(entries);
2353
- console.log(
2354
- `Summary: match=${summary.match} drift=${summary.drift} missing-local=${summary["missing-local"]} missing-registry=${summary["missing-registry"]} protected=${summary.protected}`
2355
- );
2356
- const rows = args.all ? entries : entries.filter((e) => e.status !== "match");
2357
- if (rows.length === 0) {
2358
- logger.success("No drift (matches only).");
2359
- return;
2360
- }
2361
- for (const e of rows) {
2362
- console.log(`${e.status.padEnd(18)} ${e.path}`);
2761
+ try {
2762
+ logger.info(`Registry: ${registry.root} (${registry.source})`);
2763
+ const entries = await diffAgainstRegistry(registry.root, args.cwd, manifest);
2764
+ const summary = summarizeDiff(entries);
2765
+ console.log(
2766
+ `Summary: match=${summary.match} drift=${summary.drift} missing-local=${summary["missing-local"]} missing-registry=${summary["missing-registry"]} protected=${summary.protected}`
2767
+ );
2768
+ const rows = args.all ? entries : entries.filter((e) => e.status !== "match");
2769
+ if (rows.length === 0) {
2770
+ logger.success("No drift (matches only).");
2771
+ return;
2772
+ }
2773
+ for (const e of rows) {
2774
+ console.log(`${e.status.padEnd(18)} ${e.path}`);
2775
+ }
2776
+ } finally {
2777
+ await registry.unlock?.();
2363
2778
  }
2364
2779
  }
2365
2780
  });
@@ -2370,7 +2785,7 @@ import { defineCommand as defineCommand7 } from "citty";
2370
2785
 
2371
2786
  // src/invariants/hooks-health.ts
2372
2787
  import { execFile as execFile2 } from "child_process";
2373
- import { constants as constants2, access as access4, readFile as readFile8, readdir as readdir3, stat } from "fs/promises";
2788
+ import { constants as constants2, access as access5, readFile as readFile9, readdir as readdir3, stat as stat2 } from "fs/promises";
2374
2789
  import path14 from "path";
2375
2790
  import { promisify as promisify2 } from "util";
2376
2791
  var execFileAsync2 = promisify2(execFile2);
@@ -2384,7 +2799,7 @@ var EXPECTED_EVENTS = [
2384
2799
  ];
2385
2800
  async function exists(p) {
2386
2801
  try {
2387
- await access4(p);
2802
+ await access5(p);
2388
2803
  return true;
2389
2804
  } catch {
2390
2805
  return false;
@@ -2392,7 +2807,7 @@ async function exists(p) {
2392
2807
  }
2393
2808
  async function isExecutable(p) {
2394
2809
  try {
2395
- await access4(p, constants2.X_OK);
2810
+ await access5(p, constants2.X_OK);
2396
2811
  return true;
2397
2812
  } catch {
2398
2813
  return false;
@@ -2444,7 +2859,7 @@ async function assessGitHooksInstallDrift(rootDir) {
2444
2859
  const gitDir = path14.join(root, ".git");
2445
2860
  if (!await exists(gitDir)) return [];
2446
2861
  try {
2447
- const st = await stat(gitDir);
2862
+ const st = await stat2(gitDir);
2448
2863
  if (!st.isDirectory()) return [];
2449
2864
  } catch {
2450
2865
  return [];
@@ -2472,7 +2887,7 @@ async function assessGitHooksInstallDrift(rootDir) {
2472
2887
  continue;
2473
2888
  }
2474
2889
  try {
2475
- const [a, b] = await Promise.all([readFile8(canonical, "utf8"), readFile8(installed, "utf8")]);
2890
+ const [a, b] = await Promise.all([readFile9(canonical, "utf8"), readFile9(installed, "utf8")]);
2476
2891
  if (a !== b) {
2477
2892
  advisories.push(
2478
2893
  `git-hooks drift: \`.git/hooks/${name}\` differs from \`git-hooks/${name}\`. ${installHint}`
@@ -2505,7 +2920,7 @@ async function assessHooksHealth(rootDir) {
2505
2920
  }
2506
2921
  let parsed;
2507
2922
  try {
2508
- parsed = JSON.parse(await readFile8(hooksJsonAbs, "utf8"));
2923
+ parsed = JSON.parse(await readFile9(hooksJsonAbs, "utf8"));
2509
2924
  } catch {
2510
2925
  return {
2511
2926
  status: "degraded",
@@ -2548,7 +2963,7 @@ async function assessHooksHealth(rootDir) {
2548
2963
  continue;
2549
2964
  }
2550
2965
  try {
2551
- const st = await stat(abs);
2966
+ const st = await stat2(abs);
2552
2967
  if (!st.isFile()) {
2553
2968
  reasons.push(`adapter path is not a file: \`${rel}\``);
2554
2969
  continue;
@@ -2828,11 +3243,11 @@ function createReadinessReport(scan, options) {
2828
3243
  }
2829
3244
 
2830
3245
  // src/scanner/safe-fixes.ts
2831
- import { readFile as readFile11, writeFile as writeFile4 } from "fs/promises";
3246
+ import { readFile as readFile12, writeFile as writeFile5 } from "fs/promises";
2832
3247
  import path22 from "path";
2833
3248
 
2834
3249
  // src/scanner/detect-repository.ts
2835
- import { readFile as readFile9 } from "fs/promises";
3250
+ import { readFile as readFile10 } from "fs/promises";
2836
3251
  import path15 from "path";
2837
3252
  var CONTEXT_PATHS = [
2838
3253
  ["README.md", "README"],
@@ -2938,7 +3353,7 @@ var REQUIRED_SECRET_PATTERNS = [
2938
3353
  async function detectSafety(rootDir, trackedFiles) {
2939
3354
  const gitignorePath = path15.join(rootDir, ".gitignore");
2940
3355
  const hasGitignore = await fileExists(gitignorePath);
2941
- const gitignore = hasGitignore ? await readFile9(gitignorePath, "utf8") : "";
3356
+ const gitignore = hasGitignore ? await readFile10(gitignorePath, "utf8") : "";
2942
3357
  const lines = gitignore.split(/\r?\n/).map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
2943
3358
  const ignoredSecretPatterns = REQUIRED_SECRET_PATTERNS.filter(
2944
3359
  (pattern) => lines.includes(pattern)
@@ -2956,7 +3371,7 @@ async function detectSafety(rootDir, trackedFiles) {
2956
3371
  ];
2957
3372
  const guardContents = await Promise.all(
2958
3373
  guardCandidates.map(
2959
- async (item) => await fileExists(path15.join(rootDir, item)) ? readFile9(path15.join(rootDir, item), "utf8") : ""
3374
+ async (item) => await fileExists(path15.join(rootDir, item)) ? readFile10(path15.join(rootDir, item), "utf8") : ""
2960
3375
  )
2961
3376
  );
2962
3377
  return {
@@ -3163,6 +3578,33 @@ async function detectIde(rootDir) {
3163
3578
  import path18 from "path";
3164
3579
 
3165
3580
  // src/types.ts
3581
+ var GIT_PLATFORM_META = {
3582
+ github: {
3583
+ cli: "gh",
3584
+ prTerm: "Pull Request",
3585
+ prCommand: "gh pr create",
3586
+ ciDefault: "github-actions"
3587
+ },
3588
+ gitlab: {
3589
+ cli: "glab",
3590
+ prTerm: "Merge Request",
3591
+ prCommand: "glab mr create",
3592
+ ciDefault: "gitlab-ci"
3593
+ },
3594
+ bitbucket: {
3595
+ cli: "bb",
3596
+ prTerm: "Pull Request",
3597
+ prCommand: "bb pr create",
3598
+ ciDefault: "bitbucket-pipelines"
3599
+ },
3600
+ "azure-devops": {
3601
+ cli: "az repos",
3602
+ prTerm: "Pull Request",
3603
+ prCommand: "az repos pr create",
3604
+ ciDefault: "azure-pipelines"
3605
+ },
3606
+ gitea: { cli: "tea", prTerm: "Pull Request", prCommand: "tea pr create", ciDefault: "none" }
3607
+ };
3166
3608
  var CI_PLATFORM_FILES = {
3167
3609
  "github-actions": ".github/workflows",
3168
3610
  "gitlab-ci": ".gitlab-ci.yml",
@@ -3231,7 +3673,7 @@ async function detectInfra(rootDir) {
3231
3673
  }
3232
3674
 
3233
3675
  // src/scanner/detect-services.ts
3234
- import { readFile as readFile10 } from "fs/promises";
3676
+ import { readFile as readFile11 } from "fs/promises";
3235
3677
  import path19 from "path";
3236
3678
  async function detectProjectManagement(rootDir) {
3237
3679
  const tools = [];
@@ -3242,7 +3684,7 @@ async function detectProjectManagement(rootDir) {
3242
3684
  for (const configPath of mcpConfigPaths) {
3243
3685
  if (!await fileExists(configPath)) continue;
3244
3686
  try {
3245
- const raw = await readFile10(configPath, "utf8");
3687
+ const raw = await readFile11(configPath, "utf8");
3246
3688
  const lower = raw.toLowerCase();
3247
3689
  if (lower.includes("clickup")) tools.push("clickup");
3248
3690
  if (lower.includes("jira") || lower.includes("atlassian")) tools.push("jira");
@@ -3625,10 +4067,10 @@ async function executeSafeReadinessFixes(rootDir, options) {
3625
4067
  }
3626
4068
  const gitignoreRelativePath = ".gitignore";
3627
4069
  const gitignorePath = path22.join(beforeScan.rootDir, gitignoreRelativePath);
3628
- const existingGitignore = await fileExists(gitignorePath) ? await readFile11(gitignorePath, "utf8") : "";
4070
+ const existingGitignore = await fileExists(gitignorePath) ? await readFile12(gitignorePath, "utf8") : "";
3629
4071
  const mergedGitignore = mergeSecretIgnores(existingGitignore);
3630
4072
  const gitignoreChanged = mergedGitignore !== existingGitignore;
3631
- if (gitignoreChanged && !dryRun) await writeFile4(gitignorePath, mergedGitignore, "utf8");
4073
+ if (gitignoreChanged && !dryRun) await writeFile5(gitignorePath, mergedGitignore, "utf8");
3632
4074
  recordChange(
3633
4075
  changes,
3634
4076
  "merge-secret-ignores",
@@ -3699,6 +4141,169 @@ async function writeReadinessSnapshot(rootDir, report) {
3699
4141
  return snapshotPath;
3700
4142
  }
3701
4143
 
4144
+ // src/welcome/visual-kit.ts
4145
+ import { cyan as cyan2, gray, options as koloristOptions } from "kolorist";
4146
+ var HELMET_OUTLINE = "#e2e8f0";
4147
+ var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
4148
+ var SPACE_MARKS = {
4149
+ star: "\u2726",
4150
+ tick: "\xB7",
4151
+ diamond: "\u2727",
4152
+ visor: "( )"
4153
+ };
4154
+ var KIT_TIPS = [
4155
+ "Try agent-kit doctor for repository readiness.",
4156
+ "HITL gates stay in Cursor slash commands.",
4157
+ "/run-plan never promotes to production.",
4158
+ "Mission Control is the dashboard, not the CLI name.",
4159
+ "NO_COLOR and CI keep this output static.",
4160
+ "agent-kit --help groups SETUP, MISSION, DASHBOARD, INTEGRITY."
4161
+ ];
4162
+ function shouldUseWelcomeColor(opts = {}) {
4163
+ if (opts.color === false) return false;
4164
+ if (process.env.NO_COLOR) return false;
4165
+ if (process.env.NODE_DISABLE_COLORS) return false;
4166
+ if (process.env.FORCE_COLOR === "0") return false;
4167
+ if (process.env.CI != null && process.env.CI !== "") return false;
4168
+ if (opts.color === true) return true;
4169
+ const tty = opts.stdoutIsTTY ?? Boolean(process.stdout.isTTY);
4170
+ return tty;
4171
+ }
4172
+ function shouldUseVisualMotion(opts = {}) {
4173
+ if (process.env.AGENT_KIT_REDUCED_MOTION === "1") return false;
4174
+ return shouldUseWelcomeColor(opts);
4175
+ }
4176
+ function spinnerFrame(index) {
4177
+ const n = SPINNER_FRAMES.length;
4178
+ return SPINNER_FRAMES[(index % n + n) % n] ?? SPINNER_FRAMES[0];
4179
+ }
4180
+ function tipAt(index) {
4181
+ const n = KIT_TIPS.length;
4182
+ return KIT_TIPS[(index % n + n) % n] ?? KIT_TIPS[0];
4183
+ }
4184
+ function wrapNarrow(text, columns) {
4185
+ const width = Math.max(8, Number.isFinite(columns) ? Math.floor(columns) : 80);
4186
+ const words = text.trim().split(/\s+/).filter(Boolean);
4187
+ if (words.length === 0) return "";
4188
+ const lines = [];
4189
+ let current = "";
4190
+ for (const word of words) {
4191
+ if (word.length > width) {
4192
+ if (current) {
4193
+ lines.push(current);
4194
+ current = "";
4195
+ }
4196
+ for (let i = 0; i < word.length; i += width) {
4197
+ const chunk = word.slice(i, i + width);
4198
+ if (i + width >= word.length) current = chunk;
4199
+ else lines.push(chunk);
4200
+ }
4201
+ continue;
4202
+ }
4203
+ const next = current ? `${current} ${word}` : word;
4204
+ if (next.length > width) {
4205
+ lines.push(current);
4206
+ current = word;
4207
+ } else {
4208
+ current = next;
4209
+ }
4210
+ }
4211
+ if (current) lines.push(current);
4212
+ return lines.join("\n");
4213
+ }
4214
+ function renderProgressLine(opts) {
4215
+ const motion = opts.motion ?? shouldUseVisualMotion();
4216
+ const color = opts.color ?? shouldUseWelcomeColor();
4217
+ const mark = motion ? spinnerFrame(opts.frameIndex) : SPACE_MARKS.tick;
4218
+ const tip = tipAt(opts.tipIndex);
4219
+ const label = opts.label?.trim() ?? "";
4220
+ const raw = label ? `${mark} ${label} ${SPACE_MARKS.star} ${tip}` : `${mark} ${tip}`;
4221
+ const cols = opts.columns ?? process.stdout.columns ?? 80;
4222
+ const wrapped = wrapNarrow(raw, cols);
4223
+ if (!color) return wrapped;
4224
+ const prevEnabled = koloristOptions.enabled;
4225
+ koloristOptions.enabled = true;
4226
+ try {
4227
+ return wrapped.split("\n").map((line, i) => i === 0 ? cyan2(line) : gray(line)).join("\n");
4228
+ } finally {
4229
+ koloristOptions.enabled = prevEnabled;
4230
+ }
4231
+ }
4232
+ var TtySpinner = class {
4233
+ timer = null;
4234
+ frame = 0;
4235
+ startedAt = 0;
4236
+ label = "";
4237
+ write;
4238
+ columns;
4239
+ motion;
4240
+ color;
4241
+ intervalMs;
4242
+ now;
4243
+ constructor(opts = {}) {
4244
+ this.write = opts.write ?? ((chunk) => process.stdout.write(chunk));
4245
+ this.columns = opts.columns ?? (() => process.stdout.columns ?? 80);
4246
+ this.motion = opts.motion ?? shouldUseVisualMotion();
4247
+ this.color = opts.color ?? shouldUseWelcomeColor();
4248
+ this.intervalMs = opts.intervalMs ?? 80;
4249
+ this.now = opts.now ?? (() => Date.now());
4250
+ }
4251
+ start(label = "") {
4252
+ this.stop();
4253
+ this.label = label;
4254
+ this.frame = 0;
4255
+ this.startedAt = this.now();
4256
+ this.paint(false);
4257
+ if (!this.motion) return;
4258
+ this.timer = setInterval(() => {
4259
+ this.frame += 1;
4260
+ this.paint(true);
4261
+ }, this.intervalMs);
4262
+ this.timer.unref?.();
4263
+ }
4264
+ stop(finalLine) {
4265
+ if (this.timer) {
4266
+ clearInterval(this.timer);
4267
+ this.timer = null;
4268
+ }
4269
+ if (this.motion) this.write("\r\x1B[K");
4270
+ if (finalLine) this.write(`${finalLine}
4271
+ `);
4272
+ }
4273
+ paint(overwrite) {
4274
+ const elapsed = this.now() - this.startedAt;
4275
+ const tipIndex = Math.floor(elapsed / 4e3);
4276
+ const line = renderProgressLine({
4277
+ frameIndex: this.frame,
4278
+ tipIndex,
4279
+ columns: this.columns(),
4280
+ label: this.label,
4281
+ motion: this.motion,
4282
+ color: this.color
4283
+ }).split("\n")[0] ?? "";
4284
+ if (overwrite && this.motion) this.write(`\r\x1B[K${line}`);
4285
+ else if (this.motion) this.write(line);
4286
+ else this.write(`${line}
4287
+ `);
4288
+ }
4289
+ };
4290
+ async function withCliProgress(label, fn, opts) {
4291
+ const motion = opts?.motion ?? shouldUseVisualMotion();
4292
+ if (!motion) {
4293
+ return fn();
4294
+ }
4295
+ const spinner = new TtySpinner({ ...opts, motion: true });
4296
+ spinner.start(label);
4297
+ try {
4298
+ const result = await fn();
4299
+ spinner.stop();
4300
+ return result;
4301
+ } catch (err) {
4302
+ spinner.stop();
4303
+ throw err;
4304
+ }
4305
+ }
4306
+
3702
4307
  // src/commands/doctor.ts
3703
4308
  async function runDoctor(cwd, options = {}) {
3704
4309
  const rootDir = path24.resolve(cwd);
@@ -3753,7 +4358,7 @@ function printDoctorSummary(result) {
3753
4358
  var doctorCommand = defineCommand7({
3754
4359
  meta: {
3755
4360
  name: "doctor",
3756
- description: "Diagnose repository readiness and optionally apply safe local fixes."
4361
+ description: "Diagnose repository readiness; optional --fix-safe local repairs."
3757
4362
  },
3758
4363
  args: {
3759
4364
  cwd: {
@@ -3772,7 +4377,8 @@ var doctorCommand = defineCommand7({
3772
4377
  }
3773
4378
  },
3774
4379
  async run({ args }) {
3775
- const result = await runDoctor(args.cwd, { fixSafe: args["fix-safe"] });
4380
+ const run = () => runDoctor(args.cwd, { fixSafe: args["fix-safe"] });
4381
+ const result = args.json ? await run() : await withCliProgress("doctor", run);
3776
4382
  if (args.json) {
3777
4383
  console.log(JSON.stringify(result, null, 2));
3778
4384
  return;
@@ -4044,7 +4650,7 @@ async function detectCurrentBranch() {
4044
4650
  var guardCommand = defineCommand8({
4045
4651
  meta: {
4046
4652
  name: "guard",
4047
- description: "Mechanizable deny/annotate guards (shell, prompt). Hooks are thin adapters."
4653
+ description: "Deny/annotate guards (shell, prompt). Hooks are thin adapters."
4048
4654
  },
4049
4655
  subCommands: {
4050
4656
  shell: defineCommand8({
@@ -4107,8 +4713,8 @@ var guardCommand = defineCommand8({
4107
4713
  });
4108
4714
 
4109
4715
  // src/commands/handoff.ts
4110
- import { spawn as spawn3 } from "child_process";
4111
- import { readFile as readFile12, readdir as readdir4, writeFile as writeFile5 } from "fs/promises";
4716
+ import { spawn as spawn4 } from "child_process";
4717
+ import { readFile as readFile13, readdir as readdir4, writeFile as writeFile6 } from "fs/promises";
4112
4718
  import path25 from "path";
4113
4719
  import { defineCommand as defineCommand9 } from "citty";
4114
4720
  function parsePlanFrontmatter(raw) {
@@ -4129,13 +4735,13 @@ async function findActivePlan(plansDir) {
4129
4735
  if (!await fileExists(plansDir)) return null;
4130
4736
  const files = (await readdir4(plansDir)).filter((f) => f.endsWith(".plan.md")).sort().reverse();
4131
4737
  for (const file of files) {
4132
- const raw = await readFile12(path25.join(plansDir, file), "utf8");
4738
+ const raw = await readFile13(path25.join(plansDir, file), "utf8");
4133
4739
  const fm = parsePlanFrontmatter(raw);
4134
4740
  if (fm?.todos?.some((t) => t.status !== "completed" && t.status !== "cancelled")) {
4135
4741
  return { file, raw };
4136
4742
  }
4137
4743
  }
4138
- return files[0] ? { file: files[0], raw: await readFile12(path25.join(plansDir, files[0]), "utf8") } : null;
4744
+ return files[0] ? { file: files[0], raw: await readFile13(path25.join(plansDir, files[0]), "utf8") } : null;
4139
4745
  }
4140
4746
  function now() {
4141
4747
  return (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 16);
@@ -4218,19 +4824,19 @@ function printV3Guidance() {
4218
4824
  logger.info("Resume in IDE: /resume, summaries and transcripts.");
4219
4825
  }
4220
4826
  function runCursorHandoff(scriptPath, cwd) {
4221
- return new Promise((resolve, reject) => {
4222
- const child = spawn3("sh", [scriptPath, "handoff"], {
4827
+ return new Promise((resolve2, reject) => {
4828
+ const child = spawn4("sh", [scriptPath, "handoff"], {
4223
4829
  cwd,
4224
4830
  stdio: "inherit"
4225
4831
  });
4226
4832
  child.on("error", (err) => reject(err));
4227
- child.on("close", (code) => resolve(code ?? 1));
4833
+ child.on("close", (code) => resolve2(code ?? 1));
4228
4834
  });
4229
4835
  }
4230
4836
  var handoffCommand = defineCommand9({
4231
4837
  meta: {
4232
4838
  name: "handoff",
4233
- description: "Write .cursor/HANDOFF.md from the active Cursor plan, or run ./cursor-handoff handoff when no plan exists."
4839
+ description: "Write .cursor/HANDOFF.md from the active plan (or cursor-handoff fallback)."
4234
4840
  },
4235
4841
  args: {
4236
4842
  cwd: {
@@ -4249,7 +4855,7 @@ var handoffCommand = defineCommand9({
4249
4855
  if (fm) {
4250
4856
  await ensureDir(path25.join(args.cwd, ".cursor"));
4251
4857
  const content = buildHandoff(plan.file, fm, profile);
4252
- await writeFile5(handoffPath, content, "utf8");
4858
+ await writeFile6(handoffPath, content, "utf8");
4253
4859
  logger.success("HANDOFF.md updated: .cursor/HANDOFF.md");
4254
4860
  logger.info(
4255
4861
  `Plan: ${plan.file} (${fm.todos?.filter((t) => t.status === "completed").length}/${fm.todos?.length} completed)`
@@ -4305,8 +4911,8 @@ function buildPreCompactUserMessage(payload = {}) {
4305
4911
  }
4306
4912
 
4307
4913
  // src/hooks/session-start.ts
4308
- import { spawn as spawn4 } from "child_process";
4309
- import { access as access5, readFile as readFile13 } from "fs/promises";
4914
+ import { spawn as spawn5 } from "child_process";
4915
+ import { access as access6, readFile as readFile14 } from "fs/promises";
4310
4916
  import path26 from "path";
4311
4917
 
4312
4918
  // src/invariants/handoff-schema.ts
@@ -4381,7 +4987,7 @@ var NONE_PLACEHOLDERS = /* @__PURE__ */ new Set(["none", "n/a", "empty", "nil"])
4381
4987
  var CURSOR_AWARENESS_SPAWN_TIMEOUT_MS = CHANGELOG_FETCH_TIMEOUT_MS + 3e3;
4382
4988
  async function readTextLimited(filePath, limit = 60) {
4383
4989
  try {
4384
- const lines = (await readFile13(filePath, "utf8")).split(/\r?\n/);
4990
+ const lines = (await readFile14(filePath, "utf8")).split(/\r?\n/);
4385
4991
  return lines.slice(0, limit).join("\n").trim();
4386
4992
  } catch {
4387
4993
  return "";
@@ -4389,14 +4995,14 @@ async function readTextLimited(filePath, limit = 60) {
4389
4995
  }
4390
4996
  async function readFull(filePath) {
4391
4997
  try {
4392
- return await readFile13(filePath, "utf8");
4998
+ return await readFile14(filePath, "utf8");
4393
4999
  } catch {
4394
5000
  return "";
4395
5001
  }
4396
5002
  }
4397
5003
  async function fileExists2(p) {
4398
5004
  try {
4399
- await access5(p);
5005
+ await access6(p);
4400
5006
  return true;
4401
5007
  } catch {
4402
5008
  return false;
@@ -4405,22 +5011,55 @@ async function fileExists2(p) {
4405
5011
  function parseUnprocessedDogfoodItems(readmeText) {
4406
5012
  const items = [];
4407
5013
  let inSection = false;
5014
+ let sectionLevel = 0;
4408
5015
  for (const line of readmeText.split(/\r?\n/)) {
4409
- if (line.startsWith("### Unprocessed Files")) {
5016
+ const unprocessedMatch = /^(#{2,3})\s+Unprocessed Files\b/.exec(line);
5017
+ if (unprocessedMatch) {
5018
+ const hashes = unprocessedMatch[1];
5019
+ if (!hashes) continue;
4410
5020
  inSection = true;
5021
+ sectionLevel = hashes.length;
4411
5022
  continue;
4412
5023
  }
4413
5024
  if (!inSection) continue;
4414
- if (line.startsWith("### ")) break;
4415
- const stripped = line.trim();
4416
- if (!stripped.startsWith("- ")) continue;
4417
- const body = stripped.slice(2).trim();
4418
- const normalized = body.toLowerCase().replace(/[*_]/g, "").trim();
4419
- if (!normalized || NONE_PLACEHOLDERS.has(normalized)) continue;
5025
+ const headingMatch = /^(#{1,6})\s+/.exec(line);
5026
+ if (headingMatch) {
5027
+ if (/\bProcessed Files\b/.test(line)) break;
5028
+ const hashes = headingMatch[1];
5029
+ if (hashes && hashes.length <= sectionLevel) break;
5030
+ continue;
5031
+ }
5032
+ const body = extractUnprocessedDogfoodLine(line);
5033
+ if (!body) continue;
4420
5034
  items.push(body);
4421
5035
  }
4422
5036
  return items;
4423
5037
  }
5038
+ function extractUnprocessedDogfoodLine(line) {
5039
+ const stripped = line.trim();
5040
+ if (!stripped) return null;
5041
+ let raw = null;
5042
+ if (stripped.startsWith("- ")) {
5043
+ raw = stripped.slice(2).trim();
5044
+ } else {
5045
+ const numbered = /^(\d+)[.)]\s+(.+)$/.exec(stripped);
5046
+ if (numbered?.[2]) {
5047
+ raw = numbered[2].trim();
5048
+ } else if (stripped.startsWith("|")) {
5049
+ const parts = stripped.replace(/^\|/, "").replace(/\|$/, "").split("|").map((cell) => cell.trim());
5050
+ if (parts.length === 0) return null;
5051
+ if (parts.every((cell) => /^:?-+:?$/.test(cell))) return null;
5052
+ const first = parts[0] ?? "";
5053
+ const headerish = first.toLowerCase().replace(/[*_`]/g, "").trim();
5054
+ if (/^(note|file|entrada|title|name|item|path)$/.test(headerish)) return null;
5055
+ raw = first.trim();
5056
+ }
5057
+ }
5058
+ if (!raw) return null;
5059
+ const normalized = raw.toLowerCase().replace(/[*_]/g, "").trim();
5060
+ if (!normalized || NONE_PLACEHOLDERS.has(normalized)) return null;
5061
+ return raw;
5062
+ }
4424
5063
  async function l0Present(root) {
4425
5064
  const cursor = path26.join(root, ".cursor");
4426
5065
  return await fileExists2(path26.join(cursor, "agent-kit.json")) || await fileExists2(path26.join(cursor, "commands", "agent-kit-onboard.md")) || await fileExists2(path26.join(cursor, "commands", "start-project.md"));
@@ -4465,7 +5104,7 @@ async function readinessSection(root) {
4465
5104
  const snapshotPath = path26.join(root, ".cursor", "context", "readiness.json");
4466
5105
  let data;
4467
5106
  try {
4468
- data = JSON.parse(await readFile13(snapshotPath, "utf8"));
5107
+ data = JSON.parse(await readFile14(snapshotPath, "utf8"));
4469
5108
  } catch {
4470
5109
  return null;
4471
5110
  }
@@ -4502,7 +5141,7 @@ async function dogfoodInboxSection(root) {
4502
5141
  for (const readme of candidateReadmes) {
4503
5142
  if (!await fileExists2(readme)) continue;
4504
5143
  try {
4505
- const text = await readFile13(readme, "utf8");
5144
+ const text = await readFile14(readme, "utf8");
4506
5145
  if (parseUnprocessedDogfoodItems(text).length) return DOGFOOD_INBOX_HINT;
4507
5146
  } catch {
4508
5147
  }
@@ -4512,7 +5151,7 @@ async function dogfoodInboxSection(root) {
4512
5151
  async function loadUpdateCheckPrefs(root) {
4513
5152
  try {
4514
5153
  const data = JSON.parse(
4515
- await readFile13(path26.join(root, ".cursor", "context", "config.json"), "utf8")
5154
+ await readFile14(path26.join(root, ".cursor", "context", "config.json"), "utf8")
4516
5155
  );
4517
5156
  const uc = data.updateCheck;
4518
5157
  if (!uc || typeof uc !== "object" || uc.enabled !== true) {
@@ -4524,8 +5163,8 @@ async function loadUpdateCheckPrefs(root) {
4524
5163
  }
4525
5164
  }
4526
5165
  function runUpdateCheckJson(root) {
4527
- return new Promise((resolve) => {
4528
- const child = spawn4(
5166
+ return new Promise((resolve2) => {
5167
+ const child = spawn5(
4529
5168
  process.execPath,
4530
5169
  [
4531
5170
  process.argv[1] ?? "",
@@ -4543,21 +5182,21 @@ function runUpdateCheckJson(root) {
4543
5182
  child.stdout?.on("data", (chunk) => {
4544
5183
  out += chunk.toString("utf8");
4545
5184
  });
4546
- child.on("error", () => resolve(null));
5185
+ child.on("error", () => resolve2(null));
4547
5186
  child.on("close", () => {
4548
5187
  try {
4549
5188
  const parsed = JSON.parse(out.trim());
4550
- resolve(parsed && typeof parsed === "object" ? parsed : null);
5189
+ resolve2(parsed && typeof parsed === "object" ? parsed : null);
4551
5190
  } catch {
4552
- resolve(null);
5191
+ resolve2(null);
4553
5192
  }
4554
5193
  });
4555
5194
  });
4556
5195
  }
4557
5196
  async function updateCheckSection(root) {
4558
5197
  if (await loadUpdateCheckPrefs(root) === null) return null;
4559
- const result = await new Promise((resolve) => {
4560
- const child = spawn4(
5198
+ const result = await new Promise((resolve2) => {
5199
+ const child = spawn5(
4561
5200
  "agent-kit",
4562
5201
  ["update", "--check", "--json", "--respect-prefs", "--stamp", "--cwd", root],
4563
5202
  { stdio: ["ignore", "pipe", "ignore"], timeout: 12e3, shell: false }
@@ -4567,17 +5206,17 @@ async function updateCheckSection(root) {
4567
5206
  out += chunk.toString("utf8");
4568
5207
  });
4569
5208
  child.on("error", () => {
4570
- void runUpdateCheckJson(root).then(resolve);
5209
+ void runUpdateCheckJson(root).then(resolve2);
4571
5210
  });
4572
5211
  child.on("close", (code) => {
4573
5212
  if (code !== 0 && !out.trim()) {
4574
- void runUpdateCheckJson(root).then(resolve);
5213
+ void runUpdateCheckJson(root).then(resolve2);
4575
5214
  return;
4576
5215
  }
4577
5216
  try {
4578
- resolve(JSON.parse(out.trim()));
5217
+ resolve2(JSON.parse(out.trim()));
4579
5218
  } catch {
4580
- void runUpdateCheckJson(root).then(resolve);
5219
+ void runUpdateCheckJson(root).then(resolve2);
4581
5220
  }
4582
5221
  });
4583
5222
  });
@@ -4590,7 +5229,7 @@ async function updateCheckSection(root) {
4590
5229
  async function loadCursorUpdateCheckPrefs(root) {
4591
5230
  try {
4592
5231
  const data = JSON.parse(
4593
- await readFile13(path26.join(root, ".cursor", "context", "config.json"), "utf8")
5232
+ await readFile14(path26.join(root, ".cursor", "context", "config.json"), "utf8")
4594
5233
  );
4595
5234
  const uc = data.cursorUpdateCheck;
4596
5235
  if (!uc || typeof uc !== "object" || uc.enabled !== true) {
@@ -4602,8 +5241,8 @@ async function loadCursorUpdateCheckPrefs(root) {
4602
5241
  }
4603
5242
  }
4604
5243
  function runCursorAwarenessJson(root) {
4605
- return new Promise((resolve) => {
4606
- const child = spawn4(
5244
+ return new Promise((resolve2) => {
5245
+ const child = spawn5(
4607
5246
  process.execPath,
4608
5247
  [
4609
5248
  process.argv[1] ?? "",
@@ -4621,13 +5260,13 @@ function runCursorAwarenessJson(root) {
4621
5260
  child.stdout?.on("data", (chunk) => {
4622
5261
  out += chunk.toString("utf8");
4623
5262
  });
4624
- child.on("error", () => resolve(null));
5263
+ child.on("error", () => resolve2(null));
4625
5264
  child.on("close", () => {
4626
5265
  try {
4627
5266
  const parsed = JSON.parse(out.trim());
4628
- resolve(parsed && typeof parsed === "object" ? parsed : null);
5267
+ resolve2(parsed && typeof parsed === "object" ? parsed : null);
4629
5268
  } catch {
4630
- resolve(null);
5269
+ resolve2(null);
4631
5270
  }
4632
5271
  });
4633
5272
  });
@@ -4642,15 +5281,15 @@ function shouldEmitCursorAwarenessNudge(result) {
4642
5281
  }
4643
5282
  async function cursorAwarenessSection(root, deps = {}) {
4644
5283
  if (await loadCursorUpdateCheckPrefs(root) === null) return null;
4645
- const spawnFn = deps.spawnFn ?? spawn4;
5284
+ const spawnFn = deps.spawnFn ?? spawn5;
4646
5285
  const runFallback = deps.runFallback ?? runCursorAwarenessJson;
4647
- const result = await new Promise((resolve) => {
5286
+ const result = await new Promise((resolve2) => {
4648
5287
  let settled = false;
4649
5288
  let fallbackStarted = false;
4650
5289
  const finish = (value) => {
4651
5290
  if (settled) return;
4652
5291
  settled = true;
4653
- resolve(value);
5292
+ resolve2(value);
4654
5293
  };
4655
5294
  const startFallback = () => {
4656
5295
  if (settled || fallbackStarted) return;
@@ -4739,7 +5378,7 @@ function resolveSessionRoot(payload, cwd = process.cwd()) {
4739
5378
  var hookCommand = defineCommand10({
4740
5379
  meta: {
4741
5380
  name: "hook",
4742
- description: "Cursor hook adapters (session-start, pre-compact). CLI is SoT; thin hooks shell out here."
5381
+ description: "Cursor hook adapters (session-start, pre-compact). CLI is SoT."
4743
5382
  },
4744
5383
  subCommands: {
4745
5384
  "session-start": defineCommand10({
@@ -4778,24 +5417,294 @@ var hookCommand = defineCommand10({
4778
5417
  import { intro, outro } from "@clack/prompts";
4779
5418
  import { defineCommand as defineCommand12 } from "citty";
4780
5419
 
4781
- // src/commands/install.ts
4782
- import path30 from "path";
4783
- import { defineCommand as defineCommand11 } from "citty";
4784
-
4785
- // src/generator/personalization.ts
4786
- import { readFile as readFile14, writeFile as writeFile6 } from "fs/promises";
5420
+ // src/utils/terminal.ts
5421
+ import { homedir as homedir2 } from "os";
4787
5422
  import path28 from "path";
4788
- var PERSONALIZATION_CONTRACT_VERSION = 1;
4789
- var CONTEXT_PATH = ".cursor/project-context.md";
4790
- var AGENTS_PATH = "AGENTS.md";
4791
- var RESULT_PATH = ".cursor/context/personalization.json";
4792
- function uniqueEvidence(evidence) {
4793
- const seen = /* @__PURE__ */ new Set();
4794
- return evidence.filter((item) => {
4795
- const key = `${item.source}:${item.value}`;
4796
- if (seen.has(key)) return false;
4797
- seen.add(key);
4798
- return true;
5423
+ import { confirm, isCancel } from "@clack/prompts";
5424
+ function isNonInteractive() {
5425
+ if (process.env.CI === "true" || process.env.CI === "1") return true;
5426
+ if (process.env.AGENT_KIT_YES === "1") return true;
5427
+ return !process.stdin.isTTY;
5428
+ }
5429
+ async function validateProjectRoot(resolved) {
5430
+ const home = path28.resolve(homedir2());
5431
+ if (resolved === "/" || resolved === home) {
5432
+ return { ok: false, reason: `Refused to use ${resolved} as a project root.` };
5433
+ }
5434
+ const hasGit = await fileExists(path28.join(resolved, ".git"));
5435
+ const hasManifest = await fileExists(path28.join(resolved, ".cursor", "agent-kit.json"));
5436
+ if (!hasGit && !hasManifest) {
5437
+ return {
5438
+ ok: false,
5439
+ reason: `Refused ${resolved}: no .git and no .cursor/agent-kit.json. Run from a project directory or use --force-root.`
5440
+ };
5441
+ }
5442
+ return { ok: true };
5443
+ }
5444
+ async function confirmProjectRoot(cwd, opts) {
5445
+ const resolved = path28.resolve(cwd);
5446
+ if (opts.forceRoot) {
5447
+ return resolved;
5448
+ }
5449
+ const validation = await validateProjectRoot(resolved);
5450
+ if (!validation.ok) {
5451
+ if (opts.nonInteractive) {
5452
+ throw new RootRefusedError(resolved, validation.reason);
5453
+ }
5454
+ const ok2 = await confirm({
5455
+ message: `${validation.reason} Proceed anyway?`,
5456
+ initialValue: false
5457
+ });
5458
+ if (isCancel(ok2) || !ok2) {
5459
+ throw new RootRefusedError(resolved, validation.reason);
5460
+ }
5461
+ return resolved;
5462
+ }
5463
+ if (opts.nonInteractive) {
5464
+ return resolved;
5465
+ }
5466
+ const ok = await confirm({
5467
+ message: `${opts.command === "install" ? "Install" : "Update"} Agent Kit in: ${resolved}`,
5468
+ initialValue: true
5469
+ });
5470
+ if (isCancel(ok) || !ok) {
5471
+ throw new RootRefusedError(resolved);
5472
+ }
5473
+ return resolved;
5474
+ }
5475
+ var RootRefusedError = class extends Error {
5476
+ constructor(root, reason) {
5477
+ super(reason ?? `Refused to write into ${root}. Re-run from the correct project directory.`);
5478
+ this.root = root;
5479
+ this.name = "RootRefusedError";
5480
+ }
5481
+ root;
5482
+ };
5483
+ function classifyInstallError(err) {
5484
+ const msg = err instanceof Error ? err.message : String(err);
5485
+ const code = err?.code;
5486
+ if (code === "EPERM" || code === "EACCES" || /EPERM|EACCES/.test(msg)) {
5487
+ return {
5488
+ kind: "eperm",
5489
+ message: `Permission error: ${msg}`,
5490
+ recovery: [
5491
+ "The npm cache may have ownership drift (root-written files in a user dir).",
5492
+ "Recovery options:",
5493
+ " 1. npx --cache .npm-cache @dadado/agent-kit-cli install",
5494
+ " 2. npm cache clean --force && npx @dadado/agent-kit-cli install",
5495
+ " 3. Use Port B fallback: drag install.md into the Cursor chat"
5496
+ ].join("\n")
5497
+ };
5498
+ }
5499
+ if (/403|Forbidden|unauthorized|E401|ENEEDAUTH/.test(msg)) {
5500
+ return {
5501
+ kind: "registry-auth",
5502
+ message: `Registry access denied: ${msg}`,
5503
+ recovery: [
5504
+ "The npm registry returned 403/401 for the scoped package.",
5505
+ "Recovery options:",
5506
+ " 1. Check npm auth: npm whoami (login if needed: npm login)",
5507
+ " 2. If using a private registry, verify .npmrc scope config",
5508
+ " 3. Use Port B fallback: drag install.md into the Cursor chat"
5509
+ ].join("\n")
5510
+ };
5511
+ }
5512
+ if (/ENOTFOUND|ETIMEDOUT|ECONNREFUSED|ECONNRESET|EAI_AGAIN|fetch failed/.test(msg) || code === "ENOTFOUND" || code === "ETIMEDOUT") {
5513
+ return {
5514
+ kind: "network",
5515
+ message: `Network error: ${msg}`,
5516
+ recovery: [
5517
+ "Could not reach the registry or git remote.",
5518
+ "Recovery options:",
5519
+ " 1. Check network/proxy/VPN settings",
5520
+ " 2. Retry: npx @dadado/agent-kit-cli install",
5521
+ " 3. Use --registry <local-path> if you have a local checkout"
5522
+ ].join("\n")
5523
+ };
5524
+ }
5525
+ return {
5526
+ kind: "unknown",
5527
+ message: msg,
5528
+ recovery: "Unexpected error. Check the message above and retry, or use Port B fallback."
5529
+ };
5530
+ }
5531
+
5532
+ // src/commands/install.ts
5533
+ import path33 from "path";
5534
+ import { defineCommand as defineCommand11 } from "citty";
5535
+
5536
+ // src/generator/personalization.ts
5537
+ import { readFile as readFile15, writeFile as writeFile9 } from "fs/promises";
5538
+ import path31 from "path";
5539
+
5540
+ // src/generator/claude-kit-load.ts
5541
+ import { writeFile as writeFile7 } from "fs/promises";
5542
+ import path29 from "path";
5543
+ var CLAUDE_MD_REL = "CLAUDE.md";
5544
+ var AGENT_KIT_COMMAND_REL = ".claude/commands/agent-kit.md";
5545
+ function renderClaudeMd() {
5546
+ return `# Agent Kit (Claude Code)
5547
+
5548
+ This repository uses Agent Kit / Mission Control. Before rediscovering the tree, read the shared sources of truth (paths below are pointers; do not treat this file as a second rulebook).
5549
+
5550
+ ## Read first
5551
+
5552
+ 1. \`AGENTS.md\` - cross-IDE contract
5553
+ 2. \`.cursor/project-context.md\` - verified repository facts (derived; prefer code, tests, SHAs when docs conflict)
5554
+ 3. \`.cursor/HANDOFF.md\` - if present: active plan, next to-do, queue fields
5555
+ 4. \`.cursor/commands/\` - slash catalog (Cursor). Follow the same HITL contracts here.
5556
+
5557
+ Mid-session refresh: \`/agent-kit\`.
5558
+
5559
+ ## HITL
5560
+
5561
+ Cursor Ask questions is not available in this CLI. When a command requires a choice, list the same labels as a numbered list and wait. Skip or cancel means stop. Never \`/git-prod\` without an explicit operator yes.
5562
+
5563
+ ## Non-goals
5564
+
5565
+ - Not Action A7 (Windsurf / VS Code generator parity)
5566
+ - Not Claude external plan-review audits (\`/plan-external-review\`)
5567
+ - Not \`--backend claude\` plan-loop ticks
5568
+ - Not a copy of Cursor \`sessionStart\` / other IDE hooks
5569
+ `;
5570
+ }
5571
+ function renderAgentKitCommand() {
5572
+ return `---
5573
+ description: Load Agent Kit session context (HANDOFF, project-context, commands). Manual refresh only.
5574
+ disable-model-invocation: true
5575
+ ---
5576
+
5577
+ Read these files if they exist, then summarize the active plan, next to-do, and any Gaps. Do not scan the whole repository first.
5578
+
5579
+ 1. \`AGENTS.md\`
5580
+ 2. \`.cursor/project-context.md\`
5581
+ 3. \`.cursor/HANDOFF.md\`
5582
+ 4. The plan file named in HANDOFF \`- **Plan:**\` under \`.cursor/plans/\`
5583
+
5584
+ If HANDOFF is missing, say so and point at \`/agent-kit-onboard\` or \`/start-project\` rather than inventing a plan.
5585
+
5586
+ HITL: numbered-list fallback for Ask questions labels. Never \`/git-prod\` from this skill.
5587
+
5588
+ Non-goals: not audits / \`/plan-external-review\`, not \`--backend claude\` ticks, not A7, not Cursor hook clones.
5589
+ `;
5590
+ }
5591
+ async function writeUnlessExists(rootDir, relativePath, content) {
5592
+ const target = path29.join(rootDir, relativePath);
5593
+ if (await fileExists(target)) {
5594
+ return { relativePath, status: "skipped-customized" };
5595
+ }
5596
+ await ensureDir(path29.dirname(target));
5597
+ await writeFile7(target, content, "utf8");
5598
+ return { relativePath, status: "applied" };
5599
+ }
5600
+ async function generateClaudeKitLoadArtifacts(rootDir) {
5601
+ return Promise.all([
5602
+ writeUnlessExists(rootDir, CLAUDE_MD_REL, renderClaudeMd()),
5603
+ writeUnlessExists(rootDir, AGENT_KIT_COMMAND_REL, renderAgentKitCommand())
5604
+ ]);
5605
+ }
5606
+
5607
+ // src/generator/vscode.ts
5608
+ import { writeFile as writeFile8 } from "fs/promises";
5609
+ import path30 from "path";
5610
+
5611
+ // src/generator/platform.ts
5612
+ function gitProviderLabel(profile) {
5613
+ const p = profile.git.provider;
5614
+ if (!p || p === "other") return "Git";
5615
+ const labels = {
5616
+ github: "GitHub",
5617
+ gitlab: "GitLab",
5618
+ bitbucket: "Bitbucket",
5619
+ "azure-devops": "Azure DevOps",
5620
+ gitea: "Gitea"
5621
+ };
5622
+ return labels[p] ?? "Git";
5623
+ }
5624
+ function prTerminology(profile) {
5625
+ const p = profile.git.provider;
5626
+ if (!p || p === "other") return "PR/MR";
5627
+ return GIT_PLATFORM_META[p]?.prTerm ?? "PR/MR";
5628
+ }
5629
+
5630
+ // src/generator/vscode.ts
5631
+ async function generateVSCodeArtifacts(profile) {
5632
+ const results = [];
5633
+ const vscodeDir = path30.join(profile.rootDir, ".vscode");
5634
+ const githubDir = path30.join(profile.rootDir, ".github");
5635
+ await Promise.all([ensureDir(vscodeDir), ensureDir(githubDir)]);
5636
+ const settingsPath = path30.join(vscodeDir, "settings.json");
5637
+ if (await fileExists(settingsPath)) {
5638
+ results.push({ relativePath: ".vscode/settings.json", status: "skipped-customized" });
5639
+ } else {
5640
+ await writeFile8(
5641
+ settingsPath,
5642
+ `${JSON.stringify(
5643
+ {
5644
+ "editor.formatOnSave": true,
5645
+ "editor.codeActionsOnSave": {
5646
+ "source.fixAll": "explicit"
5647
+ },
5648
+ "files.autoSave": "afterDelay"
5649
+ },
5650
+ null,
5651
+ 2
5652
+ )}
5653
+ `,
5654
+ "utf8"
5655
+ );
5656
+ results.push({ relativePath: ".vscode/settings.json", status: "applied" });
5657
+ }
5658
+ const provider = gitProviderLabel(profile);
5659
+ const prTerm = prTerminology(profile);
5660
+ const copilotPath = path30.join(githubDir, "copilot-instructions.md");
5661
+ if (await fileExists(copilotPath)) {
5662
+ results.push({ relativePath: ".github/copilot-instructions.md", status: "skipped-customized" });
5663
+ } else {
5664
+ await writeFile8(
5665
+ copilotPath,
5666
+ `# Copilot Instructions
5667
+
5668
+ - Keep code changes small and testable.
5669
+ - Use Conventional Commits (feat:, fix:, docs:, etc.).
5670
+ - Prefer security-safe defaults.
5671
+ - Git platform: ${provider}. Always create a ${prTerm} for review.
5672
+ `,
5673
+ "utf8"
5674
+ );
5675
+ results.push({ relativePath: ".github/copilot-instructions.md", status: "applied" });
5676
+ }
5677
+ if (profile.ide.plan === "vscode-pro") {
5678
+ const securityPath = path30.join(vscodeDir, "security-review.agent.md");
5679
+ if (await fileExists(securityPath)) {
5680
+ results.push({
5681
+ relativePath: ".vscode/security-review.agent.md",
5682
+ status: "skipped-customized"
5683
+ });
5684
+ } else {
5685
+ await writeFile8(
5686
+ securityPath,
5687
+ "# Security Review Agent\n\nSpecialized mode for security review.\n",
5688
+ "utf8"
5689
+ );
5690
+ results.push({ relativePath: ".vscode/security-review.agent.md", status: "applied" });
5691
+ }
5692
+ }
5693
+ return results;
5694
+ }
5695
+
5696
+ // src/generator/personalization.ts
5697
+ var PERSONALIZATION_CONTRACT_VERSION = 1;
5698
+ var CONTEXT_PATH = ".cursor/project-context.md";
5699
+ var AGENTS_PATH = "AGENTS.md";
5700
+ var RESULT_PATH = ".cursor/context/personalization.json";
5701
+ function uniqueEvidence(evidence) {
5702
+ const seen = /* @__PURE__ */ new Set();
5703
+ return evidence.filter((item) => {
5704
+ const key = `${item.source}:${item.value}`;
5705
+ if (seen.has(key)) return false;
5706
+ seen.add(key);
5707
+ return true;
4799
5708
  });
4800
5709
  }
4801
5710
  function purposeEvidence(profile) {
@@ -4928,6 +5837,16 @@ function renderProjectContext(profile) {
4928
5837
  if (profile.infra.ci !== "none" && profile.infra.ciFiles.length > 0) {
4929
5838
  sections.push(`- CI: ${profile.infra.ci}.`);
4930
5839
  }
5840
+ sections.push(
5841
+ "",
5842
+ "## Relevant skills",
5843
+ "",
5844
+ "Installed and project-owned skills for this repository. Prefer `.cursor/agent-kit.json` `skills[]` as the install index; project-only domain skills may live under `.cursor/skills/domain/`.",
5845
+ "",
5846
+ "| Skill / path | Role | Evidence |",
5847
+ "|--------------|------|----------|",
5848
+ "| (none yet) | Add rows when `/agent-kit-onboard` scaffolds domain skills or personalization installs packs | \u2014 |"
5849
+ );
4931
5850
  if (profile.context.sources.length > 0) {
4932
5851
  sections.push("", "## Sources", ...profile.context.sources.map((item) => `- ${item.value}`));
4933
5852
  }
@@ -4935,7 +5854,7 @@ function renderProjectContext(profile) {
4935
5854
  `;
4936
5855
  }
4937
5856
  async function createOwnedFile(rootDir, relativePath, content, evidence) {
4938
- const target = path28.join(rootDir, relativePath);
5857
+ const target = path31.join(rootDir, relativePath);
4939
5858
  if (await fileExists(target)) {
4940
5859
  return {
4941
5860
  kind: "file",
@@ -4945,8 +5864,8 @@ async function createOwnedFile(rootDir, relativePath, content, evidence) {
4945
5864
  evidence
4946
5865
  };
4947
5866
  }
4948
- await ensureDir(path28.dirname(target));
4949
- await writeFile6(target, content, "utf8");
5867
+ await ensureDir(path31.dirname(target));
5868
+ await writeFile9(target, content, "utf8");
4950
5869
  return {
4951
5870
  kind: "file",
4952
5871
  id: relativePath,
@@ -4962,7 +5881,7 @@ async function packTargets(registryRoot, packId) {
4962
5881
  async function existingTargets(projectRoot, targets) {
4963
5882
  const checks = await Promise.all(
4964
5883
  targets.map(
4965
- async (target) => await fileExists(path28.join(projectRoot, target)) ? target : null
5884
+ async (target) => await fileExists(path31.join(projectRoot, target)) ? target : null
4966
5885
  )
4967
5886
  );
4968
5887
  return checks.filter((target) => target !== null);
@@ -4984,14 +5903,14 @@ async function applyPersonalization(input) {
4984
5903
  componentResults.push({ ...item, status: "unavailable" });
4985
5904
  continue;
4986
5905
  }
4987
- const target = path28.posix.join(
5906
+ const target = path31.posix.join(
4988
5907
  ".cursor",
4989
5908
  "skills",
4990
5909
  skill.path.includes("/core/") ? "core" : "community",
4991
5910
  skill.id,
4992
5911
  "SKILL.md"
4993
5912
  );
4994
- if (await fileExists(path28.join(input.rootDir, target))) {
5913
+ if (await fileExists(path31.join(input.rootDir, target))) {
4995
5914
  componentResults.push({ ...item, status: "skipped-customized", path: target });
4996
5915
  protectedPaths.add(target);
4997
5916
  continue;
@@ -5039,14 +5958,68 @@ async function applyPersonalization(input) {
5039
5958
  ]);
5040
5959
  protectedPaths.add(CONTEXT_PATH);
5041
5960
  protectedPaths.add(AGENTS_PATH);
5961
+ const claudeResults = await generateClaudeKitLoadArtifacts(input.rootDir);
5962
+ const claudeItems = claudeResults.map((artifact) => {
5963
+ protectedPaths.add(artifact.relativePath);
5964
+ return {
5965
+ kind: "file",
5966
+ id: artifact.relativePath,
5967
+ path: artifact.relativePath,
5968
+ status: artifact.status,
5969
+ evidence: profileEvidence
5970
+ };
5971
+ });
5972
+ const ideDetection = await detectIde(input.rootDir);
5973
+ if (ideDetection.ide === "vscode" || ideDetection.ide === "other") {
5974
+ const git = {
5975
+ providerKind: input.profile.git.providerKind ?? "unknown",
5976
+ providerConfidence: input.profile.git.providerConfidence ?? "low",
5977
+ providerEvidence: input.profile.git.providerEvidence ?? [],
5978
+ remotes: input.profile.git.remotes ?? [],
5979
+ mode: input.profile.git.mode ?? "none",
5980
+ workflow: input.profile.git.workflow ?? "unknown",
5981
+ isDirty: input.profile.git.isDirty ?? false,
5982
+ hasLocalStaging: input.profile.git.hasLocalStaging ?? false,
5983
+ hasRemoteStaging: input.profile.git.hasRemoteStaging ?? false,
5984
+ provider: input.profile.git.provider,
5985
+ remoteUrl: input.profile.git.remoteUrl,
5986
+ remoteName: input.profile.git.remoteName,
5987
+ currentBranch: input.profile.git.currentBranch,
5988
+ defaultBranch: input.profile.git.defaultBranch
5989
+ };
5990
+ const projectProfile = {
5991
+ rootDir: input.rootDir,
5992
+ stack: input.profile.stack,
5993
+ git,
5994
+ ide: ideDetection,
5995
+ infra: input.profile.infra,
5996
+ services: input.profile.services,
5997
+ installHooks: false,
5998
+ selectedCoreComponents: []
5999
+ };
6000
+ const vscodeResults = await generateVSCodeArtifacts(projectProfile);
6001
+ const ideEvidence = [
6002
+ { source: "derived", value: `ide:${ideDetection.ide}` }
6003
+ ];
6004
+ for (const artifact of vscodeResults) {
6005
+ protectedPaths.add(artifact.relativePath);
6006
+ componentResults.push({
6007
+ kind: "file",
6008
+ id: artifact.relativePath,
6009
+ path: artifact.relativePath,
6010
+ status: artifact.status,
6011
+ evidence: ideEvidence
6012
+ });
6013
+ }
6014
+ }
5042
6015
  const result = {
5043
6016
  contractVersion: PERSONALIZATION_CONTRACT_VERSION,
5044
6017
  generatorVersion: input.generatorVersion,
5045
6018
  repositoryFingerprint: input.report.repositoryFingerprint,
5046
- items: [...fileResults, ...componentResults],
6019
+ items: [...fileResults, ...claudeItems, ...componentResults],
5047
6020
  protectedPaths: [...protectedPaths].sort()
5048
6021
  };
5049
- await writeJson(path28.join(input.rootDir, RESULT_PATH), result);
6022
+ await writeJson(path31.join(input.rootDir, RESULT_PATH), result);
5050
6023
  return {
5051
6024
  result,
5052
6025
  manifest: {
@@ -5064,26 +6037,26 @@ async function applyPersonalization(input) {
5064
6037
  };
5065
6038
  }
5066
6039
  async function readRepositoryProfile(rootDir) {
5067
- const target = path28.join(rootDir, ".cursor/agent-kit.config.json");
6040
+ const target = path31.join(rootDir, ".cursor/agent-kit.config.json");
5068
6041
  if (!await fileExists(target)) return null;
5069
- return JSON.parse(await readFile14(target, "utf8"));
6042
+ return JSON.parse(await readFile15(target, "utf8"));
5070
6043
  }
5071
6044
 
5072
6045
  // src/lifecycle/onboard-migration.ts
5073
6046
  import { createHash as createHash4 } from "crypto";
5074
- import { readFile as readFile15, unlink } from "fs/promises";
5075
- import path29 from "path";
6047
+ import { readFile as readFile16, unlink } from "fs/promises";
6048
+ import path32 from "path";
5076
6049
  var LEGACY_ONBOARD_PATH = ".cursor/commands/onboard.md";
5077
6050
  var NAMESPACED_ONBOARD_PATH = ".cursor/commands/agent-kit-onboard.md";
5078
6051
  var MANAGED_LEGACY_HASHES = /* @__PURE__ */ new Set([
5079
6052
  "b274a68941813f19b185893cb7c5561dff027f53270890029992f208e24992fe"
5080
6053
  ]);
5081
6054
  async function migrateLegacyOnboardCommand(projectRoot, managedHashes = MANAGED_LEGACY_HASHES) {
5082
- const legacyPath = path29.join(projectRoot, LEGACY_ONBOARD_PATH);
6055
+ const legacyPath = path32.join(projectRoot, LEGACY_ONBOARD_PATH);
5083
6056
  if (!await fileExists(legacyPath)) return "absent";
5084
- const namespacedPath = path29.join(projectRoot, NAMESPACED_ONBOARD_PATH);
6057
+ const namespacedPath = path32.join(projectRoot, NAMESPACED_ONBOARD_PATH);
5085
6058
  if (!await fileExists(namespacedPath)) return "preserved-customized";
5086
- const content = await readFile15(legacyPath);
6059
+ const content = await readFile16(legacyPath);
5087
6060
  const hash = createHash4("sha256").update(content).digest("hex");
5088
6061
  if (!managedHashes.has(hash)) return "preserved-customized";
5089
6062
  await unlink(legacyPath);
@@ -5156,7 +6129,7 @@ function printReadinessNarrative(result) {
5156
6129
  );
5157
6130
  }
5158
6131
  async function performInstall(options) {
5159
- const projectRoot = path30.resolve(options.cwd);
6132
+ const projectRoot = path33.resolve(options.cwd);
5160
6133
  const packs = parsePackList(options.pack);
5161
6134
  const existing = await loadAgentKitManifest(projectRoot);
5162
6135
  const registry = await resolveRegistryFromCli({
@@ -5167,53 +6140,57 @@ async function performInstall(options) {
5167
6140
  refresh: options.refresh,
5168
6141
  manifest: existing
5169
6142
  });
5170
- const draft = buildManifest({
5171
- version: KIT_VERSION,
5172
- profile: options.profile ?? existing?.profile ?? "default",
5173
- packs: packs.length > 0 ? packs : existing?.packs,
5174
- skills: existing?.skills,
5175
- protected: existing?.protected,
5176
- personalization: existing?.personalization,
5177
- registryUrl: registry.url ?? existing?.registry?.url,
5178
- registryRef: registry.ref ?? existing?.registry?.ref
5179
- });
5180
- const stats = (draft.packs?.length ?? 0) > 0 || (draft.skills?.length ?? 0) > 0 ? await syncFromManifest(registry.root, projectRoot, draft) : await installL0(registry.root, projectRoot, resolveProtectedGlobs(draft));
5181
- const manifestPath = await saveManifest(projectRoot, draft);
5182
- const readinessExecution = await executeSafeReadinessFixes(projectRoot, {
5183
- generatorVersion: KIT_VERSION
5184
- });
5185
- let readiness = readinessExecution.after;
5186
- const profile = await readRepositoryProfile(projectRoot);
5187
- if (profile) {
5188
- const registryIndex = await loadRegistry(registry.root);
5189
- const personalization = await applyPersonalization({
5190
- rootDir: projectRoot,
5191
- registryRoot: registry.root,
5192
- profile,
5193
- report: readinessExecution.after,
5194
- registry: registryIndex,
5195
- manifest: draft,
5196
- generatorVersion: KIT_VERSION
6143
+ try {
6144
+ const draft = buildManifest({
6145
+ version: KIT_VERSION,
6146
+ profile: options.profile ?? existing?.profile ?? "default",
6147
+ packs: packs.length > 0 ? packs : existing?.packs,
6148
+ skills: existing?.skills,
6149
+ protected: existing?.protected,
6150
+ personalization: existing?.personalization,
6151
+ registryUrl: registry.url ?? existing?.registry?.url,
6152
+ registryRef: registry.ref ?? existing?.registry?.ref
5197
6153
  });
5198
- await saveManifest(projectRoot, personalization.manifest);
5199
- readiness = createReadinessReport(await runScanner(projectRoot), {
6154
+ const stats = (draft.packs?.length ?? 0) > 0 || (draft.skills?.length ?? 0) > 0 ? await syncFromManifest(registry.root, projectRoot, draft) : await installL0(registry.root, projectRoot, resolveProtectedGlobs(draft));
6155
+ const manifestPath = await saveManifest(projectRoot, draft);
6156
+ const readinessExecution = await executeSafeReadinessFixes(projectRoot, {
5200
6157
  generatorVersion: KIT_VERSION
5201
6158
  });
5202
- readiness.appliedSafeFixes = readinessExecution.after.appliedSafeFixes;
6159
+ let readiness = readinessExecution.after;
6160
+ const profile = await readRepositoryProfile(projectRoot);
6161
+ if (profile) {
6162
+ const registryIndex = await loadRegistry(registry.root);
6163
+ const personalization = await applyPersonalization({
6164
+ rootDir: projectRoot,
6165
+ registryRoot: registry.root,
6166
+ profile,
6167
+ report: readinessExecution.after,
6168
+ registry: registryIndex,
6169
+ manifest: draft,
6170
+ generatorVersion: KIT_VERSION
6171
+ });
6172
+ await saveManifest(projectRoot, personalization.manifest);
6173
+ readiness = createReadinessReport(await runScanner(projectRoot), {
6174
+ generatorVersion: KIT_VERSION
6175
+ });
6176
+ readiness.appliedSafeFixes = readinessExecution.after.appliedSafeFixes;
6177
+ }
6178
+ await writeReadinessSnapshot(projectRoot, readiness);
6179
+ return {
6180
+ projectRoot,
6181
+ manifestPath,
6182
+ stats,
6183
+ readiness,
6184
+ safeChanges: readinessExecution.changes
6185
+ };
6186
+ } finally {
6187
+ await registry.unlock?.();
5203
6188
  }
5204
- await writeReadinessSnapshot(projectRoot, readiness);
5205
- return {
5206
- projectRoot,
5207
- manifestPath,
5208
- stats,
5209
- readiness,
5210
- safeChanges: readinessExecution.changes
5211
- };
5212
6189
  }
5213
6190
  var installCommand = defineCommand11({
5214
6191
  meta: {
5215
6192
  name: "install",
5216
- description: "Bootstrap L0 (+ optional packs) from the registry and write agent-kit.json."
6193
+ description: "Bootstrap L0 (+ optional packs) and write .cursor/agent-kit.json."
5217
6194
  },
5218
6195
  args: {
5219
6196
  profile: {
@@ -5225,6 +6202,17 @@ var installCommand = defineCommand11({
5225
6202
  type: "string",
5226
6203
  description: `Comma-separated L1 pack ids (e.g. cybersec,devops). Known: ${DOMAIN_PACK_IDS.join(", ")}`
5227
6204
  },
6205
+ yes: {
6206
+ type: "boolean",
6207
+ alias: "y",
6208
+ description: "Skip interactive prompts; use defaults (IDE-agnostic non-interactive mode)",
6209
+ default: false
6210
+ },
6211
+ "force-root": {
6212
+ type: "boolean",
6213
+ description: "Bypass the ambiguous-root guard (use with caution)",
6214
+ default: false
6215
+ },
5228
6216
  cwd: {
5229
6217
  type: "string",
5230
6218
  default: process.cwd()
@@ -5232,7 +6220,25 @@ var installCommand = defineCommand11({
5232
6220
  ...REGISTRY_CLI_ARGS
5233
6221
  },
5234
6222
  async run({ args }) {
5235
- const projectRoot = path30.resolve(args.cwd);
6223
+ const nonInteractive = args.yes || isNonInteractive();
6224
+ if (nonInteractive) {
6225
+ logger.info("Non-interactive mode: skipping prompts, using defaults.");
6226
+ }
6227
+ let projectRoot;
6228
+ try {
6229
+ projectRoot = await confirmProjectRoot(args.cwd, {
6230
+ nonInteractive,
6231
+ command: "install",
6232
+ forceRoot: args["force-root"]
6233
+ });
6234
+ } catch (err) {
6235
+ if (err instanceof RootRefusedError) {
6236
+ logger.error(err.message);
6237
+ process.exitCode = 1;
6238
+ return;
6239
+ }
6240
+ throw err;
6241
+ }
5236
6242
  logger.info(`Installing into: ${projectRoot}`);
5237
6243
  const packs = parsePackList(args.pack);
5238
6244
  for (const id of packs) {
@@ -5240,19 +6246,31 @@ var installCommand = defineCommand11({
5240
6246
  logger.warn(`Pack '${id}' is not in the known L1 list; will still try registry.`);
5241
6247
  }
5242
6248
  }
5243
- const result = await performInstall({
5244
- cwd: projectRoot,
5245
- profile: args.profile,
5246
- pack: args.pack,
5247
- registry: args.registry,
5248
- url: args.url,
5249
- ref: args.ref,
5250
- refresh: args.refresh
5251
- });
5252
- logApplyStats(result.stats);
5253
- logger.success(`Manifest written: ${result.manifestPath}`);
5254
- logger.success("Readiness snapshot written: .cursor/context/readiness.json");
5255
- printReadinessNarrative(result);
6249
+ try {
6250
+ const result = await withCliProgress(
6251
+ "install",
6252
+ () => performInstall({
6253
+ cwd: projectRoot,
6254
+ profile: args.profile,
6255
+ pack: args.pack,
6256
+ registry: args.registry,
6257
+ url: args.url,
6258
+ ref: args.ref,
6259
+ refresh: args.refresh
6260
+ })
6261
+ );
6262
+ logApplyStats(result.stats);
6263
+ logger.success(`Manifest written: ${result.manifestPath}`);
6264
+ logger.success("Readiness snapshot written: .cursor/context/readiness.json");
6265
+ printReadinessNarrative(result);
6266
+ } catch (err) {
6267
+ const hint = classifyInstallError(err);
6268
+ logger.error(hint.message);
6269
+ console.error(`
6270
+ ${hint.recovery}
6271
+ `);
6272
+ process.exit(1);
6273
+ }
5256
6274
  }
5257
6275
  });
5258
6276
 
@@ -5263,7 +6281,7 @@ async function runInitCompatibility(cwd, installer = performInstall) {
5263
6281
  var initCommand = defineCommand12({
5264
6282
  meta: {
5265
6283
  name: "init",
5266
- description: "Guided compatibility entry point for install and repository readiness."
6284
+ description: "Guided setup entry (compat path to install + readiness)."
5267
6285
  },
5268
6286
  args: {
5269
6287
  cwd: {
@@ -5273,25 +6291,42 @@ var initCommand = defineCommand12({
5273
6291
  }
5274
6292
  },
5275
6293
  async run({ args }) {
5276
- intro(`agent-kit v${KIT_VERSION}`);
6294
+ const nonInteractive = isNonInteractive();
6295
+ if (!nonInteractive) {
6296
+ intro(`agent-kit v${KIT_VERSION}`);
6297
+ } else {
6298
+ logger.info(`agent-kit v${KIT_VERSION} (non-interactive mode)`);
6299
+ }
5277
6300
  logger.info("init now uses the canonical install and readiness workflow.");
5278
- const result = await runInitCompatibility(args.cwd);
5279
- const pending = result.readiness.pendingActions.length;
5280
- logger.success(`L0 and readiness prepared in ${result.projectRoot}`);
5281
- outro(
5282
- pending > 0 ? "Next: run /agent-kit-onboard in Cursor to resolve the first pending action." : "Next: run /start-project in Cursor when you have a deliverable."
5283
- );
6301
+ try {
6302
+ const result = await withCliProgress("init", () => runInitCompatibility(args.cwd));
6303
+ const pending = result.readiness.pendingActions.length;
6304
+ logger.success(`L0 and readiness prepared in ${result.projectRoot}`);
6305
+ const nextStep = pending > 0 ? "Next: run /agent-kit-onboard in Cursor to resolve the first pending action." : "Next: run /start-project in Cursor when you have a deliverable.";
6306
+ if (!nonInteractive) {
6307
+ outro(nextStep);
6308
+ } else {
6309
+ logger.info(nextStep);
6310
+ }
6311
+ } catch (err) {
6312
+ const hint = classifyInstallError(err);
6313
+ logger.error(hint.message);
6314
+ console.error(`
6315
+ ${hint.recovery}
6316
+ `);
6317
+ process.exit(1);
6318
+ }
5284
6319
  }
5285
6320
  });
5286
6321
 
5287
6322
  // src/commands/monitors.ts
5288
- import path32 from "path";
6323
+ import path35 from "path";
5289
6324
  import { defineCommand as defineCommand13 } from "citty";
5290
6325
 
5291
6326
  // src/invariants/monitors-untriaged.ts
5292
6327
  import { execFile as execFile5 } from "child_process";
5293
- import { readFile as readFile16, readdir as readdir5, stat as stat2 } from "fs/promises";
5294
- import path31 from "path";
6328
+ import { readFile as readFile17, readdir as readdir5, stat as stat3 } from "fs/promises";
6329
+ import path34 from "path";
5295
6330
  import { promisify as promisify5 } from "util";
5296
6331
 
5297
6332
  // src/invariants/triage-heading.ts
@@ -5332,7 +6367,7 @@ async function gitFreshMonitorNames(rootDir) {
5332
6367
  for (const line of stdout.split("\n")) {
5333
6368
  if (!line.trim()) continue;
5334
6369
  const file = line.slice(3).trim().replace(/^.* -> /, "");
5335
- const base = path31.basename(file);
6370
+ const base = path34.basename(file);
5336
6371
  if (base.startsWith("plan-monitor-") && base.endsWith(".md")) {
5337
6372
  names.add(base);
5338
6373
  }
@@ -5355,15 +6390,15 @@ function monitorSlugFromName(fileName) {
5355
6390
  return fileName.replace(/^plan-monitor-/, "").replace(/\.md$/, "").toLowerCase();
5356
6391
  }
5357
6392
  async function selectUntriagedMonitors(rootDir) {
5358
- const root = path31.resolve(rootDir);
5359
- const memoryDir = path31.join(root, ".cursor", "memory");
6393
+ const root = path34.resolve(rootDir);
6394
+ const memoryDir = path34.join(root, ".cursor", "memory");
5360
6395
  const allNames = await listMonitorFiles(memoryDir);
5361
6396
  const selectionOrder = ["git-fresh", "handoff-aligned", "untriaged-scan"];
5362
6397
  const byName = /* @__PURE__ */ new Map();
5363
6398
  for (const name of allNames) {
5364
- const abs = path31.join(memoryDir, name);
6399
+ const abs = path34.join(memoryDir, name);
5365
6400
  try {
5366
- const [content, st] = await Promise.all([readFile16(abs, "utf8"), stat2(abs)]);
6401
+ const [content, st] = await Promise.all([readFile17(abs, "utf8"), stat3(abs)]);
5367
6402
  byName.set(name, { content, mtimeMs: st.mtimeMs });
5368
6403
  } catch {
5369
6404
  }
@@ -5376,7 +6411,7 @@ async function selectUntriagedMonitors(rootDir) {
5376
6411
  const gitFreshSet = [...gitFresh].filter(untriaged).sort();
5377
6412
  let handoff = "";
5378
6413
  try {
5379
- handoff = await readFile16(path31.join(root, ".cursor", "HANDOFF.md"), "utf8");
6414
+ handoff = await readFile17(path34.join(root, ".cursor", "HANDOFF.md"), "utf8");
5380
6415
  } catch {
5381
6416
  handoff = "";
5382
6417
  }
@@ -5396,8 +6431,8 @@ async function selectUntriagedMonitors(rootDir) {
5396
6431
  const row = byName.get(name);
5397
6432
  if (!row) continue;
5398
6433
  entries.push({
5399
- path: path31.join(memoryDir, name),
5400
- relativePath: path31.relative(root, path31.join(memoryDir, name)).split(path31.sep).join("/"),
6434
+ path: path34.join(memoryDir, name),
6435
+ relativePath: path34.relative(root, path34.join(memoryDir, name)).split(path34.sep).join("/"),
5401
6436
  mtimeMs: row.mtimeMs,
5402
6437
  hasTriageHeading: false,
5403
6438
  hasOpenGaps: hasOpenGaps(row.content),
@@ -5419,7 +6454,7 @@ async function selectUntriagedMonitors(rootDir) {
5419
6454
  var monitorsCommand = defineCommand13({
5420
6455
  meta: {
5421
6456
  name: "monitors",
5422
- description: "Plan-monitor selection helpers (untriaged SoT for /plan-review-triage)"
6457
+ description: "Select untriaged plan monitors (--untriaged; SoT for chat triage)."
5423
6458
  },
5424
6459
  args: {
5425
6460
  cwd: {
@@ -5443,7 +6478,7 @@ var monitorsCommand = defineCommand13({
5443
6478
  process.exitCode = 2;
5444
6479
  return;
5445
6480
  }
5446
- const result = await selectUntriagedMonitors(path32.resolve(args.cwd));
6481
+ const result = await selectUntriagedMonitors(path35.resolve(args.cwd));
5447
6482
  if (args.json) {
5448
6483
  console.log(JSON.stringify(result, null, 2));
5449
6484
  return;
@@ -5459,11 +6494,11 @@ var monitorsCommand = defineCommand13({
5459
6494
  });
5460
6495
 
5461
6496
  // src/commands/run-plan.ts
5462
- import path37 from "path";
6497
+ import path40 from "path";
5463
6498
  import { defineCommand as defineCommand14 } from "citty";
5464
6499
 
5465
6500
  // src/plan-loop/backends.ts
5466
- import { execFileSync as execFileSync2, spawn as spawn5 } from "child_process";
6501
+ import { execFileSync as execFileSync2, spawn as spawn6 } from "child_process";
5467
6502
  import { createWriteStream } from "fs";
5468
6503
  async function which(bin) {
5469
6504
  try {
@@ -5474,9 +6509,9 @@ async function which(bin) {
5474
6509
  }
5475
6510
  }
5476
6511
  function spawnLogged(command, args, logPath) {
5477
- return new Promise((resolve, reject) => {
6512
+ return new Promise((resolve2, reject) => {
5478
6513
  const out = createWriteStream(logPath, { flags: "w" });
5479
- const child = spawn5(command, args, {
6514
+ const child = spawn6(command, args, {
5480
6515
  stdio: ["ignore", "pipe", "pipe"]
5481
6516
  });
5482
6517
  const onData = (chunk) => {
@@ -5490,7 +6525,7 @@ function spawnLogged(command, args, logPath) {
5490
6525
  reject(err);
5491
6526
  });
5492
6527
  child.on("close", (code) => {
5493
- out.end(() => resolve({ exitCode: code ?? 1 }));
6528
+ out.end(() => resolve2({ exitCode: code ?? 1 }));
5494
6529
  });
5495
6530
  });
5496
6531
  }
@@ -5544,14 +6579,14 @@ function listBackendIds() {
5544
6579
  }
5545
6580
 
5546
6581
  // src/plan-loop/run-loop.ts
5547
- import { mkdir as mkdir5, readFile as readFile19, rm, unlink as unlink2 } from "fs/promises";
5548
- import path36 from "path";
6582
+ import { mkdir as mkdir6, readFile as readFile20, rm as rm2, unlink as unlink2 } from "fs/promises";
6583
+ import path39 from "path";
5549
6584
 
5550
6585
  // src/plan-loop/external-review.ts
5551
- import { spawn as spawn6 } from "child_process";
5552
- import path33 from "path";
5553
- var CANONICAL_REL = path33.join(".cursor", "scripts", "plan-external-review.sh");
5554
- var FALLBACK_REL = path33.join("scripts", "plan-external-review.sh");
6586
+ import { spawn as spawn7 } from "child_process";
6587
+ import path36 from "path";
6588
+ var CANONICAL_REL = path36.join(".cursor", "scripts", "plan-external-review.sh");
6589
+ var FALLBACK_REL = path36.join("scripts", "plan-external-review.sh");
5555
6590
  function isPlanExhaustedReason(reason) {
5556
6591
  const r = reason.trim().toLowerCase();
5557
6592
  if (!r) return false;
@@ -5569,12 +6604,12 @@ function shouldArmExternalPlanReview(input) {
5569
6604
  return false;
5570
6605
  }
5571
6606
  async function armExternalPlanReview(root, options = {}) {
5572
- const spawnFn = options.spawnFn ?? spawn6;
6607
+ const spawnFn = options.spawnFn ?? spawn7;
5573
6608
  const existsFn = options.existsFn ?? fileExists;
5574
6609
  const log = options.log ?? ((line) => console.log(line));
5575
6610
  const force = options.force === true;
5576
- const canonicalPath = path33.join(root, CANONICAL_REL);
5577
- const fallbackPath = path33.join(root, FALLBACK_REL);
6611
+ const canonicalPath = path36.join(root, CANONICAL_REL);
6612
+ const fallbackPath = path36.join(root, FALLBACK_REL);
5578
6613
  let scriptPath = null;
5579
6614
  let scriptRel = CANONICAL_REL;
5580
6615
  if (await existsFn(canonicalPath)) {
@@ -5592,7 +6627,7 @@ async function armExternalPlanReview(root, options = {}) {
5592
6627
  }
5593
6628
  log("Plan exhausted: arming optional external plan review (opt-in handled by script)...");
5594
6629
  const args = force ? [scriptPath, "--force", "--print"] : [scriptPath, "--print"];
5595
- return new Promise((resolve) => {
6630
+ return new Promise((resolve2) => {
5596
6631
  const child = spawnFn("bash", args, {
5597
6632
  cwd: root,
5598
6633
  env: { ...process.env, AGENT_KIT_HEADLESS: "1" }
@@ -5613,7 +6648,7 @@ async function armExternalPlanReview(root, options = {}) {
5613
6648
  log(
5614
6649
  `tip: external plan review launcher failed (${err.message}). Manual: ${scriptRel} or /plan-external-review`
5615
6650
  );
5616
- resolve({ invoked: true, exitCode: null, output });
6651
+ resolve2({ invoked: true, exitCode: null, output });
5617
6652
  });
5618
6653
  child.on("close", (code) => {
5619
6654
  if (code !== 0 && code !== null) {
@@ -5621,17 +6656,17 @@ async function armExternalPlanReview(root, options = {}) {
5621
6656
  `tip: external plan review exited ${code} (ignored; does not fail the loop). Manual: ${scriptRel} or /plan-external-review`
5622
6657
  );
5623
6658
  }
5624
- resolve({ invoked: true, exitCode: code, output });
6659
+ resolve2({ invoked: true, exitCode: code, output });
5625
6660
  });
5626
6661
  });
5627
6662
  }
5628
6663
 
5629
6664
  // src/plan-loop/persona-banners.ts
5630
- import path34 from "path";
6665
+ import path37 from "path";
5631
6666
  import {
5632
6667
  blue,
5633
- cyan as cyan2,
5634
- gray,
6668
+ cyan as cyan3,
6669
+ gray as gray2,
5635
6670
  green as green2,
5636
6671
  lightGray,
5637
6672
  lightGreen,
@@ -5644,9 +6679,9 @@ var CLI_RUN_PLAN_MODE = "cli-run-plan";
5644
6679
  var DEFAULT_CLI_PERSONA_ID = "ghost-runner";
5645
6680
  var COLORS = {
5646
6681
  white,
5647
- gray,
6682
+ gray: gray2,
5648
6683
  green: green2,
5649
- cyan: cyan2,
6684
+ cyan: cyan3,
5650
6685
  magenta,
5651
6686
  yellow: yellow2,
5652
6687
  red: red2,
@@ -5663,7 +6698,7 @@ function resolveColor(name, fallback) {
5663
6698
  async function resolveCliPersonaId(root) {
5664
6699
  try {
5665
6700
  const cfg = await readJson(
5666
- path34.join(root, ".cursor", "context", "config.json")
6701
+ path37.join(root, ".cursor", "context", "config.json")
5667
6702
  );
5668
6703
  const modes = cfg?.agentPersona?.modes ?? cfg?.workspaceSkin?.modes;
5669
6704
  const id = modes?.[CLI_RUN_PLAN_MODE];
@@ -5674,7 +6709,7 @@ async function resolveCliPersonaId(root) {
5674
6709
  }
5675
6710
  async function loadPersonaPack(root, personaId) {
5676
6711
  try {
5677
- const personaPath = path34.join(root, "registry", "personas", "core", personaId, "persona.json");
6712
+ const personaPath = path37.join(root, "registry", "personas", "core", personaId, "persona.json");
5678
6713
  const pack = await readJson(personaPath);
5679
6714
  if (!pack || typeof pack.id !== "string") return null;
5680
6715
  return pack;
@@ -5691,7 +6726,7 @@ function createPersonaBannerPrinter(persona) {
5691
6726
  const banners = persona.cliBanners;
5692
6727
  if (!banners.tickStart && !banners.tickEnd && !banners.phaseComplete) return null;
5693
6728
  const primary = resolveColor(persona.ansiPalette?.primary, white);
5694
- const secondary = resolveColor(persona.ansiPalette?.secondary, gray);
6729
+ const secondary = resolveColor(persona.ansiPalette?.secondary, gray2);
5695
6730
  const accent = resolveColor(persona.ansiPalette?.accent, green2);
5696
6731
  return {
5697
6732
  tickStart(detail) {
@@ -5723,8 +6758,8 @@ function createPersonaBannerPrinter(persona) {
5723
6758
  }
5724
6759
 
5725
6760
  // src/plan-loop/plan-state.ts
5726
- import { readFile as readFile17, readdir as readdir6 } from "fs/promises";
5727
- import path35 from "path";
6761
+ import { readFile as readFile18, readdir as readdir6 } from "fs/promises";
6762
+ import path38 from "path";
5728
6763
  function countPendingTodos(raw) {
5729
6764
  const lines = raw.split(/\r?\n/);
5730
6765
  let inFront = 0;
@@ -5752,14 +6787,14 @@ function countPendingTodos(raw) {
5752
6787
  async function findActivePlanFile(plansDir) {
5753
6788
  if (!await fileExists(plansDir)) return null;
5754
6789
  const files = (await readdir6(plansDir)).filter((f) => f.endsWith(".plan.md")).sort();
5755
- return files[0] ? path35.join(plansDir, files[0]) : null;
6790
+ return files[0] ? path38.join(plansDir, files[0]) : null;
5756
6791
  }
5757
6792
  async function readPlan(planPath) {
5758
- return readFile17(planPath, "utf8");
6793
+ return readFile18(planPath, "utf8");
5759
6794
  }
5760
6795
 
5761
6796
  // src/plan-loop/sentinel.ts
5762
- import { readFile as readFile18 } from "fs/promises";
6797
+ import { readFile as readFile19 } from "fs/promises";
5763
6798
  var SENTINEL_RE = /LOOP_TICK_RESULT:\s*(continue|stop(?:\s*[—\-].*)?)/i;
5764
6799
  function takeFromText(text) {
5765
6800
  if (!text) return null;
@@ -5806,7 +6841,7 @@ function parseSentinelFromLog(content) {
5806
6841
  }
5807
6842
  async function parseSentinelFromLogFile(logPath) {
5808
6843
  try {
5809
- const content = await readFile18(logPath, "utf8");
6844
+ const content = await readFile19(logPath, "utf8");
5810
6845
  return parseSentinelFromLog(content);
5811
6846
  } catch {
5812
6847
  return { kind: "missing" };
@@ -5829,16 +6864,16 @@ function sleep(ms) {
5829
6864
  return new Promise((r) => setTimeout(r, ms));
5830
6865
  }
5831
6866
  async function runPlanLoop(opts) {
5832
- const plansDir = path36.join(opts.root, ".cursor", "plans");
5833
- const stopFile = path36.join(opts.root, ".cursor", "loop.stop");
5834
- const logDir = path36.join(opts.root, ".cursor", "loop-logs");
6867
+ const plansDir = path39.join(opts.root, ".cursor", "plans");
6868
+ const stopFile = path39.join(opts.root, ".cursor", "loop.stop");
6869
+ const logDir = path39.join(opts.root, ".cursor", "loop-logs");
5835
6870
  const planPath = await findActivePlanFile(plansDir);
5836
6871
  if (!planPath) {
5837
6872
  logger.error("No active plan in .cursor/plans/");
5838
6873
  return 1;
5839
6874
  }
5840
6875
  const pending = async () => countPendingTodos(await readPlan(planPath));
5841
- await mkdir5(logDir, { recursive: true });
6876
+ await mkdir6(logDir, { recursive: true });
5842
6877
  try {
5843
6878
  await unlink2(stopFile);
5844
6879
  } catch {
@@ -5852,7 +6887,7 @@ async function runPlanLoop(opts) {
5852
6887
  try {
5853
6888
  const persona = await loadCliRunPlanPersona(opts.root);
5854
6889
  const banners = createPersonaBannerPrinter(persona);
5855
- console.log(`Active plan: ${path36.basename(planPath)}`);
6890
+ console.log(`Active plan: ${path39.basename(planPath)}`);
5856
6891
  console.log(`Pending to-dos: ${await pending()} | max ticks: ${opts.maxTicks}`);
5857
6892
  console.log(`Backend: ${opts.backend.id}`);
5858
6893
  if (persona) {
@@ -5884,7 +6919,7 @@ async function runPlanLoop(opts) {
5884
6919
  if (banners) banners.stop(msg);
5885
6920
  else console.log(msg);
5886
6921
  try {
5887
- await rm(stopFile, { force: true });
6922
+ await rm2(stopFile, { force: true });
5888
6923
  } catch {
5889
6924
  }
5890
6925
  break;
@@ -5895,27 +6930,30 @@ async function runPlanLoop(opts) {
5895
6930
  planExhausted = true;
5896
6931
  break;
5897
6932
  }
5898
- const logPath = path36.join(logDir, `tick-${stamp()}.log`);
5899
- const relLog = path36.relative(opts.root, logPath);
6933
+ const logPath = path39.join(logDir, `tick-${stamp()}.log`);
6934
+ const relLog = path39.relative(opts.root, logPath);
5900
6935
  console.log("");
5901
6936
  const tickLine = `=== tick ${tick}/${opts.maxTicks} - pending: ${before} - log: ${relLog} ===`;
5902
6937
  if (banners) banners.tickStart(tickLine);
5903
6938
  else console.log(tickLine);
5904
6939
  let agentExit = 0;
5905
6940
  try {
5906
- const result = await opts.backend.run({
5907
- workspace: opts.root,
5908
- prompt: TICK_PROMPT,
5909
- model: opts.model,
5910
- logPath
5911
- });
6941
+ const result = await withCliProgress(
6942
+ `tick ${tick}`,
6943
+ () => opts.backend.run({
6944
+ workspace: opts.root,
6945
+ prompt: TICK_PROMPT,
6946
+ model: opts.model,
6947
+ logPath
6948
+ })
6949
+ );
5912
6950
  agentExit = result.exitCode;
5913
6951
  } catch (err) {
5914
6952
  logger.error(String(err));
5915
6953
  return 1;
5916
6954
  }
5917
6955
  try {
5918
- const logText = await readFile19(logPath, "utf8");
6956
+ const logText = await readFile20(logPath, "utf8");
5919
6957
  if (logText.includes("Too many MCP tools")) {
5920
6958
  const msg = "Too many MCP tools for the headless model - disable servers (cursor-agent mcp disable <id>) and run again.";
5921
6959
  if (banners) banners.stop(msg);
@@ -5964,7 +7002,7 @@ async function runPlanLoop(opts) {
5964
7002
  }
5965
7003
  if (opts.sleepSeconds > 0) {
5966
7004
  if (banners) banners.sleep(opts.sleepSeconds);
5967
- await sleep(opts.sleepSeconds * 1e3);
7005
+ await withCliProgress(`sleep ${opts.sleepSeconds}s`, () => sleep(opts.sleepSeconds * 1e3));
5968
7006
  }
5969
7007
  }
5970
7008
  const pendingNow = await pending();
@@ -5972,7 +7010,7 @@ async function runPlanLoop(opts) {
5972
7010
  const finishDetail = `after ${tick} tick(s); pending: ${pendingNow}`;
5973
7011
  if (banners) banners.phaseComplete(finishDetail);
5974
7012
  console.log(
5975
- `Loop finished after ${tick} tick(s). Pending now: ${pendingNow}. Logs in ${path36.relative(opts.root, logDir)}/`
7013
+ `Loop finished after ${tick} tick(s). Pending now: ${pendingNow}. Logs in ${path39.relative(opts.root, logDir)}/`
5976
7014
  );
5977
7015
  if (planExhausted || shouldArmExternalPlanReview({ pending: pendingNow, stopReason })) {
5978
7016
  await armExternalPlanReview(opts.root);
@@ -5987,7 +7025,7 @@ async function runPlanLoop(opts) {
5987
7025
  var runPlanCommand = defineCommand14({
5988
7026
  meta: {
5989
7027
  name: "run-plan",
5990
- description: "Headless continuous plan runner: one fresh agent per tick (LOOP_TICK_RESULT contract). Never git-prod."
7028
+ description: "Headless continuous plan runner (one fresh agent per tick). Never git-prod."
5991
7029
  },
5992
7030
  args: {
5993
7031
  cwd: {
@@ -6043,7 +7081,7 @@ var runPlanCommand = defineCommand14({
6043
7081
  return;
6044
7082
  }
6045
7083
  const code = await runPlanLoop({
6046
- root: path37.resolve(args.cwd),
7084
+ root: path40.resolve(args.cwd),
6047
7085
  maxTicks,
6048
7086
  sleepSeconds,
6049
7087
  model: args.model ? String(args.model) : void 0,
@@ -6059,7 +7097,7 @@ import { defineCommand as defineCommand15 } from "citty";
6059
7097
  var scanCommand = defineCommand15({
6060
7098
  meta: {
6061
7099
  name: "scan",
6062
- description: "Scan the current repository and print detected profile."
7100
+ description: "Scan the repository and print the detected profile."
6063
7101
  },
6064
7102
  args: {
6065
7103
  cwd: {
@@ -6077,7 +7115,7 @@ var scanCommand = defineCommand15({
6077
7115
  });
6078
7116
 
6079
7117
  // src/commands/status.ts
6080
- import path38 from "path";
7118
+ import path41 from "path";
6081
7119
  import { defineCommand as defineCommand16 } from "citty";
6082
7120
  function profileStatus(profile) {
6083
7121
  if (!profile) return { origin: "none", evidence: [], profile: null };
@@ -6094,7 +7132,7 @@ function profileStatus(profile) {
6094
7132
  var statusCommand = defineCommand16({
6095
7133
  meta: {
6096
7134
  name: "status",
6097
- description: "Show Agent Kit distribution status (manifest + optional wizard profile)."
7135
+ description: "Show installed kit version, manifest, and optional profile."
6098
7136
  },
6099
7137
  args: {
6100
7138
  cwd: {
@@ -6108,11 +7146,11 @@ var statusCommand = defineCommand16({
6108
7146
  }
6109
7147
  },
6110
7148
  async run({ args }) {
6111
- const rootDir = path38.resolve(args.cwd);
7149
+ const rootDir = path41.resolve(args.cwd);
6112
7150
  const [manifest, rawProfile, scan] = await Promise.all([
6113
7151
  loadAgentKitManifest(rootDir),
6114
7152
  readJson(
6115
- path38.join(rootDir, ".cursor", "agent-kit.config.json")
7153
+ path41.join(rootDir, ".cursor", "agent-kit.config.json")
6116
7154
  ),
6117
7155
  runScanner(rootDir)
6118
7156
  ]);
@@ -6171,7 +7209,7 @@ import { defineCommand as defineCommand17 } from "citty";
6171
7209
 
6172
7210
  // src/lifecycle/check-updates.ts
6173
7211
  import { execFile as execFile6 } from "child_process";
6174
- import path39 from "path";
7212
+ import path42 from "path";
6175
7213
  import { promisify as promisify6 } from "util";
6176
7214
  var execFileAsync5 = promisify6(execFile6);
6177
7215
  var SEMVER_CORE = /^v?(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/i;
@@ -6262,11 +7300,11 @@ function intervalElapsed2(lastCheckedAt, intervalDays) {
6262
7300
  return Date.now() - last >= ms;
6263
7301
  }
6264
7302
  async function loadContextConfig2(cwd) {
6265
- const configPath = path39.join(cwd, ".cursor", "context", "config.json");
7303
+ const configPath = path42.join(cwd, ".cursor", "context", "config.json");
6266
7304
  return readJson(configPath);
6267
7305
  }
6268
7306
  async function stampLastCheckedAt(cwd) {
6269
- const configPath = path39.join(cwd, ".cursor", "context", "config.json");
7307
+ const configPath = path42.join(cwd, ".cursor", "context", "config.json");
6270
7308
  const existing = await loadContextConfig2(cwd) ?? {};
6271
7309
  const prev = existing.updateCheck && typeof existing.updateCheck === "object" ? { ...existing.updateCheck } : {};
6272
7310
  existing.updateCheck = {
@@ -6276,6 +7314,117 @@ async function stampLastCheckedAt(cwd) {
6276
7314
  };
6277
7315
  await writeJson(configPath, existing);
6278
7316
  }
7317
+ async function readLocalKitVersion(registryRoot) {
7318
+ for (const rel of ["packages/cli/package.json", "package.json"]) {
7319
+ const data = await readJson(path42.join(registryRoot, rel));
7320
+ if (data && typeof data.version === "string" && data.version.length > 0) {
7321
+ return data.version;
7322
+ }
7323
+ }
7324
+ return null;
7325
+ }
7326
+ async function checkAgainstLocalRegistry(cwd, manifest, options) {
7327
+ const registryPath = options.registryPath;
7328
+ if (!registryPath) {
7329
+ throw new Error("checkAgainstLocalRegistry requires registryPath");
7330
+ }
7331
+ let resolved;
7332
+ try {
7333
+ resolved = await resolveRegistryRoot({ cwd, registryPath });
7334
+ } catch (err) {
7335
+ const msg = err instanceof Error ? err.message : String(err);
7336
+ return {
7337
+ status: "error",
7338
+ installedVersion: manifest.version,
7339
+ latestVersion: null,
7340
+ registryUrl: path42.resolve(registryPath),
7341
+ registryRef: "local",
7342
+ applyRecommended: false,
7343
+ message: `Failed to resolve --registry: ${msg}`
7344
+ };
7345
+ }
7346
+ const registryUrl = resolved.root;
7347
+ const registryRef = "local";
7348
+ try {
7349
+ let latest;
7350
+ if (options.latestVersion !== void 0) {
7351
+ latest = options.latestVersion;
7352
+ } else {
7353
+ const raw = await readLocalKitVersion(resolved.root);
7354
+ latest = raw ? normalizeSemver(raw) : null;
7355
+ }
7356
+ const summary = summarizeDiff(await diffAgainstRegistry(resolved.root, cwd, manifest));
7357
+ const hasFileDrift = summary.drift > 0 || summary["missing-local"] > 0;
7358
+ const installed = normalizeSemver(manifest.version);
7359
+ if (!installed) {
7360
+ return {
7361
+ status: "error",
7362
+ installedVersion: manifest.version,
7363
+ latestVersion: latest,
7364
+ registryUrl,
7365
+ registryRef,
7366
+ applyRecommended: false,
7367
+ message: `Installed version "${manifest.version}" is not valid semver.`
7368
+ };
7369
+ }
7370
+ if (!latest) {
7371
+ return {
7372
+ status: "error",
7373
+ installedVersion: installed,
7374
+ latestVersion: null,
7375
+ registryUrl,
7376
+ registryRef,
7377
+ applyRecommended: false,
7378
+ message: "No semver found in the local --registry checkout (packages/cli/package.json or package.json)."
7379
+ };
7380
+ }
7381
+ const cmp = compareSemver(installed, latest);
7382
+ if (hasFileDrift) {
7383
+ return {
7384
+ status: "update-available",
7385
+ installedVersion: installed,
7386
+ latestVersion: latest,
7387
+ registryUrl,
7388
+ registryRef,
7389
+ applyRecommended: false,
7390
+ message: `Local registry has drift vs this install (drift=${summary.drift}, missing-local=${summary["missing-local"]}; source v${latest}). Run /update --registry <path> (Ask confirm) to apply; never silent.`
7391
+ };
7392
+ }
7393
+ if (cmp === 0) {
7394
+ return {
7395
+ status: "up-to-date",
7396
+ installedVersion: installed,
7397
+ latestVersion: latest,
7398
+ registryUrl,
7399
+ registryRef,
7400
+ applyRecommended: false,
7401
+ message: `Installed v${installed} matches local registry v${latest}.`
7402
+ };
7403
+ }
7404
+ if (cmp < 0) {
7405
+ return {
7406
+ status: "update-available",
7407
+ installedVersion: installed,
7408
+ latestVersion: latest,
7409
+ registryUrl,
7410
+ registryRef,
7411
+ applyRecommended: false,
7412
+ message: `Update available from local registry: v${installed} \u2192 v${latest}. Run /update --registry <path> (Ask confirm) to apply; never silent.`
7413
+ };
7414
+ }
7415
+ return {
7416
+ status: "ahead-of-public",
7417
+ installedVersion: installed,
7418
+ latestVersion: latest,
7419
+ registryUrl,
7420
+ registryRef,
7421
+ applyRecommended: false,
7422
+ message: `Installed v${installed} is ahead of local registry v${latest}.`
7423
+ };
7424
+ } finally {
7425
+ await resolved.unlock?.();
7426
+ }
7427
+ }
6279
7428
  async function checkForUpdates(cwd, options = {}) {
6280
7429
  const manifest = await loadAgentKitManifest(cwd);
6281
7430
  if (!manifest) {
@@ -6289,7 +7438,10 @@ async function checkForUpdates(cwd, options = {}) {
6289
7438
  message: "No .cursor/agent-kit.json \u2014 run agent-kit install first."
6290
7439
  };
6291
7440
  }
6292
- const registryUrl = manifest.registry?.url ?? null;
7441
+ if (options.registryPath) {
7442
+ return checkAgainstLocalRegistry(cwd, manifest, options);
7443
+ }
7444
+ const registryUrl = options.publicRegistryUrl ?? manifest.registry?.url ?? null;
6293
7445
  const registryRef = manifest.registry?.ref ?? null;
6294
7446
  if (isFactoryOrDevRegistry(registryUrl, registryRef)) {
6295
7447
  return {
@@ -6410,7 +7562,7 @@ async function checkForUpdates(cwd, options = {}) {
6410
7562
  var updateCommand = defineCommand17({
6411
7563
  meta: {
6412
7564
  name: "update",
6413
- description: "Re-apply L0/packs/skills from the registry; never overwrites L3 protected paths. Use --check for notify-only."
7565
+ description: "Re-apply L0/packs/skills from the registry (never overwrites L3). --check = notify-only."
6414
7566
  },
6415
7567
  args: {
6416
7568
  cwd: {
@@ -6419,7 +7571,7 @@ var updateCommand = defineCommand17({
6419
7571
  },
6420
7572
  check: {
6421
7573
  type: "boolean",
6422
- description: "Check-only: compare installed version to latest public tag; never apply L0 writes",
7574
+ description: "Check-only: compare installed version to the resolved registry (public tags, or --registry local checkout); never apply L0 writes",
6423
7575
  default: false
6424
7576
  },
6425
7577
  json: {
@@ -6442,6 +7594,17 @@ var updateCommand = defineCommand17({
6442
7594
  description: "Seed the managed-hash ledger from current local overlay files before applying (factory/dogfood only; consumers should not use this)",
6443
7595
  default: false
6444
7596
  },
7597
+ yes: {
7598
+ type: "boolean",
7599
+ alias: "y",
7600
+ description: "Skip interactive prompts; use defaults (IDE-agnostic non-interactive mode)",
7601
+ default: false
7602
+ },
7603
+ "force-root": {
7604
+ type: "boolean",
7605
+ description: "Bypass the ambiguous-root guard (use with caution)",
7606
+ default: false
7607
+ },
6445
7608
  ...REGISTRY_CLI_ARGS
6446
7609
  },
6447
7610
  async run({ args }) {
@@ -6449,7 +7612,8 @@ var updateCommand = defineCommand17({
6449
7612
  const result = await checkForUpdates(args.cwd, {
6450
7613
  respectPrefs: Boolean(args["respect-prefs"]),
6451
7614
  stamp: Boolean(args.stamp),
6452
- publicRegistryUrl: typeof args.url === "string" && args.url ? args.url : void 0
7615
+ publicRegistryUrl: typeof args.url === "string" && args.url ? args.url : void 0,
7616
+ registryPath: typeof args.registry === "string" && args.registry ? args.registry : void 0
6453
7617
  });
6454
7618
  if (args.json) {
6455
7619
  console.log(JSON.stringify(result, null, 2));
@@ -6463,48 +7627,71 @@ var updateCommand = defineCommand17({
6463
7627
  if (result.status === "error") process.exitCode = 2;
6464
7628
  return;
6465
7629
  }
6466
- const existing = await loadAgentKitManifest(args.cwd);
7630
+ const nonInteractive = args.yes || isNonInteractive();
7631
+ let projectRoot;
7632
+ try {
7633
+ projectRoot = await confirmProjectRoot(args.cwd, {
7634
+ nonInteractive,
7635
+ command: "update",
7636
+ forceRoot: args["force-root"]
7637
+ });
7638
+ } catch (err) {
7639
+ if (err instanceof RootRefusedError) {
7640
+ logger.error(err.message);
7641
+ process.exitCode = 1;
7642
+ return;
7643
+ }
7644
+ throw err;
7645
+ }
7646
+ const existing = await loadAgentKitManifest(projectRoot);
6467
7647
  if (!existing) {
6468
7648
  logger.warn("No .cursor/agent-kit.json \u2014 run agent-kit install first.");
6469
7649
  return;
6470
7650
  }
6471
7651
  const registry = await resolveRegistryFromCli({
6472
- cwd: args.cwd,
7652
+ cwd: projectRoot,
6473
7653
  registry: args.registry,
6474
7654
  url: args.url,
6475
7655
  ref: args.ref,
6476
7656
  refresh: args.refresh,
6477
7657
  manifest: existing
6478
7658
  });
6479
- logger.info(`Registry: ${registry.root} (${registry.source})`);
6480
- const next = buildManifest({
6481
- version: KIT_VERSION,
6482
- profile: existing.profile,
6483
- packs: existing.packs,
6484
- skills: existing.skills,
6485
- protected: existing.protected,
6486
- personalization: existing.personalization,
6487
- registryUrl: registry.url ?? existing.registry?.url,
6488
- registryRef: registry.ref ?? existing.registry?.ref
6489
- });
6490
- if (existing.overrides?.length) next.overrides = existing.overrides;
6491
- if (next.version === existing.version && existing.installedAt) {
6492
- next.installedAt = existing.installedAt;
6493
- }
6494
- if (args["seed-overlay"]) {
6495
- await seedManagedHashLedger(args.cwd);
6496
- logger.info("Seeded managed-hash ledger from current local overlay files.");
7659
+ try {
7660
+ logger.info(`Registry: ${registry.root} (${registry.source})`);
7661
+ const next = buildManifest({
7662
+ version: KIT_VERSION,
7663
+ profile: existing.profile,
7664
+ packs: existing.packs,
7665
+ skills: existing.skills,
7666
+ protected: existing.protected,
7667
+ personalization: existing.personalization,
7668
+ registryUrl: registry.url ?? existing.registry?.url,
7669
+ registryRef: registry.ref ?? existing.registry?.ref
7670
+ });
7671
+ if (existing.overrides?.length) next.overrides = existing.overrides;
7672
+ if (next.version === existing.version && existing.installedAt) {
7673
+ next.installedAt = existing.installedAt;
7674
+ }
7675
+ if (args["seed-overlay"]) {
7676
+ await seedManagedHashLedger(projectRoot);
7677
+ logger.info("Seeded managed-hash ledger from current local overlay files.");
7678
+ }
7679
+ const stats = await withCliProgress(
7680
+ "update",
7681
+ () => syncFromManifest(registry.root, projectRoot, next)
7682
+ );
7683
+ await saveManifest(projectRoot, next);
7684
+ logApplyStats(stats);
7685
+ logger.success("Update complete (L3 protected paths left untouched).");
7686
+ } finally {
7687
+ await registry.unlock?.();
6497
7688
  }
6498
- const stats = await syncFromManifest(registry.root, args.cwd, next);
6499
- await saveManifest(args.cwd, next);
6500
- logApplyStats(stats);
6501
- logger.success("Update complete (L3 protected paths left untouched).");
6502
7689
  }
6503
7690
  });
6504
7691
 
6505
7692
  // src/commands/validate.ts
6506
- import { readFile as readFile20 } from "fs/promises";
6507
- import path40 from "path";
7693
+ import { readFile as readFile21 } from "fs/promises";
7694
+ import path43 from "path";
6508
7695
  import { defineCommand as defineCommand18 } from "citty";
6509
7696
 
6510
7697
  // src/invariants/plan-schema.ts
@@ -6551,9 +7738,9 @@ function validatePlanFrontmatterText(text) {
6551
7738
  // src/commands/validate.ts
6552
7739
  async function resolveEditedPath(cwd, explicit) {
6553
7740
  if (explicit) {
6554
- const filePath2 = path40.resolve(cwd, explicit);
7741
+ const filePath2 = path43.resolve(cwd, explicit);
6555
7742
  try {
6556
- return { filePath: filePath2, content: await readFile20(filePath2, "utf8") };
7743
+ return { filePath: filePath2, content: await readFile21(filePath2, "utf8") };
6557
7744
  } catch {
6558
7745
  return null;
6559
7746
  }
@@ -6561,9 +7748,9 @@ async function resolveEditedPath(cwd, explicit) {
6561
7748
  const payload = await readStdinJson();
6562
7749
  const rel = typeof payload.file_path === "string" && payload.file_path || typeof payload.path === "string" && payload.path || typeof payload.file === "string" && payload.file || "";
6563
7750
  if (!rel) return null;
6564
- const filePath = path40.isAbsolute(rel) ? rel : path40.resolve(cwd, rel);
7751
+ const filePath = path43.isAbsolute(rel) ? rel : path43.resolve(cwd, rel);
6565
7752
  try {
6566
- return { filePath, content: await readFile20(filePath, "utf8") };
7753
+ return { filePath, content: await readFile21(filePath, "utf8") };
6567
7754
  } catch {
6568
7755
  return null;
6569
7756
  }
@@ -6578,7 +7765,7 @@ function isPlanPath(filePath) {
6578
7765
  var validateCommand = defineCommand18({
6579
7766
  meta: {
6580
7767
  name: "validate",
6581
- description: "Advisory validators for HANDOFF / plan frontmatter (afterFileEdit adapter)"
7768
+ description: "Advisory validators for HANDOFF / plan frontmatter (hook adapter)."
6582
7769
  },
6583
7770
  subCommands: {
6584
7771
  handoff: defineCommand18({
@@ -6591,10 +7778,10 @@ var validateCommand = defineCommand18({
6591
7778
  async run({ args }) {
6592
7779
  const cwd = typeof args.cwd === "string" ? args.cwd : process.cwd();
6593
7780
  const fileArg = typeof args.file === "string" ? args.file : void 0;
6594
- const filePath = fileArg ? path40.resolve(cwd, fileArg) : path40.join(path40.resolve(cwd), ".cursor", "HANDOFF.md");
7781
+ const filePath = fileArg ? path43.resolve(cwd, fileArg) : path43.join(path43.resolve(cwd), ".cursor", "HANDOFF.md");
6595
7782
  let content = "";
6596
7783
  try {
6597
- content = await readFile20(filePath, "utf8");
7784
+ content = await readFile21(filePath, "utf8");
6598
7785
  } catch {
6599
7786
  console.log(JSON.stringify({ ok: true, warnings: [], note: "file missing" }));
6600
7787
  return;
@@ -6618,10 +7805,10 @@ var validateCommand = defineCommand18({
6618
7805
  process.exitCode = 2;
6619
7806
  return;
6620
7807
  }
6621
- const filePath = path40.resolve(cwd, fileArg);
7808
+ const filePath = path43.resolve(cwd, fileArg);
6622
7809
  let content = "";
6623
7810
  try {
6624
- content = await readFile20(filePath, "utf8");
7811
+ content = await readFile21(filePath, "utf8");
6625
7812
  } catch {
6626
7813
  console.log(JSON.stringify({ ok: true, warnings: [], note: "file missing" }));
6627
7814
  return;
@@ -6640,7 +7827,7 @@ var validateCommand = defineCommand18({
6640
7827
  },
6641
7828
  async run({ args }) {
6642
7829
  const cwd = typeof args.cwd === "string" ? args.cwd : process.cwd();
6643
- const resolved = await resolveEditedPath(path40.resolve(cwd));
7830
+ const resolved = await resolveEditedPath(path43.resolve(cwd));
6644
7831
  if (!resolved) {
6645
7832
  console.log(JSON.stringify({}));
6646
7833
  return;
@@ -6682,11 +7869,201 @@ var validateCommand = defineCommand18({
6682
7869
  }
6683
7870
  });
6684
7871
 
7872
+ // src/welcome/help-groups.ts
7873
+ import { bold, gray as gray4, underline } from "kolorist";
7874
+
7875
+ // src/welcome/screen.ts
7876
+ import {
7877
+ blue as blue2,
7878
+ cyan as cyan4,
7879
+ gray as gray3,
7880
+ options as koloristOptions2,
7881
+ lightCyan,
7882
+ trueColor,
7883
+ white as white2
7884
+ } from "kolorist";
7885
+ var KOLORIST_TRUECOLOR = 3;
7886
+ var HELMET_ASCII = [
7887
+ " ____",
7888
+ " .-' '-.",
7889
+ " / .--. \\",
7890
+ " | / \\ |",
7891
+ " | | () | |",
7892
+ " | \\ / |",
7893
+ " \\ '--' /",
7894
+ " '-.____.-'",
7895
+ " /_/ \\_\\"
7896
+ ];
7897
+ function hexToRgb(hex) {
7898
+ const m = /^#([0-9a-fA-F]{6})$/.exec(hex.trim());
7899
+ const hexBody = m?.[1];
7900
+ if (!hexBody) throw new Error(`invalid hex color: ${hex}`);
7901
+ const n = Number.parseInt(hexBody, 16);
7902
+ return [n >> 16 & 255, n >> 8 & 255, n & 255];
7903
+ }
7904
+ function outlineAnsi(line) {
7905
+ const [r, g, b] = hexToRgb(HELMET_OUTLINE);
7906
+ return trueColor(r, g, b)(line);
7907
+ }
7908
+ function withKoloristColor(fn) {
7909
+ const prevEnabled = koloristOptions2.enabled;
7910
+ const prevLevel = koloristOptions2.supportLevel;
7911
+ koloristOptions2.enabled = true;
7912
+ koloristOptions2.supportLevel = KOLORIST_TRUECOLOR;
7913
+ try {
7914
+ return fn();
7915
+ } finally {
7916
+ koloristOptions2.enabled = prevEnabled;
7917
+ koloristOptions2.supportLevel = prevLevel;
7918
+ }
7919
+ }
7920
+ function hasCliSubcommand(rawArgs) {
7921
+ return Boolean(rawArgs?.some((arg) => !arg.startsWith("-")));
7922
+ }
7923
+ function renderHelmetAscii(color) {
7924
+ if (!color) return HELMET_ASCII.join("\n");
7925
+ return withKoloristColor(
7926
+ () => HELMET_ASCII.map((line, i) => {
7927
+ if (i <= 1 || i >= HELMET_ASCII.length - 1) return outlineAnsi(line);
7928
+ if (i === 4) return lightCyan(line);
7929
+ return blue2(line);
7930
+ }).join("\n")
7931
+ );
7932
+ }
7933
+ var WELCOME_UTILITY_HINTS = [
7934
+ { cmd: "agent-kit --help", hint: "grouped commands" },
7935
+ { cmd: "agent-kit doctor", hint: "repository readiness" },
7936
+ { cmd: "agent-kit status", hint: "installed kit version" },
7937
+ { cmd: "agent-kit dashboard", hint: "Mission Control panel" },
7938
+ { cmd: "agent-kit init", hint: "guided install entry" }
7939
+ ];
7940
+ function renderWelcomeScreen(opts = {}) {
7941
+ const version = opts.version ?? KIT_VERSION;
7942
+ const color = shouldUseWelcomeColor(opts);
7943
+ const title = color ? white2("Mission Kit") : "Mission Kit";
7944
+ const product = color ? cyan4("agent-kit") : "agent-kit";
7945
+ const muted = (s) => color ? gray3(s) : s;
7946
+ const lines = [
7947
+ renderHelmetAscii(color),
7948
+ "",
7949
+ `${title} \xB7 ${product} v${version}`,
7950
+ muted("HITL framework for AI-assisted IDEs \xB7 @dadado/agent-kit-cli"),
7951
+ "",
7952
+ muted("Try:"),
7953
+ ...(() => {
7954
+ const width = Math.max(...WELCOME_UTILITY_HINTS.map(({ cmd }) => cmd.length));
7955
+ return WELCOME_UTILITY_HINTS.map(({ cmd, hint }) => {
7956
+ const pad = " ".repeat(width - cmd.length + 2);
7957
+ const left = color ? cyan4(` ${cmd}`) : ` ${cmd}`;
7958
+ return `${left}${pad}${muted(hint)}`;
7959
+ });
7960
+ })(),
7961
+ "",
7962
+ muted(
7963
+ "Chat HITL (start-project, git-staging/prod, run-plan-all) stays in Cursor slash commands."
7964
+ ),
7965
+ muted(
7966
+ `${shouldUseVisualMotion(opts) ? SPACE_MARKS.star : SPACE_MARKS.tick} ${tipAt(
7967
+ [...version].reduce((n, c) => n + c.charCodeAt(0), 0)
7968
+ )}`
7969
+ )
7970
+ ];
7971
+ return `${lines.join("\n")}
7972
+ `;
7973
+ }
7974
+ function printWelcomeScreen(opts = {}) {
7975
+ process.stdout.write(renderWelcomeScreen(opts));
7976
+ }
7977
+
7978
+ // src/welcome/help-groups.ts
7979
+ var CLI_HELP_GROUPS = [
7980
+ {
7981
+ id: "setup",
7982
+ title: "SETUP",
7983
+ commands: ["init", "install", "doctor", "status", "update", "add", "scan"]
7984
+ },
7985
+ {
7986
+ id: "mission",
7987
+ title: "MISSION",
7988
+ commands: ["handoff", "run-plan"]
7989
+ },
7990
+ {
7991
+ id: "dashboard",
7992
+ title: "DASHBOARD",
7993
+ commands: ["dashboard", "dashboard-broadcast", "monitors"]
7994
+ },
7995
+ {
7996
+ id: "integrity",
7997
+ title: "INTEGRITY",
7998
+ commands: ["validate", "guard", "hook", "cursor-awareness", "diff", "contribute"]
7999
+ }
8000
+ ];
8001
+ async function resolveCommandMeta(meta) {
8002
+ if (meta == null) return void 0;
8003
+ const value = typeof meta === "function" ? await meta() : meta;
8004
+ return await Promise.resolve(value);
8005
+ }
8006
+ async function resolveSubMeta(subCommands) {
8007
+ const out = /* @__PURE__ */ new Map();
8008
+ for (const [name, raw] of Object.entries(subCommands)) {
8009
+ const resolved = typeof raw === "function" ? await raw() : raw;
8010
+ const def = resolved;
8011
+ const meta = await resolveCommandMeta(def?.meta);
8012
+ out.set(name, meta?.description ?? "");
8013
+ }
8014
+ return out;
8015
+ }
8016
+ async function renderGroupedRootHelp(cmd) {
8017
+ const color = shouldUseWelcomeColor();
8018
+ const u = (s) => color ? underline(bold(s)) : s;
8019
+ const g = (s) => color ? gray4(s) : s;
8020
+ const meta = await resolveCommandMeta(cmd.meta);
8021
+ const name = meta?.name ?? "agent-kit";
8022
+ const version = meta?.version ?? KIT_VERSION;
8023
+ const description = meta?.description ?? "HITL framework for AI-assisted IDEs (Mission Kit family)";
8024
+ const subCommands = await (typeof cmd.subCommands === "function" ? cmd.subCommands() : cmd.subCommands);
8025
+ const descriptions = subCommands ? await resolveSubMeta(subCommands) : /* @__PURE__ */ new Map();
8026
+ const placed = new Set(CLI_HELP_GROUPS.flatMap((grp) => grp.commands));
8027
+ const leftover = [...descriptions.keys()].filter((c) => !placed.has(c)).sort();
8028
+ const groups = [...CLI_HELP_GROUPS];
8029
+ if (leftover.length > 0) {
8030
+ groups.push({ id: "other", title: "OTHER", commands: leftover });
8031
+ }
8032
+ const lines = [
8033
+ g(`${description} (${name} v${version})`),
8034
+ "",
8035
+ `${u("USAGE")} \`${name} <command> [OPTIONS]\``,
8036
+ ""
8037
+ ];
8038
+ for (const group of groups) {
8039
+ const rows = group.commands.filter((c) => descriptions.has(c));
8040
+ if (rows.length === 0) continue;
8041
+ lines.push(u(group.title), "");
8042
+ const width = Math.max(...rows.map((c) => c.length));
8043
+ for (const c of rows) {
8044
+ const pad = " ".repeat(width - c.length + 2);
8045
+ lines.push(` ${c}${pad}${descriptions.get(c) ?? ""}`);
8046
+ }
8047
+ lines.push("");
8048
+ }
8049
+ const tipMark = shouldUseVisualMotion() ? SPACE_MARKS.star : SPACE_MARKS.tick;
8050
+ const tip = tipAt([...version].reduce((n, c) => n + c.charCodeAt(0), 0));
8051
+ lines.push(
8052
+ g(`Use \`${name} <command> --help\` for more information about a command.`),
8053
+ g(
8054
+ "Chat-only HITL (start-project, backlog, git-staging/prod, run-plan-all) is not a CLI surface."
8055
+ ),
8056
+ g(`${tipMark} ${tip}`),
8057
+ ""
8058
+ );
8059
+ return lines.join("\n");
8060
+ }
8061
+
6685
8062
  // src/index.ts
6686
8063
  var main = defineCommand19({
6687
8064
  meta: {
6688
8065
  name: "agent-kit",
6689
- description: "HITL framework for AI-assisted IDEs",
8066
+ description: "HITL framework for AI-assisted IDEs (Mission Kit family)",
6690
8067
  version: KIT_VERSION
6691
8068
  },
6692
8069
  subCommands: {
@@ -6708,6 +8085,19 @@ var main = defineCommand19({
6708
8085
  guard: guardCommand,
6709
8086
  monitors: monitorsCommand,
6710
8087
  validate: validateCommand
8088
+ },
8089
+ async run({ rawArgs }) {
8090
+ if (hasCliSubcommand(rawArgs)) return;
8091
+ printWelcomeScreen();
8092
+ }
8093
+ });
8094
+ runMain(main, {
8095
+ showUsage: async (cmd, parent) => {
8096
+ if (!parent) {
8097
+ process.stdout.write(`${await renderGroupedRootHelp(cmd)}
8098
+ `);
8099
+ return;
8100
+ }
8101
+ await showUsage(cmd, parent);
6711
8102
  }
6712
8103
  });
6713
- runMain(main);