@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.
Files changed (122) hide show
  1. package/docs/tui.md +9 -9
  2. package/package.json +7 -7
  3. package/src/cli/file-processor.ts +8 -13
  4. package/src/cli/oclif-help.ts +1 -1
  5. package/src/cli.ts +14 -0
  6. package/src/commit/git/index.ts +16 -16
  7. package/src/config/keybindings.ts +11 -11
  8. package/src/config/model-registry.ts +31 -66
  9. package/src/config/settings.ts +88 -95
  10. package/src/config.ts +2 -2
  11. package/src/cursor.ts +4 -4
  12. package/src/debug/index.ts +28 -28
  13. package/src/discovery/codex.ts +5 -13
  14. package/src/discovery/cursor.ts +2 -7
  15. package/src/exa/mcp-client.ts +2 -2
  16. package/src/exa/websets.ts +2 -2
  17. package/src/export/html/index.ts +3 -3
  18. package/src/export/ttsr.ts +27 -27
  19. package/src/extensibility/custom-tools/loader.ts +9 -9
  20. package/src/extensibility/extensions/runner.ts +64 -64
  21. package/src/extensibility/hooks/runner.ts +46 -46
  22. package/src/extensibility/plugins/manager.ts +49 -49
  23. package/src/index.ts +0 -1
  24. package/src/internal-urls/router.ts +5 -5
  25. package/src/ipy/kernel.ts +61 -57
  26. package/src/lsp/client.ts +1 -1
  27. package/src/lsp/clients/biome-client.ts +2 -2
  28. package/src/lsp/clients/lsp-linter-client.ts +7 -7
  29. package/src/lsp/index.ts +9 -9
  30. package/src/mcp/manager.ts +47 -47
  31. package/src/mcp/tool-bridge.ts +12 -12
  32. package/src/mcp/transports/http.ts +34 -34
  33. package/src/mcp/transports/stdio.ts +47 -47
  34. package/src/modes/components/assistant-message.ts +25 -25
  35. package/src/modes/components/bash-execution.ts +51 -51
  36. package/src/modes/components/bordered-loader.ts +7 -7
  37. package/src/modes/components/branch-summary-message.ts +7 -7
  38. package/src/modes/components/compaction-summary-message.ts +7 -7
  39. package/src/modes/components/countdown-timer.ts +15 -15
  40. package/src/modes/components/custom-editor.ts +22 -22
  41. package/src/modes/components/custom-message.ts +21 -21
  42. package/src/modes/components/dynamic-border.ts +3 -3
  43. package/src/modes/components/extensions/extension-dashboard.ts +72 -72
  44. package/src/modes/components/extensions/extension-list.ts +99 -97
  45. package/src/modes/components/extensions/inspector-panel.ts +26 -26
  46. package/src/modes/components/footer.ts +36 -36
  47. package/src/modes/components/history-search.ts +52 -52
  48. package/src/modes/components/hook-editor.ts +20 -20
  49. package/src/modes/components/hook-input.ts +20 -20
  50. package/src/modes/components/hook-message.ts +22 -22
  51. package/src/modes/components/hook-selector.ts +52 -52
  52. package/src/modes/components/index.ts +0 -1
  53. package/src/modes/components/login-dialog.ts +57 -57
  54. package/src/modes/components/model-selector.ts +173 -173
  55. package/src/modes/components/oauth-selector.ts +45 -45
  56. package/src/modes/components/plugin-settings.ts +52 -52
  57. package/src/modes/components/python-execution.ts +53 -53
  58. package/src/modes/components/queue-mode-selector.ts +7 -7
  59. package/src/modes/components/read-tool-group.ts +23 -23
  60. package/src/modes/components/session-selector.ts +40 -37
  61. package/src/modes/components/settings-selector.ts +80 -80
  62. package/src/modes/components/show-images-selector.ts +7 -7
  63. package/src/modes/components/skill-message.ts +27 -27
  64. package/src/modes/components/status-line-segment-editor.ts +81 -81
  65. package/src/modes/components/status-line.ts +73 -73
  66. package/src/modes/components/theme-selector.ts +11 -11
  67. package/src/modes/components/thinking-selector.ts +7 -7
  68. package/src/modes/components/todo-display.ts +19 -19
  69. package/src/modes/components/todo-reminder.ts +9 -9
  70. package/src/modes/components/tool-execution.ts +204 -196
  71. package/src/modes/components/tree-selector.ts +144 -144
  72. package/src/modes/components/ttsr-notification.ts +17 -17
  73. package/src/modes/components/user-message-selector.ts +18 -18
  74. package/src/modes/components/welcome.ts +10 -10
  75. package/src/modes/controllers/command-controller.ts +0 -7
  76. package/src/modes/controllers/event-controller.ts +23 -23
  77. package/src/modes/controllers/extension-ui-controller.ts +13 -13
  78. package/src/modes/controllers/input-controller.ts +4 -9
  79. package/src/modes/interactive-mode.ts +234 -241
  80. package/src/modes/rpc/rpc-client.ts +77 -77
  81. package/src/modes/rpc/rpc-mode.ts +5 -5
  82. package/src/modes/theme/theme.ts +113 -113
  83. package/src/modes/types.ts +0 -1
  84. package/src/patch/index.ts +45 -45
  85. package/src/prompts/tools/task.md +22 -2
  86. package/src/session/agent-session.ts +463 -476
  87. package/src/session/agent-storage.ts +72 -75
  88. package/src/session/auth-storage.ts +186 -252
  89. package/src/session/history-storage.ts +36 -38
  90. package/src/session/session-manager.ts +300 -299
  91. package/src/session/session-storage.ts +65 -90
  92. package/src/ssh/connection-manager.ts +9 -9
  93. package/src/task/agents.ts +1 -1
  94. package/src/task/executor.ts +2 -2
  95. package/src/task/index.ts +13 -12
  96. package/src/task/subprocess-tool-registry.ts +5 -5
  97. package/src/tools/ask.ts +7 -7
  98. package/src/tools/bash.ts +8 -7
  99. package/src/tools/browser.ts +123 -123
  100. package/src/tools/calculator.ts +46 -46
  101. package/src/tools/context.ts +9 -9
  102. package/src/tools/exit-plan-mode.ts +5 -5
  103. package/src/tools/fetch.ts +5 -5
  104. package/src/tools/find.ts +16 -16
  105. package/src/tools/grep.ts +10 -10
  106. package/src/tools/notebook.ts +6 -6
  107. package/src/tools/output-meta.ts +10 -2
  108. package/src/tools/python.ts +12 -11
  109. package/src/tools/read.ts +17 -17
  110. package/src/tools/ssh.ts +9 -9
  111. package/src/tools/submit-result.ts +13 -13
  112. package/src/tools/todo-write.ts +6 -6
  113. package/src/tools/write.ts +10 -10
  114. package/src/tui/output-block.ts +6 -6
  115. package/src/tui/utils.ts +9 -9
  116. package/src/utils/event-bus.ts +10 -10
  117. package/src/utils/frontmatter.ts +1 -1
  118. package/src/utils/ignore-files.ts +1 -1
  119. package/src/web/search/index.ts +5 -5
  120. package/src/web/search/providers/anthropic.ts +7 -2
  121. package/examples/hooks/snake.ts +0 -342
  122. 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
