Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/cli/acw.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ autoload -Uz compinit && compinit
- `--stdout` behavior:
- Without `--chat`: merges provider stderr into stdout so progress and output can be piped together.
- With `--chat`: provider stderr is appended to `.tmp/acw-sessions/<session-id>.stderr` to keep stdout clean for piping. Empty sidecar files created by `acw` are automatically removed.
- With `--chat --editor`: when stdout is a TTY, the editor prompt is echoed to stdout before assistant output.
- With `--chat --editor`: when stdout is a TTY, the user prompt is echoed immediately before provider invocation, so it appears before assistant output.
- In file mode (no `--stdout`), provider stderr is written to `<output-file>.stderr`. Empty sidecar files are removed after the provider exits.

## See Also
Expand Down
3 changes: 2 additions & 1 deletion src/cli/acw/dispatch.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ and turn appending:
output is captured to a temp file. After the provider exits, the captured
content is emitted to stdout and the assistant response is appended to the
session file. If `--editor` is used and stdout is a TTY, the editor prompt
is echoed to stdout before the assistant output.
is echoed to stdout before provider invocation so it appears before assistant
output.

**Stderr sidecar**: When `--stdout` is combined with `--chat`, provider stderr
is appended to `<session-id>.stderr` beside the session file rather than
Expand Down
11 changes: 6 additions & 5 deletions src/cli/acw/dispatch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,12 @@ acw() {
fi
fi

if [ "$chat_mode" -eq 1 ] && [ "$stdout_mode" -eq 1 ] && [ "$use_editor" -eq 1 ] && [ -t 1 ]; then
echo "User Prompt:"
cat "$original_input_file"
echo ""
fi

# Remaining arguments are provider options
local provider_exit=0
local stderr_file=""
Expand Down Expand Up @@ -452,11 +458,6 @@ acw() {
local assistant_response=""
if [ "$stdout_mode" -eq 1 ]; then
assistant_response="$chat_output_capture"
if [ "$use_editor" -eq 1 ] && [ -t 1 ]; then
echo "User Prompt:"
cat "$original_input_file"
echo ""
fi
# Emit captured output to stdout
cat "$chat_output_capture"
else
Expand Down
1 change: 1 addition & 0 deletions tests/cli/test-acw-chat.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ End-to-end tests for `acw` chat session functionality (`--chat` and `--chat-list
### Stdout Capture
- `--chat --stdout` captures and emits assistant output
- Captured output is also appended to session file
- `--chat --editor --stdout` on TTY echoes the user prompt before assistant output

### Stderr Sidecar (--chat --stdout)
- Provider stderr is written to `<session-id>.stderr` sidecar file
Expand Down
85 changes: 85 additions & 0 deletions tests/cli/test-acw-chat.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@ mkdir -p "$TEST_TMP/.tmp/acw-sessions"

source "$ACW_CLI"

TEST_BIN="$TEST_TMP/bin"
mkdir -p "$TEST_BIN"

# Stub claude provider binary for chat stdout coverage
cat > "$TEST_BIN/claude" << 'STUB'
#!/usr/bin/env bash
echo "Stub assistant output"
STUB
chmod +x "$TEST_BIN/claude"

export PATH="$TEST_BIN:$PATH"

# ============================================================
# Test 1: Chat helper functions exist
# ============================================================
Expand Down Expand Up @@ -385,4 +397,77 @@ if [ ! -f "$preexist_stderr" ]; then
test_fail "Pre-existing empty stderr sidecar should have been preserved"
fi

# ============================================================
# Test 16: TTY prompt echo appears before assistant output in chat stdout
# ============================================================
test_info "Checking chat stdout TTY prompt echo ordering"

TTY_EDITOR="$TEST_TMP/tty-editor.sh"
cat > "$TTY_EDITOR" << 'STUB'
#!/usr/bin/env bash
cat > "$1" << 'EOF'
TTY prompt content
EOF
STUB
chmod +x "$TTY_EDITOR"

if ! command -v script >/dev/null 2>&1; then
test_fail "script command is required for TTY stdout testing"
fi

TTY_RUNNER="$TEST_TMP/tty-run.sh"
cat > "$TTY_RUNNER" << STUB
#!/usr/bin/env bash
source "$ACW_CLI"
acw --chat --editor --stdout claude test-model
STUB
chmod +x "$TTY_RUNNER"

script_flavor="bsd"
if script --version >/dev/null 2>&1; then
script_flavor="util-linux"
fi

export EDITOR="$TTY_EDITOR"

set +e
if [ "$script_flavor" = "util-linux" ]; then
tty_output=$(script -q -c "bash \"$TTY_RUNNER\"" /dev/null 2>/dev/null)
else
tty_output=$(script -q /dev/null bash "$TTY_RUNNER" 2>/dev/null)
fi
exit_code=$?
set -e

if [ "$exit_code" -ne 0 ]; then
test_info "script stdout (first 40 lines):"
printf "%s\n" "$tty_output" | sed -n '1,40p'
test_fail "--chat --editor --stdout should succeed on TTY stdout"
fi

clean_tty_output=$(printf "%s\n" "$tty_output" | tr -d '\r')

if ! printf "%s\n" "$clean_tty_output" | grep -q "^User Prompt:"; then
test_fail "TTY stdout should include User Prompt header"
fi

if ! printf "%s\n" "$clean_tty_output" | grep -q "TTY prompt content"; then
test_fail "TTY stdout should include editor prompt content"
fi

if ! printf "%s\n" "$clean_tty_output" | grep -q "Stub assistant output"; then
test_fail "TTY stdout should include assistant output"
fi

prompt_line=$(printf "%s\n" "$clean_tty_output" | awk '/^User Prompt:/{print NR; exit}')
assistant_line=$(printf "%s\n" "$clean_tty_output" | awk '/Stub assistant output/{print NR; exit}')

if [ -z "$prompt_line" ] || [ -z "$assistant_line" ]; then
test_fail "TTY stdout should include prompt and assistant output"
fi

if [ "$prompt_line" -gt "$assistant_line" ]; then
test_fail "TTY stdout should echo prompt before assistant output"
fi

test_pass "All chat session tests passed"