@dadado/agent-kit-cli 5.15.0 → 5.16.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 (71) 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-7UMSGVWC.js +150 -0
  11. package/dist/chunk-2IPXQFJE.js +253 -0
  12. package/dist/chunk-2YU2H7I6.js +42 -0
  13. package/dist/chunk-4IWG5CVE.js +214 -0
  14. package/dist/chunk-5OXPNVCF.js +106 -0
  15. package/dist/chunk-5VCOWTTP.js +6 -0
  16. package/dist/chunk-6QV2BFGA.js +341 -0
  17. package/dist/chunk-77V75A7O.js +39 -0
  18. package/dist/chunk-7HDQEK6T.js +26 -0
  19. package/dist/chunk-CBCW5R5J.js +484 -0
  20. package/dist/chunk-CGTE7DHN.js +410 -0
  21. package/dist/chunk-DE3LQFSJ.js +18 -0
  22. package/dist/chunk-F6C3GFKV.js +57 -0
  23. package/dist/chunk-HFUH3HVS.js +282 -0
  24. package/dist/chunk-HS7LVVLO.js +74 -0
  25. package/dist/chunk-IHROBTNM.js +135 -0
  26. package/dist/chunk-KD2MYBB6.js +213 -0
  27. package/dist/chunk-KI6ZHBPV.js +332 -0
  28. package/dist/chunk-MAPOQEA2.js +43 -0
  29. package/dist/chunk-NQ7IQ5XT.js +26 -0
  30. package/dist/chunk-OUQZC744.js +1315 -0
  31. package/dist/chunk-OZ4QV6LM.js +1078 -0
  32. package/dist/chunk-PCCQUWE5.js +817 -0
  33. package/dist/chunk-PNFF6OU3.js +273 -0
  34. package/dist/chunk-QTON2GQX.js +203 -0
  35. package/dist/chunk-TFZ2JUXH.js +1081 -0
  36. package/dist/chunk-VGZBARQC.js +318 -0
  37. package/dist/chunk-VS2JD2XC.js +1453 -0
  38. package/dist/chunk-VY7MTPJ2.js +205 -0
  39. package/dist/chunk-WW3YZKHW.js +165 -0
  40. package/dist/chunk-XWPO3VYQ.js +53 -0
  41. package/dist/chunk-XZZAVHH4.js +503 -0
  42. package/dist/chunk-YGBBHKBE.js +82 -0
  43. package/dist/chunk-ZB43YMHW.js +20 -0
  44. package/dist/chunk-ZCTSUQRR.js +874 -0
  45. package/dist/chunk-ZXKZBTXE.js +17 -0
  46. package/dist/contribute-YKK6YCSI.js +367 -0
  47. package/dist/cursor-awareness-KTL2RGKD.js +77 -0
  48. package/dist/dashboard-YSNUGBFY.js +21 -0
  49. package/dist/dashboard-broadcast-M4CMW7RX.js +119 -0
  50. package/dist/diff-YTR7VOAN.js +74 -0
  51. package/dist/doctor-TA2FWREU.js +497 -0
  52. package/dist/dogfood-KKFVRWT6.js +366 -0
  53. package/dist/guard-EHXF76ID.js +530 -0
  54. package/dist/handoff-3UI6VKVG.js +302 -0
  55. package/dist/hook-MUV6Y7UW.js +1074 -0
  56. package/dist/index.js +62 -14068
  57. package/dist/init-TTTKGC4C.js +127 -0
  58. package/dist/install-ZMFJBPOI.js +34 -0
  59. package/dist/mission-control-LJACEVVN.js +89 -0
  60. package/dist/monitors-QV2IS3MP.js +176 -0
  61. package/dist/plan-index-JMC7Z4DW.js +44 -0
  62. package/dist/public-inbound-radar-WP3XF7GS.js +68 -0
  63. package/dist/remote-issues-SZ4HDQL5.js +44 -0
  64. package/dist/run-I3BFGULT.js +243 -0
  65. package/dist/run-plan-FO7CL4M3.js +118 -0
  66. package/dist/scan-NI2SWJ2E.js +33 -0
  67. package/dist/setup-global-JUVMJXJ5.js +340 -0
  68. package/dist/status-MVCUNCWX.js +138 -0
  69. package/dist/update-Q4EBIRUB.js +260 -0
  70. package/dist/validate-V2CW7J4B.js +201 -0
  71. package/package.json +1 -1
