steward-cli 0.26.1 → 0.27.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 +462 -15
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -41285,7 +41285,7 @@ var require_dist = __commonJS(function(exports) {
41285
41285
  });
41286
41286
 
41287
41287
  // src/app/app.ts
41288
- import { existsSync as existsSync19 } from "node:fs";
41288
+ import { existsSync as existsSync21 } from "node:fs";
41289
41289
 
41290
41290
  // src/packages/tui/src/engine/HistoryStore.ts
41291
41291
  class HistoryStore {
@@ -56001,7 +56001,7 @@ import { isIP } from "node:net";
56001
56001
  // package.json
56002
56002
  var package_default = {
56003
56003
  name: "steward-cli",
56004
- version: "0.26.1",
56004
+ version: "0.27.0",
56005
56005
  description: "Interactive AI engineering assistant for the terminal.",
56006
56006
  author: "sapirrior",
56007
56007
  license: "MIT",
@@ -59556,6 +59556,449 @@ var copyCommand = {
59556
59556
  }
59557
59557
  };
59558
59558
 
59559
+ // src/app/commands/export/index.ts
59560
+ import { spawn as spawn4 } from "node:child_process";
59561
+ import { existsSync as existsSync18, mkdirSync as mkdirSync10, writeFileSync as writeFileSync4 } from "node:fs";
59562
+ import { dirname as dirname7, isAbsolute as isAbsolute8, resolve as resolve11 } from "node:path";
59563
+ function stripAnsi2(str) {
59564
+ return str.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "");
59565
+ }
59566
+ function stripLogoFromLine(str) {
59567
+ return str.replace(/^[ ▄▀▙█▟▝▘]{6,8}\s*/, "");
59568
+ }
59569
+ async function copyToClipboard2(text) {
59570
+ const isTermux = Boolean(process.env.TERMUX_VERSION || existsSync18("/data/data/com.termux/files/usr/bin/termux-clipboard-set"));
59571
+ const [cmd, ...args] = isTermux ? ["termux-clipboard-set"] : process.platform === "darwin" ? ["pbcopy"] : process.platform === "win32" ? ["clip.exe"] : process.env.WAYLAND_DISPLAY ? ["wl-copy"] : ["xclip", "-selection", "clipboard"];
59572
+ const sendOSC52 = () => {
59573
+ try {
59574
+ process.stdout.write(`\x1B]52;c;${Buffer.from(text, "utf8").toString("base64")}\x07`);
59575
+ return true;
59576
+ } catch {
59577
+ return false;
59578
+ }
59579
+ };
59580
+ return new Promise((resolvePromise) => {
59581
+ let resolved = false;
59582
+ const finish = (ok) => {
59583
+ if (resolved)
59584
+ return;
59585
+ resolved = true;
59586
+ clearTimeout(timer);
59587
+ resolvePromise(ok || sendOSC52());
59588
+ };
59589
+ const timer = setTimeout(() => finish(false), 1200);
59590
+ try {
59591
+ const proc = spawn4(cmd, args, { stdio: ["pipe", "ignore", "ignore"] });
59592
+ proc.on("error", () => finish(false));
59593
+ proc.on("close", (code) => finish(code === 0));
59594
+ proc.stdin?.on("error", () => finish(false));
59595
+ proc.stdin?.end(text);
59596
+ } catch {
59597
+ finish(false);
59598
+ }
59599
+ });
59600
+ }
59601
+ function formatToolArgs(args) {
59602
+ if (!args || typeof args !== "object")
59603
+ return "";
59604
+ if (args.command)
59605
+ return String(args.command);
59606
+ if (args.path || args.filePath || args.targetFile) {
59607
+ return String(args.path || args.filePath || args.targetFile);
59608
+ }
59609
+ if (args.pattern)
59610
+ return String(args.pattern);
59611
+ if (args.url)
59612
+ return String(args.url);
59613
+ if (args.query)
59614
+ return String(args.query);
59615
+ const firstVal = Object.values(args)[0];
59616
+ if (typeof firstVal === "string")
59617
+ return firstVal;
59618
+ return "";
59619
+ }
59620
+ function formatToolOutput(output) {
59621
+ if (output === undefined || output === null)
59622
+ return [];
59623
+ let text = "";
59624
+ if (typeof output === "string") {
59625
+ text = output;
59626
+ } else if (typeof output === "object") {
59627
+ text = output.message || output.output || JSON.stringify(output);
59628
+ } else {
59629
+ text = String(output);
59630
+ }
59631
+ const lines = text.split(/\r?\n/).filter((l) => l.trim().length > 0);
59632
+ if (lines.length === 0)
59633
+ return [];
59634
+ const preview = lines.slice(0, 5);
59635
+ return preview.map((l, idx) => idx === 0 ? ` └ ${l}` : ` ${l}`);
59636
+ }
59637
+ function generateTranscriptFromSession(sessionData, cwd = process.cwd()) {
59638
+ const model = sessionData.model;
59639
+ const modelTag = model ? `${model.provider}/${model.modelId}` : "ai/model";
59640
+ const lines = [`Steward`, `${modelTag} · API Usage Billing`, `${cwd}`, ``];
59641
+ const turns = sessionData.turns ?? [];
59642
+ for (let turnIdx = 0;turnIdx < turns.length; turnIdx++) {
59643
+ const turn = turns[turnIdx];
59644
+ if (turnIdx > 0) {
59645
+ lines.push(``);
59646
+ }
59647
+ const messages = turn.messages ?? [];
59648
+ for (const msg of messages) {
59649
+ if (msg.role === "user") {
59650
+ const text = typeof msg.content === "string" ? msg.content : Array.isArray(msg.content) ? msg.content.map((c) => c.text || "").join(`
59651
+ `) : "";
59652
+ lines.push(`❯ ${text}`);
59653
+ lines.push(``);
59654
+ } else if (msg.role === "assistant") {
59655
+ if (Array.isArray(msg.content)) {
59656
+ for (const block of msg.content) {
59657
+ if (block.type === "thinking" && block.thinking) {
59658
+ lines.push(` Thought for ${Math.max(1, Math.round(block.thinking.length / 500))}s`);
59659
+ lines.push(``);
59660
+ } else if (block.type === "tool-call") {
59661
+ const argPreview = formatToolArgs(block.arguments);
59662
+ lines.push(`● ${block.name}${argPreview ? `(${argPreview})` : "()"}`);
59663
+ } else if (block.type === "text" && block.text) {
59664
+ const textLines = block.text.trim().split(/\r?\n/);
59665
+ for (let i = 0;i < textLines.length; i++) {
59666
+ lines.push(i === 0 ? `● ${textLines[i]}` : ` ${textLines[i]}`);
59667
+ }
59668
+ lines.push(``);
59669
+ }
59670
+ }
59671
+ } else if (typeof msg.content === "string" && msg.content.trim()) {
59672
+ const textLines = msg.content.trim().split(/\r?\n/);
59673
+ for (let i = 0;i < textLines.length; i++) {
59674
+ lines.push(i === 0 ? `● ${textLines[i]}` : ` ${textLines[i]}`);
59675
+ }
59676
+ lines.push(``);
59677
+ }
59678
+ } else if (msg.role === "tool") {
59679
+ if (Array.isArray(msg.content)) {
59680
+ for (const toolRes of msg.content) {
59681
+ const formatted = formatToolOutput(toolRes.output);
59682
+ for (const l of formatted) {
59683
+ lines.push(l);
59684
+ }
59685
+ }
59686
+ lines.push(``);
59687
+ }
59688
+ }
59689
+ }
59690
+ if (turn.timestamp) {
59691
+ const timeStr = new Date(turn.timestamp).toLocaleTimeString([], {
59692
+ hour: "2-digit",
59693
+ minute: "2-digit"
59694
+ });
59695
+ lines.push(`✻ Done · ${timeStr}`);
59696
+ }
59697
+ }
59698
+ return lines.join(`
59699
+ `).trim() + `
59700
+ `;
59701
+ }
59702
+ var exportCommand = {
59703
+ name: "export",
59704
+ description: "Exports the exact 1:1 UI conversation text to the clipboard or a file",
59705
+ usage: "/export [filename]",
59706
+ async execute(args, context) {
59707
+ if (context.session.isBusy) {
59708
+ return { handled: true, message: "Cannot export while the agent is generating a response." };
59709
+ }
59710
+ let transcript = "";
59711
+ if (typeof context.getScreenLines === "function") {
59712
+ const screenLines = context.getScreenLines();
59713
+ if (screenLines && screenLines.length > 0) {
59714
+ transcript = screenLines.map((l) => stripLogoFromLine(stripAnsi2(l))).join(`
59715
+ `).trim() + `
59716
+ `;
59717
+ }
59718
+ }
59719
+ if (!transcript.trim()) {
59720
+ const sessionData = context.session.session;
59721
+ transcript = generateTranscriptFromSession(sessionData, context.cwd || process.cwd());
59722
+ }
59723
+ if (!transcript.trim()) {
59724
+ return { handled: true, message: "No conversation messages found to export." };
59725
+ }
59726
+ const targetArg = args.join(" ").trim();
59727
+ if (!targetArg) {
59728
+ await copyToClipboard2(transcript);
59729
+ return {
59730
+ handled: true,
59731
+ message: "Copied conversation transcript to clipboard."
59732
+ };
59733
+ }
59734
+ try {
59735
+ const cwd = context.cwd || process.cwd();
59736
+ const filePath = isAbsolute8(targetArg) ? targetArg : resolve11(cwd, targetArg);
59737
+ const targetPath = filePath.endsWith(".txt") || filePath.endsWith(".md") ? filePath : `${filePath}.txt`;
59738
+ mkdirSync10(dirname7(targetPath), { recursive: true });
59739
+ writeFileSync4(targetPath, transcript, "utf-8");
59740
+ return {
59741
+ handled: true,
59742
+ message: `Conversation transcript exported to ${targetPath}`
59743
+ };
59744
+ } catch (err) {
59745
+ return {
59746
+ handled: true,
59747
+ message: `Failed to export transcript: ${err instanceof Error ? err.message : String(err)}`
59748
+ };
59749
+ }
59750
+ }
59751
+ };
59752
+
59753
+ // src/app/commands/init/index.ts
59754
+ import { existsSync as existsSync19, mkdirSync as mkdirSync11, readFileSync as readFileSync14, writeFileSync as writeFileSync5 } from "node:fs";
59755
+ import { basename, join as join17 } from "node:path";
59756
+ function generateAgentsMd(cwd) {
59757
+ const projectName = basename(cwd);
59758
+ let stack = "General";
59759
+ let commandsSection = "- `make test`: Run test suite";
59760
+ if (existsSync19(join17(cwd, "package.json"))) {
59761
+ try {
59762
+ const pkg = JSON.parse(readFileSync14(join17(cwd, "package.json"), "utf-8"));
59763
+ const scripts = pkg.scripts ? Object.keys(pkg.scripts) : [];
59764
+ stack = "Node.js / TypeScript";
59765
+ if (scripts.length > 0) {
59766
+ commandsSection = scripts.map((s) => `- \`npm run ${s}\` (or \`bun run ${s}\`)`).join(`
59767
+ `);
59768
+ }
59769
+ } catch {
59770
+ stack = "Node.js";
59771
+ }
59772
+ } else if (existsSync19(join17(cwd, "Cargo.toml"))) {
59773
+ stack = "Rust";
59774
+ commandsSection = "- `cargo build`: Build project\n- `cargo test`: Run tests";
59775
+ } else if (existsSync19(join17(cwd, "pyproject.toml")) || existsSync19(join17(cwd, "requirements.txt"))) {
59776
+ stack = "Python";
59777
+ commandsSection = "- `pytest`: Run tests\n- `python main.py`: Run application";
59778
+ } else if (existsSync19(join17(cwd, "go.mod"))) {
59779
+ stack = "Go";
59780
+ commandsSection = "- `go build ./...`: Build packages\n- `go test ./...`: Run test suite";
59781
+ }
59782
+ return `# AGENTS.md
59783
+
59784
+ Operational guidelines and architectural context for AI engineering assistants working in \`${projectName}\`.
59785
+
59786
+ ## 1. Project Overview & Tech Stack
59787
+
59788
+ - **Project:** \`${projectName}\`
59789
+ - **Primary Stack:** ${stack}
59790
+
59791
+ ## 2. Common Commands
59792
+
59793
+ ${commandsSection}
59794
+
59795
+ ## 3. Architecture & Guidelines
59796
+
59797
+ - **Code Style:** Follow established project formatting and idioms.
59798
+ - **Verification:** Run relevant tests before finalizing changes.
59799
+ - **Safety:** Verify file paths and permissions before applying destructive edits.
59800
+ `;
59801
+ }
59802
+ var DUMMY_HOOKS_JSON = JSON.stringify({
59803
+ version: 1,
59804
+ hooks: {
59805
+ SessionStart: [
59806
+ {
59807
+ name: "sample-session-start",
59808
+ command: 'echo "Steward session started"',
59809
+ enabled: false,
59810
+ description: "Runs when a session starts or resumes"
59811
+ }
59812
+ ],
59813
+ BeforeToolUse: [
59814
+ {
59815
+ name: "sample-tool-guard",
59816
+ command: 'echo "Checking tool call"',
59817
+ matcher: "bash",
59818
+ enabled: false,
59819
+ description: "Runs before specific tool executions"
59820
+ }
59821
+ ]
59822
+ }
59823
+ }, null, 2);
59824
+ var initCommand = {
59825
+ name: "init",
59826
+ description: "Initializes project with AGENTS.md, .steward/hooks.json, and .agents/ directory",
59827
+ usage: "/init",
59828
+ async execute(_args, context) {
59829
+ const cwd = context.cwd || process.cwd();
59830
+ const created = [];
59831
+ const skipped = [];
59832
+ try {
59833
+ const agentsMdPath = join17(cwd, "AGENTS.md");
59834
+ if (!existsSync19(agentsMdPath)) {
59835
+ writeFileSync5(agentsMdPath, generateAgentsMd(cwd), "utf-8");
59836
+ created.push("AGENTS.md");
59837
+ } else {
59838
+ skipped.push("AGENTS.md (already exists)");
59839
+ }
59840
+ const stewardDir = join17(cwd, ".steward");
59841
+ if (!existsSync19(stewardDir)) {
59842
+ mkdirSync11(stewardDir, { recursive: true });
59843
+ }
59844
+ const hooksPath = join17(stewardDir, "hooks.json");
59845
+ if (!existsSync19(hooksPath)) {
59846
+ writeFileSync5(hooksPath, DUMMY_HOOKS_JSON, "utf-8");
59847
+ created.push(".steward/hooks.json");
59848
+ } else {
59849
+ skipped.push(".steward/hooks.json (already exists)");
59850
+ }
59851
+ const skillsDir = join17(cwd, ".agents", "skills");
59852
+ if (!existsSync19(skillsDir)) {
59853
+ mkdirSync11(skillsDir, { recursive: true });
59854
+ created.push(".agents/skills/");
59855
+ } else {
59856
+ skipped.push(".agents/skills/ (already exists)");
59857
+ }
59858
+ const summaryParts = [];
59859
+ if (created.length > 0) {
59860
+ summaryParts.push(`Created: ${created.join(", ")}`);
59861
+ }
59862
+ if (skipped.length > 0) {
59863
+ summaryParts.push(`Preserved: ${skipped.join(", ")}`);
59864
+ }
59865
+ return {
59866
+ handled: true,
59867
+ message: `Project initialized successfully!
59868
+ ${summaryParts.join(`
59869
+ `)}`
59870
+ };
59871
+ } catch (err) {
59872
+ return {
59873
+ handled: true,
59874
+ message: `Failed to initialize project: ${err instanceof Error ? err.message : String(err)}`
59875
+ };
59876
+ }
59877
+ }
59878
+ };
59879
+
59880
+ // src/app/commands/usage/index.ts
59881
+ function formatNumber(num) {
59882
+ return (num ?? 0).toLocaleString();
59883
+ }
59884
+ function formatDuration(isoDateString) {
59885
+ if (!isoDateString)
59886
+ return "0s";
59887
+ const start = new Date(isoDateString).getTime();
59888
+ if (isNaN(start))
59889
+ return "0s";
59890
+ const elapsedSec = Math.max(0, Math.floor((Date.now() - start) / 1000));
59891
+ const hrs = Math.floor(elapsedSec / 3600);
59892
+ const mins = Math.floor(elapsedSec % 3600 / 60);
59893
+ const secs = elapsedSec % 60;
59894
+ if (hrs > 0)
59895
+ return `${hrs}h ${mins}m ${secs}s`;
59896
+ if (mins > 0)
59897
+ return `${mins}m ${secs}s`;
59898
+ return `${secs}s`;
59899
+ }
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
+ function estimateUniversalCost(usage, modelId, provider) {
59923
+ const p = provider.toLowerCase();
59924
+ const m = modelId.toLowerCase();
59925
+ if (p === "ollama" || m.includes("local")) {
59926
+ return "$0.00 USD [Local / Offline]";
59927
+ }
59928
+ const isLightweight = m.includes("flash") || m.includes("mini") || m.includes("haiku") || m.includes("8b") || m.includes("deepseek");
59929
+ const inputPerMillion = isLightweight ? 0.15 : 3;
59930
+ const outputPerMillion = isLightweight ? 0.6 : 15;
59931
+ const cachePerMillion = isLightweight ? 0.075 : 0.3;
59932
+ const baseCost = usage.inputTokens * inputPerMillion / 1e6;
59933
+ const outputCost = usage.outputTokens * outputPerMillion / 1e6;
59934
+ const cacheCost = (usage.cacheReadTokens ?? 0) * cachePerMillion / 1e6;
59935
+ const total = baseCost + outputCost + cacheCost;
59936
+ if (total === 0) {
59937
+ return "$0.00 USD [estimated]";
59938
+ }
59939
+ const formatted = total < 0.005 ? "< $0.01" : `$${total.toFixed(2)}`;
59940
+ return `~${formatted} USD [estimated benchmark]`;
59941
+ }
59942
+ var usageCommand = {
59943
+ name: "usage",
59944
+ description: "Displays token usage metrics and statistics for the current session",
59945
+ usage: "/usage",
59946
+ async execute(_args, context) {
59947
+ const sessionData = context.session.session;
59948
+ const model = context.session.getModel();
59949
+ const usage = context.session.getUsage();
59950
+ const turns = sessionData?.turns ?? [];
59951
+ const turnsCount = turns.length;
59952
+ let toolCallsCount = 0;
59953
+ for (const turn of turns) {
59954
+ if (Array.isArray(turn.messages)) {
59955
+ 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;
59960
+ }
59961
+ }
59962
+ }
59963
+ }
59964
+ const contextLimit = getContextWindowLimit(model.modelId, model.provider);
59965
+ const contextBar = renderProgressBar(usage.totalTokens, contextLimit);
59966
+ const duration = formatDuration(sessionData?.createdAt);
59967
+ const costEstimate = estimateUniversalCost(usage, model.modelId, model.provider);
59968
+ const lines = [
59969
+ `Session Usage & Analytics:`,
59970
+ `• Active Model: ${model.modelId} (${model.provider}${model.effort !== "medium" ? `, effort: ${model.effort}` : ""})`,
59971
+ `• Session Duration: ${duration}`,
59972
+ `• Activity: ${formatNumber(turnsCount)} turns (${formatNumber(toolCallsCount)} tool executions)`,
59973
+ `• Context Window: ${contextBar} (${formatNumber(usage.totalTokens)} / ${formatNumber(contextLimit)} tokens)`,
59974
+ ``,
59975
+ `Token Breakdown:`,
59976
+ `• Input Tokens: ${formatNumber(usage.inputTokens)}`,
59977
+ `• Output Tokens: ${formatNumber(usage.outputTokens)}`
59978
+ ];
59979
+ if (usage.reasoningTokens && usage.reasoningTokens > 0) {
59980
+ lines.push(`• Reasoning Tokens: ${formatNumber(usage.reasoningTokens)}`);
59981
+ }
59982
+ if (usage.cacheReadTokens && usage.cacheReadTokens > 0) {
59983
+ const totalIn = (usage.inputTokens || 0) + (usage.cacheReadTokens || 0);
59984
+ const hitRate = totalIn > 0 ? Math.round(usage.cacheReadTokens / totalIn * 100) : 0;
59985
+ lines.push(`• Cache Read Tokens: ${formatNumber(usage.cacheReadTokens)} (${hitRate}% hit rate)`);
59986
+ }
59987
+ if (usage.cacheWriteTokens && usage.cacheWriteTokens > 0) {
59988
+ lines.push(`• Cache Write Tokens: ${formatNumber(usage.cacheWriteTokens)}`);
59989
+ }
59990
+ lines.push(`• Total Tokens: ${formatNumber(usage.totalTokens)}`);
59991
+ lines.push(``);
59992
+ lines.push(`Estimated Cost:`);
59993
+ lines.push(`• Session Spend: ${costEstimate}`);
59994
+ return {
59995
+ handled: true,
59996
+ message: lines.join(`
59997
+ `)
59998
+ };
59999
+ }
60000
+ };
60001
+
59559
60002
  // src/app/commands/effort/index.ts
