@dadado/agent-kit-cli 5.15.1 → 5.17.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/dashboard/dashboard-data.mjs +69 -65
- package/dashboard/lib/guards.d.mts +5 -0
- package/dashboard/lib/guards.mjs +31 -0
- package/dashboard/lib/identity-probe.mjs +56 -0
- package/dashboard/lib/pipeline-cache.mjs +58 -0
- package/dashboard/lib/processes.mjs +71 -0
- package/dashboard/serve.mjs +43 -1
- package/dashboard/start-broadcast.mjs +2 -12
- package/dashboard/start.mjs +2 -13
- package/dist/add-HTYIFKMN.js +150 -0
- package/dist/chunk-2IPXQFJE.js +253 -0
- package/dist/chunk-2RDRMGD3.js +82 -0
- package/dist/chunk-2YU2H7I6.js +42 -0
- package/dist/chunk-4IWG5CVE.js +214 -0
- package/dist/chunk-5VCOWTTP.js +6 -0
- package/dist/chunk-77V75A7O.js +39 -0
- package/dist/chunk-7HDQEK6T.js +26 -0
- package/dist/chunk-AW74INUW.js +1456 -0
- package/dist/chunk-B73KQ5KX.js +972 -0
- package/dist/chunk-CBCW5R5J.js +484 -0
- package/dist/chunk-DE3LQFSJ.js +18 -0
- package/dist/chunk-DQA434Y4.js +106 -0
- package/dist/chunk-F4VMYR7U.js +318 -0
- package/dist/chunk-F6C3GFKV.js +57 -0
- package/dist/chunk-HFUH3HVS.js +282 -0
- package/dist/chunk-HS7LVVLO.js +74 -0
- package/dist/chunk-IHROBTNM.js +135 -0
- package/dist/chunk-KD2MYBB6.js +213 -0
- package/dist/chunk-MAPOQEA2.js +43 -0
- package/dist/chunk-N3HTJVBR.js +341 -0
- package/dist/chunk-NQ7IQ5XT.js +26 -0
- package/dist/chunk-OUQZC744.js +1315 -0
- package/dist/chunk-PCCQUWE5.js +817 -0
- package/dist/chunk-PNFF6OU3.js +273 -0
- package/dist/chunk-PNG7LO2F.js +53 -0
- package/dist/chunk-QTON2GQX.js +203 -0
- package/dist/chunk-S6GMFDKV.js +50 -0
- package/dist/chunk-SMGFN3Z4.js +503 -0
- package/dist/chunk-STGFBAYT.js +641 -0
- package/dist/chunk-TFZ2JUXH.js +1081 -0
- package/dist/chunk-VY7MTPJ2.js +205 -0
- package/dist/chunk-W24CMQ32.js +525 -0
- package/dist/chunk-WW3YZKHW.js +165 -0
- package/dist/chunk-ZB43YMHW.js +20 -0
- package/dist/chunk-ZCTSUQRR.js +874 -0
- package/dist/chunk-ZXKZBTXE.js +17 -0
- package/dist/contribute-C6AW2P6J.js +367 -0
- package/dist/cursor-awareness-KTL2RGKD.js +77 -0
- package/dist/dashboard-YSNUGBFY.js +21 -0
- package/dist/dashboard-broadcast-M4CMW7RX.js +119 -0
- package/dist/diff-VHNR62SI.js +74 -0
- package/dist/doctor-PET2HRRJ.js +236 -0
- package/dist/dogfood-KKFVRWT6.js +366 -0
- package/dist/guard-EHXF76ID.js +530 -0
- package/dist/handoff-3UI6VKVG.js +302 -0
- package/dist/hook-DYIMKJYL.js +1074 -0
- package/dist/index.js +62 -14068
- package/dist/init-Q4LBM6KT.js +127 -0
- package/dist/install-RHOS3OYZ.js +34 -0
- package/dist/mission-control-LJACEVVN.js +89 -0
- package/dist/monitors-QV2IS3MP.js +176 -0
- package/dist/plan-index-JMC7Z4DW.js +44 -0
- package/dist/public-inbound-radar-WP3XF7GS.js +68 -0
- package/dist/remote-issues-SZ4HDQL5.js +44 -0
- package/dist/run-I3BFGULT.js +243 -0
- package/dist/run-plan-FO7CL4M3.js +118 -0
- package/dist/scan-NI2SWJ2E.js +33 -0
- package/dist/setup-global-M42GEODI.js +340 -0
- package/dist/status-XPVQVQMX.js +150 -0
- package/dist/update-FJ2EBZVO.js +283 -0
- package/dist/validate-V2CW7J4B.js +201 -0
- package/package.json +3 -3
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
import {
|
|
2
|
+
contextConfigPath,
|
|
3
|
+
intervalElapsed,
|
|
4
|
+
loadContextConfig
|
|
5
|
+
} from "./chunk-NQ7IQ5XT.js";
|
|
6
|
+
import {
|
|
7
|
+
fileExists,
|
|
8
|
+
writeJson
|
|
9
|
+
} from "./chunk-77V75A7O.js";
|
|
10
|
+
|
|
11
|
+
// src/lifecycle/cursor-update-awareness.ts
|
|
12
|
+
import { access, readFile } from "fs/promises";
|
|
13
|
+
import path from "path";
|
|
14
|
+
var DEFAULT_CURSOR_CHANGELOG_URL = "https://cursor.com/changelog";
|
|
15
|
+
var CHANGELOG_FETCH_TIMEOUT_MS = 15e3;
|
|
16
|
+
var CURSOR_VERSION_MAJOR_MAX = 20;
|
|
17
|
+
var INVENTORY_REL = path.join("docs", "cursor-native-audit.md");
|
|
18
|
+
var FEATURES_REL = path.join("docs", "cursor-3-features.md");
|
|
19
|
+
async function resolveInventoryRoot(cwd) {
|
|
20
|
+
let dir = path.resolve(cwd);
|
|
21
|
+
for (; ; ) {
|
|
22
|
+
try {
|
|
23
|
+
await access(path.join(dir, INVENTORY_REL));
|
|
24
|
+
return dir;
|
|
25
|
+
} catch {
|
|
26
|
+
}
|
|
27
|
+
if (await fileExists(path.join(dir, ".git"))) break;
|
|
28
|
+
const parent = path.dirname(dir);
|
|
29
|
+
if (parent === dir) break;
|
|
30
|
+
dir = parent;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
function missingInventoryMessage() {
|
|
35
|
+
return `Missing inventory at ${INVENTORY_REL}. Run from the kit/repo root that contains that file, or pass --cwd <path>.`;
|
|
36
|
+
}
|
|
37
|
+
var DEFAULT_PREFS = {
|
|
38
|
+
enabled: false,
|
|
39
|
+
intervalDays: 7,
|
|
40
|
+
lastCheckedAt: null,
|
|
41
|
+
lastSeenCursorVersion: null,
|
|
42
|
+
changelogUrl: DEFAULT_CURSOR_CHANGELOG_URL
|
|
43
|
+
};
|
|
44
|
+
var CONVEYOR_HINT = "Confirmed gaps: Ask HITL then `/backlog-add` or `/dogfood` (lane-aware). Never auto Field Reports. Native-audit version prose owned by parked Marketplace plan.";
|
|
45
|
+
function readCursorUpdateCheckPrefs(config) {
|
|
46
|
+
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
47
|
+
return { ...DEFAULT_PREFS };
|
|
48
|
+
}
|
|
49
|
+
const raw = config.cursorUpdateCheck;
|
|
50
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
51
|
+
return { ...DEFAULT_PREFS };
|
|
52
|
+
}
|
|
53
|
+
const uc = raw;
|
|
54
|
+
const intervalDays = typeof uc.intervalDays === "number" && Number.isInteger(uc.intervalDays) && uc.intervalDays >= 1 ? uc.intervalDays : DEFAULT_PREFS.intervalDays;
|
|
55
|
+
const changelogUrl = typeof uc.changelogUrl === "string" && uc.changelogUrl.startsWith("https://") ? uc.changelogUrl : DEFAULT_PREFS.changelogUrl;
|
|
56
|
+
return {
|
|
57
|
+
enabled: uc.enabled === true,
|
|
58
|
+
intervalDays,
|
|
59
|
+
lastCheckedAt: typeof uc.lastCheckedAt === "string" ? uc.lastCheckedAt : null,
|
|
60
|
+
lastSeenCursorVersion: typeof uc.lastSeenCursorVersion === "string" ? uc.lastSeenCursorVersion : null,
|
|
61
|
+
changelogUrl
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
async function stampCursorUpdateCheck(cwd, patch) {
|
|
65
|
+
const existing = await loadContextConfig(cwd) ?? {};
|
|
66
|
+
const prev = existing.cursorUpdateCheck && typeof existing.cursorUpdateCheck === "object" ? { ...existing.cursorUpdateCheck } : {};
|
|
67
|
+
const nextSeen = patch.lastSeenCursorVersion !== void 0 ? patch.lastSeenCursorVersion : void 0;
|
|
68
|
+
if (nextSeen !== void 0 && nextSeen !== null && !isPlausibleCursorVersion(nextSeen)) {
|
|
69
|
+
existing.cursorUpdateCheck = {
|
|
70
|
+
...DEFAULT_PREFS,
|
|
71
|
+
...prev,
|
|
72
|
+
lastCheckedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
73
|
+
};
|
|
74
|
+
await writeJson(contextConfigPath(cwd), existing);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
existing.cursorUpdateCheck = {
|
|
78
|
+
...DEFAULT_PREFS,
|
|
79
|
+
...prev,
|
|
80
|
+
lastCheckedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
81
|
+
...nextSeen !== void 0 ? { lastSeenCursorVersion: nextSeen } : {}
|
|
82
|
+
};
|
|
83
|
+
await writeJson(contextConfigPath(cwd), existing);
|
|
84
|
+
}
|
|
85
|
+
function isPlausibleCursorVersion(version) {
|
|
86
|
+
const parts = String(version).split(".");
|
|
87
|
+
if (parts.length < 2 || parts.length > 3) return false;
|
|
88
|
+
const major = Number(parts[0]);
|
|
89
|
+
const minor = Number(parts[1]);
|
|
90
|
+
const patch = parts.length === 3 ? Number(parts[2]) : 0;
|
|
91
|
+
if ([major, minor, patch].some((n) => Number.isNaN(n) || n < 0)) return false;
|
|
92
|
+
if (major >= 2e3 || major > CURSOR_VERSION_MAJOR_MAX) return false;
|
|
93
|
+
if (minor > 999 || patch > 999) return false;
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
function extractLatestCursorVersion(changelogText) {
|
|
97
|
+
const text = String(changelogText ?? "");
|
|
98
|
+
const candidates = [];
|
|
99
|
+
const push = (major, minor, patch) => {
|
|
100
|
+
const version = patch !== void 0 ? `${major}.${minor}.${patch}` : `${major}.${minor}`;
|
|
101
|
+
if (isPlausibleCursorVersion(version)) candidates.push(version);
|
|
102
|
+
};
|
|
103
|
+
for (const m of text.matchAll(/<span class="label">(\d+)\.(\d+)(?:\.(\d+))?<\/span>/gi)) {
|
|
104
|
+
push(m[1] ?? "", m[2] ?? "", m[3]);
|
|
105
|
+
}
|
|
106
|
+
for (const m of text.matchAll(
|
|
107
|
+
/\b(\d+)\.(\d+)(?:\.(\d+))?\b(?=[^\n]{0,48}(?:Changelog|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec))/gi
|
|
108
|
+
)) {
|
|
109
|
+
push(m[1] ?? "", m[2] ?? "", m[3]);
|
|
110
|
+
}
|
|
111
|
+
if (candidates.length === 0) {
|
|
112
|
+
for (const m of text.matchAll(/\b(\d+)\.(\d+)(?:\.(\d+))?\b/g)) {
|
|
113
|
+
push(m[1] ?? "", m[2] ?? "", m[3]);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
let best = null;
|
|
117
|
+
let bestScore = -1;
|
|
118
|
+
for (const version of candidates) {
|
|
119
|
+
const parts = version.split(".").map(Number);
|
|
120
|
+
const score = (parts[0] ?? 0) * 1e6 + (parts[1] ?? 0) * 1e3 + (parts[2] ?? 0);
|
|
121
|
+
if (score > bestScore) {
|
|
122
|
+
bestScore = score;
|
|
123
|
+
best = version;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return best;
|
|
127
|
+
}
|
|
128
|
+
function compareCursorVersion(a, b) {
|
|
129
|
+
const pa = a.split(".").map((p) => Number(p));
|
|
130
|
+
const pb = b.split(".").map((p) => Number(p));
|
|
131
|
+
const len = Math.max(pa.length, pb.length);
|
|
132
|
+
for (let i = 0; i < len; i++) {
|
|
133
|
+
const da = pa[i] ?? 0;
|
|
134
|
+
const db = pb[i] ?? 0;
|
|
135
|
+
if (Number.isNaN(da) || Number.isNaN(db)) return 0;
|
|
136
|
+
if (da !== db) return da < db ? -1 : 1;
|
|
137
|
+
}
|
|
138
|
+
return 0;
|
|
139
|
+
}
|
|
140
|
+
function parseInventoryRefreshed(markdown) {
|
|
141
|
+
const m = markdown.match(/last refreshed\s+\*\*(\d{4}-\d{2}-\d{2})\*\*/i);
|
|
142
|
+
return m?.[1] ?? null;
|
|
143
|
+
}
|
|
144
|
+
function parseOpenActionIds(markdown) {
|
|
145
|
+
const ids = [];
|
|
146
|
+
for (const line of markdown.split("\n")) {
|
|
147
|
+
const m = line.match(/^\|\s*([A-Z]\d+)\s*\|\s*Open\s*\|/i);
|
|
148
|
+
if (m?.[1]) ids.push(m[1]);
|
|
149
|
+
}
|
|
150
|
+
return ids;
|
|
151
|
+
}
|
|
152
|
+
function parseFeatureMapNames(markdown) {
|
|
153
|
+
const names = [];
|
|
154
|
+
let inTable = false;
|
|
155
|
+
for (const line of markdown.split("\n")) {
|
|
156
|
+
if (line.startsWith("| Feature |")) {
|
|
157
|
+
inTable = true;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
if (inTable) {
|
|
161
|
+
if (!line.startsWith("|")) break;
|
|
162
|
+
if (line.includes("---")) continue;
|
|
163
|
+
const cells = line.split("|").map((c) => c.trim());
|
|
164
|
+
const name = cells[1];
|
|
165
|
+
if (name && name.toLowerCase() !== "feature") names.push(name);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return names;
|
|
169
|
+
}
|
|
170
|
+
var HEADING_NOISE = /* @__PURE__ */ new Set([
|
|
171
|
+
"changelog",
|
|
172
|
+
"improvements",
|
|
173
|
+
"bug fixes",
|
|
174
|
+
"get started",
|
|
175
|
+
"settings",
|
|
176
|
+
"powered by",
|
|
177
|
+
"powered by cloud agents",
|
|
178
|
+
"shared context",
|
|
179
|
+
"desktop improvements",
|
|
180
|
+
"desktop bug fixes",
|
|
181
|
+
"agents window improvements",
|
|
182
|
+
"agents window bug fixes",
|
|
183
|
+
"what's new in cursor",
|
|
184
|
+
"latest updates",
|
|
185
|
+
"release notes"
|
|
186
|
+
]);
|
|
187
|
+
var MAX_FEATURE_KEYWORD_GAPS = 12;
|
|
188
|
+
function visibleTextFromHtml(html) {
|
|
189
|
+
return html.replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<svg[\s\S]*?<\/svg>/gi, " ").replace(/<[^>]+>/g, " ").replace(/ /gi, " ").replace(/&/gi, "&").replace(/&#?\w+;/g, " ").replace(/\s+/g, " ").trim();
|
|
190
|
+
}
|
|
191
|
+
function normalizeFeatureKeyword(raw) {
|
|
192
|
+
let value = raw.replace(/^#+\s*/, "").replace(/\s+/g, " ").trim();
|
|
193
|
+
value = value.replace(/[.,:;]+$/g, "");
|
|
194
|
+
value = value.replace(/\s+(improvements|bug fixes)$/i, "").trim();
|
|
195
|
+
if (!value) return null;
|
|
196
|
+
if (value.startsWith("/")) {
|
|
197
|
+
const cmd = value.match(/^\/[a-z][a-z0-9-]{1,32}$/i);
|
|
198
|
+
return cmd?.[0] ? cmd[0].toLowerCase() : null;
|
|
199
|
+
}
|
|
200
|
+
const lower = value.toLowerCase();
|
|
201
|
+
if (HEADING_NOISE.has(lower)) return null;
|
|
202
|
+
if (/^\d+(\.\d+){1,2}$/.test(value)) return null;
|
|
203
|
+
if (value.length < 4 || value.length > 80) return null;
|
|
204
|
+
return value;
|
|
205
|
+
}
|
|
206
|
+
function slugifyFeatureKeyword(name) {
|
|
207
|
+
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
|
|
208
|
+
}
|
|
209
|
+
function extractChangelogFeatureKeywords(changelogText) {
|
|
210
|
+
const text = String(changelogText ?? "");
|
|
211
|
+
const buckets = {
|
|
212
|
+
slash: [],
|
|
213
|
+
h1: [],
|
|
214
|
+
h2: [],
|
|
215
|
+
h3: []
|
|
216
|
+
};
|
|
217
|
+
const seen = /* @__PURE__ */ new Set();
|
|
218
|
+
const push = (raw, bucket) => {
|
|
219
|
+
const keyword = normalizeFeatureKeyword(raw);
|
|
220
|
+
if (!keyword) return;
|
|
221
|
+
const key = keyword.toLowerCase();
|
|
222
|
+
if (seen.has(key)) return;
|
|
223
|
+
seen.add(key);
|
|
224
|
+
buckets[bucket].push(keyword);
|
|
225
|
+
};
|
|
226
|
+
for (const match of text.matchAll(/<h1[^>]*>([\s\S]*?)<\/h1>/gi)) {
|
|
227
|
+
push(visibleTextFromHtml(match[1] ?? ""), "h1");
|
|
228
|
+
}
|
|
229
|
+
for (const match of text.matchAll(/<h2[^>]*>([\s\S]*?)<\/h2>/gi)) {
|
|
230
|
+
push(visibleTextFromHtml(match[1] ?? ""), "h2");
|
|
231
|
+
}
|
|
232
|
+
for (const match of text.matchAll(/<h3[^>]*>([\s\S]*?)<\/h3>/gi)) {
|
|
233
|
+
push(visibleTextFromHtml(match[1] ?? ""), "h3");
|
|
234
|
+
}
|
|
235
|
+
for (const match of text.matchAll(/^#\s+(.+)$/gm)) {
|
|
236
|
+
push(match[1] ?? "", "h1");
|
|
237
|
+
}
|
|
238
|
+
for (const match of text.matchAll(/^##\s+(.+)$/gm)) {
|
|
239
|
+
push(match[1] ?? "", "h2");
|
|
240
|
+
}
|
|
241
|
+
for (const match of text.matchAll(/^###\s+(.+)$/gm)) {
|
|
242
|
+
push(match[1] ?? "", "h3");
|
|
243
|
+
}
|
|
244
|
+
for (const match of text.matchAll(/(^|[\s>`(])(\/[a-z][a-z0-9-]{1,32})(?![/\w-])/gi)) {
|
|
245
|
+
push(match[2] ?? "", "slash");
|
|
246
|
+
}
|
|
247
|
+
return [...buckets.slash, ...buckets.h1, ...buckets.h2, ...buckets.h3];
|
|
248
|
+
}
|
|
249
|
+
function findMissingInventoryKeywords(keywords, haystack) {
|
|
250
|
+
const lower = String(haystack ?? "").toLowerCase();
|
|
251
|
+
return keywords.filter((keyword) => {
|
|
252
|
+
const needle = keyword.toLowerCase();
|
|
253
|
+
if (lower.includes(needle)) return false;
|
|
254
|
+
if (needle.startsWith("/") && lower.includes(needle.slice(1))) return false;
|
|
255
|
+
return true;
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
async function defaultFetchText(url) {
|
|
259
|
+
if (!url.startsWith("https://")) {
|
|
260
|
+
throw new Error(`Refusing non-HTTPS changelog URL: ${url}`);
|
|
261
|
+
}
|
|
262
|
+
const res = await fetch(url, {
|
|
263
|
+
headers: { Accept: "text/html,text/plain;q=0.9,*/*;q=0.8" },
|
|
264
|
+
redirect: "follow",
|
|
265
|
+
signal: AbortSignal.timeout(CHANGELOG_FETCH_TIMEOUT_MS)
|
|
266
|
+
});
|
|
267
|
+
if (!res.ok) {
|
|
268
|
+
throw new Error(`Changelog fetch failed: HTTP ${res.status}`);
|
|
269
|
+
}
|
|
270
|
+
return res.text();
|
|
271
|
+
}
|
|
272
|
+
function baseResult(partial) {
|
|
273
|
+
return {
|
|
274
|
+
...partial,
|
|
275
|
+
applyRecommended: false,
|
|
276
|
+
fieldReportRecommended: false,
|
|
277
|
+
conveyorHint: CONVEYOR_HINT
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
async function checkCursorUpdateAwareness(cwd, options = {}) {
|
|
281
|
+
const inventoryRoot = await resolveInventoryRoot(cwd);
|
|
282
|
+
const prefs = readCursorUpdateCheckPrefs(
|
|
283
|
+
inventoryRoot ? await loadContextConfig(inventoryRoot) : null
|
|
284
|
+
);
|
|
285
|
+
const changelogUrl = options.changelogUrl ?? prefs.changelogUrl;
|
|
286
|
+
if (options.respectPrefs) {
|
|
287
|
+
if (!prefs.enabled) {
|
|
288
|
+
return baseResult({
|
|
289
|
+
status: "skipped-disabled",
|
|
290
|
+
inventoryRoot,
|
|
291
|
+
inventoryPath: INVENTORY_REL,
|
|
292
|
+
featuresPath: FEATURES_REL,
|
|
293
|
+
changelogUrl,
|
|
294
|
+
latestCursorVersion: null,
|
|
295
|
+
lastSeenCursorVersion: prefs.lastSeenCursorVersion,
|
|
296
|
+
inventoryRefreshed: null,
|
|
297
|
+
openActionIds: [],
|
|
298
|
+
gaps: [],
|
|
299
|
+
message: "cursorUpdateCheck.enabled is false (opt-in). Set true in .cursor/context/config.json to nudge."
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
if (!intervalElapsed(prefs.lastCheckedAt, prefs.intervalDays)) {
|
|
303
|
+
return baseResult({
|
|
304
|
+
status: "skipped-interval",
|
|
305
|
+
inventoryRoot,
|
|
306
|
+
inventoryPath: INVENTORY_REL,
|
|
307
|
+
featuresPath: FEATURES_REL,
|
|
308
|
+
changelogUrl,
|
|
309
|
+
latestCursorVersion: null,
|
|
310
|
+
lastSeenCursorVersion: prefs.lastSeenCursorVersion,
|
|
311
|
+
inventoryRefreshed: null,
|
|
312
|
+
openActionIds: [],
|
|
313
|
+
gaps: [],
|
|
314
|
+
message: `Within cursorUpdateCheck.intervalDays (${prefs.intervalDays}); last check ${prefs.lastCheckedAt}.`
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
if (!inventoryRoot) {
|
|
319
|
+
return baseResult({
|
|
320
|
+
status: "error",
|
|
321
|
+
inventoryRoot: null,
|
|
322
|
+
inventoryPath: INVENTORY_REL,
|
|
323
|
+
featuresPath: FEATURES_REL,
|
|
324
|
+
changelogUrl,
|
|
325
|
+
latestCursorVersion: null,
|
|
326
|
+
lastSeenCursorVersion: prefs.lastSeenCursorVersion,
|
|
327
|
+
inventoryRefreshed: null,
|
|
328
|
+
openActionIds: [],
|
|
329
|
+
gaps: [],
|
|
330
|
+
message: missingInventoryMessage()
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
const inventoryPath = path.join(inventoryRoot, INVENTORY_REL);
|
|
334
|
+
const featuresPath = path.join(inventoryRoot, FEATURES_REL);
|
|
335
|
+
let inventoryMd;
|
|
336
|
+
try {
|
|
337
|
+
inventoryMd = await readFile(inventoryPath, "utf8");
|
|
338
|
+
} catch {
|
|
339
|
+
return baseResult({
|
|
340
|
+
status: "error",
|
|
341
|
+
inventoryRoot,
|
|
342
|
+
inventoryPath: INVENTORY_REL,
|
|
343
|
+
featuresPath: FEATURES_REL,
|
|
344
|
+
changelogUrl,
|
|
345
|
+
latestCursorVersion: null,
|
|
346
|
+
lastSeenCursorVersion: prefs.lastSeenCursorVersion,
|
|
347
|
+
inventoryRefreshed: null,
|
|
348
|
+
openActionIds: [],
|
|
349
|
+
gaps: [],
|
|
350
|
+
message: missingInventoryMessage()
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
const inventoryRefreshed = parseInventoryRefreshed(inventoryMd);
|
|
354
|
+
const openActionIds = parseOpenActionIds(inventoryMd);
|
|
355
|
+
const gaps = [];
|
|
356
|
+
let featuresMd = "";
|
|
357
|
+
for (const id of openActionIds) {
|
|
358
|
+
gaps.push({
|
|
359
|
+
id: `open-action-${id}`,
|
|
360
|
+
severity: "advisory",
|
|
361
|
+
path: INVENTORY_REL,
|
|
362
|
+
evidence: `Action item ${id} is Open in the native-audit inventory`,
|
|
363
|
+
suggestedRoute: "backlog-add"
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
if (inventoryRefreshed) {
|
|
367
|
+
const refreshedMs = Date.parse(inventoryRefreshed);
|
|
368
|
+
if (!Number.isNaN(refreshedMs)) {
|
|
369
|
+
const ageDays = (Date.now() - refreshedMs) / (24 * 60 * 60 * 1e3);
|
|
370
|
+
if (ageDays > 45) {
|
|
371
|
+
gaps.push({
|
|
372
|
+
id: "inventory-stale",
|
|
373
|
+
severity: "stale",
|
|
374
|
+
path: INVENTORY_REL,
|
|
375
|
+
evidence: `Inventory last refreshed ${inventoryRefreshed} (>45 days). Version-prose refresh owned by parked Marketplace plan; awareness reports only.`,
|
|
376
|
+
suggestedRoute: "none"
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
try {
|
|
382
|
+
featuresMd = await readFile(featuresPath, "utf8");
|
|
383
|
+
if (parseFeatureMapNames(featuresMd).length === 0) {
|
|
384
|
+
gaps.push({
|
|
385
|
+
id: "features-map-empty",
|
|
386
|
+
severity: "info",
|
|
387
|
+
path: FEATURES_REL,
|
|
388
|
+
evidence: "Secondary feature map has no Feature table rows",
|
|
389
|
+
suggestedRoute: "none"
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
} catch {
|
|
393
|
+
gaps.push({
|
|
394
|
+
id: "features-map-missing",
|
|
395
|
+
severity: "info",
|
|
396
|
+
path: FEATURES_REL,
|
|
397
|
+
evidence: "Secondary feature map missing; changelog/inventory check continues",
|
|
398
|
+
suggestedRoute: "none"
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
let latestCursorVersion = null;
|
|
402
|
+
if (!options.offline) {
|
|
403
|
+
try {
|
|
404
|
+
const body = options.changelogBody ?? await (options.fetchText ?? defaultFetchText)(changelogUrl);
|
|
405
|
+
latestCursorVersion = extractLatestCursorVersion(body);
|
|
406
|
+
const haystack = `${inventoryMd}
|
|
407
|
+
${featuresMd}`;
|
|
408
|
+
const missingKeywords = findMissingInventoryKeywords(
|
|
409
|
+
extractChangelogFeatureKeywords(body),
|
|
410
|
+
haystack
|
|
411
|
+
);
|
|
412
|
+
for (const keyword of missingKeywords.slice(0, MAX_FEATURE_KEYWORD_GAPS)) {
|
|
413
|
+
const slug = slugifyFeatureKeyword(keyword);
|
|
414
|
+
if (!slug) continue;
|
|
415
|
+
gaps.push({
|
|
416
|
+
id: `feature-keyword-miss:${slug}`,
|
|
417
|
+
severity: "advisory",
|
|
418
|
+
path: FEATURES_REL,
|
|
419
|
+
evidence: `Changelog names "${keyword}" which is not listed in native-audit or cursor-3-features`,
|
|
420
|
+
suggestedRoute: "backlog-add"
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
if (latestCursorVersion && prefs.lastSeenCursorVersion && compareCursorVersion(latestCursorVersion, prefs.lastSeenCursorVersion) > 0) {
|
|
424
|
+
gaps.push({
|
|
425
|
+
id: "changelog-ahead",
|
|
426
|
+
severity: "advisory",
|
|
427
|
+
path: changelogUrl,
|
|
428
|
+
evidence: `Changelog latest ${latestCursorVersion} is ahead of lastSeenCursorVersion ${prefs.lastSeenCursorVersion}`,
|
|
429
|
+
suggestedRoute: "backlog-add"
|
|
430
|
+
});
|
|
431
|
+
} else if (latestCursorVersion && !prefs.lastSeenCursorVersion) {
|
|
432
|
+
gaps.push({
|
|
433
|
+
id: "changelog-baseline",
|
|
434
|
+
severity: "info",
|
|
435
|
+
path: changelogUrl,
|
|
436
|
+
evidence: `Changelog latest ${latestCursorVersion}; no lastSeenCursorVersion baseline yet (stamp to baseline)`,
|
|
437
|
+
suggestedRoute: "none"
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
} catch (err) {
|
|
441
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
442
|
+
return baseResult({
|
|
443
|
+
status: "error",
|
|
444
|
+
inventoryRoot,
|
|
445
|
+
inventoryPath: INVENTORY_REL,
|
|
446
|
+
featuresPath: FEATURES_REL,
|
|
447
|
+
changelogUrl,
|
|
448
|
+
latestCursorVersion: null,
|
|
449
|
+
lastSeenCursorVersion: prefs.lastSeenCursorVersion,
|
|
450
|
+
inventoryRefreshed,
|
|
451
|
+
openActionIds,
|
|
452
|
+
gaps,
|
|
453
|
+
message: `Changelog fetch error: ${msg}`
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
if (options.stamp && inventoryRoot) {
|
|
458
|
+
await stampCursorUpdateCheck(inventoryRoot, {
|
|
459
|
+
lastSeenCursorVersion: latestCursorVersion ?? prefs.lastSeenCursorVersion
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
const status = gaps.length > 0 ? "gaps-found" : "current";
|
|
463
|
+
const message = status === "current" ? "No advisory Cursor-update gaps vs inventory (check-only)." : `Found ${gaps.length} advisory gap(s). ${CONVEYOR_HINT}`;
|
|
464
|
+
return baseResult({
|
|
465
|
+
status,
|
|
466
|
+
inventoryRoot,
|
|
467
|
+
inventoryPath: INVENTORY_REL,
|
|
468
|
+
featuresPath: FEATURES_REL,
|
|
469
|
+
changelogUrl: options.offline ? null : changelogUrl,
|
|
470
|
+
latestCursorVersion,
|
|
471
|
+
lastSeenCursorVersion: prefs.lastSeenCursorVersion,
|
|
472
|
+
inventoryRefreshed,
|
|
473
|
+
openActionIds,
|
|
474
|
+
gaps,
|
|
475
|
+
message
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
export {
|
|
480
|
+
CHANGELOG_FETCH_TIMEOUT_MS,
|
|
481
|
+
resolveInventoryRoot,
|
|
482
|
+
stampCursorUpdateCheck,
|
|
483
|
+
checkCursorUpdateAwareness
|
|
484
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// src/hooks/read-stdin-json.ts
|
|
2
|
+
async function readStdinJson() {
|
|
3
|
+
const chunks = [];
|
|
4
|
+
for await (const chunk of process.stdin) {
|
|
5
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
|
|
6
|
+
}
|
|
7
|
+
const raw = Buffer.concat(chunks).toString("utf8").trim();
|
|
8
|
+
if (!raw) return {};
|
|
9
|
+
try {
|
|
10
|
+
return JSON.parse(raw);
|
|
11
|
+
} catch {
|
|
12
|
+
return {};
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export {
|
|
17
|
+
readStdinJson
|
|
18
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import {
|
|
2
|
+
L0_ARTIFACTS
|
|
3
|
+
} from "./chunk-N3HTJVBR.js";
|
|
4
|
+
import {
|
|
5
|
+
copyRegistryFile,
|
|
6
|
+
emptyStats,
|
|
7
|
+
installPack,
|
|
8
|
+
installSkillsByIds,
|
|
9
|
+
loadManagedHashLedger,
|
|
10
|
+
loadRegistry,
|
|
11
|
+
mergeStats,
|
|
12
|
+
recordOutcome,
|
|
13
|
+
resolveProtectedGlobs,
|
|
14
|
+
saveManagedHashLedger
|
|
15
|
+
} from "./chunk-AW74INUW.js";
|
|
16
|
+
import {
|
|
17
|
+
fileExists
|
|
18
|
+
} from "./chunk-77V75A7O.js";
|
|
19
|
+
|
|
20
|
+
// src/lifecycle/onboard-migration.ts
|
|
21
|
+
import { createHash } from "crypto";
|
|
22
|
+
import { readFile, unlink } from "fs/promises";
|
|
23
|
+
import path from "path";
|
|
24
|
+
var LEGACY_ONBOARD_PATH = ".cursor/commands/onboard.md";
|
|
25
|
+
var NAMESPACED_ONBOARD_PATH = ".cursor/commands/agent-kit-onboard.md";
|
|
26
|
+
var MANAGED_LEGACY_HASHES = /* @__PURE__ */ new Set([
|
|
27
|
+
"b274a68941813f19b185893cb7c5561dff027f53270890029992f208e24992fe"
|
|
28
|
+
]);
|
|
29
|
+
async function migrateLegacyOnboardCommand(projectRoot, managedHashes = MANAGED_LEGACY_HASHES) {
|
|
30
|
+
const legacyPath = path.join(projectRoot, LEGACY_ONBOARD_PATH);
|
|
31
|
+
if (!await fileExists(legacyPath)) return "absent";
|
|
32
|
+
const namespacedPath = path.join(projectRoot, NAMESPACED_ONBOARD_PATH);
|
|
33
|
+
if (!await fileExists(namespacedPath)) return "preserved-customized";
|
|
34
|
+
const content = await readFile(legacyPath);
|
|
35
|
+
const hash = createHash("sha256").update(content).digest("hex");
|
|
36
|
+
if (!managedHashes.has(hash)) return "preserved-customized";
|
|
37
|
+
await unlink(legacyPath);
|
|
38
|
+
return "removed-managed";
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// src/lifecycle/sync.ts
|
|
42
|
+
async function installL0(registryRoot, projectRoot, protectedGlobs, ledger) {
|
|
43
|
+
const stats = emptyStats();
|
|
44
|
+
const managedHashes = ledger ?? await loadManagedHashLedger(projectRoot);
|
|
45
|
+
const copyOpts = { managedHashes, persistManagedHashes: false };
|
|
46
|
+
for (const artifact of L0_ARTIFACTS) {
|
|
47
|
+
const outcome = await copyRegistryFile(
|
|
48
|
+
registryRoot,
|
|
49
|
+
projectRoot,
|
|
50
|
+
artifact.source,
|
|
51
|
+
artifact.target,
|
|
52
|
+
protectedGlobs,
|
|
53
|
+
copyOpts
|
|
54
|
+
);
|
|
55
|
+
recordOutcome(stats, artifact.target, outcome);
|
|
56
|
+
}
|
|
57
|
+
if (!ledger) await saveManagedHashLedger(projectRoot, managedHashes);
|
|
58
|
+
const migration = await migrateLegacyOnboardCommand(projectRoot);
|
|
59
|
+
if (migration === "removed-managed") {
|
|
60
|
+
stats.removed.push(".cursor/commands/onboard.md");
|
|
61
|
+
} else if (migration === "preserved-customized") {
|
|
62
|
+
stats.collisions.push(".cursor/commands/onboard.md");
|
|
63
|
+
}
|
|
64
|
+
return stats;
|
|
65
|
+
}
|
|
66
|
+
async function syncFromManifest(registryRoot, projectRoot, manifest) {
|
|
67
|
+
const protectedGlobs = resolveProtectedGlobs(manifest);
|
|
68
|
+
const stats = emptyStats();
|
|
69
|
+
const managedHashes = await loadManagedHashLedger(projectRoot);
|
|
70
|
+
const installOpts = { protectedGlobs, managedHashes };
|
|
71
|
+
try {
|
|
72
|
+
mergeStats(stats, await installL0(registryRoot, projectRoot, protectedGlobs, managedHashes));
|
|
73
|
+
for (const packId of manifest.packs ?? []) {
|
|
74
|
+
mergeStats(stats, await installPack(registryRoot, projectRoot, packId, installOpts));
|
|
75
|
+
}
|
|
76
|
+
const skillIds = manifest.skills ?? [];
|
|
77
|
+
if (skillIds.length > 0) {
|
|
78
|
+
await loadRegistry(registryRoot);
|
|
79
|
+
mergeStats(stats, await installSkillsByIds(registryRoot, projectRoot, skillIds, installOpts));
|
|
80
|
+
}
|
|
81
|
+
} finally {
|
|
82
|
+
await saveManagedHashLedger(projectRoot, managedHashes);
|
|
83
|
+
}
|
|
84
|
+
return stats;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// src/commands/shared-args.ts
|
|
88
|
+
var NON_INTERACTIVE_ROOT_ARGS = {
|
|
89
|
+
yes: {
|
|
90
|
+
type: "boolean",
|
|
91
|
+
alias: "y",
|
|
92
|
+
description: "Skip interactive prompts; use defaults (IDE-agnostic non-interactive mode)",
|
|
93
|
+
default: false
|
|
94
|
+
},
|
|
95
|
+
"force-root": {
|
|
96
|
+
type: "boolean",
|
|
97
|
+
description: "Bypass the ambiguous-root guard (use with caution)",
|
|
98
|
+
default: false
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export {
|
|
103
|
+
installL0,
|
|
104
|
+
syncFromManifest,
|
|
105
|
+
NON_INTERACTIVE_ROOT_ARGS
|
|
106
|
+
};
|