@kata-sh/cli 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +156 -0
  3. package/dist/app-paths.d.ts +4 -0
  4. package/dist/app-paths.js +6 -0
  5. package/dist/cli.d.ts +1 -0
  6. package/dist/cli.js +56 -0
  7. package/dist/loader.d.ts +2 -0
  8. package/dist/loader.js +95 -0
  9. package/dist/resource-loader.d.ts +18 -0
  10. package/dist/resource-loader.js +50 -0
  11. package/dist/wizard.d.ts +15 -0
  12. package/dist/wizard.js +159 -0
  13. package/package.json +50 -21
  14. package/pkg/dist/modes/interactive/theme/dark.json +85 -0
  15. package/pkg/dist/modes/interactive/theme/light.json +84 -0
  16. package/pkg/dist/modes/interactive/theme/theme-schema.json +335 -0
  17. package/pkg/dist/modes/interactive/theme/theme.d.ts +78 -0
  18. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -0
  19. package/pkg/dist/modes/interactive/theme/theme.js +949 -0
  20. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -0
  21. package/pkg/package.json +8 -0
  22. package/scripts/postinstall.js +45 -0
  23. package/src/resources/AGENTS.md +108 -0
  24. package/src/resources/KATA-WORKFLOW.md +661 -0
  25. package/src/resources/agents/researcher.md +29 -0
  26. package/src/resources/agents/scout.md +56 -0
  27. package/src/resources/agents/worker.md +31 -0
  28. package/src/resources/extensions/ask-user-questions.ts +200 -0
  29. package/src/resources/extensions/bg-shell/index.ts +2758 -0
  30. package/src/resources/extensions/browser-tools/BROWSER-TOOLS-V2-PROPOSAL.md +1277 -0
  31. package/src/resources/extensions/browser-tools/core.js +1057 -0
  32. package/src/resources/extensions/browser-tools/index.ts +4916 -0
  33. package/src/resources/extensions/browser-tools/package.json +20 -0
  34. package/src/resources/extensions/context7/index.ts +428 -0
  35. package/src/resources/extensions/context7/package.json +11 -0
  36. package/src/resources/extensions/get-secrets-from-user.ts +352 -0
  37. package/src/resources/extensions/github/formatters.ts +207 -0
  38. package/src/resources/extensions/github/gh-api.ts +537 -0
  39. package/src/resources/extensions/github/index.ts +778 -0
  40. package/src/resources/extensions/kata/activity-log.ts +88 -0
  41. package/src/resources/extensions/kata/auto.ts +2786 -0
  42. package/src/resources/extensions/kata/commands.ts +355 -0
  43. package/src/resources/extensions/kata/crash-recovery.ts +85 -0
  44. package/src/resources/extensions/kata/dashboard-overlay.ts +516 -0
  45. package/src/resources/extensions/kata/docs/preferences-reference.md +103 -0
  46. package/src/resources/extensions/kata/doctor.ts +683 -0
  47. package/src/resources/extensions/kata/files.ts +730 -0
  48. package/src/resources/extensions/kata/gitignore.ts +165 -0
  49. package/src/resources/extensions/kata/guided-flow.ts +976 -0
  50. package/src/resources/extensions/kata/index.ts +556 -0
  51. package/src/resources/extensions/kata/metrics.ts +397 -0
  52. package/src/resources/extensions/kata/observability-validator.ts +408 -0
  53. package/src/resources/extensions/kata/package.json +11 -0
  54. package/src/resources/extensions/kata/paths.ts +346 -0
  55. package/src/resources/extensions/kata/preferences.ts +695 -0
  56. package/src/resources/extensions/kata/prompt-loader.ts +50 -0
  57. package/src/resources/extensions/kata/prompts/complete-milestone.md +25 -0
  58. package/src/resources/extensions/kata/prompts/complete-slice.md +27 -0
  59. package/src/resources/extensions/kata/prompts/discuss.md +151 -0
  60. package/src/resources/extensions/kata/prompts/doctor-heal.md +29 -0
  61. package/src/resources/extensions/kata/prompts/execute-task.md +64 -0
  62. package/src/resources/extensions/kata/prompts/guided-complete-slice.md +1 -0
  63. package/src/resources/extensions/kata/prompts/guided-discuss-milestone.md +3 -0
  64. package/src/resources/extensions/kata/prompts/guided-discuss-slice.md +59 -0
  65. package/src/resources/extensions/kata/prompts/guided-execute-task.md +1 -0
  66. package/src/resources/extensions/kata/prompts/guided-plan-milestone.md +23 -0
  67. package/src/resources/extensions/kata/prompts/guided-plan-slice.md +1 -0
  68. package/src/resources/extensions/kata/prompts/guided-research-slice.md +11 -0
  69. package/src/resources/extensions/kata/prompts/guided-resume-task.md +1 -0
  70. package/src/resources/extensions/kata/prompts/plan-milestone.md +47 -0
  71. package/src/resources/extensions/kata/prompts/plan-slice.md +63 -0
  72. package/src/resources/extensions/kata/prompts/queue.md +85 -0
  73. package/src/resources/extensions/kata/prompts/reassess-roadmap.md +48 -0
  74. package/src/resources/extensions/kata/prompts/replan-slice.md +39 -0
  75. package/src/resources/extensions/kata/prompts/research-milestone.md +37 -0
  76. package/src/resources/extensions/kata/prompts/research-slice.md +28 -0
  77. package/src/resources/extensions/kata/prompts/run-uat.md +109 -0
  78. package/src/resources/extensions/kata/prompts/system.md +341 -0
  79. package/src/resources/extensions/kata/session-forensics.ts +550 -0
  80. package/src/resources/extensions/kata/skill-discovery.ts +137 -0
  81. package/src/resources/extensions/kata/state.ts +509 -0
  82. package/src/resources/extensions/kata/templates/context.md +76 -0
  83. package/src/resources/extensions/kata/templates/decisions.md +8 -0
  84. package/src/resources/extensions/kata/templates/milestone-summary.md +73 -0
  85. package/src/resources/extensions/kata/templates/plan.md +133 -0
  86. package/src/resources/extensions/kata/templates/preferences.md +15 -0
  87. package/src/resources/extensions/kata/templates/project.md +31 -0
  88. package/src/resources/extensions/kata/templates/reassessment.md +28 -0
  89. package/src/resources/extensions/kata/templates/requirements.md +81 -0
  90. package/src/resources/extensions/kata/templates/research.md +46 -0
  91. package/src/resources/extensions/kata/templates/roadmap.md +118 -0
  92. package/src/resources/extensions/kata/templates/slice-context.md +58 -0
  93. package/src/resources/extensions/kata/templates/slice-summary.md +99 -0
  94. package/src/resources/extensions/kata/templates/state.md +19 -0
  95. package/src/resources/extensions/kata/templates/task-plan.md +52 -0
  96. package/src/resources/extensions/kata/templates/task-summary.md +57 -0
  97. package/src/resources/extensions/kata/templates/uat.md +54 -0
  98. package/src/resources/extensions/kata/tests/activity-log-prune.test.ts +327 -0
  99. package/src/resources/extensions/kata/tests/auto-preflight.test.ts +97 -0
  100. package/src/resources/extensions/kata/tests/auto-supervisor.test.mjs +53 -0
  101. package/src/resources/extensions/kata/tests/complete-milestone.test.ts +317 -0
  102. package/src/resources/extensions/kata/tests/cost-projection.test.ts +160 -0
  103. package/src/resources/extensions/kata/tests/derive-state-deps.test.ts +477 -0
  104. package/src/resources/extensions/kata/tests/derive-state.test.ts +1013 -0
  105. package/src/resources/extensions/kata/tests/doctor.test.ts +718 -0
  106. package/src/resources/extensions/kata/tests/idle-recovery.test.ts +490 -0
  107. package/src/resources/extensions/kata/tests/metrics-io.test.ts +254 -0
  108. package/src/resources/extensions/kata/tests/metrics.test.ts +217 -0
  109. package/src/resources/extensions/kata/tests/must-have-parser.test.ts +309 -0
  110. package/src/resources/extensions/kata/tests/parsers.test.ts +1257 -0
  111. package/src/resources/extensions/kata/tests/plan-milestone.test.ts +185 -0
  112. package/src/resources/extensions/kata/tests/plan-quality-validator.test.ts +386 -0
  113. package/src/resources/extensions/kata/tests/reassess-prompt.test.ts +208 -0
  114. package/src/resources/extensions/kata/tests/replan-slice.test.ts +686 -0
  115. package/src/resources/extensions/kata/tests/requirements.test.ts +151 -0
  116. package/src/resources/extensions/kata/tests/resolve-ts-hooks.mjs +17 -0
  117. package/src/resources/extensions/kata/tests/resolve-ts.mjs +11 -0
  118. package/src/resources/extensions/kata/tests/run-uat.test.ts +383 -0
  119. package/src/resources/extensions/kata/tests/unit-runtime.test.ts +388 -0
  120. package/src/resources/extensions/kata/tests/workspace-index.test.ts +118 -0
  121. package/src/resources/extensions/kata/tests/worktree.test.ts +222 -0
  122. package/src/resources/extensions/kata/types.ts +159 -0
  123. package/src/resources/extensions/kata/unit-runtime.ts +163 -0
  124. package/src/resources/extensions/kata/workspace-index.ts +203 -0
  125. package/src/resources/extensions/kata/worktree.ts +182 -0
  126. package/src/resources/extensions/mac-tools/index.ts +852 -0
  127. package/src/resources/extensions/mac-tools/swift-cli/Package.swift +22 -0
  128. package/src/resources/extensions/mac-tools/swift-cli/Sources/main.swift +1318 -0
  129. package/src/resources/extensions/search-the-web/cache.ts +78 -0
  130. package/src/resources/extensions/search-the-web/format.ts +258 -0
  131. package/src/resources/extensions/search-the-web/http.ts +238 -0
  132. package/src/resources/extensions/search-the-web/index.ts +68 -0
  133. package/src/resources/extensions/search-the-web/tool-fetch-page.ts +519 -0
  134. package/src/resources/extensions/search-the-web/tool-llm-context.ts +404 -0
  135. package/src/resources/extensions/search-the-web/tool-search.ts +503 -0
  136. package/src/resources/extensions/search-the-web/url-utils.ts +91 -0
  137. package/src/resources/extensions/shared/confirm-ui.ts +126 -0
  138. package/src/resources/extensions/shared/interview-ui.ts +822 -0
  139. package/src/resources/extensions/shared/next-action-ui.ts +235 -0
  140. package/src/resources/extensions/shared/progress-widget.ts +282 -0
  141. package/src/resources/extensions/shared/thinking-widget.ts +107 -0
  142. package/src/resources/extensions/shared/ui.ts +400 -0
  143. package/src/resources/extensions/shared/wizard-ui.ts +551 -0
  144. package/src/resources/extensions/slash-commands/audit.ts +92 -0
  145. package/src/resources/extensions/slash-commands/create-extension.ts +375 -0
  146. package/src/resources/extensions/slash-commands/create-slash-command.ts +280 -0
  147. package/src/resources/extensions/slash-commands/index.ts +12 -0
  148. package/src/resources/extensions/slash-commands/kata-run.ts +34 -0
  149. package/src/resources/extensions/subagent/agents.ts +126 -0
  150. package/src/resources/extensions/subagent/index.ts +1293 -0
  151. package/src/resources/skills/debug-like-expert/SKILL.md +231 -0
  152. package/src/resources/skills/debug-like-expert/references/debugging-mindset.md +253 -0
  153. package/src/resources/skills/debug-like-expert/references/hypothesis-testing.md +373 -0
  154. package/src/resources/skills/debug-like-expert/references/investigation-techniques.md +337 -0
  155. package/src/resources/skills/debug-like-expert/references/verification-patterns.md +425 -0
  156. package/src/resources/skills/debug-like-expert/references/when-to-research.md +361 -0
  157. package/src/resources/skills/frontend-design/SKILL.md +45 -0
  158. package/src/resources/skills/swiftui/SKILL.md +208 -0
  159. package/src/resources/skills/swiftui/references/animations.md +921 -0
  160. package/src/resources/skills/swiftui/references/architecture.md +1561 -0
  161. package/src/resources/skills/swiftui/references/layout-system.md +1186 -0
  162. package/src/resources/skills/swiftui/references/navigation.md +1492 -0
  163. package/src/resources/skills/swiftui/references/networking-async.md +214 -0
  164. package/src/resources/skills/swiftui/references/performance.md +1706 -0
  165. package/src/resources/skills/swiftui/references/platform-integration.md +204 -0
  166. package/src/resources/skills/swiftui/references/state-management.md +1443 -0
  167. package/src/resources/skills/swiftui/references/swiftdata.md +297 -0
  168. package/src/resources/skills/swiftui/references/testing-debugging.md +247 -0
  169. package/src/resources/skills/swiftui/references/uikit-appkit-interop.md +218 -0
  170. package/src/resources/skills/swiftui/workflows/add-feature.md +191 -0
  171. package/src/resources/skills/swiftui/workflows/build-new-app.md +311 -0
  172. package/src/resources/skills/swiftui/workflows/debug-swiftui.md +192 -0
  173. package/src/resources/skills/swiftui/workflows/optimize-performance.md +197 -0
  174. package/src/resources/skills/swiftui/workflows/ship-app.md +203 -0
  175. package/src/resources/skills/swiftui/workflows/write-tests.md +235 -0
  176. package/dist/commands/task.d.ts +0 -9
  177. package/dist/commands/task.d.ts.map +0 -1
  178. package/dist/commands/task.js +0 -129
  179. package/dist/commands/task.js.map +0 -1
  180. package/dist/commands/task.test.d.ts +0 -2
  181. package/dist/commands/task.test.d.ts.map +0 -1
  182. package/dist/commands/task.test.js +0 -169
  183. package/dist/commands/task.test.js.map +0 -1
  184. package/dist/e2e/task-e2e.test.d.ts +0 -2
  185. package/dist/e2e/task-e2e.test.d.ts.map +0 -1
  186. package/dist/e2e/task-e2e.test.js +0 -173
  187. package/dist/e2e/task-e2e.test.js.map +0 -1
  188. package/dist/index.d.ts +0 -3
  189. package/dist/index.d.ts.map +0 -1
  190. package/dist/index.js +0 -93
  191. package/dist/index.js.map +0 -1
  192. package/dist/slug.d.ts +0 -2
  193. package/dist/slug.d.ts.map +0 -1
  194. package/dist/slug.js +0 -12
  195. package/dist/slug.js.map +0 -1
  196. package/dist/slug.test.d.ts +0 -2
  197. package/dist/slug.test.d.ts.map +0 -1
  198. package/dist/slug.test.js +0 -32
  199. package/dist/slug.test.js.map +0 -1
