claude-yes 1.23.3 → 1.24.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.ts +7 -0
- package/dist/claude-yes.js +209 -9
- package/dist/cli.js +209 -9
- package/dist/cli.js.map +4 -4
- package/dist/codex-yes.js +209 -9
- package/dist/copilot-yes.js +209 -9
- package/dist/cursor-yes.js +209 -9
- package/dist/gemini-yes.js +209 -9
- package/dist/grok-yes.js +209 -9
- package/dist/index.js +205 -8
- package/dist/index.js.map +6 -5
- package/index.ts +46 -1
- package/package.json +1 -1
- package/runningLock.spec.ts +477 -0
- package/runningLock.ts +324 -0
package/dist/gemini-yes.js
CHANGED
|
@@ -25,11 +25,16 @@ import { hideBin } from "yargs/helpers";
|
|
|
25
25
|
|
|
26
26
|
// dist/index.js
|
|
27
27
|
import { fromReadable, fromWritable } from "from-node-stream";
|
|
28
|
-
import { mkdir, writeFile } from "fs/promises";
|
|
29
|
-
import
|
|
28
|
+
import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
|
|
29
|
+
import path2 from "path";
|
|
30
30
|
import DIE from "phpdie";
|
|
31
31
|
import sflow from "sflow";
|
|
32
32
|
import { TerminalTextRender } from "terminal-render";
|
|
33
|
+
import { execSync } from "child_process";
|
|
34
|
+
import { existsSync } from "fs";
|
|
35
|
+
import { mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
36
|
+
import { homedir } from "os";
|
|
37
|
+
import path from "path";
|
|
33
38
|
class IdleWaiter {
|
|
34
39
|
lastActivityTime = Date.now();
|
|
35
40
|
checkInterval = 100;
|
|
@@ -67,6 +72,169 @@ class ReadyManager {
|
|
|
67
72
|
function removeControlCharacters(str) {
|
|
68
73
|
return str.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, "");
|
|
69
74
|
}
|
|
75
|
+
var LOCK_DIR = path.join(homedir(), ".claude-yes");
|
|
76
|
+
var LOCK_FILE = path.join(LOCK_DIR, "running.lock.json");
|
|
77
|
+
var MAX_RETRIES = 5;
|
|
78
|
+
var RETRY_DELAYS = [50, 100, 200, 400, 800];
|
|
79
|
+
var POLL_INTERVAL = 2000;
|
|
80
|
+
function isProcessRunning(pid) {
|
|
81
|
+
try {
|
|
82
|
+
process.kill(pid, 0);
|
|
83
|
+
return true;
|
|
84
|
+
} catch {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function getGitRoot(cwd) {
|
|
89
|
+
try {
|
|
90
|
+
const result = execSync("git rev-parse --show-toplevel", {
|
|
91
|
+
cwd,
|
|
92
|
+
encoding: "utf8",
|
|
93
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
94
|
+
});
|
|
95
|
+
return result.trim();
|
|
96
|
+
} catch {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function isGitRepo(cwd) {
|
|
101
|
+
try {
|
|
102
|
+
const gitRoot = getGitRoot(cwd);
|
|
103
|
+
return gitRoot !== null;
|
|
104
|
+
} catch {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function resolveRealPath(p) {
|
|
109
|
+
try {
|
|
110
|
+
return path.resolve(p);
|
|
111
|
+
} catch {
|
|
112
|
+
return p;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function sleep(ms) {
|
|
116
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
117
|
+
}
|
|
118
|
+
async function readLockFile() {
|
|
119
|
+
try {
|
|
120
|
+
await mkdir(LOCK_DIR, { recursive: true });
|
|
121
|
+
if (!existsSync(LOCK_FILE)) {
|
|
122
|
+
return { tasks: [] };
|
|
123
|
+
}
|
|
124
|
+
const content = await readFile(LOCK_FILE, "utf8");
|
|
125
|
+
const lockFile = JSON.parse(content);
|
|
126
|
+
lockFile.tasks = lockFile.tasks.filter((task) => {
|
|
127
|
+
if (isProcessRunning(task.pid))
|
|
128
|
+
return true;
|
|
129
|
+
return false;
|
|
130
|
+
});
|
|
131
|
+
return lockFile;
|
|
132
|
+
} catch (error) {
|
|
133
|
+
return { tasks: [] };
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
async function writeLockFile(lockFile, retryCount = 0) {
|
|
137
|
+
try {
|
|
138
|
+
await mkdir(LOCK_DIR, { recursive: true });
|
|
139
|
+
const tempFile = `${LOCK_FILE}.tmp.${process.pid}`;
|
|
140
|
+
await writeFile(tempFile, JSON.stringify(lockFile, null, 2), "utf8");
|
|
141
|
+
await rename(tempFile, LOCK_FILE);
|
|
142
|
+
} catch (error) {
|
|
143
|
+
if (retryCount < MAX_RETRIES) {
|
|
144
|
+
await sleep(RETRY_DELAYS[retryCount] || 800);
|
|
145
|
+
return writeLockFile(lockFile, retryCount + 1);
|
|
146
|
+
}
|
|
147
|
+
throw error;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
async function checkLock(cwd, prompt) {
|
|
151
|
+
const resolvedCwd = resolveRealPath(cwd);
|
|
152
|
+
const gitRoot = isGitRepo(resolvedCwd) ? getGitRoot(resolvedCwd) : null;
|
|
153
|
+
const lockKey = gitRoot || resolvedCwd;
|
|
154
|
+
const lockFile = await readLockFile();
|
|
155
|
+
const blockingTasks = lockFile.tasks.filter((task) => {
|
|
156
|
+
if (!isProcessRunning(task.pid))
|
|
157
|
+
return false;
|
|
158
|
+
if (task.status !== "running")
|
|
159
|
+
return false;
|
|
160
|
+
if (gitRoot && task.gitRoot) {
|
|
161
|
+
return task.gitRoot === gitRoot;
|
|
162
|
+
} else {
|
|
163
|
+
return task.cwd === lockKey;
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
return {
|
|
167
|
+
isLocked: blockingTasks.length > 0,
|
|
168
|
+
blockingTasks,
|
|
169
|
+
lockKey
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
async function addTask(task) {
|
|
173
|
+
const lockFile = await readLockFile();
|
|
174
|
+
lockFile.tasks = lockFile.tasks.filter((t) => t.pid !== task.pid);
|
|
175
|
+
lockFile.tasks.push(task);
|
|
176
|
+
await writeLockFile(lockFile);
|
|
177
|
+
}
|
|
178
|
+
async function updateTaskStatus(pid, status) {
|
|
179
|
+
const lockFile = await readLockFile();
|
|
180
|
+
const task = lockFile.tasks.find((t) => t.pid === pid);
|
|
181
|
+
if (task) {
|
|
182
|
+
task.status = status;
|
|
183
|
+
await writeLockFile(lockFile);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
async function removeTask(pid) {
|
|
187
|
+
const lockFile = await readLockFile();
|
|
188
|
+
lockFile.tasks = lockFile.tasks.filter((t) => t.pid !== pid);
|
|
189
|
+
await writeLockFile(lockFile);
|
|
190
|
+
}
|
|
191
|
+
async function waitForUnlock(blockingTasks, currentTask) {
|
|
192
|
+
const blockingTask = blockingTasks[0];
|
|
193
|
+
console.log(`⏳ Queueing for unlock of: ${blockingTask.task}`);
|
|
194
|
+
await addTask({ ...currentTask, status: "queued" });
|
|
195
|
+
let dots = 0;
|
|
196
|
+
while (true) {
|
|
197
|
+
await sleep(POLL_INTERVAL);
|
|
198
|
+
const lockCheck = await checkLock(currentTask.cwd, currentTask.task);
|
|
199
|
+
if (!lockCheck.isLocked) {
|
|
200
|
+
await updateTaskStatus(currentTask.pid, "running");
|
|
201
|
+
console.log(`
|
|
202
|
+
✓ Lock released, starting task...`);
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
dots = (dots + 1) % 4;
|
|
206
|
+
process.stdout.write(`\r⏳ Queueing${".".repeat(dots)}${" ".repeat(3 - dots)}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
async function acquireLock(cwd, prompt = "no prompt provided") {
|
|
210
|
+
const resolvedCwd = resolveRealPath(cwd);
|
|
211
|
+
const gitRoot = isGitRepo(resolvedCwd) ? getGitRoot(resolvedCwd) : null;
|
|
212
|
+
const task = {
|
|
213
|
+
cwd: resolvedCwd,
|
|
214
|
+
gitRoot: gitRoot || undefined,
|
|
215
|
+
task: prompt.substring(0, 100),
|
|
216
|
+
pid: process.pid,
|
|
217
|
+
status: "running",
|
|
218
|
+
startedAt: Date.now(),
|
|
219
|
+
lockedAt: Date.now()
|
|
220
|
+
};
|
|
221
|
+
const lockCheck = await checkLock(resolvedCwd, prompt);
|
|
222
|
+
if (lockCheck.isLocked) {
|
|
223
|
+
await waitForUnlock(lockCheck.blockingTasks, task);
|
|
224
|
+
} else {
|
|
225
|
+
await addTask(task);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
async function releaseLock(pid = process.pid) {
|
|
229
|
+
await removeTask(pid);
|
|
230
|
+
}
|
|
231
|
+
async function updateCurrentTaskStatus(status, pid = process.pid) {
|
|
232
|
+
await updateTaskStatus(pid, status);
|
|
233
|
+
}
|
|
234
|
+
function shouldUseLock(cwd) {
|
|
235
|
+
const resolvedCwd = resolveRealPath(cwd);
|
|
236
|
+
return true;
|
|
237
|
+
}
|
|
70
238
|
var CLI_CONFIGURES = {
|
|
71
239
|
grok: {
|
|
72
240
|
install: "npm install -g @vibe-kit/grok-cli",
|
|
@@ -104,7 +272,7 @@ var CLI_CONFIGURES = {
|
|
|
104
272
|
},
|
|
105
273
|
copilot: {
|
|
106
274
|
install: "npm install -g @github/copilot",
|
|
107
|
-
ready: [/^
|
|
275
|
+
ready: [/^ +> /, /Ctrl\+c Exit/],
|
|
108
276
|
enter: [/ │ ❯ 1. Yes, proceed/, /❯ 1. Yes/],
|
|
109
277
|
fatal: []
|
|
110
278
|
},
|
|
@@ -126,13 +294,36 @@ async function claudeYes({
|
|
|
126
294
|
exitOnIdle,
|
|
127
295
|
logFile,
|
|
128
296
|
removeControlCharactersFromStdout = false,
|
|
129
|
-
verbose = false
|
|
297
|
+
verbose = false,
|
|
298
|
+
disableLock = false
|
|
130
299
|
} = {}) {
|
|
131
300
|
const continueArgs = {
|
|
132
301
|
codex: "resume --last".split(" "),
|
|
133
302
|
claude: "--continue".split(" "),
|
|
134
303
|
gemini: []
|
|
135
304
|
};
|
|
305
|
+
const workingDir = cwd ?? process.cwd();
|
|
306
|
+
if (!disableLock && shouldUseLock(workingDir)) {
|
|
307
|
+
await acquireLock(workingDir, prompt ?? "Interactive session");
|
|
308
|
+
}
|
|
309
|
+
const cleanupLock = async () => {
|
|
310
|
+
if (!disableLock && shouldUseLock(workingDir)) {
|
|
311
|
+
await releaseLock().catch(() => null);
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
process.on("exit", () => {
|
|
315
|
+
if (!disableLock) {
|
|
316
|
+
releaseLock().catch(() => null);
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
process.on("SIGINT", async () => {
|
|
320
|
+
await cleanupLock();
|
|
321
|
+
process.exit(130);
|
|
322
|
+
});
|
|
323
|
+
process.on("SIGTERM", async () => {
|
|
324
|
+
await cleanupLock();
|
|
325
|
+
process.exit(143);
|
|
326
|
+
});
|
|
136
327
|
process.stdin.setRawMode?.(true);
|
|
137
328
|
let isFatal = false;
|
|
138
329
|
const stdinReady = new ReadyManager;
|
|
@@ -242,11 +433,15 @@ async function claudeYes({
|
|
|
242
433
|
await sendMessage(prompt);
|
|
243
434
|
const exitCode = await pendingExitCode.promise;
|
|
244
435
|
console.log(`[${cli}-yes] ${cli} exited with code ${exitCode}`);
|
|
436
|
+
if (!disableLock && shouldUseLock(workingDir)) {
|
|
437
|
+
await updateCurrentTaskStatus(exitCode === 0 ? "completed" : "failed").catch(() => null);
|
|
438
|
+
await releaseLock().catch(() => null);
|
|
439
|
+
}
|
|
245
440
|
if (logFile) {
|
|
246
441
|
verbose && console.log(`[${cli}-yes] Writing rendered logs to ${logFile}`);
|
|
247
|
-
const logFilePath =
|
|
248
|
-
await
|
|
249
|
-
await
|
|
442
|
+
const logFilePath = path2.resolve(logFile);
|
|
443
|
+
await mkdir2(path2.dirname(logFilePath), { recursive: true }).catch(() => null);
|
|
444
|
+
await writeFile2(logFilePath, terminalRender.render());
|
|
250
445
|
}
|
|
251
446
|
return { exitCode, logs: terminalRender.render() };
|
|
252
447
|
async function sendEnter(waitms = 1000) {
|
|
@@ -318,6 +513,10 @@ var argv = yargs(hideBin(process.argv)).usage("Usage: $0 [options] [claude args]
|
|
|
318
513
|
type: "string",
|
|
319
514
|
description: 'Exit after a period of inactivity, e.g., "5s" or "1m"',
|
|
320
515
|
alias: "e"
|
|
516
|
+
}).option("disable-lock", {
|
|
517
|
+
type: "boolean",
|
|
518
|
+
description: "Disable the running lock feature that prevents concurrent agents in the same directory/repo",
|
|
519
|
+
default: false
|
|
321
520
|
}).help().version().parserConfiguration({
|
|
322
521
|
"unknown-options-as-args": true,
|
|
323
522
|
"halt-at-non-option": true
|
|
@@ -345,9 +544,10 @@ var { exitCode, logs } = await claudeYes({
|
|
|
345
544
|
cliArgs: cliArgsForSpawn,
|
|
346
545
|
continueOnCrash: argv.continueOnCrash,
|
|
347
546
|
logFile: argv.logFile,
|
|
348
|
-
verbose: argv.verbose
|
|
547
|
+
verbose: argv.verbose,
|
|
548
|
+
disableLock: argv.disableLock
|
|
349
549
|
});
|
|
350
550
|
process.exit(exitCode ?? 1);
|
|
351
551
|
|
|
352
|
-
//# debugId=
|
|
552
|
+
//# debugId=4BC661A586B1849964756E2164756E21
|
|
353
553
|
//# sourceMappingURL=cli.js.map
|
package/dist/grok-yes.js
CHANGED
|
@@ -25,11 +25,16 @@ import { hideBin } from "yargs/helpers";
|
|
|
25
25
|
|
|
26
26
|
// dist/index.js
|
|
27
27
|
import { fromReadable, fromWritable } from "from-node-stream";
|
|
28
|
-
import { mkdir, writeFile } from "fs/promises";
|
|
29
|
-
import
|
|
28
|
+
import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
|
|
29
|
+
import path2 from "path";
|
|
30
30
|
import DIE from "phpdie";
|
|
31
31
|
import sflow from "sflow";
|
|
32
32
|
import { TerminalTextRender } from "terminal-render";
|
|
33
|
+
import { execSync } from "child_process";
|
|
34
|
+
import { existsSync } from "fs";
|
|
35
|
+
import { mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
36
|
+
import { homedir } from "os";
|
|
37
|
+
import path from "path";
|
|
33
38
|
class IdleWaiter {
|
|
34
39
|
lastActivityTime = Date.now();
|
|
35
40
|
checkInterval = 100;
|
|
@@ -67,6 +72,169 @@ class ReadyManager {
|
|
|
67
72
|
function removeControlCharacters(str) {
|
|
68
73
|
return str.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, "");
|
|
69
74
|
}
|
|
75
|
+
var LOCK_DIR = path.join(homedir(), ".claude-yes");
|
|
76
|
+
var LOCK_FILE = path.join(LOCK_DIR, "running.lock.json");
|
|
77
|
+
var MAX_RETRIES = 5;
|
|
78
|
+
var RETRY_DELAYS = [50, 100, 200, 400, 800];
|
|
79
|
+
var POLL_INTERVAL = 2000;
|
|
80
|
+
function isProcessRunning(pid) {
|
|
81
|
+
try {
|
|
82
|
+
process.kill(pid, 0);
|
|
83
|
+
return true;
|
|
84
|
+
} catch {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function getGitRoot(cwd) {
|
|
89
|
+
try {
|
|
90
|
+
const result = execSync("git rev-parse --show-toplevel", {
|
|
91
|
+
cwd,
|
|
92
|
+
encoding: "utf8",
|
|
93
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
94
|
+
});
|
|
95
|
+
return result.trim();
|
|
96
|
+
} catch {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function isGitRepo(cwd) {
|
|
101
|
+
try {
|
|
102
|
+
const gitRoot = getGitRoot(cwd);
|
|
103
|
+
return gitRoot !== null;
|
|
104
|
+
} catch {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function resolveRealPath(p) {
|
|
109
|
+
try {
|
|
110
|
+
return path.resolve(p);
|
|
111
|
+
} catch {
|
|
112
|
+
return p;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function sleep(ms) {
|
|
116
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
117
|
+
}
|
|
118
|
+
async function readLockFile() {
|
|
119
|
+
try {
|
|
120
|
+
await mkdir(LOCK_DIR, { recursive: true });
|
|
121
|
+
if (!existsSync(LOCK_FILE)) {
|
|
122
|
+
return { tasks: [] };
|
|
123
|
+
}
|
|
124
|
+
const content = await readFile(LOCK_FILE, "utf8");
|
|
125
|
+
const lockFile = JSON.parse(content);
|
|
126
|
+
lockFile.tasks = lockFile.tasks.filter((task) => {
|
|
127
|
+
if (isProcessRunning(task.pid))
|
|
128
|
+
return true;
|
|
129
|
+
return false;
|
|
130
|
+
});
|
|
131
|
+
return lockFile;
|
|
132
|
+
} catch (error) {
|
|
133
|
+
return { tasks: [] };
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
async function writeLockFile(lockFile, retryCount = 0) {
|
|
137
|
+
try {
|
|
138
|
+
await mkdir(LOCK_DIR, { recursive: true });
|
|
139
|
+
const tempFile = `${LOCK_FILE}.tmp.${process.pid}`;
|
|
140
|
+
await writeFile(tempFile, JSON.stringify(lockFile, null, 2), "utf8");
|
|
141
|
+
await rename(tempFile, LOCK_FILE);
|
|
142
|
+
} catch (error) {
|
|
143
|
+
if (retryCount < MAX_RETRIES) {
|
|
144
|
+
await sleep(RETRY_DELAYS[retryCount] || 800);
|
|
145
|
+
return writeLockFile(lockFile, retryCount + 1);
|
|
146
|
+
}
|
|
147
|
+
throw error;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
async function checkLock(cwd, prompt) {
|
|
151
|
+
const resolvedCwd = resolveRealPath(cwd);
|
|
152
|
+
const gitRoot = isGitRepo(resolvedCwd) ? getGitRoot(resolvedCwd) : null;
|
|
153
|
+
const lockKey = gitRoot || resolvedCwd;
|
|
154
|
+
const lockFile = await readLockFile();
|
|
155
|
+
const blockingTasks = lockFile.tasks.filter((task) => {
|
|
156
|
+
if (!isProcessRunning(task.pid))
|
|
157
|
+
return false;
|
|
158
|
+
if (task.status !== "running")
|
|
159
|
+
return false;
|
|
160
|
+
if (gitRoot && task.gitRoot) {
|
|
161
|
+
return task.gitRoot === gitRoot;
|
|
162
|
+
} else {
|
|
163
|
+
return task.cwd === lockKey;
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
return {
|
|
167
|
+
isLocked: blockingTasks.length > 0,
|
|
168
|
+
blockingTasks,
|
|
169
|
+
lockKey
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
async function addTask(task) {
|
|
173
|
+
const lockFile = await readLockFile();
|
|
174
|
+
lockFile.tasks = lockFile.tasks.filter((t) => t.pid !== task.pid);
|
|
175
|
+
lockFile.tasks.push(task);
|
|
176
|
+
await writeLockFile(lockFile);
|
|
177
|
+
}
|
|
178
|
+
async function updateTaskStatus(pid, status) {
|
|
179
|
+
const lockFile = await readLockFile();
|
|
180
|
+
const task = lockFile.tasks.find((t) => t.pid === pid);
|
|
181
|
+
if (task) {
|
|
182
|
+
task.status = status;
|
|
183
|
+
await writeLockFile(lockFile);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
async function removeTask(pid) {
|
|
187
|
+
const lockFile = await readLockFile();
|
|
188
|
+
lockFile.tasks = lockFile.tasks.filter((t) => t.pid !== pid);
|
|
189
|
+
await writeLockFile(lockFile);
|
|
190
|
+
}
|
|
191
|
+
async function waitForUnlock(blockingTasks, currentTask) {
|
|
192
|
+
const blockingTask = blockingTasks[0];
|
|
193
|
+
console.log(`⏳ Queueing for unlock of: ${blockingTask.task}`);
|
|
194
|
+
await addTask({ ...currentTask, status: "queued" });
|
|
195
|
+
let dots = 0;
|
|
196
|
+
while (true) {
|
|
197
|
+
await sleep(POLL_INTERVAL);
|
|
198
|
+
const lockCheck = await checkLock(currentTask.cwd, currentTask.task);
|
|
199
|
+
if (!lockCheck.isLocked) {
|
|
200
|
+
await updateTaskStatus(currentTask.pid, "running");
|
|
201
|
+
console.log(`
|
|
202
|
+
✓ Lock released, starting task...`);
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
dots = (dots + 1) % 4;
|
|
206
|
+
process.stdout.write(`\r⏳ Queueing${".".repeat(dots)}${" ".repeat(3 - dots)}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
async function acquireLock(cwd, prompt = "no prompt provided") {
|
|
210
|
+
const resolvedCwd = resolveRealPath(cwd);
|
|
211
|
+
const gitRoot = isGitRepo(resolvedCwd) ? getGitRoot(resolvedCwd) : null;
|
|
212
|
+
const task = {
|
|
213
|
+
cwd: resolvedCwd,
|
|
214
|
+
gitRoot: gitRoot || undefined,
|
|
215
|
+
task: prompt.substring(0, 100),
|
|
216
|
+
pid: process.pid,
|
|
217
|
+
status: "running",
|
|
218
|
+
startedAt: Date.now(),
|
|
219
|
+
lockedAt: Date.now()
|
|
220
|
+
};
|
|
221
|
+
const lockCheck = await checkLock(resolvedCwd, prompt);
|
|
222
|
+
if (lockCheck.isLocked) {
|
|
223
|
+
await waitForUnlock(lockCheck.blockingTasks, task);
|
|
224
|
+
} else {
|
|
225
|
+
await addTask(task);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
async function releaseLock(pid = process.pid) {
|
|
229
|
+
await removeTask(pid);
|
|
230
|
+
}
|
|
231
|
+
async function updateCurrentTaskStatus(status, pid = process.pid) {
|
|
232
|
+
await updateTaskStatus(pid, status);
|
|
233
|
+
}
|
|
234
|
+
function shouldUseLock(cwd) {
|
|
235
|
+
const resolvedCwd = resolveRealPath(cwd);
|
|
236
|
+
return true;
|
|
237
|
+
}
|
|
70
238
|
var CLI_CONFIGURES = {
|
|
71
239
|
grok: {
|
|
72
240
|
install: "npm install -g @vibe-kit/grok-cli",
|
|
@@ -104,7 +272,7 @@ var CLI_CONFIGURES = {
|
|
|
104
272
|
},
|
|
105
273
|
copilot: {
|
|
106
274
|
install: "npm install -g @github/copilot",
|
|
107
|
-
ready: [/^
|
|
275
|
+
ready: [/^ +> /, /Ctrl\+c Exit/],
|
|
108
276
|
enter: [/ │ ❯ 1. Yes, proceed/, /❯ 1. Yes/],
|
|
109
277
|
fatal: []
|
|
110
278
|
},
|
|
@@ -126,13 +294,36 @@ async function claudeYes({
|
|
|
126
294
|
exitOnIdle,
|
|
127
295
|
logFile,
|
|
128
296
|
removeControlCharactersFromStdout = false,
|
|
129
|
-
verbose = false
|
|
297
|
+
verbose = false,
|
|
298
|
+
disableLock = false
|
|
130
299
|
} = {}) {
|
|
131
300
|
const continueArgs = {
|
|
132
301
|
codex: "resume --last".split(" "),
|
|
133
302
|
claude: "--continue".split(" "),
|
|
134
303
|
gemini: []
|
|
135
304
|
};
|
|
305
|
+
const workingDir = cwd ?? process.cwd();
|
|
306
|
+
if (!disableLock && shouldUseLock(workingDir)) {
|
|
307
|
+
await acquireLock(workingDir, prompt ?? "Interactive session");
|
|
308
|
+
}
|
|
309
|
+
const cleanupLock = async () => {
|
|
310
|
+
if (!disableLock && shouldUseLock(workingDir)) {
|
|
311
|
+
await releaseLock().catch(() => null);
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
process.on("exit", () => {
|
|
315
|
+
if (!disableLock) {
|
|
316
|
+
releaseLock().catch(() => null);
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
process.on("SIGINT", async () => {
|
|
320
|
+
await cleanupLock();
|
|
321
|
+
process.exit(130);
|
|
322
|
+
});
|
|
323
|
+
process.on("SIGTERM", async () => {
|
|
324
|
+
await cleanupLock();
|
|
325
|
+
process.exit(143);
|
|
326
|
+
});
|
|
136
327
|
process.stdin.setRawMode?.(true);
|
|
137
328
|
let isFatal = false;
|
|
138
329
|
const stdinReady = new ReadyManager;
|
|
@@ -242,11 +433,15 @@ async function claudeYes({
|
|
|
242
433
|
await sendMessage(prompt);
|
|
243
434
|
const exitCode = await pendingExitCode.promise;
|
|
244
435
|
console.log(`[${cli}-yes] ${cli} exited with code ${exitCode}`);
|
|
436
|
+
if (!disableLock && shouldUseLock(workingDir)) {
|
|
437
|
+
await updateCurrentTaskStatus(exitCode === 0 ? "completed" : "failed").catch(() => null);
|
|
438
|
+
await releaseLock().catch(() => null);
|
|
439
|
+
}
|
|
245
440
|
if (logFile) {
|
|
246
441
|
verbose && console.log(`[${cli}-yes] Writing rendered logs to ${logFile}`);
|
|
247
|
-
const logFilePath =
|
|
248
|
-
await
|
|
249
|
-
await
|
|
442
|
+
const logFilePath = path2.resolve(logFile);
|
|
443
|
+
await mkdir2(path2.dirname(logFilePath), { recursive: true }).catch(() => null);
|
|
444
|
+
await writeFile2(logFilePath, terminalRender.render());
|
|
250
445
|
}
|
|
251
446
|
return { exitCode, logs: terminalRender.render() };
|
|
252
447
|
async function sendEnter(waitms = 1000) {
|
|
@@ -318,6 +513,10 @@ var argv = yargs(hideBin(process.argv)).usage("Usage: $0 [options] [claude args]
|
|
|
318
513
|
type: "string",
|
|
319
514
|
description: 'Exit after a period of inactivity, e.g., "5s" or "1m"',
|
|
320
515
|
alias: "e"
|
|
516
|
+
}).option("disable-lock", {
|
|
517
|
+
type: "boolean",
|
|
518
|
+
description: "Disable the running lock feature that prevents concurrent agents in the same directory/repo",
|
|
519
|
+
default: false
|
|
321
520
|
}).help().version().parserConfiguration({
|
|
322
521
|
"unknown-options-as-args": true,
|
|
323
522
|
"halt-at-non-option": true
|
|
@@ -345,9 +544,10 @@ var { exitCode, logs } = await claudeYes({
|
|
|
345
544
|
cliArgs: cliArgsForSpawn,
|
|
346
545
|
continueOnCrash: argv.continueOnCrash,
|
|
347
546
|
logFile: argv.logFile,
|
|
348
|
-
verbose: argv.verbose
|
|
547
|
+
verbose: argv.verbose,
|
|
548
|
+
disableLock: argv.disableLock
|
|
349
549
|
});
|
|
350
550
|
process.exit(exitCode ?? 1);
|
|
351
551
|
|
|
352
|
-
//# debugId=
|
|
552
|
+
//# debugId=4BC661A586B1849964756E2164756E21
|
|
353
553
|
//# sourceMappingURL=cli.js.map
|