pi-super-dev 0.1.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/CHANGELOG.md +35 -0
- package/LICENSE +21 -0
- package/README.md +135 -0
- package/agents/adversarial-reviewer.md +64 -0
- package/agents/architecture-designer.md +43 -0
- package/agents/architecture-improver.md +46 -0
- package/agents/bdd-scenario-writer.md +37 -0
- package/agents/build-cleaner.md +44 -0
- package/agents/code-assessor.md +24 -0
- package/agents/code-reviewer.md +59 -0
- package/agents/debug-analyzer.md +54 -0
- package/agents/docs-executor.md +49 -0
- package/agents/handoff-writer.md +62 -0
- package/agents/implementer.md +47 -0
- package/agents/orchestrator.md +42 -0
- package/agents/product-designer.md +42 -0
- package/agents/prototype-runner.md +36 -0
- package/agents/qa-agent.md +76 -0
- package/agents/requirements-clarifier.md +58 -0
- package/agents/research-agent.md +33 -0
- package/agents/spec-reviewer.md +46 -0
- package/agents/spec-writer.md +32 -0
- package/agents/tdd-guide.md +51 -0
- package/agents/ui-ux-designer.md +50 -0
- package/package.json +40 -0
- package/skills/super-dev/SKILL.md +35 -0
- package/src/agents.ts +38 -0
- package/src/control.ts +85 -0
- package/src/doc-validators.ts +164 -0
- package/src/extension.ts +164 -0
- package/src/helpers.ts +263 -0
- package/src/nodes.ts +550 -0
- package/src/pi-spawn.ts +296 -0
- package/src/pipeline.ts +15 -0
- package/src/prompts.ts +120 -0
- package/src/session-agent.ts +305 -0
- package/src/setup.ts +141 -0
- package/src/stages/design.ts +33 -0
- package/src/stages/implementation.ts +80 -0
- package/src/stages/index.ts +172 -0
- package/src/stages/prototype.ts +43 -0
- package/src/stages/setup.ts +32 -0
- package/src/stages/writers.ts +105 -0
- package/src/types.ts +235 -0
- package/src/workflow.ts +181 -0
package/src/workflow.ts
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The workflow runner. Builds a `StageContext` and evaluates the workflow's
|
|
3
|
+
* root node: `await workflow.root.run(state, ctx)`. All control logic lives in
|
|
4
|
+
* the node algebra (`nodes.ts`); this file only wires execution primitives.
|
|
5
|
+
*
|
|
6
|
+
* ctx.agent() — spawn a specialist `pi` subprocess (pi-spawn.ts)
|
|
7
|
+
* ctx.helper() — run a deterministic pure helper (helpers.ts)
|
|
8
|
+
* ctx.parallel() — run agent calls with a concurrency cap
|
|
9
|
+
* ctx.budget() — cap total agent spawns
|
|
10
|
+
* ctx.events — EventEmitter for waitForEvent (human-in-loop / signals)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { EventEmitter } from "node:events";
|
|
14
|
+
import { spawnAgent } from "./pi-spawn.ts";
|
|
15
|
+
import { runAgentViaSession } from "./session-agent.ts";
|
|
16
|
+
import { runHelper } from "./helpers.ts";
|
|
17
|
+
import { extractControlKeys } from "./control.ts";
|
|
18
|
+
import type {
|
|
19
|
+
AgentCall,
|
|
20
|
+
AgentResult,
|
|
21
|
+
Budget,
|
|
22
|
+
HelperCall,
|
|
23
|
+
HelperResult,
|
|
24
|
+
PipelineState,
|
|
25
|
+
RunOptions,
|
|
26
|
+
RunStatus,
|
|
27
|
+
RunSummary,
|
|
28
|
+
StageContext,
|
|
29
|
+
Workflow,
|
|
30
|
+
} from "./types.ts";
|
|
31
|
+
|
|
32
|
+
const DEFAULT_MAX_AGENTS = 200;
|
|
33
|
+
const DEFAULT_MAX_CONCURRENCY = 3;
|
|
34
|
+
|
|
35
|
+
function makeBudget(maxAgents: number): Budget {
|
|
36
|
+
const s = { count: 0, max: maxAgents };
|
|
37
|
+
return {
|
|
38
|
+
count: 0,
|
|
39
|
+
check: () => s.count < s.max,
|
|
40
|
+
spent() {
|
|
41
|
+
s.count++;
|
|
42
|
+
this.count = s.count;
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function makeContext(state: PipelineState, task: string, options: RunOptions, log: (m: string) => void): StageContext {
|
|
48
|
+
const budget = makeBudget(options.maxAgents ?? DEFAULT_MAX_AGENTS);
|
|
49
|
+
const maxConcurrency = options.maxConcurrency ?? DEFAULT_MAX_CONCURRENCY;
|
|
50
|
+
const model = options.model;
|
|
51
|
+
const signal = options.signal;
|
|
52
|
+
|
|
53
|
+
async function agent(call: AgentCall): Promise<AgentResult> {
|
|
54
|
+
budget.spent();
|
|
55
|
+
const agentCwd = state.setup?.worktreePath ?? options.cwd ?? process.cwd();
|
|
56
|
+
// First-principles retry convergence: if a gate rejected a prior attempt,
|
|
57
|
+
// it stored structured errors under state.__feedback[stageId]. Prepend them
|
|
58
|
+
// to this attempt's prompt so the agent fixes the specific failure instead
|
|
59
|
+
// of resampling the same distribution. The writer's call.id is `pipeline.<id>`.
|
|
60
|
+
const stageKey = (call.id ?? "").replace(/^pipeline\./, "");
|
|
61
|
+
const fb = (state as Record<string, unknown>).__feedback as Record<string, string[]> | undefined;
|
|
62
|
+
const feedback = fb?.[stageKey];
|
|
63
|
+
const prompt = feedback?.length
|
|
64
|
+
? `${call.prompt}\n\n## Previous attempt rejected — fix these\nThe validator rejected the prior attempt for these specific reasons:\n${feedback.map((e) => `- ${e}`).join("\n")}\nAddress every point and re-produce the complete artifact, then call structured_output.`
|
|
65
|
+
: call.prompt;
|
|
66
|
+
const common = {
|
|
67
|
+
agent: call.agent,
|
|
68
|
+
prompt,
|
|
69
|
+
cwd: agentCwd,
|
|
70
|
+
controlKeys: call.controlKeys ?? extractControlKeys(call.prompt),
|
|
71
|
+
model,
|
|
72
|
+
signal,
|
|
73
|
+
id: call.id,
|
|
74
|
+
onProgress: {
|
|
75
|
+
event: (m: string) => log(m),
|
|
76
|
+
text: (partial: string) => options.progress?.text(partial),
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
// Backend selectable. Default is 'session' (in-process createAgentSession):
|
|
80
|
+
// same SDK we peer-depend on, structured output via a schema, no spawn/
|
|
81
|
+
// stdout-buffering/<control>-parse fragility. The earlier failure (requirements
|
|
82
|
+
// gate) was NOT a session-backend defect — it was an incomplete control
|
|
83
|
+
// object caused by a permissive structured_output schema; fixed in
|
|
84
|
+
// session-agent.ts (per-stage schema + corrective re-prompt). 'subprocess'
|
|
85
|
+
// remains available via SUPER_DEV_BACKEND=subprocess.
|
|
86
|
+
const backend = options.backend ?? (process.env.SUPER_DEV_BACKEND as "session" | "subprocess" | undefined) ?? "session";
|
|
87
|
+
return backend === "session" ? runAgentViaSession(common) : spawnAgent(common);
|
|
88
|
+
}
|
|
89
|
+
async function helper(call: HelperCall): Promise<HelperResult> {
|
|
90
|
+
return runHelper(call);
|
|
91
|
+
}
|
|
92
|
+
async function parallel(calls: Array<() => Promise<AgentResult>>): Promise<AgentResult[]> {
|
|
93
|
+
const results: AgentResult[] = [];
|
|
94
|
+
const queue = [...calls];
|
|
95
|
+
async function worker(): Promise<void> {
|
|
96
|
+
while (queue.length > 0) {
|
|
97
|
+
const next = queue.shift();
|
|
98
|
+
if (!next) return;
|
|
99
|
+
results.push(await next());
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
await Promise.all(Array.from({ length: Math.min(maxConcurrency, calls.length) }, worker));
|
|
103
|
+
return results;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return { task, options, state, agent, helper, parallel, budget, log, events: new EventEmitter(), signal, results: [] };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Run a workflow for a task. */
|
|
110
|
+
export async function runWorkflow(workflow: Workflow, task: string, options: RunOptions = {}): Promise<RunSummary> {
|
|
111
|
+
const progress = options.progress;
|
|
112
|
+
const state: PipelineState = {};
|
|
113
|
+
const ctx = makeContext(
|
|
114
|
+
state,
|
|
115
|
+
task,
|
|
116
|
+
options,
|
|
117
|
+
(msg: string) => progress?.log(msg),
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
// Surface phase banners + stage logs through the progress sink. We re-bind
|
|
121
|
+
// ctx.log so control nodes' ctx.log(...) reach the caller; phase banners are
|
|
122
|
+
// emitted by the top-level sequence via a wrapping node (see stages/index.ts).
|
|
123
|
+
if (progress) {
|
|
124
|
+
ctx.events.on("phase", (label: unknown) => progress.phase(String(label)));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
let aborted = false;
|
|
128
|
+
let abortError: string | undefined;
|
|
129
|
+
try {
|
|
130
|
+
await workflow.root.run(state, ctx);
|
|
131
|
+
} catch (err) {
|
|
132
|
+
// A fatal gate (or fatal task) threw to abort the run honestly.
|
|
133
|
+
aborted = true;
|
|
134
|
+
abortError = err instanceof Error ? err.message : String(err);
|
|
135
|
+
progress?.log(`Workflow "${workflow.id}" aborted: ${abortError}`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (!aborted) progress?.log(`Workflow "${workflow.id}" complete`);
|
|
139
|
+
|
|
140
|
+
// Derive an honest overall status from the produced state — never faked.
|
|
141
|
+
const impl = state.implementation as { totalPhases?: number; allGreen?: boolean } | undefined;
|
|
142
|
+
const review = state.review as { verdict?: string } | undefined;
|
|
143
|
+
const phases = impl?.totalPhases ?? 0;
|
|
144
|
+
const green = impl?.allGreen === true;
|
|
145
|
+
const verdict = review?.verdict;
|
|
146
|
+
const approved = verdict === "Approved" || verdict === "Approved with Comments";
|
|
147
|
+
const reviewRan = review !== undefined;
|
|
148
|
+
|
|
149
|
+
let status: RunStatus;
|
|
150
|
+
if (phases === 0) {
|
|
151
|
+
status = "failed"; // no implementation produced (gate aborted, or spec had no phases)
|
|
152
|
+
} else if (green && (!reviewRan || approved)) {
|
|
153
|
+
status = "success";
|
|
154
|
+
} else {
|
|
155
|
+
status = "partial";
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Deduped list of stages that ended in `failed` (with their error).
|
|
159
|
+
const seen = new Set<string>();
|
|
160
|
+
const failedStages: { label: string; error?: string }[] = [];
|
|
161
|
+
for (const r of ctx.results) {
|
|
162
|
+
if (r.status === "failed" && !seen.has(r.id)) {
|
|
163
|
+
seen.add(r.id);
|
|
164
|
+
failedStages.push({ label: r.label || r.id, error: r.error });
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
workflowId: workflow.id,
|
|
170
|
+
specIdentifier: state.setup?.specIdentifier ?? "",
|
|
171
|
+
worktreePath: state.setup?.worktreePath ?? options.cwd ?? process.cwd(),
|
|
172
|
+
specDirectory: state.setup?.specDirectory ?? "",
|
|
173
|
+
agentsSpawned: ctx.budget.count,
|
|
174
|
+
state,
|
|
175
|
+
status,
|
|
176
|
+
failedStages,
|
|
177
|
+
error: abortError,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export { makeContext };
|