takomi 2.1.26 → 2.1.28
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 +10 -0
- package/.pi/agents/architect.md +0 -1
- package/.pi/agents/coder.md +0 -1
- package/.pi/agents/designer.md +0 -1
- package/.pi/agents/orchestrator.md +0 -1
- package/.pi/agents/reviewer.md +0 -1
- package/.pi/extensions/takomi-context-manager/diagnostics.ts +1 -1
- package/.pi/extensions/takomi-context-manager/extension-conflicts.ts +14 -3
- package/.pi/extensions/takomi-context-manager/index.ts +6 -3
- package/.pi/extensions/takomi-context-manager/model-policy-gate.ts +28 -13
- package/.pi/extensions/takomi-runtime/commands.ts +24 -7
- package/.pi/extensions/takomi-runtime/context-panel.ts +583 -282
- package/.pi/extensions/takomi-runtime/index.ts +101 -6
- package/.pi/extensions/takomi-runtime/model-routing-defaults.ts +296 -0
- package/.pi/extensions/takomi-runtime/routing-policy.ts +67 -17
- package/.pi/extensions/takomi-runtime/takomi-stats.js +44 -16
- package/.pi/extensions/takomi-subagents/pi-subagents-engine.ts +12 -2
- package/.pi/extensions/takomi-subagents/tool-runner.ts +4 -2
- package/.pi/settings.json +18 -20
- package/README.md +34 -4
- package/package.json +4 -2
- package/src/cli.js +326 -33
- package/src/harness.js +132 -16
- package/src/pi-harness.js +27 -6
- package/src/pi-optional-features.js +195 -0
- package/src/postinstall.js +27 -0
- package/src/skills-catalog.js +245 -0
- package/src/skills-installer.js +244 -101
- package/src/skills-selection-tui.js +200 -0
- package/src/store.js +418 -240
- package/src/takomi-stats.js +44 -16
package/.pi/README.md
CHANGED
|
@@ -45,6 +45,7 @@ Inside Pi, use:
|
|
|
45
45
|
- The old standalone commands (`/takomi-genesis`, `/takomi-design`, `/takomi-build`, `/takomi-kickoff`, `/autoorch`, `/orch`, `/architect`, `/code`, `/review`, and the `/takomi-subagent*` variants) are folded into `/takomi` subcommands so slash autocomplete stays small.
|
|
46
46
|
- prompt shortcuts are suffixed with `-prompt` to avoid collisions with runtime commands, e.g. `/orch-prompt`, `/build-prompt`, `/design-prompt`, `/genesis-prompt`, `/takomi-prompt`, `/prime-prompt`
|
|
47
47
|
- a project-local theme is available at `.pi/themes/takomi-noir.json`; select `takomi-noir` in `/settings` to use the Takomi UI palette
|
|
48
|
+
- optional Pi feature packs are managed by `takomi setup pi` or `takomi setup pi-features` and refreshed by `takomi refresh`; current defaults install Takomi Interview (`npm:@juicesharp/rpiv-ask-user-question`) while Takomi Todo (`npm:@juicesharp/rpiv-todo`), Browser QA (`npm:pi-chrome`), and Doc Preview (`npm:pi-markdown-preview`) stay opt-in
|
|
48
49
|
- additional workflow prompts are available as direct slash commands:
|
|
49
50
|
- `/vibe-primeAgent`
|
|
50
51
|
- `/vibe-spawnTask`
|
|
@@ -128,3 +129,12 @@ So when working on packaging, agents should distinguish between:
|
|
|
128
129
|
- rewrite JSON machine state
|
|
129
130
|
- keep task docs organized in `pending/`, `in-progress/`, `completed/`, and `blocked/`
|
|
130
131
|
- `takomi_board` intentionally cannot dispatch or redispatch agents. Use `takomi_subagent` for single, parallel, or chain execution, then call `takomi_board update_task` with the result.
|
|
132
|
+
|
|
133
|
+
## Attribution Notes
|
|
134
|
+
|
|
135
|
+
- Pi-native Takomi runs on [Pi](https://github.com/earendil-works/pi) / `@earendil-works/pi-coding-agent`, authored by Mario Zechner and Earendil Works.
|
|
136
|
+
- Takomi subagent execution builds on [`pi-subagents`](https://github.com/nicobailon/pi-subagents) by Nico Bailon, with Takomi adding lifecycle orchestration, board/state artifacts, model-routing policy, and review-loop conventions.
|
|
137
|
+
- Optional Takomi Interview and Todo feature packs integrate [`@juicesharp/rpiv-ask-user-question`](https://github.com/juicesharp/rpiv-mono/tree/main/packages/rpiv-ask-user-question) and [`@juicesharp/rpiv-todo`](https://github.com/juicesharp/rpiv-mono/tree/main/packages/rpiv-todo) by juicesharp.
|
|
138
|
+
- Optional Browser QA can use [`pi-chrome`](https://github.com/tianrendong/pi-chrome) by tianrendong.
|
|
139
|
+
- Optional Doc Preview can use [`pi-markdown-preview`](https://github.com/omaclaren/pi-markdown-preview) by omaclaren.
|
|
140
|
+
- [`context-mode`](https://github.com/mksglu/context-mode) by Mert Koseoğlu is credited as external research/power-user context tooling; it is not a Takomi default bundle.
|
package/.pi/agents/architect.md
CHANGED
package/.pi/agents/coder.md
CHANGED
package/.pi/agents/designer.md
CHANGED
package/.pi/agents/reviewer.md
CHANGED
|
@@ -41,7 +41,7 @@ export function renderReport(state: ContextManagerState, verbose = false): strin
|
|
|
41
41
|
lines.push("- Candidates: none");
|
|
42
42
|
}
|
|
43
43
|
if (report.duplicateExtensionWarnings.length > 0 || verbose) {
|
|
44
|
-
lines.push("", "Extension Conflict Diagnostics:", ...renderDuplicateExtensionGuidance(report.duplicateExtensionWarnings));
|
|
44
|
+
lines.push("", "Extension Conflict Diagnostics:", ...renderDuplicateExtensionGuidance(report.duplicateExtensionWarnings, report.cwd));
|
|
45
45
|
}
|
|
46
46
|
if (verbose) {
|
|
47
47
|
lines.push("", "Skill Index:", ...sortedSkills(state.skills).map((skill) => `- ${skill.name}`));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { access } from "node:fs/promises";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
2
3
|
import os from "node:os";
|
|
3
4
|
import path from "node:path";
|
|
4
|
-
import type { ContextManagerState } from "./state";
|
|
5
5
|
|
|
6
6
|
type KnownTakomiExtension = {
|
|
7
7
|
toolName: string;
|
|
@@ -43,9 +43,20 @@ export async function detectDuplicateTakomiExtensions(cwd: string): Promise<Arra
|
|
|
43
43
|
return warnings;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
function isTakomiSourceRepo(cwd: string): boolean {
|
|
47
|
+
return [
|
|
48
|
+
path.join(cwd, "package.json"),
|
|
49
|
+
path.join(cwd, "scripts", "pi-dev.ps1"),
|
|
50
|
+
path.join(cwd, ".pi", "extensions", "takomi-runtime", "index.ts"),
|
|
51
|
+
].every((filePath) => existsSync(filePath));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function renderDuplicateExtensionGuidance(warnings: Array<{ toolName: string; paths: string[] }>, cwd?: string): string[] {
|
|
47
55
|
if (warnings.length === 0) return ["- Duplicate Takomi extensions: none detected"];
|
|
48
|
-
const
|
|
56
|
+
const sourceRepo = cwd ? isTakomiSourceRepo(cwd) : false;
|
|
57
|
+
const lines = [sourceRepo
|
|
58
|
+
? "- Duplicate Takomi extensions detected (expected when running normal Pi inside the Takomi source repo):"
|
|
59
|
+
: "- Duplicate Takomi extensions detected:"];
|
|
49
60
|
for (const warning of warnings) {
|
|
50
61
|
lines.push(` - ${warning.toolName}`);
|
|
51
62
|
for (const filePath of warning.paths) lines.push(` - ${filePath}`);
|
|
@@ -11,6 +11,7 @@ import { registerDiagnostics } from "./diagnostics-tools";
|
|
|
11
11
|
import { installPrerequisiteGates } from "./prerequisite-gates";
|
|
12
12
|
import { installModelPolicyGate } from "./model-policy-gate";
|
|
13
13
|
import { detectDuplicateTakomiExtensions } from "./extension-conflicts";
|
|
14
|
+
import { loadTakomiModelRoutingSnapshot, renderCompactTakomiModelRoutingSummary } from "../takomi-runtime/model-routing-defaults";
|
|
14
15
|
import type { ContextManagerConfig } from "./types";
|
|
15
16
|
|
|
16
17
|
export default function takomiContextManager(pi: ExtensionAPI) {
|
|
@@ -33,6 +34,8 @@ export default function takomiContextManager(pi: ExtensionAPI) {
|
|
|
33
34
|
|
|
34
35
|
const candidates = findCandidates(event.prompt, state.skills, config);
|
|
35
36
|
const rewrite = rewritePrompt(event.systemPrompt, state.skills, candidates, config);
|
|
37
|
+
const routingSummary = renderCompactTakomiModelRoutingSummary(await loadTakomiModelRoutingSnapshot(ctx.cwd));
|
|
38
|
+
const rewrittenPrompt = routingSummary ? `${rewrite.prompt}\n\n${routingSummary}` : rewrite.prompt;
|
|
36
39
|
state.report = {
|
|
37
40
|
...state.report,
|
|
38
41
|
timestamp: new Date().toISOString(),
|
|
@@ -43,14 +46,14 @@ export default function takomiContextManager(pi: ExtensionAPI) {
|
|
|
43
46
|
duplicateExtensionWarnings,
|
|
44
47
|
promptRewrite: {
|
|
45
48
|
attempted: true,
|
|
46
|
-
changed: rewrite.changed,
|
|
49
|
+
changed: rewrite.changed || Boolean(routingSummary),
|
|
47
50
|
originalLength: event.systemPrompt.length,
|
|
48
|
-
rewrittenLength:
|
|
51
|
+
rewrittenLength: rewrittenPrompt.length,
|
|
49
52
|
removedSections: rewrite.removedSections,
|
|
50
53
|
warnings: rewrite.warnings,
|
|
51
54
|
},
|
|
52
55
|
};
|
|
53
56
|
|
|
54
|
-
return { systemPrompt:
|
|
57
|
+
return { systemPrompt: rewrittenPrompt };
|
|
55
58
|
});
|
|
56
59
|
}
|
|
@@ -5,6 +5,7 @@ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
|
5
5
|
import type { ContextManagerState } from "./state";
|
|
6
6
|
import { recordBlocked } from "./state";
|
|
7
7
|
import { resolveTakomiRoutingPolicy } from "../takomi-runtime/routing-policy";
|
|
8
|
+
import { approvedModelEquivalent, isTakomiModelApproved } from "../takomi-runtime/model-routing-defaults";
|
|
8
9
|
|
|
9
10
|
type Settings = {
|
|
10
11
|
takomi?: { modelRoutingPolicyFile?: string };
|
|
@@ -79,12 +80,21 @@ function isModelLike(value: string): boolean {
|
|
|
79
80
|
|| lower.includes("lmstudio/");
|
|
80
81
|
}
|
|
81
82
|
|
|
83
|
+
function extractPreferredProvider(text: string): string | undefined {
|
|
84
|
+
const match = text.match(/(?:preferred|default)\s+(?:provider|router)(?:\s*\/\s*(?:provider|router))?\s*:\s*([a-z0-9-]+)/i)
|
|
85
|
+
?? text.match(/use\s+([a-z0-9-]+)\s+as\s+(?:the\s+)?(?:provider|router)/i);
|
|
86
|
+
return match?.[1];
|
|
87
|
+
}
|
|
88
|
+
|
|
82
89
|
function collectModelsFromPolicy(text: string): string[] {
|
|
90
|
+
// Providerless names such as "GPT-5.5" are intent labels unless the policy
|
|
91
|
+
// declares a preferred provider/router header.
|
|
83
92
|
const explicit = (text.match(/[a-z0-9-]+\/[a-z0-9._-]+/gi) ?? []).filter(isModelLike);
|
|
93
|
+
const preferredProvider = extractPreferredProvider(text);
|
|
84
94
|
const inferred: string[] = [];
|
|
85
|
-
if (/gpt[- ]?5\.5/i.test(text)) inferred.push(
|
|
86
|
-
if (/gpt[- ]?5\.4(?!\s*mini)/i.test(text)) inferred.push(
|
|
87
|
-
if (/gpt[- ]?5\.4\s*mini/i.test(text)) inferred.push(
|
|
95
|
+
if (preferredProvider && /gpt[- ]?5\.5/i.test(text)) inferred.push(`${preferredProvider}/gpt-5.5`);
|
|
96
|
+
if (preferredProvider && /gpt[- ]?5\.4(?!\s*mini)/i.test(text)) inferred.push(`${preferredProvider}/gpt-5.4`);
|
|
97
|
+
if (preferredProvider && /gpt[- ]?5\.4\s*mini/i.test(text)) inferred.push(`${preferredProvider}/gpt-5.4-mini`);
|
|
88
98
|
return unique([...explicit, ...inferred]);
|
|
89
99
|
}
|
|
90
100
|
|
|
@@ -98,8 +108,8 @@ async function loadSnapshot(cwd: string): Promise<ModelPolicySnapshot> {
|
|
|
98
108
|
return { approvedModels, preferredModels: settingsModels.length ? unique(settingsModels) : approvedModels, sourceFiles };
|
|
99
109
|
}
|
|
100
110
|
|
|
101
|
-
function collectRequestedModelRefs(input: unknown): Array<{ holder: Record<string, unknown>; key: string; value: string }> {
|
|
102
|
-
const refs: Array<{ holder: Record<string, unknown>; key: string; value: string }> = [];
|
|
111
|
+
function collectRequestedModelRefs(input: unknown): Array<{ holder: Record<string, unknown>; key: string; value: string; index?: number }> {
|
|
112
|
+
const refs: Array<{ holder: Record<string, unknown>; key: string; value: string; index?: number }> = [];
|
|
103
113
|
function visit(value: unknown): void {
|
|
104
114
|
if (!value || typeof value !== "object") return;
|
|
105
115
|
if (Array.isArray(value)) {
|
|
@@ -111,7 +121,9 @@ function collectRequestedModelRefs(input: unknown): Array<{ holder: Record<strin
|
|
|
111
121
|
if (typeof record[key] === "string") refs.push({ holder: record, key, value: record[key] });
|
|
112
122
|
}
|
|
113
123
|
if (Array.isArray(record.fallbackModels)) {
|
|
114
|
-
|
|
124
|
+
const fallbackModels = record.fallbackModels.filter((item): item is string => typeof item === "string");
|
|
125
|
+
record.fallbackModels = fallbackModels;
|
|
126
|
+
fallbackModels.forEach((value: string, index: number) => refs.push({ holder: record, key: "fallbackModels", value, index }));
|
|
115
127
|
}
|
|
116
128
|
for (const key of ["tasks", "chain"]) visit(record[key]);
|
|
117
129
|
}
|
|
@@ -119,9 +131,12 @@ function collectRequestedModelRefs(input: unknown): Array<{ holder: Record<strin
|
|
|
119
131
|
return refs;
|
|
120
132
|
}
|
|
121
133
|
|
|
122
|
-
function
|
|
123
|
-
|
|
124
|
-
|
|
134
|
+
function setModelRef(ref: { holder: Record<string, unknown>; key: string; value: string; index?: number }, value: string): void {
|
|
135
|
+
if (ref.key === "fallbackModels" && typeof ref.index === "number" && Array.isArray(ref.holder.fallbackModels)) {
|
|
136
|
+
ref.holder.fallbackModels[ref.index] = value;
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
ref.holder[ref.key] = value;
|
|
125
140
|
}
|
|
126
141
|
|
|
127
142
|
function isModelFailure(text: string): boolean {
|
|
@@ -170,16 +185,16 @@ export function installModelPolicyGate(pi: ExtensionAPI, state: ContextManagerSt
|
|
|
170
185
|
const refs = collectRequestedModelRefs(event.input);
|
|
171
186
|
const corrections: string[] = [];
|
|
172
187
|
for (const ref of refs) {
|
|
173
|
-
if (
|
|
174
|
-
const equivalent =
|
|
188
|
+
if (isTakomiModelApproved(ref.value, approved)) continue;
|
|
189
|
+
const equivalent = approvedModelEquivalent(ref.value, approved);
|
|
175
190
|
if (equivalent) {
|
|
176
|
-
ref
|
|
191
|
+
setModelRef(ref, equivalent);
|
|
177
192
|
corrections.push(`${ref.value} -> ${equivalent}`);
|
|
178
193
|
continue;
|
|
179
194
|
}
|
|
180
195
|
const recovery = await askForInvalidModelRecovery(ctx, ref.value, approved);
|
|
181
196
|
if (recovery.action === "retry") {
|
|
182
|
-
ref
|
|
197
|
+
setModelRef(ref, recovery.model);
|
|
183
198
|
corrections.push(`${ref.value} -> ${recovery.model} (user selected recovery)`);
|
|
184
199
|
continue;
|
|
185
200
|
}
|
|
@@ -7,7 +7,7 @@ import type {
|
|
|
7
7
|
} from "../../../src/pi-takomi-core";
|
|
8
8
|
import { commandHelp, completions, statusText, workflowPrompt } from "./command-text";
|
|
9
9
|
import type { TakomiSubagentController } from "./subagent-types";
|
|
10
|
-
import {
|
|
10
|
+
import { previewTakomiRoutingPolicy, renderRoutingPolicyPreview, resolveTakomiRoutingPolicy, type RoutingPolicyInstallScope } from "./routing-policy";
|
|
11
11
|
import { collectTakomiStats, renderTakomiStats } from "./takomi-stats.js";
|
|
12
12
|
|
|
13
13
|
export type TakomiRuntimeCommandState = {
|
|
@@ -158,12 +158,29 @@ export function registerTakomiCommands(pi: ExtensionAPI, options: RegisterTakomi
|
|
|
158
158
|
const policyText = scopeMatch?.[2] ?? trimmed.replace(/^set\s+/i, "");
|
|
159
159
|
|
|
160
160
|
try {
|
|
161
|
-
const
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
:
|
|
166
|
-
|
|
161
|
+
const preview = previewTakomiRoutingPolicy(ctx.cwd, policyText, { scope });
|
|
162
|
+
const reviewPrompt = [
|
|
163
|
+
"Review this Takomi routing policy extraction before it is saved.",
|
|
164
|
+
"",
|
|
165
|
+
"Rules:",
|
|
166
|
+
"- Do not invent providers or model IDs not grounded in the policy.",
|
|
167
|
+
"- Providerless names like GPT-5.5 are routing intent unless a preferred provider/router is declared.",
|
|
168
|
+
"- Valid Takomi roles are: general, orchestrator, architect, designer, coder, reviewer.",
|
|
169
|
+
"- If the extraction is correct and safe, call takomi_apply_routing_policy with the exact policyText and scope below.",
|
|
170
|
+
"- If it is ambiguous or wrong, explain what the user should clarify and do not call the tool.",
|
|
171
|
+
"",
|
|
172
|
+
"Deterministic extraction:",
|
|
173
|
+
renderRoutingPolicyPreview(preview),
|
|
174
|
+
"",
|
|
175
|
+
"Original policy text:",
|
|
176
|
+
"```",
|
|
177
|
+
preview.policy,
|
|
178
|
+
"```",
|
|
179
|
+
"",
|
|
180
|
+
`Tool call to apply if safe: takomi_apply_routing_policy({ scope: ${JSON.stringify(scope)}, policyText: <original policy text> })`,
|
|
181
|
+
].join("\n");
|
|
182
|
+
ctx.ui.notify("Takomi routing extraction prepared. Sending it to the active model for review before saving.", "info");
|
|
183
|
+
pi.sendUserMessage(reviewPrompt);
|
|
167
184
|
} catch (error) {
|
|
168
185
|
ctx.ui.notify(error instanceof Error ? error.message : String(error), "error");
|
|
169
186
|
}
|