takomi 2.1.2 → 2.1.3

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 (49) hide show
  1. package/.pi/README.md +124 -124
  2. package/.pi/agents/architect.md +15 -15
  3. package/.pi/agents/coder.md +14 -14
  4. package/.pi/agents/designer.md +17 -17
  5. package/.pi/agents/orchestrator.md +22 -22
  6. package/.pi/agents/reviewer.md +16 -16
  7. package/.pi/extensions/oauth-router/README.md +125 -125
  8. package/.pi/extensions/oauth-router/commands.ts +380 -380
  9. package/.pi/extensions/oauth-router/config.ts +200 -200
  10. package/.pi/extensions/oauth-router/index.ts +41 -41
  11. package/.pi/extensions/oauth-router/oauth-flow.ts +154 -154
  12. package/.pi/extensions/oauth-router/oauth-store.ts +121 -121
  13. package/.pi/extensions/oauth-router/package.json +14 -14
  14. package/.pi/extensions/oauth-router/policies.ts +27 -27
  15. package/.pi/extensions/oauth-router/provider.ts +492 -492
  16. package/.pi/extensions/oauth-router/scripts/vibe-verify.py +98 -98
  17. package/.pi/extensions/oauth-router/state.ts +174 -174
  18. package/.pi/extensions/oauth-router/types.ts +153 -153
  19. package/.pi/extensions/takomi-runtime/command-text.ts +130 -130
  20. package/.pi/extensions/takomi-runtime/commands.ts +179 -179
  21. package/.pi/extensions/takomi-runtime/context-panel.ts +282 -282
  22. package/.pi/extensions/takomi-runtime/index.ts +1288 -1288
  23. package/.pi/extensions/takomi-runtime/profile.ts +114 -114
  24. package/.pi/extensions/takomi-runtime/routing-policy.ts +105 -105
  25. package/.pi/extensions/takomi-runtime/shared.ts +492 -492
  26. package/.pi/extensions/takomi-runtime/subagent-controller.ts +364 -364
  27. package/.pi/extensions/takomi-runtime/subagent-render.ts +501 -501
  28. package/.pi/extensions/takomi-runtime/subagent-types.ts +83 -83
  29. package/.pi/extensions/takomi-runtime/ui.ts +133 -133
  30. package/.pi/extensions/takomi-subagents/agent-aliases.ts +18 -18
  31. package/.pi/extensions/takomi-subagents/agents.ts +113 -113
  32. package/.pi/extensions/takomi-subagents/delegation-plan.ts +95 -95
  33. package/.pi/extensions/takomi-subagents/dispatch-helpers.ts +26 -26
  34. package/.pi/extensions/takomi-subagents/dispatch.ts +215 -215
  35. package/.pi/extensions/takomi-subagents/index.ts +75 -75
  36. package/.pi/extensions/takomi-subagents/live-updates.ts +83 -83
  37. package/.pi/extensions/takomi-subagents/native-render.ts +174 -174
  38. package/.pi/extensions/takomi-subagents/tool-runner.ts +209 -209
  39. package/.pi/themes/takomi-noir.json +81 -81
  40. package/package.json +59 -59
  41. package/src/doctor.js +87 -84
  42. package/src/pi-harness.js +355 -351
  43. package/src/pi-installer.js +193 -171
  44. package/src/pi-takomi-core/index.ts +4 -4
  45. package/src/pi-takomi-core/orchestration.ts +402 -402
  46. package/src/pi-takomi-core/routing.ts +93 -93
  47. package/src/pi-takomi-core/types.ts +173 -173
  48. package/src/pi-takomi-core/workflows.ts +299 -299
  49. package/src/skills-installer.js +101 -101
