@prompts-gpt/client 0.2.2 → 0.2.3

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 { 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";
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", "version", "help"];
16
16
  const MAX_STDIN_TOKEN_LENGTH = 4_096;
17
17
  class CliError extends Error {
18
18
  exitCode;
@@ -28,10 +28,14 @@ async function main() {
28
28
  const argv = process.argv.slice(2);
29
29
  if (argv.length === 0) {
30
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("");
31
36
  return;
32
37
  }
33
- const normalizedArgv = normalizeLegacyFlags(argv);
34
- const first = normalizedArgv[0];
38
+ const first = argv[0];
35
39
  if (first === "--help") {
36
40
  printHelp();
37
41
  return;
@@ -41,11 +45,11 @@ async function main() {
41
45
  return;
42
46
  }
43
47
  if (first === "help") {
44
- handleHelpCommand(normalizedArgv.slice(1));
48
+ handleHelpCommand(argv.slice(1));
45
49
  return;
46
50
  }
47
51
  if (first === "version") {
48
- if (normalizedArgv.length > 1) {
52
+ if (argv.length > 1) {
49
53
  throw new CliError("The `version` command does not accept additional arguments.", CLI_EXIT_CODES.usage, {
50
54
  helpCommand: "version",
51
55
  });
@@ -62,7 +66,7 @@ async function main() {
62
66
  helpCommand: "help",
63
67
  });
64
68
  }
65
- const flags = parseCommandFlags(command, normalizedArgv.slice(1));
69
+ const flags = parseCommandFlags(command, argv.slice(1));
66
70
  if (Boolean(flags.help)) {
67
71
  printHelp(command);
68
72
  return;
@@ -70,6 +74,466 @@ async function main() {
70
74
  await runCommand(command, flags);
71
75
  }
72
76
  async function runCommand(command, flags) {
77
+ if (command === "providers") {
78
+ const cwd = getResolvedCwd(flags);
79
+ const providers = await detectProviders(cwd);
80
+ if (Boolean(flags.json)) {
81
+ console.log(JSON.stringify({ cwd, providers }, null, 2));
82
+ return;
83
+ }
84
+ console.log(`Workspace: ${cwd}`);
85
+ 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}` : ""}`);
87
+ }
88
+ return;
89
+ }
90
+ if (command === "setup") {
91
+ const cwd = getResolvedCwd(flags);
92
+ const providerOrder = getStringFlag(flags, "provider-order")
93
+ ?.split(",")
94
+ .map((item) => item.trim())
95
+ .filter(Boolean);
96
+ validateProviderOrderFlag(providerOrder);
97
+ const promptFile = getStringFlag(flags, "prompt-file");
98
+ const promptFiles = getStringFlag(flags, "prompt-files")
99
+ ?.split(",")
100
+ .map((item) => item.trim())
101
+ .filter(Boolean);
102
+ const result = await initRunConfig({
103
+ cwd,
104
+ promptFile,
105
+ manifestPath: getStringFlag(flags, "manifest"),
106
+ promptFiles,
107
+ promptDir: getStringFlag(flags, "prompt-dir"),
108
+ defaultAgent: resolveRunAgent(flags, "router"),
109
+ providerOrder,
110
+ artifactsDir: getStringFlag(flags, "artifacts-dir"),
111
+ timeoutSeconds: parsePositiveIntFlag(getStringFlag(flags, "timeout"), "timeout"),
112
+ retryCount: parseNonNegativeIntFlag(getStringFlag(flags, "retry-count"), "retry-count"),
113
+ disallowDestructiveGit: Boolean(flags["allow-destructive-git"]) ? false : true,
114
+ modelOverrides: {
115
+ codex: getStringFlag(flags, "codex-model"),
116
+ cursor: getStringFlag(flags, "cursor-model"),
117
+ claude: getStringFlag(flags, "claude-model"),
118
+ copilot: getStringFlag(flags, "copilot-model"),
119
+ },
120
+ overwrite: Boolean(flags.overwrite),
121
+ });
122
+ if (Boolean(flags.json)) {
123
+ console.log(JSON.stringify(result, null, 2));
124
+ return;
125
+ }
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(", ")}`);
129
+ if (result.sourceSummary.defaultPromptFile) {
130
+ console.log(`Default prompt file: ${result.sourceSummary.defaultPromptFile}`);
131
+ }
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}`);
144
+ }
145
+ console.log("Next steps:");
146
+ console.log(` prompts-gpt run${result.sourceSummary.defaultPromptFile ? "" : " --prompt-file <path>"}`);
147
+ console.log(" prompts-gpt run-batch");
148
+ console.log(" prompts-gpt list — see all runnable assets");
149
+ console.log(" prompts-gpt status — check workspace readiness");
150
+ console.log(" prompts-gpt validate — validate your config");
151
+ return;
152
+ }
153
+ if (command === "doctor") {
154
+ const cwd = getResolvedCwd(flags);
155
+ const report = await doctor(cwd);
156
+ if (Boolean(flags.json)) {
157
+ console.log(JSON.stringify(report, null, 2));
158
+ return;
159
+ }
160
+ console.log(`Workspace: ${report.cwd}`);
161
+ console.log(`Node: ${report.nodeVersion} | OS: ${report.osPlatform}/${report.osArch} | CPUs: ${report.cpuCount}`);
162
+ console.log(`Config: ${report.configFound ? "found" : "not found"} (${report.configPath})`);
163
+ for (const provider of report.providers) {
164
+ console.log(`${provider.provider}: ${provider.available ? "available" : "missing"} | bin=${provider.bin}${provider.version ? ` | version=${provider.version}` : ""}`);
165
+ if (!provider.available) {
166
+ console.log(` hint: ${provider.installHint}`);
167
+ }
168
+ }
169
+ for (const note of report.notes) {
170
+ console.log(`note: ${note}`);
171
+ }
172
+ return;
173
+ }
174
+ if (command === "list") {
175
+ const cwd = getResolvedCwd(flags);
176
+ const assets = await discoverWorkspaceAssets(cwd);
177
+ if (Boolean(flags.json)) {
178
+ console.log(JSON.stringify(assets, null, 2));
179
+ return;
180
+ }
181
+ console.log(`Workspace: ${cwd}`);
182
+ console.log("");
183
+ if (assets.prompts.length > 0) {
184
+ console.log(`Prompt packs (${assets.prompts.length}):`);
185
+ for (const p of assets.prompts) {
186
+ console.log(` ${p.slug} — ${p.title} [${p.source}]`);
187
+ console.log(` file: .prompts-gpt/${p.file}`);
188
+ console.log(` run: prompts-gpt run --prompt-file .prompts-gpt/${p.file}`);
189
+ }
190
+ console.log("");
191
+ }
192
+ else {
193
+ console.log("Prompt packs: none found");
194
+ console.log(" Run `prompts-gpt sync` to pull prompt packs, or `prompts-gpt pull` to download them.");
195
+ console.log("");
196
+ }
197
+ if (assets.sweeps.length > 0) {
198
+ console.log(`Sweep prompts (${assets.sweeps.length}):`);
199
+ for (const s of assets.sweeps) {
200
+ console.log(` ${s.name}`);
201
+ console.log(` file: ${s.file}`);
202
+ console.log(` sweep: prompts-gpt sweep --prompt-file ${s.file}`);
203
+ }
204
+ console.log("");
205
+ }
206
+ else {
207
+ console.log("Sweep prompts: none found");
208
+ console.log(" Create .prompts-gpt/sweeps/<name>.md to add sweep prompts.");
209
+ console.log("");
210
+ }
211
+ if (assets.agents.length > 0) {
212
+ console.log(`Agent integrations (${assets.agents.length}):`);
213
+ for (const a of assets.agents) {
214
+ console.log(` ${a.target} — ${a.file}`);
215
+ }
216
+ console.log("");
217
+ }
218
+ else {
219
+ console.log("Agent integrations: none synced");
220
+ console.log(" Run `prompts-gpt sync` to generate agent files.");
221
+ console.log("");
222
+ }
223
+ console.log(`Config: ${assets.configFound ? "found" : "not found — run `prompts-gpt setup`"}`);
224
+ console.log(`Manifest: ${assets.manifestFound ? "found" : "not found — run `prompts-gpt sync`"}`);
225
+ console.log(`Credentials: ${assets.credentialsFound ? "found" : "not found — run `prompts-gpt init --token <token>`"}`);
226
+ return;
227
+ }
228
+ if (command === "status") {
229
+ const cwd = getResolvedCwd(flags);
230
+ const assets = await discoverWorkspaceAssets(cwd);
231
+ const providers = await detectProviders(cwd);
232
+ const availableProviders = providers.filter((p) => p.available);
233
+ if (Boolean(flags.json)) {
234
+ console.log(JSON.stringify({ cwd, assets, providers }, null, 2));
235
+ return;
236
+ }
237
+ console.log(`Workspace: ${cwd}`);
238
+ console.log("");
239
+ console.log("Readiness:");
240
+ console.log(` Credentials: ${assets.credentialsFound ? "✓" : "✗ — run \`prompts-gpt init --token <token>\`"}`);
241
+ console.log(` Config: ${assets.configFound ? "✓" : "✗ — run \`prompts-gpt setup\`"}`);
242
+ console.log(` Manifest: ${assets.manifestFound ? "✓" : "✗ — run \`prompts-gpt sync\`"}`);
243
+ console.log(` Prompts: ${assets.prompts.length > 0 ? `✓ (${assets.prompts.length})` : "✗ — none found"}`);
244
+ console.log(` Sweeps: ${assets.sweeps.length > 0 ? `✓ (${assets.sweeps.length})` : "— none found"}`);
245
+ 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"}`);
247
+ console.log("");
248
+ if (assets.prompts.length > 0 && availableProviders.length > 0) {
249
+ console.log("Ready to run:");
250
+ console.log(` prompts-gpt run --prompt-file .prompts-gpt/${assets.prompts[0].file}`);
251
+ if (assets.sweeps.length > 0) {
252
+ console.log(` prompts-gpt sweep --prompt-file ${assets.sweeps[0].file}`);
253
+ }
254
+ }
255
+ else {
256
+ console.log("Next steps:");
257
+ let step = 1;
258
+ if (!assets.credentialsFound)
259
+ console.log(` ${step++}. prompts-gpt init --token <project-token>`);
260
+ if (assets.prompts.length === 0)
261
+ console.log(` ${step++}. prompts-gpt sync`);
262
+ if (availableProviders.length === 0)
263
+ console.log(` ${step++}. Install a provider CLI (codex, cursor agent, claude, copilot)`);
264
+ if (!assets.configFound)
265
+ console.log(` ${step++}. prompts-gpt setup`);
266
+ }
267
+ return;
268
+ }
269
+ if (command === "validate") {
270
+ const cwd = getResolvedCwd(flags);
271
+ const result = await validateRunConfig(cwd);
272
+ if (Boolean(flags.json)) {
273
+ console.log(JSON.stringify(result, null, 2));
274
+ return;
275
+ }
276
+ console.log(`Config: ${result.configPath}`);
277
+ console.log(`Valid: ${result.valid ? "yes" : "no"}`);
278
+ for (const e of result.errors) {
279
+ console.log(` error: ${e}`);
280
+ }
281
+ for (const w of result.warnings) {
282
+ console.log(` warning: ${w}`);
283
+ }
284
+ if (result.valid && result.errors.length === 0 && result.warnings.length === 0) {
285
+ console.log(" No issues found.");
286
+ }
287
+ return;
288
+ }
289
+ if (command === "run") {
290
+ const cwd = getResolvedCwd(flags);
291
+ const promptFile = getStringFlag(flags, "prompt-file");
292
+ const config = await loadRunConfig(cwd);
293
+ warnOnConfigIssues(config);
294
+ if (!promptFile && !Boolean(flags.json)) {
295
+ if (!config.defaultPromptFile && config.batchDefaults.promptFiles.length === 0 && !config.batchDefaults.manifestPath) {
296
+ 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");
299
+ if (assets.prompts.length > 0) {
300
+ console.log("Prompt packs:");
301
+ for (const p of assets.prompts) {
302
+ console.log(` prompts-gpt run --prompt-file .prompts-gpt/${p.file}`);
303
+ }
304
+ }
305
+ if (assets.sweeps.length > 0) {
306
+ console.log("\nSweep prompts:");
307
+ for (const s of assets.sweeps) {
308
+ console.log(` prompts-gpt sweep --prompt-file ${s.file}`);
309
+ }
310
+ }
311
+ console.log("\nOr set a default: prompts-gpt setup --prompt-file <path>");
312
+ return;
313
+ }
314
+ }
315
+ }
316
+ const agent = resolveRunAgent(flags, config.defaultAgent);
317
+ const result = await runPrompt({
318
+ cwd,
319
+ promptFile,
320
+ agent,
321
+ model: getStringFlag(flags, "model"),
322
+ timeoutSeconds: parsePositiveIntFlag(getStringFlag(flags, "timeout"), "timeout"),
323
+ artifactsDir: getStringFlag(flags, "artifacts-dir"),
324
+ runId: getStringFlag(flags, "run-id"),
325
+ approveMcps: !Boolean(flags["no-approve-mcps"]),
326
+ sandboxMode: getStringFlag(flags, "sandbox"),
327
+ background: Boolean(flags.background),
328
+ permissionMode: getStringFlag(flags, "permission-mode"),
329
+ });
330
+ if (Boolean(flags.json)) {
331
+ console.log(JSON.stringify(result, null, 2));
332
+ return;
333
+ }
334
+ const agentLabel = getStringFlag(flags, "agent") ? result.provider : `${result.provider} (auto-selected)`;
335
+ console.log(`Run ID: ${result.runId}`);
336
+ console.log(`Provider: ${agentLabel} | Model: ${result.model}`);
337
+ console.log(`Exit code: ${result.exitCode}`);
338
+ console.log(`Duration: ${formatDuration(result.durationMs)}`);
339
+ console.log(`Run dir: ${result.runDir}`);
340
+ console.log(`Summary: ${result.summaryFile}`);
341
+ console.log(`Log: ${result.logFile}`);
342
+ return;
343
+ }
344
+ if (command === "run-batch") {
345
+ const cwd = getResolvedCwd(flags);
346
+ const promptFiles = getStringFlag(flags, "prompt-files")
347
+ ?.split(",")
348
+ .map((item) => item.trim())
349
+ .filter(Boolean);
350
+ const config = await loadRunConfig(cwd);
351
+ warnOnConfigIssues(config);
352
+ if (!promptFiles && !getStringFlag(flags, "manifest") && !Boolean(flags.json)) {
353
+ if (!config.batchDefaults.manifestPath && config.batchDefaults.promptFiles.length === 0) {
354
+ const assets = await discoverWorkspaceAssets(cwd);
355
+ if (assets.prompts.length > 0) {
356
+ console.log("No batch source configured. Available prompt packs:\n");
357
+ const fileList = assets.prompts.map((p) => `.prompts-gpt/${p.file}`).join(",");
358
+ console.log(` prompts-gpt run-batch --prompt-files ${fileList}`);
359
+ if (assets.manifestFound) {
360
+ console.log(` prompts-gpt run-batch --manifest .prompts-gpt/manifest.json`);
361
+ }
362
+ console.log("\nOr configure batch defaults: prompts-gpt setup");
363
+ return;
364
+ }
365
+ }
366
+ }
367
+ const agent = resolveRunAgent(flags, config.defaultAgent);
368
+ const result = await runBatch({
369
+ cwd,
370
+ manifestPath: getStringFlag(flags, "manifest"),
371
+ promptFiles,
372
+ agent,
373
+ model: getStringFlag(flags, "model"),
374
+ timeoutSeconds: parsePositiveIntFlag(getStringFlag(flags, "timeout"), "timeout"),
375
+ artifactsDir: getStringFlag(flags, "artifacts-dir"),
376
+ });
377
+ if (Boolean(flags.json)) {
378
+ console.log(JSON.stringify(result, null, 2));
379
+ return;
380
+ }
381
+ console.log(`Batch complete: total=${result.total} success=${result.success} failed=${result.failed}`);
382
+ for (const [idx, run] of result.results.entries()) {
383
+ 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)})`);
385
+ }
386
+ return;
387
+ }
388
+ if (command === "sweep") {
389
+ const cwd = getResolvedCwd(flags);
390
+ const sweepPromptFile = getStringFlag(flags, "prompt-file");
391
+ const config = await loadRunConfig(cwd);
392
+ warnOnConfigIssues(config);
393
+ 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}`);
400
+ }
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
+ }
406
+ }
407
+ console.log("\nOr set a default: prompts-gpt setup --prompt-file <path>");
408
+ return;
409
+ }
410
+ }
411
+ }
412
+ const agent = resolveRunAgent(flags, config.defaultAgent);
413
+ const onProgress = Boolean(flags.json)
414
+ ? undefined
415
+ : (event) => {
416
+ if (event.type === "preflight") {
417
+ 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}`);
420
+ for (const w of report.warnings)
421
+ console.log(`[preflight] warning: ${w}`);
422
+ }
423
+ else if (event.type === "iteration_start") {
424
+ console.log(`\n--- Iteration ${event.iteration}/${event.total} ---`);
425
+ }
426
+ else if (event.type === "iteration_end") {
427
+ const elapsed = formatDuration(event.durationMs);
428
+ console.log(`--- Iteration ${event.iteration} ${event.status} (${elapsed}) ---`);
429
+ }
430
+ else if (event.type === "attempt_retry") {
431
+ console.log(`[retry] iteration ${event.iteration}, attempt ${event.attempt}/${event.maxAttempts}, backoff ${event.backoffMs}ms`);
432
+ }
433
+ else if (event.type === "summary") {
434
+ const preview = event.lines.slice(0, 5).join("\n");
435
+ console.log(`[summary] iteration ${event.iteration}:\n${preview}${event.lines.length > 5 ? `\n ... (${event.lines.length - 5} more lines)` : ""}`);
436
+ }
437
+ else if (event.type === "sweep_end") {
438
+ console.log(`\nSweep complete: ${event.result.succeeded}/${event.result.totalIterations} succeeded in ${formatDuration(event.result.totalDurationMs)}`);
439
+ }
440
+ };
441
+ const result = await sweepPrompt({
442
+ cwd,
443
+ promptFile: getStringFlag(flags, "prompt-file"),
444
+ agent,
445
+ model: getStringFlag(flags, "model"),
446
+ iterations: parsePositiveIntFlag(getStringFlag(flags, "iterations"), "iterations"),
447
+ iterationTimeoutSeconds: parsePositiveIntFlag(getStringFlag(flags, "iteration-timeout"), "iteration-timeout"),
448
+ maxRetries: parseNonNegativeIntFlag(getStringFlag(flags, "max-retries"), "max-retries"),
449
+ artifactsDir: getStringFlag(flags, "artifacts-dir"),
450
+ runId: getStringFlag(flags, "run-id"),
451
+ approveMcps: !Boolean(flags["no-approve-mcps"]),
452
+ sandboxMode: getStringFlag(flags, "sandbox"),
453
+ phase: getStringFlag(flags, "phase"),
454
+ dryRun: Boolean(flags["dry-run"]),
455
+ maxRunDirs: parsePositiveIntFlag(getStringFlag(flags, "max-run-dirs"), "max-run-dirs"),
456
+ summaryLines: parsePositiveIntFlag(getStringFlag(flags, "summary-lines"), "summary-lines"),
457
+ onProgress,
458
+ });
459
+ if (Boolean(flags.json)) {
460
+ console.log(JSON.stringify(result, null, 2));
461
+ return;
462
+ }
463
+ if (result.dryRun) {
464
+ console.log("[dry-run] Would execute sweep with:");
465
+ console.log(` Provider: ${result.provider}`);
466
+ console.log(` Model: ${result.model}`);
467
+ console.log(` Iterations: ${result.totalIterations}`);
468
+ console.log(` Prompt: ${result.promptFile}`);
469
+ return;
470
+ }
471
+ console.log(`Run ID: ${result.runId}`);
472
+ console.log(`Provider: ${result.provider} | Model: ${result.model}`);
473
+ console.log(`Prompt: ${result.promptFile}`);
474
+ console.log(`Iterations: ${result.succeeded}/${result.totalIterations} succeeded`);
475
+ console.log(`Duration: ${formatDuration(result.totalDurationMs)}`);
476
+ console.log(`Run dir: ${result.runDir}`);
477
+ console.log(`Manifest: ${result.manifestFile}`);
478
+ if (result.failed > 0) {
479
+ process.exitCode = 1;
480
+ }
481
+ return;
482
+ }
483
+ if (command === "load-config") {
484
+ const cwd = getResolvedCwd(flags);
485
+ const client = await createClientForCommand(command, flags);
486
+ const project = await client.getProject();
487
+ const prompts = await client.pullPrompts();
488
+ if (prompts.length === 0) {
489
+ console.log("No prompt packs found in the project library. Configure prompts in Prompts Studio first.");
490
+ console.log(` Open: https://prompts-gpt.com/studio/projects`);
491
+ return;
492
+ }
493
+ const result = await syncPrompts(prompts, {
494
+ cwd,
495
+ outDir: getStringFlag(flags, "out") || DEFAULT_PROMPTS_GPT_OUT_DIR,
496
+ overwrite: true,
497
+ agent: getStringFlag(flags, "agent") || "all",
498
+ });
499
+ const providers = await detectProviders(cwd);
500
+ const availableProviders = providers.filter((p) => p.available);
501
+ let configError = null;
502
+ const configResult = await initRunConfig({
503
+ cwd,
504
+ overwrite: true,
505
+ }).catch((err) => {
506
+ configError = err.message;
507
+ return null;
508
+ });
509
+ if (Boolean(flags.json)) {
510
+ console.log(JSON.stringify({
511
+ project: { brandName: project.brandName, websiteUrl: project.websiteUrl },
512
+ synced: { prompts: result.markdown.written.length, agents: result.agents.written.length },
513
+ providers: availableProviders.map((p) => p.provider),
514
+ config: configResult ? "created" : "skipped",
515
+ }, null, 2));
516
+ return;
517
+ }
518
+ console.log(`Loaded configuration from Prompts Studio for: ${project.brandName}`);
519
+ console.log(`Synced ${result.markdown.written.length} prompt pack(s) to ${result.markdown.outDir}`);
520
+ console.log(`Synced ${result.agents.written.length} agent file(s): ${result.agents.targets.join(", ")}`);
521
+ console.log(`Manifest: ${result.manifest.manifestPath}`);
522
+ if (configResult) {
523
+ console.log(`Config: ${configResult.configPath}`);
524
+ }
525
+ else if (configError) {
526
+ console.error(`[config] Skipped: ${configError}`);
527
+ }
528
+ if (availableProviders.length > 0 && result.manifest.manifest.prompts.length > 0) {
529
+ const firstFile = result.manifest.manifest.prompts[0]?.file;
530
+ if (firstFile) {
531
+ console.log(`\nReady to run:`);
532
+ console.log(` prompts-gpt run --prompt-file .prompts-gpt/${firstFile}`);
533
+ }
534
+ }
535
+ return;
536
+ }
73
537
  if (command === "init") {
74
538
  const token = await resolveTokenInput(flags, { command });
75
539
  if (!token) {
@@ -88,13 +552,28 @@ async function runCommand(command, flags) {
88
552
  });
89
553
  }
