@wrongstack/tui 0.273.1 → 0.274.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { writeErr, resolveProjectDir, wstackGlobalRoot, createHqPublisherFromEnv, GlobalMailbox, resolveWstackPaths, loadGoal, InputBuilder, DefaultSessionRewinder, writeOut, formatTodosList, buildGoalPreamble, expectDefined as expectDefined$1, projectSlug, shouldEnhance, enhanceUserPrompt, recentTextTurns, normalizedEqual, buildChildEnv } from '@wrongstack/core';
2
2
  export { buildGoalPreamble } from '@wrongstack/core';
3
- import { Box as Box$1, useInput, useStdin, useStdout, Text as Text$1, render, useApp, measureElement, Static } from 'ink';
3
+ import { Box as Box$1, useStdout, Static, measureElement, useInput, useStdin, Text as Text$1, render, useApp } from 'ink';
4
4
  import { randomUUID } from 'crypto';
5
5
  import * as path5 from 'path';
6
- import React5, { forwardRef, useState, useEffect, useMemo, memo, useRef, useCallback, useReducer, useLayoutEffect, isValidElement, Fragment as Fragment$1 } from 'react';
6
+ import React5, { forwardRef, useState, useEffect, useMemo, memo, useRef, useLayoutEffect, useCallback, useReducer, isValidElement, Fragment as Fragment$1 } from 'react';
7
7
  import * as fs3 from 'fs/promises';
8
8
  import { expectDefined, toErrorMessage, resolveWstackPaths as resolveWstackPaths$1 } from '@wrongstack/core/utils';
9
9
  import { routeImagesForModel } from '@wrongstack/runtime/vision';
