Skip to content

Commit f6da277

Browse files
Merge pull request #167 from penta2himajin/claude/issue166-stdin-eof
fix(cli): read piped stdin to EOF instead of the first line (#166)
2 parents b2cf11b + 3eeb12d commit f6da277

2 files changed

Lines changed: 31 additions & 1 deletion

File tree

‎scripts/test_completion.sh‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,20 @@ total="$(printf '%s\n' "$line" | sed 's|COMPTEST ||' | cut -d/ -f2)"
2222
if printf '%s\n' "$out" | grep -q 'FAIL' || [ "$passed" != "$total" ]; then
2323
echo "RESULT: FAIL ($line)"; exit 1
2424
fi
25+
# ── #166: piped stdin must be read to EOF, not truncated to the first line ──────────
26+
# QWISP_FAKE=1 uses FakeBackend: no model weights, no GPU — only the tokenizer this gate
27+
# already requires. Asserts a strict increase rather than an exact count so it does not
28+
# encode the tokenizer's segmentation. Before the fix both arms reported 11 tok.
29+
tok_of() {
30+
printf '%b' "$1" | QWISP_FAKE=1 QWISP_MODEL="$MODEL" "$BIN" chat --max-tokens 2 2>&1 \
31+
| sed -n 's/.*prompt \([0-9]*\) tok.*/\1/p'
32+
}
33+
one="$(tok_of 'alpha\n')"
34+
two="$(tok_of 'alpha\nbeta gamma delta epsilon zeta eta theta iota kappa lambda\n')"
35+
if [ -z "$one" ] || [ -z "$two" ] || [ "$two" -le "$one" ]; then
36+
echo "RESULT: FAIL (piped stdin truncated to line 1: 1-line=${one:-NA} tok, 2-line=${two:-NA} tok — #166)"
37+
exit 1
38+
fi
39+
echo "[stdin] piped multi-line read to EOF: 1-line=$one tok < 2-line=$two tok ok"
40+
2541
echo "RESULT: PASS ($line)"

‎swift/Sources/qwisp/main.swift‎

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,21 @@ case "chat":
123123
}
124124
}
125125
let promptText = rest.joined(separator: " ")
126-
let prompt = promptText.isEmpty ? (readLine(strippingNewline: true) ?? "") : promptText
126+
// Piped stdin is a documented input path ("or pipe text via stdin" in the usage line), and
127+
// readLine() takes only the FIRST line — `cat spec.md | qwisp chat` silently answered on
128+
// line 1 and exited 0 (#166; it ingested 52 tokens of a 35K-token file while reporting a
129+
// healthy run). Read to EOF when stdin is not a TTY; keep readLine() interactively, where
130+
// one line IS what the user means and reading to EOF would block until ^D.
131+
let prompt: String
132+
if !promptText.isEmpty {
133+
prompt = promptText
134+
} else if isatty(FileHandle.standardInput.fileDescriptor) == 1 {
135+
prompt = readLine(strippingNewline: true) ?? ""
136+
} else {
137+
let piped = FileHandle.standardInput.readDataToEndOfFile()
138+
prompt = String(decoding: piped, as: UTF8.self)
139+
.trimmingCharacters(in: .whitespacesAndNewlines)
140+
}
127141
if prompt.isEmpty {
128142
print("usage: qwisp chat [--max-tokens N] [--lossless] <prompt> (or pipe text via stdin)")
129143
} else {

0 commit comments

Comments
 (0)