apex-code 0.3.0 → 0.5.0

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 (213) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/dist/client/transcript.d.ts.map +1 -1
  3. package/dist/client/transcript.js +1 -1
  4. package/dist/client/transcript.js.map +1 -1
  5. package/dist/core/agent-session-services.d.ts.map +1 -1
  6. package/dist/core/agent-session-services.js +2 -1
  7. package/dist/core/agent-session-services.js.map +1 -1
  8. package/dist/core/delegation/runtime.d.ts.map +1 -1
  9. package/dist/core/delegation/runtime.js +2 -2
  10. package/dist/core/delegation/runtime.js.map +1 -1
  11. package/dist/core/durable-state/daemon.d.ts.map +1 -1
  12. package/dist/core/durable-state/daemon.js +1 -1
  13. package/dist/core/durable-state/daemon.js.map +1 -1
  14. package/dist/core/extensions/types.d.ts +1 -1
  15. package/dist/core/extensions/types.d.ts.map +1 -1
  16. package/dist/core/extensions/types.js.map +1 -1
  17. package/dist/core/keybindings.d.ts +1 -1
  18. package/dist/core/keybindings.d.ts.map +1 -1
  19. package/dist/core/keybindings.js.map +1 -1
  20. package/dist/core/mcp/mcp-tool.d.ts.map +1 -1
  21. package/dist/core/mcp/mcp-tool.js +2 -4
  22. package/dist/core/mcp/mcp-tool.js.map +1 -1
  23. package/dist/core/mcp/oauth/authorize.d.ts.map +1 -1
  24. package/dist/core/mcp/oauth/authorize.js +7 -2
  25. package/dist/core/mcp/oauth/authorize.js.map +1 -1
  26. package/dist/core/mcp/runtime.d.ts +2 -2
  27. package/dist/core/mcp/runtime.d.ts.map +1 -1
  28. package/dist/core/mcp/runtime.js +8 -3
  29. package/dist/core/mcp/runtime.js.map +1 -1
  30. package/dist/core/permissions/store.d.ts +7 -2
  31. package/dist/core/permissions/store.d.ts.map +1 -1
  32. package/dist/core/permissions/store.js +6 -1
  33. package/dist/core/permissions/store.js.map +1 -1
  34. package/dist/core/project-resources.d.ts +12 -0
  35. package/dist/core/project-resources.d.ts.map +1 -1
  36. package/dist/core/project-resources.js +19 -0
  37. package/dist/core/project-resources.js.map +1 -1
  38. package/dist/core/provider-composer.d.ts.map +1 -1
  39. package/dist/core/provider-composer.js +10 -1
  40. package/dist/core/provider-composer.js.map +1 -1
  41. package/dist/core/resource-loader.d.ts +6 -0
  42. package/dist/core/resource-loader.d.ts.map +1 -1
  43. package/dist/core/resource-loader.js +9 -3
  44. package/dist/core/resource-loader.js.map +1 -1
  45. package/dist/core/sdk.d.ts +1 -1
  46. package/dist/core/sdk.d.ts.map +1 -1
  47. package/dist/core/sdk.js +3 -3
  48. package/dist/core/sdk.js.map +1 -1
  49. package/dist/core/settings-manager.d.ts +16 -3
  50. package/dist/core/settings-manager.d.ts.map +1 -1
  51. package/dist/core/settings-manager.js +39 -16
  52. package/dist/core/settings-manager.js.map +1 -1
  53. package/dist/core/tools/bash.d.ts +11 -7
  54. package/dist/core/tools/bash.d.ts.map +1 -1
  55. package/dist/core/tools/bash.js +37 -46
  56. package/dist/core/tools/bash.js.map +1 -1
  57. package/dist/core/tools/delegate.d.ts.map +1 -1
  58. package/dist/core/tools/delegate.js +2 -2
  59. package/dist/core/tools/delegate.js.map +1 -1
  60. package/dist/core/tools/edit.d.ts.map +1 -1
  61. package/dist/core/tools/edit.js +3 -3
  62. package/dist/core/tools/edit.js.map +1 -1
  63. package/dist/core/tools/find.d.ts.map +1 -1
  64. package/dist/core/tools/find.js +4 -4
  65. package/dist/core/tools/find.js.map +1 -1
  66. package/dist/core/tools/grep.d.ts.map +1 -1
  67. package/dist/core/tools/grep.js +4 -4
  68. package/dist/core/tools/grep.js.map +1 -1
  69. package/dist/core/tools/ls.d.ts.map +1 -1
  70. package/dist/core/tools/ls.js +4 -4
  71. package/dist/core/tools/ls.js.map +1 -1
  72. package/dist/core/tools/path-utils.d.ts +9 -0
  73. package/dist/core/tools/path-utils.d.ts.map +1 -1
  74. package/dist/core/tools/path-utils.js +235 -10
  75. package/dist/core/tools/path-utils.js.map +1 -1
  76. package/dist/core/tools/read.d.ts.map +1 -1
  77. package/dist/core/tools/read.js +11 -8
  78. package/dist/core/tools/read.js.map +1 -1
  79. package/dist/core/tools/write.d.ts.map +1 -1
  80. package/dist/core/tools/write.js +7 -8
  81. package/dist/core/tools/write.js.map +1 -1
  82. package/dist/core/workspace/state.d.ts.map +1 -1
  83. package/dist/core/workspace/state.js +1 -1
  84. package/dist/core/workspace/state.js.map +1 -1
  85. package/dist/extensions/llama/ui.d.ts.map +1 -1
  86. package/dist/extensions/llama/ui.js +3 -2
  87. package/dist/extensions/llama/ui.js.map +1 -1
  88. package/dist/main.d.ts.map +1 -1
  89. package/dist/main.js +3 -1
  90. package/dist/main.js.map +1 -1
  91. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  92. package/dist/modes/interactive/components/assistant-message.js +0 -10
  93. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  94. package/dist/modes/interactive/components/autocomplete-source.d.ts +8 -0
  95. package/dist/modes/interactive/components/autocomplete-source.d.ts.map +1 -0
  96. package/dist/modes/interactive/components/autocomplete-source.js +23 -0
  97. package/dist/modes/interactive/components/autocomplete-source.js.map +1 -0
  98. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  99. package/dist/modes/interactive/components/bash-execution.js +6 -4
  100. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  101. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
  102. package/dist/modes/interactive/components/bordered-loader.js +1 -1
  103. package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
  104. package/dist/modes/interactive/components/composer-dock.d.ts +12 -0
  105. package/dist/modes/interactive/components/composer-dock.d.ts.map +1 -0
  106. package/dist/modes/interactive/components/composer-dock.js +24 -0
  107. package/dist/modes/interactive/components/composer-dock.js.map +1 -0
  108. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  109. package/dist/modes/interactive/components/config-selector.js +11 -9
  110. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  111. package/dist/modes/interactive/components/diff.d.ts.map +1 -1
  112. package/dist/modes/interactive/components/diff.js +2 -2
  113. package/dist/modes/interactive/components/diff.js.map +1 -1
  114. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  115. package/dist/modes/interactive/components/extension-editor.js +9 -9
  116. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  117. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  118. package/dist/modes/interactive/components/extension-input.js +9 -5
  119. package/dist/modes/interactive/components/extension-input.js.map +1 -1
  120. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  121. package/dist/modes/interactive/components/extension-selector.js +11 -10
  122. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  123. package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
  124. package/dist/modes/interactive/components/first-time-setup.js +6 -6
  125. package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
  126. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  127. package/dist/modes/interactive/components/footer.js +8 -3
  128. package/dist/modes/interactive/components/footer.js.map +1 -1
  129. package/dist/modes/interactive/components/keybinding-hints.d.ts +21 -0
  130. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  131. package/dist/modes/interactive/components/keybinding-hints.js +34 -0
  132. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
  133. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  134. package/dist/modes/interactive/components/login-dialog.js +27 -23
  135. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  136. package/dist/modes/interactive/components/model-selector.d.ts +1 -1
  137. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  138. package/dist/modes/interactive/components/model-selector.js +14 -18
  139. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  140. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  141. package/dist/modes/interactive/components/oauth-selector.js +7 -6
  142. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  143. package/dist/modes/interactive/components/permission-preview.d.ts.map +1 -1
  144. package/dist/modes/interactive/components/permission-preview.js +3 -3
  145. package/dist/modes/interactive/components/permission-preview.js.map +1 -1
  146. package/dist/modes/interactive/components/prompt-input.d.ts +12 -0
  147. package/dist/modes/interactive/components/prompt-input.d.ts.map +1 -0
  148. package/dist/modes/interactive/components/prompt-input.js +18 -0
  149. package/dist/modes/interactive/components/prompt-input.js.map +1 -0
  150. package/dist/modes/interactive/components/scoped-models-selector.d.ts +1 -1
  151. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  152. package/dist/modes/interactive/components/scoped-models-selector.js +4 -3
  153. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  154. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  155. package/dist/modes/interactive/components/session-selector.js +26 -19
  156. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  157. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  158. package/dist/modes/interactive/components/settings-selector.js +39 -6
  159. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  160. package/dist/modes/interactive/components/settings-submenu.d.ts.map +1 -1
  161. package/dist/modes/interactive/components/settings-submenu.js +8 -6
  162. package/dist/modes/interactive/components/settings-submenu.js.map +1 -1
  163. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  164. package/dist/modes/interactive/components/thinking-selector.js +10 -5
  165. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  166. package/dist/modes/interactive/components/tool-execution.d.ts +1 -11
  167. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  168. package/dist/modes/interactive/components/tool-execution.js +9 -38
  169. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  170. package/dist/modes/interactive/components/tool-panel.d.ts.map +1 -1
  171. package/dist/modes/interactive/components/tool-panel.js +7 -2
  172. package/dist/modes/interactive/components/tool-panel.js.map +1 -1
  173. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  174. package/dist/modes/interactive/components/tree-selector.js +9 -5
  175. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  176. package/dist/modes/interactive/components/trust-selector.d.ts.map +1 -1
  177. package/dist/modes/interactive/components/trust-selector.js +11 -11
  178. package/dist/modes/interactive/components/trust-selector.js.map +1 -1
  179. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
  180. package/dist/modes/interactive/components/user-message-selector.js +2 -2
  181. package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
  182. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  183. package/dist/modes/interactive/components/user-message.js +6 -3
  184. package/dist/modes/interactive/components/user-message.js.map +1 -1
  185. package/dist/modes/interactive/interactive-mode.d.ts +7 -2
  186. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  187. package/dist/modes/interactive/interactive-mode.js +65 -65
  188. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  189. package/dist/modes/print-mode.d.ts.map +1 -1
  190. package/dist/modes/print-mode.js +49 -23
  191. package/dist/modes/print-mode.js.map +1 -1
  192. package/dist/tsconfig.json +3 -0
  193. package/dist/utils/clipboard-image.d.ts.map +1 -1
  194. package/dist/utils/clipboard-image.js +1 -1
  195. package/dist/utils/clipboard-image.js.map +1 -1
  196. package/dist/utils/syntax-highlight.d.ts.map +1 -1
  197. package/dist/utils/syntax-highlight.js +22 -22
  198. package/dist/utils/syntax-highlight.js.map +1 -1
  199. package/docs/json.md +36 -0
  200. package/docs/models.md +1 -1
  201. package/docs/settings.md +1 -1
  202. package/examples/extensions/gondolin/package-lock.json +3 -3
  203. package/examples/extensions/minimal-mode.ts +6 -6
  204. package/examples/extensions/sandbox/package-lock.json +4 -4
  205. package/examples/extensions/sandbox/package.json +1 -1
  206. package/examples/sdk/10-settings.ts +2 -2
  207. package/examples/sdk/README.md +1 -1
  208. package/npm-shrinkwrap.json +19 -34
  209. package/package.json +6 -6
  210. package/dist/modes/interactive/components/message-spine.d.ts +0 -14
  211. package/dist/modes/interactive/components/message-spine.d.ts.map +0 -1
  212. package/dist/modes/interactive/components/message-spine.js +0 -19
  213. package/dist/modes/interactive/components/message-spine.js.map +0 -1
