@hasna/prompts 0.2.2 → 0.3.0
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/index.js +905 -91
- package/dist/db/database.d.ts.map +1 -1
- package/dist/db/prompts.d.ts +8 -0
- package/dist/db/prompts.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -0
- package/dist/lib/audit.d.ts +16 -0
- package/dist/lib/audit.d.ts.map +1 -0
- package/dist/lib/completion.d.ts +3 -0
- package/dist/lib/completion.d.ts.map +1 -0
- package/dist/lib/diff.d.ts +8 -0
- package/dist/lib/diff.d.ts.map +1 -0
- package/dist/lib/importer.d.ts +8 -0
- package/dist/lib/importer.d.ts.map +1 -1
- package/dist/lib/search.d.ts.map +1 -1
- package/dist/mcp/index.js +367 -3
- package/dist/server/index.js +28 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAMrC,wBAAgB,SAAS,IAAI,MAAM,CAsBlC;AAED,wBAAgB,WAAW,IAAI,QAAQ,CAmBtC;AAED,wBAAgB,aAAa,IAAI,IAAI,CAKpC;AAGD,wBAAgB,aAAa,IAAI,IAAI,CAEpC;
|
|
1
|
+
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAMrC,wBAAgB,SAAS,IAAI,MAAM,CAsBlC;AAED,wBAAgB,WAAW,IAAI,QAAQ,CAmBtC;AAED,wBAAgB,aAAa,IAAI,IAAI,CAKpC;AAGD,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAyJD,wBAAgB,cAAc,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA0B5E;AAED,wBAAgB,MAAM,CAAC,EAAE,EAAE,QAAQ,GAAG,OAAO,CAM5C;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAkC3E"}
|
package/dist/db/prompts.d.ts
CHANGED
|
@@ -6,6 +6,14 @@ export declare function listPrompts(filter?: ListPromptsFilter): Prompt[];
|
|
|
6
6
|
export declare function updatePrompt(idOrSlug: string, input: UpdatePromptInput): Prompt;
|
|
7
7
|
export declare function deletePrompt(idOrSlug: string): void;
|
|
8
8
|
export declare function usePrompt(idOrSlug: string): Prompt;
|
|
9
|
+
export declare function getTrending(days?: number, limit?: number): Array<{
|
|
10
|
+
id: string;
|
|
11
|
+
slug: string;
|
|
12
|
+
title: string;
|
|
13
|
+
uses: number;
|
|
14
|
+
}>;
|
|
15
|
+
export declare function setExpiry(idOrSlug: string, expiresAt: string | null): Prompt;
|
|
16
|
+
export declare function setNextPrompt(idOrSlug: string, nextSlug: string | null): Prompt;
|
|
9
17
|
export declare function pinPrompt(idOrSlug: string, pinned: boolean): Prompt;
|
|
10
18
|
export declare function upsertPrompt(input: CreatePromptInput, force?: boolean): {
|
|
11
19
|
prompt: Prompt;
|
package/dist/db/prompts.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/db/prompts.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,MAAM,EACN,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EAGlB,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/db/prompts.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,MAAM,EACN,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EAGlB,MAAM,mBAAmB,CAAA;AA6B1B,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CA0C7D;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOzD;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAItD;AAED,wBAAgB,WAAW,CAAC,MAAM,GAAE,iBAAsB,GAAG,MAAM,EAAE,CA4CpE;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAiD/E;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAInD;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CASlD;AAED,wBAAgB,WAAW,CAAC,IAAI,SAAI,EAAE,KAAK,SAAK,GAAG,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAYlH;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAK5E;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAK/E;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAKnE;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,EAAE,KAAK,UAAQ,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAAE,CA6BtI;AAED,wBAAgB,cAAc;;;;;YAOJ,MAAM;cAAQ,MAAM;cAAQ,MAAM;eAAS,MAAM;mBAAa,MAAM;;;YAGpE,MAAM;cAAQ,MAAM;cAAQ,MAAM;eAAS,MAAM;sBAAgB,MAAM;;;oBAG/D,MAAM;eAAS,MAAM;;;gBAGzB,MAAM;eAAS,MAAM;;EAGlD"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { createPrompt, getPrompt, requirePrompt, listPrompts, updatePrompt, deletePrompt, usePrompt, upsertPrompt, getPromptStats, pinPrompt } from "./db/prompts.js";
|
|
1
|
+
export { createPrompt, getPrompt, requirePrompt, listPrompts, updatePrompt, deletePrompt, usePrompt, upsertPrompt, getPromptStats, pinPrompt, setNextPrompt } from "./db/prompts.js";
|
|
2
2
|
export { listVersions, getVersion, restoreVersion } from "./db/versions.js";
|
|
3
3
|
export { listCollections, getCollection, ensureCollection, movePrompt } from "./db/collections.js";
|
|
4
4
|
export { registerAgent, listAgents } from "./db/agents.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AACpL,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAC3E,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAClG,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAC1D,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACzD,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAGzF,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAG5D,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AACvG,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAGrD,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACpD,YAAY,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAGzD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAGzE,YAAY,EACV,MAAM,EACN,aAAa,EACb,UAAU,EACV,KAAK,EACL,OAAO,EACP,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,WAAW,GACZ,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,kBAAkB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -128,6 +128,26 @@ function runMigrations(db) {
|
|
|
128
128
|
CREATE INDEX IF NOT EXISTS idx_prompts_project_id ON prompts(project_id);
|
|
129
129
|
`
|
|
130
130
|
},
|
|
131
|
+
{
|
|
132
|
+
name: "005_chaining",
|
|
133
|
+
sql: `ALTER TABLE prompts ADD COLUMN next_prompt TEXT;`
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: "006_expiry",
|
|
137
|
+
sql: `ALTER TABLE prompts ADD COLUMN expires_at TEXT;`
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
name: "007_usage_log",
|
|
141
|
+
sql: `
|
|
142
|
+
CREATE TABLE IF NOT EXISTS usage_log (
|
|
143
|
+
id TEXT PRIMARY KEY,
|
|
144
|
+
prompt_id TEXT NOT NULL REFERENCES prompts(id) ON DELETE CASCADE,
|
|
145
|
+
used_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
146
|
+
);
|
|
147
|
+
CREATE INDEX IF NOT EXISTS idx_usage_log_prompt_id ON usage_log(prompt_id);
|
|
148
|
+
CREATE INDEX IF NOT EXISTS idx_usage_log_used_at ON usage_log(used_at);
|
|
149
|
+
`
|
|
150
|
+
},
|
|
131
151
|
{
|
|
132
152
|
name: "002_fts5",
|
|
133
153
|
sql: `
|
|
@@ -431,6 +451,8 @@ function rowToPrompt(row) {
|
|
|
431
451
|
tags: JSON.parse(row["tags"] || "[]"),
|
|
432
452
|
variables: JSON.parse(row["variables"] || "[]"),
|
|
433
453
|
pinned: Boolean(row["pinned"]),
|
|
454
|
+
next_prompt: row["next_prompt"] ?? null,
|
|
455
|
+
expires_at: row["expires_at"] ?? null,
|
|
434
456
|
project_id: row["project_id"] ?? null,
|
|
435
457
|
is_template: Boolean(row["is_template"]),
|
|
436
458
|
source: row["source"],
|
|
@@ -529,6 +551,7 @@ function updatePrompt(idOrSlug, input) {
|
|
|
529
551
|
description = COALESCE(?, description),
|
|
530
552
|
collection = COALESCE(?, collection),
|
|
531
553
|
tags = COALESCE(?, tags),
|
|
554
|
+
next_prompt = CASE WHEN ? IS NOT NULL THEN ? ELSE next_prompt END,
|
|
532
555
|
variables = ?,
|
|
533
556
|
is_template = ?,
|
|
534
557
|
version = version + 1,
|
|
@@ -539,6 +562,8 @@ function updatePrompt(idOrSlug, input) {
|
|
|
539
562
|
input.description ?? null,
|
|
540
563
|
input.collection ?? null,
|
|
541
564
|
input.tags ? JSON.stringify(input.tags) : null,
|
|
565
|
+
"next_prompt" in input ? input.next_prompt ?? "" : null,
|
|
566
|
+
"next_prompt" in input ? input.next_prompt ?? null : null,
|
|
542
567
|
variables,
|
|
543
568
|
is_template,
|
|
544
569
|
prompt.id,
|
|
@@ -561,6 +586,13 @@ function usePrompt(idOrSlug) {
|
|
|
561
586
|
const db = getDatabase();
|
|
562
587
|
const prompt = requirePrompt(idOrSlug);
|
|
563
588
|
db.run("UPDATE prompts SET use_count = use_count + 1, last_used_at = datetime('now') WHERE id = ?", [prompt.id]);
|
|
589
|
+
db.run("INSERT INTO usage_log (id, prompt_id) VALUES (?, ?)", [generateId("UL"), prompt.id]);
|
|
590
|
+
return requirePrompt(prompt.id);
|
|
591
|
+
}
|
|
592
|
+
function setNextPrompt(idOrSlug, nextSlug) {
|
|
593
|
+
const db = getDatabase();
|
|
594
|
+
const prompt = requirePrompt(idOrSlug);
|
|
595
|
+
db.run("UPDATE prompts SET next_prompt = ?, updated_at = datetime('now') WHERE id = ?", [nextSlug, prompt.id]);
|
|
564
596
|
return requirePrompt(prompt.id);
|
|
565
597
|
}
|
|
566
598
|
function pinPrompt(idOrSlug, pinned) {
|
|
@@ -732,6 +764,8 @@ function rowToSearchResult(row, snippet) {
|
|
|
732
764
|
tags: JSON.parse(row["tags"] || "[]"),
|
|
733
765
|
variables: JSON.parse(row["variables"] || "[]"),
|
|
734
766
|
pinned: Boolean(row["pinned"]),
|
|
767
|
+
next_prompt: row["next_prompt"] ?? null,
|
|
768
|
+
expires_at: row["expires_at"] ?? null,
|
|
735
769
|
project_id: row["project_id"] ?? null,
|
|
736
770
|
is_template: Boolean(row["is_template"]),
|
|
737
771
|
source: row["source"],
|
|
@@ -860,6 +894,7 @@ export {
|
|
|
860
894
|
upsertPrompt,
|
|
861
895
|
updatePrompt,
|
|
862
896
|
uniqueSlug,
|
|
897
|
+
setNextPrompt,
|
|
863
898
|
searchPrompts,
|
|
864
899
|
restoreVersion,
|
|
865
900
|
requirePrompt,
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface AuditIssue {
|
|
2
|
+
type: "orphaned-project" | "empty-collection" | "missing-version-history" | "near-duplicate-slug" | "expired";
|
|
3
|
+
severity: "error" | "warn" | "info";
|
|
4
|
+
prompt_id?: string;
|
|
5
|
+
slug?: string;
|
|
6
|
+
message: string;
|
|
7
|
+
}
|
|
8
|
+
export interface AuditReport {
|
|
9
|
+
issues: AuditIssue[];
|
|
10
|
+
errors: number;
|
|
11
|
+
warnings: number;
|
|
12
|
+
info: number;
|
|
13
|
+
checked_at: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function runAudit(): AuditReport;
|
|
16
|
+
//# sourceMappingURL=audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/lib/audit.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,kBAAkB,GAAG,kBAAkB,GAAG,yBAAyB,GAAG,qBAAqB,GAAG,SAAS,CAAA;IAC7G,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAA;IACnC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,UAAU,EAAE,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,QAAQ,IAAI,WAAW,CAuFtC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"completion.d.ts","sourceRoot":"","sources":["../../src/lib/completion.ts"],"names":[],"mappings":"AAYA,wBAAgB,qBAAqB,IAAI,MAAM,CAkE9C;AAED,wBAAgB,sBAAsB,IAAI,MAAM,CAgD/C"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface DiffLine {
|
|
2
|
+
type: "added" | "removed" | "unchanged";
|
|
3
|
+
content: string;
|
|
4
|
+
lineNum?: number;
|
|
5
|
+
}
|
|
6
|
+
export declare function diffTexts(a: string, b: string): DiffLine[];
|
|
7
|
+
export declare function formatDiff(lines: DiffLine[]): string;
|
|
8
|
+
//# sourceMappingURL=diff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/lib/diff.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,OAAO,GAAG,SAAS,GAAG,WAAW,CAAA;IACvC,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,QAAQ,EAAE,CAmC1D;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,CAMpD"}
|
package/dist/lib/importer.d.ts
CHANGED
|
@@ -31,6 +31,14 @@ export declare function importFromMarkdown(files: Array<{
|
|
|
31
31
|
filename: string;
|
|
32
32
|
content: string;
|
|
33
33
|
}>, changedBy?: string): ImportResult;
|
|
34
|
+
export interface SlashCommandScanResult {
|
|
35
|
+
scanned: Array<{
|
|
36
|
+
source: string;
|
|
37
|
+
file: string;
|
|
38
|
+
}>;
|
|
39
|
+
imported: ImportResult;
|
|
40
|
+
}
|
|
41
|
+
export declare function scanAndImportSlashCommands(rootDir: string, changedBy?: string): SlashCommandScanResult;
|
|
34
42
|
export declare function importFromClaudeCommands(files: Array<{
|
|
35
43
|
filename: string;
|
|
36
44
|
content: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"importer.d.ts","sourceRoot":"","sources":["../../src/lib/importer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAqB,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAElE,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC/C;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,YAAY,CA0BpF;AAED,wBAAgB,YAAY,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG;IACjD,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAGA;AAaD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAYvD;AAED,wBAAgB,qBAAqB,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAMvG;AAGD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CA8B1F;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,YAAY,CAKxH;AAKD,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,EACnD,SAAS,CAAC,EAAE,MAAM,GACjB,YAAY,CAad"}
|
|
1
|
+
{"version":3,"file":"importer.d.ts","sourceRoot":"","sources":["../../src/lib/importer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAqB,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAElE,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC/C;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,YAAY,CA0BpF;AAED,wBAAgB,YAAY,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG;IACjD,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAGA;AAaD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAYvD;AAED,wBAAgB,qBAAqB,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAMvG;AAGD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CA8B1F;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,YAAY,CAKxH;AAGD,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAChD,QAAQ,EAAE,YAAY,CAAA;CACvB;AAED,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,MAAM,EACf,SAAS,CAAC,EAAE,MAAM,GACjB,sBAAsB,CAgDxB;AAKD,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,EACnD,SAAS,CAAC,EAAE,MAAM,GACjB,YAAY,CAad"}
|
package/dist/lib/search.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/lib/search.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/lib/search.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AA0CxE,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EACb,MAAM,GAAE,IAAI,CAAC,iBAAiB,EAAE,GAAG,CAAM,GACxC,YAAY,EAAE,CAwEhB;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,YAAY,EAAE,CAmCvE"}
|
package/dist/mcp/index.js
CHANGED
|
@@ -4118,6 +4118,26 @@ function runMigrations(db) {
|
|
|
4118
4118
|
CREATE INDEX IF NOT EXISTS idx_prompts_project_id ON prompts(project_id);
|
|
4119
4119
|
`
|
|
4120
4120
|
},
|
|
4121
|
+
{
|
|
4122
|
+
name: "005_chaining",
|
|
4123
|
+
sql: `ALTER TABLE prompts ADD COLUMN next_prompt TEXT;`
|
|
4124
|
+
},
|
|
4125
|
+
{
|
|
4126
|
+
name: "006_expiry",
|
|
4127
|
+
sql: `ALTER TABLE prompts ADD COLUMN expires_at TEXT;`
|
|
4128
|
+
},
|
|
4129
|
+
{
|
|
4130
|
+
name: "007_usage_log",
|
|
4131
|
+
sql: `
|
|
4132
|
+
CREATE TABLE IF NOT EXISTS usage_log (
|
|
4133
|
+
id TEXT PRIMARY KEY,
|
|
4134
|
+
prompt_id TEXT NOT NULL REFERENCES prompts(id) ON DELETE CASCADE,
|
|
4135
|
+
used_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
4136
|
+
);
|
|
4137
|
+
CREATE INDEX IF NOT EXISTS idx_usage_log_prompt_id ON usage_log(prompt_id);
|
|
4138
|
+
CREATE INDEX IF NOT EXISTS idx_usage_log_used_at ON usage_log(used_at);
|
|
4139
|
+
`
|
|
4140
|
+
},
|
|
4121
4141
|
{
|
|
4122
4142
|
name: "002_fts5",
|
|
4123
4143
|
sql: `
|
|
@@ -4413,6 +4433,8 @@ function rowToPrompt(row) {
|
|
|
4413
4433
|
tags: JSON.parse(row["tags"] || "[]"),
|
|
4414
4434
|
variables: JSON.parse(row["variables"] || "[]"),
|
|
4415
4435
|
pinned: Boolean(row["pinned"]),
|
|
4436
|
+
next_prompt: row["next_prompt"] ?? null,
|
|
4437
|
+
expires_at: row["expires_at"] ?? null,
|
|
4416
4438
|
project_id: row["project_id"] ?? null,
|
|
4417
4439
|
is_template: Boolean(row["is_template"]),
|
|
4418
4440
|
source: row["source"],
|
|
@@ -4511,6 +4533,7 @@ function updatePrompt(idOrSlug, input) {
|
|
|
4511
4533
|
description = COALESCE(?, description),
|
|
4512
4534
|
collection = COALESCE(?, collection),
|
|
4513
4535
|
tags = COALESCE(?, tags),
|
|
4536
|
+
next_prompt = CASE WHEN ? IS NOT NULL THEN ? ELSE next_prompt END,
|
|
4514
4537
|
variables = ?,
|
|
4515
4538
|
is_template = ?,
|
|
4516
4539
|
version = version + 1,
|
|
@@ -4521,6 +4544,8 @@ function updatePrompt(idOrSlug, input) {
|
|
|
4521
4544
|
input.description ?? null,
|
|
4522
4545
|
input.collection ?? null,
|
|
4523
4546
|
input.tags ? JSON.stringify(input.tags) : null,
|
|
4547
|
+
"next_prompt" in input ? input.next_prompt ?? "" : null,
|
|
4548
|
+
"next_prompt" in input ? input.next_prompt ?? null : null,
|
|
4524
4549
|
variables,
|
|
4525
4550
|
is_template,
|
|
4526
4551
|
prompt.id,
|
|
@@ -4543,6 +4568,30 @@ function usePrompt(idOrSlug) {
|
|
|
4543
4568
|
const db = getDatabase();
|
|
4544
4569
|
const prompt = requirePrompt(idOrSlug);
|
|
4545
4570
|
db.run("UPDATE prompts SET use_count = use_count + 1, last_used_at = datetime('now') WHERE id = ?", [prompt.id]);
|
|
4571
|
+
db.run("INSERT INTO usage_log (id, prompt_id) VALUES (?, ?)", [generateId("UL"), prompt.id]);
|
|
4572
|
+
return requirePrompt(prompt.id);
|
|
4573
|
+
}
|
|
4574
|
+
function getTrending(days = 7, limit = 10) {
|
|
4575
|
+
const db = getDatabase();
|
|
4576
|
+
const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString();
|
|
4577
|
+
return db.query(`SELECT p.id, p.slug, p.title, COUNT(ul.id) as uses
|
|
4578
|
+
FROM usage_log ul
|
|
4579
|
+
JOIN prompts p ON p.id = ul.prompt_id
|
|
4580
|
+
WHERE ul.used_at >= ?
|
|
4581
|
+
GROUP BY p.id
|
|
4582
|
+
ORDER BY uses DESC
|
|
4583
|
+
LIMIT ?`).all(cutoff, limit);
|
|
4584
|
+
}
|
|
4585
|
+
function setExpiry(idOrSlug, expiresAt) {
|
|
4586
|
+
const db = getDatabase();
|
|
4587
|
+
const prompt = requirePrompt(idOrSlug);
|
|
4588
|
+
db.run("UPDATE prompts SET expires_at = ?, updated_at = datetime('now') WHERE id = ?", [expiresAt, prompt.id]);
|
|
4589
|
+
return requirePrompt(prompt.id);
|
|
4590
|
+
}
|
|
4591
|
+
function setNextPrompt(idOrSlug, nextSlug) {
|
|
4592
|
+
const db = getDatabase();
|
|
4593
|
+
const prompt = requirePrompt(idOrSlug);
|
|
4594
|
+
db.run("UPDATE prompts SET next_prompt = ?, updated_at = datetime('now') WHERE id = ?", [nextSlug, prompt.id]);
|
|
4546
4595
|
return requirePrompt(prompt.id);
|
|
4547
4596
|
}
|
|
4548
4597
|
function pinPrompt(idOrSlug, pinned) {
|
|
@@ -4713,6 +4762,8 @@ function rowToSearchResult(row, snippet) {
|
|
|
4713
4762
|
tags: JSON.parse(row["tags"] || "[]"),
|
|
4714
4763
|
variables: JSON.parse(row["variables"] || "[]"),
|
|
4715
4764
|
pinned: Boolean(row["pinned"]),
|
|
4765
|
+
next_prompt: row["next_prompt"] ?? null,
|
|
4766
|
+
expires_at: row["expires_at"] ?? null,
|
|
4716
4767
|
project_id: row["project_id"] ?? null,
|
|
4717
4768
|
is_template: Boolean(row["is_template"]),
|
|
4718
4769
|
source: row["source"],
|
|
@@ -4836,6 +4887,77 @@ function exportToJson(collection) {
|
|
|
4836
4887
|
const prompts = listPrompts({ collection, limit: 1e4 });
|
|
4837
4888
|
return { prompts, exported_at: new Date().toISOString(), collection };
|
|
4838
4889
|
}
|
|
4890
|
+
function markdownToImportItem(content, filename) {
|
|
4891
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n+([\s\S]*)$/);
|
|
4892
|
+
if (!frontmatterMatch) {
|
|
4893
|
+
if (!filename)
|
|
4894
|
+
return null;
|
|
4895
|
+
const title2 = filename.replace(/\.md$/, "").replace(/-/g, " ");
|
|
4896
|
+
return { title: title2, body: content.trim() };
|
|
4897
|
+
}
|
|
4898
|
+
const frontmatter = frontmatterMatch[1] ?? "";
|
|
4899
|
+
const body = (frontmatterMatch[2] ?? "").trim();
|
|
4900
|
+
const get = (key) => {
|
|
4901
|
+
const m = frontmatter.match(new RegExp(`^${key}:\\s*(.+)$`, "m"));
|
|
4902
|
+
return m ? (m[1] ?? "").trim() : null;
|
|
4903
|
+
};
|
|
4904
|
+
const title = get("title") ?? (filename?.replace(/\.md$/, "").replace(/-/g, " ") ?? "Untitled");
|
|
4905
|
+
const slug = get("slug") ?? undefined;
|
|
4906
|
+
const collection = get("collection") ?? undefined;
|
|
4907
|
+
const description = get("description") ?? undefined;
|
|
4908
|
+
const tagsStr = get("tags");
|
|
4909
|
+
let tags;
|
|
4910
|
+
if (tagsStr) {
|
|
4911
|
+
const inner = tagsStr.replace(/^\[/, "").replace(/\]$/, "");
|
|
4912
|
+
tags = inner.split(",").map((t) => t.trim()).filter(Boolean);
|
|
4913
|
+
}
|
|
4914
|
+
return { title, slug, body, collection, tags, description };
|
|
4915
|
+
}
|
|
4916
|
+
function scanAndImportSlashCommands(rootDir, changedBy) {
|
|
4917
|
+
const { existsSync: existsSync2, readdirSync, readFileSync } = __require("fs");
|
|
4918
|
+
const { join: join2 } = __require("path");
|
|
4919
|
+
const home = process.env["HOME"] ?? "~";
|
|
4920
|
+
const sources = [
|
|
4921
|
+
{ dir: join2(rootDir, ".claude", "commands"), collection: "claude-commands", tags: ["claude", "slash-command"] },
|
|
4922
|
+
{ dir: join2(home, ".claude", "commands"), collection: "claude-commands", tags: ["claude", "slash-command"] },
|
|
4923
|
+
{ dir: join2(rootDir, ".codex", "skills"), collection: "codex-skills", tags: ["codex", "skill"] },
|
|
4924
|
+
{ dir: join2(home, ".codex", "skills"), collection: "codex-skills", tags: ["codex", "skill"] },
|
|
4925
|
+
{ dir: join2(rootDir, ".gemini", "extensions"), collection: "gemini-extensions", tags: ["gemini", "extension"] },
|
|
4926
|
+
{ dir: join2(home, ".gemini", "extensions"), collection: "gemini-extensions", tags: ["gemini", "extension"] }
|
|
4927
|
+
];
|
|
4928
|
+
const files = [];
|
|
4929
|
+
const scanned = [];
|
|
4930
|
+
for (const { dir, collection, tags } of sources) {
|
|
4931
|
+
if (!existsSync2(dir))
|
|
4932
|
+
continue;
|
|
4933
|
+
let entries;
|
|
4934
|
+
try {
|
|
4935
|
+
entries = readdirSync(dir);
|
|
4936
|
+
} catch {
|
|
4937
|
+
continue;
|
|
4938
|
+
}
|
|
4939
|
+
for (const entry of entries) {
|
|
4940
|
+
if (!entry.endsWith(".md"))
|
|
4941
|
+
continue;
|
|
4942
|
+
const filePath = join2(dir, entry);
|
|
4943
|
+
try {
|
|
4944
|
+
const content = readFileSync(filePath, "utf-8");
|
|
4945
|
+
files.push({ filename: entry, content, collection, tags });
|
|
4946
|
+
scanned.push({ source: dir, file: entry });
|
|
4947
|
+
} catch {}
|
|
4948
|
+
}
|
|
4949
|
+
}
|
|
4950
|
+
const items = files.map((f) => {
|
|
4951
|
+
const base = markdownToImportItem(f.content, f.filename);
|
|
4952
|
+
if (base)
|
|
4953
|
+
return { ...base, collection: base.collection ?? f.collection, tags: base.tags ?? f.tags };
|
|
4954
|
+
const name = f.filename.replace(/\.md$/, "");
|
|
4955
|
+
const title = name.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
4956
|
+
return { title, slug: name, body: f.content.trim(), collection: f.collection, tags: f.tags };
|
|
4957
|
+
});
|
|
4958
|
+
const imported = importFromJson(items, changedBy);
|
|
4959
|
+
return { scanned, imported };
|
|
4960
|
+
}
|
|
4839
4961
|
|
|
4840
4962
|
// src/lib/mementos.ts
|
|
4841
4963
|
async function maybeSaveMemento(opts) {
|
|
@@ -4861,6 +4983,48 @@ async function maybeSaveMemento(opts) {
|
|
|
4861
4983
|
} catch {}
|
|
4862
4984
|
}
|
|
4863
4985
|
|
|
4986
|
+
// src/lib/diff.ts
|
|
4987
|
+
function diffTexts(a, b) {
|
|
4988
|
+
const aLines = a.split(`
|
|
4989
|
+
`);
|
|
4990
|
+
const bLines = b.split(`
|
|
4991
|
+
`);
|
|
4992
|
+
const m = aLines.length;
|
|
4993
|
+
const n = bLines.length;
|
|
4994
|
+
const lcs = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
|
|
4995
|
+
for (let i2 = 1;i2 <= m; i2++) {
|
|
4996
|
+
for (let j2 = 1;j2 <= n; j2++) {
|
|
4997
|
+
lcs[i2][j2] = aLines[i2 - 1] === bLines[j2 - 1] ? (lcs[i2 - 1][j2 - 1] ?? 0) + 1 : Math.max(lcs[i2 - 1][j2] ?? 0, lcs[i2][j2 - 1] ?? 0);
|
|
4998
|
+
}
|
|
4999
|
+
}
|
|
5000
|
+
const trace = [];
|
|
5001
|
+
let i = m, j = n;
|
|
5002
|
+
while (i > 0 || j > 0) {
|
|
5003
|
+
if (i > 0 && j > 0 && aLines[i - 1] === bLines[j - 1]) {
|
|
5004
|
+
trace.unshift({ type: "unchanged", content: aLines[i - 1] ?? "" });
|
|
5005
|
+
i--;
|
|
5006
|
+
j--;
|
|
5007
|
+
} else if (j > 0 && (i === 0 || (lcs[i][j - 1] ?? 0) >= (lcs[i - 1][j] ?? 0))) {
|
|
5008
|
+
trace.unshift({ type: "added", content: bLines[j - 1] ?? "" });
|
|
5009
|
+
j--;
|
|
5010
|
+
} else {
|
|
5011
|
+
trace.unshift({ type: "removed", content: aLines[i - 1] ?? "" });
|
|
5012
|
+
i--;
|
|
5013
|
+
}
|
|
5014
|
+
}
|
|
5015
|
+
return trace;
|
|
5016
|
+
}
|
|
5017
|
+
function formatDiff(lines) {
|
|
5018
|
+
return lines.map((l) => {
|
|
5019
|
+
if (l.type === "added")
|
|
5020
|
+
return `+ ${l.content}`;
|
|
5021
|
+
if (l.type === "removed")
|
|
5022
|
+
return `- ${l.content}`;
|
|
5023
|
+
return ` ${l.content}`;
|
|
5024
|
+
}).join(`
|
|
5025
|
+
`);
|
|
5026
|
+
}
|
|
5027
|
+
|
|
4864
5028
|
// src/lib/lint.ts
|
|
4865
5029
|
function lintPrompt(p) {
|
|
4866
5030
|
const issues = [];
|
|
@@ -4895,6 +5059,83 @@ function lintAll(prompts) {
|
|
|
4895
5059
|
return prompts.map((p) => ({ prompt: p, issues: lintPrompt(p) })).filter((r) => r.issues.length > 0);
|
|
4896
5060
|
}
|
|
4897
5061
|
|
|
5062
|
+
// src/lib/audit.ts
|
|
5063
|
+
function runAudit() {
|
|
5064
|
+
const db = getDatabase();
|
|
5065
|
+
const issues = [];
|
|
5066
|
+
const orphaned = db.query(`
|
|
5067
|
+
SELECT p.id, p.slug FROM prompts p
|
|
5068
|
+
WHERE p.project_id IS NOT NULL
|
|
5069
|
+
AND NOT EXISTS (SELECT 1 FROM projects pr WHERE pr.id = p.project_id)
|
|
5070
|
+
`).all();
|
|
5071
|
+
for (const p of orphaned) {
|
|
5072
|
+
issues.push({
|
|
5073
|
+
type: "orphaned-project",
|
|
5074
|
+
severity: "error",
|
|
5075
|
+
prompt_id: p.id,
|
|
5076
|
+
slug: p.slug,
|
|
5077
|
+
message: `Prompt "${p.slug}" references a deleted project`
|
|
5078
|
+
});
|
|
5079
|
+
}
|
|
5080
|
+
const emptyCollections = db.query(`
|
|
5081
|
+
SELECT c.name FROM collections c
|
|
5082
|
+
WHERE NOT EXISTS (SELECT 1 FROM prompts p WHERE p.collection = c.name)
|
|
5083
|
+
AND c.name != 'default'
|
|
5084
|
+
`).all();
|
|
5085
|
+
for (const c of emptyCollections) {
|
|
5086
|
+
issues.push({
|
|
5087
|
+
type: "empty-collection",
|
|
5088
|
+
severity: "info",
|
|
5089
|
+
message: `Collection "${c.name}" has no prompts`
|
|
5090
|
+
});
|
|
5091
|
+
}
|
|
5092
|
+
const missingHistory = db.query(`
|
|
5093
|
+
SELECT p.id, p.slug FROM prompts p
|
|
5094
|
+
WHERE NOT EXISTS (SELECT 1 FROM prompt_versions v WHERE v.prompt_id = p.id)
|
|
5095
|
+
`).all();
|
|
5096
|
+
for (const p of missingHistory) {
|
|
5097
|
+
issues.push({
|
|
5098
|
+
type: "missing-version-history",
|
|
5099
|
+
severity: "warn",
|
|
5100
|
+
prompt_id: p.id,
|
|
5101
|
+
slug: p.slug,
|
|
5102
|
+
message: `Prompt "${p.slug}" has no version history entries`
|
|
5103
|
+
});
|
|
5104
|
+
}
|
|
5105
|
+
const slugs = db.query("SELECT id, slug FROM prompts").all();
|
|
5106
|
+
const seen = new Map;
|
|
5107
|
+
for (const { id, slug } of slugs) {
|
|
5108
|
+
const base = slug.replace(/-\d+$/, "");
|
|
5109
|
+
if (seen.has(base) && seen.get(base) !== id) {
|
|
5110
|
+
issues.push({
|
|
5111
|
+
type: "near-duplicate-slug",
|
|
5112
|
+
severity: "info",
|
|
5113
|
+
slug,
|
|
5114
|
+
message: `"${slug}" looks like a duplicate of "${base}" \u2014 consider merging`
|
|
5115
|
+
});
|
|
5116
|
+
} else {
|
|
5117
|
+
seen.set(base, id);
|
|
5118
|
+
}
|
|
5119
|
+
}
|
|
5120
|
+
const now = new Date().toISOString();
|
|
5121
|
+
const expired = db.query(`
|
|
5122
|
+
SELECT id, slug FROM prompts WHERE expires_at IS NOT NULL AND expires_at < ?
|
|
5123
|
+
`).all(now);
|
|
5124
|
+
for (const p of expired) {
|
|
5125
|
+
issues.push({
|
|
5126
|
+
type: "expired",
|
|
5127
|
+
severity: "warn",
|
|
5128
|
+
prompt_id: p.id,
|
|
5129
|
+
slug: p.slug,
|
|
5130
|
+
message: `Prompt "${p.slug}" has expired`
|
|
5131
|
+
});
|
|
5132
|
+
}
|
|
5133
|
+
const errors2 = issues.filter((i) => i.severity === "error").length;
|
|
5134
|
+
const warnings = issues.filter((i) => i.severity === "warn").length;
|
|
5135
|
+
const info = issues.filter((i) => i.severity === "info").length;
|
|
5136
|
+
return { issues, errors: errors2, warnings, info, checked_at: new Date().toISOString() };
|
|
5137
|
+
}
|
|
5138
|
+
|
|
4898
5139
|
// src/mcp/index.ts
|
|
4899
5140
|
var server = new McpServer({ name: "open-prompts", version: "0.1.0" });
|
|
4900
5141
|
function ok(data) {
|
|
@@ -5128,6 +5369,17 @@ server.registerTool("prompts_import", {
|
|
|
5128
5369
|
const results = importFromJson(prompts, changed_by);
|
|
5129
5370
|
return ok(results);
|
|
5130
5371
|
});
|
|
5372
|
+
server.registerTool("prompts_import_slash_commands", {
|
|
5373
|
+
description: "Auto-scan .claude/commands, .codex/skills, .gemini/extensions (both project and home dir) and import all .md files as prompts.",
|
|
5374
|
+
inputSchema: {
|
|
5375
|
+
dir: exports_external.string().optional().describe("Root directory to scan (default: cwd)"),
|
|
5376
|
+
changed_by: exports_external.string().optional()
|
|
5377
|
+
}
|
|
5378
|
+
}, async ({ dir, changed_by }) => {
|
|
5379
|
+
const rootDir = dir ?? process.cwd();
|
|
5380
|
+
const result = scanAndImportSlashCommands(rootDir, changed_by);
|
|
5381
|
+
return ok(result);
|
|
5382
|
+
});
|
|
5131
5383
|
server.registerTool("prompts_update", {
|
|
5132
5384
|
description: "Update an existing prompt's fields.",
|
|
5133
5385
|
inputSchema: {
|
|
@@ -5183,9 +5435,10 @@ server.registerTool("prompts_save_from_session", {
|
|
|
5183
5435
|
collection: exports_external.string().optional().describe("Collection to save into (default: 'sessions')"),
|
|
5184
5436
|
description: exports_external.string().optional().describe("One-line description of what this prompt does"),
|
|
5185
5437
|
agent: exports_external.string().optional().describe("Agent name saving this prompt"),
|
|
5186
|
-
project: exports_external.string().optional().describe("Project name, slug, or ID to scope this prompt to")
|
|
5438
|
+
project: exports_external.string().optional().describe("Project name, slug, or ID to scope this prompt to"),
|
|
5439
|
+
pin: exports_external.boolean().optional().describe("Pin the prompt immediately so it surfaces first in all lists")
|
|
5187
5440
|
}
|
|
5188
|
-
}, async ({ title, body, slug, tags, collection, description, agent, project }) => {
|
|
5441
|
+
}, async ({ title, body, slug, tags, collection, description, agent, project, pin }) => {
|
|
5189
5442
|
try {
|
|
5190
5443
|
let project_id;
|
|
5191
5444
|
if (project) {
|
|
@@ -5206,7 +5459,118 @@ server.registerTool("prompts_save_from_session", {
|
|
|
5206
5459
|
changed_by: agent,
|
|
5207
5460
|
project_id
|
|
5208
5461
|
});
|
|
5209
|
-
|
|
5462
|
+
if (pin)
|
|
5463
|
+
pinPrompt(prompt.id, true);
|
|
5464
|
+
const final = pin ? { ...prompt, pinned: true } : prompt;
|
|
5465
|
+
return ok({ ...final, _created: created, _tip: created ? `Saved as "${prompt.slug}". Use prompts_use("${prompt.slug}") to retrieve it.` : `Updated existing prompt "${prompt.slug}".` });
|
|
5466
|
+
} catch (e) {
|
|
5467
|
+
return err(e instanceof Error ? e.message : String(e));
|
|
5468
|
+
}
|
|
5469
|
+
});
|
|
5470
|
+
server.registerTool("prompts_audit", {
|
|
5471
|
+
description: "Run a full audit: orphaned project refs, empty collections, missing version history, near-duplicate slugs, expired prompts.",
|
|
5472
|
+
inputSchema: {}
|
|
5473
|
+
}, async () => ok(runAudit()));
|
|
5474
|
+
server.registerTool("prompts_unused", {
|
|
5475
|
+
description: "List prompts with use_count = 0 \u2014 never used. Good for library cleanup.",
|
|
5476
|
+
inputSchema: { collection: exports_external.string().optional(), limit: exports_external.number().optional().default(50) }
|
|
5477
|
+
}, async ({ collection, limit }) => {
|
|
5478
|
+
const all = listPrompts({ collection, limit: 1e4 });
|
|
5479
|
+
const unused = all.filter((p) => p.use_count === 0).slice(0, limit);
|
|
5480
|
+
return ok({ unused, count: unused.length });
|
|
5481
|
+
});
|
|
5482
|
+
server.registerTool("prompts_trending", {
|
|
5483
|
+
description: "Get most-used prompts in the last N days based on per-use log.",
|
|
5484
|
+
inputSchema: {
|
|
5485
|
+
days: exports_external.number().optional().default(7),
|
|
5486
|
+
limit: exports_external.number().optional().default(10)
|
|
5487
|
+
}
|
|
5488
|
+
}, async ({ days, limit }) => ok(getTrending(days, limit)));
|
|
5489
|
+
server.registerTool("prompts_set_expiry", {
|
|
5490
|
+
description: "Set or clear an expiry date on a prompt. Pass expires_at=null to clear.",
|
|
5491
|
+
inputSchema: {
|
|
5492
|
+
id: exports_external.string(),
|
|
5493
|
+
expires_at: exports_external.string().nullable().describe("ISO date string (e.g. 2026-12-31) or null to clear")
|
|
5494
|
+
}
|
|
5495
|
+
}, async ({ id, expires_at }) => {
|
|
5496
|
+
try {
|
|
5497
|
+
return ok(setExpiry(id, expires_at));
|
|
5498
|
+
} catch (e) {
|
|
5499
|
+
return err(e instanceof Error ? e.message : String(e));
|
|
5500
|
+
}
|
|
5501
|
+
});
|
|
5502
|
+
server.registerTool("prompts_duplicate", {
|
|
5503
|
+
description: "Clone a prompt with a new slug. Copies body, tags, collection, description. Version resets to 1.",
|
|
5504
|
+
inputSchema: {
|
|
5505
|
+
id: exports_external.string(),
|
|
5506
|
+
slug: exports_external.string().optional().describe("New slug (auto-generated if omitted)"),
|
|
5507
|
+
title: exports_external.string().optional().describe("New title (defaults to 'Copy of <original>')")
|
|
5508
|
+
}
|
|
5509
|
+
}, async ({ id, slug, title }) => {
|
|
5510
|
+
try {
|
|
5511
|
+
const source = getPrompt(id);
|
|
5512
|
+
if (!source)
|
|
5513
|
+
return err(`Prompt not found: ${id}`);
|
|
5514
|
+
const { prompt } = upsertPrompt({
|
|
5515
|
+
title: title ?? `Copy of ${source.title}`,
|
|
5516
|
+
slug,
|
|
5517
|
+
body: source.body,
|
|
5518
|
+
description: source.description ?? undefined,
|
|
5519
|
+
collection: source.collection,
|
|
5520
|
+
tags: source.tags,
|
|
5521
|
+
source: "manual"
|
|
5522
|
+
});
|
|
5523
|
+
return ok(prompt);
|
|
5524
|
+
} catch (e) {
|
|
5525
|
+
return err(e instanceof Error ? e.message : String(e));
|
|
5526
|
+
}
|
|
5527
|
+
});
|
|
5528
|
+
server.registerTool("prompts_diff", {
|
|
5529
|
+
description: "Show a line diff between two versions of a prompt body. v2 defaults to current version.",
|
|
5530
|
+
inputSchema: {
|
|
5531
|
+
id: exports_external.string(),
|
|
5532
|
+
v1: exports_external.number().describe("First version number"),
|
|
5533
|
+
v2: exports_external.number().optional().describe("Second version (default: current)")
|
|
5534
|
+
}
|
|
5535
|
+
}, async ({ id, v1, v2 }) => {
|
|
5536
|
+
try {
|
|
5537
|
+
const prompt = getPrompt(id);
|
|
5538
|
+
if (!prompt)
|
|
5539
|
+
return err(`Prompt not found: ${id}`);
|
|
5540
|
+
const versions = listVersions(prompt.id);
|
|
5541
|
+
const versionA = versions.find((v) => v.version === v1);
|
|
5542
|
+
if (!versionA)
|
|
5543
|
+
return err(`Version ${v1} not found`);
|
|
5544
|
+
const bodyB = v2 ? versions.find((v) => v.version === v2)?.body ?? null : prompt.body;
|
|
5545
|
+
if (bodyB === null)
|
|
5546
|
+
return err(`Version ${v2} not found`);
|
|
5547
|
+
const lines = diffTexts(versionA.body, bodyB);
|
|
5548
|
+
return ok({ lines, formatted: formatDiff(lines), v1, v2: v2 ?? prompt.version });
|
|
5549
|
+
} catch (e) {
|
|
5550
|
+
return err(e instanceof Error ? e.message : String(e));
|
|
5551
|
+
}
|
|
5552
|
+
});
|
|
5553
|
+
server.registerTool("prompts_chain", {
|
|
5554
|
+
description: "Set or get the next prompt in a chain. After using prompt A, the agent is suggested prompt B. Pass next_prompt=null to clear.",
|
|
5555
|
+
inputSchema: {
|
|
5556
|
+
id: exports_external.string().describe("Prompt ID or slug"),
|
|
5557
|
+
next_prompt: exports_external.string().nullable().optional().describe("Slug of the next prompt in the chain, or null to clear")
|
|
5558
|
+
}
|
|
5559
|
+
}, async ({ id, next_prompt }) => {
|
|
5560
|
+
try {
|
|
5561
|
+
if (next_prompt !== undefined) {
|
|
5562
|
+
const p = setNextPrompt(id, next_prompt ?? null);
|
|
5563
|
+
return ok(p);
|
|
5564
|
+
}
|
|
5565
|
+
const chain = [];
|
|
5566
|
+
let cur = getPrompt(id);
|
|
5567
|
+
const seen = new Set;
|
|
5568
|
+
while (cur && !seen.has(cur.id)) {
|
|
5569
|
+
chain.push({ id: cur.id, slug: cur.slug, title: cur.title });
|
|
5570
|
+
seen.add(cur.id);
|
|
5571
|
+
cur = cur.next_prompt ? getPrompt(cur.next_prompt) : null;
|
|
5572
|
+
}
|
|
5573
|
+
return ok(chain);
|
|
5210
5574
|
} catch (e) {
|
|
5211
5575
|
return err(e instanceof Error ? e.message : String(e));
|
|
5212
5576
|
}
|