claude-yes 1.17.1 → 1.19.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 CHANGED
@@ -1,25 +1,51 @@
1
1
  # Yes! Claude
2
2
 
3
- A wrapper tool that automates interactions with the Claude CLI by automatically handling common prompts and responses.
3
+ A wrapper tool that automates interactions with various AI CLI tools by automatically handling common prompts and responses. Originally designed for Claude CLI, now supports multiple AI coding assistants.
4
4
 
5
5
  ⚠️ **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.
6
6
 
7
7
  ## Features
8
8
 
9
- - Same as `claude` command
10
- - Automatically responds to common prompts like "Yes, proceed" and "Yes"
11
- - So, this will Let claude keep run until your task done, and wait for your next prompt.
12
- - You can still Queue More Prompts or Cancel executing task by `ESC` or `Ctrl+C`
9
+ - **Multi-CLI Support**: Works with Claude, Gemini, Codex, Copilot, and Cursor CLI tools
10
+ - **Auto-Response**: Automatically responds to common prompts like "Yes, proceed" and "Yes"
11
+ - **Continuous Operation**: Keeps the AI assistant running until your task is done, waiting for your next prompt
12
+ - **Interactive Control**: You can still queue more prompts or cancel executing tasks with `ESC` or `Ctrl+C`
13
+ - **Crash Recovery**: Automatically restarts crashed processes (where supported)
14
+ - **Idle Detection**: Optional auto-exit when the AI becomes idle
13
15
 
14
- ## Prerequirements
16
+ ## Prerequisites
15
17
 
16
- First, install Claude Code globally:
18
+ Install the AI CLI tool(s) you want to use:
17
19
 
20
+ ### Claude
18
21
  ```bash
19
22
  npm install -g @anthropic-ai/claude-code
20
23
  ```
24
+ Learn more: https://www.anthropic.com/claude-code
21
25
 
22
- Learn more about Claude Code: https://www.anthropic.com/claude-code
26
+ ### Gemini
27
+ ```bash
28
+ # Install Gemini CLI (if available)
29
+ # Check Google's documentation for installation instructions
30
+ ```
31
+
32
+ ### Codex
33
+ ```bash
34
+ # Install Codex CLI (if available)
35
+ # Check Microsoft's documentation for installation instructions
36
+ ```
37
+
38
+ ### GitHub Copilot
39
+ ```bash
40
+ # Install GitHub Copilot CLI
41
+ # Check GitHub's documentation for installation instructions
42
+ ```
43
+
44
+ ### Cursor
45
+ ```bash
46
+ # Install Cursor agent CLI
47
+ # Check Cursor's documentation for installation instructions
48
+ ```
23
49
 
24
50
  Then install this project:
25
51
 
@@ -29,49 +55,117 @@ npm install claude-yes -g
29
55
 
30
56
  ## Usage
31
57
 
32
- ### claude-yes cli
58
+ ### Command Line Interface
33
59
 
34
60
  ```bash
35
- claude-yes [--exit-on-idle=60s] [claude-command] [claude-prompts]
36
- # works exactly same as `claude` command, and automatically says "Yes" to all yes/no prompts
61
+ claude-yes [--cli=<tool>] [--exit-on-idle=60s] [tool-command] [prompts]
62
+ ```
63
+
64
+ #### Examples
37
65
 
38
- # e.g.
66
+ **Claude (default):**
67
+ ```bash
39
68
  claude-yes "run all tests and commit current changes"
40
69
  bunx claude-yes "Solve TODO.md"
70
+ ```
71
+
72
+ **Other AI tools:**
73
+ ```bash
74
+ # Use Gemini
75
+ claude-yes --cli=gemini "help me debug this code"
41
76
 
42
- # Auto-exit when Claude becomes idle (useful for automation scripts)
77
+ # Use Codex
78
+ claude-yes --cli=codex "refactor this function"
79
+
80
+ # Use Copilot
81
+ claude-yes --cli=copilot "generate unit tests"
82
+
83
+ # Use Cursor
84
+ claude-yes --cli=cursor "optimize performance"
85
+ ```
86
+
87
+ **Auto-exit when idle (useful for automation):**
88
+ ```bash
43
89
  claude-yes --exit-on-idle=60s "run all tests and commit current changes"
90
+ ```
44
91
 
