@prompts-gpt/client 0.2.2 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js 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, PROMPTS_GPT_CREDENTIALS_FILE, PromptsGptApiError, PromptsGptClient, SUPPORTED_AGENT_TARGETS, loadLocalCredentials, saveLocalCredentials, syncPrompts, writeAgentFiles, 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 = ["init", "project", "pull", "generate", "sync", "install-agents", "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,11 +27,18 @@ 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();
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
+ `);
31
39
  return;
32
40
  }
33
- const normalizedArgv = normalizeLegacyFlags(argv);
34
- const first = normalizedArgv[0];
41
+ const first = argv[0];
35
42
  if (first === "--help") {
36
43
  printHelp();
37
44
  return;
@@ -41,11 +48,11 @@ async function main() {
41
48
  return;
42
49
  }
43
50
  if (first === "help") {
44
- handleHelpCommand(normalizedArgv.slice(1));
51
+ handleHelpCommand(argv.slice(1));
45
52
  return;
46
53
  }
47
54
  if (first === "version") {
48
- if (normalizedArgv.length > 1) {
55
+ if (argv.length > 1) {
49
56
  throw new CliError("The `version` command does not accept additional arguments.", CLI_EXIT_CODES.usage, {
50
57
  helpCommand: "version",
51
58
  });
@@ -62,7 +69,7 @@ async function main() {
62
69
  helpCommand: "help",
63
70
  });
64
71
  }
65
- const flags = parseCommandFlags(command, normalizedArgv.slice(1));
72
+ const flags = parseCommandFlags(command, argv.slice(1));
66
73
  if (Boolean(flags.help)) {
67
74
  printHelp(command);
68
75
  return;
@@ -70,8 +77,714 @@ async function main() {
70
77
  await runCommand(command, flags);
71
78
  }
72
79
  async function runCommand(command, flags) {
80
+ if (command === "providers") {
81
+ const cwd = getResolvedCwd(flags);
82
+ const providers = await detectProviders(cwd);
83
+ if (Boolean(flags.json)) {
84
+ console.log(JSON.stringify({ cwd, providers }, null, 2));
85
+ return;
86
+ }
87
+ console.log(`Workspace: ${cwd}`);
88
+ for (const provider of providers) {
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
+ }
94
+ }
95
+ return;
96
+ }
97
+ if (command === "setup") {
98
+ const cwd = getResolvedCwd(flags);
99
+ const providerOrder = getStringFlag(flags, "provider-order")
100
+ ?.split(",")
101
+ .map((item) => item.trim())
102
+ .filter(Boolean);
103
+ validateProviderOrderFlag(providerOrder);
104
+ const promptFile = getStringFlag(flags, "prompt-file");
105
+ const promptFiles = getStringFlag(flags, "prompt-files")
106
+ ?.split(",")
107
+ .map((item) => item.trim())
108
+ .filter(Boolean);
109
+ const result = await initRunConfig({
110
+ cwd,
111
+ promptFile,
112
+ manifestPath: getStringFlag(flags, "manifest"),
113
+ promptFiles,
114
+ promptDir: getStringFlag(flags, "prompt-dir"),
115
+ defaultAgent: resolveRunAgent(flags, "router"),
116
+ providerOrder,
117
+ artifactsDir: getStringFlag(flags, "artifacts-dir"),
118
+ timeoutSeconds: parsePositiveIntFlag(getStringFlag(flags, "timeout"), "timeout"),
119
+ retryCount: parseNonNegativeIntFlag(getStringFlag(flags, "retry-count"), "retry-count"),
120
+ disallowDestructiveGit: Boolean(flags["allow-destructive-git"]) ? false : true,
121
+ modelOverrides: {
122
+ codex: getStringFlag(flags, "codex-model"),
123
+ cursor: getStringFlag(flags, "cursor-model"),
124
+ claude: getStringFlag(flags, "claude-model"),
125
+ copilot: getStringFlag(flags, "copilot-model"),
126
+ },
127
+ overwrite: Boolean(flags.overwrite),
128
+ });
129
+ if (Boolean(flags.json)) {
130
+ console.log(JSON.stringify(result, null, 2));
131
+ return;
132
+ }
133
+ console.log(`Created run config: ${result.configPath}`);
134
+ console.log(`Config: ${result.configPath}`);
135
+ console.log(`Agent: ${result.config.defaultAgent ?? "router"} | Providers: ${(result.config.providerOrder ?? []).join(", ")}`);
136
+ if (result.sourceSummary.defaultPromptFile) {
137
+ console.log(`Default prompt file: ${result.sourceSummary.defaultPromptFile}`);
138
+ console.log(`Default prompt: ${result.sourceSummary.defaultPromptFile}`);
139
+ }
140
+ const availableCount = result.providerSummary.filter((p) => p.available).length;
141
+ console.log(`Providers: ${availableCount}/${result.providerSummary.length} available`);
142
+ if (availableCount === 0) {
143
+ console.log("⚠ No provider CLIs detected. Install codex, cursor agent, claude, or copilot.");
144
+ }
145
+ const setupAssets = await discoverWorkspaceAssets(cwd);
146
+ console.log("Next steps:");
147
+ console.log(` prompts-gpt run${result.sourceSummary.defaultPromptFile ? "" : " --prompt-file <path>"}`);
148
+ if (setupAssets.sweeps.length > 0) {
149
+ console.log(` prompts-gpt sweep — run a multi-iteration sweep (${setupAssets.sweeps.length} found)`);
150
+ }
151
+ console.log(" prompts-gpt list — see all runnable assets");
152
+ console.log(" prompts-gpt status — check workspace readiness");
153
+ console.log(" prompts-gpt validate — validate your config");
154
+ return;
155
+ }
156
+ if (command === "doctor") {
157
+ const cwd = getResolvedCwd(flags);
158
+ const report = await doctor(cwd);
159
+ if (Boolean(flags.json)) {
160
+ console.log(JSON.stringify(report, null, 2));
161
+ return;
162
+ }
163
+ console.log(`Workspace: ${report.cwd}`);
164
+ console.log(`Node: ${report.nodeVersion} | OS: ${report.osPlatform}/${report.osArch} | CPUs: ${report.cpuCount}`);
165
+ console.log(`Config: ${report.configFound ? "found" : "not found"} (${report.configPath})`);
166
+ for (const provider of report.providers) {
167
+ console.log(`${provider.provider}: ${provider.available ? "available" : "missing"} | bin=${provider.bin}${provider.version ? ` | version=${provider.version}` : ""}`);
168
+ if (!provider.available) {
169
+ console.log(` hint: ${provider.installHint}`);
170
+ }
171
+ }
172
+ for (const note of report.notes) {
173
+ console.log(`note: ${note}`);
174
+ }
175
+ return;
176
+ }
177
+ if (command === "list") {
178
+ const cwd = getResolvedCwd(flags);
179
+ const assets = await discoverWorkspaceAssets(cwd);
180
+ if (Boolean(flags.json)) {
181
+ console.log(JSON.stringify(assets, null, 2));
182
+ return;
183
+ }
184
+ console.log(`Workspace: ${cwd}`);
185
+ console.log("");
186
+ const providers = await detectProviders(cwd);
187
+ const availableProviderNames = providers.filter((p) => p.available).map((p) => p.provider);
188
+ if (assets.prompts.length > 0) {
189
+ console.log(`Prompt packs (${assets.prompts.length}):`);
190
+ for (const p of assets.prompts) {
191
+ const providerHint = availableProviderNames.length > 0
192
+ ? ` (run with: ${availableProviderNames[0]})`
193
+ : "";
194
+ console.log(` ${p.slug} — ${p.title} [${p.source}]${providerHint}`);
195
+ console.log(` file: .prompts-gpt/${p.file}`);
196
+ console.log(` run: prompts-gpt run --prompt-file .prompts-gpt/${p.file}`);
197
+ }
198
+ console.log("");
199
+ }
200
+ else {
201
+ console.log("Prompt packs: none found");
202
+ console.log(" Run `prompts-gpt sync` to pull prompt packs, or `prompts-gpt pull` to download them.");
203
+ console.log("");
204
+ }
205
+ if (assets.sweeps.length > 0) {
206
+ console.log(`Sweep prompts (${assets.sweeps.length}):`);
207
+ for (const s of assets.sweeps) {
208
+ const iterCount = await readSweepIterationsFromFrontmatter(path.resolve(cwd, s.file));
209
+ const iterHint = iterCount ? ` (${iterCount} iterations)` : "";
210
+ const iterFlag = iterCount ? ` --iterations ${iterCount}` : "";
211
+ console.log(` ${s.name}${iterHint}`);
212
+ console.log(` file: ${s.file}`);
213
+ console.log(` sweep: prompts-gpt sweep --prompt-file ${s.file}${iterFlag}`);
214
+ }
215
+ console.log("");
216
+ }
217
+ else {
218
+ console.log("Sweep prompts: none found");
219
+ console.log(" Create .prompts-gpt/sweeps/<name>.md to add sweep prompts.");
220
+ console.log("");
221
+ }
222
+ if (assets.agents.length > 0) {
223
+ console.log(`Agent integrations (${assets.agents.length}):`);
224
+ for (const a of assets.agents) {
225
+ console.log(` ${a.target} — ${a.file}`);
226
+ }
227
+ console.log("");
228
+ }
229
+ else {
230
+ console.log("Agent integrations: none synced");
231
+ console.log(" Run `prompts-gpt sync` to generate agent files.");
232
+ console.log("");
233
+ }
234
+ console.log(`Config: ${assets.configFound ? "found" : "not found — run `prompts-gpt setup`"}`);
235
+ console.log(`Manifest: ${assets.manifestFound ? "found" : "not found — run `prompts-gpt sync`"}`);
236
+ console.log(`Credentials: ${assets.credentialsFound ? "found" : "not found — run `prompts-gpt init --token <token>`"}`);
237
+ return;
238
+ }
239
+ if (command === "status") {
240
+ const cwd = getResolvedCwd(flags);
241
+ const assets = await discoverWorkspaceAssets(cwd);
242
+ const providers = await detectProviders(cwd);
243
+ const availableProviders = providers.filter((p) => p.available);
244
+ if (Boolean(flags.json)) {
245
+ console.log(JSON.stringify({ cwd, assets, providers }, null, 2));
246
+ return;
247
+ }
248
+ console.log(`Workspace: ${cwd}`);
249
+ console.log("");
250
+ console.log("Readiness:");
251
+ console.log(` Credentials: ${assets.credentialsFound ? "✓" : "✗ — run \`prompts-gpt init --token <token>\`"}`);
252
+ console.log(` Config: ${assets.configFound ? "✓" : "✗ — run \`prompts-gpt setup\`"}`);
253
+ console.log(` Manifest: ${assets.manifestFound ? "✓" : "✗ — run \`prompts-gpt sync\`"}`);
254
+ const cloudCount = assets.prompts.filter((p) => p.source === "library" || p.source === "generated").length;
255
+ const localCount = assets.prompts.filter((p) => p.source === "local").length;
256
+ const promptSummary = assets.prompts.length > 0
257
+ ? `✓ (${assets.prompts.length}${cloudCount > 0 ? `, ${cloudCount} synced` : ""}${localCount > 0 ? `, ${localCount} local-only` : ""})`
258
+ : "✗ — none found";
259
+ console.log(` Prompts: ${promptSummary}`);
260
+ console.log(` Sweeps: ${assets.sweeps.length > 0 ? `✓ (${assets.sweeps.length})` : "— none found"}`);
261
+ console.log(` Agents: ${assets.agents.length > 0 ? `✓ (${assets.agents.length} targets)` : "✗ — run \`prompts-gpt sync\`"}`);
262
+ if (availableProviders.length > 0) {
263
+ console.log(` Providers: ✓`);
264
+ for (const p of availableProviders) {
265
+ console.log(` ${p.provider}: ${p.bin} | model: ${p.modelDefault}${p.version ? ` | ${p.version}` : ""}`);
266
+ }
267
+ }
268
+ else {
269
+ console.log(" Providers: ✗ — no CLI found");
270
+ }
271
+ console.log("");
272
+ if (assets.prompts.length > 0 && availableProviders.length > 0) {
273
+ console.log("Ready to run:");
274
+ console.log(` prompts-gpt run --prompt-file .prompts-gpt/${assets.prompts[0].file}`);
275
+ if (assets.sweeps.length > 0) {
276
+ for (const s of assets.sweeps) {
277
+ console.log(` prompts-gpt sweep --prompt-file ${s.file}`);
278
+ }
279
+ }
280
+ }
281
+ else {
282
+ console.log("Not ready yet. Quick fix:");
283
+ console.log(" prompts-gpt quickstart");
284
+ console.log("");
285
+ console.log("Or step by step:");
286
+ let step = 1;
287
+ if (!assets.credentialsFound)
288
+ console.log(` ${step++}. prompts-gpt init --token <project-token>`);
289
+ if (assets.prompts.length === 0)
290
+ console.log(` ${step++}. prompts-gpt sync`);
291
+ if (availableProviders.length === 0)
292
+ console.log(` ${step++}. Install a provider CLI (codex, cursor agent, claude, copilot)`);
293
+ if (!assets.configFound)
294
+ console.log(` ${step++}. prompts-gpt setup`);
295
+ }
296
+ return;
297
+ }
298
+ if (command === "validate") {
299
+ const cwd = getResolvedCwd(flags);
300
+ const result = await validateRunConfig(cwd);
301
+ if (Boolean(flags.json)) {
302
+ console.log(JSON.stringify(result, null, 2));
303
+ return;
304
+ }
305
+ console.log(`Config: ${result.configPath}`);
306
+ console.log(`Valid: ${result.valid ? "yes" : "no"}`);
307
+ for (const e of result.errors) {
308
+ console.log(` error: ${e}`);
309
+ }
310
+ for (const w of result.warnings) {
311
+ console.log(` warning: ${w}`);
312
+ }
313
+ if (result.valid && result.errors.length === 0 && result.warnings.length === 0) {
314
+ console.log(" No issues found.");
315
+ }
316
+ return;
317
+ }
318
+ if (command === "run") {
319
+ const cwd = getResolvedCwd(flags);
320
+ const promptFile = getStringFlag(flags, "prompt-file");
321
+ const config = await loadRunConfig(cwd);
322
+ warnOnConfigIssues(config);
323
+ if (!promptFile && !Boolean(flags.json)) {
324
+ if (!config.defaultPromptFile && config.batchDefaults.promptFiles.length === 0 && !config.batchDefaults.manifestPath) {
325
+ const assets = await discoverWorkspaceAssets(cwd);
326
+ if (assets.prompts.length > 0 || assets.sweeps.length > 0) {
327
+ console.log("No default prompt configured. No default prompt file configured. Available options:\n");
328
+ if (assets.prompts.length > 0) {
329
+ console.log("Prompt packs:");
330
+ for (const p of assets.prompts) {
331
+ console.log(` prompts-gpt run --prompt-file .prompts-gpt/${p.file}`);
332
+ }
333
+ }
334
+ if (assets.sweeps.length > 0) {
335
+ console.log("\nSweep prompts (use `sweep` command):");
336
+ for (const s of assets.sweeps) {
337
+ console.log(` prompts-gpt sweep --prompt-file ${s.file}`);
338
+ }
339
+ }
340
+ console.log("\nOr set a default: prompts-gpt setup --prompt-file <path>");
341
+ return;
342
+ }
343
+ }
344
+ }
345
+ const agent = resolveRunAgent(flags, config.defaultAgent);
346
+ if (Boolean(flags["dry-run"])) {
347
+ const providers = await detectProviders(cwd);
348
+ const resolvedProvider = resolveRunProvider(agent, providers, config.providerOrder);
349
+ const resolvedModel = getStringFlag(flags, "model")?.trim() || config.modelOverrides[resolvedProvider]?.trim() || "";
350
+ let resolvedPrompt = promptFile || getStringFlag(flags, "prompt-file") || config.defaultPromptFile || null;
351
+ if (!resolvedPrompt) {
352
+ try {
353
+ const { resolveDefaultPromptFile: resolvePf } = await import("./index.js");
354
+ resolvedPrompt = await resolvePf(cwd, config);
355
+ }
356
+ catch {
357
+ resolvedPrompt = null;
358
+ }
359
+ }
360
+ const agentStr = getStringFlag(flags, "agent") ? agent : `${agent} (auto-selected)`;
361
+ console.log("[dry-run] Would execute:");
362
+ console.log(` Agent: ${agentStr}`);
363
+ console.log(` Provider: ${resolvedProvider}`);
364
+ console.log(` Model: ${resolvedModel || "(default)"}`);
365
+ console.log(` Prompt: ${resolvedPrompt || "(none found — pass --prompt-file)"}`);
366
+ console.log(` Timeout: ${getStringFlag(flags, "timeout") || config.timeoutSeconds}s`);
367
+ return;
368
+ }
369
+ const modelFlag = getStringFlag(flags, "model");
370
+ if (modelFlag && !Boolean(flags.json) && agent !== "router") {
371
+ const providers = await detectProviders(cwd);
372
+ const resolvedProvider = resolveRunProvider(agent, providers, config.providerOrder);
373
+ const mismatchWarning = warnModelProviderMismatch(resolvedProvider, modelFlag);
374
+ if (mismatchWarning) {
375
+ console.error(`[warning] ${mismatchWarning}`);
376
+ }
377
+ }
378
+ const result = await runPrompt({
379
+ cwd,
380
+ promptFile,
381
+ agent,
382
+ model: modelFlag,
383
+ timeoutSeconds: parsePositiveIntFlag(getStringFlag(flags, "timeout"), "timeout"),
384
+ artifactsDir: getStringFlag(flags, "artifacts-dir"),
385
+ runId: getStringFlag(flags, "run-id"),
386
+ approveMcps: !Boolean(flags["no-approve-mcps"]),
387
+ sandboxMode: getStringFlag(flags, "sandbox"),
388
+ background: Boolean(flags.background),
389
+ permissionMode: getStringFlag(flags, "permission-mode"),
390
+ });
391
+ if (Boolean(flags.json)) {
392
+ console.log(JSON.stringify(result, null, 2));
393
+ return;
394
+ }
395
+ const agentLabel = getStringFlag(flags, "agent") ? result.provider : `${result.provider} (auto — first available from provider order)`;
396
+ console.log(`Run ID: ${result.runId}`);
397
+ console.log(`Provider: ${agentLabel} | Model: ${result.model}`);
398
+ console.log(`Exit code: ${result.exitCode}`);
399
+ console.log(`Duration: ${formatDuration(result.durationMs)}`);
400
+ if (hasTokenUsage(result.tokenUsage)) {
401
+ console.log(`Tokens: ${formatTokenUsage(result.tokenUsage)}`);
402
+ }
403
+ console.log(`Run dir: ${result.runDir}`);
404
+ console.log(`Summary: ${result.summaryFile}`);
405
+ console.log(`Log: ${result.logFile}`);
406
+ if (result.exitCode === 0) {
407
+ console.log("");
408
+ console.log(`View results: cat ${result.summaryFile}`);
409
+ }
410
+ if (result.exitCode !== 0) {
411
+ const diagnostics = await extractRunDiagnostics(result.logFile, result.provider, result.model);
412
+ if (diagnostics.length > 0) {
413
+ console.log("");
414
+ console.log("Diagnostics:");
415
+ for (const d of diagnostics) {
416
+ console.log(` ${d}`);
417
+ }
418
+ }
419
+ }
420
+ return;
421
+ }
422
+ if (command === "run-batch") {
423
+ const cwd = getResolvedCwd(flags);
424
+ const promptFiles = getStringFlag(flags, "prompt-files")
425
+ ?.split(",")
426
+ .map((item) => item.trim())
427
+ .filter(Boolean);
428
+ const config = await loadRunConfig(cwd);
429
+ warnOnConfigIssues(config);
430
+ if (!promptFiles && !getStringFlag(flags, "manifest") && !Boolean(flags.json)) {
431
+ if (!config.batchDefaults.manifestPath && config.batchDefaults.promptFiles.length === 0) {
432
+ const assets = await discoverWorkspaceAssets(cwd);
433
+ if (assets.prompts.length > 0) {
434
+ console.log("No batch source configured. Available prompt packs:\n");
435
+ const fileList = assets.prompts.map((p) => `.prompts-gpt/${p.file}`).join(",");
436
+ console.log(` prompts-gpt run-batch --prompt-files ${fileList}`);
437
+ if (assets.manifestFound) {
438
+ console.log(` prompts-gpt run-batch --manifest .prompts-gpt/manifest.json`);
439
+ }
440
+ console.log("\nOr configure batch defaults: prompts-gpt setup");
441
+ return;
442
+ }
443
+ }
444
+ }
445
+ const agent = resolveRunAgent(flags, config.defaultAgent);
446
+ const result = await runBatch({
447
+ cwd,
448
+ manifestPath: getStringFlag(flags, "manifest"),
449
+ promptFiles,
450
+ agent,
451
+ model: getStringFlag(flags, "model"),
452
+ timeoutSeconds: parsePositiveIntFlag(getStringFlag(flags, "timeout"), "timeout"),
453
+ artifactsDir: getStringFlag(flags, "artifacts-dir"),
454
+ });
455
+ if (Boolean(flags.json)) {
456
+ console.log(JSON.stringify(result, null, 2));
457
+ return;
458
+ }
459
+ console.log(`Batch complete: total=${result.total} success=${result.success} failed=${result.failed}`);
460
+ if (hasTokenUsage(result.tokenUsage)) {
461
+ console.log(`Batch tokens: ${formatTokenUsage(result.tokenUsage)}`);
462
+ }
463
+ for (const [idx, run] of result.results.entries()) {
464
+ const status = run.exitCode === 0 ? "ok" : "FAIL";
465
+ const usageStr = hasTokenUsage(run.tokenUsage) ? `, tokens=${run.tokenUsage.totalTokens.toLocaleString()}` : "";
466
+ console.log(` [${idx + 1}/${result.total}] ${status} ${path.basename(run.promptFile)} -> ${run.provider} (exit=${run.exitCode}, ${formatDuration(run.durationMs)}${usageStr})`);
467
+ }
468
+ return;
469
+ }
470
+ if (command === "sweep") {
471
+ const cwd = getResolvedCwd(flags);
472
+ const sweepPromptFile = getStringFlag(flags, "prompt-file");
473
+ const config = await loadRunConfig(cwd);
474
+ warnOnConfigIssues(config);
475
+ if (!sweepPromptFile && !Boolean(flags.json)) {
476
+ const assets = await discoverWorkspaceAssets(cwd);
477
+ if (assets.sweeps.length === 1) {
478
+ const autoFile = assets.sweeps[0].file;
479
+ const iterFromFm = await readSweepIterationsFromFrontmatter(path.resolve(cwd, autoFile));
480
+ console.log(`Auto-selected sweep: ${autoFile}${iterFromFm ? ` (${iterFromFm} iterations from frontmatter)` : ""}`);
481
+ flags["prompt-file"] = autoFile;
482
+ if (iterFromFm && !getStringFlag(flags, "iterations")) {
483
+ flags.iterations = String(iterFromFm);
484
+ }
485
+ }
486
+ else if (assets.sweeps.length > 1) {
487
+ console.log(`${assets.sweeps.length} sweep files found. Pick one with --prompt-file:\n`);
488
+ for (const s of assets.sweeps) {
489
+ const iterCount = await readSweepIterationsFromFrontmatter(path.resolve(cwd, s.file));
490
+ const iterFlag = iterCount ? ` --iterations ${iterCount}` : "";
491
+ console.log(` prompts-gpt sweep --prompt-file ${s.file}${iterFlag}`);
492
+ }
493
+ if (assets.prompts.length > 0) {
494
+ console.log("\nOr run a single prompt:");
495
+ for (const p of assets.prompts.slice(0, 3)) {
496
+ console.log(` prompts-gpt run --prompt-file .prompts-gpt/${p.file}`);
497
+ }
498
+ }
499
+ return;
500
+ }
501
+ else {
502
+ console.log("No sweep files found.");
503
+ console.log(" Create .prompts-gpt/sweeps/<name>.md to add sweep prompts.");
504
+ console.log(" Or set a default: prompts-gpt setup --prompt-file <path>");
505
+ console.log(" Run `prompts-gpt list` to see what's available.");
506
+ return;
507
+ }
508
+ }
509
+ if (!getStringFlag(flags, "iterations") && getStringFlag(flags, "prompt-file")) {
510
+ const iterFromFm = await readSweepIterationsFromFrontmatter(path.resolve(cwd, getStringFlag(flags, "prompt-file")));
511
+ if (iterFromFm) {
512
+ flags.iterations = String(iterFromFm);
513
+ }
514
+ }
515
+ const agent = resolveRunAgent(flags, config.defaultAgent);
516
+ const onProgress = Boolean(flags.json)
517
+ ? undefined
518
+ : (event) => {
519
+ if (event.type === "preflight") {
520
+ const report = JSON.parse(event.message);
521
+ console.log(`[preflight] provider=${report.provider} model=${report.model} branch=${report.gitBranch} dirty=${report.gitDirtyFiles} disk=${report.diskFreeMb}MB iterations=${report.iterations}`);
522
+ console.log(`[preflight] prompt=${report.promptFile}`);
523
+ for (const w of report.warnings)
524
+ console.log(`[preflight] warning: ${w}`);
525
+ }
526
+ else if (event.type === "iteration_start") {
527
+ console.log(`\n--- Iteration ${event.iteration}/${event.total} ---`);
528
+ }
529
+ else if (event.type === "iteration_end") {
530
+ const elapsed = formatDuration(event.durationMs);
531
+ console.log(`--- Iteration ${event.iteration} ${event.status} (${elapsed}) ---`);
532
+ }
533
+ else if (event.type === "attempt_retry") {
534
+ console.log(`[retry] iteration ${event.iteration}, attempt ${event.attempt}/${event.maxAttempts}, backoff ${event.backoffMs}ms`);
535
+ }
536
+ else if (event.type === "summary") {
537
+ const preview = event.lines.slice(0, 5).join("\n");
538
+ console.log(`[summary] iteration ${event.iteration}:\n${preview}${event.lines.length > 5 ? `\n ... (${event.lines.length - 5} more lines)` : ""}`);
539
+ }
540
+ else if (event.type === "sweep_end") {
541
+ console.log(`\nSweep complete: ${event.result.succeeded}/${event.result.totalIterations} succeeded in ${formatDuration(event.result.totalDurationMs)}`);
542
+ }
543
+ };
544
+ const result = await sweepPrompt({
545
+ cwd,
546
+ promptFile: getStringFlag(flags, "prompt-file"),
547
+ agent,
548
+ model: getStringFlag(flags, "model"),
549
+ iterations: parsePositiveIntFlag(getStringFlag(flags, "iterations"), "iterations"),
550
+ iterationTimeoutSeconds: parsePositiveIntFlag(getStringFlag(flags, "iteration-timeout"), "iteration-timeout"),
551
+ maxRetries: parseNonNegativeIntFlag(getStringFlag(flags, "max-retries"), "max-retries"),
552
+ artifactsDir: getStringFlag(flags, "artifacts-dir"),
553
+ runId: getStringFlag(flags, "run-id"),
554
+ approveMcps: !Boolean(flags["no-approve-mcps"]),
555
+ sandboxMode: getStringFlag(flags, "sandbox"),
556
+ phase: getStringFlag(flags, "phase"),
557
+ dryRun: Boolean(flags["dry-run"]),
558
+ maxRunDirs: parsePositiveIntFlag(getStringFlag(flags, "max-run-dirs"), "max-run-dirs"),
559
+ summaryLines: parsePositiveIntFlag(getStringFlag(flags, "summary-lines"), "summary-lines"),
560
+ background: Boolean(flags.background),
561
+ permissionMode: getStringFlag(flags, "permission-mode"),
562
+ onProgress,
563
+ });
564
+ if (Boolean(flags.json)) {
565
+ console.log(JSON.stringify(result, null, 2));
566
+ return;
567
+ }
568
+ if (result.dryRun) {
569
+ const iterTimeout = parsePositiveIntFlag(getStringFlag(flags, "iteration-timeout"), "iteration-timeout") ?? 5400;
570
+ const estMaxDuration = result.totalIterations * iterTimeout;
571
+ console.log("[dry-run] Would execute sweep with:");
572
+ console.log(` Provider: ${result.provider}`);
573
+ console.log(` Model: ${result.model}`);
574
+ console.log(` Iterations: ${result.totalIterations}`);
575
+ console.log(` Prompt: ${result.promptFile}`);
576
+ console.log(` Max duration: ~${formatDuration(estMaxDuration * 1000)} (${result.totalIterations} x ${formatDuration(iterTimeout * 1000)})`);
577
+ try {
578
+ const { readFile: fsRead } = await import("node:fs/promises");
579
+ const previewText = await fsRead(result.promptFile, "utf8");
580
+ const previewLines = previewText.split("\n").slice(0, 5);
581
+ console.log(` Prompt preview:`);
582
+ for (const line of previewLines)
583
+ console.log(` ${line}`);
584
+ if (previewText.split("\n").length > 5)
585
+ console.log(` ... (${previewText.split("\n").length - 5} more lines)`);
586
+ }
587
+ catch { /* skip preview */ }
588
+ return;
589
+ }
590
+ console.log(`Run ID: ${result.runId}`);
591
+ console.log(`Provider: ${result.provider} | Model: ${result.model}`);
592
+ console.log(`Prompt: ${result.promptFile}`);
593
+ console.log(`Iterations: ${result.succeeded}/${result.totalIterations} succeeded`);
594
+ console.log(`Duration: ${formatDuration(result.totalDurationMs)}`);
595
+ if (hasTokenUsage(result.tokenUsage)) {
596
+ console.log(`Tokens: ${formatTokenUsage(result.tokenUsage)}`);
597
+ }
598
+ console.log(`Run dir: ${result.runDir}`);
599
+ console.log(`Manifest: ${result.manifestFile}`);
600
+ if (result.iterations.length > 1) {
601
+ console.log("");
602
+ console.log("Per-iteration results:");
603
+ for (const iter of result.iterations) {
604
+ const tools = iter.toolCounts;
605
+ const toolStr = tools.total > 0 ? ` | tools: ${tools.total} (R:${tools.reads} W:${tools.writes} S:${tools.shells})` : "";
606
+ const usageStr = hasTokenUsage(iter.tokenUsage) ? ` | tokens: ${iter.tokenUsage.totalTokens.toLocaleString()}` : "";
607
+ console.log(` [${iter.iteration}/${result.totalIterations}] ${iter.status} (${formatDuration(iter.durationMs)})${toolStr}${usageStr}`);
608
+ }
609
+ const totalTools = result.iterations.reduce((acc, i) => ({
610
+ reads: acc.reads + i.toolCounts.reads, writes: acc.writes + i.toolCounts.writes,
611
+ shells: acc.shells + i.toolCounts.shells, searches: acc.searches + i.toolCounts.searches,
612
+ webSearches: acc.webSearches + i.toolCounts.webSearches, total: acc.total + i.toolCounts.total,
613
+ }), { reads: 0, writes: 0, shells: 0, searches: 0, webSearches: 0, total: 0 });
614
+ if (totalTools.total > 0) {
615
+ console.log(` Total tools: ${totalTools.total} (reads: ${totalTools.reads}, writes: ${totalTools.writes}, shells: ${totalTools.shells}, searches: ${totalTools.searches})`);
616
+ }
617
+ }
618
+ console.log("");
619
+ console.log("Inspect results:");
620
+ console.log(` cat ${result.manifestFile}`);
621
+ console.log(` ls ${result.runDir}`);
622
+ if (result.failed > 0) {
623
+ process.exitCode = 1;
624
+ }
625
+ return;
626
+ }
627
+ if (command === "quickstart") {
628
+ const cwd = getResolvedCwd(flags);
629
+ const assets = await discoverWorkspaceAssets(cwd);
630
+ const providers = await detectProviders(cwd);
631
+ const availableProviders = providers.filter((p) => p.available);
632
+ console.log("Prompts-GPT Quickstart");
633
+ console.log("======================");
634
+ console.log("");
635
+ if (!assets.credentialsFound) {
636
+ console.log("Step 1: Save your project token");
637
+ console.log(" prompts-gpt init --token-prompt");
638
+ console.log("");
639
+ console.log(" Get your token from: https://prompts-gpt.com/studio/projects");
640
+ console.log("");
641
+ console.log("Run `prompts-gpt quickstart` again after saving your token.");
642
+ return;
643
+ }
644
+ console.log("✓ Credentials found");
645
+ if (!assets.configFound) {
646
+ console.log("→ Setting up config...");
647
+ try {
648
+ await initRunConfig({ cwd, overwrite: false });
649
+ console.log("✓ Config created");
650
+ }
651
+ catch {
652
+ console.log("✓ Config already exists");
653
+ }
654
+ }
655
+ else {
656
+ console.log("✓ Config found");
657
+ }
658
+ if (availableProviders.length === 0) {
659
+ console.log("");
660
+ console.log("✗ No provider CLIs found. Install at least one:");
661
+ console.log(" - codex: npm install -g @openai/codex");
662
+ console.log(" - claude: npm install -g @anthropic-ai/claude-code");
663
+ console.log(" - cursor: Install Cursor IDE (includes `agent` CLI)");
664
+ return;
665
+ }
666
+ console.log(`✓ Providers: ${availableProviders.map((p) => p.provider).join(", ")}`);
667
+ if (assets.prompts.length === 0 && assets.sweeps.length === 0) {
668
+ console.log("");
669
+ console.log("→ No prompts found. Syncing from Prompts Studio...");
670
+ try {
671
+ const clientOpts = await resolveClientOptions("quickstart", flags).catch(() => null);
672
+ if (clientOpts) {
673
+ const qsClient = new PromptsGptClient(clientOpts);
674
+ const pulledPrompts = await qsClient.pullPrompts();
675
+ if (pulledPrompts.length > 0) {
676
+ const syncResult = await syncPrompts(pulledPrompts, { cwd, agent: "all" });
677
+ console.log(`✓ Synced ${syncResult.markdown.written.length} prompt(s) and ${syncResult.agents.written.length} agent file(s)`);
678
+ const refreshed = await discoverWorkspaceAssets(cwd);
679
+ if (refreshed.prompts.length > 0 || refreshed.sweeps.length > 0) {
680
+ console.log(`✓ Prompts: ${refreshed.prompts.length} prompt packs, ${refreshed.sweeps.length} sweeps`);
681
+ }
682
+ }
683
+ else {
684
+ console.log("No prompts found in your Prompts Studio library.");
685
+ console.log(" Configure prompts at: https://prompts-gpt.com/studio/projects");
686
+ console.log(" Or run: prompts-gpt generate --goal \"Your task\"");
687
+ return;
688
+ }
689
+ }
690
+ else {
691
+ console.log("✗ Could not sync (credentials issue). Run manually:");
692
+ console.log(" prompts-gpt sync");
693
+ return;
694
+ }
695
+ }
696
+ catch (syncErr) {
697
+ console.log(`✗ Sync failed: ${syncErr instanceof Error ? syncErr.message : String(syncErr)}`);
698
+ console.log(" Run manually: prompts-gpt sync");
699
+ return;
700
+ }
701
+ const refreshedAssets = await discoverWorkspaceAssets(cwd);
702
+ if (refreshedAssets.prompts.length === 0 && refreshedAssets.sweeps.length === 0) {
703
+ return;
704
+ }
705
+ }
706
+ console.log(`✓ Prompts: ${assets.prompts.length} prompt packs, ${assets.sweeps.length} sweeps`);
707
+ console.log("");
708
+ console.log("You're ready! Try these commands:");
709
+ console.log("");
710
+ if (assets.prompts.length > 0) {
711
+ console.log(` prompts-gpt run --prompt-file .prompts-gpt/${assets.prompts[0].file}`);
712
+ }
713
+ if (assets.sweeps.length > 0) {
714
+ console.log(` prompts-gpt sweep --prompt-file ${assets.sweeps[0].file}`);
715
+ }
716
+ console.log(" prompts-gpt list — see all available prompts and sweeps");
717
+ console.log(" prompts-gpt status — check workspace readiness");
718
+ return;
719
+ }
720
+ if (command === "load-config") {
721
+ const cwd = getResolvedCwd(flags);
722
+ const client = await createClientForCommand(command, flags);
723
+ const project = await client.getProject();
724
+ const prompts = await client.pullPrompts();
725
+ if (prompts.length === 0) {
726
+ console.log("No prompt packs found in the project library. Configure prompts in Prompts Studio first.");
727
+ console.log(` Open: https://prompts-gpt.com/studio/projects`);
728
+ return;
729
+ }
730
+ const result = await syncPrompts(prompts, {
731
+ cwd,
732
+ outDir: getStringFlag(flags, "out") || DEFAULT_PROMPTS_GPT_OUT_DIR,
733
+ overwrite: true,
734
+ agent: getStringFlag(flags, "agent") || "all",
735
+ });
736
+ const providers = await detectProviders(cwd);
737
+ const availableProviders = providers.filter((p) => p.available);
738
+ let configError = null;
739
+ const configResult = await initRunConfig({
740
+ cwd,
741
+ overwrite: true,
742
+ }).catch((err) => {
743
+ configError = err.message;
744
+ return null;
745
+ });
746
+ if (Boolean(flags.json)) {
747
+ console.log(JSON.stringify({
748
+ project: { brandName: project.brandName, websiteUrl: project.websiteUrl },
749
+ synced: { prompts: result.markdown.written.length, agents: result.agents.written.length },
750
+ providers: availableProviders.map((p) => p.provider),
751
+ config: configResult ? "created" : "skipped",
752
+ }, null, 2));
753
+ return;
754
+ }
755
+ console.log(`Loaded configuration from Prompts Studio for: ${project.brandName}`);
756
+ console.log(`Synced ${result.markdown.written.length} prompt pack(s) to ${result.markdown.outDir}`);
757
+ console.log(`Synced ${result.agents.written.length} agent file(s): ${result.agents.targets.join(", ")}`);
758
+ console.log(`Manifest: ${result.manifest.manifestPath}`);
759
+ if (configResult) {
760
+ console.log(`Config: ${configResult.configPath}`);
761
+ }
762
+ else if (configError) {
763
+ console.error(`[config] Skipped: ${configError}`);
764
+ }
765
+ if (availableProviders.length > 0 && result.manifest.manifest.prompts.length > 0) {
766
+ const firstFile = result.manifest.manifest.prompts[0]?.file;
767
+ if (firstFile) {
768
+ console.log(`\nReady to run:`);
769
+ console.log(` prompts-gpt run --prompt-file .prompts-gpt/${firstFile}`);
770
+ }
771
+ }
772
+ const lcAssets = await discoverWorkspaceAssets(cwd);
773
+ if (lcAssets.sweeps.length > 0) {
774
+ console.log(`\nSweep files available (${lcAssets.sweeps.length}):`);
775
+ for (const s of lcAssets.sweeps) {
776
+ console.log(` prompts-gpt sweep --prompt-file ${s.file}`);
777
+ }
778
+ }
779
+ return;
780
+ }
73
781
  if (command === "init") {
74
- const token = await resolveTokenInput(flags, { command });
782
+ let token = await resolveTokenInput(flags, { command });
783
+ if (!token && process.stdin.isTTY && process.stdout.isTTY) {
784
+ console.log("No token flag provided — prompting interactively.");
785
+ console.log("Get your token from: https://prompts-gpt.com/studio/projects\n");
786
+ token = await readTokenFromPrompt(command);
787
+ }
75
788
  if (!token) {
76
789
  throw new CliError("Run `prompts-gpt init --token <project-token>`, `--token-stdin`, or `--token-prompt`.", CLI_EXIT_CODES.validation, {
77
790
  helpCommand: "init",
@@ -88,13 +801,31 @@ async function runCommand(command, flags) {
88
801
  });
89
802
  }
90
803
  const cwd = getResolvedCwd(flags);
804
+ const apiUrlFlag = getStringFlag(flags, "api-url");
805
+ if (apiUrlFlag) {
806
+ try {
807
+ const parsed = new URL(apiUrlFlag);
808
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
809
+ throw new CliError("--api-url must use https or http.", CLI_EXIT_CODES.validation, { helpCommand: "init" });
810
+ }
811
+ }
812
+ catch (e) {
813
+ if (e instanceof CliError)
814
+ throw e;
815
+ throw new CliError(`--api-url is not a valid URL: ${apiUrlFlag}`, CLI_EXIT_CODES.validation, { helpCommand: "init" });
816
+ }
817
+ }
91
818
  const result = await saveLocalCredentials({
92
819
  token,
93
- apiUrl: getStringFlag(flags, "api-url") || DEFAULT_PROMPTS_GPT_API_URL,
820
+ apiUrl: apiUrlFlag || DEFAULT_PROMPTS_GPT_API_URL,
94
821
  cwd,
95
822
  });
96
823
  console.log(`Saved Prompts-GPT credentials to ${result.credentialsPath}`);
97
824
  console.log("The credentials file is added to .gitignore.");
825
+ console.log("");
826
+ console.log("Next steps:");
827
+ console.log(" prompts-gpt quickstart — interactive setup (recommended)");
828
+ console.log(" prompts-gpt sync — pull prompts and sync agent files");
98
829
  return;
99
830
  }
