claude-yes 1.20.0 → 1.21.1

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/index.ts CHANGED
@@ -11,29 +11,38 @@ import { removeControlCharacters } from './removeControlCharacters';
11
11
  export const CLI_CONFIGURES: Record<
12
12
  string,
13
13
  {
14
+ install?: string; // hint user for install command if not installed
14
15
  binary?: string; // actual binary name if different from cli
15
- ready?: RegExp; // regex matcher for stdin ready, or line index for gemini
16
+ ready?: RegExp[]; // regex matcher for stdin ready, or line index for gemini
16
17
  enter?: RegExp[]; // array of regex to match for sending Enter
17
18
  fatal?: RegExp[]; // array of regex to match for fatal errors
18
19
  ensureArgs?: (args: string[]) => string[]; // function to ensure certain args are present
19
20
  }
20
21
  > = {
22
+ grok: {
23
+ install: 'npm install -g @vibe-kit/grok-cli',
24
+ ready: [/^ │ ❯ /],
25
+ enter: [/^ 1. Yes/],
26
+ },
21
27
  claude: {
22
- ready: /^> /, // regex matcher for stdin ready,
28
+ install: 'npm install -g @anthropic-ai/claude-code',
29
+ ready: [/^> /], // regex matcher for stdin ready,
23
30
  enter: [/❯ 1. Yes/, /❯ 1. Dark mode✔/, /Press Enter to continue…/],
24
31
  fatal: [
25
32
  /No conversation found to continue/,
26
- /⎿ {2}Claude usage limit reached\./,
33
+ /⎿ Claude usage limit reached\./,
27
34
  ],
28
35
  },
29
36
  gemini: {
37
+ install: 'npm install -g @google/gemini-cli',
30
38
  // match the agent prompt after initial lines; handled by index logic using line index
31
- ready: /Type your message/, // used with line index check
39
+ ready: [/Type your message/], // used with line index check
32
40
  enter: [/│ ● 1. Yes, allow once/],
33
41
  fatal: [],
34
42
  },
35
43
  codex: {
36
- ready: /⏎ send/,
44
+ install: 'npm install -g @openai/codex-cli',
45
+ ready: [/⏎ send/],
37
46
  enter: [/ > 1. Approve/, /> 1. Yes, allow Codex to work in this folder/],
38
47
  fatal: [/Error: The cursor position could not be read within/],
39
48
  // add to codex --search by default when not provided by the user
@@ -43,16 +52,18 @@ export const CLI_CONFIGURES: Record<
43
52
  },
44
53
  },
45
54
  copilot: {
46
- ready: /^ > /,
55
+ install: 'npm install -g @github/copilot',
56
+ ready: [/^ > /],
47
57
  enter: [/ │ ❯ 1. Yes, proceed/, /❯ 1. Yes/],
48
58
  fatal: [],
49
59
  },
50
60
  cursor: {
61
+ install: 'open https://cursor.com/ja/docs/cli/installation',
51
62
  // map logical "cursor" cli name to actual binary name
52
63
  binary: 'cursor-agent',
53
- ready: /\/ commands/,
64
+ ready: [/\/ commands/],
54
65
  enter: [/→ Run \(once\) \(y\) \(enter\)/, /▶ \[a\] Trust this workspace/],
55
- fatal: [],
66
+ fatal: [/^ Error: You've hit your usage limit/],
56
67
  },
57
68
  };