@@ -44,10 +44,12 @@ import { ensureTool } from "../../utils/tools-manager.js";
44
44
  import { checkForNewApexCodeVersion } from "../../utils/version-check.js";
45
45
  import { ArminComponent } from "./components/armin.js";
46
46
  import { AssistantMessageComponent } from "./components/assistant-message.js";
47
+ import { describeWithSource } from "./components/autocomplete-source.js";
47
48
  import { BashExecutionComponent } from "./components/bash-execution.js";
48
49
  import { BorderedLoader } from "./components/bordered-loader.js";
49
50
  import { BranchSummaryMessageComponent } from "./components/branch-summary-message.js";
50
51
  import { CompactionSummaryMessageComponent } from "./components/compaction-summary-message.js";
52
+ import { ComposerDock } from "./components/composer-dock.js";
51
53
  import { CustomEditor } from "./components/custom-editor.js";
52
54
  import { CustomEntryComponent } from "./components/custom-entry.js";
53
55
  import { CustomMessageComponent } from "./components/custom-message.js";
@@ -59,7 +61,7 @@ import { ExtensionInputComponent } from "./components/extension-input.js";
59
61
  import { ExtensionSelectorComponent } from "./components/extension-selector.js";
60
62
  import { FirstUseHints } from "./components/first-use-hints.js";
