prompts-gpt 0.2.13 → 0.2.15
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 +739 -98
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +66 -15
- package/dist/index.js.map +1 -1
- package/dist/orchestrate.d.ts +117 -0
- package/dist/orchestrate.d.ts.map +1 -0
- package/dist/orchestrate.js +706 -0
- package/dist/orchestrate.js.map +1 -0
- package/dist/runtime.d.ts +6 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +289 -48
- package/dist/runtime.js.map +1 -1
- package/dist/sweep.d.ts +27 -1
- package/dist/sweep.d.ts.map +1 -1
- package/dist/sweep.js +417 -57
- package/dist/sweep.js.map +1 -1
- package/package.json +5 -3
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { existsSync, readFileSync, statSync, readdirSync } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
4
5
|
import { parseArgs } from "node:util";
|
|
5
|
-
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, normalizeOrchestrationAgent, ORCHESTRATION_AGENT_PROFILES, runBatch, runPrompt, resolveRunProvider, warnModelProviderMismatch, sweepPrompt, validateRunConfig, discoverWorkspaceAssets, SUPPORTED_AGENT_TARGETS, detectProviders, loadLocalCredentials, saveLocalCredentials, syncPrompts, writeAgentFiles, writePromptManifest, writePromptMarkdownFiles, ensureGitignoreEntry, } 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, 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, } from "./index.js";
|
|
6
7
|
const CLI_EXIT_CODES = {
|
|
7
8
|
success: 0,
|
|
8
9
|
general: 1,
|
|
@@ -12,7 +13,7 @@ const CLI_EXIT_CODES = {
|
|
|
12
13
|
usage: 64,
|
|
13
14
|
};
|
|
14
15
|
const VALID_TOOLS = ["Codex", "Claude Code", "Cursor", "GitHub Copilot", "ChatGPT", "Gemini", "Perplexity", "Grok", "DeepSeek", "Claude"];
|
|
15
|
-
const COMMANDS = ["setup", "init", "project", "pull", "generate", "sync", "run", "run-batch", "sweep", "list", "status", "validate", "providers", "models", "sync-models", "doctor", "load-config", "quickstart", "version", "help"];
|
|
16
|
+
const COMMANDS = ["setup", "init", "project", "pull", "generate", "sync", "run", "run-batch", "sweep", "orchestrate", "diff", "list", "status", "validate", "providers", "models", "sync-models", "doctor", "load-config", "quickstart", "version", "help"];
|
|
16
17
|
const MAX_STDIN_TOKEN_LENGTH = 4_096;
|
|
17
18
|
class CliError extends Error {
|
|
18
19
|
exitCode;
|
|
@@ -79,7 +80,7 @@ async function main() {
|
|
|
79
80
|
}
|
|
80
81
|
const command = asCommandName(first);
|
|
81
82
|
if (!command) {
|
|
82
|
-
const suggestion =
|
|
83
|
+
const suggestion = findClosestCommand(first);
|
|
83
84
|
const hint = suggestion ? ` Did you mean \`prompts-gpt ${suggestion}\`?` : "";
|
|
84
85
|
const installHint = "\nIf you installed from npm, ensure you have the latest version: npm install -g prompts-gpt@latest";
|
|
85
86
|
throw new CliError(`Unknown command: ${first}.${hint}${installHint}`, CLI_EXIT_CODES.usage);
|
|
@@ -106,10 +107,11 @@ async function runCommand(command, flags) {
|
|
|
106
107
|
}
|
|
107
108
|
console.log(`Workspace: ${cwd}\n`);
|
|
108
109
|
for (const provider of providers) {
|
|
109
|
-
const icon = provider.available ? "✓" : "✗";
|
|
110
|
-
|
|
110
|
+
const icon = provider.available ? sym("✓", "+") : sym("✗", "x");
|
|
111
|
+
const statusColor = provider.available ? "\x1b[32m" : "\x1b[31m";
|
|
112
|
+
console.log(`${colorize(icon, statusColor)} ${provider.provider}: ${provider.available ? "available" : "missing"} | bin=${provider.bin} | model=${provider.modelDefault}${provider.version ? ` | ${provider.version}` : ""}`);
|
|
111
113
|
if (!provider.available) {
|
|
112
|
-
console.log(` → ${provider.installHint}`);
|
|
114
|
+
console.log(` ${sym("→", "->")} ${provider.installHint}`);
|
|
113
115
|
}
|
|
114
116
|
}
|
|
115
117
|
const noneAvailable = providers.every((p) => !p.available);
|
|
@@ -165,7 +167,7 @@ async function runCommand(command, flags) {
|
|
|
165
167
|
const availableCount = result.providerSummary.filter((p) => p.available).length;
|
|
166
168
|
console.log(`Providers: ${availableCount}/${result.providerSummary.length} available`);
|
|
167
169
|
if (availableCount === 0) {
|
|
168
|
-
console.log("⚠ No provider CLIs detected. Install codex, cursor agent, claude, or copilot
|
|
170
|
+
console.log(`${sym("⚠", "!")} No provider CLIs detected. Install codex, cursor agent, claude, or copilot.`);
|
|
169
171
|
}
|
|
170
172
|
const setupAssets = await discoverWorkspaceAssets(cwd);
|
|
171
173
|
if (isTTYInteractive() && (setupAssets.sweeps.length > 0 || setupAssets.prompts.length > 0)) {
|
|
@@ -208,9 +210,24 @@ async function runCommand(command, flags) {
|
|
|
208
210
|
}
|
|
209
211
|
if (command === "models") {
|
|
210
212
|
const cwd = getResolvedCwd(flags);
|
|
211
|
-
|
|
213
|
+
let config;
|
|
214
|
+
try {
|
|
215
|
+
config = await loadRunConfig(cwd);
|
|
216
|
+
}
|
|
217
|
+
catch (configErr) {
|
|
218
|
+
console.error(`Failed to load config: ${configErr instanceof Error ? configErr.message : String(configErr)}`);
|
|
219
|
+
console.error(`Fix or delete ${cwd}/.prompts-gpt/config.json and retry.`);
|
|
220
|
+
process.exitCode = CLI_EXIT_CODES.general;
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
212
223
|
const providerFilter = getStringFlag(flags, "provider") || getStringFlag(flags, "agent");
|
|
213
|
-
const
|
|
224
|
+
const validProviders = Object.keys(PROVIDER_MODELS);
|
|
225
|
+
if (providerFilter && !validProviders.includes(providerFilter)) {
|
|
226
|
+
console.error(`Unknown provider "${providerFilter}". Valid providers: ${validProviders.join(", ")}`);
|
|
227
|
+
process.exitCode = CLI_EXIT_CODES.validation;
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
const providers = providerFilter ? [providerFilter] : validProviders;
|
|
214
231
|
if (Boolean(flags.json)) {
|
|
215
232
|
const result = {};
|
|
216
233
|
for (const p of providers) {
|
|
@@ -253,7 +270,8 @@ async function runCommand(command, flags) {
|
|
|
253
270
|
if (addFlag && providerFilter) {
|
|
254
271
|
const modelNamePattern = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,63}$/;
|
|
255
272
|
const addModels = addFlag.split(",").map((m) => m.trim()).filter(Boolean);
|
|
256
|
-
const
|
|
273
|
+
const dangerousKeys = new Set(["__proto__", "constructor", "prototype"]);
|
|
274
|
+
const invalidModels = addModels.filter((m) => !modelNamePattern.test(m) || dangerousKeys.has(m));
|
|
257
275
|
if (invalidModels.length > 0) {
|
|
258
276
|
console.error(`Invalid model name(s): ${invalidModels.join(", ")}. Model names must be alphanumeric with dots, hyphens, or underscores.`);
|
|
259
277
|
process.exitCode = CLI_EXIT_CODES.validation;
|
|
@@ -381,16 +399,16 @@ async function runCommand(command, flags) {
|
|
|
381
399
|
console.log("Prompts-GPT Doctor");
|
|
382
400
|
console.log("==================\n");
|
|
383
401
|
console.log("System:");
|
|
384
|
-
console.log(` ${report.nodeVersion.startsWith("v18") || report.nodeVersion.startsWith("v2") ? "✓" : "⚠"} Node: ${report.nodeVersion}`);
|
|
385
|
-
console.log(` ✓ OS: ${report.osPlatform}/${report.osArch} | CPUs: ${report.cpuCount}`);
|
|
386
|
-
console.log(` ${report.configFound ? "✓" : "✗"} Config: ${report.configFound ? report.configPath : "not found — run \`prompts-gpt setup\`"}`);
|
|
402
|
+
console.log(` ${report.nodeVersion.startsWith("v18") || report.nodeVersion.startsWith("v2") ? sym("✓", "+") : sym("⚠", "!")} Node: ${report.nodeVersion}`);
|
|
403
|
+
console.log(` ${sym("✓", "+")} OS: ${report.osPlatform}/${report.osArch} | CPUs: ${report.cpuCount}`);
|
|
404
|
+
console.log(` ${report.configFound ? sym("✓", "+") : sym("✗", "x")} Config: ${report.configFound ? report.configPath : "not found — run \`prompts-gpt setup\`"}`);
|
|
387
405
|
console.log(`\n Workspace: ${report.cwd}\n`);
|
|
388
406
|
console.log("Providers:");
|
|
389
407
|
for (const provider of report.providers) {
|
|
390
|
-
const icon = provider.available ? "✓" : "✗";
|
|
408
|
+
const icon = provider.available ? sym("✓", "+") : sym("✗", "x");
|
|
391
409
|
console.log(` ${icon} ${provider.provider}: ${provider.available ? `${provider.bin}` : "not found"}${provider.version ? ` (${provider.version})` : ""}`);
|
|
392
410
|
if (!provider.available) {
|
|
393
|
-
console.log(` → ${provider.installHint}`);
|
|
411
|
+
console.log(` ${sym("→", "->")} ${provider.installHint}`);
|
|
394
412
|
}
|
|
395
413
|
}
|
|
396
414
|
const configNotes = report.notes.filter((n) => n.includes("config") || n.includes("Config") || n.includes("provider order") || n.includes("Router"));
|
|
@@ -399,17 +417,17 @@ async function runCommand(command, flags) {
|
|
|
399
417
|
if (configNotes.length > 0) {
|
|
400
418
|
console.log("\nConfiguration:");
|
|
401
419
|
for (const n of configNotes)
|
|
402
|
-
console.log(` ℹ ${n}`);
|
|
420
|
+
console.log(` ${sym("ℹ", "i")} ${n}`);
|
|
403
421
|
}
|
|
404
422
|
if (sweepNotes.length > 0) {
|
|
405
423
|
console.log("\nSweep:");
|
|
406
424
|
for (const n of sweepNotes)
|
|
407
|
-
console.log(` ℹ ${n}`);
|
|
425
|
+
console.log(` ${sym("ℹ", "i")} ${n}`);
|
|
408
426
|
}
|
|
409
427
|
if (otherNotes.length > 0) {
|
|
410
428
|
console.log("\nNotes:");
|
|
411
429
|
for (const n of otherNotes)
|
|
412
|
-
console.log(` ℹ ${n}`);
|
|
430
|
+
console.log(` ${sym("ℹ", "i")} ${n}`);
|
|
413
431
|
}
|
|
414
432
|
try {
|
|
415
433
|
const config = await loadRunConfig(cwd);
|
|
@@ -425,7 +443,7 @@ async function runCommand(command, flags) {
|
|
|
425
443
|
if (modelIssues.length > 0) {
|
|
426
444
|
console.log("\nModel Overrides:");
|
|
427
445
|
for (const issue of modelIssues)
|
|
428
|
-
console.log(` ⚠ ${issue}`);
|
|
446
|
+
console.log(` ${sym("⚠", "!")} ${issue}`);
|
|
429
447
|
}
|
|
430
448
|
const cachedModelsPath = path.resolve(cwd, DEFAULT_PROMPTS_GPT_OUT_DIR, ".models.json");
|
|
431
449
|
if (existsSync(cachedModelsPath)) {
|
|
@@ -446,25 +464,25 @@ async function runCommand(command, flags) {
|
|
|
446
464
|
catch { /* skip config check */ }
|
|
447
465
|
console.log("\nAPI Connectivity:");
|
|
448
466
|
if (typeof globalThis.fetch !== "function") {
|
|
449
|
-
console.log("
|
|
467
|
+
console.log(` ${sym("⚠", "!")} fetch is not available (Node.js 18.18+ required). Skipping API check.`);
|
|
450
468
|
}
|
|
451
469
|
else {
|
|
452
470
|
try {
|
|
453
471
|
const siteCheck = await checkPromptsGptSiteReachable(DEFAULT_PROMPTS_GPT_API_URL);
|
|
454
472
|
if (siteCheck.ok) {
|
|
455
|
-
console.log(` ✓ prompts-gpt.com reachable`);
|
|
473
|
+
console.log(` ${sym("✓", "+")} prompts-gpt.com reachable`);
|
|
456
474
|
}
|
|
457
475
|
else {
|
|
458
|
-
console.log(` ⚠ prompts-gpt.com responded with status ${siteCheck.status ?? "unknown"}`);
|
|
476
|
+
console.log(` ${sym("⚠", "!")} prompts-gpt.com responded with status ${siteCheck.status ?? "unknown"}`);
|
|
459
477
|
}
|
|
460
478
|
}
|
|
461
479
|
catch (err) {
|
|
462
480
|
const msg = err instanceof Error ? err.message : "";
|
|
463
481
|
if (msg.includes("ECONNREFUSED") || msg.includes("ENOTFOUND")) {
|
|
464
|
-
console.log("
|
|
482
|
+
console.log(` ${sym("✗", "x")} prompts-gpt.com unreachable — check network or proxy settings (HTTPS_PROXY)`);
|
|
465
483
|
}
|
|
466
484
|
else {
|
|
467
|
-
console.log("
|
|
485
|
+
console.log(` ${sym("✗", "x")} prompts-gpt.com unreachable — check network connection`);
|
|
468
486
|
}
|
|
469
487
|
}
|
|
470
488
|
}
|
|
@@ -473,6 +491,50 @@ async function runCommand(command, flags) {
|
|
|
473
491
|
console.log(`\nSDK Version: ${pkgData.version ?? "unknown"}`);
|
|
474
492
|
}
|
|
475
493
|
catch { /* skip */ }
|
|
494
|
+
// --fix mode: auto-fix common issues (idempotent — safe to run repeatedly)
|
|
495
|
+
if (Boolean(flags.fix)) {
|
|
496
|
+
const { mkdir: fsMkdir } = await import("node:fs/promises");
|
|
497
|
+
const uc = supportsColor();
|
|
498
|
+
const ok = uc ? "✓" : "[ok]";
|
|
499
|
+
const warn = uc ? "⚠" : "[!]";
|
|
500
|
+
const info = uc ? "ℹ" : "[i]";
|
|
501
|
+
console.log("\nAuto-fix:");
|
|
502
|
+
let fixCount = 0;
|
|
503
|
+
if (!report.configFound) {
|
|
504
|
+
try {
|
|
505
|
+
await initRunConfig({ cwd, overwrite: false });
|
|
506
|
+
console.log(` ${ok} Created default config`);
|
|
507
|
+
fixCount++;
|
|
508
|
+
}
|
|
509
|
+
catch {
|
|
510
|
+
console.log(` ${warn} Could not create config (may already exist)`);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
const promptsDir = path.resolve(cwd, DEFAULT_PROMPTS_GPT_OUT_DIR);
|
|
514
|
+
if (!existsSync(promptsDir)) {
|
|
515
|
+
try {
|
|
516
|
+
await fsMkdir(promptsDir, { recursive: true });
|
|
517
|
+
console.log(` ${ok} Created ${DEFAULT_PROMPTS_GPT_OUT_DIR}/ directory`);
|
|
518
|
+
fixCount++;
|
|
519
|
+
}
|
|
520
|
+
catch {
|
|
521
|
+
console.log(` ${warn} Could not create ${DEFAULT_PROMPTS_GPT_OUT_DIR}/ directory`);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
try {
|
|
525
|
+
await ensureGitignoreEntry(cwd, ".prompts-gpt/.credentials.json");
|
|
526
|
+
await ensureGitignoreEntry(cwd, ".prompts-gpt/.models.json");
|
|
527
|
+
console.log(` ${ok} Updated .gitignore with sensitive file patterns`);
|
|
528
|
+
fixCount++;
|
|
529
|
+
}
|
|
530
|
+
catch { /* gitignore update is best-effort */ }
|
|
531
|
+
if (report.providers.every((p) => !p.available)) {
|
|
532
|
+
console.log(` ${info} No providers found. Install one:`);
|
|
533
|
+
console.log(" npm install -g @openai/codex");
|
|
534
|
+
console.log(" npm install -g @anthropic-ai/claude-code");
|
|
535
|
+
}
|
|
536
|
+
console.log(`\n ${fixCount} fix${fixCount === 1 ? "" : "es"} applied.`);
|
|
537
|
+
}
|
|
476
538
|
return;
|
|
477
539
|
}
|
|
478
540
|
if (command === "list") {
|
|
@@ -589,26 +651,30 @@ async function runCommand(command, flags) {
|
|
|
589
651
|
}
|
|
590
652
|
console.log(`Workspace: ${cwd}`);
|
|
591
653
|
console.log("");
|
|
654
|
+
const docUc = supportsColor();
|
|
655
|
+
const docOk = docUc ? "✓" : "[ok]";
|
|
656
|
+
const docNo = docUc ? "✗" : "[x]";
|
|
657
|
+
const docDash = docUc ? "—" : "-";
|
|
592
658
|
console.log("Readiness:");
|
|
593
|
-
console.log(` Credentials: ${assets.credentialsFound ?
|
|
594
|
-
console.log(` Config: ${assets.configFound ?
|
|
595
|
-
console.log(` Manifest: ${assets.manifestFound ?
|
|
659
|
+
console.log(` Credentials: ${assets.credentialsFound ? docOk : `${docNo} ${docDash} run \`prompts-gpt init --token <token>\``}`);
|
|
660
|
+
console.log(` Config: ${assets.configFound ? docOk : `${docNo} ${docDash} run \`prompts-gpt setup\``}`);
|
|
661
|
+
console.log(` Manifest: ${assets.manifestFound ? docOk : `${docNo} ${docDash} run \`prompts-gpt sync\``}`);
|
|
596
662
|
const cloudCount = assets.prompts.filter((p) => p.source === "library" || p.source === "generated").length;
|
|
597
663
|
const localCount = assets.prompts.filter((p) => p.source === "local").length;
|
|
598
664
|
const promptSummary = assets.prompts.length > 0
|
|
599
|
-
?
|
|
600
|
-
:
|
|
665
|
+
? `${docOk} (${assets.prompts.length}${cloudCount > 0 ? `, ${cloudCount} synced` : ""}${localCount > 0 ? `, ${localCount} local-only` : ""})`
|
|
666
|
+
: `${docNo} ${docDash} none found`;
|
|
601
667
|
console.log(` Prompts: ${promptSummary}`);
|
|
602
|
-
console.log(` Sweeps: ${assets.sweeps.length > 0 ?
|
|
603
|
-
console.log(` Agents: ${assets.agents.length > 0 ?
|
|
668
|
+
console.log(` Sweeps: ${assets.sweeps.length > 0 ? `${docOk} (${assets.sweeps.length})` : `${docDash} none found`}`);
|
|
669
|
+
console.log(` Agents: ${assets.agents.length > 0 ? `${docOk} (${assets.agents.length} targets)` : `${docNo} ${docDash} run \`prompts-gpt sync\``}`);
|
|
604
670
|
if (availableProviders.length > 0) {
|
|
605
|
-
console.log(` Providers:
|
|
671
|
+
console.log(` Providers: ${docOk}`);
|
|
606
672
|
for (const p of availableProviders) {
|
|
607
673
|
console.log(` ${p.provider}: ${p.bin} | model: ${p.modelDefault}${p.version ? ` | ${p.version}` : ""}`);
|
|
608
674
|
}
|
|
609
675
|
}
|
|
610
676
|
else {
|
|
611
|
-
console.log(
|
|
677
|
+
console.log(` Providers: ${docNo} ${docDash} no CLI found. Install one:`);
|
|
612
678
|
console.log(" npm install -g @openai/codex # Codex");
|
|
613
679
|
console.log(" npm install -g @anthropic-ai/claude-code # Claude Code");
|
|
614
680
|
console.log(" # Cursor: install Cursor IDE (includes agent CLI)");
|
|
@@ -620,7 +686,7 @@ async function runCommand(command, flags) {
|
|
|
620
686
|
const mStat = statSync(statusModelCache);
|
|
621
687
|
const ageMs = Date.now() - mStat.mtimeMs;
|
|
622
688
|
const ageDays = Math.floor(ageMs / (1000 * 60 * 60 * 24));
|
|
623
|
-
console.log(` Models: ${ageDays <= 7 ? "
|
|
689
|
+
console.log(` Models: ${ageDays <= 7 ? docOk : (docUc ? "⚠" : "[!]")} synced ${ageDays === 0 ? "today" : `${ageDays}d ago`}${ageDays > 7 ? ` ${docDash} run 'prompts-gpt sync-models'` : ""}`);
|
|
624
690
|
}
|
|
625
691
|
catch { /* skip */ }
|
|
626
692
|
}
|
|
@@ -679,8 +745,10 @@ async function runCommand(command, flags) {
|
|
|
679
745
|
issues.push("no iterations in frontmatter");
|
|
680
746
|
if (!fm.title)
|
|
681
747
|
issues.push("no title heading");
|
|
682
|
-
const
|
|
683
|
-
|
|
748
|
+
const valUc = supportsColor();
|
|
749
|
+
const icon = issues.length === 0 ? (valUc ? "✓" : "[ok]") : (valUc ? "⚠" : "[!]");
|
|
750
|
+
const sep = issues.length > 0 ? (valUc ? " — " : " - ") : "";
|
|
751
|
+
console.log(` ${icon} ${s.name}${issues.length > 0 ? `${sep}${issues.join(", ")}` : ""}`);
|
|
684
752
|
}
|
|
685
753
|
}
|
|
686
754
|
const modelCachePath = path.resolve(cwd, DEFAULT_PROMPTS_GPT_OUT_DIR, ".models.json");
|
|
@@ -690,14 +758,14 @@ async function runCommand(command, flags) {
|
|
|
690
758
|
const ageMs = Date.now() - mStat.mtimeMs;
|
|
691
759
|
const ageDays = Math.floor(ageMs / (1000 * 60 * 60 * 24));
|
|
692
760
|
if (ageDays > 7) {
|
|
693
|
-
console.log(`\n⚠ Model registry is ${ageDays} days old. Run 'prompts-gpt sync-models' to refresh.`);
|
|
761
|
+
console.log(`\n${sym("⚠", "!")} Model registry is ${ageDays} days old. Run 'prompts-gpt sync-models' to refresh.`);
|
|
694
762
|
}
|
|
695
763
|
else {
|
|
696
|
-
console.log(`\n✓ Model registry: synced ${ageDays === 0 ? "today" : `${ageDays}d ago`}`);
|
|
764
|
+
console.log(`\n${sym("✓", "+")} Model registry: synced ${ageDays === 0 ? "today" : `${ageDays}d ago`}`);
|
|
697
765
|
}
|
|
698
766
|
}
|
|
699
767
|
catch {
|
|
700
|
-
console.log("
|
|
768
|
+
console.log(`\n${sym("⚠", "!")} Model registry not synced. Run 'prompts-gpt sync-models'.`);
|
|
701
769
|
}
|
|
702
770
|
if (result.valid && result.errors.length === 0 && result.warnings.length === 0) {
|
|
703
771
|
console.log(" No issues found.");
|
|
@@ -862,6 +930,86 @@ async function runCommand(command, flags) {
|
|
|
862
930
|
console.error(`[warning] ${mismatchWarning}`);
|
|
863
931
|
}
|
|
864
932
|
}
|
|
933
|
+
// Watch mode — re-run on file change
|
|
934
|
+
if (Boolean(flags.watch)) {
|
|
935
|
+
const watchTarget = getStringFlag(flags, "prompt-file") || config.defaultPromptFile;
|
|
936
|
+
if (!watchTarget) {
|
|
937
|
+
throw new CliError("--watch requires a prompt file (use --prompt-file or set a default)", CLI_EXIT_CODES.usage);
|
|
938
|
+
}
|
|
939
|
+
const resolvedWatch = path.resolve(cwd, watchTarget);
|
|
940
|
+
console.log(`Watching ${path.basename(resolvedWatch)} for changes... (Ctrl+C to stop)\n`);
|
|
941
|
+
const { watch: fsWatch } = await import("node:fs");
|
|
942
|
+
let running = false;
|
|
943
|
+
const doRun = async () => {
|
|
944
|
+
if (running)
|
|
945
|
+
return;
|
|
946
|
+
running = true;
|
|
947
|
+
console.log(`\n${colorize("▶ Running...", "\x1b[36m")} (${new Date().toLocaleTimeString()})`);
|
|
948
|
+
try {
|
|
949
|
+
const r = await runPrompt({
|
|
950
|
+
cwd,
|
|
951
|
+
promptFile: watchTarget,
|
|
952
|
+
agent,
|
|
953
|
+
model: modelFlag,
|
|
954
|
+
timeoutSeconds: parsePositiveIntFlag(getStringFlag(flags, "timeout"), "timeout"),
|
|
955
|
+
artifactsDir: getStringFlag(flags, "artifacts-dir"),
|
|
956
|
+
approveMcps: !Boolean(flags["no-approve-mcps"]),
|
|
957
|
+
sandboxMode: getStringFlag(flags, "sandbox"),
|
|
958
|
+
permissionMode: getStringFlag(flags, "permission-mode"),
|
|
959
|
+
});
|
|
960
|
+
const icon = r.exitCode === 0 ? colorize("✓", "\x1b[32m") : colorize("✗", "\x1b[31m");
|
|
961
|
+
console.log(`${icon} ${r.provider} exit=${r.exitCode} (${formatDuration(r.durationMs)})`);
|
|
962
|
+
}
|
|
963
|
+
catch (err) {
|
|
964
|
+
console.error(` Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
965
|
+
}
|
|
966
|
+
finally {
|
|
967
|
+
running = false;
|
|
968
|
+
console.log(`\n Watching for changes...`);
|
|
969
|
+
}
|
|
970
|
+
};
|
|
971
|
+
await doRun();
|
|
972
|
+
let debounce = null;
|
|
973
|
+
const watcher = fsWatch(resolvedWatch, () => {
|
|
974
|
+
if (debounce)
|
|
975
|
+
clearTimeout(debounce);
|
|
976
|
+
debounce = setTimeout(doRun, 500);
|
|
977
|
+
debounce.unref?.();
|
|
978
|
+
});
|
|
979
|
+
watcher.on("error", (err) => {
|
|
980
|
+
if (debounce) {
|
|
981
|
+
clearTimeout(debounce);
|
|
982
|
+
debounce = null;
|
|
983
|
+
}
|
|
984
|
+
console.error(`Watch error: ${err instanceof Error ? err.message : String(err)}`);
|
|
985
|
+
console.log("File may have been deleted. Exiting watch mode.");
|
|
986
|
+
watcher.close();
|
|
987
|
+
});
|
|
988
|
+
await new Promise((resolve) => {
|
|
989
|
+
let settled = false;
|
|
990
|
+
const settle = () => { if (settled)
|
|
991
|
+
return; settled = true; watcher.close(); if (debounce) {
|
|
992
|
+
clearTimeout(debounce);
|
|
993
|
+
debounce = null;
|
|
994
|
+
} resolve(); };
|
|
995
|
+
const onSignal = () => { settle(); };
|
|
996
|
+
process.on("SIGINT", onSignal);
|
|
997
|
+
process.on("SIGTERM", onSignal);
|
|
998
|
+
watcher.on("close", () => {
|
|
999
|
+
process.removeListener("SIGINT", onSignal);
|
|
1000
|
+
process.removeListener("SIGTERM", onSignal);
|
|
1001
|
+
settle();
|
|
1002
|
+
});
|
|
1003
|
+
});
|
|
1004
|
+
return;
|
|
1005
|
+
}
|
|
1006
|
+
// B4: --verbose shows detailed execution info
|
|
1007
|
+
if (Boolean(flags.verbose) && !Boolean(flags.json)) {
|
|
1008
|
+
const providers = await detectProviders(cwd);
|
|
1009
|
+
const resolved = resolveRunProvider(agent, providers, config.providerOrder);
|
|
1010
|
+
const resolvedModel = modelFlag?.trim() || config.modelOverrides[resolved]?.trim() || "(default)";
|
|
1011
|
+
console.log(colorize("[verbose]", "\x1b[90m") + ` provider=${resolved} model=${resolvedModel} agent=${agent} timeout=${getStringFlag(flags, "timeout") || config.timeoutSeconds}s`);
|
|
1012
|
+
}
|
|
865
1013
|
const result = await runPrompt({
|
|
866
1014
|
cwd,
|
|
867
1015
|
promptFile,
|
|
@@ -879,8 +1027,8 @@ async function runCommand(command, flags) {
|
|
|
879
1027
|
console.log(JSON.stringify(result, null, 2));
|
|
880
1028
|
return;
|
|
881
1029
|
}
|
|
882
|
-
const agentLabel = getStringFlag(flags, "agent") ? result.provider : `${result.provider} (auto
|
|
883
|
-
console.log(
|
|
1030
|
+
const agentLabel = getStringFlag(flags, "agent") ? result.provider : `${result.provider} (auto-selected)`;
|
|
1031
|
+
console.log(`\n${sym("▸", ">")} Run ID: ${result.runId}`);
|
|
884
1032
|
console.log(`Provider: ${agentLabel} | Model: ${result.model}`);
|
|
885
1033
|
console.log(`Exit code: ${result.exitCode}`);
|
|
886
1034
|
console.log(`Duration: ${formatDuration(result.durationMs)}`);
|
|
@@ -890,6 +1038,7 @@ async function runCommand(command, flags) {
|
|
|
890
1038
|
console.log(`Run dir: ${result.runDir}`);
|
|
891
1039
|
console.log(`Summary: ${result.summaryFile}`);
|
|
892
1040
|
console.log(`Log: ${result.logFile}`);
|
|
1041
|
+
console.log(`Diff: prompts-gpt diff ${result.runId}`);
|
|
893
1042
|
if (result.exitCode === 0) {
|
|
894
1043
|
try {
|
|
895
1044
|
const { readFile: fsRead } = await import("node:fs/promises");
|
|
@@ -981,6 +1130,22 @@ async function runCommand(command, flags) {
|
|
|
981
1130
|
}
|
|
982
1131
|
if (command === "sweep") {
|
|
983
1132
|
const cwd = getResolvedCwd(flags);
|
|
1133
|
+
// B1: Handle --force-unlock to clear stale sweep locks
|
|
1134
|
+
if (Boolean(flags["force-unlock"])) {
|
|
1135
|
+
const { forceReleaseSweepLock } = await import("./index.js");
|
|
1136
|
+
const released = await forceReleaseSweepLock(cwd);
|
|
1137
|
+
if (released) {
|
|
1138
|
+
console.log("Sweep lock forcefully released.");
|
|
1139
|
+
}
|
|
1140
|
+
else {
|
|
1141
|
+
console.log("No sweep lock found.");
|
|
1142
|
+
}
|
|
1143
|
+
return;
|
|
1144
|
+
}
|
|
1145
|
+
// Auto-detect CI and force non-interactive mode
|
|
1146
|
+
if (isCI() && !flags["non-interactive"]) {
|
|
1147
|
+
flags["non-interactive"] = true;
|
|
1148
|
+
}
|
|
984
1149
|
const sweepPromptFile = getStringFlag(flags, "prompt-file");
|
|
985
1150
|
const config = await loadRunConfig(cwd);
|
|
986
1151
|
warnOnConfigIssues(config);
|
|
@@ -1188,8 +1353,14 @@ async function runCommand(command, flags) {
|
|
|
1188
1353
|
const quiet = Boolean(flags.quiet);
|
|
1189
1354
|
const summaryLineCount = parsePositiveIntFlag(getStringFlag(flags, "summary-lines"), "summary-lines") ?? 40;
|
|
1190
1355
|
const sweepDurations = [];
|
|
1356
|
+
const sweepUc = supportsColor();
|
|
1357
|
+
// B11: When --json, emit NDJSON progress lines instead of silencing
|
|
1191
1358
|
const onProgress = Boolean(flags.json)
|
|
1192
|
-
?
|
|
1359
|
+
? (event) => {
|
|
1360
|
+
if (event.type !== "message" && event.type !== "tool") {
|
|
1361
|
+
console.log(JSON.stringify(event));
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1193
1364
|
: (event) => {
|
|
1194
1365
|
if (event.type === "preflight") {
|
|
1195
1366
|
const report = JSON.parse(event.message);
|
|
@@ -1208,97 +1379,118 @@ async function runCommand(command, flags) {
|
|
|
1208
1379
|
const maxRunDirs = parsePositiveIntFlag(getStringFlag(flags, "max-run-dirs"), "max-run-dirs") ?? 20;
|
|
1209
1380
|
console.log(`Log rotation: keep ${maxRunDirs} runs`);
|
|
1210
1381
|
for (const w of report.warnings)
|
|
1211
|
-
console.log(
|
|
1382
|
+
console.log(`${sweepUc ? "⚠" : "[!]"} ${w}`);
|
|
1212
1383
|
}
|
|
1213
1384
|
else if (event.type === "iteration_start") {
|
|
1214
|
-
|
|
1385
|
+
const progressBar = buildProgressBar(event.iteration - 1, event.total, 40);
|
|
1386
|
+
const divider = supportsColor() ? "\u2550".repeat(62) : "=".repeat(62);
|
|
1387
|
+
console.log(`\n${colorize(divider, "\x1b[35m")}`);
|
|
1215
1388
|
console.log(colorize(`[${new Date().toLocaleTimeString()}] Iteration ${event.iteration}/${event.total}: ${event.provider} (${event.model})`, "\x1b[1;35m"));
|
|
1216
|
-
console.log(
|
|
1389
|
+
console.log(` ${progressBar}`);
|
|
1390
|
+
console.log(colorize(divider, "\x1b[35m"));
|
|
1217
1391
|
}
|
|
1218
1392
|
else if (event.type === "message") {
|
|
1219
1393
|
if (!quiet) {
|
|
1220
1394
|
const truncText = event.text.length > 120 ? `${event.text.slice(0, 117)}...` : event.text;
|
|
1221
|
-
console.log(` ${colorize(`[${event.elapsed}]`, "\x1b[36m")} 💬 ${truncText}`);
|
|
1395
|
+
console.log(` ${colorize(`[${event.elapsed}]`, "\x1b[36m")} ${sweepUc ? "💬" : "[msg]"} ${truncText}`);
|
|
1222
1396
|
}
|
|
1223
1397
|
}
|
|
1224
1398
|
else if (event.type === "tool") {
|
|
1225
1399
|
if (!quiet) {
|
|
1226
1400
|
const c = event.counts;
|
|
1227
|
-
let icon = "🔧";
|
|
1401
|
+
let icon = sweepUc ? "🔧" : "[tool]";
|
|
1228
1402
|
let friendlyAction = event.action;
|
|
1229
1403
|
if (event.action.includes("read")) {
|
|
1230
|
-
icon = "📖";
|
|
1404
|
+
icon = sweepUc ? "📖" : "[read]";
|
|
1231
1405
|
friendlyAction = "read";
|
|
1232
1406
|
}
|
|
1233
1407
|
else if (event.action.includes("write") || event.action.includes("edit") || event.action === "str_replace") {
|
|
1234
|
-
icon = "✏️ ";
|
|
1408
|
+
icon = sweepUc ? "✏️ " : "[write]";
|
|
1235
1409
|
friendlyAction = "write";
|
|
1236
1410
|
}
|
|
1237
1411
|
else if (event.action.includes("shell") || event.action === "bash") {
|
|
1238
|
-
icon = "⚡";
|
|
1412
|
+
icon = sweepUc ? "⚡" : "[shell]";
|
|
1239
1413
|
friendlyAction = "shell";
|
|
1240
1414
|
}
|
|
1241
1415
|
else if (event.action.includes("search") || event.action.includes("grep") || event.action.includes("glob")) {
|
|
1242
|
-
icon = "🔍";
|
|
1416
|
+
icon = sweepUc ? "🔍" : "[find]";
|
|
1243
1417
|
friendlyAction = "search";
|
|
1244
1418
|
}
|
|
1245
1419
|
else if (event.action.includes("todo")) {
|
|
1246
|
-
icon = "📋";
|
|
1420
|
+
icon = sweepUc ? "📋" : "[todo]";
|
|
1247
1421
|
friendlyAction = "todo";
|
|
1248
1422
|
}
|
|
1249
1423
|
else if (event.action.includes("web") || event.action.includes("fetch")) {
|
|
1250
|
-
icon = "🌐";
|
|
1424
|
+
icon = sweepUc ? "🌐" : "[web]";
|
|
1251
1425
|
friendlyAction = "web";
|
|
1252
1426
|
}
|
|
1253
1427
|
else {
|
|
1254
1428
|
friendlyAction = event.action.replace(/ToolCall$/i, "").replace(/Tool$/i, "");
|
|
1255
1429
|
}
|
|
1256
1430
|
const fileStr = event.file ? ` ${colorize(event.file, "\x1b[90m")}` : "";
|
|
1257
|
-
const
|
|
1431
|
+
const searchIcon = sweepUc ? "🔍" : "Q";
|
|
1432
|
+
const countStr = colorize(`(R:${c.reads} W:${c.writes} S:${c.shells} ${searchIcon}:${c.searches})`, "\x1b[90m");
|
|
1258
1433
|
console.log(` ${icon} ${friendlyAction}${fileStr} ${countStr}`);
|
|
1259
1434
|
}
|
|
1260
1435
|
}
|
|
1261
1436
|
else if (event.type === "iteration_end") {
|
|
1262
1437
|
const elapsed = formatDuration(event.durationMs);
|
|
1263
|
-
const icon = event.status === "success" ? colorize("✓", "\x1b[32m") : colorize("✗", "\x1b[31m");
|
|
1438
|
+
const icon = event.status === "success" ? colorize(sweepUc ? "✓" : "OK", "\x1b[32m") : colorize(sweepUc ? "✗" : "FAIL", "\x1b[31m");
|
|
1264
1439
|
sweepDurations.push(event.durationMs);
|
|
1265
1440
|
const remaining = ((parseInt(String(getStringFlag(flags, "iterations"))) || 1) - event.iteration);
|
|
1266
1441
|
let etaStr = "";
|
|
1267
1442
|
if (remaining > 0 && sweepDurations.length > 0) {
|
|
1268
|
-
|
|
1443
|
+
// Use exponentially weighted moving average for better ETA (recent iterations weigh more)
|
|
1444
|
+
const n = sweepDurations.length;
|
|
1445
|
+
let avgMs;
|
|
1446
|
+
if (n <= 2) {
|
|
1447
|
+
avgMs = sweepDurations.reduce((a, b) => a + b, 0) / n;
|
|
1448
|
+
}
|
|
1449
|
+
else {
|
|
1450
|
+
const recentWeight = 0.7;
|
|
1451
|
+
const recentCount = Math.max(1, Math.floor(n / 2));
|
|
1452
|
+
const recentAvg = sweepDurations.slice(-recentCount).reduce((a, b) => a + b, 0) / recentCount;
|
|
1453
|
+
const overallAvg = sweepDurations.reduce((a, b) => a + b, 0) / n;
|
|
1454
|
+
avgMs = recentAvg * recentWeight + overallAvg * (1 - recentWeight);
|
|
1455
|
+
}
|
|
1269
1456
|
etaStr = ` | ETA: ~${formatDuration(avgMs * remaining)}`;
|
|
1270
1457
|
}
|
|
1271
1458
|
console.log(`${icon} Iteration ${event.iteration} ${event.status} (${elapsed})${etaStr}`);
|
|
1272
1459
|
}
|
|
1273
1460
|
else if (event.type === "attempt_retry") {
|
|
1274
|
-
console.log(colorize(` ⟳ Retry ${event.attempt}/${event.maxAttempts} for iteration ${event.iteration}, backoff ${event.backoffMs}ms`, "\x1b[33m"));
|
|
1461
|
+
console.log(colorize(` ${sweepUc ? "⟳" : "[retry]"} Retry ${event.attempt}/${event.maxAttempts} for iteration ${event.iteration}, backoff ${event.backoffMs}ms`, "\x1b[33m"));
|
|
1275
1462
|
}
|
|
1276
1463
|
else if (event.type === "summary") {
|
|
1277
1464
|
if (summaryLineCount > 0) {
|
|
1278
1465
|
const preview = event.lines.slice(0, summaryLineCount);
|
|
1279
|
-
|
|
1466
|
+
const sumDash = sweepUc ? "──" : "--";
|
|
1467
|
+
console.log(`\n${sumDash} Summary (iteration ${event.iteration}, ${preview.length}/${event.lines.length} lines) ${sumDash}`);
|
|
1280
1468
|
for (const line of preview)
|
|
1281
1469
|
console.log(` ${line}`);
|
|
1282
1470
|
if (event.lines.length > summaryLineCount) {
|
|
1283
1471
|
console.log(` ... (${event.lines.length - summaryLineCount} more lines)`);
|
|
1284
1472
|
}
|
|
1285
|
-
console.log(
|
|
1473
|
+
console.log(`${sumDash} End summary ${sumDash}`);
|
|
1286
1474
|
}
|
|
1287
1475
|
}
|
|
1288
1476
|
else if (event.type === "sweep_end") {
|
|
1289
1477
|
const r = event.result;
|
|
1290
1478
|
const cW = 62;
|
|
1291
|
-
const
|
|
1479
|
+
const uc = supportsColor();
|
|
1480
|
+
const [boxTL, boxH, boxTR, boxV, boxML, boxMR, boxBL, boxBR] = uc
|
|
1481
|
+
? ["\u2554", "\u2550", "\u2557", "\u2551", "\u2560", "\u2563", "\u255A", "\u255D"]
|
|
1482
|
+
: ["+", "=", "+", "|", "+", "+", "+", "+"];
|
|
1483
|
+
const cBar = boxH.repeat(cW);
|
|
1292
1484
|
const cRow = (label, val) => {
|
|
1293
1485
|
const plain = ` ${label.padEnd(12)} ${val}`;
|
|
1294
|
-
console.log(`${colorize(
|
|
1486
|
+
console.log(`${colorize(boxV, "\x1b[36m")}${plain.padEnd(cW)}${colorize(boxV, "\x1b[36m")}`);
|
|
1295
1487
|
};
|
|
1296
1488
|
console.log("");
|
|
1297
|
-
console.log(colorize(
|
|
1489
|
+
console.log(colorize(`${boxTL}${cBar}${boxTR}`, "\x1b[36m"));
|
|
1298
1490
|
const cTitle = "Sweep Complete";
|
|
1299
1491
|
const cTitlePad = Math.floor((cW - cTitle.length) / 2);
|
|
1300
|
-
console.log(colorize(
|
|
1301
|
-
console.log(colorize(
|
|
1492
|
+
console.log(colorize(`${boxV}${" ".repeat(cTitlePad)}${cTitle}${" ".repeat(cW - cTitlePad - cTitle.length)}${boxV}`, "\x1b[36m"));
|
|
1493
|
+
console.log(colorize(`${boxML}${cBar}${boxMR}`, "\x1b[36m"));
|
|
1302
1494
|
cRow("Model:", r.model);
|
|
1303
1495
|
cRow("Duration:", formatDuration(r.totalDurationMs));
|
|
1304
1496
|
cRow("Iterations:", String(r.totalIterations));
|
|
@@ -1306,13 +1498,13 @@ async function runCommand(command, flags) {
|
|
|
1306
1498
|
if (r.failed > 0) {
|
|
1307
1499
|
cRow("Failed:", String(r.failed));
|
|
1308
1500
|
}
|
|
1309
|
-
console.log(colorize(
|
|
1501
|
+
console.log(colorize(`${boxML}${cBar}${boxMR}`, "\x1b[36m"));
|
|
1310
1502
|
for (const iter of r.iterations) {
|
|
1311
|
-
const statusIcon = iter.status === "success" ? "
|
|
1503
|
+
const statusIcon = iter.status === "success" ? (uc ? "\u2713" : "+") : (uc ? "\u2717" : "x");
|
|
1312
1504
|
const plain = ` ${statusIcon} Iter ${iter.iteration}: ${iter.status.padEnd(16)} ${formatDuration(iter.durationMs)}`;
|
|
1313
|
-
console.log(`${colorize(
|
|
1505
|
+
console.log(`${colorize(boxV, "\x1b[36m")}${plain.padEnd(cW)}${colorize(boxV, "\x1b[36m")}`);
|
|
1314
1506
|
}
|
|
1315
|
-
console.log(colorize(
|
|
1507
|
+
console.log(colorize(`${boxBL}${cBar}${boxBR}`, "\x1b[36m"));
|
|
1316
1508
|
if (hasTokenUsage(r.tokenUsage)) {
|
|
1317
1509
|
const tu = r.tokenUsage;
|
|
1318
1510
|
console.log(`Tokens: ${formatTokenUsage(tu)}`);
|
|
@@ -1323,6 +1515,72 @@ async function runCommand(command, flags) {
|
|
|
1323
1515
|
}
|
|
1324
1516
|
}
|
|
1325
1517
|
};
|
|
1518
|
+
// Build eval config if --eval flag is set
|
|
1519
|
+
const evalConfig = Boolean(flags.eval) ? {
|
|
1520
|
+
enabled: true,
|
|
1521
|
+
evaluatorAgent: getStringFlag(flags, "eval-agent"),
|
|
1522
|
+
criteria: getStringFlag(flags, "eval-criteria")?.split(",").map((c) => c.trim()),
|
|
1523
|
+
} : undefined;
|
|
1524
|
+
// Parallel sweep — run N iterations concurrently
|
|
1525
|
+
const parallelCount = parsePositiveIntFlag(getStringFlag(flags, "parallel"), "parallel");
|
|
1526
|
+
if (parallelCount && parallelCount > 1) {
|
|
1527
|
+
const totalIterations = parsePositiveIntFlag(getStringFlag(flags, "iterations"), "iterations") ?? 1;
|
|
1528
|
+
const batchSize = Math.min(parallelCount, totalIterations);
|
|
1529
|
+
if (totalIterations > 1) {
|
|
1530
|
+
console.log(`Note: Parallel sweep runs independent iterations (no inter-iteration summary chaining).`);
|
|
1531
|
+
}
|
|
1532
|
+
console.log(`Running ${totalIterations} iterations with parallelism=${batchSize}...`);
|
|
1533
|
+
const allResults = [];
|
|
1534
|
+
const parallelWallClockStart = Date.now();
|
|
1535
|
+
for (let batchStart = 0; batchStart < totalIterations; batchStart += batchSize) {
|
|
1536
|
+
const batchEnd = Math.min(batchStart + batchSize, totalIterations);
|
|
1537
|
+
const batchPromises = [];
|
|
1538
|
+
for (let i = batchStart; i < batchEnd; i++) {
|
|
1539
|
+
batchPromises.push(sweepPrompt({
|
|
1540
|
+
cwd,
|
|
1541
|
+
promptFile: getStringFlag(flags, "prompt-file"),
|
|
1542
|
+
agent,
|
|
1543
|
+
model: getStringFlag(flags, "model"),
|
|
1544
|
+
iterations: 1,
|
|
1545
|
+
iterationTimeoutSeconds: parsePositiveIntFlag(getStringFlag(flags, "iteration-timeout"), "iteration-timeout"),
|
|
1546
|
+
maxRetries: parseNonNegativeIntFlag(getStringFlag(flags, "max-retries"), "max-retries"),
|
|
1547
|
+
artifactsDir: getStringFlag(flags, "artifacts-dir"),
|
|
1548
|
+
runId: `${getStringFlag(flags, "run-id") || "sweep"}-parallel-${i + 1}`,
|
|
1549
|
+
approveMcps: !Boolean(flags["no-approve-mcps"]),
|
|
1550
|
+
sandboxMode: getStringFlag(flags, "sandbox"),
|
|
1551
|
+
phase: getStringFlag(flags, "phase"),
|
|
1552
|
+
dryRun: Boolean(flags["dry-run"]),
|
|
1553
|
+
maxRunDirs: parsePositiveIntFlag(getStringFlag(flags, "max-run-dirs"), "max-run-dirs"),
|
|
1554
|
+
summaryLines: parsePositiveIntFlag(getStringFlag(flags, "summary-lines"), "summary-lines"),
|
|
1555
|
+
background: Boolean(flags.background),
|
|
1556
|
+
permissionMode: getStringFlag(flags, "permission-mode"),
|
|
1557
|
+
evalAfterEachIteration: evalConfig,
|
|
1558
|
+
}));
|
|
1559
|
+
}
|
|
1560
|
+
const batchResults = await Promise.allSettled(batchPromises);
|
|
1561
|
+
for (const br of batchResults) {
|
|
1562
|
+
if (br.status === "fulfilled")
|
|
1563
|
+
allResults.push(br.value);
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
const totalSucceeded = allResults.reduce((s, r) => s + r.succeeded, 0);
|
|
1567
|
+
const totalFailed = allResults.reduce((s, r) => s + r.failed, 0);
|
|
1568
|
+
const totalDuration = Date.now() - parallelWallClockStart;
|
|
1569
|
+
if (Boolean(flags.json)) {
|
|
1570
|
+
const jsonResults = allResults.map((r) => ({
|
|
1571
|
+
...r,
|
|
1572
|
+
iterations: r.iterations.map((it) => ({ ...it, toolCounts: it.toolCounts ?? null })),
|
|
1573
|
+
}));
|
|
1574
|
+
console.log(JSON.stringify({ parallel: true, results: jsonResults, totalSucceeded, totalFailed, totalDurationMs: totalDuration }, null, 2));
|
|
1575
|
+
}
|
|
1576
|
+
else {
|
|
1577
|
+
console.log(`\nParallel sweep complete: ${totalSucceeded}/${totalIterations} succeeded, ${totalFailed} failed`);
|
|
1578
|
+
console.log(`Total wall-clock time: ${formatDuration(totalDuration)}`);
|
|
1579
|
+
}
|
|
1580
|
+
if (totalFailed > 0)
|
|
1581
|
+
process.exitCode = 1;
|
|
1582
|
+
return;
|
|
1583
|
+
}
|
|
1326
1584
|
const result = await sweepPrompt({
|
|
1327
1585
|
cwd,
|
|
1328
1586
|
promptFile: getStringFlag(flags, "prompt-file"),
|
|
@@ -1341,6 +1599,7 @@ async function runCommand(command, flags) {
|
|
|
1341
1599
|
summaryLines: parsePositiveIntFlag(getStringFlag(flags, "summary-lines"), "summary-lines"),
|
|
1342
1600
|
background: Boolean(flags.background),
|
|
1343
1601
|
permissionMode: getStringFlag(flags, "permission-mode"),
|
|
1602
|
+
evalAfterEachIteration: evalConfig,
|
|
1344
1603
|
onProgress,
|
|
1345
1604
|
});
|
|
1346
1605
|
if (Boolean(flags.json)) {
|
|
@@ -1439,6 +1698,223 @@ async function runCommand(command, flags) {
|
|
|
1439
1698
|
}
|
|
1440
1699
|
return;
|
|
1441
1700
|
}
|
|
1701
|
+
if (command === "orchestrate") {
|
|
1702
|
+
const cwd = getResolvedCwd(flags);
|
|
1703
|
+
const mode = getStringFlag(flags, "mode");
|
|
1704
|
+
if (!mode || !["parallel", "pipeline", "eval"].includes(mode)) {
|
|
1705
|
+
throw new CliError("The --mode flag is required and must be one of: parallel, pipeline, eval", CLI_EXIT_CODES.usage);
|
|
1706
|
+
}
|
|
1707
|
+
const dryRun = Boolean(flags["dry-run"]);
|
|
1708
|
+
const jsonOutput = Boolean(flags.json);
|
|
1709
|
+
const timeoutSeconds = parsePositiveIntFlag(getStringFlag(flags, "timeout"), "timeout");
|
|
1710
|
+
const orchUc = supportsColor();
|
|
1711
|
+
const orchBar = orchUc ? "═══" : "===";
|
|
1712
|
+
const onProgress = jsonOutput
|
|
1713
|
+
? undefined
|
|
1714
|
+
: (event) => {
|
|
1715
|
+
if (event.type === "orchestrate_start") {
|
|
1716
|
+
console.log(`\n${colorize(`${orchBar} Orchestration`, "\x1b[35m")} mode=${event.mode}${event.providers ? ` providers=${event.providers.join(",")}` : ""} ${colorize(orchBar, "\x1b[35m")}`);
|
|
1717
|
+
}
|
|
1718
|
+
else if (event.type === "provider_start") {
|
|
1719
|
+
const stepLabel = event.step ? ` [${event.step}]` : "";
|
|
1720
|
+
console.log(` ${orchUc ? "▶" : ">"} ${event.provider}${stepLabel} starting...`);
|
|
1721
|
+
}
|
|
1722
|
+
else if (event.type === "provider_end") {
|
|
1723
|
+
const icon = event.exitCode === 0 ? colorize(orchUc ? "✓" : "OK", "\x1b[32m") : colorize(orchUc ? "✗" : "FAIL", "\x1b[31m");
|
|
1724
|
+
console.log(` ${icon} ${event.provider} finished (${formatDuration(event.durationMs)}, exit=${event.exitCode})`);
|
|
1725
|
+
}
|
|
1726
|
+
else if (event.type === "eval_start") {
|
|
1727
|
+
console.log(` ${orchUc ? "🧪" : "[eval]"} Evaluating with ${event.evaluator}...`);
|
|
1728
|
+
}
|
|
1729
|
+
else if (event.type === "eval_end") {
|
|
1730
|
+
console.log(` ${orchUc ? "📊" : "[score]"} Score: ${event.score}/100`);
|
|
1731
|
+
}
|
|
1732
|
+
else if (event.type === "orchestrate_end") {
|
|
1733
|
+
console.log(`\n${colorize(orchBar, "\x1b[35m")} Done in ${formatDuration(event.totalDurationMs)}${event.winner ? ` | Winner: ${event.winner}` : ""} ${colorize(orchBar, "\x1b[35m")}\n`);
|
|
1734
|
+
}
|
|
1735
|
+
};
|
|
1736
|
+
if (mode === "parallel") {
|
|
1737
|
+
const providersStr = getStringFlag(flags, "providers");
|
|
1738
|
+
if (!providersStr) {
|
|
1739
|
+
throw new CliError("--providers is required for parallel mode (e.g. --providers codex,claude,cursor)", CLI_EXIT_CODES.usage);
|
|
1740
|
+
}
|
|
1741
|
+
const providers = providersStr.split(",").map((p) => p.trim()).filter(Boolean);
|
|
1742
|
+
const promptFile = getStringFlag(flags, "prompt-file");
|
|
1743
|
+
if (!promptFile) {
|
|
1744
|
+
throw new CliError("--prompt-file is required for orchestrate", CLI_EXIT_CODES.usage);
|
|
1745
|
+
}
|
|
1746
|
+
const criteriaStr = getStringFlag(flags, "criteria");
|
|
1747
|
+
const criteria = criteriaStr ? criteriaStr.split(",").map((c) => c.trim()) : undefined;
|
|
1748
|
+
const evaluator = getStringFlag(flags, "evaluator");
|
|
1749
|
+
const result = await orchestrateParallel({
|
|
1750
|
+
cwd,
|
|
1751
|
+
promptFile,
|
|
1752
|
+
providers,
|
|
1753
|
+
scoringCriteria: criteria,
|
|
1754
|
+
evaluatorProvider: evaluator,
|
|
1755
|
+
timeoutSeconds,
|
|
1756
|
+
dryRun,
|
|
1757
|
+
onProgress,
|
|
1758
|
+
});
|
|
1759
|
+
if (jsonOutput) {
|
|
1760
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1761
|
+
}
|
|
1762
|
+
else {
|
|
1763
|
+
console.log("Results:");
|
|
1764
|
+
for (const run of result.runs) {
|
|
1765
|
+
const icon = run.result.exitCode === 0 ? sym("✓", "+") : sym("✗", "x");
|
|
1766
|
+
console.log(` ${icon} ${run.provider}: ${run.score}/100 — ${run.reasoning}`);
|
|
1767
|
+
}
|
|
1768
|
+
if (result.winner) {
|
|
1769
|
+
console.log(`\n ${sym("🏆", "[W]")} Winner: ${result.winner.provider} (${result.winner.score}/100)`);
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
return;
|
|
1773
|
+
}
|
|
1774
|
+
if (mode === "pipeline") {
|
|
1775
|
+
const stepsFile = getStringFlag(flags, "steps");
|
|
1776
|
+
if (!stepsFile) {
|
|
1777
|
+
throw new CliError("--steps <pipeline-config.json> is required for pipeline mode", CLI_EXIT_CODES.usage);
|
|
1778
|
+
}
|
|
1779
|
+
if (!existsSync(stepsFile)) {
|
|
1780
|
+
throw new CliError(`Pipeline steps file not found: ${stepsFile}`, CLI_EXIT_CODES.usage);
|
|
1781
|
+
}
|
|
1782
|
+
const stepsJson = JSON.parse(readFileSync(stepsFile, "utf8"));
|
|
1783
|
+
if (!Array.isArray(stepsJson) || stepsJson.length === 0) {
|
|
1784
|
+
throw new CliError("Pipeline steps file must contain a non-empty JSON array", CLI_EXIT_CODES.usage);
|
|
1785
|
+
}
|
|
1786
|
+
for (const step of stepsJson) {
|
|
1787
|
+
if (step.promptFile && !existsSync(path.resolve(cwd, step.promptFile))) {
|
|
1788
|
+
throw new CliError(`Pipeline step "${step.name}" references missing prompt file: ${step.promptFile}`, CLI_EXIT_CODES.usage);
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1791
|
+
const result = await orchestratePipeline({
|
|
1792
|
+
cwd,
|
|
1793
|
+
steps: stepsJson,
|
|
1794
|
+
timeoutSeconds,
|
|
1795
|
+
dryRun,
|
|
1796
|
+
onProgress,
|
|
1797
|
+
});
|
|
1798
|
+
if (jsonOutput) {
|
|
1799
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1800
|
+
}
|
|
1801
|
+
else {
|
|
1802
|
+
console.log("Pipeline Results:");
|
|
1803
|
+
for (const step of result.steps) {
|
|
1804
|
+
const icon = step.result.exitCode === 0 ? sym("✓", "+") : sym("✗", "x");
|
|
1805
|
+
console.log(` ${icon} ${step.name} (${step.provider}) — ${formatDuration(step.durationMs)}`);
|
|
1806
|
+
}
|
|
1807
|
+
console.log(`\n Total: ${formatDuration(result.totalDurationMs)}`);
|
|
1808
|
+
}
|
|
1809
|
+
return;
|
|
1810
|
+
}
|
|
1811
|
+
if (mode === "eval") {
|
|
1812
|
+
const promptFile = getStringFlag(flags, "prompt-file");
|
|
1813
|
+
if (!promptFile) {
|
|
1814
|
+
throw new CliError("--prompt-file is required for orchestrate eval mode", CLI_EXIT_CODES.usage);
|
|
1815
|
+
}
|
|
1816
|
+
const agent = getStringFlag(flags, "agent");
|
|
1817
|
+
const evaluator = getStringFlag(flags, "evaluator");
|
|
1818
|
+
const criteriaStr = getStringFlag(flags, "criteria");
|
|
1819
|
+
const criteria = criteriaStr ? criteriaStr.split(",").map((c) => c.trim()) : undefined;
|
|
1820
|
+
const result = await orchestrateEval({
|
|
1821
|
+
cwd,
|
|
1822
|
+
promptFile,
|
|
1823
|
+
agent,
|
|
1824
|
+
evaluatorAgent: evaluator,
|
|
1825
|
+
criteria,
|
|
1826
|
+
timeoutSeconds,
|
|
1827
|
+
dryRun,
|
|
1828
|
+
onProgress,
|
|
1829
|
+
});
|
|
1830
|
+
if (jsonOutput) {
|
|
1831
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1832
|
+
}
|
|
1833
|
+
else {
|
|
1834
|
+
console.log(`Run: exit=${result.run.exitCode ?? "?"} provider=${result.run.provider ?? "?"}`);
|
|
1835
|
+
console.log(`\nEvaluation:`);
|
|
1836
|
+
console.log(` Score: ${result.evaluation.score}/${result.evaluation.maxScore}`);
|
|
1837
|
+
console.log(` Reasoning: ${result.evaluation.reasoning}`);
|
|
1838
|
+
if (Object.keys(result.evaluation.criteriaScores).length > 0) {
|
|
1839
|
+
console.log(` Criteria:`);
|
|
1840
|
+
for (const [key, value] of Object.entries(result.evaluation.criteriaScores)) {
|
|
1841
|
+
console.log(` ${key}: ${value}/10`);
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
if (result.evaluation.suggestions.length > 0) {
|
|
1845
|
+
console.log(` Suggestions:`);
|
|
1846
|
+
for (const s of result.evaluation.suggestions) {
|
|
1847
|
+
console.log(` - ${s}`);
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
return;
|
|
1852
|
+
}
|
|
1853
|
+
return;
|
|
1854
|
+
}
|
|
1855
|
+
if (command === "diff") {
|
|
1856
|
+
const cwd = getResolvedCwd(flags);
|
|
1857
|
+
const runId = getStringFlag(flags, "run-id");
|
|
1858
|
+
const config = await loadRunConfig(cwd);
|
|
1859
|
+
const artifactsDir = path.resolve(cwd, config.artifactsDir);
|
|
1860
|
+
if (!runId) {
|
|
1861
|
+
if (!existsSync(artifactsDir)) {
|
|
1862
|
+
throw new CliError("No artifacts directory found. Run a sweep or prompt first.", CLI_EXIT_CODES.usage);
|
|
1863
|
+
}
|
|
1864
|
+
const { readdirSync: listDir } = await import("node:fs");
|
|
1865
|
+
const dirs = listDir(artifactsDir).filter((d) => {
|
|
1866
|
+
try {
|
|
1867
|
+
return statSync(path.join(artifactsDir, d)).isDirectory();
|
|
1868
|
+
}
|
|
1869
|
+
catch {
|
|
1870
|
+
return false;
|
|
1871
|
+
}
|
|
1872
|
+
}).sort().reverse().slice(0, 10);
|
|
1873
|
+
if (dirs.length === 0) {
|
|
1874
|
+
throw new CliError("No run directories found in artifacts.", CLI_EXIT_CODES.usage);
|
|
1875
|
+
}
|
|
1876
|
+
console.log("Recent runs:");
|
|
1877
|
+
for (const d of dirs)
|
|
1878
|
+
console.log(` ${d}`);
|
|
1879
|
+
console.log(`\nUsage: prompts-gpt diff <run-id>`);
|
|
1880
|
+
return;
|
|
1881
|
+
}
|
|
1882
|
+
const runDir = path.resolve(artifactsDir, runId);
|
|
1883
|
+
const caseInsensitive = process.platform === "win32";
|
|
1884
|
+
const runDirCheck = caseInsensitive ? runDir.toLowerCase() : runDir;
|
|
1885
|
+
const artDirCheck = caseInsensitive ? artifactsDir.toLowerCase() : artifactsDir;
|
|
1886
|
+
if (!runDirCheck.startsWith(artDirCheck + path.sep) && runDirCheck !== artDirCheck) {
|
|
1887
|
+
throw new CliError("Invalid run-id: path escapes the artifacts directory.", CLI_EXIT_CODES.usage);
|
|
1888
|
+
}
|
|
1889
|
+
if (!existsSync(runDir)) {
|
|
1890
|
+
throw new CliError(`Run directory not found: ${runDir}`, CLI_EXIT_CODES.usage);
|
|
1891
|
+
}
|
|
1892
|
+
const { readFile: fsRead } = await import("node:fs/promises");
|
|
1893
|
+
const deltaFile = path.join(runDir, "worktree-delta.diff");
|
|
1894
|
+
if (existsSync(deltaFile)) {
|
|
1895
|
+
const delta = await fsRead(deltaFile, "utf8");
|
|
1896
|
+
if (delta.trim()) {
|
|
1897
|
+
console.log(delta);
|
|
1898
|
+
}
|
|
1899
|
+
else {
|
|
1900
|
+
console.log("No worktree changes recorded for this run.");
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
else {
|
|
1904
|
+
const beforeFile = path.join(runDir, "worktree-before.txt");
|
|
1905
|
+
const afterFile = path.join(runDir, "worktree-after.txt");
|
|
1906
|
+
if (existsSync(beforeFile) && existsSync(afterFile)) {
|
|
1907
|
+
const before = await fsRead(beforeFile, "utf8");
|
|
1908
|
+
const after = await fsRead(afterFile, "utf8");
|
|
1909
|
+
console.log("Before:\n" + before.slice(0, 2000));
|
|
1910
|
+
console.log("\nAfter:\n" + after.slice(0, 2000));
|
|
1911
|
+
}
|
|
1912
|
+
else {
|
|
1913
|
+
console.log("No diff data available for this run.");
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
return;
|
|
1917
|
+
}
|
|
1442
1918
|
if (command === "quickstart") {
|
|
1443
1919
|
const cwd = getResolvedCwd(flags);
|
|
1444
1920
|
const assets = await discoverWorkspaceAssets(cwd);
|
|
@@ -1450,7 +1926,7 @@ async function runCommand(command, flags) {
|
|
|
1450
1926
|
const { spawnSync: gitCheck } = await import("node:child_process");
|
|
1451
1927
|
const gitResult = gitCheck("git", ["rev-parse", "--is-inside-work-tree"], { cwd, encoding: "utf8", timeout: 5000, windowsHide: true });
|
|
1452
1928
|
if (gitResult.status !== 0) {
|
|
1453
|
-
console.log("⚠ Not inside a git repository. Prompts-GPT works best in a git repo for worktree tracking
|
|
1929
|
+
console.log(`${sym("⚠", "!")} Not inside a git repository. Prompts-GPT works best in a git repo for worktree tracking.`);
|
|
1454
1930
|
console.log(" Run: git init\n");
|
|
1455
1931
|
}
|
|
1456
1932
|
if (!assets.credentialsFound) {
|
|
@@ -1462,7 +1938,7 @@ async function runCommand(command, flags) {
|
|
|
1462
1938
|
networkOk = false;
|
|
1463
1939
|
}
|
|
1464
1940
|
if (!networkOk) {
|
|
1465
|
-
console.log("⚠ Network unavailable. Quickstart requires internet for token validation.\n
|
|
1941
|
+
console.log(`${sym("⚠", "!")} Network unavailable. Quickstart requires internet for token validation.\n`);
|
|
1466
1942
|
console.log("You can still use local sweep files without a network connection:");
|
|
1467
1943
|
console.log(" 1. Create .prompts-gpt/sweeps/<name>.md with your sweep prompt");
|
|
1468
1944
|
console.log(" 2. Run: prompts-gpt sweep");
|
|
@@ -1477,29 +1953,29 @@ async function runCommand(command, flags) {
|
|
|
1477
1953
|
console.log("Run `prompts-gpt quickstart` again after saving your token.");
|
|
1478
1954
|
return;
|
|
1479
1955
|
}
|
|
1480
|
-
console.log("✓ Credentials found
|
|
1956
|
+
console.log(`${sym("✓", "+")} Credentials found`);
|
|
1481
1957
|
if (!assets.configFound) {
|
|
1482
|
-
console.log("→ Setting up config
|
|
1958
|
+
console.log(`${sym("→", "->")} Setting up config...`);
|
|
1483
1959
|
try {
|
|
1484
1960
|
await initRunConfig({ cwd, overwrite: false });
|
|
1485
|
-
console.log("✓ Config created
|
|
1961
|
+
console.log(`${sym("✓", "+")} Config created`);
|
|
1486
1962
|
}
|
|
1487
1963
|
catch {
|
|
1488
|
-
console.log("✓ Config already exists
|
|
1964
|
+
console.log(`${sym("✓", "+")} Config already exists`);
|
|
1489
1965
|
}
|
|
1490
1966
|
}
|
|
1491
1967
|
else {
|
|
1492
|
-
console.log("✓ Config found
|
|
1968
|
+
console.log(`${sym("✓", "+")} Config found`);
|
|
1493
1969
|
}
|
|
1494
1970
|
if (availableProviders.length === 0) {
|
|
1495
1971
|
console.log("");
|
|
1496
|
-
console.log("✗ No provider CLIs found. Install at least one
|
|
1972
|
+
console.log(`${sym("✗", "x")} No provider CLIs found. Install at least one:`);
|
|
1497
1973
|
console.log(" - codex: npm install -g @openai/codex");
|
|
1498
1974
|
console.log(" - claude: npm install -g @anthropic-ai/claude-code");
|
|
1499
1975
|
console.log(" - cursor: Install Cursor IDE (includes `agent` CLI)");
|
|
1500
1976
|
return;
|
|
1501
1977
|
}
|
|
1502
|
-
console.log(
|
|
1978
|
+
console.log(`${sym("✓", "+")} Providers: ${availableProviders.map((p) => p.provider).join(", ")}`);
|
|
1503
1979
|
if (assets.prompts.length === 0 && assets.sweeps.length === 0) {
|
|
1504
1980
|
console.log("");
|
|
1505
1981
|
console.log("→ No prompts found. Syncing from Prompts Studio...");
|
|
@@ -2163,6 +2639,7 @@ function getCommandOptions(command) {
|
|
|
2163
2639
|
"non-interactive": { type: "boolean" },
|
|
2164
2640
|
verbose: { type: "boolean", short: "v" },
|
|
2165
2641
|
open: { type: "boolean" },
|
|
2642
|
+
watch: { type: "boolean", short: "w" },
|
|
2166
2643
|
"list-models": { type: "boolean" },
|
|
2167
2644
|
};
|
|
2168
2645
|
}
|
|
@@ -2191,7 +2668,38 @@ function getCommandOptions(command) {
|
|
|
2191
2668
|
verbose: { type: "boolean", short: "v" },
|
|
2192
2669
|
open: { type: "boolean" },
|
|
2193
2670
|
quiet: { type: "boolean", short: "q" },
|
|
2671
|
+
parallel: { type: "string" },
|
|
2672
|
+
eval: { type: "boolean" },
|
|
2673
|
+
"eval-criteria": { type: "string" },
|
|
2674
|
+
"eval-agent": { type: "string" },
|
|
2194
2675
|
"list-models": { type: "boolean" },
|
|
2676
|
+
"force-unlock": { type: "boolean" },
|
|
2677
|
+
"allow-destructive-git": { type: "boolean" },
|
|
2678
|
+
};
|
|
2679
|
+
}
|
|
2680
|
+
if (command === "orchestrate") {
|
|
2681
|
+
return {
|
|
2682
|
+
help: { type: "boolean" },
|
|
2683
|
+
cwd: { type: "string" },
|
|
2684
|
+
json: { type: "boolean" },
|
|
2685
|
+
mode: { type: "string" },
|
|
2686
|
+
"prompt-file": { type: "string", short: "f" },
|
|
2687
|
+
providers: { type: "string" },
|
|
2688
|
+
evaluator: { type: "string" },
|
|
2689
|
+
criteria: { type: "string" },
|
|
2690
|
+
steps: { type: "string" },
|
|
2691
|
+
agent: { type: "string" },
|
|
2692
|
+
model: { type: "string" },
|
|
2693
|
+
timeout: { type: "string" },
|
|
2694
|
+
"dry-run": { type: "boolean" },
|
|
2695
|
+
"non-interactive": { type: "boolean" },
|
|
2696
|
+
};
|
|
2697
|
+
}
|
|
2698
|
+
if (command === "diff") {
|
|
2699
|
+
return {
|
|
2700
|
+
help: { type: "boolean" },
|
|
2701
|
+
cwd: { type: "string" },
|
|
2702
|
+
"run-id": { type: "string" },
|
|
2195
2703
|
};
|
|
2196
2704
|
}
|
|
2197
2705
|
if (command === "models") {
|
|
@@ -2228,7 +2736,15 @@ function getCommandOptions(command) {
|
|
|
2228
2736
|
agent: { type: "string" },
|
|
2229
2737
|
};
|
|
2230
2738
|
}
|
|
2231
|
-
if (command === "
|
|
2739
|
+
if (command === "doctor") {
|
|
2740
|
+
return {
|
|
2741
|
+
help: { type: "boolean" },
|
|
2742
|
+
cwd: { type: "string" },
|
|
2743
|
+
json: { type: "boolean" },
|
|
2744
|
+
fix: { type: "boolean" },
|
|
2745
|
+
};
|
|
2746
|
+
}
|
|
2747
|
+
if (command === "quickstart" || command === "list" || command === "status" || command === "validate" || command === "providers") {
|
|
2232
2748
|
return {
|
|
2233
2749
|
help: { type: "boolean" },
|
|
2234
2750
|
cwd: { type: "string" },
|
|
@@ -2498,7 +3014,10 @@ function validateProviderOrderFlag(values) {
|
|
|
2498
3014
|
}
|
|
2499
3015
|
}
|
|
2500
3016
|
function getResolvedCwd(flags) {
|
|
2501
|
-
|
|
3017
|
+
const raw = getStringFlag(flags, "cwd")?.trim();
|
|
3018
|
+
// B19: Strip surrounding quotes that may appear when shell doesn't consume them
|
|
3019
|
+
const cleaned = raw?.replace(/^["']|["']$/g, "");
|
|
3020
|
+
return path.resolve(cleaned || process.cwd());
|
|
2502
3021
|
}
|
|
2503
3022
|
function getStringFlag(flags, name) {
|
|
2504
3023
|
const value = flags[name];
|
|
@@ -2618,11 +3137,22 @@ Global options:
|
|
|
2618
3137
|
--json Output structured JSON (for run, sweep, list, etc.)
|
|
2619
3138
|
|
|
2620
3139
|
Environment variables:
|
|
2621
|
-
PROMPTS_GPT_TOKEN
|
|
2622
|
-
PROMPTS_GPT_API_URL
|
|
2623
|
-
PROMPTS_GPT_MODEL
|
|
2624
|
-
|
|
2625
|
-
|
|
3140
|
+
PROMPTS_GPT_TOKEN Project token (overrides stored credentials)
|
|
3141
|
+
PROMPTS_GPT_API_URL API URL (default: https://prompts-gpt.com)
|
|
3142
|
+
PROMPTS_GPT_MODEL Global model override (all providers)
|
|
3143
|
+
PROMPTS_GPT_RUN_MODEL_CODEX Model override for codex provider
|
|
3144
|
+
PROMPTS_GPT_RUN_MODEL_CLAUDE Model override for claude provider
|
|
3145
|
+
PROMPTS_GPT_RUN_MODEL_CURSOR Model override for cursor provider
|
|
3146
|
+
PROMPTS_GPT_RUN_MODEL_COPILOT Model override for copilot provider
|
|
3147
|
+
PROMPTS_GPT_RUN_AGENT Default agent (codex|claude|cursor|copilot|router)
|
|
3148
|
+
PROMPTS_GPT_RUN_PROVIDER_ORDER Provider priority (comma-separated)
|
|
3149
|
+
PROMPTS_GPT_RUN_TIMEOUT_SECONDS Default timeout for runs
|
|
3150
|
+
PROMPTS_GPT_RUN_RETRY_COUNT Number of retries on transient failures
|
|
3151
|
+
PROMPTS_GPT_RUN_ARTIFACTS_DIR Custom artifacts directory
|
|
3152
|
+
PROMPTS_GPT_RUN_DISALLOW_DESTRUCTIVE_GIT Block git stash/reset (true/false)
|
|
3153
|
+
PROMPTS_GPT_DEBUG=1 Enable debug logging
|
|
3154
|
+
PROMPTS_GPT_NON_INTERACTIVE=1 Disable interactive prompts
|
|
3155
|
+
NO_COLOR=1 Disable colored output
|
|
2626
3156
|
|
|
2627
3157
|
Providers: ${ORCHESTRATION_AGENT_PROFILES.filter((p) => p !== "router").join(", ")} (or router for auto-select)
|
|
2628
3158
|
Agent targets: ${SUPPORTED_AGENT_TARGETS.join(", ")}
|
|
@@ -2707,24 +3237,29 @@ const PROVIDER_MODELS = Object.freeze({
|
|
|
2707
3237
|
{ value: "gpt-5.4-pro", label: "gpt-5.4-pro — enhanced responses", tier: "standard" },
|
|
2708
3238
|
{ value: "gpt-5.4-mini", label: "gpt-5.4-mini — fast coding & subagents", tier: "fast" },
|
|
2709
3239
|
{ value: "gpt-5.4-nano", label: "gpt-5.4-nano — cheapest high-volume", tier: "budget" },
|
|
2710
|
-
{ value: "gpt-5.3-codex", label: "gpt-5.3-codex —
|
|
3240
|
+
{ value: "gpt-5.3-codex", label: "gpt-5.3-codex — merged GPT-5 + Codex", tier: "standard" },
|
|
2711
3241
|
{ value: "gpt-5.3-codex-spark", label: "gpt-5.3-codex-spark — 15x faster gen", tier: "fast" },
|
|
3242
|
+
{ value: "gpt-5.2-codex", label: "gpt-5.2-codex — API key auth recommended", tier: "standard" },
|
|
2712
3243
|
{ value: "o4-mini", label: "o4-mini — fast reasoning", tier: "fast" },
|
|
2713
3244
|
{ value: "o3", label: "o3 — advanced reasoning", tier: "frontier" },
|
|
2714
3245
|
{ value: "gpt-5.1-codex", label: "gpt-5.1-codex — legacy codex", tier: "budget" },
|
|
3246
|
+
{ value: "gpt-5.1-codex-mini", label: "gpt-5.1-codex-mini — small legacy", tier: "budget" },
|
|
2715
3247
|
{ value: "gpt-4.1", label: "gpt-4.1 — legacy", tier: "budget" },
|
|
2716
3248
|
],
|
|
2717
3249
|
claude: [
|
|
3250
|
+
{ value: "claude-opus-4-7", label: "claude-opus-4-7 — most capable model", tier: "frontier" },
|
|
2718
3251
|
{ value: "claude-sonnet-4-6", label: "claude-sonnet-4-6 — speed + intelligence ★", tier: "standard" },
|
|
2719
|
-
{ value: "claude-opus-4-
|
|
3252
|
+
{ value: "claude-opus-4-6", label: "claude-opus-4-6 — previous gen frontier", tier: "frontier" },
|
|
3253
|
+
{ value: "claude-opus-4-5", label: "claude-opus-4-5 — legacy opus", tier: "standard" },
|
|
2720
3254
|
{ value: "claude-haiku-4-5", label: "claude-haiku-4-5 — fastest near-frontier", tier: "fast" },
|
|
2721
3255
|
],
|
|
2722
3256
|
cursor: [
|
|
2723
3257
|
{ value: "auto", label: "auto — Cursor auto-selects best", tier: "standard" },
|
|
3258
|
+
{ value: "opus-4.7", label: "opus-4.7 — Claude Opus latest", tier: "frontier" },
|
|
2724
3259
|
{ value: "claude-4.6-opus-high", label: "claude-4.6-opus-high — frontier reasoning", tier: "frontier" },
|
|
2725
3260
|
{ value: "claude-4.6-opus-high-thinking", label: "claude-4.6-opus-high-thinking — reasoning + thinking", tier: "frontier" },
|
|
2726
3261
|
{ value: "claude-4.6-sonnet-high", label: "claude-4.6-sonnet-high — fast + smart", tier: "standard" },
|
|
2727
|
-
{ value: "gpt-5.5-
|
|
3262
|
+
{ value: "gpt-5.5-high-fast", label: "gpt-5.5-high-fast — GPT frontier", tier: "frontier" },
|
|
2728
3263
|
{ value: "composer-2", label: "composer-2 — balanced multi-file", tier: "standard" },
|
|
2729
3264
|
{ value: "composer-2-fast", label: "composer-2-fast — speed optimized", tier: "fast" },
|
|
2730
3265
|
{ value: "gpt-5.3-codex", label: "gpt-5.3-codex — OpenAI codex", tier: "standard" },
|
|
@@ -2735,14 +3270,21 @@ const PROVIDER_MODELS = Object.freeze({
|
|
|
2735
3270
|
],
|
|
2736
3271
|
copilot: [
|
|
2737
3272
|
{ value: "auto", label: "auto — Copilot auto-selects", tier: "standard" },
|
|
2738
|
-
{ value: "
|
|
2739
|
-
{ value: "
|
|
2740
|
-
{ value: "claude-sonnet-4-
|
|
2741
|
-
{ value: "claude-opus-4-5", label: "claude-opus-4-5 — Anthropic
|
|
3273
|
+
{ value: "claude-opus-4-6", label: "claude-opus-4-6 — Anthropic frontier", tier: "frontier" },
|
|
3274
|
+
{ value: "claude-sonnet-4-6", label: "claude-sonnet-4-6 — Anthropic balanced", tier: "standard" },
|
|
3275
|
+
{ value: "claude-sonnet-4-5", label: "claude-sonnet-4-5 — Anthropic previous gen", tier: "standard" },
|
|
3276
|
+
{ value: "claude-opus-4-5", label: "claude-opus-4-5 — Anthropic legacy opus", tier: "standard" },
|
|
3277
|
+
{ value: "gpt-5.3-codex", label: "gpt-5.3-codex — OpenAI coding", tier: "standard" },
|
|
3278
|
+
{ value: "gpt-5.2-codex", label: "gpt-5.2-codex — OpenAI API key auth", tier: "standard" },
|
|
3279
|
+
{ value: "gpt-5.1-codex-max", label: "gpt-5.1-codex-max — extended compute", tier: "frontier" },
|
|
3280
|
+
{ value: "gemini-3-pro", label: "gemini-3-pro — Google frontier", tier: "frontier" },
|
|
3281
|
+
{ value: "gpt-5-mini", label: "gpt-5-mini — fast included", tier: "fast" },
|
|
3282
|
+
{ value: "gpt-4.1", label: "gpt-4.1 — included with subscription", tier: "budget" },
|
|
3283
|
+
{ value: "claude-haiku-4-5", label: "claude-haiku-4-5 — fastest", tier: "fast" },
|
|
2742
3284
|
],
|
|
2743
3285
|
});
|
|
2744
3286
|
const MODEL_ALIASES = {
|
|
2745
|
-
opus: "claude-opus-4-
|
|
3287
|
+
opus: "claude-opus-4-7",
|
|
2746
3288
|
sonnet: "claude-sonnet-4-6",
|
|
2747
3289
|
haiku: "claude-haiku-4-5",
|
|
2748
3290
|
codex: "gpt-5.3-codex",
|
|
@@ -3106,6 +3648,8 @@ Options:
|
|
|
3106
3648
|
--quiet, -q Suppress live tool/message logs (keep iteration headers).
|
|
3107
3649
|
--list-models List available models for the selected provider.
|
|
3108
3650
|
--non-interactive Skip all interactive prompts (same as CI mode).
|
|
3651
|
+
--force-unlock Force-release a stale sweep lock file.
|
|
3652
|
+
--allow-destructive-git Allow agents to run git stash/reset/checkout.
|
|
3109
3653
|
--json Print machine-readable JSON output.
|
|
3110
3654
|
--cwd <path> Project directory.
|
|
3111
3655
|
--help Show this command help.
|
|
@@ -3115,6 +3659,7 @@ Environment:
|
|
|
3115
3659
|
PROMPTS_GPT_MODEL Global model override.
|
|
3116
3660
|
PROMPTS_GPT_NON_INTERACTIVE=1 Skip interactive prompts.
|
|
3117
3661
|
NO_COLOR=1 Disable colored output.
|
|
3662
|
+
PROMPTS_GPT_DEBUG=1 Enable debug logging.
|
|
3118
3663
|
|
|
3119
3664
|
Model aliases: ${Object.entries(MODEL_ALIASES).map(([k, v]) => `${k} → ${v}`).join(", ")}
|
|
3120
3665
|
|
|
@@ -3306,13 +3851,56 @@ function supportsColor() {
|
|
|
3306
3851
|
function colorize(text, code) {
|
|
3307
3852
|
return supportsColor() ? `${code}${text}\x1b[0m` : text;
|
|
3308
3853
|
}
|
|
3854
|
+
function supportsUnicode() {
|
|
3855
|
+
if (process.platform === "win32") {
|
|
3856
|
+
return Boolean(process.env.WT_SESSION || process.env.TERM_PROGRAM);
|
|
3857
|
+
}
|
|
3858
|
+
return true;
|
|
3859
|
+
}
|
|
3860
|
+
function sym(unicode, ascii) {
|
|
3861
|
+
return supportsUnicode() ? unicode : ascii;
|
|
3862
|
+
}
|
|
3863
|
+
function buildProgressBar(completed, total, width) {
|
|
3864
|
+
const safeTotal = Math.max(total, 1);
|
|
3865
|
+
const safeCompleted = Math.max(0, Math.min(completed, safeTotal));
|
|
3866
|
+
const fraction = safeCompleted / safeTotal;
|
|
3867
|
+
const filled = Math.round(fraction * width);
|
|
3868
|
+
const empty = width - filled;
|
|
3869
|
+
const pct = Math.round(fraction * 100);
|
|
3870
|
+
if (supportsColor()) {
|
|
3871
|
+
const bar = "\u2588".repeat(filled) + "\u2591".repeat(empty);
|
|
3872
|
+
return `\x1b[32m${bar}\x1b[0m ${pct}% (${safeCompleted}/${safeTotal})`;
|
|
3873
|
+
}
|
|
3874
|
+
const bar = "#".repeat(filled) + "-".repeat(empty);
|
|
3875
|
+
return `[${bar}] ${pct}% (${safeCompleted}/${safeTotal})`;
|
|
3876
|
+
}
|
|
3877
|
+
// B2: Simple spinner for long-running operations
|
|
3878
|
+
function createSpinner(label) {
|
|
3879
|
+
if (!process.stdout.isTTY || process.env.NO_COLOR || process.env.CI) {
|
|
3880
|
+
process.stdout.write(`${label}...\n`);
|
|
3881
|
+
return { stop: (text) => { if (text)
|
|
3882
|
+
console.log(text); } };
|
|
3883
|
+
}
|
|
3884
|
+
const frames = supportsUnicode() ? ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"] : ["-", "\\", "|", "/"];
|
|
3885
|
+
let i = 0;
|
|
3886
|
+
const timer = setInterval(() => {
|
|
3887
|
+
process.stdout.write(`\r${frames[i++ % frames.length]} ${label}`);
|
|
3888
|
+
}, 80);
|
|
3889
|
+
return {
|
|
3890
|
+
stop: (finalText) => {
|
|
3891
|
+
clearInterval(timer);
|
|
3892
|
+
process.stdout.write(`\r${" ".repeat(label.length + 4)}\r`);
|
|
3893
|
+
if (finalText)
|
|
3894
|
+
console.log(finalText);
|
|
3895
|
+
},
|
|
3896
|
+
};
|
|
3897
|
+
}
|
|
3309
3898
|
const viewFileCmd = process.platform === "win32" ? "type" : "cat";
|
|
3310
3899
|
const listDirCmd = process.platform === "win32" ? "dir" : "ls";
|
|
3311
3900
|
function resolveCliEntry() {
|
|
3312
3901
|
if (process.argv[1])
|
|
3313
3902
|
return process.argv[1];
|
|
3314
3903
|
try {
|
|
3315
|
-
const { fileURLToPath } = require("node:url");
|
|
3316
3904
|
return fileURLToPath(import.meta.url);
|
|
3317
3905
|
}
|
|
3318
3906
|
catch {
|
|
@@ -3355,7 +3943,8 @@ function interactiveSelect(prompt, options) {
|
|
|
3355
3943
|
let cursor = 0;
|
|
3356
3944
|
let escBuf = "";
|
|
3357
3945
|
let filterText = "";
|
|
3358
|
-
const
|
|
3946
|
+
const rawRows = typeof process.stdout.rows === "number" ? process.stdout.rows : 0;
|
|
3947
|
+
const termRows = rawRows > 2 ? rawRows : 24;
|
|
3359
3948
|
const maxVisible = Math.min(options.length, Math.max(3, termRows - 4));
|
|
3360
3949
|
const useUnicode = supportsColor();
|
|
3361
3950
|
const pointer = useUnicode ? "❯" : ">";
|
|
@@ -3581,13 +4170,24 @@ async function interactiveInput(prompt, defaultValue) {
|
|
|
3581
4170
|
resolved = true;
|
|
3582
4171
|
resolve(defaultValue || "");
|
|
3583
4172
|
});
|
|
4173
|
+
rl.on("SIGINT", () => {
|
|
4174
|
+
if (resolved)
|
|
4175
|
+
return;
|
|
4176
|
+
resolved = true;
|
|
4177
|
+
rl.close();
|
|
4178
|
+
rl.removeAllListeners();
|
|
4179
|
+
resolve(defaultValue || "");
|
|
4180
|
+
});
|
|
3584
4181
|
});
|
|
3585
4182
|
}
|
|
3586
4183
|
async function checkPromptsGptSiteReachable(apiUrl) {
|
|
3587
4184
|
const target = new URL("/", apiUrl).toString();
|
|
3588
4185
|
const request = (method) => Promise.race([
|
|
3589
4186
|
globalThis.fetch(target, { method }),
|
|
3590
|
-
new Promise((_, rej) =>
|
|
4187
|
+
new Promise((_, rej) => {
|
|
4188
|
+
const timer = setTimeout(() => rej(new Error("timeout")), 5000);
|
|
4189
|
+
timer.unref?.();
|
|
4190
|
+
}),
|
|
3591
4191
|
]);
|
|
3592
4192
|
let response = await request("HEAD");
|
|
3593
4193
|
if (response.status === 405) {
|
|
@@ -3633,7 +4233,7 @@ function buildGenerateInput(flags) {
|
|
|
3633
4233
|
includeWebSearch: Boolean(flags["web-search"]),
|
|
3634
4234
|
};
|
|
3635
4235
|
}
|
|
3636
|
-
function
|
|
4236
|
+
function redactSecrets(value) {
|
|
3637
4237
|
return value
|
|
3638
4238
|
.replace(/pgpt_[a-zA-Z0-9]{4,}/g, "pgpt_***")
|
|
3639
4239
|
.replace(/sk-[a-zA-Z0-9]{20,}/g, "sk-***")
|
|
@@ -3644,6 +4244,9 @@ function sanitizeGenerateInput(value) {
|
|
|
3644
4244
|
.replace(/ANTHROPIC_API_KEY=[^\s]+/gi, "ANTHROPIC_API_KEY=***")
|
|
3645
4245
|
.replace(/OPENAI_API_KEY=[^\s]+/gi, "OPENAI_API_KEY=***");
|
|
3646
4246
|
}
|
|
4247
|
+
function sanitizeGenerateInput(value) {
|
|
4248
|
+
return redactSecrets(value);
|
|
4249
|
+
}
|
|
3647
4250
|
function printDataTransmissionNotice(command, input) {
|
|
3648
4251
|
console.log(`[notice] The --goal text${input.context ? ", --context" : ""}${input.constraints ? ", --constraints" : ""} you provided will be sent to prompts-gpt.com to generate a prompt.`);
|
|
3649
4252
|
console.log("[notice] Do not include PII, secrets, or confidential data in these flags.");
|
|
@@ -3680,9 +4283,9 @@ main().catch((error) => {
|
|
|
3680
4283
|
}
|
|
3681
4284
|
const msg = error instanceof Error ? error.message : String(error);
|
|
3682
4285
|
try {
|
|
3683
|
-
console.error(msg
|
|
4286
|
+
console.error(redactSecrets(msg));
|
|
3684
4287
|
if (process.env.PROMPTS_GPT_DEBUG === "1" && error instanceof Error && error.stack) {
|
|
3685
|
-
console.error(error.stack
|
|
4288
|
+
console.error(redactSecrets(error.stack));
|
|
3686
4289
|
}
|
|
3687
4290
|
}
|
|
3688
4291
|
catch { /* stderr closed or broken pipe */ }
|
|
@@ -3769,4 +4372,42 @@ function formatTokenUsage(usage) {
|
|
|
3769
4372
|
}
|
|
3770
4373
|
return parts.join(" | ");
|
|
3771
4374
|
}
|
|
4375
|
+
function findClosestCommand(input) {
|
|
4376
|
+
const lower = input.toLowerCase();
|
|
4377
|
+
// Exact prefix match
|
|
4378
|
+
const prefixMatch = COMMANDS.find((c) => c.startsWith(lower.slice(0, 3)));
|
|
4379
|
+
if (prefixMatch)
|
|
4380
|
+
return prefixMatch;
|
|
4381
|
+
// Levenshtein-like: find command within edit distance 2
|
|
4382
|
+
let bestMatch = null;
|
|
4383
|
+
let bestDist = 3;
|
|
4384
|
+
for (const cmd of COMMANDS) {
|
|
4385
|
+
const dist = editDistance(lower, cmd);
|
|
4386
|
+
if (dist < bestDist) {
|
|
4387
|
+
bestDist = dist;
|
|
4388
|
+
bestMatch = cmd;
|
|
4389
|
+
}
|
|
4390
|
+
}
|
|
4391
|
+
return bestMatch;
|
|
4392
|
+
}
|
|
4393
|
+
function editDistance(a, b) {
|
|
4394
|
+
if (a.length === 0)
|
|
4395
|
+
return b.length;
|
|
4396
|
+
if (b.length === 0)
|
|
4397
|
+
return a.length;
|
|
4398
|
+
const matrix = [];
|
|
4399
|
+
for (let i = 0; i <= a.length; i++) {
|
|
4400
|
+
matrix[i] = [i];
|
|
4401
|
+
}
|
|
4402
|
+
for (let j = 0; j <= b.length; j++) {
|
|
4403
|
+
matrix[0][j] = j;
|
|
4404
|
+
}
|
|
4405
|
+
for (let i = 1; i <= a.length; i++) {
|
|
4406
|
+
for (let j = 1; j <= b.length; j++) {
|
|
4407
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
4408
|
+
matrix[i][j] = Math.min(matrix[i - 1][j] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j - 1] + cost);
|
|
4409
|
+
}
|
|
4410
|
+
}
|
|
4411
|
+
return matrix[a.length][b.length];
|
|
4412
|
+
}
|
|
3772
4413
|
//# sourceMappingURL=cli.js.map
|