@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,74 @@
1
+ import {
2
+ logger
3
+ } from "./chunk-ZB43YMHW.js";
4
+
5
+ // src/lifecycle/report.ts
6
+ function logApplyStats(stats) {
7
+ if (stats.written.length > 0) {
8
+ logger.success(`Wrote ${stats.written.length} file(s)`);
9
+ for (const p of stats.written) logger.info(` + ${p}`);
10
+ }
11
+ if (stats.unchanged.length > 0) {
12
+ logger.info(`Unchanged: ${stats.unchanged.length}`);
13
+ }
14
+ if (stats.removed.length > 0) {
15
+ logger.info(`Removed managed legacy files: ${stats.removed.length}`);
16
+ for (const p of stats.removed) logger.info(` - ${p}`);
17
+ }
18
+ if (stats.collisions.length > 0) {
19
+ logger.warn("Slash collision preserved because the legacy command is customized:");
20
+ for (const p of stats.collisions) logger.info(` ! ${p}`);
21
+ }
22
+ if (stats.preservedCustomized.length > 0) {
23
+ logger.warn(
24
+ `Preserved customized overlay (agents/skills/commands/hooks/scripts); local body kept, kit body not applied: ${stats.preservedCustomized.length}`
25
+ );
26
+ for (const p of stats.preservedCustomized) logger.info(` ! ${p}`);
27
+ logger.info(
28
+ " Run `agent-kit diff` to see the kit body; send the change upstream (`agent-kit contribute` for agents/skills/commands/hooks, a factory PR for scripts); or add the path to `protected` in .cursor/agent-kit.json to keep it pinned on purpose."
29
+ );
30
+ }
31
+ if (stats.skippedProtected.length > 0) {
32
+ logger.warn(`Skipped protected (L3): ${stats.skippedProtected.length}`);
33
+ for (const p of stats.skippedProtected) logger.info(` ~ ${p}`);
34
+ }
35
+ if (stats.skippedSymlink.length > 0) {
36
+ logger.warn(
37
+ `Skipped symlinked path (resolves outside the registry/project root, or is a symlink): ${stats.skippedSymlink.length}`
38
+ );
39
+ for (const p of stats.skippedSymlink) logger.info(` ~ ${p}`);
40
+ }
41
+ if (stats.missing.length > 0) {
42
+ logger.warn(`Missing in registry: ${stats.missing.length}`);
43
+ for (const p of stats.missing) logger.info(` ? ${p}`);
44
+ }
45
+ }
46
+ function listSkippedPaths(paths) {
47
+ const shown = paths.slice(0, 5).join(", ");
48
+ const more = paths.length > 5 ? ` (+${paths.length - 5} more)` : "";
49
+ return `${shown}${more}`;
50
+ }
51
+ function formatSkippedSymlinks(paths) {
52
+ return `${paths.length} kit path(s) were not written because they resolve outside the project (symlink): ${listSkippedPaths(paths)}. Manifest version left unchanged; replace the symlinked directory with a real one inside the project and re-run.`;
53
+ }
54
+ function formatPersonalizationSkippedSymlinks(paths, notRecorded) {
55
+ const ids = notRecorded.length > 0 ? notRecorded.join(", ") : "none";
56
+ return `${paths.length} personalization path(s) were not written because they resolve outside the project (symlink): ${listSkippedPaths(paths)}. Manifest saved without: ${ids}; replace the symlinked directory with a real one inside the project and re-run.`;
57
+ }
58
+ var SkippedSymlinkError = class extends Error {
59
+ constructor(stats, message = formatSkippedSymlinks(stats.skippedSymlink), claudeSessionStartInstructions) {
60
+ super(message);
61
+ this.stats = stats;
62
+ this.claudeSessionStartInstructions = claudeSessionStartInstructions;
63
+ this.name = "SkippedSymlinkError";
64
+ }
65
+ stats;
66
+ claudeSessionStartInstructions;
67
+ };
68
+
69
+ export {
70
+ logApplyStats,
71
+ formatSkippedSymlinks,
72
+ formatPersonalizationSkippedSymlinks,
73
+ SkippedSymlinkError
74
+ };
@@ -0,0 +1,135 @@
1
+ // src/invariants/handoff-prune.ts
2
+ var HANDOFF_MACHINE_FIELD_LABELS = [
3
+ "Plan",
4
+ "Last updated",
5
+ "Mode",
6
+ "Phase completed",
7
+ "Next phase",
8
+ "Completed to-dos",
9
+ "Next to-dos",
10
+ "Parked plans",
11
+ "Backlog plans",
12
+ "Run queue",
13
+ "Queue cursor",
14
+ "Queue status",
15
+ "Queue outcomes",
16
+ "Gaps",
17
+ "Instruction for the next agent"
18
+ ];
19
+ var HANDOFF_FIXED_SECTION_HEADING_PREFIXES = [
20
+ "## Work Status",
21
+ "## Session Context",
22
+ "## Files Touched",
23
+ "## Decisions Made",
24
+ "## Issues Found",
25
+ "## Run queue (multi-plan only)"
26
+ ];
27
+ var HANDOFF_MAX_LINES = 200;
28
+ var HANDOFF_MAX_LINE_LENGTH = 2e3;
29
+ var MACHINE_FIELD_RE = new RegExp(
30
+ `^- \\*\\*(${HANDOFF_MACHINE_FIELD_LABELS.map((l) => l.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")}):\\*\\*`
31
+ );
32
+ function isChunkStart(line) {
33
+ return /^- \*\*/.test(line) || /^## /.test(line);
34
+ }
35
+ function isFixedSectionHeading(line) {
36
+ return HANDOFF_FIXED_SECTION_HEADING_PREFIXES.some((p) => line.startsWith(p));
37
+ }
38
+ function splitIntoChunks(lines) {
39
+ const firstStart = lines.findIndex(isChunkStart);
40
+ if (firstStart === -1) return { preamble: lines, chunks: [] };
41
+ const preamble = lines.slice(0, firstStart);
42
+ const chunks = [];
43
+ let current = [lines[firstStart]];
44
+ for (const line of lines.slice(firstStart + 1)) {
45
+ if (isChunkStart(line)) {
46
+ chunks.push({ lines: current, isMachineField: MACHINE_FIELD_RE.test(current[0] ?? "") });
47
+ current = [line];
48
+ } else {
49
+ current.push(line);
50
+ }
51
+ }
52
+ chunks.push({ lines: current, isMachineField: MACHINE_FIELD_RE.test(current[0] ?? "") });
53
+ return { preamble, chunks };
54
+ }
55
+ function pruneHandoffText(text, keep) {
56
+ if (!text.trim()) {
57
+ return { ok: false, reason: "empty file" };
58
+ }
59
+ const lines = text.split("\n");
60
+ const titleIdx = lines.findIndex((l) => /^# /.test(l));
61
+ if (titleIdx === -1) {
62
+ return { ok: false, reason: "no H1 title line found" };
63
+ }
64
+ let fixedStartIdx = lines.findIndex((l) => isFixedSectionHeading(l));
65
+ if (fixedStartIdx === -1) fixedStartIdx = lines.length;
66
+ const head = lines.slice(0, titleIdx + 1);
67
+ const narrativeLines = lines.slice(titleIdx + 1, fixedStartIdx);
68
+ const trailingLines = lines.slice(fixedStartIdx);
69
+ const { preamble, chunks } = splitIntoChunks(narrativeLines);
70
+ const machineFieldCount = chunks.filter((c) => c.isMachineField).length;
71
+ if (machineFieldCount === 0) {
72
+ return { ok: false, reason: "no machine-field bullets found; refusing to prune" };
73
+ }
74
+ if (fixedStartIdx === lines.length) {
75
+ return { ok: false, reason: "no recognized fixed-section heading found; refusing to prune" };
76
+ }
77
+ const narrativeChunks = chunks.filter((c) => !c.isMachineField);
78
+ if (narrativeChunks.length <= keep) {
79
+ return { ok: true, text, archived: [], keptCount: narrativeChunks.length };
80
+ }
81
+ const keptNarrativeIdx = /* @__PURE__ */ new Set();
82
+ let seen = 0;
83
+ const chunkIsNarrative = chunks.map((c) => !c.isMachineField);
84
+ for (let i = 0; i < chunks.length; i++) {
85
+ if (chunkIsNarrative[i]) {
86
+ if (seen < keep) keptNarrativeIdx.add(i);
87
+ seen++;
88
+ }
89
+ }
90
+ const keptChunks = [];
91
+ const archivedChunks = [];
92
+ chunks.forEach((c, i) => {
93
+ if (c.isMachineField || keptNarrativeIdx.has(i)) keptChunks.push(c);
94
+ else archivedChunks.push(c);
95
+ });
96
+ const newNarrativeLines = [...preamble, ...keptChunks.flatMap((c) => c.lines)];
97
+ const newLines = [...head, ...newNarrativeLines, ...trailingLines];
98
+ return {
99
+ ok: true,
100
+ text: newLines.join("\n"),
101
+ archived: archivedChunks.map((c) => c.lines.join("\n")),
102
+ keptCount: keptNarrativeIdx.size
103
+ };
104
+ }
105
+ function checkHandoffGrowth(text) {
106
+ if (!text.trim()) return [];
107
+ const lines = text.split("\n");
108
+ const warnings = [];
109
+ if (lines.length > HANDOFF_MAX_LINES) {
110
+ warnings.push({
111
+ code: "file-over-limit",
112
+ message: `HANDOFF.md is ${lines.length} lines, over the ${HANDOFF_MAX_LINES}-line growth-contract limit. Run \`agent-kit handoff --prune\` to archive older narrative entries.`
113
+ });
114
+ }
115
+ let firstOffendingLine = -1;
116
+ let overCount = 0;
117
+ lines.forEach((l, i) => {
118
+ if (l.length > HANDOFF_MAX_LINE_LENGTH) {
119
+ overCount++;
120
+ if (firstOffendingLine === -1) firstOffendingLine = i + 1;
121
+ }
122
+ });
123
+ if (overCount > 0) {
124
+ warnings.push({
125
+ code: "line-over-limit",
126
+ message: `HANDOFF.md has ${overCount} line(s) over the ${HANDOFF_MAX_LINE_LENGTH}-char growth-contract limit (first at line ${firstOffendingLine}). Wrap long paragraphs; do not widen the limit.`
127
+ });
128
+ }
129
+ return warnings;
130
+ }
131
+
132
+ export {
133
+ pruneHandoffText,
134
+ checkHandoffGrowth
135
+ };
@@ -0,0 +1,213 @@
1
+ import {
2
+ HANDOFF_REL
3
+ } from "./chunk-5VCOWTTP.js";
4
+ import {
5
+ ensureDir
6
+ } from "./chunk-77V75A7O.js";
7
+
8
+ // src/plan-index/plan-index.ts
9
+ import { readFile, writeFile } from "fs/promises";
10
+ import path from "path";
11
+ var PLAN_INDEX_REL = ".cursor/context/plan-index.json";
12
+ var NONE = /^(none|n\/a|empty|nil)$/i;
13
+ var OPEN_STATUSES = /* @__PURE__ */ new Set(["pending", "in_progress"]);
14
+ var DONE_STATUSES = /* @__PURE__ */ new Set(["completed", "cancelled"]);
15
+ function defaultPlanIndexIo() {
16
+ return {
17
+ async readFile(absPath) {
18
+ try {
19
+ return await readFile(absPath, "utf8");
20
+ } catch {
21
+ return null;
22
+ }
23
+ },
24
+ async writeFile(absPath, body) {
25
+ await ensureDir(path.dirname(absPath));
26
+ await writeFile(absPath, body, "utf8");
27
+ }
28
+ };
29
+ }
30
+ function extractHandoffNamedPlans(handoffText) {
31
+ return {
32
+ active: extractActivePlan(handoffText),
33
+ backlog: extractPlanBasenames(
34
+ extractFieldBlock(handoffText, "Backlog plans") ?? extractFieldBlock(handoffText, "Backlog")
35
+ ),
36
+ parked: extractPlanBasenames(extractFieldBlock(handoffText, "Parked plans")),
37
+ runQueue: extractPlanBasenames(extractFieldBlock(handoffText, "Run queue"))
38
+ };
39
+ }
40
+ function collectNamedPlanBasenames(named) {
41
+ const seen = /* @__PURE__ */ new Set();
42
+ const out = [];
43
+ for (const file of [named.active, ...named.backlog, ...named.parked, ...named.runQueue]) {
44
+ if (!file || seen.has(file)) continue;
45
+ seen.add(file);
46
+ out.push(file);
47
+ }
48
+ return out;
49
+ }
50
+ function namedPlanCandidatePaths(root, basename) {
51
+ const safe = path.basename(basename);
52
+ if (!/^[A-Za-z0-9._-]+\.plan\.md$/i.test(safe)) return [];
53
+ return [
54
+ path.join(root, ".cursor", "plans", safe),
55
+ path.join(root, ".cursor", "plans", "archive", safe)
56
+ ];
57
+ }
58
+ function classifyPlanRole(file, named, openTodos) {
59
+ if (named.active === file) return "active";
60
+ if (named.backlog.includes(file)) return "backlog";
61
+ if (named.parked.includes(file)) return "parked";
62
+ if (openTodos) return "pending";
63
+ return null;
64
+ }
65
+ function parsePlanFrontmatter(raw) {
66
+ const match = raw.replace(/\r\n/g, "\n").match(/^---\n([\s\S]*?)\n---/);
67
+ if (!match?.[1]) return { todos: [] };
68
+ const block = match[1];
69
+ const name = block.match(/^name:\s*(.+)$/m)?.[1]?.trim();
70
+ const todos = [];
71
+ let currentId;
72
+ let currentContent;
73
+ for (const line of block.split("\n")) {
74
+ const idMatch = line.match(/^\s*- id:\s*(\S+)/);
75
+ if (idMatch?.[1]) {
76
+ currentId = idMatch[1].replace(/^["']|["']$/g, "");
77
+ currentContent = void 0;
78
+ continue;
79
+ }
80
+ const contentMatch = line.match(/^\s*content:\s*(.+)$/);
81
+ if (contentMatch?.[1] && currentId) {
82
+ currentContent = contentMatch[1].trim();
83
+ continue;
84
+ }
85
+ const statusMatch = line.match(/^\s*status:\s*(\S+)/);
86
+ if (statusMatch?.[1] && currentId) {
87
+ const todo = { id: currentId, status: statusMatch[1] };
88
+ if (currentContent !== void 0) todo.content = currentContent;
89
+ todos.push(todo);
90
+ currentId = void 0;
91
+ currentContent = void 0;
92
+ }
93
+ }
94
+ return { name, todos };
95
+ }
96
+ async function buildPlanIndex(root, io = defaultPlanIndexIo(), now = () => /* @__PURE__ */ new Date()) {
97
+ const handoffPath = path.join(root, HANDOFF_REL);
98
+ const handoffText = await io.readFile(handoffPath) ?? "";
99
+ const named = extractHandoffNamedPlans(handoffText);
100
+ const plans = [];
101
+ for (const file of collectNamedPlanBasenames(named)) {
102
+ const text = await readNamedPlanFile(root, file, io);
103
+ const fm = text ? parsePlanFrontmatter(text) : { todos: [] };
104
+ const pendingTodoIds = fm.todos.filter((t) => OPEN_STATUSES.has(t.status)).map((t) => t.id);
105
+ const openTodos = pendingTodoIds.length > 0;
106
+ const role = classifyPlanRole(file, named, openTodos);
107
+ if (!role) continue;
108
+ const exhausted = text !== null && fm.todos.length > 0 && fm.todos.every((t) => DONE_STATUSES.has(t.status));
109
+ const entry = {
110
+ file,
111
+ role,
112
+ openTodos,
113
+ pendingTodoIds,
114
+ exhausted
115
+ };
116
+ if (fm.name) entry.name = fm.name;
117
+ if (!text) entry.missing = true;
118
+ plans.push(entry);
119
+ }
120
+ return {
121
+ generatedAt: now().toISOString(),
122
+ source: "handoff-named",
123
+ plans
124
+ };
125
+ }
126
+ async function writePlanIndex(root, io = defaultPlanIndexIo(), now = () => /* @__PURE__ */ new Date()) {
127
+ const index = await buildPlanIndex(root, io, now);
128
+ const dest = path.join(root, PLAN_INDEX_REL);
129
+ const write = io.writeFile ?? defaultPlanIndexIo().writeFile;
130
+ if (write) {
131
+ await write(dest, `${JSON.stringify(index, null, 2)}
132
+ `);
133
+ }
134
+ return index;
135
+ }
136
+ function formatPlanIndexLines(index) {
137
+ const lines = index.plans.filter((p) => !p.exhausted).map((p) => {
138
+ const open = p.openTodos ? ` (${p.pendingTodoIds.length} open)` : "";
139
+ return `- ${p.role}: \`${p.file}\`${open}`;
140
+ });
141
+ const exhausted = index.plans.filter((p) => p.exhausted).map((p) => `\`${p.file}\``);
142
+ if (exhausted.length > 0) lines.push(`- exhausted (retire): ${exhausted.join(", ")}`);
143
+ return lines;
144
+ }
145
+ function formatPlanIndexSection(index) {
146
+ if (index.plans.length === 0) return null;
147
+ return `## Pending plans (index)
148
+
149
+ ${formatPlanIndexLines(index).join("\n")}`;
150
+ }
151
+ async function readNamedPlanFile(root, basename, io) {
152
+ for (const candidate of namedPlanCandidatePaths(root, basename)) {
153
+ const text = await io.readFile(candidate);
154
+ if (text !== null) return text;
155
+ }
156
+ return null;
157
+ }
158
+ function extractActivePlan(content) {
159
+ const match = content.match(/^- \*\*Plan:\*\*\s*(.+)$/m);
160
+ if (!match?.[1]) return null;
161
+ let raw = match[1].trim();
162
+ const tick = raw.match(/^`([^`]+)`/);
163
+ if (tick?.[1]) raw = tick[1].trim();
164
+ if (!raw || NONE.test(raw)) return null;
165
+ return raw.match(/([A-Za-z0-9._-]+\.plan\.md)/i)?.[1] ?? null;
166
+ }
167
+ function extractFieldBlock(content, label) {
168
+ const escaped = label.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
169
+ const re = new RegExp(`^- \\*\\*${escaped}:\\*\\*\\s*(.*)$`, "m");
170
+ const match = content.match(re);
171
+ if (!match) return null;
172
+ const chunks = [];
173
+ const same = (match[1] ?? "").trim();
174
+ if (same) chunks.push(same);
175
+ const after = content.slice((match.index ?? 0) + match[0].length);
176
+ for (const line of after.split("\n")) {
177
+ if (/^- \*\*[^*:\n]+:\*\*/.test(line)) break;
178
+ if (!line.trim()) continue;
179
+ if (/^\s+\S/.test(line)) {
180
+ chunks.push(line.trim());
181
+ continue;
182
+ }
183
+ break;
184
+ }
185
+ return chunks.join("\n").trim() || null;
186
+ }
187
+ function extractPlanBasenames(raw) {
188
+ if (!raw) return [];
189
+ const text = raw.trim().replace(/^\[/, "").replace(/\]\s*$/, "");
190
+ const ids = [];
191
+ const seen = /* @__PURE__ */ new Set();
192
+ const ticks = [...text.matchAll(/`([^`]+)`/g)].map((m) => m[1]);
193
+ const parts = ticks.length > 0 ? ticks : text.split(/[,;\n]/);
194
+ for (const part of parts) {
195
+ const cleaned = String(part).replace(/\(.*?\)/g, "").trim().replace(/^-\s*/, "").replace(/^\[/, "").replace(/\]$/, "").replace(/^plans\//, "");
196
+ if (!cleaned || NONE.test(cleaned)) continue;
197
+ const base = cleaned.split("/").pop();
198
+ if (!base || !/\.plan\.md$/i.test(base) || seen.has(base)) continue;
199
+ seen.add(base);
200
+ ids.push(base);
201
+ }
202
+ return ids;
203
+ }
204
+
205
+ export {
206
+ extractHandoffNamedPlans,
207
+ namedPlanCandidatePaths,
208
+ parsePlanFrontmatter,
209
+ writePlanIndex,
210
+ formatPlanIndexLines,
211
+ formatPlanIndexSection,
212
+ extractFieldBlock
213
+ };
@@ -0,0 +1,43 @@
1
+ // src/generator/claude-permissions.ts
2
+ var SHARED_DENY_RULES = [
3
+ "Bash(git push * main)",
4
+ "Bash(git push * main *)",
5
+ "Bash(git push *:main*)",
6
+ "Bash(git push *+main*)",
7
+ "Bash(git push *refs/heads/main*)",
8
+ "Bash(git push * master)",
9
+ "Bash(git push * master *)",
10
+ "Bash(git push *:master*)",
11
+ "Bash(git push *+master*)",
12
+ "Bash(git push *refs/heads/master*)",
13
+ "Bash(git push * @)",
14
+ "Bash(git push --force*)",
15
+ "Bash(git push * --force*)",
16
+ "Bash(git push -f*)",
17
+ "Bash(git push * -f*)",
18
+ "Bash(git push *--no-veri*)",
19
+ "Bash(git push * +*)",
20
+ // Any git global option before a push token (-C, -c, -P, --no-pager, --config-env, ...).
21
+ "Bash(git -* push*)"
22
+ ];
23
+ var HEADLESS_ONLY_DENY_RULES = [
24
+ "Bash(ALLOW_MAIN_PUSH=*)",
25
+ "Skill(git-prod)",
26
+ "Skill(kit-prod)",
27
+ "Edit(.cursor/hooks/**)",
28
+ "Write(.cursor/hooks/**)",
29
+ "Edit(.claude/settings.json)",
30
+ "Write(.claude/settings.json)",
31
+ "Edit(packages/cli/dist/**)",
32
+ "Write(packages/cli/dist/**)"
33
+ ];
34
+ var HEADLESS_DENY_RULES = [
35
+ ...SHARED_DENY_RULES,
36
+ ...HEADLESS_ONLY_DENY_RULES
37
+ ];
38
+ var INTERACTIVE_DENY_RULES = [...SHARED_DENY_RULES];
39
+
40
+ export {
41
+ HEADLESS_DENY_RULES,
42
+ INTERACTIVE_DENY_RULES
43
+ };