@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,355 @@
1
+ /**
2
+ * Kata Command — /kata
3
+ *
4
+ * One command, one wizard. Routes to smart entry or status.
5
+ */
6
+
7
+ import type {
8
+ ExtensionAPI,
9
+ ExtensionCommandContext,
10
+ } from "@mariozechner/pi-coding-agent";
11
+ import { existsSync, readFileSync } from "node:fs";
12
+ import { join, dirname } from "node:path";
13
+ import { fileURLToPath } from "node:url";
14
+ import { deriveState } from "./state.js";
15
+ import { KataDashboardOverlay } from "./dashboard-overlay.js";
16
+ import { showSmartEntry, showQueue, showDiscuss } from "./guided-flow.js";
17
+ import { startAuto, stopAuto, isAutoActive, isAutoPaused } from "./auto.js";
18
+ import {
19
+ getGlobalKataPreferencesPath,
20
+ getLegacyGlobalKataPreferencesPath,
21
+ getProjectKataPreferencesPath,
22
+ loadGlobalKataPreferences,
23
+ loadProjectKataPreferences,
24
+ loadEffectiveKataPreferences,
25
+ resolveAllSkillReferences,
26
+ } from "./preferences.js";
27
+ import { loadFile, saveFile } from "./files.js";
28
+ import {
29
+ formatDoctorIssuesForPrompt,
30
+ formatDoctorReport,
31
+ runKataDoctor,
32
+ selectDoctorScope,
33
+ filterDoctorIssues,
34
+ } from "./doctor.js";
35
+ import { loadPrompt } from "./prompt-loader.js";
36
+
37
+ function dispatchDoctorHeal(
38
+ pi: ExtensionAPI,
39
+ scope: string | undefined,
40
+ reportText: string,
41
+ structuredIssues: string,
42
+ ): void {
43
+ const workflowPath =
44
+ process.env.KATA_WORKFLOW_PATH ??
45
+ join(process.env.HOME ?? "~", ".kata-cli", "KATA-WORKFLOW.md");
46
+ const workflow = readFileSync(workflowPath, "utf-8");
47
+ const prompt = loadPrompt("doctor-heal", {
48
+ doctorSummary: reportText,
49
+ structuredIssues,
50
+ scopeLabel: scope ?? "active milestone / blocking scope",
51
+ doctorCommandSuffix: scope ? ` ${scope}` : "",
52
+ });
53
+
54
+ const content = `Read the following Kata workflow protocol and execute exactly.\n\n${workflow}\n\n## Your Task\n\n${prompt}`;
55
+
56
+ pi.sendMessage(
57
+ { customType: "kata-doctor-heal", content, display: false },
58
+ { triggerTurn: true },
59
+ );
60
+ }
61
+
62
+ export function registerKataCommand(pi: ExtensionAPI): void {
63
+ pi.registerCommand("kata", {
64
+ description:
65
+ "Kata — Kata Workflow: /kata auto|stop|status|queue|prefs|doctor",
66
+
67
+ getArgumentCompletions: (prefix: string) => {
68
+ const subcommands = [
69
+ "auto",
70
+ "stop",
71
+ "status",
72
+ "queue",
73
+ "discuss",
74
+ "prefs",
75
+ "doctor",
76
+ ];
77
+ const parts = prefix.trim().split(/\s+/);
78
+
79
+ if (parts.length <= 1) {
80
+ return subcommands
81
+ .filter((cmd) => cmd.startsWith(parts[0] ?? ""))
82
+ .map((cmd) => ({ value: cmd, label: cmd }));
83
+ }
84
+
85
+ if (parts[0] === "auto" && parts.length <= 2) {
86
+ const flagPrefix = parts[1] ?? "";
87
+ return ["--verbose"]
88
+ .filter((f) => f.startsWith(flagPrefix))
89
+ .map((f) => ({ value: `auto ${f}`, label: f }));
90
+ }
91
+
92
+ if (parts[0] === "prefs" && parts.length <= 2) {
93
+ const subPrefix = parts[1] ?? "";
94
+ return ["global", "project", "status"]
95
+ .filter((cmd) => cmd.startsWith(subPrefix))
96
+ .map((cmd) => ({ value: `prefs ${cmd}`, label: cmd }));
97
+ }
98
+
99
+ if (parts[0] === "doctor") {
100
+ const modePrefix = parts[1] ?? "";
101
+ const modes = ["fix", "heal", "audit"];
102
+
103
+ if (parts.length <= 2) {
104
+ return modes
105
+ .filter((cmd) => cmd.startsWith(modePrefix))
106
+ .map((cmd) => ({ value: `doctor ${cmd}`, label: cmd }));
107
+ }
108
+
109
+ return [];
110
+ }
111
+
112
+ return [];
113
+ },
114
+
115
+ async handler(args: string, ctx: ExtensionCommandContext) {
116
+ const trimmed = (typeof args === "string" ? args : "").trim();
117
+
118
+ if (trimmed === "status") {
119
+ await handleStatus(ctx);
120
+ return;
121
+ }
122
+
123
+ if (trimmed === "prefs" || trimmed.startsWith("prefs ")) {
124
+ await handlePrefs(trimmed.replace(/^prefs\s*/, "").trim(), ctx);
125
+ return;
126
+ }
127
+
128
+ if (trimmed === "doctor" || trimmed.startsWith("doctor ")) {
129
+ await handleDoctor(trimmed.replace(/^doctor\s*/, "").trim(), ctx, pi);
130
+ return;
131
+ }
132
+
133
+ if (trimmed === "auto" || trimmed.startsWith("auto ")) {
134
+ const verboseMode = trimmed.includes("--verbose");
135
+ await startAuto(ctx, pi, process.cwd(), verboseMode);
136
+ return;
137
+ }
138
+
139
+ if (trimmed === "stop") {
140
+ if (!isAutoActive() && !isAutoPaused()) {
141
+ ctx.ui.notify("Auto-mode is not running.", "info");
142
+ return;
143
+ }
144
+ await stopAuto(ctx, pi);
145
+ return;
146
+ }
147
+
148
+ if (trimmed === "queue") {
149
+ await showQueue(ctx, pi, process.cwd());
150
+ return;
151
+ }
152
+
153
+ if (trimmed === "discuss") {
154
+ await showDiscuss(ctx, pi, process.cwd());
155
+ return;
156
+ }
157
+
158
+ if (trimmed === "") {
159
+ await showSmartEntry(ctx, pi, process.cwd());
160
+ return;
161
+ }
162
+
163
+ ctx.ui.notify(
164
+ `Unknown: /kata ${trimmed}. Use /kata, /kata auto, /kata stop, /kata status, /kata queue, /kata discuss, /kata prefs [global|project|status], or /kata doctor [audit|fix|heal] [M###/S##].`,
165
+ "warning",
166
+ );
167
+ },
168
+ });
169
+ }
170
+
171
+ async function handleStatus(ctx: ExtensionCommandContext): Promise<void> {
172
+ const basePath = process.cwd();
173
+ const state = await deriveState(basePath);
174
+
175
+ if (state.registry.length === 0) {
176
+ ctx.ui.notify("No Kata milestones found. Run /kata to start.", "info");
177
+ return;
178
+ }
179
+
180
+ await ctx.ui.custom<void>(
181
+ (tui, theme, _kb, done) => {
182
+ return new KataDashboardOverlay(tui, theme, () => done());
183
+ },
184
+ {
185
+ overlay: true,
186
+ overlayOptions: {
187
+ width: "70%",
188
+ minWidth: 60,
189
+ maxHeight: "90%",
190
+ anchor: "center",
191
+ },
192
+ },
193
+ );
194
+ }
195
+
196
+ export async function fireStatusViaCommand(
197
+ ctx: import("@mariozechner/pi-coding-agent").ExtensionContext,
198
+ ): Promise<void> {
199
+ await handleStatus(ctx as ExtensionCommandContext);
200
+ }
201
+
202
+ async function handlePrefs(
203
+ args: string,
204
+ ctx: ExtensionCommandContext,
205
+ ): Promise<void> {
206
+ const trimmed = args.trim();
207
+
208
+ if (trimmed === "" || trimmed === "global") {
209
+ await ensurePreferencesFile(getGlobalKataPreferencesPath(), ctx, "global");
210
+ return;
211
+ }
212
+
213
+ if (trimmed === "project") {
214
+ await ensurePreferencesFile(getProjectKataPreferencesPath(), ctx, "project");
215
+ return;
216
+ }
217
+
218
+ if (trimmed === "status") {
219
+ const globalPrefs = loadGlobalKataPreferences();
220
+ const projectPrefs = loadProjectKataPreferences();
221
+ const canonicalGlobal = getGlobalKataPreferencesPath();
222
+ const legacyGlobal = getLegacyGlobalKataPreferencesPath();
223
+ const globalStatus = globalPrefs
224
+ ? `present: ${globalPrefs.path}${globalPrefs.path === legacyGlobal ? " (legacy fallback)" : ""}`
225
+ : `missing: ${canonicalGlobal}`;
226
+ const projectStatus = projectPrefs
227
+ ? `present: ${projectPrefs.path}`
228
+ : `missing: ${getProjectKataPreferencesPath()}`;
229
+
230
+ const lines = [
231
+ `Kata skill prefs — global ${globalStatus}; project ${projectStatus}`,
232
+ ];
233
+
234
+ const effective = loadEffectiveKataPreferences();
235
+ let hasUnresolved = false;
236
+ if (effective) {
237
+ const report = resolveAllSkillReferences(
238
+ effective.preferences,
239
+ process.cwd(),
240
+ );
241
+ const resolved = [...report.resolutions.values()].filter(
242
+ (r) => r.method !== "unresolved",
243
+ );
244
+ hasUnresolved = report.warnings.length > 0;
245
+ if (resolved.length > 0 || hasUnresolved) {
246
+ lines.push(
247
+ `Skills: ${resolved.length} resolved, ${report.warnings.length} unresolved`,
248
+ );
249
+ }
250
+ if (hasUnresolved) {
251
+ lines.push(`Unresolved: ${report.warnings.join(", ")}`);
252
+ }
253
+ }
254
+
255
+ ctx.ui.notify(lines.join("\n"), hasUnresolved ? "warning" : "info");
256
+ return;
257
+ }
258
+
259
+ ctx.ui.notify("Usage: /kata prefs [global|project|status]", "info");
260
+ }
261
+
262
+ async function handleDoctor(
263
+ args: string,
264
+ ctx: ExtensionCommandContext,
265
+ pi: ExtensionAPI,
266
+ ): Promise<void> {
267
+ const trimmed = args.trim();
268
+ const parts = trimmed ? trimmed.split(/\s+/) : [];
269
+ const mode =
270
+ parts[0] === "fix" || parts[0] === "heal" || parts[0] === "audit"
271
+ ? parts[0]
272
+ : "doctor";
273
+ const requestedScope = mode === "doctor" ? parts[0] : parts[1];
274
+ const scope = await selectDoctorScope(process.cwd(), requestedScope);
275
+ const effectiveScope = mode === "audit" ? requestedScope : scope;
276
+ const report = await runKataDoctor(process.cwd(), {
277
+ fix: mode === "fix" || mode === "heal",
278
+ scope: effectiveScope,
279
+ });
280
+
281
+ const reportText = formatDoctorReport(report, {
282
+ scope: effectiveScope,
283
+ includeWarnings: mode === "audit",
284
+ maxIssues: mode === "audit" ? 50 : 12,
285
+ title:
286
+ mode === "audit"
287
+ ? "Kata doctor audit."
288
+ : mode === "heal"
289
+ ? "Kata doctor heal prep."
290
+ : undefined,
291
+ });
292
+
293
+ ctx.ui.notify(reportText, report.ok ? "info" : "warning");
294
+
295
+ if (mode === "heal") {
296
+ const unresolved = filterDoctorIssues(report.issues, {
297
+ scope: effectiveScope,
298
+ includeWarnings: true,
299
+ });
300
+ const actionable = unresolved.filter(
301
+ (issue) =>
302
+ issue.severity === "error" ||
303
+ issue.code === "all_tasks_done_missing_slice_uat" ||
304
+ issue.code === "slice_checked_missing_uat",
305
+ );
306
+ if (actionable.length === 0) {
307
+ ctx.ui.notify(
308
+ "Doctor heal found nothing actionable to hand off to the LLM.",
309
+ "info",
310
+ );
311
+ return;
312
+ }
313
+
314
+ const structuredIssues = formatDoctorIssuesForPrompt(actionable);
315
+ dispatchDoctorHeal(pi, effectiveScope, reportText, structuredIssues);
316
+ ctx.ui.notify(
317
+ `Doctor heal dispatched ${actionable.length} issue(s) to the LLM.`,
318
+ "info",
319
+ );
320
+ }
321
+ }
322
+
323
+ async function ensurePreferencesFile(
324
+ path: string,
325
+ ctx: ExtensionCommandContext,
326
+ scope: "global" | "project",
327
+ ): Promise<void> {
328
+ if (!existsSync(path)) {
329
+ const template = await loadFile(
330
+ join(
331
+ dirname(fileURLToPath(import.meta.url)),
332
+ "templates",
333
+ "preferences.md",
334
+ ),
335
+ );
336
+ if (!template) {
337
+ ctx.ui.notify("Could not load Kata preferences template.", "error");
338
+ return;
339
+ }
340
+ await saveFile(path, template);
341
+ ctx.ui.notify(`Created ${scope} Kata skill preferences at ${path}`, "info");
342
+ } else {
343
+ ctx.ui.notify(
344
+ `Using existing ${scope} Kata skill preferences at ${path}`,
345
+ "info",
346
+ );
347
+ }
348
+
349
+ await ctx.waitForIdle();
350
+ await ctx.reload();
351
+ ctx.ui.notify(
352
+ `Edit ${path} to update ${scope} Kata skill preferences.`,
353
+ "info",
354
+ );
355
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Kata Crash Recovery
3
+ *
4
+ * Detects interrupted auto-mode sessions via a lock file.
5
+ * Written on auto-start, updated on each unit dispatch, deleted on clean stop.
6
+ * If the lock file exists on next startup, the previous session crashed.
7
+ *
8
+ * The lock records the pi session file path so crash recovery can read the
9
+ * surviving JSONL (pi appends entries incrementally via appendFileSync,
10
+ * so the file on disk reflects every tool call up to the crash point).
11
+ */
12
+
13
+ import { writeFileSync, readFileSync, unlinkSync, existsSync } from "node:fs";
14
+ import { join } from "node:path";
15
+ import { kataRoot } from "./paths.js";
16
+
17
+ const LOCK_FILE = "auto.lock";
18
+
19
+ export interface LockData {
20
+ pid: number;
21
+ startedAt: string;
22
+ unitType: string;
23
+ unitId: string;
24
+ unitStartedAt: string;
25
+ completedUnits: number;
26
+ /** Path to the pi session JSONL file that was active when this unit started. */
27
+ sessionFile?: string;
28
+ }
29
+
30
+ function lockPath(basePath: string): string {
31
+ return join(kataRoot(basePath), LOCK_FILE);
32
+ }
33
+
34
+ /** Write or update the lock file with current auto-mode state. */
35
+ export function writeLock(
36
+ basePath: string,
37
+ unitType: string,
38
+ unitId: string,
39
+ completedUnits: number,
40
+ sessionFile?: string,
41
+ ): void {
42
+ try {
43
+ const data: LockData = {
44
+ pid: process.pid,
45
+ startedAt: new Date().toISOString(),
46
+ unitType,
47
+ unitId,
48
+ unitStartedAt: new Date().toISOString(),
49
+ completedUnits,
50
+ sessionFile,
51
+ };
52
+ writeFileSync(lockPath(basePath), JSON.stringify(data, null, 2), "utf-8");
53
+ } catch { /* non-fatal */ }
54
+ }
55
+
56
+ /** Remove the lock file on clean stop. */
57
+ export function clearLock(basePath: string): void {
58
+ try {
59
+ const p = lockPath(basePath);
60
+ if (existsSync(p)) unlinkSync(p);
61
+ } catch { /* non-fatal */ }
62
+ }
63
+
64
+ /** Check if a crash lock exists and return its data. */
65
+ export function readCrashLock(basePath: string): LockData | null {
66
+ try {
67
+ const p = lockPath(basePath);
68
+ if (!existsSync(p)) return null;
69
+ const raw = readFileSync(p, "utf-8");
70
+ return JSON.parse(raw) as LockData;
71
+ } catch {
72
+ return null;
73
+ }
74
+ }
75
+
76
+ /** Format crash info for display or injection into a prompt. */
77
+ export function formatCrashInfo(lock: LockData): string {
78
+ return [
79
+ `Previous auto-mode session was interrupted.`,
80
+ ` Was executing: ${lock.unitType} (${lock.unitId})`,
81
+ ` Started at: ${lock.unitStartedAt}`,
82
+ ` Units completed before crash: ${lock.completedUnits}`,
83
+ ` PID: ${lock.pid}`,
84
+ ].join("\n");
85
+ }