@hasna/prompts 0.2.1 → 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.
@@ -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;AAsHD,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"}
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"}
@@ -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"}
@@ -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;
@@ -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;AA0B1B,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAyC7D;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,CAmCpE;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"}
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,8 +1,9 @@
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";
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
@@ -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;AAGzD,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,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,GACpB,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,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
@@ -113,6 +113,41 @@ 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
+ },
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
+ },
116
151
  {
117
152
  name: "002_fts5",
118
153
  sql: `
@@ -153,6 +188,24 @@ function runMigrations(db) {
153
188
  db.run("INSERT INTO _migrations (name) VALUES (?)", [migration.name]);
154
189
  }
155
190
  }
191
+ function resolveProject(db, idOrSlug) {
192
+ const byId = db.query("SELECT id FROM projects WHERE id = ?").get(idOrSlug);
193
+ if (byId)
194
+ return byId.id;
195
+ const bySlug = db.query("SELECT id FROM projects WHERE slug = ?").get(idOrSlug);
196
+ if (bySlug)
197
+ return bySlug.id;
198
+ const byName = db.query("SELECT id FROM projects WHERE lower(name) = ?").get(idOrSlug.toLowerCase());
199
+ if (byName)
200
+ return byName.id;
201
+ const byPrefix = db.query("SELECT id FROM projects WHERE id LIKE ? LIMIT 2").all(`${idOrSlug}%`);
202
+ if (byPrefix.length === 1 && byPrefix[0])
203
+ return byPrefix[0].id;
204
+ const bySlugPrefix = db.query("SELECT id FROM projects WHERE slug LIKE ? LIMIT 2").all(`${idOrSlug}%`);
205
+ if (bySlugPrefix.length === 1 && bySlugPrefix[0])
206
+ return bySlugPrefix[0].id;
207
+ return null;
208
+ }
156
209
  function hasFts(db) {
157
210
  return db.query("SELECT 1 FROM sqlite_master WHERE type='table' AND name='prompts_fts'").get() !== null;
158
211
  }
@@ -378,6 +431,13 @@ class TemplateRenderError extends Error {
378
431
  }
379
432
  }
380
433
 
434
+ class ProjectNotFoundError extends Error {
435
+ constructor(id) {
436
+ super(`Project not found: ${id}`);
437
+ this.name = "ProjectNotFoundError";
438
+ }
439
+ }
440
+
381
441
  // src/db/prompts.ts
382
442
  function rowToPrompt(row) {
383
443
  return {
@@ -391,6 +451,9 @@ function rowToPrompt(row) {
391
451
  tags: JSON.parse(row["tags"] || "[]"),
392
452
  variables: JSON.parse(row["variables"] || "[]"),
393
453
  pinned: Boolean(row["pinned"]),
454
+ next_prompt: row["next_prompt"] ?? null,
455
+ expires_at: row["expires_at"] ?? null,
456
+ project_id: row["project_id"] ?? null,
394
457
  is_template: Boolean(row["is_template"]),
395
458
  source: row["source"],
396
459
  version: row["version"],
@@ -414,11 +477,12 @@ function createPrompt(input) {
414
477
  ensureCollection(collection);
415
478
  const tags = JSON.stringify(input.tags || []);
416
479
  const source = input.source || "manual";
480
+ const project_id = input.project_id ?? null;
417
481
  const vars = extractVariables(input.body);
418
482
  const variables = JSON.stringify(vars.map((v) => ({ name: v, required: true })));
419
483
  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]);
484
+ db.run(`INSERT INTO prompts (id, name, slug, title, body, description, collection, tags, variables, is_template, source, project_id)
485
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [id, name, slug, input.title, input.body, input.description ?? null, collection, tags, variables, is_template, source, project_id]);
422
486
  db.run(`INSERT INTO prompt_versions (id, prompt_id, body, version, changed_by)
423
487
  VALUES (?, ?, ?, 1, ?)`, [generateId("VER"), id, input.body, input.changed_by ?? null]);
424
488
  return getPrompt(id);
@@ -462,10 +526,16 @@ function listPrompts(filter = {}) {
462
526
  params.push(`%"${tag}"%`);
463
527
  }
464
528
  }
529
+ let orderBy = "pinned DESC, use_count DESC, updated_at DESC";
530
+ if (filter.project_id !== undefined && filter.project_id !== null) {
531
+ conditions.push("(project_id = ? OR project_id IS NULL)");
532
+ params.push(filter.project_id);
533
+ orderBy = `(CASE WHEN project_id = '${filter.project_id}' THEN 0 ELSE 1 END), pinned DESC, use_count DESC, updated_at DESC`;
534
+ }
465
535
  const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
466
536
  const limit = filter.limit ?? 100;
467
537
  const offset = filter.offset ?? 0;
468
- const rows = db.query(`SELECT * FROM prompts ${where} ORDER BY pinned DESC, use_count DESC, updated_at DESC LIMIT ? OFFSET ?`).all(...params, limit, offset);
538
+ const rows = db.query(`SELECT * FROM prompts ${where} ORDER BY ${orderBy} LIMIT ? OFFSET ?`).all(...params, limit, offset);
469
539
  return rows.map(rowToPrompt);
470
540
  }
