claude-yes 1.14.2 → 1.15.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/dist/index.js CHANGED
@@ -4832,14 +4832,13 @@ function createIdleWatcher(onIdle, idleTimeout) {
4832
4832
  let idleTimeoutId = null;
4833
4833
  return {
4834
4834
  ping: () => {
4835
- if (idleTimeoutId) {
4835
+ if (idleTimeoutId)
4836
4836
  clearTimeout(idleTimeoutId);
4837
- }
4837
+ lastActiveTime = new Date;
4838
4838
  idleTimeoutId = setTimeout(() => {
4839
4839
  clearTimeout(idleTimeoutId);
4840
4840
  onIdle();
4841
4841
  }, idleTimeout);
4842
- lastActiveTime = new Date;
4843
4842
  },
4844
4843
  getLastActiveTime: () => lastActiveTime
4845
4844
  };
@@ -5088,15 +5087,29 @@ class TerminalTextRender {
5088
5087
  }
5089
5088
 
5090
5089
  // index.ts
5090
+ import { writeFile } from "fs/promises";
5091
+ import path2 from "path";
5092
+ import { mkdir } from "fs/promises";
5091
5093
  async function claudeYes({
5092
5094
  continueOnCrash,
5093
5095
  exitOnIdle,
5094
5096
  claudeArgs = [],
5095
5097
  cwd = process.cwd(),
5096
- removeControlCharactersFromStdout = false
5098
+ removeControlCharactersFromStdout = false,
5099
+ logFile,
5100
+ verbose = false
5097
5101
  } = {}) {
5098
- const defaultTimeout = 5000;
5099
- const idleTimeout = typeof exitOnIdle === "number" ? exitOnIdle : defaultTimeout;
5102
+ if (verbose) {
5103
+ console.log("calling claudeYes: ", {
5104
+ continueOnCrash,
5105
+ exitOnIdle,
5106
+ claudeArgs,
5107
+ cwd,
5108
+ removeControlCharactersFromStdout,
5109
+ logFile,
5110
+ verbose
5111
+ });
5112
+ }
5100
5113
  console.log("⭐ Starting claude, automatically responding to yes/no prompts...");
5101
5114
  console.log("⚠️ Important Security Warning: Only run this on trusted repositories. This tool automatically responds to prompts and can execute commands without user confirmation. Be aware of potential prompt injection attacks where malicious code or instructions could be embedded in files or user inputs to manipulate the automated responses.");
5102
5115
  process.stdin.setRawMode?.(true);
@@ -5113,15 +5126,16 @@ async function claudeYes({
5113
5126
  cwd,
5114
5127
  env: process.env
5115
5128
  });
5129
+ let pendingExitCode = Promise.withResolvers();
5116
5130
  async function onData(data) {
5117
5131
  await outputWriter.write(data);
5118
5132
  }
5119
5133
  shell.onData(onData);
5120
- shell.onExit(function onExit({ exitCode }) {
5121
- if (continueOnCrash && exitCode !== 0) {
5134
+ shell.onExit(function onExit({ exitCode: exitCode2 }) {
5135
+ if (continueOnCrash && exitCode2 !== 0) {
5122
5136
  if (errorNoConversation) {
5123
5137
  console.log('Claude crashed with "No conversation found to continue", exiting...');
5124
- process.exit(exitCode);
5138
+ return pendingExitCode.resolve(exitCode2);
5125
5139
  }
5126
5140
  console.log("Claude crashed, restarting...");
5127
5141
  shell = pty.spawn("claude", ["continue", "--continue"], {
@@ -5135,7 +5149,7 @@ async function claudeYes({
5135
5149
  shell.onExit(onExit);
5136
5150
  return;
5137
5151
  }
5138
- process.exit(exitCode);
5152
+ return pendingExitCode.resolve(exitCode2);
5139
5153
  });
5140
5154
  const exitClaudeCode = async () => {
5141
5155
  await src_default(["\r", "/exit", "\r"]).forEach(async (e) => {
@@ -5168,21 +5182,19 @@ async function claudeYes({
5168
5182
  readable: shellOutputStream.readable
5169
5183
  };
5170
5184
  const ttr = new TerminalTextRender;
5171
- const idleWatcher = createIdleWatcher(async () => {
5172
- if (exitOnIdle) {
5173
- if (ttr.render().replace(/\s+/g, " ").match(/esc to interrupt|to run in background/)) {
5174
- console.warn("[CLAUDE-YES] Claude is idle, but seems still working, not exiting yet");
5175
- } else {
5176
- console.warn("[CLAUDE-YES] Claude is idle, exiting...");
5177
- await exitClaudeCode();
5178
- }
5185
+ const idleWatcher = !exitOnIdle ? null : createIdleWatcher(async () => {
5186
+ if (ttr.render().replace(/\s+/g, " ").match(/esc to interrupt|to run in background/)) {
5187
+ console.log("[claude-yes] Claude is idle, but seems still working, not exiting yet");
5188
+ } else {
5189
+ console.log("[claude-yes] Claude is idle, exiting...");
5190
+ await exitClaudeCode();
5179
5191
  }
5180
- }, idleTimeout);
5192
+ }, exitOnIdle);
5181
5193
  const confirm = async () => {
5182
5194
  await sleepms(200);
5183
5195
  shell.write("\r");
5184
5196
  };
5185
- await src_default(fromReadable(process.stdin)).forEach(() => idleWatcher.ping()).map((buffer2) => buffer2.toString()).forEach((text) => ttr.write(text)).by(shellStdio).forkTo((e) => e.map((e2) => removeControlCharacters(e2)).map((e2) => e2.replaceAll("\r", "")).forEach(async (e2) => {
5197
+ src_default(fromReadable(process.stdin)).map((buffer2) => buffer2.toString()).by(shellStdio).forEach((text) => ttr.write(text)).forEach(() => idleWatcher?.ping()).forkTo((e) => e.map((e2) => removeControlCharacters(e2)).map((e2) => e2.replaceAll("\r", "")).forEach(async (e2) => {
5186
5198
  if (e2.match(/❯ 1. Yes/))
5187
5199
  return await confirm();
5188
5200
  if (e2.match(/❯ 1. Dark mode✔|Press Enter to continue…/))
@@ -5192,7 +5204,15 @@ async function claudeYes({
5192
5204
  return;
5193
5205
  }
5194
5206
  }).run()).replaceAll(/.*(?:\r\n?|\r?\n)/g, (line) => prefix + line).map((e) => removeControlCharactersFromStdout ? removeControlCharacters(e) : e).to(fromWritable(process.stdout));
5195
- return ttr.render();
5207
+ const exitCode = await pendingExitCode.promise;
5208
+ verbose && console.log(`[claude-yes] claude exited with code ${exitCode}`);
5209
+ if (logFile) {
5210
+ verbose && console.log(`[claude-yes] Writing rendered logs to ${logFile}`);
5211
+ const logFilePath = path2.resolve(logFile);
5212
+ await mkdir(path2.dirname(logFilePath), { recursive: true }).catch(() => null);
5213
+ await writeFile(logFilePath, ttr.render());
5214
+ }
5215
+ return { exitCode, logs: ttr.render() };
5196
5216
  }
5197
5217
  export {
5198
5218
  removeControlCharacters,
package/index.ts CHANGED
@@ -4,15 +4,9 @@ import { createIdleWatcher } from './createIdleWatcher';
4
4
  import { removeControlCharacters } from './removeControlCharacters';
5
5
  import { sleepms } from './utils';
6
6
  import { TerminalTextRender } from 'terminal-render';
7
- // for debug only
8
- // if (import.meta.main) await main();
9
- // async function main() {
10
- // await claudeYes({
11
- // continueOnCrash: true,
12
- // exitOnIdle: 10000,
13
- // claudeArgs: ["say hello and exit"]
14
- // })
15
- // }
7
+ import { writeFile } from 'fs/promises';
8
+ import path from 'path';
9
+ import { mkdir } from 'fs/promises';
16
10
 
17
11
  /**
18
12
  * Main function to run Claude with automatic yes/no respojnses
@@ -33,17 +27,28 @@ export default async function claudeYes({
33
27
  cwd = process.cwd(),
34
28
  // removeControlCharactersFromStdout = !process.stdout.isTTY,
35
29
  removeControlCharactersFromStdout = false,
30
+ logFile,
31
+ verbose = false,
36
32
  }: {
37
33
  continueOnCrash?: boolean;
38
- exitOnIdle?: boolean | number;
34
+ exitOnIdle?: number;
39
35
  claudeArgs?: string[];
40
36
  cwd?: string;
41
37
  removeControlCharactersFromStdout?: boolean;
38
+ logFile?: string;
39
+ verbose?: boolean;
42
40
  } = {}) {
43
- const defaultTimeout = 5e3; // 5 seconds idle timeout
44
- const idleTimeout =
45
- typeof exitOnIdle === 'number' ? exitOnIdle : defaultTimeout;
46
-
41
+ if (verbose) {
42
+ console.log('calling claudeYes: ', {
43
+ continueOnCrash,
44
+ exitOnIdle,
45
+ claudeArgs,
46
+ cwd,
47
+ removeControlCharactersFromStdout,
48
+ logFile,
49
+ verbose,
50
+ });
51
+ }
47
52
  console.log(
48
53
  '⭐ Starting claude, automatically responding to yes/no prompts...'
49
54
  );
@@ -68,6 +73,7 @@ export default async function claudeYes({
68
73
  cwd,
69
74
  env: process.env as Record<string, string>,
70
75
  });
76
+ let pendingExitCode = Promise.withResolvers<number | null>();
71
77
  // TODO handle error if claude is not installed, show msg:
72
78
  // npm install -g @anthropic-ai/claude-code
73
79
 
@@ -83,7 +89,7 @@ export default async function claudeYes({
83
89
  console.log(
84
90
  'Claude crashed with "No conversation found to continue", exiting...'
85
91
  );
86
- void process.exit(exitCode);
92
+ return pendingExitCode.resolve(exitCode);
87
93
  }
88
94
  console.log('Claude crashed, restarting...');
89
95
  shell = pty.spawn('claude', ['continue', '--continue'], {
@@ -97,7 +103,7 @@ export default async function claudeYes({
97
103
  shell.onExit(onExit);
98
104
  return;
99
105
  }
100
- void process.exit(exitCode);
106
+ return pendingExitCode.resolve(exitCode);
101
107
  });
102
108
 
103
109
  const exitClaudeCode = async () => {
@@ -144,33 +150,38 @@ export default async function claudeYes({
144
150
  };
145
151
 
146
152
  const ttr = new TerminalTextRender();
147
- const idleWatcher = createIdleWatcher(async () => {
148
- if (exitOnIdle) {
149
- if (
150
- ttr
151
- .render()
152
- .replace(/\s+/g, ' ')
153
- .match(/esc to interrupt|to run in background/)
154
- ) {
155
- console.warn(
156
- '[CLAUDE-YES] Claude is idle, but seems still working, not exiting yet'
157
- );
158
- } else {
159
- console.warn('[CLAUDE-YES] Claude is idle, exiting...');
160
- await exitClaudeCode();
161
- }
162
- }
163
- }, idleTimeout);
153
+ const idleWatcher = !exitOnIdle
154
+ ? null
155
+ : createIdleWatcher(async () => {
156
+ if (
157
+ ttr
158
+ .render()
159
+ .replace(/\s+/g, ' ')
160
+ .match(/esc to interrupt|to run in background/)
161
+ ) {
162
+ console.log(
163
+ '[claude-yes] Claude is idle, but seems still working, not exiting yet'
164
+ );
165
+ } else {
166
+ console.log('[claude-yes] Claude is idle, exiting...');
167
+ await exitClaudeCode();
168
+ }
169
+ }, exitOnIdle);
164
170
  const confirm = async () => {
165
171
  await sleepms(200);
166
172
  shell.write('\r');
167
173
  };
168
- await sflow(fromReadable<Buffer>(process.stdin))
169
- .forEach(() => idleWatcher.ping()) // ping the idle watcher on output for last active time to keep track of claude status
174
+
175
+ sflow(fromReadable<Buffer>(process.stdin))
170
176
  .map((buffer) => buffer.toString())
171
- .forEach((text) => ttr.write(text))
172
177
  // .forEach(e => appendFile('.cache/io.log', "input |" + JSON.stringify(e) + '\n')) // for debugging
173
178
  .by(shellStdio)
179
+ // handle ttr render
180
+ .forEach((text) => ttr.write(text))
181
+
182
+ // handle idle
183
+ .forEach(() => idleWatcher?.ping()) // ping the idle watcher on output for last active time to keep track of claude status
184
+ // auto-response
174
185
  .forkTo((e) =>
175
186
  e
176
187
  .map((e) => removeControlCharacters(e as string))
@@ -184,7 +195,6 @@ export default async function claudeYes({
184
195
  return;
185
196
  }
186
197
  })
187
-
188
198
  // .forEach(e => appendFile('.cache/io.log', "output|" + JSON.stringify(e) + '\n')) // for debugging
189
199
  .run()
190
200
  )
@@ -194,7 +204,19 @@ export default async function claudeYes({
194
204
  )
195
205
  .to(fromWritable(process.stdout));
196
206
 
197
- return ttr.render(); // return full rendered logs
207
+ const exitCode = await pendingExitCode.promise; // wait for the shell to exit
208
+ verbose && console.log(`[claude-yes] claude exited with code ${exitCode}`);
209
+
210
+ if (logFile) {
211
+ verbose && console.log(`[claude-yes] Writing rendered logs to ${logFile}`);
212
+ const logFilePath = path.resolve(logFile);
213
+ await mkdir(path.dirname(logFilePath), { recursive: true }).catch(
214
+ () => null
215
+ );
216
+ await writeFile(logFilePath, ttr.render());
217
+ }
218
+
219
+ return { exitCode, logs: ttr.render() };
198
220
  }
199
221
 
200
222
  export { removeControlCharacters };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-yes",
3
- "version": "1.14.2",
3
+ "version": "1.15.1",
4
4
  "homepage": "https://github.com/snomiao/claude-yes#readme",
5
5
  "license": "MIT",
6
6
  "author": "snomiao <snomiao@gmail.com>",
@@ -35,14 +35,13 @@
35
35
  "devDependencies": {
36
36
  "@types/bun": "^1.2.18",
37
37
  "@types/jest": "^30.0.0",
38
- "@types/minimist": "^1.2.5",
39
38
  "@types/node": "^24.0.10",
39
+ "@types/yargs": "^17.0.33",
40
40
  "enhanced-ms": "^4.1.0",
41
41
  "execa": "^9.6.0",
42
42
  "from-node-stream": "^0.0.11",
43
43
  "husky": "^9.1.7",
44
44
  "lint-staged": "^16.1.4",
45
- "minimist": "^1.2.8",
46
45
  "prettier": "^3.6.2",
47
46
  "semantic-release": "^24.2.6",
48
47
  "sflow": "^1.20.2",
@@ -63,7 +62,8 @@
63
62
  "dependencies": {
64
63
  "bun-pty": "^0.3.2",
65
64
  "node-pty": "^1.0.0",
66
- "terminal-render": "^1.1.0"
65
+ "terminal-render": "^1.1.0",
66
+ "yargs": "^18.0.0"
67
67
  },
68
68
  "lint-staged": {
69
69
  "*.{ts,js,json,md}": [