@hasna/prompts 0.3.11 → 0.3.13

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 CHANGED
@@ -11770,6 +11770,35 @@ var init_ids = __esm(() => {
11770
11770
  init_database();
11771
11771
  });
11772
11772
 
11773
+ // src/types/index.ts
11774
+ var PromptNotFoundError, VersionConflictError, DuplicateSlugError, ProjectNotFoundError;
11775
+ var init_types2 = __esm(() => {
11776
+ PromptNotFoundError = class PromptNotFoundError extends Error {
11777
+ constructor(id) {
11778
+ super(`Prompt not found: ${id}`);
11779
+ this.name = "PromptNotFoundError";
11780
+ }
11781
+ };
11782
+ VersionConflictError = class VersionConflictError extends Error {
11783
+ constructor(id) {
11784
+ super(`Version conflict on prompt: ${id}`);
11785
+ this.name = "VersionConflictError";
11786
+ }
11787
+ };
11788
+ DuplicateSlugError = class DuplicateSlugError extends Error {
11789
+ constructor(slug) {
11790
+ super(`A prompt with slug "${slug}" already exists`);
11791
+ this.name = "DuplicateSlugError";
11792
+ }
11793
+ };
11794
+ ProjectNotFoundError = class ProjectNotFoundError extends Error {
11795
+ constructor(id) {
11796
+ super(`Project not found: ${id}`);
11797
+ this.name = "ProjectNotFoundError";
11798
+ }
11799
+ };
11800
+ });
11801
+
11773
11802
  // src/db/collections.ts
11774
11803
  function rowToCollection(row) {
11775
11804
  return {
@@ -11908,35 +11937,6 @@ var init_template = __esm(() => {
11908
11937
  VAR_PATTERN = /\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*(?:\|\s*(.*?)\s*)?\}\}/g;
11909
11938
  });
11910
11939
 
11911
- // src/types/index.ts
11912
- var PromptNotFoundError, VersionConflictError, DuplicateSlugError, ProjectNotFoundError;
11913
- var init_types2 = __esm(() => {
11914
- PromptNotFoundError = class PromptNotFoundError extends Error {
11915
- constructor(id) {
11916
- super(`Prompt not found: ${id}`);
11917
- this.name = "PromptNotFoundError";
11918
- }
11919
- };
11920
- VersionConflictError = class VersionConflictError extends Error {
11921
- constructor(id) {
11922
- super(`Version conflict on prompt: ${id}`);
11923
- this.name = "VersionConflictError";
11924
- }
11925
- };
11926
- DuplicateSlugError = class DuplicateSlugError extends Error {
11927
- constructor(slug) {
11928
- super(`A prompt with slug "${slug}" already exists`);
11929
- this.name = "DuplicateSlugError";
11930
- }
11931
- };
11932
- ProjectNotFoundError = class ProjectNotFoundError extends Error {
11933
- constructor(id) {
11934
- super(`Project not found: ${id}`);
11935
- this.name = "ProjectNotFoundError";
11936
- }
11937
- };
11938
- });
11939
-
11940
11940
  // src/db/prompts.ts
