claude-yes 1.11.1 → 1.12.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/cli.test.ts CHANGED
@@ -1,71 +1,79 @@
1
- import { execaCommand } from 'execa';
1
+ import { execaCommand } from "execa";
2
2
  import { fromStdio } from "from-node-stream";
3
3
  import { exec } from "node:child_process";
4
- import { existsSync } from 'node:fs';
5
- import { readFile, unlink } from 'node:fs/promises';
4
+ import { existsSync } from "node:fs";
5
+ import { readFile, unlink } from "node:fs/promises";
6
6
  import sflow from "sflow";
7
7
  import { beforeAll, describe, expect, it } from "vitest";
8
8
  import { createIdleWatcher } from "./createIdleWatcher";
9
- import { sleepms } from './utils';
9
+ import { sleepms } from "./utils";
10
10
 
11
11
  beforeAll(async () => {
12
- await execaCommand(`bun run build`)
13
- .then(() => console.log('Build successful')
14
- )
15
- })
12
+ await execaCommand(`bun run build`).then(() =>
13
+ console.log("Build successful"),
14
+ );
15
+ });
16
16
 
17
- describe('CLI Tests', () => {
18
- it('Write file with auto bypass permission prompt', async () => {
19
- const flagFile = './.cache/flag.json';
20
- // clean
21
- await unlink(flagFile).catch(() => { });
17
+ describe("CLI Tests", () => {
18
+ it("Write file with auto bypass permission prompt", async () => {
19
+ const flagFile = "./.cache/flag.json";
20
+ // clean
21
+ await unlink(flagFile).catch(() => {});
22
22
 
23
- const p = exec(`node dist/cli.js --exit-on-idle=3s "just write {on: 1} into ./.cache/flag.json"`);
24
- const tr = new TransformStream<string, string>()
25
- const w = tr.writable.getWriter();
23
+ const p = exec(
24
+ `node dist/cli.js --exit-on-idle=3s "just write {on: 1} into ./.cache/flag.json"`,
25
+ );
26
+ const tr = new TransformStream<string, string>();
27
+ const w = tr.writable.getWriter();
26
28
 
27
- const exit = async () => await sflow(['\r', '/exit', '\r', '\r']).forEach(async (e) => {
28
- await sleepms(200)
29
- await w.write(e)
30
- }).run();
29
+ const exit = async () =>
30
+ await sflow(["\r", "/exit", "\r", "\r"])
31
+ .forEach(async (e) => {
32
+ await sleepms(200);
33
+ await w.write(e);
34
+ })
35
+ .run();
31
36
 
32
- // ping function to exit claude when idle
37
+ // ping function to exit claude when idle
33
38
 
34
- const { ping } = createIdleWatcher(() => exit(), 3000);
39
+ const { ping } = createIdleWatcher(() => exit(), 3000);
35
40
 
36
- const output = (await sflow(tr.readable).by(fromStdio(p)).log()
37
- .forEach(() => ping())
38
- .text())
41
+ const output = await sflow(tr.readable)
42
+ .by(fromStdio(p))
43
+ .log()
44
+ .forEach(() => ping())
45
+ .text();
39
46
 
40
- // expect the file exists
41
- expect(existsSync(flagFile)).toBe(true);
42
- // expect the output contains the file path
43
- expect(output).toContain(flagFile);
44
-
45
- // expect the file content to be 'on'
46
- expect(await new Response(await readFile(flagFile)).json()).toEqual({ on: 1 });
47
+ // expect the file exists
48
+ expect(existsSync(flagFile)).toBe(true);
49
+ // expect the output contains the file path
50
+ expect(output).toContain(flagFile);
47
51
 
48
- expect(p.exitCode).toBe(0); // expect the process to exit successfully
52
+ // expect the file content to be 'on'
53
+ expect(await new Response(await readFile(flagFile)).json()).toEqual({
54
+ on: 1,
55
+ });
49
56
 
50
- // 30 seconds timeout for this test, it usually takes 13s to run (10s for claude to solve this problem, 3s for idle watcher to exit)
51
- }, 30e3);
57
+ expect(p.exitCode).toBe(0); // expect the process to exit successfully
52
58
 
53
- it.skip('CLI --exit-on-idle flag with default timeout', async () => {
54
- const p = exec(`node dist/cli.js "echo hello" --exit-on-idle`);
55
- const tr = new TransformStream<string, string>()
56
- const output = (await sflow(tr.readable).by(fromStdio(p)).log().text())
57
- expect(output).toContain('hello');
58
- await sleepms(1000); // wait for process exit
59
- expect(p.exitCode).toBe(0);
60
- }, 30e3);
61
-
62
- it('CLI --exit-on-idle flag with custom timeout', async () => {
63
- const p = exec(`node dist/cli.js --exit-on-idle=1s "echo hello"`);
64
- const tr = new TransformStream<string, string>()
65
- const output = (await sflow(tr.readable).by(fromStdio(p)).log().text())
66
- expect(output).toContain('hello');
67
- await sleepms(1000); // wait for process exit
68
- expect(p.exitCode).toBe(0);
69
- }, 30e3);
70
- })
59
+ // 30 seconds timeout for this test, it usually takes 13s to run (10s for claude to solve this problem, 3s for idle watcher to exit)
60
+ }, 30e3);
71
61
 
62
+ it.skip("CLI --exit-on-idle flag with default timeout", async () => {
63
+ const p = exec(`node dist/cli.js "echo hello" --exit-on-idle`);
64
+ const tr = new TransformStream<string, string>();
65
+ const output = await sflow(tr.readable).by(fromStdio(p)).log().text();
66
+ expect(output).toContain("hello");
67
+ await sleepms(1000); // wait for process exit
68
+ expect(p.exitCode).toBe(0);
69
+ }, 30e3);
70
+
71
+ it("CLI --exit-on-idle flag with custom timeout", async () => {
72
+ const p = exec(`node dist/cli.js --exit-on-idle=1s "echo hello"`);
73
+ const tr = new TransformStream<string, string>();
74
+ const output = await sflow(tr.readable).by(fromStdio(p)).log().text();
75
+ expect(output).toContain("hello");
76
+ await sleepms(1000); // wait for process exit
77
+ expect(p.exitCode).toBe(0);
78
+ }, 30e3);
79
+ });
@@ -13,7 +13,7 @@ it('createIdleWatcher should trigger onIdle after timeout', async () => {
13
13
  expect(idleTriggered).toBe(true);
14
14
  }, 1000);
