pi-blueprint 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/README.md +57 -0
  2. package/dist/blueprint-command.d.ts +5 -0
  3. package/dist/blueprint-command.d.ts.map +1 -0
  4. package/dist/blueprint-command.js +56 -0
  5. package/dist/blueprint-command.js.map +1 -0
  6. package/dist/blueprint-injector.d.ts +3 -0
  7. package/dist/blueprint-injector.d.ts.map +1 -0
  8. package/dist/blueprint-injector.js +11 -0
  9. package/dist/blueprint-injector.js.map +1 -0
  10. package/dist/blueprint-tools.d.ts +4 -0
  11. package/dist/blueprint-tools.d.ts.map +1 -0
  12. package/dist/blueprint-tools.js +302 -0
  13. package/dist/blueprint-tools.js.map +1 -0
  14. package/dist/dependency-graph.d.ts +10 -0
  15. package/dist/dependency-graph.d.ts.map +1 -0
  16. package/dist/dependency-graph.js +101 -0
  17. package/dist/dependency-graph.js.map +1 -0
  18. package/dist/index.d.ts +3 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +88 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/plan-next-command.d.ts +5 -0
  23. package/dist/plan-next-command.d.ts.map +1 -0
  24. package/dist/plan-next-command.js +36 -0
  25. package/dist/plan-next-command.js.map +1 -0
  26. package/dist/plan-renderer.d.ts +3 -0
  27. package/dist/plan-renderer.d.ts.map +1 -0
  28. package/dist/plan-renderer.js +61 -0
  29. package/dist/plan-renderer.js.map +1 -0
  30. package/dist/plan-status-command.d.ts +5 -0
  31. package/dist/plan-status-command.d.ts.map +1 -0
  32. package/dist/plan-status-command.js +20 -0
  33. package/dist/plan-status-command.js.map +1 -0
  34. package/dist/plan-verify-command.d.ts +5 -0
  35. package/dist/plan-verify-command.d.ts.map +1 -0
  36. package/dist/plan-verify-command.js +65 -0
  37. package/dist/plan-verify-command.js.map +1 -0
  38. package/dist/prompts/blueprint-generate.d.ts +2 -0
  39. package/dist/prompts/blueprint-generate.d.ts.map +1 -0
  40. package/dist/prompts/blueprint-generate.js +35 -0
  41. package/dist/prompts/blueprint-generate.js.map +1 -0
  42. package/dist/prompts/phase-context.d.ts +3 -0
  43. package/dist/prompts/phase-context.d.ts.map +1 -0
  44. package/dist/prompts/phase-context.js +62 -0
  45. package/dist/prompts/phase-context.js.map +1 -0
  46. package/dist/state-machine.d.ts +11 -0
  47. package/dist/state-machine.d.ts.map +1 -0
  48. package/dist/state-machine.js +224 -0
  49. package/dist/state-machine.js.map +1 -0
  50. package/dist/storage.d.ts +13 -0
  51. package/dist/storage.d.ts.map +1 -0
  52. package/dist/storage.js +59 -0
  53. package/dist/storage.js.map +1 -0
  54. package/dist/types.d.ts +98 -0
  55. package/dist/types.d.ts.map +1 -0
  56. package/dist/types.js +7 -0
  57. package/dist/types.js.map +1 -0
  58. package/dist/verification.d.ts +4 -0
  59. package/dist/verification.d.ts.map +1 -0
  60. package/dist/verification.js +55 -0
  61. package/dist/verification.js.map +1 -0
  62. package/package.json +72 -0
  63. package/src/blueprint-command.ts +84 -0
  64. package/src/blueprint-injector.ts +10 -0
  65. package/src/blueprint-tools.ts +380 -0
  66. package/src/dependency-graph.ts +113 -0
  67. package/src/index.ts +118 -0
  68. package/src/plan-next-command.ts +56 -0
  69. package/src/plan-renderer.ts +70 -0
  70. package/src/plan-status-command.ts +30 -0
  71. package/src/plan-verify-command.ts +82 -0
  72. package/src/prompts/blueprint-generate.ts +34 -0
  73. package/src/prompts/phase-context.ts +76 -0
  74. package/src/state-machine.ts +278 -0
  75. package/src/storage.ts +83 -0
  76. package/src/types.ts +132 -0
  77. package/src/verification.ts +60 -0
