claude-yes 1.10.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 snomiao
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # Yes! Claude
2
+
3
+ A wrapper tool that automates interactions with the Claude CLI by automatically handling common prompts and responses.
4
+
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
+
7
+ ## Features
8
+
9
+ - Same as `claude` command
10
+ - Automatically responds to common prompts like "Yes, proceed" and "Yes"
11
+ - So, this will Let claude run until your task done, and wait for your next prompt.
12
+
13
+ ## Installation
14
+
15
+ First, install Claude Code globally:
16
+
17
+ ```bash
18
+ npm install -g @anthropic-ai/claude-code
19
+ ```
20
+
21
+ Learn more about Claude Code: https://www.anthropic.com/claude-code
22
+
23
+ Then install this project:
24
+
25
+ ```bash
26
+ npm install yes-claude -g
27
+ ```
28
+
29
+ ## Usage
30
+
31
+
32
+ ```bash
33
+ yes-claude [command] [prompts] [--exit-on-idle]
34
+ # works exactly same as `claude` command, and automatically says "Yes" to all yes/no prompts
35
+
36
+ # e.g.
37
+ yes-claude "run all tests and commit current changes"
38
+ bunx yes-claude "Solve TODO.md"
39
+
40
+ # Auto-exit when Claude becomes idle (useful for automation)
41
+ yes-claude "run all tests and commit current changes" --exit-on-idle
42
+
43
+ ```
44
+
45
+ The tool will:
46
+ 1. run Claude Code
47
+ 2. Whenever claude stucked on yes/no prompts, Automatically say YES, YES, YES, YES, YES to claude
48
+ 3. When using `--exit-on-idle` flag, automatically exit when Claude becomes idle for 3 seconds (useful for automation scripts)
49
+
50
+ ## Options
51
+
52
+ - `--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.
53
+
54
+ ## Implementation
55
+
56
+ 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.
57
+
58
+ ```
59
+ ❯ 1. Yes
60
+ 2. No
61
+ ```
62
+
63
+ The tool will automatically send "\r" when it detects this pattern.
64
+
65
+ ## Dependencies
66
+
67
+ - `node-pty` - For spawning and managing the Claude CLI process
68
+ - `sflow` - For stream processing and data flow management
69
+ - `from-node-stream` - For converting Node.js streams to web streams
70
+
71
+ ## Inspiration
72
+
73
+ This project was inspired by: [Claude Code full auto while I sleep : r/ClaudeAI](https://www.reddit.com/r/ClaudeAI/comments/1klk6aw/claude_code_full_auto_while_i_sleep/)
74
+
75
+ ## License
76
+
77
+ MIT
package/cli.test.ts ADDED
@@ -0,0 +1,71 @@
1
+ import { execaCommand } from 'execa';
2
+ import { fromStdio } from "from-node-stream";
3
+ import { exec } from "node:child_process";
4
+ import { existsSync } from 'node:fs';
5
+ import { readFile, unlink } from 'node:fs/promises';
6
+ import sflow from "sflow";
7
+ import { beforeAll, describe, expect, it } from "vitest";
8
+ import { createIdleWatcher } from "./createIdleWatcher";
9
+ import { sleepms } from './utils';
10
+
11
+ beforeAll(async () => {
12
+ await execaCommand(`bun run build`)
13
+ .then(() => console.log('Build successful')
14
+ )
15
+ })
16
+
17
+ describe('CLI Tests', () => {
18
+ it('Write file with auto bypass permission prompt', async () => {
19
+ const flagFile = './.cache/flag.json';
20
+ // clean
21
+ await unlink(flagFile).catch(() => { });
22
+
23
+ const p = exec(`node dist/cli.js --exit-on-idle=3s "just write {on: 1} into ./.cache/flag.json"`);
24
+ const tr = new TransformStream<string, string>()
25
+ const w = tr.writable.getWriter();
26
+
27
+ const exit = async () => await sflow(['\r', '/exit', '\r', '\r']).forEach(async (e) => {
28
+ await sleepms(200)
29
+ await w.write(e)
30
+ }).run();
31
+
32
+ // ping function to exit claude when idle
33
+
34
+ const { ping } = createIdleWatcher(() => exit(), 3000);
35
+
36
+ const output = (await sflow(tr.readable).by(fromStdio(p)).log()
37
+ .forEach(() => ping())
38
+ .text())
39
+
40
+ // expect the file exists
41
+ expect(existsSync(flagFile)).toBe(true);
42
+ // expect the output contains the file path
43
+ expect(output).toContain(flagFile);
44
+
45
+ // expect the file content to be 'on'
46
+ expect(await new Response(await readFile(flagFile)).json()).toEqual({ on: 1 });
47
+
48
+ expect(p.exitCode).toBe(0); // expect the process to exit successfully
49
+
50
+ // 30 seconds timeout for this test, it usually takes 13s to run (10s for claude to solve this problem, 3s for idle watcher to exit)
51
+ }, 30e3);
52
+
53
+ it.skip('CLI --exit-on-idle flag with default timeout', async () => {
54
+ const p = exec(`node dist/cli.js "echo hello" --exit-on-idle`);
55
+ const tr = new TransformStream<string, string>()
56
+ const output = (await sflow(tr.readable).by(fromStdio(p)).log().text())
57
+ expect(output).toContain('hello');
58
+ await sleepms(1000); // wait for process exit
59
+ expect(p.exitCode).toBe(0);
60
+ }, 30e3);
61
+
62
+ it('CLI --exit-on-idle flag with custom timeout', async () => {
63
+ const p = exec(`node dist/cli.js --exit-on-idle=1s "echo hello"`);
64
+ const tr = new TransformStream<string, string>()
65
+ const output = (await sflow(tr.readable).by(fromStdio(p)).log().text())
66
+ expect(output).toContain('hello');
67
+ await sleepms(1000); // wait for process exit
68
+ expect(p.exitCode).toBe(0);
69
+ }, 30e3);
70
+ })
71
+
package/cli.ts ADDED
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env node
2
+ import ms from "enhanced-ms";
3
+ import minimist from "minimist";
4
+ import claudeYes from ".";
5
+
6
+ // cli entry point
7
+ const args = minimist(process.argv.slice(2), {
8
+ string: ['exit-on-idle'],
9
+ boolean: ['continue-on-crash'],
10
+ // boolean: ['exit-on-idle'],
11
+ default: {
12
+ 'exit-on-idle': '60s',
13
+ 'continue-on-crash': true,
14
+ }
15
+ });
16
+
17
+ const { 'exit-on-idle': exitOnIdleArg, 'continue-on-crash': continueOnCrashArg, ...rest } = args;
18
+ const claudeArgs = Object.entries(rest).flatMap(([key, value]) => {
19
+ if (key === '_') return value as string[];
20
+ if (typeof value === 'boolean') return value ? [`--${key}`] : [];
21
+ return [`--${key}`, String(value)];
22
+ });
23
+
24
+
25
+ let exitOnIdle: boolean | number | undefined;
26
+ if (typeof exitOnIdleArg === 'string') {
27
+ if (exitOnIdleArg === '') {
28
+ exitOnIdle = true; // default timeout will be used
29
+ } else {
30
+ exitOnIdle = ms(exitOnIdleArg); // parse duration string like "5s", "30s", "1m"
31
+ }
32
+ } else {
33
+ exitOnIdle = undefined;
34
+ }
35
+
36
+
37
+ // console.debug('Parsed args:', {
38
+ // exitOnIdle,
39
+ // continueOnCrash: continueOnCrashArg,
40
+ // claudeArgs,
41
+ // });
42
+
43
+ await claudeYes({
44
+ exitOnIdle,
45
+ claudeArgs,
46
+ continueOnCrash: continueOnCrashArg,
47
+ });
@@ -0,0 +1,41 @@
1
+ import { expect, it } from 'vitest';
2
+ import { createIdleWatcher } from "./createIdleWatcher";
3
+ import { sleepms } from "./utils";
4
+
5
+ it('createIdleWatcher should trigger onIdle after timeout', async () => {
6
+ let idleTriggered = false;
7
+ const watcher = createIdleWatcher(() => {
8
+ idleTriggered = true;
9
+ }, 100);
10
+
11
+ watcher.ping();
12
+ await sleepms(150);
13
+ expect(idleTriggered).toBe(true);
14
+ }, 1000);
15
+
16
+ it.concurrent('createIdleWatcher should reset timeout on ping', async () => {
17
+ let idleTriggered = false;
18
+ const watcher = createIdleWatcher(() => {
19
+ idleTriggered = true;
20
+ }, 100);
21
+
22
+ watcher.ping();
23
+ await sleepms(50);
24
+ watcher.ping();
25
+ await sleepms(50);
26
+ expect(idleTriggered).toBe(false);
27
+ await sleepms(100);
28
+ expect(idleTriggered).toBe(true);
29
+ }, 1000);
30
+
31
+ it.concurrent('createIdleWatcher should update lastActiveTime on ping', async () => {
32
+ const watcher = createIdleWatcher(() => { }, 1000);
33
+
34
+ const initialTime = watcher.getLastActiveTime();
35
+ await sleepms(50);
36
+ watcher.ping();
37
+ const updatedTime = watcher.getLastActiveTime();
38
+
39
+ expect(updatedTime.getTime()).toBeGreaterThan(initialTime.getTime());
40
+ }, 1000);
41
+
@@ -0,0 +1,18 @@
1
+
2
+ export function createIdleWatcher(onIdle: () => void, idleTimeout: number): { ping: () => void; getLastActiveTime: () => Date; } {
3
+ let lastActiveTime = new Date();
4
+ let idleTimeoutId: NodeJS.Timeout | null = null;
5
+ return {
6
+ ping: () => {
7
+ if (idleTimeoutId) {
8
+ clearTimeout(idleTimeoutId);
9
+ }
10
+ idleTimeoutId = setTimeout(() => {
11
+ clearTimeout(idleTimeoutId!);
12
+ onIdle();
13
+ }, idleTimeout);
14
+ lastActiveTime = new Date();
15
+ },
16
+ getLastActiveTime: () => lastActiveTime
17
+ };
18
+ }