58
69
  /**
@@ -131,7 +142,7 @@ export default async function claudeYes({
131
142
  // );
132
143
 
133
144
  process.stdin.setRawMode?.(true); // must be called any stdout/stdin usage
134
- let isFatal = false; // match 'No conversation found to continue'
145
+ let isFatal = false; // when true, do not restart on crash, and exit agent
135
146
  const stdinReady = new ReadyManager();
136
147
 
137
148
  const shellOutputStream = new TransformStream<string, string>();
@@ -157,7 +168,17 @@ export default async function claudeYes({
157
168
  cliArgs = cliConf.ensureArgs?.(cliArgs) ?? cliArgs;
158
169
  const cliCommand = cliConf?.binary || cli;
159
170
 
160
- let shell = pty.spawn(cliCommand, cliArgs, getPtyOptions());
171
+ let shell = tryCatch(
172
+ () => pty.spawn(cliCommand, cliArgs, getPtyOptions()),
173
+ (error: unknown) => {
174
+ console.error(`Fatal: Failed to start ${cliCommand}.`);
175
+ if (cliConf?.install)
176
+ console.error(
177
+ `If you did not installed it yet, Please install it first: ${cliConf.install}`,
178
+ );
179
+ throw error;
180
+ },
181
+ );
161
182
  const pendingExitCode = Promise.withResolvers<number | null>();
162
183
  let pendingExitCodeValue = null;
163
184
 
@@ -182,11 +203,9 @@ export default async function claudeYes({
182
203
  );
183
204
  }
184
205
  if (isFatal) {
185
- console.log(
186
- `${cli} crashed with "No conversation found to continue", exiting...`,
187
- );
188
206
  return pendingExitCode.resolve((pendingExitCodeValue = exitCode));
189
207
  }
208
+
190
209
  console.log(`${cli} crashed, restarting...`);
191
210
 
192
211
  shell = pty.spawn(cli, continueArg, getPtyOptions());
@@ -269,33 +288,20 @@ export default async function claudeYes({
269
288
  CLI_CONFIGURES[cli as keyof typeof CLI_CONFIGURES] || null;
270
289
  if (!conf) return;
271
290
 
272
- try {
273
- // ready matcher: if matched, mark stdin ready
274
- if (conf.ready) {
275
- // special-case gemini to avoid initial prompt noise: only after many lines
276
- if (cli === 'gemini' && conf.ready instanceof RegExp) {
277
- if (e.match(conf.ready) && i > 80) return stdinReady.ready();
278
- } else if (e.match(conf.ready)) {
279
- return stdinReady.ready();
280
- }
281
- }
282
-
283
- // enter matchers: send Enter when any enter regex matches
284
- if (conf.enter && Array.isArray(conf.enter)) {
285
- for (const rx of conf.enter) {
286
- if (e.match(rx)) return await sendEnter();
287
- }
288
- }
289
-
290
- // fatal matchers: set isFatal flag when matched
291
- if (conf.fatal && Array.isArray(conf.fatal)) {
292
- for (const rx of conf.fatal) {
293
- if (e.match(rx)) return (isFatal = true);
294
- }
295
- }
296
- } catch (err) {
297
- // defensive: if e.match throws (e.g., not a string), ignore
298
- return;
291
+ // ready matcher: if matched, mark stdin ready
292
+ if (conf.ready?.some((rx: RegExp) => e.match(rx))) {
293
+ if (cli === 'gemini' && i <= 80) return; // gemini initial noise, only after many lines
294
+ stdinReady.ready();
295
+ }
296
+
297
+ // enter matchers: send Enter when any enter regex matches
298
+ if (conf.enter?.some((rx: RegExp) => e.match(rx)))
299
+ await sendEnter(300); // send Enter after 300ms idle wait
300
+
301
+ // fatal matchers: set isFatal flag when matched
302
+ if (conf.fatal?.some((rx: RegExp) => e.match(rx))) {
303
+ isFatal = true;
304
+ await exitAgent();
299
305
  }
300
306
  })
301
307
  // .forEach(e => appendFile('.cache/io.log', "output|" + JSON.stringify(e) + '\n')) // for debugging
@@ -308,22 +314,7 @@ export default async function claudeYes({
308
314
  .then(() => null); // run it immediately without await
309
315
 
310
316
  // wait for cli ready and send prompt if provided
311
- if (prompt)
312
- (async () => {
313
- // console.log(`[${cli}-yes] Ready to send prompt to ${cli}: ${prompt}`);
314
- // idleWaiter.ping();
315
- // console.log(
316
- // 'await idleWaiter.wait(1000); // wait a bit for claude to start'
317
- // );
318
- // await idleWaiter.wait(1000); // wait a bit for claude to start
319
- // console.log('await stdinReady.wait();');
320
- // await stdinReady.wait();
321
- // console.log(`[${cli}-yes] Waiting for ${cli} to be ready...`);
322
- // console.log('await idleWaiter.wait(200);');
323
- // await idleWaiter.wait(200);
324
- // console.log(`[${cli}-yes] Sending prompt to ${cli}: ${prompt}`);
325
- await sendMessage(prompt);
326
- })();
317
+ if (prompt) await sendMessage(prompt);
327
318
 
328
319
  const exitCode = await pendingExitCode.promise; // wait for the shell to exit
329
320
  console.log(`[${cli}-yes] ${cli} exited with code ${exitCode}`);
@@ -381,10 +372,20 @@ export default async function claudeYes({
381
372
 
382
373
  function getTerminalDimensions() {
383
374
  return {
384
- cols: Math.max(process.stdout.columns, 80),
375
+ // TODO: enforce minimum cols/rows to avoid layout issues
376
+ // cols: Math.max(process.stdout.columns, 80),
377
+ cols: process.stdout.columns,
385
378
  rows: process.stdout.rows,
386
379
  };
387
380
  }
388
381
  }
389
382
 
390
383
  export { removeControlCharacters };
384
+
385
+ function tryCatch<T, R>(fn: () => T, catchFn: (error: unknown) => R): T | R {
386
+ try {
387
+ return fn();
388
+ } catch (error) {
389
+ return catchFn(error);
390
+ }
391
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "claude-yes",
3
- "version": "1.20.0",
4
- "description": "A wrapper tool that automates interactions with the Claude CLI by automatically handling common prompts and responses.",
3
+ "version": "1.21.1",
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",
7
7
  "ai",
@@ -33,9 +33,10 @@
33
33
  "bin": {
34
34
  "claude-yes": "dist/claude-yes.js",
35
35
  "codex-yes": "dist/codex-yes.js",
36
- "gemini-yes": "dist/gemini-yes.js",
36
+ "copilot-yes": "dist/copilot-yes.js",
37
37
  "cursor-yes": "dist/cursor-yes.js",
38
- "copilot-yes": "dist/copilot-yes.js"
38
+ "gemini-yes": "dist/gemini-yes.js",
39
+ "grok-yes": "dist/grok-yes.js"
39
40
  },
40
41
  "directories": {
41
42
  "doc": "docs"
@@ -48,7 +49,7 @@
48
49
  "build": "bun build index.ts cli.ts --packages=external --outdir=dist --target=node --sourcemap",
49
50
  "postbuild": "bun ./postbuild.ts",
50
51
  "dev": "tsx index.ts",
51
- "fmt": "bunx @biomejs/biome check --fix",
52
+ "fmt": "bunx @biomejs/biome check --fix && bunx sort-package-json",
52
53
  "prepack": "bun run build",
53
54
  "prepare": "bunx husky",
54
55
  "test": "vitest"
@@ -58,6 +59,11 @@
58
59
  "bunx @biomejs/biome check --fix"
59
60
  ]
60
61
  },
62
+ "dependencies": {
63
+ "bun-pty": "^0.3.2",
64
+ "p-map": "^7.0.3",
65
+ "phpdie": "^1.7.0"
66
+ },
61
67
  "devDependencies": {
62
68
  "@biomejs/biome": "^2.2.5",
63
69
  "@types/bun": "^1.2.18",
@@ -73,18 +79,13 @@
73
79
  "semantic-release": "^24.2.6",
74
80
  "sflow": "^1.20.2",
75
81
  "strip-ansi-control-characters": "^2.0.0",
76
- "terminal-render": "^1.1.0",
82
+ "terminal-render": "^1.2.0",
77
83
  "tsx": "^4.20.3",
78
84
  "vitest": "^3.2.4",
79
85
  "yargs": "^18.0.0"
80
86
  },
81
87
  "peerDependencies": {
82
- "typescript": "^5.8.3",
83
- "node-pty": "^1.0.0"
84
- },
85
- "dependencies": {
86
- "bun-pty": "^0.3.2",
87
- "p-map": "^7.0.3",
88
- "phpdie": "^1.7.0"
88
+ "node-pty": "^1.0.0",
89
+ "typescript": "^5.8.3"
89
90
  }
90
91
  }
package/postbuild.ts CHANGED
@@ -1,6 +1,15 @@
1
1
  #! /usr/bin/env bun
2
+ import { execaCommand } from 'execa';
2
3
  import { copyFile } from 'fs/promises';
4
+ import { CLI_CONFIGURES } from '.';
3
5
  import * as pkg from './package.json';
4
6
 
5
7
  const src = 'dist/cli.js';
6
- await Promise.all(Object.values(pkg.bin).map((dst) => copyFile(src, dst)));
8
+ await Promise.all(
9
+ Object.keys(CLI_CONFIGURES).map(async (cli) => {
10
+ const dst = `dist/${cli}-yes.js`;
11
+ if (!pkg.bin?.[cli as keyof typeof pkg.bin])
12
+ await execaCommand(`npm pkg set bin.${cli}-yes=${dst}`);
13
+ await copyFile(src, dst);
14
+ }),
15
+ );