package/src/storage.ts ADDED
@@ -0,0 +1,83 @@
1
+ import { mkdirSync, readFileSync, writeFileSync, appendFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { homedir } from "node:os";
4
+ import { renderPlanMarkdown } from "./plan-renderer.js";
5
+ import type {
6
+ Blueprint,
7
+ BlueprintIndex,
8
+ HistoryEntry,
9
+ SessionsState,
10
+ } from "./types.js";
11
+
12
+ export function getBaseDir(baseDir?: string): string {
13
+ return baseDir ?? join(homedir(), ".pi", "blueprints");
14
+ }
15
+
16
+ export function getBlueprintDir(blueprintId: string, baseDir?: string): string {
17
+ return join(getBaseDir(baseDir), blueprintId);
18
+ }
19
+
20
+ export function ensureStorageLayout(blueprintId: string, baseDir?: string): void {
21
+ const dir = getBlueprintDir(blueprintId, baseDir);
22
+ mkdirSync(dir, { recursive: true });
23
+ }
24
+
25
+ export function ensureBaseDir(baseDir?: string): void {
26
+ mkdirSync(getBaseDir(baseDir), { recursive: true });
27
+ }
28
+
29
+ export function loadIndex(baseDir?: string): BlueprintIndex | null {
30
+ const path = join(getBaseDir(baseDir), "index.json");
31
+ return readJson<BlueprintIndex>(path);
32
+ }
33
+
34
+ export function saveIndex(index: BlueprintIndex, baseDir?: string): void {
35
+ ensureBaseDir(baseDir);
36
+ const path = join(getBaseDir(baseDir), "index.json");
37
+ writeFileSync(path, JSON.stringify(index, null, 2) + "\n");
38
+ }
39
+
40
+ export function loadBlueprint(blueprintId: string, baseDir?: string): Blueprint | null {
41
+ const path = join(getBlueprintDir(blueprintId, baseDir), "state.json");
42
+ return readJson<Blueprint>(path);
43
+ }
44
+
45
+ export function saveBlueprint(blueprint: Blueprint, baseDir?: string): void {
46
+ ensureStorageLayout(blueprint.id, baseDir);
47
+ const dir = getBlueprintDir(blueprint.id, baseDir);
48
+ writeFileSync(join(dir, "state.json"), JSON.stringify(blueprint, null, 2) + "\n");
49
+ writeFileSync(join(dir, "plan.md"), renderPlanMarkdown(blueprint));
50
+ }
51
+
52
+ export function appendHistory(
53
+ blueprintId: string,
54
+ entry: HistoryEntry,
55
+ baseDir?: string,
56
+ ): void {
57
+ ensureStorageLayout(blueprintId, baseDir);
58
+ const path = join(getBlueprintDir(blueprintId, baseDir), "history.jsonl");
59
+ appendFileSync(path, JSON.stringify(entry) + "\n");
60
+ }
61
+
62
+ export function loadSessions(blueprintId: string, baseDir?: string): SessionsState | null {
63
+ const path = join(getBlueprintDir(blueprintId, baseDir), "sessions.json");
64
+ return readJson<SessionsState>(path);
65
+ }
66
+
67
+ export function saveSessions(
68
+ blueprintId: string,
69
+ sessions: SessionsState,
70
+ baseDir?: string,
71
+ ): void {
72
+ ensureStorageLayout(blueprintId, baseDir);
73
+ const path = join(getBlueprintDir(blueprintId, baseDir), "sessions.json");
74
+ writeFileSync(path, JSON.stringify(sessions, null, 2) + "\n");
75
+ }
76
+
77
+ function readJson<T>(path: string): T | null {
78
+ try {
79
+ return JSON.parse(readFileSync(path, "utf-8")) as T;
80
+ } catch {
81
+ return null;
82
+ }
83
+ }
package/src/types.ts ADDED
@@ -0,0 +1,132 @@
1
+ export type TaskStatus = "pending" | "in_progress" | "completed" | "blocked" | "skipped";
2
+
3
+ export type PhaseStatus = "pending" | "active" | "completed" | "verified";
4
+
5
+ export type VerificationType = "tests_pass" | "typecheck_clean" | "user_approval" | "custom_command";
6
+
7
+ export type BlueprintStatus = "draft" | "active" | "completed" | "abandoned";
8
+
9
+ export type HistoryEventType =
10
+ | "blueprint_created"
11
+ | "task_started"
12
+ | "task_completed"
13
+ | "task_blocked"
14
+ | "task_skipped"
15
+ | "phase_started"
16
+ | "phase_completed"
17
+ | "phase_verified"
18
+ | "verification_passed"
19
+ | "verification_failed"
20
+ | "blueprint_completed"
21
+ | "blueprint_abandoned";
22
+
23
+ export interface Task {
24
+ readonly id: string;
25
+ readonly title: string;
26
+ readonly description: string;
27
+ readonly status: TaskStatus;
28
+ readonly acceptance_criteria: readonly string[];
29
+ readonly file_targets: readonly string[];
30
+ readonly dependencies: readonly string[];
31
+ readonly started_at: string | null;
32
+ readonly completed_at: string | null;
33
+ readonly session_id: string | null;
34
+ readonly notes: string | null;
35
+ }
36
+
37
+ export interface VerificationGate {
38
+ readonly type: VerificationType;
39
+ readonly command: string | null;
40
+ readonly description: string;
41
+ readonly passed: boolean;
42
+ readonly last_checked_at: string | null;
43
+ readonly error_message: string | null;
44
+ }
45
+
46
+ export interface Phase {
47
+ readonly id: string;
48
+ readonly title: string;
49
+ readonly description: string;
50
+ readonly status: PhaseStatus;
51
+ readonly tasks: readonly Task[];
52
+ readonly verification_gates: readonly VerificationGate[];
53
+ readonly started_at: string | null;
54
+ readonly completed_at: string | null;
55
+ }
56
+
57
+ export interface Blueprint {
58
+ readonly id: string;
59
+ readonly objective: string;
60
+ readonly project_id: string;
61
+ readonly status: BlueprintStatus;
62
+ readonly created_at: string;
63
+ readonly updated_at: string;
64
+ readonly phases: readonly Phase[];
65
+ readonly active_phase_id: string | null;
66
+ readonly active_task_id: string | null;
67
+ }
68
+
69
+ export interface HistoryEntry {
70
+ readonly timestamp: string;
71
+ readonly event: HistoryEventType;
72
+ readonly phase_id: string | null;
73
+ readonly task_id: string | null;
74
+ readonly session_id: string;
75
+ readonly details: string;
76
+ }
77
+
78
+ export interface SessionRecord {
79
+ readonly session_id: string;
80
+ readonly started_at: string;
81
+ readonly ended_at: string | null;
82
+ readonly tasks_worked: readonly string[];
83
+ readonly tasks_completed: readonly string[];
84
+ }
85
+
86
+ export interface SessionsState {
87
+ readonly sessions: readonly SessionRecord[];
88
+ }
89
+
90
+ export interface BlueprintIndexEntry {
91
+ readonly id: string;
92
+ readonly objective: string;
93
+ readonly status: BlueprintStatus;
94
+ readonly created_at: string;
95
+ readonly project_id: string;
96
+ }
97
+
98
+ export interface BlueprintIndex {
99
+ readonly active_blueprint_id: string | null;
100
+ readonly blueprints: readonly BlueprintIndexEntry[];
101
+ }
102
+
103
+ export interface ProjectInfo {
104
+ readonly id: string;
105
+ readonly name: string;
106
+ readonly root: string;
107
+ }
108
+
109
+ export interface BlueprintExtensionState {
110
+ readonly project: ProjectInfo | null;
111
+ readonly blueprint: Blueprint | null;
112
+ readonly sessionId: string;
113
+ }
114
+
115
+ export interface StateRef {
116
+ get: () => BlueprintExtensionState;
117
+ set: (s: BlueprintExtensionState) => void;
118
+ }
119
+
120
+ export interface VerificationResult {
121
+ readonly passed: boolean;
122
+ readonly output: string;
123
+ readonly duration_ms: number;
124
+ }
125
+
126
+ export function isTaskDone(task: Task): boolean {
127
+ return task.status === "completed" || task.status === "skipped";
128
+ }
129
+
130
+ export function getCompletedTaskIds(tasks: readonly Task[]): Set<string> {
131
+ return new Set(tasks.filter(isTaskDone).map((t) => t.id));
132
+ }
@@ -0,0 +1,60 @@
1
+ import { execSync } from "node:child_process";
2
+ import type { VerificationGate, VerificationResult } from "./types.js";
3
+
4
+ export function runGate(gate: VerificationGate, cwd: string): VerificationResult {
5
+ if (gate.type === "user_approval") {
6
+ return { passed: false, output: "Requires user approval", duration_ms: 0 };
7
+ }
8
+
9
+ const command = resolveCommand(gate);
10
+ if (!command) {
11
+ return { passed: false, output: `No command for gate type: ${gate.type}`, duration_ms: 0 };
12
+ }
13
+
14
+ const start = Date.now();
15
+ try {
16
+ const output = execSync(command, {
17
+ cwd,
18
+ encoding: "utf-8",
19
+ timeout: 120_000,
20
+ stdio: ["pipe", "pipe", "pipe"],
21
+ });
22
+ return { passed: true, output, duration_ms: Date.now() - start };
23
+ } catch (err: unknown) {
24
+ const duration_ms = Date.now() - start;
25
+ const output = extractExecError(err);
26
+ return { passed: false, output, duration_ms };
27
+ }
28
+ }
29
+
30
+ export function runAllGates(
31
+ gates: readonly VerificationGate[],
32
+ cwd: string,
33
+ ): readonly VerificationResult[] {
34
+ return gates
35
+ .filter((g) => g.type !== "user_approval")
36
+ .map((gate) => runGate(gate, cwd));
37
+ }
38
+
39
+ function resolveCommand(gate: VerificationGate): string | null {
40
+ switch (gate.type) {
41
+ case "tests_pass":
42
+ return "npm test";
43
+ case "typecheck_clean":
44
+ return "npx tsc --noEmit";
45
+ case "custom_command":
46
+ return gate.command;
47
+ case "user_approval":
48
+ return null;
49
+ }
50
+ }
51
+
52
+ function extractExecError(err: unknown): string {
53
+ if (err && typeof err === "object") {
54
+ const obj = err as Record<string, unknown>;
55
+ if (typeof obj["stderr"] === "string" && obj["stderr"]) return obj["stderr"];
56
+ if (typeof obj["stdout"] === "string" && obj["stdout"]) return obj["stdout"];
57
+ if (typeof obj["message"] === "string") return obj["message"];
58
+ }
59
+ return "Unknown error";
60
+ }