90
554
  const cwd = getResolvedCwd(flags);
555
+ const apiUrlFlag = getStringFlag(flags, "api-url");
556
+ if (apiUrlFlag) {
557
+ try {
558
+ const parsed = new URL(apiUrlFlag);
559
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
560
+ throw new CliError("--api-url must use https or http.", CLI_EXIT_CODES.validation, { helpCommand: "init" });
561
+ }
562
+ }
563
+ catch (e) {
564
+ if (e instanceof CliError)
565
+ throw e;
566
+ throw new CliError(`--api-url is not a valid URL: ${apiUrlFlag}`, CLI_EXIT_CODES.validation, { helpCommand: "init" });
567
+ }
568
+ }
91
569
  const result = await saveLocalCredentials({
92
570
  token,
93
- apiUrl: getStringFlag(flags, "api-url") || DEFAULT_PROMPTS_GPT_API_URL,
571
+ apiUrl: apiUrlFlag || DEFAULT_PROMPTS_GPT_API_URL,
94
572
  cwd,
95
573
  });
96
574
  console.log(`Saved Prompts-GPT credentials to ${result.credentialsPath}`);
97
575
  console.log("The credentials file is added to .gitignore.");
576
+ console.log("Next: prompts-gpt sync");
98
577
  return;
99
578
  }
