@prompts-gpt/client 0.2.4 → 0.2.6
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 +941 -83
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -3
- package/dist/index.js.map +1 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +46 -22
- package/dist/runtime.js.map +1 -1
- package/dist/sweep.d.ts.map +1 -1
- package/dist/sweep.js +144 -10
- package/dist/sweep.js.map +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -27,15 +27,32 @@ class CliError extends Error {
|
|
|
27
27
|
async function main() {
|
|
28
28
|
const argv = process.argv.slice(2);
|
|
29
29
|
if (argv.length === 0) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
prompts-gpt sweep — run a
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
30
|
+
const assets = await discoverWorkspaceAssets(process.cwd()).catch(() => null);
|
|
31
|
+
const hasSetup = assets && (assets.credentialsFound || assets.configFound || assets.prompts.length > 0 || assets.sweeps.length > 0);
|
|
32
|
+
if (hasSetup && assets) {
|
|
33
|
+
console.log(`Prompts-GPT CLI — ${assets.prompts.length} prompts, ${assets.sweeps.length} sweeps available\n`);
|
|
34
|
+
if (assets.sweeps.length > 0) {
|
|
35
|
+
console.log(" prompts-gpt sweep — run a sweep (interactive picker)");
|
|
36
|
+
}
|
|
37
|
+
if (assets.prompts.length > 0) {
|
|
38
|
+
console.log(" prompts-gpt run — run a prompt");
|
|
39
|
+
}
|
|
40
|
+
console.log(" prompts-gpt list — see all available assets");
|
|
41
|
+
console.log(" prompts-gpt sync — refresh prompts from studio");
|
|
42
|
+
console.log(" prompts-gpt status — check workspace readiness");
|
|
43
|
+
console.log(`\nRun \`prompts-gpt help\` for the full command list.\n`);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
console.log(`Prompts-GPT CLI — sync and run AI prompt packs locally\n`);
|
|
47
|
+
console.log("Get started:");
|
|
48
|
+
console.log(" prompts-gpt quickstart — setup credentials, config, and first run");
|
|
49
|
+
console.log(" prompts-gpt init --token-prompt — save your project token");
|
|
50
|
+
console.log("");
|
|
51
|
+
console.log("Already have sweep files?");
|
|
52
|
+
console.log(" prompts-gpt sweep — auto-detect and run local sweeps");
|
|
53
|
+
console.log(" prompts-gpt list — see available prompts and sweeps");
|
|
54
|
+
console.log(`\nRun \`prompts-gpt help\` for the full command list.\n`);
|
|
55
|
+
}
|
|
39
56
|
return;
|
|
40
57
|
}
|
|
41
58
|
const first = argv[0];
|
|
@@ -84,14 +101,21 @@ async function runCommand(command, flags) {
|
|
|
84
101
|
console.log(JSON.stringify({ cwd, providers }, null, 2));
|
|
85
102
|
return;
|
|
86
103
|
}
|
|
87
|
-
console.log(`Workspace: ${cwd}`);
|
|
104
|
+
console.log(`Workspace: ${cwd}\n`);
|
|
88
105
|
for (const provider of providers) {
|
|
89
|
-
const
|
|
90
|
-
console.log(`${provider.provider}: ${
|
|
106
|
+
const icon = provider.available ? "✓" : "✗";
|
|
107
|
+
console.log(`${icon} ${provider.provider}: ${provider.available ? "available" : "missing"} | bin=${provider.bin} | model=${provider.modelDefault}${provider.version ? ` | ${provider.version}` : ""}`);
|
|
91
108
|
if (!provider.available) {
|
|
92
|
-
console.log(`
|
|
109
|
+
console.log(` → ${provider.installHint}`);
|
|
93
110
|
}
|
|
94
111
|
}
|
|
112
|
+
const noneAvailable = providers.every((p) => !p.available);
|
|
113
|
+
if (noneAvailable) {
|
|
114
|
+
console.log("\nNo providers installed. Quick install:");
|
|
115
|
+
console.log(" npm install -g @openai/codex # OpenAI Codex");
|
|
116
|
+
console.log(" npm install -g @anthropic-ai/claude-code # Claude Code");
|
|
117
|
+
console.log(" # Cursor: install Cursor IDE");
|
|
118
|
+
}
|
|
95
119
|
return;
|
|
96
120
|
}
|
|
97
121
|
if (command === "setup") {
|
|
@@ -130,11 +154,9 @@ async function runCommand(command, flags) {
|
|
|
130
154
|
console.log(JSON.stringify(result, null, 2));
|
|
131
155
|
return;
|
|
132
156
|
}
|
|
133
|
-
console.log(`Created run config: ${result.configPath}`);
|
|
134
157
|
console.log(`Config: ${result.configPath}`);
|
|
135
158
|
console.log(`Agent: ${result.config.defaultAgent ?? "router"} | Providers: ${(result.config.providerOrder ?? []).join(", ")}`);
|
|
136
159
|
if (result.sourceSummary.defaultPromptFile) {
|
|
137
|
-
console.log(`Default prompt file: ${result.sourceSummary.defaultPromptFile}`);
|
|
138
160
|
console.log(`Default prompt: ${result.sourceSummary.defaultPromptFile}`);
|
|
139
161
|
}
|
|
140
162
|
const availableCount = result.providerSummary.filter((p) => p.available).length;
|
|
@@ -143,14 +165,42 @@ async function runCommand(command, flags) {
|
|
|
143
165
|
console.log("⚠ No provider CLIs detected. Install codex, cursor agent, claude, or copilot.");
|
|
144
166
|
}
|
|
145
167
|
const setupAssets = await discoverWorkspaceAssets(cwd);
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
168
|
+
if (isTTYInteractive() && (setupAssets.sweeps.length > 0 || setupAssets.prompts.length > 0)) {
|
|
169
|
+
const setupOptions = [];
|
|
170
|
+
for (const s of setupAssets.sweeps) {
|
|
171
|
+
setupOptions.push({ label: `sweep: ${s.name}`, value: `sweep:${s.file}` });
|
|
172
|
+
}
|
|
173
|
+
for (const p of setupAssets.prompts.slice(0, 3)) {
|
|
174
|
+
setupOptions.push({ label: `run: ${p.title}`, value: `run:.prompts-gpt/${p.file}` });
|
|
175
|
+
}
|
|
176
|
+
setupOptions.push({ label: "prompts-gpt list", value: "list" });
|
|
177
|
+
setupOptions.push({ label: "(done)", value: "done" });
|
|
178
|
+
console.log("");
|
|
179
|
+
const setupPicked = await interactiveSelect("What next?", setupOptions);
|
|
180
|
+
if (setupPicked === "list") {
|
|
181
|
+
const { spawnSync: spSync } = await import("node:child_process");
|
|
182
|
+
const cliEntry = process.argv[1] || require.resolve("./cli.js");
|
|
183
|
+
spSync(process.execPath, [cliEntry, "list"], { stdio: "inherit", cwd });
|
|
184
|
+
}
|
|
185
|
+
else if (setupPicked !== "done") {
|
|
186
|
+
const [action, file] = setupPicked.split(":", 2);
|
|
187
|
+
const cmd = action === "sweep" ? "sweep" : "run";
|
|
188
|
+
console.log(`\nRunning: prompts-gpt ${cmd} -f ${file}\n`);
|
|
189
|
+
const { spawnSync: spSync } = await import("node:child_process");
|
|
190
|
+
const cliEntry = process.argv[1] || require.resolve("./cli.js");
|
|
191
|
+
const setupResult = spSync(process.execPath, [cliEntry, cmd, "-f", file], { stdio: "inherit", cwd });
|
|
192
|
+
process.exitCode = setupResult.status ?? 1;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
console.log("Next steps:");
|
|
197
|
+
console.log(` prompts-gpt run${result.sourceSummary.defaultPromptFile ? "" : " --prompt-file <path>"}`);
|
|
198
|
+
if (setupAssets.sweeps.length > 0) {
|
|
199
|
+
console.log(` prompts-gpt sweep — run a multi-iteration sweep (${setupAssets.sweeps.length} found)`);
|
|
200
|
+
}
|
|
201
|
+
console.log(" prompts-gpt list — see all runnable assets");
|
|
202
|
+
console.log(" prompts-gpt status — check workspace readiness");
|
|
150
203
|
}
|
|
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
204
|
return;
|
|
155
205
|
}
|
|
156
206
|
if (command === "doctor") {
|
|
@@ -160,17 +210,38 @@ async function runCommand(command, flags) {
|
|
|
160
210
|
console.log(JSON.stringify(report, null, 2));
|
|
161
211
|
return;
|
|
162
212
|
}
|
|
163
|
-
console.log(
|
|
164
|
-
console.log(
|
|
165
|
-
console.log(
|
|
213
|
+
console.log("Prompts-GPT Doctor");
|
|
214
|
+
console.log("==================\n");
|
|
215
|
+
console.log("System:");
|
|
216
|
+
console.log(` ${report.nodeVersion.startsWith("v18") || report.nodeVersion.startsWith("v2") ? "✓" : "⚠"} Node: ${report.nodeVersion}`);
|
|
217
|
+
console.log(` ✓ OS: ${report.osPlatform}/${report.osArch} | CPUs: ${report.cpuCount}`);
|
|
218
|
+
console.log(` ${report.configFound ? "✓" : "✗"} Config: ${report.configFound ? report.configPath : "not found — run \`prompts-gpt setup\`"}`);
|
|
219
|
+
console.log(`\n Workspace: ${report.cwd}\n`);
|
|
220
|
+
console.log("Providers:");
|
|
166
221
|
for (const provider of report.providers) {
|
|
167
|
-
|
|
222
|
+
const icon = provider.available ? "✓" : "✗";
|
|
223
|
+
console.log(` ${icon} ${provider.provider}: ${provider.available ? `${provider.bin}` : "not found"}${provider.version ? ` (${provider.version})` : ""}`);
|
|
168
224
|
if (!provider.available) {
|
|
169
|
-
console.log(`
|
|
225
|
+
console.log(` → ${provider.installHint}`);
|
|
170
226
|
}
|
|
171
227
|
}
|
|
172
|
-
|
|
173
|
-
|
|
228
|
+
const configNotes = report.notes.filter((n) => n.includes("config") || n.includes("Config") || n.includes("provider order") || n.includes("Router"));
|
|
229
|
+
const sweepNotes = report.notes.filter((n) => n.includes("weep") || n.includes("lock"));
|
|
230
|
+
const otherNotes = report.notes.filter((n) => !configNotes.includes(n) && !sweepNotes.includes(n));
|
|
231
|
+
if (configNotes.length > 0) {
|
|
232
|
+
console.log("\nConfiguration:");
|
|
233
|
+
for (const n of configNotes)
|
|
234
|
+
console.log(` ℹ ${n}`);
|
|
235
|
+
}
|
|
236
|
+
if (sweepNotes.length > 0) {
|
|
237
|
+
console.log("\nSweep:");
|
|
238
|
+
for (const n of sweepNotes)
|
|
239
|
+
console.log(` ℹ ${n}`);
|
|
240
|
+
}
|
|
241
|
+
if (otherNotes.length > 0) {
|
|
242
|
+
console.log("\nNotes:");
|
|
243
|
+
for (const n of otherNotes)
|
|
244
|
+
console.log(` ℹ ${n}`);
|
|
174
245
|
}
|
|
175
246
|
return;
|
|
176
247
|
}
|
|
@@ -208,9 +279,23 @@ async function runCommand(command, flags) {
|
|
|
208
279
|
const iterCount = await readSweepIterationsFromFrontmatter(path.resolve(cwd, s.file));
|
|
209
280
|
const iterHint = iterCount ? ` (${iterCount} iterations)` : "";
|
|
210
281
|
const iterFlag = iterCount ? ` --iterations ${iterCount}` : "";
|
|
211
|
-
|
|
282
|
+
let lastRun = "";
|
|
283
|
+
try {
|
|
284
|
+
const artifactsDir = path.resolve(cwd, ".prompts-gpt-runs");
|
|
285
|
+
if (existsSync(artifactsDir)) {
|
|
286
|
+
const { readdir: rd, stat: fsStat } = await import("node:fs/promises");
|
|
287
|
+
const dirs = await rd(artifactsDir);
|
|
288
|
+
const sweepDirs = dirs.filter((d) => d.includes("sweep")).sort().reverse();
|
|
289
|
+
if (sweepDirs[0]) {
|
|
290
|
+
const s2 = await fsStat(path.join(artifactsDir, sweepDirs[0]));
|
|
291
|
+
lastRun = ` | last run: ${s2.mtime.toLocaleDateString()}`;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
catch { /* skip */ }
|
|
296
|
+
console.log(` ${s.name}${iterHint}${lastRun}`);
|
|
212
297
|
console.log(` file: ${s.file}`);
|
|
213
|
-
console.log(` sweep: prompts-gpt sweep
|
|
298
|
+
console.log(` sweep: prompts-gpt sweep -f ${s.file}${iterFlag}`);
|
|
214
299
|
}
|
|
215
300
|
console.log("");
|
|
216
301
|
}
|
|
@@ -234,6 +319,31 @@ async function runCommand(command, flags) {
|
|
|
234
319
|
console.log(`Config: ${assets.configFound ? "found" : "not found — run `prompts-gpt setup`"}`);
|
|
235
320
|
console.log(`Manifest: ${assets.manifestFound ? "found" : "not found — run `prompts-gpt sync`"}`);
|
|
236
321
|
console.log(`Credentials: ${assets.credentialsFound ? "found" : "not found — run `prompts-gpt init --token <token>`"}`);
|
|
322
|
+
if (isTTYInteractive() && availableProviderNames.length > 0) {
|
|
323
|
+
const runnableOptions = [];
|
|
324
|
+
for (const p of assets.prompts) {
|
|
325
|
+
runnableOptions.push({ label: `run: ${p.slug} — ${p.title}`, value: `run:.prompts-gpt/${p.file}` });
|
|
326
|
+
}
|
|
327
|
+
for (const s of assets.sweeps) {
|
|
328
|
+
runnableOptions.push({ label: `sweep: ${s.name}`, value: `sweep:${s.file}` });
|
|
329
|
+
}
|
|
330
|
+
if (runnableOptions.length > 1) {
|
|
331
|
+
console.log("");
|
|
332
|
+
const picked = await interactiveSelect("Run something now?", [
|
|
333
|
+
...runnableOptions,
|
|
334
|
+
{ label: "(exit)", value: "exit" },
|
|
335
|
+
]);
|
|
336
|
+
if (picked !== "exit") {
|
|
337
|
+
const [action, file] = picked.split(":", 2);
|
|
338
|
+
const cmd = action === "sweep" ? "sweep" : "run";
|
|
339
|
+
console.log(`\nRunning: prompts-gpt ${cmd} -f ${file}\n`);
|
|
340
|
+
const { spawnSync: spSync } = await import("node:child_process");
|
|
341
|
+
const cliEntry = process.argv[1] || require.resolve("./cli.js");
|
|
342
|
+
const result = spSync(process.execPath, [cliEntry, cmd, "-f", file], { stdio: "inherit", cwd });
|
|
343
|
+
process.exitCode = result.status ?? 1;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
237
347
|
return;
|
|
238
348
|
}
|
|
239
349
|
if (command === "status") {
|
|
@@ -266,7 +376,22 @@ async function runCommand(command, flags) {
|
|
|
266
376
|
}
|
|
267
377
|
}
|
|
268
378
|
else {
|
|
269
|
-
console.log(" Providers: ✗ — no CLI found");
|
|
379
|
+
console.log(" Providers: ✗ — no CLI found. Install one:");
|
|
380
|
+
console.log(" npm install -g @openai/codex # Codex");
|
|
381
|
+
console.log(" npm install -g @anthropic-ai/claude-code # Claude Code");
|
|
382
|
+
console.log(" # Cursor: install Cursor IDE (includes agent CLI)");
|
|
383
|
+
}
|
|
384
|
+
console.log("");
|
|
385
|
+
const lockFile = path.resolve(cwd, ".sweep.lock");
|
|
386
|
+
if (existsSync(lockFile)) {
|
|
387
|
+
try {
|
|
388
|
+
const { readFile: fsRead } = await import("node:fs/promises");
|
|
389
|
+
const lockData = JSON.parse(await fsRead(lockFile, "utf8"));
|
|
390
|
+
console.log(` Sweep lock: ⚠ active (PID ${lockData.pid}, started ${lockData.startedAt})`);
|
|
391
|
+
}
|
|
392
|
+
catch {
|
|
393
|
+
console.log(" Sweep lock: ⚠ found but unreadable");
|
|
394
|
+
}
|
|
270
395
|
}
|
|
271
396
|
console.log("");
|
|
272
397
|
if (assets.prompts.length > 0 && availableProviders.length > 0) {
|
|
@@ -310,6 +435,20 @@ async function runCommand(command, flags) {
|
|
|
310
435
|
for (const w of result.warnings) {
|
|
311
436
|
console.log(` warning: ${w}`);
|
|
312
437
|
}
|
|
438
|
+
const validAssets = await discoverWorkspaceAssets(cwd);
|
|
439
|
+
if (validAssets.sweeps.length > 0) {
|
|
440
|
+
console.log(`\nSweep files (${validAssets.sweeps.length}):`);
|
|
441
|
+
for (const s of validAssets.sweeps) {
|
|
442
|
+
const fm = await readSweepFrontmatter(path.resolve(cwd, s.file));
|
|
443
|
+
const issues = [];
|
|
444
|
+
if (!fm.iterations)
|
|
445
|
+
issues.push("no iterations in frontmatter");
|
|
446
|
+
if (!fm.title)
|
|
447
|
+
issues.push("no title heading");
|
|
448
|
+
const icon = issues.length === 0 ? "✓" : "⚠";
|
|
449
|
+
console.log(` ${icon} ${s.name}${issues.length > 0 ? ` — ${issues.join(", ")}` : ""}`);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
313
452
|
if (result.valid && result.errors.length === 0 && result.warnings.length === 0) {
|
|
314
453
|
console.log(" No issues found.");
|
|
315
454
|
}
|
|
@@ -323,18 +462,26 @@ async function runCommand(command, flags) {
|
|
|
323
462
|
if (!promptFile && !Boolean(flags.json)) {
|
|
324
463
|
if (!config.defaultPromptFile && config.batchDefaults.promptFiles.length === 0 && !config.batchDefaults.manifestPath) {
|
|
325
464
|
const assets = await discoverWorkspaceAssets(cwd);
|
|
326
|
-
if (assets.prompts.length > 0
|
|
327
|
-
|
|
465
|
+
if (assets.prompts.length > 0 && isTTYInteractive(flags)) {
|
|
466
|
+
const promptOptions = assets.prompts.map((p) => ({
|
|
467
|
+
label: `${p.slug} — ${p.title}`,
|
|
468
|
+
value: `.prompts-gpt/${p.file}`,
|
|
469
|
+
}));
|
|
470
|
+
const picked = await interactiveSelect("Select a prompt to run:", promptOptions);
|
|
471
|
+
flags["prompt-file"] = picked;
|
|
472
|
+
}
|
|
473
|
+
else if (assets.prompts.length > 0 || assets.sweeps.length > 0) {
|
|
474
|
+
console.log("No default prompt configured. Available options:\n");
|
|
328
475
|
if (assets.prompts.length > 0) {
|
|
329
476
|
console.log("Prompt packs:");
|
|
330
477
|
for (const p of assets.prompts) {
|
|
331
|
-
console.log(` prompts-gpt run
|
|
478
|
+
console.log(` prompts-gpt run -f .prompts-gpt/${p.file}`);
|
|
332
479
|
}
|
|
333
480
|
}
|
|
334
481
|
if (assets.sweeps.length > 0) {
|
|
335
482
|
console.log("\nSweep prompts (use `sweep` command):");
|
|
336
483
|
for (const s of assets.sweeps) {
|
|
337
|
-
console.log(` prompts-gpt sweep
|
|
484
|
+
console.log(` prompts-gpt sweep -f ${s.file}`);
|
|
338
485
|
}
|
|
339
486
|
}
|
|
340
487
|
console.log("\nOr set a default: prompts-gpt setup --prompt-file <path>");
|
|
@@ -342,6 +489,36 @@ async function runCommand(command, flags) {
|
|
|
342
489
|
}
|
|
343
490
|
}
|
|
344
491
|
}
|
|
492
|
+
const runProviders = await detectProviders(cwd);
|
|
493
|
+
const runAvailable = runProviders.filter((p) => p.available);
|
|
494
|
+
if (!getStringFlag(flags, "agent") && isTTYInteractive(flags) && !Boolean(flags.json) && runAvailable.length > 1) {
|
|
495
|
+
const lastRunProvider = await getLastUsedProvider(cwd);
|
|
496
|
+
const providerOpts = runAvailable.map((p) => ({
|
|
497
|
+
label: `${p.provider} (${p.modelDefault}${p.version ? `, ${p.version}` : ""})${p.provider === lastRunProvider ? " ★" : ""}`,
|
|
498
|
+
value: p.provider,
|
|
499
|
+
}));
|
|
500
|
+
providerOpts.push({ label: "router (auto-select)", value: "router" });
|
|
501
|
+
if (lastRunProvider) {
|
|
502
|
+
const idx = providerOpts.findIndex((o) => o.value === lastRunProvider);
|
|
503
|
+
if (idx > 0) {
|
|
504
|
+
const [item] = providerOpts.splice(idx, 1);
|
|
505
|
+
providerOpts.unshift(item);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
flags.agent = await interactiveSelect("Select a provider:", providerOpts);
|
|
509
|
+
if (flags.agent !== "router") {
|
|
510
|
+
await saveLastUsedProvider(cwd, flags.agent);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
if (!getStringFlag(flags, "model") && isTTYInteractive(flags) && !Boolean(flags.json)) {
|
|
514
|
+
const currentAgent = resolveRunAgent(flags, config.defaultAgent);
|
|
515
|
+
if (currentAgent !== "router") {
|
|
516
|
+
const modelChoices = getModelChoicesForProvider(currentAgent, config);
|
|
517
|
+
if (modelChoices.length > 0) {
|
|
518
|
+
flags.model = await interactiveSelect("Select a model:", modelChoices);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
345
522
|
const agent = resolveRunAgent(flags, config.defaultAgent);
|
|
346
523
|
if (Boolean(flags["dry-run"])) {
|
|
347
524
|
const providers = await detectProviders(cwd);
|
|
@@ -366,6 +543,23 @@ async function runCommand(command, flags) {
|
|
|
366
543
|
console.log(` Timeout: ${getStringFlag(flags, "timeout") || config.timeoutSeconds}s`);
|
|
367
544
|
return;
|
|
368
545
|
}
|
|
546
|
+
if (isTTYInteractive(flags) && !Boolean(flags.json) && !Boolean(flags["dry-run"])) {
|
|
547
|
+
const previewFile = getStringFlag(flags, "prompt-file");
|
|
548
|
+
if (previewFile && existsSync(path.resolve(cwd, previewFile))) {
|
|
549
|
+
try {
|
|
550
|
+
const { readFile: fsRead } = await import("node:fs/promises");
|
|
551
|
+
const previewContent = await fsRead(path.resolve(cwd, previewFile), "utf8");
|
|
552
|
+
const previewLines = previewContent.split("\n").slice(0, 5);
|
|
553
|
+
console.log(`\nPrompt preview (${path.basename(previewFile)}):`);
|
|
554
|
+
for (const line of previewLines)
|
|
555
|
+
console.log(` ${line}`);
|
|
556
|
+
if (previewContent.split("\n").length > 5)
|
|
557
|
+
console.log(` ... (${previewContent.split("\n").length - 5} more lines)`);
|
|
558
|
+
console.log("");
|
|
559
|
+
}
|
|
560
|
+
catch { /* skip preview */ }
|
|
561
|
+
}
|
|
562
|
+
}
|
|
369
563
|
const modelFlag = getStringFlag(flags, "model");
|
|
370
564
|
if (modelFlag && !Boolean(flags.json) && agent !== "router") {
|
|
371
565
|
const providers = await detectProviders(cwd);
|
|
@@ -404,9 +598,29 @@ async function runCommand(command, flags) {
|
|
|
404
598
|
console.log(`Summary: ${result.summaryFile}`);
|
|
405
599
|
console.log(`Log: ${result.logFile}`);
|
|
406
600
|
if (result.exitCode === 0) {
|
|
601
|
+
try {
|
|
602
|
+
const { readFile: fsRead } = await import("node:fs/promises");
|
|
603
|
+
const deltaContent = await fsRead(result.worktreeDeltaFile, "utf8");
|
|
604
|
+
if (!deltaContent.includes("No worktree delta")) {
|
|
605
|
+
const afterLines = deltaContent.split("\n").filter((l) => l.startsWith("=== AFTER ===") ? false : true);
|
|
606
|
+
const changedFiles = afterLines.filter((l) => /^\s*[MADRCU?!]/.test(l)).length;
|
|
607
|
+
if (changedFiles > 0) {
|
|
608
|
+
console.log(`Files changed: ${changedFiles}`);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
catch { /* skip */ }
|
|
407
613
|
console.log("");
|
|
408
614
|
console.log(`View results: cat ${result.summaryFile}`);
|
|
409
615
|
}
|
|
616
|
+
if (Boolean(flags.open) && result.summaryFile) {
|
|
617
|
+
try {
|
|
618
|
+
const { spawn: openSpawn } = await import("node:child_process");
|
|
619
|
+
const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "notepad" : "xdg-open";
|
|
620
|
+
openSpawn(openCmd, [result.summaryFile], { detached: true, stdio: "ignore" }).unref();
|
|
621
|
+
}
|
|
622
|
+
catch { /* ignore */ }
|
|
623
|
+
}
|
|
410
624
|
if (result.exitCode !== 0) {
|
|
411
625
|
const diagnostics = await extractRunDiagnostics(result.logFile, result.provider, result.model);
|
|
412
626
|
if (diagnostics.length > 0) {
|
|
@@ -477,31 +691,40 @@ async function runCommand(command, flags) {
|
|
|
477
691
|
if (assets.sweeps.length === 1) {
|
|
478
692
|
const autoFile = assets.sweeps[0].file;
|
|
479
693
|
const iterFromFm = await readSweepIterationsFromFrontmatter(path.resolve(cwd, autoFile));
|
|
480
|
-
console.log(`Auto-selected sweep: ${autoFile}${iterFromFm ? ` (${iterFromFm} iterations from frontmatter)` : ""}`);
|
|
694
|
+
console.log(`Auto-selected sweep: ${path.basename(autoFile)}${iterFromFm ? ` (${iterFromFm} iterations from frontmatter)` : ""}`);
|
|
481
695
|
flags["prompt-file"] = autoFile;
|
|
482
696
|
if (iterFromFm && !getStringFlag(flags, "iterations")) {
|
|
483
697
|
flags.iterations = String(iterFromFm);
|
|
484
698
|
}
|
|
485
699
|
}
|
|
486
700
|
else if (assets.sweeps.length > 1) {
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
701
|
+
if (isTTYInteractive(flags)) {
|
|
702
|
+
const sweepOptions = await Promise.all(assets.sweeps.map(async (s) => {
|
|
703
|
+
const fm = await readSweepFrontmatter(path.resolve(cwd, s.file));
|
|
704
|
+
const iterLabel = fm.iterations ? ` (${fm.iterations} iterations)` : "";
|
|
705
|
+
const titleLabel = fm.title ? ` — ${fm.title}` : "";
|
|
706
|
+
return { label: `${path.basename(s.file, ".md")}${titleLabel}${iterLabel}`, value: s.file };
|
|
707
|
+
}));
|
|
708
|
+
const picked = await interactiveSelect("Select a sweep file:", sweepOptions);
|
|
709
|
+
flags["prompt-file"] = picked;
|
|
710
|
+
const iterFromFm = await readSweepIterationsFromFrontmatter(path.resolve(cwd, picked));
|
|
711
|
+
if (iterFromFm && !getStringFlag(flags, "iterations")) {
|
|
712
|
+
flags.iterations = String(iterFromFm);
|
|
713
|
+
}
|
|
492
714
|
}
|
|
493
|
-
|
|
494
|
-
console.log(
|
|
495
|
-
for (const
|
|
496
|
-
|
|
715
|
+
else {
|
|
716
|
+
console.log(`${assets.sweeps.length} sweep files found. Pick one with --prompt-file:\n`);
|
|
717
|
+
for (const s of assets.sweeps) {
|
|
718
|
+
const iterCount = await readSweepIterationsFromFrontmatter(path.resolve(cwd, s.file));
|
|
719
|
+
const iterFlag = iterCount ? ` --iterations ${iterCount}` : "";
|
|
720
|
+
console.log(` prompts-gpt sweep -f ${s.file}${iterFlag}`);
|
|
497
721
|
}
|
|
722
|
+
return;
|
|
498
723
|
}
|
|
499
|
-
return;
|
|
500
724
|
}
|
|
501
725
|
else {
|
|
502
726
|
console.log("No sweep files found.");
|
|
503
727
|
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
728
|
console.log(" Run `prompts-gpt list` to see what's available.");
|
|
506
729
|
return;
|
|
507
730
|
}
|
|
@@ -512,33 +735,200 @@ async function runCommand(command, flags) {
|
|
|
512
735
|
flags.iterations = String(iterFromFm);
|
|
513
736
|
}
|
|
514
737
|
}
|
|
738
|
+
const providers = await detectProviders(cwd);
|
|
739
|
+
const availableProviders = providers.filter((p) => p.available);
|
|
740
|
+
if (!getStringFlag(flags, "agent") && isTTYInteractive(flags) && !Boolean(flags.json) && availableProviders.length > 1) {
|
|
741
|
+
const lastProvider = await getLastUsedProvider(cwd);
|
|
742
|
+
const providerOptions = availableProviders.map((p) => ({
|
|
743
|
+
label: `${p.provider} (${p.modelDefault}${p.version ? `, ${p.version}` : ""})${p.provider === lastProvider ? " ★" : ""}`,
|
|
744
|
+
value: p.provider,
|
|
745
|
+
}));
|
|
746
|
+
providerOptions.push({ label: "router (auto-select best available)", value: "router" });
|
|
747
|
+
if (lastProvider) {
|
|
748
|
+
const idx = providerOptions.findIndex((o) => o.value === lastProvider);
|
|
749
|
+
if (idx > 0) {
|
|
750
|
+
const [item] = providerOptions.splice(idx, 1);
|
|
751
|
+
providerOptions.unshift(item);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
const picked = await interactiveSelect("Select a provider:", providerOptions);
|
|
755
|
+
flags.agent = picked;
|
|
756
|
+
if (picked !== "router") {
|
|
757
|
+
await saveLastUsedProvider(cwd, picked);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
if (!getStringFlag(flags, "model") && isTTYInteractive(flags) && !Boolean(flags.json)) {
|
|
761
|
+
const currentAgent = resolveRunAgent(flags, config.defaultAgent);
|
|
762
|
+
if (currentAgent !== "router") {
|
|
763
|
+
const modelChoices = getModelChoicesForProvider(currentAgent, config);
|
|
764
|
+
if (modelChoices.length > 0) {
|
|
765
|
+
flags.model = await interactiveSelect("Select a model:", modelChoices);
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
if (!getStringFlag(flags, "iterations") && isTTYInteractive(flags) && !Boolean(flags.json)) {
|
|
770
|
+
const fmIter = await readSweepIterationsFromFrontmatter(path.resolve(cwd, getStringFlag(flags, "prompt-file")));
|
|
771
|
+
const defaultIter = fmIter ? String(fmIter) : "1";
|
|
772
|
+
const iterOptions = [
|
|
773
|
+
{ label: `${defaultIter} (default)`, value: defaultIter },
|
|
774
|
+
...(defaultIter !== "1" ? [{ label: "1", value: "1" }] : []),
|
|
775
|
+
...(defaultIter !== "2" ? [{ label: "2", value: "2" }] : []),
|
|
776
|
+
...(defaultIter !== "3" ? [{ label: "3", value: "3" }] : []),
|
|
777
|
+
...(defaultIter !== "5" ? [{ label: "5", value: "5" }] : []),
|
|
778
|
+
];
|
|
779
|
+
flags.iterations = await interactiveSelect("Select iterations:", iterOptions);
|
|
780
|
+
}
|
|
515
781
|
const agent = resolveRunAgent(flags, config.defaultAgent);
|
|
782
|
+
const sweepModelFlag = getStringFlag(flags, "model");
|
|
783
|
+
if (sweepModelFlag && agent !== "router" && !Boolean(flags.json)) {
|
|
784
|
+
const resolvedP = resolveRunProvider(agent, providers, config.providerOrder);
|
|
785
|
+
const check = validateModelForProvider(sweepModelFlag, resolvedP);
|
|
786
|
+
if (!check.valid) {
|
|
787
|
+
console.log(`⚠ Model "${sweepModelFlag}" may not be available for ${resolvedP}.${check.suggestion ? ` Did you mean "${check.suggestion}"?` : ""}`);
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
if (isTTYInteractive(flags) && !Boolean(flags.json) && !Boolean(flags["dry-run"])) {
|
|
791
|
+
const resolvedProvider = resolveRunProvider(agent, providers, config.providerOrder);
|
|
792
|
+
const resolvedModel = getStringFlag(flags, "model")?.trim() || config.modelOverrides[resolvedProvider]?.trim() || availableProviders.find((p) => p.provider === resolvedProvider)?.modelDefault || "auto";
|
|
793
|
+
const resolvedIter = getStringFlag(flags, "iterations") || "1";
|
|
794
|
+
const sweepFile = getStringFlag(flags, "prompt-file") || "(default)";
|
|
795
|
+
const iterTimeout = parsePositiveIntFlag(getStringFlag(flags, "iteration-timeout"), "iteration-timeout") ?? 5400;
|
|
796
|
+
const estMaxMs = parseInt(resolvedIter, 10) * iterTimeout * 1000;
|
|
797
|
+
const maxRetries = parseNonNegativeIntFlag(getStringFlag(flags, "max-retries"), "max-retries") ?? 2;
|
|
798
|
+
console.log("");
|
|
799
|
+
console.log(colorize("╔══════════════════════════════════════════════════════════════╗", "\x1b[36m"));
|
|
800
|
+
console.log(colorize("║ Prompts-GPT Sweep ║", "\x1b[36m"));
|
|
801
|
+
console.log(colorize("╠══════════════════════════════════════════════════════════════╣", "\x1b[36m"));
|
|
802
|
+
console.log(`${colorize("║", "\x1b[36m")} Sweep: ${path.basename(sweepFile).padEnd(46)} ${colorize("║", "\x1b[36m")}`);
|
|
803
|
+
console.log(`${colorize("║", "\x1b[36m")} Provider: ${resolvedProvider.padEnd(46)} ${colorize("║", "\x1b[36m")}`);
|
|
804
|
+
console.log(`${colorize("║", "\x1b[36m")} Model: ${resolvedModel.padEnd(46)} ${colorize("║", "\x1b[36m")}`);
|
|
805
|
+
console.log(`${colorize("║", "\x1b[36m")} Iterations: ${resolvedIter.padEnd(46)} ${colorize("║", "\x1b[36m")}`);
|
|
806
|
+
console.log(`${colorize("║", "\x1b[36m")} Timeout: ${`${formatDuration(iterTimeout * 1000)} per iteration`.padEnd(46)} ${colorize("║", "\x1b[36m")}`);
|
|
807
|
+
console.log(`${colorize("║", "\x1b[36m")} Max time: ${`~${formatDuration(estMaxMs)}`.padEnd(46)} ${colorize("║", "\x1b[36m")}`);
|
|
808
|
+
console.log(`${colorize("║", "\x1b[36m")} Retries: ${String(maxRetries).padEnd(46)} ${colorize("║", "\x1b[36m")}`);
|
|
809
|
+
console.log(colorize("╚══════════════════════════════════════════════════════════════╝", "\x1b[36m"));
|
|
810
|
+
console.log("");
|
|
811
|
+
const confirmOpts = [
|
|
812
|
+
{ label: "Yes, run this sweep", value: "y" },
|
|
813
|
+
{ label: "Cancel", value: "n" },
|
|
814
|
+
];
|
|
815
|
+
const confirm = await interactiveSelect("Run this sweep?", confirmOpts);
|
|
816
|
+
if (confirm === "n") {
|
|
817
|
+
console.log("Cancelled.");
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
const quiet = Boolean(flags.quiet);
|
|
822
|
+
const summaryLineCount = parsePositiveIntFlag(getStringFlag(flags, "summary-lines"), "summary-lines") ?? 40;
|
|
516
823
|
const onProgress = Boolean(flags.json)
|
|
517
824
|
? undefined
|
|
518
825
|
: (event) => {
|
|
519
826
|
if (event.type === "preflight") {
|
|
520
827
|
const report = JSON.parse(event.message);
|
|
521
|
-
console.log(`
|
|
522
|
-
console.log(`
|
|
828
|
+
console.log(`Provider: ${report.provider} | Model: ${report.model} | Iterations: ${report.iterations}`);
|
|
829
|
+
console.log(`Branch: ${report.gitBranch} | Dirty files: ${report.gitDirtyFiles} | Free disk: ${report.diskFreeMb}MB`);
|
|
830
|
+
console.log(`Prompt: ${path.basename(report.promptFile)}`);
|
|
831
|
+
try {
|
|
832
|
+
const fs = require("node:fs");
|
|
833
|
+
const skillDir = path.resolve(cwd, ".agents", "skills");
|
|
834
|
+
const ruleDir = path.resolve(cwd, ".cursor", "rules");
|
|
835
|
+
const skillCount = fs.existsSync(skillDir) ? fs.readdirSync(skillDir, { recursive: true }).filter((f) => String(f).endsWith("SKILL.md")).length : 0;
|
|
836
|
+
const ruleCount = fs.existsSync(ruleDir) ? fs.readdirSync(ruleDir).filter((f) => String(f).endsWith(".mdc")).length : 0;
|
|
837
|
+
const mcpConfig = fs.existsSync(path.resolve(cwd, ".cursor", "mcp.json")) ? "found" : "none";
|
|
838
|
+
console.log(`Skills: ${skillCount} | Rules: ${ruleCount} | MCP config: ${mcpConfig}`);
|
|
839
|
+
}
|
|
840
|
+
catch { /* skip */ }
|
|
841
|
+
const maxRunDirs = parsePositiveIntFlag(getStringFlag(flags, "max-run-dirs"), "max-run-dirs") ?? 20;
|
|
842
|
+
console.log(`Log rotation: keep ${maxRunDirs} runs`);
|
|
523
843
|
for (const w of report.warnings)
|
|
524
|
-
console.log(
|
|
844
|
+
console.log(`⚠ ${w}`);
|
|
525
845
|
}
|
|
526
846
|
else if (event.type === "iteration_start") {
|
|
527
|
-
console.log(`\n
|
|
847
|
+
console.log(`\n${colorize("══════════════════════════════════════════════════════════════", "\x1b[35m")}`);
|
|
848
|
+
console.log(colorize(`[${new Date().toLocaleTimeString()}] Iteration ${event.iteration}/${event.total}: ${event.provider} (${event.model})`, "\x1b[1;35m"));
|
|
849
|
+
console.log(colorize("══════════════════════════════════════════════════════════════", "\x1b[35m"));
|
|
850
|
+
}
|
|
851
|
+
else if (event.type === "message") {
|
|
852
|
+
if (!quiet) {
|
|
853
|
+
console.log(` ${colorize(`[${event.elapsed}]`, "\x1b[36m")} 💬 ${event.text.slice(0, 120)}`);
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
else if (event.type === "tool") {
|
|
857
|
+
if (!quiet) {
|
|
858
|
+
const c = event.counts;
|
|
859
|
+
let icon = "🔧";
|
|
860
|
+
if (event.action.includes("read"))
|
|
861
|
+
icon = "📖";
|
|
862
|
+
else if (event.action.includes("write") || event.action.includes("edit"))
|
|
863
|
+
icon = "✏️ ";
|
|
864
|
+
else if (event.action.includes("shell") || event.action === "bash")
|
|
865
|
+
icon = "⚡";
|
|
866
|
+
else if (event.action.includes("search") || event.action.includes("grep") || event.action.includes("glob"))
|
|
867
|
+
icon = "🔍";
|
|
868
|
+
else if (event.action.includes("todo"))
|
|
869
|
+
icon = "📋";
|
|
870
|
+
const fileStr = event.file ? ` ${colorize(event.file, "\x1b[90m")}` : "";
|
|
871
|
+
const countStr = colorize(`(R:${c.reads} W:${c.writes} S:${c.shells} 🔍:${c.searches})`, "\x1b[90m");
|
|
872
|
+
console.log(` ${icon} ${event.action}${fileStr} ${countStr}`);
|
|
873
|
+
}
|
|
528
874
|
}
|
|
529
875
|
else if (event.type === "iteration_end") {
|
|
530
876
|
const elapsed = formatDuration(event.durationMs);
|
|
531
|
-
|
|
877
|
+
const icon = event.status === "success" ? colorize("✓", "\x1b[32m") : colorize("✗", "\x1b[31m");
|
|
878
|
+
const durations = globalThis.__sweepDurations || [];
|
|
879
|
+
durations.push(event.durationMs);
|
|
880
|
+
globalThis.__sweepDurations = durations;
|
|
881
|
+
const remaining = ((parseInt(String(getStringFlag(flags, "iterations"))) || 1) - event.iteration);
|
|
882
|
+
let etaStr = "";
|
|
883
|
+
if (remaining > 0 && durations.length > 0) {
|
|
884
|
+
const avgMs = durations.reduce((a, b) => a + b, 0) / durations.length;
|
|
885
|
+
etaStr = ` | ETA: ~${formatDuration(avgMs * remaining)}`;
|
|
886
|
+
}
|
|
887
|
+
console.log(`${icon} Iteration ${event.iteration} ${event.status} (${elapsed})${etaStr}`);
|
|
532
888
|
}
|
|
533
889
|
else if (event.type === "attempt_retry") {
|
|
534
|
-
console.log(`
|
|
890
|
+
console.log(colorize(` ⟳ Retry ${event.attempt}/${event.maxAttempts} for iteration ${event.iteration}, backoff ${event.backoffMs}ms`, "\x1b[33m"));
|
|
535
891
|
}
|
|
536
892
|
else if (event.type === "summary") {
|
|
537
|
-
|
|
538
|
-
|
|
893
|
+
if (summaryLineCount > 0) {
|
|
894
|
+
const preview = event.lines.slice(0, summaryLineCount);
|
|
895
|
+
console.log(`\n── Summary (iteration ${event.iteration}, ${preview.length}/${event.lines.length} lines) ──`);
|
|
896
|
+
for (const line of preview)
|
|
897
|
+
console.log(` ${line}`);
|
|
898
|
+
if (event.lines.length > summaryLineCount) {
|
|
899
|
+
console.log(` ... (${event.lines.length - summaryLineCount} more lines)`);
|
|
900
|
+
}
|
|
901
|
+
console.log("── End summary ──");
|
|
902
|
+
}
|
|
539
903
|
}
|
|
540
904
|
else if (event.type === "sweep_end") {
|
|
541
|
-
|
|
905
|
+
const r = event.result;
|
|
906
|
+
console.log("");
|
|
907
|
+
console.log(colorize("╔══════════════════════════════════════════════════════════════╗", "\x1b[36m"));
|
|
908
|
+
console.log(colorize("║ Sweep Complete ║", "\x1b[36m"));
|
|
909
|
+
console.log(colorize("╠══════════════════════════════════════════════════════════════╣", "\x1b[36m"));
|
|
910
|
+
console.log(`${colorize("║", "\x1b[36m")} Model: ${r.model.padEnd(45)} ${colorize("║", "\x1b[36m")}`);
|
|
911
|
+
console.log(`${colorize("║", "\x1b[36m")} Duration: ${formatDuration(r.totalDurationMs).padEnd(45)} ${colorize("║", "\x1b[36m")}`);
|
|
912
|
+
console.log(`${colorize("║", "\x1b[36m")} Iterations: ${String(r.totalIterations).padEnd(45)} ${colorize("║", "\x1b[36m")}`);
|
|
913
|
+
console.log(`${colorize("║", "\x1b[36m")} Succeeded: ${colorize(String(r.succeeded), "\x1b[32m").padEnd(54)} ${colorize("║", "\x1b[36m")}`);
|
|
914
|
+
if (r.failed > 0) {
|
|
915
|
+
console.log(`${colorize("║", "\x1b[36m")} Failed: ${colorize(String(r.failed), "\x1b[31m").padEnd(54)} ${colorize("║", "\x1b[36m")}`);
|
|
916
|
+
}
|
|
917
|
+
console.log(colorize("╠══════════════════════════════════════════════════════════════╣", "\x1b[36m"));
|
|
918
|
+
for (const iter of r.iterations) {
|
|
919
|
+
const statusColor = iter.status === "success" ? "\x1b[32m" : "\x1b[31m";
|
|
920
|
+
const iterLine = ` Iter ${iter.iteration}: ${colorize(iter.status.padEnd(16), statusColor)} ${formatDuration(iter.durationMs)}`;
|
|
921
|
+
console.log(`${colorize("║", "\x1b[36m")}${iterLine.padEnd(60)} ${colorize("║", "\x1b[36m")}`);
|
|
922
|
+
}
|
|
923
|
+
console.log(colorize("╚══════════════════════════════════════════════════════════════╝", "\x1b[36m"));
|
|
924
|
+
if (hasTokenUsage(r.tokenUsage)) {
|
|
925
|
+
const tu = r.tokenUsage;
|
|
926
|
+
console.log(`Tokens: ${formatTokenUsage(tu)}`);
|
|
927
|
+
const estCost = ((tu.inputTokens || 0) * 0.003 + (tu.outputTokens || 0) * 0.015) / 1000;
|
|
928
|
+
if (estCost > 0.001) {
|
|
929
|
+
console.log(`Estimated cost: ~$${estCost.toFixed(2)}`);
|
|
930
|
+
}
|
|
931
|
+
}
|
|
542
932
|
}
|
|
543
933
|
};
|
|
544
934
|
const result = await sweepPrompt({
|
|
@@ -619,9 +1009,37 @@ async function runCommand(command, flags) {
|
|
|
619
1009
|
console.log("Inspect results:");
|
|
620
1010
|
console.log(` cat ${result.manifestFile}`);
|
|
621
1011
|
console.log(` ls ${result.runDir}`);
|
|
1012
|
+
if (Boolean(flags.open) && result.manifestFile) {
|
|
1013
|
+
try {
|
|
1014
|
+
const { spawn: openSpawn } = await import("node:child_process");
|
|
1015
|
+
const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "notepad" : "xdg-open";
|
|
1016
|
+
openSpawn(openCmd, [result.manifestFile], { detached: true, stdio: "ignore" }).unref();
|
|
1017
|
+
}
|
|
1018
|
+
catch { /* ignore */ }
|
|
1019
|
+
}
|
|
622
1020
|
if (result.failed > 0) {
|
|
1021
|
+
const failedIters = result.iterations.filter((it) => it.status !== "success");
|
|
1022
|
+
for (const fi of failedIters) {
|
|
1023
|
+
try {
|
|
1024
|
+
const { readFile: fsRead } = await import("node:fs/promises");
|
|
1025
|
+
const logText = await fsRead(fi.logFile, "utf8");
|
|
1026
|
+
const tailLines = logText.split("\n").slice(-80);
|
|
1027
|
+
if (tailLines.length > 0) {
|
|
1028
|
+
console.log(`\n── Last 80 lines of iteration ${fi.iteration} log ──`);
|
|
1029
|
+
for (const line of tailLines)
|
|
1030
|
+
console.log(` ${line}`);
|
|
1031
|
+
console.log("── End log tail ──");
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
catch { /* skip */ }
|
|
1035
|
+
}
|
|
623
1036
|
process.exitCode = 1;
|
|
624
1037
|
}
|
|
1038
|
+
console.log("\nNext steps:");
|
|
1039
|
+
console.log(" 1. Review iteration summaries in the run directory");
|
|
1040
|
+
if (result.failed > 0) {
|
|
1041
|
+
console.log(` 2. Re-run failed: prompts-gpt sweep -f ${getStringFlag(flags, "prompt-file")} -n ${result.failed}`);
|
|
1042
|
+
}
|
|
625
1043
|
return;
|
|
626
1044
|
}
|
|
627
1045
|
if (command === "quickstart") {
|
|
@@ -632,7 +1050,32 @@ async function runCommand(command, flags) {
|
|
|
632
1050
|
console.log("Prompts-GPT Quickstart");
|
|
633
1051
|
console.log("======================");
|
|
634
1052
|
console.log("");
|
|
1053
|
+
const { spawnSync: gitCheck } = await import("node:child_process");
|
|
1054
|
+
const gitResult = gitCheck("git", ["rev-parse", "--is-inside-work-tree"], { cwd, encoding: "utf8", timeout: 5000, windowsHide: true });
|
|
1055
|
+
if (gitResult.status !== 0) {
|
|
1056
|
+
console.log("⚠ Not inside a git repository. Prompts-GPT works best in a git repo for worktree tracking.");
|
|
1057
|
+
console.log(" Run: git init\n");
|
|
1058
|
+
}
|
|
635
1059
|
if (!assets.credentialsFound) {
|
|
1060
|
+
let networkOk = true;
|
|
1061
|
+
try {
|
|
1062
|
+
const netCheck = await Promise.race([
|
|
1063
|
+
fetch("https://prompts-gpt.com/api/health", { method: "HEAD" }),
|
|
1064
|
+
new Promise((_, rej) => setTimeout(() => rej(new Error("timeout")), 5000)),
|
|
1065
|
+
]);
|
|
1066
|
+
networkOk = netCheck instanceof Response;
|
|
1067
|
+
}
|
|
1068
|
+
catch {
|
|
1069
|
+
networkOk = false;
|
|
1070
|
+
}
|
|
1071
|
+
if (!networkOk) {
|
|
1072
|
+
console.log("⚠ Network unavailable. Quickstart requires internet for token validation.\n");
|
|
1073
|
+
console.log("You can still use local sweep files without a network connection:");
|
|
1074
|
+
console.log(" 1. Create .prompts-gpt/sweeps/<name>.md with your sweep prompt");
|
|
1075
|
+
console.log(" 2. Run: prompts-gpt sweep");
|
|
1076
|
+
console.log("\nWhen online, run `prompts-gpt quickstart` again.");
|
|
1077
|
+
return;
|
|
1078
|
+
}
|
|
636
1079
|
console.log("Step 1: Save your project token");
|
|
637
1080
|
console.log(" prompts-gpt init --token-prompt");
|
|
638
1081
|
console.log("");
|
|
@@ -694,8 +1137,21 @@ async function runCommand(command, flags) {
|
|
|
694
1137
|
}
|
|
695
1138
|
}
|
|
696
1139
|
catch (syncErr) {
|
|
697
|
-
|
|
698
|
-
|
|
1140
|
+
const syncMsg = syncErr instanceof Error ? syncErr.message : String(syncErr);
|
|
1141
|
+
if (syncErr instanceof PromptsGptApiError && (syncErr.status === 401 || syncErr.status === 403)) {
|
|
1142
|
+
console.log(`✗ Authentication failed: ${syncMsg}`);
|
|
1143
|
+
console.log(" Your token may be invalid or expired. Re-run:");
|
|
1144
|
+
console.log(" prompts-gpt init --token-prompt");
|
|
1145
|
+
}
|
|
1146
|
+
else if (syncMsg.includes("ENOTFOUND") || syncMsg.includes("ECONNREFUSED") || syncMsg.includes("fetch") || syncMsg.includes("network")) {
|
|
1147
|
+
console.log(`✗ Network error: ${syncMsg}`);
|
|
1148
|
+
console.log(" Check your internet connection and try again.");
|
|
1149
|
+
console.log(" You can still use local sweep files without network access.");
|
|
1150
|
+
}
|
|
1151
|
+
else {
|
|
1152
|
+
console.log(`✗ Sync failed: ${syncMsg}`);
|
|
1153
|
+
console.log(" Run manually: prompts-gpt sync");
|
|
1154
|
+
}
|
|
699
1155
|
return;
|
|
700
1156
|
}
|
|
701
1157
|
const refreshedAssets = await discoverWorkspaceAssets(cwd);
|
|
@@ -705,13 +1161,37 @@ async function runCommand(command, flags) {
|
|
|
705
1161
|
}
|
|
706
1162
|
console.log(`✓ Prompts: ${assets.prompts.length} prompt packs, ${assets.sweeps.length} sweeps`);
|
|
707
1163
|
console.log("");
|
|
708
|
-
console.log("You're ready!
|
|
1164
|
+
console.log("You're ready!");
|
|
1165
|
+
if (isTTYInteractive() && (assets.prompts.length > 0 || assets.sweeps.length > 0)) {
|
|
1166
|
+
const qsOptions = [];
|
|
1167
|
+
for (const s of assets.sweeps) {
|
|
1168
|
+
qsOptions.push({ label: `sweep: ${s.name}`, value: `sweep:${s.file}` });
|
|
1169
|
+
}
|
|
1170
|
+
for (const p of assets.prompts.slice(0, 5)) {
|
|
1171
|
+
qsOptions.push({ label: `run: ${p.title}`, value: `run:.prompts-gpt/${p.file}` });
|
|
1172
|
+
}
|
|
1173
|
+
qsOptions.push({ label: "(show commands and exit)", value: "exit" });
|
|
1174
|
+
console.log("");
|
|
1175
|
+
const qsPicked = await interactiveSelect("Run something now?", qsOptions);
|
|
1176
|
+
if (qsPicked !== "exit") {
|
|
1177
|
+
const [action, file] = qsPicked.split(":", 2);
|
|
1178
|
+
const cmd = action === "sweep" ? "sweep" : "run";
|
|
1179
|
+
console.log(`\nRunning: prompts-gpt ${cmd} -f ${file}\n`);
|
|
1180
|
+
const { spawnSync: spSync } = await import("node:child_process");
|
|
1181
|
+
const cliEntry = process.argv[1] || require.resolve("./cli.js");
|
|
1182
|
+
const result = spSync(process.execPath, [cliEntry, cmd, "-f", file], { stdio: "inherit", cwd });
|
|
1183
|
+
process.exitCode = result.status ?? 1;
|
|
1184
|
+
return;
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
console.log("");
|
|
1188
|
+
console.log("Try these commands:");
|
|
709
1189
|
console.log("");
|
|
710
1190
|
if (assets.prompts.length > 0) {
|
|
711
|
-
console.log(` prompts-gpt run
|
|
1191
|
+
console.log(` prompts-gpt run -f .prompts-gpt/${assets.prompts[0].file}`);
|
|
712
1192
|
}
|
|
713
1193
|
if (assets.sweeps.length > 0) {
|
|
714
|
-
console.log(` prompts-gpt sweep
|
|
1194
|
+
console.log(` prompts-gpt sweep -f ${assets.sweeps[0].file}`);
|
|
715
1195
|
}
|
|
716
1196
|
console.log(" prompts-gpt list — see all available prompts and sweeps");
|
|
717
1197
|
console.log(" prompts-gpt status — check workspace readiness");
|
|
@@ -736,13 +1216,21 @@ async function runCommand(command, flags) {
|
|
|
736
1216
|
const providers = await detectProviders(cwd);
|
|
737
1217
|
const availableProviders = providers.filter((p) => p.available);
|
|
738
1218
|
let configError = null;
|
|
739
|
-
|
|
1219
|
+
let doOverwrite = true;
|
|
1220
|
+
const existingConfig = existsSync(path.resolve(cwd, DEFAULT_RUN_CONFIG_PATH));
|
|
1221
|
+
if (existingConfig && isTTYInteractive() && !Boolean(flags.json)) {
|
|
1222
|
+
const confirmOverwrite = await interactiveInput("Existing run config found. Overwrite? [Y/n]", "Y");
|
|
1223
|
+
if (confirmOverwrite.toLowerCase() === "n" || confirmOverwrite.toLowerCase() === "no") {
|
|
1224
|
+
doOverwrite = false;
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
const configResult = doOverwrite ? await initRunConfig({
|
|
740
1228
|
cwd,
|
|
741
1229
|
overwrite: true,
|
|
742
1230
|
}).catch((err) => {
|
|
743
1231
|
configError = err.message;
|
|
744
1232
|
return null;
|
|
745
|
-
});
|
|
1233
|
+
}) : null;
|
|
746
1234
|
if (Boolean(flags.json)) {
|
|
747
1235
|
console.log(JSON.stringify({
|
|
748
1236
|
project: { brandName: project.brandName, websiteUrl: project.websiteUrl },
|
|
@@ -782,7 +1270,8 @@ async function runCommand(command, flags) {
|
|
|
782
1270
|
let token = await resolveTokenInput(flags, { command });
|
|
783
1271
|
if (!token && process.stdin.isTTY && process.stdout.isTTY) {
|
|
784
1272
|
console.log("No token flag provided — prompting interactively.");
|
|
785
|
-
console.log("Get your token from: https://prompts-gpt.com/studio/projects
|
|
1273
|
+
console.log("Get your token from: https://prompts-gpt.com/studio/projects");
|
|
1274
|
+
console.log("Token format: pgpt_xxxxxxxxxxxxxxxx (starts with 'pgpt_')\n");
|
|
786
1275
|
token = await readTokenFromPrompt(command);
|
|
787
1276
|
}
|
|
788
1277
|
if (!token) {
|
|
@@ -822,6 +1311,17 @@ async function runCommand(command, flags) {
|
|
|
822
1311
|
});
|
|
823
1312
|
console.log(`Saved Prompts-GPT credentials to ${result.credentialsPath}`);
|
|
824
1313
|
console.log("The credentials file is added to .gitignore.");
|
|
1314
|
+
if (isTTYInteractive()) {
|
|
1315
|
+
console.log("");
|
|
1316
|
+
const runQs = await interactiveInput("Run quickstart now? [Y/n]", "Y");
|
|
1317
|
+
if (runQs.toLowerCase() !== "n" && runQs.toLowerCase() !== "no") {
|
|
1318
|
+
const { spawnSync: spSync } = await import("node:child_process");
|
|
1319
|
+
const cliEntry = process.argv[1] || require.resolve("./cli.js");
|
|
1320
|
+
const qsResult = spSync(process.execPath, [cliEntry, "quickstart", "--cwd", cwd], { stdio: "inherit", cwd });
|
|
1321
|
+
process.exitCode = qsResult.status ?? 1;
|
|
1322
|
+
return;
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
825
1325
|
console.log("");
|
|
826
1326
|
console.log("Next steps:");
|
|
827
1327
|
console.log(" prompts-gpt quickstart — interactive setup (recommended)");
|
|
@@ -836,19 +1336,29 @@ async function runCommand(command, flags) {
|
|
|
836
1336
|
console.log("No prompts matched the query. Try different filters or check the project prompt library.");
|
|
837
1337
|
return;
|
|
838
1338
|
}
|
|
1339
|
+
const pullCwd = getResolvedCwd(flags);
|
|
1340
|
+
const pullOutDir = getStringFlag(flags, "out") || DEFAULT_PROMPTS_GPT_OUT_DIR;
|
|
839
1341
|
const result = await writePromptMarkdownFiles(prompts, {
|
|
840
|
-
cwd:
|
|
841
|
-
outDir:
|
|
1342
|
+
cwd: pullCwd,
|
|
1343
|
+
outDir: pullOutDir,
|
|
842
1344
|
overwrite: Boolean(flags.overwrite),
|
|
843
1345
|
});
|
|
1346
|
+
const pullManifest = await writePromptManifest(prompts, { cwd: pullCwd, outDir: pullOutDir });
|
|
844
1347
|
console.log(`Wrote ${result.written.length} prompt file(s) to ${result.outDir}.`);
|
|
1348
|
+
console.log(`Updated manifest: ${pullManifest.manifestPath}`);
|
|
1349
|
+
if (Boolean(flags.overwrite)) {
|
|
1350
|
+
const agentResult = await writeAgentFiles(prompts, { cwd: pullCwd, agent: "all", overwriteAgentFiles: true });
|
|
1351
|
+
if (agentResult.written.length > 0) {
|
|
1352
|
+
console.log(`Updated ${agentResult.written.length} agent file(s): ${agentResult.targets.join(", ")}`);
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
845
1355
|
if (result.skipped.length) {
|
|
846
1356
|
console.log(`Skipped ${result.skipped.length} existing file(s). Use --overwrite to replace them.`);
|
|
847
1357
|
}
|
|
848
1358
|
if (result.written.length > 0) {
|
|
849
1359
|
console.log("");
|
|
850
1360
|
console.log("Run a pulled prompt:");
|
|
851
|
-
console.log(` prompts-gpt run
|
|
1361
|
+
console.log(` prompts-gpt run -f ${result.written[0]}`);
|
|
852
1362
|
console.log(" prompts-gpt list — see all available prompts");
|
|
853
1363
|
}
|
|
854
1364
|
return;
|
|
@@ -880,7 +1390,9 @@ async function runCommand(command, flags) {
|
|
|
880
1390
|
const target = getStringFlag(flags, "agent") || "all";
|
|
881
1391
|
const pulled = await client.pullPrompts().catch((err) => {
|
|
882
1392
|
if (!Boolean(flags.json)) {
|
|
883
|
-
console.error(
|
|
1393
|
+
console.error(`⚠ Could not pull existing prompts for agent sync: ${err.message}`);
|
|
1394
|
+
console.error(" Agent files will only include the newly generated prompt.");
|
|
1395
|
+
console.error(" Run \`prompts-gpt sync\` afterward to include all library prompts.");
|
|
884
1396
|
}
|
|
885
1397
|
return [];
|
|
886
1398
|
});
|
|
@@ -902,6 +1414,21 @@ async function runCommand(command, flags) {
|
|
|
902
1414
|
if (result.skipped.length) {
|
|
903
1415
|
console.log("Skipped existing generated prompt. Use --overwrite to replace it.");
|
|
904
1416
|
}
|
|
1417
|
+
const genFile = result.written[0];
|
|
1418
|
+
if (genFile && isTTYInteractive()) {
|
|
1419
|
+
const genProviders = await detectProviders(cwd);
|
|
1420
|
+
if (genProviders.some((p) => p.available)) {
|
|
1421
|
+
console.log("");
|
|
1422
|
+
const runNow = await interactiveInput("Run this prompt now? [Y/n]", "Y");
|
|
1423
|
+
if (runNow.toLowerCase() !== "n" && runNow.toLowerCase() !== "no") {
|
|
1424
|
+
console.log(`\nRunning: prompts-gpt run -f ${genFile}\n`);
|
|
1425
|
+
const { spawnSync: spSync } = await import("node:child_process");
|
|
1426
|
+
const cliEntry = process.argv[1] || require.resolve("./cli.js");
|
|
1427
|
+
const genResult = spSync(process.execPath, [cliEntry, "run", "-f", genFile], { stdio: "inherit", cwd });
|
|
1428
|
+
process.exitCode = genResult.status ?? 1;
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
905
1432
|
return;
|
|
906
1433
|
}
|
|
907
1434
|
if (command === "sync") {
|
|
@@ -911,7 +1438,18 @@ async function runCommand(command, flags) {
|
|
|
911
1438
|
const prompts = [];
|
|
912
1439
|
const goal = getStringFlag(flags, "goal");
|
|
913
1440
|
const generatedOnly = Boolean(flags["generated-only"]);
|
|
914
|
-
|
|
1441
|
+
let client;
|
|
1442
|
+
try {
|
|
1443
|
+
client = await createClientForCommand(command, flags);
|
|
1444
|
+
}
|
|
1445
|
+
catch (clientErr) {
|
|
1446
|
+
if (clientErr instanceof CliError && clientErr.exitCode === CLI_EXIT_CODES.auth) {
|
|
1447
|
+
throw new CliError(`Authentication failed. Your token may be invalid, expired, or lack the required scope.\n\n` +
|
|
1448
|
+
`Re-run: prompts-gpt init --token-prompt\n` +
|
|
1449
|
+
`Get a new token: https://prompts-gpt.com/studio/projects`, CLI_EXIT_CODES.auth, { helpCommand: "sync" });
|
|
1450
|
+
}
|
|
1451
|
+
throw clientErr;
|
|
1452
|
+
}
|
|
915
1453
|
if (goal && !Boolean(flags.json)) {
|
|
916
1454
|
printDataTransmissionNotice("sync", { goal, context: getStringFlag(flags, "context"), constraints: getStringFlag(flags, "constraints") });
|
|
917
1455
|
}
|
|
@@ -927,9 +1465,13 @@ async function runCommand(command, flags) {
|
|
|
927
1465
|
});
|
|
928
1466
|
}
|
|
929
1467
|
if (Boolean(flags["dry-run"])) {
|
|
930
|
-
|
|
1468
|
+
const agentTarget = getStringFlag(flags, "agent") || "all";
|
|
1469
|
+
console.log(`[dry-run] Would sync ${prompts.length} prompt(s) for agent target: ${agentTarget}`);
|
|
931
1470
|
for (const p of prompts)
|
|
932
1471
|
console.log(` - ${p.title} (${p.source})`);
|
|
1472
|
+
const targetList = agentTarget === "all" ? SUPPORTED_AGENT_TARGETS.join(", ") : agentTarget;
|
|
1473
|
+
console.log(`\n Agent file targets: ${targetList}`);
|
|
1474
|
+
console.log(` Output dir: ${getStringFlag(flags, "out") || DEFAULT_PROMPTS_GPT_OUT_DIR}`);
|
|
933
1475
|
return;
|
|
934
1476
|
}
|
|
935
1477
|
const result = await syncPrompts(prompts, {
|
|
@@ -983,6 +1525,18 @@ async function runCommand(command, flags) {
|
|
|
983
1525
|
console.log(`Personas: ${formatList(project.targetPersonas)}`);
|
|
984
1526
|
console.log(`Competitors: ${formatCompetitors(project.competitors)}`);
|
|
985
1527
|
console.log(`API URL: ${apiUrl}`);
|
|
1528
|
+
if (isTTYInteractive()) {
|
|
1529
|
+
console.log("");
|
|
1530
|
+
const syncNow = await interactiveInput("Sync prompts from this project? [Y/n]", "Y");
|
|
1531
|
+
if (syncNow.toLowerCase() !== "n" && syncNow.toLowerCase() !== "no") {
|
|
1532
|
+
console.log("\nSyncing...");
|
|
1533
|
+
const { spawnSync: spSync } = await import("node:child_process");
|
|
1534
|
+
const cliEntry = process.argv[1] || require.resolve("./cli.js");
|
|
1535
|
+
const projCwd = getResolvedCwd(flags);
|
|
1536
|
+
const syncResult = spSync(process.execPath, [cliEntry, "sync", "--cwd", projCwd], { stdio: "inherit", cwd: projCwd });
|
|
1537
|
+
process.exitCode = syncResult.status ?? 1;
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
986
1540
|
return;
|
|
987
1541
|
}
|
|
988
1542
|
}
|
|
@@ -1004,6 +1558,9 @@ async function resolveClientOptions(command, flags) {
|
|
|
1004
1558
|
// CI fallback — never read process.env in the importable SDK, only in the CLI entrypoint
|
|
1005
1559
|
const envToken = process.env.PROMPTS_GPT_TOKEN?.trim();
|
|
1006
1560
|
if (envToken) {
|
|
1561
|
+
if (!envToken.startsWith("pgpt_")) {
|
|
1562
|
+
throw new CliError("PROMPTS_GPT_TOKEN env var must start with 'pgpt_'. Check the value.", CLI_EXIT_CODES.auth);
|
|
1563
|
+
}
|
|
1007
1564
|
return {
|
|
1008
1565
|
token: envToken,
|
|
1009
1566
|
apiUrl: explicitApiUrl || credentials?.apiUrl || DEFAULT_PROMPTS_GPT_API_URL,
|
|
@@ -1194,6 +1751,9 @@ function getCommandOptions(command) {
|
|
|
1194
1751
|
background: { type: "boolean" },
|
|
1195
1752
|
"permission-mode": { type: "string" },
|
|
1196
1753
|
"dry-run": { type: "boolean" },
|
|
1754
|
+
"non-interactive": { type: "boolean" },
|
|
1755
|
+
verbose: { type: "boolean", short: "v" },
|
|
1756
|
+
open: { type: "boolean" },
|
|
1197
1757
|
};
|
|
1198
1758
|
}
|
|
1199
1759
|
if (command === "sweep") {
|
|
@@ -1217,6 +1777,10 @@ function getCommandOptions(command) {
|
|
|
1217
1777
|
"summary-lines": { type: "string" },
|
|
1218
1778
|
background: { type: "boolean" },
|
|
1219
1779
|
"permission-mode": { type: "string" },
|
|
1780
|
+
"non-interactive": { type: "boolean" },
|
|
1781
|
+
verbose: { type: "boolean", short: "v" },
|
|
1782
|
+
open: { type: "boolean" },
|
|
1783
|
+
quiet: { type: "boolean", short: "q" },
|
|
1220
1784
|
};
|
|
1221
1785
|
}
|
|
1222
1786
|
if (command === "run-batch") {
|
|
@@ -1230,6 +1794,7 @@ function getCommandOptions(command) {
|
|
|
1230
1794
|
model: { type: "string" },
|
|
1231
1795
|
timeout: { type: "string" },
|
|
1232
1796
|
"artifacts-dir": { type: "string" },
|
|
1797
|
+
"non-interactive": { type: "boolean" },
|
|
1233
1798
|
};
|
|
1234
1799
|
}
|
|
1235
1800
|
if (command === "load-config") {
|
|
@@ -1369,7 +1934,12 @@ async function readTokenFromStdin() {
|
|
|
1369
1934
|
}
|
|
1370
1935
|
chunks.push(buffer);
|
|
1371
1936
|
}
|
|
1372
|
-
|
|
1937
|
+
const combined = Buffer.concat(chunks);
|
|
1938
|
+
const result = combined.toString("utf8").trim();
|
|
1939
|
+
combined.fill(0);
|
|
1940
|
+
for (const chunk of chunks)
|
|
1941
|
+
chunk.fill(0);
|
|
1942
|
+
return result;
|
|
1373
1943
|
}
|
|
1374
1944
|
function readTokenFromPrompt(command) {
|
|
1375
1945
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
@@ -1445,6 +2015,7 @@ function readTokenFromPrompt(command) {
|
|
|
1445
2015
|
}
|
|
1446
2016
|
buf.write(char, bufLen, "utf8");
|
|
1447
2017
|
bufLen += charBytes;
|
|
2018
|
+
stdout.write("*");
|
|
1448
2019
|
}
|
|
1449
2020
|
}
|
|
1450
2021
|
};
|
|
@@ -1640,17 +2211,86 @@ function parseNonNegativeIntFlag(raw, flagName) {
|
|
|
1640
2211
|
}
|
|
1641
2212
|
return value;
|
|
1642
2213
|
}
|
|
2214
|
+
async function getLastUsedProvider(cwd) {
|
|
2215
|
+
try {
|
|
2216
|
+
const { readFile: fsRead } = await import("node:fs/promises");
|
|
2217
|
+
const data = JSON.parse(await fsRead(path.resolve(cwd, DEFAULT_PROMPTS_GPT_OUT_DIR, ".last-provider"), "utf8"));
|
|
2218
|
+
return data.provider ?? null;
|
|
2219
|
+
}
|
|
2220
|
+
catch {
|
|
2221
|
+
return null;
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2224
|
+
async function saveLastUsedProvider(cwd, provider) {
|
|
2225
|
+
try {
|
|
2226
|
+
const { writeFile: fsWrite, mkdir: fsMkdir } = await import("node:fs/promises");
|
|
2227
|
+
const dir = path.resolve(cwd, DEFAULT_PROMPTS_GPT_OUT_DIR);
|
|
2228
|
+
await fsMkdir(dir, { recursive: true });
|
|
2229
|
+
await fsWrite(path.resolve(dir, ".last-provider"), JSON.stringify({ provider, updatedAt: new Date().toISOString() }));
|
|
2230
|
+
}
|
|
2231
|
+
catch { /* ignore */ }
|
|
2232
|
+
}
|
|
2233
|
+
function validateModelForProvider(model, provider) {
|
|
2234
|
+
const allKnown = {
|
|
2235
|
+
codex: ["gpt-5.4-mini", "o4-mini", "o3", "gpt-5.1-codex", "gpt-4.1"],
|
|
2236
|
+
claude: ["claude-sonnet-4-6", "claude-4.5-opus", "claude-4.6-sonnet", "claude-haiku-3.5", "claude-4.6-opus-high", "claude-4.6-sonnet-high"],
|
|
2237
|
+
cursor: ["auto", "claude-4.6-sonnet", "gpt-5.4-mini", "claude-4.5-opus", "claude-4.6-opus-high", "claude-4.6-sonnet-high", "composer-2", "composer-2-fast", "gpt-5.3-codex"],
|
|
2238
|
+
copilot: ["auto", "gpt-5.4-mini", "claude-4.6-sonnet"],
|
|
2239
|
+
};
|
|
2240
|
+
const known = allKnown[provider] ?? [];
|
|
2241
|
+
if (known.length === 0)
|
|
2242
|
+
return { valid: true, suggestion: "" };
|
|
2243
|
+
if (known.includes(model))
|
|
2244
|
+
return { valid: true, suggestion: "" };
|
|
2245
|
+
const close = known.find((k) => k.startsWith(model.slice(0, 5)));
|
|
2246
|
+
return { valid: false, suggestion: close ?? known[0] ?? "" };
|
|
2247
|
+
}
|
|
2248
|
+
function getModelChoicesForProvider(provider, config) {
|
|
2249
|
+
const KNOWN_MODELS = {
|
|
2250
|
+
codex: [
|
|
2251
|
+
{ label: "gpt-5.4-mini (default)", value: "gpt-5.4-mini" },
|
|
2252
|
+
{ label: "o4-mini", value: "o4-mini" },
|
|
2253
|
+
{ label: "o3", value: "o3" },
|
|
2254
|
+
{ label: "gpt-5.1-codex", value: "gpt-5.1-codex" },
|
|
2255
|
+
{ label: "gpt-4.1", value: "gpt-4.1" },
|
|
2256
|
+
],
|
|
2257
|
+
claude: [
|
|
2258
|
+
{ label: "claude-sonnet-4-6 (default)", value: "claude-sonnet-4-6" },
|
|
2259
|
+
{ label: "claude-4.5-opus", value: "claude-4.5-opus" },
|
|
2260
|
+
{ label: "claude-4.6-sonnet", value: "claude-4.6-sonnet" },
|
|
2261
|
+
{ label: "claude-haiku-3.5", value: "claude-haiku-3.5" },
|
|
2262
|
+
],
|
|
2263
|
+
cursor: [
|
|
2264
|
+
{ label: "auto (default)", value: "auto" },
|
|
2265
|
+
{ label: "claude-4.6-sonnet", value: "claude-4.6-sonnet" },
|
|
2266
|
+
{ label: "gpt-5.4-mini", value: "gpt-5.4-mini" },
|
|
2267
|
+
{ label: "claude-4.5-opus", value: "claude-4.5-opus" },
|
|
2268
|
+
],
|
|
2269
|
+
copilot: [
|
|
2270
|
+
{ label: "auto (default)", value: "auto" },
|
|
2271
|
+
{ label: "gpt-5.4-mini", value: "gpt-5.4-mini" },
|
|
2272
|
+
{ label: "claude-4.6-sonnet", value: "claude-4.6-sonnet" },
|
|
2273
|
+
],
|
|
2274
|
+
};
|
|
2275
|
+
const choices = KNOWN_MODELS[provider] ?? [];
|
|
2276
|
+
const override = config.modelOverrides[provider]?.trim();
|
|
2277
|
+
if (override && !choices.some((c) => c.value === override)) {
|
|
2278
|
+
choices.unshift({ label: `${override} (configured)`, value: override });
|
|
2279
|
+
}
|
|
2280
|
+
return choices;
|
|
2281
|
+
}
|
|
1643
2282
|
function resolveRunAgent(flags, fallback) {
|
|
1644
2283
|
const raw = getStringFlag(flags, "agent");
|
|
1645
2284
|
if (!raw)
|
|
1646
2285
|
return fallback;
|
|
1647
|
-
const
|
|
1648
|
-
const
|
|
2286
|
+
const sanitized = raw.replace(/[;&|`$(){}!<>]/g, "").trim();
|
|
2287
|
+
const normalizedRaw = sanitized.toLowerCase();
|
|
2288
|
+
const normalized = normalizeOrchestrationAgent(sanitized);
|
|
1649
2289
|
if (normalized === "router" && normalizedRaw !== "router") {
|
|
1650
2290
|
const profiles = ORCHESTRATION_AGENT_PROFILES.join(", ");
|
|
1651
2291
|
const hint = ORCHESTRATION_AGENT_PROFILES.find((p) => p.startsWith(normalizedRaw.slice(0, 3)));
|
|
1652
2292
|
const suggestion = hint ? ` Did you mean "${hint}"?` : "";
|
|
1653
|
-
throw new CliError(`Invalid --agent target: ${
|
|
2293
|
+
throw new CliError(`Invalid --agent target: ${sanitized}. Use ${profiles}.${suggestion}`, CLI_EXIT_CODES.validation);
|
|
1654
2294
|
}
|
|
1655
2295
|
return normalized;
|
|
1656
2296
|
}
|
|
@@ -1870,6 +2510,7 @@ Options:
|
|
|
1870
2510
|
--background Run as a background agent (Cursor cloud agent mode).
|
|
1871
2511
|
--permission-mode <mode> Claude Code permission mode. Default: acceptEdits.
|
|
1872
2512
|
--dry-run Preview the execution parameters without actually running.
|
|
2513
|
+
--non-interactive Skip all interactive prompts (same as CI mode).
|
|
1873
2514
|
--json Print machine-readable JSON output.
|
|
1874
2515
|
--cwd <path> Project directory.
|
|
1875
2516
|
--help Show this command help.
|
|
@@ -1931,15 +2572,21 @@ Options:
|
|
|
1931
2572
|
--max-run-dirs <n> Max artifact directories to keep. Default: 20
|
|
1932
2573
|
--summary-lines <n> Lines of summary to extract per iteration. Default: 40
|
|
1933
2574
|
--dry-run Preview what the sweep would do without executing.
|
|
2575
|
+
--non-interactive Skip all interactive prompts (same as CI mode).
|
|
1934
2576
|
--json Print machine-readable JSON output.
|
|
1935
2577
|
--cwd <path> Project directory.
|
|
1936
2578
|
--help Show this command help.
|
|
1937
2579
|
|
|
2580
|
+
Environment:
|
|
2581
|
+
PROMPTS_GPT_NON_INTERACTIVE=1 Skip interactive prompts.
|
|
2582
|
+
NO_COLOR=1 Disable colored output.
|
|
2583
|
+
|
|
1938
2584
|
Examples:
|
|
1939
|
-
prompts-gpt sweep #
|
|
2585
|
+
prompts-gpt sweep # interactive picker
|
|
1940
2586
|
prompts-gpt sweep -f .prompts-gpt/sweeps/sdk-hardening.md # explicit file
|
|
1941
2587
|
prompts-gpt sweep -f .prompts-gpt/sweeps/design.md -n 5 # 5 iterations
|
|
1942
2588
|
prompts-gpt sweep --dry-run # preview without running
|
|
2589
|
+
prompts-gpt sweep --non-interactive # skip interactive prompts
|
|
1943
2590
|
`;
|
|
1944
2591
|
}
|
|
1945
2592
|
if (command === "list") {
|
|
@@ -2072,19 +2719,222 @@ Why use it:
|
|
|
2072
2719
|
Shows focused usage for the exact command you are trying to run so shell users do not have to scan the full command list.
|
|
2073
2720
|
`;
|
|
2074
2721
|
}
|
|
2075
|
-
|
|
2722
|
+
// ── Interactive TTY selection helpers ─────────────────────────────────────────
|
|
2723
|
+
function supportsColor() {
|
|
2724
|
+
if (process.env.NO_COLOR || process.env.TERM === "dumb")
|
|
2725
|
+
return false;
|
|
2726
|
+
if (process.env.FORCE_COLOR)
|
|
2727
|
+
return true;
|
|
2728
|
+
if (process.platform === "win32") {
|
|
2729
|
+
const osRelease = (process.env.OS_VERSION || "").toLowerCase();
|
|
2730
|
+
if (osRelease.includes("windows_nt") || process.env.WT_SESSION || process.env.TERM_PROGRAM)
|
|
2731
|
+
return true;
|
|
2732
|
+
return Boolean(process.stdout.isTTY && (process.stdout.hasColors?.() ?? false));
|
|
2733
|
+
}
|
|
2734
|
+
return Boolean(process.stdout.isTTY);
|
|
2735
|
+
}
|
|
2736
|
+
function colorize(text, code) {
|
|
2737
|
+
return supportsColor() ? `${code}${text}\x1b[0m` : text;
|
|
2738
|
+
}
|
|
2739
|
+
function isTTYInteractive(flags) {
|
|
2740
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY)
|
|
2741
|
+
return false;
|
|
2742
|
+
if (flags && Boolean(flags["non-interactive"]))
|
|
2743
|
+
return false;
|
|
2744
|
+
if (process.env.CI || process.env.CONTINUOUS_INTEGRATION || process.env.GITHUB_ACTIONS ||
|
|
2745
|
+
process.env.GITLAB_CI || process.env.CIRCLECI || process.env.JENKINS_URL ||
|
|
2746
|
+
process.env.BUILDKITE || process.env.TRAVIS || process.env.TF_BUILD ||
|
|
2747
|
+
process.env.CODEBUILD_BUILD_ID)
|
|
2748
|
+
return false;
|
|
2749
|
+
if (process.env.PROMPTS_GPT_NON_INTERACTIVE === "1" || process.env.PROMPTS_GPT_NON_INTERACTIVE === "true")
|
|
2750
|
+
return false;
|
|
2751
|
+
return true;
|
|
2752
|
+
}
|
|
2753
|
+
function interactiveSelect(prompt, options) {
|
|
2754
|
+
if (options.length === 0) {
|
|
2755
|
+
return Promise.reject(new CliError("No options available for selection.", CLI_EXIT_CODES.usage));
|
|
2756
|
+
}
|
|
2757
|
+
if (options.length === 1) {
|
|
2758
|
+
return Promise.resolve(options[0].value);
|
|
2759
|
+
}
|
|
2760
|
+
return new Promise((resolve, reject) => {
|
|
2761
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
2762
|
+
reject(new CliError("Interactive selection requires a TTY.", CLI_EXIT_CODES.usage));
|
|
2763
|
+
return;
|
|
2764
|
+
}
|
|
2765
|
+
const stdin = process.stdin;
|
|
2766
|
+
const stdout = process.stdout;
|
|
2767
|
+
let cursor = 0;
|
|
2768
|
+
let escBuf = "";
|
|
2769
|
+
const maxVisible = Math.min(options.length, (process.stdout.rows ?? 24) - 3);
|
|
2770
|
+
const useUnicode = supportsColor();
|
|
2771
|
+
const pointer = useUnicode ? "❯" : ">";
|
|
2772
|
+
const formatLine = (i, selected) => {
|
|
2773
|
+
const num = i < 9 ? `${i + 1}` : " ";
|
|
2774
|
+
const prefix = selected ? colorize(pointer, "\x1b[36m") : " ";
|
|
2775
|
+
const label = selected ? colorize(options[i].label, "\x1b[1m") : options[i].label;
|
|
2776
|
+
return ` ${prefix} ${num}. ${label}`;
|
|
2777
|
+
};
|
|
2778
|
+
const writeInitial = () => {
|
|
2779
|
+
stdout.write(`${prompt}\n`);
|
|
2780
|
+
for (let i = 0; i < maxVisible; i++) {
|
|
2781
|
+
stdout.write(`${formatLine(i, i === cursor)}\n`);
|
|
2782
|
+
}
|
|
2783
|
+
if (options.length > maxVisible) {
|
|
2784
|
+
stdout.write(` ... ${options.length - maxVisible} more (scroll with arrows)\n`);
|
|
2785
|
+
}
|
|
2786
|
+
};
|
|
2787
|
+
const render = () => {
|
|
2788
|
+
const lines = maxVisible + 1 + (options.length > maxVisible ? 1 : 0);
|
|
2789
|
+
if (supportsColor()) {
|
|
2790
|
+
stdout.write(`\x1b[${lines}A\x1b[J`);
|
|
2791
|
+
}
|
|
2792
|
+
else {
|
|
2793
|
+
stdout.write("\n".repeat(2));
|
|
2794
|
+
}
|
|
2795
|
+
const posLabel = options.length > 1 ? ` (${cursor + 1}/${options.length})` : "";
|
|
2796
|
+
stdout.write(`${prompt}${posLabel}\n`);
|
|
2797
|
+
const scrollStart = Math.max(0, Math.min(cursor - Math.floor(maxVisible / 2), options.length - maxVisible));
|
|
2798
|
+
for (let vi = 0; vi < maxVisible; vi++) {
|
|
2799
|
+
const i = scrollStart + vi;
|
|
2800
|
+
stdout.write(`${formatLine(i, i === cursor)}\n`);
|
|
2801
|
+
}
|
|
2802
|
+
if (options.length > maxVisible) {
|
|
2803
|
+
stdout.write(` ... showing ${scrollStart + 1}-${scrollStart + maxVisible} of ${options.length}\n`);
|
|
2804
|
+
}
|
|
2805
|
+
};
|
|
2806
|
+
writeInitial();
|
|
2807
|
+
stdin.setEncoding("utf8");
|
|
2808
|
+
try {
|
|
2809
|
+
if (typeof stdin.setRawMode === "function")
|
|
2810
|
+
stdin.setRawMode(true);
|
|
2811
|
+
}
|
|
2812
|
+
catch {
|
|
2813
|
+
reject(new CliError("Cannot enable raw mode for interactive selection. Use --non-interactive or pass flags directly.", CLI_EXIT_CODES.usage));
|
|
2814
|
+
return;
|
|
2815
|
+
}
|
|
2816
|
+
stdin.resume();
|
|
2817
|
+
const cleanup = () => {
|
|
2818
|
+
try {
|
|
2819
|
+
stdin.removeListener("data", onData);
|
|
2820
|
+
}
|
|
2821
|
+
catch { /* already destroyed */ }
|
|
2822
|
+
try {
|
|
2823
|
+
if (typeof stdin.setRawMode === "function")
|
|
2824
|
+
stdin.setRawMode(false);
|
|
2825
|
+
}
|
|
2826
|
+
catch { /* ignore */ }
|
|
2827
|
+
try {
|
|
2828
|
+
if (!stdin.destroyed)
|
|
2829
|
+
stdin.pause();
|
|
2830
|
+
}
|
|
2831
|
+
catch { /* ignore */ }
|
|
2832
|
+
};
|
|
2833
|
+
const onData = (data) => {
|
|
2834
|
+
for (let ci = 0; ci < data.length; ci++) {
|
|
2835
|
+
const ch = data[ci];
|
|
2836
|
+
if (escBuf.length > 0) {
|
|
2837
|
+
escBuf += ch;
|
|
2838
|
+
if (escBuf.length === 2 && ch === "[")
|
|
2839
|
+
continue;
|
|
2840
|
+
if (escBuf.length >= 3) {
|
|
2841
|
+
if (escBuf === "\x1b[A" || escBuf === "\x1b[k") {
|
|
2842
|
+
cursor = cursor > 0 ? cursor - 1 : options.length - 1;
|
|
2843
|
+
render();
|
|
2844
|
+
}
|
|
2845
|
+
else if (escBuf === "\x1b[B" || escBuf === "\x1b[j") {
|
|
2846
|
+
cursor = cursor < options.length - 1 ? cursor + 1 : 0;
|
|
2847
|
+
render();
|
|
2848
|
+
}
|
|
2849
|
+
else if (escBuf === "\x1b[H") {
|
|
2850
|
+
cursor = 0;
|
|
2851
|
+
render();
|
|
2852
|
+
}
|
|
2853
|
+
else if (escBuf === "\x1b[F") {
|
|
2854
|
+
cursor = options.length - 1;
|
|
2855
|
+
render();
|
|
2856
|
+
}
|
|
2857
|
+
escBuf = "";
|
|
2858
|
+
}
|
|
2859
|
+
continue;
|
|
2860
|
+
}
|
|
2861
|
+
if (ch === "\x1b") {
|
|
2862
|
+
escBuf = "\x1b";
|
|
2863
|
+
continue;
|
|
2864
|
+
}
|
|
2865
|
+
if (ch === "\x03") {
|
|
2866
|
+
cleanup();
|
|
2867
|
+
stdout.write("\n");
|
|
2868
|
+
process.exit(130);
|
|
2869
|
+
return;
|
|
2870
|
+
}
|
|
2871
|
+
if (ch === "\r" || ch === "\n") {
|
|
2872
|
+
cleanup();
|
|
2873
|
+
stdout.write("\n");
|
|
2874
|
+
resolve(options[cursor].value);
|
|
2875
|
+
return;
|
|
2876
|
+
}
|
|
2877
|
+
if (ch === "k") {
|
|
2878
|
+
cursor = cursor > 0 ? cursor - 1 : options.length - 1;
|
|
2879
|
+
render();
|
|
2880
|
+
}
|
|
2881
|
+
else if (ch === "j") {
|
|
2882
|
+
cursor = cursor < options.length - 1 ? cursor + 1 : 0;
|
|
2883
|
+
render();
|
|
2884
|
+
}
|
|
2885
|
+
else if (ch >= "1" && ch <= "9") {
|
|
2886
|
+
const idx = parseInt(ch, 10) - 1;
|
|
2887
|
+
if (idx < options.length) {
|
|
2888
|
+
cleanup();
|
|
2889
|
+
stdout.write("\n");
|
|
2890
|
+
resolve(options[idx].value);
|
|
2891
|
+
return;
|
|
2892
|
+
}
|
|
2893
|
+
}
|
|
2894
|
+
}
|
|
2895
|
+
};
|
|
2896
|
+
stdin.on("data", onData);
|
|
2897
|
+
});
|
|
2898
|
+
}
|
|
2899
|
+
async function interactiveInput(prompt, defaultValue) {
|
|
2900
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
2901
|
+
throw new CliError("Interactive input requires a TTY.", CLI_EXIT_CODES.usage);
|
|
2902
|
+
}
|
|
2903
|
+
const { createInterface } = await import("node:readline");
|
|
2904
|
+
return new Promise((resolve) => {
|
|
2905
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
2906
|
+
const suffix = defaultValue ? ` (${defaultValue})` : "";
|
|
2907
|
+
rl.question(`${prompt}${suffix}: `, (answer) => {
|
|
2908
|
+
rl.close();
|
|
2909
|
+
resolve(answer.trim() || defaultValue || "");
|
|
2910
|
+
});
|
|
2911
|
+
});
|
|
2912
|
+
}
|
|
2913
|
+
async function readSweepFrontmatter(filePath) {
|
|
2076
2914
|
try {
|
|
2077
2915
|
const { readFile: fsRead } = await import("node:fs/promises");
|
|
2078
2916
|
const content = await fsRead(filePath, "utf8");
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2917
|
+
let iterations = null;
|
|
2918
|
+
let title = null;
|
|
2919
|
+
const iterMatch = content.match(/iterations:\s*(\d+)/i);
|
|
2920
|
+
if (iterMatch) {
|
|
2921
|
+
const val = parseInt(iterMatch[1], 10);
|
|
2082
2922
|
if (val > 0 && val <= 50)
|
|
2083
|
-
|
|
2923
|
+
iterations = val;
|
|
2084
2924
|
}
|
|
2925
|
+
const fmEnd = content.indexOf("---", content.indexOf("---") + 3);
|
|
2926
|
+
const bodyStart = fmEnd > 0 ? fmEnd + 3 : 0;
|
|
2927
|
+
const titleMatch = content.slice(bodyStart).match(/^#\s+(.+)/m);
|
|
2928
|
+
if (titleMatch) {
|
|
2929
|
+
title = titleMatch[1].trim();
|
|
2930
|
+
}
|
|
2931
|
+
return { iterations, title };
|
|
2085
2932
|
}
|
|
2086
2933
|
catch { /* skip */ }
|
|
2087
|
-
return null;
|
|
2934
|
+
return { iterations: null, title: null };
|
|
2935
|
+
}
|
|
2936
|
+
async function readSweepIterationsFromFrontmatter(filePath) {
|
|
2937
|
+
return (await readSweepFrontmatter(filePath)).iterations;
|
|
2088
2938
|
}
|
|
2089
2939
|
function buildGenerateInput(flags) {
|
|
2090
2940
|
return {
|
|
@@ -2099,7 +2949,15 @@ function buildGenerateInput(flags) {
|
|
|
2099
2949
|
};
|
|
2100
2950
|
}
|
|
2101
2951
|
function sanitizeGenerateInput(value) {
|
|
2102
|
-
return value
|
|
2952
|
+
return value
|
|
2953
|
+
.replace(/pgpt_[a-zA-Z0-9]{4,}/g, "pgpt_***")
|
|
2954
|
+
.replace(/sk-[a-zA-Z0-9]{20,}/g, "sk-***")
|
|
2955
|
+
.replace(/ghp_[a-zA-Z0-9]{36,}/g, "ghp_***")
|
|
2956
|
+
.replace(/ghu_[a-zA-Z0-9]{36,}/g, "ghu_***")
|
|
2957
|
+
.replace(/Bearer\s+[a-zA-Z0-9_.-]{20,}/gi, "Bearer ***")
|
|
2958
|
+
.replace(/xai-[a-zA-Z0-9]{20,}/g, "xai-***")
|
|
2959
|
+
.replace(/ANTHROPIC_API_KEY=[^\s]+/gi, "ANTHROPIC_API_KEY=***")
|
|
2960
|
+
.replace(/OPENAI_API_KEY=[^\s]+/gi, "OPENAI_API_KEY=***");
|
|
2103
2961
|
}
|
|
2104
2962
|
function printDataTransmissionNotice(command, input) {
|
|
2105
2963
|
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.`);
|