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.
Files changed (66) hide show
  1. package/.pi/README.md +124 -0
  2. package/.pi/agents/architect.md +16 -0
  3. package/.pi/agents/coder.md +15 -0
  4. package/.pi/agents/designer.md +18 -0
  5. package/.pi/agents/orchestrator.md +23 -0
  6. package/.pi/agents/reviewer.md +17 -0
  7. package/.pi/extensions/oauth-router/README.md +125 -0
  8. package/.pi/extensions/oauth-router/commands.ts +380 -0
  9. package/.pi/extensions/oauth-router/config.ts +200 -0
  10. package/.pi/extensions/oauth-router/index.ts +41 -0
  11. package/.pi/extensions/oauth-router/oauth-flow.ts +154 -0
  12. package/.pi/extensions/oauth-router/oauth-store.ts +121 -0
  13. package/.pi/extensions/oauth-router/package.json +14 -0
  14. package/.pi/extensions/oauth-router/policies.ts +27 -0
  15. package/.pi/extensions/oauth-router/provider.ts +492 -0
  16. package/.pi/extensions/oauth-router/scripts/vibe-verify.py +98 -0
  17. package/.pi/extensions/oauth-router/state.ts +174 -0
  18. package/.pi/extensions/oauth-router/types.ts +153 -0
  19. package/.pi/extensions/takomi-runtime/command-text.ts +130 -0
  20. package/.pi/extensions/takomi-runtime/commands.ts +179 -0
  21. package/.pi/extensions/takomi-runtime/context-panel.ts +282 -0
  22. package/.pi/extensions/takomi-runtime/index.ts +1288 -0
  23. package/.pi/extensions/takomi-runtime/profile.ts +114 -0
  24. package/.pi/extensions/takomi-runtime/routing-policy.ts +105 -0
  25. package/.pi/extensions/takomi-runtime/shared.ts +492 -0
  26. package/.pi/extensions/takomi-runtime/subagent-controller.ts +364 -0
  27. package/.pi/extensions/takomi-runtime/subagent-render.ts +501 -0
  28. package/.pi/extensions/takomi-runtime/subagent-types.ts +83 -0
  29. package/.pi/extensions/takomi-runtime/ui.ts +133 -0
  30. package/.pi/extensions/takomi-subagents/agent-aliases.ts +18 -0
  31. package/.pi/extensions/takomi-subagents/agents.ts +113 -0
  32. package/.pi/extensions/takomi-subagents/delegation-plan.ts +95 -0
  33. package/.pi/extensions/takomi-subagents/dispatch-helpers.ts +26 -0
  34. package/.pi/extensions/takomi-subagents/dispatch.ts +215 -0
  35. package/.pi/extensions/takomi-subagents/index.ts +75 -0
  36. package/.pi/extensions/takomi-subagents/live-updates.ts +83 -0
  37. package/.pi/extensions/takomi-subagents/native-render.ts +174 -0
  38. package/.pi/extensions/takomi-subagents/tool-runner.ts +209 -0
  39. package/.pi/prompts/build-prompt.md +199 -0
  40. package/.pi/prompts/design-prompt.md +134 -0
  41. package/.pi/prompts/genesis-prompt.md +133 -0
  42. package/.pi/prompts/orch-prompt.md +144 -0
  43. package/.pi/prompts/prime-prompt.md +80 -0
  44. package/.pi/prompts/takomi-prompt.md +96 -0
  45. package/.pi/prompts/vibe-primeAgent.md +97 -0
  46. package/.pi/prompts/vibe-spawnTask.md +133 -0
  47. package/.pi/prompts/vibe-syncDocs.md +100 -0
  48. package/.pi/themes/takomi-noir.json +81 -0
  49. package/README.md +28 -2
  50. package/assets/.agent/skills/pr-comment-fix/SKILL.md +182 -0
  51. package/assets/.agent/skills/takomi/SKILL.md +59 -59
  52. package/package.json +58 -45
  53. package/src/cli.js +158 -8
  54. package/src/doctor.js +84 -0
  55. package/src/pi-harness.js +351 -0
  56. package/src/pi-installer.js +171 -0
  57. package/src/pi-takomi-core/index.ts +4 -0
  58. package/src/pi-takomi-core/orchestration.ts +402 -0
  59. package/src/pi-takomi-core/routing.ts +93 -0
  60. package/src/pi-takomi-core/types.ts +173 -0
  61. package/src/pi-takomi-core/workflows.ts +299 -0
  62. package/src/skills-installer.js +101 -0
  63. package/src/utils.js +479 -447
  64. package/assets/.agent/skills/skill-creator/scripts/__pycache__/quick_validate.cpython-311.pyc +0 -0
  65. package/assets/.agent/skills/ui-ux-pro-max/scripts/__pycache__/core.cpython-311.pyc +0 -0
  66. 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
+ $@