claude-yes 1.23.1 → 1.23.3

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.map CHANGED
@@ -2,12 +2,12 @@
2
2
  "version": 3,
3
3
  "sources": ["../index.ts", "../idleWaiter.ts", "../ReadyManager.ts", "../removeControlCharacters.ts"],
4
4
  "sourcesContent": [
5
- "import { fromReadable, fromWritable } from 'from-node-stream';\nimport { appendFile, mkdir, rm, writeFile } from 'fs/promises';\nimport path from 'path';\nimport DIE from 'phpdie';\nimport sflow from 'sflow';\nimport { TerminalTextRender } from 'terminal-render';\nimport tsaComposer from 'tsa-composer';\nimport { IdleWaiter } from './idleWaiter';\nimport { ReadyManager } from './ReadyManager';\nimport { removeControlCharacters } from './removeControlCharacters';\n\nexport const CLI_CONFIGURES: Record<\n string,\n {\n install?: string; // hint user for install command if not installed\n binary?: string; // actual binary name if different from cli\n ready?: RegExp[]; // regex matcher for stdin ready, or line index for gemini\n enter?: RegExp[]; // array of regex to match for sending Enter\n fatal?: RegExp[]; // array of regex to match for fatal errors\n ensureArgs?: (args: string[]) => string[]; // function to ensure certain args are present\n }\n> = {\n grok: {\n install: 'npm install -g @vibe-kit/grok-cli',\n ready: [/^ │ ❯ /],\n enter: [/^ 1. Yes/],\n },\n claude: {\n install: 'npm install -g @anthropic-ai/claude-code',\n // ready: [/^> /], // regex matcher for stdin ready\n ready: [/\\? for shortcuts/], // regex matcher for stdin ready\n enter: [/❯ 1. Yes/, /❯ 1. Dark mode✔/, /Press Enter to continue…/],\n fatal: [\n /No conversation found to continue/,\n /⎿ Claude usage limit reached\\./,\n ],\n },\n gemini: {\n install: 'npm install -g @google/gemini-cli',\n // match the agent prompt after initial lines; handled by index logic using line index\n ready: [/Type your message/], // used with line index check\n enter: [/│ ● 1. Yes, allow once/],\n fatal: [],\n },\n codex: {\n install: 'npm install -g @openai/codex-cli',\n ready: [/⏎ send/],\n enter: [\n /> 1. Yes, allow Codex to work in this folder/,\n /▌ > 1. Approve and run now/,\n ],\n fatal: [/Error: The cursor position could not be read within/],\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 install: 'npm install -g @github/copilot',\n ready: [/^ > /],\n enter: [/ │ ❯ 1. Yes, proceed/, /❯ 1. Yes/],\n fatal: [],\n },\n cursor: {\n install: 'open https://cursor.com/ja/docs/cli/installation',\n // map logical \"cursor\" cli name to actual binary name\n binary: 'cursor-agent',\n ready: [/\\/ commands/],\n enter: [/→ Run \\(once\\) \\(y\\) \\(enter\\)/, /▶ \\[a\\] Trust this workspace/],\n fatal: [/^ Error: You've hit your usage limit/],\n },\n};\n/**\n * Main function to run agent-cli with automatic yes/no responses\n * @param options Configuration options\n * @param options.continueOnCrash - If true, automatically restart agent-cli when it crashes:\n * 1. Shows message 'agent-cli crashed, restarting..'\n * 2. Spawns a new 'agent-cli --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 agent-cli is idle. Boolean or timeout in milliseconds, recommended 5000 - 60000, default is false\n * @param options.cliArgs - Additional arguments to pass to the agent-cli 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 await rm('agent-yes.log').catch(() => null); // ignore error if file doesn't exist\n const yesLog = tsaComposer()(async function yesLog(msg: string) {\n await appendFile('agent-yes.log', `${msg}\\n`).catch(() => null);\n });\n const continueArgs = {\n codex: 'resume --last'.split(' '),\n claude: '--continue'.split(' '),\n gemini: [], // not possible yet\n };\n\n process.stdin.setRawMode?.(true); // must be called any stdout/stdin usage\n let isFatal = false; // when true, do not restart on crash, and exit agent\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 = tryCatch(\n () => pty.spawn(cliCommand, cliArgs, getPtyOptions()),\n (error: unknown) => {\n console.error(`Fatal: Failed to start ${cliCommand}.`);\n if (cliConf?.install)\n console.error(\n `If you did not installed it yet, Please install it first: ${cliConf.install}`,\n );\n throw error;\n },\n );\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 return pendingExitCode.resolve((pendingExitCodeValue = exitCode));\n }\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 // console.log(\n // `[${cli}-yes] Started ${cli} with args: ${[cliCommand, ...cliArgs].join(\" \")}`\n // );\n // Message streaming\n\n sflow(fromReadable<Buffer>(process.stdin))\n .map((buffer) => buffer.toString())\n // .map((e) => e.replaceAll('\\x1a', '')) // remove ctrl+z from user's input (seems bug)\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(20); // 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) => {\n terminalRender.write(text);\n // todo: .onStatus((msg)=> shell.write(msg))\n if (process.stdin.isTTY) return; // only handle it when stdin is not tty\n if (!text.includes('\\u001b[6n')) return; // only asked for cursor position\n // todo: use terminalRender API to get cursor position when new version is available\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 // when agent asking position, respond with row; col\n // const rendered = terminalRender.render();\n const { col, row } = terminalRender.getCursorPosition();\n console.log(\n `[${cli}-yes] Responding cursor position: row=${row}, col=${col}`,\n );\n shell.write(`\\u001b[${row};${col}R`); // reply cli when getting cursor position\n // const row = rendered.split('\\n').length + 1;\n // const col = (rendered.split('\\n').slice(-1)[0]?.length || 0) + 1;\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((e) => yesLog`output|${e}`) // for debugging\n // Generic auto-response handler driven by CLI_CONFIGURES\n .forEach(async (e, i) => {\n const conf =\n CLI_CONFIGURES[cli as keyof typeof CLI_CONFIGURES] || null;\n if (!conf) return;\n\n // ready matcher: if matched, mark stdin ready\n if (conf.ready?.some((rx: RegExp) => e.match(rx))) {\n await yesLog`ready |${e}`;\n if (cli === 'gemini' && i <= 80) return; // gemini initial noise, only after many lines\n stdinReady.ready();\n }\n\n // enter matchers: send Enter when any enter regex matches\n if (conf.enter?.some((rx: RegExp) => e.match(rx))) {\n await yesLog`enter |${e}`;\n await sendEnter(300); // send Enter after 300ms idle wait\n return;\n }\n\n // fatal matchers: set isFatal flag when matched\n if (conf.fatal?.some((rx: RegExp) => e.match(rx))) {\n await yesLog`fatal |${e}`;\n isFatal = true;\n await exitAgent();\n }\n })\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 (cli === 'codex') shell.write(`\\u001b[1;1R`); // send cursor position response when stdin is not tty\n if (prompt) await sendMessage(prompt);\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 await idleWaiter.wait(waitms);\n const et = Date.now();\n // process.stdout.write(`\\ridleWaiter.wait(${waitms}) took ${et - st}ms\\r`);\n await yesLog`sendEn| idleWaiter.wait(${String(waitms)}) took ${String(et - st)}ms`;\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 await yesLog`send |${message}`;\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 if (!process.stdout.isTTY) return { cols: 80, rows: 30 }; // default size when not tty\n return {\n // TODO: enforce minimum cols/rows to avoid layout issues\n // cols: Math.max(process.stdout.columns, 80),\n cols: Math.min(Math.max(20, process.stdout.columns), 80),\n rows: process.stdout.rows,\n };\n }\n}\n\nexport { removeControlCharacters };\n\nfunction tryCatch<T, R>(fn: () => T, catchFn: (error: unknown) => R): T | R {\n try {\n return fn();\n } catch (error) {\n return catchFn(error);\n }\n}\n",
5
+ "import { fromReadable, fromWritable } from 'from-node-stream';\nimport { appendFile, mkdir, rm, writeFile } from 'fs/promises';\nimport path from 'path';\nimport DIE from 'phpdie';\nimport sflow from 'sflow';\nimport { TerminalTextRender } from 'terminal-render';\nimport tsaComposer from 'tsa-composer';\nimport { IdleWaiter } from './idleWaiter';\nimport { ReadyManager } from './ReadyManager';\nimport { removeControlCharacters } from './removeControlCharacters';\n\n// const yesLog = tsaComposer()(async function yesLog(msg: string) {\n// // await rm('agent-yes.log').catch(() => null); // ignore error if file doesn't exist\n// await appendFile(\"agent-yes.log\", `${msg}\\n`).catch(() => null);\n// });\n\nexport const CLI_CONFIGURES: Record<\n string,\n {\n install?: string; // hint user for install command if not installed\n binary?: string; // actual binary name if different from cli\n ready?: RegExp[]; // regex matcher for stdin ready, or line index for gemini\n enter?: RegExp[]; // array of regex to match for sending Enter\n fatal?: RegExp[]; // array of regex to match for fatal errors\n ensureArgs?: (args: string[]) => string[]; // function to ensure certain args are present\n }\n> = {\n grok: {\n install: 'npm install -g @vibe-kit/grok-cli',\n ready: [/^ │ ❯ /],\n enter: [/^ 1. Yes/],\n },\n claude: {\n install: 'npm install -g @anthropic-ai/claude-code',\n // ready: [/^> /], // regex matcher for stdin ready\n ready: [/\\? for shortcuts/], // regex matcher for stdin ready\n enter: [/❯ 1. Yes/, /❯ 1. Dark mode✔/, /Press Enter to continue…/],\n fatal: [\n /No conversation found to continue/,\n /⎿ Claude usage limit reached\\./,\n ],\n },\n gemini: {\n install: 'npm install -g @google/gemini-cli',\n // match the agent prompt after initial lines; handled by index logic using line index\n ready: [/Type your message/], // used with line index check\n enter: [/│ ● 1. Yes, allow once/],\n fatal: [],\n },\n codex: {\n install: 'npm install -g @openai/codex-cli',\n ready: [/⏎ send/],\n enter: [\n /> 1. Yes, allow Codex to work in this folder/,\n /> 1. Approve and run now/,\n ],\n fatal: [/Error: The cursor position could not be read within/],\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 install: 'npm install -g @github/copilot',\n ready: [/^ > /],\n enter: [/ │ ❯ 1. Yes, proceed/, /❯ 1. Yes/],\n fatal: [],\n },\n cursor: {\n install: 'open https://cursor.com/ja/docs/cli/installation',\n // map logical \"cursor\" cli name to actual binary name\n binary: 'cursor-agent',\n ready: [/\\/ commands/],\n enter: [/→ Run \\(once\\) \\(y\\) \\(enter\\)/, /▶ \\[a\\] Trust this workspace/],\n fatal: [/^ Error: You've hit your usage limit/],\n },\n};\n/**\n * Main function to run agent-cli with automatic yes/no responses\n * @param options Configuration options\n * @param options.continueOnCrash - If true, automatically restart agent-cli when it crashes:\n * 1. Shows message 'agent-cli crashed, restarting..'\n * 2. Spawns a new 'agent-cli --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 agent-cli is idle. Boolean or timeout in milliseconds, recommended 5000 - 60000, default is false\n * @param options.cliArgs - Additional arguments to pass to the agent-cli 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 process.stdin.setRawMode?.(true); // must be called any stdout/stdin usage\n let isFatal = false; // when true, do not restart on crash, and exit agent\n const stdinReady = new ReadyManager();\n const nextStdout = 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 = tryCatch(\n () => pty.spawn(cliCommand, cliArgs, getPtyOptions()),\n (error: unknown) => {\n console.error(`Fatal: Failed to start ${cliCommand}.`);\n if (cliConf?.install)\n console.error(\n `If you did not installed it yet, Please install it first: ${cliConf.install}`,\n );\n throw error;\n },\n );\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 nextStdout.ready();\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 nextStdout.ready();\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 return pendingExitCode.resolve((pendingExitCodeValue = exitCode));\n }\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 // console.log(\n // `[${cli}-yes] Started ${cli} with args: ${[cliCommand, ...cliArgs].join(\" \")}`\n // );\n // Message streaming\n\n sflow(fromReadable<Buffer>(process.stdin))\n .map((buffer) => buffer.toString())\n // .map((e) => e.replaceAll('\\x1a', '')) // remove ctrl+z from user's input (seems bug)\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(20); // 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) => {\n terminalRender.write(text);\n // todo: .onStatus((msg)=> shell.write(msg))\n if (process.stdin.isTTY) return; // only handle it when stdin is not tty\n if (!text.includes('\\u001b[6n')) return; // only asked for cursor position\n // todo: use terminalRender API to get cursor position when new version is available\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 // when agent asking position, respond with row; col\n // const rendered = terminalRender.render();\n const { col, row } = terminalRender.getCursorPosition();\n shell.write(`\\u001b[${row};${col}R`); // reply cli when getting cursor position\n // await yesLog(`cursor|respond position: row=${row}, col=${col}`);\n // const row = rendered.split('\\n').length + 1;\n // const col = (rendered.split('\\n').slice(-1)[0]?.length || 0) + 1;\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 .by((s) => {\n if (cli === 'codex') 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\n return s.lines({ EOL: 'NONE' }); // other clis use ink, which is rerendering the block based on \\n lines\n })\n // .forEach((e) => yesLog`output|${e}`) // for debugging\n // Generic auto-response handler driven by CLI_CONFIGURES\n .forEach(async (e, i) => {\n const conf =\n CLI_CONFIGURES[cli as keyof typeof CLI_CONFIGURES] || null;\n if (!conf) return;\n\n // ready matcher: if matched, mark stdin ready\n if (conf.ready?.some((rx: RegExp) => e.match(rx))) {\n // await yesLog`ready |${e}`;\n if (cli === 'gemini' && i <= 80) return; // gemini initial noise, only after many lines\n stdinReady.ready();\n }\n\n // enter matchers: send Enter when any enter regex matches\n if (conf.enter?.some((rx: RegExp) => e.match(rx))) {\n // await yesLog`enter |${e}`;\n await sendEnter(300); // send Enter after 300ms idle wait\n return;\n }\n\n // fatal matchers: set isFatal flag when matched\n if (conf.fatal?.some((rx: RegExp) => e.match(rx))) {\n // await yesLog`fatal |${e}`;\n isFatal = true;\n await exitAgent();\n }\n })\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 (cli === 'codex') shell.write(`\\u001b[1;1R`); // send cursor position response when stdin is not tty\n if (prompt) await sendMessage(prompt);\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 await idleWaiter.wait(waitms);\n const et = Date.now();\n // process.stdout.write(`\\ridleWaiter.wait(${waitms}) took ${et - st}ms\\r`);\n // await yesLog`sendEn| idleWaiter.wait(${String(waitms)}) took ${String(et - st)}ms`;\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 // await yesLog`send |${message}`;\n shell.write(message);\n nextStdout.unready();\n idleWaiter.ping(); // just sent a message, wait for echo\n await nextStdout.wait();\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 if (!process.stdout.isTTY) return { cols: 80, rows: 30 }; // default size when not tty\n return {\n // TODO: enforce minimum cols/rows to avoid layout issues\n // cols: Math.max(process.stdout.columns, 80),\n cols: Math.min(Math.max(20, process.stdout.columns), 80),\n rows: process.stdout.rows,\n };\n }\n}\n\nexport { removeControlCharacters };\n\nfunction tryCatch<T, R>(fn: () => T, catchFn: (error: unknown) => R): T | R {\n try {\n return fn();\n } catch (error) {\n return catchFn(error);\n }\n}\n",
6
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
7
  "export class ReadyManager {\n private isReady = false;\n private readyQueue: (() => void)[] = [];\n wait() {\n if (this.isReady) return;\n return new Promise<void>((resolve) => this.readyQueue.push(resolve));\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
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
9
  ],
