@kata-sh/cli 0.1.0 → 0.1.1

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 (199) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +156 -0
  3. package/dist/app-paths.d.ts +4 -0
  4. package/dist/app-paths.js +6 -0
  5. package/dist/cli.d.ts +1 -0
  6. package/dist/cli.js +56 -0
  7. package/dist/loader.d.ts +2 -0
  8. package/dist/loader.js +95 -0
  9. package/dist/resource-loader.d.ts +18 -0
  10. package/dist/resource-loader.js +50 -0
  11. package/dist/wizard.d.ts +15 -0
  12. package/dist/wizard.js +159 -0
  13. package/package.json +50 -21
  14. package/pkg/dist/modes/interactive/theme/dark.json +85 -0
  15. package/pkg/dist/modes/interactive/theme/light.json +84 -0
  16. package/pkg/dist/modes/interactive/theme/theme-schema.json +335 -0
  17. package/pkg/dist/modes/interactive/theme/theme.d.ts +78 -0
  18. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -0
  19. package/pkg/dist/modes/interactive/theme/theme.js +949 -0
  20. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -0
  21. package/pkg/package.json +8 -0
  22. package/scripts/postinstall.js +45 -0
  23. package/src/resources/AGENTS.md +108 -0
  24. package/src/resources/KATA-WORKFLOW.md +661 -0
  25. package/src/resources/agents/researcher.md +29 -0
  26. package/src/resources/agents/scout.md +56 -0
  27. package/src/resources/agents/worker.md +31 -0
  28. package/src/resources/extensions/ask-user-questions.ts +200 -0
  29. package/src/resources/extensions/bg-shell/index.ts +2758 -0
  30. package/src/resources/extensions/browser-tools/BROWSER-TOOLS-V2-PROPOSAL.md +1277 -0
  31. package/src/resources/extensions/browser-tools/core.js +1057 -0
  32. package/src/resources/extensions/browser-tools/index.ts +4916 -0
  33. package/src/resources/extensions/browser-tools/package.json +20 -0
  34. package/src/resources/extensions/context7/index.ts +428 -0
  35. package/src/resources/extensions/context7/package.json +11 -0
  36. package/src/resources/extensions/get-secrets-from-user.ts +352 -0
  37. package/src/resources/extensions/github/formatters.ts +207 -0
  38. package/src/resources/extensions/github/gh-api.ts +537 -0
  39. package/src/resources/extensions/github/index.ts +778 -0
  40. package/src/resources/extensions/kata/activity-log.ts +88 -0
  41. package/src/resources/extensions/kata/auto.ts +2786 -0
  42. package/src/resources/extensions/kata/commands.ts +355 -0
  43. package/src/resources/extensions/kata/crash-recovery.ts +85 -0
  44. package/src/resources/extensions/kata/dashboard-overlay.ts +516 -0
  45. package/src/resources/extensions/kata/docs/preferences-reference.md +103 -0
  46. package/src/resources/extensions/kata/doctor.ts +683 -0
  47. package/src/resources/extensions/kata/files.ts +730 -0
  48. package/src/resources/extensions/kata/gitignore.ts +165 -0
  49. package/src/resources/extensions/kata/guided-flow.ts +976 -0
  50. package/src/resources/extensions/kata/index.ts +556 -0
  51. package/src/resources/extensions/kata/metrics.ts +397 -0
  52. package/src/resources/extensions/kata/observability-validator.ts +408 -0
  53. package/src/resources/extensions/kata/package.json +11 -0
  54. package/src/resources/extensions/kata/paths.ts +346 -0
  55. package/src/resources/extensions/kata/preferences.ts +695 -0
  56. package/src/resources/extensions/kata/prompt-loader.ts +50 -0
  57. package/src/resources/extensions/kata/prompts/complete-milestone.md +25 -0
  58. package/src/resources/extensions/kata/prompts/complete-slice.md +27 -0
  59. package/src/resources/extensions/kata/prompts/discuss.md +151 -0
  60. package/src/resources/extensions/kata/prompts/doctor-heal.md +29 -0
  61. package/src/resources/extensions/kata/prompts/execute-task.md +64 -0
  62. package/src/resources/extensions/kata/prompts/guided-complete-slice.md +1 -0
  63. package/src/resources/extensions/kata/prompts/guided-discuss-milestone.md +3 -0
  64. package/src/resources/extensions/kata/prompts/guided-discuss-slice.md +59 -0
  65. package/src/resources/extensions/kata/prompts/guided-execute-task.md +1 -0
  66. package/src/resources/extensions/kata/prompts/guided-plan-milestone.md +23 -0
  67. package/src/resources/extensions/kata/prompts/guided-plan-slice.md +1 -0
  68. package/src/resources/extensions/kata/prompts/guided-research-slice.md +11 -0
  69. package/src/resources/extensions/kata/prompts/guided-resume-task.md +1 -0
  70. package/src/resources/extensions/kata/prompts/plan-milestone.md +47 -0
  71. package/src/resources/extensions/kata/prompts/plan-slice.md +63 -0
  72. package/src/resources/extensions/kata/prompts/queue.md +85 -0
  73. package/src/resources/extensions/kata/prompts/reassess-roadmap.md +48 -0
  74. package/src/resources/extensions/kata/prompts/replan-slice.md +39 -0
  75. package/src/resources/extensions/kata/prompts/research-milestone.md +37 -0
  76. package/src/resources/extensions/kata/prompts/research-slice.md +28 -0
  77. package/src/resources/extensions/kata/prompts/run-uat.md +109 -0
  78. package/src/resources/extensions/kata/prompts/system.md +341 -0
  79. package/src/resources/extensions/kata/session-forensics.ts +550 -0
  80. package/src/resources/extensions/kata/skill-discovery.ts +137 -0
  81. package/src/resources/extensions/kata/state.ts +509 -0
  82. package/src/resources/extensions/kata/templates/context.md +76 -0
  83. package/src/resources/extensions/kata/templates/decisions.md +8 -0
  84. package/src/resources/extensions/kata/templates/milestone-summary.md +73 -0
  85. package/src/resources/extensions/kata/templates/plan.md +133 -0
  86. package/src/resources/extensions/kata/templates/preferences.md +15 -0
  87. package/src/resources/extensions/kata/templates/project.md +31 -0
  88. package/src/resources/extensions/kata/templates/reassessment.md +28 -0
  89. package/src/resources/extensions/kata/templates/requirements.md +81 -0
  90. package/src/resources/extensions/kata/templates/research.md +46 -0
  91. package/src/resources/extensions/kata/templates/roadmap.md +118 -0
  92. package/src/resources/extensions/kata/templates/slice-context.md +58 -0
  93. package/src/resources/extensions/kata/templates/slice-summary.md +99 -0
  94. package/src/resources/extensions/kata/templates/state.md +19 -0
  95. package/src/resources/extensions/kata/templates/task-plan.md +52 -0
  96. package/src/resources/extensions/kata/templates/task-summary.md +57 -0
  97. package/src/resources/extensions/kata/templates/uat.md +54 -0
  98. package/src/resources/extensions/kata/tests/activity-log-prune.test.ts +327 -0
  99. package/src/resources/extensions/kata/tests/auto-preflight.test.ts +97 -0
  100. package/src/resources/extensions/kata/tests/auto-supervisor.test.mjs +53 -0
  101. package/src/resources/extensions/kata/tests/complete-milestone.test.ts +317 -0
  102. package/src/resources/extensions/kata/tests/cost-projection.test.ts +160 -0
  103. package/src/resources/extensions/kata/tests/derive-state-deps.test.ts +477 -0
  104. package/src/resources/extensions/kata/tests/derive-state.test.ts +1013 -0
  105. package/src/resources/extensions/kata/tests/doctor.test.ts +718 -0
  106. package/src/resources/extensions/kata/tests/idle-recovery.test.ts +490 -0
  107. package/src/resources/extensions/kata/tests/metrics-io.test.ts +254 -0
  108. package/src/resources/extensions/kata/tests/metrics.test.ts +217 -0
  109. package/src/resources/extensions/kata/tests/must-have-parser.test.ts +309 -0
  110. package/src/resources/extensions/kata/tests/parsers.test.ts +1257 -0
  111. package/src/resources/extensions/kata/tests/plan-milestone.test.ts +185 -0
  112. package/src/resources/extensions/kata/tests/plan-quality-validator.test.ts +386 -0
  113. package/src/resources/extensions/kata/tests/reassess-prompt.test.ts +208 -0
  114. package/src/resources/extensions/kata/tests/replan-slice.test.ts +686 -0
  115. package/src/resources/extensions/kata/tests/requirements.test.ts +151 -0
  116. package/src/resources/extensions/kata/tests/resolve-ts-hooks.mjs +17 -0
  117. package/src/resources/extensions/kata/tests/resolve-ts.mjs +11 -0
  118. package/src/resources/extensions/kata/tests/run-uat.test.ts +383 -0
  119. package/src/resources/extensions/kata/tests/unit-runtime.test.ts +388 -0
  120. package/src/resources/extensions/kata/tests/workspace-index.test.ts +118 -0
  121. package/src/resources/extensions/kata/tests/worktree.test.ts +222 -0
  122. package/src/resources/extensions/kata/types.ts +159 -0
  123. package/src/resources/extensions/kata/unit-runtime.ts +163 -0
  124. package/src/resources/extensions/kata/workspace-index.ts +203 -0
  125. package/src/resources/extensions/kata/worktree.ts +182 -0
  126. package/src/resources/extensions/mac-tools/index.ts +852 -0
  127. package/src/resources/extensions/mac-tools/swift-cli/Package.swift +22 -0
  128. package/src/resources/extensions/mac-tools/swift-cli/Sources/main.swift +1318 -0
  129. package/src/resources/extensions/search-the-web/cache.ts +78 -0
  130. package/src/resources/extensions/search-the-web/format.ts +258 -0
  131. package/src/resources/extensions/search-the-web/http.ts +238 -0
  132. package/src/resources/extensions/search-the-web/index.ts +68 -0
  133. package/src/resources/extensions/search-the-web/tool-fetch-page.ts +519 -0
  134. package/src/resources/extensions/search-the-web/tool-llm-context.ts +404 -0
  135. package/src/resources/extensions/search-the-web/tool-search.ts +503 -0
  136. package/src/resources/extensions/search-the-web/url-utils.ts +91 -0
  137. package/src/resources/extensions/shared/confirm-ui.ts +126 -0
  138. package/src/resources/extensions/shared/interview-ui.ts +822 -0
  139. package/src/resources/extensions/shared/next-action-ui.ts +235 -0
  140. package/src/resources/extensions/shared/progress-widget.ts +282 -0
  141. package/src/resources/extensions/shared/thinking-widget.ts +107 -0
  142. package/src/resources/extensions/shared/ui.ts +400 -0
  143. package/src/resources/extensions/shared/wizard-ui.ts +551 -0
  144. package/src/resources/extensions/slash-commands/audit.ts +92 -0
  145. package/src/resources/extensions/slash-commands/create-extension.ts +375 -0
  146. package/src/resources/extensions/slash-commands/create-slash-command.ts +280 -0
  147. package/src/resources/extensions/slash-commands/index.ts +12 -0
  148. package/src/resources/extensions/slash-commands/kata-run.ts +34 -0
  149. package/src/resources/extensions/subagent/agents.ts +126 -0
  150. package/src/resources/extensions/subagent/index.ts +1293 -0
  151. package/src/resources/skills/debug-like-expert/SKILL.md +231 -0
  152. package/src/resources/skills/debug-like-expert/references/debugging-mindset.md +253 -0
  153. package/src/resources/skills/debug-like-expert/references/hypothesis-testing.md +373 -0
  154. package/src/resources/skills/debug-like-expert/references/investigation-techniques.md +337 -0
  155. package/src/resources/skills/debug-like-expert/references/verification-patterns.md +425 -0
  156. package/src/resources/skills/debug-like-expert/references/when-to-research.md +361 -0
  157. package/src/resources/skills/frontend-design/SKILL.md +45 -0
  158. package/src/resources/skills/swiftui/SKILL.md +208 -0
  159. package/src/resources/skills/swiftui/references/animations.md +921 -0
  160. package/src/resources/skills/swiftui/references/architecture.md +1561 -0
  161. package/src/resources/skills/swiftui/references/layout-system.md +1186 -0
  162. package/src/resources/skills/swiftui/references/navigation.md +1492 -0
  163. package/src/resources/skills/swiftui/references/networking-async.md +214 -0
  164. package/src/resources/skills/swiftui/references/performance.md +1706 -0
  165. package/src/resources/skills/swiftui/references/platform-integration.md +204 -0
  166. package/src/resources/skills/swiftui/references/state-management.md +1443 -0
  167. package/src/resources/skills/swiftui/references/swiftdata.md +297 -0
  168. package/src/resources/skills/swiftui/references/testing-debugging.md +247 -0
  169. package/src/resources/skills/swiftui/references/uikit-appkit-interop.md +218 -0
  170. package/src/resources/skills/swiftui/workflows/add-feature.md +191 -0
  171. package/src/resources/skills/swiftui/workflows/build-new-app.md +311 -0
  172. package/src/resources/skills/swiftui/workflows/debug-swiftui.md +192 -0
  173. package/src/resources/skills/swiftui/workflows/optimize-performance.md +197 -0
  174. package/src/resources/skills/swiftui/workflows/ship-app.md +203 -0
  175. package/src/resources/skills/swiftui/workflows/write-tests.md +235 -0
  176. package/dist/commands/task.d.ts +0 -9
  177. package/dist/commands/task.d.ts.map +0 -1
  178. package/dist/commands/task.js +0 -129
  179. package/dist/commands/task.js.map +0 -1
  180. package/dist/commands/task.test.d.ts +0 -2
  181. package/dist/commands/task.test.d.ts.map +0 -1
  182. package/dist/commands/task.test.js +0 -169
  183. package/dist/commands/task.test.js.map +0 -1
  184. package/dist/e2e/task-e2e.test.d.ts +0 -2
  185. package/dist/e2e/task-e2e.test.d.ts.map +0 -1
  186. package/dist/e2e/task-e2e.test.js +0 -173
  187. package/dist/e2e/task-e2e.test.js.map +0 -1
  188. package/dist/index.d.ts +0 -3
  189. package/dist/index.d.ts.map +0 -1
  190. package/dist/index.js +0 -93
  191. package/dist/index.js.map +0 -1
  192. package/dist/slug.d.ts +0 -2
  193. package/dist/slug.d.ts.map +0 -1
  194. package/dist/slug.js +0 -12
  195. package/dist/slug.js.map +0 -1
  196. package/dist/slug.test.d.ts +0 -2
  197. package/dist/slug.test.d.ts.map +0 -1
  198. package/dist/slug.test.js +0 -32
  199. package/dist/slug.test.js.map +0 -1
