claude-yes 1.16.1 → 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/README.md +5 -1
- package/ReadyManager.ts +18 -0
- package/cli.ts +2 -1
- package/dist/cli.js +60 -40
- package/dist/cli.js.map +6 -5
- package/dist/index.js +59 -39
- package/dist/index.js.map +5 -4
- package/index.ts +42 -43
- package/package.json +6 -5
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
|
|
27
|
-
env
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
100
|
-
|
|
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 (
|
|
117
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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
|
-
|
|
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,
|
|
219
|
+
await writeFile(logFilePath, render.render());
|
|
221
220
|
}
|
|
222
221
|
|
|
223
|
-
return { exitCode, logs:
|
|
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.
|
|
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",
|
|
@@ -57,23 +57,24 @@
|
|
|
57
57
|
"node-pty": "^1.0.0"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
|
-
"terminal-render": "^1.1.0",
|
|
61
|
-
"yargs": "^18.0.0",
|
|
62
60
|
"@types/bun": "^1.2.18",
|
|
63
61
|
"@types/jest": "^30.0.0",
|
|
64
62
|
"@types/node": "^24.0.10",
|
|
65
63
|
"@types/yargs": "^17.0.33",
|
|
66
|
-
"sflow": "^1.20.2",
|
|
67
64
|
"enhanced-ms": "^4.1.0",
|
|
68
65
|
"execa": "^9.6.0",
|
|
69
66
|
"from-node-stream": "^0.0.11",
|
|
70
67
|
"husky": "^9.1.7",
|
|
71
68
|
"lint-staged": "^16.1.4",
|
|
72
69
|
"prettier": "^3.6.2",
|
|
70
|
+
"rambda": "^10.3.2",
|
|
73
71
|
"semantic-release": "^24.2.6",
|
|
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"
|