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/LICENSE +21 -0
- package/README.md +77 -0
- package/cli.test.ts +71 -0
- package/cli.ts +47 -0
- package/createIdleWatcher.spec.ts +41 -0
- package/createIdleWatcher.ts +18 -0
- package/dist/cli.js +6053 -0
- package/dist/index.js +4949 -0
- package/index.ts +136 -0
- package/package.json +73 -0
- package/removeControlCharacters.ts +4 -0
- package/sleep.ts +3 -0
- package/utils.ts +3 -0
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
|
+
}
|
package/sleep.ts
ADDED
package/utils.ts
ADDED