61
63
  import { FooterComponent, formatTokens } from "./components/footer.js";
62
- import { formatKeyText, keyDisplayText, keyHint, keyText, rawKeyHint } from "./components/keybinding-hints.js";
64
+ import { formatKeyText, hintRow, keyDisplayText, keyHint, keyText, rawKeyHint } from "./components/keybinding-hints.js";
63
65
  import { LoginDialogComponent } from "./components/login-dialog.js";
64
66
  import { createMermaidMarkdownTransformer } from "./components/mermaid.js";
65
67
  import { ModelSelectorComponent } from "./components/model-selector.js";
@@ -368,6 +370,13 @@ export class InteractiveMode {
368
370
  autoCompactionEscapeHandler;
369
371
  // Auto-retry state
370
372
  retryEscapeHandler;
373
+ /**
374
+ * The failed attempt an auto-retry would supersede. `component` is set while its error is
375
+ * still on screen, and a retry removes it from the chat.
376
+ */
377
+ retriedAttempt;
378
+ /** Set when the user presses Escape during a retry wait, which is the only way to cancel one. */
379
+ retryCancelled = false;
371
380
  // Messages queued while compaction is running
372
381
  compactionQueuedMessages = [];
373
382
  // Shutdown state
@@ -472,7 +481,7 @@ export class InteractiveMode {
472
481
  autocompleteFooter: () => this.buildAutocompleteFooter(),
473
482
  });