45
- # Alternative: use with claude-code-execute
92
+ **Alternative with claude-code-execute:**
93
+ ```bash
46
94
  claude-code-execute claude-yes "your task here"
47
95
  ```
48
96
 
97
+ ### Supported CLI Tools
98
+
99
+ | Tool | CLI Name | Description |
100
+ |------|----------|-------------|
101
+ | Claude | `claude` | Anthropic's Claude Code (default) |
102
+ | Gemini | `gemini` | Google's Gemini CLI |
103
+ | Codex | `codex` | Microsoft's Codex CLI |
104
+ | Copilot | `copilot` | GitHub Copilot CLI |
105
+ | Cursor | `cursor` | Cursor agent CLI |
106
+
49
107
  The tool will:
50
108
 
51
- 1. run Claude Code
52
- 2. Whenever claude stucked on yes/no prompts, Automatically say YES, YES, YES, YES, YES to claude
53
- 3. When using `--exit-on-idle` flag, automatically exit when Claude becomes idle for 3 seconds (useful for automation scripts)
109
+ 1. Run the specified AI CLI tool
110
+ 2. Automatically respond "Yes" to common yes/no prompts
111
+ 3. Handle tool-specific patterns and responses
112
+ 4. When using `--exit-on-idle` flag, automatically exit when the tool becomes idle
54
113
 
55
114
  <!-- TODO: add usage As lib: call await claudeYes() and it returns render result -->
56
115
 
57
116
  ## Options
58
117
 
59
- - `--exit-on-idle`: Automatically exit when Claude becomes idle for 3 seconds. Useful for automation scripts where you want the process to terminate when Claude finishes its work.
118
+ - `--cli=<tool>`: Specify which AI CLI tool to use (claude, gemini, codex, copilot, cursor). Defaults to `claude`.
119
+ - `--exit-on-idle=<seconds>`: Automatically exit when the AI tool becomes idle for the specified duration. Useful for automation scripts.
60
120
 
61
- ## Implementation
121
+ ## Library Usage
62
122
 
63
- The tool simply mirrors the terminal use node-pty and looks for "❯ 1. Yes" patterns to automatically respond with "\r" to proceed with Claude's prompts.
123
+ You can also use this as a library in your Node.js projects:
64
124
 
125
+ ```typescript
126
+ import claudeYes from 'claude-yes';
127
+
128
+ // Use Claude
129
+ await claudeYes({
130
+ prompt: 'help me solve all todos in my codebase',
131
+ cli: 'claude',
132
+ cliArgs: ['--verbose'],
133
+ exitOnIdle: 30000, // exit after 30 seconds of idle
134
+ continueOnCrash: true,
135
+ logFile: 'claude.log',
136
+ });
137
+
138
+ // Use other tools
139
+ await claudeYes({
140
+ prompt: 'debug this function',
141
+ cli: 'gemini',
142
+ exitOnIdle: 60000,
143
+ });
65
144
  ```