- public session: AgentSession;
78
- public sessionManager: SessionManager;
79
- public settings: Settings;
80
- public keybindings: KeybindingsManager;
81
- public agent: AgentSession["agent"];
82
- public historyStorage?: HistoryStorage;
83
-
84
- public ui: TUI;
85
- public chatContainer: Container;
86
- public pendingMessagesContainer: Container;
87
- public statusContainer: Container;
88
- public todoContainer: Container;
89
- public editor: CustomEditor;
90
- public editorContainer: Container;
91
- public statusLine: StatusLineComponent;
92
-
93
- public isInitialized = false;
94
- public isBackgrounded = false;
95
- public isBashMode = false;
96
- public toolOutputExpanded = false;
97
- public todoExpanded = false;
98
- public planModeEnabled = false;
99
- public planModePaused = false;
100
- public planModePlanFilePath: string | undefined = undefined;
101
- public todoItems: TodoItem[] = [];
102
- public hideThinkingBlock = false;
103
- public pendingImages: ImageContent[] = [];
104
- public compactionQueuedMessages: CompactionQueuedMessage[] = [];
105
- public pendingTools = new Map<string, ToolExecutionHandle>();
106
- public pendingBashComponents: BashExecutionComponent[] = [];
107
- public bashComponent: BashExecutionComponent | undefined = undefined;
108
- public pendingPythonComponents: PythonExecutionComponent[] = [];
109
- public pythonComponent: PythonExecutionComponent | undefined = undefined;
110
- public isPythonMode = false;
111
- public streamingComponent: AssistantMessageComponent | undefined = undefined;
112
- public streamingMessage: AssistantMessage | undefined = undefined;
113
- public loadingAnimation: Loader | undefined = undefined;
114
- public autoCompactionLoader: Loader | undefined = undefined;
115
- public retryLoader: Loader | undefined = undefined;
116
- private pendingWorkingMessage: string | undefined;
117
- private readonly defaultWorkingMessage = `Working… (esc to interrupt)`;
118
- public autoCompactionEscapeHandler?: () => void;
119
- public retryEscapeHandler?: () => void;
120
- public unsubscribe?: () => void;
121
- public onInputCallback?: (input: { text: string; images?: ImageContent[] }) => void;
122
- public lastSigintTime = 0;
123
- public lastEscapeTime = 0;
124
- public shutdownRequested = false;
125
- private isShuttingDown = false;
126
- public hookSelector: HookSelectorComponent | undefined = undefined;
127
- public hookInput: HookInputComponent | undefined = undefined;
128
- public hookEditor: HookEditorComponent | undefined = undefined;
129
- public lastStatusSpacer: Spacer | undefined = undefined;
130
- public lastStatusText: Text | undefined = undefined;
131
- public fileSlashCommands: Set<string> = new Set();
132
- public skillCommands: Map<string, string> = new Map();
133
-
134
- private pendingSlashCommands: SlashCommand[] = [];
135
- private cleanupUnsubscribe?: () => void;
136
- private readonly version: string;
137
- private readonly changelogMarkdown: string | undefined;
138
- private planModePreviousTools: string[] | undefined;
139
- private planModePreviousModel: Model | undefined;
140
- private pendingModelSwitch: Model | undefined;
141
- private planModeHasEntered = false;
142
- public readonly lspServers:
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
- public mcpManager?: import("../mcp").MCPManager;
146
- private readonly toolUiContextSetter: (uiContext: ExtensionUIContext, hasUI: boolean) => void;
145
+ mcpManager?: import("../mcp").MCPManager;
146
+ readonly #toolUiContextSetter: (uiContext: ExtensionUIContext, hasUI: boolean) => void;
147
147
 
