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.
|
|
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.
|
|
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
|
@@ -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(
|
|
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
|
}
|