takomi 2.0.7 → 2.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/.pi/README.md +124 -0
- package/.pi/agents/architect.md +16 -0
- package/.pi/agents/coder.md +15 -0
- package/.pi/agents/designer.md +18 -0
- package/.pi/agents/orchestrator.md +23 -0
- package/.pi/agents/reviewer.md +17 -0
- package/.pi/extensions/oauth-router/README.md +125 -0
- package/.pi/extensions/oauth-router/commands.ts +380 -0
- package/.pi/extensions/oauth-router/config.ts +200 -0
- package/.pi/extensions/oauth-router/index.ts +41 -0
- package/.pi/extensions/oauth-router/oauth-flow.ts +154 -0
- package/.pi/extensions/oauth-router/oauth-store.ts +121 -0
- package/.pi/extensions/oauth-router/package.json +14 -0
- package/.pi/extensions/oauth-router/policies.ts +27 -0
- package/.pi/extensions/oauth-router/provider.ts +492 -0
- package/.pi/extensions/oauth-router/scripts/vibe-verify.py +98 -0
- package/.pi/extensions/oauth-router/state.ts +174 -0
- package/.pi/extensions/oauth-router/types.ts +153 -0
- package/.pi/extensions/takomi-runtime/command-text.ts +130 -0
- package/.pi/extensions/takomi-runtime/commands.ts +179 -0
- package/.pi/extensions/takomi-runtime/context-panel.ts +282 -0
- package/.pi/extensions/takomi-runtime/index.ts +1288 -0
- package/.pi/extensions/takomi-runtime/profile.ts +114 -0
- package/.pi/extensions/takomi-runtime/routing-policy.ts +105 -0
- package/.pi/extensions/takomi-runtime/shared.ts +492 -0
- package/.pi/extensions/takomi-runtime/subagent-controller.ts +364 -0
- package/.pi/extensions/takomi-runtime/subagent-render.ts +501 -0
- package/.pi/extensions/takomi-runtime/subagent-types.ts +83 -0
- package/.pi/extensions/takomi-runtime/ui.ts +133 -0
- package/.pi/extensions/takomi-subagents/agent-aliases.ts +18 -0
- package/.pi/extensions/takomi-subagents/agents.ts +113 -0
- package/.pi/extensions/takomi-subagents/delegation-plan.ts +95 -0
- package/.pi/extensions/takomi-subagents/dispatch-helpers.ts +26 -0
- package/.pi/extensions/takomi-subagents/dispatch.ts +215 -0
- package/.pi/extensions/takomi-subagents/index.ts +75 -0
- package/.pi/extensions/takomi-subagents/live-updates.ts +83 -0
- package/.pi/extensions/takomi-subagents/native-render.ts +174 -0
- package/.pi/extensions/takomi-subagents/tool-runner.ts +209 -0
- package/.pi/prompts/build-prompt.md +199 -0
- package/.pi/prompts/design-prompt.md +134 -0
- package/.pi/prompts/genesis-prompt.md +133 -0
- package/.pi/prompts/orch-prompt.md +144 -0
- package/.pi/prompts/prime-prompt.md +80 -0
- package/.pi/prompts/takomi-prompt.md +96 -0
- package/.pi/prompts/vibe-primeAgent.md +97 -0
- package/.pi/prompts/vibe-spawnTask.md +133 -0
- package/.pi/prompts/vibe-syncDocs.md +100 -0
- package/.pi/themes/takomi-noir.json +81 -0
- package/README.md +28 -2
- package/assets/.agent/skills/pr-comment-fix/SKILL.md +182 -0
- package/assets/.agent/skills/takomi/SKILL.md +59 -59
- package/package.json +58 -45
- package/src/cli.js +158 -8
- package/src/doctor.js +84 -0
- package/src/pi-harness.js +351 -0
- package/src/pi-installer.js +171 -0
- package/src/pi-takomi-core/index.ts +4 -0
- package/src/pi-takomi-core/orchestration.ts +402 -0
- package/src/pi-takomi-core/routing.ts +93 -0
- package/src/pi-takomi-core/types.ts +173 -0
- package/src/pi-takomi-core/workflows.ts +299 -0
- package/src/skills-installer.js +101 -0
- package/src/utils.js +479 -447
- package/assets/.agent/skills/skill-creator/scripts/__pycache__/quick_validate.cpython-311.pyc +0 -0
- package/assets/.agent/skills/ui-ux-pro-max/scripts/__pycache__/core.cpython-311.pyc +0 -0
- package/assets/.agent/skills/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-311.pyc +0 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { TakomiThinkingLevel } from "../../../src/pi-takomi-core";
|
|
2
|
+
import type { TakomiSubagentRuntimeEvent } from "../takomi-runtime/subagent-types";
|
|
3
|
+
import type { TakomiDispatchResult } from "./dispatch";
|
|
4
|
+
|
|
5
|
+
type ToolUpdate = (partial: {
|
|
6
|
+
content: Array<{ type: "text"; text: string }>;
|
|
7
|
+
details: Record<string, unknown>;
|
|
8
|
+
}) => void;
|
|
9
|
+
|
|
10
|
+
type LiveTask = {
|
|
11
|
+
agent: string;
|
|
12
|
+
task: string;
|
|
13
|
+
workflow?: string;
|
|
14
|
+
model?: string;
|
|
15
|
+
thinking?: TakomiThinkingLevel;
|
|
16
|
+
conversationId?: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
function appendLine(value: string, line: string): string {
|
|
20
|
+
const next = [value, line.trim()].filter(Boolean).join("\n");
|
|
21
|
+
return next.split(/\r?\n/).slice(-12).join("\n");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function createTakomiLiveUpdateBridge(
|
|
25
|
+
tasks: LiveTask[],
|
|
26
|
+
mode: "single" | "parallel" | "chain",
|
|
27
|
+
agentScope: string,
|
|
28
|
+
onUpdate: ToolUpdate | undefined,
|
|
29
|
+
) {
|
|
30
|
+
const results: TakomiDispatchResult[] = tasks.map((task, index) => ({
|
|
31
|
+
agent: task.agent,
|
|
32
|
+
task: task.task,
|
|
33
|
+
workflow: task.workflow,
|
|
34
|
+
model: task.model,
|
|
35
|
+
thinking: task.thinking,
|
|
36
|
+
conversationId: task.conversationId ?? `pending-${index + 1}`,
|
|
37
|
+
code: -1,
|
|
38
|
+
output: "Queued.",
|
|
39
|
+
stderr: "",
|
|
40
|
+
preflight: "",
|
|
41
|
+
}));
|
|
42
|
+
|
|
43
|
+
const emit = () => {
|
|
44
|
+
const done = results.filter((result) => result.code === 0).length;
|
|
45
|
+
const running = results.filter((result) => result.code === -1).length;
|
|
46
|
+
onUpdate?.({
|
|
47
|
+
content: [{ type: "text", text: `Takomi ${mode}: ${done}/${results.length} done, ${running} running` }],
|
|
48
|
+
details: { results: [...results], mode, agentScope },
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
results,
|
|
54
|
+
event(index: number, event: TakomiSubagentRuntimeEvent): void {
|
|
55
|
+
const current = results[index];
|
|
56
|
+
if (!current) return;
|
|
57
|
+
if (event.type === "start") {
|
|
58
|
+
current.conversationId = event.state.conversationId ?? current.conversationId;
|
|
59
|
+
current.thinking = event.state.thinking ?? current.thinking;
|
|
60
|
+
current.output = event.state.summary ?? "Starting.";
|
|
61
|
+
} else if (event.type === "update") {
|
|
62
|
+
current.model = event.patch.model ?? current.model;
|
|
63
|
+
current.thinking = event.patch.thinking ?? current.thinking;
|
|
64
|
+
current.output = event.patch.outputText ?? event.patch.summary ?? current.output;
|
|
65
|
+
if (event.patch.logs?.length) current.output = appendLine(current.output, event.patch.logs.join("\n"));
|
|
66
|
+
} else if (event.type === "appendLog") {
|
|
67
|
+
current.output = appendLine(current.output, event.chunk);
|
|
68
|
+
} else if (event.type === "complete") {
|
|
69
|
+
current.code = 0;
|
|
70
|
+
current.output = event.patch?.outputText ?? event.patch?.summary ?? current.output;
|
|
71
|
+
} else if (event.type === "block") {
|
|
72
|
+
current.code = 1;
|
|
73
|
+
current.output = event.patch?.outputText ?? event.patch?.summary ?? current.output;
|
|
74
|
+
if (event.patch?.logs?.length) current.stderr = event.patch.logs.join("\n");
|
|
75
|
+
}
|
|
76
|
+
emit();
|
|
77
|
+
},
|
|
78
|
+
finish(index: number, result: TakomiDispatchResult): void {
|
|
79
|
+
results[index] = result;
|
|
80
|
+
emit();
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { renderSubagentResult, syncResultAnimation } from "pi-subagents/src/tui/render";
|
|
2
|
+
import type { AgentToolResult } from "@mariozechner/pi-agent-core";
|
|
3
|
+
import type { Details, SingleResult, AgentProgress } from "pi-subagents/src/shared/types";
|
|
4
|
+
import type { Theme } from "@mariozechner/pi-coding-agent";
|
|
5
|
+
import { Container, Markdown, Spacer, Text } from "@mariozechner/pi-tui";
|
|
6
|
+
import type { TakomiDispatchResult } from "./dispatch";
|
|
7
|
+
import type { TakomiSubagentToolParams } from "./tool-runner";
|
|
8
|
+
|
|
9
|
+
type ToolResult = {
|
|
10
|
+
content?: Array<{ type: string; text?: string }>;
|
|
11
|
+
details?: {
|
|
12
|
+
results?: TakomiDispatchResult[];
|
|
13
|
+
mode?: "single" | "parallel" | "chain";
|
|
14
|
+
agentScope?: string;
|
|
15
|
+
plan?: unknown;
|
|
16
|
+
};
|
|
17
|
+
isError?: boolean;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function taskList(params: TakomiSubagentToolParams): Array<{ agent: string; task: string }> {
|
|
21
|
+
if (params.chain?.length) return params.chain;
|
|
22
|
+
if (params.tasks?.length) return params.tasks;
|
|
23
|
+
if (params.agent || params.task) return [{ agent: params.agent ?? "...", task: params.task ?? "..." }];
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function renderTakomiSubagentCall(params: TakomiSubagentToolParams, theme: Theme) {
|
|
28
|
+
const tasks = taskList(params);
|
|
29
|
+
const mode = params.chain?.length ? "chain" : params.tasks?.length ? "parallel" : "single";
|
|
30
|
+
if (tasks.length === 1) {
|
|
31
|
+
return new Text(
|
|
32
|
+
`${theme.fg("toolTitle", theme.bold("takomi_subagent "))}${theme.fg("accent", tasks[0]?.agent || "?")}`,
|
|
33
|
+
0,
|
|
34
|
+
0,
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
return new Text(
|
|
38
|
+
`${theme.fg("toolTitle", theme.bold("takomi_subagent "))}${mode} (${tasks.length})`,
|
|
39
|
+
0,
|
|
40
|
+
0,
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function parseTakomiOutput(outputText: string) {
|
|
45
|
+
const rawLines = outputText.split(/\r?\n/);
|
|
46
|
+
const textLines: string[] = [];
|
|
47
|
+
const recentTools: Array<{ tool: string; args: string; endMs: number }> = [];
|
|
48
|
+
let currentTool: string | undefined;
|
|
49
|
+
let currentToolArgs: string | undefined;
|
|
50
|
+
|
|
51
|
+
for (const line of rawLines) {
|
|
52
|
+
if (!line.trim()) continue;
|
|
53
|
+
|
|
54
|
+
// 1. Check for tool lifecycle markers
|
|
55
|
+
if (line.startsWith("Tool start: ")) {
|
|
56
|
+
currentTool = line.replace("Tool start: ", "").trim();
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (line.startsWith("Tool complete: ") || line.startsWith("Tool failed: ")) {
|
|
60
|
+
const toolName = line.replace(/Tool (complete|failed): /, "").trim();
|
|
61
|
+
recentTools.push({ tool: toolName, args: "", endMs: Date.now() });
|
|
62
|
+
if (currentTool === toolName) {
|
|
63
|
+
currentTool = undefined;
|
|
64
|
+
currentToolArgs = undefined;
|
|
65
|
+
}
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 2. Handle JSON blobs (tool calls)
|
|
70
|
+
const jsonStartIdx = line.indexOf('{"');
|
|
71
|
+
if (jsonStartIdx !== -1) {
|
|
72
|
+
const beforeJson = line.substring(0, jsonStartIdx).trim();
|
|
73
|
+
if (beforeJson) textLines.push(beforeJson);
|
|
74
|
+
|
|
75
|
+
const jsonPart = line.substring(jsonStartIdx);
|
|
76
|
+
try {
|
|
77
|
+
const parsed = JSON.parse(jsonPart);
|
|
78
|
+
const toolName = parsed.tool || (parsed.command ? parsed.command.split(" ")[0] : undefined);
|
|
79
|
+
if (toolName) {
|
|
80
|
+
const args = parsed.args ? JSON.stringify(parsed.args) : (parsed.command || "");
|
|
81
|
+
currentTool = toolName;
|
|
82
|
+
currentToolArgs = args;
|
|
83
|
+
}
|
|
84
|
+
} catch (e) {
|
|
85
|
+
if (jsonPart.trim().length > 1) {
|
|
86
|
+
textLines.push(jsonPart);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// 3. Skip lone brackets
|
|
93
|
+
const trimmed = line.trim();
|
|
94
|
+
if (trimmed === "{" || trimmed === "}") continue;
|
|
95
|
+
|
|
96
|
+
// 4. Everything else is text (thoughts)
|
|
97
|
+
textLines.push(line);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return { textLines, recentTools, currentTool, currentToolArgs };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function renderTakomiSubagentResult(result: ToolResult, options: { expanded?: boolean; isPartial?: boolean }, theme: Theme, context: any) {
|
|
104
|
+
const details = result.details;
|
|
105
|
+
const results = details?.results ?? [];
|
|
106
|
+
|
|
107
|
+
if (results.length === 0) {
|
|
108
|
+
const text = result.content?.find((item) => item.type === "text")?.text ?? "(no output)";
|
|
109
|
+
return new Text(text, 0, 0);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const mappedResults: SingleResult[] = results.map((r, i) => {
|
|
113
|
+
const isRunning = r.code === -1;
|
|
114
|
+
const outputText = r.output || r.stderr || "";
|
|
115
|
+
const { textLines, recentTools, currentTool, currentToolArgs } = parseTakomiOutput(outputText);
|
|
116
|
+
|
|
117
|
+
const recentOutput = textLines.slice(-5);
|
|
118
|
+
|
|
119
|
+
const progress: AgentProgress | undefined = isRunning ? {
|
|
120
|
+
index: i,
|
|
121
|
+
agent: r.agent,
|
|
122
|
+
status: "running",
|
|
123
|
+
task: r.task || "",
|
|
124
|
+
lastActivityAt: Date.now(),
|
|
125
|
+
currentTool,
|
|
126
|
+
currentToolArgs,
|
|
127
|
+
currentToolStartedAt: currentTool ? Date.now() : undefined,
|
|
128
|
+
recentTools,
|
|
129
|
+
recentOutput,
|
|
130
|
+
toolCount: recentTools.length + (currentTool ? 1 : 0),
|
|
131
|
+
tokens: 0,
|
|
132
|
+
durationMs: 0,
|
|
133
|
+
} : {
|
|
134
|
+
index: i,
|
|
135
|
+
agent: r.agent,
|
|
136
|
+
status: "completed", // Takomi results in mappedResults are usually final if code !== -1
|
|
137
|
+
task: r.task || "",
|
|
138
|
+
lastActivityAt: Date.now(),
|
|
139
|
+
recentTools,
|
|
140
|
+
recentOutput: [],
|
|
141
|
+
toolCount: recentTools.length,
|
|
142
|
+
tokens: 0,
|
|
143
|
+
durationMs: 0,
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
agent: r.agent,
|
|
148
|
+
task: r.task || "",
|
|
149
|
+
exitCode: isRunning ? 0 : r.code,
|
|
150
|
+
model: r.model,
|
|
151
|
+
usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 },
|
|
152
|
+
progress,
|
|
153
|
+
truncation: isRunning ? undefined : {
|
|
154
|
+
text: textLines.join("\n").replace(/\n/g, " \n"),
|
|
155
|
+
truncated: false
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const mappedDetails: Details = {
|
|
161
|
+
mode: details?.mode ?? "single",
|
|
162
|
+
results: mappedResults,
|
|
163
|
+
chainAgents: details?.mode === "chain" ? results.map(r => r.agent) : undefined,
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const agentToolResult: AgentToolResult<Details> = {
|
|
167
|
+
content: result.content as any || [],
|
|
168
|
+
details: mappedDetails,
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
syncResultAnimation(agentToolResult, context);
|
|
172
|
+
return renderSubagentResult(agentToolResult, { expanded: options.expanded ?? false }, theme);
|
|
173
|
+
}
|
|
174
|
+
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
3
|
+
import type { TakomiThinkingLevel } from "../../../src/pi-takomi-core";
|
|
4
|
+
import { loadTakomiProfile } from "../takomi-runtime/profile";
|
|
5
|
+
import {
|
|
6
|
+
TAKOMI_SUBAGENT_EVENT_CHANNEL,
|
|
7
|
+
type TakomiSubagentRuntimeEvent,
|
|
8
|
+
} from "../takomi-runtime/subagent-types";
|
|
9
|
+
import { resolveAgentName } from "./agent-aliases";
|
|
10
|
+
import { discoverTakomiAgents, type TakomiAgentConfig, type TakomiAgentScope } from "./agents";
|
|
11
|
+
import { createTakomiDelegationPlan, renderTakomiDelegationPlan } from "./delegation-plan";
|
|
12
|
+
import { dispatchTakomiSubagent, type TakomiDispatchResult } from "./dispatch";
|
|
13
|
+
import { createTakomiLiveUpdateBridge } from "./live-updates";
|
|
14
|
+
|
|
15
|
+
type ChecklistItem = string | { text: string; done?: boolean };
|
|
16
|
+
|
|
17
|
+
export type TakomiSubagentToolTask = {
|
|
18
|
+
agent: string;
|
|
19
|
+
task: string;
|
|
20
|
+
workflow?: string;
|
|
21
|
+
skills?: string[];
|
|
22
|
+
model?: string;
|
|
23
|
+
fallbackModels?: string[];
|
|
24
|
+
thinking?: TakomiThinkingLevel;
|
|
25
|
+
conversationId?: string;
|
|
26
|
+
cwd?: string;
|
|
27
|
+
checklist?: ChecklistItem[];
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type TakomiSubagentToolParams = Partial<TakomiSubagentToolTask> & {
|
|
31
|
+
tasks?: TakomiSubagentToolTask[];
|
|
32
|
+
chain?: TakomiSubagentToolTask[];
|
|
33
|
+
confirmLaunch?: boolean;
|
|
34
|
+
previewOnly?: boolean;
|
|
35
|
+
agentScope?: TakomiAgentScope;
|
|
36
|
+
confirmProjectAgents?: boolean;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
type ToolUpdate = (partial: {
|
|
40
|
+
content: Array<{ type: "text"; text: string }>;
|
|
41
|
+
details: Record<string, unknown>;
|
|
42
|
+
}) => void;
|
|
43
|
+
const MAX_PARALLEL_TASKS = 8, MAX_CONCURRENCY = 4;
|
|
44
|
+
|
|
45
|
+
function emitRuntimeSubagentEvent(pi: ExtensionAPI, event: TakomiSubagentRuntimeEvent): void {
|
|
46
|
+
pi.events.emit(TAKOMI_SUBAGENT_EVENT_CHANNEL, event);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function resultText(result: TakomiDispatchResult): string {
|
|
50
|
+
return [
|
|
51
|
+
result.preflight,
|
|
52
|
+
result.output || result.stderr || `Subagent ${result.agent} finished without output.`,
|
|
53
|
+
].filter(Boolean).join("\n\n");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function textResult<TDetails extends Record<string, unknown>>(text: string, details: TDetails, isError?: boolean) {
|
|
57
|
+
return { content: [{ type: "text" as const, text }], details, isError };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function mapWithConcurrencyLimit<TIn, TOut>(
|
|
61
|
+
items: TIn[],
|
|
62
|
+
concurrency: number,
|
|
63
|
+
fn: (item: TIn, index: number) => Promise<TOut>,
|
|
64
|
+
): Promise<TOut[]> {
|
|
65
|
+
const results = new Array<TOut>(items.length);
|
|
66
|
+
let nextIndex = 0;
|
|
67
|
+
const workers = new Array(Math.min(Math.max(concurrency, 1), items.length)).fill(undefined).map(async () => {
|
|
68
|
+
while (nextIndex < items.length) {
|
|
69
|
+
const current = nextIndex++;
|
|
70
|
+
results[current] = await fn(items[current], current);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
await Promise.all(workers);
|
|
74
|
+
return results;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function hasProjectAgents(tasks: Array<{ agent: string }>, agents: Map<string, TakomiAgentConfig>): boolean {
|
|
78
|
+
return tasks.some((task) => agents.get(task.agent)?.source === "project");
|
|
79
|
+
}
|
|
80
|
+
function resolveMode(params: TakomiSubagentToolParams): "single" | "parallel" | "chain" | undefined {
|
|
81
|
+
const hasChain = Boolean(params.chain?.length);
|
|
82
|
+
const hasParallel = Boolean(params.tasks?.length);
|
|
83
|
+
const hasSingle = Boolean(params.agent && params.task);
|
|
84
|
+
if (Number(hasChain) + Number(hasParallel) + Number(hasSingle) !== 1) return undefined;
|
|
85
|
+
return hasChain ? "chain" : hasParallel ? "parallel" : "single";
|
|
86
|
+
}
|
|
87
|
+
function resolveTasks(params: TakomiSubagentToolParams): TakomiSubagentToolTask[] {
|
|
88
|
+
if (params.chain?.length) return params.chain;
|
|
89
|
+
if (params.tasks?.length) return params.tasks;
|
|
90
|
+
if (params.agent && params.task) {
|
|
91
|
+
return [{
|
|
92
|
+
agent: params.agent,
|
|
93
|
+
task: params.task,
|
|
94
|
+
workflow: params.workflow,
|
|
95
|
+
skills: params.skills,
|
|
96
|
+
model: params.model,
|
|
97
|
+
fallbackModels: params.fallbackModels,
|
|
98
|
+
thinking: params.thinking,
|
|
99
|
+
conversationId: params.conversationId,
|
|
100
|
+
cwd: params.cwd,
|
|
101
|
+
checklist: params.checklist,
|
|
102
|
+
}];
|
|
103
|
+
}
|
|
104
|
+
return [];
|
|
105
|
+
}
|
|
106
|
+
export async function executeTakomiSubagentTool(
|
|
107
|
+
pi: ExtensionAPI,
|
|
108
|
+
params: TakomiSubagentToolParams,
|
|
109
|
+
signal: AbortSignal | undefined,
|
|
110
|
+
onUpdate: ToolUpdate | undefined,
|
|
111
|
+
ctx: ExtensionContext,
|
|
112
|
+
) {
|
|
113
|
+
const rootCwd = params.cwd ? path.resolve(ctx.cwd, params.cwd) : ctx.cwd;
|
|
114
|
+
const profile = await loadTakomiProfile(rootCwd);
|
|
115
|
+
const agentScope = params.agentScope ?? "both";
|
|
116
|
+
const agents = discoverTakomiAgents(rootCwd, agentScope);
|
|
117
|
+
const byName = new Map<string, TakomiAgentConfig>(agents.map((agent) => [agent.name, agent]));
|
|
118
|
+
const mode = resolveMode(params);
|
|
119
|
+
const tasks = resolveTasks(params).map((task) => ({
|
|
120
|
+
...task,
|
|
121
|
+
agent: resolveAgentName(task.agent, byName),
|
|
122
|
+
}));
|
|
123
|
+
|
|
124
|
+
if (!mode) {
|
|
125
|
+
return textResult(
|
|
126
|
+
`Provide exactly one mode: agent/task, tasks, or chain.\nAvailable agents: ${agents.map((agent) => `${agent.name} (${agent.source})`).join(", ") || "none"}`,
|
|
127
|
+
{ results: [], availableAgents: agents.map((agent) => agent.name), agentScope },
|
|
128
|
+
true,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
if (mode === "parallel" && tasks.length > MAX_PARALLEL_TASKS) {
|
|
132
|
+
return textResult(`Too many parallel tasks (${tasks.length}). Max is ${MAX_PARALLEL_TASKS}.`, { results: [], agentScope }, true);
|
|
133
|
+
}
|
|
134
|
+
if (params.confirmProjectAgents !== false && ctx.hasUI && hasProjectAgents(tasks, byName)) {
|
|
135
|
+
const names = tasks.map((task) => byName.get(task.agent)).filter((agent): agent is TakomiAgentConfig => agent?.source === "project").map((agent) => agent.name);
|
|
136
|
+
const ok = await ctx.ui.confirm("Run project-local Takomi agents?", `Agents: ${[...new Set(names)].join(", ")}\n\nProject agents are repo-controlled. Continue only for trusted repositories.`);
|
|
137
|
+
if (!ok) return textResult("Canceled: project-local agents not approved.", { results: [], agentScope, mode });
|
|
138
|
+
}
|
|
139
|
+
const plan = createTakomiDelegationPlan({
|
|
140
|
+
source: "takomi-tool",
|
|
141
|
+
launchMode: profile.launchMode ?? "auto",
|
|
142
|
+
profile,
|
|
143
|
+
tasks: tasks.map((task, index) => ({
|
|
144
|
+
id: task.conversationId ?? `direct-${index + 1}`,
|
|
145
|
+
title: task.task,
|
|
146
|
+
agent: task.agent,
|
|
147
|
+
task: task.task,
|
|
148
|
+
workflow: task.workflow,
|
|
149
|
+
model: task.model,
|
|
150
|
+
fallbackModels: task.fallbackModels,
|
|
151
|
+
thinking: task.thinking,
|
|
152
|
+
conversationId: task.conversationId,
|
|
153
|
+
checklist: task.checklist,
|
|
154
|
+
dispatchPolicy: "subagent",
|
|
155
|
+
})),
|
|
156
|
+
});
|
|
157
|
+
if (params.previewOnly || (plan.launchMode === "manual" && !params.confirmLaunch)) {
|
|
158
|
+
return textResult(renderTakomiDelegationPlan(plan), { plan, availableAgents: agents.map((agent) => agent.name), agentScope, mode });
|
|
159
|
+
}
|
|
160
|
+
const live = createTakomiLiveUpdateBridge(tasks, mode, agentScope, onUpdate);
|
|
161
|
+
const runOne = async (item: TakomiSubagentToolTask, index: number, previousOutput = "") => {
|
|
162
|
+
const config = byName.get(item.agent);
|
|
163
|
+
if (!config) throw new Error(`Unknown subagent '${item.agent}'. Available: ${agents.map((agent) => `${agent.name} (${agent.source})`).join(", ") || "none"}`);
|
|
164
|
+
const result = await dispatchTakomiSubagent(ctx, {
|
|
165
|
+
agent: config,
|
|
166
|
+
task: item.task.replaceAll("{previous}", previousOutput),
|
|
167
|
+
rootCwd,
|
|
168
|
+
cwd: item.cwd,
|
|
169
|
+
workflow: item.workflow,
|
|
170
|
+
skills: item.skills,
|
|
171
|
+
model: item.model,
|
|
172
|
+
fallbackModels: item.fallbackModels,
|
|
173
|
+
thinking: item.thinking,
|
|
174
|
+
conversationId: item.conversationId,
|
|
175
|
+
checklist: item.checklist,
|
|
176
|
+
source: "takomi-tool",
|
|
177
|
+
}, signal, {
|
|
178
|
+
emit: (event) => {
|
|
179
|
+
emitRuntimeSubagentEvent(pi, event);
|
|
180
|
+
live.event(index, event);
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
live.finish(index, result);
|
|
184
|
+
return result;
|
|
185
|
+
};
|
|
186
|
+
const results: TakomiDispatchResult[] = [];
|
|
187
|
+
try {
|
|
188
|
+
if (mode === "chain") {
|
|
189
|
+
let previousOutput = "";
|
|
190
|
+
for (const [index, item] of tasks.entries()) {
|
|
191
|
+
const result = await runOne(item, index, previousOutput);
|
|
192
|
+
previousOutput = result.output;
|
|
193
|
+
results.push(result);
|
|
194
|
+
if (result.code !== 0) return textResult(resultText(result), { results, mode, agentScope }, true);
|
|
195
|
+
}
|
|
196
|
+
} else if (mode === "parallel") {
|
|
197
|
+
results.push(...await mapWithConcurrencyLimit(tasks, MAX_CONCURRENCY, async (item, index) => {
|
|
198
|
+
return runOne(item, index);
|
|
199
|
+
}));
|
|
200
|
+
} else {
|
|
201
|
+
results.push(await runOne(tasks[0], 0));
|
|
202
|
+
}
|
|
203
|
+
} catch (error) {
|
|
204
|
+
return textResult(error instanceof Error ? error.message : String(error), { results, mode, agentScope }, true);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const failed = results.find((result) => result.code !== 0);
|
|
208
|
+
return textResult(results.map(resultText).join("\n\n---\n\n"), { results, mode, agentScope }, Boolean(failed) || undefined);
|
|
209
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Run the full Takomi Vibe Build workflow for the next request
|
|
3
|
+
---
|
|
4
|
+
# Workflow: Build VibeCode Project V3 (The Builder)
|
|
5
|
+
|
|
6
|
+
> Pi prompt alias for the richer build workflow.
|
|
7
|
+
|
|
8
|
+
> **Version 3** — verification after every file, FR-based progress, type-safe development, and explicit handoff.
|
|
9
|
+
|
|
10
|
+
**You are the VibeCode Builder Agent.**
|
|
11
|
+
You execute the approved plan.
|
|
12
|
+
You do **not** drift into loose strategy here — you build, verify, and report.
|
|
13
|
+
Follow the blueprints precisely. Keep scope tight. Verify constantly.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Provider / Model Selection
|
|
18
|
+
Before using `takomi_subagent`, setting a model override, or naming a provider/model:
|
|
19
|
+
- use the injected Pi model-registry context and active Takomi routing policy
|
|
20
|
+
- prefer provider-qualified model IDs from the registry context
|
|
21
|
+
- only choose from available options
|
|
22
|
+
- do **not** hardcode a model/provider from memory
|
|
23
|
+
- if the intended provider is unavailable, say so immediately and continue without that subagent unless the user approves another route
|
|
24
|
+
- run `pi --list-models` only when registry context is missing or the user asks for visible diagnostics
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Steps
|
|
29
|
+
|
|
30
|
+
### 1. Context Loading (MANDATORY)
|
|
31
|
+
Before writing any code, read and internalize:
|
|
32
|
+
- `docs/Project_Requirements.md`
|
|
33
|
+
- `docs/Coding_Guidelines.md`
|
|
34
|
+
- `docs/issues/`
|
|
35
|
+
- `docs/mockups/` if they exist
|
|
36
|
+
|
|
37
|
+
Acknowledge aloud that you will:
|
|
38
|
+
- run `tsc --noEmit` after every TypeScript file edit when relevant
|
|
39
|
+
- reference the issue file for each FR implemented
|
|
40
|
+
- mark acceptance criteria as you complete them
|
|
41
|
+
|
|
42
|
+
If any of the required docs are missing, call that out clearly before proceeding.
|
|
43
|
+
|
|
44
|
+
### 2. Project Scaffolding / Setup
|
|
45
|
+
If setup work is needed:
|
|
46
|
+
- prefer `pnpm`
|
|
47
|
+
- use PowerShell-safe commands when giving command examples
|
|
48
|
+
- scaffold in a temporary directory when appropriate
|
|
49
|
+
- merge carefully into the repo root
|
|
50
|
+
- do **not** clobber existing work silently
|
|
51
|
+
|
|
52
|
+
Example scaffold flow:
|
|
53
|
+
|
|
54
|
+
```powershell
|
|
55
|
+
mkdir temp-scaffold
|
|
56
|
+
pnpm create next-app temp-scaffold --ts --tailwind --eslint --app --src-dir --import-alias "@/*" --use-pnpm --no-git --skip-install
|
|
57
|
+
Get-ChildItem -Path temp-scaffold -Force | Copy-Item -Destination . -Recurse -Force
|
|
58
|
+
Remove-Item -Path temp-scaffold -Recurse -Force
|
|
59
|
+
pnpm install
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 3. Styling / Foundation Setup
|
|
63
|
+
Establish or align global styling and structure with:
|
|
64
|
+
- the approved design system
|
|
65
|
+
- repository conventions
|
|
66
|
+
- mockups if present
|
|
67
|
+
|
|
68
|
+
If a global style layer exists, refine it instead of replacing it casually.
|
|
69
|
+
|
|
70
|
+
### 4. MUS Implementation Loop
|
|
71
|
+
For each FR marked `MUS`:
|
|
72
|
+
|
|
73
|
+
#### 4.1 Announce the FR
|
|
74
|
+
Use a visible progress statement such as:
|
|
75
|
+
|
|
76
|
+
```text
|
|
77
|
+
📋 Implementing FR-XXX: [Title]
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
#### 4.2 Read the Issue
|
|
81
|
+
Open `docs/issues/FR-XXX.md` and review:
|
|
82
|
+
- user story
|
|
83
|
+
- proposed solution
|
|
84
|
+
- implementation flow
|
|
85
|
+
- technical approach
|
|
86
|
+
- acceptance criteria
|
|
87
|
+
|
|
88
|
+
#### 4.3 Implement
|
|
89
|
+
Build according to:
|
|
90
|
+
- `docs/Coding_Guidelines.md`
|
|
91
|
+
- `docs/mockups/` if present
|
|
92
|
+
- the issue guidance
|
|
93
|
+
- existing project patterns
|
|
94
|
+
|
|
95
|
+
#### 4.4 Verify (MANDATORY)
|
|
96
|
+
After **every** TypeScript / TSX edit:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
npx tsc --noEmit
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
If type-check fails:
|
|
103
|
+
1. stop
|
|
104
|
+
2. fix the error before touching the next TS file
|
|
105
|
+
3. rerun until it passes
|
|
106
|
+
|
|
107
|
+
#### 4.5 Mark Progress
|
|
108
|
+
Update the corresponding issue file as acceptance criteria are completed.
|
|
109
|
+
|
|
110
|
+
Example:
|
|
111
|
+
|
|
112
|
+
```markdown
|
|
113
|
+
## Acceptance Criteria
|
|
114
|
+
- [x] Outcome 1 ✅ Completed
|
|
115
|
+
- [x] Outcome 2 ✅ Completed
|
|
116
|
+
- [ ] Outcome 3
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 5. Progress Checkpoints
|
|
120
|
+
After every 3 FRs, or after a meaningful chunk of work, report:
|
|
121
|
+
- completed FRs
|
|
122
|
+
- blocked FRs
|
|
123
|
+
- current verification status
|
|
124
|
+
- next FRs
|
|
125
|
+
|
|
126
|
+
Suggested format:
|
|
127
|
+
|
|
128
|
+
```text
|
|
129
|
+
📊 Progress Checkpoint
|
|
130
|
+
|
|
131
|
+
✅ Completed:
|
|
132
|
+
- FR-001: [Title]
|
|
133
|
+
- FR-002: [Title]
|
|
134
|
+
- FR-003: [Title]
|
|
135
|
+
|
|
136
|
+
📈 Type-check: PASS
|
|
137
|
+
🎯 Next: FR-004, FR-005, FR-006
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### 6. Final Verification Gate
|
|
141
|
+
Before claiming build completion, run the strongest practical verification available:
|
|
142
|
+
- TypeScript
|
|
143
|
+
- lint
|
|
144
|
+
- build
|
|
145
|
+
- project verification scripts if present
|
|
146
|
+
|
|
147
|
+
Typical sequence:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
npx tsc --noEmit
|
|
151
|
+
pnpm lint
|
|
152
|
+
pnpm build
|
|
153
|
+
python scripts/vibe-verify.py 2>/dev/null
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Fix failures before declaring success.
|
|
157
|
+
|
|
158
|
+
### 7. Generate Handoff Report
|
|
159
|
+
Create or update `docs/Builder_Handoff_Report.md` with:
|
|
160
|
+
- what was built
|
|
161
|
+
- FRs completed
|
|
162
|
+
- files created/modified
|
|
163
|
+
- verification results
|
|
164
|
+
- how to run the project
|
|
165
|
+
- future work / remaining FRs
|
|
166
|
+
|
|
167
|
+
Include a concise verification table when useful.
|
|
168
|
+
|
|
169
|
+
### 8. Final Message
|
|
170
|
+
End with a clear build handoff that states:
|
|
171
|
+
- implemented features
|
|
172
|
+
- verification results
|
|
173
|
+
- where the handoff report lives
|
|
174
|
+
- the recommended next command or next stage (`continueBuild` or `finalize`)
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Recovery Protocol
|
|
179
|
+
If something breaks badly:
|
|
180
|
+
- inspect `git status`
|
|
181
|
+
- inspect `git diff`
|
|
182
|
+
- revert surgically when needed
|
|
183
|
+
- stash if necessary
|
|
184
|
+
- do not plow ahead through broken state
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Output Rules
|
|
189
|
+
- treat build as a disciplined implementation workflow
|
|
190
|
+
- do not one-shot freestyle large implementation without structure
|
|
191
|
+
- keep progress visible
|
|
192
|
+
- keep scope controlled
|
|
193
|
+
- keep verification explicit
|
|
194
|
+
- stay aligned with approved requirements and mockups
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Current User Request
|
|
199
|
+
$@
|