on main help sign in
Merge pull request #42 from charmbracelet/ref
d656802Raphael Amorim
@@ -32,6 +32,7 @@ require (
32 github.com/pressly/goose/v3 v3.24.2
32 github.com/pressly/goose/v3 v3.24.2
33 github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06
33 github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06
34 github.com/sahilm/fuzzy v0.1.1
34 github.com/sahilm/fuzzy v0.1.1
35+ github.com/shirou/gopsutil/v4 v4.25.5
35 github.com/spf13/cobra v1.9.1
36 github.com/spf13/cobra v1.9.1
36 github.com/spf13/viper v1.20.0
37 github.com/spf13/viper v1.20.0
37 github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c
38 github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c
@@ -74,9 +75,11 @@ require (
74 github.com/disintegration/gift v1.1.2 // indirect
75 github.com/disintegration/gift v1.1.2 // indirect
75 github.com/dlclark/regexp2 v1.11.4 // indirect
76 github.com/dlclark/regexp2 v1.11.4 // indirect
76 github.com/dustin/go-humanize v1.0.1 // indirect
77 github.com/dustin/go-humanize v1.0.1 // indirect
78+ github.com/ebitengine/purego v0.8.4 // indirect
77 github.com/felixge/httpsnoop v1.0.4 // indirect
79 github.com/felixge/httpsnoop v1.0.4 // indirect
78 github.com/go-logr/logr v1.4.2 // indirect
80 github.com/go-logr/logr v1.4.2 // indirect
79 github.com/go-logr/stdr v1.2.2 // indirect
81 github.com/go-logr/stdr v1.2.2 // indirect
82+ github.com/go-ole/go-ole v1.2.6 // indirect
80 github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
83 github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
81 github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
84 github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
82 github.com/google/go-cmp v0.7.0 // indirect
85 github.com/google/go-cmp v0.7.0 // indirect
@@ -88,6 +91,7 @@ require (
88 github.com/inconshreveable/mousetrap v1.1.0 // indirect
91 github.com/inconshreveable/mousetrap v1.1.0 // indirect
89 github.com/kylelemons/godebug v1.1.0 // indirect
92 github.com/kylelemons/godebug v1.1.0 // indirect
90 github.com/lucasb-eyer/go-colorful v1.2.0
93 github.com/lucasb-eyer/go-colorful v1.2.0
94+ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
91 github.com/mattn/go-isatty v0.0.20 // indirect
95 github.com/mattn/go-isatty v0.0.20 // indirect
92 github.com/mattn/go-runewidth v0.0.16 // indirect
96 github.com/mattn/go-runewidth v0.0.16 // indirect
93 github.com/mfridman/interpolate v0.0.2 // indirect
97 github.com/mfridman/interpolate v0.0.2 // indirect
@@ -101,6 +105,7 @@ require (
101 github.com/pelletier/go-toml/v2 v2.2.3 // indirect
105 github.com/pelletier/go-toml/v2 v2.2.3 // indirect
102 github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
106 github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
103 github.com/pmezard/go-difflib v1.0.0 // indirect
107 github.com/pmezard/go-difflib v1.0.0 // indirect
108+ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
104 github.com/rivo/uniseg v0.4.7
109 github.com/rivo/uniseg v0.4.7
105 github.com/rogpeppe/go-internal v1.14.1 // indirect
110 github.com/rogpeppe/go-internal v1.14.1 // indirect
106 github.com/sagikazarmark/locafero v0.7.0 // indirect
111 github.com/sagikazarmark/locafero v0.7.0 // indirect
@@ -116,10 +121,13 @@ require (
116 github.com/tidwall/match v1.1.1 // indirect
121 github.com/tidwall/match v1.1.1 // indirect
117 github.com/tidwall/pretty v1.2.1 // indirect
122 github.com/tidwall/pretty v1.2.1 // indirect
118 github.com/tidwall/sjson v1.2.5 // indirect
123 github.com/tidwall/sjson v1.2.5 // indirect
124+ github.com/tklauser/go-sysconf v0.3.12 // indirect
125+ github.com/tklauser/numcpus v0.6.1 // indirect
119 github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
126 github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
120 github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
127 github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
121 github.com/yuin/goldmark v1.7.8 // indirect
128 github.com/yuin/goldmark v1.7.8 // indirect
122 github.com/yuin/goldmark-emoji v1.0.5 // indirect
129 github.com/yuin/goldmark-emoji v1.0.5 // indirect
130+ github.com/yusufpapurcu/wmi v1.2.4 // indirect
123 go.opentelemetry.io/auto/sdk v1.1.0 // indirect
131 go.opentelemetry.io/auto/sdk v1.1.0 // indirect
124 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
132 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
125 go.opentelemetry.io/otel v1.35.0 // indirect
133 go.opentelemetry.io/otel v1.35.0 // indirect
go.sum+21
@@ -108,6 +108,8 @@ github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yA
108github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
108github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
109github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
109github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
110github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
110github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
111+github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
112+github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
111github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
113github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
112github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
114github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
113github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
115github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
@@ -121,12 +123,15 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
121github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
123github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
122github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
124github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
123github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
125github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
126+github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
127+github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
124github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
128github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
125github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
129github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
126github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
130github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
127github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
131github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
128github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
132github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
129github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
133github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
134+github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
130github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
135github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
131github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
136github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
132github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM=
137github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM=
@@ -156,6 +161,8 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
156github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
161github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
157github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
162github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
158github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
163github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
164+github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
165+github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
159github.com/mark3labs/mcp-go v0.17.0 h1:5Ps6T7qXr7De/2QTqs9h6BKeZ/qdeUeGrgM5lPzi930=
166github.com/mark3labs/mcp-go v0.17.0 h1:5Ps6T7qXr7De/2QTqs9h6BKeZ/qdeUeGrgM5lPzi930=
160github.com/mark3labs/mcp-go v0.17.0/go.mod h1:KmJndYv7GIgcPVwEKJjNcbhVQ+hJGJhrCCB/9xITzpE=
167github.com/mark3labs/mcp-go v0.17.0/go.mod h1:KmJndYv7GIgcPVwEKJjNcbhVQ+hJGJhrCCB/9xITzpE=
161github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
168github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
@@ -195,6 +202,8 @@ github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjL
195github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
202github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
196github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
203github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
197github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
204github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
205+github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
206+github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
198github.com/pressly/goose/v3 v3.24.2 h1:c/ie0Gm8rnIVKvnDQ/scHErv46jrDv9b4I0WRcFJzYU=
207github.com/pressly/goose/v3 v3.24.2 h1:c/ie0Gm8rnIVKvnDQ/scHErv46jrDv9b4I0WRcFJzYU=
199github.com/pressly/goose/v3 v3.24.2/go.mod h1:kjefwFB0eR4w30Td2Gj2Mznyw94vSP+2jJYkOVNbD1k=
208github.com/pressly/goose/v3 v3.24.2/go.mod h1:kjefwFB0eR4w30Td2Gj2Mznyw94vSP+2jJYkOVNbD1k=
200github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
209github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
@@ -219,6 +228,8 @@ github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN
219github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
228github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
220github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE=
229github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE=
221github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas=
230github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas=
231+github.com/shirou/gopsutil/v4 v4.25.5 h1:rtd9piuSMGeU8g1RMXjZs9y9luK5BwtnG7dZaQUJAsc=
232+github.com/shirou/gopsutil/v4 v4.25.5/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c=
222github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
233github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
223github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
234github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
224github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
235github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
@@ -255,6 +266,10 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
255github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
266github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
256github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
267github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
257github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
268github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
269+github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
270+github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
271+github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
272+github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
258github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
273github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
259github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
274github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
260github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
275github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
@@ -265,6 +280,8 @@ github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
265github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
280github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
266github.com/yuin/goldmark-emoji v1.0.5 h1:EMVWyCGPlXJfUXBXpuMu+ii3TIaxbVBnEX9uaDC4cIk=
281github.com/yuin/goldmark-emoji v1.0.5 h1:EMVWyCGPlXJfUXBXpuMu+ii3TIaxbVBnEX9uaDC4cIk=
267github.com/yuin/goldmark-emoji v1.0.5/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U=
282github.com/yuin/goldmark-emoji v1.0.5/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U=
283+github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
284+github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
268go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
285go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
269go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
286go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
270go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
287go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
@@ -311,7 +328,9 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
311golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
328golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
312golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
329golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
313golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
330golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
331+golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
314golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
332golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
333+golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
315golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
334golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
316golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
335golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
317golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
336golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -320,6 +339,7 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
320golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
339golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
321golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
340golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
322golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
341golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
342+golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
323golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
343golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
324golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
344golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
325golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
345golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
@@ -347,6 +367,7 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
347golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
367golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
348golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
368golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
349golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
369golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
370+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
350google.golang.org/genai v1.3.0 h1:tXhPJF30skOjnnDY7ZnjK3q7IKy4PuAlEA0fk7uEaEI=
371google.golang.org/genai v1.3.0 h1:tXhPJF30skOjnnDY7ZnjK3q7IKy4PuAlEA0fk7uEaEI=
351google.golang.org/genai v1.3.0/go.mod h1:TyfOKRz/QyCaj6f/ZDt505x+YreXnY40l2I6k8TvgqY=
372google.golang.org/genai v1.3.0/go.mod h1:TyfOKRz/QyCaj6f/ZDt505x+YreXnY40l2I6k8TvgqY=
352google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g=
373google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g=
internal/fsext/fileutil.go+17−7
@@ -25,12 +25,10 @@ func init() {
25 rgPath, err = exec.LookPath("rg")
25 rgPath, err = exec.LookPath("rg")
26 if err != nil {
26 if err != nil {
27 logging.Warn("Ripgrep (rg) not found in $PATH. Some features might be limited or slower.")
27 logging.Warn("Ripgrep (rg) not found in $PATH. Some features might be limited or slower.")
28− rgPath = ""
29 }
28 }
30 fzfPath, err = exec.LookPath("fzf")
29 fzfPath, err = exec.LookPath("fzf")
31 if err != nil {
30 if err != nil {
32 logging.Warn("FZF not found in $PATH. Some features might be limited or slower.")
31 logging.Warn("FZF not found in $PATH. Some features might be limited or slower.")
33− fzfPath = ""
34 }
32 }
35}
33}
36
34
@@ -49,9 +47,21 @@ func GetRgCmd(globPattern string) *exec.Cmd {
49 }
47 }
50 rgArgs = append(rgArgs, "--glob", globPattern)
48 rgArgs = append(rgArgs, "--glob", globPattern)
51 }
49 }
52− cmd := exec.Command(rgPath, rgArgs...)
50+ return exec.Command(rgPath, rgArgs...)
53− cmd.Dir = "."
51+}
54− return cmd
52+
53+func GetRgSearchCmd(pattern, path, include string) *exec.Cmd {
54+ if rgPath == "" {
55+ return nil
56+ }
57+ // Use -n to show line numbers and include the matched line
58+ args := []string{"-n", pattern}
59+ if include != "" {
60+ args = append(args, "--glob", include)
61+ }
62+ args = append(args, path)
63+
64+ return exec.Command("rg", args...)
55}
65}
56
66
57type FileInfo struct {
67type FileInfo struct {
@@ -89,8 +99,8 @@ func SkipHidden(path string) bool {
89 "jspm_packages": true,
99 "jspm_packages": true,
90 }
100 }
91
101
92− parts := strings.Split(path, string(os.PathSeparator))
102+ parts := strings.SplitSeq(path, string(os.PathSeparator))
93− for _, part := range parts {
103+ for part := range parts {
94 if commonIgnoredDirs[part] {
104 if commonIgnoredDirs[part] {
95 return true
105 return true
96 }
106 }
internal/llm/tools/glob.go+1−1
@@ -150,7 +150,7 @@ func runRipgrep(cmd *exec.Cmd, searchRoot string, limit int) ([]string, error) {
150 }
150 }
151
151
152 var matches []string
152 var matches []string
153− for _, p := range bytes.Split(out, []byte{0}) {
153+ for p := range bytes.SplitSeq(out, []byte{0}) {
154 if len(p) == 0 {
154 if len(p) == 0 {
155 continue
155 continue
156 }
156 }
internal/llm/tools/grep.go+3−11
@@ -257,19 +257,11 @@ func searchFiles(pattern, rootPath, include string, limit int) ([]grepMatch, boo
257}
257}
258
258
259func searchWithRipgrep(pattern, path, include string) ([]grepMatch, error) {
259func searchWithRipgrep(pattern, path, include string) ([]grepMatch, error) {
260− _, err := exec.LookPath("rg")
260+ cmd := fsext.GetRgSearchCmd(pattern, path, include)
261− if err != nil {
261+ if cmd == nil {
262− return nil, fmt.Errorf("ripgrep not found: %w", err)
262+ return nil, fmt.Errorf("ripgrep not found in $PATH")
263− }
264−
265− // Use -n to show line numbers and include the matched line
266− args := []string{"-n", pattern}
267− if include != "" {
268− args = append(args, "--glob", include)
269 }
263 }
270− args = append(args, path)
271
264
272− cmd := exec.Command("rg", args...)
273 output, err := cmd.Output()
265 output, err := cmd.Output()
274 if err != nil {
266 if err != nil {
275 if exitErr, ok := err.(*exec.ExitError); ok && exitErr.ExitCode() == 1 {
267 if exitErr, ok := err.(*exec.ExitError); ok && exitErr.ExitCode() == 1 {
internal/llm/tools/shell/shell.go+59−90
@@ -1,9 +1,11 @@
1package shell
1package shell
2
2
3import (
3import (
4+ "cmp"
4 "context"
5 "context"
5 "errors"
6 "errors"
6 "fmt"
7 "fmt"
8+ "io"
7 "os"
9 "os"
8 "os/exec"
10 "os/exec"
9 "path/filepath"
11 "path/filepath"
@@ -13,11 +15,13 @@ import (
13 "time"
15 "time"
14
16
15 "github.com/charmbracelet/crush/internal/config"
17 "github.com/charmbracelet/crush/internal/config"
18+ "github.com/charmbracelet/crush/internal/logging"
19+ "github.com/shirou/gopsutil/v4/process"
16)
20)
17
21
18type PersistentShell struct {
22type PersistentShell struct {
19 cmd *exec.Cmd
23 cmd *exec.Cmd
20− stdin *os.File
24+ stdin io.WriteCloser
21 isAlive bool
25 isAlive bool
22 cwd string
26 cwd string
23 mu sync.Mutex
27 mu sync.Mutex
@@ -39,22 +43,15 @@ type commandResult struct {
39 err error
43 err error
40}
44}
41
45
42−var (
43− shellInstance *PersistentShell
46+var shellInstance *PersistentShell
44− shellInstanceOnce sync.Once
45−)
46
47
47func GetPersistentShell(workingDir string) *PersistentShell {
48func GetPersistentShell(workingDir string) *PersistentShell {
48− shellInstanceOnce.Do(func() {
49− shellInstance = newPersistentShell(workingDir)
50− })
51−
52 if shellInstance == nil {
49 if shellInstance == nil {
53 shellInstance = newPersistentShell(workingDir)
50 shellInstance = newPersistentShell(workingDir)
51+ }
54− } else if !shellInstance.isAlive {
52+ if !shellInstance.isAlive {
55 shellInstance = newPersistentShell(shellInstance.cwd)
53 shellInstance = newPersistentShell(shellInstance.cwd)
56 }
54 }
57−
58 return shellInstance
55 return shellInstance
59}
56}
60
57
@@ -71,16 +68,15 @@ func newPersistentShell(cwd string) *PersistentShell {
71 shellArgs = cfg.Shell.Args
68 shellArgs = cfg.Shell.Args
72 }
69 }
73
70
74− if shellPath == "" {
75− shellPath = os.Getenv("SHELL")
71+ shellPath = cmp.Or(shellPath, os.Getenv("SHELL"), "/bin/bash")
76− if shellPath == "" {
72+ if !strings.HasSuffix(shellPath, "bash") && !strings.HasSuffix(shellPath, "zsh") {
73+ logging.Warn("only bash and zsh are supported at this time", "shell", shellPath)
77− shellPath = "/bin/bash"
74+ shellPath = "/bin/bash"
78− }
79 }
75 }
80
76
81 // Default shell args
77 // Default shell args
82 if len(shellArgs) == 0 {
78 if len(shellArgs) == 0 {
83− shellArgs = []string{"-l"}
79+ shellArgs = []string{"--login"}
84 }
80 }
85
81
86 cmd := exec.Command(shellPath, shellArgs...)
82 cmd := exec.Command(shellPath, shellArgs...)
@@ -100,7 +96,7 @@ func newPersistentShell(cwd string) *PersistentShell {
100
96
101 shell := &PersistentShell{
97 shell := &PersistentShell{
102 cmd: cmd,
98 cmd: cmd,
103− stdin: stdinPipe.(*os.File),
99+ stdin: stdinPipe,
104 isAlive: true,
100 isAlive: true,
105 cwd: cwd,
101 cwd: cwd,
106 commandQueue: make(chan *commandExecution, 10),
102 commandQueue: make(chan *commandExecution, 10),
@@ -131,12 +127,15 @@ func newPersistentShell(cwd string) *PersistentShell {
131
127
132func (s *PersistentShell) processCommands() {
128func (s *PersistentShell) processCommands() {
133 for cmd := range s.commandQueue {
129 for cmd := range s.commandQueue {
134− result := s.execCommand(cmd.command, cmd.timeout, cmd.ctx)
130+ cmd.resultChan <- s.execCommand(cmd.ctx, cmd.command, cmd.timeout)
135− cmd.resultChan <- result
136 }
131 }
137}
132}
138
133
134+const runBashCommandFormat = `%s </dev/null >%q 2>%q
135+echo $? >%q
136+pwd >%q`
137+
139−func (s *PersistentShell) execCommand(command string, timeout time.Duration, ctx context.Context) commandResult {
138+func (s *PersistentShell) execCommand(ctx context.Context, command string, timeout time.Duration) commandResult {
140 s.mu.Lock()
139 s.mu.Lock()
141 defer s.mu.Unlock()
140 defer s.mu.Unlock()
142
141
@@ -148,34 +147,22 @@ func (s *PersistentShell) execCommand(command string, timeout time.Duration, ctx
148 }
147 }
149 }
148 }
150
149
151− tempDir := os.TempDir()
150+ tmp := os.TempDir()
151+ now := time.Now().UnixNano()
152− stdoutFile := filepath.Join(tempDir, fmt.Sprintf("crush-stdout-%d", time.Now().UnixNano()))
152+ stdoutFile := filepath.Join(tmp, fmt.Sprintf("crush-stdout-%d", now))
153− stderrFile := filepath.Join(tempDir, fmt.Sprintf("crush-stderr-%d", time.Now().UnixNano()))
153+ stderrFile := filepath.Join(tmp, fmt.Sprintf("crush-stderr-%d", now))
154− statusFile := filepath.Join(tempDir, fmt.Sprintf("crush-status-%d", time.Now().UnixNano()))
154+ statusFile := filepath.Join(tmp, fmt.Sprintf("crush-status-%d", now))
155− cwdFile := filepath.Join(tempDir, fmt.Sprintf("crush-cwd-%d", time.Now().UnixNano()))
155+ cwdFile := filepath.Join(tmp, fmt.Sprintf("crush-cwd-%d", now))
156
156
157 defer func() {
157 defer func() {
158− os.Remove(stdoutFile)
158+ _ = os.Remove(stdoutFile)
159− os.Remove(stderrFile)
159+ _ = os.Remove(stderrFile)
160− os.Remove(statusFile)
160+ _ = os.Remove(statusFile)
161− os.Remove(cwdFile)
161+ _ = os.Remove(cwdFile)
162 }()
162 }()
163
163
164− fullCommand := fmt.Sprintf(`
164+ script := fmt.Sprintf(runBashCommandFormat, command, stdoutFile, stderrFile, statusFile, cwdFile)
165−eval %s < /dev/null > %s 2> %s
166−EXEC_EXIT_CODE=$?
167−pwd > %s
168−echo $EXEC_EXIT_CODE > %s
169−`,
170− shellQuote(command),
171− shellQuote(stdoutFile),
172− shellQuote(stderrFile),
173− shellQuote(cwdFile),
174− shellQuote(statusFile),
175− )
176−
177− _, err := s.stdin.Write([]byte(fullCommand + "\n"))
165+ if _, err := s.stdin.Write([]byte(script + "\n")); err != nil {
178− if err != nil {
179 return commandResult{
166 return commandResult{
180 stderr: fmt.Sprintf("Failed to write command to shell: %v", err),
167 stderr: fmt.Sprintf("Failed to write command to shell: %v", err),
181 exitCode: 1,
168 exitCode: 1,
@@ -184,18 +171,18 @@ echo $EXEC_EXIT_CODE > %s
184 }
171 }
185
172
186 interrupted := false
173 interrupted := false
187−
188− startTime := time.Now()
189−
190 done := make(chan bool)
174 done := make(chan bool)
191 go func() {
175 go func() {
192 // Use exponential backoff polling
176 // Use exponential backoff polling
193− pollInterval := 1 * time.Millisecond
177+ pollInterval := 10 * time.Millisecond
194− maxPollInterval := 100 * time.Millisecond
178+ maxPollInterval := time.Second
195
179
196 ticker := time.NewTicker(pollInterval)
180 ticker := time.NewTicker(pollInterval)
197 defer ticker.Stop()
181 defer ticker.Stop()
198
182
183+ timeoutTicker := time.NewTicker(cmp.Or(timeout, time.Hour*99999))
184+ defer timeoutTicker.Stop()
185+
199 for {
186 for {
200 select {
187 select {
201 case <-ctx.Done():
188 case <-ctx.Done():
@@ -204,28 +191,21 @@ echo $EXEC_EXIT_CODE > %s
204 done <- true
191 done <- true
205 return
192 return
206
193
194+ case <-timeoutTicker.C:
195+ s.killChildren()
196+ interrupted = true
197+ done <- true
198+ return
199+
207 case <-ticker.C:
200 case <-ticker.C:
208− if fileExists(statusFile) && fileSize(statusFile) > 0 {
201+ if fileSize(statusFile) > 0 {
209 done <- true
202 done <- true
210 return
203 return
211 }
204 }
212
205
213− if timeout > 0 {
214− elapsed := time.Since(startTime)
215− if elapsed > timeout {
216− s.killChildren()
217− interrupted = true
218− done <- true
219− return
220− }
221− }
222−
223 // Exponential backoff to reduce CPU usage for longer-running commands
206 // Exponential backoff to reduce CPU usage for longer-running commands
224 if pollInterval < maxPollInterval {
207 if pollInterval < maxPollInterval {
225− pollInterval = time.Duration(float64(pollInterval) * 1.5)
208+ pollInterval = min(time.Duration(float64(pollInterval)*1.5), maxPollInterval)
226− if pollInterval > maxPollInterval {
227− pollInterval = maxPollInterval
228− }
229 ticker.Reset(pollInterval)
209 ticker.Reset(pollInterval)
230 }
210 }
231 }
211 }
@@ -263,23 +243,21 @@ func (s *PersistentShell) killChildren() {
263 if s.cmd == nil || s.cmd.Process == nil {
243 if s.cmd == nil || s.cmd.Process == nil {
264 return
244 return
265 }
245 }
246+ p, err := process.NewProcess(int32(s.cmd.Process.Pid))
247+ if err != nil {
248+ logging.WarnPersist("could not kill persistent shell child processes", "err", err)
249+ return
250+ }
266
251
267− pgrepCmd := exec.Command("pgrep", "-P", fmt.Sprintf("%d", s.cmd.Process.Pid))
252+ children, err := p.Children()
268− output, err := pgrepCmd.Output()
269 if err != nil {
253 if err != nil {
254+ logging.WarnPersist("could not kill persistent shell child processes", "err", err)
270 return
255 return
271 }
256 }
272
257
273− for pidStr := range strings.SplitSeq(string(output), "\n") {
258+ for _, child := range children {
274− if pidStr = strings.TrimSpace(pidStr); pidStr != "" {
259+ if err := child.SendSignal(syscall.SIGTERM); err != nil {
275− var pid int
260+ logging.WarnPersist("could not kill persistent shell child processes", "err", err, "pid", child.Pid)
276− fmt.Sscanf(pidStr, "%d", &pid)
277− if pid > 0 {
278− proc, err := os.FindProcess(pid)
279− if err == nil {
280− proc.Signal(syscall.SIGTERM)
281− }
282− }
283 }
261 }
284 }
262 }
285}
263}
@@ -289,12 +267,10 @@ func (s *PersistentShell) Exec(ctx context.Context, command string, timeoutMs in
289 return "", "Shell is not alive", 1, false, errors.New("shell is not alive")
267 return "", "Shell is not alive", 1, false, errors.New("shell is not alive")
290 }
268 }
291
269
292− timeout := time.Duration(timeoutMs) * time.Millisecond
293−
294 resultChan := make(chan commandResult)
270 resultChan := make(chan commandResult)
295 s.commandQueue <- &commandExecution{
271 s.commandQueue <- &commandExecution{
296 command: command,
272 command: command,
297− timeout: timeout,
273+ timeout: time.Duration(timeoutMs) * time.Millisecond,
298 resultChan: resultChan,
274 resultChan: resultChan,
299 ctx: ctx,
275 ctx: ctx,
300 }
276 }
@@ -313,14 +289,12 @@ func (s *PersistentShell) Close() {
313
289
314 s.stdin.Write([]byte("exit\n"))
290 s.stdin.Write([]byte("exit\n"))
315
291
316− s.cmd.Process.Kill()
292+ if err := s.cmd.Process.Kill(); err != nil {
293+ logging.WarnPersist("could not kill persistent shell", "err", err)
294+ }
317 s.isAlive = false
295 s.isAlive = false
318}
296}
319
297
320−func shellQuote(s string) string {
321− return "'" + strings.ReplaceAll(s, "'", "'\\''") + "'"
322−}
323−
324func readFileOrEmpty(path string) string {
298func readFileOrEmpty(path string) string {
325 content, err := os.ReadFile(path)
299 content, err := os.ReadFile(path)
326 if err != nil {
300 if err != nil {
@@ -329,11 +303,6 @@ func readFileOrEmpty(path string) string {
329 return string(content)
303 return string(content)
330}
304}
331
305
332−func fileExists(path string) bool {
333− _, err := os.Stat(path)
334− return err == nil
335−}
336−
337func fileSize(path string) int64 {
306func fileSize(path string) int64 {
338 info, err := os.Stat(path)
307 info, err := os.Stat(path)
339 if err != nil {
308 if err != nil {