zidane 6.1.13 → 6.2.6

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 (88) hide show
  1. package/dist/{acp-BqfKqQ9x.js → acp-CYJvnqPa.js} +2 -2
  2. package/dist/{acp-BqfKqQ9x.js.map → acp-CYJvnqPa.js.map} +1 -1
  3. package/dist/acp-cli.js +4 -4
  4. package/dist/acp.d.ts +1 -1
  5. package/dist/acp.js +1 -1
  6. package/dist/{agent-CZ3WSWqR.js → agent-D11SxCvA.js} +155 -75
  7. package/dist/agent-D11SxCvA.js.map +1 -0
  8. package/dist/agent.d.ts +1 -1
  9. package/dist/agent.js +2 -2
  10. package/dist/{anthropic-CV_OSYVD.js → anthropic-DdlSgT6K.js} +47 -25
  11. package/dist/anthropic-DdlSgT6K.js.map +1 -0
  12. package/dist/{auth-D5FfJtLf.js → auth-Ys_7TmRj.js} +2 -2
  13. package/dist/{auth-D5FfJtLf.js.map → auth-Ys_7TmRj.js.map} +1 -1
  14. package/dist/{cache-telemetry-BKAwsNxm.js → cache-telemetry-KJ7B-_Gh.js} +8 -3
  15. package/dist/cache-telemetry-KJ7B-_Gh.js.map +1 -0
  16. package/dist/chat/pure.d.ts +3 -3
  17. package/dist/chat/pure.js +2 -2
  18. package/dist/chat.d.ts +12 -5
  19. package/dist/chat.d.ts.map +1 -1
  20. package/dist/chat.js +7 -7
  21. package/dist/{completion-core-CXYSVhZo.js → completion-core-BRo86R_L.js} +23 -10
  22. package/dist/completion-core-BRo86R_L.js.map +1 -0
  23. package/dist/contexts/daytona.d.ts +1 -1
  24. package/dist/contexts/e2b.d.ts +1 -1
  25. package/dist/eval.d.ts +1 -1
  26. package/dist/extensions.d.ts +2 -2
  27. package/dist/extensions.js +2 -2
  28. package/dist/headless.d.ts +1 -1
  29. package/dist/headless.js +1 -1
  30. package/dist/{index-X02SI0nt.d.ts → index-CNyYEOy0.d.ts} +351 -42
  31. package/dist/index-CNyYEOy0.d.ts.map +1 -0
  32. package/dist/index.d.ts +2 -2
  33. package/dist/index.js +6 -6
  34. package/dist/{generate-data-WL_yGVL6.js → inline-autocomplete-BAwBcZtD.js} +191 -6
  35. package/dist/inline-autocomplete-BAwBcZtD.js.map +1 -0
  36. package/dist/mcp.d.ts +1 -1
  37. package/dist/output/stream-json.d.ts +1 -1
  38. package/dist/output/terminal.d.ts +1 -1
  39. package/dist/presets.d.ts +1 -1
  40. package/dist/presets.js +1 -1
  41. package/dist/providers/anthropic.d.ts +1 -1
  42. package/dist/providers/anthropic.js +1 -1
  43. package/dist/providers/arcee.d.ts +1 -1
  44. package/dist/providers/baseten.d.ts +1 -1
  45. package/dist/providers/cerebras.d.ts +1 -1
  46. package/dist/providers/local.d.ts +1 -1
  47. package/dist/providers/openai-compat.d.ts +1 -1
  48. package/dist/providers/openai.d.ts +1 -1
  49. package/dist/providers/openrouter.d.ts +1 -1
  50. package/dist/providers/xai.d.ts +1 -1
  51. package/dist/providers.d.ts +1 -1
  52. package/dist/providers.js +1 -1
  53. package/dist/restate.d.ts +1 -1
  54. package/dist/session/sqlite.d.ts +1 -1
  55. package/dist/session.d.ts +1 -1
  56. package/dist/skills.d.ts +1 -1
  57. package/dist/{tool-formatters-kg8O6wHZ.d.ts → tool-formatters-B7oWiy5m.d.ts} +2 -2
  58. package/dist/tool-formatters-B7oWiy5m.d.ts.map +1 -0
  59. package/dist/tools/fetch-url.d.ts +1 -1
  60. package/dist/tools/web-search.d.ts +1 -1
  61. package/dist/tools.d.ts +1 -1
  62. package/dist/tools.js +1 -1
  63. package/dist/{transcript-anchors-DAF4RbyU.d.ts → transcript-anchors-BUZ915pM.d.ts} +2 -2
  64. package/dist/{transcript-anchors-DAF4RbyU.d.ts.map → transcript-anchors-BUZ915pM.d.ts.map} +1 -1
  65. package/dist/{transcript-anchors-DAf0Mr_9.js → transcript-anchors-cc4gsU-j.js} +23 -8
  66. package/dist/transcript-anchors-cc4gsU-j.js.map +1 -0
  67. package/dist/tui.d.ts +25 -15
  68. package/dist/tui.d.ts.map +1 -1
  69. package/dist/tui.js +539 -57
  70. package/dist/tui.js.map +1 -1
  71. package/dist/{turn-operations-zkZ1L0eb.d.ts → turn-operations-2Ou_DkbC.d.ts} +3 -3
  72. package/dist/{turn-operations-zkZ1L0eb.d.ts.map → turn-operations-2Ou_DkbC.d.ts.map} +1 -1
  73. package/dist/{turn-operations-D9p-y9EC.js → turn-operations-CXjRaltC.js} +72 -2
  74. package/dist/{turn-operations-D9p-y9EC.js.map → turn-operations-CXjRaltC.js.map} +1 -1
  75. package/dist/types-BIarq1qE.js.map +1 -1
  76. package/dist/types.d.ts +1 -1
  77. package/docs/ARCHITECTURE.md +1 -1
  78. package/docs/EXTENSIONS.md +43 -6
  79. package/package.json +2 -1
  80. package/scripts/eval.ts +21 -3
  81. package/dist/agent-CZ3WSWqR.js.map +0 -1
  82. package/dist/anthropic-CV_OSYVD.js.map +0 -1
  83. package/dist/cache-telemetry-BKAwsNxm.js.map +0 -1
  84. package/dist/completion-core-CXYSVhZo.js.map +0 -1
  85. package/dist/generate-data-WL_yGVL6.js.map +0 -1
  86. package/dist/index-X02SI0nt.d.ts.map +0 -1
  87. package/dist/tool-formatters-kg8O6wHZ.d.ts.map +0 -1
  88. package/dist/transcript-anchors-DAf0Mr_9.js.map +0 -1
