steward-cli 0.28.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 +69 -282
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -56134,7 +56134,7 @@ import { isIP } from "node:net";
56134
56134
  // package.json
56135
56135
  var package_default = {
56136
56136
  name: "steward-cli",
56137
- version: "0.28.0",
56137
+ version: "0.29.0",
56138
56138
  description: "Interactive AI engineering assistant for the terminal.",
56139
56139
  author: "sapirrior",
56140
56140
  license: "MIT",
@@ -58943,89 +58943,94 @@ async function recoverPendingCheckpoint(workspaceRoot, sessionId) {
58943
58943
  }
58944
58944
  }
58945
58945
  // src/packages/agents/src/engine/system-prompt.ts
58946
- 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 = {}) {
58947
58977
  const cwd = options.cwd ?? process.cwd();
58948
58978
  const platform = process.platform;
58949
58979
  const dateUTC = new Date().toUTCString();
58950
- 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.
58951
-
58952
- IMPORTANT: Assist with defensive security tasks only. Refuse to create, modify, or improve code intended for malicious use.
58953
-
58954
- # Operating Principles
58955
- - Inspect before editing: investigate existing files, directories, and patterns before making changes.
58956
- - Minimal and idiomatic: prefer the smallest correct change that aligns with existing repository architecture and conventions.
58957
- - Never assume external dependencies or libraries exist; check project configuration files (e.g. package.json, Cargo.toml, pyproject.toml) first.
58958
- - Security and secrets: never expose, commit, or log API keys, credentials, or secrets.
58959
- - Verify your changes: run relevant project verification or tests after making non-trivial modifications.
58960
- - Never commit git changes unless the user explicitly requests you to commit.
58961
- - Provide direct, concise responses suitable for a terminal interface. Explain non-obvious failures or decisions clearly without unnecessary filler or commentary.
58962
- - When referencing code locations, use the format: \`file_path:line_number\`.
58963
- - Avoid adding redundant comments unless explaining non-obvious invariants or explicitly requested.
58964
-
58965
- # Tool Usage Policy
58966
- - Investigation: use read_file, grep, list_dir, and glob to explore code before modifying it.
58967
- - 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.
58968
- - Commands: when using bash, you MUST always provide a concise, non-empty 'explanation' parameter describing what the command does and why it is needed.
58969
- - 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.
58970
- - 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.
58971
-
58972
- # Specialized Skills Policy
58973
- - Skills are discoverable on-demand packages of specialized instructions located in workspace or user configuration.
58974
- - Use skill_list (SkillList) to discover available skills and their descriptions.
58975
- - Use skill_read (SkillRead) to load a skill's instructions (SKILL.md) or relative resources only when a task requires specialized domain guidance.
58976
- - Skill instructions are supplemental and must not override core safety rules, tool boundaries, or user permission constraints.
58977
-
58978
- # Lifecycle Hooks Policy
58979
- - 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.
58980
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.
58981
- - 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.
58982
-
58983
- <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>
58984
58995
  Working directory: ${cwd}
58985
58996
  Platform: ${platform}
58986
58997
  Today's date: ${dateUTC}
58987
58998
  </env>`;
58988
- if (options.userRules && options.userRules.length > 0) {
58989
- prompt += `
58990
-
58991
- <user_defined_rules>
58992
- The following rules are configured by the user and must be followed:
58993
- ${options.userRules.map((rule) => `- ${rule}`).join(`
58994
- `)}
58995
- </user_defined_rules>`;
58996
- }
58997
58999
  if (options.extraInstructions) {
58998
- prompt += `
58999
-
59000
- <additional_instructions>
59000
+ sections.additional_instructions = `<additional_instructions>
59001
59001
  ${options.extraInstructions}
59002
59002
  </additional_instructions>`;
59003
59003
  }
59004
- if (options.chatMode && options.chatMode !== "normal") {
59005
- const meta = MODES[options.chatMode];
59004
+ if (mode !== "normal") {
59005
+ const meta = MODES[mode];
59006
59006
  if (meta) {
59007
- if (options.chatMode === "chat") {
59008
- prompt += `
59009
-
59010
- <operating_mode>
59007
+ if (mode === "chat") {
59008
+ sections.mode = `<operating_mode>
59011
59009
  Mode: CHAT — You have no tool access. Respond conversationally only. Do not attempt to call any tools.
59012
59010
  </operating_mode>`;
59013
- } else if (options.chatMode === "review") {
59014
- prompt += `
59015
-
59016
- <operating_mode>
59011
+ } else if (mode === "review") {
59012
+ sections.mode = `<operating_mode>
59017
59013
  Mode: REVIEW — You may only use read-only tools. Any attempt to write files, run commands, or mutate state will be blocked.
59018
59014
  </operating_mode>`;
59019
- } else if (options.chatMode === "build") {
59020
- prompt += `
59021
-
59022
- <operating_mode>
59015
+ } else if (mode === "build") {
59016
+ sections.mode = `<operating_mode>
59023
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.
59024
59018
  </operating_mode>`;
59025
59019
  }
59026
59020
  }
59027
59021
  }