100
579
  if (command === "pull") {
@@ -145,12 +624,19 @@ async function runCommand(command, flags) {
145
624
  const shouldSyncAgents = Boolean(flags["sync-agents"]) || typeof getStringFlag(flags, "agent") === "string";
146
625
  if (shouldSyncAgents) {
147
626
  const target = getStringFlag(flags, "agent") || "all";
148
- const agentResult = await writeAgentFiles([prompt], {
627
+ const pulled = await client.pullPrompts().catch(() => []);
628
+ const syncedPrompts = mergePromptPacks([prompt, ...pulled]);
629
+ const agentResult = await writeAgentFiles(syncedPrompts, {
149
630
  cwd,
150
631
  agent: target,
151
632
  overwriteAgentFiles: true,
152
633
  });
634
+ const manifestResult = await writePromptManifest(syncedPrompts, {
635
+ cwd,
636
+ outDir: getStringFlag(flags, "out") || DEFAULT_PROMPTS_GPT_OUT_DIR,
637
+ });
153
638
  console.log(`Synced ${agentResult.written.length} agent file(s) for ${agentResult.targets.join(", ")}.`);
639
+ console.log(`Updated manifest: ${manifestResult.manifestPath}`);
154
640
  }
155
641
  console.log(`Generated: ${prompt.title}`);
156
642
  console.log(`Wrote to ${result.written[0] ?? result.outDir}.`);
@@ -199,45 +685,43 @@ async function runCommand(command, flags) {
199
685
  overwrite: Boolean(flags.overwrite),
200
686
  agent: getStringFlag(flags, "agent") || "all",
201
687
  });
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}`);
688
+ console.log(`Synced ${result.markdown.written.length} new prompt file(s) to ${result.markdown.outDir}.`);
205
689
  if (result.markdown.skipped.length) {
206
- console.log(`Skipped ${result.markdown.skipped.length} existing Markdown file(s). Use --overwrite to replace them.`);
207
- }
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;
218
- }
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;
690
+ console.log(`Kept ${result.markdown.skipped.length} existing file(s) unchanged. Use --overwrite to replace.`);
224
691
  }
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(", ")}.`);
692
+ console.log(`Synced ${result.agents.written.length} agent file(s): ${result.agents.targets.join(", ")}.`);
693
+ console.log(`Manifest: ${result.manifest.manifestPath}`);
231
694
  return;
232
695
  }
