@ashx-j/lunr 0.2.12 → 0.2.14

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 (225) hide show
  1. package/CHANGELOG.md +16 -1
  2. package/README.md +6 -3
  3. package/dist/builtin-extensions/ashxj-tui.d.ts +16 -1
  4. package/dist/builtin-extensions/ashxj-tui.d.ts.map +1 -1
  5. package/dist/builtin-extensions/ashxj-tui.js +44 -20
  6. package/dist/builtin-extensions/ashxj-tui.js.map +1 -1
  7. package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts +2 -0
  8. package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts.map +1 -1
  9. package/dist/builtin-extensions/pi-subagents/src/extension/index.js +27 -22
  10. package/dist/builtin-extensions/pi-subagents/src/extension/index.js.map +1 -1
  11. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.d.ts.map +1 -1
  12. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.js +5 -2
  13. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.js.map +1 -1
  14. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-resume.d.ts.map +1 -1
  15. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-resume.js +12 -1
  16. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-resume.js.map +1 -1
  17. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-status.d.ts +1 -0
  18. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-status.d.ts.map +1 -1
  19. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-status.js +3 -2
  20. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-status.js.map +1 -1
  21. package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-append.d.ts.map +1 -1
  22. package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-append.js +1 -0
  23. package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-append.js.map +1 -1
  24. package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-root-attachment.d.ts.map +1 -1
  25. package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-root-attachment.js +3 -0
  26. package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-root-attachment.js.map +1 -1
  27. package/dist/builtin-extensions/pi-subagents/src/runs/background/fleet-view.d.ts.map +1 -1
  28. package/dist/builtin-extensions/pi-subagents/src/runs/background/fleet-view.js +3 -3
  29. package/dist/builtin-extensions/pi-subagents/src/runs/background/fleet-view.js.map +1 -1
  30. package/dist/builtin-extensions/pi-subagents/src/runs/background/run-status.d.ts.map +1 -1
  31. package/dist/builtin-extensions/pi-subagents/src/runs/background/run-status.js +2 -2
  32. package/dist/builtin-extensions/pi-subagents/src/runs/background/run-status.js.map +1 -1
  33. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.d.ts.map +1 -1
  34. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js +3 -0
  35. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js.map +1 -1
  36. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.d.ts.map +1 -1
  37. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js +7 -1
  38. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js.map +1 -1
  39. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.d.ts.map +1 -1
  40. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js +2 -0
  41. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js.map +1 -1
  42. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.d.ts +1 -0
  43. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.d.ts.map +1 -1
  44. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js +19 -3
  45. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js.map +1 -1
  46. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts +10 -1
  47. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts.map +1 -1
  48. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js +19 -0
  49. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js.map +1 -1
  50. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts +1 -0
  51. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts.map +1 -1
  52. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.js.map +1 -1
  53. package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.d.ts +2 -1
  54. package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.d.ts.map +1 -1
  55. package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.js +17 -0
  56. package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.js.map +1 -1
  57. package/dist/builtin-extensions/pi-subagents/src/shared/formatters.d.ts +3 -1
  58. package/dist/builtin-extensions/pi-subagents/src/shared/formatters.d.ts.map +1 -1
  59. package/dist/builtin-extensions/pi-subagents/src/shared/formatters.js +14 -3
  60. package/dist/builtin-extensions/pi-subagents/src/shared/formatters.js.map +1 -1
  61. package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts +14 -0
  62. package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts.map +1 -1
  63. package/dist/builtin-extensions/pi-subagents/src/shared/types.js.map +1 -1
  64. package/dist/builtin-extensions/pi-subagents/src/tui/fleet.d.ts.map +1 -1
  65. package/dist/builtin-extensions/pi-subagents/src/tui/fleet.js +2 -2
  66. package/dist/builtin-extensions/pi-subagents/src/tui/fleet.js.map +1 -1
  67. package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts.map +1 -1
  68. package/dist/builtin-extensions/pi-subagents/src/tui/render.js +12 -10
  69. package/dist/builtin-extensions/pi-subagents/src/tui/render.js.map +1 -1
  70. package/dist/catalog/models.json +1 -1
  71. package/dist/catalog/providers/amazon-bedrock.json +1 -1
  72. package/dist/catalog/providers/anthropic.json +1 -1
  73. package/dist/catalog/providers/azure-openai-responses.json +1 -1
  74. package/dist/catalog/providers/cloudflare-ai-gateway.json +1 -1
  75. package/dist/catalog/providers/cloudflare-workers-ai.json +1 -1
  76. package/dist/catalog/providers/fireworks.json +1 -1
  77. package/dist/catalog/providers/github-copilot.json +1 -1
  78. package/dist/catalog/providers/google-vertex.json +1 -1
  79. package/dist/catalog/providers/google.json +1 -1
  80. package/dist/catalog/providers/groq.json +1 -1
  81. package/dist/catalog/providers/huggingface.json +1 -1
  82. package/dist/catalog/providers/nvidia.json +1 -1
  83. package/dist/catalog/providers/openai-codex.json +1 -1
  84. package/dist/catalog/providers/openai.json +1 -1
  85. package/dist/catalog/providers/opencode-go.json +1 -1
  86. package/dist/catalog/providers/opencode.json +1 -1
  87. package/dist/catalog/providers/openrouter.json +1 -1
  88. package/dist/catalog/providers/together.json +1 -1
  89. package/dist/catalog/providers/vercel-ai-gateway.json +1 -1
  90. package/dist/catalog/publication.json +131 -3
  91. package/dist/cli/list-models.d.ts.map +1 -1
  92. package/dist/cli/list-models.js +1 -0
  93. package/dist/cli/list-models.js.map +1 -1
  94. package/dist/cli.d.ts.map +1 -1
  95. package/dist/cli.js +52 -27
  96. package/dist/cli.js.map +1 -1
  97. package/dist/core/catalog-cache-scope.d.ts +3 -0
  98. package/dist/core/catalog-cache-scope.d.ts.map +1 -0
  99. package/dist/core/catalog-cache-scope.js +12 -0
  100. package/dist/core/catalog-cache-scope.js.map +1 -0
  101. package/dist/core/catalog-merge.d.ts +2 -0
  102. package/dist/core/catalog-merge.d.ts.map +1 -1
  103. package/dist/core/catalog-merge.js +29 -2
  104. package/dist/core/catalog-merge.js.map +1 -1
  105. package/dist/core/catalog-refresh-polling.d.ts +8 -0
  106. package/dist/core/catalog-refresh-polling.d.ts.map +1 -0
  107. package/dist/core/catalog-refresh-polling.js +30 -0
  108. package/dist/core/catalog-refresh-polling.js.map +1 -0
  109. package/dist/core/codex-catalog.d.ts +3 -0
  110. package/dist/core/codex-catalog.d.ts.map +1 -0
  111. package/dist/core/codex-catalog.js +15 -0
  112. package/dist/core/codex-catalog.js.map +1 -0
  113. package/dist/core/context-breakdown.d.ts +3 -1
  114. package/dist/core/context-breakdown.d.ts.map +1 -1
  115. package/dist/core/context-breakdown.js +5 -2
  116. package/dist/core/context-breakdown.js.map +1 -1
  117. package/dist/core/customize.d.ts +2 -4
  118. package/dist/core/customize.d.ts.map +1 -1
  119. package/dist/core/customize.js +2 -8
  120. package/dist/core/customize.js.map +1 -1
  121. package/dist/core/footer-data-provider.d.ts +3 -4
  122. package/dist/core/footer-data-provider.d.ts.map +1 -1
  123. package/dist/core/footer-data-provider.js +54 -82
  124. package/dist/core/footer-data-provider.js.map +1 -1
  125. package/dist/core/live-catalog.d.ts +5 -2
  126. package/dist/core/live-catalog.d.ts.map +1 -1
  127. package/dist/core/live-catalog.js +108 -27
  128. package/dist/core/live-catalog.js.map +1 -1
  129. package/dist/core/model-runtime.d.ts +8 -0
  130. package/dist/core/model-runtime.d.ts.map +1 -1
  131. package/dist/core/model-runtime.js +64 -12
  132. package/dist/core/model-runtime.js.map +1 -1
  133. package/dist/core/official-catalog.d.ts +4 -0
  134. package/dist/core/official-catalog.d.ts.map +1 -1
  135. package/dist/core/official-catalog.js +86 -21
  136. package/dist/core/official-catalog.js.map +1 -1
  137. package/dist/core/settings-manager.d.ts +7 -6
  138. package/dist/core/settings-manager.d.ts.map +1 -1
  139. package/dist/core/settings-manager.js +13 -17
  140. package/dist/core/settings-manager.js.map +1 -1
  141. package/dist/core/slash-commands.d.ts.map +1 -1
  142. package/dist/core/slash-commands.js +1 -0
  143. package/dist/core/slash-commands.js.map +1 -1
  144. package/dist/index.d.ts +1 -1
  145. package/dist/index.d.ts.map +1 -1
  146. package/dist/index.js.map +1 -1
  147. package/dist/main.d.ts +2 -0
  148. package/dist/main.d.ts.map +1 -1
  149. package/dist/main.js +106 -50
  150. package/dist/main.js.map +1 -1
  151. package/dist/migrations.d.ts +2 -0
  152. package/dist/migrations.d.ts.map +1 -1
  153. package/dist/migrations.js +14 -4
  154. package/dist/migrations.js.map +1 -1
  155. package/dist/modes/interactive/components/assistant-message.d.ts +1 -3
  156. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  157. package/dist/modes/interactive/components/assistant-message.js +4 -32
  158. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  159. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  160. package/dist/modes/interactive/components/custom-editor.js +2 -1
  161. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  162. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  163. package/dist/modes/interactive/components/model-selector.js +4 -1
  164. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  165. package/dist/modes/interactive/components/plan-message.d.ts +1 -3
  166. package/dist/modes/interactive/components/plan-message.d.ts.map +1 -1
  167. package/dist/modes/interactive/components/plan-message.js +1 -16
  168. package/dist/modes/interactive/components/plan-message.js.map +1 -1
  169. package/dist/modes/interactive/components/settings-selector.d.ts +3 -5
  170. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  171. package/dist/modes/interactive/components/settings-selector.js +113 -117
  172. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  173. package/dist/modes/interactive/components/user-message.d.ts +1 -3
  174. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  175. package/dist/modes/interactive/components/user-message.js +2 -19
  176. package/dist/modes/interactive/components/user-message.js.map +1 -1
  177. package/dist/modes/interactive/interactive-mode.d.ts +12 -1
  178. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  179. package/dist/modes/interactive/interactive-mode.js +153 -130
  180. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  181. package/dist/modes/interactive/skill-tag-autocomplete.d.ts +13 -0
  182. package/dist/modes/interactive/skill-tag-autocomplete.d.ts.map +1 -0
  183. package/dist/modes/interactive/skill-tag-autocomplete.js +72 -0
  184. package/dist/modes/interactive/skill-tag-autocomplete.js.map +1 -0
  185. package/dist/modes/interactive/theme/moon.json +0 -1
  186. package/dist/modes/interactive/theme/theme-schema.json +3 -3
  187. package/dist/modes/interactive/theme/theme.d.ts +1 -1
  188. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  189. package/dist/modes/interactive/theme/theme.js +95 -90
  190. package/dist/modes/interactive/theme/theme.js.map +1 -1
  191. package/dist/startup/interactive-view.d.ts +55 -0
  192. package/dist/startup/interactive-view.d.ts.map +1 -0
  193. package/dist/startup/interactive-view.js +332 -0
  194. package/dist/startup/interactive-view.js.map +1 -0
  195. package/dist/startup/launch-routing.d.ts +8 -0
  196. package/dist/startup/launch-routing.d.ts.map +1 -0
  197. package/dist/startup/launch-routing.js +29 -0
  198. package/dist/startup/launch-routing.js.map +1 -0
  199. package/dist/startup/startup-milestones.d.ts +4 -0
  200. package/dist/startup/startup-milestones.d.ts.map +1 -0
  201. package/dist/startup/startup-milestones.js +14 -0
  202. package/dist/startup/startup-milestones.js.map +1 -0
  203. package/dist/utils/syntax-highlight.d.ts.map +1 -1
  204. package/dist/utils/syntax-highlight.js +10 -4
  205. package/dist/utils/syntax-highlight.js.map +1 -1
  206. package/docs/features.md +2 -2
  207. package/docs/interactive-startup.md +58 -0
  208. package/docs/keybindings.md +1 -1
  209. package/docs/model-catalog.md +29 -0
  210. package/docs/quickstart.md +1 -1
  211. package/docs/settings.md +2 -3
  212. package/docs/skills.md +10 -0
  213. package/docs/terminal-setup.md +2 -2
  214. package/docs/usage.md +2 -1
  215. package/docs/windows.md +1 -1
  216. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  217. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  218. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  219. package/examples/extensions/gondolin/package-lock.json +2 -2
  220. package/examples/extensions/gondolin/package.json +1 -1
  221. package/examples/extensions/sandbox/package-lock.json +2 -2
  222. package/examples/extensions/sandbox/package.json +1 -1
  223. package/examples/extensions/with-deps/package-lock.json +2 -2
  224. package/examples/extensions/with-deps/package.json +1 -1
  225. package/package.json +16 -16