package/dist/tui.js CHANGED
@@ -1,5 +1,5 @@
1
- import { $ as useMcpAuthState, $n as buildPlanSystem, $t as restoreChainFallbacks, A as runOAuthLogin, Bt as createDiscoverySlot, Ct as SETTINGS_CHOICES, D as formatPathForCwd, Dt as clampFps, Et as SettingsProvider, G as buildVisibleMcpRows, Gt as useConfig, Ht as useDiscovery, J as createFileMcpCredentialStore, Jt as buildRunModel, K as indexOfServerRow, Kt as resolveConfig, L as loadMcpToolsCache, M as buildMcpServers, Mt as resolveTheme, O as fetchOAuthRedirect, Ot as useSettings, P as discoverProjectMcps, Q as useMcpAuthDispatch, Qn as buildBuildSystem, Qt as providerKeySetChanged, R as mcpToolsCachePath, St as SETTINGS_CATEGORIES, T as suggestSafelistEntries, Tn as accentColor, Tt as SETTINGS_TOGGLES, U as useMcpToolToggleMap, Ut as useDiscoveryOptional, V as subscribeMcpToolsCache, Vn as useActiveTodos, Vt as DiscoveryProvider, Wt as ConfigProvider, Xt as chainsEqual, Yt as chainPositionOf, Z as McpAuthProvider, Zt as filterChainFallbacks, _ as useSafeModeQueue, _n as discoverAgentsMd, _r as toolCallPreview, _t as buildHints, a as useSurfaces, an as bootTick, at as buildResumedToolResultsTurn, b as getSafelist, br as usageByModel, bt as useEnabledToggleSet, c as useStreamBuffer, cr as lastContextSizeFromTurns, ct as makeRequestInteraction, d as discoverProjectSkills, dr as marginTopFor, dt as useInteractionsActions, er as envSection, et as getMcpAuthStatus, ft as useInteractionsQueue, g as useSafeModeActions, gn as chatAutoCompactBehavior, gt as truncateTrailing, h as SafeModeProvider, hr as sumRunCosts, ht as hintsLength, i as useSelectStyle, ir as compactSummaryEvent, it as PRESENT_PLAN_TOOL, j as supportsOAuth, jn as TODO_STATUS_GLYPHS, jt as resolveChipColor, k as oauthUsesManualCodePaste, l as buildSkillsConfig, lr as listSessionMeta, lt as pendingInteractionsFromTurns, m as writeSessionExport, mr as stripSpawnTokensLine, mt as clipHintsToWidth, n as ThemeProvider, nn as useCompletion, nr as createChatTurnsReadTool, nt as ASK_USER_TOOL, on as buildUpdateHint, or as deriveSessionTitle, ot as createInteractionTools, pr as serverToolResultSummary, pt as EMPTY_HINTS, q as parentServerName, qt as resolveStoragePaths, r as useColors, rn as tryOpenBrowser, rt as InteractionsProvider, s as useTheme, sn as useUpdateCheck, sr as eventsFromTurns, t as computeTurnAnchors, tn as toggleChainEntry, tr as chatReferenceTokensForPrompt, u as defaultSkillScanPaths, ur as loadState, vr as toolResultText, wt as SETTINGS_INPUTS, x as isOnSafelist, xt as DEFAULT_SETTINGS, y as addToSafelist, yr as updateToolEventOutcomes, yt as listProjectFiles, z as refreshMcpToolsCatalog } from "./transcript-anchors-DAf0Mr_9.js";
2
- import { $ as composeExtensionPreset, B as ExtensionInstallError, Bt as ensureKeybindingsFile, Et as createModelSearchTool, H as installExtensionFromSpec, Ht as groupBindings, J as createExtensionHud, K as removeInstalledExtension, P as summaryToTurn, St as tailTruncate, Ut as keybindingsPath, Vt as formatBindingForDisplay, Wt as matchesBinding, X as discoverAndBuildExtensionRegistry, Y as registerExtensionHostModules, Yt as resolveExtensionKeybindings, _ as buildPostCompactAttachments, c as applyCompactSummaryCutoff, en as inlineLoadedExtension, et as disposeExtensionRegistry, f as cleanupPersistedSession, g as resolveTasksDir, h as resolvePersistDir, ht as isSpawnTool, it as createExtensionHostRuntimeBridge, lt as basic_default, m as resolveMcpWarningsDir, mn as effectiveContextWindow, nn as parseExtensionScanPathEnv, on as coerceExtensionConfigInput, pn as OUTPUT_RESERVE_TOKENS, q as updateInstalledExtension, rn as scanExtensionDirectories, rt as partitionExtensionCatalog, t as createAgent, tt as filterExtensionRegistry, un as resolveExtensionConfigValue, wt as looksBinary, x as compactConversation, y as selectFilesFromSession, z as createExtensionPromptBridge, zt as KEYBINDING_KEY_COL_WIDTH } from "./agent-CZ3WSWqR.js";
1
+ import { $ as useMcpAuthState, $n as buildPlanSystem, $t as restoreChainFallbacks, A as runOAuthLogin, Bt as createDiscoverySlot, Ct as SETTINGS_CHOICES, D as formatPathForCwd, Dt as clampFps, Et as SettingsProvider, G as buildVisibleMcpRows, Gt as useConfig, Ht as useDiscovery, J as createFileMcpCredentialStore, Jt as buildRunModel, K as indexOfServerRow, Kt as resolveConfig, L as loadMcpToolsCache, M as buildMcpServers, Mt as resolveTheme, O as fetchOAuthRedirect, Ot as useSettings, P as discoverProjectMcps, Q as useMcpAuthDispatch, Qn as buildBuildSystem, Qt as providerKeySetChanged, R as mcpToolsCachePath, St as SETTINGS_CATEGORIES, T as suggestSafelistEntries, Tn as accentColor, Tt as SETTINGS_TOGGLES, U as useMcpToolToggleMap, Ut as useDiscoveryOptional, V as subscribeMcpToolsCache, Vn as useActiveTodos, Vt as DiscoveryProvider, Wt as ConfigProvider, Xt as chainsEqual, Yt as chainPositionOf, Z as McpAuthProvider, Zt as filterChainFallbacks, _ as useSafeModeQueue, _n as discoverAgentsMd, _r as toolCallPreview, _t as buildHints, a as useSurfaces, an as bootTick, at as buildResumedToolResultsTurn, b as getSafelist, br as usageByModel, bt as useEnabledToggleSet, c as useStreamBuffer, cr as lastContextSizeFromTurns, ct as makeRequestInteraction, d as discoverProjectSkills, dr as marginTopFor, dt as useInteractionsActions, er as envSection, et as getMcpAuthStatus, ft as useInteractionsQueue, g as useSafeModeActions, gn as chatAutoCompactBehavior, gt as truncateTrailing, h as SafeModeProvider, hr as sumRunCosts, ht as hintsLength, i as useSelectStyle, ir as compactSummaryEvent, it as PRESENT_PLAN_TOOL, j as supportsOAuth, jn as TODO_STATUS_GLYPHS, jt as resolveChipColor, k as oauthUsesManualCodePaste, l as buildSkillsConfig, lr as listSessionMeta, lt as pendingInteractionsFromTurns, m as writeSessionExport, mr as stripSpawnTokensLine, mt as clipHintsToWidth, n as ThemeProvider, nn as useCompletion, nr as createChatTurnsReadTool, nt as ASK_USER_TOOL, on as buildUpdateHint, or as deriveSessionTitle, ot as createInteractionTools, pr as serverToolResultSummary, pt as EMPTY_HINTS, q as parentServerName, qt as resolveStoragePaths, r as useColors, rn as tryOpenBrowser, rt as InteractionsProvider, s as useTheme, sn as useUpdateCheck, sr as eventsFromTurns, t as computeTurnAnchors, tn as toggleChainEntry, tr as chatReferenceTokensForPrompt, u as defaultSkillScanPaths, ur as loadState, vr as toolResultText, wt as SETTINGS_INPUTS, x as isOnSafelist, xt as DEFAULT_SETTINGS, y as addToSafelist, yr as updateToolEventOutcomes, yt as listProjectFiles, z as refreshMcpToolsCatalog } from "./transcript-anchors-cc4gsU-j.js";
2
+ import { $ as composeExtensionPreset, B as ExtensionInstallError, Bt as ensureKeybindingsFile, Et as createModelSearchTool, H as installExtensionFromSpec, Ht as groupBindings, J as createExtensionHud, K as removeInstalledExtension, P as summaryToTurn, St as tailTruncate, Ut as keybindingsPath, Vt as formatBindingForDisplay, Wt as matchesBinding, X as discoverAndBuildExtensionRegistry, Y as registerExtensionHostModules, Yt as resolveExtensionKeybindings, _ as buildPostCompactAttachments, c as applyCompactSummaryCutoff, dn as sanitizeExtensionConfigActionPatch, en as inlineLoadedExtension, et as disposeExtensionRegistry, f as cleanupPersistedSession, g as resolveTasksDir, h as resolvePersistDir, hn as effectiveContextWindow, ht as isSpawnTool, it as createExtensionHostRuntimeBridge, lt as basic_default, m as resolveMcpWarningsDir, mn as OUTPUT_RESERVE_TOKENS, nn as parseExtensionScanPathEnv, on as coerceExtensionConfigInput, q as updateInstalledExtension, rn as scanExtensionDirectories, rt as partitionExtensionCatalog, t as createAgent, tt as filterExtensionRegistry, un as resolveExtensionConfigValue, wt as looksBinary, x as compactConversation, y as selectFilesFromSession, z as createExtensionPromptBridge, zt as KEYBINDING_KEY_COL_WIDTH } from "./agent-D11SxCvA.js";
3
3
  import { a as formatDuration, c as previewLine, i as fmtTokens, l as shortId, n as compactPath, o as formatTaskStatus, r as fmtCost, s as formatTaskSummary, t as ageString } from "./format-BF745pLL.js";
4
4
  import { n as appendCapped } from "./output-cap-kBUHBlnq.js";
5
5
  import { i as killProcessGroup, n as SUPPORTS_PROCESS_GROUPS, r as createProcessContext } from "./contexts-A4ep-iJs.js";
@@ -12,14 +12,14 @@ import { replaceDynamicSection } from "./prompt.js";
12
12
  import { a as getExtensionSessionState, i as demoteStaleRunningStatus, o as loadSession, s as setExtensionSessionState, t as createSession } from "./session-D7JSEMME.js";
13
13
  import { o as withTimeout } from "./timeout-BDetbuN6.js";
14
14
  import { n as connectMcpServers, u as McpOAuthProvider } from "./mcp-CkHUripi.js";
15
- import { c as commandBlockToEvent, d as executeCommand, f as parseCommandInvocation, i as shouldAutoGenerateTitle, l as commandBlocksToContent, r as generateSessionTitle, t as generateStructuredData } from "./generate-data-WL_yGVL6.js";
16
- import { s as resolveCompletionSubmit } from "./completion-core-CXYSVhZo.js";
15
+ import { a as generateStructuredData, c as shouldAutoGenerateTitle, f as commandBlockToEvent, g as parseCommandInvocation, h as executeCommand, n as patchInlineGhost, p as commandBlocksToContent, r as resolveInlineSuggestion, s as generateSessionTitle, t as createPathAutocompleteProvider } from "./inline-autocomplete-BAwBcZtD.js";
16
+ import { s as resolveCompletionSubmit } from "./completion-core-BRo86R_L.js";
17
17
  import { i as installSkillFromSpec, r as SkillInstallError } from "./skills-BlmY7QhV.js";
18
18
  import { i as ZIDANE_EXTENSION_SKILL_NAME, l as withZidaneExtensionSkill, o as extensionDirForCwd, r as EXTENSION_EDIT_METADATA_KEY, s as extensionScaffold, t as loginMcpServer } from "./login-Bp_tRj_e.js";
19
- import { C as restoreModelOptions, S as piIdOf, _ as modelOptionsFor, m as getContextWindow, p as enabledModelOptions, s as setProviderCredential, t as detectAuth, v as modelSupportsReasoning } from "./auth-D5FfJtLf.js";
19
+ import { C as restoreModelOptions, S as piIdOf, _ as modelOptionsFor, m as getContextWindow, p as enabledModelOptions, s as setProviderCredential, t as detectAuth, v as modelSupportsReasoning } from "./auth-Ys_7TmRj.js";
20
20
  import { createDockerContext } from "./contexts/docker.js";
21
21
  import { createTuiStore } from "./session/sqlite.js";
