guardian-framework 0.1.62 → 0.1.64

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
@@ -3178,7 +3178,7 @@ __export(exports_package, {
3178
3178
  bin: () => bin,
3179
3179
  author: () => author
3180
3180
  });
3181
- var name = "guardian-framework", version = "0.1.62", 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;
3181
+ var name = "guardian-framework", version = "0.1.64", 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;
3182
3182
  var init_package = __esm(() => {
3183
3183
  exports = {
3184
3184
  ".": {
@@ -15085,8 +15085,13 @@ async function runVerify(targetDir = process.cwd()) {
15085
15085
 
15086
15086
  // src/index.ts
15087
15087
  init_manifest();
15088
- var VERSION = "0.1.61";
15089
- var PKG_NAME = "guardian-framework";
15088
+ import { readFileSync as readFileSync16 } from "fs";
15089
+ import { join as join20 } from "path";
15090
+ import { fileURLToPath as fileURLToPath3 } from "url";
15091
+ var __dirname3 = fileURLToPath3(new URL(".", import.meta.url));
15092
+ var PKG = JSON.parse(readFileSync16(join20(__dirname3, "..", "package.json"), "utf-8"));
15093
+ var VERSION = PKG.version;
15094
+ var PKG_NAME = PKG.name;
15090
15095
  var _newerVersion = null;
15091
15096
  function checkForUpdateSync() {
15092
15097
  try {
package/dist/exports.js CHANGED
@@ -1963,7 +1963,7 @@ __export(exports_package, {
1963
1963
  bin: () => bin,
1964
1964
  author: () => author
1965
1965
  });
1966
- var name = "guardian-framework", version = "0.1.62", 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;
1966
+ var name = "guardian-framework", version = "0.1.64", 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;
1967
1967
  var init_package = __esm(() => {
1968
1968
  exports = {
1969
1969
  ".": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "guardian-framework",
3
- "version": "0.1.62",
3
+ "version": "0.1.64",
4
4
  "description": "Token-optimized agentic framework scaffolder with pi-first architecture",
5
5
  "type": "module",
6
6
  "main": "dist/exports.js",
@@ -54,6 +54,24 @@ export function readRepository(cwd: string): string | null {
54
54
  return null;
55
55
  }
56
56
 
57
+ /**
58
+ * Read project language from guardian-manifest.json (defaults to "typescript")
59
+ */
60
+ export function readLanguage(cwd: string): string {
61
+ try {
62
+ const manifestPath = join(cwd, "guardian-manifest.json");
63
+ if (existsSync(manifestPath)) {
64
+ const raw = readFileSync(manifestPath, "utf-8");
65
+ const manifest = JSON.parse(raw) as { language?: string; templateContext?: { language?: string } };
66
+ if (manifest.language) return manifest.language;
67
+ if (manifest.templateContext?.language) return manifest.templateContext.language;
68
+ }
69
+ } catch {
70
+ // fall through to default
71
+ }
72
+ return "typescript";
73
+ }
74
+
57
75
  /**
58
76
  * Get forge base URL. For GitLab, detects self-hosted instances via glab config.
59
77
  */
@@ -5,6 +5,7 @@ import type { ExtensionContext, EpicState, ModuleComponent, ArchitectureSlice, R
5
5
  import {
6
6
  readRepoTool as adapterReadRepoTool,
7
7
  readRepository as adapterReadRepository,
8
+ readLanguage as adapterReadLanguage,
8
9
  getGitBaseUrl as adapterGetGitBaseUrl,
9
10
  commandExists as adapterCommandExists,
10
11
  runScript as adapterRunScript,
@@ -30,6 +31,7 @@ export function log(ctx: ExtensionContext, message: string, level = "info") {
30
31
  export const runScript = adapterRunScript;
31
32
  export const readRepoTool = adapterReadRepoTool;
32
33
  export const readRepository = adapterReadRepository;
34
+ export const readLanguage = adapterReadLanguage;
33
35
  export const getGitBaseUrl = adapterGetGitBaseUrl;
34
36
  export const commandExists = adapterCommandExists;
35
37
 
@@ -0,0 +1,354 @@
1
+ /**
2
+ * TDD Test Generator for Guardian Architect Extension
3
+ *
4
+ * Deterministic test file generation from architecture module components.
5
+ * No LLM involvement — tests are generated from structured component data.
6
+ *
7
+ * When --tdd is enabled on an epic via /architect --epic "Name" --tdd,
8
+ * this generator creates failing test files before implementation issues
9
+ * reach the agent. The agent makes them green via Red→Green→Refactor.
10
+ */
11
+
12
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
13
+ import { join } from "node:path";
14
+ import type { ModuleComponent } from "./types.ts";
15
+
16
+ // ── Supported Languages ──
17
+
18
+ const TDD_LANGUAGES = new Set(["typescript", "rust", "python", "go", "java"]);
19
+
20
+ export function isTddSupported(language: string): boolean {
21
+ return TDD_LANGUAGES.has(language);
22
+ }
23
+
24
+ // ── Main Generator ──
25
+
26
+ export interface TddGenerateOptions {
27
+ /** Array of components from the epic's slice */
28
+ components: ModuleComponent[];
29
+ /** Module identifier (e.g., "audit-ingestion") */
30
+ moduleId: string;
31
+ /** Project root directory */
32
+ cwd: string;
33
+ /** Language from manifest (default: "typescript") */
34
+ language: string;
35
+ /** Base test directory (default: "tests/unit") */
36
+ testBaseDir?: string;
37
+ /** Module name for path construction */
38
+ moduleName?: string;
39
+ }
40
+
41
+ /**
42
+ * Generate failing test files for all components in an epic slice.
43
+ * Returns list of generated file paths.
44
+ */
45
+ export function generateEpicTestFiles(options: TddGenerateOptions): string[] {
46
+ const {
47
+ components,
48
+ moduleId,
49
+ cwd,
50
+ language,
51
+ testBaseDir = "tests/unit",
52
+ moduleName,
53
+ } = options;
54
+
55
+ if (!isTddSupported(language)) {
56
+ return [];
57
+ }
58
+
59
+ const generated: string[] = [];
60
+ const modulePath = moduleName || moduleId;
61
+
62
+ for (const comp of components) {
63
+ const componentId = comp.name.toLowerCase().replace(/[^a-z0-9]+/g, "-");
64
+ const testDir = join(cwd, testBaseDir, modulePath, componentId);
65
+ mkdirSync(testDir, { recursive: true });
66
+
67
+ const fileName = getTestFileName(componentId, language);
68
+ const filePath = join(testDir, fileName);
69
+
70
+ // Skip if file already exists (don't overwrite user edits)
71
+ if (existsSync(filePath)) continue;
72
+
73
+ const content = generateTestContent(comp, language, moduleId, componentId);
74
+ writeFileSync(filePath, content, "utf-8");
75
+ generated.push(filePath);
76
+ }
77
+
78
+ return generated;
79
+ }
80
+
81
+ // ── Test File Name ──
82
+
83
+ function getTestFileName(componentId: string, language: string): string {
84
+ switch (language) {
85
+ case "typescript":
86
+ return `${componentId}.test.ts`;
87
+ case "rust":
88
+ return `${componentId}_test.rs`;
89
+ case "python":
90
+ return `test_${componentId}.py`;
91
+ case "go":
92
+ return `${componentId}_test.go`;
93
+ case "java":
94
+ return `${toPascalCase(componentId)}Test.java`;
95
+ default:
96
+ return `${componentId}.test.ts`;
97
+ }
98
+ }
99
+
100
+ // ── Test Content Generation ──
101
+
102
+ function generateTestContent(
103
+ component: ModuleComponent,
104
+ language: string,
105
+ moduleId: string,
106
+ componentId: string,
107
+ ): string {
108
+ switch (language) {
109
+ case "rust":
110
+ return generateRust(component, moduleId, componentId);
111
+ case "python":
112
+ return generatePython(component, moduleId, componentId);
113
+ case "go":
114
+ return generateGo(component, moduleId, componentId);
115
+ case "java":
116
+ return generateJava(component, moduleId, componentId);
117
+ case "typescript":
118
+ default:
119
+ return generateTypeScript(component, moduleId, componentId);
120
+ }
121
+ }
122
+
123
+ // ── TypeScript Test Generator ──
124
+
125
+ function generateTypeScript(
126
+ component: ModuleComponent,
127
+ moduleId: string,
128
+ componentId: string,
129
+ ): string {
130
+ const lines: string[] = [
131
+ `// Generated by Guardian TDD`,
132
+ `// From: ${component.name} (${moduleId} module)`,
133
+ `// These tests will FAIL until ${component.name} is implemented.`,
134
+ ``,
135
+ `import { describe, it, expect } from "bun:test";`,
136
+ ``,
137
+ `describe("${component.name}", () => {`,
138
+ ];
139
+
140
+ // Instantiation test
141
+ lines.push(...indent([
142
+ `it("should be defined", () => {`,
143
+ ` expect(false).toBe(true);`,
144
+ ` // TODO: uncomment when ${component.name} is implemented`,
145
+ ` // const instance = new ${component.name.replace(/[^a-zA-Z0-9]/g, "")}();`,
146
+ ` // expect(instance).toBeDefined();`,
147
+ `});`,
148
+ ], 1));
149
+
150
+ // Dependency interaction tests
151
+ for (const dep of component.dependencies) {
152
+ lines.push(...indent([
153
+ `it("should interact with ${dep}", () => {`,
154
+ ` expect(false).toBe(true);`,
155
+ ` // TODO: implement dependency test for ${dep}`,
156
+ ` // const mock = { /* mock ${dep} interface */ };`,
157
+ ` // const result = new ${component.name.replace(/[^a-zA-Z0-9]/g, "")}(mock);`,
158
+ ` // expect(result).toBeDefined();`,
159
+ `});`,
160
+ ], 1));
161
+ }
162
+
163
+ lines.push(`});`);
164
+ lines.push(``);
165
+ return lines.join("\n");
166
+ }
167
+
168
+ // ── Rust Test Generator ──
169
+
170
+ function generateRust(
171
+ component: ModuleComponent,
172
+ moduleId: string,
173
+ componentId: string,
174
+ ): string {
175
+ const lines: string[] = [
176
+ `// Generated by Guardian TDD`,
177
+ `// From: ${component.name} (${moduleId} module)`,
178
+ `// These tests will FAIL until ${component.name} is implemented.`,
179
+ ``,
180
+ `#[cfg(test)]`,
181
+ `mod tests {`,
182
+ ` use super::*;`,
183
+ ``,
184
+ ` #[test]`,
185
+ ` fn test_${componentId}_is_defined() {`,
186
+ ` // TODO: uncomment when ${component.name} is implemented`,
187
+ ` // let _instance = ${toPascalCase(componentId)}::new();`,
188
+ ` assert!(false, "Test not yet implemented");`,
189
+ ` }`,
190
+ ];
191
+
192
+ for (const dep of component.dependencies) {
193
+ lines.push(...[
194
+ ``,
195
+ ` #[test]`,
196
+ ` fn test_${componentId}_interacts_with_${dep.replace(/[^a-z0-9]/gi, "_").toLowerCase()}() {`,
197
+ ` // TODO: implement dependency test for ${dep}`,
198
+ ` // let mock = Mock${dep.replace(/[^a-zA-Z0-9]/g, "")}::new();`,
199
+ ` // let result = ${toPascalCase(componentId)}::new(mock);`,
200
+ ` assert!(false, "Dependency test not yet implemented");`,
201
+ ` }`,
202
+ ]);
203
+ }
204
+
205
+ lines.push(`}`);
206
+ lines.push(``);
207
+ return lines.join("\n");
208
+ }
209
+
210
+ // ── Python Test Generator ──
211
+
212
+ function generatePython(
213
+ component: ModuleComponent,
214
+ moduleId: string,
215
+ componentId: string,
216
+ ): string {
217
+ const clsName = toPascalCase(componentId);
218
+ const lines: string[] = [
219
+ `# Generated by Guardian TDD`,
220
+ `# From: ${component.name} (${moduleId} module)`,
221
+ `# These tests will FAIL until ${component.name} is implemented.`,
222
+ ``,
223
+ `import pytest`,
224
+ ``,
225
+ ``,
226
+ `class Test${clsName}:`,
227
+ ` """Tests for ${component.name}."""`,
228
+ ``,
229
+ ` def test_${componentId}_is_defined(self):`,
230
+ ` """${component.name} should be instantiable."""`,
231
+ ` assert False, "TODO: uncomment when ${component.name} is implemented"`,
232
+ ` # instance = ${clsName}()`,
233
+ ` # assert instance is not None`,
234
+ ];
235
+
236
+ for (const dep of component.dependencies) {
237
+ lines.push(...[
238
+ ``,
239
+ ` def test_${componentId}_interacts_with_${dep.replace(/[^a-z0-9]/gi, "_").toLowerCase()}(self):`,
240
+ ` """${component.name} should interact with ${dep}."""`,
241
+ ` assert False, "TODO: implement dependency test for ${dep}"`,
242
+ ` # mock = MagicMock(spec=${dep.replace(/[^a-zA-Z0-9]/g, "")})`,
243
+ ` # instance = ${clsName}(mock)`,
244
+ ` # result = instance.execute()`,
245
+ ` # assert result is not None`,
246
+ ]);
247
+ }
248
+
249
+ lines.push(``);
250
+ return lines.join("\n");
251
+ }
252
+
253
+ // ── Go Test Generator ──
254
+
255
+ function generateGo(
256
+ component: ModuleComponent,
257
+ moduleId: string,
258
+ componentId: string,
259
+ ): string {
260
+ const lines: string[] = [
261
+ `// Generated by Guardian TDD`,
262
+ `// From: ${component.name} (${moduleId} module)`,
263
+ `// These tests will FAIL until ${component.name} is implemented.`,
264
+ ``,
265
+ `package ${moduleId.replace(/[^a-zA-Z0-9]/g, "").toLowerCase()}`,
266
+ ``,
267
+ `import "testing"`,
268
+ ``,
269
+ `func Test${toPascalCase(componentId)}_IsDefined(t *testing.T) {`,
270
+ ` // TODO: uncomment when ${component.name} is implemented`,
271
+ ` // instance := New${toPascalCase(componentId)}()`,
272
+ ` // if instance == nil {`,
273
+ ` // t.Fail()`,
274
+ ` // }`,
275
+ ` t.Fatal("Test not yet implemented")`,
276
+ `}`,
277
+ ];
278
+
279
+ for (const dep of component.dependencies) {
280
+ lines.push(...[
281
+ ``,
282
+ `func Test${toPascalCase(componentId)}_InteractsWith_${capitalize(dep.replace(/[^a-zA-Z0-9]/g, ""))}(t *testing.T) {`,
283
+ ` // TODO: implement dependency test for ${dep}`,
284
+ ` // mock := new(Mock${capitalize(dep.replace(/[^a-zA-Z0-9]/g, ""))})`,
285
+ ` // instance := New${toPascalCase(componentId)}(mock)`,
286
+ ` // result := instance.Execute()`,
287
+ ` t.Fatal("Dependency test not yet implemented")`,
288
+ `}`,
289
+ ]);
290
+ }
291
+
292
+ lines.push(``);
293
+ return lines.join("\n");
294
+ }
295
+
296
+ // ── Java Test Generator ──
297
+
298
+ function generateJava(
299
+ component: ModuleComponent,
300
+ moduleId: string,
301
+ componentId: string,
302
+ ): string {
303
+ const clsName = toPascalCase(componentId);
304
+ const lines: string[] = [
305
+ `// Generated by Guardian TDD`,
306
+ `// From: ${component.name} (${moduleId} module)`,
307
+ `// These tests will FAIL until ${component.name} is implemented.`,
308
+ ``,
309
+ `import org.junit.jupiter.api.Test;`,
310
+ `import static org.junit.jupiter.api.Assertions.*;`,
311
+ ``,
312
+ `class ${clsName}Test {`,
313
+ ``,
314
+ ` @Test`,
315
+ ` void test${clsName}_IsDefined() {`,
316
+ ` // TODO: uncomment when ${component.name} is implemented`,
317
+ ` // ${clsName} instance = new ${clsName}();`,
318
+ ` // assertNotNull(instance);`,
319
+ ` fail("Test not yet implemented");`,
320
+ ` }`,
321
+ ];
322
+
323
+ for (const dep of component.dependencies) {
324
+ lines.push(...[
325
+ ``,
326
+ ` @Test`,
327
+ ` void test${clsName}_InteractsWith_${capitalize(dep.replace(/[^a-zA-Z0-9]/g, ""))}() {`,
328
+ ` // TODO: implement dependency test for ${dep}`,
329
+ ` // ${capitalize(dep.replace(/[^a-zA-Z0-9]/g, ""))} mock = mock(${capitalize(dep.replace(/[^a-zA-Z0-9]/g, ""))}.class);`,
330
+ ` // ${clsName} instance = new ${clsName}(mock);`,
331
+ ` fail("Dependency test not yet implemented");`,
332
+ ` }`,
333
+ ]);
334
+ }
335
+
336
+ lines.push(`}`);
337
+ lines.push(``);
338
+ return lines.join("\n");
339
+ }
340
+
341
+ // ── Utility ──
342
+
343
+ function capitalize(s: string): string {
344
+ return s.charAt(0).toUpperCase() + s.slice(1);
345
+ }
346
+
347
+ function toPascalCase(s: string): string {
348
+ return s.split("-").map(capitalize).join("");
349
+ }
350
+
351
+ function indent(lines: string[], level: number): string[] {
352
+ const pad = " ".repeat(level);
353
+ return lines.map((l) => `${pad}${l}`);
354
+ }
@@ -113,5 +113,6 @@ export type EpicState = {
113
113
  issues: { id: string; title: string; status: string; remoteIssueId?: string | null }[];
114
114
  currentIssueIndex: number;
115
115
  createdAt: string;
116
+ tdd: boolean;
116
117
  };
117
118
 
@@ -24,6 +24,7 @@ import {
24
24
  parseRoadmap,
25
25
  readGroupId,
26
26
  readRepository,
27
+ readLanguage,
27
28
  readRepoTool,
28
29
  runScript,
29
30
  saveRoadmapState,
@@ -34,6 +35,10 @@ import {
34
35
  generateIssueMarkdown,
35
36
  generateProofingMarkdown,
36
37
  } from "./architect-lib/generators";
38
+ import {
39
+ generateEpicTestFiles,
40
+ isTddSupported,
41
+ } from "./architect-lib/tdd-generator";
37
42
 
38
43
  // ── Helpers ──
39
44
 
@@ -94,6 +99,7 @@ function formatEpicStatus(state: EpicState | null): string {
94
99
  `Progress: ${done}/${total} components`,
95
100
  `Issues: ${(state.issues || []).length}`,
96
101
  `Pipeline: ${state.status}`,
102
+ state.tdd ? `TDD: enabled` : `TDD: disabled`,
97
103
  ].join("\n");
98
104
  }
99
105
 
@@ -114,6 +120,7 @@ class EpicManager {
114
120
  ctx: ExtensionContext,
115
121
  name: string,
116
122
  trackingIssueId?: string,
123
+ tdd = false,
117
124
  ): Promise<EpicState> {
118
125
  const moduleFiles = discoverModules(this.cwd);
119
126
  if (moduleFiles.length === 0) {
@@ -293,6 +300,20 @@ class EpicManager {
293
300
  }
294
301
  issues.push(readinessEntry);
295
302
 
303
+ // ── TDD test generation ──
304
+ let tddTestFiles: string[] = [];
305
+ if (tdd) {
306
+ const language = readLanguage(this.cwd);
307
+ if (isTddSupported(language)) {
308
+ tddTestFiles = generateEpicTestFiles({
309
+ components: slice.nextLogicalSlice,
310
+ moduleId: slice.module,
311
+ cwd: this.cwd,
312
+ language,
313
+ });
314
+ }
315
+ }
316
+
296
317
  const state: EpicState = {
297
318
  name,
298
319
  trackingIssueId: effectiveTrackingId,
@@ -302,6 +323,7 @@ class EpicManager {
302
323
  status: "planning",
303
324
  currentIssueIndex: 0,
304
325
  createdAt: new Date().toISOString(),
326
+ tdd,
305
327
  };
306
328
 
307
329
  this.state = state;
@@ -339,7 +361,7 @@ export default function (pi: ExtensionAPI) {
339
361
  const tokens = raw ? parseArgs(raw) : [];
340
362
  if (tokens.length === 0) {
341
363
  ctx.ui.notify(
342
- "Usage: /architect [--epic Name] [--tracking-issue N] | --roadmap | --phase \"Phase 1\" | --phase-status | --phase-done <N> | --phase-module-done <N> \"Module\" | status | next-epic | abort",
364
+ "Usage: /architect [--epic Name] [--tracking-issue N] [--tdd] | --roadmap | --phase \"Phase 1\" | --phase-status | --phase-done <N> | --phase-module-done <N> \"Module\" | status | next-epic | abort",
343
365
  "info",
344
366
  );
345
367
  return;
@@ -701,19 +723,20 @@ export default function (pi: ExtensionAPI) {
701
723
 
702
724
  const epicName = findFlag(tokens, "--epic");
703
725
  const trackingIssueId = findFlag(tokens, "--tracking-issue");
726
+ const tddEnabled = tokens.includes("--tdd");
704
727
 
705
728
  if (!epicName) {
706
- ctx.ui.notify('Usage: /architect --epic "Epic Name" [--tracking-issue N]', "error");
729
+ ctx.ui.notify('Usage: /architect --epic "Epic Name" [--tracking-issue N] [--tdd]', "error");
707
730
  return;
708
731
  }
709
732
 
710
733
  try {
711
734
  if (!epicName || epicName.trim() === "") {
712
- ctx.ui.notify('Usage: /architect --epic "Epic Name"', "error");
735
+ ctx.ui.notify('Usage: /architect --epic "Epic Name" [--tracking-issue N] [--tdd]', "error");
713
736
  return;
714
737
  }
715
738
 
716
- const state = await manager.startEpic(ctx, epicName, trackingIssueId);
739
+ const state = await manager.startEpic(ctx, epicName, trackingIssueId, tddEnabled);
717
740
 
718
741
  if (!state || !state.slices || state.slices.length === 0) {
719
742
  ctx.ui.notify("Failed to discover architecture components. Check .pi/architecture/modules/.", "error");
@@ -811,6 +834,15 @@ export default function (pi: ExtensionAPI) {
811
834
  "3. Run `pipeline_run_acceptance` to validate your work",
812
835
  "4. Call `pipeline_advance` to move to the next step",
813
836
  "5. Pipeline auto-advances through: implement → validate → create-mr → merge",
837
+ tddEnabled ? [
838
+ "",
839
+ "**TDD Mode: ON**",
840
+ "- Failing test files were generated from architecture contracts before issues were created.",
841
+ "- Start each component by examining its test file in the `tests/unit/` directory.",
842
+ "- Run tests first to see them fail, then implement to make them pass (Red→Green→Refactor).",
843
+ "- Tests are living artifacts — evolve them as the component grows.",
844
+ "- Reference `.pi/skills/tdd-practice.md` for TDD guidance.",
845
+ ].join("\n") : "",
814
846
  "",
815
847
  "---",
816
848
  "",
@@ -1,5 +1,5 @@
1
1
  {
2
- "timestamp": "2026-07-11T13:19:30Z",
2
+ "timestamp": "2026-07-12T10:38:04Z",
3
3
  "mode": "all",
4
4
  "stages_run": [],
5
5
  "summary": {