claude-yes 1.15.0 → 1.16.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.
@@ -0,0 +1,13 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../index.ts", "../createIdleWatcher.ts", "../removeControlCharacters.ts", "../utils.ts"],
4
+ "sourcesContent": [
5
+ "import { fromReadable, fromWritable } from 'from-node-stream';\nimport sflow from 'sflow';\nimport { createIdleWatcher } from './createIdleWatcher';\nimport { removeControlCharacters } from './removeControlCharacters';\nimport { sleepms } from './utils';\nimport { TerminalTextRender } from 'terminal-render';\nimport { writeFile } from 'fs/promises';\nimport path from 'path';\nimport { mkdir } from 'fs/promises';\n\n/**\n * Main function to run Claude with automatic yes/no respojnses\n * @param options Configuration options\n * @param options.continueOnCrash - If true, automatically restart Claude when it crashes:\n * 1. Shows message 'Claude crashed, restarting..'\n * 2. Spawns a new 'claude --continue' process\n * 3. Re-attaches the new process to the shell stdio (pipes new process stdin/stdout)\n * 4. If it crashes with \"No conversation found to continue\", exits the process\n * @param options.exitOnIdle - Exit when Claude is idle. Boolean or timeout in milliseconds\n * @param options.claudeArgs - Additional arguments to pass to the Claude CLI\n * @param options.removeControlCharactersFromStdout - Remove ANSI control characters from stdout. Defaults to !process.stdout.isTTY\n */\nexport default async function claudeYes({\n claudeArgs = [],\n continueOnCrash,\n cwd = process.cwd(),\n env = process.env as Record<string, string>,\n exitOnIdle,\n logFile,\n removeControlCharactersFromStdout = false, // = !process.stdout.isTTY,\n verbose = false,\n}: {\n claudeArgs?: string[];\n continueOnCrash?: boolean;\n cwd?: string;\n env?: Record<string, string>;\n exitOnIdle?: number;\n logFile?: string;\n removeControlCharactersFromStdout?: boolean;\n verbose?: boolean;\n} = {}) {\n if (verbose) {\n console.log('calling claudeYes: ', {\n continueOnCrash,\n exitOnIdle,\n claudeArgs,\n cwd,\n removeControlCharactersFromStdout,\n logFile,\n verbose,\n });\n }\n console.log(\n '⭐ Starting claude, automatically responding to yes/no prompts...'\n );\n console.log(\n '⚠️ 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.'\n );\n\n process.stdin.setRawMode?.(true); //must be called any stdout/stdin usage\n const prefix = ''; // \"YESC|\"\n const PREFIXLENGTH = prefix.length;\n let errorNoConversation = false; // match 'No conversation found to continue'\n\n const shellOutputStream = new TransformStream<string, string>();\n const outputWriter = shellOutputStream.writable.getWriter();\n // const pty = await import('node-pty');\n\n // recommened to use bun pty in windows\n const pty = process.versions.bun\n ? await import('bun-pty')\n : await import('node-pty');\n let shell = pty.spawn('claude', claudeArgs, {\n name: 'xterm-color',\n cols: process.stdout.columns - PREFIXLENGTH,\n rows: process.stdout.rows,\n cwd,\n env,\n });\n let pendingExitCode = Promise.withResolvers<number | null>();\n // TODO handle error if claude is not installed, show msg:\n // npm install -g @anthropic-ai/claude-code\n\n async function onData(data: string) {\n // append data to the buffer, so we can process it later\n await outputWriter.write(data);\n }\n shell.onData(onData);\n // when claude process exits, exit the main process with the same exit code\n shell.onExit(function onExit({ exitCode }) {\n if (continueOnCrash && exitCode !== 0) {\n if (errorNoConversation) {\n console.log(\n 'Claude crashed with \"No conversation found to continue\", exiting...'\n );\n return pendingExitCode.resolve(exitCode);\n }\n console.log('Claude crashed, restarting...');\n shell = pty.spawn('claude', ['--continue', 'continue'], {\n name: 'xterm-color',\n cols: process.stdout.columns - PREFIXLENGTH,\n rows: process.stdout.rows,\n cwd,\n env,\n });\n shell.onData(onData);\n shell.onExit(onExit);\n return;\n }\n return pendingExitCode.resolve(exitCode);\n });\n\n const exitClaudeCode = async () => {\n // send exit command to the shell, must sleep a bit to avoid claude treat it as pasted input\n await sflow(['\\r', '/exit', '\\r'])\n .forEach(async (e) => {\n await sleepms(200);\n shell.write(e);\n })\n .run();\n\n // wait for shell to exit or kill it with a timeout\n let exited = false;\n await Promise.race([\n new Promise<void>((resolve) =>\n shell.onExit(() => {\n resolve();\n exited = true;\n })\n ), // resolve when shell exits\n // if shell doesn't exit in 5 seconds, kill it\n new Promise<void>((resolve) =>\n setTimeout(() => {\n if (exited) return; // if shell already exited, do nothing\n shell.kill(); // kill the shell process if it doesn't exit in time\n resolve();\n }, 5000)\n ), // 5 seconds timeout\n ]);\n };\n\n // when current tty resized, resize the pty\n process.stdout.on('resize', () => {\n const { columns, rows } = process.stdout;\n shell.resize(columns - PREFIXLENGTH, rows);\n });\n\n const shellStdio = {\n writable: new WritableStream<string>({\n write: (data) => shell.write(data),\n close: () => {},\n }),\n readable: shellOutputStream.readable,\n };\n\n const ttr = new TerminalTextRender();\n const idleWatcher = !exitOnIdle\n ? null\n : createIdleWatcher(async () => {\n if (\n ttr\n .render()\n .replace(/\\s+/g, ' ')\n .match(/esc to interrupt|to run in background/)\n ) {\n console.log(\n '[claude-yes] Claude is idle, but seems still working, not exiting yet'\n );\n } else {\n console.log('[claude-yes] Claude is idle, exiting...');\n await exitClaudeCode();\n }\n }, exitOnIdle);\n const confirm = async () => {\n await sleepms(200);\n shell.write('\\r');\n };\n\n sflow(fromReadable<Buffer>(process.stdin))\n .map((buffer) => buffer.toString())\n // .forEach(e => appendFile('.cache/io.log', \"input |\" + JSON.stringify(e) + '\\n')) // for debugging\n .by(shellStdio)\n // handle ttr render\n .forEach((text) => ttr.write(text))\n\n // handle idle\n .forEach(() => idleWatcher?.ping()) // ping the idle watcher on output for last active time to keep track of claude status\n // auto-response\n .forkTo((e) =>\n e\n .map((e) => removeControlCharacters(e as string))\n .map((e) => e.replaceAll('\\r', '')) // remove carriage return\n .forEach(async (e) => {\n if (e.match(/❯ 1. Yes/)) return await confirm();\n if (e.match(/❯ 1. Dark mode✔|Press Enter to continue…/))\n return await confirm();\n if (e.match(/No conversation found to continue/)) {\n errorNoConversation = true; // set flag to true if error message is found\n return;\n }\n })\n // .forEach(e => appendFile('.cache/io.log', \"output|\" + JSON.stringify(e) + '\\n')) // for debugging\n .run()\n )\n .replaceAll(/.*(?:\\r\\n?|\\r?\\n)/g, (line) => prefix + line) // add prefix\n .map((e) =>\n removeControlCharactersFromStdout ? removeControlCharacters(e) : e\n )\n .to(fromWritable(process.stdout));\n\n const exitCode = await pendingExitCode.promise; // wait for the shell to exit\n verbose && console.log(`[claude-yes] claude exited with code ${exitCode}`);\n\n if (logFile) {\n verbose && console.log(`[claude-yes] Writing rendered logs to ${logFile}`);\n const logFilePath = path.resolve(logFile);\n await mkdir(path.dirname(logFilePath), { recursive: true }).catch(\n () => null\n );\n await writeFile(logFilePath, ttr.render());\n }\n\n return { exitCode, logs: ttr.render() };\n}\n\nexport { removeControlCharacters };\n",
6
+ "export function createIdleWatcher(\n onIdle: () => void,\n idleTimeout: number\n): { ping: () => void; getLastActiveTime: () => Date } {\n let lastActiveTime = new Date();\n let idleTimeoutId: NodeJS.Timeout | null = null;\n\n return {\n ping: () => {\n if (idleTimeoutId) clearTimeout(idleTimeoutId);\n\n lastActiveTime = new Date();\n idleTimeoutId = setTimeout(() => {\n clearTimeout(idleTimeoutId!);\n onIdle();\n }, idleTimeout);\n },\n getLastActiveTime: () => lastActiveTime,\n };\n}\n",
7
+ "export function removeControlCharacters(str: string): string {\n // Matches control characters in the C0 and C1 ranges, including Delete (U+007F)\n return str.replace(/[\\u001b\\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, '');\n}\n",
8
+ "export function sleepms(ms: number) {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n"
9
+ ],
10
+ "mappings": "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",
11
+ "debugId": "56353F5D622FBAA864756E2164756E21",
12
+ "names": []
13
+ }
package/index.ts CHANGED
@@ -5,15 +5,8 @@ import { removeControlCharacters } from './removeControlCharacters';
5
5
  import { sleepms } from './utils';