148
- private readonly commandController: CommandController;
149
- private readonly eventController: EventController;
150
- private readonly extensionUiController: ExtensionUiController;
151
- private readonly inputController: InputController;
152
- private readonly selectorController: SelectorController;
153
- private readonly uiHelpers: UiHelpers;
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.version = version;
171
- this.changelogMarkdown = changelogMarkdown;
172
- this.toolUiContextSetter = setToolUIContext;
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.pendingSlashCommands = [...BUILTIN_SLASH_COMMANDS, ...hookCommands, ...customCommands, ...skillCommandList];
230
+ this.#pendingSlashCommands = [...BUILTIN_SLASH_COMMANDS, ...hookCommands, ...customCommands, ...skillCommandList];
231
231
 
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);
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.cleanupUnsubscribe = postmortem.register("session-manager-flush", () => this.sessionManager.flush());
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.pendingSlashCommands, ...fileSlashCommands],
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.version, modelName, providerName, recentSessions, lspServerInfo);
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.changelogMarkdown) {
300
+ if (this.#changelogMarkdown) {
301
301
  this.ui.addChild(new DynamicBorder());
302
302
  if (settings.get("collapseChangelog")) {
303
- const versionMatch = this.changelogMarkdown.match(/##\s+\[?(\d+\.\d+\.\d+)\]?/);
304
- const latestVersion = versionMatch ? versionMatch[1] : this.version;
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.changelogMarkdown.trim(), 1, 0, getMarkdownTheme()));
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.inputController.setupKeyHandlers();
333
- this.inputController.setupEditorSubmitHandler();
332
+ this.#inputController.setupKeyHandlers();
333
+ this.#inputController.setupEditorSubmitHandler();
334
334
 
335
335
  // Load initial todos
336
- await this.loadTodoList();
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.restoreModeFromSession();
350
+ await this.#restoreModeFromSession();
351
351
 
352
352
  // Subscribe to agent events
353
- this.subscribeToAgent();
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
- private formatTodoLine(todo: TodoItem, prefix: string): string {
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
- private getCollapsedTodos(todos: TodoItem[]): TodoItem[] {
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
- private renderTodoList(): void {
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.getCollapsedTodos(this.todoItems);
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.formatTodoLine(todo, prefix));
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
- private async loadTodoList(): Promise<void> {
454
+ async #loadTodoList(): Promise<void> {
455
455
  const sessionFile = this.sessionManager.getSessionFile() ?? null;
456
456
  if (!sessionFile) {
457
457
  this.todoItems = [];
458
- this.renderTodoList();
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.renderTodoList();
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.renderTodoList();
478
+ this.#renderTodoList();
479
479
  }
480
480
 
481
- private getPlanFilePath(): string {
481
+ #getPlanFilePath(): string {
482
482
  const sessionId = this.sessionManager.getSessionId();
483
483
  return `plan://${sessionId}/plan.md`;
484
484
  }
485
485
 
486
- private resolvePlanFilePath(planFilePath: string): string {
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
- private updatePlanModeStatus(): void {
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
- private async applyPlanModeModel(): Promise<void> {
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.planModePreviousModel = currentModel;
516
+ this.#planModePreviousModel = currentModel;
517
517
  if (this.session.isStreaming) {
518
- this.pendingModelSwitch = planModel;
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.pendingModelSwitch;
532
+ const model = this.#pendingModelSwitch;
533
533
  if (!model) return;
534
- this.pendingModelSwitch = undefined;
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
- private async restoreModeFromSession(): Promise<void> {
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.enterPlanMode({ planFilePath });
549
+ await this.#enterPlanMode({ planFilePath });
550
550
  } else if (sessionContext.mode === "plan_paused") {
551
551
  this.planModePaused = true;
552
- this.planModeHasEntered = true;
553
- this.updatePlanModeStatus();
552
+ this.#planModeHasEntered = true;
553
+ this.#updatePlanModeStatus();
554
554
  }
555
555
  }
556
556
 
557
- private async enterPlanMode(options?: {
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.getPlanFilePath();
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.planModePreviousTools = previousTools;
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.planModeHasEntered,
579
+ reentry: this.#planModeHasEntered,
583
580
  });
584
581
  if (this.session.isStreaming) {
585
582
  await this.session.sendPlanModeContext({ deliverAs: "steer" });
586
583
  }
587
- this.planModeHasEntered = true;
588
- await this.applyPlanModeModel();
589
- this.updatePlanModeStatus();
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
- private async exitPlanMode(options?: { silent?: boolean; paused?: boolean }): Promise<void> {
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.planModePreviousTools;
596
+ const previousTools = this.#planModePreviousTools;
600
597
  if (previousTools && previousTools.length > 0) {
601
598
  await this.session.setActiveToolsByName(previousTools);
602
599
  }
603
- if (this.planModePreviousModel) {
600
+ if (this.#planModePreviousModel) {
604
601
  if (this.session.isStreaming) {
605
- this.pendingModelSwitch = this.planModePreviousModel;
602
+ this.#pendingModelSwitch = this.#planModePreviousModel;
606
603
  } else {
607
- await this.session.setModelTemporary(this.planModePreviousModel);
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.planModePreviousTools = undefined;
616
- this.planModePreviousModel = undefined;
617
- this.updatePlanModeStatus();
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
- private async readPlanFile(planFilePath: string): Promise<string | null> {
626
- const resolvedPath = this.resolvePlanFilePath(planFilePath);
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
- private renderPlanPreview(planContent: string): void {
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
- private async approvePlan(planContent: string): Promise<void> {
648
- const previousTools = this.planModePreviousTools ?? this.session.getActiveToolNames();
649
- await this.exitPlanMode({ silent: true, paused: false });
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.exitPlanMode({ paused: true });
663
+ await this.#exitPlanMode({ paused: true });
667
664
  return;
668
665
  }
669
- await this.enterPlanMode();
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.getPlanFilePath();
675
+ const planFilePath = details.planFilePath || this.planModePlanFilePath || this.#getPlanFilePath();
679
676
  this.planModePlanFilePath = planFilePath;
680
- const planContent = await this.readPlanFile(planFilePath);
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.renderPlanPreview(planContent);
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.approvePlan(planContent);
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.cleanupUnsubscribe) {
715
- this.cleanupUnsubscribe();
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.isShuttingDown) return;
725
- this.isShuttingDown = true;
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.toolUiContextSetter(uiContext, hasUI);
761
+ this.#toolUiContextSetter(uiContext, hasUI);
765
762
  }
766
763
 
767
764
  initializeHookRunner(uiContext: ExtensionUIContext, hasUI: boolean): void {
768
- this.extensionUiController.initializeHookRunner(uiContext, hasUI);
765
+ this.#extensionUiController.initializeHookRunner(uiContext, hasUI);
769
766
  }
770
767
 
771
768
  createBackgroundUiContext(): ExtensionUIContext {
772
- return this.extensionUiController.createBackgroundUiContext();
769
+ return this.#extensionUiController.createBackgroundUiContext();
773
770
  }
774
771
 
775
772
  // Event handling
776
773
  async handleBackgroundEvent(event: AgentSessionEvent): Promise<void> {
777
- await this.eventController.handleBackgroundEvent(event);
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.uiHelpers.showStatus(message, options);
779
+ this.#uiHelpers.showStatus(message, options);
783
780
  }
784
781
 
785
782
  showError(message: string): void {
786
- this.uiHelpers.showError(message);
783
+ this.#uiHelpers.showError(message);
787
784
  }
788
785
 
789
786
  showWarning(message: string): void {
790
- this.uiHelpers.showWarning(message);
787
+ this.#uiHelpers.showWarning(message);
791
788
  }
792
789
 
793
790
  setWorkingMessage(message?: string): void {
794
791
  if (message === undefined) {
795
- this.pendingWorkingMessage = undefined;
792
+ this.#pendingWorkingMessage = undefined;
796
793
  if (this.loadingAnimation) {
797
- this.loadingAnimation.setMessage(this.defaultWorkingMessage);
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.pendingWorkingMessage = message;
804
+ this.#pendingWorkingMessage = message;
808
805
  }
809
806
 
810
807
  applyPendingWorkingMessage(): void {
811
- if (this.pendingWorkingMessage === undefined) {
808
+ if (this.#pendingWorkingMessage === undefined) {
812
809
  return;
813
810
  }
814
811
 
815
- const message = this.pendingWorkingMessage;
816
- this.pendingWorkingMessage = undefined;
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.uiHelpers.showNewVersionNotification(newVersion);
818
+ this.#uiHelpers.showNewVersionNotification(newVersion);
822
819
  }
823
820
 
824
821
  clearEditor(): void {
825
- this.uiHelpers.clearEditor();
822
+ this.#uiHelpers.clearEditor();
826
823
  }
827
824
 
828
825
  updatePendingMessagesDisplay(): void {
829
- this.uiHelpers.updatePendingMessagesDisplay();
826
+ this.#uiHelpers.updatePendingMessagesDisplay();
830
827
  }
831
828
 
832
829
  queueCompactionMessage(text: string, mode: "steer" | "followUp"): void {
833
- this.uiHelpers.queueCompactionMessage(text, mode);
830
+ this.#uiHelpers.queueCompactionMessage(text, mode);
834
831
  }
835
832
 
836
833
  flushCompactionQueue(options?: { willRetry?: boolean }): Promise<void> {
837
- return this.uiHelpers.flushCompactionQueue(options);
834
+ return this.#uiHelpers.flushCompactionQueue(options);
838
835
  }
839
836
 
840
837
  flushPendingBashComponents(): void {
841
- this.uiHelpers.flushPendingBashComponents();
838
+ this.#uiHelpers.flushPendingBashComponents();
842
839
  }
843
840
 
844
841
  isKnownSlashCommand(text: string): boolean {
845
- return this.uiHelpers.isKnownSlashCommand(text);
842
+ return this.#uiHelpers.isKnownSlashCommand(text);
846
843
  }
847
844
 
848
845
  addMessageToChat(message: AgentMessage, options?: { populateHistory?: boolean }): void {
849
- this.uiHelpers.addMessageToChat(message, options);
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.uiHelpers.renderSessionContext(sessionContext, options);
853
+ this.#uiHelpers.renderSessionContext(sessionContext, options);
857
854
  }
858
855
 
859
856
  renderInitialMessages(): void {
860
- this.uiHelpers.renderInitialMessages();
857
+ this.#uiHelpers.renderInitialMessages();
861
858
  }
862
859
 
863
860
  getUserMessageText(message: Message): string {
864
- return this.uiHelpers.getUserMessageText(message);
861
+ return this.#uiHelpers.getUserMessageText(message);
865
862
  }
866
863
 
867
864
  findLastAssistantMessage(): AssistantMessage | undefined {
868
- return this.uiHelpers.findLastAssistantMessage();
865
+ return this.#uiHelpers.findLastAssistantMessage();
869
866
  }
870
867
 
871
868
  extractAssistantText(message: AssistantMessage): string {
872
- return this.uiHelpers.extractAssistantText(message);
869
+ return this.#uiHelpers.extractAssistantText(message);
873
870
  }
874
871
 
875
872
  // Command handling
876
873
  handleExportCommand(text: string): Promise<void> {
877
- return this.commandController.handleExportCommand(text);
874
+ return this.#commandController.handleExportCommand(text);
878
875
  }
879
876
 
880
877
  handleDumpCommand(): Promise<void> {
881
- return this.commandController.handleDumpCommand();
878
+ return this.#commandController.handleDumpCommand();
882
879
  }
883
880
 
884
881
  handleShareCommand(): Promise<void> {
885
- return this.commandController.handleShareCommand();
882
+ return this.#commandController.handleShareCommand();
886
883
  }
887
884
 
888
885
  handleCopyCommand(): Promise<void> {
889
- return this.commandController.handleCopyCommand();
886
+ return this.#commandController.handleCopyCommand();
890
887
  }
891
888
 
892
889
  handleSessionCommand(): Promise<void> {
893
- return this.commandController.handleSessionCommand();
890
+ return this.#commandController.handleSessionCommand();
894
891
  }
895
892
 
896
893
  handleUsageCommand(reports?: UsageReport[] | null): Promise<void> {
897
- return this.commandController.handleUsageCommand(reports);
894
+ return this.#commandController.handleUsageCommand(reports);
898
895
  }
899
896
 
900
897
  async handleChangelogCommand(): Promise<void> {
901
- await this.commandController.handleChangelogCommand();
898
+ await this.#commandController.handleChangelogCommand();
902
899
  }
903
900
 
904
901
  handleHotkeysCommand(): void {
905
- this.commandController.handleHotkeysCommand();
902
+ this.#commandController.handleHotkeysCommand();
906
903
  }
907
904
 
908
905
  handleClearCommand(): Promise<void> {
909
- return this.commandController.handleClearCommand();
906
+ return this.#commandController.handleClearCommand();
910
907
  }
911
908
 
912
909
  handleForkCommand(): Promise<void> {
913
- return this.commandController.handleForkCommand();
910
+ return this.#commandController.handleForkCommand();
914
911
  }
915
912
 
916
913
  showDebugSelector(): void {
917
- this.selectorController.showDebugSelector();
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.commandController.handleBashCommand(command, excludeFromContext);
918
+ return this.#commandController.handleBashCommand(command, excludeFromContext);
926
919
  }
927
920
 
928
921
  handlePythonCommand(code: string, excludeFromContext?: boolean): Promise<void> {
929
- return this.commandController.handlePythonCommand(code, excludeFromContext);
922
+ return this.#commandController.handlePythonCommand(code, excludeFromContext);
930
923
  }
931
924
 
932
925
  handleCompactCommand(customInstructions?: string): Promise<void> {
933
- return this.commandController.handleCompactCommand(customInstructions);
926
+ return this.#commandController.handleCompactCommand(customInstructions);
934
927
  }
935
928
 
936
929
  handleHandoffCommand(customInstructions?: string): Promise<void> {
937
- return this.commandController.handleHandoffCommand(customInstructions);
930
+ return this.#commandController.handleHandoffCommand(customInstructions);
938
931
  }
939
932
 
940
933
  executeCompaction(customInstructionsOrOptions?: string | CompactOptions, isAuto?: boolean): Promise<void> {
941
- return this.commandController.executeCompaction(customInstructionsOrOptions, isAuto);
934
+ return this.#commandController.executeCompaction(customInstructionsOrOptions, isAuto);
942
935
  }
943
936
 
944
937
  openInBrowser(urlOrPath: string): void {
945
- this.commandController.openInBrowser(urlOrPath);
938
+ this.#commandController.openInBrowser(urlOrPath);
946
939
  }
947
940
 
948
941
  // Selector handling
949
942
  showSettingsSelector(): void {
950
- this.selectorController.showSettingsSelector();
943
+ this.#selectorController.showSettingsSelector();
951
944
  }
952
945
 
953
946
  showHistorySearch(): void {
954
- this.selectorController.showHistorySearch();
947
+ this.#selectorController.showHistorySearch();
955
948
  }
956
949
 
957
950
  showExtensionsDashboard(): void {
958
- void this.selectorController.showExtensionsDashboard();
951
+ void this.#selectorController.showExtensionsDashboard();
959
952
  }
960
953
 
961
954
  showModelSelector(options?: { temporaryOnly?: boolean }): void {
962
- this.selectorController.showModelSelector(options);
955
+ this.#selectorController.showModelSelector(options);
963
956
  }
964
957
 
965
958
  showUserMessageSelector(): void {
966
- this.selectorController.showUserMessageSelector();
959
+ this.#selectorController.showUserMessageSelector();
967
960
  }
968
961
 
969
962
  showTreeSelector(): void {
970
- this.selectorController.showTreeSelector();
963
+ this.#selectorController.showTreeSelector();
971
964
  }
972
965
 
973
966
  showSessionSelector(): void {
974
- this.selectorController.showSessionSelector();
967
+ this.#selectorController.showSessionSelector();
975
968
  }
976
969
 
977
970
  handleResumeSession(sessionPath: string): Promise<void> {
978
- return this.selectorController.handleResumeSession(sessionPath);
971
+ return this.#selectorController.handleResumeSession(sessionPath);
979
972
  }
980
973
 
981
974
  showOAuthSelector(mode: "login" | "logout"): Promise<void> {
982
- return this.selectorController.showOAuthSelector(mode);
975
+ return this.#selectorController.showOAuthSelector(mode);
983
976
  }
984
977
 
985
978
  showHookConfirm(title: string, message: string): Promise<boolean> {
986
- return this.extensionUiController.showHookConfirm(title, message);
979
+ return this.#extensionUiController.showHookConfirm(title, message);
987
980
  }
988
981
 
989
982
  // Input handling
990
983
  handleCtrlC(): void {
991
- this.inputController.handleCtrlC();
984
+ this.#inputController.handleCtrlC();
992
985
  }
993
986
 
994
987
  handleCtrlD(): void {
995
- this.inputController.handleCtrlD();
988
+ this.#inputController.handleCtrlD();
996
989
  }
997
990
 
998
991
  handleCtrlZ(): void {
999
- this.inputController.handleCtrlZ();
992
+ this.#inputController.handleCtrlZ();
1000
993
  }
1001
994
 
1002
995
  handleDequeue(): void {
1003
- this.inputController.handleDequeue();
996
+ this.#inputController.handleDequeue();
1004
997
  }
1005
998
 
1006
999
  handleBackgroundCommand(): void {
1007
- this.inputController.handleBackgroundCommand();
1000
+ this.#inputController.handleBackgroundCommand();
1008
1001
  }
1009
1002
 
1010
1003
  handleImagePaste(): Promise<boolean> {
1011
- return this.inputController.handleImagePaste();
1004
+ return this.#inputController.handleImagePaste();
1012
1005
  }
1013
1006
 
1014
1007
  cycleThinkingLevel(): void {
1015
- this.inputController.cycleThinkingLevel();
1008
+ this.#inputController.cycleThinkingLevel();
1016
1009
  }
1017
1010
 
1018
1011
  cycleRoleModel(options?: { temporary?: boolean }): Promise<void> {
1019
- return this.inputController.cycleRoleModel(options);
1012
+ return this.#inputController.cycleRoleModel(options);
1020
1013
  }
1021
1014
 
1022
1015
  toggleToolOutputExpansion(): void {
1023
- this.inputController.toggleToolOutputExpansion();
1016
+ this.#inputController.toggleToolOutputExpansion();
1024
1017
  }
1025
1018
 
1026
1019
  setToolsExpanded(expanded: boolean): void {
1027
- this.inputController.setToolsExpanded(expanded);
1020
+ this.#inputController.setToolsExpanded(expanded);
1028
1021
  }
1029
1022
 
1030
1023
  toggleThinkingBlockVisibility(): void {
1031
- this.inputController.toggleThinkingBlockVisibility();
1024
+ this.#inputController.toggleThinkingBlockVisibility();
1032
1025
  }
1033
1026
 
1034
1027
  toggleTodoExpansion(): void {
1035
1028
  this.todoExpanded = !this.todoExpanded;
1036
- this.renderTodoList();
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.renderTodoList();
1035
+ this.#renderTodoList();
1043
1036
  this.ui.requestRender();
1044
1037
  }
1045
1038
 
1046
1039
  async reloadTodos(): Promise<void> {
1047
- await this.loadTodoList();
1040
+ await this.#loadTodoList();
1048
1041
  this.ui.requestRender();
1049
1042
  }
1050
1043
 
1051
1044
  openExternalEditor(): void {
1052
- this.inputController.openExternalEditor();
1045
+ this.#inputController.openExternalEditor();
1053
1046
  }
1054
1047
 
1055
1048
  registerExtensionShortcuts(): void {
1056
- this.inputController.registerExtensionShortcuts();
1049
+ this.#inputController.registerExtensionShortcuts();
1057
1050
  }
1058
1051
 
1059
1052
  // Hook UI methods
1060
1053
  initHooksAndCustomTools(): Promise<void> {
1061
- return this.extensionUiController.initHooksAndCustomTools();
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.extensionUiController.emitCustomToolSessionEvent(reason, previousSessionFile);
1061
+ return this.#extensionUiController.emitCustomToolSessionEvent(reason, previousSessionFile);
1069
1062
  }
1070
1063
 
1071
1064
  setHookWidget(key: string, content: unknown): void {
1072
- this.extensionUiController.setHookWidget(key, content);
1065
+ this.#extensionUiController.setHookWidget(key, content);
1073
1066
  }
1074
1067
 
1075
1068
  setHookStatus(key: string, text: string | undefined): void {
1076
- this.extensionUiController.setHookStatus(key, text);
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.extensionUiController.showHookSelector(title, options, dialogOptions);
1077
+ return this.#extensionUiController.showHookSelector(title, options, dialogOptions);
1085
1078
  }
1086
1079
 
1087
1080
  hideHookSelector(): void {
1088
- this.extensionUiController.hideHookSelector();
1081
+ this.#extensionUiController.hideHookSelector();
1089
1082
  }
1090
1083
 
1091
1084
  showHookInput(title: string, placeholder?: string): Promise<string | undefined> {
1092
- return this.extensionUiController.showHookInput(title, placeholder);
1085
+ return this.#extensionUiController.showHookInput(title, placeholder);
1093
1086
  }
1094
1087
 
1095
1088
  hideHookInput(): void {
1096
- this.extensionUiController.hideHookInput();
1089
+ this.#extensionUiController.hideHookInput();
1097
1090
  }
1098
1091
 
1099
1092
  showHookEditor(title: string, prefill?: string): Promise<string | undefined> {
1100
- return this.extensionUiController.showHookEditor(title, prefill);
1093
+ return this.#extensionUiController.showHookEditor(title, prefill);
1101
1094
  }
1102
1095
 
1103
1096
  hideHookEditor(): void {
1104
- this.extensionUiController.hideHookEditor();
1097
+ this.#extensionUiController.hideHookEditor();
1105
1098
  }
1106
1099
 
1107
1100
  showHookNotify(message: string, type?: "info" | "warning" | "error"): void {
1108
- this.extensionUiController.showHookNotify(message, type);
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.extensionUiController.showHookCustom(factory);
1112
+ return this.#extensionUiController.showHookCustom(factory);
1120
1113
  }
1121
1114
 
1122
1115
  showExtensionError(extensionPath: string, error: string): void {
1123
- this.extensionUiController.showExtensionError(extensionPath, error);
1116
+ this.#extensionUiController.showExtensionError(extensionPath, error);
1124
1117
  }
1125
1118
 
1126
1119
  showToolError(toolName: string, error: string): void {
1127
- this.extensionUiController.showToolError(toolName, error);
1120
+ this.#extensionUiController.showToolError(toolName, error);
1128
1121
  }
1129
1122
 
1130
- private subscribeToAgent(): void {
1131
- this.eventController.subscribeToAgent();
1123
+ #subscribeToAgent(): void {
1124
+ this.#eventController.subscribeToAgent();
1132
1125
  }
1133
1126
  }