@oh-my-pi/pi-coding-agent 11.8.2 → 11.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/tui.md +9 -9
- package/package.json +7 -7
- package/src/cli/file-processor.ts +8 -13
- package/src/cli/oclif-help.ts +1 -1
- package/src/cli.ts +14 -0
- package/src/commit/git/index.ts +16 -16
- package/src/config/keybindings.ts +11 -11
- package/src/config/model-registry.ts +31 -66
- package/src/config/settings.ts +88 -95
- package/src/config.ts +2 -2
- package/src/cursor.ts +4 -4
- package/src/debug/index.ts +28 -28
- package/src/discovery/codex.ts +5 -13
- package/src/discovery/cursor.ts +2 -7
- package/src/exa/mcp-client.ts +2 -2
- package/src/exa/websets.ts +2 -2
- package/src/export/html/index.ts +3 -3
- package/src/export/ttsr.ts +27 -27
- package/src/extensibility/custom-tools/loader.ts +9 -9
- package/src/extensibility/extensions/runner.ts +64 -64
- package/src/extensibility/hooks/runner.ts +46 -46
- package/src/extensibility/plugins/manager.ts +49 -49
- package/src/index.ts +0 -1
- package/src/internal-urls/router.ts +5 -5
- package/src/ipy/kernel.ts +61 -57
- package/src/lsp/client.ts +1 -1
- package/src/lsp/clients/biome-client.ts +2 -2
- package/src/lsp/clients/lsp-linter-client.ts +7 -7
- package/src/lsp/index.ts +9 -9
- package/src/mcp/manager.ts +47 -47
- package/src/mcp/tool-bridge.ts +12 -12
- package/src/mcp/transports/http.ts +34 -34
- package/src/mcp/transports/stdio.ts +47 -47
- package/src/modes/components/assistant-message.ts +25 -25
- package/src/modes/components/bash-execution.ts +51 -51
- package/src/modes/components/bordered-loader.ts +7 -7
- package/src/modes/components/branch-summary-message.ts +7 -7
- package/src/modes/components/compaction-summary-message.ts +7 -7
- package/src/modes/components/countdown-timer.ts +15 -15
- package/src/modes/components/custom-editor.ts +22 -22
- package/src/modes/components/custom-message.ts +21 -21
- package/src/modes/components/dynamic-border.ts +3 -3
- package/src/modes/components/extensions/extension-dashboard.ts +72 -72
- package/src/modes/components/extensions/extension-list.ts +99 -97
- package/src/modes/components/extensions/inspector-panel.ts +26 -26
- package/src/modes/components/footer.ts +36 -36
- package/src/modes/components/history-search.ts +52 -52
- package/src/modes/components/hook-editor.ts +20 -20
- package/src/modes/components/hook-input.ts +20 -20
- package/src/modes/components/hook-message.ts +22 -22
- package/src/modes/components/hook-selector.ts +52 -52
- package/src/modes/components/index.ts +0 -1
- package/src/modes/components/login-dialog.ts +57 -57
- package/src/modes/components/model-selector.ts +173 -173
- package/src/modes/components/oauth-selector.ts +45 -45
- package/src/modes/components/plugin-settings.ts +52 -52
- package/src/modes/components/python-execution.ts +53 -53
- package/src/modes/components/queue-mode-selector.ts +7 -7
- package/src/modes/components/read-tool-group.ts +23 -23
- package/src/modes/components/session-selector.ts +40 -37
- package/src/modes/components/settings-selector.ts +80 -80
- package/src/modes/components/show-images-selector.ts +7 -7
- package/src/modes/components/skill-message.ts +27 -27
- package/src/modes/components/status-line-segment-editor.ts +81 -81
- package/src/modes/components/status-line.ts +73 -73
- package/src/modes/components/theme-selector.ts +11 -11
- package/src/modes/components/thinking-selector.ts +7 -7
- package/src/modes/components/todo-display.ts +19 -19
- package/src/modes/components/todo-reminder.ts +9 -9
- package/src/modes/components/tool-execution.ts +204 -196
- package/src/modes/components/tree-selector.ts +144 -144
- package/src/modes/components/ttsr-notification.ts +17 -17
- package/src/modes/components/user-message-selector.ts +18 -18
- package/src/modes/components/welcome.ts +10 -10
- package/src/modes/controllers/command-controller.ts +0 -7
- package/src/modes/controllers/event-controller.ts +23 -23
- package/src/modes/controllers/extension-ui-controller.ts +13 -13
- package/src/modes/controllers/input-controller.ts +4 -9
- package/src/modes/interactive-mode.ts +234 -241
- package/src/modes/rpc/rpc-client.ts +77 -77
- package/src/modes/rpc/rpc-mode.ts +5 -5
- package/src/modes/theme/theme.ts +113 -113
- package/src/modes/types.ts +0 -1
- package/src/patch/index.ts +45 -45
- package/src/prompts/tools/task.md +22 -2
- package/src/session/agent-session.ts +463 -476
- package/src/session/agent-storage.ts +72 -75
- package/src/session/auth-storage.ts +186 -252
- package/src/session/history-storage.ts +36 -38
- package/src/session/session-manager.ts +300 -299
- package/src/session/session-storage.ts +65 -90
- package/src/ssh/connection-manager.ts +9 -9
- package/src/task/agents.ts +1 -1
- package/src/task/executor.ts +2 -2
- package/src/task/index.ts +13 -12
- package/src/task/subprocess-tool-registry.ts +5 -5
- package/src/tools/ask.ts +7 -7
- package/src/tools/bash.ts +8 -7
- package/src/tools/browser.ts +123 -123
- package/src/tools/calculator.ts +46 -46
- package/src/tools/context.ts +9 -9
- package/src/tools/exit-plan-mode.ts +5 -5
- package/src/tools/fetch.ts +5 -5
- package/src/tools/find.ts +16 -16
- package/src/tools/grep.ts +10 -10
- package/src/tools/notebook.ts +6 -6
- package/src/tools/output-meta.ts +10 -2
- package/src/tools/python.ts +12 -11
- package/src/tools/read.ts +17 -17
- package/src/tools/ssh.ts +9 -9
- package/src/tools/submit-result.ts +13 -13
- package/src/tools/todo-write.ts +6 -6
- package/src/tools/write.ts +10 -10
- package/src/tui/output-block.ts +6 -6
- package/src/tui/utils.ts +9 -9
- package/src/utils/event-bus.ts +10 -10
- package/src/utils/frontmatter.ts +1 -1
- package/src/utils/ignore-files.ts +1 -1
- package/src/web/search/index.ts +5 -5
- package/src/web/search/providers/anthropic.ts +7 -2
- package/examples/hooks/snake.ts +0 -342
- package/src/modes/components/armin.ts +0 -379
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Handles TUI rendering and user interaction, delegating business logic to AgentSession.
|
|
4
4
|
*/
|
|
5
5
|
import * as path from "node:path";
|
|
6
|
-
import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
|
|
6
|
+
import type { Agent, AgentMessage } from "@oh-my-pi/pi-agent-core";
|
|
7
7
|
import type { AssistantMessage, ImageContent, Message, Model, UsageReport } from "@oh-my-pi/pi-ai";
|
|
8
8
|
import type { Component, Loader, SlashCommand } from "@oh-my-pi/pi-tui";
|
|
9
9
|
import {
|
|
@@ -74,83 +74,83 @@ export interface InteractiveModeOptions {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
export class InteractiveMode implements InteractiveModeContext {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
77
|
+
session: AgentSession;
|
|
78
|
+
sessionManager: SessionManager;
|
|
79
|
+
settings: Settings;
|
|
80
|
+
keybindings: KeybindingsManager;
|
|
81
|
+
agent: Agent;
|
|
82
|
+
historyStorage?: HistoryStorage;
|
|
83
|
+
|
|
84
|
+
ui: TUI;
|
|
85
|
+
chatContainer: Container;
|
|
86
|
+
pendingMessagesContainer: Container;
|
|
87
|
+
statusContainer: Container;
|
|
88
|
+
todoContainer: Container;
|
|
89
|
+
editor: CustomEditor;
|
|
90
|
+
editorContainer: Container;
|
|
91
|
+
statusLine: StatusLineComponent;
|
|
92
|
+
|
|
93
|
+
isInitialized = false;
|
|
94
|
+
isBackgrounded = false;
|
|
95
|
+
isBashMode = false;
|
|
96
|
+
toolOutputExpanded = false;
|
|
97
|
+
todoExpanded = false;
|
|
98
|
+
planModeEnabled = false;
|
|
99
|
+
planModePaused = false;
|
|
100
|
+
planModePlanFilePath: string | undefined = undefined;
|
|
101
|
+
todoItems: TodoItem[] = [];
|
|
102
|
+
hideThinkingBlock = false;
|
|
103
|
+
pendingImages: ImageContent[] = [];
|
|
104
|
+
compactionQueuedMessages: CompactionQueuedMessage[] = [];
|
|
105
|
+
pendingTools = new Map<string, ToolExecutionHandle>();
|
|
106
|
+
pendingBashComponents: BashExecutionComponent[] = [];
|
|
107
|
+
bashComponent: BashExecutionComponent | undefined = undefined;
|
|
108
|
+
pendingPythonComponents: PythonExecutionComponent[] = [];
|
|
109
|
+
pythonComponent: PythonExecutionComponent | undefined = undefined;
|
|
110
|
+
isPythonMode = false;
|
|
111
|
+
streamingComponent: AssistantMessageComponent | undefined = undefined;
|
|
112
|
+
streamingMessage: AssistantMessage | undefined = undefined;
|
|
113
|
+
loadingAnimation: Loader | undefined = undefined;
|
|
114
|
+
autoCompactionLoader: Loader | undefined = undefined;
|
|
115
|
+
retryLoader: Loader | undefined = undefined;
|
|
116
|
+
#pendingWorkingMessage: string | undefined;
|
|
117
|
+
readonly #defaultWorkingMessage = `Working… (esc to interrupt)`;
|
|
118
|
+
autoCompactionEscapeHandler?: () => void;
|
|
119
|
+
retryEscapeHandler?: () => void;
|
|
120
|
+
unsubscribe?: () => void;
|
|
121
|
+
onInputCallback?: (input: { text: string; images?: ImageContent[] }) => void;
|
|
122
|
+
lastSigintTime = 0;
|
|
123
|
+
lastEscapeTime = 0;
|
|
124
|
+
shutdownRequested = false;
|
|
125
|
+
#isShuttingDown = false;
|
|
126
|
+
hookSelector: HookSelectorComponent | undefined = undefined;
|
|
127
|
+
hookInput: HookInputComponent | undefined = undefined;
|
|
128
|
+
hookEditor: HookEditorComponent | undefined = undefined;
|
|
129
|
+
lastStatusSpacer: Spacer | undefined = undefined;
|
|
130
|
+
lastStatusText: Text | undefined = undefined;
|
|
131
|
+
fileSlashCommands: Set<string> = new Set();
|
|
132
|
+
skillCommands: Map<string, string> = new Map();
|
|
133
|
+
|
|
134
|
+
#pendingSlashCommands: SlashCommand[] = [];
|
|
135
|
+
#cleanupUnsubscribe?: () => void;
|
|
136
|
+
readonly #version: string;
|
|
137
|
+
readonly #changelogMarkdown: string | undefined;
|
|
138
|
+
#planModePreviousTools: string[] | undefined;
|
|
139
|
+
#planModePreviousModel: Model | undefined;
|
|
140
|
+
#pendingModelSwitch: Model | undefined;
|
|
141
|
+
#planModeHasEntered = false;
|
|
142
|
+
readonly lspServers:
|
|
143
143
|
| Array<{ name: string; status: "ready" | "error"; fileTypes: string[]; error?: string }>
|
|
144
144
|
| undefined = undefined;
|
|
145
|
-
|
|
146
|
-
|
|
145
|
+
mcpManager?: import("../mcp").MCPManager;
|
|
146
|
+
readonly #toolUiContextSetter: (uiContext: ExtensionUIContext, hasUI: boolean) => void;
|
|
147
147
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
148
|
+
readonly #commandController: CommandController;
|
|
149
|
+
readonly #eventController: EventController;
|
|
150
|
+
readonly #extensionUiController: ExtensionUiController;
|
|
151
|
+
readonly #inputController: InputController;
|
|
152
|
+
readonly #selectorController: SelectorController;
|
|
153
|
+
readonly #uiHelpers: UiHelpers;
|
|
154
154
|
|
|
155
155
|
constructor(
|
|
156
156
|
session: AgentSession,
|
|
@@ -167,9 +167,9 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
167
167
|
this.settings = session.settings;
|
|
168
168
|
this.keybindings = KeybindingsManager.inMemory();
|
|
169
169
|
this.agent = session.agent;
|
|
170
|
-
this
|
|
171
|
-
this
|
|
172
|
-
this
|
|
170
|
+
this.#version = version;
|
|
171
|
+
this.#changelogMarkdown = changelogMarkdown;
|
|
172
|
+
this.#toolUiContextSetter = setToolUIContext;
|
|
173
173
|
this.lspServers = lspServers;
|
|
174
174
|
this.mcpManager = mcpManager;
|
|
175
175
|
|
|
@@ -227,14 +227,14 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
227
227
|
}
|
|
228
228
|
|
|
229
229
|
// Store pending commands for init() where file commands are loaded async
|
|
230
|
-
this
|
|
230
|
+
this.#pendingSlashCommands = [...BUILTIN_SLASH_COMMANDS, ...hookCommands, ...customCommands, ...skillCommandList];
|
|
231
231
|
|
|
232
|
-
this
|
|
233
|
-
this
|
|
234
|
-
this
|
|
235
|
-
this
|
|
236
|
-
this
|
|
237
|
-
this
|
|
232
|
+
this.#uiHelpers = new UiHelpers(this);
|
|
233
|
+
this.#extensionUiController = new ExtensionUiController(this);
|
|
234
|
+
this.#eventController = new EventController(this);
|
|
235
|
+
this.#commandController = new CommandController(this);
|
|
236
|
+
this.#selectorController = new SelectorController(this);
|
|
237
|
+
this.#inputController = new InputController(this);
|
|
238
238
|
}
|
|
239
239
|
|
|
240
240
|
async init(): Promise<void> {
|
|
@@ -245,7 +245,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
245
245
|
debugStartup("InteractiveMode.init:keybindings");
|
|
246
246
|
|
|
247
247
|
// Register session manager flush for signal handlers (SIGINT, SIGTERM, SIGHUP)
|
|
248
|
-
this
|
|
248
|
+
this.#cleanupUnsubscribe = postmortem.register("session-manager-flush", () => this.sessionManager.flush());
|
|
249
249
|
debugStartup("InteractiveMode.init:cleanupRegistered");
|
|
250
250
|
|
|
251
251
|
// Load and convert file commands to SlashCommand format (async)
|
|
@@ -259,7 +259,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
259
259
|
|
|
260
260
|
// Setup autocomplete with all commands
|
|
261
261
|
const autocompleteProvider = new CombinedAutocompleteProvider(
|
|
262
|
-
[...this
|
|
262
|
+
[...this.#pendingSlashCommands, ...fileSlashCommands],
|
|
263
263
|
process.cwd(),
|
|
264
264
|
);
|
|
265
265
|
this.editor.setAutocompleteProvider(autocompleteProvider);
|
|
@@ -288,7 +288,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
288
288
|
if (!startupQuiet) {
|
|
289
289
|
// Add welcome header
|
|
290
290
|
debugStartup("InteractiveMode.init:welcomeComponent:start");
|
|
291
|
-
const welcome = new WelcomeComponent(this
|
|
291
|
+
const welcome = new WelcomeComponent(this.#version, modelName, providerName, recentSessions, lspServerInfo);
|
|
292
292
|
debugStartup("InteractiveMode.init:welcomeComponent:created");
|
|
293
293
|
|
|
294
294
|
// Setup UI layout
|
|
@@ -297,17 +297,17 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
297
297
|
this.ui.addChild(new Spacer(1));
|
|
298
298
|
|
|
299
299
|
// Add changelog if provided
|
|
300
|
-
if (this
|
|
300
|
+
if (this.#changelogMarkdown) {
|
|
301
301
|
this.ui.addChild(new DynamicBorder());
|
|
302
302
|
if (settings.get("collapseChangelog")) {
|
|
303
|
-
const versionMatch = this
|
|
304
|
-
const latestVersion = versionMatch ? versionMatch[1] : this
|
|
303
|
+
const versionMatch = this.#changelogMarkdown.match(/##\s+\[?(\d+\.\d+\.\d+)\]?/);
|
|
304
|
+
const latestVersion = versionMatch ? versionMatch[1] : this.#version;
|
|
305
305
|
const condensedText = `Updated to v${latestVersion}. Use ${theme.bold("/changelog")} to view full changelog.`;
|
|
306
306
|
this.ui.addChild(new Text(condensedText, 1, 0));
|
|
307
307
|
} else {
|
|
308
308
|
this.ui.addChild(new Text(theme.bold(theme.fg("accent", "What's New")), 1, 0));
|
|
309
309
|
this.ui.addChild(new Spacer(1));
|
|
310
|
-
this.ui.addChild(new Markdown(this
|
|
310
|
+
this.ui.addChild(new Markdown(this.#changelogMarkdown.trim(), 1, 0, getMarkdownTheme()));
|
|
311
311
|
this.ui.addChild(new Spacer(1));
|
|
312
312
|
}
|
|
313
313
|
this.ui.addChild(new DynamicBorder());
|
|
@@ -329,11 +329,11 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
329
329
|
this.ui.addChild(this.statusLine); // Only renders hook statuses (main status in editor border)
|
|
330
330
|
this.ui.setFocus(this.editor);
|
|
331
331
|
|
|
332
|
-
this
|
|
333
|
-
this
|
|
332
|
+
this.#inputController.setupKeyHandlers();
|
|
333
|
+
this.#inputController.setupEditorSubmitHandler();
|
|
334
334
|
|
|
335
335
|
// Load initial todos
|
|
336
|
-
await this
|
|
336
|
+
await this.#loadTodoList();
|
|
337
337
|
|
|
338
338
|
// Start the UI
|
|
339
339
|
this.ui.start();
|
|
@@ -347,10 +347,10 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
347
347
|
await this.initHooksAndCustomTools();
|
|
348
348
|
|
|
349
349
|
// Restore mode from session (e.g. plan mode on resume)
|
|
350
|
-
await this
|
|
350
|
+
await this.#restoreModeFromSession();
|
|
351
351
|
|
|
352
352
|
// Subscribe to agent events
|
|
353
|
-
this
|
|
353
|
+
this.#subscribeToAgent();
|
|
354
354
|
|
|
355
355
|
// Set up theme file watcher
|
|
356
356
|
onThemeChange(() => {
|
|
@@ -403,7 +403,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
403
403
|
this.renderSessionContext(context);
|
|
404
404
|
}
|
|
405
405
|
|
|
406
|
-
|
|
406
|
+
#formatTodoLine(todo: TodoItem, prefix: string): string {
|
|
407
407
|
const checkbox = theme.checkbox;
|
|
408
408
|
const label = todo.content;
|
|
409
409
|
switch (todo.status) {
|
|
@@ -416,7 +416,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
416
416
|
}
|
|
417
417
|
}
|
|
418
418
|
|
|
419
|
-
|
|
419
|
+
#getCollapsedTodos(todos: TodoItem[]): TodoItem[] {
|
|
420
420
|
let startIndex = 0;
|
|
421
421
|
for (let i = todos.length - 1; i >= 0; i -= 1) {
|
|
422
422
|
if (todos[i].status === "completed") {
|
|
@@ -427,20 +427,20 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
427
427
|
return todos.slice(startIndex, startIndex + 5);
|
|
428
428
|
}
|
|
429
429
|
|
|
430
|
-
|
|
430
|
+
#renderTodoList(): void {
|
|
431
431
|
this.todoContainer.clear();
|
|
432
432
|
if (this.todoItems.length === 0) {
|
|
433
433
|
return;
|
|
434
434
|
}
|
|
435
435
|
|
|
436
|
-
const visibleTodos = this.todoExpanded ? this.todoItems : this
|
|
436
|
+
const visibleTodos = this.todoExpanded ? this.todoItems : this.#getCollapsedTodos(this.todoItems);
|
|
437
437
|
const indent = " ";
|
|
438
438
|
const hook = theme.tree.hook;
|
|
439
439
|
const lines = [indent + theme.bold(theme.fg("accent", "Todos"))];
|
|
440
440
|
|
|
441
441
|
visibleTodos.forEach((todo, index) => {
|
|
442
442
|
const prefix = `${indent}${index === 0 ? hook : " "} `;
|
|
443
|
-
lines.push(this
|
|
443
|
+
lines.push(this.#formatTodoLine(todo, prefix));
|
|
444
444
|
});
|
|
445
445
|
|
|
446
446
|
if (!this.todoExpanded && visibleTodos.length < this.todoItems.length) {
|
|
@@ -451,11 +451,11 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
451
451
|
this.todoContainer.addChild(new Text(lines.join("\n"), 1, 0));
|
|
452
452
|
}
|
|
453
453
|
|
|
454
|
-
|
|
454
|
+
async #loadTodoList(): Promise<void> {
|
|
455
455
|
const sessionFile = this.sessionManager.getSessionFile() ?? null;
|
|
456
456
|
if (!sessionFile) {
|
|
457
457
|
this.todoItems = [];
|
|
458
|
-
this
|
|
458
|
+
this.#renderTodoList();
|
|
459
459
|
return;
|
|
460
460
|
}
|
|
461
461
|
const artifactsDir = sessionFile.slice(0, -6);
|
|
@@ -470,20 +470,20 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
470
470
|
} catch (error) {
|
|
471
471
|
if (isEnoent(error)) {
|
|
472
472
|
this.todoItems = [];
|
|
473
|
-
this
|
|
473
|
+
this.#renderTodoList();
|
|
474
474
|
return;
|
|
475
475
|
}
|
|
476
476
|
logger.warn("Failed to load todos", { path: todoPath, error: String(error) });
|
|
477
477
|
}
|
|
478
|
-
this
|
|
478
|
+
this.#renderTodoList();
|
|
479
479
|
}
|
|
480
480
|
|
|
481
|
-
|
|
481
|
+
#getPlanFilePath(): string {
|
|
482
482
|
const sessionId = this.sessionManager.getSessionId();
|
|
483
483
|
return `plan://${sessionId}/plan.md`;
|
|
484
484
|
}
|
|
485
485
|
|
|
486
|
-
|
|
486
|
+
#resolvePlanFilePath(planFilePath: string): string {
|
|
487
487
|
if (planFilePath.startsWith("plan://")) {
|
|
488
488
|
return resolvePlanUrlToPath(planFilePath, {
|
|
489
489
|
getPlansDirectory: () => this.settings.getPlansDirectory(),
|
|
@@ -493,7 +493,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
493
493
|
return planFilePath;
|
|
494
494
|
}
|
|
495
495
|
|
|
496
|
-
|
|
496
|
+
#updatePlanModeStatus(): void {
|
|
497
497
|
const status =
|
|
498
498
|
this.planModeEnabled || this.planModePaused
|
|
499
499
|
? {
|
|
@@ -506,16 +506,16 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
506
506
|
this.ui.requestRender();
|
|
507
507
|
}
|
|
508
508
|
|
|
509
|
-
|
|
509
|
+
async #applyPlanModeModel(): Promise<void> {
|
|
510
510
|
const planModel = this.session.resolveRoleModel("plan");
|
|
511
511
|
if (!planModel) return;
|
|
512
512
|
const currentModel = this.session.model;
|
|
513
513
|
if (currentModel && currentModel.provider === planModel.provider && currentModel.id === planModel.id) {
|
|
514
514
|
return;
|
|
515
515
|
}
|
|
516
|
-
this
|
|
516
|
+
this.#planModePreviousModel = currentModel;
|
|
517
517
|
if (this.session.isStreaming) {
|
|
518
|
-
this
|
|
518
|
+
this.#pendingModelSwitch = planModel;
|
|
519
519
|
return;
|
|
520
520
|
}
|
|
521
521
|
try {
|
|
@@ -529,9 +529,9 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
529
529
|
|
|
530
530
|
/** Apply any deferred model switch after the current stream ends. */
|
|
531
531
|
async flushPendingModelSwitch(): Promise<void> {
|
|
532
|
-
const model = this
|
|
532
|
+
const model = this.#pendingModelSwitch;
|
|
533
533
|
if (!model) return;
|
|
534
|
-
this
|
|
534
|
+
this.#pendingModelSwitch = undefined;
|
|
535
535
|
try {
|
|
536
536
|
await this.session.setModelTemporary(model);
|
|
537
537
|
} catch (error) {
|
|
@@ -542,35 +542,32 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
542
542
|
}
|
|
543
543
|
|
|
544
544
|
/** Restore mode state from session entries on resume (e.g. plan mode). */
|
|
545
|
-
|
|
545
|
+
async #restoreModeFromSession(): Promise<void> {
|
|
546
546
|
const sessionContext = this.sessionManager.buildSessionContext();
|
|
547
547
|
if (sessionContext.mode === "plan") {
|
|
548
548
|
const planFilePath = sessionContext.modeData?.planFilePath as string | undefined;
|
|
549
|
-
await this
|
|
549
|
+
await this.#enterPlanMode({ planFilePath });
|
|
550
550
|
} else if (sessionContext.mode === "plan_paused") {
|
|
551
551
|
this.planModePaused = true;
|
|
552
|
-
this
|
|
553
|
-
this
|
|
552
|
+
this.#planModeHasEntered = true;
|
|
553
|
+
this.#updatePlanModeStatus();
|
|
554
554
|
}
|
|
555
555
|
}
|
|
556
556
|
|
|
557
|
-
|
|
558
|
-
planFilePath?: string;
|
|
559
|
-
workflow?: "parallel" | "iterative";
|
|
560
|
-
}): Promise<void> {
|
|
557
|
+
async #enterPlanMode(options?: { planFilePath?: string; workflow?: "parallel" | "iterative" }): Promise<void> {
|
|
561
558
|
if (this.planModeEnabled) {
|
|
562
559
|
return;
|
|
563
560
|
}
|
|
564
561
|
|
|
565
562
|
this.planModePaused = false;
|
|
566
563
|
|
|
567
|
-
const planFilePath = options?.planFilePath ?? this
|
|
564
|
+
const planFilePath = options?.planFilePath ?? this.#getPlanFilePath();
|
|
568
565
|
const previousTools = this.session.getActiveToolNames();
|
|
569
566
|
const hasExitTool = this.session.getToolByName("exit_plan_mode") !== undefined;
|
|
570
567
|
const planTools = hasExitTool ? [...previousTools, "exit_plan_mode"] : previousTools;
|
|
571
568
|
const uniquePlanTools = [...new Set(planTools)];
|
|
572
569
|
|
|
573
|
-
this
|
|
570
|
+
this.#planModePreviousTools = previousTools;
|
|
574
571
|
this.planModePlanFilePath = planFilePath;
|
|
575
572
|
this.planModeEnabled = true;
|
|
576
573
|
|
|
@@ -579,32 +576,32 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
579
576
|
enabled: true,
|
|
580
577
|
planFilePath,
|
|
581
578
|
workflow: options?.workflow ?? "parallel",
|
|
582
|
-
reentry: this
|
|
579
|
+
reentry: this.#planModeHasEntered,
|
|
583
580
|
});
|
|
584
581
|
if (this.session.isStreaming) {
|
|
585
582
|
await this.session.sendPlanModeContext({ deliverAs: "steer" });
|
|
586
583
|
}
|
|
587
|
-
this
|
|
588
|
-
await this
|
|
589
|
-
this
|
|
584
|
+
this.#planModeHasEntered = true;
|
|
585
|
+
await this.#applyPlanModeModel();
|
|
586
|
+
this.#updatePlanModeStatus();
|
|
590
587
|
this.sessionManager.appendModeChange("plan", { planFilePath });
|
|
591
588
|
this.showStatus(`Plan mode enabled. Plan file: ${planFilePath}`);
|
|
592
589
|
}
|
|
593
590
|
|
|
594
|
-
|
|
591
|
+
async #exitPlanMode(options?: { silent?: boolean; paused?: boolean }): Promise<void> {
|
|
595
592
|
if (!this.planModeEnabled) {
|
|
596
593
|
return;
|
|
597
594
|
}
|
|
598
595
|
|
|
599
|
-
const previousTools = this
|
|
596
|
+
const previousTools = this.#planModePreviousTools;
|
|
600
597
|
if (previousTools && previousTools.length > 0) {
|
|
601
598
|
await this.session.setActiveToolsByName(previousTools);
|
|
602
599
|
}
|
|
603
|
-
if (this
|
|
600
|
+
if (this.#planModePreviousModel) {
|
|
604
601
|
if (this.session.isStreaming) {
|
|
605
|
-
this
|
|
602
|
+
this.#pendingModelSwitch = this.#planModePreviousModel;
|
|
606
603
|
} else {
|
|
607
|
-
await this.session.setModelTemporary(this
|
|
604
|
+
await this.session.setModelTemporary(this.#planModePreviousModel);
|
|
608
605
|
}
|
|
609
606
|
}
|
|
610
607
|
|
|
@@ -612,9 +609,9 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
612
609
|
this.planModeEnabled = false;
|
|
613
610
|
this.planModePaused = options?.paused ?? false;
|
|
614
611
|
this.planModePlanFilePath = undefined;
|
|
615
|
-
this
|
|
616
|
-
this
|
|
617
|
-
this
|
|
612
|
+
this.#planModePreviousTools = undefined;
|
|
613
|
+
this.#planModePreviousModel = undefined;
|
|
614
|
+
this.#updatePlanModeStatus();
|
|
618
615
|
const paused = options?.paused ?? false;
|
|
619
616
|
this.sessionManager.appendModeChange(paused ? "plan_paused" : "none");
|
|
620
617
|
if (!options?.silent) {
|
|
@@ -622,8 +619,8 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
622
619
|
}
|
|
623
620
|
}
|
|
624
621
|
|
|
625
|
-
|
|
626
|
-
const resolvedPath = this
|
|
622
|
+
async #readPlanFile(planFilePath: string): Promise<string | null> {
|
|
623
|
+
const resolvedPath = this.#resolvePlanFilePath(planFilePath);
|
|
627
624
|
try {
|
|
628
625
|
return await Bun.file(resolvedPath).text();
|
|
629
626
|
} catch (error) {
|
|
@@ -634,7 +631,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
634
631
|
}
|
|
635
632
|
}
|
|
636
633
|
|
|
637
|
-
|
|
634
|
+
#renderPlanPreview(planContent: string): void {
|
|
638
635
|
this.chatContainer.addChild(new Spacer(1));
|
|
639
636
|
this.chatContainer.addChild(new DynamicBorder());
|
|
640
637
|
this.chatContainer.addChild(new Text(theme.bold(theme.fg("accent", "Plan Review")), 1, 1));
|
|
@@ -644,9 +641,9 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
644
641
|
this.ui.requestRender();
|
|
645
642
|
}
|
|
646
643
|
|
|
647
|
-
|
|
648
|
-
const previousTools = this
|
|
649
|
-
await this
|
|
644
|
+
async #approvePlan(planContent: string): Promise<void> {
|
|
645
|
+
const previousTools = this.#planModePreviousTools ?? this.session.getActiveToolNames();
|
|
646
|
+
await this.#exitPlanMode({ silent: true, paused: false });
|
|
650
647
|
await this.handleClearCommand();
|
|
651
648
|
if (previousTools.length > 0) {
|
|
652
649
|
await this.session.setActiveToolsByName(previousTools);
|
|
@@ -663,10 +660,10 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
663
660
|
"This exits plan mode without approving a plan.",
|
|
664
661
|
);
|
|
665
662
|
if (!confirmed) return;
|
|
666
|
-
await this
|
|
663
|
+
await this.#exitPlanMode({ paused: true });
|
|
667
664
|
return;
|
|
668
665
|
}
|
|
669
|
-
await this
|
|
666
|
+
await this.#enterPlanMode();
|
|
670
667
|
}
|
|
671
668
|
|
|
672
669
|
async handleExitPlanModeTool(details: ExitPlanModeDetails): Promise<void> {
|
|
@@ -675,15 +672,15 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
675
672
|
return;
|
|
676
673
|
}
|
|
677
674
|
|
|
678
|
-
const planFilePath = details.planFilePath || this.planModePlanFilePath || this
|
|
675
|
+
const planFilePath = details.planFilePath || this.planModePlanFilePath || this.#getPlanFilePath();
|
|
679
676
|
this.planModePlanFilePath = planFilePath;
|
|
680
|
-
const planContent = await this
|
|
677
|
+
const planContent = await this.#readPlanFile(planFilePath);
|
|
681
678
|
if (!planContent) {
|
|
682
679
|
this.showError(`Plan file not found at ${planFilePath}`);
|
|
683
680
|
return;
|
|
684
681
|
}
|
|
685
682
|
|
|
686
|
-
this
|
|
683
|
+
this.#renderPlanPreview(planContent);
|
|
687
684
|
const choice = await this.showHookSelector("Plan mode - next step", [
|
|
688
685
|
"Approve and execute",
|
|
689
686
|
"Refine plan",
|
|
@@ -691,7 +688,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
691
688
|
]);
|
|
692
689
|
|
|
693
690
|
if (choice === "Approve and execute") {
|
|
694
|
-
await this
|
|
691
|
+
await this.#approvePlan(planContent);
|
|
695
692
|
return;
|
|
696
693
|
}
|
|
697
694
|
if (choice === "Refine plan") {
|
|
@@ -711,8 +708,8 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
711
708
|
if (this.unsubscribe) {
|
|
712
709
|
this.unsubscribe();
|
|
713
710
|
}
|
|
714
|
-
if (this
|
|
715
|
-
this
|
|
711
|
+
if (this.#cleanupUnsubscribe) {
|
|
712
|
+
this.#cleanupUnsubscribe();
|
|
716
713
|
}
|
|
717
714
|
if (this.isInitialized) {
|
|
718
715
|
this.ui.stop();
|
|
@@ -721,8 +718,8 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
721
718
|
}
|
|
722
719
|
|
|
723
720
|
async shutdown(): Promise<void> {
|
|
724
|
-
if (this
|
|
725
|
-
this
|
|
721
|
+
if (this.#isShuttingDown) return;
|
|
722
|
+
this.#isShuttingDown = true;
|
|
726
723
|
|
|
727
724
|
// Flush pending session writes before shutdown
|
|
728
725
|
await this.sessionManager.flush();
|
|
@@ -761,40 +758,40 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
761
758
|
|
|
762
759
|
// Extension UI integration
|
|
763
760
|
setToolUIContext(uiContext: ExtensionUIContext, hasUI: boolean): void {
|
|
764
|
-
this
|
|
761
|
+
this.#toolUiContextSetter(uiContext, hasUI);
|
|
765
762
|
}
|
|
766
763
|
|
|
767
764
|
initializeHookRunner(uiContext: ExtensionUIContext, hasUI: boolean): void {
|
|
768
|
-
this
|
|
765
|
+
this.#extensionUiController.initializeHookRunner(uiContext, hasUI);
|
|
769
766
|
}
|
|
770
767
|
|
|
771
768
|
createBackgroundUiContext(): ExtensionUIContext {
|
|
772
|
-
return this
|
|
769
|
+
return this.#extensionUiController.createBackgroundUiContext();
|
|
773
770
|
}
|
|
774
771
|
|
|
775
772
|
// Event handling
|
|
776
773
|
async handleBackgroundEvent(event: AgentSessionEvent): Promise<void> {
|
|
777
|
-
await this
|
|
774
|
+
await this.#eventController.handleBackgroundEvent(event);
|
|
778
775
|
}
|
|
779
776
|
|
|
780
777
|
// UI helpers
|
|
781
778
|
showStatus(message: string, options?: { dim?: boolean }): void {
|
|
782
|
-
this
|
|
779
|
+
this.#uiHelpers.showStatus(message, options);
|
|
783
780
|
}
|
|
784
781
|
|
|
785
782
|
showError(message: string): void {
|
|
786
|
-
this
|
|
783
|
+
this.#uiHelpers.showError(message);
|
|
787
784
|
}
|
|
788
785
|
|
|
789
786
|
showWarning(message: string): void {
|
|
790
|
-
this
|
|
787
|
+
this.#uiHelpers.showWarning(message);
|
|
791
788
|
}
|
|
792
789
|
|
|
793
790
|
setWorkingMessage(message?: string): void {
|
|
794
791
|
if (message === undefined) {
|
|
795
|
-
this
|
|
792
|
+
this.#pendingWorkingMessage = undefined;
|
|
796
793
|
if (this.loadingAnimation) {
|
|
797
|
-
this.loadingAnimation.setMessage(this
|
|
794
|
+
this.loadingAnimation.setMessage(this.#defaultWorkingMessage);
|
|
798
795
|
}
|
|
799
796
|
return;
|
|
800
797
|
}
|
|
@@ -804,276 +801,272 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
804
801
|
return;
|
|
805
802
|
}
|
|
806
803
|
|
|
807
|
-
this
|
|
804
|
+
this.#pendingWorkingMessage = message;
|
|
808
805
|
}
|
|
809
806
|
|
|
810
807
|
applyPendingWorkingMessage(): void {
|
|
811
|
-
if (this
|
|
808
|
+
if (this.#pendingWorkingMessage === undefined) {
|
|
812
809
|
return;
|
|
813
810
|
}
|
|
814
811
|
|
|
815
|
-
const message = this
|
|
816
|
-
this
|
|
812
|
+
const message = this.#pendingWorkingMessage;
|
|
813
|
+
this.#pendingWorkingMessage = undefined;
|
|
817
814
|
this.setWorkingMessage(message);
|
|
818
815
|
}
|
|
819
816
|
|
|
820
817
|
showNewVersionNotification(newVersion: string): void {
|
|
821
|
-
this
|
|
818
|
+
this.#uiHelpers.showNewVersionNotification(newVersion);
|
|
822
819
|
}
|
|
823
820
|
|
|
824
821
|
clearEditor(): void {
|
|
825
|
-
this
|
|
822
|
+
this.#uiHelpers.clearEditor();
|
|
826
823
|
}
|
|
827
824
|
|
|
828
825
|
updatePendingMessagesDisplay(): void {
|
|
829
|
-
this
|
|
826
|
+
this.#uiHelpers.updatePendingMessagesDisplay();
|
|
830
827
|
}
|
|
831
828
|
|
|
832
829
|
queueCompactionMessage(text: string, mode: "steer" | "followUp"): void {
|
|
833
|
-
this
|
|
830
|
+
this.#uiHelpers.queueCompactionMessage(text, mode);
|
|
834
831
|
}
|
|
835
832
|
|
|
836
833
|
flushCompactionQueue(options?: { willRetry?: boolean }): Promise<void> {
|
|
837
|
-
return this
|
|
834
|
+
return this.#uiHelpers.flushCompactionQueue(options);
|
|
838
835
|
}
|
|
839
836
|
|
|
840
837
|
flushPendingBashComponents(): void {
|
|
841
|
-
this
|
|
838
|
+
this.#uiHelpers.flushPendingBashComponents();
|
|
842
839
|
}
|
|
843
840
|
|
|
844
841
|
isKnownSlashCommand(text: string): boolean {
|
|
845
|
-
return this
|
|
842
|
+
return this.#uiHelpers.isKnownSlashCommand(text);
|
|
846
843
|
}
|
|
847
844
|
|
|
848
845
|
addMessageToChat(message: AgentMessage, options?: { populateHistory?: boolean }): void {
|
|
849
|
-
this
|
|
846
|
+
this.#uiHelpers.addMessageToChat(message, options);
|
|
850
847
|
}
|
|
851
848
|
|
|
852
849
|
renderSessionContext(
|
|
853
850
|
sessionContext: SessionContext,
|
|
854
851
|
options?: { updateFooter?: boolean; populateHistory?: boolean },
|
|
855
852
|
): void {
|
|
856
|
-
this
|
|
853
|
+
this.#uiHelpers.renderSessionContext(sessionContext, options);
|
|
857
854
|
}
|
|
858
855
|
|
|
859
856
|
renderInitialMessages(): void {
|
|
860
|
-
this
|
|
857
|
+
this.#uiHelpers.renderInitialMessages();
|
|
861
858
|
}
|
|
862
859
|
|
|
863
860
|
getUserMessageText(message: Message): string {
|
|
864
|
-
return this
|
|
861
|
+
return this.#uiHelpers.getUserMessageText(message);
|
|
865
862
|
}
|
|
866
863
|
|
|
867
864
|
findLastAssistantMessage(): AssistantMessage | undefined {
|
|
868
|
-
return this
|
|
865
|
+
return this.#uiHelpers.findLastAssistantMessage();
|
|
869
866
|
}
|
|
870
867
|
|
|
871
868
|
extractAssistantText(message: AssistantMessage): string {
|
|
872
|
-
return this
|
|
869
|
+
return this.#uiHelpers.extractAssistantText(message);
|
|
873
870
|
}
|
|
874
871
|
|
|
875
872
|
// Command handling
|
|
876
873
|
handleExportCommand(text: string): Promise<void> {
|
|
877
|
-
return this
|
|
874
|
+
return this.#commandController.handleExportCommand(text);
|
|
878
875
|
}
|
|
879
876
|
|
|
880
877
|
handleDumpCommand(): Promise<void> {
|
|
881
|
-
return this
|
|
878
|
+
return this.#commandController.handleDumpCommand();
|
|
882
879
|
}
|
|
883
880
|
|
|
884
881
|
handleShareCommand(): Promise<void> {
|
|
885
|
-
return this
|
|
882
|
+
return this.#commandController.handleShareCommand();
|
|
886
883
|
}
|
|
887
884
|
|
|
888
885
|
handleCopyCommand(): Promise<void> {
|
|
889
|
-
return this
|
|
886
|
+
return this.#commandController.handleCopyCommand();
|
|
890
887
|
}
|
|
891
888
|
|
|
892
889
|
handleSessionCommand(): Promise<void> {
|
|
893
|
-
return this
|
|
890
|
+
return this.#commandController.handleSessionCommand();
|
|
894
891
|
}
|
|
895
892
|
|
|
896
893
|
handleUsageCommand(reports?: UsageReport[] | null): Promise<void> {
|
|
897
|
-
return this
|
|
894
|
+
return this.#commandController.handleUsageCommand(reports);
|
|
898
895
|
}
|
|
899
896
|
|
|
900
897
|
async handleChangelogCommand(): Promise<void> {
|
|
901
|
-
await this
|
|
898
|
+
await this.#commandController.handleChangelogCommand();
|
|
902
899
|
}
|
|
903
900
|
|
|
904
901
|
handleHotkeysCommand(): void {
|
|
905
|
-
this
|
|
902
|
+
this.#commandController.handleHotkeysCommand();
|
|
906
903
|
}
|
|
907
904
|
|
|
908
905
|
handleClearCommand(): Promise<void> {
|
|
909
|
-
return this
|
|
906
|
+
return this.#commandController.handleClearCommand();
|
|
910
907
|
}
|
|
911
908
|
|
|
912
909
|
handleForkCommand(): Promise<void> {
|
|
913
|
-
return this
|
|
910
|
+
return this.#commandController.handleForkCommand();
|
|
914
911
|
}
|
|
915
912
|
|
|
916
913
|
showDebugSelector(): void {
|
|
917
|
-
this
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
handleArminSaysHi(): void {
|
|
921
|
-
this.commandController.handleArminSaysHi();
|
|
914
|
+
this.#selectorController.showDebugSelector();
|
|
922
915
|
}
|
|
923
916
|
|
|
924
917
|
handleBashCommand(command: string, excludeFromContext?: boolean): Promise<void> {
|
|
925
|
-
return this
|
|
918
|
+
return this.#commandController.handleBashCommand(command, excludeFromContext);
|
|
926
919
|
}
|
|
927
920
|
|
|
928
921
|
handlePythonCommand(code: string, excludeFromContext?: boolean): Promise<void> {
|
|
929
|
-
return this
|
|
922
|
+
return this.#commandController.handlePythonCommand(code, excludeFromContext);
|
|
930
923
|
}
|
|
931
924
|
|
|
932
925
|
handleCompactCommand(customInstructions?: string): Promise<void> {
|
|
933
|
-
return this
|
|
926
|
+
return this.#commandController.handleCompactCommand(customInstructions);
|
|
934
927
|
}
|
|
935
928
|
|
|
936
929
|
handleHandoffCommand(customInstructions?: string): Promise<void> {
|
|
937
|
-
return this
|
|
930
|
+
return this.#commandController.handleHandoffCommand(customInstructions);
|
|
938
931
|
}
|
|
939
932
|
|
|
940
933
|
executeCompaction(customInstructionsOrOptions?: string | CompactOptions, isAuto?: boolean): Promise<void> {
|
|
941
|
-
return this
|
|
934
|
+
return this.#commandController.executeCompaction(customInstructionsOrOptions, isAuto);
|
|
942
935
|
}
|
|
943
936
|
|
|
944
937
|
openInBrowser(urlOrPath: string): void {
|
|
945
|
-
this
|
|
938
|
+
this.#commandController.openInBrowser(urlOrPath);
|
|
946
939
|
}
|
|
947
940
|
|
|
948
941
|
// Selector handling
|
|
949
942
|
showSettingsSelector(): void {
|
|
950
|
-
this
|
|
943
|
+
this.#selectorController.showSettingsSelector();
|
|
951
944
|
}
|
|
952
945
|
|
|
953
946
|
showHistorySearch(): void {
|
|
954
|
-
this
|
|
947
|
+
this.#selectorController.showHistorySearch();
|
|
955
948
|
}
|
|
956
949
|
|
|
957
950
|
showExtensionsDashboard(): void {
|
|
958
|
-
void this
|
|
951
|
+
void this.#selectorController.showExtensionsDashboard();
|
|
959
952
|
}
|
|
960
953
|
|
|
961
954
|
showModelSelector(options?: { temporaryOnly?: boolean }): void {
|
|
962
|
-
this
|
|
955
|
+
this.#selectorController.showModelSelector(options);
|
|
963
956
|
}
|
|
964
957
|
|
|
965
958
|
showUserMessageSelector(): void {
|
|
966
|
-
this
|
|
959
|
+
this.#selectorController.showUserMessageSelector();
|
|
967
960
|
}
|
|
968
961
|
|
|
969
962
|
showTreeSelector(): void {
|
|
970
|
-
this
|
|
963
|
+
this.#selectorController.showTreeSelector();
|
|
971
964
|
}
|
|
972
965
|
|
|
973
966
|
showSessionSelector(): void {
|
|
974
|
-
this
|
|
967
|
+
this.#selectorController.showSessionSelector();
|
|
975
968
|
}
|
|
976
969
|
|
|
977
970
|
handleResumeSession(sessionPath: string): Promise<void> {
|
|
978
|
-
return this
|
|
971
|
+
return this.#selectorController.handleResumeSession(sessionPath);
|
|
979
972
|
}
|
|
980
973
|
|
|
981
974
|
showOAuthSelector(mode: "login" | "logout"): Promise<void> {
|
|
982
|
-
return this
|
|
975
|
+
return this.#selectorController.showOAuthSelector(mode);
|
|
983
976
|
}
|
|
984
977
|
|
|
985
978
|
showHookConfirm(title: string, message: string): Promise<boolean> {
|
|
986
|
-
return this
|
|
979
|
+
return this.#extensionUiController.showHookConfirm(title, message);
|
|
987
980
|
}
|
|
988
981
|
|
|
989
982
|
// Input handling
|
|
990
983
|
handleCtrlC(): void {
|
|
991
|
-
this
|
|
984
|
+
this.#inputController.handleCtrlC();
|
|
992
985
|
}
|
|
993
986
|
|
|
994
987
|
handleCtrlD(): void {
|
|
995
|
-
this
|
|
988
|
+
this.#inputController.handleCtrlD();
|
|
996
989
|
}
|
|
997
990
|
|
|
998
991
|
handleCtrlZ(): void {
|
|
999
|
-
this
|
|
992
|
+
this.#inputController.handleCtrlZ();
|
|
1000
993
|
}
|
|
1001
994
|
|
|
1002
995
|
handleDequeue(): void {
|
|
1003
|
-
this
|
|
996
|
+
this.#inputController.handleDequeue();
|
|
1004
997
|
}
|
|
1005
998
|
|
|
1006
999
|
handleBackgroundCommand(): void {
|
|
1007
|
-
this
|
|
1000
|
+
this.#inputController.handleBackgroundCommand();
|
|
1008
1001
|
}
|
|
1009
1002
|
|
|
1010
1003
|
handleImagePaste(): Promise<boolean> {
|
|
1011
|
-
return this
|
|
1004
|
+
return this.#inputController.handleImagePaste();
|
|
1012
1005
|
}
|
|
1013
1006
|
|
|
1014
1007
|
cycleThinkingLevel(): void {
|
|
1015
|
-
this
|
|
1008
|
+
this.#inputController.cycleThinkingLevel();
|
|
1016
1009
|
}
|
|
1017
1010
|
|
|
1018
1011
|
cycleRoleModel(options?: { temporary?: boolean }): Promise<void> {
|
|
1019
|
-
return this
|
|
1012
|
+
return this.#inputController.cycleRoleModel(options);
|
|
1020
1013
|
}
|
|
1021
1014
|
|
|
1022
1015
|
toggleToolOutputExpansion(): void {
|
|
1023
|
-
this
|
|
1016
|
+
this.#inputController.toggleToolOutputExpansion();
|
|
1024
1017
|
}
|
|
1025
1018
|
|
|
1026
1019
|
setToolsExpanded(expanded: boolean): void {
|
|
1027
|
-
this
|
|
1020
|
+
this.#inputController.setToolsExpanded(expanded);
|
|
1028
1021
|
}
|
|
1029
1022
|
|
|
1030
1023
|
toggleThinkingBlockVisibility(): void {
|
|
1031
|
-
this
|
|
1024
|
+
this.#inputController.toggleThinkingBlockVisibility();
|
|
1032
1025
|
}
|
|
1033
1026
|
|
|
1034
1027
|
toggleTodoExpansion(): void {
|
|
1035
1028
|
this.todoExpanded = !this.todoExpanded;
|
|
1036
|
-
this
|
|
1029
|
+
this.#renderTodoList();
|
|
1037
1030
|
this.ui.requestRender();
|
|
1038
1031
|
}
|
|
1039
1032
|
|
|
1040
1033
|
setTodos(todos: TodoItem[]): void {
|
|
1041
1034
|
this.todoItems = todos;
|
|
1042
|
-
this
|
|
1035
|
+
this.#renderTodoList();
|
|
1043
1036
|
this.ui.requestRender();
|
|
1044
1037
|
}
|
|
1045
1038
|
|
|
1046
1039
|
async reloadTodos(): Promise<void> {
|
|
1047
|
-
await this
|
|
1040
|
+
await this.#loadTodoList();
|
|
1048
1041
|
this.ui.requestRender();
|
|
1049
1042
|
}
|
|
1050
1043
|
|
|
1051
1044
|
openExternalEditor(): void {
|
|
1052
|
-
this
|
|
1045
|
+
this.#inputController.openExternalEditor();
|
|
1053
1046
|
}
|
|
1054
1047
|
|
|
1055
1048
|
registerExtensionShortcuts(): void {
|
|
1056
|
-
this
|
|
1049
|
+
this.#inputController.registerExtensionShortcuts();
|
|
1057
1050
|
}
|
|
1058
1051
|
|
|
1059
1052
|
// Hook UI methods
|
|
1060
1053
|
initHooksAndCustomTools(): Promise<void> {
|
|
1061
|
-
return this
|
|
1054
|
+
return this.#extensionUiController.initHooksAndCustomTools();
|
|
1062
1055
|
}
|
|
1063
1056
|
|
|
1064
1057
|
emitCustomToolSessionEvent(
|
|
1065
1058
|
reason: "start" | "switch" | "branch" | "tree" | "shutdown",
|
|
1066
1059
|
previousSessionFile?: string,
|
|
1067
1060
|
): Promise<void> {
|
|
1068
|
-
return this
|
|
1061
|
+
return this.#extensionUiController.emitCustomToolSessionEvent(reason, previousSessionFile);
|
|
1069
1062
|
}
|
|
1070
1063
|
|
|
1071
1064
|
setHookWidget(key: string, content: unknown): void {
|
|
1072
|
-
this
|
|
1065
|
+
this.#extensionUiController.setHookWidget(key, content);
|
|
1073
1066
|
}
|
|
1074
1067
|
|
|
1075
1068
|
setHookStatus(key: string, text: string | undefined): void {
|
|
1076
|
-
this
|
|
1069
|
+
this.#extensionUiController.setHookStatus(key, text);
|
|
1077
1070
|
}
|
|
1078
1071
|
|
|
1079
1072
|
showHookSelector(
|
|
@@ -1081,31 +1074,31 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1081
1074
|
options: string[],
|
|
1082
1075
|
dialogOptions?: ExtensionUIDialogOptions,
|
|
1083
1076
|
): Promise<string | undefined> {
|
|
1084
|
-
return this
|
|
1077
|
+
return this.#extensionUiController.showHookSelector(title, options, dialogOptions);
|
|
1085
1078
|
}
|
|
1086
1079
|
|
|
1087
1080
|
hideHookSelector(): void {
|
|
1088
|
-
this
|
|
1081
|
+
this.#extensionUiController.hideHookSelector();
|
|
1089
1082
|
}
|
|
1090
1083
|
|
|
1091
1084
|
showHookInput(title: string, placeholder?: string): Promise<string | undefined> {
|
|
1092
|
-
return this
|
|
1085
|
+
return this.#extensionUiController.showHookInput(title, placeholder);
|
|
1093
1086
|
}
|
|
1094
1087
|
|
|
1095
1088
|
hideHookInput(): void {
|
|
1096
|
-
this
|
|
1089
|
+
this.#extensionUiController.hideHookInput();
|
|
1097
1090
|
}
|
|
1098
1091
|
|
|
1099
1092
|
showHookEditor(title: string, prefill?: string): Promise<string | undefined> {
|
|
1100
|
-
return this
|
|
1093
|
+
return this.#extensionUiController.showHookEditor(title, prefill);
|
|
1101
1094
|
}
|
|
1102
1095
|
|
|
1103
1096
|
hideHookEditor(): void {
|
|
1104
|
-
this
|
|
1097
|
+
this.#extensionUiController.hideHookEditor();
|
|
1105
1098
|
}
|
|
1106
1099
|
|
|
1107
1100
|
showHookNotify(message: string, type?: "info" | "warning" | "error"): void {
|
|
1108
|
-
this
|
|
1101
|
+
this.#extensionUiController.showHookNotify(message, type);
|
|
1109
1102
|
}
|
|
1110
1103
|
|
|
1111
1104
|
showHookCustom<T>(
|
|
@@ -1116,18 +1109,18 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1116
1109
|
done: (result: T) => void,
|
|
1117
1110
|
) => (Component & { dispose?(): void }) | Promise<Component & { dispose?(): void }>,
|
|
1118
1111
|
): Promise<T> {
|
|
1119
|
-
return this
|
|
1112
|
+
return this.#extensionUiController.showHookCustom(factory);
|
|
1120
1113
|
}
|
|
1121
1114
|
|
|
1122
1115
|
showExtensionError(extensionPath: string, error: string): void {
|
|
1123
|
-
this
|
|
1116
|
+
this.#extensionUiController.showExtensionError(extensionPath, error);
|
|
1124
1117
|
}
|
|
1125
1118
|
|
|
1126
1119
|
showToolError(toolName: string, error: string): void {
|
|
1127
|
-
this
|
|
1120
|
+
this.#extensionUiController.showToolError(toolName, error);
|
|
1128
1121
|
}
|
|
1129
1122
|
|
|
1130
|
-
|
|
1131
|
-
this
|
|
1123
|
+
#subscribeToAgent(): void {
|
|
1124
|
+
this.#eventController.subscribeToAgent();
|
|
1132
1125
|
}
|
|
1133
1126
|
}
|