zidane 5.6.3 → 5.6.7

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.
package/dist/tui.js CHANGED
@@ -1,9 +1,9 @@
1
- import { $r as TODO_STATUS_GLYPHS, $t as isEditErrorResult, A as getSafelist, An as resolveApprovalForPayload, At as SettingsProvider, B as oauthUsesManualCodePaste, Bn as keybindingsPath, Br as piIdOf, Ct as buildHints, D as useSafeModeQueue, Dt as DEFAULT_SETTINGS, E as useSafeModeActions, En as buildEditOutcomesAnnotation, Er as setProviderCredential, Et as useEnabledToggleSet, F as suggestSafelistEntry, Ft as resolveChipColor, G as indexOfEntry, Gt as useDiscoveryOptional, H as supportsOAuth, Ht as createDiscoverySlot, In as KEYBINDING_DEFS, Ir as modelSupportsReasoning, It as resolveTheme, J as discoverProjectMcps, Jn as uniqueSkillNamesFromReferences, Jt as resolveConfig, K as buildMcpServers, Kt as ConfigProvider, L as splitPromptSegments, Mn as stripEditOutcomesAnnotation, Mt as useSettings, Nn as summarizeOutcomes, On as mergeApprovalAndBodyOutcomes, Ot as SETTINGS_CHOICES, Pn as findGitRoot, Pr as getContextWindow, Qt as eventsFromTurns, R as formatPathForCwd, Rn as ensureKeybindingsFile, Si as envSection, Sn as previewEditPayload, St as generateSessionTitle, T as SafeModeProvider, Tt as listProjectFiles, U as buildModelCatalog, Ut as DiscoveryProvider, V as runOAuthLogin, Vn as matchesBinding, W as filterModelCatalog, Wt as useDiscovery, Xn as createFilesCompletionProvider, Yt as EDIT_TOOL_NAMES, Z as createFileMcpCredentialStore, Zt as deriveSessionTitle, _ as turnContextSize, _n as buildUnifiedDiff, _t as EMPTY_HINTS, a as computeTurnAnchors, an as marginTopFor, ar as tryOpenBrowser, at as splitMarkdownCodeBlocks, b as defaultSkillScanPaths, bi as buildBuildSystem, bn as extractEditPayload, br as detectAuth, bt as truncateTrailing, c as formatToolCall, cn as stripSpawnTokensLine, cr as buildUpdateHint, d as useSelectStyle, dn as toolCallPreview, en as isTurnHighlighted, et as McpAuthProvider, f as useSurfaces, fn as toolResultText, ft as makeRequestInteraction, g as finalizeStreamingMarkdownForOwner, gn as buildContextualDiff, gt as useInteractionsQueue, h as finalizeStreamingMarkdown, ht as useInteractionsActions, i as turnAsText, ir as buildLinearRamp, j as isOnSafelist, jn as rewriteMultiEditHeader, jt as clampFps, k as addToSafelist, kn as parseEditOutcomesFromResult, kt as SETTINGS_TOGGLES, l as ThemeProvider, li as useActiveTodos, ln as sumRunCosts, lr as useUpdateCheck, lt as buildResumedToolResultsTurn, m as useTheme, mn as updateToolEventOutcomes, n as deleteTurnSafely, nn as lastContextSizeFromTurns, nr as useCompletion, nt as useMcpAuthState, o as TOOL_DISPLAY, pn as turnSelectionOwnership, pt as pendingInteractionsFromTurns, qn as createSkillsCompletionProvider, qr as accentColor, qt as useConfig, r as truncateTurnsAt, rn as listSessionMeta, rr as blendHsl, rt as getMcpAuthStatus, s as displayNameFor, sn as selectableTurnIds, sr as bootTick, st as InteractionsProvider, tn as isVisible, tt as useMcpAuthDispatch, u as useColors, ut as createInteractionTools, v as useStreamBuffer, vr as AUTO_COMPACT_MIN_GROWTH_FRACTION, vt as clipHintsToWidth, w as writeSessionExport, wn as summarizeEditPayload, x as discoverProjectSkills, xi as buildPlanSystem, xn as filetypeFromPath, y as buildSkillsConfig, yr as shouldAutoCompact, yt as hintsLength, z as fetchOAuthRedirect, zn as formatBindingForDisplay } from "./turn-operations-CH7rnULP.js";
2
- import { A as resolvePersistDir, B as formatTaskStatus, H as previewLine, I as ageString, L as compactPath, O as cleanupPersistedSession, R as fmtTokens, U as shortId, V as formatTaskSummary, j as resolveTasksDir, p as createAgent, z as formatDuration } from "./tools-Bgx8OBqK.js";
1
+ import { A as getSafelist, Ar as setProviderCredential, At as SETTINGS_TOGGLES, B as oauthUsesManualCodePaste, Bn as KEYBINDING_KEY_COL_WIDTH, Br as modelSupportsReasoning, Cn as extractEditPayload, Cr as shouldAutoCompact, Ct as buildHints, D as useSafeModeQueue, Di as buildBuildSystem, Dn as summarizeEditPayload, Dt as DEFAULT_SETTINGS, E as useSafeModeActions, Et as useEnabledToggleSet, F as suggestSafelistEntry, Fn as stripEditOutcomesAnnotation, G as indexOfEntry, Gn as matchesBinding, Gr as discoverAgentsMd, Gt as useDiscovery, H as supportsOAuth, Hn as formatBindingForDisplay, In as summarizeOutcomes, It as resolveChipColor, J as discoverProjectMcps, Jt as useConfig, K as buildMcpServers, Kt as useDiscoveryOptional, L as splitPromptSegments, Lt as resolveTheme, Mn as parseEditOutcomesFromResult, Mt as clampFps, Nn as resolveApprovalForPayload, Nt as useSettings, Oi as buildPlanSystem, Ot as SETTINGS_CATEGORIES, Pn as rewriteMultiEditHeader, Qn as uniqueSkillNamesFromReferences, Qt as EDIT_TOOL_NAMES, R as formatPathForCwd, Rn as KEYBINDING_DEFS, Rr as getContextWindow, Sr as AUTO_COMPACT_MIN_GROWTH_FRACTION, St as generateSessionTitle, T as SafeModeProvider, Tn as previewEditPayload, Tt as listProjectFiles, U as buildModelCatalog, Un as groupBindings, Ut as createDiscoverySlot, V as runOAuthLogin, Vn as ensureKeybindingsFile, W as filterModelCatalog, Wn as keybindingsPath, Wr as piIdOf, Wt as DiscoveryProvider, Yt as resolveConfig, Z as createFileMcpCredentialStore, Zn as createSkillsCompletionProvider, _ as turnContextSize, _n as updateToolEventOutcomes, _t as EMPTY_HINTS, a as computeTurnAnchors, an as lastContextSizeFromTurns, at as splitMarkdownCodeBlocks, b as defaultSkillScanPaths, bn as buildUnifiedDiff, bt as truncateTrailing, c as formatToolCall, cn as marginTopFor, cr as buildLinearRamp, d as useSelectStyle, dn as stripSpawnTokensLine, dr as bootTick, ei as accentColor, en as deriveSessionTitle, er as createFilesCompletionProvider, et as McpAuthProvider, f as useSurfaces, fn as sumRunCosts, fr as buildUpdateHint, ft as makeRequestInteraction, g as finalizeStreamingMarkdownForOwner, gi as useActiveTodos, gn as turnSelectionOwnership, gt as useInteractionsQueue, h as finalizeStreamingMarkdown, hn as toolResultText, ht as useInteractionsActions, i as turnAsText, in as isVisible, j as isOnSafelist, jn as mergeApprovalAndBodyOutcomes, jt as SettingsProvider, k as addToSafelist, ki as envSection, kn as buildEditOutcomesAnnotation, kt as SETTINGS_CHOICES, l as ThemeProvider, lr as tryOpenBrowser, lt as buildResumedToolResultsTurn, m as useTheme, mn as toolCallPreview, n as deleteTurnSafely, nn as isEditErrorResult, nt as useMcpAuthState, o as TOOL_DISPLAY, oi as TODO_STATUS_GLYPHS, on as listSessionMeta, or as useCompletion, pr as useUpdateCheck, pt as pendingInteractionsFromTurns, qr as findGitRoot, qt as ConfigProvider, r as truncateTurnsAt, rn as isTurnHighlighted, rt as getMcpAuthStatus, s as displayNameFor, sr as blendHsl, st as InteractionsProvider, tn as eventsFromTurns, tt as useMcpAuthDispatch, u as useColors, un as selectableTurnIds, ut as createInteractionTools, v as useStreamBuffer, vt as clipHintsToWidth, w as writeSessionExport, wn as filetypeFromPath, wr as detectAuth, x as discoverProjectSkills, y as buildSkillsConfig, yn as buildContextualDiff, yt as hintsLength, z as fetchOAuthRedirect } from "./turn-operations-D-OQYUgS.js";
2
+ import { A as resolvePersistDir, B as formatTaskStatus, H as previewLine, I as ageString, L as compactPath, O as cleanupPersistedSession, R as fmtTokens, U as shortId, V as formatTaskSummary, j as resolveTasksDir, p as createAgent, z as formatDuration } from "./tools-FerA0zSl.js";
3
3
  import { n as createProcessContext } from "./contexts-BOtMvzli.js";
