prompts-gpt 0.2.10 → 0.2.12

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 CHANGED
@@ -450,15 +450,12 @@ async function runCommand(command, flags) {
450
450
  }
451
451
  else {
452
452
  try {
453
- const healthRes = await Promise.race([
454
- globalThis.fetch(`${DEFAULT_PROMPTS_GPT_API_URL}/api/health`, { method: "HEAD" }),
455
- new Promise((_, rej) => setTimeout(() => rej(new Error("timeout")), 5000)),
456
- ]);
457
- if (healthRes instanceof Response && healthRes.ok) {
453
+ const siteCheck = await checkPromptsGptSiteReachable(DEFAULT_PROMPTS_GPT_API_URL);
454
+ if (siteCheck.ok) {
458
455
  console.log(` ✓ prompts-gpt.com reachable`);
459
456
  }
460
457
  else {
461
- console.log(` ⚠ prompts-gpt.com responded with status ${healthRes.status}`);
458
+ console.log(` ⚠ prompts-gpt.com responded with status ${siteCheck.status ?? "unknown"}`);
462
459
  }
463
460
  }
464
461
  catch (err) {
@@ -980,6 +977,11 @@ async function runCommand(command, flags) {
980
977
  }
981
978
  return;
982
979
  }
980
+ const providers = await detectProviders(cwd);
981
+ const availableProviders = providers.filter((p) => p.available);
982
+ if (availableProviders.length === 0 && !Boolean(flags.json)) {
983
+ throw new CliError("No supported provider CLI was found on PATH. Install Codex, Cursor Agent, Claude Code, or Copilot CLI, then run `prompts-gpt doctor`.", CLI_EXIT_CODES.validation, { helpCommand: "providers" });
984
+ }
983
985
  if (!sweepPromptFile && !Boolean(flags.json)) {
984
986
  const assets = await discoverWorkspaceAssets(cwd);
985
987
  if (assets.sweeps.length === 1) {
@@ -987,7 +989,7 @@ async function runCommand(command, flags) {
987
989
  const iterFromFm = await readSweepIterationsFromFrontmatter(path.resolve(cwd, autoFile));
988
990
  console.log(`Auto-selected sweep: ${path.basename(autoFile)}${iterFromFm ? ` (${iterFromFm} iterations from frontmatter)` : ""}`);
989
991
  flags["prompt-file"] = autoFile;
990
- if (iterFromFm && !getStringFlag(flags, "iterations")) {
992
+ if (iterFromFm && !getStringFlag(flags, "iterations") && !isTTYInteractive(flags)) {
991
993
  flags.iterations = String(iterFromFm);
992
994
  }
993
995
  }
@@ -995,16 +997,11 @@ async function runCommand(command, flags) {
995
997
  if (isTTYInteractive(flags)) {
996
998
  const sweepOptions = await Promise.all(assets.sweeps.map(async (s) => {
997
999
  const fm = await readSweepFrontmatter(path.resolve(cwd, s.file));
998
- const iterLabel = fm.iterations ? ` (${fm.iterations} iterations)` : "";
999
1000
  const titleLabel = fm.title ? ` — ${fm.title}` : "";
1000
- return { label: `${path.basename(s.file, ".md")}${titleLabel}${iterLabel}`, value: s.file };
1001
+ return { label: `${path.basename(s.file, ".md")}${titleLabel}`, value: s.file };
1001
1002
  }));
1002
1003
  const picked = await interactiveSelect("Select a sweep file:", sweepOptions);
1003
1004
  flags["prompt-file"] = picked;
1004
- const iterFromFm = await readSweepIterationsFromFrontmatter(path.resolve(cwd, picked));
1005
- if (iterFromFm && !getStringFlag(flags, "iterations")) {
1006
- flags.iterations = String(iterFromFm);
1007
- }
1008
1005
  }
1009
1006
  else {
1010
1007
  console.log(`${assets.sweeps.length} sweep files found. Pick one with --prompt-file:\n`);
@@ -1023,14 +1020,12 @@ async function runCommand(command, flags) {
1023
1020
  return;
1024
1021
  }
1025
1022
  }
1026
- if (!getStringFlag(flags, "iterations") && getStringFlag(flags, "prompt-file")) {
1023
+ if (!getStringFlag(flags, "iterations") && getStringFlag(flags, "prompt-file") && !isTTYInteractive(flags)) {
1027
1024
  const iterFromFm = await readSweepIterationsFromFrontmatter(path.resolve(cwd, getStringFlag(flags, "prompt-file")));
1028
1025
  if (iterFromFm) {
1029
1026
  flags.iterations = String(iterFromFm);
1030
1027
  }
1031
1028
  }
1032
- const providers = await detectProviders(cwd);
1033
- const availableProviders = providers.filter((p) => p.available);
1034
1029
  if (!getStringFlag(flags, "agent") && isTTYInteractive(flags) && !Boolean(flags.json)) {
1035
1030
  if (availableProviders.length === 1) {
1036
1031
  const only = availableProviders[0];
@@ -1058,6 +1053,12 @@ async function runCommand(command, flags) {
1058
1053
  }
1059
1054
  }
1060
1055
  }
1056
+ const selectedAgent = resolveRunAgent(flags, config.defaultAgent);
1057
+ if (selectedAgent !== "router" && !providers.find((p) => p.provider === selectedAgent)?.available && !Boolean(flags.json)) {
1058
+ const selectedProvider = providers.find((p) => p.provider === selectedAgent);
1059
+ const available = availableProviders.map((p) => p.provider).join(", ");
1060
+ throw new CliError(`Provider ${selectedAgent} is not available. ${selectedProvider?.installHint ?? "Install the provider CLI and ensure it is on PATH."}${available ? ` Available providers: ${available}.` : ""}`, CLI_EXIT_CODES.validation, { helpCommand: "providers" });
1061
+ }
1061
1062
  if (!getStringFlag(flags, "model") && isTTYInteractive(flags) && !Boolean(flags.json)) {
1062
1063
  const currentAgent = resolveRunAgent(flags, config.defaultAgent);
1063
1064
  if (currentAgent !== "router") {
@@ -1088,14 +1089,16 @@ async function runCommand(command, flags) {
1088
1089
  }
1089
1090
  if (!getStringFlag(flags, "iterations") && isTTYInteractive(flags) && !Boolean(flags.json) && getStringFlag(flags, "prompt-file")) {
1090
1091
  const fmIter = await readSweepIterationsFromFrontmatter(path.resolve(cwd, getStringFlag(flags, "prompt-file")));
1091
- const defaultIter = fmIter ? String(fmIter) : "1";
1092
+ const defaultIter = "1";
1092
1093
  const iterOptions = [
1093
1094
  { label: `${defaultIter} (default)`, value: defaultIter },
1094
- ...(defaultIter !== "1" ? [{ label: "1", value: "1" }] : []),
1095
- ...(defaultIter !== "2" ? [{ label: "2", value: "2" }] : []),
1096
- ...(defaultIter !== "3" ? [{ label: "3", value: "3" }] : []),
1097
- ...(defaultIter !== "5" ? [{ label: "5", value: "5" }] : []),
1095
+ { label: "2", value: "2" },
1096
+ { label: "3", value: "3" },
1097
+ { label: "5", value: "5" },
1098
1098
  ];
1099
+ if (fmIter && !iterOptions.some((option) => option.value === String(fmIter))) {
1100
+ iterOptions.push({ label: String(fmIter), value: String(fmIter) });
1101
+ }
1099
1102
  flags.iterations = await interactiveSelect("Select iterations:", iterOptions);
1100
1103
  }
1101
1104
  const agent = resolveRunAgent(flags, config.defaultAgent);
@@ -1424,11 +1427,7 @@ async function runCommand(command, flags) {
1424
1427
  if (!assets.credentialsFound) {
1425
1428
  let networkOk = true;
1426
1429
  try {
1427
- const netCheck = await Promise.race([
1428
- fetch("https://prompts-gpt.com/api/health", { method: "HEAD" }),
1429
- new Promise((_, rej) => setTimeout(() => rej(new Error("timeout")), 5000)),
1430
- ]);
1431
- networkOk = netCheck instanceof Response;
1430
+ networkOk = (await checkPromptsGptSiteReachable(DEFAULT_PROMPTS_GPT_API_URL)).ok;
1432
1431
  }
1433
1432
  catch {
1434
1433
  networkOk = false;
@@ -2689,9 +2688,8 @@ const PROVIDER_MODELS = Object.freeze({
2689
2688
  { value: "gpt-4.1", label: "gpt-4.1 — legacy", tier: "budget" },
2690
2689
  ],
2691
2690
  claude: [
2692
- { value: "claude-opus-4-7", label: "claude-opus-4-7latest, most capable", tier: "frontier" },
2693
- { value: "claude-opus-4-6", label: "claude-opus-4-6 — previous gen opus", tier: "frontier" },
2694
- { value: "claude-sonnet-4-6", label: "claude-sonnet-4-6 — speed + intelligence", tier: "standard" },
2691
+ { value: "claude-sonnet-4-6", label: "claude-sonnet-4-6speed + intelligence ★", tier: "standard" },
2692
+ { value: "claude-opus-4-5", label: "claude-opus-4-5 — previous gen opus", tier: "frontier" },
2695
2693
  { value: "claude-haiku-4-5", label: "claude-haiku-4-5 — fastest near-frontier", tier: "fast" },
2696
2694
  ],
2697
2695
  cursor: [
@@ -2705,6 +2703,7 @@ const PROVIDER_MODELS = Object.freeze({
2705
2703
  { value: "gpt-5.3-codex", label: "gpt-5.3-codex — OpenAI codex", tier: "standard" },
2706
2704
  { value: "gpt-5.4-mini", label: "gpt-5.4-mini — fast & affordable", tier: "fast" },
2707
2705
  { value: "gemini-3.1-pro", label: "gemini-3.1-pro — Google frontier", tier: "standard" },
2706
+ { value: "grok-4.3", label: "grok-4.3 — xAI frontier", tier: "frontier" },
2708
2707
  { value: "claude-4.5-opus", label: "claude-4.5-opus — previous gen opus", tier: "standard" },
2709
2708
  ],
2710
2709
  copilot: [
@@ -2712,11 +2711,11 @@ const PROVIDER_MODELS = Object.freeze({
2712
2711
  { value: "gpt-5.5", label: "gpt-5.5 — frontier", tier: "frontier" },
2713
2712
  { value: "gpt-5.4-mini", label: "gpt-5.4-mini — fast", tier: "fast" },
2714
2713
  { value: "claude-sonnet-4-6", label: "claude-sonnet-4-6 — Anthropic", tier: "standard" },
2715
- { value: "claude-opus-4-7", label: "claude-opus-4-7 — Anthropic frontier", tier: "frontier" },
2714
+ { value: "claude-opus-4-5", label: "claude-opus-4-5 — Anthropic frontier", tier: "frontier" },
2716
2715
  ],
2717
2716
  });
2718
2717
  const MODEL_ALIASES = {
2719
- opus: "claude-opus-4-7",
2718
+ opus: "claude-opus-4-5",
2720
2719
  sonnet: "claude-sonnet-4-6",
2721
2720
  haiku: "claude-haiku-4-5",
2722
2721
  codex: "gpt-5.3-codex",
@@ -2726,6 +2725,7 @@ const MODEL_ALIASES = {
2726
2725
  composer: "composer-2",
2727
2726
  fast: "composer-2-fast",
2728
2727
  gemini: "gemini-3.1-pro",
2728
+ grok: "grok-4.3",
2729
2729
  o3: "o3",
2730
2730
  pro: "gpt-5.5-pro",
2731
2731
  };
@@ -3056,13 +3056,12 @@ Why use it:
3056
3056
  Runs the same prompt N times, feeding each iteration's summary into the next.
3057
3057
  Includes pre-flight checks, safety guards, SIGTERM handling, and progress monitoring.
3058
3058
 
3059
- When --prompt-file is omitted and only one .prompts-gpt/sweeps/*.md file exists,
3060
- it is auto-selected. The iteration count defaults to the \`iterations:\` value
3061
- in the sweep file's YAML frontmatter (or 1 if not specified).
3059
+ Interactive runs always ask for the iteration count. Non-interactive runs use
3060
+ the \`iterations:\` value in the sweep file's YAML frontmatter when present.
3062
3061
 
3063
3062
  Options:
3064
3063
  -f, --prompt-file <path> Prompt file to sweep. Auto-detects local sweeps if omitted.
3065
- -n, --iterations <n> Number of iterations. Default: from frontmatter or 1.
3064
+ -n, --iterations <n> Number of iterations. Interactive default: 1.
3066
3065
  --agent <name> Orchestration profile. Default from config or router.
3067
3066
  --model <name> Model override for the selected provider.
3068
3067
  --iteration-timeout <secs> Timeout per iteration in seconds. Default: 5400 (90 min)
@@ -3300,6 +3299,9 @@ function interactiveSelect(prompt, options) {
3300
3299
  if (options.length === 0) {
3301
3300
  return Promise.reject(new CliError("No options available for selection.", CLI_EXIT_CODES.usage));
3302
3301
  }
3302
+ if (!Array.isArray(options) || options.some((o) => typeof o.label !== "string" || typeof o.value !== "string")) {
3303
+ return Promise.reject(new CliError("Invalid options for selection.", CLI_EXIT_CODES.usage));
3304
+ }
3303
3305
  if (options.length === 1) {
3304
3306
  return Promise.resolve(options[0].value);
3305
3307
  }
@@ -3313,7 +3315,7 @@ function interactiveSelect(prompt, options) {
3313
3315
  let cursor = 0;
3314
3316
  let escBuf = "";
3315
3317
  let filterText = "";
3316
- const termRows = typeof process.stdout.rows === "number" && process.stdout.rows > 0 ? process.stdout.rows : 24;
3318
+ const termRows = typeof process.stdout.rows === "number" && process.stdout.rows > 2 ? process.stdout.rows : 24;
3317
3319
  const maxVisible = Math.min(options.length, Math.max(3, termRows - 4));
3318
3320
  const useUnicode = supportsColor();
3319
3321
  const pointer = useUnicode ? "❯" : ">";
@@ -3460,8 +3462,11 @@ function interactiveSelect(prompt, options) {
3460
3462
  }
3461
3463
  if (ch === "\x03") {
3462
3464
  cleanup();
3463
- stdout.write("\n");
3464
- process.exit(130);
3465
+ try {
3466
+ stdout.write("\n");
3467
+ }
3468
+ catch { /* stdout may be closed */ }
3469
+ reject(new CliError("Selection cancelled.", CLI_EXIT_CODES.general));
3465
3470
  return;
3466
3471
  }
3467
3472
  if (ch === "\r" || ch === "\n") {
@@ -3518,16 +3523,37 @@ async function interactiveInput(prompt, defaultValue) {
3518
3523
  }
3519
3524
  const { createInterface } = await import("node:readline");
3520
3525
  return new Promise((resolve) => {
3526
+ let resolved = false;
3521
3527
  const rl = createInterface({ input: process.stdin, output: process.stdout });
3522
3528
  const suffix = defaultValue ? ` (${defaultValue})` : "";
3523
3529
  rl.question(`${prompt}${suffix}: `, (answer) => {
3530
+ if (resolved)
3531
+ return;
3532
+ resolved = true;
3524
3533
  rl.close();
3525
3534
  rl.removeAllListeners();
3526
3535
  resolve((answer.trim() || defaultValue || "").slice(0, 1024));
3527
3536
  });
3528
- rl.on("close", () => resolve(defaultValue || ""));
3537
+ rl.on("close", () => {
3538
+ if (resolved)
3539
+ return;
3540
+ resolved = true;
3541
+ resolve(defaultValue || "");
3542
+ });
3529
3543
  });
3530
3544
  }
3545
+ async function checkPromptsGptSiteReachable(apiUrl) {
3546
+ const target = new URL("/", apiUrl).toString();
3547
+ const request = (method) => Promise.race([
3548
+ globalThis.fetch(target, { method }),
3549
+ new Promise((_, rej) => setTimeout(() => rej(new Error("timeout")), 5000)),
3550
+ ]);
3551
+ let response = await request("HEAD");
3552
+ if (response.status === 405) {
3553
+ response = await request("GET");
3554
+ }
3555
+ return { ok: response.ok, status: response.status };
3556
+ }
3531
3557
  async function readSweepFrontmatter(filePath) {
3532
3558
  try {
3533
3559
  const { readFile: fsRead } = await import("node:fs/promises");
@@ -3585,10 +3611,13 @@ function printDataTransmissionNotice(command, input) {
3585
3611
  }
3586
3612
  main().catch((error) => {
3587
3613
  if (error instanceof CliError) {
3588
- console.error(error.message);
3589
- if (error.helpCommand) {
3590
- console.error(`Run \`prompts-gpt help${error.helpCommand ? ` ${error.helpCommand}` : ""}\` for usage.`);
3614
+ try {
3615
+ console.error(error.message);
3616
+ if (error.helpCommand) {
3617
+ console.error(`Run \`prompts-gpt help${error.helpCommand ? ` ${error.helpCommand}` : ""}\` for usage.`);
3618
+ }
3591
3619
  }
3620
+ catch { /* stderr closed or broken pipe */ }
3592
3621
  process.exitCode = error.exitCode;
3593
3622
  return;
3594
3623
  }