59560
60003
  var EFFORT_HELP = `Reasoning Effort Levels:
59561
60004
  none : disable reasoning/thinking
@@ -60025,7 +60468,10 @@ class CommandRegistry {
60025
60468
  }
60026
60469
  }
60027
60470
  var builtInCommands = [
60471
+ initCommand,
60028
60472
  copyCommand,
60473
+ exportCommand,
60474
+ usageCommand,
60029
60475
  rewindCommand,
60030
60476
  modelCommand,
60031
60477
  loginCommand,
@@ -62174,7 +62620,7 @@ var pn = x.lex;
62174
62620
 
62175
62621
  // src/packages/tui/src/format/highlight.ts
62176
62622
  var import_cli_highlight = __toESM(require_dist(), 1);
62177
- import { extname, basename } from "node:path";
62623
+ import { extname, basename as basename2 } from "node:path";
62178
62624
  var EXTENSION_MAP = {
62179
62625
  ".ts": "typescript",
62180
62626
  ".tsx": "typescript",
@@ -62219,7 +62665,7 @@ var FILENAME_MAP = {
62219
62665
  function detectLanguageFromPath(filePath) {
62220
62666
  if (!filePath)
62221
62667
  return;
62222
- const name = basename(filePath).toLowerCase();
62668
+ const name = basename2(filePath).toLowerCase();
62223
62669
  if (FILENAME_MAP[name])
62224
62670
  return FILENAME_MAP[name];
62225
62671
  const ext = extname(filePath).toLowerCase();
@@ -64151,13 +64597,13 @@ class UpdateCheckerService {
64151
64597
  }
64152
64598
  }
64153
64599
  // src/app/utils/open-url.ts
64154
- import { spawn as spawn4 } from "node:child_process";
64155
- import { existsSync as existsSync18 } from "node:fs";
64600
+ import { spawn as spawn5 } from "node:child_process";
64601
+ import { existsSync as existsSync20 } from "node:fs";
64156
64602
  function openUrl(url) {
64157
64603
  const platform = process.platform;
64158
64604
  try {
64159
- if (process.env.TERMUX_VERSION || existsSync18("/data/data/com.termux/files/usr/bin/termux-open-url")) {
64160
- const child = spawn4("termux-open-url", [url], {
64605
+ if (process.env.TERMUX_VERSION || existsSync20("/data/data/com.termux/files/usr/bin/termux-open-url")) {
64606
+ const child = spawn5("termux-open-url", [url], {
64161
64607
  detached: true,
64162
64608
  stdio: "ignore"
64163
64609
  });
@@ -64165,7 +64611,7 @@ function openUrl(url) {
64165
64611
  return;
64166
64612
  }
64167
64613
  if (platform === "win32") {
64168
- const child = spawn4("cmd.exe", ["/c", "start", '""', url], {
64614
+ const child = spawn5("cmd.exe", ["/c", "start", '""', url], {
64169
64615
  detached: true,
64170
64616
  stdio: "ignore"
64171
64617
  });
@@ -64173,14 +64619,14 @@ function openUrl(url) {
64173
64619
  return;
64174
64620
  }
64175
64621
  if (platform === "darwin") {
64176
- const child = spawn4("open", [url], {
64622
+ const child = spawn5("open", [url], {
64177
64623
  detached: true,
64178
64624
  stdio: "ignore"
64179
64625
  });
64180
64626
  child.unref();
64181
64627
  return;
64182
64628
  }
64183
- const child = spawn4("xdg-open", [url], {
64629
+ const child = spawn5("xdg-open", [url], {
64184
64630
  detached: true,
64185
64631
  stdio: "ignore"
64186
64632
  });
@@ -65974,7 +66420,7 @@ async function applyCommandResult(text, cmdResult, ctx) {
65974
66420
  }
65975
66421
 
65976
66422
  // src/app/utils/bash.ts
65977
- import { spawn as spawn5 } from "node:child_process";
66423
+ import { spawn as spawn6 } from "node:child_process";
65978
66424
  async function executeDirectBash(options) {
65979
66425
  const { command, cwd = process.cwd(), abortSignal, onChunk } = options;
65980
66426
  const { shell, args } = getPlatformShell();
@@ -65987,7 +66433,7 @@ async function executeDirectBash(options) {
65987
66433
  let output = "";
65988
66434
  let isSettled = false;
65989
66435
  try {
65990
- child = spawn5(shell, childArgs, {
66436
+ child = spawn6(shell, childArgs, {
65991
66437
  cwd,
65992
66438
  env: process.env,
65993
66439
  stdio: ["pipe", "pipe", "pipe"],
@@ -66206,7 +66652,7 @@ class TUIApp {
66206
66652
  source: this.session.session.turns.length > 0 ? "resume" : "startup"
66207
66653
  });
66208
66654
  const logPath = getSessionLogPath(this.session.session.date, this.session.session.id);
66209
- if (this.session.session.turns.length > 0 || existsSync19(logPath)) {
66655
+ if (this.session.session.turns.length > 0 || existsSync21(logPath)) {
66210
66656
  renderTranscript(this.engine, this.session.session, this.header);
66211
66657
  } else {
66212
66658
  this.engine.commit("header", this.header.render());
@@ -66312,7 +66758,8 @@ class TUIApp {
66312
66758
  if (defaultCommandRegistry.isCommand(text)) {
66313
66759
  const cmdResult = await defaultCommandRegistry.execute(text, {
66314
66760
  session: this.session,
66315
- cwd: this.cwd
66761
+ cwd: this.cwd,
66762
+ getScreenLines: () => this.engine.history.getEntries().filter((e) => e.kind !== "logo").flatMap((e) => e.lines)
66316
66763
  });
66317
66764
  await applyCommandResult(text, cmdResult, {
66318
66765
  engine: this.engine,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "steward-cli",
3
- "version": "0.26.1",
3
+ "version": "0.27.0",
4
4
  "description": "Interactive AI engineering assistant for the terminal.",
5
5
  "license": "MIT",
6
6
  "author": "sapirrior",