Skip to content

Commit 15ffcf1

Browse files
committed
Improve terminal detection and escape sequence handling
Replace custom terminal detection with golang.org/x/term library.
1 parent 7b2517c commit 15ffcf1

File tree

3 files changed

+22
-8
lines changed

3 files changed

+22
-8
lines changed

go.mod

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ require (
77
github.com/aws/aws-sdk-go-v2 v1.36.2
88
github.com/aws/aws-sdk-go-v2/config v1.29.7
99
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.25.0
10+
golang.org/x/term v0.29.0
1011
rsc.io/markdown v0.0.0-20241212154241-6bf72452917f
1112
)
1213

@@ -23,5 +24,6 @@ require (
2324
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.15 // indirect
2425
github.com/aws/aws-sdk-go-v2/service/sts v1.33.15 // indirect
2526
github.com/aws/smithy-go v1.22.3 // indirect
27+
golang.org/x/sys v0.30.0 // indirect
2628
golang.org/x/text v0.22.0 // indirect
2729
)

go.sum

+4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k=
3232
github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
3333
github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68=
3434
github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
35+
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
36+
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
37+
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
38+
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
3539
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
3640
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
3741
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=

main.go

+16-8
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"slices"
2222
"strconv"
2323
"strings"
24+
"sync"
2425
"time"
2526
"unicode/utf8"
2627

@@ -29,13 +30,14 @@ import (
2930
"github.com/aws/aws-sdk-go-v2/service/bedrockruntime"
3031
"github.com/aws/aws-sdk-go-v2/service/bedrockruntime/document"
3132
"github.com/aws/aws-sdk-go-v2/service/bedrockruntime/types"
33+
"golang.org/x/term"
3234
"rsc.io/markdown"
3335
)
3436

3537
func main() {
3638
log.SetFlags(0)
3739
log.SetPrefix("llmcli: ")
38-
if st, err := os.Stderr.Stat(); err == nil && st.Mode()&os.ModeCharDevice != 0 {
40+
if term.IsTerminal(int(os.Stderr.Fd())) {
3941
log.SetPrefix("\033[1m" + log.Prefix() + ansiReset)
4042
}
4143
args := runArgs{model: os.Getenv("LLMCLI_MODEL")}
@@ -200,16 +202,16 @@ func run(ctx context.Context, args runArgs) error {
200202
if args.budget != 0 {
201203
// just in case we changed stdout formatting at the start of “thinking”
202204
// but failed mid-way before resetting formatting
203-
defer os.Stdout.WriteString(ansiReset)
205+
defer termWrite(ansiReset)
204206
}
205207
var thinking bool
206208
for chunk := range rc.Chunks() {
207209
if !thinking && chunk.thinking {
208210
thinking = true
209-
os.Stdout.WriteString(ansiItalic) // bypassing wr
211+
termWrite(ansiItalic) // bypassing wr
210212
} else if thinking && !chunk.thinking {
211213
thinking = false
212-
os.Stdout.WriteString(ansiReset) // bypassing wr
214+
termWrite(ansiReset) // bypassing wr
213215
io.WriteString(wr, "\n\n* * *\n\n")
214216
}
215217
io.WriteString(wr, chunk.text)
@@ -226,6 +228,15 @@ func run(ctx context.Context, args runArgs) error {
226228
return nil
227229
}
228230

231+
var stdoutIsTerm = sync.OnceValue(func() bool { return term.IsTerminal(int(os.Stdout.Fd())) })
232+
233+
func termWrite(s string) {
234+
if !stdoutIsTerm() {
235+
return
236+
}
237+
os.Stdout.WriteString(s)
238+
}
239+
229240
func newResponseConsumer(cso *bedrockruntime.ConverseStreamOutput) *responseConsumer {
230241
return &responseConsumer{cso: cso}
231242
}
@@ -283,10 +294,7 @@ func (r *responseConsumer) Chunks() iter.Seq[chunk] {
283294
}
284295

285296
func readPrompt(args runArgs) (string, error) {
286-
var stdinIsTerminal bool
287-
if st, err := os.Stdin.Stat(); err == nil {
288-
stdinIsTerminal = st.Mode()&os.ModeCharDevice != 0
289-
}
297+
stdinIsTerminal := term.IsTerminal(int(os.Stdin.Fd()))
290298
var pb strings.Builder
291299
var stdinData []byte
292300
var err error

0 commit comments

Comments
 (0)