233
696
  if (command === "project") {
234
- const client = await createClientForCommand(command, flags);
697
+ const clientOptions = await resolveClientOptions(command, flags);
698
+ const client = new PromptsGptClient(clientOptions);
235
699
  const project = await client.getProject();
236
- console.log(`${project.brandName} (${project.websiteUrl})`);
700
+ const apiUrl = clientOptions.apiUrl;
701
+ if (Boolean(flags.json)) {
702
+ console.log(JSON.stringify({
703
+ project,
704
+ apiUrl,
705
+ }, null, 2));
706
+ return;
707
+ }
708
+ console.log(`Project: ${project.brandName}`);
709
+ console.log(`Website: ${project.websiteUrl}`);
710
+ console.log(`Industry: ${project.industryCategory}`);
711
+ console.log(`Language: ${project.targetLanguage}`);
712
+ console.log(`Countries: ${formatList(project.targetCountries)}`);
713
+ console.log(`Aliases: ${formatList(project.brandAliases)}`);
714
+ console.log(`Keywords: ${formatList(project.productKeywords)}`);
715
+ console.log(`Personas: ${formatList(project.targetPersonas)}`);
716
+ console.log(`Competitors: ${formatCompetitors(project.competitors)}`);
717
+ console.log(`API URL: ${apiUrl}`);
237
718
  return;
238
719
  }
239
720
  }
240
721
  async function createClientForCommand(command, flags) {
722
+ return new PromptsGptClient(await resolveClientOptions(command, flags));
723
+ }
724
+ async function resolveClientOptions(command, flags) {
241
725
  const cwd = getResolvedCwd(flags);
242
726
  const explicitToken = await resolveTokenInput(flags, { command });
243
727
  const explicitApiUrl = getStringFlag(flags, "api-url");
@@ -248,14 +732,25 @@ async function createClientForCommand(command, flags) {
248
732
  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
733
  }
250
734
  const token = explicitToken || credentials?.token || "";
735
+ if (!token && !process.stdin.isTTY) {
736
+ // CI fallback — never read process.env in the importable SDK, only in the CLI entrypoint
737
+ const envToken = process.env.PROMPTS_GPT_TOKEN?.trim();
738
+ if (envToken) {
739
+ return {
740
+ token: envToken,
741
+ apiUrl: explicitApiUrl || credentials?.apiUrl || DEFAULT_PROMPTS_GPT_API_URL,
742
+ fetch,
743
+ };
744
+ }
745
+ }
251
746
  if (!token) {
252
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 });
253
748
  }
