guardian-framework 0.1.7 → 0.1.8

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/cli.js CHANGED
@@ -1335,7 +1335,7 @@ __export(exports_package, {
1335
1335
  bin: () => bin,
1336
1336
  author: () => author
1337
1337
  });
1338
- var name = "guardian-framework", version = "0.1.7", description = "Token-optimized agentic framework scaffolder with pi-first architecture", type = "module", main = "dist/exports.js", exports, types = "dist/exports.d.ts", bin, files, engines, scripts, publishConfig, pi, repository, homepage = "https://github.com/arman-jalili/guardian-framework#readme", bugs, dependencies, devDependencies, keywords, author = "Arman Wolkensteiner-Jalili", license = "MIT", package_default;
1338
+ var name = "guardian-framework", version = "0.1.8", description = "Token-optimized agentic framework scaffolder with pi-first architecture", type = "module", main = "dist/exports.js", exports, types = "dist/exports.d.ts", bin, files, engines, scripts, publishConfig, pi, repository, homepage = "https://github.com/arman-jalili/guardian-framework#readme", bugs, dependencies, devDependencies, keywords, author = "Arman Wolkensteiner-Jalili", license = "MIT", package_default;
1339
1339
  var init_package = __esm(() => {
1340
1340
  exports = {
1341
1341
  ".": {
package/dist/exports.js CHANGED
@@ -995,7 +995,7 @@ __export(exports_package, {
995
995
  bin: () => bin,
996
996
  author: () => author
997
997
  });
998
- var name = "guardian-framework", version = "0.1.7", description = "Token-optimized agentic framework scaffolder with pi-first architecture", type = "module", main = "dist/exports.js", exports, types = "dist/exports.d.ts", bin, files, engines, scripts, publishConfig, pi, repository, homepage = "https://github.com/arman-jalili/guardian-framework#readme", bugs, dependencies, devDependencies, keywords, author = "Arman Wolkensteiner-Jalili", license = "MIT", package_default;
998
+ var name = "guardian-framework", version = "0.1.8", description = "Token-optimized agentic framework scaffolder with pi-first architecture", type = "module", main = "dist/exports.js", exports, types = "dist/exports.d.ts", bin, files, engines, scripts, publishConfig, pi, repository, homepage = "https://github.com/arman-jalili/guardian-framework#readme", bugs, dependencies, devDependencies, keywords, author = "Arman Wolkensteiner-Jalili", license = "MIT", package_default;
999
999
  var init_package = __esm(() => {
1000
1000
  exports = {
1001
1001
  ".": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "guardian-framework",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "Token-optimized agentic framework scaffolder with pi-first architecture",
5
5
  "type": "module",
6
6
  "main": "dist/exports.js",
@@ -0,0 +1,99 @@
1
+ # Implementation Roadmap
2
+
3
+ <!--
4
+ @canonical .pi/architecture/implementation-roadmap.md
5
+ Status: Proposed
6
+ Date: [Date]
7
+ -->
8
+
9
+ ## Overview
10
+
11
+ This roadmap defines the phased implementation of the project, organizing bounded
12
+ contexts into delivery phases with explicit dependencies, database migrations, and
13
+ acceptance criteria.
14
+
15
+ **Format conventions:**
16
+ - Each `## Phase N` section defines one delivery phase
17
+ - `### Modules` table lists the bounded contexts implemented in this phase
18
+ - `### Dependencies` lists phase prerequisites
19
+ - `### Database Migrations` lists SQL migration files
20
+ - `### Acceptance Criteria` lists verifiable checkboxes
21
+
22
+ ---
23
+
24
+ ## Phase 0: [Phase Name] (Days [Start]–[End])
25
+
26
+ **Goal:** [One-sentence goal for this phase]
27
+
28
+ ### Modules
29
+
30
+ | Module | Deliverables | Architecture Doc |
31
+ |--------|-------------|------------------|
32
+ | [Module Name] | [Key deliverables] | `.pi/architecture/modules/[module].md` |
33
+ | [Module Name] | [Key deliverables] | `.pi/architecture/modules/[module].md` |
34
+
35
+ ### Dependencies
36
+
37
+ - [Phase name or "None"]
38
+
39
+ ### Database Migrations
40
+
41
+ - `NNN_[migration_name]`: [Description of what it creates]
42
+
43
+ ### Acceptance Criteria
44
+
45
+ - [ ] [Verifiable criterion]
46
+ - [ ] [Verifiable criterion]
47
+
48
+ ---
49
+
50
+ ## Phase 1: [Phase Name] (Days [Start]–[End])
51
+
52
+ **Goal:** [One-sentence goal for this phase]
53
+
54
+ ### Modules
55
+
56
+ | Module | Deliverables | Architecture Doc |
57
+ |--------|-------------|------------------|
58
+ | [Module Name] | [Key deliverables] | `.pi/architecture/modules/[module].md` |
59
+
60
+ ### Dependencies
61
+
62
+ - Phase 0
63
+
64
+ ### Database Migrations
65
+
66
+ - `NNN_[migration_name]`: [Description]
67
+
68
+ ### Acceptance Criteria
69
+
70
+ - [ ] [Verifiable criterion]
71
+
72
+ ---
73
+
74
+ ## Dependency Graph
75
+
76
+ ```
77
+ Phase 0 ([Name])
78
+
79
+
80
+ Phase 1 ([Name])
81
+
82
+
83
+ Phase 2 ([Name])
84
+ ```
85
+
86
+ ## Effort Estimates
87
+
88
+ | Phase | Modules | Estimated Days | Risk | Dependencies |
89
+ |-------|---------|---------------|------|-------------|
90
+ | P0: [Name] | [N] | [N] | [Low/Medium/High] | None |
91
+ | P1: [Name] | [N] | [N] | [Low/Medium/High] | P0 |
92
+ | P2: [Name] | [N] | [N] | [Low/Medium/High] | P1 |
93
+
94
+ ## Key Milestones
95
+
96
+ | Milestone | Target | What's True |
97
+ |-----------|--------|-------------|
98
+ | **M0: [Name]** | Day [N] | [What's working] |
99
+ | **M1: [Name]** | Day [N] | [What's working] |
@@ -6,12 +6,17 @@
6
6
  *
7
7
  * COMMANDS
8
8
  * /architect --epic "Name" [--tracking-issue N] Start new epic
9
+ * /architect --roadmap Show roadmap status
10
+ * /architect --phase "Phase 1" Start a roadmap phase
11
+ * /architect --phase-status Show current phase
12
+ * /architect --phase-done <N> Mark phase complete
9
13
  * /architect status Show current state
10
14
  * /architect next-epic Show next planned slice
11
15
  * /architect abort Cancel current epic
12
16
  *
13
17
  * TOOLS
14
18
  * architect_status - Current epic state and progress
19
+ * architect_roadmap - Show roadmap phases and status
15
20
  * architect_discover - Discover modules and find next logical slice
16
21
  */
17
22
 
@@ -88,10 +93,39 @@ type EpicState = {
88
93
  createdAt: string;
89
94
  };
90
95
 
96
+ // ── Roadmap Types ──
97
+
98
+ type RoadmapPhaseModule = {
99
+ name: string;
100
+ deliverables: string;
101
+ doc: string;
102
+ };
103
+
104
+ type RoadmapPhase = {
105
+ index: number;
106
+ title: string;
107
+ goal: string;
108
+ days: string;
109
+ modules: RoadmapPhaseModule[];
110
+ dependencies: string[];
111
+ migrations: string[];
112
+ criteria: string[];
113
+ status: "pending" | "in_progress" | "done";
114
+ };
115
+
116
+ type RoadmapState = {
117
+ phases: RoadmapPhase[];
118
+ currentPhaseIndex: number;
119
+ startedAt: string;
120
+ updatedAt: string;
121
+ };
122
+
91
123
  // ── Constants ──
92
124
 
93
125
  const ARCH_MODULES_DIR = ".pi/architecture/modules";
94
126
  const ISSUES_DIR = ".pi/issues";
127
+ const ROADMAP_FILE = ".pi/architecture/implementation-roadmap.md";
128
+ const ROADMAP_STATE_FILE = ".pi/.guardian-roadmap-state.json";
95
129
 
96
130
  // ── Helpers ──
97
131
 
@@ -442,6 +476,204 @@ function findNextLogicalSlice(cwd: string, moduleFiles: string[]): ArchitectureS
442
476
  return null;
443
477
  }
444
478
 
479
+ // ── Roadmap Parsing ──
480
+
481
+ function parseRoadmap(cwd: string): RoadmapPhase[] {
482
+ const path = join(cwd, ROADMAP_FILE);
483
+ if (!existsSync(path)) return [];
484
+ const content = readFileSync(path, "utf-8");
485
+ const lines = content.split("\n");
486
+ const phases: RoadmapPhase[] = [];
487
+ let currentPhase: Partial<RoadmapPhase> | null = null;
488
+ let inModules = false;
489
+ let inDeps = false;
490
+ let inMigrations = false;
491
+ let inCriteria = false;
492
+
493
+ for (let i = 0; i < lines.length; i++) {
494
+ const line = lines[i];
495
+ const trimmed = line.trim();
496
+
497
+ // Detect phase heading: ## Phase N: Name (Days X-Y)
498
+ const phaseMatch = trimmed.match(/^## Phase (\d+):\s*(.+?)\s*\((Days\s*[^)]+)\)?$/i);
499
+ if (phaseMatch) {
500
+ if (currentPhase && currentPhase.title) {
501
+ phases.push({
502
+ index: currentPhase.index!,
503
+ title: currentPhase.title!,
504
+ goal: currentPhase.goal || "",
505
+ days: currentPhase.days || "",
506
+ modules: currentPhase.modules || [],
507
+ dependencies: currentPhase.dependencies || [],
508
+ migrations: currentPhase.migrations || [],
509
+ criteria: currentPhase.criteria || [],
510
+ status: "pending",
511
+ });
512
+ }
513
+ currentPhase = {
514
+ index: parseInt(phaseMatch[1], 10),
515
+ title: phaseMatch[2].trim(),
516
+ days: phaseMatch[3].trim(),
517
+ modules: [],
518
+ dependencies: [],
519
+ migrations: [],
520
+ criteria: [],
521
+ };
522
+ inModules = false; inDeps = false; inMigrations = false; inCriteria = false;
523
+ continue;
524
+ }
525
+
526
+ if (!currentPhase) continue;
527
+
528
+ // Goal line
529
+ const goalMatch = trimmed.match(/^\*\*Goal:\*\*\s*(.+)/i);
530
+ if (goalMatch) {
531
+ currentPhase.goal = goalMatch[1].trim();
532
+ continue;
533
+ }
534
+
535
+ // Section headers
536
+ if (trimmed.match(/^###\s+Modules?/i)) {
537
+ inModules = true; inDeps = false; inMigrations = false; inCriteria = false; continue;
538
+ }
539
+ if (trimmed.match(/^###\s+Dependencies?/i)) {
540
+ inModules = false; inDeps = true; inMigrations = false; inCriteria = false; continue;
541
+ }
542
+ if (trimmed.match(/^###\s+Database\s+Migrations?/i)) {
543
+ inModules = false; inDeps = false; inMigrations = true; inCriteria = false; continue;
544
+ }
545
+ if (trimmed.match(/^###\s+Acceptance\s+Criteria/i)) {
546
+ inModules = false; inDeps = false; inMigrations = false; inCriteria = true; continue;
547
+ }
548
+
549
+ // Exit section on next ### heading
550
+ if (trimmed.startsWith("###") && !trimmed.match(/^###\s+(Modules?|Dependencies?|Database\s+Migrations?|Acceptance\s+Criteria)/i)) {
551
+ inModules = false; inDeps = false; inMigrations = false; inCriteria = false;
552
+ }
553
+
554
+ // Parse module table rows: | Module | Deliverables | Doc |
555
+ if (inModules && trimmed.startsWith("|") && !trimmed.startsWith("|---") && !trimmed.startsWith("| Module")) {
556
+ const parts = trimmed.split("|").map((p: string) => p.trim()).filter(Boolean);
557
+ if (parts.length >= 2) {
558
+ currentPhase.modules!.push({
559
+ name: parts[0],
560
+ deliverables: parts[1] || "",
561
+ doc: parts[2] || `.pi/architecture/modules/${parts[0].toLowerCase().replace(/[^a-z0-9]+/g, "-")}.md`,
562
+ });
563
+ }
564
+ }
565
+
566
+ // Parse dependencies: list items
567
+ if (inDeps && trimmed.startsWith("-")) {
568
+ currentPhase.dependencies!.push(trimmed.replace(/^[-\s]+/, "").trim());
569
+ }
570
+
571
+ // Parse migrations: - NNN_name: description
572
+ if (inMigrations && trimmed.startsWith("-")) {
573
+ currentPhase.migrations!.push(trimmed.replace(/^[-\s]+/, "").trim());
574
+ }
575
+
576
+ // Parse acceptance criteria: - [ ] item
577
+ if (inCriteria && trimmed.startsWith("- [")) {
578
+ currentPhase.criteria!.push(trimmed.replace(/^\[-\s*\]\s*/, "").trim());
579
+ }
580
+ }
581
+
582
+ // Push last phase
583
+ if (currentPhase && currentPhase.title) {
584
+ phases.push({
585
+ index: currentPhase.index!,
586
+ title: currentPhase.title!,
587
+ goal: currentPhase.goal || "",
588
+ days: currentPhase.days || "",
589
+ modules: currentPhase.modules || [],
590
+ dependencies: currentPhase.dependencies || [],
591
+ migrations: currentPhase.migrations || [],
592
+ criteria: currentPhase.criteria || [],
593
+ status: "pending",
594
+ });
595
+ }
596
+
597
+ // Restore status from saved state
598
+ const saved = loadRoadmapState(cwd);
599
+ if (saved) {
600
+ for (const phase of phases) {
601
+ const savedPhase = saved.phases.find((p: RoadmapPhase) => p.index === phase.index);
602
+ if (savedPhase) phase.status = savedPhase.status;
603
+ }
604
+ }
605
+
606
+ return phases;
607
+ }
608
+
609
+ function saveRoadmapState(cwd: string, state: RoadmapState): void {
610
+ const dir = dirname(join(cwd, ROADMAP_STATE_FILE));
611
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
612
+ writeFileSync(join(cwd, ROADMAP_STATE_FILE), JSON.stringify(state, null, 2));
613
+ }
614
+
615
+ function loadRoadmapState(cwd: string): RoadmapState | null {
616
+ const p = join(cwd, ROADMAP_STATE_FILE);
617
+ if (!existsSync(p)) return null;
618
+ try {
619
+ return JSON.parse(readFileSync(p, "utf-8"));
620
+ } catch {
621
+ return null;
622
+ }
623
+ }
624
+
625
+ function formatRoadmapStatus(phases: RoadmapPhase[]): string {
626
+ if (phases.length === 0) {
627
+ return "No implementation-roadmap.md found in .pi/architecture/.";
628
+ }
629
+ const lines = ["## Implementation Roadmap", ""];
630
+ for (const phase of phases) {
631
+ const icon = phase.status === "done" ? "✅" : phase.status === "in_progress" ? "🔄" : "⏳";
632
+ lines.push(`### Phase ${phase.index}: ${phase.title} ${icon}`);
633
+ lines.push(`**Goal:** ${phase.goal}`);
634
+ lines.push(`**Days:** ${phase.days}`);
635
+ lines.push(`**Status:** ${phase.status}`);
636
+ lines.push(`**Modules (${phase.modules.length}):** ${phase.modules.map((m: RoadmapPhaseModule) => m.name).join(", ")}`);
637
+ if (phase.migrations.length > 0) {
638
+ lines.push(`**Migrations:** ${phase.migrations.length}`);
639
+ }
640
+ if (phase.criteria.length > 0) {
641
+ const done = phase.criteria.filter(() => phase.status === "done").length;
642
+ lines.push(`**Criteria:** ${done}/${phase.criteria.length}`);
643
+ }
644
+ if (phase.dependencies.length > 0) {
645
+ lines.push(`**Depends on:** ${phase.dependencies.join(", ")}`);
646
+ }
647
+ lines.push("");
648
+ }
649
+
650
+ const done = phases.filter((p) => p.status === "done").length;
651
+ const inProgress = phases.filter((p) => p.status === "in_progress").length;
652
+ lines.push(`**Overall:** ${done}/${phases.length} phases done, ${inProgress} in progress`);
653
+ return lines.join("\n");
654
+ }
655
+
656
+ function getNextPendingPhase(phases: RoadmapPhase[]): RoadmapPhase | null {
657
+ for (const phase of phases) {
658
+ if (phase.status !== "done") {
659
+ // Check dependencies
660
+ const depsMet = phase.dependencies.every((dep: string) => {
661
+ // Dependency format: "Phase N" or "Phase N: Name"
662
+ const depMatch = dep.match(/Phase\s+(\d+)/i);
663
+ if (depMatch) {
664
+ const depIdx = parseInt(depMatch[1], 10);
665
+ const depPhase = phases.find((p: RoadmapPhase) => p.index === depIdx);
666
+ return depPhase && depPhase.status === "done";
667
+ }
668
+ return true; // non-phase dependency treated as met
669
+ });
670
+ if (!depsMet) return null; // can't start yet
671
+ return phase;
672
+ }
673
+ }
674
+ return null; // all done
675
+ }
676
+
445
677
  // ── Issue Generation ──
446
678
 
447
679
  function generateIssueMarkdown(
@@ -1248,13 +1480,16 @@ export default function (pi: ExtensionAPI) {
1248
1480
  }
1249
1481
 
1250
1482
  pi.registerCommand("architect", {
1251
- description: "Orchestrate the full architecture-to-implementation process",
1483
+ description: "Orchestrate the full architecture-to-implementation process including roadmap phases",
1252
1484
  handler: async (args, ctx) => {
1253
1485
  if (!manager) manager = new EpicManager(ctx.cwd);
1254
1486
  const raw = typeof args === "string" ? args : "";
1255
1487
  const tokens = raw ? raw.split(/\s+/).filter(Boolean) : [];
1256
1488
  if (tokens.length === 0) {
1257
- ctx.ui.notify("Usage: /architect [--epic Name] [--tracking-issue N] | status | next-epic | abort", "info");
1489
+ ctx.ui.notify(
1490
+ "Usage: /architect [--epic Name] [--tracking-issue N] | --roadmap | --phase \"Phase 1\" | --phase-status | --phase-done <N> | status | next-epic | abort",
1491
+ "info",
1492
+ );
1258
1493
  return;
1259
1494
  }
1260
1495
  const action = tokens[0];
@@ -1285,6 +1520,177 @@ export default function (pi: ExtensionAPI) {
1285
1520
  const epicName = findFlag(tokens, "--epic");
1286
1521
  const trackingIssueId = findFlag(tokens, "--tracking-issue");
1287
1522
 
1523
+ // ── Roadmap commands ──
1524
+ if (tokens[0] === "--roadmap" || action === "roadmap") {
1525
+ const phases = parseRoadmap(ctx.cwd);
1526
+ ctx.ui.notify(formatRoadmapStatus(phases), "info");
1527
+ return;
1528
+ }
1529
+
1530
+ if (tokens[0] === "--phase-status" || action === "phase-status") {
1531
+ const phases = parseRoadmap(ctx.cwd);
1532
+ const next = getNextPendingPhase(phases);
1533
+ if (!next) {
1534
+ const allDone = phases.every((p) => p.status === "done");
1535
+ ctx.ui.notify(allDone ? "All phases complete! 🎉" : "Next phase blocked by dependencies.", "info");
1536
+ return;
1537
+ }
1538
+ ctx.ui.notify(`Next phase: Phase ${next.index}: ${next.title} (${next.modules.length} modules)`, "info");
1539
+ return;
1540
+ }
1541
+
1542
+ if (tokens[0] === "--phase" || action === "phase") {
1543
+ const phaseName = findFlag(tokens, "--phase") || tokens.slice(1).join(" ");
1544
+ if (!phaseName) {
1545
+ ctx.ui.notify('Usage: /architect --phase "Phase 1"', "error");
1546
+ return;
1547
+ }
1548
+
1549
+ const phases = parseRoadmap(ctx.cwd);
1550
+ if (phases.length === 0) {
1551
+ ctx.ui.notify("No implementation-roadmap.md found in .pi/architecture/.", "error");
1552
+ return;
1553
+ }
1554
+
1555
+ // Find phase by name or index
1556
+ let targetPhase: RoadmapPhase | undefined;
1557
+ const idxMatch = phaseName.match(/Phase\s+(\d+)/i);
1558
+ if (idxMatch) {
1559
+ targetPhase = phases.find((p) => p.index === parseInt(idxMatch[1], 10));
1560
+ } else {
1561
+ targetPhase = phases.find((p) => p.title.toLowerCase().includes(phaseName.toLowerCase()));
1562
+ }
1563
+
1564
+ if (!targetPhase) {
1565
+ ctx.ui.notify(`Phase "${phaseName}" not found in roadmap.`, "error");
1566
+ return;
1567
+ }
1568
+
1569
+ // Check dependencies
1570
+ const unmetDeps = targetPhase.dependencies.filter((dep) => {
1571
+ const depMatch = dep.match(/Phase\s+(\d+)/i);
1572
+ if (!depMatch) return false;
1573
+ const depPhase = phases.find((p) => p.index === parseInt(depMatch[1], 10));
1574
+ return depPhase && depPhase.status !== "done";
1575
+ });
1576
+ if (unmetDeps.length > 0) {
1577
+ ctx.ui.notify(
1578
+ `Cannot start Phase ${targetPhase.index}: unmet dependencies (${unmetDeps.join(", ")}).`,
1579
+ "error",
1580
+ );
1581
+ return;
1582
+ }
1583
+
1584
+ if (targetPhase.status === "done") {
1585
+ ctx.ui.notify(`Phase ${targetPhase.index}: ${targetPhase.title} is already complete.`, "info");
1586
+ return;
1587
+ }
1588
+
1589
+ // Mark phase as in_progress
1590
+ const roadmapState = loadRoadmapState(ctx.cwd) || {
1591
+ phases: [],
1592
+ currentPhaseIndex: 0,
1593
+ startedAt: new Date().toISOString(),
1594
+ updatedAt: new Date().toISOString(),
1595
+ };
1596
+ roadmapState.phases = phases.map((p) => ({
1597
+ ...p,
1598
+ status: p.index === targetPhase!.index ? "in_progress" : p.status,
1599
+ }));
1600
+ roadmapState.currentPhaseIndex = targetPhase.index;
1601
+ roadmapState.updatedAt = new Date().toISOString();
1602
+ saveRoadmapState(ctx.cwd, roadmapState);
1603
+
1604
+ // Generate start message with module breakdown
1605
+ const moduleList = targetPhase.modules
1606
+ .map((m) => `- **${m.name}**: ${m.deliverables}`)
1607
+ .join("\n");
1608
+ const migrateList =
1609
+ targetPhase.migrations.length > 0
1610
+ ? "\n\n### Database Migrations\n" +
1611
+ targetPhase.migrations.map((m) => `- \`${m}\``).join("\n")
1612
+ : "";
1613
+
1614
+ const instructions = [
1615
+ `## Phase ${targetPhase.index}: ${targetPhase.title}`,
1616
+ `**Goal:** ${targetPhase.goal}`,
1617
+ `**Days:** ${targetPhase.days}`,
1618
+ "",
1619
+ "### Modules",
1620
+ moduleList,
1621
+ migrateList,
1622
+ "",
1623
+ "### How to proceed",
1624
+ "Create epics for each module above using:",
1625
+ ` \`/architect --epic "<module-name>"\``,
1626
+ "",
1627
+ "Complete all module epics, then mark phase done:",
1628
+ ` \`/architect --phase-done ${targetPhase.index}\``,
1629
+ "",
1630
+ "### Acceptance Criteria",
1631
+ targetPhase.criteria.map((c) => `- [ ] ${c}`).join("\n"),
1632
+ ].join("\n");
1633
+
1634
+ ctx.ui.notify(
1635
+ `Phase ${targetPhase.index}: ${targetPhase.title} started (${targetPhase.modules.length} modules).`,
1636
+ "success",
1637
+ );
1638
+ pi.sendMessage(
1639
+ { content: instructions, display: true },
1640
+ { deliverAs: "followUp", triggerTurn: true },
1641
+ );
1642
+ return;
1643
+ }
1644
+
1645
+ if (tokens[0] === "--phase-done" || action === "phase-done") {
1646
+ const phaseIdx = parseInt(tokens[1] || "", 10);
1647
+ if (isNaN(phaseIdx)) {
1648
+ ctx.ui.notify('Usage: /architect --phase-done <phase-number>', "error");
1649
+ return;
1650
+ }
1651
+ const phases = parseRoadmap(ctx.cwd);
1652
+ const phase = phases.find((p) => p.index === phaseIdx);
1653
+ if (!phase) {
1654
+ ctx.ui.notify(`Phase ${phaseIdx} not found.`, "error");
1655
+ return;
1656
+ }
1657
+ const roadmapState = loadRoadmapState(ctx.cwd) || {
1658
+ phases: [],
1659
+ currentPhaseIndex: 0,
1660
+ startedAt: new Date().toISOString(),
1661
+ updatedAt: new Date().toISOString(),
1662
+ };
1663
+ roadmapState.phases = phases.map((p) => ({
1664
+ ...p,
1665
+ status: p.index === phaseIdx ? "done" : p.status,
1666
+ }));
1667
+ roadmapState.updatedAt = new Date().toISOString();
1668
+ saveRoadmapState(ctx.cwd, roadmapState);
1669
+ ctx.ui.notify(`Phase ${phaseIdx}: ${phase.title} marked complete! ✅`, "success");
1670
+
1671
+ const next = getNextPendingPhase(
1672
+ phases.map((p) => ({ ...p, status: p.index === phaseIdx ? "done" : p.status })),
1673
+ );
1674
+ if (next) {
1675
+ pi.sendMessage(
1676
+ {
1677
+ content: `**Next up:** Phase ${next.index}: ${next.title} — run \`/architect --phase "Phase ${next.index}"\` to start.`,
1678
+ display: true,
1679
+ },
1680
+ { deliverAs: "followUp", triggerTurn: true },
1681
+ );
1682
+ } else {
1683
+ const allDone = phases.every((p) => p.index === phaseIdx || p.status === "done");
1684
+ if (allDone) {
1685
+ pi.sendMessage(
1686
+ { content: "🎉 **All roadmap phases complete!**", display: true },
1687
+ { deliverAs: "followUp", triggerTurn: true },
1688
+ );
1689
+ }
1690
+ }
1691
+ return;
1692
+ }
1693
+
1288
1694
  if (!epicName) {
1289
1695
  ctx.ui.notify('Usage: /architect --epic "Epic Name" [--tracking-issue N]', "error");
1290
1696
  return;
@@ -1455,4 +1861,15 @@ export default function (pi: ExtensionAPI) {
1455
1861
  return { content: [{ type: "text", text: lines.join("\n") }] };
1456
1862
  },
1457
1863
  });
1864
+
1865
+ pi.registerTool({
1866
+ name: "architect_roadmap",
1867
+ label: "Architect Roadmap",
1868
+ description: "Show the implementation roadmap phases and status.",
1869
+ parameters: { type: "object", properties: {} },
1870
+ async execute(_toolCallId, _params, _signal, _onUpdate, ctx) {
1871
+ const phases = parseRoadmap(ctx.cwd);
1872
+ return { content: [{ type: "text", text: formatRoadmapStatus(phases) }] };
1873
+ },
1874
+ });
1458
1875
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "timestamp": "2026-07-02T20:21:01Z",
2
+ "timestamp": "2026-07-02T20:32:22Z",
3
3
  "mode": "all",
4
4
  "stages_run": [],
5
5
  "summary": {