@earendil-works/pi-coding-agent 0.84.0 → 0.84.2

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 (169) hide show
  1. package/CHANGELOG.md +87 -2
  2. package/README.md +1 -0
  3. package/dist/cli/args.d.ts +1 -0
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +14 -3
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/cli/auth-check.d.ts +19 -0
  8. package/dist/cli/auth-check.d.ts.map +1 -0
  9. package/dist/cli/auth-check.js +50 -0
  10. package/dist/cli/auth-check.js.map +1 -0
  11. package/dist/cli/auth-command.d.ts +24 -0
  12. package/dist/cli/auth-command.d.ts.map +1 -0
  13. package/dist/cli/auth-command.js +103 -0
  14. package/dist/cli/auth-command.js.map +1 -0
  15. package/dist/cli/credential-print.d.ts +4 -14
  16. package/dist/cli/credential-print.d.ts.map +1 -1
  17. package/dist/cli/credential-print.js +38 -79
  18. package/dist/cli/credential-print.js.map +1 -1
  19. package/dist/core/agent-session.d.ts +7 -2
  20. package/dist/core/agent-session.d.ts.map +1 -1
  21. package/dist/core/agent-session.js +6 -6
  22. package/dist/core/agent-session.js.map +1 -1
  23. package/dist/core/auth-storage.d.ts +10 -0
  24. package/dist/core/auth-storage.d.ts.map +1 -1
  25. package/dist/core/auth-storage.js +78 -1
  26. package/dist/core/auth-storage.js.map +1 -1
  27. package/dist/core/experimental.d.ts +4 -0
  28. package/dist/core/experimental.d.ts.map +1 -1
  29. package/dist/core/experimental.js +4 -0
  30. package/dist/core/experimental.js.map +1 -1
  31. package/dist/core/extensions/types.d.ts +9 -0
  32. package/dist/core/extensions/types.d.ts.map +1 -1
  33. package/dist/core/extensions/types.js.map +1 -1
  34. package/dist/core/keybindings.d.ts +32 -0
  35. package/dist/core/keybindings.d.ts.map +1 -1
  36. package/dist/core/model-config.d.ts +5 -0
  37. package/dist/core/model-config.d.ts.map +1 -1
  38. package/dist/core/model-config.js +1 -0
  39. package/dist/core/model-config.js.map +1 -1
  40. package/dist/core/model-resolver.d.ts.map +1 -1
  41. package/dist/core/model-resolver.js +1 -0
  42. package/dist/core/model-resolver.js.map +1 -1
  43. package/dist/core/model-runtime.d.ts +2 -0
  44. package/dist/core/model-runtime.d.ts.map +1 -1
  45. package/dist/core/model-runtime.js +3 -1
  46. package/dist/core/model-runtime.js.map +1 -1
  47. package/dist/core/project-trust.d.ts.map +1 -1
  48. package/dist/core/project-trust.js +2 -2
  49. package/dist/core/project-trust.js.map +1 -1
  50. package/dist/core/sdk.d.ts +5 -3
  51. package/dist/core/sdk.d.ts.map +1 -1
  52. package/dist/core/sdk.js +2 -1
  53. package/dist/core/sdk.js.map +1 -1
  54. package/dist/core/session-manager.d.ts.map +1 -1
  55. package/dist/core/session-manager.js +2 -2
  56. package/dist/core/session-manager.js.map +1 -1
  57. package/dist/core/settings-manager.d.ts +6 -0
  58. package/dist/core/settings-manager.d.ts.map +1 -1
  59. package/dist/core/settings-manager.js +12 -0
  60. package/dist/core/settings-manager.js.map +1 -1
  61. package/dist/core/system-prompt.d.ts.map +1 -1
  62. package/dist/core/system-prompt.js +1 -1
  63. package/dist/core/system-prompt.js.map +1 -1
  64. package/dist/core/tools/bash.d.ts +4 -0
  65. package/dist/core/tools/bash.d.ts.map +1 -1
  66. package/dist/core/tools/bash.js +8 -4
  67. package/dist/core/tools/bash.js.map +1 -1
  68. package/dist/core/tools/edit.d.ts +4 -0
  69. package/dist/core/tools/edit.d.ts.map +1 -1
  70. package/dist/core/tools/edit.js +13 -7
  71. package/dist/core/tools/edit.js.map +1 -1
  72. package/dist/core/tools/find.d.ts +4 -0
  73. package/dist/core/tools/find.d.ts.map +1 -1
  74. package/dist/core/tools/find.js +6 -2
  75. package/dist/core/tools/find.js.map +1 -1
  76. package/dist/core/tools/grep.d.ts +4 -0
  77. package/dist/core/tools/grep.d.ts.map +1 -1
  78. package/dist/core/tools/grep.js +6 -2
  79. package/dist/core/tools/grep.js.map +1 -1
  80. package/dist/core/tools/ls.d.ts +4 -0
  81. package/dist/core/tools/ls.d.ts.map +1 -1
  82. package/dist/core/tools/ls.js +5 -1
  83. package/dist/core/tools/ls.js.map +1 -1
  84. package/dist/core/tools/read.d.ts +4 -0
  85. package/dist/core/tools/read.d.ts.map +1 -1
  86. package/dist/core/tools/read.js +8 -2
  87. package/dist/core/tools/read.js.map +1 -1
  88. package/dist/core/tools/write.d.ts +4 -0
  89. package/dist/core/tools/write.d.ts.map +1 -1
  90. package/dist/core/tools/write.js +8 -2
  91. package/dist/core/tools/write.js.map +1 -1
  92. package/dist/index.d.ts +1 -1
  93. package/dist/index.d.ts.map +1 -1
  94. package/dist/index.js.map +1 -1
  95. package/dist/main.d.ts.map +1 -1
  96. package/dist/main.js +62 -22
  97. package/dist/main.js.map +1 -1
  98. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  99. package/dist/modes/interactive/components/model-selector.js +2 -1
  100. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  101. package/dist/modes/interactive/components/settings-selector.d.ts +3 -1
  102. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  103. package/dist/modes/interactive/components/settings-selector.js +10 -0
  104. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  105. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  106. package/dist/modes/interactive/components/tool-execution.js +10 -1
  107. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  108. package/dist/modes/interactive/interactive-mode.d.ts +11 -2
  109. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  110. package/dist/modes/interactive/interactive-mode.js +113 -31
  111. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  112. package/dist/modes/interactive/model-catalog-refresh.d.ts +7 -0
  113. package/dist/modes/interactive/model-catalog-refresh.d.ts.map +1 -0
  114. package/dist/modes/interactive/model-catalog-refresh.js +34 -0
  115. package/dist/modes/interactive/model-catalog-refresh.js.map +1 -0
  116. package/dist/modes/interactive/theme/dark.json +2 -0
  117. package/dist/modes/interactive/theme/light.json +2 -0
  118. package/dist/modes/interactive/theme/theme-controller.d.ts +10 -2
  119. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
  120. package/dist/modes/interactive/theme/theme-controller.js +25 -11
  121. package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
  122. package/dist/modes/interactive/theme/theme-schema.json +9 -1
  123. package/dist/modes/interactive/theme/theme.d.ts +5 -3
  124. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  125. package/dist/modes/interactive/theme/theme.js +23 -5
  126. package/dist/modes/interactive/theme/theme.js.map +1 -1
  127. package/dist/modes/json-event.d.ts +4 -1
  128. package/dist/modes/json-event.d.ts.map +1 -1
  129. package/dist/modes/json-event.js +5 -2
  130. package/dist/modes/json-event.js.map +1 -1
  131. package/dist/package-manager-cli.d.ts.map +1 -1
  132. package/dist/package-manager-cli.js +1 -1
  133. package/dist/package-manager-cli.js.map +1 -1
  134. package/dist/server/create-harness.d.ts +26 -0
  135. package/dist/server/create-harness.d.ts.map +1 -0
  136. package/dist/server/create-harness.js +106 -0
  137. package/dist/server/create-harness.js.map +1 -0
  138. package/dist/utils/tools-manager.d.ts +10 -1
  139. package/dist/utils/tools-manager.d.ts.map +1 -1
  140. package/dist/utils/tools-manager.js +14 -19
  141. package/dist/utils/tools-manager.js.map +1 -1
  142. package/docs/compaction.md +6 -6
  143. package/docs/custom-provider.md +1 -1
  144. package/docs/environment-variables.md +8 -2
  145. package/docs/extensions.md +7 -2
  146. package/docs/json.md +6 -4
  147. package/docs/keybindings.md +16 -6
  148. package/docs/providers.md +8 -1
  149. package/docs/rpc.md +15 -4
  150. package/docs/sdk.md +9 -0
  151. package/docs/settings.md +31 -0
  152. package/docs/terminal-setup.md +23 -2
  153. package/docs/themes.md +24 -3
  154. package/docs/tui.md +2 -2
  155. package/docs/usage.md +3 -2
  156. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  157. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  158. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  159. package/examples/extensions/gondolin/package-lock.json +2 -2
  160. package/examples/extensions/gondolin/package.json +1 -1
  161. package/examples/extensions/sandbox/package-lock.json +2 -2
  162. package/examples/extensions/sandbox/package.json +1 -1
  163. package/examples/extensions/subagent/README.md +2 -0
  164. package/examples/extensions/subagent/agents.ts +40 -9
  165. package/examples/extensions/subagent/index.ts +21 -3
  166. package/examples/extensions/with-deps/package-lock.json +2 -2
  167. package/examples/extensions/with-deps/package.json +1 -1
  168. package/npm-shrinkwrap.json +27 -78
  169. package/package.json +7 -7