4
4
  import { c as errorMessage } from "./errors-DdZXnyXE.js";
5
5
  import { s as McpOAuthProvider, t as connectMcpServers } from "./mcp-ngMS0S6N.js";
6
- import { C as summaryToTurn, a as selectFilesFromSession, r as buildPostCompactAttachments, s as compactConversation, t as loginMcpServer } from "./login-YckkS-Bq.js";
6
+ import { C as summaryToTurn, a as selectFilesFromSession, r as buildPostCompactAttachments, s as compactConversation, t as loginMcpServer } from "./login-Btpliwct.js";
7
7
  import { n as formatTokenUsage } from "./stats-Lc3zL3RM.js";
8
8
  import { n as loadSession, t as createSession } from "./session-BoEW_wCR.js";
9
9
  import { createTuiStore } from "./session/sqlite.js";
@@ -2017,7 +2017,7 @@ MarkdownBlock.displayName = "MarkdownBlock";
2017
2017
  const TOOL_RESULT_MAX_LINES = 6;
2018
2018
  function ToolResultBlock({ text, indent }) {
2019
2019
  const COLOR = useColors();
2020
- const rawLines = text.split("\n");
2020
+ const rawLines = stripAnsiSequences(text).split("\n");
2021
2021
  let end = rawLines.length;
2022
2022
  while (end > 0 && rawLines[end - 1].trim() === "") end--;
2023
2023
  if (end === 0) return null;
@@ -3142,23 +3142,7 @@ function EffortRow({ level, isCurrent, isFocused, highlightBg }) {
3142
3142
  }
3143
3143
  //#endregion
3144
3144
  //#region src/tui/keybindings-modal.tsx
3145
- /**
3146
- * Fixed column width for the rendered key — derived once from
3147
- * {@link KEYBINDING_DEFS} so adding an action with a wider default spec
3148
- * (`ctrl+shift+x`, etc.) automatically grows the column instead of
3149
- * truncating the label. Falls back to a sensible minimum so empty /
3150
- * one-glyph defaults don't collapse the layout.
3151
- */
3152
- const KEY_COL_WIDTH = (() => {
3153
- let max = 8;
3154
- for (const def of KEYBINDING_DEFS) {
3155
- const width = formatBindingForDisplay(def.default).length;
3156
- if (width > max) max = width;
3157
- }
3158
- return max + 2;
3159
- })();
3160
3145
  function KeybindingsModal({ bindings, filePath, onEditFile, onClose }) {
3161
- const COLOR = useColors();
3162
3146
  const SURFACE = useSurfaces();
3163
3147
  const { height: termHeight } = useTerminalDimensions();
3164
3148
  const scrollRef = useRef(null);
@@ -3192,24 +3176,77 @@ function KeybindingsModal({ bindings, filePath, onEditFile, onClose }) {
3192
3176
  flexGrow: 1,
3193
3177
  flexShrink: 1
3194
3178
  },
3195
- children: sections.map((section, sectionIdx) => /* @__PURE__ */ jsxs("box", {
3196
- style: {
3197
- flexDirection: "column",
3198
- flexShrink: 0,
3199
- marginTop: sectionIdx === 0 ? 0 : 1
3200
- },
3201
- children: [/* @__PURE__ */ jsx(SectionHeader, { label: section.group }), section.rows.map((row) => /* @__PURE__ */ jsx(BindingRow, {
3202
- def: row.def,
3203
- spec: row.spec
3204
- }, row.def.action))]
3205
- }, section.group))
3179
+ children: /* @__PURE__ */ jsx(KeybindingsCatalog, { sections })
3206
3180
  })
