guardian-framework 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +463 -0
  3. package/dist/cli.js +12958 -0
  4. package/dist/exports.js +12765 -0
  5. package/package.json +61 -0
  6. package/templates/languages/go-patterns.md +411 -0
  7. package/templates/languages/java-patterns.md +545 -0
  8. package/templates/languages/python-patterns.md +312 -0
  9. package/templates/languages/rust-patterns.md +232 -0
  10. package/templates/languages/typescript-patterns.md +313 -0
  11. package/templates/pi/INDEX.md +437 -0
  12. package/templates/pi/agent/AGENTS.md +166 -0
  13. package/templates/pi/agents/architecture-coordinator.md +40 -0
  14. package/templates/pi/agents/architecture-validator.md +36 -0
  15. package/templates/pi/agents/bootstrap-implementer.md +37 -0
  16. package/templates/pi/agents/issue-factory.md +31 -0
  17. package/templates/pi/agents/operations-validator.md +32 -0
  18. package/templates/pi/agents/security-validator.md +33 -0
  19. package/templates/pi/architecture/CHANGELOG.md +78 -0
  20. package/templates/pi/architecture/decisions/ADR-template.md +76 -0
  21. package/templates/pi/architecture/diagrams/system-overview.md +159 -0
  22. package/templates/pi/architecture/modules/module-template.md +152 -0
  23. package/templates/pi/context/checklists.md +91 -0
  24. package/templates/pi/context/domain-workflow.md +180 -0
  25. package/templates/pi/context/output-formats.md +135 -0
  26. package/templates/pi/context/patterns-base.md +47 -0
  27. package/templates/pi/context/patterns.md +38 -0
  28. package/templates/pi/context/project.md +88 -0
  29. package/templates/pi/domain/exploration.md +93 -0
  30. package/templates/pi/domain/ubiquitous-language.md +24 -0
  31. package/templates/pi/extensions/architect-lib/generators.ts +528 -0
  32. package/templates/pi/extensions/architect-lib/helpers.ts +344 -0
  33. package/templates/pi/extensions/architect-lib/types.ts +117 -0
  34. package/templates/pi/extensions/architect.ts +1425 -0
  35. package/templates/pi/extensions/ask-user-question.ts +579 -0
  36. package/templates/pi/extensions/bash-guard.ts +556 -0
  37. package/templates/pi/extensions/config-reload.ts +115 -0
  38. package/templates/pi/extensions/coordinator.ts +304 -0
  39. package/templates/pi/extensions/curator.ts +543 -0
  40. package/templates/pi/extensions/domain-explorer.ts +990 -0
  41. package/templates/pi/extensions/filechanges.ts +656 -0
  42. package/templates/pi/extensions/goal-loop.ts +717 -0
  43. package/templates/pi/extensions/hooks.ts +452 -0
  44. package/templates/pi/extensions/kanban.ts +547 -0
  45. package/templates/pi/extensions/pipeline.ts +1246 -0
  46. package/templates/pi/extensions/plan-mode.ts +209 -0
  47. package/templates/pi/extensions/project-scaffolder.ts +284 -0
  48. package/templates/pi/extensions/read-only-mode.ts +192 -0
  49. package/templates/pi/extensions/redaction.ts +128 -0
  50. package/templates/pi/extensions/session-persistence.ts +174 -0
  51. package/templates/pi/extensions/slash-commands.ts +229 -0
  52. package/templates/pi/extensions/snippets.ts +201 -0
  53. package/templates/pi/extensions/validation-runner.ts +100 -0
  54. package/templates/pi/github/agents/architecture-coordinator.agent.md +27 -0
  55. package/templates/pi/github/agents/architecture-validator.agent.md +20 -0
  56. package/templates/pi/github/agents/bootstrap-implementer.agent.md +20 -0
  57. package/templates/pi/github/agents/epic-planner.agent.md +70 -0
  58. package/templates/pi/github/agents/issue-factory.agent.md +20 -0
  59. package/templates/pi/github/agents/operations-validator.agent.md +20 -0
  60. package/templates/pi/github/agents/security-validator.agent.md +20 -0
  61. package/templates/pi/github/copilot/settings.json +16 -0
  62. package/templates/pi/github/copilot-instructions.md +155 -0
  63. package/templates/pi/github/instructions/architecture.instructions.md +71 -0
  64. package/templates/pi/github/instructions/validation.instructions.md +69 -0
  65. package/templates/pi/github/workflows/01-planning-workflow.md +75 -0
  66. package/templates/pi/github/workflows/02-issue-generation-workflow.md +69 -0
  67. package/templates/pi/github/workflows/03-implementation-workflow.md +80 -0
  68. package/templates/pi/github/workflows/04-validation-workflow.md +68 -0
  69. package/templates/pi/preflight_report.json +99 -0
  70. package/templates/pi/prompts/blueprint-update.md +262 -0
  71. package/templates/pi/prompts/blueprint-validate.md +218 -0
  72. package/templates/pi/prompts/bug-fix.md +50 -0
  73. package/templates/pi/prompts/ci-blueprint.md +131 -0
  74. package/templates/pi/prompts/context-refresh.md +258 -0
  75. package/templates/pi/prompts/epic-plan.md +356 -0
  76. package/templates/pi/prompts/epic-template.md +186 -0
  77. package/templates/pi/prompts/feature-development.md +131 -0
  78. package/templates/pi/prompts/git-issues.md +243 -0
  79. package/templates/pi/prompts/hotfix.md +71 -0
  80. package/templates/pi/prompts/issue-closeout.md +342 -0
  81. package/templates/pi/prompts/issue-draft.md +218 -0
  82. package/templates/pi/prompts/issue-implementation-series.md +173 -0
  83. package/templates/pi/prompts/issue-merge.md +372 -0
  84. package/templates/pi/prompts/issue-template-set.md +392 -0
  85. package/templates/pi/prompts/issue-template.md +207 -0
  86. package/templates/pi/prompts/pattern-extract.md +269 -0
  87. package/templates/pi/prompts/plan-to-issues.md +430 -0
  88. package/templates/pi/prompts/refactoring.md +82 -0
  89. package/templates/pi/prompts/scope-analyzer.md +254 -0
  90. package/templates/pi/prompts/sync-check.md +230 -0
  91. package/templates/pi/scripts/categorize-issues.sh +171 -0
  92. package/templates/pi/scripts/ci/check_architecture_conformance.sh +748 -0
  93. package/templates/pi/scripts/ci/check_planning_packet.py +228 -0
  94. package/templates/pi/scripts/ci/run_hardening_stages.sh +264 -0
  95. package/templates/pi/scripts/ci/run_preflight.sh +649 -0
  96. package/templates/pi/scripts/ci/run_stage.sh +193 -0
  97. package/templates/pi/scripts/ci/stage_docs_policy.sh +82 -0
  98. package/templates/pi/scripts/ci/stage_integration.sh +3 -0
  99. package/templates/pi/scripts/ci/stage_lint.sh +130 -0
  100. package/templates/pi/scripts/ci/stage_migration_verify.sh +3 -0
  101. package/templates/pi/scripts/ci/stage_package_build.sh +3 -0
  102. package/templates/pi/scripts/ci/stage_release_readiness.sh +3 -0
  103. package/templates/pi/scripts/ci/stage_remaining.sh +242 -0
  104. package/templates/pi/scripts/ci/stage_security.sh +3 -0
  105. package/templates/pi/scripts/ci/stage_static_analysis.sh +114 -0
  106. package/templates/pi/scripts/ci/stage_unit.sh +119 -0
  107. package/templates/pi/scripts/ci/validate_agent_output.py +181 -0
  108. package/templates/pi/scripts/ci/validate_agent_output.sh +243 -0
  109. package/templates/pi/scripts/create-feature-branch.sh +136 -0
  110. package/templates/pi/scripts/create-mr.sh +117 -0
  111. package/templates/pi/scripts/fetch-issues.sh +131 -0
  112. package/templates/pi/scripts/generate-architecture.sh +161 -0
  113. package/templates/pi/scripts/git/close-epic.sh +69 -0
  114. package/templates/pi/scripts/git/close-issue.sh +35 -0
  115. package/templates/pi/scripts/git/create-tracking-issue.sh +174 -0
  116. package/templates/pi/scripts/git/link-issue-to-epic.sh +51 -0
  117. package/templates/pi/scripts/git/update-tracking-issue.sh +66 -0
  118. package/templates/pi/scripts/languages/go/validate-architecture.sh +139 -0
  119. package/templates/pi/scripts/languages/go/validate-canonical.sh +129 -0
  120. package/templates/pi/scripts/languages/go/validate-ci.sh +132 -0
  121. package/templates/pi/scripts/languages/go/validate-integration.sh +156 -0
  122. package/templates/pi/scripts/languages/go/validate-operations.sh +146 -0
  123. package/templates/pi/scripts/languages/go/validate-security.sh +135 -0
  124. package/templates/pi/scripts/languages/go/validate-tests.sh +125 -0
  125. package/templates/pi/scripts/languages/java/validate-annotations.sh +185 -0
  126. package/templates/pi/scripts/languages/java/validate-architecture.sh +159 -0
  127. package/templates/pi/scripts/languages/java/validate-canonical.sh +114 -0
  128. package/templates/pi/scripts/languages/java/validate-ci.sh +269 -0
  129. package/templates/pi/scripts/languages/java/validate-integration.sh +149 -0
  130. package/templates/pi/scripts/languages/java/validate-operations.sh +142 -0
  131. package/templates/pi/scripts/languages/java/validate-security.sh +174 -0
  132. package/templates/pi/scripts/languages/java/validate-spring-architecture.sh +227 -0
  133. package/templates/pi/scripts/languages/java/validate-tests.sh +159 -0
  134. package/templates/pi/scripts/languages/python/validate-architecture.sh +99 -0
  135. package/templates/pi/scripts/languages/python/validate-canonical.sh +124 -0
  136. package/templates/pi/scripts/languages/python/validate-ci.sh +130 -0
  137. package/templates/pi/scripts/languages/python/validate-integration.sh +119 -0
  138. package/templates/pi/scripts/languages/python/validate-operations.sh +107 -0
  139. package/templates/pi/scripts/languages/python/validate-security.sh +68 -0
  140. package/templates/pi/scripts/languages/python/validate-tests.sh +102 -0
  141. package/templates/pi/scripts/languages/rust/validate-architecture.sh +188 -0
  142. package/templates/pi/scripts/languages/rust/validate-canonical.sh +146 -0
  143. package/templates/pi/scripts/languages/rust/validate-ci.sh +147 -0
  144. package/templates/pi/scripts/languages/rust/validate-integration.sh +149 -0
  145. package/templates/pi/scripts/languages/rust/validate-operations.sh +154 -0
  146. package/templates/pi/scripts/languages/rust/validate-security.sh +153 -0
  147. package/templates/pi/scripts/languages/rust/validate-tests.sh +150 -0
  148. package/templates/pi/scripts/languages/typescript/validate-architecture.sh +157 -0
  149. package/templates/pi/scripts/languages/typescript/validate-canonical.sh +135 -0
  150. package/templates/pi/scripts/languages/typescript/validate-ci.sh +244 -0
  151. package/templates/pi/scripts/languages/typescript/validate-integration.sh +166 -0
  152. package/templates/pi/scripts/languages/typescript/validate-operations.sh +162 -0
  153. package/templates/pi/scripts/languages/typescript/validate-security.sh +169 -0
  154. package/templates/pi/scripts/languages/typescript/validate-tests.sh +158 -0
  155. package/templates/pi/scripts/merge-mr.sh +95 -0
  156. package/templates/pi/scripts/mr-validation.sh +228 -0
  157. package/templates/pi/scripts/validate-architecture-readiness.sh +153 -0
  158. package/templates/pi/scripts/validate-architecture.sh +22 -0
  159. package/templates/pi/scripts/validate-canonical.sh +22 -0
  160. package/templates/pi/scripts/validate-ci.sh +37 -0
  161. package/templates/pi/scripts/validate-integration.sh +22 -0
  162. package/templates/pi/scripts/validate-operations.sh +22 -0
  163. package/templates/pi/scripts/validate-security.sh +22 -0
  164. package/templates/pi/scripts/validate-tests.sh +22 -0
  165. package/templates/pi/scripts/validate-ubiquitous-language.sh +291 -0
  166. package/templates/pi/scripts/validation-cache.sh +139 -0
  167. package/templates/pi/skills/agents/architecture-coordinator.md +178 -0
  168. package/templates/pi/skills/agents/architecture-generator.md +79 -0
  169. package/templates/pi/skills/agents/architecture-validator.md +74 -0
  170. package/templates/pi/skills/agents/ci-mr-validator.md +46 -0
  171. package/templates/pi/skills/agents/code-developer.md +76 -0
  172. package/templates/pi/skills/agents/commit.md +46 -0
  173. package/templates/pi/skills/agents/curator.md +71 -0
  174. package/templates/pi/skills/agents/debug.md +59 -0
  175. package/templates/pi/skills/agents/documentation-maintainer.md +36 -0
  176. package/templates/pi/skills/agents/goal-loop.md +125 -0
  177. package/templates/pi/skills/agents/hooks.md +122 -0
  178. package/templates/pi/skills/agents/integration-validator.md +35 -0
  179. package/templates/pi/skills/agents/issue-creator.md +75 -0
  180. package/templates/pi/skills/agents/issue-factory.md +143 -0
  181. package/templates/pi/skills/agents/kanban.md +85 -0
  182. package/templates/pi/skills/agents/land.md +59 -0
  183. package/templates/pi/skills/agents/operations-validator.md +43 -0
  184. package/templates/pi/skills/agents/pipeline.md +113 -0
  185. package/templates/pi/skills/agents/plan-mode.md +77 -0
  186. package/templates/pi/skills/agents/pull.md +40 -0
  187. package/templates/pi/skills/agents/push.md +41 -0
  188. package/templates/pi/skills/agents/security-validator.md +85 -0
  189. package/templates/pi/skills/agents/session-persistence.md +119 -0
  190. package/templates/pi/skills/agents/slash-commands.md +117 -0
  191. package/templates/pi/skills/agents/snippets.md +99 -0
  192. package/templates/pi/skills/agents/subagent-registry.md +78 -0
  193. package/templates/pi/skills/agents/test-validator.md +42 -0
  194. package/templates/pi/skills/validators/architecture-validator.md +43 -0
  195. package/templates/pi/skills/validators/ci-validator.md +27 -0
  196. package/templates/pi/skills/validators/context-compaction.md +73 -0
  197. package/templates/pi/skills/validators/integration-validator.md +38 -0
  198. package/templates/pi/skills/validators/model-registry.md +96 -0
  199. package/templates/pi/skills/validators/operations-validator.md +34 -0
  200. package/templates/pi/skills/validators/security-guards.md +72 -0
  201. package/templates/pi/skills/validators/security-validator.md +41 -0
  202. package/templates/pi/skills/validators/system-prompt-tiers.md +69 -0
  203. package/templates/pi/skills/validators/test-validator.md +33 -0
  204. package/templates/pi/types.ts +62 -0
  205. package/templates/pi/validators/README.md +93 -0
  206. package/templates/pi/validators/default.toml +209 -0
  207. package/templates/pi/validators/spring.toml +142 -0
  208. package/templates/pi/workpad.md +53 -0
  209. package/templates/project/java/Dockerfile +14 -0
  210. package/templates/project/java/README.md +40 -0
  211. package/templates/project/java/pom.xml +132 -0
  212. package/templates/project/typescript/Dockerfile +15 -0
  213. package/templates/project/typescript/README.md +29 -0
  214. package/templates/project/typescript/package.json +23 -0
  215. package/templates/project/typescript/tsconfig.json +21 -0
  216. package/templates/workflow.md +123 -0
