zidane 6.2.14 → 6.2.18

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 (78) hide show
  1. package/dist/{acp-DxYjSCQW.js → acp-CxE1-kHy.js} +2 -2
  2. package/dist/{acp-DxYjSCQW.js.map → acp-CxE1-kHy.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-DoEx_WD3.js → agent-DBfHPAgz.js} +81 -4
  7. package/dist/agent-DBfHPAgz.js.map +1 -0
  8. package/dist/agent.d.ts +1 -1
  9. package/dist/agent.js +1 -1
  10. package/dist/{anthropic-BYsc6qWj.js → anthropic-uKvj1yle.js} +76 -9
  11. package/dist/anthropic-uKvj1yle.js.map +1 -0
  12. package/dist/{auth-Bb479DFB.js → auth-CpdQebUy.js} +2 -2
  13. package/dist/{auth-Bb479DFB.js.map → auth-CpdQebUy.js.map} +1 -1
  14. package/dist/chat/pure.d.ts +3 -3
  15. package/dist/chat.d.ts +12 -6
  16. package/dist/chat.d.ts.map +1 -1
  17. package/dist/chat.js +3 -3
  18. package/dist/contexts/daytona.d.ts +1 -1
  19. package/dist/contexts/e2b.d.ts +1 -1
  20. package/dist/eval.d.ts +1 -1
  21. package/dist/extensions.d.ts +1 -1
  22. package/dist/extensions.js +1 -1
  23. package/dist/headless.d.ts +1 -1
  24. package/dist/headless.js +1 -1
  25. package/dist/{index-l0wehkWU.d.ts → index-CC0JD-0H.d.ts} +188 -138
  26. package/dist/index-CC0JD-0H.d.ts.map +1 -0
  27. package/dist/index.d.ts +2 -2
  28. package/dist/index.js +4 -4
  29. package/dist/inline-autocomplete-BAwBcZtD.js.map +1 -1
  30. package/dist/{login-Clf-R27F.js → login-DreurRAq.js} +2 -2
  31. package/dist/{login-Clf-R27F.js.map → login-DreurRAq.js.map} +1 -1
  32. package/dist/{mcp-BCaN_Gyg.js → mcp-H6bqyM94.js} +24 -4
  33. package/dist/mcp-H6bqyM94.js.map +1 -0
  34. package/dist/mcp.d.ts +2 -2
  35. package/dist/mcp.js +2 -2
  36. package/dist/output/stream-json.d.ts +1 -1
  37. package/dist/output/terminal.d.ts +1 -1
  38. package/dist/presets.d.ts +1 -1
  39. package/dist/presets.js +1 -1
  40. package/dist/providers/anthropic.d.ts +1 -1
  41. package/dist/providers/anthropic.js +1 -1
  42. package/dist/providers/arcee.d.ts +1 -1
  43. package/dist/providers/baseten.d.ts +1 -1
  44. package/dist/providers/cerebras.d.ts +1 -1
  45. package/dist/providers/local.d.ts +1 -1
  46. package/dist/providers/openai-compat.d.ts +1 -1
  47. package/dist/providers/openai.d.ts +1 -1
  48. package/dist/providers/openrouter.d.ts +1 -1
  49. package/dist/providers/xai.d.ts +1 -1
  50. package/dist/providers.d.ts +1 -1
  51. package/dist/providers.js +1 -1
  52. package/dist/restate.d.ts +1 -1
  53. package/dist/session/sqlite.d.ts +1 -1
  54. package/dist/session.d.ts +1 -1
  55. package/dist/skills.d.ts +1 -1
  56. package/dist/{tool-formatters-DdgS-xE2.d.ts → tool-formatters-BD3oohVq.d.ts} +2 -2
  57. package/dist/{tool-formatters-DdgS-xE2.d.ts.map → tool-formatters-BD3oohVq.d.ts.map} +1 -1
  58. package/dist/tools/fetch-url.d.ts +1 -1
  59. package/dist/tools/web-search.d.ts +1 -1
  60. package/dist/tools.d.ts +1 -1
  61. package/dist/tools.js +1 -1
  62. package/dist/{transcript-anchors-DE6D0dNo.js → transcript-anchors-DUusSVpZ.js} +18 -9
  63. package/dist/transcript-anchors-DUusSVpZ.js.map +1 -0
  64. package/dist/{transcript-anchors-DKjXpQ0h.d.ts → transcript-anchors-DZYw3L1u.d.ts} +2 -2
  65. package/dist/{transcript-anchors-DKjXpQ0h.d.ts.map → transcript-anchors-DZYw3L1u.d.ts.map} +1 -1
  66. package/dist/tui.d.ts +19 -4
  67. package/dist/tui.d.ts.map +1 -1
  68. package/dist/tui.js +269 -39
  69. package/dist/tui.js.map +1 -1
  70. package/dist/{turn-operations-sE-Sli9H.d.ts → turn-operations-BcrzRAoE.d.ts} +3 -3
  71. package/dist/{turn-operations-sE-Sli9H.d.ts.map → turn-operations-BcrzRAoE.d.ts.map} +1 -1
  72. package/dist/types.d.ts +1 -1
  73. package/package.json +1 -1
  74. package/dist/agent-DoEx_WD3.js.map +0 -1
  75. package/dist/anthropic-BYsc6qWj.js.map +0 -1
  76. package/dist/index-l0wehkWU.d.ts.map +0 -1
  77. package/dist/mcp-BCaN_Gyg.js.map +0 -1
  78. package/dist/transcript-anchors-DE6D0dNo.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-DE6D0dNo.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-DoEx_WD3.js";
