@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,222 @@
1
+ import {
2
+ mkdtempSync,
3
+ mkdirSync,
4
+ readFileSync,
5
+ rmSync,
6
+ writeFileSync,
7
+ } from "node:fs";
8
+ import { join } from "node:path";
9
+ import { tmpdir } from "node:os";
10
+ import { execSync } from "node:child_process";
11
+
12
+ import {
13
+ autoCommitCurrentBranch,
14
+ ensureSliceBranch,
15
+ getActiveSliceBranch,
16
+ getCurrentBranch,
17
+ getSliceBranchName,
18
+ mergeSliceToMain,
19
+ switchToMain,
20
+ } from "../worktree.ts";
21
+ import { deriveState } from "../state.ts";
22
+ import { indexWorkspace } from "../workspace-index.ts";
23
+
24
+ let passed = 0;
25
+ let failed = 0;
26
+
27
+ function assert(condition: boolean, message: string): void {
28
+ if (condition) passed++;
29
+ else {
30
+ failed++;
31
+ console.error(` FAIL: ${message}`);
32
+ }
33
+ }
34
+
35
+ function assertEq<T>(actual: T, expected: T, message: string): void {
36
+ if (JSON.stringify(actual) === JSON.stringify(expected)) passed++;
37
+ else {
38
+ failed++;
39
+ console.error(
40
+ ` FAIL: ${message} — expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`,
41
+ );
42
+ }
43
+ }
44
+
45
+ function run(command: string, cwd: string): string {
46
+ return execSync(command, {
47
+ cwd,
48
+ stdio: ["ignore", "pipe", "pipe"],
49
+ encoding: "utf-8",
50
+ }).trim();
51
+ }
52
+
53
+ const base = mkdtempSync(join(tmpdir(), "kata-branch-test-"));
54
+ run("git init -b main", base);
55
+ run("git config user.name 'Pi Test'", base);
56
+ run("git config user.email 'pi@example.com'", base);
57
+ mkdirSync(join(base, ".kata", "milestones", "M001", "slices", "S01", "tasks"), {
58
+ recursive: true,
59
+ });
60
+ writeFileSync(join(base, "README.md"), "hello\n", "utf-8");
61
+ writeFileSync(
62
+ join(base, ".kata", "milestones", "M001", "M001-ROADMAP.md"),
63
+ `# M001: Demo\n\n## Slices\n- [ ] **S01: Slice One** \`risk:low\` \`depends:[]\`\n > After this: demo works\n`,
64
+ "utf-8",
65
+ );
66
+ writeFileSync(
67
+ join(base, ".kata", "milestones", "M001", "slices", "S01", "S01-PLAN.md"),
68
+ `# S01: Slice One\n\n**Goal:** Demo\n**Demo:** Demo\n\n## Must-Haves\n- done\n\n## Tasks\n- [ ] **T01: Implement** \`est:10m\`\n do it\n`,
69
+ "utf-8",
70
+ );
71
+ run("git add .", base);
72
+ run("git commit -m 'chore: init'", base);
73
+
74
+ async function main(): Promise<void> {
75
+ console.log("\n=== ensureSliceBranch ===");
76
+ const created = ensureSliceBranch(base, "M001", "S01");
77
+ assert(created, "branch created on first ensure");
78
+ assertEq(getCurrentBranch(base), "kata/M001/S01", "switched to slice branch");
79
+
80
+ console.log("\n=== idempotent ensure ===");
81
+ const secondCreate = ensureSliceBranch(base, "M001", "S01");
82
+ assertEq(secondCreate, false, "branch not recreated on second ensure");
83
+ assertEq(getCurrentBranch(base), "kata/M001/S01", "still on slice branch");
84
+
85
+ console.log("\n=== getActiveSliceBranch ===");
86
+ assertEq(
87
+ getActiveSliceBranch(base),
88
+ "kata/M001/S01",
89
+ "getActiveSliceBranch returns current slice branch",
90
+ );
91
+
92
+ console.log("\n=== state surfaces active branch ===");
93
+ const state = await deriveState(base);
94
+ assertEq(state.activeBranch, "kata/M001/S01", "state exposes active branch");
95
+
96
+ console.log("\n=== workspace index surfaces branch ===");
97
+ const index = await indexWorkspace(base);
98
+ const slice = index.milestones[0]?.slices[0];
99
+ assertEq(slice?.branch, "kata/M001/S01", "workspace index exposes branch");
100
+
101
+ console.log("\n=== autoCommitCurrentBranch ===");
102
+ // Clean — should return null
103
+ const cleanResult = autoCommitCurrentBranch(
104
+ base,
105
+ "execute-task",
106
+ "M001/S01/T01",
107
+ );
108
+ assertEq(cleanResult, null, "returns null for clean repo");
109
+
110
+ // Make dirty
111
+ writeFileSync(join(base, "dirty.txt"), "uncommitted\n", "utf-8");
112
+ const dirtyResult = autoCommitCurrentBranch(
113
+ base,
114
+ "execute-task",
115
+ "M001/S01/T01",
116
+ );
117
+ assert(dirtyResult !== null, "returns commit message for dirty repo");
118
+ assert(
119
+ dirtyResult!.includes("M001/S01/T01"),
120
+ "commit message includes unit id",
121
+ );
122
+ assertEq(
123
+ run("git status --short", base),
124
+ "",
125
+ "repo is clean after auto-commit",
126
+ );
127
+
128
+ console.log("\n=== switchToMain ===");
129
+ switchToMain(base);
130
+ assertEq(getCurrentBranch(base), "main", "switched back to main");
131
+ assertEq(
132
+ getActiveSliceBranch(base),
133
+ null,
134
+ "getActiveSliceBranch returns null on main",
135
+ );
136
+
137
+ console.log("\n=== mergeSliceToMain ===");
138
+ // Switch back to slice, make a change, switch to main, merge
139
+ ensureSliceBranch(base, "M001", "S01");
140
+ writeFileSync(join(base, "README.md"), "hello from slice\n", "utf-8");
141
+ run("git add README.md", base);
142
+ run("git commit -m 'feat: slice change'", base);
143
+ switchToMain(base);
144
+
145
+ const merge = mergeSliceToMain(base, "M001", "S01", "Slice One");
146
+ assertEq(merge.branch, "kata/M001/S01", "merge reports branch");
147
+ assertEq(getCurrentBranch(base), "main", "still on main after merge");
148
+ assert(
149
+ readFileSync(join(base, "README.md"), "utf-8").includes("slice"),
150
+ "main got squashed content",
151
+ );
152
+ assert(merge.deletedBranch, "branch was deleted");
153
+
154
+ // Verify branch is actually gone
155
+ const branches = run("git branch", base);
156
+ assert(!branches.includes("kata/M001/S01"), "slice branch no longer exists");
157
+
158
+ console.log("\n=== switchToMain auto-commits dirty files ===");
159
+ // Set up S02
160
+ mkdirSync(
161
+ join(base, ".kata", "milestones", "M001", "slices", "S02", "tasks"),
162
+ { recursive: true },
163
+ );
164
+ writeFileSync(
165
+ join(base, ".kata", "milestones", "M001", "M001-ROADMAP.md"),
166
+ [
167
+ "# M001: Demo",
168
+ "",
169
+ "## Slices",
170
+ "- [x] **S01: Slice One** `risk:low` `depends:[]`",
171
+ " > Done",
172
+ "- [ ] **S02: Slice Two** `risk:low` `depends:[]`",
173
+ " > Demo 2",
174
+ ].join("\n") + "\n",
175
+ "utf-8",
176
+ );
177
+ run("git add .", base);
178
+ run("git commit -m 'chore: add S02'", base);
179
+
180
+ ensureSliceBranch(base, "M001", "S02");
181
+ writeFileSync(join(base, "feature.txt"), "new feature\n", "utf-8");
182
+ // Don't commit — switchToMain should auto-commit
183
+ switchToMain(base);
184
+ assertEq(
185
+ getCurrentBranch(base),
186
+ "main",
187
+ "switched to main despite dirty files",
188
+ );
189
+
190
+ // Verify the commit happened on the slice branch
191
+ ensureSliceBranch(base, "M001", "S02");
192
+ assert(
193
+ readFileSync(join(base, "feature.txt"), "utf-8").includes("new feature"),
194
+ "dirty file was committed on slice branch",
195
+ );
196
+ switchToMain(base);
197
+
198
+ // Now merge S02
199
+ const mergeS02 = mergeSliceToMain(base, "M001", "S02", "Slice Two");
200
+ assert(
201
+ readFileSync(join(base, "feature.txt"), "utf-8").includes("new feature"),
202
+ "main got feature from auto-committed branch",
203
+ );
204
+ assertEq(mergeS02.deletedBranch, true, "S02 branch deleted");
205
+
206
+ console.log("\n=== getSliceBranchName ===");
207
+ assertEq(
208
+ getSliceBranchName("M001", "S01"),
209
+ "kata/M001/S01",
210
+ "branch name format correct",
211
+ );
212
+
213
+ rmSync(base, { recursive: true, force: true });
214
+ console.log(`\nResults: ${passed} passed, ${failed} failed`);
215
+ if (failed > 0) process.exit(1);
216
+ console.log("All tests passed ✓");
217
+ }
218
+
219
+ main().catch((error) => {
220
+ console.error(error);
221
+ process.exit(1);
222
+ });
@@ -0,0 +1,159 @@
1
+ // Kata Extension — Core Type Definitions
2
+ // Types consumed by state derivation, file parsing, and status display.
3
+ // Pure interfaces — no logic, no runtime dependencies.
4
+
5
+ // ─── Enums & Literal Unions ────────────────────────────────────────────────
6
+
7
+ export type RiskLevel = 'low' | 'medium' | 'high';
8
+ export type Phase = 'pre-planning' | 'discussing' | 'researching' | 'planning' | 'executing' | 'verifying' | 'summarizing' | 'advancing' | 'completing-milestone' | 'replanning-slice' | 'complete' | 'paused' | 'blocked';
9
+ export type ContinueStatus = 'in_progress' | 'interrupted' | 'compacted';
10
+
11
+ // ─── Roadmap (Milestone-level) ─────────────────────────────────────────────
12
+
13
+ export interface RoadmapSliceEntry {
14
+ id: string; // e.g. "S01"
15
+ title: string; // e.g. "Types + File I/O + Git Operations"
16
+ risk: RiskLevel;
17
+ depends: string[]; // e.g. ["S01", "S02"]
18
+ done: boolean;
19
+ demo: string; // the "After this:" sentence
20
+ }
21
+
22
+ export interface BoundaryMapEntry {
23
+ fromSlice: string; // e.g. "S01"
24
+ toSlice: string; // e.g. "S02" or "terminal"
25
+ produces: string; // raw text block of what this slice produces
26
+ consumes: string; // raw text block of what it consumes (or "nothing")
27
+ }
28
+
29
+ export interface Roadmap {
30
+ title: string; // e.g. "M001: Kata Extension — Hierarchical Planning with Auto Mode"
31
+ vision: string;
32
+ successCriteria: string[];
33
+ slices: RoadmapSliceEntry[];
34
+ boundaryMap: BoundaryMapEntry[];
35
+ }
36
+
37
+ // ─── Slice Plan ────────────────────────────────────────────────────────────
38
+
39
+ export interface TaskPlanEntry {
40
+ id: string; // e.g. "T01"
41
+ title: string; // e.g. "Core Type Definitions"
42
+ description: string;
43
+ done: boolean;
44
+ estimate: string; // e.g. "30m", "2h" — informational only
45
+ files?: string[]; // e.g. ["types.ts", "files.ts"] — extracted from "- Files:" subline
46
+ verify?: string; // e.g. "run tests" — extracted from "- Verify:" subline
47
+ }
48
+
49
+ export interface SlicePlan {
50
+ id: string; // e.g. "S01"
51
+ title: string; // from the H1
52
+ goal: string;
53
+ demo: string;
54
+ mustHaves: string[]; // top-level must-have bullet points
55
+ tasks: TaskPlanEntry[];
56
+ filesLikelyTouched: string[];
57
+ }
58
+
59
+ // ─── Summary (Task & Slice level) ──────────────────────────────────────────
60
+
61
+ export interface SummaryRequires {
62
+ slice: string;
63
+ provides: string;
64
+ }
65
+
66
+ export interface SummaryFrontmatter {
67
+ id: string;
68
+ parent: string;
69
+ milestone: string;
70
+ provides: string[];
71
+ requires: SummaryRequires[];
72
+ affects: string[];
73
+ key_files: string[];
74
+ key_decisions: string[];
75
+ patterns_established: string[];
76
+ drill_down_paths: string[];
77
+ observability_surfaces: string[];
78
+ duration: string;
79
+ verification_result: string;
80
+ completed_at: string;
81
+ blocker_discovered: boolean;
82
+ }
83
+
84
+ export interface FileModified {
85
+ path: string;
86
+ description: string;
87
+ }
88
+
89
+ export interface Summary {
90
+ frontmatter: SummaryFrontmatter;
91
+ title: string;
92
+ oneLiner: string;
93
+ whatHappened: string;
94
+ deviations: string;
95
+ filesModified: FileModified[];
96
+ }
97
+
98
+ // ─── Continue-Here ─────────────────────────────────────────────────────────
99
+
100
+ export interface ContinueFrontmatter {
101
+ milestone: string;
102
+ slice: string;
103
+ task: string;
104
+ step: number;
105
+ totalSteps: number;
106
+ status: ContinueStatus;
107
+ savedAt: string;
108
+ }
109
+
110
+ export interface Continue {
111
+ frontmatter: ContinueFrontmatter;
112
+ completedWork: string;
113
+ remainingWork: string;
114
+ decisions: string;
115
+ context: string;
116
+ nextAction: string;
117
+ }
118
+
119
+ // ─── Kata State (Derived Dashboard) ────────────────────────────────────────
120
+
121
+ export interface ActiveRef {
122
+ id: string;
123
+ title: string;
124
+ }
125
+
126
+ export interface MilestoneRegistryEntry {
127
+ id: string;
128
+ title: string;
129
+ status: 'complete' | 'active' | 'pending';
130
+ /** Milestone IDs that must be complete before this milestone becomes active. Populated from CONTEXT.md YAML frontmatter. */
131
+ dependsOn?: string[];
132
+ }
133
+
134
+ export interface RequirementCounts {
135
+ active: number;
136
+ validated: number;
137
+ deferred: number;
138
+ outOfScope: number;
139
+ blocked: number;
140
+ total: number;
141
+ }
142
+
143
+ export interface KataState {
144
+ activeMilestone: ActiveRef | null;
145
+ activeSlice: ActiveRef | null;
146
+ activeTask: ActiveRef | null;
147
+ phase: Phase;
148
+ recentDecisions: string[];
149
+ blockers: string[];
150
+ nextAction: string;
151
+ activeBranch?: string;
152
+ registry: MilestoneRegistryEntry[];
153
+ requirements?: RequirementCounts;
154
+ progress?: {
155
+ milestones: { done: number; total: number };
156
+ slices?: { done: number; total: number };
157
+ tasks?: { done: number; total: number };
158
+ };
159
+ }
@@ -0,0 +1,163 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import {
4
+ kataRoot,
5
+ relSliceFile,
6
+ relTaskFile,
7
+ resolveSliceFile,
8
+ resolveTaskFile,
9
+ } from "./paths.ts";
10
+ import { loadFile, parseTaskPlanMustHaves, countMustHavesMentionedInSummary } from "./files.ts";
11
+
12
+ export type UnitRuntimePhase =
13
+ | "dispatched"
14
+ | "wrapup-warning-sent"
15
+ | "timeout"
16
+ | "recovered"
17
+ | "finalized"
18
+ | "paused"
19
+ | "skipped";
20
+
21
+ export interface ExecuteTaskRecoveryStatus {
22
+ planPath: string;
23
+ summaryPath: string;
24
+ summaryExists: boolean;
25
+ taskChecked: boolean;
26
+ nextActionAdvanced: boolean;
27
+ mustHaveCount: number;
28
+ mustHavesMentionedInSummary: number;
29
+ }
30
+
31
+ export interface AutoUnitRuntimeRecord {
32
+ version: 1;
33
+ unitType: string;
34
+ unitId: string;
35
+ startedAt: number;
36
+ updatedAt: number;
37
+ phase: UnitRuntimePhase;
38
+ wrapupWarningSent: boolean;
39
+ timeoutAt: number | null;
40
+ lastProgressAt: number;
41
+ progressCount: number;
42
+ lastProgressKind: string;
43
+ recovery?: ExecuteTaskRecoveryStatus;
44
+ recoveryAttempts?: number;
45
+ lastRecoveryReason?: "idle" | "hard";
46
+ }
47
+
48
+ function runtimeDir(basePath: string): string {
49
+ return join(kataRoot(basePath), "runtime", "units");
50
+ }
51
+
52
+ function runtimePath(basePath: string, unitType: string, unitId: string): string {
53
+ return join(runtimeDir(basePath), `${unitType}-${unitId.replace(/[\/]/g, "-")}.json`);
54
+ }
55
+
56
+ export function writeUnitRuntimeRecord(
57
+ basePath: string,
58
+ unitType: string,
59
+ unitId: string,
60
+ startedAt: number,
61
+ updates: Partial<AutoUnitRuntimeRecord> = {},
62
+ ): AutoUnitRuntimeRecord {
63
+ const dir = runtimeDir(basePath);
64
+ mkdirSync(dir, { recursive: true });
65
+ const path = runtimePath(basePath, unitType, unitId);
66
+ const prev = readUnitRuntimeRecord(basePath, unitType, unitId);
67
+ const next: AutoUnitRuntimeRecord = {
68
+ version: 1,
69
+ unitType,
70
+ unitId,
71
+ startedAt,
72
+ updatedAt: Date.now(),
73
+ phase: updates.phase ?? prev?.phase ?? "dispatched",
74
+ wrapupWarningSent: updates.wrapupWarningSent ?? prev?.wrapupWarningSent ?? false,
75
+ timeoutAt: updates.timeoutAt ?? prev?.timeoutAt ?? null,
76
+ lastProgressAt: updates.lastProgressAt ?? prev?.lastProgressAt ?? Date.now(),
77
+ progressCount: updates.progressCount ?? prev?.progressCount ?? 0,
78
+ lastProgressKind: updates.lastProgressKind ?? prev?.lastProgressKind ?? "dispatch",
79
+ recovery: updates.recovery ?? prev?.recovery,
80
+ recoveryAttempts: updates.recoveryAttempts ?? prev?.recoveryAttempts ?? 0,
81
+ lastRecoveryReason: updates.lastRecoveryReason ?? prev?.lastRecoveryReason,
82
+ };
83
+ writeFileSync(path, JSON.stringify(next, null, 2) + "\n", "utf-8");
84
+ return next;
85
+ }
86
+
87
+ export function readUnitRuntimeRecord(basePath: string, unitType: string, unitId: string): AutoUnitRuntimeRecord | null {
88
+ const path = runtimePath(basePath, unitType, unitId);
89
+ if (!existsSync(path)) return null;
90
+ try {
91
+ return JSON.parse(readFileSync(path, "utf-8")) as AutoUnitRuntimeRecord;
92
+ } catch {
93
+ return null;
94
+ }
95
+ }
96
+
97
+ export function clearUnitRuntimeRecord(basePath: string, unitType: string, unitId: string): void {
98
+ const path = runtimePath(basePath, unitType, unitId);
99
+ if (existsSync(path)) unlinkSync(path);
100
+ }
101
+
102
+ export async function inspectExecuteTaskDurability(
103
+ basePath: string,
104
+ unitId: string,
105
+ ): Promise<ExecuteTaskRecoveryStatus | null> {
106
+ const [mid, sid, tid] = unitId.split("/");
107
+ if (!mid || !sid || !tid) return null;
108
+
109
+ const planAbs = resolveSliceFile(basePath, mid, sid, "PLAN");
110
+ const summaryAbs = resolveTaskFile(basePath, mid, sid, tid, "SUMMARY");
111
+ const stateAbs = join(kataRoot(basePath), "STATE.md");
112
+
113
+ const planPath = relSliceFile(basePath, mid, sid, "PLAN");
114
+ const summaryPath = relTaskFile(basePath, mid, sid, tid, "SUMMARY");
115
+
116
+ const planContent = planAbs ? await loadFile(planAbs) : null;
117
+ const stateContent = existsSync(stateAbs) ? readFileSync(stateAbs, "utf-8") : "";
118
+ const summaryExists = !!(summaryAbs && existsSync(summaryAbs));
119
+
120
+ const escapedTid = tid.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
121
+ const taskChecked = !!planContent && new RegExp(`^- \\[[xX]\\] \\*\\*${escapedTid}:`, "m").test(planContent);
122
+ const nextActionAdvanced = !new RegExp(`Execute ${tid}\\b`).test(stateContent);
123
+
124
+ // Must-have coverage: load task plan and count mentions in summary
125
+ let mustHaveCount = 0;
126
+ let mustHavesMentionedInSummary = 0;
127
+
128
+ const taskPlanAbs = resolveTaskFile(basePath, mid, sid, tid, "PLAN");
129
+ if (taskPlanAbs) {
130
+ const taskPlanContent = await loadFile(taskPlanAbs);
131
+ if (taskPlanContent) {
132
+ const mustHaves = parseTaskPlanMustHaves(taskPlanContent);
133
+ mustHaveCount = mustHaves.length;
134
+ if (mustHaveCount > 0 && summaryExists && summaryAbs) {
135
+ const summaryContent = await loadFile(summaryAbs);
136
+ if (summaryContent) {
137
+ mustHavesMentionedInSummary = countMustHavesMentionedInSummary(mustHaves, summaryContent);
138
+ }
139
+ }
140
+ }
141
+ }
142
+
143
+ return {
144
+ planPath,
145
+ summaryPath,
146
+ summaryExists,
147
+ taskChecked,
148
+ nextActionAdvanced,
149
+ mustHaveCount,
150
+ mustHavesMentionedInSummary,
151
+ };
152
+ }
153
+
154
+ export function formatExecuteTaskRecoveryStatus(status: ExecuteTaskRecoveryStatus): string {
155
+ const missing = [] as string[];
156
+ if (!status.summaryExists) missing.push(`summary missing (${status.summaryPath})`);
157
+ if (!status.taskChecked) missing.push(`task checkbox unchecked in ${status.planPath}`);
158
+ if (!status.nextActionAdvanced) missing.push("state next action still points at the timed-out task");
159
+ if (status.mustHaveCount > 0 && status.mustHavesMentionedInSummary < status.mustHaveCount) {
160
+ missing.push(`must-have gap: ${status.mustHavesMentionedInSummary} of ${status.mustHaveCount} must-haves addressed in summary`);
161
+ }
162
+ return missing.length > 0 ? missing.join("; ") : "all durable task artifacts present";
163
+ }