@@ -7,7 +7,7 @@ import * as fs from "node:fs";
7
7
  import * as os from "node:os";
8
8
  import * as path from "node:path";
9
9
  import { getSupportedThinkingLevels, } from "@ashx-j/lunr-ai/compat";
10
- import { collectImageMarkerIds, CombinedAutocompleteProvider, Container, formatImageMarker, fuzzyFilter, isFocusable, Markdown, matchesKey, ProcessTerminal, Spacer, setKeybindings, Text, TruncatedText, TUI, visibleWidth, } from "@ashx-j/lunr-tui";
10
+ import { CombinedAutocompleteProvider, Container, collectImageMarkerIds, formatImageMarker, fuzzyFilter, isFocusable, Markdown, matchesKey, ProcessTerminal, Spacer, setKeybindings, Text, TruncatedText, TUI, visibleWidth, } from "@ashx-j/lunr-tui";
11
11
  import chalk from "chalk";
12
12
  import { spawn, spawnSync } from "child_process";
13
13
  import { APP_NAME, APP_TITLE, appendDebugLog, CONFIG_DIR_NAME, getAgentDir, getAuthPath, getDebugLogPath, getDocsPath, getShareViewerUrl, VERSION, } from "../../config.js";
@@ -15,13 +15,13 @@ import { parseSkillBlock } from "../../core/agent-session.js";
15
15
  import { SessionImportFileNotFoundError } from "../../core/agent-session-runtime.js";