1
+ import { $ as useMcpAuthDispatch, $n as buildBuildSystem, $t as providerKeySetChanged, A as runOAuthLogin, B as refreshMcpToolsCatalog, Ct as SETTINGS_CATEGORIES, D as formatPathForCwd, Dt as SettingsProvider, En as accentColor, Et as SETTINGS_TOGGLES, F as parseJsonTolerant, Gt as ConfigProvider, H as subscribeMcpToolsCache, Hn as useActiveTodos, Ht as DiscoveryProvider, J as parentServerName, Jt as resolveStoragePaths, K as buildVisibleMcpRows, Kt as useConfig, M as buildMcpServers, Mn as TODO_STATUS_GLYPHS, Mt as resolveChipColor, Nt as resolveTheme, O as fetchOAuthRedirect, Ot as clampFps, P as discoverProjectMcps, Q as McpAuthProvider, Qt as filterChainFallbacks, R as loadMcpToolsCache, St as DEFAULT_SETTINGS, T as suggestSafelistEntries, Tt as SETTINGS_INPUTS, Ut as useDiscovery, Vt as createDiscoverySlot, W as useMcpToolToggleMap, Wt as useDiscoveryOptional, Xt as chainPositionOf, Y as createFileMcpCredentialStore, Yt as buildRunModel, Zt as chainsEqual, _ as useSafeModeQueue, _n as chatAutoCompactBehavior, _t as truncateTrailing, a as useSurfaces, ar as compactSummaryEvent, at as PRESENT_PLAN_TOOL, b as getSafelist, br as updateToolEventOutcomes, bt as listProjectFiles, c as useStreamBuffer, cn as useUpdateCheck, cr as eventsFromTurns, d as discoverProjectSkills, dr as loadState, en as restoreChainFallbacks, er as buildPlanSystem, et as useMcpAuthState, fr as marginTopFor, ft as useInteractionsActions, g as useSafeModeActions, gr as sumRunCosts, gt as hintsLength, h as SafeModeProvider, hr as stripSpawnTokensLine, ht as clipHintsToWidth, i as useSelectStyle, in as tryOpenBrowser, it as InteractionsProvider, j as supportsOAuth, k as oauthUsesManualCodePaste, kt as useSettings, l as buildSkillsConfig, lr as lastContextSizeFromTurns, lt as makeRequestInteraction, m as writeSessionExport, mr as serverToolResultSummary, mt as EMPTY_HINTS, n as ThemeProvider, nn as toggleChainEntry, nr as chatReferenceTokensForPrompt, on as bootTick, ot as buildResumedToolResultsTurn, pt as useInteractionsQueue, q as indexOfServerRow, qt as resolveConfig, r as useColors, rn as useCompletion, rr as createChatTurnsReadTool, rt as ASK_USER_TOOL, s as useTheme, sn as buildUpdateHint, sr as deriveSessionTitle, st as createInteractionTools, t as computeTurnAnchors, tr as envSection, tt as getMcpAuthStatus, u as defaultSkillScanPaths, ur as listSessionMeta, ut as pendingInteractionsFromTurns, vn as discoverAgentsMd, vr as toolCallPreview, vt as buildHints, wt as SETTINGS_CHOICES, x as isOnSafelist, xr as usageByModel, xt as useEnabledToggleSet, y as addToSafelist, yr as toolResultText, z as mcpToolsCachePath } from "./transcript-anchors-DUusSVpZ.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-DBfHPAgz.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";
@@ -7,16 +7,16 @@ import { l as errorMessage } from "./errors-DJUxZg9b.js";
7
7
  import { M as findGitRoot, j as projectUserPaths } from "./resolve-HtxmBWs9.js";
8
8
  import { a as createRingBufferSink, i as createLoggingHooks, l as logFileTimestamp, n as createFileSink, o as fanoutSink, r as createLogger, s as formatLogLine, t as consoleSink } from "./logger-DBX9uYOw.js";
9
9
  import { i as sniffImageMediaType } from "./media-sniff-Bn76JxAu.js";
10
+ import { d as McpOAuthProvider, n as connectMcpServers, p as hasAuthorizationHeader } from "./mcp-H6bqyM94.js";
10
11
  import { n as flattenTurns, r as formatTokenUsage, t as effectiveInputFromTurn } from "./stats-DAKBEKjc.js";
11
12
  import { replaceDynamicSection } from "./prompt.js";
12
13
  import { a as getExtensionSessionState, i as demoteStaleRunningStatus, o as loadSession, s as setExtensionSessionState, t as createSession } from "./session-DzrXKLw-.js";
13
14
  import { o as withTimeout } from "./timeout-BDetbuN6.js";