@@ -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,332 @@
1
+ import {
2
+ createReadinessReport
3
+ } from "./chunk-PNFF6OU3.js";
4
+ import {
5
+ KIT_OWNED_IGNORE_PATTERNS,
6
+ REPOSITORY_PROFILE_REL,
7
+ REQUIRED_SECRET_PATTERNS,
8
+ detectGitDirty,
9
+ runScanner
10
+ } from "./chunk-ZCTSUQRR.js";
11
+ import {
12
+ CONTEXT_CONFIG_REL,
13
+ contextConfigPath
14
+ } from "./chunk-NQ7IQ5XT.js";
15
+ import {
16
+ ensureDir,
17
+ fileExists,
18
+ readJson,
19
+ writeJson
20
+ } from "./chunk-77V75A7O.js";
21
+
22
+ // src/scanner/safe-fixes.ts
23
+ import { readFile, writeFile } from "fs/promises";
24
+ import path from "path";
25
+ var CONTEXT_CONFIG_REL_POSIX = CONTEXT_CONFIG_REL.replace(/\\/g, "/");
26
+ var ESSENTIAL_DIRECTORIES = [
27
+ ".cursor",
28
+ ".cursor/context",
29
+ ".cursor/context/current",
30
+ ".cursor/context/backups",
31
+ ".cursor/plans",
32
+ ".cursor/memory"
33
+ ];
34
+ function isObject(value) {
35
+ return typeof value === "object" && value !== null && !Array.isArray(value);
36
+ }
37
+ function mergeMissing(existing, defaults) {
38
+ if (!isObject(existing) || !isObject(defaults)) return existing ?? defaults;
39
+ const merged = { ...existing };
40
+ for (const [key, value] of Object.entries(defaults)) {
41
+ merged[key] = key in existing ? mergeMissing(existing[key], value) : value;
42
+ }
43
+ return merged;
44
+ }
45
+ function jsonEqual(left, right) {
46
+ return JSON.stringify(left) === JSON.stringify(right);
47
+ }
48
+ function relativeEvidence(relativePath, detail) {
49
+ return [`${relativePath}: ${detail}`];
50
+ }
51
+ function createProfile(scan, report, generatedAt) {
52
+ const git = {
53
+ mode: scan.git.mode,
54
+ workflow: scan.git.workflow,
55
+ remotes: scan.git.remotes,
56
+ remoteUrl: scan.git.remoteUrl,
57
+ remoteName: scan.git.remoteName,
58
+ currentBranch: scan.git.currentBranch,
59
+ defaultBranch: scan.git.defaultBranch,
60
+ isDirty: scan.git.isDirty,
61
+ hasLocalStaging: scan.git.hasLocalStaging,
62
+ hasRemoteStaging: scan.git.hasRemoteStaging
63
+ };
64
+ if (scan.git.providerConfidence === "high") {
65
+ git.provider = scan.git.provider;
66
+ git.providerKind = scan.git.providerKind;
67
+ git.providerConfidence = scan.git.providerConfidence;
68
+ git.providerEvidence = scan.git.providerEvidence;
69
+ }
70
+ return {
71
+ schemaVersion: 1,
72
+ contractVersion: 1,
73
+ purpose: scan.purpose,
74
+ stack: scan.stack,
75
+ git,
76
+ infra: scan.infra,
77
+ services: scan.services,
78
+ context: scan.context,
79
+ detection: {
80
+ generatedAt,
81
+ repositoryFingerprint: report.repositoryFingerprint,
82
+ providerConfidence: scan.git.providerConfidence,
83
+ providerEvidence: scan.git.providerEvidence
84
+ }
85
+ };
86
+ }
87
+ function createOnboardingState(report, generatedAt, deferredItems = report.deferredChecks) {
88
+ const validDeferredCheckIds = new Set(
89
+ deferredItems.filter((item) => item.reason.trim().length > 0).map((item) => item.checkId)
90
+ );
91
+ const unresolvedEssential = report.pillars.flatMap((pillar) => pillar.checks).some((check) => {
92
+ if (!check.essential || check.status === "ready") return false;
93
+ if (check.status === "blocked") return true;
94
+ if (!validDeferredCheckIds.has(check.id)) return true;
95
+ return check.actions.length > 0;
96
+ });
97
+ return {
98
+ contractVersion: 1,
99
+ status: unresolvedEssential ? "in_progress" : "completed",
100
+ updatedAt: generatedAt,
101
+ checks: Object.fromEntries(
102
+ report.pillars.flatMap((pillar) => pillar.checks).map((check) => [
103
+ check.id,
104
+ {
105
+ status: check.status,
106
+ essential: check.essential,
107
+ evidence: check.evidence
108
+ }
109
+ ])
110
+ ),
111
+ deferredItems
112
+ };
113
+ }
114
+ function validDeferredItems(value, fallback) {
115
+ if (!Array.isArray(value)) return fallback;
116
+ return value.filter(
117
+ (item) => isObject(item) && typeof item.checkId === "string" && typeof item.reason === "string" && (item.recoveryCommand === void 0 || typeof item.recoveryCommand === "string")
118
+ );
119
+ }
120
+ function reconcileOnboardingState(report, existingConfig, generatedAt) {
121
+ const existing = isObject(existingConfig.onboarding) ? existingConfig.onboarding : {};
122
+ const deferredItems = validDeferredItems(existing.deferredItems, report.deferredChecks);
123
+ const derived = createOnboardingState(report, generatedAt, deferredItems);
124
+ const candidate = {
125
+ ...existing,
126
+ ...derived
127
+ };
128
+ const { updatedAt: _candidateUpdatedAt, ...candidateState } = candidate;
129
+ const { updatedAt: existingUpdatedAt, ...existingState } = existing;
130
+ candidate.updatedAt = jsonEqual(candidateState, existingState) && typeof existingUpdatedAt === "string" ? existingUpdatedAt : generatedAt;
131
+ return candidate;
132
+ }
133
+ function preferenceDefaults(onboarding, onboarded) {
134
+ return {
135
+ onboarded: onboarded === true,
136
+ onboarding,
137
+ externalPlanReview: {
138
+ enabled: false,
139
+ backend: "claude",
140
+ autoRemediate: false,
141
+ offerOnExhausted: true,
142
+ mode: "paste",
143
+ midBatchAudits: false,
144
+ preflight: "off"
145
+ },
146
+ autoHandoff: false,
147
+ agentPersona: {
148
+ default: "autopilot",
149
+ modes: {
150
+ "continue-plan": "autopilot",
151
+ "run-plan": "night-shift",
152
+ "cli-run-plan": "ghost-runner"
153
+ }
154
+ }
155
+ };
156
+ }
157
+ function mergeIgnorePatterns(existing, patterns) {
158
+ const activeLines = new Set(
159
+ existing.split(/\r?\n/).map((line) => line.trim()).filter((line) => line && !line.startsWith("#"))
160
+ );
161
+ const missing = patterns.filter((pattern) => !activeLines.has(pattern));
162
+ if (missing.length === 0) return existing;
163
+ const prefix = existing.length === 0 ? "" : existing.endsWith("\n") ? existing : `${existing}
164
+ `;
165
+ return `${prefix}${missing.join("\n")}
166
+ `;
167
+ }
168
+ function mergeSecretIgnores(existing) {
169
+ return mergeIgnorePatterns(existing, REQUIRED_SECRET_PATTERNS);
170
+ }
171
+ function mergeKitOwnedIgnores(existing) {
172
+ return mergeIgnorePatterns(existing, KIT_OWNED_IGNORE_PATTERNS);
173
+ }
174
+ function recordChange(changes, id, relativePath, changed, dryRun, evidence) {
175
+ changes.push({
176
+ id,
177
+ path: relativePath,
178
+ status: changed ? dryRun ? "planned" : "applied" : "skipped",
179
+ evidence
180
+ });
181
+ }
182
+ function appliedActions(changes) {
183
+ return changes.filter((change) => change.status === "applied").map((change) => ({
184
+ id: change.id,
185
+ status: "ready",
186
+ recommendation: `Applied safe local change to ${change.path}`,
187
+ owner: "system"
188
+ }));
189
+ }
190
+ function withoutGeneratedAtForComparison(profile) {
191
+ const { generatedAt: _generatedAt, ...detection } = isObject(profile.detection) ? profile.detection : {};
192
+ return { ...profile, detection };
193
+ }
194
+ function reconcileFreshOverExisting(desired, existing) {
195
+ const merged = { ...existing };
196
+ for (const [key, value] of Object.entries(desired)) {
197
+ const existingValue = existing[key];
198
+ merged[key] = isObject(value) && isObject(existingValue) ? reconcileFreshOverExisting(value, existingValue) : value;
199
+ }
200
+ return merged;
201
+ }
202
+ async function refreshRepositoryProfile(rootDir, options) {
203
+ const generatedAt = options.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString();
204
+ const scan = await runScanner(rootDir);
205
+ const report = createReadinessReport(scan, {
206
+ generatorVersion: options.generatorVersion,
207
+ generatedAt
208
+ });
209
+ const desiredProfile = createProfile(scan, report, generatedAt);
210
+ const profilePath = path.join(scan.rootDir, REPOSITORY_PROFILE_REL);
211
+ const existingProfile = await readJson(profilePath) ?? {};
212
+ const reconciled = reconcileFreshOverExisting(desiredProfile, existingProfile);
213
+ const meaningfulChange = !jsonEqual(
214
+ withoutGeneratedAtForComparison(existingProfile),
215
+ withoutGeneratedAtForComparison(reconciled)
216
+ );
217
+ if (!meaningfulChange) {
218
+ return { changed: false, profile: existingProfile };
219
+ }
220
+ await writeJson(profilePath, reconciled);
221
+ return { changed: true, profile: reconciled };
222
+ }
223
+ async function executeSafeReadinessFixes(rootDir, options) {
224
+ const dryRun = options.dryRun ?? false;
225
+ const generatedAt = options.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString();
226
+ const beforeScan = await runScanner(rootDir);
227
+ const before = createReadinessReport(beforeScan, {
228
+ generatorVersion: options.generatorVersion,
229
+ generatedAt
230
+ });
231
+ const changes = [];
232
+ for (const relativePath of ESSENTIAL_DIRECTORIES) {
233
+ const absolutePath = path.join(beforeScan.rootDir, relativePath);
234
+ const exists = await fileExists(absolutePath);
235
+ if (!exists && !dryRun) await ensureDir(absolutePath);
236
+ recordChange(
237
+ changes,
238
+ "ensure-agent-kit-directory",
239
+ relativePath,
240
+ !exists,
241
+ dryRun,
242
+ relativeEvidence(relativePath, exists ? "already exists" : "missing directory")
243
+ );
244
+ }
245
+ const gitignoreRelativePath = ".gitignore";
246
+ const gitignorePath = path.join(beforeScan.rootDir, gitignoreRelativePath);
247
+ const existingGitignore = await fileExists(gitignorePath) ? await readFile(gitignorePath, "utf8") : "";
248
+ const gitignoreAfterSecrets = mergeSecretIgnores(existingGitignore);
249
+ const secretsChanged = gitignoreAfterSecrets !== existingGitignore;
250
+ const mergedGitignore = mergeKitOwnedIgnores(gitignoreAfterSecrets);
251
+ const gitignoreChanged = mergedGitignore !== existingGitignore;
252
+ const kitOwnedChanged = mergedGitignore !== gitignoreAfterSecrets;
253
+ if (gitignoreChanged && !dryRun) await writeFile(gitignorePath, mergedGitignore, "utf8");
254
+ recordChange(
255
+ changes,
256
+ "merge-secret-ignores",
257
+ gitignoreRelativePath,
258
+ secretsChanged,
259
+ dryRun,
260
+ relativeEvidence(
261
+ gitignoreRelativePath,
262
+ secretsChanged ? "required secret patterns are missing" : "required patterns are present"
263
+ )
264
+ );
265
+ recordChange(
266
+ changes,
267
+ "merge-kit-owned-ignores",
268
+ gitignoreRelativePath,
269
+ kitOwnedChanged,
270
+ dryRun,
271
+ relativeEvidence(
272
+ gitignoreRelativePath,
273
+ kitOwnedChanged ? "kit-owned session/derived paths are missing" : "kit-owned session/derived paths are present"
274
+ )
275
+ );
276
+ const profilePath = path.join(beforeScan.rootDir, REPOSITORY_PROFILE_REL);
277
+ const existingProfile = await readJson(profilePath) ?? {};
278
+ const desiredProfile = createProfile(beforeScan, before, generatedAt);
279
+ const mergedProfile = mergeMissing(existingProfile, desiredProfile);
280
+ const profileChanged = !jsonEqual(existingProfile, mergedProfile);
281
+ if (profileChanged && !dryRun) await writeJson(profilePath, mergedProfile);
282
+ recordChange(
283
+ changes,
284
+ "merge-repository-profile",
285
+ REPOSITORY_PROFILE_REL,
286
+ profileChanged,
287
+ dryRun,
288
+ relativeEvidence(
289
+ REPOSITORY_PROFILE_REL,
290
+ profileChanged ? "missing scanner-derived facts" : "existing facts preserved"
291
+ )
292
+ );
293
+ const evidenceScan = dryRun ? beforeScan : await runScanner(beforeScan.rootDir);
294
+ const evidenceReport = createReadinessReport(evidenceScan, {
295
+ generatorVersion: options.generatorVersion,
296
+ generatedAt
297
+ });
298
+ const configPath = contextConfigPath(beforeScan.rootDir);
299
+ const existingContextConfig = await readJson(configPath) ?? {};
300
+ const onboarding = reconcileOnboardingState(evidenceReport, existingContextConfig, generatedAt);
301
+ const defaults = preferenceDefaults(onboarding, existingContextConfig.onboarded);
302
+ const mergedContextConfig = mergeMissing(existingContextConfig, defaults);
303
+ mergedContextConfig.onboarding = onboarding;
304
+ const contextConfigChanged = !jsonEqual(existingContextConfig, mergedContextConfig);
305
+ if (contextConfigChanged && !dryRun) await writeJson(configPath, mergedContextConfig);
306
+ recordChange(
307
+ changes,
308
+ "merge-onboarding-state",
309
+ CONTEXT_CONFIG_REL_POSIX,
310
+ contextConfigChanged,
311
+ dryRun,
312
+ relativeEvidence(
313
+ CONTEXT_CONFIG_REL_POSIX,
314
+ contextConfigChanged ? "missing preferences or onboarding state" : "state already merged"
315
+ )
316
+ );
317
+ const afterScan = contextConfigChanged && !dryRun && evidenceScan.git.mode !== "none" ? {
318
+ ...evidenceScan,
319
+ git: { ...evidenceScan.git, isDirty: await detectGitDirty(evidenceScan.rootDir) }
320
+ } : evidenceScan;
321
+ const after = createReadinessReport(afterScan, {
322
+ generatorVersion: options.generatorVersion,
323
+ generatedAt
324
+ });
325
+ after.appliedSafeFixes = appliedActions(changes);
326
+ return { dryRun, changes, before, after };
327
+ }
328
+
329
+ export {
330
+ refreshRepositoryProfile,
331
+ executeSafeReadinessFixes
332
+ };
@@ -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
+ };
@@ -0,0 +1,26 @@
1
+ import {
2
+ readJson
3
+ } from "./chunk-77V75A7O.js";
4
+
5
+ // src/lifecycle/context-config.ts
6
+ import path from "path";
7
+ var CONTEXT_CONFIG_REL = path.join(".cursor", "context", "config.json");
8
+ function contextConfigPath(cwd) {
9
+ return path.join(cwd, CONTEXT_CONFIG_REL);
10
+ }
11
+ async function loadContextConfig(cwd) {
12
+ return readJson(contextConfigPath(cwd));
13
+ }
14
+ function intervalElapsed(lastCheckedAt, intervalDays) {
15
+ if (!lastCheckedAt) return true;
16
+ const last = Date.parse(lastCheckedAt);
17
+ if (Number.isNaN(last)) return true;
18
+ return Date.now() - last >= intervalDays * 24 * 60 * 60 * 1e3;
19
+ }
20
+
21
+ export {
22
+ CONTEXT_CONFIG_REL,
23
+ contextConfigPath,
24
+ loadContextConfig,
25
+ intervalElapsed
26
+ };