claude-yes 1.24.2 → 1.26.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 +136 -38
- package/dist/claude-yes.js +326 -175
- package/dist/cli.js +326 -175
- package/dist/cli.js.map +11 -5
- package/dist/codex-yes.js +326 -175
- package/dist/copilot-yes.js +326 -175
- package/dist/cursor-yes.js +326 -175
- package/dist/gemini-yes.js +326 -175
- package/dist/grok-yes.js +326 -175
- package/dist/index.js +226 -105
- package/dist/index.js.map +10 -5
- package/dist/qwen-yes.js +12102 -0
- package/package.json +39 -4
- package/ts/cli-idle.spec.ts +15 -12
- package/ts/cli.ts +15 -83
- package/ts/defineConfig.ts +12 -0
- package/ts/index.ts +166 -159
- package/ts/parseCliArgs.spec.ts +220 -0
- package/ts/parseCliArgs.ts +111 -0
- package/ts/postbuild.ts +6 -4
- package/ts/runningLock.spec.ts +45 -31
- package/ts/tryCatch.ts +25 -0
- package/ts/utils.ts +20 -0
- package/ts/yesLog.ts +27 -0
- package/ts/cli.test.ts +0 -63
package/ts/index.ts
CHANGED
|
@@ -5,6 +5,8 @@ import DIE from 'phpdie';
|
|
|
5
5
|
import sflow from 'sflow';
|
|
6
6
|
import { TerminalTextRender } from 'terminal-render';
|
|
7
7
|
import tsaComposer from 'tsa-composer';
|
|
8
|
+
import rawConfig from '../cli-yes.config.js';
|
|
9
|
+
import { defineCliYesConfig } from './defineConfig.js';
|
|
8
10
|
import { IdleWaiter } from './idleWaiter';
|
|
9
11
|
import { ReadyManager } from './ReadyManager';
|
|
10
12
|
import { removeControlCharacters } from './removeControlCharacters';
|
|
@@ -14,73 +16,36 @@ import {
|
|
|
14
16
|
shouldUseLock,
|
|
15
17
|
updateCurrentTaskStatus,
|
|
16
18
|
} from './runningLock';
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
string
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
ready: [/^ │ ❯ /],
|
|
36
|
-
enter: [/^ 1. Yes/],
|
|
37
|
-
},
|
|
38
|
-
claude: {
|
|
39
|
-
install: 'npm install -g @anthropic-ai/claude-code',
|
|
40
|
-
// ready: [/^> /], // regex matcher for stdin ready
|
|
41
|
-
ready: [/\? for shortcuts/], // regex matcher for stdin ready
|
|
42
|
-
enter: [/❯ 1. Yes/, /❯ 1. Dark mode✔/, /Press Enter to continue…/],
|
|
43
|
-
fatal: [
|
|
44
|
-
/No conversation found to continue/,
|
|
45
|
-
/⎿ Claude usage limit reached\./,
|
|
46
|
-
],
|
|
47
|
-
},
|
|
48
|
-
gemini: {
|
|
49
|
-
install: 'npm install -g @google/gemini-cli',
|
|
50
|
-
// match the agent prompt after initial lines; handled by index logic using line index
|
|
51
|
-
ready: [/Type your message/], // used with line index check
|
|
52
|
-
enter: [/│ ● 1. Yes, allow once/],
|
|
53
|
-
fatal: [],
|
|
54
|
-
},
|
|
55
|
-
codex: {
|
|
56
|
-
install: 'npm install -g @openai/codex-cli',
|
|
57
|
-
ready: [/⏎ send/],
|
|
58
|
-
enter: [
|
|
59
|
-
/> 1. Yes, allow Codex to work in this folder/,
|
|
60
|
-
/> 1. Approve and run now/,
|
|
61
|
-
],
|
|
62
|
-
fatal: [/Error: The cursor position could not be read within/],
|
|
63
|
-
// add to codex --search by default when not provided by the user
|
|
64
|
-
ensureArgs: (args: string[]) => {
|
|
65
|
-
if (!args.includes('--search')) return ['--search', ...args];
|
|
66
|
-
return args;
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
copilot: {
|
|
70
|
-
install: 'npm install -g @github/copilot',
|
|
71
|
-
ready: [/^ +> /, /Ctrl\+c Exit/],
|
|
72
|
-
enter: [/ │ ❯ 1. Yes, proceed/, /❯ 1. Yes/],
|
|
73
|
-
fatal: [],
|
|
74
|
-
},
|
|
75
|
-
cursor: {
|
|
76
|
-
install: 'open https://cursor.com/ja/docs/cli/installation',
|
|
77
|
-
// map logical "cursor" cli name to actual binary name
|
|
78
|
-
binary: 'cursor-agent',
|
|
79
|
-
ready: [/\/ commands/],
|
|
80
|
-
enter: [/→ Run \(once\) \(y\) \(enter\)/, /▶ \[a\] Trust this workspace/],
|
|
81
|
-
fatal: [/^ Error: You've hit your usage limit/],
|
|
82
|
-
},
|
|
19
|
+
import { catcher } from './tryCatch';
|
|
20
|
+
import { deepMixin } from './utils';
|
|
21
|
+
import { yesLog } from './yesLog';
|
|
22
|
+
|
|
23
|
+
export type AgentCliConfig = {
|
|
24
|
+
install?: string; // hint user for install command if not installed
|
|
25
|
+
version?: string; // hint user for version command to check if installed
|
|
26
|
+
binary?: string; // actual binary name if different from cli, e.g. cursor -> cursor-agent
|
|
27
|
+
ready?: RegExp[]; // regex matcher for stdin ready, or line index for gemini
|
|
28
|
+
enter?: RegExp[]; // array of regex to match for sending Enter
|
|
29
|
+
fatal?: RegExp[]; // array of regex to match for fatal errors
|
|
30
|
+
restoreArgs?: string[]; // arguments to continue the session when crashed
|
|
31
|
+
defaultArgs?: string[]; // function to ensure certain args are present
|
|
32
|
+
noEOL?: boolean; // if true, do not split lines by \n, used for codex, which uses cursor-move csi code instead of \n to move lines
|
|
33
|
+
promptArg?: (string & {}) | 'first-arg' | 'last-arg'; // argument name to pass the prompt, e.g. --prompt, or first-arg for positional arg
|
|
34
|
+
};
|
|
35
|
+
export type CliYesConfig = {
|
|
36
|
+
clis: { [key: string]: AgentCliConfig };
|
|
83
37
|
};
|
|
38
|
+
|
|
39
|
+
// load user config from cli-yes.config.ts if exists
|
|
40
|
+
export const config = await rawConfig;
|
|
41
|
+
|
|
42
|
+
export const CLIS_CONFIG = config.clis as Record<
|
|
43
|
+
keyof Awaited<typeof config>['clis'],
|
|
44
|
+
AgentCliConfig
|
|
45
|
+
>;
|
|
46
|
+
export type SUPPORTED_CLIS = keyof typeof CLIS_CONFIG;
|
|
47
|
+
export const SUPPORTED_CLIS = Object.keys(CLIS_CONFIG) as SUPPORTED_CLIS[];
|
|
48
|
+
|
|
84
49
|
/**
|
|
85
50
|
* Main function to run agent-cli with automatic yes/no responses
|
|
86
51
|
* @param options Configuration options
|
|
@@ -96,77 +61,86 @@ export const CLI_CONFIGURES: Record<
|
|
|
96
61
|
*
|
|
97
62
|
* @example
|
|
98
63
|
* ```typescript
|
|
99
|
-
* import
|
|
100
|
-
* await
|
|
64
|
+
* import cliYes from 'cli-yes';
|
|
65
|
+
* await cliYes({
|
|
101
66
|
* prompt: 'help me solve all todos in my codebase',
|
|
102
67
|
*
|
|
103
68
|
* // optional
|
|
104
|
-
*
|
|
105
|
-
* cliArgs: ['--verbose'], // additional args to pass to claude
|
|
69
|
+
* cliArgs: ['--verbose'], // additional args to pass to agent-cli
|
|
106
70
|
* exitOnIdle: 30000, // exit after 30 seconds of idle
|
|
107
|
-
*
|
|
71
|
+
* robust: true, // auto restart with --continue if claude crashes, default is true
|
|
108
72
|
* logFile: 'claude.log', // save logs to file
|
|
109
73
|
* disableLock: false, // disable running lock (default is false)
|
|
110
74
|
* });
|
|
111
75
|
* ```
|
|
112
76
|
*/
|
|
113
|
-
export default async function
|
|
114
|
-
cli
|
|
77
|
+
export default async function cliYes({
|
|
78
|
+
cli,
|
|
115
79
|
cliArgs = [],
|
|
116
80
|
prompt,
|
|
117
|
-
|
|
81
|
+
robust = true,
|
|
118
82
|
cwd,
|
|
119
83
|
env,
|
|
120
84
|
exitOnIdle,
|
|
121
85
|
logFile,
|
|
122
86
|
removeControlCharactersFromStdout = false, // = !process.stdout.isTTY,
|
|
123
87
|
verbose = false,
|
|
124
|
-
|
|
88
|
+
queue = true,
|
|
125
89
|
}: {
|
|
126
|
-
cli
|
|
90
|
+
cli: SUPPORTED_CLIS;
|
|
127
91
|
cliArgs?: string[];
|
|
128
92
|
prompt?: string;
|
|
129
|
-
|
|
93
|
+
robust?: boolean;
|
|
130
94
|
cwd?: string;
|
|
131
95
|
env?: Record<string, string>;
|
|
132
96
|
exitOnIdle?: number;
|
|
133
97
|
logFile?: string;
|
|
134
98
|
removeControlCharactersFromStdout?: boolean;
|
|
135
99
|
verbose?: boolean;
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
100
|
+
queue?: boolean;
|
|
101
|
+
}) {
|
|
102
|
+
// those overrides seems only works in bun
|
|
103
|
+
// await Promise.allSettled([
|
|
104
|
+
// import(path.join(process.cwd(), "cli-yes.config")),
|
|
105
|
+
// ])
|
|
106
|
+
// .then((e) => e.flatMap((e) => (e.status === "fulfilled" ? [e.value] : [])))
|
|
107
|
+
// .then(e=>e.at(0))
|
|
108
|
+
// .then((e) => e.default as ReturnType<typeof defineCliYesConfig>)
|
|
109
|
+
// .then(async (override) => deepMixin(config, override || {}))
|
|
110
|
+
// .catch((error) => {
|
|
111
|
+
// if (process.env.VERBOSE)
|
|
112
|
+
// console.warn("Fail to load cli-yes.config.ts", error);
|
|
113
|
+
// });
|
|
114
|
+
|
|
115
|
+
if (!cli) throw new Error(`cli is required`);
|
|
116
|
+
const conf = CLIS_CONFIG[cli] || DIE(`Unsupported cli tool: ${cli}`);
|
|
143
117
|
|
|
144
118
|
// Acquire lock before starting agent (if in git repo or same cwd and lock is not disabled)
|
|
145
119
|
const workingDir = cwd ?? process.cwd();
|
|
146
|
-
if (
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
// Register cleanup handlers for lock release
|
|
151
|
-
const cleanupLock = async () => {
|
|
152
|
-
if (!disableLock && shouldUseLock(workingDir)) {
|
|
153
|
-
await releaseLock().catch(() => null); // Ignore errors during cleanup
|
|
120
|
+
if (queue) {
|
|
121
|
+
if (queue && shouldUseLock(workingDir)) {
|
|
122
|
+
await acquireLock(workingDir, prompt ?? 'Interactive session');
|
|
154
123
|
}
|
|
155
|
-
};
|
|
156
124
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
process.exit(
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
125
|
+
// Register cleanup handlers for lock release
|
|
126
|
+
const cleanupLock = async () => {
|
|
127
|
+
if (queue && shouldUseLock(workingDir)) {
|
|
128
|
+
await releaseLock().catch(() => null); // Ignore errors during cleanup
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
process.on('exit', () => {
|
|
133
|
+
if (queue) releaseLock().catch(() => null);
|
|
134
|
+
});
|
|
135
|
+
process.on('SIGINT', async (code) => {
|
|
136
|
+
await cleanupLock();
|
|
137
|
+
process.exit(code);
|
|
138
|
+
});
|
|
139
|
+
process.on('SIGTERM', async (code) => {
|
|
140
|
+
await cleanupLock();
|
|
141
|
+
process.exit(code);
|
|
142
|
+
});
|
|
143
|
+
}
|
|
170
144
|
|
|
171
145
|
process.stdin.setRawMode?.(true); // must be called any stdout/stdin usage
|
|
172
146
|
let isFatal = false; // when true, do not restart on crash, and exit agent
|
|
@@ -192,21 +166,49 @@ export default async function claudeYes({
|
|
|
192
166
|
});
|
|
193
167
|
|
|
194
168
|
// Apply CLI specific configurations (moved to CLI_CONFIGURES)
|
|
195
|
-
const cliConf = (
|
|
196
|
-
cliArgs = cliConf.
|
|
169
|
+
const cliConf = (CLIS_CONFIG as Record<string, AgentCliConfig>)[cli] || {};
|
|
170
|
+
cliArgs = cliConf.defaultArgs
|
|
171
|
+
? [...cliConf.defaultArgs, ...cliArgs]
|
|
172
|
+
: cliArgs;
|
|
173
|
+
|
|
174
|
+
if (prompt && cliConf.promptArg) {
|
|
175
|
+
if (cliConf.promptArg === 'first-arg') {
|
|
176
|
+
cliArgs = [prompt, ...cliArgs];
|
|
177
|
+
prompt = undefined; // clear prompt to avoid sending later
|
|
178
|
+
} else if (cliConf.promptArg === 'last-arg') {
|
|
179
|
+
cliArgs = [...cliArgs, prompt];
|
|
180
|
+
prompt = undefined; // clear prompt to avoid sending later
|
|
181
|
+
} else if (cliConf.promptArg.startsWith('--')) {
|
|
182
|
+
cliArgs = [cliConf.promptArg, prompt, ...cliArgs];
|
|
183
|
+
prompt = undefined; // clear prompt to avoid sending later
|
|
184
|
+
} else {
|
|
185
|
+
console.warn(`Unknown promptArg format: ${cliConf.promptArg}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
197
188
|
const cliCommand = cliConf?.binary || cli;
|
|
198
189
|
|
|
199
|
-
let shell =
|
|
200
|
-
() => pty.spawn(cliCommand, cliArgs, getPtyOptions()),
|
|
190
|
+
let shell = catcher(
|
|
201
191
|
(error: unknown) => {
|
|
202
192
|
console.error(`Fatal: Failed to start ${cliCommand}.`);
|
|
203
|
-
if (cliConf?.install)
|
|
193
|
+
if (cliConf?.install && isCommandNotFoundError(error))
|
|
204
194
|
console.error(
|
|
205
195
|
`If you did not installed it yet, Please install it first: ${cliConf.install}`,
|
|
206
196
|
);
|
|
207
197
|
throw error;
|
|
198
|
+
|
|
199
|
+
function isCommandNotFoundError(e: unknown) {
|
|
200
|
+
if (e instanceof Error) {
|
|
201
|
+
return (
|
|
202
|
+
e.message.includes('command not found') ||
|
|
203
|
+
e.message.includes('ENOENT') ||
|
|
204
|
+
e.message.includes('spawn') // windows
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
208
209
|
},
|
|
209
|
-
|
|
210
|
+
() => pty.spawn(cliCommand, cliArgs, getPtyOptions()),
|
|
211
|
+
)();
|
|
210
212
|
const pendingExitCode = Promise.withResolvers<number | null>();
|
|
211
213
|
let pendingExitCodeValue = null;
|
|
212
214
|
|
|
@@ -214,35 +216,35 @@ export default async function claudeYes({
|
|
|
214
216
|
// npm install -g @anthropic-ai/claude-code
|
|
215
217
|
|
|
216
218
|
async function onData(data: string) {
|
|
217
|
-
nextStdout.ready();
|
|
218
219
|
// append data to the buffer, so we can process it later
|
|
219
220
|
await outputWriter.write(data);
|
|
220
221
|
}
|
|
221
222
|
|
|
222
223
|
shell.onData(onData);
|
|
223
224
|
shell.onExit(function onExit({ exitCode }) {
|
|
224
|
-
nextStdout.ready();
|
|
225
225
|
stdinReady.unready(); // start buffer stdin
|
|
226
226
|
const agentCrashed = exitCode !== 0;
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
227
|
+
|
|
228
|
+
if (agentCrashed && robust && conf?.restoreArgs) {
|
|
229
|
+
if (!conf.restoreArgs) {
|
|
230
|
+
return console.warn(
|
|
231
|
+
`robust is only supported for ${Object.entries(CLIS_CONFIG)
|
|
232
|
+
.filter(([_, v]) => v.restoreArgs)
|
|
233
|
+
.map(([k]) => k)
|
|
234
|
+
.join(', ')} currently, not ${cli}`,
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
if (isFatal) {
|
|
238
|
+
return pendingExitCode.resolve((pendingExitCodeValue = exitCode));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
console.log(`${cli} crashed, restarting...`);
|
|
242
|
+
|
|
243
|
+
shell = pty.spawn(cli, conf.restoreArgs, getPtyOptions());
|
|
244
|
+
shell.onData(onData);
|
|
245
|
+
shell.onExit(onExit);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
246
248
|
return pendingExitCode.resolve((pendingExitCodeValue = exitCode));
|
|
247
249
|
});
|
|
248
250
|
|
|
@@ -294,7 +296,8 @@ export default async function claudeYes({
|
|
|
294
296
|
readable: shellOutputStream.readable,
|
|
295
297
|
})
|
|
296
298
|
.forEach(() => idleWaiter.ping())
|
|
297
|
-
.forEach((
|
|
299
|
+
.forEach(() => nextStdout.ready())
|
|
300
|
+
.forEach(async (text) => {
|
|
298
301
|
terminalRender.write(text);
|
|
299
302
|
// todo: .onStatus((msg)=> shell.write(msg))
|
|
300
303
|
if (process.stdin.isTTY) return; // only handle it when stdin is not tty
|
|
@@ -306,7 +309,7 @@ export default async function claudeYes({
|
|
|
306
309
|
// const rendered = terminalRender.render();
|
|
307
310
|
const { col, row } = terminalRender.getCursorPosition();
|
|
308
311
|
shell.write(`\u001b[${row};${col}R`); // reply cli when getting cursor position
|
|
309
|
-
|
|
312
|
+
await yesLog`cursor|respond position: row=${String(row)}, col=${String(col)}`;
|
|
310
313
|
// const row = rendered.split('\n').length + 1;
|
|
311
314
|
// const col = (rendered.split('\n').slice(-1)[0]?.length || 0) + 1;
|
|
312
315
|
})
|
|
@@ -317,33 +320,29 @@ export default async function claudeYes({
|
|
|
317
320
|
.map((e) => removeControlCharacters(e))
|
|
318
321
|
.map((e) => e.replaceAll('\r', '')) // remove carriage return
|
|
319
322
|
.by((s) => {
|
|
320
|
-
if (
|
|
323
|
+
if (conf.noEOL) return s; // codex use cursor-move csi code insteadof \n to move lines, so the output have no \n at all, this hack prevents stuck on unended line
|
|
321
324
|
return s.lines({ EOL: 'NONE' }); // other clis use ink, which is rerendering the block based on \n lines
|
|
322
325
|
})
|
|
323
|
-
|
|
326
|
+
.forEach((e) => yesLog`output|${e}`) // for debugging
|
|
324
327
|
// Generic auto-response handler driven by CLI_CONFIGURES
|
|
325
328
|
.forEach(async (e, i) => {
|
|
326
|
-
const conf =
|
|
327
|
-
CLI_CONFIGURES[cli as keyof typeof CLI_CONFIGURES] || null;
|
|
328
|
-
if (!conf) return;
|
|
329
|
-
|
|
330
329
|
// ready matcher: if matched, mark stdin ready
|
|
331
330
|
if (conf.ready?.some((rx: RegExp) => e.match(rx))) {
|
|
332
|
-
|
|
331
|
+
await yesLog`ready |${e}`;
|
|
333
332
|
if (cli === 'gemini' && i <= 80) return; // gemini initial noise, only after many lines
|
|
334
333
|
stdinReady.ready();
|
|
335
334
|
}
|
|
336
335
|
|
|
337
336
|
// enter matchers: send Enter when any enter regex matches
|
|
338
337
|
if (conf.enter?.some((rx: RegExp) => e.match(rx))) {
|
|
339
|
-
|
|
338
|
+
await yesLog`enter |${e}`;
|
|
340
339
|
await sendEnter(300); // send Enter after 300ms idle wait
|
|
341
340
|
return;
|
|
342
341
|
}
|
|
343
342
|
|
|
344
343
|
// fatal matchers: set isFatal flag when matched
|
|
345
344
|
if (conf.fatal?.some((rx: RegExp) => e.match(rx))) {
|
|
346
|
-
|
|
345
|
+
await yesLog`fatal |${e}`;
|
|
347
346
|
isFatal = true;
|
|
348
347
|
await exitAgent();
|
|
349
348
|
}
|
|
@@ -364,7 +363,7 @@ export default async function claudeYes({
|
|
|
364
363
|
console.log(`[${cli}-yes] ${cli} exited with code ${exitCode}`);
|
|
365
364
|
|
|
366
365
|
// Update task status and release lock
|
|
367
|
-
if (
|
|
366
|
+
if (queue && shouldUseLock(workingDir)) {
|
|
368
367
|
await updateCurrentTaskStatus(
|
|
369
368
|
exitCode === 0 ? 'completed' : 'failed',
|
|
370
369
|
).catch(() => null);
|
|
@@ -388,24 +387,40 @@ export default async function claudeYes({
|
|
|
388
387
|
await idleWaiter.wait(waitms);
|
|
389
388
|
const et = Date.now();
|
|
390
389
|
// process.stdout.write(`\ridleWaiter.wait(${waitms}) took ${et - st}ms\r`);
|
|
391
|
-
|
|
392
|
-
|
|
390
|
+
await yesLog`sendEn| idleWaiter.wait(${String(waitms)}) took ${String(et - st)}ms`;
|
|
391
|
+
nextStdout.unready();
|
|
393
392
|
shell.write('\r');
|
|
393
|
+
// retry once if not received any output in 1 second after sending Enter
|
|
394
|
+
await Promise.race([
|
|
395
|
+
nextStdout.wait(),
|
|
396
|
+
new Promise<void>((resolve) =>
|
|
397
|
+
setTimeout(() => {
|
|
398
|
+
if (!nextStdout.ready) {
|
|
399
|
+
shell.write('\r');
|
|
400
|
+
}
|
|
401
|
+
resolve();
|
|
402
|
+
}, 1000),
|
|
403
|
+
),
|
|
404
|
+
]);
|
|
394
405
|
}
|
|
395
406
|
|
|
396
407
|
async function sendMessage(message: string) {
|
|
397
408
|
await stdinReady.wait();
|
|
398
409
|
// show in-place message: write msg and move cursor back start
|
|
399
|
-
|
|
400
|
-
shell.write(message);
|
|
410
|
+
yesLog`send |${message}`;
|
|
401
411
|
nextStdout.unready();
|
|
412
|
+
shell.write(message);
|
|
402
413
|
idleWaiter.ping(); // just sent a message, wait for echo
|
|
414
|
+
yesLog`waiting next stdout|${message}`;
|
|
403
415
|
await nextStdout.wait();
|
|
404
|
-
|
|
416
|
+
yesLog`sending enter`;
|
|
417
|
+
await sendEnter(1000);
|
|
418
|
+
yesLog`sent enter`;
|
|
405
419
|
}
|
|
406
420
|
|
|
407
421
|
async function exitAgent() {
|
|
408
|
-
|
|
422
|
+
robust = false; // disable robust to avoid auto restart
|
|
423
|
+
|
|
409
424
|
// send exit command to the shell, must sleep a bit to avoid claude treat it as pasted input
|
|
410
425
|
await sendMessage('/exit');
|
|
411
426
|
|
|
@@ -437,11 +452,3 @@ export default async function claudeYes({
|
|
|
437
452
|
}
|
|
438
453
|
|
|
439
454
|
export { removeControlCharacters };
|
|
440
|
-
|
|
441
|
-
function tryCatch<T, R>(fn: () => T, catchFn: (error: unknown) => R): T | R {
|
|
442
|
-
try {
|
|
443
|
-
return fn();
|
|
444
|
-
} catch (error) {
|
|
445
|
-
return catchFn(error);
|
|
446
|
-
}
|
|
447
|
-
}
|