@@ -0,0 +1,185 @@
1
+ // Tests for inlinePriorMilestoneSummary — the cross-milestone context bridging helper.
2
+ //
3
+ // Scenarios covered:
4
+ // (A) M002 with M001-SUMMARY.md present → returns string containing "Prior Milestone Summary" and summary content
5
+ // (B) M001 (no prior milestone in dir) → returns null
6
+ // (C) M002 with no M001-SUMMARY.md written → returns null
7
+ // (D) M003 with M002 dir present but no M002-SUMMARY.md → returns null
8
+
9
+ import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
10
+ import { join, dirname } from "node:path";
11
+ import { tmpdir } from "node:os";
12
+ import { fileURLToPath } from "node:url";
13
+
14
+ import { inlinePriorMilestoneSummary } from "../files.ts";
15
+
16
+ // ─── Worktree-aware prompt loader ──────────────────────────────────────────
17
+ const __dirname = dirname(fileURLToPath(import.meta.url));
18
+
19
+ // ─── Assertion helpers ─────────────────────────────────────────────────────
20
+
21
+ let passed = 0;
22
+ let failed = 0;
23
+
24
+ function assert(condition: boolean, message: string): void {
25
+ if (condition) {
26
+ passed++;
27
+ } else {
28
+ failed++;
29
+ console.error(` FAIL: ${message}`);
30
+ }
31
+ }
32
+
33
+ function assertEq<T>(actual: T, expected: T, message: string): void {
34
+ if (JSON.stringify(actual) === JSON.stringify(expected)) {
35
+ passed++;
36
+ } else {
37
+ failed++;
38
+ console.error(
39
+ ` FAIL: ${message} — expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`,
40
+ );
41
+ }
42
+ }
43
+
44
+ // ─── Fixture helpers ───────────────────────────────────────────────────────
45
+
46
+ function createFixtureBase(): string {
47
+ const base = mkdtempSync(join(tmpdir(), "kata-plan-ms-test-"));
48
+ mkdirSync(join(base, ".kata", "milestones"), { recursive: true });
49
+ return base;
50
+ }
51
+
52
+ function writeMilestoneDir(base: string, mid: string): void {
53
+ mkdirSync(join(base, ".kata", "milestones", mid), { recursive: true });
54
+ }
55
+
56
+ function writeMilestoneSummary(
57
+ base: string,
58
+ mid: string,
59
+ content: string,
60
+ ): void {
61
+ const dir = join(base, ".kata", "milestones", mid);
62
+ mkdirSync(dir, { recursive: true });
63
+ writeFileSync(join(dir, `${mid}-SUMMARY.md`), content);
64
+ }
65
+
66
+ function cleanup(base: string): void {
67
+ rmSync(base, { recursive: true, force: true });
68
+ }
69
+
70
+ // ═══════════════════════════════════════════════════════════════════════════
71
+ // Tests
72
+ // ═══════════════════════════════════════════════════════════════════════════
73
+
74
+ async function main(): Promise<void> {
75
+ // ─── (A) M002 with M001-SUMMARY.md present ────────────────────────────────
76
+ console.log(
77
+ '\n── (A) M002 with M001-SUMMARY.md present → string containing "Prior Milestone Summary"',
78
+ );
79
+ {
80
+ const base = createFixtureBase();
81
+ try {
82
+ writeMilestoneDir(base, "M001");
83
+ writeMilestoneDir(base, "M002");
84
+ writeMilestoneSummary(
85
+ base,
86
+ "M001",
87
+ "# M001 Summary\n\nKey decisions: used TypeScript throughout.\n",
88
+ );
89
+
90
+ const result = await inlinePriorMilestoneSummary("M002", base);
91
+
92
+ assert(
93
+ result !== null,
94
+ "(A) result is not null when prior milestone has SUMMARY",
95
+ );
96
+ assert(
97
+ typeof result === "string" &&
98
+ result.includes("Prior Milestone Summary"),
99
+ '(A) result contains "Prior Milestone Summary" label',
100
+ );
101
+ assert(
102
+ typeof result === "string" &&
103
+ result.includes("Key decisions: used TypeScript throughout."),
104
+ "(A) result contains the summary file content",
105
+ );
106
+ } finally {
107
+ cleanup(base);
108
+ }
109
+ }
110
+
111
+ // ─── (B) M001 (no prior milestone in dir) ─────────────────────────────────
112
+ console.log("\n── (B) M001 — first milestone, no prior → null");
113
+ {
114
+ const base = createFixtureBase();
115
+ try {
116
+ writeMilestoneDir(base, "M001");
117
+
118
+ const result = await inlinePriorMilestoneSummary("M001", base);
119
+
120
+ assertEq(result, null, "(B) M001 with no prior milestone → null");
121
+ } finally {
122
+ cleanup(base);
123
+ }
124
+ }
125
+
126
+ // ─── (C) M002 with no M001-SUMMARY.md ────────────────────────────────────
127
+ console.log("\n── (C) M002 with M001 dir but no M001-SUMMARY.md → null");
128
+ {
129
+ const base = createFixtureBase();
130
+ try {
131
+ writeMilestoneDir(base, "M001");
132
+ writeMilestoneDir(base, "M002");
133
+ // Intentionally do NOT write M001-SUMMARY.md
134
+
135
+ const result = await inlinePriorMilestoneSummary("M002", base);
136
+
137
+ assertEq(result, null, "(C) M002 when M001 has no SUMMARY file → null");
138
+ } finally {
139
+ cleanup(base);
140
+ }
141
+ }
142
+
143
+ // ─── (D) M003 with M002 dir but no M002-SUMMARY.md ───────────────────────
144
+ console.log(
145
+ "\n── (D) M003, M002 is immediately prior but has no SUMMARY → null",
146
+ );
147
+ {
148
+ const base = createFixtureBase();
149
+ try {
150
+ writeMilestoneDir(base, "M001");
151
+ writeMilestoneDir(base, "M002");
152
+ writeMilestoneDir(base, "M003");
153
+ // M001 has a summary — but M002 (the immediately prior to M003) does NOT
154
+ writeMilestoneSummary(base, "M001", "# M001 Summary\n\nOld context.\n");
155
+ // Intentionally do NOT write M002-SUMMARY.md
156
+
157
+ const result = await inlinePriorMilestoneSummary("M003", base);
158
+
159
+ assertEq(
160
+ result,
161
+ null,
162
+ "(D) M003 when M002 (immediately prior) has no SUMMARY → null",
163
+ );
164
+ } finally {
165
+ cleanup(base);
166
+ }
167
+ }
168
+
169
+ // ═══════════════════════════════════════════════════════════════════════════
170
+ // Results
171
+ // ═══════════════════════════════════════════════════════════════════════════
172
+
173
+ console.log(`\n${"=".repeat(40)}`);
174
+ console.log(`Results: ${passed} passed, ${failed} failed`);
175
+ if (failed > 0) {
176
+ process.exit(1);
177
+ } else {
178
+ console.log("All tests passed ✓");
179
+ }
180
+ }
181
+
182
+ main().catch((error) => {
183
+ console.error(error);
184
+ process.exit(1);
185
+ });
@@ -0,0 +1,386 @@
1
+ import { validateTaskPlanContent, validateSlicePlanContent } from '../observability-validator.ts';
2
+
3
+ let passed = 0;
4
+ let failed = 0;
5
+
6
+ function assert(condition: boolean, message: string): void {
7
+ if (condition) passed++;
8
+ else {
9
+ failed++;
10
+ console.error(` FAIL: ${message}`);
11
+ }
12
+ }
13
+
14
+ function assertEq<T>(actual: T, expected: T, message: string): void {
15
+ if (JSON.stringify(actual) === JSON.stringify(expected)) passed++;
16
+ else {
17
+ failed++;
18
+ console.error(` FAIL: ${message} — expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`);
19
+ }
20
+ }
21
+
22
+ // ═══════════════════════════════════════════════════════════════════════════
23
+ // validateTaskPlanContent — empty/missing Steps section
24
+ // ═══════════════════════════════════════════════════════════════════════════
25
+
26
+ console.log('\n=== validateTaskPlanContent: empty Steps section ===');
27
+ {
28
+ const content = `# T01: Some Task
29
+
30
+ ## Description
31
+
32
+ Do something useful.
33
+
34
+ ## Steps
35
+
36
+ ## Verification
37
+
38
+ - Run the tests and confirm output.
39
+ `;
40
+
41
+ const issues = validateTaskPlanContent('T01-PLAN.md', content);
42
+ const stepsIssues = issues.filter(i => i.ruleId === 'empty_steps_section');
43
+ assert(stepsIssues.length >= 1, 'empty Steps section produces empty_steps_section issue');
44
+ if (stepsIssues.length > 0) {
45
+ assertEq(stepsIssues[0].severity, 'warning', 'empty_steps_section severity is warning');
46
+ assertEq(stepsIssues[0].scope, 'task-plan', 'empty_steps_section scope is task-plan');
47
+ }
48
+ }
49
+
50
+ console.log('\n=== validateTaskPlanContent: missing Steps section entirely ===');
51
+ {
52
+ const content = `# T01: Some Task
53
+
54
+ ## Description
55
+
56
+ Do something useful.
57
+
58
+ ## Verification
59
+
60
+ - Run the tests.
61
+ `;
62
+
63
+ const issues = validateTaskPlanContent('T01-PLAN.md', content);
64
+ const stepsIssues = issues.filter(i => i.ruleId === 'empty_steps_section');
65
+ assert(stepsIssues.length >= 1, 'missing Steps section produces empty_steps_section issue');
66
+ }
67
+
68
+ // ═══════════════════════════════════════════════════════════════════════════
69
+ // validateTaskPlanContent — placeholder-only Verification
70
+ // ═══════════════════════════════════════════════════════════════════════════
71
+
72
+ console.log('\n=== validateTaskPlanContent: placeholder-only Verification ===');
73
+ {
74
+ const content = `# T01: Some Task
75
+
76
+ ## Steps
77
+
78
+ 1. Do the thing.
79
+ 2. Do the other thing.
80
+
81
+ ## Verification
82
+
83
+ - {{placeholder verification step}}
84
+ - {{another placeholder}}
85
+ `;
86
+
87
+ const issues = validateTaskPlanContent('T01-PLAN.md', content);
88
+ const verifyIssues = issues.filter(i => i.ruleId === 'placeholder_verification');
89
+ assert(verifyIssues.length >= 1, 'placeholder-only Verification produces placeholder_verification issue');
90
+ if (verifyIssues.length > 0) {
91
+ assertEq(verifyIssues[0].severity, 'warning', 'placeholder_verification severity is warning');
92
+ assertEq(verifyIssues[0].scope, 'task-plan', 'placeholder_verification scope is task-plan');
93
+ }
94
+ }
95
+
96
+ console.log('\n=== validateTaskPlanContent: Verification with only template text ===');
97
+ {
98
+ const content = `# T01: Some Task
99
+
100
+ ## Steps
101
+
102
+ 1. Do the thing.
103
+
104
+ ## Verification
105
+
106
+ {{whatWasVerifiedAndHow — commands run, tests passed, behavior confirmed}}
107
+ `;
108
+
109
+ const issues = validateTaskPlanContent('T01-PLAN.md', content);
110
+ const verifyIssues = issues.filter(i => i.ruleId === 'placeholder_verification');
111
+ assert(verifyIssues.length >= 1, 'template-text-only Verification produces placeholder_verification issue');
112
+ }
113
+
114
+ // ═══════════════════════════════════════════════════════════════════════════
115
+ // validateSlicePlanContent — empty inline task entries
116
+ // ═══════════════════════════════════════════════════════════════════════════
117
+
118
+ console.log('\n=== validateSlicePlanContent: empty inline task entries ===');
119
+ {
120
+ const content = `# S01: Some Slice
121
+
122
+ **Goal:** Build the thing.
123
+ **Demo:** It works.
124
+
125
+ ## Tasks
126
+
127
+ - [ ] **T01: First Task** \`est:20m\`
128
+
129
+ - [ ] **T02: Second Task** \`est:15m\`
130
+
131
+ ## Verification
132
+
133
+ - Run the tests.
134
+ `;
135
+
136
+ const issues = validateSlicePlanContent('S01-PLAN.md', content);
137
+ const emptyTaskIssues = issues.filter(i => i.ruleId === 'empty_task_entry');
138
+ assert(emptyTaskIssues.length >= 1, 'task entries with no description produce empty_task_entry issue');
139
+ if (emptyTaskIssues.length > 0) {
140
+ assertEq(emptyTaskIssues[0].severity, 'warning', 'empty_task_entry severity is warning');
141
+ assertEq(emptyTaskIssues[0].scope, 'slice-plan', 'empty_task_entry scope is slice-plan');
142
+ }
143
+ }
144
+
145
+ console.log('\n=== validateSlicePlanContent: task entries with content are fine ===');
146
+ {
147
+ const content = `# S01: Some Slice
148
+
149
+ **Goal:** Build the thing.
150
+ **Demo:** It works.
151
+
152
+ ## Tasks
153
+
154
+ - [ ] **T01: First Task** \`est:20m\`
155
+ - Why: Because it matters.
156
+ - Files: \`src/index.ts\`
157
+ - Do: Implement the feature.
158
+
159
+ - [ ] **T02: Second Task** \`est:15m\`
160
+ - Why: Also important.
161
+ - Do: Add tests.
162
+
163
+ ## Verification
164
+
165
+ - Run the tests.
166
+ `;
167
+
168
+ const issues = validateSlicePlanContent('S01-PLAN.md', content);
169
+ const emptyTaskIssues = issues.filter(i => i.ruleId === 'empty_task_entry');
170
+ assertEq(emptyTaskIssues.length, 0, 'task entries with description content produce no empty_task_entry issues');
171
+ }
172
+
173
+ // ═══════════════════════════════════════════════════════════════════════════
174
+ // validateTaskPlanContent — scope_estimate over threshold
175
+ // ═══════════════════════════════════════════════════════════════════════════
176
+
177
+ console.log('\n=== validateTaskPlanContent: scope_estimate over threshold ===');
178
+ {
179
+ const content = `---
180
+ estimated_steps: 12
181
+ estimated_files: 15
182
+ ---
183
+
184
+ # T01: Big Task
185
+
186
+ ## Steps
187
+
188
+ 1. Step one.
189
+ 2. Step two.
190
+ 3. Step three.
191
+
192
+ ## Verification
193
+
194
+ - Check it works.
195
+ `;
196
+
197
+ const issues = validateTaskPlanContent('T01-PLAN.md', content);
198
+ const stepsOverIssues = issues.filter(i => i.ruleId === 'scope_estimate_steps_high');
199
+ const filesOverIssues = issues.filter(i => i.ruleId === 'scope_estimate_files_high');
200
+ assert(stepsOverIssues.length >= 1, 'estimated_steps=12 (>=10) produces scope_estimate_steps_high issue');
201
+ assert(filesOverIssues.length >= 1, 'estimated_files=15 (>=12) produces scope_estimate_files_high issue');
202
+ if (stepsOverIssues.length > 0) {
203
+ assertEq(stepsOverIssues[0].severity, 'warning', 'scope_estimate_steps_high severity is warning');
204
+ assertEq(stepsOverIssues[0].scope, 'task-plan', 'scope_estimate_steps_high scope is task-plan');
205
+ }
206
+ if (filesOverIssues.length > 0) {
207
+ assertEq(filesOverIssues[0].severity, 'warning', 'scope_estimate_files_high severity is warning');
208
+ }
209
+ }
210
+
211
+ // ═══════════════════════════════════════════════════════════════════════════
212
+ // validateTaskPlanContent — scope_estimate within limits
213
+ // ═══════════════════════════════════════════════════════════════════════════
214
+
215
+ console.log('\n=== validateTaskPlanContent: scope_estimate within limits ===');
216
+ {
217
+ const content = `---
218
+ estimated_steps: 4
219
+ estimated_files: 6
220
+ ---
221
+
222
+ # T01: Small Task
223
+
224
+ ## Steps
225
+
226
+ 1. Do the thing.
227
+
228
+ ## Verification
229
+
230
+ - Verify it works.
231
+ `;
232
+
233
+ const issues = validateTaskPlanContent('T01-PLAN.md', content);
234
+ const scopeIssues = issues.filter(i =>
235
+ i.ruleId === 'scope_estimate_steps_high' || i.ruleId === 'scope_estimate_files_high'
236
+ );
237
+ assertEq(scopeIssues.length, 0, 'scope_estimate within limits produces no scope issues');
238
+ }
239
+
240
+ // ═══════════════════════════════════════════════════════════════════════════
241
+ // validateTaskPlanContent — missing scope_estimate (no warning)
242
+ // ═══════════════════════════════════════════════════════════════════════════
243
+
244
+ console.log('\n=== validateTaskPlanContent: missing scope_estimate ===');
245
+ {
246
+ const content = `# T01: No Frontmatter Task
247
+
248
+ ## Steps
249
+
250
+ 1. Do the thing.
251
+
252
+ ## Verification
253
+
254
+ - Verify it works.
255
+ `;
256
+
257
+ const issues = validateTaskPlanContent('T01-PLAN.md', content);
258
+ const scopeIssues = issues.filter(i =>
259
+ i.ruleId === 'scope_estimate_steps_high' || i.ruleId === 'scope_estimate_files_high'
260
+ );
261
+ assertEq(scopeIssues.length, 0, 'missing scope_estimate produces no scope issues');
262
+ }
263
+
264
+ console.log('\n=== validateTaskPlanContent: frontmatter without scope keys ===');
265
+ {
266
+ const content = `---
267
+ id: T01
268
+ parent: S01
269
+ ---
270
+
271
+ # T01: Task With Other Frontmatter
272
+
273
+ ## Steps
274
+
275
+ 1. Do the thing.
276
+
277
+ ## Verification
278
+
279
+ - Verify it works.
280
+ `;
281
+
282
+ const issues = validateTaskPlanContent('T01-PLAN.md', content);
283
+ const scopeIssues = issues.filter(i =>
284
+ i.ruleId === 'scope_estimate_steps_high' || i.ruleId === 'scope_estimate_files_high'
285
+ );
286
+ assertEq(scopeIssues.length, 0, 'frontmatter without scope keys produces no scope issues');
287
+ }
288
+
289
+ // ═══════════════════════════════════════════════════════════════════════════
290
+ // Clean plans — no false positives
291
+ // ═══════════════════════════════════════════════════════════════════════════
292
+
293
+ console.log('\n=== Clean task plan: no plan-quality issues ===');
294
+ {
295
+ const content = `---
296
+ estimated_steps: 5
297
+ estimated_files: 3
298
+ ---
299
+
300
+ # T01: Well-Formed Task
301
+
302
+ ## Description
303
+
304
+ A real task with real content.
305
+
306
+ ## Steps
307
+
308
+ 1. Read the input files.
309
+ 2. Parse the configuration.
310
+ 3. Transform the data.
311
+ 4. Write the output.
312
+ 5. Verify the results.
313
+
314
+ ## Must-Haves
315
+
316
+ - [ ] Output file is valid JSON
317
+ - [ ] All input records are processed
318
+
319
+ ## Verification
320
+
321
+ - Run \`node --test tests/transform.test.ts\` — all assertions pass
322
+ - Manually inspect output.json for correct structure
323
+
324
+ ## Observability Impact
325
+
326
+ - Signals added/changed: structured error log on parse failure
327
+ - How a future agent inspects this: check stderr for JSON parse errors
328
+ - Failure state exposed: exit code 1 + error message on invalid input
329
+ `;
330
+
331
+ const issues = validateTaskPlanContent('T01-PLAN.md', content);
332
+ const planQualityIssues = issues.filter(i =>
333
+ i.ruleId === 'empty_steps_section' ||
334
+ i.ruleId === 'placeholder_verification' ||
335
+ i.ruleId === 'scope_estimate_steps_high' ||
336
+ i.ruleId === 'scope_estimate_files_high'
337
+ );
338
+ assertEq(planQualityIssues.length, 0, 'clean task plan produces no plan-quality issues');
339
+ }
340
+
341
+ console.log('\n=== Clean slice plan: no plan-quality issues ===');
342
+ {
343
+ const content = `# S01: Well-Formed Slice
344
+
345
+ **Goal:** Build a complete feature.
346
+ **Demo:** Run the test suite and see all green.
347
+
348
+ ## Tasks
349
+
350
+ - [ ] **T01: Create tests** \`est:20m\`
351
+ - Why: Tests define the contract before implementation.
352
+ - Files: \`tests/feature.test.ts\`
353
+ - Do: Write comprehensive test assertions.
354
+ - Verify: Test file runs without syntax errors.
355
+
356
+ - [ ] **T02: Implement feature** \`est:30m\`
357
+ - Why: Core implementation.
358
+ - Files: \`src/feature.ts\`
359
+ - Do: Build the feature to make tests pass.
360
+ - Verify: All tests pass.
361
+
362
+ ## Verification
363
+
364
+ - \`node --test tests/feature.test.ts\` — all assertions pass
365
+ - Check error output for diagnostic messages
366
+
367
+ ## Observability / Diagnostics
368
+
369
+ - Runtime signals: structured error objects with error codes
370
+ - Inspection surfaces: test output shows pass/fail counts
371
+ - Failure visibility: exit code 1 on failure with descriptive message
372
+ - Redaction constraints: none
373
+ `;
374
+
375
+ const issues = validateSlicePlanContent('S01-PLAN.md', content);
376
+ const planQualityIssues = issues.filter(i => i.ruleId === 'empty_task_entry');
377
+ assertEq(planQualityIssues.length, 0, 'clean slice plan produces no empty_task_entry issues');
378
+ }
379
+
380
+ // ═══════════════════════════════════════════════════════════════════════════
381
+ // Results
382
+ // ═══════════════════════════════════════════════════════════════════════════
383
+
384
+ console.log(`\nResults: ${passed} passed, ${failed} failed`);
385
+ if (failed > 0) process.exit(1);
386
+ console.log('All tests passed ✓');