@riotprompt/riotplan 0.0.4 → 1.0.2-dev.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,58 +1,1574 @@
1
- const PLAN_CONVENTIONS = {
2
- /** Meta-prompt file patterns */
3
- metaPromptPatterns: [
4
- "{code}-prompt.md",
5
- "prompt-of-prompts.md",
6
- "{code}.md"
7
- ],
8
- /** Step file pattern */
9
- stepPattern: /^(\d{2})-(.+)\.md$/,
10
- /** Standard files */
11
- standardFiles: {
12
- summary: "SUMMARY.md",
13
- status: "STATUS.md",
14
- executionPlan: "EXECUTION_PLAN.md",
15
- readme: "README.md"
16
- },
17
- /** Standard directories */
18
- standardDirs: {
19
- plan: "plan",
20
- analysis: "analysis",
21
- architecture: "architecture",
22
- implementation: "implementation",
23
- testing: "testing"
24
- },
25
- /** Status emoji mapping */
26
- statusEmoji: {
1
+ import { P as PLAN_CONVENTIONS, l as loadPlan, r as renderToHtml, a as renderToJson, b as renderToMarkdown } from "./cli-BAr1IsMF.js";
2
+ import { B, C, F, H, M, c, R, S, d, e, f, g, h, i, j, k, m, n, o, p, q, s, t, u, v, w, x, y, z, A, D, E, G, I, J, K, L, N, O, Q, T, U, V, W, X, Y, Z, _, $, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af, ag } from "./cli-BAr1IsMF.js";
3
+ import { access, readFile, writeFile, readdir, mkdir } from "node:fs/promises";
4
+ import { isAbsolute, resolve, relative, join, basename, dirname } from "node:path";
5
+ import { homedir } from "node:os";
6
+ import chalk from "chalk";
7
+ function parseStatus(content, options = {}) {
8
+ const warnings = [];
9
+ const titleMatch = content.match(/^#\s+(.+)$/m);
10
+ const title = titleMatch ? titleMatch[1].trim() : "Unknown Plan";
11
+ const currentState = parseCurrentState(content);
12
+ const stepProgress = parseStepProgress(content);
13
+ const blockers = parseBlockers(content);
14
+ const issues = parseIssues(content);
15
+ const notes = parseNotes(content);
16
+ const document = {
17
+ title,
18
+ currentState,
19
+ stepProgress,
20
+ blockers,
21
+ issues,
22
+ notes
23
+ };
24
+ const completedSteps = stepProgress.filter(
25
+ (s2) => s2.status === "completed"
26
+ ).length;
27
+ const progress = stepProgress.length > 0 ? Math.round(completedSteps / stepProgress.length * 100) : 0;
28
+ const state = {
29
+ status: currentState.status,
30
+ lastUpdatedAt: currentState.lastUpdated ? new Date(currentState.lastUpdated) : /* @__PURE__ */ new Date(),
31
+ blockers: blockers.map(
32
+ (desc, i2) => ({
33
+ id: `blocker-${i2 + 1}`,
34
+ description: desc,
35
+ severity: "medium",
36
+ affectedSteps: [],
37
+ createdAt: /* @__PURE__ */ new Date()
38
+ })
39
+ ),
40
+ issues: issues.map(
41
+ (desc, i2) => ({
42
+ id: `issue-${i2 + 1}`,
43
+ title: desc.substring(0, 50),
44
+ description: desc,
45
+ severity: "medium",
46
+ createdAt: /* @__PURE__ */ new Date()
47
+ })
48
+ ),
49
+ progress
50
+ };
51
+ if (currentState.startedAt) {
52
+ state.startedAt = new Date(currentState.startedAt);
53
+ }
54
+ if (options.steps) {
55
+ const currentStepNum = extractStepNumber(currentState.currentStep);
56
+ if (currentStepNum) state.currentStep = currentStepNum;
57
+ const lastStepNum = extractStepNumber(currentState.lastCompleted);
58
+ if (lastStepNum) state.lastCompletedStep = lastStepNum;
59
+ if (stepProgress.length > 0 && stepProgress.length !== options.steps.length) {
60
+ warnings.push(
61
+ `Step count mismatch: STATUS.md has ${stepProgress.length}, plan has ${options.steps.length}`
62
+ );
63
+ }
64
+ }
65
+ return { document, state, warnings };
66
+ }
67
+ function parseCurrentState(content) {
68
+ const state = {
69
+ status: "pending"
70
+ };
71
+ const statusMatch = content.match(
72
+ /\|\s*\*\*Status\*\*\s*\|\s*([^|]+)/i
73
+ );
74
+ if (statusMatch) {
75
+ const statusText = statusMatch[1].trim();
76
+ const emoji = findStatusEmoji(statusText);
77
+ if (emoji) {
78
+ state.status = parseStatusEmoji(emoji);
79
+ } else {
80
+ state.status = parseStatusText(statusText);
81
+ }
82
+ }
83
+ const currentMatch = content.match(
84
+ /\|\s*\*\*Current Step\*\*\s*\|\s*([^|]+)/i
85
+ );
86
+ if (currentMatch && currentMatch[1].trim() !== "-") {
87
+ state.currentStep = currentMatch[1].trim();
88
+ }
89
+ const lastMatch = content.match(
90
+ /\|\s*\*\*Last Completed\*\*\s*\|\s*([^|]+)/i
91
+ );
92
+ if (lastMatch && lastMatch[1].trim() !== "-") {
93
+ state.lastCompleted = lastMatch[1].trim();
94
+ }
95
+ const startedMatch = content.match(
96
+ /\|\s*\*\*Started\*\*\s*\|\s*([^|]+)/i
97
+ );
98
+ if (startedMatch && startedMatch[1].trim() !== "-") {
99
+ state.startedAt = startedMatch[1].trim();
100
+ }
101
+ const updatedMatch = content.match(
102
+ /\|\s*\*\*Last Updated\*\*\s*\|\s*([^|]+)/i
103
+ );
104
+ if (updatedMatch && updatedMatch[1].trim() !== "-") {
105
+ state.lastUpdated = updatedMatch[1].trim();
106
+ }
107
+ return state;
108
+ }
109
+ const STATUS_EMOJIS = ["⬜", "🔄", "✅", "❌", "⏸️", "⏭️"];
110
+ function findStatusEmoji(text) {
111
+ for (const emoji of STATUS_EMOJIS) {
112
+ if (text.includes(emoji)) {
113
+ return emoji;
114
+ }
115
+ }
116
+ return null;
117
+ }
118
+ function parseStatusEmoji(emoji) {
119
+ return PLAN_CONVENTIONS.emojiToStatus[emoji] || "pending";
120
+ }
121
+ function parseStatusText(text) {
122
+ const normalized = text.toLowerCase().replace(/[^a-z_]/g, "");
123
+ const valid = [
124
+ "pending",
125
+ "in_progress",
126
+ "completed",
127
+ "failed",
128
+ "blocked",
129
+ "skipped"
130
+ ];
131
+ if (valid.includes(normalized)) {
132
+ return normalized;
133
+ }
134
+ if (normalized.includes("progress") || normalized.includes("inprogress")) {
135
+ return "in_progress";
136
+ }
137
+ if (normalized.includes("complete") || normalized.includes("done")) {
138
+ return "completed";
139
+ }
140
+ if (normalized.includes("block")) {
141
+ return "blocked";
142
+ }
143
+ if (normalized.includes("skip")) {
144
+ return "skipped";
145
+ }
146
+ if (normalized.includes("fail") || normalized.includes("error")) {
147
+ return "failed";
148
+ }
149
+ return "pending";
150
+ }
151
+ function parseStepProgress(content) {
152
+ const steps = [];
153
+ const lines = content.split("\n");
154
+ let inTable = false;
155
+ let headerFound = false;
156
+ const tableRows = [];
157
+ for (const line of lines) {
158
+ if (!headerFound && /\|\s*Step\s*\|\s*Name\s*\|/i.test(line)) {
159
+ headerFound = true;
160
+ continue;
161
+ }
162
+ if (headerFound && !inTable && /^\|[-\s|]+\|$/.test(line)) {
163
+ inTable = true;
164
+ continue;
165
+ }
166
+ if (inTable) {
167
+ if (!line.trim() || /^##/.test(line)) {
168
+ break;
169
+ }
170
+ if (line.includes("|")) {
171
+ tableRows.push(line);
172
+ }
173
+ }
174
+ }
175
+ if (tableRows.length === 0) return steps;
176
+ for (const row of tableRows) {
177
+ if (row.match(/^\|[-\s|]+\|$/)) continue;
178
+ const cells = row.split("|").map((c2) => c2.trim()).filter((c2) => c2);
179
+ if (cells.length >= 3) {
180
+ const step = cells[0];
181
+ const name = cells[1];
182
+ const statusStr = cells[2];
183
+ const started = cells[3] || void 0;
184
+ const completed = cells[4] || void 0;
185
+ const notes = cells[5] || void 0;
186
+ const emoji = findStatusEmoji(statusStr);
187
+ const status = emoji ? parseStatusEmoji(emoji) : parseStatusText(statusStr);
188
+ steps.push({
189
+ step,
190
+ name,
191
+ status,
192
+ started: started && started !== "-" ? started : void 0,
193
+ completed: completed && completed !== "-" ? completed : void 0,
194
+ notes: notes && notes !== "-" ? notes : void 0
195
+ });
196
+ }
197
+ }
198
+ return steps;
199
+ }
200
+ function parseBlockers(content) {
201
+ const blockers = [];
202
+ const section = extractSection(content, "Blockers");
203
+ if (!section) return blockers;
204
+ if (section.toLowerCase().includes("none")) {
205
+ return blockers;
206
+ }
207
+ const listItems = section.match(/^[-*]\s+(.+)$/gm);
208
+ if (listItems) {
209
+ for (const item of listItems) {
210
+ const text = item.replace(/^[-*]\s+/, "").trim();
211
+ if (text && !text.toLowerCase().includes("none")) {
212
+ blockers.push(text);
213
+ }
214
+ }
215
+ }
216
+ return blockers;
217
+ }
218
+ function parseIssues(content) {
219
+ const issues = [];
220
+ const section = extractSection(content, "Issues");
221
+ if (!section) return issues;
222
+ if (section.toLowerCase().includes("none")) {
223
+ return issues;
224
+ }
225
+ const listItems = section.match(/^[-*]\s+(.+)$/gm);
226
+ if (listItems) {
227
+ for (const item of listItems) {
228
+ const text = item.replace(/^[-*]\s+/, "").trim();
229
+ if (text && !text.toLowerCase().includes("none")) {
230
+ issues.push(text);
231
+ }
232
+ }
233
+ }
234
+ return issues;
235
+ }
236
+ function parseNotes(content) {
237
+ const section = extractSection(content, "Notes");
238
+ if (!section || section.trim() === "") return void 0;
239
+ const trimmed = section.trim();
240
+ if (trimmed === "-" || trimmed.toLowerCase() === "none" || trimmed.toLowerCase() === "none currently.") {
241
+ return void 0;
242
+ }
243
+ return trimmed;
244
+ }
245
+ function extractSection(content, name) {
246
+ const lines = content.split("\n");
247
+ const sectionLines = [];
248
+ let inSection = false;
249
+ for (const line of lines) {
250
+ if (new RegExp(`^##\\s*${name}`, "i").test(line)) {
251
+ inSection = true;
252
+ continue;
253
+ }
254
+ if (inSection && (/^##/.test(line) || /^---/.test(line))) {
255
+ break;
256
+ }
257
+ if (inSection) {
258
+ sectionLines.push(line);
259
+ }
260
+ }
261
+ return sectionLines.length > 0 ? sectionLines.join("\n").trim() : void 0;
262
+ }
263
+ function extractStepNumber(stepRef) {
264
+ if (!stepRef) return void 0;
265
+ const match = stepRef.match(/(\d+)/);
266
+ return match ? parseInt(match[1]) : void 0;
267
+ }
268
+ function parseRelationshipsFromContent(content) {
269
+ const relationships = [];
270
+ let frontmatter = null;
271
+ if (content.startsWith("---\n")) {
272
+ const endMarker = content.indexOf("\n---", 4);
273
+ if (endMarker !== -1) {
274
+ frontmatter = content.substring(4, endMarker);
275
+ }
276
+ }
277
+ if (frontmatter) {
278
+ const spawnedFrom = frontmatter.match(/spawned-from:\s*(.+)/);
279
+ if (spawnedFrom) {
280
+ relationships.push({
281
+ type: "spawned-from",
282
+ targetPath: spawnedFrom[1].trim()
283
+ });
284
+ }
285
+ const blocksMatch = frontmatter.match(/blocks:\s*(.+)/);
286
+ if (blocksMatch) {
287
+ const paths = blocksMatch[1].split(",").map((p2) => p2.trim());
288
+ for (const path of paths) {
289
+ relationships.push({
290
+ type: "blocks",
291
+ targetPath: path
292
+ });
293
+ }
294
+ }
295
+ const blockedBy = frontmatter.match(/blocked-by:\s*(.+)/);
296
+ if (blockedBy) {
297
+ const paths = blockedBy[1].split(",").map((p2) => p2.trim());
298
+ for (const path of paths) {
299
+ relationships.push({
300
+ type: "blocked-by",
301
+ targetPath: path
302
+ });
303
+ }
304
+ }
305
+ const relatedMatch = frontmatter.match(/related:\s*(.+)/);
306
+ if (relatedMatch) {
307
+ const paths = relatedMatch[1].split(",").map((p2) => p2.trim());
308
+ for (const path of paths) {
309
+ relationships.push({
310
+ type: "related",
311
+ targetPath: path
312
+ });
313
+ }
314
+ }
315
+ }
316
+ const lines = content.split("\n");
317
+ const sectionLines = [];
318
+ let inSection = false;
319
+ for (const line of lines) {
320
+ if (/^##\s+Related\s+Plans?$/i.test(line)) {
321
+ inSection = true;
322
+ continue;
323
+ }
324
+ if (inSection && /^#/.test(line)) {
325
+ break;
326
+ }
327
+ if (inSection) {
328
+ sectionLines.push(line);
329
+ }
330
+ }
331
+ if (sectionLines.length > 0) {
332
+ const section = sectionLines.join("\n");
333
+ const lines2 = section.split("\n");
334
+ for (const line of lines2) {
335
+ const trimmed = line.trim();
336
+ if (!trimmed.startsWith("-")) continue;
337
+ const itemContent = trimmed.slice(1).trim();
338
+ const typePathMatch = itemContent.match(
339
+ /\*\*(\w+(?:-\w+)?)\*\*:\s*([^\s]+)(?:\s+-\s+(.+))?/
340
+ );
341
+ if (typePathMatch) {
342
+ const [, typeStr, path, reason] = typePathMatch;
343
+ const type = normalizeRelationType(typeStr);
344
+ if (type) {
345
+ relationships.push({
346
+ type,
347
+ targetPath: path,
348
+ reason: reason?.trim()
349
+ });
350
+ continue;
351
+ }
352
+ }
353
+ const linkMatch = itemContent.match(
354
+ /\[([^\]]+)\]\(([^)]+)\)(?:\s*-\s*(.+))?/
355
+ );
356
+ if (linkMatch) {
357
+ const [, , path, desc] = linkMatch;
358
+ let type = "related";
359
+ let reason = desc?.trim();
360
+ if (desc) {
361
+ const typeInDesc = desc.match(
362
+ /\((spawned-from|spawned|blocks|blocked-by|related)\)/i
363
+ );
364
+ if (typeInDesc) {
365
+ type = normalizeRelationType(typeInDesc[1]) || "related";
366
+ reason = desc.replace(typeInDesc[0], "").trim();
367
+ } else if (desc.toLowerCase().includes("blocks")) {
368
+ type = "blocks";
369
+ } else if (desc.toLowerCase().includes("blocked")) {
370
+ type = "blocked-by";
371
+ } else if (desc.toLowerCase().includes("spawned from")) {
372
+ type = "spawned-from";
373
+ }
374
+ }
375
+ relationships.push({
376
+ type,
377
+ targetPath: path,
378
+ reason: reason || void 0
379
+ });
380
+ continue;
381
+ }
382
+ const simpleMatch = itemContent.match(
383
+ /([^\s(]+)\s*(?:\((\w+(?:-\w+)?)\))?(?:\s*-\s*(.+))?/
384
+ );
385
+ if (simpleMatch) {
386
+ const [, path, typeStr, reason] = simpleMatch;
387
+ const type = typeStr ? normalizeRelationType(typeStr) || "related" : "related";
388
+ relationships.push({
389
+ type,
390
+ targetPath: path,
391
+ reason: reason?.trim()
392
+ });
393
+ }
394
+ }
395
+ }
396
+ const seen = /* @__PURE__ */ new Set();
397
+ return relationships.filter((r) => {
398
+ const key = `${r.type}:${r.targetPath}`;
399
+ if (seen.has(key)) return false;
400
+ seen.add(key);
401
+ return true;
402
+ });
403
+ }
404
+ function normalizeRelationType(str) {
405
+ const normalized = str.toLowerCase().replace(/_/g, "-");
406
+ const validTypes = [
407
+ "spawned-from",
408
+ "spawned",
409
+ "blocks",
410
+ "blocked-by",
411
+ "related"
412
+ ];
413
+ return validTypes.find((t2) => t2 === normalized) || null;
414
+ }
415
+ async function parseRelationshipsFromPlan(planPath) {
416
+ const absolutePath = resolve(planPath);
417
+ const relationships = [];
418
+ try {
419
+ const summaryPath = join(absolutePath, "SUMMARY.md");
420
+ const content = await readFile(summaryPath, "utf-8");
421
+ relationships.push(...parseRelationshipsFromContent(content));
422
+ } catch {
423
+ }
424
+ try {
425
+ const files = ["prompt-of-prompts.md"];
426
+ const planCode = absolutePath.split("/").pop();
427
+ if (planCode) {
428
+ files.push(`${planCode}-prompt.md`, `${planCode}.md`);
429
+ }
430
+ for (const file of files) {
431
+ try {
432
+ const filePath = join(absolutePath, file);
433
+ const content = await readFile(filePath, "utf-8");
434
+ relationships.push(...parseRelationshipsFromContent(content));
435
+ } catch {
436
+ }
437
+ }
438
+ } catch {
439
+ }
440
+ const seen = /* @__PURE__ */ new Set();
441
+ return relationships.filter((r) => {
442
+ const key = `${r.type}:${r.targetPath}`;
443
+ if (seen.has(key)) return false;
444
+ seen.add(key);
445
+ return true;
446
+ });
447
+ }
448
+ async function addRelationship(plan, options) {
449
+ const { type, targetPath, steps, reason } = options;
450
+ const resolvedTarget = isAbsolute(targetPath) ? targetPath : resolve(plan.metadata.path, targetPath);
451
+ let targetValid = false;
452
+ let targetPlan;
453
+ try {
454
+ await access(resolvedTarget);
455
+ const target = await loadPlan(resolvedTarget);
456
+ targetValid = true;
457
+ targetPlan = {
458
+ code: target.metadata.code,
459
+ name: target.metadata.name,
460
+ path: resolvedTarget
461
+ };
462
+ } catch {
463
+ }
464
+ const relationship = {
465
+ type,
466
+ planPath: targetPath,
467
+ // Store original path
468
+ steps,
469
+ reason,
470
+ createdAt: /* @__PURE__ */ new Date()
471
+ };
472
+ if (!plan.relationships) {
473
+ plan.relationships = [];
474
+ }
475
+ plan.relationships.push(relationship);
476
+ return {
477
+ relationship,
478
+ targetValid,
479
+ targetPlan
480
+ };
481
+ }
482
+ function removeRelationship(plan, targetPath, type) {
483
+ if (!plan.relationships) return [];
484
+ const removed = [];
485
+ plan.relationships = plan.relationships.filter((r) => {
486
+ const matches = r.planPath === targetPath && (type === void 0 || r.type === type);
487
+ if (matches) removed.push(r);
488
+ return !matches;
489
+ });
490
+ return removed;
491
+ }
492
+ function getRelationshipsByType(plan, type) {
493
+ return (plan.relationships || []).filter((r) => r.type === type);
494
+ }
495
+ function getInverseRelationType(type) {
496
+ switch (type) {
497
+ case "spawned-from":
498
+ return "spawned";
499
+ case "spawned":
500
+ return "spawned-from";
501
+ case "blocks":
502
+ return "blocked-by";
503
+ case "blocked-by":
504
+ return "blocks";
505
+ case "related":
506
+ return "related";
507
+ }
508
+ }
509
+ function createBidirectionalRelationship(sourcePlan, targetPlan, type, reason) {
510
+ if (!sourcePlan.relationships) {
511
+ sourcePlan.relationships = [];
512
+ }
513
+ sourcePlan.relationships.push({
514
+ type,
515
+ planPath: relative(sourcePlan.metadata.path, targetPlan.metadata.path),
516
+ reason,
517
+ createdAt: /* @__PURE__ */ new Date()
518
+ });
519
+ if (!targetPlan.relationships) {
520
+ targetPlan.relationships = [];
521
+ }
522
+ targetPlan.relationships.push({
523
+ type: getInverseRelationType(type),
524
+ planPath: relative(targetPlan.metadata.path, sourcePlan.metadata.path),
525
+ reason,
526
+ createdAt: /* @__PURE__ */ new Date()
527
+ });
528
+ }
529
+ async function validateRelationships(plan) {
530
+ const invalid = [];
531
+ const validRelationships = [];
532
+ if (!plan.relationships) {
533
+ return { valid: true, invalid: [], validRelationships: [] };
534
+ }
535
+ for (const rel of plan.relationships) {
536
+ const resolvedPath = isAbsolute(rel.planPath) ? rel.planPath : resolve(plan.metadata.path, rel.planPath);
537
+ try {
538
+ await access(resolvedPath);
539
+ await loadPlan(resolvedPath);
540
+ validRelationships.push(rel);
541
+ } catch {
542
+ invalid.push({
543
+ relationship: rel,
544
+ reason: `Target plan not found: ${rel.planPath}`
545
+ });
546
+ }
547
+ }
548
+ return {
549
+ valid: invalid.length === 0,
550
+ invalid,
551
+ validRelationships
552
+ };
553
+ }
554
+ function getBlockingPlans(plan) {
555
+ return getRelationshipsByType(plan, "blocked-by").map((r) => r.planPath);
556
+ }
557
+ function getBlockedPlans(plan) {
558
+ return getRelationshipsByType(plan, "blocks").map((r) => r.planPath);
559
+ }
560
+ function getParentPlan(plan) {
561
+ const spawnedFrom = getRelationshipsByType(plan, "spawned-from");
562
+ return spawnedFrom.length > 0 ? spawnedFrom[0].planPath : null;
563
+ }
564
+ function getChildPlans(plan) {
565
+ return getRelationshipsByType(plan, "spawned").map((r) => r.planPath);
566
+ }
567
+ function getRelatedPlans(plan) {
568
+ return getRelationshipsByType(plan, "related").map((r) => r.planPath);
569
+ }
570
+ function generateRelationshipsMarkdown(plan) {
571
+ if (!plan.relationships || plan.relationships.length === 0) {
572
+ return "";
573
+ }
574
+ const lines = ["## Related Plans", ""];
575
+ const byType = /* @__PURE__ */ new Map();
576
+ for (const rel of plan.relationships) {
577
+ const list = byType.get(rel.type) || [];
578
+ list.push(rel);
579
+ byType.set(rel.type, list);
580
+ }
581
+ const typeLabels = {
582
+ "spawned-from": "Spawned From",
583
+ spawned: "Spawned",
584
+ blocks: "Blocks",
585
+ "blocked-by": "Blocked By",
586
+ related: "Related"
587
+ };
588
+ for (const [type, rels] of byType) {
589
+ lines.push(`### ${typeLabels[type]}`);
590
+ lines.push("");
591
+ for (const rel of rels) {
592
+ let line = `- **${type}**: ${rel.planPath}`;
593
+ if (rel.reason) {
594
+ line += ` - ${rel.reason}`;
595
+ }
596
+ if (rel.steps && rel.steps.length > 0) {
597
+ line += ` (Steps: ${rel.steps.join(", ")})`;
598
+ }
599
+ lines.push(line);
600
+ }
601
+ lines.push("");
602
+ }
603
+ return lines.join("\n");
604
+ }
605
+ async function updatePlanRelationships(plan) {
606
+ const summaryPath = join(plan.metadata.path, "SUMMARY.md");
607
+ let content;
608
+ try {
609
+ content = await readFile(summaryPath, "utf-8");
610
+ } catch {
611
+ content = `# ${plan.metadata.name}
612
+
613
+ ${plan.metadata.description || "Plan summary."}
614
+
615
+ `;
616
+ }
617
+ const lines = content.split("\n");
618
+ const filtered = [];
619
+ let inRelatedSection = false;
620
+ for (let i2 = 0; i2 < lines.length; i2++) {
621
+ const line = lines[i2];
622
+ if (/^##\s+Related\s+Plans?$/i.test(line)) {
623
+ inRelatedSection = true;
624
+ continue;
625
+ }
626
+ if (inRelatedSection && /^#/.test(line)) {
627
+ inRelatedSection = false;
628
+ }
629
+ if (!inRelatedSection) {
630
+ filtered.push(line);
631
+ }
632
+ }
633
+ content = filtered.join("\n");
634
+ const relSection = generateRelationshipsMarkdown(plan);
635
+ if (relSection) {
636
+ content = content.trimEnd() + "\n\n" + relSection;
637
+ }
638
+ await writeFile(summaryPath, content);
639
+ }
640
+ const REGISTRY_FILENAME = ".riotplan-registry.json";
641
+ const REGISTRY_VERSION = "1.0";
642
+ function getDefaultRegistryPath() {
643
+ return join(homedir(), REGISTRY_FILENAME);
644
+ }
645
+ function createRegistry(searchPaths = []) {
646
+ return {
647
+ version: REGISTRY_VERSION,
648
+ lastUpdatedAt: /* @__PURE__ */ new Date(),
649
+ searchPaths: searchPaths.map((p2) => resolve(p2)),
650
+ plans: /* @__PURE__ */ new Map(),
651
+ byCode: /* @__PURE__ */ new Map(),
652
+ byStatus: /* @__PURE__ */ new Map()
653
+ };
654
+ }
655
+ async function loadRegistry(path) {
656
+ const registryPath = path || getDefaultRegistryPath();
657
+ try {
658
+ await access(registryPath);
659
+ const content = await readFile(registryPath, "utf-8");
660
+ const data = JSON.parse(content);
661
+ const registry = createRegistry(data.searchPaths || []);
662
+ registry.version = data.version || REGISTRY_VERSION;
663
+ registry.lastUpdatedAt = new Date(data.lastUpdatedAt);
664
+ if (data.plans && Array.isArray(data.plans)) {
665
+ for (const plan of data.plans) {
666
+ const entry = {
667
+ ...plan,
668
+ registeredAt: new Date(plan.registeredAt),
669
+ lastScannedAt: new Date(plan.lastScannedAt)
670
+ };
671
+ registry.plans.set(entry.path, entry);
672
+ indexPlan(registry, entry);
673
+ }
674
+ }
675
+ return registry;
676
+ } catch {
677
+ return null;
678
+ }
679
+ }
680
+ async function saveRegistry(registry, path) {
681
+ const registryPath = path || getDefaultRegistryPath();
682
+ registry.lastUpdatedAt = /* @__PURE__ */ new Date();
683
+ const data = {
684
+ version: registry.version,
685
+ lastUpdatedAt: registry.lastUpdatedAt.toISOString(),
686
+ searchPaths: registry.searchPaths,
687
+ plans: Array.from(registry.plans.values()).map((plan) => ({
688
+ ...plan,
689
+ registeredAt: plan.registeredAt.toISOString(),
690
+ lastScannedAt: plan.lastScannedAt.toISOString()
691
+ }))
692
+ };
693
+ await writeFile(registryPath, JSON.stringify(data, null, 2));
694
+ }
695
+ async function scanForPlans(registry, options = {}) {
696
+ const {
697
+ searchPaths = registry.searchPaths,
698
+ maxDepth = 3,
699
+ includeHidden = false,
700
+ excludeDirs = ["node_modules", ".git", "dist", "coverage"]
701
+ } = options;
702
+ let discovered = 0;
703
+ for (const searchPath of searchPaths) {
704
+ const absolutePath = resolve(searchPath);
705
+ try {
706
+ discovered += await scanDirectory(
707
+ registry,
708
+ absolutePath,
709
+ 0,
710
+ maxDepth,
711
+ includeHidden,
712
+ excludeDirs
713
+ );
714
+ } catch {
715
+ }
716
+ }
717
+ for (const path of searchPaths) {
718
+ const absolutePath = resolve(path);
719
+ if (!registry.searchPaths.includes(absolutePath)) {
720
+ registry.searchPaths.push(absolutePath);
721
+ }
722
+ }
723
+ registry.lastUpdatedAt = /* @__PURE__ */ new Date();
724
+ return discovered;
725
+ }
726
+ async function scanDirectory(registry, dirPath, depth, maxDepth, includeHidden, excludeDirs) {
727
+ if (depth > maxDepth) return 0;
728
+ let discovered = 0;
729
+ const dirName = basename(dirPath);
730
+ if (excludeDirs.includes(dirName)) return 0;
731
+ if (!includeHidden && dirName.startsWith(".")) return 0;
732
+ if (await isPlanDirectory(dirPath)) {
733
+ try {
734
+ const entry = await createPlanEntry(dirPath);
735
+ registerPlan(registry, entry);
736
+ discovered++;
737
+ } catch {
738
+ }
739
+ return discovered;
740
+ }
741
+ try {
742
+ const entries = await readdir(dirPath, { withFileTypes: true });
743
+ for (const entry of entries) {
744
+ if (entry.isDirectory()) {
745
+ const subPath = join(dirPath, entry.name);
746
+ discovered += await scanDirectory(
747
+ registry,
748
+ subPath,
749
+ depth + 1,
750
+ maxDepth,
751
+ includeHidden,
752
+ excludeDirs
753
+ );
754
+ }
755
+ }
756
+ } catch {
757
+ }
758
+ return discovered;
759
+ }
760
+ async function isPlanDirectory(dirPath) {
761
+ const indicators = [
762
+ "SUMMARY.md",
763
+ "STATUS.md",
764
+ "EXECUTION_PLAN.md",
765
+ "plan/"
766
+ // plan subdirectory
767
+ ];
768
+ let matches = 0;
769
+ for (const indicator of indicators) {
770
+ try {
771
+ const checkPath = join(dirPath, indicator);
772
+ await access(checkPath);
773
+ matches++;
774
+ } catch {
775
+ }
776
+ }
777
+ if (matches >= 2) return true;
778
+ try {
779
+ const files = await readdir(dirPath);
780
+ const hasSteps = files.some((f2) => PLAN_CONVENTIONS.stepPattern.test(f2));
781
+ if (hasSteps) return true;
782
+ } catch {
783
+ }
784
+ try {
785
+ const planDir = join(dirPath, "plan");
786
+ const files = await readdir(planDir);
787
+ const hasSteps = files.some((f2) => PLAN_CONVENTIONS.stepPattern.test(f2));
788
+ if (hasSteps) return true;
789
+ } catch {
790
+ }
791
+ return false;
792
+ }
793
+ async function createPlanEntry(planPath) {
794
+ const plan = await loadPlan(planPath);
795
+ const now = /* @__PURE__ */ new Date();
796
+ return {
797
+ code: plan.metadata.code,
798
+ name: plan.metadata.name,
799
+ path: planPath,
800
+ status: plan.state.status,
801
+ progress: plan.state.progress,
802
+ stepCount: plan.steps.length,
803
+ completedSteps: plan.steps.filter((s2) => s2.status === "completed").length,
804
+ registeredAt: now,
805
+ lastScannedAt: now,
806
+ tags: plan.metadata.tags,
807
+ description: plan.metadata.description?.slice(0, 200),
808
+ parentPlan: plan.relationships?.find((r) => r.type === "spawned-from")?.planPath
809
+ };
810
+ }
811
+ function registerPlan(registry, entry) {
812
+ const existing = registry.plans.get(entry.path);
813
+ if (existing) {
814
+ removeFromIndex(registry, existing);
815
+ }
816
+ registry.plans.set(entry.path, entry);
817
+ indexPlan(registry, entry);
818
+ }
819
+ function unregisterPlan(registry, path) {
820
+ const entry = registry.plans.get(path);
821
+ if (!entry) return false;
822
+ removeFromIndex(registry, entry);
823
+ registry.plans.delete(path);
824
+ return true;
825
+ }
826
+ function indexPlan(registry, entry) {
827
+ const codePaths = registry.byCode.get(entry.code) || [];
828
+ if (!codePaths.includes(entry.path)) {
829
+ codePaths.push(entry.path);
830
+ registry.byCode.set(entry.code, codePaths);
831
+ }
832
+ const statusPaths = registry.byStatus.get(entry.status) || [];
833
+ if (!statusPaths.includes(entry.path)) {
834
+ statusPaths.push(entry.path);
835
+ registry.byStatus.set(entry.status, statusPaths);
836
+ }
837
+ }
838
+ function removeFromIndex(registry, entry) {
839
+ const codePaths = registry.byCode.get(entry.code);
840
+ if (codePaths) {
841
+ const idx = codePaths.indexOf(entry.path);
842
+ if (idx !== -1) {
843
+ codePaths.splice(idx, 1);
844
+ if (codePaths.length === 0) {
845
+ registry.byCode.delete(entry.code);
846
+ }
847
+ }
848
+ }
849
+ const statusPaths = registry.byStatus.get(entry.status);
850
+ if (statusPaths) {
851
+ const idx = statusPaths.indexOf(entry.path);
852
+ if (idx !== -1) {
853
+ statusPaths.splice(idx, 1);
854
+ if (statusPaths.length === 0) {
855
+ registry.byStatus.delete(entry.status);
856
+ }
857
+ }
858
+ }
859
+ }
860
+ async function refreshPlan(registry, path) {
861
+ const existing = registry.plans.get(path);
862
+ try {
863
+ const entry = await createPlanEntry(path);
864
+ if (existing) {
865
+ entry.registeredAt = existing.registeredAt;
866
+ }
867
+ registerPlan(registry, entry);
868
+ return entry;
869
+ } catch {
870
+ if (existing) {
871
+ unregisterPlan(registry, path);
872
+ }
873
+ return null;
874
+ }
875
+ }
876
+ async function refreshAllPlans(registry) {
877
+ let updated = 0;
878
+ let removed = 0;
879
+ const paths = Array.from(registry.plans.keys());
880
+ for (const path of paths) {
881
+ const result = await refreshPlan(registry, path);
882
+ if (result) {
883
+ updated++;
884
+ } else {
885
+ removed++;
886
+ }
887
+ }
888
+ registry.lastUpdatedAt = /* @__PURE__ */ new Date();
889
+ return { updated, removed };
890
+ }
891
+ function searchPlans(registry, options = {}) {
892
+ let results = Array.from(registry.plans.values());
893
+ if (options.status) {
894
+ const statuses = Array.isArray(options.status) ? options.status : [options.status];
895
+ results = results.filter((p2) => statuses.includes(p2.status));
896
+ }
897
+ if (options.codePattern) {
898
+ const pattern = new RegExp(options.codePattern, "i");
899
+ results = results.filter((p2) => pattern.test(p2.code));
900
+ }
901
+ if (options.namePattern) {
902
+ const pattern = new RegExp(options.namePattern, "i");
903
+ results = results.filter((p2) => pattern.test(p2.name));
904
+ }
905
+ if (options.tags && options.tags.length > 0) {
906
+ results = results.filter(
907
+ (p2) => options.tags.some((tag) => p2.tags?.includes(tag))
908
+ );
909
+ }
910
+ if (options.minProgress !== void 0) {
911
+ results = results.filter((p2) => p2.progress >= options.minProgress);
912
+ }
913
+ if (options.maxProgress !== void 0) {
914
+ results = results.filter((p2) => p2.progress <= options.maxProgress);
915
+ }
916
+ if (options.sortBy) {
917
+ const dir = options.sortDir === "desc" ? -1 : 1;
918
+ results.sort((a, b) => {
919
+ const aVal = a[options.sortBy];
920
+ const bVal = b[options.sortBy];
921
+ if (aVal === void 0 || aVal === null) return 1 * dir;
922
+ if (bVal === void 0 || bVal === null) return -1 * dir;
923
+ if (typeof aVal === "string" && typeof bVal === "string") {
924
+ return aVal.localeCompare(bVal) * dir;
925
+ }
926
+ if (typeof aVal === "number" && typeof bVal === "number") {
927
+ return (aVal - bVal) * dir;
928
+ }
929
+ if (aVal instanceof Date && bVal instanceof Date) {
930
+ return (aVal.getTime() - bVal.getTime()) * dir;
931
+ }
932
+ return 0;
933
+ });
934
+ }
935
+ const total = results.length;
936
+ if (options.offset !== void 0) {
937
+ results = results.slice(options.offset);
938
+ }
939
+ if (options.limit !== void 0) {
940
+ results = results.slice(0, options.limit);
941
+ }
942
+ return { plans: results, total };
943
+ }
944
+ function getPlanByCode(registry, code) {
945
+ const paths = registry.byCode.get(code);
946
+ if (!paths || paths.length === 0) return null;
947
+ return registry.plans.get(paths[0]) || null;
948
+ }
949
+ function getPlanByPath(registry, path) {
950
+ return registry.plans.get(resolve(path)) || null;
951
+ }
952
+ function getPlansByStatus(registry, status) {
953
+ const paths = registry.byStatus.get(status) || [];
954
+ return paths.map((p2) => registry.plans.get(p2)).filter((p2) => p2 !== void 0);
955
+ }
956
+ function getRegistryStats(registry) {
957
+ const plans = Array.from(registry.plans.values());
958
+ const byStatus = {
959
+ pending: 0,
960
+ in_progress: 0,
961
+ completed: 0,
962
+ failed: 0,
963
+ blocked: 0,
964
+ skipped: 0
965
+ };
966
+ for (const plan of plans) {
967
+ byStatus[plan.status]++;
968
+ }
969
+ const totalProgress = plans.reduce((sum, p2) => sum + p2.progress, 0);
970
+ const averageProgress = plans.length > 0 ? Math.round(totalProgress / plans.length) : 0;
971
+ const sortedByDate = [...plans].sort(
972
+ (a, b) => a.registeredAt.getTime() - b.registeredAt.getTime()
973
+ );
974
+ return {
975
+ totalPlans: plans.length,
976
+ byStatus,
977
+ averageProgress,
978
+ oldestPlan: sortedByDate[0],
979
+ newestPlan: sortedByDate[sortedByDate.length - 1],
980
+ searchPathCount: registry.searchPaths.length
981
+ };
982
+ }
983
+ function generateRetrospective(plan, options) {
984
+ const steps = plan.steps || [];
985
+ const completedSteps = steps.filter((s2) => s2.status === "completed");
986
+ const skippedSteps = steps.filter((s2) => s2.status === "skipped");
987
+ let duration;
988
+ if (plan.state.startedAt && plan.state.completedAt) {
989
+ const start = plan.state.startedAt instanceof Date ? plan.state.startedAt.getTime() : new Date(plan.state.startedAt).getTime();
990
+ const end = plan.state.completedAt instanceof Date ? plan.state.completedAt.getTime() : new Date(plan.state.completedAt).getTime();
991
+ duration = end - start;
992
+ }
993
+ const whatWentWell = options?.whatWentWell || generateWhatWentWell(plan);
994
+ const whatCouldImprove = options?.whatCouldImprove || generateWhatCouldImprove(plan);
995
+ const keyLearnings = options?.keyLearnings || [];
996
+ const actionItems = options?.actionItems || [];
997
+ return {
998
+ planName: plan.metadata.name,
999
+ planCode: plan.metadata.code,
1000
+ startedAt: plan.state.startedAt,
1001
+ completedAt: plan.state.completedAt,
1002
+ duration,
1003
+ totalSteps: steps.length,
1004
+ completedSteps: completedSteps.length,
1005
+ skippedSteps: skippedSteps.length,
1006
+ whatWentWell,
1007
+ whatCouldImprove,
1008
+ keyLearnings,
1009
+ actionItems,
1010
+ stepsSummary: steps.map((s2) => ({
1011
+ number: s2.number,
1012
+ title: s2.title,
1013
+ status: s2.status,
1014
+ duration: s2.duration,
1015
+ notes: s2.notes
1016
+ }))
1017
+ };
1018
+ }
1019
+ function generateWhatWentWell(plan) {
1020
+ const items = [];
1021
+ const steps = plan.steps || [];
1022
+ const completed = steps.filter((s2) => s2.status === "completed").length;
1023
+ const rate = steps.length > 0 ? completed / steps.length * 100 : 0;
1024
+ if (rate === 100) {
1025
+ items.push("All steps completed successfully");
1026
+ } else if (rate >= 90) {
1027
+ items.push(`High completion rate (${rate.toFixed(0)}%)`);
1028
+ }
1029
+ if (plan.state.blockers.length === 0) {
1030
+ items.push("No blockers encountered");
1031
+ }
1032
+ if (plan.feedback && plan.feedback.length > 0) {
1033
+ items.push(`Successfully incorporated ${plan.feedback.length} feedback item(s)`);
1034
+ }
1035
+ return items;
1036
+ }
1037
+ function generateWhatCouldImprove(plan) {
1038
+ const items = [];
1039
+ const steps = plan.steps || [];
1040
+ const skipped = steps.filter((s2) => s2.status === "skipped");
1041
+ if (skipped.length > 0) {
1042
+ items.push(`${skipped.length} step(s) were skipped - review if necessary`);
1043
+ }
1044
+ if (plan.state.blockers.length > 0) {
1045
+ items.push(
1046
+ `Address recurring blockers: ${plan.state.blockers.map((b) => b.description).join(", ")}`
1047
+ );
1048
+ }
1049
+ if (plan.state.issues.length > 0) {
1050
+ items.push(`Resolve outstanding issues before next iteration`);
1051
+ }
1052
+ return items;
1053
+ }
1054
+ function generateRetrospectiveMarkdown(retro) {
1055
+ const lines = [];
1056
+ lines.push(`# Retrospective: ${retro.planName}
1057
+ `);
1058
+ lines.push(`**Plan Code:** ${retro.planCode}
1059
+ `);
1060
+ if (retro.startedAt) {
1061
+ lines.push(`**Started:** ${formatDate(retro.startedAt)}`);
1062
+ }
1063
+ if (retro.completedAt) {
1064
+ lines.push(`**Completed:** ${formatDate(retro.completedAt)}`);
1065
+ }
1066
+ if (retro.duration) {
1067
+ lines.push(`**Duration:** ${formatDuration(retro.duration)}`);
1068
+ }
1069
+ lines.push("");
1070
+ lines.push("## Summary\n");
1071
+ lines.push(`| Metric | Value |`);
1072
+ lines.push(`|--------|-------|`);
1073
+ lines.push(`| Total Steps | ${retro.totalSteps} |`);
1074
+ lines.push(`| Completed | ${retro.completedSteps} |`);
1075
+ lines.push(`| Skipped | ${retro.skippedSteps} |`);
1076
+ lines.push(
1077
+ `| Completion Rate | ${(retro.completedSteps / retro.totalSteps * 100).toFixed(0)}% |`
1078
+ );
1079
+ lines.push("");
1080
+ lines.push("## What Went Well\n");
1081
+ if (retro.whatWentWell.length > 0) {
1082
+ for (const item of retro.whatWentWell) {
1083
+ lines.push(`- ✅ ${item}`);
1084
+ }
1085
+ } else {
1086
+ lines.push("*No entries yet. Add your insights here.*");
1087
+ }
1088
+ lines.push("");
1089
+ lines.push("## What Could Improve\n");
1090
+ if (retro.whatCouldImprove.length > 0) {
1091
+ for (const item of retro.whatCouldImprove) {
1092
+ lines.push(`- 🔄 ${item}`);
1093
+ }
1094
+ } else {
1095
+ lines.push("*No entries yet. Add your insights here.*");
1096
+ }
1097
+ lines.push("");
1098
+ lines.push("## Key Learnings\n");
1099
+ if (retro.keyLearnings.length > 0) {
1100
+ for (const item of retro.keyLearnings) {
1101
+ lines.push(`- 💡 ${item}`);
1102
+ }
1103
+ } else {
1104
+ lines.push("*What did you learn? Document it here.*");
1105
+ }
1106
+ lines.push("");
1107
+ lines.push("## Action Items\n");
1108
+ if (retro.actionItems.length > 0) {
1109
+ for (const item of retro.actionItems) {
1110
+ lines.push(`- [ ] ${item}`);
1111
+ }
1112
+ } else {
1113
+ lines.push("*Any follow-up tasks? Add them here.*");
1114
+ }
1115
+ lines.push("");
1116
+ lines.push("## Steps Summary\n");
1117
+ lines.push(`| # | Title | Status | Notes |`);
1118
+ lines.push(`|---|-------|--------|-------|`);
1119
+ for (const step of retro.stepsSummary) {
1120
+ const status = formatStepStatus(step.status);
1121
+ const notes = step.notes || "-";
1122
+ lines.push(`| ${step.number} | ${step.title} | ${status} | ${notes} |`);
1123
+ }
1124
+ lines.push("");
1125
+ lines.push("---");
1126
+ lines.push(`*Generated on ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}*`);
1127
+ return lines.join("\n");
1128
+ }
1129
+ async function createRetrospective(plan, options) {
1130
+ const retro = generateRetrospective(plan, options);
1131
+ const content = generateRetrospectiveMarkdown(retro);
1132
+ const retroPath = join(plan.metadata.path, "RETROSPECTIVE.md");
1133
+ await writeFile(retroPath, content, "utf-8");
1134
+ return retroPath;
1135
+ }
1136
+ function formatDate(date) {
1137
+ if (typeof date === "string") return date;
1138
+ return date.toISOString().split("T")[0];
1139
+ }
1140
+ function formatDuration(ms) {
1141
+ const hours = Math.floor(ms / (1e3 * 60 * 60));
1142
+ const minutes = Math.floor(ms % (1e3 * 60 * 60) / (1e3 * 60));
1143
+ const days = Math.floor(hours / 24);
1144
+ if (days > 0) {
1145
+ return `${days} day(s), ${hours % 24} hour(s)`;
1146
+ }
1147
+ if (hours > 0) {
1148
+ return `${hours} hour(s), ${minutes} minute(s)`;
1149
+ }
1150
+ return `${minutes} minute(s)`;
1151
+ }
1152
+ function formatStepStatus(status) {
1153
+ const emojis = {
1154
+ completed: "✅",
1155
+ skipped: "⏭️",
1156
+ pending: "⬜",
1157
+ in_progress: "🔄",
1158
+ blocked: "⏸️"
1159
+ };
1160
+ return `${emojis[status] || ""} ${status}`;
1161
+ }
1162
+ const HISTORY_FILE = ".history/HISTORY.json";
1163
+ function initHistory(initialVersion = "0.1") {
1164
+ return {
1165
+ revisions: [
1166
+ {
1167
+ version: initialVersion,
1168
+ createdAt: /* @__PURE__ */ new Date(),
1169
+ message: "Initial version"
1170
+ }
1171
+ ],
1172
+ currentVersion: initialVersion,
1173
+ milestones: []
1174
+ };
1175
+ }
1176
+ async function loadHistory(planPath) {
1177
+ const historyPath = join(planPath, HISTORY_FILE);
1178
+ let history;
1179
+ try {
1180
+ const content = await readFile(historyPath, "utf-8");
1181
+ const data = JSON.parse(content);
1182
+ history = {
1183
+ ...data,
1184
+ revisions: data.revisions.map((r) => ({
1185
+ ...r,
1186
+ createdAt: new Date(r.createdAt)
1187
+ })),
1188
+ milestones: data.milestones?.map((m2) => ({
1189
+ ...m2,
1190
+ createdAt: new Date(m2.createdAt)
1191
+ }))
1192
+ };
1193
+ } catch {
1194
+ history = initHistory();
1195
+ }
1196
+ return createHistoryManager(history, historyPath);
1197
+ }
1198
+ async function saveHistory(history, planPath) {
1199
+ const historyPath = join(planPath, HISTORY_FILE);
1200
+ await mkdir(dirname(historyPath), { recursive: true });
1201
+ const data = {
1202
+ ...history,
1203
+ revisions: history.revisions.map((r) => ({
1204
+ ...r,
1205
+ createdAt: r.createdAt instanceof Date ? r.createdAt.toISOString() : r.createdAt
1206
+ })),
1207
+ milestones: history.milestones?.map((m2) => ({
1208
+ ...m2,
1209
+ createdAt: m2.createdAt instanceof Date ? m2.createdAt.toISOString() : m2.createdAt
1210
+ }))
1211
+ };
1212
+ await writeFile(historyPath, JSON.stringify(data, null, 2), "utf-8");
1213
+ }
1214
+ function createHistoryManager(history, path) {
1215
+ return {
1216
+ history,
1217
+ path,
1218
+ async save() {
1219
+ const planPath = dirname(dirname(path));
1220
+ await saveHistory(history, planPath);
1221
+ },
1222
+ async reload() {
1223
+ const planPath = dirname(dirname(path));
1224
+ const reloaded = await loadHistory(planPath);
1225
+ Object.assign(history, reloaded.history);
1226
+ }
1227
+ };
1228
+ }
1229
+ function createRevision(history, message, options) {
1230
+ const currentVersion = history.currentVersion;
1231
+ const [major, minor] = currentVersion.split(".").map(Number);
1232
+ const newVersion = `${major}.${minor + 1}`;
1233
+ const revision = {
1234
+ version: newVersion,
1235
+ createdAt: /* @__PURE__ */ new Date(),
1236
+ message,
1237
+ author: options?.author,
1238
+ feedbackId: options?.feedbackId
1239
+ };
1240
+ history.revisions.push(revision);
1241
+ history.currentVersion = newVersion;
1242
+ return revision;
1243
+ }
1244
+ function getRevision(history, version) {
1245
+ const index = history.revisions.findIndex((r) => r.version === version);
1246
+ if (index === -1) return void 0;
1247
+ const revision = history.revisions[index];
1248
+ return {
1249
+ ...revision,
1250
+ index,
1251
+ isCurrent: revision.version === history.currentVersion
1252
+ };
1253
+ }
1254
+ function listRevisions(history) {
1255
+ return history.revisions.map((r, index) => ({
1256
+ ...r,
1257
+ index,
1258
+ isCurrent: r.version === history.currentVersion
1259
+ }));
1260
+ }
1261
+ function compareRevisions(history, fromVersion, toVersion) {
1262
+ const from = getRevision(history, fromVersion);
1263
+ const to = getRevision(history, toVersion);
1264
+ if (!from || !to) return void 0;
1265
+ const fromTime = from.createdAt instanceof Date ? from.createdAt.getTime() : new Date(from.createdAt).getTime();
1266
+ const toTime = to.createdAt instanceof Date ? to.createdAt.getTime() : new Date(to.createdAt).getTime();
1267
+ return {
1268
+ from,
1269
+ to,
1270
+ timeDiff: toTime - fromTime,
1271
+ revisionCount: Math.abs(to.index - from.index)
1272
+ };
1273
+ }
1274
+ function getLatestRevision(history) {
1275
+ if (history.revisions.length === 0) return void 0;
1276
+ const index = history.revisions.length - 1;
1277
+ const revision = history.revisions[index];
1278
+ return {
1279
+ ...revision,
1280
+ index,
1281
+ isCurrent: true
1282
+ };
1283
+ }
1284
+ function nextVersion(currentVersion) {
1285
+ const [major, minor] = currentVersion.split(".").map(Number);
1286
+ return `${major}.${minor + 1}`;
1287
+ }
1288
+ function createMilestone(history, name, description) {
1289
+ const milestone = {
1290
+ name,
1291
+ version: history.currentVersion,
1292
+ createdAt: /* @__PURE__ */ new Date(),
1293
+ description
1294
+ };
1295
+ if (!history.milestones) {
1296
+ history.milestones = [];
1297
+ }
1298
+ history.milestones.push(milestone);
1299
+ return milestone;
1300
+ }
1301
+ function getMilestone(history, name) {
1302
+ if (!history.milestones) return void 0;
1303
+ const index = history.milestones.findIndex((m2) => m2.name === name);
1304
+ if (index === -1) return void 0;
1305
+ return {
1306
+ ...history.milestones[index],
1307
+ index
1308
+ };
1309
+ }
1310
+ function listMilestones(history) {
1311
+ if (!history.milestones) return [];
1312
+ return history.milestones.map((m2, index) => ({
1313
+ ...m2,
1314
+ index
1315
+ }));
1316
+ }
1317
+ function rollbackToMilestone(history, milestoneName) {
1318
+ const milestone = getMilestone(history, milestoneName);
1319
+ if (!milestone) {
1320
+ return {
1321
+ success: false,
1322
+ error: `Milestone not found: ${milestoneName}`
1323
+ };
1324
+ }
1325
+ const currentIndex = history.revisions.findIndex(
1326
+ (r) => r.version === history.currentVersion
1327
+ );
1328
+ const targetIndex = history.revisions.findIndex(
1329
+ (r) => r.version === milestone.version
1330
+ );
1331
+ if (targetIndex === -1) {
1332
+ return {
1333
+ success: false,
1334
+ error: `Revision not found for milestone: ${milestone.version}`
1335
+ };
1336
+ }
1337
+ const revisionsRolledBack = currentIndex - targetIndex;
1338
+ history.currentVersion = milestone.version;
1339
+ return {
1340
+ success: true,
1341
+ milestone,
1342
+ newVersion: milestone.version,
1343
+ revisionsRolledBack
1344
+ };
1345
+ }
1346
+ function getLatestMilestone(history) {
1347
+ if (!history.milestones || history.milestones.length === 0) {
1348
+ return void 0;
1349
+ }
1350
+ const index = history.milestones.length - 1;
1351
+ return {
1352
+ ...history.milestones[index],
1353
+ index
1354
+ };
1355
+ }
1356
+ function renderPlan(plan, options) {
1357
+ try {
1358
+ let content;
1359
+ switch (options.format) {
1360
+ case "markdown":
1361
+ content = renderToMarkdown(plan, options);
1362
+ break;
1363
+ case "json":
1364
+ content = renderToJson(plan, options);
1365
+ break;
1366
+ case "html":
1367
+ content = renderToHtml(plan, options);
1368
+ break;
1369
+ default:
1370
+ return {
1371
+ success: false,
1372
+ error: `Unknown format: ${options.format}`
1373
+ };
1374
+ }
1375
+ return {
1376
+ success: true,
1377
+ content,
1378
+ format: options.format
1379
+ };
1380
+ } catch (error) {
1381
+ return {
1382
+ success: false,
1383
+ error: error instanceof Error ? error.message : "Unknown render error"
1384
+ };
1385
+ }
1386
+ }
1387
+ class MockStepExecutor {
1388
+ delay;
1389
+ shouldFail;
1390
+ constructor(options) {
1391
+ this.delay = options?.delay ?? 100;
1392
+ this.shouldFail = options?.shouldFail ?? false;
1393
+ }
1394
+ async execute(context) {
1395
+ await new Promise((resolve2) => setTimeout(resolve2, this.delay));
1396
+ context.onProgress?.(`Executing step ${context.step.number}...`);
1397
+ if (this.shouldFail) {
1398
+ return {
1399
+ success: false,
1400
+ step: context.step.number,
1401
+ error: new Error("Mock execution failed"),
1402
+ duration: 0
1403
+ };
1404
+ }
1405
+ return {
1406
+ success: true,
1407
+ step: context.step.number,
1408
+ output: `Completed step ${context.step.number}: ${context.step.title}`,
1409
+ duration: 0
1410
+ };
1411
+ }
1412
+ }
1413
+ function createExecutor(config) {
1414
+ switch (config.type) {
1415
+ case "mock":
1416
+ return new MockStepExecutor();
1417
+ case "anthropic":
1418
+ case "openai":
1419
+ case "gemini":
1420
+ throw new Error(
1421
+ `Provider '${config.type}' requires @riotprompt/execution-${config.type} package. Import and configure it directly for LLM-powered execution.`
1422
+ );
1423
+ default:
1424
+ throw new Error(`Unknown provider: ${config.type}`);
1425
+ }
1426
+ }
1427
+ async function executeStep$1(executor, plan, stepNumber, options) {
1428
+ const step = plan.steps?.find((s2) => s2.number === stepNumber);
1429
+ if (!step) {
1430
+ return {
1431
+ success: false,
1432
+ step: stepNumber,
1433
+ error: new Error(`Step ${stepNumber} not found`),
1434
+ duration: 0
1435
+ };
1436
+ }
1437
+ const context = {
1438
+ plan,
1439
+ step,
1440
+ provider: options?.provider ?? { type: "mock" },
1441
+ workingDirectory: options?.workingDirectory,
1442
+ env: options?.env,
1443
+ onProgress: options?.onProgress,
1444
+ onComplete: options?.onComplete
1445
+ };
1446
+ const result = await executor.execute(context);
1447
+ options?.onComplete?.(result);
1448
+ return result;
1449
+ }
1450
+ async function executePendingSteps(executor, plan, options) {
1451
+ const results = [];
1452
+ const pendingSteps = plan.steps?.filter((s2) => s2.status === "pending") ?? [];
1453
+ for (const step of pendingSteps) {
1454
+ const result = await executeStep$1(executor, plan, step.number, {
1455
+ provider: options?.provider,
1456
+ onProgress: (msg) => options?.onProgress?.(step.number, msg),
1457
+ onComplete: (res) => options?.onStepComplete?.(step.number, res)
1458
+ });
1459
+ results.push(result);
1460
+ if (!result.success && options?.stopOnError) {
1461
+ break;
1462
+ }
1463
+ }
1464
+ return results;
1465
+ }
1466
+ function outputSuccess(message) {
1467
+ console.log(chalk.green("✓") + " " + message);
1468
+ }
1469
+ function outputError(message) {
1470
+ console.error(chalk.red("✗") + " " + message);
1471
+ }
1472
+ function outputWarning(message) {
1473
+ console.log(chalk.yellow("⚠") + " " + message);
1474
+ }
1475
+ function outputInfo(message) {
1476
+ console.log(chalk.blue("ℹ") + " " + message);
1477
+ }
1478
+ function getStatusIcon(status) {
1479
+ const icons = {
27
1480
  pending: "⬜",
28
1481
  in_progress: "🔄",
29
1482
  completed: "✅",
30
1483
  failed: "❌",
31
1484
  blocked: "⏸️",
32
1485
  skipped: "⏭️"
1486
+ };
1487
+ return icons[status] || "⬜";
1488
+ }
1489
+ function formatStatus(status) {
1490
+ const colors = {
1491
+ pending: chalk.dim,
1492
+ in_progress: chalk.yellow,
1493
+ completed: chalk.green,
1494
+ failed: chalk.red,
1495
+ blocked: chalk.magenta,
1496
+ skipped: chalk.gray
1497
+ };
1498
+ const color = colors[status] || chalk.white;
1499
+ return color(status.replace("_", " "));
1500
+ }
1501
+ function outputStepList(steps) {
1502
+ for (const step of steps) {
1503
+ const num = String(step.number).padStart(2, "0");
1504
+ const icon = getStatusIcon(step.status);
1505
+ const status = formatStatus(step.status);
1506
+ console.log(
1507
+ ` ${icon} ${chalk.bold(num)} ${step.title} ${chalk.dim(`[${status}]`)}`
1508
+ );
33
1509
  }
34
- };
35
- const VERSION = "0.0.1";
36
- async function loadPlan(_path) {
37
- throw new Error(
38
- "riotplan.loadPlan is not yet implemented. Coming in v0.1.0!"
39
- );
40
1510
  }
41
- async function createPlan(_config) {
42
- throw new Error(
43
- "riotplan.createPlan is not yet implemented. Coming in v0.1.0!"
1511
+ function outputPlanSummary(plan) {
1512
+ const { metadata, state, steps } = plan;
1513
+ const completedCount = steps.filter((s2) => s2.status === "completed").length;
1514
+ console.log();
1515
+ console.log(chalk.bold(metadata.name));
1516
+ console.log(chalk.dim(`Code: ${metadata.code}`));
1517
+ console.log();
1518
+ console.log(
1519
+ `Status: ${getStatusIcon(state.status)} ${formatStatus(state.status)}`
44
1520
  );
45
- }
46
- function parseStatus(_content) {
47
- throw new Error(
48
- "riotplan.parseStatus is not yet implemented. Coming in v0.1.0!"
1521
+ console.log(
1522
+ `Progress: ${state.progress}% (${completedCount}/${steps.length} steps)`
49
1523
  );
1524
+ if (state.currentStep) {
1525
+ const currentStep = steps.find((s2) => s2.number === state.currentStep);
1526
+ if (currentStep) {
1527
+ console.log(
1528
+ `Current: Step ${state.currentStep} - ${currentStep.title}`
1529
+ );
1530
+ }
1531
+ }
1532
+ if (state.blockers.length > 0) {
1533
+ console.log();
1534
+ console.log(chalk.yellow(`Blockers: ${state.blockers.length}`));
1535
+ for (const blocker of state.blockers) {
1536
+ console.log(chalk.dim(` - ${blocker.description}`));
1537
+ }
1538
+ }
50
1539
  }
51
- function generateStatus(_plan) {
52
- throw new Error(
53
- "riotplan.generateStatus is not yet implemented. Coming in v0.1.0!"
54
- );
1540
+ function outputJson(data) {
1541
+ console.log(JSON.stringify(data, null, 2));
1542
+ }
1543
+ class CliError extends Error {
1544
+ constructor(message, code, exitCode = 1) {
1545
+ super(message);
1546
+ this.code = code;
1547
+ this.exitCode = exitCode;
1548
+ this.name = "CliError";
1549
+ }
1550
+ }
1551
+ function handleError(error) {
1552
+ if (error instanceof CliError) {
1553
+ console.error(chalk.red(`Error [${error.code}]: ${error.message}`));
1554
+ process.exit(error.exitCode);
1555
+ }
1556
+ if (error instanceof Error) {
1557
+ console.error(chalk.red(`Error: ${error.message}`));
1558
+ if (process.env.DEBUG) {
1559
+ console.error(error.stack);
1560
+ }
1561
+ process.exit(1);
1562
+ }
1563
+ console.error(chalk.red("An unknown error occurred"));
1564
+ process.exit(1);
1565
+ }
1566
+ function notImplemented(feature) {
1567
+ console.log(chalk.yellow(`⚠ ${feature} is not yet implemented.`));
1568
+ console.log(chalk.dim("Check back in a future version!"));
1569
+ process.exit(0);
55
1570
  }
1571
+ const VERSION = "0.0.1";
56
1572
  async function executeStep(_plan, _stepNumber, _context) {
57
1573
  throw new Error(
58
1574
  "riotplan.executeStep is not yet implemented. Coming in v0.1.0!"
@@ -69,14 +1585,144 @@ function updatePlanState(_plan, _stepNumber, _result) {
69
1585
  );
70
1586
  }
71
1587
  export {
1588
+ B as BasicTemplate,
1589
+ C as CRITERIA_PATTERNS,
1590
+ CliError,
1591
+ F as FeatureTemplate,
1592
+ H as HEALTH_THRESHOLDS,
1593
+ M as MigrationTemplate,
1594
+ MockStepExecutor,
72
1595
  PLAN_CONVENTIONS,
1596
+ c as PRIORITY_WEIGHTS,
1597
+ R as RefactoringTemplate,
1598
+ S as SprintTemplate,
73
1599
  VERSION,
74
- createPlan,
1600
+ addRelationship,
1601
+ d as applyTemplate,
1602
+ e as archiveCommand,
1603
+ f as blockStep,
1604
+ g as buildDependencyGraph,
1605
+ h as buildDependencyGraphFromMap,
1606
+ i as checkCompletion,
1607
+ j as checkCoverage,
1608
+ compareRevisions,
1609
+ k as completeStep,
1610
+ m as computeExecutionOrder,
1611
+ n as createAnalysisDirectory,
1612
+ createBidirectionalRelationship,
1613
+ createExecutor,
1614
+ o as createFeedback,
1615
+ createMilestone,
1616
+ p as createPlan,
1617
+ q as createProgram,
1618
+ createRegistry,
1619
+ createRetrospective,
1620
+ createRevision,
1621
+ executePendingSteps,
75
1622
  executeStep,
76
- generateStatus,
1623
+ executeStep$1 as executeStepWithExecutor,
1624
+ s as failStep,
1625
+ t as findCriticalPath,
1626
+ formatStatus,
1627
+ generateRelationshipsMarkdown,
1628
+ generateRetrospective,
1629
+ generateRetrospectiveMarkdown,
1630
+ u as generateStatus,
1631
+ getBlockedPlans,
1632
+ v as getBlockedSteps,
1633
+ getBlockingPlans,
1634
+ getChildPlans,
1635
+ w as getCriteriaSummary,
1636
+ getDefaultRegistryPath,
1637
+ x as getDependencyChain,
1638
+ y as getFeedback,
1639
+ getInverseRelationType,
1640
+ getLatestMilestone,
1641
+ getLatestRevision,
1642
+ getMilestone,
1643
+ getParentPlan,
1644
+ getPlanByCode,
1645
+ getPlanByPath,
1646
+ getPlansByStatus,
1647
+ z as getReadySteps,
1648
+ getRegistryStats,
1649
+ getRelatedPlans,
1650
+ getRelationshipsByType,
1651
+ getRevision,
1652
+ getStatusIcon,
1653
+ A as getTemplate,
1654
+ handleError,
1655
+ D as hasAnalysis,
1656
+ E as initCommand,
1657
+ initHistory,
1658
+ G as insertStep,
1659
+ I as listFeedback,
1660
+ listMilestones,
1661
+ listRevisions,
1662
+ J as listTemplates,
1663
+ K as listTemplatesByCategory,
1664
+ L as loadAmendmentPrompts,
1665
+ N as loadAnalysis,
1666
+ O as loadElaborationPrompts,
1667
+ loadHistory,
77
1668
  loadPlan,
1669
+ loadRegistry,
1670
+ Q as moveStep,
1671
+ nextVersion,
1672
+ notImplemented,
1673
+ outputError,
1674
+ outputInfo,
1675
+ outputJson,
1676
+ outputPlanSummary,
1677
+ outputStepList,
1678
+ outputSuccess,
1679
+ outputWarning,
1680
+ T as parseAllDependencies,
1681
+ U as parseCriteria,
1682
+ V as parseCriteriaFromContent,
1683
+ W as parseDependenciesFromContent,
1684
+ X as parseDependenciesFromFile,
1685
+ parseRelationshipsFromContent,
1686
+ parseRelationshipsFromPlan,
78
1687
  parseStatus,
1688
+ refreshAllPlans,
1689
+ refreshPlan,
1690
+ registerPlan,
1691
+ Y as registerPlanCommands,
1692
+ Z as registerRenderCommands,
1693
+ _ as registerTemplate,
1694
+ removeRelationship,
1695
+ $ as removeStep,
1696
+ a0 as renderCommand,
1697
+ renderPlan,
1698
+ renderToHtml,
1699
+ renderToJson,
1700
+ renderToMarkdown,
79
1701
  resumePlan,
80
- updatePlanState
1702
+ rollbackToMilestone,
1703
+ a1 as saveAmendmentPrompt,
1704
+ a2 as saveElaborationPrompt,
1705
+ saveHistory,
1706
+ a3 as saveInitialPrompt,
1707
+ saveRegistry,
1708
+ scanForPlans,
1709
+ searchPlans,
1710
+ a4 as searchTemplatesByTag,
1711
+ a5 as skipStep,
1712
+ a6 as startStep,
1713
+ a7 as templateCommand,
1714
+ a8 as templateListCommand,
1715
+ a9 as templateShowCommand,
1716
+ aa as templateUseCommand,
1717
+ ab as unblockStep,
1718
+ unregisterPlan,
1719
+ updatePlanRelationships,
1720
+ updatePlanState,
1721
+ ac as updateStatus,
1722
+ ad as updateStepDependencies,
1723
+ ae as validateCommand,
1724
+ af as validateDependencies,
1725
+ ag as validatePlan,
1726
+ validateRelationships
81
1727
  };
82
1728
  //# sourceMappingURL=index.js.map