claude-yes 1.16.1 → 1.17.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/index.ts CHANGED
@@ -7,6 +7,7 @@ import { TerminalTextRender } from 'terminal-render';
7
7
  import { writeFile } from 'fs/promises';
8
8
  import path from 'path';
9
9
  import { mkdir } from 'fs/promises';
10
+ import { ReadyManager } from './ReadyManager';
10
11
 
11
12
  /**
12
13
  * Main function to run Claude with automatic yes/no respojnses
@@ -23,9 +24,9 @@ import { mkdir } from 'fs/promises';
23
24
  export default async function claudeYes({
24
25
  claudeArgs = [],
25
26
  continueOnCrash,
26
- cwd = process.cwd(),
27
- env = process.env as Record<string, string>,
28
- exitOnIdle,
27
+ cwd,
28
+ env,
29
+ exitOnIdle = 60e3,
29
30
  logFile,
30
31
  removeControlCharactersFromStdout = false, // = !process.stdout.isTTY,
31
32
  verbose = false,
@@ -61,6 +62,7 @@ export default async function claudeYes({
61
62
  const prefix = ''; // "YESC|"
62
63
  const PREFIXLENGTH = prefix.length;
63
64
  let errorNoConversation = false; // match 'No conversation found to continue'
65
+ const shellReady = new ReadyManager();
64
66
 
65
67
  const shellOutputStream = new TransformStream<string, string>();
66
68
  const outputWriter = shellOutputStream.writable.getWriter();
@@ -68,66 +70,66 @@ export default async function claudeYes({
68
70
 
69
71
  // recommened to use bun pty in windows
70
72
  const pty = process.versions.bun
71
- ? await import('bun-pty')
72
- : await import('node-pty');
73
- let shell = pty.spawn('claude', claudeArgs, {
73
+ ? await import('bun-pty').catch(() => {
74
+ throw new Error('Please install bun-pty');
75
+ })
76
+ : await import('node-pty').catch(() => {
77
+ throw new Error('Please install node-pty');
78
+ });
79
+
80
+ const getPtyOptions = () => ({
74
81
  name: 'xterm-color',
75
82
  cols: process.stdout.columns - PREFIXLENGTH,
76
83
  rows: process.stdout.rows,
77
- cwd,
78
- env,
84
+ cwd: cwd ?? process.cwd(),
85
+ env: env ?? (process.env as Record<string, string>),
79
86
  });
87
+ let shell = pty.spawn('claude', claudeArgs, getPtyOptions());
80
88
  let pendingExitCode = Promise.withResolvers<number | null>();
89
+ let pendingExitCodeValue = null;
90
+
81
91
  // TODO handle error if claude is not installed, show msg:
82
92
  // npm install -g @anthropic-ai/claude-code
83
93
 
84
94
  async function onData(data: string) {
85
95
  // append data to the buffer, so we can process it later
86
96
  await outputWriter.write(data);
97
+ shellReady.ready(); // shell has output, also means ready for stdin
87
98
  }
99
+
88
100
  shell.onData(onData);
89
- // when claude process exits, exit the main process with the same exit code
90
101
  shell.onExit(function onExit({ exitCode }) {
91
- if (continueOnCrash && exitCode !== 0) {
102
+ shellReady.unready(); // start buffer stdin
103
+ const claudeCrashed = exitCode !== 0;
104
+ if (claudeCrashed && continueOnCrash) {
92
105
  if (errorNoConversation) {
93
106
  console.log(
94
107
  'Claude crashed with "No conversation found to continue", exiting...'
95
108
  );
96
- return pendingExitCode.resolve(exitCode);
109
+ return pendingExitCode.resolve((pendingExitCodeValue = exitCode));
97
110
  }
98
111
  console.log('Claude crashed, restarting...');
99
- shell = pty.spawn('claude', ['--continue', 'continue'], {
100
- name: 'xterm-color',
101
- cols: process.stdout.columns - PREFIXLENGTH,
102
- rows: process.stdout.rows,
103
- cwd,
104
- env,
105
- });
112
+
113
+ shell = pty.spawn('claude', ['--continue', 'continue'], getPtyOptions());
106
114
  shell.onData(onData);
107
115
  shell.onExit(onExit);
108
116
  return;
109
117
  }
110
- return pendingExitCode.resolve(exitCode);
118
+ return pendingExitCode.resolve((pendingExitCodeValue = exitCode));
111
119
  });
112
120
 
113
121
  const exitClaudeCode = async () => {
122
+ continueOnCrash = false;
114
123
  // send exit command to the shell, must sleep a bit to avoid claude treat it as pasted input
115
124
  await sflow(['\r', '/exit', '\r'])
116
- .forEach(async (e) => {
117
- await sleepms(200);
118
- shell.write(e);
119
- })
125
+ .forEach(async () => await sleepms(200))
126
+ .forEach(async (e) => shell.write(e))
120
127
  .run();
121
128
 
122
129
  // wait for shell to exit or kill it with a timeout
123
130
  let exited = false;
124
131
  await Promise.race([
125
- new Promise<void>((resolve) =>
126
- shell.onExit(() => {
127
- resolve();
128
- exited = true;
129
- })
130
- ), // resolve when shell exits
132
+ pendingExitCode.promise.then(() => (exited = true)), // resolve when shell exits
131
133
  // if shell doesn't exit in 5 seconds, kill it
132
134
  new Promise<void>((resolve) =>
133
135
  setTimeout(() => {
@@ -145,20 +147,12 @@ export default async function claudeYes({
145
147
  shell.resize(columns - PREFIXLENGTH, rows);
146
148
  });
147
149
 
148
- const shellStdio = {
149
- writable: new WritableStream<string>({
150
- write: (data) => shell.write(data),
151
- close: () => {},
152
- }),
153
- readable: shellOutputStream.readable,
154
- };
155
-
156
- const ttr = new TerminalTextRender();
150
+ const render = new TerminalTextRender();
157
151
  const idleWatcher = !exitOnIdle
158
152
  ? null
159
153
  : createIdleWatcher(async () => {
160
154
  if (
161
- ttr
155
+ render
162
156
  .render()
163
157
  .replace(/\s+/g, ' ')
164
158
  .match(/esc to interrupt|to run in background/)
@@ -179,9 +173,18 @@ export default async function claudeYes({
179
173
  sflow(fromReadable<Buffer>(process.stdin))
180
174
  .map((buffer) => buffer.toString())
181
175
  // .forEach(e => appendFile('.cache/io.log', "input |" + JSON.stringify(e) + '\n')) // for debugging
182
- .by(shellStdio)
183
- // handle ttr render
184
- .forEach((text) => ttr.write(text))
176
+ // pipe
177
+ .by({
178
+ writable: new WritableStream<string>({
179
+ write: async (data) => {
180
+ await shellReady.wait();
181
+ shell.write(data);
182
+ },
183
+ }),
184
+ readable: shellOutputStream.readable,
185
+ })
186
+ // handle terminal render
187
+ .forEach((text) => render.write(text))
185
188
 
186
189
  // handle idle
187
190
  .forEach(() => idleWatcher?.ping()) // ping the idle watcher on output for last active time to keep track of claude status
@@ -209,7 +212,7 @@ export default async function claudeYes({
209
212
  .to(fromWritable(process.stdout));
210
213
 
211
214
  const exitCode = await pendingExitCode.promise; // wait for the shell to exit
212
- verbose && console.log(`[claude-yes] claude exited with code ${exitCode}`);
215
+ console.log(`[claude-yes] claude exited with code ${exitCode}`);
213
216
 
214
217
  if (logFile) {
215
218
  verbose && console.log(`[claude-yes] Writing rendered logs to ${logFile}`);
@@ -217,10 +220,10 @@ export default async function claudeYes({
217
220
  await mkdir(path.dirname(logFilePath), { recursive: true }).catch(
218
221
  () => null
219
222
  );
220
- await writeFile(logFilePath, ttr.render());
223
+ await writeFile(logFilePath, render.render());
221
224
  }
222
225
 
223
- return { exitCode, logs: ttr.render() };
226
+ return { exitCode, logs: render.render() };
224
227
  }
225
228
 
226
229
  export { removeControlCharacters };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-yes",
3
- "version": "1.16.1",
3
+ "version": "1.17.1",
4
4
  "description": "A wrapper tool that automates interactions with the Claude CLI by automatically handling common prompts and responses.",
5
5
  "keywords": [
6
6
  "claude",
@@ -27,7 +27,7 @@
27
27
  "import": "./dist/index.js",
28
28
  "types": "./index.ts"
29
29
  },
30
- "main": "index.js",
30
+ "main": "dist/index.js",
31
31
  "module": "index.ts",
32
32
  "types": "./index.ts",
33
33
  "bin": {
@@ -52,30 +52,31 @@
52
52
  "prettier --write"
53
53
  ]
54
54
  },
55
- "dependencies": {
56
- "bun-pty": "^0.3.2",
57
- "node-pty": "^1.0.0"
58
- },
59
55
  "devDependencies": {
60
- "terminal-render": "^1.1.0",
61
- "yargs": "^18.0.0",
62
56
  "@types/bun": "^1.2.18",
63
57
  "@types/jest": "^30.0.0",
64
58
  "@types/node": "^24.0.10",
65
59
  "@types/yargs": "^17.0.33",
66
- "sflow": "^1.20.2",
67
60
  "enhanced-ms": "^4.1.0",
68
61
  "execa": "^9.6.0",
69
62
  "from-node-stream": "^0.0.11",
70
63
  "husky": "^9.1.7",
71
64
  "lint-staged": "^16.1.4",
72
65
  "prettier": "^3.6.2",
66
+ "rambda": "^10.3.2",
73
67
  "semantic-release": "^24.2.6",
68
+ "sflow": "^1.20.2",
74
69
  "strip-ansi-control-characters": "^2.0.0",
70
+ "terminal-render": "^1.1.0",
75
71
  "tsx": "^4.20.3",
76
- "vitest": "^3.2.4"
72
+ "vitest": "^3.2.4",
73
+ "yargs": "^18.0.0"
77
74
  },
78
75
  "peerDependencies": {
79
- "typescript": "^5.8.3"
76
+ "typescript": "^5.8.3",
77
+ "node-pty": "^1.0.0"
78
+ },
79
+ "dependencies": {
80
+ "bun-pty": "^0.3.2"
80
81
  }
81
82
  }