@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.
- 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,375 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import {
|
|
3
|
+
showInterviewRound,
|
|
4
|
+
type Question,
|
|
5
|
+
type RoundResult,
|
|
6
|
+
} from "../shared/interview-ui.js";
|
|
7
|
+
|
|
8
|
+
export default function createExtension(pi: ExtensionAPI) {
|
|
9
|
+
pi.registerCommand("create-extension", {
|
|
10
|
+
description:
|
|
11
|
+
"Scaffold a new pi extension with interview-driven context gathering",
|
|
12
|
+
async handler(args, ctx) {
|
|
13
|
+
const inlineName = (typeof args === "string" ? args : "").trim();
|
|
14
|
+
|
|
15
|
+
// ── Interview — always runs first ─────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
const questions: Question[] = [
|
|
18
|
+
...(!inlineName
|
|
19
|
+
? [
|
|
20
|
+
{
|
|
21
|
+
id: "purpose",
|
|
22
|
+
header: "Purpose",
|
|
23
|
+
question: "What should this extension do?",
|
|
24
|
+
options: [
|
|
25
|
+
{
|
|
26
|
+
label: "Add a custom tool",
|
|
27
|
+
description:
|
|
28
|
+
"Register a new tool the LLM can call (like kata_plan, plan_clarify).",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
label: "Add a slash command",
|
|
32
|
+
description:
|
|
33
|
+
"A /command the user types — runs logic, optionally triggers an agent turn.",
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
label: "React to agent events",
|
|
37
|
+
description:
|
|
38
|
+
"Hook into turn_end, agent_end, tool_call, etc. to observe or intercept.",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
label: "Custom TUI component",
|
|
42
|
+
description:
|
|
43
|
+
"Render a widget, overlay, dialog, or custom editor in the terminal UI.",
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
} satisfies Question,
|
|
47
|
+
]
|
|
48
|
+
: []),
|
|
49
|
+
{
|
|
50
|
+
id: "ui",
|
|
51
|
+
header: "UI",
|
|
52
|
+
question: "Does this extension need any custom UI?",
|
|
53
|
+
options: [
|
|
54
|
+
{
|
|
55
|
+
label: "No UI",
|
|
56
|
+
description:
|
|
57
|
+
"Pure logic — no dialogs, widgets, or custom rendering needed.",
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
label: "Dialogs only",
|
|
61
|
+
description:
|
|
62
|
+
"Uses built-in ctx.ui.select / ctx.ui.input / ctx.ui.confirm dialogs.",
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
label: "Status / widget",
|
|
66
|
+
description:
|
|
67
|
+
"Shows a persistent status indicator or footer widget.",
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
label: "Full custom component",
|
|
71
|
+
description:
|
|
72
|
+
"Uses ctx.ui.custom() to render a fully bespoke TUI component.",
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: "events",
|
|
78
|
+
header: "Events",
|
|
79
|
+
question: "Does it need to hook into the agent lifecycle?",
|
|
80
|
+
options: [
|
|
81
|
+
{
|
|
82
|
+
label: "No — standalone",
|
|
83
|
+
description:
|
|
84
|
+
"Runs only when explicitly invoked — no event listeners needed.",
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
label: "Yes — tool_call",
|
|
88
|
+
description:
|
|
89
|
+
"Intercepts or observes tool calls before or after they run.",
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
label: "Yes — turn / session",
|
|
93
|
+
description:
|
|
94
|
+
"Reacts to turn_end, agent_end, session_start, or similar lifecycle events.",
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
label: "Yes — context / prompt",
|
|
98
|
+
description:
|
|
99
|
+
"Modifies the system prompt or filters messages via context / before_agent_start.",
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
id: "persistence",
|
|
105
|
+
header: "State",
|
|
106
|
+
question:
|
|
107
|
+
"Does this extension need to persist state across sessions?",
|
|
108
|
+
options: [
|
|
109
|
+
{
|
|
110
|
+
label: "No state needed",
|
|
111
|
+
description: "Stateless — each invocation is independent.",
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
label: "In-memory only",
|
|
115
|
+
description:
|
|
116
|
+
"Keeps state while the session is running but doesn't survive restarts.",
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
label: "Persisted to session",
|
|
120
|
+
description:
|
|
121
|
+
"Uses pi.appendEntry() to write state into the session JSONL for resume.",
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
id: "complexity",
|
|
127
|
+
header: "Complexity",
|
|
128
|
+
question: "How complex is the implementation?",
|
|
129
|
+
options: [
|
|
130
|
+
{
|
|
131
|
+
label: "Simple — one concern",
|
|
132
|
+
description:
|
|
133
|
+
"A single tool or command, minimal branching, easy to follow.",
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
label: "Moderate — a few parts",
|
|
137
|
+
description:
|
|
138
|
+
"A command plus an event hook, or a tool with custom rendering.",
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
label: "Complex — full extension",
|
|
142
|
+
description:
|
|
143
|
+
"Multiple tools, commands, events, UI, and state working together.",
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
},
|
|
147
|
+
];
|
|
148
|
+
|
|
149
|
+
const result: RoundResult = await showInterviewRound(
|
|
150
|
+
questions,
|
|
151
|
+
{
|
|
152
|
+
progress: "New pi extension · Context",
|
|
153
|
+
reviewHeadline: "Review your choices",
|
|
154
|
+
exitHeadline: "Cancel extension creation?",
|
|
155
|
+
exitLabel: "cancel",
|
|
156
|
+
},
|
|
157
|
+
ctx,
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
// User hit Esc — bail silently
|
|
161
|
+
if (!result.answers || Object.keys(result.answers).length === 0) {
|
|
162
|
+
ctx.ui.notify("Cancelled.", "info");
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ── Resolve name / description ────────────────────────────────────────
|
|
167
|
+
|
|
168
|
+
let extensionDescription = inlineName;
|
|
169
|
+
if (!extensionDescription) {
|
|
170
|
+
const purpose = result.answers["purpose"];
|
|
171
|
+
if (purpose) {
|
|
172
|
+
extensionDescription = purpose.notes?.trim()
|
|
173
|
+
? purpose.notes.trim()
|
|
174
|
+
: Array.isArray(purpose.selected)
|
|
175
|
+
? purpose.selected[0]
|
|
176
|
+
: purpose.selected;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (!extensionDescription) {
|
|
181
|
+
ctx.ui.notify(
|
|
182
|
+
"No description captured — add details in the notes field next time.",
|
|
183
|
+
"warning",
|
|
184
|
+
);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ── Build and send the enriched prompt ────────────────────────────────
|
|
189
|
+
|
|
190
|
+
sendPrompt(extensionDescription, result, pi);
|
|
191
|
+
},
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ─── Prompt builder ───────────────────────────────────────────────────────────
|
|
196
|
+
|
|
197
|
+
function formatAnswers(result: RoundResult): string {
|
|
198
|
+
const lines: string[] = [];
|
|
199
|
+
|
|
200
|
+
const purpose = result.answers["purpose"];
|
|
201
|
+
if (purpose?.notes) {
|
|
202
|
+
lines.push(`- **Extension goal (user's words)**: ${purpose.notes}`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const ui = result.answers["ui"];
|
|
206
|
+
if (ui) {
|
|
207
|
+
const selected = Array.isArray(ui.selected) ? ui.selected[0] : ui.selected;
|
|
208
|
+
lines.push(
|
|
209
|
+
`- **UI needs**: ${selected}${ui.notes ? ` — ${ui.notes}` : ""}`,
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const events = result.answers["events"];
|
|
214
|
+
if (events) {
|
|
215
|
+
const selected = Array.isArray(events.selected)
|
|
216
|
+
? events.selected[0]
|
|
217
|
+
: events.selected;
|
|
218
|
+
lines.push(
|
|
219
|
+
`- **Event hooks**: ${selected}${events.notes ? ` — ${events.notes}` : ""}`,
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const persistence = result.answers["persistence"];
|
|
224
|
+
if (persistence) {
|
|
225
|
+
const selected = Array.isArray(persistence.selected)
|
|
226
|
+
? persistence.selected[0]
|
|
227
|
+
: persistence.selected;
|
|
228
|
+
lines.push(
|
|
229
|
+
`- **State persistence**: ${selected}${persistence.notes ? ` — ${persistence.notes}` : ""}`,
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const complexity = result.answers["complexity"];
|
|
234
|
+
if (complexity) {
|
|
235
|
+
const selected = Array.isArray(complexity.selected)
|
|
236
|
+
? complexity.selected[0]
|
|
237
|
+
: complexity.selected;
|
|
238
|
+
lines.push(
|
|
239
|
+
`- **Complexity**: ${selected}${complexity.notes ? ` — ${complexity.notes}` : ""}`,
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return lines.join("\n");
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function sendPrompt(
|
|
247
|
+
description: string,
|
|
248
|
+
result: RoundResult,
|
|
249
|
+
pi: ExtensionAPI,
|
|
250
|
+
): void {
|
|
251
|
+
const contextSection = `\n## Context gathered from user\n${formatAnswers(result)}\n`;
|
|
252
|
+
|
|
253
|
+
// Determine which doc sections to highlight based on answers
|
|
254
|
+
const uiAnswer = result.answers["ui"];
|
|
255
|
+
const uiSelected = uiAnswer
|
|
256
|
+
? Array.isArray(uiAnswer.selected)
|
|
257
|
+
? uiAnswer.selected[0]
|
|
258
|
+
: uiAnswer.selected
|
|
259
|
+
: "";
|
|
260
|
+
|
|
261
|
+
const eventsAnswer = result.answers["events"];
|
|
262
|
+
const eventsSelected = eventsAnswer
|
|
263
|
+
? Array.isArray(eventsAnswer.selected)
|
|
264
|
+
? eventsAnswer.selected[0]
|
|
265
|
+
: eventsAnswer.selected
|
|
266
|
+
: "";
|
|
267
|
+
|
|
268
|
+
const persistenceAnswer = result.answers["persistence"];
|
|
269
|
+
const persistenceSelected = persistenceAnswer
|
|
270
|
+
? Array.isArray(persistenceAnswer.selected)
|
|
271
|
+
? persistenceAnswer.selected[0]
|
|
272
|
+
: persistenceAnswer.selected
|
|
273
|
+
: "";
|
|
274
|
+
|
|
275
|
+
const docHints: string[] = [
|
|
276
|
+
"- `~/.kata-cli/agent/docs/extending-pi/01-what-are-extensions.md` — capabilities overview",
|
|
277
|
+
"- `~/.kata-cli/agent/docs/extending-pi/03-getting-started.md` — minimal extension, hot reload",
|
|
278
|
+
"- `~/.kata-cli/agent/docs/extending-pi/08-extensioncontext-what-you-can-access.md` — ExtensionContext API",
|
|
279
|
+
"- `~/.kata-cli/agent/docs/extending-pi/09-extensionapi-what-you-can-do.md` — ExtensionAPI: registration, messaging",
|
|
280
|
+
"- `~/.kata-cli/agent/docs/extending-pi/22-key-rules-gotchas.md` — must-read rules before shipping",
|
|
281
|
+
];
|
|
282
|
+
|
|
283
|
+
if (uiSelected.includes("custom component")) {
|
|
284
|
+
docHints.push(
|
|
285
|
+
"- `~/.kata-cli/agent/docs/extending-pi/12-custom-ui-visual-components.md` — dialogs, widgets, overlays",
|
|
286
|
+
);
|
|
287
|
+
docHints.push(
|
|
288
|
+
"- `~/.kata-cli/agent/docs/pi-ui-tui/06-ctx-ui-custom-full-custom-components.md` — ctx.ui.custom() API",
|
|
289
|
+
);
|
|
290
|
+
docHints.push(
|
|
291
|
+
"- `~/.kata-cli/agent/docs/pi-ui-tui/07-built-in-components-the-building-blocks.md` — Text, Box, SelectList",
|
|
292
|
+
);
|
|
293
|
+
docHints.push(
|
|
294
|
+
"- `~/.kata-cli/agent/docs/pi-ui-tui/09-keyboard-input-how-to-handle-keys.md` — Key, matchesKey",
|
|
295
|
+
);
|
|
296
|
+
docHints.push(
|
|
297
|
+
"- `~/.kata-cli/agent/docs/pi-ui-tui/10-line-width-the-cardinal-rule.md` — truncation, width rules",
|
|
298
|
+
);
|
|
299
|
+
docHints.push(
|
|
300
|
+
"- `~/.kata-cli/agent/docs/pi-ui-tui/19-building-a-complete-component-step-by-step.md` — step-by-step guide",
|
|
301
|
+
);
|
|
302
|
+
docHints.push(
|
|
303
|
+
"- `~/.kata-cli/agent/docs/pi-ui-tui/21-common-mistakes-and-how-to-avoid-them.md` — pitfalls",
|
|
304
|
+
);
|
|
305
|
+
} else if (uiSelected.includes("Dialogs")) {
|
|
306
|
+
docHints.push(
|
|
307
|
+
"- `~/.kata-cli/agent/docs/pi-ui-tui/04-built-in-dialog-methods.md` — select, confirm, input, editor",
|
|
308
|
+
);
|
|
309
|
+
} else if (uiSelected.includes("Status")) {
|
|
310
|
+
docHints.push(
|
|
311
|
+
"- `~/.kata-cli/agent/docs/pi-ui-tui/05-persistent-ui-elements.md` — status, widgets, footer, header",
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (uiSelected.includes("tool") || result.answers["purpose"]) {
|
|
316
|
+
docHints.push(
|
|
317
|
+
"- `~/.kata-cli/agent/docs/extending-pi/14-custom-rendering-controlling-what-the-user-sees.md` — renderCall / renderResult",
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (eventsSelected && !eventsSelected.includes("standalone")) {
|
|
322
|
+
docHints.push(
|
|
323
|
+
"- `~/.kata-cli/agent/docs/extending-pi/07-events-the-nervous-system.md` — all events reference",
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (eventsSelected.includes("context / prompt")) {
|
|
328
|
+
docHints.push(
|
|
329
|
+
"- `~/.kata-cli/agent/docs/extending-pi/15-system-prompt-modification.md` — system prompt hooks",
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (persistenceSelected.includes("session")) {
|
|
334
|
+
docHints.push(
|
|
335
|
+
"- `~/.kata-cli/agent/docs/extending-pi/13-state-management-persistence.md` — pi.appendEntry, session state",
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const prompt = `Create a new pi extension based on this description:
|
|
340
|
+
|
|
341
|
+
"${description}"
|
|
342
|
+
${contextSection}
|
|
343
|
+
## Reference documentation
|
|
344
|
+
|
|
345
|
+
Before writing any code, read the relevant docs below. They contain the exact APIs, rules, and patterns for building pi extensions — do not guess or rely on general TypeScript knowledge alone.
|
|
346
|
+
|
|
347
|
+
${docHints.join("\n")}
|
|
348
|
+
|
|
349
|
+
## Output
|
|
350
|
+
|
|
351
|
+
Write the complete implementation as a single self-contained extension file:
|
|
352
|
+
|
|
353
|
+
\`~/.kata-cli/agent/extensions/<kebab-case-name>.ts\`
|
|
354
|
+
|
|
355
|
+
Then register it in the main extensions index:
|
|
356
|
+
|
|
357
|
+
\`~/.kata-cli/agent/extensions/index.ts\` — import and call the new extension's default export alongside existing ones
|
|
358
|
+
|
|
359
|
+
## Rules you must follow exactly
|
|
360
|
+
|
|
361
|
+
- Extension entry point: \`export default function <camelCaseName>(pi: ExtensionAPI): void { ... }\`
|
|
362
|
+
- Import type: \`import type { ExtensionAPI, ExtensionContext, ExtensionCommandContext } from "@mariozechner/pi-coding-agent";\`
|
|
363
|
+
- \`pi\` is the registration surface — call \`pi.registerCommand\`, \`pi.registerTool\`, \`pi.on\`, \`pi.registerShortcut\` inside the default export
|
|
364
|
+
- \`ctx\` (ExtensionCommandContext or ExtensionContext) is passed to handlers and event callbacks — never stored, never assumed available globally
|
|
365
|
+
- To send a message to the agent: \`pi.sendUserMessage("...")\` or \`pi.sendMessage({ content, display }, { triggerTurn })\`
|
|
366
|
+
- To show UI: \`ctx.ui.notify\`, \`ctx.ui.select\`, \`ctx.ui.input\`, \`ctx.ui.confirm\`, \`ctx.ui.custom\`
|
|
367
|
+
- To run shell commands: \`await pi.exec("cmd", ["arg1"])\` — returns \`{ stdout, stderr, exitCode }\`
|
|
368
|
+
- Events use \`pi.on("event_name", async (event, ctx) => { ... })\`
|
|
369
|
+
- No direct file I/O without \`node:fs\` — import it explicitly if needed
|
|
370
|
+
- Read the gotchas file before finalising: \`22-key-rules-gotchas.md\`
|
|
371
|
+
|
|
372
|
+
After writing the files, run \`/reload\` to load the new extension.`;
|
|
373
|
+
|
|
374
|
+
pi.sendUserMessage(prompt);
|
|
375
|
+
}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import {
|
|
3
|
+
showInterviewRound,
|
|
4
|
+
type Question,
|
|
5
|
+
type RoundResult,
|
|
6
|
+
} from "../shared/interview-ui.js";
|
|
7
|
+
|
|
8
|
+
export default function createSlashCommand(pi: ExtensionAPI) {
|
|
9
|
+
pi.registerCommand("create-slash-command", {
|
|
10
|
+
description:
|
|
11
|
+
"Generate a new slash command extension from a plain-English description",
|
|
12
|
+
async handler(args, ctx) {
|
|
13
|
+
const inlineDescription = (typeof args === "string" ? args : "").trim();
|
|
14
|
+
|
|
15
|
+
// ── Interview — always run, no free-text step first ───────────────────
|
|
16
|
+
//
|
|
17
|
+
// If the user already typed a description as args, we skip the "what
|
|
18
|
+
// should it do?" question and go straight to the behaviour questions.
|
|
19
|
+
// Otherwise it's the first question in the round.
|
|
20
|
+
|
|
21
|
+
const questions: Question[] = [
|
|
22
|
+
...(!inlineDescription
|
|
23
|
+
? [
|
|
24
|
+
{
|
|
25
|
+
id: "purpose",
|
|
26
|
+
header: "Purpose",
|
|
27
|
+
question: "What should this slash command do?",
|
|
28
|
+
options: [
|
|
29
|
+
{
|
|
30
|
+
label: "Automate git workflow",
|
|
31
|
+
description:
|
|
32
|
+
"Commit, branch, diff, stash — anything git-related.",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
label: "Send a crafted prompt",
|
|
36
|
+
description:
|
|
37
|
+
"Build a rich context prompt and hand it to the LLM.",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
label: "Run a shell task",
|
|
41
|
+
description:
|
|
42
|
+
"Execute CLI tools (npm, docker, etc.) and show the output.",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
label: "Something else",
|
|
46
|
+
description: "Describe it in the notes field below.",
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
} satisfies Question,
|
|
50
|
+
]
|
|
51
|
+
: []),
|
|
52
|
+
{
|
|
53
|
+
id: "trigger",
|
|
54
|
+
header: "Trigger",
|
|
55
|
+
question: "How does this command kick off its work?",
|
|
56
|
+
options: [
|
|
57
|
+
{
|
|
58
|
+
label: "Sends to agent",
|
|
59
|
+
description:
|
|
60
|
+
"Builds a prompt and hands off to the LLM to do the heavy lifting.",
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
label: "Runs shell commands",
|
|
64
|
+
description:
|
|
65
|
+
"Executes CLI commands directly (git, npm, etc.) without an LLM turn.",
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
label: "Shows a UI prompt",
|
|
69
|
+
description:
|
|
70
|
+
"Pops up a select/input dialog to gather more info, then acts.",
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
label: "Mixed — UI then agent",
|
|
74
|
+
description:
|
|
75
|
+
"Collects some info via a dialog, then sends a crafted prompt to the LLM.",
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: "output",
|
|
81
|
+
header: "Output",
|
|
82
|
+
question: "How should the command communicate results to the user?",
|
|
83
|
+
options: [
|
|
84
|
+
{
|
|
85
|
+
label: "Agent response",
|
|
86
|
+
description:
|
|
87
|
+
"The LLM writes the response — the command just triggers the turn.",
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
label: "Notification",
|
|
91
|
+
description:
|
|
92
|
+
"A brief inline notification (success/error/info) — no agent turn.",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
label: "Command output",
|
|
96
|
+
description:
|
|
97
|
+
"Shows raw shell output or a formatted summary in the chat.",
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: "args",
|
|
103
|
+
header: "Arguments",
|
|
104
|
+
question: "Does the command take arguments when invoked?",
|
|
105
|
+
options: [
|
|
106
|
+
{
|
|
107
|
+
label: "No args needed",
|
|
108
|
+
description:
|
|
109
|
+
"Called as just /command-name — gathers everything it needs at runtime.",
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
label: "Optional freeform arg",
|
|
113
|
+
description:
|
|
114
|
+
"User can type /command-name <something>, but it works without it too.",
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
label: "Required arg",
|
|
118
|
+
description:
|
|
119
|
+
"Needs a specific value typed after the name; shows usage if missing.",
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
id: "complexity",
|
|
125
|
+
header: "Complexity",
|
|
126
|
+
question: "How complex does the implementation need to be?",
|
|
127
|
+
options: [
|
|
128
|
+
{
|
|
129
|
+
label: "Simple — one action",
|
|
130
|
+
description:
|
|
131
|
+
"Does one thing in a handful of lines. Easy to follow.",
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
label: "Moderate — a few steps",
|
|
135
|
+
description:
|
|
136
|
+
"Some branching, maybe a shell call or two, a conditional prompt.",
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
label: "Complex — multi-step",
|
|
140
|
+
description:
|
|
141
|
+
"Multiple async steps, error handling, state, or UI interactions.",
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
},
|
|
145
|
+
];
|
|
146
|
+
|
|
147
|
+
const result: RoundResult = await showInterviewRound(
|
|
148
|
+
questions,
|
|
149
|
+
{
|
|
150
|
+
progress: "New slash command · Context",
|
|
151
|
+
reviewHeadline: "Review your choices",
|
|
152
|
+
exitHeadline: "Cancel command creation?",
|
|
153
|
+
exitLabel: "cancel",
|
|
154
|
+
},
|
|
155
|
+
ctx,
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
// User hit Esc with nothing answered — bail silently
|
|
159
|
+
if (!result.answers || Object.keys(result.answers).length === 0) {
|
|
160
|
+
ctx.ui.notify("Cancelled.", "info");
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ── Resolve description ───────────────────────────────────────────────
|
|
165
|
+
|
|
166
|
+
let description = inlineDescription;
|
|
167
|
+
if (!description) {
|
|
168
|
+
const purpose = result.answers["purpose"];
|
|
169
|
+
if (purpose) {
|
|
170
|
+
const selected = Array.isArray(purpose.selected)
|
|
171
|
+
? purpose.selected[0]
|
|
172
|
+
: purpose.selected;
|
|
173
|
+
description = purpose.notes
|
|
174
|
+
? purpose.notes // prefer their own words from the notes field
|
|
175
|
+
: selected;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (!description) {
|
|
180
|
+
ctx.ui.notify(
|
|
181
|
+
"No description captured — add details in the notes field next time.",
|
|
182
|
+
"warning",
|
|
183
|
+
);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// ── Build and send the enriched prompt ────────────────────────────────
|
|
188
|
+
|
|
189
|
+
sendPrompt(description, result, pi);
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// ─── Prompt builder ───────────────────────────────────────────────────────────
|
|
195
|
+
|
|
196
|
+
function formatAnswers(result: RoundResult): string {
|
|
197
|
+
const lines: string[] = [];
|
|
198
|
+
|
|
199
|
+
const purpose = result.answers["purpose"];
|
|
200
|
+
if (purpose?.notes) {
|
|
201
|
+
lines.push(`- **Command goal (user's words)**: ${purpose.notes}`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const trigger = result.answers["trigger"];
|
|
205
|
+
if (trigger) {
|
|
206
|
+
const selected = Array.isArray(trigger.selected)
|
|
207
|
+
? trigger.selected[0]
|
|
208
|
+
: trigger.selected;
|
|
209
|
+
lines.push(
|
|
210
|
+
`- **Trigger pattern**: ${selected}${trigger.notes ? ` — ${trigger.notes}` : ""}`,
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const output = result.answers["output"];
|
|
215
|
+
if (output) {
|
|
216
|
+
const selected = Array.isArray(output.selected)
|
|
217
|
+
? output.selected[0]
|
|
218
|
+
: output.selected;
|
|
219
|
+
lines.push(
|
|
220
|
+
`- **Output style**: ${selected}${output.notes ? ` — ${output.notes}` : ""}`,
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const argsAnswer = result.answers["args"];
|
|
225
|
+
if (argsAnswer) {
|
|
226
|
+
const selected = Array.isArray(argsAnswer.selected)
|
|
227
|
+
? argsAnswer.selected[0]
|
|
228
|
+
: argsAnswer.selected;
|
|
229
|
+
lines.push(
|
|
230
|
+
`- **Arguments**: ${selected}${argsAnswer.notes ? ` — ${argsAnswer.notes}` : ""}`,
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const complexity = result.answers["complexity"];
|
|
235
|
+
if (complexity) {
|
|
236
|
+
const selected = Array.isArray(complexity.selected)
|
|
237
|
+
? complexity.selected[0]
|
|
238
|
+
: complexity.selected;
|
|
239
|
+
lines.push(
|
|
240
|
+
`- **Complexity**: ${selected}${complexity.notes ? ` — ${complexity.notes}` : ""}`,
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return lines.join("\n");
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function sendPrompt(
|
|
248
|
+
description: string,
|
|
249
|
+
result: RoundResult,
|
|
250
|
+
pi: ExtensionAPI,
|
|
251
|
+
): void {
|
|
252
|
+
const contextSection = `\n## Context gathered from user\n${formatAnswers(result)}\n`;
|
|
253
|
+
|
|
254
|
+
const prompt = `Create a new pi slash command extension based on this description:
|
|
255
|
+
|
|
256
|
+
"${description}"
|
|
257
|
+
${contextSection}
|
|
258
|
+
Write the complete file contents for two files:
|
|
259
|
+
|
|
260
|
+
1. \`~/.kata-cli/agent/extensions/slash-commands/<name>.ts\` — the command implementation
|
|
261
|
+
2. Update \`~/.kata-cli/agent/extensions/slash-commands/index.ts\` — import and register the new command alongside existing ones
|
|
262
|
+
|
|
263
|
+
Rules you must follow exactly:
|
|
264
|
+
- Command registration: \`pi.registerCommand("name", { description, handler })\`
|
|
265
|
+
- Handler signature: \`async handler(args: string, ctx: ExtensionCommandContext)\`
|
|
266
|
+
- \`args\` is the raw string typed after the command name (may be empty)
|
|
267
|
+
- To send a message to the agent: \`pi.sendUserMessage("...")\` — this triggers an agent turn
|
|
268
|
+
- To show a quick notification without triggering a turn: \`ctx.ui.notify("...", "info" | "success" | "error")\`
|
|
269
|
+
- To run a shell command: \`await pi.exec("cmd", ["arg1", "arg2"])\` — returns \`{ stdout, stderr, exitCode }\`
|
|
270
|
+
- To show a select dialog: \`await ctx.ui.select("prompt", ["Option A", "Option B"])\` — returns the chosen string
|
|
271
|
+
- To show a text input dialog: \`await ctx.ui.input("prompt", "placeholder")\` — returns the string or null
|
|
272
|
+
- \`pi\` is captured in closure from the outer \`export default function(pi: ExtensionAPI)\` — use it freely inside the handler
|
|
273
|
+
- No \`ctx.session\`, no \`ctx.sendMessage\`, no \`args[]\` array — these do not exist
|
|
274
|
+
- Import type: \`import type { ExtensionAPI, ExtensionCommandContext } from "@mariozechner/pi-coding-agent";\`
|
|
275
|
+
- Export default: \`export default function <camelCaseName>(pi: ExtensionAPI) { ... }\`
|
|
276
|
+
|
|
277
|
+
After writing the files, run \`/reload\` to load the new command.`;
|
|
278
|
+
|
|
279
|
+
pi.sendUserMessage(prompt);
|
|
280
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import createSlashCommand from "./create-slash-command.js";
|
|
3
|
+
import createExtension from "./create-extension.js";
|
|
4
|
+
import auditCommand from "./audit.js";
|
|
5
|
+
import kataRun from "./kata-run.js";
|
|
6
|
+
|
|
7
|
+
export default function slashCommands(pi: ExtensionAPI) {
|
|
8
|
+
createSlashCommand(pi);
|
|
9
|
+
createExtension(pi);
|
|
10
|
+
auditCommand(pi);
|
|
11
|
+
kataRun(pi);
|
|
12
|
+
}
|