59028
- 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
+ `);
59029
59034
  }
59030
59035
 
59031
59036
  // src/packages/agents/src/engine/turn-context.ts
@@ -61246,7 +61251,6 @@ class StatusBar extends Component_default {
61246
61251
  wrap = false;
61247
61252
  clip = true;
61248
61253
  warningTimer = null;
61249
- updateTimer = null;
61250
61254
  constructor(props) {
61251
61255
  super(props);
61252
61256
  this.state = {
@@ -61255,8 +61259,7 @@ class StatusBar extends Component_default {
61255
61259
  isBusy: props.isBusy,
61256
61260
  exitPending: props.exitPending,
61257
61261
  isBashMode: props.isBashMode,
61258
- warning: props.warning,
61259
- updateStatus: props.updateStatus
61262
+ warning: props.warning
61260
61263
  };
61261
61264
  }
61262
61265
  setBashMode(isBashMode) {
@@ -61270,21 +61273,6 @@ class StatusBar extends Component_default {
61270
61273
  update(partial) {
61271
61274
  this.setState(partial);
61272
61275
  }
61273
- setUpdateStatus(updateStatus, durationMs) {
61274
- if (this.updateTimer) {
61275
- clearTimeout(this.updateTimer);
61276
- this.updateTimer = null;
61277
- }
61278
- this.setState({ updateStatus });
61279
- if (updateStatus && durationMs && durationMs > 0) {
61280
- this.updateTimer = setTimeout(() => {
61281
- this.updateTimer = null;
61282
- if (this.state.updateStatus === updateStatus) {
61283
- this.setState({ updateStatus: undefined });
61284
- }
61285
- }, durationMs);
61286
- }
61287
- }
61288
61276
  showWarning(warningText, durationMs = 4000) {
61289
61277
  if (this.warningTimer) {
61290
61278
  clearTimeout(this.warningTimer);
@@ -61303,15 +61291,11 @@ class StatusBar extends Component_default {
61303
61291
  clearTimeout(this.warningTimer);
61304
61292
  this.warningTimer = null;
61305
61293
  }
61306
- if (this.updateTimer) {
61307
- clearTimeout(this.updateTimer);
61308
- this.updateTimer = null;
61309
- }
61310
61294
  }
61311
61295
  render(width) {
61312
61296
  const termWidth = width ?? process.stdout.columns ?? 80;
61313
61297
  const maxCols = Math.max(1, termWidth);
61314
- const { model, isBusy, exitPending, isBashMode, warning, updateStatus, chatMode } = this.state;
61298
+ const { model, isBusy, exitPending, isBashMode, warning, chatMode } = this.state;
61315
61299
  let left = "";
61316
61300
  if (warning) {
61317
61301
  left = c.warning(warning);
@@ -61321,18 +61305,6 @@ class StatusBar extends Component_default {
61321
61305
  left = c.muted("esc to interrupt");
61322
61306
  } else if (isBashMode) {
61323
61307
  left = c.permission("! for bash mode");
61324
- } else if (updateStatus && updateStatus.state !== "idle") {
61325
- if (updateStatus.state === "checking") {
61326
- left = c.text(updateStatus.message ?? "Checking for updates...");
61327
- } else if (updateStatus.state === "available") {
61328
- left = c.text(updateStatus.message ?? `Update v${updateStatus.version ?? ""} is available`);
61329
- } else if (updateStatus.state === "no-updates") {
61330
- left = c.text(updateStatus.message ?? "Steward is up to date");
61331
- } else if (updateStatus.state === "error" && updateStatus.message) {
61332
- left = c.error(updateStatus.message);
61333
- } else {
61334
- left = c.muted("? for shortcuts");
61335
- }
61336
61308
  } else {
61337
61309
  left = c.muted("? for shortcuts");
61338
61310
  }
@@ -64565,163 +64537,7 @@ function renderTranscript(engine, sessionData, header, options) {
64565
64537
  formatTurnFooter(engine, lastTurnPresentation);
64566
64538
  }
64567
64539
  }
64568
- // src/packages/services/src/updater/service.ts
64569
- var DEFAULT_REPO = "sapirrior/steward";
64570
- function parseSemver(v) {
64571
- if (!v || typeof v !== "string") {
64572
- return [0, 0, 0];
64573
- }
64574
- const clean = v.replace(/^v/i, "").trim().split("-")[0] ?? "";
64575
- const parts = clean.split(".").map((p) => parseInt(p, 10) || 0);
64576
- return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];
64577
- }
64578
- function isNewerVersion(current, latest) {
64579
- const [cMaj, cMin, cPat] = parseSemver(current);
64580
- const [lMaj, lMin, lPat] = parseSemver(latest);
64581
- if (lMaj > cMaj)
64582
- return true;
64583
- if (lMaj < cMaj)
64584
- return false;
64585
- if (lMin > cMin)
64586
- return true;
64587
- if (lMin < cMin)
64588
- return false;
64589
- return lPat > cPat;
64590
- }
64591
64540
 
64592
- class UpdateCheckerService {
64593
- currentVersion;
64594
- repo;
64595
- state = "idle";
64596
- options;
64597
- checkTimer = null;
64598
- resetTimer = null;
64599
- constructor(options = {}) {
64600
- this.options = options;
64601
- this.currentVersion = options.currentVersion || package_default.version || "0.0.0";
64602
- this.repo = options.repo || DEFAULT_REPO;
64603
- }
64604
- getState() {
64605
- return this.state;
64606
- }
64607
- getRepo() {
64608
- return this.repo;
64609
- }
64610
- getCurrentVersion() {
64611
- return this.currentVersion;
64612
- }
64613
- get rawPackageJsonUrl() {
64614
- return `https://raw.githubusercontent.com/${this.repo}/main/package.json`;
64615
- }
64616
- setState(state, info) {
64617
- this.state = state;
64618
- this.options.onStatusChange?.(state, info);
64619
- }
64620
- async checkForUpdates() {
64621
- if (process.env.STEWARD_NO_UPDATE_CHECK === "1" || process.env.STEWARD_NO_UPDATE_CHECK === "true") {
64622
- return null;
64623
- }
64624
- if (this.resetTimer) {
64625
- clearTimeout(this.resetTimer);
64626
- this.resetTimer = null;
64627
- }
64628
- try {
64629
- this.setState("checking", { message: "Checking for updates..." });
64630
- const checkStartTime = Date.now();
64631
- const controller = new AbortController;
64632
- const timeout = setTimeout(() => controller.abort(), this.options.timeoutMs ?? 1e4);
64633
- const response = await fetch(this.rawPackageJsonUrl, {
64634
- headers: {
64635
- "User-Agent": `steward-cli/${this.currentVersion}`,
64636
- Accept: "application/json"
64637
- },
64638
- signal: controller.signal
64639
- });
64640
- clearTimeout(timeout);
64641
- const elapsed = Date.now() - checkStartTime;
64642
- const targetMinDisplay = this.options.minDisplayMs !== undefined ? this.options.minDisplayMs : 1000;
64643
- const minDisplayDelay = Math.max(0, targetMinDisplay - elapsed);
64644
- if (minDisplayDelay > 0) {
64645
- await new Promise((resolve) => setTimeout(resolve, minDisplayDelay));
64646
- }
64647
- if (!response.ok) {
64648
- this.setState("error", { message: "Failed to check for updates" });
64649
- this.resetTimer = setTimeout(() => {
64650
- if (this.state === "error")
64651
- this.setState("idle");
64652
- }, 3000);
64653
- return null;
64654
- }
64655
- const data = await response.json();
64656
- if (!data || typeof data !== "object" || typeof data.version !== "string") {
64657
- this.setState("error", { message: "Invalid upstream version response" });
64658
- this.resetTimer = setTimeout(() => {
64659
- if (this.state === "error")
64660
- this.setState("idle");
64661
- }, 3000);
64662
- return null;
64663
- }
64664
- const latestVersion = data.version.replace(/^v/i, "").trim();
64665
- if (!latestVersion) {
64666
- this.setState("error", { message: "Empty upstream version response" });
64667
- this.resetTimer = setTimeout(() => {
64668
- if (this.state === "error")
64669
- this.setState("idle");
64670
- }, 3000);
64671
- return null;
64672
- }
64673
- const hasUpdate = isNewerVersion(this.currentVersion, latestVersion);
64674
- const updateInfo = {
64675
- currentVersion: this.currentVersion,
64676
- latestVersion,
64677
- hasUpdate
64678
- };
64679
- if (hasUpdate) {
64680
- this.setState("available", {
64681
- version: latestVersion,
64682
- message: `Update v${latestVersion} is available`
64683
- });
64684
- } else {
64685
- this.setState("no-updates", {
64686
- version: this.currentVersion,
64687
- message: `Steward is up to date (v${this.currentVersion})`
64688
- });
64689
- this.resetTimer = setTimeout(() => {
64690
- if (this.state === "no-updates")
64691
- this.setState("idle");
64692
- }, 3500);
64693
- }
64694
- return updateInfo;
64695
- } catch (err) {
64696
- logError(err, { component: "UpdateCheckerService", method: "checkForUpdates" });
64697
- this.setState("error", { message: "Failed to check for updates" });
64698
- this.resetTimer = setTimeout(() => {
64699
- if (this.state === "error")
64700
- this.setState("idle");
64701
- }, 3000);
64702
- return null;
64703
- }
64704
- }
64705
- startBackgroundCheck(initialDelayMs = 2000) {
64706
- if (this.checkTimer) {
64707
- clearTimeout(this.checkTimer);
64708
- }
64709
- this.checkTimer = setTimeout(async () => {
64710
- this.checkTimer = null;
64711
- await this.checkForUpdates();
64712
- }, initialDelayMs);
64713
- }
64714
- dispose() {
64715
- if (this.checkTimer) {
64716
- clearTimeout(this.checkTimer);
64717
- this.checkTimer = null;
64718
- }
64719
- if (this.resetTimer) {
64720
- clearTimeout(this.resetTimer);
64721
- this.resetTimer = null;
64722
- }
64723
- }
64724
- }
64725
64541
  // src/app/utils/open-url.ts