@@ -119,6 +119,32 @@ function useTokenCounterRefresh(tokenCounter, events) {
119
119
  // src/thinking-word.ts
120
120
  var DEFAULT_TUI_THINKING_WORD = "thinking";
121
121
  var MAX_TUI_THINKING_WORD_LENGTH = 16;
122
+ var TUI_THINKING_WORD_POOL = [
123
+ "pondering",
124
+ "cogitating",
125
+ "ruminating",
126
+ "noodling",
127
+ "brewing",
128
+ "conjuring",
129
+ "percolating",
130
+ "scheming",
131
+ "tinkering",
132
+ "vibing",
133
+ "crafting",
134
+ "wrangling",
135
+ "summoning",
136
+ "finagling",
137
+ "marinating",
138
+ "hatching",
139
+ "juggling",
140
+ "spelunking",
141
+ "contemplating",
142
+ "bamboozling",
143
+ "alchemizing",
144
+ "incubating",
145
+ "doodling",
146
+ "mulling"
147
+ ];
122
148
  function normalizeTuiThinkingWord(value) {
123
149
  if (typeof value !== "string") return DEFAULT_TUI_THINKING_WORD;
124
150
  const word = value.trim();
@@ -128,6 +154,18 @@ function normalizeTuiThinkingWord(value) {
128
154
  if (!/^[\p{L}\p{N}_-]+$/u.test(word)) return DEFAULT_TUI_THINKING_WORD;
129
155
  return word;
130
156
  }
157
+ function isRandomTuiThinkingWord(value) {
158
+ if (typeof value !== "string") return true;
159
+ const word = value.trim().toLowerCase();
160
+ return word.length === 0 || word === DEFAULT_TUI_THINKING_WORD || word === "random";
161
+ }
162
+ function pickRandomTuiThinkingWord(previous) {
163
+ const pool = TUI_THINKING_WORD_POOL;
164
+ const candidates = previous ? pool.filter((w) => w !== previous) : pool;
165
+ const list = candidates.length > 0 ? candidates : pool;
166
+ const idx = Math.floor(Math.random() * list.length);
167
+ return list[idx] ?? list[0] ?? DEFAULT_TUI_THINKING_WORD;
168
+ }
131
169
  function chipExpired(meta, now = Date.now()) {
132
170
  if (meta.expiresIn == null) return false;
133
171
  return now >= meta.shownAt + meta.expiresIn * 6e4;
@@ -2477,10 +2515,84 @@ function helpSections() {
2477
2515
  { keys: "/fleet", desc: "multi-agent fleet controls" },
2478
2516
  { keys: "/goal", desc: "set an autonomous goal" },
2479
2517
  { keys: "/autonomy", desc: "autonomy mode (eternal / off)" },
2480
- { keys: "/settings", desc: "autonomy defaults (also Ctrl+S)" },
2518
+ { keys: "/settings", desc: "settings picker (also: /settings <chord> <value> \xB7 /settings reset <chord>)" },
2519
+ { keys: "/settings-get", desc: "show setting value(s) without opening picker" },
2481
2520
  { keys: "/clear", desc: "clear the conversation" }
2482
2521
  ]
2483
2522
  },
2523
+ {
2524
+ // Surface user-facing knobs that don't have a key chord or slash
2525
+ // command of their own — the only way to change them is through the
2526
+ // settings picker. Keeping the descriptions terse so the overlay
2527
+ // fits in narrow terminals.
2528
+ title: "Settings",
2529
+ entries: [
2530
+ {
2531
+ keys: "Multi-diff summary",
2532
+ desc: "min files before per-tool aggregate footer (Ctrl+M in picker, settings \u2192 tools, 0 = off, default 5)"
2533
+ },
2534
+ {
2535
+ keys: "Index on session start",
2536
+ desc: "run incremental index at startup (Ctrl+I in picker)"
2537
+ },
2538
+ {
2539
+ keys: "Thinking word",
2540
+ desc: "status-bar working word (Ctrl+W in picker; Enter to type your own)"
2541
+ },
2542
+ {
2543
+ keys: "Refine countdown / Refine",
2544
+ desc: "prompt refinement delay / on-off (Ctrl+R / Ctrl+E in picker)"
2545
+ },
2546
+ {
2547
+ keys: "Reasoning mode",
2548
+ desc: "auto / on / off (Ctrl+N in picker)"
2549
+ },
2550
+ {
2551
+ keys: "Max concurrent",
2552
+ desc: "parallel subagent cap (Ctrl+L in picker, settings \u2192 fleet)"
2553
+ },
2554
+ {
2555
+ keys: "Statusline",
2556
+ desc: "detailed / minimum density (Ctrl+D in picker)"
2557
+ },
2558
+ {
2559
+ keys: "Default autonomy mode",
2560
+ desc: "off / suggest / auto (Alt+A in picker, settings \u2192 autonomy)"
2561
+ },
2562
+ {
2563
+ keys: "YOLO mode",
2564
+ desc: "skip all confirmation prompts (Alt+Y in picker, settings \u2192 UX)"
2565
+ },
2566
+ {
2567
+ keys: "Token-saving mode",
2568
+ desc: "off / minimal / light / medium / aggressive (Alt+T in picker)"
2569
+ },
2570
+ {
2571
+ keys: "Context mode",
2572
+ desc: "balanced / frugal / deep / archival (Alt+X in picker, settings \u2192 context)"
2573
+ },
2574
+ {
2575
+ keys: "Confirm before exit",
2576
+ desc: "confirmation on Esc interrupt & Ctrl+C exit (Alt+S in picker, settings \u2192 UX)"
2577
+ },
2578
+ {
2579
+ keys: "Completion chime",
2580
+ desc: "play a sound when agent finishes (Alt+C in picker, settings \u2192 UX)"
2581
+ },
2582
+ {
2583
+ keys: "Log level / Audit level",
2584
+ desc: "console log verbosity / audit depth (Alt+Shift+L / Alt+Shift+A in picker, settings \u2192 logging)"
2585
+ },
2586
+ {
2587
+ keys: "Stream debug logging",
2588
+ desc: "hex-dump raw SSE bytes to stderr (Alt+Shift+B in picker, settings \u2192 debug)"
2589
+ },
2590
+ {
2591
+ keys: "Config scope",
2592
+ desc: "global / project (Alt+Shift+G in picker, settings \u2192 debug)"
2593
+ }
2594
+ ]
2595
+ },
2484
2596
  {
2485
2597
  title: "Tool Colors",
2486
2598
  entries: toolColorLegend()
@@ -5017,6 +5129,61 @@ function CodeBlock({
5017
5129
  }
5018
5130
  );
5019
5131
  }
5132
+ var MULTI_DIFF_SUMMARY_THRESHOLD = 5;
5133
+ function summarizeMultiFileDiffs(items) {
5134
+ let added = 0;
5135
+ let removed = 0;
5136
+ let hiddenAdded = 0;
5137
+ let hiddenRemoved = 0;
5138
+ let truncatedFiles = 0;
5139
+ for (const item of items) {
5140
+ added += item.preview.added;
5141
+ removed += item.preview.removed;
5142
+ hiddenAdded += item.preview.hiddenAdded;
5143
+ hiddenRemoved += item.preview.hiddenRemoved;
5144
+ if (item.preview.hidden > 0) truncatedFiles += 1;
5145
+ }
5146
+ return {
5147
+ fileCount: items.length,
5148
+ added,
5149
+ removed,
5150
+ hiddenAdded,
5151
+ hiddenRemoved,
5152
+ truncatedFiles
5153
+ };
5154
+ }
5155
+ function formatMultiDiffSummary(summary, threshold = MULTI_DIFF_SUMMARY_THRESHOLD) {
5156
+ if (threshold === 0) return null;
5157
+ const effectiveThreshold = threshold < 0 ? MULTI_DIFF_SUMMARY_THRESHOLD : threshold;
5158
+ if (summary.fileCount < effectiveThreshold) return null;
5159
+ const parts = [`${summary.fileCount} files`];
5160
+ if (summary.added > 0) parts.push(`+${summary.added}`);
5161
+ if (summary.removed > 0) parts.push(`-${summary.removed}`);
5162
+ if (summary.hiddenAdded > 0 || summary.hiddenRemoved > 0) {
5163
+ const hiddenParts = [];
5164
+ if (summary.hiddenAdded > 0) hiddenParts.push(`+${summary.hiddenAdded}`);
5165
+ if (summary.hiddenRemoved > 0) hiddenParts.push(`-${summary.hiddenRemoved}`);
5166
+ parts.push(`\u2026 ${hiddenParts.join(" ")} hidden across ${summary.truncatedFiles} file${summary.truncatedFiles === 1 ? "" : "s"}`);
5167
+ }
5168
+ return parts.join(" \xB7 ");
5169
+ }
5170
+ function DiffFileBlock({
5171
+ path: path7,
5172
+ preview
5173
+ }) {
5174
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
5175
+ /* @__PURE__ */ jsx(Text, { dimColor: true, italic: true, children: path7 }),
5176
+ /* @__PURE__ */ jsx(
5177
+ DiffBlock,
5178
+ {
5179
+ rows: preview.rows,
5180
+ hidden: preview.hidden,
5181
+ hiddenAdded: preview.hiddenAdded,
5182
+ hiddenRemoved: preview.hiddenRemoved
5183
+ }
5184
+ )
5185
+ ] });
5186
+ }
5020
5187
  function DiffBlock({
5021
5188
  rows,
5022
5189
  hidden,
@@ -5154,20 +5321,167 @@ function extractDiffPreview(toolName, output, input) {
5154
5321
  } else if (toolName === "replace") {
5155
5322
  const parsed = tryParseJson(text);
5156
5323
  if (parsed && typeof parsed === "object") {
5157
- diff = collectReplaceDiffs(parsed);
5324
+ diff = joinReplaceDiffs(parsed);
5158
5325
  }
5159
5326
  }
5160
5327
  if (!diff?.trim() || diff.startsWith("(no-op")) return void 0;
5161
5328
  const preview = parseUnifiedDiff(diff, DIFF_MAX_LINES);
5162
5329
  return preview.rows.length > 0 ? preview : void 0;
5163
5330
  }
5164
- function collectReplaceDiffs(obj) {
5165
- const results = Array.isArray(obj["results"]) ? obj["results"] : [];
5166
- const diffs = results.map(
5167
- (result) => result && typeof result === "object" ? stringOf(result["diff"]) : void 0
5168
- ).filter((diff) => Boolean(diff?.trim()));
5331
+ function joinReplaceDiffs(obj) {
5332
+ const items = splitReplaceDiffs(obj);
5333
+ const diffs = items.map((item) => item.diff);
5169
5334
  return diffs.length > 0 ? diffs.join("\n") : void 0;
5170
5335
  }
5336
+ function splitReplaceDiffs(obj) {
5337
+ const results = Array.isArray(obj["results"]) ? obj["results"] : [];
5338
+ const items = [];
5339
+ for (const result of results) {
5340
+ if (!result || typeof result !== "object") continue;
5341
+ const record = result;
5342
+ const diff = stringOf(record["diff"]);
5343
+ if (!diff?.trim()) continue;
5344
+ const path7 = stringOf(record["path"]);
5345
+ items.push(path7 ? { path: path7, diff } : { diff });
5346
+ }
5347
+ return items;
5348
+ }
5349
+ function extractMultiFileDiffs(toolName, output, input) {
5350
+ if (!output) return void 0;
5351
+ const items = collectMultiFileDiffItems(toolName, output, input);
5352
+ if (items === void 0) return void 0;
5353
+ if (items.length === 0) return void 0;
5354
+ const previews = [];
5355
+ for (const item of items) {
5356
+ const preview = parseUnifiedDiff(item.diff, DIFF_MAX_LINES);
5357
+ if (preview.rows.length === 0) continue;
5358
+ previews.push({ path: item.path ?? "unknown file", preview });
5359
+ }
5360
+ return previews.length > 0 ? previews : void 0;
5361
+ }
5362
+ function collectMultiFileDiffItems(toolName, output, _input) {
5363
+ const trimmed = output.trim();
5364
+ if (!trimmed) return void 0;
5365
+ if (toolName === "replace") {
5366
+ const parsed = tryParseJson(trimmed);
5367
+ if (!parsed || typeof parsed !== "object") return void 0;
5368
+ const items = splitReplaceDiffs(parsed);
5369
+ if (items.length === 0) return items;
5370
+ const allMissing = items.every((item) => !item.path);
5371
+ const fallback = allMissing && _input && typeof _input === "object" && typeof _input["path"] === "string" ? stringOf(_input["path"]) : void 0;
5372
+ if (fallback) {
5373
+ return items.map((item) => ({ path: item.path ?? fallback, diff: item.diff }));
5374
+ }
5375
+ return items;
5376
+ }
5377
+ if (toolName === "diff") {
5378
+ const parsed = tryParseJson(trimmed);
5379
+ if (parsed && typeof parsed === "object") {
5380
+ const obj = parsed;
5381
+ const files = Array.isArray(obj["files"]) ? obj["files"].map(stringOf).filter((p) => Boolean(p)) : [];
5382
+ const diff = stringOf(obj["diff"]) ?? stringOf(obj["stdout"]);
5383
+ if (!diff?.trim()) return void 0;
5384
+ const blocks = splitGitStyleDiff(diff);
5385
+ return blocks.map((block, idx) => ({
5386
+ path: files[idx] ?? block.path,
5387
+ diff: block.diff
5388
+ }));
5389
+ }
5390
+ if (trimmed.includes("@@")) return [{ diff: trimmed }];
5391
+ return void 0;
5392
+ }
5393
+ if (toolName === "patch") {
5394
+ const parsed = tryParseJson(trimmed);
5395
+ let diffText;
5396
+ let explicitFiles = [];
5397
+ if (parsed && typeof parsed === "object") {
5398
+ const obj = parsed;
5399
+ diffText = stringOf(obj["diff"]) ?? stringOf(obj["stdout"]);
5400
+ if (Array.isArray(obj["files"])) {
5401
+ explicitFiles = obj["files"].map(stringOf).filter((p) => Boolean(p));
5402
+ }
5403
+ } else if (trimmed.includes("@@") || trimmed.startsWith("---")) {
5404
+ diffText = trimmed;
5405
+ }
5406
+ if (!diffText?.trim()) return void 0;
5407
+ const blocks = splitGitStyleDiff(diffText);
5408
+ if (blocks.length === 0) {
5409
+ const path7 = explicitFiles[0] ?? extractPatchHeaderPath(diffText);
5410
+ return [{ path: path7, diff: diffText }];
5411
+ }
5412
+ return blocks.map((block, idx) => ({
5413
+ path: explicitFiles[idx] ?? block.path,
5414
+ diff: block.diff
5415
+ }));
5416
+ }
5417
+ return void 0;
5418
+ }
5419
+ function splitGitStyleDiff(diff) {
5420
+ const lines = diff.split("\n");
5421
+ const blocks = [];
5422
+ let current = null;
5423
+ const flush = () => {
5424
+ if (!current) return;
5425
+ const text = current.body.join("\n").trim();
5426
+ if (text) blocks.push({ path: current.path, diff: text });
5427
+ current = null;
5428
+ };
5429
+ for (const line of lines) {
5430
+ if (line.startsWith("diff --git ")) {
5431
+ flush();
5432
+ current = { path: parseDiffGitPath(line), body: [line], hasGitHeader: true };
5433
+ continue;
5434
+ }
5435
+ if (current) {
5436
+ current.body.push(line);
5437
+ if (!current.hasGitHeader && line.startsWith("--- a/") && current.body.length > 2) {
5438
+ const carriedBody = current.body.slice(0, -1);
5439
+ const carriedPath = current.path;
5440
+ const text = carriedBody.join("\n").trim();
5441
+ if (text) blocks.push({ path: carriedPath, diff: text });
5442
+ current = {
5443
+ path: line.slice("--- a/".length).trim(),
5444
+ body: [line],
5445
+ hasGitHeader: false
5446
+ };
5447
+ }
5448
+ } else if (line.startsWith("--- a/")) {
5449
+ current = { path: line.slice("--- a/".length).trim(), body: [line], hasGitHeader: false };
5450
+ } else if (line.startsWith("+++ b/")) {
5451
+ current = { body: [line], hasGitHeader: false };
5452
+ }
5453
+ }
5454
+ flush();
5455
+ if (blocks.length === 1 && !blocks[0].path) {
5456
+ const path7 = extractPatchHeaderPath(blocks[0].diff);
5457
+ if (path7) blocks[0] = { path: path7, diff: blocks[0].diff };
5458
+ }
5459
+ return blocks;
5460
+ }
5461
+ function parseDiffGitPath(line) {
5462
+ const rest = line.slice("diff --git ".length).trim();
5463
+ if (!rest) return void 0;
5464
+ const sep2 = rest.lastIndexOf(" b/");
5465
+ if (sep2 > 0 && sep2 > rest.length - sep2 - 3) {
5466
+ return rest.slice(sep2 + 3);
5467
+ }
5468
+ const spaceIdx = rest.lastIndexOf(" ");
5469
+ if (spaceIdx > 0) {
5470
+ const rhs = rest.slice(spaceIdx + 1);
5471
+ return rhs.startsWith("b/") ? rhs.slice(2) : rhs;
5472
+ }
5473
+ return void 0;
5474
+ }
5475
+ function extractPatchHeaderPath(diffText) {
5476
+ for (const line of diffText.split("\n")) {
5477
+ if (line.startsWith("+++ ")) {
5478
+ const path7 = line.slice(4).trim();
5479
+ const cleaned = path7.replace(/^b\//, "").split(" ")[0].trim();
5480
+ return cleaned || void 0;
5481
+ }
5482
+ }
5483
+ return void 0;
5484
+ }
5171
5485
  function newFileDiffFromWriteInput(output, input) {
5172
5486
  if (!input || typeof input !== "object") return void 0;
5173
5487
  const obj = input;
@@ -5466,7 +5780,7 @@ var Entry = React5.memo(function Entry2({
5466
5780
  termWidth,
5467
5781
  setSuggestions,
5468
5782
  autonomyMode,
5469
- autoSubmitCountdown
5783
+ multiDiffSummaryThreshold
5470
5784
  }) {
5471
5785
  const nextSteps = useMemo(() => {
5472
5786
  if (entry.kind !== "assistant") return { steps: [], stripped: "" };
@@ -5549,7 +5863,7 @@ var Entry = React5.memo(function Entry2({
5549
5863
  /* @__PURE__ */ jsx(Text, { bold: true, color: theme.accent, children: ` ${s2.index}. ` }),
5550
5864
  /* @__PURE__ */ jsx(Text, { children: s2.text }),
5551
5865
  s2.auto ? /* @__PURE__ */ jsx(Text, { color: "cyan", dimColor: true, children: " auto" }) : null,
5552
- autonomyMode === "auto" && i === 0 ? autoSubmitCountdown != null && autoSubmitCountdown > 0 ? /* @__PURE__ */ jsx(Text, { color: autoSubmitCountdown > 20 ? "green" : autoSubmitCountdown > 10 ? "yellow" : "red", children: ` auto in ${autoSubmitCountdown}s` }) : /* @__PURE__ */ jsx(Text, { color: "cyan", children: " \u23E9" }) : null
5866
+ autonomyMode === "auto" && i === 0 ? /* @__PURE__ */ jsx(Text, { color: "cyan", children: " \u23E9" }) : null
5553
5867
  ] }) }, s2.index))
5554
5868
  ]
5555
5869
  }
@@ -5558,30 +5872,49 @@ var Entry = React5.memo(function Entry2({
5558
5872
  }
5559
5873
  case "tool": {
5560
5874
  const { glyph, color } = getToolVisual(entry.name);
5561
- const argSummary = formatToolArgs(entry.name, entry.input);
5562
- const outLines = formatToolOutput(
5875
+ const {
5876
+ argSummary,
5877
+ outLines,
5878
+ visualLines,
5879
+ diff,
5880
+ multiDiffs,
5881
+ sizeChip
5882
+ } = useMemo(() => {
5883
+ const argSummary2 = formatToolArgs(entry.name, entry.input);
5884
+ const outLines2 = formatToolOutput(
5885
+ entry.name,
5886
+ entry.output,
5887
+ entry.ok,
5888
+ entry.outputBytes,
5889
+ entry.outputLines
5890
+ );
5891
+ const visualLines2 = formatToolVisualOutput(entry.name, entry.output, entry.ok, entry.input);
5892
+ const diff2 = entry.ok ? extractDiffPreview(entry.name, entry.output, entry.input) : void 0;
5893
+ const multiDiffs2 = entry.ok && !diff2 && (entry.name === "replace" || entry.name === "diff" || entry.name === "patch") ? extractMultiFileDiffs(entry.name, entry.output, entry.input) : void 0;
5894
+ const sizeChip2 = (() => {
5895
+ if (!entry.ok) return "";
5896
+ const parts = [];
5897
+ if (entry.outputLines !== void 0 && entry.outputLines > 0) {
5898
+ parts.push(`${entry.outputLines} L`);
5899
+ }
5900
+ if (entry.outputBytes && entry.outputBytes > 0) {
5901
+ parts.push(fmtBytes(entry.outputBytes));
5902
+ }
5903
+ if (entry.outputTokens && entry.outputTokens > 0) {
5904
+ parts.push(`\u2248${fmtTok2(entry.outputTokens)} tok`);
5905
+ }
5906
+ return parts.join(" \xB7 ");
5907
+ })();
5908
+ return { argSummary: argSummary2, outLines: outLines2, visualLines: visualLines2, diff: diff2, multiDiffs: multiDiffs2, sizeChip: sizeChip2 };
5909
+ }, [
5563
5910
  entry.name,
5564
5911
  entry.output,
5912
+ entry.input,
5565
5913
  entry.ok,
5566
5914
  entry.outputBytes,
5567
- entry.outputLines
5568
- );
5569
- const visualLines = formatToolVisualOutput(entry.name, entry.output, entry.ok, entry.input);
5570
- const diff = entry.ok ? extractDiffPreview(entry.name, entry.output, entry.input) : void 0;
5571
- const sizeChip = (() => {
5572
- if (!entry.ok) return "";
5573
- const parts = [];
5574
- if (entry.outputLines !== void 0 && entry.outputLines > 0) {
5575
- parts.push(`${entry.outputLines} L`);
5576
- }
5577
- if (entry.outputBytes && entry.outputBytes > 0) {
5578
- parts.push(fmtBytes(entry.outputBytes));
5579
- }
5580
- if (entry.outputTokens && entry.outputTokens > 0) {
5581
- parts.push(`\u2248${fmtTok2(entry.outputTokens)} tok`);
5582
- }
5583
- return parts.join(" \xB7 ");
5584
- })();
5915
+ entry.outputLines,
5916
+ entry.outputTokens
5917
+ ]);
5585
5918
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
5586
5919
  /* @__PURE__ */ jsxs(Text, { children: [
5587
5920
  /* @__PURE__ */ jsx(Text, { color, children: glyph }),
@@ -5594,8 +5927,8 @@ var Entry = React5.memo(function Entry2({
5594
5927
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \xB7 ${fmtDuration(entry.durationMs)}` }),
5595
5928
  sizeChip ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \xB7 ${sizeChip}` }) : null
5596
5929
  ] }),
5597
- visualLines ? /* @__PURE__ */ jsx(ToolOutputLines, { lines: visualLines, hasFollowingBlock: Boolean(diff) }) : outLines.map((line, i) => /* @__PURE__ */ jsxs(Text, { children: [
5598
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: i === outLines.length - 1 && !diff ? " \u2514\u2500 " : " \u251C\u2500 " }),
5930
+ visualLines ? /* @__PURE__ */ jsx(ToolOutputLines, { lines: visualLines, hasFollowingBlock: Boolean(diff || multiDiffs) }) : outLines.map((line, i) => /* @__PURE__ */ jsxs(Text, { children: [
5931
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: i === outLines.length - 1 && !diff && !multiDiffs ? " \u2514\u2500 " : " \u251C\u2500 " }),
5599
5932
  /* @__PURE__ */ jsx(
5600
5933
  Text,
5601
5934
  {
@@ -5605,7 +5938,16 @@ var Entry = React5.memo(function Entry2({
5605
5938
  }
5606
5939
  )
5607
5940
  ] }, i)),
5608
- diff ? /* @__PURE__ */ jsx(
5941
+ multiDiffs ? /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
5942
+ (() => {
5943
+ const summaryLine = formatMultiDiffSummary(
5944
+ summarizeMultiFileDiffs(multiDiffs),
5945
+ multiDiffSummaryThreshold ?? -1
5946
+ );
5947
+ return summaryLine ? /* @__PURE__ */ jsx(Text, { dimColor: true, italic: true, children: summaryLine }) : null;
5948
+ })(),
5949
+ multiDiffs.map((item) => /* @__PURE__ */ jsx(DiffFileBlock, { path: item.path, preview: item.preview }, item.path))
5950
+ ] }) : diff ? /* @__PURE__ */ jsx(
5609
5951
  DiffBlock,
5610
5952
  {
5611
5953
  rows: diff.rows,
@@ -5725,7 +6067,7 @@ var Entry = React5.memo(function Entry2({
5725
6067
  }
5726
6068
  }
5727
6069
  });
5728
- function History({ entries, generation, streamingText, toolStream, setSuggestions, autonomyMode, autoSubmitCountdown }) {
6070
+ var History = memo(function History2({ entries, generation, streamingText, toolStream, setSuggestions, autonomyMode, multiDiffSummaryThreshold }) {
5729
6071
  const { stdout } = useStdout();
5730
6072
  const [termSize, setTermSize] = useState({
5731
6073
  columns: stdout?.columns ?? 80,
@@ -5743,10 +6085,10 @@ function History({ entries, generation, streamingText, toolStream, setSuggestion
5743
6085
  const termWidth = termSize.columns;
5744
6086
  const tail = streamingText ? tailForDisplay(streamingText, MAX_STREAM_DISPLAY_CHARS) : "";
5745
6087
  return /* @__PURE__ */ jsxs(Fragment, { children: [
5746
- /* @__PURE__ */ jsx(Static, { items: entries, children: (entry) => /* @__PURE__ */ jsx(Box, { marginBottom: entry.kind === "turn-summary" ? 1 : 0, children: /* @__PURE__ */ jsx(Entry, { entry, termWidth, setSuggestions, autonomyMode, autoSubmitCountdown }) }, entry.id) }, generation ?? 0),
6088
+ /* @__PURE__ */ jsx(Static, { items: entries, children: (entry) => /* @__PURE__ */ jsx(Box, { marginBottom: entry.kind === "turn-summary" ? 1 : 0, children: /* @__PURE__ */ jsx(Entry, { entry, termWidth, setSuggestions, autonomyMode, multiDiffSummaryThreshold }) }, entry.id) }, generation ?? 0),
5747
6089
  /* @__PURE__ */ jsx(Box, { flexGrow: 1, children: tail ? /* @__PURE__ */ jsx(AssistantTail, { text: tail, termWidth }) : null })
5748
6090
  ] });
5749
- }
6091
+ });
5750
6092
  var MAX_MOUNTED = 500;
5751
6093
  function scrollbarThumb(rows, offset, total) {
5752
6094
  const scrollable = total > rows;
@@ -5784,7 +6126,7 @@ function Scrollbar({
5784
6126
  i
5785
6127
  )) });
5786
6128
  }
5787
- function ScrollableHistory({
6129
+ var ScrollableHistory = memo(function ScrollableHistory2({
5788
6130
  entries,
5789
6131
  streamingText,
5790
6132
  toolStream,
@@ -5794,8 +6136,7 @@ function ScrollableHistory({
5794
6136
  onMeasure,
5795
6137
  maxWidth,
5796
6138
  setSuggestions,
5797
- autonomyMode,
5798
- autoSubmitCountdown
6139
+ autonomyMode
5799
6140
  }) {
5800
6141
  const { stdout } = useStdout();
5801
6142
  const rawWidth = stdout?.columns ?? 80;
@@ -5834,7 +6175,7 @@ function ScrollableHistory({
5834
6175
  flexShrink: 0,
5835
6176
  children: [
5836
6177
  hiddenCount > 0 ? /* @__PURE__ */ jsx(Box, { flexShrink: 0, children: /* @__PURE__ */ jsx(Text, { dimColor: true, italic: true, children: ` \u2191 ${hiddenCount} earlier ${hiddenCount === 1 ? "entry" : "entries"} (scroll lives in this session; full log on disk)` }) }) : null,
5837
- shown.map((entry) => /* @__PURE__ */ jsx(Box, { marginBottom: entry.kind === "turn-summary" ? 1 : 0, flexShrink: 0, children: /* @__PURE__ */ jsx(Entry, { entry, termWidth, setSuggestions, autonomyMode, autoSubmitCountdown }) }, entry.id)),
6178
+ shown.map((entry) => /* @__PURE__ */ jsx(Box, { marginBottom: entry.kind === "turn-summary" ? 1 : 0, flexShrink: 0, children: /* @__PURE__ */ jsx(Entry, { entry, termWidth, setSuggestions, autonomyMode }) }, entry.id)),
5838
6179
  tail ? /* @__PURE__ */ jsx(AssistantTail, { text: tail, termWidth }) : null,
5839
6180
  toolTail && toolStream ? /* @__PURE__ */ jsx(
5840
6181
  ToolStreamBox,
@@ -5852,7 +6193,7 @@ function ScrollableHistory({
5852
6193
  ),
5853
6194
  /* @__PURE__ */ jsx(Scrollbar, { rows: vp, offset: Math.max(0, scrollOffset), total: totalLines })
5854
6195
  ] });
5855
- }
6196
+ });
5856
6197
  var MB = 1024 * 1024;
5857
6198
  function defaultHeapLogPath() {
5858
6199
  return path5.join(wstackGlobalRoot(), "logs", "heap.jsonl");
@@ -7606,6 +7947,7 @@ var MAX_ITERATIONS_PRESETS = [100, 200, 500, 1e3, 0];
7606
7947
  var MAX_CONCURRENT_PRESETS = [1, 3, 4, 5, 10, 25, 50, 0];
7607
7948
  var AUTO_PROCEED_MAX_PRESETS = [10, 25, 50, 100, 250, 0];
7608
7949
  var ENHANCE_DELAY_PRESETS = [15e3, 3e4, 45e3, 6e4, 9e4, 12e4];
7950
+ var MULTI_DIFF_SUMMARY_THRESHOLD_PRESETS = [3, 5, 8, 10, 15, 0];
7609
7951
  var ENHANCE_LANGUAGES = ["original", "english"];
7610
7952
  var TOKEN_SAVING_TIERS = ["off", "minimal", "light", "medium", "aggressive"];
7611
7953
  var TOKEN_SAVING_TIER_DESCS = {
@@ -7624,6 +7966,10 @@ function formatMaxIterations(n) {
7624
7966
  if (n === 0) return "unlimited";
7625
7967
  return String(n);
7626
7968
  }
7969
+ function formatMultiDiffSummaryThreshold(n) {
7970
+ if (n === 0) return "off";
7971
+ return String(n);
7972
+ }
7627
7973
  function formatEnhanceDelay(ms) {
7628
7974
  return `${Math.round(ms / 1e3)}s`;
7629
7975
  }
@@ -7632,10 +7978,473 @@ var MODE_DESC = {
7632
7978
  suggest: "Shows next-step suggestions after each turn",
7633
7979
  auto: "Self-driving \u2014 agent continues automatically"
7634
7980
  };
7635
- var SETTINGS_FIELD_COUNT = 35;
7981
+ var SETTINGS_FIELD_COUNT = 36;
7982
+ var THINKING_WORD_FIELD = 22;
7983
+ var SETTINGS_PICKER_JUMP_CHORDS = Object.freeze([
7984
+ // ── Ctrl chords (Tools / Reasoning / Fleet / Debug sections) ──
7985
+ // Most-tweaked rows first — these are the knobs users reach for daily.
7986
+ { mod: "ctrl", letter: "i", field: 20, label: "Index on session start" },
7987
+ { mod: "ctrl", letter: "m", field: 21, label: "Multi-diff summary" },
7988
+ { mod: "ctrl", letter: "w", field: 22, label: "Thinking word" },
7989
+ { mod: "ctrl", letter: "r", field: 17, label: "Refine preview countdown" },
7990
+ { mod: "ctrl", letter: "e", field: 18, label: "Refine" },
7991
+ { mod: "ctrl", letter: "n", field: 23, label: "Reasoning mode" },
7992
+ { mod: "ctrl", letter: "l", field: 30, label: "Max concurrent" },
7993
+ { mod: "ctrl", letter: "d", field: 34, label: "Statusline" },
7994
+ // ── Alt chords (Autonomy / UX / Features / Context sections) ──
7995
+ // The Ctrl set above is dominated by Tools + Reasoning rows; Alt picks up
7996
+ // the spread-out sections to give every region of the picker a fast path.
7997
+ { mod: "alt", letter: "a", field: 0, label: "Default autonomy mode" },
7998
+ { mod: "alt", letter: "y", field: 3, label: "YOLO mode" },
7999
+ { mod: "alt", letter: "c", field: 5, label: "Completion chime" },
8000
+ { mod: "alt", letter: "s", field: 6, label: "Confirm before exit" },
8001
+ { mod: "alt", letter: "t", field: 13, label: "Token-saving mode" },
8002
+ { mod: "alt", letter: "x", field: 29, label: "Context mode" },
8003
+ // ── Alt+Shift chords (Logging / Debug sections) ──
8004
+ // 'L' is taken by Ctrl+L (Max concurrent), so the Logging rows get the
8005
+ // composed Alt+Shift version. A standalone Alt+L would shadow the same
8006
+ // letter as Max concurrent's Ctrl chord, so the composition is the
8007
+ // disambiguator. 'A' and 'B' are both currently free as Alt+Shift
8008
+ // letters (B is unmapped at any mod), so the Debug row's "Stream debug
8009
+ // logging" takes B (de**B**ug, raw-**B**yte stream) rather than churn
8010
+ // the existing Ctrl+D → Statusline binding to make room. The Config
8011
+ // scope row uses G (**G**lobal / project) — G is free as Alt+Shift even
8012
+ // though Ctrl+G is the agents-monitor chord, because the mod+letter
8013
+ // composition makes them distinct at the keyboard-handler level.
8014
+ { mod: "alt-shift", letter: "l", field: 31, label: "Log level" },
8015
+ { mod: "alt-shift", letter: "a", field: 32, label: "Audit level" },
8016
+ { mod: "alt-shift", letter: "b", field: 33, label: "Stream debug logging" },
8017
+ { mod: "alt-shift", letter: "g", field: 35, label: "Config scope" }
8018
+ ]);
8019
+ function settingsPickerJumpField(mod, letter) {
8020
+ const lower = letter.toLowerCase();
8021
+ const chord = SETTINGS_PICKER_JUMP_CHORDS.find((c) => c.mod === mod && c.letter === lower);
8022
+ return chord?.field;
8023
+ }
8024
+ function settingsPickerSlug(label) {
8025
+ return label.toLowerCase().replace(/\s+/g, "-");
8026
+ }
8027
+ function settingsPickerJumpByName(name) {
8028
+ const query = settingsPickerSlug(name);
8029
+ if (!query) return void 0;
8030
+ const exact = SETTINGS_PICKER_JUMP_CHORDS.find(
8031
+ (c) => settingsPickerSlug(c.label) === query
8032
+ );
8033
+ if (exact) return exact.field;
8034
+ const queryTokens = query.split("-");
8035
+ for (const c of SETTINGS_PICKER_JUMP_CHORDS) {
8036
+ const labelTokens = settingsPickerSlug(c.label).split("-");
8037
+ if (queryTokens.every((t, i) => labelTokens[i] === t)) {
8038
+ return c.field;
8039
+ }
8040
+ }
8041
+ for (const c of SETTINGS_PICKER_JUMP_CHORDS) {
8042
+ const labelTokens = settingsPickerSlug(c.label).split("-");
8043
+ if (labelTokens.some((t) => t === query || t.startsWith(`${query}-`))) {
8044
+ return c.field;
8045
+ }
8046
+ }
8047
+ return void 0;
8048
+ }
8049
+ function settingsPickerJumpNames() {
8050
+ return SETTINGS_PICKER_JUMP_CHORDS.map((c) => settingsPickerSlug(c.label));
8051
+ }
8052
+ var THINKING_WORD_PRESETS = [
8053
+ "thinking",
8054
+ "random",
8055
+ "working",
8056
+ "cooking",
8057
+ "vibing",
8058
+ "pondering",
8059
+ "brewing",
8060
+ "crunching",
8061
+ "computing",
8062
+ "grinding",
8063
+ "noodling",
8064
+ "churning",
8065
+ "hacking"
8066
+ ];
7636
8067
  var CONFIG_SCOPES = ["global", "project"];
8068
+ var SETTINGS_FIELD_LABELS = [
8069
+ "Default autonomy mode",
8070
+ // 0
8071
+ "Auto-proceed delay",
8072
+ // 1
8073
+ "Terminal title animation",
8074
+ // 2
8075
+ "YOLO mode",
8076
+ // 3
8077
+ "Stream fleet",
8078
+ // 4
8079
+ "Completion chime",
8080
+ // 5
8081
+ "Confirm before exit",
8082
+ // 6
8083
+ "Next prediction",
8084
+ // 7
8085
+ "MCP features",
8086
+ // 8
8087
+ "Plugin features",
8088
+ // 9
8089
+ "Memory features",
8090
+ // 10
8091
+ "Skills features",
8092
+ // 11
8093
+ "Models registry",
8094
+ // 12
8095
+ "Token-saving mode",
8096
+ // 13
8097
+ "Allow outside project root",
8098
+ // 14
8099
+ "Max iterations",
8100
+ // 15
8101
+ "Auto-proceed max iterations",
8102
+ // 16
8103
+ "Refine preview countdown",
8104
+ // 17
8105
+ "Refine",
8106
+ // 18
8107
+ "Refine language",
8108
+ // 19
8109
+ "Index on session start",
8110
+ // 20
8111
+ "Multi-diff summary",
8112
+ // 21
8113
+ "Thinking word",
8114
+ // 22
8115
+ "Reasoning mode",
8116
+ // 23
8117
+ "Reasoning effort",
8118
+ // 24
8119
+ "Reasoning preserve",
8120
+ // 25
8121
+ "Cache TTL",
8122
+ // 26
8123
+ "Context auto-compact",
8124
+ // 27
8125
+ "Compactor strategy",
8126
+ // 28
8127
+ "Context mode",
8128
+ // 29
8129
+ "Max concurrent",
8130
+ // 30
8131
+ "Log level",
8132
+ // 31
8133
+ "Audit level",
8134
+ // 32
8135
+ "Stream debug logging",
8136
+ // 33
8137
+ "Statusline",
8138
+ // 34
8139
+ "Config scope"
8140
+ // 35
8141
+ ];
8142
+ function resolveSettingsFieldValue(field, input) {
8143
+ const raw = input.trim().toLowerCase();
8144
+ const label = SETTINGS_FIELD_LABELS[field] ?? `Field ${field}`;
8145
+ const BOOL_FIELDS = /* @__PURE__ */ new Map([
8146
+ [2, "titleAnimation"],
8147
+ [3, "yolo"],
8148
+ [4, "streamFleet"],
8149
+ [5, "chime"],
8150
+ [6, "confirmExit"],
8151
+ [7, "nextPrediction"],
8152
+ [8, "featureMcp"],
8153
+ [9, "featurePlugins"],
8154
+ [10, "featureMemory"],
8155
+ [11, "featureSkills"],
8156
+ [12, "featureModelsRegistry"],
8157
+ [14, "allowOutsideProjectRoot"],
8158
+ [18, "enhanceEnabled"],
8159
+ [20, "indexOnStart"],
8160
+ [25, "reasoningPreserve"],
8161
+ [27, "contextAutoCompact"],
8162
+ [33, "debugStream"]
8163
+ ]);
8164
+ const boolKey = BOOL_FIELDS.get(field);
8165
+ if (boolKey) {
8166
+ if (["on", "true", "yes", "1"].includes(raw)) {
8167
+ return { ok: true, patch: { [boolKey]: true }, label, displayValue: "on" };
8168
+ }
8169
+ if (["off", "false", "no", "0"].includes(raw)) {
8170
+ return { ok: true, patch: { [boolKey]: false }, label, displayValue: "off" };
8171
+ }
8172
+ return { ok: false, error: `Invalid value "${input}" for ${label}. Use on or off.` };
8173
+ }
8174
+ const ENUM_FIELDS = [
8175
+ [0, "mode", SETTINGS_MODES],
8176
+ [13, "tokenSavingTier", TOKEN_SAVING_TIERS],
8177
+ [19, "enhanceLanguage", ENHANCE_LANGUAGES],
8178
+ [23, "reasoningMode", REASONING_MODES],
8179
+ [24, "reasoningEffort", REASONING_EFFORTS],
8180
+ [26, "cacheTtl", CACHE_TTLS],
8181
+ [28, "contextStrategy", COMPACTOR_STRATEGIES],
8182
+ [29, "contextMode", CONTEXT_MODES],
8183
+ [31, "logLevel", LOG_LEVELS],
8184
+ [32, "auditLevel", AUDIT_LEVELS],
8185
+ [34, "statuslineMode", STATUSLINE_MODES],
8186
+ [35, "configScope", CONFIG_SCOPES]
8187
+ ];
8188
+ for (const [f, key, values] of ENUM_FIELDS) {
8189
+ if (field !== f) continue;
8190
+ const match = values.find((v) => v.toLowerCase() === raw);
8191
+ if (match) {
8192
+ return { ok: true, patch: { [key]: match }, label, displayValue: match };
8193
+ }
8194
+ return {
8195
+ ok: false,
8196
+ error: `Invalid value "${input}" for ${label}. Valid: ${values.join(", ")}.`
8197
+ };
8198
+ }
8199
+ const presetLabel = (n, zeroLabel) => n === 0 ? zeroLabel : String(n);
8200
+ const PRESET_FIELDS = [
8201
+ [1, "delayMs", DELAY_PRESETS_MS, (n) => formatSettingsDelay(n)],
8202
+ [15, "maxIterations", MAX_ITERATIONS_PRESETS, (n) => formatMaxIterations(n)],
8203
+ [16, "autoProceedMaxIterations", AUTO_PROCEED_MAX_PRESETS, (n) => formatMaxIterations(n)],
8204
+ [17, "enhanceDelayMs", ENHANCE_DELAY_PRESETS, (n) => formatEnhanceDelay(n)],
8205
+ [21, "multiDiffSummaryThreshold", MULTI_DIFF_SUMMARY_THRESHOLD_PRESETS, (n) => formatMultiDiffSummaryThreshold(n)],
8206
+ [30, "maxConcurrent", MAX_CONCURRENT_PRESETS, (n) => presetLabel(n, "runtime default")]
8207
+ ];
8208
+ for (const [f, key, presets, fmt2] of PRESET_FIELDS) {
8209
+ if (field !== f) continue;
8210
+ const asNum = Number.parseInt(raw, 10);
8211
+ if (!Number.isNaN(asNum) && presets.includes(asNum)) {
8212
+ return { ok: true, patch: { [key]: asNum }, label, displayValue: fmt2(asNum) };
8213
+ }
8214
+ const byName = presets.find((p) => fmt2(p).toLowerCase() === raw);
8215
+ if (byName !== void 0) {
8216
+ return { ok: true, patch: { [key]: byName }, label, displayValue: fmt2(byName) };
8217
+ }
8218
+ const options = presets.map((p) => fmt2(p)).join(", ");
8219
+ return {
8220
+ ok: false,
8221
+ error: `Invalid value "${input}" for ${label}. Available: ${options}.`
8222
+ };
8223
+ }
8224
+ if (field === 22) {
8225
+ const word = input.trim();
8226
+ if (word.length === 0 || word.length > MAX_TUI_THINKING_WORD_LENGTH) {
8227
+ return {
8228
+ ok: false,
8229
+ error: `"${input}" is not a valid thinking word. Use a single short word (1\u2013${MAX_TUI_THINKING_WORD_LENGTH} chars, letters/numbers only).`
8230
+ };
8231
+ }
8232
+ if (!/^[\p{L}\p{N}_-]+$/u.test(word)) {
8233
+ return {
8234
+ ok: false,
8235
+ error: `"${input}" is not a valid thinking word. Use a single short word (1\u2013${MAX_TUI_THINKING_WORD_LENGTH} chars, letters/numbers only).`
8236
+ };
8237
+ }
8238
+ return { ok: true, patch: { thinkingWord: word }, label, displayValue: word };
8239
+ }
8240
+ return { ok: false, error: `Unknown settings field ${field}.` };
8241
+ }
8242
+ function getSettingsFieldValue(values, field) {
8243
+ const label = SETTINGS_FIELD_LABELS[field] ?? `Field ${field}`;
8244
+ const BOOL_KEYS = [
8245
+ [2, "titleAnimation"],
8246
+ [3, "yolo"],
8247
+ [4, "streamFleet"],
8248
+ [5, "chime"],
8249
+ [6, "confirmExit"],
8250
+ [7, "nextPrediction"],
8251
+ [8, "featureMcp"],
8252
+ [9, "featurePlugins"],
8253
+ [10, "featureMemory"],
8254
+ [11, "featureSkills"],
8255
+ [12, "featureModelsRegistry"],
8256
+ [14, "allowOutsideProjectRoot"],
8257
+ [18, "enhanceEnabled"],
8258
+ [20, "indexOnStart"],
8259
+ [25, "reasoningPreserve"],
8260
+ [27, "contextAutoCompact"],
8261
+ [33, "debugStream"]
8262
+ ];
8263
+ for (const [f, key] of BOOL_KEYS) {
8264
+ if (field !== f) continue;
8265
+ return { ok: true, label, displayValue: values[key] ? "on" : "off" };
8266
+ }
8267
+ const ENUM_KEYS = [
8268
+ [0, "mode"],
8269
+ [13, "tokenSavingTier"],
8270
+ [19, "enhanceLanguage"],
8271
+ [23, "reasoningMode"],
8272
+ [24, "reasoningEffort"],
8273
+ [26, "cacheTtl"],
8274
+ [28, "contextStrategy"],
8275
+ [29, "contextMode"],
8276
+ [31, "logLevel"],
8277
+ [32, "auditLevel"],
8278
+ [34, "statuslineMode"],
8279
+ [35, "configScope"]
8280
+ ];
8281
+ for (const [f, key] of ENUM_KEYS) {
8282
+ if (field !== f) continue;
8283
+ return { ok: true, label, displayValue: String(values[key]) };
8284
+ }
8285
+ const presetLabel = (n, zeroLabel) => n === 0 ? zeroLabel : String(n);
8286
+ const PRESET_KEYS = [
8287
+ [1, "delayMs", formatSettingsDelay],
8288
+ [15, "maxIterations", formatMaxIterations],
8289
+ [16, "autoProceedMaxIterations", formatMaxIterations],
8290
+ [17, "enhanceDelayMs", formatEnhanceDelay],
8291
+ [21, "multiDiffSummaryThreshold", formatMultiDiffSummaryThreshold],
8292
+ [30, "maxConcurrent", (n) => presetLabel(n, "runtime default")]
8293
+ ];
8294
+ for (const [f, key, fmt2] of PRESET_KEYS) {
8295
+ if (field !== f) continue;
8296
+ return { ok: true, label, displayValue: fmt2(values[key]) };
8297
+ }
8298
+ if (field === 22) {
8299
+ return { ok: true, label, displayValue: values.thinkingWord };
8300
+ }
8301
+ return { ok: false, error: `Unknown settings field ${field}.` };
8302
+ }
8303
+ var SETTINGS_SECTIONS = [
8304
+ {
8305
+ name: "Autonomy",
8306
+ fields: [0, 1]
8307
+ },
8308
+ {
8309
+ name: "UX",
8310
+ fields: [2, 3, 4, 5, 6, 7]
8311
+ },
8312
+ {
8313
+ name: "Features",
8314
+ fields: [8, 9, 10, 11, 12, 13, 14]
8315
+ },
8316
+ {
8317
+ name: "Tools",
8318
+ fields: [15, 16, 17, 18, 19, 20, 21, 22]
8319
+ },
8320
+ {
8321
+ name: "Reasoning",
8322
+ fields: [23, 24, 25, 26]
8323
+ },
8324
+ {
8325
+ name: "Context",
8326
+ fields: [27, 28, 29]
8327
+ },
8328
+ {
8329
+ name: "Fleet",
8330
+ fields: [30]
8331
+ },
8332
+ {
8333
+ name: "Logging",
8334
+ fields: [31, 32]
8335
+ },
8336
+ {
8337
+ name: "Debug",
8338
+ fields: [33, 34, 35]
8339
+ }
8340
+ ];
8341
+ function formatAllSettingsSummary(values) {
8342
+ const lines = [];
8343
+ for (const section of SETTINGS_SECTIONS) {
8344
+ lines.push(`\u2500\u2500 ${section.name} \u2500\u2500`);
8345
+ for (const field of section.fields) {
8346
+ const result = getSettingsFieldValue(values, field);
8347
+ if (result.ok) {
8348
+ lines.push(` ${result.label.padEnd(28)} ${result.displayValue}`);
8349
+ }
8350
+ }
8351
+ }
8352
+ return lines.join("\n");
8353
+ }
8354
+ var SETTINGS_DEFAULTS = Object.freeze({
8355
+ mode: "off",
8356
+ delayMs: 0,
8357
+ titleAnimation: true,
8358
+ yolo: false,
8359
+ streamFleet: true,
8360
+ chime: false,
8361
+ confirmExit: true,
8362
+ nextPrediction: false,
8363
+ featureMcp: true,
8364
+ featurePlugins: true,
8365
+ featureMemory: true,
8366
+ featureSkills: true,
8367
+ featureModelsRegistry: true,
8368
+ tokenSavingTier: "off",
8369
+ allowOutsideProjectRoot: true,
8370
+ contextAutoCompact: true,
8371
+ contextStrategy: "hybrid",
8372
+ contextMode: "balanced",
8373
+ maxConcurrent: 10,
8374
+ logLevel: "info",
8375
+ auditLevel: "standard",
8376
+ indexOnStart: true,
8377
+ multiDiffSummaryThreshold: 5,
8378
+ maxIterations: 500,
8379
+ autoProceedMaxIterations: 50,
8380
+ enhanceDelayMs: 6e4,
8381
+ enhanceEnabled: true,
8382
+ enhanceLanguage: "original",
8383
+ debugStream: false,
8384
+ statuslineMode: "detailed",
8385
+ reasoningMode: "auto",
8386
+ reasoningEffort: "high",
8387
+ reasoningPreserve: false,
8388
+ thinkingWord: "thinking",
8389
+ cacheTtl: "default",
8390
+ configScope: "global"
8391
+ });
8392
+ function resetSettingsFieldValue(field) {
8393
+ const result = getSettingsFieldValue(SETTINGS_DEFAULTS, field);
8394
+ if (!result.ok) return result;
8395
+ const patch = buildResetPatch(field);
8396
+ if (!patch) return { ok: false, error: `Unknown settings field ${field}.` };
8397
+ return { ok: true, patch, label: result.label, displayValue: result.displayValue };
8398
+ }
8399
+ function buildResetPatch(field) {
8400
+ const KEY_MAP = [
8401
+ [0, "mode"],
8402
+ [1, "delayMs"],
8403
+ [2, "titleAnimation"],
8404
+ [3, "yolo"],
8405
+ [4, "streamFleet"],
8406
+ [5, "chime"],
8407
+ [6, "confirmExit"],
8408
+ [7, "nextPrediction"],
8409
+ [8, "featureMcp"],
8410
+ [9, "featurePlugins"],
8411
+ [10, "featureMemory"],
8412
+ [11, "featureSkills"],
8413
+ [12, "featureModelsRegistry"],
8414
+ [13, "tokenSavingTier"],
8415
+ [14, "allowOutsideProjectRoot"],
8416
+ [15, "maxIterations"],
8417
+ [16, "autoProceedMaxIterations"],
8418
+ [17, "enhanceDelayMs"],
8419
+ [18, "enhanceEnabled"],
8420
+ [19, "enhanceLanguage"],
8421
+ [20, "indexOnStart"],
8422
+ [21, "multiDiffSummaryThreshold"],
8423
+ [22, "thinkingWord"],
8424
+ [23, "reasoningMode"],
8425
+ [24, "reasoningEffort"],
8426
+ [25, "reasoningPreserve"],
8427
+ [26, "cacheTtl"],
8428
+ [27, "contextAutoCompact"],
8429
+ [28, "contextStrategy"],
8430
+ [29, "contextMode"],
8431
+ [30, "maxConcurrent"],
8432
+ [31, "logLevel"],
8433
+ [32, "auditLevel"],
8434
+ [33, "debugStream"],
8435
+ [34, "statuslineMode"],
8436
+ [35, "configScope"]
8437
+ ];
8438
+ for (const [f, key] of KEY_MAP) {
8439
+ if (f === field) {
8440
+ return { [key]: SETTINGS_DEFAULTS[key] };
8441
+ }
8442
+ }
8443
+ return null;
8444
+ }
7637
8445
  function SettingsPicker({
7638
8446
  field,
8447
+ filter,
7639
8448
  mode,
7640
8449
  delayMs,
7641
8450
  titleAnimation,
@@ -7657,7 +8466,10 @@ function SettingsPicker({
7657
8466
  enhanceEnabled,
7658
8467
  enhanceLanguage,
7659
8468
  indexOnStart,
8469
+ multiDiffSummaryThreshold,
7660
8470
  thinkingWord,
8471
+ thinkingWordEditing,
8472
+ thinkingWordDraft,
7661
8473
  reasoningMode,
7662
8474
  reasoningEffort,
7663
8475
  reasoningPreserve,
@@ -7784,12 +8596,17 @@ function SettingsPicker({
7784
8596
  value: boolVal(indexOnStart),
7785
8597
  detail: "Run incremental index at session start"
7786
8598
  },
8599
+ {
8600
+ label: "Multi-diff summary",
8601
+ value: formatMultiDiffSummaryThreshold(multiDiffSummaryThreshold),
8602
+ detail: "Min files before aggregate header (0 = off, default 5, 10 for big diffs)"
8603
+ },
7787
8604
  // ── Reasoning ──
7788
8605
  { section: "Reasoning" },
7789
8606
  {
7790
8607
  label: "Thinking word",
7791
- value: thinkingWord,
7792
- detail: "Word shown in status bar while agent works"
8608
+ value: thinkingWordEditing ? `${thinkingWordDraft ?? ""}\u258F` : thinkingWord,
8609
+ detail: thinkingWordEditing ? "type a word \xB7 Enter \u2713 \xB7 Esc \u2717 (\u226416, letters/digits/_/-)" : "Status-bar working word \xB7 thinking/random = surprise me \xB7 \u2190/\u2192 presets \xB7 Enter to type"
7793
8610
  },
7794
8611
  {
7795
8612
  label: "Reasoning mode",
@@ -7869,6 +8686,94 @@ function SettingsPicker({
7869
8686
  for (let i = 0; i < rows.length; i++) {
7870
8687
  if (!rows[i]?.section) fieldRowIndex.push(i);
7871
8688
  }
8689
+ const filterActive = Boolean(filter && filter.length > 1);
8690
+ const filterQuery = filter && filter.length > 1 ? filter.slice(1).toLowerCase() : "";
8691
+ const fuzzyMatch = (haystack, needle) => {
8692
+ const lower = haystack.toLowerCase();
8693
+ const indices = [];
8694
+ let ni = 0;
8695
+ for (let hi = 0; hi < lower.length && ni < needle.length; hi++) {
8696
+ if (lower[hi] === needle[ni]) {
8697
+ indices.push(hi);
8698
+ ni++;
8699
+ }
8700
+ }
8701
+ return ni === needle.length ? indices : void 0;
8702
+ };
8703
+ const scoreMatch = (contiguous, matchIndices, labelLength) => {
8704
+ const tier = contiguous ? 0 : 1e3;
8705
+ if (!matchIndices || matchIndices.length === 0) {
8706
+ return tier;
8707
+ }
8708
+ const firstPos = matchIndices[0] ?? 0;
8709
+ const span = (matchIndices.at(-1) ?? 0) - firstPos;
8710
+ const gap = span - (matchIndices.length - 1);
8711
+ const lengthPenalty = Math.max(0, labelLength - 8);
8712
+ return tier + firstPos + gap * 2 + lengthPenalty;
8713
+ };
8714
+ const fuzzyResults = filterActive ? SETTINGS_PICKER_JUMP_CHORDS.map((c) => {
8715
+ const label = c.label;
8716
+ const contiguous = label.toLowerCase().includes(filterQuery);
8717
+ if (contiguous) {
8718
+ const firstIdx = label.toLowerCase().indexOf(filterQuery);
8719
+ const indices = Array.from({ length: filterQuery.length }, (_, i) => firstIdx + i);
8720
+ return { chord: c, contiguous, fuzzyIdx: void 0, score: scoreMatch(true, indices, label.length) };
8721
+ }
8722
+ const fuzzyIdx = fuzzyMatch(label, filterQuery);
8723
+ return {
8724
+ chord: c,
8725
+ contiguous: false,
8726
+ fuzzyIdx,
8727
+ score: fuzzyIdx !== void 0 ? scoreMatch(false, fuzzyIdx, label.length) : Infinity
8728
+ };
8729
+ }) : [];
8730
+ const rankedResults = fuzzyResults.filter((r) => r.contiguous || r.fuzzyIdx !== void 0).sort((a, b) => a.score - b.score);
8731
+ const filteredFieldIndices = rankedResults.map((r) => r.chord.field);
8732
+ const highlightSegments = (label) => {
8733
+ if (!filterActive || !filterQuery) return [{ text: label, match: false }];
8734
+ const result = fuzzyResults.find((r) => r.chord.label === label);
8735
+ if (!result) return [{ text: label, match: false }];
8736
+ if (result.contiguous) {
8737
+ const lower = label.toLowerCase();
8738
+ const segments = [];
8739
+ let cursor = 0;
8740
+ while (cursor < label.length) {
8741
+ const idx = lower.indexOf(filterQuery, cursor);
8742
+ if (idx === -1) {
8743
+ segments.push({ text: label.slice(cursor), match: false });
8744
+ break;
8745
+ }
8746
+ if (idx > cursor) {
8747
+ segments.push({ text: label.slice(cursor, idx), match: false });
8748
+ }
8749
+ segments.push({ text: label.slice(idx, idx + filterQuery.length), match: true });
8750
+ cursor = idx + filterQuery.length;
8751
+ }
8752
+ return segments;
8753
+ }
8754
+ if (result.fuzzyIdx) {
8755
+ const matchSet = new Set(result.fuzzyIdx);
8756
+ const segments = [];
8757
+ let current = "";
8758
+ let currentMatch = false;
8759
+ for (let i = 0; i < label.length; i++) {
8760
+ const isMatch = matchSet.has(i);
8761
+ if (i === 0) {
8762
+ current = label[i] ?? "";
8763
+ currentMatch = isMatch;
8764
+ } else if (isMatch === currentMatch) {
8765
+ current += label[i] ?? "";
8766
+ } else {
8767
+ segments.push({ text: current, match: currentMatch });
8768
+ current = label[i] ?? "";
8769
+ currentMatch = isMatch;
8770
+ }
8771
+ }
8772
+ if (current) segments.push({ text: current, match: currentMatch });
8773
+ return segments;
8774
+ }
8775
+ return [{ text: label, match: false }];
8776
+ };
7872
8777
  const VISIBLE_FIELDS = 8;
7873
8778
  const totalFields = fieldRowIndex.length;
7874
8779
  const windowStart = totalFields <= VISIBLE_FIELDS ? 0 : Math.max(0, Math.min(field - Math.floor(VISIBLE_FIELDS / 2), totalFields - VISIBLE_FIELDS));
@@ -7897,30 +8802,80 @@ function SettingsPicker({
7897
8802
  };
7898
8803
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
7899
8804
  /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\u2501\u2501 Settings \u2501\u2501" }),
7900
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 field \xB7 \u2190/\u2192 change + autosave \xB7 F5 to close" }),
7901
- hasAbove ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \u2191 ${windowStart} field${windowStart === 1 ? "" : "s"} above` }) : null,
7902
- rows.map((row, i) => {
7903
- const fieldAtRow = fieldRowIndex.indexOf(i);
7904
- if (fieldAtRow === -1) {
7905
- if (shouldShowSection(i)) {
7906
- return /* @__PURE__ */ jsxs(Text, { bold: true, color: "green", children: [
7907
- "\u2500\u2500 ",
7908
- row.section,
7909
- " \u2500\u2500"
7910
- ] }, `section-${row.section ?? i}`);
8805
+ filterActive ? /* @__PURE__ */ jsx(Text, { color: "yellow", bold: true, children: `Filter: ${filter} (${filteredFieldIndices.length} match${filteredFieldIndices.length === 1 ? "" : "es"})` }) : /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 field \xB7 \u2190/\u2192 change + autosave \xB7 `/` to search \xB7 F5 to close" }),
8806
+ hasAbove && !filterActive ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \u2191 ${windowStart} field${windowStart === 1 ? "" : "s"} above` }) : null,
8807
+ filterActive && filteredFieldIndices.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, italic: true, children: "No matching settings rows." }) : null,
8808
+ filterActive ? (
8809
+ // Filter mode: render ranked rows in score order (not picker
8810
+ // order). Each row is looked up by its field index from the
8811
+ // `rows` array via `fieldRowIndex`.
8812
+ rankedResults.map((result) => {
8813
+ const fieldIdx = result.chord.field;
8814
+ const rowIdx = fieldRowIndex[fieldIdx] ?? -1;
8815
+ const row = rows[rowIdx];
8816
+ if (!row || !row.label) return null;
8817
+ const selected = fieldIdx === field;
8818
+ const labelStr = row.label;
8819
+ const segments = highlightSegments(labelStr);
8820
+ const padNeeded = Math.max(0, 26 - labelStr.length);
8821
+ return /* @__PURE__ */ jsxs(Text, { inverse: selected, ...selected ? { color: "yellow" } : {}, children: [
8822
+ selected ? "\u203A " : " ",
8823
+ segments.map((seg, j) => /* @__PURE__ */ jsx(
8824
+ Text,
8825
+ {
8826
+ bold: true,
8827
+ ...seg.match ? { color: "yellow" } : { dimColor: true },
8828
+ children: seg.text
8829
+ },
8830
+ j
8831
+ )),
8832
+ /* @__PURE__ */ jsx(Text, { bold: true, dimColor: true, children: " ".repeat(padNeeded) }),
8833
+ /* @__PURE__ */ jsx(Text, { color: "cyan", children: String(row.value ?? "").padEnd(12) }),
8834
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: row.detail ?? "" })
8835
+ ] }, `frow-${labelStr}`);
8836
+ })
8837
+ ) : (
8838
+ // Normal mode: render rows in picker order with windowing.
8839
+ rows.map((row, i) => {
8840
+ const fieldAtRow = fieldRowIndex.indexOf(i);
8841
+ if (fieldAtRow === -1) {
8842
+ if (filterActive) return null;
8843
+ if (shouldShowSection(i)) {
8844
+ return /* @__PURE__ */ jsxs(Text, { bold: true, color: "green", children: [
8845
+ "\u2500\u2500 ",
8846
+ row.section,
8847
+ " \u2500\u2500"
8848
+ ] }, `section-${row.section ?? i}`);
8849
+ }
8850
+ return null;
7911
8851
  }
7912
- return null;
7913
- }
7914
- if (fieldAtRow < windowStart || fieldAtRow >= windowEnd) return null;
7915
- const selected = fieldAtRow === field;
7916
- return /* @__PURE__ */ jsxs(Text, { inverse: selected, ...selected ? { color: "yellow" } : {}, children: [
7917
- selected ? "\u203A " : " ",
7918
- /* @__PURE__ */ jsx(Text, { bold: true, children: (row.label ?? "").padEnd(26) }),
7919
- /* @__PURE__ */ jsx(Text, { color: "cyan", children: String(row.value ?? "").padEnd(12) }),
7920
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: row.detail ?? "" })
7921
- ] }, `row-${row.label ?? fieldAtRow}`);
7922
- }),
7923
- hasBelow ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \u2193 ${totalFields - windowEnd} field${totalFields - windowEnd === 1 ? "" : "s"} below` }) : null,
8852
+ if (filterActive) {
8853
+ if (!filteredFieldIndices.includes(fieldAtRow)) return null;
8854
+ } else if (fieldAtRow < windowStart || fieldAtRow >= windowEnd) return null;
8855
+ const selected = fieldAtRow === field;
8856
+ const labelStr = row.label ?? "";
8857
+ const segments = highlightSegments(labelStr);
8858
+ const padNeeded = Math.max(0, 26 - labelStr.length);
8859
+ return /* @__PURE__ */ jsxs(Text, { inverse: selected, ...selected ? { color: "yellow" } : {}, children: [
8860
+ selected ? "\u203A " : " ",
8861
+ filterActive ? /* @__PURE__ */ jsxs(Fragment, { children: [
8862
+ segments.map((seg, j) => /* @__PURE__ */ jsx(
8863
+ Text,
8864
+ {
8865
+ bold: true,
8866
+ ...seg.match ? { color: "yellow" } : { dimColor: true },
8867
+ children: seg.text
8868
+ },
8869
+ j
8870
+ )),
8871
+ /* @__PURE__ */ jsx(Text, { bold: true, dimColor: true, children: " ".repeat(padNeeded) })
8872
+ ] }) : /* @__PURE__ */ jsx(Text, { bold: true, children: labelStr.padEnd(26) }),
8873
+ /* @__PURE__ */ jsx(Text, { color: "cyan", children: String(row.value ?? "").padEnd(12) }),
8874
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: row.detail ?? "" })
8875
+ ] }, `row-${row.label ?? fieldAtRow}`);
8876
+ })
8877
+ ),
8878
+ hasBelow && !filterActive ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \u2193 ${totalFields - windowEnd} field${totalFields - windowEnd === 1 ? "" : "s"} below` }) : null,
7924
8879
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: configScope === "project" ? "Persisted to <project>/.wrongstack/config.json" : "Persisted to ~/.wrongstack/config.json" }),
7925
8880
  hint ? /* @__PURE__ */ jsx(Text, { color: "yellow", children: hint }) : null
7926
8881
  ] });
@@ -8066,19 +9021,23 @@ function StatuslinePicker({
8066
9021
  const visibleChipsMap = new Map(visibleChips.map((c) => [c.key, c]));
8067
9022
  const totalFields = STATUSLINE_ITEMS.length;
8068
9023
  const byLine = groupByLine(STATUSLINE_ITEMS);
9024
+ const VISIBLE_FIELDS = 8;
9025
+ const windowStart = Math.max(0, Math.min(field - Math.floor(VISIBLE_FIELDS / 2), totalFields - VISIBLE_FIELDS));
9026
+ const windowEnd = Math.min(windowStart + VISIBLE_FIELDS, totalFields);
9027
+ const hasAbove = windowStart > 0;
9028
+ const hasBelow = windowEnd < totalFields;
8069
9029
  const rows = [];
8070
9030
  for (const [line, items] of [...byLine.entries()].sort((a, b) => a[0] - b[0])) {
9031
+ const windowedItems = items.filter((item) => {
9032
+ const idx = STATUSLINE_ITEMS.indexOf(item);
9033
+ return idx >= windowStart && idx < windowEnd;
9034
+ });
9035
+ if (windowedItems.length === 0) continue;
8071
9036
  rows.push({ section: `Line ${line}` });
8072
- for (const item of items) {
8073
- const fieldIdx = STATUSLINE_ITEMS.indexOf(item);
8074
- rows.push({ item, fieldIdx });
9037
+ for (const item of windowedItems) {
9038
+ rows.push({ item, fieldIdx: STATUSLINE_ITEMS.indexOf(item) });
8075
9039
  }
8076
9040
  }
8077
- const VISIBLE_FIELDS = 7;
8078
- const windowStart = Math.max(0, Math.min(field - Math.floor(VISIBLE_FIELDS / 2), totalFields - VISIBLE_FIELDS));
8079
- const windowEnd = Math.min(windowStart + VISIBLE_FIELDS, totalFields);
8080
- const hasAbove = windowStart > 0;
8081
- const hasBelow = windowEnd < totalFields;
8082
9041
  const boolVal = (item) => {
8083
9042
  if (hiddenSet.has(item)) return "off";
8084
9043
  if (STREAM_CHIP_KEYS.includes(item)) {
@@ -10402,7 +11361,21 @@ function reducer(state, action) {
10402
11361
  ...closePanels(state),
10403
11362
  settingsPicker: {
10404
11363
  open: true,
10405
- field: 0,
11364
+ // The persisted `lastSettingsField` (from the canonical Settings
11365
+ // shape) drives where the picker lands on open. The slice's
11366
+ // `field` is a working copy that mirrors it until the user
11367
+ // navigates again. `state.settingsPicker.lastSettingsField` is
11368
+ // the fallback for the (rare) case where the action omits it
11369
+ // — e.g., a tests/dispatch path that hasn't been updated yet.
11370
+ // `||` (not `??`): a payload `lastSettingsField` of 0 means
11371
+ // "no saved value" (the default in the canonical Settings),
11372
+ // so we fall through to the runtime state's tracked value.
11373
+ // This lets the in-session `settingsFieldSet`/`settingsFieldMove`
11374
+ // preserve the field across close/reopen within the same session,
11375
+ // while a non-zero persisted value (loaded from disk) takes
11376
+ // priority on a fresh open.
11377
+ field: action.lastSettingsField || state.settingsPicker.lastSettingsField || 0,
11378
+ lastSettingsField: action.lastSettingsField || state.settingsPicker.lastSettingsField || 0,
10406
11379
  mode: action.mode,
10407
11380
  delayMs: action.delayMs,
10408
11381
  titleAnimation: action.titleAnimation,
@@ -10425,6 +11398,7 @@ function reducer(state, action) {
10425
11398
  logLevel: action.logLevel,
10426
11399
  auditLevel: action.auditLevel,
10427
11400
  indexOnStart: action.indexOnStart,
11401
+ multiDiffSummaryThreshold: action.multiDiffSummaryThreshold,
10428
11402
  maxIterations: action.maxIterations,
10429
11403
  autoProceedMaxIterations: action.autoProceedMaxIterations,
10430
11404
  enhanceDelayMs: action.enhanceDelayMs,
@@ -10436,6 +11410,11 @@ function reducer(state, action) {
10436
11410
  reasoningEffort: action.reasoningEffort,
10437
11411
  reasoningPreserve: action.reasoningPreserve,
10438
11412
  thinkingWord: action.thinkingWord,
11413
+ thinkingWordEditing: false,
11414
+ thinkingWordDraft: "",
11415
+ // Filter is always cleared on open — the user starts fresh.
11416
+ // Persisted `lastSettingsField` is restored separately above.
11417
+ filter: "",
10439
11418
  cacheTtl: action.cacheTtl,
10440
11419
  configScope: action.configScope,
10441
11420
  hint: void 0
@@ -10450,13 +11429,28 @@ function reducer(state, action) {
10450
11429
  const next = (state.settingsPicker.field + action.delta + SETTINGS_FIELD_COUNT) % SETTINGS_FIELD_COUNT;
10451
11430
  return {
10452
11431
  ...state,
10453
- settingsPicker: { ...state.settingsPicker, field: next, hint: void 0 }
11432
+ settingsPicker: {
11433
+ ...state.settingsPicker,
11434
+ field: next,
11435
+ lastSettingsField: next,
11436
+ thinkingWordEditing: false,
11437
+ thinkingWordDraft: "",
11438
+ hint: void 0
11439
+ }
10454
11440
  };
10455
11441
  }
10456
11442
  case "settingsFieldSet": {
10457
11443
  const field = action.field >= 0 && action.field < SETTINGS_FIELD_COUNT ? action.field : 0;
10458
- return { ...state, settingsPicker: { ...state.settingsPicker, field, hint: void 0 } };
11444
+ return {
11445
+ ...state,
11446
+ settingsPicker: { ...state.settingsPicker, field, lastSettingsField: field, hint: void 0 }
11447
+ };
10459
11448
  }
11449
+ case "settingsFilterSet":
11450
+ return {
11451
+ ...state,
11452
+ settingsPicker: { ...state.settingsPicker, filter: action.filter }
11453
+ };
10460
11454
  case "settingsValueChange": {
10461
11455
  const sp = state.settingsPicker;
10462
11456
  const f = sp.field;
@@ -10618,8 +11612,28 @@ function reducer(state, action) {
10618
11612
  ...state,
10619
11613
  settingsPicker: { ...sp, indexOnStart: !sp.indexOnStart, hint: bootHint }
10620
11614
  };
10621
- if (f === 21) return state;
10622
- if (f === 22) {
11615
+ if (f === 21) {
11616
+ const j = MULTI_DIFF_SUMMARY_THRESHOLD_PRESETS.indexOf(sp.multiDiffSummaryThreshold);
11617
+ const base = j < 0 ? 0 : j;
11618
+ const next = (base + action.delta + MULTI_DIFF_SUMMARY_THRESHOLD_PRESETS.length) % MULTI_DIFF_SUMMARY_THRESHOLD_PRESETS.length;
11619
+ const multiDiffSummaryThreshold = expectDefined$1(MULTI_DIFF_SUMMARY_THRESHOLD_PRESETS[next]);
11620
+ return {
11621
+ ...state,
11622
+ settingsPicker: { ...sp, multiDiffSummaryThreshold, hint: void 0 }
11623
+ };
11624
+ }
11625
+ if (f === THINKING_WORD_FIELD) {
11626
+ const cur = sp.thinkingWord;
11627
+ const list = THINKING_WORD_PRESETS.includes(cur) ? [...THINKING_WORD_PRESETS] : [cur, ...THINKING_WORD_PRESETS];
11628
+ const i = list.indexOf(cur);
11629
+ const base = i < 0 ? 0 : i;
11630
+ const next = (base + action.delta + list.length) % list.length;
11631
+ return {
11632
+ ...state,
11633
+ settingsPicker: { ...sp, thinkingWord: expectDefined$1(list[next]), hint: void 0 }
11634
+ };
11635
+ }
11636
+ if (f === 23) {
10623
11637
  const i = REASONING_MODES.indexOf(sp.reasoningMode);
10624
11638
  const base = i < 0 ? 0 : i;
10625
11639
  const next = (base + action.delta + REASONING_MODES.length) % REASONING_MODES.length;
@@ -10632,7 +11646,7 @@ function reducer(state, action) {
10632
11646
  }
10633
11647
  };
10634
11648
  }
10635
- if (f === 23) {
11649
+ if (f === 24) {
10636
11650
  const i = REASONING_EFFORTS.indexOf(
10637
11651
  sp.reasoningEffort
10638
11652
  );
@@ -10647,12 +11661,12 @@ function reducer(state, action) {
10647
11661
  }
10648
11662
  };
10649
11663
  }
10650
- if (f === 24)
11664
+ if (f === 25)
10651
11665
  return {
10652
11666
  ...state,
10653
11667
  settingsPicker: { ...sp, reasoningPreserve: !sp.reasoningPreserve, hint: void 0 }
10654
11668
  };
10655
- if (f === 25) {
11669
+ if (f === 26) {
10656
11670
  const i = CACHE_TTLS.indexOf(sp.cacheTtl);
10657
11671
  const base = i < 0 ? 0 : i;
10658
11672
  const next = (base + action.delta + CACHE_TTLS.length) % CACHE_TTLS.length;
@@ -10661,12 +11675,12 @@ function reducer(state, action) {
10661
11675
  settingsPicker: { ...sp, cacheTtl: expectDefined$1(CACHE_TTLS[next]), hint: void 0 }
10662
11676
  };
10663
11677
  }
10664
- if (f === 26)
11678
+ if (f === 27)
10665
11679
  return {
10666
11680
  ...state,
10667
11681
  settingsPicker: { ...sp, contextAutoCompact: !sp.contextAutoCompact, hint: void 0 }
10668
11682
  };
10669
- if (f === 27) {
11683
+ if (f === 28) {
10670
11684
  const i = COMPACTOR_STRATEGIES.indexOf(sp.contextStrategy);
10671
11685
  const base = i < 0 ? 0 : i;
10672
11686
  const next = (base + action.delta + COMPACTOR_STRATEGIES.length) % COMPACTOR_STRATEGIES.length;
@@ -10679,7 +11693,7 @@ function reducer(state, action) {
10679
11693
  }
10680
11694
  };
10681
11695
  }
10682
- if (f === 28) {
11696
+ if (f === 29) {
10683
11697
  const i = CONTEXT_MODES.indexOf(sp.contextMode);
10684
11698
  const base = i < 0 ? 0 : i;
10685
11699
  const next = (base + action.delta + CONTEXT_MODES.length) % CONTEXT_MODES.length;
@@ -10692,7 +11706,7 @@ function reducer(state, action) {
10692
11706
  }
10693
11707
  };
10694
11708
  }
10695
- if (f === 29) {
11709
+ if (f === 30) {
10696
11710
  const j = MAX_CONCURRENT_PRESETS.indexOf(sp.maxConcurrent);
10697
11711
  const base = j < 0 ? 0 : j;
10698
11712
  const next = (base + action.delta + MAX_CONCURRENT_PRESETS.length) % MAX_CONCURRENT_PRESETS.length;
@@ -10706,7 +11720,7 @@ function reducer(state, action) {
10706
11720
  }
10707
11721
  };
10708
11722
  }
10709
- if (f === 30) {
11723
+ if (f === 31) {
10710
11724
  const i = LOG_LEVELS.indexOf(sp.logLevel);
10711
11725
  const base = i < 0 ? 0 : i;
10712
11726
  const next = (base + action.delta + LOG_LEVELS.length) % LOG_LEVELS.length;
@@ -10715,7 +11729,7 @@ function reducer(state, action) {
10715
11729
  settingsPicker: { ...sp, logLevel: expectDefined$1(LOG_LEVELS[next]), hint: void 0 }
10716
11730
  };
10717
11731
  }
10718
- if (f === 31) {
11732
+ if (f === 32) {
10719
11733
  const i = AUDIT_LEVELS.indexOf(sp.auditLevel);
10720
11734
  const base = i < 0 ? 0 : i;
10721
11735
  const next = (base + action.delta + AUDIT_LEVELS.length) % AUDIT_LEVELS.length;
@@ -10724,12 +11738,12 @@ function reducer(state, action) {
10724
11738
  settingsPicker: { ...sp, auditLevel: expectDefined$1(AUDIT_LEVELS[next]), hint: void 0 }
10725
11739
  };
10726
11740
  }
10727
- if (f === 32)
11741
+ if (f === 33)
10728
11742
  return {
10729
11743
  ...state,
10730
11744
  settingsPicker: { ...sp, debugStream: !sp.debugStream, hint: void 0 }
10731
11745
  };
10732
- if (f === 33) {
11746
+ if (f === 34) {
10733
11747
  const i = STATUSLINE_MODES.indexOf(sp.statuslineMode);
10734
11748
  const base = i < 0 ? STATUSLINE_MODES.indexOf("detailed") : i;
10735
11749
  const next = (base + action.delta + STATUSLINE_MODES.length) % STATUSLINE_MODES.length;
@@ -10742,7 +11756,7 @@ function reducer(state, action) {
10742
11756
  }
10743
11757
  };
10744
11758
  }
10745
- if (f === 34) {
11759
+ if (f === 35) {
10746
11760
  const i = CONFIG_SCOPES.indexOf(sp.configScope);
10747
11761
  const base = i < 0 ? 0 : i;
10748
11762
  const next = (base + action.delta + CONFIG_SCOPES.length) % CONFIG_SCOPES.length;
@@ -10757,8 +11771,67 @@ function reducer(state, action) {
10757
11771
  }
10758
11772
  return state;
10759
11773
  }
11774
+ case "settingsValueSet": {
11775
+ return {
11776
+ ...state,
11777
+ settingsPicker: { ...state.settingsPicker, ...action.patch, hint: void 0 }
11778
+ };
11779
+ }
10760
11780
  case "settingsHint":
10761
11781
  return { ...state, settingsPicker: { ...state.settingsPicker, hint: action.text } };
11782
+ case "settingsThinkingEditStart":
11783
+ return {
11784
+ ...state,
11785
+ settingsPicker: {
11786
+ ...state.settingsPicker,
11787
+ thinkingWordEditing: true,
11788
+ // Seed the draft with the current word so the user edits from it.
11789
+ thinkingWordDraft: state.settingsPicker.thinkingWord,
11790
+ hint: void 0
11791
+ }
11792
+ };
11793
+ case "settingsThinkingEditChange":
11794
+ return {
11795
+ ...state,
11796
+ settingsPicker: {
11797
+ ...state.settingsPicker,
11798
+ // Hard-cap the draft so it can't grow past the persisted limit.
11799
+ thinkingWordDraft: action.draft.slice(0, MAX_TUI_THINKING_WORD_LENGTH),
11800
+ hint: void 0
11801
+ }
11802
+ };
11803
+ case "settingsThinkingEditCommit": {
11804
+ const sp = state.settingsPicker;
11805
+ const raw = sp.thinkingWordDraft.trim();
11806
+ if (raw.length === 0) {
11807
+ return {
11808
+ ...state,
11809
+ settingsPicker: { ...sp, thinkingWordEditing: false, thinkingWordDraft: "", hint: void 0 }
11810
+ };
11811
+ }
11812
+ const normalized = normalizeTuiThinkingWord(raw);
11813
+ const valid = normalized === raw;
11814
+ return {
11815
+ ...state,
11816
+ settingsPicker: {
11817
+ ...sp,
11818
+ thinkingWord: valid ? normalized : sp.thinkingWord,
11819
+ thinkingWordEditing: false,
11820
+ thinkingWordDraft: "",
11821
+ hint: valid ? void 0 : `Invalid word \u2014 keep it \u2264${MAX_TUI_THINKING_WORD_LENGTH} chars (letters/digits/_/-)`
11822
+ }
11823
+ };
11824
+ }
11825
+ case "settingsThinkingEditCancel":
11826
+ return {
11827
+ ...state,
11828
+ settingsPicker: {
11829
+ ...state.settingsPicker,
11830
+ thinkingWordEditing: false,
11831
+ thinkingWordDraft: "",
11832
+ hint: void 0
11833
+ }
11834
+ };
10762
11835
  // ── Statusline picker ───────────────────────────────────────────────
10763
11836
  case "statuslineOpen":
10764
11837
  return {
@@ -12085,7 +13158,7 @@ function App({
12085
13158
  },
12086
13159
  autonomyPicker: { open: false, options: [], selected: 0 },
12087
13160
  resumePicker: { open: false, sessions: [], selected: 0, busy: false, hint: void 0, error: void 0 },
12088
- settingsPicker: { open: false, field: 0, mode: "off", delayMs: 0, titleAnimation: true, yolo: false, streamFleet: true, chime: false, confirmExit: true, nextPrediction: false, featureMcp: true, featurePlugins: true, featureMemory: true, featureSkills: true, featureModelsRegistry: true, tokenSavingTier: "off", allowOutsideProjectRoot: true, contextAutoCompact: true, contextStrategy: "hybrid", contextMode: "balanced", maxConcurrent: 10, logLevel: "info", auditLevel: "standard", indexOnStart: true, maxIterations: 500, autoProceedMaxIterations: 50, enhanceDelayMs: 6e4, enhanceEnabled: true, enhanceLanguage: "original", debugStream: false, statuslineMode: "detailed", reasoningMode: "auto", reasoningEffort: "high", reasoningPreserve: false, thinkingWord: "thinking", cacheTtl: "default", configScope: "global" },
13161
+ settingsPicker: { open: false, field: 0, lastSettingsField: 0, filter: "", mode: "off", delayMs: 0, titleAnimation: true, yolo: false, streamFleet: true, chime: false, confirmExit: true, nextPrediction: false, featureMcp: true, featurePlugins: true, featureMemory: true, featureSkills: true, featureModelsRegistry: true, tokenSavingTier: "off", allowOutsideProjectRoot: true, contextAutoCompact: true, contextStrategy: "hybrid", contextMode: "balanced", maxConcurrent: 10, logLevel: "info", auditLevel: "standard", indexOnStart: true, multiDiffSummaryThreshold: 5, maxIterations: 500, autoProceedMaxIterations: 50, enhanceDelayMs: 6e4, enhanceEnabled: true, enhanceLanguage: "original", debugStream: false, statuslineMode: "detailed", reasoningMode: "auto", reasoningEffort: "high", reasoningPreserve: false, thinkingWord: "thinking", thinkingWordEditing: false, thinkingWordDraft: "", cacheTtl: "default", configScope: "global" },
12089
13162
  statuslinePicker: { open: false, field: 0, hiddenItems: [], visibleChips: [], hint: void 0 },
12090
13163
  projectPicker: { open: false, allItems: [], items: [], selected: 0, filter: "", hint: void 0 },
12091
13164
  fKeyPicker: { open: false, selected: 0 },
@@ -12222,6 +13295,20 @@ function App({
12222
13295
  const liveSettings = getSettings?.();
12223
13296
  const liveStatuslineMode = liveSettings?.statuslineMode ?? "detailed";
12224
13297
  const liveThinkingWord = liveSettings?.thinkingWord ?? "thinking";
13298
+ const [rolledThinkingWord, setRolledThinkingWord] = useState(
13299
+ () => pickRandomTuiThinkingWord()
13300
+ );
13301
+ const thinkingWorking = state.status === "running" || state.status === "streaming";
13302
+ const prevThinkingWorkingRef = useRef(false);
13303
+ useEffect(() => {
13304
+ if (thinkingWorking && !prevThinkingWorkingRef.current) {
13305
+ setRolledThinkingWord((prev) => pickRandomTuiThinkingWord(prev));
13306
+ }
13307
+ prevThinkingWorkingRef.current = thinkingWorking;
13308
+ }, [thinkingWorking]);
13309
+ const displayThinkingWord = isRandomTuiThinkingWord(liveThinkingWord) ? rolledThinkingWord : liveThinkingWord;
13310
+ const displayThinkingWordRef = useRef(displayThinkingWord);
13311
+ displayThinkingWordRef.current = displayThinkingWord;
12225
13312
  const chimeRef = useRef(chime);
12226
13313
  chimeRef.current = liveSettings?.chime ?? chime;
12227
13314
  const confirmExitRef = useRef(confirmExit);
@@ -12809,6 +13896,8 @@ function App({
12809
13896
  logLevel: sp.logLevel,
12810
13897
  auditLevel: sp.auditLevel,
12811
13898
  indexOnStart: sp.indexOnStart,
13899
+ multiDiffSummaryThreshold: sp.multiDiffSummaryThreshold,
13900
+ lastSettingsField: sp.lastSettingsField,
12812
13901
  maxIterations: sp.maxIterations,
12813
13902
  autoProceedMaxIterations: sp.autoProceedMaxIterations,
12814
13903
  enhanceDelayMs: sp.enhanceDelayMs,
@@ -13208,6 +14297,8 @@ function App({
13208
14297
  logLevel: s2.logLevel ?? "info",
13209
14298
  auditLevel: s2.auditLevel ?? "standard",
13210
14299
  indexOnStart: s2.indexOnStart ?? true,
14300
+ multiDiffSummaryThreshold: s2.multiDiffSummaryThreshold ?? 5,
14301
+ lastSettingsField: s2.lastSettingsField ?? 0,
13211
14302
  maxIterations: s2.maxIterations ?? 500,
13212
14303
  autoProceedMaxIterations: s2.autoProceedMaxIterations ?? 50,
13213
14304
  enhanceDelayMs: s2.enhanceDelayMs ?? 6e4,
@@ -13352,6 +14443,8 @@ function App({
13352
14443
  logLevel: sp.logLevel,
13353
14444
  auditLevel: sp.auditLevel,
13354
14445
  indexOnStart: sp.indexOnStart,
14446
+ multiDiffSummaryThreshold: sp.multiDiffSummaryThreshold,
14447
+ lastSettingsField: sp.lastSettingsField,
13355
14448
  maxIterations: sp.maxIterations,
13356
14449
  autoProceedMaxIterations: sp.autoProceedMaxIterations,
13357
14450
  enhanceDelayMs: sp.enhanceDelayMs,
@@ -13442,8 +14535,95 @@ function App({
13442
14535
  const cmd = {
13443
14536
  name: "settings",
13444
14537
  aliases: ["config", "prefs"],
13445
- description: "Open the interactive settings editor (19 config fields across 8 sections).",
13446
- async run() {
14538
+ description: "Open the settings editor, or set a value inline: /settings [<chord> [<value>]].",
14539
+ argsHint: "[<chord> [<value>]]",
14540
+ help: "Open the settings editor.\n\n /settings Open on the last-visited row\n /settings <chord> Open on that row\n /settings <chord> <v> Set <chord> to <v> without opening the picker\n /settings reset <chord> Reset <chord> to its factory default\n\nExamples:\n /settings yolo on Enable YOLO mode\n /settings multi-diff 8 Set multi-diff threshold to 8\n /settings thinking-word pondering Set the working-state word\n\nAvailable chords:\n " + settingsPickerJumpNames().join("\n "),
14541
+ async run(args) {
14542
+ const query = args.trim();
14543
+ if (query === "") {
14544
+ openSettings();
14545
+ return { message: void 0 };
14546
+ }
14547
+ if (query === "reset" || query.startsWith("reset ")) {
14548
+ const subArg = query.slice("reset".length).trim();
14549
+ if (subArg === "") {
14550
+ return {
14551
+ message: "Usage: /settings reset <chord>\nAvailable: " + settingsPickerJumpNames().join(", ")
14552
+ };
14553
+ }
14554
+ const field2 = settingsPickerJumpByName(subArg);
14555
+ if (field2 === void 0) {
14556
+ return {
14557
+ message: `Unknown settings row "${subArg}".
14558
+ Available chords:
14559
+ ${settingsPickerJumpNames().join("\n ")}`
14560
+ };
14561
+ }
14562
+ const result = resetSettingsFieldValue(field2);
14563
+ if (!result.ok) {
14564
+ return { message: result.error };
14565
+ }
14566
+ dispatch({ type: "settingsValueSet", patch: result.patch });
14567
+ const cur = getSettings ? getSettings() : void 0;
14568
+ if (cur && saveSettings) {
14569
+ const { tokenSavingTier, ...rest } = result.patch;
14570
+ Promise.resolve(
14571
+ saveSettings({
14572
+ ...cur,
14573
+ ...rest,
14574
+ ...tokenSavingTier !== void 0 ? { featureTokenSaving: tokenSavingTier } : {}
14575
+ })
14576
+ ).then((err) => {
14577
+ if (err) dispatch({ type: "settingsHint", text: err });
14578
+ });
14579
+ }
14580
+ return { message: `\u21BA ${result.label} reset to ${result.displayValue}` };
14581
+ }
14582
+ const spaceIdx = query.indexOf(" ");
14583
+ if (spaceIdx > 0) {
14584
+ const rowName = query.slice(0, spaceIdx);
14585
+ const valueStr = query.slice(spaceIdx + 1).trim();
14586
+ const field2 = settingsPickerJumpByName(rowName);
14587
+ if (field2 === void 0) {
14588
+ return {
14589
+ message: `Unknown settings row "${rowName}".
14590
+ Available chords:
14591
+ ${settingsPickerJumpNames().join("\n ")}`
14592
+ };
14593
+ }
14594
+ if (valueStr === "") {
14595
+ dispatch({ type: "settingsFieldSet", field: field2 });
14596
+ openSettings();
14597
+ return { message: void 0 };
14598
+ }
14599
+ const result = resolveSettingsFieldValue(field2, valueStr);
14600
+ if (!result.ok) {
14601
+ return { message: result.error };
14602
+ }
14603
+ dispatch({ type: "settingsValueSet", patch: result.patch });
14604
+ const cur = getSettings ? getSettings() : void 0;
14605
+ if (cur && saveSettings) {
14606
+ const { tokenSavingTier, ...rest } = result.patch;
14607
+ const updated = {
14608
+ ...cur,
14609
+ ...rest,
14610
+ ...tokenSavingTier !== void 0 ? { featureTokenSaving: tokenSavingTier } : {}
14611
+ };
14612
+ Promise.resolve(saveSettings(updated)).then((err) => {
14613
+ if (err) dispatch({ type: "settingsHint", text: err });
14614
+ });
14615
+ }
14616
+ return { message: `\u2713 ${result.label} \u2192 ${result.displayValue}` };
14617
+ }
14618
+ const field = settingsPickerJumpByName(query);
14619
+ if (field === void 0) {
14620
+ return {
14621
+ message: `Unknown settings row "${query}".
14622
+ Available chords:
14623
+ ${settingsPickerJumpNames().join("\n ")}`
14624
+ };
14625
+ }
14626
+ dispatch({ type: "settingsFieldSet", field });
13447
14627
  openSettings();
13448
14628
  return { message: void 0 };
13449
14629
  }
@@ -13452,7 +14632,39 @@ function App({
13452
14632
  return () => {
13453
14633
  slashRegistry.unregister("settings");
13454
14634
  };
13455
- }, [slashRegistry, getSettings, saveSettings, openSettings]);
14635
+ }, [slashRegistry, getSettings, saveSettings, openSettings, dispatch]);
14636
+ useEffect(() => {
14637
+ const cmd = {
14638
+ name: "settings-get",
14639
+ aliases: ["config-get", "get"],
14640
+ description: "Read a setting value without opening the picker.",
14641
+ argsHint: "<chord>",
14642
+ help: 'Show the current value of a setting.\n\nExamples:\n /settings-get yolo \u2192 "YOLO mode: off"\n /settings-get multi-diff \u2192 "Multi-diff summary: 5"\n /settings-get log-level \u2192 "Log level: info"\n\nAvailable chords:\n ' + settingsPickerJumpNames().join("\n "),
14643
+ async run(args) {
14644
+ const query = args.trim();
14645
+ if (query === "") {
14646
+ return { message: formatAllSettingsSummary(state.settingsPicker) };
14647
+ }
14648
+ const field = settingsPickerJumpByName(query);
14649
+ if (field === void 0) {
14650
+ return {
14651
+ message: `Unknown settings row "${query}".
14652
+ Available chords:
14653
+ ${settingsPickerJumpNames().join("\n ")}`
14654
+ };
14655
+ }
14656
+ const result = getSettingsFieldValue(state.settingsPicker, field);
14657
+ if (!result.ok) {
14658
+ return { message: result.error };
14659
+ }
14660
+ return { message: `${result.label}: ${result.displayValue}` };
14661
+ }
14662
+ };
14663
+ slashRegistry.register(cmd, "tui", { official: true });
14664
+ return () => {
14665
+ slashRegistry.unregister("settings-get");
14666
+ };
14667
+ }, [slashRegistry, state.settingsPicker]);
13456
14668
  useEffect(() => {
13457
14669
  const cmd = {
13458
14670
  name: "statusline",
@@ -14207,6 +15419,29 @@ function App({
14207
15419
  return;
14208
15420
  }
14209
15421
  if (state.settingsPicker.open) {
15422
+ const sp = state.settingsPicker;
15423
+ if (sp.thinkingWordEditing) {
15424
+ if (key.escape) {
15425
+ dispatch({ type: "settingsThinkingEditCancel" });
15426
+ return;
15427
+ }
15428
+ if (isEnter) {
15429
+ const now = Date.now();
15430
+ if (now - lastEnterAtRef.current < 50) return;
15431
+ lastEnterAtRef.current = now;
15432
+ dispatch({ type: "settingsThinkingEditCommit" });
15433
+ return;
15434
+ }
15435
+ if (key.backspace) {
15436
+ dispatch({ type: "settingsThinkingEditChange", draft: sp.thinkingWordDraft.slice(0, -1) });
15437
+ return;
15438
+ }
15439
+ if (input && input.length === 1 && input.charCodeAt(0) >= 32 && input.charCodeAt(0) < 127) {
15440
+ dispatch({ type: "settingsThinkingEditChange", draft: sp.thinkingWordDraft + input });
15441
+ return;
15442
+ }
15443
+ return;
15444
+ }
14210
15445
  if (key.escape || key.ctrl && input === "s") {
14211
15446
  dispatch({ type: "settingsClose" });
14212
15447
  return;
@@ -14215,6 +15450,33 @@ function App({
14215
15450
  dispatch({ type: "settingsFieldMove", delta: key.mouse.wheel > 0 ? -1 : 1 });
14216
15451
  return;
14217
15452
  }
15453
+ if (input && input.length === 1 && (key.ctrl || key.meta)) {
15454
+ const mod = key.ctrl ? "ctrl" : key.shift ? "alt-shift" : "alt";
15455
+ const field = settingsPickerJumpField(mod, input);
15456
+ if (field !== void 0) {
15457
+ dispatch({ type: "settingsFieldSet", field });
15458
+ return;
15459
+ }
15460
+ }
15461
+ if (input === "/" && sp.filter === "") {
15462
+ dispatch({ type: "settingsFilterSet", filter: "/" });
15463
+ return;
15464
+ }
15465
+ if (sp.filter !== "") {
15466
+ if (key.escape) {
15467
+ dispatch({ type: "settingsFilterSet", filter: "" });
15468
+ return;
15469
+ }
15470
+ if (key.backspace) {
15471
+ const next2 = sp.filter.length > 1 ? sp.filter.slice(0, -1) : "";
15472
+ dispatch({ type: "settingsFilterSet", filter: next2 });
15473
+ return;
15474
+ }
15475
+ if (input && input.length === 1 && input.charCodeAt(0) >= 32 && input.charCodeAt(0) < 127) {
15476
+ dispatch({ type: "settingsFilterSet", filter: sp.filter + input });
15477
+ return;
15478
+ }
15479
+ }
14218
15480
  if (key.upArrow) {
14219
15481
  dispatch({ type: "settingsFieldMove", delta: -1 });
14220
15482
  return;
@@ -14235,7 +15497,15 @@ function App({
14235
15497
  const now = Date.now();
14236
15498
  if (now - lastEnterAtRef.current < 50) return;
14237
15499
  lastEnterAtRef.current = now;
14238
- dispatch({ type: "settingsValueChange", delta: 1 });
15500
+ if (sp.filter !== "") {
15501
+ dispatch({ type: "settingsFilterSet", filter: "" });
15502
+ return;
15503
+ }
15504
+ if (sp.field === THINKING_WORD_FIELD) {
15505
+ dispatch({ type: "settingsThinkingEditStart" });
15506
+ } else {
15507
+ dispatch({ type: "settingsValueChange", delta: 1 });
15508
+ }
14239
15509
  return;
14240
15510
  }
14241
15511
  return;
@@ -14853,6 +16123,8 @@ function App({
14853
16123
  logLevel: cfg.logLevel ?? "info",
14854
16124
  auditLevel: cfg.auditLevel ?? "standard",
14855
16125
  indexOnStart: cfg.indexOnStart ?? true,
16126
+ multiDiffSummaryThreshold: cfg.multiDiffSummaryThreshold ?? 5,
16127
+ lastSettingsField: cfg.lastSettingsField ?? 0,
14856
16128
  maxIterations: cfg.maxIterations ?? 500,
14857
16129
  autoProceedMaxIterations: cfg.autoProceedMaxIterations ?? 50,
14858
16130
  enhanceDelayMs: cfg.enhanceDelayMs ?? 6e4,
@@ -15124,7 +16396,7 @@ function App({
15124
16396
  // Must match the rendered state label width — while streaming the bar
15125
16397
  // shows the configured thinking word, so the span shifts with it.
15126
16398
  // Omitting this made the model chip un-clickable mid-stream.
15127
- thinkingWord: liveThinkingWord,
16399
+ thinkingWord: displayThinkingWordRef.current,
15128
16400
  model: `${liveProvider}/${liveModel}`
15129
16401
  });
15130
16402
  if (inSpan(span)) {
@@ -15732,10 +17004,12 @@ User message:
15732
17004
  if (autonomyLive === "auto" && nextStepsAutoSubmitTimerRef.current != null) {
15733
17005
  switchAutonomy?.("off");
15734
17006
  }
15735
- dispatch({
15736
- type: "addEntry",
15737
- entry: { kind: "user", text: displayText, queued: true, pasteContent }
15738
- });
17007
+ if (!steering) {
17008
+ dispatch({
17009
+ type: "addEntry",
17010
+ entry: { kind: "user", text: displayText, queued: true, pasteContent }
17011
+ });
17012
+ }
15739
17013
  dispatch({ type: "enqueue", item: { displayText, blocks } });
15740
17014
  return;
15741
17015
  }
@@ -15808,7 +17082,7 @@ User message:
15808
17082
  onMeasure: (totalLines) => dispatch({ type: "setMeasuredLines", totalLines }),
15809
17083
  setSuggestions,
15810
17084
  autonomyMode: autonomyLive,
15811
- autoSubmitCountdown: nextStepsAutoSubmitCountdown
17085
+ multiDiffSummaryThreshold: state.settingsPicker.multiDiffSummaryThreshold
15812
17086
  }
15813
17087
  ) : /* @__PURE__ */ jsx(
15814
17088
  History,
@@ -15819,7 +17093,7 @@ User message:
15819
17093
  toolStream: state.toolStream,
15820
17094
  setSuggestions,
15821
17095
  autonomyMode: autonomyLive,
15822
- autoSubmitCountdown: nextStepsAutoSubmitCountdown
17096
+ multiDiffSummaryThreshold: state.settingsPicker.multiDiffSummaryThreshold
15823
17097
  }
15824
17098
  ),
15825
17099
  /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexShrink: 0, ref: bottomRegionRef, children: [
@@ -15909,7 +17183,10 @@ User message:
15909
17183
  logLevel: state.settingsPicker.logLevel,
15910
17184
  auditLevel: state.settingsPicker.auditLevel,
15911
17185
  indexOnStart: state.settingsPicker.indexOnStart,
17186
+ multiDiffSummaryThreshold: state.settingsPicker.multiDiffSummaryThreshold,
15912
17187
  thinkingWord: state.settingsPicker.thinkingWord,
17188
+ thinkingWordEditing: state.settingsPicker.thinkingWordEditing,
17189
+ thinkingWordDraft: state.settingsPicker.thinkingWordDraft,
15913
17190
  maxIterations: state.settingsPicker.maxIterations,
15914
17191
  autoProceedMaxIterations: state.settingsPicker.autoProceedMaxIterations,
15915
17192
  enhanceDelayMs: state.settingsPicker.enhanceDelayMs,
@@ -15922,6 +17199,7 @@ User message:
15922
17199
  reasoningPreserve: state.settingsPicker.reasoningPreserve,
15923
17200
  cacheTtl: state.settingsPicker.cacheTtl,
15924
17201
  configScope: state.settingsPicker.configScope,
17202
+ filter: state.settingsPicker.filter,
15925
17203
  hint: state.settingsPicker.hint
15926
17204
  }
15927
17205
  ) : null,
@@ -16080,7 +17358,7 @@ User message:
16080
17358
  model: `${liveProvider}/${liveModel}`,
16081
17359
  version: appVersion,
16082
17360
  state: state.status,
16083
- thinkingWord: liveThinkingWord,
17361
+ thinkingWord: displayThinkingWord,
16084
17362
  tokenCounter,
16085
17363
  hint: renderRunningTools(state.runningTools) || state.hint,
16086
17364
  queueCount: state.queue.length,