3207
- }), /* @__PURE__ */ jsx(EditFileButton, {
3181
+ }), /* @__PURE__ */ jsx(KeybindingsEditFileButton, {
3208
3182
  filePath,
3209
- highlightBg: SURFACE.selection,
3210
- brand: COLOR.brand,
3211
- mute: COLOR.mute,
3212
- dim: COLOR.dim
3183
+ highlightBg: SURFACE.selection
3184
+ })]
3185
+ });
3186
+ }
3187
+ function KeybindingsCatalog({ sections }) {
3188
+ return /* @__PURE__ */ jsx("box", {
3189
+ style: { flexDirection: "column" },
3190
+ children: sections.map((section, sectionIdx) => /* @__PURE__ */ jsxs("box", {
3191
+ style: {
3192
+ flexDirection: "column",
3193
+ flexShrink: 0,
3194
+ marginTop: sectionIdx === 0 ? 0 : 1
3195
+ },
3196
+ children: [/* @__PURE__ */ jsx(SectionHeader, { label: section.group }), section.rows.map((row) => /* @__PURE__ */ jsx(BindingRow, {
3197
+ def: row.def,
3198
+ spec: row.spec
3199
+ }, row.def.action))]
3200
+ }, section.group))
3201
+ });
3202
+ }
3203
+ /**
3204
+ * Highlighted "Edit keybindings file" button. Used as the standalone
3205
+ * modal's pinned footer AND as the Settings-modal Keybindings tab's
3206
+ * pinned action row. Activation is the caller's responsibility (the
3207
+ * standalone modal uses its own `useKeyboard`; the Settings modal
3208
+ * routes `↵` through its top-level handler).
3209
+ */
3210
+ function KeybindingsEditFileButton({ filePath, highlightBg }) {
3211
+ const COLOR = useColors();
3212
+ const display = filePath ? compactPath(filePath) : "~/.zidane/keybindings.json";
3213
+ return /* @__PURE__ */ jsxs("box", {
3214
+ style: {
3215
+ flexShrink: 0,
3216
+ flexDirection: "column",
3217
+ paddingLeft: 1,
3218
+ paddingRight: 1,
3219
+ backgroundColor: highlightBg
3220
+ },
3221
+ children: [/* @__PURE__ */ jsxs("text", {
3222
+ wrapMode: "none",
3223
+ children: [
3224
+ /* @__PURE__ */ jsx("span", {
3225
+ fg: COLOR.brand,
3226
+ children: "▶ "
3227
+ }),
3228
+ /* @__PURE__ */ jsx("span", {
3229
+ fg: COLOR.brand,
3230
+ children: "Edit keybindings file"
3231
+ }),
3232
+ /* @__PURE__ */ jsx("span", {
3233
+ fg: COLOR.mute,
3234
+ children: " "
3235
+ }),
3236
+ /* @__PURE__ */ jsx("span", {
3237
+ fg: COLOR.brand,
3238
+ children: "›"
3239
+ })
3240
+ ]
3241
+ }), /* @__PURE__ */ jsxs("text", {
3242
+ wrapMode: "none",
3243
+ children: [/* @__PURE__ */ jsx("span", {
3244
+ fg: COLOR.mute,
3245
+ children: " "
3246
+ }), /* @__PURE__ */ jsx("span", {
3247
+ fg: COLOR.dim,
3248
+ children: `${display} — restart to apply changes`
3249
+ })]
3213
3250
  })]
3214
3251
  });
3215
3252
  }