14
- import { n as connectMcpServers, u as McpOAuthProvider } from "./mcp-BCaN_Gyg.js";
15
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
16
  import { s as resolveCompletionSubmit } from "./completion-core-BRo86R_L.js";
17
17
  import { i as installSkillFromSpec, r as SkillInstallError } from "./skills-DKWqoaYH.js";
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-Clf-R27F.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-Bb479DFB.js";
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-DreurRAq.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-CpdQebUy.js";
20
20
  import { createDockerContext } from "./contexts/docker.js";
21
21
  import { createTuiStore } from "./session/sqlite.js";
22
22
  import { $ as extractEditPayload, B as isVisible, C as chunkMarkdownForRender, E as stripHtmlCommentsOutsideCode, F as BASH_CALL_ID_PREFIX, G as streamEventSearchText, H as promptHistoryFromEvents, I as EDIT_TOOL_NAMES, J as buildContextualDiff, K as turnSelectionOwnership, L as isEditErrorResult, N as blendHsl, O as createSkillsCompletionProvider, P as buildLinearRamp, R as isTurnHighlighted, T as splitMarkdownCodeBlocks, U as selectableTurnIds, V as isVisibleDuringSearch, W as selectionAfterTurnRemoval, X as clipDiffForRender, Y as buildUnifiedDiff, _ as turnContextSize, a as TOOL_DISPLAY, at as buildEditOutcomesAnnotation, b as filterModelCatalog, ct as parseEditOutcomesFromResult, d as finalizeStreamingMarkdown, dt as stripEditOutcomesAnnotation, et as filetypeFromPath, f as finalizeStreamingMarkdownForOwner, ft as summarizeOutcomes, g as revealEarlierTranscriptEvents, h as replaceTranscriptTurnEvents, ht as createChatsCompletionProvider, i as turnAsText, j as createFilesCompletionProvider, k as uniqueSkillNamesFromReferences, lt as resolveApprovalForPayload, n as deleteTurnSafely, o as displayNameFor, p as hiddenEarlierTranscriptCount, r as truncateTurnsAt, rt as summarizeEditPayload, s as formatToolCall, st as mergeApprovalAndBodyOutcomes, tt as previewEditPayload, u as capTranscriptEvents, ut as rewriteMultiEditHeader, v as splitPromptSegments, w as normalizeMarkdownCodeFences, y as buildModelCatalog, z as isUserBashEvent } from "./turn-operations-CokV32wf.js";
@@ -153,7 +153,7 @@ function upsertMcpServer(filePath, entry) {
153
153
  action: "added"
154
154
  };
155
155
  }
156
- const raw = JSON.parse(readFileSync(filePath, "utf8"));
156
+ const raw = parseJsonTolerant(readFileSync(filePath, "utf8"));
157
157
  if (Array.isArray(raw)) {
158
158
  const idx = raw.findIndex((e) => isRecord(e) && e.name === entry.name);
159
159
  const next = [...raw];
@@ -196,6 +196,41 @@ function upsertMcpServer(filePath, entry) {
196
196
  }
197
197
  throw new Error(`${filePath} is not a recognized mcps.json shape (array, map, or {"mcpServers": …}).`);
198
198
  }
199
+ /**
200
+ * Remove the entry named `name` from the mcps config at `filePath`,
201
+ * preserving the file's shape and everything else in it — the removal
202
+ * counterpart of {@link upsertMcpServer}. Throws when the file is
203
+ * missing, malformed, an unrecognized shape, or has no entry of that
204
+ * name (the caller shows the message on the settings op line; silently
205
+ * "succeeding" on a no-op would lie about what happened).
206
+ */
207
+ function removeMcpServer(filePath, name) {
208
+ if (!existsSync(filePath)) throw new Error(`${filePath} does not exist.`);
209
+ const raw = parseJsonTolerant(readFileSync(filePath, "utf8"));
210
+ if (Array.isArray(raw)) {
211
+ const next = raw.filter((e) => !(isRecord(e) && e.name === name));
212
+ if (next.length === raw.length) throw new Error(`no entry named "${name}" in ${filePath}.`);
213
+ writeFileSync(filePath, `${JSON.stringify(next, null, 2)}\n`);
214
+ return { path: filePath };
215
+ }
216
+ if (isRecord(raw)) {
217
+ if (isRecord(raw.mcpServers)) {
218
+ if (!(name in raw.mcpServers)) throw new Error(`no entry named "${name}" in ${filePath}.`);
219
+ const { [name]: _dropped, ...rest } = raw.mcpServers;
220
+ const next = {
221
+ ...raw,
222
+ mcpServers: rest
223
+ };
224
+ writeFileSync(filePath, `${JSON.stringify(next, null, 2)}\n`);
225
+ return { path: filePath };
226
+ }
227
+ if (!(name in raw)) throw new Error(`no entry named "${name}" in ${filePath}.`);
228
+ const { [name]: _dropped, ...next } = raw;
229
+ writeFileSync(filePath, `${JSON.stringify(next, null, 2)}\n`);
230
+ return { path: filePath };
231
+ }
232
+ throw new Error(`${filePath} is not a recognized mcps.json shape (array, map, or {"mcpServers": …}).`);
233
+ }
199
234
  /** Map-shaped files key entries by name — the inner object must not repeat it. */
