declare-cc 0.1.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 (52) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +326 -0
  3. package/agents/gsd-codebase-mapper.md +761 -0
  4. package/agents/gsd-debugger.md +1198 -0
  5. package/agents/gsd-executor.md +451 -0
  6. package/agents/gsd-integration-checker.md +440 -0
  7. package/agents/gsd-phase-researcher.md +484 -0
  8. package/agents/gsd-plan-checker.md +625 -0
  9. package/agents/gsd-planner.md +1164 -0
  10. package/agents/gsd-project-researcher.md +618 -0
  11. package/agents/gsd-research-synthesizer.md +236 -0
  12. package/agents/gsd-roadmapper.md +639 -0
  13. package/agents/gsd-verifier.md +555 -0
  14. package/bin/install.js +1815 -0
  15. package/commands/declare/actions.md +78 -0
  16. package/commands/declare/future.md +52 -0
  17. package/commands/declare/milestones.md +81 -0
  18. package/commands/declare/status.md +62 -0
  19. package/commands/gsd/add-phase.md +39 -0
  20. package/commands/gsd/add-todo.md +42 -0
  21. package/commands/gsd/audit-milestone.md +42 -0
  22. package/commands/gsd/check-todos.md +41 -0
  23. package/commands/gsd/cleanup.md +18 -0
  24. package/commands/gsd/complete-milestone.md +136 -0
  25. package/commands/gsd/debug.md +162 -0
  26. package/commands/gsd/discuss-phase.md +87 -0
  27. package/commands/gsd/execute-phase.md +42 -0
  28. package/commands/gsd/health.md +22 -0
  29. package/commands/gsd/help.md +22 -0
  30. package/commands/gsd/insert-phase.md +33 -0
  31. package/commands/gsd/join-discord.md +18 -0
  32. package/commands/gsd/list-phase-assumptions.md +50 -0
  33. package/commands/gsd/map-codebase.md +71 -0
  34. package/commands/gsd/new-milestone.md +51 -0
  35. package/commands/gsd/new-project.md +42 -0
  36. package/commands/gsd/new-project.md.bak +1041 -0
  37. package/commands/gsd/pause-work.md +35 -0
  38. package/commands/gsd/plan-milestone-gaps.md +40 -0
  39. package/commands/gsd/plan-phase.md +44 -0
  40. package/commands/gsd/progress.md +24 -0
  41. package/commands/gsd/quick.md +40 -0
  42. package/commands/gsd/reapply-patches.md +110 -0
  43. package/commands/gsd/remove-phase.md +32 -0
  44. package/commands/gsd/research-phase.md +187 -0
  45. package/commands/gsd/resume-work.md +40 -0
  46. package/commands/gsd/set-profile.md +34 -0
  47. package/commands/gsd/settings.md +36 -0
  48. package/commands/gsd/update.md +37 -0
  49. package/commands/gsd/verify-work.md +39 -0
  50. package/dist/declare-tools.cjs +2962 -0
  51. package/package.json +45 -0
  52. package/scripts/build-hooks.js +42 -0
