claude-yes 1.16.0 → 1.17.0

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();
@@ -70,64 +72,60 @@ export default async function claudeYes({
70
72
  const pty = process.versions.bun
71
73
  ? await import('bun-pty')
72
74
  : await import('node-pty');
73
- let shell = pty.spawn('claude', claudeArgs, {
75
+
76
+ const getPtyOptions = () => ({
74
77
  name: 'xterm-color',
75
78
  cols: process.stdout.columns - PREFIXLENGTH,
76
79
  rows: process.stdout.rows,
77
- cwd,
78
- env,
80
+ cwd: cwd ?? process.cwd(),
81
+ env: env ?? (process.env as Record<string, string>),
79
82
  });
83
+ let shell = pty.spawn('claude', claudeArgs, getPtyOptions());
80
84
  let pendingExitCode = Promise.withResolvers<number | null>();
85
+ let pendingExitCodeValue = null;
86
+
81
87
  // TODO handle error if claude is not installed, show msg:
82
88
  // npm install -g @anthropic-ai/claude-code
83
89
 
84
90
  async function onData(data: string) {
85
91
  // append data to the buffer, so we can process it later
86
92
  await outputWriter.write(data);
93
+ shellReady.ready(); // shell has output, also means ready for stdin
87
94
  }
95
+
88
96
  shell.onData(onData);
89
- // when claude process exits, exit the main process with the same exit code
90
97
  shell.onExit(function onExit({ exitCode }) {
91
- if (continueOnCrash && exitCode !== 0) {
98
+ shellReady.unready(); // start buffer stdin
99
+ const claudeCrashed = exitCode !== 0;
100
+ if (claudeCrashed && continueOnCrash) {
92
101
  if (errorNoConversation) {
93
102
  console.log(
94
103
  'Claude crashed with "No conversation found to continue", exiting...'
95
104
  );
96
- return pendingExitCode.resolve(exitCode);
105
+ return pendingExitCode.resolve((pendingExitCodeValue = exitCode));
97
106
  }
98
107
  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
- });
108
+
109
+ shell = pty.spawn('claude', ['--continue', 'continue'], getPtyOptions());
106
110
  shell.onData(onData);
107
111
  shell.onExit(onExit);
108
112
  return;
109
113
  }
110
- return pendingExitCode.resolve(exitCode);
114
+ return pendingExitCode.resolve((pendingExitCodeValue = exitCode));
111
115
  });
112
116
 
113
117
  const exitClaudeCode = async () => {
118
+ continueOnCrash = false;
114
119
  // send exit command to the shell, must sleep a bit to avoid claude treat it as pasted input
115
120
  await sflow(['\r', '/exit', '\r'])
116
- .forEach(async (e) => {
117
- await sleepms(200);
118
- shell.write(e);
119
- })
121
+ .forEach(async () => await sleepms(200))
122
+ .forEach(async (e) => shell.write(e))
120
123
  .run();
121
124
 
122
125
  // wait for shell to exit or kill it with a timeout
123
126
  let exited = false;
124
127
  await Promise.race([
125
- new Promise<void>((resolve) =>
126
- shell.onExit(() => {
127
- resolve();
128
- exited = true;
129
- })
130
- ), // resolve when shell exits
128
+ pendingExitCode.promise.then(() => (exited = true)), // resolve when shell exits
131
129
  // if shell doesn't exit in 5 seconds, kill it
132
130
  new Promise<void>((resolve) =>
133
131
  setTimeout(() => {
@@ -145,20 +143,12 @@ export default async function claudeYes({
145
143
  shell.resize(columns - PREFIXLENGTH, rows);
146
144
  });
147
145
 
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();
146
+ const render = new TerminalTextRender();
157
147
  const idleWatcher = !exitOnIdle
158
148
  ? null
159
149
  : createIdleWatcher(async () => {
160
150
  if (
161
- ttr
151
+ render
162
152
  .render()
163
153
  .replace(/\s+/g, ' ')
164
154
  .match(/esc to interrupt|to run in background/)
@@ -179,9 +169,18 @@ export default async function claudeYes({
179
169
  sflow(fromReadable<Buffer>(process.stdin))
180
170
  .map((buffer) => buffer.toString())
181
171
  // .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))
172
+ // pipe
173
+ .by({
174
+ writable: new WritableStream<string>({
175
+ write: async (data) => {
176
+ await shellReady.wait();
177
+ shell.write(data);
178
+ },
179
+ }),
180
+ readable: shellOutputStream.readable,
181
+ })
182
+ // handle terminal render
183
+ .forEach((text) => render.write(text))
185
184
 
186
185
  // handle idle
187
186
  .forEach(() => idleWatcher?.ping()) // ping the idle watcher on output for last active time to keep track of claude status
@@ -209,7 +208,7 @@ export default async function claudeYes({
209
208
  .to(fromWritable(process.stdout));
210
209
 
211
210
  const exitCode = await pendingExitCode.promise; // wait for the shell to exit
212
- verbose && console.log(`[claude-yes] claude exited with code ${exitCode}`);
211
+ console.log(`[claude-yes] claude exited with code ${exitCode}`);
213
212
 
214
213
  if (logFile) {
215
214
  verbose && console.log(`[claude-yes] Writing rendered logs to ${logFile}`);
@@ -217,10 +216,10 @@ export default async function claudeYes({
217
216
  await mkdir(path.dirname(logFilePath), { recursive: true }).catch(
218
217
  () => null
219
218
  );
220
- await writeFile(logFilePath, ttr.render());
219
+ await writeFile(logFilePath, render.render());
221
220
  }
222
221
 
223
- return { exitCode, logs: ttr.render() };
222
+ return { exitCode, logs: render.render() };
224
223
  }
225
224
 
226
225
  export { removeControlCharacters };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-yes",
3
- "version": "1.16.0",
3
+ "version": "1.17.0",
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",
@@ -41,7 +41,7 @@
41
41
  "dist"
42
42
  ],
43
43
  "scripts": {
44
- "build": "bun build index.ts cli.ts --outdir=dist --packages=external --target=node --sourcemap",
44
+ "build": "bun build index.ts cli.ts --outdir=dist --external=node-pty --external=bun-pty --target=node --sourcemap",
45
45
  "dev": "tsx index.ts",
46
46
  "fmt": "prettier -w .",
47
47
  "prepare": "husky",
@@ -54,9 +54,7 @@
54
54
  },
55
55
  "dependencies": {
56
56
  "bun-pty": "^0.3.2",
57
- "node-pty": "^1.0.0",
58
- "terminal-render": "^1.1.0",
59
- "yargs": "^18.0.0"
57
+ "node-pty": "^1.0.0"
60
58
  },
61
59
  "devDependencies": {
62
60
  "@types/bun": "^1.2.18",
@@ -69,11 +67,14 @@
69
67
  "husky": "^9.1.7",
70
68
  "lint-staged": "^16.1.4",
71
69
  "prettier": "^3.6.2",
70
+ "rambda": "^10.3.2",
72
71
  "semantic-release": "^24.2.6",
73
72
  "sflow": "^1.20.2",
74
73
  "strip-ansi-control-characters": "^2.0.0",
74
+ "terminal-render": "^1.1.0",
75
75
  "tsx": "^4.20.3",
76
- "vitest": "^3.2.4"
76
+ "vitest": "^3.2.4",
77
+ "yargs": "^18.0.0"
77
78
  },
78
79
  "peerDependencies": {
79
80
  "typescript": "^5.8.3"