@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.
- package/LICENSE +21 -0
- package/README.md +156 -0
- package/dist/app-paths.d.ts +4 -0
- package/dist/app-paths.js +6 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +56 -0
- package/dist/loader.d.ts +2 -0
- package/dist/loader.js +95 -0
- package/dist/resource-loader.d.ts +18 -0
- package/dist/resource-loader.js +50 -0
- package/dist/wizard.d.ts +15 -0
- package/dist/wizard.js +159 -0
- package/package.json +50 -21
- package/pkg/dist/modes/interactive/theme/dark.json +85 -0
- package/pkg/dist/modes/interactive/theme/light.json +84 -0
- package/pkg/dist/modes/interactive/theme/theme-schema.json +335 -0
- package/pkg/dist/modes/interactive/theme/theme.d.ts +78 -0
- package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -0
- package/pkg/dist/modes/interactive/theme/theme.js +949 -0
- package/pkg/dist/modes/interactive/theme/theme.js.map +1 -0
- package/pkg/package.json +8 -0
- package/scripts/postinstall.js +45 -0
- package/src/resources/AGENTS.md +108 -0
- package/src/resources/KATA-WORKFLOW.md +661 -0
- package/src/resources/agents/researcher.md +29 -0
- package/src/resources/agents/scout.md +56 -0
- package/src/resources/agents/worker.md +31 -0
- package/src/resources/extensions/ask-user-questions.ts +200 -0
- package/src/resources/extensions/bg-shell/index.ts +2758 -0
- package/src/resources/extensions/browser-tools/BROWSER-TOOLS-V2-PROPOSAL.md +1277 -0
- package/src/resources/extensions/browser-tools/core.js +1057 -0
- package/src/resources/extensions/browser-tools/index.ts +4916 -0
- package/src/resources/extensions/browser-tools/package.json +20 -0
- package/src/resources/extensions/context7/index.ts +428 -0
- package/src/resources/extensions/context7/package.json +11 -0
- package/src/resources/extensions/get-secrets-from-user.ts +352 -0
- package/src/resources/extensions/github/formatters.ts +207 -0
- package/src/resources/extensions/github/gh-api.ts +537 -0
- package/src/resources/extensions/github/index.ts +778 -0
- package/src/resources/extensions/kata/activity-log.ts +88 -0
- package/src/resources/extensions/kata/auto.ts +2786 -0
- package/src/resources/extensions/kata/commands.ts +355 -0
- package/src/resources/extensions/kata/crash-recovery.ts +85 -0
- package/src/resources/extensions/kata/dashboard-overlay.ts +516 -0
- package/src/resources/extensions/kata/docs/preferences-reference.md +103 -0
- package/src/resources/extensions/kata/doctor.ts +683 -0
- package/src/resources/extensions/kata/files.ts +730 -0
- package/src/resources/extensions/kata/gitignore.ts +165 -0
- package/src/resources/extensions/kata/guided-flow.ts +976 -0
- package/src/resources/extensions/kata/index.ts +556 -0
- package/src/resources/extensions/kata/metrics.ts +397 -0
- package/src/resources/extensions/kata/observability-validator.ts +408 -0
- package/src/resources/extensions/kata/package.json +11 -0
- package/src/resources/extensions/kata/paths.ts +346 -0
- package/src/resources/extensions/kata/preferences.ts +695 -0
- package/src/resources/extensions/kata/prompt-loader.ts +50 -0
- package/src/resources/extensions/kata/prompts/complete-milestone.md +25 -0
- package/src/resources/extensions/kata/prompts/complete-slice.md +27 -0
- package/src/resources/extensions/kata/prompts/discuss.md +151 -0
- package/src/resources/extensions/kata/prompts/doctor-heal.md +29 -0
- package/src/resources/extensions/kata/prompts/execute-task.md +64 -0
- package/src/resources/extensions/kata/prompts/guided-complete-slice.md +1 -0
- package/src/resources/extensions/kata/prompts/guided-discuss-milestone.md +3 -0
- package/src/resources/extensions/kata/prompts/guided-discuss-slice.md +59 -0
- package/src/resources/extensions/kata/prompts/guided-execute-task.md +1 -0
- package/src/resources/extensions/kata/prompts/guided-plan-milestone.md +23 -0
- package/src/resources/extensions/kata/prompts/guided-plan-slice.md +1 -0
- package/src/resources/extensions/kata/prompts/guided-research-slice.md +11 -0
- package/src/resources/extensions/kata/prompts/guided-resume-task.md +1 -0
- package/src/resources/extensions/kata/prompts/plan-milestone.md +47 -0
- package/src/resources/extensions/kata/prompts/plan-slice.md +63 -0
- package/src/resources/extensions/kata/prompts/queue.md +85 -0
- package/src/resources/extensions/kata/prompts/reassess-roadmap.md +48 -0
- package/src/resources/extensions/kata/prompts/replan-slice.md +39 -0
- package/src/resources/extensions/kata/prompts/research-milestone.md +37 -0
- package/src/resources/extensions/kata/prompts/research-slice.md +28 -0
- package/src/resources/extensions/kata/prompts/run-uat.md +109 -0
- package/src/resources/extensions/kata/prompts/system.md +341 -0
- package/src/resources/extensions/kata/session-forensics.ts +550 -0
- package/src/resources/extensions/kata/skill-discovery.ts +137 -0
- package/src/resources/extensions/kata/state.ts +509 -0
- package/src/resources/extensions/kata/templates/context.md +76 -0
- package/src/resources/extensions/kata/templates/decisions.md +8 -0
- package/src/resources/extensions/kata/templates/milestone-summary.md +73 -0
- package/src/resources/extensions/kata/templates/plan.md +133 -0
- package/src/resources/extensions/kata/templates/preferences.md +15 -0
- package/src/resources/extensions/kata/templates/project.md +31 -0
- package/src/resources/extensions/kata/templates/reassessment.md +28 -0
- package/src/resources/extensions/kata/templates/requirements.md +81 -0
- package/src/resources/extensions/kata/templates/research.md +46 -0
- package/src/resources/extensions/kata/templates/roadmap.md +118 -0
- package/src/resources/extensions/kata/templates/slice-context.md +58 -0
- package/src/resources/extensions/kata/templates/slice-summary.md +99 -0
- package/src/resources/extensions/kata/templates/state.md +19 -0
- package/src/resources/extensions/kata/templates/task-plan.md +52 -0
- package/src/resources/extensions/kata/templates/task-summary.md +57 -0
- package/src/resources/extensions/kata/templates/uat.md +54 -0
- package/src/resources/extensions/kata/tests/activity-log-prune.test.ts +327 -0
- package/src/resources/extensions/kata/tests/auto-preflight.test.ts +97 -0
- package/src/resources/extensions/kata/tests/auto-supervisor.test.mjs +53 -0
- package/src/resources/extensions/kata/tests/complete-milestone.test.ts +317 -0
- package/src/resources/extensions/kata/tests/cost-projection.test.ts +160 -0
- package/src/resources/extensions/kata/tests/derive-state-deps.test.ts +477 -0
- package/src/resources/extensions/kata/tests/derive-state.test.ts +1013 -0
- package/src/resources/extensions/kata/tests/doctor.test.ts +718 -0
- package/src/resources/extensions/kata/tests/idle-recovery.test.ts +490 -0
- package/src/resources/extensions/kata/tests/metrics-io.test.ts +254 -0
- package/src/resources/extensions/kata/tests/metrics.test.ts +217 -0
- package/src/resources/extensions/kata/tests/must-have-parser.test.ts +309 -0
- package/src/resources/extensions/kata/tests/parsers.test.ts +1257 -0
- package/src/resources/extensions/kata/tests/plan-milestone.test.ts +185 -0
- package/src/resources/extensions/kata/tests/plan-quality-validator.test.ts +386 -0
- package/src/resources/extensions/kata/tests/reassess-prompt.test.ts +208 -0
- package/src/resources/extensions/kata/tests/replan-slice.test.ts +686 -0
- package/src/resources/extensions/kata/tests/requirements.test.ts +151 -0
- package/src/resources/extensions/kata/tests/resolve-ts-hooks.mjs +17 -0
- package/src/resources/extensions/kata/tests/resolve-ts.mjs +11 -0
- package/src/resources/extensions/kata/tests/run-uat.test.ts +383 -0
- package/src/resources/extensions/kata/tests/unit-runtime.test.ts +388 -0
- package/src/resources/extensions/kata/tests/workspace-index.test.ts +118 -0
- package/src/resources/extensions/kata/tests/worktree.test.ts +222 -0
- package/src/resources/extensions/kata/types.ts +159 -0
- package/src/resources/extensions/kata/unit-runtime.ts +163 -0
- package/src/resources/extensions/kata/workspace-index.ts +203 -0
- package/src/resources/extensions/kata/worktree.ts +182 -0
- package/src/resources/extensions/mac-tools/index.ts +852 -0
- package/src/resources/extensions/mac-tools/swift-cli/Package.swift +22 -0
- package/src/resources/extensions/mac-tools/swift-cli/Sources/main.swift +1318 -0
- package/src/resources/extensions/search-the-web/cache.ts +78 -0
- package/src/resources/extensions/search-the-web/format.ts +258 -0
- package/src/resources/extensions/search-the-web/http.ts +238 -0
- package/src/resources/extensions/search-the-web/index.ts +68 -0
- package/src/resources/extensions/search-the-web/tool-fetch-page.ts +519 -0
- package/src/resources/extensions/search-the-web/tool-llm-context.ts +404 -0
- package/src/resources/extensions/search-the-web/tool-search.ts +503 -0
- package/src/resources/extensions/search-the-web/url-utils.ts +91 -0
- package/src/resources/extensions/shared/confirm-ui.ts +126 -0
- package/src/resources/extensions/shared/interview-ui.ts +822 -0
- package/src/resources/extensions/shared/next-action-ui.ts +235 -0
- package/src/resources/extensions/shared/progress-widget.ts +282 -0
- package/src/resources/extensions/shared/thinking-widget.ts +107 -0
- package/src/resources/extensions/shared/ui.ts +400 -0
- package/src/resources/extensions/shared/wizard-ui.ts +551 -0
- package/src/resources/extensions/slash-commands/audit.ts +92 -0
- package/src/resources/extensions/slash-commands/create-extension.ts +375 -0
- package/src/resources/extensions/slash-commands/create-slash-command.ts +280 -0
- package/src/resources/extensions/slash-commands/index.ts +12 -0
- package/src/resources/extensions/slash-commands/kata-run.ts +34 -0
- package/src/resources/extensions/subagent/agents.ts +126 -0
- package/src/resources/extensions/subagent/index.ts +1293 -0
- package/src/resources/skills/debug-like-expert/SKILL.md +231 -0
- package/src/resources/skills/debug-like-expert/references/debugging-mindset.md +253 -0
- package/src/resources/skills/debug-like-expert/references/hypothesis-testing.md +373 -0
- package/src/resources/skills/debug-like-expert/references/investigation-techniques.md +337 -0
- package/src/resources/skills/debug-like-expert/references/verification-patterns.md +425 -0
- package/src/resources/skills/debug-like-expert/references/when-to-research.md +361 -0
- package/src/resources/skills/frontend-design/SKILL.md +45 -0
- package/src/resources/skills/swiftui/SKILL.md +208 -0
- package/src/resources/skills/swiftui/references/animations.md +921 -0
- package/src/resources/skills/swiftui/references/architecture.md +1561 -0
- package/src/resources/skills/swiftui/references/layout-system.md +1186 -0
- package/src/resources/skills/swiftui/references/navigation.md +1492 -0
- package/src/resources/skills/swiftui/references/networking-async.md +214 -0
- package/src/resources/skills/swiftui/references/performance.md +1706 -0
- package/src/resources/skills/swiftui/references/platform-integration.md +204 -0
- package/src/resources/skills/swiftui/references/state-management.md +1443 -0
- package/src/resources/skills/swiftui/references/swiftdata.md +297 -0
- package/src/resources/skills/swiftui/references/testing-debugging.md +247 -0
- package/src/resources/skills/swiftui/references/uikit-appkit-interop.md +218 -0
- package/src/resources/skills/swiftui/workflows/add-feature.md +191 -0
- package/src/resources/skills/swiftui/workflows/build-new-app.md +311 -0
- package/src/resources/skills/swiftui/workflows/debug-swiftui.md +192 -0
- package/src/resources/skills/swiftui/workflows/optimize-performance.md +197 -0
- package/src/resources/skills/swiftui/workflows/ship-app.md +203 -0
- package/src/resources/skills/swiftui/workflows/write-tests.md +235 -0
- package/dist/commands/task.d.ts +0 -9
- package/dist/commands/task.d.ts.map +0 -1
- package/dist/commands/task.js +0 -129
- package/dist/commands/task.js.map +0 -1
- package/dist/commands/task.test.d.ts +0 -2
- package/dist/commands/task.test.d.ts.map +0 -1
- package/dist/commands/task.test.js +0 -169
- package/dist/commands/task.test.js.map +0 -1
- package/dist/e2e/task-e2e.test.d.ts +0 -2
- package/dist/e2e/task-e2e.test.d.ts.map +0 -1
- package/dist/e2e/task-e2e.test.js +0 -173
- package/dist/e2e/task-e2e.test.js.map +0 -1
- package/dist/index.d.ts +0 -3
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -93
- package/dist/index.js.map +0 -1
- package/dist/slug.d.ts +0 -2
- package/dist/slug.d.ts.map +0 -1
- package/dist/slug.js +0 -12
- package/dist/slug.js.map +0 -1
- package/dist/slug.test.d.ts +0 -2
- package/dist/slug.test.d.ts.map +0 -1
- package/dist/slug.test.js +0 -32
- 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
|
+
}
|