@@ -1,114 +1,114 @@
1
- import { readFile } from "node:fs/promises";
2
- import os from "node:os";
3
- import path from "node:path";
4
- import type {
5
- TakomiDispatchDefaults,
6
- TakomiProfile,
7
- TakomiRole,
8
- VibeLifecycleStage,
9
- } from "../../../src/pi-takomi-core";
10
-
11
- export const DEFAULT_TAKOMI_PROFILE: TakomiProfile = {
12
- version: 1,
13
- autoOrchestrate: true,
14
- launchMode: "auto",
15
- foreground: true,
16
- background: true,
17
- reviewAfterImplementation: true,
18
- roles: {
19
- orchestrator: { agent: "orchestrator", dispatchPolicy: "subagent" },
20
- architect: { agent: "architect", dispatchPolicy: "subagent" },
21
- design: { agent: "designer", dispatchPolicy: "subagent" },
22
- code: { agent: "coder", dispatchPolicy: "subagent" },
23
- review: { agent: "reviewer", dispatchPolicy: "review-first" },
24
- },
25
- stages: {
26
- genesis: { agent: "architect", dispatchPolicy: "subagent" },
27
- design: { agent: "designer", dispatchPolicy: "subagent" },
28
- build: { agent: "orchestrator", dispatchPolicy: "subagent" },
29
- },
30
- review: {
31
- enabled: true,
32
- agent: "reviewer",
33
- maxIterations: 2,
34
- sameConversation: true,
35
- },
36
- };
37
-
38
- function cleanStringArray(value: unknown): string[] | undefined {
39
- if (!Array.isArray(value)) return undefined;
40
- const parts = value.filter((item): item is string => typeof item === "string" && item.trim().length > 0);
41
- return parts.length ? parts : undefined;
42
- }
43
-
44
- function mergeDefaults(
45
- base?: TakomiDispatchDefaults,
46
- next?: TakomiDispatchDefaults,
47
- ): TakomiDispatchDefaults | undefined {
48
- if (!base && !next) return undefined;
49
- const fallbackModels = cleanStringArray(next?.fallbackModels) ?? cleanStringArray(base?.fallbackModels);
50
- return {
51
- ...base,
52
- ...next,
53
- fallbackModels,
54
- };
55
- }
56
-
57
- function mergeProfile(base: TakomiProfile, next?: Partial<TakomiProfile>): TakomiProfile {
58
- if (!next) return base;
59
- const roles = { ...(base.roles ?? {}) };
60
- for (const [role, defaults] of Object.entries(next.roles ?? {}) as Array<[TakomiRole, TakomiDispatchDefaults]>) {
61
- roles[role] = mergeDefaults(roles[role], defaults);
62
- }
63
-
64
- const stages = { ...(base.stages ?? {}) };
65
- for (const [stage, defaults] of Object.entries(next.stages ?? {}) as Array<[VibeLifecycleStage, TakomiDispatchDefaults]>) {
66
- stages[stage] = mergeDefaults(stages[stage], defaults);
67
- }
68
-
69
- return {
70
- ...base,
71
- ...next,
72
- version: 1,
73
- autoOrchestrate: next.autoOrchestrate ?? base.autoOrchestrate,
74
- launchMode: next.launchMode ?? base.launchMode ?? "auto",
75
- foreground: next.foreground ?? base.foreground ?? true,
76
- background: next.background ?? base.background ?? true,
77
- reviewAfterImplementation: next.reviewAfterImplementation ?? base.reviewAfterImplementation ?? true,
78
- roles,
79
- stages,
80
- review: {
81
- ...base.review,
82
- ...next.review,
83
- enabled: next.review?.enabled ?? base.review?.enabled ?? true,
84
- sameConversation: next.review?.sameConversation ?? base.review?.sameConversation ?? true,
85
- },
86
- };
87
- }
88
-
89
- async function readProfileFile(filePath: string): Promise<Partial<TakomiProfile> | undefined> {
90
- try {
91
- return JSON.parse(await readFile(filePath, "utf8")) as Partial<TakomiProfile>;
92
- } catch {
93
- return undefined;
94
- }
95
- }
96
-
97
- export async function loadTakomiProfile(cwd: string): Promise<TakomiProfile> {
98
- const projectProfilePath = path.join(cwd, ".pi", "takomi-profile.json");
99
- const userProfilePath = path.join(os.homedir(), ".pi", "agent", "takomi", "profile.json");
100
- const projectProfile = await readProfileFile(projectProfilePath);
101
- const userProfile = await readProfileFile(userProfilePath);
102
- return mergeProfile(mergeProfile(DEFAULT_TAKOMI_PROFILE, projectProfile), userProfile);
103
- }
104
-
105
- export function getProfileDefaults(
106
- profile: TakomiProfile,
107
- role: TakomiRole,
108
- stage?: VibeLifecycleStage,
109
- ): TakomiDispatchDefaults {
110
- return {
111
- ...(stage ? profile.stages?.[stage] : undefined),
112
- ...profile.roles?.[role],
113
- };
114
- }
1
+ import { readFile } from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import type {
5
+ TakomiDispatchDefaults,
6
+ TakomiProfile,
7
+ TakomiRole,
8
+ VibeLifecycleStage,
9
+ } from "../../../src/pi-takomi-core";
10
+
11
+ export const DEFAULT_TAKOMI_PROFILE: TakomiProfile = {
12
+ version: 1,
13
+ autoOrchestrate: true,
14
+ launchMode: "auto",
15
+ foreground: true,
16
+ background: true,
17
+ reviewAfterImplementation: true,
18
+ roles: {
19
+ orchestrator: { agent: "orchestrator", dispatchPolicy: "subagent" },
20
+ architect: { agent: "architect", dispatchPolicy: "subagent" },
21
+ design: { agent: "designer", dispatchPolicy: "subagent" },
22
+ code: { agent: "coder", dispatchPolicy: "subagent" },
23
+ review: { agent: "reviewer", dispatchPolicy: "review-first" },
24
+ },
25
+ stages: {
26
+ genesis: { agent: "architect", dispatchPolicy: "subagent" },
27
+ design: { agent: "designer", dispatchPolicy: "subagent" },
28
+ build: { agent: "orchestrator", dispatchPolicy: "subagent" },
29
+ },
30
+ review: {
31
+ enabled: true,
32
+ agent: "reviewer",
33
+ maxIterations: 2,
34
+ sameConversation: true,
35
+ },
36
+ };
37
+
38
+ function cleanStringArray(value: unknown): string[] | undefined {
39
+ if (!Array.isArray(value)) return undefined;
40
+ const parts = value.filter((item): item is string => typeof item === "string" && item.trim().length > 0);
41
+ return parts.length ? parts : undefined;
42
+ }
43
+
44
+ function mergeDefaults(
45
+ base?: TakomiDispatchDefaults,
46
+ next?: TakomiDispatchDefaults,
47
+ ): TakomiDispatchDefaults | undefined {
48
+ if (!base && !next) return undefined;
49
+ const fallbackModels = cleanStringArray(next?.fallbackModels) ?? cleanStringArray(base?.fallbackModels);
50
+ return {
51
+ ...base,
52
+ ...next,
53
+ fallbackModels,
54
+ };
55
+ }
56
+
57
+ function mergeProfile(base: TakomiProfile, next?: Partial<TakomiProfile>): TakomiProfile {
58
+ if (!next) return base;
59
+ const roles = { ...(base.roles ?? {}) };
60
+ for (const [role, defaults] of Object.entries(next.roles ?? {}) as Array<[TakomiRole, TakomiDispatchDefaults]>) {
61
+ roles[role] = mergeDefaults(roles[role], defaults);
62
+ }
63
+
64
+ const stages = { ...(base.stages ?? {}) };
65
+ for (const [stage, defaults] of Object.entries(next.stages ?? {}) as Array<[VibeLifecycleStage, TakomiDispatchDefaults]>) {
66
+ stages[stage] = mergeDefaults(stages[stage], defaults);
67
+ }
68
+
69
+ return {
70
+ ...base,
71
+ ...next,
72
+ version: 1,
73
+ autoOrchestrate: next.autoOrchestrate ?? base.autoOrchestrate,
74
+ launchMode: next.launchMode ?? base.launchMode ?? "auto",
75
+ foreground: next.foreground ?? base.foreground ?? true,
76
+ background: next.background ?? base.background ?? true,
77
+ reviewAfterImplementation: next.reviewAfterImplementation ?? base.reviewAfterImplementation ?? true,
78
+ roles,
79
+ stages,
80
+ review: {
81
+ ...base.review,
82
+ ...next.review,
83
+ enabled: next.review?.enabled ?? base.review?.enabled ?? true,
84
+ sameConversation: next.review?.sameConversation ?? base.review?.sameConversation ?? true,
85
+ },
86
+ };
87
+ }
88
+
89
+ async function readProfileFile(filePath: string): Promise<Partial<TakomiProfile> | undefined> {
90
+ try {
91
+ return JSON.parse(await readFile(filePath, "utf8")) as Partial<TakomiProfile>;
92
+ } catch {
93
+ return undefined;
94
+ }
95
+ }
96
+
97
+ export async function loadTakomiProfile(cwd: string): Promise<TakomiProfile> {
98
+ const projectProfilePath = path.join(cwd, ".pi", "takomi-profile.json");
99
+ const userProfilePath = path.join(os.homedir(), ".pi", "agent", "takomi", "profile.json");
100
+ const projectProfile = await readProfileFile(projectProfilePath);
101
+ const userProfile = await readProfileFile(userProfilePath);
102
+ return mergeProfile(mergeProfile(DEFAULT_TAKOMI_PROFILE, projectProfile), userProfile);
103
+ }
104
+
105
+ export function getProfileDefaults(
106
+ profile: TakomiProfile,
107
+ role: TakomiRole,
108
+ stage?: VibeLifecycleStage,
109
+ ): TakomiDispatchDefaults {
110
+ return {
111
+ ...(stage ? profile.stages?.[stage] : undefined),
112
+ ...profile.roles?.[role],
113
+ };
114
+ }
@@ -1,105 +1,105 @@
1
- import { mkdir, readFile, writeFile } from "node:fs/promises";
2
- import path from "node:path";
3
-
4
- export const TAKOMI_ROUTING_POLICY_RELATIVE = path.join(".pi", "takomi", "model-routing.md");
5
-
6
- export type RoutingPolicyInstallResult = {
7
- policyPath: string;
8
- settingsPath: string;
9
- settingsUpdated: boolean;
10
- detectedDefaults: string[];
11
- };
12
-
13
- type JsonObject = Record<string, unknown>;
14
-
15
- function asObject(value: unknown): JsonObject {
16
- return value && typeof value === "object" && !Array.isArray(value) ? value as JsonObject : {};
17
- }
18
-
19
- async function readJsonObject(filePath: string): Promise<JsonObject> {
20
- try {
21
- return asObject(JSON.parse(await readFile(filePath, "utf8")));
22
- } catch {
23
- return {};
24
- }
25
- }
26
-
27
- function extractQuotedPolicy(text: string): string {
28
- const triple = text.match(/"""([\s\S]*?)"""|```(?:\w+)?\s*([\s\S]*?)```/);
29
- const raw = (triple?.[1] ?? triple?.[2] ?? text).trim();
30
- return raw.replace(/^update\s+(?:takomi\s+)?(?:model\s+)?routing\s+(?:logic|policy|philosophy)\s*:?/i, "").trim();
31
- }
32
-
33
- function deriveSubagentDefaults(policy: string): { overrides: JsonObject; detected: string[] } {
34
- const lower = policy.toLowerCase();
35
- const has55 = /gpt[- ]?5\.5/.test(lower);
36
- const has54 = /gpt[- ]?5\.4(?!\s*mini)/.test(lower);
37
- const hasMini = /gpt[- ]?5\.4\s*mini/.test(lower);
38
- if (!has55 && !has54 && !hasMini) return { overrides: {}, detected: [] };
39
-
40
- const model55 = "oauth-router/gpt-5.5";
41
- const model54 = "oauth-router/gpt-5.4";
42
- const modelMini = "oauth-router/gpt-5.4-mini";
43
- const overrides: JsonObject = {};
44
- const detected: string[] = [];
45
-
46
- if (has55) {
47
- overrides.oracle = { model: model55, thinking: "high" };
48
- overrides.reviewer = { model: model55, thinking: "high" };
49
- overrides.planner = { model: model55, thinking: "medium" };
50
- detected.push("oracle/reviewer → GPT-5.5 high", "planner → GPT-5.5 medium");
51
- }
52
- if (has54) {
53
- overrides.worker = { model: model54, thinking: "high", fallbackModels: has55 ? [`${model55}:low`] : undefined };
54
- overrides.contextBuilder = { model: model54, thinking: "high" };
55
- overrides["context-builder"] = { model: model54, thinking: "high" };
56
- detected.push("worker/context-builder → GPT-5.4 high");
57
- }
58
- if (hasMini) {
59
- overrides.scout = { model: modelMini, thinking: "high" };
60
- overrides.delegate = { model: modelMini, thinking: "high" };
61
- detected.push("scout/delegate → GPT-5.4 Mini high");
62
- }
63
- return { overrides, detected };
64
- }
65
-
66
- export async function installTakomiRoutingPolicy(cwd: string, input: string): Promise<RoutingPolicyInstallResult> {
67
- const policy = extractQuotedPolicy(input);
68
- if (!policy) throw new Error("No routing policy text found. Paste the policy after /takomi routing or inside triple quotes.");
69
-
70
- const policyPath = path.join(cwd, TAKOMI_ROUTING_POLICY_RELATIVE);
71
- const settingsPath = path.join(cwd, ".pi", "settings.json");
72
- await mkdir(path.dirname(policyPath), { recursive: true });
73
- await mkdir(path.dirname(settingsPath), { recursive: true });
74
- await writeFile(policyPath, `# Takomi Model Routing Policy\n\n${policy}\n`, "utf8");
75
-
76
- const settings = await readJsonObject(settingsPath);
77
- const takomi = asObject(settings.takomi);
78
- takomi.modelRoutingPolicyFile = TAKOMI_ROUTING_POLICY_RELATIVE.replaceAll(path.sep, "/");
79
- settings.takomi = takomi;
80
-
81
- const { overrides, detected } = deriveSubagentDefaults(policy);
82
- if (Object.keys(overrides).length > 0) {
83
- const subagents = asObject(settings.subagents);
84
- const existingOverrides = asObject(subagents.agentOverrides);
85
- subagents.agentOverrides = { ...existingOverrides, ...overrides };
86
- settings.subagents = subagents;
87
- }
88
-
89
- await writeFile(settingsPath, `${JSON.stringify(settings, null, 2)}\n`, "utf8");
90
- return { policyPath, settingsPath, settingsUpdated: true, detectedDefaults: detected };
91
- }
92
-
93
- export async function loadTakomiRoutingPolicy(cwd: string): Promise<string | undefined> {
94
- const settingsPath = path.join(cwd, ".pi", "settings.json");
95
- const settings = await readJsonObject(settingsPath);
96
- const takomi = asObject(settings.takomi);
97
- const configured = typeof takomi.modelRoutingPolicyFile === "string" ? takomi.modelRoutingPolicyFile : TAKOMI_ROUTING_POLICY_RELATIVE;
98
- const policyPath = path.isAbsolute(configured) ? configured : path.join(cwd, configured);
99
- try {
100
- const text = (await readFile(policyPath, "utf8")).trim();
101
- return text || undefined;
102
- } catch {
103
- return undefined;
104
- }
105
- }
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+
4
+ export const TAKOMI_ROUTING_POLICY_RELATIVE = path.join(".pi", "takomi", "model-routing.md");
5
+
6
+ export type RoutingPolicyInstallResult = {
7
+ policyPath: string;
8
+ settingsPath: string;
9
+ settingsUpdated: boolean;
10
+ detectedDefaults: string[];
11
+ };
12
+
13
+ type JsonObject = Record<string, unknown>;
14
+
15
+ function asObject(value: unknown): JsonObject {
16
+ return value && typeof value === "object" && !Array.isArray(value) ? value as JsonObject : {};
17
+ }
18
+
19
+ async function readJsonObject(filePath: string): Promise<JsonObject> {
20
+ try {
21
+ return asObject(JSON.parse(await readFile(filePath, "utf8")));
22
+ } catch {
23
+ return {};
24
+ }
25
+ }
26
+
27
+ function extractQuotedPolicy(text: string): string {
28
+ const triple = text.match(/"""([\s\S]*?)"""|```(?:\w+)?\s*([\s\S]*?)```/);
29
+ const raw = (triple?.[1] ?? triple?.[2] ?? text).trim();
30
+ return raw.replace(/^update\s+(?:takomi\s+)?(?:model\s+)?routing\s+(?:logic|policy|philosophy)\s*:?/i, "").trim();
31
+ }
32
+
33
+ function deriveSubagentDefaults(policy: string): { overrides: JsonObject; detected: string[] } {
34
+ const lower = policy.toLowerCase();
35
+ const has55 = /gpt[- ]?5\.5/.test(lower);
36
+ const has54 = /gpt[- ]?5\.4(?!\s*mini)/.test(lower);
37
+ const hasMini = /gpt[- ]?5\.4\s*mini/.test(lower);
38
+ if (!has55 && !has54 && !hasMini) return { overrides: {}, detected: [] };
39
+
40
+ const model55 = "oauth-router/gpt-5.5";
41
+ const model54 = "oauth-router/gpt-5.4";
42
+ const modelMini = "oauth-router/gpt-5.4-mini";
43
+ const overrides: JsonObject = {};
44
+ const detected: string[] = [];
45
+
46
+ if (has55) {
47
+ overrides.oracle = { model: model55, thinking: "high" };
48
+ overrides.reviewer = { model: model55, thinking: "high" };
49
+ overrides.planner = { model: model55, thinking: "medium" };
50
+ detected.push("oracle/reviewer → GPT-5.5 high", "planner → GPT-5.5 medium");
51
+ }
52
+ if (has54) {
53
+ overrides.worker = { model: model54, thinking: "high", fallbackModels: has55 ? [`${model55}:low`] : undefined };
54
+ overrides.contextBuilder = { model: model54, thinking: "high" };
55
+ overrides["context-builder"] = { model: model54, thinking: "high" };
56
+ detected.push("worker/context-builder → GPT-5.4 high");
57
+ }
58
+ if (hasMini) {
59
+ overrides.scout = { model: modelMini, thinking: "high" };
60
+ overrides.delegate = { model: modelMini, thinking: "high" };
61
+ detected.push("scout/delegate → GPT-5.4 Mini high");
62
+ }
63
+ return { overrides, detected };
64
+ }
65
+
66
+ export async function installTakomiRoutingPolicy(cwd: string, input: string): Promise<RoutingPolicyInstallResult> {
67
+ const policy = extractQuotedPolicy(input);
68
+ if (!policy) throw new Error("No routing policy text found. Paste the policy after /takomi routing or inside triple quotes.");
69
+
70
+ const policyPath = path.join(cwd, TAKOMI_ROUTING_POLICY_RELATIVE);
71
+ const settingsPath = path.join(cwd, ".pi", "settings.json");
72
+ await mkdir(path.dirname(policyPath), { recursive: true });
73
+ await mkdir(path.dirname(settingsPath), { recursive: true });
74
+ await writeFile(policyPath, `# Takomi Model Routing Policy\n\n${policy}\n`, "utf8");
75
+
76
+ const settings = await readJsonObject(settingsPath);
77
+ const takomi = asObject(settings.takomi);
78
+ takomi.modelRoutingPolicyFile = TAKOMI_ROUTING_POLICY_RELATIVE.replaceAll(path.sep, "/");
79
+ settings.takomi = takomi;
80
+
81
+ const { overrides, detected } = deriveSubagentDefaults(policy);
82
+ if (Object.keys(overrides).length > 0) {
83
+ const subagents = asObject(settings.subagents);
84
+ const existingOverrides = asObject(subagents.agentOverrides);
85
+ subagents.agentOverrides = { ...existingOverrides, ...overrides };
86
+ settings.subagents = subagents;
87
+ }
88
+
89
+ await writeFile(settingsPath, `${JSON.stringify(settings, null, 2)}\n`, "utf8");
90
+ return { policyPath, settingsPath, settingsUpdated: true, detectedDefaults: detected };
91
+ }
92
+
93
+ export async function loadTakomiRoutingPolicy(cwd: string): Promise<string | undefined> {
94
+ const settingsPath = path.join(cwd, ".pi", "settings.json");
95
+ const settings = await readJsonObject(settingsPath);
96
+ const takomi = asObject(settings.takomi);
97
+ const configured = typeof takomi.modelRoutingPolicyFile === "string" ? takomi.modelRoutingPolicyFile : TAKOMI_ROUTING_POLICY_RELATIVE;
98
+ const policyPath = path.isAbsolute(configured) ? configured : path.join(cwd, configured);
99
+ try {
100
+ const text = (await readFile(policyPath, "utf8")).trim();
101
+ return text || undefined;
102
+ } catch {
103
+ return undefined;
104
+ }
105
+ }