10
- "mappings": "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",
11
- "debugId": "D97240F2071C1DE564756E2164756E21",
10
+ "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;;;ACQO,MAAM,WAAW;AAAA,EACtB,mBAAmB,KAAK,IAAI;AAAA,EAC5B,gBAAgB;AAAA,EAEhB,WAAW,GAAG;AAAA,IACZ,KAAK,KAAK;AAAA;AAAA,EAGZ,IAAI,GAAG;AAAA,IACL,KAAK,mBAAmB,KAAK,IAAI;AAAA,IACjC,OAAO;AAAA;AAAA,OAGH,KAAI,CAAC,IAAY;AAAA,IACrB,OAAO,KAAK,oBAAoB,KAAK,IAAI,IAAI;AAAA,MAC3C,MAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,KAAK,aAAa,CAAC;AAAA;AAE5E;;;AC9BO,MAAM,aAAa;AAAA,EAChB,UAAU;AAAA,EACV,aAA6B,CAAC;AAAA,EACtC,IAAI,GAAG;AAAA,IACL,IAAI,KAAK;AAAA,MAAS;AAAA,IAClB,OAAO,IAAI,QAAc,CAAC,YAAY,KAAK,WAAW,KAAK,OAAO,CAAC;AAAA;AAAA,EAErE,OAAO,GAAG;AAAA,IACR,KAAK,UAAU;AAAA;AAAA,EAEjB,KAAK,GAAG;AAAA,IACN,KAAK,UAAU;AAAA,IACf,IAAI,CAAC,KAAK,WAAW;AAAA,MAAQ;AAAA,IAC7B,KAAK,WAAW,OAAO,CAAC,EAAE,IAAI,CAAC,YAAY,QAAQ,CAAC;AAAA;AAExD;;;ACfO,SAAS,uBAAuB,CAAC,KAAqB;AAAA,EAE3D,OAAO,IAAI,QACT,+EACA,EACF;AAAA;;;AHWK,IAAM,iBAUT;AAAA,EACF,MAAM;AAAA,IACJ,SAAS;AAAA,IACT,OAAO,CAAC,SAAQ;AAAA,IAChB,OAAO,CAAC,YAAY;AAAA,EACtB;AAAA,EACA,QAAQ;AAAA,IACN,SAAS;AAAA,IAET,OAAO,CAAC,kBAAkB;AAAA,IAC1B,OAAO,CAAC,YAAW,mBAAmB,0BAA0B;AAAA,IAChE,OAAO;AAAA,MACL;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN,SAAS;AAAA,IAET,OAAO,CAAC,mBAAmB;AAAA,IAC3B,OAAO,CAAC,wBAAuB;AAAA,IAC/B,OAAO,CAAC;AAAA,EACV;AAAA,EACA,OAAO;AAAA,IACL,SAAS;AAAA,IACT,OAAO,CAAC,QAAO;AAAA,IACf,OAAO;AAAA,MACL;AAAA,MACA;AAAA,IACF;AAAA,IACA,OAAO,CAAC,qDAAqD;AAAA,IAE7D,YAAY,CAAC,SAAmB;AAAA,MAC9B,IAAI,CAAC,KAAK,SAAS,UAAU;AAAA,QAAG,OAAO,CAAC,YAAY,GAAG,IAAI;AAAA,MAC3D,OAAO;AAAA;AAAA,EAEX;AAAA,EACA,SAAS;AAAA,IACP,SAAS;AAAA,IACT,OAAO,CAAC,OAAO;AAAA,IACf,OAAO,CAAC,wBAAuB,UAAU;AAAA,IACzC,OAAO,CAAC;AAAA,EACV;AAAA,EACA,QAAQ;AAAA,IACN,SAAS;AAAA,IAET,QAAQ;AAAA,IACR,OAAO,CAAC,aAAa;AAAA,IACrB,OAAO,CAAC,kCAAiC,8BAA8B;AAAA,IACvE,OAAO,CAAC,uCAAuC;AAAA,EACjD;AACF;AA4BA,eAA8B,SAAS;AAAA,EACrC,MAAM;AAAA,EACN,UAAU,CAAC;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,oCAAoC;AAAA,EACpC,UAAU;AAAA,IAYR,CAAC,GAAG;AAAA,EACN,MAAM,eAAe;AAAA,IACnB,OAAO,gBAAgB,MAAM,GAAG;AAAA,IAChC,QAAQ,aAAa,MAAM,GAAG;AAAA,IAC9B,QAAQ,CAAC;AAAA,EACX;AAAA,EAEA,QAAQ,MAAM,aAAa,IAAI;AAAA,EAC/B,IAAI,UAAU;AAAA,EACd,MAAM,aAAa,IAAI;AAAA,EACvB,MAAM,aAAa,IAAI;AAAA,EAEvB,MAAM,oBAAoB,IAAI;AAAA,EAC9B,MAAM,eAAe,kBAAkB,SAAS,UAAU;AAAA,EAI1D,MAAM,MAAM,MAAa,mBACtB,MAAM,YAAY,MAAa,iBAAU,EACzC,MAAM,YACL,IAAI,mEAAmE,CACzE;AAAA,EAEF,MAAM,gBAAgB,OAAO;AAAA,IAC3B,MAAM;AAAA,OACH,sBAAsB;AAAA,IACzB,KAAK,OAAO,QAAQ,IAAI;AAAA,IACxB,KAAK,OAAQ,QAAQ;AAAA,EACvB;AAAA,EAGA,MAAM,UAAW,eAAuC,QAAQ,CAAC;AAAA,EACjE,UAAU,QAAQ,aAAa,OAAO,KAAK;AAAA,EAC3C,MAAM,aAAa,SAAS,UAAU;AAAA,EAEtC,IAAI,QAAQ,SACV,MAAM,IAAI,MAAM,YAAY,SAAS,cAAc,CAAC,GACpD,CAAC,UAAmB;AAAA,IAClB,QAAQ,MAAM,0BAA0B,aAAa;AAAA,IACrD,IAAI,SAAS;AAAA,MACX,QAAQ,MACN,6DAA6D,QAAQ,SACvE;AAAA,IACF,MAAM;AAAA,GAEV;AAAA,EACA,MAAM,kBAAkB,QAAQ,cAA6B;AAAA,EAC7D,IAAI,uBAAuB;AAAA,EAK3B,eAAe,MAAM,CAAC,MAAc;AAAA,IAClC,WAAW,MAAM;AAAA,IAEjB,MAAM,aAAa,MAAM,IAAI;AAAA;AAAA,EAG/B,MAAM,OAAO,MAAM;AAAA,EACnB,MAAM,OAAO,SAAS,MAAM,GAAG,uBAAY;AAAA,IACzC,WAAW,MAAM;AAAA,IACjB,WAAW,QAAQ;AAAA,IACnB,MAAM,eAAe,cAAa;AAAA,IAClC,MAAM,cAAe,aAA0C;AAAA,IAE/D,IAAI,gBAAgB,mBAAmB,aAAa;AAAA,MAClD,IAAI,CAAC,aAAa;AAAA,QAChB,OAAO,QAAQ,KACb,yCAAyC,OAAO,KAAK,YAAY,EAAE,KAAK,IAAI,oBAAoB,KAClG;AAAA,MACF;AAAA,MACA,IAAI,SAAS;AAAA,QACX,OAAO,gBAAgB,QAAS,uBAAuB,SAAS;AAAA,MAClE;AAAA,MAEA,QAAQ,IAAI,GAAG,4BAA4B;AAAA,MAE3C,QAAQ,IAAI,MAAM,KAAK,aAAa,cAAc,CAAC;AAAA,MACnD,MAAM,OAAO,MAAM;AAAA,MACnB,MAAM,OAAO,MAAM;AAAA,MACnB;AAAA,IACF;AAAA,IACA,OAAO,gBAAgB,QAAS,uBAAuB,SAAS;AAAA,GACjE;AAAA,EAGD,QAAQ,OAAO,GAAG,UAAU,MAAM;AAAA,IAChC,QAAQ,MAAM,SAAS,sBAAsB;AAAA,IAC7C,MAAM,OAAO,MAAM,IAAI;AAAA,GACxB;AAAA,EAED,MAAM,iBAAiB,IAAI;AAAA,EAC3B,MAAM,kBAAkB,MACtB,eACG,OAAO,EACP,QAAQ,QAAQ,GAAG,EACnB,MAAM,uCAAuC;AAAA,EAElD,MAAM,aAAa,IAAI;AAAA,EACvB,IAAI;AAAA,IACF,WAAW,KAAK,UAAU,EAAE,KAAK,YAAY;AAAA,MAC3C,IAAI,gBAAgB,GAAG;AAAA,QACrB,QAAQ,IACN,uEACF;AAAA,QACA;AAAA,MACF;AAAA,MAEA,QAAQ,IAAI,yCAAyC;AAAA,MACrD,MAAM,UAAU;AAAA,KACjB;AAAA,EAOH,MAAM,aAAqB,QAAQ,KAAK,CAAC,EACtC,IAAI,CAAC,WAAW,OAAO,SAAS,CAAC,EAIjC,GAAG;AAAA,IACF,UAAU,IAAI,eAAuB;AAAA,MACnC,OAAO,OAAO,SAAS;AAAA,QACrB,MAAM,WAAW,KAAK;AAAA,QAEtB,MAAM,MAAM,IAAI;AAAA;AAAA,IAEpB,CAAC;AAAA,IACD,UAAU,kBAAkB;AAAA,EAC9B,CAAC,EACA,QAAQ,MAAM,WAAW,KAAK,CAAC,EAC/B,QAAQ,CAAC,SAAS;AAAA,IACjB,eAAe,MAAM,IAAI;AAAA,IAEzB,IAAI,QAAQ,MAAM;AAAA,MAAO;AAAA,IACzB,IAAI,CAAC,KAAK,SAAS,SAAW;AAAA,MAAG;AAAA,IAMjC,QAAQ,KAAK,QAAQ,eAAe,kBAAkB;AAAA,IACtD,MAAM,MAAM,QAAU,OAAO,MAAM;AAAA,GAIpC,EAGA,OAAO,CAAC,MACP,EACG,IAAI,CAAC,OAAM,wBAAwB,EAAC,CAAC,EACrC,IAAI,CAAC,OAAM,GAAE,WAAW,MAAM,EAAE,CAAC,EACjC,GAAG,CAAC,MAAM;AAAA,IACT,IAAI,QAAQ;AAAA,MAAS,OAAO;AAAA,IAC5B,OAAO,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC;AAAA,GAC/B,EAGA,QAAQ,OAAO,IAAG,MAAM;AAAA,IACvB,MAAM,OACJ,eAAe,QAAuC;AAAA,IACxD,IAAI,CAAC;AAAA,MAAM;AAAA,IAGX,IAAI,KAAK,OAAO,KAAK,CAAC,OAAe,GAAE,MAAM,EAAE,CAAC,GAAG;AAAA,MAEjD,IAAI,QAAQ,YAAY,KAAK;AAAA,QAAI;AAAA,MACjC,WAAW,MAAM;AAAA,IACnB;AAAA,IAGA,IAAI,KAAK,OAAO,KAAK,CAAC,OAAe,GAAE,MAAM,EAAE,CAAC,GAAG;AAAA,MAEjD,MAAM,UAAU,GAAG;AAAA,MACnB;AAAA,IACF;AAAA,IAGA,IAAI,KAAK,OAAO,KAAK,CAAC,OAAe,GAAE,MAAM,EAAE,CAAC,GAAG;AAAA,MAEjD,UAAU;AAAA,MACV,MAAM,UAAU;AAAA,IAClB;AAAA,GACD,EACA,IAAI,CACT,EACC,IAAI,CAAC,MACJ,oCAAoC,wBAAwB,CAAC,IAAI,CACnE,EACC,GAAG,aAAa,QAAQ,MAAM,CAAC,EAC/B,KAAK,MAAM,IAAI;AAAA,EAGlB,IAAI,QAAQ;AAAA,IAAS,MAAM,MAAM,WAAa;AAAA,EAC9C,IAAI;AAAA,IAAQ,MAAM,YAAY,MAAM;AAAA,EAEpC,MAAM,WAAW,MAAM,gBAAgB;AAAA,EACvC,QAAQ,IAAI,IAAI,YAAY,wBAAwB,UAAU;AAAA,EAE9D,IAAI,SAAS;AAAA,IACX,WAAW,QAAQ,IAAI,IAAI,qCAAqC,SAAS;AAAA,IACzE,MAAM,cAAc,KAAK,QAAQ,OAAO;AAAA,IACxC,MAAM,MAAM,KAAK,QAAQ,WAAW,GAAG,EAAE,WAAW,KAAK,CAAC,EAAE,MAC1D,MAAM,IACR;AAAA,IACA,MAAM,UAAU,aAAa,eAAe,OAAO,CAAC;AAAA,EACtD;AAAA,EAEA,OAAO,EAAE,UAAU,MAAM,eAAe,OAAO,EAAE;AAAA,EAEjD,eAAe,SAAS,CAAC,SAAS,MAAM;AAAA,IAEtC,MAAM,KAAK,KAAK,IAAI;AAAA,IACpB,MAAM,WAAW,KAAK,MAAM;AAAA,IAC5B,MAAM,KAAK,KAAK,IAAI;AAAA,IAIpB,MAAM,MAAM,IAAI;AAAA;AAAA,EAGlB,eAAe,WAAW,CAAC,SAAiB;AAAA,IAC1C,MAAM,WAAW,KAAK;AAAA,IAGtB,MAAM,MAAM,OAAO;AAAA,IACnB,WAAW,QAAQ;AAAA,IACnB,WAAW,KAAK;AAAA,IAChB,MAAM,WAAW,KAAK;AAAA,IACtB,MAAM,UAAU;AAAA;AAAA,EAGlB,eAAe,SAAS,GAAG;AAAA,IACzB,kBAAkB;AAAA,IAElB,MAAM,YAAY,OAAO;AAAA,IAGzB,IAAI,SAAS;AAAA,IACb,MAAM,QAAQ,KAAK;AAAA,MACjB,gBAAgB,QAAQ,KAAK,MAAO,SAAS,IAAK;AAAA,MAGlD,IAAI,QAAc,CAAC,YACjB,WAAW,MAAM;AAAA,QACf,IAAI;AAAA,UAAQ;AAAA,QACZ,MAAM,KAAK;AAAA,QACX,QAAQ;AAAA,SACP,IAAI,CACT;AAAA,IACF,CAAC;AAAA;AAAA,EAGH,SAAS,qBAAqB,GAAG;AAAA,IAC/B,IAAI,CAAC,QAAQ,OAAO;AAAA,MAAO,OAAO,EAAE,MAAM,IAAI,MAAM,GAAG;AAAA,IACvD,OAAO;AAAA,MAGL,MAAM,KAAK,IAAI,KAAK,IAAI,IAAI,QAAQ,OAAO,OAAO,GAAG,EAAE;AAAA,MACvD,MAAM,QAAQ,OAAO;AAAA,IACvB;AAAA;AAAA;AAMJ,SAAS,QAAc,CAAC,IAAa,SAAuC;AAAA,EAC1E,IAAI;AAAA,IACF,OAAO,GAAG;AAAA,IACV,OAAO,OAAO;AAAA,IACd,OAAO,QAAQ,KAAK;AAAA;AAAA;",
11
+ "debugId": "D43B551F287C778F64756E2164756E21",
12
12
  "names": []
