prompts-gpt 0.3.3 → 0.3.4
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/dist/cli.js +202 -135
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -3
- package/dist/index.js.map +1 -1
- package/dist/model-registry.d.ts +8 -0
- package/dist/model-registry.d.ts.map +1 -0
- package/dist/model-registry.js +76 -0
- package/dist/model-registry.js.map +1 -0
- package/dist/orchestrate.d.ts +1 -0
- package/dist/orchestrate.d.ts.map +1 -1
- package/dist/orchestrate.js +64 -27
- package/dist/orchestrate.js.map +1 -1
- package/dist/runtime.d.ts +4 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +158 -40
- package/dist/runtime.js.map +1 -1
- package/dist/sweep.d.ts.map +1 -1
- package/dist/sweep.js +45 -33
- package/dist/sweep.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,8 @@ import { existsSync, readFileSync, statSync, readdirSync } from "node:fs";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { parseArgs } from "node:util";
|
|
6
|
-
import { hasTokenUsage, DEFAULT_PROMPTS_GPT_API_URL, DEFAULT_PROMPTS_GPT_OUT_DIR, DEFAULT_RUN_CONFIG_PATH, PROMPTS_GPT_CREDENTIALS_FILE, PromptsGptApiError, PromptsGptClient, doctor, initRunConfig, loadRunConfig, normalizeConcreteProvider, normalizeOrchestrationAgent, ORCHESTRATION_AGENT_PROFILES, runBatch, runPrompt, resolveRunProvider, warnModelProviderMismatch, sweepPrompt, validateRunConfig, discoverWorkspaceAssets, SUPPORTED_AGENT_TARGETS, detectProviders, loadLocalCredentials, saveLocalCredentials, syncPrompts, writeAgentFiles, writePromptManifest, writePromptMarkdownFiles, ensureGitignoreEntry, isCI, orchestrateParallel, orchestratePipeline, orchestrateEval, captureGitBranch, resolveModelWithWarning, getModelCostTier, estimateTokenCost, DEFAULT_MODELS, } from "./index.js";
|
|
6
|
+
import { hasTokenUsage, DEFAULT_PROMPTS_GPT_API_URL, DEFAULT_PROMPTS_GPT_OUT_DIR, DEFAULT_RUN_CONFIG_PATH, PROMPTS_GPT_CREDENTIALS_FILE, PromptsGptApiError, PromptsGptClient, doctor, initRunConfig, loadRunConfig, normalizeConcreteProvider, normalizeOrchestrationAgent, ORCHESTRATION_AGENT_PROFILES, runBatch, runPrompt, resolveRunProvider, warnModelProviderMismatch, sweepPrompt, validateRunConfig, discoverWorkspaceAssets, SUPPORTED_AGENT_TARGETS, detectProviders, loadLocalCredentials, saveLocalCredentials, syncPrompts, writeAgentFiles, writePromptManifest, writePromptMarkdownFiles, ensureGitignoreEntry, isProcessAlive, isCI, orchestrateParallel, orchestratePipeline, orchestrateEval, captureGitBranch, resolveModelWithWarning, getModelCostTier, estimateTokenCost, DEFAULT_MODELS, } from "./index.js";
|
|
7
|
+
import { PROVIDER_MODELS } from "./model-registry.js";
|
|
7
8
|
const CLI_EXIT_CODES = {
|
|
8
9
|
success: 0,
|
|
9
10
|
general: 1,
|
|
@@ -210,7 +211,7 @@ async function runCommand(command, flags) {
|
|
|
210
211
|
if (setupPicked === "list") {
|
|
211
212
|
const { spawnSync: spSync } = await import("node:child_process");
|
|
212
213
|
const cliEntry = resolveCliEntry();
|
|
213
|
-
spSync(process.execPath, [cliEntry, "list"], { stdio: "inherit", cwd });
|
|
214
|
+
spSync(process.execPath, [cliEntry, "list"], { stdio: "inherit", cwd, shell: false, windowsHide: true });
|
|
214
215
|
}
|
|
215
216
|
else if (setupPicked !== "done") {
|
|
216
217
|
const [action, file] = setupPicked.split(":", 2);
|
|
@@ -218,7 +219,7 @@ async function runCommand(command, flags) {
|
|
|
218
219
|
console.log(`\nRunning: prompts-gpt ${cmd} -f ${file}\n`);
|
|
219
220
|
const { spawnSync: spSync } = await import("node:child_process");
|
|
220
221
|
const cliEntry = resolveCliEntry();
|
|
221
|
-
const setupResult = spSync(process.execPath, [cliEntry, cmd, "-f", file], { stdio: "inherit", cwd });
|
|
222
|
+
const setupResult = spSync(process.execPath, [cliEntry, cmd, "-f", file], { stdio: "inherit", cwd, shell: false, windowsHide: true });
|
|
222
223
|
process.exitCode = setupResult.status ?? 1;
|
|
223
224
|
}
|
|
224
225
|
}
|
|
@@ -496,12 +497,7 @@ async function runCommand(command, flags) {
|
|
|
496
497
|
const lockContent = JSON.parse(readFileSync(sweepLockPath, "utf8"));
|
|
497
498
|
const lockAge = Date.now() - new Date(lockContent.startedAt ?? 0).getTime();
|
|
498
499
|
const lockAgeHours = lockAge / (60 * 60 * 1000);
|
|
499
|
-
|
|
500
|
-
try {
|
|
501
|
-
process.kill(lockContent.pid ?? 0, 0);
|
|
502
|
-
holderAlive = true;
|
|
503
|
-
}
|
|
504
|
-
catch { /* dead */ }
|
|
500
|
+
const holderAlive = isProcessAlive(lockContent.pid);
|
|
505
501
|
if (!holderAlive) {
|
|
506
502
|
console.log(`\n${sym("⚠", "!")} Stale sweep lock detected (PID ${lockContent.pid} is no longer running)`);
|
|
507
503
|
console.log(` Started: ${lockContent.startedAt ?? "unknown"}`);
|
|
@@ -673,7 +669,7 @@ async function runCommand(command, flags) {
|
|
|
673
669
|
console.log(`\nRunning: prompts-gpt ${cmd} -f ${file}\n`);
|
|
674
670
|
const { spawnSync: spSync } = await import("node:child_process");
|
|
675
671
|
const cliEntry = resolveCliEntry();
|
|
676
|
-
const result = spSync(process.execPath, [cliEntry, cmd, "-f", file], { stdio: "inherit", cwd });
|
|
672
|
+
const result = spSync(process.execPath, [cliEntry, cmd, "-f", file], { stdio: "inherit", cwd, shell: false, windowsHide: true });
|
|
677
673
|
process.exitCode = result.status ?? 1;
|
|
678
674
|
}
|
|
679
675
|
}
|
|
@@ -826,6 +822,9 @@ async function runCommand(command, flags) {
|
|
|
826
822
|
}
|
|
827
823
|
if (command === "run") {
|
|
828
824
|
const cwd = getResolvedCwd(flags);
|
|
825
|
+
if (isCI() && !flags["non-interactive"]) {
|
|
826
|
+
flags["non-interactive"] = true;
|
|
827
|
+
}
|
|
829
828
|
const promptFile = getStringFlag(flags, "prompt-file");
|
|
830
829
|
const config = await loadRunConfig(cwd);
|
|
831
830
|
warnOnConfigIssues(config);
|
|
@@ -1014,6 +1013,10 @@ async function runCommand(command, flags) {
|
|
|
1014
1013
|
const doRun = async () => {
|
|
1015
1014
|
if (running)
|
|
1016
1015
|
return;
|
|
1016
|
+
if (!existsSync(resolvedWatch)) {
|
|
1017
|
+
console.log(` File deleted: ${resolvedWatch}. Skipping run.`);
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1017
1020
|
running = true;
|
|
1018
1021
|
console.log(`\n${colorize("▶ Running...", "\x1b[36m")} (${new Date().toLocaleTimeString()})`);
|
|
1019
1022
|
try {
|
|
@@ -1041,20 +1044,28 @@ async function runCommand(command, flags) {
|
|
|
1041
1044
|
};
|
|
1042
1045
|
await doRun();
|
|
1043
1046
|
let debounce = null;
|
|
1047
|
+
const closeWatcher = (message) => {
|
|
1048
|
+
if (debounce) {
|
|
1049
|
+
clearTimeout(debounce);
|
|
1050
|
+
debounce = null;
|
|
1051
|
+
}
|
|
1052
|
+
if (message)
|
|
1053
|
+
console.log(message);
|
|
1054
|
+
watcher.close();
|
|
1055
|
+
};
|
|
1044
1056
|
const watcher = fsWatch(resolvedWatch, () => {
|
|
1057
|
+
if (!existsSync(resolvedWatch)) {
|
|
1058
|
+
closeWatcher(` File deleted: ${resolvedWatch}. Exiting watch mode.`);
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
1045
1061
|
if (debounce)
|
|
1046
1062
|
clearTimeout(debounce);
|
|
1047
1063
|
debounce = setTimeout(doRun, 500);
|
|
1048
1064
|
debounce.unref?.();
|
|
1049
1065
|
});
|
|
1050
1066
|
watcher.on("error", (err) => {
|
|
1051
|
-
if (debounce) {
|
|
1052
|
-
clearTimeout(debounce);
|
|
1053
|
-
debounce = null;
|
|
1054
|
-
}
|
|
1055
1067
|
console.error(`Watch error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1056
|
-
|
|
1057
|
-
watcher.close();
|
|
1068
|
+
closeWatcher("File may have been deleted. Exiting watch mode.");
|
|
1058
1069
|
});
|
|
1059
1070
|
await new Promise((resolve) => {
|
|
1060
1071
|
let settled = false;
|
|
@@ -1141,11 +1152,11 @@ async function runCommand(command, flags) {
|
|
|
1141
1152
|
try {
|
|
1142
1153
|
const { spawn: openSpawn } = await import("node:child_process");
|
|
1143
1154
|
if (process.platform === "win32") {
|
|
1144
|
-
openSpawn("cmd", ["/c", "start", "", result.summaryFile], { detached: true, stdio: "ignore", windowsHide: true }).unref();
|
|
1155
|
+
openSpawn("cmd", ["/c", "start", "", result.summaryFile], { detached: true, stdio: "ignore", windowsHide: true, shell: false }).unref();
|
|
1145
1156
|
}
|
|
1146
1157
|
else {
|
|
1147
1158
|
const openCmd = process.platform === "darwin" ? "open" : "xdg-open";
|
|
1148
|
-
openSpawn(openCmd, [result.summaryFile], { detached: true, stdio: "ignore" }).unref();
|
|
1159
|
+
openSpawn(openCmd, [result.summaryFile], { detached: true, stdio: "ignore", shell: false }).unref();
|
|
1149
1160
|
}
|
|
1150
1161
|
}
|
|
1151
1162
|
catch { /* ignore */ }
|
|
@@ -1965,11 +1976,11 @@ async function runCommand(command, flags) {
|
|
|
1965
1976
|
try {
|
|
1966
1977
|
const { spawn: openSpawn } = await import("node:child_process");
|
|
1967
1978
|
if (process.platform === "win32") {
|
|
1968
|
-
openSpawn("cmd", ["/c", "start", "", result.manifestFile], { detached: true, stdio: "ignore", windowsHide: true }).unref();
|
|
1979
|
+
openSpawn("cmd", ["/c", "start", "", result.manifestFile], { detached: true, stdio: "ignore", windowsHide: true, shell: false }).unref();
|
|
1969
1980
|
}
|
|
1970
1981
|
else {
|
|
1971
1982
|
const openCmd = process.platform === "darwin" ? "open" : "xdg-open";
|
|
1972
|
-
openSpawn(openCmd, [result.manifestFile], { detached: true, stdio: "ignore" }).unref();
|
|
1983
|
+
openSpawn(openCmd, [result.manifestFile], { detached: true, stdio: "ignore", shell: false }).unref();
|
|
1973
1984
|
}
|
|
1974
1985
|
}
|
|
1975
1986
|
catch { /* ignore */ }
|
|
@@ -2181,15 +2192,12 @@ async function runCommand(command, flags) {
|
|
|
2181
2192
|
let pipelineDefaults = {};
|
|
2182
2193
|
try {
|
|
2183
2194
|
const parsed = JSON.parse(readFileSync(resolvedStepsFile, "utf8"));
|
|
2184
|
-
if (
|
|
2195
|
+
if (isPipelineStepArray(parsed)) {
|
|
2185
2196
|
stepsJson = parsed;
|
|
2186
2197
|
}
|
|
2187
|
-
else if (
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
if (wrapper.defaults && typeof wrapper.defaults === "object") {
|
|
2191
|
-
pipelineDefaults = wrapper.defaults;
|
|
2192
|
-
}
|
|
2198
|
+
else if (isPipelineConfigObject(parsed)) {
|
|
2199
|
+
stepsJson = parsed.steps;
|
|
2200
|
+
pipelineDefaults = parsed.defaults ?? {};
|
|
2193
2201
|
}
|
|
2194
2202
|
else {
|
|
2195
2203
|
throw new Error("Pipeline config must be an array of steps or an object with { defaults?, steps }.");
|
|
@@ -2213,9 +2221,13 @@ async function runCommand(command, flags) {
|
|
|
2213
2221
|
if (!step || typeof step.name !== "string" || typeof step.promptFile !== "string") {
|
|
2214
2222
|
throw new CliError(`Pipeline step entries must include name and promptFile fields: ${resolvedStepsFile}`, CLI_EXIT_CODES.validation);
|
|
2215
2223
|
}
|
|
2216
|
-
|
|
2224
|
+
const stepPromptPath = path.resolve(cwd, step.promptFile);
|
|
2225
|
+
if (!existsSync(stepPromptPath)) {
|
|
2217
2226
|
throw new CliError(`Pipeline step "${step.name}" references missing prompt file: ${step.promptFile}`, CLI_EXIT_CODES.usage);
|
|
2218
2227
|
}
|
|
2228
|
+
if (!statSync(stepPromptPath).isFile()) {
|
|
2229
|
+
throw new CliError(`Pipeline step "${step.name}" promptFile is not a file: ${step.promptFile}`, CLI_EXIT_CODES.usage);
|
|
2230
|
+
}
|
|
2219
2231
|
const effectiveAgent = typeof step.agent === "string" && step.agent.trim() ? step.agent : defaultPipelineAgent;
|
|
2220
2232
|
normalizeOrchestrationAgent(effectiveAgent);
|
|
2221
2233
|
step.agent = effectiveAgent;
|
|
@@ -2441,7 +2453,19 @@ async function runCommand(command, flags) {
|
|
|
2441
2453
|
if (!existsSync(runDir)) {
|
|
2442
2454
|
throw new CliError(`Run directory not found: ${runDir}`, CLI_EXIT_CODES.usage);
|
|
2443
2455
|
}
|
|
2444
|
-
const { readFile: fsRead } = await import("node:fs/promises");
|
|
2456
|
+
const { lstat: fsLstat, readFile: fsRead, realpath: fsRealpath } = await import("node:fs/promises");
|
|
2457
|
+
const runDirStat = await fsLstat(runDir).catch((error) => {
|
|
2458
|
+
throw new CliError(`Cannot inspect run directory ${runDir}: ${error instanceof Error ? error.message : String(error)}`, CLI_EXIT_CODES.usage);
|
|
2459
|
+
});
|
|
2460
|
+
if (!runDirStat.isDirectory() || runDirStat.isSymbolicLink()) {
|
|
2461
|
+
throw new CliError(`Invalid run-id: ${runId} is not a regular run directory.`, CLI_EXIT_CODES.usage);
|
|
2462
|
+
}
|
|
2463
|
+
const [realRunDir, realArtifactsDir] = await Promise.all([fsRealpath(runDir), fsRealpath(artifactsDir)]);
|
|
2464
|
+
const realRunDirCheck = caseInsensitive ? realRunDir.toLowerCase() : realRunDir;
|
|
2465
|
+
const realArtifactsDirCheck = caseInsensitive ? realArtifactsDir.toLowerCase() : realArtifactsDir;
|
|
2466
|
+
if (!realRunDirCheck.startsWith(realArtifactsDirCheck + path.sep) && realRunDirCheck !== realArtifactsDirCheck) {
|
|
2467
|
+
throw new CliError("Invalid run-id: resolved path escapes the artifacts directory.", CLI_EXIT_CODES.usage);
|
|
2468
|
+
}
|
|
2445
2469
|
const deltaFile = path.join(runDir, "worktree-delta.diff");
|
|
2446
2470
|
if (existsSync(deltaFile)) {
|
|
2447
2471
|
const delta = await fsRead(deltaFile, "utf8");
|
|
@@ -2484,7 +2508,7 @@ async function runCommand(command, flags) {
|
|
|
2484
2508
|
console.log("======================");
|
|
2485
2509
|
console.log("");
|
|
2486
2510
|
const { spawnSync: gitCheck } = await import("node:child_process");
|
|
2487
|
-
const gitResult = gitCheck("git", ["rev-parse", "--is-inside-work-tree"], { cwd, encoding: "utf8", timeout: 5000, windowsHide: true });
|
|
2511
|
+
const gitResult = gitCheck("git", ["rev-parse", "--is-inside-work-tree"], { cwd, encoding: "utf8", timeout: 5000, windowsHide: true, shell: false });
|
|
2488
2512
|
if (gitResult.status !== 0) {
|
|
2489
2513
|
console.log(`${sym("⚠", "!")} Not inside a git repository. Prompts-GPT works best in a git repo for worktree tracking.`);
|
|
2490
2514
|
console.log(" Run: git init\n");
|
|
@@ -2619,7 +2643,7 @@ async function runCommand(command, flags) {
|
|
|
2619
2643
|
console.log(`\nRunning: prompts-gpt ${cmd} -f ${file}\n`);
|
|
2620
2644
|
const { spawnSync: spSync } = await import("node:child_process");
|
|
2621
2645
|
const cliEntry = resolveCliEntry();
|
|
2622
|
-
const result = spSync(process.execPath, [cliEntry, cmd, "-f", file], { stdio: "inherit", cwd });
|
|
2646
|
+
const result = spSync(process.execPath, [cliEntry, cmd, "-f", file], { stdio: "inherit", cwd, shell: false, windowsHide: true });
|
|
2623
2647
|
process.exitCode = result.status ?? 1;
|
|
2624
2648
|
return;
|
|
2625
2649
|
}
|
|
@@ -2757,7 +2781,7 @@ async function runCommand(command, flags) {
|
|
|
2757
2781
|
if (runQs.toLowerCase() !== "n" && runQs.toLowerCase() !== "no") {
|
|
2758
2782
|
const { spawnSync: spSync } = await import("node:child_process");
|
|
2759
2783
|
const cliEntry = resolveCliEntry();
|
|
2760
|
-
const qsResult = spSync(process.execPath, [cliEntry, "quickstart", "--cwd", cwd], { stdio: "inherit", cwd });
|
|
2784
|
+
const qsResult = spSync(process.execPath, [cliEntry, "quickstart", "--cwd", cwd], { stdio: "inherit", cwd, shell: false, windowsHide: true });
|
|
2761
2785
|
process.exitCode = qsResult.status ?? 1;
|
|
2762
2786
|
return;
|
|
2763
2787
|
}
|
|
@@ -2865,7 +2889,7 @@ async function runCommand(command, flags) {
|
|
|
2865
2889
|
console.log(`\nRunning: prompts-gpt run -f ${genFile}\n`);
|
|
2866
2890
|
const { spawnSync: spSync } = await import("node:child_process");
|
|
2867
2891
|
const cliEntry = resolveCliEntry();
|
|
2868
|
-
const genResult = spSync(process.execPath, [cliEntry, "run", "-f", genFile], { stdio: "inherit", cwd });
|
|
2892
|
+
const genResult = spSync(process.execPath, [cliEntry, "run", "-f", genFile], { stdio: "inherit", cwd, shell: false, windowsHide: true });
|
|
2869
2893
|
process.exitCode = genResult.status ?? 1;
|
|
2870
2894
|
}
|
|
2871
2895
|
}
|
|
@@ -3153,7 +3177,7 @@ async function runCommand(command, flags) {
|
|
|
3153
3177
|
const { spawnSync: spSync } = await import("node:child_process");
|
|
3154
3178
|
const cliEntry = resolveCliEntry();
|
|
3155
3179
|
const projCwd = getResolvedCwd(flags);
|
|
3156
|
-
const syncResult = spSync(process.execPath, [cliEntry, "sync", "--cwd", projCwd], { stdio: "inherit", cwd: projCwd });
|
|
3180
|
+
const syncResult = spSync(process.execPath, [cliEntry, "sync", "--cwd", projCwd], { stdio: "inherit", cwd: projCwd, shell: false, windowsHide: true });
|
|
3157
3181
|
process.exitCode = syncResult.status ?? 1;
|
|
3158
3182
|
}
|
|
3159
3183
|
}
|
|
@@ -3748,6 +3772,7 @@ function readTokenFromPrompt(command) {
|
|
|
3748
3772
|
stdin.setRawMode?.(true);
|
|
3749
3773
|
}
|
|
3750
3774
|
catch {
|
|
3775
|
+
cleanup();
|
|
3751
3776
|
reject(new CliError("Cannot enable raw mode for token prompt. Use --token-stdin instead.", CLI_EXIT_CODES.usage, { helpCommand: command }));
|
|
3752
3777
|
return;
|
|
3753
3778
|
}
|
|
@@ -3835,15 +3860,54 @@ async function applyDoctorFixes(cwd) {
|
|
|
3835
3860
|
skipped.push(`${DEFAULT_PROMPTS_GPT_OUT_DIR}/ directory already exists`);
|
|
3836
3861
|
}
|
|
3837
3862
|
try {
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3863
|
+
const gitignoreEntries = [
|
|
3864
|
+
".prompts-gpt/.credentials.json",
|
|
3865
|
+
".prompts-gpt/.models.json",
|
|
3866
|
+
".scripts/runs",
|
|
3867
|
+
".sweep.lock",
|
|
3868
|
+
];
|
|
3869
|
+
const gitignoreUpdates = [];
|
|
3870
|
+
for (const entry of gitignoreEntries) {
|
|
3871
|
+
gitignoreUpdates.push(await ensureGitignoreEntry(cwd, entry));
|
|
3872
|
+
}
|
|
3873
|
+
if (gitignoreUpdates.some(Boolean)) {
|
|
3874
|
+
applied.push("Updated .gitignore with sensitive file and sweep artifact patterns");
|
|
3875
|
+
}
|
|
3876
|
+
else {
|
|
3877
|
+
skipped.push(".gitignore already contains sensitive file and sweep artifact patterns");
|
|
3878
|
+
}
|
|
3843
3879
|
}
|
|
3844
3880
|
catch {
|
|
3845
3881
|
failed.push("Could not update .gitignore with sensitive file and sweep artifact patterns");
|
|
3846
3882
|
}
|
|
3883
|
+
const sweepLockPath = path.resolve(cwd, ".sweep.lock");
|
|
3884
|
+
if (existsSync(sweepLockPath)) {
|
|
3885
|
+
try {
|
|
3886
|
+
const lockRaw = readFileSync(sweepLockPath, "utf8");
|
|
3887
|
+
const lockData = JSON.parse(lockRaw);
|
|
3888
|
+
const lockAge = Date.now() - new Date(lockData.startedAt ?? 0).getTime();
|
|
3889
|
+
const lockAgeHours = lockAge / (60 * 60 * 1000);
|
|
3890
|
+
const holderAlive = isProcessAlive(lockData.pid);
|
|
3891
|
+
if (!holderAlive || (Number.isFinite(lockAgeHours) && lockAgeHours >= 4)) {
|
|
3892
|
+
const { rm: fsRm } = await import("node:fs/promises");
|
|
3893
|
+
await fsRm(sweepLockPath, { force: true });
|
|
3894
|
+
applied.push("Removed stale sweep lock");
|
|
3895
|
+
}
|
|
3896
|
+
else {
|
|
3897
|
+
skipped.push("Sweep lock is active (held by running process)");
|
|
3898
|
+
}
|
|
3899
|
+
}
|
|
3900
|
+
catch {
|
|
3901
|
+
try {
|
|
3902
|
+
const { rm: fsRm } = await import("node:fs/promises");
|
|
3903
|
+
await fsRm(sweepLockPath, { force: true });
|
|
3904
|
+
applied.push("Removed corrupt sweep lock file");
|
|
3905
|
+
}
|
|
3906
|
+
catch {
|
|
3907
|
+
failed.push("Could not remove corrupt sweep lock file");
|
|
3908
|
+
}
|
|
3909
|
+
}
|
|
3910
|
+
}
|
|
3847
3911
|
return { applied, skipped, failed };
|
|
3848
3912
|
}
|
|
3849
3913
|
function getStringFlag(flags, name) {
|
|
@@ -3939,6 +4003,51 @@ function truncatePreview(text, maxLen) {
|
|
|
3939
4003
|
return clean;
|
|
3940
4004
|
return clean.slice(0, maxLen - 3) + "...";
|
|
3941
4005
|
}
|
|
4006
|
+
function isPlainRecord(value) {
|
|
4007
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
4008
|
+
}
|
|
4009
|
+
function isOptionalString(value) {
|
|
4010
|
+
return value === undefined || typeof value === "string";
|
|
4011
|
+
}
|
|
4012
|
+
function isOptionalNumber(value) {
|
|
4013
|
+
return value === undefined || (typeof value === "number" && Number.isFinite(value));
|
|
4014
|
+
}
|
|
4015
|
+
function isStringRecord(value) {
|
|
4016
|
+
if (value === undefined)
|
|
4017
|
+
return true;
|
|
4018
|
+
if (!isPlainRecord(value))
|
|
4019
|
+
return false;
|
|
4020
|
+
return Object.entries(value).every(([key, entry]) => key !== "__proto__" && key !== "constructor" && key !== "prototype" && typeof entry === "string");
|
|
4021
|
+
}
|
|
4022
|
+
function isPipelineStepEntry(value) {
|
|
4023
|
+
if (!isPlainRecord(value))
|
|
4024
|
+
return false;
|
|
4025
|
+
return typeof value.name === "string" &&
|
|
4026
|
+
typeof value.promptFile === "string" &&
|
|
4027
|
+
isOptionalString(value.agent) &&
|
|
4028
|
+
isOptionalString(value.model) &&
|
|
4029
|
+
isOptionalNumber(value.timeout) &&
|
|
4030
|
+
isOptionalNumber(value.retries) &&
|
|
4031
|
+
isOptionalString(value.sandboxMode) &&
|
|
4032
|
+
isStringRecord(value.env);
|
|
4033
|
+
}
|
|
4034
|
+
function isPipelineStepArray(value) {
|
|
4035
|
+
return Array.isArray(value) && value.every(isPipelineStepEntry);
|
|
4036
|
+
}
|
|
4037
|
+
function isPipelineDefaults(value) {
|
|
4038
|
+
if (value === undefined)
|
|
4039
|
+
return true;
|
|
4040
|
+
if (!isPlainRecord(value))
|
|
4041
|
+
return false;
|
|
4042
|
+
return isOptionalString(value.agent) &&
|
|
4043
|
+
isOptionalString(value.model) &&
|
|
4044
|
+
isOptionalNumber(value.timeout) &&
|
|
4045
|
+
isOptionalNumber(value.retries) &&
|
|
4046
|
+
isOptionalString(value.sandboxMode);
|
|
4047
|
+
}
|
|
4048
|
+
function isPipelineConfigObject(value) {
|
|
4049
|
+
return isPlainRecord(value) && isPipelineStepArray(value.steps) && isPipelineDefaults(value.defaults);
|
|
4050
|
+
}
|
|
3942
4051
|
function formatList(values) {
|
|
3943
4052
|
if (!Array.isArray(values) || values.length === 0)
|
|
3944
4053
|
return "None";
|
|
@@ -4149,82 +4258,6 @@ async function saveLastUsedModel(cwd, provider, model) {
|
|
|
4149
4258
|
}
|
|
4150
4259
|
const CODEX_API_KEY_ENV_NAMES = ["OPENAI_API_KEY", "CODEX_API_KEY", "OPENAI_API_KEY_FILE"];
|
|
4151
4260
|
const CODEX_CHATGPT_UNSUPPORTED_MODELS = new Set(["gpt-5.5-pro", "gpt-5.4-pro", "gpt-5.5-high-fast"]);
|
|
4152
|
-
const PROVIDER_MODELS = Object.freeze({
|
|
4153
|
-
codex: [
|
|
4154
|
-
{ value: "gpt-5.5", label: "gpt-5.5 — frontier coding & reasoning", tier: "frontier" },
|
|
4155
|
-
{ value: "gpt-5.5-pro", label: "gpt-5.5-pro — smarter, more precise", tier: "frontier" },
|
|
4156
|
-
{ value: "gpt-5.4", label: "gpt-5.4 — strong coding model", tier: "standard" },
|
|
4157
|
-
{ value: "gpt-5.4-pro", label: "gpt-5.4-pro — enhanced responses", tier: "standard" },
|
|
4158
|
-
{ value: "gpt-5.4-mini", label: "gpt-5.4-mini — fast coding & subagents", tier: "fast" },
|
|
4159
|
-
{ value: "gpt-5.4-nano", label: "gpt-5.4-nano — cheapest high-volume", tier: "budget" },
|
|
4160
|
-
{ value: "gpt-5.3-codex", label: "gpt-5.3-codex — merged GPT-5 + Codex", tier: "standard" },
|
|
4161
|
-
{ value: "gpt-5.3-codex-spark", label: "gpt-5.3-codex-spark — 15x faster gen", tier: "fast" },
|
|
4162
|
-
{ value: "o3", label: "o3 — advanced reasoning", tier: "frontier" },
|
|
4163
|
-
],
|
|
4164
|
-
claude: [
|
|
4165
|
-
{ value: "claude-opus-4-7", label: "claude-opus-4-7 — most capable model", tier: "frontier" },
|
|
4166
|
-
{ value: "claude-opus-4-6", label: "claude-opus-4-6 — previous gen opus", tier: "frontier" },
|
|
4167
|
-
{ value: "claude-sonnet-4-6", label: "claude-sonnet-4-6 — balanced default", tier: "standard" },
|
|
4168
|
-
{ value: "claude-sonnet-4-5", label: "claude-sonnet-4-5 — previous gen sonnet", tier: "standard" },
|
|
4169
|
-
{ value: "claude-3-5-haiku", label: "claude-3-5-haiku — fastest & cheapest", tier: "fast" },
|
|
4170
|
-
{ value: "claude-haiku-4-5", label: "claude-haiku-4-5 — fast haiku", tier: "fast" },
|
|
4171
|
-
],
|
|
4172
|
-
cursor: [
|
|
4173
|
-
{ value: "auto", label: "auto — Cursor auto-selects best", tier: "standard" },
|
|
4174
|
-
{ value: "claude-4.7-opus", label: "claude-4.7-opus — Claude Opus 4.7", tier: "frontier" },
|
|
4175
|
-
{ value: "claude-4.7-opus-fast", label: "claude-4.7-opus-fast — Claude Opus 4.7 Fast", tier: "frontier" },
|
|
4176
|
-
{ value: "claude-4.6-opus-high", label: "claude-4.6-opus-high — Claude Opus 4.6 High", tier: "frontier" },
|
|
4177
|
-
{ value: "claude-4.6-opus-high-thinking", label: "claude-4.6-opus-high-thinking — Claude Opus 4.6 Thinking", tier: "frontier" },
|
|
4178
|
-
{ value: "claude-4.6-sonnet-high", label: "claude-4.6-sonnet-high — fast + smart", tier: "standard" },
|
|
4179
|
-
{ value: "gpt-5.5", label: "gpt-5.5 — OpenAI frontier", tier: "frontier" },
|
|
4180
|
-
{ value: "gpt-5.5-high-fast", label: "gpt-5.5-high-fast — GPT frontier fast", tier: "frontier" },
|
|
4181
|
-
{ value: "gpt-5.4", label: "gpt-5.4 — OpenAI strong coding", tier: "standard" },
|
|
4182
|
-
{ value: "gpt-5.4-mini", label: "gpt-5.4-mini — fast & affordable", tier: "fast" },
|
|
4183
|
-
{ value: "gpt-5.4-nano", label: "gpt-5.4-nano — cheapest", tier: "budget" },
|
|
4184
|
-
{ value: "gpt-5.3-codex", label: "gpt-5.3-codex — OpenAI codex", tier: "standard" },
|
|
4185
|
-
{ value: "gpt-5.2", label: "gpt-5.2 — OpenAI coding", tier: "standard" },
|
|
4186
|
-
{ value: "gpt-5", label: "gpt-5 — OpenAI base", tier: "standard" },
|
|
4187
|
-
{ value: "gpt-5-fast", label: "gpt-5-fast — faster GPT-5", tier: "standard" },
|
|
4188
|
-
{ value: "gpt-5-mini", label: "gpt-5-mini — small GPT-5", tier: "fast" },
|
|
4189
|
-
{ value: "o3-pro-high", label: "o3-pro-high — advanced reasoning", tier: "frontier" },
|
|
4190
|
-
{ value: "composer-2.5", label: "composer-2.5 — Cursor latest model", tier: "standard" },
|
|
4191
|
-
{ value: "composer-2", label: "composer-2 — balanced multi-file", tier: "standard" },
|
|
4192
|
-
{ value: "composer-2-fast", label: "composer-2-fast — speed optimized", tier: "fast" },
|
|
4193
|
-
{ value: "composer-1.5", label: "composer-1.5 — legacy capable", tier: "standard" },
|
|
4194
|
-
{ value: "composer-1", label: "composer-1 — legacy model", tier: "fast" },
|
|
4195
|
-
{ value: "gemini-3.1-pro", label: "gemini-3.1-pro — Google latest", tier: "standard" },
|
|
4196
|
-
{ value: "gemini-3-pro", label: "gemini-3-pro — Google frontier", tier: "standard" },
|
|
4197
|
-
{ value: "gemini-3-flash", label: "gemini-3-flash — Google fast", tier: "fast" },
|
|
4198
|
-
{ value: "gemini-2.5-flash", label: "gemini-2.5-flash — Google budget", tier: "fast" },
|
|
4199
|
-
{ value: "grok-4.3", label: "grok-4.3 — xAI frontier", tier: "frontier" },
|
|
4200
|
-
{ value: "grok-4.20", label: "grok-4.20 — xAI standard", tier: "standard" },
|
|
4201
|
-
{ value: "kimi-k2.5", label: "kimi-k2.5 — Moonshot coding", tier: "standard" },
|
|
4202
|
-
{ value: "claude-4.5-opus", label: "claude-4.5-opus — previous gen opus", tier: "standard" },
|
|
4203
|
-
{ value: "claude-4.5-haiku", label: "claude-4.5-haiku — fast haiku", tier: "fast" },
|
|
4204
|
-
],
|
|
4205
|
-
copilot: [
|
|
4206
|
-
{ value: "auto", label: "auto — Copilot auto-selects", tier: "standard" },
|
|
4207
|
-
{ value: "claude-opus-4.7", label: "claude-opus-4.7 — Anthropic frontier", tier: "frontier" },
|
|
4208
|
-
{ value: "claude-opus-4.6", label: "claude-opus-4.6 — Anthropic frontier", tier: "frontier" },
|
|
4209
|
-
{ value: "claude-sonnet-4.6", label: "claude-sonnet-4.6 — Anthropic balanced", tier: "standard" },
|
|
4210
|
-
{ value: "claude-sonnet-4.5", label: "claude-sonnet-4.5 — Anthropic previous gen", tier: "standard" },
|
|
4211
|
-
{ value: "claude-opus-4.5", label: "claude-opus-4.5 — Anthropic legacy opus", tier: "standard" },
|
|
4212
|
-
{ value: "claude-haiku-4.5", label: "claude-haiku-4.5 — fastest", tier: "fast" },
|
|
4213
|
-
{ value: "gpt-5.5", label: "gpt-5.5 — OpenAI frontier", tier: "frontier" },
|
|
4214
|
-
{ value: "gpt-5.4", label: "gpt-5.4 — OpenAI standard", tier: "standard" },
|
|
4215
|
-
{ value: "gpt-5.4-mini", label: "gpt-5.4-mini — fast included", tier: "fast" },
|
|
4216
|
-
{ value: "gpt-5.4-nano", label: "gpt-5.4-nano — cheapest high-volume", tier: "budget" },
|
|
4217
|
-
{ value: "gpt-5.3-codex", label: "gpt-5.3-codex — OpenAI coding", tier: "standard" },
|
|
4218
|
-
{ value: "gpt-5-mini", label: "gpt-5-mini — included fast model", tier: "fast" },
|
|
4219
|
-
{ value: "gemini-3.1-pro", label: "gemini-3.1-pro — Google latest", tier: "frontier" },
|
|
4220
|
-
{ value: "gemini-3-pro", label: "gemini-3-pro — Google frontier", tier: "standard" },
|
|
4221
|
-
{ value: "gemini-2.5-pro", label: "gemini-2.5-pro — Google frontier", tier: "standard" },
|
|
4222
|
-
{ value: "gemini-2.5-flash", label: "gemini-2.5-flash — Google fast & affordable", tier: "fast" },
|
|
4223
|
-
{ value: "gemini-3-flash", label: "gemini-3-flash — Google fast", tier: "fast" },
|
|
4224
|
-
{ value: "raptor-mini", label: "raptor-mini — preview tuned mini", tier: "fast" },
|
|
4225
|
-
{ value: "goldeneye", label: "goldeneye — preview tuned coding model", tier: "budget" },
|
|
4226
|
-
],
|
|
4227
|
-
});
|
|
4228
4261
|
// Model names are used directly — no alias resolution needed
|
|
4229
4262
|
function hasCodexApiKeyAuth(env = process.env) {
|
|
4230
4263
|
return CODEX_API_KEY_ENV_NAMES.some((name) => String(env[name] ?? "").trim().length > 0);
|
|
@@ -5185,8 +5218,8 @@ function slugifyFilename(text, fallback) {
|
|
|
5185
5218
|
}
|
|
5186
5219
|
function buildProgressBar(completed, total, width) {
|
|
5187
5220
|
const safeWidth = Number.isFinite(width) && width > 0 ? Math.trunc(width) : 20;
|
|
5188
|
-
const safeTotal = Math.
|
|
5189
|
-
const safeCompleted = Math.max(0, Math.min(completed, safeTotal));
|
|
5221
|
+
const safeTotal = Number.isFinite(total) && total > 0 ? Math.trunc(total) : 1;
|
|
5222
|
+
const safeCompleted = Number.isFinite(completed) ? Math.max(0, Math.min(Math.trunc(completed), safeTotal)) : 0;
|
|
5190
5223
|
const fraction = safeCompleted / safeTotal;
|
|
5191
5224
|
const filled = Math.min(Math.floor(fraction * safeWidth), safeWidth);
|
|
5192
5225
|
const empty = safeWidth - filled;
|
|
@@ -5272,9 +5305,12 @@ function interactiveSelect(prompt, options) {
|
|
|
5272
5305
|
const maxVisible = Math.min(options.length, Math.max(3, termRows - 4));
|
|
5273
5306
|
const useUnicode = supportsUnicode();
|
|
5274
5307
|
const pointer = useUnicode ? "\u276f" : ">";
|
|
5275
|
-
const
|
|
5276
|
-
|
|
5277
|
-
|
|
5308
|
+
const getScrollStart = (visible) => visible.length <= maxVisible
|
|
5309
|
+
? 0
|
|
5310
|
+
: Math.max(0, Math.min(cursor - Math.floor(maxVisible / 2), visible.length - maxVisible));
|
|
5311
|
+
const formatLine = (entry, shortcutIndex, selected) => {
|
|
5312
|
+
const numberWidth = String(Math.min(9, maxVisible)).length;
|
|
5313
|
+
const num = shortcutIndex <= 9 ? String(shortcutIndex).padStart(numberWidth, " ") : " ".repeat(numberWidth);
|
|
5278
5314
|
const prefix = selected ? colorize(pointer, "\x1b[36m") : " ";
|
|
5279
5315
|
const label = selected ? colorize(entry.label, "\x1b[1m") : entry.label;
|
|
5280
5316
|
return ` ${prefix} ${num}. ${label}`;
|
|
@@ -5295,15 +5331,13 @@ function interactiveSelect(prompt, options) {
|
|
|
5295
5331
|
const visibleCount = Math.min(maxVisible, visible.length);
|
|
5296
5332
|
const posLabel = visible.length > 1 ? ` (${cursor + 1}/${visible.length})` : "";
|
|
5297
5333
|
const filterLabel = filterText ? colorize(` filter: "${filterText}"`, "\x1b[33m") : "";
|
|
5298
|
-
const scrollStart = visible
|
|
5299
|
-
? 0
|
|
5300
|
-
: Math.max(0, Math.min(cursor - Math.floor(maxVisible / 2), visible.length - maxVisible));
|
|
5334
|
+
const scrollStart = getScrollStart(visible);
|
|
5301
5335
|
const lines = [];
|
|
5302
5336
|
lines.push(`${prompt}${posLabel}${filterLabel}`);
|
|
5303
5337
|
for (let vi = 0; vi < visibleCount; vi++) {
|
|
5304
5338
|
const i = scrollStart + vi;
|
|
5305
5339
|
if (i < visible.length) {
|
|
5306
|
-
lines.push(formatLine(visible[i],
|
|
5340
|
+
lines.push(formatLine(visible[i], vi + 1, i === cursor));
|
|
5307
5341
|
}
|
|
5308
5342
|
}
|
|
5309
5343
|
if (visible.length > maxVisible) {
|
|
@@ -5360,7 +5394,7 @@ function interactiveSelect(prompt, options) {
|
|
|
5360
5394
|
return;
|
|
5361
5395
|
}
|
|
5362
5396
|
stdin.resume();
|
|
5363
|
-
const
|
|
5397
|
+
const cancelSelection = () => {
|
|
5364
5398
|
if (settled)
|
|
5365
5399
|
return;
|
|
5366
5400
|
settled = true;
|
|
@@ -5371,11 +5405,17 @@ function interactiveSelect(prompt, options) {
|
|
|
5371
5405
|
catch { /* stdout may be closed */ }
|
|
5372
5406
|
reject(new CliError("Selection cancelled.", CLI_EXIT_CODES.general));
|
|
5373
5407
|
};
|
|
5408
|
+
const onSigint = () => { cancelSelection(); };
|
|
5409
|
+
const onSigterm = () => { cancelSelection(); };
|
|
5374
5410
|
const cleanup = () => {
|
|
5375
5411
|
try {
|
|
5376
5412
|
process.removeListener("SIGINT", onSigint);
|
|
5377
5413
|
}
|
|
5378
5414
|
catch { /* ignore */ }
|
|
5415
|
+
try {
|
|
5416
|
+
process.removeListener("SIGTERM", onSigterm);
|
|
5417
|
+
}
|
|
5418
|
+
catch { /* ignore */ }
|
|
5379
5419
|
try {
|
|
5380
5420
|
stdin.removeListener("data", onData);
|
|
5381
5421
|
}
|
|
@@ -5407,6 +5447,7 @@ function interactiveSelect(prompt, options) {
|
|
|
5407
5447
|
stdin.on("end", onEnd);
|
|
5408
5448
|
stdin.on("error", onError);
|
|
5409
5449
|
process.on("SIGINT", onSigint);
|
|
5450
|
+
process.on("SIGTERM", onSigterm);
|
|
5410
5451
|
const onData = (data) => {
|
|
5411
5452
|
const visible = getVisibleOptions();
|
|
5412
5453
|
for (let ci = 0; ci < data.length; ci++) {
|
|
@@ -5542,17 +5583,18 @@ function interactiveSelect(prompt, options) {
|
|
|
5542
5583
|
}
|
|
5543
5584
|
else if (ch >= "1" && ch <= "9" && !filterText) {
|
|
5544
5585
|
const idx = parseInt(ch, 10) - 1;
|
|
5545
|
-
|
|
5586
|
+
const visibleIndex = getScrollStart(visible) + idx;
|
|
5587
|
+
if (visibleIndex < visible.length && idx < 9) {
|
|
5546
5588
|
if (settled)
|
|
5547
5589
|
return;
|
|
5548
5590
|
settled = true;
|
|
5549
5591
|
cleanup();
|
|
5550
5592
|
stdout.write("\n");
|
|
5551
|
-
resolve(visible[
|
|
5593
|
+
resolve(visible[visibleIndex].value);
|
|
5552
5594
|
return;
|
|
5553
5595
|
}
|
|
5554
5596
|
}
|
|
5555
|
-
else if (ch
|
|
5597
|
+
else if (isPrintableInteractiveInput(ch)) {
|
|
5556
5598
|
filterText += ch;
|
|
5557
5599
|
cursor = 0;
|
|
5558
5600
|
render();
|
|
@@ -5562,6 +5604,11 @@ function interactiveSelect(prompt, options) {
|
|
|
5562
5604
|
stdin.on("data", onData);
|
|
5563
5605
|
});
|
|
5564
5606
|
}
|
|
5607
|
+
function isPrintableInteractiveInput(value) {
|
|
5608
|
+
if (!value)
|
|
5609
|
+
return false;
|
|
5610
|
+
return !/[\x00-\x1f\x7f]/.test(value);
|
|
5611
|
+
}
|
|
5565
5612
|
async function interactiveInput(prompt, defaultValue) {
|
|
5566
5613
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
5567
5614
|
throw new CliError("Interactive input requires a TTY.", CLI_EXIT_CODES.usage);
|
|
@@ -5585,6 +5632,14 @@ async function interactiveInput(prompt, defaultValue) {
|
|
|
5585
5632
|
resolved = true;
|
|
5586
5633
|
resolve(defaultValue || "");
|
|
5587
5634
|
});
|
|
5635
|
+
rl.on("error", (err) => {
|
|
5636
|
+
if (resolved)
|
|
5637
|
+
return;
|
|
5638
|
+
resolved = true;
|
|
5639
|
+
rl.close();
|
|
5640
|
+
rl.removeAllListeners();
|
|
5641
|
+
reject(new CliError(`Input stream error: ${err.message}`, CLI_EXIT_CODES.general));
|
|
5642
|
+
});
|
|
5588
5643
|
rl.on("SIGINT", () => {
|
|
5589
5644
|
if (resolved)
|
|
5590
5645
|
return;
|
|
@@ -5596,14 +5651,26 @@ async function interactiveInput(prompt, defaultValue) {
|
|
|
5596
5651
|
});
|
|
5597
5652
|
}
|
|
5598
5653
|
async function checkPromptsGptSiteReachable(apiUrl) {
|
|
5654
|
+
if (typeof globalThis.fetch !== "function") {
|
|
5655
|
+
return { ok: false, status: null };
|
|
5656
|
+
}
|
|
5599
5657
|
const target = new URL("/", apiUrl).toString();
|
|
5600
|
-
const request = (method) =>
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5658
|
+
const request = async (method) => {
|
|
5659
|
+
let timer = null;
|
|
5660
|
+
try {
|
|
5661
|
+
return await Promise.race([
|
|
5662
|
+
globalThis.fetch(target, { method }),
|
|
5663
|
+
new Promise((_, reject) => {
|
|
5664
|
+
timer = setTimeout(() => reject(new Error("timeout")), 5000);
|
|
5665
|
+
timer.unref?.();
|
|
5666
|
+
}),
|
|
5667
|
+
]);
|
|
5668
|
+
}
|
|
5669
|
+
finally {
|
|
5670
|
+
if (timer)
|
|
5671
|
+
clearTimeout(timer);
|
|
5672
|
+
}
|
|
5673
|
+
};
|
|
5607
5674
|
let response = await request("HEAD");
|
|
5608
5675
|
if (response.status === 405) {
|
|
5609
5676
|
response = await request("GET");
|