@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.
Files changed (72) hide show
  1. package/dashboard/dashboard-data.mjs +69 -65
  2. package/dashboard/lib/guards.d.mts +5 -0
  3. package/dashboard/lib/guards.mjs +31 -0
  4. package/dashboard/lib/identity-probe.mjs +56 -0
  5. package/dashboard/lib/pipeline-cache.mjs +58 -0
  6. package/dashboard/lib/processes.mjs +71 -0
  7. package/dashboard/serve.mjs +43 -1
  8. package/dashboard/start-broadcast.mjs +2 -12
  9. package/dashboard/start.mjs +2 -13
  10. package/dist/add-HTYIFKMN.js +150 -0
  11. package/dist/chunk-2IPXQFJE.js +253 -0
  12. package/dist/chunk-2RDRMGD3.js +82 -0
  13. package/dist/chunk-2YU2H7I6.js +42 -0
  14. package/dist/chunk-4IWG5CVE.js +214 -0
  15. package/dist/chunk-5VCOWTTP.js +6 -0
  16. package/dist/chunk-77V75A7O.js +39 -0
  17. package/dist/chunk-7HDQEK6T.js +26 -0
  18. package/dist/chunk-AW74INUW.js +1456 -0
  19. package/dist/chunk-B73KQ5KX.js +972 -0
  20. package/dist/chunk-CBCW5R5J.js +484 -0
  21. package/dist/chunk-DE3LQFSJ.js +18 -0
  22. package/dist/chunk-DQA434Y4.js +106 -0
  23. package/dist/chunk-F4VMYR7U.js +318 -0
  24. package/dist/chunk-F6C3GFKV.js +57 -0
  25. package/dist/chunk-HFUH3HVS.js +282 -0
  26. package/dist/chunk-HS7LVVLO.js +74 -0
  27. package/dist/chunk-IHROBTNM.js +135 -0
  28. package/dist/chunk-KD2MYBB6.js +213 -0
  29. package/dist/chunk-MAPOQEA2.js +43 -0
  30. package/dist/chunk-N3HTJVBR.js +341 -0
  31. package/dist/chunk-NQ7IQ5XT.js +26 -0
  32. package/dist/chunk-OUQZC744.js +1315 -0
  33. package/dist/chunk-PCCQUWE5.js +817 -0
  34. package/dist/chunk-PNFF6OU3.js +273 -0
  35. package/dist/chunk-PNG7LO2F.js +53 -0
  36. package/dist/chunk-QTON2GQX.js +203 -0
  37. package/dist/chunk-S6GMFDKV.js +50 -0
  38. package/dist/chunk-SMGFN3Z4.js +503 -0
  39. package/dist/chunk-STGFBAYT.js +641 -0
  40. package/dist/chunk-TFZ2JUXH.js +1081 -0
  41. package/dist/chunk-VY7MTPJ2.js +205 -0
  42. package/dist/chunk-W24CMQ32.js +525 -0
  43. package/dist/chunk-WW3YZKHW.js +165 -0
  44. package/dist/chunk-ZB43YMHW.js +20 -0
  45. package/dist/chunk-ZCTSUQRR.js +874 -0
  46. package/dist/chunk-ZXKZBTXE.js +17 -0
  47. package/dist/contribute-C6AW2P6J.js +367 -0
  48. package/dist/cursor-awareness-KTL2RGKD.js +77 -0
  49. package/dist/dashboard-YSNUGBFY.js +21 -0
  50. package/dist/dashboard-broadcast-M4CMW7RX.js +119 -0
  51. package/dist/diff-VHNR62SI.js +74 -0
  52. package/dist/doctor-PET2HRRJ.js +236 -0
  53. package/dist/dogfood-KKFVRWT6.js +366 -0
  54. package/dist/guard-EHXF76ID.js +530 -0
  55. package/dist/handoff-3UI6VKVG.js +302 -0
  56. package/dist/hook-DYIMKJYL.js +1074 -0
  57. package/dist/index.js +62 -14068
  58. package/dist/init-Q4LBM6KT.js +127 -0
  59. package/dist/install-RHOS3OYZ.js +34 -0
  60. package/dist/mission-control-LJACEVVN.js +89 -0
  61. package/dist/monitors-QV2IS3MP.js +176 -0
  62. package/dist/plan-index-JMC7Z4DW.js +44 -0
  63. package/dist/public-inbound-radar-WP3XF7GS.js +68 -0
  64. package/dist/remote-issues-SZ4HDQL5.js +44 -0
  65. package/dist/run-I3BFGULT.js +243 -0
  66. package/dist/run-plan-FO7CL4M3.js +118 -0
  67. package/dist/scan-NI2SWJ2E.js +33 -0
  68. package/dist/setup-global-M42GEODI.js +340 -0
  69. package/dist/status-XPVQVQMX.js +150 -0
  70. package/dist/update-FJ2EBZVO.js +283 -0
  71. package/dist/validate-V2CW7J4B.js +201 -0
  72. package/package.json +3 -3