@@ -3232,7 +3269,7 @@ function SectionHeader({ label }) {
3232
3269
  function BindingRow({ def, spec }) {
3233
3270
  const COLOR = useColors();
3234
3271
  const display = formatBindingForDisplay(spec);
3235
- const keyText = (display || "—").padEnd(KEY_COL_WIDTH, " ");
3272
+ const keyText = (display || "—").padEnd(KEYBINDING_KEY_COL_WIDTH, " ");
3236
3273
  return /* @__PURE__ */ jsxs("box", {
3237
3274
  style: {
3238
3275
  flexDirection: "column",
@@ -3252,7 +3289,7 @@ function BindingRow({ def, spec }) {
3252
3289
  }), /* @__PURE__ */ jsx("box", {
3253
3290
  style: {
3254
3291
  flexShrink: 0,
3255
- paddingLeft: KEY_COL_WIDTH
3292
+ paddingLeft: KEYBINDING_KEY_COL_WIDTH
3256
3293
  },
3257
3294
  children: /* @__PURE__ */ jsx("text", {
3258
3295
  wrapMode: "word",
@@ -3262,48 +3299,6 @@ function BindingRow({ def, spec }) {
3262
3299
  })]
3263
3300
  });
3264
3301
  }
3265
- function EditFileButton({ filePath, highlightBg, brand, mute, dim }) {
3266
- const display = filePath ? compactPath(filePath) : "~/.zidane/keybindings.json";
3267
- return /* @__PURE__ */ jsxs("box", {
3268
- style: {
3269
- flexShrink: 0,
3270
- flexDirection: "column",
3271
- paddingLeft: 1,
3272
- paddingRight: 1,
3273
- backgroundColor: highlightBg
3274
- },
3275
- children: [/* @__PURE__ */ jsxs("text", {
3276
- wrapMode: "none",
3277
- children: [
3278
- /* @__PURE__ */ jsx("span", {
3279
- fg: brand,
3280
- children: "▶ "
3281
- }),
3282
- /* @__PURE__ */ jsx("span", {
3283
- fg: brand,
3284
- children: "Edit keybindings file"
3285
- }),
3286
- /* @__PURE__ */ jsx("span", {
3287
- fg: mute,
3288
- children: " "
3289
- }),
3290
- /* @__PURE__ */ jsx("span", {
3291
- fg: brand,
3292
- children: "›"
3293
- })
3294
- ]
3295
- }), /* @__PURE__ */ jsxs("text", {
3296
- wrapMode: "none",
3297
- children: [/* @__PURE__ */ jsx("span", {
3298
- fg: mute,
3299
- children: " "
3300
- }), /* @__PURE__ */ jsx("span", {
3301
- fg: dim,
3302
- children: `${display} — restart to apply changes`
3303
- })]
3304
- })]
3305
- });
3306
- }
3307
3302
  function CountsBadge$1({ count }) {
3308
3303
  const COLOR = useColors();
3309
3304
  return /* @__PURE__ */ jsxs("text", {
@@ -3324,36 +3319,6 @@ function CountsBadge$1({ count }) {
3324
3319
  ]
3325
3320
  });
3326
3321
  }
3327
- /**
3328
- * Walk `KEYBINDING_DEFS` in order and bucket rows into contiguous
3329
- * sections by `group`. Preserves catalog order — if two actions in the
3330
- * same group are split by an entry from another group, they'd render
3331
- * as two separate sections with the same header (catalog order wins
3332
- * over "merge same-group entries" so the on-screen story matches the
3333
- * on-disk file).
3334
- */
3335
- function groupBindings(bindings) {
3336
- const sections = [];
3337
- for (const def of KEYBINDING_DEFS) {
3338
- const last = sections[sections.length - 1];
3339
- const spec = bindings[def.action] ?? "";
3340
- if (last && last.group === def.group) {
3341
- last.rows.push({
3342
- def,
3343
- spec
3344
- });
3345
- continue;
3346
- }
3347
- sections.push({
3348
- group: def.group,
3349
- rows: [{
3350
- def,
3351
- spec
3352
- }]
3353
- });
3354
- }
3355
- return sections;
3356
- }
3357
3322
  //#endregion
3358
3323
  //#region src/tui/model-picker.tsx
3359
3324
  /**
@@ -7999,22 +7964,22 @@ function statusColor(status, COLOR) {
7999
7964
  }
8000
7965
  //#endregion
8001
7966
  //#region src/tui/settings-modal.tsx
8002
- const TAB_ORDER = [
8003
- "general",
8004
- "skills",
8005
- "mcps"
8006
- ];
8007
7967
  const TAB_LABELS = {
8008
- general: "General",
7968
+ ...Object.fromEntries(SETTINGS_CATEGORIES.map((c) => [c.id, c.label])),
7969
+ keybindings: "Keybindings",
7970
+ authentication: "Authentication",
8009
7971
  skills: "Skills",
8010
7972
  mcps: "MCP servers"
8011
7973
  };
7974
+ function isCategoryTab(id) {
7975
+ return id !== "skills" && id !== "mcps" && id !== "keybindings" && id !== "authentication";
7976
+ }
8012
7977
  function anchorIdFor(index) {
8013
7978
  return `settings-row-${index}`;
8014
7979
  }
8015
7980
  const COL_TITLE = " ";
8016
7981
  const SPACER_CHECKBOX_WIDTH = " ";
8017
- function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCatalogProp, mcpsErrors: mcpsErrorsProp, actions } = {}) {
7982
+ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCatalogProp, mcpsErrors: mcpsErrorsProp, keybindings, keybindingsPath, authentication, actions } = {}) {
8018
7983
  const COLOR = useColors();
8019
7984
  const SURFACE = useSurfaces();
8020
7985
  const { settings, toggle: toggleBoolean, setSetting } = useSettings();
@@ -8035,13 +8000,19 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
8035
8000
  keyOf: (m) => m.config.name,
8036
8001
  settingKey: "enabledMcps"
8037
8002
  });
8038
- const [activeTab, setActiveTab] = useState("general");
8039
- const [cursorByTab, setCursorByTab] = useState({
8040
- general: 0,
8041
- skills: 0,
8042
- mcps: 0
8043
- });
8003
+ const tabOrder = useMemo(() => {
8004
+ const tabs = [...SETTINGS_CATEGORIES.map((c) => c.id)];
8005
+ if (keybindings) tabs.push("keybindings");
8006
+ if (authentication) tabs.push("authentication");
8007
+ tabs.push("skills", "mcps");
8008
+ return tabs;
8009
+ }, [keybindings, authentication]);
8010
+ const [activeTab, setActiveTab] = useState(tabOrder[0]);
8011
+ const [cursorByTab, setCursorByTab] = useState(() => Object.fromEntries(tabOrder.map((id) => [id, 0])));
8044
8012
  const [query, setQuery] = useState("");
8013
+ useEffect(() => {
8014
+ if (!tabOrder.includes(activeTab)) setActiveTab(tabOrder[0]);
8015
+ }, [tabOrder, activeTab]);
8045
8016
  useEffect(() => {
8046
8017
  inputRef.current?.focus();
8047
8018
  }, []);
@@ -8055,15 +8026,17 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
8055
8026
  ...c
8056
8027
  }));
8057
8028
  const acts = [];
8058
- if (actions?.onOpenKeybindings) acts.push({
8029
+ if (actions?.onOpenKeybindings && !keybindings) acts.push({
8059
8030
  kind: "action",
8031
+ category: "ui",
8060
8032
  id: "keybindings",
8061
8033
  label: "Keybindings",
8062
8034
  description: "open ~/.zidane/keybindings.json (restart to apply)",
8063
8035
  onPick: actions.onOpenKeybindings
8064
8036
  });
8065
- if (actions?.onReauth) acts.push({
8037
+ if (actions?.onReauth && !authentication) acts.push({
8066
8038
  kind: "action",
8039
+ category: "agent",
8067
8040
  id: "reauth",
8068
8041
  label: "Authentication",
8069
8042
  description: "switch provider, add another, or re-authenticate",
@@ -8074,14 +8047,29 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
8074
8047
  ...choices,
8075
8048
  ...acts
8076
8049
  ];
8077
- }, [actions]);
8078
- const filteredGeneral = useMemo(() => generalItems.filter((it) => matchesQuery(generalCorpus(it), query)), [generalItems, query]);
8050
+ }, [
8051
+ actions,
8052
+ keybindings,
8053
+ authentication
8054
+ ]);
8055
+ const filteredByCategory = useMemo(() => {
8056
+ const buckets = Object.fromEntries(SETTINGS_CATEGORIES.map((c) => [c.id, []]));
8057
+ for (const it of generalItems) {
8058
+ if (!matchesQuery(generalCorpus(it), query)) continue;
8059
+ buckets[it.category].push(it);
8060
+ }
8061
+ return buckets;
8062
+ }, [generalItems, query]);
8079
8063
  const filteredSkills = useMemo(() => skillsCatalog.filter((s) => matchesQuery(skillCorpus(s), query)), [skillsCatalog, query]);
8080
8064
  const filteredMcps = useMemo(() => mcpsCatalog.filter((m) => matchesQuery(mcpCorpus(m), query)), [mcpsCatalog, query]);
8065
+ const filteredProviders = useMemo(() => (authentication?.providers ?? []).filter((p) => matchesQuery(providerCorpus(p), query)), [authentication?.providers, query]);
8066
+ const authRowCount = filteredProviders.length + (authentication ? 1 : 0);
8081
8067
  const filteredSize = {
8082
- general: filteredGeneral.length,
8068
+ ...Object.fromEntries(SETTINGS_CATEGORIES.map((c) => [c.id, filteredByCategory[c.id].length])),
8083
8069
  skills: filteredSkills.length,
8084
- mcps: filteredMcps.length
8070
+ mcps: filteredMcps.length,
8071
+ keybindings: 0,
8072
+ authentication: authRowCount
8085
8073
  };
8086
8074
  const cursor = Math.min(cursorByTab[activeTab], Math.max(0, filteredSize[activeTab] - 1));
8087
8075
  const moveCursor = useCallback((delta) => setCursorByTab((prev) => {
@@ -8101,6 +8089,7 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
8101
8089
  }));
8102
8090
  }, [activeTab]);
8103
8091
  useEffect(() => {
8092
+ if (activeTab === "keybindings") return;
8104
8093
  const sb = scrollboxRef.current;
8105
8094
  if (!sb) return;
8106
8095
  const handle = requestAnimationFrame(() => {
@@ -8112,6 +8101,15 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
8112
8101
  activeTab,
8113
8102
  query
8114
8103
  ]);
8104
+ useEffect(() => {
8105
+ if (activeTab !== "keybindings") return;
8106
+ const sb = scrollboxRef.current;
8107
+ if (!sb) return;
8108
+ const handle = requestAnimationFrame(() => {
8109
+ sb.scrollTo(0);
8110
+ });
8111
+ return () => cancelAnimationFrame(handle);
8112
+ }, [activeTab]);
8115
8113
  const focusedMcp = filteredMcps[cursor];
8116
8114
  const focusedMcpStatus = focusedMcp ? getMcpAuthStatus(authState, focusedMcp.config.name) : void 0;
8117
8115
  const oauthPasteActive = activeTab === "mcps" && focusedMcpStatus?.kind === "authorizing" && !!focusedMcpStatus.url;
@@ -8123,21 +8121,43 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
8123
8121
  if (oauthPasteActive) return;
8124
8122
  if (!key.ctrl && !key.meta && !key.shift && (key.name === "left" || key.name === "right")) {
8125
8123
  key.preventDefault();
8126
- const idx = TAB_ORDER.indexOf(activeTab);
8127
- setActiveTab(key.name === "left" ? TAB_ORDER[(idx - 1 + TAB_ORDER.length) % TAB_ORDER.length] : TAB_ORDER[(idx + 1) % TAB_ORDER.length]);
8124
+ const idx = tabOrder.indexOf(activeTab);
8125
+ setActiveTab(key.name === "left" ? tabOrder[(idx - 1 + tabOrder.length) % tabOrder.length] : tabOrder[(idx + 1) % tabOrder.length]);
8128
8126
  return;
8129
8127
  }
8130
8128
  if (key.name === "up" || key.ctrl && key.name === "p") {
8131
- moveCursor(-1);
8129
+ if (activeTab === "keybindings") scrollboxRef.current?.scrollBy(-2);
8130
+ else moveCursor(-1);
8132
8131
  return;
8133
8132
  }
8134
8133
  if (key.name === "down" || key.ctrl && key.name === "n") {
8135
- moveCursor(1);
8134
+ if (activeTab === "keybindings") scrollboxRef.current?.scrollBy(2);
8135
+ else moveCursor(1);
8136
+ return;
8137
+ }
8138
+ if ((key.name === "pageup" || key.ctrl && key.name === "b") && activeTab === "keybindings") {
8139
+ scrollboxRef.current?.scrollBy(-.5, "viewport");
8140
+ return;
8141
+ }
8142
+ if ((key.name === "pagedown" || key.ctrl && key.name === "f") && activeTab === "keybindings") {
8143
+ scrollboxRef.current?.scrollBy(.5, "viewport");
8144
+ return;
8145
+ }
8146
+ if (key.name === "home" && activeTab === "keybindings") {
8147
+ scrollboxRef.current?.scrollTo(0);
8148
+ return;
8149
+ }
8150
+ if (key.name === "end" && activeTab === "keybindings") {
8151
+ const sb = scrollboxRef.current;
8152
+ if (sb) sb.scrollTo({
8153
+ x: 0,
8154
+ y: sb.scrollHeight
8155
+ });
8136
8156
  return;
8137
8157
  }
8138
8158
  if (key.name === "return") {
8139
- if (activeTab === "general") {
8140
- const it = filteredGeneral[cursor];
8159
+ if (isCategoryTab(activeTab)) {
8160
+ const it = filteredByCategory[activeTab][cursor];
8141
8161
  if (!it) return;
8142
8162
  if (it.kind === "toggle") toggleBoolean(it.key);
8143
8163
  else if (it.kind === "choice") {
@@ -8156,6 +8176,24 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
8156
8176
  if (activeTab === "mcps") {
8157
8177
  const m = filteredMcps[cursor];
8158
8178
  if (m) mcpsToggle.toggle(m.config.name);
8179
+ return;
8180
+ }
8181
+ if (activeTab === "keybindings") {
8182
+ actions?.onOpenKeybindings?.();
8183
+ return;
8184
+ }
8185
+ if (activeTab === "authentication") {
8186
+ if (cursor === filteredProviders.length || !filteredProviders[cursor]) {
8187
+ actions?.onReauth?.();
8188
+ return;
8189
+ }
8190
+ const provider = filteredProviders[cursor];
8191
+ if (provider.available && actions?.onPickProvider) {
8192
+ actions.onPickProvider(provider);
8193
+ return;
8194
+ }
8195
+ actions?.onReauth?.();
8196
+ return;
8159
8197
  }
8160
8198
  return;
8161
8199
  }
@@ -8178,23 +8216,29 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
8178
8216
  if (activeTab === "mcps" && actions?.onRefreshMcps) actions.onRefreshMcps();
8179
8217
  }
8180
8218
  });
8219
+ const totalsByCategory = useMemo(() => {
8220
+ const t = Object.fromEntries(SETTINGS_CATEGORIES.map((c) => [c.id, 0]));
8221
+ for (const it of generalItems) t[it.category]++;
8222
+ return t;
8223
+ }, [generalItems]);
8181
8224
  return /* @__PURE__ */ jsxs(Modal, {
8182
8225
  title: "settings",
8183
- maxWidth: 140,
8226
+ maxWidth: 160,
8184
8227
  minWidth: 76,
8185
- maxHeight: 44,
8228
+ maxHeight: 50,
8186
8229
  horizontalMargin: 2,
8187
8230
  verticalMargin: 1,
8188
8231
  children: [
8189
8232
  /* @__PURE__ */ jsx("box", {
8190
8233
  style: { flexShrink: 0 },
8191
8234
  children: /* @__PURE__ */ jsx(TabStrip, {
8235
+ order: tabOrder,
8192
8236
  active: activeTab,
8193
8237
  counts: {
8194
- general: {
8195
- current: filteredGeneral.length,
8196
- total: generalItems.length
8197
- },
8238
+ ...Object.fromEntries(SETTINGS_CATEGORIES.map((c) => [c.id, {
8239
+ current: filteredByCategory[c.id].length,
8240
+ total: totalsByCategory[c.id]
8241
+ }])),
8198
8242
  skills: {
8199
8243
  current: filteredSkills.length,
8200
8244
  total: skillsCatalog.length
@@ -8234,8 +8278,8 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
8234
8278
  },
8235
8279
  stickyScroll: false,
8236
8280
  children: [
8237
- activeTab === "general" && /* @__PURE__ */ jsx(GeneralList, {
8238
- items: filteredGeneral,
8281
+ isCategoryTab(activeTab) && /* @__PURE__ */ jsx(GeneralList, {
8282
+ items: filteredByCategory[activeTab],
8239
8283
  cursor,
8240
8284
  highlightBg: SURFACE.selection,
8241
8285
  query
@@ -8257,9 +8301,27 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
8257
8301
  query,
8258
8302
  errors: mcpsErrors,
8259
8303
  authState
8304
+ }),
8305
+ activeTab === "keybindings" && keybindings && /* @__PURE__ */ jsx(KeybindingsList, {
8306
+ bindings: keybindings,
8307
+ query
8308
+ }),
8309
+ activeTab === "authentication" && authentication && /* @__PURE__ */ jsx(AuthenticationList, {
8310
+ view: authentication,
8311
+ providers: filteredProviders,
8312
+ cursor,
8313
+ highlightBg: SURFACE.selection,
8314
+ query
8260
8315
  })
8261
8316
  ]
8262
8317
  }),
8318
+ activeTab === "keybindings" && /* @__PURE__ */ jsx("box", {
8319
+ style: { flexShrink: 0 },
8320
+ children: /* @__PURE__ */ jsx(KeybindingsEditFileButton, {
8321
+ filePath: keybindingsPath,
8322
+ highlightBg: SURFACE.selection
8323
+ })
8324
+ }),
8263
8325
  activeTab === "mcps" && focusedMcp && focusedMcpStatus && /* @__PURE__ */ jsx("box", {
8264
8326
  style: { flexShrink: 0 },
8265
8327
  children: renderMcpDetailPanel(focusedMcp, focusedMcpStatus, COLOR)
@@ -8277,7 +8339,7 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
8277
8339
  ]
8278
8340
  });
8279
8341
  }
8280
- function TabStrip({ active, counts }) {
8342
+ function TabStrip({ order, active, counts }) {
8281
8343
  const COLOR = useColors();
8282
8344
  const SURFACE = useSurfaces();
8283
8345
  return /* @__PURE__ */ jsx("box", {
@@ -8285,16 +8347,17 @@ function TabStrip({ active, counts }) {
8285
8347
  flexDirection: "row",
8286
8348
  height: 1
8287
8349
  },
8288
- children: TAB_ORDER.map((id, i) => {
8350
+ children: order.map((id, i) => {
8289
8351
  const isActive = id === active;
8290
8352
  const label = TAB_LABELS[id];
8291
- const { current, total } = counts[id];
8292
- const badge = current !== total ? `${current}/${total}` : `${total}`;
8353
+ const tabCounts = counts[id];
8354
+ const showBadge = !!tabCounts;
8355
+ const badge = tabCounts ? tabCounts.current !== tabCounts.total ? `${tabCounts.current}/${tabCounts.total}` : `${tabCounts.total}` : "";
8293
8356
  return /* @__PURE__ */ jsx("box", {
8294
8357
  style: {
8295
8358
  paddingLeft: 1,
8296
8359
  paddingRight: 1,
8297
- marginRight: i === TAB_ORDER.length - 1 ? 0 : 1,
8360
+ marginRight: i === order.length - 1 ? 0 : 1,
8298
8361
  backgroundColor: isActive ? SURFACE.selection : void 0
8299
8362
  },
8300
8363
  children: /* @__PURE__ */ jsxs("text", {
@@ -8302,7 +8365,7 @@ function TabStrip({ active, counts }) {
8302
8365
  children: [/* @__PURE__ */ jsx("span", {
8303
8366
  fg: isActive ? COLOR.brand : COLOR.dim,
8304
8367
  children: label
8305
- }), /* @__PURE__ */ jsx("span", {
8368
+ }), showBadge && /* @__PURE__ */ jsx("span", {
8306
8369
  fg: isActive ? COLOR.brand : COLOR.mute,
8307
8370
  children: ` ${badge}`
8308
8371
  })]
@@ -8445,7 +8508,12 @@ function McpsList({ items, enabledSet, totalCount, cursor, highlightBg, query, e
8445
8508
  fg: COLOR.model,
8446
8509
  children: " mcps.json "
8447
8510
  }),
8448
- "into",
8511
+ "(or",
8512
+ /* @__PURE__ */ jsx("span", {
8513
+ fg: COLOR.model,
8514
+ children: " mcp.json "
8515
+ }),
8516
+ ") into",
8449
8517
  /* @__PURE__ */ jsx("span", {
8450
8518
  fg: COLOR.model,
8451
8519
  children: " .zidane/ "
@@ -8512,6 +8580,179 @@ function McpsList({ items, enabledSet, totalCount, cursor, highlightBg, query, e
8512
8580
  })]
8513
8581
  });
8514
8582
  }
8583
+ function KeybindingsList({ bindings, query }) {
8584
+ const sections = useMemo(() => groupBindings(bindings), [bindings]);
8585
+ const filteredSections = useMemo(() => {
8586
+ if (!query.trim()) return sections;
8587
+ return sections.map((section) => ({
8588
+ group: section.group,
8589
+ rows: section.rows.filter((row) => matchesQuery(keybindingCorpus(row), query))
8590
+ })).filter((s) => s.rows.length > 0);
8591
+ }, [sections, query]);
8592
+ if (filteredSections.length === 0) return /* @__PURE__ */ jsx(EmptyRow, { label: `no keybindings match "${query}"` });
8593
+ return /* @__PURE__ */ jsx(KeybindingsCatalog, { sections: filteredSections });
8594
+ }
8595
+ function AuthenticationList({ view, providers, cursor, highlightBg, query }) {
8596
+ const COLOR = useColors();
8597
+ const home = homedir();
8598
+ const totalProviders = view.providers?.length ?? 0;
8599
+ const addRowIndex = providers.length;
8600
+ return /* @__PURE__ */ jsxs("box", {
8601
+ style: { flexDirection: "column" },
8602
+ children: [
8603
+ /* @__PURE__ */ jsxs("box", {
8604
+ style: {
8605
+ flexDirection: "column",
8606
+ flexShrink: 0,
8607
+ paddingLeft: 1,
8608
+ paddingRight: 1
8609
+ },
8610
+ children: [
8611
+ /* @__PURE__ */ jsx("text", {
8612
+ wrapMode: "none",
8613
+ children: /* @__PURE__ */ jsx("span", {
8614
+ fg: COLOR.brand,
8615
+ children: "Current"
8616
+ })
8617
+ }),
8618
+ /* @__PURE__ */ jsxs("text", {
8619
+ wrapMode: "none",
8620
+ children: [
8621
+ /* @__PURE__ */ jsx("span", {
8622
+ fg: COLOR.mute,
8623
+ children: " provider "
8624
+ }),
8625
+ /* @__PURE__ */ jsx("span", {
8626
+ fg: view.currentProviderLabel ? COLOR.accent : COLOR.mute,
8627
+ children: view.currentProviderLabel ?? "— not selected —"
8628
+ }),
8629
+ view.currentProviderKey && view.currentProviderLabel && view.currentProviderKey !== view.currentProviderLabel.toLowerCase() && /* @__PURE__ */ jsx("span", {
8630
+ fg: COLOR.dim,
8631
+ children: ` (${view.currentProviderKey})`
8632
+ })
8633
+ ]
8634
+ }),
8635
+ /* @__PURE__ */ jsxs("text", {
8636
+ wrapMode: "none",
8637
+ children: [/* @__PURE__ */ jsx("span", {
8638
+ fg: COLOR.mute,
8639
+ children: " model "
8640
+ }), /* @__PURE__ */ jsx("span", {
8641
+ fg: view.currentModel ? COLOR.model : COLOR.mute,
8642
+ children: view.currentModel ?? "— not selected —"
8643
+ })]
8644
+ })
8645
+ ]
8646
+ }),
8647
+ /* @__PURE__ */ jsx("box", {
8648
+ style: {
8649
+ flexDirection: "column",
8650
+ flexShrink: 0,
8651
+ marginTop: 1,
8652
+ paddingLeft: 1,
8653
+ paddingRight: 1
8654
+ },
8655
+ children: /* @__PURE__ */ jsxs("text", {
8656
+ wrapMode: "none",
8657
+ children: [/* @__PURE__ */ jsx("span", {
8658
+ fg: COLOR.brand,
8659
+ children: "Providers"
8660
+ }), /* @__PURE__ */ jsx("span", {
8661
+ fg: COLOR.mute,
8662
+ children: totalProviders > 0 ? ` ${providers.length}/${totalProviders}` : ""
8663
+ })]
8664
+ })
8665
+ }),
8666
+ totalProviders === 0 && /* @__PURE__ */ jsx("box", {
8667
+ style: {
8668
+ paddingLeft: 3,
8669
+ paddingRight: 1
8670
+ },
8671
+ children: /* @__PURE__ */ jsx("text", {
8672
+ fg: COLOR.mute,
8673
+ children: "No providers registered."
8674
+ })
8675
+ }),
8676
+ totalProviders > 0 && providers.length === 0 && /* @__PURE__ */ jsx(EmptyRow, { label: `no providers match "${query}"` }),
8677
+ providers.map((p, i) => {
8678
+ const isCurrent = view.currentProviderKey === p.key;
8679
+ const focused = i === cursor;
8680
+ const bg = focused ? highlightBg : void 0;
8681
+ const marker = focused ? "▶ " : isCurrent ? "▸ " : " ";
8682
+ const labelColor = focused || isCurrent ? COLOR.brand : p.available ? COLOR.dim : COLOR.mute;
8683
+ return /* @__PURE__ */ jsxs("box", {
8684
+ id: anchorIdFor(i),
8685
+ style: {
8686
+ flexDirection: "column",
8687
+ flexShrink: 0,
8688
+ paddingLeft: 1,
8689
+ paddingRight: 1,
8690
+ backgroundColor: bg
8691
+ },
8692
+ children: [/* @__PURE__ */ jsxs("text", {
8693
+ wrapMode: "none",
8694
+ children: [
8695
+ /* @__PURE__ */ jsx("span", {
8696
+ fg: focused ? COLOR.brand : COLOR.mute,
8697
+ children: marker
8698
+ }),
8699
+ /* @__PURE__ */ jsx("span", {
8700
+ fg: labelColor,
8701
+ children: p.label
8702
+ }),
8703
+ /* @__PURE__ */ jsx("span", {
8704
+ fg: p.available ? COLOR.accent : COLOR.mute,
8705
+ children: p.available ? " ✓ configured" : " · not configured"
8706
+ }),
8707
+ isCurrent && /* @__PURE__ */ jsx("span", {
8708
+ fg: COLOR.mute,
8709
+ children: " (active)"
8710
+ })
8711
+ ]
8712
+ }), p.methods.length > 0 && /* @__PURE__ */ jsx("text", {
8713
+ wrapMode: "none",
8714
+ fg: COLOR.mute,
8715
+ children: ` ${p.methods.map((m) => `${m.source}: ${displayPath$1(m.detail, home)}`).join(" · ")}`
8716
+ })]
8717
+ }, p.key);
8718
+ }),
8719
+ /* @__PURE__ */ jsx(AuthAddRow, {
8720
+ anchorIndex: addRowIndex,
8721
+ focused: cursor === addRowIndex,
8722
+ highlightBg
8723
+ })
8724
+ ]
8725
+ });
8726
+ }
8727
+ function AuthAddRow({ anchorIndex, focused, highlightBg }) {
8728
+ const COLOR = useColors();
8729
+ const bg = focused ? highlightBg : void 0;
8730
+ return /* @__PURE__ */ jsxs("box", {
8731
+ id: anchorIdFor(anchorIndex),
8732
+ style: {
8733
+ flexDirection: "column",
8734
+ flexShrink: 0,
8735
+ marginTop: 1,
8736
+ paddingLeft: 1,
8737
+ paddingRight: 1,
8738
+ backgroundColor: bg
8739
+ },
8740
+ children: [/* @__PURE__ */ jsxs("text", {
8741
+ wrapMode: "none",
8742
+ children: [/* @__PURE__ */ jsx("span", {
8743
+ fg: focused ? COLOR.brand : COLOR.mute,
8744
+ children: focused ? "▶ " : " "
8745
+ }), /* @__PURE__ */ jsx("span", {
8746
+ fg: focused ? COLOR.brand : COLOR.dim,
8747
+ children: "+ add or re-configure a provider"
8748
+ })]
8749
+ }), /* @__PURE__ */ jsx("text", {
8750
+ wrapMode: "none",
8751
+ fg: COLOR.mute,
8752
+ children: " launch the setup wizard"
8753
+ })]
8754
+ });
8755
+ }
8515
8756
  function ToggleRow({ id, label, description, enabled, focused, bg }) {
8516
8757
  const COLOR = useColors();
8517
8758
  return /* @__PURE__ */ jsxs("box", {
@@ -8656,7 +8897,7 @@ function renderMcpDetailPanel(entry, status, COLOR) {
8656
8897
  return /* @__PURE__ */ jsx(McpAuthorizingPanel, {
8657
8898
  serverName: entry.config.name,
8658
8899
  authUrl: status.url,
8659
- maxLineWidth: 134,
8900
+ maxLineWidth: 154,
8660
8901
  inputFocused: true
8661
8902
  });
8662
8903
  }
@@ -8742,12 +8983,12 @@ function Hints({ activeTab, focusedMcp, focusedMcpStatus, canRefreshSkills, canR
8742
8983
  fg: COLOR.warn,
8743
8984
  children: "↑↓"
8744
8985
  }),
8745
- " navigate · ",
8986
+ activeTab === "keybindings" ? " scroll · " : " navigate · ",
8746
8987
  /* @__PURE__ */ jsx("span", {
8747
8988
  fg: COLOR.warn,
8748
8989
  children: "↵"
8749
8990
  }),
8750
- activeTab === "general" ? " toggle/cycle/select" : " toggle",
8991
+ isCategoryTab(activeTab) ? " toggle/cycle/select" : activeTab === "keybindings" ? " edit file" : activeTab === "authentication" ? " switch / re-authenticate" : " toggle",
8751
8992
  showLogin && /* @__PURE__ */ jsxs("span", { children: [
8752
8993
  " · ",
8753
8994
  /* @__PURE__ */ jsx("span", {
@@ -8812,6 +9053,12 @@ function mcpCorpus(m) {
8812
9053
  (m.config.args ?? []).join(" ")
8813
9054
  ].join(" ").toLowerCase();
8814
9055
  }
9056
+ function keybindingCorpus(row) {
9057
+ return `${row.spec} ${row.def.label} ${row.def.description} ${row.def.action}`.toLowerCase();
9058
+ }
9059
+ function providerCorpus(p) {
9060
+ return `${p.label} ${p.key} ${p.methods.map((m) => `${m.source} ${m.detail}`).join(" ")}`.toLowerCase();
9061
+ }
8815
9062
  function mcpDetail(entry) {
8816
9063
  const transport = entry.config.transport;
8817
9064
  return `${transport} · ${transport === "stdio" ? entry.config.command ?? "" : entry.config.url ?? ""}`;
@@ -8824,11 +9071,11 @@ function canLogout$1(status) {
8824
9071
  return status.kind === "authed" || status.kind === "error" || status.kind === "authorizing";
8825
9072
  }
8826
9073
  function searchPlaceholder(tab) {
8827
- switch (tab) {
8828
- case "general": return "search settings — name, description…";
8829
- case "skills": return "search skillsname, description…";
8830
- case "mcps": return "search MCP servers — name, transport, command/URL…";
8831
- }
9074
+ if (tab === "skills") return "search skills — name, description…";
9075
+ if (tab === "mcps") return "search MCP servers — name, transport, command/URL…";
9076
+ if (tab === "keybindings") return "search keybindingsaction, key, description…";
9077
+ if (tab === "authentication") return "search providers — name, method…";
9078
+ return `search ${TAB_LABELS[tab].toLowerCase()} settings — name, description…`;
8832
9079
  }
8833
9080
  function displayPath$1(path, home) {
8834
9081
  if (home && path.startsWith(`${home}/`)) return `~/${path.slice(home.length + 1)}`;
@@ -9564,6 +9811,7 @@ function AppShell() {
9564
9811
  const lastResumedSessionId = initialState.lastSessionId;
9565
9812
  const resumeLastSessionOnLaunch = initialState.settings?.resumeLastSession ?? true;
9566
9813
  const dataDir = config.paths.userDir;
9814
+ const cacheDir = config.paths.cacheDir;
9567
9815
  const [pickedAgent, setPickedAgent] = useState(() => agentRegistry[initialAgentId] ?? Object.values(agentRegistry)[0]);
9568
9816
  const pickedAgentRef = useRef(pickedAgent);
9569
9817
  const safeModeEnabledRef = useRef(settings.safeMode);
@@ -9591,6 +9839,10 @@ function AppShell() {
9591
9839
  useEffect(() => {
9592
9840
  allowInteractionRef.current = settings.allowInteraction;
9593
9841
  }, [settings.allowInteraction]);
9842
+ const userInstructionsScopeRef = useRef(settings.userInstructionsScope);
9843
+ useEffect(() => {
9844
+ userInstructionsScopeRef.current = settings.userInstructionsScope;
9845
+ }, [settings.userInstructionsScope]);
9594
9846
  const autoCompactRef = useRef(settings.autoCompact);
9595
9847
  useEffect(() => {
9596
9848
  autoCompactRef.current = settings.autoCompact;
@@ -9987,14 +10239,20 @@ function AppShell() {
9987
10239
  const allowInteraction = allowInteractionRef.current !== false;
9988
10240
  const interactionTools = allowInteraction ? createInteractionTools({ requestInteraction: makeRequestInteraction(interactions) }) : {};
9989
10241
  const actualCwd = cwdRef.current;
10242
+ const agentsMd = discoverAgentsMd({
10243
+ cwd: actualCwd,
10244
+ prefix: config.prefix,
10245
+ scope: userInstructionsScopeRef.current
10246
+ });
9990
10247
  const envOpts = {
9991
10248
  cwd: actualCwd,
9992
10249
  ...projectDir !== actualCwd ? { projectRoot: projectDir } : {},
9993
- allowInteraction
10250
+ allowInteraction,
10251
+ ...agentsMd.block ? { userInstructions: agentsMd.block } : {}
9994
10252
  };
9995
10253
  const builtInSystem = profile.id === "build" ? buildBuildSystem(envOpts) : profile.id === "plan" ? buildPlanSystem(envOpts) : null;
9996
10254
  const persistDir = resolvePersistDir({
9997
- userDir: dataDir,
10255
+ userDir: cacheDir,
9998
10256
  sessionId: session.id
9999
10257
  });
10000
10258
  const persistBehavior = persistToolResultsRef.current === false ? {
@@ -10002,7 +10260,7 @@ function AppShell() {
10002
10260
  persistDir
10003
10261
  } : { persistDir };
10004
10262
  const tasksDir = resolveTasksDir({
10005
- userDir: dataDir,
10263
+ userDir: cacheDir,
10006
10264
  sessionId: session.id
10007
10265
  });
10008
10266
  const agent = createAgent({
@@ -10386,6 +10644,7 @@ function AppShell() {
10386
10644
  config.prefix,
10387
10645
  interactions,
10388
10646
  dataDir,
10647
+ cacheDir,
10389
10648
  mcpCredentialStore,
10390
10649
  registerInFlightTool,
10391
10650
  unregisterInFlightTool
@@ -11191,11 +11450,11 @@ function AppShell() {
11191
11450
  return;
11192
11451
  }
11193
11452
  cleanupPersistedSession(resolvePersistDir({
11194
- userDir: dataDir,
11453
+ userDir: cacheDir,
11195
11454
  sessionId: id
11196
11455
  }));
11197
11456
  cleanupPersistedSession(resolveTasksDir({
11198
- userDir: dataDir,
11457
+ userDir: cacheDir,
11199
11458
  sessionId: id
11200
11459
  }));
11201
11460
  const wasCurrent = id === currentSession?.id;
@@ -11217,7 +11476,8 @@ function AppShell() {
11217
11476
  teardown,
11218
11477
  refreshSessions,
11219
11478
  stateStore,
11220
- dataDir
11479
+ dataDir,
11480
+ cacheDir
11221
11481
  ]);
11222
11482
  const onGenerateTitle = useCallback(async (sessionId, signal) => {
11223
11483
  if (!picked) throw new Error("No provider picked — open the chat screen first.");
@@ -11535,15 +11795,31 @@ function AppShell() {
11535
11795
  return;
11536
11796
  }
11537
11797
  if (matchesBinding(key, keybindings.openSettings) && screen !== "auth") {
11538
- modal.open(/* @__PURE__ */ jsx(SettingsModal, { actions: {
11539
- onReauth,
11540
- onOpenKeybindings: onOpenKeybindingsFile,
11541
- onLoginMcp,
11542
- onLogoutMcp,
11543
- onCancelLoginMcp,
11544
- onRefreshSkills,
11545
- onRefreshMcps
11546
- } }));
11798
+ const allProviders = detectAuth(config.paths.userDir, providerRegistry);
11799
+ const onPickProviderFromSettings = (provider) => {
11800
+ modal.close();
11801
+ onPickProvider(provider);
11802
+ };
11803
+ modal.open(/* @__PURE__ */ jsx(SettingsModal, {
11804
+ keybindings,
11805
+ keybindingsPath: keybindingsPath(config.paths.userDir),
11806
+ authentication: {
11807
+ currentProviderLabel: picked?.provider.label,
11808
+ currentProviderKey: picked?.provider.key,
11809
+ currentModel: picked?.model,
11810
+ providers: allProviders
11811
+ },
11812
+ actions: {
11813
+ onReauth,
11814
+ onPickProvider: onPickProviderFromSettings,
11815
+ onOpenKeybindings: onOpenKeybindingsFile,
11816
+ onLoginMcp,
11817
+ onLogoutMcp,
11818
+ onCancelLoginMcp,
11819
+ onRefreshSkills,
11820
+ onRefreshMcps
11821
+ }
11822
+ }));
11547
11823
  return;
11548
11824
  }
11549
11825
  if (matchesBinding(key, keybindings.openSessionDetails)) {
@@ -11654,7 +11930,7 @@ function AppShell() {
11654
11930
  packageName: autoUpdateConfig?.packageName ?? "",
11655
11931
  currentVersion: autoUpdateConfig?.currentVersion ?? "",
11656
11932
  enabled: !!autoUpdateConfig && settings.checkForUpdates,
11657
- cacheDir: dataDir,
11933
+ cacheDir,
11658
11934
  registry: autoUpdateConfig?.registry,
11659
11935
  channel: autoUpdateConfig?.channel,
11660
11936
  cacheTtlMs: autoUpdateConfig?.cacheTtlMs
@@ -12107,7 +12383,12 @@ function McpsSettingsModal({ catalog, errors, onLogin, onLogout, onCancelLogin,
12107
12383
  fg: COLOR.model,
12108
12384
  children: " mcps.json "
12109
12385
  }),
12110
- "into",
12386
+ "(or",
12387
+ /* @__PURE__ */ jsx("span", {
12388
+ fg: COLOR.model,
12389
+ children: " mcp.json "
12390
+ }),
12391
+ ") into",
12111
12392
  /* @__PURE__ */ jsx("span", {
12112
12393
  fg: COLOR.model,
12113
12394
  children: " .zidane/ "