@leing2021/super-pi 0.23.13 → 0.24.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 +8 -4
- package/extensions/ce-core/index.ts +15 -196
- package/package.json +3 -3
- package/skills/03-work/SKILL.md +5 -7
- package/skills/08-help/references/workflow-sequence.md +4 -4
- package/extensions/ce-core/tools/async-mutex.ts +0 -28
- package/extensions/ce-core/tools/parallel-subagent.ts +0 -196
- package/extensions/ce-core/tools/subagent-depth-guard.ts +0 -86
- package/extensions/ce-core/tools/subagent-events.ts +0 -287
- package/extensions/ce-core/tools/subagent-json-runner.ts +0 -175
- package/extensions/ce-core/tools/subagent-renderer.ts +0 -478
- package/extensions/ce-core/tools/subagent.ts +0 -209
package/README.md
CHANGED
|
@@ -73,18 +73,22 @@ You: /skill:03-work docs/plans/plan.md
|
|
|
73
73
|
|-------|-------------|-----------|
|
|
74
74
|
| **01-brainstorm** | Structured multi-round discovery, domain vocabulary persistence | `brainstorm_dialog` |
|
|
75
75
|
| **02-plan** | TDD-gated implementation units, optional CEO Review | `plan_diff` |
|
|
76
|
-
| **03-work** | Inline-first execution,
|
|
76
|
+
| **03-work** | Inline-first execution, checkpoint resume, strict TDD, stop-the-line | `session_checkpoint`, `task_splitter` |
|
|
77
77
|
| **04-review** | Auto-assigned reviewers, five-axis findings, autofix loop | `review_router` |
|
|
78
78
|
| **05-learn** | Pattern extraction → searchable solution artifacts | `pattern_extractor` |
|
|
79
79
|
| **06-next** | Next-step recommendation + workflow status | `workflow_state` |
|
|
80
80
|
| **07-worktree** | Isolated git worktree development | `worktree_manager` |
|
|
81
81
|
| **08-help** | Phase 1 skill explainer | — |
|
|
82
82
|
|
|
83
|
-
###
|
|
83
|
+
### Optional: External subagent delegation
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
For generic child agent delegation (background jobs, parallel audits, scout/worker/oracle agents), install the community extension `pi-subagents` separately. Super Pi does not require it.
|
|
86
86
|
|
|
87
|
-
|
|
87
|
+
```bash
|
|
88
|
+
pi install npm:pi-subagents
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Super Pi focuses on inline workflow execution. Use subagents only when you need true process isolation or background runs.
|
|
88
92
|
|
|
89
93
|
### Model & Thinking Routing
|
|
90
94
|
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises"
|
|
2
2
|
import path from "node:path"
|
|
3
|
-
import type { ExtensionAPI } from "@
|
|
3
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"
|
|
4
4
|
import { Type } from "typebox"
|
|
5
5
|
import { createArtifactHelperTool, type ArtifactType } from "./tools/artifact-helper"
|
|
6
6
|
import { createAskUserQuestionTool } from "./tools/ask-user-question"
|
|
7
|
-
import { createSubagentTool } from "./tools/subagent"
|
|
8
7
|
import { createWorkflowStateTool } from "./tools/workflow-state"
|
|
9
8
|
import { createWorktreeManagerTool } from "./tools/worktree-manager"
|
|
10
9
|
import { createReviewRouterTool } from "./tools/review-router"
|
|
11
|
-
import { createParallelSubagentTool } from "./tools/parallel-subagent"
|
|
12
10
|
import { createSessionCheckpointTool } from "./tools/session-checkpoint"
|
|
13
11
|
import { createTaskSplitterTool } from "./tools/task-splitter"
|
|
14
12
|
import { createBrainstormDialogTool } from "./tools/brainstorm-dialog"
|
|
@@ -19,11 +17,6 @@ import { createContextHandoffTool } from "./tools/context-handoff"
|
|
|
19
17
|
import { filterBashOutput } from "./tools/bash-output-filter"
|
|
20
18
|
import { filterReadOutput } from "./tools/read-output-filter"
|
|
21
19
|
import { COMPACTION_FOCUS_INSTRUCTIONS } from "./tools/compaction-optimizer"
|
|
22
|
-
import { createJsonRunner, type JsonRunConfig } from "./tools/subagent-json-runner"
|
|
23
|
-
import { getFinalOutput, isFailedResult, type SingleResult } from "./tools/subagent-events"
|
|
24
|
-
import { renderSubagentCall, renderSubagentResult } from "./tools/subagent-renderer"
|
|
25
|
-
import type { SubagentLiveRunner, SubagentLiveExecOptions, SubagentLiveDetails } from "./tools/subagent"
|
|
26
|
-
import type { ParallelSubagentLiveDetails } from "./tools/parallel-subagent"
|
|
27
20
|
|
|
28
21
|
const PIPELINE_STAGE_KEYS = new Set([
|
|
29
22
|
"01-brainstorm",
|
|
@@ -116,34 +109,6 @@ function parseModelRef(
|
|
|
116
109
|
}
|
|
117
110
|
}
|
|
118
111
|
|
|
119
|
-
/**
|
|
120
|
-
* Create a spawn-based subagent runner that uses pi --mode json
|
|
121
|
-
* with per-process env (no global process.env mutation).
|
|
122
|
-
*/
|
|
123
|
-
function createSubagentRunner(
|
|
124
|
-
pi: ExtensionAPI,
|
|
125
|
-
signal?: AbortSignal,
|
|
126
|
-
): SubagentLiveRunner {
|
|
127
|
-
const jsonRunner = createJsonRunner()
|
|
128
|
-
return async (prompt: string, options?: SubagentLiveExecOptions) => {
|
|
129
|
-
const config: JsonRunConfig = {
|
|
130
|
-
prompt,
|
|
131
|
-
agent: "",
|
|
132
|
-
task: "",
|
|
133
|
-
cwd: (pi as any).cwd ?? process.cwd(),
|
|
134
|
-
extraFlags: options?.extraFlags,
|
|
135
|
-
extraEnv: options?.extraEnv,
|
|
136
|
-
signal,
|
|
137
|
-
onUpdate: options?.onUpdate,
|
|
138
|
-
}
|
|
139
|
-
const result = await jsonRunner.run(config)
|
|
140
|
-
if (isFailedResult(result) || result.exitCode !== 0) {
|
|
141
|
-
throw new Error(result.errorMessage || result.stderr || `Subagent failed for prompt: ${prompt}`)
|
|
142
|
-
}
|
|
143
|
-
return result
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
112
|
const artifactHelperParams = Type.Object({
|
|
148
113
|
repoRoot: Type.String({ description: "Repository root where workflow artifacts should be created" }),
|
|
149
114
|
artifactType: Type.Union([
|
|
@@ -166,18 +131,6 @@ const askUserQuestionParams = Type.Object({
|
|
|
166
131
|
allowCustom: Type.Optional(Type.Boolean({ description: "Allow a custom answer when options are present" })),
|
|
167
132
|
})
|
|
168
133
|
|
|
169
|
-
const subagentTaskSchema = Type.Object({
|
|
170
|
-
agent: Type.String({ description: "Skill name to invoke via /skill:<name>" }),
|
|
171
|
-
task: Type.String({ description: "Task text passed to the subagent" }),
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
const subagentParams = Type.Object({
|
|
175
|
-
agent: Type.Optional(Type.String({ description: "Single subagent skill name" })),
|
|
176
|
-
task: Type.Optional(Type.String({ description: "Single subagent task" })),
|
|
177
|
-
chain: Type.Optional(Type.Array(subagentTaskSchema, { description: "Serial subagent chain with optional {previous} placeholder" })),
|
|
178
|
-
inheritSkills: Type.Optional(Type.Boolean({ description: "Whether the subagent should inherit skills. Default: true" })),
|
|
179
|
-
})
|
|
180
|
-
|
|
181
134
|
const workflowStateParams = Type.Object({
|
|
182
135
|
repoRoot: Type.String({ description: "Repository root to scan for workflow artifacts" }),
|
|
183
136
|
})
|
|
@@ -200,19 +153,6 @@ const reviewRouterParams = Type.Object({
|
|
|
200
153
|
deletions: Type.Number({ description: "Number of lines removed" }),
|
|
201
154
|
})
|
|
202
155
|
|
|
203
|
-
const parallelSubagentTaskSchema = Type.Object({
|
|
204
|
-
agent: Type.String({ description: "Skill name to invoke via /skill:<name>" }),
|
|
205
|
-
task: Type.String({ description: "Task text passed to the subagent" }),
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
const parallelSubagentParams = Type.Object({
|
|
209
|
-
tasks: Type.Union([
|
|
210
|
-
Type.Array(parallelSubagentTaskSchema),
|
|
211
|
-
Type.String({ description: "JSON stringified array of tasks" }),
|
|
212
|
-
], { description: "Array of independent tasks to run concurrently (can be a JSON string)" }),
|
|
213
|
-
inheritSkills: Type.Optional(Type.Boolean({ description: "Whether subagents should inherit skills. Default: true" })),
|
|
214
|
-
})
|
|
215
|
-
|
|
216
156
|
const sessionCheckpointParams = Type.Object({
|
|
217
157
|
operation: Type.Union([
|
|
218
158
|
Type.Literal("save"),
|
|
@@ -346,9 +286,18 @@ export default function ceCoreExtension(pi: ExtensionAPI) {
|
|
|
346
286
|
return { action: "continue" as const }
|
|
347
287
|
}
|
|
348
288
|
|
|
289
|
+
// Skip model/thinking switching during streaming steers — these are
|
|
290
|
+
// mid-stream interrupts, not new pipeline invocations.
|
|
291
|
+
if (event.streamingBehavior === "steer") {
|
|
292
|
+
return { action: "continue" as const }
|
|
293
|
+
}
|
|
294
|
+
|
|
349
295
|
const settings = await readSettings(ctx.cwd)
|
|
350
296
|
const modelStrategy = settings?.modelStrategy
|
|
351
297
|
const thinkingStrategy = settings?.thinkingStrategy
|
|
298
|
+
// Notification guard: only notify in interactive (TUI) or RPC modes.
|
|
299
|
+
// For interactive input capability checks (askUserQuestion), use ctx.hasUI directly.
|
|
300
|
+
const shouldNotify = ctx.mode === "tui" || ctx.mode === "rpc"
|
|
352
301
|
|
|
353
302
|
// Model switching
|
|
354
303
|
if (modelStrategy) {
|
|
@@ -362,19 +311,19 @@ export default function ceCoreExtension(pi: ExtensionAPI) {
|
|
|
362
311
|
if (model) {
|
|
363
312
|
const switched = await pi.setModel(model)
|
|
364
313
|
if (switched) {
|
|
365
|
-
if (
|
|
314
|
+
if (shouldNotify) {
|
|
366
315
|
ctx.ui.notify(`Switched model for ${stageKey}: ${model.provider}/${model.id}`, "info")
|
|
367
316
|
}
|
|
368
317
|
} else {
|
|
369
|
-
if (
|
|
318
|
+
if (shouldNotify) {
|
|
370
319
|
ctx.ui.notify(`No API key for ${stageKey}: ${model.provider}/${model.id}`, "warning")
|
|
371
320
|
}
|
|
372
321
|
}
|
|
373
|
-
} else if (
|
|
322
|
+
} else if (shouldNotify) {
|
|
374
323
|
ctx.ui.notify(`Model not found for ${stageKey}: ${targetModelRef}`, "warning")
|
|
375
324
|
}
|
|
376
325
|
}
|
|
377
|
-
} else if (
|
|
326
|
+
} else if (shouldNotify) {
|
|
378
327
|
ctx.ui.notify(`Invalid modelStrategy for ${stageKey}: ${targetModelRef}`, "warning")
|
|
379
328
|
}
|
|
380
329
|
}
|
|
@@ -399,7 +348,7 @@ export default function ceCoreExtension(pi: ExtensionAPI) {
|
|
|
399
348
|
const currentLevel = pi.getThinkingLevel()
|
|
400
349
|
if (currentLevel !== normalized) {
|
|
401
350
|
pi.setThinkingLevel(normalized)
|
|
402
|
-
if (
|
|
351
|
+
if (shouldNotify) {
|
|
403
352
|
ctx.ui.notify(`Switched thinking level for ${stageKey}: ${normalized}`, "info")
|
|
404
353
|
}
|
|
405
354
|
}
|
|
@@ -411,11 +360,9 @@ export default function ceCoreExtension(pi: ExtensionAPI) {
|
|
|
411
360
|
|
|
412
361
|
const artifactHelper = createArtifactHelperTool()
|
|
413
362
|
const askUserQuestion = createAskUserQuestionTool()
|
|
414
|
-
const subagent = createSubagentTool()
|
|
415
363
|
const workflowState = createWorkflowStateTool()
|
|
416
364
|
const worktreeManager = createWorktreeManagerTool()
|
|
417
365
|
const reviewRouter = createReviewRouterTool()
|
|
418
|
-
const parallelSubagent = createParallelSubagentTool()
|
|
419
366
|
const sessionCheckpoint = createSessionCheckpointTool()
|
|
420
367
|
const taskSplitter = createTaskSplitterTool()
|
|
421
368
|
const brainstormDialog = createBrainstormDialogTool()
|
|
@@ -489,45 +436,6 @@ export default function ceCoreExtension(pi: ExtensionAPI) {
|
|
|
489
436
|
},
|
|
490
437
|
})
|
|
491
438
|
|
|
492
|
-
pi.registerTool({
|
|
493
|
-
name: subagent.name,
|
|
494
|
-
label: "CE Subagent",
|
|
495
|
-
description: "Run a single CE skill-based subagent or a serial chain in an isolated Pi process.",
|
|
496
|
-
parameters: subagentParams,
|
|
497
|
-
async execute(_toolCallId, params, signal, onUpdate, ctx) {
|
|
498
|
-
const result = await subagent.execute(
|
|
499
|
-
{
|
|
500
|
-
agent: params.agent,
|
|
501
|
-
task: params.task,
|
|
502
|
-
chain: params.chain,
|
|
503
|
-
inheritSkills: params.inheritSkills,
|
|
504
|
-
},
|
|
505
|
-
createSubagentRunner(pi, signal),
|
|
506
|
-
{ onUpdate: onUpdate ? (details) => onUpdate({ content: [], details }) : undefined },
|
|
507
|
-
)
|
|
508
|
-
|
|
509
|
-
// Build final content from results
|
|
510
|
-
const lastResult = result.results[result.results.length - 1]
|
|
511
|
-
const finalText = lastResult ? getFinalOutput(lastResult.messages) : ""
|
|
512
|
-
const isError = lastResult ? isFailedResult(lastResult) : false
|
|
513
|
-
|
|
514
|
-
return {
|
|
515
|
-
content: [{ type: "text", text: finalText || "(no output)" }],
|
|
516
|
-
details: result,
|
|
517
|
-
isError: isError || undefined,
|
|
518
|
-
}
|
|
519
|
-
},
|
|
520
|
-
renderCall(args, theme, _context) {
|
|
521
|
-
return renderSubagentCall(args, theme)
|
|
522
|
-
},
|
|
523
|
-
renderResult(result, renderContext, theme, _context) {
|
|
524
|
-
// Handle partial updates where data is at top level (from onUpdate)
|
|
525
|
-
// rather than nested in .details (from final result return value)
|
|
526
|
-
const data = (result.details || result) as SubagentLiveDetails
|
|
527
|
-
return renderSubagentResult(data, renderContext, theme)
|
|
528
|
-
},
|
|
529
|
-
})
|
|
530
|
-
|
|
531
439
|
pi.registerTool({
|
|
532
440
|
name: workflowState.name,
|
|
533
441
|
label: "Workflow State",
|
|
@@ -599,73 +507,6 @@ export default function ceCoreExtension(pi: ExtensionAPI) {
|
|
|
599
507
|
},
|
|
600
508
|
})
|
|
601
509
|
|
|
602
|
-
pi.registerTool({
|
|
603
|
-
name: parallelSubagent.name,
|
|
604
|
-
label: "CE Parallel Subagent",
|
|
605
|
-
description: "Run multiple CE skill-based subagent tasks concurrently. IMPORTANT: Provide 'tasks' as a clean JSON array object. If the environment forces a string, provide a valid JSON array string.",
|
|
606
|
-
parameters: parallelSubagentParams,
|
|
607
|
-
async execute(_toolCallId, params, signal, onUpdate) {
|
|
608
|
-
let tasks: any[]
|
|
609
|
-
if (typeof params.tasks === "string") {
|
|
610
|
-
try {
|
|
611
|
-
const cleaned = params.tasks.replace(/^```json\s*|```$/g, "").trim()
|
|
612
|
-
tasks = JSON.parse(cleaned)
|
|
613
|
-
} catch (e) {
|
|
614
|
-
throw new Error(`Failed to parse tasks string as JSON: ${e instanceof Error ? e.message : String(e)}`)
|
|
615
|
-
}
|
|
616
|
-
} else {
|
|
617
|
-
tasks = params.tasks
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
const result = await parallelSubagent.execute(
|
|
621
|
-
{
|
|
622
|
-
tasks,
|
|
623
|
-
inheritSkills: params.inheritSkills,
|
|
624
|
-
},
|
|
625
|
-
createSubagentRunner(pi, signal),
|
|
626
|
-
{ onUpdate: onUpdate ? (details) => onUpdate({ content: [], details }) : undefined },
|
|
627
|
-
)
|
|
628
|
-
|
|
629
|
-
// Build compact summary content for LLM consumption
|
|
630
|
-
const successCount = result.results.filter(r => r.exitCode === 0).length
|
|
631
|
-
const failCount = result.results.filter(r => isFailedResult(r)).length
|
|
632
|
-
const summaries = result.results.map((r, i) => {
|
|
633
|
-
const icon = isFailedResult(r) ? "✗" : "✓"
|
|
634
|
-
const output = getFinalOutput(r.messages) || r.errorMessage || r.stderr || "(no output)"
|
|
635
|
-
// Compact: first non-empty line, stripped of markdown formatting
|
|
636
|
-
const summaryLine = output.split("\n").find((l: string) => l.trim().length > 0) || ""
|
|
637
|
-
const cleaned = summaryLine.replace(/^#+\s*/, "").replace(/\*\*/g, "").trim()
|
|
638
|
-
const oneLine = cleaned.length > 200 ? cleaned.slice(0, 199) + "…" : cleaned
|
|
639
|
-
return `${i + 1}. ${icon} ${r.agent} — ${oneLine}`
|
|
640
|
-
})
|
|
641
|
-
|
|
642
|
-
// Full outputs available in details for expanded view
|
|
643
|
-
const fullOutputs = result.results.map(r => {
|
|
644
|
-
const output = getFinalOutput(r.messages) || ""
|
|
645
|
-
return output
|
|
646
|
-
})
|
|
647
|
-
|
|
648
|
-
return {
|
|
649
|
-
content: [{
|
|
650
|
-
type: "text",
|
|
651
|
-
text: `Parallel: ${successCount}/${result.results.length} succeeded${failCount > 0 ? `, ${failCount} failed` : ""}\n${summaries.join("\n")}`,
|
|
652
|
-
}],
|
|
653
|
-
details: result,
|
|
654
|
-
}
|
|
655
|
-
},
|
|
656
|
-
renderCall(args, theme, _context) {
|
|
657
|
-
return renderSubagentCall({
|
|
658
|
-
tasks: typeof args.tasks === "string" ? [] : args.tasks,
|
|
659
|
-
}, theme)
|
|
660
|
-
},
|
|
661
|
-
renderResult(result, renderContext, theme, _context) {
|
|
662
|
-
// Handle partial updates where data is at top level (from onUpdate)
|
|
663
|
-
// rather than nested in .details (from final result return value)
|
|
664
|
-
const data = (result.details || result) as ParallelSubagentLiveDetails
|
|
665
|
-
return renderSubagentResult(data, renderContext, theme)
|
|
666
|
-
},
|
|
667
|
-
})
|
|
668
|
-
|
|
669
510
|
pi.registerTool({
|
|
670
511
|
name: sessionCheckpoint.name,
|
|
671
512
|
label: "Session Checkpoint",
|
|
@@ -909,31 +750,9 @@ export default function ceCoreExtension(pi: ExtensionAPI) {
|
|
|
909
750
|
|
|
910
751
|
export { createArtifactHelperTool } from "./tools/artifact-helper"
|
|
911
752
|
export { createAskUserQuestionTool } from "./tools/ask-user-question"
|
|
912
|
-
export { createSubagentTool } from "./tools/subagent"
|
|
913
|
-
export { checkSubagentDepth, getChildDepthEnv, DEFAULT_MAX_SUBAGENT_DEPTH } from "./tools/subagent-depth-guard"
|
|
914
|
-
export { AsyncMutex } from "./tools/async-mutex"
|
|
915
|
-
export { createJsonRunner } from "./tools/subagent-json-runner"
|
|
916
|
-
export {
|
|
917
|
-
parseJsonEvent,
|
|
918
|
-
applyEventToResult,
|
|
919
|
-
type SingleResult as SingleResultType,
|
|
920
|
-
type UsageStats,
|
|
921
|
-
type ParsedEvent,
|
|
922
|
-
isFailedResult as isFailedSingleResult,
|
|
923
|
-
formatUsageStats,
|
|
924
|
-
makeInitialResult,
|
|
925
|
-
getFinalOutput as getFinalOutputFromMessages,
|
|
926
|
-
getDisplayItems,
|
|
927
|
-
} from "./tools/subagent-events"
|
|
928
|
-
export {
|
|
929
|
-
formatToolCall,
|
|
930
|
-
renderSubagentCall,
|
|
931
|
-
renderSubagentResult,
|
|
932
|
-
} from "./tools/subagent-renderer"
|
|
933
753
|
export { createWorkflowStateTool } from "./tools/workflow-state"
|
|
934
754
|
export { createWorktreeManagerTool } from "./tools/worktree-manager"
|
|
935
755
|
export { createReviewRouterTool } from "./tools/review-router"
|
|
936
|
-
export { createParallelSubagentTool } from "./tools/parallel-subagent"
|
|
937
756
|
export { createSessionCheckpointTool } from "./tools/session-checkpoint"
|
|
938
757
|
export { createTaskSplitterTool } from "./tools/task-splitter"
|
|
939
758
|
export { createBrainstormDialogTool } from "./tools/brainstorm-dialog"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leing2021/super-pi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Pi-native Compound Engineering package for iterative development workflows",
|
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
"test": "bun test"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
|
-
"@
|
|
38
|
+
"@earendil-works/pi-coding-agent": ">=0.74.0",
|
|
39
39
|
"typebox": ">=1.0.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@
|
|
42
|
+
"@earendil-works/pi-coding-agent": "^0.76.0",
|
|
43
43
|
"bun-types": "^1.3.12"
|
|
44
44
|
},
|
|
45
45
|
"pi": {
|
package/skills/03-work/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: 03-work
|
|
3
|
-
description: "Execute plan units with
|
|
3
|
+
description: "Execute plan units with TDD enforcement and checkpoint resume. Use when a plan path is ready for implementation."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Work
|
|
@@ -19,16 +19,14 @@ See [shared pipeline instructions](../references/pipeline-config.md) for model r
|
|
|
19
19
|
2. **Priority:** project-level `{repo-root}/rules/` overrides package defaults
|
|
20
20
|
3. **Distinguish input:** plan path vs bare prompt
|
|
21
21
|
4. Derive tasks from plan **implementation units**
|
|
22
|
-
5. **Execution mode:**
|
|
23
|
-
- **Inline mode** is the default execution path
|
|
24
|
-
- **`ce_parallel_subagent`** only for bounded, non-interactive, easily verifiable leaf tasks
|
|
25
|
-
- **`ce_subagent`** only for rare dependent serial chains of leaf tasks
|
|
26
|
-
- Never use either tool to invoke pipeline-stage skills (`01-brainstorm through 05-learn`). Run those stages directly with `/skill:<stage>`.
|
|
22
|
+
5. **Execution mode:** **inline mode** — all plan units execute inline in the current session. No built-in subagent tools.
|
|
27
23
|
6. Use **`session_checkpoint`** to track progress and enable resume
|
|
28
24
|
7. Use **`task_splitter`** to analyze dependencies before execution
|
|
29
25
|
8. If in **worktree** (via `07-worktree`), execute inside it
|
|
30
26
|
9. End by recommending `04-review`
|
|
31
27
|
|
|
28
|
+
> **Advanced:** If you need external child agent delegation (background runs, parallel audits), install `pi-subagents` separately. Super Pi does not require it.
|
|
29
|
+
|
|
32
30
|
## Hard gates — TDD enforcement
|
|
33
31
|
|
|
34
32
|
Every step follows **RED → GREEN → REFACTOR**:
|
|
@@ -76,7 +74,7 @@ If the same tool, command, or implementation unit fails 3 consecutive times, sto
|
|
|
76
74
|
3. Read implementation units if plan path
|
|
77
75
|
4. Load `session_checkpoint` to skip completed units
|
|
78
76
|
5. Use `task_splitter` for dependency analysis
|
|
79
|
-
6. Execute: **inline mode**
|
|
77
|
+
6. Execute: **inline mode** — all units run in the current session
|
|
80
78
|
7. Follow TDD per unit: RED → minimal code → GREEN → refactor → unit-level **verification**
|
|
81
79
|
8. **Source-driven gate:** Before implementing framework/library-specific code, verify the API or pattern against official documentation. Flag unverified patterns as UNVERIFIED in output.
|
|
82
80
|
9. Record progress via `references/progress-update-format.md`
|
|
@@ -41,10 +41,10 @@ Use `07-worktree` for isolated feature development on a separate branch.
|
|
|
41
41
|
### 03-work
|
|
42
42
|
**Use when:** plan is ready or task is tightly scoped.
|
|
43
43
|
|
|
44
|
-
**Execution
|
|
45
|
-
- Inline execution for
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
**Execution mode:**
|
|
45
|
+
- Inline execution for all units in the current session
|
|
46
|
+
|
|
47
|
+
**Advanced:** For external child agent delegation, install `pi-subagents` separately.
|
|
48
48
|
|
|
49
49
|
**Key outputs:**
|
|
50
50
|
- Completion report with verification evidence
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Async mutex for serializing critical sections that mutate shared state
|
|
3
|
-
* (e.g. process.env) across concurrent async operations.
|
|
4
|
-
*/
|
|
5
|
-
export class AsyncMutex {
|
|
6
|
-
private queue: (() => void)[] = []
|
|
7
|
-
private locked = false
|
|
8
|
-
|
|
9
|
-
async acquire(): Promise<() => void> {
|
|
10
|
-
return new Promise<() => void>((resolve) => {
|
|
11
|
-
const tryAcquire = () => {
|
|
12
|
-
if (!this.locked) {
|
|
13
|
-
this.locked = true
|
|
14
|
-
resolve(() => this.release())
|
|
15
|
-
} else {
|
|
16
|
-
this.queue.push(tryAcquire)
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
tryAcquire()
|
|
20
|
-
})
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
private release(): void {
|
|
24
|
-
this.locked = false
|
|
25
|
-
const next = this.queue.shift()
|
|
26
|
-
if (next) next()
|
|
27
|
-
}
|
|
28
|
-
}
|
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CE parallel subagent tool — runs multiple CE skill-based subagent tasks concurrently.
|
|
3
|
-
*
|
|
4
|
-
* Features:
|
|
5
|
-
* - Recursion depth guard (PI_SUBAGENT_DEPTH / PI_SUBAGENT_MAX_DEPTH)
|
|
6
|
-
* - Optional context slimming via --no-skills flag
|
|
7
|
-
* - Concurrency limit via mapWithConcurrencyLimit (from pi official example)
|
|
8
|
-
* - Live TUI updates with running placeholders and per-task status
|
|
9
|
-
* - Promise.allSettled semantics: one failure does NOT cancel others
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import {
|
|
13
|
-
checkSubagentDepth,
|
|
14
|
-
getChildDepthEnv,
|
|
15
|
-
} from "./subagent-depth-guard"
|
|
16
|
-
import {
|
|
17
|
-
type SingleResult,
|
|
18
|
-
makeInitialResult,
|
|
19
|
-
isFailedResult,
|
|
20
|
-
getFinalOutput,
|
|
21
|
-
makeFailedResult,
|
|
22
|
-
invokeRunner,
|
|
23
|
-
type AnyRunner,
|
|
24
|
-
} from "./subagent-events"
|
|
25
|
-
import {
|
|
26
|
-
assertNonPipelineStageSkill,
|
|
27
|
-
type SubagentLiveRunner,
|
|
28
|
-
type SubagentLiveExecOptions,
|
|
29
|
-
type SubagentRunner,
|
|
30
|
-
} from "./subagent"
|
|
31
|
-
|
|
32
|
-
// ---------------------------------------------------------------------------
|
|
33
|
-
// Constants (from pi official example)
|
|
34
|
-
// ---------------------------------------------------------------------------
|
|
35
|
-
|
|
36
|
-
const MAX_PARALLEL_TASKS = 8
|
|
37
|
-
const MAX_CONCURRENCY = 4
|
|
38
|
-
|
|
39
|
-
// ---------------------------------------------------------------------------
|
|
40
|
-
// mapWithConcurrencyLimit (directly from pi official subagent example)
|
|
41
|
-
// ---------------------------------------------------------------------------
|
|
42
|
-
|
|
43
|
-
async function mapWithConcurrencyLimit<TIn, TOut>(
|
|
44
|
-
items: TIn[],
|
|
45
|
-
concurrency: number,
|
|
46
|
-
fn: (item: TIn, index: number) => Promise<TOut>,
|
|
47
|
-
): Promise<TOut[]> {
|
|
48
|
-
if (items.length === 0) return []
|
|
49
|
-
const limit = Math.max(1, Math.min(concurrency, items.length))
|
|
50
|
-
const results: TOut[] = new Array(items.length)
|
|
51
|
-
let nextIndex = 0
|
|
52
|
-
const workers = new Array(limit).fill(null).map(async () => {
|
|
53
|
-
while (true) {
|
|
54
|
-
const current = nextIndex++
|
|
55
|
-
if (current >= items.length) return
|
|
56
|
-
results[current] = await fn(items[current], current)
|
|
57
|
-
}
|
|
58
|
-
})
|
|
59
|
-
await Promise.all(workers)
|
|
60
|
-
return results
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// ---------------------------------------------------------------------------
|
|
64
|
-
// Public types
|
|
65
|
-
// ---------------------------------------------------------------------------
|
|
66
|
-
|
|
67
|
-
export interface ParallelSubagentTask {
|
|
68
|
-
agent: string
|
|
69
|
-
task: string
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export interface ParallelSubagentInput {
|
|
73
|
-
tasks: ParallelSubagentTask[]
|
|
74
|
-
/** Whether subagents should inherit skills. Default: true */
|
|
75
|
-
inheritSkills?: boolean
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export interface ParallelSubagentLiveDetails {
|
|
79
|
-
mode: "parallel"
|
|
80
|
-
results: SingleResult[]
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// ---------------------------------------------------------------------------
|
|
84
|
-
// Tool factory
|
|
85
|
-
// ---------------------------------------------------------------------------
|
|
86
|
-
|
|
87
|
-
export function createParallelSubagentTool() {
|
|
88
|
-
return {
|
|
89
|
-
name: "ce_parallel_subagent" as const,
|
|
90
|
-
|
|
91
|
-
async execute(
|
|
92
|
-
input: ParallelSubagentInput,
|
|
93
|
-
runner: SubagentLiveRunner | SubagentRunner,
|
|
94
|
-
toolCtx?: { onUpdate?: (details: ParallelSubagentLiveDetails) => void },
|
|
95
|
-
): Promise<ParallelSubagentLiveDetails> {
|
|
96
|
-
// Recursion depth guard
|
|
97
|
-
const depthCheck = checkSubagentDepth()
|
|
98
|
-
if (!depthCheck.allowed) {
|
|
99
|
-
throw new Error(depthCheck.reason!)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (!input.tasks || input.tasks.length === 0) {
|
|
103
|
-
throw new Error("ce_parallel_subagent requires at least one task")
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (input.tasks.length > MAX_PARALLEL_TASKS) {
|
|
107
|
-
throw new Error(
|
|
108
|
-
`Too many parallel tasks (${input.tasks.length}). Max is ${MAX_PARALLEL_TASKS}.`,
|
|
109
|
-
)
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
for (const task of input.tasks) {
|
|
113
|
-
assertNonPipelineStageSkill(task.agent, "ce_parallel_subagent")
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const execOptions = buildParallelExecOptions(input.inheritSkills)
|
|
117
|
-
|
|
118
|
-
// Initialize all results as running placeholders
|
|
119
|
-
const allResults: SingleResult[] = input.tasks.map(({ agent, task }) =>
|
|
120
|
-
makeInitialResult(agent, task),
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
// Emit initial state (all running)
|
|
124
|
-
emitParallelUpdate(allResults, toolCtx)
|
|
125
|
-
|
|
126
|
-
// Run tasks with concurrency limit
|
|
127
|
-
await mapWithConcurrencyLimit(input.tasks, MAX_CONCURRENCY, async (t, index) => {
|
|
128
|
-
const liveOptions: SubagentLiveExecOptions = {
|
|
129
|
-
...execOptions,
|
|
130
|
-
onUpdate: (partial: SingleResult) => {
|
|
131
|
-
allResults[index] = partial
|
|
132
|
-
emitParallelUpdate(allResults, toolCtx)
|
|
133
|
-
},
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
try {
|
|
137
|
-
const result = await invokeRunner(runner, `/skill:${t.agent} ${t.task}`, liveOptions)
|
|
138
|
-
allResults[index] = result
|
|
139
|
-
} catch (e) {
|
|
140
|
-
// Preserve allSettled semantics: one failure does not cancel others
|
|
141
|
-
allResults[index] = makeFailedResult(t.agent, t.task, e instanceof Error ? e.message : String(e))
|
|
142
|
-
}
|
|
143
|
-
emitParallelUpdate(allResults, toolCtx)
|
|
144
|
-
return allResults[index]
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
return { mode: "parallel", results: allResults }
|
|
148
|
-
},
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// ---------------------------------------------------------------------------
|
|
153
|
-
// Helpers
|
|
154
|
-
// ---------------------------------------------------------------------------
|
|
155
|
-
|
|
156
|
-
function emitParallelUpdate(
|
|
157
|
-
results: SingleResult[],
|
|
158
|
-
toolCtx?: { onUpdate?: (details: ParallelSubagentLiveDetails) => void },
|
|
159
|
-
) {
|
|
160
|
-
if (toolCtx?.onUpdate) {
|
|
161
|
-
toolCtx.onUpdate({ mode: "parallel", results: [...results] })
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function buildParallelExecOptions(inheritSkills?: boolean): { extraFlags?: string[]; extraEnv: Record<string, string> } {
|
|
166
|
-
const flags: string[] = []
|
|
167
|
-
const env: Record<string, string> = {}
|
|
168
|
-
|
|
169
|
-
if (inheritSkills === false) {
|
|
170
|
-
flags.push("--no-skills")
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
Object.assign(env, getChildDepthEnv())
|
|
174
|
-
|
|
175
|
-
return {
|
|
176
|
-
extraFlags: flags.length > 0 ? flags : undefined,
|
|
177
|
-
extraEnv: env,
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// ---------------------------------------------------------------------------
|
|
182
|
-
// Legacy backward-compat types and exports
|
|
183
|
-
// ---------------------------------------------------------------------------
|
|
184
|
-
|
|
185
|
-
/** @deprecated Use ParallelSubagentLiveDetails instead */
|
|
186
|
-
export interface ParallelResultItem {
|
|
187
|
-
status: "fulfilled" | "rejected"
|
|
188
|
-
value?: string
|
|
189
|
-
reason?: string
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/** @deprecated Use ParallelSubagentLiveDetails instead */
|
|
193
|
-
export interface ParallelSubagentResult {
|
|
194
|
-
mode: "parallel"
|
|
195
|
-
outputs: ParallelResultItem[]
|
|
196
|
-
}
|