claude-yes 0.0.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/dist/index.js ADDED
@@ -0,0 +1,285 @@
1
+ import { createRequire } from "node:module";
2
+ var __create = Object.create;
3
+ var __getProtoOf = Object.getPrototypeOf;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __toESM = (mod, isNodeMode, target) => {
8
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
9
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
+ for (let key of __getOwnPropNames(mod))
11
+ if (!__hasOwnProp.call(to, key))
12
+ __defProp(to, key, {
13
+ get: () => mod[key],
14
+ enumerable: true
15
+ });
16
+ return to;
17
+ };
18
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
19
+
20
+ // index.ts
21
+ import { fromReadable, fromWritable } from "from-node-stream";
22
+ import { mkdir, writeFile } from "fs/promises";
23
+ import path from "path";
24
+ import DIE from "phpdie";
25
+ import sflow from "sflow";
26
+ import { TerminalTextRender } from "terminal-render";
27
+
28
+ // idleWaiter.ts
29
+ class IdleWaiter {
30
+ lastActivityTime = Date.now();
31
+ checkInterval = 100;
32
+ constructor() {
33
+ this.ping();
34
+ }
35
+ ping() {
36
+ this.lastActivityTime = Date.now();
37
+ return this;
38
+ }
39
+ async wait(ms) {
40
+ while (this.lastActivityTime >= Date.now() - ms)
41
+ await new Promise((resolve) => setTimeout(resolve, this.checkInterval));
42
+ }
43
+ }
44
+
45
+ // ReadyManager.ts
46
+ class ReadyManager {
47
+ isReady = false;
48
+ readyQueue = [];
49
+ wait() {
50
+ return new Promise((resolve) => {
51
+ if (this.isReady)
52
+ return resolve();
53
+ this.readyQueue.push(resolve);
54
+ });
55
+ }
56
+ unready() {
57
+ this.isReady = false;
58
+ }
59
+ ready() {
60
+ this.isReady = true;
61
+ if (!this.readyQueue.length)
62
+ return;
63
+ this.readyQueue.splice(0).map((resolve) => resolve());
64
+ }
65
+ }
66
+
67
+ // removeControlCharacters.ts
68
+ function removeControlCharacters(str) {
69
+ return str.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, "");
70
+ }
71
+
72
+ // index.ts
73
+ var CLI_CONFIGURES = {
74
+ claude: {
75
+ ready: "",
76
+ enter: []
77
+ },
78
+ gemini: {
79
+ ready: "",
80
+ enter: []
81
+ },
82
+ codex: {
83
+ ready: "",
84
+ enter: [],
85
+ ensureArgs: (args) => {
86
+ if (!args.includes("--search"))
87
+ return ["--search", ...args];
88
+ return args;
89
+ }
90
+ },
91
+ copilot: {},
92
+ cursor: {
93
+ binary: "cursor-agent"
94
+ }
95
+ };
96
+ async function claudeYes({
97
+ cli = "claude",
98
+ cliArgs = [],
99
+ prompt,
100
+ continueOnCrash,
101
+ cwd,
102
+ env,
103
+ exitOnIdle,
104
+ logFile,
105
+ removeControlCharactersFromStdout = false,
106
+ verbose = false
107
+ } = {}) {
108
+ const continueArgs = {
109
+ codex: "resume --last".split(" "),
110
+ claude: "--continue".split(" "),
111
+ gemini: []
112
+ };
113
+ process.stdin.setRawMode?.(true);
114
+ let isFatal = false;
115
+ const stdinReady = new ReadyManager;
116
+ const shellOutputStream = new TransformStream;
117
+ const outputWriter = shellOutputStream.writable.getWriter();
118
+ const pty = await import("node-pty").catch(async () => await import("bun-pty")).catch(async () => DIE("Please install node-pty or bun-pty, run this: bun install bun-pty"));
119
+ const getPtyOptions = () => ({
120
+ name: "xterm-color",
121
+ ...getTerminalDimensions(),
122
+ cwd: cwd ?? process.cwd(),
123
+ env: env ?? process.env
124
+ });
125
+ const cliConf = CLI_CONFIGURES[cli] || {};
126
+ cliArgs = cliConf.ensureArgs?.(cliArgs) ?? cliArgs;
127
+ const cliCommand = cliConf?.binary || cli;
128
+ let shell = pty.spawn(cliCommand, cliArgs, getPtyOptions());
129
+ const pendingExitCode = Promise.withResolvers();
130
+ let pendingExitCodeValue = null;
131
+ async function onData(data) {
132
+ await outputWriter.write(data);
133
+ }
134
+ shell.onData(onData);
135
+ shell.onExit(function onExit({ exitCode: exitCode2 }) {
136
+ stdinReady.unready();
137
+ const agentCrashed = exitCode2 !== 0;
138
+ const continueArg = continueArgs[cli];
139
+ if (agentCrashed && continueOnCrash && continueArg) {
140
+ if (!continueArg) {
141
+ return console.warn(`continueOnCrash is only supported for ${Object.keys(continueArgs).join(", ")} currently, not ${cli}`);
142
+ }
143
+ if (isFatal) {
144
+ console.log(`${cli} crashed with "No conversation found to continue", exiting...`);
145
+ return pendingExitCode.resolve(pendingExitCodeValue = exitCode2);
146
+ }
147
+ console.log(`${cli} crashed, restarting...`);
148
+ shell = pty.spawn(cli, continueArg, getPtyOptions());
149
+ shell.onData(onData);
150
+ shell.onExit(onExit);
151
+ return;
152
+ }
153
+ return pendingExitCode.resolve(pendingExitCodeValue = exitCode2);
154
+ });
155
+ process.stdout.on("resize", () => {
156
+ const { cols, rows } = getTerminalDimensions();
157
+ shell.resize(cols, rows);
158
+ });
159
+ const terminalRender = new TerminalTextRender;
160
+ const isStillWorkingQ = () => terminalRender.render().replace(/\s+/g, " ").match(/esc to interrupt|to run in background/);
161
+ const idleWaiter = new IdleWaiter;
162
+ if (exitOnIdle)
163
+ idleWaiter.wait(exitOnIdle).then(async () => {
164
+ if (isStillWorkingQ()) {
165
+ console.log("[${cli}-yes] ${cli} is idle, but seems still working, not exiting yet");
166
+ return;
167
+ }
168
+ console.log("[${cli}-yes] ${cli} is idle, exiting...");
169
+ await exitAgent();
170
+ });
171
+ sflow(fromReadable(process.stdin)).map((buffer) => buffer.toString()).map((e) => e.replaceAll("\x1A", "")).by({
172
+ writable: new WritableStream({
173
+ write: async (data) => {
174
+ await idleWaiter.wait(200);
175
+ shell.write(data);
176
+ }
177
+ }),
178
+ readable: shellOutputStream.readable
179
+ }).forEach(() => idleWaiter.ping()).forEach((text) => terminalRender.write(text)).forEach((txt) => {
180
+ if (process.stdin.isTTY)
181
+ return;
182
+ if (txt.includes("\x1B[6n"))
183
+ return;
184
+ const rendered = terminalRender.render();
185
+ const row = rendered.split(`
186
+ `).length + 1;
187
+ const col = (rendered.split(`
188
+ `).slice(-1)[0]?.length || 0) + 1;
189
+ }).forkTo((e) => e.map((e2) => removeControlCharacters(e2)).map((e2) => e2.replaceAll("\r", "")).lines({ EOL: "NONE" }).forEach(async (e2) => {
190
+ if (cli !== "claude")
191
+ return;
192
+ if (e2.match(/^> /))
193
+ return stdinReady.ready();
194
+ if (e2.match(/❯ 1. Yes/))
195
+ return await sendEnter();
196
+ if (e2.match(/❯ 1. Dark mode✔|Press Enter to continue…/))
197
+ return await sendEnter();
198
+ if (e2.match(/No conversation found to continue/))
199
+ return isFatal = true;
200
+ if (e2.match(/⎿ {2}Claude usage limit reached./))
201
+ return isFatal = true;
202
+ }).forEach(async (e2, i) => {
203
+ if (cli !== "gemini")
204
+ return;
205
+ if (e2.match(/ > {3}Type your message/) && i > 80) {
206
+ return stdinReady.ready();
207
+ }
208
+ if (e2.match(/│ ● 1. Yes, allow once/))
209
+ return await sendEnter();
210
+ }).forEach(async (e2) => {
211
+ if (cli === "codex") {
212
+ if (e2.match(/ > 1. Approve/))
213
+ return await sendEnter();
214
+ if (e2.match(/Error: The cursor position could not be read within/))
215
+ return isFatal = true;
216
+ if (e2.match(/> 1. Yes, allow Codex to work in this folder/))
217
+ return await sendEnter();
218
+ if (e2.match(/⏎ send/))
219
+ return stdinReady.ready();
220
+ return;
221
+ }
222
+ if (cli === "cursor") {
223
+ if (e2.match(/\/ commands/))
224
+ return stdinReady.ready();
225
+ if (e2.match(/→ Run \(once\) \(y\) \(enter\)/))
226
+ return await sendEnter();
227
+ if (e2.match(/▶ \[a\] Trust this workspace/))
228
+ return await sendEnter();
229
+ }
230
+ }).run()).map((e) => removeControlCharactersFromStdout ? removeControlCharacters(e) : e).to(fromWritable(process.stdout)).then(() => null);
231
+ if (prompt)
232
+ (async () => {
233
+ await sendMessage(prompt);
234
+ })();
235
+ const exitCode = await pendingExitCode.promise;
236
+ console.log(`[${cli}-yes] ${cli} exited with code ${exitCode}`);
237
+ if (logFile) {
238
+ verbose && console.log(`[${cli}-yes] Writing rendered logs to ${logFile}`);
239
+ const logFilePath = path.resolve(logFile);
240
+ await mkdir(path.dirname(logFilePath), { recursive: true }).catch(() => null);
241
+ await writeFile(logFilePath, terminalRender.render());
242
+ }
243
+ return { exitCode, logs: terminalRender.render() };
244
+ async function sendEnter(waitms = 1000) {
245
+ const st = Date.now();
246
+ await idleWaiter.wait(waitms);
247
+ const et = Date.now();
248
+ process.stdout.write(`\ridleWaiter.wait(${waitms}) took ${et - st}ms\r`);
249
+ shell.write("\r");
250
+ }
251
+ async function sendMessage(message) {
252
+ await stdinReady.wait();
253
+ shell.write(message);
254
+ idleWaiter.ping();
255
+ await sendEnter();
256
+ }
257
+ async function exitAgent() {
258
+ continueOnCrash = false;
259
+ await sendMessage("/exit");
260
+ let exited = false;
261
+ await Promise.race([
262
+ pendingExitCode.promise.then(() => exited = true),
263
+ new Promise((resolve) => setTimeout(() => {
264
+ if (exited)
265
+ return;
266
+ shell.kill();
267
+ resolve();
268
+ }, 5000))
269
+ ]);
270
+ }
271
+ function getTerminalDimensions() {
272
+ return {
273
+ cols: Math.max(process.stdout.columns, 80),
274
+ rows: process.stdout.rows
275
+ };
276
+ }
277
+ }
278
+ export {
279
+ removeControlCharacters,
280
+ claudeYes as default,
281
+ CLI_CONFIGURES
282
+ };
283
+
284
+ //# debugId=5CB1D5337A468C1E64756E2164756E21
285
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,13 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../index.ts", "../idleWaiter.ts", "../ReadyManager.ts", "../removeControlCharacters.ts"],
4
+ "sourcesContent": [
5
+ "import { fromReadable, fromWritable } from \"from-node-stream\";\nimport { mkdir, writeFile } from \"fs/promises\";\nimport path from \"path\";\nimport DIE from \"phpdie\";\nimport sflow from \"sflow\";\nimport { TerminalTextRender } from \"terminal-render\";\nimport { IdleWaiter } from \"./idleWaiter\";\nimport { ReadyManager } from \"./ReadyManager\";\nimport { removeControlCharacters } from \"./removeControlCharacters\";\n\nexport const CLI_CONFIGURES = {\n claude: {\n ready: '', // regex matcher for stdin ready,\n enter: [\n // regexs\n ]\n },\n gemini: {\n ready: '', // regex matcher for stdin ready,\n enter: [\n // regexs\n ]\n },\n codex: {\n ready: '', // regex matcher for stdin ready,\n enter: [\n//regexs\n ],\n // add to codex --search by default when not provided by the user\n ensureArgs: (args: string[]) => {\n if (!args.includes('--search')) return ['--search', ...args];\n return args;\n },\n },\n copilot: {\n // todo\n },\n cursor: {\n // map logical \"cursor\" cli name to actual binary name\n binary: 'cursor-agent',\n },\n};\n/**\n * Main function to run Claude with automatic yes/no responses\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, recommended 5000 - 60000, default is false\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 *\n * @example\n * ```typescript\n * import claudeYes from 'claude-yes';\n * await claudeYes({\n * prompt: 'help me solve all todos in my codebase',\n *\n * // optional\n * cli: 'claude',\n * cliArgs: ['--verbose'], // additional args to pass to claude\n * exitOnIdle: 30000, // exit after 30 seconds of idle\n * continueOnCrash: true, // restart if claude crashes, default is true\n * logFile: 'claude.log', // save logs to file\n * });\n * ```\n */\nexport default async function claudeYes({\n cli = \"claude\",\n cliArgs = [],\n prompt,\n continueOnCrash,\n cwd,\n env,\n exitOnIdle,\n logFile,\n removeControlCharactersFromStdout = false, // = !process.stdout.isTTY,\n verbose = false,\n}: {\n cli?: (string & {}) | keyof typeof CLI_CONFIGURES;\n cliArgs?: string[];\n prompt?: 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 const continueArgs = {\n codex: \"resume --last\".split(\" \"),\n claude: \"--continue\".split(\" \"),\n gemini: [], // not possible yet\n };\n\n // if (verbose) {\n // console.log('calling claudeYes: ', {\n // cli,\n // continueOnCrash,\n // exitOnIdle,\n // cliArgs,\n // cwd,\n // removeControlCharactersFromStdout,\n // logFile,\n // verbose,\n // });\n // }\n // console.log(\n // `⭐ Starting ${cli}, 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 let isFatal = false; // match 'No conversation found to continue'\n const stdinReady = new ReadyManager();\n\n const shellOutputStream = new TransformStream<string, string>();\n const outputWriter = shellOutputStream.writable.getWriter();\n // const pty = await import('node-pty');\n\n // its recommened to use bun-pty in windows\n const pty = await import(\"node-pty\")\n .catch(async () => await import(\"bun-pty\"))\n .catch(async () =>\n DIE(\"Please install node-pty or bun-pty, run this: bun install bun-pty\")\n );\n\n const getPtyOptions = () => ({\n name: \"xterm-color\",\n ...getTerminalDimensions(),\n cwd: cwd ?? process.cwd(),\n env: env ?? (process.env as Record<string, string>),\n });\n\n // Apply CLI specific configurations (moved to CLI_CONFIGURES)\n const cliConf = (CLI_CONFIGURES as Record<string, any>)[cli] || {};\n cliArgs = cliConf.ensureArgs?.(cliArgs) ?? cliArgs;\n const cliCommand = cliConf?.binary || cli;\n\n let shell = pty.spawn(cliCommand, cliArgs, getPtyOptions());\n const pendingExitCode = Promise.withResolvers<number | null>();\n let pendingExitCodeValue = null;\n\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\n shell.onData(onData);\n shell.onExit(function onExit({ exitCode }) {\n stdinReady.unready(); // start buffer stdin\n const agentCrashed = exitCode !== 0;\n const continueArg = (continueArgs as Record<string, string[]>)[cli];\n\n if (agentCrashed && continueOnCrash && continueArg) {\n if (!continueArg) {\n return console.warn(\n `continueOnCrash is only supported for ${Object.keys(continueArgs).join(\", \")} currently, not ${cli}`\n );\n }\n if (isFatal) {\n console.log(\n `${cli} crashed with \"No conversation found to continue\", exiting...`\n );\n return pendingExitCode.resolve((pendingExitCodeValue = exitCode));\n }\n console.log(`${cli} crashed, restarting...`);\n\n shell = pty.spawn(cli, continueArg, getPtyOptions());\n shell.onData(onData);\n shell.onExit(onExit);\n return;\n }\n return pendingExitCode.resolve((pendingExitCodeValue = exitCode));\n });\n\n // when current tty resized, resize the pty\n process.stdout.on(\"resize\", () => {\n const { cols, rows } = getTerminalDimensions(); // minimum 80 columns to avoid layout issues\n shell.resize(cols, rows); // minimum 80 columns to avoid layout issues\n });\n\n const terminalRender = new TerminalTextRender();\n const isStillWorkingQ = () =>\n terminalRender\n .render()\n .replace(/\\s+/g, \" \")\n .match(/esc to interrupt|to run in background/);\n\n const idleWaiter = new IdleWaiter();\n if (exitOnIdle)\n idleWaiter.wait(exitOnIdle).then(async () => {\n if (isStillWorkingQ()) {\n console.log(\n \"[${cli}-yes] ${cli} is idle, but seems still working, not exiting yet\"\n );\n return;\n }\n\n console.log(\"[${cli}-yes] ${cli} is idle, exiting...\");\n await exitAgent();\n });\n\n // Message streaming\n sflow(fromReadable<Buffer>(process.stdin))\n .map((buffer) => buffer.toString())\n .map((e) => e.replaceAll(\"\\x1a\", \"\")) // remove ctrl+z from user's input\n // .forEach(e => appendFile('.cache/io.log', \"input |\" + JSON.stringify(e) + '\\n')) // for debugging\n // pipe\n .by({\n writable: new WritableStream<string>({\n write: async (data) => {\n // await stdinReady.wait();\n await idleWaiter.wait(200); // wait for idle for 200ms to avoid messing up claude's input\n shell.write(data);\n },\n }),\n readable: shellOutputStream.readable,\n })\n .forEach(() => idleWaiter.ping())\n .forEach((text) => terminalRender.write(text))\n .forEach((txt) => {\n // xterm replies CSI row; column R if asked cursor position\n // https://en.wikipedia.org/wiki/ANSI_escape_code#:~:text=citation%20needed%5D-,xterm%20replies,-CSI%20row%C2%A0%3B\n if (process.stdin.isTTY) return; // only handle it when stdin is not tty\n if (txt.includes(\"\\u001b[6n\")) return; // only asked\n const rendered = terminalRender.render();\n // when asking position, respond with row; col\n const row = rendered.split(\"\\n\").length + 1;\n const col = (rendered.split(\"\\n\").slice(-1)[0]?.length || 0) + 1;\n // shell.write(`\\u001b[${row};${col}R`);\n })\n\n // auto-response\n .forkTo((e) =>\n e\n .map((e) => removeControlCharacters(e))\n .map((e) => e.replaceAll(\"\\r\", \"\")) // remove carriage return\n .lines({ EOL: \"NONE\" })\n .forEach(async (e) => {\n if (cli !== \"claude\") return;\n\n if (e.match(/^> /)) return stdinReady.ready();\n if (e.match(/❯ 1. Yes/)) return await sendEnter();\n if (e.match(/❯ 1. Dark mode✔|Press Enter to continue…/))\n return await sendEnter();\n if (e.match(/No conversation found to continue/))\n return (isFatal = true); // set flag to true if error message is found;\n if (e.match(/⎿ {2}Claude usage limit reached./))\n return (isFatal = true); // set flag to true if error message is found;\n // reached limit, exiting...\n })\n .forEach(async (e, i) => {\n if (cli !== \"gemini\") return;\n if (e.match(/ > {3}Type your message/) && i > 80) {\n // wait until 80 lines to avoid the initial prompt\n return stdinReady.ready();\n }\n if (e.match(/│ ● 1. Yes, allow once/)) return await sendEnter();\n })\n .forEach(async (e) => {\n if (cli === \"codex\") {\n if (e.match(/ > 1. Approve/)) return await sendEnter();\n if (e.match(/Error: The cursor position could not be read within/))\n return (isFatal = true);\n if (e.match(/> 1. Yes, allow Codex to work in this folder/))\n return await sendEnter();\n if (e.match(/⏎ send/)) return stdinReady.ready();\n return;\n }\n if (cli === \"cursor\") {\n if (e.match(/\\/ commands/)) return stdinReady.ready();\n if (e.match(/→ Run \\(once\\) \\(y\\) \\(enter\\)/))\n return await sendEnter();\n if (e.match(/▶ \\[a\\] Trust this workspace/))\n return await sendEnter();\n }\n })\n // .forEach(e => appendFile('.cache/io.log', \"output|\" + JSON.stringify(e) + '\\n')) // for debugging\n .run()\n )\n .map((e) =>\n removeControlCharactersFromStdout ? removeControlCharacters(e) : e\n )\n .to(fromWritable(process.stdout))\n .then(() => null); // run it immediately without await\n\n // wait for cli ready and send prompt if provided\n if (prompt)\n (async () => {\n // console.log(`[${cli}-yes] Ready to send prompt to ${cli}: ${prompt}`);\n // idleWaiter.ping();\n // console.log(\n // 'await idleWaiter.wait(1000); // wait a bit for claude to start'\n // );\n // await idleWaiter.wait(1000); // wait a bit for claude to start\n // console.log('await stdinReady.wait();');\n // await stdinReady.wait();\n // console.log(`[${cli}-yes] Waiting for ${cli} to be ready...`);\n // console.log('await idleWaiter.wait(200);');\n // await idleWaiter.wait(200);\n // console.log(`[${cli}-yes] Sending prompt to ${cli}: ${prompt}`);\n await sendMessage(prompt);\n })();\n\n const exitCode = await pendingExitCode.promise; // wait for the shell to exit\n console.log(`[${cli}-yes] ${cli} exited with code ${exitCode}`);\n\n if (logFile) {\n verbose && console.log(`[${cli}-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, terminalRender.render());\n }\n\n return { exitCode, logs: terminalRender.render() };\n\n async function sendEnter(waitms = 1000) {\n // wait for idle for a bit to let agent cli finish rendering\n const st = Date.now();\n\n await idleWaiter.wait(waitms);\n const et = Date.now();\n process.stdout.write(`\\ridleWaiter.wait(${waitms}) took ${et - st}ms\\r`);\n\n shell.write(\"\\r\");\n }\n\n async function sendMessage(message: string) {\n await stdinReady.wait();\n // show in-place message: write msg and move cursor back start\n shell.write(message);\n idleWaiter.ping(); // just sent a message, wait for echo\n await sendEnter();\n }\n\n async function exitAgent() {\n continueOnCrash = false;\n // send exit command to the shell, must sleep a bit to avoid claude treat it as pasted input\n await sendMessage(\"/exit\");\n\n // wait for shell to exit or kill it with a timeout\n let exited = false;\n await Promise.race([\n pendingExitCode.promise.then(() => (exited = true)), // resolve when shell exits\n\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 function getTerminalDimensions() {\n return {\n cols: Math.max(process.stdout.columns, 80),\n rows: process.stdout.rows,\n };\n }\n}\n\nexport { removeControlCharacters };\n",
6
+ "/**\n * A utility class to wait for idle periods based on activity pings.\n *\n * @example\n * const idleWaiter = new IdleWaiter();\n *\n * // Somewhere in your code, when activity occurs:\n * idleWaiter.ping();\n *\n * // To wait for an idle period of 5 seconds:\n * await idleWaiter.wait(5000);\n * console.log('System has been idle for 5 seconds');\n */\nexport class IdleWaiter {\n lastActivityTime = Date.now();\n checkInterval = 100; // Default check interval in milliseconds\n\n constructor() {\n this.ping();\n }\n\n ping() {\n this.lastActivityTime = Date.now();\n return this;\n }\n\n async wait(ms: number) {\n while (this.lastActivityTime >= Date.now() - ms)\n await new Promise((resolve) => setTimeout(resolve, this.checkInterval));\n }\n}\n",
7
+ "export class ReadyManager {\n private isReady = false;\n private readyQueue: (() => void)[] = [];\n wait() {\n return new Promise<void>((resolve) => {\n if (this.isReady) return resolve();\n this.readyQueue.push(resolve);\n });\n }\n unready() {\n this.isReady = false;\n }\n ready() {\n this.isReady = true;\n if (!this.readyQueue.length) return; // check len for performance\n this.readyQueue.splice(0).map((resolve) => resolve());\n }\n}\n",
8
+ "export function removeControlCharacters(str: string): string {\n // Matches control characters in the C0 and C1 ranges, including Delete (U+007F)\n return str.replace(\n /[\\u001b\\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,\n '',\n );\n}\n"
9
+ ],
10
+ "mappings": "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",
11
+ "debugId": "5CB1D5337A468C1E64756E2164756E21",
12
+ "names": []
13
+ }
package/idleWaiter.ts ADDED
@@ -0,0 +1,31 @@
1
+ /**
2
+ * A utility class to wait for idle periods based on activity pings.
3
+ *
4
+ * @example
5
+ * const idleWaiter = new IdleWaiter();
6
+ *
7
+ * // Somewhere in your code, when activity occurs:
8
+ * idleWaiter.ping();
9
+ *
10
+ * // To wait for an idle period of 5 seconds:
11
+ * await idleWaiter.wait(5000);
12
+ * console.log('System has been idle for 5 seconds');
13
+ */
14
+ export class IdleWaiter {
15
+ lastActivityTime = Date.now();
16
+ checkInterval = 100; // Default check interval in milliseconds
17
+
18
+ constructor() {
19
+ this.ping();
20
+ }
21
+
22
+ ping() {
23
+ this.lastActivityTime = Date.now();
24
+ return this;
25
+ }
26
+
27
+ async wait(ms: number) {
28
+ while (this.lastActivityTime >= Date.now() - ms)
29
+ await new Promise((resolve) => setTimeout(resolve, this.checkInterval));
30
+ }
31
+ }