steward-cli 0.15.0 → 0.17.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/cli.js CHANGED
@@ -42222,6 +42222,40 @@ function stringWidth(input, options = {}) {
42222
42222
  // src/tui/engine/cell-layout.ts
42223
42223
  import { appendFileSync } from "node:fs";
42224
42224
 
42225
+ // src/theme/figures.ts
42226
+ var figures = {
42227
+ blackCircle: "●",
42228
+ bullet: "·",
42229
+ bulletSmall: "∙",
42230
+ teardropAsterisk: "✻",
42231
+ asterisk: "*",
42232
+ threeDots: "∴",
42233
+ treeBranch: "└",
42234
+ treeVertical: "│",
42235
+ pointer: ">",
42236
+ pointerBold: "❯",
42237
+ pointerSmall: "›",
42238
+ tick: "✔",
42239
+ cross: "✖",
42240
+ warning: "⚠",
42241
+ info: "ℹ",
42242
+ ellipsis: "…",
42243
+ arrowUp: "↑",
42244
+ arrowDown: "↓",
42245
+ arrowLeft: "←",
42246
+ arrowRight: "→",
42247
+ blockquoteBar: "▎",
42248
+ horizontalLine: "─",
42249
+ heavyHorizontal: "━",
42250
+ dashedLine: "╌",
42251
+ effortLow: "○",
42252
+ effortMedium: "◐",
42253
+ effortHigh: "●",
42254
+ effortMax: "◉",
42255
+ sliderPointer: "▲",
42256
+ sliderDivider: "┆",
42257
+ spinnerFrames: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
42258
+ };
42225
42259
  // node_modules/chalk/source/utilities.js
42226
42260
  function stringReplaceAll(string, substring, postfix) {
42227
42261
  let index = string.indexOf(substring);
@@ -42770,80 +42804,420 @@ var chalk = createChalk();
42770
42804
  var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
42771
42805
  var source_default = chalk;
42772
42806
 
42773
- // src/theme/figures.ts
42774
- import { platform } from "node:os";
42775
- var isDarwin = platform() === "darwin";
42776
- var figures = {
42777
- blackCircle: "●",
42778
- bullet: "·",
42779
- bulletSmall: "∙",
42780
- teardropAsterisk: "✻",
42781
- asterisk: "*",
42782
- threeDots: "∴",
42783
- treeBranch: "└",
42784
- treeVertical: "│",
42785
- pointer: ">",
42786
- pointerBold: "❯",
42787
- pointerSmall: "›",
42788
- tick: "✔",
42789
- cross: "✖",
42790
- warning: "⚠",
42791
- info: "ℹ",
42792
- ellipsis: "…",
42793
- arrowUp: "↑",
42794
- arrowDown: "↓",
42795
- arrowLeft: "←",
42796
- arrowRight: "→",
42797
- blockquoteBar: "▎",
42798
- horizontalLine: "─",
42799
- heavyHorizontal: "━",
42800
- dashedLine: "╌",
42801
- effortLow: "○",
42802
- effortMedium: "◐",
42803
- effortHigh: "●",
42804
- effortMax: "◉",
42805
- sliderPointer: "▲",
42806
- sliderDivider: "┆",
42807
- spinnerFrames: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
42808
- };
42807
+ // src/theme/apply.ts
42808
+ function resolveThemeColor(color, isBg = false) {
42809
+ if (!color || color === "default" || color === "none" || color === "transparent" || color === "ansi(reset)" || color === "ansi(default)") {
42810
+ return (s) => s;
42811
+ }
42812
+ if (color === "dim" || color === "ansi(dim)") {
42813
+ return isBg ? (s) => s : (s) => source_default.dim(s);
42814
+ }
42815
+ if (color.startsWith("ansi(") && color.endsWith(")")) {
42816
+ const name = color.slice(5, -1).trim();
42817
+ if (name === "reset" || name === "none" || name === "transparent" || name === "default") {
42818
+ return (s) => s;
42819
+ }
42820
+ if (name === "dim") {
42821
+ return isBg ? (s) => s : (s) => source_default.dim(s);
42822
+ }
42823
+ if (isBg && !name.startsWith("bg")) {
42824
+ const bgName = `bg${name.charAt(0).toUpperCase()}${name.slice(1)}`;
42825
+ const fn = source_default[bgName];
42826
+ if (typeof fn === "function")
42827
+ return fn;
42828
+ }
42829
+ const fn = source_default[name];
42830
+ if (typeof fn === "function")
42831
+ return fn;
42832
+ return (s) => s;
42833
+ }
42834
+ if (color.startsWith("rgb(")) {
42835
+ const match = color.match(/\d+/g);
42836
+ if (match && match.length >= 3) {
42837
+ const [r, g, b] = [Number(match[0]), Number(match[1]), Number(match[2])];
42838
+ return isBg ? source_default.bgRgb(r, g, b) : source_default.rgb(r, g, b);
42839
+ }
42840
+ }
42841
+ if (color.startsWith("#")) {
42842
+ return isBg ? source_default.bgHex(color) : source_default.hex(color);
42843
+ }
42844
+ return (s) => s;
42845
+ }
42846
+
42847
+ // src/theme/style.ts
42848
+ var _themeVersion = 0;
42849
+ function themeVersion() {
42850
+ return _themeVersion;
42851
+ }
42852
+ function bumpThemeVersion() {
42853
+ _themeVersion++;
42854
+ }
42855
+ var colorCache = new Map;
42856
+ var cachedVersion = -1;
42857
+ function getCachedColor(token, isBg) {
42858
+ if (source_default.level === 0) {
42859
+ return (s) => s;
42860
+ }
42861
+ const v = _themeVersion;
42862
+ if (v !== cachedVersion) {
42863
+ colorCache.clear();
42864
+ cachedVersion = v;
42865
+ }
42866
+ const key = `${isBg ? "bg:" : "fg:"}${token}`;
42867
+ let fn = colorCache.get(key);
42868
+ if (!fn) {
42869
+ const theme = getTheme();
42870
+ const colorStr = theme[token];
42871
+ fn = resolveThemeColor(colorStr ?? (isBg ? "default" : "default"), isBg);
42872
+ colorCache.set(key, fn);
42873
+ }
42874
+ return fn;
42875
+ }
42876
+ var c = new Proxy({}, {
42877
+ get(_target, prop) {
42878
+ return (s) => getCachedColor(prop, false)(s);
42879
+ }
42880
+ });
42881
+ var bg = new Proxy({}, {
42882
+ get(_target, prop) {
42883
+ return (s) => getCachedColor(prop, true)(s);
42884
+ }
42885
+ });
42886
+ var bold = (s) => source_default.level === 0 ? s : source_default.bold(s);
42887
+ var italic = (s) => source_default.level === 0 ? s : source_default.italic(s);
42888
+ var underline = (s) => source_default.level === 0 ? s : source_default.underline(s);
42889
+ var strikethrough = (s) => source_default.level === 0 ? s : source_default.strikethrough(s);
42890
+
42809
42891
  // src/theme/colors.ts
42810
42892
  var darkTheme = {
42893
+ text: "rgb(255,255,255)",
42894
+ muted: "rgb(110,110,110)",
42895
+ subtle: "rgb(80,80,80)",
42811
42896
  brand: "rgb(215,119,87)",
42812
- brandShimmer: "rgb(235,159,127)",
42813
- permission: "rgb(177,185,249)",
42814
- permissionDim: "rgb(93,100,128)",
42815
- permissionShimmer: "rgb(207,215,255)",
42816
- lavenderHeader: "rgb(129,136,165)",
42817
- lavenderLight: "rgb(183,184,228)",
42818
- bashPink: "rgb(253,93,177)",
42897
+ info: "rgb(123,165,218)",
42819
42898
  success: "rgb(78,186,101)",
42820
- error: "rgb(255,107,128)",
42821
42899
  warning: "rgb(255,193,7)",
42822
- info: "rgb(123,165,218)",
42823
- bulletSuccess: "rgb(75,185,99)",
42824
- bulletError: "rgb(255,107,128)",
42825
- bulletRunning: "rgb(255,255,255)",
42826
- text: "rgb(255,255,255)",
42827
- textMuted: "rgb(110,110,110)",
42828
- subtle: "rgb(80,80,80)",
42829
- inactive: "rgb(153,153,153)",
42900
+ error: "rgb(255,107,128)",
42901
+ permission: "rgb(177,185,249)",
42902
+ selected: "rgb(177,185,249)",
42903
+ current: "rgb(255,193,7)",
42830
42904
  promptBorder: "rgb(136,136,136)",
42831
- userCardBg: "rgb(55,55,55)",
42905
+ rule: "rgb(129,136,165)",
42832
42906
  userChevron: "rgb(82,82,82)",
42833
- toolHeaderBg: "rgb(35,35,40)",
42834
- diffAddBG: "rgb(19,54,14)",
42835
- diffAddFG: "rgb(126,231,135)",
42836
- diffDeleteBG: "rgb(54,5,9)",
42837
- diffDeleteFG: "rgb(255,123,114)",
42838
- dividerRule: "rgb(129,136,165)",
42839
- dashedRule: "rgb(51,51,51)"
42907
+ userBg: "rgb(55,55,55)",
42908
+ diffAddBg: "rgb(19,54,14)",
42909
+ diffDelBg: "rgb(54,5,9)",
42910
+ diffAddFg: "rgb(126,231,135)",
42911
+ diffDelFg: "rgb(255,123,114)",
42912
+ syntax: {
42913
+ keyword: "#ff7b72",
42914
+ string: "#a5d6ff",
42915
+ number: "#79c0ff",
42916
+ literal: "#79c0ff",
42917
+ comment: "#8b949e",
42918
+ function: "#d2a8ff",
42919
+ type: "#ffa657",
42920
+ variable: "#ffa657",
42921
+ tag: "#7ee787",
42922
+ attr: "#79c0ff",
42923
+ meta: "#79c0ff",
42924
+ addition: "#7ee787",
42925
+ deletion: "#ffa198",
42926
+ default: "#c9d1d9"
42927
+ }
42840
42928
  };
42929
+ var lightTheme = {
42930
+ text: "default",
42931
+ muted: "rgb(120,120,120)",
42932
+ subtle: "rgb(175,175,175)",
42933
+ brand: "rgb(215,119,87)",
42934
+ info: "rgb(40,100,180)",
42935
+ success: "rgb(44,122,57)",
42936
+ warning: "rgb(150,108,30)",
42937
+ error: "rgb(171,43,63)",
42938
+ permission: "rgb(87,105,247)",
42939
+ selected: "rgb(87,105,247)",
42940
+ current: "rgb(150,108,30)",
42941
+ promptBorder: "rgb(153,153,153)",
42942
+ rule: "rgb(120,130,160)",
42943
+ userChevron: "rgb(160,160,160)",
42944
+ userBg: "rgb(240,240,240)",
42945
+ diffAddBg: "rgb(220,245,220)",
42946
+ diffDelBg: "rgb(255,230,230)",
42947
+ diffAddFg: "rgb(30,120,40)",
42948
+ diffDelFg: "rgb(180,40,40)",
42949
+ syntax: {
42950
+ keyword: "#d73a49",
42951
+ string: "#032f62",
42952
+ number: "#005cc5",
42953
+ literal: "#005cc5",
42954
+ comment: "#6a737d",
42955
+ function: "#6f42c1",
42956
+ type: "#e36209",
42957
+ variable: "#e36209",
42958
+ tag: "#22863a",
42959
+ attr: "#005cc5",
42960
+ meta: "#005cc5",
42961
+ addition: "#22863a",
42962
+ deletion: "#b31d28",
42963
+ default: "default"
42964
+ }
42965
+ };
42966
+ var draculaTheme = {
42967
+ text: "rgb(248,248,242)",
42968
+ muted: "rgb(98,114,164)",
42969
+ subtle: "rgb(68,71,90)",
42970
+ brand: "rgb(255,184,108)",
42971
+ info: "rgb(139,233,253)",
42972
+ success: "rgb(80,250,123)",
42973
+ warning: "rgb(255,184,108)",
42974
+ error: "rgb(255,85,85)",
42975
+ permission: "rgb(189,147,249)",
42976
+ selected: "rgb(189,147,249)",
42977
+ current: "rgb(255,184,108)",
42978
+ promptBorder: "rgb(98,114,164)",
42979
+ rule: "rgb(189,147,249)",
42980
+ userChevron: "rgb(98,114,164)",
42981
+ userBg: "rgb(68,71,90)",
42982
+ diffAddBg: "rgb(12,38,18)",
42983
+ diffDelBg: "rgb(45,15,15)",
42984
+ diffAddFg: "rgb(80,250,123)",
42985
+ diffDelFg: "rgb(255,85,85)",
42986
+ syntax: {
42987
+ keyword: "rgb(255,121,198)",
42988
+ string: "rgb(241,250,140)",
42989
+ number: "rgb(189,147,249)",
42990
+ literal: "rgb(189,147,249)",
42991
+ comment: "rgb(98,114,164)",
42992
+ function: "rgb(80,250,123)",
42993
+ type: "rgb(139,233,253)",
42994
+ variable: "rgb(255,184,108)",
42995
+ tag: "rgb(255,121,198)",
42996
+ attr: "rgb(80,250,123)",
42997
+ meta: "rgb(255,121,198)",
42998
+ addition: "rgb(80,250,123)",
42999
+ deletion: "rgb(255,85,85)",
43000
+ default: "rgb(248,248,242)"
43001
+ }
43002
+ };
43003
+ var darkAnsiTheme = {
43004
+ text: "default",
43005
+ muted: "dim",
43006
+ subtle: "dim",
43007
+ brand: "ansi(yellow)",
43008
+ info: "ansi(cyan)",
43009
+ success: "ansi(green)",
43010
+ warning: "ansi(yellow)",
43011
+ error: "ansi(red)",
43012
+ permission: "ansi(blue)",
43013
+ selected: "ansi(blue)",
43014
+ current: "ansi(yellow)",
43015
+ promptBorder: "dim",
43016
+ rule: "dim",
43017
+ userChevron: "dim",
43018
+ userBg: "ansi(bgBlackBright)",
43019
+ diffAddBg: "default",
43020
+ diffDelBg: "default",
43021
+ diffAddFg: "ansi(green)",
43022
+ diffDelFg: "ansi(red)",
43023
+ syntax: {
43024
+ keyword: "ansi(red)",
43025
+ string: "ansi(cyan)",
43026
+ number: "ansi(cyan)",
43027
+ literal: "ansi(cyan)",
43028
+ comment: "ansi(gray)",
43029
+ function: "ansi(magenta)",
43030
+ type: "ansi(yellow)",
43031
+ variable: "ansi(yellow)",
43032
+ tag: "ansi(green)",
43033
+ attr: "ansi(cyan)",
43034
+ meta: "ansi(blue)",
43035
+ addition: "ansi(green)",
43036
+ deletion: "ansi(red)",
43037
+ default: "ansi(white)"
43038
+ }
43039
+ };
43040
+ var lightAnsiTheme = {
43041
+ text: "default",
43042
+ muted: "dim",
43043
+ subtle: "dim",
43044
+ brand: "ansi(yellow)",
43045
+ info: "ansi(blue)",
43046
+ success: "ansi(green)",
43047
+ warning: "ansi(yellow)",
43048
+ error: "ansi(red)",
43049
+ permission: "ansi(blue)",
43050
+ selected: "ansi(blue)",
43051
+ current: "ansi(yellow)",
43052
+ promptBorder: "dim",
43053
+ rule: "dim",
43054
+ userChevron: "dim",
43055
+ userBg: "ansi(bgWhiteBright)",
43056
+ diffAddBg: "default",
43057
+ diffDelBg: "default",
43058
+ diffAddFg: "ansi(green)",
43059
+ diffDelFg: "ansi(red)",
43060
+ syntax: {
43061
+ keyword: "ansi(red)",
43062
+ string: "ansi(blue)",
43063
+ number: "ansi(blue)",
43064
+ literal: "ansi(blue)",
43065
+ comment: "ansi(gray)",
43066
+ function: "ansi(magenta)",
43067
+ type: "ansi(yellow)",
43068
+ variable: "ansi(yellow)",
43069
+ tag: "ansi(green)",
43070
+ attr: "ansi(blue)",
43071
+ meta: "ansi(blue)",
43072
+ addition: "ansi(green)",
43073
+ deletion: "ansi(red)",
43074
+ default: "default"
43075
+ }
43076
+ };
43077
+ var builtInThemes = [
43078
+ {
43079
+ name: "dark",
43080
+ label: "Dark (Default)",
43081
+ description: "Default dark theme with terracotta and purple accents",
43082
+ theme: darkTheme,
43083
+ source: "built-in"
43084
+ },
43085
+ {
43086
+ name: "light",
43087
+ label: "Light",
43088
+ description: "Clean light palette optimized for light backgrounds",
43089
+ theme: lightTheme,
43090
+ source: "built-in"
43091
+ },
43092
+ {
43093
+ name: "dracula",
43094
+ label: "Dracula",
43095
+ description: "Official gothic-inspired vampire color scheme",
43096
+ theme: draculaTheme,
43097
+ source: "built-in"
43098
+ },
43099
+ {
43100
+ name: "dark-ansi",
43101
+ label: "Dark ANSI",
43102
+ description: "16-color ANSI palette respecting your terminal theme",
43103
+ theme: darkAnsiTheme,
43104
+ source: "built-in"
43105
+ },
43106
+ {
43107
+ name: "light-ansi",
43108
+ label: "Light ANSI",
43109
+ description: "16-color ANSI light palette respecting terminal colors",
43110
+ theme: lightAnsiTheme,
43111
+ source: "built-in"
43112
+ }
43113
+ ];
43114
+ var registry = new Map;
43115
+ for (const t of builtInThemes) {
43116
+ registry.set(t.name, t);
43117
+ }
43118
+ var activeThemeId = "dark";
43119
+ function listThemes() {
43120
+ return Array.from(registry.values());
43121
+ }
43122
+ function findTheme(query) {
43123
+ const q = query.trim().toLowerCase().replace(/^["']|["']$/g, "");
43124
+ if (!q)
43125
+ return;
43126
+ return listThemes().find((t) => t.name.toLowerCase() === q || t.label.toLowerCase() === q || t.label.toLowerCase().startsWith(q));
43127
+ }
43128
+ function setActiveTheme(name) {
43129
+ if (registry.has(name)) {
43130
+ activeThemeId = name;
43131
+ bumpThemeVersion();
43132
+ return true;
43133
+ }
43134
+ activeThemeId = "dark";
43135
+ bumpThemeVersion();
43136
+ return false;
43137
+ }
43138
+ function getActiveThemeName() {
43139
+ return activeThemeId;
43140
+ }
42841
43141
  function getTheme() {
42842
- return darkTheme;
43142
+ return registry.get(activeThemeId)?.theme ?? darkTheme;
42843
43143
  }
42844
43144
  // src/theme/logo.ts
42845
43145
  var LOGO_LINES = [" ▄▄▄▄▄ ", "▀▙███▟▀", " ▝ ▘ "];
42846
- var LOGO_WIDTH = Math.max(...LOGO_LINES.map((line) => stringWidth(line)));
43146
+ // src/theme/syntax.ts
43147
+ function buildHighlightTheme(theme) {
43148
+ const syn = theme.syntax;
43149
+ const kw = resolveThemeColor(syn.keyword);
43150
+ const str = resolveThemeColor(syn.string);
43151
+ const num = resolveThemeColor(syn.number);
43152
+ const lit = resolveThemeColor(syn.literal);
43153
+ const com = resolveThemeColor(syn.comment);
43154
+ const fn = resolveThemeColor(syn.function);
43155
+ const typ = resolveThemeColor(syn.type);
43156
+ const vr = resolveThemeColor(syn.variable);
43157
+ const tg = resolveThemeColor(syn.tag);
43158
+ const at = resolveThemeColor(syn.attr);
43159
+ const mt = resolveThemeColor(syn.meta);
43160
+ const add = resolveThemeColor(syn.addition);
43161
+ const del = resolveThemeColor(syn.deletion);
43162
+ const def = resolveThemeColor(syn.default);
43163
+ return {
43164
+ keyword: kw,
43165
+ built_in: typ,
43166
+ type: typ,
43167
+ literal: lit,
43168
+ number: num,
43169
+ regexp: tg,
43170
+ string: str,
43171
+ subst: def,
43172
+ symbol: tg,
43173
+ class: typ,
43174
+ function: fn,
43175
+ title: fn,
43176
+ params: def,
43177
+ comment: (s) => com(italic(s)),
43178
+ doctag: kw,
43179
+ meta: mt,
43180
+ "meta-keyword": kw,
43181
+ "meta-string": str,
43182
+ section: (s) => mt(bold(s)),
43183
+ tag: tg,
43184
+ name: tg,
43185
+ "builtin-name": typ,
43186
+ attr: at,
43187
+ attribute: at,
43188
+ variable: vr,
43189
+ bullet: vr,
43190
+ code: def,
43191
+ emphasis: (s) => italic(s),
43192
+ strong: (s) => bold(s),
43193
+ formula: tg,
43194
+ link: (s) => str(underline(s)),
43195
+ quote: com,
43196
+ "selector-tag": tg,
43197
+ "selector-id": (s) => mt(bold(s)),
43198
+ "selector-class": at,
43199
+ "selector-attr": at,
43200
+ "selector-pseudo": at,
43201
+ "template-tag": tg,
43202
+ "template-variable": vr,
43203
+ addition: add,
43204
+ deletion: del,
43205
+ default: def
43206
+ };
43207
+ }
43208
+ var cachedHighlightTheme = null;
43209
+ var cachedHighlightVersion = -1;
43210
+ function getHighlightTheme() {
43211
+ if (source_default.level === 0) {
43212
+ return { default: (s) => s };
43213
+ }
43214
+ const v = themeVersion();
43215
+ if (!cachedHighlightTheme || v !== cachedHighlightVersion) {
43216
+ cachedHighlightTheme = buildHighlightTheme(getTheme());
43217
+ cachedHighlightVersion = v;
43218
+ }
43219
+ return cachedHighlightTheme;
43220
+ }
42847
43221
  // node_modules/marked/lib/marked.esm.js
42848
43222
  function A() {
42849
43223
  return { async: false, breaks: false, extensions: null, gfm: true, hooks: null, pedantic: false, renderer: null, silent: false, tokenizer: null, walkTokens: null };
@@ -44221,19 +44595,89 @@ var an = f.parseInline;
44221
44595
  var un = b.parse;
44222
44596
  var pn = x.lex;
44223
44597
 
44224
- // src/utils/markdown.ts
44598
+ // src/utils/highlight.ts
44225
44599
  var import_cli_highlight = __toESM(require_dist(), 1);
44226
- var EOL = `
44227
- `;
44228
- function themeColor(color) {
44229
- if (color.startsWith("rgb(")) {
44230
- const match = color.match(/\d+/g);
44231
- if (match && match.length >= 3) {
44232
- return source_default.rgb(Number(match[0]), Number(match[1]), Number(match[2]));
44600
+ import { extname, basename } from "node:path";
44601
+ var EXTENSION_MAP = {
44602
+ ".ts": "typescript",
44603
+ ".tsx": "typescript",
44604
+ ".js": "javascript",
44605
+ ".jsx": "javascript",
44606
+ ".mjs": "javascript",
44607
+ ".cjs": "javascript",
44608
+ ".json": "json",
44609
+ ".py": "python",
44610
+ ".rb": "ruby",
44611
+ ".go": "go",
44612
+ ".rs": "rust",
44613
+ ".sh": "bash",
44614
+ ".bash": "bash",
44615
+ ".zsh": "bash",
44616
+ ".html": "html",
44617
+ ".htm": "html",
44618
+ ".css": "css",
44619
+ ".scss": "scss",
44620
+ ".less": "less",
44621
+ ".md": "markdown",
44622
+ ".markdown": "markdown",
44623
+ ".yaml": "yaml",
44624
+ ".yml": "yaml",
44625
+ ".toml": "ini",
44626
+ ".sql": "sql",
44627
+ ".c": "c",
44628
+ ".h": "c",
44629
+ ".cpp": "cpp",
44630
+ ".hpp": "cpp",
44631
+ ".cc": "cpp",
44632
+ ".java": "java",
44633
+ ".php": "php",
44634
+ ".xml": "xml",
44635
+ ".svg": "xml"
44636
+ };
44637
+ var FILENAME_MAP = {
44638
+ dockerfile: "dockerfile",
44639
+ makefile: "makefile",
44640
+ cmakelists: "cmake"
44641
+ };
44642
+ function detectLanguageFromPath(filePath) {
44643
+ if (!filePath)
44644
+ return;
44645
+ const name = basename(filePath).toLowerCase();
44646
+ if (FILENAME_MAP[name])
44647
+ return FILENAME_MAP[name];
44648
+ const ext = extname(filePath).toLowerCase();
44649
+ if (ext && EXTENSION_MAP[ext]) {
44650
+ return EXTENSION_MAP[ext];
44651
+ }
44652
+ if (ext && import_cli_highlight.supportsLanguage(ext.slice(1))) {
44653
+ return ext.slice(1);
44654
+ }
44655
+ return;
44656
+ }
44657
+ function highlightCode(code, options) {
44658
+ if (!code)
44659
+ return "";
44660
+ const lang = options?.language ?? detectLanguageFromPath(options?.filePath);
44661
+ try {
44662
+ if (lang && import_cli_highlight.supportsLanguage(lang)) {
44663
+ return import_cli_highlight.highlight(code, {
44664
+ language: lang,
44665
+ ignoreIllegals: true,
44666
+ theme: getHighlightTheme()
44667
+ });
44233
44668
  }
44669
+ return import_cli_highlight.highlight(code, {
44670
+ ignoreIllegals: true,
44671
+ theme: getHighlightTheme()
44672
+ });
44673
+ } catch {
44674
+ return code;
44234
44675
  }
44235
- return source_default.hex(color);
44236
44676
  }
44677
+
44678
+ // src/utils/markdown.ts
44679
+ var EOL = `
44680
+ `;
44237
44681
  var markedConfigured = false;
44238
44682
  function configureMarked() {
44239
44683
  if (markedConfigured)
@@ -44260,53 +44704,47 @@ function padAligned(content, contentWidth, columnWidth, align) {
44260
44704
  return content + " ".repeat(padding);
44261
44705
  }
44262
44706
  function formatToken(token, listDepth = 0, orderedListNumber = null, parent = null) {
44263
- const theme = getTheme();
44264
44707
  switch (token.type) {
44265
44708
  case "blockquote": {
44266
44709
  const inner = (token.tokens ?? []).map((t) => formatToken(t, 0, null, null)).join("");
44267
- const bar = themeColor(theme.subtle)("│");
44268
- return inner.split(EOL).map((line) => stripAnsi(line).trim() ? `${bar} ${source_default.italic(line)}` : line).join(EOL);
44710
+ const bar = c.subtle("│");
44711
+ return inner.split(EOL).map((line) => stripAnsi(line).trim() ? `${bar} ${italic(line)}` : line).join(EOL);
44269
44712
  }
44270
44713
  case "code": {
44271
44714
  const code = token;
44272
- let highlighted = code.text;
44273
- if (code.lang && import_cli_highlight.supportsLanguage(code.lang)) {
44274
- try {
44275
- highlighted = import_cli_highlight.highlight(code.text, { language: code.lang, ignoreIllegals: true });
44276
- } catch {}
44277
- }
44278
- const gutter = themeColor(theme.subtle)("│");
44715
+ const highlighted = highlightCode(code.text, { language: code.lang });
44716
+ const gutter = c.subtle("│");
44279
44717
  const lines = highlighted.split(EOL);
44280
44718
  const formatted = lines.map((line) => ` ${gutter} ${line}`).join(EOL);
44281
44719
  return formatted + EOL + EOL;
44282
44720
  }
44283
44721
  case "codespan": {
44284
- return themeColor(theme.permission)(token.text);
44722
+ return c.permission(token.text);
44285
44723
  }
44286
44724
  case "em": {
44287
- return source_default.italic((token.tokens ?? []).map((t) => formatToken(t, 0, null, parent)).join(""));
44725
+ return italic((token.tokens ?? []).map((t) => formatToken(t, 0, null, parent)).join(""));
44288
44726
  }
44289
44727
  case "strong": {
44290
- return source_default.bold((token.tokens ?? []).map((t) => formatToken(t, 0, null, parent)).join(""));
44728
+ return bold((token.tokens ?? []).map((t) => formatToken(t, 0, null, parent)).join(""));
44291
44729
  }
44292
44730
  case "heading": {
44293
44731
  const heading = token;
44294
44732
  const text = (heading.tokens ?? []).map((t) => formatToken(t, 0, null, null)).join("");
44295
44733
  if (heading.depth === 1) {
44296
- return themeColor(theme.brand).bold.underline(text) + EOL + EOL;
44734
+ return underline(bold(c.brand(text))) + EOL + EOL;
44297
44735
  }
44298
44736
  if (heading.depth === 2) {
44299
- return themeColor(theme.brand).bold(text) + EOL + EOL;
44737
+ return bold(c.brand(text)) + EOL + EOL;
44300
44738
  }
44301
- return source_default.bold(text) + EOL + EOL;
44739
+ return bold(text) + EOL + EOL;
44302
44740
  }
44303
44741
  case "hr": {
44304
- return themeColor(theme.dashedRule)("─".repeat(40)) + EOL + EOL;
44742
+ return c.rule("─".repeat(40)) + EOL + EOL;
44305
44743
  }
44306
44744
  case "link": {
44307
44745
  const link = token;
44308
44746
  const linkText = (link.tokens ?? []).map((t) => formatToken(t, 0, null, link)).join("");
44309
- return linkText ? `${themeColor(theme.info)(linkText)} (${source_default.dim(link.href)})` : link.href;
44747
+ return linkText ? `${c.info(linkText)} (${c.muted(link.href)})` : link.href;
44310
44748
  }
44311
44749
  case "list": {
44312
44750
  const list = token;
@@ -44320,7 +44758,7 @@ function formatToken(token, listDepth = 0, orderedListNumber = null, parent = nu
44320
44758
  const listTokens = item.tokens?.filter((t) => t.type === "list") ?? [];
44321
44759
  const mainContent = nonListTokens.map((t) => formatToken(t, listDepth + 1, orderedListNumber, token)).join("").trim();
44322
44760
  const nestedLists = listTokens.map((t) => formatToken(t, listDepth + 1, null, token)).join("");
44323
- return `${indent}${themeColor(theme.brand)(prefix)} ${mainContent}${EOL}${nestedLists}`;
44761
+ return `${indent}${c.brand(prefix)} ${mainContent}${EOL}${nestedLists}`;
44324
44762
  }
44325
44763
  case "paragraph": {
44326
44764
  return (token.tokens ?? []).map((t) => formatToken(t, 0, null, null)).join("") + EOL + EOL;
@@ -44345,7 +44783,7 @@ function formatToken(token, listDepth = 0, orderedListNumber = null, parent = nu
44345
44783
  const plain = stripAnsi(raw);
44346
44784
  const align = isHeader ? "center" : tableToken.align?.[i] ?? "left";
44347
44785
  const padded = padAligned(raw, stringWidth(plain), w, align);
44348
- return isHeader ? ` ${source_default.bold(padded)} ` : ` ${padded} `;
44786
+ return isHeader ? ` ${bold(padded)} ` : ` ${padded} `;
44349
44787
  });
44350
44788
  return D("│") + parts.join(D("│")) + D("│");
44351
44789
  };
@@ -44358,7 +44796,7 @@ function formatToken(token, listDepth = 0, orderedListNumber = null, parent = nu
44358
44796
  }
44359
44797
  return Math.max(maxWidth, 3);
44360
44798
  });
44361
- const D = themeColor(theme.subtle);
44799
+ const D = (s) => c.subtle(s);
44362
44800
  const hbar = (w) => "─".repeat(w + 2);
44363
44801
  const topBorder = D("┌") + columnWidths.map((w) => D(hbar(w))).join(D("┬")) + D("┐");
44364
44802
  const midBorder = D("├") + columnWidths.map((w) => D(hbar(w))).join(D("┼")) + D("┤");
@@ -44390,23 +44828,11 @@ function applyMarkdown(content) {
44390
44828
  }
44391
44829
 
44392
44830
  // src/tui/utils/format.ts
44393
- function themeColor2(color) {
44394
- if (color.startsWith("rgb(")) {
44395
- const match = color.match(/\d+/g);
44396
- if (match && match.length >= 3) {
44397
- return source_default.rgb(Number(match[0]), Number(match[1]), Number(match[2]));
44398
- }
44399
- }
44400
- return source_default.hex(color);
44831
+ function themeColor(color) {
44832
+ return resolveThemeColor(color, false);
44401
44833
  }
44402
- function themeBgColor(color) {
44403
- if (color.startsWith("rgb(")) {
44404
- const match = color.match(/\d+/g);
44405
- if (match && match.length >= 3) {
44406
- return source_default.bgRgb(Number(match[0]), Number(match[1]), Number(match[2]));
44407
- }
44408
- }
44409
- return source_default.bgHex(color);
44834
+ function visibleWidth(text) {
44835
+ return stringWidth(stripAnsi(text));
44410
44836
  }
44411
44837
  function formatMarkdown(md) {
44412
44838
  return applyMarkdown(md);
@@ -44503,17 +44929,16 @@ function truncateToWidth(styledText, maxWidth) {
44503
44929
  return result.includes("\x1B") && !result.endsWith("\x1B[0m") ? result + "\x1B[0m" : result;
44504
44930
  }
44505
44931
  function getStatusBullet(status, pulse = false) {
44506
- const theme = getTheme();
44507
44932
  if (status === "completed") {
44508
- return themeColor2(theme.success)(figures.blackCircle);
44933
+ return c.success(figures.blackCircle);
44509
44934
  }
44510
44935
  if (status === "failed") {
44511
- return themeColor2(theme.error)(figures.blackCircle);
44936
+ return c.error(figures.blackCircle);
44512
44937
  }
44513
44938
  if (pulse) {
44514
- return source_default.white.bold(figures.blackCircle);
44939
+ return bold(c.text(figures.blackCircle));
44515
44940
  }
44516
- return source_default.dim(figures.blackCircle);
44941
+ return c.muted(figures.blackCircle);
44517
44942
  }
44518
44943
 
44519
44944
  // src/tui/engine/cell-layout.ts
@@ -45210,6 +45635,75 @@ class StateRenderer {
45210
45635
  }
45211
45636
  }
45212
45637
 
45638
+ // src/tui/utils/tool-detail.ts
45639
+ function renderToolDetail(detail) {
45640
+ if (detail.kind === "text") {
45641
+ return detail.text.split(`
45642
+ `).map((l) => c.muted(l)).join(`
45643
+ `);
45644
+ }
45645
+ if (detail.kind === "code") {
45646
+ const highlighted = highlightCode(detail.text, { filePath: detail.filePath });
45647
+ const contentLines = highlighted.split(/\r?\n/);
45648
+ if (contentLines.length === 0 || contentLines.length === 1 && !contentLines[0]) {
45649
+ return "";
45650
+ }
45651
+ const cap = 50;
45652
+ const shown = contentLines.slice(0, cap);
45653
+ const padWidth = String(contentLines.length).length;
45654
+ const lines = shown.map((l, i) => `${c.muted(String(i + 1).padStart(padWidth))} ${l}`);
45655
+ if (contentLines.length > cap) {
45656
+ lines.push(c.muted(` … (${contentLines.length - cap} more lines)`));
45657
+ }
45658
+ return lines.join(`
45659
+ `);
45660
+ }
45661
+ if (detail.kind === "diff") {
45662
+ let maxLineNum = 1;
45663
+ for (const hunk of detail.hunks) {
45664
+ for (const line of hunk.lines) {
45665
+ if (line.oldLineNumber)
45666
+ maxLineNum = Math.max(maxLineNum, line.oldLineNumber);
45667
+ if (line.newLineNumber)
45668
+ maxLineNum = Math.max(maxLineNum, line.newLineNumber);
45669
+ }
45670
+ }
45671
+ const padWidth = Math.max(1, String(maxLineNum).length);
45672
+ const diffLines = [];
45673
+ const cap = 30;
45674
+ let count = 0;
45675
+ let overflow = 0;
45676
+ for (const hunk of detail.hunks) {
45677
+ for (const line of hunk.lines) {
45678
+ if (count >= cap) {
45679
+ overflow++;
45680
+ continue;
45681
+ }
45682
+ count++;
45683
+ if (line.kind === "deletion") {
45684
+ const numStr = String(line.oldLineNumber ?? "").padStart(padWidth, " ");
45685
+ const body = line.spans ? "-" + line.spans.map((s) => s.kind === "deletion" ? bold(s.text) : s.text).join("") : `-${line.text}`;
45686
+ diffLines.push(bg.diffDelBg(c.diffDelFg(`${numStr} ${body}`)));
45687
+ } else if (line.kind === "addition") {
45688
+ const numStr = String(line.newLineNumber ?? "").padStart(padWidth, " ");
45689
+ const body = line.spans ? "+" + line.spans.map((s) => s.kind === "addition" ? bold(s.text) : s.text).join("") : `+${line.text}`;
45690
+ diffLines.push(bg.diffAddBg(c.diffAddFg(`${numStr} ${body}`)));
45691
+ } else {
45692
+ const numStr = String(line.newLineNumber ?? line.oldLineNumber ?? "").padStart(padWidth, " ");
45693
+ const highlightedContext = highlightCode(line.text, { filePath: detail.filePath });
45694
+ diffLines.push(`${c.muted(`${numStr} `)} ${highlightedContext}`);
45695
+ }
45696
+ }
45697
+ }
45698
+ if (overflow > 0) {
45699
+ diffLines.push(c.muted(` … (${overflow} more lines)`));
45700
+ }
45701
+ return diffLines.join(`
45702
+ `);
45703
+ }
45704
+ return "";
45705
+ }
45706
+
45213
45707
  // src/session/logs/store.ts
45214
45708
  import {
45215
45709
  appendFileSync as appendFileSync2,
@@ -45233,7 +45727,6 @@ import {
45233
45727
  readdirSync,
45234
45728
  readFileSync,
45235
45729
  renameSync,
45236
- rmSync,
45237
45730
  unlinkSync,
45238
45731
  writeFileSync,
45239
45732
  writeSync
@@ -48900,10 +49393,10 @@ class $ZodRegistry {
48900
49393
  return this._map.has(schema);
48901
49394
  }
48902
49395
  }
48903
- function registry() {
49396
+ function registry2() {
48904
49397
  return new $ZodRegistry;
48905
49398
  }
48906
- (_a2 = globalThis).__zod_globalRegistry ?? (_a2.__zod_globalRegistry = registry());
49399
+ (_a2 = globalThis).__zod_globalRegistry ?? (_a2.__zod_globalRegistry = registry2());
48907
49400
  var globalRegistry = globalThis.__zod_globalRegistry;
48908
49401
  // node_modules/zod/v4/core/api.js
48909
49402
  function _string(Class, params) {
@@ -52317,9 +52810,7 @@ function buildSessionPresentationProjection(events) {
52317
52810
 
52318
52811
  // src/tui/utils/message-formatter.ts
52319
52812
  function formatUserMessage(content, targetWidth) {
52320
- const theme = getTheme();
52321
52813
  const termCols = typeof targetWidth === "number" && targetWidth > 0 ? targetWidth : process.stdout.columns || 80;
52322
- const bg = themeBgColor(theme.userCardBg);
52323
52814
  const pointer = `${figures.pointerBold} `;
52324
52815
  const prefix = pointer;
52325
52816
  const availableTextWidth = Math.max(10, termCols - 2);
@@ -52333,22 +52824,20 @@ function formatUserMessage(content, targetWidth) {
52333
52824
  for (const segment of wrappedSegments) {
52334
52825
  const p = isFirstRow ? prefix : " ";
52335
52826
  isFirstRow = false;
52336
- const visibleLen = stringWidth(p) + stringWidth(segment);
52827
+ const visibleLen = visibleWidth(p) + visibleWidth(segment);
52337
52828
  const padLen = Math.max(0, termCols - visibleLen);
52338
- const pStyled = themeColor2(theme.userChevron)(p);
52339
- const textStyled = source_default.white(segment);
52340
- const fullRow = bg(`${pStyled}${textStyled}${" ".repeat(padLen)}`);
52829
+ const pStyled = c.userChevron(p);
52830
+ const textStyled = c.text(segment);
52831
+ const fullRow = bg.userBg(`${pStyled}${textStyled}${" ".repeat(padLen)}`);
52341
52832
  lines.push(fullRow);
52342
52833
  }
52343
52834
  }
52344
52835
  return lines;
52345
52836
  }
52346
52837
  function formatSystemMessage(content) {
52347
- const theme = getTheme();
52348
- const infoColor = themeColor2(theme.permission);
52349
52838
  const rawLines = content.split(`
52350
52839
  `);
52351
- return rawLines.map((l, i) => i === 0 ? ` ${source_default.dim("└ ")}${infoColor(l)}` : ` ${infoColor(l)}`);
52840
+ return rawLines.map((l, i) => i === 0 ? ` ${c.muted("└ ")}${c.permission(l)}` : ` ${c.permission(l)}`);
52352
52841
  }
52353
52842
  function formatAssistantMessage(content) {
52354
52843
  const lines = [];
@@ -52373,7 +52862,7 @@ function formatAssistantMessage(content) {
52373
52862
  continue;
52374
52863
  }
52375
52864
  if (i === 0) {
52376
- const bullet = source_default.white(`${figures.blackCircle} `);
52865
+ const bullet = c.text(`${figures.blackCircle} `);
52377
52866
  lines.push(`${bullet}${l}`);
52378
52867
  } else {
52379
52868
  lines.push(` ${l}`);
@@ -52385,7 +52874,6 @@ function formatAssistantMessage(content) {
52385
52874
  }
52386
52875
  function formatToolStatus(options) {
52387
52876
  const { toolName, displayName, icon, argsSummary, status, error, targetWidth } = options;
52388
- const theme = getTheme();
52389
52877
  const fullTermWidth = typeof targetWidth === "number" && targetWidth > 0 ? targetWidth : process.stdout.columns || 80;
52390
52878
  const bullet = getStatusBullet(status);
52391
52879
  const dispName = displayName ?? icon ?? toolName;
@@ -52394,26 +52882,40 @@ function formatToolStatus(options) {
52394
52882
  const truncatedArg = cleanFirstLine.length > maxArgLen ? truncateMiddle(cleanFirstLine, maxArgLen) : cleanFirstLine;
52395
52883
  let mainLine = `${bullet} ${dispName}`;
52396
52884
  if (truncatedArg) {
52397
- mainLine += `${source_default.dim("(")}${source_default.dim(truncatedArg)}${source_default.dim(")")}`;
52885
+ mainLine += `${c.muted("(")}${c.muted(truncatedArg)}${c.muted(")")}`;
52398
52886
  } else {
52399
- mainLine += `${source_default.dim("()")}`;
52887
+ mainLine += `${c.muted("()")}`;
52400
52888
  }
52401
52889
  const lines = [mainLine];
52402
- if (status === "completed" && options.toolOutput) {
52403
- const cleanOutput = options.toolOutput.trim();
52404
- if (cleanOutput) {
52405
- const outputLines = cleanOutput.split(`
52890
+ const summaryVal = options.summary ?? options.toolOutput;
52891
+ if (status === "completed" && summaryVal) {
52892
+ let headline = "";
52893
+ let detailText = "";
52894
+ if (typeof summaryVal === "string") {
52895
+ const outputLines = summaryVal.trim().split(`
52896
+ `);
52897
+ headline = outputLines[0]?.trim() ?? "";
52898
+ detailText = outputLines.slice(1).join(`
52406
52899
  `);
52407
- const summaryText = outputLines[0]?.trim() ?? "";
52900
+ } else {
52901
+ headline = summaryVal.headline;
52902
+ if (summaryVal.detail) {
52903
+ detailText = renderToolDetail(summaryVal.detail);
52904
+ }
52905
+ }
52906
+ if (headline) {
52408
52907
  const maxOutLen = Math.max(10, fullTermWidth - 6);
52409
- const truncatedSummary = summaryText.length > maxOutLen ? `${summaryText.slice(0, maxOutLen - 1)}…` : summaryText;
52410
- lines.push(` ${source_default.dim("└ ")}${source_default.white(truncatedSummary)}`);
52411
- const maxContentWidth = Math.max(30, Math.min(fullTermWidth - 4, Math.floor(fullTermWidth * 0.85)));
52412
- for (let i = 1;i < outputLines.length; i++) {
52413
- const rawLine = outputLines[i] ?? "";
52414
- const prefixed = ` ${rawLine}`;
52415
- const wrapped = wrapVisualLine(prefixed, maxContentWidth, " ");
52416
- lines.push(...wrapped);
52908
+ const truncatedSummary = headline.length > maxOutLen ? `${headline.slice(0, maxOutLen - 1)}…` : headline;
52909
+ lines.push(` ${c.muted("└ ")}${c.text(truncatedSummary)}`);
52910
+ if (detailText) {
52911
+ const detailLines = detailText.split(`
52912
+ `);
52913
+ const maxContentWidth = Math.max(30, Math.min(fullTermWidth - 4, Math.floor(fullTermWidth * 0.85)));
52914
+ for (const rawLine of detailLines) {
52915
+ const prefixed = ` ${rawLine}`;
52916
+ const wrapped = wrapVisualLine(prefixed, maxContentWidth, " ");
52917
+ lines.push(...wrapped);
52918
+ }
52417
52919
  }
52418
52920
  }
52419
52921
  }
@@ -52423,25 +52925,23 @@ function formatToolStatus(options) {
52423
52925
  `)[0]?.trim() || rawError;
52424
52926
  const maxErrLen = Math.max(10, fullTermWidth - 6);
52425
52927
  const truncatedErr = firstLineErr.length > maxErrLen ? `${firstLineErr.slice(0, maxErrLen - 1)}…` : firstLineErr;
52426
- lines.push(` ${source_default.dim("└ ")}${themeColor2(theme.error)(truncatedErr)}`);
52928
+ lines.push(` ${c.muted("└ ")}${c.error(truncatedErr)}`);
52427
52929
  }
52428
52930
  return lines;
52429
52931
  }
52430
52932
  function formatErrorBadge(error, retryInfo) {
52431
- const theme = getTheme();
52432
- const errColor = themeColor2(theme.error);
52433
- const ast = errColor(figures.asterisk);
52434
- const midDot = source_default.dim(` ${figures.bullet} `);
52933
+ const ast = c.error(figures.asterisk);
52934
+ const midDot = c.muted(` ${figures.bullet} `);
52435
52935
  let msg = error.shortMessage;
52436
52936
  if (retryInfo) {
52437
- const retryStr = source_default.dim(`Retrying in ${retryInfo.countdownSec}s · attempt ${retryInfo.attempt}/${retryInfo.maxAttempts}`);
52438
- msg = `${errColor(error.shortMessage)}${midDot}${retryStr}`;
52937
+ const retryStr = c.muted(`Retrying in ${retryInfo.countdownSec}s · attempt ${retryInfo.attempt}/${retryInfo.maxAttempts}`);
52938
+ msg = `${c.error(error.shortMessage)}${midDot}${retryStr}`;
52439
52939
  } else {
52440
- msg = errColor(error.shortMessage);
52940
+ msg = c.error(error.shortMessage);
52441
52941
  }
52442
52942
  const lines = [`${ast} ${msg}`];
52443
52943
  if (error.suggestedAction) {
52444
- lines.push(` ${source_default.dim("└ ")}${source_default.dim(error.suggestedAction)}`);
52944
+ lines.push(` ${c.muted("└ ")}${c.muted(error.suggestedAction)}`);
52445
52945
  }
52446
52946
  return lines;
52447
52947
  }
@@ -52449,8 +52949,8 @@ function formatTurnStatus(durationMs, timestamp = new Date, verb) {
52449
52949
  const selectedVerb = verb ?? chooseTurnStatusVerb();
52450
52950
  const sec = Math.max(1, Math.round(durationMs / 1000));
52451
52951
  const timeStr = timestamp.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
52452
- const midDot = source_default.dim(` ${figures.bullet} `);
52453
- return `${source_default.dim(`${figures.asterisk} ${selectedVerb} for ${sec}s`)}${midDot}${source_default.dim(`done ${timeStr}`)}`;
52952
+ const midDot = c.muted(` ${figures.bullet} `);
52953
+ return `${c.muted(`${figures.asterisk} ${selectedVerb} for ${sec}s`)}${midDot}${c.muted(`done ${timeStr}`)}`;
52454
52954
  }
52455
52955
 
52456
52956
  // src/tui/engine/HistoryLayoutCache.ts
@@ -53347,6 +53847,18 @@ function saveVoiceLanguage(languageTag) {
53347
53847
  voiceLanguage: languageTag.trim()
53348
53848
  });
53349
53849
  }
53850
+ function getSavedTheme() {
53851
+ const settings = loadSettings();
53852
+ if (settings.theme && typeof settings.theme === "string") {
53853
+ return settings.theme;
53854
+ }
53855
+ return;
53856
+ }
53857
+ function saveThemeSelection(name) {
53858
+ saveSettings({
53859
+ theme: name
53860
+ });
53861
+ }
53350
53862
  function normalizeFolderPath(inputPath) {
53351
53863
  let absolute = resolve(inputPath);
53352
53864
  try {
@@ -53387,6 +53899,18 @@ function trustFolder(absolutePath) {
53387
53899
  }
53388
53900
  });
53389
53901
  }
53902
+ function getSavedMode() {
53903
+ const settings = loadSettings();
53904
+ if (settings.mode && typeof settings.mode === "string" && ["normal", "chat", "review", "build"].includes(settings.mode)) {
53905
+ return settings.mode;
53906
+ }
53907
+ return;
53908
+ }
53909
+ function saveModeSelection(name) {
53910
+ saveSettings({
53911
+ mode: name
53912
+ });
53913
+ }
53390
53914
 
53391
53915
  // src/services/checkpoint/path.ts
53392
53916
  function computeWorkspaceHash(workspaceRoot) {
@@ -53616,6 +54140,25 @@ async function saveCheckpointManifest(workspaceHash, sessionId, manifest) {
53616
54140
  atomicWriteJson(targetPath, manifest);
53617
54141
  });
53618
54142
  }
54143
+ function loadPendingJournal(workspaceHash, sessionId) {
54144
+ const targetPath = getPendingJournalPath(workspaceHash, sessionId);
54145
+ if (!existsSync6(targetPath)) {
54146
+ return null;
54147
+ }
54148
+ try {
54149
+ const raw = readFileSync5(targetPath, "utf-8");
54150
+ const parsed = JSON.parse(raw);
54151
+ const result = PendingJournalSchema.safeParse(parsed);
54152
+ if (result.success) {
54153
+ return result.data;
54154
+ }
54155
+ quarantineCheckpointFile(targetPath);
54156
+ return null;
54157
+ } catch {
54158
+ quarantineCheckpointFile(targetPath);
54159
+ return null;
54160
+ }
54161
+ }
53619
54162
  async function savePendingJournal(workspaceHash, sessionId, journal) {
53620
54163
  const key = `${workspaceHash}:${sessionId}`;
53621
54164
  await storeSerializer.run(key, () => {
@@ -53805,7 +54348,7 @@ class MutationCheckpointTracker {
53805
54348
  };
53806
54349
  await savePendingJournal(this.workspaceHash, this.sessionId, journal);
53807
54350
  }
53808
- async commitTurn(turnId, turnStatus = "complete") {
54351
+ async commitTurn(turnId, _turnStatus = "complete") {
53809
54352
  if (this.activeTurnId !== turnId) {
53810
54353
  return;
53811
54354
  }
@@ -54088,6 +54631,30 @@ async function executeRewind(options) {
54088
54631
  releaseLocks();
54089
54632
  }
54090
54633
  }
54634
+ async function recoverPendingCheckpoint(workspaceRoot, sessionId) {
54635
+ const workspaceHash = computeWorkspaceHash(workspaceRoot);
54636
+ const journal = loadPendingJournal(workspaceHash, sessionId);
54637
+ if (!journal)
54638
+ return;
54639
+ if (journal.kind === "rewind") {
54640
+ if (journal.phase === "files-restored" || journal.phase === "files-pending") {
54641
+ for (const file of journal.files) {
54642
+ if (file.post) {
54643
+ const absPath = resolve3(workspaceRoot, file.relativePath);
54644
+ try {
54645
+ restoreFileStateToDisk(workspaceHash, absPath, file.post);
54646
+ } catch {}
54647
+ }
54648
+ }
54649
+ }
54650
+ await deletePendingJournal(workspaceHash, sessionId);
54651
+ } else {
54652
+ const committedFiles = journal.files.filter((f) => f.mutationCommitted);
54653
+ if (committedFiles.length === 0) {
54654
+ await deletePendingJournal(workspaceHash, sessionId);
54655
+ }
54656
+ }
54657
+ }
54091
54658
  // node_modules/@ai-sdk/provider/dist/index.js
54092
54659
  var marker = "vercel.ai.error";
54093
54660
  var symbol = Symbol.for(marker);
@@ -70934,6 +71501,72 @@ var originalGenerateCallId6 = createIdGenerator({
70934
71501
  });
70935
71502
  var defaultDownload2 = createDownload();
70936
71503
 
71504
+ // src/engine/mode.ts
71505
+ var MODES = {
71506
+ normal: {
71507
+ name: "normal",
71508
+ label: "Normal",
71509
+ description: "All tools with file and bash permission prompts",
71510
+ color: "text",
71511
+ allows: ["read", "write", "exec"],
71512
+ autoApproveFiles: false
71513
+ },
71514
+ chat: {
71515
+ name: "chat",
71516
+ label: "Chat",
71517
+ description: "Conversation only — no tool access",
71518
+ color: "info",
71519
+ allows: [],
71520
+ autoApproveFiles: false
71521
+ },
71522
+ review: {
71523
+ name: "review",
71524
+ label: "Review",
71525
+ description: "Read-only tools only — cannot modify files or run commands",
71526
+ color: "warning",
71527
+ allows: ["read"],
71528
+ autoApproveFiles: false
71529
+ },
71530
+ build: {
71531
+ name: "build",
71532
+ label: "Build",
71533
+ description: "All tools, file mutations auto-approved, bash still prompts",
71534
+ color: "success",
71535
+ allows: ["read", "write", "exec"],
71536
+ autoApproveFiles: true
71537
+ }
71538
+ };
71539
+ var isAllowed = (m, a) => {
71540
+ const modeMeta = MODES[m];
71541
+ if (!modeMeta)
71542
+ return false;
71543
+ return modeMeta.allows.includes(a);
71544
+ };
71545
+ var MODE_ORDER = ["normal", "chat", "review", "build"];
71546
+ var activeMode = "normal";
71547
+ function getActiveMode() {
71548
+ return activeMode;
71549
+ }
71550
+ function setActiveMode(name) {
71551
+ if (name in MODES) {
71552
+ activeMode = name;
71553
+ }
71554
+ }
71555
+ function cycleMode() {
71556
+ const idx = MODE_ORDER.indexOf(activeMode);
71557
+ activeMode = MODE_ORDER[(idx + 1) % MODE_ORDER.length];
71558
+ return activeMode;
71559
+ }
71560
+ function listModes() {
71561
+ return Object.values(MODES);
71562
+ }
71563
+ function findMode(query) {
71564
+ const q = query.trim().toLowerCase();
71565
+ if (!q)
71566
+ return;
71567
+ return listModes().find((m) => m.name === q || m.label.toLowerCase() === q || m.label.toLowerCase().startsWith(q));
71568
+ }
71569
+
70937
71570
  // src/tools/catalog.ts
70938
71571
  class ToolCatalog {
70939
71572
  tools = new Map;
@@ -70952,11 +71585,19 @@ class ToolCatalog {
70952
71585
  }
70953
71586
  toAISDKTools(context) {
70954
71587
  const aiTools = {};
71588
+ const mode = context.mode ?? "normal";
70955
71589
  for (const [name, def] of this.tools.entries()) {
71590
+ if (!isAllowed(mode, def.access)) {
71591
+ continue;
71592
+ }
70956
71593
  aiTools[name] = tool({
70957
71594
  description: def.description,
70958
71595
  inputSchema: def.parameters,
70959
71596
  execute: async (args) => {
71597
+ if (!isAllowed(mode, def.access)) {
71598
+ const modeLabel = MODES[mode]?.label ?? mode;
71599
+ throw new Error(`Tool "${name}" is not available in ${modeLabel} mode.`);
71600
+ }
70960
71601
  return def.execute(args, context);
70961
71602
  }
70962
71603
  });
@@ -71025,6 +71666,7 @@ function isBinaryBuffer(buffer) {
71025
71666
  var readFileTool = {
71026
71667
  name: "read_file",
71027
71668
  displayName: "Read",
71669
+ access: "read",
71028
71670
  description: "Reads the contents of a file. Supports reading specific line ranges via offset and limit. Read is read-only and does not mutate or checkpoint.",
71029
71671
  parameters: readFileInputSchema,
71030
71672
  confirmationPolicy: "never",
@@ -71141,6 +71783,7 @@ var writeFileInputSchema = object({
71141
71783
  var writeFileTool = {
71142
71784
  name: "write_file",
71143
71785
  displayName: "Write",
71786
+ access: "write",
71144
71787
  description: "Writes or creates a whole file in the workspace. Automatically creates parent directories if needed. Every write mutation participates in checkpointing for /rewind.",
71145
71788
  parameters: writeFileInputSchema,
71146
71789
  confirmationPolicy: "never",
@@ -71148,20 +71791,16 @@ var writeFileTool = {
71148
71791
  const lines = result?.linesWritten ?? 0;
71149
71792
  const filePath = args.file_path;
71150
71793
  const isNew = result?.isNew ?? true;
71151
- const summary = isNew ? `Created ${filePath} (${lines} line${lines === 1 ? "" : "s"})` : `Wrote ${lines} line${lines === 1 ? "" : "s"} to ${filePath}`;
71152
- const contentLines = args.content.split(/\r?\n/);
71153
- if (contentLines.length === 0 || contentLines.length === 1 && !contentLines[0]) {
71154
- return summary;
71155
- }
71156
- const cap = 50;
71157
- const shown = contentLines.slice(0, cap);
71158
- const padWidth = String(contentLines.length).length;
71159
- const detail = shown.map((l, i) => `${source_default.dim(String(i + 1).padStart(padWidth))} ${source_default.white(l)}`).join(`
71160
- `);
71161
- const overflow = contentLines.length > cap ? `
71162
- … (${contentLines.length - cap} more lines)` : "";
71163
- return `${summary}
71164
- ${detail}${overflow}`;
71794
+ const headline = isNew ? `Created ${filePath} (${lines} line${lines === 1 ? "" : "s"})` : `Wrote ${lines} line${lines === 1 ? "" : "s"} to ${filePath}`;
71795
+ return {
71796
+ headline,
71797
+ detail: {
71798
+ kind: "code",
71799
+ filePath: args.file_path,
71800
+ text: args.content,
71801
+ totalLines: lines
71802
+ }
71803
+ };
71165
71804
  },
71166
71805
  execute: async (args, context) => {
71167
71806
  const { absolutePath: targetPath, relativePath } = resolveDirectMutationPath(context.cwd, args.file_path);
@@ -71492,61 +72131,25 @@ function countOccurrences(content, substring) {
71492
72131
  var editFileTool = {
71493
72132
  name: "edit_file",
71494
72133
  displayName: "Edit",
72134
+ access: "write",
71495
72135
  description: "Performs exact string replacements in an existing file. Every edit mutation participates in checkpointing for /rewind.",
71496
72136
  parameters: editFileInputSchema,
71497
72137
  confirmationPolicy: "never",
71498
72138
  summarize: (args, result) => {
71499
72139
  const added = result?.addedLines ?? 0;
71500
72140
  const removed = result?.removedLines ?? 0;
71501
- const summary = `Added ${added} line${added === 1 ? "" : "s"}, removed ${removed} line${removed === 1 ? "" : "s"}`;
72141
+ const headline = `Added ${added} line${added === 1 ? "" : "s"}, removed ${removed} line${removed === 1 ? "" : "s"}`;
71502
72142
  if (added === 0 && removed === 0)
71503
- return summary;
72143
+ return { headline };
71504
72144
  const diff = buildUnifiedDiff(args.old_string, args.new_string, 2);
71505
- const theme = getTheme();
71506
- const deleteStyle = (str) => themeBgColor(theme.diffDeleteBG)(themeColor2(theme.diffDeleteFG)(str));
71507
- const addStyle = (str) => themeBgColor(theme.diffAddBG)(themeColor2(theme.diffAddFG)(str));
71508
- const contextStyle = (str) => source_default.dim(str);
71509
- let maxLineNum = 1;
71510
- for (const hunk of diff.hunks) {
71511
- for (const line of hunk.lines) {
71512
- if (line.oldLineNumber)
71513
- maxLineNum = Math.max(maxLineNum, line.oldLineNumber);
71514
- if (line.newLineNumber)
71515
- maxLineNum = Math.max(maxLineNum, line.newLineNumber);
71516
- }
71517
- }
71518
- const padWidth = Math.max(1, String(maxLineNum).length);
71519
- const diffLines = [];
71520
- const cap = 30;
71521
- let count = 0;
71522
- let overflow = 0;
71523
- for (const hunk of diff.hunks) {
71524
- for (const line of hunk.lines) {
71525
- if (count >= cap) {
71526
- overflow++;
71527
- continue;
71528
- }
71529
- count++;
71530
- if (line.kind === "deletion") {
71531
- const numStr = String(line.oldLineNumber ?? "").padStart(padWidth, " ");
71532
- const body = line.spans ? "-" + line.spans.map((s) => s.kind === "deletion" ? source_default.bold(s.text) : s.text).join("") : `-${line.text}`;
71533
- diffLines.push(deleteStyle(`${numStr} ${body}`));
71534
- } else if (line.kind === "addition") {
71535
- const numStr = String(line.newLineNumber ?? "").padStart(padWidth, " ");
71536
- const body = line.spans ? "+" + line.spans.map((s) => s.kind === "addition" ? source_default.bold(s.text) : s.text).join("") : `+${line.text}`;
71537
- diffLines.push(addStyle(`${numStr} ${body}`));
71538
- } else {
71539
- const numStr = String(line.newLineNumber ?? line.oldLineNumber ?? "").padStart(padWidth, " ");
71540
- diffLines.push(contextStyle(`${numStr} ${line.text}`));
71541
- }
72145
+ return {
72146
+ headline,
72147
+ detail: {
72148
+ kind: "diff",
72149
+ filePath: args.file_path,
72150
+ hunks: diff.hunks
71542
72151
  }
71543
- }
71544
- if (overflow > 0) {
71545
- diffLines.push(source_default.dim(` … (${overflow} more lines)`));
71546
- }
71547
- return diffLines.length > 0 ? `${summary}
71548
- ${diffLines.join(`
71549
- `)}` : summary;
72152
+ };
71550
72153
  },
71551
72154
  execute: async (args, context) => {
71552
72155
  const { absolutePath: targetPath, relativePath } = resolveDirectMutationPath(context.cwd, args.file_path);
@@ -71688,6 +72291,7 @@ function walkDir(dir, baseDir, matcher, results, limit) {
71688
72291
  var globTool = {
71689
72292
  name: "glob",
71690
72293
  displayName: "Glob",
72294
+ access: "read",
71691
72295
  description: "Finds files matching a glob pattern or file name in the workspace. Read-only search capability with bounded results.",
71692
72296
  parameters: globInputSchema,
71693
72297
  confirmationPolicy: "never",
@@ -71791,6 +72395,7 @@ function searchInDir(dir, baseDir, regex, fileFilter, results, filesWithMatches,
71791
72395
  var grepTool = {
71792
72396
  name: "grep",
71793
72397
  displayName: "Grep",
72398
+ access: "read",
71794
72399
  description: "Searches for text or regex patterns across files in the workspace. Read-only content search with structured match positions.",
71795
72400
  parameters: grepInputSchema,
71796
72401
  confirmationPolicy: "never",
@@ -71870,6 +72475,7 @@ var listDirInputSchema = object({
71870
72475
  var listDirTool = {
71871
72476
  name: "list_dir",
71872
72477
  displayName: "List",
72478
+ access: "read",
71873
72479
  description: "Lists files and folders in a specified directory. Read-only operation.",
71874
72480
  parameters: listDirInputSchema,
71875
72481
  confirmationPolicy: "never",
@@ -71930,6 +72536,7 @@ var sleepInputSchema = object({
71930
72536
  var sleepTool = {
71931
72537
  name: "sleep",
71932
72538
  displayName: "Sleep",
72539
+ access: "read",
71933
72540
  description: "Pauses current agent execution temporarily (between 1 and 300 seconds). Read-only and cancellable.",
71934
72541
  parameters: sleepInputSchema,
71935
72542
  confirmationPolicy: "never",
@@ -72099,7 +72706,7 @@ class ShellExecution {
72099
72706
  };
72100
72707
  child.stdout?.on("data", (chunk) => handleChunk(chunk, false));
72101
72708
  child.stderr?.on("data", (chunk) => handleChunk(chunk, true));
72102
- const onExit = (code, signal) => {
72709
+ const onExit = (code, _signal) => {
72103
72710
  if (!this.isRunning)
72104
72711
  return;
72105
72712
  this.endedAt = new Date().toISOString();
@@ -72555,6 +73162,7 @@ var bashInputSchema = object({
72555
73162
  var bashTool = {
72556
73163
  name: "bash",
72557
73164
  displayName: "Bash",
73165
+ access: "exec",
72558
73166
  description: "Executes a command in the platform shell. An explanation of what the command does and why it is needed is MANDATORY. Commands that exceed the foreground limit (~12s) automatically continue as background shell tasks. Use task_read, task_send_input, and task_kill to manage background tasks. Note: Bash commands are NOT tracked by the file checkpoint system (/rewind).",
72559
73167
  parameters: bashInputSchema,
72560
73168
  confirmationPolicy: "never",
@@ -72627,6 +73235,7 @@ var taskReadInputSchema = object({
72627
73235
  var taskReadTool = {
72628
73236
  name: "task_read",
72629
73237
  displayName: "Task Read",
73238
+ access: "read",
72630
73239
  description: "Reads current status, exit code, and recent bounded output snapshot of a background shell task. Non-blocking.",
72631
73240
  parameters: taskReadInputSchema,
72632
73241
  confirmationPolicy: "never",
@@ -72661,6 +73270,7 @@ function normalizeStdinInput(rawInput, appendNewline = true) {
72661
73270
  var taskSendInputTool = {
72662
73271
  name: "task_send_input",
72663
73272
  displayName: "Task Send Input",
73273
+ access: "exec",
72664
73274
  description: "Writes input text to the standard input (stdin) of a running background shell task. Automatically appends a newline unless append_newline is set to false.",
72665
73275
  parameters: taskSendInputSchema,
72666
73276
  confirmationPolicy: "never",
@@ -72684,6 +73294,7 @@ var taskKillInputSchema = object({
72684
73294
  var taskKillTool = {
72685
73295
  name: "task_kill",
72686
73296
  displayName: "Task Kill",
73297
+ access: "exec",
72687
73298
  description: "Terminates a running background shell task and its descendant process tree.",
72688
73299
  parameters: taskKillInputSchema,
72689
73300
  confirmationPolicy: "never",
@@ -72703,6 +73314,7 @@ var taskListInputSchema = object({});
72703
73314
  var taskListTool = {
72704
73315
  name: "task_list",
72705
73316
  displayName: "Task List",
73317
+ access: "read",
72706
73318
  description: "Lists all currently tracked background shell tasks along with their IDs, statuses, commands, and exit codes.",
72707
73319
  parameters: taskListInputSchema,
72708
73320
  confirmationPolicy: "never",
@@ -72724,7 +73336,7 @@ var taskListTool = {
72724
73336
  // package.json
72725
73337
  var package_default = {
72726
73338
  name: "steward-cli",
72727
- version: "0.15.0",
73339
+ version: "0.17.0",
72728
73340
  description: "Interactive AI engineering assistant for the terminal with human-in-the-loop control across local and frontier models.",
72729
73341
  author: "sapirrior",
72730
73342
  license: "MIT",
@@ -72786,6 +73398,7 @@ function cleanHtmlContent(html) {
72786
73398
  var webFetchTool = {
72787
73399
  name: "web_fetch",
72788
73400
  displayName: "Web",
73401
+ access: "read",
72789
73402
  description: "Fetches text and documentation content from a public web URL. Automatically cleans HTML markup into readable text.",
72790
73403
  parameters: webFetchInputSchema,
72791
73404
  confirmationPolicy: "never",
@@ -72917,6 +73530,7 @@ async function searchDuckDuckGoDirect(query, limit, signal) {
72917
73530
  var webSearchTool = {
72918
73531
  name: "web_search",
72919
73532
  displayName: "Web Search",
73533
+ access: "read",
72920
73534
  description: "Searches the web directly for up-to-date information, documentation, package releases, and technical answers. Returns structured search results with titles, clean URLs, and snippets.",
72921
73535
  parameters: webSearchInputSchema,
72922
73536
  confirmationPolicy: "never",
@@ -73301,6 +73915,7 @@ var todoWriteTool = {
73301
73915
  name: "todo_write",
73302
73916
  displayName: "TodoWrite",
73303
73917
  icon: "\uD83D\uDCDD",
73918
+ access: "write",
73304
73919
  description: "Initializes or replaces the current session todo list with 2 to 10 items. Use this to establish or replace the multi-step execution plan for the session.",
73305
73920
  parameters: todoWriteParamsSchema,
73306
73921
  summarizeArgs(args) {
@@ -73362,6 +73977,7 @@ var todoUpdateTool = {
73362
73977
  name: "todo_update",
73363
73978
  displayName: "TodoUpdate",
73364
73979
  icon: "✓",
73980
+ access: "write",
73365
73981
  description: "Updates a single todo item status or description in the current session. Cannot create new items or update multiple items at once.",
73366
73982
  parameters: todoUpdateParamsSchema,
73367
73983
  summarizeArgs(args) {
@@ -73427,6 +74043,7 @@ var todoReadTool = {
73427
74043
  name: "todo_read",
73428
74044
  displayName: "TodoRead",
73429
74045
  icon: "\uD83D\uDCCB",
74046
+ access: "read",
73430
74047
  description: "Reads the current session todo list without modifying it. Returns an empty result if no todos have been established yet.",
73431
74048
  parameters: todoReadParamsSchema,
73432
74049
  summarizeArgs() {
@@ -73612,6 +74229,7 @@ var skillListTool = {
73612
74229
  name: "skill_list",
73613
74230
  displayName: "SkillList",
73614
74231
  icon: "\uD83D\uDCA1",
74232
+ access: "read",
73615
74233
  description: "Discovers and lists all available specialized skills and their descriptions. Use this when you need specialized domain knowledge or workflows.",
73616
74234
  parameters: skillListParamsSchema,
73617
74235
  summarizeArgs() {
@@ -73644,6 +74262,7 @@ var skillReadTool = {
73644
74262
  name: "skill_read",
73645
74263
  displayName: "SkillRead",
73646
74264
  icon: "\uD83D\uDCD6",
74265
+ access: "read",
73647
74266
  description: "Loads and reads the instructions (SKILL.md) or a relative resource file of a specialized skill by name. Use this when a task requires specialized guidelines.",
73648
74267
  parameters: skillReadParamsSchema,
73649
74268
  summarizeArgs(args) {
@@ -73662,6 +74281,108 @@ var skillReadTool = {
73662
74281
  return `SkillRead: ${target}${len}`;
73663
74282
  }
73664
74283
  };
74284
+ // src/tools/summary.ts
74285
+ function summarizeToolResult(toolDef, args, result, isError = false) {
74286
+ if (isError)
74287
+ return;
74288
+ if (!toolDef) {
74289
+ if (typeof result === "string") {
74290
+ const firstLine = result.split(`
74291
+ `)[0]?.trim();
74292
+ return firstLine ? { headline: firstLine } : undefined;
74293
+ }
74294
+ if (result && typeof result === "object" && typeof result.message === "string") {
74295
+ return { headline: result.message };
74296
+ }
74297
+ return;
74298
+ }
74299
+ if (toolDef.summarize) {
74300
+ const sum = toolDef.summarize(args, result);
74301
+ if (typeof sum === "string") {
74302
+ const [headline, ...rest] = sum.split(`
74303
+ `);
74304
+ return {
74305
+ headline: headline ?? "",
74306
+ detail: rest.length > 0 ? { kind: "text", text: rest.join(`
74307
+ `) } : undefined
74308
+ };
74309
+ }
74310
+ return sum;
74311
+ }
74312
+ if (typeof result === "string") {
74313
+ const firstLine = result.split(`
74314
+ `)[0]?.trim();
74315
+ return { headline: firstLine || toolDef.displayName };
74316
+ }
74317
+ if (result && typeof result === "object" && typeof result.message === "string") {
74318
+ return { headline: result.message };
74319
+ }
74320
+ return { headline: toolDef.displayName };
74321
+ }
74322
+ function formatPlainToolSummary(summary) {
74323
+ if (!summary)
74324
+ return;
74325
+ if (!summary.detail)
74326
+ return summary.headline;
74327
+ if (summary.detail.kind === "text") {
74328
+ return `${summary.headline}
74329
+ ${summary.detail.text}`;
74330
+ }
74331
+ if (summary.detail.kind === "code") {
74332
+ const lines = summary.detail.text.split(/\r?\n/);
74333
+ const cap = 50;
74334
+ const shown = lines.slice(0, cap);
74335
+ const padWidth = String(lines.length).length;
74336
+ const detail = shown.map((l, i) => `${String(i + 1).padStart(padWidth)} ${l}`).join(`
74337
+ `);
74338
+ const overflow = lines.length > cap ? `
74339
+ … (${lines.length - cap} more lines)` : "";
74340
+ return `${summary.headline}
74341
+ ${detail}${overflow}`;
74342
+ }
74343
+ if (summary.detail.kind === "diff") {
74344
+ const diffLines = [];
74345
+ let maxLineNum = 1;
74346
+ for (const hunk of summary.detail.hunks) {
74347
+ for (const line of hunk.lines) {
74348
+ if (line.oldLineNumber)
74349
+ maxLineNum = Math.max(maxLineNum, line.oldLineNumber);
74350
+ if (line.newLineNumber)
74351
+ maxLineNum = Math.max(maxLineNum, line.newLineNumber);
74352
+ }
74353
+ }
74354
+ const padWidth = Math.max(1, String(maxLineNum).length);
74355
+ const cap = 30;
74356
+ let count = 0;
74357
+ let overflow = 0;
74358
+ for (const hunk of summary.detail.hunks) {
74359
+ for (const line of hunk.lines) {
74360
+ if (count >= cap) {
74361
+ overflow++;
74362
+ continue;
74363
+ }
74364
+ count++;
74365
+ if (line.kind === "deletion") {
74366
+ const numStr = String(line.oldLineNumber ?? "").padStart(padWidth, " ");
74367
+ diffLines.push(`${numStr} -${line.text}`);
74368
+ } else if (line.kind === "addition") {
74369
+ const numStr = String(line.newLineNumber ?? "").padStart(padWidth, " ");
74370
+ diffLines.push(`${numStr} +${line.text}`);
74371
+ } else {
74372
+ const numStr = String(line.newLineNumber ?? line.oldLineNumber ?? "").padStart(padWidth, " ");
74373
+ diffLines.push(`${numStr} ${line.text}`);
74374
+ }
74375
+ }
74376
+ }
74377
+ if (overflow > 0) {
74378
+ diffLines.push(` … (${overflow} more lines)`);
74379
+ }
74380
+ return diffLines.length > 0 ? `${summary.headline}
74381
+ ${diffLines.join(`
74382
+ `)}` : summary.headline;
74383
+ }
74384
+ return summary.headline;
74385
+ }
73665
74386
 
73666
74387
  // src/tools/index.ts
73667
74388
  var builtInTools = [
@@ -89031,7 +89752,7 @@ function inferDocMediaType(uriOrName) {
89031
89752
  }
89032
89753
  return "application/octet-stream";
89033
89754
  }
89034
- function basename(uriOrName) {
89755
+ function basename2(uriOrName) {
89035
89756
  const parts = uriOrName.split("/");
89036
89757
  const last = parts[parts.length - 1];
89037
89758
  return last && last.length > 0 ? last : undefined;
@@ -89069,7 +89790,7 @@ function annotationToSource({
89069
89790
  ...fileCitation.file_name != null ? { title: fileCitation.file_name } : {}
89070
89791
  };
89071
89792
  }
89072
- const filename = (_c = fileCitation.file_name) != null ? _c : basename(uri);
89793
+ const filename = (_c = fileCitation.file_name) != null ? _c : basename2(uri);
89073
89794
  const mediaType = inferDocMediaType(uri);
89074
89795
  return {
89075
89796
  type: "source",
@@ -89172,7 +89893,7 @@ function builtinToolResultToSources({
89172
89893
  });
89173
89894
  continue;
89174
89895
  }
89175
- const filename = (_h = entry.file_name) != null ? _h : basename(uri);
89896
+ const filename = (_h = entry.file_name) != null ? _h : basename2(uri);
89176
89897
  const mediaType = inferDocMediaType(uri);
89177
89898
  sources.push({
89178
89899
  type: "source",
@@ -117680,8 +118401,7 @@ var PROVIDER_REGISTRY = {
117680
118401
  envVar: "DEEPSEEK_API_KEY",
117681
118402
  apiKey: (c) => c.deepseekApiKey,
117682
118403
  defaultModel: "deepseek-chat",
117683
- create: (apiKey) => createDeepSeek({ apiKey }),
117684
- isTaggedReasoning: () => true
118404
+ create: (apiKey) => createDeepSeek({ apiKey })
117685
118405
  },
117686
118406
  openrouter: {
117687
118407
  name: "openrouter",
@@ -117696,8 +118416,7 @@ var PROVIDER_REGISTRY = {
117696
118416
  "HTTP-Referer": "https://github.com/sapirrior/steward",
117697
118417
  "X-Title": "steward"
117698
118418
  }
117699
- }),
117700
- isTaggedReasoning: (modelId) => isTaggedReasoningModel(modelId)
118419
+ })
117701
118420
  },
117702
118421
  custom: {
117703
118422
  name: "custom",
@@ -117708,20 +118427,9 @@ var PROVIDER_REGISTRY = {
117708
118427
  name: "custom",
117709
118428
  baseURL: cfg.custom.baseURL,
117710
118429
  apiKey: cfg.custom.apiKey
117711
- }),
117712
- isTaggedReasoning: (modelId) => isTaggedReasoningModel(modelId)
118430
+ })
117713
118431
  }
117714
118432
  };
117715
- var DEFAULT_MODELS_BY_PROVIDER = {
117716
- gemini: PROVIDER_REGISTRY.gemini.defaultModel,
117717
- anthropic: PROVIDER_REGISTRY.anthropic.defaultModel,
117718
- openai: PROVIDER_REGISTRY.openai.defaultModel,
117719
- xai: PROVIDER_REGISTRY.xai.defaultModel,
117720
- mistral: PROVIDER_REGISTRY.mistral.defaultModel,
117721
- deepseek: PROVIDER_REGISTRY.deepseek.defaultModel,
117722
- openrouter: PROVIDER_REGISTRY.openrouter.defaultModel,
117723
- custom: PROVIDER_REGISTRY.custom.defaultModel
117724
- };
117725
118433
  var DEFAULT_REASONING_EFFORT = "provider-default";
117726
118434
  var REASONING_EFFORT_MAP = {
117727
118435
  0: "provider-default",
@@ -117731,13 +118439,6 @@ var REASONING_EFFORT_MAP = {
117731
118439
  4: "medium",
117732
118440
  5: "high",
117733
118441
  6: "xhigh",
117734
- "0": "provider-default",
117735
- "1": "none",
117736
- "2": "minimal",
117737
- "3": "low",
117738
- "4": "medium",
117739
- "5": "high",
117740
- "6": "xhigh",
117741
118442
  default: "provider-default",
117742
118443
  "provider-default": "provider-default",
117743
118444
  none: "none",
@@ -117784,7 +118485,7 @@ function resolveActiveModelSelection(requested, config = getEnvConfig()) {
117784
118485
  if (!hasProviderConfig(requested.provider, config)) {
117785
118486
  throw new Error(`Provider "${requested.provider}" was requested, but its credentials are not configured in the environment.`);
117786
118487
  }
117787
- const modelId = requested.provider === "custom" ? config.custom.modelName || DEFAULT_MODELS_BY_PROVIDER.custom : DEFAULT_MODELS_BY_PROVIDER[requested.provider];
118488
+ const modelId = requested.provider === "custom" ? config.custom.modelName || PROVIDER_REGISTRY.custom.defaultModel : PROVIDER_REGISTRY[requested.provider].defaultModel;
117788
118489
  return {
117789
118490
  provider: requested.provider,
117790
118491
  modelId,
@@ -117814,7 +118515,7 @@ function resolveActiveModelSelection(requested, config = getEnvConfig()) {
117814
118515
  }
117815
118516
  for (const provider of PROVIDER_SELECTION_PRIORITY) {
117816
118517
  if (available.includes(provider)) {
117817
- const modelId = provider === "custom" ? config.custom.modelName || DEFAULT_MODELS_BY_PROVIDER.custom : DEFAULT_MODELS_BY_PROVIDER[provider];
118518
+ const modelId = provider === "custom" ? config.custom.modelName || PROVIDER_REGISTRY.custom.defaultModel : PROVIDER_REGISTRY[provider].defaultModel;
117818
118519
  return {
117819
118520
  provider,
117820
118521
  modelId,
@@ -117825,7 +118526,7 @@ function resolveActiveModelSelection(requested, config = getEnvConfig()) {
117825
118526
  const fallbackProvider = available[0];
117826
118527
  return {
117827
118528
  provider: fallbackProvider,
117828
- modelId: DEFAULT_MODELS_BY_PROVIDER[fallbackProvider],
118529
+ modelId: PROVIDER_REGISTRY[fallbackProvider].defaultModel,
117829
118530
  effort
117830
118531
  };
117831
118532
  }
@@ -117839,15 +118540,7 @@ function createModelInstance(selection, config = getEnvConfig()) {
117839
118540
  throw new Error(`${descriptor.envVar} is not configured in the environment.`);
117840
118541
  }
117841
118542
  const modelId = selection.provider === "custom" ? config.custom.modelName || selection.modelId : selection.modelId;
117842
- const baseModel = descriptor.create(apiKey, config)(modelId);
117843
- return applyModelMiddlewarePipeline(baseModel, selection, descriptor);
117844
- }
117845
- function applyModelMiddlewarePipeline(model, _selection, _descriptor) {
117846
- return model;
117847
- }
117848
- function isTaggedReasoningModel(modelId) {
117849
- const lower = modelId.toLowerCase();
117850
- return lower.startsWith("deepseek-r1") || lower.includes("/deepseek-r1") || lower.includes("deepseek-reasoner") || lower === "r1" || lower.endsWith("-r1") || lower.startsWith("qwq") || lower.includes("qwen") && lower.includes("think");
118543
+ return descriptor.create(apiKey, config)(modelId);
117851
118544
  }
117852
118545
  function inferProviderFromModelId(modelId) {
117853
118546
  const lower = modelId.toLowerCase();
@@ -117933,6 +118626,30 @@ ${options.userRules.map((rule) => `- ${rule}`).join(`
117933
118626
  ${options.extraInstructions}
117934
118627
  </additional_instructions>`;
117935
118628
  }
118629
+ if (options.chatMode && options.chatMode !== "normal") {
118630
+ const meta = MODES[options.chatMode];
118631
+ if (meta) {
118632
+ if (options.chatMode === "chat") {
118633
+ prompt += `
118634
+
118635
+ <operating_mode>
118636
+ Mode: CHAT — You have no tool access. Respond conversationally only. Do not attempt to call any tools.
118637
+ </operating_mode>`;
118638
+ } else if (options.chatMode === "review") {
118639
+ prompt += `
118640
+
118641
+ <operating_mode>
118642
+ Mode: REVIEW — You may only use read-only tools. Any attempt to write files, run commands, or mutate state will be blocked.
118643
+ </operating_mode>`;
118644
+ } else if (options.chatMode === "build") {
118645
+ prompt += `
118646
+
118647
+ <operating_mode>
118648
+ Mode: BUILD — You have full tool access. File writes and edits are automatically approved without user confirmation — act decisively and make changes directly. Bash commands still require user approval.
118649
+ </operating_mode>`;
118650
+ }
118651
+ }
118652
+ }
117936
118653
  return prompt;
117937
118654
  }
117938
118655
 
@@ -118083,14 +118800,17 @@ class AgentSession {
118083
118800
  lockManager: globalMutationLockManager
118084
118801
  });
118085
118802
  await tracker.beginTurn(turnId, this.sessionData.turns.length + 1);
118803
+ const mode = getActiveMode();
118804
+ const effectiveFilePermission = MODES[mode]?.autoApproveFiles ? async (_req) => ({ allowed: true }) : options.requestFilePermission;
118086
118805
  const toolContext = {
118087
118806
  cwd,
118807
+ mode,
118088
118808
  sessionId: this.sessionData.id,
118089
118809
  abortSignal: this.activeAbortController.signal,
118090
118810
  checkpointTracker: tracker,
118091
118811
  mutationLocks: globalMutationLockManager,
118092
118812
  requestBashPermission: options.requestBashPermission,
118093
- requestFilePermission: options.requestFilePermission,
118813
+ requestFilePermission: effectiveFilePermission,
118094
118814
  shellTasks: this.shellTasks
118095
118815
  };
118096
118816
  let activeTools;
@@ -118103,7 +118823,8 @@ class AgentSession {
118103
118823
  }
118104
118824
  const instructions = buildSystemPrompt({
118105
118825
  cwd,
118106
- extraInstructions: options.extraInstructions
118826
+ extraInstructions: options.extraInstructions,
118827
+ chatMode: mode
118107
118828
  });
118108
118829
  const wrappedOnEvent = (event) => {
118109
118830
  if (event.type === "tool-call") {
@@ -118119,7 +118840,8 @@ class AgentSession {
118119
118840
  });
118120
118841
  } else if (event.type === "tool-result") {
118121
118842
  const toolDef = defaultToolCatalog.get(event.toolResult.name);
118122
- const outputSummary = !event.toolResult.isError && toolDef?.summarize ? toolDef.summarize(event.toolResult.args, event.toolResult.result) : formatToolOutputSummary(event.toolResult.result, event.toolResult.isError);
118843
+ const summaryObj = summarizeToolResult(toolDef, event.toolResult.args, event.toolResult.result, event.toolResult.isError);
118844
+ const outputSummary = formatPlainToolSummary(summaryObj);
118123
118845
  const errorMessage = event.toolResult.isError ? typeof event.toolResult.result === "object" && event.toolResult.result !== null ? event.toolResult.result.message ?? JSON.stringify(event.toolResult.result) : String(event.toolResult.result) : undefined;
118124
118846
  const status = event.toolResult.isError ? "failed" : "completed";
118125
118847
  this.sessionLogWriter?.append({
@@ -118852,6 +119574,84 @@ var skillsCommand = {
118852
119574
  }
118853
119575
  };
118854
119576
 
119577
+ // src/commands/theme/index.ts
119578
+ var themeCommand = {
119579
+ name: "theme",
119580
+ description: "View or switch the active UI color theme with interactive theme picker",
119581
+ usage: "/theme [theme_name]",
119582
+ async execute(args, _context) {
119583
+ if (args.length === 0) {
119584
+ return {
119585
+ handled: true,
119586
+ data: {
119587
+ showThemePicker: true,
119588
+ themes: listThemes(),
119589
+ current: getActiveThemeName()
119590
+ }
119591
+ };
119592
+ }
119593
+ const matched = findTheme(args.join(" "));
119594
+ if (!matched) {
119595
+ const validNames = listThemes().map((t) => t.name).join(", ");
119596
+ return {
119597
+ handled: true,
119598
+ message: `Theme "${args.join(" ")}" was not found.
119599
+ Available themes: ${validNames}.
119600
+ Type "/theme" to open the interactive theme picker.`
119601
+ };
119602
+ }
119603
+ setActiveTheme(matched.name);
119604
+ saveThemeSelection(matched.name);
119605
+ return {
119606
+ handled: true,
119607
+ message: `Active theme switched to ${matched.label} and saved to ~/.steward/settings.json.`,
119608
+ data: { selected: matched, themeSwitched: true }
119609
+ };
119610
+ }
119611
+ };
119612
+
119613
+ // src/commands/mode/index.ts
119614
+ var modeCommand = {
119615
+ name: "mode",
119616
+ aliases: [],
119617
+ description: "Switch or view the current chat mode",
119618
+ execute: async (args) => {
119619
+ if (args.length === 0) {
119620
+ const current = getActiveMode();
119621
+ const currentMeta = listModes().find((m) => m.name === current);
119622
+ const modeList = listModes().map((m) => ` - ${m.label} (${m.name}): ${m.description}`).join(`
119623
+ `);
119624
+ return {
119625
+ handled: true,
119626
+ message: `Current mode: ${currentMeta?.label ?? current}
119627
+
119628
+ Available modes:
119629
+ ${modeList}
119630
+
119631
+ Use "/mode <name>" or press Ctrl+B to cycle modes.`
119632
+ };
119633
+ }
119634
+ const query = args.join(" ");
119635
+ const matched = findMode(query);
119636
+ if (matched) {
119637
+ setActiveMode(matched.name);
119638
+ saveModeSelection(matched.name);
119639
+ return {
119640
+ handled: true,
119641
+ message: `Switched to ${matched.label} mode. ${matched.description}.`,
119642
+ data: {
119643
+ modeSwitched: true,
119644
+ selectedMode: matched
119645
+ }
119646
+ };
119647
+ }
119648
+ return {
119649
+ handled: true,
119650
+ message: `Unknown mode "${query}". Valid modes are: ${listModes().map((m) => m.label).join(", ")}.`
119651
+ };
119652
+ }
119653
+ };
119654
+
118855
119655
  // src/commands/registry.ts
118856
119656
  class CommandRegistry {
118857
119657
  commands = new Map;
@@ -118894,6 +119694,8 @@ var builtInCommands = [
118894
119694
  rewindCommand,
118895
119695
  modelCommand,
118896
119696
  effortCommand,
119697
+ themeCommand,
119698
+ modeCommand,
118897
119699
  clearCommand,
118898
119700
  exitCommand,
118899
119701
  quitCommand,
@@ -118999,27 +119801,25 @@ class TextElement {
118999
119801
  const effWidth = Math.max(1, maxW);
119000
119802
  let styled = rawContent;
119001
119803
  if (this.props.bold)
119002
- styled = source_default.bold(styled);
119003
- if (this.props.dim)
119004
- styled = source_default.dim(styled);
119804
+ styled = bold(styled);
119005
119805
  if (this.props.italic)
119006
- styled = source_default.italic(styled);
119806
+ styled = italic(styled);
119007
119807
  if (this.props.underline)
119008
- styled = source_default.underline(styled);
119808
+ styled = underline(styled);
119009
119809
  if (this.props.strikethrough)
119010
- styled = source_default.strikethrough(styled);
119810
+ styled = strikethrough(styled);
119011
119811
  if (this.props.color) {
119012
119812
  if (typeof this.props.color === "function") {
119013
119813
  styled = this.props.color(styled);
119014
- } else {
119015
- styled = themeColor2(this.props.color)(styled);
119814
+ } else if (this.props.color in c) {
119815
+ styled = c[this.props.color](styled);
119016
119816
  }
119017
119817
  }
119018
119818
  if (this.props.bgColor) {
119019
119819
  if (typeof this.props.bgColor === "function") {
119020
119820
  styled = this.props.bgColor(styled);
119021
- } else {
119022
- styled = themeBgColor(this.props.bgColor)(styled);
119821
+ } else if (this.props.bgColor in bg) {
119822
+ styled = bg[this.props.bgColor](styled);
119023
119823
  }
119024
119824
  }
119025
119825
  const isWrappable = this.props.wrap ?? true;
@@ -119196,7 +119996,7 @@ class BoxElement {
119196
119996
  applyBorders(lines, totalWidth, border) {
119197
119997
  if (border === "none")
119198
119998
  return lines;
119199
- const colorFn = typeof this.props.borderColor === "function" ? this.props.borderColor : this.props.borderColor ? themeColor2(this.props.borderColor) : (s) => s;
119999
+ const colorFn = typeof this.props.borderColor === "function" ? this.props.borderColor : this.props.borderColor ? themeColor(this.props.borderColor) : (s) => s;
119200
120000
  if (border === "top-bottom") {
119201
120001
  const rule = colorFn(figures.horizontalLine.repeat(totalWidth));
119202
120002
  return [rule, ...lines, rule];
@@ -119223,34 +120023,30 @@ function Box(props, children) {
119223
120023
  }
119224
120024
  // src/tui/primitives/widgets/ModalBox.ts
119225
120025
  function renderModalBox(options) {
119226
- const theme = getTheme();
119227
120026
  const termWidth = options.width ?? process.stdout.columns ?? 80;
119228
120027
  const maxCols = Math.max(1, termWidth);
119229
120028
  const dividerWidth = Math.max(1, termWidth);
119230
- const lavHeader = themeColor2(theme.lavenderHeader);
119231
- const infoColor = themeColor2(theme.info);
119232
- const dashRule = themeColor2(theme.dashedRule);
119233
120029
  const elements = [];
119234
- elements.push(Text(lavHeader(figures.horizontalLine.repeat(dividerWidth)), { clip: true }));
120030
+ elements.push(Text(c.rule(figures.horizontalLine.repeat(dividerWidth)), { clip: true }));
119235
120031
  if (options.subtitle) {
119236
120032
  elements.push(Box({ direction: "row", justify: "space-between", width: maxCols }, [
119237
- Text(options.title, { color: theme.info }),
119238
- Text(options.subtitle, { dim: true })
120033
+ Text(options.title, { color: "info" }),
120034
+ Text(options.subtitle, { color: "muted" })
119239
120035
  ]));
119240
120036
  } else {
119241
- elements.push(Text(options.title, { color: theme.lavenderLight }));
120037
+ elements.push(Text(options.title, { color: "permission" }));
119242
120038
  }
119243
120039
  if (options.queryInput) {
119244
- const pointer = infoColor(`${figures.pointer} `);
119245
- const queryDisplay = options.queryInput.query ? source_default.white(options.queryInput.query) : source_default.dim(options.queryInput.placeholder);
120040
+ const pointer = c.info(`${figures.pointer} `);
120041
+ const queryDisplay = options.queryInput.query ? c.text(options.queryInput.query) : c.muted(options.queryInput.placeholder);
119246
120042
  elements.push(Text(`${pointer}${queryDisplay}`));
119247
- elements.push(Text(dashRule(figures.horizontalLine.repeat(dividerWidth)), { clip: true }));
120043
+ elements.push(Text(c.rule(figures.horizontalLine.repeat(dividerWidth)), { clip: true }));
119248
120044
  }
119249
120045
  for (const child of options.content) {
119250
120046
  elements.push(child);
119251
120047
  }
119252
120048
  if (options.footer) {
119253
- elements.push(Text(source_default.dim.italic(options.footer)));
120049
+ elements.push(Text(italic(c.muted(options.footer))));
119254
120050
  }
119255
120051
  const modalBox = Box({ direction: "column", width: maxCols, clip: true }, elements);
119256
120052
  return modalBox.render(maxCols);
@@ -119367,6 +120163,8 @@ function parseKeyInput(chunk) {
119367
120163
  const str = typeof chunk === "string" ? chunk : chunk.toString();
119368
120164
  if (str === "\x03")
119369
120165
  return { type: "ctrl-c", raw: str };
120166
+ if (str === "\x02")
120167
+ return { type: "ctrl-b", raw: str };
119370
120168
  if (str === "\x14")
119371
120169
  return { type: "ctrl-t", raw: str };
119372
120170
  if (str === "\x1B[5~")
@@ -119491,12 +120289,10 @@ class Header extends Component {
119491
120289
  return rawPath;
119492
120290
  }
119493
120291
  render(_width) {
119494
- const theme = getTheme();
119495
120292
  const version = this.props.version ?? DEFAULT_VERSION;
119496
- const brandColor = themeColor2(theme.brand);
119497
- const logoL0 = brandColor(LOGO_LINES[0] ?? "");
119498
- const logoL1 = brandColor(LOGO_LINES[1] ?? "");
119499
- const logoL2 = brandColor(LOGO_LINES[2] ?? "");
120293
+ const logoL0 = c.brand(LOGO_LINES[0] ?? "");
120294
+ const logoL1 = c.brand(LOGO_LINES[1] ?? "");
120295
+ const logoL2 = c.brand(LOGO_LINES[2] ?? "");
119500
120296
  const modelObj = this.props.model ?? { provider: "anthropic", modelId: "claude-3-7-sonnet" };
119501
120297
  const modelTag = `${modelObj.provider}/${modelObj.modelId}`;
119502
120298
  const cwdFormatted = this.formatCwd(this.props.cwd ?? process.cwd());
@@ -119508,17 +120304,17 @@ class Header extends Component {
119508
120304
  /* @__PURE__ */ jsx(Text, {
119509
120305
  wrap: false,
119510
120306
  clip: false,
119511
- children: `${logoL0} ${source_default.white.bold("Steward")} ${source_default.dim(`v${version}`)}`
120307
+ children: `${logoL0} ${bold(c.text("Steward"))} ${c.muted(`v${version}`)}`
119512
120308
  }),
119513
120309
  /* @__PURE__ */ jsx(Text, {
119514
120310
  wrap: false,
119515
120311
  clip: false,
119516
- children: `${logoL1} ${source_default.dim(modelTag)} ${source_default.dim("·")} ${source_default.dim("API Usage Billing")}`
120312
+ children: `${logoL1} ${c.muted(modelTag)} ${c.muted("·")} ${c.muted("API Usage Billing")}`
119517
120313
  }),
119518
120314
  /* @__PURE__ */ jsx(Text, {
119519
120315
  wrap: false,
119520
120316
  clip: false,
119521
- children: `${logoL2} ${source_default.dim(cwdFormatted)}`
120317
+ children: `${logoL2} ${c.muted(cwdFormatted)}`
119522
120318
  }),
119523
120319
  /* @__PURE__ */ jsx(Text, {
119524
120320
  wrap: false,
@@ -119532,17 +120328,6 @@ class Header extends Component {
119532
120328
  }
119533
120329
 
119534
120330
  // src/tui/components/StatusBar.tsx
119535
- function formatTokens(n) {
119536
- if (n < 0)
119537
- return "0";
119538
- if (n >= 1e9)
119539
- return `${(n / 1e9).toFixed(1)}B`;
119540
- if (n >= 1e6)
119541
- return `${(n / 1e6).toFixed(1)}M`;
119542
- if (n >= 1000)
119543
- return `${(n / 1000).toFixed(1)}K`;
119544
- return `${n}`;
119545
- }
119546
120331
  function formatVoiceDuration(sec) {
119547
120332
  const m = Math.floor(sec / 60).toString().padStart(2, "0");
119548
120333
  const s = (sec % 60).toString().padStart(2, "0");
@@ -119558,8 +120343,8 @@ class StatusBar extends Component {
119558
120343
  constructor(props) {
119559
120344
  super(props);
119560
120345
  this.state = {
120346
+ chatMode: props.chatMode ?? getActiveMode(),
119561
120347
  model: props.model,
119562
- usage: props.usage,
119563
120348
  isBusy: props.isBusy,
119564
120349
  exitPending: props.exitPending,
119565
120350
  warning: props.warning,
@@ -119568,6 +120353,9 @@ class StatusBar extends Component {
119568
120353
  updateStatus: props.updateStatus
119569
120354
  };
119570
120355
  }
120356
+ setMode(mode) {
120357
+ this.setState({ chatMode: mode });
120358
+ }
119571
120359
  update(partial) {
119572
120360
  this.setState(partial);
119573
120361
  }
@@ -119631,61 +120419,61 @@ class StatusBar extends Component {
119631
120419
  }
119632
120420
  }
119633
120421
  render(width) {
119634
- const theme = getTheme();
119635
120422
  const termWidth = width ?? process.stdout.columns ?? 80;
119636
120423
  const maxCols = Math.max(1, termWidth);
119637
120424
  const {
119638
120425
  model,
119639
- usage,
119640
120426
  isBusy,
119641
120427
  exitPending,
119642
120428
  warning,
119643
120429
  voiceState,
119644
120430
  voiceDurationSec,
119645
- updateStatus
120431
+ updateStatus,
120432
+ chatMode
119646
120433
  } = this.state;
119647
120434
  let left = "";
119648
120435
  if (warning) {
119649
- left = themeColor2(theme.warning)(warning);
120436
+ left = c.warning(warning);
119650
120437
  } else if (voiceState && voiceState !== "idle") {
119651
120438
  if (voiceState === "connecting") {
119652
- left = `${source_default.white.bold("Connecting...")} ${source_default.dim("Ctrl+T to stop")}`;
120439
+ left = `${bold(c.text("Connecting..."))} ${c.muted("Ctrl+T to stop")}`;
119653
120440
  } else if (voiceState === "recording") {
119654
120441
  const timeStr = formatVoiceDuration(voiceDurationSec ?? 0);
119655
- left = `${source_default.white.bold(`Recording... ${timeStr}`)} ${source_default.dim("Ctrl+T to stop")}`;
120442
+ left = `${bold(c.text(`Recording... ${timeStr}`))} ${c.muted("Ctrl+T to stop")}`;
119656
120443
  } else if (voiceState === "finishing") {
119657
- left = source_default.white.bold("Finishing...");
120444
+ left = bold(c.text("Finishing..."));
119658
120445
  }
119659
120446
  } else if (exitPending) {
119660
- const errColor = themeColor2(theme.error);
119661
- left = `${errColor("▸ ")}${source_default.dim("Press ")}${errColor("Ctrl+C")}${source_default.dim(" again to exit")}`;
120447
+ left = `${c.error("▸ ")}${c.muted("Press ")}${c.error("Ctrl+C")}${c.muted(" again to exit")}`;
119662
120448
  } else if (updateStatus && updateStatus.state !== "idle") {
119663
120449
  if (updateStatus.state === "checking") {
119664
- left = source_default.white(updateStatus.message ?? "Checking for updates...");
120450
+ left = c.text(updateStatus.message ?? "Checking for updates...");
119665
120451
  } else if (updateStatus.state === "available") {
119666
- left = source_default.white(updateStatus.message ?? `Update v${updateStatus.version ?? ""} is available`);
120452
+ left = c.text(updateStatus.message ?? `Update v${updateStatus.version ?? ""} is available`);
119667
120453
  } else if (updateStatus.state === "no-updates") {
119668
- left = source_default.white(updateStatus.message ?? "Steward is up to date");
120454
+ left = c.text(updateStatus.message ?? "Steward is up to date");
119669
120455
  } else if (updateStatus.state === "error" && updateStatus.message) {
119670
- left = themeColor2(theme.error)(updateStatus.message);
120456
+ left = c.error(updateStatus.message);
119671
120457
  } else if (isBusy) {
119672
- left = source_default.dim("esc to interrupt");
120458
+ left = c.muted("esc to interrupt");
119673
120459
  } else {
119674
- left = source_default.dim("? for shortcuts");
120460
+ left = c.muted("? for shortcuts");
119675
120461
  }
119676
120462
  } else if (isBusy) {
119677
- left = source_default.dim("esc to interrupt");
120463
+ left = c.muted("esc to interrupt");
119678
120464
  } else {
119679
- left = source_default.dim("? for shortcuts");
120465
+ left = c.muted("? for shortcuts");
119680
120466
  }
120467
+ const mode = chatMode ?? "normal";
120468
+ const modeMeta = MODES[mode];
120469
+ const modeColorKey = modeMeta?.color ?? "text";
120470
+ const modeLabel = modeMeta?.label ?? mode;
119681
120471
  const parts = [];
119682
- if (usage && usage.totalTokens > 0) {
119683
- parts.push(`${formatTokens(usage.totalTokens)} tokens`);
119684
- }
120472
+ parts.push(c[modeColorKey](modeLabel));
119685
120473
  const effort = model?.effort ?? "provider-default";
119686
120474
  const effortDisplay = effort === "provider-default" ? "default" : effort;
119687
- parts.push(effortDisplay);
119688
- const right = parts.length > 0 ? themeColor2(theme.textMuted)(parts.join(` ${figures.bullet} `)) : "";
120475
+ parts.push(c.muted(effortDisplay));
120476
+ const right = parts.length > 0 ? parts.join(c.muted(` ${figures.bullet} `)) : "";
119689
120477
  const element = /* @__PURE__ */ jsxs(Box, {
119690
120478
  direction: "row",
119691
120479
  justify: "space-between",
@@ -119805,9 +120593,9 @@ class StreamingView extends Component {
119805
120593
  if (targetArg) {
119806
120594
  const maxArgLen = Math.max(10, maxCols - toolName.length - 8);
119807
120595
  const truncatedArg = targetArg.length > maxArgLen ? truncateMiddle(targetArg, maxArgLen) : targetArg;
119808
- line += `${source_default.dim("(")}${source_default.dim(truncatedArg)}${source_default.dim(")")}`;
120596
+ line += `${c.muted("(")}${c.muted(truncatedArg)}${c.muted(")")}`;
119809
120597
  } else {
119810
- line += `${source_default.dim("()")}`;
120598
+ line += `${c.muted("()")}`;
119811
120599
  }
119812
120600
  lines.push(line);
119813
120601
  if (activeTool.recentLines && activeTool.recentLines.length > 0) {
@@ -119815,16 +120603,16 @@ class StreamingView extends Component {
119815
120603
  const maxLogLen = Math.max(10, maxCols - 6);
119816
120604
  for (let i = 0;i < toShow.length; i++) {
119817
120605
  const l = toShow[i] ?? "";
119818
- const p = i === 0 ? ` ${source_default.dim("└ ")}` : " ";
120606
+ const p = i === 0 ? ` ${c.muted("└ ")}` : " ";
119819
120607
  const truncatedLog = l.length > maxLogLen ? `${l.slice(0, maxLogLen - 1)}…` : l;
119820
- lines.push(`${p}${source_default.dim(truncatedLog)}`);
120608
+ lines.push(`${p}${c.muted(truncatedLog)}`);
119821
120609
  }
119822
120610
  } else {
119823
- lines.push(` ${source_default.dim("└ Running...")}`);
120611
+ lines.push(` ${c.muted("└ Running...")}`);
119824
120612
  }
119825
120613
  } else if (isThinking && !text) {
119826
120614
  const bullet = getStatusBullet("running", pulseFrame % 2 === 0);
119827
- lines.push(`${bullet} ${source_default.bold("Thinking..")}`);
120615
+ lines.push(`${bullet} ${bold(c.text("Thinking.."))}`);
119828
120616
  const elapsedMs = thinkingStartTime ? performance.now() - thinkingStartTime : pulseFrame * 120;
119829
120617
  let subText = "wait...";
119830
120618
  if (elapsedMs < 2000) {
@@ -119836,7 +120624,7 @@ class StreamingView extends Component {
119836
120624
  } else {
119837
120625
  subText = "taking a bit time...";
119838
120626
  }
119839
- lines.push(` ${source_default.dim("└ ")}${source_default.dim(subText)}`);
120627
+ lines.push(` ${c.muted("└ ")}${c.muted(subText)}`);
119840
120628
  }
119841
120629
  if (text) {
119842
120630
  if (lines.length > 0)
@@ -119863,7 +120651,7 @@ class StreamingView extends Component {
119863
120651
  continue;
119864
120652
  }
119865
120653
  if (i === 0) {
119866
- const bullet = source_default.white(`${figures.blackCircle} `);
120654
+ const bullet = c.text(`${figures.blackCircle} `);
119867
120655
  lines.push(`${bullet}${l}`);
119868
120656
  } else {
119869
120657
  lines.push(` ${l}`);
@@ -120079,7 +120867,7 @@ class PromptInput extends Component {
120079
120867
  return true;
120080
120868
  }
120081
120869
  const isSlashMode = this.state.value.startsWith("/") && !this.state.value.includes(" ");
120082
- const matchingCommands = isSlashMode ? defaultCommandRegistry.getAll().filter((c) => `/${c.name}`.toLowerCase().startsWith(this.state.value.toLowerCase())) : [];
120870
+ const matchingCommands = isSlashMode ? defaultCommandRegistry.getAll().filter((c2) => `/${c2.name}`.toLowerCase().startsWith(this.state.value.toLowerCase())) : [];
120083
120871
  if (action.type === "newline") {
120084
120872
  const before = this.state.value.slice(0, this.state.cursorPos);
120085
120873
  const after = this.state.value.slice(this.state.cursorPos);
@@ -120343,7 +121131,6 @@ ${after}`, this.state.cursorPos);
120343
121131
  return this.renderWithCursor(width).lines;
120344
121132
  }
120345
121133
  renderWithCursor(width) {
120346
- const theme = getTheme();
120347
121134
  const termWidth = width ?? process.stdout.columns ?? 80;
120348
121135
  const maxCols = Math.max(1, termWidth);
120349
121136
  const dividerWidth = maxCols;
@@ -120364,12 +121151,12 @@ ${after}`, this.state.cursorPos);
120364
121151
  const lines = [];
120365
121152
  let cursor = null;
120366
121153
  if (escPending) {
120367
- lines.push(themeColor2(theme.permission)("Press Esc again to clear"));
121154
+ lines.push(c.permission("Press Esc again to clear"));
120368
121155
  }
120369
- const borderColor = disabled ? themeColor2(theme.subtle) : themeColor2(theme.promptBorder);
121156
+ const borderColor = disabled ? c.subtle : c.promptBorder;
120370
121157
  if (disabled) {
120371
121158
  lines.push(borderColor(figures.horizontalLine.repeat(dividerWidth)));
120372
- lines.push(truncateToWidth(` ${source_default.dim("Esc to stop")}`, maxCols));
121159
+ lines.push(truncateToWidth(` ${c.muted("Esc to stop")}`, maxCols));
120373
121160
  lines.push(borderColor(figures.horizontalLine.repeat(dividerWidth)));
120374
121161
  return { lines, cursor: null };
120375
121162
  }
@@ -120378,22 +121165,19 @@ ${after}`, this.state.cursorPos);
120378
121165
  const leftDashes = borderColor(figures.horizontalLine.repeat(4));
120379
121166
  const rightLen = Math.max(0, maxCols - (4 + histText.length));
120380
121167
  const rightDashes = borderColor(figures.horizontalLine.repeat(rightLen));
120381
- lines.push(`${leftDashes}${source_default.white(histText)}${rightDashes}`);
121168
+ lines.push(`${leftDashes}${c.text(histText)}${rightDashes}`);
120382
121169
  } else {
120383
121170
  lines.push(borderColor(figures.horizontalLine.repeat(dividerWidth)));
120384
121171
  }
120385
121172
  const displayData = voiceMode !== "idle" && voiceTranscript ? insertTextAtAnchor(value, voiceAnchor, voiceTranscript) : { value, cursorPos };
120386
121173
  const effectiveValue = displayData.value;
120387
121174
  const effectiveCursorPos = displayData.cursorPos;
120388
- const chevColor = themeColor2(theme.userChevron);
120389
- const pointer = chevColor(`${figures.pointerBold} `);
120390
- const prefixLen = 2;
120391
121175
  if (effectiveValue.length === 0) {
120392
121176
  cursor = {
120393
121177
  logicalLineIndex: lines.length,
120394
- characterOffsetWithinLine: prefixLen
121178
+ characterOffsetWithinLine: 0
120395
121179
  };
120396
- lines.push(truncateToWidth(`${pointer}${source_default.dim("Type your message...")}`, maxCols));
121180
+ lines.push(truncateToWidth(c.muted("Type your message..."), maxCols));
120397
121181
  } else {
120398
121182
  const vLines = effectiveValue.split(`
120399
121183
  `);
@@ -120406,37 +121190,35 @@ ${after}`, this.state.cursorPos);
120406
121190
  if (cursor === null && effectiveCursorPos >= currentOffset && (effectiveCursorPos <= lineEndOffset || isLast)) {
120407
121191
  cursor = {
120408
121192
  logicalLineIndex: lines.length,
120409
- characterOffsetWithinLine: prefixLen + (effectiveCursorPos - currentOffset)
121193
+ characterOffsetWithinLine: effectiveCursorPos - currentOffset
120410
121194
  };
120411
121195
  }
120412
- const p = i === 0 ? pointer : " ";
120413
- lines.push(`${p}${source_default.white(l)}`);
121196
+ lines.push(c.text(l));
120414
121197
  currentOffset = lineEndOffset + 1;
120415
121198
  }
120416
121199
  }
120417
121200
  lines.push(borderColor(figures.horizontalLine.repeat(dividerWidth)));
120418
121201
  const isSlashMode = voiceMode === "idle" && effectiveValue.startsWith("/") && !effectiveValue.includes(" ");
120419
- const matchingCommands = isSlashMode ? defaultCommandRegistry.getAll().filter((c) => `/${c.name}`.toLowerCase().startsWith(effectiveValue.toLowerCase())) : [];
121202
+ const matchingCommands = isSlashMode ? defaultCommandRegistry.getAll().filter((cmd) => `/${cmd.name}`.toLowerCase().startsWith(effectiveValue.toLowerCase())) : [];
120420
121203
  if (isSlashMode && matchingCommands.length > 0 && effectiveValue !== `/${matchingCommands[0]?.name} `) {
120421
121204
  for (let i = 0;i < matchingCommands.length; i++) {
120422
121205
  const cmd = matchingCommands[i];
120423
121206
  const isSelected = i === paletteIdx;
120424
121207
  const namePadded = `/${cmd.name}`.padEnd(18);
120425
121208
  if (isSelected) {
120426
- lines.push(truncateToWidth(`${source_default.white.bold(namePadded)}${source_default.white.bold(cmd.description)}`, maxCols));
121209
+ lines.push(truncateToWidth(`${bold(c.text(namePadded))}${bold(c.text(cmd.description))}`, maxCols));
120427
121210
  } else {
120428
- lines.push(truncateToWidth(`${source_default.dim(namePadded)}${source_default.dim(cmd.description)}`, maxCols));
121211
+ lines.push(truncateToWidth(`${c.muted(namePadded)}${c.muted(cmd.description)}`, maxCols));
120429
121212
  }
120430
121213
  }
120431
121214
  }
120432
121215
  if (voiceMode === "idle" && fileMatches.length > 0) {
120433
- const infoColor = themeColor2(theme.info);
120434
- lines.push(source_default.dim("Matching files (@):"));
121216
+ lines.push(c.muted("Matching files (@):"));
120435
121217
  for (let i = 0;i < fileMatches.length; i++) {
120436
121218
  const f = fileMatches[i];
120437
121219
  const isSelected = i === fileSelectIdx;
120438
- const p = isSelected ? infoColor(`${figures.pointerBold} `) : " ";
120439
- const fileText = isSelected ? infoColor(f) : source_default.dim(f);
121220
+ const p = isSelected ? c.info(`${figures.pointerBold} `) : " ";
121221
+ const fileText = isSelected ? c.info(f) : c.muted(f);
120440
121222
  lines.push(truncateToWidth(`${p}${fileText}`, maxCols));
120441
121223
  }
120442
121224
  }
@@ -120493,19 +121275,14 @@ class TrustGate extends Component {
120493
121275
  }
120494
121276
  }
120495
121277
  render(width) {
120496
- const theme = getTheme();
120497
121278
  const termWidth = width ?? process.stdout.columns ?? 80;
120498
121279
  const maxCols = Math.max(1, termWidth);
120499
- const warnColor = themeColor2(theme.warning);
120500
- const permColor = themeColor2(theme.permission);
120501
- const mutedColor = themeColor2(theme.textMuted);
120502
- const dimGuideColor = themeColor2(theme.inactive);
120503
121280
  const { selectedIndex } = this.state;
120504
121281
  const isYesSelected = selectedIndex === 0;
120505
121282
  const isNoSelected = selectedIndex === 1;
120506
121283
  const pointer = figures.pointerBold ?? "❯";
120507
- const yesOptionText = isYesSelected ? `${permColor(pointer)} ${permColor.bold("1. Yes, I trust this folder")}` : ` ${source_default.white("1. Yes, I trust this folder")}`;
120508
- const noOptionText = isNoSelected ? `${permColor(pointer)} ${permColor.bold("2. No, exit")}` : ` ${source_default.white("2. No, exit")}`;
121284
+ const yesOptionText = isYesSelected ? `${c.permission(pointer)} ${bold(c.permission("1. Yes, I trust this folder"))}` : ` ${c.text("1. Yes, I trust this folder")}`;
121285
+ const noOptionText = isNoSelected ? `${c.permission(pointer)} ${bold(c.permission("2. No, exit"))}` : ` ${c.text("2. No, exit")}`;
120509
121286
  const safetyCheckText = "You should only proceed if you trust this workspace. Accessing untrusted workspaces may allow malicious code in the repository to compromise security or mislead the assistant.";
120510
121287
  const capabilityText = "Steward can read files and execute commands to investigate, build, and debug with your explicit permission. Pre-mutation checkpoints ensure every edit is reversible via /rewind.";
120511
121288
  const element = /* @__PURE__ */ jsxs(Box, {
@@ -120517,7 +121294,7 @@ class TrustGate extends Component {
120517
121294
  /* @__PURE__ */ jsx(Text, {
120518
121295
  wrap: false,
120519
121296
  clip: false,
120520
- children: warnColor.bold("Accessing workspace:")
121297
+ children: bold(c.warning("Accessing workspace:"))
120521
121298
  }),
120522
121299
  /* @__PURE__ */ jsx(Text, {
120523
121300
  wrap: false,
@@ -120527,7 +121304,7 @@ class TrustGate extends Component {
120527
121304
  /* @__PURE__ */ jsx(Text, {
120528
121305
  wrap: false,
120529
121306
  clip: false,
120530
- children: source_default.white.bold(this.props.cwd)
121307
+ children: bold(c.text(this.props.cwd))
120531
121308
  }),
120532
121309
  /* @__PURE__ */ jsx(Text, {
120533
121310
  wrap: false,
@@ -120536,7 +121313,7 @@ class TrustGate extends Component {
120536
121313
  }),
120537
121314
  /* @__PURE__ */ jsx(Text, {
120538
121315
  wrap: true,
120539
- children: source_default.white(safetyCheckText)
121316
+ children: c.text(safetyCheckText)
120540
121317
  }),
120541
121318
  /* @__PURE__ */ jsx(Text, {
120542
121319
  wrap: false,
@@ -120545,7 +121322,7 @@ class TrustGate extends Component {
120545
121322
  }),
120546
121323
  /* @__PURE__ */ jsx(Text, {
120547
121324
  wrap: true,
120548
- children: source_default.white(capabilityText)
121325
+ children: c.text(capabilityText)
120549
121326
  }),
120550
121327
  /* @__PURE__ */ jsx(Text, {
120551
121328
  wrap: false,
@@ -120555,7 +121332,7 @@ class TrustGate extends Component {
120555
121332
  /* @__PURE__ */ jsx(Text, {
120556
121333
  wrap: false,
120557
121334
  clip: false,
120558
- children: dimGuideColor("Security guide")
121335
+ children: c.muted("Security guide")
120559
121336
  }),
120560
121337
  /* @__PURE__ */ jsx(Text, {
120561
121338
  wrap: false,
@@ -120580,7 +121357,7 @@ class TrustGate extends Component {
120580
121357
  /* @__PURE__ */ jsx(Text, {
120581
121358
  wrap: false,
120582
121359
  clip: false,
120583
- children: mutedColor(source_default.italic("Enter to confirm · Esc to cancel"))
121360
+ children: italic(c.muted("Enter to confirm · Esc to cancel"))
120584
121361
  })
120585
121362
  ]
120586
121363
  });
@@ -120605,12 +121382,9 @@ class ModelPicker extends SelectList {
120605
121382
  onSelect: props.onSelect,
120606
121383
  onCancel: props.onCancel,
120607
121384
  renderItem: (m, isSelected, maxCols) => {
120608
- const theme = getTheme();
120609
- const selColor = themeColor2(theme.permission);
120610
- const yellowColor = themeColor2(theme.warning);
120611
121385
  const isCurrent = m.provider === props.currentModel.provider && m.model_id === props.currentModel.modelId;
120612
- const pointer = isSelected ? selColor(`${figures.pointer} `) : " ";
120613
- const activeBadge = isCurrent ? yellowColor.bold(" (active)") : "";
121386
+ const pointer = isSelected ? c.selected(`${figures.pointer} `) : " ";
121387
+ const activeBadge = isCurrent ? bold(c.current(" (active)")) : "";
120614
121388
  const providerName = m.provider.toUpperCase();
120615
121389
  const caps = m.capabilities?.reasoning ? "reasoning" : "chat";
120616
121390
  const meta = `${providerName} • ${caps}`;
@@ -120619,14 +121393,14 @@ class ModelPicker extends SelectList {
120619
121393
  width: maxCols,
120620
121394
  children: [
120621
121395
  /* @__PURE__ */ jsx(Text, {
120622
- color: isSelected ? selColor : isCurrent ? yellowColor : source_default.white,
121396
+ color: isSelected ? "selected" : isCurrent ? "current" : "text",
120623
121397
  wrap: false,
120624
121398
  clip: true,
120625
121399
  ellipsis: true,
120626
121400
  children: `${pointer}${m.model_id}${activeBadge}`
120627
121401
  }),
120628
121402
  /* @__PURE__ */ jsx(Text, {
120629
- dim: true,
121403
+ color: "muted",
120630
121404
  wrap: false,
120631
121405
  clip: true,
120632
121406
  ellipsis: true,
@@ -120639,6 +121413,60 @@ class ModelPicker extends SelectList {
120639
121413
  }
120640
121414
  }
120641
121415
 
121416
+ // src/tui/components/docks/ThemePicker.tsx
121417
+ class ThemePicker extends SelectList {
121418
+ wrap = false;
121419
+ clip = true;
121420
+ ellipsis = false;
121421
+ constructor(props) {
121422
+ const currentMeta = props.themes.find((t) => t.name === props.currentTheme);
121423
+ const subtitle = currentMeta ? `Current: ${currentMeta.label}` : `Current: ${props.currentTheme}`;
121424
+ super({
121425
+ items: props.themes,
121426
+ title: "Select Theme",
121427
+ subtitle,
121428
+ placeholder: "Type to filter themes…",
121429
+ emptyMessage: " No themes matching query.",
121430
+ maxVisible: 4,
121431
+ searchFilter: (t, q) => t.name.toLowerCase().includes(q) || t.label.toLowerCase().includes(q) || t.description.toLowerCase().includes(q),
121432
+ onSelect: props.onSelect,
121433
+ onCancel: props.onCancel,
121434
+ renderItem: (t, isSelected, maxCols) => {
121435
+ const isCurrent = t.name === props.currentTheme;
121436
+ const pointer = isSelected ? c.selected(`${figures.pointer} `) : " ";
121437
+ const activeBadge = isCurrent ? bold(c.current(" (active)")) : "";
121438
+ const tBrand = resolveThemeColor(t.theme.brand, false)(figures.blackCircle);
121439
+ const tSuccess = resolveThemeColor(t.theme.success, false)(figures.blackCircle);
121440
+ const tWarning = resolveThemeColor(t.theme.warning, false)(figures.blackCircle);
121441
+ const tError = resolveThemeColor(t.theme.error, false)(figures.blackCircle);
121442
+ const tInfo = resolveThemeColor(t.theme.info, false)(figures.blackCircle);
121443
+ const tPerm = resolveThemeColor(t.theme.permission, false)(figures.blackCircle);
121444
+ const swatches = ` ${tBrand} ${tSuccess} ${tWarning} ${tError} ${tInfo} ${tPerm} ${t.description}`;
121445
+ return /* @__PURE__ */ jsxs(Box, {
121446
+ direction: "column",
121447
+ width: maxCols,
121448
+ children: [
121449
+ /* @__PURE__ */ jsx(Text, {
121450
+ color: isSelected ? "selected" : isCurrent ? "current" : "text",
121451
+ wrap: false,
121452
+ clip: true,
121453
+ ellipsis: true,
121454
+ children: `${pointer}${t.label}${activeBadge}`
121455
+ }),
121456
+ /* @__PURE__ */ jsx(Text, {
121457
+ color: "muted",
121458
+ wrap: false,
121459
+ clip: true,
121460
+ ellipsis: true,
121461
+ children: swatches
121462
+ })
121463
+ ]
121464
+ });
121465
+ }
121466
+ });
121467
+ }
121468
+ }
121469
+
120642
121470
  // src/tui/components/docks/SessionMenu.tsx
120643
121471
  class SessionMenu extends SelectList {
120644
121472
  wrap = false;
@@ -120661,11 +121489,9 @@ class SessionMenu extends SelectList {
120661
121489
  onSelect: props.onSelect,
120662
121490
  onCancel: props.onCancel,
120663
121491
  renderItem: (s, isSelected, maxCols) => {
120664
- const theme = getTheme();
120665
- const selColor = themeColor2(theme.permission);
120666
121492
  const rawTitle = s.name || s.turns?.[0]?.userPrompt || "Untitled Session";
120667
121493
  const cleanTitle = rawTitle.replace(/\s+/g, " ").trim();
120668
- const pointer = isSelected ? selColor(`${figures.pointer} `) : " ";
121494
+ const pointer = isSelected ? c.selected(`${figures.pointer} `) : " ";
120669
121495
  const shortId = s.id.slice(0, 8);
120670
121496
  const dateStr = s.date ? s.date.split("T")[0] : "";
120671
121497
  const turnsCount = s.turns?.length ?? 0;
@@ -120680,14 +121506,14 @@ class SessionMenu extends SelectList {
120680
121506
  width: maxCols,
120681
121507
  children: [
120682
121508
  /* @__PURE__ */ jsx(Text, {
120683
- color: isSelected ? selColor : source_default.white,
121509
+ color: isSelected ? "selected" : "text",
120684
121510
  wrap: false,
120685
121511
  clip: true,
120686
121512
  ellipsis: true,
120687
121513
  children: `${pointer}${cleanTitle}`
120688
121514
  }),
120689
121515
  /* @__PURE__ */ jsx(Text, {
120690
- dim: true,
121516
+ color: "muted",
120691
121517
  wrap: false,
120692
121518
  clip: true,
120693
121519
  ellipsis: true,
@@ -120731,18 +121557,20 @@ class ShortcutsMenu extends Component {
120731
121557
  { key: "/", desc: "Commands" },
120732
121558
  { key: "@", desc: "Mention files" },
120733
121559
  { key: "?", desc: "Shortcuts menu" },
121560
+ { key: "Ctrl+B", desc: "Switch mode" },
120734
121561
  { key: "Esc Esc", desc: "Clear input" }
120735
121562
  ];
120736
121563
  const rightCol = [
120737
121564
  { key: "Shift+Enter", desc: "Newline" },
120738
121565
  { key: "Ctrl+C", desc: "Abort / exit" },
120739
121566
  { key: "PgUp/PgDn", desc: "Scroll history" },
120740
- { key: "↑ / ↓", desc: "History navigation" }
121567
+ { key: "↑ / ↓", desc: "History navigation" },
121568
+ { key: "Ctrl+T", desc: "Voice dictation" }
120741
121569
  ];
120742
121570
  const rows = leftCol.map((left, i) => {
120743
121571
  const right = rightCol[i];
120744
- const leftFormatted = ` ${source_default.white(left.key.padEnd(11))} ${source_default.dim(left.desc.padEnd(18))}`;
120745
- const rightFormatted = `${source_default.white(right.key.padEnd(13))} ${source_default.dim(right.desc)}`;
121572
+ const leftFormatted = ` ${c.text(left.key.padEnd(11))} ${c.muted(left.desc.padEnd(18))}`;
121573
+ const rightFormatted = `${c.text(right.key.padEnd(13))} ${c.muted(right.desc)}`;
120746
121574
  return /* @__PURE__ */ jsxs(Box, {
120747
121575
  direction: "row",
120748
121576
  justify: "start",
@@ -120835,12 +121663,8 @@ class EffortPicker extends Component {
120835
121663
  }
120836
121664
  }
120837
121665
  render(width) {
120838
- const theme = getTheme();
120839
121666
  const termWidth = width ?? process.stdout.columns ?? 80;
120840
121667
  const maxCols = Math.max(1, termWidth);
120841
- const permColor = themeColor2(theme.permission);
120842
- const infoColor = themeColor2(theme.info);
120843
- const trackColor = themeColor2(theme.promptBorder);
120844
121668
  const { selectedIndex } = this.state;
120845
121669
  const activeOption = EFFORT_OPTIONS[selectedIndex] ?? EFFORT_OPTIONS[0];
120846
121670
  const leftPad = 4;
@@ -120850,24 +121674,23 @@ class EffortPicker extends Component {
120850
121674
  const fasterText = "Faster";
120851
121675
  const smarterText = "Smarter";
120852
121676
  const spectrumGap = Math.max(1, totalSliderCols - (fasterText.length + smarterText.length));
120853
- const spectrumRow = `${" ".repeat(leftPad)}${infoColor.bold(fasterText)}${" ".repeat(spectrumGap)}${permColor.bold(smarterText)}`;
120854
- const yellowColor = themeColor2(theme.warning);
121677
+ const spectrumRow = `${" ".repeat(leftPad)}${bold(c.info(fasterText))}${" ".repeat(spectrumGap)}${bold(c.permission(smarterText))}`;
120855
121678
  let trackChars = "";
120856
121679
  for (let i = 0;i < EFFORT_OPTIONS.length; i++) {
120857
121680
  const isSelected = i === selectedIndex;
120858
121681
  const opt = EFFORT_OPTIONS[i];
120859
121682
  const isCurrent = opt.id === this.props.currentEffort;
120860
- const pointerColor = isCurrent ? yellowColor : permColor;
121683
+ const pointerColor = isCurrent ? c.current : c.permission;
120861
121684
  const slotCenter = Math.floor(colWidth / 2);
120862
- for (let c = 0;c < colWidth; c++) {
120863
- if (c === slotCenter) {
121685
+ for (let ch = 0;ch < colWidth; ch++) {
121686
+ if (ch === slotCenter) {
120864
121687
  if (isSelected) {
120865
121688
  trackChars += pointerColor(figures.sliderPointer ?? "▲");
120866
121689
  } else {
120867
- trackChars += trackColor(figures.horizontalLine);
121690
+ trackChars += c.promptBorder(figures.horizontalLine);
120868
121691
  }
120869
121692
  } else {
120870
- trackChars += trackColor(figures.horizontalLine);
121693
+ trackChars += c.promptBorder(figures.horizontalLine);
120871
121694
  }
120872
121695
  }
120873
121696
  }
@@ -120879,13 +121702,13 @@ class EffortPicker extends Component {
120879
121702
  const isCurrent = opt.id === this.props.currentEffort;
120880
121703
  let labelText = opt.label;
120881
121704
  if (isSelected && isCurrent) {
120882
- labelText = yellowColor.bold(labelText);
121705
+ labelText = bold(c.current(labelText));
120883
121706
  } else if (isSelected) {
120884
- labelText = permColor.bold(labelText);
121707
+ labelText = bold(c.permission(labelText));
120885
121708
  } else if (isCurrent) {
120886
- labelText = yellowColor.bold(labelText);
121709
+ labelText = bold(c.current(labelText));
120887
121710
  } else {
120888
- labelText = source_default.white(labelText);
121711
+ labelText = c.text(labelText);
120889
121712
  }
120890
121713
  const textLen = opt.label.length;
120891
121714
  const padLeft = Math.floor((colWidth - textLen) / 2);
@@ -120893,7 +121716,7 @@ class EffortPicker extends Component {
120893
121716
  labelCols += `${" ".repeat(Math.max(0, padLeft))}${labelText}${" ".repeat(Math.max(0, padRight))}`;
120894
121717
  }
120895
121718
  const labelsRow = `${" ".repeat(leftPad)}${labelCols}`;
120896
- const descText = ` ${source_default.white.bold(activeOption.description)}`;
121719
+ const descText = ` ${bold(c.text(activeOption.description))}`;
120897
121720
  const contentElements = [
120898
121721
  /* @__PURE__ */ jsxs(Box, {
120899
121722
  direction: "column",
@@ -121044,30 +121867,28 @@ class RewindMenu extends SelectList {
121044
121867
  onSelect: props.onSelect,
121045
121868
  onCancel: props.onCancel,
121046
121869
  renderItem: (item, isSelected, maxCols) => {
121047
- const theme = getTheme();
121048
- const selColor = themeColor2(theme.permission);
121049
- const pointer = isSelected ? selColor(`${figures.pointer} `) : " ";
121870
+ const pointer = isSelected ? c.selected(`${figures.pointer} `) : " ";
121050
121871
  let summaryText;
121051
121872
  if (item.hasCodeChanges) {
121052
- const filePart = source_default.dim(`${item.changedFileCount} file${item.changedFileCount !== 1 ? "s" : ""} changed`);
121873
+ const filePart = c.muted(`${item.changedFileCount} file${item.changedFileCount !== 1 ? "s" : ""} changed`);
121053
121874
  const diffBadges = [];
121054
121875
  if (item.addedLines > 0) {
121055
- diffBadges.push(themeColor2(theme.diffAddFG)(`+${item.addedLines}`));
121876
+ diffBadges.push(c.diffAddFg(`+${item.addedLines}`));
121056
121877
  }
121057
121878
  if (item.deletedLines > 0) {
121058
- diffBadges.push(themeColor2(theme.diffDeleteFG)(`-${item.deletedLines}`));
121879
+ diffBadges.push(c.diffDelFg(`-${item.deletedLines}`));
121059
121880
  }
121060
121881
  const diffStr = diffBadges.length > 0 ? ` ${diffBadges.join(" ")}` : "";
121061
121882
  summaryText = ` ${filePart}${diffStr}`;
121062
121883
  } else {
121063
- summaryText = ` ${source_default.dim("No code changes")}`;
121884
+ summaryText = ` ${c.muted("No code changes")}`;
121064
121885
  }
121065
121886
  return /* @__PURE__ */ jsxs(Box, {
121066
121887
  direction: "column",
121067
121888
  width: maxCols,
121068
121889
  children: [
121069
121890
  /* @__PURE__ */ jsx(Text, {
121070
- color: isSelected ? selColor : source_default.white,
121891
+ color: isSelected ? "selected" : "text",
121071
121892
  wrap: false,
121072
121893
  clip: true,
121073
121894
  ellipsis: true,
@@ -121168,18 +121989,14 @@ class BashPermissionDock extends Component {
121168
121989
  return Math.max(0, renderedCmdLines.length - maxVisibleReviewLines);
121169
121990
  }
121170
121991
  render(width) {
121171
- const theme = getTheme();
121172
121992
  const termWidth = width ?? process.stdout.columns ?? 80;
121173
121993
  const maxCols = Math.max(1, termWidth);
121174
- const dividerColor = themeColor2(theme.dividerRule);
121175
- const permColor = themeColor2(theme.permission);
121176
- const mutedColor = themeColor2(theme.textMuted);
121177
121994
  const { mode, selectedIndex, scrollOffset } = this.state;
121178
121995
  const { command, explanation } = this.props;
121179
121996
  const pointer = figures.pointerBold ?? "❯";
121180
121997
  const isYesSelected = selectedIndex === 0;
121181
121998
  const isNoSelected = selectedIndex === 1;
121182
- const divider = dividerColor(figures.horizontalLine.repeat(maxCols));
121999
+ const divider = c.rule(figures.horizontalLine.repeat(maxCols));
121183
122000
  if (mode === "REVIEW") {
121184
122001
  const cmdText = new Text(command, { wrap: true, clip: false });
121185
122002
  const renderedCmdLines = cmdText.render(maxCols);
@@ -121207,7 +122024,7 @@ class BashPermissionDock extends Component {
121207
122024
  /* @__PURE__ */ jsx(Text, {
121208
122025
  wrap: false,
121209
122026
  clip: false,
121210
- children: source_default.white.bold("Bash command (review)")
122027
+ children: bold(c.text("Bash command (review)"))
121211
122028
  }),
121212
122029
  /* @__PURE__ */ jsx(Text, {
121213
122030
  wrap: false,
@@ -121217,7 +122034,7 @@ class BashPermissionDock extends Component {
121217
122034
  visibleCmdLines.map((line) => /* @__PURE__ */ jsx(Text, {
121218
122035
  wrap: false,
121219
122036
  clip: false,
121220
- children: ` ${source_default.white(line)}`
122037
+ children: ` ${c.text(line)}`
121221
122038
  })),
121222
122039
  /* @__PURE__ */ jsx(Text, {
121223
122040
  wrap: false,
@@ -121227,7 +122044,7 @@ class BashPermissionDock extends Component {
121227
122044
  /* @__PURE__ */ jsx(Text, {
121228
122045
  wrap: false,
121229
122046
  clip: false,
121230
- children: mutedColor(source_default.italic("↑/↓ scroll · Esc / f to return"))
122047
+ children: italic(c.muted("↑/↓ scroll · Esc / f to return"))
121231
122048
  })
121232
122049
  ]
121233
122050
  });
@@ -121237,8 +122054,8 @@ class BashPermissionDock extends Component {
121237
122054
  const renderedCmdLines = cmdText.render(Math.max(10, maxCols - 4));
121238
122055
  const previewLines = renderedCmdLines.slice(0, 3);
121239
122056
  const hiddenCount = Math.max(0, renderedCmdLines.length - 3);
121240
- const yesOptionText = isYesSelected ? `${permColor(pointer)} ${permColor.bold("1. Yes")}` : ` ${source_default.white("1. Yes")}`;
121241
- const noOptionText = isNoSelected ? `${permColor(pointer)} ${permColor.bold("2. No")}` : ` ${source_default.white("2. No")}`;
122057
+ const yesOptionText = isYesSelected ? `${c.permission(pointer)} ${bold(c.permission("1. Yes"))}` : ` ${c.text("1. Yes")}`;
122058
+ const noOptionText = isNoSelected ? `${c.permission(pointer)} ${bold(c.permission("2. No"))}` : ` ${c.text("2. No")}`;
121242
122059
  const element = /* @__PURE__ */ jsxs(Box, {
121243
122060
  direction: "column",
121244
122061
  width: maxCols,
@@ -121258,7 +122075,7 @@ class BashPermissionDock extends Component {
121258
122075
  /* @__PURE__ */ jsx(Text, {
121259
122076
  wrap: false,
121260
122077
  clip: false,
121261
- children: source_default.white.bold("Bash command")
122078
+ children: bold(c.text("Bash command"))
121262
122079
  }),
121263
122080
  /* @__PURE__ */ jsx(Text, {
121264
122081
  wrap: false,
@@ -121268,12 +122085,12 @@ class BashPermissionDock extends Component {
121268
122085
  previewLines.map((line) => /* @__PURE__ */ jsx(Text, {
121269
122086
  wrap: false,
121270
122087
  clip: false,
121271
- children: ` ${source_default.white(line)}`
122088
+ children: ` ${c.text(line)}`
121272
122089
  })),
121273
122090
  hiddenCount > 0 && /* @__PURE__ */ jsx(Text, {
121274
122091
  wrap: false,
121275
122092
  clip: false,
121276
- children: ` ${source_default.dim(`(+${hiddenCount} hidden)`)}`
122093
+ children: ` ${c.muted(`(+${hiddenCount} hidden)`)}`
121277
122094
  }),
121278
122095
  /* @__PURE__ */ jsx(Text, {
121279
122096
  wrap: false,
@@ -121282,7 +122099,7 @@ class BashPermissionDock extends Component {
121282
122099
  }),
121283
122100
  /* @__PURE__ */ jsx(Text, {
121284
122101
  wrap: true,
121285
- children: ` ${source_default.dim(explanation)}`
122102
+ children: ` ${c.muted(explanation)}`
121286
122103
  }),
121287
122104
  /* @__PURE__ */ jsx(Text, {
121288
122105
  wrap: false,
@@ -121292,7 +122109,7 @@ class BashPermissionDock extends Component {
121292
122109
  /* @__PURE__ */ jsx(Text, {
121293
122110
  wrap: false,
121294
122111
  clip: false,
121295
- children: source_default.white("Do you want to proceed?")
122112
+ children: c.text("Do you want to proceed?")
121296
122113
  }),
121297
122114
  /* @__PURE__ */ jsx(Text, {
121298
122115
  wrap: false,
@@ -121317,7 +122134,7 @@ class BashPermissionDock extends Component {
121317
122134
  /* @__PURE__ */ jsx(Text, {
121318
122135
  wrap: false,
121319
122136
  clip: false,
121320
- children: mutedColor(source_default.italic("Esc to cancel · f to review"))
122137
+ children: italic(c.muted("Esc to cancel · f to review"))
121321
122138
  })
121322
122139
  ]
121323
122140
  });
@@ -121406,15 +122223,11 @@ class FilePermissionDock extends Component {
121406
122223
  return Math.max(0, allContentRows.length - maxVisibleReviewLines);
121407
122224
  }
121408
122225
  renderContentRows(maxCols) {
121409
- const theme = getTheme();
121410
122226
  const { request } = this.props;
121411
122227
  const { kind, before, after } = request;
121412
- const diffAdd = themeColor2(theme.diffAddFG);
121413
- const diffAddBg = themeBgColor(theme.diffAddBG);
121414
- const diffDelete = themeColor2(theme.diffDeleteFG);
121415
- const diffDeleteBg = themeBgColor(theme.diffDeleteBG);
121416
122228
  if (kind === "create" || kind === "overwrite") {
121417
- const rawLines = after.split(/\r?\n/);
122229
+ const highlighted = highlightCode(after, { filePath: request.filePath });
122230
+ const rawLines = highlighted.split(/\r?\n/);
121418
122231
  const lines = after === "" ? [] : rawLines;
121419
122232
  const maxLineNum = Math.max(1, lines.length);
121420
122233
  const gutterWidth = Math.max(2, String(maxLineNum).length);
@@ -121422,9 +122235,9 @@ class FilePermissionDock extends Component {
121422
122235
  for (let i = 0;i < lines.length; i++) {
121423
122236
  const lineNum = i + 1;
121424
122237
  const lineNumStr = String(lineNum).padStart(gutterWidth, " ");
121425
- const firstPrefix = ` ${source_default.dim(lineNumStr)} `;
122238
+ const firstPrefix = ` ${c.muted(lineNumStr)} `;
121426
122239
  const contPrefix = ` ${" ".repeat(gutterWidth)} `;
121427
- const wrapped = prefixedBlock(firstPrefix, source_default.white(lines[i]), {
122240
+ const wrapped = prefixedBlock(firstPrefix, lines[i] ?? "", {
121428
122241
  continuationPrefix: contPrefix,
121429
122242
  width: maxCols
121430
122243
  });
@@ -121448,32 +122261,33 @@ class FilePermissionDock extends Component {
121448
122261
  for (const line of hunk.lines) {
121449
122262
  if (line.kind === "deletion") {
121450
122263
  const numStr = String(line.oldLineNumber ?? "").padStart(gutterWidth, " ");
121451
- const firstPrefix = ` ${source_default.dim(numStr)} `;
122264
+ const firstPrefix = ` ${c.muted(numStr)} `;
121452
122265
  const contPrefix = ` ${" ".repeat(gutterWidth)} `;
121453
- const bodyText = line.spans ? diffDelete("-") + line.spans.map((s) => s.kind === "deletion" ? source_default.bold(diffDelete(s.text)) : diffDelete(s.text)).join("") : diffDelete(`-${line.text}`);
122266
+ const bodyText = line.spans ? c.diffDelFg("-") + line.spans.map((s) => s.kind === "deletion" ? bold(c.diffDelFg(s.text)) : c.diffDelFg(s.text)).join("") : c.diffDelFg(`-${line.text}`);
121454
122267
  const wrapped = prefixedBlock(firstPrefix, bodyText, {
121455
122268
  continuationPrefix: contPrefix,
121456
122269
  width: maxCols,
121457
- bg: diffDeleteBg
122270
+ bg: bg.diffDelBg
121458
122271
  });
121459
122272
  renderedRows.push(...wrapped);
121460
122273
  } else if (line.kind === "addition") {
121461
122274
  const numStr = String(line.newLineNumber ?? "").padStart(gutterWidth, " ");
121462
- const firstPrefix = ` ${source_default.dim(numStr)} `;
122275
+ const firstPrefix = ` ${c.muted(numStr)} `;
121463
122276
  const contPrefix = ` ${" ".repeat(gutterWidth)} `;
121464
- const bodyText = line.spans ? diffAdd("+") + line.spans.map((s) => s.kind === "addition" ? source_default.bold(diffAdd(s.text)) : diffAdd(s.text)).join("") : diffAdd(`+${line.text}`);
122277
+ const bodyText = line.spans ? c.diffAddFg("+") + line.spans.map((s) => s.kind === "addition" ? bold(c.diffAddFg(s.text)) : c.diffAddFg(s.text)).join("") : c.diffAddFg(`+${line.text}`);
121465
122278
  const wrapped = prefixedBlock(firstPrefix, bodyText, {
121466
122279
  continuationPrefix: contPrefix,
121467
122280
  width: maxCols,
121468
- bg: diffAddBg
122281
+ bg: bg.diffAddBg
121469
122282
  });
121470
122283
  renderedRows.push(...wrapped);
121471
122284
  } else {
121472
122285
  const lineNum = line.newLineNumber ?? line.oldLineNumber ?? "";
121473
122286
  const numStr = String(lineNum).padStart(gutterWidth, " ");
121474
- const firstPrefix = ` ${source_default.dim(numStr)} `;
122287
+ const firstPrefix = ` ${c.muted(numStr)} `;
121475
122288
  const contPrefix = ` ${" ".repeat(gutterWidth)} `;
121476
- const wrapped = prefixedBlock(firstPrefix, source_default.white(line.text), {
122289
+ const highlightedLine = highlightCode(line.text, { filePath: request.filePath });
122290
+ const wrapped = prefixedBlock(firstPrefix, highlightedLine, {
121477
122291
  continuationPrefix: contPrefix,
121478
122292
  width: maxCols
121479
122293
  });
@@ -121484,19 +122298,15 @@ class FilePermissionDock extends Component {
121484
122298
  return renderedRows;
121485
122299
  }
121486
122300
  render(width) {
121487
- const theme = getTheme();
121488
122301
  const termWidth = width ?? process.stdout.columns ?? 80;
121489
122302
  const maxCols = Math.max(1, termWidth);
121490
- const dividerColor = themeColor2(theme.dividerRule);
121491
- const permColor = themeColor2(theme.permission);
121492
- const mutedColor = themeColor2(theme.textMuted);
121493
122303
  const { mode, selectedIndex, scrollOffset } = this.state;
121494
122304
  const { request } = this.props;
121495
122305
  const { kind, filePath } = request;
121496
122306
  const pointer = figures.pointerBold ?? "❯";
121497
122307
  const isYesSelected = selectedIndex === 0;
121498
122308
  const isNoSelected = selectedIndex === 1;
121499
- const divider = dividerColor(figures.horizontalLine.repeat(maxCols));
122309
+ const divider = c.rule(figures.horizontalLine.repeat(maxCols));
121500
122310
  let title = "Edit file";
121501
122311
  let question = `Do you want to make this edit to ${filePath}?`;
121502
122312
  if (kind === "create") {
@@ -121532,12 +122342,12 @@ class FilePermissionDock extends Component {
121532
122342
  /* @__PURE__ */ jsx(Text, {
121533
122343
  wrap: false,
121534
122344
  clip: false,
121535
- children: source_default.white.bold(`${title} (review)`)
122345
+ children: bold(c.text(`${title} (review)`))
121536
122346
  }),
121537
122347
  /* @__PURE__ */ jsx(Text, {
121538
122348
  wrap: false,
121539
122349
  clip: false,
121540
- children: ` ${source_default.dim(filePath)}`
122350
+ children: ` ${c.muted(filePath)}`
121541
122351
  }),
121542
122352
  /* @__PURE__ */ jsx(Text, {
121543
122353
  wrap: false,
@@ -121557,7 +122367,7 @@ class FilePermissionDock extends Component {
121557
122367
  /* @__PURE__ */ jsx(Text, {
121558
122368
  wrap: false,
121559
122369
  clip: false,
121560
- children: mutedColor(source_default.italic("↑/↓ scroll · Esc / f to return"))
122370
+ children: italic(c.muted("↑/↓ scroll · Esc / f to return"))
121561
122371
  })
121562
122372
  ]
121563
122373
  });
@@ -121565,8 +122375,8 @@ class FilePermissionDock extends Component {
121565
122375
  }
121566
122376
  const previewRows = allContentRows.slice(0, 10);
121567
122377
  const hiddenCount = Math.max(0, allContentRows.length - 10);
121568
- const yesOptionText = isYesSelected ? `${permColor(pointer)} ${permColor.bold("1. Yes")}` : ` ${source_default.white("1. Yes")}`;
121569
- const noOptionText = isNoSelected ? `${permColor(pointer)} ${permColor.bold("2. No")}` : ` ${source_default.white("2. No")}`;
122378
+ const yesOptionText = isYesSelected ? `${c.permission(pointer)} ${bold(c.permission("1. Yes"))}` : ` ${c.text("1. Yes")}`;
122379
+ const noOptionText = isNoSelected ? `${c.permission(pointer)} ${bold(c.permission("2. No"))}` : ` ${c.text("2. No")}`;
121570
122380
  const element = /* @__PURE__ */ jsxs(Box, {
121571
122381
  direction: "column",
121572
122382
  width: maxCols,
@@ -121586,12 +122396,12 @@ class FilePermissionDock extends Component {
121586
122396
  /* @__PURE__ */ jsx(Text, {
121587
122397
  wrap: false,
121588
122398
  clip: false,
121589
- children: source_default.white.bold(title)
122399
+ children: bold(c.text(title))
121590
122400
  }),
121591
122401
  /* @__PURE__ */ jsx(Text, {
121592
122402
  wrap: false,
121593
122403
  clip: false,
121594
- children: ` ${source_default.dim(filePath)}`
122404
+ children: ` ${c.muted(filePath)}`
121595
122405
  }),
121596
122406
  /* @__PURE__ */ jsx(Text, {
121597
122407
  wrap: false,
@@ -121606,7 +122416,7 @@ class FilePermissionDock extends Component {
121606
122416
  hiddenCount > 0 && /* @__PURE__ */ jsx(Text, {
121607
122417
  wrap: false,
121608
122418
  clip: false,
121609
- children: ` ${source_default.dim(`(+${hiddenCount} hidden)`)}`
122419
+ children: ` ${c.muted(`(+${hiddenCount} hidden)`)}`
121610
122420
  }),
121611
122421
  /* @__PURE__ */ jsx(Text, {
121612
122422
  wrap: false,
@@ -121616,7 +122426,7 @@ class FilePermissionDock extends Component {
121616
122426
  /* @__PURE__ */ jsx(Text, {
121617
122427
  wrap: false,
121618
122428
  clip: false,
121619
- children: source_default.white(question)
122429
+ children: c.text(question)
121620
122430
  }),
121621
122431
  /* @__PURE__ */ jsx(Text, {
121622
122432
  wrap: false,
@@ -121641,7 +122451,7 @@ class FilePermissionDock extends Component {
121641
122451
  /* @__PURE__ */ jsx(Text, {
121642
122452
  wrap: false,
121643
122453
  clip: false,
121644
- children: mutedColor(source_default.italic("Esc to cancel · f to review"))
122454
+ children: italic(c.muted("Esc to cancel · f to review"))
121645
122455
  })
121646
122456
  ]
121647
122457
  });
@@ -121780,6 +122590,67 @@ class PermissionQueue {
121780
122590
  }
121781
122591
  }
121782
122592
  }
122593
+
122594
+ // src/tui/utils/transcript.ts
122595
+ function formatTurnFooter(engine, turnPresentationEnd) {
122596
+ if (!turnPresentationEnd)
122597
+ return;
122598
+ if (turnPresentationEnd.status === "complete") {
122599
+ engine.commit("system", [
122600
+ "",
122601
+ formatTurnStatus(turnPresentationEnd.durationMs, new Date(turnPresentationEnd.finishedAt), turnPresentationEnd.statusVerb)
122602
+ ]);
122603
+ if (turnPresentationEnd.stopReason === "step-limit") {
122604
+ engine.commit("system", formatSystemMessage("Step budget reached. Generation stopped early."));
122605
+ }
122606
+ } else if ((turnPresentationEnd.status === "errored" || turnPresentationEnd.status === "interrupted") && turnPresentationEnd.errorMessage) {
122607
+ const structured = classifyError(new Error(turnPresentationEnd.errorMessage));
122608
+ engine.commit("system", formatErrorBadge(structured));
122609
+ }
122610
+ }
122611
+ function renderTranscript(engine, sessionData, header) {
122612
+ engine.clearAll();
122613
+ engine.commit("header", header.render());
122614
+ const log = loadSessionLog(sessionData.date, sessionData.id);
122615
+ const projection = log ? buildSessionPresentationProjection(log.events) : null;
122616
+ const items = rehydrateSessionHistory(sessionData, projection);
122617
+ let currentTurnId = undefined;
122618
+ for (const item of items) {
122619
+ if (item.turnId && item.turnId !== currentTurnId) {
122620
+ if (currentTurnId && projection) {
122621
+ const prevTurnPresentation = projection.turns.get(currentTurnId)?.end;
122622
+ formatTurnFooter(engine, prevTurnPresentation);
122623
+ }
122624
+ currentTurnId = item.turnId;
122625
+ }
122626
+ if (item.type === "user") {
122627
+ engine.commitPrompt(item.content);
122628
+ } else if (item.type === "system") {
122629
+ engine.commit("system", formatSystemMessage(item.content));
122630
+ } else if (item.type === "tool" && item.toolData) {
122631
+ const toolData = item.toolData;
122632
+ engine.commit("tool-result", (w) => formatToolStatus({
122633
+ toolName: toolData.toolName,
122634
+ displayName: toolData.displayName,
122635
+ icon: toolData.icon,
122636
+ argsSummary: toolData.argsSummary,
122637
+ status: toolData.status,
122638
+ durationMs: toolData.durationMs,
122639
+ error: toolData.error,
122640
+ toolOutput: toolData.toolOutput,
122641
+ targetWidth: w
122642
+ }), { hangingIndent: 2 });
122643
+ } else if (item.type === "assistant") {
122644
+ engine.commit("assistant-message", formatAssistantMessage(item.content), {
122645
+ hangingIndent: 2
122646
+ });
122647
+ }
122648
+ }
122649
+ if (currentTurnId && projection) {
122650
+ const lastTurnPresentation = projection.turns.get(currentTurnId)?.end;
122651
+ formatTurnFooter(engine, lastTurnPresentation);
122652
+ }
122653
+ }
121783
122654
  // src/voice/model.ts
121784
122655
  var VOICE_MODEL_ID = "gemini-3.5-transcribe-live";
121785
122656
  // src/voice/errors.ts
@@ -121878,7 +122749,7 @@ async function defaultCheckParec() {
121878
122749
  }
121879
122750
  async function checkVoicePrerequisites(options = {}) {
121880
122751
  const config = options.config ?? getEnvConfig();
121881
- const checkRecorder = options.checkParecFn ?? options.checkArecordFn ?? defaultCheckParec;
122752
+ const checkRecorder = options.checkParecFn ?? defaultCheckParec;
121882
122753
  const hasGemini = hasProviderConfig("gemini", config);
121883
122754
  const geminiApiKey = config.geminiApiKey;
121884
122755
  if (!hasGemini || !geminiApiKey) {
@@ -122835,7 +123706,6 @@ class TUIApp {
122835
123706
  });
122836
123707
  this.statusBar = new StatusBar({
122837
123708
  model,
122838
- usage: this.session.session.totalUsage,
122839
123709
  isBusy: false
122840
123710
  });
122841
123711
  this.voiceController = new VoiceController({
@@ -123020,6 +123890,18 @@ class TUIApp {
123020
123890
  }, 1500);
123021
123891
  return true;
123022
123892
  }
123893
+ if (action.type === "ctrl-b") {
123894
+ if (this.activeModal)
123895
+ return true;
123896
+ if (this.session.isBusy || this.isBusy) {
123897
+ this.statusBar.showWarning("Cannot change mode while Steward is generating");
123898
+ return true;
123899
+ }
123900
+ const newMode = cycleMode();
123901
+ saveModeSelection(newMode);
123902
+ this.statusBar.setMode(newMode);
123903
+ return true;
123904
+ }
123023
123905
  if (action.type === "ctrl-t") {
123024
123906
  if (this.activeModal) {
123025
123907
  return true;
@@ -123095,76 +123977,41 @@ class TUIApp {
123095
123977
  this.engine.mount(picker, { kind: "dock" });
123096
123978
  this.engine.mount(this.statusBar);
123097
123979
  }
123980
+ openThemePicker(themes) {
123981
+ if (this.activeModal)
123982
+ this.closeModal();
123983
+ this.engine.unmount(this.promptInput);
123984
+ this.engine.unmount(this.statusBar);
123985
+ const currentTheme = getActiveThemeName();
123986
+ const picker = new ThemePicker({
123987
+ themes,
123988
+ currentTheme,
123989
+ onSelect: (selected) => {
123990
+ setActiveTheme(selected.name);
123991
+ saveThemeSelection(selected.name);
123992
+ renderTranscript(this.engine, this.session.session, this.header);
123993
+ this.engine.mount(this.streamingView);
123994
+ this.engine.mount(this.promptInput, { keepCursorVisible: true, kind: "input" });
123995
+ this.engine.mount(this.statusBar);
123996
+ this.engine.requestFrame(true);
123997
+ this.engine.commit("system", formatSystemMessage(`Theme switched to ${selected.label}.`));
123998
+ this.closeModal();
123999
+ },
124000
+ onCancel: () => this.closeModal()
124001
+ });
124002
+ this.activeModal = picker;
124003
+ this.engine.mount(picker, { kind: "dock" });
124004
+ this.engine.mount(this.statusBar);
124005
+ }
123098
124006
  switchToSession(selected) {
123099
124007
  this.permissionQueue.clear();
124008
+ recoverPendingCheckpoint(this.cwd, selected.id).catch(() => {});
123100
124009
  this.session.shutdown().catch(() => {});
123101
124010
  this.session = AgentSession.resume(selected);
123102
124011
  const model = this.session.getModel();
123103
124012
  this.header.props.model = model;
123104
- this.statusBar.update({ model, usage: this.session.session.totalUsage });
123105
- this.engine.clearAll();
123106
- this.engine.commit("header", this.header.render());
123107
- const log = loadSessionLog(selected.date, selected.id);
123108
- const projection = log ? buildSessionPresentationProjection(log.events) : null;
123109
- const items = rehydrateSessionHistory(selected, projection);
123110
- let currentTurnId = undefined;
123111
- for (const item of items) {
123112
- if (item.turnId && item.turnId !== currentTurnId) {
123113
- if (currentTurnId && projection) {
123114
- const prevTurnPresentation = projection.turns.get(currentTurnId)?.end;
123115
- if (prevTurnPresentation && prevTurnPresentation.status === "complete") {
123116
- this.engine.commit("system", [
123117
- "",
123118
- formatTurnStatus(prevTurnPresentation.durationMs, new Date(prevTurnPresentation.finishedAt), prevTurnPresentation.statusVerb)
123119
- ]);
123120
- if (prevTurnPresentation.stopReason === "step-limit") {
123121
- this.engine.commit("system", formatSystemMessage("Step budget reached. Generation stopped early."));
123122
- }
123123
- } else if (prevTurnPresentation && (prevTurnPresentation.status === "errored" || prevTurnPresentation.status === "interrupted") && prevTurnPresentation.errorMessage) {
123124
- const structured = classifyError(new Error(prevTurnPresentation.errorMessage));
123125
- this.engine.commit("system", formatErrorBadge(structured));
123126
- }
123127
- }
123128
- currentTurnId = item.turnId;
123129
- }
123130
- if (item.type === "user") {
123131
- this.engine.commitPrompt(item.content);
123132
- } else if (item.type === "system") {
123133
- this.engine.commit("system", formatSystemMessage(item.content));
123134
- } else if (item.type === "tool" && item.toolData) {
123135
- const toolData = item.toolData;
123136
- this.engine.commit("tool-result", (w) => formatToolStatus({
123137
- toolName: toolData.toolName,
123138
- displayName: toolData.displayName,
123139
- icon: toolData.icon,
123140
- argsSummary: toolData.argsSummary,
123141
- status: toolData.status,
123142
- durationMs: toolData.durationMs,
123143
- error: toolData.error,
123144
- toolOutput: toolData.toolOutput,
123145
- targetWidth: w
123146
- }), { hangingIndent: 2 });
123147
- } else if (item.type === "assistant") {
123148
- this.engine.commit("assistant-message", formatAssistantMessage(item.content), {
123149
- hangingIndent: 2
123150
- });
123151
- }
123152
- }
123153
- if (currentTurnId && projection) {
123154
- const lastTurnPresentation = projection.turns.get(currentTurnId)?.end;
123155
- if (lastTurnPresentation && lastTurnPresentation.status === "complete") {
123156
- this.engine.commit("system", [
123157
- "",
123158
- formatTurnStatus(lastTurnPresentation.durationMs, new Date(lastTurnPresentation.finishedAt), lastTurnPresentation.statusVerb)
123159
- ]);
123160
- if (lastTurnPresentation.stopReason === "step-limit") {
123161
- this.engine.commit("system", formatSystemMessage("Step budget reached. Generation stopped early."));
123162
- }
123163
- } else if (lastTurnPresentation && (lastTurnPresentation.status === "errored" || lastTurnPresentation.status === "interrupted") && lastTurnPresentation.errorMessage) {
123164
- const structured = classifyError(new Error(lastTurnPresentation.errorMessage));
123165
- this.engine.commit("system", formatErrorBadge(structured));
123166
- }
123167
- }
124013
+ this.statusBar.update({ model });
124014
+ renderTranscript(this.engine, selected, this.header);
123168
124015
  this.engine.mount(this.streamingView);
123169
124016
  this.engine.mount(this.promptInput, { keepCursorVisible: true, kind: "input" });
123170
124017
  this.engine.mount(this.statusBar);
@@ -123273,6 +124120,18 @@ class TUIApp {
123273
124120
  this.openModelPicker(cmdResult.data.models ?? []);
123274
124121
  return;
123275
124122
  }
124123
+ if (cmdResult.data?.showThemePicker) {
124124
+ this.openThemePicker(cmdResult.data.themes ?? listThemes());
124125
+ return;
124126
+ }
124127
+ if (cmdResult.data?.themeSwitched) {
124128
+ for (const comp of this.engine.components)
124129
+ comp.markDirty();
124130
+ this.engine.requestFrame(true);
124131
+ }
124132
+ if (cmdResult.data?.modeSwitched && cmdResult.data?.selectedMode) {
124133
+ this.statusBar.setMode(cmdResult.data.selectedMode.name);
124134
+ }
123276
124135
  if (cmdResult.data?.showEffortPicker) {
123277
124136
  this.openEffortPicker();
123278
124137
  return;
@@ -123293,8 +124152,7 @@ class TUIApp {
123293
124152
  const updatedModel = this.session.getModel();
123294
124153
  this.header.props.model = updatedModel;
123295
124154
  this.statusBar.update({
123296
- model: updatedModel,
123297
- usage: this.session.session.totalUsage
124155
+ model: updatedModel
123298
124156
  });
123299
124157
  return;
123300
124158
  }
@@ -123350,7 +124208,7 @@ class TUIApp {
123350
124208
  const argsSummary = JSON.stringify(event.toolResult.args);
123351
124209
  const status = event.toolResult.isError ? "failed" : "completed";
123352
124210
  const error = event.toolResult.isError ? typeof event.toolResult.result === "object" && event.toolResult.result !== null ? event.toolResult.result.message ?? JSON.stringify(event.toolResult.result) : String(event.toolResult.result) : undefined;
123353
- const toolOutput = !event.toolResult.isError && toolDef?.summarize ? toolDef.summarize(event.toolResult.args, event.toolResult.result) : undefined;
124211
+ const summary = summarizeToolResult(toolDef, event.toolResult.args, event.toolResult.result, event.toolResult.isError);
123354
124212
  this.engine.commit("tool-result", (w) => formatToolStatus({
123355
124213
  toolName,
123356
124214
  displayName,
@@ -123359,7 +124217,7 @@ class TUIApp {
123359
124217
  status,
123360
124218
  durationMs,
123361
124219
  error,
123362
- toolOutput,
124220
+ summary,
123363
124221
  targetWidth: w
123364
124222
  }), { hangingIndent: 2 });
123365
124223
  this.streamingView.setThinking(true);
@@ -123381,9 +124239,6 @@ class TUIApp {
123381
124239
  "",
123382
124240
  formatTurnStatus(totalDurationMs, finishedAt, event.summary.statusVerb)
123383
124241
  ]);
123384
- this.statusBar.update({
123385
- usage: this.session.session.totalUsage
123386
- });
123387
124242
  if (event.summary.stopReason === "step-limit") {
123388
124243
  this.engine.commit("system", formatSystemMessage("Step budget reached. Generation stopped early."));
123389
124244
  }
@@ -123413,9 +124268,6 @@ class TUIApp {
123413
124268
  this.closeModal();
123414
124269
  }
123415
124270
  this.setBusy(false);
123416
- this.statusBar.update({
123417
- usage: this.session.session.totalUsage
123418
- });
123419
124271
  }
123420
124272
  }
123421
124273
  setBusy(busy) {
@@ -123469,10 +124321,35 @@ function parseCliArgs(args) {
123469
124321
  rawArgs: args
123470
124322
  };
123471
124323
  }
124324
+ if (target === "theme" || target === "ui.theme" || target === "ui-theme" || target === "color") {
124325
+ const value = subArgs.slice(1).join(" ").trim() || undefined;
124326
+ return {
124327
+ command: "config",
124328
+ configArgs: { target: "theme", value },
124329
+ rawArgs: args
124330
+ };
124331
+ }
124332
+ if (target === "model" || target === "ai.model" || target === "ai-model") {
124333
+ const value = subArgs.slice(1).join(" ").trim() || undefined;
124334
+ return {
124335
+ command: "config",
124336
+ configArgs: { target: "model", value },
124337
+ rawArgs: args
124338
+ };
124339
+ }
124340
+ if (target === "mode" || target === "ui.mode" || target === "ui-mode") {
124341
+ const value = subArgs.slice(1).join(" ").trim() || undefined;
124342
+ return {
124343
+ command: "config",
124344
+ configArgs: { target: "mode", value },
124345
+ rawArgs: args
124346
+ };
124347
+ }
123472
124348
  console.error(`Error: Unknown configuration target "${target}".
123473
124349
  ` + `Usage:
123474
- ` + ` steward --config View all current configurations
123475
- ` + ` steward --config voice <lang> Configure preferred voice language`);
124350
+ ` + ` steward --config View all current configurations
124351
+ ` + ` steward --config voice <lang> Configure preferred voice language
124352
+ ` + ` steward --config theme <name> Configure UI color theme`);
123476
124353
  process.exit(1);
123477
124354
  }
123478
124355
  const unrecognized = args[0];
@@ -123566,9 +124443,13 @@ function handleConfigCommand(configArgs) {
123566
124443
  const voiceDisplay = voiceLang ? `${getLanguageDisplayName(voiceLang)} (${voiceLang})` : "auto-detect (default)";
123567
124444
  const savedModel = getSavedModel();
123568
124445
  const modelDisplay = savedModel ? `${savedModel.provider}:${savedModel.modelId} (effort: ${savedModel.effort ?? "provider-default"})` : "not configured (using environment priority)";
124446
+ const savedTheme = getSavedTheme() ?? "dark (default)";
124447
+ const savedMode = getSavedMode() ?? "normal (default)";
123569
124448
  console.log(`Steward Configuration (${settingsPath}):`);
124449
+ console.log(` UI Theme: ${savedTheme}`);
123570
124450
  console.log(` Voice Language: ${voiceDisplay}`);
123571
124451
  console.log(` Saved Model: ${modelDisplay}`);
124452
+ console.log(` Chat Mode: ${savedMode}`);
123572
124453
  process.exit(0);
123573
124454
  }
123574
124455
  if (configArgs.target === "voice") {
@@ -123601,6 +124482,48 @@ function handleConfigCommand(configArgs) {
123601
124482
  console.log(`✓ Voice language configured: ${resolved.name} (${resolved.tag})`);
123602
124483
  process.exit(0);
123603
124484
  }
124485
+ if (configArgs.target === "mode") {
124486
+ const rawMode = configArgs.value?.trim();
124487
+ const allModes = listModes();
124488
+ if (!rawMode) {
124489
+ const current = getSavedMode() ?? "normal";
124490
+ const currentMeta = findMode(current);
124491
+ console.log(`Current chat mode: ${currentMeta?.label ?? current} (${current})`);
124492
+ process.exit(0);
124493
+ }
124494
+ const matched = findMode(rawMode);
124495
+ if (!matched) {
124496
+ console.error(`Error: Unknown mode "${rawMode}".
124497
+ ` + `Available modes:
124498
+ ` + allModes.map((m) => ` steward --config mode ${m.name.padEnd(10)} (${m.label})`).join(`
124499
+ `));
124500
+ process.exit(1);
124501
+ }
124502
+ saveModeSelection(matched.name);
124503
+ console.log(`✓ Chat mode configured: ${matched.label} (${matched.name})`);
124504
+ process.exit(0);
124505
+ }
124506
+ if (configArgs.target === "theme") {
124507
+ const rawTheme = configArgs.value?.trim();
124508
+ const allThemes = listThemes();
124509
+ if (!rawTheme) {
124510
+ const current = getSavedTheme() ?? "dark";
124511
+ const currentMeta = findTheme(current);
124512
+ console.log(`Current UI theme: ${currentMeta?.label ?? current} (${current})`);
124513
+ process.exit(0);
124514
+ }
124515
+ const matched = findTheme(rawTheme);
124516
+ if (!matched) {
124517
+ console.error(`Error: Unknown theme "${rawTheme}".
124518
+ ` + `Available themes:
124519
+ ` + allThemes.map((t) => ` steward --config theme ${t.name.padEnd(18)} (${t.label})`).join(`
124520
+ `));
124521
+ process.exit(1);
124522
+ }
124523
+ saveThemeSelection(matched.name);
124524
+ console.log(`✓ UI theme configured: ${matched.label} (${matched.name})`);
124525
+ process.exit(0);
124526
+ }
123604
124527
  }
123605
124528
 
123606
124529
  // src/cli/commands/version/index.ts
@@ -123654,6 +124577,8 @@ async function runCli(argv = process.argv.slice(2)) {
123654
124577
  return;
123655
124578
  }
123656
124579
  try {
124580
+ setActiveTheme(getSavedTheme() ?? "dark");
124581
+ setActiveMode(getSavedMode() ?? "normal");
123657
124582
  const session = new AgentSession;
123658
124583
  const app = new TUIApp({
123659
124584
  version: VERSION16,