@@ -0,0 +1,150 @@
1
+ import {
2
+ formatSkippedSymlinks,
3
+ logApplyStats
4
+ } from "./chunk-HS7LVVLO.js";
5
+ import {
6
+ REGISTRY_CLI_ARGS,
7
+ resolveRegistryFromCli
8
+ } from "./chunk-PNG7LO2F.js";
9
+ import {
10
+ logger
11
+ } from "./chunk-ZB43YMHW.js";
12
+ import {
13
+ allPacks,
14
+ allSkills,
15
+ buildManifest,
16
+ findPack,
17
+ findSkill,
18
+ installPack,
19
+ installSkill,
20
+ loadAgentKitManifest,
21
+ loadRegistry,
22
+ resolveProtectedGlobs,
23
+ saveManifest,
24
+ upsertIdList
25
+ } from "./chunk-AW74INUW.js";
26
+ import "./chunk-5VCOWTTP.js";
27
+ import {
28
+ KIT_VERSION
29
+ } from "./chunk-7HDQEK6T.js";
30
+ import "./chunk-77V75A7O.js";
31
+
32
+ // src/commands/add.ts
33
+ import { defineCommand } from "citty";
34
+ var addCommand = defineCommand({
35
+ meta: {
36
+ name: "add",
37
+ description: "Install a skill or L1 pack from the registry."
38
+ },
39
+ args: {
40
+ id: {
41
+ type: "positional",
42
+ description: "Skill id or pack id from registry"
43
+ },
44
+ skill: {
45
+ type: "boolean",
46
+ description: "Force treat id as a skill (when pack shares the same id)",
47
+ default: false
48
+ },
49
+ pack: {
50
+ type: "boolean",
51
+ description: "Force treat id as a pack (when skill shares the same id)",
52
+ default: false
53
+ },
54
+ cwd: {
55
+ type: "string",
56
+ default: process.cwd()
57
+ },
58
+ ...REGISTRY_CLI_ARGS
59
+ },
60
+ async run({ args }) {
61
+ if (!args.id) throw new Error("Provide an id: agent-kit add <skill|pack>");
62
+ if (args.skill && args.pack) {
63
+ throw new Error("Use only one of --skill or --pack");
64
+ }
65
+ const existing = await loadAgentKitManifest(args.cwd, { onWarning: logger.warn });
66
+ const registry = await resolveRegistryFromCli({
67
+ cwd: args.cwd,
68
+ registry: args.registry,
69
+ url: args.url,
70
+ ref: args.ref,
71
+ refresh: args.refresh,
72
+ manifest: existing
73
+ });
74
+ try {
75
+ const protectedGlobs = resolveProtectedGlobs(existing);
76
+ const index = await loadRegistry(registry.root);
77
+ const asSkill = allSkills(index).find((s) => s.id === args.id);
78
+ const asPack = allPacks(index).find((p) => p.id === args.id);
79
+ const base = existing ? { ...existing } : buildManifest({
80
+ version: KIT_VERSION,
81
+ profile: "default",
82
+ registryUrl: registry.url,
83
+ registryRef: registry.ref
84
+ });
85
+ const skipped = (stats) => {
86
+ if (stats.skippedSymlink.length === 0) return false;
87
+ logger.error(formatSkippedSymlinks(stats.skippedSymlink));
88
+ process.exitCode = 1;
89
+ return true;
90
+ };
91
+ const finish = async (kind, id) => {
92
+ if (kind === "skill") base.skills = upsertIdList(base.skills, id);
93
+ else base.packs = upsertIdList(base.packs, id);
94
+ if (registry.url || registry.ref) {
95
+ base.registry = {
96
+ url: registry.url ?? base.registry?.url,
97
+ ref: registry.ref ?? base.registry?.ref
98
+ };
99
+ }
100
+ base.version = KIT_VERSION;
101
+ await saveManifest(args.cwd, base);
102
+ };
103
+ if (args.skill) {
104
+ const skill = await findSkill(registry.root, args.id);
105
+ const stats = await installSkill(registry.root, args.cwd, skill, { protectedGlobs });
106
+ logApplyStats(stats);
107
+ if (skipped(stats)) return;
108
+ await finish("skill", skill.id);
109
+ logger.success(`Skill '${skill.id}' added.`);
110
+ return;
111
+ }
112
+ if (args.pack) {
113
+ const pack = await findPack(registry.root, args.id);
114
+ const stats = await installPack(registry.root, args.cwd, pack.id, { protectedGlobs });
115
+ logApplyStats(stats);
116
+ if (skipped(stats)) return;
117
+ await finish("pack", pack.id);
118
+ logger.success(`Pack '${pack.id}' added.`);
119
+ return;
120
+ }
121
+ if (asSkill && asPack) {
122
+ throw new Error(
123
+ `'${args.id}' is both a skill and a pack. Disambiguate: agent-kit add --skill ${args.id} | agent-kit add --pack ${args.id}`
124
+ );
125
+ }
126
+ if (asSkill) {
127
+ const stats = await installSkill(registry.root, args.cwd, asSkill, { protectedGlobs });
128
+ logApplyStats(stats);
129
+ if (skipped(stats)) return;
130
+ await finish("skill", asSkill.id);
131
+ logger.success(`Skill '${asSkill.id}' added.`);
132
+ return;
133
+ }
134
+ if (asPack) {
135
+ const stats = await installPack(registry.root, args.cwd, asPack.id, { protectedGlobs });
136
+ logApplyStats(stats);
137
+ if (skipped(stats)) return;
138
+ await finish("pack", asPack.id);
139
+ logger.success(`Pack '${asPack.id}' added.`);
140
+ return;
141
+ }
142
+ throw new Error(`'${args.id}' not found as skill or pack in registry.`);
143
+ } finally {
144
+ await registry.unlock?.();
145
+ }
146
+ }
147
+ });
148
+ export {
149
+ addCommand
150
+ };
@@ -0,0 +1,253 @@
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/public-inbound-radar.ts
12
+ import { execFile } from "child_process";
13
+ import { access } from "fs/promises";
14
+ import path from "path";
15
+ import { promisify } from "util";
16
+ var execFileAsync = promisify(execFile);
17
+ var PUBLIC_INBOUND_REPO = "agent-kit-startup/agent-kit";
18
+ var DEFAULT_PREFS = {
19
+ enabled: false,
20
+ intervalDays: 7,
21
+ lastCheckedAt: null
22
+ };
23
+ function emptySummary() {
24
+ return { issues: 0, humanPullRequests: 0, dependabotPullRequests: 0, total: 0 };
25
+ }
26
+ function summarize(items) {
27
+ const summary = emptySummary();
28
+ for (const item of items) {
29
+ summary.total += 1;
30
+ if (item.class === "issue") summary.issues += 1;
31
+ else if (item.class === "dependabot") summary.dependabotPullRequests += 1;
32
+ else summary.humanPullRequests += 1;
33
+ }
34
+ return summary;
35
+ }
36
+ function labelNames(labels) {
37
+ if (!Array.isArray(labels)) return [];
38
+ return labels.map((entry) => typeof entry === "string" ? entry : entry?.name).filter((name) => typeof name === "string" && name.length > 0);
39
+ }
40
+ function isDependabotAuthor(login) {
41
+ if (!login) return false;
42
+ const normalized = login.toLowerCase();
43
+ return normalized === "dependabot" || normalized === "dependabot[bot]" || normalized === "app/dependabot" || normalized.startsWith("dependabot") || normalized === "renovate" || normalized === "renovate[bot]";
44
+ }
45
+ function classifyInbound(kind, authorLogin) {
46
+ if (kind === "issue") return "issue";
47
+ return isDependabotAuthor(authorLogin) ? "dependabot" : "human-pr";
48
+ }
49
+ function readPublicInboundCheckPrefs(config) {
50
+ if (!config || typeof config !== "object" || Array.isArray(config)) {
51
+ return { ...DEFAULT_PREFS };
52
+ }
53
+ const raw = config.publicInboundCheck;
54
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
55
+ return { ...DEFAULT_PREFS };
56
+ }
57
+ const uc = raw;
58
+ const intervalDays = typeof uc.intervalDays === "number" && Number.isInteger(uc.intervalDays) && uc.intervalDays >= 1 ? uc.intervalDays : DEFAULT_PREFS.intervalDays;
59
+ return {
60
+ enabled: uc.enabled === true,
61
+ intervalDays,
62
+ lastCheckedAt: typeof uc.lastCheckedAt === "string" ? uc.lastCheckedAt : null
63
+ };
64
+ }
65
+ async function stampPublicInboundCheck(cwd, checkedAt) {
66
+ const existing = await loadContextConfig(cwd) ?? {};
67
+ const prev = existing.publicInboundCheck && typeof existing.publicInboundCheck === "object" ? { ...existing.publicInboundCheck } : {};
68
+ existing.publicInboundCheck = {
69
+ ...DEFAULT_PREFS,
70
+ ...prev,
71
+ lastCheckedAt: (checkedAt ?? /* @__PURE__ */ new Date()).toISOString()
72
+ };
73
+ await writeJson(contextConfigPath(cwd), existing);
74
+ }
75
+ function mapRows(kind, rows) {
76
+ const items = [];
77
+ for (const row of rows) {
78
+ const number = typeof row.number === "number" ? row.number : Number(row.number);
79
+ if (!Number.isFinite(number) || number <= 0) continue;
80
+ const author = row.author?.login?.trim() || "unknown";
81
+ const title = typeof row.title === "string" && row.title.trim() ? row.title.trim() : "(no title)";
82
+ const updatedAt = typeof row.updatedAt === "string" && row.updatedAt.trim() ? row.updatedAt.trim() : "";
83
+ const url = typeof row.url === "string" && row.url.trim() ? row.url.trim() : `https://github.com/${PUBLIC_INBOUND_REPO}/${kind === "issue" ? "issues" : "pull"}/${number}`;
84
+ items.push({
85
+ kind,
86
+ class: classifyInbound(kind, author),
87
+ number,
88
+ title,
89
+ author,
90
+ updatedAt,
91
+ url,
92
+ labels: labelNames(row.labels),
93
+ cite: `${PUBLIC_INBOUND_REPO}#${number}`
94
+ });
95
+ }
96
+ return items;
97
+ }
98
+ async function defaultRunGh(args, options) {
99
+ const { stdout } = await execFileAsync("gh", args, {
100
+ cwd: options?.cwd,
101
+ encoding: "utf8",
102
+ maxBuffer: 4 * 1024 * 1024,
103
+ timeout: 3e4
104
+ });
105
+ return stdout;
106
+ }
107
+ async function ghAvailable(runGh, cwd) {
108
+ try {
109
+ await runGh(["--version"], { cwd });
110
+ return true;
111
+ } catch {
112
+ return false;
113
+ }
114
+ }
115
+ async function detectFactoryCheckout(cwd) {
116
+ const root = path.resolve(cwd);
117
+ try {
118
+ const { stdout } = await execFileAsync("git", ["remote", "get-url", "origin"], {
119
+ cwd: root,
120
+ encoding: "utf8",
121
+ timeout: 5e3
122
+ });
123
+ if (/agent-kit-dev/i.test(stdout)) return true;
124
+ } catch {
125
+ }
126
+ return fileExists(path.join(root, "dogfood", "README.md"));
127
+ }
128
+ async function listJson(runGh, cwd, resource) {
129
+ const stdout = await runGh(
130
+ [
131
+ resource,
132
+ "list",
133
+ "--repo",
134
+ PUBLIC_INBOUND_REPO,
135
+ "--state",
136
+ "open",
137
+ "--limit",
138
+ "100",
139
+ "--json",
140
+ "number,title,author,updatedAt,labels,url"
141
+ ],
142
+ { cwd }
143
+ );
144
+ const trimmed = stdout.trim();
145
+ if (!trimmed) return [];
146
+ const parsed = JSON.parse(trimmed);
147
+ return Array.isArray(parsed) ? parsed : [];
148
+ }
149
+ function resultBase(status, message, factory, now, items = []) {
150
+ return {
151
+ status,
152
+ repo: PUBLIC_INBOUND_REPO,
153
+ checkedAt: now.toISOString(),
154
+ factory,
155
+ items,
156
+ summary: summarize(items),
157
+ message,
158
+ mutateRecommended: false
159
+ };
160
+ }
161
+ async function checkPublicInbound(cwd, options = {}) {
162
+ const now = options.now?.() ?? /* @__PURE__ */ new Date();
163
+ const runGh = options.runGh ?? defaultRunGh;
164
+ const isFactory = options.isFactory ?? detectFactoryCheckout;
165
+ const root = path.resolve(cwd);
166
+ try {
167
+ await access(root);
168
+ } catch {
169
+ return resultBase("error", `cwd unreadable: ${root}`, false, now);
170
+ }
171
+ const factory = await isFactory(root);
172
+ if (!factory) {
173
+ return resultBase(
174
+ "skipped-non-factory",
175
+ "Factory-only check. Aborting outside agent-kit-dev / dogfood checkout.",
176
+ false,
177
+ now
178
+ );
179
+ }
180
+ if (options.respectPrefs) {
181
+ const prefs = readPublicInboundCheckPrefs(await loadContextConfig(root));
182
+ if (!prefs.enabled) {
183
+ return resultBase(
184
+ "skipped-disabled",
185
+ "publicInboundCheck.enabled is false (opt-in). Set true in .cursor/context/config.json to nudge.",
186
+ true,
187
+ now
188
+ );
189
+ }
190
+ if (!intervalElapsed(prefs.lastCheckedAt, prefs.intervalDays)) {
191
+ return resultBase(
192
+ "skipped-interval",
193
+ `Within publicInboundCheck.intervalDays (${prefs.intervalDays}); last check ${prefs.lastCheckedAt}.`,
194
+ true,
195
+ now
196
+ );
197
+ }
198
+ }
199
+ if (options.offline) {
200
+ return resultBase("skipped-offline", "Offline flag set; skipped GitHub fetch.", true, now);
201
+ }
202
+ if (!await ghAvailable(runGh, root)) {
203
+ return resultBase(
204
+ "skipped-gh-missing",
205
+ "gh CLI missing or not runnable; fail-open skip.",
206
+ true,
207
+ now
208
+ );
209
+ }
210
+ try {
211
+ const [issueRows, prRows] = await Promise.all([
212
+ listJson(runGh, root, "issue"),
213
+ listJson(runGh, root, "pr")
214
+ ]);
215
+ const items = [...mapRows("issue", issueRows), ...mapRows("pull_request", prRows)].sort(
216
+ (a, b) => b.number - a.number
217
+ );
218
+ const summary = summarize(items);
219
+ const message = summary.total === 0 ? `No open issues or pull requests on ${PUBLIC_INBOUND_REPO}.` : `Open inbound on ${PUBLIC_INBOUND_REPO}: ${summary.issues} issue(s), ${summary.humanPullRequests} human PR(s), ${summary.dependabotPullRequests} Dependabot PR(s).`;
220
+ const result = resultBase("ok", message, true, now, items);
221
+ if (options.stamp) {
222
+ await stampPublicInboundCheck(root, now);
223
+ }
224
+ return result;
225
+ } catch (err) {
226
+ const detail = err instanceof Error ? err.message : String(err);
227
+ if (/ENOTFOUND|ECONNREFUSED|ETIMEDOUT|network|offline|Could not resolve/i.test(detail)) {
228
+ return resultBase(
229
+ "skipped-offline",
230
+ `GitHub unreachable; fail-open skip (${detail}).`,
231
+ true,
232
+ now
233
+ );
234
+ }
235
+ return resultBase("error", `Public inbound check failed: ${detail}`, true, now);
236
+ }
237
+ }
238
+ function shouldEmitPublicInboundNudge(result) {
239
+ if (!result || result.status !== "ok") return false;
240
+ if (result.mutateRecommended === true) return false;
241
+ const summary = result.summary;
242
+ if (!summary || typeof summary !== "object") return false;
243
+ const total = summary.total;
244
+ return typeof total === "number" && total > 0;
245
+ }
246
+
247
+ export {
248
+ PUBLIC_INBOUND_REPO,
249
+ stampPublicInboundCheck,
250
+ detectFactoryCheckout,
251
+ checkPublicInbound,
252
+ shouldEmitPublicInboundNudge
253
+ };
@@ -0,0 +1,82 @@
1
+ import {
2
+ managedPairs
3
+ } from "./chunk-N3HTJVBR.js";
4
+ import {
5
+ isProtectedPath,
6
+ resolveContained,
7
+ resolveProtectedGlobs
8
+ } from "./chunk-AW74INUW.js";
9
+
10
+ // src/lifecycle/diff.ts
11
+ import { readFile } from "fs/promises";
12
+ import path from "path";
13
+ async function readIfExists(abs) {
14
+ try {
15
+ return await readFile(abs, "utf8");
16
+ } catch {
17
+ return null;
18
+ }
19
+ }
20
+ async function comparePair(registryRoot, projectRoot, sourceRel, targetRel, protectedGlobs) {
21
+ const posixTarget = targetRel.split(path.sep).join("/");
22
+ if (isProtectedPath(posixTarget, protectedGlobs)) {
23
+ return { path: posixTarget, status: "protected" };
24
+ }
25
+ let sourceAbs;
26
+ try {
27
+ sourceAbs = resolveContained(registryRoot, sourceRel);
28
+ } catch {
29
+ return { path: posixTarget, status: "missing-registry" };
30
+ }
31
+ const registryContent = await readIfExists(sourceAbs);
32
+ if (registryContent === null) {
33
+ return { path: posixTarget, status: "missing-registry" };
34
+ }
35
+ let targetAbs;
36
+ try {
37
+ targetAbs = resolveContained(projectRoot, targetRel);
38
+ } catch {
39
+ return { path: posixTarget, status: "missing-local" };
40
+ }
41
+ const localContent = await readIfExists(targetAbs);
42
+ if (localContent === null) return { path: posixTarget, status: "missing-local" };
43
+ if (localContent === registryContent) return { path: posixTarget, status: "match" };
44
+ return { path: posixTarget, status: "drift" };
45
+ }
46
+ async function diffAgainstRegistry(registryRoot, projectRoot, manifest) {
47
+ const protectedGlobs = resolveProtectedGlobs(manifest);
48
+ const entries = [];
49
+ const seen = /* @__PURE__ */ new Set();
50
+ const pushUnique = async (sourceRel, targetRel) => {
51
+ const key = targetRel.split(path.sep).join("/");
52
+ if (seen.has(key)) return;
53
+ seen.add(key);
54
+ entries.push(
55
+ await comparePair(registryRoot, projectRoot, sourceRel, targetRel, protectedGlobs)
56
+ );
57
+ };
58
+ const { pairs, missingSkills } = await managedPairs(registryRoot, manifest);
59
+ for (const { sourceRel, targetRel } of pairs) {
60
+ await pushUnique(sourceRel, targetRel);
61
+ }
62
+ for (const id of missingSkills) {
63
+ entries.push({ path: `skill:${id}`, status: "missing-registry" });
64
+ }
65
+ return entries;
66
+ }
67
+ function summarizeDiff(entries) {
68
+ const summary = {
69
+ match: 0,
70
+ drift: 0,
71
+ "missing-local": 0,
72
+ "missing-registry": 0,
73
+ protected: 0
74
+ };
75
+ for (const e of entries) summary[e.status] += 1;
76
+ return summary;
77
+ }
78
+
79
+ export {
80
+ diffAgainstRegistry,
81
+ summarizeDiff
82
+ };
@@ -0,0 +1,42 @@
1
+ // src/invariants/handoff-schema.ts
2
+ var MACHINE_LIST_CHECKS = [
3
+ {
4
+ heading: "Backlog plans",
5
+ fieldLabels: ["Backlog plans", "Backlog"],
6
+ code: "heading-without-field-backlog"
7
+ },
8
+ {
9
+ heading: "Parked plans",
10
+ fieldLabels: ["Parked plans"],
11
+ code: "heading-without-field-parked"
12
+ },
13
+ {
14
+ heading: "Run queue",
15
+ fieldLabels: ["Run queue"],
16
+ code: "heading-without-field-run-queue"
17
+ }
18
+ ];
19
+ var CITE = "agent-kit validate handoff (see .cursor/context/templates/handoff.md)";
20
+ function validateHandoffText(text) {
21
+ if (!text.trim()) return [];
22
+ const warnings = [];
23
+ for (const check of MACHINE_LIST_CHECKS) {
24
+ const headingRe = new RegExp(`^##\\s+${check.heading}\\s*$`, "m");
25
+ if (!headingRe.test(text)) continue;
26
+ const hasField = check.fieldLabels.some(
27
+ (label) => new RegExp(`^- \\*\\*${label}:\\*\\*`, "m").test(text)
28
+ );
29
+ if (!hasField) {
30
+ warnings.push({
31
+ code: check.code,
32
+ message: `\`## ${check.heading}\` found without \`- **${check.fieldLabels[0]}:**\` (Mission Control will miss this list; rewrite as a field bullet).`,
33
+ cite: CITE
34
+ });
35
+ }
36
+ }
37
+ return warnings;
38
+ }
39
+
40
+ export {
41
+ validateHandoffText
42
+ };