22
- import { A as buildLinearRamp, D as createFilesCompletionProvider, F as isUserBashEvent, H as buildUnifiedDiff, I as isVisible, J as previewEditPayload, K as extractEditPayload, L as promptHistoryFromEvents, M as EDIT_TOOL_NAMES, N as isEditErrorResult, P as isTurnHighlighted, Q as buildEditOutcomesAnnotation, R as selectableTurnIds, S as splitMarkdownCodeBlocks, T as uniqueSkillNamesFromReferences, U as clipDiffForRender, V as buildContextualDiff, X as summarizeEditPayload, _ as filterModelCatalog, a as TOOL_DISPLAY, at as summarizeOutcomes, b as chunkMarkdownForRender, ct as createChatsCompletionProvider, d as finalizeStreamingMarkdown, et as mergeApprovalAndBodyOutcomes, f as finalizeStreamingMarkdownForOwner, g as buildModelCatalog, h as splitPromptSegments, i as turnAsText, it as stripEditOutcomesAnnotation, j as BASH_CALL_ID_PREFIX, k as blendHsl, m as turnContextSize, n as deleteTurnSafely, nt as resolveApprovalForPayload, o as displayNameFor, q as filetypeFromPath, r as truncateTurnsAt, rt as rewriteMultiEditHeader, s as formatToolCall, tt as parseEditOutcomesFromResult, u as capTranscriptEvents, v as indexOfEntry, w as createSkillsCompletionProvider, x as normalizeMarkdownCodeFences, z as turnSelectionOwnership } from "./turn-operations-D9p-y9EC.js";
22
+ import { $ as buildEditOutcomesAnnotation, A as blendHsl, B as turnSelectionOwnership, C as stripHtmlCommentsOutsideCode, E as uniqueSkillNamesFromReferences, F as isTurnHighlighted, H as buildContextualDiff, I as isUserBashEvent, J as filetypeFromPath, L as isVisible, M as BASH_CALL_ID_PREFIX, N as EDIT_TOOL_NAMES, O as createFilesCompletionProvider, P as isEditErrorResult, R as promptHistoryFromEvents, S as splitMarkdownCodeBlocks, T as createSkillsCompletionProvider, U as buildUnifiedDiff, W as clipDiffForRender, Y as previewEditPayload, Z as summarizeEditPayload, _ as filterModelCatalog, a as TOOL_DISPLAY, at as stripEditOutcomesAnnotation, b as chunkMarkdownForRender, d as finalizeStreamingMarkdown, f as finalizeStreamingMarkdownForOwner, g as buildModelCatalog, h as splitPromptSegments, i as turnAsText, it as rewriteMultiEditHeader, j as buildLinearRamp, lt as createChatsCompletionProvider, m as turnContextSize, n as deleteTurnSafely, nt as parseEditOutcomesFromResult, o as displayNameFor, ot as summarizeOutcomes, q as extractEditPayload, r as truncateTurnsAt, rt as resolveApprovalForPayload, s as formatToolCall, tt as mergeApprovalAndBodyOutcomes, u as capTranscriptEvents, v as indexOfEntry, x as normalizeMarkdownCodeFences, z as selectableTurnIds } from "./turn-operations-CXjRaltC.js";
23
23
  import { basename, dirname, join, relative, resolve } from "node:path";
24
24
  import { Buffer } from "node:buffer";
25
25
  import * as fs from "node:fs";