13
13
  }
package/index.ts CHANGED
@@ -9,6 +9,11 @@ import { IdleWaiter } from './idleWaiter';
9
9
  import { ReadyManager } from './ReadyManager';
10
10
  import { removeControlCharacters } from './removeControlCharacters';
11
11
 
12
+ // const yesLog = tsaComposer()(async function yesLog(msg: string) {
13
+ // // await rm('agent-yes.log').catch(() => null); // ignore error if file doesn't exist
14
+ // await appendFile("agent-yes.log", `${msg}\n`).catch(() => null);
15
+ // });
16
+
12
17
  export const CLI_CONFIGURES: Record<
13
18
  string,
14
19
  {
@@ -47,7 +52,7 @@ export const CLI_CONFIGURES: Record<
47
52
  ready: [/⏎ send/],
48
53
  enter: [
49
54
  /> 1. Yes, allow Codex to work in this folder/,
50
- /▌ > 1. Approve and run now/,
55
+ /> 1. Approve and run now/,
51
56
  ],
52
57
  fatal: [/Error: The cursor position could not be read within/],
53
58
  // add to codex --search by default when not provided by the user
@@ -121,10 +126,6 @@ export default async function claudeYes({
121
126
  removeControlCharactersFromStdout?: boolean;
122
127
  verbose?: boolean;
123
128
  } = {}) {
124
- await rm('agent-yes.log').catch(() => null); // ignore error if file doesn't exist
125
- const yesLog = tsaComposer()(async function yesLog(msg: string) {
126
- await appendFile('agent-yes.log', `${msg}\n`).catch(() => null);
127
- });
128
129
  const continueArgs = {
129
130
  codex: 'resume --last'.split(' '),
130
131
  claude: '--continue'.split(' '),
@@ -134,6 +135,7 @@ export default async function claudeYes({
134
135
  process.stdin.setRawMode?.(true); // must be called any stdout/stdin usage
135
136
  let isFatal = false; // when true, do not restart on crash, and exit agent
136
137
  const stdinReady = new ReadyManager();
138
+ const nextStdout = new ReadyManager();
137
139
 
138
140
  const shellOutputStream = new TransformStream<string, string>();
139
141
  const outputWriter = shellOutputStream.writable.getWriter();
@@ -176,12 +178,14 @@ export default async function claudeYes({
176
178
  // npm install -g @anthropic-ai/claude-code
177
179
 
178
180
  async function onData(data: string) {
181
+ nextStdout.ready();
179
182
  // append data to the buffer, so we can process it later
180
183
  await outputWriter.write(data);
181
184
  }
182
185
 
183
186
  shell.onData(onData);
184
187
  shell.onExit(function onExit({ exitCode }) {
188
+ nextStdout.ready();
185
189
  stdinReady.unready(); // start buffer stdin
186
190
  const agentCrashed = exitCode !== 0;
187
191
  const continueArg = (continueArgs as Record<string, string[]>)[cli];
@@ -265,10 +269,8 @@ export default async function claudeYes({
265
269
  // when agent asking position, respond with row; col
266
270
  // const rendered = terminalRender.render();
267
271
  const { col, row } = terminalRender.getCursorPosition();
268
- console.log(
269
- `[${cli}-yes] Responding cursor position: row=${row}, col=${col}`,
270
- );
271
272
  shell.write(`\u001b[${row};${col}R`); // reply cli when getting cursor position
273
+ // await yesLog(`cursor|respond position: row=${row}, col=${col}`);
272
274
  // const row = rendered.split('\n').length + 1;
273
275
  // const col = (rendered.split('\n').slice(-1)[0]?.length || 0) + 1;
274
276
  })
@@ -278,8 +280,11 @@ export default async function claudeYes({
278
280
  e
279
281
  .map((e) => removeControlCharacters(e))
280
282
  .map((e) => e.replaceAll('\r', '')) // remove carriage return
281
- .lines({ EOL: 'NONE' })
282
- .forEach((e) => yesLog`output|${e}`) // for debugging
283
+ .by((s) => {
284
+ if (cli === 'codex') 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
285
+ return s.lines({ EOL: 'NONE' }); // other clis use ink, which is rerendering the block based on \n lines
286
+ })
287
+ // .forEach((e) => yesLog`output|${e}`) // for debugging
283
288
  // Generic auto-response handler driven by CLI_CONFIGURES
284
289
  .forEach(async (e, i) => {
285
290
  const conf =
@@ -288,21 +293,21 @@ export default async function claudeYes({
288
293
 
289
294
  // ready matcher: if matched, mark stdin ready
290
295
  if (conf.ready?.some((rx: RegExp) => e.match(rx))) {
291
- await yesLog`ready |${e}`;
296
+ // await yesLog`ready |${e}`;
292
297
  if (cli === 'gemini' && i <= 80) return; // gemini initial noise, only after many lines
293
298
  stdinReady.ready();
294
299
  }
295
300
 
296
301
  // enter matchers: send Enter when any enter regex matches
297
302
  if (conf.enter?.some((rx: RegExp) => e.match(rx))) {
298
- await yesLog`enter |${e}`;
303
+ // await yesLog`enter |${e}`;
299
304
  await sendEnter(300); // send Enter after 300ms idle wait
300
305
  return;
301
306
  }
302
307
 
303
308
  // fatal matchers: set isFatal flag when matched
304
309
  if (conf.fatal?.some((rx: RegExp) => e.match(rx))) {
305
- await yesLog`fatal |${e}`;
310
+ // await yesLog`fatal |${e}`;
306
311
  isFatal = true;
307
312
  await exitAgent();
308
313
  }
@@ -339,7 +344,7 @@ export default async function claudeYes({
339
344
  await idleWaiter.wait(waitms);
340
345
  const et = Date.now();
341
346
  // process.stdout.write(`\ridleWaiter.wait(${waitms}) took ${et - st}ms\r`);
342
- await yesLog`sendEn| idleWaiter.wait(${String(waitms)}) took ${String(et - st)}ms`;
347
+ // await yesLog`sendEn| idleWaiter.wait(${String(waitms)}) took ${String(et - st)}ms`;
343
348
 
344
349
  shell.write('\r');
345
350
  }
@@ -347,9 +352,11 @@ export default async function claudeYes({
347
352
  async function sendMessage(message: string) {
348
353
  await stdinReady.wait();
349
354
  // show in-place message: write msg and move cursor back start
350
- await yesLog`send |${message}`;
355
+ // await yesLog`send |${message}`;
351
356
  shell.write(message);
357
+ nextStdout.unready();
352
358
  idleWaiter.ping(); // just sent a message, wait for echo
359
+ await nextStdout.wait();
353
360
  await sendEnter();
354
361
  }
355
362
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-yes",
3
- "version": "1.23.1",
3
+ "version": "1.23.3",
4
4
  "description": "A wrapper tool that automates interactions with various AI CLI tools by automatically handling common prompts and responses.",
5
5
  "keywords": [
6
6
  "claude",