takomi 2.1.35 → 2.1.37

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 (33) hide show
  1. package/.pi/README.md +6 -3
  2. package/.pi/extensions/oauth-router/commands.ts +36 -35
  3. package/.pi/extensions/oauth-router/index.ts +8 -8
  4. package/.pi/extensions/takomi-context-manager/diagnostics-tools.ts +40 -7
  5. package/.pi/extensions/takomi-context-manager/diagnostics.ts +534 -55
  6. package/.pi/extensions/takomi-context-manager/index.ts +14 -2
  7. package/.pi/extensions/takomi-context-manager/model-policy-gate.ts +16 -4
  8. package/.pi/extensions/takomi-context-manager/policy-tools.ts +7 -0
  9. package/.pi/extensions/takomi-context-manager/prerequisite-gates.ts +2 -0
  10. package/.pi/extensions/takomi-context-manager/session-state.ts +160 -0
  11. package/.pi/extensions/takomi-context-manager/skill-registry.ts +120 -0
  12. package/.pi/extensions/takomi-context-manager/skill-tools.ts +26 -3
  13. package/.pi/extensions/takomi-context-manager/state.ts +11 -1
  14. package/.pi/extensions/takomi-context-manager/types.ts +9 -1
  15. package/.pi/extensions/takomi-runtime/command-text.ts +2 -1
  16. package/.pi/extensions/takomi-runtime/commands.ts +13 -1
  17. package/.pi/extensions/takomi-runtime/index.ts +68 -1
  18. package/.pi/extensions/takomi-runtime/ui.ts +3 -3
  19. package/.pi/extensions/takomi-subagents/index.ts +37 -4
  20. package/.pi/extensions/takomi-subagents/native-render.ts +84 -7
  21. package/.pi/extensions/takomi-subagents/pi-subagents-engine.ts +18 -3
  22. package/.pi/extensions/takomi-subagents/tool-runner.ts +52 -5
  23. package/README.md +14 -7
  24. package/assets/.agent/skills/exam-creator-skill/SKILL.md +182 -0
  25. package/assets/.agent/skills/exam-creator-skill/references/model-routing-policy.md +48 -0
  26. package/assets/.agent/skills/exam-creator-skill/references/workflow-rulebook.md +115 -0
  27. package/package.json +2 -2
  28. package/src/cli.js +134 -13
  29. package/src/doctor.js +7 -1
  30. package/src/harness.js +54 -32
  31. package/src/owned-tree.js +28 -0
  32. package/src/pi-harness.js +149 -24
  33. package/src/store.js +2 -0
@@ -81,9 +81,9 @@ export function renderRuntimeStatus(theme: Theme, state: RuntimeHudState): strin
81
81
  const sourceLabel = source === "manual" ? theme.fg("warning", "manual") : theme.fg("success", source);
82
82
  const role = state.stage && state.role !== state.stage ? theme.fg("dim", state.role) : "";
83
83
  const plan = state.planMode ? theme.fg("warning", "plan") : theme.fg("dim", "direct");
84
- const gate = state.launchMode === "manual" ? theme.fg("warning", "review-gate") : "";
84
+ const gate = state.launchMode === "manual" ? theme.fg("warning", "review-gate") : theme.fg("success", "auto-gate");
85
85
  const subagents = state.subagentsEnabled === false ? theme.fg("error", "subagents:off") : "";
86
- return [theme.fg("accent", "Takomi"), sourceLabel, stageBadge, role, gate || plan, subagents].filter(Boolean).join(" ");
86
+ return [theme.fg("accent", "Takomi"), sourceLabel, stageBadge, role, gate, plan, subagents].filter(Boolean).join(" ");
87
87
  }
88
88
 