@@ -0,0 +1,2962 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __commonJS = (cb, mod) => function __require() {
5
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
6
+ };
7
+
8
+ // src/git/commit.js
9
+ var require_commit = __commonJS({
10
+ "src/git/commit.js"(exports2, module2) {
11
+ "use strict";
12
+ var { execFileSync } = require("node:child_process");
13
+ var { readFileSync, existsSync } = require("node:fs");
14
+ var { join } = require("node:path");
15
+ function execGit(cwd, args) {
16
+ try {
17
+ const stdout = execFileSync("git", args, {
18
+ cwd,
19
+ encoding: "utf-8",
20
+ stdio: ["pipe", "pipe", "pipe"]
21
+ });
22
+ return { exitCode: 0, stdout: stdout.trim(), stderr: "" };
23
+ } catch (err) {
24
+ return {
25
+ exitCode: err.status || 1,
26
+ stdout: (err.stdout || "").trim(),
27
+ stderr: (err.stderr || "").trim()
28
+ };
29
+ }
30
+ }
31
+ function loadConfig(cwd) {
32
+ const configPath = join(cwd, ".planning", "config.json");
33
+ if (!existsSync(configPath)) {
34
+ return { commit_docs: true };
35
+ }
36
+ try {
37
+ const raw = readFileSync(configPath, "utf-8");
38
+ const parsed = JSON.parse(raw);
39
+ return { commit_docs: true, ...parsed };
40
+ } catch {
41
+ return { commit_docs: true };
42
+ }
43
+ }
44
+ function isGitIgnored(cwd, path) {
45
+ const result = execGit(cwd, ["check-ignore", "-q", path]);
46
+ return result.exitCode === 0;
47
+ }
48
+ function commitPlanningDocs2(cwd, message, files) {
49
+ const config = loadConfig(cwd);
50
+ if (!config.commit_docs) {
51
+ return { committed: false, reason: "skipped_config" };
52
+ }
53
+ if (isGitIgnored(cwd, ".planning")) {
54
+ return { committed: false, reason: "skipped_gitignored" };
55
+ }
56
+ const filesToStage = files.length > 0 ? files : [".planning/"];
57
+ for (const file of filesToStage) {
58
+ const addResult = execGit(cwd, ["add", file]);
59
+ if (addResult.exitCode !== 0) {
60
+ return { committed: false, reason: "error", error: `Failed to stage ${file}: ${addResult.stderr}` };
61
+ }
62
+ }
63
+ const result = execGit(cwd, ["commit", "-m", message]);
64
+ if (result.exitCode !== 0) {
65
+ const combined = result.stdout + " " + result.stderr;
66
+ if (combined.includes("nothing to commit") || combined.includes("nothing added to commit")) {
67
+ return { committed: false, reason: "nothing_to_commit" };
68
+ }
69
+ return { committed: false, reason: "error", error: result.stderr || result.stdout };
70
+ }
71
+ const hashResult = execGit(cwd, ["rev-parse", "--short", "HEAD"]);
72
+ return { committed: true, hash: hashResult.stdout };
73
+ }
74
+ module2.exports = { commitPlanningDocs: commitPlanningDocs2, loadConfig, execGit };
75
+ }
76
+ });
77
+
78
+ // src/artifacts/future.js
79
+ var require_future = __commonJS({
80
+ "src/artifacts/future.js"(exports2, module2) {
81
+ "use strict";
82
+ function extractField(lines, field) {
83
+ const pattern = new RegExp(`^\\*\\*${field}:\\*\\*`, "i");
84
+ const line = lines.find((l) => pattern.test(l.trim()));
85
+ if (!line) return null;
86
+ return line.trim().replace(/^\*\*[^:]+:\*\*\s*/, "").trim();
87
+ }
88
+ function parseFutureFile(content) {
89
+ if (!content || !content.trim()) return [];
90
+ const declarations = [];
91
+ const sections = content.split(/^## /m).slice(1);
92
+ for (const section of sections) {
93
+ const lines = section.trim().split("\n");
94
+ const headerMatch = lines[0].match(/^(D-\d+):\s*(.+)/);
95
+ if (!headerMatch) continue;
96
+ const [, id, title] = headerMatch;
97
+ const statement = extractField(lines, "Statement") || "";
98
+ const rawStatus = extractField(lines, "Status") || "PENDING";
99
+ const status = rawStatus.toUpperCase().trim();
100
+ const rawMilestones = extractField(lines, "Milestones");
101
+ const milestones = rawMilestones ? rawMilestones.split(",").map((s) => s.trim()).filter(Boolean) : [];
102
+ declarations.push({ id, title: title.trim(), statement, status, milestones });
103
+ }
104
+ return declarations;
105
+ }
106
+ function writeFutureFile(declarations, projectName) {
107
+ const lines = [`# Future: ${projectName}`, ""];
108
+ for (const d of declarations) {
109
+ lines.push(`## ${d.id}: ${d.title}`);
110
+ lines.push(`**Statement:** ${d.statement}`);
111
+ lines.push(`**Status:** ${d.status}`);
112
+ lines.push(`**Milestones:** ${d.milestones.join(", ")}`);
113
+ lines.push("");
114
+ }
115
+ return lines.join("\n");
116
+ }
117
+ function parseFutureArchive(content) {
118
+ if (!content || !content.trim()) return [];
119
+ const entries = [];
120
+ const sections = content.split(/^## /m).slice(1);
121
+ for (const section of sections) {
122
+ const lines = section.trim().split("\n");
123
+ const headerMatch = lines[0].match(/^Archived:\s*(D-\d+)\s*--\s*(.+)/);
124
+ if (!headerMatch) continue;
125
+ const [, id, title] = headerMatch;
126
+ const statement = extractField(lines, "Statement") || "";
127
+ const archivedAt = extractField(lines, "Archived") || "";
128
+ const reason = extractField(lines, "Reason") || "";
129
+ const replacedBy = extractField(lines, "Replaced By") || "";
130
+ const statusAtArchive = extractField(lines, "Status at Archive") || "";
131
+ entries.push({ id, title: title.trim(), statement, archivedAt, reason, replacedBy, statusAtArchive });
132
+ }
133
+ return entries;
134
+ }
135
+ function writeFutureArchive(entries) {
136
+ const lines = ["# Future Archive", ""];
137
+ for (let i = 0; i < entries.length; i++) {
138
+ const e = entries[i];
139
+ if (i > 0) lines.push("---", "");
140
+ lines.push(`## Archived: ${e.id} -- ${e.title}`);
141
+ lines.push(`**Statement:** ${e.statement}`);
142
+ lines.push(`**Archived:** ${e.archivedAt}`);
143
+ lines.push(`**Reason:** ${e.reason}`);
144
+ lines.push(`**Replaced By:** ${e.replacedBy}`);
145
+ lines.push(`**Status at Archive:** ${e.statusAtArchive}`);
146
+ lines.push("");
147
+ }
148
+ return lines.join("\n");
149
+ }
150
+ function appendToArchive(existingContent, entry) {
151
+ const entries = parseFutureArchive(existingContent || "");
152
+ entries.push(entry);
153
+ return writeFutureArchive(entries);
154
+ }
155
+ module2.exports = { parseFutureFile, writeFutureFile, parseFutureArchive, writeFutureArchive, appendToArchive };
156
+ }
157
+ });
158
+
159
+ // src/artifacts/milestones.js
160
+ var require_milestones = __commonJS({
161
+ "src/artifacts/milestones.js"(exports2, module2) {
162
+ "use strict";
163
+ function parseMarkdownTable(text) {
164
+ const lines = text.trim().split("\n").filter((l) => l.trim().startsWith("|"));
165
+ if (lines.length < 2) return [];
166
+ const headers = lines[0].split("|").map((h) => h.trim()).filter(Boolean);
167
+ return lines.slice(2).map((line) => {
168
+ const cells = line.split("|").map((c) => c.trim()).filter(Boolean);
169
+ const row = {};
170
+ headers.forEach((h, i) => {
171
+ row[h] = cells[i] || "";
172
+ });
173
+ return row;
174
+ });
175
+ }
176
+ function splitMultiValue(value) {
177
+ if (!value || !value.trim()) return [];
178
+ return value.split(",").map((s) => s.trim()).filter(Boolean);
179
+ }
180
+ function parseMilestonesFile(content) {
181
+ if (!content || !content.trim()) {
182
+ return { milestones: [] };
183
+ }
184
+ const milestonesMatch = content.match(/## Milestones\s*\n([\s\S]*?)(?=## |$)/i);
185
+ const milestoneRows = milestonesMatch ? parseMarkdownTable(milestonesMatch[1]) : [];
186
+ const milestones = milestoneRows.map((row) => ({
187
+ id: (row["ID"] || "").trim(),
188
+ title: (row["Title"] || "").trim(),
189
+ status: (row["Status"] || "PENDING").trim().toUpperCase(),
190
+ realizes: splitMultiValue(row["Realizes"] || ""),
191
+ hasPlan: (row["Plan"] || "").trim().toUpperCase() === "YES"
192
+ })).filter((m) => m.id);
193
+ return { milestones };
194
+ }
195
+ function pad(str, width) {
196
+ return str + " ".repeat(Math.max(0, width - str.length));
197
+ }
198
+ function writeMilestonesFile(milestones, projectNameOrActions, maybeProjectName) {
199
+ const projectName = maybeProjectName || (typeof projectNameOrActions === "string" ? projectNameOrActions : "Project");
200
+ const lines = [`# Milestones: ${projectName}`, ""];
201
+ lines.push("## Milestones", "");
202
+ const mHeaders = ["ID", "Title", "Status", "Realizes", "Plan"];
203
+ const mRows = milestones.map((m) => [
204
+ m.id,
205
+ m.title,
206
+ m.status,
207
+ m.realizes.join(", "),
208
+ m.hasPlan ? "YES" : "NO"
209
+ ]);
210
+ lines.push(...formatTable(mHeaders, mRows));
211
+ lines.push("");
212
+ return lines.join("\n");
213
+ }
214
+ function formatTable(headers, rows) {
215
+ const widths = headers.map((h, i) => {
216
+ const cellWidths = rows.map((r) => (r[i] || "").length);
217
+ return Math.max(h.length, ...cellWidths);
218
+ });
219
+ const headerLine = "| " + headers.map((h, i) => pad(h, widths[i])).join(" | ") + " |";
220
+ const separatorLine = "|" + widths.map((w) => "-".repeat(w + 2)).join("|") + "|";
221
+ const dataLines = rows.map(
222
+ (row) => "| " + row.map((cell, i) => pad(cell, widths[i])).join(" | ") + " |"
223
+ );
224
+ return [headerLine, separatorLine, ...dataLines];
225
+ }
226
+ module2.exports = { parseMilestonesFile, writeMilestonesFile, parseMarkdownTable };
227
+ }
228
+ });
229
+
230
+ // src/commands/init.js
231
+ var require_init = __commonJS({
232
+ "src/commands/init.js"(exports2, module2) {
233
+ "use strict";
234
+ var { existsSync, mkdirSync, writeFileSync, readFileSync } = require("node:fs");
235
+ var { join, basename } = require("node:path");
236
+ var { writeFutureFile } = require_future();
237
+ var { writeMilestonesFile } = require_milestones();
238
+ var { commitPlanningDocs: commitPlanningDocs2 } = require_commit();
239
+ function runInit2(cwd, args) {
240
+ const projectName = args && args[0] || basename(cwd);
241
+ const planningDir = join(cwd, ".planning");
242
+ const milestonesDir = join(planningDir, "milestones");
243
+ const artifacts = [
244
+ { name: "FUTURE.md", path: join(planningDir, "FUTURE.md") },
245
+ { name: "MILESTONES.md", path: join(planningDir, "MILESTONES.md") },
246
+ { name: "config.json", path: join(planningDir, "config.json") }
247
+ ];
248
+ const created = [];
249
+ const existing = [];
250
+ if (!existsSync(planningDir)) {
251
+ mkdirSync(planningDir, { recursive: true });
252
+ }
253
+ if (!existsSync(milestonesDir)) {
254
+ mkdirSync(milestonesDir, { recursive: true });
255
+ }
256
+ for (const artifact of artifacts) {
257
+ if (existsSync(artifact.path)) {
258
+ existing.push(artifact.name);
259
+ } else {
260
+ let content;
261
+ switch (artifact.name) {
262
+ case "FUTURE.md":
263
+ content = writeFutureFile([], projectName);
264
+ break;
265
+ case "MILESTONES.md":
266
+ content = writeMilestonesFile([], projectName);
267
+ break;
268
+ case "config.json":
269
+ content = JSON.stringify({ commit_docs: true }, null, 2) + "\n";
270
+ break;
271
+ }
272
+ writeFileSync(artifact.path, content, "utf-8");
273
+ created.push(artifact.name);
274
+ }
275
+ }
276
+ if (created.length === 0) {
277
+ return {
278
+ initialized: true,
279
+ created,
280
+ existing,
281
+ committed: false
282
+ };
283
+ }
284
+ const filesToCommit = created.map((name) => join(".planning", name));
285
+ const commitResult = commitPlanningDocs2(
286
+ cwd,
287
+ `docs(declare): initialize project "${projectName}"`,
288
+ filesToCommit
289
+ );
290
+ return {
291
+ initialized: true,
292
+ created,
293
+ existing,
294
+ committed: commitResult.committed,
295
+ hash: commitResult.hash
296
+ };
297
+ }
298
+ module2.exports = { runInit: runInit2 };
299
+ }
300
+ });
301
+
302
+ // src/artifacts/plan.js
303
+ var require_plan = __commonJS({
304
+ "src/artifacts/plan.js"(exports2, module2) {
305
+ "use strict";
306
+ function extractField(lines, field) {
307
+ const pattern = new RegExp(`^\\*\\*${field}:\\*\\*`, "i");
308
+ const line = lines.find((l) => pattern.test(l.trim()));
309
+ if (!line) return null;
310
+ return line.trim().replace(/^\*\*[^:]+:\*\*\s*/, "").trim();
311
+ }
312
+ function parsePlanFile(content) {
313
+ if (!content || !content.trim()) {
314
+ return { milestone: null, realizes: [], status: "PENDING", derived: "", actions: [] };
315
+ }
316
+ const headerMatch = content.match(/^# Plan:\s*(M-\d+)/m);
317
+ const milestone = headerMatch ? headerMatch[1] : null;
318
+ const headerSection = content.split(/^## /m)[0] || "";
319
+ const headerLines = headerSection.split("\n");
320
+ const realizesRaw = extractField(headerLines, "Realizes");
321
+ const realizes = realizesRaw ? realizesRaw.split(",").map((s) => s.trim()).filter(Boolean) : [];
322
+ const status = (extractField(headerLines, "Status") || "PENDING").toUpperCase();
323
+ const derived = extractField(headerLines, "Derived") || "";
324
+ const actionSections = content.split(/^### /m).slice(1);
325
+ const actions = actionSections.map((section) => {
326
+ const lines = section.trim().split("\n");
327
+ const actionHeaderMatch = lines[0].match(/^(A-\d+):\s*(.+)/);
328
+ if (!actionHeaderMatch) return null;
329
+ const [, id, title] = actionHeaderMatch;
330
+ const actionStatus = (extractField(lines, "Status") || "PENDING").toUpperCase();
331
+ const produces = extractField(lines, "Produces") || "";
332
+ const description = lines.slice(1).filter((l) => !l.trim().startsWith("**")).map((l) => l.trim()).filter(Boolean).join("\n");
333
+ return { id, title: title.trim(), status: actionStatus, produces, description };
334
+ }).filter(Boolean);
335
+ return { milestone, realizes, status, derived, actions };
336
+ }
337
+ function writePlanFile(milestoneId, milestoneTitle, realizes, actions) {
338
+ const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
339
+ const lines = [
340
+ `# Plan: ${milestoneId} -- ${milestoneTitle}`,
341
+ "",
342
+ `**Milestone:** ${milestoneId}`,
343
+ `**Realizes:** ${realizes.join(", ")}`,
344
+ `**Status:** PENDING`,
345
+ `**Derived:** ${today}`,
346
+ "",
347
+ "## Actions",
348
+ ""
349
+ ];
350
+ for (const action of actions) {
351
+ const id = action.id || "A-XX";
352
+ const status = action.status || "PENDING";
353
+ const produces = action.produces || "";
354
+ lines.push(`### ${id}: ${action.title}`);
355
+ lines.push(`**Status:** ${status}`);
356
+ if (produces) {
357
+ lines.push(`**Produces:** ${produces}`);
358
+ }
359
+ if (action.description) {
360
+ lines.push(action.description);
361
+ }
362
+ lines.push("");
363
+ }
364
+ return lines.join("\n");
365
+ }
366
+ module2.exports = { parsePlanFile, writePlanFile };
367
+ }
368
+ });
369
+
370
+ // src/artifacts/milestone-folders.js
371
+ var require_milestone_folders = __commonJS({
372
+ "src/artifacts/milestone-folders.js"(exports2, module2) {
373
+ "use strict";
374
+ var { existsSync, mkdirSync, readdirSync, renameSync } = require("node:fs");
375
+ var { join, basename } = require("node:path");
376
+ function slugify(title) {
377
+ return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
378
+ }
379
+ function milestoneFolderName(id, title) {
380
+ return `${id}-${slugify(title)}`;
381
+ }
382
+ function getMilestoneFolderPath(planningDir, id, title) {
383
+ return join(planningDir, "milestones", milestoneFolderName(id, title));
384
+ }
385
+ function ensureMilestoneFolder(planningDir, id, title) {
386
+ const folderPath = getMilestoneFolderPath(planningDir, id, title);
387
+ if (!existsSync(folderPath)) {
388
+ mkdirSync(folderPath, { recursive: true });
389
+ }
390
+ return folderPath;
391
+ }
392
+ function findMilestoneFolder(planningDir, id) {
393
+ const milestonesDir = join(planningDir, "milestones");
394
+ if (!existsSync(milestonesDir)) return null;
395
+ const entries = readdirSync(milestonesDir, { withFileTypes: true });
396
+ const folder = entries.find((e) => e.isDirectory() && e.name.startsWith(id));
397
+ return folder ? join(milestonesDir, folder.name) : null;
398
+ }
399
+ function archiveMilestoneFolder(planningDir, id) {
400
+ const folder = findMilestoneFolder(planningDir, id);
401
+ if (!folder) return false;
402
+ const archiveDir = join(planningDir, "milestones", "_archived");
403
+ mkdirSync(archiveDir, { recursive: true });
404
+ renameSync(folder, join(archiveDir, basename(folder)));
405
+ return true;
406
+ }
407
+ module2.exports = {
408
+ slugify,
409
+ milestoneFolderName,
410
+ getMilestoneFolderPath,
411
+ ensureMilestoneFolder,
412
+ findMilestoneFolder,
413
+ archiveMilestoneFolder
414
+ };
415
+ }
416
+ });
417
+
418
+ // src/graph/engine.js
419
+ var require_engine = __commonJS({
420
+ "src/graph/engine.js"(exports2, module2) {
421
+ "use strict";
422
+ var PREFIX_TO_TYPE = { D: "declaration", M: "milestone", A: "action" };
423
+ var TYPE_TO_PREFIX = { declaration: "D", milestone: "M", action: "A" };
424
+ var VALID_STATUSES = /* @__PURE__ */ new Set(["PENDING", "ACTIVE", "DONE", "KEPT", "BROKEN", "HONORED", "RENEGOTIATED"]);
425
+ var COMPLETED_STATUSES = /* @__PURE__ */ new Set(["DONE", "KEPT", "HONORED", "RENEGOTIATED"]);
426
+ function isCompleted(status) {
427
+ return COMPLETED_STATUSES.has(status);
428
+ }
429
+ var VALID_TYPES = /* @__PURE__ */ new Set(["declaration", "milestone", "action"]);
430
+ var VALID_EDGE_DIRECTIONS = {
431
+ action: "milestone",
432
+ milestone: "declaration"
433
+ };
434
+ var DeclareDag = class _DeclareDag {
435
+ constructor() {
436
+ this.nodes = /* @__PURE__ */ new Map();
437
+ this.upEdges = /* @__PURE__ */ new Map();
438
+ this.downEdges = /* @__PURE__ */ new Map();
439
+ }
440
+ // ---------------------------------------------------------------------------
441
+ // Node operations
442
+ // ---------------------------------------------------------------------------
443
+ /**
444
+ * Add a node to the graph.
445
+ * @param {string} id - Semantic ID (D-XX, M-XX, A-XX)
446
+ * @param {string} type - 'declaration' | 'milestone' | 'action'
447
+ * @param {string} title - Human-readable title
448
+ * @param {string} [status='PENDING'] - PENDING | ACTIVE | DONE
449
+ * @param {Record<string, any>} [metadata={}] - Additional metadata
450
+ * @returns {DeclareDag} this (for chaining)
451
+ */
452
+ addNode(id, type, title, status = "PENDING", metadata = {}) {
453
+ if (!VALID_TYPES.has(type)) {
454
+ throw new Error(`Invalid node type: ${type}. Must be one of: declaration, milestone, action`);
455
+ }
456
+ if (!VALID_STATUSES.has(status)) {
457
+ throw new Error(`Invalid status: ${status}. Must be one of: PENDING, ACTIVE, DONE`);
458
+ }
459
+ const prefix = id.split("-")[0];
460
+ if (PREFIX_TO_TYPE[prefix] !== type) {
461
+ throw new Error(
462
+ `ID prefix '${prefix}' doesn't match type '${type}'. Expected prefix '${TYPE_TO_PREFIX[type]}' for type '${type}'`
463
+ );
464
+ }
465
+ if (!/^[DMA]-\d+$/.test(id)) {
466
+ throw new Error(`Invalid ID format: ${id}. Expected format: D-01, M-01, A-01`);
467
+ }
468
+ if (this.nodes.has(id)) {
469
+ throw new Error(`Node already exists: ${id}`);
470
+ }
471
+ this.nodes.set(id, { id, type, title, status, metadata });
472
+ if (!this.upEdges.has(id)) this.upEdges.set(id, /* @__PURE__ */ new Set());
473
+ if (!this.downEdges.has(id)) this.downEdges.set(id, /* @__PURE__ */ new Set());
474
+ return this;
475
+ }
476
+ /**
477
+ * Remove a node and all its edges.
478
+ * @param {string} id
479
+ * @returns {DeclareDag} this
480
+ */
481
+ removeNode(id) {
482
+ if (!this.nodes.has(id)) {
483
+ throw new Error(`Node not found: ${id}`);
484
+ }
485
+ const upTargets = this.upEdges.get(id);
486
+ if (upTargets) {
487
+ for (const target of upTargets) {
488
+ const downSet = this.downEdges.get(target);
489
+ if (downSet) downSet.delete(id);
490
+ }
491
+ }
492
+ const downSources = this.downEdges.get(id);
493
+ if (downSources) {
494
+ for (const source of downSources) {
495
+ const upSet = this.upEdges.get(source);
496
+ if (upSet) upSet.delete(id);
497
+ }
498
+ }
499
+ this.upEdges.delete(id);
500
+ this.downEdges.delete(id);
501
+ this.nodes.delete(id);
502
+ return this;
503
+ }
504
+ /**
505
+ * Get a node by ID.
506
+ * @param {string} id
507
+ * @returns {{id: string, type: string, title: string, status: string, metadata: Record<string, any>} | undefined}
508
+ */
509
+ getNode(id) {
510
+ return this.nodes.get(id);
511
+ }
512
+ /**
513
+ * Update a node's status.
514
+ * @param {string} id
515
+ * @param {string} status - PENDING | ACTIVE | DONE
516
+ * @returns {DeclareDag} this
517
+ */
518
+ updateNodeStatus(id, status) {
519
+ if (!VALID_STATUSES.has(status)) {
520
+ throw new Error(`Invalid status: ${status}. Must be one of: PENDING, ACTIVE, DONE`);
521
+ }
522
+ const node = this.nodes.get(id);
523
+ if (!node) {
524
+ throw new Error(`Node not found: ${id}`);
525
+ }
526
+ node.status = status;
527
+ return this;
528
+ }
529
+ // ---------------------------------------------------------------------------
530
+ // Edge operations
531
+ // ---------------------------------------------------------------------------
532
+ /**
533
+ * Add an upward-causation edge.
534
+ * Valid directions: action->milestone, milestone->declaration.
535
+ * @param {string} from - Source node ID
536
+ * @param {string} to - Target node ID
537
+ * @returns {DeclareDag} this
538
+ */
539
+ addEdge(from, to) {
540
+ const fromNode = this.nodes.get(from);
541
+ const toNode = this.nodes.get(to);
542
+ if (!fromNode) throw new Error(`Node not found: ${from}`);
543
+ if (!toNode) throw new Error(`Node not found: ${to}`);
544
+ const expectedTo = VALID_EDGE_DIRECTIONS[fromNode.type];
545
+ if (expectedTo !== toNode.type) {
546
+ throw new Error(
547
+ `Invalid edge: ${fromNode.type} -> ${toNode.type}. Only action->milestone and milestone->declaration edges are allowed`
548
+ );
549
+ }
550
+ this.upEdges.get(from).add(to);
551
+ this.downEdges.get(to).add(from);
552
+ return this;
553
+ }
554
+ /**
555
+ * Remove an edge.
556
+ * @param {string} from
557
+ * @param {string} to
558
+ * @returns {DeclareDag} this
559
+ */
560
+ removeEdge(from, to) {
561
+ const upSet = this.upEdges.get(from);
562
+ if (upSet) upSet.delete(to);
563
+ const downSet = this.downEdges.get(to);
564
+ if (downSet) downSet.delete(from);
565
+ return this;
566
+ }
567
+ // ---------------------------------------------------------------------------
568
+ // Layer queries
569
+ // ---------------------------------------------------------------------------
570
+ /** @returns {Array<{id: string, type: string, title: string, status: string, metadata: Record<string, any>}>} */
571
+ getDeclarations() {
572
+ return this._byType("declaration");
573
+ }
574
+ /** @returns {Array<{id: string, type: string, title: string, status: string, metadata: Record<string, any>}>} */
575
+ getMilestones() {
576
+ return this._byType("milestone");
577
+ }
578
+ /** @returns {Array<{id: string, type: string, title: string, status: string, metadata: Record<string, any>}>} */
579
+ getActions() {
580
+ return this._byType("action");
581
+ }
582
+ /**
583
+ * Get nodes that this node causes/realizes (upward neighbors).
584
+ * @param {string} id
585
+ * @returns {Array<{id: string, type: string, title: string, status: string, metadata: Record<string, any>}>}
586
+ */
587
+ getUpstream(id) {
588
+ const targets = this.upEdges.get(id);
589
+ if (!targets) return [];
590
+ return [...targets].map((t) => this.nodes.get(t)).filter(Boolean);
591
+ }
592
+ /**
593
+ * Get nodes that cause/realize this node (downward neighbors).
594
+ * @param {string} id
595
+ * @returns {Array<{id: string, type: string, title: string, status: string, metadata: Record<string, any>}>}
596
+ */
597
+ getDownstream(id) {
598
+ const sources = this.downEdges.get(id);
599
+ if (!sources) return [];
600
+ return [...sources].map((s) => this.nodes.get(s)).filter(Boolean);
601
+ }
602
+ /**
603
+ * @param {string} type
604
+ * @returns {Array<{id: string, type: string, title: string, status: string, metadata: Record<string, any>}>}
605
+ * @private
606
+ */
607
+ _byType(type) {
608
+ return [...this.nodes.values()].filter((n) => n.type === type);
609
+ }
610
+ // ---------------------------------------------------------------------------
611
+ // Validation (runs on /declare:status, NOT on normal operations)
612
+ // ---------------------------------------------------------------------------
613
+ /**
614
+ * Validate graph structure.
615
+ * Checks: orphan nodes, cycles, broken edges, invalid edge directions.
616
+ * @returns {{ valid: boolean, errors: Array<{type: string, node?: string, from?: string, to?: string, message: string}> }}
617
+ */
618
+ validate() {
619
+ const errors = [];
620
+ for (const [id, node] of this.nodes) {
621
+ if (node.type === "declaration") continue;
622
+ if (this.upEdges.get(id).size === 0) {
623
+ errors.push({
624
+ type: "orphan",
625
+ node: id,
626
+ message: `${id} has no upward connection`
627
+ });
628
+ }
629
+ }
630
+ if (this._hasCycle()) {
631
+ errors.push({
632
+ type: "cycle",
633
+ message: "Graph contains a cycle"
634
+ });
635
+ }
636
+ for (const [from, targets] of this.upEdges) {
637
+ for (const to of targets) {
638
+ if (!this.nodes.has(to)) {
639
+ errors.push({
640
+ type: "broken_edge",
641
+ from,
642
+ to,
643
+ message: `Edge target ${to} not found`
644
+ });
645
+ }
646
+ }
647
+ }
648
+ return { valid: errors.length === 0, errors };
649
+ }
650
+ // ---------------------------------------------------------------------------
651
+ // Topological sort
652
+ // ---------------------------------------------------------------------------
653
+ /**
654
+ * Kahn's algorithm topological sort.
655
+ * Returns node IDs in execution order: actions first, then milestones, then declarations.
656
+ * @returns {string[]}
657
+ * @throws {Error} If cycle detected
658
+ */
659
+ topologicalSort() {
660
+ const inDegree = /* @__PURE__ */ new Map();
661
+ const adjList = /* @__PURE__ */ new Map();
662
+ for (const id of this.nodes.keys()) {
663
+ inDegree.set(id, 0);
664
+ adjList.set(id, /* @__PURE__ */ new Set());
665
+ }
666
+ for (const [from, targets] of this.upEdges) {
667
+ for (const to of targets) {
668
+ if (!this.nodes.has(to)) continue;
669
+ adjList.get(from).add(to);
670
+ inDegree.set(to, (inDegree.get(to) || 0) + 1);
671
+ }
672
+ }
673
+ const queue = [];
674
+ for (const [id, deg] of inDegree) {
675
+ if (deg === 0) queue.push(id);
676
+ }
677
+ const sorted = [];
678
+ while (queue.length > 0) {
679
+ const node = queue.shift();
680
+ sorted.push(node);
681
+ for (const neighbor of adjList.get(node)) {
682
+ inDegree.set(neighbor, inDegree.get(neighbor) - 1);
683
+ if (inDegree.get(neighbor) === 0) queue.push(neighbor);
684
+ }
685
+ }
686
+ if (sorted.length !== this.nodes.size) {
687
+ throw new Error("Cycle detected: topological sort incomplete");
688
+ }
689
+ return sorted;
690
+ }
691
+ /**
692
+ * Check if graph has a cycle.
693
+ * @returns {boolean}
694
+ * @private
695
+ */
696
+ _hasCycle() {
697
+ try {
698
+ this.topologicalSort();
699
+ return false;
700
+ } catch {
701
+ return true;
702
+ }
703
+ }
704
+ // ---------------------------------------------------------------------------
705
+ // Serialization
706
+ // ---------------------------------------------------------------------------
707
+ /**
708
+ * Serialize graph to JSON-compatible object.
709
+ * @returns {{ nodes: Array, edges: Array<{from: string, to: string}> }}
710
+ */
711
+ toJSON() {
712
+ return {
713
+ nodes: [...this.nodes.values()].map((n) => ({ ...n })),
714
+ edges: [...this.upEdges.entries()].flatMap(
715
+ ([from, tos]) => [...tos].map((to) => ({ from, to }))
716
+ )
717
+ };
718
+ }
719
+ /**
720
+ * Reconstruct DeclareDag from JSON.
721
+ * @param {{ nodes: Array, edges: Array<{from: string, to: string}> }} data
722
+ * @returns {DeclareDag}
723
+ */
724
+ static fromJSON(data) {
725
+ const dag = new _DeclareDag();
726
+ for (const node of data.nodes) {
727
+ dag.addNode(node.id, node.type, node.title, node.status, node.metadata || {});
728
+ }
729
+ for (const edge of data.edges) {
730
+ dag.addEdge(edge.from, edge.to);
731
+ }
732
+ return dag;
733
+ }
734
+ // ---------------------------------------------------------------------------
735
+ // Auto-increment helper
736
+ // ---------------------------------------------------------------------------
737
+ /**
738
+ * Get the next available ID for a given type.
739
+ * Scans existing nodes, finds max numeric suffix, returns next ID.
740
+ * @param {string} type - 'declaration' | 'milestone' | 'action'
741
+ * @returns {string} Next ID (e.g., 'D-03' if D-01, D-02 exist)
742
+ */
743
+ nextId(type) {
744
+ if (!VALID_TYPES.has(type)) {
745
+ throw new Error(`Invalid type: ${type}`);
746
+ }
747
+ const prefix = TYPE_TO_PREFIX[type];
748
+ let max = 0;
749
+ for (const [id, node] of this.nodes) {
750
+ if (node.type === type) {
751
+ const num = parseInt(id.split("-")[1], 10);
752
+ if (num > max) max = num;
753
+ }
754
+ }
755
+ const next = max + 1;
756
+ const padded = next < 10 ? `0${next}` : `${next}`;
757
+ return `${prefix}-${padded}`;
758
+ }
759
+ // ---------------------------------------------------------------------------
760
+ // Node counts
761
+ // ---------------------------------------------------------------------------
762
+ /** @returns {number} Total node count */
763
+ get size() {
764
+ return this.nodes.size;
765
+ }
766
+ /**
767
+ * Get graph statistics.
768
+ * @returns {{ declarations: number, milestones: number, actions: number, edges: number, byStatus: {PENDING: number, ACTIVE: number, DONE: number} }}
769
+ */
770
+ stats() {
771
+ const byStatus = {};
772
+ for (const s of VALID_STATUSES) byStatus[s] = 0;
773
+ let declarations = 0;
774
+ let milestones = 0;
775
+ let actions = 0;
776
+ let edges = 0;
777
+ for (const node of this.nodes.values()) {
778
+ byStatus[node.status]++;
779
+ if (node.type === "declaration") declarations++;
780
+ else if (node.type === "milestone") milestones++;
781
+ else if (node.type === "action") actions++;
782
+ }
783
+ for (const targets of this.upEdges.values()) {
784
+ edges += targets.size;
785
+ }
786
+ return { declarations, milestones, actions, edges, byStatus };
787
+ }
788
+ };
789
+ function findOrphans(dag) {
790
+ const { errors } = dag.validate();
791
+ return errors.filter((e) => e.type === "orphan" && e.node).map((e) => {
792
+ const node = dag.getNode(e.node);
793
+ return node ? { id: node.id, type: node.type, title: node.title, status: node.status } : { id: e.node, type: "unknown", title: "", status: "" };
794
+ });
795
+ }
796
+ module2.exports = { DeclareDag, COMPLETED_STATUSES, isCompleted, findOrphans };
797
+ }
798
+ });
799
+
800
+ // src/commands/build-dag.js
801
+ var require_build_dag = __commonJS({
802
+ "src/commands/build-dag.js"(exports2, module2) {
803
+ "use strict";
804
+ var { existsSync, readFileSync, readdirSync } = require("node:fs");
805
+ var { join } = require("node:path");
806
+ var { parseFutureFile } = require_future();
807
+ var { parseMilestonesFile } = require_milestones();
808
+ var { parsePlanFile } = require_plan();
809
+ var { DeclareDag } = require_engine();
810
+ function loadActionsFromFolders(planningDir) {
811
+ const milestonesDir = join(planningDir, "milestones");
812
+ if (!existsSync(milestonesDir)) return [];
813
+ const allActions = [];
814
+ const entries = readdirSync(milestonesDir, { withFileTypes: true });
815
+ for (const entry of entries) {
816
+ if (!entry.isDirectory() || entry.name.startsWith("_")) continue;
817
+ const planPath = join(milestonesDir, entry.name, "PLAN.md");
818
+ if (!existsSync(planPath)) continue;
819
+ const content = readFileSync(planPath, "utf-8");
820
+ const { milestone, actions } = parsePlanFile(content);
821
+ for (const action of actions) {
822
+ allActions.push({
823
+ id: action.id,
824
+ title: action.title,
825
+ status: action.status,
826
+ produces: action.produces,
827
+ causes: milestone ? [milestone] : []
828
+ });
829
+ }
830
+ }
831
+ return allActions;
832
+ }
833
+ function buildDagFromDisk(cwd) {
834
+ const planningDir = join(cwd, ".planning");
835
+ if (!existsSync(planningDir)) {
836
+ return { error: "No Declare project found. Run /declare:init first." };
837
+ }
838
+ const futurePath = join(planningDir, "FUTURE.md");
839
+ const milestonesPath = join(planningDir, "MILESTONES.md");
840
+ const futureContent = existsSync(futurePath) ? readFileSync(futurePath, "utf-8") : "";
841
+ const milestonesContent = existsSync(milestonesPath) ? readFileSync(milestonesPath, "utf-8") : "";
842
+ const declarations = parseFutureFile(futureContent);
843
+ const { milestones } = parseMilestonesFile(milestonesContent);
844
+ const actions = loadActionsFromFolders(planningDir);
845
+ const dag = new DeclareDag();
846
+ for (const d of declarations) {
847
+ dag.addNode(d.id, "declaration", d.title, d.status || "PENDING");
848
+ }
849
+ for (const m of milestones) {
850
+ dag.addNode(m.id, "milestone", m.title, m.status || "PENDING");
851
+ }
852
+ for (const a of actions) {
853
+ dag.addNode(a.id, "action", a.title, a.status || "PENDING");
854
+ }
855
+ for (const m of milestones) {
856
+ for (const declId of m.realizes) {
857
+ if (dag.getNode(declId)) {
858
+ dag.addEdge(m.id, declId);
859
+ }
860
+ }
861
+ }
862
+ for (const a of actions) {
863
+ for (const milestoneId of a.causes) {
864
+ if (dag.getNode(milestoneId)) {
865
+ dag.addEdge(a.id, milestoneId);
866
+ }
867
+ }
868
+ }
869
+ return { dag, declarations, milestones, actions };
870
+ }
871
+ module2.exports = { buildDagFromDisk, loadActionsFromFolders };
872
+ }
873
+ });
874
+
875
+ // src/commands/compute-performance.js
876
+ var require_compute_performance = __commonJS({
877
+ "src/commands/compute-performance.js"(exports2, module2) {
878
+ "use strict";
879
+ var { buildDagFromDisk } = require_build_dag();
880
+ var { isCompleted } = require_engine();
881
+ function ratioToLabel(ratio, highThreshold, medThreshold) {
882
+ if (ratio >= highThreshold) return "HIGH";
883
+ if (ratio >= medThreshold) return "MEDIUM";
884
+ return "LOW";
885
+ }
886
+ function combineLabels(alignment, integrity) {
887
+ if (alignment === "LOW" || integrity === "LOW") return "LOW";
888
+ if (alignment === "HIGH" && integrity === "HIGH") return "HIGH";
889
+ return "MEDIUM";
890
+ }
891
+ function runComputePerformance2(cwd) {
892
+ const graphResult = buildDagFromDisk(cwd);
893
+ if ("error" in graphResult) return graphResult;
894
+ const { dag } = graphResult;
895
+ const declarations = dag.getDeclarations();
896
+ if (declarations.length === 0) {
897
+ return {
898
+ perDeclaration: [],
899
+ rollup: { alignment: "HIGH", integrity: "HIGH", performance: "HIGH" }
900
+ };
901
+ }
902
+ const perDeclaration = declarations.map((decl) => {
903
+ const downstream = dag.getDownstream(decl.id);
904
+ const milestones = downstream.filter((n) => n.type === "milestone");
905
+ let milestonesWithActions = 0;
906
+ for (const m of milestones) {
907
+ const mDownstream = dag.getDownstream(m.id);
908
+ if (mDownstream.some((n) => n.type === "action")) {
909
+ milestonesWithActions++;
910
+ }
911
+ }
912
+ const alignmentRatio = milestones.length > 0 ? milestonesWithActions / milestones.length : 0;
913
+ const alignmentLevel = milestones.length === 0 ? "LOW" : ratioToLabel(alignmentRatio, 0.8, 0.5);
914
+ let verified = 0;
915
+ let broken = 0;
916
+ let pending = 0;
917
+ for (const m of milestones) {
918
+ const status = m.status;
919
+ if (status === "KEPT" || status === "HONORED" || status === "RENEGOTIATED") {
920
+ verified++;
921
+ } else if (status === "BROKEN") {
922
+ broken++;
923
+ } else {
924
+ pending++;
925
+ }
926
+ }
927
+ let integrityLevel;
928
+ if (milestones.length === 0) {
929
+ integrityLevel = "HIGH";
930
+ } else {
931
+ const brokenRatio = broken / milestones.length;
932
+ const verifiedRatio = verified / milestones.length;
933
+ if (brokenRatio > 0.3) {
934
+ integrityLevel = "LOW";
935
+ } else if (verifiedRatio >= 0.7 && broken === 0) {
936
+ integrityLevel = "HIGH";
937
+ } else if (verifiedRatio >= 0.4) {
938
+ integrityLevel = "MEDIUM";
939
+ } else {
940
+ integrityLevel = "LOW";
941
+ }
942
+ }
943
+ const performance = combineLabels(alignmentLevel, integrityLevel);
944
+ return {
945
+ declarationId: decl.id,
946
+ declarationTitle: decl.title,
947
+ statement: decl.metadata?.statement || decl.title,
948
+ alignment: {
949
+ level: alignmentLevel,
950
+ milestonesTotal: milestones.length,
951
+ milestonesWithActions
952
+ },
953
+ integrity: {
954
+ level: integrityLevel,
955
+ verified,
956
+ broken,
957
+ pending,
958
+ total: milestones.length
959
+ },
960
+ performance
961
+ };
962
+ });
963
+ const hasAnyLowAlignment = perDeclaration.some((d) => d.alignment.level === "LOW");
964
+ const hasAnyLowIntegrity = perDeclaration.some((d) => d.integrity.level === "LOW");
965
+ const allHighAlignment = perDeclaration.every((d) => d.alignment.level === "HIGH");
966
+ const allHighIntegrity = perDeclaration.every((d) => d.integrity.level === "HIGH");
967
+ const rollupAlignment = hasAnyLowAlignment ? "LOW" : allHighAlignment ? "HIGH" : "MEDIUM";
968
+ const rollupIntegrity = hasAnyLowIntegrity ? "LOW" : allHighIntegrity ? "HIGH" : "MEDIUM";
969
+ const rollupPerformance = combineLabels(rollupAlignment, rollupIntegrity);
970
+ return {
971
+ perDeclaration,
972
+ rollup: {
973
+ alignment: rollupAlignment,
974
+ integrity: rollupIntegrity,
975
+ performance: rollupPerformance
976
+ }
977
+ };
978
+ }
979
+ module2.exports = { runComputePerformance: runComputePerformance2 };
980
+ }
981
+ });
982
+
983
+ // src/commands/status.js
984
+ var require_status = __commonJS({
985
+ "src/commands/status.js"(exports2, module2) {
986
+ "use strict";
987
+ var { existsSync, readFileSync } = require("node:fs");
988
+ var { join, basename } = require("node:path");
989
+ var { execFileSync } = require("node:child_process");
990
+ var { parsePlanFile } = require_plan();
991
+ var { findMilestoneFolder } = require_milestone_folders();
992
+ var { buildDagFromDisk } = require_build_dag();
993
+ var { isCompleted } = require_engine();
994
+ var { runComputePerformance: runComputePerformance2 } = require_compute_performance();
995
+ function detectStaleness(cwd, planningDir, milestones) {
996
+ const indicators = [];
997
+ for (const m of milestones) {
998
+ const folder = findMilestoneFolder(planningDir, m.id);
999
+ if (!folder) {
1000
+ indicators.push({ milestone: m.id, issue: "NO_PLAN", detail: "No plan derived yet" });
1001
+ continue;
1002
+ }
1003
+ const planPath = join(folder, "PLAN.md");
1004
+ if (!existsSync(planPath)) {
1005
+ indicators.push({ milestone: m.id, issue: "EMPTY_FOLDER", detail: "Folder exists but no PLAN.md" });
1006
+ continue;
1007
+ }
1008
+ try {
1009
+ const lastMod = execFileSync("git", ["log", "-1", "--format=%ct", "--", planPath], {
1010
+ cwd,
1011
+ encoding: "utf-8",
1012
+ stdio: ["pipe", "pipe", "pipe"]
1013
+ }).trim();
1014
+ if (lastMod) {
1015
+ const ageDays = (Date.now() - parseInt(lastMod, 10) * 1e3) / 864e5;
1016
+ if (ageDays > 30) {
1017
+ indicators.push({ milestone: m.id, issue: "STALE", detail: `Plan not updated in ${Math.floor(ageDays)} days` });
1018
+ }
1019
+ }
1020
+ } catch {
1021
+ }
1022
+ const plan = parsePlanFile(readFileSync(planPath, "utf-8"));
1023
+ if (m.status === "ACTIVE" && plan.actions.length > 0 && plan.actions.every((a) => isCompleted(a.status))) {
1024
+ indicators.push({ milestone: m.id, issue: "COMPLETABLE", detail: "All actions done, milestone still ACTIVE" });
1025
+ }
1026
+ if (isCompleted(m.status) && plan.actions.some((a) => !isCompleted(a.status))) {
1027
+ indicators.push({ milestone: m.id, issue: "INCONSISTENT", detail: "Milestone marked completed but has incomplete actions" });
1028
+ }
1029
+ }
1030
+ return indicators;
1031
+ }
1032
+ function runStatus2(cwd) {
1033
+ const planningDir = join(cwd, ".planning");
1034
+ const graphResult = buildDagFromDisk(cwd);
1035
+ if (graphResult.error) return graphResult;
1036
+ const { dag, declarations, milestones, actions } = graphResult;
1037
+ const projectName = basename(cwd);
1038
+ const validation = dag.validate();
1039
+ const stats = dag.stats();
1040
+ let lastActivity = "No activity recorded";
1041
+ try {
1042
+ const output = execFileSync("git", ["log", "-1", "--format=%ci %s", "--", ".planning/"], {
1043
+ cwd,
1044
+ encoding: "utf-8",
1045
+ stdio: ["pipe", "pipe", "pipe"]
1046
+ }).trim();
1047
+ if (output) {
1048
+ lastActivity = output;
1049
+ }
1050
+ } catch {
1051
+ }
1052
+ const withPlan = milestones.filter((m) => m.hasPlan).length;
1053
+ const coverage = {
1054
+ total: milestones.length,
1055
+ withPlan,
1056
+ percentage: milestones.length > 0 ? Math.round(withPlan / milestones.length * 100) : 100
1057
+ };
1058
+ const staleness = detectStaleness(cwd, planningDir, milestones);
1059
+ const integrity = {
1060
+ total: milestones.length,
1061
+ verified: 0,
1062
+ // KEPT + HONORED + RENEGOTIATED
1063
+ kept: 0,
1064
+ honored: 0,
1065
+ renegotiated: 0,
1066
+ broken: 0,
1067
+ pending: 0
1068
+ // PENDING + ACTIVE + DONE (not yet verified)
1069
+ };
1070
+ for (const m of milestones) {
1071
+ switch (m.status) {
1072
+ case "KEPT":
1073
+ integrity.kept++;
1074
+ integrity.verified++;
1075
+ break;
1076
+ case "HONORED":
1077
+ integrity.honored++;
1078
+ integrity.verified++;
1079
+ break;
1080
+ case "RENEGOTIATED":
1081
+ integrity.renegotiated++;
1082
+ integrity.verified++;
1083
+ break;
1084
+ case "BROKEN":
1085
+ integrity.broken++;
1086
+ break;
1087
+ default:
1088
+ integrity.pending++;
1089
+ break;
1090
+ }
1091
+ }
1092
+ let health = "healthy";
1093
+ if (!validation.valid) {
1094
+ const hasCycle = validation.errors.some((e) => e.type === "cycle");
1095
+ const hasBroken = validation.errors.some((e) => e.type === "broken_edge");
1096
+ health = hasCycle || hasBroken ? "errors" : "warnings";
1097
+ }
1098
+ if (staleness.length > 0 && health === "healthy") {
1099
+ health = "warnings";
1100
+ }
1101
+ if (integrity.broken > 0 && health === "healthy") {
1102
+ health = "warnings";
1103
+ }
1104
+ let performance = null;
1105
+ try {
1106
+ const perfResult = runComputePerformance2(cwd);
1107
+ if (!perfResult.error) {
1108
+ performance = {
1109
+ perDeclaration: perfResult.perDeclaration.map((d) => ({
1110
+ declarationId: d.declarationId,
1111
+ declarationTitle: d.declarationTitle,
1112
+ alignment: d.alignment.level,
1113
+ integrity: d.integrity.level,
1114
+ performance: d.performance
1115
+ })),
1116
+ rollup: perfResult.rollup
1117
+ };
1118
+ }
1119
+ } catch {
1120
+ }
1121
+ return {
1122
+ project: projectName,
1123
+ stats: {
1124
+ declarations: stats.declarations,
1125
+ milestones: stats.milestones,
1126
+ actions: stats.actions,
1127
+ edges: stats.edges,
1128
+ byStatus: stats.byStatus
1129
+ },
1130
+ validation: {
1131
+ valid: validation.valid,
1132
+ errors: validation.errors
1133
+ },
1134
+ lastActivity,
1135
+ health,
1136
+ coverage,
1137
+ staleness,
1138
+ integrity,
1139
+ performance
1140
+ };
1141
+ }
1142
+ module2.exports = { runStatus: runStatus2 };
1143
+ }
1144
+ });
1145
+
1146
+ // src/commands/help.js
1147
+ var require_help = __commonJS({
1148
+ "src/commands/help.js"(exports2, module2) {
1149
+ "use strict";
1150
+ function runHelp2() {
1151
+ return {
1152
+ commands: [
1153
+ {
1154
+ name: "/declare:init",
1155
+ description: "Initialize a Declare project with future declarations and graph structure",
1156
+ usage: "/declare:init [project-name]"
1157
+ },
1158
+ {
1159
+ name: "/declare:status",
1160
+ description: "Show graph state, coverage, staleness indicators, and last activity",
1161
+ usage: "/declare:status"
1162
+ },
1163
+ {
1164
+ name: "add-declaration",
1165
+ description: "Add a new declaration to FUTURE.md with auto-incremented ID",
1166
+ usage: 'add-declaration --title "..." --statement "..."'
1167
+ },
1168
+ {
1169
+ name: "add-milestone",
1170
+ description: "Add a new milestone to MILESTONES.md linked to declarations",
1171
+ usage: 'add-milestone --title "..." --realizes D-01[,D-02]'
1172
+ },
1173
+ {
1174
+ name: "create-plan",
1175
+ description: "Write action plan (PLAN.md) to milestone folder",
1176
+ usage: `create-plan --milestone M-XX --actions '[{"title":"...","produces":"..."}]'`
1177
+ },
1178
+ {
1179
+ name: "load-graph",
1180
+ description: "Load full graph state as JSON with stats and validation",
1181
+ usage: "load-graph"
1182
+ },
1183
+ {
1184
+ name: "/declare:milestones",
1185
+ description: "Derive milestones backward from declared futures with checkbox confirmation",
1186
+ usage: "/declare:milestones [D-XX]"
1187
+ },
1188
+ {
1189
+ name: "/declare:actions",
1190
+ description: "Derive action plans per milestone and write PLAN.md to milestone folders",
1191
+ usage: "/declare:actions [M-XX]"
1192
+ },
1193
+ {
1194
+ name: "/declare:help",
1195
+ description: "Show available Declare commands",
1196
+ usage: "/declare:help"
1197
+ }
1198
+ ],
1199
+ version: "0.1.0"
1200
+ };
1201
+ }
1202
+ module2.exports = { runHelp: runHelp2 };
1203
+ }
1204
+ });
1205
+
1206
+ // src/commands/parse-args.js
1207
+ var require_parse_args = __commonJS({
1208
+ "src/commands/parse-args.js"(exports2, module2) {
1209
+ "use strict";
1210
+ function parseFlag(args, flag) {
1211
+ const flagStr = `--${flag}`;
1212
+ const idx = args.indexOf(flagStr);
1213
+ if (idx === -1 || idx + 1 >= args.length) return null;
1214
+ return args[idx + 1];
1215
+ }
1216
+ module2.exports = { parseFlag };
1217
+ }
1218
+ });
1219
+
1220
+ // src/commands/add-declaration.js
1221
+ var require_add_declaration = __commonJS({
1222
+ "src/commands/add-declaration.js"(exports2, module2) {
1223
+ "use strict";
1224
+ var { existsSync, readFileSync, writeFileSync, mkdirSync } = require("node:fs");
1225
+ var { join, basename } = require("node:path");
1226
+ var { parseFutureFile, writeFutureFile } = require_future();
1227
+ var { DeclareDag } = require_engine();
1228
+ var { commitPlanningDocs: commitPlanningDocs2, loadConfig } = require_commit();
1229
+ var { parseFlag } = require_parse_args();
1230
+ function runAddDeclaration2(cwd, args) {
1231
+ const title = parseFlag(args, "title");
1232
+ const statement = parseFlag(args, "statement");
1233
+ if (!title) {
1234
+ return { error: "Missing required flag: --title" };
1235
+ }
1236
+ if (!statement) {
1237
+ return { error: "Missing required flag: --statement" };
1238
+ }
1239
+ const planningDir = join(cwd, ".planning");
1240
+ const futurePath = join(planningDir, "FUTURE.md");
1241
+ const projectName = basename(cwd);
1242
+ if (!existsSync(planningDir)) {
1243
+ mkdirSync(planningDir, { recursive: true });
1244
+ }
1245
+ const futureContent = existsSync(futurePath) ? readFileSync(futurePath, "utf-8") : "";
1246
+ const declarations = parseFutureFile(futureContent);
1247
+ const dag = new DeclareDag();
1248
+ for (const d of declarations) {
1249
+ dag.addNode(d.id, "declaration", d.title, d.status || "PENDING");
1250
+ }
1251
+ const id = dag.nextId("declaration");
1252
+ declarations.push({
1253
+ id,
1254
+ title,
1255
+ statement,
1256
+ status: "PENDING",
1257
+ milestones: []
1258
+ });
1259
+ const content = writeFutureFile(declarations, projectName);
1260
+ writeFileSync(futurePath, content, "utf-8");
1261
+ const config = loadConfig(cwd);
1262
+ let committed = false;
1263
+ let hash;
1264
+ if (config.commit_docs !== false) {
1265
+ const result = commitPlanningDocs2(
1266
+ cwd,
1267
+ `declare: add ${id} "${title}"`,
1268
+ [".planning/FUTURE.md"]
1269
+ );
1270
+ committed = result.committed;
1271
+ hash = result.hash;
1272
+ }
1273
+ return { id, title, statement, status: "PENDING", committed, hash };
1274
+ }
1275
+ module2.exports = { runAddDeclaration: runAddDeclaration2 };
1276
+ }
1277
+ });
1278
+
1279
+ // src/commands/add-milestone.js
1280
+ var require_add_milestone = __commonJS({
1281
+ "src/commands/add-milestone.js"(exports2, module2) {
1282
+ "use strict";
1283
+ var { existsSync, readFileSync, writeFileSync, mkdirSync } = require("node:fs");
1284
+ var { join, basename } = require("node:path");
1285
+ var { parseFutureFile, writeFutureFile } = require_future();
1286
+ var { parseMilestonesFile, writeMilestonesFile } = require_milestones();
1287
+ var { DeclareDag } = require_engine();
1288
+ var { commitPlanningDocs: commitPlanningDocs2, loadConfig } = require_commit();
1289
+ var { parseFlag } = require_parse_args();
1290
+ function runAddMilestone2(cwd, args) {
1291
+ const title = parseFlag(args, "title");
1292
+ const realizesRaw = parseFlag(args, "realizes");
1293
+ if (!title) {
1294
+ return { error: "Missing required flag: --title" };
1295
+ }
1296
+ if (!realizesRaw) {
1297
+ return { error: "Missing required flag: --realizes" };
1298
+ }
1299
+ const realizes = realizesRaw.split(",").map((s) => s.trim()).filter(Boolean);
1300
+ const planningDir = join(cwd, ".planning");
1301
+ const futurePath = join(planningDir, "FUTURE.md");
1302
+ const milestonesPath = join(planningDir, "MILESTONES.md");
1303
+ const projectName = basename(cwd);
1304
+ if (!existsSync(planningDir)) {
1305
+ mkdirSync(planningDir, { recursive: true });
1306
+ }
1307
+ const futureContent = existsSync(futurePath) ? readFileSync(futurePath, "utf-8") : "";
1308
+ const milestonesContent = existsSync(milestonesPath) ? readFileSync(milestonesPath, "utf-8") : "";
1309
+ const declarations = parseFutureFile(futureContent);
1310
+ const { milestones } = parseMilestonesFile(milestonesContent);
1311
+ const dag = new DeclareDag();
1312
+ for (const d of declarations) {
1313
+ dag.addNode(d.id, "declaration", d.title, d.status || "PENDING");
1314
+ }
1315
+ for (const m of milestones) {
1316
+ dag.addNode(m.id, "milestone", m.title, m.status || "PENDING");
1317
+ }
1318
+ for (const declId of realizes) {
1319
+ if (!dag.getNode(declId)) {
1320
+ return { error: `Declaration not found: ${declId}` };
1321
+ }
1322
+ }
1323
+ const id = dag.nextId("milestone");
1324
+ milestones.push({
1325
+ id,
1326
+ title,
1327
+ status: "PENDING",
1328
+ realizes,
1329
+ hasPlan: false
1330
+ });
1331
+ for (const declId of realizes) {
1332
+ const decl = declarations.find((d) => d.id === declId);
1333
+ if (decl && !decl.milestones.includes(id)) {
1334
+ decl.milestones.push(id);
1335
+ }
1336
+ }
1337
+ const futureOutput = writeFutureFile(declarations, projectName);
1338
+ writeFileSync(futurePath, futureOutput, "utf-8");
1339
+ const milestonesOutput = writeMilestonesFile(milestones, projectName);
1340
+ writeFileSync(milestonesPath, milestonesOutput, "utf-8");
1341
+ const config = loadConfig(cwd);
1342
+ let committed = false;
1343
+ let hash;
1344
+ if (config.commit_docs !== false) {
1345
+ const result = commitPlanningDocs2(
1346
+ cwd,
1347
+ `declare: add ${id} "${title}"`,
1348
+ [".planning/FUTURE.md", ".planning/MILESTONES.md"]
1349
+ );
1350
+ committed = result.committed;
1351
+ hash = result.hash;
1352
+ }
1353
+ return { id, title, realizes, status: "PENDING", committed, hash };
1354
+ }
1355
+ module2.exports = { runAddMilestone: runAddMilestone2 };
1356
+ }
1357
+ });
1358
+
1359
+ // src/commands/add-milestones-batch.js
1360
+ var require_add_milestones_batch = __commonJS({
1361
+ "src/commands/add-milestones-batch.js"(exports2, module2) {
1362
+ "use strict";
1363
+ var { existsSync, readFileSync, writeFileSync, mkdirSync } = require("node:fs");
1364
+ var { join, basename } = require("node:path");
1365
+ var { parseFutureFile, writeFutureFile } = require_future();
1366
+ var { parseMilestonesFile, writeMilestonesFile } = require_milestones();
1367
+ var { DeclareDag } = require_engine();
1368
+ var { commitPlanningDocs: commitPlanningDocs2, loadConfig } = require_commit();
1369
+ var { parseFlag } = require_parse_args();
1370
+ function runAddMilestonesBatch2(cwd, args) {
1371
+ const jsonRaw = parseFlag(args, "json");
1372
+ if (!jsonRaw) {
1373
+ return { error: "Missing required flag: --json (JSON array of { title, realizes })" };
1374
+ }
1375
+ let inputs;
1376
+ try {
1377
+ inputs = JSON.parse(jsonRaw);
1378
+ } catch {
1379
+ return { error: "Invalid JSON in --json flag" };
1380
+ }
1381
+ if (!Array.isArray(inputs) || inputs.length === 0) {
1382
+ return { error: "--json must be a non-empty array of { title, realizes }" };
1383
+ }
1384
+ for (let i = 0; i < inputs.length; i++) {
1385
+ if (!inputs[i].title) return { error: `Item ${i}: missing title` };
1386
+ if (!inputs[i].realizes) return { error: `Item ${i}: missing realizes` };
1387
+ }
1388
+ const planningDir = join(cwd, ".planning");
1389
+ const futurePath = join(planningDir, "FUTURE.md");
1390
+ const milestonesPath = join(planningDir, "MILESTONES.md");
1391
+ const projectName = basename(cwd);
1392
+ if (!existsSync(planningDir)) {
1393
+ mkdirSync(planningDir, { recursive: true });
1394
+ }
1395
+ const futureContent = existsSync(futurePath) ? readFileSync(futurePath, "utf-8") : "";
1396
+ const milestonesContent = existsSync(milestonesPath) ? readFileSync(milestonesPath, "utf-8") : "";
1397
+ const declarations = parseFutureFile(futureContent);
1398
+ const { milestones } = parseMilestonesFile(milestonesContent);
1399
+ const dag = new DeclareDag();
1400
+ for (const d of declarations) {
1401
+ dag.addNode(d.id, "declaration", d.title, d.status || "PENDING");
1402
+ }
1403
+ for (const m of milestones) {
1404
+ dag.addNode(m.id, "milestone", m.title, m.status || "PENDING");
1405
+ }
1406
+ for (let i = 0; i < inputs.length; i++) {
1407
+ const realizes = inputs[i].realizes.split(",").map((s) => s.trim()).filter(Boolean);
1408
+ for (const declId of realizes) {
1409
+ if (!dag.getNode(declId)) {
1410
+ return { error: `Item ${i}: declaration not found: ${declId}` };
1411
+ }
1412
+ }
1413
+ }
1414
+ const results = [];
1415
+ for (const input of inputs) {
1416
+ const realizes = input.realizes.split(",").map((s) => s.trim()).filter(Boolean);
1417
+ const id = dag.nextId("milestone");
1418
+ dag.addNode(id, "milestone", input.title, "PENDING");
1419
+ milestones.push({
1420
+ id,
1421
+ title: input.title,
1422
+ status: "PENDING",
1423
+ realizes,
1424
+ hasPlan: false
1425
+ });
1426
+ for (const declId of realizes) {
1427
+ const decl = declarations.find((d) => d.id === declId);
1428
+ if (decl && !decl.milestones.includes(id)) {
1429
+ decl.milestones.push(id);
1430
+ }
1431
+ }
1432
+ results.push({ id, title: input.title, realizes, status: "PENDING" });
1433
+ }
1434
+ const futureOutput = writeFutureFile(declarations, projectName);
1435
+ writeFileSync(futurePath, futureOutput, "utf-8");
1436
+ const milestonesOutput = writeMilestonesFile(milestones, projectName);
1437
+ writeFileSync(milestonesPath, milestonesOutput, "utf-8");
1438
+ const config = loadConfig(cwd);
1439
+ let committed = false;
1440
+ let hash;
1441
+ if (config.commit_docs !== false) {
1442
+ const ids = results.map((r) => r.id).join(", ");
1443
+ const result = commitPlanningDocs2(
1444
+ cwd,
1445
+ `declare: add milestones ${ids}`,
1446
+ [".planning/FUTURE.md", ".planning/MILESTONES.md"]
1447
+ );
1448
+ committed = result.committed;
1449
+ hash = result.hash;
1450
+ }
1451
+ return { milestones: results, committed, hash };
1452
+ }
1453
+ module2.exports = { runAddMilestonesBatch: runAddMilestonesBatch2 };
1454
+ }
1455
+ });
1456
+
1457
+ // src/commands/add-action.js
1458
+ var require_add_action = __commonJS({
1459
+ "src/commands/add-action.js"(exports2, module2) {
1460
+ "use strict";
1461
+ function runAddAction2(_cwd, _args) {
1462
+ return {
1463
+ error: `add-action has been replaced by create-plan. Use: node declare-tools.cjs create-plan --milestone M-XX --actions '[{"title":"...","produces":"..."}]'`
1464
+ };
1465
+ }
1466
+ module2.exports = { runAddAction: runAddAction2 };
1467
+ }
1468
+ });
1469
+
1470
+ // src/commands/load-graph.js
1471
+ var require_load_graph = __commonJS({
1472
+ "src/commands/load-graph.js"(exports2, module2) {
1473
+ "use strict";
1474
+ var { buildDagFromDisk, loadActionsFromFolders } = require_build_dag();
1475
+ function runLoadGraph2(cwd) {
1476
+ const graphResult = buildDagFromDisk(cwd);
1477
+ if (graphResult.error) return graphResult;
1478
+ const { dag, declarations, milestones, actions } = graphResult;
1479
+ return {
1480
+ declarations,
1481
+ milestones,
1482
+ actions,
1483
+ stats: dag.stats(),
1484
+ validation: dag.validate()
1485
+ };
1486
+ }
1487
+ module2.exports = { runLoadGraph: runLoadGraph2, loadActionsFromFolders };
1488
+ }
1489
+ });
1490
+
1491
+ // src/commands/create-plan.js
1492
+ var require_create_plan = __commonJS({
1493
+ "src/commands/create-plan.js"(exports2, module2) {
1494
+ "use strict";
1495
+ var { existsSync, readFileSync, writeFileSync } = require("node:fs");
1496
+ var { join, basename } = require("node:path");
1497
+ var { parseFutureFile } = require_future();
1498
+ var { parseMilestonesFile, writeMilestonesFile } = require_milestones();
1499
+ var { writePlanFile } = require_plan();
1500
+ var { loadActionsFromFolders } = require_load_graph();
1501
+ var { ensureMilestoneFolder } = require_milestone_folders();
1502
+ var { DeclareDag } = require_engine();
1503
+ var { commitPlanningDocs: commitPlanningDocs2, loadConfig } = require_commit();
1504
+ var { parseFlag } = require_parse_args();
1505
+ function runCreatePlan2(cwd, args) {
1506
+ const milestoneId = parseFlag(args, "milestone");
1507
+ const actionsRaw = parseFlag(args, "actions");
1508
+ if (!milestoneId) {
1509
+ return { error: "Missing required flag: --milestone" };
1510
+ }
1511
+ if (!actionsRaw) {
1512
+ return { error: "Missing required flag: --actions" };
1513
+ }
1514
+ let actionDefs;
1515
+ try {
1516
+ actionDefs = JSON.parse(actionsRaw);
1517
+ if (!Array.isArray(actionDefs)) {
1518
+ return { error: "--actions must be a JSON array of [{title, produces}]" };
1519
+ }
1520
+ } catch {
1521
+ return { error: "--actions must be valid JSON: " + actionsRaw };
1522
+ }
1523
+ const planningDir = join(cwd, ".planning");
1524
+ const milestonesPath = join(planningDir, "MILESTONES.md");
1525
+ const futurePath = join(planningDir, "FUTURE.md");
1526
+ const projectName = basename(cwd);
1527
+ if (!existsSync(planningDir)) {
1528
+ return { error: "No Declare project found. Run /declare:init first." };
1529
+ }
1530
+ const futureContent = existsSync(futurePath) ? readFileSync(futurePath, "utf-8") : "";
1531
+ const milestonesContent = existsSync(milestonesPath) ? readFileSync(milestonesPath, "utf-8") : "";
1532
+ const declarations = parseFutureFile(futureContent);
1533
+ const { milestones } = parseMilestonesFile(milestonesContent);
1534
+ const targetMs = milestones.find((m) => m.id === milestoneId);
1535
+ if (!targetMs) {
1536
+ return { error: `Milestone not found: ${milestoneId}` };
1537
+ }
1538
+ const dag = new DeclareDag();
1539
+ for (const d of declarations) {
1540
+ dag.addNode(d.id, "declaration", d.title, d.status || "PENDING");
1541
+ }
1542
+ for (const m of milestones) {
1543
+ dag.addNode(m.id, "milestone", m.title, m.status || "PENDING");
1544
+ }
1545
+ const existingActions = loadActionsFromFolders(planningDir);
1546
+ for (const a of existingActions) {
1547
+ dag.addNode(a.id, "action", a.title, a.status || "PENDING");
1548
+ }
1549
+ const actions = actionDefs.map((def) => {
1550
+ const id = dag.nextId("action");
1551
+ dag.addNode(id, "action", def.title, "PENDING");
1552
+ return {
1553
+ id,
1554
+ title: def.title,
1555
+ status: "PENDING",
1556
+ produces: def.produces || ""
1557
+ };
1558
+ });
1559
+ const folder = ensureMilestoneFolder(planningDir, milestoneId, targetMs.title);
1560
+ const planContent = writePlanFile(milestoneId, targetMs.title, targetMs.realizes, actions);
1561
+ const planPath = join(folder, "PLAN.md");
1562
+ writeFileSync(planPath, planContent, "utf-8");
1563
+ targetMs.hasPlan = true;
1564
+ const msOutput = writeMilestonesFile(milestones, projectName);
1565
+ writeFileSync(milestonesPath, msOutput, "utf-8");
1566
+ const relFolder = folder.replace(cwd + "/", "");
1567
+ const filesToCommit = [
1568
+ ".planning/MILESTONES.md",
1569
+ join(relFolder, "PLAN.md")
1570
+ ];
1571
+ const config = loadConfig(cwd);
1572
+ let committed = false;
1573
+ let hash;
1574
+ if (config.commit_docs !== false) {
1575
+ const result = commitPlanningDocs2(
1576
+ cwd,
1577
+ `declare: create plan for ${milestoneId} "${targetMs.title}"`,
1578
+ filesToCommit
1579
+ );
1580
+ committed = result.committed;
1581
+ hash = result.hash;
1582
+ }
1583
+ return {
1584
+ milestone: milestoneId,
1585
+ folder: relFolder,
1586
+ actions: actions.map((a) => ({ id: a.id, title: a.title, produces: a.produces })),
1587
+ committed,
1588
+ hash
1589
+ };
1590
+ }
1591
+ module2.exports = { runCreatePlan: runCreatePlan2 };
1592
+ }
1593
+ });
1594
+
1595
+ // src/commands/trace.js
1596
+ var require_trace = __commonJS({
1597
+ "src/commands/trace.js"(exports2, module2) {
1598
+ "use strict";
1599
+ var { writeFileSync } = require("node:fs");
1600
+ var { resolve } = require("node:path");
1601
+ var { parseFlag } = require_parse_args();
1602
+ var { buildDagFromDisk } = require_build_dag();
1603
+ function traceUpward(dag, startId) {
1604
+ const startNode = dag.getNode(startId);
1605
+ if (!startNode) return [];
1606
+ if (startNode.type === "declaration") {
1607
+ return [[{ id: startNode.id, type: startNode.type, title: startNode.title, status: startNode.status }]];
1608
+ }
1609
+ const upstream = dag.getUpstream(startId);
1610
+ if (upstream.length === 0) {
1611
+ return [[{ id: startNode.id, type: startNode.type, title: startNode.title, status: startNode.status }]];
1612
+ }
1613
+ const paths = [];
1614
+ const startEntry = { id: startNode.id, type: startNode.type, title: startNode.title, status: startNode.status };
1615
+ for (const parent of upstream) {
1616
+ const parentPaths = traceUpward(dag, parent.id);
1617
+ for (const parentPath of parentPaths) {
1618
+ paths.push([startEntry, ...parentPath]);
1619
+ }
1620
+ }
1621
+ return paths;
1622
+ }
1623
+ function formatTracePaths(nodeId, node, paths) {
1624
+ if (paths.length === 0) {
1625
+ return `${nodeId}: (not found)
1626
+ `;
1627
+ }
1628
+ const lines = [];
1629
+ lines.push(`${node.id}: ${node.title} [${node.status}]`);
1630
+ if (paths.length === 1 && paths[0].length === 1) {
1631
+ lines.push(" (no upstream connections)");
1632
+ return lines.join("\n") + "\n";
1633
+ }
1634
+ const byFirstUpstream = /* @__PURE__ */ new Map();
1635
+ for (const path of paths) {
1636
+ if (path.length < 2) continue;
1637
+ const firstUp = path[1];
1638
+ if (!byFirstUpstream.has(firstUp.id)) {
1639
+ byFirstUpstream.set(firstUp.id, []);
1640
+ }
1641
+ byFirstUpstream.get(firstUp.id).push(path);
1642
+ }
1643
+ const upstreamIds = [...byFirstUpstream.keys()];
1644
+ for (let i = 0; i < upstreamIds.length; i++) {
1645
+ const upId = upstreamIds[i];
1646
+ const groupPaths = byFirstUpstream.get(upId);
1647
+ const isLast = i === upstreamIds.length - 1;
1648
+ const connector = isLast ? "\u2514\u2500\u2500" : "\u251C\u2500\u2500";
1649
+ const indent = isLast ? " " : "\u2502 ";
1650
+ const upNode = groupPaths[0][1];
1651
+ lines.push(`${connector} ${upNode.id}: ${upNode.title} [${upNode.status}]`);
1652
+ const declNodes = /* @__PURE__ */ new Map();
1653
+ for (const path of groupPaths) {
1654
+ if (path.length > 2) {
1655
+ const decl = path[2];
1656
+ declNodes.set(decl.id, decl);
1657
+ }
1658
+ }
1659
+ const declList = [...declNodes.values()];
1660
+ for (let j = 0; j < declList.length; j++) {
1661
+ const decl = declList[j];
1662
+ const declIsLast = j === declList.length - 1;
1663
+ const declConnector = declIsLast ? "\u2514\u2500\u2500" : "\u251C\u2500\u2500";
1664
+ lines.push(`${indent}${declConnector} ${decl.id}: ${decl.title} [${decl.status}]`);
1665
+ }
1666
+ }
1667
+ return lines.join("\n") + "\n";
1668
+ }
1669
+ function runTrace2(cwd, args) {
1670
+ const graphResult = buildDagFromDisk(cwd);
1671
+ if (graphResult.error) return graphResult;
1672
+ const { dag } = graphResult;
1673
+ const nodeId = parseFlag(args, "node") || (args[0] && !args[0].startsWith("--") ? args[0] : null);
1674
+ const outputFile = parseFlag(args, "output");
1675
+ if (!nodeId) {
1676
+ const declarations = dag.getDeclarations().map((n) => ({ id: n.id, title: n.title, status: n.status }));
1677
+ const milestones = dag.getMilestones().map((n) => ({ id: n.id, title: n.title, status: n.status }));
1678
+ const actions = dag.getActions().map((n) => ({ id: n.id, title: n.title, status: n.status }));
1679
+ return {
1680
+ nodes: {
1681
+ declarations,
1682
+ milestones,
1683
+ actions,
1684
+ total: declarations.length + milestones.length + actions.length
1685
+ }
1686
+ };
1687
+ }
1688
+ const node = dag.getNode(nodeId);
1689
+ if (!node) {
1690
+ return { error: `Node not found: ${nodeId}. Use 'trace' without arguments to see available nodes.` };
1691
+ }
1692
+ let paths = traceUpward(dag, nodeId);
1693
+ let truncated = false;
1694
+ let totalPaths = paths.length;
1695
+ if (paths.length > 20) {
1696
+ paths = paths.slice(0, 20);
1697
+ truncated = true;
1698
+ }
1699
+ const nodeInfo = { id: node.id, type: node.type, title: node.title, status: node.status };
1700
+ const formatted = formatTracePaths(nodeId, nodeInfo, paths);
1701
+ let resolvedOutput;
1702
+ if (outputFile) {
1703
+ resolvedOutput = resolve(cwd, outputFile);
1704
+ writeFileSync(resolvedOutput, formatted, "utf-8");
1705
+ }
1706
+ const result = {
1707
+ nodeId,
1708
+ node: nodeInfo,
1709
+ paths,
1710
+ pathCount: paths.length,
1711
+ formatted
1712
+ };
1713
+ if (truncated) {
1714
+ result.truncated = true;
1715
+ result.totalPaths = totalPaths;
1716
+ }
1717
+ if (resolvedOutput) {
1718
+ result.outputFile = resolvedOutput;
1719
+ }
1720
+ return result;
1721
+ }
1722
+ module2.exports = { runTrace: runTrace2, traceUpward, formatTracePaths };
1723
+ }
1724
+ });
1725
+
1726
+ // src/commands/prioritize.js
1727
+ var require_prioritize = __commonJS({
1728
+ "src/commands/prioritize.js"(exports2, module2) {
1729
+ "use strict";
1730
+ var { writeFileSync } = require("node:fs");
1731
+ var { resolve } = require("node:path");
1732
+ var { parseFlag } = require_parse_args();
1733
+ var { buildDagFromDisk } = require_build_dag();
1734
+ function dependencyWeight(dag, nodeId) {
1735
+ const visited = /* @__PURE__ */ new Set();
1736
+ const queue = [nodeId];
1737
+ while (queue.length > 0) {
1738
+ const current = (
1739
+ /** @type {string} */
1740
+ queue.shift()
1741
+ );
1742
+ if (visited.has(current)) continue;
1743
+ visited.add(current);
1744
+ const upstream = dag.getUpstream(current);
1745
+ for (const parent of upstream) {
1746
+ if (!visited.has(parent.id)) {
1747
+ queue.push(parent.id);
1748
+ }
1749
+ }
1750
+ }
1751
+ return visited.size - 1;
1752
+ }
1753
+ function getSubtreeNodeIds(dag, rootId) {
1754
+ const visited = /* @__PURE__ */ new Set();
1755
+ const queue = [rootId];
1756
+ while (queue.length > 0) {
1757
+ const current = (
1758
+ /** @type {string} */
1759
+ queue.shift()
1760
+ );
1761
+ if (visited.has(current)) continue;
1762
+ visited.add(current);
1763
+ const downstream = dag.getDownstream(current);
1764
+ for (const child of downstream) {
1765
+ if (!visited.has(child.id)) {
1766
+ queue.push(child.id);
1767
+ }
1768
+ }
1769
+ }
1770
+ return visited;
1771
+ }
1772
+ function rankActions(dag, filterDeclarationId) {
1773
+ let actions = dag.getActions();
1774
+ if (filterDeclarationId) {
1775
+ const subtreeNodes = getSubtreeNodeIds(dag, filterDeclarationId);
1776
+ actions = actions.filter((a) => subtreeNodes.has(a.id));
1777
+ }
1778
+ const ranked = actions.map((a) => ({
1779
+ id: a.id,
1780
+ title: a.title,
1781
+ score: dependencyWeight(dag, a.id)
1782
+ }));
1783
+ ranked.sort((a, b) => b.score - a.score || a.id.localeCompare(b.id));
1784
+ return ranked.map((item, index) => ({
1785
+ rank: index + 1,
1786
+ ...item
1787
+ }));
1788
+ }
1789
+ function formatRanking(ranking, filter) {
1790
+ const lines = [];
1791
+ if (filter) {
1792
+ lines.push(`Priority ranking (filtered by ${filter}):`);
1793
+ } else {
1794
+ lines.push("Priority ranking (all actions):");
1795
+ }
1796
+ lines.push("");
1797
+ if (ranking.length === 0) {
1798
+ lines.push(" No actions found.");
1799
+ return lines.join("\n") + "\n";
1800
+ }
1801
+ for (const item of ranking) {
1802
+ lines.push(` ${item.rank}. ${item.id}: ${item.title} (score: ${item.score})`);
1803
+ }
1804
+ return lines.join("\n") + "\n";
1805
+ }
1806
+ function runPrioritize2(cwd, args) {
1807
+ const graphResult = buildDagFromDisk(cwd);
1808
+ if (graphResult.error) return graphResult;
1809
+ const { dag } = graphResult;
1810
+ const filterDeclaration = parseFlag(args, "declaration");
1811
+ const outputFile = parseFlag(args, "output");
1812
+ if (filterDeclaration) {
1813
+ const filterNode = dag.getNode(filterDeclaration);
1814
+ if (!filterNode) {
1815
+ return { error: `Declaration not found: ${filterDeclaration}. Use a valid D-XX ID.` };
1816
+ }
1817
+ if (filterNode.type !== "declaration") {
1818
+ return { error: `${filterDeclaration} is a ${filterNode.type}, not a declaration. Use --declaration with a D-XX ID.` };
1819
+ }
1820
+ }
1821
+ const ranking = rankActions(dag, filterDeclaration || void 0);
1822
+ const formatted = formatRanking(ranking, filterDeclaration);
1823
+ const result = {
1824
+ ranking,
1825
+ filter: filterDeclaration || null,
1826
+ totalActions: ranking.length,
1827
+ formatted
1828
+ };
1829
+ if (outputFile) {
1830
+ const resolvedOutput = resolve(cwd, outputFile);
1831
+ writeFileSync(resolvedOutput, formatted, "utf-8");
1832
+ result.outputFile = resolvedOutput;
1833
+ }
1834
+ return result;
1835
+ }
1836
+ module2.exports = { runPrioritize: runPrioritize2, dependencyWeight, getSubtreeNodeIds, rankActions };
1837
+ }
1838
+ });
1839
+
1840
+ // src/commands/visualize.js
1841
+ var require_visualize = __commonJS({
1842
+ "src/commands/visualize.js"(exports2, module2) {
1843
+ "use strict";
1844
+ var { writeFileSync } = require("node:fs");
1845
+ var { resolve } = require("node:path");
1846
+ var { parseFlag } = require_parse_args();
1847
+ var { buildDagFromDisk } = require_build_dag();
1848
+ function getSubtreeNodeIds(dag, rootId) {
1849
+ const visited = /* @__PURE__ */ new Set();
1850
+ const queue = [rootId];
1851
+ while (queue.length > 0) {
1852
+ const current = (
1853
+ /** @type {string} */
1854
+ queue.shift()
1855
+ );
1856
+ if (visited.has(current)) continue;
1857
+ visited.add(current);
1858
+ const downstream = dag.getDownstream(current);
1859
+ for (const child of downstream) {
1860
+ if (!visited.has(child.id)) {
1861
+ queue.push(child.id);
1862
+ }
1863
+ }
1864
+ }
1865
+ return visited;
1866
+ }
1867
+ function statusMarker(dag, node) {
1868
+ if (node.status === "DONE") return "[\u2713]";
1869
+ if (node.status === "ACTIVE") return "[>]";
1870
+ const children = dag.getDownstream(node.id);
1871
+ if (children.length > 0) {
1872
+ const hasNonDoneChild = children.some((c) => c.status !== "DONE");
1873
+ if (hasNonDoneChild) return "[!]";
1874
+ }
1875
+ return "[\u25CB]";
1876
+ }
1877
+ function sortById(nodes) {
1878
+ return [...nodes].sort((a, b) => a.id.localeCompare(b.id));
1879
+ }
1880
+ function buildTreeData(dag, scopeFilter) {
1881
+ const declarations = sortById(dag.getDeclarations());
1882
+ const tree = [];
1883
+ for (const decl of declarations) {
1884
+ if (scopeFilter && !scopeFilter.has(decl.id)) continue;
1885
+ const declEntry = {
1886
+ node: { id: decl.id, type: decl.type, title: decl.title, status: decl.status, marker: statusMarker(dag, decl) },
1887
+ children: []
1888
+ };
1889
+ const milestones = sortById(dag.getDownstream(decl.id));
1890
+ for (const ms of milestones) {
1891
+ if (scopeFilter && !scopeFilter.has(ms.id)) continue;
1892
+ const msEntry = {
1893
+ node: { id: ms.id, type: ms.type, title: ms.title, status: ms.status, marker: statusMarker(dag, ms) },
1894
+ children: []
1895
+ };
1896
+ const actions = sortById(dag.getDownstream(ms.id));
1897
+ for (const act of actions) {
1898
+ if (scopeFilter && !scopeFilter.has(act.id)) continue;
1899
+ msEntry.children.push({
1900
+ node: { id: act.id, type: act.type, title: act.title, status: act.status, marker: statusMarker(dag, act) },
1901
+ children: []
1902
+ });
1903
+ }
1904
+ declEntry.children.push(msEntry);
1905
+ }
1906
+ tree.push(declEntry);
1907
+ }
1908
+ if (tree.length === 0 && scopeFilter) {
1909
+ const milestones = sortById(dag.getMilestones());
1910
+ for (const ms of milestones) {
1911
+ if (!scopeFilter.has(ms.id)) continue;
1912
+ const msEntry = {
1913
+ node: { id: ms.id, type: ms.type, title: ms.title, status: ms.status, marker: statusMarker(dag, ms) },
1914
+ children: []
1915
+ };
1916
+ const actions = sortById(dag.getDownstream(ms.id));
1917
+ for (const act of actions) {
1918
+ if (scopeFilter && !scopeFilter.has(act.id)) continue;
1919
+ msEntry.children.push({
1920
+ node: { id: act.id, type: act.type, title: act.title, status: act.status, marker: statusMarker(dag, act) },
1921
+ children: []
1922
+ });
1923
+ }
1924
+ tree.push(msEntry);
1925
+ }
1926
+ }
1927
+ return tree;
1928
+ }
1929
+ function formatTree(tree) {
1930
+ const lines = [];
1931
+ for (const root of tree) {
1932
+ lines.push(`${root.node.id}: ${root.node.title} ${root.node.marker}`);
1933
+ renderChildren(root.children, "", lines);
1934
+ lines.push("");
1935
+ }
1936
+ while (lines.length > 0 && lines[lines.length - 1] === "") {
1937
+ lines.pop();
1938
+ }
1939
+ return lines.join("\n") + "\n";
1940
+ }
1941
+ function renderChildren(children, prefix, lines) {
1942
+ for (let i = 0; i < children.length; i++) {
1943
+ const child = children[i];
1944
+ const isLast = i === children.length - 1;
1945
+ const connector = isLast ? "\u2514\u2500\u2500" : "\u251C\u2500\u2500";
1946
+ const childPrefix = isLast ? `${prefix} ` : `${prefix}\u2502 `;
1947
+ lines.push(`${prefix}${connector} ${child.node.id}: ${child.node.title} ${child.node.marker}`);
1948
+ if (child.children.length > 0) {
1949
+ renderChildren(child.children, childPrefix, lines);
1950
+ }
1951
+ }
1952
+ }
1953
+ function runVisualize2(cwd, args) {
1954
+ const graphResult = buildDagFromDisk(cwd);
1955
+ if (graphResult.error) return graphResult;
1956
+ const { dag } = graphResult;
1957
+ const scopeId = args[0] && !args[0].startsWith("--") ? args[0] : null;
1958
+ const outputFile = parseFlag(args, "output");
1959
+ let scopeFilter = null;
1960
+ if (scopeId) {
1961
+ const scopeNode = dag.getNode(scopeId);
1962
+ if (!scopeNode) {
1963
+ return { error: `Node not found: ${scopeId}. Use a valid D-XX or M-XX ID.` };
1964
+ }
1965
+ scopeFilter = getSubtreeNodeIds(dag, scopeId);
1966
+ }
1967
+ const tree = buildTreeData(dag, scopeFilter);
1968
+ const formatted = formatTree(tree);
1969
+ let declCount = 0;
1970
+ let msCount = 0;
1971
+ let actCount = 0;
1972
+ function countNodes(nodes) {
1973
+ for (const entry of nodes) {
1974
+ const type = entry.node.type;
1975
+ if (type === "declaration") declCount++;
1976
+ else if (type === "milestone") msCount++;
1977
+ else if (type === "action") actCount++;
1978
+ countNodes(entry.children);
1979
+ }
1980
+ }
1981
+ countNodes(tree);
1982
+ const result = {
1983
+ scope: scopeId || "full",
1984
+ tree,
1985
+ formatted,
1986
+ stats: { declarations: declCount, milestones: msCount, actions: actCount }
1987
+ };
1988
+ if (outputFile) {
1989
+ const resolvedOutput = resolve(cwd, outputFile);
1990
+ writeFileSync(resolvedOutput, formatted, "utf-8");
1991
+ result.outputFile = resolvedOutput;
1992
+ }
1993
+ return result;
1994
+ }
1995
+ module2.exports = { runVisualize: runVisualize2, getSubtreeNodeIds, buildTreeData, formatTree, statusMarker };
1996
+ }
1997
+ });
1998
+
1999
+ // src/commands/compute-waves.js
2000
+ var require_compute_waves = __commonJS({
2001
+ "src/commands/compute-waves.js"(exports2, module2) {
2002
+ "use strict";
2003
+ var { parseFlag } = require_parse_args();
2004
+ var { buildDagFromDisk } = require_build_dag();
2005
+ var { isCompleted } = require_engine();
2006
+ function runComputeWaves2(cwd, args) {
2007
+ const milestoneId = parseFlag(args, "milestone");
2008
+ if (!milestoneId) {
2009
+ return { error: "Missing --milestone flag. Usage: compute-waves --milestone M-XX" };
2010
+ }
2011
+ const graphResult = buildDagFromDisk(cwd);
2012
+ if ("error" in graphResult) return graphResult;
2013
+ const { dag } = graphResult;
2014
+ const milestone = dag.getNode(milestoneId);
2015
+ if (!milestone) {
2016
+ return { error: `Milestone not found: ${milestoneId}` };
2017
+ }
2018
+ if (milestone.type !== "milestone") {
2019
+ return { error: `${milestoneId} is not a milestone (type: ${milestone.type})` };
2020
+ }
2021
+ const actions = dag.getDownstream(milestoneId).filter((n) => n.type === "action" && !isCompleted(n.status));
2022
+ if (actions.length === 0) {
2023
+ return {
2024
+ milestoneId,
2025
+ milestoneTitle: milestone.title,
2026
+ declarations: [],
2027
+ waves: [],
2028
+ totalActions: 0,
2029
+ allDone: true
2030
+ };
2031
+ }
2032
+ const wave1Actions = actions.map((a) => ({
2033
+ id: a.id,
2034
+ title: a.title,
2035
+ status: a.status,
2036
+ produces: a.metadata.produces || ""
2037
+ })).sort((a, b) => a.id.localeCompare(b.id));
2038
+ const upstream = dag.getUpstream(milestoneId);
2039
+ const declarations = upstream.filter((n) => n.type === "declaration").map((n) => ({ id: n.id, title: n.title }));
2040
+ return {
2041
+ milestoneId,
2042
+ milestoneTitle: milestone.title,
2043
+ declarations,
2044
+ waves: [{ wave: 1, actions: wave1Actions }],
2045
+ totalActions: wave1Actions.length,
2046
+ allDone: false
2047
+ };
2048
+ }
2049
+ module2.exports = { runComputeWaves: runComputeWaves2 };
2050
+ }
2051
+ });
2052
+
2053
+ // src/artifacts/exec-plan.js
2054
+ var require_exec_plan = __commonJS({
2055
+ "src/artifacts/exec-plan.js"(exports2, module2) {
2056
+ "use strict";
2057
+ var { traceUpward } = require_trace();
2058
+ function buildWhyChain(paths, milestoneId, milestoneTitle) {
2059
+ const declMap = /* @__PURE__ */ new Map();
2060
+ for (const path of paths) {
2061
+ for (const node of path) {
2062
+ if (node.type === "declaration") {
2063
+ declMap.set(node.id, node.title);
2064
+ }
2065
+ }
2066
+ }
2067
+ const declarations = [...declMap.entries()].map(([id, title]) => ({ id, title }));
2068
+ const declStrings = declarations.map((d) => `${d.id}: ${d.title}`);
2069
+ const whyChain = declStrings.length > 0 ? `This action causes ${milestoneId} ("${milestoneTitle}") which realizes ${declStrings.join(", ")}` : `This action causes ${milestoneId} ("${milestoneTitle}")`;
2070
+ return { whyChain, declarations };
2071
+ }
2072
+ function generateExecPlan(dag, actionId, milestoneId, waveNumber) {
2073
+ const action = dag.getNode(actionId);
2074
+ const milestone = dag.getNode(milestoneId);
2075
+ if (!action) {
2076
+ return `# Error: Action ${actionId} not found in DAG
2077
+ `;
2078
+ }
2079
+ if (!milestone) {
2080
+ return `# Error: Milestone ${milestoneId} not found in DAG
2081
+ `;
2082
+ }
2083
+ const paths = traceUpward(dag, actionId);
2084
+ const { whyChain, declarations } = buildWhyChain(paths, milestoneId, milestone.title);
2085
+ const produces = action.metadata.produces || "";
2086
+ const description = action.metadata.description || action.title;
2087
+ const contextRefs = [
2088
+ "@.planning/FUTURE.md",
2089
+ "@.planning/MILESTONES.md"
2090
+ ];
2091
+ const lines = [
2092
+ "---",
2093
+ `phase: ${milestoneId}`,
2094
+ `plan: ${actionId}`,
2095
+ "type: execute",
2096
+ `wave: ${waveNumber}`,
2097
+ "depends_on: []",
2098
+ "files_modified: []",
2099
+ "autonomous: true",
2100
+ "---",
2101
+ "",
2102
+ "<objective>",
2103
+ action.title,
2104
+ "",
2105
+ `Purpose: ${whyChain}`,
2106
+ `Output: ${produces || "See action description"}`,
2107
+ "</objective>",
2108
+ "",
2109
+ "<context>",
2110
+ ...contextRefs,
2111
+ "</context>",
2112
+ "",
2113
+ "<tasks>",
2114
+ "",
2115
+ '<task type="auto">',
2116
+ ` <name>Task 1: ${action.title}</name>`,
2117
+ ` <files>${produces || "TBD - executor determines from action scope"}</files>`,
2118
+ " <action>",
2119
+ description,
2120
+ "",
2121
+ `Context: ${whyChain}`,
2122
+ " </action>",
2123
+ ` <verify>Verify that the action's output exists and functions correctly</verify>`,
2124
+ ` <done>${produces || action.title} is complete and verified</done>`,
2125
+ "</task>",
2126
+ "",
2127
+ "</tasks>",
2128
+ "",
2129
+ "<verification>",
2130
+ "1. Action produces artifacts exist on disk",
2131
+ "2. Any tests related to this action pass",
2132
+ "3. Git commits reflect the work done",
2133
+ "</verification>",
2134
+ "",
2135
+ "<success_criteria>",
2136
+ `${action.title} is complete, verified, and advances milestone ${milestoneId}`,
2137
+ "</success_criteria>",
2138
+ "",
2139
+ "<output>",
2140
+ "After completion, commit atomically and report results to orchestrator.",
2141
+ "</output>",
2142
+ ""
2143
+ ];
2144
+ return lines.join("\n");
2145
+ }
2146
+ module2.exports = { generateExecPlan, buildWhyChain };
2147
+ }
2148
+ });
2149
+
2150
+ // src/commands/generate-exec-plan.js
2151
+ var require_generate_exec_plan = __commonJS({
2152
+ "src/commands/generate-exec-plan.js"(exports2, module2) {
2153
+ "use strict";
2154
+ var { writeFileSync } = require("node:fs");
2155
+ var { join } = require("node:path");
2156
+ var { parseFlag } = require_parse_args();
2157
+ var { buildDagFromDisk } = require_build_dag();
2158
+ var { generateExecPlan } = require_exec_plan();
2159
+ var { findMilestoneFolder } = require_milestone_folders();
2160
+ function runGenerateExecPlan2(cwd, args) {
2161
+ const actionId = parseFlag(args, "action");
2162
+ if (!actionId) {
2163
+ return { error: "Missing --action flag. Usage: generate-exec-plan --action A-XX --milestone M-XX" };
2164
+ }
2165
+ const milestoneId = parseFlag(args, "milestone");
2166
+ if (!milestoneId) {
2167
+ return { error: "Missing --milestone flag. Usage: generate-exec-plan --action A-XX --milestone M-XX" };
2168
+ }
2169
+ const waveStr = parseFlag(args, "wave");
2170
+ const wave = waveStr ? parseInt(waveStr, 10) : 1;
2171
+ const graphResult = buildDagFromDisk(cwd);
2172
+ if ("error" in graphResult) return graphResult;
2173
+ const { dag } = graphResult;
2174
+ const action = dag.getNode(actionId);
2175
+ if (!action) {
2176
+ return { error: `Action not found: ${actionId}` };
2177
+ }
2178
+ if (action.type !== "action") {
2179
+ return { error: `${actionId} is not an action (type: ${action.type})` };
2180
+ }
2181
+ const milestone = dag.getNode(milestoneId);
2182
+ if (!milestone) {
2183
+ return { error: `Milestone not found: ${milestoneId}` };
2184
+ }
2185
+ if (milestone.type !== "milestone") {
2186
+ return { error: `${milestoneId} is not a milestone (type: ${milestone.type})` };
2187
+ }
2188
+ const content = generateExecPlan(dag, actionId, milestoneId, wave);
2189
+ const planningDir = join(cwd, ".planning");
2190
+ const milestoneFolder = findMilestoneFolder(planningDir, milestoneId);
2191
+ if (!milestoneFolder) {
2192
+ return { error: `Milestone folder not found for ${milestoneId}. Run /declare:actions first to create the milestone plan.` };
2193
+ }
2194
+ const numericSuffix = actionId.split("-")[1];
2195
+ const filename = `EXEC-PLAN-${numericSuffix}.md`;
2196
+ const outputPath = join(milestoneFolder, filename);
2197
+ writeFileSync(outputPath, content, "utf-8");
2198
+ return {
2199
+ actionId,
2200
+ milestoneId,
2201
+ wave,
2202
+ outputPath,
2203
+ content: content.substring(0, 200)
2204
+ };
2205
+ }
2206
+ module2.exports = { runGenerateExecPlan: runGenerateExecPlan2 };
2207
+ }
2208
+ });
2209
+
2210
+ // src/commands/verify-wave.js
2211
+ var require_verify_wave = __commonJS({
2212
+ "src/commands/verify-wave.js"(exports2, module2) {
2213
+ "use strict";
2214
+ var { existsSync } = require("node:fs");
2215
+ var { resolve } = require("node:path");
2216
+ var { parseFlag } = require_parse_args();
2217
+ var { buildDagFromDisk } = require_build_dag();
2218
+ var { traceUpward } = require_trace();
2219
+ var { isCompleted } = require_engine();
2220
+ function looksLikeFilePath(produces) {
2221
+ if (!produces || produces.trim() === "") return false;
2222
+ return /[/\\]/.test(produces) || /\.\w{1,10}$/.test(produces);
2223
+ }
2224
+ function runVerifyWave2(cwd, args) {
2225
+ const milestoneId = parseFlag(args, "milestone");
2226
+ if (!milestoneId) {
2227
+ return { error: 'Missing --milestone flag. Usage: verify-wave --milestone M-XX --actions "A-01,A-02"' };
2228
+ }
2229
+ const actionsStr = parseFlag(args, "actions");
2230
+ if (!actionsStr) {
2231
+ return { error: 'Missing --actions flag. Usage: verify-wave --milestone M-XX --actions "A-01,A-02"' };
2232
+ }
2233
+ const completedActionIds = actionsStr.split(",").map((s) => s.trim()).filter(Boolean);
2234
+ const graphResult = buildDagFromDisk(cwd);
2235
+ if ("error" in graphResult) return graphResult;
2236
+ const { dag } = graphResult;
2237
+ const milestone = dag.getNode(milestoneId);
2238
+ if (!milestone) {
2239
+ return { error: `Milestone not found: ${milestoneId}` };
2240
+ }
2241
+ if (milestone.type !== "milestone") {
2242
+ return { error: `${milestoneId} is not a milestone (type: ${milestone.type})` };
2243
+ }
2244
+ const allChecks = [];
2245
+ const completedActions = [];
2246
+ for (const actionId of completedActionIds) {
2247
+ const action = dag.getNode(actionId);
2248
+ allChecks.push({
2249
+ actionId,
2250
+ check: "action-exists",
2251
+ passed: !!action
2252
+ });
2253
+ if (!action) {
2254
+ completedActions.push({ id: actionId, title: "(not found)", producesExist: null });
2255
+ continue;
2256
+ }
2257
+ const produces = action.metadata.produces || "";
2258
+ if (looksLikeFilePath(produces)) {
2259
+ const filePath = resolve(cwd, produces);
2260
+ const exists = existsSync(filePath);
2261
+ allChecks.push({
2262
+ actionId,
2263
+ check: "produces-exist",
2264
+ passed: exists
2265
+ });
2266
+ completedActions.push({ id: actionId, title: action.title, producesExist: exists });
2267
+ } else {
2268
+ allChecks.push({
2269
+ actionId,
2270
+ check: "produces-exist",
2271
+ passed: true
2272
+ });
2273
+ completedActions.push({ id: actionId, title: action.title, producesExist: null });
2274
+ }
2275
+ }
2276
+ const allMilestoneActions = dag.getDownstream(milestoneId).filter((n) => n.type === "action");
2277
+ const milestoneCompletable = allMilestoneActions.every(
2278
+ (a) => isCompleted(a.status) || completedActionIds.includes(a.id)
2279
+ );
2280
+ const paths = traceUpward(dag, milestoneId);
2281
+ const declMap = /* @__PURE__ */ new Map();
2282
+ for (const path of paths) {
2283
+ for (const node of path) {
2284
+ if (node.type === "declaration") {
2285
+ declMap.set(node.id, node.title);
2286
+ }
2287
+ }
2288
+ }
2289
+ const declarations = [...declMap.entries()].map(([id, title]) => ({ id, title }));
2290
+ const declStrings = declarations.map((d) => `${d.id}: ${d.title}`);
2291
+ const whyChain = declStrings.length > 0 ? `${milestoneId} ("${milestone.title}") realizes ${declStrings.join(", ")}` : `${milestoneId} ("${milestone.title}")`;
2292
+ const passed = allChecks.every((c) => c.passed);
2293
+ return {
2294
+ milestoneId,
2295
+ milestoneTitle: milestone.title,
2296
+ completedActions,
2297
+ milestoneCompletable,
2298
+ traceContext: {
2299
+ declarations,
2300
+ whyChain
2301
+ },
2302
+ allChecks,
2303
+ passed
2304
+ };
2305
+ }
2306
+ module2.exports = { runVerifyWave: runVerifyWave2 };
2307
+ }
2308
+ });
2309
+
2310
+ // src/commands/execute.js
2311
+ var require_execute = __commonJS({
2312
+ "src/commands/execute.js"(exports2, module2) {
2313
+ "use strict";
2314
+ var { join } = require("node:path");
2315
+ var { parseFlag } = require_parse_args();
2316
+ var { buildDagFromDisk } = require_build_dag();
2317
+ var { findMilestoneFolder } = require_milestone_folders();
2318
+ var { isCompleted } = require_engine();
2319
+ function runExecute2(cwd, args) {
2320
+ const milestoneId = parseFlag(args, "milestone");
2321
+ const graphResult = buildDagFromDisk(cwd);
2322
+ if ("error" in graphResult) return graphResult;
2323
+ const { dag, milestones } = graphResult;
2324
+ if (!milestoneId) {
2325
+ const milestonePicker = milestones.map((m) => {
2326
+ const actions = dag.getDownstream(m.id).filter((n) => n.type === "action");
2327
+ const doneCount2 = actions.filter((a) => isCompleted(a.status)).length;
2328
+ return {
2329
+ id: m.id,
2330
+ title: m.title,
2331
+ status: m.status,
2332
+ actionCount: actions.length,
2333
+ doneCount: doneCount2
2334
+ };
2335
+ });
2336
+ return { milestones: milestonePicker };
2337
+ }
2338
+ const milestone = dag.getNode(milestoneId);
2339
+ if (!milestone) {
2340
+ return { error: `Milestone not found: ${milestoneId}` };
2341
+ }
2342
+ if (milestone.type !== "milestone") {
2343
+ return { error: `${milestoneId} is not a milestone (type: ${milestone.type})` };
2344
+ }
2345
+ const allActionsRaw = dag.getDownstream(milestoneId).filter((n) => n.type === "action");
2346
+ const allActions = allActionsRaw.map((a) => ({
2347
+ id: a.id,
2348
+ title: a.title,
2349
+ status: a.status,
2350
+ produces: a.metadata.produces || ""
2351
+ })).sort((a, b) => a.id.localeCompare(b.id));
2352
+ const pendingActions = allActions.filter((a) => !isCompleted(a.status));
2353
+ const doneCount = allActions.length - pendingActions.length;
2354
+ const waves = [];
2355
+ if (pendingActions.length > 0) {
2356
+ waves.push({ wave: 1, actions: pendingActions });
2357
+ }
2358
+ const upstream = dag.getUpstream(milestoneId);
2359
+ const declarations = upstream.filter((n) => n.type === "declaration").map((n) => ({ id: n.id, title: n.title }));
2360
+ const planningDir = join(cwd, ".planning");
2361
+ const milestoneFolderPath = findMilestoneFolder(planningDir, milestoneId);
2362
+ return {
2363
+ milestoneId,
2364
+ milestoneTitle: milestone.title,
2365
+ status: milestone.status,
2366
+ declarations,
2367
+ allActions,
2368
+ pendingActions,
2369
+ waves,
2370
+ totalActions: allActions.length,
2371
+ pendingCount: pendingActions.length,
2372
+ doneCount,
2373
+ allDone: pendingActions.length === 0,
2374
+ milestoneFolderPath
2375
+ };
2376
+ }
2377
+ module2.exports = { runExecute: runExecute2 };
2378
+ }
2379
+ });
2380
+
2381
+ // src/commands/verify-milestone.js
2382
+ var require_verify_milestone = __commonJS({
2383
+ "src/commands/verify-milestone.js"(exports2, module2) {
2384
+ "use strict";
2385
+ var { existsSync, readFileSync, statSync } = require("node:fs");
2386
+ var { resolve, join } = require("node:path");
2387
+ var { execFileSync } = require("node:child_process");
2388
+ var { parseFlag } = require_parse_args();
2389
+ var { buildDagFromDisk } = require_build_dag();
2390
+ var { findMilestoneFolder } = require_milestone_folders();
2391
+ var { parsePlanFile } = require_plan();
2392
+ var { traceUpward } = require_trace();
2393
+ function looksLikeFilePath(produces) {
2394
+ if (!produces || produces.trim() === "") return false;
2395
+ return /[/\\]/.test(produces) || /\.\w{1,10}$/.test(produces);
2396
+ }
2397
+ function runVerifyMilestone2(cwd, args) {
2398
+ const milestoneId = parseFlag(args, "milestone");
2399
+ if (!milestoneId) {
2400
+ return { error: "Missing --milestone flag. Usage: verify-milestone --milestone M-XX" };
2401
+ }
2402
+ const graphResult = buildDagFromDisk(cwd);
2403
+ if ("error" in graphResult) return graphResult;
2404
+ const { dag } = graphResult;
2405
+ const milestone = dag.getNode(milestoneId);
2406
+ if (!milestone) {
2407
+ return { error: `Milestone not found: ${milestoneId}` };
2408
+ }
2409
+ if (milestone.type !== "milestone") {
2410
+ return { error: `${milestoneId} is not a milestone (type: ${milestone.type})` };
2411
+ }
2412
+ const planningDir = join(cwd, ".planning");
2413
+ const milestoneFolder = findMilestoneFolder(planningDir, milestoneId);
2414
+ const criteria = [];
2415
+ let scCounter = 1;
2416
+ if (milestoneFolder) {
2417
+ const planPath = join(milestoneFolder, "PLAN.md");
2418
+ if (existsSync(planPath)) {
2419
+ const planContent = readFileSync(planPath, "utf-8");
2420
+ const plan = parsePlanFile(planContent);
2421
+ for (const action of plan.actions) {
2422
+ if (!action.produces) continue;
2423
+ if (looksLikeFilePath(action.produces)) {
2424
+ const filePath = resolve(cwd, action.produces);
2425
+ const exists = existsSync(filePath);
2426
+ let evidence = exists ? `File exists` : `File not found: ${action.produces}`;
2427
+ if (exists) {
2428
+ try {
2429
+ const stats = statSync(filePath);
2430
+ evidence = `File exists (${stats.size} bytes)`;
2431
+ } catch {
2432
+ }
2433
+ }
2434
+ criteria.push({
2435
+ id: `SC-${String(scCounter).padStart(2, "0")}`,
2436
+ type: "artifact",
2437
+ passed: exists,
2438
+ description: `${action.id} produces ${action.produces}`,
2439
+ evidence,
2440
+ actionId: action.id
2441
+ });
2442
+ scCounter++;
2443
+ }
2444
+ }
2445
+ }
2446
+ }
2447
+ const packagePath = join(cwd, "package.json");
2448
+ if (existsSync(packagePath)) {
2449
+ try {
2450
+ const pkg = JSON.parse(readFileSync(packagePath, "utf-8"));
2451
+ if (pkg.scripts && pkg.scripts.test) {
2452
+ let testPassed = false;
2453
+ let testEvidence = "";
2454
+ try {
2455
+ const result = execFileSync("npm", ["test"], {
2456
+ cwd,
2457
+ timeout: 6e4,
2458
+ stdio: "pipe"
2459
+ });
2460
+ testPassed = true;
2461
+ testEvidence = "npm test exited with code 0";
2462
+ } catch (err) {
2463
+ testPassed = false;
2464
+ const stderr = err.stderr ? err.stderr.toString().slice(0, 200) : "";
2465
+ testEvidence = `npm test failed (exit code ${err.status || "unknown"}): ${stderr}`;
2466
+ }
2467
+ criteria.push({
2468
+ id: `SC-${String(scCounter).padStart(2, "0")}`,
2469
+ type: "test",
2470
+ passed: testPassed,
2471
+ description: "npm test passes",
2472
+ evidence: testEvidence
2473
+ });
2474
+ scCounter++;
2475
+ }
2476
+ } catch {
2477
+ }
2478
+ }
2479
+ criteria.push({
2480
+ id: `SC-${String(scCounter).padStart(2, "0")}`,
2481
+ type: "ai",
2482
+ passed: null,
2483
+ description: "AI assessment of milestone truth alignment",
2484
+ evidence: null
2485
+ });
2486
+ const programmaticCriteria = criteria.filter((c) => c.type !== "ai");
2487
+ const programmaticPassed = programmaticCriteria.length === 0 || programmaticCriteria.every((c) => c.passed === true);
2488
+ const paths = traceUpward(dag, milestoneId);
2489
+ const declMap = /* @__PURE__ */ new Map();
2490
+ for (const path of paths) {
2491
+ for (const node of path) {
2492
+ if (node.type === "declaration") {
2493
+ declMap.set(node.id, node.title);
2494
+ }
2495
+ }
2496
+ }
2497
+ const declarations = [...declMap.entries()].map(([id, title]) => ({ id, title }));
2498
+ const declStrings = declarations.map((d) => `${d.id}: ${d.title}`);
2499
+ const whyChain = declStrings.length > 0 ? `${milestoneId} ("${milestone.title}") realizes ${declStrings.join(", ")}` : `${milestoneId} ("${milestone.title}")`;
2500
+ return {
2501
+ milestoneId,
2502
+ milestoneTitle: milestone.title,
2503
+ milestoneFolder,
2504
+ criteria,
2505
+ programmaticPassed,
2506
+ aiAssessmentNeeded: true,
2507
+ traceContext: {
2508
+ declarations,
2509
+ whyChain
2510
+ }
2511
+ };
2512
+ }
2513
+ module2.exports = { runVerifyMilestone: runVerifyMilestone2 };
2514
+ }
2515
+ });
2516
+
2517
+ // src/commands/check-drift.js
2518
+ var require_check_drift = __commonJS({
2519
+ "src/commands/check-drift.js"(exports2, module2) {
2520
+ "use strict";
2521
+ var { buildDagFromDisk } = require_build_dag();
2522
+ var { findOrphans } = require_engine();
2523
+ function findNearestConnections(dag, orphanId) {
2524
+ const node = dag.getNode(orphanId);
2525
+ if (!node) return [];
2526
+ const suggestions = [];
2527
+ if (node.type === "action") {
2528
+ const milestones = dag.getMilestones();
2529
+ for (const m of milestones) {
2530
+ const downstream = dag.getDownstream(m.id);
2531
+ const hasActions = downstream.some((d) => d.type === "action");
2532
+ if (hasActions) {
2533
+ suggestions.push({
2534
+ type: "connect",
2535
+ target: m.id,
2536
+ targetTitle: m.title,
2537
+ reason: `Milestone already has ${downstream.filter((d) => d.type === "action").length} action(s)`
2538
+ });
2539
+ }
2540
+ if (suggestions.length >= 3) break;
2541
+ }
2542
+ if (suggestions.length === 0) {
2543
+ for (const m of milestones) {
2544
+ suggestions.push({
2545
+ type: "connect",
2546
+ target: m.id,
2547
+ targetTitle: m.title,
2548
+ reason: "Available milestone"
2549
+ });
2550
+ if (suggestions.length >= 3) break;
2551
+ }
2552
+ }
2553
+ } else if (node.type === "milestone") {
2554
+ const declarations = dag.getDeclarations();
2555
+ for (const d of declarations) {
2556
+ if (d.status === "ACTIVE" || d.status === "PENDING") {
2557
+ suggestions.push({
2558
+ type: "connect",
2559
+ target: d.id,
2560
+ targetTitle: d.title,
2561
+ reason: `Declaration is ${d.status}`
2562
+ });
2563
+ }
2564
+ if (suggestions.length >= 3) break;
2565
+ }
2566
+ if (suggestions.length === 0) {
2567
+ for (const d of declarations) {
2568
+ suggestions.push({
2569
+ type: "connect",
2570
+ target: d.id,
2571
+ targetTitle: d.title,
2572
+ reason: "Available declaration"
2573
+ });
2574
+ if (suggestions.length >= 3) break;
2575
+ }
2576
+ }
2577
+ }
2578
+ return suggestions.slice(0, 3);
2579
+ }
2580
+ function runCheckDrift2(cwd) {
2581
+ const graphResult = buildDagFromDisk(cwd);
2582
+ if ("error" in graphResult) return graphResult;
2583
+ const { dag } = graphResult;
2584
+ const orphans = findOrphans(dag);
2585
+ const driftedNodes = orphans.map((orphan) => ({
2586
+ id: orphan.id,
2587
+ type: orphan.type,
2588
+ title: orphan.title,
2589
+ status: orphan.status,
2590
+ suggestions: findNearestConnections(dag, orphan.id)
2591
+ }));
2592
+ return {
2593
+ hasDrift: driftedNodes.length > 0,
2594
+ driftedNodes
2595
+ };
2596
+ }
2597
+ module2.exports = { runCheckDrift: runCheckDrift2 };
2598
+ }
2599
+ });
2600
+
2601
+ // src/commands/check-occurrence.js
2602
+ var require_check_occurrence = __commonJS({
2603
+ "src/commands/check-occurrence.js"(exports2, module2) {
2604
+ "use strict";
2605
+ var { parseFlag } = require_parse_args();
2606
+ var { buildDagFromDisk } = require_build_dag();
2607
+ var { isCompleted } = require_engine();
2608
+ function runCheckOccurrence2(cwd, args) {
2609
+ const targetDecl = parseFlag(args || [], "declaration");
2610
+ const graphResult = buildDagFromDisk(cwd);
2611
+ if ("error" in graphResult) return graphResult;
2612
+ const { dag } = graphResult;
2613
+ const allDeclarations = dag.getDeclarations();
2614
+ const declarations = targetDecl ? allDeclarations.filter((d) => d.id === targetDecl) : allDeclarations;
2615
+ if (targetDecl && declarations.length === 0) {
2616
+ return { error: `Declaration not found: ${targetDecl}` };
2617
+ }
2618
+ const result = declarations.map((decl) => {
2619
+ const downstream = dag.getDownstream(decl.id);
2620
+ const milestones = downstream.filter((n) => n.type === "milestone");
2621
+ let totalActions = 0;
2622
+ let completedActions = 0;
2623
+ for (const m of milestones) {
2624
+ const mDownstream = dag.getDownstream(m.id);
2625
+ const actions = mDownstream.filter((n) => n.type === "action");
2626
+ totalActions += actions.length;
2627
+ completedActions += actions.filter((a) => isCompleted(a.status)).length;
2628
+ }
2629
+ return {
2630
+ declarationId: decl.id,
2631
+ statement: decl.metadata?.statement || decl.title,
2632
+ status: decl.status,
2633
+ milestoneCount: milestones.length,
2634
+ milestones: milestones.map((m) => ({
2635
+ id: m.id,
2636
+ title: m.title,
2637
+ status: m.status
2638
+ })),
2639
+ actionSummary: {
2640
+ total: totalActions,
2641
+ completed: completedActions
2642
+ }
2643
+ };
2644
+ });
2645
+ return { declarations: result };
2646
+ }
2647
+ module2.exports = { runCheckOccurrence: runCheckOccurrence2 };
2648
+ }
2649
+ });
2650
+
2651
+ // src/commands/renegotiate.js
2652
+ var require_renegotiate = __commonJS({
2653
+ "src/commands/renegotiate.js"(exports2, module2) {
2654
+ "use strict";
2655
+ var { existsSync, readFileSync, writeFileSync, mkdirSync } = require("node:fs");
2656
+ var { join } = require("node:path");
2657
+ var { parseFlag } = require_parse_args();
2658
+ var { buildDagFromDisk } = require_build_dag();
2659
+ var { parseFutureFile, writeFutureFile, appendToArchive } = require_future();
2660
+ var { isCompleted } = require_engine();
2661
+ function runRenegotiate2(cwd, args) {
2662
+ const declId = parseFlag(args || [], "declaration");
2663
+ const reason = parseFlag(args || [], "reason");
2664
+ if (!declId) {
2665
+ return { error: 'Missing --declaration flag. Usage: renegotiate --declaration D-XX --reason "..."' };
2666
+ }
2667
+ if (!reason) {
2668
+ return { error: 'Missing --reason flag. Usage: renegotiate --declaration D-XX --reason "..."' };
2669
+ }
2670
+ const graphResult = buildDagFromDisk(cwd);
2671
+ if ("error" in graphResult) return graphResult;
2672
+ const { dag } = graphResult;
2673
+ const declNode = dag.getNode(declId);
2674
+ if (!declNode || declNode.type !== "declaration") {
2675
+ return { error: `Declaration not found: ${declId}` };
2676
+ }
2677
+ const planningDir = join(cwd, ".planning");
2678
+ const futurePath = join(planningDir, "FUTURE.md");
2679
+ const archivePath = join(planningDir, "FUTURE-ARCHIVE.md");
2680
+ const futureContent = existsSync(futurePath) ? readFileSync(futurePath, "utf-8") : "";
2681
+ const declarations = parseFutureFile(futureContent);
2682
+ const declEntry = declarations.find((d) => d.id === declId);
2683
+ if (!declEntry) {
2684
+ return { error: `Declaration ${declId} not found in FUTURE.md` };
2685
+ }
2686
+ declEntry.status = "RENEGOTIATED";
2687
+ const headerMatch = futureContent.match(/^# Future: (.+)/m);
2688
+ const projectName = headerMatch ? headerMatch[1].trim() : "Project";
2689
+ writeFileSync(futurePath, writeFutureFile(declarations, projectName));
2690
+ const archivedAt = (/* @__PURE__ */ new Date()).toISOString();
2691
+ const existingArchive = existsSync(archivePath) ? readFileSync(archivePath, "utf-8") : "";
2692
+ const updatedArchive = appendToArchive(existingArchive, {
2693
+ id: declId,
2694
+ title: declEntry.title,
2695
+ statement: declEntry.statement,
2696
+ archivedAt,
2697
+ reason,
2698
+ replacedBy: "",
2699
+ statusAtArchive: "RENEGOTIATED"
2700
+ });
2701
+ writeFileSync(archivePath, updatedArchive);
2702
+ const downstream = dag.getDownstream(declId);
2703
+ const milestones = downstream.filter((n) => n.type === "milestone");
2704
+ const orphanedMilestones = [];
2705
+ for (const m of milestones) {
2706
+ const upstream = dag.getUpstream(m.id);
2707
+ const realizesOnlyThis = upstream.length === 1 && upstream[0].id === declId;
2708
+ if (realizesOnlyThis) {
2709
+ const mDownstream = dag.getDownstream(m.id);
2710
+ const actions = mDownstream.filter((n) => n.type === "action").map((a) => ({ id: a.id, title: a.title, status: a.status }));
2711
+ orphanedMilestones.push({
2712
+ id: m.id,
2713
+ title: m.title,
2714
+ status: m.status,
2715
+ actions
2716
+ });
2717
+ }
2718
+ }
2719
+ return {
2720
+ archived: { id: declId, title: declEntry.title, archivedAt },
2721
+ orphanedMilestones,
2722
+ archivePath,
2723
+ nextStep: "Create replacement declaration or reassign milestones"
2724
+ };
2725
+ }
2726
+ module2.exports = { runRenegotiate: runRenegotiate2 };
2727
+ }
2728
+ });
2729
+
2730
+ // src/declare-tools.js
2731
+ var { commitPlanningDocs } = require_commit();
2732
+ var { runInit } = require_init();
2733
+ var { runStatus } = require_status();
2734
+ var { runHelp } = require_help();
2735
+ var { runAddDeclaration } = require_add_declaration();
2736
+ var { runAddMilestone } = require_add_milestone();
2737
+ var { runAddMilestonesBatch } = require_add_milestones_batch();
2738
+ var { runAddAction } = require_add_action();
2739
+ var { runCreatePlan } = require_create_plan();
2740
+ var { runLoadGraph } = require_load_graph();
2741
+ var { runTrace } = require_trace();
2742
+ var { runPrioritize } = require_prioritize();
2743
+ var { runVisualize } = require_visualize();
2744
+ var { runComputeWaves } = require_compute_waves();
2745
+ var { runGenerateExecPlan } = require_generate_exec_plan();
2746
+ var { runVerifyWave } = require_verify_wave();
2747
+ var { runExecute } = require_execute();
2748
+ var { runVerifyMilestone } = require_verify_milestone();
2749
+ var { runCheckDrift } = require_check_drift();
2750
+ var { runCheckOccurrence } = require_check_occurrence();
2751
+ var { runComputePerformance } = require_compute_performance();
2752
+ var { runRenegotiate } = require_renegotiate();
2753
+ function parseCwdFlag(argv) {
2754
+ const idx = argv.indexOf("--cwd");
2755
+ if (idx === -1 || idx + 1 >= argv.length) return null;
2756
+ return argv[idx + 1];
2757
+ }
2758
+ function parsePositionalArgs(argv) {
2759
+ const positional = [];
2760
+ let i = 0;
2761
+ while (i < argv.length) {
2762
+ if (argv[i] === "--cwd") {
2763
+ i += 2;
2764
+ } else if (argv[i] === "--files") {
2765
+ i++;
2766
+ while (i < argv.length && !argv[i].startsWith("--")) i++;
2767
+ } else if (argv[i].startsWith("--")) {
2768
+ i++;
2769
+ } else {
2770
+ positional.push(argv[i]);
2771
+ i++;
2772
+ }
2773
+ }
2774
+ return positional;
2775
+ }
2776
+ function parseFilesFlag(argv) {
2777
+ const idx = argv.indexOf("--files");
2778
+ if (idx === -1) return [];
2779
+ const files = [];
2780
+ for (let i = idx + 1; i < argv.length; i++) {
2781
+ if (argv[i].startsWith("--")) break;
2782
+ files.push(argv[i]);
2783
+ }
2784
+ return files;
2785
+ }
2786
+ function main() {
2787
+ const args = process.argv.slice(2);
2788
+ const command = args[0];
2789
+ if (!command) {
2790
+ console.log(JSON.stringify({ error: "No command specified. Use: commit, init, status, add-declaration, add-milestone, add-milestones, create-plan, load-graph, trace, prioritize, visualize, compute-waves, generate-exec-plan, verify-wave, verify-milestone, execute, check-drift, check-occurrence, compute-performance, renegotiate, help" }));
2791
+ process.exit(1);
2792
+ }
2793
+ try {
2794
+ switch (command) {
2795
+ case "commit": {
2796
+ const message = args[1];
2797
+ if (!message) {
2798
+ console.log(JSON.stringify({ error: "commit requires a message argument" }));
2799
+ process.exit(1);
2800
+ }
2801
+ const files = parseFilesFlag(args);
2802
+ const cwd = process.cwd();
2803
+ const result = commitPlanningDocs(cwd, message, files);
2804
+ console.log(JSON.stringify(result));
2805
+ process.exit(result.committed || result.reason === "nothing_to_commit" ? 0 : 1);
2806
+ break;
2807
+ }
2808
+ case "init": {
2809
+ const cwdInit = parseCwdFlag(args) || process.cwd();
2810
+ const initArgs = parsePositionalArgs(args.slice(1));
2811
+ const result = runInit(cwdInit, initArgs);
2812
+ console.log(JSON.stringify(result));
2813
+ break;
2814
+ }
2815
+ case "status": {
2816
+ const cwdStatus = parseCwdFlag(args) || process.cwd();
2817
+ const result = runStatus(cwdStatus);
2818
+ console.log(JSON.stringify(result));
2819
+ if (result.error) process.exit(1);
2820
+ break;
2821
+ }
2822
+ case "help": {
2823
+ const result = runHelp();
2824
+ console.log(JSON.stringify(result));
2825
+ break;
2826
+ }
2827
+ case "add-declaration": {
2828
+ const cwdAddDecl = parseCwdFlag(args) || process.cwd();
2829
+ const result = runAddDeclaration(cwdAddDecl, args.slice(1));
2830
+ console.log(JSON.stringify(result));
2831
+ if (result.error) process.exit(1);
2832
+ break;
2833
+ }
2834
+ case "add-milestone": {
2835
+ const cwdAddMs = parseCwdFlag(args) || process.cwd();
2836
+ const result = runAddMilestone(cwdAddMs, args.slice(1));
2837
+ console.log(JSON.stringify(result));
2838
+ if (result.error) process.exit(1);
2839
+ break;
2840
+ }
2841
+ case "add-milestones": {
2842
+ const cwdAddMsBatch = parseCwdFlag(args) || process.cwd();
2843
+ const result = runAddMilestonesBatch(cwdAddMsBatch, args.slice(1));
2844
+ console.log(JSON.stringify(result));
2845
+ if (result.error) process.exit(1);
2846
+ break;
2847
+ }
2848
+ case "add-action": {
2849
+ const cwdAddAct = parseCwdFlag(args) || process.cwd();
2850
+ const result = runAddAction(cwdAddAct, args.slice(1));
2851
+ console.log(JSON.stringify(result));
2852
+ if (result.error) process.exit(1);
2853
+ break;
2854
+ }
2855
+ case "create-plan": {
2856
+ const cwdCreatePlan = parseCwdFlag(args) || process.cwd();
2857
+ const result = runCreatePlan(cwdCreatePlan, args.slice(1));
2858
+ console.log(JSON.stringify(result));
2859
+ if (result.error) process.exit(1);
2860
+ break;
2861
+ }
2862
+ case "load-graph": {
2863
+ const cwdLoadGraph = parseCwdFlag(args) || process.cwd();
2864
+ const result = runLoadGraph(cwdLoadGraph);
2865
+ console.log(JSON.stringify(result));
2866
+ if (result.error) process.exit(1);
2867
+ break;
2868
+ }
2869
+ case "trace": {
2870
+ const cwdTrace = parseCwdFlag(args) || process.cwd();
2871
+ const result = runTrace(cwdTrace, args.slice(1));
2872
+ console.log(JSON.stringify(result));
2873
+ if (result.error) process.exit(1);
2874
+ break;
2875
+ }
2876
+ case "prioritize": {
2877
+ const cwdPrioritize = parseCwdFlag(args) || process.cwd();
2878
+ const result = runPrioritize(cwdPrioritize, args.slice(1));
2879
+ console.log(JSON.stringify(result));
2880
+ if (result.error) process.exit(1);
2881
+ break;
2882
+ }
2883
+ case "visualize": {
2884
+ const cwdVisualize = parseCwdFlag(args) || process.cwd();
2885
+ const result = runVisualize(cwdVisualize, args.slice(1));
2886
+ console.log(JSON.stringify(result));
2887
+ if (result.error) process.exit(1);
2888
+ break;
2889
+ }
2890
+ case "compute-waves": {
2891
+ const cwdComputeWaves = parseCwdFlag(args) || process.cwd();
2892
+ const result = runComputeWaves(cwdComputeWaves, args.slice(1));
2893
+ console.log(JSON.stringify(result));
2894
+ if (result.error) process.exit(1);
2895
+ break;
2896
+ }
2897
+ case "generate-exec-plan": {
2898
+ const cwdGenExecPlan = parseCwdFlag(args) || process.cwd();
2899
+ const result = runGenerateExecPlan(cwdGenExecPlan, args.slice(1));
2900
+ console.log(JSON.stringify(result));
2901
+ if (result.error) process.exit(1);
2902
+ break;
2903
+ }
2904
+ case "verify-wave": {
2905
+ const cwdVerifyWave = parseCwdFlag(args) || process.cwd();
2906
+ const result = runVerifyWave(cwdVerifyWave, args.slice(1));
2907
+ console.log(JSON.stringify(result));
2908
+ if (result.error) process.exit(1);
2909
+ break;
2910
+ }
2911
+ case "execute": {
2912
+ const cwdExecute = parseCwdFlag(args) || process.cwd();
2913
+ const result = runExecute(cwdExecute, args.slice(1));
2914
+ console.log(JSON.stringify(result));
2915
+ if (result.error) process.exit(1);
2916
+ break;
2917
+ }
2918
+ case "verify-milestone": {
2919
+ const cwdVerifyMs = parseCwdFlag(args) || process.cwd();
2920
+ const result = runVerifyMilestone(cwdVerifyMs, args.slice(1));
2921
+ console.log(JSON.stringify(result));
2922
+ if (result.error) process.exit(1);
2923
+ break;
2924
+ }
2925
+ case "check-drift": {
2926
+ const cwdCheckDrift = parseCwdFlag(args) || process.cwd();
2927
+ const result = runCheckDrift(cwdCheckDrift);
2928
+ console.log(JSON.stringify(result));
2929
+ if (result.error) process.exit(1);
2930
+ break;
2931
+ }
2932
+ case "check-occurrence": {
2933
+ const cwdCheckOcc = parseCwdFlag(args) || process.cwd();
2934
+ const result = runCheckOccurrence(cwdCheckOcc, args.slice(1));
2935
+ console.log(JSON.stringify(result));
2936
+ if (result.error) process.exit(1);
2937
+ break;
2938
+ }
2939
+ case "compute-performance": {
2940
+ const cwdCompPerf = parseCwdFlag(args) || process.cwd();
2941
+ const result = runComputePerformance(cwdCompPerf);
2942
+ console.log(JSON.stringify(result));
2943
+ if (result.error) process.exit(1);
2944
+ break;
2945
+ }
2946
+ case "renegotiate": {
2947
+ const cwdReneg = parseCwdFlag(args) || process.cwd();
2948
+ const result = runRenegotiate(cwdReneg, args.slice(1));
2949
+ console.log(JSON.stringify(result));
2950
+ if (result.error) process.exit(1);
2951
+ break;
2952
+ }
2953
+ default:
2954
+ console.log(JSON.stringify({ error: `Unknown command: ${command}. Use: commit, init, status, add-declaration, add-milestone, add-milestones, create-plan, load-graph, trace, prioritize, visualize, compute-waves, generate-exec-plan, verify-wave, verify-milestone, execute, check-drift, check-occurrence, compute-performance, renegotiate, help` }));
2955
+ process.exit(1);
2956
+ }
2957
+ } catch (err) {
2958
+ console.log(JSON.stringify({ error: err.message || String(err) }));
2959
+ process.exit(1);
2960
+ }
2961
+ }
2962
+ main();