@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,346 @@
1
+ /**
2
+ * Kata Paths — ID-based path resolution
3
+ *
4
+ * Directories use bare IDs: M001/, S01/, etc.
5
+ * Files use ID-SUFFIX: M001-ROADMAP.md, S01-PLAN.md, T01-PLAN.md
6
+ *
7
+ * Resolvers still handle legacy descriptor-suffixed names
8
+ * (e.g. M001-FLIGHT-SIMULATOR/, T03-INSTALL-PACKAGES-PLAN.md)
9
+ * via prefix matching, so existing projects work without migration.
10
+ */
11
+
12
+ import { readdirSync, existsSync } from "node:fs";
13
+ import { join } from "node:path";
14
+
15
+ // ─── Name Builders ─────────────────────────────────────────────────────────
16
+
17
+ /**
18
+ * Build a directory name from an ID.
19
+ * ("M001") → "M001"
20
+ */
21
+ export function buildDirName(id: string): string {
22
+ return id;
23
+ }
24
+
25
+ /**
26
+ * Build a milestone-level file name.
27
+ * ("M001", "CONTEXT") → "M001-CONTEXT.md"
28
+ */
29
+ export function buildMilestoneFileName(
30
+ milestoneId: string,
31
+ suffix: string,
32
+ ): string {
33
+ return `${milestoneId}-${suffix}.md`;
34
+ }
35
+
36
+ /**
37
+ * Build a slice-level file name.
38
+ * ("S01", "PLAN") → "S01-PLAN.md"
39
+ */
40
+ export function buildSliceFileName(sliceId: string, suffix: string): string {
41
+ return `${sliceId}-${suffix}.md`;
42
+ }
43
+
44
+ /**
45
+ * Build a task file name.
46
+ * ("T03", "PLAN") → "T03-PLAN.md"
47
+ * ("T03", "SUMMARY") → "T03-SUMMARY.md"
48
+ */
49
+ export function buildTaskFileName(taskId: string, suffix: string): string {
50
+ return `${taskId}-${suffix}.md`;
51
+ }
52
+
53
+ // ─── Resolvers ─────────────────────────────────────────────────────────────
54
+
55
+ /**
56
+ * Find a directory entry by ID prefix within a parent directory.
57
+ * Exact match first (M001), then prefix match (M001-SOMETHING) for
58
+ * backward compatibility with legacy descriptor directories.
59
+ * Returns the full directory name or null.
60
+ */
61
+ export function resolveDir(parentDir: string, idPrefix: string): string | null {
62
+ if (!existsSync(parentDir)) return null;
63
+ try {
64
+ const entries = readdirSync(parentDir, { withFileTypes: true });
65
+ // Exact match first (current convention: bare ID)
66
+ const exact = entries.find((e) => e.isDirectory() && e.name === idPrefix);
67
+ if (exact) return exact.name;
68
+ // Prefix match for legacy descriptor dirs: M001-SOMETHING
69
+ const prefixed = entries.find(
70
+ (e) => e.isDirectory() && e.name.startsWith(idPrefix + "-"),
71
+ );
72
+ return prefixed ? prefixed.name : null;
73
+ } catch {
74
+ return null;
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Find a file by ID prefix and suffix within a directory.
80
+ * Checks in order:
81
+ * 1. Direct: ID-SUFFIX.md (e.g. M001-ROADMAP.md, T03-PLAN.md)
82
+ * 2. Legacy descriptor: ID-DESCRIPTOR-SUFFIX.md (e.g. T03-INSTALL-PACKAGES-PLAN.md)
83
+ * 3. Legacy bare: suffix.md (e.g. roadmap.md)
84
+ */
85
+ export function resolveFile(
86
+ dir: string,
87
+ idPrefix: string,
88
+ suffix: string,
89
+ ): string | null {
90
+ if (!existsSync(dir)) return null;
91
+ const target = `${idPrefix}-${suffix}.md`.toUpperCase();
92
+ try {
93
+ const entries = readdirSync(dir);
94
+ // Direct match: ID-SUFFIX.md
95
+ const direct = entries.find((e) => e.toUpperCase() === target);
96
+ if (direct) return direct;
97
+ // Legacy pattern match: ID-DESCRIPTOR-SUFFIX.md
98
+ const pattern = new RegExp(`^${idPrefix}-.*-${suffix}\\.md$`, "i");
99
+ const match = entries.find((e) => pattern.test(e));
100
+ if (match) return match;
101
+ // Legacy fallback: suffix.md
102
+ const legacy = entries.find(
103
+ (e) => e.toLowerCase() === `${suffix.toLowerCase()}.md`,
104
+ );
105
+ if (legacy) return legacy;
106
+ return null;
107
+ } catch {
108
+ return null;
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Find all task files matching a pattern in a tasks directory.
114
+ * Returns sorted file names matching T##-SUFFIX.md or legacy T##-*-SUFFIX.md
115
+ */
116
+ export function resolveTaskFiles(tasksDir: string, suffix: string): string[] {
117
+ if (!existsSync(tasksDir)) return [];
118
+ try {
119
+ // Current convention: T01-PLAN.md
120
+ const currentPattern = new RegExp(`^T\\d+-${suffix}\\.md$`, "i");
121
+ // Legacy convention: T01-INSTALL-PACKAGES-PLAN.md
122
+ const legacyPattern = new RegExp(`^T\\d+-.*-${suffix}\\.md$`, "i");
123
+ return readdirSync(tasksDir)
124
+ .filter((f) => currentPattern.test(f) || legacyPattern.test(f))
125
+ .sort();
126
+ } catch {
127
+ return [];
128
+ }
129
+ }
130
+
131
+ // ─── Full Path Builders ────────────────────────────────────────────────────
132
+
133
+ export const KATA_ROOT_FILES = {
134
+ PROJECT: "PROJECT.md",
135
+ DECISIONS: "DECISIONS.md",
136
+ QUEUE: "QUEUE.md",
137
+ STATE: "STATE.md",
138
+ REQUIREMENTS: "REQUIREMENTS.md",
139
+ } as const;
140
+
141
+ export type KataRootFileKey = keyof typeof KATA_ROOT_FILES;
142
+
143
+ const LEGACY_KATA_ROOT_FILES: Record<KataRootFileKey, string> = {
144
+ PROJECT: "project.md",
145
+ DECISIONS: "decisions.md",
146
+ QUEUE: "queue.md",
147
+ STATE: "state.md",
148
+ REQUIREMENTS: "requirements.md",
149
+ };
150
+
151
+ export function kataRoot(basePath: string): string {
152
+ return join(basePath, ".kata");
153
+ }
154
+
155
+ export function milestonesDir(basePath: string): string {
156
+ return join(kataRoot(basePath), "milestones");
157
+ }
158
+
159
+ export function resolveKataRootFile(
160
+ basePath: string,
161
+ key: KataRootFileKey,
162
+ ): string {
163
+ const root = kataRoot(basePath);
164
+ const canonical = join(root, KATA_ROOT_FILES[key]);
165
+ if (existsSync(canonical)) return canonical;
166
+ const legacy = join(root, LEGACY_KATA_ROOT_FILES[key]);
167
+ if (existsSync(legacy)) return legacy;
168
+ return canonical;
169
+ }
170
+
171
+ export function relKataRootFile(key: KataRootFileKey): string {
172
+ return `.kata/${KATA_ROOT_FILES[key]}`;
173
+ }
174
+
175
+ /**
176
+ * Resolve the full path to a milestone directory.
177
+ * Returns null if the milestone doesn't exist.
178
+ */
179
+ export function resolveMilestonePath(
180
+ basePath: string,
181
+ milestoneId: string,
182
+ ): string | null {
183
+ const dir = resolveDir(milestonesDir(basePath), milestoneId);
184
+ return dir ? join(milestonesDir(basePath), dir) : null;
185
+ }
186
+
187
+ /**
188
+ * Resolve the full path to a milestone file (e.g. ROADMAP, CONTEXT, RESEARCH).
189
+ */
190
+ export function resolveMilestoneFile(
191
+ basePath: string,
192
+ milestoneId: string,
193
+ suffix: string,
194
+ ): string | null {
195
+ const mDir = resolveMilestonePath(basePath, milestoneId);
196
+ if (!mDir) return null;
197
+ const file = resolveFile(mDir, milestoneId, suffix);
198
+ return file ? join(mDir, file) : null;
199
+ }
200
+
201
+ /**
202
+ * Resolve the full path to a slice directory within a milestone.
203
+ */
204
+ export function resolveSlicePath(
205
+ basePath: string,
206
+ milestoneId: string,
207
+ sliceId: string,
208
+ ): string | null {
209
+ const mDir = resolveMilestonePath(basePath, milestoneId);
210
+ if (!mDir) return null;
211
+ const slicesDir = join(mDir, "slices");
212
+ const dir = resolveDir(slicesDir, sliceId);
213
+ return dir ? join(slicesDir, dir) : null;
214
+ }
215
+
216
+ /**
217
+ * Resolve the full path to a slice file (e.g. PLAN, RESEARCH, CONTEXT, SUMMARY).
218
+ */
219
+ export function resolveSliceFile(
220
+ basePath: string,
221
+ milestoneId: string,
222
+ sliceId: string,
223
+ suffix: string,
224
+ ): string | null {
225
+ const sDir = resolveSlicePath(basePath, milestoneId, sliceId);
226
+ if (!sDir) return null;
227
+ const file = resolveFile(sDir, sliceId, suffix);
228
+ return file ? join(sDir, file) : null;
229
+ }
230
+
231
+ /**
232
+ * Resolve the tasks directory within a slice.
233
+ */
234
+ export function resolveTasksDir(
235
+ basePath: string,
236
+ milestoneId: string,
237
+ sliceId: string,
238
+ ): string | null {
239
+ const sDir = resolveSlicePath(basePath, milestoneId, sliceId);
240
+ if (!sDir) return null;
241
+ const tDir = join(sDir, "tasks");
242
+ return existsSync(tDir) ? tDir : null;
243
+ }
244
+
245
+ /**
246
+ * Resolve a specific task file.
247
+ */
248
+ export function resolveTaskFile(
249
+ basePath: string,
250
+ milestoneId: string,
251
+ sliceId: string,
252
+ taskId: string,
253
+ suffix: string,
254
+ ): string | null {
255
+ const tDir = resolveTasksDir(basePath, milestoneId, sliceId);
256
+ if (!tDir) return null;
257
+ const file = resolveFile(tDir, taskId, suffix);
258
+ return file ? join(tDir, file) : null;
259
+ }
260
+
261
+ // ─── Relative Path Builders (for prompts — .kata/milestones/...) ────────────
262
+
263
+ /**
264
+ * Build relative .kata/ path to a milestone directory.
265
+ * Uses the actual directory name on disk if it exists, otherwise bare ID.
266
+ */
267
+ export function relMilestonePath(
268
+ basePath: string,
269
+ milestoneId: string,
270
+ ): string {
271
+ const dir = resolveDir(milestonesDir(basePath), milestoneId);
272
+ if (dir) return `.kata/milestones/${dir}`;
273
+ return `.kata/milestones/${milestoneId}`;
274
+ }
275
+
276
+ /**
277
+ * Build relative .kata/ path to a milestone file.
278
+ */
279
+ export function relMilestoneFile(
280
+ basePath: string,
281
+ milestoneId: string,
282
+ suffix: string,
283
+ ): string {
284
+ const mRel = relMilestonePath(basePath, milestoneId);
285
+ const mDir = resolveMilestonePath(basePath, milestoneId);
286
+ if (mDir) {
287
+ const file = resolveFile(mDir, milestoneId, suffix);
288
+ if (file) return `${mRel}/${file}`;
289
+ }
290
+ return `${mRel}/${buildMilestoneFileName(milestoneId, suffix)}`;
291
+ }
292
+
293
+ /**
294
+ * Build relative .kata/ path to a slice directory.
295
+ */
296
+ export function relSlicePath(
297
+ basePath: string,
298
+ milestoneId: string,
299
+ sliceId: string,
300
+ ): string {
301
+ const mRel = relMilestonePath(basePath, milestoneId);
302
+ const mDir = resolveMilestonePath(basePath, milestoneId);
303
+ if (mDir) {
304
+ const slicesDir = join(mDir, "slices");
305
+ const dir = resolveDir(slicesDir, sliceId);
306
+ if (dir) return `${mRel}/slices/${dir}`;
307
+ }
308
+ return `${mRel}/slices/${sliceId}`;
309
+ }
310
+
311
+ /**
312
+ * Build relative .kata/ path to a slice file.
313
+ */
314
+ export function relSliceFile(
315
+ basePath: string,
316
+ milestoneId: string,
317
+ sliceId: string,
318
+ suffix: string,
319
+ ): string {
320
+ const sRel = relSlicePath(basePath, milestoneId, sliceId);
321
+ const sDir = resolveSlicePath(basePath, milestoneId, sliceId);
322
+ if (sDir) {
323
+ const file = resolveFile(sDir, sliceId, suffix);
324
+ if (file) return `${sRel}/${file}`;
325
+ }
326
+ return `${sRel}/${buildSliceFileName(sliceId, suffix)}`;
327
+ }
328
+
329
+ /**
330
+ * Build relative .kata/ path to a task file.
331
+ */
332
+ export function relTaskFile(
333
+ basePath: string,
334
+ milestoneId: string,
335
+ sliceId: string,
336
+ taskId: string,
337
+ suffix: string,
338
+ ): string {
339
+ const sRel = relSlicePath(basePath, milestoneId, sliceId);
340
+ const tDir = resolveTasksDir(basePath, milestoneId, sliceId);
341
+ if (tDir) {
342
+ const file = resolveFile(tDir, taskId, suffix);
343
+ if (file) return `${sRel}/tasks/${file}`;
344
+ }
345
+ return `${sRel}/tasks/${buildTaskFileName(taskId, suffix)}`;
346
+ }