200
235
  function stripName(entry) {
201
236
  const { name: _name, ...rest } = entry;
@@ -4915,6 +4950,39 @@ function DiscoveryShell({ children }) {
4915
4950
  const [mcpsErrors, setMcpsErrors] = useState([]);
4916
4951
  const [filesCatalog, setFilesCatalog] = useState([]);
4917
4952
  const [mcpToolsByServer, setMcpToolsByServer] = useState(() => readToolsByServer(dataDir));
4953
+ /**
4954
+ * Seed MCP auth badges from the on-disk credential store so rows show
4955
+ * their auth state IMMEDIATELY (first paint, discovery refresh) instead
4956
+ * of waiting a bootstrap round-trip:
4957
+ * - `auth: 'oauth'` entries — `✓ authed` when tokens exist, else
4958
+ * `! needs login` (the flag says OAuth is required either way).
4959
+ * - flagless entries — `✓ authed` only when stored tokens exist
4960
+ * (tokens are proof the server is OAuth: the same rule the bootstrap
4961
+ * uses to attach the provider). No tokens → stay `idle`; most
4962
+ * flagless servers are plain stdio/http and "needs login" would lie.
4963
+ * - static-Authorization entries — untouched (the user owns auth).
4964
+ * The badge is a PREDICTION from disk state; bootstrap events
4965
+ * (connect / auth-required / auth-error) overwrite it with ground truth.
4966
+ */
4967
+ const seedMcpAuthBadges = useCallback((servers) => {
4968
+ for (const entry of servers) {
4969
+ const explicitOauth = entry.config.auth === "oauth";
4970
+ if (!explicitOauth && hasAuthorizationHeader(entry.config.headers)) continue;
4971
+ const hasTokens = !!mcpCredentialStore.load(entry.config.name)?.tokens;
4972
+ if (explicitOauth) dispatchAuthRef.current(hasTokens ? {
4973
+ type: "auth-success",
4974
+ name: entry.config.name
4975
+ } : {
4976
+ type: "auth-required",
4977
+ name: entry.config.name,
4978
+ reason: "no-tokens"
4979
+ });
4980
+ else if (hasTokens) dispatchAuthRef.current({
4981
+ type: "auth-success",
4982
+ name: entry.config.name
4983
+ });
4984
+ }
4985
+ }, [mcpCredentialStore]);
4918
4986
  const filesSlotRef = useRef(null);
4919
4987
  const skillsSlotRef = useRef(null);
4920
4988
  /** Last-published MCP discovery result — see the value-compare in `refreshMcps`. */
@@ -4967,18 +5035,7 @@ function DiscoveryShell({ children }) {
4967
5035
  setMcpsCatalog(servers);
4968
5036
  setMcpsErrors(errors);
4969
5037
  bootTick(`discovery:mcps (${servers.length} servers, ${errors.length} parse errors)`);
4970
- for (const entry of servers) {
4971
- if (entry.config.auth !== "oauth") continue;
4972
- const hasTokens = !!mcpCredentialStore.load(entry.config.name)?.tokens;
4973
- dispatchAuthRef.current(hasTokens ? {
4974
- type: "auth-success",
4975
- name: entry.config.name
4976
- } : {
4977
- type: "auth-required",
4978
- name: entry.config.name,
4979
- reason: "no-tokens"
4980
- });
4981
- }
5038
+ seedMcpAuthBadges(servers);
4982
5039
  } catch (err) {
4983
5040
  debugLog$1("discoverProjectMcps failed", err);
4984
5041
  }
@@ -4989,7 +5046,7 @@ function DiscoveryShell({ children }) {
4989
5046
  }, [
4990
5047
  projectDir,
4991
5048
  config.prefix,
4992
- mcpCredentialStore,
5049
+ seedMcpAuthBadges,
4993
5050
  foreignAgents
4994
5051
  ]);
4995
5052
  const ensureFiles = useCallback(() => filesSlotRef.current?.ensure() ?? Promise.resolve([]), []);
@@ -5007,30 +5064,20 @@ function DiscoveryShell({ children }) {
5007
5064
  servers,
5008
5065
  errors
5009
5066
  });
5010
- if (fingerprint === mcpsFingerprintRef.current) return Promise.resolve();
5067
+ if (fingerprint === mcpsFingerprintRef.current) return Promise.resolve(false);
5011
5068
  mcpsFingerprintRef.current = fingerprint;
5012
5069
  setMcpsCatalog(servers);
5013
5070
  setMcpsErrors(errors);
5014
- for (const entry of servers) {
5015
- if (entry.config.auth !== "oauth") continue;
5016
- const hasTokens = !!mcpCredentialStore.load(entry.config.name)?.tokens;
5017
- dispatchAuthRef.current(hasTokens ? {
5018
- type: "auth-success",
5019
- name: entry.config.name
5020
- } : {
5021
- type: "auth-required",
5022
- name: entry.config.name,
5023
- reason: "no-tokens"
5024
- });
5025
- }
5071
+ seedMcpAuthBadges(servers);
5026
5072
  } catch (err) {
5027
5073
  debugLog$1("refreshMcps failed", err);
5074
+ return Promise.resolve(false);
5028
5075
  }
