steward-cli 0.20.0 → 0.21.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/README.md +1 -1
- package/dist/cli.js +351 -26
- package/dist/steward +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Steward
|
|
2
2
|
|
|
3
|
-
[](https://github.com/sapirrior/steward/releases)
|
|
4
4
|
[](https://www.npmjs.com/package/steward-cli)
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
[](https://bun.sh)
|
package/dist/cli.js
CHANGED
|
@@ -41959,6 +41959,9 @@ var require_dist2 = __commonJS(function(exports) {
|
|
|
41959
41959
|
__exportStar(require_theme(), exports);
|
|
41960
41960
|
});
|
|
41961
41961
|
|
|
41962
|
+
// src/app/app.ts
|
|
41963
|
+
import { existsSync as existsSync16 } from "node:fs";
|
|
41964
|
+
|
|
41962
41965
|
// src/packages/tui/src/engine/HistoryStore.ts
|
|
41963
41966
|
class HistoryStore {
|
|
41964
41967
|
entries = [];
|
|
@@ -68876,6 +68879,28 @@ function rehydrateSessionHistory(sessionData, projection) {
|
|
|
68876
68879
|
}
|
|
68877
68880
|
}
|
|
68878
68881
|
}
|
|
68882
|
+
if (projection) {
|
|
68883
|
+
for (const [turnId, turnPres] of projection.turns) {
|
|
68884
|
+
if (turnId.startsWith("direct-")) {
|
|
68885
|
+
for (const [toolCallId, toolLog] of turnPres.tools) {
|
|
68886
|
+
restoredItems.push({
|
|
68887
|
+
id: `direct-bash-${toolCallId}`,
|
|
68888
|
+
turnId,
|
|
68889
|
+
type: "tool",
|
|
68890
|
+
content: "",
|
|
68891
|
+
toolData: {
|
|
68892
|
+
toolName: toolLog.toolName,
|
|
68893
|
+
displayName: toolLog.displayName,
|
|
68894
|
+
status: toolLog.status,
|
|
68895
|
+
durationMs: toolLog.durationMs,
|
|
68896
|
+
error: toolLog.errorMessage,
|
|
68897
|
+
toolOutput: toolLog.outputSummary
|
|
68898
|
+
}
|
|
68899
|
+
});
|
|
68900
|
+
}
|
|
68901
|
+
}
|
|
68902
|
+
}
|
|
68903
|
+
}
|
|
68879
68904
|
return restoredItems;
|
|
68880
68905
|
}
|
|
68881
68906
|
// src/packages/services/src/session/logs/types.ts
|
|
@@ -71055,7 +71080,21 @@ var bashTool = {
|
|
|
71055
71080
|
return `Moved to background · task id: ${result.taskId}`;
|
|
71056
71081
|
}
|
|
71057
71082
|
const code = result && "exitCode" in result ? result.exitCode : 0;
|
|
71058
|
-
|
|
71083
|
+
const headline = `Ran successfully · exit code: ${code}`;
|
|
71084
|
+
const stdout = result && "stdout" in result && typeof result.stdout === "string" ? result.stdout : "";
|
|
71085
|
+
const stderr = result && "stderr" in result && typeof result.stderr === "string" ? result.stderr : "";
|
|
71086
|
+
const output = (stdout + (stdout && stderr ? `
|
|
71087
|
+
` : "") + stderr).trim();
|
|
71088
|
+
if (output) {
|
|
71089
|
+
return {
|
|
71090
|
+
headline,
|
|
71091
|
+
detail: {
|
|
71092
|
+
kind: "text",
|
|
71093
|
+
text: output
|
|
71094
|
+
}
|
|
71095
|
+
};
|
|
71096
|
+
}
|
|
71097
|
+
return headline;
|
|
71059
71098
|
},
|
|
71060
71099
|
execute: async (args, context) => {
|
|
71061
71100
|
const command = typeof args?.command === "string" ? args.command.trim() : "";
|
|
@@ -71221,7 +71260,7 @@ import { isIP } from "node:net";
|
|
|
71221
71260
|
// package.json
|
|
71222
71261
|
var package_default = {
|
|
71223
71262
|
name: "steward-cli",
|
|
71224
|
-
version: "0.
|
|
71263
|
+
version: "0.21.0",
|
|
71225
71264
|
description: "Interactive AI engineering assistant for the terminal with human-in-the-loop control across local and frontier models.",
|
|
71226
71265
|
author: "sapirrior",
|
|
71227
71266
|
license: "MIT",
|
|
@@ -117135,6 +117174,9 @@ class Component {
|
|
|
117135
117174
|
this.engine.requestFrame();
|
|
117136
117175
|
}
|
|
117137
117176
|
}
|
|
117177
|
+
forceUpdate() {
|
|
117178
|
+
this.markDirty();
|
|
117179
|
+
}
|
|
117138
117180
|
_cachedCursor = null;
|
|
117139
117181
|
_getLines(width, forceRedraw = false) {
|
|
117140
117182
|
if (this._dirty || forceRedraw || width !== undefined && width !== this._lastWidth) {
|
|
@@ -117736,10 +117778,16 @@ class StatusBar extends Component_default {
|
|
|
117736
117778
|
model: props.model,
|
|
117737
117779
|
isBusy: props.isBusy,
|
|
117738
117780
|
exitPending: props.exitPending,
|
|
117781
|
+
isBashMode: props.isBashMode,
|
|
117739
117782
|
warning: props.warning,
|
|
117740
117783
|
updateStatus: props.updateStatus
|
|
117741
117784
|
};
|
|
117742
117785
|
}
|
|
117786
|
+
setBashMode(isBashMode) {
|
|
117787
|
+
if (this.state.isBashMode !== isBashMode) {
|
|
117788
|
+
this.setState({ isBashMode });
|
|
117789
|
+
}
|
|
117790
|
+
}
|
|
117743
117791
|
setMode(mode) {
|
|
117744
117792
|
this.setState({ chatMode: mode });
|
|
117745
117793
|
}
|
|
@@ -117787,12 +117835,16 @@ class StatusBar extends Component_default {
|
|
|
117787
117835
|
render(width) {
|
|
117788
117836
|
const termWidth = width ?? process.stdout.columns ?? 80;
|
|
117789
117837
|
const maxCols = Math.max(1, termWidth);
|
|
117790
|
-
const { model, isBusy, exitPending, warning, updateStatus, chatMode } = this.state;
|
|
117838
|
+
const { model, isBusy, exitPending, isBashMode, warning, updateStatus, chatMode } = this.state;
|
|
117791
117839
|
let left = "";
|
|
117792
117840
|
if (warning) {
|
|
117793
117841
|
left = c.warning(warning);
|
|
117794
117842
|
} else if (exitPending) {
|
|
117795
117843
|
left = `${c.error("▸ ")}${c.muted("Press ")}${c.error("Ctrl+C")}${c.muted(" again to exit")}`;
|
|
117844
|
+
} else if (isBusy) {
|
|
117845
|
+
left = c.muted("esc to interrupt");
|
|
117846
|
+
} else if (isBashMode) {
|
|
117847
|
+
left = c.permission("! for bash mode");
|
|
117796
117848
|
} else if (updateStatus && updateStatus.state !== "idle") {
|
|
117797
117849
|
if (updateStatus.state === "checking") {
|
|
117798
117850
|
left = c.text(updateStatus.message ?? "Checking for updates...");
|
|
@@ -117802,13 +117854,9 @@ class StatusBar extends Component_default {
|
|
|
117802
117854
|
left = c.text(updateStatus.message ?? "Steward is up to date");
|
|
117803
117855
|
} else if (updateStatus.state === "error" && updateStatus.message) {
|
|
117804
117856
|
left = c.error(updateStatus.message);
|
|
117805
|
-
} else if (isBusy) {
|
|
117806
|
-
left = c.muted("esc to interrupt");
|
|
117807
117857
|
} else {
|
|
117808
117858
|
left = c.muted("? for shortcuts");
|
|
117809
117859
|
}
|
|
117810
|
-
} else if (isBusy) {
|
|
117811
|
-
left = c.muted("esc to interrupt");
|
|
117812
117860
|
} else {
|
|
117813
117861
|
left = c.muted("? for shortcuts");
|
|
117814
117862
|
}
|
|
@@ -119575,6 +119623,7 @@ class StreamingView extends Component_default {
|
|
|
119575
119623
|
isStreaming: false,
|
|
119576
119624
|
isThinking: false,
|
|
119577
119625
|
activeTool: null,
|
|
119626
|
+
activeCommand: null,
|
|
119578
119627
|
pulseFrame: 0
|
|
119579
119628
|
};
|
|
119580
119629
|
}
|
|
@@ -119615,8 +119664,25 @@ class StreamingView extends Component_default {
|
|
|
119615
119664
|
});
|
|
119616
119665
|
}
|
|
119617
119666
|
}
|
|
119667
|
+
setActiveCommand(command) {
|
|
119668
|
+
this.setState({
|
|
119669
|
+
activeCommand: command ? { command, recentLines: [] } : null,
|
|
119670
|
+
isThinking: false
|
|
119671
|
+
});
|
|
119672
|
+
this.ensurePulse();
|
|
119673
|
+
}
|
|
119674
|
+
updateCommandOutput(recentLines) {
|
|
119675
|
+
if (this.state.activeCommand) {
|
|
119676
|
+
this.setState({
|
|
119677
|
+
activeCommand: {
|
|
119678
|
+
...this.state.activeCommand,
|
|
119679
|
+
recentLines
|
|
119680
|
+
}
|
|
119681
|
+
});
|
|
119682
|
+
}
|
|
119683
|
+
}
|
|
119618
119684
|
ensurePulse() {
|
|
119619
|
-
const isBusy = this.state.isStreaming || this.state.activeTool || this.state.isThinking;
|
|
119685
|
+
const isBusy = this.state.isStreaming || this.state.activeTool || this.state.activeCommand || this.state.isThinking;
|
|
119620
119686
|
if (isBusy && !this.pulseTimer) {
|
|
119621
119687
|
this.pulseTimer = setInterval(() => {
|
|
119622
119688
|
this.setState({ pulseFrame: this.state.pulseFrame + 1 });
|
|
@@ -119637,6 +119703,7 @@ class StreamingView extends Component_default {
|
|
|
119637
119703
|
isThinking: false,
|
|
119638
119704
|
thinkingStartTime: undefined,
|
|
119639
119705
|
activeTool: null,
|
|
119706
|
+
activeCommand: null,
|
|
119640
119707
|
pulseFrame: 0
|
|
119641
119708
|
});
|
|
119642
119709
|
}
|
|
@@ -119647,13 +119714,37 @@ class StreamingView extends Component_default {
|
|
|
119647
119714
|
}
|
|
119648
119715
|
}
|
|
119649
119716
|
render(width) {
|
|
119650
|
-
const {
|
|
119651
|
-
|
|
119717
|
+
const {
|
|
119718
|
+
text,
|
|
119719
|
+
isStreaming,
|
|
119720
|
+
isThinking,
|
|
119721
|
+
thinkingStartTime,
|
|
119722
|
+
activeTool,
|
|
119723
|
+
activeCommand,
|
|
119724
|
+
pulseFrame
|
|
119725
|
+
} = this.state;
|
|
119726
|
+
if (!isStreaming && !text && !activeTool && !activeCommand && !isThinking)
|
|
119652
119727
|
return [];
|
|
119653
119728
|
const termWidth = width ?? process.stdout.columns ?? 80;
|
|
119654
119729
|
const maxCols = Math.max(1, termWidth);
|
|
119655
119730
|
const textWidth = Math.max(1, maxCols - 2);
|
|
119656
119731
|
const lines = [];
|
|
119732
|
+
if (activeCommand) {
|
|
119733
|
+
lines.push(`${c.permission("!")} ${c.text(activeCommand.command)}`);
|
|
119734
|
+
if (activeCommand.recentLines && activeCommand.recentLines.length > 0) {
|
|
119735
|
+
const toShow = activeCommand.recentLines.slice(-10);
|
|
119736
|
+
const maxLogLen = Math.max(10, maxCols - 6);
|
|
119737
|
+
for (let i = 0;i < toShow.length; i++) {
|
|
119738
|
+
const l = toShow[i] ?? "";
|
|
119739
|
+
const p = i === 0 ? ` ${c.muted("└ ")}` : " ";
|
|
119740
|
+
const truncatedLog = l.length > maxLogLen ? `${l.slice(0, maxLogLen - 1)}…` : l;
|
|
119741
|
+
lines.push(`${p}${c.text(truncatedLog)}`);
|
|
119742
|
+
}
|
|
119743
|
+
} else {
|
|
119744
|
+
lines.push(` ${c.muted("└ ")}${c.muted("Running...")}`);
|
|
119745
|
+
}
|
|
119746
|
+
return lines;
|
|
119747
|
+
}
|
|
119657
119748
|
if (activeTool) {
|
|
119658
119749
|
const bullet = getStatusBullet("running", pulseFrame % 2 === 0);
|
|
119659
119750
|
const toolName = activeTool.displayName ?? (activeTool.name ? activeTool.name.charAt(0).toUpperCase() + activeTool.name.slice(1) : "Tool");
|
|
@@ -120101,7 +120192,7 @@ class PromptInput extends Component_default {
|
|
|
120101
120192
|
return true;
|
|
120102
120193
|
}
|
|
120103
120194
|
if (this.commandPalette.dismiss()) {
|
|
120104
|
-
this.
|
|
120195
|
+
this.markDirty();
|
|
120105
120196
|
return true;
|
|
120106
120197
|
}
|
|
120107
120198
|
if (this.state.value.length > 0) {
|
|
@@ -120161,6 +120252,9 @@ ${after}`, this.state.cursorPos);
|
|
|
120161
120252
|
}
|
|
120162
120253
|
const trimmed = this.state.value.trim();
|
|
120163
120254
|
if (trimmed) {
|
|
120255
|
+
if (trimmed.startsWith("!")) {
|
|
120256
|
+
this.props.onBashModeChange?.(false);
|
|
120257
|
+
}
|
|
120164
120258
|
this.historyController.add(trimmed);
|
|
120165
120259
|
this.setState({ value: "", cursorPos: 0, fileMatches: [] });
|
|
120166
120260
|
this.autocomplete.clear();
|
|
@@ -120312,6 +120406,11 @@ ${after}`, this.state.cursorPos);
|
|
|
120312
120406
|
}
|
|
120313
120407
|
updateValueAndCheckCompletions(nextVal, nextPos) {
|
|
120314
120408
|
const clampedPos = Math.max(0, Math.min(nextVal.length, nextPos));
|
|
120409
|
+
const prevIsBash = this.state.value.startsWith("!");
|
|
120410
|
+
const nextIsBash = nextVal.startsWith("!");
|
|
120411
|
+
if (prevIsBash !== nextIsBash) {
|
|
120412
|
+
this.props.onBashModeChange?.(nextIsBash);
|
|
120413
|
+
}
|
|
120315
120414
|
this.commandPalette.resetDismissed();
|
|
120316
120415
|
this.setState({ value: nextVal, cursorPos: clampedPos });
|
|
120317
120416
|
this.autocomplete.updateQuery(nextVal, clampedPos, () => {
|
|
@@ -120348,7 +120447,8 @@ ${after}`, this.state.cursorPos);
|
|
|
120348
120447
|
if (escPending) {
|
|
120349
120448
|
lines.push(c.permission("Press Esc again to clear"));
|
|
120350
120449
|
}
|
|
120351
|
-
const
|
|
120450
|
+
const isBashMode = value.startsWith("!");
|
|
120451
|
+
const borderColor = isBashMode ? c.permission : disabled ? c.subtle : c.promptBorder;
|
|
120352
120452
|
if (disabled) {
|
|
120353
120453
|
lines.push(borderColor(figures.horizontalLine.repeat(dividerWidth)));
|
|
120354
120454
|
lines.push(truncateToWidth2(` ${c.muted("Esc to stop")}`, maxCols));
|
|
@@ -120694,13 +120794,20 @@ class PermissionQueue {
|
|
|
120694
120794
|
}
|
|
120695
120795
|
|
|
120696
120796
|
// src/app/ui/utils/tool-detail.ts
|
|
120697
|
-
function renderToolDetail(detail, targetWidth) {
|
|
120797
|
+
function renderToolDetail(detail, targetWidth, expanded = false) {
|
|
120698
120798
|
if (detail.kind === "pre-styled") {
|
|
120699
120799
|
return detail.text;
|
|
120700
120800
|
}
|
|
120701
120801
|
if (detail.kind === "text") {
|
|
120702
|
-
|
|
120703
|
-
`)
|
|
120802
|
+
const rawLines = detail.text.split(`
|
|
120803
|
+
`);
|
|
120804
|
+
const cap = expanded ? Infinity : 15;
|
|
120805
|
+
const shown = rawLines.slice(0, cap);
|
|
120806
|
+
const lines = shown.map((l) => c.muted(l));
|
|
120807
|
+
if (rawLines.length > cap) {
|
|
120808
|
+
lines.push(c.text(` … (${rawLines.length - cap} more lines)`));
|
|
120809
|
+
}
|
|
120810
|
+
return lines.join(`
|
|
120704
120811
|
`);
|
|
120705
120812
|
}
|
|
120706
120813
|
if (detail.kind === "code") {
|
|
@@ -120709,12 +120816,12 @@ function renderToolDetail(detail, targetWidth) {
|
|
|
120709
120816
|
if (contentLines.length === 0 || contentLines.length === 1 && !contentLines[0]) {
|
|
120710
120817
|
return "";
|
|
120711
120818
|
}
|
|
120712
|
-
const cap = 50;
|
|
120819
|
+
const cap = expanded ? Infinity : 50;
|
|
120713
120820
|
const shown = contentLines.slice(0, cap);
|
|
120714
120821
|
const padWidth = String(contentLines.length).length;
|
|
120715
120822
|
const lines = shown.map((l, i) => `${c.muted(String(i + 1).padStart(padWidth))} ${l}`);
|
|
120716
120823
|
if (contentLines.length > cap) {
|
|
120717
|
-
lines.push(c.
|
|
120824
|
+
lines.push(c.text(` … (${contentLines.length - cap} more lines)`));
|
|
120718
120825
|
}
|
|
120719
120826
|
return lines.join(`
|
|
120720
120827
|
`);
|
|
@@ -120732,7 +120839,7 @@ function renderToolDetail(detail, targetWidth) {
|
|
|
120732
120839
|
const padWidth = Math.max(1, String(maxLineNum).length);
|
|
120733
120840
|
const blockWidth = targetWidth ? Math.max(30, targetWidth) : undefined;
|
|
120734
120841
|
const diffLines = [];
|
|
120735
|
-
const cap = 30;
|
|
120842
|
+
const cap = expanded ? Infinity : 30;
|
|
120736
120843
|
let count = 0;
|
|
120737
120844
|
let overflow = 0;
|
|
120738
120845
|
for (const hunk of detail.hunks) {
|
|
@@ -120758,7 +120865,7 @@ function renderToolDetail(detail, targetWidth) {
|
|
|
120758
120865
|
}
|
|
120759
120866
|
}
|
|
120760
120867
|
if (overflow > 0) {
|
|
120761
|
-
diffLines.push(c.
|
|
120868
|
+
diffLines.push(c.text(` … (${overflow} more lines)`));
|
|
120762
120869
|
}
|
|
120763
120870
|
return diffLines.join(`
|
|
120764
120871
|
`);
|
|
@@ -120833,7 +120940,7 @@ function formatToolStatus(options) {
|
|
|
120833
120940
|
} else {
|
|
120834
120941
|
headline = summaryVal.headline;
|
|
120835
120942
|
if (summaryVal.detail) {
|
|
120836
|
-
detailText = renderToolDetail(summaryVal.detail);
|
|
120943
|
+
detailText = renderToolDetail(summaryVal.detail, fullTermWidth, options.expanded);
|
|
120837
120944
|
}
|
|
120838
120945
|
}
|
|
120839
120946
|
if (headline) {
|
|
@@ -120854,11 +120961,20 @@ function formatToolStatus(options) {
|
|
|
120854
120961
|
}
|
|
120855
120962
|
if (status === "failed" || error) {
|
|
120856
120963
|
const rawError = error || "Operation failed";
|
|
120857
|
-
const
|
|
120858
|
-
`)
|
|
120964
|
+
const errLines = rawError.split(`
|
|
120965
|
+
`);
|
|
120966
|
+
const firstLineErr = errLines[0]?.trim() || rawError;
|
|
120859
120967
|
const maxErrLen = Math.max(10, fullTermWidth - 6);
|
|
120860
120968
|
const truncatedErr = firstLineErr.length > maxErrLen ? `${firstLineErr.slice(0, maxErrLen - 1)}…` : firstLineErr;
|
|
120861
120969
|
lines.push(` ${c.muted("└ ")}${c.error(truncatedErr)}`);
|
|
120970
|
+
if (options.expanded && errLines.length > 1) {
|
|
120971
|
+
for (let i = 1;i < errLines.length; i++) {
|
|
120972
|
+
const line = errLines[i];
|
|
120973
|
+
if (line.trim()) {
|
|
120974
|
+
lines.push(` ${c.error(line)}`);
|
|
120975
|
+
}
|
|
120976
|
+
}
|
|
120977
|
+
}
|
|
120862
120978
|
}
|
|
120863
120979
|
return lines;
|
|
120864
120980
|
}
|
|
@@ -120902,7 +121018,7 @@ function formatTurnFooter(engine, turnPresentationEnd) {
|
|
|
120902
121018
|
engine.commit("system", formatErrorBadge(structured));
|
|
120903
121019
|
}
|
|
120904
121020
|
}
|
|
120905
|
-
function renderTranscript(engine, sessionData, header) {
|
|
121021
|
+
function renderTranscript(engine, sessionData, header, options) {
|
|
120906
121022
|
engine.clearAll();
|
|
120907
121023
|
engine.commit("header", header.render());
|
|
120908
121024
|
const log = loadSessionLog(sessionData.date, sessionData.id);
|
|
@@ -120923,6 +121039,28 @@ function renderTranscript(engine, sessionData, header) {
|
|
|
120923
121039
|
engine.commit("system", formatSystemMessage(item.content));
|
|
120924
121040
|
} else if (item.type === "tool" && item.toolData) {
|
|
120925
121041
|
const toolData = item.toolData;
|
|
121042
|
+
if (toolData.toolName === "direct-bash") {
|
|
121043
|
+
const cmd = toolData.displayName || "";
|
|
121044
|
+
const lines = [`${c.permission("!")} ${c.text(cmd)}`];
|
|
121045
|
+
if (toolData.status === "completed") {
|
|
121046
|
+
if (toolData.toolOutput) {
|
|
121047
|
+
const outLines = toolData.toolOutput.split(/\r?\n/);
|
|
121048
|
+
lines.push(` ${c.muted("└ ")}${c.text(outLines[0] ?? "")}`);
|
|
121049
|
+
for (let i = 1;i < outLines.length; i++) {
|
|
121050
|
+
lines.push(` ${c.text(outLines[i] ?? "")}`);
|
|
121051
|
+
}
|
|
121052
|
+
}
|
|
121053
|
+
} else {
|
|
121054
|
+
const errMsg = toolData.error || (toolData.toolOutput ? `Command failed: ${toolData.toolOutput}` : "Command failed");
|
|
121055
|
+
const errLines = errMsg.split(/\r?\n/);
|
|
121056
|
+
lines.push(` ${c.muted("└ ")}${c.error(errLines[0] ?? "")}`);
|
|
121057
|
+
for (let i = 1;i < errLines.length; i++) {
|
|
121058
|
+
lines.push(` ${c.error(errLines[i] ?? "")}`);
|
|
121059
|
+
}
|
|
121060
|
+
}
|
|
121061
|
+
engine.commit("raw", lines);
|
|
121062
|
+
continue;
|
|
121063
|
+
}
|
|
120926
121064
|
const toolDef = defaultToolCatalog.get(toolData.toolName);
|
|
120927
121065
|
const displayName = toolData.displayName ?? toolDef?.displayName;
|
|
120928
121066
|
const icon = toolData.icon ?? toolDef?.icon;
|
|
@@ -120937,7 +121075,8 @@ function renderTranscript(engine, sessionData, header) {
|
|
|
120937
121075
|
durationMs: toolData.durationMs,
|
|
120938
121076
|
error: toolData.error,
|
|
120939
121077
|
summary,
|
|
120940
|
-
targetWidth: w
|
|
121078
|
+
targetWidth: w,
|
|
121079
|
+
expanded: options?.expanded
|
|
120941
121080
|
}), { hangingIndent: 2 });
|
|
120942
121081
|
} else if (item.type === "assistant") {
|
|
120943
121082
|
engine.commit("assistant-message", formatAssistantMessage(item.content), {
|
|
@@ -122582,6 +122721,91 @@ async function applyCommandResult(text, cmdResult, ctx) {
|
|
|
122582
122721
|
return true;
|
|
122583
122722
|
}
|
|
122584
122723
|
|
|
122724
|
+
// src/app/utils/bash.ts
|
|
122725
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
122726
|
+
async function executeDirectBash(options) {
|
|
122727
|
+
const { command, cwd = process.cwd(), abortSignal, onChunk } = options;
|
|
122728
|
+
const { shell, args } = getPlatformShell();
|
|
122729
|
+
const childArgs = [...args, command];
|
|
122730
|
+
const startTime = performance.now();
|
|
122731
|
+
return new Promise((resolve, reject) => {
|
|
122732
|
+
let child = null;
|
|
122733
|
+
let stdout = "";
|
|
122734
|
+
let stderr = "";
|
|
122735
|
+
let output = "";
|
|
122736
|
+
let isSettled = false;
|
|
122737
|
+
try {
|
|
122738
|
+
child = spawn2(shell, childArgs, {
|
|
122739
|
+
cwd,
|
|
122740
|
+
env: process.env,
|
|
122741
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
122742
|
+
detached: process.platform !== "win32"
|
|
122743
|
+
});
|
|
122744
|
+
} catch (err) {
|
|
122745
|
+
return reject(err);
|
|
122746
|
+
}
|
|
122747
|
+
const cleanup = () => {
|
|
122748
|
+
if (abortSignal) {
|
|
122749
|
+
abortSignal.removeEventListener("abort", onAbort);
|
|
122750
|
+
}
|
|
122751
|
+
};
|
|
122752
|
+
const finish = (exitCode) => {
|
|
122753
|
+
if (isSettled)
|
|
122754
|
+
return;
|
|
122755
|
+
isSettled = true;
|
|
122756
|
+
cleanup();
|
|
122757
|
+
const durationMs = Math.max(0, Math.round(performance.now() - startTime));
|
|
122758
|
+
resolve({
|
|
122759
|
+
command,
|
|
122760
|
+
exitCode,
|
|
122761
|
+
stdout,
|
|
122762
|
+
stderr,
|
|
122763
|
+
output: output.trim(),
|
|
122764
|
+
durationMs
|
|
122765
|
+
});
|
|
122766
|
+
};
|
|
122767
|
+
const onAbort = () => {
|
|
122768
|
+
if (child && !isSettled) {
|
|
122769
|
+
try {
|
|
122770
|
+
if (process.platform !== "win32" && child.pid) {
|
|
122771
|
+
process.kill(-child.pid, "SIGKILL");
|
|
122772
|
+
} else {
|
|
122773
|
+
child.kill("SIGKILL");
|
|
122774
|
+
}
|
|
122775
|
+
} catch {}
|
|
122776
|
+
}
|
|
122777
|
+
};
|
|
122778
|
+
if (abortSignal) {
|
|
122779
|
+
if (abortSignal.aborted) {
|
|
122780
|
+
onAbort();
|
|
122781
|
+
} else {
|
|
122782
|
+
abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
122783
|
+
}
|
|
122784
|
+
}
|
|
122785
|
+
child.stdout?.on("data", (chunk) => {
|
|
122786
|
+
const text = chunk.toString("utf-8");
|
|
122787
|
+
stdout += text;
|
|
122788
|
+
output += text;
|
|
122789
|
+
onChunk?.(text, output);
|
|
122790
|
+
});
|
|
122791
|
+
child.stderr?.on("data", (chunk) => {
|
|
122792
|
+
const text = chunk.toString("utf-8");
|
|
122793
|
+
stderr += text;
|
|
122794
|
+
output += text;
|
|
122795
|
+
onChunk?.(text, output);
|
|
122796
|
+
});
|
|
122797
|
+
child.on("error", (err) => {
|
|
122798
|
+
if (isSettled)
|
|
122799
|
+
return;
|
|
122800
|
+
isSettled = true;
|
|
122801
|
+
cleanup();
|
|
122802
|
+
reject(err);
|
|
122803
|
+
});
|
|
122804
|
+
child.on("close", (code) => finish(code));
|
|
122805
|
+
child.on("exit", (code) => finish(code));
|
|
122806
|
+
});
|
|
122807
|
+
}
|
|
122808
|
+
|
|
122585
122809
|
// src/app/app.ts
|
|
122586
122810
|
class TUIApp {
|
|
122587
122811
|
engine;
|
|
@@ -122599,6 +122823,7 @@ class TUIApp {
|
|
|
122599
122823
|
isScrollViewMode = false;
|
|
122600
122824
|
updateChecker;
|
|
122601
122825
|
permissionQueue;
|
|
122826
|
+
directBashAbortController = null;
|
|
122602
122827
|
constructor(options = {}) {
|
|
122603
122828
|
this.cwd = options.cwd ?? process.cwd();
|
|
122604
122829
|
this.session = options.initialSession ?? new AgentSession;
|
|
@@ -122615,6 +122840,7 @@ class TUIApp {
|
|
|
122615
122840
|
onSubmit: (text) => this.handleSubmit(text),
|
|
122616
122841
|
onAbort: () => this.handleAbort(),
|
|
122617
122842
|
onToggleHelp: () => this.modals.toggleHelp(),
|
|
122843
|
+
onBashModeChange: (isBash) => this.statusBar.setBashMode(isBash),
|
|
122618
122844
|
cwd: this.cwd,
|
|
122619
122845
|
initialHistory: this.session.session.turns.map((t) => {
|
|
122620
122846
|
const userMsg = t.messages.find((m) => m.role === "user");
|
|
@@ -122718,7 +122944,8 @@ class TUIApp {
|
|
|
122718
122944
|
}
|
|
122719
122945
|
proceedStart() {
|
|
122720
122946
|
this.engine.ensureAlternateScreen();
|
|
122721
|
-
|
|
122947
|
+
const logPath = getSessionLogPath(this.session.session.date, this.session.session.id);
|
|
122948
|
+
if (this.session.session.turns.length > 0 || existsSync16(logPath)) {
|
|
122722
122949
|
renderTranscript(this.engine, this.session.session, this.header);
|
|
122723
122950
|
} else {
|
|
122724
122951
|
this.engine.commit("header", this.header.render());
|
|
@@ -122797,19 +123024,25 @@ class TUIApp {
|
|
|
122797
123024
|
toggleScrollViewMode() {
|
|
122798
123025
|
if (this.isScrollViewMode) {
|
|
122799
123026
|
this.isScrollViewMode = false;
|
|
123027
|
+
renderTranscript(this.engine, this.session.session, this.header, { expanded: false });
|
|
123028
|
+
this.engine.mount(this.streamingView, { kind: "custom" });
|
|
122800
123029
|
this.engine.mount(this.promptInput, { keepCursorVisible: true, kind: "input" });
|
|
122801
123030
|
this.engine.mount(this.statusBar, { kind: "custom" });
|
|
122802
123031
|
this.engine.scrollToBottom();
|
|
122803
123032
|
} else {
|
|
122804
123033
|
this.isScrollViewMode = true;
|
|
123034
|
+
this.engine.unmount(this.streamingView);
|
|
122805
123035
|
this.engine.unmount(this.promptInput);
|
|
122806
123036
|
this.engine.unmount(this.statusBar);
|
|
122807
123037
|
this.engine.hideCursor();
|
|
122808
|
-
this.engine.
|
|
123038
|
+
renderTranscript(this.engine, this.session.session, this.header, { expanded: true });
|
|
122809
123039
|
}
|
|
122810
123040
|
}
|
|
122811
123041
|
handleAbort() {
|
|
122812
123042
|
if (this.isBusy) {
|
|
123043
|
+
if (this.directBashAbortController) {
|
|
123044
|
+
this.directBashAbortController.abort();
|
|
123045
|
+
}
|
|
122813
123046
|
this.permissionQueue.clear();
|
|
122814
123047
|
this.session.abort();
|
|
122815
123048
|
}
|
|
@@ -122833,6 +123066,98 @@ class TUIApp {
|
|
|
122833
123066
|
});
|
|
122834
123067
|
return;
|
|
122835
123068
|
}
|
|
123069
|
+
if (text.startsWith("!")) {
|
|
123070
|
+
const command = text.slice(1).trim();
|
|
123071
|
+
if (!command)
|
|
123072
|
+
return;
|
|
123073
|
+
this.setBusy(true);
|
|
123074
|
+
this.directBashAbortController = new AbortController;
|
|
123075
|
+
const callId = `direct-bash-${Date.now()}`;
|
|
123076
|
+
const startedAt = new Date().toISOString();
|
|
123077
|
+
const logWriter = new SessionLogWriter(this.session.session.date, this.session.session.id);
|
|
123078
|
+
logWriter.append({
|
|
123079
|
+
schemaVersion: 1,
|
|
123080
|
+
sessionId: this.session.session.id,
|
|
123081
|
+
turnId: `direct-${Date.now()}`,
|
|
123082
|
+
type: "tool-start",
|
|
123083
|
+
timestamp: startedAt,
|
|
123084
|
+
toolCallId: callId,
|
|
123085
|
+
toolName: "bash",
|
|
123086
|
+
startedAt
|
|
123087
|
+
});
|
|
123088
|
+
this.streamingView.setActiveCommand(command);
|
|
123089
|
+
try {
|
|
123090
|
+
const result = await executeDirectBash({
|
|
123091
|
+
command,
|
|
123092
|
+
cwd: this.cwd,
|
|
123093
|
+
abortSignal: this.directBashAbortController.signal,
|
|
123094
|
+
onChunk: (_chunk, fullOutput) => {
|
|
123095
|
+
const recent = fullOutput.split(/\r?\n/).filter(Boolean);
|
|
123096
|
+
this.streamingView.updateCommandOutput(recent);
|
|
123097
|
+
}
|
|
123098
|
+
});
|
|
123099
|
+
this.streamingView.setActiveCommand(null);
|
|
123100
|
+
this.streamingView.reset();
|
|
123101
|
+
logWriter.append({
|
|
123102
|
+
schemaVersion: 1,
|
|
123103
|
+
sessionId: this.session.session.id,
|
|
123104
|
+
turnId: `direct-${Date.now()}`,
|
|
123105
|
+
type: "tool-end",
|
|
123106
|
+
timestamp: new Date().toISOString(),
|
|
123107
|
+
toolCallId: callId,
|
|
123108
|
+
toolName: "direct-bash",
|
|
123109
|
+
finishedAt: new Date().toISOString(),
|
|
123110
|
+
durationMs: result.durationMs,
|
|
123111
|
+
status: result.exitCode === 0 ? "completed" : "failed",
|
|
123112
|
+
displayName: command,
|
|
123113
|
+
outputSummary: result.output,
|
|
123114
|
+
errorMessage: result.exitCode !== 0 ? `Command failed with exit code ${result.exitCode ?? 1}` : undefined
|
|
123115
|
+
});
|
|
123116
|
+
const lines = [`${c.permission("!")} ${c.text(command)}`];
|
|
123117
|
+
if (result.exitCode === 0) {
|
|
123118
|
+
if (result.output) {
|
|
123119
|
+
const outLines = result.output.split(/\r?\n/);
|
|
123120
|
+
lines.push(` ${c.muted("└ ")}${c.text(outLines[0] ?? "")}`);
|
|
123121
|
+
for (let i = 1;i < outLines.length; i++) {
|
|
123122
|
+
lines.push(` ${c.text(outLines[i] ?? "")}`);
|
|
123123
|
+
}
|
|
123124
|
+
}
|
|
123125
|
+
} else {
|
|
123126
|
+
const errMsg = `Command failed with exit code ${result.exitCode ?? 1}${result.output ? ": " + result.output : ""}`;
|
|
123127
|
+
const errLines = errMsg.split(/\r?\n/);
|
|
123128
|
+
lines.push(` ${c.muted("└ ")}${c.error(errLines[0] ?? "")}`);
|
|
123129
|
+
for (let i = 1;i < errLines.length; i++) {
|
|
123130
|
+
lines.push(` ${c.error(errLines[i] ?? "")}`);
|
|
123131
|
+
}
|
|
123132
|
+
}
|
|
123133
|
+
this.engine.commit("raw", lines);
|
|
123134
|
+
} catch (err) {
|
|
123135
|
+
this.streamingView.setActiveCommand(null);
|
|
123136
|
+
this.streamingView.reset();
|
|
123137
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
123138
|
+
logWriter.append({
|
|
123139
|
+
schemaVersion: 1,
|
|
123140
|
+
sessionId: this.session.session.id,
|
|
123141
|
+
turnId: `direct-${Date.now()}`,
|
|
123142
|
+
type: "tool-end",
|
|
123143
|
+
timestamp: new Date().toISOString(),
|
|
123144
|
+
toolCallId: callId,
|
|
123145
|
+
toolName: "direct-bash",
|
|
123146
|
+
finishedAt: new Date().toISOString(),
|
|
123147
|
+
status: "failed",
|
|
123148
|
+
displayName: command,
|
|
123149
|
+
errorMessage: msg
|
|
123150
|
+
});
|
|
123151
|
+
this.engine.commit("raw", [
|
|
123152
|
+
`${c.permission("!")} ${c.text(command)}`,
|
|
123153
|
+
` ${c.muted("└ ")}${c.error(msg)}`
|
|
123154
|
+
]);
|
|
123155
|
+
} finally {
|
|
123156
|
+
this.directBashAbortController = null;
|
|
123157
|
+
this.setBusy(false);
|
|
123158
|
+
}
|
|
123159
|
+
return;
|
|
123160
|
+
}
|
|
122836
123161
|
this.engine.commitPrompt(text);
|
|
122837
123162
|
this.setBusy(true);
|
|
122838
123163
|
this.streamingView.setThinking(true);
|
package/dist/steward
CHANGED
package/package.json
CHANGED