11941
11941
  function rowToPrompt(row) {
11942
11942
  return {
@@ -12338,8 +12338,7 @@ var {
12338
12338
  } = import__.default;
12339
12339
 
12340
12340
  // src/cli/index.tsx
12341
- init_prompts();
12342
- import chalk from "chalk";
12341
+ import chalk3 from "chalk";
12343
12342
  import { createRequire as createRequire2 } from "module";
12344
12343
 
12345
12344
  // src/db/versions.ts
@@ -12437,154 +12436,7 @@ function deleteProject(idOrSlug) {
12437
12436
  }
12438
12437
 
12439
12438
  // src/cli/index.tsx
12440
- init_database();
12441
-
12442
- // src/lib/search.ts
12443
- init_database();
12444
12439
  init_prompts();
12445
- function rowToSearchResult(row, snippet) {
12446
- return {
12447
- prompt: {
12448
- id: row["id"],
12449
- name: row["name"],
12450
- slug: row["slug"],
12451
- title: row["title"],
12452
- body: row["body"],
12453
- description: row["description"] ?? null,
12454
- collection: row["collection"],
12455
- tags: JSON.parse(row["tags"] || "[]"),
12456
- variables: JSON.parse(row["variables"] || "[]"),
12457
- pinned: Boolean(row["pinned"]),
12458
- next_prompt: row["next_prompt"] ?? null,
12459
- expires_at: row["expires_at"] ?? null,
12460
- project_id: row["project_id"] ?? null,
12461
- is_template: Boolean(row["is_template"]),
12462
- source: row["source"],
12463
- version: row["version"],
12464
- use_count: row["use_count"],
12465
- last_used_at: row["last_used_at"] ?? null,
12466
- created_at: row["created_at"],
12467
- updated_at: row["updated_at"]
12468
- },
12469
- score: row["score"] ?? 1,
12470
- snippet
12471
- };
12472
- }
12473
- function escapeFtsQuery(q) {
12474
- return q.trim().split(/\s+/).filter(Boolean).map((w) => `"${w.replace(/"/g, '""')}"*`).join(" ");
12475
- }
12476
- function searchPrompts(query, filter = {}) {
12477
- const db = getDatabase();
12478
- if (!query.trim()) {
12479
- const prompts = listPrompts(filter);
12480
- return prompts.map((p) => ({ prompt: p, score: 1 }));
12481
- }
12482
- if (hasFts(db)) {
12483
- const ftsQuery = escapeFtsQuery(query);
12484
- const conditions = [];
12485
- const params = [];
12486
- if (filter.collection) {
12487
- conditions.push("p.collection = ?");
12488
- params.push(filter.collection);
12489
- }
12490
- if (filter.is_template !== undefined) {
12491
- conditions.push("p.is_template = ?");
12492
- params.push(filter.is_template ? 1 : 0);
12493
- }
12494
- if (filter.source) {
12495
- conditions.push("p.source = ?");
12496
- params.push(filter.source);
12497
- }
12498
- if (filter.tags && filter.tags.length > 0) {
12499
- const tagConds = filter.tags.map(() => "p.tags LIKE ?");
12500
- conditions.push(`(${tagConds.join(" OR ")})`);
12501
- for (const tag of filter.tags)
12502
- params.push(`%"${tag}"%`);
12503
- }
12504
- if (filter.project_id !== undefined && filter.project_id !== null) {
12505
- conditions.push("(p.project_id = ? OR p.project_id IS NULL)");
12506
- params.push(filter.project_id);
12507
- }
12508
- const where = conditions.length > 0 ? `AND ${conditions.join(" AND ")}` : "";
12509
- const limit = filter.limit ?? 50;
12510
- const offset = filter.offset ?? 0;
12511
- try {
12512
- const rows2 = db.query(`SELECT p.*, bm25(prompts_fts) as score,
12513
- snippet(prompts_fts, 2, '[', ']', '...', 10) as snippet
12514
- FROM prompts p
12515
- INNER JOIN prompts_fts ON prompts_fts.rowid = p.rowid
12516
- WHERE prompts_fts MATCH ?
12517
- ${where}
12518
- ORDER BY bm25(prompts_fts)
12519
- LIMIT ? OFFSET ?`).all(ftsQuery, ...params, limit, offset);
12520
- return rows2.map((r) => rowToSearchResult(r, r["snippet"]));
12521
- } catch {}
12522
- }
12523
- const like = `%${query}%`;
12524
- const rows = db.query(`SELECT *, 1 as score FROM prompts
12525
- WHERE (name LIKE ? OR slug LIKE ? OR title LIKE ? OR body LIKE ? OR description LIKE ? OR tags LIKE ?)
12526
- ORDER BY use_count DESC, updated_at DESC
12527
- LIMIT ? OFFSET ?`).all(like, like, like, like, like, like, filter.limit ?? 10, filter.offset ?? 0);
12528
- return rows.map((r) => rowToSearchResult(r));
12529
- }
12530
- function findSimilar(promptId, limit = 5) {
12531
- const db = getDatabase();
12532
- const prompt = db.query("SELECT * FROM prompts WHERE id = ?").get(promptId);
12533
- if (!prompt)
12534
- return [];
12535
- const tags = JSON.parse(prompt["tags"] || "[]");
12536
- const collection = prompt["collection"];
12537
- if (tags.length === 0) {
12538
- const rows = db.query("SELECT *, 1 as score FROM prompts WHERE collection = ? AND id != ? ORDER BY use_count DESC LIMIT ?").all(collection, promptId, limit);
12539
- return rows.map((r) => rowToSearchResult(r));
12540
- }
12541
- const allRows = db.query("SELECT * FROM prompts WHERE id != ?").all(promptId);
12542
- const scored = allRows.map((row) => {
12543
- const rowTags = JSON.parse(row["tags"] || "[]");
12544
- const overlap = rowTags.filter((t) => tags.includes(t)).length;
12545
- const sameCollection = row["collection"] === collection ? 1 : 0;
12546
- return { row, score: overlap * 2 + sameCollection };
12547
- });
12548
- return scored.filter((s) => s.score > 0).sort((a, b) => b.score - a.score).slice(0, limit).map((s) => rowToSearchResult(s.row, undefined));
12549
- }
12550
-
12551
- // src/cli/index.tsx
12552
- init_template();
12553
- init_importer();
12554
-
12555
- // src/lib/lint.ts
12556
- function lintPrompt(p) {
12557
- const issues = [];
12558
- const issue = (severity, rule, message) => ({
12559
- prompt_id: p.id,
12560
- slug: p.slug,
12561
- severity,
12562
- rule,
12563
- message
12564
- });
12565
- if (!p.description) {
12566
- issues.push(issue("warn", "missing-description", "No description provided"));
12567
- }
12568
- if (p.body.trim().length < 10) {
12569
- issues.push(issue("error", "body-too-short", `Body is only ${p.body.trim().length} characters`));
12570
- }
12571
- if (p.tags.length === 0) {
12572
- issues.push(issue("info", "no-tags", "No tags \u2014 prompt will be harder to discover"));
12573
- }
12574
- if (p.is_template) {
12575
- const undocumented = p.variables.filter((v) => !v.description || v.description.trim() === "");
12576
- if (undocumented.length > 0) {
12577
- issues.push(issue("warn", "undocumented-vars", `Template variables without description: ${undocumented.map((v) => v.name).join(", ")}`));
12578
- }
12579
- }
12580
- if (p.collection === "default" && p.use_count === 0) {
12581
- issues.push(issue("info", "uncollected", "In default collection and never used \u2014 consider organizing"));
12582
- }
12583
- return issues;
12584
- }
12585
- function lintAll(prompts) {
12586
- return prompts.map((p) => ({ prompt: p, issues: lintPrompt(p) })).filter((r) => r.issues.length > 0);
12587
- }
12588
12440
 
12589
12441
  // src/db/schedules.ts
12590
12442
  init_database();
@@ -12997,30 +12849,66 @@ function diffTexts(a, b) {
12997
12849
  return trace;
12998
12850
  }
12999
12851
 
12852
+ // src/lib/lint.ts
12853
+ function lintPrompt(p) {
12854
+ const issues = [];
12855
+ const issue = (severity, rule, message) => ({
12856
+ prompt_id: p.id,
12857
+ slug: p.slug,
12858
+ severity,
12859
+ rule,
12860
+ message
12861
+ });
12862
+ if (!p.description) {
12863
+ issues.push(issue("warn", "missing-description", "No description provided"));
12864
+ }
12865
+ if (p.body.trim().length < 10) {
12866
+ issues.push(issue("error", "body-too-short", `Body is only ${p.body.trim().length} characters`));
12867
+ }
12868
+ if (p.tags.length === 0) {
12869
+ issues.push(issue("info", "no-tags", "No tags \u2014 prompt will be harder to discover"));
12870
+ }
12871
+ if (p.is_template) {
12872
+ const undocumented = p.variables.filter((v) => !v.description || v.description.trim() === "");
12873
+ if (undocumented.length > 0) {
12874
+ issues.push(issue("warn", "undocumented-vars", `Template variables without description: ${undocumented.map((v) => v.name).join(", ")}`));
12875
+ }
12876
+ }
12877
+ if (p.collection === "default" && p.use_count === 0) {
12878
+ issues.push(issue("info", "uncollected", "In default collection and never used \u2014 consider organizing"));
12879
+ }
12880
+ return issues;
12881
+ }
12882
+ function lintAll(prompts) {
12883
+ return prompts.map((p) => ({ prompt: p, issues: lintPrompt(p) })).filter((r) => r.issues.length > 0);
12884
+ }
12885
+
13000
12886
  // src/cli/index.tsx
13001
- var require2 = createRequire2(import.meta.url);
13002
- var pkg = require2("../../package.json");
13003
- 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");
13004
- function isJson() {
12887
+ init_importer();
12888
+
12889
+ // src/cli/utils.ts
12890
+ init_database();
12891
+ import chalk from "chalk";
12892
+ function isJson(program2) {
13005
12893
  return Boolean(program2.opts()["json"]);
13006
12894
  }
13007
- function getActiveProjectId() {
12895
+ function getActiveProjectId(program2) {
13008
12896
  const projectName = program2.opts()["project"] ?? process.env["PROMPTS_PROJECT"];
13009
12897
  if (!projectName)
13010
12898
  return null;
13011
12899
  const db = getDatabase();
13012
12900
  return resolveProject(db, projectName);
13013
12901
  }
13014
- function output(data) {
13015
- if (isJson()) {
12902
+ function output(program2, data) {
12903
+ if (isJson(program2)) {
13016
12904
  console.log(JSON.stringify(data, null, 2));
13017
12905
  } else {
13018
12906
  console.log(data);
13019
12907
  }
13020
12908
  }
13021
- function handleError(e) {
12909
+ function handleError(program2, e) {
13022
12910
  const msg = e instanceof Error ? e.message : String(e);
13023
- if (isJson()) {
12911
+ if (isJson(program2)) {
13024
12912
  console.log(JSON.stringify({ error: msg }));
13025
12913
  } else {
13026
12914
  console.error(chalk.red("Error: " + msg));
@@ -13033,322 +12921,468 @@ function fmtPrompt(p) {
13033
12921
  const pin = p.pinned ? chalk.yellow(" \uD83D\uDCCC") : "";
13034
12922
  return `${chalk.bold(p.id)} ${chalk.green(p.slug)}${template}${pin} ${p.title}${tags} ${chalk.gray(p.collection)}`;
13035
12923
  }
13036
- program2.command("save <title>").description("Save a new prompt (or update existing by slug)").option("-b, --body <body>", "Prompt body (use - to read from stdin)").option("-f, --file <path>", "Read body from file").option("-s, --slug <slug>", "Custom slug").option("-d, --description <desc>", "Short description").option("-c, --collection <name>", "Collection", "default").option("-t, --tags <tags>", "Comma-separated tags").option("--source <source>", "Source: manual|ai-session|imported", "manual").option("--agent <name>", "Agent name (for attribution)").option("--force", "Save even if a similar prompt already exists").option("--pin", "Pin immediately so it appears first in all lists").action(async (title, opts) => {
13037
- try {
13038
- let body = opts["body"] ?? "";
13039
- if (opts["file"]) {
13040
- const { readFileSync: readFileSync2 } = await import("fs");
13041
- body = readFileSync2(opts["file"], "utf-8");
13042
- } else if (opts["body"] === "-" || !opts["body"] && !opts["file"]) {
13043
- const chunks = [];
13044
- for await (const chunk of process.stdin)
13045
- chunks.push(chunk);
13046
- body = Buffer.concat(chunks).toString("utf-8").trim();
13047
- }
13048
- if (!body)
13049
- handleError("No body provided. Use --body, --file, or pipe via stdin.");
13050
- const project_id = getActiveProjectId();
13051
- const { prompt, created, duplicate_warning } = upsertPrompt({
13052
- title,
13053
- body,
13054
- slug: opts["slug"],
13055
- description: opts["description"],
13056
- collection: opts["collection"],
13057
- tags: opts["tags"] ? opts["tags"].split(",").map((t) => t.trim()) : [],
13058
- source: opts["source"] || "manual",
13059
- changed_by: opts["agent"],
13060
- project_id
13061
- }, Boolean(opts["force"]));
13062
- if (duplicate_warning && !isJson()) {
13063
- console.warn(chalk.yellow(`Warning: ${duplicate_warning}`));
13064
- }
13065
- if (opts["pin"])
13066
- pinPrompt(prompt.id, true);
13067
- if (isJson()) {
13068
- output(opts["pin"] ? { ...prompt, pinned: true } : prompt);
13069
- } else {
13070
- const action = created ? chalk.green("Created") : chalk.yellow("Updated");
13071
- console.log(`${action} ${chalk.bold(prompt.id)} \u2014 ${chalk.green(prompt.slug)}`);
13072
- console.log(chalk.gray(` Title: ${prompt.title}`));
13073
- console.log(chalk.gray(` Collection: ${prompt.collection}`));
13074
- if (opts["pin"])
13075
- console.log(chalk.yellow(" \uD83D\uDCCC Pinned"));
13076
- if (prompt.is_template) {
13077
- const vars = extractVariableInfo(prompt.body);
13078
- console.log(chalk.cyan(` Template vars: ${vars.map((v) => v.name).join(", ")}`));
13079
- }
13080
- }
13081
- } catch (e) {
13082
- handleError(e);
13083
- }
13084
- });
13085
- program2.command("use <id>").description("Get a prompt's body and increment its use counter").option("--edit", "Open in $EDITOR for quick tweaks before printing").action(async (id, opts) => {
13086
- try {
13087
- const prompt = usePrompt(id);
13088
- let body = prompt.body;
13089
- if (opts.edit) {
13090
- const editor = process.env["EDITOR"] ?? process.env["VISUAL"] ?? "nano";
13091
- const { writeFileSync: writeFileSync2, readFileSync: readFileSync2, unlinkSync } = await import("fs");
13092
- const { tmpdir } = await import("os");
13093
- const { join: join7 } = await import("path");
13094
- const tmp = join7(tmpdir(), `prompts-${prompt.id}-${Date.now()}.md`);
13095
- writeFileSync2(tmp, body);
13096
- const proc = Bun.spawnSync([editor, tmp], { stdio: ["inherit", "inherit", "inherit"] });
13097
- if (proc.exitCode === 0) {
13098
- body = readFileSync2(tmp, "utf-8");
13099
- }
13100
- try {
13101
- unlinkSync(tmp);
13102
- } catch {}
12924
+
12925
+ // src/cli/commands/prompts.tsx
12926
+ init_prompts();
12927
+ import chalk2 from "chalk";
12928
+
12929
+ // src/lib/search.ts
12930
+ init_database();
12931
+ init_prompts();
12932
+ function rowToSearchResult(row, snippet) {
12933
+ return {
12934
+ prompt: {
12935
+ id: row["id"],
12936
+ name: row["name"],
12937
+ slug: row["slug"],
12938
+ title: row["title"],
12939
+ body: row["body"],
12940
+ description: row["description"] ?? null,
12941
+ collection: row["collection"],
12942
+ tags: JSON.parse(row["tags"] || "[]"),
12943
+ variables: JSON.parse(row["variables"] || "[]"),
12944
+ pinned: Boolean(row["pinned"]),
12945
+ next_prompt: row["next_prompt"] ?? null,
12946
+ expires_at: row["expires_at"] ?? null,
12947
+ project_id: row["project_id"] ?? null,
12948
+ is_template: Boolean(row["is_template"]),
12949
+ source: row["source"],
12950
+ version: row["version"],
12951
+ use_count: row["use_count"],
12952
+ last_used_at: row["last_used_at"] ?? null,
12953
+ created_at: row["created_at"],
12954
+ updated_at: row["updated_at"]
12955
+ },
12956
+ score: row["score"] ?? 1,
12957
+ snippet
12958
+ };
12959
+ }
12960
+ function escapeFtsQuery(q) {
12961
+ return q.trim().split(/\s+/).filter(Boolean).map((w) => `"${w.replace(/"/g, '""')}"*`).join(" ");
12962
+ }
12963
+ function searchPrompts(query, filter = {}) {
12964
+ const db = getDatabase();
12965
+ if (!query.trim()) {
12966
+ const prompts = listPrompts(filter);
12967
+ return prompts.map((p) => ({ prompt: p, score: 1 }));
12968
+ }
12969
+ if (hasFts(db)) {
12970
+ const ftsQuery = escapeFtsQuery(query);
12971
+ const conditions = [];
12972
+ const params = [];
12973
+ if (filter.collection) {
12974
+ conditions.push("p.collection = ?");
12975
+ params.push(filter.collection);
13103
12976
  }
13104
- if (isJson()) {
13105
- output({ ...prompt, body });
13106
- } else {
13107
- console.log(body);
13108
- if (prompt.next_prompt) {
13109
- console.error(chalk.gray(`
13110
- \u2192 next: ${chalk.bold(prompt.next_prompt)}`));
13111
- }
12977
+ if (filter.is_template !== undefined) {
12978
+ conditions.push("p.is_template = ?");
12979
+ params.push(filter.is_template ? 1 : 0);
13112
12980
  }
13113
- } catch (e) {
13114
- handleError(e);
12981
+ if (filter.source) {
12982
+ conditions.push("p.source = ?");
12983
+ params.push(filter.source);
12984
+ }
12985
+ if (filter.tags && filter.tags.length > 0) {
12986
+ const tagConds = filter.tags.map(() => "p.tags LIKE ?");
12987
+ conditions.push(`(${tagConds.join(" OR ")})`);
12988
+ for (const tag of filter.tags)
12989
+ params.push(`%"${tag}"%`);
12990
+ }
12991
+ if (filter.project_id !== undefined && filter.project_id !== null) {
12992
+ conditions.push("(p.project_id = ? OR p.project_id IS NULL)");
12993
+ params.push(filter.project_id);
12994
+ }
12995
+ const where = conditions.length > 0 ? `AND ${conditions.join(" AND ")}` : "";
12996
+ const limit = filter.limit ?? 50;
12997
+ const offset = filter.offset ?? 0;
12998
+ try {
12999
+ const rows2 = db.query(`SELECT p.*, bm25(prompts_fts) as score,
13000
+ snippet(prompts_fts, 2, '[', ']', '...', 10) as snippet
13001
+ FROM prompts p
13002
+ INNER JOIN prompts_fts ON prompts_fts.rowid = p.rowid
13003
+ WHERE prompts_fts MATCH ?
13004
+ ${where}
13005
+ ORDER BY bm25(prompts_fts)
13006
+ LIMIT ? OFFSET ?`).all(ftsQuery, ...params, limit, offset);
13007
+ return rows2.map((r) => rowToSearchResult(r, r["snippet"]));
13008
+ } catch {}
13115
13009
  }
13116
- });
13117
- program2.command("get <id>").description("Get prompt details without incrementing use counter").action((id) => {
13118
- try {
13119
- const prompt = getPrompt(id);
13120
- if (!prompt)
13121
- handleError(`Prompt not found: ${id}`);
13122
- output(isJson() ? prompt : fmtPrompt(prompt));
13123
- } catch (e) {
13124
- handleError(e);
13010
+ const like = `%${query}%`;
13011
+ const rows = db.query(`SELECT *, 1 as score FROM prompts
13012
+ WHERE (name LIKE ? OR slug LIKE ? OR title LIKE ? OR body LIKE ? OR description LIKE ? OR tags LIKE ?)
13013
+ ORDER BY use_count DESC, updated_at DESC
13014
+ LIMIT ? OFFSET ?`).all(like, like, like, like, like, like, filter.limit ?? 10, filter.offset ?? 0);
13015
+ return rows.map((r) => rowToSearchResult(r));
13016
+ }
13017
+ function findSimilar(promptId, limit = 5) {
13018
+ const db = getDatabase();
13019
+ const prompt = db.query("SELECT * FROM prompts WHERE id = ?").get(promptId);
13020
+ if (!prompt)
13021
+ return [];
13022
+ const tags = JSON.parse(prompt["tags"] || "[]");
13023
+ const collection = prompt["collection"];
13024
+ if (tags.length === 0) {
13025
+ const rows = db.query("SELECT *, 1 as score FROM prompts WHERE collection = ? AND id != ? ORDER BY use_count DESC LIMIT ?").all(collection, promptId, limit);
13026
+ return rows.map((r) => rowToSearchResult(r));
13125
13027
  }
13126
- });
13127
- 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) => {
13128
- try {
13129
- const project_id = getActiveProjectId();
13130
- let prompts = listPrompts({
13131
- collection: opts["collection"],
13132
- tags: opts["tags"] ? opts["tags"].split(",").map((t) => t.trim()) : undefined,
13133
- is_template: opts["templates"] ? true : undefined,
13134
- limit: parseInt(opts["limit"]) || 50,
13135
- ...project_id !== null ? { project_id } : {}
13136
- });
13137
- if (opts["recent"]) {
13138
- prompts = prompts.filter((p) => p.last_used_at !== null).sort((a, b) => (b.last_used_at ?? "").localeCompare(a.last_used_at ?? ""));
13028
+ const allRows = db.query("SELECT * FROM prompts WHERE id != ?").all(promptId);
13029
+ const scored = allRows.map((row) => {
13030
+ const rowTags = JSON.parse(row["tags"] || "[]");
13031
+ const overlap = rowTags.filter((t) => tags.includes(t)).length;
13032
+ const sameCollection = row["collection"] === collection ? 1 : 0;
13033
+ return { row, score: overlap * 2 + sameCollection };
13034
+ });
13035
+ return scored.filter((s) => s.score > 0).sort((a, b) => b.score - a.score).slice(0, limit).map((s) => rowToSearchResult(s.row, undefined));
13036
+ }
13037
+
13038
+ // src/cli/commands/prompts.tsx
13039
+ init_template();
13040
+ function registerPromptCommands(program2) {
13041
+ program2.command("save <title>").description("Save a new prompt (or update existing by slug)").option("-b, --body <body>", "Prompt body (use - to read from stdin)").option("-f, --file <path>", "Read body from file").option("-s, --slug <slug>", "Custom slug").option("-d, --description <desc>", "Short description").option("-c, --collection <name>", "Collection", "default").option("-t, --tags <tags>", "Comma-separated tags").option("--source <source>", "Source: manual|ai-session|imported", "manual").option("--agent <name>", "Agent name (for attribution)").option("--force", "Save even if a similar prompt already exists").option("--pin", "Pin immediately so it appears first in all lists").action(async (title, opts) => {
13042
+ try {
13043
+ let body = opts["body"] ?? "";
13044
+ if (opts["file"]) {
13045
+ const { readFileSync: readFileSync2 } = await import("fs");
13046
+ body = readFileSync2(opts["file"], "utf-8");
13047
+ } else if (opts["body"] === "-" || !opts["body"] && !opts["file"]) {
13048
+ const chunks = [];
13049
+ for await (const chunk of process.stdin)
13050
+ chunks.push(chunk);
13051
+ body = Buffer.concat(chunks).toString("utf-8").trim();
13052
+ }
13053
+ if (!body)
13054
+ handleError(program2, "No body provided. Use --body, --file, or pipe via stdin.");
13055
+ const project_id = getActiveProjectId(program2);
13056
+ const { prompt, created, duplicate_warning } = upsertPrompt({
13057
+ title,
13058
+ body,
13059
+ slug: opts["slug"],
13060
+ description: opts["description"],
13061
+ collection: opts["collection"],
13062
+ tags: opts["tags"] ? opts["tags"].split(",").map((t) => t.trim()) : [],
13063
+ source: opts["source"] || "manual",
13064
+ changed_by: opts["agent"],
13065
+ project_id
13066
+ }, Boolean(opts["force"]));
13067
+ if (duplicate_warning && !isJson(program2)) {
13068
+ console.warn(chalk2.yellow(`Warning: ${duplicate_warning}`));
13069
+ }
13070
+ if (opts["pin"])
13071
+ pinPrompt(prompt.id, true);
13072
+ if (isJson(program2)) {
13073
+ output(program2, opts["pin"] ? { ...prompt, pinned: true } : prompt);
13074
+ } else {
13075
+ const action = created ? chalk2.green("Created") : chalk2.yellow("Updated");
13076
+ console.log(`${action} ${chalk2.bold(prompt.id)} \u2014 ${chalk2.green(prompt.slug)}`);
13077
+ console.log(chalk2.gray(` Title: ${prompt.title}`));
13078
+ console.log(chalk2.gray(` Collection: ${prompt.collection}`));
13079
+ if (opts["pin"])
13080
+ console.log(chalk2.yellow(" \uD83D\uDCCC Pinned"));
13081
+ if (prompt.is_template) {
13082
+ const vars = extractVariableInfo(prompt.body);
13083
+ console.log(chalk2.cyan(` Template vars: ${vars.map((v) => v.name).join(", ")}`));
13084
+ }
13085
+ }
13086
+ } catch (e) {
13087
+ handleError(program2, e);
13139
13088
  }
13140
- if (isJson()) {
13141
- output(prompts);
13142
- } else if (prompts.length === 0) {
13143
- console.log(chalk.gray("No prompts found."));
13144
- } else {
13145
- for (const p of prompts)
13146
- console.log(fmtPrompt(p));
13147
- console.log(chalk.gray(`
13089
+ });
13090
+ program2.command("use <id>").description("Get a prompt's body and increment its use counter").option("--edit", "Open in $EDITOR for quick tweaks before printing").action(async (id, opts) => {
13091
+ try {
13092
+ const prompt = usePrompt(id);
13093
+ let body = prompt.body;
13094
+ if (opts.edit) {
13095
+ const editor = process.env["EDITOR"] ?? process.env["VISUAL"] ?? "nano";
13096
+ const { writeFileSync: writeFileSync2, readFileSync: readFileSync2, unlinkSync } = await import("fs");
13097
+ const { tmpdir } = await import("os");
13098
+ const { join: join7 } = await import("path");
13099
+ const tmp = join7(tmpdir(), `prompts-${prompt.id}-${Date.now()}.md`);
13100
+ writeFileSync2(tmp, body);
13101
+ const proc = Bun.spawnSync([editor, tmp], { stdio: ["inherit", "inherit", "inherit"] });
13102
+ if (proc.exitCode === 0) {
13103
+ body = readFileSync2(tmp, "utf-8");
13104
+ }
13105
+ try {
13106
+ unlinkSync(tmp);
13107
+ } catch {}
13108
+ }
13109
+ if (isJson(program2)) {
13110
+ output(program2, { ...prompt, body });
13111
+ } else {
13112
+ console.log(body);
13113
+ if (prompt.next_prompt) {
13114
+ console.error(chalk2.gray(`
13115
+ \u2192 next: ${chalk2.bold(prompt.next_prompt)}`));
13116
+ }
13117
+ }
13118
+ } catch (e) {
13119
+ handleError(program2, e);
13120
+ }
13121
+ });
13122
+ program2.command("get <id>").description("Get prompt details without incrementing use counter").action((id) => {
13123
+ try {
13124
+ const prompt = getPrompt(id);
13125
+ if (!prompt)
13126
+ handleError(program2, `Prompt not found: ${id}`);
13127
+ output(program2, isJson(program2) ? prompt : fmtPrompt(prompt));
13128
+ } catch (e) {
13129
+ handleError(program2, e);
13130
+ }
13131
+ });
13132
+ 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) => {
13133
+ try {
13134
+ const project_id = getActiveProjectId(program2);
13135
+ let prompts = listPrompts({
13136
+ collection: opts["collection"],
13137
+ tags: opts["tags"] ? opts["tags"].split(",").map((t) => t.trim()) : undefined,
13138
+ is_template: opts["templates"] ? true : undefined,
13139
+ limit: parseInt(opts["limit"]) || 50,
13140
+ ...project_id !== null ? { project_id } : {}
13141
+ });
13142
+ if (opts["recent"]) {
13143
+ prompts = prompts.filter((p) => p.last_used_at !== null).sort((a, b) => (b.last_used_at ?? "").localeCompare(a.last_used_at ?? ""));
13144
+ }
13145
+ if (isJson(program2)) {
13146
+ output(program2, prompts);
13147
+ } else if (prompts.length === 0) {
13148
+ console.log(chalk2.gray("No prompts found."));
13149
+ } else {
13150
+ for (const p of prompts)
13151
+ console.log(fmtPrompt(p));
13152
+ console.log(chalk2.gray(`
13148
13153
  ${prompts.length} prompt(s)`));
13154
+ }
13155
+ } catch (e) {
13156
+ handleError(program2, e);
13149
13157
  }
13150
- } catch (e) {
13151
- handleError(e);
13152
- }
13153
- });
13154
- 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) => {
13155
- try {
13156
- const project_id = getActiveProjectId();
13157
- const results = searchPrompts(query, {
13158
- collection: opts["collection"],
13159
- tags: opts["tags"] ? opts["tags"].split(",").map((t) => t.trim()) : undefined,
13160
- limit: parseInt(opts["limit"] ?? "20") || 20,
13161
- ...project_id !== null ? { project_id } : {}
13162
- });
13163
- if (isJson()) {
13164
- output(results);
13165
- } else if (results.length === 0) {
13166
- console.log(chalk.gray("No results."));
13167
- } else {
13168
- for (const r of results) {
13169
- console.log(fmtPrompt(r.prompt));
13170
- if (r.snippet) {
13171
- const highlighted = r.snippet.replace(/\[([^\]]+)\]/g, (_m, word) => chalk.yellowBright(word));
13172
- console.log(chalk.gray(" ") + chalk.gray(highlighted));
13158
+ });
13159
+ 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) => {
13160
+ try {
13161
+ const project_id = getActiveProjectId(program2);
13162
+ const results = searchPrompts(query, {
13163
+ collection: opts["collection"],
13164
+ tags: opts["tags"] ? opts["tags"].split(",").map((t) => t.trim()) : undefined,
13165
+ limit: parseInt(opts["limit"] ?? "20") || 20,
13166
+ ...project_id !== null ? { project_id } : {}
13167
+ });
13168
+ if (isJson(program2)) {
13169
+ output(program2, results);
13170
+ } else if (results.length === 0) {
13171
+ console.log(chalk2.gray("No results."));
13172
+ } else {
13173
+ for (const r of results) {
13174
+ console.log(fmtPrompt(r.prompt));
13175
+ if (r.snippet) {
13176
+ const highlighted = r.snippet.replace(/\[([^\]]+)\]/g, (_m, word) => chalk2.yellowBright(word));
13177
+ console.log(chalk2.gray(" ") + chalk2.gray(highlighted));
13178
+ }
13173
13179
  }
13174
- }
13175
- console.log(chalk.gray(`
13180
+ console.log(chalk2.gray(`
13176
13181
  ${results.length} result(s)`));
13182
+ }
13183
+ } catch (e) {
13184
+ handleError(program2, e);
13177
13185
  }
13178
- } catch (e) {
13179
- handleError(e);
13180
- }
13181
- });
13182
- program2.command("render <id>").description("Render a template prompt by filling in {{variables}}").option("-v, --var <assignments...>", "Variable assignments as key=value").action((id, opts) => {
13183
- try {
13184
- const prompt = usePrompt(id);
13185
- const vars = {};
13186
- for (const assignment of opts.var ?? []) {
13187
- const eq = assignment.indexOf("=");
13188
- if (eq === -1)
13189
- handleError(`Invalid var format: ${assignment}. Use key=value`);
13190
- vars[assignment.slice(0, eq)] = assignment.slice(eq + 1);
13191
- }
13192
- const result = renderTemplate(prompt.body, vars);
13193
- if (isJson()) {
13194
- output(result);
13195
- } else {
13196
- console.log(result.rendered);
13197
- if (result.missing_vars.length > 0)
13198
- console.error(chalk.yellow(`
13186
+ });
13187
+ program2.command("render <id>").description("Render a template prompt by filling in {{variables}}").option("-v, --var <assignments...>", "Variable assignments as key=value").action((id, opts) => {
13188
+ try {
13189
+ const prompt = usePrompt(id);
13190
+ const vars = {};
13191
+ for (const assignment of opts.var ?? []) {
13192
+ const eq = assignment.indexOf("=");
13193
+ if (eq === -1)
13194
+ handleError(program2, `Invalid var format: ${assignment}. Use key=value`);
13195
+ vars[assignment.slice(0, eq)] = assignment.slice(eq + 1);
13196
+ }
13197
+ const result = renderTemplate(prompt.body, vars);
13198
+ if (isJson(program2)) {
13199
+ output(program2, result);
13200
+ } else {
13201
+ console.log(result.rendered);
13202
+ if (result.missing_vars.length > 0)
13203
+ console.error(chalk2.yellow(`
13199
13204
  Warning: missing vars: ${result.missing_vars.join(", ")}`));
13200
- if (result.used_defaults.length > 0)
13201
- console.error(chalk.gray(`Used defaults: ${result.used_defaults.join(", ")}`));
13205
+ if (result.used_defaults.length > 0)
13206
+ console.error(chalk2.gray(`Used defaults: ${result.used_defaults.join(", ")}`));
13207
+ }
13208
+ } catch (e) {
13209
+ handleError(program2, e);
13202
13210
  }
13203
- } catch (e) {
13204
- handleError(e);
13205
- }
13206
- });
13207
- program2.command("templates").description("List template prompts").option("-c, --collection <name>").action((opts) => {
13208
- try {
13209
- const prompts = listPrompts({ is_template: true, collection: opts["collection"] });
13210
- if (isJson()) {
13211
- output(prompts);
13212
- } else if (prompts.length === 0) {
13213
- console.log(chalk.gray("No templates found."));
13214
- } else {
13215
- for (const p of prompts) {
13216
- const vars = extractVariableInfo(p.body);
13217
- console.log(fmtPrompt(p));
13218
- console.log(chalk.cyan(` vars: ${vars.map((v) => v.required ? v.name : `${v.name}?`).join(", ")}`));
13211
+ });
13212
+ program2.command("templates").description("List template prompts").option("-c, --collection <name>").action((opts) => {
13213
+ try {
13214
+ const prompts = listPrompts({ is_template: true, collection: opts["collection"] });
13215
+ if (isJson(program2)) {
13216
+ output(program2, prompts);
13217
+ } else if (prompts.length === 0) {
13218
+ console.log(chalk2.gray("No templates found."));
13219
+ } else {
13220
+ for (const p of prompts) {
13221
+ const vars = extractVariableInfo(p.body);
13222
+ console.log(fmtPrompt(p));
13223
+ console.log(chalk2.cyan(` vars: ${vars.map((v) => v.required ? v.name : `${v.name}?`).join(", ")}`));
13224
+ }
13219
13225
  }
13226
+ } catch (e) {
13227
+ handleError(program2, e);
13220
13228
  }
13221
- } catch (e) {
13222
- handleError(e);
13223
- }
13224
- });
13225
- program2.command("inspect <id>").description("Show a prompt's variables (for templates)").action((id) => {
13226
- try {
13227
- const prompt = getPrompt(id);
13228
- if (!prompt)
13229
- handleError(`Prompt not found: ${id}`);
13230
- const vars = extractVariableInfo(prompt.body);
13231
- if (isJson()) {
13232
- output(vars);
13233
- } else if (vars.length === 0) {
13234
- console.log(chalk.gray("No template variables found."));
13235
- } else {
13236
- console.log(chalk.bold(`Variables for ${prompt.slug}:`));
13237
- for (const v of vars) {
13238
- const req = v.required ? chalk.red("required") : chalk.green("optional");
13239
- const def = v.default !== null ? chalk.gray(` (default: "${v.default}")`) : "";
13240
- console.log(` ${chalk.bold(v.name)} ${req}${def}`);
13229
+ });
13230
+ program2.command("inspect <id>").description("Show a prompt's variables (for templates)").action((id) => {
13231
+ try {
13232
+ const prompt = getPrompt(id);
13233
+ if (!prompt)
13234
+ handleError(program2, `Prompt not found: ${id}`);
13235
+ const vars = extractVariableInfo(prompt.body);
13236
+ if (isJson(program2)) {
13237
+ output(program2, vars);
13238
+ } else if (vars.length === 0) {
13239
+ console.log(chalk2.gray("No template variables found."));
13240
+ } else {
13241
+ console.log(chalk2.bold(`Variables for ${prompt.slug}:`));
13242
+ for (const v of vars) {
13243
+ const req = v.required ? chalk2.red("required") : chalk2.green("optional");
13244
+ const def = v.default !== null ? chalk2.gray(` (default: "${v.default}")`) : "";
13245
+ console.log(` ${chalk2.bold(v.name)} ${req}${def}`);
13246
+ }
13241
13247
  }
13248
+ } catch (e) {
13249
+ handleError(program2, e);
13242
13250
  }
13243
- } catch (e) {
13244
- handleError(e);
13245
- }
13246
- });
13247
- program2.command("update <id>").description("Update a prompt's fields").option("--title <title>").option("-b, --body <body>").option("-d, --description <desc>").option("-c, --collection <name>").option("-t, --tags <tags>").option("--agent <name>").action((id, opts) => {
13248
- try {
13249
- const prompt = updatePrompt(id, {
13250
- title: opts["title"] ?? undefined,
13251
- body: opts["body"] ?? undefined,
13252
- description: opts["description"] ?? undefined,
13253
- collection: opts["collection"] ?? undefined,
13254
- tags: opts["tags"] ? opts["tags"].split(",").map((t) => t.trim()) : undefined,
13255
- changed_by: opts["agent"] ?? undefined
13256
- });
13257
- if (isJson())
13258
- output(prompt);
13259
- else
13260
- console.log(`${chalk.yellow("Updated")} ${chalk.bold(prompt.id)} \u2014 ${chalk.green(prompt.slug)}`);
13261
- } catch (e) {
13262
- handleError(e);
13263
- }
13264
- });
13265
- program2.command("delete <id>").description("Delete a prompt").option("-y, --yes", "Skip confirmation").action(async (id, opts) => {
13266
- try {
13267
- const prompt = getPrompt(id);
13268
- if (!prompt)
13269
- handleError(`Prompt not found: ${id}`);
13270
- if (!opts.yes && !isJson()) {
13271
- const { createInterface } = await import("readline");
13272
- const rl = createInterface({ input: process.stdin, output: process.stdout });
13273
- await new Promise((resolve) => {
13274
- rl.question(chalk.yellow(`Delete "${prompt.slug}"? [y/N] `), (ans) => {
13275
- rl.close();
13276
- if (ans.toLowerCase() !== "y") {
13277
- console.log("Cancelled.");
13278
- process.exit(0);
13279
- }
13280
- resolve();
13281
- });
13251
+ });
13252
+ program2.command("update <id>").description("Update a prompt's fields").option("--title <title>").option("-b, --body <body>").option("-d, --description <desc>").option("-c, --collection <name>").option("-t, --tags <tags>").option("--agent <name>").action((id, opts) => {
13253
+ try {
13254
+ const prompt = updatePrompt(id, {
13255
+ title: opts["title"] ?? undefined,
13256
+ body: opts["body"] ?? undefined,
13257
+ description: opts["description"] ?? undefined,
13258
+ collection: opts["collection"] ?? undefined,
13259
+ tags: opts["tags"] ? opts["tags"].split(",").map((t) => t.trim()) : undefined,
13260
+ changed_by: opts["agent"] ?? undefined
13282
13261
  });
13262
+ if (isJson(program2))
13263
+ output(program2, prompt);
13264
+ else
13265
+ console.log(`${chalk2.yellow("Updated")} ${chalk2.bold(prompt.id)} \u2014 ${chalk2.green(prompt.slug)}`);
13266
+ } catch (e) {
13267
+ handleError(program2, e);
13283
13268
  }
13284
- deletePrompt(id);
13285
- if (isJson())
13286
- output({ deleted: true, id: prompt.id });
13287
- else
13288
- console.log(chalk.red(`Deleted ${prompt.slug}`));
13289
- } catch (e) {
13290
- handleError(e);
13291
- }
13292
- });
13269
+ });
13270
+ program2.command("delete <id>").description("Delete a prompt").option("-y, --yes", "Skip confirmation").action(async (id, opts) => {
13271
+ try {
13272
+ const prompt = getPrompt(id);
13273
+ if (!prompt)
13274
+ handleError(program2, `Prompt not found: ${id}`);
13275
+ if (!opts.yes && !isJson(program2)) {
13276
+ const { createInterface } = await import("readline");
13277
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
13278
+ await new Promise((resolve) => {
13279
+ rl.question(chalk2.yellow(`Delete "${prompt.slug}"? [y/N] `), (ans) => {
13280
+ rl.close();
13281
+ if (ans.toLowerCase() !== "y") {
13282
+ console.log("Cancelled.");
13283
+ process.exit(0);
13284
+ }
13285
+ resolve();
13286
+ });
13287
+ });
13288
+ }
13289
+ deletePrompt(id);
13290
+ if (isJson(program2))
13291
+ output(program2, { deleted: true, id: prompt.id });
13292
+ else
13293
+ console.log(chalk2.red(`Deleted ${prompt.slug}`));
13294
+ } catch (e) {
13295
+ handleError(program2, e);
13296
+ }
13297
+ });
13298
+ program2.command("similar <id>").description("Find prompts similar to a given prompt (by tag overlap and collection)").option("-n, --limit <n>", "Max results", "5").action((id, opts) => {
13299
+ try {
13300
+ const prompt = getPrompt(id);
13301
+ if (!prompt)
13302
+ handleError(program2, `Prompt not found: ${id}`);
13303
+ const results = findSimilar(prompt.id, parseInt(opts["limit"] ?? "5") || 5);
13304
+ if (isJson(program2)) {
13305
+ output(program2, results);
13306
+ return;
13307
+ }
13308
+ if (results.length === 0) {
13309
+ console.log(chalk2.gray("No similar prompts found."));
13310
+ return;
13311
+ }
13312
+ for (const r of results) {
13313
+ const score = chalk2.gray(`${Math.round(r.score * 100)}%`);
13314
+ console.log(`${fmtPrompt(r.prompt)} ${score}`);
13315
+ }
13316
+ } catch (e) {
13317
+ handleError(program2, e);
13318
+ }
13319
+ });
13320
+ }
13321
+
13322
+ // src/cli/index.tsx
13323
+ var require2 = createRequire2(import.meta.url);
13324
+ var pkg = require2("../../package.json");
13325
+ 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");
13326
+ registerPromptCommands(program2);
13293
13327
  program2.command("history <id>").description("Show version history for a prompt").action((id) => {
13294
13328
  try {
13295
13329
  const prompt = getPrompt(id);
13296
13330
  if (!prompt)
13297
- handleError(`Prompt not found: ${id}`);
13331
+ handleError(program2, `Prompt not found: ${id}`);
13298
13332
  const versions = listVersions(prompt.id);
13299
- if (isJson()) {
13300
- output(versions);
13333
+ if (isJson(program2)) {
13334
+ output(program2, versions);
13301
13335
  } else {
13302
- console.log(chalk.bold(`Version history for ${prompt.slug}:`));
13336
+ console.log(chalk3.bold(`Version history for ${prompt.slug}:`));
13303
13337
  for (const v of versions) {
13304
- const current = v.version === prompt.version ? chalk.green(" \u2190 current") : "";
13305
- const by = v.changed_by ? chalk.gray(` by ${v.changed_by}`) : "";
13306
- console.log(` v${v.version} ${chalk.gray(v.created_at)}${by}${current}`);
13338
+ const current = v.version === prompt.version ? chalk3.green(" \u2190 current") : "";
13339
+ const by = v.changed_by ? chalk3.gray(` by ${v.changed_by}`) : "";
13340
+ console.log(` v${v.version} ${chalk3.gray(v.created_at)}${by}${current}`);
13307
13341
  }
13308
13342
  }
13309
13343
  } catch (e) {
13310
- handleError(e);
13344
+ handleError(program2, e);
13311
13345
  }
13312
13346
  });
13313
13347
  program2.command("restore <id> <version>").description("Restore a prompt to a previous version").option("--agent <name>").action((id, version, opts) => {
13314
13348
  try {
13315
13349
  const prompt = getPrompt(id);
13316
13350
  if (!prompt)
13317
- handleError(`Prompt not found: ${id}`);
13351
+ handleError(program2, `Prompt not found: ${id}`);
13318
13352
  restoreVersion(prompt.id, parseInt(version), opts["agent"]);
13319
- if (isJson())
13320
- output({ restored: true, id: prompt.id, version: parseInt(version) });
13353
+ if (isJson(program2))
13354
+ output(program2, { restored: true, id: prompt.id, version: parseInt(version) });
13321
13355
  else
13322
- console.log(chalk.green(`Restored ${prompt.slug} to v${version}`));
13356
+ console.log(chalk3.green(`Restored ${prompt.slug} to v${version}`));
13323
13357
  } catch (e) {
13324
- handleError(e);
13358
+ handleError(program2, e);
13325
13359
  }
13326
13360
  });
13327
13361
  program2.command("collections").description("List all collections").action(() => {
13328
13362
  try {
13329
13363
  const cols = listCollections();
13330
- if (isJson()) {
13331
- output(cols);
13364
+ if (isJson(program2)) {
13365
+ output(program2, cols);
13332
13366
  } else {
13333
13367
  for (const c of cols) {
13334
- console.log(`${chalk.bold(c.name)} ${chalk.gray(`${c.prompt_count} prompt(s)`)}`);
13368
+ console.log(`${chalk3.bold(c.name)} ${chalk3.gray(`${c.prompt_count} prompt(s)`)}`);
13335
13369
  if (c.description)
13336
- console.log(chalk.gray(" " + c.description));
13370
+ console.log(chalk3.gray(" " + c.description));
13337
13371
  }
13338
13372
  }
13339
13373
  } catch (e) {
13340
- handleError(e);
13374
+ handleError(program2, e);
13341
13375
  }
13342
13376
  });
13343
13377
  program2.command("move <id> <collection>").description("Move a prompt to a different collection").action((id, collection) => {
13344
13378
  try {
13345
13379
  movePrompt(id, collection);
13346
- if (isJson())
13347
- output({ moved: true, id, collection });
13380
+ if (isJson(program2))
13381
+ output(program2, { moved: true, id, collection });
13348
13382
  else
13349
- console.log(`${chalk.green("Moved")} ${id} \u2192 ${chalk.bold(collection)}`);
13383
+ console.log(`${chalk3.green("Moved")} ${id} \u2192 ${chalk3.bold(collection)}`);
13350
13384
  } catch (e) {
13351
- handleError(e);
13385
+ handleError(program2, e);
13352
13386
  }
13353
13387
  });
13354
13388
  program2.command("export").description("Export prompts as JSON").option("-c, --collection <name>").option("-o, --output <file>", "Write to file instead of stdout").action(async (opts) => {
@@ -13358,12 +13392,12 @@ program2.command("export").description("Export prompts as JSON").option("-c, --c
13358
13392
  if (opts["output"]) {
13359
13393
  const { writeFileSync: writeFileSync2 } = await import("fs");
13360
13394
  writeFileSync2(opts["output"], json);
13361
- console.log(chalk.green(`Exported ${data.prompts.length} prompt(s) to ${opts["output"]}`));
13395
+ console.log(chalk3.green(`Exported ${data.prompts.length} prompt(s) to ${opts["output"]}`));
13362
13396
  } else {
13363
13397
  console.log(json);
13364
13398
  }
13365
13399
  } catch (e) {
13366
- handleError(e);
13400
+ handleError(program2, e);
13367
13401
  }
13368
13402
  });
13369
13403
  program2.command("import <file>").description("Import prompts from a JSON file").option("--agent <name>").action(async (file, opts) => {
@@ -13372,100 +13406,100 @@ program2.command("import <file>").description("Import prompts from a JSON file")
13372
13406
  const raw = JSON.parse(readFileSync2(file, "utf-8"));
13373
13407
  const items = Array.isArray(raw) ? raw : raw.prompts ?? [];
13374
13408
  const results = importFromJson(items, opts["agent"]);
13375
- if (isJson())
13376
- output(results);
13409
+ if (isJson(program2))
13410
+ output(program2, results);
13377
13411
  else {
13378
- console.log(chalk.green(`Created: ${results.created}, Updated: ${results.updated}`));
13412
+ console.log(chalk3.green(`Created: ${results.created}, Updated: ${results.updated}`));
13379
13413
  if (results.errors.length > 0) {
13380
- console.error(chalk.red(`Errors: ${results.errors.length}`));
13414
+ console.error(chalk3.red(`Errors: ${results.errors.length}`));
13381
13415
  for (const e of results.errors)
13382
- console.error(chalk.red(` ${e.item}: ${e.error}`));
13416
+ console.error(chalk3.red(` ${e.item}: ${e.error}`));
13383
13417
  }
13384
13418
  }
13385
13419
  } catch (e) {
13386
- handleError(e);
13420
+ handleError(program2, e);
13387
13421
  }
13388
13422
  });
13389
13423
  program2.command("stats").description("Show usage statistics").action(() => {
13390
13424
  try {
13391
13425
  const stats = getPromptStats();
13392
- if (isJson()) {
13393
- output(stats);
13426
+ if (isJson(program2)) {
13427
+ output(program2, stats);
13394
13428
  } else {
13395
- console.log(chalk.bold("Prompt Stats"));
13429
+ console.log(chalk3.bold("Prompt Stats"));
13396
13430
  console.log(` Total: ${stats.total_prompts} Templates: ${stats.total_templates} Collections: ${stats.total_collections}`);
13397
13431
  if (stats.most_used.length > 0) {
13398
- console.log(chalk.bold(`
13432
+ console.log(chalk3.bold(`
13399
13433
  Most used:`));
13400
13434
  for (const p of stats.most_used)
13401
- console.log(` ${chalk.green(p.slug)} ${chalk.gray(`${p.use_count}\xD7`)}`);
13435
+ console.log(` ${chalk3.green(p.slug)} ${chalk3.gray(`${p.use_count}\xD7`)}`);
13402
13436
  }
13403
13437
  if (stats.by_collection.length > 0) {
13404
- console.log(chalk.bold(`
13438
+ console.log(chalk3.bold(`
13405
13439
  By collection:`));
13406
13440
  for (const c of stats.by_collection)
13407
- console.log(` ${chalk.bold(c.collection)} ${chalk.gray(`${c.count}`)}`);
13441
+ console.log(` ${chalk3.bold(c.collection)} ${chalk3.gray(`${c.count}`)}`);
13408
13442
  }
13409
13443
  }
13410
13444
  } catch (e) {
13411
- handleError(e);
13445
+ handleError(program2, e);
13412
13446
  }
13413
13447
  });
13414
13448
  program2.command("recent [n]").description("Show recently used prompts (default: 10)").action((n) => {
13415
13449
  try {
13416
13450
  const limit = parseInt(n ?? "10") || 10;
13417
13451
  const prompts = listPrompts({ limit }).filter((p) => p.last_used_at !== null).sort((a, b) => (b.last_used_at ?? "").localeCompare(a.last_used_at ?? "")).slice(0, limit);
13418
- if (isJson()) {
13419
- output(prompts);
13452
+ if (isJson(program2)) {
13453
+ output(program2, prompts);
13420
13454
  return;
13421
13455
  }
13422
13456
  if (prompts.length === 0) {
13423
- console.log(chalk.gray("No recently used prompts."));
13457
+ console.log(chalk3.gray("No recently used prompts."));
13424
13458
  return;
13425
13459
  }
13426
13460
  for (const p of prompts) {
13427
- const ago = chalk.gray(new Date(p.last_used_at).toLocaleString());
13428
- console.log(`${chalk.bold(p.id)} ${chalk.green(p.slug)} ${p.title} ${ago}`);
13461
+ const ago = chalk3.gray(new Date(p.last_used_at).toLocaleString());
13462
+ console.log(`${chalk3.bold(p.id)} ${chalk3.green(p.slug)} ${p.title} ${ago}`);
13429
13463
  }
13430
13464
  } catch (e) {
13431
- handleError(e);
13465
+ handleError(program2, e);
13432
13466
  }
13433
13467
  });
13434
13468
  program2.command("lint").description("Check prompt quality: missing descriptions, undocumented vars, short bodies, no tags").option("-c, --collection <name>", "Lint only this collection").action((opts) => {
13435
13469
  try {
13436
13470
  const prompts = listPrompts({ collection: opts["collection"], limit: 1e4 });
13437
13471
  const results = lintAll(prompts);
13438
- if (isJson()) {
13439
- output(results);
13472
+ if (isJson(program2)) {
13473
+ output(program2, results);
13440
13474
  return;
13441
13475
  }
13442
13476
  if (results.length === 0) {
13443
- console.log(chalk.green("\u2713 All prompts pass lint."));
13477
+ console.log(chalk3.green("\u2713 All prompts pass lint."));
13444
13478
  return;
13445
13479
  }
13446
13480
  let errors = 0, warns = 0, infos = 0;
13447
13481
  for (const { prompt: p, issues } of results) {
13448
13482
  console.log(`
13449
- ${chalk.bold(p.slug)} ${chalk.gray(p.id)}`);
13483
+ ${chalk3.bold(p.slug)} ${chalk3.gray(p.id)}`);
13450
13484
  for (const issue of issues) {
13451
13485
  if (issue.severity === "error") {
13452
- console.log(chalk.red(` \u2717 [${issue.rule}] ${issue.message}`));
13486
+ console.log(chalk3.red(` \u2717 [${issue.rule}] ${issue.message}`));
13453
13487
  errors++;
13454
13488
  } else if (issue.severity === "warn") {
13455
- console.log(chalk.yellow(` \u26A0 [${issue.rule}] ${issue.message}`));
13489
+ console.log(chalk3.yellow(` \u26A0 [${issue.rule}] ${issue.message}`));
13456
13490
  warns++;
13457
13491
  } else {
13458
- console.log(chalk.gray(` \u2139 [${issue.rule}] ${issue.message}`));
13492
+ console.log(chalk3.gray(` \u2139 [${issue.rule}] ${issue.message}`));
13459
13493
  infos++;
13460
13494
  }
13461
13495
  }
13462
13496
  }
13463
- console.log(chalk.bold(`
13497
+ console.log(chalk3.bold(`
13464
13498
  ${results.length} prompt(s) with issues \u2014 ${errors} errors, ${warns} warnings, ${infos} info`));
13465
13499
  if (errors > 0)
13466
13500
  process.exit(1);
13467
13501
  } catch (e) {
13468
- handleError(e);
13502
+ handleError(program2, e);
13469
13503
  }
13470
13504
  });
13471
13505
  program2.command("stale [days]").description("List prompts not used in N days (default: 30)").action((days) => {
@@ -13476,54 +13510,54 @@ program2.command("stale [days]").description("List prompts not used in N days (d
13476
13510
  const stale = all.filter((p) => p.last_used_at === null || p.last_used_at < cutoff).sort((a, b) => (a.last_used_at ?? "").localeCompare(b.last_used_at ?? ""));
13477
13511
  const now = new Date().toISOString();
13478
13512
  const expired = all.filter((p) => p.expires_at !== null && p.expires_at < now);
13479
- if (isJson()) {
13480
- output({ stale, expired });
13513
+ if (isJson(program2)) {
13514
+ output(program2, { stale, expired });
13481
13515
  return;
13482
13516
  }
13483
13517
  if (expired.length > 0) {
13484
- console.log(chalk.red(`
13518
+ console.log(chalk3.red(`
13485
13519
  Expired (${expired.length}):`));
13486
13520
  for (const p of expired)
13487
- console.log(chalk.red(` \u2717 ${p.slug}`) + chalk.gray(` expired ${new Date(p.expires_at).toLocaleDateString()}`));
13521
+ console.log(chalk3.red(` \u2717 ${p.slug}`) + chalk3.gray(` expired ${new Date(p.expires_at).toLocaleDateString()}`));
13488
13522
  }
13489
13523
  if (stale.length === 0 && expired.length === 0) {
13490
- console.log(chalk.green(`No stale prompts (threshold: ${threshold} days).`));
13524
+ console.log(chalk3.green(`No stale prompts (threshold: ${threshold} days).`));
13491
13525
  return;
13492
13526
  }
13493
13527
  if (stale.length > 0) {
13494
- console.log(chalk.bold(`
13528
+ console.log(chalk3.bold(`
13495
13529
  Stale prompts (not used in ${threshold}+ days):`));
13496
13530
  for (const p of stale) {
13497
- const last = p.last_used_at ? chalk.gray(new Date(p.last_used_at).toLocaleDateString()) : chalk.red("never");
13498
- console.log(` ${chalk.green(p.slug)} ${chalk.gray(`${p.use_count}\xD7`)} last used: ${last}`);
13531
+ const last = p.last_used_at ? chalk3.gray(new Date(p.last_used_at).toLocaleDateString()) : chalk3.red("never");
13532
+ console.log(` ${chalk3.green(p.slug)} ${chalk3.gray(`${p.use_count}\xD7`)} last used: ${last}`);
13499
13533
  }
13500
13534
  }
13501
- console.log(chalk.gray(`
13535
+ console.log(chalk3.gray(`
13502
13536
  ${stale.length} stale prompt(s)`));
13503
13537
  } catch (e) {
13504
- handleError(e);
13538
+ handleError(program2, e);
13505
13539
  }
13506
13540
  });
13507
13541
  program2.command("pin <id>").description("Pin a prompt so it always appears first in lists").action((id) => {
13508
13542
  try {
13509
13543
  const p = pinPrompt(id, true);
13510
- if (isJson())
13511
- output(p);
13544
+ if (isJson(program2))
13545
+ output(program2, p);
13512
13546
  else
13513
- console.log(chalk.yellow(`\uD83D\uDCCC Pinned ${chalk.bold(p.slug)}`));
13547
+ console.log(chalk3.yellow(`\uD83D\uDCCC Pinned ${chalk3.bold(p.slug)}`));
13514
13548
  } catch (e) {
13515
- handleError(e);
13549
+ handleError(program2, e);
13516
13550
  }
13517
13551
  });
13518
13552
  program2.command("unpin <id>").description("Unpin a prompt").action((id) => {
13519
13553
  try {
13520
13554
  const p = pinPrompt(id, false);
13521
- if (isJson())
13522
- output(p);
13555
+ if (isJson(program2))
13556
+ output(program2, p);
13523
13557
  else
13524
- console.log(chalk.gray(`Unpinned ${chalk.bold(p.slug)}`));
13558
+ console.log(chalk3.gray(`Unpinned ${chalk3.bold(p.slug)}`));
13525
13559
  } catch (e) {
13526
- handleError(e);
13560
+ handleError(program2, e);
13527
13561
  }
13528
13562
  });
13529
13563
  program2.command("copy <id>").description("Copy prompt body to clipboard and increment use counter").action(async (id) => {
@@ -13548,96 +13582,96 @@ program2.command("copy <id>").description("Copy prompt body to clipboard and inc
13548
13582
  await proc.exited;
13549
13583
  }
13550
13584
  } else {
13551
- handleError("Clipboard not supported on this platform. Use `prompts use` instead.");
13585
+ handleError(program2, "Clipboard not supported on this platform. Use `prompts use` instead.");
13552
13586
  }
13553
- if (isJson())
13554
- output({ copied: true, id: prompt.id, slug: prompt.slug });
13587
+ if (isJson(program2))
13588
+ output(program2, { copied: true, id: prompt.id, slug: prompt.slug });
13555
13589
  else
13556
- console.log(chalk.green(`Copied ${chalk.bold(prompt.slug)} to clipboard`));
13590
+ console.log(chalk3.green(`Copied ${chalk3.bold(prompt.slug)} to clipboard`));
13557
13591
  } catch (e) {
13558
- handleError(e);
13592
+ handleError(program2, e);
13559
13593
  }
13560
13594
  });
13561
13595
  var projectCmd = program2.command("project").description("Manage projects");
13562
13596
  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) => {
13563
13597
  try {
13564
13598
  const project = createProject({ name, description: opts["description"], path: opts["path"] });
13565
- if (isJson())
13566
- output(project);
13599
+ if (isJson(program2))
13600
+ output(program2, project);
13567
13601
  else {
13568
- console.log(`${chalk.green("Created")} project ${chalk.bold(project.name)} \u2014 ${chalk.gray(project.slug)}`);
13602
+ console.log(`${chalk3.green("Created")} project ${chalk3.bold(project.name)} \u2014 ${chalk3.gray(project.slug)}`);
13569
13603
  if (project.description)
13570
- console.log(chalk.gray(` ${project.description}`));
13604
+ console.log(chalk3.gray(` ${project.description}`));
13571
13605
  }
13572
13606
  } catch (e) {
13573
- handleError(e);
13607
+ handleError(program2, e);
13574
13608
  }
13575
13609
  });
13576
13610
  projectCmd.command("list").description("List all projects").action(() => {
13577
13611
  try {
13578
13612
  const projects = listProjects();
13579
- if (isJson()) {
13580
- output(projects);
13613
+ if (isJson(program2)) {
13614
+ output(program2, projects);
13581
13615
  return;
13582
13616
  }
13583
13617
  if (projects.length === 0) {
13584
- console.log(chalk.gray("No projects."));
13618
+ console.log(chalk3.gray("No projects."));
13585
13619
  return;
13586
13620
  }
13587
13621
  for (const p of projects) {
13588
- console.log(`${chalk.bold(p.name)} ${chalk.gray(p.slug)} ${chalk.cyan(`${p.prompt_count} prompt(s)`)}`);
13622
+ console.log(`${chalk3.bold(p.name)} ${chalk3.gray(p.slug)} ${chalk3.cyan(`${p.prompt_count} prompt(s)`)}`);
13589
13623
  if (p.description)
13590
- console.log(chalk.gray(` ${p.description}`));
13624
+ console.log(chalk3.gray(` ${p.description}`));
13591
13625
  }
13592
13626
  } catch (e) {
13593
- handleError(e);
13627
+ handleError(program2, e);
13594
13628
  }
13595
13629
  });
13596
13630
  projectCmd.command("get <id>").description("Get project details").action((id) => {
13597
13631
  try {
13598
13632
  const project = getProject(id);
13599
13633
  if (!project)
13600
- handleError(`Project not found: ${id}`);
13601
- output(isJson() ? project : `${chalk.bold(project.name)} ${chalk.gray(project.slug)} ${chalk.cyan(`${project.prompt_count} prompt(s)`)}`);
13634
+ handleError(program2, `Project not found: ${id}`);
13635
+ output(program2, isJson(program2) ? project : `${chalk3.bold(project.name)} ${chalk3.gray(project.slug)} ${chalk3.cyan(`${project.prompt_count} prompt(s)`)}`);
13602
13636
  } catch (e) {
13603
- handleError(e);
13637
+ handleError(program2, e);
13604
13638
  }
13605
13639
  });
13606
13640
  projectCmd.command("prompts <id>").description("List all prompts for a project (project-scoped + globals)").option("-n, --limit <n>", "Max results", "100").action((id, opts) => {
13607
13641
  try {
13608
13642
  const project = getProject(id);
13609
13643
  if (!project)
13610
- handleError(`Project not found: ${id}`);
13644
+ handleError(program2, `Project not found: ${id}`);
13611
13645
  const prompts = listPrompts({ project_id: project.id, limit: parseInt(opts["limit"] ?? "100") || 100 });
13612
- if (isJson()) {
13613
- output(prompts);
13646
+ if (isJson(program2)) {
13647
+ output(program2, prompts);
13614
13648
  return;
13615
13649
  }
13616
13650
  if (prompts.length === 0) {
13617
- console.log(chalk.gray("No prompts."));
13651
+ console.log(chalk3.gray("No prompts."));
13618
13652
  return;
13619
13653
  }
13620
- console.log(chalk.bold(`Prompts for project: ${project.name}`));
13654
+ console.log(chalk3.bold(`Prompts for project: ${project.name}`));
13621
13655
  for (const p of prompts) {
13622
- const scope = p.project_id ? chalk.cyan(" [project]") : chalk.gray(" [global]");
13656
+ const scope = p.project_id ? chalk3.cyan(" [project]") : chalk3.gray(" [global]");
13623
13657
  console.log(fmtPrompt(p) + scope);
13624
13658
  }
13625
- console.log(chalk.gray(`
13659
+ console.log(chalk3.gray(`
13626
13660
  ${prompts.length} prompt(s)`));
13627
13661
  } catch (e) {
13628
- handleError(e);
13662
+ handleError(program2, e);
13629
13663
  }
13630
13664
  });
13631
13665
  projectCmd.command("delete <id>").description("Delete a project (prompts become global)").option("-y, --yes", "Skip confirmation").action(async (id, opts) => {
13632
13666
  try {
13633
13667
  const project = getProject(id);
13634
13668
  if (!project)
13635
- handleError(`Project not found: ${id}`);
13636
- if (!opts.yes && !isJson()) {
13669
+ handleError(program2, `Project not found: ${id}`);
13670
+ if (!opts.yes && !isJson(program2)) {
13637
13671
  const { createInterface } = await import("readline");
13638
13672
  const rl = createInterface({ input: process.stdin, output: process.stdout });
13639
13673
  await new Promise((resolve) => {
13640
- rl.question(chalk.yellow(`Delete project "${project.name}"? Prompts will become global. [y/N] `), (ans) => {
13674
+ rl.question(chalk3.yellow(`Delete project "${project.name}"? Prompts will become global. [y/N] `), (ans) => {
13641
13675
  rl.close();
13642
13676
  if (ans.toLowerCase() !== "y") {
13643
13677
  console.log("Cancelled.");
@@ -13648,94 +13682,94 @@ projectCmd.command("delete <id>").description("Delete a project (prompts become
13648
13682
  });
13649
13683
  }
13650
13684
  deleteProject(id);
13651
- if (isJson())
13652
- output({ deleted: true, id: project.id });
13685
+ if (isJson(program2))
13686
+ output(program2, { deleted: true, id: project.id });
13653
13687
  else
13654
- console.log(chalk.red(`Deleted project ${project.name}`));
13688
+ console.log(chalk3.red(`Deleted project ${project.name}`));
13655
13689
  } catch (e) {
13656
- handleError(e);
13690
+ handleError(program2, e);
13657
13691
  }
13658
13692
  });
13659
13693
  program2.command("audit").description("Check for orphaned project refs, empty collections, missing history, near-duplicate slugs, expired prompts").action(() => {
13660
13694
  try {
13661
13695
  const report = runAudit();
13662
- if (isJson()) {
13663
- output(report);
13696
+ if (isJson(program2)) {
13697
+ output(program2, report);
13664
13698
  return;
13665
13699
  }
13666
13700
  if (report.issues.length === 0) {
13667
- console.log(chalk.green("\u2713 No audit issues found."));
13701
+ console.log(chalk3.green("\u2713 No audit issues found."));
13668
13702
  return;
13669
13703
  }
13670
13704
  for (const issue of report.issues) {
13671
- const sym = issue.severity === "error" ? chalk.red("\u2717") : issue.severity === "warn" ? chalk.yellow("\u26A0") : chalk.gray("\u2139");
13672
- const slug = issue.slug ? chalk.green(` ${issue.slug}`) : "";
13705
+ const sym = issue.severity === "error" ? chalk3.red("\u2717") : issue.severity === "warn" ? chalk3.yellow("\u26A0") : chalk3.gray("\u2139");
13706
+ const slug = issue.slug ? chalk3.green(` ${issue.slug}`) : "";
13673
13707
  console.log(`${sym}${slug} ${issue.message}`);
13674
13708
  }
13675
- console.log(chalk.bold(`
13709
+ console.log(chalk3.bold(`
13676
13710
  ${report.issues.length} issue(s) \u2014 ${report.errors} errors, ${report.warnings} warnings, ${report.info} info`));
13677
13711
  if (report.errors > 0)
13678
13712
  process.exit(1);
13679
13713
  } catch (e) {
13680
- handleError(e);
13714
+ handleError(program2, e);
13681
13715
  }
13682
13716
  });
13683
13717
  program2.command("unused").description("List prompts that have never been used (use_count = 0)").option("-c, --collection <name>").option("-n, --limit <n>", "Max results", "50").action((opts) => {
13684
13718
  try {
13685
13719
  const all = listPrompts({ collection: opts["collection"], limit: parseInt(opts["limit"] ?? "50") || 50 });
13686
13720
  const unused = all.filter((p) => p.use_count === 0).sort((a, b) => a.created_at.localeCompare(b.created_at));
13687
- if (isJson()) {
13688
- output(unused);
13721
+ if (isJson(program2)) {
13722
+ output(program2, unused);
13689
13723
  return;
13690
13724
  }
13691
13725
  if (unused.length === 0) {
13692
- console.log(chalk.green("All prompts have been used at least once."));
13726
+ console.log(chalk3.green("All prompts have been used at least once."));
13693
13727
  return;
13694
13728
  }
13695
- console.log(chalk.bold(`Unused prompts (${unused.length}):`));
13729
+ console.log(chalk3.bold(`Unused prompts (${unused.length}):`));
13696
13730
  for (const p of unused) {
13697
- console.log(` ${fmtPrompt(p)} ${chalk.gray(`created ${new Date(p.created_at).toLocaleDateString()}`)}`);
13731
+ console.log(` ${fmtPrompt(p)} ${chalk3.gray(`created ${new Date(p.created_at).toLocaleDateString()}`)}`);
13698
13732
  }
13699
13733
  } catch (e) {
13700
- handleError(e);
13734
+ handleError(program2, e);
13701
13735
  }
13702
13736
  });
13703
13737
  program2.command("trending").description("Most used prompts in the last N days").option("--days <n>", "Lookback window in days", "7").option("-n, --limit <n>", "Max results", "10").action((opts) => {
13704
13738
  try {
13705
13739
  const results = getTrending(parseInt(opts["days"] ?? "7") || 7, parseInt(opts["limit"] ?? "10") || 10);
13706
- if (isJson()) {
13707
- output(results);
13740
+ if (isJson(program2)) {
13741
+ output(program2, results);
13708
13742
  return;
13709
13743
  }
13710
13744
  if (results.length === 0) {
13711
- console.log(chalk.gray("No usage data yet."));
13745
+ console.log(chalk3.gray("No usage data yet."));
13712
13746
  return;
13713
13747
  }
13714
- console.log(chalk.bold(`Trending (last ${opts["days"] ?? "7"} days):`));
13748
+ console.log(chalk3.bold(`Trending (last ${opts["days"] ?? "7"} days):`));
13715
13749
  for (const r of results) {
13716
- console.log(` ${chalk.green(r.slug)} ${chalk.bold(String(r.uses))}\xD7 ${chalk.gray(r.title)}`);
13750
+ console.log(` ${chalk3.green(r.slug)} ${chalk3.bold(String(r.uses))}\xD7 ${chalk3.gray(r.title)}`);
13717
13751
  }
13718
13752
  } catch (e) {
13719
- handleError(e);
13753
+ handleError(program2, e);
13720
13754
  }
13721
13755
  });
13722
13756
  program2.command("expire <id> [date]").description("Set expiry date for a prompt (ISO date, e.g. 2026-12-31). Use 'none' to clear.").action((id, date) => {
13723
13757
  try {
13724
13758
  const expiresAt = !date || date === "none" ? null : new Date(date).toISOString();
13725
13759
  const p = setExpiry(id, expiresAt);
13726
- if (isJson())
13727
- output(p);
13760
+ if (isJson(program2))
13761
+ output(program2, p);
13728
13762
  else
13729
- console.log(expiresAt ? chalk.yellow(`Expires ${p.slug} on ${new Date(expiresAt).toLocaleDateString()}`) : chalk.gray(`Cleared expiry for ${p.slug}`));
13763
+ console.log(expiresAt ? chalk3.yellow(`Expires ${p.slug} on ${new Date(expiresAt).toLocaleDateString()}`) : chalk3.gray(`Cleared expiry for ${p.slug}`));
13730
13764
  } catch (e) {
13731
- handleError(e);
13765
+ handleError(program2, e);
13732
13766
  }
13733
13767
  });
13734
13768
  program2.command("duplicate <id>").description("Clone a prompt with a new slug").option("-s, --to <slug>", "New slug (auto-generated if omitted)").option("--title <title>", "New title (defaults to 'Copy of <original>')").action((id, opts) => {
13735
13769
  try {
13736
13770
  const source = getPrompt(id);
13737
13771
  if (!source)
13738
- handleError(`Prompt not found: ${id}`);
13772
+ handleError(program2, `Prompt not found: ${id}`);
13739
13773
  const p = source;
13740
13774
  const { prompt } = upsertPrompt({
13741
13775
  title: opts["title"] ?? `Copy of ${p.title}`,
@@ -13746,43 +13780,43 @@ program2.command("duplicate <id>").description("Clone a prompt with a new slug")
13746
13780
  tags: p.tags,
13747
13781
  source: "manual"
13748
13782
  });
13749
- if (isJson())
13750
- output(prompt);
13783
+ if (isJson(program2))
13784
+ output(program2, prompt);
13751
13785
  else
13752
- console.log(`${chalk.green("Duplicated")} ${chalk.bold(p.slug)} \u2192 ${chalk.bold(prompt.slug)}`);
13786
+ console.log(`${chalk3.green("Duplicated")} ${chalk3.bold(p.slug)} \u2192 ${chalk3.bold(prompt.slug)}`);
13753
13787
  } catch (e) {
13754
- handleError(e);
13788
+ handleError(program2, e);
13755
13789
  }
13756
13790
  });
13757
13791
  program2.command("diff <id> <v1> [v2]").description("Show diff between two versions of a prompt (v2 defaults to current)").action((id, v1, v2) => {
13758
13792
  try {
13759
13793
  const prompt = getPrompt(id);
13760
13794
  if (!prompt)
13761
- handleError(`Prompt not found: ${id}`);
13795
+ handleError(program2, `Prompt not found: ${id}`);
13762
13796
  const versions = listVersions(prompt.id);
13763
13797
  const versionA = versions.find((v) => v.version === parseInt(v1));
13764
13798
  if (!versionA)
13765
- handleError(`Version ${v1} not found`);
13799
+ handleError(program2, `Version ${v1} not found`);
13766
13800
  const bodyB = v2 ? versions.find((v) => v.version === parseInt(v2))?.body ?? null : prompt.body;
13767
13801
  if (bodyB === null)
13768
- handleError(`Version ${v2} not found`);
13802
+ handleError(program2, `Version ${v2} not found`);
13769
13803
  const lines = diffTexts(versionA.body, bodyB);
13770
- if (isJson()) {
13771
- output(lines);
13804
+ if (isJson(program2)) {
13805
+ output(program2, lines);
13772
13806
  return;
13773
13807
  }
13774
13808
  const label2 = v2 ? `v${v2}` : "current";
13775
- console.log(chalk.bold(`${prompt.slug}: v${v1} \u2192 ${label2}`));
13809
+ console.log(chalk3.bold(`${prompt.slug}: v${v1} \u2192 ${label2}`));
13776
13810
  for (const l of lines) {
13777
13811
  if (l.type === "added")
13778
- console.log(chalk.green(`+ ${l.content}`));
13812
+ console.log(chalk3.green(`+ ${l.content}`));
13779
13813
  else if (l.type === "removed")
13780
- console.log(chalk.red(`- ${l.content}`));
13814
+ console.log(chalk3.red(`- ${l.content}`));
13781
13815
  else
13782
- console.log(chalk.gray(` ${l.content}`));
13816
+ console.log(chalk3.gray(` ${l.content}`));
13783
13817
  }
13784
13818
  } catch (e) {
13785
- handleError(e);
13819
+ handleError(program2, e);
13786
13820
  }
13787
13821
  });
13788
13822
  program2.command("chain <id> [next]").description("Set the next prompt in a chain, or show the chain for a prompt. Use 'none' to clear.").action((id, next) => {
@@ -13790,15 +13824,15 @@ program2.command("chain <id> [next]").description("Set the next prompt in a chai
13790
13824
  if (next !== undefined) {
13791
13825
  const nextSlug = next === "none" ? null : next;
13792
13826
  const p = setNextPrompt(id, nextSlug);
13793
- if (isJson())
13794
- output(p);
13827
+ if (isJson(program2))
13828
+ output(program2, p);
13795
13829
  else
13796
- console.log(nextSlug ? `${chalk.green(p.slug)} \u2192 ${chalk.bold(nextSlug)}` : chalk.gray(`Cleared chain for ${p.slug}`));
13830
+ console.log(nextSlug ? `${chalk3.green(p.slug)} \u2192 ${chalk3.bold(nextSlug)}` : chalk3.gray(`Cleared chain for ${p.slug}`));
13797
13831
  return;
13798
13832
  }
13799
13833
  const prompt = getPrompt(id);
13800
13834
  if (!prompt)
13801
- handleError(`Prompt not found: ${id}`);
13835
+ handleError(program2, `Prompt not found: ${id}`);
13802
13836
  const chain = [];
13803
13837
  let cur = prompt;
13804
13838
  const seen = new Set;
@@ -13807,35 +13841,13 @@ program2.command("chain <id> [next]").description("Set the next prompt in a chai
13807
13841
  seen.add(cur.id);
13808
13842
  cur = cur.next_prompt ? getPrompt(cur.next_prompt) : null;
13809
13843
  }
13810
- if (isJson()) {
13811
- output(chain);
13844
+ if (isJson(program2)) {
13845
+ output(program2, chain);
13812
13846
  return;
13813
13847
  }
13814
- console.log(chain.map((c) => chalk.green(c.slug)).join(chalk.gray(" \u2192 ")));
13815
- } catch (e) {
13816
- handleError(e);
13817
- }
13818
- });
13819
- program2.command("similar <id>").description("Find prompts similar to a given prompt (by tag overlap and collection)").option("-n, --limit <n>", "Max results", "5").action((id, opts) => {
13820
- try {
13821
- const prompt = getPrompt(id);
13822
- if (!prompt)
13823
- handleError(`Prompt not found: ${id}`);
13824
- const results = findSimilar(prompt.id, parseInt(opts["limit"] ?? "5") || 5);
13825
- if (isJson()) {
13826
- output(results);
13827
- return;
13828
- }
13829
- if (results.length === 0) {
13830
- console.log(chalk.gray("No similar prompts found."));
13831
- return;
13832
- }
13833
- for (const r of results) {
13834
- const score = chalk.gray(`${Math.round(r.score * 100)}%`);
13835
- console.log(`${fmtPrompt(r.prompt)} ${score}`);
13836
- }
13848
+ console.log(chain.map((c) => chalk3.green(c.slug)).join(chalk3.gray(" \u2192 ")));
13837
13849
  } catch (e) {
13838
- handleError(e);
13850
+ handleError(program2, e);
13839
13851
  }
13840
13852
  });
13841
13853
  program2.command("completion [shell]").description("Output shell completion script (zsh or bash)").action((shell) => {
@@ -13845,7 +13857,7 @@ program2.command("completion [shell]").description("Output shell completion scri
13845
13857
  } else if (sh === "bash") {
13846
13858
  console.log(generateBashCompletion());
13847
13859
  } else {
13848
- handleError(`Unknown shell: ${sh}. Use 'zsh' or 'bash'.`);
13860
+ handleError(program2, `Unknown shell: ${sh}. Use 'zsh' or 'bash'.`);
13849
13861
  }
13850
13862
  });
13851
13863
  program2.command("watch [dir]").description("Watch a directory for .md changes and auto-import prompts (default: .prompts/)").option("-c, --collection <name>", "Collection to import into", "watched").option("--agent <name>", "Attribution").action(async (dir, opts) => {
@@ -13854,7 +13866,7 @@ program2.command("watch [dir]").description("Watch a directory for .md changes a
13854
13866
  const watchDir = resolve(dir ?? join7(process.cwd(), ".prompts"));
13855
13867
  if (!existsSync5(watchDir))
13856
13868
  mkdirSync4(watchDir, { recursive: true });
13857
- console.log(chalk.bold(`Watching ${watchDir} for .md changes\u2026`) + chalk.gray(" (Ctrl+C to stop)"));
13869
+ console.log(chalk3.bold(`Watching ${watchDir} for .md changes\u2026`) + chalk3.gray(" (Ctrl+C to stop)"));
13858
13870
  const { importFromMarkdown: importFromMarkdown2 } = await Promise.resolve().then(() => (init_importer(), exports_importer));
13859
13871
  const { readFileSync: readFileSync2 } = await import("fs");
13860
13872
  const fsWatch = (await import("fs")).watch;
@@ -13865,10 +13877,10 @@ program2.command("watch [dir]").description("Watch a directory for .md changes a
13865
13877
  try {
13866
13878
  const content = readFileSync2(filePath, "utf-8");
13867
13879
  const result = importFromMarkdown2([{ filename, content }], opts["agent"]);
13868
- const action = result.created > 0 ? chalk.green("Created") : chalk.yellow("Updated");
13869
- console.log(`${action}: ${chalk.bold(filename.replace(".md", ""))} ${chalk.gray(new Date().toLocaleTimeString())}`);
13880
+ const action = result.created > 0 ? chalk3.green("Created") : chalk3.yellow("Updated");
13881
+ console.log(`${action}: ${chalk3.bold(filename.replace(".md", ""))} ${chalk3.gray(new Date().toLocaleTimeString())}`);
13870
13882
  } catch {
13871
- console.error(chalk.red(`Failed to import: ${filename}`));
13883
+ console.error(chalk3.red(`Failed to import: ${filename}`));
13872
13884
  }
13873
13885
  });
13874
13886
  await new Promise(() => {});
@@ -13876,37 +13888,37 @@ program2.command("watch [dir]").description("Watch a directory for .md changes a
13876
13888
  program2.command("import-slash-commands").description("Auto-scan .claude/commands, .codex/skills, .gemini/extensions and import all prompts").option("--dir <path>", "Root dir to scan (default: cwd)", process.cwd()).option("--agent <name>", "Attribution").action((opts) => {
13877
13889
  try {
13878
13890
  const { scanned, imported } = scanAndImportSlashCommands(opts["dir"] ?? process.cwd(), opts["agent"]);
13879
- if (isJson()) {
13880
- output({ scanned, imported });
13891
+ if (isJson(program2)) {
13892
+ output(program2, { scanned, imported });
13881
13893
  return;
13882
13894
  }
13883
13895
  if (scanned.length === 0) {
13884
- console.log(chalk.gray("No slash command files found."));
13896
+ console.log(chalk3.gray("No slash command files found."));
13885
13897
  return;
13886
13898
  }
13887
- console.log(chalk.bold(`Scanned ${scanned.length} file(s):`));
13899
+ console.log(chalk3.bold(`Scanned ${scanned.length} file(s):`));
13888
13900
  for (const s of scanned)
13889
- console.log(chalk.gray(` ${s.source}/${s.file}`));
13901
+ console.log(chalk3.gray(` ${s.source}/${s.file}`));
13890
13902
  console.log(`
13891
- ${chalk.green(`Created: ${imported.created}`)} ${chalk.yellow(`Updated: ${imported.updated}`)}`);
13903
+ ${chalk3.green(`Created: ${imported.created}`)} ${chalk3.yellow(`Updated: ${imported.updated}`)}`);
13892
13904
  if (imported.errors.length > 0) {
13893
13905
  for (const e of imported.errors)
13894
- console.error(chalk.red(` \u2717 ${e.item}: ${e.error}`));
13906
+ console.error(chalk3.red(` \u2717 ${e.item}: ${e.error}`));
13895
13907
  }
13896
13908
  } catch (e) {
13897
- handleError(e);
13909
+ handleError(program2, e);
13898
13910
  }
13899
13911
  });
13900
13912
  program2.command("remove <id>").alias("rm").alias("uninstall").description("Remove a prompt (alias for delete)").option("-y, --yes", "Skip confirmation").action(async (id, opts) => {
13901
13913
  try {
13902
13914
  const prompt = getPrompt(id);
13903
13915
  if (!prompt)
13904
- handleError(`Prompt not found: ${id}`);
13905
- if (!opts.yes && !isJson()) {
13916
+ handleError(program2, `Prompt not found: ${id}`);
13917
+ if (!opts.yes && !isJson(program2)) {
13906
13918
  const { createInterface } = await import("readline");
13907
13919
  const rl = createInterface({ input: process.stdin, output: process.stdout });
13908
13920
  await new Promise((resolve) => {
13909
- rl.question(chalk.yellow(`Remove "${prompt.slug}"? [y/N] `), (ans) => {
13921
+ rl.question(chalk3.yellow(`Remove "${prompt.slug}"? [y/N] `), (ans) => {
13910
13922
  rl.close();
13911
13923
  if (ans.toLowerCase() !== "y") {
13912
13924
  console.log("Cancelled.");
@@ -13917,12 +13929,12 @@ program2.command("remove <id>").alias("rm").alias("uninstall").description("Remo
13917
13929
  });
13918
13930
  }
13919
13931
  deletePrompt(id);
13920
- if (isJson())
13921
- output({ deleted: true, id: prompt.id });
13932
+ if (isJson(program2))
13933
+ output(program2, { deleted: true, id: prompt.id });
13922
13934
  else
13923
- console.log(chalk.red(`Removed ${prompt.slug}`));
13935
+ console.log(chalk3.red(`Removed ${prompt.slug}`));
13924
13936
  } catch (e) {
13925
- handleError(e);
13937
+ handleError(program2, e);
13926
13938
  }
13927
13939
  });
13928
13940
  var scheduleCmd = program2.command("schedule").description("Manage prompt schedules");
@@ -13930,81 +13942,81 @@ scheduleCmd.command("add <id> <cron>").description("Schedule a prompt to run on
13930
13942
  try {
13931
13943
  const cronError = validateCron(cron);
13932
13944
  if (cronError)
13933
- handleError(`Invalid cron: ${cronError}`);
13945
+ handleError(program2, `Invalid cron: ${cronError}`);
13934
13946
  const prompt = getPrompt(id);
13935
13947
  if (!prompt)
13936
- handleError(`Prompt not found: ${id}`);
13948
+ handleError(program2, `Prompt not found: ${id}`);
13937
13949
  const vars = opts.vars ? JSON.parse(opts.vars) : undefined;
13938
13950
  const schedule = createSchedule({ prompt_id: prompt.id, prompt_slug: prompt.slug, cron, vars, agent_id: opts.agent });
13939
- if (isJson()) {
13940
- output(schedule);
13951
+ if (isJson(program2)) {
13952
+ output(program2, schedule);
13941
13953
  return;
13942
13954
  }
13943
- console.log(chalk.green(`Scheduled "${prompt.slug}" [${schedule.id}]`));
13955
+ console.log(chalk3.green(`Scheduled "${prompt.slug}" [${schedule.id}]`));
13944
13956
  console.log(` Cron: ${cron}`);
13945
13957
  console.log(` Next run: ${schedule.next_run_at}`);
13946
13958
  } catch (e) {
13947
- handleError(e);
13959
+ handleError(program2, e);
13948
13960
  }
13949
13961
  });
13950
13962
  scheduleCmd.command("list [id]").description("List schedules, optionally filtered by prompt ID").action((id) => {
13951
13963
  try {
13952
13964
  const schedules = listSchedules(id);
13953
- if (isJson()) {
13954
- output(schedules);
13965
+ if (isJson(program2)) {
13966
+ output(program2, schedules);
13955
13967
  return;
13956
13968
  }
13957
13969
  if (!schedules.length) {
13958
- console.log(chalk.gray("No schedules."));
13970
+ console.log(chalk3.gray("No schedules."));
13959
13971
  return;
13960
13972
  }
13961
13973
  for (const s of schedules) {
13962
- console.log(`${chalk.bold(s.id)} ${chalk.cyan(s.prompt_slug)} cron:${s.cron} next:${s.next_run_at} runs:${s.run_count}`);
13974
+ console.log(`${chalk3.bold(s.id)} ${chalk3.cyan(s.prompt_slug)} cron:${s.cron} next:${s.next_run_at} runs:${s.run_count}`);
13963
13975
  }
13964
13976
  } catch (e) {
13965
- handleError(e);
13977
+ handleError(program2, e);
13966
13978
  }
13967
13979
  });
13968
13980
  scheduleCmd.command("remove <scheduleId>").alias("delete").description("Remove a prompt schedule").action((scheduleId) => {
13969
13981
  try {
13970
13982
  deleteSchedule(scheduleId);
13971
- if (isJson())
13972
- output({ deleted: true, id: scheduleId });
13983
+ if (isJson(program2))
13984
+ output(program2, { deleted: true, id: scheduleId });
13973
13985
  else
13974
- console.log(chalk.red(`Removed schedule ${scheduleId}`));
13986
+ console.log(chalk3.red(`Removed schedule ${scheduleId}`));
13975
13987
  } catch (e) {
13976
- handleError(e);
13988
+ handleError(program2, e);
13977
13989
  }
13978
13990
  });
13979
13991
  scheduleCmd.command("due").description("Show and execute all due schedules").option("--dry-run", "Show due prompts without marking them as ran").action((opts) => {
13980
13992
  try {
13981
13993
  const due = getDueSchedules();
13982
13994
  if (!due.length) {
13983
- console.log(chalk.gray("No prompts due."));
13995
+ console.log(chalk3.gray("No prompts due."));
13984
13996
  return;
13985
13997
  }
13986
- if (isJson()) {
13987
- output(due);
13998
+ if (isJson(program2)) {
13999
+ output(program2, due);
13988
14000
  return;
13989
14001
  }
13990
14002
  for (const d of due) {
13991
- console.log(chalk.bold(`
14003
+ console.log(chalk3.bold(`
13992
14004
  [${d.id}] ${d.prompt_slug}`));
13993
- console.log(chalk.gray(`Next run: ${d.next_run_at} | Runs: ${d.run_count}`));
13994
- console.log(chalk.white(d.rendered));
14005
+ console.log(chalk3.gray(`Next run: ${d.next_run_at} | Runs: ${d.run_count}`));
14006
+ console.log(chalk3.white(d.rendered));
13995
14007
  }
13996
14008
  if (!opts.dryRun)
13997
- console.log(chalk.green(`
14009
+ console.log(chalk3.green(`
13998
14010
  \u2713 Marked ${due.length} schedule(s) as ran.`));
13999
14011
  } catch (e) {
14000
- handleError(e);
14012
+ handleError(program2, e);
14001
14013
  }
14002
14014
  });
14003
14015
  scheduleCmd.command("next <cron>").description("Preview when a cron expression will fire").option("-n, --count <n>", "Number of runs to show", "5").action((cron, opts) => {
14004
14016
  try {
14005
14017
  const cronError = validateCron(cron);
14006
14018
  if (cronError)
14007
- handleError(`Invalid cron: ${cronError}`);
14019
+ handleError(program2, `Invalid cron: ${cronError}`);
14008
14020
  const count = parseInt(opts.count ?? "5", 10);
14009
14021
  const runs = [];
14010
14022
  let from = new Date;
@@ -14013,15 +14025,15 @@ scheduleCmd.command("next <cron>").description("Preview when a cron expression w
14013
14025
  runs.push(next.toISOString());
14014
14026
  from = next;
14015
14027
  }
14016
- if (isJson()) {
14017
- output({ cron, next_runs: runs });
14028
+ if (isJson(program2)) {
14029
+ output(program2, { cron, next_runs: runs });
14018
14030
  return;
14019
14031
  }
14020
- console.log(chalk.bold(`Next ${count} runs for "${cron}":`));
14032
+ console.log(chalk3.bold(`Next ${count} runs for "${cron}":`));
14021
14033
  for (const r of runs)
14022
14034
  console.log(` ${r}`);
14023
14035
  } catch (e) {
14024
- handleError(e);
14036
+ handleError(program2, e);
14025
14037
  }
14026
14038
  });
14027
14039
  program2.parse();