100
831
  if (command === "pull") {
@@ -114,28 +845,30 @@ async function runCommand(command, flags) {
114
845
  if (result.skipped.length) {
115
846
  console.log(`Skipped ${result.skipped.length} existing file(s). Use --overwrite to replace them.`);
116
847
  }
848
+ if (result.written.length > 0) {
849
+ console.log("");
850
+ console.log("Run a pulled prompt:");
851
+ console.log(` prompts-gpt run --prompt-file ${result.written[0]}`);
852
+ console.log(" prompts-gpt list — see all available prompts");
853
+ }
117
854
  return;
118
855
  }
119
856
  if (command === "generate") {
120
857
  validateToolFlag(getStringFlag(flags, "tool"));
121
858
  const goal = getStringFlag(flags, "goal");
122
859
  if (!goal) {
123
- throw new CliError("Prompt generation requires `--goal`.", CLI_EXIT_CODES.validation, {
124
- helpCommand: "generate",
125
- });
860
+ throw new CliError("Prompt generation requires `--goal`.\n\n" +
861
+ "Example:\n" +
862
+ " prompts-gpt generate --goal \"Review pull requests for security issues\"\n" +
863
+ " prompts-gpt generate --goal \"Write unit tests\" --sync-agents\n\n" +
864
+ "Use --sync-agents to also write agent files (AGENTS.md, .cursor/rules, etc.).", CLI_EXIT_CODES.validation, { helpCommand: "generate" });
126
865
  }
127
866
  validateAgentFlag(getStringFlag(flags, "agent"));
867
+ if (!Boolean(flags.json)) {
868
+ printDataTransmissionNotice("generate", { goal, context: getStringFlag(flags, "context"), constraints: getStringFlag(flags, "constraints") });
869
+ }
128
870
  const client = await createClientForCommand(command, flags);
129
- const prompt = await client.generatePrompt({
130
- goal,
131
- context: getStringFlag(flags, "context") || "",
132
- constraints: getStringFlag(flags, "constraints") || "",
133
- desiredOutput: getStringFlag(flags, "desired-output") || "A reusable prompt pack saved as Markdown.",
134
- tool: getStringFlag(flags, "tool") || "Codex",
135
- mode: getStringFlag(flags, "mode") || "implement",
136
- artifactType: getStringFlag(flags, "artifact-type") || "prompt-file",
137
- includeWebSearch: Boolean(flags["web-search"]),
138
- });
871
+ const prompt = await client.generatePrompt(buildGenerateInput(flags));
139
872
  const cwd = getResolvedCwd(flags);
140
873
  const result = await writePromptMarkdownFiles([prompt], {
141
874
  cwd,
@@ -145,12 +878,24 @@ async function runCommand(command, flags) {
145
878
  const shouldSyncAgents = Boolean(flags["sync-agents"]) || typeof getStringFlag(flags, "agent") === "string";
146
879
  if (shouldSyncAgents) {
147
880
  const target = getStringFlag(flags, "agent") || "all";
148
- const agentResult = await writeAgentFiles([prompt], {
881
+ const pulled = await client.pullPrompts().catch((err) => {
882
+ if (!Boolean(flags.json)) {
883
+ console.error(`[warning] Could not pull existing prompts for agent sync: ${err.message}`);
884
+ }
885
+ return [];
886
+ });
887
+ const syncedPrompts = mergePromptPacks([prompt, ...pulled]);
888
+ const agentResult = await writeAgentFiles(syncedPrompts, {
149
889
  cwd,
150
890
  agent: target,
151
891
  overwriteAgentFiles: true,
152
892
  });
893
+ const manifestResult = await writePromptManifest(syncedPrompts, {
894
+ cwd,
895
+ outDir: getStringFlag(flags, "out") || DEFAULT_PROMPTS_GPT_OUT_DIR,
896
+ });
153
897
  console.log(`Synced ${agentResult.written.length} agent file(s) for ${agentResult.targets.join(", ")}.`);
898
+ console.log(`Updated manifest: ${manifestResult.manifestPath}`);
154
899
  }
155
900
  console.log(`Generated: ${prompt.title}`);
156
901
  console.log(`Wrote to ${result.written[0] ?? result.outDir}.`);
@@ -167,17 +912,11 @@ async function runCommand(command, flags) {
167
912
  const goal = getStringFlag(flags, "goal");
168
913
  const generatedOnly = Boolean(flags["generated-only"]);
169
914
  const client = await createClientForCommand(command, flags);
915
+ if (goal && !Boolean(flags.json)) {
916
+ printDataTransmissionNotice("sync", { goal, context: getStringFlag(flags, "context"), constraints: getStringFlag(flags, "constraints") });
917
+ }
170
918
  if (goal) {
171
- prompts.push(await client.generatePrompt({
172
- goal,
173
- context: getStringFlag(flags, "context") || "",
174
- constraints: getStringFlag(flags, "constraints") || "",
175
- desiredOutput: getStringFlag(flags, "desired-output") || "A reusable prompt pack saved as Markdown.",
176
- tool: getStringFlag(flags, "tool") || "Codex",
177
- mode: getStringFlag(flags, "mode") || "implement",
178
- artifactType: getStringFlag(flags, "artifact-type") || "prompt-file",
179
- includeWebSearch: Boolean(flags["web-search"]),
180
- }));
919
+ prompts.push(await client.generatePrompt(buildGenerateInput(flags)));
181
920
  }
182
921
  if (!generatedOnly) {
183
922
  prompts.push(...await client.pullPrompts(buildPullQuery(flags, parsedLimit)));
@@ -199,45 +938,58 @@ async function runCommand(command, flags) {
199
938
  overwrite: Boolean(flags.overwrite),
200
939
  agent: getStringFlag(flags, "agent") || "all",
201
940
  });
202
- console.log(`Synced ${result.markdown.written.length} Markdown prompt file(s) to ${result.markdown.outDir}.`);
203
- console.log(`Synced ${result.agents.written.length} agent integration file(s): ${result.agents.targets.join(", ")}.`);
204
- console.log(`Updated manifest: ${result.manifest.manifestPath}`);
941
+ console.log(`Synced ${result.markdown.written.length} new prompt file(s) to ${result.markdown.outDir}.`);
205
942
  if (result.markdown.skipped.length) {
206
- console.log(`Skipped ${result.markdown.skipped.length} existing Markdown file(s). Use --overwrite to replace them.`);
943
+ console.log(`Kept ${result.markdown.skipped.length} existing file(s) unchanged. Use --overwrite to replace.`);
207
944
  }
208
- return;
209
- }
210
- if (command === "install-agents") {
211
- validateAgentFlag(getStringFlag(flags, "agent"));
212
- const parsedLimit = parseLimitFlag(getStringFlag(flags, "limit"));
213
- const client = await createClientForCommand(command, flags);
214
- const prompts = await client.pullPrompts(buildPullQuery(flags, parsedLimit));
215
- if (prompts.length === 0) {
216
- console.log("No prompts matched the query, so no agent files were written.");
217
- return;
945
+ console.log(`Synced ${result.agents.written.length} agent file(s): ${result.agents.targets.join(", ")}.`);
946
+ console.log(`Manifest: ${result.manifest.manifestPath}`);
947
+ const syncCwd = getResolvedCwd(flags);
948
+ const postSyncAssets = await discoverWorkspaceAssets(syncCwd);
949
+ if (postSyncAssets.sweeps.length > 0) {
950
+ console.log(`Sweeps: ${postSyncAssets.sweeps.length} sweep file(s) available.`);
218
951
  }
219
- if (Boolean(flags["dry-run"])) {
220
- console.log(`[dry-run] Would install agent files for ${prompts.length} prompt(s), target: ${getStringFlag(flags, "agent") || "all"}.`);
221
- for (const p of prompts)
222
- console.log(` - ${p.title}`);
223
- return;
952
+ const postProviders = await detectProviders(syncCwd);
953
+ const postAvailable = postProviders.filter((p) => p.available);
954
+ if (postAvailable.length > 0 && postSyncAssets.prompts.length > 0) {
955
+ console.log("");
956
+ console.log("Next steps:");
957
+ console.log(` prompts-gpt run --prompt-file .prompts-gpt/${postSyncAssets.prompts[0].file}`);
958
+ if (postSyncAssets.sweeps.length > 0) {
959
+ console.log(` prompts-gpt sweep --prompt-file ${postSyncAssets.sweeps[0].file}`);
960
+ }
224
961
  }
225
- const result = await writeAgentFiles(prompts, {
226
- cwd: getResolvedCwd(flags),
227
- agent: getStringFlag(flags, "agent") || "all",
228
- overwriteAgentFiles: true,
229
- });
230
- console.log(`Synced ${result.written.length} agent integration file(s): ${result.targets.join(", ")}.`);
231
962
  return;
232
963
  }
233
964
  if (command === "project") {
234
- const client = await createClientForCommand(command, flags);
965
+ const clientOptions = await resolveClientOptions(command, flags);
966
+ const client = new PromptsGptClient(clientOptions);
235
967
  const project = await client.getProject();
236
- console.log(`${project.brandName} (${project.websiteUrl})`);
968
+ const apiUrl = clientOptions.apiUrl;
969
+ if (Boolean(flags.json)) {
970
+ console.log(JSON.stringify({
971
+ project,
972
+ apiUrl,
973
+ }, null, 2));
974
+ return;
975
+ }
976
+ console.log(`Project: ${project.brandName}`);
977
+ console.log(`Website: ${project.websiteUrl}`);
978
+ console.log(`Industry: ${project.industryCategory}`);
979
+ console.log(`Language: ${project.targetLanguage}`);
980
+ console.log(`Countries: ${formatList(project.targetCountries)}`);
981
+ console.log(`Aliases: ${formatList(project.brandAliases)}`);
982
+ console.log(`Keywords: ${formatList(project.productKeywords)}`);
983
+ console.log(`Personas: ${formatList(project.targetPersonas)}`);
984
+ console.log(`Competitors: ${formatCompetitors(project.competitors)}`);
985
+ console.log(`API URL: ${apiUrl}`);
237
986
  return;
238
987
  }
239
988
  }
240
989
  async function createClientForCommand(command, flags) {
990
+ return new PromptsGptClient(await resolveClientOptions(command, flags));
991
+ }
992
+ async function resolveClientOptions(command, flags) {
241
993
  const cwd = getResolvedCwd(flags);
242
994
  const explicitToken = await resolveTokenInput(flags, { command });
243
995
  const explicitApiUrl = getStringFlag(flags, "api-url");
@@ -248,14 +1000,29 @@ async function createClientForCommand(command, flags) {
248
1000
  throw new CliError(`Could not read local credentials at ${credentialsPath}. Re-run \`prompts-gpt init --token <project-token>\` to replace the file.`, CLI_EXIT_CODES.auth, { helpCommand: command });
249
1001
  }
250
1002
  const token = explicitToken || credentials?.token || "";
1003
+ if (!token && !process.stdin.isTTY) {
1004
+ // CI fallback — never read process.env in the importable SDK, only in the CLI entrypoint
1005
+ const envToken = process.env.PROMPTS_GPT_TOKEN?.trim();
1006
+ if (envToken) {
1007
+ return {
1008
+ token: envToken,
1009
+ apiUrl: explicitApiUrl || credentials?.apiUrl || DEFAULT_PROMPTS_GPT_API_URL,
1010
+ fetch,
1011
+ };
1012
+ }
1013
+ }
251
1014
  if (!token) {
252
- 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 });
1015
+ const cloudCommands = new Set(["sync", "generate", "pull", "load-config", "project"]);
1016
+ const extra = cloudCommands.has(command)
1017
+ ? `\n\nThis command connects to prompts-gpt.com. Set up credentials first:\n prompts-gpt init --token <project-token>\n prompts-gpt quickstart`
1018
+ : "";
1019
+ 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 });
253
1020
  }
254
- return new PromptsGptClient({
1021
+ return {
255
1022
  token,
256
1023
  apiUrl: explicitApiUrl || credentials?.apiUrl || DEFAULT_PROMPTS_GPT_API_URL,
257
1024
  fetch,
258
- });
1025
+ };
259
1026
  }
260
1027
  function buildPullQuery(flags, limit) {
261
1028
  return {
@@ -330,8 +1097,33 @@ function getCommandOptions(command) {
330
1097
  if (command === "init") {
331
1098
  return common;
332
1099
  }
1100
+ if (command === "setup") {
1101
+ return {
1102
+ help: { type: "boolean" },
1103
+ cwd: { type: "string" },
1104
+ json: { type: "boolean" },
1105
+ overwrite: { type: "boolean" },
1106
+ "prompt-file": { type: "string" },
1107
+ "prompt-dir": { type: "string" },
1108
+ manifest: { type: "string" },
1109
+ "prompt-files": { type: "string" },
1110
+ agent: { type: "string" },
1111
+ "provider-order": { type: "string" },
1112
+ timeout: { type: "string" },
1113
+ "retry-count": { type: "string" },
1114
+ "artifacts-dir": { type: "string" },
1115
+ "allow-destructive-git": { type: "boolean" },
1116
+ "codex-model": { type: "string" },
1117
+ "cursor-model": { type: "string" },
1118
+ "claude-model": { type: "string" },
1119
+ "copilot-model": { type: "string" },
1120
+ };
1121
+ }
333
1122
  if (command === "project") {
334
- return common;
1123
+ return {
1124
+ ...common,
1125
+ json: { type: "boolean" },
1126
+ };
335
1127
  }
336
1128
  if (command === "pull") {
337
1129
  return {
@@ -386,54 +1178,135 @@ function getCommandOptions(command) {
386
1178
  "dry-run": { type: "boolean" },
387
1179
  };
388
1180
  }
389
- return {
390
- ...common,
391
- query: { type: "string" },
392
- q: { type: "string" },
393
- category: { type: "string" },
394
- tool: { type: "string" },
395
- "output-type": { type: "string" },
396
- limit: { type: "string" },
397
- agent: { type: "string" },
398
- "dry-run": { type: "boolean" },
399
- };
400
- }
401
- function normalizeLegacyFlags(argv) {
402
- const aliases = new Map([
403
- ["agents", "agent"],
404
- ["apiUrl", "api-url"],
405
- ["desiredOutput", "desired-output"],
406
- ["artifactType", "artifact-type"],
407
- ["outputType", "output-type"],
408
- ["webSearch", "web-search"],
409
- ["generatedOnly", "generated-only"],
410
- ["syncAgents", "sync-agents"],
411
- ["tokenStdin", "token-stdin"],
412
- ["tokenPrompt", "token-prompt"],
413
- ["dryRun", "dry-run"],
414
- ]);
415
- return argv.map((arg) => {
416
- if (!arg.startsWith("--"))
417
- return arg;
418
- const eqIndex = arg.indexOf("=");
419
- const rawKey = eqIndex === -1 ? arg.slice(2) : arg.slice(2, eqIndex);
420
- const mappedKey = aliases.get(rawKey) ?? rawKey;
421
- return eqIndex === -1 ? `--${mappedKey}` : `--${mappedKey}=${arg.slice(eqIndex + 1)}`;
1181
+ if (command === "run") {
1182
+ return {
1183
+ help: { type: "boolean" },
1184
+ cwd: { type: "string" },
1185
+ json: { type: "boolean" },
1186
+ "prompt-file": { type: "string", short: "f" },
1187
+ agent: { type: "string" },
1188
+ model: { type: "string" },
1189
+ timeout: { type: "string" },
1190
+ "artifacts-dir": { type: "string" },
1191
+ "run-id": { type: "string" },
1192
+ sandbox: { type: "string" },
1193
+ "no-approve-mcps": { type: "boolean" },
1194
+ background: { type: "boolean" },
1195
+ "permission-mode": { type: "string" },
1196
+ "dry-run": { type: "boolean" },
1197
+ };
1198
+ }
1199
+ if (command === "sweep") {
1200
+ return {
1201
+ help: { type: "boolean" },
1202
+ cwd: { type: "string" },
1203
+ json: { type: "boolean" },
1204
+ "prompt-file": { type: "string", short: "f" },
1205
+ agent: { type: "string" },
1206
+ model: { type: "string" },
1207
+ iterations: { type: "string", short: "n" },
1208
+ "iteration-timeout": { type: "string" },
1209
+ "max-retries": { type: "string" },
1210
+ "artifacts-dir": { type: "string" },
1211
+ "run-id": { type: "string" },
1212
+ sandbox: { type: "string" },
1213
+ "no-approve-mcps": { type: "boolean" },
1214
+ phase: { type: "string" },
1215
+ "dry-run": { type: "boolean" },
1216
+ "max-run-dirs": { type: "string" },
1217
+ "summary-lines": { type: "string" },
1218
+ background: { type: "boolean" },
1219
+ "permission-mode": { type: "string" },
1220
+ };
1221
+ }
1222
+ if (command === "run-batch") {
1223
+ return {
1224
+ help: { type: "boolean" },
1225
+ cwd: { type: "string" },
1226
+ json: { type: "boolean" },
1227
+ manifest: { type: "string" },
1228
+ "prompt-files": { type: "string" },
1229
+ agent: { type: "string" },
1230
+ model: { type: "string" },
1231
+ timeout: { type: "string" },
1232
+ "artifacts-dir": { type: "string" },
1233
+ };
1234
+ }
1235
+ if (command === "load-config") {
1236
+ return {
1237
+ ...common,
1238
+ json: { type: "boolean" },
1239
+ out: { type: "string" },
1240
+ agent: { type: "string" },
1241
+ };
1242
+ }
1243
+ if (command === "quickstart" || command === "list" || command === "status" || command === "validate" || command === "providers" || command === "doctor") {
1244
+ return {
1245
+ help: { type: "boolean" },
1246
+ cwd: { type: "string" },
1247
+ json: { type: "boolean" },
1248
+ };
1249
+ }
1250
+ throw new CliError(`Unsupported command: ${command}.`, CLI_EXIT_CODES.usage, {
1251
+ helpCommand: "help",
422
1252
  });
423
1253
  }
424
1254
  function toCliParseError(error, command) {
425
- const message = normalizeParseErrorMessage(error instanceof Error ? error.message : String(error));
1255
+ const runnableCmd = isRunnableCommand(command) ? command : undefined;
1256
+ const message = normalizeParseErrorMessage(error instanceof Error ? error.message : String(error), runnableCmd);
426
1257
  return new CliError(message, CLI_EXIT_CODES.usage, {
427
1258
  helpCommand: command,
428
1259
  });
429
1260
  }
430
- function normalizeParseErrorMessage(message) {
1261
+ function normalizeParseErrorMessage(message, command) {
431
1262
  const trimmed = message.trim();
432
1263
  if (trimmed.startsWith("Unknown option")) {
433
- return `${trimmed}.`;
1264
+ const match = trimmed.match(/Unknown option '(--[^']+)'/);
1265
+ const unknownFlag = match?.[1];
1266
+ const base = trimmed.endsWith(".") ? trimmed : `${trimmed}.`;
1267
+ if (unknownFlag && command) {
1268
+ const suggestion = suggestClosestFlag(unknownFlag, command);
1269
+ if (suggestion) {
1270
+ return `${base} Did you mean ${suggestion}?`;
1271
+ }
1272
+ }
1273
+ return base;
434
1274
  }
435
1275
  return trimmed;
436
1276
  }
1277
+ function suggestClosestFlag(unknown, command) {
1278
+ const options = getCommandOptions(command);
1279
+ const flagName = unknown.replace(/^--/, "");
1280
+ const candidates = Object.keys(options);
1281
+ let best = null;
1282
+ let bestScore = 0;
1283
+ for (const candidate of candidates) {
1284
+ const score = computeFlagSimilarity(flagName, candidate);
1285
+ if (score > bestScore && score >= 0.4) {
1286
+ bestScore = score;
1287
+ best = candidate;
1288
+ }
1289
+ }
1290
+ return best ? `--${best}` : null;
1291
+ }
1292
+ function computeFlagSimilarity(a, b) {
1293
+ if (a === b)
1294
+ return 1;
1295
+ const shorter = a.length < b.length ? a : b;
1296
+ const longer = a.length < b.length ? b : a;
1297
+ if (longer.startsWith(shorter) || longer.endsWith(shorter))
1298
+ return 0.8;
1299
+ if (longer.includes(shorter) && shorter.length >= 3)
1300
+ return 0.6;
1301
+ if (shorter.length === 0)
1302
+ return 0;
1303
+ let matches = 0;
1304
+ for (let i = 0; i < Math.min(a.length, b.length); i++) {
1305
+ if (a[i] === b[i])
1306
+ matches++;
1307
+ }
1308
+ return matches / Math.max(a.length, b.length);
1309
+ }
437
1310
  function validateFlagConflicts(command, flags) {
438
1311
  const tokenSourceCount = [Boolean(flags.token), Boolean(flags["token-stdin"]), Boolean(flags["token-prompt"])].filter(Boolean).length;
439
1312
  if (tokenSourceCount > 1) {
@@ -446,6 +1319,22 @@ function validateFlagConflicts(command, flags) {
446
1319
  helpCommand: command,
447
1320
  });
448
1321
  }
1322
+ const promptSourceCount = [
1323
+ Boolean(flags["prompt-file"]),
1324
+ Boolean(flags["prompt-files"]),
1325
+ Boolean(flags.manifest),
1326
+ Boolean(flags["prompt-dir"]),
1327
+ ].filter(Boolean).length;
1328
+ if (command === "setup" && promptSourceCount > 1) {
1329
+ throw new CliError("Use only one prompt source for `setup`: `--prompt-file`, `--prompt-files`, `--manifest`, or `--prompt-dir`.", CLI_EXIT_CODES.usage, {
1330
+ helpCommand: command,
1331
+ });
1332
+ }
1333
+ if (command === "run-batch" && Boolean(flags["prompt-files"]) && Boolean(flags.manifest)) {
1334
+ throw new CliError("Use either `--prompt-files` or `--manifest`, not both.", CLI_EXIT_CODES.usage, {
1335
+ helpCommand: command,
1336
+ });
1337
+ }
449
1338
  }
450
1339
  async function resolveTokenInput(flags, options) {
451
1340
  const explicitToken = getStringFlag(flags, "token")?.trim();
@@ -491,26 +1380,48 @@ function readTokenFromPrompt(command) {
491
1380
  return new Promise((resolve, reject) => {
492
1381
  const stdin = process.stdin;
493
1382
  const stdout = process.stdout;
494
- let token = "";
1383
+ const buf = Buffer.alloc(MAX_STDIN_TOKEN_LENGTH);
1384
+ let bufLen = 0;
495
1385
  const cleanup = () => {
496
1386
  stdin.removeListener("data", onData);
1387
+ process.removeListener("SIGTERM", onSigterm);
1388
+ process.removeListener("SIGINT", onSigint);
497
1389
  stdin.setRawMode?.(false);
498
1390
  stdin.pause();
499
1391
  };
1392
+ const extractAndWipe = () => {
1393
+ const result = buf.toString("utf8", 0, bufLen).trim();
1394
+ buf.fill(0, 0, bufLen);
1395
+ bufLen = 0;
1396
+ return result;
1397
+ };
500
1398
  const finish = (callback) => {
501
1399
  stdout.write("\n");
502
1400
  cleanup();
503
1401
  callback();
504
1402
  };
1403
+ const onSigterm = () => {
1404
+ buf.fill(0, 0, bufLen);
1405
+ cleanup();
1406
+ process.exit(130);
1407
+ };
1408
+ const onSigint = () => {
1409
+ buf.fill(0, 0, bufLen);
1410
+ cleanup();
1411
+ process.exit(130);
1412
+ };
1413
+ process.on("SIGTERM", onSigterm);
1414
+ process.on("SIGINT", onSigint);
505
1415
  const onData = (chunk) => {
506
1416
  const value = typeof chunk === "string" ? chunk : chunk.toString("utf8");
507
1417
  for (const char of value) {
508
1418
  if (char === "\u0003") {
1419
+ buf.fill(0, 0, bufLen);
509
1420
  finish(() => reject(new CliError("Token entry cancelled.", CLI_EXIT_CODES.general, { helpCommand: command })));
510
1421
  return;
511
1422
  }
512
1423
  if (char === "\r" || char === "\n") {
513
- const trimmed = token.trim();
1424
+ const trimmed = extractAndWipe();
514
1425
  if (!trimmed) {
515
1426
  finish(() => reject(new CliError("Project token is required.", CLI_EXIT_CODES.validation, { helpCommand: command })));
516
1427
  return;
@@ -519,21 +1430,33 @@ function readTokenFromPrompt(command) {
519
1430
  return;
520
1431
  }
521
1432
  if (char === "\u007f" || char === "\b") {
522
- token = token.slice(0, -1);
1433
+ if (bufLen > 0) {
1434
+ bufLen--;
1435
+ buf[bufLen] = 0;
1436
+ }
523
1437
  continue;
524
1438
  }
525
1439
  if (char >= " ") {
526
- token += char;
527
- if (token.length > MAX_STDIN_TOKEN_LENGTH) {
1440
+ const charBytes = Buffer.byteLength(char, "utf8");
1441
+ if (bufLen + charBytes > MAX_STDIN_TOKEN_LENGTH) {
1442
+ buf.fill(0, 0, bufLen);
528
1443
  finish(() => reject(new CliError("Token input is too long.", CLI_EXIT_CODES.validation, { helpCommand: command })));
529
1444
  return;
530
1445
  }
1446
+ buf.write(char, bufLen, "utf8");
1447
+ bufLen += charBytes;
531
1448
  }
532
1449
  }
533
1450
  };
534
1451
  stdout.write("Project token: ");
535
1452
  stdin.setEncoding("utf8");
536
- stdin.setRawMode?.(true);
1453
+ try {
1454
+ stdin.setRawMode?.(true);
1455
+ }
1456
+ catch {
1457
+ reject(new CliError("Cannot enable raw mode for token prompt. Use --token-stdin instead.", CLI_EXIT_CODES.usage, { helpCommand: command }));
1458
+ return;
1459
+ }
537
1460
  stdin.resume();
538
1461
  stdin.on("data", onData);
539
1462
  });
@@ -542,11 +1465,11 @@ function parseLimitFlag(raw) {
542
1465
  if (raw === undefined)
543
1466
  return undefined;
544
1467
  if (!/^\d+$/.test(raw)) {
545
- throw new CliError("`--limit` must be a positive integer.", CLI_EXIT_CODES.validation);
1468
+ throw new CliError("`--limit` must be a positive integer between 1 and 100.", CLI_EXIT_CODES.validation);
546
1469
  }
547
1470
  const value = Number.parseInt(raw, 10);
548
- if (!Number.isSafeInteger(value) || value < 1) {
549
- throw new CliError("`--limit` must be a positive integer.", CLI_EXIT_CODES.validation);
1471
+ if (!Number.isSafeInteger(value) || value < 1 || value > 100) {
1472
+ throw new CliError("`--limit` must be a positive integer between 1 and 100.", CLI_EXIT_CODES.validation);
550
1473
  }
551
1474
  return value;
552
1475
  }
@@ -568,6 +1491,18 @@ function validateAgentFlag(agent) {
568
1491
  throw new CliError(`Invalid --agent target: ${invalid.join(", ")}. Use ${SUPPORTED_AGENT_TARGETS.join(", ")}, or all.`, CLI_EXIT_CODES.validation);
569
1492
  }
570
1493
  }
1494
+ function validateProviderOrderFlag(values) {
1495
+ if (!values?.length)
1496
+ return;
1497
+ const invalid = values.filter((value) => {
1498
+ const raw = value.trim().toLowerCase();
1499
+ const normalized = normalizeOrchestrationAgent(raw);
1500
+ return !raw || normalized === "router";
1501
+ });
1502
+ if (invalid.length > 0) {
1503
+ throw new CliError(`Invalid --provider-order value: ${invalid.join(", ")}. Use codex, cursor, claude, or copilot.`, CLI_EXIT_CODES.validation);
1504
+ }
1505
+ }
571
1506
  function getResolvedCwd(flags) {
572
1507
  return path.resolve(getStringFlag(flags, "cwd") || process.cwd());
573
1508
  }
@@ -575,6 +1510,44 @@ function getStringFlag(flags, name) {
575
1510
  const value = flags[name];
576
1511
  return typeof value === "string" ? value : undefined;
577
1512
  }
1513
+ function formatDuration(ms) {
1514
+ if (ms < 1000)
1515
+ return `${ms}ms`;
1516
+ const totalSeconds = Math.round(ms / 1000);
1517
+ const hours = Math.floor(totalSeconds / 3600);
1518
+ const minutes = Math.floor((totalSeconds % 3600) / 60);
1519
+ const seconds = totalSeconds % 60;
1520
+ if (hours > 0)
1521
+ return `${hours}h ${minutes}m ${seconds}s`;
1522
+ if (minutes > 0)
1523
+ return `${minutes}m ${seconds}s`;
1524
+ return `${seconds}s`;
1525
+ }
1526
+ function formatList(values) {
1527
+ if (!Array.isArray(values) || values.length === 0)
1528
+ return "None";
1529
+ return values.join(", ");
1530
+ }
1531
+ function formatCompetitors(values) {
1532
+ if (!Array.isArray(values) || values.length === 0)
1533
+ return "None";
1534
+ return values.map((competitor) => {
1535
+ const aliases = competitor.aliases?.length ? ` [aliases: ${competitor.aliases.join(", ")}]` : "";
1536
+ return competitor.websiteUrl ? `${competitor.name} (${competitor.websiteUrl})${aliases}` : `${competitor.name}${aliases}`;
1537
+ }).join("; ");
1538
+ }
1539
+ function mergePromptPacks(prompts) {
1540
+ const deduped = new Map();
1541
+ for (const prompt of prompts) {
1542
+ deduped.set(normalizePromptKey(prompt), prompt);
1543
+ }
1544
+ return [...deduped.values()];
1545
+ }
1546
+ function normalizePromptKey(prompt) {
1547
+ const raw = String(prompt.slug || prompt.title || "prompt").trim().toLowerCase();
1548
+ const normalized = raw.replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
1549
+ return normalized || "prompt";
1550
+ }
578
1551
  function asCommandName(value) {
579
1552
  if (!value)
580
1553
  return undefined;
@@ -609,53 +1582,144 @@ Usage:
609
1582
  prompts-gpt help [command]
610
1583
  prompts-gpt version
611
1584
 
612
- Commands:
613
- init Save a project token in .prompts-gpt/.credentials.json
614
- project Show the current project linked to the token
1585
+ Setup:
1586
+ quickstart Interactive setup credentials, config, and first run
1587
+ init Save a project token
1588
+ setup Scaffold local orchestration config
1589
+ status Show workspace readiness and next steps
1590
+ doctor Validate prerequisites and config
1591
+
1592
+ Sync & Generate:
1593
+ sync Pull prompt packs and sync agent files
615
1594
  pull Download prompt packs as Markdown files
616
- generate Generate one prompt pack from a goal
617
- sync Generate and/or pull prompt packs, then sync agent files
618
- install-agents Refresh agent-specific instruction files from pulled prompts
619
- version Show the current CLI version
620
- help Show global or command-specific help
1595
+ generate Generate a prompt pack from a goal
1596
+ load-config Pull config and prompts from Prompts Studio
1597
+
1598
+ Run:
1599
+ run Execute one prompt with a local agent (-f <file>)
1600
+ run-batch Execute multiple prompts
1601
+ sweep Multi-iteration execution (-f <file> -n <count>)
1602
+
1603
+ Inspect:
1604
+ list Show prompts, sweeps, and agent integrations
1605
+ validate Check config for errors
1606
+ providers Show detected provider CLIs
1607
+ project Show project linked to the token
621
1608
 
622
1609
  Global options:
623
1610
  --help Show help
624
1611
  --version Show the current CLI version
625
1612
 
626
- Supported tools: ${VALID_TOOLS.join(", ")}
627
- Supported agent targets: ${SUPPORTED_AGENT_TARGETS.join(", ")}
1613
+ Providers: ${ORCHESTRATION_AGENT_PROFILES.filter((p) => p !== "router").join(", ")} (or router for auto-select)
1614
+ Agent targets: ${SUPPORTED_AGENT_TARGETS.join(", ")}
628
1615
  `);
629
1616
  }
1617
+ function parsePositiveIntFlag(raw, flagName) {
1618
+ if (raw === undefined)
1619
+ return undefined;
1620
+ const label = flagName ? `--${flagName}` : "Value";
1621
+ if (!/^\d+$/.test(raw)) {
1622
+ throw new CliError(`${label} must be a positive integer, got "${raw}".`, CLI_EXIT_CODES.validation);
1623
+ }
1624
+ const value = Number.parseInt(raw, 10);
1625
+ if (!Number.isSafeInteger(value) || value < 1) {
1626
+ throw new CliError(`${label} must be a positive integer, got "${raw}".`, CLI_EXIT_CODES.validation);
1627
+ }
1628
+ return value;
1629
+ }
1630
+ function parseNonNegativeIntFlag(raw, flagName) {
1631
+ if (raw === undefined)
1632
+ return undefined;
1633
+ const label = flagName ? `--${flagName}` : "Value";
1634
+ if (!/^\d+$/.test(raw)) {
1635
+ throw new CliError(`${label} must be a non-negative integer, got "${raw}".`, CLI_EXIT_CODES.validation);
1636
+ }
1637
+ const value = Number.parseInt(raw, 10);
1638
+ if (!Number.isSafeInteger(value) || value < 0) {
1639
+ throw new CliError(`${label} must be a non-negative integer, got "${raw}".`, CLI_EXIT_CODES.validation);
1640
+ }
1641
+ return value;
1642
+ }
1643
+ function resolveRunAgent(flags, fallback) {
1644
+ const raw = getStringFlag(flags, "agent");
1645
+ if (!raw)
1646
+ return fallback;
1647
+ const normalizedRaw = raw.trim().toLowerCase();
1648
+ const normalized = normalizeOrchestrationAgent(raw);
1649
+ if (normalized === "router" && normalizedRaw !== "router") {
1650
+ const profiles = ORCHESTRATION_AGENT_PROFILES.join(", ");
1651
+ const hint = ORCHESTRATION_AGENT_PROFILES.find((p) => p.startsWith(normalizedRaw.slice(0, 3)));
1652
+ const suggestion = hint ? ` Did you mean "${hint}"?` : "";
1653
+ throw new CliError(`Invalid --agent target: ${raw}. Use ${profiles}.${suggestion}`, CLI_EXIT_CODES.validation);
1654
+ }
1655
+ return normalized;
1656
+ }
630
1657
  function getCommandHelp(command) {
631
1658
  if (command === "init") {
632
1659
  return `prompts-gpt init
633
1660
 
634
1661
  Usage:
635
- prompts-gpt init (--token <project-token> | --token-stdin | --token-prompt) [--api-url <url>] [--cwd <path>]
1662
+ prompts-gpt init [--token <project-token> | --token-stdin | --token-prompt] [--api-url <url>] [--cwd <path>]
636
1663
 
637
1664
  Why use it:
638
- Stores a project token in a local credentials file so day-one CLI usage does not require re-pasting secrets on every command.
1665
+ Stores a project token locally so you don't have to pass it on every command.
1666
+ If run without flags in an interactive terminal, it will prompt for the token.
639
1667
 
640
1668
  Options:
641
1669
  --token <token> Project API token. Must start with pgpt_.
642
- --token-stdin Read the project token from stdin so it does not end up in shell history.
1670
+ --token-stdin Read the project token from stdin (for CI/CD pipelines).
643
1671
  --token-prompt Prompt for the project token without echoing it.
644
1672
  --api-url <url> Custom API base URL for self-hosted instances.
645
- --cwd <path> Target project directory that will receive .prompts-gpt/.credentials.json.
1673
+ --cwd <path> Target project directory.
646
1674
  --help Show this command help.
1675
+
1676
+ Examples:
1677
+ prompts-gpt init # interactive prompt
1678
+ prompts-gpt init --token pgpt_abc123 # direct token
1679
+ printf '%s' "$TOKEN" | prompts-gpt init --token-stdin # CI/CD
1680
+ `;
1681
+ }
1682
+ if (command === "setup") {
1683
+ return `prompts-gpt setup
1684
+
1685
+ Usage:
1686
+ prompts-gpt setup [--prompt-file <path> | --prompt-files <a,b,c> | --manifest <path> | --prompt-dir <path>] [--agent codex|cursor|claude|copilot|router] [--provider-order <a,b,c>] [--timeout <seconds>] [--retry-count <n>] [--artifacts-dir <path>] [--codex-model <name>] [--cursor-model <name>] [--claude-model <name>] [--copilot-model <name>] [--allow-destructive-git] [--overwrite] [--json] [--cwd <path>]
1687
+
1688
+ Why use it:
1689
+ Scaffolds a generic local orchestration config so a repo can run prompts through Codex, Cursor, Claude Code, or Copilot with minimal repeated CLI flags.
1690
+
1691
+ Options:
1692
+ --prompt-file <path> Default single prompt file for \`prompts-gpt run\`.
1693
+ --prompt-files <list> Default batch prompt files for \`prompts-gpt run-batch\`.
1694
+ --manifest <path> Use this manifest as the batch source. Default: auto-detect .prompts-gpt/manifest.json
1695
+ --prompt-dir <path> Scan a prompt directory when you do not use .prompts-gpt.
1696
+ --agent <name> Default orchestration profile. Default: router
1697
+ --provider-order <list> Comma-separated router order such as codex,cursor,claude,copilot
1698
+ --timeout <seconds> Default timeout in seconds. Default: 900
1699
+ --retry-count <n> Retry count for spawn and timeout failures. Default: 0
1700
+ --artifacts-dir <path> Run artifact directory. Default: .scripts/runs
1701
+ --codex-model <name> Default model override for codex.
1702
+ --cursor-model <name> Default model override for cursor.
1703
+ --claude-model <name> Default model override for claude.
1704
+ --copilot-model <name> Default model override for copilot.
1705
+ --allow-destructive-git Write config with destructive git protection disabled.
1706
+ --overwrite Replace an existing config file.
1707
+ --json Print the generated config payload as JSON.
1708
+ --cwd <path> Project directory.
1709
+ --help Show this command help.
647
1710
  `;
648
1711
  }
649
1712
  if (command === "project") {
650
1713
  return `prompts-gpt project
651
1714
 
652
1715
  Usage:
653
- prompts-gpt project [--token <project-token> | --token-stdin | --token-prompt] [--api-url <url>] [--cwd <path>]
1716
+ prompts-gpt project [--json] [--token <project-token> | --token-stdin | --token-prompt] [--api-url <url>] [--cwd <path>]
654
1717
 
655
1718
  Why use it:
656
1719
  Verifies that the current local token resolves to the expected Prompts-GPT project before syncing files.
657
1720
 
658
1721
  Options:
1722
+ --json Print the full project payload as JSON.
659
1723
  --token <token> Override the saved local token for this command only.
660
1724
  --token-stdin Read the override token from stdin for this command only.
661
1725
  --token-prompt Prompt for the override token without echoing it.
@@ -673,6 +1737,10 @@ Usage:
673
1737
  Why use it:
674
1738
  Pulls prompt packs into local Markdown files so teams can review and use the same prompt assets inside their repository.
675
1739
 
1740
+ Data privacy:
1741
+ Downloads prompts from your prompts-gpt.com project library.
1742
+ No local files or repo content are uploaded.
1743
+
676
1744
  Options:
677
1745
  --query <text> Search query for the project prompt library.
678
1746
  --q <text> Short alias for --query.
@@ -699,6 +1767,11 @@ Usage:
699
1767
  Why use it:
700
1768
  Creates a new reusable prompt pack from a concrete developer goal, then optionally installs agent-readable files immediately.
701
1769
 
1770
+ Data privacy:
1771
+ The text you pass via --goal, --context, and --constraints is sent to prompts-gpt.com
1772
+ for AI-powered prompt generation. No local files or repo content are uploaded.
1773
+ Do not include PII, secrets, or confidential data in these flags.
1774
+
702
1775
  Options:
703
1776
  --goal <text> Required. The task to generate a prompt pack for.
704
1777
  --context <text> Extra project or stack context for the generator.
@@ -711,13 +1784,19 @@ Options:
711
1784
  --out <dir> Output directory inside the current project. Default: .prompts-gpt
712
1785
  --overwrite Replace an existing generated Markdown file.
713
1786
  --agent <targets> Sync agent files for specific targets after generation.
714
- --sync-agents Sync all agent files after generation.
1787
+ When set, also writes agent-specific files (AGENTS.md, .cursor/rules, etc.).
1788
+ --sync-agents Sync all agent files after generation (same as --agent all).
715
1789
  --token <token> Override the saved local token for this command only.
716
1790
  --token-stdin Read the override token from stdin for this command only.
717
1791
  --token-prompt Prompt for the override token without echoing it.
718
1792
  --api-url <url> Override the saved API base URL for this command only.
719
1793
  --cwd <path> Project directory to inspect for local credentials and output files.
720
1794
  --help Show this command help.
1795
+
1796
+ Examples:
1797
+ prompts-gpt generate --goal "Review PRs for security issues"
1798
+ prompts-gpt generate --goal "Write unit tests" --sync-agents
1799
+ prompts-gpt generate --goal "Add error handling" --agent cursor,codex
721
1800
  `;
722
1801
  }
723
1802
  if (command === "sync") {
@@ -729,6 +1808,13 @@ Usage:
729
1808
  Why use it:
730
1809
  Gives teams a one-command path to refresh Markdown prompts, agent files, and the local manifest from the same source of truth.
731
1810
 
1811
+ Data privacy:
1812
+ Prompts are downloaded from your prompts-gpt.com project library.
1813
+ When --goal is used, the text you pass is sent to prompts-gpt.com for AI generation.
1814
+ No local files or repo content are uploaded — only explicit flag values are transmitted.
1815
+ Do not include PII, secrets, or confidential data in --goal, --context, or --constraints.
1816
+ Use --dry-run to preview what would be synced without sending or writing anything.
1817
+
732
1818
  Options:
733
1819
  --goal <text> Generate one prompt pack before syncing.
734
1820
  --generated-only Skip library pull and sync only the generated prompt pack from --goal.
@@ -754,31 +1840,216 @@ Options:
754
1840
  --api-url <url> Override the saved API base URL for this command only.
755
1841
  --cwd <path> Project directory to inspect for local credentials and output files.
756
1842
  --help Show this command help.
1843
+
1844
+ Examples:
1845
+ prompts-gpt sync # pull all prompts + sync agent files
1846
+ prompts-gpt sync --goal "Add error handling" # generate + pull + sync
1847
+ prompts-gpt sync --dry-run # preview without writing
1848
+ prompts-gpt sync --agent cursor,codex # sync only specific agents
757
1849
  `;
758
1850
  }
759
- if (command === "install-agents") {
760
- return `prompts-gpt install-agents
1851
+ if (command === "run") {
1852
+ return `prompts-gpt run
761
1853
 
762
1854
  Usage:
763
- prompts-gpt install-agents [--query <text> | --q <text>] [--category <name>] [--tool <name>] [--output-type <name>] [--limit <n>] [--agent <targets>] [--dry-run] [--token <project-token> | --token-stdin | --token-prompt] [--api-url <url>] [--cwd <path>]
1855
+ prompts-gpt run [-f <path>] [--agent <name>] [--model <name>] [--timeout <seconds>] [--dry-run]
764
1856
 
765
1857
  Why use it:
766
- Refreshes agent-specific instruction files from the current prompt library without rewriting the Markdown prompt directory.
1858
+ Executes one local prompt file with the built-in provider adapter runtime and writes run artifacts.
1859
+ If \`--prompt-file\` is omitted and only one prompt exists locally, it is auto-selected.
767
1860
 
768
1861
  Options:
769
- --query <text> Search query for the project prompt library.
770
- --q <text> Short alias for --query.
771
- --category <name> Filter prompts by category.
772
- --tool <name> Filter prompts by supported tool.
773
- --output-type <name> Filter prompts by output type.
774
- --limit <n> Positive integer limit for returned prompts.
775
- --agent <targets> Comma-separated agent targets. Default: all
776
- --dry-run Preview what would be installed without writing any files.
777
- --token <token> Override the saved local token for this command only.
778
- --token-stdin Read the override token from stdin for this command only.
779
- --token-prompt Prompt for the override token without echoing it.
780
- --api-url <url> Override the saved API base URL for this command only.
781
- --cwd <path> Project directory to inspect for local credentials and agent files.
1862
+ -f, --prompt-file <path> Optional explicit prompt Markdown or text file.
1863
+ --agent <name> Orchestration profile. Default from ${DEFAULT_RUN_CONFIG_PATH} or router.
1864
+ --model <name> Optional model override for the selected provider.
1865
+ --timeout <seconds> Execution timeout in seconds.
1866
+ --artifacts-dir <path> Override run artifacts directory.
1867
+ --run-id <id> Explicit run ID for artifact folder naming.
1868
+ --sandbox <mode> Codex sandbox mode. Default: workspace-write.
1869
+ --no-approve-mcps Disable Cursor MCP auto-approval flag.
1870
+ --background Run as a background agent (Cursor cloud agent mode).
1871
+ --permission-mode <mode> Claude Code permission mode. Default: acceptEdits.
1872
+ --dry-run Preview the execution parameters without actually running.
1873
+ --json Print machine-readable JSON output.
1874
+ --cwd <path> Project directory.
1875
+ --help Show this command help.
1876
+ `;
1877
+ }
1878
+ if (command === "run-batch") {
1879
+ return `prompts-gpt run-batch
1880
+
1881
+ Usage:
1882
+ prompts-gpt run-batch [--manifest <path> | --prompt-files <a,b,c>] [--agent <name>] [--model <name>] [--timeout <seconds>]
1883
+
1884
+ Why use it:
1885
+ Executes multiple prompt files in sequence using the configured prompt source, a manifest, or an explicit file list.
1886
+
1887
+ Options:
1888
+ --manifest <path> Prompt manifest path. Overrides config batch defaults.
1889
+ --prompt-files <list> Comma-separated prompt files to run.
1890
+ --agent <name> Orchestration profile. Default from ${DEFAULT_RUN_CONFIG_PATH} or router.
1891
+ --model <name> Optional model override for all runs.
1892
+ --timeout <seconds> Execution timeout in seconds per prompt.
1893
+ --artifacts-dir <path> Override run artifacts directory.
1894
+ --json Print machine-readable JSON output.
1895
+ --cwd <path> Project directory.
1896
+ --help Show this command help.
1897
+
1898
+ Examples:
1899
+ prompts-gpt run-batch --manifest .prompts-gpt/manifest.json
1900
+ prompts-gpt run-batch --prompt-files .prompts-gpt/review.md,.prompts-gpt/tests.md
1901
+ `;
1902
+ }
1903
+ if (command === "sweep") {
1904
+ return `prompts-gpt sweep
1905
+
1906
+ Usage:
1907
+ prompts-gpt sweep [-f <path>] [-n <count>] [--agent <name>] [--model <name>] [--dry-run]
1908
+
1909
+ Why use it:
1910
+ Runs the same prompt N times, feeding each iteration's summary into the next.
1911
+ Includes pre-flight checks, safety guards, SIGTERM handling, and progress monitoring.
1912
+
1913
+ When --prompt-file is omitted and only one .prompts-gpt/sweeps/*.md file exists,
1914
+ it is auto-selected. The iteration count defaults to the \`iterations:\` value
1915
+ in the sweep file's YAML frontmatter (or 1 if not specified).
1916
+
1917
+ Options:
1918
+ -f, --prompt-file <path> Prompt file to sweep. Auto-detects local sweeps if omitted.
1919
+ -n, --iterations <n> Number of iterations. Default: from frontmatter or 1.
1920
+ --agent <name> Orchestration profile. Default from config or router.
1921
+ --model <name> Model override for the selected provider.
1922
+ --iteration-timeout <secs> Timeout per iteration in seconds. Default: 5400 (90 min)
1923
+ --max-retries <n> Max retries per iteration on spawn/timeout. Default: 2
1924
+ --phase <name> Optional phase label injected into the prompt.
1925
+ --artifacts-dir <path> Override run artifacts directory.
1926
+ --run-id <id> Explicit run ID for artifact folder naming.
1927
+ --sandbox <mode> Codex sandbox mode. Default: workspace-write
1928
+ --no-approve-mcps Disable Cursor MCP auto-approval flag.
1929
+ --background Run as a background agent (Cursor cloud agent mode).
1930
+ --permission-mode <mode> Claude Code permission mode. Default: acceptEdits
1931
+ --max-run-dirs <n> Max artifact directories to keep. Default: 20
1932
+ --summary-lines <n> Lines of summary to extract per iteration. Default: 40
1933
+ --dry-run Preview what the sweep would do without executing.
1934
+ --json Print machine-readable JSON output.
1935
+ --cwd <path> Project directory.
1936
+ --help Show this command help.
1937
+
1938
+ Examples:
1939
+ prompts-gpt sweep # auto-pick local sweep
1940
+ prompts-gpt sweep -f .prompts-gpt/sweeps/sdk-hardening.md # explicit file
1941
+ prompts-gpt sweep -f .prompts-gpt/sweeps/design.md -n 5 # 5 iterations
1942
+ prompts-gpt sweep --dry-run # preview without running
1943
+ `;
1944
+ }
1945
+ if (command === "list") {
1946
+ return `prompts-gpt list
1947
+
1948
+ Usage:
1949
+ prompts-gpt list [--json] [--cwd <path>]
1950
+
1951
+ Why use it:
1952
+ Shows all prompt packs, sweep files, and agent integrations available in the workspace.
1953
+ Gives runnable commands for each entry so you can copy-paste them directly.
1954
+
1955
+ Options:
1956
+ --json Print as JSON.
1957
+ --cwd <path> Project directory.
1958
+ --help Show this command help.
1959
+ `;
1960
+ }
1961
+ if (command === "status") {
1962
+ return `prompts-gpt status
1963
+
1964
+ Usage:
1965
+ prompts-gpt status [--json] [--cwd <path>]
1966
+
1967
+ Why use it:
1968
+ Shows workspace readiness at a glance: credentials, config, manifest,
1969
+ prompts, sweeps, agent integrations, and available providers.
1970
+
1971
+ Options:
1972
+ --json Print as JSON.
1973
+ --cwd <path> Project directory.
1974
+ --help Show this command help.
1975
+ `;
1976
+ }
1977
+ if (command === "validate") {
1978
+ return `prompts-gpt validate
1979
+
1980
+ Usage:
1981
+ prompts-gpt validate [--json] [--cwd <path>]
1982
+
1983
+ Why use it:
1984
+ Validates the .prompts-gpt/config.json file and reports errors and
1985
+ warnings without running any commands.
1986
+
1987
+ Options:
1988
+ --json Print as JSON.
1989
+ --cwd <path> Project directory.
1990
+ --help Show this command help.
1991
+ `;
1992
+ }
1993
+ if (command === "providers") {
1994
+ return `prompts-gpt providers
1995
+
1996
+ Usage:
1997
+ prompts-gpt providers [--json] [--cwd <path>]
1998
+
1999
+ Why use it:
2000
+ Shows provider CLI detection details for codex, cursor, claude, and copilot so you can decide the best router order before running setup.
2001
+ `;
2002
+ }
2003
+ if (command === "doctor") {
2004
+ return `prompts-gpt doctor
2005
+
2006
+ Usage:
2007
+ prompts-gpt doctor [--json] [--cwd <path>]
2008
+
2009
+ Why use it:
2010
+ Checks runtime prerequisites, provider availability, prompt-source readiness, and run config health.
2011
+ `;
2012
+ }
2013
+ if (command === "load-config") {
2014
+ return `prompts-gpt load-config
2015
+
2016
+ Usage:
2017
+ prompts-gpt load-config [--agent <targets>] [--out <dir>] [--json] [--token <project-token> | --token-stdin | --token-prompt] [--api-url <url>] [--cwd <path>]
2018
+
2019
+ Why use it:
2020
+ One command to pull all prompts, agent files, and config from Prompts Studio
2021
+ into your local workspace. Replaces the multi-step init + sync + setup flow
2022
+ for users who manage their prompt library in the Prompts Studio web app.
2023
+
2024
+ Data privacy:
2025
+ Downloads prompts and project config from your prompts-gpt.com project.
2026
+ No local files or repo content are uploaded.
2027
+
2028
+ Options:
2029
+ --agent <targets> Comma-separated agent targets. Default: all
2030
+ --out <dir> Output directory. Default: .prompts-gpt
2031
+ --json Print machine-readable JSON output.
2032
+ --token <token> Override saved token.
2033
+ --token-stdin Read token from stdin.
2034
+ --token-prompt Prompt for token.
2035
+ --api-url <url> Override API URL.
2036
+ --cwd <path> Project directory.
2037
+ --help Show this command help.
2038
+ `;
2039
+ }
2040
+ if (command === "quickstart") {
2041
+ return `prompts-gpt quickstart
2042
+
2043
+ Usage:
2044
+ prompts-gpt quickstart [--json] [--cwd <path>]
2045
+
2046
+ Why use it:
2047
+ Walks through setup in one command — checks credentials, creates config, detects providers,
2048
+ and shows runnable commands. The fastest path from install to first run.
2049
+
2050
+ Options:
2051
+ --json Print machine-readable JSON output.
2052
+ --cwd <path> Project directory.
782
2053
  --help Show this command help.
783
2054
  `;
784
2055
  }
@@ -801,6 +2072,41 @@ Why use it:
801
2072
  Shows focused usage for the exact command you are trying to run so shell users do not have to scan the full command list.
802
2073
  `;
803
2074
  }
2075
+ async function readSweepIterationsFromFrontmatter(filePath) {
2076
+ try {
2077
+ const { readFile: fsRead } = await import("node:fs/promises");
2078
+ const content = await fsRead(filePath, "utf8");
2079
+ const match = content.match(/iterations:\s*(\d+)/i);
2080
+ if (match) {
2081
+ const val = parseInt(match[1], 10);
2082
+ if (val > 0 && val <= 50)
2083
+ return val;
2084
+ }
2085
+ }
2086
+ catch { /* skip */ }
2087
+ return null;
2088
+ }
2089
+ function buildGenerateInput(flags) {
2090
+ return {
2091
+ goal: getStringFlag(flags, "goal") || "",
2092
+ context: sanitizeGenerateInput(getStringFlag(flags, "context") || ""),
2093
+ constraints: sanitizeGenerateInput(getStringFlag(flags, "constraints") || ""),
2094
+ desiredOutput: getStringFlag(flags, "desired-output") || "A reusable prompt pack saved as Markdown.",
2095
+ tool: getStringFlag(flags, "tool") || "Codex",
2096
+ mode: getStringFlag(flags, "mode") || "implement",
2097
+ artifactType: getStringFlag(flags, "artifact-type") || "prompt-file",
2098
+ includeWebSearch: Boolean(flags["web-search"]),
2099
+ };
2100
+ }
2101
+ function sanitizeGenerateInput(value) {
2102
+ 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_***");
2103
+ }
2104
+ function printDataTransmissionNotice(command, input) {
2105
+ 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.`);
2106
+ console.log("[notice] Do not include PII, secrets, or confidential data in these flags.");
2107
+ console.log(`[notice] No local files or repo content are uploaded. Only the explicit flag values for \`${command}\` are transmitted.`);
2108
+ console.log("");
2109
+ }
804
2110
  main().catch((error) => {
805
2111
  if (error instanceof CliError) {
806
2112
  console.error(error.message);
@@ -821,9 +2127,55 @@ main().catch((error) => {
821
2127
  : CLI_EXIT_CODES.general;
822
2128
  return;
823
2129
  }
824
- console.error(error instanceof Error ? error.message : String(error));
2130
+ const msg = error instanceof Error ? error.message : String(error);
2131
+ console.error(msg.replace(/pgpt_[a-zA-Z0-9]{4,}/g, "pgpt_***"));
825
2132
  process.exitCode = CLI_EXIT_CODES.general;
826
2133
  });
2134
+ async function extractRunDiagnostics(logFile, provider, model) {
2135
+ const diagnostics = [];
2136
+ try {
2137
+ const { readFile: fsReadFile } = await import("node:fs/promises");
2138
+ const log = await fsReadFile(logFile, "utf8");
2139
+ const errorLines = log.split("\n").filter((line) => line.startsWith("ERROR:") || line.includes('"error"'));
2140
+ for (const line of errorLines.slice(0, 5)) {
2141
+ if (line.includes("not supported")) {
2142
+ const modelMatch = line.match(/The '([^']+)' model is not supported/);
2143
+ const badModel = modelMatch?.[1] ?? model;
2144
+ diagnostics.push(`Model "${badModel}" is not supported by ${provider} with your account.`);
2145
+ diagnostics.push(`Fix: prompts-gpt run --model o4-mini`);
2146
+ diagnostics.push(`Or: prompts-gpt run --agent claude`);
2147
+ break;
2148
+ }
2149
+ if (line.includes("unauthorized") || line.includes("authentication")) {
2150
+ diagnostics.push(`Authentication failed for ${provider}. Check your ${provider} account login.`);
2151
+ diagnostics.push(`Fix: Run the ${provider} CLI directly to verify auth: ${provider} --version`);
2152
+ break;
2153
+ }
2154
+ if (line.includes("rate_limit") || line.includes("429")) {
2155
+ diagnostics.push(`Rate limited by ${provider}. Wait and try again.`);
2156
+ break;
2157
+ }
2158
+ }
2159
+ if (diagnostics.length === 0 && errorLines.length > 0) {
2160
+ const firstError = errorLines[0].slice(0, 200);
2161
+ diagnostics.push(`Provider error: ${firstError}`);
2162
+ }
2163
+ if (diagnostics.length === 0) {
2164
+ diagnostics.push(`${provider} exited with code 1. Check the log for details:`);
2165
+ diagnostics.push(` cat ${logFile}`);
2166
+ diagnostics.push(`Try a different provider: prompts-gpt run --agent claude`);
2167
+ }
2168
+ }
2169
+ catch {
2170
+ diagnostics.push(`Could not read log file. Check: ${logFile}`);
2171
+ }
2172
+ return diagnostics;
2173
+ }
2174
+ function warnOnConfigIssues(config) {
2175
+ for (const w of config.configWarnings) {
2176
+ console.error(`[config warning] ${w}`);
2177
+ }
2178
+ }
827
2179
  function formatApiError(error) {
828
2180
  const lines = [error.message];
829
2181
  for (const [field, messages] of Object.entries(error.fieldErrors ?? {})) {
@@ -839,4 +2191,21 @@ function formatApiError(error) {
839
2191
  }
840
2192
  return lines.join("\n");
841
2193
  }
2194
+ function formatTokenUsage(usage) {
2195
+ const parts = [
2196
+ `total=${usage.totalTokens.toLocaleString()}`,
2197
+ `input=${usage.inputTokens.toLocaleString()}`,
2198
+ `output=${usage.outputTokens.toLocaleString()}`,
2199
+ ];
2200
+ if (usage.reasoningTokens > 0) {
2201
+ parts.push(`reasoning=${usage.reasoningTokens.toLocaleString()}`);
2202
+ }
2203
+ if (usage.cacheReadTokens > 0) {
2204
+ parts.push(`cache-read=${usage.cacheReadTokens.toLocaleString()}`);
2205
+ }
2206
+ if (usage.cacheWriteTokens > 0) {
2207
+ parts.push(`cache-write=${usage.cacheWriteTokens.toLocaleString()}`);
2208
+ }
2209
+ return parts.join(" | ");
2210
+ }
842
2211
  //# sourceMappingURL=cli.js.map