@@ -50,16 +50,18 @@ function scoreCommands(catalog, query) {
50
50
  const scored = [];
51
51
  for (const command of catalog) {
52
52
  const name = command.name.toLowerCase();
53
+ const aliases = (command.aliases ?? []).map((a) => a.toLowerCase());
54
+ const group = command.group?.toLowerCase();
53
55
  const desc = command.description.toLowerCase();
54
56
  if (q.length === 0) scored.push({
55
57
  command,
56
58
  rank: 4
57
59
  });
58
- else if (name.startsWith(q)) scored.push({
60
+ else if (name.startsWith(q) || aliases.some((a) => a.startsWith(q))) scored.push({
59
61
  command,
60
62
  rank: 0
61
63
  });
62
- else if (name.includes(q)) scored.push({
64
+ else if (name.includes(q) || aliases.some((a) => a.includes(q)) || group !== void 0 && q.length >= 2 && group.startsWith(q)) scored.push({
63
65
  command,
64
66
  rank: 1
65
67
  });
@@ -76,7 +78,7 @@ function scoreCommands(catalog, query) {
76
78
  return scored.map(({ command }) => ({
77
79
  id: command.name,
78
80
  label: command.name,
79
- description: command.description,
81
+ description: command.aliases?.length ? `${command.description} (alias: ${command.aliases.map((a) => `/${a}`).join(", ")})` : command.description,
80
82
  insertText: `/${command.name} `,
81
83
  data: command
82
84
  }));
@@ -97,7 +99,7 @@ function createCommandsCompletionProvider(opts) {
97
99
  if (spaceIdx !== -1) {
98
100
  if (ctx.text.trimStart()[0] !== "/") return [];
99
101
  const typed = query.slice(0, spaceIdx).toLowerCase();
100
- const command = opts.getCommands().find((c) => c.name.toLowerCase() === typed);
102
+ const command = opts.getCommands().find((c) => c.name.toLowerCase() === typed || c.aliases?.some((a) => a.toLowerCase() === typed));
101
103
  if (!command?.completeArgs) return [];
102
104
  const argsPrefix = query.slice(spaceIdx + 1).replace(/^\s+/, "");
103
105
  return Promise.resolve(command.completeArgs(argsPrefix)).then((suggestions) => (suggestions ?? []).map((s) => ({
@@ -1254,6 +1256,36 @@ function RenderCall({ render }) {
1254
1256
  return /* @__PURE__ */ jsx$1(Fragment, { children: render() });
1255
1257
  }
1256
1258
  //#endregion
1259
+ //#region src/tui/terminal-sanitize.ts
1260
+ /**
1261
+ * Terminal-display sanitation.
1262
+ *
1263
+ * Provider and model text is written into a terminal cell buffer that does
1264
+ * NOT neutralize control bytes: a raw ESC sequence in model output would be
1265
+ * forwarded to the user's terminal at paint time (recolor, cursor moves,
1266
+ * window retitling — classic escape injection). Applied once at the
1267
+ * rendering boundary — persistence and provider round-trips keep raw text.
1268
+ *
1269
+ * Lives in `src/tui` (not `src/chat`): the chat layer is renderer-agnostic
1270
+ * by invariant and must not carry escape-sequence literals.
1271
+ */
1272
+ /** CSI sequences: ESC [ params intermediates final. */
1273
+ const ANSI_CSI = /\u001B\[[0-9:;<=>?]*[ -/]*[@-~]/g;
1274
+ /** OSC sequences (window title, hyperlinks, …), terminated by BEL or ST. */
1275
+ const ANSI_OSC = /\u001B\][\s\S]*?(?:\u0007|\u001B\\)/g;
1276
+ /** Any remaining ESC + one char (SS2/SS3, unterminated introducers, …). */
1277
+ const ANSI_ESC_OTHER = /\u001B[\s\S]?/g;
1278
+ /** C0 controls except \n and \t (\r is normalized to \n beforehand), plus DEL. */
1279
+ const CONTROL_CHARS = /[\u0000-\u0008\v\f\u000E-\u001F\u007F]/g;
1280
+ /**
1281
+ * Make arbitrary provider/model/tool text safe to paint into a terminal
1282
+ * cell buffer: normalize \r to \n, drop ANSI escape sequences, drop the
1283
+ * remaining C0 control characters (keeping \n and \t).
1284
+ */
1285
+ function stripTerminalControlChars(text) {
1286
+ return text.replace(/\r\n?/g, "\n").replace(ANSI_OSC, "").replace(ANSI_CSI, "").replace(ANSI_ESC_OTHER, "").replace(CONTROL_CHARS, "");
1287
+ }
1288
+ //#endregion
1257
1289
  //#region src/tui/theme.ts
1258
1290
  /**
1259
1291
  * Resolve a theme color string to an OpenTUI `RGBA`.
@@ -2138,7 +2170,8 @@ function rowStyle(paddingLeft) {
2138
2170
  function EventLineImpl({ event, previous, depthOffset = 0, hideChildLabel = false, selected = false, settleCodeEpoch = 0 }) {
2139
2171
  const COLOR = useColors();
2140
2172
  const { settings } = useSettings();
2141
- const safeText = event.text === "" ? " " : event.text;
2173
+ const cleanText = stripTerminalControlChars(event.text);
2174
+ const safeText = cleanText === "" ? " " : cleanText;
2142
2175
  const effectiveDepth = Math.max(0, (event.depth ?? 0) - depthOffset);
2143
2176
  const row = rowStyle(indentFor(settings.hideSubagentOutput && (event.kind === "spawn-start" || event.kind === "spawn-end") ? 0 : effectiveDepth));
2144
2177
  const child = isChild(event);
@@ -2158,7 +2191,8 @@ function EventLineImpl({ event, previous, depthOffset = 0, hideChildLabel = fals
2158
2191
  });
2159
2192
  case "thinking": {
2160
2193
  const startsRun = !(previous?.kind === "thinking" && previous.childId === event.childId);
2161
- const thinkingText = safeText.replace(/\*\*([^*]+)\*\*/g, "$1");
2194
+ const thinkingText = safeText.replace(/<!--[\s\S]*?-->/g, "").replace(/\*\*([^*]+)\*\*/g, "$1").trimEnd();
2195
+ if (thinkingText.trim() === "") return null;
2162
2196
  return /* @__PURE__ */ jsx$1("box", {
2163
2197
  style: row,
2164
2198
  children: /* @__PURE__ */ jsxs("box", {
@@ -2900,7 +2934,7 @@ const MarkdownBlock = memo(({ text, dim, selected = false, settleCodeEpoch = 0 }
2900
2934
  dimmed: dim
2901
2935
  });
2902
2936
  const renderer = useRenderer();
2903
- const content = normalizeMarkdownCodeFences(text.replace(/^\n+|\n+$/g, ""));
2937
+ const content = normalizeMarkdownCodeFences(stripHtmlCommentsOutsideCode(stripTerminalControlChars(text)).replace(/^\n+|\n+$/g, ""));
2904
2938
  const chunks = useMemo(() => chunkMarkdownForRender(content), [content]);
2905
2939
  const bag = useRef({
2906
2940
  ctx: renderer,
@@ -5998,17 +6032,48 @@ function CompletionPopup({ state, visibleRows = 6 }) {
5998
6032
  });
5999
6033
  height = 3;
6000
6034
  } else {
6001
- const rows = Math.min(state.items.length, visibleRows);
6035
+ const showSections = state.items.some((it) => it.section);
6036
+ const displayRows = [];
6037
+ let prevSection;
6038
+ for (const [index, item] of state.items.entries()) {
6039
+ if (showSections && item.section && item.section !== prevSection) {
6040
+ displayRows.push({
6041
+ kind: "header",
6042
+ section: item.section
6043
+ });
6044
+ prevSection = item.section;
6045
+ }
6046
+ displayRows.push({
6047
+ kind: "item",
6048
+ item,
6049
+ index
6050
+ });
6051
+ }
6052
+ const rows = Math.min(displayRows.length, visibleRows);
6053
+ const selectedRow = displayRows.findIndex((r) => r.kind === "item" && r.index === state.selectedIndex);
6002
6054
  const half = Math.floor(rows / 2);
6003
- let start = Math.max(0, state.selectedIndex - half);
6004
- if (start + rows > state.items.length) start = state.items.length - rows;
6005
- const slice = state.items.slice(start, start + rows);
6006
- const showSections = new Set(state.items.map((it) => it.section).filter(Boolean)).size > 1;
6055
+ let start = Math.max(0, selectedRow - half);
6056
+ if (start + rows > displayRows.length) start = displayRows.length - rows;
6057
+ const slice = displayRows.slice(start, start + rows);
6007
6058
  height = 2 + rows + 1;
6008
6059
  body = /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx$1("box", {
6009
6060
  style: { flexDirection: "column" },
6010
- children: slice.map((item, i) => {
6011
- const focused = start + i === state.selectedIndex;
6061
+ children: slice.map((row, i) => {
6062
+ if (row.kind === "header") return /* @__PURE__ */ jsx$1("box", {
6063
+ style: {
6064
+ height: 1,
6065
+ overflow: "hidden",
6066
+ flexShrink: 0
6067
+ },
6068
+ children: /* @__PURE__ */ jsx$1("text", {
6069
+ fg: COLOR.mute,
6070
+ wrapMode: "none",
6071
+ children: `── ${row.section} ${"─".repeat(200)}`
6072
+ })
6073
+ }, `header:${row.section}:${start + i}`);
6074
+ const { item, index } = row;
6075
+ const focused = index === state.selectedIndex;
6076
+ const titleFg = (item.providerId !== void 0 ? SURFACE.chips[item.providerId] : void 0)?.bg ?? (item.providerId === "commands" ? COLOR.accent : SELECT.textColor);
6012
6077
  return /* @__PURE__ */ jsx$1("box", {
6013
6078
  style: {
6014
6079
  height: 1,
@@ -6024,12 +6089,8 @@ function CompletionPopup({ state, visibleRows = 6 }) {
6024
6089
  fg: focused ? COLOR.brand : COLOR.mute,
6025
6090
  children: focused ? "▶ " : " "
6026
6091
  }),
6027
- showSections && item.section && /* @__PURE__ */ jsxs("span", {
6028
- fg: COLOR.mute,
6029
- children: [item.section, " "]
6030
- }),
6031
6092
  /* @__PURE__ */ jsx$1("span", {
6032
- fg: focused ? COLOR.brand : SELECT.textColor,
6093
+ fg: focused ? COLOR.brand : titleFg,
6033
6094
  children: item.label
6034
6095
  }),
6035
6096
  item.description && /* @__PURE__ */ jsxs("span", {
@@ -9463,7 +9524,7 @@ const MIME_BY_EXT = {
9463
9524
  * default-value reference at the prop destructure.
9464
9525
  */
9465
9526
  const EMPTY_QUEUED_MESSAGES = [];
9466
- function ChatScreen({ cwd, events, busy, compacting = false, queuedMessages = EMPTY_QUEUED_MESSAGES, queueSelectionIndex = null, queueShortcuts, onEnterQueueFromEmptyPrompt, settings, onSubmit, session, pending, onApproval, pendingInteraction, onInteraction, completionProviders, onCompletionTab, enterSelectsCompletion, onPopupOpenChange, selectedTurnId, promptTriggerHints, attachImageBinding, editorBinding, sessionDetailsBinding, liveSession = null }) {
9527
+ function ChatScreen({ cwd, events, busy, compacting = false, queuedMessages = EMPTY_QUEUED_MESSAGES, queueSelectionIndex = null, queueShortcuts, onEnterQueueFromEmptyPrompt, settings, onSubmit, session, pending, onApproval, pendingInteraction, onInteraction, completionProviders, inlineAutocompleteProviders, onCompletionTab, enterSelectsCompletion, onPopupOpenChange, selectedTurnId, promptTriggerHints, attachImageBinding, editorBinding, sessionDetailsBinding, liveSession = null }) {
9467
9528
  const COLOR = useColors();
9468
9529
  const titleText = session?.title ?? "New session";
9469
9530
  const sessionShortcutKey = formatBindingForDisplay(sessionDetailsBinding);
@@ -9577,6 +9638,7 @@ function ChatScreen({ cwd, events, busy, compacting = false, queuedMessages = EM
9577
9638
  userPrompts,
9578
9639
  onSubmit,
9579
9640
  completionProviders,
9641
+ inlineAutocompleteProviders,
9580
9642
  onCompletionTab,
9581
9643
  enterSelectsCompletion,
9582
9644
  onPopupOpenChange: handlePopupOpenChange,
@@ -9898,7 +9960,15 @@ function previewText(text) {
9898
9960
  }
9899
9961
  /** Stable empty providers reference — avoids `useCompletion` rerun on every render when no providers are wired. */
9900
9962
  const EMPTY_PROVIDERS = [];
9901
- function PromptBlock({ userPrompts, onSubmit, completionProviders, onCompletionTab, enterSelectsCompletion, onPopupOpenChange, selectMode = null, triggerHints, busy = false, onEnterQueueFromEmpty, attachImageBinding, editorBinding }) {
9963
+ /** Stable empty reference for the inline-autocomplete effect's dep array. */
9964
+ const EMPTY_INLINE_PROVIDERS = [];
9965
+ /**
9966
+ * Debounce before querying inline-autocomplete providers. Long enough to
9967
+ * skip intermediate keystrokes while typing a path, short enough that the
9968
+ * ghost feels immediate on a pause.
9969
+ */
9970
+ const INLINE_GHOST_DEBOUNCE_MS = 120;
9971
+ function PromptBlock({ userPrompts, onSubmit, completionProviders, inlineAutocompleteProviders, onCompletionTab, enterSelectsCompletion, onPopupOpenChange, selectMode = null, triggerHints, busy = false, onEnterQueueFromEmpty, attachImageBinding, editorBinding }) {
9902
9972
  const focused = useModalAwareFocus();
9903
9973
  const renderer = useRenderer();
9904
9974
  const COLOR = useColors();
@@ -9984,6 +10054,9 @@ function PromptBlock({ userPrompts, onSubmit, completionProviders, onCompletionT
9984
10054
  shellModeRef.current = on;
9985
10055
  setShellMode(on);
9986
10056
  }, []);
10057
+ const [ghost, setGhost] = useState(null);
10058
+ const ghostRef = useRef(null);
10059
+ ghostRef.current = ghost;
9987
10060
  /**
9988
10061
  * Pull the latest buffer state from the OpenTUI textarea ref. Called from
9989
10062
  * `onContentChange` + `onKeyDown` so cursor moves (without text changes)
@@ -10001,9 +10074,49 @@ function PromptBlock({ userPrompts, onSubmit, completionProviders, onCompletionT
10001
10074
  text: ta.plainText,
10002
10075
  cursor: ta.cursorOffset
10003
10076
  });
10077
+ setGhost((prev) => patchInlineGhost(prev, ta.plainText, ta.cursorOffset));
10004
10078
  const total = ta.editorView.getTotalVirtualLineCount();
10005
10079
  setContentLines(Math.max(MIN_CONTENT_LINES, total));
10006
10080
  }, []);
10081
+ const inlineProviders = inlineAutocompleteProviders ?? EMPTY_INLINE_PROVIDERS;
10082
+ const inlineEnabled = settings.inlineAutocomplete && inlineProviders.length > 0;
10083
+ useEffect(() => {
10084
+ const atWordBoundary = bufferState.cursor === bufferState.text.length || /\s/.test(bufferState.text[bufferState.cursor]);
10085
+ if (!inlineEnabled || popupOpen || selectMode !== null || shellMode || bufferState.text.length === 0 || !atWordBoundary) {
10086
+ setGhost(null);
10087
+ return;
10088
+ }
10089
+ const controller = new AbortController();
10090
+ const { text, cursor } = bufferState;
10091
+ const timer = setTimeout(() => {
10092
+ resolveInlineSuggestion(inlineProviders, {
10093
+ text,
10094
+ cursor,
10095
+ cwd: process.cwd(),
10096
+ signal: controller.signal
10097
+ }).then((res) => {
10098
+ if (controller.signal.aborted) return;
10099
+ const suffixes = (res?.suffixes ?? []).map((s) => s.split("\n")[0]).filter((s) => s.length > 0);
10100
+ setGhost(suffixes.length > 0 ? {
10101
+ suffixes,
10102
+ index: 0,
10103
+ text,
10104
+ cursor
10105
+ } : null);
10106
+ });
10107
+ }, INLINE_GHOST_DEBOUNCE_MS);
10108
+ return () => {
10109
+ controller.abort();
10110
+ clearTimeout(timer);
10111
+ };
10112
+ }, [
10113
+ bufferState,
10114
+ inlineEnabled,
10115
+ popupOpen,
10116
+ selectMode,
10117
+ shellMode,
10118
+ inlineProviders
10119
+ ]);
10007
10120
  /**
10008
10121
  * Open the current prompt in `$EDITOR` (git-style round-trip). The
10009
10122
  * external-editor module is pure; the TUI handoff is owned here — suspend
@@ -10255,6 +10368,30 @@ function PromptBlock({ userPrompts, onSubmit, completionProviders, onCompletionT
10255
10368
  return;
10256
10369
  }
10257
10370
  if (event.ctrl || event.shift || event.meta) return;
10371
+ if (ghostRef.current) {
10372
+ const g = ghostRef.current;
10373
+ if (event.name === "tab") {
10374
+ const ta = textareaRef.current;
10375
+ if (ta && ta.plainText === g.text && ta.cursorOffset === g.cursor) {
10376
+ event.preventDefault();
10377
+ ta.insertText(g.suffixes[g.index]);
10378
+ setGhost(null);
10379
+ syncBuffer();
10380
+ return;
10381
+ }
10382
+ }
10383
+ if ((event.name === "up" || event.name === "down") && g.suffixes.length > 1) {
10384
+ event.preventDefault();
10385
+ const delta = event.name === "down" ? 1 : -1;
10386
+ setGhost({
10387
+ ...g,
10388
+ index: (g.index + delta + g.suffixes.length) % g.suffixes.length
10389
+ });
10390
+ return;
10391
+ }
10392
+ if (event.name === "escape") setGhost(null);
10393
+ else if (event.name === "left" || event.name === "right" || event.name === "home" || event.name === "end" || event.name === "pageup" || event.name === "pagedown" || event.name === "up" || event.name === "down") setGhost(null);
10394
+ }
10258
10395
  if (event.name !== "up" && event.name !== "down") return;
10259
10396
  const buffer = textareaRef.current;
10260
10397
  if (!buffer) return;
@@ -10292,6 +10429,27 @@ function PromptBlock({ userPrompts, onSubmit, completionProviders, onCompletionT
10292
10429
  attachments,
10293
10430
  showNotice
10294
10431
  ]);
10432
+ const ghostPosition = useMemo(() => {
10433
+ if (!ghost) return null;
10434
+ const ta = textareaRef.current;
10435
+ if (!ta || ta.plainText !== ghost.text || ta.cursorOffset !== ghost.cursor) return null;
10436
+ const cursor = ta.visualCursor;
10437
+ if (cursor.visualRow < 0 || cursor.visualRow >= Math.min(MAX_CONTENT_LINES, contentLines)) return null;
10438
+ return {
10439
+ row: cursor.visualRow,
10440
+ col: cursor.visualCol
10441
+ };
10442
+ }, [
10443
+ ghost,
10444
+ contentLines,
10445
+ bufferState
10446
+ ]);
10447
+ const ghostTail = useMemo(() => {
10448
+ if (!ghost) return "";
10449
+ const rest = ghost.text.slice(ghost.cursor);
10450
+ const nl = rest.indexOf("\n");
10451
+ return nl === -1 ? rest : rest.slice(0, nl);
10452
+ }, [ghost]);
10295
10453
  const boxHeight = Math.min(MAX_CONTENT_LINES, contentLines) + 2;
10296
10454
  return /* @__PURE__ */ jsxs("box", {
10297
10455
  style: {
@@ -10347,7 +10505,7 @@ function PromptBlock({ userPrompts, onSubmit, completionProviders, onCompletionT
10347
10505
  flexDirection: "column",
10348
10506
  flexShrink: 0
10349
10507
  },
10350
- children: [/* @__PURE__ */ jsx$1("box", {
10508
+ children: [/* @__PURE__ */ jsxs("box", {
10351
10509
  style: {
10352
10510
  border: true,
10353
10511
  borderColor: selectMode ? COLOR.warn : shellMode ? COLOR.accent : COLOR.borderActive,
@@ -10356,7 +10514,7 @@ function PromptBlock({ userPrompts, onSubmit, completionProviders, onCompletionT
10356
10514
  height: boxHeight,
10357
10515
  flexDirection: "column"
10358
10516
  },
10359
- children: /* @__PURE__ */ jsx$1("textarea", {
10517
+ children: [/* @__PURE__ */ jsx$1("textarea", {
10360
10518
  ref: textareaRef,
10361
10519
  focused: focused && !selectMode,
10362
10520
  keyBindings: TEXTAREA_BINDINGS,
@@ -10371,7 +10529,24 @@ function PromptBlock({ userPrompts, onSubmit, completionProviders, onCompletionT
10371
10529
  onContentChange: syncBuffer,
10372
10530
  onKeyDown,
10373
10531
  onPaste
10374
- })
10532
+ }), ghostPosition !== null && ghost !== null && /* @__PURE__ */ jsx$1("box", {
10533
+ style: {
10534
+ position: "absolute",
10535
+ top: ghostPosition.row,
10536
+ left: 1 + ghostPosition.col,
10537
+ right: 1,
10538
+ height: 1,
10539
+ overflow: "hidden",
10540
+ backgroundColor: SURFACE.background
10541
+ },
10542
+ children: /* @__PURE__ */ jsxs("text", {
10543
+ wrapMode: "none",
10544
+ children: [/* @__PURE__ */ jsx$1("span", {
10545
+ fg: COLOR.dim,
10546
+ children: ghost.suffixes[ghost.index]
10547
+ }), ghostTail.length > 0 && /* @__PURE__ */ jsx$1("span", { children: ghostTail })]
10548
+ })
10549
+ })]
10375
10550
  }), /* @__PURE__ */ jsx$1(PromptHints, {
10376
10551
  selectMode,
10377
10552
  triggerHints,
@@ -11909,6 +12084,10 @@ const EMPTY_TOOLS = Object.freeze({});
11909
12084
  /** Stable empty defaults for the extension-config props — keep memo deps from churning. */
11910
12085
  const EMPTY_CONFIG_CATEGORIES = Object.freeze({});
11911
12086
  const EMPTY_CONFIG_ITEMS = Object.freeze({});
12087
+ /** How long a config action's run-result message stays before yielding to the live probe. */
12088
+ const CONFIG_ACTION_DONE_TTL_MS = 8e3;
12089
+ /** Errors linger longer — the user may need to read them — but expire too. */
12090
+ const CONFIG_ACTION_ERROR_TTL_MS = 2e4;
11912
12091
  const TAB_LABELS = {
11913
12092
  ...Object.fromEntries(SETTINGS_CATEGORIES.map((c) => [c.id, c.label])),
11914
12093
  keybindings: "Keybindings",
@@ -12009,7 +12188,77 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
12009
12188
  }
12010
12189
  });
12011
12190
  }, [settings.extensionConfig, setSetting]);
12191
+ const writeConfigValues = useCallback((extName, values) => {
12192
+ const prev = settings.extensionConfig ?? {};
12193
+ const prevExt = prev[extName] ?? {};
12194
+ setSetting("extensionConfig", {
12195
+ ...prev,
12196
+ [extName]: {
12197
+ ...prevExt,
12198
+ ...values
12199
+ }
12200
+ });
12201
+ }, [settings.extensionConfig, setSetting]);
12202
+ const [configAction, setConfigAction] = useState(null);
12203
+ const [actionProbes, setActionProbes] = useState({});
12204
+ const [actionProbeNonce, setActionProbeNonce] = useState(0);
12205
+ useEffect(() => {
12206
+ if (!configAction || configAction.state === "busy") return;
12207
+ const ttl = configAction.state === "error" ? CONFIG_ACTION_ERROR_TTL_MS : CONFIG_ACTION_DONE_TTL_MS;
12208
+ const id = setTimeout(() => {
12209
+ setConfigAction((current) => current === configAction ? null : current);
12210
+ setActionProbeNonce((n) => n + 1);
12211
+ }, ttl);
12212
+ return () => clearTimeout(id);
12213
+ }, [configAction]);
12214
+ const writeConfigValuesRef = useRef(writeConfigValues);
12215
+ writeConfigValuesRef.current = writeConfigValues;
12216
+ const extensionConfigItemsRef = useRef(extensionConfigItems);
12217
+ extensionConfigItemsRef.current = extensionConfigItems;
12218
+ const configActionBusyRef = useRef(false);
12219
+ const runConfigAction = useCallback((extName, item) => {
12220
+ if (configActionBusyRef.current) return;
12221
+ configActionBusyRef.current = true;
12222
+ setConfigAction({
12223
+ extName,
12224
+ key: item.key,
12225
+ state: "busy"
12226
+ });
12227
+ (async () => {
12228
+ try {
12229
+ const result = await item.run() ?? void 0;
12230
+ const { accepted, rejected } = sanitizeExtensionConfigActionPatch(extensionConfigItemsRef.current[extName], result?.config);
12231
+ const acceptedKeys = Object.keys(accepted);
12232
+ if (acceptedKeys.length > 0) writeConfigValuesRef.current(extName, accepted);
12233
+ const parts = [];
12234
+ if (result?.message) parts.push(result.message);
12235
+ else if (acceptedKeys.length > 0) parts.push(`updated ${acceptedKeys.join(", ")}`);
12236
+ else parts.push("done");
12237
+ if (rejected.length > 0) parts.push(`(ignored invalid: ${rejected.join(", ")})`);
12238
+ setConfigAction({
12239
+ extName,
12240
+ key: item.key,
12241
+ state: "done",
12242
+ text: parts.join(" ")
12243
+ });
12244
+ } catch (err) {
12245
+ setConfigAction({
12246
+ extName,
12247
+ key: item.key,
12248
+ state: "error",
12249
+ text: errorMessage(err)
12250
+ });
12251
+ } finally {
12252
+ configActionBusyRef.current = false;
12253
+ }
12254
+ setActionProbeNonce((n) => n + 1);
12255
+ })();
12256
+ }, []);
12012
12257
  const activateConfigRow = useCallback((extName, item) => {
12258
+ if (item.type === "action") {
12259
+ runConfigAction(extName, item);
12260
+ return;
12261
+ }
12013
12262
  const current = resolveExtensionConfigValue(item, settings.extensionConfig?.[extName]?.[item.key]);
12014
12263
  if (item.type === "boolean") {
12015
12264
  writeConfigValue(extName, item.key, !current);
@@ -12031,7 +12280,8 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
12031
12280
  }, [
12032
12281
  settings.extensionConfig,
12033
12282
  writeConfigValue,
12034
- modelCatalog
12283
+ modelCatalog,
12284
+ runConfigAction
12035
12285
  ]);
12036
12286
  const applyModelEdit = useCallback((ids) => {
12037
12287
  if (configEdit) writeConfigValue(configEdit.extName, configEdit.key, ids.join(", "));
@@ -12057,6 +12307,16 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
12057
12307
  const [expandedExtensions, setExpandedExtensions] = useState(() => /* @__PURE__ */ new Set());
12058
12308
  const onRefreshMcpToolsRef = useRef(actions?.onRefreshMcpTools);
12059
12309
  onRefreshMcpToolsRef.current = actions?.onRefreshMcpTools;
12310
+ const [mcpToolsRefreshing, setMcpToolsRefreshing] = useState(false);
12311
+ const fireMcpToolsRefresh = useCallback(() => {
12312
+ const run = onRefreshMcpToolsRef.current;
12313
+ if (!run) return;
12314
+ setMcpToolsRefreshing(true);
12315
+ Promise.resolve(run()).catch(() => {}).finally(() => setMcpToolsRefreshing(false));
12316
+ }, []);
12317
+ const mcpsCatalogLiveRef = useRef([]);
12318
+ mcpsCatalogLiveRef.current = mcpsCatalog;
12319
+ const mcpColdProbedRef = useRef(/* @__PURE__ */ new Set());
12060
12320
  const tabOrder = useMemo(() => {
12061
12321
  const tabs = [...SETTINGS_CATEGORIES.map((c) => c.id)];
12062
12322
  if (keybindings) tabs.push("keybindings");
@@ -12073,6 +12333,40 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
12073
12333
  logs
12074
12334
  ]);
12075
12335
  const [activeTab, setActiveTab] = useState(tabOrder[0]);
12336
+ useEffect(() => {
12337
+ if (activeTab !== "extensions") return;
12338
+ let cancelled = false;
12339
+ for (const [extName, byKey] of Object.entries(extensionConfigItems)) for (const item of Object.values(byKey)) {
12340
+ if (item.type !== "action" || typeof item.status !== "function") continue;
12341
+ (async () => {
12342
+ let status;
12343
+ try {
12344
+ status = await Promise.race([Promise.resolve(item.status()), new Promise((resolve) => setTimeout(resolve, 3e3))]);
12345
+ } catch {
12346
+ status = void 0;
12347
+ }
12348
+ if (cancelled) return;
12349
+ const mapKey = `${extName}:${item.key}`;
12350
+ setActionProbes((prev) => {
12351
+ if (status && typeof status.message === "string") return {
12352
+ ...prev,
12353
+ [mapKey]: status
12354
+ };
12355
+ if (!(mapKey in prev)) return prev;
12356
+ const next = { ...prev };
12357
+ delete next[mapKey];
12358
+ return next;
12359
+ });
12360
+ })();
12361
+ }
12362
+ return () => {
12363
+ cancelled = true;
12364
+ };
12365
+ }, [
12366
+ activeTab,
12367
+ extensionConfigItems,
12368
+ actionProbeNonce
12369
+ ]);
12076
12370
  const [extEditNotice, setExtEditNotice] = useState(null);
12077
12371
  const [extOp, setExtOp] = useState(null);
12078
12372
  const [removeArmed, setRemoveArmed] = useState(null);
@@ -12183,8 +12477,23 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
12183
12477
  }, [tabOrder, activeTab]);
12184
12478
  useEffect(() => {
12185
12479
  if (activeTab !== "mcps") return;
12186
- onRefreshMcpToolsRef.current?.();
12187
- }, [activeTab]);
12480
+ if (!onRefreshMcpToolsRef.current) return;
12481
+ for (const d of mcpsCatalogLiveRef.current) mcpColdProbedRef.current.add(d.config.name);
12482
+ fireMcpToolsRefresh();
12483
+ }, [activeTab, fireMcpToolsRefresh]);
12484
+ useEffect(() => {
12485
+ if (activeTab !== "mcps") return;
12486
+ const cold = mcpsCatalog.filter((d) => mcpsToggle.enabledSet.has(d.config.name) && (mcpToolsByServer[d.config.name]?.length ?? 0) === 0 && !mcpColdProbedRef.current.has(d.config.name));
12487
+ if (cold.length === 0) return;
12488
+ for (const d of cold) mcpColdProbedRef.current.add(d.config.name);
12489
+ fireMcpToolsRefresh();
12490
+ }, [
12491
+ activeTab,
12492
+ mcpsCatalog,
12493
+ mcpsToggle.enabledSet,
12494
+ mcpToolsByServer,
12495
+ fireMcpToolsRefresh
12496
+ ]);
12188
12497
  const extensionThemes = extensionThemesProp ?? [];
12189
12498
  const generalItems = useMemo(() => {
12190
12499
  const toggles = SETTINGS_TOGGLES.map((t) => ({
@@ -12322,13 +12631,19 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
12322
12631
  const row = mcpVisibleRows[cursor];
12323
12632
  if (!row || row.kind !== "server") return;
12324
12633
  const name = row.entry.config.name;
12634
+ if ((mcpToolsByServer[name]?.length ?? 0) === 0) fireMcpToolsRefresh();
12325
12635
  setExpandedMcps((prev) => {
12326
12636
  if (prev.has(name)) return prev;
12327
12637
  const next = new Set(prev);
12328
12638
  next.add(name);
12329
12639
  return next;
12330
12640
  });
12331
- }, [mcpVisibleRows, cursor]);
12641
+ }, [
12642
+ mcpVisibleRows,
12643
+ cursor,
12644
+ mcpToolsByServer,
12645
+ fireMcpToolsRefresh
12646
+ ]);
12332
12647
  const collapseFocusedMcpRow = useCallback(() => {
12333
12648
  const row = mcpVisibleRows[cursor];
12334
12649
  if (!row) return;
@@ -12558,8 +12873,12 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
12558
12873
  if (activeTab === "mcps") {
12559
12874
  const row = mcpVisibleRows[cursor];
12560
12875
  if (!row) return;
12561
- if (row.kind === "server") mcpsToggle.toggle(row.entry.config.name);
12562
- else if (row.kind === "tool") mcpToolToggleMap[row.serverName]?.toggle(row.tool.name);
12876
+ if (row.kind === "server") {
12877
+ const name = row.entry.config.name;
12878
+ const enabling = !mcpsToggle.enabledSet.has(name);
12879
+ mcpsToggle.toggle(name);
12880
+ if (enabling && (mcpToolsByServer[name]?.length ?? 0) === 0) fireMcpToolsRefresh();
12881
+ } else if (row.kind === "tool") mcpToolToggleMap[row.serverName]?.toggle(row.tool.name);
12563
12882
  return;
12564
12883
  }
12565
12884
  if (activeTab === "keybindings") {
@@ -12680,7 +12999,7 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
12680
12999
  }
12681
13000
  if (activeTab === "mcps") {
12682
13001
  if (actions?.onRefreshMcps) actions.onRefreshMcps();
12683
- if (actions?.onRefreshMcpTools) actions.onRefreshMcpTools();
13002
+ fireMcpToolsRefresh();
12684
13003
  }
12685
13004
  }
12686
13005
  });
@@ -12785,7 +13104,9 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
12785
13104
  onCommitEdit: commitConfigEdit,
12786
13105
  modelCatalog,
12787
13106
  onApplyModelEdit: applyModelEdit,
12788
- onCancelEdit: cancelConfigEdit
13107
+ onCancelEdit: cancelConfigEdit,
13108
+ actionStatus: configAction,
13109
+ actionProbes
12789
13110
  }),
12790
13111
  activeTab === "mcps" && /* @__PURE__ */ jsx$1(McpsList, {
12791
13112
  rows: mcpVisibleRows,
@@ -12793,6 +13114,7 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
12793
13114
  enabledSet: mcpsToggle.enabledSet,
12794
13115
  toolToggleMap: mcpToolToggleMap,
12795
13116
  toolsByServer: mcpToolsByServer,
13117
+ toolsRefreshing: mcpToolsRefreshing,
12796
13118
  expanded: expandedMcps,
12797
13119
  totalCount: mcpsCatalog.length,
12798
13120
  cursor,
@@ -13058,8 +13380,8 @@ function GeneralList({ items, cursor, highlightBg, query, contentCols, editingKe
13058
13380
  })
13059
13381
  });
13060
13382
  }
13061
- const EXTENSION_CONFIG_INDENT = 0;
13062
- function ExtensionsList({ rows, enabledSet, extensionConfig, categories, totalCount, cursor, highlightBg, query, contentCols, editing, inputRef, onCommitEdit, modelCatalog, onApplyModelEdit, onCancelEdit }) {
13383
+ const EXTENSION_CONFIG_INDENT = 3;
13384
+ function ExtensionsList({ rows, enabledSet, extensionConfig, categories, totalCount, cursor, highlightBg, query, contentCols, editing, inputRef, onCommitEdit, modelCatalog, onApplyModelEdit, onCancelEdit, actionStatus, actionProbes }) {
13063
13385
  const COLOR = useColors();
13064
13386
  const home = homedir();
13065
13387
  if (totalCount === 0) return /* @__PURE__ */ jsxs("box", {
@@ -13206,7 +13528,19 @@ function ExtensionsList({ rows, enabledSet, extensionConfig, categories, totalCo
13206
13528
  const childBg = childFocused ? highlightBg : void 0;
13207
13529
  const value = resolveExtensionConfigValue(item, extensionConfig?.[group.ext.name]?.[item.key]);
13208
13530
  const childDesc = truncateToCols(item.description ?? "", descCols);
13209
- if (item.type === "boolean") childEls.push(/* @__PURE__ */ jsx$1(ToggleRow, {
13531
+ if (item.type === "action") {
13532
+ const status = actionStatus && actionStatus.extName === group.ext.name && actionStatus.key === item.key ? actionStatus : null;
13533
+ childEls.push(/* @__PURE__ */ jsx$1(ConfigActionRow, {
13534
+ id: anchorIdFor(index),
13535
+ indent: EXTENSION_CONFIG_INDENT,
13536
+ label: item.label,
13537
+ description: childDesc,
13538
+ status,
13539
+ probe: actionProbes[`${group.ext.name}:${item.key}`] ?? null,
13540
+ focused: childFocused,
13541
+ bg: childBg
13542
+ }, item.key));
13543
+ } else if (item.type === "boolean") childEls.push(/* @__PURE__ */ jsx$1(ToggleRow, {
13210
13544
  id: anchorIdFor(index),
13211
13545
  indent: EXTENSION_CONFIG_INDENT,
13212
13546
  label: item.label,
@@ -13345,6 +13679,53 @@ function CreateExtensionButton() {
13345
13679
  })]
13346
13680
  });
13347
13681
  }
13682
+ function ConfigActionRow({ id, label, description, status, probe, focused, bg, indent = 0 }) {
13683
+ const COLOR = useColors();
13684
+ const statusText = (status?.state === "busy" ? "running…" : status?.text) ?? probe?.message;
13685
+ const statusColor = status ? status.state === "error" ? COLOR.error : status.state === "busy" ? COLOR.mute : COLOR.accent : probe ? probe.state === "error" ? COLOR.error : probe.state === "off" ? COLOR.mute : COLOR.accent : COLOR.mute;
13686
+ return /* @__PURE__ */ jsxs("box", {
13687
+ id,
13688
+ style: {
13689
+ flexDirection: "column",
13690
+ flexShrink: 0,
13691
+ paddingLeft: 1 + indent,
13692
+ paddingRight: 1,
13693
+ backgroundColor: bg
13694
+ },
13695
+ children: [/* @__PURE__ */ jsxs("text", {
13696
+ wrapMode: "none",
13697
+ children: [
13698
+ /* @__PURE__ */ jsx$1("span", {
13699
+ fg: focused ? COLOR.brand : COLOR.mute,
13700
+ children: focused ? "▶ " : " "
13701
+ }),
13702
+ /* @__PURE__ */ jsx$1("span", {
13703
+ fg: COLOR.mute,
13704
+ children: SPACER_CHECKBOX_WIDTH
13705
+ }),
13706
+ /* @__PURE__ */ jsx$1("span", {
13707
+ fg: focused ? COLOR.brand : COLOR.dim,
13708
+ children: label
13709
+ }),
13710
+ statusText && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx$1("span", {
13711
+ fg: COLOR.mute,
13712
+ children: ": "
13713
+ }), /* @__PURE__ */ jsx$1("span", {
13714
+ fg: statusColor,
13715
+ children: statusText
13716
+ })] }),
13717
+ focused && status?.state !== "busy" && /* @__PURE__ */ jsx$1("span", {
13718
+ fg: COLOR.brand,
13719
+ children: " ↵ run"
13720
+ })
13721
+ ]
13722
+ }), description && /* @__PURE__ */ jsx$1("text", {
13723
+ wrapMode: "none",
13724
+ fg: COLOR.mute,
13725
+ children: `${COL_TITLE}${description}`
13726
+ })]
13727
+ });
13728
+ }
13348
13729
  function ConfigInputRow({ id, label, description, value, editing, initial, inputRef, onCommit, focused, bg, indent = 0 }) {
13349
13730
  const COLOR = useColors();
13350
13731
  return /* @__PURE__ */ jsxs("box", {
@@ -13552,7 +13933,7 @@ function SkillsList({ items, enabledSet, totalCount, cursor, highlightBg, query,
13552
13933
  })
13553
13934
  });
13554
13935
  }
13555
- function McpsList({ rows, filteredCount, enabledSet, toolToggleMap, toolsByServer, expanded, totalCount, cursor, highlightBg, query, errors, authState, contentCols }) {
13936
+ function McpsList({ rows, filteredCount, enabledSet, toolToggleMap, toolsByServer, toolsRefreshing, expanded, totalCount, cursor, highlightBg, query, errors, authState, contentCols }) {
13556
13937
  const COLOR = useColors();
13557
13938
  const home = homedir();
13558
13939
  if (totalCount === 0) return /* @__PURE__ */ jsxs("box", {
@@ -13651,7 +14032,8 @@ function McpsList({ rows, filteredCount, enabledSet, toolToggleMap, toolsByServe
13651
14032
  anchorId: anchorIdFor(index),
13652
14033
  focused,
13653
14034
  bg,
13654
- COLOR
14035
+ COLOR,
14036
+ refreshing: toolsRefreshing
13655
14037
  });
13656
14038
  })]
13657
14039
  }, `group:${group.server.entry.config.name}`))]
@@ -13726,7 +14108,7 @@ function renderToolListRow({ serverName, tool, focused, bg, enabled, anchorId, C
13726
14108
  ]
13727
14109
  }, `tool:${serverName}/${tool.name}`);
13728
14110
  }
13729
- function renderEmptyToolsListRow({ serverName, anchorId, focused, bg, COLOR }) {
14111
+ function renderEmptyToolsListRow({ serverName, anchorId, focused, bg, COLOR, refreshing }) {
13730
14112
  return /* @__PURE__ */ jsxs("text", {
13731
14113
  id: anchorId,
13732
14114
  wrapMode: "none",
@@ -13736,7 +14118,7 @@ function renderEmptyToolsListRow({ serverName, anchorId, focused, bg, COLOR }) {
13736
14118
  children: focused ? TOOL_ROW_FOCUS_INDENT$1 : TOOL_ROW_INDENT$1
13737
14119
  }), /* @__PURE__ */ jsx$1("span", {
13738
14120
  fg: COLOR.mute,
13739
- children: "⟲ No cached tools yet. Send any prompt with this server enabled to populate."
14121
+ children: refreshing ? "⟲ Fetching tool list…" : "⟲ No tools reported check the server command or login; ctrl+r retries."
13740
14122
  })]
13741
14123
  }, `tool-empty:${serverName}`);
13742
14124
  }
@@ -14157,6 +14539,37 @@ function renderMcpDetailPanel(entry, status, COLOR) {
14157
14539
  })
14158
14540
  ]
14159
14541
  });
14542
+ if (status.kind === "bootstrap-error") return /* @__PURE__ */ jsxs("box", {
14543
+ style: {
14544
+ flexDirection: "column",
14545
+ border: ["top"],
14546
+ borderColor: COLOR.border,
14547
+ paddingTop: 1
14548
+ },
14549
+ children: [
14550
+ /* @__PURE__ */ jsx$1("text", {
14551
+ fg: COLOR.error,
14552
+ children: `Server failed to start: ${entry.config.name}`
14553
+ }),
14554
+ /* @__PURE__ */ jsx$1("text", {
14555
+ fg: COLOR.dim,
14556
+ children: status.error
14557
+ }),
14558
+ /* @__PURE__ */ jsxs("text", {
14559
+ fg: COLOR.mute,
14560
+ children: [
14561
+ "Fix the command / URL in mcps.json, then",
14562
+ " ",
14563
+ /* @__PURE__ */ jsx$1("span", {
14564
+ fg: COLOR.warn,
14565
+ children: "ctrl+r"
14566
+ }),
14567
+ " ",
14568
+ "to re-probe."
14569
+ ]
14570
+ })
14571
+ ]
14572
+ });
14160
14573
  return null;
14161
14574
  }
14162
14575
  function renderInlineMcpBadge(status, COLOR) {
@@ -14178,6 +14591,10 @@ function renderInlineMcpBadge(status, COLOR) {
14178
14591
  fg: COLOR.error,
14179
14592
  children: " · ✗ login failed"
14180
14593
  });
14594
+ case "bootstrap-error": return /* @__PURE__ */ jsx$1("span", {
14595
+ fg: COLOR.error,
14596
+ children: " · ✗ failed to start"
14597
+ });
14181
14598
  }
14182
14599
  }
14183
14600
  function renderMcpErrors(errors, home, warnColor) {
@@ -14309,7 +14726,7 @@ function Hints({ activeTab, focusedMcp, focusedMcpRow, focusedMcpStatus, canRefr
14309
14726
  " · ",
14310
14727
  /* @__PURE__ */ jsx$1("span", {
14311
14728
  fg: COLOR.warn,
14312
- children: "ctrl+R"
14729
+ children: "ctrl+r"
14313
14730
  }),
14314
14731
  " refresh"
14315
14732
  ] }),
@@ -15894,6 +16311,58 @@ function AppShell() {
15894
16311
  config.extensionScanPaths
15895
16312
  ]);
15896
16313
  /**
16314
+ * Hot-load on enable: toggling an extension ON in Settings → Extensions
16315
+ * used to take effect only at the next launch — live filtering can remove
16316
+ * an already-loaded extension's contributions, but it can't import a
16317
+ * module that boot skipped, so the row stayed blank (no version, no
16318
+ * settings) until a reboot. This effect closes that asymmetry: when a
16319
+ * newly-enabled name matches a registry entry whose `setup()` never ran,
16320
+ * it rides the same rebuild path as `/reload` and the disk watcher —
16321
+ * `registryBuildOptions` reads the allowlist through the eagerly-written
16322
+ * `enabledExtensionsRef`, so the rebuild sees the fresh toggle. The
16323
+ * settings modal renders registry state as live props, so the extension's
16324
+ * config items appear immediately, modal still open.
16325
+ *
16326
+ * Toggling OFF needs nothing here (`filterExtensionRegistry` handles it
16327
+ * reactively), and re-enabling an extension that is still in `loaded`
16328
+ * (same-session toggle round-trip) is also pure filtering — only
16329
+ * never-set-up entries trigger the reload. Errored entries are excluded:
16330
+ * a rebuild wouldn't fix them, and the modal already surfaces the error.
16331
+ */
16332
+ const handledEnabledExtensionsRef = useRef(settings.enabledExtensions);
16333
+ const [enableRetryNonce, setEnableRetryNonce] = useState(0);
16334
+ useEffect(() => {
16335
+ const prev = handledEnabledExtensionsRef.current;
16336
+ const next = settings.enabledExtensions;
16337
+ if (next === void 0 || next === prev) return;
16338
+ const prevSet = new Set(prev ?? []);
16339
+ const registry = extensionRegistryLiveRef.current;
16340
+ const needsLoad = next.filter((name) => !prevSet.has(name) && (registry.inactive.some((e) => e.name === name && !e.error) || registry.loaded.some((e) => e.name === name && !e.setupRan && !e.error)));
16341
+ if (needsLoad.length === 0) {
16342
+ handledEnabledExtensionsRef.current = next;
16343
+ return;
16344
+ }
16345
+ if (extensionAutoReloadInFlightRef.current || runningRef.current) {
16346
+ const id = setTimeout(() => setEnableRetryNonce((n) => n + 1), EXTENSION_WATCH_POLL_MS);
16347
+ return () => clearTimeout(id);
16348
+ }
16349
+ handledEnabledExtensionsRef.current = next;
16350
+ extensionAutoReloadInFlightRef.current = true;
16351
+ (async () => {
16352
+ try {
16353
+ await reloadExtensionsRef.current();
16354
+ streamRef.current?.appendImmediate({
16355
+ kind: "info",
16356
+ text: `\u{1F50C} Enabled ${needsLoad.join(", ")} — settings and UI are live; agent tools apply next run.`
16357
+ });
16358
+ } catch (err) {
16359
+ debugLog("extension enable: hot-load failed", err);
16360
+ } finally {
16361
+ extensionAutoReloadInFlightRef.current = false;
16362
+ }
16363
+ })();
16364
+ }, [settings.enabledExtensions, enableRetryNonce]);
16365
+ /**
15897
16366
  * Auto-wake trigger for background-task notifications. When a
15898
16367
  * backgrounded shell task exits while the agent is IDLE, nothing would
15899
16368
  * otherwise deliver the queued `<task-notification>` until the user
@@ -15917,6 +16386,11 @@ function AppShell() {
15917
16386
  const [showQuitConfirm, setShowQuitConfirm] = useState(false);
15918
16387
  const showQuitConfirmRef = useRef(false);
15919
16388
  showQuitConfirmRef.current = showQuitConfirm;
16389
+ useEffect(() => {
16390
+ if (!showQuitConfirm) return;
16391
+ modal.lock();
16392
+ return () => modal.unlock();
16393
+ }, [showQuitConfirm, modal]);
15920
16394
  const [quitConfirmSelected, setQuitConfirmSelected] = useState("cancel");
15921
16395
  const quitConfirmSelectedRef = useRef("cancel");
15922
16396
  quitConfirmSelectedRef.current = quitConfirmSelected;
@@ -16059,6 +16533,7 @@ function AppShell() {
16059
16533
  const key = command.name.toLowerCase();
16060
16534
  if (seen.has(key)) continue;
16061
16535
  seen.add(key);
16536
+ for (const alias of command.aliases ?? []) seen.add(alias.toLowerCase());
16062
16537
  merged.push(command);
16063
16538
  }
16064
16539
  return merged;
@@ -16093,6 +16568,7 @@ function AppShell() {
16093
16568
  visibleChatCatalog,
16094
16569
  liveExtensionCompletionProviders
16095
16570
  ]);
16571
+ const inlineAutocompleteProviders = useMemo(() => [createPathAutocompleteProvider({ cwd: () => process.cwd() }), ...filterExtensionRegistry(extensionRegistry, settings.enabledExtensions).inlineAutocompleteProviders], [extensionRegistry, settings.enabledExtensions]);
16096
16572
  useEffect(() => {
16097
16573
  const handle = agentRef.current?.handle;
16098
16574
  if (handle) handle.cwd = cwd;
@@ -16575,12 +17051,16 @@ function AppShell() {
16575
17051
  });
16576
17052
  agent.hooks.hook("mcp:bootstrap:end", (ctx) => {
16577
17053
  if (ctx.ok) return;
16578
- if (mcpsCatalogRef.current.find((d) => d.config.name === ctx.name)?.config.auth !== "oauth") return;
16579
- dispatchAuthRef.current({
17054
+ if (mcpsCatalogRef.current.find((d) => d.config.name === ctx.name)?.config.auth === "oauth") dispatchAuthRef.current({
16580
17055
  type: "auth-error",
16581
17056
  name: ctx.name,
16582
17057
  error: ctx.error.message
16583
17058
  });
17059
+ else dispatchAuthRef.current({
17060
+ type: "bootstrap-error",
17061
+ name: ctx.name,
17062
+ error: ctx.error.message
17063
+ });
16584
17064
  });
16585
17065
  agent.hooks.hook("mcp:auth:url", ({ name, url }) => {
16586
17066
  dispatchAuthRef.current({
@@ -18330,7 +18810,7 @@ function AppShell() {
18330
18810
  /** Route a submitted prompt to the command executor when it's a `/cmd`, otherwise to the model. */
18331
18811
  const runPromptOrCommand = useCallback(async (prompt, references, attachments, opts) => {
18332
18812
  if (!opts?.wake) {
18333
- const invocation = parseCommandInvocation(prompt, commandsRef.current.map((c) => c.name));
18813
+ const invocation = parseCommandInvocation(prompt, commandsRef.current);
18334
18814
  if (invocation) {
18335
18815
  await runCommand(invocation);
18336
18816
  return;
@@ -18401,7 +18881,7 @@ function AppShell() {
18401
18881
  suppressWakeRef.current = false;
18402
18882
  (async () => {
18403
18883
  let effectivePrompt = prompt;
18404
- if (parseCommandInvocation(prompt, commandsRef.current.map((c) => c.name)) === null && parseBashInvocation(prompt) === null) for (const contribution of extensionRegistry.promptTransforms) try {
18884
+ if (parseCommandInvocation(prompt, commandsRef.current) === null && parseBashInvocation(prompt) === null) for (const contribution of extensionRegistry.promptTransforms) try {
18405
18885
  const out = await contribution.value(effectivePrompt);
18406
18886
  if (out === void 0) continue;
18407
18887
  if (typeof out === "string") {
@@ -19667,6 +20147,7 @@ function AppShell() {
19667
20147
  pendingInteraction,
19668
20148
  onInteraction: onInteractionResolve,
19669
20149
  completionProviders,
20150
+ inlineAutocompleteProviders,
19670
20151
  onCompletionTab: (providerId) => {
19671
20152
  if (providerId !== "chats") return false;
19672
20153
  toggleChatReferenceScope();
@@ -20257,7 +20738,7 @@ function ToggleListModal({ catalog, keyOf, settingKey, gcKeys, title, renderDeta
20257
20738
  children: [
20258
20739
  onRefresh && /* @__PURE__ */ jsxs("span", { children: [/* @__PURE__ */ jsx$1("span", {
20259
20740
  fg: COLOR.warn,
20260
- children: "ctrl+R"
20741
+ children: "ctrl+r"
20261
20742
  }), " refresh · "] }),
20262
20743
  /* @__PURE__ */ jsx$1("span", {
20263
20744
  fg: COLOR.warn,
@@ -20316,7 +20797,7 @@ function ToggleListModal({ catalog, keyOf, settingKey, gcKeys, title, renderDeta
20316
20797
  " toggle · ",
20317
20798
  onRefresh && /* @__PURE__ */ jsxs("span", { children: [/* @__PURE__ */ jsx$1("span", {
20318
20799
  fg: COLOR.warn,
20319
- children: "ctrl+R"
20800
+ children: "ctrl+r"
20320
20801
  }), " refresh · "] }),
20321
20802
  /* @__PURE__ */ jsx$1("span", {
20322
20803
  fg: COLOR.warn,
@@ -20343,10 +20824,11 @@ function ToggleListModal({ catalog, keyOf, settingKey, gcKeys, title, renderDeta
20343
20824
  * folder without leaving the TUI.
20344
20825
  *
20345
20826
  * Enabled state lives in `settings.enabledExtensions` (mirrors
20346
- * `enabledSkills` / `enabledMcps`). Toggling takes effect on the NEXT
20347
- * launch extension registration is a boot-time concern (themes /
20348
- * agents / providers / keybindings all fold into the resolved config
20349
- * at `resolveConfig` time).
20827
+ * `enabledSkills` / `enabledMcps`). Toggling OFF filters the live
20828
+ * registry immediately; toggling ON an extension that boot skipped
20829
+ * hot-reloads the registry (see "Hot-load on enable" in app.tsx), so
20830
+ * its `setup()` runs and its settings appear without a restart.
20831
+ * Agent-side contributions (tools / hooks) apply on the next run.
20350
20832
  */
20351
20833
  function ExtensionsSettingsModal({ catalog, inactive }) {
20352
20834
  const COLOR = useColors();
@@ -21034,7 +21516,7 @@ function displayPath(path, home) {
21034
21516
  * row geometry live in `<ToggleListModal>`; this file just supplies the
21035
21517
  * skill-specific column (description) and the empty-state hint.
21036
21518
  *
21037
- * Pass `onRefresh` to expose `ctrl+R` (force re-scan) on the list. The
21519
+ * Pass `onRefresh` to expose `ctrl+r` (force re-scan) on the list. The
21038
21520
  * standalone modal is for embedders — the in-app flow goes through
21039
21521
  * `<SettingsModal>` which exposes the same affordance via its own
21040
21522
  * `SettingsActions.onRefreshSkills` plumbing.