66
- ❯ 1. Yes
67
- 2. No
68
- ```
69
145
 
70
- The tool will automatically send "\r" when it detects this pattern.
146
+ ## Implementation
147
+
148
+ The tool uses `node-pty` to spawn and manage AI CLI processes, with a sophisticated pattern-matching system that:
149
+
150
+ 1. **Detects Ready States**: Recognizes when each CLI tool is ready to accept input
151
+ 2. **Auto-Responds**: Automatically sends "Yes" responses to common prompts
152
+ 3. **Handles Fatal Errors**: Detects and responds to fatal error conditions
153
+ 4. **Manages Process Lifecycle**: Handles crashes, restarts, and graceful exits
154
+
155
+ Each supported CLI has its own configuration defining:
156
+ - **Ready patterns**: Regex patterns that indicate the tool is ready for input
157
+ - **Enter patterns**: Patterns that trigger automatic "Yes" responses
158
+ - **Fatal patterns**: Patterns that indicate fatal errors requiring exit
159
+ - **Binary mapping**: Maps logical names to actual executable names
160
+ - **Argument handling**: Special argument processing (e.g., adding `--search` to Codex)
71
161
 
72
162
  ## Dependencies
73
163
 
74
- - `node-pty` - For spawning and managing the Claude CLI process
164
+ - `node-pty` or `bun-pty` - For spawning and managing AI CLI processes
165
+ - `from-node-stream` - Stream processing utilities
166
+ - `sflow` - Functional stream processing
167
+ - `terminal-render` - Terminal rendering and text processing
168
+ - `phpdie` - Error handling utilities
75
169
 
76
170
  ## Inspiration
77
171
 
package/ReadyManager.ts CHANGED
@@ -2,10 +2,8 @@ export class ReadyManager {
2
2
  private isReady = false;
3
3
  private readyQueue: (() => void)[] = [];
4
4
  wait() {
5
- return new Promise<void>((resolve) => {
6
- if (this.isReady) return resolve();
7
- this.readyQueue.push(resolve);
8
- });
5
+ if (this.isReady) return;
6
+ return new Promise<void>((resolve) => this.readyQueue.push(resolve));
9
7
  }
10
8
  unready() {
11
9
  this.isReady = false;
package/cli-idle.spec.ts CHANGED
@@ -6,7 +6,7 @@ import { sleepms } from './utils';
6
6
  // 2025-08-11 ok
7
7
  it.skip('CLI --exit-on-idle flag with custom timeout', async () => {
8
8
  const p = exec(
9
- `bunx tsx ./cli.ts --verbose --logFile=./cli-idle.log --exit-on-idle=3s "say hello and wait"`
9
+ `bunx tsx ./cli.ts --verbose --logFile=./cli-idle.log --exit-on-idle=3s "say hello and wait"`,
10
10
  );
11
11
  const tr = new TransformStream<string, string>();
12
12
  const output = await sflow(tr.readable).by(fromStdio(p)).log().text();
package/cli.test.ts CHANGED
@@ -1,11 +1,11 @@
1
- import { execaCommand } from 'execa';
2
- import { fromStdio } from 'from-node-stream';
3
1
  import { exec } from 'node:child_process';
4
2
  import { existsSync } from 'node:fs';
5
3
  import { readFile, unlink } from 'node:fs/promises';
4
+ import { execaCommand } from 'execa';
5
+ import { fromStdio } from 'from-node-stream';
6
6
  import sflow from 'sflow';
7
7
  import { beforeAll, describe, expect, it } from 'vitest';
8
- import { createIdleWatcher } from './createIdleWatcher';
8
+ import { IdleWaiter } from './idleWaiter';
9
9
  import { sleepms } from './utils';
10
10
 
11
11
  it('Write file with auto bypass prompts', async () => {
@@ -17,7 +17,7 @@ it('Write file with auto bypass prompts', async () => {
17
17
  }
18
18
 
19
19
  const p = exec(
20
- `bunx tsx ./cli.ts --logFile=./cli-rendered.log --exit-on-idle=3s "just write {on: 1} into ./.cache/flag.json and wait"`
20
+ `bunx tsx ./cli.ts --logFile=./cli-rendered.log --exit-on-idle=3s "just write {on: 1} into ./.cache/flag.json and wait"`,
21
21
  );
22
22
  const pExitCode = new Promise<number | null>((r) => p.once('exit', r));
23
23
 
@@ -34,12 +34,13 @@ it('Write file with auto bypass prompts', async () => {
34
34
 
35
35
  // ping function to exit claude when idle
36
36
 
37
- const { ping } = createIdleWatcher(() => exit(), 3000);
37
+ const idleWaiter = new IdleWaiter();
38
+ idleWaiter.wait(3000).then(() => exit());
38
39
 
39
40
  const output = await sflow(tr.readable)
40
41
  .by(fromStdio(p))
41
42
  .log()
42
- .forEach(() => ping())
43
+ .forEach(() => idleWaiter.ping())
43
44
  .text();
44
45
 
45
46
  // expect the file exists
package/cli.ts CHANGED
@@ -1,35 +1,44 @@
1
1
  #!/usr/bin/env node
2
- import ms from 'enhanced-ms';
2
+ import enhancedMs from 'enhanced-ms';
3
3
  import yargs from 'yargs';
4
4
  import { hideBin } from 'yargs/helpers';
5
5
  import claudeYes from '.';
6
6
 
7
7
  // cli entry point
8
8
  const argv = yargs(hideBin(process.argv))
9
- .usage('Usage: $0 [options] [--] [claude args]')
9
+ .usage('Usage: $0 [options] [claude args] [--] [prompts...]')
10
10
  .example(
11
11
  '$0 --exit-on-idle=30s --continue-on-crash "help me solve all todos in my codebase"',
12
- 'Run Claude with a 30 seconds idle timeout and continue on crash'
12
+ 'Run Claude with a 30 seconds idle timeout and continue on crash',
13
13
  )
14
- .option('exit-on-idle', {
15
- type: 'string',
16
- default: '60s',
17
- description:
18
- 'Exit after being idle for specified duration, default 1min, set to 0 to disable this behaviour',
19
- })
20
14
  .option('continue-on-crash', {
21
15
  type: 'boolean',
22
16
  default: true,
23
- description: 'Continue running even if Claude crashes',
17
+ description:
18
+ 'spawn Claude with --continue if it crashes, only works for claude',
24
19
  })
25
20
  .option('log-file', {
26
21
  type: 'string',
27
- description: 'Path to log file for output logging',
22
+ description: 'Log file to write to',
23
+ })
24
+ .option('cli', {
25
+ type: 'string',
26
+ description:
27
+ 'Claude CLI command, e.g. "claude,gemini,codex,cursor,copilot", default is "claude"',
28
+ })
29
+ .option('prompt', {
30
+ type: 'string',
31
+ description: 'Prompt to send to Claude',
32
+ alias: 'p',
28
33
  })
29
34
  .option('verbose', {
30
35
  type: 'boolean',
31
- default: false,
32
36
  description: 'Enable verbose logging',
37
+ default: false,
38
+ })
39
+ .option('exit-on-idle', {
40
+ type: 'string',
41
+ description: 'Exit after a period of inactivity, e.g., "5s" or "1m"',
33
42
  })
34
43
  .parserConfiguration({
35
44
  'unknown-options-as-args': true,
@@ -37,9 +46,38 @@ const argv = yargs(hideBin(process.argv))
37
46
  })
38
47
  .parseSync();
39
48
 
49
+ // detect cli name for cli, while package.json have multiple bin link: {"claude-yes": "cli.js", "codex-yes": "cli.js", "gemini-yes": "cli.js"}
50
+ if (!argv.cli) {
51
+ const cliName = process.argv[1]?.split('/').pop()?.split('-')[0];
52
+ argv.cli = cliName || 'claude';
53
+ }
54
+
55
+ // Support: everything after a literal `--` is a prompt string. Example:
56
+ // claude-yes --exit-on-idle=30s -- "help me refactor this"
57
+ // In that example the prompt will be `help me refactor this` and won't be
58
+ // passed as args to the underlying CLI binary.
59
+ const rawArgs = process.argv.slice(2);
60
+ const dashIndex = rawArgs.indexOf('--');
61
+ let promptFromDash: string | undefined = undefined;
62
+ let cliArgsForSpawn: string[] = [];
63
+ if (dashIndex !== -1) {
64
+ // join everything after `--` into a single prompt string
65
+ const after = rawArgs.slice(dashIndex + 1);
66
+ promptFromDash = after.join(' ');
67
+ // use everything before `--` as the cli args
68
+ cliArgsForSpawn = rawArgs.slice(0, dashIndex).map(String);
69
+ } else {
70
+ // fallback to yargs parsed positional args when `--` is not used
71
+ cliArgsForSpawn = argv._.map((e) => String(e));
72
+ }
73
+
74
+ console.clear();
40
75
  const { exitCode, logs } = await claudeYes({
41
- exitOnIdle: argv.exitOnIdle != null ? ms(argv.exitOnIdle) : undefined,
42
- claudeArgs: argv._.map((e) => String(e)),
76
+ cli: argv.cli,
77
+ // prefer explicit --prompt / -p; otherwise use the text after `--` if present
78
+ prompt: argv.prompt || promptFromDash,
79
+ exitOnIdle: argv.exitOnIdle ? enhancedMs(argv.exitOnIdle) : undefined,
80
+ cliArgs: cliArgsForSpawn,
43
81
  continueOnCrash: argv.continueOnCrash,
44
82
  logFile: argv.logFile,
45
83
  verbose: argv.verbose,
@@ -0,0 +1,332 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from "node:module";
3
+ var __create = Object.create;
4
+ var __getProtoOf = Object.getPrototypeOf;
5
+ var __defProp = Object.defineProperty;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __toESM = (mod, isNodeMode, target) => {
9
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
10
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
11
+ for (let key of __getOwnPropNames(mod))
12
+ if (!__hasOwnProp.call(to, key))
13
+ __defProp(to, key, {
14
+ get: () => mod[key],
15
+ enumerable: true
16
+ });
17
+ return to;
18
+ };
19
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
20
+
21
+ // cli.ts
22
+ import enhancedMs from "enhanced-ms";
23
+ import yargs from "yargs";
24
+ import { hideBin } from "yargs/helpers";
25
+
26
+ // dist/index.js
27
+ import { fromReadable, fromWritable } from "from-node-stream";
28
+ import { mkdir, writeFile } from "fs/promises";
29
+ import path from "path";
30
+ import DIE from "phpdie";
31
+ import sflow from "sflow";
32
+ import { TerminalTextRender } from "terminal-render";
33
+ class IdleWaiter {
34
+ lastActivityTime = Date.now();
35
+ checkInterval = 100;
36
+ constructor() {
37
+ this.ping();
38
+ }
39
+ ping() {
40
+ this.lastActivityTime = Date.now();
41
+ return this;
42
+ }
43
+ async wait(ms) {
44
+ while (this.lastActivityTime >= Date.now() - ms)
45
+ await new Promise((resolve) => setTimeout(resolve, this.checkInterval));
46
+ }
47
+ }
48
+
49
+ class ReadyManager {
50
+ isReady = false;
51
+ readyQueue = [];
52
+ wait() {
53
+ if (this.isReady)
54
+ return;
55
+ return new Promise((resolve) => this.readyQueue.push(resolve));
56
+ }
57
+ unready() {
58
+ this.isReady = false;
59
+ }
60
+ ready() {
61
+ this.isReady = true;
62
+ if (!this.readyQueue.length)
63
+ return;
64
+ this.readyQueue.splice(0).map((resolve) => resolve());
65
+ }
66
+ }
67
+ function removeControlCharacters(str) {
68
+ return str.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, "");
69
+ }
70
+ var CLI_CONFIGURES = {
71
+ claude: {
72
+ ready: /^> /,
73
+ enter: [/❯ 1. Yes/, /❯ 1. Dark mode✔/, /Press Enter to continue…/],
74
+ fatal: [
75
+ /No conversation found to continue/,
76
+ /⎿ {2}Claude usage limit reached\./
77
+ ]
78
+ },
79
+ gemini: {
80
+ ready: /Type your message/,
81
+ enter: [/│ ● 1. Yes, allow once/],
82
+ fatal: []
83
+ },
84
+ codex: {
85
+ ready: /⏎ send/,
86
+ enter: [/ > 1. Approve/, /> 1. Yes, allow Codex to work in this folder/],
87
+ fatal: [/Error: The cursor position could not be read within/],
88
+ ensureArgs: (args) => {
89
+ if (!args.includes("--search"))
90
+ return ["--search", ...args];
91
+ return args;
92
+ }
93
+ },
94
+ copilot: {
95
+ ready: /^ > /,
96
+ enter: [/ │ ❯ 1. Yes, proceed/, /❯ 1. Yes/],
97
+ fatal: []
98
+ },
99
+ cursor: {
100
+ binary: "cursor-agent",
101
+ ready: /\/ commands/,
102
+ enter: [/→ Run \(once\) \(y\) \(enter\)/, /▶ \[a\] Trust this workspace/],
103
+ fatal: []
104
+ }
105
+ };
106
+ async function claudeYes({
107
+ cli = "claude",
108
+ cliArgs = [],
109
+ prompt,
110
+ continueOnCrash,
111
+ cwd,
112
+ env,
113
+ exitOnIdle,
114
+ logFile,
115
+ removeControlCharactersFromStdout = false,
116
+ verbose = false
117
+ } = {}) {
118
+ const continueArgs = {
119
+ codex: "resume --last".split(" "),
120
+ claude: "--continue".split(" "),
121
+ gemini: []
122
+ };
123
+ process.stdin.setRawMode?.(true);
124
+ let isFatal = false;
125
+ const stdinReady = new ReadyManager;
126
+ const shellOutputStream = new TransformStream;
127
+ const outputWriter = shellOutputStream.writable.getWriter();
128
+ 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"));
129
+ const getPtyOptions = () => ({
130
+ name: "xterm-color",
131
+ ...getTerminalDimensions(),
132
+ cwd: cwd ?? process.cwd(),
133
+ env: env ?? process.env
134
+ });
135
+ const cliConf = CLI_CONFIGURES[cli] || {};
136
+ cliArgs = cliConf.ensureArgs?.(cliArgs) ?? cliArgs;
137
+ const cliCommand = cliConf?.binary || cli;
138
+ let shell = pty.spawn(cliCommand, cliArgs, getPtyOptions());
139
+ const pendingExitCode = Promise.withResolvers();
140
+ let pendingExitCodeValue = null;
141
+ async function onData(data) {
142
+ await outputWriter.write(data);
143
+ }
144
+ shell.onData(onData);
145
+ shell.onExit(function onExit({ exitCode: exitCode2 }) {
146
+ stdinReady.unready();
147
+ const agentCrashed = exitCode2 !== 0;
148
+ const continueArg = continueArgs[cli];
149
+ if (agentCrashed && continueOnCrash && continueArg) {
150
+ if (!continueArg) {
151
+ return console.warn(`continueOnCrash is only supported for ${Object.keys(continueArgs).join(", ")} currently, not ${cli}`);
152
+ }
153
+ if (isFatal) {
154
+ console.log(`${cli} crashed with "No conversation found to continue", exiting...`);
155
+ return pendingExitCode.resolve(pendingExitCodeValue = exitCode2);
156
+ }
157
+ console.log(`${cli} crashed, restarting...`);
158
+ shell = pty.spawn(cli, continueArg, getPtyOptions());
159
+ shell.onData(onData);
160
+ shell.onExit(onExit);
161
+ return;
162
+ }
163
+ return pendingExitCode.resolve(pendingExitCodeValue = exitCode2);
164
+ });
165
+ process.stdout.on("resize", () => {
166
+ const { cols, rows } = getTerminalDimensions();
167
+ shell.resize(cols, rows);
168
+ });
169
+ const terminalRender = new TerminalTextRender;
170
+ const isStillWorkingQ = () => terminalRender.render().replace(/\s+/g, " ").match(/esc to interrupt|to run in background/);
171
+ const idleWaiter = new IdleWaiter;
172
+ if (exitOnIdle)
173
+ idleWaiter.wait(exitOnIdle).then(async () => {
174
+ if (isStillWorkingQ()) {
175
+ console.log("[${cli}-yes] ${cli} is idle, but seems still working, not exiting yet");
176
+ return;
177
+ }
178
+ console.log("[${cli}-yes] ${cli} is idle, exiting...");
179
+ await exitAgent();
180
+ });
181
+ sflow(fromReadable(process.stdin)).map((buffer) => buffer.toString()).by({
182
+ writable: new WritableStream({
183
+ write: async (data) => {
184
+ await stdinReady.wait();
185
+ shell.write(data);
186
+ }
187
+ }),
188
+ readable: shellOutputStream.readable
189
+ }).forEach(() => idleWaiter.ping()).forEach((text) => {
190
+ terminalRender.write(text);
191
+ if (process.stdin.isTTY)
192
+ return;
193
+ if (text.includes("\x1B[6n"))
194
+ return;
195
+ const rendered = terminalRender.render();
196
+ const row = rendered.split(`
197
+ `).length + 1;
198
+ const col = (rendered.split(`
199
+ `).slice(-1)[0]?.length || 0) + 1;
200
+ shell.write(`\x1B[${row};${col}R`);
201
+ }).forkTo((e) => e.map((e2) => removeControlCharacters(e2)).map((e2) => e2.replaceAll("\r", "")).lines({ EOL: "NONE" }).forEach(async (e2, i) => {
202
+ const conf = CLI_CONFIGURES[cli] || null;
203
+ if (!conf)
204
+ return;
205
+ try {
206
+ if (conf.ready) {
207
+ if (cli === "gemini" && conf.ready instanceof RegExp) {
208
+ if (e2.match(conf.ready) && i > 80)
209
+ return stdinReady.ready();
210
+ } else if (e2.match(conf.ready)) {
211
+ return stdinReady.ready();
212
+ }
213
+ }
214
+ if (conf.enter && Array.isArray(conf.enter)) {
215
+ for (const rx of conf.enter) {
216
+ if (e2.match(rx))
217
+ return await sendEnter();
218
+ }
219
+ }
220
+ if (conf.fatal && Array.isArray(conf.fatal)) {
221
+ for (const rx of conf.fatal) {
222
+ if (e2.match(rx))
223
+ return isFatal = true;
224
+ }
225
+ }
226
+ } catch (err) {
227
+ return;
228
+ }
229
+ }).run()).map((e) => removeControlCharactersFromStdout ? removeControlCharacters(e) : e).to(fromWritable(process.stdout)).then(() => null);
230
+ if (prompt)
231
+ (async () => {
232
+ await sendMessage(prompt);
233
+ })();
234
+ const exitCode = await pendingExitCode.promise;
235
+ console.log(`[${cli}-yes] ${cli} exited with code ${exitCode}`);
236
+ if (logFile) {
237
+ verbose && console.log(`[${cli}-yes] Writing rendered logs to ${logFile}`);
238
+ const logFilePath = path.resolve(logFile);
239
+ await mkdir(path.dirname(logFilePath), { recursive: true }).catch(() => null);
240
+ await writeFile(logFilePath, terminalRender.render());
241
+ }
242
+ return { exitCode, logs: terminalRender.render() };
243
+ async function sendEnter(waitms = 1000) {
244
+ const st = Date.now();
245
+ await idleWaiter.wait(waitms);
246
+ const et = Date.now();
247
+ process.stdout.write(`\ridleWaiter.wait(${waitms}) took ${et - st}ms\r`);
248
+ shell.write("\r");
249
+ }
250
+ async function sendMessage(message) {
251
+ await stdinReady.wait();
252
+ shell.write(message);
253
+ idleWaiter.ping();
254
+ await sendEnter();
255
+ }
256
+ async function exitAgent() {
257
+ continueOnCrash = false;
258
+ await sendMessage("/exit");
259
+ let exited = false;
260
+ await Promise.race([
261
+ pendingExitCode.promise.then(() => exited = true),
262
+ new Promise((resolve) => setTimeout(() => {
263
+ if (exited)
264
+ return;
265
+ shell.kill();
266
+ resolve();
267
+ }, 5000))
268
+ ]);
269
+ }
270
+ function getTerminalDimensions() {
271
+ return {
272
+ cols: Math.max(process.stdout.columns, 80),
273
+ rows: process.stdout.rows
274
+ };
275
+ }
276
+ }
277
+
278
+ // cli.ts
279
+ var argv = yargs(hideBin(process.argv)).usage("Usage: $0 [options] [claude args] [--] [prompts...]").example('$0 --exit-on-idle=30s --continue-on-crash "help me solve all todos in my codebase"', "Run Claude with a 30 seconds idle timeout and continue on crash").option("continue-on-crash", {
280
+ type: "boolean",
281
+ default: true,
282
+ description: "spawn Claude with --continue if it crashes, only works for claude"
283
+ }).option("log-file", {
284
+ type: "string",
285
+ description: "Log file to write to"
286
+ }).option("cli", {
287
+ type: "string",
288
+ description: 'Claude CLI command, e.g. "claude,gemini,codex,cursor,copilot", default is "claude"'
289
+ }).option("prompt", {
290
+ type: "string",
291
+ description: "Prompt to send to Claude",
292
+ alias: "p"
293
+ }).option("verbose", {
294
+ type: "boolean",
295
+ description: "Enable verbose logging",
296
+ default: false
297
+ }).option("exit-on-idle", {
298
+ type: "string",
299
+ description: 'Exit after a period of inactivity, e.g., "5s" or "1m"'
300
+ }).parserConfiguration({
301
+ "unknown-options-as-args": true,
302
+ "halt-at-non-option": true
303
+ }).parseSync();
304
+ if (!argv.cli) {
305
+ const cliName = process.argv[1]?.split("/").pop()?.split("-")[0];
306
+ argv.cli = cliName || "claude";
307
+ }
308
+ var rawArgs = process.argv.slice(2);
309
+ var dashIndex = rawArgs.indexOf("--");
310
+ var promptFromDash = undefined;
311
+ var cliArgsForSpawn = [];
312
+ if (dashIndex !== -1) {
313
+ const after = rawArgs.slice(dashIndex + 1);
314
+ promptFromDash = after.join(" ");
315
+ cliArgsForSpawn = rawArgs.slice(0, dashIndex).map(String);
316
+ } else {
317
+ cliArgsForSpawn = argv._.map((e) => String(e));
318
+ }
319
+ console.clear();
320
+ var { exitCode, logs } = await claudeYes({
321
+ cli: argv.cli,
322
+ prompt: argv.prompt || promptFromDash,
323
+ exitOnIdle: argv.exitOnIdle ? enhancedMs(argv.exitOnIdle) : undefined,
324
+ cliArgs: cliArgsForSpawn,
325
+ continueOnCrash: argv.continueOnCrash,
326
+ logFile: argv.logFile,
327
+ verbose: argv.verbose
328
+ });
329
+ process.exit(exitCode ?? 1);
330
+
331
+ //# debugId=0E9D5C1D34AAD47764756E2164756E21
332
+ //# sourceMappingURL=cli.js.map