@@ -69,6 +69,7 @@ import { TrustSelectorComponent } from "./components/trust-selector.js";
69
69
  import { UserMessageComponent } from "./components/user-message.js";
70
70
  import { UserMessageSelectorComponent } from "./components/user-message-selector.js";
71
71
  import { editInExternalEditor } from "./external-editor.js";
72
+ import { refreshModelCatalogs } from "./model-catalog-refresh.js";
72
73
  import { getModelSearchText } from "./model-search.js";
73
74
  import { getAvailableThemes, getAvailableThemesWithPaths, getEditorTheme, getMarkdownTheme, getThemeByName, onThemeChange, setRegisteredThemes, stopThemeWatcher, Theme, theme, } from "./theme/theme.js";
74
75
  import { InteractiveThemeController } from "./theme/theme-controller.js";
@@ -169,7 +170,22 @@ function formatLoginProviderCompletionDescription(provider) {
169
170
  export function createInteractiveTui(options) {
170
171
  const terminal = options.terminal ?? new ProcessTerminal();
171
172
  if (options.tuiMode === "fullscreen") {
172
- return new TuiAltScreen(terminal, options.showHardwareCursor, options.logDirectory, { openUrl: openBrowser });
173
+ const styleSearchMatch = (text) => theme.bg("searchMatchBg", theme.fg("searchMatchText", text));
174
+ return new TuiAltScreen(terminal, options.showHardwareCursor, options.logDirectory, {
175
+ searchMatchStyle: (text) => theme.underline(styleSearchMatch(text)),
176
+ searchCurrentMatchStyle: (text) => theme.bold(theme.inverse(styleSearchMatch(text))),
177
+ openUrl: openBrowser,
178
+ onRightClickPaste: options.onRightClickPaste,
179
+ copySelection: async (text) => {
180
+ try {
181
+ await copyToClipboard(text);
182
+ return true;
183
+ }
184
+ catch {
185
+ return false;
186
+ }
187
+ },
188
+ });
173
189
  }
174
190
  return new TuiMainScreen(terminal, options.showHardwareCursor, options.logDirectory);
175
191
  }
@@ -181,12 +197,19 @@ export function createInteractiveTuiReference(getTui) {
181
197
  const value = Reflect.get(tui, property, tui);
182
198
  if (typeof value !== "function")
183
199
  return value;
200
+ let methodTui = tui;
201
+ let method = value;
184
202
  return (...args) => {
185
- const tui = getTui();
186
- const method = Reflect.get(tui, property, tui);
187
- if (typeof method !== "function")
188
- throw new TypeError(`TUI property ${String(property)} is not callable`);
189
- return Reflect.apply(method, tui, args);
203
+ const currentTui = getTui();
204
+ if (currentTui !== methodTui) {
205
+ const currentMethod = Reflect.get(currentTui, property, currentTui);
206
+ if (typeof currentMethod !== "function") {
207
+ throw new TypeError(`TUI property ${String(property)} is not callable`);
208
+ }
209
+ methodTui = currentTui;
210
+ method = currentMethod;
211
+ }
212
+ return Reflect.apply(method, methodTui, args);
190
213
  };
191
214
  },
192
215
  set: (_target, property, value) => {
@@ -243,6 +266,7 @@ export class InteractiveMode {
243
266
  // Status line tracking (for mutating immediately-sequential status updates)
244
267
  lastStatusSpacer = undefined;
245
268
  lastStatusText = undefined;
269
+ managedToolStatusStarted = false;
246
270
  // Streaming message tracking
247
271
  streamingComponent = undefined;
248
272
  streamingMessage = undefined;
@@ -295,6 +319,9 @@ export class InteractiveMode {
295
319
  // Custom header from extension (undefined = use built-in header)
296
320
  customHeader = undefined;
297
321
  options;
322
+ onRightClickPaste = () => {
323
+ void this.handleRightClickPaste();
324
+ };
298
325
  autoTrustOnReloadCwd;
299
326
  themeController;
300
327
  // Convenience accessors
@@ -320,12 +347,14 @@ export class InteractiveMode {
320
347
  });
321
348
  this.runtimeHost.setRebindSession(async () => {
322
349
  await this.rebindCurrentSession({ renderBeforeBind: true });
350
+ await this.themeController.applyFromSettings();
323
351
  });
324
352
  this.version = VERSION;
325
353
  this.renderer = createInteractiveTui({
326
354
  tuiMode,
327
355
  showHardwareCursor: this.settingsManager.getShowHardwareCursor(),
328
356
  logDirectory: getAgentDir(),
357
+ onRightClickPaste: this.onRightClickPaste,
329
358
  });
330
359
  this.ui = createInteractiveTuiReference(() => this.renderer);
331
360
  this.ui.setClearOnShrink(this.settingsManager.getClearOnShrink());
@@ -361,7 +390,12 @@ export class InteractiveMode {
361
390
  this.outputPad = this.settingsManager.getOutputPad();
362
391
  // Register themes from resource loader and initialize
363
392
  setRegisteredThemes(this.session.resourceLoader.getThemes().themes);
364
- this.themeController = new InteractiveThemeController(this.ui, this.settingsManager, (message) => this.showError(message), () => this.updateEditorBorderColor());
393
+ this.themeController = new InteractiveThemeController(this.ui, {
394
+ getSettingsManager: () => this.settingsManager,
395
+ showError: (message) => this.showError(message),
396
+ onChanged: () => this.updateEditorBorderColor(),
397
+ initialThemeSetting: options.initialThemeSetting,
398
+ });
365
399
  }
366
400
  getAutocompleteSourceTag(sourceInfo) {
367
401
  if (!sourceInfo) {
@@ -524,14 +558,14 @@ export class InteractiveMode {
524
558
  tui.setLayoutRoot(this.fullscreenLayoutRoot);
525
559
  }
526
560
  }
527
- stopInteractiveTui() {
528
- if (this.renderer.mode === "fullscreen") {
561
+ stopInteractiveTui(fullscreenExitOutput) {
562
+ if (this.renderer.mode === "fullscreen" && fullscreenExitOutput === "transcript") {
529
563
  while (this.renderer.hasOverlayEntries)
530
564
  this.renderer.hideOverlay();
531
565
  this.switchTuiMode("regular", false, false);
532
566
  this.renderer.renderNow();
533
567
  }
534
- this.ui.stop();
568
+ this.ui.stop({ preserveScreen: this.renderer.mode === "fullscreen" });
535
569
  }
536
570
  switchTuiMode(mode, restoreProgress = true, startRenderer = true) {
537
571
  const previousUi = this.renderer;
@@ -558,6 +592,7 @@ export class InteractiveMode {
558
592
  showHardwareCursor,
559
593
  logDirectory: getAgentDir(),
560
594
  terminal,
595
+ onRightClickPaste: this.onRightClickPaste,
561
596
  });
562
597
  nextUi.setClearOnShrink(clearOnShrink);
563
598
  nextUi.onDebug = onDebug;
@@ -587,10 +622,6 @@ export class InteractiveMode {
587
622
  this.registerSignalHandlers();
588
623
  // Load changelog (only show new entries, skip for resumed sessions)
589
624
  this.changelogMarkdown = this.getChangelogForDisplay();
590
- // Ensure fd and rg are available (downloads if missing, adds to PATH via getBinDir)
591
- // Both are needed: fd for autocomplete, rg for grep tool and bash commands
592
- const [fdPath] = await Promise.all([ensureTool("fd"), ensureTool("rg")]);
593
- this.fdPath = fdPath;
594
625
  if (this.session.scopedModels.length > 0 && (this.options.verbose || !this.settingsManager.getQuietStartup())) {
595
626
  const modelList = this.session.scopedModels
596
627
  .map((sm) => {
@@ -634,9 +665,11 @@ export class InteractiveMode {
634
665
  this.widgetContainerBelow,
635
666
  this.footerContainer,
636
667
  ]);
668
+ // Accept text while startup completes, but only enable interrupt, exit, and submission feedback.
669
+ this.defaultEditor.onAction("app.clear", () => this.handleCtrlC());
670
+ this.defaultEditor.onCtrlD = () => this.handleCtrlD();
671
+ this.defaultEditor.onSubmit = (text) => this.handleStartupSubmit(text);
637
672
  this.ui.setFocus(this.editor);
638
- this.setupKeyHandlers();
639
- this.setupEditorSubmitHandler();
640
673
  // Start the UI before initializing extensions so session_start handlers can use interactive dialogs
641
674
  this.ui.start();
642
675
  this.isInitialized = true;
@@ -688,6 +721,18 @@ export class InteractiveMode {
688
721
  this.headerContainer.addChild(this.builtInHeader);
689
722
  }
690
723
  this.ui.requestRender();
724
+ // Ensure fd and rg are available after mounting the TUI (downloads if missing, adds to PATH via getBinDir)
725
+ // so slow downloads do not make startup appear frozen.
726
+ // Both are needed: fd for autocomplete, rg for grep tool and bash commands.
727
+ const [fdPath] = await Promise.all([
728
+ ensureTool("fd", (status) => this.showManagedToolStatus(status)),
729
+ ensureTool("rg", (status) => this.showManagedToolStatus(status)),
730
+ ]);
731
+ this.fdPath = fdPath;
732
+ // Enable the remaining input handlers only after managed-tool setup completes.
733
+ this.setupKeyHandlers();
734
+ this.setupEditorSubmitHandler();
735
+ this.ui.requestRender();
691
736
  // Initialize extensions first so resources are shown before messages
692
737
  await this.rebindCurrentSession();
693
738
  // Render initial messages AFTER showing loaded resources
@@ -727,8 +772,7 @@ export class InteractiveMode {
727
772
  if (!process.env.PI_OFFLINE) {
728
773
  const controller = new AbortController();
729
774
  const timeout = setTimeout(() => controller.abort(), 15_000);
730
- void this.session.modelRuntime
731
- .refresh({ signal: controller.signal })
775
+ void refreshModelCatalogs(this.session.modelRuntime, controller.signal)
732
776
  .then(() => this.updateAvailableProviderCount())
733
777
  .catch(() => { })
734
778
  .finally(() => clearTimeout(timeout));
@@ -1484,7 +1528,7 @@ export class InteractiveMode {
1484
1528
  const message = error instanceof Error ? error.message : String(error);
1485
1529
  this.showError(`${prefix}: ${message}`);
1486
1530
  stopThemeWatcher();
1487
- this.stop();
1531
+ this.stop("transcript");
1488
1532
  process.exit(1);
1489
1533
  }
1490
1534
  renderCurrentSessionState() {
@@ -2237,6 +2281,22 @@ export class InteractiveMode {
2237
2281
  void this.handleClipboardPaste();
2238
2282
  };
2239
2283
  }
2284
+ async handleRightClickPaste() {
2285
+ const target = this.renderer.getFocusedComponent();
2286
+ const handleInput = target?.handleInput;
2287
+ if (!target || !handleInput)
2288
+ return;
2289
+ try {
2290
+ const text = await readClipboardText();
2291
+ if (!text || this.renderer.getFocusedComponent() !== target)
2292
+ return;
2293
+ handleInput.call(target, `\x1b[200~${text}\x1b[201~`);
2294
+ this.ui.requestRender();
2295
+ }
2296
+ catch {
2297
+ // Silently ignore clipboard errors (may not have permission, etc.)
2298
+ }
2299
+ }
2240
2300
  async handleClipboardPaste() {
2241
2301
  try {
2242
2302
  const image = await readClipboardImage();
@@ -2260,6 +2320,10 @@ export class InteractiveMode {
2260
2320
  // Silently ignore clipboard errors (may not have permission, etc.)
2261
2321
  }
2262
2322
  }
2323
+ handleStartupSubmit(text) {
2324
+ this.editor.setText(text);
2325
+ this.showStatus("Startup is still in progress");
2326
+ }
2263
2327
  setupEditorSubmitHandler() {
2264
2328
  this.defaultEditor.onSubmit = async (text) => {
2265
2329
  text = text.trim();
@@ -2735,6 +2799,19 @@ export class InteractiveMode {
2735
2799
  : message.content.filter((c) => c.type === "text");
2736
2800
  return textBlocks.map((c) => c.text).join("");
2737
2801
  }
2802
+ /** Show a managed-tool status update in the chat. */
2803
+ showManagedToolStatus(status) {
2804
+ if (!this.managedToolStatusStarted) {
2805
+ this.chatContainer.addChild(new Spacer(1));
2806
+ this.managedToolStatusStarted = true;
2807
+ }
2808
+ const message = status.type === "warning" ? `Warning: ${status.message}` : status.message;
2809
+ const color = status.type === "warning" ? "warning" : "dim";
2810
+ this.chatContainer.addChild(new Text(theme.fg(color, message), 1, 0));
2811
+ this.lastStatusSpacer = undefined;
2812
+ this.lastStatusText = undefined;
2813
+ this.ui.requestRender();
2814
+ }
2738
2815
  /**
2739
2816
  * Show a status message in the chat.
2740
2817
  *
@@ -3106,7 +3183,7 @@ export class InteractiveMode {
3106
3183
  this.ui.stop();
3107
3184
  }
3108
3185
  catch { }
3109
- console.error("pi exiting due to uncaughtException:");
3186
+ console.error(`${APP_NAME} exiting due to uncaughtException:`);
3110
3187
  console.error(error);
3111
3188
  process.exit(1);
3112
3189
  }
@@ -3591,7 +3668,7 @@ export class InteractiveMode {
3591
3668
  httpIdleTimeoutMs: this.settingsManager.getHttpIdleTimeoutMs(),
3592
3669
  thinkingLevel: this.session.thinkingLevel,
3593
3670
  availableThinkingLevels: this.session.getAvailableThinkingLevels(),
3594
- currentTheme: this.settingsManager.getThemeSetting() || "dark",
3671
+ currentTheme: this.themeController.getThemeSelection() || "dark",
3595
3672
  terminalTheme: this.themeController.getTerminalTheme(),
3596
3673
  availableThemes: getAvailableThemes(),
3597
3674
  hideThinkingBlock: this.hideThinkingBlock,
@@ -3610,6 +3687,7 @@ export class InteractiveMode {
3610
3687
  clearOnShrink: this.settingsManager.getClearOnShrink(),
3611
3688
  showTerminalProgress: this.settingsManager.getShowTerminalProgress(),
3612
3689
  tuiMode: this.ui.mode,
3690
+ fullscreenExitOutput: this.settingsManager.getFullscreenExitOutput(),
3613
3691
  fullscreenScrollbar: this.settingsManager.getFullscreenScrollbar(),
3614
3692
  warnings: this.settingsManager.getWarnings(),
3615
3693
  }, {
@@ -3665,7 +3743,7 @@ export class InteractiveMode {
3665
3743
  },
3666
3744
  onThemeChange: (themeSetting) => {
3667
3745
  this.settingsManager.setTheme(themeSetting);
3668
- void this.themeController.applyFromSettings();
3746
+ void this.themeController.setThemeSetting(themeSetting);
3669
3747
  },
3670
3748
  onThemePreview: (themeName) => this.themeController.preview(themeName),
3671
3749
  onHideThinkingBlockChange: (hidden) => {
@@ -3764,6 +3842,9 @@ export class InteractiveMode {
3764
3842
  this.statusContainer.clear();
3765
3843
  this.showStatus(`TUI mode: ${mode}`);
3766
3844
  },
3845
+ onFullscreenExitOutputChange: (output) => {
3846
+ this.settingsManager.setFullscreenExitOutput(output);
3847
+ },
3767
3848
  onFullscreenScrollbarChange: (mode) => {
3768
3849
  this.settingsManager.setFullscreenScrollbar(mode);
3769
3850
  this.applyFullscreenScrollbarSetting();
@@ -3816,7 +3897,7 @@ export class InteractiveMode {
3816
3897
  controller.abort();
3817
3898
  }, 15_000);
3818
3899
  try {
3819
- const result = await this.session.modelRuntime.refresh({ signal: controller.signal });
3900
+ const result = await refreshModelCatalogs(this.session.modelRuntime, controller.signal);
3820
3901
  if (result.aborted && timedOut) {
3821
3902
  this.showWarning("Model refresh timed out; searching cached models.");
3822
3903
  }
@@ -3904,7 +3985,7 @@ export class InteractiveMode {
3904
3985
  onSelect: (selection) => {
3905
3986
  trustStore.setMany(selection.updates);
3906
3987
  done();
3907
- this.showStatus(`Saved trust decision: ${selection.trusted ? "trusted" : "untrusted"}. Restart pi for this to take effect.`);
3988
+ this.showStatus(`Saved trust decision: ${selection.trusted ? "trusted" : "untrusted"}. Restart ${APP_NAME} for this to take effect.`);
3908
3989
  },
3909
3990
  onCancel: () => {
3910
3991
  done();
@@ -4004,8 +4085,7 @@ export class InteractiveMode {
4004
4085
  this.ui.requestRender();
4005
4086
  },
4006
4087
  });
4007
- void this.session.modelRuntime
4008
- .refresh({ signal: controller.signal })
4088
+ void refreshModelCatalogs(this.session.modelRuntime, controller.signal)
4009
4089
  .then((result) => {
4010
4090
  if (disposed)
4011
4091
  return;
@@ -4559,7 +4639,7 @@ export class InteractiveMode {
4559
4639
  this.ui.requestRender();
4560
4640
  };
4561
4641
  const dialog = new LoginDialogComponent(this.ui, providerOption.id, () => restoreEditor(), providerOption.name, `${providerOption.name} setup`);
4562
- dialog.showInfo(`${providerOption.method?.name ?? "Authentication"} is configured outside pi.`, [], true);
4642
+ dialog.showInfo(`${providerOption.method?.name ?? "Authentication"} is configured outside ${APP_NAME}.`, [], true);
4563
4643
  this.editorContainer.clear();
4564
4644
  this.editorContainer.addChild(dialog);
4565
4645
  this.ui.setFocus(dialog);
@@ -4796,7 +4876,9 @@ export class InteractiveMode {
4796
4876
  this.showStatus(`Session exported to: ${filePath}`);
4797
4877
  }
4798
4878
  else {
4799
- const filePath = await this.session.exportToHtml(outputPath);
4879
+ const filePath = await this.session.exportToHtml(outputPath, {
4880
+ themeName: theme.name,
4881
+ });
4800
4882
  this.showStatus(`Session exported to: ${filePath}`);
4801
4883
  }
4802
4884
  }
@@ -4887,7 +4969,7 @@ export class InteractiveMode {
4887
4969
  // Export to a temp file
4888
4970
  const tmpFile = path.join(os.tmpdir(), "session.html");
4889
4971
  try {
4890
- await this.session.exportToHtml(tmpFile);
4972
+ await this.session.exportToHtml(tmpFile, { themeName: theme.name });
4891
4973
  }
4892
4974
  catch (error) {
4893
4975
  this.showError(`Failed to export session: ${error instanceof Error ? error.message : "Unknown error"}`);
@@ -5333,7 +5415,7 @@ export class InteractiveMode {
5333
5415
  // Ignore, will be emitted as an event
5334
5416
  }
5335
5417
  }
5336
- stop() {
5418
+ stop(fullscreenExitOutput = this.settingsManager.getFullscreenExitOutput()) {
5337
5419
  this.disposeActiveSelector();
5338
5420
  if (this.settingsManager.getShowTerminalProgress()) {
5339
5421
  this.ui.terminal.setProgress(false);
@@ -5347,7 +5429,7 @@ export class InteractiveMode {
5347
5429
  this.unsubscribe();
5348
5430
  }
5349
5431
  if (this.isInitialized) {
5350
- this.stopInteractiveTui();
5432
+ this.stopInteractiveTui(fullscreenExitOutput);
5351
5433
  this.isInitialized = false;
5352
5434
  }
5353
5435
  this.unregisterSignalHandlers();