(command: string)
| 952 | } |
| 953 | |
| 954 | private runShellCommandFromInput(command: string): void { |
| 955 | const session = this.session; |
| 956 | if (session === undefined) { |
| 957 | this.showError('No active session for shell command.'); |
| 958 | return; |
| 959 | } |
| 960 | // Echo the command locally (bash-input) with a `$` prompt. The agent also |
| 961 | // records it for resume; this is the live view. |
| 962 | this.appendTranscriptEntry({ |
| 963 | id: nextTranscriptId(), |
| 964 | kind: 'user', |
| 965 | turnId: undefined, |
| 966 | renderMode: 'plain', |
| 967 | content: currentTheme.fg('shellMode', `$ ${command}`), |
| 968 | bullet: '', |
| 969 | }); |
| 970 | // Create the live output entry up front. ShellRunComponent owns its own |
| 971 | // rendering (running card → final view) and is mutated in place as output |
| 972 | // streams in and on completion. |
| 973 | const commandId = nextTranscriptId(); |
| 974 | const outputEntry: TranscriptEntry = { |
| 975 | id: commandId, |
| 976 | kind: 'status', |
| 977 | turnId: undefined, |
| 978 | renderMode: 'plain', |
| 979 | content: '', |
| 980 | }; |
| 981 | const outputComponent = new ShellRunComponent(() => this.state.ui.requestRender()); |
| 982 | this.shellOutputStreams.set(commandId, { entry: outputEntry, component: outputComponent }); |
| 983 | this.state.transcriptEntries.push(outputEntry); |
| 984 | markTranscriptComponent(outputComponent, outputEntry); |
| 985 | this.state.transcriptContainer.addChild(outputComponent); |
| 986 | // Treat command execution as a streaming phase so input queues, the activity |
| 987 | // pane shows the moon spinner, and ctrl+b is enabled while it runs. |
| 988 | this.setAppState({ streamingPhase: 'shell' }); |
| 989 | this.state.ui.requestRender(); |
| 990 | |
| 991 | this.track('shell_command'); |
| 992 | |
| 993 | void session.runShellCommand(command, { commandId }).then( |
| 994 | ({ stdout, stderr, isError, backgrounded }) => { |
| 995 | this.finishShellOutput(commandId, stdout, stderr, isError, backgrounded); |
| 996 | }, |
| 997 | (error: unknown) => { |
| 998 | const message = formatErrorMessage(error); |
| 999 | this.finishShellOutput(commandId, '', message, true); |
| 1000 | this.showError(`Shell command failed: ${message}`); |
| 1001 | }, |
| 1002 | ); |
| 1003 | } |
| 1004 | |
| 1005 | handleShellOutput(event: { commandId: string; update: { kind: string; text?: string } }): void { |
| 1006 | const stream = this.shellOutputStreams.get(event.commandId); |
no test coverage detected