6
6
  import { TerminalTextRender } from 'terminal-render';
7
7
  import { writeFile } from 'fs/promises';
8
- // for debug only
9
- // if (import.meta.main) await main();
10
- // async function main() {
11
- // await claudeYes({
12
- // continueOnCrash: true,
13
- // exitOnIdle: 10000,
14
- // claudeArgs: ["say hello and exit"]
15
- // })
16
- // }
8
+ import path from 'path';
9
+ import { mkdir } from 'fs/promises';
17
10
 
18
11
  /**
19
12
  * Main function to run Claude with automatic yes/no respojnses
@@ -28,25 +21,35 @@ import { writeFile } from 'fs/promises';
28
21
  * @param options.removeControlCharactersFromStdout - Remove ANSI control characters from stdout. Defaults to !process.stdout.isTTY
29
22
  */
30
23
  export default async function claudeYes({
31
- continueOnCrash,
32
- exitOnIdle,
33
24
  claudeArgs = [],
25
+ continueOnCrash,
34
26
  cwd = process.cwd(),
35
- // removeControlCharactersFromStdout = !process.stdout.isTTY,
36
- removeControlCharactersFromStdout = false,
27
+ env = process.env as Record<string, string>,
28
+ exitOnIdle,
37
29
  logFile,
30
+ removeControlCharactersFromStdout = false, // = !process.stdout.isTTY,
31
+ verbose = false,
38
32
  }: {
39
- continueOnCrash?: boolean;
40
- exitOnIdle?: boolean | number;
41
33
  claudeArgs?: string[];
34
+ continueOnCrash?: boolean;
42
35
  cwd?: string;
43
- removeControlCharactersFromStdout?: boolean;
36
+ env?: Record<string, string>;
37
+ exitOnIdle?: number;
44
38
  logFile?: string;
39
+ removeControlCharactersFromStdout?: boolean;
40
+ verbose?: boolean;
45
41
  } = {}) {
46
- const defaultIdleTimeout = 60e3;
47
- const idleTimeout =
48
- typeof exitOnIdle === 'number' ? exitOnIdle : defaultIdleTimeout;
49
-
42
+ if (verbose) {
43
+ console.log('calling claudeYes: ', {
44
+ continueOnCrash,
45
+ exitOnIdle,
46
+ claudeArgs,
47
+ cwd,
48
+ removeControlCharactersFromStdout,
49
+ logFile,
50
+ verbose,
51
+ });
52
+ }
50
53
  console.log(
51
54
  '⭐ Starting claude, automatically responding to yes/no prompts...'
52
55
  );
@@ -61,7 +64,10 @@ export default async function claudeYes({
61
64
 
62
65
  const shellOutputStream = new TransformStream<string, string>();
63
66
  const outputWriter = shellOutputStream.writable.getWriter();
64
- const pty = globalThis.Bun
67
+ // const pty = await import('node-pty');
68
+
69
+ // recommened to use bun pty in windows
70
+ const pty = process.versions.bun
65
71
  ? await import('bun-pty')
66
72
  : await import('node-pty');
67
73
  let shell = pty.spawn('claude', claudeArgs, {
@@ -69,8 +75,9 @@ export default async function claudeYes({
69
75
  cols: process.stdout.columns - PREFIXLENGTH,
70
76
  rows: process.stdout.rows,
71
77
  cwd,
72
- env: process.env as Record<string, string>,
78
+ env,
73
79
  });
80
+ let pendingExitCode = Promise.withResolvers<number | null>();
74
81
  // TODO handle error if claude is not installed, show msg:
75
82
  // npm install -g @anthropic-ai/claude-code
76
83
 
@@ -86,21 +93,21 @@ export default async function claudeYes({
86
93
  console.log(
87
94
  'Claude crashed with "No conversation found to continue", exiting...'
88
95
  );
89
- void process.exit(exitCode);
96
+ return pendingExitCode.resolve(exitCode);
90
97
  }
91
98
  console.log('Claude crashed, restarting...');
92
- shell = pty.spawn('claude', ['continue', '--continue'], {
99
+ shell = pty.spawn('claude', ['--continue', 'continue'], {
93
100
  name: 'xterm-color',
94
101
  cols: process.stdout.columns - PREFIXLENGTH,
95
102
  rows: process.stdout.rows,
96
103
  cwd,
97
- env: process.env as Record<string, string>,
104
+ env,
98
105
  });
99
106
  shell.onData(onData);
100
107
  shell.onExit(onExit);
101
108
  return;
102
109
  }
103
- void process.exit(exitCode);
110
+ return pendingExitCode.resolve(exitCode);
104
111
  });
105
112
 
106
113
  const exitClaudeCode = async () => {
@@ -147,33 +154,38 @@ export default async function claudeYes({
147
154
  };
148
155
 
149
156
  const ttr = new TerminalTextRender();
150
- const idleWatcher = createIdleWatcher(async () => {
151
- if (exitOnIdle) {
152
- if (
153
- ttr
154
- .render()
155
- .replace(/\s+/g, ' ')
156
- .match(/esc to interrupt|to run in background/)
157
- ) {
158
- console.warn(
159
- '[CLAUDE-YES] Claude is idle, but seems still working, not exiting yet'
160
- );
161
- } else {
162
- console.warn('[CLAUDE-YES] Claude is idle, exiting...');
163
- await exitClaudeCode();
164
- }
165
- }
166
- }, idleTimeout);
157
+ const idleWatcher = !exitOnIdle
158
+ ? null
159
+ : createIdleWatcher(async () => {
160
+ if (
161
+ ttr
162
+ .render()
163
+ .replace(/\s+/g, ' ')
164
+ .match(/esc to interrupt|to run in background/)
165
+ ) {
166
+ console.log(
167
+ '[claude-yes] Claude is idle, but seems still working, not exiting yet'
168
+ );
169
+ } else {
170
+ console.log('[claude-yes] Claude is idle, exiting...');
171
+ await exitClaudeCode();
172
+ }
173
+ }, exitOnIdle);
167
174
  const confirm = async () => {
168
175
  await sleepms(200);
169
176
  shell.write('\r');
170
177
  };
171
- await sflow(fromReadable<Buffer>(process.stdin))
172
- .forEach(() => idleWatcher.ping()) // ping the idle watcher on output for last active time to keep track of claude status
178
+
179
+ sflow(fromReadable<Buffer>(process.stdin))
173
180
  .map((buffer) => buffer.toString())
174
- .forEach((text) => ttr.write(text))
175
181
  // .forEach(e => appendFile('.cache/io.log', "input |" + JSON.stringify(e) + '\n')) // for debugging
176
182
  .by(shellStdio)
183
+ // handle ttr render
184
+ .forEach((text) => ttr.write(text))
185
+
186
+ // handle idle
187
+ .forEach(() => idleWatcher?.ping()) // ping the idle watcher on output for last active time to keep track of claude status
188
+ // auto-response
177
189
  .forkTo((e) =>
178
190
  e
179
191
  .map((e) => removeControlCharacters(e as string))
@@ -187,7 +199,6 @@ export default async function claudeYes({
187
199
  return;
188
200
  }
189
201
  })
190
-
191
202
  // .forEach(e => appendFile('.cache/io.log', "output|" + JSON.stringify(e) + '\n')) // for debugging
192
203
  .run()
193
204
  )
@@ -197,8 +208,19 @@ export default async function claudeYes({
197
208
  )
198
209
  .to(fromWritable(process.stdout));
199
210
 
200
- logFile && (await writeFile(logFile, ttr.render()));
201
- return ttr.render(); // return full rendered logs
211
+ const exitCode = await pendingExitCode.promise; // wait for the shell to exit
212
+ verbose && console.log(`[claude-yes] claude exited with code ${exitCode}`);
213
+
214
+ if (logFile) {
215
+ verbose && console.log(`[claude-yes] Writing rendered logs to ${logFile}`);
216
+ const logFilePath = path.resolve(logFile);
217
+ await mkdir(path.dirname(logFilePath), { recursive: true }).catch(
218
+ () => null
219
+ );
220
+ await writeFile(logFilePath, ttr.render());
221
+ }
222
+
223
+ return { exitCode, logs: ttr.render() };
202
224
  }
203
225
 
204
226
  export { removeControlCharacters };
package/package.json CHANGED
@@ -1,9 +1,7 @@
1
1
  {
2
2
  "name": "claude-yes",
3
- "version": "1.15.0",
4
- "homepage": "https://github.com/snomiao/claude-yes#readme",
5
- "license": "MIT",
6
- "author": "snomiao <snomiao@gmail.com>",
3
+ "version": "1.16.0",
4
+ "description": "A wrapper tool that automates interactions with the Claude CLI by automatically handling common prompts and responses.",
7
5
  "keywords": [
8
6
  "claude",
9
7
  "ai",
@@ -14,23 +12,51 @@
14
12
  "anthropic",
15
13
  "auto-response"
16
14
  ],
17
- "files": [
18
- "*.ts",
19
- "dist"
20
- ],
21
- "bin": {
22
- "claude-yes": "dist/cli.js"
15
+ "homepage": "https://github.com/snomiao/claude-yes#readme",
16
+ "bugs": {
17
+ "url": "https://github.com/snomiao/claude-yes/issues"
23
18
  },
24
19
  "repository": {
25
20
  "type": "git",
26
21
  "url": "git+https://github.com/snomiao/claude-yes.git"
27
22
  },
23
+ "license": "MIT",
24
+ "author": "snomiao <snomiao@gmail.com>",
25
+ "type": "module",
26
+ "exports": {
27
+ "import": "./dist/index.js",
28
+ "types": "./index.ts"
29
+ },
30
+ "main": "index.js",
31
+ "module": "index.ts",
32
+ "types": "./index.ts",
33
+ "bin": {
34
+ "claude-yes": "dist/cli.js"
35
+ },
36
+ "directories": {
37
+ "doc": "docs"
38
+ },
39
+ "files": [
40
+ "*.ts",
41
+ "dist"
42
+ ],
28
43
  "scripts": {
44
+ "build": "bun build index.ts cli.ts --outdir=dist --packages=external --target=node --sourcemap",
29
45
  "dev": "tsx index.ts",
30
- "build": "bun build index.ts cli.ts --outdir=dist --external=node-pty --external=bun-pty --target=node",
31
- "test": "vitest",
46
+ "fmt": "prettier -w .",
32
47
  "prepare": "husky",
33
- "fmt": "prettier -w ."
48
+ "test": "vitest"
49
+ },
50
+ "lint-staged": {
51
+ "*.{ts,js,json,md}": [
52
+ "prettier --write"
53
+ ]
54
+ },
55
+ "dependencies": {
56
+ "bun-pty": "^0.3.2",
57
+ "node-pty": "^1.0.0",
58
+ "terminal-render": "^1.1.0",
59
+ "yargs": "^18.0.0"
34
60
  },
35
61
  "devDependencies": {
36
62
  "@types/bun": "^1.2.18",
@@ -51,31 +77,5 @@
51
77
  },
52
78
  "peerDependencies": {
53
79
  "typescript": "^5.8.3"
54
- },
55
- "type": "module",
56
- "exports": {
57
- "import": "./dist/index.js",
58
- "types": "./index.ts"
59
- },
60
- "module": "index.ts",
61
- "types": "./index.ts",
62
- "dependencies": {
63
- "bun-pty": "^0.3.2",
64
- "node-pty": "^1.0.0",
65
- "terminal-render": "^1.1.0",
66
- "yargs": "^18.0.0"
67
- },
68
- "lint-staged": {
69
- "*.{ts,js,json,md}": [
70
- "prettier --write"
71
- ]
72
- },
73
- "description": "A wrapper tool that automates interactions with the Claude CLI by automatically handling common prompts and responses.",
74
- "main": "index.js",
75
- "directories": {
76
- "doc": "docs"
77
- },
78
- "bugs": {
79
- "url": "https://github.com/snomiao/claude-yes/issues"
80
80
  }
81
81
  }