474
483
  this.editor = this.defaultEditor;
475
- this.editorContainer = new Container();
484
+ this.editorContainer = new ComposerDock(() => this.editor);
476
485
  this.editorContainer.addChild(this.editor);
477
486
  this.footerDataProvider = new FooterDataProvider(this.sessionManager.getCwd());
478
487
  this.footer = new FooterComponent(this.session, this.footerDataProvider, this.settingsManager);
@@ -491,32 +500,6 @@ export class InteractiveMode {
491
500
  initialThemeSetting: options.initialThemeSetting,
492
501
  });
493
502
  }
494
- getAutocompleteSourceTag(sourceInfo) {
495
- if (!sourceInfo) {
496
- return undefined;
497
- }
498
- const scopePrefix = sourceInfo.scope === "user" ? "u" : sourceInfo.scope === "project" ? "p" : "t";
499
- const source = sourceInfo.source.trim();
500
- if (source === "auto" || source === "local" || source === "cli") {
501
- return scopePrefix;
502
- }
503
- if (source.startsWith("npm:")) {
504
- return `${scopePrefix}:${source}`;
505
- }
506
- const gitSource = parseGitUrl(source);
507
- if (gitSource) {
508
- const ref = gitSource.ref ? `@${gitSource.ref}` : "";
509
- return `${scopePrefix}:git:${gitSource.host}/${gitSource.path}${ref}`;
510
- }
511
- return scopePrefix;
512
- }
513
- prefixAutocompleteDescription(description, sourceInfo) {
514
- const sourceTag = this.getAutocompleteSourceTag(sourceInfo);
515
- if (!sourceTag) {
516
- return description;
517
- }
518
- return description ? `[${sourceTag}] ${description}` : `[${sourceTag}]`;
519
- }
520
503
  getBuiltInCommandConflictDiagnostics(extensionRunner) {
521
504
  const builtinNames = new Set(BUILTIN_SLASH_COMMANDS.map((command) => command.name));
522
505
  return extensionRunner
@@ -595,7 +578,7 @@ export class InteractiveMode {
595
578
  // Convert prompt templates to SlashCommand format for autocomplete
596
579
  const templateCommands = this.session.promptTemplates.map((cmd) => ({
597
580
  name: cmd.name,
598
- description: this.prefixAutocompleteDescription(cmd.description, cmd.sourceInfo),
581
+ description: describeWithSource(cmd.description, cmd.sourceInfo),
599
582
  ...(cmd.argumentHint && { argumentHint: cmd.argumentHint }),
600
583
  }));
601
584
  // Convert extension commands to SlashCommand format
@@ -605,7 +588,7 @@ export class InteractiveMode {
605
588
  .filter((cmd) => !builtinCommandNames.has(cmd.name))
606
589
  .map((cmd) => ({
607
590
  name: cmd.invocationName,
608
- description: this.prefixAutocompleteDescription(cmd.description, cmd.sourceInfo),
591
+ description: describeWithSource(cmd.description, cmd.sourceInfo),
609
592
  getArgumentCompletions: cmd.getArgumentCompletions,
610
593
  }));
611
594
  // Build skill commands from session.skills (if enabled)
@@ -617,7 +600,7 @@ export class InteractiveMode {
617
600
  this.skillCommands.set(commandName, skill.filePath);
618
601
  skillCommandList.push({
619
602
  name: commandName,
620
- description: this.prefixAutocompleteDescription(skill.description, skill.sourceInfo),
603
+ description: describeWithSource(skill.description, skill.sourceInfo),
621
604
  });
622
605
  }
623
606
  }
@@ -661,7 +644,7 @@ export class InteractiveMode {
661
644
  this.chatContainer.addChild(new Text(condensedText, 1, 0));
662
645
  }
663
646
  else {
664
- this.chatContainer.addChild(new Text(theme.bold(theme.fg("accent", "What's New")), 1, 0));
647
+ this.chatContainer.addChild(new Text(theme.bold(theme.fg("accent", "What's new")), 1, 0));
665
648
  this.chatContainer.addChild(new Spacer(1));
666
649
  this.chatContainer.addChild(new Markdown(this.changelogMarkdown.trim(), 1, 0, this.getMarkdownThemeWithSettings()));
667
650
  this.chatContainer.addChild(new Spacer(1));
@@ -806,25 +789,25 @@ export class InteractiveMode {
806
789
  // The full cheatsheet is verbose-only. The default screen leads with the
807
790
  // mark and the runtime facts that actually differ between launches.
808
791
  const expandedInstructions = [
809
- hint("app.interrupt", "to interrupt"),
810
- hint("app.clear", "to clear"),
811
- rawKeyHint(`${keyText("app.clear")} twice`, "to exit"),
812
- hint("app.exit", "to exit (empty)"),
813
- hint("app.suspend", "to suspend"),
814
- keyHint("tui.editor.deleteToLineEnd", "to delete to end"),
815
- hint("app.thinking.cycle", "to cycle thinking level"),
816
- rawKeyHint(`${keyText("app.model.cycleForward")}/${keyText("app.model.cycleBackward")}`, "to cycle models"),
817
- hint("app.model.select", "to select model"),
818
- hint("app.tools.expand", "to expand tools"),
819
- hint("app.thinking.toggle", "to expand thinking"),
820
- hint("app.editor.external", "for external editor"),
821
- rawKeyHint("/", "for commands"),
822
- rawKeyHint("!", "to run bash"),
823
- rawKeyHint("!!", "to run bash (no context)"),
824
- hint("app.message.followUp", "to queue follow-up"),
825
- hint("app.message.dequeue", "to edit all queued messages"),
826
- hint("app.clipboard.pasteImage", "to paste image (with text fallback)"),
827
- rawKeyHint("drop files", "to attach"),
792
+ hint("app.interrupt", "interrupt"),
793
+ hint("app.clear", "clear"),
794
+ rawKeyHint(`${keyText("app.clear")} twice`, "exit"),
795
+ hint("app.exit", "exit (empty)"),
796
+ hint("app.suspend", "suspend"),
797
+ keyHint("tui.editor.deleteToLineEnd", "delete to end"),
798
+ hint("app.thinking.cycle", "cycle thinking level"),
799
+ rawKeyHint(`${keyText("app.model.cycleForward")}/${keyText("app.model.cycleBackward")}`, "cycle models"),
800
+ hint("app.model.select", "select model"),
801
+ hint("app.tools.expand", "expand tools"),
802
+ hint("app.thinking.toggle", "expand thinking"),
803
+ hint("app.editor.external", "external editor"),
804
+ rawKeyHint("/", "commands"),
805
+ rawKeyHint("!", "run bash"),
806
+ rawKeyHint("!!", "run bash (no context)"),
807
+ hint("app.message.followUp", "queue follow-up"),
808
+ hint("app.message.dequeue", "edit all queued messages"),
809
+ hint("app.clipboard.pasteImage", "paste image (with text fallback)"),
810
+ rawKeyHint("drop files", "attach"),
828
811
  ].join("\n");
829
812
  this.builtInHeader = new ApexSplashHeader(this.version, () => this.session.state.model?.id, () => this.session.sessionManager.getCwd(), this.options.verbose ? expandedInstructions : undefined, {
830
813
  topPadding: true,
@@ -1553,13 +1536,12 @@ export class InteractiveMode {
1553
1536
  */
1554
1537
  /** Live keybindings under the autocomplete dropdown. */
1555
1538
  buildAutocompleteFooter() {
1556
- const separator = theme.fg("borderMuted", this.settingsManager.getSymbolPreset() === "ascii" ? " - " : " · ");
1557
- return [
1558
- rawKeyHint(`${keyText("tui.select.up")}/${keyText("tui.select.down")}`, "move"),
1559
- keyHint("tui.select.confirm", "select"),
1560
- keyHint("tui.input.tab", "complete"),
1561
- keyHint("tui.select.cancel", "dismiss"),
1562
- ].join(separator);
1539
+ return hintRow([
1540
+ [["tui.select.up", "tui.select.down"], "move"],
1541
+ ["tui.select.confirm", "select"],
1542
+ ["tui.input.tab", "complete"],
1543
+ ["tui.select.cancel", "dismiss"],
1544
+ ], { ascii: this.settingsManager.getSymbolPreset() === "ascii" });
1563
1545
  }
1564
1546
  buildStartupShortcuts() {
1565
1547
  const entry = (sigil, label) => `${theme.fg("accent", sigil)} ${theme.fg("muted", label)}`;
@@ -2895,6 +2877,7 @@ export class InteractiveMode {
2895
2877
  this.ui.requestRender();
2896
2878
  }
2897
2879
  else if (event.message.role === "assistant") {
2880
+ this.retriedAttempt = undefined;
2898
2881
  this.streamingComponent = new AssistantMessageComponent(undefined, this.isThinkingHidden(), this.getMarkdownThemeWithSettings(), this.hiddenThinkingLabel, this.outputPad, this.getMarkdownTransformers());
2899
2882
  this.streamingMessage = event.message;
2900
2883
  this.chatContainer.addChild(this.streamingComponent);
@@ -2949,6 +2932,9 @@ export class InteractiveMode {
2949
2932
  if (!errorMessage) {
2950
2933
  errorMessage = this.streamingMessage.errorMessage || "Error";
2951
2934
  }
2935
+ if (this.streamingMessage.stopReason === "error" && this.pendingTools.size === 0) {
2936
+ this.retriedAttempt = { error: errorMessage, component: this.streamingComponent };
2937
+ }
2952
2938
  for (const [, component] of this.pendingTools.entries()) {
2953
2939
  component.updateResult({
2954
2940
  content: [{ type: "text", text: errorMessage }],
@@ -3084,9 +3070,16 @@ export class InteractiveMode {
3084
3070
  break;
3085
3071
  }
3086
3072
  case "auto_retry_start": {
3073
+ // The session drops the failed attempt from agent state; the chat drops it too, and
3074
+ // the retry indicator carries the attempt count instead.
3075
+ if (this.retriedAttempt?.component) {
3076
+ this.chatContainer.removeChild(this.retriedAttempt.component);
3077
+ this.retriedAttempt = { error: this.retriedAttempt.error };
3078
+ }
3087
3079
  // Set up escape to abort retry
3088
3080
  this.retryEscapeHandler = this.defaultEditor.onEscape;
3089
3081
  this.defaultEditor.onEscape = () => {
3082
+ this.retryCancelled = true;
3090
3083
  this.session.abortRetry();
3091
3084
  };
3092
3085
  this.showStatusIndicator(new RetryStatusIndicator(this.ui, event.attempt, event.maxAttempts, event.delayMs));
@@ -3102,8 +3095,16 @@ export class InteractiveMode {
3102
3095
  this.clearStatusIndicator("retry");
3103
3096
  // Show error only on final failure (success shows normal response)
3104
3097
  if (!event.success) {
3105
- this.showError(`Retry failed after ${event.attempt} attempts: ${event.finalError || "Unknown error"}`);
3098
+ const lastAttempt = this.retriedAttempt;
3099
+ if (lastAttempt?.component)
3100
+ this.chatContainer.removeChild(lastAttempt.component);
3101
+ const outcome = this.retryCancelled
3102
+ ? "retry cancelled"
3103
+ : `gave up after ${event.attempt} ${event.attempt === 1 ? "retry" : "retries"}`;
3104
+ this.showError(`${lastAttempt?.error ?? event.finalError ?? "Unknown error"} (${outcome})`);
3106
3105
  }
3106
+ this.retriedAttempt = undefined;
3107
+ this.retryCancelled = false;
3107
3108
  this.ui.requestRender();
3108
3109
  break;
3109
3110
  }
@@ -4455,7 +4456,7 @@ export class InteractiveMode {
4455
4456
  if (this.anthropicSubscriptionWarningShown) {
4456
4457
  return;
4457
4458
  }
4458
- if (!model || model.provider !== "anthropic") {
4459
+ if (model?.provider !== "anthropic") {
4459
4460
  return;
4460
4461
  }
4461
4462
  try {
@@ -5364,12 +5365,11 @@ export class InteractiveMode {
5364
5365
  }
5365
5366
  this.resetExtensionUI();
5366
5367
  const reloadBox = new Container();
5367
- const borderColor = (s) => theme.fg("border", s);
5368
- reloadBox.addChild(new DynamicBorder(borderColor));
5368
+ reloadBox.addChild(new DynamicBorder());
5369
5369
  reloadBox.addChild(new Spacer(1));
5370
- reloadBox.addChild(new Text(theme.fg("muted", "Reloading keybindings, extensions, skills, prompts, themes, and context files..."), 1, 0));
5370
+ reloadBox.addChild(new Text(theme.fg("muted", "Reloading keybindings, extensions, skills, prompts, themes, and context files..."), 0, 0));
5371
5371
  reloadBox.addChild(new Spacer(1));
5372
- reloadBox.addChild(new DynamicBorder(borderColor));
5372
+ reloadBox.addChild(new DynamicBorder());
5373
5373
  const previousEditor = this.editor;
5374
5374
  this.editorContainer.clear();
5375
5375
  this.editorContainer.addChild(reloadBox);
@@ -5723,7 +5723,7 @@ export class InteractiveMode {
5723
5723
  : "No changelog entries found.";
5724
5724
  this.chatContainer.addChild(new Spacer(1));
5725
5725
  this.chatContainer.addChild(new DynamicBorder());
5726
- this.chatContainer.addChild(new Text(theme.bold(theme.fg("accent", "What's New")), 1, 0));
5726
+ this.chatContainer.addChild(new Text(theme.bold(theme.fg("accent", "What's new")), 1, 0));
5727
5727
  this.chatContainer.addChild(new Spacer(1));
5728
5728
  this.chatContainer.addChild(new Markdown(changelogMarkdown, 1, 1, this.getMarkdownThemeWithSettings()));
5729
5729
  this.chatContainer.addChild(new DynamicBorder());