471
541
  function updatePrompt(idOrSlug, input) {
@@ -481,6 +551,7 @@ function updatePrompt(idOrSlug, input) {
481
551
  description = COALESCE(?, description),
482
552
  collection = COALESCE(?, collection),
483
553
  tags = COALESCE(?, tags),
554
+ next_prompt = CASE WHEN ? IS NOT NULL THEN ? ELSE next_prompt END,
484
555
  variables = ?,
485
556
  is_template = ?,
486
557
  version = version + 1,
@@ -491,6 +562,8 @@ function updatePrompt(idOrSlug, input) {
491
562
  input.description ?? null,
492
563
  input.collection ?? null,
493
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,
494
567
  variables,
495
568
  is_template,
496
569
  prompt.id,
@@ -513,6 +586,13 @@ function usePrompt(idOrSlug) {
513
586
  const db = getDatabase();
514
587
  const prompt = requirePrompt(idOrSlug);
515
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]);
516
596
  return requirePrompt(prompt.id);
517
597
  }
518
598
  function pinPrompt(idOrSlug, pinned) {
@@ -625,6 +705,51 @@ function listAgents() {
625
705
  const rows = db.query("SELECT * FROM agents ORDER BY last_seen_at DESC").all();
626
706
  return rows.map(rowToAgent);
627
707
  }
708
+ // src/db/projects.ts
709
+ function rowToProject(row, promptCount) {
710
+ return {
711
+ id: row["id"],
712
+ name: row["name"],
713
+ slug: row["slug"],
714
+ description: row["description"] ?? null,
715
+ path: row["path"] ?? null,
716
+ prompt_count: promptCount,
717
+ created_at: row["created_at"]
718
+ };
719
+ }
720
+ function createProject(input) {
721
+ const db = getDatabase();
722
+ const id = generateId("proj");
723
+ const slug = generateSlug(input.name);
724
+ db.run(`INSERT INTO projects (id, name, slug, description, path) VALUES (?, ?, ?, ?, ?)`, [id, input.name, slug, input.description ?? null, input.path ?? null]);
725
+ return getProject(id);
726
+ }
727
+ function getProject(idOrSlug) {
728
+ const db = getDatabase();
729
+ const id = resolveProject(db, idOrSlug);
730
+ if (!id)
731
+ return null;
732
+ const row = db.query("SELECT * FROM projects WHERE id = ?").get(id);
733
+ if (!row)
734
+ return null;
735
+ const countRow = db.query("SELECT COUNT(*) as n FROM prompts WHERE project_id = ?").get(id);
736
+ return rowToProject(row, countRow.n);
737
+ }
738
+ function listProjects() {
739
+ const db = getDatabase();
740
+ const rows = db.query("SELECT * FROM projects ORDER BY name ASC").all();
741
+ return rows.map((row) => {
742
+ const countRow = db.query("SELECT COUNT(*) as n FROM prompts WHERE project_id = ?").get(row["id"]);
743
+ return rowToProject(row, countRow.n);
744
+ });
745
+ }
746
+ function deleteProject(idOrSlug) {
747
+ const db = getDatabase();
748
+ const id = resolveProject(db, idOrSlug);
749
+ if (!id)
750
+ throw new ProjectNotFoundError(idOrSlug);
751
+ db.run("DELETE FROM projects WHERE id = ?", [id]);
752
+ }
628
753
  // src/lib/search.ts
629
754
  function rowToSearchResult(row, snippet) {
630
755
  return {
@@ -639,6 +764,9 @@ function rowToSearchResult(row, snippet) {
639
764
  tags: JSON.parse(row["tags"] || "[]"),
640
765
  variables: JSON.parse(row["variables"] || "[]"),
641
766
  pinned: Boolean(row["pinned"]),
767
+ next_prompt: row["next_prompt"] ?? null,
768
+ expires_at: row["expires_at"] ?? null,
769
+ project_id: row["project_id"] ?? null,
642
770
  is_template: Boolean(row["is_template"]),
643
771
  source: row["source"],
644
772
  version: row["version"],
@@ -682,6 +810,10 @@ function searchPrompts(query, filter = {}) {
682
810
  for (const tag of filter.tags)
683
811
  params.push(`%"${tag}"%`);
684
812
  }
813
+ if (filter.project_id !== undefined && filter.project_id !== null) {
814
+ conditions.push("(p.project_id = ? OR p.project_id IS NULL)");
815
+ params.push(filter.project_id);
816
+ }
685
817
  const where = conditions.length > 0 ? `AND ${conditions.join(" AND ")}` : "";
686
818
  const limit = filter.limit ?? 50;
687
819
  const offset = filter.offset ?? 0;
@@ -762,6 +894,7 @@ export {
762
894
  upsertPrompt,
763
895
  updatePrompt,
764
896
  uniqueSlug,
897
+ setNextPrompt,
765
898
  searchPrompts,
766
899
  restoreVersion,
767
900
  requirePrompt,
@@ -771,12 +904,14 @@ export {
771
904
  movePrompt,
772
905
  listVersions,
773
906
  listPrompts,
907
+ listProjects,
774
908
  listCollections,
775
909
  listAgents,
776
910
  importFromJson,
777
911
  getVersion,
778
912
  getPromptStats,
779
913
  getPrompt,
914
+ getProject,
780
915
  getDbPath,
781
916
  getDatabase,
782
917
  getCollection,
@@ -789,9 +924,12 @@ export {
789
924
  exportToJson,
790
925
  ensureCollection,
791
926
  deletePrompt,
927
+ deleteProject,
792
928
  createPrompt,
929
+ createProject,
793
930
  VersionConflictError,
794
931
  TemplateRenderError,
795
932
  PromptNotFoundError,
933
+ ProjectNotFoundError,
796
934
  DuplicateSlugError
797
935
  };
@@ -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,3 @@
1
+ export declare function generateZshCompletion(): string;
2
+ export declare function generateBashCompletion(): string;
3
+ //# sourceMappingURL=completion.d.ts.map
@@ -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"}
@@ -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"}
@@ -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;AAuCxE,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EACb,MAAM,GAAE,IAAI,CAAC,iBAAiB,EAAE,GAAG,CAAM,GACxC,YAAY,EAAE,CAoEhB;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,YAAY,EAAE,CAmCvE"}
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"}