steward-cli 0.27.0 → 0.29.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.
Files changed (2) hide show
  1. package/dist/cli.js +265 -352
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -42874,8 +42874,8 @@ function resolveThemeColor(color, isBg = false) {
42874
42874
  if (color === "dim" || color === "ansi(dim)") {
42875
42875
  return isBg ? (s) => s : (s) => source_default.dim(s);
42876
42876
  }
42877
- if (color.startsWith("ansi(") && color.endsWith(")")) {
42878
- const name = color.slice(5, -1).trim();
42877
+ if (color.startsWith("ansi(") && color.endsWith(")") || color.startsWith("ansi:")) {
42878
+ const name = color.startsWith("ansi:") ? color.slice(5).trim() : color.slice(5, -1).trim();
42879
42879
  if (name === "reset" || name === "none" || name === "transparent" || name === "default") {
42880
42880
  return (s) => s;
42881
42881
  }
@@ -43062,48 +43062,122 @@ var draculaTheme = {
43062
43062
  default: "rgb(248,248,242)"
43063
43063
  }
43064
43064
  };
43065
+ var darkDaltonizedTheme = {
43066
+ text: "rgb(255,255,255)",
43067
+ muted: "rgb(153,153,153)",
43068
+ subtle: "rgb(80,80,80)",
43069
+ brand: "rgb(255,153,51)",
43070
+ info: "rgb(153,204,255)",
43071
+ success: "rgb(51,153,255)",
43072
+ warning: "rgb(255,204,0)",
43073
+ error: "rgb(255,102,102)",
43074
+ permission: "rgb(153,204,255)",
43075
+ selected: "rgb(153,204,255)",
43076
+ current: "rgb(255,204,0)",
43077
+ promptBorder: "rgb(136,136,136)",
43078
+ rule: "rgb(102,153,153)",
43079
+ userChevron: "rgb(136,136,136)",
43080
+ userBg: "rgb(55,55,55)",
43081
+ diffAddBg: "rgb(0,68,102)",
43082
+ diffDelBg: "rgb(102,0,0)",
43083
+ diffAddFg: "rgb(0,119,179)",
43084
+ diffDelFg: "rgb(179,0,0)",
43085
+ syntax: {
43086
+ keyword: "rgb(255,178,102)",
43087
+ string: "rgb(153,204,255)",
43088
+ number: "rgb(255,255,102)",
43089
+ literal: "rgb(255,255,102)",
43090
+ comment: "rgb(153,153,153)",
43091
+ function: "rgb(178,102,255)",
43092
+ type: "rgb(255,153,51)",
43093
+ variable: "rgb(255,178,102)",
43094
+ tag: "rgb(102,178,255)",
43095
+ attr: "rgb(153,204,255)",
43096
+ meta: "rgb(178,102,255)",
43097
+ addition: "rgb(102,178,255)",
43098
+ deletion: "rgb(255,102,102)",
43099
+ default: "rgb(255,255,255)"
43100
+ }
43101
+ };
43102
+ var lightDaltonizedTheme = {
43103
+ text: "rgb(0,0,0)",
43104
+ muted: "rgb(102,102,102)",
43105
+ subtle: "rgb(175,175,175)",
43106
+ brand: "rgb(255,153,51)",
43107
+ info: "rgb(51,102,255)",
43108
+ success: "rgb(0,102,153)",
43109
+ warning: "rgb(255,153,0)",
43110
+ error: "rgb(204,0,0)",
43111
+ permission: "rgb(51,102,255)",
43112
+ selected: "rgb(51,102,255)",
43113
+ current: "rgb(255,153,0)",
43114
+ promptBorder: "rgb(153,153,153)",
43115
+ rule: "rgb(51,102,102)",
43116
+ userChevron: "rgb(153,153,153)",
43117
+ userBg: "rgb(220,220,220)",
43118
+ diffAddBg: "rgb(153,204,255)",
43119
+ diffDelBg: "rgb(255,204,204)",
43120
+ diffAddFg: "rgb(51,102,204)",
43121
+ diffDelFg: "rgb(153,51,51)",
43122
+ syntax: {
43123
+ keyword: "rgb(204,0,0)",
43124
+ string: "rgb(0,102,204)",
43125
+ number: "rgb(0,102,204)",
43126
+ literal: "rgb(0,102,204)",
43127
+ comment: "rgb(102,102,102)",
43128
+ function: "rgb(128,0,128)",
43129
+ type: "rgb(255,128,0)",
43130
+ variable: "rgb(255,128,0)",
43131
+ tag: "rgb(0,102,204)",
43132
+ attr: "rgb(0,102,204)",
43133
+ meta: "rgb(128,0,128)",
43134
+ addition: "rgb(0,102,153)",
43135
+ deletion: "rgb(204,0,0)",
43136
+ default: "rgb(0,0,0)"
43137
+ }
43138
+ };
43065
43139
  var darkAnsiTheme = {
43066
- text: "default",
43067
- muted: "dim",
43068
- subtle: "dim",
43069
- brand: "ansi(yellow)",
43070
- info: "ansi(cyan)",
43071
- success: "ansi(green)",
43072
- warning: "ansi(yellow)",
43073
- error: "ansi(red)",
43074
- permission: "ansi(blue)",
43075
- selected: "ansi(blue)",
43076
- current: "ansi(yellow)",
43077
- promptBorder: "dim",
43078
- rule: "dim",
43079
- userChevron: "dim",
43140
+ text: "ansi(whiteBright)",
43141
+ muted: "ansi(white)",
43142
+ subtle: "ansi(white)",
43143
+ brand: "ansi(redBright)",
43144
+ info: "ansi(blueBright)",
43145
+ success: "ansi(greenBright)",
43146
+ warning: "ansi(yellowBright)",
43147
+ error: "ansi(redBright)",
43148
+ permission: "ansi(blueBright)",
43149
+ selected: "ansi(blueBright)",
43150
+ current: "ansi(yellowBright)",
43151
+ promptBorder: "ansi(white)",
43152
+ rule: "ansi(cyanBright)",
43153
+ userChevron: "ansi(white)",
43080
43154
  userBg: "ansi(bgBlackBright)",
43081
43155
  diffAddBg: "default",
43082
43156
  diffDelBg: "default",
43083
- diffAddFg: "ansi(green)",
43084
- diffDelFg: "ansi(red)",
43157
+ diffAddFg: "ansi(greenBright)",
43158
+ diffDelFg: "ansi(redBright)",
43085
43159
  syntax: {
43086
- keyword: "ansi(red)",
43160
+ keyword: "ansi(redBright)",
43087
43161
  string: "ansi(cyan)",
43088
43162
  number: "ansi(cyan)",
43089
43163
  literal: "ansi(cyan)",
43090
43164
  comment: "ansi(gray)",
43091
- function: "ansi(magenta)",
43092
- type: "ansi(yellow)",
43093
- variable: "ansi(yellow)",
43094
- tag: "ansi(green)",
43165
+ function: "ansi(magentaBright)",
43166
+ type: "ansi(yellowBright)",
43167
+ variable: "ansi(yellowBright)",
43168
+ tag: "ansi(greenBright)",
43095
43169
  attr: "ansi(cyan)",
43096
- meta: "ansi(blue)",
43097
- addition: "ansi(green)",
43098
- deletion: "ansi(red)",
43099
- default: "ansi(white)"
43170
+ meta: "ansi(blueBright)",
43171
+ addition: "ansi(greenBright)",
43172
+ deletion: "ansi(redBright)",
43173
+ default: "ansi(whiteBright)"
43100
43174
  }
43101
43175
  };
43102
43176
  var lightAnsiTheme = {
43103
- text: "default",
43104
- muted: "dim",
43105
- subtle: "dim",
43106
- brand: "ansi(yellow)",
43177
+ text: "ansi(black)",
43178
+ muted: "ansi(blackBright)",
43179
+ subtle: "ansi(blackBright)",
43180
+ brand: "ansi(redBright)",
43107
43181
  info: "ansi(blue)",
43108
43182
  success: "ansi(green)",
43109
43183
  warning: "ansi(yellow)",
@@ -43111,14 +43185,14 @@ var lightAnsiTheme = {
43111
43185
  permission: "ansi(blue)",
43112
43186
  selected: "ansi(blue)",
43113
43187
  current: "ansi(yellow)",
43114
- promptBorder: "dim",
43115
- rule: "dim",
43116
- userChevron: "dim",
43188
+ promptBorder: "ansi(white)",
43189
+ rule: "ansi(cyan)",
43190
+ userChevron: "ansi(blackBright)",
43117
43191
  userBg: "ansi(bgWhiteBright)",
43118
43192
  diffAddBg: "default",
43119
43193
  diffDelBg: "default",
43120
- diffAddFg: "ansi(green)",
43121
- diffDelFg: "ansi(red)",
43194
+ diffAddFg: "ansi(greenBright)",
43195
+ diffDelFg: "ansi(redBright)",
43122
43196
  syntax: {
43123
43197
  keyword: "ansi(red)",
43124
43198
  string: "ansi(blue)",
@@ -43133,7 +43207,44 @@ var lightAnsiTheme = {
43133
43207
  meta: "ansi(blue)",
43134
43208
  addition: "ansi(green)",
43135
43209
  deletion: "ansi(red)",
43136
- default: "default"
43210
+ default: "ansi(black)"
43211
+ }
43212
+ };
43213
+ var noColorTheme = {
43214
+ text: "rgb(255,255,255)",
43215
+ muted: "rgb(255,255,255)",
43216
+ subtle: "rgb(255,255,255)",
43217
+ brand: "rgb(255,255,255)",
43218
+ info: "rgb(255,255,255)",
43219
+ success: "rgb(255,255,255)",
43220
+ warning: "rgb(255,255,255)",
43221
+ error: "rgb(255,255,255)",
43222
+ permission: "rgb(255,255,255)",
43223
+ selected: "rgb(255,255,255)",
43224
+ current: "rgb(255,255,255)",
43225
+ promptBorder: "rgb(255,255,255)",
43226
+ rule: "rgb(255,255,255)",
43227
+ userChevron: "rgb(255,255,255)",
43228
+ userBg: "default",
43229
+ diffAddBg: "default",
43230
+ diffDelBg: "default",
43231
+ diffAddFg: "rgb(255,255,255)",
43232
+ diffDelFg: "rgb(255,255,255)",
43233
+ syntax: {
43234
+ keyword: "rgb(255,255,255)",
43235
+ string: "rgb(255,255,255)",
43236
+ number: "rgb(255,255,255)",
43237
+ literal: "rgb(255,255,255)",
43238
+ comment: "rgb(255,255,255)",
43239
+ function: "rgb(255,255,255)",
43240
+ type: "rgb(255,255,255)",
43241
+ variable: "rgb(255,255,255)",
43242
+ tag: "rgb(255,255,255)",
43243
+ attr: "rgb(255,255,255)",
43244
+ meta: "rgb(255,255,255)",
43245
+ addition: "rgb(255,255,255)",
43246
+ deletion: "rgb(255,255,255)",
43247
+ default: "rgb(255,255,255)"
43137
43248
  }
43138
43249
  };
43139
43250
  var builtInThemes = [
@@ -43151,6 +43262,20 @@ var builtInThemes = [
43151
43262
  theme: lightTheme,
43152
43263
  source: "built-in"
43153
43264
  },
43265
+ {
43266
+ name: "dark-daltonized",
43267
+ label: "Dark Daltonized",
43268
+ description: "Color-blind friendly dark theme adjusted for deuteranopia",
43269
+ theme: darkDaltonizedTheme,
43270
+ source: "built-in"
43271
+ },
43272
+ {
43273
+ name: "light-daltonized",
43274
+ label: "Light Daltonized",
43275
+ description: "Color-blind friendly light theme adjusted for deuteranopia",
43276
+ theme: lightDaltonizedTheme,
43277
+ source: "built-in"
43278
+ },
43154
43279
  {
43155
43280
  name: "dracula",
43156
43281
  label: "Dracula",
@@ -43171,6 +43296,13 @@ var builtInThemes = [
43171
43296
  description: "16-color ANSI light palette respecting terminal colors",
43172
43297
  theme: lightAnsiTheme,
43173
43298
  source: "built-in"
43299
+ },
43300
+ {
43301
+ name: "no-color",
43302
+ label: "No Color",
43303
+ description: "Monochrome pure white palette with no color styling",
43304
+ theme: noColorTheme,
43305
+ source: "built-in"
43174
43306
  }
43175
43307
  ];
43176
43308
  var registry = new Map;
@@ -43185,7 +43317,8 @@ function findTheme(query) {
43185
43317
  const q = query.trim().toLowerCase().replace(/^["']|["']$/g, "");
43186
43318
  if (!q)
43187
43319
  return;
43188
- return listThemes().find((t) => t.name.toLowerCase() === q || t.label.toLowerCase() === q || t.label.toLowerCase().startsWith(q));
43320
+ const normalizedQ = q.replace(/[\s-_]+/g, "");
43321
+ return listThemes().find((t) => t.name.toLowerCase() === q || t.label.toLowerCase() === q || t.label.toLowerCase().startsWith(q) || t.name.replace(/[\s-_]+/g, "") === normalizedQ || t.label.toLowerCase().replace(/[\s-_]+/g, "") === normalizedQ);
43189
43322
  }
43190
43323
  function setActiveTheme(name) {
43191
43324
  if (registry.has(name)) {
@@ -56001,7 +56134,7 @@ import { isIP } from "node:net";
56001
56134
  // package.json
56002
56135
  var package_default = {
56003
56136
  name: "steward-cli",
56004
- version: "0.27.0",
56137
+ version: "0.29.0",
56005
56138
  description: "Interactive AI engineering assistant for the terminal.",
56006
56139
  author: "sapirrior",
56007
56140
  license: "MIT",
@@ -58810,89 +58943,94 @@ async function recoverPendingCheckpoint(workspaceRoot, sessionId) {
58810
58943
  }
58811
58944
  }
58812
58945
  // src/packages/agents/src/engine/system-prompt.ts
58813
- function buildSystemPrompt(options = {}) {
58946
+ var DEFAULT_PREAMBLE = `You are Steward, an interactive software-engineering agent operating in a terminal workspace. Assist the user with building, refactoring, debugging, inspecting, and verifying software.
58947
+
58948
+ IMPORTANT: Assist with defensive security tasks only. Refuse to create, modify, or improve code intended for malicious use.`;
58949
+ function buildRules(options) {
58950
+ const rules = [];
58951
+ const seen = new Set;
58952
+ const addRule = (rule) => {
58953
+ const normalized = rule.trim();
58954
+ if (!normalized || seen.has(normalized))
58955
+ return;
58956
+ seen.add(normalized);
58957
+ rules.push(normalized);
58958
+ };
58959
+ addRule("Inspect before editing: investigate existing files, directories, and patterns before making changes.");
58960
+ addRule("Minimal and idiomatic: prefer the smallest correct change that aligns with existing repository architecture and conventions.");
58961
+ addRule("Never assume external dependencies or libraries exist; check project configuration files (e.g. package.json, Cargo.toml, pyproject.toml) first.");
58962
+ addRule("Security and secrets: never expose, commit, or log API keys, credentials, or secrets.");
58963
+ addRule("Verify your changes: run relevant project verification or tests after making non-trivial modifications.");
58964
+ addRule("Never commit git changes unless the user explicitly requests you to commit.");
58965
+ addRule("Provide direct, concise responses suitable for a terminal interface. Explain non-obvious failures or decisions clearly without unnecessary filler or commentary.");
58966
+ addRule("When referencing code locations, use the format: `file_path:line_number`.");
58967
+ addRule("Avoid adding redundant comments unless explaining non-obvious invariants or explicitly requested.");
58968
+ const mode = options.chatMode ?? "normal";
58969
+ if (mode !== "chat") {
58970
+ addRule("ALWAYS use write_file and edit_file to create or modify files rather than bash redirection (e.g. cat > file); write_file and edit_file participate in the file checkpoint and rewind system (/rewind).");
58971
+ addRule("When using bash, you MUST always provide a concise, non-empty 'explanation' parameter describing what the command does and why it is needed.");
58972
+ }
58973
+ return rules.map((r) => `- ${r}`).join(`
58974
+ `);
58975
+ }
58976
+ function buildSystemPromptSections(options = {}) {
58814
58977
  const cwd = options.cwd ?? process.cwd();
58815
58978
  const platform = process.platform;
58816
58979
  const dateUTC = new Date().toUTCString();
58817
- let prompt = `You are Steward, an interactive software-engineering agent operating in a terminal workspace. Assist the user with building, refactoring, debugging, inspecting, and verifying software.
58818
-
58819
- IMPORTANT: Assist with defensive security tasks only. Refuse to create, modify, or improve code intended for malicious use.
58820
-
58821
- # Operating Principles
58822
- - Inspect before editing: investigate existing files, directories, and patterns before making changes.
58823
- - Minimal and idiomatic: prefer the smallest correct change that aligns with existing repository architecture and conventions.
58824
- - Never assume external dependencies or libraries exist; check project configuration files (e.g. package.json, Cargo.toml, pyproject.toml) first.
58825
- - Security and secrets: never expose, commit, or log API keys, credentials, or secrets.
58826
- - Verify your changes: run relevant project verification or tests after making non-trivial modifications.
58827
- - Never commit git changes unless the user explicitly requests you to commit.
58828
- - Provide direct, concise responses suitable for a terminal interface. Explain non-obvious failures or decisions clearly without unnecessary filler or commentary.
58829
- - When referencing code locations, use the format: \`file_path:line_number\`.
58830
- - Avoid adding redundant comments unless explaining non-obvious invariants or explicitly requested.
58831
-
58832
- # Tool Usage Policy
58833
- - Investigation: use read_file, grep, list_dir, and glob to explore code before modifying it.
58834
- - Mutations: ALWAYS use write_file and edit_file to create or modify files rather than bash redirection (e.g. cat > file). write_file and edit_file participate in the file checkpoint and rewind system (/rewind), whereas bash commands are not tracked by checkpoints.
58835
- - Commands: when using bash, you MUST always provide a concise, non-empty 'explanation' parameter describing what the command does and why it is needed.
58836
- - Background tasks: use task_list to inspect active and recent tasks, task_read to check status/output, task_send_input to send standard input (with a trailing newline), and task_kill to terminate.
58837
- - Web: use web_fetch and web_search when external documentation or live data is needed. If web_fetch indicates a redirect, follow up with the target URL.
58838
-
58839
- # Specialized Skills Policy
58840
- - Skills are discoverable on-demand packages of specialized instructions located in workspace or user configuration.
58841
- - Use skill_list (SkillList) to discover available skills and their descriptions.
58842
- - Use skill_read (SkillRead) to load a skill's instructions (SKILL.md) or relative resources only when a task requires specialized domain guidance.
58843
- - Skill instructions are supplemental and must not override core safety rules, tool boundaries, or user permission constraints.
58844
-
58845
- # Lifecycle Hooks Policy
58846
- - The active workspace or user configuration may define deterministic lifecycle hooks (\`.steward/hooks.json\` or \`~/.steward/hooks.json\`) that inspect, augment, or block actions.
58980
+ const mode = options.chatMode ?? "normal";
58981
+ const sections = {};
58982
+ sections.preamble = DEFAULT_PREAMBLE;
58983
+ sections.rules = `# Operating Principles
58984
+ ${buildRules(options)}`;
58985
+ if (mode !== "chat") {
58986
+ sections.skills = `# Specialized Skills Policy
58987
+ - Use skill_list (SkillList) to discover available skills and skill_read (SkillRead) to load specialized instructions on-demand when domain guidance is needed.
58988
+ - Skill instructions are supplemental and must not override core safety rules, tool boundaries, or user permission constraints.`;
58989
+ sections.hooks = `# Lifecycle Hooks Policy
58990
+ - The active workspace or user configuration may define deterministic lifecycle hooks (.steward/hooks.json or ~/.steward/hooks.json) that inspect, augment, or block actions.
58847
58991
  - When a tool call is blocked by a hook, treat the block reason as authoritative workspace policy, do not attempt to force or repeat the blocked action, and adjust your plan or inform the user.
58848
- - When hook guidance or continuation instructions (\`[Hook Context]\` or \`[AgentStop Continuation]\`) are injected into the context, follow that guidance to perform any requested verification or adjustments.
58849
-
58850
- <env>
58992
+ - When hook guidance or continuation instructions ([Hook Context] or [AgentStop Continuation]) are injected into the context, follow that guidance to perform any requested verification or adjustments.`;
58993
+ }
58994
+ sections.env = `<env>
58851
58995
  Working directory: ${cwd}
58852
58996
  Platform: ${platform}
58853
58997
  Today's date: ${dateUTC}
58854
58998
  </env>`;
58855
- if (options.userRules && options.userRules.length > 0) {
58856
- prompt += `
58857
-
58858
- <user_defined_rules>
58859
- The following rules are configured by the user and must be followed:
58860
- ${options.userRules.map((rule) => `- ${rule}`).join(`
58861
- `)}
58862
- </user_defined_rules>`;
58863
- }
58864
58999
  if (options.extraInstructions) {
58865
- prompt += `
58866
-
58867
- <additional_instructions>
59000
+ sections.additional_instructions = `<additional_instructions>
58868
59001
  ${options.extraInstructions}
58869
59002
  </additional_instructions>`;
58870
59003
  }
58871
- if (options.chatMode && options.chatMode !== "normal") {
58872
- const meta = MODES[options.chatMode];
59004
+ if (mode !== "normal") {
59005
+ const meta = MODES[mode];
58873
59006
  if (meta) {
58874
- if (options.chatMode === "chat") {
58875
- prompt += `
58876
-
58877
- <operating_mode>
59007
+ if (mode === "chat") {
59008
+ sections.mode = `<operating_mode>
58878
59009
  Mode: CHAT — You have no tool access. Respond conversationally only. Do not attempt to call any tools.
58879
59010
  </operating_mode>`;
58880
- } else if (options.chatMode === "review") {
58881
- prompt += `
58882
-
58883
- <operating_mode>
59011
+ } else if (mode === "review") {
59012
+ sections.mode = `<operating_mode>
58884
59013
  Mode: REVIEW — You may only use read-only tools. Any attempt to write files, run commands, or mutate state will be blocked.
58885
59014
  </operating_mode>`;
58886
- } else if (options.chatMode === "build") {
58887
- prompt += `
58888
-
58889
- <operating_mode>
59015
+ } else if (mode === "build") {
59016
+ sections.mode = `<operating_mode>
58890
59017
  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.
58891
59018
  </operating_mode>`;
58892
59019
  }
58893
59020
  }
58894
59021
  }
58895
- return prompt;
59022
+ return sections;
59023
+ }
59024
+ function buildSystemPrompt(options = {}) {
59025
+ const sections = buildSystemPromptSections(options);
59026
+ const parts = [];
59027
+ for (const [key, content] of Object.entries(sections)) {
59028
+ if (content)
59029
+ parts.push(content);
59030
+ }
59031
+ return parts.join(`
59032
+
59033
+ `);
58896
59034
  }
58897
59035
 
58898
59036
  // src/packages/agents/src/engine/turn-context.ts
@@ -59464,6 +59602,19 @@ class AgentSession {
59464
59602
  }
59465
59603
  }
59466
59604
 
59605
+ // src/app/commands/bug/index.ts
59606
+ var bugCommand = {
59607
+ name: "bug",
59608
+ description: "Report issues or submit feedback to GitHub",
59609
+ usage: "/bug",
59610
+ async execute(_args, _context) {
59611
+ return {
59612
+ handled: true,
59613
+ message: "Report issues or feedback to https://github.com/sapirrior/steward/issues"
59614
+ };
59615
+ }
59616
+ };
59617
+
59467
59618
  // src/app/commands/clear/index.ts
59468
59619
  var clearCommand = {
59469
59620
  name: "clear",
@@ -59897,28 +60048,6 @@ function formatDuration(isoDateString) {
59897
60048
  return `${mins}m ${secs}s`;
59898
60049
  return `${secs}s`;
59899
60050
  }
59900
- function getContextWindowLimit(modelId, provider) {
59901
- const m = modelId.toLowerCase();
59902
- const p = provider.toLowerCase();
59903
- if (p === "gemini" || m.includes("gemini"))
59904
- return 1e6;
59905
- if (p === "anthropic" || m.includes("claude"))
59906
- return 200000;
59907
- if (m.includes("deepseek"))
59908
- return 64000;
59909
- if (m.includes("gpt-4") || m.includes("o1") || m.includes("o3") || m.includes("llama"))
59910
- return 128000;
59911
- return 128000;
59912
- }
59913
- function renderProgressBar(current, max, length = 16) {
59914
- if (max <= 0)
59915
- return `[${"░".repeat(length)}] 0%`;
59916
- const ratio = Math.min(Math.max(current / max, 0), 1);
59917
- const percent = Math.round(ratio * 100);
59918
- const filled = Math.round(ratio * length);
59919
- const empty = length - filled;
59920
- return `[${"█".repeat(filled)}${"░".repeat(empty)}] ${percent}%`;
59921
- }
59922
60051
  function estimateUniversalCost(usage, modelId, provider) {
59923
60052
  const p = provider.toLowerCase();
59924
60053
  const m = modelId.toLowerCase();
@@ -59952,17 +60081,18 @@ var usageCommand = {
59952
60081
  let toolCallsCount = 0;
59953
60082
  for (const turn of turns) {
59954
60083
  if (Array.isArray(turn.messages)) {
60084
+ let turnToolCalls = 0;
60085
+ let turnToolMessages = 0;
59955
60086
  for (const msg of turn.messages) {
59956
- if (msg.role === "tool") {
59957
- toolCallsCount++;
59958
- } else if (msg.role === "assistant" && Array.isArray(msg.content)) {
59959
- toolCallsCount += msg.content.filter((c) => c?.type === "tool-call").length;
60087
+ if (msg.role === "assistant" && Array.isArray(msg.content)) {
60088
+ turnToolCalls += msg.content.filter((c) => c?.type === "tool-call").length;
60089
+ } else if (msg.role === "tool") {
60090
+ turnToolMessages++;
59960
60091
  }
59961
60092
  }
60093
+ toolCallsCount += turnToolCalls > 0 ? turnToolCalls : turnToolMessages;
59962
60094
  }
59963
60095
  }
59964
- const contextLimit = getContextWindowLimit(model.modelId, model.provider);
59965
- const contextBar = renderProgressBar(usage.totalTokens, contextLimit);
59966
60096
  const duration = formatDuration(sessionData?.createdAt);
59967
60097
  const costEstimate = estimateUniversalCost(usage, model.modelId, model.provider);
59968
60098
  const lines = [
@@ -59970,7 +60100,6 @@ var usageCommand = {
59970
60100
  `• Active Model: ${model.modelId} (${model.provider}${model.effort !== "medium" ? `, effort: ${model.effort}` : ""})`,
59971
60101
  `• Session Duration: ${duration}`,
59972
60102
  `• Activity: ${formatNumber(turnsCount)} turns (${formatNumber(toolCallsCount)} tool executions)`,
59973
- `• Context Window: ${contextBar} (${formatNumber(usage.totalTokens)} / ${formatNumber(contextLimit)} tokens)`,
59974
60103
  ``,
59975
60104
  `Token Breakdown:`,
59976
60105
  `• Input Tokens: ${formatNumber(usage.inputTokens)}`,
@@ -60468,6 +60597,7 @@ class CommandRegistry {
60468
60597
  }
60469
60598
  }
60470
60599
  var builtInCommands = [
60600
+ bugCommand,
60471
60601
  initCommand,
60472
60602
  copyCommand,
60473
60603
  exportCommand,
@@ -61121,7 +61251,6 @@ class StatusBar extends Component_default {
61121
61251
  wrap = false;
61122
61252
  clip = true;
61123
61253
  warningTimer = null;
61124
- updateTimer = null;
61125
61254
  constructor(props) {
61126
61255
  super(props);
61127
61256
  this.state = {
@@ -61130,8 +61259,7 @@ class StatusBar extends Component_default {
61130
61259
  isBusy: props.isBusy,
61131
61260
  exitPending: props.exitPending,
61132
61261
  isBashMode: props.isBashMode,
61133
- warning: props.warning,
61134
- updateStatus: props.updateStatus
61262
+ warning: props.warning
61135
61263
  };
61136
61264
  }
61137
61265
  setBashMode(isBashMode) {
@@ -61145,21 +61273,6 @@ class StatusBar extends Component_default {
61145
61273
  update(partial) {
61146
61274
  this.setState(partial);
61147
61275
  }
61148
- setUpdateStatus(updateStatus, durationMs) {
61149
- if (this.updateTimer) {
61150
- clearTimeout(this.updateTimer);
61151
- this.updateTimer = null;
61152
- }
61153
- this.setState({ updateStatus });
61154
- if (updateStatus && durationMs && durationMs > 0) {
61155
- this.updateTimer = setTimeout(() => {
61156
- this.updateTimer = null;
61157
- if (this.state.updateStatus === updateStatus) {
61158
- this.setState({ updateStatus: undefined });
61159
- }
61160
- }, durationMs);
61161
- }
61162
- }
61163
61276
  showWarning(warningText, durationMs = 4000) {
61164
61277
  if (this.warningTimer) {
61165
61278
  clearTimeout(this.warningTimer);
@@ -61178,15 +61291,11 @@ class StatusBar extends Component_default {
61178
61291
  clearTimeout(this.warningTimer);
61179
61292
  this.warningTimer = null;
61180
61293
  }
61181
- if (this.updateTimer) {
61182
- clearTimeout(this.updateTimer);
61183
- this.updateTimer = null;
61184
- }
61185
61294
  }
61186
61295
  render(width) {
61187
61296
  const termWidth = width ?? process.stdout.columns ?? 80;
61188
61297
  const maxCols = Math.max(1, termWidth);
61189
- const { model, isBusy, exitPending, isBashMode, warning, updateStatus, chatMode } = this.state;
61298
+ const { model, isBusy, exitPending, isBashMode, warning, chatMode } = this.state;
61190
61299
  let left = "";
61191
61300
  if (warning) {
61192
61301
  left = c.warning(warning);
@@ -61196,18 +61305,6 @@ class StatusBar extends Component_default {
61196
61305
  left = c.muted("esc to interrupt");
61197
61306
  } else if (isBashMode) {
61198
61307
  left = c.permission("! for bash mode");
61199
- } else if (updateStatus && updateStatus.state !== "idle") {
61200
- if (updateStatus.state === "checking") {
61201
- left = c.text(updateStatus.message ?? "Checking for updates...");
61202
- } else if (updateStatus.state === "available") {
61203
- left = c.text(updateStatus.message ?? `Update v${updateStatus.version ?? ""} is available`);
61204
- } else if (updateStatus.state === "no-updates") {
61205
- left = c.text(updateStatus.message ?? "Steward is up to date");
61206
- } else if (updateStatus.state === "error" && updateStatus.message) {
61207
- left = c.error(updateStatus.message);
61208
- } else {
61209
- left = c.muted("? for shortcuts");
61210
- }
61211
61308
  } else {
61212
61309
  left = c.muted("? for shortcuts");
61213
61310
  }
@@ -64224,11 +64321,12 @@ function renderToolDetail(detail, targetWidth, expanded = false) {
64224
64321
  }
64225
64322
 
64226
64323
  // src/app/ui/utils/message-formatter.ts
64227
- function formatSystemMessage(content) {
64324
+ function formatSlashCommandOutput(content) {
64228
64325
  const rawLines = content.split(`
64229
64326
  `);
64230
- return rawLines.map((l, i) => i === 0 ? ` ${c.muted("└ ")}${c.permission(l)}` : ` ${c.permission(l)}`);
64327
+ return rawLines.map((l, i) => i === 0 ? ` ${c.muted("└ ")}${l}` : ` ${l}`);
64231
64328
  }
64329
+ var formatSystemMessage = formatSlashCommandOutput;
64232
64330
  function formatAssistantMessage(content) {
64233
64331
  const lines = [];
64234
64332
  if (content) {
@@ -64439,163 +64537,7 @@ function renderTranscript(engine, sessionData, header, options) {
64439
64537
  formatTurnFooter(engine, lastTurnPresentation);
64440
64538
  }
64441
64539
  }
64442
- // src/packages/services/src/updater/service.ts
64443
- var DEFAULT_REPO = "sapirrior/steward";
64444
- function parseSemver(v) {
64445
- if (!v || typeof v !== "string") {
64446
- return [0, 0, 0];
64447
- }
64448
- const clean = v.replace(/^v/i, "").trim().split("-")[0] ?? "";
64449
- const parts = clean.split(".").map((p) => parseInt(p, 10) || 0);
64450
- return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];
64451
- }
64452
- function isNewerVersion(current, latest) {
64453
- const [cMaj, cMin, cPat] = parseSemver(current);
64454
- const [lMaj, lMin, lPat] = parseSemver(latest);
64455
- if (lMaj > cMaj)
64456
- return true;
64457
- if (lMaj < cMaj)
64458
- return false;
64459
- if (lMin > cMin)
64460
- return true;
64461
- if (lMin < cMin)
64462
- return false;
64463
- return lPat > cPat;
64464
- }
64465
64540
 
64466
- class UpdateCheckerService {
64467
- currentVersion;
64468
- repo;
64469
- state = "idle";
64470
- options;
64471
- checkTimer = null;
64472
- resetTimer = null;
64473
- constructor(options = {}) {
64474
- this.options = options;
64475
- this.currentVersion = options.currentVersion || package_default.version || "0.0.0";
64476
- this.repo = options.repo || DEFAULT_REPO;
64477
- }
64478
- getState() {
64479
- return this.state;
64480
- }
64481
- getRepo() {
64482
- return this.repo;
64483
- }
64484
- getCurrentVersion() {
64485
- return this.currentVersion;
64486
- }
64487
- get rawPackageJsonUrl() {
64488
- return `https://raw.githubusercontent.com/${this.repo}/main/package.json`;
64489
- }
64490
- setState(state, info) {
64491
- this.state = state;
64492
- this.options.onStatusChange?.(state, info);
64493
- }
64494
- async checkForUpdates() {
64495
- if (process.env.STEWARD_NO_UPDATE_CHECK === "1" || process.env.STEWARD_NO_UPDATE_CHECK === "true") {
64496
- return null;
64497
- }
64498
- if (this.resetTimer) {
64499
- clearTimeout(this.resetTimer);
64500
- this.resetTimer = null;
64501
- }
64502
- try {
64503
- this.setState("checking", { message: "Checking for updates..." });
64504
- const checkStartTime = Date.now();
64505
- const controller = new AbortController;
64506
- const timeout = setTimeout(() => controller.abort(), this.options.timeoutMs ?? 1e4);
64507
- const response = await fetch(this.rawPackageJsonUrl, {
64508
- headers: {
64509
- "User-Agent": `steward-cli/${this.currentVersion}`,
64510
- Accept: "application/json"
64511
- },
64512
- signal: controller.signal
64513
- });
64514
- clearTimeout(timeout);
64515
- const elapsed = Date.now() - checkStartTime;
64516
- const targetMinDisplay = this.options.minDisplayMs !== undefined ? this.options.minDisplayMs : 1000;
64517
- const minDisplayDelay = Math.max(0, targetMinDisplay - elapsed);
64518
- if (minDisplayDelay > 0) {
64519
- await new Promise((resolve) => setTimeout(resolve, minDisplayDelay));
64520
- }
64521
- if (!response.ok) {
64522
- this.setState("error", { message: "Failed to check for updates" });
64523
- this.resetTimer = setTimeout(() => {
64524
- if (this.state === "error")
64525
- this.setState("idle");
64526
- }, 3000);
64527
- return null;
64528
- }
64529
- const data = await response.json();
64530
- if (!data || typeof data !== "object" || typeof data.version !== "string") {
64531
- this.setState("error", { message: "Invalid upstream version response" });
64532
- this.resetTimer = setTimeout(() => {
64533
- if (this.state === "error")
64534
- this.setState("idle");
64535
- }, 3000);
64536
- return null;
64537
- }
64538
- const latestVersion = data.version.replace(/^v/i, "").trim();
64539
- if (!latestVersion) {
64540
- this.setState("error", { message: "Empty upstream version response" });
64541
- this.resetTimer = setTimeout(() => {
64542
- if (this.state === "error")
64543
- this.setState("idle");
64544
- }, 3000);
64545
- return null;
64546
- }
64547
- const hasUpdate = isNewerVersion(this.currentVersion, latestVersion);
64548
- const updateInfo = {
64549
- currentVersion: this.currentVersion,
64550
- latestVersion,
64551
- hasUpdate
64552
- };
64553
- if (hasUpdate) {
64554
- this.setState("available", {
64555
- version: latestVersion,
64556
- message: `Update v${latestVersion} is available`
64557
- });
64558
- } else {
64559
- this.setState("no-updates", {
64560
- version: this.currentVersion,
64561
- message: `Steward is up to date (v${this.currentVersion})`
64562
- });
64563
- this.resetTimer = setTimeout(() => {
64564
- if (this.state === "no-updates")
64565
- this.setState("idle");
64566
- }, 3500);
64567
- }
64568
- return updateInfo;
64569
- } catch (err) {
64570
- logError(err, { component: "UpdateCheckerService", method: "checkForUpdates" });
64571
- this.setState("error", { message: "Failed to check for updates" });
64572
- this.resetTimer = setTimeout(() => {
64573
- if (this.state === "error")
64574
- this.setState("idle");
64575
- }, 3000);
64576
- return null;
64577
- }
64578
- }
64579
- startBackgroundCheck(initialDelayMs = 2000) {
64580
- if (this.checkTimer) {
64581
- clearTimeout(this.checkTimer);
64582
- }
64583
- this.checkTimer = setTimeout(async () => {
64584
- this.checkTimer = null;
64585
- await this.checkForUpdates();
64586
- }, initialDelayMs);
64587
- }
64588
- dispose() {
64589
- if (this.checkTimer) {
64590
- clearTimeout(this.checkTimer);
64591
- this.checkTimer = null;
64592
- }
64593
- if (this.resetTimer) {
64594
- clearTimeout(this.resetTimer);
64595
- this.resetTimer = null;
64596
- }
64597
- }
64598
- }
64599
64541
  // src/app/utils/open-url.ts
64600
64542
  import { spawn as spawn5 } from "node:child_process";
64601
64543
  import { existsSync as existsSync20 } from "node:fs";
@@ -66411,7 +66353,7 @@ async function applyCommandResult(text, cmdResult, ctx) {
66411
66353
  return true;
66412
66354
  }
66413
66355
  if (cmdResult.message) {
66414
- ctx.engine.commit("system", formatSystemMessage(cmdResult.message));
66356
+ ctx.engine.commit("system", formatSlashCommandOutput(cmdResult.message));
66415
66357
  }
66416
66358
  const updatedModel = ctx.session.getModel();
66417
66359
  ctx.header.render();
@@ -66519,7 +66461,6 @@ class TUIApp {
66519
66461
  ctrlCTimer = null;
66520
66462
  isBusy = false;
66521
66463
  isScrollViewMode = false;
66522
- updateChecker;
66523
66464
  permissionQueue;
66524
66465
  directBashAbortController = null;
66525
66466
  constructor(options = {}) {
@@ -66567,32 +66508,6 @@ class TUIApp {
66567
66508
  streamingView: this.streamingView,
66568
66509
  cwd: this.cwd
66569
66510
  });
66570
- this.updateChecker = new UpdateCheckerService({
66571
- currentVersion: options.version,
66572
- onStatusChange: (state, info) => {
66573
- if (state === "idle") {
66574
- this.statusBar.setUpdateStatus(undefined);
66575
- } else if (state === "available") {
66576
- this.statusBar.setUpdateStatus({
66577
- state,
66578
- version: info?.version,
66579
- message: info?.message
66580
- }, 8000);
66581
- } else if (state === "error") {
66582
- this.statusBar.setUpdateStatus({
66583
- state,
66584
- version: info?.version,
66585
- message: info?.message
66586
- }, 4000);
66587
- } else {
66588
- this.statusBar.setUpdateStatus({
66589
- state,
66590
- version: info?.version,
66591
- message: info?.message
66592
- });
66593
- }
66594
- }
66595
- });
66596
66511
  this.permissionQueue = new PermissionQueue({
66597
66512
  onShow: (item) => {
66598
66513
  if (this.modals.getActiveModal())
@@ -66660,7 +66575,6 @@ class TUIApp {
66660
66575
  this.engine.mount(this.streamingView, { kind: "custom" });
66661
66576
  this.engine.mount(this.promptInput, { keepCursorVisible: true, kind: "input" });
66662
66577
  this.engine.mount(this.statusBar, { kind: "custom" });
66663
- this.updateChecker.startBackgroundCheck(3000);
66664
66578
  this.engine.addInputListener((chunk) => {
66665
66579
  const action = parseKeyInput(chunk);
66666
66580
  if (action.type === "ctrl-c") {
@@ -66912,7 +66826,6 @@ class TUIApp {
66912
66826
  exit() {
66913
66827
  this.permissionQueue.clear();
66914
66828
  this.session.shutdown().catch(() => {});
66915
- this.updateChecker.dispose();
66916
66829
  this.engine.cleanupSync();
66917
66830
  if (this.onExitCallback) {
66918
66831
  this.onExitCallback();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "steward-cli",
3
- "version": "0.27.0",
3
+ "version": "0.29.0",
4
4
  "description": "Interactive AI engineering assistant for the terminal.",
5
5
  "license": "MIT",
6
6
  "author": "sapirrior",