254
- return new PromptsGptClient({
749
+ return {
255
750
  token,
256
751
  apiUrl: explicitApiUrl || credentials?.apiUrl || DEFAULT_PROMPTS_GPT_API_URL,
257
752
  fetch,
258
- });
753
+ };
259
754
  }
260
755
  function buildPullQuery(flags, limit) {
261
756
  return {
@@ -330,8 +825,33 @@ function getCommandOptions(command) {
330
825
  if (command === "init") {
331
826
  return common;
332
827
  }
828
+ if (command === "setup") {
829
+ return {
830
+ help: { type: "boolean" },
831
+ cwd: { type: "string" },
832
+ json: { type: "boolean" },
833
+ overwrite: { type: "boolean" },
834
+ "prompt-file": { type: "string" },
835
+ "prompt-dir": { type: "string" },
836
+ manifest: { type: "string" },
837
+ "prompt-files": { type: "string" },
838
+ agent: { type: "string" },
839
+ "provider-order": { type: "string" },
840
+ timeout: { type: "string" },
841
+ "retry-count": { type: "string" },
842
+ "artifacts-dir": { type: "string" },
843
+ "allow-destructive-git": { type: "boolean" },
844
+ "codex-model": { type: "string" },
845
+ "cursor-model": { type: "string" },
846
+ "claude-model": { type: "string" },
847
+ "copilot-model": { type: "string" },
848
+ };
849
+ }
333
850
  if (command === "project") {
334
- return common;
851
+ return {
852
+ ...common,
853
+ json: { type: "boolean" },
854
+ };
335
855
  }
336
856
  if (command === "pull") {
337
857
  return {
@@ -386,39 +906,76 @@ function getCommandOptions(command) {
386
906
  "dry-run": { type: "boolean" },
387
907
  };
388
908
  }
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)}`;
909
+ if (command === "run") {
910
+ return {
911
+ help: { type: "boolean" },
912
+ cwd: { type: "string" },
913
+ json: { type: "boolean" },
914
+ "prompt-file": { type: "string" },
915
+ agent: { type: "string" },
916
+ model: { type: "string" },
917
+ timeout: { type: "string" },
918
+ "artifacts-dir": { type: "string" },
919
+ "run-id": { type: "string" },
920
+ sandbox: { type: "string" },
921
+ "no-approve-mcps": { type: "boolean" },
922
+ background: { type: "boolean" },
923
+ "permission-mode": { type: "string" },
924
+ };
925
+ }
926
+ if (command === "sweep") {
927
+ return {
928
+ help: { type: "boolean" },
929
+ cwd: { type: "string" },
930
+ json: { type: "boolean" },
931
+ "prompt-file": { type: "string" },
932
+ agent: { type: "string" },
933
+ model: { type: "string" },
934
+ iterations: { type: "string" },
935
+ "iteration-timeout": { type: "string" },
936
+ "max-retries": { type: "string" },
937
+ "artifacts-dir": { type: "string" },
938
+ "run-id": { type: "string" },
939
+ sandbox: { type: "string" },
940
+ "no-approve-mcps": { type: "boolean" },
941
+ phase: { type: "string" },
942
+ "dry-run": { type: "boolean" },
943
+ "max-run-dirs": { type: "string" },
944
+ "summary-lines": { type: "string" },
945
+ background: { type: "boolean" },
946
+ "permission-mode": { type: "string" },
947
+ };
948
+ }
949
+ if (command === "run-batch") {
950
+ return {
951
+ help: { type: "boolean" },
952
+ cwd: { type: "string" },
953
+ json: { type: "boolean" },
954
+ manifest: { type: "string" },
955
+ "prompt-files": { type: "string" },
956
+ agent: { type: "string" },
957
+ model: { type: "string" },
958
+ timeout: { type: "string" },
959
+ "artifacts-dir": { type: "string" },
960
+ };
961
+ }
962
+ if (command === "load-config") {
963
+ return {
964
+ ...common,
965
+ json: { type: "boolean" },
966
+ out: { type: "string" },
967
+ agent: { type: "string" },
968
+ };
969
+ }
970
+ if (command === "list" || command === "status" || command === "validate" || command === "providers" || command === "doctor") {
971
+ return {
972
+ help: { type: "boolean" },
973
+ cwd: { type: "string" },
974
+ json: { type: "boolean" },
975
+ };
976
+ }
977
+ throw new CliError(`Unsupported command: ${command}.`, CLI_EXIT_CODES.usage, {
978
+ helpCommand: "help",
422
979
  });
423
980
  }
424
981
  function toCliParseError(error, command) {
@@ -446,6 +1003,22 @@ function validateFlagConflicts(command, flags) {
446
1003
  helpCommand: command,
447
1004
  });
448
1005
  }
1006
+ const promptSourceCount = [
1007
+ Boolean(flags["prompt-file"]),
1008
+ Boolean(flags["prompt-files"]),
1009
+ Boolean(flags.manifest),
1010
+ Boolean(flags["prompt-dir"]),
1011
+ ].filter(Boolean).length;
1012
+ if (command === "setup" && promptSourceCount > 1) {
1013
+ throw new CliError("Use only one prompt source for `setup`: `--prompt-file`, `--prompt-files`, `--manifest`, or `--prompt-dir`.", CLI_EXIT_CODES.usage, {
1014
+ helpCommand: command,
1015
+ });
1016
+ }
1017
+ if (command === "run-batch" && Boolean(flags["prompt-files"]) && Boolean(flags.manifest)) {
1018
+ throw new CliError("Use either `--prompt-files` or `--manifest`, not both.", CLI_EXIT_CODES.usage, {
1019
+ helpCommand: command,
1020
+ });
1021
+ }
449
1022
  }
450
1023
  async function resolveTokenInput(flags, options) {
451
1024
  const explicitToken = getStringFlag(flags, "token")?.trim();
@@ -491,26 +1064,48 @@ function readTokenFromPrompt(command) {
491
1064
  return new Promise((resolve, reject) => {
492
1065
  const stdin = process.stdin;
493
1066
  const stdout = process.stdout;
494
- let token = "";
1067
+ const buf = Buffer.alloc(MAX_STDIN_TOKEN_LENGTH);
1068
+ let bufLen = 0;
495
1069
  const cleanup = () => {
496
1070
  stdin.removeListener("data", onData);
1071
+ process.removeListener("SIGTERM", onSigterm);
1072
+ process.removeListener("SIGINT", onSigint);
497
1073
  stdin.setRawMode?.(false);
498
1074
  stdin.pause();
499
1075
  };
1076
+ const extractAndWipe = () => {
1077
+ const result = buf.toString("utf8", 0, bufLen).trim();
1078
+ buf.fill(0, 0, bufLen);
1079
+ bufLen = 0;
1080
+ return result;
1081
+ };
500
1082
  const finish = (callback) => {
501
1083
  stdout.write("\n");
502
1084
  cleanup();
503
1085
  callback();
504
1086
  };
1087
+ const onSigterm = () => {
1088
+ buf.fill(0, 0, bufLen);
1089
+ cleanup();
1090
+ process.exit(130);
1091
+ };
1092
+ const onSigint = () => {
1093
+ buf.fill(0, 0, bufLen);
1094
+ cleanup();
1095
+ process.exit(130);
1096
+ };
1097
+ process.on("SIGTERM", onSigterm);
1098
+ process.on("SIGINT", onSigint);
505
1099
  const onData = (chunk) => {
506
1100
  const value = typeof chunk === "string" ? chunk : chunk.toString("utf8");
507
1101
  for (const char of value) {
508
1102
  if (char === "\u0003") {
1103
+ buf.fill(0, 0, bufLen);
509
1104
  finish(() => reject(new CliError("Token entry cancelled.", CLI_EXIT_CODES.general, { helpCommand: command })));
510
1105
  return;
511
1106
  }
512
1107
  if (char === "\r" || char === "\n") {
513
- const trimmed = token.trim();
1108
+ const trimmed = extractAndWipe();
514
1109
  if (!trimmed) {
515
1110
  finish(() => reject(new CliError("Project token is required.", CLI_EXIT_CODES.validation, { helpCommand: command })));
516
1111
  return;
@@ -519,21 +1114,33 @@ function readTokenFromPrompt(command) {
519
1114
  return;
520
1115
  }
521
1116
  if (char === "\u007f" || char === "\b") {
522
- token = token.slice(0, -1);
1117
+ if (bufLen > 0) {
1118
+ bufLen--;
1119
+ buf[bufLen] = 0;
1120
+ }
523
1121
  continue;
524
1122
  }
525
1123
  if (char >= " ") {
526
- token += char;
527
- if (token.length > MAX_STDIN_TOKEN_LENGTH) {
1124
+ const charBytes = Buffer.byteLength(char, "utf8");
1125
+ if (bufLen + charBytes > MAX_STDIN_TOKEN_LENGTH) {
1126
+ buf.fill(0, 0, bufLen);
528
1127
  finish(() => reject(new CliError("Token input is too long.", CLI_EXIT_CODES.validation, { helpCommand: command })));
529
1128
  return;
530
1129
  }
1130
+ buf.write(char, bufLen, "utf8");
1131
+ bufLen += charBytes;
531
1132
  }
532
1133
  }
533
1134
  };
534
1135
  stdout.write("Project token: ");
535
1136
  stdin.setEncoding("utf8");
536
- stdin.setRawMode?.(true);
1137
+ try {
1138
+ stdin.setRawMode?.(true);
1139
+ }
1140
+ catch {
1141
+ reject(new CliError("Cannot enable raw mode for token prompt. Use --token-stdin instead.", CLI_EXIT_CODES.usage, { helpCommand: command }));
1142
+ return;
1143
+ }
537
1144
  stdin.resume();
538
1145
  stdin.on("data", onData);
539
1146
  });
@@ -542,11 +1149,11 @@ function parseLimitFlag(raw) {
542
1149
  if (raw === undefined)
543
1150
  return undefined;
544
1151
  if (!/^\d+$/.test(raw)) {
545
- throw new CliError("`--limit` must be a positive integer.", CLI_EXIT_CODES.validation);
1152
+ throw new CliError("`--limit` must be a positive integer between 1 and 100.", CLI_EXIT_CODES.validation);
546
1153
  }
547
1154
  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);
1155
+ if (!Number.isSafeInteger(value) || value < 1 || value > 100) {
1156
+ throw new CliError("`--limit` must be a positive integer between 1 and 100.", CLI_EXIT_CODES.validation);
550
1157
  }
551
1158
  return value;
552
1159
  }
@@ -568,6 +1175,18 @@ function validateAgentFlag(agent) {
568
1175
  throw new CliError(`Invalid --agent target: ${invalid.join(", ")}. Use ${SUPPORTED_AGENT_TARGETS.join(", ")}, or all.`, CLI_EXIT_CODES.validation);
569
1176
  }
570
1177
  }
1178
+ function validateProviderOrderFlag(values) {
1179
+ if (!values?.length)
1180
+ return;
1181
+ const invalid = values.filter((value) => {
1182
+ const raw = value.trim().toLowerCase();
1183
+ const normalized = normalizeOrchestrationAgent(raw);
1184
+ return !raw || normalized === "router";
1185
+ });
1186
+ if (invalid.length > 0) {
1187
+ throw new CliError(`Invalid --provider-order value: ${invalid.join(", ")}. Use codex, cursor, claude, or copilot.`, CLI_EXIT_CODES.validation);
1188
+ }
1189
+ }
571
1190
  function getResolvedCwd(flags) {
572
1191
  return path.resolve(getStringFlag(flags, "cwd") || process.cwd());
573
1192
  }
@@ -575,6 +1194,44 @@ function getStringFlag(flags, name) {
575
1194
  const value = flags[name];
576
1195
  return typeof value === "string" ? value : undefined;
577
1196
  }
1197
+ function formatDuration(ms) {
1198
+ if (ms < 1000)
1199
+ return `${ms}ms`;
1200
+ const totalSeconds = Math.round(ms / 1000);
1201
+ const hours = Math.floor(totalSeconds / 3600);
1202
+ const minutes = Math.floor((totalSeconds % 3600) / 60);
1203
+ const seconds = totalSeconds % 60;
1204
+ if (hours > 0)
1205
+ return `${hours}h ${minutes}m ${seconds}s`;
1206
+ if (minutes > 0)
1207
+ return `${minutes}m ${seconds}s`;
1208
+ return `${seconds}s`;
1209
+ }
1210
+ function formatList(values) {
1211
+ if (!Array.isArray(values) || values.length === 0)
1212
+ return "None";
1213
+ return values.join(", ");
1214
+ }
1215
+ function formatCompetitors(values) {
1216
+ if (!Array.isArray(values) || values.length === 0)
1217
+ return "None";
1218
+ return values.map((competitor) => {
1219
+ const aliases = competitor.aliases?.length ? ` [aliases: ${competitor.aliases.join(", ")}]` : "";
1220
+ return competitor.websiteUrl ? `${competitor.name} (${competitor.websiteUrl})${aliases}` : `${competitor.name}${aliases}`;
1221
+ }).join("; ");
1222
+ }
1223
+ function mergePromptPacks(prompts) {
1224
+ const deduped = new Map();
1225
+ for (const prompt of prompts) {
1226
+ deduped.set(normalizePromptKey(prompt), prompt);
1227
+ }
1228
+ return [...deduped.values()];
1229
+ }
1230
+ function normalizePromptKey(prompt) {
1231
+ const raw = String(prompt.slug || prompt.title || "prompt").trim().toLowerCase();
1232
+ const normalized = raw.replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
1233
+ return normalized || "prompt";
1234
+ }
578
1235
  function asCommandName(value) {
579
1236
  if (!value)
580
1237
  return undefined;
@@ -611,11 +1268,20 @@ Usage:
611
1268
 
612
1269
  Commands:
613
1270
  init Save a project token in .prompts-gpt/.credentials.json
614
- project Show the current project linked to the token
1271
+ setup Scaffold .prompts-gpt/config.json for local orchestration
1272
+ load-config Pull configuration and prompts directly from Prompts Studio
615
1273
  pull Download prompt packs as Markdown files
616
1274
  generate Generate one prompt pack from a goal
617
1275
  sync Generate and/or pull prompt packs, then sync agent files
618
- install-agents Refresh agent-specific instruction files from pulled prompts
1276
+ list Show available prompts, sweeps, and agent integrations
1277
+ 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
619
1285
  version Show the current CLI version
620
1286
  help Show global or command-specific help
621
1287
 
@@ -625,8 +1291,49 @@ Global options:
625
1291
 
626
1292
  Supported tools: ${VALID_TOOLS.join(", ")}
627
1293
  Supported agent targets: ${SUPPORTED_AGENT_TARGETS.join(", ")}
1294
+ Run agent profiles: ${ORCHESTRATION_AGENT_PROFILES.join(", ")}
628
1295
  `);
629
1296
  }