@@ -0,0 +1,344 @@
1
+ import { execFileSync, execSync } from "node:child_process";
2
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import type { ExtensionContext, EpicState, ModuleComponent, ArchitectureSlice } from "./types.ts";
5
+ // ── Constants ──
6
+
7
+ export const EPIC_STATE_KEY = ".pi/.guardian-epic-state.json";
8
+ export const ARCH_MODULES_DIR = ".pi/architecture/modules";
9
+ export const ISSUES_DIR = ".pi/issues";
10
+
11
+ // ── Helpers ──
12
+
13
+ export function log(ctx: ExtensionContext, message: string, level = "info") {
14
+ ctx.ui.notify(message, level);
15
+ }
16
+
17
+ export function runScript(cwd: string, script: string): { exitCode: number; stdout: string } {
18
+ try {
19
+ const stdout = execSync(`bash -c "${script}"`, { cwd, timeout: 120_000, encoding: "utf-8" });
20
+ return { exitCode: 0, stdout };
21
+ } catch (e: unknown) {
22
+ const err = e as { status?: number; stdout?: string; message?: string };
23
+ return { exitCode: err.status ?? 1, stdout: err.stdout ?? err.message ?? "" };
24
+ }
25
+ }
26
+
27
+ // Read repoTool from guardian-manifest.json (defaults to "gh")
28
+ export function readRepoTool(cwd: string): string {
29
+ try {
30
+ const manifestPath = join(cwd, "guardian-manifest.json");
31
+ if (existsSync(manifestPath)) {
32
+ const raw = readFileSync(manifestPath, "utf-8");
33
+ const manifest = JSON.parse(raw) as { repoTool?: string };
34
+ if (manifest.repoTool === "glab") return "glab";
35
+ }
36
+ } catch {
37
+ // fall through to default
38
+ }
39
+ return "gh";
40
+ }
41
+
42
+ // Read the repository slug (owner/repo) from guardian-manifest.json
43
+ export function readRepository(cwd: string): string | null {
44
+ try {
45
+ const manifestPath = join(cwd, "guardian-manifest.json");
46
+ if (existsSync(manifestPath)) {
47
+ const raw = readFileSync(manifestPath, "utf-8");
48
+ const manifest = JSON.parse(raw) as {
49
+ repository?: string;
50
+ templateContext?: { repository?: string };
51
+ };
52
+ if (manifest.repository) return manifest.repository;
53
+ if (manifest.templateContext?.repository)
54
+ return manifest.templateContext.repository;
55
+ }
56
+ } catch {
57
+ // ignore
58
+ }
59
+ return null;
60
+ }
61
+
62
+ /**
63
+ * Get Git platform base URL. For GitLab, tries to detect self-hosted instances.
64
+ */
65
+ export function getGitBaseUrl(repoTool: string): string {
66
+ if (repoTool === "glab") {
67
+ try {
68
+ const uri = execSync("glab config get gitlab_uri 2>/dev/null", {
69
+ encoding: "utf-8",
70
+ }).trim();
71
+ if (uri) return uri.replace(/\/+$/, "");
72
+ } catch {
73
+ // fall through to default
74
+ }
75
+ return "https://gitlab.com";
76
+ }
77
+ return "https://github.com";
78
+ }
79
+
80
+ export function commandExists(cmd: string): boolean {
81
+ try {
82
+ execSync(`command -v ${cmd}`, { stdio: "ignore" });
83
+ return true;
84
+ } catch {
85
+ return false;
86
+ }
87
+ }
88
+
89
+ // Try to create a remote GitHub/GitLab issue via the shell script wrapper.
90
+ // Uses execFileSync to avoid shell quoting issues with nested commands.
91
+ export function createRemoteIssue(
92
+ cwd: string,
93
+ title: string,
94
+ bodyFilePath: string,
95
+ labels: string,
96
+ repository?: string,
97
+ ): { success: boolean; issueNumber: string | null; error?: string } {
98
+ const createScript = join(cwd, ".pi/scripts/git/create-tracking-issue.sh");
99
+ if (!existsSync(createScript)) {
100
+ return { success: false, issueNumber: null, error: "create-tracking-issue.sh not found" };
101
+ }
102
+
103
+ const args: string[] = [
104
+ createScript,
105
+ "--title",
106
+ title,
107
+ "--body-file",
108
+ bodyFilePath,
109
+ "--labels",
110
+ labels,
111
+ ];
112
+ if (repository) args.push("--repo", repository);
113
+
114
+ let stdout = "";
115
+ let exitCode = 0;
116
+ try {
117
+ stdout = execFileSync("bash", args, {
118
+ cwd,
119
+ timeout: 120_000,
120
+ encoding: "utf-8",
121
+ });
122
+ } catch (e: unknown) {
123
+ const err = e as { status?: number; stdout?: string; message?: string };
124
+ exitCode = err.status ?? 1;
125
+ stdout = err.stdout ?? err.message ?? "";
126
+ }
127
+
128
+ if (exitCode !== 0) {
129
+ return { success: false, issueNumber: null, error: stdout };
130
+ }
131
+
132
+ const numberMatch = stdout.match(/TRACKING_ID=(\d+)/);
133
+ if (numberMatch) {
134
+ return { success: true, issueNumber: numberMatch[1] };
135
+ }
136
+ const urlMatch = stdout.match(/#(\d+)/);
137
+ if (urlMatch) {
138
+ return { success: true, issueNumber: urlMatch[1] };
139
+ }
140
+ return { success: false, issueNumber: null, error: "Could not parse issue number" };
141
+ }
142
+
143
+ // Ensure the GitHub/GitLab repository exists and local git remote is configured.
144
+ // Returns the repository slug if remote is ready, empty string if not available.
145
+ export function ensureRemoteRepo(
146
+ cwd: string,
147
+ repository: string,
148
+ epicName: string,
149
+ repoTool: string,
150
+ ): string {
151
+ // Check if remote already exists via git remote
152
+ const remoteCheck = runScript(cwd, "git remote get-url origin 2>/dev/null");
153
+ if (remoteCheck.exitCode === 0) {
154
+ return repository;
155
+ }
156
+
157
+ // Remote not configured locally — ensure the remote repo exists on GitHub/GitLab
158
+ if (repoTool === "gh") {
159
+ runScript(
160
+ cwd,
161
+ `gh repo create "${repository}" --private --description "Epic: ${epicName}" 2>&1`,
162
+ );
163
+ // Remove stale origin if it exists but points nowhere useful
164
+ runScript(cwd, "git remote remove origin 2>/dev/null");
165
+ const httpsUrl = `https://github.com/${repository}.git`;
166
+ runScript(cwd, `git remote add origin "${httpsUrl}"`);
167
+ return repository;
168
+ }
169
+
170
+ // GitLab path — detect self-hosted base URL from glab config
171
+ const glabBaseUrl = getGitBaseUrl("glab");
172
+ runScript(
173
+ cwd,
174
+ `glab repo create "${repository}" --private --description "Epic: ${epicName}" 2>&1`,
175
+ );
176
+ runScript(cwd, "git remote remove origin 2>/dev/null");
177
+ const httpsUrl = `${glabBaseUrl}/${repository}.git`;
178
+ runScript(cwd, `git remote add origin "${httpsUrl}"`);
179
+ return repository;
180
+ }
181
+
182
+ // Link a remote issue to the epic tracking issue
183
+ export function linkRemoteIssue(
184
+ cwd: string,
185
+ issueId: string,
186
+ epicId: string,
187
+ ): { success: boolean; error?: string } {
188
+ const linkScript = join(cwd, ".pi/scripts/git/link-issue-to-epic.sh");
189
+ if (!existsSync(linkScript)) {
190
+ return { success: false, error: "link-issue-to-epic.sh not found" };
191
+ }
192
+
193
+ const safeIssue = issueId.replace(/[^a-zA-Z0-9 _\-.]/g, "");
194
+ const safeEpic = epicId.replace(/[^a-zA-Z0-9 _\-.]/g, "");
195
+
196
+ const cmd = `bash "${linkScript}" --issue-id "${safeIssue}" --epic-id "${safeEpic}"`;
197
+ const result = runScript(cwd, cmd);
198
+ if (result.exitCode !== 0) {
199
+ return { success: false, error: result.stdout };
200
+ }
201
+ return { success: true };
202
+ }
203
+
204
+ // ── Architecture Discovery ──
205
+
206
+ export function readGroupId(cwd: string): string {
207
+ // Try pom.xml
208
+ const pomPath = join(cwd, "pom.xml");
209
+ try {
210
+ const pom = readFileSync(pomPath, "utf-8");
211
+ const match = pom.match(/<groupId>([^<]+)<\/groupId>/);
212
+ if (match && match[1] !== "com.example") return match[1];
213
+ } catch {}
214
+ // Try build.gradle
215
+ const gradlePath = join(cwd, "build.gradle");
216
+ try {
217
+ const gradle = readFileSync(gradlePath, "utf-8");
218
+ const match = gradle.match(/group\s*=\s*['"]([^'"]+)['"]/);
219
+ if (match) return match[1];
220
+ } catch {}
221
+ return "com.example";
222
+ }
223
+
224
+ export function findModuleByName(cwd: string, name: string): string | null {
225
+ const files = discoverModules(cwd);
226
+ const nameLower = name.toLowerCase().replace(/[^a-z0-9]/g, "");
227
+ for (const f of files) {
228
+ const key = f.replace(".md", "").toLowerCase().replace(/[^a-z0-9]/g, "");
229
+ if (key === nameLower || nameLower.includes(key) || key.includes(nameLower)) {
230
+ return f;
231
+ }
232
+ }
233
+ return null;
234
+ }
235
+
236
+ export function discoverModules(cwd: string): string[] {
237
+ const dir = join(cwd, ARCH_MODULES_DIR);
238
+ if (!existsSync(dir)) return [];
239
+ try {
240
+ return readdirSync(dir).filter((f) => f.endsWith(".md"));
241
+ } catch {
242
+ return [];
243
+ }
244
+ }
245
+
246
+ export function parseModuleFile(filePath: string): ModuleComponent[] {
247
+ if (!existsSync(filePath)) return [];
248
+ const content = readFileSync(filePath, "utf-8");
249
+ const components: ModuleComponent[] = [];
250
+
251
+ const lines = content.split("\n");
252
+ let inComponentSection = false;
253
+ let inDetailsSection = false;
254
+ let currentName = "";
255
+ let currentStatus = "";
256
+ let currentDesc = "";
257
+ let currentDeps: string[] = [];
258
+
259
+ function saveCurrent() {
260
+ if (currentName) {
261
+ // Default to planned if no explicit status found
262
+ const status = currentStatus || "planned";
263
+ const desc = currentDesc || `${currentName} component`;
264
+ components.push({
265
+ name: currentName,
266
+ status: status as ModuleComponent["status"],
267
+ description: desc.trim(),
268
+ dependencies: currentDeps.length > 0 ? currentDeps : ["none"],
269
+ });
270
+ }
271
+ }
272
+
273
+ for (const line of lines) {
274
+ const trimmed = line.trim();
275
+
276
+ // Enter component section (supports "## Components", "## Component Details", "## Component")
277
+ if (trimmed.match(/^##\s+Components?/i) || trimmed.match(/^##\s+Component\s+Details/i)) {
278
+ inComponentSection = true;
279
+ continue;
280
+ }
281
+
282
+ // Leave component section on next top-level section
283
+ if (inComponentSection && trimmed.match(/^##\s+/) && !trimmed.match(/^##\s+Components?/i)) {
284
+ saveCurrent();
285
+ currentName = "";
286
+ currentStatus = "";
287
+ currentDesc = "";
288
+ currentDeps = [];
289
+ inComponentSection = false;
290
+ inDetailsSection = false;
291
+ continue;
292
+ }
293
+
294
+ // Component heading (###) — start a new component entry
295
+ if (inComponentSection && trimmed.match(/^###\s+/)) {
296
+ // Skip non-component ### headings like "### Depends On" or "### Security"
297
+ const name = trimmed.replace(/^###\s+/, "");
298
+ if (name.match(/^(depends|security|testing|performance|error|change|data flow|responsibilities|overview|interfaces|inputs|outputs)/i)) {
299
+ continue;
300
+ }
301
+ saveCurrent();
302
+ currentName = name;
303
+ currentStatus = "";
304
+ currentDesc = "";
305
+ currentDeps = [];
306
+ continue;
307
+ }
308
+
309
+ if (!currentName) continue;
310
+
311
+ if (trimmed.startsWith("status:")) {
312
+ currentStatus = trimmed.replace("status:", "").trim().toLowerCase();
313
+ } else if (trimmed.startsWith("depends:")) {
314
+ const depsStr = trimmed.replace("depends:", "").trim();
315
+ if (depsStr && depsStr !== "none" && depsStr !== "[TODO") {
316
+ currentDeps = depsStr.split(",").map((d) => d.trim()).filter(Boolean);
317
+ }
318
+ } else if (trimmed.startsWith("**Purpose:**")) {
319
+ currentDesc = trimmed.replace(/\*\*Purpose:\*\*\s*/, "").trim();
320
+ } else if (!currentDesc && trimmed.length > 10 && !trimmed.startsWith("#") && !trimmed.startsWith("-") && !trimmed.startsWith("|") && !trimmed.startsWith(">") && !trimmed.startsWith("```")) {
321
+ // Use first substantial sentence as description
322
+ currentDesc = trimmed.slice(0, 200);
323
+ }
324
+ }
325
+
326
+ saveCurrent();
327
+ return components;
328
+ }
329
+
330
+ export function findNextLogicalSlice(cwd: string, moduleFiles: string[]): ArchitectureSlice | null {
331
+ for (const moduleFile of moduleFiles) {
332
+ const components = parseModuleFile(join(cwd, ARCH_MODULES_DIR, moduleFile));
333
+ const planned = components.filter((c) => c.status === "planned");
334
+ if (planned.length > 0) {
335
+ return {
336
+ module: moduleFile.replace(".md", ""),
337
+ components,
338
+ nextLogicalSlice: planned,
339
+ };
340
+ }
341
+ }
342
+ return null;
343
+ }
344
+
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Architect Extension — Full Architecture-to-Implementation Pipeline
3
+ *
4
+ * OVERVIEW
5
+ * ========
6
+ * /architect is the planning engine that reads architecture module docs from
7
+ * .pi/architecture/modules/ and produces epics + issues for implementation.
8
+ * It is the bridge between domain exploration (/domain --explore) and
9
+ * implementation (/implement-series).
10
+ *
11
+ * PREREQUISITES
12
+ * ============
13
+ * - .pi/architecture/modules/*.md must exist (from /domain --architect-scaffold
14
+ * or manually created)
15
+ * - Git repository initialized (handled automatically if missing)
16
+ * - gh or glab CLI authenticated for remote issue creation (optional)
17
+ *
18
+ * COMMANDS
19
+ * =======
20
+ * /architect --epic "Name" [--tracking-issue N]
21
+ * Starts a new epic: discovers architecture modules, finds the next
22
+ * planned slice, generates issues, creates pipeline state, and sends
23
+ * implementation instructions to the agent via pi.sendMessage() with
24
+ * triggerTurn=true.
25
+ *
26
+ * /architect status
27
+ * Shows current epic state: module, component, pipeline progress,
28
+ * issues, and validators.
29
+ *
30
+ * /architect next-epic
31
+ * Shows which module and components should be implemented next,
32
+ * based on component status (planned vs implemented).
33
+ *
34
+ * /architect abort
35
+ * Cancels the current epic, cleans pipeline state.
36
+ *
37
+ * TOOLS (agent-callable)
38
+ * =====================
39
+ * architect_status — Returns current epic state and progress.
40
+ * architect_discover — Discovers modules and finds next logical slice.
41
+ *
42
+ * WORKFLOW
43
+ * =======
44
+ * 1. /domain --explore "intent" (discovery)
45
+ * 2. /domain --architect-scaffold <id> (generates modules)
46
+ * 3. /architect --epic "Name" (planning + issue gen)
47
+ * 4. Agent implements issues via pipeline
48
+ */
49
+
50
+ // ── Types ──
51
+
52
+ export type ExtensionContext = {
53
+ cwd: string;
54
+ ui: {
55
+ notify(message: string, level?: string): void;
56
+ setStatus(key: string, message: string | null): void;
57
+ confirm(title: string, message: string): Promise<boolean>;
58
+ };
59
+ tools: { execute(name: string, params: Record<string, unknown>): Promise<unknown> };
60
+ };
61
+
62
+ export type ExtensionAPI = {
63
+ on(event: string, handler: (event: unknown, ctx: ExtensionContext) => void | Promise<void>): void;
64
+ registerTool(options: {
65
+ name: string;
66
+ label: string;
67
+ description: string;
68
+ parameters: unknown;
69
+ execute(
70
+ toolCallId: string,
71
+ params: Record<string, unknown>,
72
+ signal: AbortSignal,
73
+ onUpdate: (update: { type: string; message: string }) => void,
74
+ ctx: ExtensionContext,
75
+ ): unknown | Promise<unknown>;
76
+ }): void;
77
+ registerCommand(
78
+ name: string,
79
+ options: {
80
+ description: string;
81
+ handler(args: string, ctx: ExtensionContext): unknown | Promise<unknown>;
82
+ },
83
+ ): void;
84
+ sendMessage<T = unknown>(
85
+ message: { customType?: string; content: string; display?: boolean; details?: Record<string, unknown> },
86
+ options?: { deliverAs?: "steer" | "followUp" | "nextTurn"; triggerTurn?: boolean },
87
+ ): void;
88
+ sendUserMessage(
89
+ content: string,
90
+ options?: { deliverAs?: "steer" | "followUp" },
91
+ ): void;
92
+ };
93
+
94
+ export type ModuleComponent = {
95
+ name: string;
96
+ status: "planned" | "in-progress" | "implemented" | "deprecated";
97
+ description: string;
98
+ dependencies: string[];
99
+ };
100
+
101
+ export type ArchitectureSlice = {
102
+ module: string;
103
+ components: ModuleComponent[];
104
+ nextLogicalSlice: ModuleComponent[];
105
+ };
106
+
107
+ export type EpicState = {
108
+ name: string;
109
+ trackingIssueId: string | null;
110
+ epicId: string | null;
111
+ status: "planning" | "validating" | "publishing" | "executing" | "done" | "aborted";
112
+ slices: ArchitectureSlice[];
113
+ issues: { id: string; title: string; status: string; remoteIssueId?: string | null }[];
114
+ currentIssueIndex: number;
115
+ createdAt: string;
116
+ };
117
+