prompts-gpt 0.2.11 → 0.2.13

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
@@ -182,7 +182,7 @@ async function runCommand(command, flags) {
182
182
  const setupPicked = await interactiveSelect("What next?", setupOptions);
183
183
  if (setupPicked === "list") {
184
184
  const { spawnSync: spSync } = await import("node:child_process");
185
- const cliEntry = process.argv[1] || require.resolve("./cli.js");
185
+ const cliEntry = resolveCliEntry();
186
186
  spSync(process.execPath, [cliEntry, "list"], { stdio: "inherit", cwd });
187
187
  }
188
188
  else if (setupPicked !== "done") {
@@ -190,7 +190,7 @@ async function runCommand(command, flags) {
190
190
  const cmd = action === "sweep" ? "sweep" : "run";
191
191
  console.log(`\nRunning: prompts-gpt ${cmd} -f ${file}\n`);
192
192
  const { spawnSync: spSync } = await import("node:child_process");
193
- const cliEntry = process.argv[1] || require.resolve("./cli.js");
193
+ const cliEntry = resolveCliEntry();
194
194
  const setupResult = spSync(process.execPath, [cliEntry, cmd, "-f", file], { stdio: "inherit", cwd });
195
195
  process.exitCode = setupResult.status ?? 1;
196
196
  }
@@ -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) {
@@ -573,7 +570,7 @@ async function runCommand(command, flags) {
573
570
  const cmd = action === "sweep" ? "sweep" : "run";
574
571
  console.log(`\nRunning: prompts-gpt ${cmd} -f ${file}\n`);
575
572
  const { spawnSync: spSync } = await import("node:child_process");
576
- const cliEntry = process.argv[1] || require.resolve("./cli.js");
573
+ const cliEntry = resolveCliEntry();
577
574
  const result = spSync(process.execPath, [cliEntry, cmd, "-f", file], { stdio: "inherit", cwd });
578
575
  process.exitCode = result.status ?? 1;
579
576
  }
@@ -755,23 +752,36 @@ async function runCommand(command, flags) {
755
752
  }
756
753
  const runProviders = await detectProviders(cwd);
757
754
  const runAvailable = runProviders.filter((p) => p.available);
758
- if (!getStringFlag(flags, "agent") && isTTYInteractive(flags) && !Boolean(flags.json) && runAvailable.length > 1) {
759
- const lastRunProvider = await getLastUsedProvider(cwd);
760
- const providerOpts = runAvailable.map((p) => ({
761
- label: `${p.provider} (${p.modelDefault}${p.version ? `, ${p.version}` : ""})${p.provider === lastRunProvider ? " ★" : ""}`,
762
- value: p.provider,
763
- }));
764
- providerOpts.push({ label: "router (auto-select)", value: "router" });
765
- if (lastRunProvider) {
766
- const idx = providerOpts.findIndex((o) => o.value === lastRunProvider);
767
- if (idx > 0) {
768
- const [item] = providerOpts.splice(idx, 1);
769
- providerOpts.unshift(item);
770
- }
755
+ if (!getStringFlag(flags, "agent") && runAvailable.length === 0 && !isTTYInteractive(flags)) {
756
+ throw new CliError("No supported provider CLI found on PATH. Install Codex, Cursor Agent, Claude Code, or Copilot CLI.\nRun `prompts-gpt doctor` for details.", CLI_EXIT_CODES.validation, { helpCommand: "providers" });
757
+ }
758
+ if (!getStringFlag(flags, "agent") && isTTYInteractive(flags) && !Boolean(flags.json)) {
759
+ if (runAvailable.length === 0) {
760
+ throw new CliError("No supported provider CLI found on PATH. Install Codex, Cursor Agent, Claude Code, or Copilot CLI.\nRun `prompts-gpt doctor` for details.", CLI_EXIT_CODES.validation, { helpCommand: "providers" });
761
+ }
762
+ else if (runAvailable.length === 1) {
763
+ const only = runAvailable[0];
764
+ console.log(`Using provider: ${only.provider} (${only.modelDefault}${only.version ? `, ${only.version}` : ""})`);
765
+ flags.agent = only.provider;
771
766
  }
772
- flags.agent = await interactiveSelect("Select a provider:", providerOpts);
773
- if (flags.agent !== "router") {
774
- await saveLastUsedProvider(cwd, flags.agent);
767
+ else {
768
+ const lastRunProvider = await getLastUsedProvider(cwd);
769
+ const providerOpts = runAvailable.map((p) => ({
770
+ label: `${p.provider} (${p.modelDefault}${p.version ? `, ${p.version}` : ""})${p.provider === lastRunProvider ? " ★" : ""}`,
771
+ value: p.provider,
772
+ }));
773
+ providerOpts.push({ label: "router (auto-select)", value: "router" });
774
+ if (lastRunProvider) {
775
+ const idx = providerOpts.findIndex((o) => o.value === lastRunProvider);
776
+ if (idx > 0) {
777
+ const [item] = providerOpts.splice(idx, 1);
778
+ providerOpts.unshift(item);
779
+ }
780
+ }
781
+ flags.agent = await interactiveSelect("Select a provider:", providerOpts);
782
+ if (flags.agent !== "router") {
783
+ await saveLastUsedProvider(cwd, flags.agent);
784
+ }
775
785
  }
776
786
  }
777
787
  if (!getStringFlag(flags, "model") && isTTYInteractive(flags) && !Boolean(flags.json)) {
@@ -894,13 +904,18 @@ async function runCommand(command, flags) {
894
904
  }
895
905
  catch { /* skip */ }
896
906
  console.log("");
897
- console.log(`View results: cat ${result.summaryFile}`);
907
+ console.log(`View results: ${viewFileCmd} ${result.summaryFile}`);
898
908
  }
899
909
  if (Boolean(flags.open) && result.summaryFile) {
900
910
  try {
901
911
  const { spawn: openSpawn } = await import("node:child_process");
902
- const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "notepad" : "xdg-open";
903
- openSpawn(openCmd, [result.summaryFile], { detached: true, stdio: "ignore" }).unref();
912
+ if (process.platform === "win32") {
913
+ openSpawn("cmd", ["/c", "start", "", result.summaryFile], { detached: true, stdio: "ignore", windowsHide: true }).unref();
914
+ }
915
+ else {
916
+ const openCmd = process.platform === "darwin" ? "open" : "xdg-open";
917
+ openSpawn(openCmd, [result.summaryFile], { detached: true, stdio: "ignore" }).unref();
918
+ }
904
919
  }
905
920
  catch { /* ignore */ }
906
921
  }
@@ -980,6 +995,16 @@ async function runCommand(command, flags) {
980
995
  }
981
996
  return;
982
997
  }
998
+ const providers = await detectProviders(cwd);
999
+ const availableProviders = providers.filter((p) => p.available);
1000
+ if (availableProviders.length === 0) {
1001
+ if (Boolean(flags.json)) {
1002
+ console.log(JSON.stringify({ error: "no_providers", message: "No supported provider CLI was found on PATH." }, null, 2));
1003
+ process.exitCode = CLI_EXIT_CODES.validation;
1004
+ return;
1005
+ }
1006
+ 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" });
1007
+ }
983
1008
  if (!sweepPromptFile && !Boolean(flags.json)) {
984
1009
  const assets = await discoverWorkspaceAssets(cwd);
985
1010
  if (assets.sweeps.length === 1) {
@@ -1024,8 +1049,6 @@ async function runCommand(command, flags) {
1024
1049
  flags.iterations = String(iterFromFm);
1025
1050
  }
1026
1051
  }
1027
- const providers = await detectProviders(cwd);
1028
- const availableProviders = providers.filter((p) => p.available);
1029
1052
  if (!getStringFlag(flags, "agent") && isTTYInteractive(flags) && !Boolean(flags.json)) {
1030
1053
  if (availableProviders.length === 1) {
1031
1054
  const only = availableProviders[0];
@@ -1053,6 +1076,12 @@ async function runCommand(command, flags) {
1053
1076
  }
1054
1077
  }
1055
1078
  }
1079
+ const selectedAgent = resolveRunAgent(flags, config.defaultAgent);
1080
+ if (selectedAgent !== "router" && !providers.find((p) => p.provider === selectedAgent)?.available && !Boolean(flags.json)) {
1081
+ const selectedProvider = providers.find((p) => p.provider === selectedAgent);
1082
+ const available = availableProviders.map((p) => p.provider).join(", ");
1083
+ 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" });
1084
+ }
1056
1085
  if (!getStringFlag(flags, "model") && isTTYInteractive(flags) && !Boolean(flags.json)) {
1057
1086
  const currentAgent = resolveRunAgent(flags, config.defaultAgent);
1058
1087
  if (currentAgent !== "router") {
@@ -1120,7 +1149,8 @@ async function runCommand(command, flags) {
1120
1149
  `${formatDuration(iterTimeout * 1000)} per iteration`, `~${formatDuration(estMaxMs)}`,
1121
1150
  String(maxRetries), tierBadge,
1122
1151
  ];
1123
- const longestVal = Math.max(...boxValues.map((v) => v.length), 30);
1152
+ const stripEmoji = (s) => s.replace(/[\u{1F300}-\u{1F9FF}\u{2600}-\u{27BF}\u{2B50}]/gu, " ");
1153
+ const longestVal = Math.max(...boxValues.map((v) => stripEmoji(v).length), 30);
1124
1154
  const innerW = longestVal + 16;
1125
1155
  const uc = supportsColor();
1126
1156
  const [TL, H, TR, V, ML, MR, BL, BR] = uc ? ["╔", "═", "╗", "║", "╠", "╣", "╚", "╝"] : ["+", "-", "+", "|", "+", "+", "+", "+"];
@@ -1369,13 +1399,18 @@ async function runCommand(command, flags) {
1369
1399
  }
1370
1400
  console.log("");
1371
1401
  console.log("Inspect results:");
1372
- console.log(` cat ${result.manifestFile}`);
1373
- console.log(` ls ${result.runDir}`);
1402
+ console.log(` ${viewFileCmd} ${result.manifestFile}`);
1403
+ console.log(` ${listDirCmd} ${result.runDir}`);
1374
1404
  if (Boolean(flags.open) && result.manifestFile) {
1375
1405
  try {
1376
1406
  const { spawn: openSpawn } = await import("node:child_process");
1377
- const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "notepad" : "xdg-open";
1378
- openSpawn(openCmd, [result.manifestFile], { detached: true, stdio: "ignore" }).unref();
1407
+ if (process.platform === "win32") {
1408
+ openSpawn("cmd", ["/c", "start", "", result.manifestFile], { detached: true, stdio: "ignore", windowsHide: true }).unref();
1409
+ }
1410
+ else {
1411
+ const openCmd = process.platform === "darwin" ? "open" : "xdg-open";
1412
+ openSpawn(openCmd, [result.manifestFile], { detached: true, stdio: "ignore" }).unref();
1413
+ }
1379
1414
  }
1380
1415
  catch { /* ignore */ }
1381
1416
  }
@@ -1421,11 +1456,7 @@ async function runCommand(command, flags) {
1421
1456
  if (!assets.credentialsFound) {
1422
1457
  let networkOk = true;
1423
1458
  try {
1424
- const netCheck = await Promise.race([
1425
- fetch("https://prompts-gpt.com/api/health", { method: "HEAD" }),
1426
- new Promise((_, rej) => setTimeout(() => rej(new Error("timeout")), 5000)),
1427
- ]);
1428
- networkOk = netCheck instanceof Response;
1459
+ networkOk = (await checkPromptsGptSiteReachable(DEFAULT_PROMPTS_GPT_API_URL)).ok;
1429
1460
  }
1430
1461
  catch {
1431
1462
  networkOk = false;
@@ -1540,7 +1571,7 @@ async function runCommand(command, flags) {
1540
1571
  const cmd = action === "sweep" ? "sweep" : "run";
1541
1572
  console.log(`\nRunning: prompts-gpt ${cmd} -f ${file}\n`);
1542
1573
  const { spawnSync: spSync } = await import("node:child_process");
1543
- const cliEntry = process.argv[1] || require.resolve("./cli.js");
1574
+ const cliEntry = resolveCliEntry();
1544
1575
  const result = spSync(process.execPath, [cliEntry, cmd, "-f", file], { stdio: "inherit", cwd });
1545
1576
  process.exitCode = result.status ?? 1;
1546
1577
  return;
@@ -1678,7 +1709,7 @@ async function runCommand(command, flags) {
1678
1709
  const runQs = await interactiveInput("Run quickstart now? [Y/n]", "Y");
1679
1710
  if (runQs.toLowerCase() !== "n" && runQs.toLowerCase() !== "no") {
1680
1711
  const { spawnSync: spSync } = await import("node:child_process");
1681
- const cliEntry = process.argv[1] || require.resolve("./cli.js");
1712
+ const cliEntry = resolveCliEntry();
1682
1713
  const qsResult = spSync(process.execPath, [cliEntry, "quickstart", "--cwd", cwd], { stdio: "inherit", cwd });
1683
1714
  process.exitCode = qsResult.status ?? 1;
1684
1715
  return;
@@ -1786,7 +1817,7 @@ async function runCommand(command, flags) {
1786
1817
  if (runNow.toLowerCase() !== "n" && runNow.toLowerCase() !== "no") {
1787
1818
  console.log(`\nRunning: prompts-gpt run -f ${genFile}\n`);
1788
1819
  const { spawnSync: spSync } = await import("node:child_process");
1789
- const cliEntry = process.argv[1] || require.resolve("./cli.js");
1820
+ const cliEntry = resolveCliEntry();
1790
1821
  const genResult = spSync(process.execPath, [cliEntry, "run", "-f", genFile], { stdio: "inherit", cwd });
1791
1822
  process.exitCode = genResult.status ?? 1;
1792
1823
  }
@@ -1909,7 +1940,7 @@ async function runCommand(command, flags) {
1909
1940
  if (syncNow.toLowerCase() !== "n" && syncNow.toLowerCase() !== "no") {
1910
1941
  console.log("\nSyncing...");
1911
1942
  const { spawnSync: spSync } = await import("node:child_process");
1912
- const cliEntry = process.argv[1] || require.resolve("./cli.js");
1943
+ const cliEntry = resolveCliEntry();
1913
1944
  const projCwd = getResolvedCwd(flags);
1914
1945
  const syncResult = spSync(process.execPath, [cliEntry, "sync", "--cwd", projCwd], { stdio: "inherit", cwd: projCwd });
1915
1946
  process.exitCode = syncResult.status ?? 1;
@@ -2654,7 +2685,7 @@ async function getLastUsedModel(cwd, provider) {
2654
2685
  }
2655
2686
  async function saveLastUsedModel(cwd, provider, model) {
2656
2687
  try {
2657
- const { readFile: fsRead, writeFile: fsWrite, mkdir: fsMkdir, rename } = await import("node:fs/promises");
2688
+ const { readFile: fsRead, writeFile: fsWrite, mkdir: fsMkdir } = await import("node:fs/promises");
2658
2689
  const dir = path.resolve(cwd, DEFAULT_PROMPTS_GPT_OUT_DIR);
2659
2690
  await fsMkdir(dir, { recursive: true });
2660
2691
  const filePath = path.resolve(dir, ".last-models");
@@ -2664,9 +2695,7 @@ async function saveLastUsedModel(cwd, provider, model) {
2664
2695
  }
2665
2696
  catch { /* new file */ }
2666
2697
  existing[provider] = model;
2667
- const tmpPath = `${filePath}.tmp.${process.pid}`;
2668
- await fsWrite(tmpPath, JSON.stringify(existing, null, 2));
2669
- await rename(tmpPath, filePath);
2698
+ await fsWrite(filePath, JSON.stringify(existing, null, 2));
2670
2699
  }
2671
2700
  catch { /* ignore */ }
2672
2701
  }
@@ -2686,9 +2715,8 @@ const PROVIDER_MODELS = Object.freeze({
2686
2715
  { value: "gpt-4.1", label: "gpt-4.1 — legacy", tier: "budget" },
2687
2716
  ],
2688
2717
  claude: [
2689
- { value: "claude-opus-4-7", label: "claude-opus-4-7latest, most capable", tier: "frontier" },
2690
- { value: "claude-opus-4-6", label: "claude-opus-4-6 — previous gen opus", tier: "frontier" },
2691
- { value: "claude-sonnet-4-6", label: "claude-sonnet-4-6 — speed + intelligence", tier: "standard" },
2718
+ { value: "claude-sonnet-4-6", label: "claude-sonnet-4-6speed + intelligence ★", tier: "standard" },
2719
+ { value: "claude-opus-4-5", label: "claude-opus-4-5 — previous gen opus", tier: "frontier" },
2692
2720
  { value: "claude-haiku-4-5", label: "claude-haiku-4-5 — fastest near-frontier", tier: "fast" },
2693
2721
  ],
2694
2722
  cursor: [
@@ -2702,6 +2730,7 @@ const PROVIDER_MODELS = Object.freeze({
2702
2730
  { value: "gpt-5.3-codex", label: "gpt-5.3-codex — OpenAI codex", tier: "standard" },
2703
2731
  { value: "gpt-5.4-mini", label: "gpt-5.4-mini — fast & affordable", tier: "fast" },
2704
2732
  { value: "gemini-3.1-pro", label: "gemini-3.1-pro — Google frontier", tier: "standard" },
2733
+ { value: "grok-4.3", label: "grok-4.3 — xAI frontier", tier: "frontier" },
2705
2734
  { value: "claude-4.5-opus", label: "claude-4.5-opus — previous gen opus", tier: "standard" },
2706
2735
  ],
2707
2736
  copilot: [
@@ -2709,11 +2738,11 @@ const PROVIDER_MODELS = Object.freeze({
2709
2738
  { value: "gpt-5.5", label: "gpt-5.5 — frontier", tier: "frontier" },
2710
2739
  { value: "gpt-5.4-mini", label: "gpt-5.4-mini — fast", tier: "fast" },
2711
2740
  { value: "claude-sonnet-4-6", label: "claude-sonnet-4-6 — Anthropic", tier: "standard" },
2712
- { value: "claude-opus-4-7", label: "claude-opus-4-7 — Anthropic frontier", tier: "frontier" },
2741
+ { value: "claude-opus-4-5", label: "claude-opus-4-5 — Anthropic frontier", tier: "frontier" },
2713
2742
  ],
2714
2743
  });
2715
2744
  const MODEL_ALIASES = {
2716
- opus: "claude-opus-4-7",
2745
+ opus: "claude-opus-4-5",
2717
2746
  sonnet: "claude-sonnet-4-6",
2718
2747
  haiku: "claude-haiku-4-5",
2719
2748
  codex: "gpt-5.3-codex",
@@ -2723,6 +2752,7 @@ const MODEL_ALIASES = {
2723
2752
  composer: "composer-2",
2724
2753
  fast: "composer-2-fast",
2725
2754
  gemini: "gemini-3.1-pro",
2755
+ grok: "grok-4.3",
2726
2756
  o3: "o3",
2727
2757
  pro: "gpt-5.5-pro",
2728
2758
  };
@@ -3276,6 +3306,19 @@ function supportsColor() {
3276
3306
  function colorize(text, code) {
3277
3307
  return supportsColor() ? `${code}${text}\x1b[0m` : text;
3278
3308
  }
3309
+ const viewFileCmd = process.platform === "win32" ? "type" : "cat";
3310
+ const listDirCmd = process.platform === "win32" ? "dir" : "ls";
3311
+ function resolveCliEntry() {
3312
+ if (process.argv[1])
3313
+ return process.argv[1];
3314
+ try {
3315
+ const { fileURLToPath } = require("node:url");
3316
+ return fileURLToPath(import.meta.url);
3317
+ }
3318
+ catch {
3319
+ return "cli.js";
3320
+ }
3321
+ }
3279
3322
  function isTTYInteractive(flags) {
3280
3323
  if (!process.stdin.isTTY || !process.stdout.isTTY)
3281
3324
  return false;
@@ -3288,7 +3331,7 @@ function isTTYInteractive(flags) {
3288
3331
  return false;
3289
3332
  if (process.env.PROMPTS_GPT_NON_INTERACTIVE === "1" || process.env.PROMPTS_GPT_NON_INTERACTIVE === "true")
3290
3333
  return false;
3291
- if (process.env.POWERSHELL_ISE === "1" || process.env.PSModulePath?.includes("ISE"))
3334
+ if (process.env.POWERSHELL_ISE === "1")
3292
3335
  return false;
3293
3336
  return true;
3294
3337
  }
@@ -3296,6 +3339,9 @@ function interactiveSelect(prompt, options) {
3296
3339
  if (options.length === 0) {
3297
3340
  return Promise.reject(new CliError("No options available for selection.", CLI_EXIT_CODES.usage));
3298
3341
  }
3342
+ if (!Array.isArray(options) || options.some((o) => typeof o.label !== "string" || typeof o.value !== "string")) {
3343
+ return Promise.reject(new CliError("Invalid options for selection.", CLI_EXIT_CODES.usage));
3344
+ }
3299
3345
  if (options.length === 1) {
3300
3346
  return Promise.resolve(options[0].value);
3301
3347
  }
@@ -3309,7 +3355,7 @@ function interactiveSelect(prompt, options) {
3309
3355
  let cursor = 0;
3310
3356
  let escBuf = "";
3311
3357
  let filterText = "";
3312
- const termRows = typeof process.stdout.rows === "number" && process.stdout.rows > 0 ? process.stdout.rows : 24;
3358
+ const termRows = typeof process.stdout.rows === "number" && process.stdout.rows > 2 ? process.stdout.rows : 24;
3313
3359
  const maxVisible = Math.min(options.length, Math.max(3, termRows - 4));
3314
3360
  const useUnicode = supportsColor();
3315
3361
  const pointer = useUnicode ? "❯" : ">";
@@ -3333,7 +3379,7 @@ function interactiveSelect(prompt, options) {
3333
3379
  }
3334
3380
  stdout.write(`${hints}\n`);
3335
3381
  };
3336
- const supportsAnsiCursor = supportsColor() || (process.platform === "win32" && Boolean(process.env.WT_SESSION));
3382
+ const supportsAnsiCursor = supportsColor() || (process.platform === "win32" && Boolean(process.env.WT_SESSION || process.env.TERM_PROGRAM || process.env.ConEmuANSI));
3337
3383
  const render = () => {
3338
3384
  const visibleCount = Math.min(maxVisible, options.length);
3339
3385
  const footerLines = 1 + (options.length > maxVisible ? 1 : 0) + 1;
@@ -3342,7 +3388,8 @@ function interactiveSelect(prompt, options) {
3342
3388
  stdout.write(`\x1b[${lines}A\x1b[J`);
3343
3389
  }
3344
3390
  else {
3345
- stdout.write("\n".repeat(2));
3391
+ for (let i = 0; i < lines; i++)
3392
+ stdout.write("\x1b[A\x1b[2K");
3346
3393
  }
3347
3394
  const posLabel = options.length > 1 ? ` (${cursor + 1}/${options.length})` : "";
3348
3395
  const filterLabel = filterText ? colorize(` filter: "${filterText}"`, "\x1b[33m") : "";
@@ -3456,8 +3503,11 @@ function interactiveSelect(prompt, options) {
3456
3503
  }
3457
3504
  if (ch === "\x03") {
3458
3505
  cleanup();
3459
- stdout.write("\n");
3460
- process.exit(130);
3506
+ try {
3507
+ stdout.write("\n");
3508
+ }
3509
+ catch { /* stdout may be closed */ }
3510
+ reject(new CliError("Selection cancelled.", CLI_EXIT_CODES.general));
3461
3511
  return;
3462
3512
  }
3463
3513
  if (ch === "\r" || ch === "\n") {
@@ -3514,16 +3564,37 @@ async function interactiveInput(prompt, defaultValue) {
3514
3564
  }
3515
3565
  const { createInterface } = await import("node:readline");
3516
3566
  return new Promise((resolve) => {
3567
+ let resolved = false;
3517
3568
  const rl = createInterface({ input: process.stdin, output: process.stdout });
3518
3569
  const suffix = defaultValue ? ` (${defaultValue})` : "";
3519
3570
  rl.question(`${prompt}${suffix}: `, (answer) => {
3571
+ if (resolved)
3572
+ return;
3573
+ resolved = true;
3520
3574
  rl.close();
3521
3575
  rl.removeAllListeners();
3522
3576
  resolve((answer.trim() || defaultValue || "").slice(0, 1024));
3523
3577
  });
3524
- rl.on("close", () => resolve(defaultValue || ""));
3578
+ rl.on("close", () => {
3579
+ if (resolved)
3580
+ return;
3581
+ resolved = true;
3582
+ resolve(defaultValue || "");
3583
+ });
3525
3584
  });
3526
3585
  }
3586
+ async function checkPromptsGptSiteReachable(apiUrl) {
3587
+ const target = new URL("/", apiUrl).toString();
3588
+ const request = (method) => Promise.race([
3589
+ globalThis.fetch(target, { method }),
3590
+ new Promise((_, rej) => setTimeout(() => rej(new Error("timeout")), 5000)),
3591
+ ]);
3592
+ let response = await request("HEAD");
3593
+ if (response.status === 405) {
3594
+ response = await request("GET");
3595
+ }
3596
+ return { ok: response.ok, status: response.status };
3597
+ }
3527
3598
  async function readSweepFrontmatter(filePath) {
3528
3599
  try {
3529
3600
  const { readFile: fsRead } = await import("node:fs/promises");
@@ -3581,10 +3652,13 @@ function printDataTransmissionNotice(command, input) {
3581
3652
  }
3582
3653
  main().catch((error) => {
3583
3654
  if (error instanceof CliError) {
3584
- console.error(error.message);
3585
- if (error.helpCommand) {
3586
- console.error(`Run \`prompts-gpt help${error.helpCommand ? ` ${error.helpCommand}` : ""}\` for usage.`);
3655
+ try {
3656
+ console.error(error.message);
3657
+ if (error.helpCommand) {
3658
+ console.error(`Run \`prompts-gpt help${error.helpCommand ? ` ${error.helpCommand}` : ""}\` for usage.`);
3659
+ }
3587
3660
  }
3661
+ catch { /* stderr closed or broken pipe */ }
3588
3662
  process.exitCode = error.exitCode;
3589
3663
  return;
3590
3664
  }
@@ -3645,7 +3719,7 @@ async function extractRunDiagnostics(logFile, provider, model) {
3645
3719
  }
3646
3720
  if (diagnostics.length === 0) {
3647
3721
  diagnostics.push(`${provider} exited with code 1. Check the log for details:`);
3648
- diagnostics.push(` cat ${logFile}`);
3722
+ diagnostics.push(` ${viewFileCmd} ${logFile}`);
3649
3723
  diagnostics.push(`Try a different provider: prompts-gpt run --agent claude`);
3650
3724
  }
3651
3725
  }