takomi 2.1.2 → 2.1.4

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 (52) 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 +511 -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 +90 -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 +306 -215
  35. package/.pi/extensions/takomi-subagents/index.ts +76 -75
  36. package/.pi/extensions/takomi-subagents/live-updates.ts +136 -83
  37. package/.pi/extensions/takomi-subagents/native-render.ts +5 -142
  38. package/.pi/extensions/takomi-subagents/pi-subagents-engine.ts +228 -0
  39. package/.pi/extensions/takomi-subagents/tool-runner.ts +209 -209
  40. package/.pi/themes/takomi-noir.json +81 -81
  41. package/package.json +59 -59
  42. package/src/cli.js +14 -0
  43. package/src/doctor.js +87 -84
  44. package/src/pi-harness.js +355 -351
  45. package/src/pi-installer.js +193 -171
  46. package/src/pi-takomi-core/index.ts +4 -4
  47. package/src/pi-takomi-core/orchestration.ts +402 -402
  48. package/src/pi-takomi-core/routing.ts +93 -93
  49. package/src/pi-takomi-core/types.ts +173 -173
  50. package/src/pi-takomi-core/workflows.ts +299 -299
  51. package/src/skills-installer.js +101 -101
  52. package/src/update-check.js +140 -0
@@ -1,282 +1,282 @@
1
- /**
2
- * Takomi Context Panel - right-side overlay showing session context.
3
- *
4
- * Tracks file edits, tool usage, and Takomi runtime metadata.
5
- * Toggled with Alt+C or /takomi-context.
6
- */
7
-
8
- import type { ExtensionAPI, ExtensionContext, Theme } from "@mariozechner/pi-coding-agent";
9
- import { ellipsizeMiddle, formatDuration, truncateToWidth } from "./shared";
10
-
11
- interface Component {
12
- render(width: number): string[];
13
- handleInput?(data: string): void;
14
- invalidate(): void;
15
- }
16
-
17
- interface OverlayHandle {
18
- hide(): void;
19
- setHidden(hidden: boolean): void;
20
- isHidden(): boolean;
21
- }
22
-
23
- export type FileEdit = {
24
- path: string;
25
- action: "M" | "+" | "R";
26
- timestamp: number;
27
- };
28
-
29
- export type ToolUseCount = {
30
- [tool: string]: number;
31
- };
32
-
33
- export type ContextRuntimeState = {
34
- role?: string;
35
- stage?: string;
36
- workflow?: string;
37
- activeSessionId?: string;
38
- autoOrch?: boolean;
39
- launchMode?: string;
40
- planMode?: boolean;
41
- activeSubagent?: string;
42
- };
43
-
44
- export type ContextPanelState = {
45
- fileEdits: FileEdit[];
46
- toolUses: ToolUseCount;
47
- sessionStart: number;
48
- lastToolAt: number;
49
- runtime: ContextRuntimeState;
50
- };
51
-
52
- function createEmptyState(): ContextPanelState {
53
- return {
54
- fileEdits: [],
55
- toolUses: {},
56
- sessionStart: Date.now(),
57
- lastToolAt: 0,
58
- runtime: {},
59
- };
60
- }
61
-
62
- class ContextPanelComponent implements Component {
63
- constructor(
64
- private readonly theme: Theme,
65
- private readonly getState: () => ContextPanelState,
66
- private readonly getCtx: () => ExtensionContext | undefined,
67
- ) {}
68
-
69
- invalidate(): void {}
70
-
71
- render(width: number): string[] {
72
- const theme = this.theme;
73
- const state = this.getState();
74
- const ctx = this.getCtx();
75
- const panelWidth = Math.min(36, Math.max(24, width));
76
- const innerWidth = panelWidth - 4;
77
- const pad = " ";
78
- const lines: string[] = [];
79
- const hBar = theme.fg("dim", "─".repeat(panelWidth));
80
-
81
- lines.push(hBar);
82
- lines.push(`${pad}${theme.fg("accent", "◎ Context")}`);
83
- lines.push("");
84
-
85
- if (ctx) {
86
- const elapsed = formatDuration(Date.now() - state.sessionStart);
87
- const runtimeLabel = [
88
- state.runtime.role ?? "agent",
89
- state.runtime.stage ?? "-",
90
- ].join(" · ");
91
-
92
- lines.push(`${pad}${theme.fg("muted", runtimeLabel)}`);
93
- lines.push(`${pad}${theme.fg("dim", "Session:")} ${theme.fg("muted", elapsed)}`);
94
-
95
- if (state.runtime.workflow) {
96
- lines.push(`${pad}${theme.fg("dim", "Flow:")} ${theme.fg("muted", truncateToWidth(state.runtime.workflow, innerWidth - 10))}`);
97
- }
98
- if (state.runtime.activeSessionId) {
99
- lines.push(`${pad}${theme.fg("dim", "ID:")} ${theme.fg("muted", ellipsizeMiddle(state.runtime.activeSessionId, innerWidth - 10))}`);
100
- }
101
-
102
- const contextUsage = ctx.getContextUsage();
103
- if (contextUsage && contextUsage.percent !== null) {
104
- const pct = Math.round(contextUsage.percent);
105
- const tokStr = contextUsage.tokens !== null
106
- ? `${Math.round(contextUsage.tokens / 1000)}k/${Math.round(contextUsage.contextWindow / 1000)}k`
107
- : "?";
108
- const ctxTone = pct > 80 ? "error" : pct > 60 ? "warning" : "muted";
109
- lines.push(`${pad}${theme.fg("dim", "Context:")} ${theme.fg(ctxTone as never, `${pct}%`)} ${theme.fg("dim", `(${tokStr})`)}`);
110
- }
111
-
112
- if (ctx.model) {
113
- lines.push(`${pad}${theme.fg("dim", "Model:")} ${theme.fg("muted", ellipsizeMiddle(ctx.model.id, innerWidth - 10))}`);
114
- }
115
- if (state.runtime.activeSubagent) {
116
- lines.push(`${pad}${theme.fg("dim", "Agent:")} ${theme.fg("muted", truncateToWidth(state.runtime.activeSubagent, innerWidth - 10))}`);
117
- }
118
-
119
- const modeFlags = [
120
- state.runtime.launchMode ?? (state.runtime.autoOrch ? "auto" : "manual"),
121
- state.runtime.planMode ? "plan" : "direct",
122
- ].join(" | ");
123
- lines.push(`${pad}${theme.fg("dim", "Mode:")} ${theme.fg("muted", modeFlags)}`);
124
- lines.push("");
125
- }
126
-
127
- lines.push(`${pad}${theme.fg("accent", "-- Files Modified --")}`);
128
- if (state.fileEdits.length === 0) {
129
- lines.push(`${pad}${theme.fg("dim", " (none yet)")}`);
130
- } else {
131
- const seen = new Map<string, FileEdit>();
132
- for (const edit of state.fileEdits) seen.set(edit.path, edit);
133
- const deduped = [...seen.values()].slice(-12);
134
-
135
- for (const edit of deduped) {
136
- const icon = edit.action === "+" ? theme.fg("success", "+") : edit.action === "R" ? theme.fg("muted", "R") : theme.fg("warning", "M");
137
- const displayPath = ellipsizeMiddle(edit.path.replace(/\\/g, "/"), innerWidth - 4);
138
- lines.push(`${pad} ${icon} ${truncateToWidth(displayPath, innerWidth - 4)}`);
139
- }
140
- if (seen.size > 12) {
141
- lines.push(`${pad} ${theme.fg("dim", `... +${seen.size - 12} more`)}`);
142
- }
143
- }
144
- lines.push("");
145
-
146
- lines.push(`${pad}${theme.fg("accent", "-- Tool Activity --")}`);
147
- const toolEntries = Object.entries(state.toolUses).sort((a, b) => b[1] - a[1]);
148
- if (toolEntries.length === 0) {
149
- lines.push(`${pad}${theme.fg("dim", " (no tools used)")}`);
150
- } else {
151
- for (const [tool, count] of toolEntries.slice(0, 8)) {
152
- lines.push(`${pad} ${theme.fg("success", "✓")} ${tool} ${theme.fg("dim", `(${count})`)}`);
153
- }
154
- }
155
- lines.push("");
156
-
157
- lines.push(`${pad}${theme.fg("dim", "(Alt+C to close)")}`);
158
- lines.push(hBar);
159
- return lines;
160
- }
161
- }
162
-
163
- export class TakomiContextPanel {
164
- private state = createEmptyState();
165
- private visible = false;
166
- private overlayHandle?: OverlayHandle;
167
- private requestRender?: () => void;
168
- private lastCtx?: ExtensionContext;
169
-
170
- getState(): ContextPanelState {
171
- return this.state;
172
- }
173
-
174
- isVisible(): boolean {
175
- return this.visible;
176
- }
177
-
178
- setRuntimeState(runtime: ContextRuntimeState): void {
179
- this.state.runtime = { ...runtime };
180
- this.requestRender?.();
181
- }
182
-
183
- trackFileEdit(filePath: string, action: "M" | "+" | "R"): void {
184
- this.state.fileEdits.push({ path: filePath, action, timestamp: Date.now() });
185
- this.state.lastToolAt = Date.now();
186
- this.requestRender?.();
187
- }
188
-
189
- trackToolUse(toolName: string): void {
190
- this.state.toolUses[toolName] = (this.state.toolUses[toolName] ?? 0) + 1;
191
- this.state.lastToolAt = Date.now();
192
- this.requestRender?.();
193
- }
194
-
195
- resetSession(): void {
196
- this.state = createEmptyState();
197
- this.requestRender?.();
198
- }
199
-
200
- refresh(): void {
201
- this.requestRender?.();
202
- }
203
-
204
- toggle(ctx: ExtensionContext): void {
205
- this.lastCtx = ctx;
206
- if (this.visible) this.hide();
207
- else this.show(ctx);
208
- }
209
-
210
- show(ctx: ExtensionContext): void {
211
- this.lastCtx = ctx;
212
- if (!ctx.hasUI) return;
213
- if (this.visible) {
214
- this.requestRender?.();
215
- return;
216
- }
217
- this.visible = true;
218
-
219
- void ctx.ui.custom<void>(
220
- (tui, theme, _keybindings, _done) => {
221
- this.requestRender = () => tui.requestRender();
222
- return new ContextPanelComponent(theme, () => this.state, () => this.lastCtx);
223
- },
224
- {
225
- overlay: true,
226
- overlayOptions: {
227
- width: 36,
228
- maxHeight: "70%",
229
- anchor: "right-center",
230
- margin: { right: 1, top: 2, bottom: 3 },
231
- nonCapturing: true,
232
- visible: (termWidth) => termWidth >= 100,
233
- },
234
- onHandle: (handle) => {
235
- this.overlayHandle = handle;
236
- },
237
- },
238
- ).then(() => {
239
- this.visible = false;
240
- this.overlayHandle = undefined;
241
- this.requestRender = undefined;
242
- });
243
- }
244
-
245
- hide(): void {
246
- this.visible = false;
247
- this.overlayHandle?.hide();
248
- this.overlayHandle = undefined;
249
- this.requestRender = undefined;
250
- }
251
- }
252
-
253
- export function wireContextPanel(pi: ExtensionAPI, panel: TakomiContextPanel): void {
254
- pi.on("tool_result", (event) => {
255
- const toolName = event.toolName;
256
- panel.trackToolUse(toolName);
257
-
258
- if (toolName === "edit" || toolName === "write" || toolName === "read") {
259
- const input = event.input as { file_path?: string; filePath?: string };
260
- const filePath = input.file_path ?? input.filePath;
261
- if (filePath) {
262
- const action = toolName === "write" ? "+" : toolName === "read" ? "R" : "M";
263
- panel.trackFileEdit(filePath, action);
264
- }
265
- }
266
- });
267
-
268
- pi.on("turn_end", () => {
269
- panel.refresh();
270
- });
271
-
272
- pi.on("session_start", () => {
273
- panel.resetSession();
274
- });
275
-
276
- pi.registerShortcut("alt+c", {
277
- description: "Toggle Takomi context panel",
278
- handler: async (ctx) => {
279
- panel.toggle(ctx);
280
- },
281
- });
282
- }
1
+ /**
2
+ * Takomi Context Panel - right-side overlay showing session context.
3
+ *
4
+ * Tracks file edits, tool usage, and Takomi runtime metadata.
5
+ * Toggled with Alt+C or /takomi-context.
6
+ */
7
+
8
+ import type { ExtensionAPI, ExtensionContext, Theme } from "@mariozechner/pi-coding-agent";
9
+ import { ellipsizeMiddle, formatDuration, truncateToWidth } from "./shared";
10
+
11
+ interface Component {
12
+ render(width: number): string[];
13
+ handleInput?(data: string): void;
14
+ invalidate(): void;
15
+ }
16
+
17
+ interface OverlayHandle {
18
+ hide(): void;
19
+ setHidden(hidden: boolean): void;
20
+ isHidden(): boolean;
21
+ }
22
+
23
+ export type FileEdit = {
24
+ path: string;
25
+ action: "M" | "+" | "R";
26
+ timestamp: number;
27
+ };
28
+
29
+ export type ToolUseCount = {
30
+ [tool: string]: number;
31
+ };
32
+
33
+ export type ContextRuntimeState = {
34
+ role?: string;
35
+ stage?: string;
36
+ workflow?: string;
37
+ activeSessionId?: string;
38
+ autoOrch?: boolean;
39
+ launchMode?: string;
40
+ planMode?: boolean;
41
+ activeSubagent?: string;
42
+ };
43
+
44
+ export type ContextPanelState = {
45
+ fileEdits: FileEdit[];
46
+ toolUses: ToolUseCount;
47
+ sessionStart: number;
48
+ lastToolAt: number;
49
+ runtime: ContextRuntimeState;
50
+ };
51
+
52
+ function createEmptyState(): ContextPanelState {
53
+ return {
54
+ fileEdits: [],
55
+ toolUses: {},
56
+ sessionStart: Date.now(),
57
+ lastToolAt: 0,
58
+ runtime: {},
59
+ };
60
+ }
61
+
62
+ class ContextPanelComponent implements Component {
63
+ constructor(
64
+ private readonly theme: Theme,
65
+ private readonly getState: () => ContextPanelState,
66
+ private readonly getCtx: () => ExtensionContext | undefined,
67
+ ) {}
68
+
69
+ invalidate(): void {}
70
+
71
+ render(width: number): string[] {
72
+ const theme = this.theme;
73
+ const state = this.getState();
74
+ const ctx = this.getCtx();
75
+ const panelWidth = Math.min(36, Math.max(24, width));
76
+ const innerWidth = panelWidth - 4;
77
+ const pad = " ";
78
+ const lines: string[] = [];
79
+ const hBar = theme.fg("dim", "─".repeat(panelWidth));
80
+
81
+ lines.push(hBar);
82
+ lines.push(`${pad}${theme.fg("accent", "◎ Context")}`);
83
+ lines.push("");
84
+
85
+ if (ctx) {
86
+ const elapsed = formatDuration(Date.now() - state.sessionStart);
87
+ const runtimeLabel = [
88
+ state.runtime.role ?? "agent",
89
+ state.runtime.stage ?? "-",
90
+ ].join(" · ");
91
+
92
+ lines.push(`${pad}${theme.fg("muted", runtimeLabel)}`);
93
+ lines.push(`${pad}${theme.fg("dim", "Session:")} ${theme.fg("muted", elapsed)}`);
94
+
95
+ if (state.runtime.workflow) {
96
+ lines.push(`${pad}${theme.fg("dim", "Flow:")} ${theme.fg("muted", truncateToWidth(state.runtime.workflow, innerWidth - 10))}`);
97
+ }
98
+ if (state.runtime.activeSessionId) {
99
+ lines.push(`${pad}${theme.fg("dim", "ID:")} ${theme.fg("muted", ellipsizeMiddle(state.runtime.activeSessionId, innerWidth - 10))}`);
100
+ }
101
+
102
+ const contextUsage = ctx.getContextUsage();
103
+ if (contextUsage && contextUsage.percent !== null) {
104
+ const pct = Math.round(contextUsage.percent);
105
+ const tokStr = contextUsage.tokens !== null
106
+ ? `${Math.round(contextUsage.tokens / 1000)}k/${Math.round(contextUsage.contextWindow / 1000)}k`
107
+ : "?";
108
+ const ctxTone = pct > 80 ? "error" : pct > 60 ? "warning" : "muted";
109
+ lines.push(`${pad}${theme.fg("dim", "Context:")} ${theme.fg(ctxTone as never, `${pct}%`)} ${theme.fg("dim", `(${tokStr})`)}`);
110
+ }
111
+
112
+ if (ctx.model) {
113
+ lines.push(`${pad}${theme.fg("dim", "Model:")} ${theme.fg("muted", ellipsizeMiddle(ctx.model.id, innerWidth - 10))}`);
114
+ }
115
+ if (state.runtime.activeSubagent) {
116
+ lines.push(`${pad}${theme.fg("dim", "Agent:")} ${theme.fg("muted", truncateToWidth(state.runtime.activeSubagent, innerWidth - 10))}`);
117
+ }
118
+
119
+ const modeFlags = [
120
+ state.runtime.launchMode ?? (state.runtime.autoOrch ? "auto" : "manual"),
121
+ state.runtime.planMode ? "plan" : "direct",
122
+ ].join(" | ");
123
+ lines.push(`${pad}${theme.fg("dim", "Mode:")} ${theme.fg("muted", modeFlags)}`);
124
+ lines.push("");
125
+ }
126
+
127
+ lines.push(`${pad}${theme.fg("accent", "-- Files Modified --")}`);
128
+ if (state.fileEdits.length === 0) {
129
+ lines.push(`${pad}${theme.fg("dim", " (none yet)")}`);
130
+ } else {
131
+ const seen = new Map<string, FileEdit>();
132
+ for (const edit of state.fileEdits) seen.set(edit.path, edit);
133
+ const deduped = [...seen.values()].slice(-12);
134
+
135
+ for (const edit of deduped) {
136
+ const icon = edit.action === "+" ? theme.fg("success", "+") : edit.action === "R" ? theme.fg("muted", "R") : theme.fg("warning", "M");
137
+ const displayPath = ellipsizeMiddle(edit.path.replace(/\\/g, "/"), innerWidth - 4);
138
+ lines.push(`${pad} ${icon} ${truncateToWidth(displayPath, innerWidth - 4)}`);
139
+ }
140
+ if (seen.size > 12) {
141
+ lines.push(`${pad} ${theme.fg("dim", `... +${seen.size - 12} more`)}`);
142
+ }
143
+ }
144
+ lines.push("");
145
+
146
+ lines.push(`${pad}${theme.fg("accent", "-- Tool Activity --")}`);
147
+ const toolEntries = Object.entries(state.toolUses).sort((a, b) => b[1] - a[1]);
148
+ if (toolEntries.length === 0) {
149
+ lines.push(`${pad}${theme.fg("dim", " (no tools used)")}`);
150
+ } else {
151
+ for (const [tool, count] of toolEntries.slice(0, 8)) {
152
+ lines.push(`${pad} ${theme.fg("success", "✓")} ${tool} ${theme.fg("dim", `(${count})`)}`);
153
+ }
154
+ }
155
+ lines.push("");
156
+
157
+ lines.push(`${pad}${theme.fg("dim", "(Alt+C to close)")}`);
158
+ lines.push(hBar);
159
+ return lines;
160
+ }
161
+ }
162
+
163
+ export class TakomiContextPanel {
164
+ private state = createEmptyState();
165
+ private visible = false;
166
+ private overlayHandle?: OverlayHandle;
167
+ private requestRender?: () => void;
168
+ private lastCtx?: ExtensionContext;
169
+
170
+ getState(): ContextPanelState {
171
+ return this.state;
172
+ }
173
+
174
+ isVisible(): boolean {
175
+ return this.visible;
176
+ }
177
+
178
+ setRuntimeState(runtime: ContextRuntimeState): void {
179
+ this.state.runtime = { ...runtime };
180
+ this.requestRender?.();
181
+ }
182
+
183
+ trackFileEdit(filePath: string, action: "M" | "+" | "R"): void {
184
+ this.state.fileEdits.push({ path: filePath, action, timestamp: Date.now() });
185
+ this.state.lastToolAt = Date.now();
186
+ this.requestRender?.();
187
+ }
188
+
189
+ trackToolUse(toolName: string): void {
190
+ this.state.toolUses[toolName] = (this.state.toolUses[toolName] ?? 0) + 1;
191
+ this.state.lastToolAt = Date.now();
192
+ this.requestRender?.();
193
+ }
194
+
195
+ resetSession(): void {
196
+ this.state = createEmptyState();
197
+ this.requestRender?.();
198
+ }
199
+
200
+ refresh(): void {
201
+ this.requestRender?.();
202
+ }
203
+
204
+ toggle(ctx: ExtensionContext): void {
205
+ this.lastCtx = ctx;
206
+ if (this.visible) this.hide();
207
+ else this.show(ctx);
208
+ }
209
+
210
+ show(ctx: ExtensionContext): void {
211
+ this.lastCtx = ctx;
212
+ if (!ctx.hasUI) return;
213
+ if (this.visible) {
214
+ this.requestRender?.();
215
+ return;
216
+ }
217
+ this.visible = true;
218
+
219
+ void ctx.ui.custom<void>(
220
+ (tui, theme, _keybindings, _done) => {
221
+ this.requestRender = () => tui.requestRender();
222
+ return new ContextPanelComponent(theme, () => this.state, () => this.lastCtx);
223
+ },
224
+ {
225
+ overlay: true,
226
+ overlayOptions: {
227
+ width: 36,
228
+ maxHeight: "70%",
229
+ anchor: "right-center",
230
+ margin: { right: 1, top: 2, bottom: 3 },
231
+ nonCapturing: true,
232
+ visible: (termWidth) => termWidth >= 100,
233
+ },
234
+ onHandle: (handle) => {
235
+ this.overlayHandle = handle;
236
+ },
237
+ },
238
+ ).then(() => {
239
+ this.visible = false;
240
+ this.overlayHandle = undefined;
241
+ this.requestRender = undefined;
242
+ });
243
+ }
244
+
245
+ hide(): void {
246
+ this.visible = false;
247
+ this.overlayHandle?.hide();
248
+ this.overlayHandle = undefined;
249
+ this.requestRender = undefined;
250
+ }
251
+ }
252
+
253
+ export function wireContextPanel(pi: ExtensionAPI, panel: TakomiContextPanel): void {
254
+ pi.on("tool_result", (event) => {
255
+ const toolName = event.toolName;
256
+ panel.trackToolUse(toolName);
257
+
258
+ if (toolName === "edit" || toolName === "write" || toolName === "read") {
259
+ const input = event.input as { file_path?: string; filePath?: string };
260
+ const filePath = input.file_path ?? input.filePath;
261
+ if (filePath) {
262
+ const action = toolName === "write" ? "+" : toolName === "read" ? "R" : "M";
263
+ panel.trackFileEdit(filePath, action);
264
+ }
265
+ }
266
+ });
267
+
268
+ pi.on("turn_end", () => {
269
+ panel.refresh();
270
+ });
271
+
272
+ pi.on("session_start", () => {
273
+ panel.resetSession();
274
+ });
275
+
276
+ pi.registerShortcut("alt+c", {
277
+ description: "Toggle Takomi context panel",
278
+ handler: async (ctx) => {
279
+ panel.toggle(ctx);
280
+ },
281
+ });
282
+ }