claude-yes 1.15.0 → 1.16.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/cli-idle.spec.ts +17 -0
- package/cli.test.ts +50 -67
- package/cli.ts +13 -23
- package/createIdleWatcher.ts +18 -16
- package/dist/cli.js +158 -11356
- package/dist/cli.js.map +14 -0
- package/dist/index.js +54 -5069
- package/dist/index.js.map +13 -0
- package/index.ts +71 -49
- package/package.json +39 -39
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../index.ts", "../createIdleWatcher.ts", "../removeControlCharacters.ts", "../utils.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import { fromReadable, fromWritable } from 'from-node-stream';\nimport sflow from 'sflow';\nimport { createIdleWatcher } from './createIdleWatcher';\nimport { removeControlCharacters } from './removeControlCharacters';\nimport { sleepms } from './utils';\nimport { TerminalTextRender } from 'terminal-render';\nimport { writeFile } from 'fs/promises';\nimport path from 'path';\nimport { mkdir } from 'fs/promises';\n\n/**\n * Main function to run Claude with automatic yes/no respojnses\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\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 */\nexport default async function claudeYes({\n claudeArgs = [],\n continueOnCrash,\n cwd = process.cwd(),\n env = process.env as Record<string, string>,\n exitOnIdle,\n logFile,\n removeControlCharactersFromStdout = false, // = !process.stdout.isTTY,\n verbose = false,\n}: {\n claudeArgs?: 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 if (verbose) {\n console.log('calling claudeYes: ', {\n continueOnCrash,\n exitOnIdle,\n claudeArgs,\n cwd,\n removeControlCharactersFromStdout,\n logFile,\n verbose,\n });\n }\n console.log(\n '⭐ Starting claude, 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 const prefix = ''; // \"YESC|\"\n const PREFIXLENGTH = prefix.length;\n let errorNoConversation = false; // match 'No conversation found to continue'\n\n const shellOutputStream = new TransformStream<string, string>();\n const outputWriter = shellOutputStream.writable.getWriter();\n // const pty = await import('node-pty');\n\n // recommened to use bun pty in windows\n const pty = process.versions.bun\n ? await import('bun-pty')\n : await import('node-pty');\n let shell = pty.spawn('claude', claudeArgs, {\n name: 'xterm-color',\n cols: process.stdout.columns - PREFIXLENGTH,\n rows: process.stdout.rows,\n cwd,\n env,\n });\n let pendingExitCode = Promise.withResolvers<number | null>();\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 shell.onData(onData);\n // when claude process exits, exit the main process with the same exit code\n shell.onExit(function onExit({ exitCode }) {\n if (continueOnCrash && exitCode !== 0) {\n if (errorNoConversation) {\n console.log(\n 'Claude crashed with \"No conversation found to continue\", exiting...'\n );\n return pendingExitCode.resolve(exitCode);\n }\n console.log('Claude crashed, restarting...');\n shell = pty.spawn('claude', ['--continue', 'continue'], {\n name: 'xterm-color',\n cols: process.stdout.columns - PREFIXLENGTH,\n rows: process.stdout.rows,\n cwd,\n env,\n });\n shell.onData(onData);\n shell.onExit(onExit);\n return;\n }\n return pendingExitCode.resolve(exitCode);\n });\n\n const exitClaudeCode = async () => {\n // send exit command to the shell, must sleep a bit to avoid claude treat it as pasted input\n await sflow(['\\r', '/exit', '\\r'])\n .forEach(async (e) => {\n await sleepms(200);\n shell.write(e);\n })\n .run();\n\n // wait for shell to exit or kill it with a timeout\n let exited = false;\n await Promise.race([\n new Promise<void>((resolve) =>\n shell.onExit(() => {\n resolve();\n exited = true;\n })\n ), // resolve when shell exits\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 // when current tty resized, resize the pty\n process.stdout.on('resize', () => {\n const { columns, rows } = process.stdout;\n shell.resize(columns - PREFIXLENGTH, rows);\n });\n\n const shellStdio = {\n writable: new WritableStream<string>({\n write: (data) => shell.write(data),\n close: () => {},\n }),\n readable: shellOutputStream.readable,\n };\n\n const ttr = new TerminalTextRender();\n const idleWatcher = !exitOnIdle\n ? null\n : createIdleWatcher(async () => {\n if (\n ttr\n .render()\n .replace(/\\s+/g, ' ')\n .match(/esc to interrupt|to run in background/)\n ) {\n console.log(\n '[claude-yes] Claude is idle, but seems still working, not exiting yet'\n );\n } else {\n console.log('[claude-yes] Claude is idle, exiting...');\n await exitClaudeCode();\n }\n }, exitOnIdle);\n const confirm = async () => {\n await sleepms(200);\n shell.write('\\r');\n };\n\n sflow(fromReadable<Buffer>(process.stdin))\n .map((buffer) => buffer.toString())\n // .forEach(e => appendFile('.cache/io.log', \"input |\" + JSON.stringify(e) + '\\n')) // for debugging\n .by(shellStdio)\n // handle ttr render\n .forEach((text) => ttr.write(text))\n\n // handle idle\n .forEach(() => idleWatcher?.ping()) // ping the idle watcher on output for last active time to keep track of claude status\n // auto-response\n .forkTo((e) =>\n e\n .map((e) => removeControlCharacters(e as string))\n .map((e) => e.replaceAll('\\r', '')) // remove carriage return\n .forEach(async (e) => {\n if (e.match(/❯ 1. Yes/)) return await confirm();\n if (e.match(/❯ 1. Dark mode✔|Press Enter to continue…/))\n return await confirm();\n if (e.match(/No conversation found to continue/)) {\n errorNoConversation = true; // set flag to true if error message is found\n return;\n }\n })\n // .forEach(e => appendFile('.cache/io.log', \"output|\" + JSON.stringify(e) + '\\n')) // for debugging\n .run()\n )\n .replaceAll(/.*(?:\\r\\n?|\\r?\\n)/g, (line) => prefix + line) // add prefix\n .map((e) =>\n removeControlCharactersFromStdout ? removeControlCharacters(e) : e\n )\n .to(fromWritable(process.stdout));\n\n const exitCode = await pendingExitCode.promise; // wait for the shell to exit\n verbose && console.log(`[claude-yes] claude exited with code ${exitCode}`);\n\n if (logFile) {\n verbose && console.log(`[claude-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, ttr.render());\n }\n\n return { exitCode, logs: ttr.render() };\n}\n\nexport { removeControlCharacters };\n",
|
|
6
|
+
"export function createIdleWatcher(\n onIdle: () => void,\n idleTimeout: number\n): { ping: () => void; getLastActiveTime: () => Date } {\n let lastActiveTime = new Date();\n let idleTimeoutId: NodeJS.Timeout | null = null;\n\n return {\n ping: () => {\n if (idleTimeoutId) clearTimeout(idleTimeoutId);\n\n lastActiveTime = new Date();\n idleTimeoutId = setTimeout(() => {\n clearTimeout(idleTimeoutId!);\n onIdle();\n }, idleTimeout);\n },\n getLastActiveTime: () => lastActiveTime,\n };\n}\n",
|
|
7
|
+
"export function removeControlCharacters(str: string): string {\n // Matches control characters in the C0 and C1 ranges, including Delete (U+007F)\n return str.replace(/[\\u001b\\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, '');\n}\n",
|
|
8
|
+
"export function sleepms(ms: number) {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n"
|
|
9
|
+
],
|
|
10
|
+
"mappings": "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",
|
|
11
|
+
"debugId": "56353F5D622FBAA864756E2164756E21",
|
|
12
|
+
"names": []
|
|
13
|
+
}
|
package/index.ts
CHANGED
|
@@ -5,15 +5,8 @@ import { removeControlCharacters } from './removeControlCharacters';
|
|
|
5
5
|
import { sleepms } from './utils';
|
|
6
6
|
import { TerminalTextRender } from 'terminal-render';
|
|
7
7
|
import { writeFile } from 'fs/promises';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
// async function main() {
|
|
11
|
-
// await claudeYes({
|
|
12
|
-
// continueOnCrash: true,
|
|
13
|
-
// exitOnIdle: 10000,
|
|
14
|
-
// claudeArgs: ["say hello and exit"]
|
|
15
|
-
// })
|
|
16
|
-
// }
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { mkdir } from 'fs/promises';
|
|
17
10
|
|
|
18
11
|
/**
|
|
19
12
|
* Main function to run Claude with automatic yes/no respojnses
|
|
@@ -28,25 +21,35 @@ import { writeFile } from 'fs/promises';
|
|
|
28
21
|
* @param options.removeControlCharactersFromStdout - Remove ANSI control characters from stdout. Defaults to !process.stdout.isTTY
|
|
29
22
|
*/
|
|
30
23
|
export default async function claudeYes({
|
|
31
|
-
continueOnCrash,
|
|
32
|
-
exitOnIdle,
|
|
33
24
|
claudeArgs = [],
|
|
25
|
+
continueOnCrash,
|
|
34
26
|
cwd = process.cwd(),
|
|
35
|
-
|
|
36
|
-
|
|
27
|
+
env = process.env as Record<string, string>,
|
|
28
|
+
exitOnIdle,
|
|
37
29
|
logFile,
|
|
30
|
+
removeControlCharactersFromStdout = false, // = !process.stdout.isTTY,
|
|
31
|
+
verbose = false,
|
|
38
32
|
}: {
|
|
39
|
-
continueOnCrash?: boolean;
|
|
40
|
-
exitOnIdle?: boolean | number;
|
|
41
33
|
claudeArgs?: string[];
|
|
34
|
+
continueOnCrash?: boolean;
|
|
42
35
|
cwd?: string;
|
|
43
|
-
|
|
36
|
+
env?: Record<string, string>;
|
|
37
|
+
exitOnIdle?: number;
|
|
44
38
|
logFile?: string;
|
|
39
|
+
removeControlCharactersFromStdout?: boolean;
|
|
40
|
+
verbose?: boolean;
|
|
45
41
|
} = {}) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
42
|
+
if (verbose) {
|
|
43
|
+
console.log('calling claudeYes: ', {
|
|
44
|
+
continueOnCrash,
|
|
45
|
+
exitOnIdle,
|
|
46
|
+
claudeArgs,
|
|
47
|
+
cwd,
|
|
48
|
+
removeControlCharactersFromStdout,
|
|
49
|
+
logFile,
|
|
50
|
+
verbose,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
50
53
|
console.log(
|
|
51
54
|
'⭐ Starting claude, automatically responding to yes/no prompts...'
|
|
52
55
|
);
|
|
@@ -61,7 +64,10 @@ export default async function claudeYes({
|
|
|
61
64
|
|
|
62
65
|
const shellOutputStream = new TransformStream<string, string>();
|
|
63
66
|
const outputWriter = shellOutputStream.writable.getWriter();
|
|
64
|
-
const pty =
|
|
67
|
+
// const pty = await import('node-pty');
|
|
68
|
+
|
|
69
|
+
// recommened to use bun pty in windows
|
|
70
|
+
const pty = process.versions.bun
|
|
65
71
|
? await import('bun-pty')
|
|
66
72
|
: await import('node-pty');
|
|
67
73
|
let shell = pty.spawn('claude', claudeArgs, {
|
|
@@ -69,8 +75,9 @@ export default async function claudeYes({
|
|
|
69
75
|
cols: process.stdout.columns - PREFIXLENGTH,
|
|
70
76
|
rows: process.stdout.rows,
|
|
71
77
|
cwd,
|
|
72
|
-
env
|
|
78
|
+
env,
|
|
73
79
|
});
|
|
80
|
+
let pendingExitCode = Promise.withResolvers<number | null>();
|
|
74
81
|
// TODO handle error if claude is not installed, show msg:
|
|
75
82
|
// npm install -g @anthropic-ai/claude-code
|
|
76
83
|
|
|
@@ -86,21 +93,21 @@ export default async function claudeYes({
|
|
|
86
93
|
console.log(
|
|
87
94
|
'Claude crashed with "No conversation found to continue", exiting...'
|
|
88
95
|
);
|
|
89
|
-
|
|
96
|
+
return pendingExitCode.resolve(exitCode);
|
|
90
97
|
}
|
|
91
98
|
console.log('Claude crashed, restarting...');
|
|
92
|
-
shell = pty.spawn('claude', ['continue', '
|
|
99
|
+
shell = pty.spawn('claude', ['--continue', 'continue'], {
|
|
93
100
|
name: 'xterm-color',
|
|
94
101
|
cols: process.stdout.columns - PREFIXLENGTH,
|
|
95
102
|
rows: process.stdout.rows,
|
|
96
103
|
cwd,
|
|
97
|
-
env
|
|
104
|
+
env,
|
|
98
105
|
});
|
|
99
106
|
shell.onData(onData);
|
|
100
107
|
shell.onExit(onExit);
|
|
101
108
|
return;
|
|
102
109
|
}
|
|
103
|
-
|
|
110
|
+
return pendingExitCode.resolve(exitCode);
|
|
104
111
|
});
|
|
105
112
|
|
|
106
113
|
const exitClaudeCode = async () => {
|
|
@@ -147,33 +154,38 @@ export default async function claudeYes({
|
|
|
147
154
|
};
|
|
148
155
|
|
|
149
156
|
const ttr = new TerminalTextRender();
|
|
150
|
-
const idleWatcher =
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
157
|
+
const idleWatcher = !exitOnIdle
|
|
158
|
+
? null
|
|
159
|
+
: createIdleWatcher(async () => {
|
|
160
|
+
if (
|
|
161
|
+
ttr
|
|
162
|
+
.render()
|
|
163
|
+
.replace(/\s+/g, ' ')
|
|
164
|
+
.match(/esc to interrupt|to run in background/)
|
|
165
|
+
) {
|
|
166
|
+
console.log(
|
|
167
|
+
'[claude-yes] Claude is idle, but seems still working, not exiting yet'
|
|
168
|
+
);
|
|
169
|
+
} else {
|
|
170
|
+
console.log('[claude-yes] Claude is idle, exiting...');
|
|
171
|
+
await exitClaudeCode();
|
|
172
|
+
}
|
|
173
|
+
}, exitOnIdle);
|
|
167
174
|
const confirm = async () => {
|
|
168
175
|
await sleepms(200);
|
|
169
176
|
shell.write('\r');
|
|
170
177
|
};
|
|
171
|
-
|
|
172
|
-
|
|
178
|
+
|
|
179
|
+
sflow(fromReadable<Buffer>(process.stdin))
|
|
173
180
|
.map((buffer) => buffer.toString())
|
|
174
|
-
.forEach((text) => ttr.write(text))
|
|
175
181
|
// .forEach(e => appendFile('.cache/io.log', "input |" + JSON.stringify(e) + '\n')) // for debugging
|
|
176
182
|
.by(shellStdio)
|
|
183
|
+
// handle ttr render
|
|
184
|
+
.forEach((text) => ttr.write(text))
|
|
185
|
+
|
|
186
|
+
// handle idle
|
|
187
|
+
.forEach(() => idleWatcher?.ping()) // ping the idle watcher on output for last active time to keep track of claude status
|
|
188
|
+
// auto-response
|
|
177
189
|
.forkTo((e) =>
|
|
178
190
|
e
|
|
179
191
|
.map((e) => removeControlCharacters(e as string))
|
|
@@ -187,7 +199,6 @@ export default async function claudeYes({
|
|
|
187
199
|
return;
|
|
188
200
|
}
|
|
189
201
|
})
|
|
190
|
-
|
|
191
202
|
// .forEach(e => appendFile('.cache/io.log', "output|" + JSON.stringify(e) + '\n')) // for debugging
|
|
192
203
|
.run()
|
|
193
204
|
)
|
|
@@ -197,8 +208,19 @@ export default async function claudeYes({
|
|
|
197
208
|
)
|
|
198
209
|
.to(fromWritable(process.stdout));
|
|
199
210
|
|
|
200
|
-
|
|
201
|
-
|
|
211
|
+
const exitCode = await pendingExitCode.promise; // wait for the shell to exit
|
|
212
|
+
verbose && console.log(`[claude-yes] claude exited with code ${exitCode}`);
|
|
213
|
+
|
|
214
|
+
if (logFile) {
|
|
215
|
+
verbose && console.log(`[claude-yes] Writing rendered logs to ${logFile}`);
|
|
216
|
+
const logFilePath = path.resolve(logFile);
|
|
217
|
+
await mkdir(path.dirname(logFilePath), { recursive: true }).catch(
|
|
218
|
+
() => null
|
|
219
|
+
);
|
|
220
|
+
await writeFile(logFilePath, ttr.render());
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return { exitCode, logs: ttr.render() };
|
|
202
224
|
}
|
|
203
225
|
|
|
204
226
|
export { removeControlCharacters };
|
package/package.json
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-yes",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"author": "snomiao <snomiao@gmail.com>",
|
|
3
|
+
"version": "1.16.0",
|
|
4
|
+
"description": "A wrapper tool that automates interactions with the Claude CLI by automatically handling common prompts and responses.",
|
|
7
5
|
"keywords": [
|
|
8
6
|
"claude",
|
|
9
7
|
"ai",
|
|
@@ -14,23 +12,51 @@
|
|
|
14
12
|
"anthropic",
|
|
15
13
|
"auto-response"
|
|
16
14
|
],
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
],
|
|
21
|
-
"bin": {
|
|
22
|
-
"claude-yes": "dist/cli.js"
|
|
15
|
+
"homepage": "https://github.com/snomiao/claude-yes#readme",
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/snomiao/claude-yes/issues"
|
|
23
18
|
},
|
|
24
19
|
"repository": {
|
|
25
20
|
"type": "git",
|
|
26
21
|
"url": "git+https://github.com/snomiao/claude-yes.git"
|
|
27
22
|
},
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"author": "snomiao <snomiao@gmail.com>",
|
|
25
|
+
"type": "module",
|
|
26
|
+
"exports": {
|
|
27
|
+
"import": "./dist/index.js",
|
|
28
|
+
"types": "./index.ts"
|
|
29
|
+
},
|
|
30
|
+
"main": "index.js",
|
|
31
|
+
"module": "index.ts",
|
|
32
|
+
"types": "./index.ts",
|
|
33
|
+
"bin": {
|
|
34
|
+
"claude-yes": "dist/cli.js"
|
|
35
|
+
},
|
|
36
|
+
"directories": {
|
|
37
|
+
"doc": "docs"
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"*.ts",
|
|
41
|
+
"dist"
|
|
42
|
+
],
|
|
28
43
|
"scripts": {
|
|
44
|
+
"build": "bun build index.ts cli.ts --outdir=dist --packages=external --target=node --sourcemap",
|
|
29
45
|
"dev": "tsx index.ts",
|
|
30
|
-
"
|
|
31
|
-
"test": "vitest",
|
|
46
|
+
"fmt": "prettier -w .",
|
|
32
47
|
"prepare": "husky",
|
|
33
|
-
"
|
|
48
|
+
"test": "vitest"
|
|
49
|
+
},
|
|
50
|
+
"lint-staged": {
|
|
51
|
+
"*.{ts,js,json,md}": [
|
|
52
|
+
"prettier --write"
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"bun-pty": "^0.3.2",
|
|
57
|
+
"node-pty": "^1.0.0",
|
|
58
|
+
"terminal-render": "^1.1.0",
|
|
59
|
+
"yargs": "^18.0.0"
|
|
34
60
|
},
|
|
35
61
|
"devDependencies": {
|
|
36
62
|
"@types/bun": "^1.2.18",
|
|
@@ -51,31 +77,5 @@
|
|
|
51
77
|
},
|
|
52
78
|
"peerDependencies": {
|
|
53
79
|
"typescript": "^5.8.3"
|
|
54
|
-
},
|
|
55
|
-
"type": "module",
|
|
56
|
-
"exports": {
|
|
57
|
-
"import": "./dist/index.js",
|
|
58
|
-
"types": "./index.ts"
|
|
59
|
-
},
|
|
60
|
-
"module": "index.ts",
|
|
61
|
-
"types": "./index.ts",
|
|
62
|
-
"dependencies": {
|
|
63
|
-
"bun-pty": "^0.3.2",
|
|
64
|
-
"node-pty": "^1.0.0",
|
|
65
|
-
"terminal-render": "^1.1.0",
|
|
66
|
-
"yargs": "^18.0.0"
|
|
67
|
-
},
|
|
68
|
-
"lint-staged": {
|
|
69
|
-
"*.{ts,js,json,md}": [
|
|
70
|
-
"prettier --write"
|
|
71
|
-
]
|
|
72
|
-
},
|
|
73
|
-
"description": "A wrapper tool that automates interactions with the Claude CLI by automatically handling common prompts and responses.",
|
|
74
|
-
"main": "index.js",
|
|
75
|
-
"directories": {
|
|
76
|
-
"doc": "docs"
|
|
77
|
-
},
|
|
78
|
-
"bugs": {
|
|
79
|
-
"url": "https://github.com/snomiao/claude-yes/issues"
|
|
80
80
|
}
|
|
81
81
|
}
|