@leing2021/super-pi 0.14.0
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/README.md +372 -0
- package/extensions/.gitkeep +0 -0
- package/extensions/ce-core/index.ts +528 -0
- package/extensions/ce-core/tools/artifact-helper.ts +73 -0
- package/extensions/ce-core/tools/ask-user-question.ts +53 -0
- package/extensions/ce-core/tools/brainstorm-dialog.ts +167 -0
- package/extensions/ce-core/tools/parallel-subagent.ts +54 -0
- package/extensions/ce-core/tools/pattern-extractor.ts +139 -0
- package/extensions/ce-core/tools/plan-diff.ts +136 -0
- package/extensions/ce-core/tools/review-router.ts +95 -0
- package/extensions/ce-core/tools/session-checkpoint.ts +212 -0
- package/extensions/ce-core/tools/session-history.ts +117 -0
- package/extensions/ce-core/tools/subagent.ts +59 -0
- package/extensions/ce-core/tools/task-splitter.ts +122 -0
- package/extensions/ce-core/tools/workflow-state.ts +80 -0
- package/extensions/ce-core/tools/worktree-manager.ts +131 -0
- package/extensions/ce-core/utils/artifact-paths.ts +23 -0
- package/extensions/ce-core/utils/name-utils.ts +8 -0
- package/package.json +53 -0
- package/skills/.gitkeep +0 -0
- package/skills/01-brainstorm/SKILL.md +120 -0
- package/skills/01-brainstorm/references/builder-mode.md +39 -0
- package/skills/01-brainstorm/references/handoff.md +8 -0
- package/skills/01-brainstorm/references/premise-challenge.md +23 -0
- package/skills/01-brainstorm/references/requirements-template.md +25 -0
- package/skills/01-brainstorm/references/startup-diagnostic.md +108 -0
- package/skills/02-plan/SKILL.md +77 -0
- package/skills/02-plan/references/ceo-review-mode.md +130 -0
- package/skills/02-plan/references/handoff.md +8 -0
- package/skills/02-plan/references/implementation-unit-template.md +25 -0
- package/skills/02-plan/references/plan-template.md +21 -0
- package/skills/03-work/SKILL.md +65 -0
- package/skills/03-work/references/handoff.md +8 -0
- package/skills/03-work/references/progress-update-format.md +17 -0
- package/skills/04-review/SKILL.md +72 -0
- package/skills/04-review/references/findings-schema.md +17 -0
- package/skills/04-review/references/handoff.md +20 -0
- package/skills/04-review/references/qa-test-mode.md +134 -0
- package/skills/04-review/references/reviewer-selection.md +24 -0
- package/skills/05-compound/SKILL.md +32 -0
- package/skills/05-compound/assets/solution-template.md +27 -0
- package/skills/05-compound/references/category-map.md +9 -0
- package/skills/05-compound/references/overlap-rules.md +7 -0
- package/skills/05-compound/references/solution-schema.yaml +27 -0
- package/skills/05-compound/references/solution-search-strategy.md +60 -0
- package/skills/06-next/SKILL.md +37 -0
- package/skills/06-next/references/recommendation-logic.md +46 -0
- package/skills/07-worktree/SKILL.md +35 -0
- package/skills/07-worktree/references/worktree-lifecycle.md +22 -0
- package/skills/08-status/SKILL.md +41 -0
- package/skills/08-status/references/artifact-locations.md +10 -0
- package/skills/09-help/SKILL.md +37 -0
- package/skills/09-help/references/workflow-sequence.md +9 -0
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent"
|
|
2
|
+
import { Type } from "@sinclair/typebox"
|
|
3
|
+
import { createArtifactHelperTool, type ArtifactType } from "./tools/artifact-helper"
|
|
4
|
+
import { createAskUserQuestionTool } from "./tools/ask-user-question"
|
|
5
|
+
import { createSubagentTool } from "./tools/subagent"
|
|
6
|
+
import { createWorkflowStateTool } from "./tools/workflow-state"
|
|
7
|
+
import { createWorktreeManagerTool } from "./tools/worktree-manager"
|
|
8
|
+
import { createReviewRouterTool } from "./tools/review-router"
|
|
9
|
+
import { createParallelSubagentTool } from "./tools/parallel-subagent"
|
|
10
|
+
import { createSessionCheckpointTool } from "./tools/session-checkpoint"
|
|
11
|
+
import { createTaskSplitterTool } from "./tools/task-splitter"
|
|
12
|
+
import { createBrainstormDialogTool } from "./tools/brainstorm-dialog"
|
|
13
|
+
import { createPlanDiffTool } from "./tools/plan-diff"
|
|
14
|
+
import { createSessionHistoryTool } from "./tools/session-history"
|
|
15
|
+
import { createPatternExtractorTool } from "./tools/pattern-extractor"
|
|
16
|
+
|
|
17
|
+
const artifactHelperParams = Type.Object({
|
|
18
|
+
repoRoot: Type.String({ description: "Repository root where workflow artifacts should be created" }),
|
|
19
|
+
artifactType: Type.Union([
|
|
20
|
+
Type.Literal("brainstorm"),
|
|
21
|
+
Type.Literal("plan"),
|
|
22
|
+
Type.Literal("solution"),
|
|
23
|
+
Type.Literal("run"),
|
|
24
|
+
], { description: "Artifact type to resolve" }),
|
|
25
|
+
date: Type.Optional(Type.String({ description: "Date prefix for dated artifacts" })),
|
|
26
|
+
topic: Type.Optional(Type.String({ description: "Topic or slug source for the artifact" })),
|
|
27
|
+
category: Type.Optional(Type.String({ description: "Solution category for docs/solutions" })),
|
|
28
|
+
skillName: Type.Optional(Type.String({ description: "Skill name for run artifacts" })),
|
|
29
|
+
runId: Type.Optional(Type.String({ description: "Run identifier for runtime artifacts" })),
|
|
30
|
+
ensureDir: Type.Optional(Type.Boolean({ description: "Create the parent directory when true" })),
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
const askUserQuestionParams = Type.Object({
|
|
34
|
+
question: Type.String({ description: "Question shown to the user" }),
|
|
35
|
+
options: Type.Optional(Type.Array(Type.String(), { description: "Selectable options" })),
|
|
36
|
+
allowCustom: Type.Optional(Type.Boolean({ description: "Allow a custom answer when options are present" })),
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
const subagentTaskSchema = Type.Object({
|
|
40
|
+
agent: Type.String({ description: "Skill name to invoke via /skill:<name>" }),
|
|
41
|
+
task: Type.String({ description: "Task text passed to the subagent" }),
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
const subagentParams = Type.Object({
|
|
45
|
+
agent: Type.Optional(Type.String({ description: "Single subagent skill name" })),
|
|
46
|
+
task: Type.Optional(Type.String({ description: "Single subagent task" })),
|
|
47
|
+
chain: Type.Optional(Type.Array(subagentTaskSchema, { description: "Serial subagent chain with optional {previous} placeholder" })),
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
const workflowStateParams = Type.Object({
|
|
51
|
+
repoRoot: Type.String({ description: "Repository root to scan for workflow artifacts" }),
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
const worktreeManagerParams = Type.Object({
|
|
55
|
+
operation: Type.Union([
|
|
56
|
+
Type.Literal("create"),
|
|
57
|
+
Type.Literal("detect"),
|
|
58
|
+
Type.Literal("merge"),
|
|
59
|
+
Type.Literal("cleanup"),
|
|
60
|
+
], { description: "Worktree operation to perform" }),
|
|
61
|
+
repoRoot: Type.String({ description: "Repository root" }),
|
|
62
|
+
branchName: Type.Optional(Type.String({ description: "Feature branch name for create/merge/cleanup" })),
|
|
63
|
+
worktreePath: Type.Optional(Type.String({ description: "Worktree directory path for cleanup" })),
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
const reviewRouterParams = Type.Object({
|
|
67
|
+
filesChanged: Type.Array(Type.String(), { description: "List of file paths changed in the diff" }),
|
|
68
|
+
insertions: Type.Number({ description: "Number of lines added" }),
|
|
69
|
+
deletions: Type.Number({ description: "Number of lines removed" }),
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
const parallelSubagentTaskSchema = Type.Object({
|
|
73
|
+
agent: Type.String({ description: "Skill name to invoke via /skill:<name>" }),
|
|
74
|
+
task: Type.String({ description: "Task text passed to the subagent" }),
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
const parallelSubagentParams = Type.Object({
|
|
78
|
+
tasks: Type.Array(parallelSubagentTaskSchema, { description: "Array of independent tasks to run concurrently" }),
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
const sessionCheckpointParams = Type.Object({
|
|
82
|
+
operation: Type.Union([
|
|
83
|
+
Type.Literal("save"),
|
|
84
|
+
Type.Literal("load"),
|
|
85
|
+
Type.Literal("list"),
|
|
86
|
+
Type.Literal("fail"),
|
|
87
|
+
Type.Literal("retry"),
|
|
88
|
+
], { description: "Checkpoint operation" }),
|
|
89
|
+
repoRoot: Type.String({ description: "Repository root" }),
|
|
90
|
+
planPath: Type.Optional(Type.String({ description: "Plan artifact path" })),
|
|
91
|
+
completedUnits: Type.Optional(Type.Array(Type.String(), { description: "List of completed implementation unit names" })),
|
|
92
|
+
failedUnit: Type.Optional(Type.String({ description: "Name of the unit that failed" })),
|
|
93
|
+
error: Type.Optional(Type.String({ description: "Error message from the failure" })),
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
const splitterUnitSchema = Type.Object({
|
|
97
|
+
name: Type.String({ description: "Implementation unit name" }),
|
|
98
|
+
files: Type.Array(Type.String(), { description: "Files this unit touches" }),
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
const taskSplitterParams = Type.Object({
|
|
102
|
+
units: Type.Array(splitterUnitSchema, { description: "Implementation units to analyze for dependencies" }),
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
const brainstormDialogParams = Type.Object({
|
|
106
|
+
operation: Type.Union([
|
|
107
|
+
Type.Literal("start"),
|
|
108
|
+
Type.Literal("refine"),
|
|
109
|
+
Type.Literal("summarize"),
|
|
110
|
+
], { description: "Dialog operation" }),
|
|
111
|
+
repoRoot: Type.String({ description: "Repository root" }),
|
|
112
|
+
artifactPath: Type.String({ description: "Brainstorm artifact path" }),
|
|
113
|
+
analysis: Type.Optional(Type.String({ description: "Agent's current analysis" })),
|
|
114
|
+
questions: Type.Optional(Type.Array(Type.String(), { description: "Open questions for the user" })),
|
|
115
|
+
userResponses: Type.Optional(Type.Array(Type.String(), { description: "User's answers from previous round" })),
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
const planUnitSchema = Type.Object({
|
|
119
|
+
name: Type.String({ description: "Unit name" }),
|
|
120
|
+
description: Type.String({ description: "Unit description" }),
|
|
121
|
+
files: Type.Array(Type.String(), { description: "Files this unit touches" }),
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
const planChangeSchema = Type.Object({
|
|
125
|
+
action: Type.Union([Type.Literal("add"), Type.Literal("remove"), Type.Literal("modify")], { description: "Change action" }),
|
|
126
|
+
name: Type.String({ description: "Unit name" }),
|
|
127
|
+
description: Type.Optional(Type.String({ description: "Updated description" })),
|
|
128
|
+
files: Type.Optional(Type.Array(Type.String(), { description: "Updated file list" })),
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
const planDiffParams = Type.Object({
|
|
132
|
+
operation: Type.Union([Type.Literal("compare"), Type.Literal("patch")], { description: "Diff operation" }),
|
|
133
|
+
existingUnits: Type.Array(planUnitSchema, { description: "Current plan units" }),
|
|
134
|
+
newRequirements: Type.Optional(Type.Array(planUnitSchema, { description: "Updated requirements for compare" })),
|
|
135
|
+
changes: Type.Optional(Type.Array(planChangeSchema, { description: "Changes to apply for patch" })),
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
const sessionHistoryParams = Type.Object({
|
|
139
|
+
operation: Type.Union([
|
|
140
|
+
Type.Literal("record"),
|
|
141
|
+
Type.Literal("query"),
|
|
142
|
+
Type.Literal("latest"),
|
|
143
|
+
], { description: "History operation" }),
|
|
144
|
+
repoRoot: Type.String({ description: "Repository root" }),
|
|
145
|
+
skill: Type.Optional(Type.String({ description: "Skill name to filter or record" })),
|
|
146
|
+
artifactPath: Type.Optional(Type.String({ description: "Artifact path" })),
|
|
147
|
+
summary: Type.Optional(Type.String({ description: "Execution summary" })),
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
const artifactInputSchema = Type.Object({
|
|
151
|
+
path: Type.String({ description: "Artifact path" }),
|
|
152
|
+
content: Type.String({ description: "Artifact content" }),
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
const patternSchema = Type.Object({
|
|
156
|
+
keyword: Type.String({ description: "Pattern keyword" }),
|
|
157
|
+
occurrences: Type.Number({ description: "Number of occurrences" }),
|
|
158
|
+
sources: Type.Array(Type.String(), { description: "Artifact sources" }),
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
const patternExtractorParams = Type.Object({
|
|
162
|
+
operation: Type.Union([
|
|
163
|
+
Type.Literal("extract"),
|
|
164
|
+
Type.Literal("categorize"),
|
|
165
|
+
], { description: "Pattern operation" }),
|
|
166
|
+
artifacts: Type.Optional(Type.Array(artifactInputSchema, { description: "Artifacts to analyze" })),
|
|
167
|
+
keywords: Type.Optional(Type.Array(Type.String(), { description: "Keywords to search for" })),
|
|
168
|
+
patterns: Type.Optional(Type.Array(patternSchema, { description: "Patterns to categorize" })),
|
|
169
|
+
categories: Type.Optional(Type.Record(Type.String(), Type.Array(Type.String()), { description: "Category name to keyword mapping" })),
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
export default function ceCoreExtension(pi: ExtensionAPI) {
|
|
173
|
+
const artifactHelper = createArtifactHelperTool()
|
|
174
|
+
const askUserQuestion = createAskUserQuestionTool()
|
|
175
|
+
const subagent = createSubagentTool()
|
|
176
|
+
const workflowState = createWorkflowStateTool()
|
|
177
|
+
const worktreeManager = createWorktreeManagerTool()
|
|
178
|
+
const reviewRouter = createReviewRouterTool()
|
|
179
|
+
const parallelSubagent = createParallelSubagentTool()
|
|
180
|
+
const sessionCheckpoint = createSessionCheckpointTool()
|
|
181
|
+
const taskSplitter = createTaskSplitterTool()
|
|
182
|
+
const brainstormDialog = createBrainstormDialogTool()
|
|
183
|
+
const planDiff = createPlanDiffTool()
|
|
184
|
+
const sessionHistory = createSessionHistoryTool()
|
|
185
|
+
const patternExtractor = createPatternExtractorTool()
|
|
186
|
+
|
|
187
|
+
pi.registerTool({
|
|
188
|
+
name: artifactHelper.name,
|
|
189
|
+
label: "Artifact Helper",
|
|
190
|
+
description: "Resolve and optionally create standard Compound Engineering artifact paths.",
|
|
191
|
+
parameters: artifactHelperParams,
|
|
192
|
+
async execute(_toolCallId, params) {
|
|
193
|
+
const result = await artifactHelper.execute({
|
|
194
|
+
repoRoot: params.repoRoot,
|
|
195
|
+
artifactType: params.artifactType as ArtifactType,
|
|
196
|
+
date: params.date,
|
|
197
|
+
topic: params.topic,
|
|
198
|
+
category: params.category,
|
|
199
|
+
skillName: params.skillName,
|
|
200
|
+
runId: params.runId,
|
|
201
|
+
ensureDir: params.ensureDir,
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
content: [{ type: "text", text: result.path }],
|
|
206
|
+
details: result,
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
pi.registerTool({
|
|
212
|
+
name: askUserQuestion.name,
|
|
213
|
+
label: "Ask User Question",
|
|
214
|
+
description: "Ask the user a structured question with optional choices and custom answers.",
|
|
215
|
+
parameters: askUserQuestionParams,
|
|
216
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
217
|
+
if (!ctx.hasUI) {
|
|
218
|
+
return {
|
|
219
|
+
isError: true,
|
|
220
|
+
content: [{ type: "text", text: "UI is unavailable in this mode." }],
|
|
221
|
+
details: { answer: null, mode: "cancelled" },
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const result = await askUserQuestion.execute(
|
|
226
|
+
{
|
|
227
|
+
question: params.question,
|
|
228
|
+
options: params.options,
|
|
229
|
+
allowCustom: params.allowCustom,
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
input: async (question) => ctx.ui.input(question),
|
|
233
|
+
select: async (question, options) => ctx.ui.select(question, options),
|
|
234
|
+
},
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
const contentText = result.answer === null
|
|
238
|
+
? "User cancelled."
|
|
239
|
+
: result.mode === "custom"
|
|
240
|
+
? `User answered: ${result.answer}`
|
|
241
|
+
: result.mode === "select"
|
|
242
|
+
? `User selected: ${result.answer}`
|
|
243
|
+
: `User answered: ${result.answer}`
|
|
244
|
+
|
|
245
|
+
return {
|
|
246
|
+
content: [{ type: "text", text: contentText }],
|
|
247
|
+
details: result,
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
pi.registerTool({
|
|
253
|
+
name: subagent.name,
|
|
254
|
+
label: "Subagent",
|
|
255
|
+
description: "Run a single skill-based subagent or a serial chain in an isolated Pi process.",
|
|
256
|
+
parameters: subagentParams,
|
|
257
|
+
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
258
|
+
const result = await subagent.execute(
|
|
259
|
+
{
|
|
260
|
+
agent: params.agent,
|
|
261
|
+
task: params.task,
|
|
262
|
+
chain: params.chain,
|
|
263
|
+
},
|
|
264
|
+
async (prompt: string) => {
|
|
265
|
+
const execResult = await pi.exec("pi", ["--no-session", "-p", prompt], {
|
|
266
|
+
signal,
|
|
267
|
+
timeout: 10 * 60 * 1000,
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
if (execResult.code !== 0) {
|
|
271
|
+
throw new Error(execResult.stderr || execResult.stdout || `Subagent failed for prompt: ${prompt}`)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return (execResult.stdout || "").trim()
|
|
275
|
+
},
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
return {
|
|
279
|
+
content: [{ type: "text", text: result.outputs[result.outputs.length - 1] ?? "" }],
|
|
280
|
+
details: result,
|
|
281
|
+
}
|
|
282
|
+
},
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
pi.registerTool({
|
|
286
|
+
name: workflowState.name,
|
|
287
|
+
label: "Workflow State",
|
|
288
|
+
description: "Scan repo-local Compound Engineering artifacts and return structured workflow state.",
|
|
289
|
+
parameters: workflowStateParams,
|
|
290
|
+
async execute(_toolCallId, params) {
|
|
291
|
+
const result = await workflowState.execute({
|
|
292
|
+
repoRoot: params.repoRoot,
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
return {
|
|
296
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
297
|
+
details: result,
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
pi.registerTool({
|
|
303
|
+
name: worktreeManager.name,
|
|
304
|
+
label: "Worktree Manager",
|
|
305
|
+
description: "Manage git worktree lifecycle: create, detect, merge, and cleanup worktrees for isolated feature development.",
|
|
306
|
+
parameters: worktreeManagerParams,
|
|
307
|
+
async execute(_toolCallId, params, signal) {
|
|
308
|
+
const result = await worktreeManager.execute(
|
|
309
|
+
{
|
|
310
|
+
operation: params.operation,
|
|
311
|
+
repoRoot: params.repoRoot,
|
|
312
|
+
branchName: params.branchName,
|
|
313
|
+
worktreePath: params.worktreePath,
|
|
314
|
+
},
|
|
315
|
+
async (args: string[]) => {
|
|
316
|
+
const execResult = await pi.exec("git", args.slice(1), {
|
|
317
|
+
signal,
|
|
318
|
+
timeout: 60 * 1000,
|
|
319
|
+
cwd: params.repoRoot,
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
if (execResult.code !== 0) {
|
|
323
|
+
throw new Error(execResult.stderr || `git ${args.slice(1).join(" ")} failed`)
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
return (execResult.stdout || "").trim()
|
|
327
|
+
},
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
return {
|
|
331
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
332
|
+
details: result,
|
|
333
|
+
}
|
|
334
|
+
},
|
|
335
|
+
})
|
|
336
|
+
|
|
337
|
+
pi.registerTool({
|
|
338
|
+
name: reviewRouter.name,
|
|
339
|
+
label: "Review Router",
|
|
340
|
+
description: "Analyze diff metadata and recommend reviewer personas for structured code review.",
|
|
341
|
+
parameters: reviewRouterParams,
|
|
342
|
+
async execute(_toolCallId, params) {
|
|
343
|
+
const result = await reviewRouter.execute({
|
|
344
|
+
filesChanged: params.filesChanged,
|
|
345
|
+
insertions: params.insertions,
|
|
346
|
+
deletions: params.deletions,
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
return {
|
|
350
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
351
|
+
details: result,
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
})
|
|
355
|
+
|
|
356
|
+
pi.registerTool({
|
|
357
|
+
name: parallelSubagent.name,
|
|
358
|
+
label: "Parallel Subagent",
|
|
359
|
+
description: "Run multiple skill-based subagent tasks concurrently.",
|
|
360
|
+
parameters: parallelSubagentParams,
|
|
361
|
+
async execute(_toolCallId, params, signal) {
|
|
362
|
+
const result = await parallelSubagent.execute(
|
|
363
|
+
{ tasks: params.tasks },
|
|
364
|
+
async (prompt: string) => {
|
|
365
|
+
const execResult = await pi.exec("pi", ["--no-session", "-p", prompt], {
|
|
366
|
+
signal,
|
|
367
|
+
timeout: 10 * 60 * 1000,
|
|
368
|
+
})
|
|
369
|
+
|
|
370
|
+
if (execResult.code !== 0) {
|
|
371
|
+
throw new Error(execResult.stderr || execResult.stdout || `Subagent failed for prompt: ${prompt}`)
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return (execResult.stdout || "").trim()
|
|
375
|
+
},
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
return {
|
|
379
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
380
|
+
details: result,
|
|
381
|
+
}
|
|
382
|
+
},
|
|
383
|
+
})
|
|
384
|
+
|
|
385
|
+
pi.registerTool({
|
|
386
|
+
name: sessionCheckpoint.name,
|
|
387
|
+
label: "Session Checkpoint",
|
|
388
|
+
description: "Save and load plan execution checkpoints for resume-from-checkpoint behavior.",
|
|
389
|
+
parameters: sessionCheckpointParams,
|
|
390
|
+
async execute(_toolCallId, params) {
|
|
391
|
+
const result = await sessionCheckpoint.execute({
|
|
392
|
+
operation: params.operation,
|
|
393
|
+
repoRoot: params.repoRoot,
|
|
394
|
+
planPath: params.planPath,
|
|
395
|
+
completedUnits: params.completedUnits,
|
|
396
|
+
failedUnit: params.failedUnit,
|
|
397
|
+
error: params.error,
|
|
398
|
+
})
|
|
399
|
+
|
|
400
|
+
return {
|
|
401
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
402
|
+
details: result,
|
|
403
|
+
}
|
|
404
|
+
},
|
|
405
|
+
})
|
|
406
|
+
|
|
407
|
+
pi.registerTool({
|
|
408
|
+
name: taskSplitter.name,
|
|
409
|
+
label: "Task Splitter",
|
|
410
|
+
description: "Analyze implementation units for file-level dependencies and output parallel-safe execution groups.",
|
|
411
|
+
parameters: taskSplitterParams,
|
|
412
|
+
async execute(_toolCallId, params) {
|
|
413
|
+
const result = taskSplitter.execute({
|
|
414
|
+
units: params.units,
|
|
415
|
+
})
|
|
416
|
+
|
|
417
|
+
return {
|
|
418
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
419
|
+
details: result,
|
|
420
|
+
}
|
|
421
|
+
},
|
|
422
|
+
})
|
|
423
|
+
|
|
424
|
+
pi.registerTool({
|
|
425
|
+
name: brainstormDialog.name,
|
|
426
|
+
label: "Brainstorm Dialog",
|
|
427
|
+
description: "Manage multi-round brainstorm conversations with iterative refinement.",
|
|
428
|
+
parameters: brainstormDialogParams,
|
|
429
|
+
async execute(_toolCallId, params) {
|
|
430
|
+
const result = await brainstormDialog.execute({
|
|
431
|
+
operation: params.operation,
|
|
432
|
+
repoRoot: params.repoRoot,
|
|
433
|
+
artifactPath: params.artifactPath,
|
|
434
|
+
analysis: params.analysis,
|
|
435
|
+
questions: params.questions,
|
|
436
|
+
userResponses: params.userResponses,
|
|
437
|
+
})
|
|
438
|
+
|
|
439
|
+
return {
|
|
440
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
441
|
+
details: result,
|
|
442
|
+
}
|
|
443
|
+
},
|
|
444
|
+
})
|
|
445
|
+
|
|
446
|
+
pi.registerTool({
|
|
447
|
+
name: planDiff.name,
|
|
448
|
+
label: "Plan Diff",
|
|
449
|
+
description: "Compare plan units with new requirements or apply incremental changes to an existing plan.",
|
|
450
|
+
parameters: planDiffParams,
|
|
451
|
+
async execute(_toolCallId, params) {
|
|
452
|
+
const result = planDiff.execute({
|
|
453
|
+
operation: params.operation,
|
|
454
|
+
existingUnits: params.existingUnits,
|
|
455
|
+
newRequirements: params.newRequirements,
|
|
456
|
+
changes: params.changes,
|
|
457
|
+
})
|
|
458
|
+
|
|
459
|
+
return {
|
|
460
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
461
|
+
details: result,
|
|
462
|
+
}
|
|
463
|
+
},
|
|
464
|
+
})
|
|
465
|
+
|
|
466
|
+
pi.registerTool({
|
|
467
|
+
name: sessionHistory.name,
|
|
468
|
+
label: "Session History",
|
|
469
|
+
description: "Record and query CE skill execution history.",
|
|
470
|
+
parameters: sessionHistoryParams,
|
|
471
|
+
async execute(_toolCallId, params) {
|
|
472
|
+
const result = await sessionHistory.execute({
|
|
473
|
+
operation: params.operation,
|
|
474
|
+
repoRoot: params.repoRoot,
|
|
475
|
+
skill: params.skill,
|
|
476
|
+
artifactPath: params.artifactPath,
|
|
477
|
+
summary: params.summary,
|
|
478
|
+
})
|
|
479
|
+
|
|
480
|
+
return {
|
|
481
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
482
|
+
details: result,
|
|
483
|
+
}
|
|
484
|
+
},
|
|
485
|
+
})
|
|
486
|
+
|
|
487
|
+
pi.registerTool({
|
|
488
|
+
name: patternExtractor.name,
|
|
489
|
+
label: "Pattern Extractor",
|
|
490
|
+
description: "Extract and categorize recurring patterns from artifacts.",
|
|
491
|
+
parameters: patternExtractorParams,
|
|
492
|
+
async execute(_toolCallId, params) {
|
|
493
|
+
const input: Record<string, unknown> = { operation: params.operation }
|
|
494
|
+
if (params.artifacts) input.artifacts = params.artifacts
|
|
495
|
+
if (params.keywords) input.keywords = params.keywords
|
|
496
|
+
if (params.patterns) input.patterns = params.patterns
|
|
497
|
+
if (params.categories) input.categories = params.categories
|
|
498
|
+
|
|
499
|
+
const result = patternExtractor.execute(input as any)
|
|
500
|
+
|
|
501
|
+
return {
|
|
502
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
503
|
+
details: result,
|
|
504
|
+
}
|
|
505
|
+
},
|
|
506
|
+
})
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
export { createArtifactHelperTool } from "./tools/artifact-helper"
|
|
510
|
+
export { createAskUserQuestionTool } from "./tools/ask-user-question"
|
|
511
|
+
export { createSubagentTool } from "./tools/subagent"
|
|
512
|
+
export { createWorkflowStateTool } from "./tools/workflow-state"
|
|
513
|
+
export { createWorktreeManagerTool } from "./tools/worktree-manager"
|
|
514
|
+
export { createReviewRouterTool } from "./tools/review-router"
|
|
515
|
+
export { createParallelSubagentTool } from "./tools/parallel-subagent"
|
|
516
|
+
export { createSessionCheckpointTool } from "./tools/session-checkpoint"
|
|
517
|
+
export { createTaskSplitterTool } from "./tools/task-splitter"
|
|
518
|
+
export { createBrainstormDialogTool } from "./tools/brainstorm-dialog"
|
|
519
|
+
export { createPlanDiffTool } from "./tools/plan-diff"
|
|
520
|
+
export { createSessionHistoryTool } from "./tools/session-history"
|
|
521
|
+
export { createPatternExtractorTool } from "./tools/pattern-extractor"
|
|
522
|
+
export {
|
|
523
|
+
getBrainstormArtifactPath,
|
|
524
|
+
getPlanArtifactPath,
|
|
525
|
+
getSolutionArtifactPath,
|
|
526
|
+
getRunArtifactPath,
|
|
527
|
+
} from "./utils/artifact-paths"
|
|
528
|
+
export { normalizeSlug } from "./utils/name-utils"
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { mkdir } from "node:fs/promises"
|
|
2
|
+
import path from "node:path"
|
|
3
|
+
import {
|
|
4
|
+
getBrainstormArtifactPath,
|
|
5
|
+
getPlanArtifactPath,
|
|
6
|
+
getSolutionArtifactPath,
|
|
7
|
+
getRunArtifactPath,
|
|
8
|
+
} from "../utils/artifact-paths"
|
|
9
|
+
|
|
10
|
+
export type ArtifactType = "brainstorm" | "plan" | "solution" | "run"
|
|
11
|
+
|
|
12
|
+
export interface ArtifactHelperInput {
|
|
13
|
+
repoRoot: string
|
|
14
|
+
artifactType: ArtifactType
|
|
15
|
+
date?: string
|
|
16
|
+
topic?: string
|
|
17
|
+
category?: string
|
|
18
|
+
skillName?: string
|
|
19
|
+
runId?: string
|
|
20
|
+
ensureDir?: boolean
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ArtifactHelperResult {
|
|
24
|
+
path: string
|
|
25
|
+
createdDirectories: string[]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function createArtifactHelperTool() {
|
|
29
|
+
return {
|
|
30
|
+
name: "artifact_helper",
|
|
31
|
+
async execute(input: ArtifactHelperInput): Promise<ArtifactHelperResult> {
|
|
32
|
+
const artifactPath = resolveArtifactPath(input)
|
|
33
|
+
const directory = path.dirname(artifactPath)
|
|
34
|
+
const createdDirectories: string[] = []
|
|
35
|
+
|
|
36
|
+
if (input.ensureDir) {
|
|
37
|
+
await mkdir(directory, { recursive: true })
|
|
38
|
+
createdDirectories.push(directory)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
path: artifactPath,
|
|
43
|
+
createdDirectories,
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function resolveArtifactPath(input: ArtifactHelperInput): string {
|
|
50
|
+
switch (input.artifactType) {
|
|
51
|
+
case "brainstorm":
|
|
52
|
+
return getBrainstormArtifactPath(input.repoRoot, required(input.date, "date"), required(input.topic, "topic"))
|
|
53
|
+
case "plan":
|
|
54
|
+
return getPlanArtifactPath(input.repoRoot, required(input.date, "date"), required(input.topic, "topic"))
|
|
55
|
+
case "solution":
|
|
56
|
+
return getSolutionArtifactPath(
|
|
57
|
+
input.repoRoot,
|
|
58
|
+
required(input.category, "category"),
|
|
59
|
+
required(input.topic, "topic"),
|
|
60
|
+
required(input.date, "date"),
|
|
61
|
+
)
|
|
62
|
+
case "run":
|
|
63
|
+
return getRunArtifactPath(input.repoRoot, required(input.skillName, "skillName"), required(input.runId, "runId"))
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function required(value: string | undefined, field: string): string {
|
|
68
|
+
if (!value) {
|
|
69
|
+
throw new Error(`artifact_helper requires ${field}`)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return value
|
|
73
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export interface AskUserQuestionInput {
|
|
2
|
+
question: string
|
|
3
|
+
options?: string[]
|
|
4
|
+
allowCustom?: boolean
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface AskUserQuestionUi {
|
|
8
|
+
input(question: string): Promise<string | null>
|
|
9
|
+
select(question: string, options: string[]): Promise<string | null>
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface AskUserQuestionResult {
|
|
13
|
+
answer: string | null
|
|
14
|
+
mode: "input" | "select" | "custom" | "cancelled"
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const CUSTOM_OPTION = "Other"
|
|
18
|
+
|
|
19
|
+
export function createAskUserQuestionTool() {
|
|
20
|
+
return {
|
|
21
|
+
name: "ask_user_question",
|
|
22
|
+
async execute(
|
|
23
|
+
input: AskUserQuestionInput,
|
|
24
|
+
ui: AskUserQuestionUi,
|
|
25
|
+
): Promise<AskUserQuestionResult> {
|
|
26
|
+
const options = input.options ?? []
|
|
27
|
+
|
|
28
|
+
if (options.length === 0) {
|
|
29
|
+
const answer = await ui.input(input.question)
|
|
30
|
+
return answer === null
|
|
31
|
+
? { answer: null, mode: "cancelled" }
|
|
32
|
+
: { answer, mode: "input" }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const allowCustom = input.allowCustom ?? false
|
|
36
|
+
const selectOptions = allowCustom ? [...options, CUSTOM_OPTION] : options
|
|
37
|
+
const selected = await ui.select(input.question, selectOptions)
|
|
38
|
+
|
|
39
|
+
if (selected === null) {
|
|
40
|
+
return { answer: null, mode: "cancelled" }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (allowCustom && selected === CUSTOM_OPTION) {
|
|
44
|
+
const customAnswer = await ui.input("Your answer")
|
|
45
|
+
return customAnswer === null
|
|
46
|
+
? { answer: null, mode: "cancelled" }
|
|
47
|
+
: { answer: customAnswer, mode: "custom" }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return { answer: selected, mode: "select" }
|
|
51
|
+
},
|
|
52
|
+
}
|
|
53
|
+
}
|