@@ -0,0 +1,556 @@
1
+ /**
2
+ * Kata Extension — /kata
3
+ *
4
+ * One command, one wizard. Reads state from disk, shows contextual options,
5
+ * dispatches through KATA-WORKFLOW.md. The LLM does the rest.
6
+ *
7
+ * Auto-mode: /kata auto loops fresh sessions until milestone complete.
8
+ *
9
+ * Commands:
10
+ * /kata — contextual wizard (smart entry point)
11
+ * /kata auto — start auto-mode (fresh session per unit)
12
+ * /kata stop — stop auto-mode gracefully
13
+ * /kata status — progress dashboard
14
+ *
15
+ * Hooks:
16
+ * before_agent_start — inject Kata system context for Kata projects
17
+ * agent_end — auto-mode advancement
18
+ * session_before_compact — save continue.md OR block during auto
19
+ */
20
+
21
+ import type {
22
+ ExtensionAPI,
23
+ ExtensionContext,
24
+ } from "@mariozechner/pi-coding-agent";
25
+
26
+ import { registerKataCommand } from "./commands.js";
27
+ import {
28
+ saveFile,
29
+ formatContinue,
30
+ loadFile,
31
+ parseContinue,
32
+ parseSummary,
33
+ } from "./files.js";
34
+ import { loadPrompt } from "./prompt-loader.js";
35
+ import { deriveState } from "./state.js";
36
+ import {
37
+ isAutoActive,
38
+ isAutoPaused,
39
+ handleAgentEnd,
40
+ pauseAuto,
41
+ getAutoDashboardData,
42
+ } from "./auto.js";
43
+ import { saveActivityLog } from "./activity-log.js";
44
+ import { checkAutoStartAfterDiscuss } from "./guided-flow.js";
45
+ import { KataDashboardOverlay } from "./dashboard-overlay.js";
46
+ import {
47
+ loadEffectiveKataPreferences,
48
+ renderPreferencesForSystemPrompt,
49
+ resolveAllSkillReferences,
50
+ } from "./preferences.js";
51
+ import {
52
+ hasSkillSnapshot,
53
+ detectNewSkills,
54
+ formatSkillsXml,
55
+ } from "./skill-discovery.js";
56
+ import {
57
+ resolveSlicePath,
58
+ resolveSliceFile,
59
+ resolveTaskFile,
60
+ resolveTaskFiles,
61
+ resolveTasksDir,
62
+ relSliceFile,
63
+ relSlicePath,
64
+ relTaskFile,
65
+ buildSliceFileName,
66
+ kataRoot,
67
+ } from "./paths.js";
68
+ import { Key } from "@mariozechner/pi-tui";
69
+ import { join } from "node:path";
70
+ import { existsSync } from "node:fs";
71
+ import { Text } from "@mariozechner/pi-tui";
72
+
73
+ // ── ASCII logo ────────────────────────────────────────────────────────────
74
+ const KATA_LOGO_LINES = [
75
+ " ██╗ ██╗ █████╗ ████████╗ █████╗ ",
76
+ " ██║ ██╔╝██╔══██╗╚══██╔══╝██╔══██╗",
77
+ " █████╔╝ ███████║ ██║ ███████║",
78
+ " ██╔═██╗ ██╔══██║ ██║ ██╔══██║",
79
+ " ██║ ██╗██║ ██║ ██║ ██║ ██║",
80
+ " ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝",
81
+ ];
82
+
83
+ export default function (pi: ExtensionAPI) {
84
+ registerKataCommand(pi);
85
+
86
+ // ── session_start: render branded Kata header ───────────────────────────
87
+ pi.on("session_start", async (_event, ctx) => {
88
+ const theme = ctx.ui.theme;
89
+ const version = process.env.KATA_VERSION || "0.0.0";
90
+
91
+ const logoText = KATA_LOGO_LINES.map((line) =>
92
+ theme.fg("accent", line),
93
+ ).join("\n");
94
+ const titleLine = ` ${theme.bold("Kata CLI")} ${theme.fg("dim", `v${version}`)}`;
95
+
96
+ const headerContent = `${logoText}\n${titleLine}`;
97
+ ctx.ui.setHeader((_ui, _theme) => new Text(headerContent, 1, 0));
98
+ });
99
+
100
+ // ── Ctrl+Alt+G shortcut — Kata dashboard overlay ────────────────────────
101
+ pi.registerShortcut(Key.ctrlAlt("g"), {
102
+ description: "Open Kata dashboard",
103
+ handler: async (ctx) => {
104
+ // Only show if .kata/ exists
105
+ if (!existsSync(join(process.cwd(), ".kata"))) {
106
+ ctx.ui.notify("No .kata/ directory found. Run /kata to start.", "info");
107
+ return;
108
+ }
109
+
110
+ await ctx.ui.custom<void>(
111
+ (tui, theme, _kb, done) => {
112
+ return new KataDashboardOverlay(tui, theme, () => done());
113
+ },
114
+ {
115
+ overlay: true,
116
+ overlayOptions: {
117
+ width: "90%",
118
+ minWidth: 80,
119
+ maxHeight: "92%",
120
+ anchor: "center",
121
+ },
122
+ },
123
+ );
124
+ },
125
+ });
126
+
127
+ // ── before_agent_start: inject Kata contract into true system prompt ─────
128
+ pi.on("before_agent_start", async (event, ctx: ExtensionContext) => {
129
+ if (!existsSync(join(process.cwd(), ".kata"))) return;
130
+
131
+ const systemContent = loadPrompt("system");
132
+ const loadedPreferences = loadEffectiveKataPreferences();
133
+ let preferenceBlock = "";
134
+ if (loadedPreferences) {
135
+ const cwd = process.cwd();
136
+ const report = resolveAllSkillReferences(
137
+ loadedPreferences.preferences,
138
+ cwd,
139
+ );
140
+ preferenceBlock = `\n\n${renderPreferencesForSystemPrompt(loadedPreferences.preferences, report.resolutions)}`;
141
+
142
+ // Emit warnings for unresolved skill references
143
+ if (report.warnings.length > 0) {
144
+ ctx.ui.notify(
145
+ `Kata skill preferences: ${report.warnings.length} unresolved skill${report.warnings.length === 1 ? "" : "s"}: ${report.warnings.join(", ")}`,
146
+ "warning",
147
+ );
148
+ }
149
+ }
150
+
151
+ // Detect skills installed during this auto-mode session
152
+ let newSkillsBlock = "";
153
+ if (hasSkillSnapshot()) {
154
+ const newSkills = detectNewSkills();
155
+ if (newSkills.length > 0) {
156
+ newSkillsBlock = formatSkillsXml(newSkills);
157
+ }
158
+ }
159
+
160
+ const injection = await buildGuidedExecuteContextInjection(
161
+ event.prompt,
162
+ process.cwd(),
163
+ );
164
+
165
+ return {
166
+ systemPrompt: `${event.systemPrompt}\n\n[SYSTEM CONTEXT — Kata]\n\n${systemContent}${preferenceBlock}${newSkillsBlock}`,
167
+ ...(injection
168
+ ? {
169
+ message: {
170
+ customType: "kata-guided-context",
171
+ content: injection,
172
+ display: false,
173
+ },
174
+ }
175
+ : {}),
176
+ };
177
+ });
178
+
179
+ // ── agent_end: auto-mode advancement or auto-start after discuss ───────────
180
+ pi.on("agent_end", async (event, ctx: ExtensionContext) => {
181
+ // If discuss phase just finished, start auto-mode
182
+ if (checkAutoStartAfterDiscuss()) return;
183
+
184
+ // If auto-mode is already running, advance to next unit
185
+ if (!isAutoActive()) return;
186
+
187
+ // If the agent was aborted (user pressed Escape), pause auto-mode
188
+ // instead of advancing. This preserves the conversation so the user
189
+ // can inspect what happened, interact with the agent, or resume.
190
+ const lastMsg = event.messages[event.messages.length - 1];
191
+ if (
192
+ lastMsg &&
193
+ "stopReason" in lastMsg &&
194
+ lastMsg.stopReason === "aborted"
195
+ ) {
196
+ await pauseAuto(ctx, pi);
197
+ return;
198
+ }
199
+
200
+ await handleAgentEnd(ctx, pi);
201
+ });
202
+
203
+ // ── session_before_compact ────────────────────────────────────────────────
204
+ pi.on("session_before_compact", async (_event, _ctx: ExtensionContext) => {
205
+ // Block compaction during auto-mode — each unit is a fresh session
206
+ // Also block during paused state — context is valuable for the user
207
+ if (isAutoActive() || isAutoPaused()) {
208
+ return { cancel: true };
209
+ }
210
+
211
+ const basePath = process.cwd();
212
+ const state = await deriveState(basePath);
213
+
214
+ // Only save continue.md if we're actively executing a task
215
+ if (!state.activeMilestone || !state.activeSlice || !state.activeTask)
216
+ return;
217
+ if (state.phase !== "executing") return;
218
+
219
+ const sDir = resolveSlicePath(
220
+ basePath,
221
+ state.activeMilestone.id,
222
+ state.activeSlice.id,
223
+ );
224
+ if (!sDir) return;
225
+
226
+ // Check for existing continue file (new naming or legacy)
227
+ const existingFile = resolveSliceFile(
228
+ basePath,
229
+ state.activeMilestone.id,
230
+ state.activeSlice.id,
231
+ "CONTINUE",
232
+ );
233
+ if (existingFile && (await loadFile(existingFile))) return;
234
+ const legacyContinue = join(sDir, "continue.md");
235
+ if (await loadFile(legacyContinue)) return;
236
+
237
+ const continuePath = join(
238
+ sDir,
239
+ buildSliceFileName(state.activeSlice.id, "CONTINUE"),
240
+ );
241
+
242
+ const continueData = {
243
+ frontmatter: {
244
+ milestone: state.activeMilestone.id,
245
+ slice: state.activeSlice.id,
246
+ task: state.activeTask.id,
247
+ step: 0,
248
+ totalSteps: 0,
249
+ status: "compacted" as const,
250
+ savedAt: new Date().toISOString(),
251
+ },
252
+ completedWork: `Task ${state.activeTask.id} (${state.activeTask.title}) was in progress when compaction occurred.`,
253
+ remainingWork: "Check the task plan for remaining steps.",
254
+ decisions: "Check task summary files for prior decisions.",
255
+ context: "Session was auto-compacted by Pi. Resume with /kata.",
256
+ nextAction: `Resume task ${state.activeTask.id}: ${state.activeTask.title}.`,
257
+ };
258
+
259
+ await saveFile(continuePath, formatContinue(continueData));
260
+ });
261
+
262
+ // ── session_shutdown: save activity log on Ctrl+C / SIGTERM ─────────────
263
+ pi.on("session_shutdown", async (_event, ctx: ExtensionContext) => {
264
+ if (!isAutoActive() && !isAutoPaused()) return;
265
+
266
+ // Save the current session — the lock file stays on disk
267
+ // so the next /kata auto knows it was interrupted
268
+ const dash = getAutoDashboardData();
269
+ if (dash.currentUnit) {
270
+ saveActivityLog(
271
+ ctx,
272
+ dash.basePath,
273
+ dash.currentUnit.type,
274
+ dash.currentUnit.id,
275
+ );
276
+ }
277
+ });
278
+ }
279
+
280
+ async function buildGuidedExecuteContextInjection(
281
+ prompt: string,
282
+ basePath: string,
283
+ ): Promise<string | null> {
284
+ const executeMatch = prompt.match(
285
+ /Execute the next task:\s+(T\d+)\s+\("([^"]+)"\)\s+in slice\s+(S\d+)\s+of milestone\s+(M\d+)/i,
286
+ );
287
+ if (executeMatch) {
288
+ const [, taskId, taskTitle, sliceId, milestoneId] = executeMatch;
289
+ return buildTaskExecutionContextInjection(
290
+ basePath,
291
+ milestoneId,
292
+ sliceId,
293
+ taskId,
294
+ taskTitle,
295
+ );
296
+ }
297
+
298
+ const resumeMatch = prompt.match(
299
+ /Resume interrupted work\.[\s\S]*?slice\s+(S\d+)\s+of milestone\s+(M\d+)/i,
300
+ );
301
+ if (resumeMatch) {
302
+ const [, sliceId, milestoneId] = resumeMatch;
303
+ const state = await deriveState(basePath);
304
+ if (
305
+ state.activeMilestone?.id === milestoneId &&
306
+ state.activeSlice?.id === sliceId &&
307
+ state.activeTask
308
+ ) {
309
+ return buildTaskExecutionContextInjection(
310
+ basePath,
311
+ milestoneId,
312
+ sliceId,
313
+ state.activeTask.id,
314
+ state.activeTask.title,
315
+ );
316
+ }
317
+ }
318
+
319
+ return null;
320
+ }
321
+
322
+ async function buildTaskExecutionContextInjection(
323
+ basePath: string,
324
+ milestoneId: string,
325
+ sliceId: string,
326
+ taskId: string,
327
+ taskTitle: string,
328
+ ): Promise<string> {
329
+ const taskPlanPath = resolveTaskFile(
330
+ basePath,
331
+ milestoneId,
332
+ sliceId,
333
+ taskId,
334
+ "PLAN",
335
+ );
336
+ const taskPlanRelPath = relTaskFile(
337
+ basePath,
338
+ milestoneId,
339
+ sliceId,
340
+ taskId,
341
+ "PLAN",
342
+ );
343
+ const taskPlanContent = taskPlanPath ? await loadFile(taskPlanPath) : null;
344
+ const taskPlanInline = taskPlanContent
345
+ ? [
346
+ "## Inlined Task Plan (authoritative local execution contract)",
347
+ `Source: \`${taskPlanRelPath}\``,
348
+ "",
349
+ taskPlanContent.trim(),
350
+ ].join("\n")
351
+ : [
352
+ "## Inlined Task Plan (authoritative local execution contract)",
353
+ `Task plan not found at dispatch time. Read \`${taskPlanRelPath}\` before executing.`,
354
+ ].join("\n");
355
+
356
+ const slicePlanPath = resolveSliceFile(
357
+ basePath,
358
+ milestoneId,
359
+ sliceId,
360
+ "PLAN",
361
+ );
362
+ const slicePlanRelPath = relSliceFile(basePath, milestoneId, sliceId, "PLAN");
363
+ const slicePlanContent = slicePlanPath ? await loadFile(slicePlanPath) : null;
364
+ const slicePlanExcerpt = extractSliceExecutionExcerpt(
365
+ slicePlanContent,
366
+ slicePlanRelPath,
367
+ );
368
+
369
+ const priorTaskLines = await buildCarryForwardLines(
370
+ basePath,
371
+ milestoneId,
372
+ sliceId,
373
+ taskId,
374
+ );
375
+ const resumeSection = await buildResumeSection(
376
+ basePath,
377
+ milestoneId,
378
+ sliceId,
379
+ );
380
+
381
+ return [
382
+ "[Kata Guided Execute Context]",
383
+ "Use this injected context as startup context for guided task execution. Treat the inlined task plan as the authoritative local execution contract. Use source artifacts to verify details and run checks.",
384
+ "",
385
+ resumeSection,
386
+ "",
387
+ "## Carry-Forward Context",
388
+ ...priorTaskLines,
389
+ "",
390
+ taskPlanInline,
391
+ "",
392
+ slicePlanExcerpt,
393
+ "",
394
+ "## Backing Source Artifacts",
395
+ `- Slice plan: \`${slicePlanRelPath}\``,
396
+ `- Task plan source: \`${taskPlanRelPath}\``,
397
+ ].join("\n");
398
+ }
399
+
400
+ async function buildCarryForwardLines(
401
+ basePath: string,
402
+ milestoneId: string,
403
+ sliceId: string,
404
+ taskId: string,
405
+ ): Promise<string[]> {
406
+ const tDir = resolveTasksDir(basePath, milestoneId, sliceId);
407
+ if (!tDir) return ["- No prior task summaries in this slice."];
408
+
409
+ const currentNum = parseInt(taskId.replace(/^T/, ""), 10);
410
+ const sRel = relSlicePath(basePath, milestoneId, sliceId);
411
+ const summaryFiles = resolveTaskFiles(tDir, "SUMMARY")
412
+ .filter((file) => parseInt(file.replace(/^T/, ""), 10) < currentNum)
413
+ .sort();
414
+
415
+ if (summaryFiles.length === 0)
416
+ return ["- No prior task summaries in this slice."];
417
+
418
+ const lines = await Promise.all(
419
+ summaryFiles.map(async (file) => {
420
+ const absPath = join(tDir, file);
421
+ const content = await loadFile(absPath);
422
+ const relPath = `${sRel}/tasks/${file}`;
423
+ if (!content) return `- \`${relPath}\``;
424
+
425
+ const summary = parseSummary(content);
426
+ const provided = summary.frontmatter.provides.slice(0, 2).join("; ");
427
+ const decisions = summary.frontmatter.key_decisions
428
+ .slice(0, 2)
429
+ .join("; ");
430
+ const patterns = summary.frontmatter.patterns_established
431
+ .slice(0, 2)
432
+ .join("; ");
433
+ const diagnostics = extractMarkdownSection(content, "Diagnostics");
434
+
435
+ const parts = [summary.title || relPath];
436
+ if (summary.oneLiner) parts.push(summary.oneLiner);
437
+ if (provided) parts.push(`provides: ${provided}`);
438
+ if (decisions) parts.push(`decisions: ${decisions}`);
439
+ if (patterns) parts.push(`patterns: ${patterns}`);
440
+ if (diagnostics) parts.push(`diagnostics: ${oneLine(diagnostics)}`);
441
+
442
+ return `- \`${relPath}\` — ${parts.join(" | ")}`;
443
+ }),
444
+ );
445
+
446
+ return lines;
447
+ }
448
+
449
+ async function buildResumeSection(
450
+ basePath: string,
451
+ milestoneId: string,
452
+ sliceId: string,
453
+ ): Promise<string> {
454
+ const continueFile = resolveSliceFile(
455
+ basePath,
456
+ milestoneId,
457
+ sliceId,
458
+ "CONTINUE",
459
+ );
460
+ const legacyDir = resolveSlicePath(basePath, milestoneId, sliceId);
461
+ const legacyPath = legacyDir ? join(legacyDir, "continue.md") : null;
462
+ const continueContent = continueFile ? await loadFile(continueFile) : null;
463
+ const legacyContent =
464
+ !continueContent && legacyPath ? await loadFile(legacyPath) : null;
465
+ const resolvedContent = continueContent ?? legacyContent;
466
+ const resolvedRelPath = continueContent
467
+ ? relSliceFile(basePath, milestoneId, sliceId, "CONTINUE")
468
+ : legacyPath
469
+ ? `${relSlicePath(basePath, milestoneId, sliceId)}/continue.md`
470
+ : null;
471
+
472
+ if (!resolvedContent || !resolvedRelPath) {
473
+ return [
474
+ "## Resume State",
475
+ "- No continue file present. Start from the top of the task plan.",
476
+ ].join("\n");
477
+ }
478
+
479
+ const cont = parseContinue(resolvedContent);
480
+ const lines = [
481
+ "## Resume State",
482
+ `Source: \`${resolvedRelPath}\``,
483
+ `- Status: ${cont.frontmatter.status || "in_progress"}`,
484
+ ];
485
+
486
+ if (cont.frontmatter.step && cont.frontmatter.totalSteps) {
487
+ lines.push(
488
+ `- Progress: step ${cont.frontmatter.step} of ${cont.frontmatter.totalSteps}`,
489
+ );
490
+ }
491
+ if (cont.completedWork)
492
+ lines.push(`- Completed: ${oneLine(cont.completedWork)}`);
493
+ if (cont.remainingWork)
494
+ lines.push(`- Remaining: ${oneLine(cont.remainingWork)}`);
495
+ if (cont.decisions) lines.push(`- Decisions: ${oneLine(cont.decisions)}`);
496
+ if (cont.nextAction) lines.push(`- Next action: ${oneLine(cont.nextAction)}`);
497
+
498
+ return lines.join("\n");
499
+ }
500
+
501
+ function extractSliceExecutionExcerpt(
502
+ content: string | null,
503
+ relPath: string,
504
+ ): string {
505
+ if (!content) {
506
+ return [
507
+ "## Slice Plan Excerpt",
508
+ `Slice plan not found at dispatch time. Read \`${relPath}\` before running slice-level verification.`,
509
+ ].join("\n");
510
+ }
511
+
512
+ const lines = content.split("\n");
513
+ const goalLine = lines.find((line) => line.startsWith("**Goal:**"))?.trim();
514
+ const demoLine = lines.find((line) => line.startsWith("**Demo:**"))?.trim();
515
+ const verification = extractMarkdownSection(content, "Verification");
516
+ const observability = extractMarkdownSection(
517
+ content,
518
+ "Observability / Diagnostics",
519
+ );
520
+
521
+ const parts = ["## Slice Plan Excerpt", `Source: \`${relPath}\``];
522
+ if (goalLine) parts.push(goalLine);
523
+ if (demoLine) parts.push(demoLine);
524
+ if (verification)
525
+ parts.push("", "### Slice Verification", verification.trim());
526
+ if (observability)
527
+ parts.push(
528
+ "",
529
+ "### Slice Observability / Diagnostics",
530
+ observability.trim(),
531
+ );
532
+ return parts.join("\n");
533
+ }
534
+
535
+ function extractMarkdownSection(
536
+ content: string,
537
+ heading: string,
538
+ ): string | null {
539
+ const match = new RegExp(`^## ${escapeRegExp(heading)}\\s*$`, "m").exec(
540
+ content,
541
+ );
542
+ if (!match) return null;
543
+ const start = match.index + match[0].length;
544
+ const rest = content.slice(start);
545
+ const nextHeading = rest.match(/^##\s+/m);
546
+ const end = nextHeading?.index ?? rest.length;
547
+ return rest.slice(0, end).trim();
548
+ }
549
+
550
+ function escapeRegExp(value: string): string {
551
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
552
+ }
553
+
554
+ function oneLine(text: string): string {
555
+ return text.replace(/\s+/g, " ").trim();
556
+ }