@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/LICENSE +75 -21
- package/README.md +83 -288
- package/dist/cli.js +993 -104
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +284 -36
- package/dist/index.js.map +1 -1
- package/dist/runtime.d.ts +205 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +1173 -0
- package/dist/runtime.js.map +1 -0
- package/dist/sweep.d.ts +174 -0
- package/dist/sweep.d.ts.map +1 -0
- package/dist/sweep.js +594 -0
- package/dist/sweep.js.map +1 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
import { chmod, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
2
|
import { existsSync, readFileSync } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
export { DEFAULT_RUN_ARTIFACTS_DIR, DEFAULT_RUN_CONFIG_PATH, ORCHESTRATION_AGENT_PROFILES, DEFAULT_MODELS, normalizeOrchestrationAgent, loadRunConfig, detectProviders, doctor, initRunConfig, runBatch, runPrompt, resolveRunProvider, resolveTimeoutSeconds, resolveDefaultPromptFile, assertPromptFitsLaunch, executeProviderCommandWithRetries, executeProviderCommand, captureWorktreeStatus, buildWorktreeDelta, buildProviderCommand, formatCombinedOutput, appendFileSafe, validateRunConfig, discoverWorkspaceAssets, isCI, } from "./runtime.js";
|
|
5
|
+
export { sweepPrompt, acquireSweepLock, releaseSweepLock, parseStreamJsonToolCounts, streamJsonHasResult, extractIterationSummary, buildIterationPrompt, runPreFlight, writeSweepManifest, } from "./sweep.js";
|
|
4
6
|
export const DEFAULT_PROMPTS_GPT_API_URL = "https://prompts-gpt.com";
|
|
5
7
|
export const DEFAULT_PROMPTS_GPT_OUT_DIR = ".prompts-gpt";
|
|
6
8
|
export const PROMPTS_GPT_CREDENTIALS_FILE = ".credentials.json";
|
|
7
9
|
export const PROMPTS_GPT_MANIFEST_FILE = "manifest.json";
|
|
8
|
-
export const SUPPORTED_AGENT_TARGETS = [
|
|
10
|
+
export const SUPPORTED_AGENT_TARGETS = [
|
|
11
|
+
"codex",
|
|
12
|
+
"claude-code",
|
|
13
|
+
"cursor",
|
|
14
|
+
"vscode",
|
|
15
|
+
"copilot",
|
|
16
|
+
"continue",
|
|
17
|
+
"gemini-cli",
|
|
18
|
+
"windsurf",
|
|
19
|
+
"cline",
|
|
20
|
+
"junie",
|
|
21
|
+
"amp",
|
|
22
|
+
];
|
|
9
23
|
export class PromptsGptApiError extends Error {
|
|
10
24
|
status;
|
|
11
25
|
code;
|
|
@@ -20,7 +34,7 @@ export class PromptsGptApiError extends Error {
|
|
|
20
34
|
this.code = options?.code ?? "UNKNOWN_ERROR";
|
|
21
35
|
this.recovery = options?.recovery ?? "Retry the request or create a fresh project token.";
|
|
22
36
|
this.requestId = options?.requestId ?? null;
|
|
23
|
-
this.fieldErrors = options?.fieldErrors;
|
|
37
|
+
this.fieldErrors = options?.fieldErrors ? Object.freeze({ ...options.fieldErrors }) : undefined;
|
|
24
38
|
this.retryAfterMs = options?.retryAfterMs ?? null;
|
|
25
39
|
}
|
|
26
40
|
}
|
|
@@ -33,11 +47,16 @@ export class PromptsGptClient {
|
|
|
33
47
|
token;
|
|
34
48
|
fetchImpl;
|
|
35
49
|
timeoutMs;
|
|
50
|
+
accountId;
|
|
36
51
|
constructor(options) {
|
|
37
52
|
this.apiUrl = safeNormalizeApiUrl(options.apiUrl);
|
|
38
53
|
this.token = options.token?.trim() || null;
|
|
39
54
|
this.fetchImpl = options.fetch;
|
|
40
55
|
this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
56
|
+
this.accountId = options.accountId?.trim() || null;
|
|
57
|
+
if (this.token && this.apiUrl.startsWith("http://") && !this.apiUrl.includes("localhost") && !this.apiUrl.includes("127.0.0.1")) {
|
|
58
|
+
throw new PromptsGptApiError("Refusing to send credentials over unencrypted HTTP. Use HTTPS or localhost.", { code: "INSECURE_TRANSPORT", recovery: "Change the API URL to use https://." });
|
|
59
|
+
}
|
|
41
60
|
}
|
|
42
61
|
async getProject(options = {}) {
|
|
43
62
|
const data = await this.request("/api/sdk/v1/project", options);
|
|
@@ -121,7 +140,11 @@ export class PromptsGptClient {
|
|
|
121
140
|
authorization: `Bearer ${this.token}`,
|
|
122
141
|
accept: "application/json",
|
|
123
142
|
"x-prompts-gpt-client": `@prompts-gpt/client/${getClientVersion()}`,
|
|
143
|
+
"x-prompts-gpt-build": getBuildFingerprint(),
|
|
124
144
|
};
|
|
145
|
+
if (this.accountId) {
|
|
146
|
+
headers["x-prompts-gpt-account"] = this.accountId;
|
|
147
|
+
}
|
|
125
148
|
if (!options.omitUserAgent) {
|
|
126
149
|
headers["user-agent"] = `prompts-gpt-client/${getClientVersion()}`;
|
|
127
150
|
}
|
|
@@ -245,7 +268,9 @@ function normalizeTimeoutMs(value) {
|
|
|
245
268
|
recovery: "Provide a timeout greater than 0.",
|
|
246
269
|
});
|
|
247
270
|
}
|
|
248
|
-
|
|
271
|
+
const MAX_API_TIMEOUT_MS = 600_000;
|
|
272
|
+
const capped = Math.min(Math.trunc(value), MAX_API_TIMEOUT_MS);
|
|
273
|
+
return capped;
|
|
249
274
|
}
|
|
250
275
|
function serializePromptQuery(query) {
|
|
251
276
|
const params = new URLSearchParams();
|
|
@@ -340,11 +365,37 @@ function parseRetryAfterHeader(value) {
|
|
|
340
365
|
return Math.min(Math.max(retryAt - Date.now(), 0), 600_000);
|
|
341
366
|
}
|
|
342
367
|
function sleep(ms) {
|
|
343
|
-
return new Promise((resolve) =>
|
|
368
|
+
return new Promise((resolve) => {
|
|
369
|
+
setTimeout(resolve, ms);
|
|
370
|
+
});
|
|
344
371
|
}
|
|
345
372
|
function generateRequestId() {
|
|
346
|
-
|
|
347
|
-
|
|
373
|
+
try {
|
|
374
|
+
const crypto = globalThis.crypto;
|
|
375
|
+
const bytes = new Uint8Array(8);
|
|
376
|
+
crypto.getRandomValues(bytes);
|
|
377
|
+
return `pgcli_${Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("")}`;
|
|
378
|
+
}
|
|
379
|
+
catch {
|
|
380
|
+
const hex = Array.from({ length: 8 }, () => Math.floor(Math.random() * 256).toString(16).padStart(2, "0")).join("");
|
|
381
|
+
return `pgcli_${hex}`;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
const BUILD_TS = "dev";
|
|
385
|
+
const BUILD_ACCOUNT_ID = "unattributed";
|
|
386
|
+
let cachedBuildFingerprint = null;
|
|
387
|
+
function getBuildFingerprint() {
|
|
388
|
+
if (cachedBuildFingerprint)
|
|
389
|
+
return cachedBuildFingerprint;
|
|
390
|
+
cachedBuildFingerprint = `${getClientVersion()}/${BUILD_TS}/${BUILD_ACCOUNT_ID}`;
|
|
391
|
+
return cachedBuildFingerprint;
|
|
392
|
+
}
|
|
393
|
+
export function getAttribution() {
|
|
394
|
+
return {
|
|
395
|
+
version: getClientVersion(),
|
|
396
|
+
buildTs: BUILD_TS,
|
|
397
|
+
accountId: BUILD_ACCOUNT_ID,
|
|
398
|
+
};
|
|
348
399
|
}
|
|
349
400
|
let cachedVersion = null;
|
|
350
401
|
function getClientVersion() {
|
|
@@ -381,16 +432,24 @@ export async function loadLocalCredentials(cwd = process.cwd()) {
|
|
|
381
432
|
if (!existsSync(credentialsPath))
|
|
382
433
|
return null;
|
|
383
434
|
try {
|
|
384
|
-
const
|
|
435
|
+
const raw = await readFile(credentialsPath, "utf8");
|
|
436
|
+
const parsed = JSON.parse(raw);
|
|
437
|
+
if (!parsed || typeof parsed !== "object")
|
|
438
|
+
return null;
|
|
385
439
|
let apiUrl = DEFAULT_PROMPTS_GPT_API_URL;
|
|
386
440
|
if (typeof parsed.apiUrl === "string") {
|
|
387
441
|
try {
|
|
388
|
-
new URL(parsed.apiUrl);
|
|
389
|
-
|
|
442
|
+
const url = new URL(parsed.apiUrl);
|
|
443
|
+
if (url.protocol === "https:" || url.protocol === "http:") {
|
|
444
|
+
apiUrl = parsed.apiUrl;
|
|
445
|
+
}
|
|
390
446
|
}
|
|
391
|
-
catch { }
|
|
447
|
+
catch { /* invalid URL — use default */ }
|
|
392
448
|
}
|
|
393
449
|
const rawToken = typeof parsed.token === "string" ? parsed.token.trim() : null;
|
|
450
|
+
if (rawToken && !rawToken.startsWith("pgpt_")) {
|
|
451
|
+
return { token: null, apiUrl };
|
|
452
|
+
}
|
|
394
453
|
return {
|
|
395
454
|
token: rawToken || null,
|
|
396
455
|
apiUrl,
|
|
@@ -446,6 +505,9 @@ export async function writePromptMarkdownFiles(prompts, options = {}) {
|
|
|
446
505
|
}
|
|
447
506
|
export async function writeAgentFiles(prompts, options = {}) {
|
|
448
507
|
const cwd = options.cwd ?? process.cwd();
|
|
508
|
+
if (!existsSync(cwd)) {
|
|
509
|
+
throw new Error(`Working directory does not exist: ${cwd}`);
|
|
510
|
+
}
|
|
449
511
|
const targets = normalizeAgentTargets(options.agent ?? options.agents ?? "all");
|
|
450
512
|
const overwrite = Boolean(options.overwriteAgentFiles);
|
|
451
513
|
const written = [];
|
|
@@ -494,9 +556,13 @@ export async function writePromptManifest(prompts, options = {}) {
|
|
|
494
556
|
const normalizedPrompts = assertUniquePromptFileStems(prompts);
|
|
495
557
|
await mkdir(outDir, { recursive: true });
|
|
496
558
|
const manifestPath = path.join(outDir, PROMPTS_GPT_MANIFEST_FILE);
|
|
559
|
+
const attribution = getAttribution();
|
|
497
560
|
const payload = {
|
|
498
561
|
version: 1,
|
|
499
562
|
generatedAt: new Date().toISOString(),
|
|
563
|
+
generatedBy: `@prompts-gpt/client@${attribution.version}`,
|
|
564
|
+
accountId: attribution.accountId,
|
|
565
|
+
buildFingerprint: `${attribution.version}/${attribution.buildTs}/${attribution.accountId}`,
|
|
500
566
|
count: normalizedPrompts.length,
|
|
501
567
|
prompts: normalizedPrompts.map(({ prompt, stem }) => ({
|
|
502
568
|
slug: stem,
|
|
@@ -567,24 +633,47 @@ function buildAgentFiles(target, prompts) {
|
|
|
567
633
|
].join("\n"),
|
|
568
634
|
}];
|
|
569
635
|
}
|
|
636
|
+
if (target === "claude-code") {
|
|
637
|
+
return [{
|
|
638
|
+
path: "CLAUDE.md",
|
|
639
|
+
managedBlock: true,
|
|
640
|
+
content: [
|
|
641
|
+
"# Prompts-GPT Claude Code Instructions",
|
|
642
|
+
"",
|
|
643
|
+
"Prompts synced by `prompts-gpt sync` live in `.prompts-gpt/`. Start with [.prompts-gpt/manifest.json](.prompts-gpt/manifest.json), then open the prompt packs linked below before starting related work.",
|
|
644
|
+
"",
|
|
645
|
+
"## Available Prompt Packs",
|
|
646
|
+
...prompts.map(({ prompt, stem }) => `- [${prompt.title}](.prompts-gpt/${stem}.md)`),
|
|
647
|
+
"",
|
|
648
|
+
"When a prompt pack is relevant, load it, adapt variables to the current task, and keep verification tied to the prompt's acceptance criteria.",
|
|
649
|
+
"",
|
|
650
|
+
].join("\n"),
|
|
651
|
+
}];
|
|
652
|
+
}
|
|
570
653
|
if (target === "cursor") {
|
|
571
|
-
return prompts.
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
654
|
+
return prompts.flatMap(({ prompt, stem }) => ([
|
|
655
|
+
{
|
|
656
|
+
path: `.cursor/rules/prompts-gpt-${stem}.mdc`,
|
|
657
|
+
content: [
|
|
658
|
+
"---",
|
|
659
|
+
`description: ${yamlScalar(prompt.summary || prompt.title)}`,
|
|
660
|
+
"globs: []",
|
|
661
|
+
"alwaysApply: false",
|
|
662
|
+
"---",
|
|
663
|
+
"",
|
|
664
|
+
`# ${prompt.title}`,
|
|
665
|
+
"",
|
|
666
|
+
prompt.promptText,
|
|
667
|
+
"",
|
|
668
|
+
prompt.usageNotes ? `Usage notes: ${prompt.usageNotes}` : "",
|
|
669
|
+
"",
|
|
670
|
+
].filter(Boolean).join("\n"),
|
|
671
|
+
},
|
|
672
|
+
{
|
|
673
|
+
path: `.cursor/commands/prompts-gpt-${stem}.md`,
|
|
674
|
+
content: formatCursorCommandMarkdown(prompt, stem),
|
|
675
|
+
},
|
|
676
|
+
]));
|
|
588
677
|
}
|
|
589
678
|
if (target === "vscode") {
|
|
590
679
|
return [
|
|
@@ -604,12 +693,12 @@ function buildAgentFiles(target, prompts) {
|
|
|
604
693
|
path: ".github/instructions/prompts-gpt.instructions.md",
|
|
605
694
|
content: [
|
|
606
695
|
"---",
|
|
607
|
-
'applyTo: "AGENTS.md,.prompts-gpt/**/*.md,.github/copilot-instructions.md,.github/prompts/**/*.prompt.md,.cursor/rules/**/*.mdc,.vscode/prompts-gpt.code-snippets"',
|
|
696
|
+
'applyTo: "AGENTS.md,.prompts-gpt/**/*.md,.github/copilot-instructions.md,.github/prompts/**/*.prompt.md,.cursor/rules/**/*.mdc,.cursor/commands/**/*.md,.vscode/prompts-gpt.code-snippets"',
|
|
608
697
|
"---",
|
|
609
698
|
"",
|
|
610
699
|
"# Prompts-GPT managed artifacts",
|
|
611
700
|
"",
|
|
612
|
-
"These files are generated or refreshed by `prompts-gpt sync
|
|
701
|
+
"These files are generated or refreshed by `prompts-gpt sync`.",
|
|
613
702
|
"",
|
|
614
703
|
"- Treat `.prompts-gpt/manifest.json` as the source of truth for discoverable prompt packs and generated agent files.",
|
|
615
704
|
"- Prefer updating the upstream prompt pack or rerunning sync instead of manually editing generated agent artifacts.",
|
|
@@ -629,6 +718,110 @@ function buildAgentFiles(target, prompts) {
|
|
|
629
718
|
content: formatCopilotPromptMarkdown(prompt, stem),
|
|
630
719
|
}));
|
|
631
720
|
}
|
|
721
|
+
if (target === "continue") {
|
|
722
|
+
return prompts.map(({ prompt, stem }) => ({
|
|
723
|
+
path: `.continue/rules/prompts-gpt-${stem}.md`,
|
|
724
|
+
content: [
|
|
725
|
+
`# ${prompt.title}`,
|
|
726
|
+
"",
|
|
727
|
+
`[Canonical prompt pack](../../.prompts-gpt/${stem}.md)`,
|
|
728
|
+
"",
|
|
729
|
+
prompt.summary ?? "",
|
|
730
|
+
"",
|
|
731
|
+
prompt.promptText ?? "",
|
|
732
|
+
"",
|
|
733
|
+
prompt.usageNotes ? `Usage notes: ${prompt.usageNotes}` : "",
|
|
734
|
+
"",
|
|
735
|
+
].filter(Boolean).join("\n"),
|
|
736
|
+
}));
|
|
737
|
+
}
|
|
738
|
+
if (target === "gemini-cli") {
|
|
739
|
+
return [{
|
|
740
|
+
path: "GEMINI.md",
|
|
741
|
+
managedBlock: true,
|
|
742
|
+
content: [
|
|
743
|
+
"# Prompts-GPT Gemini CLI Instructions",
|
|
744
|
+
"",
|
|
745
|
+
"Prompts synced by `prompts-gpt sync` live in `.prompts-gpt/`. Start with [.prompts-gpt/manifest.json](.prompts-gpt/manifest.json), then open the prompt packs linked below before starting related work.",
|
|
746
|
+
"",
|
|
747
|
+
"## Available Prompt Packs",
|
|
748
|
+
...prompts.map(({ prompt, stem }) => `- [${prompt.title}](.prompts-gpt/${stem}.md)`),
|
|
749
|
+
"",
|
|
750
|
+
"When a prompt pack is relevant, load it, adapt variables to the current task, and keep verification tied to the prompt's acceptance criteria.",
|
|
751
|
+
"",
|
|
752
|
+
].join("\n"),
|
|
753
|
+
}];
|
|
754
|
+
}
|
|
755
|
+
if (target === "windsurf") {
|
|
756
|
+
return prompts.map(({ prompt, stem }) => ({
|
|
757
|
+
path: `.windsurf/rules/prompts-gpt-${stem}.md`,
|
|
758
|
+
content: [
|
|
759
|
+
`# ${prompt.title}`,
|
|
760
|
+
"",
|
|
761
|
+
`[Canonical prompt pack](../../.prompts-gpt/${stem}.md)`,
|
|
762
|
+
"",
|
|
763
|
+
prompt.summary ?? "",
|
|
764
|
+
"",
|
|
765
|
+
prompt.promptText ?? "",
|
|
766
|
+
"",
|
|
767
|
+
"Use this as a workspace rule for Cascade or Devin Local when the prompt pack matches the task.",
|
|
768
|
+
prompt.usageNotes ? `Usage notes: ${prompt.usageNotes}` : "",
|
|
769
|
+
"",
|
|
770
|
+
].filter(Boolean).join("\n"),
|
|
771
|
+
}));
|
|
772
|
+
}
|
|
773
|
+
if (target === "cline") {
|
|
774
|
+
return prompts.map(({ prompt, stem }) => ({
|
|
775
|
+
path: `.clinerules/prompts-gpt-${stem}.md`,
|
|
776
|
+
content: [
|
|
777
|
+
`# ${prompt.title}`,
|
|
778
|
+
"",
|
|
779
|
+
`[Canonical prompt pack](../.prompts-gpt/${stem}.md)`,
|
|
780
|
+
"",
|
|
781
|
+
prompt.summary ?? "",
|
|
782
|
+
"",
|
|
783
|
+
prompt.promptText ?? "",
|
|
784
|
+
"",
|
|
785
|
+
"Enable this rule when the current Cline task matches the linked prompt pack.",
|
|
786
|
+
prompt.usageNotes ? `Usage notes: ${prompt.usageNotes}` : "",
|
|
787
|
+
"",
|
|
788
|
+
].filter(Boolean).join("\n"),
|
|
789
|
+
}));
|
|
790
|
+
}
|
|
791
|
+
if (target === "junie") {
|
|
792
|
+
return [{
|
|
793
|
+
path: ".junie/guidelines.md",
|
|
794
|
+
managedBlock: true,
|
|
795
|
+
content: [
|
|
796
|
+
"# Prompts-GPT Junie Guidelines",
|
|
797
|
+
"",
|
|
798
|
+
"Prompts synced by `prompts-gpt sync` live in `.prompts-gpt/`. Start with [.prompts-gpt/manifest.json](.prompts-gpt/manifest.json), then open the prompt packs linked below before starting related work.",
|
|
799
|
+
"",
|
|
800
|
+
"## Available Prompt Packs",
|
|
801
|
+
...prompts.map(({ prompt, stem }) => `- [${prompt.title}](../.prompts-gpt/${stem}.md)`),
|
|
802
|
+
"",
|
|
803
|
+
"When a prompt pack is relevant, load it, adapt variables to the current task, and keep verification tied to the prompt's acceptance criteria.",
|
|
804
|
+
"",
|
|
805
|
+
].join("\n"),
|
|
806
|
+
}];
|
|
807
|
+
}
|
|
808
|
+
if (target === "amp") {
|
|
809
|
+
return [{
|
|
810
|
+
path: "AGENT.md",
|
|
811
|
+
managedBlock: true,
|
|
812
|
+
content: [
|
|
813
|
+
"# Prompts-GPT Amp Instructions",
|
|
814
|
+
"",
|
|
815
|
+
"Prompts synced by `prompts-gpt sync` live in `.prompts-gpt/`. Start with [.prompts-gpt/manifest.json](.prompts-gpt/manifest.json), then open the prompt packs linked below before starting related work.",
|
|
816
|
+
"",
|
|
817
|
+
"## Available Prompt Packs",
|
|
818
|
+
...prompts.map(({ prompt, stem }) => `- [${prompt.title}](.prompts-gpt/${stem}.md)`),
|
|
819
|
+
"",
|
|
820
|
+
"When a prompt pack is relevant, load it, adapt variables to the current task, and keep verification tied to the prompt's acceptance criteria.",
|
|
821
|
+
"",
|
|
822
|
+
].join("\n"),
|
|
823
|
+
}];
|
|
824
|
+
}
|
|
632
825
|
return [];
|
|
633
826
|
}
|
|
634
827
|
function buildVsCodeSnippets(prompts) {
|
|
@@ -689,13 +882,52 @@ function buildDiscoverablePromptFiles(stem, prompt) {
|
|
|
689
882
|
return {
|
|
690
883
|
markdown: `.prompts-gpt/${stem}.md`,
|
|
691
884
|
codexInstructions: supports("codex") ? "AGENTS.md" : null,
|
|
885
|
+
claudeCodeInstructions: supports("claude-code") ? "CLAUDE.md" : null,
|
|
692
886
|
cursorRule: supports("cursor") ? `.cursor/rules/prompts-gpt-${stem}.mdc` : null,
|
|
887
|
+
cursorCommand: supports("cursor") ? `.cursor/commands/prompts-gpt-${stem}.md` : null,
|
|
693
888
|
vscodeInstructions: supports("vscode") ? ".github/copilot-instructions.md" : null,
|
|
694
889
|
copilotPathInstructions: supports("vscode") ? ".github/instructions/prompts-gpt.instructions.md" : null,
|
|
695
890
|
vscodeSnippets: supports("vscode") ? ".vscode/prompts-gpt.code-snippets" : null,
|
|
696
891
|
copilotPrompt: supports("copilot") ? `.github/prompts/prompts-gpt-${stem}.prompt.md` : null,
|
|
892
|
+
continueRule: supports("continue") ? `.continue/rules/prompts-gpt-${stem}.md` : null,
|
|
893
|
+
geminiInstructions: supports("gemini-cli") ? "GEMINI.md" : null,
|
|
894
|
+
windsurfRule: supports("windsurf") ? `.windsurf/rules/prompts-gpt-${stem}.md` : null,
|
|
895
|
+
clineRule: supports("cline") ? `.clinerules/prompts-gpt-${stem}.md` : null,
|
|
896
|
+
junieGuidelines: supports("junie") ? ".junie/guidelines.md" : null,
|
|
897
|
+
ampInstructions: supports("amp") ? "AGENT.md" : null,
|
|
697
898
|
};
|
|
698
899
|
}
|
|
900
|
+
function formatCursorCommandMarkdown(prompt, stem) {
|
|
901
|
+
const lines = [
|
|
902
|
+
`# ${prompt.title}`,
|
|
903
|
+
"",
|
|
904
|
+
`[Canonical prompt pack](../../.prompts-gpt/${stem}.md)`,
|
|
905
|
+
"",
|
|
906
|
+
prompt.summary ?? "",
|
|
907
|
+
"",
|
|
908
|
+
"## Task",
|
|
909
|
+
"",
|
|
910
|
+
prompt.promptText ?? "",
|
|
911
|
+
];
|
|
912
|
+
if (prompt.variables?.length) {
|
|
913
|
+
lines.push("", "## Inputs", "");
|
|
914
|
+
for (const variable of prompt.variables) {
|
|
915
|
+
lines.push(`- ${String(variable).replace(/[{}$]/g, "")}`);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
if (prompt.usageNotes) {
|
|
919
|
+
lines.push("", "## Usage Notes", "", prompt.usageNotes);
|
|
920
|
+
}
|
|
921
|
+
lines.push("", "Verify the output against `.prompts-gpt/manifest.json` and the linked canonical prompt pack.", "");
|
|
922
|
+
return lines
|
|
923
|
+
.reduce((acc, line, index, list) => {
|
|
924
|
+
if (line === "" && index > 0 && list[index - 1] === "" && (index < 2 || list[index - 2] === ""))
|
|
925
|
+
return acc;
|
|
926
|
+
acc.push(line);
|
|
927
|
+
return acc;
|
|
928
|
+
}, [])
|
|
929
|
+
.join("\n");
|
|
930
|
+
}
|
|
699
931
|
function formatCopilotPromptMarkdown(prompt, stem) {
|
|
700
932
|
const sections = [
|
|
701
933
|
"---",
|
|
@@ -755,8 +987,9 @@ function escapeMarkdownLinkText(value) {
|
|
|
755
987
|
return value.replace(/[[\]]/g, "\\$&");
|
|
756
988
|
}
|
|
757
989
|
async function writePromptIndex(prompts, { outDir }) {
|
|
758
|
-
const indexPath = path.
|
|
759
|
-
|
|
990
|
+
const indexPath = path.resolve(outDir, "README.md");
|
|
991
|
+
assertInside(indexPath, path.resolve(outDir));
|
|
992
|
+
const managedContent = [
|
|
760
993
|
"# Prompts-GPT Prompt Packs",
|
|
761
994
|
"",
|
|
762
995
|
"These prompts were synced by `prompts-gpt`. Re-run `prompts-gpt sync` to refresh Markdown and agent files.",
|
|
@@ -770,8 +1003,12 @@ async function writePromptIndex(prompts, { outDir }) {
|
|
|
770
1003
|
}),
|
|
771
1004
|
"",
|
|
772
1005
|
].join("\n");
|
|
773
|
-
|
|
774
|
-
|
|
1006
|
+
let existing = "";
|
|
1007
|
+
try {
|
|
1008
|
+
existing = await readFile(indexPath, "utf8");
|
|
1009
|
+
}
|
|
1010
|
+
catch { }
|
|
1011
|
+
await writeFile(indexPath, upsertManagedBlock(existing, managedContent));
|
|
775
1012
|
}
|
|
776
1013
|
function normalizeAgentTargets(value) {
|
|
777
1014
|
const raw = Array.isArray(value) ? value.join(",") : String(value ?? "all");
|
|
@@ -834,8 +1071,8 @@ function assertInside(filePath, directory) {
|
|
|
834
1071
|
}
|
|
835
1072
|
}
|
|
836
1073
|
function safeSlug(value) {
|
|
837
|
-
const raw = String(value ?? "");
|
|
838
|
-
if (!raw
|
|
1074
|
+
const raw = String(value ?? "").trim();
|
|
1075
|
+
if (!raw)
|
|
839
1076
|
return "prompt";
|
|
840
1077
|
const slug = raw
|
|
841
1078
|
.toLowerCase()
|
|
@@ -844,7 +1081,9 @@ function safeSlug(value) {
|
|
|
844
1081
|
.replace(/[^a-z0-9]+/g, "-")
|
|
845
1082
|
.replace(/^-+|-+$/g, "")
|
|
846
1083
|
.slice(0, 90);
|
|
847
|
-
|
|
1084
|
+
if (!slug || slug === "-")
|
|
1085
|
+
return "prompt";
|
|
1086
|
+
return slug;
|
|
848
1087
|
}
|
|
849
1088
|
function yamlScalar(value) {
|
|
850
1089
|
const s = String(value ?? "");
|
|
@@ -863,5 +1102,14 @@ async function ensureGitignoreEntry(cwd, entry) {
|
|
|
863
1102
|
const needsLeadingNewline = existing.length > 0 && !existing.endsWith("\n") && !existing.endsWith("\r\n");
|
|
864
1103
|
const prefix = needsLeadingNewline ? eol : "";
|
|
865
1104
|
await writeFile(gitignorePath, `${existing}${prefix}${entry}${eol}`);
|
|
1105
|
+
// Re-read to verify entry was written (guards against concurrent writers)
|
|
1106
|
+
const verify = await readFile(gitignorePath, "utf8");
|
|
1107
|
+
const verifyLines = verify.split(/\r?\n/).map((line) => line.trim());
|
|
1108
|
+
const count = verifyLines.filter((l) => l === entry).length;
|
|
1109
|
+
if (count > 1) {
|
|
1110
|
+
// Deduplicate if a concurrent call wrote the same entry
|
|
1111
|
+
const deduped = verifyLines.filter((l, i) => l !== entry || verifyLines.indexOf(entry) === i);
|
|
1112
|
+
await writeFile(gitignorePath, deduped.join(eol) + (verify.endsWith("\n") || verify.endsWith("\r\n") ? eol : ""));
|
|
1113
|
+
}
|
|
866
1114
|
}
|
|
867
1115
|
//# sourceMappingURL=index.js.map
|