@hasna/prompts 0.2.1 → 0.2.2
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 +191 -7
- package/dist/db/database.d.ts +1 -0
- package/dist/db/database.d.ts.map +1 -1
- package/dist/db/projects.d.ts +10 -0
- package/dist/db/projects.d.ts.map +1 -0
- package/dist/db/prompts.d.ts.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +106 -3
- package/dist/lib/search.d.ts.map +1 -1
- package/dist/mcp/index.js +188 -14
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +147 -4
- package/dist/types/index.d.ts +15 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -2187,6 +2187,21 @@ function runMigrations(db) {
|
|
|
2187
2187
|
name: "003_pinned",
|
|
2188
2188
|
sql: `ALTER TABLE prompts ADD COLUMN pinned INTEGER NOT NULL DEFAULT 0;`
|
|
2189
2189
|
},
|
|
2190
|
+
{
|
|
2191
|
+
name: "004_projects",
|
|
2192
|
+
sql: `
|
|
2193
|
+
CREATE TABLE IF NOT EXISTS projects (
|
|
2194
|
+
id TEXT PRIMARY KEY,
|
|
2195
|
+
name TEXT NOT NULL UNIQUE,
|
|
2196
|
+
slug TEXT NOT NULL UNIQUE,
|
|
2197
|
+
description TEXT,
|
|
2198
|
+
path TEXT,
|
|
2199
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2200
|
+
);
|
|
2201
|
+
ALTER TABLE prompts ADD COLUMN project_id TEXT REFERENCES projects(id) ON DELETE SET NULL;
|
|
2202
|
+
CREATE INDEX IF NOT EXISTS idx_prompts_project_id ON prompts(project_id);
|
|
2203
|
+
`
|
|
2204
|
+
},
|
|
2190
2205
|
{
|
|
2191
2206
|
name: "002_fts5",
|
|
2192
2207
|
sql: `
|
|
@@ -2227,6 +2242,24 @@ function runMigrations(db) {
|
|
|
2227
2242
|
db.run("INSERT INTO _migrations (name) VALUES (?)", [migration.name]);
|
|
2228
2243
|
}
|
|
2229
2244
|
}
|
|
2245
|
+
function resolveProject(db, idOrSlug) {
|
|
2246
|
+
const byId = db.query("SELECT id FROM projects WHERE id = ?").get(idOrSlug);
|
|
2247
|
+
if (byId)
|
|
2248
|
+
return byId.id;
|
|
2249
|
+
const bySlug = db.query("SELECT id FROM projects WHERE slug = ?").get(idOrSlug);
|
|
2250
|
+
if (bySlug)
|
|
2251
|
+
return bySlug.id;
|
|
2252
|
+
const byName = db.query("SELECT id FROM projects WHERE lower(name) = ?").get(idOrSlug.toLowerCase());
|
|
2253
|
+
if (byName)
|
|
2254
|
+
return byName.id;
|
|
2255
|
+
const byPrefix = db.query("SELECT id FROM projects WHERE id LIKE ? LIMIT 2").all(`${idOrSlug}%`);
|
|
2256
|
+
if (byPrefix.length === 1 && byPrefix[0])
|
|
2257
|
+
return byPrefix[0].id;
|
|
2258
|
+
const bySlugPrefix = db.query("SELECT id FROM projects WHERE slug LIKE ? LIMIT 2").all(`${idOrSlug}%`);
|
|
2259
|
+
if (bySlugPrefix.length === 1 && bySlugPrefix[0])
|
|
2260
|
+
return bySlugPrefix[0].id;
|
|
2261
|
+
return null;
|
|
2262
|
+
}
|
|
2230
2263
|
function hasFts(db) {
|
|
2231
2264
|
return db.query("SELECT 1 FROM sqlite_master WHERE type='table' AND name='prompts_fts'").get() !== null;
|
|
2232
2265
|
}
|
|
@@ -2435,6 +2468,12 @@ class DuplicateSlugError extends Error {
|
|
|
2435
2468
|
this.name = "DuplicateSlugError";
|
|
2436
2469
|
}
|
|
2437
2470
|
}
|
|
2471
|
+
class ProjectNotFoundError extends Error {
|
|
2472
|
+
constructor(id) {
|
|
2473
|
+
super(`Project not found: ${id}`);
|
|
2474
|
+
this.name = "ProjectNotFoundError";
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2438
2477
|
|
|
2439
2478
|
// src/db/prompts.ts
|
|
2440
2479
|
function rowToPrompt(row) {
|
|
@@ -2449,6 +2488,7 @@ function rowToPrompt(row) {
|
|
|
2449
2488
|
tags: JSON.parse(row["tags"] || "[]"),
|
|
2450
2489
|
variables: JSON.parse(row["variables"] || "[]"),
|
|
2451
2490
|
pinned: Boolean(row["pinned"]),
|
|
2491
|
+
project_id: row["project_id"] ?? null,
|
|
2452
2492
|
is_template: Boolean(row["is_template"]),
|
|
2453
2493
|
source: row["source"],
|
|
2454
2494
|
version: row["version"],
|
|
@@ -2472,11 +2512,12 @@ function createPrompt(input) {
|
|
|
2472
2512
|
ensureCollection(collection);
|
|
2473
2513
|
const tags = JSON.stringify(input.tags || []);
|
|
2474
2514
|
const source = input.source || "manual";
|
|
2515
|
+
const project_id = input.project_id ?? null;
|
|
2475
2516
|
const vars = extractVariables(input.body);
|
|
2476
2517
|
const variables = JSON.stringify(vars.map((v) => ({ name: v, required: true })));
|
|
2477
2518
|
const is_template = vars.length > 0 ? 1 : 0;
|
|
2478
|
-
db.run(`INSERT INTO prompts (id, name, slug, title, body, description, collection, tags, variables, is_template, source)
|
|
2479
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [id, name, slug, input.title, input.body, input.description ?? null, collection, tags, variables, is_template, source]);
|
|
2519
|
+
db.run(`INSERT INTO prompts (id, name, slug, title, body, description, collection, tags, variables, is_template, source, project_id)
|
|
2520
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [id, name, slug, input.title, input.body, input.description ?? null, collection, tags, variables, is_template, source, project_id]);
|
|
2480
2521
|
db.run(`INSERT INTO prompt_versions (id, prompt_id, body, version, changed_by)
|
|
2481
2522
|
VALUES (?, ?, ?, 1, ?)`, [generateId("VER"), id, input.body, input.changed_by ?? null]);
|
|
2482
2523
|
return getPrompt(id);
|
|
@@ -2520,10 +2561,16 @@ function listPrompts(filter = {}) {
|
|
|
2520
2561
|
params.push(`%"${tag}"%`);
|
|
2521
2562
|
}
|
|
2522
2563
|
}
|
|
2564
|
+
let orderBy = "pinned DESC, use_count DESC, updated_at DESC";
|
|
2565
|
+
if (filter.project_id !== undefined && filter.project_id !== null) {
|
|
2566
|
+
conditions.push("(project_id = ? OR project_id IS NULL)");
|
|
2567
|
+
params.push(filter.project_id);
|
|
2568
|
+
orderBy = `(CASE WHEN project_id = '${filter.project_id}' THEN 0 ELSE 1 END), pinned DESC, use_count DESC, updated_at DESC`;
|
|
2569
|
+
}
|
|
2523
2570
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
2524
2571
|
const limit = filter.limit ?? 100;
|
|
2525
2572
|
const offset = filter.offset ?? 0;
|
|
2526
|
-
const rows = db.query(`SELECT * FROM prompts ${where} ORDER BY
|
|
2573
|
+
const rows = db.query(`SELECT * FROM prompts ${where} ORDER BY ${orderBy} LIMIT ? OFFSET ?`).all(...params, limit, offset);
|
|
2527
2574
|
return rows.map(rowToPrompt);
|
|
2528
2575
|
}
|
|
2529
2576
|
function updatePrompt(idOrSlug, input) {
|
|
@@ -2656,6 +2703,52 @@ function restoreVersion(promptId, version, changedBy) {
|
|
|
2656
2703
|
VALUES (?, ?, ?, ?, ?)`, [generateId("VER"), promptId, ver.body, newVersion, changedBy ?? null]);
|
|
2657
2704
|
}
|
|
2658
2705
|
|
|
2706
|
+
// src/db/projects.ts
|
|
2707
|
+
function rowToProject(row, promptCount) {
|
|
2708
|
+
return {
|
|
2709
|
+
id: row["id"],
|
|
2710
|
+
name: row["name"],
|
|
2711
|
+
slug: row["slug"],
|
|
2712
|
+
description: row["description"] ?? null,
|
|
2713
|
+
path: row["path"] ?? null,
|
|
2714
|
+
prompt_count: promptCount,
|
|
2715
|
+
created_at: row["created_at"]
|
|
2716
|
+
};
|
|
2717
|
+
}
|
|
2718
|
+
function createProject(input) {
|
|
2719
|
+
const db = getDatabase();
|
|
2720
|
+
const id = generateId("proj");
|
|
2721
|
+
const slug = generateSlug(input.name);
|
|
2722
|
+
db.run(`INSERT INTO projects (id, name, slug, description, path) VALUES (?, ?, ?, ?, ?)`, [id, input.name, slug, input.description ?? null, input.path ?? null]);
|
|
2723
|
+
return getProject(id);
|
|
2724
|
+
}
|
|
2725
|
+
function getProject(idOrSlug) {
|
|
2726
|
+
const db = getDatabase();
|
|
2727
|
+
const id = resolveProject(db, idOrSlug);
|
|
2728
|
+
if (!id)
|
|
2729
|
+
return null;
|
|
2730
|
+
const row = db.query("SELECT * FROM projects WHERE id = ?").get(id);
|
|
2731
|
+
if (!row)
|
|
2732
|
+
return null;
|
|
2733
|
+
const countRow = db.query("SELECT COUNT(*) as n FROM prompts WHERE project_id = ?").get(id);
|
|
2734
|
+
return rowToProject(row, countRow.n);
|
|
2735
|
+
}
|
|
2736
|
+
function listProjects() {
|
|
2737
|
+
const db = getDatabase();
|
|
2738
|
+
const rows = db.query("SELECT * FROM projects ORDER BY name ASC").all();
|
|
2739
|
+
return rows.map((row) => {
|
|
2740
|
+
const countRow = db.query("SELECT COUNT(*) as n FROM prompts WHERE project_id = ?").get(row["id"]);
|
|
2741
|
+
return rowToProject(row, countRow.n);
|
|
2742
|
+
});
|
|
2743
|
+
}
|
|
2744
|
+
function deleteProject(idOrSlug) {
|
|
2745
|
+
const db = getDatabase();
|
|
2746
|
+
const id = resolveProject(db, idOrSlug);
|
|
2747
|
+
if (!id)
|
|
2748
|
+
throw new ProjectNotFoundError(idOrSlug);
|
|
2749
|
+
db.run("DELETE FROM projects WHERE id = ?", [id]);
|
|
2750
|
+
}
|
|
2751
|
+
|
|
2659
2752
|
// src/lib/search.ts
|
|
2660
2753
|
function rowToSearchResult(row, snippet) {
|
|
2661
2754
|
return {
|
|
@@ -2670,6 +2763,7 @@ function rowToSearchResult(row, snippet) {
|
|
|
2670
2763
|
tags: JSON.parse(row["tags"] || "[]"),
|
|
2671
2764
|
variables: JSON.parse(row["variables"] || "[]"),
|
|
2672
2765
|
pinned: Boolean(row["pinned"]),
|
|
2766
|
+
project_id: row["project_id"] ?? null,
|
|
2673
2767
|
is_template: Boolean(row["is_template"]),
|
|
2674
2768
|
source: row["source"],
|
|
2675
2769
|
version: row["version"],
|
|
@@ -2713,6 +2807,10 @@ function searchPrompts(query, filter = {}) {
|
|
|
2713
2807
|
for (const tag of filter.tags)
|
|
2714
2808
|
params.push(`%"${tag}"%`);
|
|
2715
2809
|
}
|
|
2810
|
+
if (filter.project_id !== undefined && filter.project_id !== null) {
|
|
2811
|
+
conditions.push("(p.project_id = ? OR p.project_id IS NULL)");
|
|
2812
|
+
params.push(filter.project_id);
|
|
2813
|
+
}
|
|
2716
2814
|
const where = conditions.length > 0 ? `AND ${conditions.join(" AND ")}` : "";
|
|
2717
2815
|
const limit = filter.limit ?? 50;
|
|
2718
2816
|
const offset = filter.offset ?? 0;
|
|
@@ -2806,10 +2904,17 @@ function lintAll(prompts) {
|
|
|
2806
2904
|
// src/cli/index.tsx
|
|
2807
2905
|
var require2 = createRequire(import.meta.url);
|
|
2808
2906
|
var pkg = require2("../../package.json");
|
|
2809
|
-
var program2 = new Command().name("prompts").version(pkg.version).description("Reusable prompt library \u2014 save, search, render prompts from any AI session").option("--json", "Output as JSON");
|
|
2907
|
+
var program2 = new Command().name("prompts").version(pkg.version).description("Reusable prompt library \u2014 save, search, render prompts from any AI session").option("--json", "Output as JSON").option("--project <name>", "Active project (name, slug, or ID) for scoped operations");
|
|
2810
2908
|
function isJson() {
|
|
2811
2909
|
return Boolean(program2.opts()["json"]);
|
|
2812
2910
|
}
|
|
2911
|
+
function getActiveProjectId() {
|
|
2912
|
+
const projectName = program2.opts()["project"] ?? process.env["PROMPTS_PROJECT"];
|
|
2913
|
+
if (!projectName)
|
|
2914
|
+
return null;
|
|
2915
|
+
const db = getDatabase();
|
|
2916
|
+
return resolveProject(db, projectName);
|
|
2917
|
+
}
|
|
2813
2918
|
function output(data) {
|
|
2814
2919
|
if (isJson()) {
|
|
2815
2920
|
console.log(JSON.stringify(data, null, 2));
|
|
@@ -2846,6 +2951,7 @@ program2.command("save <title>").description("Save a new prompt (or update exist
|
|
|
2846
2951
|
}
|
|
2847
2952
|
if (!body)
|
|
2848
2953
|
handleError("No body provided. Use --body, --file, or pipe via stdin.");
|
|
2954
|
+
const project_id = getActiveProjectId();
|
|
2849
2955
|
const { prompt, created, duplicate_warning } = upsertPrompt({
|
|
2850
2956
|
title,
|
|
2851
2957
|
body,
|
|
@@ -2854,7 +2960,8 @@ program2.command("save <title>").description("Save a new prompt (or update exist
|
|
|
2854
2960
|
collection: opts["collection"],
|
|
2855
2961
|
tags: opts["tags"] ? opts["tags"].split(",").map((t) => t.trim()) : [],
|
|
2856
2962
|
source: opts["source"] || "manual",
|
|
2857
|
-
changed_by: opts["agent"]
|
|
2963
|
+
changed_by: opts["agent"],
|
|
2964
|
+
project_id
|
|
2858
2965
|
}, Boolean(opts["force"]));
|
|
2859
2966
|
if (duplicate_warning && !isJson()) {
|
|
2860
2967
|
console.warn(chalk.yellow(`Warning: ${duplicate_warning}`));
|
|
@@ -2899,11 +3006,13 @@ program2.command("get <id>").description("Get prompt details without incrementin
|
|
|
2899
3006
|
});
|
|
2900
3007
|
program2.command("list").description("List prompts").option("-c, --collection <name>", "Filter by collection").option("-t, --tags <tags>", "Filter by tags (comma-separated)").option("--templates", "Show only templates").option("--recent", "Sort by recently used").option("-n, --limit <n>", "Max results", "50").action((opts) => {
|
|
2901
3008
|
try {
|
|
3009
|
+
const project_id = getActiveProjectId();
|
|
2902
3010
|
let prompts = listPrompts({
|
|
2903
3011
|
collection: opts["collection"],
|
|
2904
3012
|
tags: opts["tags"] ? opts["tags"].split(",").map((t) => t.trim()) : undefined,
|
|
2905
3013
|
is_template: opts["templates"] ? true : undefined,
|
|
2906
|
-
limit: parseInt(opts["limit"]) || 50
|
|
3014
|
+
limit: parseInt(opts["limit"]) || 50,
|
|
3015
|
+
...project_id !== null ? { project_id } : {}
|
|
2907
3016
|
});
|
|
2908
3017
|
if (opts["recent"]) {
|
|
2909
3018
|
prompts = prompts.filter((p) => p.last_used_at !== null).sort((a, b) => (b.last_used_at ?? "").localeCompare(a.last_used_at ?? ""));
|
|
@@ -2924,10 +3033,12 @@ ${prompts.length} prompt(s)`));
|
|
|
2924
3033
|
});
|
|
2925
3034
|
program2.command("search <query>").description("Full-text search across prompts (FTS5)").option("-c, --collection <name>").option("-t, --tags <tags>").option("-n, --limit <n>", "Max results", "20").action((query, opts) => {
|
|
2926
3035
|
try {
|
|
3036
|
+
const project_id = getActiveProjectId();
|
|
2927
3037
|
const results = searchPrompts(query, {
|
|
2928
3038
|
collection: opts["collection"],
|
|
2929
3039
|
tags: opts["tags"] ? opts["tags"].split(",").map((t) => t.trim()) : undefined,
|
|
2930
|
-
limit: parseInt(opts["limit"] ?? "20") || 20
|
|
3040
|
+
limit: parseInt(opts["limit"] ?? "20") || 20,
|
|
3041
|
+
...project_id !== null ? { project_id } : {}
|
|
2931
3042
|
});
|
|
2932
3043
|
if (isJson()) {
|
|
2933
3044
|
output(results);
|
|
@@ -3316,4 +3427,77 @@ program2.command("copy <id>").description("Copy prompt body to clipboard and inc
|
|
|
3316
3427
|
handleError(e);
|
|
3317
3428
|
}
|
|
3318
3429
|
});
|
|
3430
|
+
var projectCmd = program2.command("project").description("Manage projects");
|
|
3431
|
+
projectCmd.command("create <name>").description("Create a new project").option("-d, --description <desc>", "Short description").option("--path <path>", "Filesystem path this project maps to").action((name, opts) => {
|
|
3432
|
+
try {
|
|
3433
|
+
const project = createProject({ name, description: opts["description"], path: opts["path"] });
|
|
3434
|
+
if (isJson())
|
|
3435
|
+
output(project);
|
|
3436
|
+
else {
|
|
3437
|
+
console.log(`${chalk.green("Created")} project ${chalk.bold(project.name)} \u2014 ${chalk.gray(project.slug)}`);
|
|
3438
|
+
if (project.description)
|
|
3439
|
+
console.log(chalk.gray(` ${project.description}`));
|
|
3440
|
+
}
|
|
3441
|
+
} catch (e) {
|
|
3442
|
+
handleError(e);
|
|
3443
|
+
}
|
|
3444
|
+
});
|
|
3445
|
+
projectCmd.command("list").description("List all projects").action(() => {
|
|
3446
|
+
try {
|
|
3447
|
+
const projects = listProjects();
|
|
3448
|
+
if (isJson()) {
|
|
3449
|
+
output(projects);
|
|
3450
|
+
return;
|
|
3451
|
+
}
|
|
3452
|
+
if (projects.length === 0) {
|
|
3453
|
+
console.log(chalk.gray("No projects."));
|
|
3454
|
+
return;
|
|
3455
|
+
}
|
|
3456
|
+
for (const p of projects) {
|
|
3457
|
+
console.log(`${chalk.bold(p.name)} ${chalk.gray(p.slug)} ${chalk.cyan(`${p.prompt_count} prompt(s)`)}`);
|
|
3458
|
+
if (p.description)
|
|
3459
|
+
console.log(chalk.gray(` ${p.description}`));
|
|
3460
|
+
}
|
|
3461
|
+
} catch (e) {
|
|
3462
|
+
handleError(e);
|
|
3463
|
+
}
|
|
3464
|
+
});
|
|
3465
|
+
projectCmd.command("get <id>").description("Get project details").action((id) => {
|
|
3466
|
+
try {
|
|
3467
|
+
const project = getProject(id);
|
|
3468
|
+
if (!project)
|
|
3469
|
+
handleError(`Project not found: ${id}`);
|
|
3470
|
+
output(isJson() ? project : `${chalk.bold(project.name)} ${chalk.gray(project.slug)} ${chalk.cyan(`${project.prompt_count} prompt(s)`)}`);
|
|
3471
|
+
} catch (e) {
|
|
3472
|
+
handleError(e);
|
|
3473
|
+
}
|
|
3474
|
+
});
|
|
3475
|
+
projectCmd.command("delete <id>").description("Delete a project (prompts become global)").option("-y, --yes", "Skip confirmation").action(async (id, opts) => {
|
|
3476
|
+
try {
|
|
3477
|
+
const project = getProject(id);
|
|
3478
|
+
if (!project)
|
|
3479
|
+
handleError(`Project not found: ${id}`);
|
|
3480
|
+
if (!opts.yes && !isJson()) {
|
|
3481
|
+
const { createInterface } = await import("readline");
|
|
3482
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
3483
|
+
await new Promise((resolve) => {
|
|
3484
|
+
rl.question(chalk.yellow(`Delete project "${project.name}"? Prompts will become global. [y/N] `), (ans) => {
|
|
3485
|
+
rl.close();
|
|
3486
|
+
if (ans.toLowerCase() !== "y") {
|
|
3487
|
+
console.log("Cancelled.");
|
|
3488
|
+
process.exit(0);
|
|
3489
|
+
}
|
|
3490
|
+
resolve();
|
|
3491
|
+
});
|
|
3492
|
+
});
|
|
3493
|
+
}
|
|
3494
|
+
deleteProject(id);
|
|
3495
|
+
if (isJson())
|
|
3496
|
+
output({ deleted: true, id: project.id });
|
|
3497
|
+
else
|
|
3498
|
+
console.log(chalk.red(`Deleted project ${project.name}`));
|
|
3499
|
+
} catch (e) {
|
|
3500
|
+
handleError(e);
|
|
3501
|
+
}
|
|
3502
|
+
});
|
|
3319
3503
|
program2.parse();
|
package/dist/db/database.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export declare function getDbPath(): string;
|
|
|
3
3
|
export declare function getDatabase(): Database;
|
|
4
4
|
export declare function closeDatabase(): void;
|
|
5
5
|
export declare function resetDatabase(): void;
|
|
6
|
+
export declare function resolveProject(db: Database, idOrSlug: string): string | null;
|
|
6
7
|
export declare function hasFts(db: Database): boolean;
|
|
7
8
|
export declare function resolvePrompt(db: Database, idOrSlug: string): string | null;
|
|
8
9
|
//# sourceMappingURL=database.d.ts.map
|
|
@@ -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;AAqID,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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Project } from "../types/index.js";
|
|
2
|
+
export declare function createProject(input: {
|
|
3
|
+
name: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
path?: string;
|
|
6
|
+
}): Project;
|
|
7
|
+
export declare function getProject(idOrSlug: string): Project | null;
|
|
8
|
+
export declare function listProjects(): Project[];
|
|
9
|
+
export declare function deleteProject(idOrSlug: string): void;
|
|
10
|
+
//# sourceMappingURL=projects.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../src/db/projects.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAehD,wBAAgB,aAAa,CAAC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAWnG;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAU3D;AAED,wBAAgB,YAAY,IAAI,OAAO,EAAE,CAOxC;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAKpD"}
|
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;AA2B1B,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,CA8C/E;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAInD;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAQlD;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
|
@@ -3,6 +3,7 @@ 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";
|
|
5
5
|
export { getDatabase, getDbPath } from "./db/database.js";
|
|
6
|
+
export { createProject, getProject, listProjects, deleteProject } from "./db/projects.js";
|
|
6
7
|
export { searchPrompts, findSimilar } from "./lib/search.js";
|
|
7
8
|
export { extractVariables, extractVariableInfo, renderTemplate, validateVars } from "./lib/template.js";
|
|
8
9
|
export type { VariableInfo } from "./lib/template.js";
|
|
@@ -10,6 +11,6 @@ export { importFromJson, exportToJson } from "./lib/importer.js";
|
|
|
10
11
|
export { findDuplicates } from "./lib/duplicates.js";
|
|
11
12
|
export type { DuplicateMatch } from "./lib/duplicates.js";
|
|
12
13
|
export { generateSlug, uniqueSlug, generatePromptId } from "./lib/ids.js";
|
|
13
|
-
export type { Prompt, PromptVersion, Collection, Agent, TemplateVariable, PromptSource, CreatePromptInput, UpdatePromptInput, ListPromptsFilter, SearchResult, RenderResult, PromptStats, } from "./types/index.js";
|
|
14
|
-
export { PromptNotFoundError, VersionConflictError, DuplicateSlugError, TemplateRenderError, } from "./types/index.js";
|
|
14
|
+
export type { Prompt, PromptVersion, Collection, Agent, Project, TemplateVariable, PromptSource, CreatePromptInput, UpdatePromptInput, ListPromptsFilter, SearchResult, RenderResult, PromptStats, } from "./types/index.js";
|
|
15
|
+
export { PromptNotFoundError, VersionConflictError, DuplicateSlugError, TemplateRenderError, ProjectNotFoundError, } from "./types/index.js";
|
|
15
16
|
//# sourceMappingURL=index.d.ts.map
|
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;AACrK,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;
|
|
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;AACrK,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
|
@@ -113,6 +113,21 @@ function runMigrations(db) {
|
|
|
113
113
|
name: "003_pinned",
|
|
114
114
|
sql: `ALTER TABLE prompts ADD COLUMN pinned INTEGER NOT NULL DEFAULT 0;`
|
|
115
115
|
},
|
|
116
|
+
{
|
|
117
|
+
name: "004_projects",
|
|
118
|
+
sql: `
|
|
119
|
+
CREATE TABLE IF NOT EXISTS projects (
|
|
120
|
+
id TEXT PRIMARY KEY,
|
|
121
|
+
name TEXT NOT NULL UNIQUE,
|
|
122
|
+
slug TEXT NOT NULL UNIQUE,
|
|
123
|
+
description TEXT,
|
|
124
|
+
path TEXT,
|
|
125
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
126
|
+
);
|
|
127
|
+
ALTER TABLE prompts ADD COLUMN project_id TEXT REFERENCES projects(id) ON DELETE SET NULL;
|
|
128
|
+
CREATE INDEX IF NOT EXISTS idx_prompts_project_id ON prompts(project_id);
|
|
129
|
+
`
|
|
130
|
+
},
|
|
116
131
|
{
|
|
117
132
|
name: "002_fts5",
|
|
118
133
|
sql: `
|
|
@@ -153,6 +168,24 @@ function runMigrations(db) {
|
|
|
153
168
|
db.run("INSERT INTO _migrations (name) VALUES (?)", [migration.name]);
|
|
154
169
|
}
|
|
155
170
|
}
|
|
171
|
+
function resolveProject(db, idOrSlug) {
|
|
172
|
+
const byId = db.query("SELECT id FROM projects WHERE id = ?").get(idOrSlug);
|
|
173
|
+
if (byId)
|
|
174
|
+
return byId.id;
|
|
175
|
+
const bySlug = db.query("SELECT id FROM projects WHERE slug = ?").get(idOrSlug);
|
|
176
|
+
if (bySlug)
|
|
177
|
+
return bySlug.id;
|
|
178
|
+
const byName = db.query("SELECT id FROM projects WHERE lower(name) = ?").get(idOrSlug.toLowerCase());
|
|
179
|
+
if (byName)
|
|
180
|
+
return byName.id;
|
|
181
|
+
const byPrefix = db.query("SELECT id FROM projects WHERE id LIKE ? LIMIT 2").all(`${idOrSlug}%`);
|
|
182
|
+
if (byPrefix.length === 1 && byPrefix[0])
|
|
183
|
+
return byPrefix[0].id;
|
|
184
|
+
const bySlugPrefix = db.query("SELECT id FROM projects WHERE slug LIKE ? LIMIT 2").all(`${idOrSlug}%`);
|
|
185
|
+
if (bySlugPrefix.length === 1 && bySlugPrefix[0])
|
|
186
|
+
return bySlugPrefix[0].id;
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
156
189
|
function hasFts(db) {
|
|
157
190
|
return db.query("SELECT 1 FROM sqlite_master WHERE type='table' AND name='prompts_fts'").get() !== null;
|
|
158
191
|
}
|
|
@@ -378,6 +411,13 @@ class TemplateRenderError extends Error {
|
|
|
378
411
|
}
|
|
379
412
|
}
|
|
380
413
|
|
|
414
|
+
class ProjectNotFoundError extends Error {
|
|
415
|
+
constructor(id) {
|
|
416
|
+
super(`Project not found: ${id}`);
|
|
417
|
+
this.name = "ProjectNotFoundError";
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
381
421
|
// src/db/prompts.ts
|
|
382
422
|
function rowToPrompt(row) {
|
|
383
423
|
return {
|
|
@@ -391,6 +431,7 @@ function rowToPrompt(row) {
|
|
|
391
431
|
tags: JSON.parse(row["tags"] || "[]"),
|
|
392
432
|
variables: JSON.parse(row["variables"] || "[]"),
|
|
393
433
|
pinned: Boolean(row["pinned"]),
|
|
434
|
+
project_id: row["project_id"] ?? null,
|
|
394
435
|
is_template: Boolean(row["is_template"]),
|
|
395
436
|
source: row["source"],
|
|
396
437
|
version: row["version"],
|
|
@@ -414,11 +455,12 @@ function createPrompt(input) {
|
|
|
414
455
|
ensureCollection(collection);
|
|
415
456
|
const tags = JSON.stringify(input.tags || []);
|
|
416
457
|
const source = input.source || "manual";
|
|
458
|
+
const project_id = input.project_id ?? null;
|
|
417
459
|
const vars = extractVariables(input.body);
|
|
418
460
|
const variables = JSON.stringify(vars.map((v) => ({ name: v, required: true })));
|
|
419
461
|
const is_template = vars.length > 0 ? 1 : 0;
|
|
420
|
-
db.run(`INSERT INTO prompts (id, name, slug, title, body, description, collection, tags, variables, is_template, source)
|
|
421
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [id, name, slug, input.title, input.body, input.description ?? null, collection, tags, variables, is_template, source]);
|
|
462
|
+
db.run(`INSERT INTO prompts (id, name, slug, title, body, description, collection, tags, variables, is_template, source, project_id)
|
|
463
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [id, name, slug, input.title, input.body, input.description ?? null, collection, tags, variables, is_template, source, project_id]);
|
|
422
464
|
db.run(`INSERT INTO prompt_versions (id, prompt_id, body, version, changed_by)
|
|
423
465
|
VALUES (?, ?, ?, 1, ?)`, [generateId("VER"), id, input.body, input.changed_by ?? null]);
|
|
424
466
|
return getPrompt(id);
|
|
@@ -462,10 +504,16 @@ function listPrompts(filter = {}) {
|
|
|
462
504
|
params.push(`%"${tag}"%`);
|
|
463
505
|
}
|
|
464
506
|
}
|
|
507
|
+
let orderBy = "pinned DESC, use_count DESC, updated_at DESC";
|
|
508
|
+
if (filter.project_id !== undefined && filter.project_id !== null) {
|
|
509
|
+
conditions.push("(project_id = ? OR project_id IS NULL)");
|
|
510
|
+
params.push(filter.project_id);
|
|
511
|
+
orderBy = `(CASE WHEN project_id = '${filter.project_id}' THEN 0 ELSE 1 END), pinned DESC, use_count DESC, updated_at DESC`;
|
|
512
|
+
}
|
|
465
513
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
466
514
|
const limit = filter.limit ?? 100;
|
|
467
515
|
const offset = filter.offset ?? 0;
|
|
468
|
-
const rows = db.query(`SELECT * FROM prompts ${where} ORDER BY
|
|
516
|
+
const rows = db.query(`SELECT * FROM prompts ${where} ORDER BY ${orderBy} LIMIT ? OFFSET ?`).all(...params, limit, offset);
|
|
469
517
|
return rows.map(rowToPrompt);
|
|
470
518
|
}
|
|
471
519
|
function updatePrompt(idOrSlug, input) {
|
|
@@ -625,6 +673,51 @@ function listAgents() {
|
|
|
625
673
|
const rows = db.query("SELECT * FROM agents ORDER BY last_seen_at DESC").all();
|
|
626
674
|
return rows.map(rowToAgent);
|
|
627
675
|
}
|
|
676
|
+
// src/db/projects.ts
|
|
677
|
+
function rowToProject(row, promptCount) {
|
|
678
|
+
return {
|
|
679
|
+
id: row["id"],
|
|
680
|
+
name: row["name"],
|
|
681
|
+
slug: row["slug"],
|
|
682
|
+
description: row["description"] ?? null,
|
|
683
|
+
path: row["path"] ?? null,
|
|
684
|
+
prompt_count: promptCount,
|
|
685
|
+
created_at: row["created_at"]
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
function createProject(input) {
|
|
689
|
+
const db = getDatabase();
|
|
690
|
+
const id = generateId("proj");
|
|
691
|
+
const slug = generateSlug(input.name);
|
|
692
|
+
db.run(`INSERT INTO projects (id, name, slug, description, path) VALUES (?, ?, ?, ?, ?)`, [id, input.name, slug, input.description ?? null, input.path ?? null]);
|
|
693
|
+
return getProject(id);
|
|
694
|
+
}
|
|
695
|
+
function getProject(idOrSlug) {
|
|
696
|
+
const db = getDatabase();
|
|
697
|
+
const id = resolveProject(db, idOrSlug);
|
|
698
|
+
if (!id)
|
|
699
|
+
return null;
|
|
700
|
+
const row = db.query("SELECT * FROM projects WHERE id = ?").get(id);
|
|
701
|
+
if (!row)
|
|
702
|
+
return null;
|
|
703
|
+
const countRow = db.query("SELECT COUNT(*) as n FROM prompts WHERE project_id = ?").get(id);
|
|
704
|
+
return rowToProject(row, countRow.n);
|
|
705
|
+
}
|
|
706
|
+
function listProjects() {
|
|
707
|
+
const db = getDatabase();
|
|
708
|
+
const rows = db.query("SELECT * FROM projects ORDER BY name ASC").all();
|
|
709
|
+
return rows.map((row) => {
|
|
710
|
+
const countRow = db.query("SELECT COUNT(*) as n FROM prompts WHERE project_id = ?").get(row["id"]);
|
|
711
|
+
return rowToProject(row, countRow.n);
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
function deleteProject(idOrSlug) {
|
|
715
|
+
const db = getDatabase();
|
|
716
|
+
const id = resolveProject(db, idOrSlug);
|
|
717
|
+
if (!id)
|
|
718
|
+
throw new ProjectNotFoundError(idOrSlug);
|
|
719
|
+
db.run("DELETE FROM projects WHERE id = ?", [id]);
|
|
720
|
+
}
|
|
628
721
|
// src/lib/search.ts
|
|
629
722
|
function rowToSearchResult(row, snippet) {
|
|
630
723
|
return {
|
|
@@ -639,6 +732,7 @@ function rowToSearchResult(row, snippet) {
|
|
|
639
732
|
tags: JSON.parse(row["tags"] || "[]"),
|
|
640
733
|
variables: JSON.parse(row["variables"] || "[]"),
|
|
641
734
|
pinned: Boolean(row["pinned"]),
|
|
735
|
+
project_id: row["project_id"] ?? null,
|
|
642
736
|
is_template: Boolean(row["is_template"]),
|
|
643
737
|
source: row["source"],
|
|
644
738
|
version: row["version"],
|
|
@@ -682,6 +776,10 @@ function searchPrompts(query, filter = {}) {
|
|
|
682
776
|
for (const tag of filter.tags)
|
|
683
777
|
params.push(`%"${tag}"%`);
|
|
684
778
|
}
|
|
779
|
+
if (filter.project_id !== undefined && filter.project_id !== null) {
|
|
780
|
+
conditions.push("(p.project_id = ? OR p.project_id IS NULL)");
|
|
781
|
+
params.push(filter.project_id);
|
|
782
|
+
}
|
|
685
783
|
const where = conditions.length > 0 ? `AND ${conditions.join(" AND ")}` : "";
|
|
686
784
|
const limit = filter.limit ?? 50;
|
|
687
785
|
const offset = filter.offset ?? 0;
|
|
@@ -771,12 +869,14 @@ export {
|
|
|
771
869
|
movePrompt,
|
|
772
870
|
listVersions,
|
|
773
871
|
listPrompts,
|
|
872
|
+
listProjects,
|
|
774
873
|
listCollections,
|
|
775
874
|
listAgents,
|
|
776
875
|
importFromJson,
|
|
777
876
|
getVersion,
|
|
778
877
|
getPromptStats,
|
|
779
878
|
getPrompt,
|
|
879
|
+
getProject,
|
|
780
880
|
getDbPath,
|
|
781
881
|
getDatabase,
|
|
782
882
|
getCollection,
|
|
@@ -789,9 +889,12 @@ export {
|
|
|
789
889
|
exportToJson,
|
|
790
890
|
ensureCollection,
|
|
791
891
|
deletePrompt,
|
|
892
|
+
deleteProject,
|
|
792
893
|
createPrompt,
|
|
894
|
+
createProject,
|
|
793
895
|
VersionConflictError,
|
|
794
896
|
TemplateRenderError,
|
|
795
897
|
PromptNotFoundError,
|
|
898
|
+
ProjectNotFoundError,
|
|
796
899
|
DuplicateSlugError
|
|
797
900
|
};
|
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;AAwCxE,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
|
@@ -4103,6 +4103,21 @@ function runMigrations(db) {
|
|
|
4103
4103
|
name: "003_pinned",
|
|
4104
4104
|
sql: `ALTER TABLE prompts ADD COLUMN pinned INTEGER NOT NULL DEFAULT 0;`
|
|
4105
4105
|
},
|
|
4106
|
+
{
|
|
4107
|
+
name: "004_projects",
|
|
4108
|
+
sql: `
|
|
4109
|
+
CREATE TABLE IF NOT EXISTS projects (
|
|
4110
|
+
id TEXT PRIMARY KEY,
|
|
4111
|
+
name TEXT NOT NULL UNIQUE,
|
|
4112
|
+
slug TEXT NOT NULL UNIQUE,
|
|
4113
|
+
description TEXT,
|
|
4114
|
+
path TEXT,
|
|
4115
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
4116
|
+
);
|
|
4117
|
+
ALTER TABLE prompts ADD COLUMN project_id TEXT REFERENCES projects(id) ON DELETE SET NULL;
|
|
4118
|
+
CREATE INDEX IF NOT EXISTS idx_prompts_project_id ON prompts(project_id);
|
|
4119
|
+
`
|
|
4120
|
+
},
|
|
4106
4121
|
{
|
|
4107
4122
|
name: "002_fts5",
|
|
4108
4123
|
sql: `
|
|
@@ -4143,6 +4158,24 @@ function runMigrations(db) {
|
|
|
4143
4158
|
db.run("INSERT INTO _migrations (name) VALUES (?)", [migration.name]);
|
|
4144
4159
|
}
|
|
4145
4160
|
}
|
|
4161
|
+
function resolveProject(db, idOrSlug) {
|
|
4162
|
+
const byId = db.query("SELECT id FROM projects WHERE id = ?").get(idOrSlug);
|
|
4163
|
+
if (byId)
|
|
4164
|
+
return byId.id;
|
|
4165
|
+
const bySlug = db.query("SELECT id FROM projects WHERE slug = ?").get(idOrSlug);
|
|
4166
|
+
if (bySlug)
|
|
4167
|
+
return bySlug.id;
|
|
4168
|
+
const byName = db.query("SELECT id FROM projects WHERE lower(name) = ?").get(idOrSlug.toLowerCase());
|
|
4169
|
+
if (byName)
|
|
4170
|
+
return byName.id;
|
|
4171
|
+
const byPrefix = db.query("SELECT id FROM projects WHERE id LIKE ? LIMIT 2").all(`${idOrSlug}%`);
|
|
4172
|
+
if (byPrefix.length === 1 && byPrefix[0])
|
|
4173
|
+
return byPrefix[0].id;
|
|
4174
|
+
const bySlugPrefix = db.query("SELECT id FROM projects WHERE slug LIKE ? LIMIT 2").all(`${idOrSlug}%`);
|
|
4175
|
+
if (bySlugPrefix.length === 1 && bySlugPrefix[0])
|
|
4176
|
+
return bySlugPrefix[0].id;
|
|
4177
|
+
return null;
|
|
4178
|
+
}
|
|
4146
4179
|
function hasFts(db) {
|
|
4147
4180
|
return db.query("SELECT 1 FROM sqlite_master WHERE type='table' AND name='prompts_fts'").get() !== null;
|
|
4148
4181
|
}
|
|
@@ -4360,6 +4393,12 @@ class DuplicateSlugError extends Error {
|
|
|
4360
4393
|
this.name = "DuplicateSlugError";
|
|
4361
4394
|
}
|
|
4362
4395
|
}
|
|
4396
|
+
class ProjectNotFoundError extends Error {
|
|
4397
|
+
constructor(id) {
|
|
4398
|
+
super(`Project not found: ${id}`);
|
|
4399
|
+
this.name = "ProjectNotFoundError";
|
|
4400
|
+
}
|
|
4401
|
+
}
|
|
4363
4402
|
|
|
4364
4403
|
// src/db/prompts.ts
|
|
4365
4404
|
function rowToPrompt(row) {
|
|
@@ -4374,6 +4413,7 @@ function rowToPrompt(row) {
|
|
|
4374
4413
|
tags: JSON.parse(row["tags"] || "[]"),
|
|
4375
4414
|
variables: JSON.parse(row["variables"] || "[]"),
|
|
4376
4415
|
pinned: Boolean(row["pinned"]),
|
|
4416
|
+
project_id: row["project_id"] ?? null,
|
|
4377
4417
|
is_template: Boolean(row["is_template"]),
|
|
4378
4418
|
source: row["source"],
|
|
4379
4419
|
version: row["version"],
|
|
@@ -4397,11 +4437,12 @@ function createPrompt(input) {
|
|
|
4397
4437
|
ensureCollection(collection);
|
|
4398
4438
|
const tags = JSON.stringify(input.tags || []);
|
|
4399
4439
|
const source = input.source || "manual";
|
|
4440
|
+
const project_id = input.project_id ?? null;
|
|
4400
4441
|
const vars = extractVariables(input.body);
|
|
4401
4442
|
const variables = JSON.stringify(vars.map((v) => ({ name: v, required: true })));
|
|
4402
4443
|
const is_template = vars.length > 0 ? 1 : 0;
|
|
4403
|
-
db.run(`INSERT INTO prompts (id, name, slug, title, body, description, collection, tags, variables, is_template, source)
|
|
4404
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [id, name, slug, input.title, input.body, input.description ?? null, collection, tags, variables, is_template, source]);
|
|
4444
|
+
db.run(`INSERT INTO prompts (id, name, slug, title, body, description, collection, tags, variables, is_template, source, project_id)
|
|
4445
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [id, name, slug, input.title, input.body, input.description ?? null, collection, tags, variables, is_template, source, project_id]);
|
|
4405
4446
|
db.run(`INSERT INTO prompt_versions (id, prompt_id, body, version, changed_by)
|
|
4406
4447
|
VALUES (?, ?, ?, 1, ?)`, [generateId("VER"), id, input.body, input.changed_by ?? null]);
|
|
4407
4448
|
return getPrompt(id);
|
|
@@ -4445,10 +4486,16 @@ function listPrompts(filter = {}) {
|
|
|
4445
4486
|
params.push(`%"${tag}"%`);
|
|
4446
4487
|
}
|
|
4447
4488
|
}
|
|
4489
|
+
let orderBy = "pinned DESC, use_count DESC, updated_at DESC";
|
|
4490
|
+
if (filter.project_id !== undefined && filter.project_id !== null) {
|
|
4491
|
+
conditions.push("(project_id = ? OR project_id IS NULL)");
|
|
4492
|
+
params.push(filter.project_id);
|
|
4493
|
+
orderBy = `(CASE WHEN project_id = '${filter.project_id}' THEN 0 ELSE 1 END), pinned DESC, use_count DESC, updated_at DESC`;
|
|
4494
|
+
}
|
|
4448
4495
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
4449
4496
|
const limit = filter.limit ?? 100;
|
|
4450
4497
|
const offset = filter.offset ?? 0;
|
|
4451
|
-
const rows = db.query(`SELECT * FROM prompts ${where} ORDER BY
|
|
4498
|
+
const rows = db.query(`SELECT * FROM prompts ${where} ORDER BY ${orderBy} LIMIT ? OFFSET ?`).all(...params, limit, offset);
|
|
4452
4499
|
return rows.map(rowToPrompt);
|
|
4453
4500
|
}
|
|
4454
4501
|
function updatePrompt(idOrSlug, input) {
|
|
@@ -4606,6 +4653,52 @@ function registerAgent(name, description) {
|
|
|
4606
4653
|
return rowToAgent(db.query("SELECT * FROM agents WHERE id = ?").get(id));
|
|
4607
4654
|
}
|
|
4608
4655
|
|
|
4656
|
+
// src/db/projects.ts
|
|
4657
|
+
function rowToProject(row, promptCount) {
|
|
4658
|
+
return {
|
|
4659
|
+
id: row["id"],
|
|
4660
|
+
name: row["name"],
|
|
4661
|
+
slug: row["slug"],
|
|
4662
|
+
description: row["description"] ?? null,
|
|
4663
|
+
path: row["path"] ?? null,
|
|
4664
|
+
prompt_count: promptCount,
|
|
4665
|
+
created_at: row["created_at"]
|
|
4666
|
+
};
|
|
4667
|
+
}
|
|
4668
|
+
function createProject(input) {
|
|
4669
|
+
const db = getDatabase();
|
|
4670
|
+
const id = generateId("proj");
|
|
4671
|
+
const slug = generateSlug(input.name);
|
|
4672
|
+
db.run(`INSERT INTO projects (id, name, slug, description, path) VALUES (?, ?, ?, ?, ?)`, [id, input.name, slug, input.description ?? null, input.path ?? null]);
|
|
4673
|
+
return getProject(id);
|
|
4674
|
+
}
|
|
4675
|
+
function getProject(idOrSlug) {
|
|
4676
|
+
const db = getDatabase();
|
|
4677
|
+
const id = resolveProject(db, idOrSlug);
|
|
4678
|
+
if (!id)
|
|
4679
|
+
return null;
|
|
4680
|
+
const row = db.query("SELECT * FROM projects WHERE id = ?").get(id);
|
|
4681
|
+
if (!row)
|
|
4682
|
+
return null;
|
|
4683
|
+
const countRow = db.query("SELECT COUNT(*) as n FROM prompts WHERE project_id = ?").get(id);
|
|
4684
|
+
return rowToProject(row, countRow.n);
|
|
4685
|
+
}
|
|
4686
|
+
function listProjects() {
|
|
4687
|
+
const db = getDatabase();
|
|
4688
|
+
const rows = db.query("SELECT * FROM projects ORDER BY name ASC").all();
|
|
4689
|
+
return rows.map((row) => {
|
|
4690
|
+
const countRow = db.query("SELECT COUNT(*) as n FROM prompts WHERE project_id = ?").get(row["id"]);
|
|
4691
|
+
return rowToProject(row, countRow.n);
|
|
4692
|
+
});
|
|
4693
|
+
}
|
|
4694
|
+
function deleteProject(idOrSlug) {
|
|
4695
|
+
const db = getDatabase();
|
|
4696
|
+
const id = resolveProject(db, idOrSlug);
|
|
4697
|
+
if (!id)
|
|
4698
|
+
throw new ProjectNotFoundError(idOrSlug);
|
|
4699
|
+
db.run("DELETE FROM projects WHERE id = ?", [id]);
|
|
4700
|
+
}
|
|
4701
|
+
|
|
4609
4702
|
// src/lib/search.ts
|
|
4610
4703
|
function rowToSearchResult(row, snippet) {
|
|
4611
4704
|
return {
|
|
@@ -4620,6 +4713,7 @@ function rowToSearchResult(row, snippet) {
|
|
|
4620
4713
|
tags: JSON.parse(row["tags"] || "[]"),
|
|
4621
4714
|
variables: JSON.parse(row["variables"] || "[]"),
|
|
4622
4715
|
pinned: Boolean(row["pinned"]),
|
|
4716
|
+
project_id: row["project_id"] ?? null,
|
|
4623
4717
|
is_template: Boolean(row["is_template"]),
|
|
4624
4718
|
source: row["source"],
|
|
4625
4719
|
version: row["version"],
|
|
@@ -4663,6 +4757,10 @@ function searchPrompts(query, filter = {}) {
|
|
|
4663
4757
|
for (const tag of filter.tags)
|
|
4664
4758
|
params.push(`%"${tag}"%`);
|
|
4665
4759
|
}
|
|
4760
|
+
if (filter.project_id !== undefined && filter.project_id !== null) {
|
|
4761
|
+
conditions.push("(p.project_id = ? OR p.project_id IS NULL)");
|
|
4762
|
+
params.push(filter.project_id);
|
|
4763
|
+
}
|
|
4666
4764
|
const where = conditions.length > 0 ? `AND ${conditions.join(" AND ")}` : "";
|
|
4667
4765
|
const limit = filter.limit ?? 50;
|
|
4668
4766
|
const offset = filter.offset ?? 0;
|
|
@@ -4816,11 +4914,19 @@ server.registerTool("prompts_save", {
|
|
|
4816
4914
|
tags: exports_external.array(exports_external.string()).optional().describe("Tags for filtering and search"),
|
|
4817
4915
|
source: exports_external.enum(["manual", "ai-session", "imported"]).optional().describe("Where this prompt came from"),
|
|
4818
4916
|
changed_by: exports_external.string().optional().describe("Agent name making this change"),
|
|
4819
|
-
force: exports_external.boolean().optional().describe("Save even if a similar prompt already exists")
|
|
4917
|
+
force: exports_external.boolean().optional().describe("Save even if a similar prompt already exists"),
|
|
4918
|
+
project: exports_external.string().optional().describe("Project name, slug, or ID to scope this prompt to")
|
|
4820
4919
|
}
|
|
4821
4920
|
}, async (args) => {
|
|
4822
4921
|
try {
|
|
4823
|
-
const { force, ...input } = args;
|
|
4922
|
+
const { force, project, ...input } = args;
|
|
4923
|
+
if (project) {
|
|
4924
|
+
const db = getDatabase();
|
|
4925
|
+
const pid = resolveProject(db, project);
|
|
4926
|
+
if (!pid)
|
|
4927
|
+
return err(`Project not found: ${project}`);
|
|
4928
|
+
input.project_id = pid;
|
|
4929
|
+
}
|
|
4824
4930
|
const { prompt, created, duplicate_warning } = upsertPrompt(input, force ?? false);
|
|
4825
4931
|
return ok({ ...prompt, _created: created, _duplicate_warning: duplicate_warning ?? null });
|
|
4826
4932
|
} catch (e) {
|
|
@@ -4844,9 +4950,19 @@ server.registerTool("prompts_list", {
|
|
|
4844
4950
|
is_template: exports_external.boolean().optional(),
|
|
4845
4951
|
source: exports_external.enum(["manual", "ai-session", "imported"]).optional(),
|
|
4846
4952
|
limit: exports_external.number().optional().default(50),
|
|
4847
|
-
offset: exports_external.number().optional().default(0)
|
|
4848
|
-
|
|
4849
|
-
}
|
|
4953
|
+
offset: exports_external.number().optional().default(0),
|
|
4954
|
+
project: exports_external.string().optional().describe("Project name, slug, or ID \u2014 shows project prompts first, then globals")
|
|
4955
|
+
}
|
|
4956
|
+
}, async ({ project, ...args }) => {
|
|
4957
|
+
if (project) {
|
|
4958
|
+
const db = getDatabase();
|
|
4959
|
+
const pid = resolveProject(db, project);
|
|
4960
|
+
if (!pid)
|
|
4961
|
+
return err(`Project not found: ${project}`);
|
|
4962
|
+
return ok(listPrompts({ ...args, project_id: pid }));
|
|
4963
|
+
}
|
|
4964
|
+
return ok(listPrompts(args));
|
|
4965
|
+
});
|
|
4850
4966
|
server.registerTool("prompts_delete", {
|
|
4851
4967
|
description: "Delete a prompt by ID or slug.",
|
|
4852
4968
|
inputSchema: { id: exports_external.string() }
|
|
@@ -4916,9 +5032,19 @@ server.registerTool("prompts_search", {
|
|
|
4916
5032
|
tags: exports_external.array(exports_external.string()).optional(),
|
|
4917
5033
|
is_template: exports_external.boolean().optional(),
|
|
4918
5034
|
source: exports_external.enum(["manual", "ai-session", "imported"]).optional(),
|
|
4919
|
-
limit: exports_external.number().optional().default(20)
|
|
4920
|
-
|
|
4921
|
-
}
|
|
5035
|
+
limit: exports_external.number().optional().default(20),
|
|
5036
|
+
project: exports_external.string().optional().describe("Project name, slug, or ID to scope search")
|
|
5037
|
+
}
|
|
5038
|
+
}, async ({ q, project, ...filter }) => {
|
|
5039
|
+
if (project) {
|
|
5040
|
+
const db = getDatabase();
|
|
5041
|
+
const pid = resolveProject(db, project);
|
|
5042
|
+
if (!pid)
|
|
5043
|
+
return err(`Project not found: ${project}`);
|
|
5044
|
+
return ok(searchPrompts(q, { ...filter, project_id: pid }));
|
|
5045
|
+
}
|
|
5046
|
+
return ok(searchPrompts(q, filter));
|
|
5047
|
+
});
|
|
4922
5048
|
server.registerTool("prompts_similar", {
|
|
4923
5049
|
description: "Find prompts similar to a given prompt (by tag overlap and collection).",
|
|
4924
5050
|
inputSchema: {
|
|
@@ -5056,10 +5182,19 @@ server.registerTool("prompts_save_from_session", {
|
|
|
5056
5182
|
tags: exports_external.array(exports_external.string()).optional().describe("Relevant tags extracted from the prompt context"),
|
|
5057
5183
|
collection: exports_external.string().optional().describe("Collection to save into (default: 'sessions')"),
|
|
5058
5184
|
description: exports_external.string().optional().describe("One-line description of what this prompt does"),
|
|
5059
|
-
agent: exports_external.string().optional().describe("Agent name saving this prompt")
|
|
5185
|
+
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")
|
|
5060
5187
|
}
|
|
5061
|
-
}, async ({ title, body, slug, tags, collection, description, agent }) => {
|
|
5188
|
+
}, async ({ title, body, slug, tags, collection, description, agent, project }) => {
|
|
5062
5189
|
try {
|
|
5190
|
+
let project_id;
|
|
5191
|
+
if (project) {
|
|
5192
|
+
const db = getDatabase();
|
|
5193
|
+
const pid = resolveProject(db, project);
|
|
5194
|
+
if (!pid)
|
|
5195
|
+
return err(`Project not found: ${project}`);
|
|
5196
|
+
project_id = pid;
|
|
5197
|
+
}
|
|
5063
5198
|
const { prompt, created } = upsertPrompt({
|
|
5064
5199
|
title,
|
|
5065
5200
|
body,
|
|
@@ -5068,7 +5203,8 @@ server.registerTool("prompts_save_from_session", {
|
|
|
5068
5203
|
collection: collection ?? "sessions",
|
|
5069
5204
|
description,
|
|
5070
5205
|
source: "ai-session",
|
|
5071
|
-
changed_by: agent
|
|
5206
|
+
changed_by: agent,
|
|
5207
|
+
project_id
|
|
5072
5208
|
});
|
|
5073
5209
|
return ok({ ...prompt, _created: created, _tip: created ? `Saved as "${prompt.slug}". Use prompts_use("${prompt.slug}") to retrieve it.` : `Updated existing prompt "${prompt.slug}".` });
|
|
5074
5210
|
} catch (e) {
|
|
@@ -5131,5 +5267,43 @@ server.registerTool("prompts_stats", {
|
|
|
5131
5267
|
description: "Get usage statistics: most used prompts, recently used, counts by collection and source.",
|
|
5132
5268
|
inputSchema: {}
|
|
5133
5269
|
}, async () => ok(getPromptStats()));
|
|
5270
|
+
server.registerTool("prompts_project_create", {
|
|
5271
|
+
description: "Create a new project to scope prompts.",
|
|
5272
|
+
inputSchema: {
|
|
5273
|
+
name: exports_external.string().describe("Project name"),
|
|
5274
|
+
description: exports_external.string().optional().describe("Short description"),
|
|
5275
|
+
path: exports_external.string().optional().describe("Optional filesystem path this project maps to")
|
|
5276
|
+
}
|
|
5277
|
+
}, async ({ name, description, path }) => {
|
|
5278
|
+
try {
|
|
5279
|
+
return ok(createProject({ name, description, path }));
|
|
5280
|
+
} catch (e) {
|
|
5281
|
+
return err(e instanceof Error ? e.message : String(e));
|
|
5282
|
+
}
|
|
5283
|
+
});
|
|
5284
|
+
server.registerTool("prompts_project_list", {
|
|
5285
|
+
description: "List all projects with prompt counts.",
|
|
5286
|
+
inputSchema: {}
|
|
5287
|
+
}, async () => ok(listProjects()));
|
|
5288
|
+
server.registerTool("prompts_project_get", {
|
|
5289
|
+
description: "Get a project by ID, slug, or name.",
|
|
5290
|
+
inputSchema: { id: exports_external.string().describe("Project ID, slug, or name") }
|
|
5291
|
+
}, async ({ id }) => {
|
|
5292
|
+
const project = getProject(id);
|
|
5293
|
+
if (!project)
|
|
5294
|
+
return err(`Project not found: ${id}`);
|
|
5295
|
+
return ok(project);
|
|
5296
|
+
});
|
|
5297
|
+
server.registerTool("prompts_project_delete", {
|
|
5298
|
+
description: "Delete a project. Prompts in the project become global (project_id set to null).",
|
|
5299
|
+
inputSchema: { id: exports_external.string().describe("Project ID, slug, or name") }
|
|
5300
|
+
}, async ({ id }) => {
|
|
5301
|
+
try {
|
|
5302
|
+
deleteProject(id);
|
|
5303
|
+
return ok({ deleted: true, id });
|
|
5304
|
+
} catch (e) {
|
|
5305
|
+
return err(e instanceof Error ? e.message : String(e));
|
|
5306
|
+
}
|
|
5307
|
+
});
|
|
5134
5308
|
var transport = new StdioServerTransport;
|
|
5135
5309
|
await server.connect(transport);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":";;;eAsCmB,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;;AAF9C,wBAkOC"}
|
package/dist/server/index.js
CHANGED
|
@@ -115,6 +115,21 @@ function runMigrations(db) {
|
|
|
115
115
|
name: "003_pinned",
|
|
116
116
|
sql: `ALTER TABLE prompts ADD COLUMN pinned INTEGER NOT NULL DEFAULT 0;`
|
|
117
117
|
},
|
|
118
|
+
{
|
|
119
|
+
name: "004_projects",
|
|
120
|
+
sql: `
|
|
121
|
+
CREATE TABLE IF NOT EXISTS projects (
|
|
122
|
+
id TEXT PRIMARY KEY,
|
|
123
|
+
name TEXT NOT NULL UNIQUE,
|
|
124
|
+
slug TEXT NOT NULL UNIQUE,
|
|
125
|
+
description TEXT,
|
|
126
|
+
path TEXT,
|
|
127
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
128
|
+
);
|
|
129
|
+
ALTER TABLE prompts ADD COLUMN project_id TEXT REFERENCES projects(id) ON DELETE SET NULL;
|
|
130
|
+
CREATE INDEX IF NOT EXISTS idx_prompts_project_id ON prompts(project_id);
|
|
131
|
+
`
|
|
132
|
+
},
|
|
118
133
|
{
|
|
119
134
|
name: "002_fts5",
|
|
120
135
|
sql: `
|
|
@@ -155,6 +170,24 @@ function runMigrations(db) {
|
|
|
155
170
|
db.run("INSERT INTO _migrations (name) VALUES (?)", [migration.name]);
|
|
156
171
|
}
|
|
157
172
|
}
|
|
173
|
+
function resolveProject(db, idOrSlug) {
|
|
174
|
+
const byId = db.query("SELECT id FROM projects WHERE id = ?").get(idOrSlug);
|
|
175
|
+
if (byId)
|
|
176
|
+
return byId.id;
|
|
177
|
+
const bySlug = db.query("SELECT id FROM projects WHERE slug = ?").get(idOrSlug);
|
|
178
|
+
if (bySlug)
|
|
179
|
+
return bySlug.id;
|
|
180
|
+
const byName = db.query("SELECT id FROM projects WHERE lower(name) = ?").get(idOrSlug.toLowerCase());
|
|
181
|
+
if (byName)
|
|
182
|
+
return byName.id;
|
|
183
|
+
const byPrefix = db.query("SELECT id FROM projects WHERE id LIKE ? LIMIT 2").all(`${idOrSlug}%`);
|
|
184
|
+
if (byPrefix.length === 1 && byPrefix[0])
|
|
185
|
+
return byPrefix[0].id;
|
|
186
|
+
const bySlugPrefix = db.query("SELECT id FROM projects WHERE slug LIKE ? LIMIT 2").all(`${idOrSlug}%`);
|
|
187
|
+
if (bySlugPrefix.length === 1 && bySlugPrefix[0])
|
|
188
|
+
return bySlugPrefix[0].id;
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
158
191
|
function hasFts(db) {
|
|
159
192
|
return db.query("SELECT 1 FROM sqlite_master WHERE type='table' AND name='prompts_fts'").get() !== null;
|
|
160
193
|
}
|
|
@@ -363,6 +396,12 @@ class DuplicateSlugError extends Error {
|
|
|
363
396
|
this.name = "DuplicateSlugError";
|
|
364
397
|
}
|
|
365
398
|
}
|
|
399
|
+
class ProjectNotFoundError extends Error {
|
|
400
|
+
constructor(id) {
|
|
401
|
+
super(`Project not found: ${id}`);
|
|
402
|
+
this.name = "ProjectNotFoundError";
|
|
403
|
+
}
|
|
404
|
+
}
|
|
366
405
|
|
|
367
406
|
// src/db/prompts.ts
|
|
368
407
|
function rowToPrompt(row) {
|
|
@@ -377,6 +416,7 @@ function rowToPrompt(row) {
|
|
|
377
416
|
tags: JSON.parse(row["tags"] || "[]"),
|
|
378
417
|
variables: JSON.parse(row["variables"] || "[]"),
|
|
379
418
|
pinned: Boolean(row["pinned"]),
|
|
419
|
+
project_id: row["project_id"] ?? null,
|
|
380
420
|
is_template: Boolean(row["is_template"]),
|
|
381
421
|
source: row["source"],
|
|
382
422
|
version: row["version"],
|
|
@@ -400,11 +440,12 @@ function createPrompt(input) {
|
|
|
400
440
|
ensureCollection(collection);
|
|
401
441
|
const tags = JSON.stringify(input.tags || []);
|
|
402
442
|
const source = input.source || "manual";
|
|
443
|
+
const project_id = input.project_id ?? null;
|
|
403
444
|
const vars = extractVariables(input.body);
|
|
404
445
|
const variables = JSON.stringify(vars.map((v) => ({ name: v, required: true })));
|
|
405
446
|
const is_template = vars.length > 0 ? 1 : 0;
|
|
406
|
-
db.run(`INSERT INTO prompts (id, name, slug, title, body, description, collection, tags, variables, is_template, source)
|
|
407
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [id, name, slug, input.title, input.body, input.description ?? null, collection, tags, variables, is_template, source]);
|
|
447
|
+
db.run(`INSERT INTO prompts (id, name, slug, title, body, description, collection, tags, variables, is_template, source, project_id)
|
|
448
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [id, name, slug, input.title, input.body, input.description ?? null, collection, tags, variables, is_template, source, project_id]);
|
|
408
449
|
db.run(`INSERT INTO prompt_versions (id, prompt_id, body, version, changed_by)
|
|
409
450
|
VALUES (?, ?, ?, 1, ?)`, [generateId("VER"), id, input.body, input.changed_by ?? null]);
|
|
410
451
|
return getPrompt(id);
|
|
@@ -448,10 +489,16 @@ function listPrompts(filter = {}) {
|
|
|
448
489
|
params.push(`%"${tag}"%`);
|
|
449
490
|
}
|
|
450
491
|
}
|
|
492
|
+
let orderBy = "pinned DESC, use_count DESC, updated_at DESC";
|
|
493
|
+
if (filter.project_id !== undefined && filter.project_id !== null) {
|
|
494
|
+
conditions.push("(project_id = ? OR project_id IS NULL)");
|
|
495
|
+
params.push(filter.project_id);
|
|
496
|
+
orderBy = `(CASE WHEN project_id = '${filter.project_id}' THEN 0 ELSE 1 END), pinned DESC, use_count DESC, updated_at DESC`;
|
|
497
|
+
}
|
|
451
498
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
452
499
|
const limit = filter.limit ?? 100;
|
|
453
500
|
const offset = filter.offset ?? 0;
|
|
454
|
-
const rows = db.query(`SELECT * FROM prompts ${where} ORDER BY
|
|
501
|
+
const rows = db.query(`SELECT * FROM prompts ${where} ORDER BY ${orderBy} LIMIT ? OFFSET ?`).all(...params, limit, offset);
|
|
455
502
|
return rows.map(rowToPrompt);
|
|
456
503
|
}
|
|
457
504
|
function updatePrompt(idOrSlug, input) {
|
|
@@ -578,6 +625,52 @@ function restoreVersion(promptId, version, changedBy) {
|
|
|
578
625
|
VALUES (?, ?, ?, ?, ?)`, [generateId("VER"), promptId, ver.body, newVersion, changedBy ?? null]);
|
|
579
626
|
}
|
|
580
627
|
|
|
628
|
+
// src/db/projects.ts
|
|
629
|
+
function rowToProject(row, promptCount) {
|
|
630
|
+
return {
|
|
631
|
+
id: row["id"],
|
|
632
|
+
name: row["name"],
|
|
633
|
+
slug: row["slug"],
|
|
634
|
+
description: row["description"] ?? null,
|
|
635
|
+
path: row["path"] ?? null,
|
|
636
|
+
prompt_count: promptCount,
|
|
637
|
+
created_at: row["created_at"]
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
function createProject(input) {
|
|
641
|
+
const db = getDatabase();
|
|
642
|
+
const id = generateId("proj");
|
|
643
|
+
const slug = generateSlug(input.name);
|
|
644
|
+
db.run(`INSERT INTO projects (id, name, slug, description, path) VALUES (?, ?, ?, ?, ?)`, [id, input.name, slug, input.description ?? null, input.path ?? null]);
|
|
645
|
+
return getProject(id);
|
|
646
|
+
}
|
|
647
|
+
function getProject(idOrSlug) {
|
|
648
|
+
const db = getDatabase();
|
|
649
|
+
const id = resolveProject(db, idOrSlug);
|
|
650
|
+
if (!id)
|
|
651
|
+
return null;
|
|
652
|
+
const row = db.query("SELECT * FROM projects WHERE id = ?").get(id);
|
|
653
|
+
if (!row)
|
|
654
|
+
return null;
|
|
655
|
+
const countRow = db.query("SELECT COUNT(*) as n FROM prompts WHERE project_id = ?").get(id);
|
|
656
|
+
return rowToProject(row, countRow.n);
|
|
657
|
+
}
|
|
658
|
+
function listProjects() {
|
|
659
|
+
const db = getDatabase();
|
|
660
|
+
const rows = db.query("SELECT * FROM projects ORDER BY name ASC").all();
|
|
661
|
+
return rows.map((row) => {
|
|
662
|
+
const countRow = db.query("SELECT COUNT(*) as n FROM prompts WHERE project_id = ?").get(row["id"]);
|
|
663
|
+
return rowToProject(row, countRow.n);
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
function deleteProject(idOrSlug) {
|
|
667
|
+
const db = getDatabase();
|
|
668
|
+
const id = resolveProject(db, idOrSlug);
|
|
669
|
+
if (!id)
|
|
670
|
+
throw new ProjectNotFoundError(idOrSlug);
|
|
671
|
+
db.run("DELETE FROM projects WHERE id = ?", [id]);
|
|
672
|
+
}
|
|
673
|
+
|
|
581
674
|
// src/lib/search.ts
|
|
582
675
|
function rowToSearchResult(row, snippet) {
|
|
583
676
|
return {
|
|
@@ -592,6 +685,7 @@ function rowToSearchResult(row, snippet) {
|
|
|
592
685
|
tags: JSON.parse(row["tags"] || "[]"),
|
|
593
686
|
variables: JSON.parse(row["variables"] || "[]"),
|
|
594
687
|
pinned: Boolean(row["pinned"]),
|
|
688
|
+
project_id: row["project_id"] ?? null,
|
|
595
689
|
is_template: Boolean(row["is_template"]),
|
|
596
690
|
source: row["source"],
|
|
597
691
|
version: row["version"],
|
|
@@ -635,6 +729,10 @@ function searchPrompts(query, filter = {}) {
|
|
|
635
729
|
for (const tag of filter.tags)
|
|
636
730
|
params.push(`%"${tag}"%`);
|
|
637
731
|
}
|
|
732
|
+
if (filter.project_id !== undefined && filter.project_id !== null) {
|
|
733
|
+
conditions.push("(p.project_id = ? OR p.project_id IS NULL)");
|
|
734
|
+
params.push(filter.project_id);
|
|
735
|
+
}
|
|
638
736
|
const where = conditions.length > 0 ? `AND ${conditions.join(" AND ")}` : "";
|
|
639
737
|
const limit = filter.limit ?? 50;
|
|
640
738
|
const offset = filter.offset ?? 0;
|
|
@@ -755,7 +853,15 @@ var server_default = {
|
|
|
755
853
|
const source = url.searchParams.get("source") ?? undefined;
|
|
756
854
|
const limit = parseInt(url.searchParams.get("limit") ?? "100");
|
|
757
855
|
const offset = parseInt(url.searchParams.get("offset") ?? "0");
|
|
758
|
-
|
|
856
|
+
const projectParam = url.searchParams.get("project") ?? undefined;
|
|
857
|
+
let project_id;
|
|
858
|
+
if (projectParam) {
|
|
859
|
+
const pid = resolveProject(getDatabase(), projectParam);
|
|
860
|
+
if (!pid)
|
|
861
|
+
return notFound(`Project not found: ${projectParam}`);
|
|
862
|
+
project_id = pid;
|
|
863
|
+
}
|
|
864
|
+
return json(listPrompts({ collection, tags, is_template, source, limit, offset, project_id }));
|
|
759
865
|
}
|
|
760
866
|
if (path === "/api/prompts" && method === "POST") {
|
|
761
867
|
const body = await parseBody(req);
|
|
@@ -864,6 +970,43 @@ var server_default = {
|
|
|
864
970
|
const collection = url.searchParams.get("collection") ?? undefined;
|
|
865
971
|
return json(exportToJson(collection));
|
|
866
972
|
}
|
|
973
|
+
if (path === "/api/projects" && method === "GET") {
|
|
974
|
+
return json(listProjects());
|
|
975
|
+
}
|
|
976
|
+
if (path === "/api/projects" && method === "POST") {
|
|
977
|
+
const { name, description, path: projPath } = await parseBody(req);
|
|
978
|
+
if (!name)
|
|
979
|
+
return badRequest("name is required");
|
|
980
|
+
return json(createProject({ name, description, path: projPath }), 201);
|
|
981
|
+
}
|
|
982
|
+
const projectMatch = path.match(/^\/api\/projects\/([^/]+)$/);
|
|
983
|
+
if (projectMatch) {
|
|
984
|
+
const projId = projectMatch[1];
|
|
985
|
+
if (method === "GET") {
|
|
986
|
+
const project = getProject(projId);
|
|
987
|
+
if (!project)
|
|
988
|
+
return notFound(`Project not found: ${projId}`);
|
|
989
|
+
return json(project);
|
|
990
|
+
}
|
|
991
|
+
if (method === "DELETE") {
|
|
992
|
+
try {
|
|
993
|
+
deleteProject(projId);
|
|
994
|
+
return json({ deleted: true, id: projId });
|
|
995
|
+
} catch (e) {
|
|
996
|
+
return notFound(e instanceof Error ? e.message : String(e));
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
const projectPromptsMatch = path.match(/^\/api\/projects\/([^/]+)\/prompts$/);
|
|
1001
|
+
if (projectPromptsMatch && method === "GET") {
|
|
1002
|
+
const projId = projectPromptsMatch[1];
|
|
1003
|
+
const project = getProject(projId);
|
|
1004
|
+
if (!project)
|
|
1005
|
+
return notFound(`Project not found: ${projId}`);
|
|
1006
|
+
const limit = parseInt(url.searchParams.get("limit") ?? "100");
|
|
1007
|
+
const offset = parseInt(url.searchParams.get("offset") ?? "0");
|
|
1008
|
+
return json(listPrompts({ project_id: project.id, limit, offset }));
|
|
1009
|
+
}
|
|
867
1010
|
if (path === "/health") {
|
|
868
1011
|
return json({ status: "ok", port: PORT });
|
|
869
1012
|
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -11,12 +11,22 @@ export interface Prompt {
|
|
|
11
11
|
is_template: boolean;
|
|
12
12
|
source: PromptSource;
|
|
13
13
|
pinned: boolean;
|
|
14
|
+
project_id: string | null;
|
|
14
15
|
version: number;
|
|
15
16
|
use_count: number;
|
|
16
17
|
last_used_at: string | null;
|
|
17
18
|
created_at: string;
|
|
18
19
|
updated_at: string;
|
|
19
20
|
}
|
|
21
|
+
export interface Project {
|
|
22
|
+
id: string;
|
|
23
|
+
name: string;
|
|
24
|
+
slug: string;
|
|
25
|
+
description: string | null;
|
|
26
|
+
path: string | null;
|
|
27
|
+
prompt_count: number;
|
|
28
|
+
created_at: string;
|
|
29
|
+
}
|
|
20
30
|
export interface TemplateVariable {
|
|
21
31
|
name: string;
|
|
22
32
|
description?: string;
|
|
@@ -56,6 +66,7 @@ export interface CreatePromptInput {
|
|
|
56
66
|
tags?: string[];
|
|
57
67
|
source?: PromptSource;
|
|
58
68
|
changed_by?: string;
|
|
69
|
+
project_id?: string | null;
|
|
59
70
|
}
|
|
60
71
|
export interface UpdatePromptInput {
|
|
61
72
|
title?: string;
|
|
@@ -73,6 +84,7 @@ export interface ListPromptsFilter {
|
|
|
73
84
|
q?: string;
|
|
74
85
|
limit?: number;
|
|
75
86
|
offset?: number;
|
|
87
|
+
project_id?: string | null;
|
|
76
88
|
}
|
|
77
89
|
export interface SearchResult {
|
|
78
90
|
prompt: Prompt;
|
|
@@ -123,4 +135,7 @@ export declare class DuplicateSlugError extends Error {
|
|
|
123
135
|
export declare class TemplateRenderError extends Error {
|
|
124
136
|
constructor(message: string);
|
|
125
137
|
}
|
|
138
|
+
export declare class ProjectNotFoundError extends Error {
|
|
139
|
+
constructor(id: string);
|
|
140
|
+
}
|
|
126
141
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,SAAS,EAAE,gBAAgB,EAAE,CAAA;IAC7B,WAAW,EAAE,OAAO,CAAA;IACpB,MAAM,EAAE,YAAY,CAAA;IACpB,MAAM,EAAE,OAAO,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,YAAY,GAAG,UAAU,CAAA;AAE/D,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf,MAAM,CAAC,EAAE,YAAY,CAAA;IACrB,UAAU,CAAC,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
|