@kata-sh/cli 0.1.0 → 0.1.2

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,490 @@
1
+ import {
2
+ mkdtempSync,
3
+ mkdirSync,
4
+ readFileSync,
5
+ rmSync,
6
+ writeFileSync,
7
+ existsSync,
8
+ } from "node:fs";
9
+ import { join } from "node:path";
10
+ import { tmpdir } from "node:os";
11
+ import {
12
+ resolveExpectedArtifactPath,
13
+ writeBlockerPlaceholder,
14
+ skipExecuteTask,
15
+ } from "../auto.ts";
16
+
17
+ let passed = 0;
18
+ let failed = 0;
19
+
20
+ function assert(condition: boolean, message: string): void {
21
+ if (condition) passed++;
22
+ else {
23
+ failed++;
24
+ console.error(` FAIL: ${message}`);
25
+ }
26
+ }
27
+
28
+ function assertEq<T>(actual: T, expected: T, message: string): void {
29
+ if (JSON.stringify(actual) === JSON.stringify(expected)) passed++;
30
+ else {
31
+ failed++;
32
+ console.error(
33
+ ` FAIL: ${message} — expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`,
34
+ );
35
+ }
36
+ }
37
+
38
+ function createFixtureBase(): string {
39
+ const base = mkdtempSync(join(tmpdir(), "kata-idle-recovery-test-"));
40
+ mkdirSync(
41
+ join(base, ".kata", "milestones", "M001", "slices", "S01", "tasks"),
42
+ { recursive: true },
43
+ );
44
+ return base;
45
+ }
46
+
47
+ function cleanup(base: string): void {
48
+ rmSync(base, { recursive: true, force: true });
49
+ }
50
+
51
+ // ═══ resolveExpectedArtifactPath ═════════════════════════════════════════════
52
+
53
+ {
54
+ console.log("\n=== resolveExpectedArtifactPath: research-milestone ===");
55
+ const base = createFixtureBase();
56
+ try {
57
+ const result = resolveExpectedArtifactPath(
58
+ "research-milestone",
59
+ "M001",
60
+ base,
61
+ );
62
+ assert(result !== null, "should resolve a path");
63
+ assert(
64
+ result!.endsWith("M001-RESEARCH.md"),
65
+ `path should end with M001-RESEARCH.md, got ${result}`,
66
+ );
67
+ } finally {
68
+ cleanup(base);
69
+ }
70
+ }
71
+
72
+ {
73
+ console.log("\n=== resolveExpectedArtifactPath: plan-milestone ===");
74
+ const base = createFixtureBase();
75
+ try {
76
+ const result = resolveExpectedArtifactPath("plan-milestone", "M001", base);
77
+ assert(result !== null, "should resolve a path");
78
+ assert(
79
+ result!.endsWith("M001-ROADMAP.md"),
80
+ `path should end with M001-ROADMAP.md, got ${result}`,
81
+ );
82
+ } finally {
83
+ cleanup(base);
84
+ }
85
+ }
86
+
87
+ {
88
+ console.log("\n=== resolveExpectedArtifactPath: research-slice ===");
89
+ const base = createFixtureBase();
90
+ try {
91
+ const result = resolveExpectedArtifactPath(
92
+ "research-slice",
93
+ "M001/S01",
94
+ base,
95
+ );
96
+ assert(result !== null, "should resolve a path");
97
+ assert(
98
+ result!.endsWith("S01-RESEARCH.md"),
99
+ `path should end with S01-RESEARCH.md, got ${result}`,
100
+ );
101
+ } finally {
102
+ cleanup(base);
103
+ }
104
+ }
105
+
106
+ {
107
+ console.log("\n=== resolveExpectedArtifactPath: plan-slice ===");
108
+ const base = createFixtureBase();
109
+ try {
110
+ const result = resolveExpectedArtifactPath("plan-slice", "M001/S01", base);
111
+ assert(result !== null, "should resolve a path");
112
+ assert(
113
+ result!.endsWith("S01-PLAN.md"),
114
+ `path should end with S01-PLAN.md, got ${result}`,
115
+ );
116
+ } finally {
117
+ cleanup(base);
118
+ }
119
+ }
120
+
121
+ {
122
+ console.log("\n=== resolveExpectedArtifactPath: complete-milestone ===");
123
+ const base = createFixtureBase();
124
+ try {
125
+ const result = resolveExpectedArtifactPath(
126
+ "complete-milestone",
127
+ "M001",
128
+ base,
129
+ );
130
+ assert(result !== null, "should resolve a path");
131
+ assert(
132
+ result!.endsWith("M001-SUMMARY.md"),
133
+ `path should end with M001-SUMMARY.md, got ${result}`,
134
+ );
135
+ } finally {
136
+ cleanup(base);
137
+ }
138
+ }
139
+
140
+ {
141
+ console.log(
142
+ "\n=== resolveExpectedArtifactPath: unknown unit type → null ===",
143
+ );
144
+ const base = createFixtureBase();
145
+ try {
146
+ const result = resolveExpectedArtifactPath(
147
+ "unknown-type",
148
+ "M001/S01",
149
+ base,
150
+ );
151
+ assertEq(result, null, "unknown type returns null");
152
+ } finally {
153
+ cleanup(base);
154
+ }
155
+ }
156
+
157
+ // ═══ writeBlockerPlaceholder ═════════════════════════════════════════════════
158
+
159
+ {
160
+ console.log(
161
+ "\n=== writeBlockerPlaceholder: writes file for research-slice ===",
162
+ );
163
+ const base = createFixtureBase();
164
+ try {
165
+ const result = writeBlockerPlaceholder(
166
+ "research-slice",
167
+ "M001/S01",
168
+ base,
169
+ "idle recovery exhausted 2 attempts",
170
+ );
171
+ assert(result !== null, "should return relative path");
172
+ const absPath = resolveExpectedArtifactPath(
173
+ "research-slice",
174
+ "M001/S01",
175
+ base,
176
+ )!;
177
+ assert(existsSync(absPath), "file should exist on disk");
178
+ const content = readFileSync(absPath, "utf-8");
179
+ assert(content.includes("BLOCKER"), "should contain BLOCKER heading");
180
+ assert(
181
+ content.includes("idle recovery exhausted 2 attempts"),
182
+ "should contain the reason",
183
+ );
184
+ assert(content.includes("research-slice"), "should mention the unit type");
185
+ assert(content.includes("M001/S01"), "should mention the unit ID");
186
+ } finally {
187
+ cleanup(base);
188
+ }
189
+ }
190
+
191
+ {
192
+ console.log(
193
+ "\n=== writeBlockerPlaceholder: creates directory if missing ===",
194
+ );
195
+ const base = mkdtempSync(join(tmpdir(), "kata-idle-recovery-test-"));
196
+ try {
197
+ // Only create milestone dir, not slice dir
198
+ mkdirSync(join(base, ".kata", "milestones", "M001"), { recursive: true });
199
+ // resolveSlicePath needs the slice dir to exist to resolve, so this should return null
200
+ const result = writeBlockerPlaceholder(
201
+ "research-slice",
202
+ "M001/S01",
203
+ base,
204
+ "test reason",
205
+ );
206
+ // Since the slice dir doesn't exist, resolveExpectedArtifactPath returns null
207
+ assertEq(
208
+ result,
209
+ null,
210
+ "returns null when directory structure doesn't exist",
211
+ );
212
+ } finally {
213
+ cleanup(base);
214
+ }
215
+ }
216
+
217
+ {
218
+ console.log(
219
+ "\n=== writeBlockerPlaceholder: writes file for research-milestone ===",
220
+ );
221
+ const base = createFixtureBase();
222
+ try {
223
+ const result = writeBlockerPlaceholder(
224
+ "research-milestone",
225
+ "M001",
226
+ base,
227
+ "hard timeout",
228
+ );
229
+ assert(result !== null, "should return relative path");
230
+ const absPath = resolveExpectedArtifactPath(
231
+ "research-milestone",
232
+ "M001",
233
+ base,
234
+ )!;
235
+ assert(existsSync(absPath), "file should exist on disk");
236
+ const content = readFileSync(absPath, "utf-8");
237
+ assert(content.includes("BLOCKER"), "should contain BLOCKER heading");
238
+ assert(content.includes("hard timeout"), "should contain the reason");
239
+ } finally {
240
+ cleanup(base);
241
+ }
242
+ }
243
+
244
+ {
245
+ console.log("\n=== writeBlockerPlaceholder: unknown type → null ===");
246
+ const base = createFixtureBase();
247
+ try {
248
+ const result = writeBlockerPlaceholder(
249
+ "execute-task",
250
+ "M001/S01/T01",
251
+ base,
252
+ "test",
253
+ );
254
+ assertEq(
255
+ result,
256
+ null,
257
+ "execute-task has no single artifact path, returns null",
258
+ );
259
+ } finally {
260
+ cleanup(base);
261
+ }
262
+ }
263
+
264
+ // ═══ skipExecuteTask ═════════════════════════════════════════════════════════
265
+
266
+ {
267
+ console.log(
268
+ "\n=== skipExecuteTask: writes summary and checks plan checkbox ===",
269
+ );
270
+ const base = createFixtureBase();
271
+ try {
272
+ const planPath = join(
273
+ base,
274
+ ".kata",
275
+ "milestones",
276
+ "M001",
277
+ "slices",
278
+ "S01",
279
+ "S01-PLAN.md",
280
+ );
281
+ writeFileSync(
282
+ planPath,
283
+ [
284
+ "# S01: Test Slice",
285
+ "",
286
+ "## Tasks",
287
+ "",
288
+ "- [ ] **T01: First task** `est:10m`",
289
+ " Do the first thing.",
290
+ "- [ ] **T02: Second task** `est:15m`",
291
+ " Do the second thing.",
292
+ ].join("\n"),
293
+ "utf-8",
294
+ );
295
+
296
+ const result = skipExecuteTask(
297
+ base,
298
+ "M001",
299
+ "S01",
300
+ "T01",
301
+ { summaryExists: false, taskChecked: false },
302
+ "idle",
303
+ 2,
304
+ );
305
+
306
+ assert(result === true, "should return true");
307
+
308
+ // Check summary was written
309
+ const summaryPath = join(
310
+ base,
311
+ ".kata",
312
+ "milestones",
313
+ "M001",
314
+ "slices",
315
+ "S01",
316
+ "tasks",
317
+ "T01-SUMMARY.md",
318
+ );
319
+ assert(existsSync(summaryPath), "task summary should exist");
320
+ const summaryContent = readFileSync(summaryPath, "utf-8");
321
+ assert(
322
+ summaryContent.includes("BLOCKER"),
323
+ "summary should contain BLOCKER",
324
+ );
325
+ assert(summaryContent.includes("T01"), "summary should mention task ID");
326
+
327
+ // Check plan checkbox was marked
328
+ const planContent = readFileSync(planPath, "utf-8");
329
+ assert(planContent.includes("- [x] **T01:"), "T01 should be checked");
330
+ assert(planContent.includes("- [ ] **T02:"), "T02 should remain unchecked");
331
+ } finally {
332
+ cleanup(base);
333
+ }
334
+ }
335
+
336
+ {
337
+ console.log("\n=== skipExecuteTask: skips summary if already exists ===");
338
+ const base = createFixtureBase();
339
+ try {
340
+ const planPath = join(
341
+ base,
342
+ ".kata",
343
+ "milestones",
344
+ "M001",
345
+ "slices",
346
+ "S01",
347
+ "S01-PLAN.md",
348
+ );
349
+ writeFileSync(planPath, "- [ ] **T01: Task** `est:10m`\n", "utf-8");
350
+
351
+ // Pre-write a summary
352
+ const summaryPath = join(
353
+ base,
354
+ ".kata",
355
+ "milestones",
356
+ "M001",
357
+ "slices",
358
+ "S01",
359
+ "tasks",
360
+ "T01-SUMMARY.md",
361
+ );
362
+ writeFileSync(summaryPath, "# Real summary\nActual work done.", "utf-8");
363
+
364
+ const result = skipExecuteTask(
365
+ base,
366
+ "M001",
367
+ "S01",
368
+ "T01",
369
+ { summaryExists: true, taskChecked: false },
370
+ "idle",
371
+ 2,
372
+ );
373
+
374
+ assert(result === true, "should return true");
375
+
376
+ // Summary should be untouched (not overwritten with blocker)
377
+ const content = readFileSync(summaryPath, "utf-8");
378
+ assert(
379
+ content.includes("Real summary"),
380
+ "original summary should be preserved",
381
+ );
382
+ assert(!content.includes("BLOCKER"), "should not contain BLOCKER");
383
+
384
+ // Plan checkbox should still be marked
385
+ const planContent = readFileSync(planPath, "utf-8");
386
+ assert(planContent.includes("- [x] **T01:"), "T01 should be checked");
387
+ } finally {
388
+ cleanup(base);
389
+ }
390
+ }
391
+
392
+ {
393
+ console.log("\n=== skipExecuteTask: skips checkbox if already checked ===");
394
+ const base = createFixtureBase();
395
+ try {
396
+ const planPath = join(
397
+ base,
398
+ ".kata",
399
+ "milestones",
400
+ "M001",
401
+ "slices",
402
+ "S01",
403
+ "S01-PLAN.md",
404
+ );
405
+ writeFileSync(planPath, "- [x] **T01: Task** `est:10m`\n", "utf-8");
406
+
407
+ const result = skipExecuteTask(
408
+ base,
409
+ "M001",
410
+ "S01",
411
+ "T01",
412
+ { summaryExists: false, taskChecked: true },
413
+ "idle",
414
+ 2,
415
+ );
416
+
417
+ assert(result === true, "should return true");
418
+
419
+ // Summary should be written (since summaryExists was false)
420
+ const summaryPath = join(
421
+ base,
422
+ ".kata",
423
+ "milestones",
424
+ "M001",
425
+ "slices",
426
+ "S01",
427
+ "tasks",
428
+ "T01-SUMMARY.md",
429
+ );
430
+ assert(existsSync(summaryPath), "task summary should exist");
431
+
432
+ // Plan checkbox should be untouched
433
+ const planContent = readFileSync(planPath, "utf-8");
434
+ assert(planContent.includes("- [x] **T01:"), "T01 should remain checked");
435
+ } finally {
436
+ cleanup(base);
437
+ }
438
+ }
439
+
440
+ {
441
+ console.log(
442
+ "\n=== skipExecuteTask: handles special regex chars in task ID ===",
443
+ );
444
+ const base = createFixtureBase();
445
+ try {
446
+ const planPath = join(
447
+ base,
448
+ ".kata",
449
+ "milestones",
450
+ "M001",
451
+ "slices",
452
+ "S01",
453
+ "S01-PLAN.md",
454
+ );
455
+ writeFileSync(planPath, "- [ ] **T01.1: Sub-task** `est:10m`\n", "utf-8");
456
+
457
+ const result = skipExecuteTask(
458
+ base,
459
+ "M001",
460
+ "S01",
461
+ "T01.1",
462
+ { summaryExists: false, taskChecked: false },
463
+ "idle",
464
+ 2,
465
+ );
466
+
467
+ assert(result === true, "should return true");
468
+
469
+ const planContent = readFileSync(planPath, "utf-8");
470
+ assert(
471
+ planContent.includes("- [x] **T01.1:"),
472
+ "T01.1 should be checked (regex chars escaped)",
473
+ );
474
+ } finally {
475
+ cleanup(base);
476
+ }
477
+ }
478
+
479
+ // ═════════════════════════════════════════════════════════════════════════════
480
+ // Results
481
+ // ═════════════════════════════════════════════════════════════════════════════
482
+
483
+ console.log(`\n${"=".repeat(40)}`);
484
+ if (failed > 0) {
485
+ console.log(`Results: ${passed} passed, ${failed} failed`);
486
+ process.exit(1);
487
+ } else {
488
+ console.log(`Results: ${passed} passed, ${failed} failed`);
489
+ console.log("All tests passed ✓");
490
+ }