1297
+ function parsePositiveIntFlag(raw, flagName) {
1298
+ if (raw === undefined)
1299
+ return undefined;
1300
+ const label = flagName ? `--${flagName}` : "Value";
1301
+ if (!/^\d+$/.test(raw)) {
1302
+ throw new CliError(`${label} must be a positive integer, got "${raw}".`, CLI_EXIT_CODES.validation);
1303
+ }
1304
+ const value = Number.parseInt(raw, 10);
1305
+ if (!Number.isSafeInteger(value) || value < 1) {
1306
+ throw new CliError(`${label} must be a positive integer, got "${raw}".`, CLI_EXIT_CODES.validation);
1307
+ }
1308
+ return value;
1309
+ }
1310
+ function parseNonNegativeIntFlag(raw, flagName) {
1311
+ if (raw === undefined)
1312
+ return undefined;
1313
+ const label = flagName ? `--${flagName}` : "Value";
1314
+ if (!/^\d+$/.test(raw)) {
1315
+ throw new CliError(`${label} must be a non-negative integer, got "${raw}".`, CLI_EXIT_CODES.validation);
1316
+ }
1317
+ const value = Number.parseInt(raw, 10);
1318
+ if (!Number.isSafeInteger(value) || value < 0) {
1319
+ throw new CliError(`${label} must be a non-negative integer, got "${raw}".`, CLI_EXIT_CODES.validation);
1320
+ }
1321
+ return value;
1322
+ }
1323
+ function resolveRunAgent(flags, fallback) {
1324
+ const raw = getStringFlag(flags, "agent");
1325
+ if (!raw)
1326
+ return fallback;
1327
+ const normalizedRaw = raw.trim().toLowerCase();
1328
+ const normalized = normalizeOrchestrationAgent(raw);
1329
+ if (normalized === "router" && normalizedRaw !== "router") {
1330
+ const profiles = ORCHESTRATION_AGENT_PROFILES.join(", ");
1331
+ const hint = ORCHESTRATION_AGENT_PROFILES.find((p) => p.startsWith(normalizedRaw.slice(0, 3)));
1332
+ const suggestion = hint ? ` Did you mean "${hint}"?` : "";
1333
+ throw new CliError(`Invalid --agent target: ${raw}. Use ${profiles}.${suggestion}`, CLI_EXIT_CODES.validation);
1334
+ }
1335
+ return normalized;
1336
+ }
630
1337
  function getCommandHelp(command) {
631
1338
  if (command === "init") {
632
1339
  return `prompts-gpt init
@@ -644,18 +1351,49 @@ Options:
644
1351
  --api-url <url> Custom API base URL for self-hosted instances.
645
1352
  --cwd <path> Target project directory that will receive .prompts-gpt/.credentials.json.
646
1353
  --help Show this command help.
1354
+ `;
1355
+ }
1356
+ if (command === "setup") {
1357
+ return `prompts-gpt setup
1358
+
1359
+ Usage:
1360
+ 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>]
1361
+
1362
+ Why use it:
1363
+ Scaffolds a generic local orchestration config so a repo can run prompts through Codex, Cursor, Claude Code, or Copilot with minimal repeated CLI flags.
1364
+
1365
+ Options:
1366
+ --prompt-file <path> Default single prompt file for \`prompts-gpt run\`.
1367
+ --prompt-files <list> Default batch prompt files for \`prompts-gpt run-batch\`.
1368
+ --manifest <path> Use this manifest as the batch source. Default: auto-detect .prompts-gpt/manifest.json
1369
+ --prompt-dir <path> Scan a prompt directory when you do not use .prompts-gpt.
1370
+ --agent <name> Default orchestration profile. Default: router
1371
+ --provider-order <list> Comma-separated router order such as codex,cursor,claude,copilot
1372
+ --timeout <seconds> Default timeout in seconds. Default: 900
1373
+ --retry-count <n> Retry count for spawn and timeout failures. Default: 0
1374
+ --artifacts-dir <path> Run artifact directory. Default: .scripts/runs
1375
+ --codex-model <name> Default model override for codex.
1376
+ --cursor-model <name> Default model override for cursor.
1377
+ --claude-model <name> Default model override for claude.
1378
+ --copilot-model <name> Default model override for copilot.
1379
+ --allow-destructive-git Write config with destructive git protection disabled.
1380
+ --overwrite Replace an existing config file.
1381
+ --json Print the generated config payload as JSON.
1382
+ --cwd <path> Project directory.
1383
+ --help Show this command help.
647
1384
  `;
648
1385
  }
649
1386
  if (command === "project") {
650
1387
  return `prompts-gpt project
651
1388
 
652
1389
  Usage:
653
- prompts-gpt project [--token <project-token> | --token-stdin | --token-prompt] [--api-url <url>] [--cwd <path>]
1390
+ prompts-gpt project [--json] [--token <project-token> | --token-stdin | --token-prompt] [--api-url <url>] [--cwd <path>]
654
1391
 
655
1392
  Why use it:
656
1393
  Verifies that the current local token resolves to the expected Prompts-GPT project before syncing files.
657
1394
 
658
1395
  Options:
1396
+ --json Print the full project payload as JSON.
659
1397
  --token <token> Override the saved local token for this command only.
660
1398
  --token-stdin Read the override token from stdin for this command only.
661
1399
  --token-prompt Prompt for the override token without echoing it.
@@ -756,30 +1494,175 @@ Options:
756
1494
  --help Show this command help.
757
1495
  `;
758
1496
  }
759
- if (command === "install-agents") {
760
- return `prompts-gpt install-agents
1497
+ if (command === "run") {
1498
+ return `prompts-gpt run
761
1499
 
762
1500
  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>]
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>]
764
1502
 