16
16
  import { CACHE_TTL_MS, collectCacheMisses, computeCacheWaste, detectCacheMiss, } from "../../core/cache-stats.js";
17
17
  import { formatCatalogRefreshSummary } from "../../core/catalog-merge.js";
18
+ import { startCatalogRefreshPolling } from "../../core/catalog-refresh-polling.js";
18
19
  import { computeContextBreakdown } from "../../core/context-breakdown.js";
19
20
  import { FooterDataProvider } from "../../core/footer-data-provider.js";
20
21
  import { hasGrokCliXaiAuth } from "../../core/grok-cli-auth.js";
21
22
  import { configureHttpDispatcher, formatHttpIdleTimeoutMs } from "../../core/http-dispatcher.js";
22
23
  import { detectInjectedPrompt } from "../../core/injected-prompt.js";
23
24
  import { KeybindingsManager } from "../../core/keybindings.js";
24
- import { applyIncompleteAnswers, describeIncomplete, INCOMPLETE_PROMPT_CAP, parsePositiveInt, } from "../../core/live-catalog.js";
25
25
  import { createCompactionSummaryMessage } from "../../core/messages.js";
26
26
  import { isValidProviderBaseUrl, parseModelIds, slugifyProviderId, upsertCustomProvider, } from "../../core/model-config-writer.js";
27
27
  import { defaultModelPerProvider, findExactModelReferenceMatch, resolveModelScope } from "../../core/model-resolver.js";
@@ -43,7 +43,7 @@ import { time } from "../../core/timings.js";
43
43
  import { hasTrustRequiringProjectResources, ProjectTrustStore } from "../../core/trust-manager.js";
44
44
  import { checkForUpdate, markUpdateNotified } from "../../core/update-check.js";
45
45
  import { getAllPlanUsageResults, getUsageServiceBridge } from "../../core/usage-service.js";
46
- import { modelToUserEntry } from "../../core/user-models.js";
46
+ import { markStartupMilestone } from "../../startup/startup-milestones.js";
47
47
  import { copyToClipboard, readClipboardText } from "../../utils/clipboard.js";
48
48
  import { extensionForImageMimeType, readClipboardImage } from "../../utils/clipboard-image.js";
49
49
  import { parseGitUrl } from "../../utils/git.js";
@@ -89,6 +89,7 @@ import { renderUsageBox } from "./components/usage-view.js";
89
89
  import { UserMessageComponent } from "./components/user-message.js";
90
90
  import { UserMessageSelectorComponent } from "./components/user-message-selector.js";
91
91
  import { getModelSearchText } from "./model-search.js";
92
+ import { wrapSkillTagAutocomplete } from "./skill-tag-autocomplete.js";
92
93
  import { computeSmoothRevealStep, countGraphemesBeforeToolCall, countMessageGraphemes, sliceMessageContent, } from "./smooth-streaming.js";
93
94
  import { getAvailableThemes, getAvailableThemesWithPaths, getEditorTheme, getMarkdownTheme, getThemeByName, getThemeName, onThemeChange, setRegisteredThemes, stopThemeWatcher, Theme, theme, } from "./theme/theme.js";
94
95
  import { InteractiveThemeController } from "./theme/theme-controller.js";
@@ -198,6 +199,11 @@ function formatLoginProviderCompletionDescription(provider) {
198
199
  const authTypes = provider.authTypes.map(formatAuthSelectorProviderType).join("/");
199
200
  return provider.name === provider.id ? authTypes : `${provider.name} · ${authTypes}`;
200
201
  }
