@prompts-gpt/client 0.2.3 → 0.2.5

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
@@ -2,7 +2,7 @@
2
2
  import { existsSync } from "node:fs";
3
3
  import path from "node:path";
4
4
  import { parseArgs } from "node:util";
5
- import { 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, sweepPrompt, validateRunConfig, discoverWorkspaceAssets, SUPPORTED_AGENT_TARGETS, detectProviders, loadLocalCredentials, saveLocalCredentials, syncPrompts, writeAgentFiles, writePromptManifest, writePromptMarkdownFiles, } from "./index.js";
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, } from "./index.js";
6
6
  const CLI_EXIT_CODES = {
7
7
  success: 0,
8
8
  general: 1,
@@ -12,7 +12,7 @@ const CLI_EXIT_CODES = {
12
12
  usage: 64,
13
13
  };
14
14
  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", "doctor", "load-config", "version", "help"];
15
+ const COMMANDS = ["setup", "init", "project", "pull", "generate", "sync", "run", "run-batch", "sweep", "list", "status", "validate", "providers", "doctor", "load-config", "quickstart", "version", "help"];
16
16
  const MAX_STDIN_TOKEN_LENGTH = 4_096;
17
17
  class CliError extends Error {
18
18
  exitCode;
@@ -27,12 +27,15 @@ class CliError extends Error {
27
27
  async function main() {
28
28
  const argv = process.argv.slice(2);
29
29
  if (argv.length === 0) {
30
- printHelp();
31
- console.log("Quick start:");
32
- console.log(" prompts-gpt status — check workspace readiness");
33
- console.log(" prompts-gpt list see available prompts, sweeps, agents");
34
- console.log(" prompts-gpt help <cmd> detailed help for a command");
35
- console.log("");
30
+ console.log(`Prompts-GPT CLI — sync and run AI prompt packs locally
31
+
32
+ Get started:
33
+ prompts-gpt quickstart setup credentials, config, and first run
34
+ prompts-gpt list see available prompts and sweeps
35
+ prompts-gpt sweep — run a multi-iteration sweep (auto-detects local sweeps)
36
+
37
+ Run \`prompts-gpt help\` for the full command list.
38
+ `);
36
39
  return;
37
40
  }
38
41
  const first = argv[0];
@@ -83,7 +86,11 @@ async function runCommand(command, flags) {
83
86
  }
84
87
  console.log(`Workspace: ${cwd}`);
85
88
  for (const provider of providers) {
86
- console.log(`${provider.provider}: ${provider.available ? "available" : "missing"} | bin=${provider.bin} | model=${provider.modelDefault}${provider.version ? ` | version=${provider.version}` : ""}`);
89
+ const status = provider.available ? "available" : "missing";
90
+ console.log(`${provider.provider}: ${status} | bin=${provider.bin} | model=${provider.modelDefault}${provider.version ? ` | version=${provider.version}` : ""}`);
91
+ if (!provider.available) {
92
+ console.log(` install: ${provider.installHint}`);
93
+ }
87
94
  }
88
95
  return;
89
96
  }
@@ -123,31 +130,24 @@ async function runCommand(command, flags) {
123
130
  console.log(JSON.stringify(result, null, 2));
124
131
  return;
125
132
  }
126
- console.log(`Created run config: ${result.configPath}`);
127
- console.log(`Default agent: ${result.config.defaultAgent ?? "router"}`);
128
- console.log(`Provider order: ${(result.config.providerOrder ?? []).join(", ")}`);
133
+ console.log(`Config: ${result.configPath}`);
134
+ console.log(`Agent: ${result.config.defaultAgent ?? "router"} | Providers: ${(result.config.providerOrder ?? []).join(", ")}`);
129
135
  if (result.sourceSummary.defaultPromptFile) {
130
- console.log(`Default prompt file: ${result.sourceSummary.defaultPromptFile}`);
136
+ console.log(`Default prompt: ${result.sourceSummary.defaultPromptFile}`);
131
137
  }
132
- if (result.sourceSummary.manifestPath) {
133
- console.log(`Batch default manifest: ${result.sourceSummary.manifestPath}`);
134
- }
135
- else if (result.sourceSummary.promptFiles.length > 0) {
136
- console.log(`Batch default prompt files: ${result.sourceSummary.promptFiles.length}`);
137
- }
138
- else {
139
- console.log("Batch default prompt sources: none detected");
140
- }
141
- console.log("Detected providers:");
142
- for (const provider of result.providerSummary) {
143
- console.log(`- ${provider.provider}: ${provider.available ? "available" : "missing"} | bin=${provider.bin}`);
138
+ const availableCount = result.providerSummary.filter((p) => p.available).length;
139
+ console.log(`Providers: ${availableCount}/${result.providerSummary.length} available`);
140
+ if (availableCount === 0) {
141
+ console.log("⚠ No provider CLIs detected. Install codex, cursor agent, claude, or copilot.");
144
142
  }
143
+ const setupAssets = await discoverWorkspaceAssets(cwd);
145
144
  console.log("Next steps:");
146
145
  console.log(` prompts-gpt run${result.sourceSummary.defaultPromptFile ? "" : " --prompt-file <path>"}`);
147
- console.log(" prompts-gpt run-batch");
146
+ if (setupAssets.sweeps.length > 0) {
147
+ console.log(` prompts-gpt sweep — run a multi-iteration sweep (${setupAssets.sweeps.length} found)`);
148
+ }
148
149
  console.log(" prompts-gpt list — see all runnable assets");
149
150
  console.log(" prompts-gpt status — check workspace readiness");
150
- console.log(" prompts-gpt validate — validate your config");
151
151
  return;
152
152
  }
153
153
  if (command === "doctor") {
@@ -180,10 +180,15 @@ async function runCommand(command, flags) {
180
180
  }
181
181
  console.log(`Workspace: ${cwd}`);
182
182
  console.log("");
183
+ const providers = await detectProviders(cwd);
184
+ const availableProviderNames = providers.filter((p) => p.available).map((p) => p.provider);
183
185
  if (assets.prompts.length > 0) {
184
186
  console.log(`Prompt packs (${assets.prompts.length}):`);
185
187
  for (const p of assets.prompts) {
186
- console.log(` ${p.slug} ${p.title} [${p.source}]`);
188
+ const providerHint = availableProviderNames.length > 0
189
+ ? ` (run with: ${availableProviderNames[0]})`
190
+ : "";
191
+ console.log(` ${p.slug} — ${p.title} [${p.source}]${providerHint}`);
187
192
  console.log(` file: .prompts-gpt/${p.file}`);
188
193
  console.log(` run: prompts-gpt run --prompt-file .prompts-gpt/${p.file}`);
189
194
  }
@@ -197,9 +202,12 @@ async function runCommand(command, flags) {
197
202
  if (assets.sweeps.length > 0) {
198
203
  console.log(`Sweep prompts (${assets.sweeps.length}):`);
199
204
  for (const s of assets.sweeps) {
200
- console.log(` ${s.name}`);
205
+ const iterCount = await readSweepIterationsFromFrontmatter(path.resolve(cwd, s.file));
206
+ const iterHint = iterCount ? ` (${iterCount} iterations)` : "";
207
+ const iterFlag = iterCount ? ` --iterations ${iterCount}` : "";
208
+ console.log(` ${s.name}${iterHint}`);
201
209
  console.log(` file: ${s.file}`);
202
- console.log(` sweep: prompts-gpt sweep --prompt-file ${s.file}`);
210
+ console.log(` sweep: prompts-gpt sweep --prompt-file ${s.file}${iterFlag}`);
203
211
  }
204
212
  console.log("");
205
213
  }
@@ -223,6 +231,30 @@ async function runCommand(command, flags) {
223
231
  console.log(`Config: ${assets.configFound ? "found" : "not found — run `prompts-gpt setup`"}`);
224
232
  console.log(`Manifest: ${assets.manifestFound ? "found" : "not found — run `prompts-gpt sync`"}`);
225
233
  console.log(`Credentials: ${assets.credentialsFound ? "found" : "not found — run `prompts-gpt init --token <token>`"}`);
234
+ if (isTTYInteractive() && availableProviderNames.length > 0) {
235
+ const runnableOptions = [];
236
+ for (const p of assets.prompts) {
237
+ runnableOptions.push({ label: `run: ${p.slug} — ${p.title}`, value: `run:.prompts-gpt/${p.file}` });
238
+ }
239
+ for (const s of assets.sweeps) {
240
+ runnableOptions.push({ label: `sweep: ${s.name}`, value: `sweep:${s.file}` });
241
+ }
242
+ if (runnableOptions.length > 1) {
243
+ console.log("");
244
+ const picked = await interactiveSelect("Run something now?", [
245
+ ...runnableOptions,
246
+ { label: "(exit)", value: "exit" },
247
+ ]);
248
+ if (picked !== "exit") {
249
+ const [action, file] = picked.split(":", 2);
250
+ const cmd = action === "sweep" ? "sweep" : "run";
251
+ console.log(`\nRunning: prompts-gpt ${cmd} -f ${file}\n`);
252
+ const { spawnSync: spSync } = await import("node:child_process");
253
+ const result = spSync(process.execPath, [process.argv[1], cmd, "-f", file], { stdio: "inherit", cwd });
254
+ process.exitCode = result.status ?? 1;
255
+ }
256
+ }
257
+ }
226
258
  return;
227
259
  }
228
260
  if (command === "status") {
@@ -240,20 +272,38 @@ async function runCommand(command, flags) {
240
272
  console.log(` Credentials: ${assets.credentialsFound ? "✓" : "✗ — run \`prompts-gpt init --token <token>\`"}`);
241
273
  console.log(` Config: ${assets.configFound ? "✓" : "✗ — run \`prompts-gpt setup\`"}`);
242
274
  console.log(` Manifest: ${assets.manifestFound ? "✓" : "✗ — run \`prompts-gpt sync\`"}`);
243
- console.log(` Prompts: ${assets.prompts.length > 0 ? `✓ (${assets.prompts.length})` : " none found"}`);
275
+ const cloudCount = assets.prompts.filter((p) => p.source === "library" || p.source === "generated").length;
276
+ const localCount = assets.prompts.filter((p) => p.source === "local").length;
277
+ const promptSummary = assets.prompts.length > 0
278
+ ? `✓ (${assets.prompts.length}${cloudCount > 0 ? `, ${cloudCount} synced` : ""}${localCount > 0 ? `, ${localCount} local-only` : ""})`
279
+ : "✗ — none found";
280
+ console.log(` Prompts: ${promptSummary}`);
244
281
  console.log(` Sweeps: ${assets.sweeps.length > 0 ? `✓ (${assets.sweeps.length})` : "— none found"}`);
245
282
  console.log(` Agents: ${assets.agents.length > 0 ? `✓ (${assets.agents.length} targets)` : "✗ — run \`prompts-gpt sync\`"}`);
246
- console.log(` Providers: ${availableProviders.length > 0 ? `✓ (${availableProviders.map((p) => p.provider).join(", ")})` : "✗ — no CLI found"}`);
283
+ if (availableProviders.length > 0) {
284
+ console.log(` Providers: ✓`);
285
+ for (const p of availableProviders) {
286
+ console.log(` ${p.provider}: ${p.bin} | model: ${p.modelDefault}${p.version ? ` | ${p.version}` : ""}`);
287
+ }
288
+ }
289
+ else {
290
+ console.log(" Providers: ✗ — no CLI found");
291
+ }
247
292
  console.log("");
248
293
  if (assets.prompts.length > 0 && availableProviders.length > 0) {
249
294
  console.log("Ready to run:");
250
295
  console.log(` prompts-gpt run --prompt-file .prompts-gpt/${assets.prompts[0].file}`);
251
296
  if (assets.sweeps.length > 0) {
252
- console.log(` prompts-gpt sweep --prompt-file ${assets.sweeps[0].file}`);
297
+ for (const s of assets.sweeps) {
298
+ console.log(` prompts-gpt sweep --prompt-file ${s.file}`);
299
+ }
253
300
  }
254
301
  }
255
302
  else {
256
- console.log("Next steps:");
303
+ console.log("Not ready yet. Quick fix:");
304
+ console.log(" prompts-gpt quickstart");
305
+ console.log("");
306
+ console.log("Or step by step:");
257
307
  let step = 1;
258
308
  if (!assets.credentialsFound)
259
309
  console.log(` ${step++}. prompts-gpt init --token <project-token>`);
@@ -294,18 +344,26 @@ async function runCommand(command, flags) {
294
344
  if (!promptFile && !Boolean(flags.json)) {
295
345
  if (!config.defaultPromptFile && config.batchDefaults.promptFiles.length === 0 && !config.batchDefaults.manifestPath) {
296
346
  const assets = await discoverWorkspaceAssets(cwd);
297
- if (assets.prompts.length > 0 || assets.sweeps.length > 0) {
298
- console.log("No default prompt file configured. Available options:\n");
347
+ if (assets.prompts.length > 0 && isTTYInteractive(flags)) {
348
+ const promptOptions = assets.prompts.map((p) => ({
349
+ label: `${p.slug} — ${p.title}`,
350
+ value: `.prompts-gpt/${p.file}`,
351
+ }));
352
+ const picked = await interactiveSelect("Select a prompt to run:", promptOptions);
353
+ flags["prompt-file"] = picked;
354
+ }
355
+ else if (assets.prompts.length > 0 || assets.sweeps.length > 0) {
356
+ console.log("No default prompt configured. Available options:\n");
299
357
  if (assets.prompts.length > 0) {
300
358
  console.log("Prompt packs:");
301
359
  for (const p of assets.prompts) {
302
- console.log(` prompts-gpt run --prompt-file .prompts-gpt/${p.file}`);
360
+ console.log(` prompts-gpt run -f .prompts-gpt/${p.file}`);
303
361
  }
304
362
  }
305
363
  if (assets.sweeps.length > 0) {
306
- console.log("\nSweep prompts:");
364
+ console.log("\nSweep prompts (use `sweep` command):");
307
365
  for (const s of assets.sweeps) {
308
- console.log(` prompts-gpt sweep --prompt-file ${s.file}`);
366
+ console.log(` prompts-gpt sweep -f ${s.file}`);
309
367
  }
310
368
  }
311
369
  console.log("\nOr set a default: prompts-gpt setup --prompt-file <path>");
@@ -313,12 +371,54 @@ async function runCommand(command, flags) {
313
371
  }
314
372
  }
315
373
  }
374
+ const runProviders = await detectProviders(cwd);
375
+ const runAvailable = runProviders.filter((p) => p.available);
376
+ if (!getStringFlag(flags, "agent") && isTTYInteractive(flags) && !Boolean(flags.json) && runAvailable.length > 1) {
377
+ const providerOpts = runAvailable.map((p) => ({
378
+ label: `${p.provider} (${p.modelDefault}${p.version ? `, ${p.version}` : ""})`,
379
+ value: p.provider,
380
+ }));
381
+ providerOpts.push({ label: "router (auto-select)", value: "router" });
382
+ flags.agent = await interactiveSelect("Select a provider:", providerOpts);
383
+ }
316
384
  const agent = resolveRunAgent(flags, config.defaultAgent);
385
+ if (Boolean(flags["dry-run"])) {
386
+ const providers = await detectProviders(cwd);
387
+ const resolvedProvider = resolveRunProvider(agent, providers, config.providerOrder);
388
+ const resolvedModel = getStringFlag(flags, "model")?.trim() || config.modelOverrides[resolvedProvider]?.trim() || "";
389
+ let resolvedPrompt = promptFile || getStringFlag(flags, "prompt-file") || config.defaultPromptFile || null;
390
+ if (!resolvedPrompt) {
391
+ try {
392
+ const { resolveDefaultPromptFile: resolvePf } = await import("./index.js");
393
+ resolvedPrompt = await resolvePf(cwd, config);
394
+ }
395
+ catch {
396
+ resolvedPrompt = null;
397
+ }
398
+ }
399
+ const agentStr = getStringFlag(flags, "agent") ? agent : `${agent} (auto-selected)`;
400
+ console.log("[dry-run] Would execute:");
401
+ console.log(` Agent: ${agentStr}`);
402
+ console.log(` Provider: ${resolvedProvider}`);
403
+ console.log(` Model: ${resolvedModel || "(default)"}`);
404
+ console.log(` Prompt: ${resolvedPrompt || "(none found — pass --prompt-file)"}`);
405
+ console.log(` Timeout: ${getStringFlag(flags, "timeout") || config.timeoutSeconds}s`);
406
+ return;
407
+ }
408
+ const modelFlag = getStringFlag(flags, "model");
409
+ if (modelFlag && !Boolean(flags.json) && agent !== "router") {
410
+ const providers = await detectProviders(cwd);
411
+ const resolvedProvider = resolveRunProvider(agent, providers, config.providerOrder);
412
+ const mismatchWarning = warnModelProviderMismatch(resolvedProvider, modelFlag);
413
+ if (mismatchWarning) {
414
+ console.error(`[warning] ${mismatchWarning}`);
415
+ }
416
+ }
317
417
  const result = await runPrompt({
318
418
  cwd,
319
419
  promptFile,
320
420
  agent,
321
- model: getStringFlag(flags, "model"),
421
+ model: modelFlag,
322
422
  timeoutSeconds: parsePositiveIntFlag(getStringFlag(flags, "timeout"), "timeout"),
323
423
  artifactsDir: getStringFlag(flags, "artifacts-dir"),
324
424
  runId: getStringFlag(flags, "run-id"),
@@ -331,14 +431,31 @@ async function runCommand(command, flags) {
331
431
  console.log(JSON.stringify(result, null, 2));
332
432
  return;
333
433
  }
334
- const agentLabel = getStringFlag(flags, "agent") ? result.provider : `${result.provider} (auto-selected)`;
434
+ const agentLabel = getStringFlag(flags, "agent") ? result.provider : `${result.provider} (auto — first available from provider order)`;
335
435
  console.log(`Run ID: ${result.runId}`);
336
436
  console.log(`Provider: ${agentLabel} | Model: ${result.model}`);
337
437
  console.log(`Exit code: ${result.exitCode}`);
338
438
  console.log(`Duration: ${formatDuration(result.durationMs)}`);
439
+ if (hasTokenUsage(result.tokenUsage)) {
440
+ console.log(`Tokens: ${formatTokenUsage(result.tokenUsage)}`);
441
+ }
339
442
  console.log(`Run dir: ${result.runDir}`);
340
443
  console.log(`Summary: ${result.summaryFile}`);
341
444
  console.log(`Log: ${result.logFile}`);
445
+ if (result.exitCode === 0) {
446
+ console.log("");
447
+ console.log(`View results: cat ${result.summaryFile}`);
448
+ }
449
+ if (result.exitCode !== 0) {
450
+ const diagnostics = await extractRunDiagnostics(result.logFile, result.provider, result.model);
451
+ if (diagnostics.length > 0) {
452
+ console.log("");
453
+ console.log("Diagnostics:");
454
+ for (const d of diagnostics) {
455
+ console.log(` ${d}`);
456
+ }
457
+ }
458
+ }
342
459
  return;
343
460
  }
344
461
  if (command === "run-batch") {
@@ -379,9 +496,13 @@ async function runCommand(command, flags) {
379
496
  return;
380
497
  }
381
498
  console.log(`Batch complete: total=${result.total} success=${result.success} failed=${result.failed}`);
499
+ if (hasTokenUsage(result.tokenUsage)) {
500
+ console.log(`Batch tokens: ${formatTokenUsage(result.tokenUsage)}`);
501
+ }
382
502
  for (const [idx, run] of result.results.entries()) {
383
503
  const status = run.exitCode === 0 ? "ok" : "FAIL";
384
- console.log(` [${idx + 1}/${result.total}] ${status} ${path.basename(run.promptFile)} -> ${run.provider} (exit=${run.exitCode}, ${formatDuration(run.durationMs)})`);
504
+ const usageStr = hasTokenUsage(run.tokenUsage) ? `, tokens=${run.tokenUsage.totalTokens.toLocaleString()}` : "";
505
+ console.log(` [${idx + 1}/${result.total}] ${status} ${path.basename(run.promptFile)} -> ${run.provider} (exit=${run.exitCode}, ${formatDuration(run.durationMs)}${usageStr})`);
385
506
  }
386
507
  return;
387
508
  }
@@ -391,41 +512,141 @@ async function runCommand(command, flags) {
391
512
  const config = await loadRunConfig(cwd);
392
513
  warnOnConfigIssues(config);
393
514
  if (!sweepPromptFile && !Boolean(flags.json)) {
394
- if (!config.defaultPromptFile) {
395
- const assets = await discoverWorkspaceAssets(cwd);
396
- if (assets.sweeps.length > 0) {
397
- console.log("No default prompt file configured. Available sweep prompts:\n");
398
- for (const s of assets.sweeps) {
399
- console.log(` prompts-gpt sweep --prompt-file ${s.file}`);
515
+ const assets = await discoverWorkspaceAssets(cwd);
516
+ if (assets.sweeps.length === 1) {
517
+ const autoFile = assets.sweeps[0].file;
518
+ const iterFromFm = await readSweepIterationsFromFrontmatter(path.resolve(cwd, autoFile));
519
+ console.log(`Auto-selected sweep: ${path.basename(autoFile)}${iterFromFm ? ` (${iterFromFm} iterations from frontmatter)` : ""}`);
520
+ flags["prompt-file"] = autoFile;
521
+ if (iterFromFm && !getStringFlag(flags, "iterations")) {
522
+ flags.iterations = String(iterFromFm);
523
+ }
524
+ }
525
+ else if (assets.sweeps.length > 1) {
526
+ if (isTTYInteractive(flags)) {
527
+ const sweepOptions = await Promise.all(assets.sweeps.map(async (s) => {
528
+ const fm = await readSweepFrontmatter(path.resolve(cwd, s.file));
529
+ const iterLabel = fm.iterations ? ` (${fm.iterations} iterations)` : "";
530
+ const titleLabel = fm.title ? ` — ${fm.title}` : "";
531
+ return { label: `${path.basename(s.file, ".md")}${titleLabel}${iterLabel}`, value: s.file };
532
+ }));
533
+ const picked = await interactiveSelect("Select a sweep file:", sweepOptions);
534
+ flags["prompt-file"] = picked;
535
+ const iterFromFm = await readSweepIterationsFromFrontmatter(path.resolve(cwd, picked));
536
+ if (iterFromFm && !getStringFlag(flags, "iterations")) {
537
+ flags.iterations = String(iterFromFm);
400
538
  }
401
- if (assets.prompts.length > 0) {
402
- console.log("\nOr run a prompt pack instead:");
403
- for (const p of assets.prompts.slice(0, 3)) {
404
- console.log(` prompts-gpt run --prompt-file .prompts-gpt/${p.file}`);
405
- }
539
+ }
540
+ else {
541
+ console.log(`${assets.sweeps.length} sweep files found. Pick one with --prompt-file:\n`);
542
+ for (const s of assets.sweeps) {
543
+ const iterCount = await readSweepIterationsFromFrontmatter(path.resolve(cwd, s.file));
544
+ const iterFlag = iterCount ? ` --iterations ${iterCount}` : "";
545
+ console.log(` prompts-gpt sweep -f ${s.file}${iterFlag}`);
406
546
  }
407
- console.log("\nOr set a default: prompts-gpt setup --prompt-file <path>");
408
547
  return;
409
548
  }
410
549
  }
550
+ else {
551
+ console.log("No sweep files found.");
552
+ console.log(" Create .prompts-gpt/sweeps/<name>.md to add sweep prompts.");
553
+ console.log(" Run `prompts-gpt list` to see what's available.");
554
+ return;
555
+ }
556
+ }
557
+ if (!getStringFlag(flags, "iterations") && getStringFlag(flags, "prompt-file")) {
558
+ const iterFromFm = await readSweepIterationsFromFrontmatter(path.resolve(cwd, getStringFlag(flags, "prompt-file")));
559
+ if (iterFromFm) {
560
+ flags.iterations = String(iterFromFm);
561
+ }
562
+ }
563
+ const providers = await detectProviders(cwd);
564
+ const availableProviders = providers.filter((p) => p.available);
565
+ if (!getStringFlag(flags, "agent") && isTTYInteractive(flags) && !Boolean(flags.json) && availableProviders.length > 1) {
566
+ const providerOptions = availableProviders.map((p) => ({
567
+ label: `${p.provider} (${p.modelDefault}${p.version ? `, ${p.version}` : ""})`,
568
+ value: p.provider,
569
+ }));
570
+ providerOptions.push({ label: "router (auto-select best available)", value: "router" });
571
+ const picked = await interactiveSelect("Select a provider:", providerOptions);
572
+ flags.agent = picked;
573
+ }
574
+ if (!getStringFlag(flags, "model") && isTTYInteractive(flags) && !Boolean(flags.json)) {
575
+ const currentAgent = resolveRunAgent(flags, config.defaultAgent);
576
+ if (currentAgent !== "router") {
577
+ const defaultModel = config.modelOverrides[currentAgent]?.trim() || "";
578
+ const providerDefault = availableProviders.find((p) => p.provider === currentAgent)?.modelDefault || "";
579
+ const hint = defaultModel || providerDefault;
580
+ const modelInput = await interactiveInput("Model", hint);
581
+ if (modelInput && modelInput !== hint) {
582
+ flags.model = modelInput;
583
+ }
584
+ }
585
+ }
586
+ if (!getStringFlag(flags, "iterations") && isTTYInteractive(flags) && !Boolean(flags.json)) {
587
+ const fmIter = await readSweepIterationsFromFrontmatter(path.resolve(cwd, getStringFlag(flags, "prompt-file")));
588
+ const defaultIter = fmIter ? String(fmIter) : "1";
589
+ const iterInput = await interactiveInput("Iterations", defaultIter);
590
+ if (iterInput) {
591
+ flags.iterations = iterInput;
592
+ }
411
593
  }
412
594
  const agent = resolveRunAgent(flags, config.defaultAgent);
595
+ if (isTTYInteractive(flags) && !Boolean(flags.json) && !Boolean(flags["dry-run"])) {
596
+ const resolvedProvider = resolveRunProvider(agent, providers, config.providerOrder);
597
+ const resolvedModel = getStringFlag(flags, "model")?.trim() || config.modelOverrides[resolvedProvider]?.trim() || availableProviders.find((p) => p.provider === resolvedProvider)?.modelDefault || "auto";
598
+ const resolvedIter = getStringFlag(flags, "iterations") || "1";
599
+ const sweepFile = getStringFlag(flags, "prompt-file") || "(default)";
600
+ const iterTimeout = parsePositiveIntFlag(getStringFlag(flags, "iteration-timeout"), "iteration-timeout") ?? 5400;
601
+ const estMaxMs = parseInt(resolvedIter, 10) * iterTimeout * 1000;
602
+ console.log("");
603
+ console.log(` Sweep: ${path.basename(sweepFile)}`);
604
+ console.log(` Provider: ${resolvedProvider}`);
605
+ console.log(` Model: ${resolvedModel}`);
606
+ console.log(` Iterations: ${resolvedIter}`);
607
+ console.log(` Max time: ~${formatDuration(estMaxMs)}`);
608
+ console.log("");
609
+ const confirm = await interactiveInput("Run this sweep? [Y/n]", "Y");
610
+ if (confirm.toLowerCase() === "n" || confirm.toLowerCase() === "no") {
611
+ console.log("Cancelled.");
612
+ return;
613
+ }
614
+ }
413
615
  const onProgress = Boolean(flags.json)
414
616
  ? undefined
415
617
  : (event) => {
416
618
  if (event.type === "preflight") {
417
619
  const report = JSON.parse(event.message);
418
- console.log(`[preflight] provider=${report.provider} model=${report.model} branch=${report.gitBranch} dirty=${report.gitDirtyFiles} disk=${report.diskFreeMb}MB iterations=${report.iterations}`);
419
- console.log(`[preflight] prompt=${report.promptFile}`);
620
+ console.log(`Provider: ${report.provider} | Model: ${report.model} | Iterations: ${report.iterations}`);
621
+ console.log(`Branch: ${report.gitBranch} | Dirty files: ${report.gitDirtyFiles} | Free disk: ${report.diskFreeMb}MB`);
622
+ console.log(`Prompt: ${path.basename(report.promptFile)}`);
420
623
  for (const w of report.warnings)
421
- console.log(`[preflight] warning: ${w}`);
624
+ console.log(`⚠ ${w}`);
422
625
  }
423
626
  else if (event.type === "iteration_start") {
424
627
  console.log(`\n--- Iteration ${event.iteration}/${event.total} ---`);
628
+ if (process.stdout.isTTY) {
629
+ const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
630
+ let fi = 0;
631
+ const startMs = Date.now();
632
+ const spinner = setInterval(() => {
633
+ const elapsed = formatDuration(Date.now() - startMs);
634
+ process.stdout.write(`\r${frames[fi++ % frames.length]} Running ${event.provider}... ${elapsed} `);
635
+ }, 120);
636
+ spinner.unref?.();
637
+ const clearSpinner = () => { clearInterval(spinner); process.stdout.write("\r\x1b[K"); };
638
+ globalThis.__sweepSpinner = clearSpinner;
639
+ }
425
640
  }
426
641
  else if (event.type === "iteration_end") {
642
+ const clearFn = globalThis.__sweepSpinner;
643
+ if (clearFn) {
644
+ clearFn();
645
+ delete globalThis.__sweepSpinner;
646
+ }
427
647
  const elapsed = formatDuration(event.durationMs);
428
- console.log(`--- Iteration ${event.iteration} ${event.status} (${elapsed}) ---`);
648
+ const icon = event.status === "success" ? "✓" : "✗";
649
+ console.log(`${icon} Iteration ${event.iteration} ${event.status} (${elapsed})`);
429
650
  }
430
651
  else if (event.type === "attempt_retry") {
431
652
  console.log(`[retry] iteration ${event.iteration}, attempt ${event.attempt}/${event.maxAttempts}, backoff ${event.backoffMs}ms`);
@@ -454,6 +675,8 @@ async function runCommand(command, flags) {
454
675
  dryRun: Boolean(flags["dry-run"]),
455
676
  maxRunDirs: parsePositiveIntFlag(getStringFlag(flags, "max-run-dirs"), "max-run-dirs"),
456
677
  summaryLines: parsePositiveIntFlag(getStringFlag(flags, "summary-lines"), "summary-lines"),
678
+ background: Boolean(flags.background),
679
+ permissionMode: getStringFlag(flags, "permission-mode"),
457
680
  onProgress,
458
681
  });
459
682
  if (Boolean(flags.json)) {
@@ -461,11 +684,25 @@ async function runCommand(command, flags) {
461
684
  return;
462
685
  }
463
686
  if (result.dryRun) {
687
+ const iterTimeout = parsePositiveIntFlag(getStringFlag(flags, "iteration-timeout"), "iteration-timeout") ?? 5400;
688
+ const estMaxDuration = result.totalIterations * iterTimeout;
464
689
  console.log("[dry-run] Would execute sweep with:");
465
690
  console.log(` Provider: ${result.provider}`);
466
691
  console.log(` Model: ${result.model}`);
467
692
  console.log(` Iterations: ${result.totalIterations}`);
468
693
  console.log(` Prompt: ${result.promptFile}`);
694
+ console.log(` Max duration: ~${formatDuration(estMaxDuration * 1000)} (${result.totalIterations} x ${formatDuration(iterTimeout * 1000)})`);
695
+ try {
696
+ const { readFile: fsRead } = await import("node:fs/promises");
697
+ const previewText = await fsRead(result.promptFile, "utf8");
698
+ const previewLines = previewText.split("\n").slice(0, 5);
699
+ console.log(` Prompt preview:`);
700
+ for (const line of previewLines)
701
+ console.log(` ${line}`);
702
+ if (previewText.split("\n").length > 5)
703
+ console.log(` ... (${previewText.split("\n").length - 5} more lines)`);
704
+ }
705
+ catch { /* skip preview */ }
469
706
  return;
470
707
  }
471
708
  console.log(`Run ID: ${result.runId}`);
@@ -473,13 +710,154 @@ async function runCommand(command, flags) {
473
710
  console.log(`Prompt: ${result.promptFile}`);
474
711
  console.log(`Iterations: ${result.succeeded}/${result.totalIterations} succeeded`);
475
712
  console.log(`Duration: ${formatDuration(result.totalDurationMs)}`);
713
+ if (hasTokenUsage(result.tokenUsage)) {
714
+ console.log(`Tokens: ${formatTokenUsage(result.tokenUsage)}`);
715
+ }
476
716
  console.log(`Run dir: ${result.runDir}`);
477
717
  console.log(`Manifest: ${result.manifestFile}`);
718
+ if (result.iterations.length > 1) {
719
+ console.log("");
720
+ console.log("Per-iteration results:");
721
+ for (const iter of result.iterations) {
722
+ const tools = iter.toolCounts;
723
+ const toolStr = tools.total > 0 ? ` | tools: ${tools.total} (R:${tools.reads} W:${tools.writes} S:${tools.shells})` : "";
724
+ const usageStr = hasTokenUsage(iter.tokenUsage) ? ` | tokens: ${iter.tokenUsage.totalTokens.toLocaleString()}` : "";
725
+ console.log(` [${iter.iteration}/${result.totalIterations}] ${iter.status} (${formatDuration(iter.durationMs)})${toolStr}${usageStr}`);
726
+ }
727
+ const totalTools = result.iterations.reduce((acc, i) => ({
728
+ reads: acc.reads + i.toolCounts.reads, writes: acc.writes + i.toolCounts.writes,
729
+ shells: acc.shells + i.toolCounts.shells, searches: acc.searches + i.toolCounts.searches,
730
+ webSearches: acc.webSearches + i.toolCounts.webSearches, total: acc.total + i.toolCounts.total,
731
+ }), { reads: 0, writes: 0, shells: 0, searches: 0, webSearches: 0, total: 0 });
732
+ if (totalTools.total > 0) {
733
+ console.log(` Total tools: ${totalTools.total} (reads: ${totalTools.reads}, writes: ${totalTools.writes}, shells: ${totalTools.shells}, searches: ${totalTools.searches})`);
734
+ }
735
+ }
736
+ console.log("");
737
+ console.log("Inspect results:");
738
+ console.log(` cat ${result.manifestFile}`);
739
+ console.log(` ls ${result.runDir}`);
478
740
  if (result.failed > 0) {
479
741
  process.exitCode = 1;
480
742
  }
481
743
  return;
482
744
  }
745
+ if (command === "quickstart") {
746
+ const cwd = getResolvedCwd(flags);
747
+ const assets = await discoverWorkspaceAssets(cwd);
748
+ const providers = await detectProviders(cwd);
749
+ const availableProviders = providers.filter((p) => p.available);
750
+ console.log("Prompts-GPT Quickstart");
751
+ console.log("======================");
752
+ console.log("");
753
+ if (!assets.credentialsFound) {
754
+ console.log("Step 1: Save your project token");
755
+ console.log(" prompts-gpt init --token-prompt");
756
+ console.log("");
757
+ console.log(" Get your token from: https://prompts-gpt.com/studio/projects");
758
+ console.log("");
759
+ console.log("Run `prompts-gpt quickstart` again after saving your token.");
760
+ return;
761
+ }
762
+ console.log("✓ Credentials found");
763
+ if (!assets.configFound) {
764
+ console.log("→ Setting up config...");
765
+ try {
766
+ await initRunConfig({ cwd, overwrite: false });
767
+ console.log("✓ Config created");
768
+ }
769
+ catch {
770
+ console.log("✓ Config already exists");
771
+ }
772
+ }
773
+ else {
774
+ console.log("✓ Config found");
775
+ }
776
+ if (availableProviders.length === 0) {
777
+ console.log("");
778
+ console.log("✗ No provider CLIs found. Install at least one:");
779
+ console.log(" - codex: npm install -g @openai/codex");
780
+ console.log(" - claude: npm install -g @anthropic-ai/claude-code");
781
+ console.log(" - cursor: Install Cursor IDE (includes `agent` CLI)");
782
+ return;
783
+ }
784
+ console.log(`✓ Providers: ${availableProviders.map((p) => p.provider).join(", ")}`);
785
+ if (assets.prompts.length === 0 && assets.sweeps.length === 0) {
786
+ console.log("");
787
+ console.log("→ No prompts found. Syncing from Prompts Studio...");
788
+ try {
789
+ const clientOpts = await resolveClientOptions("quickstart", flags).catch(() => null);
790
+ if (clientOpts) {
791
+ const qsClient = new PromptsGptClient(clientOpts);
792
+ const pulledPrompts = await qsClient.pullPrompts();
793
+ if (pulledPrompts.length > 0) {
794
+ const syncResult = await syncPrompts(pulledPrompts, { cwd, agent: "all" });
795
+ console.log(`✓ Synced ${syncResult.markdown.written.length} prompt(s) and ${syncResult.agents.written.length} agent file(s)`);
796
+ const refreshed = await discoverWorkspaceAssets(cwd);
797
+ if (refreshed.prompts.length > 0 || refreshed.sweeps.length > 0) {
798
+ console.log(`✓ Prompts: ${refreshed.prompts.length} prompt packs, ${refreshed.sweeps.length} sweeps`);
799
+ }
800
+ }
801
+ else {
802
+ console.log("No prompts found in your Prompts Studio library.");
803
+ console.log(" Configure prompts at: https://prompts-gpt.com/studio/projects");
804
+ console.log(" Or run: prompts-gpt generate --goal \"Your task\"");
805
+ return;
806
+ }
807
+ }
808
+ else {
809
+ console.log("✗ Could not sync (credentials issue). Run manually:");
810
+ console.log(" prompts-gpt sync");
811
+ return;
812
+ }
813
+ }
814
+ catch (syncErr) {
815
+ console.log(`✗ Sync failed: ${syncErr instanceof Error ? syncErr.message : String(syncErr)}`);
816
+ console.log(" Run manually: prompts-gpt sync");
817
+ return;
818
+ }
819
+ const refreshedAssets = await discoverWorkspaceAssets(cwd);
820
+ if (refreshedAssets.prompts.length === 0 && refreshedAssets.sweeps.length === 0) {
821
+ return;
822
+ }
823
+ }
824
+ console.log(`✓ Prompts: ${assets.prompts.length} prompt packs, ${assets.sweeps.length} sweeps`);
825
+ console.log("");
826
+ console.log("You're ready!");
827
+ if (isTTYInteractive() && (assets.prompts.length > 0 || assets.sweeps.length > 0)) {
828
+ const qsOptions = [];
829
+ for (const s of assets.sweeps) {
830
+ qsOptions.push({ label: `sweep: ${s.name}`, value: `sweep:${s.file}` });
831
+ }
832
+ for (const p of assets.prompts.slice(0, 5)) {
833
+ qsOptions.push({ label: `run: ${p.title}`, value: `run:.prompts-gpt/${p.file}` });
834
+ }
835
+ qsOptions.push({ label: "(show commands and exit)", value: "exit" });
836
+ console.log("");
837
+ const qsPicked = await interactiveSelect("Run something now?", qsOptions);
838
+ if (qsPicked !== "exit") {
839
+ const [action, file] = qsPicked.split(":", 2);
840
+ const cmd = action === "sweep" ? "sweep" : "run";
841
+ console.log(`\nRunning: prompts-gpt ${cmd} -f ${file}\n`);
842
+ const { spawnSync: spSync } = await import("node:child_process");
843
+ const result = spSync(process.execPath, [process.argv[1], cmd, "-f", file], { stdio: "inherit", cwd });
844
+ process.exitCode = result.status ?? 1;
845
+ return;
846
+ }
847
+ }
848
+ console.log("");
849
+ console.log("Try these commands:");
850
+ console.log("");
851
+ if (assets.prompts.length > 0) {
852
+ console.log(` prompts-gpt run -f .prompts-gpt/${assets.prompts[0].file}`);
853
+ }
854
+ if (assets.sweeps.length > 0) {
855
+ console.log(` prompts-gpt sweep -f ${assets.sweeps[0].file}`);
856
+ }
857
+ console.log(" prompts-gpt list — see all available prompts and sweeps");
858
+ console.log(" prompts-gpt status — check workspace readiness");
859
+ return;
860
+ }
483
861
  if (command === "load-config") {
484
862
  const cwd = getResolvedCwd(flags);
485
863
  const client = await createClientForCommand(command, flags);
@@ -532,10 +910,22 @@ async function runCommand(command, flags) {
532
910
  console.log(` prompts-gpt run --prompt-file .prompts-gpt/${firstFile}`);
533
911
  }
534
912
  }
913
+ const lcAssets = await discoverWorkspaceAssets(cwd);
914
+ if (lcAssets.sweeps.length > 0) {
915
+ console.log(`\nSweep files available (${lcAssets.sweeps.length}):`);
916
+ for (const s of lcAssets.sweeps) {
917
+ console.log(` prompts-gpt sweep --prompt-file ${s.file}`);
918
+ }
919
+ }
535
920
  return;
536
921
  }
537
922
  if (command === "init") {
538
- const token = await resolveTokenInput(flags, { command });
923
+ let token = await resolveTokenInput(flags, { command });
924
+ if (!token && process.stdin.isTTY && process.stdout.isTTY) {
925
+ console.log("No token flag provided — prompting interactively.");
926
+ console.log("Get your token from: https://prompts-gpt.com/studio/projects\n");
927
+ token = await readTokenFromPrompt(command);
928
+ }
539
929
  if (!token) {
540
930
  throw new CliError("Run `prompts-gpt init --token <project-token>`, `--token-stdin`, or `--token-prompt`.", CLI_EXIT_CODES.validation, {
541
931
  helpCommand: "init",
@@ -573,7 +963,10 @@ async function runCommand(command, flags) {
573
963
  });
574
964
  console.log(`Saved Prompts-GPT credentials to ${result.credentialsPath}`);
575
965
  console.log("The credentials file is added to .gitignore.");
576
- console.log("Next: prompts-gpt sync");
966
+ console.log("");
967
+ console.log("Next steps:");
968
+ console.log(" prompts-gpt quickstart — interactive setup (recommended)");
969
+ console.log(" prompts-gpt sync — pull prompts and sync agent files");
577
970
  return;
578
971
  }
579
972
  if (command === "pull") {
@@ -593,28 +986,30 @@ async function runCommand(command, flags) {
593
986
  if (result.skipped.length) {
594
987
  console.log(`Skipped ${result.skipped.length} existing file(s). Use --overwrite to replace them.`);
595
988
  }
989
+ if (result.written.length > 0) {
990
+ console.log("");
991
+ console.log("Run a pulled prompt:");
992
+ console.log(` prompts-gpt run --prompt-file ${result.written[0]}`);
993
+ console.log(" prompts-gpt list — see all available prompts");
994
+ }
596
995
  return;
597
996
  }
598
997
  if (command === "generate") {
599
998
  validateToolFlag(getStringFlag(flags, "tool"));
600
999
  const goal = getStringFlag(flags, "goal");
601
1000
  if (!goal) {
602
- throw new CliError("Prompt generation requires `--goal`.", CLI_EXIT_CODES.validation, {
603
- helpCommand: "generate",
604
- });
1001
+ throw new CliError("Prompt generation requires `--goal`.\n\n" +
1002
+ "Example:\n" +
1003
+ " prompts-gpt generate --goal \"Review pull requests for security issues\"\n" +
1004
+ " prompts-gpt generate --goal \"Write unit tests\" --sync-agents\n\n" +
1005
+ "Use --sync-agents to also write agent files (AGENTS.md, .cursor/rules, etc.).", CLI_EXIT_CODES.validation, { helpCommand: "generate" });
605
1006
  }
606
1007
  validateAgentFlag(getStringFlag(flags, "agent"));
1008
+ if (!Boolean(flags.json)) {
1009
+ printDataTransmissionNotice("generate", { goal, context: getStringFlag(flags, "context"), constraints: getStringFlag(flags, "constraints") });
1010
+ }
607
1011
  const client = await createClientForCommand(command, flags);
608
- const prompt = await client.generatePrompt({
609
- goal,
610
- context: getStringFlag(flags, "context") || "",
611
- constraints: getStringFlag(flags, "constraints") || "",
612
- desiredOutput: getStringFlag(flags, "desired-output") || "A reusable prompt pack saved as Markdown.",
613
- tool: getStringFlag(flags, "tool") || "Codex",
614
- mode: getStringFlag(flags, "mode") || "implement",
615
- artifactType: getStringFlag(flags, "artifact-type") || "prompt-file",
616
- includeWebSearch: Boolean(flags["web-search"]),
617
- });
1012
+ const prompt = await client.generatePrompt(buildGenerateInput(flags));
618
1013
  const cwd = getResolvedCwd(flags);
619
1014
  const result = await writePromptMarkdownFiles([prompt], {
620
1015
  cwd,
@@ -624,7 +1019,12 @@ async function runCommand(command, flags) {
624
1019
  const shouldSyncAgents = Boolean(flags["sync-agents"]) || typeof getStringFlag(flags, "agent") === "string";
625
1020
  if (shouldSyncAgents) {
626
1021
  const target = getStringFlag(flags, "agent") || "all";
627
- const pulled = await client.pullPrompts().catch(() => []);
1022
+ const pulled = await client.pullPrompts().catch((err) => {
1023
+ if (!Boolean(flags.json)) {
1024
+ console.error(`[warning] Could not pull existing prompts for agent sync: ${err.message}`);
1025
+ }
1026
+ return [];
1027
+ });
628
1028
  const syncedPrompts = mergePromptPacks([prompt, ...pulled]);
629
1029
  const agentResult = await writeAgentFiles(syncedPrompts, {
630
1030
  cwd,
@@ -653,17 +1053,11 @@ async function runCommand(command, flags) {
653
1053
  const goal = getStringFlag(flags, "goal");
654
1054
  const generatedOnly = Boolean(flags["generated-only"]);
655
1055
  const client = await createClientForCommand(command, flags);
1056
+ if (goal && !Boolean(flags.json)) {
1057
+ printDataTransmissionNotice("sync", { goal, context: getStringFlag(flags, "context"), constraints: getStringFlag(flags, "constraints") });
1058
+ }
656
1059
  if (goal) {
657
- prompts.push(await client.generatePrompt({
658
- goal,
659
- context: getStringFlag(flags, "context") || "",
660
- constraints: getStringFlag(flags, "constraints") || "",
661
- desiredOutput: getStringFlag(flags, "desired-output") || "A reusable prompt pack saved as Markdown.",
662
- tool: getStringFlag(flags, "tool") || "Codex",
663
- mode: getStringFlag(flags, "mode") || "implement",
664
- artifactType: getStringFlag(flags, "artifact-type") || "prompt-file",
665
- includeWebSearch: Boolean(flags["web-search"]),
666
- }));
1060
+ prompts.push(await client.generatePrompt(buildGenerateInput(flags)));
667
1061
  }
668
1062
  if (!generatedOnly) {
669
1063
  prompts.push(...await client.pullPrompts(buildPullQuery(flags, parsedLimit)));
@@ -691,6 +1085,21 @@ async function runCommand(command, flags) {
691
1085
  }
692
1086
  console.log(`Synced ${result.agents.written.length} agent file(s): ${result.agents.targets.join(", ")}.`);
693
1087
  console.log(`Manifest: ${result.manifest.manifestPath}`);
1088
+ const syncCwd = getResolvedCwd(flags);
1089
+ const postSyncAssets = await discoverWorkspaceAssets(syncCwd);
1090
+ if (postSyncAssets.sweeps.length > 0) {
1091
+ console.log(`Sweeps: ${postSyncAssets.sweeps.length} sweep file(s) available.`);
1092
+ }
1093
+ const postProviders = await detectProviders(syncCwd);
1094
+ const postAvailable = postProviders.filter((p) => p.available);
1095
+ if (postAvailable.length > 0 && postSyncAssets.prompts.length > 0) {
1096
+ console.log("");
1097
+ console.log("Next steps:");
1098
+ console.log(` prompts-gpt run --prompt-file .prompts-gpt/${postSyncAssets.prompts[0].file}`);
1099
+ if (postSyncAssets.sweeps.length > 0) {
1100
+ console.log(` prompts-gpt sweep --prompt-file ${postSyncAssets.sweeps[0].file}`);
1101
+ }
1102
+ }
694
1103
  return;
695
1104
  }
696
1105
  if (command === "project") {
@@ -744,7 +1153,11 @@ async function resolveClientOptions(command, flags) {
744
1153
  }
745
1154
  }
746
1155
  if (!token) {
747
- throw new CliError("Project token is missing. Run `prompts-gpt init --token <project-token>` or pass `--token`, `--token-stdin`, or `--token-prompt` for this command.", CLI_EXIT_CODES.auth, { helpCommand: command });
1156
+ const cloudCommands = new Set(["sync", "generate", "pull", "load-config", "project"]);
1157
+ const extra = cloudCommands.has(command)
1158
+ ? `\n\nThis command connects to prompts-gpt.com. Set up credentials first:\n prompts-gpt init --token <project-token>\n prompts-gpt quickstart`
1159
+ : "";
1160
+ throw new CliError(`Project token is missing. Run \`prompts-gpt init --token <project-token>\` or pass \`--token\`, \`--token-stdin\`, or \`--token-prompt\` for this command.${extra}`, CLI_EXIT_CODES.auth, { helpCommand: command });
748
1161
  }
749
1162
  return {
750
1163
  token,
@@ -911,7 +1324,7 @@ function getCommandOptions(command) {
911
1324
  help: { type: "boolean" },
912
1325
  cwd: { type: "string" },
913
1326
  json: { type: "boolean" },
914
- "prompt-file": { type: "string" },
1327
+ "prompt-file": { type: "string", short: "f" },
915
1328
  agent: { type: "string" },
916
1329
  model: { type: "string" },
917
1330
  timeout: { type: "string" },
@@ -921,6 +1334,8 @@ function getCommandOptions(command) {
921
1334
  "no-approve-mcps": { type: "boolean" },
922
1335
  background: { type: "boolean" },
923
1336
  "permission-mode": { type: "string" },
1337
+ "dry-run": { type: "boolean" },
1338
+ "non-interactive": { type: "boolean" },
924
1339
  };
925
1340
  }
926
1341
  if (command === "sweep") {
@@ -928,10 +1343,10 @@ function getCommandOptions(command) {
928
1343
  help: { type: "boolean" },
929
1344
  cwd: { type: "string" },
930
1345
  json: { type: "boolean" },
931
- "prompt-file": { type: "string" },
1346
+ "prompt-file": { type: "string", short: "f" },
932
1347
  agent: { type: "string" },
933
1348
  model: { type: "string" },
934
- iterations: { type: "string" },
1349
+ iterations: { type: "string", short: "n" },
935
1350
  "iteration-timeout": { type: "string" },
936
1351
  "max-retries": { type: "string" },
937
1352
  "artifacts-dir": { type: "string" },
@@ -944,6 +1359,7 @@ function getCommandOptions(command) {
944
1359
  "summary-lines": { type: "string" },
945
1360
  background: { type: "boolean" },
946
1361
  "permission-mode": { type: "string" },
1362
+ "non-interactive": { type: "boolean" },
947
1363
  };
948
1364
  }
949
1365
  if (command === "run-batch") {
@@ -967,7 +1383,7 @@ function getCommandOptions(command) {
967
1383
  agent: { type: "string" },
968
1384
  };
969
1385
  }
970
- if (command === "list" || command === "status" || command === "validate" || command === "providers" || command === "doctor") {
1386
+ if (command === "quickstart" || command === "list" || command === "status" || command === "validate" || command === "providers" || command === "doctor") {
971
1387
  return {
972
1388
  help: { type: "boolean" },
973
1389
  cwd: { type: "string" },
@@ -979,18 +1395,61 @@ function getCommandOptions(command) {
979
1395
  });
980
1396
  }
981
1397
  function toCliParseError(error, command) {
982
- const message = normalizeParseErrorMessage(error instanceof Error ? error.message : String(error));
1398
+ const runnableCmd = isRunnableCommand(command) ? command : undefined;
1399
+ const message = normalizeParseErrorMessage(error instanceof Error ? error.message : String(error), runnableCmd);
983
1400
  return new CliError(message, CLI_EXIT_CODES.usage, {
984
1401
  helpCommand: command,
985
1402
  });
986
1403
  }
987
- function normalizeParseErrorMessage(message) {
1404
+ function normalizeParseErrorMessage(message, command) {
988
1405
  const trimmed = message.trim();
989
1406
  if (trimmed.startsWith("Unknown option")) {
990
- return `${trimmed}.`;
1407
+ const match = trimmed.match(/Unknown option '(--[^']+)'/);
1408
+ const unknownFlag = match?.[1];
1409
+ const base = trimmed.endsWith(".") ? trimmed : `${trimmed}.`;
1410
+ if (unknownFlag && command) {
1411
+ const suggestion = suggestClosestFlag(unknownFlag, command);
1412
+ if (suggestion) {
1413
+ return `${base} Did you mean ${suggestion}?`;
1414
+ }
1415
+ }
1416
+ return base;
991
1417
  }
992
1418
  return trimmed;
993
1419
  }
1420
+ function suggestClosestFlag(unknown, command) {
1421
+ const options = getCommandOptions(command);
1422
+ const flagName = unknown.replace(/^--/, "");
1423
+ const candidates = Object.keys(options);
1424
+ let best = null;
1425
+ let bestScore = 0;
1426
+ for (const candidate of candidates) {
1427
+ const score = computeFlagSimilarity(flagName, candidate);
1428
+ if (score > bestScore && score >= 0.4) {
1429
+ bestScore = score;
1430
+ best = candidate;
1431
+ }
1432
+ }
1433
+ return best ? `--${best}` : null;
1434
+ }
1435
+ function computeFlagSimilarity(a, b) {
1436
+ if (a === b)
1437
+ return 1;
1438
+ const shorter = a.length < b.length ? a : b;
1439
+ const longer = a.length < b.length ? b : a;
1440
+ if (longer.startsWith(shorter) || longer.endsWith(shorter))
1441
+ return 0.8;
1442
+ if (longer.includes(shorter) && shorter.length >= 3)
1443
+ return 0.6;
1444
+ if (shorter.length === 0)
1445
+ return 0;
1446
+ let matches = 0;
1447
+ for (let i = 0; i < Math.min(a.length, b.length); i++) {
1448
+ if (a[i] === b[i])
1449
+ matches++;
1450
+ }
1451
+ return matches / Math.max(a.length, b.length);
1452
+ }
994
1453
  function validateFlagConflicts(command, flags) {
995
1454
  const tokenSourceCount = [Boolean(flags.token), Boolean(flags["token-stdin"]), Boolean(flags["token-prompt"])].filter(Boolean).length;
996
1455
  if (tokenSourceCount > 1) {
@@ -1266,32 +1725,36 @@ Usage:
1266
1725
  prompts-gpt help [command]
1267
1726
  prompts-gpt version
1268
1727
 
1269
- Commands:
1270
- init Save a project token in .prompts-gpt/.credentials.json
1271
- setup Scaffold .prompts-gpt/config.json for local orchestration
1272
- load-config Pull configuration and prompts directly from Prompts Studio
1273
- pull Download prompt packs as Markdown files
1274
- generate Generate one prompt pack from a goal
1275
- sync Generate and/or pull prompt packs, then sync agent files
1276
- list Show available prompts, sweeps, and agent integrations
1728
+ Setup:
1729
+ quickstart Interactive setup credentials, config, and first run
1730
+ init Save a project token
1731
+ setup Scaffold local orchestration config
1277
1732
  status Show workspace readiness and next steps
1278
- validate Validate .prompts-gpt/config.json and report issues
1279
- run Execute a prompt file with local agent orchestration
1280
- run-batch Execute multiple prompt files from config, manifest, or list
1281
- sweep Multi-iteration prompt execution with progress and summaries
1282
- providers Show detected local provider CLIs
1283
- doctor Validate local run prerequisites and config
1284
- project Show the current project linked to the token
1285
- version Show the current CLI version
1286
- help Show global or command-specific help
1733
+ doctor Validate prerequisites and config
1734
+
1735
+ Sync & Generate:
1736
+ sync Pull prompt packs and sync agent files
1737
+ pull Download prompt packs as Markdown files
1738
+ generate Generate a prompt pack from a goal
1739
+ load-config Pull config and prompts from Prompts Studio
1740
+
1741
+ Run:
1742
+ run Execute one prompt with a local agent (-f <file>)
1743
+ run-batch Execute multiple prompts
1744
+ sweep Multi-iteration execution (-f <file> -n <count>)
1745
+
1746
+ Inspect:
1747
+ list Show prompts, sweeps, and agent integrations
1748
+ validate Check config for errors
1749
+ providers Show detected provider CLIs
1750
+ project Show project linked to the token
1287
1751
 
1288
1752
  Global options:
1289
1753
  --help Show help
1290
1754
  --version Show the current CLI version
1291
1755
 
1292
- Supported tools: ${VALID_TOOLS.join(", ")}
1293
- Supported agent targets: ${SUPPORTED_AGENT_TARGETS.join(", ")}
1294
- Run agent profiles: ${ORCHESTRATION_AGENT_PROFILES.join(", ")}
1756
+ Providers: ${ORCHESTRATION_AGENT_PROFILES.filter((p) => p !== "router").join(", ")} (or router for auto-select)
1757
+ Agent targets: ${SUPPORTED_AGENT_TARGETS.join(", ")}
1295
1758
  `);
1296
1759
  }
1297
1760
  function parsePositiveIntFlag(raw, flagName) {
@@ -1339,18 +1802,24 @@ function getCommandHelp(command) {
1339
1802
  return `prompts-gpt init
1340
1803
 
1341
1804
  Usage:
1342
- prompts-gpt init (--token <project-token> | --token-stdin | --token-prompt) [--api-url <url>] [--cwd <path>]
1805
+ prompts-gpt init [--token <project-token> | --token-stdin | --token-prompt] [--api-url <url>] [--cwd <path>]
1343
1806
 
1344
1807
  Why use it:
1345
- Stores a project token in a local credentials file so day-one CLI usage does not require re-pasting secrets on every command.
1808
+ Stores a project token locally so you don't have to pass it on every command.
1809
+ If run without flags in an interactive terminal, it will prompt for the token.
1346
1810
 
1347
1811
  Options:
1348
1812
  --token <token> Project API token. Must start with pgpt_.
1349
- --token-stdin Read the project token from stdin so it does not end up in shell history.
1813
+ --token-stdin Read the project token from stdin (for CI/CD pipelines).
1350
1814
  --token-prompt Prompt for the project token without echoing it.
1351
1815
  --api-url <url> Custom API base URL for self-hosted instances.
1352
- --cwd <path> Target project directory that will receive .prompts-gpt/.credentials.json.
1816
+ --cwd <path> Target project directory.
1353
1817
  --help Show this command help.
1818
+
1819
+ Examples:
1820
+ prompts-gpt init # interactive prompt
1821
+ prompts-gpt init --token pgpt_abc123 # direct token
1822
+ printf '%s' "$TOKEN" | prompts-gpt init --token-stdin # CI/CD
1354
1823
  `;
1355
1824
  }
1356
1825
  if (command === "setup") {
@@ -1411,6 +1880,10 @@ Usage:
1411
1880
  Why use it:
1412
1881
  Pulls prompt packs into local Markdown files so teams can review and use the same prompt assets inside their repository.
1413
1882
 
1883
+ Data privacy:
1884
+ Downloads prompts from your prompts-gpt.com project library.
1885
+ No local files or repo content are uploaded.
1886
+
1414
1887
  Options:
1415
1888
  --query <text> Search query for the project prompt library.
1416
1889
  --q <text> Short alias for --query.
@@ -1437,6 +1910,11 @@ Usage:
1437
1910
  Why use it:
1438
1911
  Creates a new reusable prompt pack from a concrete developer goal, then optionally installs agent-readable files immediately.
1439
1912
 
1913
+ Data privacy:
1914
+ The text you pass via --goal, --context, and --constraints is sent to prompts-gpt.com
1915
+ for AI-powered prompt generation. No local files or repo content are uploaded.
1916
+ Do not include PII, secrets, or confidential data in these flags.
1917
+
1440
1918
  Options:
1441
1919
  --goal <text> Required. The task to generate a prompt pack for.
1442
1920
  --context <text> Extra project or stack context for the generator.
@@ -1449,13 +1927,19 @@ Options:
1449
1927
  --out <dir> Output directory inside the current project. Default: .prompts-gpt
1450
1928
  --overwrite Replace an existing generated Markdown file.
1451
1929
  --agent <targets> Sync agent files for specific targets after generation.
1452
- --sync-agents Sync all agent files after generation.
1930
+ When set, also writes agent-specific files (AGENTS.md, .cursor/rules, etc.).
1931
+ --sync-agents Sync all agent files after generation (same as --agent all).
1453
1932
  --token <token> Override the saved local token for this command only.
1454
1933
  --token-stdin Read the override token from stdin for this command only.
1455
1934
  --token-prompt Prompt for the override token without echoing it.
1456
1935
  --api-url <url> Override the saved API base URL for this command only.
1457
1936
  --cwd <path> Project directory to inspect for local credentials and output files.
1458
1937
  --help Show this command help.
1938
+
1939
+ Examples:
1940
+ prompts-gpt generate --goal "Review PRs for security issues"
1941
+ prompts-gpt generate --goal "Write unit tests" --sync-agents
1942
+ prompts-gpt generate --goal "Add error handling" --agent cursor,codex
1459
1943
  `;
1460
1944
  }
1461
1945
  if (command === "sync") {
@@ -1467,6 +1951,13 @@ Usage:
1467
1951
  Why use it:
1468
1952
  Gives teams a one-command path to refresh Markdown prompts, agent files, and the local manifest from the same source of truth.
1469
1953
 
1954
+ Data privacy:
1955
+ Prompts are downloaded from your prompts-gpt.com project library.
1956
+ When --goal is used, the text you pass is sent to prompts-gpt.com for AI generation.
1957
+ No local files or repo content are uploaded — only explicit flag values are transmitted.
1958
+ Do not include PII, secrets, or confidential data in --goal, --context, or --constraints.
1959
+ Use --dry-run to preview what would be synced without sending or writing anything.
1960
+
1470
1961
  Options:
1471
1962
  --goal <text> Generate one prompt pack before syncing.
1472
1963
  --generated-only Skip library pull and sync only the generated prompt pack from --goal.
@@ -1492,19 +1983,26 @@ Options:
1492
1983
  --api-url <url> Override the saved API base URL for this command only.
1493
1984
  --cwd <path> Project directory to inspect for local credentials and output files.
1494
1985
  --help Show this command help.
1986
+
1987
+ Examples:
1988
+ prompts-gpt sync # pull all prompts + sync agent files
1989
+ prompts-gpt sync --goal "Add error handling" # generate + pull + sync
1990
+ prompts-gpt sync --dry-run # preview without writing
1991
+ prompts-gpt sync --agent cursor,codex # sync only specific agents
1495
1992
  `;
1496
1993
  }
1497
1994
  if (command === "run") {
1498
1995
  return `prompts-gpt run
1499
1996
 
1500
1997
  Usage:
1501
- prompts-gpt run [--prompt-file <path>] [--agent codex|cursor|claude|copilot|router] [--model <name>] [--timeout <seconds>] [--artifacts-dir <path>] [--run-id <id>] [--sandbox <mode>] [--no-approve-mcps] [--background] [--permission-mode <mode>] [--json] [--cwd <path>]
1998
+ prompts-gpt run [-f <path>] [--agent <name>] [--model <name>] [--timeout <seconds>] [--dry-run]
1502
1999
 
1503
2000
  Why use it:
1504
- Executes one local prompt file with the built-in provider adapter runtime and writes run artifacts. If \`--prompt-file\` is omitted, the command uses the configured default prompt.
2001
+ Executes one local prompt file with the built-in provider adapter runtime and writes run artifacts.
2002
+ If \`--prompt-file\` is omitted and only one prompt exists locally, it is auto-selected.
1505
2003
 
1506
2004
  Options:
1507
- --prompt-file <path> Optional explicit prompt Markdown or text file.
2005
+ -f, --prompt-file <path> Optional explicit prompt Markdown or text file.
1508
2006
  --agent <name> Orchestration profile. Default from ${DEFAULT_RUN_CONFIG_PATH} or router.
1509
2007
  --model <name> Optional model override for the selected provider.
1510
2008
  --timeout <seconds> Execution timeout in seconds.
@@ -1514,6 +2012,8 @@ Options:
1514
2012
  --no-approve-mcps Disable Cursor MCP auto-approval flag.
1515
2013
  --background Run as a background agent (Cursor cloud agent mode).
1516
2014
  --permission-mode <mode> Claude Code permission mode. Default: acceptEdits.
2015
+ --dry-run Preview the execution parameters without actually running.
2016
+ --non-interactive Skip all interactive prompts (same as CI mode).
1517
2017
  --json Print machine-readable JSON output.
1518
2018
  --cwd <path> Project directory.
1519
2019
  --help Show this command help.
@@ -1523,7 +2023,7 @@ Options:
1523
2023
  return `prompts-gpt run-batch
1524
2024
 
1525
2025
  Usage:
1526
- prompts-gpt run-batch [--manifest <path> | --prompt-files <a,b,c>] [--agent codex|cursor|claude|copilot|router] [--model <name>] [--timeout <seconds>] [--artifacts-dir <path>] [--json] [--cwd <path>]
2026
+ prompts-gpt run-batch [--manifest <path> | --prompt-files <a,b,c>] [--agent <name>] [--model <name>] [--timeout <seconds>]
1527
2027
 
1528
2028
  Why use it:
1529
2029
  Executes multiple prompt files in sequence using the configured prompt source, a manifest, or an explicit file list.
@@ -1538,25 +2038,31 @@ Options:
1538
2038
  --json Print machine-readable JSON output.
1539
2039
  --cwd <path> Project directory.
1540
2040
  --help Show this command help.
2041
+
2042
+ Examples:
2043
+ prompts-gpt run-batch --manifest .prompts-gpt/manifest.json
2044
+ prompts-gpt run-batch --prompt-files .prompts-gpt/review.md,.prompts-gpt/tests.md
1541
2045
  `;
1542
2046
  }
1543
2047
  if (command === "sweep") {
1544
2048
  return `prompts-gpt sweep
1545
2049
 
1546
2050
  Usage:
1547
- prompts-gpt sweep [--prompt-file <path>] [--agent codex|cursor|claude|copilot|router] [--model <name>] [--iterations <n>] [--iteration-timeout <seconds>] [--max-retries <n>] [--phase <name>] [--artifacts-dir <path>] [--run-id <id>] [--sandbox <mode>] [--no-approve-mcps] [--background] [--permission-mode <mode>] [--max-run-dirs <n>] [--summary-lines <n>] [--dry-run] [--json] [--cwd <path>]
2051
+ prompts-gpt sweep [-f <path>] [-n <count>] [--agent <name>] [--model <name>] [--dry-run]
1548
2052
 
1549
2053
  Why use it:
1550
- Replaces the bash sweep scripts with a generic multi-iteration execution engine.
1551
2054
  Runs the same prompt N times, feeding each iteration's summary into the next.
1552
- Includes pre-flight checks, safety guards, SIGTERM handling, progress monitoring,
1553
- and a structured sweep manifest.
2055
+ Includes pre-flight checks, safety guards, SIGTERM handling, and progress monitoring.
2056
+
2057
+ When --prompt-file is omitted and only one .prompts-gpt/sweeps/*.md file exists,
2058
+ it is auto-selected. The iteration count defaults to the \`iterations:\` value
2059
+ in the sweep file's YAML frontmatter (or 1 if not specified).
1554
2060
 
1555
2061
  Options:
1556
- --prompt-file <path> Prompt file to sweep. Reads from config if omitted.
2062
+ -f, --prompt-file <path> Prompt file to sweep. Auto-detects local sweeps if omitted.
2063
+ -n, --iterations <n> Number of iterations. Default: from frontmatter or 1.
1557
2064
  --agent <name> Orchestration profile. Default from config or router.
1558
2065
  --model <name> Model override for the selected provider.
1559
- --iterations <n> Number of sweep iterations. Default: 1
1560
2066
  --iteration-timeout <secs> Timeout per iteration in seconds. Default: 5400 (90 min)
1561
2067
  --max-retries <n> Max retries per iteration on spawn/timeout. Default: 2
1562
2068
  --phase <name> Optional phase label injected into the prompt.
@@ -1569,9 +2075,21 @@ Options:
1569
2075
  --max-run-dirs <n> Max artifact directories to keep. Default: 20
1570
2076
  --summary-lines <n> Lines of summary to extract per iteration. Default: 40
1571
2077
  --dry-run Preview what the sweep would do without executing.
2078
+ --non-interactive Skip all interactive prompts (same as CI mode).
1572
2079
  --json Print machine-readable JSON output.
1573
2080
  --cwd <path> Project directory.
1574
2081
  --help Show this command help.
2082
+
2083
+ Environment:
2084
+ PROMPTS_GPT_NON_INTERACTIVE=1 Skip interactive prompts.
2085
+ NO_COLOR=1 Disable colored output.
2086
+
2087
+ Examples:
2088
+ prompts-gpt sweep # interactive picker
2089
+ prompts-gpt sweep -f .prompts-gpt/sweeps/sdk-hardening.md # explicit file
2090
+ prompts-gpt sweep -f .prompts-gpt/sweeps/design.md -n 5 # 5 iterations
2091
+ prompts-gpt sweep --dry-run # preview without running
2092
+ prompts-gpt sweep --non-interactive # skip interactive prompts
1575
2093
  `;
1576
2094
  }
1577
2095
  if (command === "list") {
@@ -1653,6 +2171,10 @@ Why use it:
1653
2171
  into your local workspace. Replaces the multi-step init + sync + setup flow
1654
2172
  for users who manage their prompt library in the Prompts Studio web app.
1655
2173
 
2174
+ Data privacy:
2175
+ Downloads prompts and project config from your prompts-gpt.com project.
2176
+ No local files or repo content are uploaded.
2177
+
1656
2178
  Options:
1657
2179
  --agent <targets> Comma-separated agent targets. Default: all
1658
2180
  --out <dir> Output directory. Default: .prompts-gpt
@@ -1663,6 +2185,22 @@ Options:
1663
2185
  --api-url <url> Override API URL.
1664
2186
  --cwd <path> Project directory.
1665
2187
  --help Show this command help.
2188
+ `;
2189
+ }
2190
+ if (command === "quickstart") {
2191
+ return `prompts-gpt quickstart
2192
+
2193
+ Usage:
2194
+ prompts-gpt quickstart [--json] [--cwd <path>]
2195
+
2196
+ Why use it:
2197
+ Walks through setup in one command — checks credentials, creates config, detects providers,
2198
+ and shows runnable commands. The fastest path from install to first run.
2199
+
2200
+ Options:
2201
+ --json Print machine-readable JSON output.
2202
+ --cwd <path> Project directory.
2203
+ --help Show this command help.
1666
2204
  `;
1667
2205
  }
1668
2206
  if (command === "version") {
@@ -1684,6 +2222,233 @@ Why use it:
1684
2222
  Shows focused usage for the exact command you are trying to run so shell users do not have to scan the full command list.
1685
2223
  `;
1686
2224
  }
2225
+ // ── Interactive TTY selection helpers ─────────────────────────────────────────
2226
+ function supportsColor() {
2227
+ if (process.env.NO_COLOR || process.env.TERM === "dumb")
2228
+ return false;
2229
+ if (process.env.FORCE_COLOR)
2230
+ return true;
2231
+ if (process.platform === "win32") {
2232
+ const osRelease = (process.env.OS_VERSION || "").toLowerCase();
2233
+ if (osRelease.includes("windows_nt") || process.env.WT_SESSION || process.env.TERM_PROGRAM)
2234
+ return true;
2235
+ return Boolean(process.stdout.isTTY && (process.stdout.hasColors?.() ?? false));
2236
+ }
2237
+ return Boolean(process.stdout.isTTY);
2238
+ }
2239
+ function colorize(text, code) {
2240
+ return supportsColor() ? `${code}${text}\x1b[0m` : text;
2241
+ }
2242
+ function isTTYInteractive(flags) {
2243
+ if (!process.stdin.isTTY || !process.stdout.isTTY)
2244
+ return false;
2245
+ if (flags && Boolean(flags["non-interactive"]))
2246
+ return false;
2247
+ if (process.env.CI || process.env.CONTINUOUS_INTEGRATION || process.env.GITHUB_ACTIONS ||
2248
+ process.env.GITLAB_CI || process.env.CIRCLECI || process.env.JENKINS_URL ||
2249
+ process.env.BUILDKITE || process.env.TRAVIS || process.env.TF_BUILD ||
2250
+ process.env.CODEBUILD_BUILD_ID)
2251
+ return false;
2252
+ if (process.env.PROMPTS_GPT_NON_INTERACTIVE === "1" || process.env.PROMPTS_GPT_NON_INTERACTIVE === "true")
2253
+ return false;
2254
+ return true;
2255
+ }
2256
+ function interactiveSelect(prompt, options) {
2257
+ if (options.length === 0) {
2258
+ return Promise.reject(new CliError("No options available for selection.", CLI_EXIT_CODES.usage));
2259
+ }
2260
+ if (options.length === 1) {
2261
+ return Promise.resolve(options[0].value);
2262
+ }
2263
+ return new Promise((resolve, reject) => {
2264
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
2265
+ reject(new CliError("Interactive selection requires a TTY.", CLI_EXIT_CODES.usage));
2266
+ return;
2267
+ }
2268
+ const stdin = process.stdin;
2269
+ const stdout = process.stdout;
2270
+ let cursor = 0;
2271
+ let escBuf = "";
2272
+ const maxVisible = Math.min(options.length, (process.stdout.rows ?? 24) - 3);
2273
+ const formatLine = (i, selected) => {
2274
+ const num = i < 9 ? `${i + 1}` : " ";
2275
+ const prefix = selected ? colorize("❯", "\x1b[36m") : " ";
2276
+ const label = selected ? colorize(options[i].label, "\x1b[1m") : options[i].label;
2277
+ return ` ${prefix} ${num}. ${label}`;
2278
+ };
2279
+ const writeInitial = () => {
2280
+ stdout.write(`${prompt}\n`);
2281
+ for (let i = 0; i < maxVisible; i++) {
2282
+ stdout.write(`${formatLine(i, i === cursor)}\n`);
2283
+ }
2284
+ if (options.length > maxVisible) {
2285
+ stdout.write(` ... ${options.length - maxVisible} more (scroll with arrows)\n`);
2286
+ }
2287
+ };
2288
+ const render = () => {
2289
+ const lines = maxVisible + 1 + (options.length > maxVisible ? 1 : 0);
2290
+ if (supportsColor()) {
2291
+ stdout.write(`\x1b[${lines}A\x1b[J`);
2292
+ }
2293
+ else {
2294
+ stdout.write("\n".repeat(2));
2295
+ }
2296
+ stdout.write(`${prompt}\n`);
2297
+ const scrollStart = Math.max(0, Math.min(cursor - Math.floor(maxVisible / 2), options.length - maxVisible));
2298
+ for (let vi = 0; vi < maxVisible; vi++) {
2299
+ const i = scrollStart + vi;
2300
+ stdout.write(`${formatLine(i, i === cursor)}\n`);
2301
+ }
2302
+ if (options.length > maxVisible) {
2303
+ stdout.write(` ... showing ${scrollStart + 1}-${scrollStart + maxVisible} of ${options.length}\n`);
2304
+ }
2305
+ };
2306
+ writeInitial();
2307
+ stdin.setEncoding("utf8");
2308
+ try {
2309
+ if (typeof stdin.setRawMode === "function")
2310
+ stdin.setRawMode(true);
2311
+ }
2312
+ catch {
2313
+ reject(new CliError("Cannot enable raw mode for interactive selection. Use --non-interactive or pass flags directly.", CLI_EXIT_CODES.usage));
2314
+ return;
2315
+ }
2316
+ stdin.resume();
2317
+ const cleanup = () => {
2318
+ stdin.removeListener("data", onData);
2319
+ try {
2320
+ if (typeof stdin.setRawMode === "function")
2321
+ stdin.setRawMode(false);
2322
+ }
2323
+ catch { /* ignore */ }
2324
+ stdin.pause();
2325
+ };
2326
+ const onData = (data) => {
2327
+ for (let ci = 0; ci < data.length; ci++) {
2328
+ const ch = data[ci];
2329
+ if (escBuf.length > 0) {
2330
+ escBuf += ch;
2331
+ if (escBuf.length === 2 && ch === "[")
2332
+ continue;
2333
+ if (escBuf.length >= 3) {
2334
+ if (escBuf === "\x1b[A" || escBuf === "\x1b[k") {
2335
+ cursor = cursor > 0 ? cursor - 1 : options.length - 1;
2336
+ render();
2337
+ }
2338
+ else if (escBuf === "\x1b[B" || escBuf === "\x1b[j") {
2339
+ cursor = cursor < options.length - 1 ? cursor + 1 : 0;
2340
+ render();
2341
+ }
2342
+ else if (escBuf === "\x1b[H") {
2343
+ cursor = 0;
2344
+ render();
2345
+ }
2346
+ else if (escBuf === "\x1b[F") {
2347
+ cursor = options.length - 1;
2348
+ render();
2349
+ }
2350
+ escBuf = "";
2351
+ }
2352
+ continue;
2353
+ }
2354
+ if (ch === "\x1b") {
2355
+ escBuf = "\x1b";
2356
+ continue;
2357
+ }
2358
+ if (ch === "\x03") {
2359
+ cleanup();
2360
+ stdout.write("\n");
2361
+ process.exit(130);
2362
+ return;
2363
+ }
2364
+ if (ch === "\r" || ch === "\n") {
2365
+ cleanup();
2366
+ stdout.write("\n");
2367
+ resolve(options[cursor].value);
2368
+ return;
2369
+ }
2370
+ if (ch === "k") {
2371
+ cursor = cursor > 0 ? cursor - 1 : options.length - 1;
2372
+ render();
2373
+ }
2374
+ else if (ch === "j") {
2375
+ cursor = cursor < options.length - 1 ? cursor + 1 : 0;
2376
+ render();
2377
+ }
2378
+ else if (ch >= "1" && ch <= "9") {
2379
+ const idx = parseInt(ch, 10) - 1;
2380
+ if (idx < options.length) {
2381
+ cleanup();
2382
+ stdout.write("\n");
2383
+ resolve(options[idx].value);
2384
+ return;
2385
+ }
2386
+ }
2387
+ }
2388
+ };
2389
+ stdin.on("data", onData);
2390
+ });
2391
+ }
2392
+ function interactiveInput(prompt, defaultValue) {
2393
+ return new Promise(async (resolve, reject) => {
2394
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
2395
+ reject(new CliError("Interactive input requires a TTY.", CLI_EXIT_CODES.usage));
2396
+ return;
2397
+ }
2398
+ const { createInterface } = await import("node:readline");
2399
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
2400
+ const suffix = defaultValue ? ` (${defaultValue})` : "";
2401
+ rl.question(`${prompt}${suffix}: `, (answer) => {
2402
+ rl.close();
2403
+ resolve(answer.trim() || defaultValue || "");
2404
+ });
2405
+ });
2406
+ }
2407
+ async function readSweepFrontmatter(filePath) {
2408
+ try {
2409
+ const { readFile: fsRead } = await import("node:fs/promises");
2410
+ const content = await fsRead(filePath, "utf8");
2411
+ let iterations = null;
2412
+ let title = null;
2413
+ const iterMatch = content.match(/iterations:\s*(\d+)/i);
2414
+ if (iterMatch) {
2415
+ const val = parseInt(iterMatch[1], 10);
2416
+ if (val > 0 && val <= 50)
2417
+ iterations = val;
2418
+ }
2419
+ const titleMatch = content.match(/^#\s+(.+)/m);
2420
+ if (titleMatch) {
2421
+ title = titleMatch[1].trim();
2422
+ }
2423
+ return { iterations, title };
2424
+ }
2425
+ catch { /* skip */ }
2426
+ return { iterations: null, title: null };
2427
+ }
2428
+ async function readSweepIterationsFromFrontmatter(filePath) {
2429
+ return (await readSweepFrontmatter(filePath)).iterations;
2430
+ }
2431
+ function buildGenerateInput(flags) {
2432
+ return {
2433
+ goal: getStringFlag(flags, "goal") || "",
2434
+ context: sanitizeGenerateInput(getStringFlag(flags, "context") || ""),
2435
+ constraints: sanitizeGenerateInput(getStringFlag(flags, "constraints") || ""),
2436
+ desiredOutput: getStringFlag(flags, "desired-output") || "A reusable prompt pack saved as Markdown.",
2437
+ tool: getStringFlag(flags, "tool") || "Codex",
2438
+ mode: getStringFlag(flags, "mode") || "implement",
2439
+ artifactType: getStringFlag(flags, "artifact-type") || "prompt-file",
2440
+ includeWebSearch: Boolean(flags["web-search"]),
2441
+ };
2442
+ }
2443
+ function sanitizeGenerateInput(value) {
2444
+ return value.replace(/pgpt_[a-zA-Z0-9]{4,}/g, "pgpt_***").replace(/sk-[a-zA-Z0-9]{20,}/g, "sk-***").replace(/ghp_[a-zA-Z0-9]{36,}/g, "ghp_***");
2445
+ }
2446
+ function printDataTransmissionNotice(command, input) {
2447
+ 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.`);
2448
+ console.log("[notice] Do not include PII, secrets, or confidential data in these flags.");
2449
+ console.log(`[notice] No local files or repo content are uploaded. Only the explicit flag values for \`${command}\` are transmitted.`);
2450
+ console.log("");
2451
+ }
1687
2452
  main().catch((error) => {
1688
2453
  if (error instanceof CliError) {
1689
2454
  console.error(error.message);
@@ -1708,6 +2473,46 @@ main().catch((error) => {
1708
2473
  console.error(msg.replace(/pgpt_[a-zA-Z0-9]{4,}/g, "pgpt_***"));
1709
2474
  process.exitCode = CLI_EXIT_CODES.general;
1710
2475
  });
2476
+ async function extractRunDiagnostics(logFile, provider, model) {
2477
+ const diagnostics = [];
2478
+ try {
2479
+ const { readFile: fsReadFile } = await import("node:fs/promises");
2480
+ const log = await fsReadFile(logFile, "utf8");
2481
+ const errorLines = log.split("\n").filter((line) => line.startsWith("ERROR:") || line.includes('"error"'));
2482
+ for (const line of errorLines.slice(0, 5)) {
2483
+ if (line.includes("not supported")) {
2484
+ const modelMatch = line.match(/The '([^']+)' model is not supported/);
2485
+ const badModel = modelMatch?.[1] ?? model;
2486
+ diagnostics.push(`Model "${badModel}" is not supported by ${provider} with your account.`);
2487
+ diagnostics.push(`Fix: prompts-gpt run --model o4-mini`);
2488
+ diagnostics.push(`Or: prompts-gpt run --agent claude`);
2489
+ break;
2490
+ }
2491
+ if (line.includes("unauthorized") || line.includes("authentication")) {
2492
+ diagnostics.push(`Authentication failed for ${provider}. Check your ${provider} account login.`);
2493
+ diagnostics.push(`Fix: Run the ${provider} CLI directly to verify auth: ${provider} --version`);
2494
+ break;
2495
+ }
2496
+ if (line.includes("rate_limit") || line.includes("429")) {
2497
+ diagnostics.push(`Rate limited by ${provider}. Wait and try again.`);
2498
+ break;
2499
+ }
2500
+ }
2501
+ if (diagnostics.length === 0 && errorLines.length > 0) {
2502
+ const firstError = errorLines[0].slice(0, 200);
2503
+ diagnostics.push(`Provider error: ${firstError}`);
2504
+ }
2505
+ if (diagnostics.length === 0) {
2506
+ diagnostics.push(`${provider} exited with code 1. Check the log for details:`);
2507
+ diagnostics.push(` cat ${logFile}`);
2508
+ diagnostics.push(`Try a different provider: prompts-gpt run --agent claude`);
2509
+ }
2510
+ }
2511
+ catch {
2512
+ diagnostics.push(`Could not read log file. Check: ${logFile}`);
2513
+ }
2514
+ return diagnostics;
2515
+ }
1711
2516
  function warnOnConfigIssues(config) {
1712
2517
  for (const w of config.configWarnings) {
1713
2518
  console.error(`[config warning] ${w}`);
@@ -1728,4 +2533,21 @@ function formatApiError(error) {
1728
2533
  }
1729
2534
  return lines.join("\n");
1730
2535
  }
2536
+ function formatTokenUsage(usage) {
2537
+ const parts = [
2538
+ `total=${usage.totalTokens.toLocaleString()}`,
2539
+ `input=${usage.inputTokens.toLocaleString()}`,
2540
+ `output=${usage.outputTokens.toLocaleString()}`,
2541
+ ];
2542
+ if (usage.reasoningTokens > 0) {
2543
+ parts.push(`reasoning=${usage.reasoningTokens.toLocaleString()}`);
2544
+ }
2545
+ if (usage.cacheReadTokens > 0) {
2546
+ parts.push(`cache-read=${usage.cacheReadTokens.toLocaleString()}`);
2547
+ }
2548
+ if (usage.cacheWriteTokens > 0) {
2549
+ parts.push(`cache-write=${usage.cacheWriteTokens.toLocaleString()}`);
2550
+ }
2551
+ return parts.join(" | ");
2552
+ }
1731
2553
  //# sourceMappingURL=cli.js.map