765
1503
  Why use it:
766
- Refreshes agent-specific instruction files from the current prompt library without rewriting the Markdown prompt directory.
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.
767
1505
 
768
1506
  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.
1507
+ --prompt-file <path> Optional explicit prompt Markdown or text file.
1508
+ --agent <name> Orchestration profile. Default from ${DEFAULT_RUN_CONFIG_PATH} or router.
1509
+ --model <name> Optional model override for the selected provider.
1510
+ --timeout <seconds> Execution timeout in seconds.
1511
+ --artifacts-dir <path> Override run artifacts directory.
1512
+ --run-id <id> Explicit run ID for artifact folder naming.
1513
+ --sandbox <mode> Codex sandbox mode. Default: workspace-write.
1514
+ --no-approve-mcps Disable Cursor MCP auto-approval flag.
1515
+ --background Run as a background agent (Cursor cloud agent mode).
1516
+ --permission-mode <mode> Claude Code permission mode. Default: acceptEdits.
1517
+ --json Print machine-readable JSON output.
1518
+ --cwd <path> Project directory.
1519
+ --help Show this command help.
1520
+ `;
1521
+ }
1522
+ if (command === "run-batch") {
1523
+ return `prompts-gpt run-batch
1524
+
1525
+ 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>]
1527
+
1528
+ Why use it:
1529
+ Executes multiple prompt files in sequence using the configured prompt source, a manifest, or an explicit file list.
1530
+
1531
+ Options:
1532
+ --manifest <path> Prompt manifest path. Overrides config batch defaults.
1533
+ --prompt-files <list> Comma-separated prompt files to run.
1534
+ --agent <name> Orchestration profile. Default from ${DEFAULT_RUN_CONFIG_PATH} or router.
1535
+ --model <name> Optional model override for all runs.
1536
+ --timeout <seconds> Execution timeout in seconds per prompt.
1537
+ --artifacts-dir <path> Override run artifacts directory.
1538
+ --json Print machine-readable JSON output.
1539
+ --cwd <path> Project directory.
1540
+ --help Show this command help.
1541
+ `;
1542
+ }
1543
+ if (command === "sweep") {
1544
+ return `prompts-gpt sweep
1545
+
1546
+ 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>]
1548
+
1549
+ Why use it:
1550
+ Replaces the bash sweep scripts with a generic multi-iteration execution engine.
1551
+ 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.
1554
+
1555
+ Options:
1556
+ --prompt-file <path> Prompt file to sweep. Reads from config if omitted.
1557
+ --agent <name> Orchestration profile. Default from config or router.
1558
+ --model <name> Model override for the selected provider.
1559
+ --iterations <n> Number of sweep iterations. Default: 1
1560
+ --iteration-timeout <secs> Timeout per iteration in seconds. Default: 5400 (90 min)
1561
+ --max-retries <n> Max retries per iteration on spawn/timeout. Default: 2
1562
+ --phase <name> Optional phase label injected into the prompt.
1563
+ --artifacts-dir <path> Override run artifacts directory.
1564
+ --run-id <id> Explicit run ID for artifact folder naming.
1565
+ --sandbox <mode> Codex sandbox mode. Default: workspace-write
1566
+ --no-approve-mcps Disable Cursor MCP auto-approval flag.
1567
+ --background Run as a background agent (Cursor cloud agent mode).
1568
+ --permission-mode <mode> Claude Code permission mode. Default: acceptEdits
1569
+ --max-run-dirs <n> Max artifact directories to keep. Default: 20
1570
+ --summary-lines <n> Lines of summary to extract per iteration. Default: 40
1571
+ --dry-run Preview what the sweep would do without executing.
1572
+ --json Print machine-readable JSON output.
1573
+ --cwd <path> Project directory.
1574
+ --help Show this command help.
1575
+ `;
1576
+ }
1577
+ if (command === "list") {
1578
+ return `prompts-gpt list
1579
+
1580
+ Usage:
1581
+ prompts-gpt list [--json] [--cwd <path>]
1582
+
1583
+ Why use it:
1584
+ Shows all prompt packs, sweep files, and agent integrations available in the workspace.
1585
+ Gives runnable commands for each entry so you can copy-paste them directly.
1586
+
1587
+ Options:
1588
+ --json Print as JSON.
1589
+ --cwd <path> Project directory.
782
1590
  --help Show this command help.