64726
64542
  import { spawn as spawn5 } from "node:child_process";
64727
64543
  import { existsSync as existsSync20 } from "node:fs";
@@ -66645,7 +66461,6 @@ class TUIApp {
66645
66461
  ctrlCTimer = null;
66646
66462
  isBusy = false;
66647
66463
  isScrollViewMode = false;
66648
- updateChecker;
66649
66464
  permissionQueue;
66650
66465
  directBashAbortController = null;
66651
66466
  constructor(options = {}) {
@@ -66693,32 +66508,6 @@ class TUIApp {
66693
66508
  streamingView: this.streamingView,
66694
66509
  cwd: this.cwd
66695
66510
  });
66696
- this.updateChecker = new UpdateCheckerService({
66697
- currentVersion: options.version,
66698
- onStatusChange: (state, info) => {
66699
- if (state === "idle") {
66700
- this.statusBar.setUpdateStatus(undefined);
66701
- } else if (state === "available") {
66702
- this.statusBar.setUpdateStatus({
66703
- state,
66704
- version: info?.version,
66705
- message: info?.message
66706
- }, 8000);
66707
- } else if (state === "error") {
66708
- this.statusBar.setUpdateStatus({
66709
- state,
66710
- version: info?.version,
66711
- message: info?.message
66712
- }, 4000);
66713
- } else {
66714
- this.statusBar.setUpdateStatus({
66715
- state,
66716
- version: info?.version,
66717
- message: info?.message
66718
- });
66719
- }
66720
- }
66721
- });
66722
66511
  this.permissionQueue = new PermissionQueue({
66723
66512
  onShow: (item) => {
66724
66513
  if (this.modals.getActiveModal())
@@ -66786,7 +66575,6 @@ class TUIApp {
66786
66575
  this.engine.mount(this.streamingView, { kind: "custom" });
66787
66576
  this.engine.mount(this.promptInput, { keepCursorVisible: true, kind: "input" });
66788
66577
  this.engine.mount(this.statusBar, { kind: "custom" });
66789
- this.updateChecker.startBackgroundCheck(3000);
66790
66578
  this.engine.addInputListener((chunk) => {
66791
66579
  const action = parseKeyInput(chunk);
66792
66580
  if (action.type === "ctrl-c") {
@@ -67038,7 +66826,6 @@ class TUIApp {
67038
66826
  exit() {
67039
66827
  this.permissionQueue.clear();
67040
66828
  this.session.shutdown().catch(() => {});
67041
- this.updateChecker.dispose();
67042
66829
  this.engine.cleanupSync();
67043
66830
  if (this.onExitCallback) {
67044
66831
  this.onExitCallback();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "steward-cli",
3
- "version": "0.28.0",
3
+ "version": "0.29.0",
4
4
  "description": "Interactive AI engineering assistant for the terminal.",
5
5
  "license": "MIT",
6
6
  "author": "sapirrior",