202
+ export async function startInteractiveToolInstalls(currentFdPath, onFdInstalled, installer = ensureTool) {
203
+ const [fdPath] = await Promise.all([installer("fd", true), installer("rg", true)]);
204
+ if (fdPath && fdPath !== currentFdPath)
205
+ onFdInstalled(fdPath);
206
+ }
201
207
  export class InteractiveMode {
202
208
  runtimeHost;
203
209
  ui;
@@ -218,6 +224,7 @@ export class InteractiveMode {
218
224
  keybindings;
219
225
  version;
220
226
  isInitialized = false;
227
+ stopCatalogRefresh;
221
228
  onInputCallback;
222
229
  pendingUserInputs = [];
223
230
  stagedSubmitImages;
@@ -312,6 +319,8 @@ export class InteractiveMode {
312
319
  autoTrustOnReloadCwd;
313
320
  themeController;
314
321
  deferredBuiltinsAttached = false;
322
+ startupReadyPromise;
323
+ startupFeatureError;
315
324
  deferredBuiltinAttachPromise;
316
325
  // Convenience accessors
317
326
  get session() {
@@ -352,7 +361,7 @@ export class InteractiveMode {
352
361
  this.syncPermissionModeEffects(this.settingsManager.getDefaultPermissionMode());
353
362
  });
354
363
  this.version = VERSION;
355
- this.ui = new TUI(new ProcessTerminal(), this.settingsManager.getShowHardwareCursor());
364
+ this.ui = options.startupView?.ui ?? new TUI(new ProcessTerminal(), this.settingsManager.getShowHardwareCursor());
356
365
  this.ui.setClearOnShrink(this.settingsManager.getClearOnShrink());
357
366
  // lunr: register the permission-mode footer bridge (provider-side: InteractiveMode owns the state).
358
367
  registerPermissionModeBridge(() => getPermissionMode());
@@ -363,21 +372,29 @@ export class InteractiveMode {
363
372
  // lunr: reset permission mode to configured default on startup.
364
373
  resetPermissions(this.settingsManager.getDefaultPermissionMode());
365
374
  this.headerContainer = new Container();
375
+ if (options.startupView) {
376
+ this.headerContainer.addChild(new Spacer(1));
377
+ this.headerContainer.addChild(options.startupView.header);
378
+ this.customFooter = options.startupView.footer;
379
+ }
366
380
  this.loadedResourcesContainer = new Container();
367
381
  this.chatContainer = new Container();
368
382
  this.pendingMessagesContainer = new Container();
369
- this.statusContainer = new Container();
383
+ this.statusContainer = options.startupView?.statusContainer ?? new Container();
370
384
  this.widgetContainerAbove = new Container();
371
385
  this.widgetContainerBelow = new Container();
372
- this.keybindings = KeybindingsManager.create();
386
+ this.keybindings = options.startupView?.keybindings ?? KeybindingsManager.create();
373
387
  setKeybindings(this.keybindings);
374
388
  const editorPaddingX = this.settingsManager.getEditorPaddingX();
375
389
  const autocompleteMaxVisible = this.settingsManager.getAutocompleteMaxVisible();
376
- this.defaultEditor = new CustomEditor(this.ui, getEditorTheme(), this.keybindings, {
377
- paddingX: editorPaddingX,
378
- autocompleteMaxVisible,
379
- });
390
+ this.defaultEditor =
391
+ options.startupView?.editor ??
392
+ new CustomEditor(this.ui, getEditorTheme(), this.keybindings, {
393
+ paddingX: editorPaddingX,
394
+ autocompleteMaxVisible,
395
+ });
380
396
  this.editor = this.defaultEditor;
397
+ options.startupView?.setCurrentEditor(() => this.editor);
381
398
  this.editorContainer = new Container();
382
399
  this.editorContainer.addChild(this.editor);
383
400
  this.footerDataProvider = new FooterDataProvider(this.sessionManager.getCwd());
@@ -511,11 +528,17 @@ export class InteractiveMode {
511
528
  });
512
529
  }
513
530
  }
514
- return new CombinedAutocompleteProvider([...slashCommands, ...templateCommands, ...extensionCommands, ...skillCommandList], this.sessionManager.getCwd(), this.fdPath);
531
+ return wrapSkillTagAutocomplete(new CombinedAutocompleteProvider([...slashCommands, ...templateCommands, ...extensionCommands, ...skillCommandList], this.sessionManager.getCwd(), this.fdPath), {
532
+ character: this.settingsManager.getSkillTagCharacter(),
533
+ skills: this.session.resourceLoader.getSkills().skills.map((skill) => ({
534
+ name: skill.name,
535
+ description: this.prefixAutocompleteDescription(skill.description, skill.sourceInfo),
536
+ })),
537
+ });
515
538
  }