1591
+ `;
1592
+ }
1593
+ if (command === "status") {
1594
+ return `prompts-gpt status
1595
+
1596
+ Usage:
1597
+ prompts-gpt status [--json] [--cwd <path>]
1598
+
1599
+ Why use it:
1600
+ Shows workspace readiness at a glance: credentials, config, manifest,
1601
+ prompts, sweeps, agent integrations, and available providers.
1602
+
1603
+ Options:
1604
+ --json Print as JSON.
1605
+ --cwd <path> Project directory.
1606
+ --help Show this command help.
1607
+ `;
1608
+ }
1609
+ if (command === "validate") {
1610
+ return `prompts-gpt validate
1611
+
1612
+ Usage:
1613
+ prompts-gpt validate [--json] [--cwd <path>]
1614
+
1615
+ Why use it:
1616
+ Validates the .prompts-gpt/config.json file and reports errors and
1617
+ warnings without running any commands.
1618
+
1619
+ Options:
1620
+ --json Print as JSON.
1621
+ --cwd <path> Project directory.
1622
+ --help Show this command help.
1623
+ `;
1624
+ }
1625
+ if (command === "providers") {
1626
+ return `prompts-gpt providers
1627
+
1628
+ Usage:
1629
+ prompts-gpt providers [--json] [--cwd <path>]
1630
+
1631
+ Why use it:
1632
+ Shows provider CLI detection details for codex, cursor, claude, and copilot so you can decide the best router order before running setup.
1633
+ `;
1634
+ }
1635
+ if (command === "doctor") {
1636
+ return `prompts-gpt doctor
1637
+
1638
+ Usage:
1639
+ prompts-gpt doctor [--json] [--cwd <path>]
1640
+
1641
+ Why use it:
1642
+ Checks runtime prerequisites, provider availability, prompt-source readiness, and run config health.
1643
+ `;
1644
+ }
1645
+ if (command === "load-config") {
1646
+ return `prompts-gpt load-config
1647
+
1648
+ Usage:
1649
+ prompts-gpt load-config [--agent <targets>] [--out <dir>] [--json] [--token <project-token> | --token-stdin | --token-prompt] [--api-url <url>] [--cwd <path>]
1650
+
1651
+ Why use it:
1652
+ One command to pull all prompts, agent files, and config from Prompts Studio
1653
+ into your local workspace. Replaces the multi-step init + sync + setup flow
1654
+ for users who manage their prompt library in the Prompts Studio web app.
1655
+
1656
+ Options:
1657
+ --agent <targets> Comma-separated agent targets. Default: all
1658
+ --out <dir> Output directory. Default: .prompts-gpt
1659
+ --json Print machine-readable JSON output.
1660
+ --token <token> Override saved token.
1661
+ --token-stdin Read token from stdin.
1662
+ --token-prompt Prompt for token.
1663
+ --api-url <url> Override API URL.
1664
+ --cwd <path> Project directory.
1665
+ --help Show this command help.
783
1666
  `;
784
1667
  }
785
1668
  if (command === "version") {
@@ -821,9 +1704,15 @@ main().catch((error) => {
821
1704
  : CLI_EXIT_CODES.general;
822
1705
  return;
823
1706
  }
824
- console.error(error instanceof Error ? error.message : String(error));
1707
+ const msg = error instanceof Error ? error.message : String(error);
1708
+ console.error(msg.replace(/pgpt_[a-zA-Z0-9]{4,}/g, "pgpt_***"));
825
1709
  process.exitCode = CLI_EXIT_CODES.general;
826
1710
  });
1711
+ function warnOnConfigIssues(config) {
1712
+ for (const w of config.configWarnings) {
1713
+ console.error(`[config warning] ${w}`);
1714
+ }
1715
+ }
827
1716
  function formatApiError(error) {
828
1717
  const lines = [error.message];
829
1718
  for (const [field, messages] of Object.entries(error.fieldErrors ?? {})) {