takomi 2.1.44 → 2.1.45
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 +4 -3
- package/.pi/extensions/oauth-router/commands.ts +66 -35
- package/.pi/extensions/oauth-router/index.ts +51 -7
- package/.pi/extensions/oauth-router/report-ui.ts +205 -0
- package/.pi/extensions/takomi-context-manager/diagnostics-tools.ts +41 -3
- package/.pi/extensions/takomi-context-manager/diagnostics.ts +26 -0
- package/.pi/extensions/takomi-context-manager/index.ts +3 -2
- package/.pi/extensions/takomi-context-manager/model-policy-gate.ts +36 -15
- package/.pi/extensions/takomi-context-manager/policy-tools.ts +93 -42
- package/.pi/extensions/takomi-context-manager/skill-categories.d.ts +11 -0
- package/.pi/extensions/takomi-context-manager/skill-categories.js +212 -0
- package/.pi/extensions/takomi-context-manager/skill-registry.ts +179 -8
- package/.pi/extensions/takomi-context-manager/skill-tools.ts +208 -103
- package/.pi/extensions/takomi-context-manager/tool-renderers.ts +112 -0
- package/.pi/extensions/takomi-context-manager/types.ts +6 -0
- package/.pi/extensions/takomi-runtime/commands.ts +45 -12
- package/.pi/extensions/takomi-runtime/gate-provenance.ts +24 -0
- package/.pi/extensions/takomi-runtime/index.ts +111 -42
- package/.pi/extensions/takomi-runtime/routing-policy.ts +14 -2
- package/.pi/extensions/takomi-runtime/takomi-stats.js +46 -26
- package/.pi/extensions/takomi-runtime/tool-renderers.ts +234 -0
- package/.pi/extensions/takomi-runtime/workflow-catalog.ts +54 -0
- package/.pi/extensions/takomi-subagents/agents.ts +4 -0
- package/.pi/extensions/takomi-subagents/async-lifecycle.ts +401 -0
- package/.pi/extensions/takomi-subagents/detached-results.ts +940 -0
- package/.pi/extensions/takomi-subagents/index.ts +46 -1
- package/.pi/extensions/takomi-subagents/native-render.ts +250 -188
- package/.pi/extensions/takomi-subagents/pi-subagents-engine.ts +55 -46
- package/.pi/extensions/takomi-subagents/pi-subagents-internal.ts +11 -5
- package/.pi/extensions/takomi-subagents/result-heartbeat.ts +55 -0
- package/.pi/extensions/takomi-subagents/subagent-ux.ts +162 -0
- package/.pi/extensions/takomi-subagents/tool-runner.ts +158 -28
- package/.pi/takomi/model-routing.md +282 -3
- package/assets/.agent/skills/shared-resend-portfolio/SKILL.md +124 -0
- package/package.json +4 -3
- package/src/pi-takomi-core/types.ts +10 -0
- package/src/pi-takomi-core/workflows.ts +86 -45
- package/src/skills-catalog.js +2 -202
- package/src/skills-installer.js +4 -1
|
@@ -2,9 +2,10 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
|
2
2
|
import type { AutocompleteItem } from "@earendil-works/pi-tui";
|
|
3
3
|
import { Type } from "typebox";
|
|
4
4
|
import type { ContextManagerState } from "./state";
|
|
5
|
-
import { renderReport, type ContextReportMode } from "./diagnostics";
|
|
5
|
+
import { contextReportPresentation, renderReport, type ContextReportMode } from "./diagnostics";
|
|
6
6
|
import { discoverSkillsFromFilesystem, mergeSkills } from "./skill-registry";
|
|
7
7
|
import { persistReportSnapshot, restoreReportFromSession } from "./session-state";
|
|
8
|
+
import { renderCompactCard, renderExpandedMarkdown, renderToolCall, resultText, sanitizePresentation } from "./tool-renderers";
|
|
8
9
|
|
|
9
10
|
export function registerDiagnostics(pi: ExtensionAPI, state: ContextManagerState): void {
|
|
10
11
|
pi.registerTool({
|
|
@@ -29,7 +30,44 @@ export function registerDiagnostics(pi: ExtensionAPI, state: ContextManagerState
|
|
|
29
30
|
state.report.toolCalls.contextReport += 1;
|
|
30
31
|
persistReportSnapshot(pi, state, "context_report");
|
|
31
32
|
const mode = (params.verbose ? "verbose" : params.mode ?? "summary") as ContextReportMode;
|
|
32
|
-
|
|
33
|
+
const text = renderReport(state, mode);
|
|
34
|
+
// Keep model-facing content exactly as requested. Expanded presentation
|
|
35
|
+
// renders that same mode-specific report rather than silently promoting
|
|
36
|
+
// summary/problems requests to verbose diagnostics.
|
|
37
|
+
const presentation = contextReportPresentation(state);
|
|
38
|
+
return { content: [{ type: "text", text }], details: { ...state.report, mode, presentation } };
|
|
39
|
+
},
|
|
40
|
+
renderCall(args, theme) {
|
|
41
|
+
return renderToolCall("context_report", args.verbose ? "verbose" : args.mode ?? "summary", theme);
|
|
42
|
+
},
|
|
43
|
+
renderResult(result, { expanded }, theme) {
|
|
44
|
+
const details = result.details as {
|
|
45
|
+
mode?: ContextReportMode;
|
|
46
|
+
skillCount?: number;
|
|
47
|
+
loadedByTool?: string[];
|
|
48
|
+
loadedPolicies?: string[];
|
|
49
|
+
presentation?: {
|
|
50
|
+
status?: "success" | "warning" | "error" | "pending";
|
|
51
|
+
summary?: string;
|
|
52
|
+
attentionCount?: number;
|
|
53
|
+
};
|
|
54
|
+
} | undefined;
|
|
55
|
+
const presentation = details?.presentation;
|
|
56
|
+
const text = resultText(result);
|
|
57
|
+
const status = presentation?.status ?? "pending";
|
|
58
|
+
const summary = presentation?.summary ?? "Informational";
|
|
59
|
+
const attentionCount = presentation?.attentionCount ?? 0;
|
|
60
|
+
const metadata = `${details?.skillCount ?? 0} skills · ${details?.loadedPolicies?.length ?? 0} policies loaded · ${attentionCount} attention items`;
|
|
61
|
+
if (!expanded) {
|
|
62
|
+
return renderCompactCard({ status, title: "Context health", summary, metadata }, theme);
|
|
63
|
+
}
|
|
64
|
+
return renderExpandedMarkdown({
|
|
65
|
+
status,
|
|
66
|
+
title: "Context report",
|
|
67
|
+
summary,
|
|
68
|
+
metadata: [metadata, `Requested mode: ${details?.mode ?? "summary"}`],
|
|
69
|
+
markdown: text,
|
|
70
|
+
}, theme);
|
|
33
71
|
},
|
|
34
72
|
});
|
|
35
73
|
|
|
@@ -55,7 +93,7 @@ export function registerDiagnostics(pi: ExtensionAPI, state: ContextManagerState
|
|
|
55
93
|
persistReportSnapshot(pi, state, "context-report-command");
|
|
56
94
|
const requested = args.trim();
|
|
57
95
|
const mode: ContextReportMode = requested === "verbose" || requested === "problems" || requested === "summary" ? requested : "summary";
|
|
58
|
-
ctx.ui.notify(renderReport(state, mode), "info");
|
|
96
|
+
ctx.ui.notify(sanitizePresentation(renderReport(state, mode)), "info");
|
|
59
97
|
},
|
|
60
98
|
});
|
|
61
99
|
}
|
|
@@ -139,6 +139,32 @@ function overallStatus(issues: ReportIssue[]): { label: string; next: string; wa
|
|
|
139
139
|
return { label: "⚠ Attention Required", next: actionable[0].fix, warningCount: actionable.length };
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
+
export type ContextReportPresentation = {
|
|
143
|
+
status: "success" | "warning" | "error" | "pending";
|
|
144
|
+
summary: string;
|
|
145
|
+
attentionCount: number;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/** Structured execution metadata shared by context_report renderers. */
|
|
149
|
+
export function contextReportPresentation(state: ContextManagerState): ContextReportPresentation {
|
|
150
|
+
syncReportLedger(state);
|
|
151
|
+
const issues = collectIssues(state);
|
|
152
|
+
const overall = overallStatus(issues);
|
|
153
|
+
const actionable = issues.filter((issue) => issue.severity !== "info");
|
|
154
|
+
const status = issues.length === 0
|
|
155
|
+
? "success"
|
|
156
|
+
: actionable.some((issue) => issue.severity === "failed" || issue.severity === "blocked")
|
|
157
|
+
? "error"
|
|
158
|
+
: actionable.length > 0
|
|
159
|
+
? "warning"
|
|
160
|
+
: "pending";
|
|
161
|
+
return {
|
|
162
|
+
status,
|
|
163
|
+
summary: overall.label.replace(/^[^\w]+\s*/, ""),
|
|
164
|
+
attentionCount: overall.warningCount,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
142
168
|
function promptRewriteState(state: ContextManagerState): string {
|
|
143
169
|
const rewrite = state.report.promptRewrite;
|
|
144
170
|
if (!rewrite.attempted) return "Not run yet";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { loadConfig, DEFAULT_CONFIG } from "./config";
|
|
3
3
|
import { createState } from "./state";
|
|
4
|
-
import { collectSkillsFromOptions, collectSkillsFromXml, discoverSkillsFromFilesystem, mergeSkills } from "./skill-registry";
|
|
4
|
+
import { collectSkillsFromOptions, collectSkillsFromXml, discoverSkillsFromFilesystem, enrichSkillsWithInstallerTaxonomy, mergeSkills } from "./skill-registry";
|
|
5
5
|
import { discoverPolicies } from "./policy-registry";
|
|
6
6
|
import { findCandidates } from "./context-router";
|
|
7
7
|
import { rewritePrompt } from "./prompt-rewriter";
|
|
@@ -48,10 +48,11 @@ export default function takomiContextManager(pi: ExtensionAPI) {
|
|
|
48
48
|
const optionSkills = collectSkillsFromOptions(event.systemPromptOptions);
|
|
49
49
|
const xmlSkills = collectSkillsFromXml(event.systemPrompt);
|
|
50
50
|
const suppliedSkills = [...optionSkills, ...xmlSkills];
|
|
51
|
+
const enrichedSuppliedSkills = await enrichSkillsWithInstallerTaxonomy(suppliedSkills);
|
|
51
52
|
const filesystemSkills = suppliedSkills.length === 0
|
|
52
53
|
? await discoverSkillsFromFilesystem(ctx.cwd)
|
|
53
54
|
: [];
|
|
54
|
-
state.skills = mergeSkills([...filesystemSkills, ...
|
|
55
|
+
state.skills = mergeSkills([...filesystemSkills, ...enrichedSuppliedSkills]);
|
|
55
56
|
|
|
56
57
|
const candidates = findCandidates(event.prompt, state.skills, config);
|
|
57
58
|
const rewrite = rewritePrompt(event.systemPrompt, state.skills, candidates, config);
|
|
@@ -7,6 +7,7 @@ import { recordBlocked } from "./state";
|
|
|
7
7
|
import { resolveTakomiRoutingPolicy } from "../takomi-runtime/routing-policy";
|
|
8
8
|
import { approvedModelEquivalent, isTakomiModelApproved } from "../takomi-runtime/model-routing-defaults";
|
|
9
9
|
import { persistReportSnapshot } from "./session-state";
|
|
10
|
+
import { sanitizePresentation } from "./tool-renderers";
|
|
10
11
|
|
|
11
12
|
type Settings = {
|
|
12
13
|
takomi?: { modelRoutingPolicyFile?: string };
|
|
@@ -162,18 +163,40 @@ type RecoveryChoice =
|
|
|
162
163
|
| { action: "retry"; model: string }
|
|
163
164
|
| { action: "stop" };
|
|
164
165
|
|
|
166
|
+
type RecoveryOptions = {
|
|
167
|
+
options: string[];
|
|
168
|
+
retryModels: Map<string, string>;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Keep policy model IDs raw for selection and mutation, while passing only
|
|
173
|
+
* presentation-safe labels to the terminal UI. Disambiguate labels that become
|
|
174
|
+
* identical after sanitization so a selected label still maps to one raw ID.
|
|
175
|
+
*/
|
|
176
|
+
function recoveryOptions(approved: string[]): RecoveryOptions {
|
|
177
|
+
const retryModels = new Map<string, string>();
|
|
178
|
+
const options: string[] = [];
|
|
179
|
+
for (const model of approved) {
|
|
180
|
+
const baseLabel = `Retry with ${sanitizePresentation(model)}`;
|
|
181
|
+
let label = baseLabel;
|
|
182
|
+
let duplicate = 2;
|
|
183
|
+
while (retryModels.has(label)) label = `${baseLabel} (${duplicate++})`;
|
|
184
|
+
retryModels.set(label, model);
|
|
185
|
+
options.push(label);
|
|
186
|
+
}
|
|
187
|
+
options.push("Stop and let me send a new prompt");
|
|
188
|
+
return { options, retryModels };
|
|
189
|
+
}
|
|
190
|
+
|
|
165
191
|
async function askForInvalidModelRecovery(ctx: { ui: { select(title: string, options: string[]): Promise<string | undefined>; notify(message: string, level?: string): void }; abort?: () => void }, requested: string, approved: string[]): Promise<RecoveryChoice> {
|
|
166
192
|
if (approved.length === 0) return { action: "stop" };
|
|
167
|
-
const
|
|
168
|
-
...approved.map((model) => `Retry with ${model}`),
|
|
169
|
-
"Stop and let me send a new prompt",
|
|
170
|
-
];
|
|
193
|
+
const recovery = recoveryOptions(approved);
|
|
171
194
|
const choice = await ctx.ui.select(
|
|
172
|
-
`takomi_subagent requested a model outside your routing policy: ${requested}
|
|
173
|
-
options,
|
|
195
|
+
sanitizePresentation(`takomi_subagent requested a model outside your routing policy: ${requested}`),
|
|
196
|
+
recovery.options,
|
|
174
197
|
);
|
|
175
|
-
|
|
176
|
-
return { action: "stop" };
|
|
198
|
+
const model = choice ? recovery.retryModels.get(choice) : undefined;
|
|
199
|
+
return model ? { action: "retry", model } : { action: "stop" };
|
|
177
200
|
}
|
|
178
201
|
|
|
179
202
|
export function installModelPolicyGate(pi: ExtensionAPI, state: ContextManagerState): void {
|
|
@@ -221,7 +244,8 @@ export function installModelPolicyGate(pi: ExtensionAPI, state: ContextManagerSt
|
|
|
221
244
|
timestamp,
|
|
222
245
|
})));
|
|
223
246
|
persistReportSnapshot(pi, state, "model-policy-correction");
|
|
224
|
-
|
|
247
|
+
const notification = `Takomi context manager corrected subagent model routing:\n- ${corrections.map((correction) => `${sanitizePresentation(correction.from)} -> ${sanitizePresentation(correction.to)}${correction.recovery ? ` (${sanitizePresentation(correction.recovery)})` : ""}`).join("\n- ")}\n\nBe careful to follow /takomi routing policy next time.`;
|
|
248
|
+
ctx.ui.notify(notification, "warning");
|
|
225
249
|
}
|
|
226
250
|
});
|
|
227
251
|
|
|
@@ -231,12 +255,9 @@ export function installModelPolicyGate(pi: ExtensionAPI, state: ContextManagerSt
|
|
|
231
255
|
if (!isModelFailure(content)) return;
|
|
232
256
|
|
|
233
257
|
const snapshot = await loadSnapshot(ctx.cwd);
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
];
|
|
238
|
-
const choice = await ctx.ui.select("Takomi subagent model/provider failure. How do you want to continue?", options);
|
|
239
|
-
const retryModel = choice?.startsWith("Retry with ") ? choice.replace("Retry with ", "") : undefined;
|
|
258
|
+
const recovery = recoveryOptions(snapshot.approvedModels);
|
|
259
|
+
const choice = await ctx.ui.select("Takomi subagent model/provider failure. How do you want to continue?", recovery.options);
|
|
260
|
+
const retryModel = choice ? recovery.retryModels.get(choice) : undefined;
|
|
240
261
|
const stopped = !retryModel;
|
|
241
262
|
const guidance = [
|
|
242
263
|
"Takomi subagent failed with a model/provider-related error.",
|
|
@@ -1,42 +1,93 @@
|
|
|
1
|
-
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import { Type } from "typebox";
|
|
3
|
-
import type { ContextManagerState } from "./state";
|
|
4
|
-
import { syncReportLedger } from "./state";
|
|
5
|
-
import { renderPolicies, renderPolicyManifest } from "./policy-registry";
|
|
6
|
-
import { persistReportSnapshot, restoreReportFromSession } from "./session-state";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
state.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
1
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { Type } from "typebox";
|
|
3
|
+
import type { ContextManagerState } from "./state";
|
|
4
|
+
import { syncReportLedger } from "./state";
|
|
5
|
+
import { renderPolicies, renderPolicyManifest } from "./policy-registry";
|
|
6
|
+
import { persistReportSnapshot, restoreReportFromSession } from "./session-state";
|
|
7
|
+
import { renderCompactCard, renderExpandedMarkdown, renderToolCall, resultText } from "./tool-renderers";
|
|
8
|
+
import { normalizeName } from "./skill-registry";
|
|
9
|
+
|
|
10
|
+
function requestedPolicies(state: ContextManagerState, names: string[] | undefined): string[] {
|
|
11
|
+
return names?.length ? names : [...state.policies.values()].map((policy) => policy.name).sort((a, b) => a.localeCompare(b, "en"));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function missingPolicies(state: ContextManagerState, names: string[]): string[] {
|
|
15
|
+
return names.filter((name) => !state.policies.has(normalizeName(name)));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function registerPolicyTools(pi: ExtensionAPI, state: ContextManagerState): void {
|
|
19
|
+
pi.registerTool({
|
|
20
|
+
name: "policy_manifest",
|
|
21
|
+
label: "Policy Manifest",
|
|
22
|
+
description: "Return descriptions for available context policy packs without loading full policy content.",
|
|
23
|
+
promptSnippet: "Show available context policy pack descriptions",
|
|
24
|
+
parameters: Type.Object({ policies: Type.Optional(Type.Array(Type.String({ description: "Policy name to inspect" }))) }),
|
|
25
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
26
|
+
restoreReportFromSession(state, ctx);
|
|
27
|
+
state.report.timestamp = new Date().toISOString();
|
|
28
|
+
state.report.cwd = ctx.cwd;
|
|
29
|
+
state.report.toolCalls.policyManifest += 1;
|
|
30
|
+
persistReportSnapshot(pi, state, "policy_manifest");
|
|
31
|
+
const requested = requestedPolicies(state, params.policies);
|
|
32
|
+
const missing = missingPolicies(state, requested);
|
|
33
|
+
return { content: [{ type: "text", text: renderPolicyManifest(state.policies, params.policies ?? []) }], details: { requested, found: requested.length - missing.length, missing } };
|
|
34
|
+
},
|
|
35
|
+
renderCall(args, theme) {
|
|
36
|
+
return renderToolCall("policy_manifest", args.policies?.length ? `${args.policies.length} requested` : "all policies", theme);
|
|
37
|
+
},
|
|
38
|
+
renderResult(result, { expanded }, theme) {
|
|
39
|
+
const details = result.details as { requested?: string[]; found?: number; missing?: string[] } | undefined;
|
|
40
|
+
const requested = details?.requested ?? [];
|
|
41
|
+
const found = details?.found ?? 0;
|
|
42
|
+
const missing = details?.missing ?? [];
|
|
43
|
+
const status = missing.length ? "warning" : requested.length ? "success" : "pending";
|
|
44
|
+
const summary = missing.length ? `${found} available · ${missing.length} unavailable` : requested.length ? `${found} policies available` : "no policies discovered";
|
|
45
|
+
const metadata = missing.length ? `Missing: ${missing.join(", ")}` : `${requested.length} requested`;
|
|
46
|
+
if (!expanded) return renderCompactCard({ status, title: "Policy manifest", summary, metadata }, theme);
|
|
47
|
+
return renderExpandedMarkdown({ status, title: "Policy manifest", summary, metadata: [metadata], markdown: resultText(result) }, theme);
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
pi.registerTool({
|
|
52
|
+
name: "policy_load",
|
|
53
|
+
label: "Policy Load",
|
|
54
|
+
description: "Load one or more context policy packs required before sensitive tools such as takomi_subagent.",
|
|
55
|
+
promptSnippet: "Load policy packs required before sensitive tool calls",
|
|
56
|
+
parameters: Type.Object({ policies: Type.Array(Type.String({ description: "Policy pack name to load" })) }),
|
|
57
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
58
|
+
restoreReportFromSession(state, ctx);
|
|
59
|
+
state.report.timestamp = new Date().toISOString();
|
|
60
|
+
state.report.cwd = ctx.cwd;
|
|
61
|
+
state.report.toolCalls.policyLoad += 1;
|
|
62
|
+
const text = renderPolicies(state.policies, state.loadedPolicies, params.policies);
|
|
63
|
+
syncReportLedger(state);
|
|
64
|
+
persistReportSnapshot(pi, state, "policy_load");
|
|
65
|
+
const missing = missingPolicies(state, params.policies);
|
|
66
|
+
return {
|
|
67
|
+
content: [{ type: "text", text }],
|
|
68
|
+
details: {
|
|
69
|
+
requested: params.policies,
|
|
70
|
+
loadedPolicies: [...state.loadedPolicies].sort(),
|
|
71
|
+
loadedCount: params.policies.length - missing.length,
|
|
72
|
+
missing,
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
},
|
|
76
|
+
renderCall(args, theme) {
|
|
77
|
+
return renderToolCall("policy_load", `${args.policies.length} requested`, theme);
|
|
78
|
+
},
|
|
79
|
+
renderResult(result, { expanded }, theme) {
|
|
80
|
+
const details = result.details as { requested?: string[]; loadedCount?: number; missing?: string[] } | undefined;
|
|
81
|
+
const requested = details?.requested ?? [];
|
|
82
|
+
const loadedCount = details?.loadedCount ?? 0;
|
|
83
|
+
const missing = details?.missing ?? [];
|
|
84
|
+
const status = missing.length || requested.length === 0 ? "warning" : "success";
|
|
85
|
+
const summary = requested.length === 0
|
|
86
|
+
? "no policies requested"
|
|
87
|
+
: missing.length ? `${loadedCount} loaded · ${missing.length} unavailable` : `${loadedCount} policies loaded`;
|
|
88
|
+
const metadata = missing.length ? `Missing: ${missing.join(", ")}` : `${requested.length} requested`;
|
|
89
|
+
if (!expanded) return renderCompactCard({ status, title: "Policy load", summary, metadata }, theme);
|
|
90
|
+
return renderExpandedMarkdown({ status, title: "Policy load", summary, metadata: [metadata], markdown: resultText(result) }, theme);
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type SkillCategory = {
|
|
2
|
+
id: string;
|
|
3
|
+
title: string;
|
|
4
|
+
color: string;
|
|
5
|
+
description: string;
|
|
6
|
+
skills: string[];
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const CORE_SKILLS: string[];
|
|
10
|
+
export const SKILL_CATEGORIES: SkillCategory[];
|
|
11
|
+
export function getSkillCategory(skillName: string): string | undefined;
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
export const CORE_SKILLS = [
|
|
2
|
+
'takomi',
|
|
3
|
+
'sync-docs',
|
|
4
|
+
'code-review',
|
|
5
|
+
'security-audit',
|
|
6
|
+
'optimize-agent-context',
|
|
7
|
+
'agent-recovery',
|
|
8
|
+
'avoid-feature-creep',
|
|
9
|
+
'ai-sdk',
|
|
10
|
+
'git-commit-generation',
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
export const SKILL_CATEGORIES = [
|
|
14
|
+
{
|
|
15
|
+
id: 'core',
|
|
16
|
+
title: 'Core / Recommended',
|
|
17
|
+
color: 'cyan',
|
|
18
|
+
description: 'Essential skills for efficient Takomi usage.',
|
|
19
|
+
skills: CORE_SKILLS,
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: 'developer',
|
|
23
|
+
title: 'Developer / Frameworks',
|
|
24
|
+
color: 'blue',
|
|
25
|
+
description: 'Framework, repo, and developer workflow helpers.',
|
|
26
|
+
skills: [
|
|
27
|
+
'ai-sdk',
|
|
28
|
+
'nextjs-standards',
|
|
29
|
+
'context7',
|
|
30
|
+
'monorepo-management',
|
|
31
|
+
'upgrading-expo',
|
|
32
|
+
'github-ops',
|
|
33
|
+
'git-worktree',
|
|
34
|
+
'git-commit-generation',
|
|
35
|
+
'pr-comment-fix',
|
|
36
|
+
'shared-resend-portfolio',
|
|
37
|
+
'jules',
|
|
38
|
+
'gemini',
|
|
39
|
+
'anti-gravity',
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 'security',
|
|
44
|
+
title: 'Security / Review',
|
|
45
|
+
color: 'red',
|
|
46
|
+
description: 'Security, audit, and review workflows.',
|
|
47
|
+
skills: [
|
|
48
|
+
'security-audit',
|
|
49
|
+
'audit-website',
|
|
50
|
+
'code-review',
|
|
51
|
+
'jstar-reviewer',
|
|
52
|
+
'convex-security-audit',
|
|
53
|
+
'convex-security-check',
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: 'convex',
|
|
58
|
+
title: 'Convex',
|
|
59
|
+
color: 'green',
|
|
60
|
+
description: 'Convex framework skills and best practices.',
|
|
61
|
+
skills: [
|
|
62
|
+
'convex',
|
|
63
|
+
'convex-agents',
|
|
64
|
+
'convex-best-practices',
|
|
65
|
+
'convex-component-authoring',
|
|
66
|
+
'convex-cron-jobs',
|
|
67
|
+
'convex-file-storage',
|
|
68
|
+
'convex-functions',
|
|
69
|
+
'convex-http-actions',
|
|
70
|
+
'convex-migrations',
|
|
71
|
+
'convex-realtime',
|
|
72
|
+
'convex-schema-validator',
|
|
73
|
+
'convex-security-audit',
|
|
74
|
+
'convex-security-check',
|
|
75
|
+
],
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: 'frontend',
|
|
79
|
+
title: 'Frontend / UI',
|
|
80
|
+
color: 'magenta',
|
|
81
|
+
description: 'Frontend implementation, UI/UX, components, and testing.',
|
|
82
|
+
skills: [
|
|
83
|
+
'frontend-design',
|
|
84
|
+
'web-design-guidelines',
|
|
85
|
+
'building-native-ui',
|
|
86
|
+
'ui-ux-pro-max',
|
|
87
|
+
'component-analysis',
|
|
88
|
+
'21st-dev-components',
|
|
89
|
+
'stitch',
|
|
90
|
+
'webapp-testing',
|
|
91
|
+
'figma',
|
|
92
|
+
],
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
id: 'docs-office',
|
|
96
|
+
title: 'Docs / Office / Extraction',
|
|
97
|
+
color: 'yellow',
|
|
98
|
+
description: 'Document formats, extraction, and README support.',
|
|
99
|
+
skills: [
|
|
100
|
+
'pdf',
|
|
101
|
+
'docx',
|
|
102
|
+
'pptx',
|
|
103
|
+
'xlsx',
|
|
104
|
+
'high-fidelity-extraction',
|
|
105
|
+
'crafting-effective-readmes',
|
|
106
|
+
'exam-creator-skill',
|
|
107
|
+
],
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
id: 'marketing',
|
|
111
|
+
title: 'Marketing / SEO / Copy',
|
|
112
|
+
color: 'green',
|
|
113
|
+
description: 'Marketing, SEO, naming, pricing, and social strategy.',
|
|
114
|
+
skills: [
|
|
115
|
+
'copywriting',
|
|
116
|
+
'marketing-ideas',
|
|
117
|
+
'pricing-strategy',
|
|
118
|
+
'programmatic-seo',
|
|
119
|
+
'seo-ready',
|
|
120
|
+
'social-content',
|
|
121
|
+
'twitter-automation',
|
|
122
|
+
'google-trends',
|
|
123
|
+
'domain-name-brainstormer',
|
|
124
|
+
'global-brand-namer',
|
|
125
|
+
'youtube-pipeline',
|
|
126
|
+
],
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
id: 'ai-media',
|
|
130
|
+
title: 'AI Media / Content Creation',
|
|
131
|
+
color: 'magenta',
|
|
132
|
+
description: 'Optional AI media/content skills. Not installed by default.',
|
|
133
|
+
skills: [
|
|
134
|
+
'ai-avatar-video',
|
|
135
|
+
'ai-marketing-videos',
|
|
136
|
+
'ai-podcast-creation',
|
|
137
|
+
'ai-product-photography',
|
|
138
|
+
'ai-social-media-content',
|
|
139
|
+
'ai-voice-cloning',
|
|
140
|
+
'takomi-flow',
|
|
141
|
+
],
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
id: 'creative-video',
|
|
145
|
+
title: 'Creative / Video / Art',
|
|
146
|
+
color: 'blue',
|
|
147
|
+
description: 'Creative visuals, video, animation, and art workflows.',
|
|
148
|
+
skills: [
|
|
149
|
+
'algorithmic-art',
|
|
150
|
+
'blender-mcp-scene-director',
|
|
151
|
+
'takomi-flow',
|
|
152
|
+
'remotion',
|
|
153
|
+
'remotion-real-ui-video',
|
|
154
|
+
'youtube-pipeline',
|
|
155
|
+
'ai-avatar-video',
|
|
156
|
+
'ai-marketing-videos',
|
|
157
|
+
'photo-book-builder',
|
|
158
|
+
'hyperframes',
|
|
159
|
+
'general-video',
|
|
160
|
+
'motion-graphics',
|
|
161
|
+
],
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
id: 'hyperframes',
|
|
165
|
+
title: 'HyperFrames',
|
|
166
|
+
color: 'magenta',
|
|
167
|
+
description: 'HTML-native video creation and animation framework.',
|
|
168
|
+
skills: [
|
|
169
|
+
'hyperframes',
|
|
170
|
+
'hyperframes-core',
|
|
171
|
+
'hyperframes-keyframes',
|
|
172
|
+
'hyperframes-animation',
|
|
173
|
+
'hyperframes-creative',
|
|
174
|
+
'hyperframes-media',
|
|
175
|
+
'hyperframes-registry',
|
|
176
|
+
'hyperframes-cli',
|
|
177
|
+
'remotion-to-hyperframes',
|
|
178
|
+
'media-use',
|
|
179
|
+
'embedded-captions',
|
|
180
|
+
'faceless-explainer',
|
|
181
|
+
'general-video',
|
|
182
|
+
'motion-graphics',
|
|
183
|
+
'music-to-video',
|
|
184
|
+
'pr-to-video',
|
|
185
|
+
'product-launch-video',
|
|
186
|
+
'slideshow',
|
|
187
|
+
'talking-head-recut',
|
|
188
|
+
'website-to-video',
|
|
189
|
+
],
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
id: 'skill-building',
|
|
193
|
+
title: 'Skill Building / Prompting / Orchestration',
|
|
194
|
+
color: 'cyan',
|
|
195
|
+
description: 'Skill authoring, prompt engineering, and optional orchestration helpers.',
|
|
196
|
+
skills: [
|
|
197
|
+
'skill-creator',
|
|
198
|
+
'prompt-engineering',
|
|
199
|
+
'subagent-driven-development',
|
|
200
|
+
'spawn-task',
|
|
201
|
+
],
|
|
202
|
+
},
|
|
203
|
+
];
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Resolve a bundled skill to its canonical installer category. Some skills are
|
|
207
|
+
* intentionally listed in multiple browsing categories; the first catalog
|
|
208
|
+
* entry is the stable primary taxonomy used by install manifests and reports.
|
|
209
|
+
*/
|
|
210
|
+
export function getSkillCategory(skillName) {
|
|
211
|
+
return SKILL_CATEGORIES.find((category) => category.skills.includes(skillName))?.id;
|
|
212
|
+
}
|