15
15
 
16
- it('createIdleWatcher should reset timeout on ping', async () => {
16
+ it.concurrent('createIdleWatcher should reset timeout on ping', async () => {
17
17
  let idleTriggered = false;
18
18
  const watcher = createIdleWatcher(() => {
19
19
  idleTriggered = true;
@@ -28,7 +28,7 @@ it('createIdleWatcher should reset timeout on ping', async () => {
28
28
  expect(idleTriggered).toBe(true);
29
29
  }, 1000);
30
30
 
31
- it('createIdleWatcher should update lastActiveTime on ping', async () => {
31
+ it.concurrent('createIdleWatcher should update lastActiveTime on ping', async () => {
32
32
  const watcher = createIdleWatcher(() => { }, 1000);
33
33
 
34
34
  const initialTime = watcher.getLastActiveTime();
package/dist/cli.js CHANGED
@@ -5046,173 +5046,6 @@ function fromWritable(i) {
5046
5046
  });
5047
5047
  }
5048
5048
 
5049
- // node_modules/bun-pty/dist/index.js
5050
- import { dlopen, FFIType, ptr } from "bun:ffi";
5051
- import { Buffer } from "buffer";
5052
- import { join as join2 } from "path";
5053
- import { existsSync } from "fs";
5054
-
5055
- class EventEmitter {
5056
- listeners = [];
5057
- event = (listener) => {
5058
- this.listeners.push(listener);
5059
- return {
5060
- dispose: () => {
5061
- const i = this.listeners.indexOf(listener);
5062
- if (i !== -1) {
5063
- this.listeners.splice(i, 1);
5064
- }
5065
- }
5066
- };
5067
- };
5068
- fire(data) {
5069
- for (const listener of this.listeners) {
5070
- listener(data);
5071
- }
5072
- }
5073
- }
5074
- var DEFAULT_COLS = 80;
5075
- var DEFAULT_ROWS = 24;
5076
- var DEFAULT_FILE = "sh";
5077
- var DEFAULT_NAME = "xterm";
5078
- function resolveLibPath() {
5079
- const env = process.env.BUN_PTY_LIB;
5080
- if (env && existsSync(env))
5081
- return env;
5082
- const platform = process.platform;
5083
- const arch = process.arch;
5084
- const filename = platform === "darwin" ? arch === "arm64" ? "librust_pty_arm64.dylib" : "librust_pty.dylib" : platform === "win32" ? "rust_pty.dll" : arch === "arm64" ? "librust_pty_arm64.so" : "librust_pty.so";
5085
- const base = Bun.fileURLToPath(import.meta.url);
5086
- const here = base.replace(/\/dist\/.*$/, "");
5087
- const fallbackPaths = [
5088
- join2(here, "rust-pty", "target", "release", filename),
5089
- join2(here, "..", "bun-pty", "rust-pty", "target", "release", filename),
5090
- join2(process.cwd(), "node_modules", "bun-pty", "rust-pty", "target", "release", filename)
5091
- ];
5092
- for (const path2 of fallbackPaths) {
5093
- if (existsSync(path2))
5094
- return path2;
5095
- }
5096
- throw new Error(`librust_pty shared library not found.
5097
- Checked:
5098
- - BUN_PTY_LIB=${env ?? "<unset>"}
5099
- - ${fallbackPaths.join(`
5100
- - `)}
5101
-
5102
- Set BUN_PTY_LIB or ensure one of these paths contains the file.`);
5103
- }
5104
- var libPath = resolveLibPath();
5105
- var lib;
5106
- try {
5107
- lib = dlopen(libPath, {
5108
- bun_pty_spawn: {
5109
- args: [FFIType.cstring, FFIType.cstring, FFIType.i32, FFIType.i32],
5110
- returns: FFIType.i32
5111
- },
5112
- bun_pty_write: {
5113
- args: [FFIType.i32, FFIType.pointer, FFIType.i32],
5114
- returns: FFIType.i32
5115
- },
5116
- bun_pty_read: {
5117
- args: [FFIType.i32, FFIType.pointer, FFIType.i32],
5118
- returns: FFIType.i32
5119
- },
5120
- bun_pty_resize: {
5121
- args: [FFIType.i32, FFIType.i32, FFIType.i32],
5122
- returns: FFIType.i32
5123
- },
5124
- bun_pty_kill: { args: [FFIType.i32], returns: FFIType.i32 },
5125
- bun_pty_get_pid: { args: [FFIType.i32], returns: FFIType.i32 },
5126
- bun_pty_close: { args: [FFIType.i32], returns: FFIType.void }
5127
- });
5128
- } catch (error) {
5129
- console.error("Failed to load lib", error);
5130
- }
5131
-
5132
- class Terminal {
5133
- handle = -1;
5134
- _pid = -1;
5135
- _cols = DEFAULT_COLS;
5136
- _rows = DEFAULT_ROWS;
5137
- _name = DEFAULT_NAME;
5138
- _readLoop = false;
5139
- _closing = false;
5140
- _onData = new EventEmitter;
5141
- _onExit = new EventEmitter;
5142
- constructor(file = DEFAULT_FILE, args = [], opts = { name: DEFAULT_NAME }) {
5143
- this._cols = opts.cols ?? DEFAULT_COLS;
5144
- this._rows = opts.rows ?? DEFAULT_ROWS;
5145
- const cwd = opts.cwd ?? process.cwd();
5146
- const cmdline = [file, ...args].join(" ");
5147
- this.handle = lib.symbols.bun_pty_spawn(Buffer.from(`${cmdline}\x00`, "utf8"), Buffer.from(`${cwd}\x00`, "utf8"), this._cols, this._rows);
5148
- if (this.handle < 0)
5149
- throw new Error("PTY spawn failed");
5150
- this._pid = lib.symbols.bun_pty_get_pid(this.handle);
5151
- this._startReadLoop();
5152
- }
5153
- get pid() {
5154
- return this._pid;
5155
- }
5156
- get cols() {
5157
- return this._cols;
5158
- }
5159
- get rows() {
5160
- return this._rows;
5161
- }
5162
- get process() {
5163
- return "shell";
5164
- }
5165
- get onData() {
5166
- return this._onData.event;
5167
- }
5168
- get onExit() {
5169
- return this._onExit.event;
5170
- }
5171
- write(data) {
5172
- if (this._closing)
5173
- return;
5174
- const buf = Buffer.from(data, "utf8");
5175
- lib.symbols.bun_pty_write(this.handle, ptr(buf), buf.length);
5176
- }
5177
- resize(cols, rows) {
5178
- if (this._closing)
5179
- return;
5180
- this._cols = cols;
5181
- this._rows = rows;
5182
- lib.symbols.bun_pty_resize(this.handle, cols, rows);
5183
- }
5184
- kill(signal = "SIGTERM") {
5185
- if (this._closing)
5186
- return;
5187
- this._closing = true;
5188
- lib.symbols.bun_pty_kill(this.handle);
5189
- lib.symbols.bun_pty_close(this.handle);
5190
- this._onExit.fire({ exitCode: 0, signal });
5191
- }
5192
- async _startReadLoop() {
5193
- if (this._readLoop)
5194
- return;
5195
- this._readLoop = true;
5196
- const buf = Buffer.allocUnsafe(4096);
5197
- while (this._readLoop && !this._closing) {
5198
- const n = lib.symbols.bun_pty_read(this.handle, ptr(buf), buf.length);
5199
- if (n > 0) {
5200
- this._onData.fire(buf.subarray(0, n).toString("utf8"));
5201
- } else if (n === -2) {
5202
- this._onExit.fire({ exitCode: 0 });
5203
- break;
5204
- } else if (n < 0) {
5205
- break;
5206
- } else {
5207
- await new Promise((r) => setTimeout(r, 8));
5208
- }
5209
- }
5210
- }
5211
- }
5212
- function spawn(file, args, options) {
5213
- return new Terminal(file, args, options);
5214
- }
5215
-
5216
5049
  // createIdleWatcher.ts
5217
5050
  function createIdleWatcher(onIdle, idleTimeout) {
5218
5051
  let lastActiveTime = new Date;
@@ -5245,11 +5078,18 @@ function sleepms(ms) {
5245
5078
  // index.ts
5246
5079
  if (__require.main == __require.module)
5247
5080
  await main();
5248
- async function main() {}
5081
+ async function main() {
5082
+ await claudeYes({
5083
+ continueOnCrash: true,
5084
+ exitOnIdle: 1e4,
5085
+ claudeArgs: ["say hello and exit"]
5086
+ });
5087
+ }
5249
5088
  async function claudeYes({
5250
5089
  continueOnCrash,
5251
5090
  exitOnIdle,
5252
- claudeArgs = []
5091
+ claudeArgs = [],
5092
+ cwd = process.cwd()
5253
5093
  } = {}) {
5254
5094
  const defaultTimeout = 5000;
5255
5095
  const idleTimeout = typeof exitOnIdle === "number" ? exitOnIdle : defaultTimeout;
@@ -5261,11 +5101,12 @@ async function claudeYes({
5261
5101
  let errorNoConversation = false;
5262
5102
  const shellOutputStream = new TransformStream;
5263
5103
  const outputWriter = shellOutputStream.writable.getWriter();
5264
- let shell = spawn("claude", claudeArgs, {
5104
+ const pty = globalThis.Bun ? await import("bun-pty") : await import("node-pty");
5105
+ let shell = pty.spawn("claude", claudeArgs, {
5265
5106
  name: "xterm-color",
5266
5107
  cols: process.stdout.columns - PREFIXLENGTH,
5267
5108
  rows: process.stdout.rows,
5268
- cwd: process.cwd(),
5109
+ cwd,
5269
5110
  env: process.env
5270
5111
  });
5271
5112
  async function onData(data) {
@@ -5279,11 +5120,11 @@ async function claudeYes({
5279
5120
  process.exit(exitCode);
5280
5121
  }
5281
5122
  console.log("Claude crashed, restarting...");
5282
- shell = spawn("claude", ["continue", "--continue"], {
5123
+ shell = pty.spawn("claude", ["continue", "--continue"], {
5283
5124
  name: "xterm-color",
5284
5125
  cols: process.stdout.columns - PREFIXLENGTH,
5285
5126
  rows: process.stdout.rows,
5286
- cwd: process.cwd(),
5127
+ cwd,
5287
5128
  env: process.env
5288
5129
  });
5289
5130
  shell.onData(onData);
package/dist/index.js CHANGED
@@ -4826,173 +4826,6 @@ function fromWritable(i) {
4826
4826
  });
4827
4827
  }
4828
4828
 
4829
- // node_modules/bun-pty/dist/index.js
4830
- import { dlopen, FFIType, ptr } from "bun:ffi";
4831
- import { Buffer } from "buffer";
4832
- import { join as join2 } from "path";
4833
- import { existsSync } from "fs";
4834
-
4835
- class EventEmitter {
4836
- listeners = [];
4837
- event = (listener) => {
4838
- this.listeners.push(listener);
4839
- return {
4840
- dispose: () => {
4841
- const i = this.listeners.indexOf(listener);
4842
- if (i !== -1) {
4843
- this.listeners.splice(i, 1);
4844
- }
4845
- }
4846
- };
4847
- };
4848
- fire(data) {
4849
- for (const listener of this.listeners) {
4850
- listener(data);
4851
- }
4852
- }
4853
- }
4854
- var DEFAULT_COLS = 80;
4855
- var DEFAULT_ROWS = 24;
4856
- var DEFAULT_FILE = "sh";
4857
- var DEFAULT_NAME = "xterm";
4858
- function resolveLibPath() {
4859
- const env = process.env.BUN_PTY_LIB;
4860
- if (env && existsSync(env))
4861
- return env;
4862
- const platform = process.platform;
4863
- const arch = process.arch;
4864
- const filename = platform === "darwin" ? arch === "arm64" ? "librust_pty_arm64.dylib" : "librust_pty.dylib" : platform === "win32" ? "rust_pty.dll" : arch === "arm64" ? "librust_pty_arm64.so" : "librust_pty.so";
4865
- const base = Bun.fileURLToPath(import.meta.url);
4866
- const here = base.replace(/\/dist\/.*$/, "");
4867
- const fallbackPaths = [
4868
- join2(here, "rust-pty", "target", "release", filename),
4869
- join2(here, "..", "bun-pty", "rust-pty", "target", "release", filename),
4870
- join2(process.cwd(), "node_modules", "bun-pty", "rust-pty", "target", "release", filename)
4871
- ];
4872
- for (const path2 of fallbackPaths) {
4873
- if (existsSync(path2))
4874
- return path2;
4875
- }
4876
- throw new Error(`librust_pty shared library not found.
4877
- Checked:
4878
- - BUN_PTY_LIB=${env ?? "<unset>"}
4879
- - ${fallbackPaths.join(`
4880
- - `)}
4881
-
4882
- Set BUN_PTY_LIB or ensure one of these paths contains the file.`);
4883
- }
4884
- var libPath = resolveLibPath();
4885
- var lib;
4886
- try {
4887
- lib = dlopen(libPath, {
4888
- bun_pty_spawn: {
4889
- args: [FFIType.cstring, FFIType.cstring, FFIType.i32, FFIType.i32],
4890
- returns: FFIType.i32
4891
- },
4892
- bun_pty_write: {
4893
- args: [FFIType.i32, FFIType.pointer, FFIType.i32],
4894
- returns: FFIType.i32
4895
- },
4896
- bun_pty_read: {
4897
- args: [FFIType.i32, FFIType.pointer, FFIType.i32],
4898
- returns: FFIType.i32
4899
- },
4900
- bun_pty_resize: {
4901
- args: [FFIType.i32, FFIType.i32, FFIType.i32],
4902
- returns: FFIType.i32
4903
- },
4904
- bun_pty_kill: { args: [FFIType.i32], returns: FFIType.i32 },
4905
- bun_pty_get_pid: { args: [FFIType.i32], returns: FFIType.i32 },
4906
- bun_pty_close: { args: [FFIType.i32], returns: FFIType.void }
4907
- });
4908
- } catch (error) {
4909
- console.error("Failed to load lib", error);
4910
- }
4911
-
4912
- class Terminal {
4913
- handle = -1;
4914
- _pid = -1;
4915
- _cols = DEFAULT_COLS;
4916
- _rows = DEFAULT_ROWS;
4917
- _name = DEFAULT_NAME;
4918
- _readLoop = false;
4919
- _closing = false;
4920
- _onData = new EventEmitter;
4921
- _onExit = new EventEmitter;
4922
- constructor(file = DEFAULT_FILE, args = [], opts = { name: DEFAULT_NAME }) {
4923
- this._cols = opts.cols ?? DEFAULT_COLS;
4924
- this._rows = opts.rows ?? DEFAULT_ROWS;
4925
- const cwd = opts.cwd ?? process.cwd();
4926
- const cmdline = [file, ...args].join(" ");
4927
- this.handle = lib.symbols.bun_pty_spawn(Buffer.from(`${cmdline}\x00`, "utf8"), Buffer.from(`${cwd}\x00`, "utf8"), this._cols, this._rows);
4928
- if (this.handle < 0)
4929
- throw new Error("PTY spawn failed");
4930
- this._pid = lib.symbols.bun_pty_get_pid(this.handle);
4931
- this._startReadLoop();
4932
- }
4933
- get pid() {
4934
- return this._pid;
4935
- }
4936
- get cols() {
4937
- return this._cols;
4938
- }
4939
- get rows() {
4940
- return this._rows;
4941
- }
4942
- get process() {
4943
- return "shell";
4944
- }
4945
- get onData() {
4946
- return this._onData.event;
4947
- }
4948
- get onExit() {
4949
- return this._onExit.event;
4950
- }
4951
- write(data) {
4952
- if (this._closing)
4953
- return;
4954
- const buf = Buffer.from(data, "utf8");
4955
- lib.symbols.bun_pty_write(this.handle, ptr(buf), buf.length);
4956
- }
4957
- resize(cols, rows) {
4958
- if (this._closing)
4959
- return;
4960
- this._cols = cols;
4961
- this._rows = rows;
4962
- lib.symbols.bun_pty_resize(this.handle, cols, rows);
4963
- }
4964
- kill(signal = "SIGTERM") {
4965
- if (this._closing)
4966
- return;
4967
- this._closing = true;
4968
- lib.symbols.bun_pty_kill(this.handle);
4969
- lib.symbols.bun_pty_close(this.handle);
4970
- this._onExit.fire({ exitCode: 0, signal });
4971
- }
4972
- async _startReadLoop() {
4973
- if (this._readLoop)
4974
- return;
4975
- this._readLoop = true;
4976
- const buf = Buffer.allocUnsafe(4096);
4977
- while (this._readLoop && !this._closing) {
4978
- const n = lib.symbols.bun_pty_read(this.handle, ptr(buf), buf.length);
4979
- if (n > 0) {
4980
- this._onData.fire(buf.subarray(0, n).toString("utf8"));
4981
- } else if (n === -2) {
4982
- this._onExit.fire({ exitCode: 0 });
4983
- break;
4984
- } else if (n < 0) {
4985
- break;
4986
- } else {
4987
- await new Promise((r) => setTimeout(r, 8));
4988
- }
4989
- }
4990
- }
4991
- }
4992
- function spawn(file, args, options) {
4993
- return new Terminal(file, args, options);
4994
- }
4995
-
4996
4829
  // createIdleWatcher.ts
4997
4830
  function createIdleWatcher(onIdle, idleTimeout) {
4998
4831
  let lastActiveTime = new Date;
@@ -5025,11 +4858,18 @@ function sleepms(ms) {
5025
4858
  // index.ts
5026
4859
  if (__require.main == __require.module)
5027
4860
  await main();
5028
- async function main() {}
4861
+ async function main() {
4862
+ await claudeYes({
4863
+ continueOnCrash: true,
4864
+ exitOnIdle: 1e4,
4865
+ claudeArgs: ["say hello and exit"]
4866
+ });
4867
+ }
5029
4868
  async function claudeYes({
5030
4869
  continueOnCrash,
5031
4870
  exitOnIdle,
5032
- claudeArgs = []
4871
+ claudeArgs = [],
4872
+ cwd = process.cwd()
5033
4873
  } = {}) {
5034
4874
  const defaultTimeout = 5000;
5035
4875
  const idleTimeout = typeof exitOnIdle === "number" ? exitOnIdle : defaultTimeout;
@@ -5041,11 +4881,12 @@ async function claudeYes({
5041
4881
  let errorNoConversation = false;
5042
4882
  const shellOutputStream = new TransformStream;
5043
4883
  const outputWriter = shellOutputStream.writable.getWriter();
5044
- let shell = spawn("claude", claudeArgs, {
4884
+ const pty = globalThis.Bun ? await import("bun-pty") : await import("node-pty");
4885
+ let shell = pty.spawn("claude", claudeArgs, {
5045
4886
  name: "xterm-color",
5046
4887
  cols: process.stdout.columns - PREFIXLENGTH,
5047
4888
  rows: process.stdout.rows,
5048
- cwd: process.cwd(),
4889
+ cwd,
5049
4890
  env: process.env
5050
4891
  });
5051
4892
  async function onData(data) {
@@ -5059,11 +4900,11 @@ async function claudeYes({
5059
4900
  process.exit(exitCode);
5060
4901
  }
5061
4902
  console.log("Claude crashed, restarting...");
5062
- shell = spawn("claude", ["continue", "--continue"], {
4903
+ shell = pty.spawn("claude", ["continue", "--continue"], {
5063
4904
  name: "xterm-color",
5064
4905
  cols: process.stdout.columns - PREFIXLENGTH,
5065
4906
  rows: process.stdout.rows,
5066
- cwd: process.cwd(),
4907
+ cwd,
5067
4908
  env: process.env
5068
4909
  });
5069
4910
  shell.onData(onData);
package/index.ts CHANGED
@@ -1,19 +1,21 @@
1
1
  import { fromReadable, fromWritable } from "from-node-stream";
2
- import * as pty from "bun-pty";
3
2
  import sflow from "sflow";
4
3
  import { createIdleWatcher } from "./createIdleWatcher";
5
4
  import { removeControlCharacters } from "./removeControlCharacters";
6
5
  import { sleepms } from "./utils";
7
6
 
8
7
  if (import.meta.main) await main();
8
+
9
9
  async function main() {
10
- // this script not support bun yet, so use node js to run.
11
- // Note: Attempted migration to bun-pty but reverted due to GLIBC compatibility issues (requires GLIBC 2.39)
12
- // bun-pty is not compatible with this environment, using node-pty instead
10
+ await claudeYes({
11
+ continueOnCrash: true,
12
+ exitOnIdle: 10000,
13
+ claudeArgs: ["say hello and exit"]
14
+ })
13
15
  }
14
16
 
15
17
  /**
16
- * Main function to run Claude with automatic yes/no responses
18
+ * Main function to run Claude with automatic yes/no respojnses
17
19
  * @param options Configuration options
18
20
  * @param options.continueOnCrash - If true, automatically restart Claude when it crashes:
19
21
  * 1. Shows message 'Claude crashed, restarting..'
@@ -27,10 +29,12 @@ export default async function claudeYes({
27
29
  continueOnCrash,
28
30
  exitOnIdle,
29
31
  claudeArgs = [],
32
+ cwd = process.cwd(),
30
33
  }: {
31
34
  continueOnCrash?: boolean;
32
35
  exitOnIdle?: boolean | number;
33
36
  claudeArgs?: string[];
37
+ cwd?: string;
34
38
  } = {}) {
35
39
  const defaultTimeout = 5e3; // 5 seconds idle timeout
36
40
  const idleTimeout =
@@ -50,12 +54,14 @@ export default async function claudeYes({
50
54
 
51
55
  const shellOutputStream = new TransformStream<string, string>();
52
56
  const outputWriter = shellOutputStream.writable.getWriter();
53
-
57
+ const pty = globalThis.Bun
58
+ ? await import('bun-pty')
59
+ : await import('node-pty');
54
60
  let shell = pty.spawn("claude", claudeArgs, {
55
- name: "xterm-color", // use xterm color mode
61
+ name: "xterm-color",
56
62
  cols: process.stdout.columns - PREFIXLENGTH,
57
63
  rows: process.stdout.rows,
58
- cwd: process.cwd(),
64
+ cwd,
59
65
  env: process.env as Record<string, string>,
60
66
  });
61
67
  // TODO handle error if claude is not installed, show msg:
@@ -77,10 +83,10 @@ export default async function claudeYes({
77
83
  }
78
84
  console.log("Claude crashed, restarting...");
79
85
  shell = pty.spawn("claude", ["continue", "--continue"], {
80
- name: "xterm-color", // use xterm color mode
86
+ name: "xterm-color",
81
87
  cols: process.stdout.columns - PREFIXLENGTH,
82
88
  rows: process.stdout.rows,
83
- cwd: process.cwd(),
89
+ cwd,
84
90
  env: process.env as Record<string, string>,
85
91
  });
86
92
  shell.onData(onData);
@@ -128,7 +134,7 @@ export default async function claudeYes({
128
134
  const shellStdio = {
129
135
  writable: new WritableStream<string>({
130
136
  write: (data) => shell.write(data),
131
- close: () => {},
137
+ close: () => { },
132
138
  }),
133
139
  readable: shellOutputStream.readable,
134
140
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-yes",
3
- "version": "1.11.1",
3
+ "version": "1.12.0-beta.1",
4
4
  "homepage": "https://github.com/snomiao/claude-yes#readme",
5
5
  "license": "MIT",
6
6
  "author": "snomiao <snomiao@gmail.com>",
@@ -27,7 +27,7 @@
27
27
  },
28
28
  "scripts": {
29
29
  "dev": "tsx index.ts",
30
- "build": "bun build index.ts cli.ts --outdir=dist --external=node-pty --target=node",
30
+ "build": "bun build index.ts cli.ts --outdir=dist --external=node-pty --external=bun-pty --target=node",
31
31
  "test": "vitest",
32
32
  "prepare": "husky",
33
33
  "fmt": "prettier -w ."
@@ -35,17 +35,18 @@
35
35
  "devDependencies": {
36
36
  "@types/bun": "^1.2.18",
37
37
  "@types/jest": "^30.0.0",
38
+ "@types/minimist": "^1.2.5",
38
39
  "@types/node": "^24.0.10",
39
40
  "enhanced-ms": "^4.1.0",
40
41
  "execa": "^9.6.0",
41
42
  "from-node-stream": "^0.0.11",
42
43
  "husky": "^9.1.7",
44
+ "lint-staged": "^16.1.4",
43
45
  "minimist": "^1.2.8",
44
46
  "prettier": "^3.6.2",
45
47
  "semantic-release": "^24.2.6",
46
48
  "sflow": "^1.20.2",
47
49
  "strip-ansi-control-characters": "^2.0.0",
48
- "@types/minimist": "^1.2.5",
49
50
  "tsx": "^4.20.3",
50
51
  "vitest": "^3.2.4"
51
52
  },
@@ -60,7 +61,13 @@
60
61
  "module": "index.ts",
61
62
  "types": "./index.ts",
62
63
  "dependencies": {
63
- "bun-pty": "^0.3.2"
64
+ "bun-pty": "^0.3.2",
65
+ "node-pty": "^1.0.0"
66
+ },
67
+ "lint-staged": {
68
+ "*.{ts,js,json,md}": [
69
+ "prettier --write"
70
+ ]
64
71
  },
65
72
  "description": "A wrapper tool that automates interactions with the Claude CLI by automatically handling common prompts and responses.",
66
73
  "main": "index.js",