516
539
  setupAutocompleteProvider() {
517
540
  let provider = this.createBaseAutocompleteProvider();
518
- const triggerCharacters = [];
541
+ const triggerCharacters = [...(provider.triggerCharacters ?? [])];
519
542
  for (const wrapProvider of this.autocompleteProviderWrappers) {
520
543
  provider = wrapProvider(provider);
521
544
  triggerCharacters.push(...(provider.triggerCharacters ?? []));
@@ -533,14 +556,10 @@ export class InteractiveMode {
533
556
  if (this.isInitialized)
534
557
  return;
535
558
  this.registerSignalHandlers();
536
- // Use already-installed fd immediately. Download in the background so a
537
- // missing binary or GitHub outage cannot block first paint.
538
559
  this.fdPath = getToolPath("fd") ?? undefined;
539
- void Promise.all([ensureTool("fd"), ensureTool("rg")]).then(([fdPath]) => {
540
- if (fdPath && fdPath !== this.fdPath) {
541
- this.fdPath = fdPath;
542
- this.setupAutocompleteProvider();
543
- }
560
+ void startInteractiveToolInstalls(this.fdPath, (fdPath) => {
561
+ this.fdPath = fdPath;
562
+ this.setupAutocompleteProvider();
544
563
  });
545
564
  time("ensureTools");
546
565
  if (this.session.scopedModels.length > 0 && (this.options.verbose || !this.settingsManager.getQuietStartup())) {
@@ -556,6 +575,10 @@ export class InteractiveMode {
556
575
  : "";
557
576
  console.log(theme.fg("dim", `Model scope: ${modelList}${cycleHint}`));
558
577
  }
578
+ if (this.options.startupView) {
579
+ this.ui.pinFrom(null);
580
+ this.ui.clear();
581
+ }
559
582
  // Add header container as first child. Populate it after applying theme settings.
560
583
  // Keep loaded resources before chat so restored session messages never precede them.
561
584
  this.ui.addChild(this.headerContainer);
@@ -567,14 +590,16 @@ export class InteractiveMode {
567
590
  this.ui.addChild(this.widgetContainerAbove);
568
591
  this.ui.addChild(this.editorContainer);
569
592
  this.ui.addChild(this.widgetContainerBelow);
570
- this.ui.addChild(this.footer);
593
+ this.ui.addChild(this.customFooter ?? this.footer);
571
594
  this.ui.setFocus(this.editor);
572
- this.setupKeyHandlers();
573
- this.setupEditorSubmitHandler();
574
595
  this.ui.pinFrom(this.widgetContainerAbove);
596
+ this.setupKeyHandlers();
597
+ if (!this.options.startupView)
598
+ this.setupEditorSubmitHandler();
575
599
  this.ui.setAlternateScreen(true);
576
600
  // Start the UI before initializing extensions so session_start handlers can use interactive dialogs
577
- this.ui.start();
601
+ if (!this.options.startupView)
602
+ this.ui.start();
578
603
  this.isInitialized = true;
579
604
  time("ui.start");
580
605
  void this.maybeNotifyCliUpdate();
@@ -611,6 +636,7 @@ export class InteractiveMode {
611
636
  setRollbackWarningHandler((msg) => this.showStatus(msg));
612
637
  this.syncPermissionModeEffects(this.settingsManager.getDefaultPermissionMode());
613
638
  await this.themeController.applyFromSettings();
639
+ this.headerContainer.clear();
614
640
  // Add boot screen header (unless silenced)
615
641
  if (this.options.verbose || !this.settingsManager.getQuietStartup()) {
616
642
  const header = theme.bold(theme.fg("accent", APP_NAME)) + theme.fg("dim", ` v${this.version}`);
@@ -630,9 +656,32 @@ export class InteractiveMode {
630
656
  await this.rebindCurrentSession();
631
657
  time("rebindCurrentSession");
632
658
  this.renderInitialMessages();
659
+ this.ui.requestRender();
660
+ for (const diagnostic of this.options.startupDiagnostics ?? [])
661
+ this.showStatus(diagnostic.message);
662
+ for (const warning of this.options.deprecationWarnings ?? [])
663
+ this.showWarning(warning);
633
664
  this.deferredBuiltinAttachPromise = this.attachDeferredBuiltinExtensions();
634
- void this.deferredBuiltinAttachPromise.then(() => {
665
+ this.startupReadyPromise = this.deferredBuiltinAttachPromise.then(() => {
666
+ if (!this.isInitialized)
667
+ return;
668
+ if (this.startupFeatureError)
669
+ throw this.startupFeatureError;
670
+ if (this.options.startupView) {
671
+ this.setupEditorSubmitHandler();
672
+ // Extensions can replace the editor. Wire its submit callback after readiness as well.
673
+ this.editor.onSubmit = this.defaultEditor.onSubmit;
674
+ this.options.startupView.activate();
675
+ }
635
676
  time("attachDeferredBuiltins");
677
+ markStartupMilestone("prompt_barrier_open");
678
+ void this.options.deferredMaintenance?.();
679
+ });
680
+ void this.startupReadyPromise.catch((error) => {
681
+ if (this.options.startupView)
682
+ this.options.startupView.showError(error);
683
+ else
684
+ this.showError(String(error));
636
685
  });
637
686
  // Set up theme file watcher
638
687
  onThemeChange(() => {
@@ -648,6 +697,21 @@ export class InteractiveMode {
648
697
  void this.updateAvailableProviderCount().then(() => {
649
698
  time("updateAvailableProviderCount");
650
699
  });
700
+ void this.waitForDeferredBuiltins().then(() => {
701
+ if (!this.isInitialized)
702
+ return;
703
+ this.stopCatalogRefresh = startCatalogRefreshPolling({
704
+ refresh: (signal) => this.session.modelRuntime.refreshIfStale({ signal }),
705
+ isBusy: () => this.session.isStreaming,
706
+ onUpdate: () => {
707
+ this.session.refreshModelFromRegistry();
708
+ this.footer.invalidate();
709
+ },
710
+ });
711
+ });
712
+ }
713
+ async waitForStartupReady() {
714
+ await this.startupReadyPromise;
651
715
  }
652
716
  /** Wait until deferred builtins have attached (or were skipped). */
653
717
  async waitForDeferredBuiltins() {
@@ -708,6 +772,7 @@ export class InteractiveMode {
708
772
  if (!this.isInitialized) {
709
773
  await this.init();
710
774
  }
775
+ await this.waitForStartupReady();
711
776
  // Check tmux keyboard setup asynchronously
712
777
  this.checkTmuxKeyboardSetup().then((warning) => {
713
778
  if (warning) {
@@ -1275,6 +1340,8 @@ export class InteractiveMode {
1275
1340
  catch (error) {
1276
1341
  this.deferredBuiltinsAttached = false;
1277
1342
  const message = error instanceof Error ? error.message : String(error);
1343
+ if (this.options.startupView)
1344
+ this.startupFeatureError = new Error(message);
1278
1345
  this.showError(`Failed to load deferred extensions: ${message}`);
1279
1346
  }
1280
1347
  finally {
@@ -1935,6 +2002,7 @@ export class InteractiveMode {
1935
2002
  this.editorComponentFactory = factory;
1936
2003
  // Save text from current editor before switching
1937
2004
  const currentText = this.editor.getText();
2005
+ const startupImages = this.options.startupView ? this.editor.getPendingImages?.() : undefined;
1938
2006
  this.editorContainer.clear();
1939
2007
  if (factory) {
1940
2008
  // Create the custom editor with tui, theme, and keybindings
@@ -1943,7 +2011,16 @@ export class InteractiveMode {
1943
2011
  newEditor.onSubmit = this.defaultEditor.onSubmit;
1944
2012
  newEditor.onChange = this.defaultEditor.onChange;
1945
2013
  // Copy text from previous editor
1946
- newEditor.setText(currentText);
2014
+ if (newEditor !== this.editor) {
2015
+ newEditor.setText(currentText);
2016
+ if (startupImages?.length) {
2017
+ if (newEditor.restoreImageMarkers)
2018
+ newEditor.restoreImageMarkers(startupImages);
2019
+ else
2020
+ for (const image of startupImages)
2021
+ this.fallbackImageAttachments.set(image.id, image);
2022
+ }
2023
+ }
1947
2024
  // Copy appearance settings if supported
1948
2025
  if (newEditor.borderColor !== undefined) {
1949
2026
  newEditor.borderColor = this.defaultEditor.borderColor;
@@ -1958,16 +2035,15 @@ export class InteractiveMode {
1958
2035
  // If extending CustomEditor, copy app-level handlers
1959
2036
  // Use duck typing since instanceof fails across jiti module boundaries
1960
2037
  const customEditor = newEditor;
1961
- if ("actionHandlers" in customEditor && customEditor.actionHandlers instanceof Map) {
2038
+ if (newEditor !== this.defaultEditor &&
2039
+ "actionHandlers" in customEditor &&
2040
+ customEditor.actionHandlers instanceof Map) {
1962
2041
  if (!customEditor.onEscape) {
1963
2042
  customEditor.onEscape = () => this.defaultEditor.onEscape?.();
1964
2043
  }
1965
2044
  if (!customEditor.onCtrlD) {
1966
2045
  customEditor.onCtrlD = () => this.defaultEditor.onCtrlD?.();
1967
2046
  }
1968
- if (!customEditor.onPasteImage) {
1969
- customEditor.onPasteImage = () => this.defaultEditor.onPasteImage?.();
1970
- }
1971
2047
  if (!customEditor.onExtensionShortcut) {
1972
2048
  customEditor.onExtensionShortcut = (data) => this.defaultEditor.onExtensionShortcut?.(data);
1973
2049
  }
@@ -1980,7 +2056,8 @@ export class InteractiveMode {
1980
2056
  }
1981
2057
  else {
1982
2058
  // Restore default editor with text from custom editor
1983
- this.defaultEditor.setText(currentText);
2059
+ if (this.editor !== this.defaultEditor)
2060
+ this.defaultEditor.setText(currentText);
1984
2061
  this.editor = this.defaultEditor;
1985
2062
  }
1986
2063
  this.editorContainer.addChild(this.editor);
@@ -2142,6 +2219,7 @@ export class InteractiveMode {
2142
2219
  // lunr: tool/thinking expand is per-item click; ctrl+o is not bound (tree filter still uses ctrl+o).
2143
2220
  this.defaultEditor.onAction("app.thinking.toggle", () => this.toggleThinkingBlockVisibility());
2144
2221
  this.defaultEditor.onAction("app.editor.external", () => this.openExternalEditor());
2222
+ this.defaultEditor.onAction("app.clipboard.pasteImage", () => void this.handleClipboardPaste());
2145
2223
  this.defaultEditor.onAction("app.message.copy", () => void this.handleCopyCommand());
2146
2224
  this.defaultEditor.onAction("app.message.followUp", () => this.handleFollowUp());
2147
2225
  this.defaultEditor.onAction("app.message.dequeue", () => this.handleDequeue());
@@ -2156,12 +2234,6 @@ export class InteractiveMode {
2156
2234
  this.updateEditorBorderColor();
2157
2235
  }
2158
2236
  };
2159
- // Handle clipboard paste (keybinding: app.clipboard.pasteImage, Alt+V on Windows —
2160
- // Ctrl+V is owned by the terminal and never reaches us). Images become `[image_n]`
2161
- // chips; otherwise, paste plain text from the system clipboard.
2162
- this.defaultEditor.onPasteImage = () => {
2163
- void this.handleClipboardPaste();
2164
- };
2165
2237
  this.ui.addInputListener((data) => {
2166
2238
  if (!this.ui.isPinned())
2167
2239
  return;
@@ -2283,7 +2355,7 @@ export class InteractiveMode {
2283
2355
  restoreEditorFromTreeResult(result) {
2284
2356
  this.restoreEditorDraft(result.editorText ?? "", result.editorImages);
2285
2357
  }
2286
- async handleClipboardPaste() {
2358
+ async handleClipboardPaste(options = {}) {
2287
2359
  try {
2288
2360
  const image = await readClipboardImage();
2289
2361
  if (image) {
@@ -2293,13 +2365,15 @@ export class InteractiveMode {
2293
2365
  this.showStatus(`Pasted ${formatImageMarker(id)}`);
2294
2366
  return;
2295
2367
  }
2296
- const text = await readClipboardText();
2297
- if (text) {
2298
- this.editor.insertTextAtCursor?.(text);
2299
- this.ui.requestRender();
2300
- return;
2368
+ if (options.textFallback !== false) {
2369
+ const text = await readClipboardText();
2370
+ if (text) {
2371
+ this.editor.insertTextAtCursor?.(text);
2372
+ this.ui.requestRender();
2373
+ return;
2374
+ }
2301
2375
  }
2302
- this.showStatus("Clipboard contains no image or text");
2376
+ this.showStatus(options.textFallback === false ? "Clipboard contains no image" : "Clipboard contains no image or text");
2303
2377
  }
2304
2378
  catch (err) {
2305
2379
  this.showStatus(`Clipboard paste failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -2315,6 +2389,11 @@ export class InteractiveMode {
2315
2389
  const images = this.consumeStagedSubmitImages() ?? (await this.loadImageAttachments(this.takeSubmittedImages(text)));
2316
2390
  this.ui.setChatScroll(0);
2317
2391
  // Handle commands
2392
+ if (text === "/paste-image") {
2393
+ this.editor.setText("");
2394
+ await this.handleClipboardPaste({ textFallback: false });
2395
+ return;
2396
+ }
2318
2397
  if (text === "/settings") {
2319
2398
  void this.showSettingsSelector();
2320
2399
  this.editor.setText("");
@@ -2734,7 +2813,7 @@ export class InteractiveMode {
2734
2813
  this.ui.requestRender();
2735
2814
  }
2736
2815
  else if (event.message.role === "assistant") {
2737
- this.streamingComponent = new AssistantMessageComponent(undefined, this.hideThinkingBlock, this.getMarkdownThemeWithSettings(), this.hiddenThinkingLabel, this.outputPad, this.settingsManager.getGutterRail(), this.thinkingCollapse);
2816
+ this.streamingComponent = new AssistantMessageComponent(undefined, this.hideThinkingBlock, this.getMarkdownThemeWithSettings(), this.hiddenThinkingLabel, this.outputPad, this.thinkingCollapse);
2738
2817
  // lunr: collapsible reasoning — fresh timing array for this message.
2739
2818
  this.thinkingRunTimings = [];
2740
2819
  this.streamingComponent.setThinkingTimings(this.thinkingRunTimings);
@@ -2871,6 +2950,7 @@ export class InteractiveMode {
2871
2950
  break;
2872
2951
  }
2873
2952
  case "agent_end":
2953
+ this.session.refreshModelFromRegistry();
2874
2954
  if (this.settingsManager.getShowTerminalProgress()) {
2875
2955
  this.ui.terminal.setProgress(false);
2876
2956
  }
@@ -3082,12 +3162,12 @@ export class InteractiveMode {
3082
3162
  // Render user message separately if present
3083
3163
  if (skillBlock.userMessage) {
3084
3164
  this.chatContainer.addChild(new Spacer(1));
3085
- const userComponent = new UserMessageComponent(skillBlock.userMessage, this.getMarkdownThemeWithSettings(), this.outputPad, this.settingsManager.getGutterRail());
3165
+ const userComponent = new UserMessageComponent(skillBlock.userMessage, this.getMarkdownThemeWithSettings(), this.outputPad);
3086
3166
  this.chatContainer.addChild(userComponent);
3087
3167
  }
3088
3168
  }
3089
3169
  else {
3090
- const userComponent = new UserMessageComponent(textContent, this.getMarkdownThemeWithSettings(), this.outputPad, this.settingsManager.getGutterRail());
3170
+ const userComponent = new UserMessageComponent(textContent, this.getMarkdownThemeWithSettings(), this.outputPad);
3091
3171
  this.chatContainer.addChild(userComponent);
3092
3172
  }
3093
3173
  if (options?.populateHistory) {
@@ -3097,7 +3177,7 @@ export class InteractiveMode {
3097
3177
  break;
3098
3178
  }
3099
3179
  case "assistant": {
3100
- const assistantComponent = new AssistantMessageComponent(message, this.hideThinkingBlock, this.getMarkdownThemeWithSettings(), this.hiddenThinkingLabel, this.outputPad, this.settingsManager.getGutterRail(), this.thinkingCollapse);
3180
+ const assistantComponent = new AssistantMessageComponent(message, this.hideThinkingBlock, this.getMarkdownThemeWithSettings(), this.hiddenThinkingLabel, this.outputPad, this.thinkingCollapse);
3101
3181
  // lunr: collapsible reasoning — re-attach live timings when this
3102
3182
  // message was streamed in this session (undefined = history).
3103
3183
  assistantComponent.setThinkingTimings(this.thinkingTimingsByMessage.get(message));
@@ -3891,6 +3971,7 @@ export class InteractiveMode {
3891
3971
  autoResizeImages: this.settingsManager.getImageAutoResize(),
3892
3972
  blockImages: this.settingsManager.getBlockImages(),
3893
3973
  enableSkillCommands: this.settingsManager.getEnableSkillCommands(),
3974
+ skillTagCharacter: this.settingsManager.getSkillTagCharacter(),
3894
3975
  steeringMode: this.session.steeringMode,
3895
3976
  followUpMode: this.session.followUpMode,
3896
3977
  transport: this.settingsManager.getTransport(),
@@ -3921,8 +4002,6 @@ export class InteractiveMode {
3921
4002
  memoryCharCap: this.settingsManager.getMemoryCharCap(),
3922
4003
  searchCurator: getSearchCuratorSetting(),
3923
4004
  // lunr: TUI customize settings
3924
- gutterRail: this.settingsManager.getGutterRail(),
3925
- promptSymbol: this.settingsManager.getPromptSymbol(),
3926
4005
  footerMcp: this.settingsManager.getFooterMcp(),
3927
4006
  footerLsp: this.settingsManager.getFooterLsp(),
3928
4007
  footerContext: this.settingsManager.getFooterContext(),
@@ -3973,6 +4052,10 @@ export class InteractiveMode {
3973
4052
  this.settingsManager.setEnableSkillCommands(enabled);
3974
4053
  this.setupAutocompleteProvider();
3975
4054
  },
4055
+ onSkillTagCharacterChange: (character) => {
4056
+ this.settingsManager.setSkillTagCharacter(character);
4057
+ this.setupAutocompleteProvider();
4058
+ },
3976
4059
  onSteeringModeChange: (mode) => {
3977
4060
  this.session.setSteeringMode(mode);
3978
4061
  },
@@ -4120,12 +4203,6 @@ export class InteractiveMode {
4120
4203
  }
4121
4204
  },
4122
4205
  // lunr: TUI customize callbacks
4123
- onGutterRailChange: (enabled) => {
4124
- this.settingsManager.setGutterRail(enabled);
4125
- },
4126
- onPromptSymbolChange: (enabled) => {
4127
- this.settingsManager.setPromptSymbol(enabled);
4128
- },
4129
4206
  onFooterMcpChange: (enabled) => {
4130
4207
  this.settingsManager.setFooterMcp(enabled);
4131
4208
  },
@@ -4245,40 +4322,9 @@ export class InteractiveMode {
4245
4322
  this.footer.invalidate();
4246
4323
  return;
4247
4324
  }
4248
- const incomplete = result.incomplete;
4249
- const toPrompt = incomplete.slice(0, INCOMPLETE_PROMPT_CAP);
4250
- const extraDefaults = Math.max(0, incomplete.length - toPrompt.length);
4251
- const persisted = [];
4325
+ // Discovered metadata stays in its cache, not in user overrides. Enrichment is automatic.
4326
+ const extraDefaults = result.incomplete.length;
4252
4327
  const asked = [];
4253
- let skipRemaining = false;
4254
- for (const item of toPrompt) {
4255
- if (skipRemaining) {
4256
- persisted.push(modelToUserEntry(item.draft));
4257
- continue;
4258
- }
4259
- const choice = await this.showExtensionSelector(describeIncomplete(item), [
4260
- "Enter details",
4261
- "Use defaults",
4262
- "Skip remaining",
4263
- ]);
4264
- asked.push(`${item.provider}/${item.id}`);
4265
- if (choice === "Skip remaining") {
4266
- skipRemaining = true;
4267
- persisted.push(modelToUserEntry(item.draft));
4268
- continue;
4269
- }
4270
- if (choice === "Enter details") {
4271
- const detailed = await this.promptIncompleteModelDetails(item);
4272
- persisted.push(modelToUserEntry(detailed ?? item.draft));
4273
- continue;
4274
- }
4275
- persisted.push(modelToUserEntry(item.draft));
4276
- }
4277
- for (const item of incomplete.slice(toPrompt.length)) {
4278
- persisted.push(modelToUserEntry(item.draft));
4279
- }
4280
- if (persisted.length > 0)
4281
- runtime.persistUserModels(persisted);
4282
4328
  let ollama;
4283
4329
  const runner = this.session.extensionRunner;
4284
4330
  const cloudRefresh = globalThis[Symbol.for("@lunr/ollama-cloud-refresh")];
@@ -4299,53 +4345,26 @@ export class InteractiveMode {
4299
4345
  evictedUserRows: result.official.evictedUserRows,
4300
4346
  }
4301
4347
  : undefined,
4302
- providers: result.live.map((entry) => ({
4303
- id: entry.providerId,
4304
- status: entry.status,
4305
- added: entry.added,
4306
- total: entry.total,
4307
- error: entry.error,
4308
- })),
4348
+ providers: [
4349
+ ...result.live.map((entry) => ({
4350
+ id: entry.providerId,
4351
+ status: entry.status,
4352
+ added: entry.added,
4353
+ total: entry.total,
4354
+ error: entry.error,
4355
+ })),
4356
+ ...Object.entries(result.official?.errors ?? {}).map(([id, error]) => ({
4357
+ id,
4358
+ status: "error",
4359
+ error,
4360
+ })),
4361
+ ],
4309
4362
  asked,
4310
4363
  extraDefaults,
4311
4364
  ollama,
4312
4365
  }));
4313
4366
  this.footer.invalidate();
4314
4367
  }
4315
- async promptIncompleteModelDetails(item) {
4316
- const contextInput = await this.showExtensionInput(`${item.provider}/${item.id} — context window:`, String(item.draft.contextWindow || 128000));
4317
- if (contextInput === undefined)
4318
- return undefined;
4319
- const maxTokensInput = await this.showExtensionInput(`${item.provider}/${item.id} — max output tokens:`, String(item.draft.maxTokens || 8192));
4320
- if (maxTokensInput === undefined)
4321
- return undefined;
4322
- const inputChoice = await this.showExtensionSelector(`${item.provider}/${item.id} — input:`, [
4323
- "text",
4324
- "text + image",
4325
- ]);
4326
- if (inputChoice === undefined)
4327
- return undefined;
4328
- const reasoningChoice = await this.showExtensionSelector(`${item.provider}/${item.id} — reasoning:`, [
4329
- "yes",
4330
- "no",
4331
- ]);
4332
- if (reasoningChoice === undefined)
4333
- return undefined;
4334
- let api = item.draft.api;
4335
- if (item.availableApis.length > 1) {
4336
- const apiChoice = await this.showExtensionSelector(`${item.provider}/${item.id} — API protocol:`, item.availableApis.map(String));
4337
- if (apiChoice === undefined)
4338
- return undefined;
4339
- api = apiChoice;
4340
- }
4341
- return applyIncompleteAnswers(item.draft, {
4342
- contextWindow: parsePositiveInt(contextInput, item.draft.contextWindow),
4343
- maxTokens: parsePositiveInt(maxTokensInput, item.draft.maxTokens),
4344
- input: inputChoice === "text + image" ? ["text", "image"] : ["text"],
4345
- reasoning: reasoningChoice === "yes",
4346
- api,
4347
- });
4348
- }
4349
4368
  async findExactModelMatch(searchTerm) {
4350
4369
  const models = await this.getModelCandidates();
4351
4370
  return findExactModelReferenceMatch(searchTerm, models);
@@ -6123,6 +6142,7 @@ export class InteractiveMode {
6123
6142
  tools,
6124
6143
  messages: this.session.messages,
6125
6144
  contextWindow,
6145
+ globalAgentsPath: path.join(getAgentDir(), "AGENTS.md"),
6126
6146
  });
6127
6147
  }
6128
6148
  handleFastCommand(arg) {
@@ -6387,7 +6407,7 @@ export class InteractiveMode {
6387
6407
  // the result text the model sees. Esc/close counts as Decline.
6388
6408
  showPlanInChat(summary) {
6389
6409
  this.chatContainer.addChild(new Spacer(1));
6390
- this.chatContainer.addChild(new PlanMessageComponent(summary, this.getMarkdownThemeWithSettings(), this.outputPad, this.settingsManager.getGutterRail()));
6410
+ this.chatContainer.addChild(new PlanMessageComponent(summary, this.getMarkdownThemeWithSettings(), this.outputPad));
6391
6411
  this.ui.requestRender();
6392
6412
  }
6393
6413
  async showPlanApprovalDialog(summary) {
@@ -6601,6 +6621,7 @@ ${cycleThinkingLevel ? `| \`${cycleThinkingLevel}\` | Cycle thinking level |\n`
6601
6621
  | \`${followUp}\` | Queue follow-up message |
6602
6622
  | \`${dequeue}\` | Restore queued messages |
6603
6623
  | \`${pasteImage}\` | Paste image or text from clipboard |
6624
+ | \`/paste-image\` | Paste image without a terminal shortcut |
6604
6625
  | \`/\` | Slash commands |
6605
6626
  | \`!\` | Run bash command |
6606
6627
  | \`!!\` | Run bash command (excluded from context) |
@@ -6769,6 +6790,8 @@ ${cycleThinkingLevel ? `| \`${cycleThinkingLevel}\` | Cycle thinking level |\n`
6769
6790
  }
6770
6791
  }
6771
6792
  stop() {
6793
+ this.stopCatalogRefresh?.();
6794
+ this.stopCatalogRefresh = undefined;
6772
6795
  if (this.settingsManager.getShowTerminalProgress()) {
6773
6796
  this.ui.terminal.setProgress(false);
6774
6797
  }