89
89
  export function renderRuntimeWidget(theme: Theme, state: RuntimeHudState): string[] {
@@ -95,7 +95,7 @@ export function renderRuntimeWidget(theme: Theme, state: RuntimeHudState): strin
95
95
  sourceLabel,
96
96
  badge(theme, primary, stageTone(state.stage)),
97
97
  state.stage && state.role !== state.stage ? theme.fg("dim", `role:${state.role}`) : "",
98
- state.launchMode === "manual" ? theme.fg("warning", "review-gate") : "",
98
+ state.launchMode === "manual" ? theme.fg("warning", "review-gate") : theme.fg("success", "auto-gate"),
99
99
  state.planMode ? theme.fg("warning", "plan") : theme.fg("dim", "direct"),
100
100
  state.subagentsEnabled === false ? theme.fg("error", "subagents:off") : theme.fg("dim", "subagents:on"),
101
101
  state.workflow ? theme.fg("dim", `wf:${state.workflow}`) : "",
@@ -1,6 +1,7 @@
1
1
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
2
  import { Type } from "typebox";
3
3
  import { renderTakomiSubagentCall, renderTakomiSubagentResult } from "./native-render";
4
+ import { loadPiSubagentsInternals } from "./pi-subagents-internal";
4
5
  import { executeTakomiSubagentTool } from "./tool-runner";
5
6
 
6
7
  const ChecklistItemSchema = Type.Object({
@@ -30,8 +31,24 @@ const TaskSchema = Type.Object({
30
31
  checklist: Type.Optional(Type.Array(Type.Union([Type.String(), ChecklistItemSchema]))),
31
32
  });
32
33
 
34
+ const ContextSchema = Type.Union([
35
+ Type.Literal("fresh"),
36
+ Type.Literal("fork"),
37
+ ]);
38
+
39
+ const ActionSchema = Type.Union([
40
+ Type.Literal("list"),
41
+ Type.Literal("get"),
42
+ Type.Literal("models"),
43
+ Type.Literal("status"),
44
+ Type.Literal("interrupt"),
45
+ Type.Literal("resume"),
46
+ Type.Literal("doctor"),
47
+ ]);
48
+
33
49
  const SubagentParameters = Type.Object({
34
- agent: Type.Optional(Type.String({ description: "Agent name for single execution" })),
50
+ action: Type.Optional(ActionSchema),
51
+ agent: Type.Optional(Type.String({ description: "Agent name for single execution, or target agent for get/models actions" })),
35
52
  task: Type.Optional(Type.String({ description: "Task for single execution" })),
36
53
  workflow: Type.Optional(Type.String({ description: "Workflow or playbook overlay for this task" })),
37
54
  skills: Type.Optional(Type.Array(Type.String(), { description: "Extra skills to apply during the task" })),
@@ -46,6 +63,14 @@ const SubagentParameters = Type.Object({
46
63
  previewOnly: Type.Optional(Type.Boolean({ description: "Return the delegation plan without launching" })),
47
64
  clarify: Type.Optional(Type.Boolean({ description: "Show the native pi-subagents TUI to preview/edit before execution. Especially useful for chains; requires an interactive Pi TUI." })),
48
65
  chain: Type.Optional(Type.Array(TaskSchema, { description: "Sequential chain of subagent tasks" })),
66
+ context: Type.Optional(ContextSchema),
67
+ async: Type.Optional(Type.Boolean({ description: "Run through native pi-subagents background/async mode" })),
68
+ concurrency: Type.Optional(Type.Number({ description: "Maximum concurrency for parallel task groups" })),
69
+ worktree: Type.Optional(Type.Boolean({ description: "Use native pi-subagents worktree isolation where supported" })),
70
+ id: Type.Optional(Type.String({ description: "Native async/control run id for status, interrupt, or resume actions" })),
71
+ message: Type.Optional(Type.String({ description: "Follow-up message for resume actions" })),
72
+ index: Type.Optional(Type.Number({ description: "Child index for resume actions when needed" })),
73
+ chainName: Type.Optional(Type.String({ description: "Saved chain name for get actions" })),
49
74
  agentScope: Type.Optional(Type.Union([Type.Literal("user"), Type.Literal("project"), Type.Literal("both")])),
50
75
  // Project-agent confirmation and hard-stop overrides are enforced server-side;
51
76
  // they are intentionally not model-callable parameters.
@@ -55,11 +80,14 @@ function registerSubagentTool(pi: ExtensionAPI): void {
55
80
  pi.registerTool({
56
81
  name: "takomi_subagent",
57
82
  label: "Takomi",
58
- description: "Run subagents with Pi-style single, parallel, or chain modes plus Takomi lifecycle metadata.",
59
- promptSnippet: "Delegate lifecycle-aware Takomi work to specialist subagents. Use single, tasks, or chain; reuse conversationId for review loops.",
83
+ description: "Run subagents with Pi-style single, parallel, chain, async, fork, and management/status modes plus Takomi lifecycle metadata.",
84
+ promptSnippet: "Delegate lifecycle-aware Takomi work to specialist subagents. Use single, tasks, chain, or action=list/status/doctor; reuse conversationId for review loops.",
60
85
  promptGuidelines: [
61
86
  "Use this tool during orchestration when a specialist should handle a task.",
87
+ "Use action=list to discover agents and action=status/doctor for native pi-subagents diagnostics/control instead of raw subagent.",
62
88
  "Use tasks for independent parallel work and chain for dependent handoffs with {previous}.",
89
+ "Set context=fork only when inherited parent-session history is needed; otherwise prefer fresh or the agent default.",
90
+ "Set async=true for long-running work when the parent can continue safely; keep active worktree writes single-threaded unless worktree isolation is enabled.",
63
91
  "Set clarify=true when the user asks to preview/edit a subagent run in the native Pi TUI before launch.",
64
92
  "Use model, fallbackModels, and thinking only when deliberate; otherwise let the agent/profile defaults apply.",
65
93
  "If review sends work back to the same agent, reuse the same conversationId for continuity.",
@@ -74,6 +102,11 @@ function registerSubagentTool(pi: ExtensionAPI): void {
74
102
  });
75
103
  }
76
104
 
77
- export default function takomiSubagents(pi: ExtensionAPI) {
105
+ export default async function takomiSubagents(pi: ExtensionAPI) {
106
+ // Preload the native pi-subagents renderer before tool registration. Rendering
107
+ // callbacks are synchronous, so if we only lazy-load internals during execution
108
+ // a completed takomi_subagent result can fall back to Takomi's plain text
109
+ // renderer and lose the native compact/expanded details shown by `subagent`.
110
+ await loadPiSubagentsInternals();
78
111
  registerSubagentTool(pi);
79
112
  }
@@ -38,12 +38,16 @@ function resultText(result: ToolResult): string {
38
38
  : JSON.stringify((result as any)?.details ?? {}, null, 2);
39
39
  }
40
40
 
41
- function summarizeCollapsedResult(text: string, status: string, theme: Theme): string {
41
+ function extractPolicyNames(text: string): string[] {
42
42
  const policyMatch = text.match(/Required policies:\n((?:- .+\n?)+)/);
43
- const policies = policyMatch?.[1]
43
+ return policyMatch?.[1]
44
44
  ?.split("\n")
45
45
  .map((line) => line.replace(/^[-\s]+/, "").trim())
46
46
  .filter(Boolean) ?? [];
47
+ }
48
+
49
+ function summarizeCollapsedResult(text: string, status: string, theme: Theme): string {
50
+ const policies = extractPolicyNames(text);
47
51
  const lineCount = text ? text.split(/\r?\n/).length : 0;
48
52
  const label = policies.length > 0
49
53
  ? `policy context loaded: ${policies.join(", ")}`
@@ -53,22 +57,89 @@ function summarizeCollapsedResult(text: string, status: string, theme: Theme): s
53
57
  return `${theme.fg(color, `${icon} takomi_subagent ${status}`)}${theme.fg("dim", ` · ${label} (ctrl+o to expand)`)}`;
54
58
  }
55
59
 
60
+ function isPolicyGateBlock(text: string): boolean {
61
+ return /^Blocked\s+takomi_subagent:\s+required policy context had not been loaded yet\./m.test(text)
62
+ && /\nRequired policies:\n/.test(text)
63
+ && /\nLoaded policy context:\n/.test(text);
64
+ }
65
+
66
+ function renderPolicyGateBlock(text: string, expanded: boolean | undefined, theme: Theme): string {
67
+ const policies = extractPolicyNames(text);
68
+ const lineCount = text ? text.split(/\r?\n/).length : 0;
69
+ const policyLabel = policies.length ? policies.join(", ") : "required policy";
70
+ if (!expanded) {
71
+ return [
72
+ theme.fg("warning", "⚠ takomi_subagent blocked"),
73
+ theme.fg("dim", `Required policy context loaded for this session: ${policyLabel}.`),
74
+ theme.fg("dim", `Retry the original tool call. ${lineCount} policy lines hidden (ctrl+o to expand).`),
75
+ ].join("\n");
76
+ }
77
+ return [
78
+ theme.fg("warning", "⚠ takomi_subagent blocked"),
79
+ theme.fg("dim", "Policy context was loaded and passed back to the model; retry the original call."),
80
+ theme.fg("dim", "Press ctrl+o again to collapse."),
81
+ "",
82
+ text,
83
+ ].join("\n");
84
+ }
85
+
56
86
  function recentOutputLines(value: unknown): string[] {
57
87
  if (Array.isArray(value)) return value.map(String).filter(Boolean).slice(-3);
58
88
  if (typeof value === "string") return value.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).slice(-3);
59
89
  return [];
60
90
  }
61
91
 
92
+ function normalizeLiveStatus(value: string | undefined): string | undefined {
93
+ if (!value) return undefined;
94
+ if (value === "complete") return "completed";
95
+ if (value === "in-progress") return "running";
96
+ if (value === "timed-out") return "failed";
97
+ return value;
98
+ }
99
+
100
+ function rawLiveStatus(row: any): string {
101
+ const explicit = normalizeLiveStatus(
102
+ typeof row?.progress?.status === "string" ? row.progress.status
103
+ : typeof row?.status === "string" ? row.status
104
+ : undefined,
105
+ );
106
+ if (explicit) return explicit;
107
+
108
+ const exitCode = typeof row?.exitCode === "number" ? row.exitCode
109
+ : typeof row?.code === "number" ? row.code
110
+ : undefined;
111
+ if (exitCode === 0) return "completed";
112
+ if (exitCode === undefined || exitCode === null || exitCode === -1) return "running";
113
+ return "failed";
114
+ }
115
+
116
+ function isLiveTerminalStatus(status: string): boolean {
117
+ return ["completed", "failed", "blocked", "detached", "paused"].includes(status);
118
+ }
119
+
120
+ function displayLiveStatus(status: string, allRowsTerminal: boolean): string {
121
+ // Native pi-subagents can emit one last partial update after the child process
122
+ // reaches exitCode 0 but before the takomi_subagent tool result has actually
123
+ // settled. In that window the tool card is still partial/running, so showing a
124
+ // child as "completed" is misleading. Call it finalizing until Pi renders the
125
+ // real final result.
126
+ if (allRowsTerminal && status === "completed") return "finalizing";
127
+ return status;
128
+ }
129
+
62
130
  function livePartialText(result: ToolResult, theme: Theme): string {
63
131
  const details = (result as any)?.details ?? {};
64
132
  const results = Array.isArray(details.results) ? details.results : [];
65
133
  const progress = Array.isArray(details.progress) ? details.progress : [];
134
+ const rows = results.length ? results : progress;
135
+ const rawStatuses = rows.map(rawLiveStatus);
136
+ const allRowsTerminal = rows.length > 0 && rawStatuses.every(isLiveTerminalStatus);
137
+ const titleStatus = allRowsTerminal ? "finalizing" : "running";
66
138
  const lines = [
67
- theme.fg("toolTitle", theme.bold("takomi_subagent running")),
139
+ theme.fg("toolTitle", theme.bold(`takomi_subagent ${titleStatus}`)),
68
140
  theme.fg("dim", "Live detail is bounded while streaming so manual scroll/ctrl+o does not jump on every token."),
69
141
  ];
70
142
 
71
- const rows = results.length ? results : progress;
72
143
  if (!rows.length) {
73
144
  const text = resultText(result).split(/\r?\n/).find((line) => line.trim())?.trim();
74
145
  lines.push(theme.fg("dim", text || "Waiting for subagent progress…"));
@@ -76,7 +147,7 @@ function livePartialText(result: ToolResult, theme: Theme): string {
76
147
  }
77
148
 
78
149
  rows.slice(0, 6).forEach((row: any, index: number) => {
79
- const status = row.status ?? (row.exitCode === 0 ? "completed" : row.exitCode === -1 ? "running" : row.exitCode === undefined ? "running" : "failed");
150
+ const status = displayLiveStatus(rawStatuses[index] ?? rawLiveStatus(row), allRowsTerminal);
80
151
  const agent = row.agent ?? `task ${index + 1}`;
81
152
  const task = String(row.task ?? "").replace(/\s+/g, " ").trim();
82
153
  const currentTool = row.currentTool ?? row.progress?.currentTool;
@@ -93,15 +164,21 @@ function livePartialText(result: ToolResult, theme: Theme): string {
93
164
  }
94
165
 
95
166
  export function renderTakomiSubagentResult(result: ToolResult, options: { expanded?: boolean; isPartial?: boolean }, theme: Theme, context: any): any {
96
- const status = (result as any)?.isError ? "failed" : options.isPartial ? "running" : "completed";
167
+ const status = ((result as any)?.isError || context?.isError) ? "failed" : options.isPartial ? "running" : "completed";
97
168
  const text = resultText(result);
98
169
 
170
+ if (isPolicyGateBlock(text)) {
171
+ return new Text(renderPolicyGateBlock(text, options.expanded, theme), 0, 0);
172
+ }
173
+
174
+ const native = renderNativeSubagentResult(result, options, theme, context);
175
+ if (options.isPartial && native) return native;
176
+
99
177
  if (options.isPartial) {
100
178
  if (options.expanded) return new Text(livePartialText(result, theme), 0, 0);
101
179
  return new Text(summarizeCollapsedResult(text, status, theme), 0, 0);
102
180
  }
103
181
 
104
- const native = renderNativeSubagentResult(result, options, theme, context);
105
182
  if (native) return native;
106
183
 
107
184
  if (!options.expanded) {
@@ -54,7 +54,8 @@ function createState(): SubagentState {
54
54
  };
55
55
  }
56
56
 
57
- function resolveMode(params: TakomiSubagentToolParams): "single" | "parallel" | "chain" | undefined {
57
+ function resolveMode(params: TakomiSubagentToolParams): "single" | "parallel" | "chain" | "action" | undefined {
58
+ if (params.action) return "action";
58
59
  const hasChain = Boolean(params.chain?.length);
59
60
  const hasParallel = Boolean(params.tasks?.length);
60
61
  const hasSingle = Boolean(params.agent && params.task);
@@ -205,13 +206,27 @@ function toSubagentParams(params: TakomiSubagentToolParams, rootCwd: string, dis
205
206
  const base = {
206
207
  agentScope: params.agentScope ?? "both",
207
208
  cwd: rootCwd,
208
- context: "fresh" as const,
209
- async: false,
209
+ ...(params.context ? { context: params.context } : {}),
210
+ ...(params.async !== undefined ? { async: params.async } : {}),
211
+ ...(params.concurrency !== undefined ? { concurrency: params.concurrency } : {}),
212
+ ...(params.worktree !== undefined ? { worktree: params.worktree } : {}),
210
213
  clarify: params.clarify === true,
211
214
  includeProgress: true,
212
215
  sessionDir: stableConversationSessionDir(rootCwd, tasks),
213
216
  };
214
217
 
218
+ if (mode === "action") {
219
+ return {
220
+ ...base,
221
+ action: params.action,
222
+ agent: params.agent,
223
+ chainName: params.chainName,
224
+ id: params.id,
225
+ message: params.message,
226
+ index: params.index,
227
+ };
228
+ }
229
+
215
230
  if (mode === "single") {
216
231
  const task = tasks[0]!;
217
232
  const mapped = mapSingleTask(task, names, rootCwd);
@@ -1,7 +1,7 @@
1
1
  import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
4
- import type { TakomiThinkingLevel } from "../../../src/pi-takomi-core";
4
+ import type { TakomiLaunchMode, TakomiThinkingLevel } from "../../../src/pi-takomi-core";
5
5
  import { loadTakomiProfile } from "../takomi-runtime/profile";
6
6
  import { applyTakomiRoutingDefaults, loadTakomiModelRoutingSnapshot } from "../takomi-runtime/model-routing-defaults";
7
7
  import { resolveAgentName } from "./agent-aliases";
@@ -25,11 +25,20 @@ export type TakomiSubagentToolTask = {
25
25
  };
26
26
 
27
27
  export type TakomiSubagentToolParams = Partial<TakomiSubagentToolTask> & {
28
+ action?: "list" | "get" | "models" | "status" | "interrupt" | "resume" | "doctor";
28
29
  tasks?: TakomiSubagentToolTask[];
29
30
  chain?: TakomiSubagentToolTask[];
30
31
  confirmLaunch?: boolean;
31
32
  previewOnly?: boolean;
32
33
  clarify?: boolean;
34
+ context?: "fresh" | "fork";
35
+ async?: boolean;
36
+ concurrency?: number;
37
+ worktree?: boolean;
38
+ id?: string;
39
+ message?: string;
40
+ index?: number;
41
+ chainName?: string;
33
42
  agentScope?: TakomiAgentScope;
34
43
  };
35
44
 
@@ -192,6 +201,17 @@ function withNativeHardStop(result: any, hardStop: { reason: string; message: st
192
201
  },
193
202
  };
194
203
  }
204
+
205
+ function readRuntimeLaunchMode(ctx: ExtensionContext): TakomiLaunchMode | undefined {
206
+ const entries = ctx.sessionManager.getEntries();
207
+ for (let i = entries.length - 1; i >= 0; i -= 1) {
208
+ const entry = entries[i] as { type?: string; customType?: string; data?: { launchMode?: unknown } };
209
+ if (entry.type !== "custom" || entry.customType !== "takomi-runtime-state") continue;
210
+ if (entry.data?.launchMode === "manual" || entry.data?.launchMode === "auto") return entry.data.launchMode;
211
+ }
212
+ return undefined;
213
+ }
214
+
195
215
  function resolveMode(params: TakomiSubagentToolParams): "single" | "parallel" | "chain" | undefined {
196
216
  const hasChain = Boolean(params.chain?.length);
197
217
  const hasParallel = Boolean(params.tasks?.length);
@@ -234,7 +254,34 @@ export async function executeTakomiSubagentTool(
234
254
  return textResult(message, { results: [], agentScope: params.agentScope ?? "both" }, true);
235
255
  }
236
256
  const profile = await loadTakomiProfile(rootCwd);
257
+ const runtimeLaunchMode = readRuntimeLaunchMode(ctx);
237
258
  const agentScope = params.agentScope ?? "both";
259
+
260
+ if (params.action) {
261
+ try {
262
+ const nativeResult: any = await engine.execute(
263
+ "takomi-tool",
264
+ { ...params, cwd: rootCwd, agentScope },
265
+ signal,
266
+ onUpdate as any,
267
+ ctx,
268
+ );
269
+ return {
270
+ ...nativeResult,
271
+ details: {
272
+ ...(nativeResult?.details ?? {}),
273
+ takomi: {
274
+ action: params.action,
275
+ agentScope,
276
+ },
277
+ },
278
+ };
279
+ } catch (error) {
280
+ const message = error instanceof Error ? error.message : String(error);
281
+ return textResult(message, { results: [], action: params.action, agentScope }, true);
282
+ }
283
+ }
284
+
238
285
  const agents = discoverTakomiAgents(rootCwd, agentScope);
239
286
  const byName = new Map<string, TakomiAgentConfig>(agents.map((agent) => [agent.name, agent]));
240
287
  const mode = resolveMode(params);
@@ -290,7 +337,7 @@ export async function executeTakomiSubagentTool(
290
337
  }
291
338
  const plan = createTakomiDelegationPlan({
292
339
  source: "takomi-tool",
293
- launchMode: profile.launchMode ?? "auto",
340
+ launchMode: runtimeLaunchMode ?? profile.launchMode ?? "auto",
294
341
  profile,
295
342
  tasks: tasks.map((task, index) => ({
296
343
  id: task.conversationId ?? `direct-${index + 1}`,
@@ -316,10 +363,10 @@ export async function executeTakomiSubagentTool(
316
363
  }
317
364
  try {
318
365
  const nativeParams: TakomiSubagentToolParams = mode === "single"
319
- ? { ...params, ...tasks[0]!, agentScope }
366
+ ? { ...params, ...tasks[0]!, cwd: rootCwd, agentScope }
320
367
  : mode === "parallel"
321
- ? { ...params, tasks, agentScope }
322
- : { ...params, chain: tasks, agentScope };
368
+ ? { ...params, cwd: rootCwd, tasks, agentScope }
369
+ : { ...params, cwd: rootCwd, chain: tasks, agentScope };
323
370
 
324
371
  const nativeResult: any = await engine.execute(
325
372
  "takomi-tool",
package/README.md CHANGED
@@ -50,6 +50,8 @@ Legacy commands like `takomi install pi`, `takomi sync pi`, `takomi upgrade`, an
50
50
 
51
51
  During `takomi setup pi` or `takomi setup pi-features`, Takomi offers optional Pi feature packs with recommended/manual/select-all/skip choices. Current defaults install **Takomi Interview** (`npm:@juicesharp/rpiv-ask-user-question`) so models can ask structured clarification questions. **Takomi Todo** (`npm:@juicesharp/rpiv-todo`), **Takomi Browser QA** (`npm:pi-chrome`), and **Takomi Doc Preview** (`npm:pi-markdown-preview`) remain opt-in. `takomi refresh` runs Pi's package updater so installed optional, custom, old, and new Pi packages are reconciled together.
52
52
 
53
+ Takomi keeps `pi-subagents` installed as an internal runtime module, but setup/refresh now detects legacy raw Pi subagent activation (`npm:pi-subagents` in Pi settings or `~/.pi/agent/extensions/subagent`) and offers to disable it so models see `takomi_subagent` instead of two competing subagent tools.
54
+
53
55
  ### Context Manager
54
56
 
55
57
  Takomi now ships a Pi-native `takomi-context-manager` extension. It reduces prompt bloat with progressive context loading:
@@ -60,10 +62,11 @@ Takomi now ships a Pi-native `takomi-context-manager` extension. It reduces prom
60
62
  - `takomi_subagent` is guarded by routing-policy context and can recover from wrong-provider model choices
61
63
  - `/context-report` shows prompt compaction, loaded skills/policies, blocked actions, model-routing corrections, and duplicate extension diagnostics
62
64
 
65
+ `context_report` is context-manager-specific rather than a clone of Pi's Alt-C window. It restores its own hidden snapshots and Pi tool-result history after `/reload`/restart, and labels any remaining gaps instead of reporting fresh in-memory zeros as session truth. It supports `mode: "summary" | "verbose" | "problems"`; `verbose: true` remains a compatibility alias for `mode: "verbose"`. The `/context-report` slash command supports argument completions for `summary`, `verbose`, and `problems`.
63
66
 
64
67
  ### Option A: Global Install (Best for Multi-IDE Users) ⭐
65
68
 
66
- Install once. Use everywhere. Your skills follow you across every AI harness(Antigravity, KiloCode, Windsurf, Cursor, Gemini CLI):
69
+ Install once. Use everywhere. Your skills follow you across detected AI harnesses (Antigravity, Claude Code, Codex, Cursor, Kilo Code, Pi/shared Agent Skills, Windsurf):
67
70
 
68
71
  ```bash
69
72
  # Using pnpm
@@ -184,17 +187,21 @@ npx -y skills add https://github.com/JStaRFilms/VibeCode-Protocol-Suite --skill
184
187
 
185
188
  **One install. Every IDE. Zero friction.**
186
189
 
187
- Takomi v2.0 introduces the **Global Skills Router** — install skills once, and they appear in every AI harness you use. No symlinks. No admin privileges. Works on Mac & Windows.
190
+ Takomi v2.0 introduces the **Global Skills Router** — install skills once into `~/.takomi/`, and Takomi syncs them into each harness' current global skills directory. You can choose symlink/junction mode for one canonical copy, auto fallback, or plain copy mode. Works on Mac & Windows.
191
+
192
+ Note: `SKILL.md` is portable, but `~/.agents/skills` is **not** a universal global skills directory. Pi does load `~/.agents/skills`, and Takomi keeps that as the Pi/shared Agent Skills target. Other harnesses use their own global paths. See `docs/skills-harness-targets.md` for the current checked path table.
188
193
 
189
194
  ### Supported Harnesses
190
195
 
191
196
  | Harness | Global Skills Path | Global Workflows Path |
192
197
  |---|---|---|
193
- | **Antigravity** | `~/.gemini/antigravity/skills/` | `~/.gemini/antigravity/global_workflows/` |
194
- | **KiloCode** | `~/.kilocode/skills/` | `~/.kilocode/workflows/` |
198
+ | **Antigravity** | `~/.gemini/config/skills/` | `~/.gemini/config/global_workflows/` |
199
+ | **Claude Code** | `~/.claude/skills/` | _(skills only)_ |
200
+ | **Codex** | `~/.codex/skills/` | _(skills only)_ |
201
+ | **Cursor** | `~/.cursor/skills/` | _(skills only)_ |
202
+ | **Kilo Code** | `~/.kilocode/skills/` | `~/.kilocode/workflows/` |
203
+ | **Pi / shared Agent Skills** | `~/.agents/skills/` | _(skills only)_ |
195
204
  | **Windsurf** | `~/.codeium/windsurf/skills/` | `~/.codeium/windsurf/global_workflows/` |
196
- | **Global agents-compatible CLIs** _(e.g., Codex, Gemini CLI)_ | `~/.agents/skills/` | _(skills only)_ |
197
- | **Cursor** | `~/.cursor/skills/` | _(uses rules)_ |
198
205
 
199
206
  ### CLI Commands
200
207
 
@@ -505,7 +512,7 @@ Externally sourced skills and optional Pi packages retain credit to their upstre
505
512
  - **Context7**: From [upstash/context7](https://github.com/upstash/context7) — fresh library documentation fetcher.
506
513
  - **Audit Website**: From [squirrelscan/skills](https://github.com/squirrelscan/skills) — professional website auditor.
507
514
  - **Convex Skills**: From [waynesutton/convexskills](https://github.com/waynesutton/convexskills) — complete Convex development suite including **Functions**, **Schema Validation**, **Realtime**, **Agents**, **File Storage**, **Migrations**, **HTTP Actions**, **Cron Jobs**, **Component Authoring**, **Best Practices**, **Security Audit**, **Security Check**, **Avoid Feature Creep**, and **Optimize Agent Context**.
508
- - **Gemini CLI**: Custom VibeCode skill for large-context processing with Gemini 3 Pro.
515
+ - **Anti-Gravity**: Custom skill for running Google's current Anti-Gravity CLI workflow for large-context review and analysis.
509
516
  - **Google Stitch Skills**: From [google-labs-code/stitch-skills](https://github.com/google-labs-code/stitch-skills) — Design-to-code suite including **design-md**, **enhance-prompt**, **stitch-loop**, **react-components**, and **shadcn-ui**.
510
517
  - **Jules**: From [sanjay3290/ai-skills](https://github.com/sanjay3290/ai-skills) — delegate coding tasks to Google Jules AI agent.
511
518
  - **Subagent Execution**: Built on **[`pi-subagents`](https://github.com/nicobailon/pi-subagents)** by **Nico Bailon** — providing the underlying Pi extension for delegated subagent runs (result rendering, live progress, single/parallel/chain execution, session/artifact handling, and related subagent tooling), upon which Takomi adds its own lifecycle orchestration, model-routing policy, and workflow metadata.
@@ -0,0 +1,182 @@
1
+ ---
2
+ name: exam-creator-skill
3
+ description: Creates and revises school exam papers using Johno's preferred workflow. Use for JSS/Primary exam generation, remixing questions from lesson notes or chat transcripts, building question papers plus marking schemes, creating assets folders for diagrams, applying the 40-mark house style, producing compact DOCX output, and running draft/marking/verification passes with room for user feedback and revisions.
4
+ ---
5
+
6
+ # Exam Creator Skill
7
+
8
+ Use this skill when building or revising school exam papers, especially for OBHIS / Olive Blessed Crest Academy.
9
+
10
+ See the detailed house rules in [references/workflow-rulebook.md](references/workflow-rulebook.md).
11
+ See the model routing policy in [references/model-routing-policy.md](references/model-routing-policy.md).
12
+
13
+ ## Core Principle
14
+
15
+ Follow the user's house style by default, but always leave room for correction. If the user gives a new rule, prefer the new rule and update the working assumptions before generating more papers.
16
+
17
+ Terminology rule: when the user says **Gemini**, treat that as **Gemini via the agy CLI** unless the user explicitly says otherwise.
18
+
19
+ ## Default Workflow
20
+
21
+ 1. **Identify the subject, class, term, and destination folder.**
22
+ 2. **Collect the authority material**:
23
+ - lesson notes
24
+ - ChatGPT transcripts
25
+ - sample exam layout if provided
26
+ - photographed handwritten pages, when provided
27
+ 3. **For photographed handwritten pages, extract with multimodal vision first**:
28
+ - use a multimodal model/subagent to read the image directly
29
+ - batch images in small ordered groups, usually 3-5 images per extraction task
30
+ - explicitly forbid OCR/Tesseract/Python preprocessing on the first pass unless the model is not multimodal
31
+ - use OCR only as fallback when direct model vision is unavailable or fails on a specific page
32
+ - preserve image filename/page references for every uncertain item
33
+ 4. **Confirm the paper pattern before drafting** if any of these are unclear:
34
+ - subject family (Mathematics/English vs other subjects)
35
+ - mark split
36
+ - question counts
37
+ - whether theory is answer-all or answer-any
38
+ 5. **Create or reuse clean folders**:
39
+ - `Questions/`
40
+ - `Marking Scheme/` only when requested
41
+ - `Questions/assets/` for all generated SVG/PNG/image diagrams
42
+ 6. **Draft the paper first.**
43
+ 7. **Create the marking scheme second only when requested.**
44
+ 8. **Run an independent verification pass third.**
45
+ 9. **Apply user feedback and rerender** instead of defending the first draft.
46
+
47
+ ## Preferred Multi-Agent Pattern
48
+
49
+ Default model split:
50
+ - orchestrator: parent assistant
51
+ - drafting: Gemini via agy CLI by default, escalate to GPT 5.4 High when the paper is tricky
52
+ - marking: Gemini via agy CLI
53
+ - verification: GPT
54
+
55
+ When subagents are available, use this split:
56
+
57
+ - **Drafting agent**: creates the paper and diagram assets.
58
+ - **Marking agent**: creates the marking scheme and validates every objective answer.
59
+ - **Review agent**: independently rechecks answers, numbering, marks, wording, and diagram correctness.
60
+
61
+ If subagents are not available, still preserve the same three-pass logic manually.
62
+
63
+ ## House Rules To Hardcode
64
+
65
+ ### 1) Marks and paper shape
66
+
67
+ #### Mathematics and English
68
+ Use this as the default unless the user overrides it:
69
+
70
+ - total exam score: **40 marks**
71
+ - **50 objective questions**
72
+ - each objective = **1/2 mark**
73
+ - objective total = **25 marks**
74
+ - **5 theory questions**
75
+ - student answers **any 3**
76
+ - theory total = **15 marks**
77
+ - no separate Section B short-answer block unless the user explicitly asks for one
78
+
79
+ #### Other subjects
80
+ Default assumption:
81
+
82
+ - still over **40 marks** total
83
+ - can include open-ended plus theory sections
84
+ - if the exact split is not stated, ask before finalizing
85
+
86
+ Never silently reuse the old 100-mark structure unless the user explicitly asks for it.
87
+
88
+ ### 2) Layout rules
89
+
90
+ For the final paper DOCX, prefer:
91
+
92
+ - **Times New Roman**
93
+ - use one font size consistently within a paper; **start with 14 pt** and only reduce it if layout pressure makes it necessary
94
+ - narrow margins
95
+ - every exam header should include, in this order where possible: school name, academic session, term/exam title, pupil name/date line, examination number/time line, class, arm, subject, and instructions
96
+ - header must be **1-column**
97
+ - body should be **1-column by default**; use 2-column or 3-column only when it genuinely improves fit for dense objective papers
98
+ - do not apply a multi-column body just because the skill mentions compact layout; use judgment per subject
99
+ - bold black headings instead of oversized decorative headings
100
+ - objective options must start on the **same line as the question** whenever possible; keep them inline with about 3 spaces between choices, not vertically stacked unless unavoidable
101
+ - if the paper looks like it will run to **3 pages**, try a **2-column or 3-column body layout** before reducing the font size; use judgment based on content density
102
+ - preserve the school name and student-detail heading fields from the source, or leave clear blanks/placeholders if they are not provided
103
+ - if the source uses an arm/stream/class line, keep it but do not hardcode labels like `Arm:` unless the source explicitly requires them
104
+ - when using separate header/body sections in DOCX, make the body section **continuous** so it does not jump to a new page
105
+ - avoid hard page breaks between the heading block and the paper body unless the user explicitly asks for a new page
106
+ - remove explicit `Total Marks: ...` header line unless requested
107
+ - target about **2 pages** per paper where practical
108
+
109
+ ### 3) Diagram and image rules
110
+
111
+ - Keep all generated figures inside `Questions/assets/`.
112
+ - Choose the diagram method by complexity:
113
+ - simple geometric/line diagrams, tables, clocks, arrows, and labels: SVG first; generate PNG copies if DOCX rendering needs them
114
+ - pictorial objects for young pupils, e.g. mango, pot, bucket, moon, table, cup, house, animals, people, classroom objects: prefer Anti-Gravity image generation or another image-generation path, then place the generated PNG/JPG in `Questions/assets/`
115
+ - hybrid diagrams: combine clean SVG labels/structure with generated image assets when useful
116
+ - When using Anti-Gravity for diagrams, explicitly tell it whether to generate images, use SVG, or use a hybrid method, and require it to report which method was used.
117
+ - Never include the teacher's written answer labels in student-facing diagrams. If the source labels a keyboard as "Keyboard" or groups division answers for marking guidance, remove that answer/label from the final exam.
118
+ - Keep diagrams separated when the source has separate question items or separate objects that must be arranged on the paper. Do not combine unrelated drawings into one asset just for convenience; combine only when the source/question explicitly presents them as one group.
119
+ - Preserve the source question order for diagram-heavy nursery/primary papers by rebuilding from the original images, not only from extraction summaries.
120
+ - If an item asks pupils to colour an object or flag, do not colour it in the exam; provide an outline only unless the instruction asks for a sample.
121
+ - Keep angle labels and vertex labels clearly visible for geometry.
122
+ - Avoid placing numbers directly on triangle edges.
123
+ - If labels are cramped, move them outward and add white-backed label boxes if needed.
124
+
125
+ ### 4) Feedback loop
126
+
127
+ After each significant draft:
128
+
129
+ - expect user corrections
130
+ - revise quickly
131
+ - update the source files and rerender the DOCX
132
+ - if the DOCX is locked/open, save a `v2` file rather than failing the task
133
+
134
+ ## Output Expectations
135
+
136
+ For each subject batch, try to maintain:
137
+
138
+ - editable paper source, usually `questions.md`
139
+ - concise support/source metadata if useful
140
+ - final DOCX for the paper
141
+ - marking scheme source
142
+ - final DOCX for the marking scheme when requested
143
+ - objective-validation notes
144
+ - verification report
145
+ - all diagrams in `Questions/assets/`
146
+
147
+ ## Authoring Guidance
148
+
149
+ - Keep questions standard and school-appropriate.
150
+ - Remix questions reasonably from the provided curriculum material.
151
+ - Stay within the authority source unless the user allows expansion.
152
+ - Make objective questions unambiguous.
153
+ - Do not leak answers from teacher notes, source annotations, labels, grouped examples, or marking cues into the student-facing paper.
154
+ - For handwritten sources, try hard to infer unclear words from context, subject vocabulary, options, and neighboring pages before marking `[unclear]`.
155
+ - If uncertainty remains, create a user-review list with image filename/page number, question number, cropped/quoted context if possible, and the competing interpretations. Ask the user instead of leaving unexplained uncertainty in the final paper.
156
+ - Ensure the marking scheme matches final numbering exactly when a marking scheme is requested.
157
+ - For diagrams, refer to them consistently as Diagram A, B, C, etc.
158
+
159
+ ## Required Clarifications
160
+
161
+ Ask the user before proceeding when any of the following is missing:
162
+
163
+ - subject
164
+ - class level
165
+ - authority source
166
+ - sample layout path if layout matching matters
167
+ - exact marks pattern for non-Math/non-English subjects
168
+ - desired final format if not obvious
169
+ - final interpretation of handwritten items that remain ambiguous after a multimodal-first extraction pass and contextual inference
170
+
171
+ ## Safe Revision Rules
172
+
173
+ When updating an already-generated paper:
174
+
175
+ 1. revise the editable source first
176
+ 2. revise the marking scheme next if numbering/marks changed
177
+ 3. rerun validation/verification if answers, wording, or diagrams changed
178
+ 4. rerender DOCX last
179
+
180
+ ## Reference
181
+
182
+ Load and follow [references/workflow-rulebook.md](references/workflow-rulebook.md) when you need the exact house style details.