5029
- return Promise.resolve();
5076
+ return Promise.resolve(true);
5030
5077
  }, [
5031
5078
  projectDir,
5032
5079
  config.prefix,
5033
- mcpCredentialStore,
5080
+ seedMcpAuthBadges,
5034
5081
  foreignAgents
5035
5082
  ]);
5036
5083
  useEffect(() => {
@@ -13370,6 +13417,8 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
13370
13417
  const [skillOp, setSkillOp] = useState(null);
13371
13418
  const [mcpAdd, setMcpAdd] = useState(false);
13372
13419
  const [mcpOp, setMcpOp] = useState(null);
13420
+ const [skillRemoveArmed, setSkillRemoveArmed] = useState(null);
13421
+ const [mcpRemoveArmed, setMcpRemoveArmed] = useState(null);
13373
13422
  const [cursorByTab, setCursorByTab] = useState(() => Object.fromEntries(tabOrder.map((id) => [id, 0])));
13374
13423
  const [query, setQuery] = useState("");
13375
13424
  const [logsMinLevel, setLogsMinLevel] = useState("debug");
@@ -13792,6 +13841,13 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
13792
13841
  useEffect(() => {
13793
13842
  setRemoveArmed(null);
13794
13843
  }, [focusedExtension?.name, activeTab]);
13844
+ const focusedSkill = activeTab === "skills" ? filteredSkills[cursor] : void 0;
13845
+ useEffect(() => {
13846
+ setSkillRemoveArmed(null);
13847
+ }, [focusedSkill?.name, activeTab]);
13848
+ useEffect(() => {
13849
+ setMcpRemoveArmed(null);
13850
+ }, [focusedMcp?.config.name, activeTab]);
13795
13851
  const oauthPasteActive = activeTab === "mcps" && focusedMcpStatus?.kind === "authorizing" && !!focusedMcpStatus.url;
13796
13852
  useEffect(() => {
13797
13853
  if (!oauthPasteActive && !editingInputKey) inputRef.current?.focus();
@@ -13999,15 +14055,69 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
13999
14055
  if (key.ctrl && key.name === "g" && activeTab === "skills" && actions?.onInstallSkill) {
14000
14056
  key.preventDefault();
14001
14057
  if (skillOp?.kind === "busy") return;
14058
+ setSkillRemoveArmed(null);
14002
14059
  setSkillInstallOpen(true);
14003
14060
  return;
14004
14061
  }
14062
+ if (key.ctrl && key.name === "x" && activeTab === "skills" && actions?.onRemoveSkill) {
14063
+ key.preventDefault();
14064
+ const skill = focusedSkill;
14065
+ if (!skill || skillOp?.kind === "busy") return;
14066
+ if (skillRemoveArmed !== skill.name) {
14067
+ setSkillRemoveArmed(skill.name);
14068
+ setSkillOp({
14069
+ kind: "notice",
14070
+ text: `delete "${skill.name}" from disk? press ctrl+x again to confirm.`
14071
+ });
14072
+ return;
14073
+ }
14074
+ setSkillRemoveArmed(null);
14075
+ setSkillOp({
14076
+ kind: "busy",
14077
+ text: `deleting "${skill.name}"…`
14078
+ });
14079
+ actions.onRemoveSkill(skill).then((message) => setSkillOp({
14080
+ kind: "notice",
14081
+ text: message
14082
+ })).catch((err) => setSkillOp({
14083
+ kind: "error",
14084
+ text: errorMessage(err)
14085
+ }));
14086
+ return;
14087
+ }
14005
14088
  if (key.ctrl && key.name === "g" && activeTab === "mcps" && actions?.onAddMcp) {
14006
14089
  key.preventDefault();
14007
14090
  if (mcpOp?.kind === "busy") return;
14091
+ setMcpRemoveArmed(null);
14008
14092
  setMcpAdd(true);
14009
14093
  return;
14010
14094
  }
14095
+ if (key.ctrl && key.name === "x" && activeTab === "mcps" && actions?.onRemoveMcp) {
14096
+ key.preventDefault();
14097
+ if (focusedMcpRow?.kind !== "server" || !focusedMcp || mcpOp?.kind === "busy") return;
14098
+ const name = focusedMcp.config.name;
14099
+ if (mcpRemoveArmed !== name) {
14100
+ setMcpRemoveArmed(name);
14101
+ setMcpOp({
14102
+ kind: "notice",
14103
+ text: `remove "${name}" from ${displayPath$1(focusedMcp.path, homedir())}? press ctrl+x again to confirm.`
14104
+ });
14105
+ return;
14106
+ }
14107
+ setMcpRemoveArmed(null);
14108
+ setMcpOp({
14109
+ kind: "busy",
14110
+ text: `removing "${name}"…`
14111
+ });
14112
+ actions.onRemoveMcp(focusedMcp).then((message) => setMcpOp({
14113
+ kind: "notice",
14114
+ text: message
14115
+ })).catch((err) => setMcpOp({
14116
+ kind: "error",
14117
+ text: errorMessage(err)
14118
+ }));
14119
+ return;
14120
+ }
14011
14121
  if (key.ctrl && key.name === "r") {
14012
14122
  if (activeTab === "skills" && actions?.onRefreshSkills) {
14013
14123
  actions.onRefreshSkills();
@@ -14271,6 +14381,8 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
14271
14381
  canInstallExtension: !!actions?.onInstallExtension,
14272
14382
  canInstallSkill: !!actions?.onInstallSkill,
14273
14383
  canAddMcp: !!actions?.onAddMcp,
14384
+ canRemoveSkill: !!actions?.onRemoveSkill,
14385
+ canRemoveMcp: !!actions?.onRemoveMcp,
14274
14386
  focusedExtensionExpandable: activeTab === "extensions" && extensionVisibleRows[cursor] !== void 0 && (() => {
14275
14387
  const row = extensionVisibleRows[cursor];
14276
14388
  return row.kind === "config" || row.kind === "extension" && row.hasConfig;
@@ -14934,6 +15046,7 @@ function SkillsList({ items, enabledSet, totalCount, cursor, highlightBg, query,
14934
15046
  const bg = focused ? highlightBg : void 0;
14935
15047
  const enabled = enabledSet.has(entry.name);
14936
15048
  const description = truncateToCols(entry.description ?? "", descCols);
15049
+ const origin = skillOriginLabel(entry);
14937
15050
  return /* @__PURE__ */ jsxs("box", {
14938
15051
  id: anchorIdFor(i),
14939
15052
  style: {
@@ -14957,6 +15070,10 @@ function SkillsList({ items, enabledSet, totalCount, cursor, highlightBg, query,
14957
15070
  /* @__PURE__ */ jsx$1("span", {
14958
15071
  fg: focused ? COLOR.brand : COLOR.dim,
14959
15072
  children: entry.name
15073
+ }),
15074
+ origin && /* @__PURE__ */ jsxs("span", {
15075
+ fg: COLOR.mute,
15076
+ children: [" · ", origin]
14960
15077
  })
14961
15078
  ]
14962
15079
  }), /* @__PURE__ */ jsx$1("text", {
@@ -15642,7 +15759,7 @@ function renderMcpErrors(errors, home, warnColor) {
15642
15759
  }, err.path))
15643
15760
  });
15644
15761
  }
15645
- function Hints({ activeTab, focusedMcp, focusedMcpRow, focusedMcpStatus, canRefreshSkills, canRefreshMcps, canEditExtension, canCreateExtension, canUpdateExtension, canRemoveExtension, canInstallExtension, canInstallSkill, canAddMcp, focusedExtensionExpandable }) {
15762
+ function Hints({ activeTab, focusedMcp, focusedMcpRow, focusedMcpStatus, canRefreshSkills, canRefreshMcps, canEditExtension, canCreateExtension, canUpdateExtension, canRemoveExtension, canInstallExtension, canInstallSkill, canAddMcp, canRemoveSkill, canRemoveMcp, focusedExtensionExpandable }) {
15646
15763
  const COLOR = useColors();
15647
15764
  const onServerRow = focusedMcpRow?.kind === "server";
15648
15765
  const showLogin = activeTab === "mcps" && onServerRow && !!focusedMcp && !!focusedMcpStatus && canLogin$1(focusedMcp, focusedMcpStatus);
@@ -15733,6 +15850,14 @@ function Hints({ activeTab, focusedMcp, focusedMcpRow, focusedMcpStatus, canRefr
15733
15850
  }),
15734
15851
  " install"
15735
15852
  ] }),
15853
+ activeTab === "skills" && canRemoveSkill && /* @__PURE__ */ jsxs("span", { children: [
15854
+ " · ",
15855
+ /* @__PURE__ */ jsx$1("span", {
15856
+ fg: COLOR.warn,
15857
+ children: "ctrl+x"
15858
+ }),
15859
+ " delete"
15860
+ ] }),
15736
15861
  activeTab === "mcps" && canAddMcp && /* @__PURE__ */ jsxs("span", { children: [
15737
15862
  " · ",
15738
15863
  /* @__PURE__ */ jsx$1("span", {
@@ -15741,6 +15866,14 @@ function Hints({ activeTab, focusedMcp, focusedMcpRow, focusedMcpStatus, canRefr
15741
15866
  }),
15742
15867
  " add server"
15743
15868
  ] }),
15869
+ activeTab === "mcps" && canRemoveMcp && onServerRow && /* @__PURE__ */ jsxs("span", { children: [
15870
+ " · ",
15871
+ /* @__PURE__ */ jsx$1("span", {
15872
+ fg: COLOR.warn,
15873
+ children: "ctrl+x"
15874
+ }),
15875
+ " remove"
15876
+ ] }),
15744
15877
  showLogin && /* @__PURE__ */ jsxs("span", { children: [
15745
15878
  " · ",
15746
15879
  /* @__PURE__ */ jsx$1("span", {
@@ -15789,7 +15922,22 @@ function generalCorpus(item) {
15789
15922
  return parts.join(" ").toLowerCase();
15790
15923
  }
15791
15924
  function skillCorpus(s) {
15792
- return `${s.name} ${s.description ?? ""}`.toLowerCase();
15925
+ return `${s.name} ${s.description ?? ""} ${skillOriginLabel(s)}`.toLowerCase();
15926
+ }
15927
+ /**
15928
+ * Short provenance chip for a skill row. Standard scan roots
15929
+ * (`.agents/skills`, `.{prefix}/skills` — project or user) are fully
15930
+ * described by the scope tag; a skill from a custom `skillsScanPaths`
15931
+ * entry shows the tilde-collapsed path itself so the user can tell
15932
+ * WHICH extra root contributed it. Inline / builtin skills (no
15933
+ * directory on disk) show their source tag.
15934
+ */
15935
+ function skillOriginLabel(skill) {
15936
+ const dir = skill.baseDir ?? (skill.location ? dirname(skill.location) : void 0);
15937
+ if (!dir) return skill.source ?? "inline";
15938
+ const root = dirname(dir);
15939
+ if (/\/\.[^/]+\/skills$/.test(root)) return skill.source ?? "project";
15940
+ return truncateToCols(displayPath$1(root, homedir()), 36);
15793
15941
  }
15794
15942
  function extensionCorpus(e) {
15795
15943
  return [
@@ -17082,6 +17230,8 @@ function AppShell() {
17082
17230
  const dispatchAuth = useMcpAuthDispatch();
17083
17231
  const dispatchAuthRef = useRef(dispatchAuth);
17084
17232
  dispatchAuthRef.current = dispatchAuth;
17233
+ const pendingAutoLoginMcpRef = useRef(null);
17234
+ const onLoginMcpRef = useRef(null);
17085
17235
  const skillsCatalogRef = useRef(skillsCatalog);
17086
17236
  skillsCatalogRef.current = skillsCatalog;
17087
17237
  const enabledSkillsRef = useRef(settings.enabledSkills);
@@ -18232,6 +18382,13 @@ function AppShell() {
18232
18382
  name,
18233
18383
  reason
18234
18384
  });
18385
+ if (pendingAutoLoginMcpRef.current === name) {
18386
+ pendingAutoLoginMcpRef.current = null;
18387
+ onLoginMcpRef.current?.(name);
18388
+ }
18389
+ });
18390
+ agent.hooks.hook("mcp:bootstrap:settled", () => {
18391
+ pendingAutoLoginMcpRef.current = null;
18235
18392
  });
18236
18393
  agent.hooks.hook("mcp:bootstrap:end", (ctx) => {
18237
18394
  if (ctx.ok) return;
@@ -18275,6 +18432,7 @@ function AppShell() {
18275
18432
  type: "connected",
18276
18433
  name
18277
18434
  });
18435
+ if (pendingAutoLoginMcpRef.current === name) pendingAutoLoginMcpRef.current = null;
18278
18436
  });
18279
18437
  agent.hooks.hook("stream:thinking", ({ delta, turnId }) => stream.queueStreamDelta("thinking", delta, { turnId }));
18280
18438
  agent.hooks.hook("stream:text", ({ delta, turnId }) => stream.queueStreamDelta("markdown", delta, { turnId }));
@@ -19243,9 +19401,73 @@ function AppShell() {
19243
19401
  cwd: cwdRef.current,
19244
19402
  prefix: config.prefix
19245
19403
  }), entry);
19246
- await onRefreshMcps();
19404
+ pendingAutoLoginMcpRef.current = entry.name;
19405
+ if (!await onRefreshMcps() && pendingAutoLoginMcpRef.current === entry.name) pendingAutoLoginMcpRef.current = null;
19247
19406
  return `${result.action} "${entry.name}" → ${compactPath(result.path, 64)}`;
19248
19407
  }, [config.prefix, onRefreshMcps]);
19408
+ /**
19409
+ * Delete a discovered skill's directory (Settings → Skills → ctrl+x,
19410
+ * two-press confirm in the modal). Only skills that live on disk in a
19411
+ * root we can safely mutate qualify; the SKILL.md existence check
19412
+ * guards against ever recursively deleting a directory that isn't a
19413
+ * skill (e.g. a stale catalog entry pointing at a reused path).
19414
+ *
19415
+ * `.agents/skills/` entries ARE deletable — deliberately asymmetric
19416
+ * with `onRemoveMcp`'s ownership gate. An MCP removal edits a shared
19417
+ * multi-entry FILE other tools author (clobber risk); a skill is a
19418
+ * self-contained directory that is itself the install/uninstall unit,
19419
+ * and cross-tool installers (skills.sh, `ctrl+g` here) drop skills
19420
+ * into `.agents/skills/` expecting exactly this kind of removal.
19421
+ */
19422
+ const onRemoveSkill = useCallback(async (skill) => {
19423
+ const dir = skill.baseDir ?? (skill.location ? dirname(skill.location) : void 0);
19424
+ if (!dir || skill.source === "inline" || skill.source === "builtin") throw new Error(`"${skill.name}" is ${skill.source ?? "inline"} — nothing on disk to delete.`);
19425
+ if (!existsSync(join(dir, "SKILL.md"))) throw new Error(`${compactPath(dir)} has no SKILL.md — refusing to delete.`);
19426
+ rmSync(dir, {
19427
+ recursive: true,
19428
+ force: true
19429
+ });
19430
+ const allow = enabledSkillsRef.current;
19431
+ if (allow?.includes(skill.name)) {
19432
+ const next = allow.filter((n) => n !== skill.name);
19433
+ enabledSkillsRef.current = next;
19434
+ setSetting("enabledSkills", next);
19435
+ }
19436
+ await onRefreshSkills();
19437
+ return `deleted "${skill.name}" (${compactPath(dir)})`;
19438
+ }, [setSetting, onRefreshSkills]);
19439
+ /**
19440
+ * Remove an MCP server from the config file that declared it
19441
+ * (Settings → MCPs → ctrl+x, two-press confirm in the modal).
19442
+ * Ownership-gated: only entries in a `.{prefix}/` file are removable —
19443
+ * `.agents/` siblings and imported foreign configs (Claude / Cursor /
19444
+ * VS Code) are cross-tool files we read but never rewrite, so the
19445
+ * error points the user at the declaring file instead.
19446
+ */
19447
+ const onRemoveMcp = useCallback(async (entry) => {
19448
+ const name = entry.config.name;
19449
+ const prefix = (config.prefix ?? ".zidane").replace(/^\./, "");
19450
+ if (!entry.path.includes(`/.${prefix}/`)) throw new Error(`"${name}" is defined in ${compactPath(entry.path, 64)} — zidane doesn't edit that file; remove the entry there.`);
19451
+ removeMcpServer(entry.path, name);
19452
+ const enabled = enabledMcpsRef.current;
19453
+ if (enabled?.includes(name)) {
19454
+ const next = enabled.filter((n) => n !== name);
19455
+ enabledMcpsRef.current = next;
19456
+ setSetting("enabledMcps", next);
19457
+ }
19458
+ const deny = disabledMcpToolsRef.current;
19459
+ if (deny && name in deny) {
19460
+ const { [name]: _dropped, ...rest } = deny;
19461
+ disabledMcpToolsRef.current = rest;
19462
+ setSetting("disabledMcpTools", rest);
19463
+ }
19464
+ await onRefreshMcps();
19465
+ return `removed "${name}" from ${compactPath(entry.path, 64)}`;
19466
+ }, [
19467
+ config.prefix,
19468
+ setSetting,
19469
+ onRefreshMcps
19470
+ ]);
19249
19471
  const popupOpenRef = useRef(false);
19250
19472
  const onPopupOpenChange = useCallback((open) => {
19251
19473
  popupOpenRef.current = open;
@@ -19706,7 +19928,7 @@ function AppShell() {
19706
19928
  * user runs comes from that same gate; each `runAgent` awaits its
19707
19929
  * `agent.run` so the single-flight agent is never re-entered.
19708
19930
  */
19709
- const runCommand = useCallback(async (invocation) => {
19931
+ const runCommand = useCallback(async (invocation, attachments = []) => {
19710
19932
  const session = sessionRef.current;
19711
19933
  const agent = agentRef.current;
19712
19934
  if (!session) return;
@@ -19766,6 +19988,11 @@ function AppShell() {
19766
19988
  const ctx = {
19767
19989
  args: invocation.args,
19768
19990
  argv: invocation.argv,
19991
+ ...attachments.length > 0 ? { attachments: attachments.map((a) => ({
19992
+ name: a.name,
19993
+ mediaType: a.mediaType,
19994
+ data: a.content.toString("base64")
19995
+ })) } : {},
19769
19996
  data: {},
19770
19997
  emit: emitBlock,
19771
19998
  runAgent: async (agentPrompt, agentOpts) => {
@@ -20142,7 +20369,7 @@ function AppShell() {
20142
20369
  if (!opts?.wake) {
20143
20370
  const invocation = parseCommandInvocation(prompt, commandsRef.current);
20144
20371
  if (invocation) {
20145
- await runCommand(invocation);
20372
+ await runCommand(invocation, attachments);
20146
20373
  return;
20147
20374
  }
20148
20375
  const bashCommand = parseBashInvocation(prompt);
@@ -20518,6 +20745,7 @@ function AppShell() {
20518
20745
  mcpCredentialStore,
20519
20746
  rebuildActiveAgent
20520
20747
  ]);
20748
+ onLoginMcpRef.current = onLoginMcp;
20521
20749
  const onLogoutMcp = useCallback((name) => {
20522
20750
  mcpCredentialStore.delete(name);
20523
20751
  const entry = mcpsCatalogRef.current.find((d) => d.config.name === name);
@@ -21199,6 +21427,8 @@ function AppShell() {
21199
21427
  }),
21200
21428
  onInstallSkill,
21201
21429
  onAddMcp,
21430
+ onRemoveSkill,
21431
+ onRemoveMcp,
21202
21432
  onPickCompactionModel
21203
21433
  }
21204
21434
  }));