claude-yes 1.10.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 ADDED
@@ -0,0 +1,136 @@
1
+
2
+ import { fromReadable, fromWritable } from "from-node-stream";
3
+ import * as pty from "node-pty";
4
+ import sflow from "sflow";
5
+ import { createIdleWatcher } from "./createIdleWatcher";
6
+ import { removeControlCharacters } from "./removeControlCharacters";
7
+ import { sleepms } from "./utils";
8
+
9
+
10
+ if (import.meta.main) await main();
11
+ async function main() {
12
+ // this script not support bun yet, so use node js to run.
13
+
14
+ // node-pty is not supported in bun, so we use node.js to run this script
15
+ }
16
+
17
+ export default async function claudeYes({ continueOnCrash, exitOnIdle, claudeArgs = [] }: { continueOnCrash?: boolean, exitOnIdle?: boolean | number, claudeArgs?: string[] } = {}) {
18
+ const defaultTimeout = 5e3; // 5 seconds idle timeout
19
+ const idleTimeout = typeof exitOnIdle === 'number' ? exitOnIdle : defaultTimeout;
20
+
21
+ console.log('⭐ Starting claude, automatically responding to yes/no prompts...');
22
+ 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.');
23
+
24
+ process.stdin.setRawMode?.(true) //must be called any stdout/stdin usage
25
+ const prefix = '' // "YESC|"
26
+ const PREFIXLENGTH = prefix.length;
27
+
28
+
29
+ // TODO: implement this flag to continue on crash
30
+ // 1. if it crashes, show message 'claude crashed, restarting..'
31
+ // 2. spawn a 'claude --continue'
32
+ // 3. when new process it's ready, re-attach the into new process (in shellStdio, pipe new process stdin/stdout to )
33
+ // 4. if it crashes again, exit the process
34
+
35
+ const shellOutputStream = new TransformStream<string, string>()
36
+ const outputWriter = shellOutputStream.writable.getWriter()
37
+
38
+ let shell = pty.spawn('claude', claudeArgs, {
39
+ cols: process.stdout.columns - PREFIXLENGTH,
40
+ rows: process.stdout.rows,
41
+ cwd: process.cwd(),
42
+ env: process.env,
43
+ });
44
+ // TODO handle error if claude is not installed, show msg:
45
+ // npm install -g @anthropic-ai/claude-code
46
+
47
+ async function onData(data: string) {
48
+ // append data to the buffer, so we can process it later
49
+ await outputWriter.write(data);
50
+ }
51
+ shell.onData(onData)
52
+ // when claude process exits, exit the main process with the same exit code
53
+ shell.onExit(function onExit({ exitCode }) {
54
+ if (continueOnCrash) {
55
+ if (exitCode !== 0) {
56
+ console.log('Claude crashed, restarting...');
57
+ shell = pty.spawn('claude', ['continue', '--continue'], {
58
+ cols: process.stdout.columns - PREFIXLENGTH,
59
+ rows: process.stdout.rows,
60
+ cwd: process.cwd(),
61
+ env: process.env,
62
+ });
63
+ shell.onData(onData)
64
+ shell.onExit(onExit);
65
+ }
66
+ }
67
+ void process.exit(exitCode);
68
+ });
69
+
70
+ const exitClaudeCode = async () => {
71
+ // send exit command to the shell, must sleep a bit to avoid claude treat it as pasted input
72
+ await sflow(['\r', '/exit', '\r']).forEach(async (e) => {
73
+ await sleepms(200)
74
+ shell.write(e)
75
+ }).run();
76
+
77
+ // wait for shell to exit or kill it with a timeout
78
+ let exited = false;
79
+ await Promise.race([
80
+ new Promise<void>((resolve) => shell.onExit(() => { resolve(); exited = true; })), // resolve when shell exits
81
+ // if shell doesn't exit in 5 seconds, kill it
82
+ new Promise<void>((resolve) => setTimeout(() => {
83
+ if (exited) return; // if shell already exited, do nothing
84
+ shell.kill(); // kill the shell process if it doesn't exit in time
85
+ resolve();
86
+ }, 5000)) // 5 seconds timeout
87
+ ]);
88
+ }
89
+
90
+ // when current tty resized, resize the pty
91
+ process.stdout.on('resize', () => {
92
+ const { columns, rows } = process.stdout;
93
+ shell.resize(columns - PREFIXLENGTH, rows);
94
+ });
95
+
96
+ const shellStdio = {
97
+ writable: new WritableStream<string>({ write: (data) => shell.write(data), close: () => { } }),
98
+ readable: shellOutputStream.readable
99
+ };
100
+
101
+ const idleWatcher = createIdleWatcher(async () => {
102
+ if (exitOnIdle) {
103
+ console.log('Claude is idle, exiting...');
104
+ await exitClaudeCode()
105
+ }
106
+ }, idleTimeout);
107
+
108
+ await sflow(fromReadable<Buffer>(process.stdin))
109
+ .map((buffer) => buffer.toString())
110
+ // .forEach(e => appendFile('.cache/io.log', "input |" + JSON.stringify(e) + '\n')) // for debugging
111
+ .by(shellStdio)
112
+ .forkTo(e => e
113
+ .map(e => removeControlCharacters(e as string))
114
+ .map(e => e.replaceAll('\r', '')) // remove carriage return
115
+ .forEach(async e => {
116
+ if (e.match(/❯ 1. Yes/)) {
117
+ await sleepms(200)
118
+ shell.write("\r")
119
+ }
120
+ })
121
+ .forEach(async e => {
122
+ if (e.match(/❯ 1. Dark mode✔|Press Enter to continue…/)) {
123
+ await sleepms(200)
124
+ shell.write("\r")
125
+ }
126
+ })
127
+ // .forEach(e => appendFile('.cache/io.log', "output|" + JSON.stringify(e) + '\n')) // for debugging
128
+ .run()
129
+ )
130
+ .replaceAll(/.*(?:\r\n?|\r?\n)/g, (line) => prefix + line) // add prefix
131
+ .forEach(() => idleWatcher.ping()) // ping the idle watcher on output for last active time to keep track of claude status
132
+ .map(e => !process.stdout.isTTY ? removeControlCharacters(e) : (e)) // remove control characters if output is not a TTY
133
+ .to(fromWritable(process.stdout));
134
+ }
135
+
136
+ export { removeControlCharacters };
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "claude-yes",
3
+ "version": "1.10.0",
4
+ "homepage": "https://github.com/snomiao/claude-yes#readme",
5
+ "license": "MIT",
6
+ "author": "snomiao <snomiao@gmail.com>",
7
+ "keywords": [
8
+ "claude",
9
+ "ai",
10
+ "automation",
11
+ "cli",
12
+ "wrapper",
13
+ "assistant",
14
+ "anthropic",
15
+ "auto-response"
16
+ ],
17
+ "files": [
18
+ "*.ts",
19
+ "dist"
20
+ ],
21
+ "bin": {
22
+ "claude-yes": "dist/cli.js"
23
+ },
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/snomiao/claude-yes.git"
27
+ },
28
+ "scripts": {
29
+ "dev": "tsx index.ts",
30
+ "build": "bun build index.ts cli.ts --outdir=dist --external=node-pty --target=node",
31
+ "test": "vitest",
32
+ "prepare": "husky",
33
+ "fmt": "prettier -w ."
34
+ },
35
+ "devDependencies": {
36
+ "@types/bun": "^1.2.18",
37
+ "@types/jest": "^30.0.0",
38
+ "@types/node": "^24.0.10",
39
+ "enhanced-ms": "^4.1.0",
40
+ "execa": "^9.6.0",
41
+ "from-node-stream": "^0.0.11",
42
+ "husky": "^9.1.7",
43
+ "minimist": "^1.2.8",
44
+ "prettier": "^3.6.2",
45
+ "semantic-release": "^24.2.6",
46
+ "sflow": "^1.20.2",
47
+ "strip-ansi-control-characters": "^2.0.0",
48
+ "@types/minimist": "^1.2.5",
49
+ "tsx": "^4.20.3",
50
+ "vitest": "^3.2.4"
51
+ },
52
+ "peerDependencies": {
53
+ "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
+ "node-pty": "^1.0.0"
64
+ },
65
+ "description": "A wrapper tool that automates interactions with the Claude CLI by automatically handling common prompts and responses.",
66
+ "main": "index.js",
67
+ "directories": {
68
+ "doc": "docs"
69
+ },
70
+ "bugs": {
71
+ "url": "https://github.com/snomiao/claude-yes/issues"
72
+ }
73
+ }
@@ -0,0 +1,4 @@
1
+ export function removeControlCharacters(str: string): string {
2
+ // Matches control characters in the C0 and C1 ranges, including Delete (U+007F)
3
+ return str.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, '');
4
+ }
package/sleep.ts ADDED
@@ -0,0 +1,3 @@
1
+ export function sleep(ms: number) {
2
+ return new Promise(resolve => setTimeout(resolve, ms));
3
+ }
package/utils.ts ADDED
@@ -0,0 +1,3 @@
1
+ export function sleepms(ms: number) {
2
+ return new Promise(resolve => setTimeout(resolve, ms));
3
+ }