@dadado/agent-kit-cli 4.8.9 → 5.0.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/LICENSE +75 -21
- package/README.md +17 -0
- package/dashboard/dashboard-data.mjs +103 -0
- package/dashboard/dashboard.html +504 -171
- package/dashboard/lib/broadcast-share.mjs +251 -0
- package/dashboard/lib/guards.d.mts +10 -0
- package/dashboard/lib/guards.mjs +15 -2
- package/dashboard/lib/open-browser.d.mts +31 -0
- package/dashboard/lib/open-browser.mjs +298 -0
- package/dashboard/lib/semantic-model.mjs +215 -9
- package/dashboard/open.html +213 -0
- package/dashboard/serve.mjs +6 -2
- package/dashboard/start-broadcast.mjs +73 -30
- package/dashboard/start.mjs +21 -25
- package/dist/index.js +1417 -416
- package/package.json +4 -2
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,116 @@ 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
|
+
"0b5fe4729c3c68654434837630d775b847c082418f25b0b2c581700fe84b797a",
|
|
76
|
+
"1106b279558b6a20f87951102f172a680976f3e6530855d5dad083e1f89b9ba3",
|
|
73
77
|
"15566765ec95dad3bc3160f20ac910e01a13471df536e6d990f4b263700784a9",
|
|
74
78
|
"1681e6cf7b80a67cd999b94b8b6e16eb8caed0e11b32905848ba733da956e902",
|
|
79
|
+
"17818e47e06349ee35c1d8e1cc16e8bcbbbcc89c664014e9096e1f08c8567807",
|
|
75
80
|
"1974a5af0e4ba2bb0fc7e1872ef9ab70e323323ab216958c31731bcdd82c8241",
|
|
81
|
+
"1b384771cc6228215e31e6a9ab0943b13a5cdc463354a2641f1c0d85b4357e84",
|
|
82
|
+
"1b5f8c4197393dc9110d020f3b619b8502862adeb6522a6b9be4b49dd8794612",
|
|
83
|
+
"1c5c1af87951367ad04bb6fa5b1d6ebaaefbc0ee95989b624cabf406a1869907",
|
|
84
|
+
"1ded5c7b7eefc355fc1d17feb65ec472e35ed96076e47ce95f7bff5c1b84746c",
|
|
76
85
|
"1e58e0d4f2e8459b95a40d1b1e72c5c85a87230cbde945840a84073fe3a20422",
|
|
77
86
|
"285668d08f1b15d96286d27e29cc4a67e126e097fb8a940bdde33ccf25bd2bcd",
|
|
87
|
+
"2acdb148b07f3a4872b896da419af796fd3fefaace7ac79cdee0aab447004a83",
|
|
88
|
+
"2cd7fc018a384b0188737259ecf593ce25b4b75cc38b3e410063ac91745894b9",
|
|
89
|
+
"2f6cedfa61cb62126efd6b845bbf4d8d7913e39223d5f799ac09ef64c0489824",
|
|
90
|
+
"3204a7449f3031916a8d57b37144e5d8ded6c94aa5659493fa5cc2c7321f4eab",
|
|
91
|
+
"33982aea21a074d21a638850475ce1fefa7d043ec035de3855889cc68d60cf35",
|
|
92
|
+
"38675aec695e9d4e7f16e8fbe3facc3280f47fb875bf9ac50bd6837b92b7a7fa",
|
|
93
|
+
"3ee94e2441218025c085856679d17b4a4fe7a6e3d0274423abe20a422a5ab05d",
|
|
78
94
|
"3f3354825ff06ff9f9ae1e29b71784fef4250020c23c1b490940cf06b2be0f28",
|
|
95
|
+
"46a1dd4a07a2190ab9fb09abc05a3962277209f8e0c28033bb41450301474c86",
|
|
96
|
+
"48a0d55e21132a305946b195db7f6b75da78d44a1d32166fb0c69bef4a5ffbe2",
|
|
97
|
+
"4b62a46c74e5fa6a80283f21e8e6711958fbcd577051dc73ff4050643a031475",
|
|
79
98
|
"4bf23acbc9bd4e0f8468e61f4970e182600b72d9025d29e81ddbd83cb7b1710a",
|
|
80
99
|
"4c5167d0ebde685f266dce32c516a7c43876e0520647b93f07f2e2147e6a7b2f",
|
|
81
100
|
"4dc4efcd1b43c643d3889fb349df8d4964d0867c3a67eb67e2ec168fbb70fc75",
|
|
101
|
+
"4f133554d01cedc62280006f4cf6453e25a5cb41ef6dde4a43f9508cb36ef75d",
|
|
102
|
+
"4fe1e2bce8bd55a6decdd62c887a423cf85148b692f313aad4c777d0e50596b3",
|
|
82
103
|
"4ff844cf789cef4b1e809a55ca006316cd964bcdb2557d91c916a08e30ad15f6",
|
|
83
104
|
"533b41599c25698e5746166096a68fd4080efb5e16287b2a1ed5f73295cda446",
|
|
105
|
+
"54dda8addb74bc187ee23c31f8b7d17ad19e745a45169d29ffea094860eb18f2",
|
|
84
106
|
"570c3f91fd31a4cc1e56e997c85feab5c6756f45fc73dc741afe573912111231",
|
|
107
|
+
"5cb36f5dab846b2551afd05eab92c14122853c147d7f02136d1c04e01e3c9def",
|
|
85
108
|
"5d302d207521c53b9fa814ee0eba1376a5b2bfd2983c0373657c9fdf9884541b",
|
|
109
|
+
"5f5c125f7edf280029f214589ce3e088fc74bad717e30178405a3b537673c42f",
|
|
86
110
|
"621bc6cecf7c710a97e93463ca9b8872a1fcab1325c358cc8be65b418eebb339",
|
|
111
|
+
"693c732658e31d6403cf59e85985518f24ab92bf7fcdc3c225e9a79e54597a43",
|
|
87
112
|
"69715048da3963947aa8a39e7ac30ec01632fec862bfcb9559c46764b4833be0",
|
|
113
|
+
"6994595e3a62edb6c518006a6bece6848c7077977c041439cf7c822b89304a14",
|
|
88
114
|
"6a42e76fc038ca69ddad18541b8df161770c58980fefaf9e5e80d87407074bec",
|
|
89
115
|
"6a9af9e8a95dbb279347166c43dcee245ca673f9687841086b49911c54944c26",
|
|
90
116
|
"6b66ed7747ea2d19cb457f5af656e3c07f9f563f7f32b3951fa4ae5f27f0ac46",
|
|
117
|
+
"6dede0588274f5b44d833bcc696ec4776eb978ed24a20efdcc9c38caf3df1576",
|
|
118
|
+
"71991a10972b8b956cae93c88517e31741c95e351814082df65e44f813590210",
|
|
119
|
+
"75e591bfde61ca1ca3ca1303cc9bc487197af2f713c68f346a46e94ca9432fd5",
|
|
91
120
|
"76ae85bbc336c6f435d9653e8f254ae49774b4ebaecf7335697115933eb8797c",
|
|
121
|
+
"79878f0f6f59d1a0b65d94011c0cafe7b6f8e03e89f568aa638b8ba7a2c34076",
|
|
92
122
|
"8108b202c829627c3c2df33575016ebe942df42acf4f1cc393900be3b82b402c",
|
|
123
|
+
"819b7bd3ba9e8bf12dcd4f81a76c98e48261b34c397f339b36d36eb364d9c3f4",
|
|
93
124
|
"8619500b0c7024390d43e3aaefa2a2d731728d7c664aaf90e0acdd067f3397ce",
|
|
125
|
+
"875b0a8805280b2a2df43f2c42f9063fe0623a34fa19c3aa5d2ec48211f3ebae",
|
|
94
126
|
"8a11c80b0fbf6c4aabf60eec05b8cca7dccf5bca472787a8afa3028fd8fbedfa",
|
|
95
127
|
"8b25659727063013e8bf6d7925f815b2630c2919b21e6fdac354362bf122bfab",
|
|
96
128
|
"8c7edbacc74b1431fbecb04c9857c0e7a9cb1578fc6ca6367be888dbcec3c98f",
|
|
129
|
+
"8ffc181bb152c68a3353309bfdc87c42bdb925b98675cdba959c0cdb90b2adea",
|
|
130
|
+
"91ce0c933e3c8ee81927b8224f944ec5ce5445098a2ad154773092e3541dbf69",
|
|
131
|
+
"9309af07a0a99a56143386b3ddd2a35fae29378fba3f6b4c9297a5d1c02a9724",
|
|
97
132
|
"9439da963acbe5dd88a3aa4d6ba4fb297db666ce8f2c9d3917dbc6d6e517f9aa",
|
|
133
|
+
"989e7e1bdfb29dedf850405f28648810d008885196efe7ae38c6e7554c7ce45d",
|
|
98
134
|
"98d96e25f67aa9c234cb0c422f0d701c22a04bfe184fb4eb68295003d429552b",
|
|
99
135
|
"99031145609af46e7e0f315006bf633f47002eccece8eb7af8b5efd762313ade",
|
|
100
136
|
"9a3cea681399b42ef930a0720802378fe1b06ee73370df9ede9d0309c3c60ab2",
|
|
137
|
+
"9ccf52fe53c6fa4d731650ab4928ce50e201fa34f3e174fee70e3d29867fef2a",
|
|
101
138
|
"9d905aca0123f3018c81c579217104f1749d8da802f3837b2407fbad8f2d803f",
|
|
139
|
+
"9e324c52c1a9d1ceee738bccf47da92910aca9f7a5a21cc41738d9573c414830",
|
|
140
|
+
"a013a6a476160183d6457ff6c1c51ec228bdca359b11eb15cd9b7fc177a705db",
|
|
102
141
|
"a08d04f8efa121ad498f96873778c22630c9172c07c216839e348cd0bb7c2292",
|
|
142
|
+
"a7cd7058bcb6fbd962c37317fa16893bc86a270d0164af5a0e6f3f026aa408ad",
|
|
143
|
+
"a833a7131cdfc003b45a48cb26c6ce33dc218c1814156047db5211a79192e0a0",
|
|
144
|
+
"ac2f1f7d9e68f9c98fc4fe5ca348b1a90cf484141bbd116db871d63bdcc3c7c3",
|
|
103
145
|
"b5c7a10df90d209be07159b7ff9bfa24e869538a0b6026bb5ce79e1cdd5b2528",
|
|
146
|
+
"bd9fd1efacbd132c5dafc12cd0cf2b651a2b1fab7b2d7a9ba4a3e44128097f48",
|
|
147
|
+
"be964ff33a40280e13ce6071fb3e30ce87c374527efbb74c25c9d4534084a634",
|
|
104
148
|
"c96d522be440fbb638dbe953cebf1bb31185c431ac2e14446369cfb7d155a951",
|
|
105
149
|
"cf2bd11891b934484171fa705f470de284e3874402e1e487750735dbc7692857",
|
|
150
|
+
"d0e3bf82efbe32c09761689802ffea7838046dbdd4803b88ead5ec3dfc7efb7e",
|
|
106
151
|
"d4659e4bb6fd6022559fc632b17db6a39007c04bc0ded59017a0dacc013632ed",
|
|
107
152
|
"d484243167e13c34f9df7ff378baf2967f7cbfcd88170d4d32e745efbcae7480",
|
|
153
|
+
"d67ac8fa1edec6e50c82aeca064335e799e408d0e4bbc95e1407cf7463a61de3",
|
|
154
|
+
"d7ad53f06a41bcb2f2b998fd4a4c99209a0cf287cf0d0d4acbcc36c73dd46e4a",
|
|
108
155
|
"d96aa15dde45147aa40db64684688a4b5c9ad660dcd16c55156b31933a42c37e",
|
|
109
156
|
"d9ecd90f80ebc25db73ebed27c32596b9cfce518b3026a3a4d5787321ba6324a",
|
|
110
157
|
"dd01d051dbab257e16609b02c983bad6a576ed8473781b4f1df9ef2eb5f85795",
|
|
158
|
+
"dd9ff2ff8aa7bcb2cdfc59092ad73227631c1c3619b495131f7faee65224cb17",
|
|
111
159
|
"de6efabde6a1d69357926b88b5a4ed3eec19e11b82597cb999f5365f952305b8",
|
|
160
|
+
"debc0758b3e39294df85ccb7eab3455e93a2b8c608602d309a7b0ebdb6618805",
|
|
112
161
|
"e03b5764b908f9ed5c592108fbc11d5962d9256fd129a63ab4a39c8efaa2ef3e",
|
|
113
162
|
"e102e77f9f520f938d26861978249e4d85a8c8755d48c8e79b21fef9423325ee",
|
|
163
|
+
"e2d89f780c6ae5393dc05818f9e3e9a8bf6629eac05702eea8f9a25b578e3ff1",
|
|
164
|
+
"e2f1e9108fd450817ab42570db656b1e43b20b5ba703b14027ce540e1b8fe502",
|
|
165
|
+
"e37c62695b5c70451ef1a0c90b31b73a9c442ebc55fb3fc3f79c294b993b99ea",
|
|
166
|
+
"e3c32aec17e8f728cbd13162a123a9debffb8bbcec527a00b1c2df30a1a20080",
|
|
114
167
|
"e511e04064ffb8c429b659d2ae2e0f223e8cc51039ae816753095a5f7a160595",
|
|
168
|
+
"e5682d4696e874d09f12a1d65891fee5971201a29c654c08f5d3b90fb9a6a0b7",
|
|
169
|
+
"e57437bf790fda1d43b895e2b5b0a7d2d57635c319ac0e2439ba2222a825bcde",
|
|
170
|
+
"e8bb214c3e551c44f379db13c5962ef20698b505b0227d283d3ae632e1bc3626",
|
|
171
|
+
"ea36e74c9d592980cba5c08d04ee70fc59a6cd01e87a50bbfae4f668557df4b4",
|
|
115
172
|
"ebd41f8df764a14b3460016fafcfdde98c4c3a59a3c17f773451ef061fc42f95",
|
|
173
|
+
"ef181a4e2bd9f82f45628637b8d66925ffa20801558808f1b03ea72b7cb82a72",
|
|
116
174
|
"f061432a2606049717c0cbf098ef07b1da4df2b136c4cb9cdaa7ea44c876bcd6",
|
|
117
175
|
"f061d36a1180293519fbb560bbf659c621bd90885e995fca6eaf2eda9e418a50",
|
|
118
|
-
"f28731a82f4b394a5765fdefe2d1b667810831f6dcea400ca62bc66e1d65371c"
|
|
176
|
+
"f28731a82f4b394a5765fdefe2d1b667810831f6dcea400ca62bc66e1d65371c",
|
|
177
|
+
"f46c108457e4fe12953dca75b409e06fca9cb264baffabe0c0d04c89af471e56",
|
|
178
|
+
"f6bc17efa746680a44bbc49be67ac3f924d9c306c6d937f9959c57a1d3c5b9af",
|
|
179
|
+
"f981764422d468567b5aff31148dc659aeee22cb13dc0ecd873d737deaf07372",
|
|
180
|
+
"fa306a0cdb0f40c817e32164cd03b946564a02b4b7f7c3f4f0b9513096584d28",
|
|
181
|
+
"fa5cf460eb314437081f7cea30dc8041c0bd6fc3f560a74bc2d7be1bf07384b0"
|
|
119
182
|
]);
|
|
120
183
|
|
|
121
184
|
// src/lifecycle/paths.ts
|
|
@@ -475,11 +538,119 @@ function logApplyStats(stats) {
|
|
|
475
538
|
|
|
476
539
|
// src/registry/resolve.ts
|
|
477
540
|
import { execFile } from "child_process";
|
|
478
|
-
import { createHash as createHash2 } from "crypto";
|
|
541
|
+
import { createHash as createHash2, randomUUID } from "crypto";
|
|
542
|
+
import { mkdir as mkdir4, readFile as readFile4, rename, rm, stat, writeFile as writeFile3 } from "fs/promises";
|
|
479
543
|
import { homedir } from "os";
|
|
480
544
|
import path5 from "path";
|
|
481
545
|
import { promisify } from "util";
|
|
482
546
|
var execFileAsync = promisify(execFile);
|
|
547
|
+
var LOCK_TIMEOUT_MS = 6e4;
|
|
548
|
+
var LOCK_RETRY_MS = 200;
|
|
549
|
+
var LOCK_STALE_MS = 5 * 6e4;
|
|
550
|
+
var LOCK_REFRESH_MS = 1e4;
|
|
551
|
+
function lockOwnerPath(lockDir) {
|
|
552
|
+
return path5.join(lockDir, "owner.json");
|
|
553
|
+
}
|
|
554
|
+
function isProcessAlive(pid) {
|
|
555
|
+
try {
|
|
556
|
+
process.kill(pid, 0);
|
|
557
|
+
return true;
|
|
558
|
+
} catch {
|
|
559
|
+
return false;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
async function readLockOwner(lockDir) {
|
|
563
|
+
try {
|
|
564
|
+
const raw = await readFile4(lockOwnerPath(lockDir), "utf8");
|
|
565
|
+
const parsed = JSON.parse(raw);
|
|
566
|
+
if (typeof parsed.pid === "number" && typeof parsed.uuid === "string") {
|
|
567
|
+
return parsed;
|
|
568
|
+
}
|
|
569
|
+
} catch {
|
|
570
|
+
}
|
|
571
|
+
return null;
|
|
572
|
+
}
|
|
573
|
+
async function writeLockOwner(lockDir, owner) {
|
|
574
|
+
const finalPath = lockOwnerPath(lockDir);
|
|
575
|
+
const tmpPath = path5.join(lockDir, `owner.${owner.uuid}.tmp`);
|
|
576
|
+
await writeFile3(tmpPath, JSON.stringify(owner), "utf8");
|
|
577
|
+
await rename(tmpPath, finalPath);
|
|
578
|
+
}
|
|
579
|
+
async function refreshLockOwner(lockDir, uuid) {
|
|
580
|
+
const owner = await readLockOwner(lockDir);
|
|
581
|
+
if (!owner || owner.uuid !== uuid) return;
|
|
582
|
+
await writeLockOwner(lockDir, { ...owner, updatedAt: Date.now() });
|
|
583
|
+
}
|
|
584
|
+
async function releaseCacheLock(lockDir, uuid) {
|
|
585
|
+
const owner = await readLockOwner(lockDir);
|
|
586
|
+
if (!owner || owner.uuid !== uuid) {
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
try {
|
|
590
|
+
await rm(lockDir, { recursive: true, force: true });
|
|
591
|
+
} catch {
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
async function tryReclaimStaleLock(lockDir) {
|
|
595
|
+
let owner;
|
|
596
|
+
let lockStat;
|
|
597
|
+
try {
|
|
598
|
+
lockStat = await stat(lockDir);
|
|
599
|
+
owner = await readLockOwner(lockDir);
|
|
600
|
+
} catch {
|
|
601
|
+
return false;
|
|
602
|
+
}
|
|
603
|
+
const now2 = Date.now();
|
|
604
|
+
if (owner) {
|
|
605
|
+
const ownerFresh = now2 - owner.updatedAt < LOCK_STALE_MS;
|
|
606
|
+
if (isProcessAlive(owner.pid) && ownerFresh) {
|
|
607
|
+
return false;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
if (now2 - lockStat.mtimeMs > LOCK_STALE_MS) {
|
|
611
|
+
try {
|
|
612
|
+
await rm(lockDir, { recursive: true, force: true });
|
|
613
|
+
return true;
|
|
614
|
+
} catch {
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
return false;
|
|
618
|
+
}
|
|
619
|
+
async function acquireCacheLock(cacheDir) {
|
|
620
|
+
const lockDir = `${cacheDir}.lock`;
|
|
621
|
+
const deadline = Date.now() + LOCK_TIMEOUT_MS;
|
|
622
|
+
const uuid = randomUUID();
|
|
623
|
+
const owner = { pid: process.pid, uuid, updatedAt: Date.now() };
|
|
624
|
+
await mkdir4(path5.dirname(lockDir), { recursive: true });
|
|
625
|
+
while (Date.now() < deadline) {
|
|
626
|
+
try {
|
|
627
|
+
await mkdir4(lockDir, { recursive: false });
|
|
628
|
+
await writeLockOwner(lockDir, owner);
|
|
629
|
+
const refreshInterval = setInterval(() => {
|
|
630
|
+
refreshLockOwner(lockDir, uuid).catch(() => {
|
|
631
|
+
});
|
|
632
|
+
}, LOCK_REFRESH_MS);
|
|
633
|
+
return async () => {
|
|
634
|
+
clearInterval(refreshInterval);
|
|
635
|
+
await releaseCacheLock(lockDir, uuid);
|
|
636
|
+
};
|
|
637
|
+
} catch (err) {
|
|
638
|
+
const code = err.code;
|
|
639
|
+
if (code === "EEXIST") {
|
|
640
|
+
if (await tryReclaimStaleLock(lockDir)) {
|
|
641
|
+
continue;
|
|
642
|
+
}
|
|
643
|
+
await new Promise((r) => setTimeout(r, LOCK_RETRY_MS + Math.random() * 100));
|
|
644
|
+
continue;
|
|
645
|
+
}
|
|
646
|
+
if (code === "ENOENT") {
|
|
647
|
+
continue;
|
|
648
|
+
}
|
|
649
|
+
throw err;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
throw new Error(`Timed out waiting for cache lock on ${cacheDir}. Another install may be stuck.`);
|
|
653
|
+
}
|
|
483
654
|
var DEFAULT_REGISTRY_URL = "https://github.com/agent-kit-startup/agent-kit";
|
|
484
655
|
var DEFAULT_REGISTRY_REF = "main";
|
|
485
656
|
var SAFE_URL = /^https:\/\/[^\s]+$/;
|
|
@@ -504,15 +675,32 @@ async function hasRegistryIndex(root) {
|
|
|
504
675
|
return fileExists(path5.join(root, "registry", "registry.json"));
|
|
505
676
|
}
|
|
506
677
|
async function cloneRegistry(url, ref, dest) {
|
|
507
|
-
const { mkdir:
|
|
508
|
-
await
|
|
678
|
+
const { mkdir: mkdir7 } = await import("fs/promises");
|
|
679
|
+
await mkdir7(path5.dirname(dest), { recursive: true });
|
|
509
680
|
try {
|
|
510
681
|
await execFileAsync("git", ["clone", "--depth", "1", "--branch", ref, "--", url, dest], {
|
|
511
682
|
env: gitEnv()
|
|
512
683
|
});
|
|
513
|
-
} catch {
|
|
514
|
-
|
|
515
|
-
|
|
684
|
+
} catch (firstErr) {
|
|
685
|
+
try {
|
|
686
|
+
await execFileAsync("git", ["clone", "--depth", "1", "--", url, dest], { env: gitEnv() });
|
|
687
|
+
await execFileAsync("git", ["checkout", ref, "--"], { cwd: dest, env: gitEnv() });
|
|
688
|
+
} catch (secondErr) {
|
|
689
|
+
const msg = secondErr instanceof Error ? secondErr.message : String(secondErr);
|
|
690
|
+
if (/403|Authentication|could not read Username/i.test(msg)) {
|
|
691
|
+
throw new Error(
|
|
692
|
+
`Registry clone failed (auth/access): ${url}@${ref}. Check git credentials or use --registry <local-path> for a local checkout.`
|
|
693
|
+
);
|
|
694
|
+
}
|
|
695
|
+
if (/Could not resolve host|Network is unreachable|Connection refused/i.test(msg)) {
|
|
696
|
+
throw new Error(
|
|
697
|
+
`Registry clone failed (network): ${url}@${ref}. Check network/proxy settings or use --registry <local-path>.`
|
|
698
|
+
);
|
|
699
|
+
}
|
|
700
|
+
throw new Error(
|
|
701
|
+
`Registry clone failed: ${url}@${ref}. ${msg}. Use --registry <local-path> if you have a local checkout.`
|
|
702
|
+
);
|
|
703
|
+
}
|
|
516
704
|
}
|
|
517
705
|
}
|
|
518
706
|
async function refreshCache(cacheDir) {
|
|
@@ -556,15 +744,24 @@ async function resolveRegistryRoot(options) {
|
|
|
556
744
|
const ref = options.registryRef ?? DEFAULT_REGISTRY_REF;
|
|
557
745
|
assertSafeRegistrySource(url, ref);
|
|
558
746
|
const dest = path5.join(homedir(), ".cache", "agent-kit", "registry", cacheKey(url, ref));
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
747
|
+
const unlock = await acquireCacheLock(dest);
|
|
748
|
+
let ownershipTransferred = false;
|
|
749
|
+
try {
|
|
750
|
+
if (await hasRegistryIndex(dest)) {
|
|
751
|
+
await refreshCache(dest);
|
|
752
|
+
} else {
|
|
753
|
+
await cloneRegistry(url, ref, dest);
|
|
754
|
+
if (!await hasRegistryIndex(dest)) {
|
|
755
|
+
throw new Error(`Cloned ${url}@${ref} but registry/registry.json is missing`);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
ownershipTransferred = true;
|
|
759
|
+
return { root: dest, source: "remote-cache", url, ref, unlock };
|
|
760
|
+
} finally {
|
|
761
|
+
if (!ownershipTransferred) {
|
|
762
|
+
await unlock();
|
|
763
|
+
}
|
|
566
764
|
}
|
|
567
|
-
return { root: dest, source: "remote-cache", url, ref };
|
|
568
765
|
}
|
|
569
766
|
|
|
570
767
|
// src/lifecycle/resolve-cli.ts
|
|
@@ -826,7 +1023,7 @@ async function findPack(rootDir, packId) {
|
|
|
826
1023
|
}
|
|
827
1024
|
|
|
828
1025
|
// src/registry/install.ts
|
|
829
|
-
import { readFile as
|
|
1026
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
830
1027
|
import path8 from "path";
|
|
831
1028
|
function targetForMember(member) {
|
|
832
1029
|
switch (member.kind) {
|
|
@@ -945,7 +1142,7 @@ function packMemberTargets(member) {
|
|
|
945
1142
|
var addCommand = defineCommand({
|
|
946
1143
|
meta: {
|
|
947
1144
|
name: "add",
|
|
948
|
-
description: "Install a skill or L1 pack from the registry
|
|
1145
|
+
description: "Install a skill or L1 pack from the registry."
|
|
949
1146
|
},
|
|
950
1147
|
args: {
|
|
951
1148
|
id: {
|
|
@@ -982,64 +1179,68 @@ var addCommand = defineCommand({
|
|
|
982
1179
|
refresh: args.refresh,
|
|
983
1180
|
manifest: existing
|
|
984
1181
|
});
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1182
|
+
try {
|
|
1183
|
+
const protectedGlobs = resolveProtectedGlobs(existing);
|
|
1184
|
+
const index = await loadRegistry(registry.root);
|
|
1185
|
+
const asSkill = allSkills(index).find((s) => s.id === args.id);
|
|
1186
|
+
const asPack = allPacks(index).find((p) => p.id === args.id);
|
|
1187
|
+
const base = existing ? { ...existing } : buildManifest({
|
|
1188
|
+
version: KIT_VERSION,
|
|
1189
|
+
profile: "default",
|
|
1190
|
+
registryUrl: registry.url,
|
|
1191
|
+
registryRef: registry.ref
|
|
1192
|
+
});
|
|
1193
|
+
const finish = async (kind, id) => {
|
|
1194
|
+
if (kind === "skill") base.skills = upsertIdList(base.skills, id);
|
|
1195
|
+
else base.packs = upsertIdList(base.packs, id);
|
|
1196
|
+
if (registry.url || registry.ref) {
|
|
1197
|
+
base.registry = {
|
|
1198
|
+
url: registry.url ?? base.registry?.url,
|
|
1199
|
+
ref: registry.ref ?? base.registry?.ref
|
|
1200
|
+
};
|
|
1201
|
+
}
|
|
1202
|
+
base.version = KIT_VERSION;
|
|
1203
|
+
await saveManifest(args.cwd, base);
|
|
1204
|
+
};
|
|
1205
|
+
if (args.skill) {
|
|
1206
|
+
const skill = await findSkill(registry.root, args.id);
|
|
1207
|
+
const stats = await installSkill(registry.root, args.cwd, skill, { protectedGlobs });
|
|
1208
|
+
logApplyStats(stats);
|
|
1209
|
+
await finish("skill", skill.id);
|
|
1210
|
+
logger.success(`Skill '${skill.id}' added.`);
|
|
1211
|
+
return;
|
|
1003
1212
|
}
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
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;
|
|
1213
|
+
if (args.pack) {
|
|
1214
|
+
const pack = await findPack(registry.root, args.id);
|
|
1215
|
+
const stats = await installPack(registry.root, args.cwd, pack.id, { protectedGlobs });
|
|
1216
|
+
logApplyStats(stats);
|
|
1217
|
+
await finish("pack", pack.id);
|
|
1218
|
+
logger.success(`Pack '${pack.id}' added.`);
|
|
1219
|
+
return;
|
|
1220
|
+
}
|
|
1221
|
+
if (asSkill && asPack) {
|
|
1222
|
+
throw new Error(
|
|
1223
|
+
`'${args.id}' is both a skill and a pack. Disambiguate: agent-kit add --skill ${args.id} | agent-kit add --pack ${args.id}`
|
|
1224
|
+
);
|
|
1225
|
+
}
|
|
1226
|
+
if (asSkill) {
|
|
1227
|
+
const stats = await installSkill(registry.root, args.cwd, asSkill, { protectedGlobs });
|
|
1228
|
+
logApplyStats(stats);
|
|
1229
|
+
await finish("skill", asSkill.id);
|
|
1230
|
+
logger.success(`Skill '${asSkill.id}' added.`);
|
|
1231
|
+
return;
|
|
1232
|
+
}
|
|
1233
|
+
if (asPack) {
|
|
1234
|
+
const stats = await installPack(registry.root, args.cwd, asPack.id, { protectedGlobs });
|
|
1235
|
+
logApplyStats(stats);
|
|
1236
|
+
await finish("pack", asPack.id);
|
|
1237
|
+
logger.success(`Pack '${asPack.id}' added.`);
|
|
1238
|
+
return;
|
|
1239
|
+
}
|
|
1240
|
+
throw new Error(`'${args.id}' not found as skill or pack in registry.`);
|
|
1241
|
+
} finally {
|
|
1242
|
+
await registry.unlock?.();
|
|
1041
1243
|
}
|
|
1042
|
-
throw new Error(`'${args.id}' not found as skill or pack in registry.`);
|
|
1043
1244
|
}
|
|
1044
1245
|
});
|
|
1045
1246
|
|
|
@@ -1047,7 +1248,7 @@ var addCommand = defineCommand({
|
|
|
1047
1248
|
import { defineCommand as defineCommand2 } from "citty";
|
|
1048
1249
|
|
|
1049
1250
|
// src/lifecycle/contribute.ts
|
|
1050
|
-
import { mkdir as
|
|
1251
|
+
import { mkdir as mkdir5, readFile as readFile6, writeFile as writeFile4 } from "fs/promises";
|
|
1051
1252
|
import path9 from "path";
|
|
1052
1253
|
|
|
1053
1254
|
// src/lifecycle/contribute-gate.ts
|
|
@@ -1240,6 +1441,9 @@ var L0_ARTIFACTS = [
|
|
|
1240
1441
|
source: ".cursor/commands/dogfood.md",
|
|
1241
1442
|
target: ".cursor/commands/dogfood.md"
|
|
1242
1443
|
},
|
|
1444
|
+
// Factory-only: `.cursor/commands/public-issue-triage.md` is intentionally
|
|
1445
|
+
// omitted from L0 (and excluded from public-sync). See ADR
|
|
1446
|
+
// 2026-08-05_factory-only-public-issue-triage-command.md.
|
|
1243
1447
|
{
|
|
1244
1448
|
source: ".cursor/commands/cursor-update-awareness.md",
|
|
1245
1449
|
target: ".cursor/commands/cursor-update-awareness.md"
|
|
@@ -1338,7 +1542,7 @@ var L0_ARTIFACTS = [
|
|
|
1338
1542
|
// src/lifecycle/contribute.ts
|
|
1339
1543
|
async function readLocal(projectRoot, rel) {
|
|
1340
1544
|
try {
|
|
1341
|
-
return await
|
|
1545
|
+
return await readFile6(resolveContained(projectRoot, rel), "utf8");
|
|
1342
1546
|
} catch {
|
|
1343
1547
|
return null;
|
|
1344
1548
|
}
|
|
@@ -1414,7 +1618,7 @@ async function planContribute(options) {
|
|
|
1414
1618
|
}
|
|
1415
1619
|
if (kind === "drift") {
|
|
1416
1620
|
try {
|
|
1417
|
-
const upstream = await
|
|
1621
|
+
const upstream = await readFile6(
|
|
1418
1622
|
resolveContained(options.registryRoot, registryPath),
|
|
1419
1623
|
"utf8"
|
|
1420
1624
|
);
|
|
@@ -1480,10 +1684,10 @@ async function writeContributeToRegistry(projectRoot, registryRoot, accepted) {
|
|
|
1480
1684
|
const written = [];
|
|
1481
1685
|
for (const c of accepted) {
|
|
1482
1686
|
if (!c.registryPath) continue;
|
|
1483
|
-
const content = await
|
|
1687
|
+
const content = await readFile6(resolveContained(projectRoot, c.projectPath), "utf8");
|
|
1484
1688
|
const dest = resolveContained(registryRoot, c.registryPath);
|
|
1485
|
-
await
|
|
1486
|
-
await
|
|
1689
|
+
await mkdir5(path9.dirname(dest), { recursive: true });
|
|
1690
|
+
await writeFile4(dest, content, "utf8");
|
|
1487
1691
|
written.push(c.registryPath);
|
|
1488
1692
|
}
|
|
1489
1693
|
return written;
|
|
@@ -1554,57 +1758,61 @@ var contributeCommand = defineCommand2({
|
|
|
1554
1758
|
refresh: args.refresh,
|
|
1555
1759
|
manifest
|
|
1556
1760
|
});
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
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";
|
|
1761
|
+
try {
|
|
1762
|
+
if (registry.source !== "flag" && args.write) {
|
|
1763
|
+
logger.warn(
|
|
1764
|
+
"contribute --write needs a local kit checkout. Pass --registry /path/to/agent-kit."
|
|
1765
|
+
);
|
|
1766
|
+
return;
|
|
1767
|
+
}
|
|
1768
|
+
logger.info(`Registry: ${registry.root} (${registry.source})`);
|
|
1769
|
+
const plan = await planContribute({
|
|
1770
|
+
registryRoot: registry.root,
|
|
1771
|
+
projectRoot: args.cwd,
|
|
1772
|
+
manifest,
|
|
1773
|
+
extraPaths: parsePaths(args.path),
|
|
1774
|
+
includeDrift: args.drift
|
|
1775
|
+
});
|
|
1580
1776
|
console.log(
|
|
1581
|
-
|
|
1777
|
+
`Contribute plan: accepted=${plan.accepted.length} rejected=${plan.rejected.length} (drift scan=${args.drift})`
|
|
1582
1778
|
);
|
|
1583
|
-
|
|
1584
|
-
|
|
1779
|
+
if (plan.candidates.length === 0) {
|
|
1780
|
+
logger.success("Nothing to contribute (no drift / no --path).");
|
|
1781
|
+
return;
|
|
1585
1782
|
}
|
|
1783
|
+
for (const c of plan.candidates) {
|
|
1784
|
+
const mark = c.gateOk ? "ok" : "FAIL";
|
|
1785
|
+
console.log(
|
|
1786
|
+
`${mark.padEnd(4)} ${c.kind.padEnd(5)} ${c.projectPath} \u2192 ${c.registryPath || "(unmapped)"}`
|
|
1787
|
+
);
|
|
1788
|
+
for (const issue of c.issues) {
|
|
1789
|
+
console.log(` [${issue.code}] ${issue.message}`);
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
if (plan.accepted.length === 0) {
|
|
1793
|
+
logger.warn("No files passed the contribute gate.");
|
|
1794
|
+
return;
|
|
1795
|
+
}
|
|
1796
|
+
console.log("\n--- Suggested PR body ---\n");
|
|
1797
|
+
console.log(formatPrBody(plan.accepted));
|
|
1798
|
+
if (!args.write) {
|
|
1799
|
+
logger.info(
|
|
1800
|
+
"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)."
|
|
1801
|
+
);
|
|
1802
|
+
return;
|
|
1803
|
+
}
|
|
1804
|
+
const written = await writeContributeToRegistry(args.cwd, registry.root, plan.accepted);
|
|
1805
|
+
logger.success(`Wrote ${written.length} file(s) into ${registry.root}`);
|
|
1806
|
+
for (const p of written) console.log(` + ${p}`);
|
|
1807
|
+
console.log("\nNext (HITL \u2014 review before push):");
|
|
1808
|
+
console.log(` cd ${registry.root}`);
|
|
1809
|
+
console.log(" git checkout -b contribute/<short-topic>");
|
|
1810
|
+
console.log(' git add <files> && git commit -m "feat: contribute <topic> from consumer"');
|
|
1811
|
+
console.log(" git push -u origin HEAD");
|
|
1812
|
+
console.log(' gh pr create --base main --title "feat: contribute <topic>" --body-file -');
|
|
1813
|
+
} finally {
|
|
1814
|
+
await registry.unlock?.();
|
|
1586
1815
|
}
|
|
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
1816
|
}
|
|
1609
1817
|
});
|
|
1610
1818
|
|
|
@@ -1612,13 +1820,30 @@ var contributeCommand = defineCommand2({
|
|
|
1612
1820
|
import { defineCommand as defineCommand3 } from "citty";
|
|
1613
1821
|
|
|
1614
1822
|
// src/lifecycle/cursor-update-awareness.ts
|
|
1615
|
-
import { readFile as
|
|
1823
|
+
import { access as access2, readFile as readFile7 } from "fs/promises";
|
|
1616
1824
|
import path10 from "path";
|
|
1617
1825
|
var DEFAULT_CURSOR_CHANGELOG_URL = "https://cursor.com/changelog";
|
|
1618
1826
|
var CHANGELOG_FETCH_TIMEOUT_MS = 15e3;
|
|
1619
1827
|
var CURSOR_VERSION_MAJOR_MAX = 20;
|
|
1620
1828
|
var INVENTORY_REL = path10.join("docs", "cursor-native-audit.md");
|
|
1621
1829
|
var FEATURES_REL = path10.join("docs", "cursor-3-features.md");
|
|
1830
|
+
async function resolveInventoryRoot(cwd) {
|
|
1831
|
+
let dir = path10.resolve(cwd);
|
|
1832
|
+
for (; ; ) {
|
|
1833
|
+
try {
|
|
1834
|
+
await access2(path10.join(dir, INVENTORY_REL));
|
|
1835
|
+
return dir;
|
|
1836
|
+
} catch {
|
|
1837
|
+
}
|
|
1838
|
+
const parent = path10.dirname(dir);
|
|
1839
|
+
if (parent === dir) break;
|
|
1840
|
+
dir = parent;
|
|
1841
|
+
}
|
|
1842
|
+
return null;
|
|
1843
|
+
}
|
|
1844
|
+
function missingInventoryMessage() {
|
|
1845
|
+
return `Missing inventory at ${INVENTORY_REL}. Run from the kit/repo root that contains that file, or pass --cwd <path>.`;
|
|
1846
|
+
}
|
|
1622
1847
|
var DEFAULT_PREFS = {
|
|
1623
1848
|
enabled: false,
|
|
1624
1849
|
intervalDays: 7,
|
|
@@ -1786,8 +2011,6 @@ function baseResult(partial) {
|
|
|
1786
2011
|
};
|
|
1787
2012
|
}
|
|
1788
2013
|
async function checkCursorUpdateAwareness(cwd, options = {}) {
|
|
1789
|
-
const inventoryPath = path10.join(cwd, INVENTORY_REL);
|
|
1790
|
-
const featuresPath = path10.join(cwd, FEATURES_REL);
|
|
1791
2014
|
const prefs = readCursorUpdateCheckPrefs(await loadContextConfig(cwd));
|
|
1792
2015
|
const changelogUrl = options.changelogUrl ?? prefs.changelogUrl;
|
|
1793
2016
|
if (options.respectPrefs) {
|
|
@@ -1820,9 +2043,26 @@ async function checkCursorUpdateAwareness(cwd, options = {}) {
|
|
|
1820
2043
|
});
|
|
1821
2044
|
}
|
|
1822
2045
|
}
|
|
2046
|
+
const inventoryRoot = await resolveInventoryRoot(cwd);
|
|
2047
|
+
if (!inventoryRoot) {
|
|
2048
|
+
return baseResult({
|
|
2049
|
+
status: "error",
|
|
2050
|
+
inventoryPath: INVENTORY_REL,
|
|
2051
|
+
featuresPath: FEATURES_REL,
|
|
2052
|
+
changelogUrl,
|
|
2053
|
+
latestCursorVersion: null,
|
|
2054
|
+
lastSeenCursorVersion: prefs.lastSeenCursorVersion,
|
|
2055
|
+
inventoryRefreshed: null,
|
|
2056
|
+
openActionIds: [],
|
|
2057
|
+
gaps: [],
|
|
2058
|
+
message: missingInventoryMessage()
|
|
2059
|
+
});
|
|
2060
|
+
}
|
|
2061
|
+
const inventoryPath = path10.join(inventoryRoot, INVENTORY_REL);
|
|
2062
|
+
const featuresPath = path10.join(inventoryRoot, FEATURES_REL);
|
|
1823
2063
|
let inventoryMd;
|
|
1824
2064
|
try {
|
|
1825
|
-
inventoryMd = await
|
|
2065
|
+
inventoryMd = await readFile7(inventoryPath, "utf8");
|
|
1826
2066
|
} catch {
|
|
1827
2067
|
return baseResult({
|
|
1828
2068
|
status: "error",
|
|
@@ -1834,7 +2074,7 @@ async function checkCursorUpdateAwareness(cwd, options = {}) {
|
|
|
1834
2074
|
inventoryRefreshed: null,
|
|
1835
2075
|
openActionIds: [],
|
|
1836
2076
|
gaps: [],
|
|
1837
|
-
message:
|
|
2077
|
+
message: missingInventoryMessage()
|
|
1838
2078
|
});
|
|
1839
2079
|
}
|
|
1840
2080
|
const inventoryRefreshed = parseInventoryRefreshed(inventoryMd);
|
|
@@ -1865,7 +2105,7 @@ async function checkCursorUpdateAwareness(cwd, options = {}) {
|
|
|
1865
2105
|
}
|
|
1866
2106
|
}
|
|
1867
2107
|
try {
|
|
1868
|
-
const featuresMd = await
|
|
2108
|
+
const featuresMd = await readFile7(featuresPath, "utf8");
|
|
1869
2109
|
if (parseFeatureMapNames(featuresMd).length === 0) {
|
|
1870
2110
|
gaps.push({
|
|
1871
2111
|
id: "features-map-empty",
|
|
@@ -1947,7 +2187,7 @@ async function checkCursorUpdateAwareness(cwd, options = {}) {
|
|
|
1947
2187
|
var cursorAwarenessCommand = defineCommand3({
|
|
1948
2188
|
meta: {
|
|
1949
2189
|
name: "cursor-awareness",
|
|
1950
|
-
description: "Opt-in advisory:
|
|
2190
|
+
description: "Opt-in advisory: Cursor changelog vs native-audit gaps (never apply)."
|
|
1951
2191
|
},
|
|
1952
2192
|
args: {
|
|
1953
2193
|
cwd: {
|
|
@@ -2009,17 +2249,124 @@ var cursorAwarenessCommand = defineCommand3({
|
|
|
2009
2249
|
});
|
|
2010
2250
|
|
|
2011
2251
|
// src/commands/dashboard-broadcast.ts
|
|
2012
|
-
import { spawn as
|
|
2013
|
-
import { access as
|
|
2252
|
+
import { spawn as spawn3 } from "child_process";
|
|
2253
|
+
import { access as access4 } from "fs/promises";
|
|
2014
2254
|
import path12 from "path";
|
|
2015
2255
|
import { defineCommand as defineCommand5 } from "citty";
|
|
2016
2256
|
|
|
2017
2257
|
// src/commands/dashboard.ts
|
|
2018
|
-
import { execFileSync, spawn } from "child_process";
|
|
2019
|
-
import {
|
|
2258
|
+
import { execFileSync, spawn as spawn2 } from "child_process";
|
|
2259
|
+
import { existsSync, readFileSync as readFileSync2, realpathSync } from "fs";
|
|
2260
|
+
import { access as access3 } from "fs/promises";
|
|
2020
2261
|
import path11 from "path";
|
|
2021
2262
|
import { fileURLToPath } from "url";
|
|
2022
2263
|
import { defineCommand as defineCommand4 } from "citty";
|
|
2264
|
+
|
|
2265
|
+
// ../../dashboard/lib/guards.mjs
|
|
2266
|
+
import { randomBytes, timingSafeEqual } from "crypto";
|
|
2267
|
+
import { networkInterfaces } from "os";
|
|
2268
|
+
import { resolve } from "path";
|
|
2269
|
+
var KIT_ROOT_ENV_KEYS = Object.freeze(["MISSION_CONTROL_KIT_ROOT", "AGENT_KIT_HOME"]);
|
|
2270
|
+
var CONTEXT_CONFIG_REL = ".cursor/context/config.json";
|
|
2271
|
+
var CONFIG_PERSONA_IDS = Object.freeze(["autopilot", "night-shift", "ghost-runner"]);
|
|
2272
|
+
var CONFIG_PERSONA_MODES = Object.freeze(["continue-plan", "run-plan", "cli-run-plan"]);
|
|
2273
|
+
var CONFIG_REVIEW_BACKENDS = Object.freeze(["claude"]);
|
|
2274
|
+
var CONFIG_REVIEW_MODES = Object.freeze(["paste", "autonomous"]);
|
|
2275
|
+
var CONFIG_REVIEW_PREFLIGHT = Object.freeze(["off", "warn", "block"]);
|
|
2276
|
+
function resolveContextConfigPath(repoRoot, fsHooks = {}) {
|
|
2277
|
+
const exists2 = fsHooks.existsSync;
|
|
2278
|
+
const realpath = fsHooks.realpathSync;
|
|
2279
|
+
const mkdir7 = fsHooks.mkdirSync;
|
|
2280
|
+
if (typeof repoRoot !== "string" || !repoRoot) {
|
|
2281
|
+
return { ok: false, error: "invalid repo root" };
|
|
2282
|
+
}
|
|
2283
|
+
const abs = resolve(repoRoot, CONTEXT_CONFIG_REL);
|
|
2284
|
+
const contextDir = resolve(repoRoot, ".cursor", "context");
|
|
2285
|
+
const absNorm = abs.replace(/\\/g, "/");
|
|
2286
|
+
if (!absNorm.endsWith("/.cursor/context/config.json")) {
|
|
2287
|
+
return { ok: false, error: "path escape" };
|
|
2288
|
+
}
|
|
2289
|
+
try {
|
|
2290
|
+
if (typeof mkdir7 === "function" && typeof exists2 === "function" && !exists2(contextDir)) {
|
|
2291
|
+
mkdir7(contextDir, { recursive: true });
|
|
2292
|
+
}
|
|
2293
|
+
if (typeof realpath === "function" && typeof exists2 === "function" && exists2(abs)) {
|
|
2294
|
+
const fileReal = String(realpath(abs)).replace(/\\/g, "/");
|
|
2295
|
+
const rootReal = String(realpath(repoRoot)).replace(/\\/g, "/");
|
|
2296
|
+
if (!fileReal.startsWith(`${rootReal}/`)) {
|
|
2297
|
+
return { ok: false, error: "path escape" };
|
|
2298
|
+
}
|
|
2299
|
+
if (!fileReal.endsWith("/.cursor/context/config.json")) {
|
|
2300
|
+
return { ok: false, error: "path escape" };
|
|
2301
|
+
}
|
|
2302
|
+
return { ok: true, path: realpath(abs) };
|
|
2303
|
+
}
|
|
2304
|
+
} catch {
|
|
2305
|
+
return { ok: false, error: "path escape" };
|
|
2306
|
+
}
|
|
2307
|
+
return { ok: true, path: abs };
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2310
|
+
// ../../dashboard/lib/open-browser.mjs
|
|
2311
|
+
import { spawn, spawnSync } from "child_process";
|
|
2312
|
+
import { readFileSync } from "fs";
|
|
2313
|
+
import { platform as osPlatform } from "os";
|
|
2314
|
+
var OS_DEFAULT_TOKENS = /* @__PURE__ */ new Set(["", "default", "os", "ask"]);
|
|
2315
|
+
function isSafePreferredBrowser(value) {
|
|
2316
|
+
if (typeof value !== "string") return false;
|
|
2317
|
+
const s = value.trim();
|
|
2318
|
+
if (!s) return false;
|
|
2319
|
+
if (/[/\\]/.test(s)) return false;
|
|
2320
|
+
if (/[\0-\x1f\x7f]/.test(s)) return false;
|
|
2321
|
+
if (/[$`;&|<>(){}[\]!*?#~"'%^=,+]/.test(s)) return false;
|
|
2322
|
+
if (s.includes(":")) return false;
|
|
2323
|
+
if (/^-/.test(s)) return false;
|
|
2324
|
+
return true;
|
|
2325
|
+
}
|
|
2326
|
+
function normalizePreferredBrowser(value) {
|
|
2327
|
+
if (value == null) return null;
|
|
2328
|
+
const s = String(value).trim();
|
|
2329
|
+
if (!s || OS_DEFAULT_TOKENS.has(s.toLowerCase())) return null;
|
|
2330
|
+
if (!isSafePreferredBrowser(s)) return null;
|
|
2331
|
+
return s;
|
|
2332
|
+
}
|
|
2333
|
+
function readPreferredBrowserFromConfig(configPath, fsHooks = {}) {
|
|
2334
|
+
const read = fsHooks.readFileSync ?? readFileSync;
|
|
2335
|
+
try {
|
|
2336
|
+
const raw = read(configPath, "utf8");
|
|
2337
|
+
const data = JSON.parse(raw);
|
|
2338
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) return null;
|
|
2339
|
+
const mc = data.missionControl;
|
|
2340
|
+
if (!mc || typeof mc !== "object" || Array.isArray(mc)) return null;
|
|
2341
|
+
return mc.preferredBrowser ?? null;
|
|
2342
|
+
} catch {
|
|
2343
|
+
return null;
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2346
|
+
|
|
2347
|
+
// src/commands/dashboard.ts
|
|
2348
|
+
function readPreferredBrowserFromWorkspace(cwd, readFile22 = readFileSync2) {
|
|
2349
|
+
const resolved = resolveContextConfigPath(path11.resolve(cwd), {
|
|
2350
|
+
existsSync,
|
|
2351
|
+
realpathSync
|
|
2352
|
+
});
|
|
2353
|
+
if (!resolved.ok) return null;
|
|
2354
|
+
const value = readPreferredBrowserFromConfig(resolved.path, { readFileSync: readFile22 });
|
|
2355
|
+
return normalizePreferredBrowser(value);
|
|
2356
|
+
}
|
|
2357
|
+
function applyDashboardOpenEnv(env, opts) {
|
|
2358
|
+
const next = { ...env };
|
|
2359
|
+
if (opts.noOpen) next.MISSION_CONTROL_NO_OPEN = "1";
|
|
2360
|
+
const flag = opts.browser?.trim();
|
|
2361
|
+
if (flag) {
|
|
2362
|
+
const safe = normalizePreferredBrowser(flag);
|
|
2363
|
+
if (safe) next.MISSION_CONTROL_PREFERRED_BROWSER = safe;
|
|
2364
|
+
} else if (!next.MISSION_CONTROL_PREFERRED_BROWSER && opts.cwd) {
|
|
2365
|
+
const fromConfig = readPreferredBrowserFromWorkspace(opts.cwd);
|
|
2366
|
+
if (fromConfig) next.MISSION_CONTROL_PREFERRED_BROWSER = fromConfig;
|
|
2367
|
+
}
|
|
2368
|
+
return next;
|
|
2369
|
+
}
|
|
2023
2370
|
function bundledDashboardCandidates(filename, moduleUrl = import.meta.url) {
|
|
2024
2371
|
const here = path11.dirname(fileURLToPath(moduleUrl));
|
|
2025
2372
|
return [
|
|
@@ -2034,7 +2381,7 @@ function bundledDashboardCandidates(filename, moduleUrl = import.meta.url) {
|
|
|
2034
2381
|
async function firstExisting(candidates) {
|
|
2035
2382
|
for (const candidate2 of candidates) {
|
|
2036
2383
|
try {
|
|
2037
|
-
await
|
|
2384
|
+
await access3(candidate2);
|
|
2038
2385
|
return path11.resolve(candidate2);
|
|
2039
2386
|
} catch {
|
|
2040
2387
|
}
|
|
@@ -2046,7 +2393,7 @@ async function findDashboardStart(cwd, env = process.env, options = {}) {
|
|
|
2046
2393
|
for (; ; ) {
|
|
2047
2394
|
const candidate2 = path11.join(dir, "dashboard", "start.mjs");
|
|
2048
2395
|
try {
|
|
2049
|
-
await
|
|
2396
|
+
await access3(candidate2);
|
|
2050
2397
|
return candidate2;
|
|
2051
2398
|
} catch {
|
|
2052
2399
|
}
|
|
@@ -2059,14 +2406,14 @@ async function findDashboardStart(cwd, env = process.env, options = {}) {
|
|
|
2059
2406
|
if (!base || typeof base !== "string" || !base.trim()) continue;
|
|
2060
2407
|
const candidate2 = path11.join(path11.resolve(base.trim()), "dashboard", "start.mjs");
|
|
2061
2408
|
try {
|
|
2062
|
-
await
|
|
2409
|
+
await access3(candidate2);
|
|
2063
2410
|
return candidate2;
|
|
2064
2411
|
} catch {
|
|
2065
2412
|
}
|
|
2066
2413
|
}
|
|
2067
2414
|
const sibling = path11.join(path11.resolve(cwd), "..", "agent-kit", "dashboard", "start.mjs");
|
|
2068
2415
|
try {
|
|
2069
|
-
await
|
|
2416
|
+
await access3(sibling);
|
|
2070
2417
|
return path11.resolve(sibling);
|
|
2071
2418
|
} catch {
|
|
2072
2419
|
}
|
|
@@ -2076,6 +2423,15 @@ async function findDashboardStart(cwd, env = process.env, options = {}) {
|
|
|
2076
2423
|
}
|
|
2077
2424
|
function resolveDashboardSnapshotRoot(cwd) {
|
|
2078
2425
|
const abs = path11.resolve(cwd);
|
|
2426
|
+
let dir = abs;
|
|
2427
|
+
for (; ; ) {
|
|
2428
|
+
if (existsSync(path11.join(dir, ".cursor", "agent-kit.json"))) {
|
|
2429
|
+
return dir;
|
|
2430
|
+
}
|
|
2431
|
+
const parent = path11.dirname(dir);
|
|
2432
|
+
if (parent === dir) break;
|
|
2433
|
+
dir = parent;
|
|
2434
|
+
}
|
|
2079
2435
|
try {
|
|
2080
2436
|
const top = execFileSync("git", ["-C", abs, "rev-parse", "--show-toplevel"], {
|
|
2081
2437
|
encoding: "utf8",
|
|
@@ -2087,46 +2443,68 @@ function resolveDashboardSnapshotRoot(cwd) {
|
|
|
2087
2443
|
return abs;
|
|
2088
2444
|
}
|
|
2089
2445
|
function runStartScript(startPath, env) {
|
|
2090
|
-
return new Promise((
|
|
2091
|
-
const child =
|
|
2446
|
+
return new Promise((resolve2, reject) => {
|
|
2447
|
+
const child = spawn2(process.execPath, [startPath], {
|
|
2092
2448
|
cwd: path11.dirname(path11.dirname(startPath)),
|
|
2093
2449
|
env,
|
|
2094
2450
|
stdio: "inherit"
|
|
2095
2451
|
});
|
|
2096
2452
|
child.on("error", reject);
|
|
2097
|
-
child.on("close", (code) =>
|
|
2453
|
+
child.on("close", (code) => resolve2(code ?? 1));
|
|
2098
2454
|
});
|
|
2099
2455
|
}
|
|
2100
2456
|
var dashboardCommand = defineCommand4({
|
|
2101
2457
|
meta: {
|
|
2102
2458
|
name: "dashboard",
|
|
2103
|
-
description: "Start Mission Control for this workspace (
|
|
2459
|
+
description: "Start Mission Control for this workspace (loopback; opens panel URL)."
|
|
2104
2460
|
},
|
|
2105
2461
|
args: {
|
|
2106
2462
|
cwd: {
|
|
2107
2463
|
type: "string",
|
|
2108
2464
|
default: process.cwd(),
|
|
2109
|
-
description: "Workspace to snapshot (git root
|
|
2465
|
+
description: "Workspace to snapshot (nearest .cursor/agent-kit.json, else git root); also searched upward for dashboard/start.mjs"
|
|
2110
2466
|
},
|
|
2111
2467
|
"no-open": {
|
|
2112
2468
|
type: "boolean",
|
|
2113
2469
|
default: false,
|
|
2114
2470
|
description: "Do not open a browser; only ensure the server is up and print the URL"
|
|
2471
|
+
},
|
|
2472
|
+
browser: {
|
|
2473
|
+
type: "string",
|
|
2474
|
+
description: "Preferred browser app/binary for this launch (overrides config missionControl.preferredBrowser)"
|
|
2115
2475
|
}
|
|
2116
2476
|
},
|
|
2117
2477
|
async run({ args }) {
|
|
2118
2478
|
const snapshotRoot = resolveDashboardSnapshotRoot(args.cwd);
|
|
2119
2479
|
const startPath = await findDashboardStart(args.cwd);
|
|
2120
2480
|
if (!startPath) {
|
|
2121
|
-
logger.error(
|
|
2122
|
-
|
|
2481
|
+
logger.error("No dashboard/start.mjs found.");
|
|
2482
|
+
console.error(
|
|
2483
|
+
[
|
|
2484
|
+
"",
|
|
2485
|
+
"The dashboard runtime is not available in this workspace.",
|
|
2486
|
+
"L0 install provides the /dashboard command text but not the panel itself.",
|
|
2487
|
+
"",
|
|
2488
|
+
"Recovery (pick one):",
|
|
2489
|
+
" 1. Upgrade the CLI: npx @dadado/agent-kit-cli@latest dashboard",
|
|
2490
|
+
" (Path C ships dashboard/ from 4.8.2 onward)",
|
|
2491
|
+
" 2. Set an env var pointing to an agent-kit checkout:",
|
|
2492
|
+
" export MISSION_CONTROL_KIT_ROOT=/path/to/agent-kit",
|
|
2493
|
+
" agent-kit dashboard",
|
|
2494
|
+
" 3. Place an agent-kit sibling: ../agent-kit/dashboard/start.mjs",
|
|
2495
|
+
" 4. Run directly from a kit tree: node dashboard/start.mjs",
|
|
2496
|
+
"",
|
|
2497
|
+
"Works in Cursor, VS Code, and any Node.js terminal.",
|
|
2498
|
+
""
|
|
2499
|
+
].join("\n")
|
|
2123
2500
|
);
|
|
2124
2501
|
process.exitCode = 1;
|
|
2125
2502
|
return;
|
|
2126
2503
|
}
|
|
2127
|
-
const env =
|
|
2128
|
-
|
|
2129
|
-
|
|
2504
|
+
const env = applyDashboardOpenEnv(
|
|
2505
|
+
{ ...process.env, MISSION_CONTROL_REPO_ROOT: snapshotRoot },
|
|
2506
|
+
{ noOpen: Boolean(args["no-open"]), browser: args.browser, cwd: snapshotRoot }
|
|
2507
|
+
);
|
|
2130
2508
|
const code = await runStartScript(startPath, env);
|
|
2131
2509
|
if (code !== 0) process.exitCode = code;
|
|
2132
2510
|
}
|
|
@@ -2138,7 +2516,7 @@ async function findDashboardBroadcastStart(cwd, env = process.env, options = {})
|
|
|
2138
2516
|
for (; ; ) {
|
|
2139
2517
|
const candidate2 = path12.join(dir, "dashboard", "start-broadcast.mjs");
|
|
2140
2518
|
try {
|
|
2141
|
-
await
|
|
2519
|
+
await access4(candidate2);
|
|
2142
2520
|
return candidate2;
|
|
2143
2521
|
} catch {
|
|
2144
2522
|
}
|
|
@@ -2151,7 +2529,7 @@ async function findDashboardBroadcastStart(cwd, env = process.env, options = {})
|
|
|
2151
2529
|
if (!base || typeof base !== "string" || !base.trim()) continue;
|
|
2152
2530
|
const candidate2 = path12.join(path12.resolve(base.trim()), "dashboard", "start-broadcast.mjs");
|
|
2153
2531
|
try {
|
|
2154
|
-
await
|
|
2532
|
+
await access4(candidate2);
|
|
2155
2533
|
return candidate2;
|
|
2156
2534
|
} catch {
|
|
2157
2535
|
}
|
|
@@ -2164,7 +2542,7 @@ async function findDashboardBroadcastStart(cwd, env = process.env, options = {})
|
|
|
2164
2542
|
"start-broadcast.mjs"
|
|
2165
2543
|
);
|
|
2166
2544
|
try {
|
|
2167
|
-
await
|
|
2545
|
+
await access4(sibling);
|
|
2168
2546
|
return path12.resolve(sibling);
|
|
2169
2547
|
} catch {
|
|
2170
2548
|
}
|
|
@@ -2173,7 +2551,7 @@ async function findDashboardBroadcastStart(cwd, env = process.env, options = {})
|
|
|
2173
2551
|
options.moduleUrl ?? import.meta.url
|
|
2174
2552
|
)) {
|
|
2175
2553
|
try {
|
|
2176
|
-
await
|
|
2554
|
+
await access4(candidate2);
|
|
2177
2555
|
return path12.resolve(candidate2);
|
|
2178
2556
|
} catch {
|
|
2179
2557
|
}
|
|
@@ -2181,14 +2559,14 @@ async function findDashboardBroadcastStart(cwd, env = process.env, options = {})
|
|
|
2181
2559
|
return null;
|
|
2182
2560
|
}
|
|
2183
2561
|
function runStartScript2(startPath, env) {
|
|
2184
|
-
return new Promise((
|
|
2185
|
-
const child =
|
|
2562
|
+
return new Promise((resolve2, reject) => {
|
|
2563
|
+
const child = spawn3(process.execPath, [startPath], {
|
|
2186
2564
|
cwd: path12.dirname(path12.dirname(startPath)),
|
|
2187
2565
|
env,
|
|
2188
2566
|
stdio: "inherit"
|
|
2189
2567
|
});
|
|
2190
2568
|
child.on("error", reject);
|
|
2191
|
-
child.on("close", (code) =>
|
|
2569
|
+
child.on("close", (code) => resolve2(code ?? 1));
|
|
2192
2570
|
});
|
|
2193
2571
|
}
|
|
2194
2572
|
var dashboardBroadcastCommand = defineCommand5({
|
|
@@ -2205,7 +2583,11 @@ var dashboardBroadcastCommand = defineCommand5({
|
|
|
2205
2583
|
"no-open": {
|
|
2206
2584
|
type: "boolean",
|
|
2207
2585
|
default: false,
|
|
2208
|
-
description: "Do not open a browser; only ensure the server is up and print
|
|
2586
|
+
description: "Do not open a browser; only ensure the server is up and print Share URL (when masking is on) plus token / LAN lines"
|
|
2587
|
+
},
|
|
2588
|
+
browser: {
|
|
2589
|
+
type: "string",
|
|
2590
|
+
description: "Preferred browser app/binary for this launch (overrides config missionControl.preferredBrowser)"
|
|
2209
2591
|
}
|
|
2210
2592
|
},
|
|
2211
2593
|
async run({ args }) {
|
|
@@ -2218,8 +2600,11 @@ var dashboardBroadcastCommand = defineCommand5({
|
|
|
2218
2600
|
process.exitCode = 1;
|
|
2219
2601
|
return;
|
|
2220
2602
|
}
|
|
2221
|
-
const
|
|
2222
|
-
|
|
2603
|
+
const snapshotRoot = resolveDashboardSnapshotRoot(args.cwd);
|
|
2604
|
+
const env = applyDashboardOpenEnv(
|
|
2605
|
+
{ ...process.env },
|
|
2606
|
+
{ noOpen: Boolean(args["no-open"]), browser: args.browser, cwd: snapshotRoot }
|
|
2607
|
+
);
|
|
2223
2608
|
const code = await runStartScript2(startPath, env);
|
|
2224
2609
|
if (code !== 0) process.exitCode = code;
|
|
2225
2610
|
}
|
|
@@ -2229,11 +2614,11 @@ var dashboardBroadcastCommand = defineCommand5({
|
|
|
2229
2614
|
import { defineCommand as defineCommand6 } from "citty";
|
|
2230
2615
|
|
|
2231
2616
|
// src/lifecycle/diff.ts
|
|
2232
|
-
import { readFile as
|
|
2617
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
2233
2618
|
import path13 from "path";
|
|
2234
2619
|
async function readIfExists(abs) {
|
|
2235
2620
|
try {
|
|
2236
|
-
return await
|
|
2621
|
+
return await readFile8(abs, "utf8");
|
|
2237
2622
|
} catch {
|
|
2238
2623
|
return null;
|
|
2239
2624
|
}
|
|
@@ -2347,19 +2732,23 @@ var diffCommand = defineCommand6({
|
|
|
2347
2732
|
refresh: args.refresh,
|
|
2348
2733
|
manifest
|
|
2349
2734
|
});
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2735
|
+
try {
|
|
2736
|
+
logger.info(`Registry: ${registry.root} (${registry.source})`);
|
|
2737
|
+
const entries = await diffAgainstRegistry(registry.root, args.cwd, manifest);
|
|
2738
|
+
const summary = summarizeDiff(entries);
|
|
2739
|
+
console.log(
|
|
2740
|
+
`Summary: match=${summary.match} drift=${summary.drift} missing-local=${summary["missing-local"]} missing-registry=${summary["missing-registry"]} protected=${summary.protected}`
|
|
2741
|
+
);
|
|
2742
|
+
const rows = args.all ? entries : entries.filter((e) => e.status !== "match");
|
|
2743
|
+
if (rows.length === 0) {
|
|
2744
|
+
logger.success("No drift (matches only).");
|
|
2745
|
+
return;
|
|
2746
|
+
}
|
|
2747
|
+
for (const e of rows) {
|
|
2748
|
+
console.log(`${e.status.padEnd(18)} ${e.path}`);
|
|
2749
|
+
}
|
|
2750
|
+
} finally {
|
|
2751
|
+
await registry.unlock?.();
|
|
2363
2752
|
}
|
|
2364
2753
|
}
|
|
2365
2754
|
});
|
|
@@ -2370,7 +2759,7 @@ import { defineCommand as defineCommand7 } from "citty";
|
|
|
2370
2759
|
|
|
2371
2760
|
// src/invariants/hooks-health.ts
|
|
2372
2761
|
import { execFile as execFile2 } from "child_process";
|
|
2373
|
-
import { constants as constants2, access as
|
|
2762
|
+
import { constants as constants2, access as access5, readFile as readFile9, readdir as readdir3, stat as stat2 } from "fs/promises";
|
|
2374
2763
|
import path14 from "path";
|
|
2375
2764
|
import { promisify as promisify2 } from "util";
|
|
2376
2765
|
var execFileAsync2 = promisify2(execFile2);
|
|
@@ -2384,7 +2773,7 @@ var EXPECTED_EVENTS = [
|
|
|
2384
2773
|
];
|
|
2385
2774
|
async function exists(p) {
|
|
2386
2775
|
try {
|
|
2387
|
-
await
|
|
2776
|
+
await access5(p);
|
|
2388
2777
|
return true;
|
|
2389
2778
|
} catch {
|
|
2390
2779
|
return false;
|
|
@@ -2392,7 +2781,7 @@ async function exists(p) {
|
|
|
2392
2781
|
}
|
|
2393
2782
|
async function isExecutable(p) {
|
|
2394
2783
|
try {
|
|
2395
|
-
await
|
|
2784
|
+
await access5(p, constants2.X_OK);
|
|
2396
2785
|
return true;
|
|
2397
2786
|
} catch {
|
|
2398
2787
|
return false;
|
|
@@ -2444,7 +2833,7 @@ async function assessGitHooksInstallDrift(rootDir) {
|
|
|
2444
2833
|
const gitDir = path14.join(root, ".git");
|
|
2445
2834
|
if (!await exists(gitDir)) return [];
|
|
2446
2835
|
try {
|
|
2447
|
-
const st = await
|
|
2836
|
+
const st = await stat2(gitDir);
|
|
2448
2837
|
if (!st.isDirectory()) return [];
|
|
2449
2838
|
} catch {
|
|
2450
2839
|
return [];
|
|
@@ -2472,7 +2861,7 @@ async function assessGitHooksInstallDrift(rootDir) {
|
|
|
2472
2861
|
continue;
|
|
2473
2862
|
}
|
|
2474
2863
|
try {
|
|
2475
|
-
const [a, b] = await Promise.all([
|
|
2864
|
+
const [a, b] = await Promise.all([readFile9(canonical, "utf8"), readFile9(installed, "utf8")]);
|
|
2476
2865
|
if (a !== b) {
|
|
2477
2866
|
advisories.push(
|
|
2478
2867
|
`git-hooks drift: \`.git/hooks/${name}\` differs from \`git-hooks/${name}\`. ${installHint}`
|
|
@@ -2505,7 +2894,7 @@ async function assessHooksHealth(rootDir) {
|
|
|
2505
2894
|
}
|
|
2506
2895
|
let parsed;
|
|
2507
2896
|
try {
|
|
2508
|
-
parsed = JSON.parse(await
|
|
2897
|
+
parsed = JSON.parse(await readFile9(hooksJsonAbs, "utf8"));
|
|
2509
2898
|
} catch {
|
|
2510
2899
|
return {
|
|
2511
2900
|
status: "degraded",
|
|
@@ -2548,7 +2937,7 @@ async function assessHooksHealth(rootDir) {
|
|
|
2548
2937
|
continue;
|
|
2549
2938
|
}
|
|
2550
2939
|
try {
|
|
2551
|
-
const st = await
|
|
2940
|
+
const st = await stat2(abs);
|
|
2552
2941
|
if (!st.isFile()) {
|
|
2553
2942
|
reasons.push(`adapter path is not a file: \`${rel}\``);
|
|
2554
2943
|
continue;
|
|
@@ -2828,11 +3217,11 @@ function createReadinessReport(scan, options) {
|
|
|
2828
3217
|
}
|
|
2829
3218
|
|
|
2830
3219
|
// src/scanner/safe-fixes.ts
|
|
2831
|
-
import { readFile as
|
|
3220
|
+
import { readFile as readFile12, writeFile as writeFile5 } from "fs/promises";
|
|
2832
3221
|
import path22 from "path";
|
|
2833
3222
|
|
|
2834
3223
|
// src/scanner/detect-repository.ts
|
|
2835
|
-
import { readFile as
|
|
3224
|
+
import { readFile as readFile10 } from "fs/promises";
|
|
2836
3225
|
import path15 from "path";
|
|
2837
3226
|
var CONTEXT_PATHS = [
|
|
2838
3227
|
["README.md", "README"],
|
|
@@ -2938,7 +3327,7 @@ var REQUIRED_SECRET_PATTERNS = [
|
|
|
2938
3327
|
async function detectSafety(rootDir, trackedFiles) {
|
|
2939
3328
|
const gitignorePath = path15.join(rootDir, ".gitignore");
|
|
2940
3329
|
const hasGitignore = await fileExists(gitignorePath);
|
|
2941
|
-
const gitignore = hasGitignore ? await
|
|
3330
|
+
const gitignore = hasGitignore ? await readFile10(gitignorePath, "utf8") : "";
|
|
2942
3331
|
const lines = gitignore.split(/\r?\n/).map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
|
|
2943
3332
|
const ignoredSecretPatterns = REQUIRED_SECRET_PATTERNS.filter(
|
|
2944
3333
|
(pattern) => lines.includes(pattern)
|
|
@@ -2956,7 +3345,7 @@ async function detectSafety(rootDir, trackedFiles) {
|
|
|
2956
3345
|
];
|
|
2957
3346
|
const guardContents = await Promise.all(
|
|
2958
3347
|
guardCandidates.map(
|
|
2959
|
-
async (item) => await fileExists(path15.join(rootDir, item)) ?
|
|
3348
|
+
async (item) => await fileExists(path15.join(rootDir, item)) ? readFile10(path15.join(rootDir, item), "utf8") : ""
|
|
2960
3349
|
)
|
|
2961
3350
|
);
|
|
2962
3351
|
return {
|
|
@@ -3163,6 +3552,33 @@ async function detectIde(rootDir) {
|
|
|
3163
3552
|
import path18 from "path";
|
|
3164
3553
|
|
|
3165
3554
|
// src/types.ts
|
|
3555
|
+
var GIT_PLATFORM_META = {
|
|
3556
|
+
github: {
|
|
3557
|
+
cli: "gh",
|
|
3558
|
+
prTerm: "Pull Request",
|
|
3559
|
+
prCommand: "gh pr create",
|
|
3560
|
+
ciDefault: "github-actions"
|
|
3561
|
+
},
|
|
3562
|
+
gitlab: {
|
|
3563
|
+
cli: "glab",
|
|
3564
|
+
prTerm: "Merge Request",
|
|
3565
|
+
prCommand: "glab mr create",
|
|
3566
|
+
ciDefault: "gitlab-ci"
|
|
3567
|
+
},
|
|
3568
|
+
bitbucket: {
|
|
3569
|
+
cli: "bb",
|
|
3570
|
+
prTerm: "Pull Request",
|
|
3571
|
+
prCommand: "bb pr create",
|
|
3572
|
+
ciDefault: "bitbucket-pipelines"
|
|
3573
|
+
},
|
|
3574
|
+
"azure-devops": {
|
|
3575
|
+
cli: "az repos",
|
|
3576
|
+
prTerm: "Pull Request",
|
|
3577
|
+
prCommand: "az repos pr create",
|
|
3578
|
+
ciDefault: "azure-pipelines"
|
|
3579
|
+
},
|
|
3580
|
+
gitea: { cli: "tea", prTerm: "Pull Request", prCommand: "tea pr create", ciDefault: "none" }
|
|
3581
|
+
};
|
|
3166
3582
|
var CI_PLATFORM_FILES = {
|
|
3167
3583
|
"github-actions": ".github/workflows",
|
|
3168
3584
|
"gitlab-ci": ".gitlab-ci.yml",
|
|
@@ -3231,7 +3647,7 @@ async function detectInfra(rootDir) {
|
|
|
3231
3647
|
}
|
|
3232
3648
|
|
|
3233
3649
|
// src/scanner/detect-services.ts
|
|
3234
|
-
import { readFile as
|
|
3650
|
+
import { readFile as readFile11 } from "fs/promises";
|
|
3235
3651
|
import path19 from "path";
|
|
3236
3652
|
async function detectProjectManagement(rootDir) {
|
|
3237
3653
|
const tools = [];
|
|
@@ -3242,7 +3658,7 @@ async function detectProjectManagement(rootDir) {
|
|
|
3242
3658
|
for (const configPath of mcpConfigPaths) {
|
|
3243
3659
|
if (!await fileExists(configPath)) continue;
|
|
3244
3660
|
try {
|
|
3245
|
-
const raw = await
|
|
3661
|
+
const raw = await readFile11(configPath, "utf8");
|
|
3246
3662
|
const lower = raw.toLowerCase();
|
|
3247
3663
|
if (lower.includes("clickup")) tools.push("clickup");
|
|
3248
3664
|
if (lower.includes("jira") || lower.includes("atlassian")) tools.push("jira");
|
|
@@ -3625,10 +4041,10 @@ async function executeSafeReadinessFixes(rootDir, options) {
|
|
|
3625
4041
|
}
|
|
3626
4042
|
const gitignoreRelativePath = ".gitignore";
|
|
3627
4043
|
const gitignorePath = path22.join(beforeScan.rootDir, gitignoreRelativePath);
|
|
3628
|
-
const existingGitignore = await fileExists(gitignorePath) ? await
|
|
4044
|
+
const existingGitignore = await fileExists(gitignorePath) ? await readFile12(gitignorePath, "utf8") : "";
|
|
3629
4045
|
const mergedGitignore = mergeSecretIgnores(existingGitignore);
|
|
3630
4046
|
const gitignoreChanged = mergedGitignore !== existingGitignore;
|
|
3631
|
-
if (gitignoreChanged && !dryRun) await
|
|
4047
|
+
if (gitignoreChanged && !dryRun) await writeFile5(gitignorePath, mergedGitignore, "utf8");
|
|
3632
4048
|
recordChange(
|
|
3633
4049
|
changes,
|
|
3634
4050
|
"merge-secret-ignores",
|
|
@@ -3753,7 +4169,7 @@ function printDoctorSummary(result) {
|
|
|
3753
4169
|
var doctorCommand = defineCommand7({
|
|
3754
4170
|
meta: {
|
|
3755
4171
|
name: "doctor",
|
|
3756
|
-
description: "Diagnose repository readiness
|
|
4172
|
+
description: "Diagnose repository readiness; optional --fix-safe local repairs."
|
|
3757
4173
|
},
|
|
3758
4174
|
args: {
|
|
3759
4175
|
cwd: {
|
|
@@ -4044,7 +4460,7 @@ async function detectCurrentBranch() {
|
|
|
4044
4460
|
var guardCommand = defineCommand8({
|
|
4045
4461
|
meta: {
|
|
4046
4462
|
name: "guard",
|
|
4047
|
-
description: "
|
|
4463
|
+
description: "Deny/annotate guards (shell, prompt). Hooks are thin adapters."
|
|
4048
4464
|
},
|
|
4049
4465
|
subCommands: {
|
|
4050
4466
|
shell: defineCommand8({
|
|
@@ -4107,8 +4523,8 @@ var guardCommand = defineCommand8({
|
|
|
4107
4523
|
});
|
|
4108
4524
|
|
|
4109
4525
|
// src/commands/handoff.ts
|
|
4110
|
-
import { spawn as
|
|
4111
|
-
import { readFile as
|
|
4526
|
+
import { spawn as spawn4 } from "child_process";
|
|
4527
|
+
import { readFile as readFile13, readdir as readdir4, writeFile as writeFile6 } from "fs/promises";
|
|
4112
4528
|
import path25 from "path";
|
|
4113
4529
|
import { defineCommand as defineCommand9 } from "citty";
|
|
4114
4530
|
function parsePlanFrontmatter(raw) {
|
|
@@ -4129,13 +4545,13 @@ async function findActivePlan(plansDir) {
|
|
|
4129
4545
|
if (!await fileExists(plansDir)) return null;
|
|
4130
4546
|
const files = (await readdir4(plansDir)).filter((f) => f.endsWith(".plan.md")).sort().reverse();
|
|
4131
4547
|
for (const file of files) {
|
|
4132
|
-
const raw = await
|
|
4548
|
+
const raw = await readFile13(path25.join(plansDir, file), "utf8");
|
|
4133
4549
|
const fm = parsePlanFrontmatter(raw);
|
|
4134
4550
|
if (fm?.todos?.some((t) => t.status !== "completed" && t.status !== "cancelled")) {
|
|
4135
4551
|
return { file, raw };
|
|
4136
4552
|
}
|
|
4137
4553
|
}
|
|
4138
|
-
return files[0] ? { file: files[0], raw: await
|
|
4554
|
+
return files[0] ? { file: files[0], raw: await readFile13(path25.join(plansDir, files[0]), "utf8") } : null;
|
|
4139
4555
|
}
|
|
4140
4556
|
function now() {
|
|
4141
4557
|
return (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 16);
|
|
@@ -4218,19 +4634,19 @@ function printV3Guidance() {
|
|
|
4218
4634
|
logger.info("Resume in IDE: /resume, summaries and transcripts.");
|
|
4219
4635
|
}
|
|
4220
4636
|
function runCursorHandoff(scriptPath, cwd) {
|
|
4221
|
-
return new Promise((
|
|
4222
|
-
const child =
|
|
4637
|
+
return new Promise((resolve2, reject) => {
|
|
4638
|
+
const child = spawn4("sh", [scriptPath, "handoff"], {
|
|
4223
4639
|
cwd,
|
|
4224
4640
|
stdio: "inherit"
|
|
4225
4641
|
});
|
|
4226
4642
|
child.on("error", (err) => reject(err));
|
|
4227
|
-
child.on("close", (code) =>
|
|
4643
|
+
child.on("close", (code) => resolve2(code ?? 1));
|
|
4228
4644
|
});
|
|
4229
4645
|
}
|
|
4230
4646
|
var handoffCommand = defineCommand9({
|
|
4231
4647
|
meta: {
|
|
4232
4648
|
name: "handoff",
|
|
4233
|
-
description: "Write .cursor/HANDOFF.md from the active
|
|
4649
|
+
description: "Write .cursor/HANDOFF.md from the active plan (or cursor-handoff fallback)."
|
|
4234
4650
|
},
|
|
4235
4651
|
args: {
|
|
4236
4652
|
cwd: {
|
|
@@ -4249,7 +4665,7 @@ var handoffCommand = defineCommand9({
|
|
|
4249
4665
|
if (fm) {
|
|
4250
4666
|
await ensureDir(path25.join(args.cwd, ".cursor"));
|
|
4251
4667
|
const content = buildHandoff(plan.file, fm, profile);
|
|
4252
|
-
await
|
|
4668
|
+
await writeFile6(handoffPath, content, "utf8");
|
|
4253
4669
|
logger.success("HANDOFF.md updated: .cursor/HANDOFF.md");
|
|
4254
4670
|
logger.info(
|
|
4255
4671
|
`Plan: ${plan.file} (${fm.todos?.filter((t) => t.status === "completed").length}/${fm.todos?.length} completed)`
|
|
@@ -4305,8 +4721,8 @@ function buildPreCompactUserMessage(payload = {}) {
|
|
|
4305
4721
|
}
|
|
4306
4722
|
|
|
4307
4723
|
// src/hooks/session-start.ts
|
|
4308
|
-
import { spawn as
|
|
4309
|
-
import { access as
|
|
4724
|
+
import { spawn as spawn5 } from "child_process";
|
|
4725
|
+
import { access as access6, readFile as readFile14 } from "fs/promises";
|
|
4310
4726
|
import path26 from "path";
|
|
4311
4727
|
|
|
4312
4728
|
// src/invariants/handoff-schema.ts
|
|
@@ -4381,7 +4797,7 @@ var NONE_PLACEHOLDERS = /* @__PURE__ */ new Set(["none", "n/a", "empty", "nil"])
|
|
|
4381
4797
|
var CURSOR_AWARENESS_SPAWN_TIMEOUT_MS = CHANGELOG_FETCH_TIMEOUT_MS + 3e3;
|
|
4382
4798
|
async function readTextLimited(filePath, limit = 60) {
|
|
4383
4799
|
try {
|
|
4384
|
-
const lines = (await
|
|
4800
|
+
const lines = (await readFile14(filePath, "utf8")).split(/\r?\n/);
|
|
4385
4801
|
return lines.slice(0, limit).join("\n").trim();
|
|
4386
4802
|
} catch {
|
|
4387
4803
|
return "";
|
|
@@ -4389,14 +4805,14 @@ async function readTextLimited(filePath, limit = 60) {
|
|
|
4389
4805
|
}
|
|
4390
4806
|
async function readFull(filePath) {
|
|
4391
4807
|
try {
|
|
4392
|
-
return await
|
|
4808
|
+
return await readFile14(filePath, "utf8");
|
|
4393
4809
|
} catch {
|
|
4394
4810
|
return "";
|
|
4395
4811
|
}
|
|
4396
4812
|
}
|
|
4397
4813
|
async function fileExists2(p) {
|
|
4398
4814
|
try {
|
|
4399
|
-
await
|
|
4815
|
+
await access6(p);
|
|
4400
4816
|
return true;
|
|
4401
4817
|
} catch {
|
|
4402
4818
|
return false;
|
|
@@ -4405,22 +4821,55 @@ async function fileExists2(p) {
|
|
|
4405
4821
|
function parseUnprocessedDogfoodItems(readmeText) {
|
|
4406
4822
|
const items = [];
|
|
4407
4823
|
let inSection = false;
|
|
4824
|
+
let sectionLevel = 0;
|
|
4408
4825
|
for (const line of readmeText.split(/\r?\n/)) {
|
|
4409
|
-
|
|
4826
|
+
const unprocessedMatch = /^(#{2,3})\s+Unprocessed Files\b/.exec(line);
|
|
4827
|
+
if (unprocessedMatch) {
|
|
4828
|
+
const hashes = unprocessedMatch[1];
|
|
4829
|
+
if (!hashes) continue;
|
|
4410
4830
|
inSection = true;
|
|
4831
|
+
sectionLevel = hashes.length;
|
|
4411
4832
|
continue;
|
|
4412
4833
|
}
|
|
4413
4834
|
if (!inSection) continue;
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4835
|
+
const headingMatch = /^(#{1,6})\s+/.exec(line);
|
|
4836
|
+
if (headingMatch) {
|
|
4837
|
+
if (/\bProcessed Files\b/.test(line)) break;
|
|
4838
|
+
const hashes = headingMatch[1];
|
|
4839
|
+
if (hashes && hashes.length <= sectionLevel) break;
|
|
4840
|
+
continue;
|
|
4841
|
+
}
|
|
4842
|
+
const body = extractUnprocessedDogfoodLine(line);
|
|
4843
|
+
if (!body) continue;
|
|
4420
4844
|
items.push(body);
|
|
4421
4845
|
}
|
|
4422
4846
|
return items;
|
|
4423
4847
|
}
|
|
4848
|
+
function extractUnprocessedDogfoodLine(line) {
|
|
4849
|
+
const stripped = line.trim();
|
|
4850
|
+
if (!stripped) return null;
|
|
4851
|
+
let raw = null;
|
|
4852
|
+
if (stripped.startsWith("- ")) {
|
|
4853
|
+
raw = stripped.slice(2).trim();
|
|
4854
|
+
} else {
|
|
4855
|
+
const numbered = /^(\d+)[.)]\s+(.+)$/.exec(stripped);
|
|
4856
|
+
if (numbered?.[2]) {
|
|
4857
|
+
raw = numbered[2].trim();
|
|
4858
|
+
} else if (stripped.startsWith("|")) {
|
|
4859
|
+
const parts = stripped.replace(/^\|/, "").replace(/\|$/, "").split("|").map((cell) => cell.trim());
|
|
4860
|
+
if (parts.length === 0) return null;
|
|
4861
|
+
if (parts.every((cell) => /^:?-+:?$/.test(cell))) return null;
|
|
4862
|
+
const first = parts[0] ?? "";
|
|
4863
|
+
const headerish = first.toLowerCase().replace(/[*_`]/g, "").trim();
|
|
4864
|
+
if (/^(note|file|entrada|title|name|item|path)$/.test(headerish)) return null;
|
|
4865
|
+
raw = first.trim();
|
|
4866
|
+
}
|
|
4867
|
+
}
|
|
4868
|
+
if (!raw) return null;
|
|
4869
|
+
const normalized = raw.toLowerCase().replace(/[*_]/g, "").trim();
|
|
4870
|
+
if (!normalized || NONE_PLACEHOLDERS.has(normalized)) return null;
|
|
4871
|
+
return raw;
|
|
4872
|
+
}
|
|
4424
4873
|
async function l0Present(root) {
|
|
4425
4874
|
const cursor = path26.join(root, ".cursor");
|
|
4426
4875
|
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 +4914,7 @@ async function readinessSection(root) {
|
|
|
4465
4914
|
const snapshotPath = path26.join(root, ".cursor", "context", "readiness.json");
|
|
4466
4915
|
let data;
|
|
4467
4916
|
try {
|
|
4468
|
-
data = JSON.parse(await
|
|
4917
|
+
data = JSON.parse(await readFile14(snapshotPath, "utf8"));
|
|
4469
4918
|
} catch {
|
|
4470
4919
|
return null;
|
|
4471
4920
|
}
|
|
@@ -4502,7 +4951,7 @@ async function dogfoodInboxSection(root) {
|
|
|
4502
4951
|
for (const readme of candidateReadmes) {
|
|
4503
4952
|
if (!await fileExists2(readme)) continue;
|
|
4504
4953
|
try {
|
|
4505
|
-
const text = await
|
|
4954
|
+
const text = await readFile14(readme, "utf8");
|
|
4506
4955
|
if (parseUnprocessedDogfoodItems(text).length) return DOGFOOD_INBOX_HINT;
|
|
4507
4956
|
} catch {
|
|
4508
4957
|
}
|
|
@@ -4512,7 +4961,7 @@ async function dogfoodInboxSection(root) {
|
|
|
4512
4961
|
async function loadUpdateCheckPrefs(root) {
|
|
4513
4962
|
try {
|
|
4514
4963
|
const data = JSON.parse(
|
|
4515
|
-
await
|
|
4964
|
+
await readFile14(path26.join(root, ".cursor", "context", "config.json"), "utf8")
|
|
4516
4965
|
);
|
|
4517
4966
|
const uc = data.updateCheck;
|
|
4518
4967
|
if (!uc || typeof uc !== "object" || uc.enabled !== true) {
|
|
@@ -4524,8 +4973,8 @@ async function loadUpdateCheckPrefs(root) {
|
|
|
4524
4973
|
}
|
|
4525
4974
|
}
|
|
4526
4975
|
function runUpdateCheckJson(root) {
|
|
4527
|
-
return new Promise((
|
|
4528
|
-
const child =
|
|
4976
|
+
return new Promise((resolve2) => {
|
|
4977
|
+
const child = spawn5(
|
|
4529
4978
|
process.execPath,
|
|
4530
4979
|
[
|
|
4531
4980
|
process.argv[1] ?? "",
|
|
@@ -4543,21 +4992,21 @@ function runUpdateCheckJson(root) {
|
|
|
4543
4992
|
child.stdout?.on("data", (chunk) => {
|
|
4544
4993
|
out += chunk.toString("utf8");
|
|
4545
4994
|
});
|
|
4546
|
-
child.on("error", () =>
|
|
4995
|
+
child.on("error", () => resolve2(null));
|
|
4547
4996
|
child.on("close", () => {
|
|
4548
4997
|
try {
|
|
4549
4998
|
const parsed = JSON.parse(out.trim());
|
|
4550
|
-
|
|
4999
|
+
resolve2(parsed && typeof parsed === "object" ? parsed : null);
|
|
4551
5000
|
} catch {
|
|
4552
|
-
|
|
5001
|
+
resolve2(null);
|
|
4553
5002
|
}
|
|
4554
5003
|
});
|
|
4555
5004
|
});
|
|
4556
5005
|
}
|
|
4557
5006
|
async function updateCheckSection(root) {
|
|
4558
5007
|
if (await loadUpdateCheckPrefs(root) === null) return null;
|
|
4559
|
-
const result = await new Promise((
|
|
4560
|
-
const child =
|
|
5008
|
+
const result = await new Promise((resolve2) => {
|
|
5009
|
+
const child = spawn5(
|
|
4561
5010
|
"agent-kit",
|
|
4562
5011
|
["update", "--check", "--json", "--respect-prefs", "--stamp", "--cwd", root],
|
|
4563
5012
|
{ stdio: ["ignore", "pipe", "ignore"], timeout: 12e3, shell: false }
|
|
@@ -4567,17 +5016,17 @@ async function updateCheckSection(root) {
|
|
|
4567
5016
|
out += chunk.toString("utf8");
|
|
4568
5017
|
});
|
|
4569
5018
|
child.on("error", () => {
|
|
4570
|
-
void runUpdateCheckJson(root).then(
|
|
5019
|
+
void runUpdateCheckJson(root).then(resolve2);
|
|
4571
5020
|
});
|
|
4572
5021
|
child.on("close", (code) => {
|
|
4573
5022
|
if (code !== 0 && !out.trim()) {
|
|
4574
|
-
void runUpdateCheckJson(root).then(
|
|
5023
|
+
void runUpdateCheckJson(root).then(resolve2);
|
|
4575
5024
|
return;
|
|
4576
5025
|
}
|
|
4577
5026
|
try {
|
|
4578
|
-
|
|
5027
|
+
resolve2(JSON.parse(out.trim()));
|
|
4579
5028
|
} catch {
|
|
4580
|
-
void runUpdateCheckJson(root).then(
|
|
5029
|
+
void runUpdateCheckJson(root).then(resolve2);
|
|
4581
5030
|
}
|
|
4582
5031
|
});
|
|
4583
5032
|
});
|
|
@@ -4590,7 +5039,7 @@ async function updateCheckSection(root) {
|
|
|
4590
5039
|
async function loadCursorUpdateCheckPrefs(root) {
|
|
4591
5040
|
try {
|
|
4592
5041
|
const data = JSON.parse(
|
|
4593
|
-
await
|
|
5042
|
+
await readFile14(path26.join(root, ".cursor", "context", "config.json"), "utf8")
|
|
4594
5043
|
);
|
|
4595
5044
|
const uc = data.cursorUpdateCheck;
|
|
4596
5045
|
if (!uc || typeof uc !== "object" || uc.enabled !== true) {
|
|
@@ -4602,8 +5051,8 @@ async function loadCursorUpdateCheckPrefs(root) {
|
|
|
4602
5051
|
}
|
|
4603
5052
|
}
|
|
4604
5053
|
function runCursorAwarenessJson(root) {
|
|
4605
|
-
return new Promise((
|
|
4606
|
-
const child =
|
|
5054
|
+
return new Promise((resolve2) => {
|
|
5055
|
+
const child = spawn5(
|
|
4607
5056
|
process.execPath,
|
|
4608
5057
|
[
|
|
4609
5058
|
process.argv[1] ?? "",
|
|
@@ -4621,13 +5070,13 @@ function runCursorAwarenessJson(root) {
|
|
|
4621
5070
|
child.stdout?.on("data", (chunk) => {
|
|
4622
5071
|
out += chunk.toString("utf8");
|
|
4623
5072
|
});
|
|
4624
|
-
child.on("error", () =>
|
|
5073
|
+
child.on("error", () => resolve2(null));
|
|
4625
5074
|
child.on("close", () => {
|
|
4626
5075
|
try {
|
|
4627
5076
|
const parsed = JSON.parse(out.trim());
|
|
4628
|
-
|
|
5077
|
+
resolve2(parsed && typeof parsed === "object" ? parsed : null);
|
|
4629
5078
|
} catch {
|
|
4630
|
-
|
|
5079
|
+
resolve2(null);
|
|
4631
5080
|
}
|
|
4632
5081
|
});
|
|
4633
5082
|
});
|
|
@@ -4642,15 +5091,15 @@ function shouldEmitCursorAwarenessNudge(result) {
|
|
|
4642
5091
|
}
|
|
4643
5092
|
async function cursorAwarenessSection(root, deps = {}) {
|
|
4644
5093
|
if (await loadCursorUpdateCheckPrefs(root) === null) return null;
|
|
4645
|
-
const spawnFn = deps.spawnFn ??
|
|
5094
|
+
const spawnFn = deps.spawnFn ?? spawn5;
|
|
4646
5095
|
const runFallback = deps.runFallback ?? runCursorAwarenessJson;
|
|
4647
|
-
const result = await new Promise((
|
|
5096
|
+
const result = await new Promise((resolve2) => {
|
|
4648
5097
|
let settled = false;
|
|
4649
5098
|
let fallbackStarted = false;
|
|
4650
5099
|
const finish = (value) => {
|
|
4651
5100
|
if (settled) return;
|
|
4652
5101
|
settled = true;
|
|
4653
|
-
|
|
5102
|
+
resolve2(value);
|
|
4654
5103
|
};
|
|
4655
5104
|
const startFallback = () => {
|
|
4656
5105
|
if (settled || fallbackStarted) return;
|
|
@@ -4739,7 +5188,7 @@ function resolveSessionRoot(payload, cwd = process.cwd()) {
|
|
|
4739
5188
|
var hookCommand = defineCommand10({
|
|
4740
5189
|
meta: {
|
|
4741
5190
|
name: "hook",
|
|
4742
|
-
description: "Cursor hook adapters (session-start, pre-compact). CLI is SoT
|
|
5191
|
+
description: "Cursor hook adapters (session-start, pre-compact). CLI is SoT."
|
|
4743
5192
|
},
|
|
4744
5193
|
subCommands: {
|
|
4745
5194
|
"session-start": defineCommand10({
|
|
@@ -4778,13 +5227,216 @@ var hookCommand = defineCommand10({
|
|
|
4778
5227
|
import { intro, outro } from "@clack/prompts";
|
|
4779
5228
|
import { defineCommand as defineCommand12 } from "citty";
|
|
4780
5229
|
|
|
5230
|
+
// src/utils/terminal.ts
|
|
5231
|
+
import { homedir as homedir2 } from "os";
|
|
5232
|
+
import path28 from "path";
|
|
5233
|
+
import { confirm, isCancel } from "@clack/prompts";
|
|
5234
|
+
function isNonInteractive() {
|
|
5235
|
+
if (process.env.CI === "true" || process.env.CI === "1") return true;
|
|
5236
|
+
if (process.env.AGENT_KIT_YES === "1") return true;
|
|
5237
|
+
return !process.stdin.isTTY;
|
|
5238
|
+
}
|
|
5239
|
+
async function validateProjectRoot(resolved) {
|
|
5240
|
+
const home = path28.resolve(homedir2());
|
|
5241
|
+
if (resolved === "/" || resolved === home) {
|
|
5242
|
+
return { ok: false, reason: `Refused to use ${resolved} as a project root.` };
|
|
5243
|
+
}
|
|
5244
|
+
const hasGit = await fileExists(path28.join(resolved, ".git"));
|
|
5245
|
+
const hasManifest = await fileExists(path28.join(resolved, ".cursor", "agent-kit.json"));
|
|
5246
|
+
if (!hasGit && !hasManifest) {
|
|
5247
|
+
return {
|
|
5248
|
+
ok: false,
|
|
5249
|
+
reason: `Refused ${resolved}: no .git and no .cursor/agent-kit.json. Run from a project directory or use --force-root.`
|
|
5250
|
+
};
|
|
5251
|
+
}
|
|
5252
|
+
return { ok: true };
|
|
5253
|
+
}
|
|
5254
|
+
async function confirmProjectRoot(cwd, opts) {
|
|
5255
|
+
const resolved = path28.resolve(cwd);
|
|
5256
|
+
if (opts.forceRoot) {
|
|
5257
|
+
return resolved;
|
|
5258
|
+
}
|
|
5259
|
+
const validation = await validateProjectRoot(resolved);
|
|
5260
|
+
if (!validation.ok) {
|
|
5261
|
+
if (opts.nonInteractive) {
|
|
5262
|
+
throw new RootRefusedError(resolved, validation.reason);
|
|
5263
|
+
}
|
|
5264
|
+
const ok2 = await confirm({
|
|
5265
|
+
message: `${validation.reason} Proceed anyway?`,
|
|
5266
|
+
initialValue: false
|
|
5267
|
+
});
|
|
5268
|
+
if (isCancel(ok2) || !ok2) {
|
|
5269
|
+
throw new RootRefusedError(resolved, validation.reason);
|
|
5270
|
+
}
|
|
5271
|
+
return resolved;
|
|
5272
|
+
}
|
|
5273
|
+
if (opts.nonInteractive) {
|
|
5274
|
+
return resolved;
|
|
5275
|
+
}
|
|
5276
|
+
const ok = await confirm({
|
|
5277
|
+
message: `${opts.command === "install" ? "Install" : "Update"} Agent Kit in: ${resolved}`,
|
|
5278
|
+
initialValue: true
|
|
5279
|
+
});
|
|
5280
|
+
if (isCancel(ok) || !ok) {
|
|
5281
|
+
throw new RootRefusedError(resolved);
|
|
5282
|
+
}
|
|
5283
|
+
return resolved;
|
|
5284
|
+
}
|
|
5285
|
+
var RootRefusedError = class extends Error {
|
|
5286
|
+
constructor(root, reason) {
|
|
5287
|
+
super(reason ?? `Refused to write into ${root}. Re-run from the correct project directory.`);
|
|
5288
|
+
this.root = root;
|
|
5289
|
+
this.name = "RootRefusedError";
|
|
5290
|
+
}
|
|
5291
|
+
root;
|
|
5292
|
+
};
|
|
5293
|
+
function classifyInstallError(err) {
|
|
5294
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5295
|
+
const code = err?.code;
|
|
5296
|
+
if (code === "EPERM" || code === "EACCES" || /EPERM|EACCES/.test(msg)) {
|
|
5297
|
+
return {
|
|
5298
|
+
kind: "eperm",
|
|
5299
|
+
message: `Permission error: ${msg}`,
|
|
5300
|
+
recovery: [
|
|
5301
|
+
"The npm cache may have ownership drift (root-written files in a user dir).",
|
|
5302
|
+
"Recovery options:",
|
|
5303
|
+
" 1. npx --cache .npm-cache @dadado/agent-kit-cli install",
|
|
5304
|
+
" 2. npm cache clean --force && npx @dadado/agent-kit-cli install",
|
|
5305
|
+
" 3. Use Port B fallback: drag install.md into the Cursor chat"
|
|
5306
|
+
].join("\n")
|
|
5307
|
+
};
|
|
5308
|
+
}
|
|
5309
|
+
if (/403|Forbidden|unauthorized|E401|ENEEDAUTH/.test(msg)) {
|
|
5310
|
+
return {
|
|
5311
|
+
kind: "registry-auth",
|
|
5312
|
+
message: `Registry access denied: ${msg}`,
|
|
5313
|
+
recovery: [
|
|
5314
|
+
"The npm registry returned 403/401 for the scoped package.",
|
|
5315
|
+
"Recovery options:",
|
|
5316
|
+
" 1. Check npm auth: npm whoami (login if needed: npm login)",
|
|
5317
|
+
" 2. If using a private registry, verify .npmrc scope config",
|
|
5318
|
+
" 3. Use Port B fallback: drag install.md into the Cursor chat"
|
|
5319
|
+
].join("\n")
|
|
5320
|
+
};
|
|
5321
|
+
}
|
|
5322
|
+
if (/ENOTFOUND|ETIMEDOUT|ECONNREFUSED|ECONNRESET|EAI_AGAIN|fetch failed/.test(msg) || code === "ENOTFOUND" || code === "ETIMEDOUT") {
|
|
5323
|
+
return {
|
|
5324
|
+
kind: "network",
|
|
5325
|
+
message: `Network error: ${msg}`,
|
|
5326
|
+
recovery: [
|
|
5327
|
+
"Could not reach the registry or git remote.",
|
|
5328
|
+
"Recovery options:",
|
|
5329
|
+
" 1. Check network/proxy/VPN settings",
|
|
5330
|
+
" 2. Retry: npx @dadado/agent-kit-cli install",
|
|
5331
|
+
" 3. Use --registry <local-path> if you have a local checkout"
|
|
5332
|
+
].join("\n")
|
|
5333
|
+
};
|
|
5334
|
+
}
|
|
5335
|
+
return {
|
|
5336
|
+
kind: "unknown",
|
|
5337
|
+
message: msg,
|
|
5338
|
+
recovery: "Unexpected error. Check the message above and retry, or use Port B fallback."
|
|
5339
|
+
};
|
|
5340
|
+
}
|
|
5341
|
+
|
|
4781
5342
|
// src/commands/install.ts
|
|
4782
|
-
import
|
|
5343
|
+
import path32 from "path";
|
|
4783
5344
|
import { defineCommand as defineCommand11 } from "citty";
|
|
4784
5345
|
|
|
4785
5346
|
// src/generator/personalization.ts
|
|
4786
|
-
import { readFile as
|
|
4787
|
-
import
|
|
5347
|
+
import { readFile as readFile15, writeFile as writeFile8 } from "fs/promises";
|
|
5348
|
+
import path30 from "path";
|
|
5349
|
+
|
|
5350
|
+
// src/generator/vscode.ts
|
|
5351
|
+
import { writeFile as writeFile7 } from "fs/promises";
|
|
5352
|
+
import path29 from "path";
|
|
5353
|
+
|
|
5354
|
+
// src/generator/platform.ts
|
|
5355
|
+
function gitProviderLabel(profile) {
|
|
5356
|
+
const p = profile.git.provider;
|
|
5357
|
+
if (!p || p === "other") return "Git";
|
|
5358
|
+
const labels = {
|
|
5359
|
+
github: "GitHub",
|
|
5360
|
+
gitlab: "GitLab",
|
|
5361
|
+
bitbucket: "Bitbucket",
|
|
5362
|
+
"azure-devops": "Azure DevOps",
|
|
5363
|
+
gitea: "Gitea"
|
|
5364
|
+
};
|
|
5365
|
+
return labels[p] ?? "Git";
|
|
5366
|
+
}
|
|
5367
|
+
function prTerminology(profile) {
|
|
5368
|
+
const p = profile.git.provider;
|
|
5369
|
+
if (!p || p === "other") return "PR/MR";
|
|
5370
|
+
return GIT_PLATFORM_META[p]?.prTerm ?? "PR/MR";
|
|
5371
|
+
}
|
|
5372
|
+
|
|
5373
|
+
// src/generator/vscode.ts
|
|
5374
|
+
async function generateVSCodeArtifacts(profile) {
|
|
5375
|
+
const results = [];
|
|
5376
|
+
const vscodeDir = path29.join(profile.rootDir, ".vscode");
|
|
5377
|
+
const githubDir = path29.join(profile.rootDir, ".github");
|
|
5378
|
+
await Promise.all([ensureDir(vscodeDir), ensureDir(githubDir)]);
|
|
5379
|
+
const settingsPath = path29.join(vscodeDir, "settings.json");
|
|
5380
|
+
if (await fileExists(settingsPath)) {
|
|
5381
|
+
results.push({ relativePath: ".vscode/settings.json", status: "skipped-customized" });
|
|
5382
|
+
} else {
|
|
5383
|
+
await writeFile7(
|
|
5384
|
+
settingsPath,
|
|
5385
|
+
`${JSON.stringify(
|
|
5386
|
+
{
|
|
5387
|
+
"editor.formatOnSave": true,
|
|
5388
|
+
"editor.codeActionsOnSave": {
|
|
5389
|
+
"source.fixAll": "explicit"
|
|
5390
|
+
},
|
|
5391
|
+
"files.autoSave": "afterDelay"
|
|
5392
|
+
},
|
|
5393
|
+
null,
|
|
5394
|
+
2
|
|
5395
|
+
)}
|
|
5396
|
+
`,
|
|
5397
|
+
"utf8"
|
|
5398
|
+
);
|
|
5399
|
+
results.push({ relativePath: ".vscode/settings.json", status: "applied" });
|
|
5400
|
+
}
|
|
5401
|
+
const provider = gitProviderLabel(profile);
|
|
5402
|
+
const prTerm = prTerminology(profile);
|
|
5403
|
+
const copilotPath = path29.join(githubDir, "copilot-instructions.md");
|
|
5404
|
+
if (await fileExists(copilotPath)) {
|
|
5405
|
+
results.push({ relativePath: ".github/copilot-instructions.md", status: "skipped-customized" });
|
|
5406
|
+
} else {
|
|
5407
|
+
await writeFile7(
|
|
5408
|
+
copilotPath,
|
|
5409
|
+
`# Copilot Instructions
|
|
5410
|
+
|
|
5411
|
+
- Keep code changes small and testable.
|
|
5412
|
+
- Use Conventional Commits (feat:, fix:, docs:, etc.).
|
|
5413
|
+
- Prefer security-safe defaults.
|
|
5414
|
+
- Git platform: ${provider}. Always create a ${prTerm} for review.
|
|
5415
|
+
`,
|
|
5416
|
+
"utf8"
|
|
5417
|
+
);
|
|
5418
|
+
results.push({ relativePath: ".github/copilot-instructions.md", status: "applied" });
|
|
5419
|
+
}
|
|
5420
|
+
if (profile.ide.plan === "vscode-pro") {
|
|
5421
|
+
const securityPath = path29.join(vscodeDir, "security-review.agent.md");
|
|
5422
|
+
if (await fileExists(securityPath)) {
|
|
5423
|
+
results.push({
|
|
5424
|
+
relativePath: ".vscode/security-review.agent.md",
|
|
5425
|
+
status: "skipped-customized"
|
|
5426
|
+
});
|
|
5427
|
+
} else {
|
|
5428
|
+
await writeFile7(
|
|
5429
|
+
securityPath,
|
|
5430
|
+
"# Security Review Agent\n\nSpecialized mode for security review.\n",
|
|
5431
|
+
"utf8"
|
|
5432
|
+
);
|
|
5433
|
+
results.push({ relativePath: ".vscode/security-review.agent.md", status: "applied" });
|
|
5434
|
+
}
|
|
5435
|
+
}
|
|
5436
|
+
return results;
|
|
5437
|
+
}
|
|
5438
|
+
|
|
5439
|
+
// src/generator/personalization.ts
|
|
4788
5440
|
var PERSONALIZATION_CONTRACT_VERSION = 1;
|
|
4789
5441
|
var CONTEXT_PATH = ".cursor/project-context.md";
|
|
4790
5442
|
var AGENTS_PATH = "AGENTS.md";
|
|
@@ -4928,6 +5580,16 @@ function renderProjectContext(profile) {
|
|
|
4928
5580
|
if (profile.infra.ci !== "none" && profile.infra.ciFiles.length > 0) {
|
|
4929
5581
|
sections.push(`- CI: ${profile.infra.ci}.`);
|
|
4930
5582
|
}
|
|
5583
|
+
sections.push(
|
|
5584
|
+
"",
|
|
5585
|
+
"## Relevant skills",
|
|
5586
|
+
"",
|
|
5587
|
+
"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/`.",
|
|
5588
|
+
"",
|
|
5589
|
+
"| Skill / path | Role | Evidence |",
|
|
5590
|
+
"|--------------|------|----------|",
|
|
5591
|
+
"| (none yet) | Add rows when `/agent-kit-onboard` scaffolds domain skills or personalization installs packs | \u2014 |"
|
|
5592
|
+
);
|
|
4931
5593
|
if (profile.context.sources.length > 0) {
|
|
4932
5594
|
sections.push("", "## Sources", ...profile.context.sources.map((item) => `- ${item.value}`));
|
|
4933
5595
|
}
|
|
@@ -4935,7 +5597,7 @@ function renderProjectContext(profile) {
|
|
|
4935
5597
|
`;
|
|
4936
5598
|
}
|
|
4937
5599
|
async function createOwnedFile(rootDir, relativePath, content, evidence) {
|
|
4938
|
-
const target =
|
|
5600
|
+
const target = path30.join(rootDir, relativePath);
|
|
4939
5601
|
if (await fileExists(target)) {
|
|
4940
5602
|
return {
|
|
4941
5603
|
kind: "file",
|
|
@@ -4945,8 +5607,8 @@ async function createOwnedFile(rootDir, relativePath, content, evidence) {
|
|
|
4945
5607
|
evidence
|
|
4946
5608
|
};
|
|
4947
5609
|
}
|
|
4948
|
-
await ensureDir(
|
|
4949
|
-
await
|
|
5610
|
+
await ensureDir(path30.dirname(target));
|
|
5611
|
+
await writeFile8(target, content, "utf8");
|
|
4950
5612
|
return {
|
|
4951
5613
|
kind: "file",
|
|
4952
5614
|
id: relativePath,
|
|
@@ -4962,7 +5624,7 @@ async function packTargets(registryRoot, packId) {
|
|
|
4962
5624
|
async function existingTargets(projectRoot, targets) {
|
|
4963
5625
|
const checks = await Promise.all(
|
|
4964
5626
|
targets.map(
|
|
4965
|
-
async (target) => await fileExists(
|
|
5627
|
+
async (target) => await fileExists(path30.join(projectRoot, target)) ? target : null
|
|
4966
5628
|
)
|
|
4967
5629
|
);
|
|
4968
5630
|
return checks.filter((target) => target !== null);
|
|
@@ -4984,14 +5646,14 @@ async function applyPersonalization(input) {
|
|
|
4984
5646
|
componentResults.push({ ...item, status: "unavailable" });
|
|
4985
5647
|
continue;
|
|
4986
5648
|
}
|
|
4987
|
-
const target =
|
|
5649
|
+
const target = path30.posix.join(
|
|
4988
5650
|
".cursor",
|
|
4989
5651
|
"skills",
|
|
4990
5652
|
skill.path.includes("/core/") ? "core" : "community",
|
|
4991
5653
|
skill.id,
|
|
4992
5654
|
"SKILL.md"
|
|
4993
5655
|
);
|
|
4994
|
-
if (await fileExists(
|
|
5656
|
+
if (await fileExists(path30.join(input.rootDir, target))) {
|
|
4995
5657
|
componentResults.push({ ...item, status: "skipped-customized", path: target });
|
|
4996
5658
|
protectedPaths.add(target);
|
|
4997
5659
|
continue;
|
|
@@ -5039,6 +5701,49 @@ async function applyPersonalization(input) {
|
|
|
5039
5701
|
]);
|
|
5040
5702
|
protectedPaths.add(CONTEXT_PATH);
|
|
5041
5703
|
protectedPaths.add(AGENTS_PATH);
|
|
5704
|
+
const ideDetection = await detectIde(input.rootDir);
|
|
5705
|
+
if (ideDetection.ide === "vscode" || ideDetection.ide === "other") {
|
|
5706
|
+
const git = {
|
|
5707
|
+
providerKind: input.profile.git.providerKind ?? "unknown",
|
|
5708
|
+
providerConfidence: input.profile.git.providerConfidence ?? "low",
|
|
5709
|
+
providerEvidence: input.profile.git.providerEvidence ?? [],
|
|
5710
|
+
remotes: input.profile.git.remotes ?? [],
|
|
5711
|
+
mode: input.profile.git.mode ?? "none",
|
|
5712
|
+
workflow: input.profile.git.workflow ?? "unknown",
|
|
5713
|
+
isDirty: input.profile.git.isDirty ?? false,
|
|
5714
|
+
hasLocalStaging: input.profile.git.hasLocalStaging ?? false,
|
|
5715
|
+
hasRemoteStaging: input.profile.git.hasRemoteStaging ?? false,
|
|
5716
|
+
provider: input.profile.git.provider,
|
|
5717
|
+
remoteUrl: input.profile.git.remoteUrl,
|
|
5718
|
+
remoteName: input.profile.git.remoteName,
|
|
5719
|
+
currentBranch: input.profile.git.currentBranch,
|
|
5720
|
+
defaultBranch: input.profile.git.defaultBranch
|
|
5721
|
+
};
|
|
5722
|
+
const projectProfile = {
|
|
5723
|
+
rootDir: input.rootDir,
|
|
5724
|
+
stack: input.profile.stack,
|
|
5725
|
+
git,
|
|
5726
|
+
ide: ideDetection,
|
|
5727
|
+
infra: input.profile.infra,
|
|
5728
|
+
services: input.profile.services,
|
|
5729
|
+
installHooks: false,
|
|
5730
|
+
selectedCoreComponents: []
|
|
5731
|
+
};
|
|
5732
|
+
const vscodeResults = await generateVSCodeArtifacts(projectProfile);
|
|
5733
|
+
const ideEvidence = [
|
|
5734
|
+
{ source: "derived", value: `ide:${ideDetection.ide}` }
|
|
5735
|
+
];
|
|
5736
|
+
for (const artifact of vscodeResults) {
|
|
5737
|
+
protectedPaths.add(artifact.relativePath);
|
|
5738
|
+
componentResults.push({
|
|
5739
|
+
kind: "file",
|
|
5740
|
+
id: artifact.relativePath,
|
|
5741
|
+
path: artifact.relativePath,
|
|
5742
|
+
status: artifact.status,
|
|
5743
|
+
evidence: ideEvidence
|
|
5744
|
+
});
|
|
5745
|
+
}
|
|
5746
|
+
}
|
|
5042
5747
|
const result = {
|
|
5043
5748
|
contractVersion: PERSONALIZATION_CONTRACT_VERSION,
|
|
5044
5749
|
generatorVersion: input.generatorVersion,
|
|
@@ -5046,7 +5751,7 @@ async function applyPersonalization(input) {
|
|
|
5046
5751
|
items: [...fileResults, ...componentResults],
|
|
5047
5752
|
protectedPaths: [...protectedPaths].sort()
|
|
5048
5753
|
};
|
|
5049
|
-
await writeJson(
|
|
5754
|
+
await writeJson(path30.join(input.rootDir, RESULT_PATH), result);
|
|
5050
5755
|
return {
|
|
5051
5756
|
result,
|
|
5052
5757
|
manifest: {
|
|
@@ -5064,26 +5769,26 @@ async function applyPersonalization(input) {
|
|
|
5064
5769
|
};
|
|
5065
5770
|
}
|
|
5066
5771
|
async function readRepositoryProfile(rootDir) {
|
|
5067
|
-
const target =
|
|
5772
|
+
const target = path30.join(rootDir, ".cursor/agent-kit.config.json");
|
|
5068
5773
|
if (!await fileExists(target)) return null;
|
|
5069
|
-
return JSON.parse(await
|
|
5774
|
+
return JSON.parse(await readFile15(target, "utf8"));
|
|
5070
5775
|
}
|
|
5071
5776
|
|
|
5072
5777
|
// src/lifecycle/onboard-migration.ts
|
|
5073
5778
|
import { createHash as createHash4 } from "crypto";
|
|
5074
|
-
import { readFile as
|
|
5075
|
-
import
|
|
5779
|
+
import { readFile as readFile16, unlink } from "fs/promises";
|
|
5780
|
+
import path31 from "path";
|
|
5076
5781
|
var LEGACY_ONBOARD_PATH = ".cursor/commands/onboard.md";
|
|
5077
5782
|
var NAMESPACED_ONBOARD_PATH = ".cursor/commands/agent-kit-onboard.md";
|
|
5078
5783
|
var MANAGED_LEGACY_HASHES = /* @__PURE__ */ new Set([
|
|
5079
5784
|
"b274a68941813f19b185893cb7c5561dff027f53270890029992f208e24992fe"
|
|
5080
5785
|
]);
|
|
5081
5786
|
async function migrateLegacyOnboardCommand(projectRoot, managedHashes = MANAGED_LEGACY_HASHES) {
|
|
5082
|
-
const legacyPath =
|
|
5787
|
+
const legacyPath = path31.join(projectRoot, LEGACY_ONBOARD_PATH);
|
|
5083
5788
|
if (!await fileExists(legacyPath)) return "absent";
|
|
5084
|
-
const namespacedPath =
|
|
5789
|
+
const namespacedPath = path31.join(projectRoot, NAMESPACED_ONBOARD_PATH);
|
|
5085
5790
|
if (!await fileExists(namespacedPath)) return "preserved-customized";
|
|
5086
|
-
const content = await
|
|
5791
|
+
const content = await readFile16(legacyPath);
|
|
5087
5792
|
const hash = createHash4("sha256").update(content).digest("hex");
|
|
5088
5793
|
if (!managedHashes.has(hash)) return "preserved-customized";
|
|
5089
5794
|
await unlink(legacyPath);
|
|
@@ -5156,7 +5861,7 @@ function printReadinessNarrative(result) {
|
|
|
5156
5861
|
);
|
|
5157
5862
|
}
|
|
5158
5863
|
async function performInstall(options) {
|
|
5159
|
-
const projectRoot =
|
|
5864
|
+
const projectRoot = path32.resolve(options.cwd);
|
|
5160
5865
|
const packs = parsePackList(options.pack);
|
|
5161
5866
|
const existing = await loadAgentKitManifest(projectRoot);
|
|
5162
5867
|
const registry = await resolveRegistryFromCli({
|
|
@@ -5167,53 +5872,57 @@ async function performInstall(options) {
|
|
|
5167
5872
|
refresh: options.refresh,
|
|
5168
5873
|
manifest: existing
|
|
5169
5874
|
});
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
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
|
|
5875
|
+
try {
|
|
5876
|
+
const draft = buildManifest({
|
|
5877
|
+
version: KIT_VERSION,
|
|
5878
|
+
profile: options.profile ?? existing?.profile ?? "default",
|
|
5879
|
+
packs: packs.length > 0 ? packs : existing?.packs,
|
|
5880
|
+
skills: existing?.skills,
|
|
5881
|
+
protected: existing?.protected,
|
|
5882
|
+
personalization: existing?.personalization,
|
|
5883
|
+
registryUrl: registry.url ?? existing?.registry?.url,
|
|
5884
|
+
registryRef: registry.ref ?? existing?.registry?.ref
|
|
5197
5885
|
});
|
|
5198
|
-
await
|
|
5199
|
-
|
|
5886
|
+
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));
|
|
5887
|
+
const manifestPath = await saveManifest(projectRoot, draft);
|
|
5888
|
+
const readinessExecution = await executeSafeReadinessFixes(projectRoot, {
|
|
5200
5889
|
generatorVersion: KIT_VERSION
|
|
5201
5890
|
});
|
|
5202
|
-
readiness
|
|
5891
|
+
let readiness = readinessExecution.after;
|
|
5892
|
+
const profile = await readRepositoryProfile(projectRoot);
|
|
5893
|
+
if (profile) {
|
|
5894
|
+
const registryIndex = await loadRegistry(registry.root);
|
|
5895
|
+
const personalization = await applyPersonalization({
|
|
5896
|
+
rootDir: projectRoot,
|
|
5897
|
+
registryRoot: registry.root,
|
|
5898
|
+
profile,
|
|
5899
|
+
report: readinessExecution.after,
|
|
5900
|
+
registry: registryIndex,
|
|
5901
|
+
manifest: draft,
|
|
5902
|
+
generatorVersion: KIT_VERSION
|
|
5903
|
+
});
|
|
5904
|
+
await saveManifest(projectRoot, personalization.manifest);
|
|
5905
|
+
readiness = createReadinessReport(await runScanner(projectRoot), {
|
|
5906
|
+
generatorVersion: KIT_VERSION
|
|
5907
|
+
});
|
|
5908
|
+
readiness.appliedSafeFixes = readinessExecution.after.appliedSafeFixes;
|
|
5909
|
+
}
|
|
5910
|
+
await writeReadinessSnapshot(projectRoot, readiness);
|
|
5911
|
+
return {
|
|
5912
|
+
projectRoot,
|
|
5913
|
+
manifestPath,
|
|
5914
|
+
stats,
|
|
5915
|
+
readiness,
|
|
5916
|
+
safeChanges: readinessExecution.changes
|
|
5917
|
+
};
|
|
5918
|
+
} finally {
|
|
5919
|
+
await registry.unlock?.();
|
|
5203
5920
|
}
|
|
5204
|
-
await writeReadinessSnapshot(projectRoot, readiness);
|
|
5205
|
-
return {
|
|
5206
|
-
projectRoot,
|
|
5207
|
-
manifestPath,
|
|
5208
|
-
stats,
|
|
5209
|
-
readiness,
|
|
5210
|
-
safeChanges: readinessExecution.changes
|
|
5211
|
-
};
|
|
5212
5921
|
}
|
|
5213
5922
|
var installCommand = defineCommand11({
|
|
5214
5923
|
meta: {
|
|
5215
5924
|
name: "install",
|
|
5216
|
-
description: "Bootstrap L0 (+ optional packs)
|
|
5925
|
+
description: "Bootstrap L0 (+ optional packs) and write .cursor/agent-kit.json."
|
|
5217
5926
|
},
|
|
5218
5927
|
args: {
|
|
5219
5928
|
profile: {
|
|
@@ -5225,6 +5934,17 @@ var installCommand = defineCommand11({
|
|
|
5225
5934
|
type: "string",
|
|
5226
5935
|
description: `Comma-separated L1 pack ids (e.g. cybersec,devops). Known: ${DOMAIN_PACK_IDS.join(", ")}`
|
|
5227
5936
|
},
|
|
5937
|
+
yes: {
|
|
5938
|
+
type: "boolean",
|
|
5939
|
+
alias: "y",
|
|
5940
|
+
description: "Skip interactive prompts; use defaults (IDE-agnostic non-interactive mode)",
|
|
5941
|
+
default: false
|
|
5942
|
+
},
|
|
5943
|
+
"force-root": {
|
|
5944
|
+
type: "boolean",
|
|
5945
|
+
description: "Bypass the ambiguous-root guard (use with caution)",
|
|
5946
|
+
default: false
|
|
5947
|
+
},
|
|
5228
5948
|
cwd: {
|
|
5229
5949
|
type: "string",
|
|
5230
5950
|
default: process.cwd()
|
|
@@ -5232,7 +5952,25 @@ var installCommand = defineCommand11({
|
|
|
5232
5952
|
...REGISTRY_CLI_ARGS
|
|
5233
5953
|
},
|
|
5234
5954
|
async run({ args }) {
|
|
5235
|
-
const
|
|
5955
|
+
const nonInteractive = args.yes || isNonInteractive();
|
|
5956
|
+
if (nonInteractive) {
|
|
5957
|
+
logger.info("Non-interactive mode: skipping prompts, using defaults.");
|
|
5958
|
+
}
|
|
5959
|
+
let projectRoot;
|
|
5960
|
+
try {
|
|
5961
|
+
projectRoot = await confirmProjectRoot(args.cwd, {
|
|
5962
|
+
nonInteractive,
|
|
5963
|
+
command: "install",
|
|
5964
|
+
forceRoot: args["force-root"]
|
|
5965
|
+
});
|
|
5966
|
+
} catch (err) {
|
|
5967
|
+
if (err instanceof RootRefusedError) {
|
|
5968
|
+
logger.error(err.message);
|
|
5969
|
+
process.exitCode = 1;
|
|
5970
|
+
return;
|
|
5971
|
+
}
|
|
5972
|
+
throw err;
|
|
5973
|
+
}
|
|
5236
5974
|
logger.info(`Installing into: ${projectRoot}`);
|
|
5237
5975
|
const packs = parsePackList(args.pack);
|
|
5238
5976
|
for (const id of packs) {
|
|
@@ -5240,19 +5978,28 @@ var installCommand = defineCommand11({
|
|
|
5240
5978
|
logger.warn(`Pack '${id}' is not in the known L1 list; will still try registry.`);
|
|
5241
5979
|
}
|
|
5242
5980
|
}
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
|
|
5981
|
+
try {
|
|
5982
|
+
const result = await performInstall({
|
|
5983
|
+
cwd: projectRoot,
|
|
5984
|
+
profile: args.profile,
|
|
5985
|
+
pack: args.pack,
|
|
5986
|
+
registry: args.registry,
|
|
5987
|
+
url: args.url,
|
|
5988
|
+
ref: args.ref,
|
|
5989
|
+
refresh: args.refresh
|
|
5990
|
+
});
|
|
5991
|
+
logApplyStats(result.stats);
|
|
5992
|
+
logger.success(`Manifest written: ${result.manifestPath}`);
|
|
5993
|
+
logger.success("Readiness snapshot written: .cursor/context/readiness.json");
|
|
5994
|
+
printReadinessNarrative(result);
|
|
5995
|
+
} catch (err) {
|
|
5996
|
+
const hint = classifyInstallError(err);
|
|
5997
|
+
logger.error(hint.message);
|
|
5998
|
+
console.error(`
|
|
5999
|
+
${hint.recovery}
|
|
6000
|
+
`);
|
|
6001
|
+
process.exit(1);
|
|
6002
|
+
}
|
|
5256
6003
|
}
|
|
5257
6004
|
});
|
|
5258
6005
|
|
|
@@ -5263,7 +6010,7 @@ async function runInitCompatibility(cwd, installer = performInstall) {
|
|
|
5263
6010
|
var initCommand = defineCommand12({
|
|
5264
6011
|
meta: {
|
|
5265
6012
|
name: "init",
|
|
5266
|
-
description: "Guided
|
|
6013
|
+
description: "Guided setup entry (compat path to install + readiness)."
|
|
5267
6014
|
},
|
|
5268
6015
|
args: {
|
|
5269
6016
|
cwd: {
|
|
@@ -5273,25 +6020,42 @@ var initCommand = defineCommand12({
|
|
|
5273
6020
|
}
|
|
5274
6021
|
},
|
|
5275
6022
|
async run({ args }) {
|
|
5276
|
-
|
|
6023
|
+
const nonInteractive = isNonInteractive();
|
|
6024
|
+
if (!nonInteractive) {
|
|
6025
|
+
intro(`agent-kit v${KIT_VERSION}`);
|
|
6026
|
+
} else {
|
|
6027
|
+
logger.info(`agent-kit v${KIT_VERSION} (non-interactive mode)`);
|
|
6028
|
+
}
|
|
5277
6029
|
logger.info("init now uses the canonical install and readiness workflow.");
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
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
|
-
|
|
6030
|
+
try {
|
|
6031
|
+
const result = await runInitCompatibility(args.cwd);
|
|
6032
|
+
const pending = result.readiness.pendingActions.length;
|
|
6033
|
+
logger.success(`L0 and readiness prepared in ${result.projectRoot}`);
|
|
6034
|
+
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.";
|
|
6035
|
+
if (!nonInteractive) {
|
|
6036
|
+
outro(nextStep);
|
|
6037
|
+
} else {
|
|
6038
|
+
logger.info(nextStep);
|
|
6039
|
+
}
|
|
6040
|
+
} catch (err) {
|
|
6041
|
+
const hint = classifyInstallError(err);
|
|
6042
|
+
logger.error(hint.message);
|
|
6043
|
+
console.error(`
|
|
6044
|
+
${hint.recovery}
|
|
6045
|
+
`);
|
|
6046
|
+
process.exit(1);
|
|
6047
|
+
}
|
|
5284
6048
|
}
|
|
5285
6049
|
});
|
|
5286
6050
|
|
|
5287
6051
|
// src/commands/monitors.ts
|
|
5288
|
-
import
|
|
6052
|
+
import path34 from "path";
|
|
5289
6053
|
import { defineCommand as defineCommand13 } from "citty";
|
|
5290
6054
|
|
|
5291
6055
|
// src/invariants/monitors-untriaged.ts
|
|
5292
6056
|
import { execFile as execFile5 } from "child_process";
|
|
5293
|
-
import { readFile as
|
|
5294
|
-
import
|
|
6057
|
+
import { readFile as readFile17, readdir as readdir5, stat as stat3 } from "fs/promises";
|
|
6058
|
+
import path33 from "path";
|
|
5295
6059
|
import { promisify as promisify5 } from "util";
|
|
5296
6060
|
|
|
5297
6061
|
// src/invariants/triage-heading.ts
|
|
@@ -5332,7 +6096,7 @@ async function gitFreshMonitorNames(rootDir) {
|
|
|
5332
6096
|
for (const line of stdout.split("\n")) {
|
|
5333
6097
|
if (!line.trim()) continue;
|
|
5334
6098
|
const file = line.slice(3).trim().replace(/^.* -> /, "");
|
|
5335
|
-
const base =
|
|
6099
|
+
const base = path33.basename(file);
|
|
5336
6100
|
if (base.startsWith("plan-monitor-") && base.endsWith(".md")) {
|
|
5337
6101
|
names.add(base);
|
|
5338
6102
|
}
|
|
@@ -5355,15 +6119,15 @@ function monitorSlugFromName(fileName) {
|
|
|
5355
6119
|
return fileName.replace(/^plan-monitor-/, "").replace(/\.md$/, "").toLowerCase();
|
|
5356
6120
|
}
|
|
5357
6121
|
async function selectUntriagedMonitors(rootDir) {
|
|
5358
|
-
const root =
|
|
5359
|
-
const memoryDir =
|
|
6122
|
+
const root = path33.resolve(rootDir);
|
|
6123
|
+
const memoryDir = path33.join(root, ".cursor", "memory");
|
|
5360
6124
|
const allNames = await listMonitorFiles(memoryDir);
|
|
5361
6125
|
const selectionOrder = ["git-fresh", "handoff-aligned", "untriaged-scan"];
|
|
5362
6126
|
const byName = /* @__PURE__ */ new Map();
|
|
5363
6127
|
for (const name of allNames) {
|
|
5364
|
-
const abs =
|
|
6128
|
+
const abs = path33.join(memoryDir, name);
|
|
5365
6129
|
try {
|
|
5366
|
-
const [content, st] = await Promise.all([
|
|
6130
|
+
const [content, st] = await Promise.all([readFile17(abs, "utf8"), stat3(abs)]);
|
|
5367
6131
|
byName.set(name, { content, mtimeMs: st.mtimeMs });
|
|
5368
6132
|
} catch {
|
|
5369
6133
|
}
|
|
@@ -5376,7 +6140,7 @@ async function selectUntriagedMonitors(rootDir) {
|
|
|
5376
6140
|
const gitFreshSet = [...gitFresh].filter(untriaged).sort();
|
|
5377
6141
|
let handoff = "";
|
|
5378
6142
|
try {
|
|
5379
|
-
handoff = await
|
|
6143
|
+
handoff = await readFile17(path33.join(root, ".cursor", "HANDOFF.md"), "utf8");
|
|
5380
6144
|
} catch {
|
|
5381
6145
|
handoff = "";
|
|
5382
6146
|
}
|
|
@@ -5396,8 +6160,8 @@ async function selectUntriagedMonitors(rootDir) {
|
|
|
5396
6160
|
const row = byName.get(name);
|
|
5397
6161
|
if (!row) continue;
|
|
5398
6162
|
entries.push({
|
|
5399
|
-
path:
|
|
5400
|
-
relativePath:
|
|
6163
|
+
path: path33.join(memoryDir, name),
|
|
6164
|
+
relativePath: path33.relative(root, path33.join(memoryDir, name)).split(path33.sep).join("/"),
|
|
5401
6165
|
mtimeMs: row.mtimeMs,
|
|
5402
6166
|
hasTriageHeading: false,
|
|
5403
6167
|
hasOpenGaps: hasOpenGaps(row.content),
|
|
@@ -5419,7 +6183,7 @@ async function selectUntriagedMonitors(rootDir) {
|
|
|
5419
6183
|
var monitorsCommand = defineCommand13({
|
|
5420
6184
|
meta: {
|
|
5421
6185
|
name: "monitors",
|
|
5422
|
-
description: "
|
|
6186
|
+
description: "Select untriaged plan monitors (--untriaged; SoT for chat triage)."
|
|
5423
6187
|
},
|
|
5424
6188
|
args: {
|
|
5425
6189
|
cwd: {
|
|
@@ -5443,7 +6207,7 @@ var monitorsCommand = defineCommand13({
|
|
|
5443
6207
|
process.exitCode = 2;
|
|
5444
6208
|
return;
|
|
5445
6209
|
}
|
|
5446
|
-
const result = await selectUntriagedMonitors(
|
|
6210
|
+
const result = await selectUntriagedMonitors(path34.resolve(args.cwd));
|
|
5447
6211
|
if (args.json) {
|
|
5448
6212
|
console.log(JSON.stringify(result, null, 2));
|
|
5449
6213
|
return;
|
|
@@ -5459,11 +6223,11 @@ var monitorsCommand = defineCommand13({
|
|
|
5459
6223
|
});
|
|
5460
6224
|
|
|
5461
6225
|
// src/commands/run-plan.ts
|
|
5462
|
-
import
|
|
6226
|
+
import path39 from "path";
|
|
5463
6227
|
import { defineCommand as defineCommand14 } from "citty";
|
|
5464
6228
|
|
|
5465
6229
|
// src/plan-loop/backends.ts
|
|
5466
|
-
import { execFileSync as execFileSync2, spawn as
|
|
6230
|
+
import { execFileSync as execFileSync2, spawn as spawn6 } from "child_process";
|
|
5467
6231
|
import { createWriteStream } from "fs";
|
|
5468
6232
|
async function which(bin) {
|
|
5469
6233
|
try {
|
|
@@ -5474,9 +6238,9 @@ async function which(bin) {
|
|
|
5474
6238
|
}
|
|
5475
6239
|
}
|
|
5476
6240
|
function spawnLogged(command, args, logPath) {
|
|
5477
|
-
return new Promise((
|
|
6241
|
+
return new Promise((resolve2, reject) => {
|
|
5478
6242
|
const out = createWriteStream(logPath, { flags: "w" });
|
|
5479
|
-
const child =
|
|
6243
|
+
const child = spawn6(command, args, {
|
|
5480
6244
|
stdio: ["ignore", "pipe", "pipe"]
|
|
5481
6245
|
});
|
|
5482
6246
|
const onData = (chunk) => {
|
|
@@ -5490,7 +6254,7 @@ function spawnLogged(command, args, logPath) {
|
|
|
5490
6254
|
reject(err);
|
|
5491
6255
|
});
|
|
5492
6256
|
child.on("close", (code) => {
|
|
5493
|
-
out.end(() =>
|
|
6257
|
+
out.end(() => resolve2({ exitCode: code ?? 1 }));
|
|
5494
6258
|
});
|
|
5495
6259
|
});
|
|
5496
6260
|
}
|
|
@@ -5544,14 +6308,14 @@ function listBackendIds() {
|
|
|
5544
6308
|
}
|
|
5545
6309
|
|
|
5546
6310
|
// src/plan-loop/run-loop.ts
|
|
5547
|
-
import { mkdir as
|
|
5548
|
-
import
|
|
6311
|
+
import { mkdir as mkdir6, readFile as readFile20, rm as rm2, unlink as unlink2 } from "fs/promises";
|
|
6312
|
+
import path38 from "path";
|
|
5549
6313
|
|
|
5550
6314
|
// src/plan-loop/external-review.ts
|
|
5551
|
-
import { spawn as
|
|
5552
|
-
import
|
|
5553
|
-
var CANONICAL_REL =
|
|
5554
|
-
var FALLBACK_REL =
|
|
6315
|
+
import { spawn as spawn7 } from "child_process";
|
|
6316
|
+
import path35 from "path";
|
|
6317
|
+
var CANONICAL_REL = path35.join(".cursor", "scripts", "plan-external-review.sh");
|
|
6318
|
+
var FALLBACK_REL = path35.join("scripts", "plan-external-review.sh");
|
|
5555
6319
|
function isPlanExhaustedReason(reason) {
|
|
5556
6320
|
const r = reason.trim().toLowerCase();
|
|
5557
6321
|
if (!r) return false;
|
|
@@ -5569,12 +6333,12 @@ function shouldArmExternalPlanReview(input) {
|
|
|
5569
6333
|
return false;
|
|
5570
6334
|
}
|
|
5571
6335
|
async function armExternalPlanReview(root, options = {}) {
|
|
5572
|
-
const spawnFn = options.spawnFn ??
|
|
6336
|
+
const spawnFn = options.spawnFn ?? spawn7;
|
|
5573
6337
|
const existsFn = options.existsFn ?? fileExists;
|
|
5574
6338
|
const log = options.log ?? ((line) => console.log(line));
|
|
5575
6339
|
const force = options.force === true;
|
|
5576
|
-
const canonicalPath =
|
|
5577
|
-
const fallbackPath =
|
|
6340
|
+
const canonicalPath = path35.join(root, CANONICAL_REL);
|
|
6341
|
+
const fallbackPath = path35.join(root, FALLBACK_REL);
|
|
5578
6342
|
let scriptPath = null;
|
|
5579
6343
|
let scriptRel = CANONICAL_REL;
|
|
5580
6344
|
if (await existsFn(canonicalPath)) {
|
|
@@ -5592,7 +6356,7 @@ async function armExternalPlanReview(root, options = {}) {
|
|
|
5592
6356
|
}
|
|
5593
6357
|
log("Plan exhausted: arming optional external plan review (opt-in handled by script)...");
|
|
5594
6358
|
const args = force ? [scriptPath, "--force", "--print"] : [scriptPath, "--print"];
|
|
5595
|
-
return new Promise((
|
|
6359
|
+
return new Promise((resolve2) => {
|
|
5596
6360
|
const child = spawnFn("bash", args, {
|
|
5597
6361
|
cwd: root,
|
|
5598
6362
|
env: { ...process.env, AGENT_KIT_HEADLESS: "1" }
|
|
@@ -5613,7 +6377,7 @@ async function armExternalPlanReview(root, options = {}) {
|
|
|
5613
6377
|
log(
|
|
5614
6378
|
`tip: external plan review launcher failed (${err.message}). Manual: ${scriptRel} or /plan-external-review`
|
|
5615
6379
|
);
|
|
5616
|
-
|
|
6380
|
+
resolve2({ invoked: true, exitCode: null, output });
|
|
5617
6381
|
});
|
|
5618
6382
|
child.on("close", (code) => {
|
|
5619
6383
|
if (code !== 0 && code !== null) {
|
|
@@ -5621,13 +6385,13 @@ async function armExternalPlanReview(root, options = {}) {
|
|
|
5621
6385
|
`tip: external plan review exited ${code} (ignored; does not fail the loop). Manual: ${scriptRel} or /plan-external-review`
|
|
5622
6386
|
);
|
|
5623
6387
|
}
|
|
5624
|
-
|
|
6388
|
+
resolve2({ invoked: true, exitCode: code, output });
|
|
5625
6389
|
});
|
|
5626
6390
|
});
|
|
5627
6391
|
}
|
|
5628
6392
|
|
|
5629
6393
|
// src/plan-loop/persona-banners.ts
|
|
5630
|
-
import
|
|
6394
|
+
import path36 from "path";
|
|
5631
6395
|
import {
|
|
5632
6396
|
blue,
|
|
5633
6397
|
cyan as cyan2,
|
|
@@ -5663,7 +6427,7 @@ function resolveColor(name, fallback) {
|
|
|
5663
6427
|
async function resolveCliPersonaId(root) {
|
|
5664
6428
|
try {
|
|
5665
6429
|
const cfg = await readJson(
|
|
5666
|
-
|
|
6430
|
+
path36.join(root, ".cursor", "context", "config.json")
|
|
5667
6431
|
);
|
|
5668
6432
|
const modes = cfg?.agentPersona?.modes ?? cfg?.workspaceSkin?.modes;
|
|
5669
6433
|
const id = modes?.[CLI_RUN_PLAN_MODE];
|
|
@@ -5674,7 +6438,7 @@ async function resolveCliPersonaId(root) {
|
|
|
5674
6438
|
}
|
|
5675
6439
|
async function loadPersonaPack(root, personaId) {
|
|
5676
6440
|
try {
|
|
5677
|
-
const personaPath =
|
|
6441
|
+
const personaPath = path36.join(root, "registry", "personas", "core", personaId, "persona.json");
|
|
5678
6442
|
const pack = await readJson(personaPath);
|
|
5679
6443
|
if (!pack || typeof pack.id !== "string") return null;
|
|
5680
6444
|
return pack;
|
|
@@ -5723,8 +6487,8 @@ function createPersonaBannerPrinter(persona) {
|
|
|
5723
6487
|
}
|
|
5724
6488
|
|
|
5725
6489
|
// src/plan-loop/plan-state.ts
|
|
5726
|
-
import { readFile as
|
|
5727
|
-
import
|
|
6490
|
+
import { readFile as readFile18, readdir as readdir6 } from "fs/promises";
|
|
6491
|
+
import path37 from "path";
|
|
5728
6492
|
function countPendingTodos(raw) {
|
|
5729
6493
|
const lines = raw.split(/\r?\n/);
|
|
5730
6494
|
let inFront = 0;
|
|
@@ -5752,14 +6516,14 @@ function countPendingTodos(raw) {
|
|
|
5752
6516
|
async function findActivePlanFile(plansDir) {
|
|
5753
6517
|
if (!await fileExists(plansDir)) return null;
|
|
5754
6518
|
const files = (await readdir6(plansDir)).filter((f) => f.endsWith(".plan.md")).sort();
|
|
5755
|
-
return files[0] ?
|
|
6519
|
+
return files[0] ? path37.join(plansDir, files[0]) : null;
|
|
5756
6520
|
}
|
|
5757
6521
|
async function readPlan(planPath) {
|
|
5758
|
-
return
|
|
6522
|
+
return readFile18(planPath, "utf8");
|
|
5759
6523
|
}
|
|
5760
6524
|
|
|
5761
6525
|
// src/plan-loop/sentinel.ts
|
|
5762
|
-
import { readFile as
|
|
6526
|
+
import { readFile as readFile19 } from "fs/promises";
|
|
5763
6527
|
var SENTINEL_RE = /LOOP_TICK_RESULT:\s*(continue|stop(?:\s*[—\-].*)?)/i;
|
|
5764
6528
|
function takeFromText(text) {
|
|
5765
6529
|
if (!text) return null;
|
|
@@ -5806,7 +6570,7 @@ function parseSentinelFromLog(content) {
|
|
|
5806
6570
|
}
|
|
5807
6571
|
async function parseSentinelFromLogFile(logPath) {
|
|
5808
6572
|
try {
|
|
5809
|
-
const content = await
|
|
6573
|
+
const content = await readFile19(logPath, "utf8");
|
|
5810
6574
|
return parseSentinelFromLog(content);
|
|
5811
6575
|
} catch {
|
|
5812
6576
|
return { kind: "missing" };
|
|
@@ -5829,16 +6593,16 @@ function sleep(ms) {
|
|
|
5829
6593
|
return new Promise((r) => setTimeout(r, ms));
|
|
5830
6594
|
}
|
|
5831
6595
|
async function runPlanLoop(opts) {
|
|
5832
|
-
const plansDir =
|
|
5833
|
-
const stopFile =
|
|
5834
|
-
const logDir =
|
|
6596
|
+
const plansDir = path38.join(opts.root, ".cursor", "plans");
|
|
6597
|
+
const stopFile = path38.join(opts.root, ".cursor", "loop.stop");
|
|
6598
|
+
const logDir = path38.join(opts.root, ".cursor", "loop-logs");
|
|
5835
6599
|
const planPath = await findActivePlanFile(plansDir);
|
|
5836
6600
|
if (!planPath) {
|
|
5837
6601
|
logger.error("No active plan in .cursor/plans/");
|
|
5838
6602
|
return 1;
|
|
5839
6603
|
}
|
|
5840
6604
|
const pending = async () => countPendingTodos(await readPlan(planPath));
|
|
5841
|
-
await
|
|
6605
|
+
await mkdir6(logDir, { recursive: true });
|
|
5842
6606
|
try {
|
|
5843
6607
|
await unlink2(stopFile);
|
|
5844
6608
|
} catch {
|
|
@@ -5852,7 +6616,7 @@ async function runPlanLoop(opts) {
|
|
|
5852
6616
|
try {
|
|
5853
6617
|
const persona = await loadCliRunPlanPersona(opts.root);
|
|
5854
6618
|
const banners = createPersonaBannerPrinter(persona);
|
|
5855
|
-
console.log(`Active plan: ${
|
|
6619
|
+
console.log(`Active plan: ${path38.basename(planPath)}`);
|
|
5856
6620
|
console.log(`Pending to-dos: ${await pending()} | max ticks: ${opts.maxTicks}`);
|
|
5857
6621
|
console.log(`Backend: ${opts.backend.id}`);
|
|
5858
6622
|
if (persona) {
|
|
@@ -5884,7 +6648,7 @@ async function runPlanLoop(opts) {
|
|
|
5884
6648
|
if (banners) banners.stop(msg);
|
|
5885
6649
|
else console.log(msg);
|
|
5886
6650
|
try {
|
|
5887
|
-
await
|
|
6651
|
+
await rm2(stopFile, { force: true });
|
|
5888
6652
|
} catch {
|
|
5889
6653
|
}
|
|
5890
6654
|
break;
|
|
@@ -5895,8 +6659,8 @@ async function runPlanLoop(opts) {
|
|
|
5895
6659
|
planExhausted = true;
|
|
5896
6660
|
break;
|
|
5897
6661
|
}
|
|
5898
|
-
const logPath =
|
|
5899
|
-
const relLog =
|
|
6662
|
+
const logPath = path38.join(logDir, `tick-${stamp()}.log`);
|
|
6663
|
+
const relLog = path38.relative(opts.root, logPath);
|
|
5900
6664
|
console.log("");
|
|
5901
6665
|
const tickLine = `=== tick ${tick}/${opts.maxTicks} - pending: ${before} - log: ${relLog} ===`;
|
|
5902
6666
|
if (banners) banners.tickStart(tickLine);
|
|
@@ -5915,7 +6679,7 @@ async function runPlanLoop(opts) {
|
|
|
5915
6679
|
return 1;
|
|
5916
6680
|
}
|
|
5917
6681
|
try {
|
|
5918
|
-
const logText = await
|
|
6682
|
+
const logText = await readFile20(logPath, "utf8");
|
|
5919
6683
|
if (logText.includes("Too many MCP tools")) {
|
|
5920
6684
|
const msg = "Too many MCP tools for the headless model - disable servers (cursor-agent mcp disable <id>) and run again.";
|
|
5921
6685
|
if (banners) banners.stop(msg);
|
|
@@ -5972,7 +6736,7 @@ async function runPlanLoop(opts) {
|
|
|
5972
6736
|
const finishDetail = `after ${tick} tick(s); pending: ${pendingNow}`;
|
|
5973
6737
|
if (banners) banners.phaseComplete(finishDetail);
|
|
5974
6738
|
console.log(
|
|
5975
|
-
`Loop finished after ${tick} tick(s). Pending now: ${pendingNow}. Logs in ${
|
|
6739
|
+
`Loop finished after ${tick} tick(s). Pending now: ${pendingNow}. Logs in ${path38.relative(opts.root, logDir)}/`
|
|
5976
6740
|
);
|
|
5977
6741
|
if (planExhausted || shouldArmExternalPlanReview({ pending: pendingNow, stopReason })) {
|
|
5978
6742
|
await armExternalPlanReview(opts.root);
|
|
@@ -5987,7 +6751,7 @@ async function runPlanLoop(opts) {
|
|
|
5987
6751
|
var runPlanCommand = defineCommand14({
|
|
5988
6752
|
meta: {
|
|
5989
6753
|
name: "run-plan",
|
|
5990
|
-
description: "Headless continuous plan runner
|
|
6754
|
+
description: "Headless continuous plan runner (one fresh agent per tick). Never git-prod."
|
|
5991
6755
|
},
|
|
5992
6756
|
args: {
|
|
5993
6757
|
cwd: {
|
|
@@ -6043,7 +6807,7 @@ var runPlanCommand = defineCommand14({
|
|
|
6043
6807
|
return;
|
|
6044
6808
|
}
|
|
6045
6809
|
const code = await runPlanLoop({
|
|
6046
|
-
root:
|
|
6810
|
+
root: path39.resolve(args.cwd),
|
|
6047
6811
|
maxTicks,
|
|
6048
6812
|
sleepSeconds,
|
|
6049
6813
|
model: args.model ? String(args.model) : void 0,
|
|
@@ -6059,7 +6823,7 @@ import { defineCommand as defineCommand15 } from "citty";
|
|
|
6059
6823
|
var scanCommand = defineCommand15({
|
|
6060
6824
|
meta: {
|
|
6061
6825
|
name: "scan",
|
|
6062
|
-
description: "Scan the
|
|
6826
|
+
description: "Scan the repository and print the detected profile."
|
|
6063
6827
|
},
|
|
6064
6828
|
args: {
|
|
6065
6829
|
cwd: {
|
|
@@ -6077,7 +6841,7 @@ var scanCommand = defineCommand15({
|
|
|
6077
6841
|
});
|
|
6078
6842
|
|
|
6079
6843
|
// src/commands/status.ts
|
|
6080
|
-
import
|
|
6844
|
+
import path40 from "path";
|
|
6081
6845
|
import { defineCommand as defineCommand16 } from "citty";
|
|
6082
6846
|
function profileStatus(profile) {
|
|
6083
6847
|
if (!profile) return { origin: "none", evidence: [], profile: null };
|
|
@@ -6094,7 +6858,7 @@ function profileStatus(profile) {
|
|
|
6094
6858
|
var statusCommand = defineCommand16({
|
|
6095
6859
|
meta: {
|
|
6096
6860
|
name: "status",
|
|
6097
|
-
description: "Show
|
|
6861
|
+
description: "Show installed kit version, manifest, and optional profile."
|
|
6098
6862
|
},
|
|
6099
6863
|
args: {
|
|
6100
6864
|
cwd: {
|
|
@@ -6108,11 +6872,11 @@ var statusCommand = defineCommand16({
|
|
|
6108
6872
|
}
|
|
6109
6873
|
},
|
|
6110
6874
|
async run({ args }) {
|
|
6111
|
-
const rootDir =
|
|
6875
|
+
const rootDir = path40.resolve(args.cwd);
|
|
6112
6876
|
const [manifest, rawProfile, scan] = await Promise.all([
|
|
6113
6877
|
loadAgentKitManifest(rootDir),
|
|
6114
6878
|
readJson(
|
|
6115
|
-
|
|
6879
|
+
path40.join(rootDir, ".cursor", "agent-kit.config.json")
|
|
6116
6880
|
),
|
|
6117
6881
|
runScanner(rootDir)
|
|
6118
6882
|
]);
|
|
@@ -6171,7 +6935,7 @@ import { defineCommand as defineCommand17 } from "citty";
|
|
|
6171
6935
|
|
|
6172
6936
|
// src/lifecycle/check-updates.ts
|
|
6173
6937
|
import { execFile as execFile6 } from "child_process";
|
|
6174
|
-
import
|
|
6938
|
+
import path41 from "path";
|
|
6175
6939
|
import { promisify as promisify6 } from "util";
|
|
6176
6940
|
var execFileAsync5 = promisify6(execFile6);
|
|
6177
6941
|
var SEMVER_CORE = /^v?(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/i;
|
|
@@ -6262,11 +7026,11 @@ function intervalElapsed2(lastCheckedAt, intervalDays) {
|
|
|
6262
7026
|
return Date.now() - last >= ms;
|
|
6263
7027
|
}
|
|
6264
7028
|
async function loadContextConfig2(cwd) {
|
|
6265
|
-
const configPath =
|
|
7029
|
+
const configPath = path41.join(cwd, ".cursor", "context", "config.json");
|
|
6266
7030
|
return readJson(configPath);
|
|
6267
7031
|
}
|
|
6268
7032
|
async function stampLastCheckedAt(cwd) {
|
|
6269
|
-
const configPath =
|
|
7033
|
+
const configPath = path41.join(cwd, ".cursor", "context", "config.json");
|
|
6270
7034
|
const existing = await loadContextConfig2(cwd) ?? {};
|
|
6271
7035
|
const prev = existing.updateCheck && typeof existing.updateCheck === "object" ? { ...existing.updateCheck } : {};
|
|
6272
7036
|
existing.updateCheck = {
|
|
@@ -6410,7 +7174,7 @@ async function checkForUpdates(cwd, options = {}) {
|
|
|
6410
7174
|
var updateCommand = defineCommand17({
|
|
6411
7175
|
meta: {
|
|
6412
7176
|
name: "update",
|
|
6413
|
-
description: "Re-apply L0/packs/skills from the registry
|
|
7177
|
+
description: "Re-apply L0/packs/skills from the registry (never overwrites L3). --check = notify-only."
|
|
6414
7178
|
},
|
|
6415
7179
|
args: {
|
|
6416
7180
|
cwd: {
|
|
@@ -6442,6 +7206,17 @@ var updateCommand = defineCommand17({
|
|
|
6442
7206
|
description: "Seed the managed-hash ledger from current local overlay files before applying (factory/dogfood only; consumers should not use this)",
|
|
6443
7207
|
default: false
|
|
6444
7208
|
},
|
|
7209
|
+
yes: {
|
|
7210
|
+
type: "boolean",
|
|
7211
|
+
alias: "y",
|
|
7212
|
+
description: "Skip interactive prompts; use defaults (IDE-agnostic non-interactive mode)",
|
|
7213
|
+
default: false
|
|
7214
|
+
},
|
|
7215
|
+
"force-root": {
|
|
7216
|
+
type: "boolean",
|
|
7217
|
+
description: "Bypass the ambiguous-root guard (use with caution)",
|
|
7218
|
+
default: false
|
|
7219
|
+
},
|
|
6445
7220
|
...REGISTRY_CLI_ARGS
|
|
6446
7221
|
},
|
|
6447
7222
|
async run({ args }) {
|
|
@@ -6463,48 +7238,68 @@ var updateCommand = defineCommand17({
|
|
|
6463
7238
|
if (result.status === "error") process.exitCode = 2;
|
|
6464
7239
|
return;
|
|
6465
7240
|
}
|
|
6466
|
-
const
|
|
7241
|
+
const nonInteractive = args.yes || isNonInteractive();
|
|
7242
|
+
let projectRoot;
|
|
7243
|
+
try {
|
|
7244
|
+
projectRoot = await confirmProjectRoot(args.cwd, {
|
|
7245
|
+
nonInteractive,
|
|
7246
|
+
command: "update",
|
|
7247
|
+
forceRoot: args["force-root"]
|
|
7248
|
+
});
|
|
7249
|
+
} catch (err) {
|
|
7250
|
+
if (err instanceof RootRefusedError) {
|
|
7251
|
+
logger.error(err.message);
|
|
7252
|
+
process.exitCode = 1;
|
|
7253
|
+
return;
|
|
7254
|
+
}
|
|
7255
|
+
throw err;
|
|
7256
|
+
}
|
|
7257
|
+
const existing = await loadAgentKitManifest(projectRoot);
|
|
6467
7258
|
if (!existing) {
|
|
6468
7259
|
logger.warn("No .cursor/agent-kit.json \u2014 run agent-kit install first.");
|
|
6469
7260
|
return;
|
|
6470
7261
|
}
|
|
6471
7262
|
const registry = await resolveRegistryFromCli({
|
|
6472
|
-
cwd:
|
|
7263
|
+
cwd: projectRoot,
|
|
6473
7264
|
registry: args.registry,
|
|
6474
7265
|
url: args.url,
|
|
6475
7266
|
ref: args.ref,
|
|
6476
7267
|
refresh: args.refresh,
|
|
6477
7268
|
manifest: existing
|
|
6478
7269
|
});
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
|
|
6482
|
-
|
|
6483
|
-
|
|
6484
|
-
|
|
6485
|
-
|
|
6486
|
-
|
|
6487
|
-
|
|
6488
|
-
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
|
|
6492
|
-
next.
|
|
6493
|
-
|
|
6494
|
-
|
|
6495
|
-
|
|
6496
|
-
|
|
7270
|
+
try {
|
|
7271
|
+
logger.info(`Registry: ${registry.root} (${registry.source})`);
|
|
7272
|
+
const next = buildManifest({
|
|
7273
|
+
version: KIT_VERSION,
|
|
7274
|
+
profile: existing.profile,
|
|
7275
|
+
packs: existing.packs,
|
|
7276
|
+
skills: existing.skills,
|
|
7277
|
+
protected: existing.protected,
|
|
7278
|
+
personalization: existing.personalization,
|
|
7279
|
+
registryUrl: registry.url ?? existing.registry?.url,
|
|
7280
|
+
registryRef: registry.ref ?? existing.registry?.ref
|
|
7281
|
+
});
|
|
7282
|
+
if (existing.overrides?.length) next.overrides = existing.overrides;
|
|
7283
|
+
if (next.version === existing.version && existing.installedAt) {
|
|
7284
|
+
next.installedAt = existing.installedAt;
|
|
7285
|
+
}
|
|
7286
|
+
if (args["seed-overlay"]) {
|
|
7287
|
+
await seedManagedHashLedger(projectRoot);
|
|
7288
|
+
logger.info("Seeded managed-hash ledger from current local overlay files.");
|
|
7289
|
+
}
|
|
7290
|
+
const stats = await syncFromManifest(registry.root, projectRoot, next);
|
|
7291
|
+
await saveManifest(projectRoot, next);
|
|
7292
|
+
logApplyStats(stats);
|
|
7293
|
+
logger.success("Update complete (L3 protected paths left untouched).");
|
|
7294
|
+
} finally {
|
|
7295
|
+
await registry.unlock?.();
|
|
6497
7296
|
}
|
|
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
7297
|
}
|
|
6503
7298
|
});
|
|
6504
7299
|
|
|
6505
7300
|
// src/commands/validate.ts
|
|
6506
|
-
import { readFile as
|
|
6507
|
-
import
|
|
7301
|
+
import { readFile as readFile21 } from "fs/promises";
|
|
7302
|
+
import path42 from "path";
|
|
6508
7303
|
import { defineCommand as defineCommand18 } from "citty";
|
|
6509
7304
|
|
|
6510
7305
|
// src/invariants/plan-schema.ts
|
|
@@ -6551,9 +7346,9 @@ function validatePlanFrontmatterText(text) {
|
|
|
6551
7346
|
// src/commands/validate.ts
|
|
6552
7347
|
async function resolveEditedPath(cwd, explicit) {
|
|
6553
7348
|
if (explicit) {
|
|
6554
|
-
const filePath2 =
|
|
7349
|
+
const filePath2 = path42.resolve(cwd, explicit);
|
|
6555
7350
|
try {
|
|
6556
|
-
return { filePath: filePath2, content: await
|
|
7351
|
+
return { filePath: filePath2, content: await readFile21(filePath2, "utf8") };
|
|
6557
7352
|
} catch {
|
|
6558
7353
|
return null;
|
|
6559
7354
|
}
|
|
@@ -6561,9 +7356,9 @@ async function resolveEditedPath(cwd, explicit) {
|
|
|
6561
7356
|
const payload = await readStdinJson();
|
|
6562
7357
|
const rel = typeof payload.file_path === "string" && payload.file_path || typeof payload.path === "string" && payload.path || typeof payload.file === "string" && payload.file || "";
|
|
6563
7358
|
if (!rel) return null;
|
|
6564
|
-
const filePath =
|
|
7359
|
+
const filePath = path42.isAbsolute(rel) ? rel : path42.resolve(cwd, rel);
|
|
6565
7360
|
try {
|
|
6566
|
-
return { filePath, content: await
|
|
7361
|
+
return { filePath, content: await readFile21(filePath, "utf8") };
|
|
6567
7362
|
} catch {
|
|
6568
7363
|
return null;
|
|
6569
7364
|
}
|
|
@@ -6578,7 +7373,7 @@ function isPlanPath(filePath) {
|
|
|
6578
7373
|
var validateCommand = defineCommand18({
|
|
6579
7374
|
meta: {
|
|
6580
7375
|
name: "validate",
|
|
6581
|
-
description: "Advisory validators for HANDOFF / plan frontmatter (
|
|
7376
|
+
description: "Advisory validators for HANDOFF / plan frontmatter (hook adapter)."
|
|
6582
7377
|
},
|
|
6583
7378
|
subCommands: {
|
|
6584
7379
|
handoff: defineCommand18({
|
|
@@ -6591,10 +7386,10 @@ var validateCommand = defineCommand18({
|
|
|
6591
7386
|
async run({ args }) {
|
|
6592
7387
|
const cwd = typeof args.cwd === "string" ? args.cwd : process.cwd();
|
|
6593
7388
|
const fileArg = typeof args.file === "string" ? args.file : void 0;
|
|
6594
|
-
const filePath = fileArg ?
|
|
7389
|
+
const filePath = fileArg ? path42.resolve(cwd, fileArg) : path42.join(path42.resolve(cwd), ".cursor", "HANDOFF.md");
|
|
6595
7390
|
let content = "";
|
|
6596
7391
|
try {
|
|
6597
|
-
content = await
|
|
7392
|
+
content = await readFile21(filePath, "utf8");
|
|
6598
7393
|
} catch {
|
|
6599
7394
|
console.log(JSON.stringify({ ok: true, warnings: [], note: "file missing" }));
|
|
6600
7395
|
return;
|
|
@@ -6618,10 +7413,10 @@ var validateCommand = defineCommand18({
|
|
|
6618
7413
|
process.exitCode = 2;
|
|
6619
7414
|
return;
|
|
6620
7415
|
}
|
|
6621
|
-
const filePath =
|
|
7416
|
+
const filePath = path42.resolve(cwd, fileArg);
|
|
6622
7417
|
let content = "";
|
|
6623
7418
|
try {
|
|
6624
|
-
content = await
|
|
7419
|
+
content = await readFile21(filePath, "utf8");
|
|
6625
7420
|
} catch {
|
|
6626
7421
|
console.log(JSON.stringify({ ok: true, warnings: [], note: "file missing" }));
|
|
6627
7422
|
return;
|
|
@@ -6640,7 +7435,7 @@ var validateCommand = defineCommand18({
|
|
|
6640
7435
|
},
|
|
6641
7436
|
async run({ args }) {
|
|
6642
7437
|
const cwd = typeof args.cwd === "string" ? args.cwd : process.cwd();
|
|
6643
|
-
const resolved = await resolveEditedPath(
|
|
7438
|
+
const resolved = await resolveEditedPath(path42.resolve(cwd));
|
|
6644
7439
|
if (!resolved) {
|
|
6645
7440
|
console.log(JSON.stringify({}));
|
|
6646
7441
|
return;
|
|
@@ -6682,11 +7477,204 @@ var validateCommand = defineCommand18({
|
|
|
6682
7477
|
}
|
|
6683
7478
|
});
|
|
6684
7479
|
|
|
7480
|
+
// src/welcome/help-groups.ts
|
|
7481
|
+
import { bold, gray as gray3, underline } from "kolorist";
|
|
7482
|
+
|
|
7483
|
+
// src/welcome/screen.ts
|
|
7484
|
+
import {
|
|
7485
|
+
blue as blue2,
|
|
7486
|
+
cyan as cyan3,
|
|
7487
|
+
gray as gray2,
|
|
7488
|
+
options as koloristOptions,
|
|
7489
|
+
lightCyan,
|
|
7490
|
+
trueColor,
|
|
7491
|
+
white as white2
|
|
7492
|
+
} from "kolorist";
|
|
7493
|
+
var HELMET_OUTLINE = "#e2e8f0";
|
|
7494
|
+
var KOLORIST_TRUECOLOR = 3;
|
|
7495
|
+
var HELMET_ASCII = [
|
|
7496
|
+
" ____",
|
|
7497
|
+
" .-' '-.",
|
|
7498
|
+
" / .--. \\",
|
|
7499
|
+
" | / \\ |",
|
|
7500
|
+
" | | () | |",
|
|
7501
|
+
" | \\ / |",
|
|
7502
|
+
" \\ '--' /",
|
|
7503
|
+
" '-.____.-'",
|
|
7504
|
+
" /_/ \\_\\"
|
|
7505
|
+
];
|
|
7506
|
+
function hexToRgb(hex) {
|
|
7507
|
+
const m = /^#([0-9a-fA-F]{6})$/.exec(hex.trim());
|
|
7508
|
+
const hexBody = m?.[1];
|
|
7509
|
+
if (!hexBody) throw new Error(`invalid hex color: ${hex}`);
|
|
7510
|
+
const n = Number.parseInt(hexBody, 16);
|
|
7511
|
+
return [n >> 16 & 255, n >> 8 & 255, n & 255];
|
|
7512
|
+
}
|
|
7513
|
+
function outlineAnsi(line) {
|
|
7514
|
+
const [r, g, b] = hexToRgb(HELMET_OUTLINE);
|
|
7515
|
+
return trueColor(r, g, b)(line);
|
|
7516
|
+
}
|
|
7517
|
+
function withKoloristColor(fn) {
|
|
7518
|
+
const prevEnabled = koloristOptions.enabled;
|
|
7519
|
+
const prevLevel = koloristOptions.supportLevel;
|
|
7520
|
+
koloristOptions.enabled = true;
|
|
7521
|
+
koloristOptions.supportLevel = KOLORIST_TRUECOLOR;
|
|
7522
|
+
try {
|
|
7523
|
+
return fn();
|
|
7524
|
+
} finally {
|
|
7525
|
+
koloristOptions.enabled = prevEnabled;
|
|
7526
|
+
koloristOptions.supportLevel = prevLevel;
|
|
7527
|
+
}
|
|
7528
|
+
}
|
|
7529
|
+
function hasCliSubcommand(rawArgs) {
|
|
7530
|
+
return Boolean(rawArgs?.some((arg) => !arg.startsWith("-")));
|
|
7531
|
+
}
|
|
7532
|
+
function shouldUseWelcomeColor(opts = {}) {
|
|
7533
|
+
if (opts.color === false) return false;
|
|
7534
|
+
if (process.env.NO_COLOR) return false;
|
|
7535
|
+
if (process.env.NODE_DISABLE_COLORS) return false;
|
|
7536
|
+
if (process.env.FORCE_COLOR === "0") return false;
|
|
7537
|
+
if (process.env.CI != null && process.env.CI !== "") return false;
|
|
7538
|
+
if (opts.color === true) return true;
|
|
7539
|
+
const tty = opts.stdoutIsTTY ?? Boolean(process.stdout.isTTY);
|
|
7540
|
+
return tty;
|
|
7541
|
+
}
|
|
7542
|
+
function renderHelmetAscii(color) {
|
|
7543
|
+
if (!color) return HELMET_ASCII.join("\n");
|
|
7544
|
+
return withKoloristColor(
|
|
7545
|
+
() => HELMET_ASCII.map((line, i) => {
|
|
7546
|
+
if (i <= 1 || i >= HELMET_ASCII.length - 1) return outlineAnsi(line);
|
|
7547
|
+
if (i === 4) return lightCyan(line);
|
|
7548
|
+
return blue2(line);
|
|
7549
|
+
}).join("\n")
|
|
7550
|
+
);
|
|
7551
|
+
}
|
|
7552
|
+
var WELCOME_UTILITY_HINTS = [
|
|
7553
|
+
{ cmd: "agent-kit --help", hint: "grouped commands" },
|
|
7554
|
+
{ cmd: "agent-kit doctor", hint: "repository readiness" },
|
|
7555
|
+
{ cmd: "agent-kit status", hint: "installed kit version" },
|
|
7556
|
+
{ cmd: "agent-kit dashboard", hint: "Mission Control panel" },
|
|
7557
|
+
{ cmd: "agent-kit init", hint: "guided install entry" }
|
|
7558
|
+
];
|
|
7559
|
+
function renderWelcomeScreen(opts = {}) {
|
|
7560
|
+
const version = opts.version ?? KIT_VERSION;
|
|
7561
|
+
const color = shouldUseWelcomeColor(opts);
|
|
7562
|
+
const title = color ? white2("Mission Kit") : "Mission Kit";
|
|
7563
|
+
const product = color ? cyan3("agent-kit") : "agent-kit";
|
|
7564
|
+
const muted = (s) => color ? gray2(s) : s;
|
|
7565
|
+
const lines = [
|
|
7566
|
+
renderHelmetAscii(color),
|
|
7567
|
+
"",
|
|
7568
|
+
`${title} \xB7 ${product} v${version}`,
|
|
7569
|
+
muted("HITL framework for AI-assisted IDEs \xB7 @dadado/agent-kit-cli"),
|
|
7570
|
+
"",
|
|
7571
|
+
muted("Try:"),
|
|
7572
|
+
...(() => {
|
|
7573
|
+
const width = Math.max(...WELCOME_UTILITY_HINTS.map(({ cmd }) => cmd.length));
|
|
7574
|
+
return WELCOME_UTILITY_HINTS.map(({ cmd, hint }) => {
|
|
7575
|
+
const pad = " ".repeat(width - cmd.length + 2);
|
|
7576
|
+
const left = color ? cyan3(` ${cmd}`) : ` ${cmd}`;
|
|
7577
|
+
return `${left}${pad}${muted(hint)}`;
|
|
7578
|
+
});
|
|
7579
|
+
})(),
|
|
7580
|
+
"",
|
|
7581
|
+
muted(
|
|
7582
|
+
"Chat HITL (start-project, git-staging/prod, run-plan-all) stays in Cursor slash commands."
|
|
7583
|
+
)
|
|
7584
|
+
];
|
|
7585
|
+
return `${lines.join("\n")}
|
|
7586
|
+
`;
|
|
7587
|
+
}
|
|
7588
|
+
function printWelcomeScreen(opts = {}) {
|
|
7589
|
+
process.stdout.write(renderWelcomeScreen(opts));
|
|
7590
|
+
}
|
|
7591
|
+
|
|
7592
|
+
// src/welcome/help-groups.ts
|
|
7593
|
+
var CLI_HELP_GROUPS = [
|
|
7594
|
+
{
|
|
7595
|
+
id: "setup",
|
|
7596
|
+
title: "SETUP",
|
|
7597
|
+
commands: ["init", "install", "doctor", "status", "update", "add", "scan"]
|
|
7598
|
+
},
|
|
7599
|
+
{
|
|
7600
|
+
id: "mission",
|
|
7601
|
+
title: "MISSION",
|
|
7602
|
+
commands: ["handoff", "run-plan"]
|
|
7603
|
+
},
|
|
7604
|
+
{
|
|
7605
|
+
id: "dashboard",
|
|
7606
|
+
title: "DASHBOARD",
|
|
7607
|
+
commands: ["dashboard", "dashboard-broadcast", "monitors"]
|
|
7608
|
+
},
|
|
7609
|
+
{
|
|
7610
|
+
id: "integrity",
|
|
7611
|
+
title: "INTEGRITY",
|
|
7612
|
+
commands: ["validate", "guard", "hook", "cursor-awareness", "diff", "contribute"]
|
|
7613
|
+
}
|
|
7614
|
+
];
|
|
7615
|
+
async function resolveCommandMeta(meta) {
|
|
7616
|
+
if (meta == null) return void 0;
|
|
7617
|
+
const value = typeof meta === "function" ? await meta() : meta;
|
|
7618
|
+
return await Promise.resolve(value);
|
|
7619
|
+
}
|
|
7620
|
+
async function resolveSubMeta(subCommands) {
|
|
7621
|
+
const out = /* @__PURE__ */ new Map();
|
|
7622
|
+
for (const [name, raw] of Object.entries(subCommands)) {
|
|
7623
|
+
const resolved = typeof raw === "function" ? await raw() : raw;
|
|
7624
|
+
const def = resolved;
|
|
7625
|
+
const meta = await resolveCommandMeta(def?.meta);
|
|
7626
|
+
out.set(name, meta?.description ?? "");
|
|
7627
|
+
}
|
|
7628
|
+
return out;
|
|
7629
|
+
}
|
|
7630
|
+
async function renderGroupedRootHelp(cmd) {
|
|
7631
|
+
const color = shouldUseWelcomeColor();
|
|
7632
|
+
const u = (s) => color ? underline(bold(s)) : s;
|
|
7633
|
+
const g = (s) => color ? gray3(s) : s;
|
|
7634
|
+
const meta = await resolveCommandMeta(cmd.meta);
|
|
7635
|
+
const name = meta?.name ?? "agent-kit";
|
|
7636
|
+
const version = meta?.version ?? KIT_VERSION;
|
|
7637
|
+
const description = meta?.description ?? "HITL framework for AI-assisted IDEs (Mission Kit family)";
|
|
7638
|
+
const subCommands = await (typeof cmd.subCommands === "function" ? cmd.subCommands() : cmd.subCommands);
|
|
7639
|
+
const descriptions = subCommands ? await resolveSubMeta(subCommands) : /* @__PURE__ */ new Map();
|
|
7640
|
+
const placed = new Set(CLI_HELP_GROUPS.flatMap((grp) => grp.commands));
|
|
7641
|
+
const leftover = [...descriptions.keys()].filter((c) => !placed.has(c)).sort();
|
|
7642
|
+
const groups = [...CLI_HELP_GROUPS];
|
|
7643
|
+
if (leftover.length > 0) {
|
|
7644
|
+
groups.push({ id: "other", title: "OTHER", commands: leftover });
|
|
7645
|
+
}
|
|
7646
|
+
const lines = [
|
|
7647
|
+
g(`${description} (${name} v${version})`),
|
|
7648
|
+
"",
|
|
7649
|
+
`${u("USAGE")} \`${name} <command> [OPTIONS]\``,
|
|
7650
|
+
""
|
|
7651
|
+
];
|
|
7652
|
+
for (const group of groups) {
|
|
7653
|
+
const rows = group.commands.filter((c) => descriptions.has(c));
|
|
7654
|
+
if (rows.length === 0) continue;
|
|
7655
|
+
lines.push(u(group.title), "");
|
|
7656
|
+
const width = Math.max(...rows.map((c) => c.length));
|
|
7657
|
+
for (const c of rows) {
|
|
7658
|
+
const pad = " ".repeat(width - c.length + 2);
|
|
7659
|
+
lines.push(` ${c}${pad}${descriptions.get(c) ?? ""}`);
|
|
7660
|
+
}
|
|
7661
|
+
lines.push("");
|
|
7662
|
+
}
|
|
7663
|
+
lines.push(
|
|
7664
|
+
g(`Use \`${name} <command> --help\` for more information about a command.`),
|
|
7665
|
+
g(
|
|
7666
|
+
"Chat-only HITL (start-project, backlog, git-staging/prod, run-plan-all) is not a CLI surface."
|
|
7667
|
+
),
|
|
7668
|
+
""
|
|
7669
|
+
);
|
|
7670
|
+
return lines.join("\n");
|
|
7671
|
+
}
|
|
7672
|
+
|
|
6685
7673
|
// src/index.ts
|
|
6686
7674
|
var main = defineCommand19({
|
|
6687
7675
|
meta: {
|
|
6688
7676
|
name: "agent-kit",
|
|
6689
|
-
description: "HITL framework for AI-assisted IDEs",
|
|
7677
|
+
description: "HITL framework for AI-assisted IDEs (Mission Kit family)",
|
|
6690
7678
|
version: KIT_VERSION
|
|
6691
7679
|
},
|
|
6692
7680
|
subCommands: {
|
|
@@ -6708,6 +7696,19 @@ var main = defineCommand19({
|
|
|
6708
7696
|
guard: guardCommand,
|
|
6709
7697
|
monitors: monitorsCommand,
|
|
6710
7698
|
validate: validateCommand
|
|
7699
|
+
},
|
|
7700
|
+
async run({ rawArgs }) {
|
|
7701
|
+
if (hasCliSubcommand(rawArgs)) return;
|
|
7702
|
+
printWelcomeScreen();
|
|
7703
|
+
}
|
|
7704
|
+
});
|
|
7705
|
+
runMain(main, {
|
|
7706
|
+
showUsage: async (cmd, parent) => {
|
|
7707
|
+
if (!parent) {
|
|
7708
|
+
process.stdout.write(`${await renderGroupedRootHelp(cmd)}
|
|
7709
|
+
`);
|
|
7710
|
+
return;
|
|
7711
|
+
}
|
|
7712
|
+
await showUsage(cmd, parent);
|
|
6711
7713
|
}
|
|
6712
7714
|
});
|
|
6713
|
-
runMain(main);
|