@hasna/prompts 0.3.14 → 0.3.16
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/commands/config.d.ts +3 -0
- package/dist/cli/commands/config.d.ts.map +1 -0
- package/dist/cli/commands/qol.d.ts +3 -0
- package/dist/cli/commands/qol.d.ts.map +1 -0
- package/dist/cli/index.js +1199 -205
- package/dist/mcp/index.js +218 -2
- package/package.json +2 -2
package/dist/cli/index.js
CHANGED
|
@@ -11932,12 +11932,73 @@ function renderTemplate(body, vars) {
|
|
|
11932
11932
|
});
|
|
11933
11933
|
return { rendered, missing_vars: missing, used_defaults: usedDefaults };
|
|
11934
11934
|
}
|
|
11935
|
+
function validateVars(body, provided) {
|
|
11936
|
+
const infos = extractVariableInfo(body);
|
|
11937
|
+
const required = infos.filter((v) => v.required).map((v) => v.name);
|
|
11938
|
+
const optional = infos.filter((v) => !v.required).map((v) => v.name);
|
|
11939
|
+
const all = infos.map((v) => v.name);
|
|
11940
|
+
const missing = required.filter((v) => !(v in provided));
|
|
11941
|
+
const extra = Object.keys(provided).filter((v) => !all.includes(v));
|
|
11942
|
+
return { missing, extra, optional };
|
|
11943
|
+
}
|
|
11935
11944
|
var VAR_PATTERN;
|
|
11936
11945
|
var init_template = __esm(() => {
|
|
11937
11946
|
VAR_PATTERN = /\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*(?:\|\s*(.*?)\s*)?\}\}/g;
|
|
11938
11947
|
});
|
|
11939
11948
|
|
|
11940
11949
|
// src/db/prompts.ts
|
|
11950
|
+
var exports_prompts = {};
|
|
11951
|
+
__export(exports_prompts, {
|
|
11952
|
+
usePrompt: () => usePrompt,
|
|
11953
|
+
upsertPrompt: () => upsertPrompt,
|
|
11954
|
+
updatePrompt: () => updatePrompt,
|
|
11955
|
+
setNextPrompt: () => setNextPrompt,
|
|
11956
|
+
setExpiry: () => setExpiry,
|
|
11957
|
+
requirePrompt: () => requirePrompt,
|
|
11958
|
+
promptToSaveResult: () => promptToSaveResult,
|
|
11959
|
+
pinPrompt: () => pinPrompt,
|
|
11960
|
+
listPromptsSlim: () => listPromptsSlim,
|
|
11961
|
+
listPrompts: () => listPrompts,
|
|
11962
|
+
getTrending: () => getTrending,
|
|
11963
|
+
getPromptStats: () => getPromptStats,
|
|
11964
|
+
getPrompt: () => getPrompt,
|
|
11965
|
+
deletePrompt: () => deletePrompt,
|
|
11966
|
+
createPrompt: () => createPrompt
|
|
11967
|
+
});
|
|
11968
|
+
function rowToSlimPrompt(row) {
|
|
11969
|
+
const variables = JSON.parse(row["variables"] || "[]");
|
|
11970
|
+
return {
|
|
11971
|
+
id: row["id"],
|
|
11972
|
+
slug: row["slug"],
|
|
11973
|
+
title: row["title"],
|
|
11974
|
+
description: row["description"] ?? null,
|
|
11975
|
+
collection: row["collection"],
|
|
11976
|
+
tags: JSON.parse(row["tags"] || "[]"),
|
|
11977
|
+
variable_names: variables.map((v) => v.name),
|
|
11978
|
+
is_template: Boolean(row["is_template"]),
|
|
11979
|
+
source: row["source"],
|
|
11980
|
+
pinned: Boolean(row["pinned"]),
|
|
11981
|
+
next_prompt: row["next_prompt"] ?? null,
|
|
11982
|
+
expires_at: row["expires_at"] ?? null,
|
|
11983
|
+
project_id: row["project_id"] ?? null,
|
|
11984
|
+
use_count: row["use_count"],
|
|
11985
|
+
last_used_at: row["last_used_at"] ?? null,
|
|
11986
|
+
created_at: row["created_at"],
|
|
11987
|
+
updated_at: row["updated_at"]
|
|
11988
|
+
};
|
|
11989
|
+
}
|
|
11990
|
+
function promptToSaveResult(prompt, created, duplicate_warning) {
|
|
11991
|
+
return {
|
|
11992
|
+
id: prompt.id,
|
|
11993
|
+
slug: prompt.slug,
|
|
11994
|
+
title: prompt.title,
|
|
11995
|
+
collection: prompt.collection,
|
|
11996
|
+
is_template: prompt.is_template,
|
|
11997
|
+
variable_names: prompt.variables.map((v) => v.name),
|
|
11998
|
+
created,
|
|
11999
|
+
duplicate_warning: duplicate_warning ?? null
|
|
12000
|
+
};
|
|
12001
|
+
}
|
|
11941
12002
|
function rowToPrompt(row) {
|
|
11942
12003
|
return {
|
|
11943
12004
|
id: row["id"],
|
|
@@ -12037,6 +12098,40 @@ function listPrompts(filter = {}) {
|
|
|
12037
12098
|
const rows = db.query(`SELECT * FROM prompts ${where} ORDER BY ${orderBy} LIMIT ? OFFSET ?`).all(...params, limit, offset);
|
|
12038
12099
|
return rows.map(rowToPrompt);
|
|
12039
12100
|
}
|
|
12101
|
+
function listPromptsSlim(filter = {}) {
|
|
12102
|
+
const db = getDatabase();
|
|
12103
|
+
const conditions = [];
|
|
12104
|
+
const params = [];
|
|
12105
|
+
if (filter.collection) {
|
|
12106
|
+
conditions.push("collection = ?");
|
|
12107
|
+
params.push(filter.collection);
|
|
12108
|
+
}
|
|
12109
|
+
if (filter.is_template !== undefined) {
|
|
12110
|
+
conditions.push("is_template = ?");
|
|
12111
|
+
params.push(filter.is_template ? 1 : 0);
|
|
12112
|
+
}
|
|
12113
|
+
if (filter.source) {
|
|
12114
|
+
conditions.push("source = ?");
|
|
12115
|
+
params.push(filter.source);
|
|
12116
|
+
}
|
|
12117
|
+
if (filter.tags && filter.tags.length > 0) {
|
|
12118
|
+
const tagConds = filter.tags.map(() => "tags LIKE ?");
|
|
12119
|
+
conditions.push(`(${tagConds.join(" OR ")})`);
|
|
12120
|
+
for (const tag of filter.tags)
|
|
12121
|
+
params.push(`%"${tag}"%`);
|
|
12122
|
+
}
|
|
12123
|
+
let orderBy = "pinned DESC, use_count DESC, updated_at DESC";
|
|
12124
|
+
if (filter.project_id) {
|
|
12125
|
+
conditions.push("(project_id = ? OR project_id IS NULL)");
|
|
12126
|
+
params.push(filter.project_id);
|
|
12127
|
+
orderBy = `(CASE WHEN project_id = '${filter.project_id}' THEN 0 ELSE 1 END), pinned DESC, use_count DESC, updated_at DESC`;
|
|
12128
|
+
}
|
|
12129
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
12130
|
+
const limit = filter.limit ?? 20;
|
|
12131
|
+
const offset = filter.offset ?? 0;
|
|
12132
|
+
const rows = db.query(`SELECT id, slug, name, title, description, collection, tags, variables, is_template, source, pinned, next_prompt, expires_at, project_id, use_count, last_used_at, created_at, updated_at FROM prompts ${where} ORDER BY ${orderBy} LIMIT ? OFFSET ?`).all(...params, limit, offset);
|
|
12133
|
+
return rows.map(rowToSlimPrompt);
|
|
12134
|
+
}
|
|
12040
12135
|
function updatePrompt(idOrSlug, input) {
|
|
12041
12136
|
const db = getDatabase();
|
|
12042
12137
|
const prompt = requirePrompt(idOrSlug);
|
|
@@ -12321,6 +12416,205 @@ var init_importer = __esm(() => {
|
|
|
12321
12416
|
init_prompts();
|
|
12322
12417
|
});
|
|
12323
12418
|
|
|
12419
|
+
// src/lib/search.ts
|
|
12420
|
+
var exports_search = {};
|
|
12421
|
+
__export(exports_search, {
|
|
12422
|
+
searchPromptsSlim: () => searchPromptsSlim,
|
|
12423
|
+
searchPrompts: () => searchPrompts,
|
|
12424
|
+
findSimilar: () => findSimilar
|
|
12425
|
+
});
|
|
12426
|
+
function rowToSlimSearchResult(row, snippet) {
|
|
12427
|
+
const variables = JSON.parse(row["variables"] || "[]");
|
|
12428
|
+
return {
|
|
12429
|
+
id: row["id"],
|
|
12430
|
+
slug: row["slug"],
|
|
12431
|
+
title: row["title"],
|
|
12432
|
+
description: row["description"] ?? null,
|
|
12433
|
+
collection: row["collection"],
|
|
12434
|
+
tags: JSON.parse(row["tags"] || "[]"),
|
|
12435
|
+
variable_names: variables.map((v) => v.name),
|
|
12436
|
+
is_template: Boolean(row["is_template"]),
|
|
12437
|
+
use_count: row["use_count"],
|
|
12438
|
+
score: row["score"] ?? 1,
|
|
12439
|
+
snippet
|
|
12440
|
+
};
|
|
12441
|
+
}
|
|
12442
|
+
function rowToSearchResult(row, snippet) {
|
|
12443
|
+
return {
|
|
12444
|
+
prompt: {
|
|
12445
|
+
id: row["id"],
|
|
12446
|
+
name: row["name"],
|
|
12447
|
+
slug: row["slug"],
|
|
12448
|
+
title: row["title"],
|
|
12449
|
+
body: row["body"],
|
|
12450
|
+
description: row["description"] ?? null,
|
|
12451
|
+
collection: row["collection"],
|
|
12452
|
+
tags: JSON.parse(row["tags"] || "[]"),
|
|
12453
|
+
variables: JSON.parse(row["variables"] || "[]"),
|
|
12454
|
+
pinned: Boolean(row["pinned"]),
|
|
12455
|
+
next_prompt: row["next_prompt"] ?? null,
|
|
12456
|
+
expires_at: row["expires_at"] ?? null,
|
|
12457
|
+
project_id: row["project_id"] ?? null,
|
|
12458
|
+
is_template: Boolean(row["is_template"]),
|
|
12459
|
+
source: row["source"],
|
|
12460
|
+
version: row["version"],
|
|
12461
|
+
use_count: row["use_count"],
|
|
12462
|
+
last_used_at: row["last_used_at"] ?? null,
|
|
12463
|
+
created_at: row["created_at"],
|
|
12464
|
+
updated_at: row["updated_at"]
|
|
12465
|
+
},
|
|
12466
|
+
score: row["score"] ?? 1,
|
|
12467
|
+
snippet
|
|
12468
|
+
};
|
|
12469
|
+
}
|
|
12470
|
+
function escapeFtsQuery(q) {
|
|
12471
|
+
return q.trim().split(/\s+/).filter(Boolean).map((w) => `"${w.replace(/"/g, '""')}"*`).join(" ");
|
|
12472
|
+
}
|
|
12473
|
+
function searchPrompts(query, filter = {}) {
|
|
12474
|
+
const db = getDatabase();
|
|
12475
|
+
if (!query.trim()) {
|
|
12476
|
+
const prompts = listPrompts(filter);
|
|
12477
|
+
return prompts.map((p) => ({ prompt: p, score: 1 }));
|
|
12478
|
+
}
|
|
12479
|
+
if (hasFts(db)) {
|
|
12480
|
+
const ftsQuery = escapeFtsQuery(query);
|
|
12481
|
+
const conditions = [];
|
|
12482
|
+
const params = [];
|
|
12483
|
+
if (filter.collection) {
|
|
12484
|
+
conditions.push("p.collection = ?");
|
|
12485
|
+
params.push(filter.collection);
|
|
12486
|
+
}
|
|
12487
|
+
if (filter.is_template !== undefined) {
|
|
12488
|
+
conditions.push("p.is_template = ?");
|
|
12489
|
+
params.push(filter.is_template ? 1 : 0);
|
|
12490
|
+
}
|
|
12491
|
+
if (filter.source) {
|
|
12492
|
+
conditions.push("p.source = ?");
|
|
12493
|
+
params.push(filter.source);
|
|
12494
|
+
}
|
|
12495
|
+
if (filter.tags && filter.tags.length > 0) {
|
|
12496
|
+
const tagConds = filter.tags.map(() => "p.tags LIKE ?");
|
|
12497
|
+
conditions.push(`(${tagConds.join(" OR ")})`);
|
|
12498
|
+
for (const tag of filter.tags)
|
|
12499
|
+
params.push(`%"${tag}"%`);
|
|
12500
|
+
}
|
|
12501
|
+
if (filter.project_id !== undefined && filter.project_id !== null) {
|
|
12502
|
+
conditions.push("(p.project_id = ? OR p.project_id IS NULL)");
|
|
12503
|
+
params.push(filter.project_id);
|
|
12504
|
+
}
|
|
12505
|
+
const where = conditions.length > 0 ? `AND ${conditions.join(" AND ")}` : "";
|
|
12506
|
+
const limit = filter.limit ?? 50;
|
|
12507
|
+
const offset = filter.offset ?? 0;
|
|
12508
|
+
try {
|
|
12509
|
+
const rows2 = db.query(`SELECT p.*, bm25(prompts_fts) as score,
|
|
12510
|
+
snippet(prompts_fts, 2, '[', ']', '...', 10) as snippet
|
|
12511
|
+
FROM prompts p
|
|
12512
|
+
INNER JOIN prompts_fts ON prompts_fts.rowid = p.rowid
|
|
12513
|
+
WHERE prompts_fts MATCH ?
|
|
12514
|
+
${where}
|
|
12515
|
+
ORDER BY bm25(prompts_fts)
|
|
12516
|
+
LIMIT ? OFFSET ?`).all(ftsQuery, ...params, limit, offset);
|
|
12517
|
+
return rows2.map((r) => rowToSearchResult(r, r["snippet"]));
|
|
12518
|
+
} catch {}
|
|
12519
|
+
}
|
|
12520
|
+
const like = `%${query}%`;
|
|
12521
|
+
const rows = db.query(`SELECT *, 1 as score FROM prompts
|
|
12522
|
+
WHERE (name LIKE ? OR slug LIKE ? OR title LIKE ? OR body LIKE ? OR description LIKE ? OR tags LIKE ?)
|
|
12523
|
+
ORDER BY use_count DESC, updated_at DESC
|
|
12524
|
+
LIMIT ? OFFSET ?`).all(like, like, like, like, like, like, filter.limit ?? 10, filter.offset ?? 0);
|
|
12525
|
+
return rows.map((r) => rowToSearchResult(r));
|
|
12526
|
+
}
|
|
12527
|
+
function searchPromptsSlim(query, filter = {}) {
|
|
12528
|
+
const db = getDatabase();
|
|
12529
|
+
if (!query.trim()) {
|
|
12530
|
+
return listPromptsSlim(filter).map((p) => ({
|
|
12531
|
+
id: p.id,
|
|
12532
|
+
slug: p.slug,
|
|
12533
|
+
title: p.title,
|
|
12534
|
+
description: p.description,
|
|
12535
|
+
collection: p.collection,
|
|
12536
|
+
tags: p.tags,
|
|
12537
|
+
variable_names: p.variable_names,
|
|
12538
|
+
is_template: p.is_template,
|
|
12539
|
+
use_count: p.use_count,
|
|
12540
|
+
score: 1
|
|
12541
|
+
}));
|
|
12542
|
+
}
|
|
12543
|
+
if (hasFts(db)) {
|
|
12544
|
+
const ftsQuery = escapeFtsQuery(query);
|
|
12545
|
+
const conditions = [];
|
|
12546
|
+
const params = [];
|
|
12547
|
+
if (filter.collection) {
|
|
12548
|
+
conditions.push("p.collection = ?");
|
|
12549
|
+
params.push(filter.collection);
|
|
12550
|
+
}
|
|
12551
|
+
if (filter.is_template !== undefined) {
|
|
12552
|
+
conditions.push("p.is_template = ?");
|
|
12553
|
+
params.push(filter.is_template ? 1 : 0);
|
|
12554
|
+
}
|
|
12555
|
+
if (filter.source) {
|
|
12556
|
+
conditions.push("p.source = ?");
|
|
12557
|
+
params.push(filter.source);
|
|
12558
|
+
}
|
|
12559
|
+
if (filter.tags && filter.tags.length > 0) {
|
|
12560
|
+
const tagConds = filter.tags.map(() => "p.tags LIKE ?");
|
|
12561
|
+
conditions.push(`(${tagConds.join(" OR ")})`);
|
|
12562
|
+
for (const tag of filter.tags)
|
|
12563
|
+
params.push(`%"${tag}"%`);
|
|
12564
|
+
}
|
|
12565
|
+
if (filter.project_id) {
|
|
12566
|
+
conditions.push("(p.project_id = ? OR p.project_id IS NULL)");
|
|
12567
|
+
params.push(filter.project_id);
|
|
12568
|
+
}
|
|
12569
|
+
const where = conditions.length > 0 ? `AND ${conditions.join(" AND ")}` : "";
|
|
12570
|
+
const limit = filter.limit ?? 10;
|
|
12571
|
+
const offset = filter.offset ?? 0;
|
|
12572
|
+
try {
|
|
12573
|
+
const rows2 = db.query(`SELECT p.id, p.slug, p.name, p.title, p.description, p.collection, p.tags, p.variables,
|
|
12574
|
+
p.is_template, p.use_count, bm25(prompts_fts) as score,
|
|
12575
|
+
snippet(prompts_fts, 2, '[', ']', '...', 10) as snippet
|
|
12576
|
+
FROM prompts p
|
|
12577
|
+
INNER JOIN prompts_fts ON prompts_fts.rowid = p.rowid
|
|
12578
|
+
WHERE prompts_fts MATCH ?
|
|
12579
|
+
${where}
|
|
12580
|
+
ORDER BY bm25(prompts_fts)
|
|
12581
|
+
LIMIT ? OFFSET ?`).all(ftsQuery, ...params, limit, offset);
|
|
12582
|
+
return rows2.map((r) => rowToSlimSearchResult(r, r["snippet"]));
|
|
12583
|
+
} catch {}
|
|
12584
|
+
}
|
|
12585
|
+
const like = `%${query}%`;
|
|
12586
|
+
const rows = db.query(`SELECT id, slug, name, title, description, collection, tags, variables, is_template, use_count, 1 as score
|
|
12587
|
+
FROM prompts
|
|
12588
|
+
WHERE (name LIKE ? OR slug LIKE ? OR title LIKE ? OR body LIKE ? OR description LIKE ? OR tags LIKE ?)
|
|
12589
|
+
ORDER BY use_count DESC, updated_at DESC
|
|
12590
|
+
LIMIT ? OFFSET ?`).all(like, like, like, like, like, like, filter.limit ?? 10, filter.offset ?? 0);
|
|
12591
|
+
return rows.map((r) => rowToSlimSearchResult(r));
|
|
12592
|
+
}
|
|
12593
|
+
function findSimilar(promptId, limit = 5) {
|
|
12594
|
+
const db = getDatabase();
|
|
12595
|
+
const prompt = db.query("SELECT * FROM prompts WHERE id = ?").get(promptId);
|
|
12596
|
+
if (!prompt)
|
|
12597
|
+
return [];
|
|
12598
|
+
const tags = JSON.parse(prompt["tags"] || "[]");
|
|
12599
|
+
const collection = prompt["collection"];
|
|
12600
|
+
if (tags.length === 0) {
|
|
12601
|
+
const rows = db.query("SELECT *, 1 as score FROM prompts WHERE collection = ? AND id != ? ORDER BY use_count DESC LIMIT ?").all(collection, promptId, limit);
|
|
12602
|
+
return rows.map((r) => rowToSearchResult(r));
|
|
12603
|
+
}
|
|
12604
|
+
const allRows = db.query("SELECT * FROM prompts WHERE id != ?").all(promptId);
|
|
12605
|
+
const scored = allRows.map((row) => {
|
|
12606
|
+
const rowTags = JSON.parse(row["tags"] || "[]");
|
|
12607
|
+
const overlap = rowTags.filter((t) => tags.includes(t)).length;
|
|
12608
|
+
const sameCollection = row["collection"] === collection ? 1 : 0;
|
|
12609
|
+
return { row, score: overlap * 2 + sameCollection };
|
|
12610
|
+
});
|
|
12611
|
+
return scored.filter((s) => s.score > 0).sort((a, b) => b.score - a.score).slice(0, limit).map((s) => rowToSearchResult(s.row, undefined));
|
|
12612
|
+
}
|
|
12613
|
+
var init_search = __esm(() => {
|
|
12614
|
+
init_database();
|
|
12615
|
+
init_prompts();
|
|
12616
|
+
});
|
|
12617
|
+
|
|
12324
12618
|
// node_modules/commander/esm.mjs
|
|
12325
12619
|
var import__ = __toESM(require_commander(), 1);
|
|
12326
12620
|
var {
|
|
@@ -12339,7 +12633,7 @@ var {
|
|
|
12339
12633
|
|
|
12340
12634
|
// src/cli/index.tsx
|
|
12341
12635
|
init_collections();
|
|
12342
|
-
import
|
|
12636
|
+
import chalk6 from "chalk";
|
|
12343
12637
|
import { createRequire as createRequire2 } from "module";
|
|
12344
12638
|
|
|
12345
12639
|
// src/db/projects.ts
|
|
@@ -12848,119 +13142,9 @@ function fmtPrompt(p) {
|
|
|
12848
13142
|
|
|
12849
13143
|
// src/cli/commands/prompts.tsx
|
|
12850
13144
|
init_prompts();
|
|
13145
|
+
init_search();
|
|
13146
|
+
init_template();
|
|
12851
13147
|
import chalk2 from "chalk";
|
|
12852
|
-
|
|
12853
|
-
// src/lib/search.ts
|
|
12854
|
-
init_database();
|
|
12855
|
-
init_prompts();
|
|
12856
|
-
function rowToSearchResult(row, snippet) {
|
|
12857
|
-
return {
|
|
12858
|
-
prompt: {
|
|
12859
|
-
id: row["id"],
|
|
12860
|
-
name: row["name"],
|
|
12861
|
-
slug: row["slug"],
|
|
12862
|
-
title: row["title"],
|
|
12863
|
-
body: row["body"],
|
|
12864
|
-
description: row["description"] ?? null,
|
|
12865
|
-
collection: row["collection"],
|
|
12866
|
-
tags: JSON.parse(row["tags"] || "[]"),
|
|
12867
|
-
variables: JSON.parse(row["variables"] || "[]"),
|
|
12868
|
-
pinned: Boolean(row["pinned"]),
|
|
12869
|
-
next_prompt: row["next_prompt"] ?? null,
|
|
12870
|
-
expires_at: row["expires_at"] ?? null,
|
|
12871
|
-
project_id: row["project_id"] ?? null,
|
|
12872
|
-
is_template: Boolean(row["is_template"]),
|
|
12873
|
-
source: row["source"],
|
|
12874
|
-
version: row["version"],
|
|
12875
|
-
use_count: row["use_count"],
|
|
12876
|
-
last_used_at: row["last_used_at"] ?? null,
|
|
12877
|
-
created_at: row["created_at"],
|
|
12878
|
-
updated_at: row["updated_at"]
|
|
12879
|
-
},
|
|
12880
|
-
score: row["score"] ?? 1,
|
|
12881
|
-
snippet
|
|
12882
|
-
};
|
|
12883
|
-
}
|
|
12884
|
-
function escapeFtsQuery(q) {
|
|
12885
|
-
return q.trim().split(/\s+/).filter(Boolean).map((w) => `"${w.replace(/"/g, '""')}"*`).join(" ");
|
|
12886
|
-
}
|
|
12887
|
-
function searchPrompts(query, filter = {}) {
|
|
12888
|
-
const db = getDatabase();
|
|
12889
|
-
if (!query.trim()) {
|
|
12890
|
-
const prompts = listPrompts(filter);
|
|
12891
|
-
return prompts.map((p) => ({ prompt: p, score: 1 }));
|
|
12892
|
-
}
|
|
12893
|
-
if (hasFts(db)) {
|
|
12894
|
-
const ftsQuery = escapeFtsQuery(query);
|
|
12895
|
-
const conditions = [];
|
|
12896
|
-
const params = [];
|
|
12897
|
-
if (filter.collection) {
|
|
12898
|
-
conditions.push("p.collection = ?");
|
|
12899
|
-
params.push(filter.collection);
|
|
12900
|
-
}
|
|
12901
|
-
if (filter.is_template !== undefined) {
|
|
12902
|
-
conditions.push("p.is_template = ?");
|
|
12903
|
-
params.push(filter.is_template ? 1 : 0);
|
|
12904
|
-
}
|
|
12905
|
-
if (filter.source) {
|
|
12906
|
-
conditions.push("p.source = ?");
|
|
12907
|
-
params.push(filter.source);
|
|
12908
|
-
}
|
|
12909
|
-
if (filter.tags && filter.tags.length > 0) {
|
|
12910
|
-
const tagConds = filter.tags.map(() => "p.tags LIKE ?");
|
|
12911
|
-
conditions.push(`(${tagConds.join(" OR ")})`);
|
|
12912
|
-
for (const tag of filter.tags)
|
|
12913
|
-
params.push(`%"${tag}"%`);
|
|
12914
|
-
}
|
|
12915
|
-
if (filter.project_id !== undefined && filter.project_id !== null) {
|
|
12916
|
-
conditions.push("(p.project_id = ? OR p.project_id IS NULL)");
|
|
12917
|
-
params.push(filter.project_id);
|
|
12918
|
-
}
|
|
12919
|
-
const where = conditions.length > 0 ? `AND ${conditions.join(" AND ")}` : "";
|
|
12920
|
-
const limit = filter.limit ?? 50;
|
|
12921
|
-
const offset = filter.offset ?? 0;
|
|
12922
|
-
try {
|
|
12923
|
-
const rows2 = db.query(`SELECT p.*, bm25(prompts_fts) as score,
|
|
12924
|
-
snippet(prompts_fts, 2, '[', ']', '...', 10) as snippet
|
|
12925
|
-
FROM prompts p
|
|
12926
|
-
INNER JOIN prompts_fts ON prompts_fts.rowid = p.rowid
|
|
12927
|
-
WHERE prompts_fts MATCH ?
|
|
12928
|
-
${where}
|
|
12929
|
-
ORDER BY bm25(prompts_fts)
|
|
12930
|
-
LIMIT ? OFFSET ?`).all(ftsQuery, ...params, limit, offset);
|
|
12931
|
-
return rows2.map((r) => rowToSearchResult(r, r["snippet"]));
|
|
12932
|
-
} catch {}
|
|
12933
|
-
}
|
|
12934
|
-
const like = `%${query}%`;
|
|
12935
|
-
const rows = db.query(`SELECT *, 1 as score FROM prompts
|
|
12936
|
-
WHERE (name LIKE ? OR slug LIKE ? OR title LIKE ? OR body LIKE ? OR description LIKE ? OR tags LIKE ?)
|
|
12937
|
-
ORDER BY use_count DESC, updated_at DESC
|
|
12938
|
-
LIMIT ? OFFSET ?`).all(like, like, like, like, like, like, filter.limit ?? 10, filter.offset ?? 0);
|
|
12939
|
-
return rows.map((r) => rowToSearchResult(r));
|
|
12940
|
-
}
|
|
12941
|
-
function findSimilar(promptId, limit = 5) {
|
|
12942
|
-
const db = getDatabase();
|
|
12943
|
-
const prompt = db.query("SELECT * FROM prompts WHERE id = ?").get(promptId);
|
|
12944
|
-
if (!prompt)
|
|
12945
|
-
return [];
|
|
12946
|
-
const tags = JSON.parse(prompt["tags"] || "[]");
|
|
12947
|
-
const collection = prompt["collection"];
|
|
12948
|
-
if (tags.length === 0) {
|
|
12949
|
-
const rows = db.query("SELECT *, 1 as score FROM prompts WHERE collection = ? AND id != ? ORDER BY use_count DESC LIMIT ?").all(collection, promptId, limit);
|
|
12950
|
-
return rows.map((r) => rowToSearchResult(r));
|
|
12951
|
-
}
|
|
12952
|
-
const allRows = db.query("SELECT * FROM prompts WHERE id != ?").all(promptId);
|
|
12953
|
-
const scored = allRows.map((row) => {
|
|
12954
|
-
const rowTags = JSON.parse(row["tags"] || "[]");
|
|
12955
|
-
const overlap = rowTags.filter((t) => tags.includes(t)).length;
|
|
12956
|
-
const sameCollection = row["collection"] === collection ? 1 : 0;
|
|
12957
|
-
return { row, score: overlap * 2 + sameCollection };
|
|
12958
|
-
});
|
|
12959
|
-
return scored.filter((s) => s.score > 0).sort((a, b) => b.score - a.score).slice(0, limit).map((s) => rowToSearchResult(s.row, undefined));
|
|
12960
|
-
}
|
|
12961
|
-
|
|
12962
|
-
// src/cli/commands/prompts.tsx
|
|
12963
|
-
init_template();
|
|
12964
13148
|
function registerPromptCommands(program2) {
|
|
12965
13149
|
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) => {
|
|
12966
13150
|
try {
|
|
@@ -13390,12 +13574,784 @@ function registerVersionCommands(program2) {
|
|
|
13390
13574
|
});
|
|
13391
13575
|
}
|
|
13392
13576
|
|
|
13577
|
+
// src/cli/commands/qol.ts
|
|
13578
|
+
init_prompts();
|
|
13579
|
+
init_collections();
|
|
13580
|
+
init_search();
|
|
13581
|
+
init_template();
|
|
13582
|
+
import chalk4 from "chalk";
|
|
13583
|
+
function registerQolCommands(program2) {
|
|
13584
|
+
program2.command("body <id>").description("Print prompt body without incrementing use counter (pipe-friendly)").action((id) => {
|
|
13585
|
+
try {
|
|
13586
|
+
const prompt = getPrompt(id);
|
|
13587
|
+
if (!prompt)
|
|
13588
|
+
handleError(program2, `Prompt not found: ${id}`);
|
|
13589
|
+
if (isJson(program2))
|
|
13590
|
+
output(program2, { id: prompt.id, slug: prompt.slug, body: prompt.body });
|
|
13591
|
+
else
|
|
13592
|
+
process.stdout.write(prompt.body);
|
|
13593
|
+
} catch (e) {
|
|
13594
|
+
handleError(program2, e);
|
|
13595
|
+
}
|
|
13596
|
+
});
|
|
13597
|
+
program2.command("render <id>").description("Render a template prompt with variable substitution").option("--var <kv>", "Variable as key=value (repeatable)", (val, acc) => {
|
|
13598
|
+
acc.push(val);
|
|
13599
|
+
return acc;
|
|
13600
|
+
}, []).option("--vars <json>", "Variables as JSON object").action((id, opts) => {
|
|
13601
|
+
try {
|
|
13602
|
+
const prompt = getPrompt(id);
|
|
13603
|
+
if (!prompt)
|
|
13604
|
+
handleError(program2, `Prompt not found: ${id}`);
|
|
13605
|
+
const vars = {};
|
|
13606
|
+
if (opts.vars) {
|
|
13607
|
+
Object.assign(vars, JSON.parse(opts.vars));
|
|
13608
|
+
}
|
|
13609
|
+
for (const kv of opts.var) {
|
|
13610
|
+
const eq = kv.indexOf("=");
|
|
13611
|
+
if (eq === -1)
|
|
13612
|
+
handleError(program2, `Invalid --var format (expected key=value): ${kv}`);
|
|
13613
|
+
vars[kv.slice(0, eq)] = kv.slice(eq + 1);
|
|
13614
|
+
}
|
|
13615
|
+
const { rendered, missing_vars, used_defaults } = renderTemplate(prompt.body, vars);
|
|
13616
|
+
if (isJson(program2)) {
|
|
13617
|
+
output(program2, { rendered, missing_vars, used_defaults });
|
|
13618
|
+
return;
|
|
13619
|
+
}
|
|
13620
|
+
if (missing_vars.length > 0) {
|
|
13621
|
+
console.error(chalk4.yellow(`Warning: unresolved variables: ${missing_vars.join(", ")}`));
|
|
13622
|
+
}
|
|
13623
|
+
if (used_defaults.length > 0 && !isJson(program2)) {
|
|
13624
|
+
console.error(chalk4.gray(`Using defaults for: ${used_defaults.join(", ")}`));
|
|
13625
|
+
}
|
|
13626
|
+
process.stdout.write(rendered);
|
|
13627
|
+
} catch (e) {
|
|
13628
|
+
handleError(program2, e);
|
|
13629
|
+
}
|
|
13630
|
+
});
|
|
13631
|
+
program2.command("similar <id>").description("Find prompts similar to the given one (by tags and collection)").option("-n, --limit <n>", "Max results", "5").action((id, opts) => {
|
|
13632
|
+
try {
|
|
13633
|
+
const prompt = getPrompt(id);
|
|
13634
|
+
if (!prompt)
|
|
13635
|
+
handleError(program2, `Prompt not found: ${id}`);
|
|
13636
|
+
const limit = parseInt(opts.limit ?? "5") || 5;
|
|
13637
|
+
const results = findSimilar(prompt.id, limit);
|
|
13638
|
+
if (isJson(program2)) {
|
|
13639
|
+
output(program2, results.map((r) => r.prompt));
|
|
13640
|
+
return;
|
|
13641
|
+
}
|
|
13642
|
+
if (results.length === 0) {
|
|
13643
|
+
console.log(chalk4.gray("No similar prompts found."));
|
|
13644
|
+
return;
|
|
13645
|
+
}
|
|
13646
|
+
console.log(chalk4.bold(`Similar to ${chalk4.green(prompt.slug)}:`));
|
|
13647
|
+
for (const r of results) {
|
|
13648
|
+
console.log(` ${fmtPrompt(r.prompt)} ${chalk4.gray(`score:${r.score}`)}`);
|
|
13649
|
+
}
|
|
13650
|
+
} catch (e) {
|
|
13651
|
+
handleError(program2, e);
|
|
13652
|
+
}
|
|
13653
|
+
});
|
|
13654
|
+
program2.command("edit <id>").description("Open a prompt in $EDITOR for full editing (title, body, description, tags, collection)").option("--agent <name>", "Attribution").action(async (id, opts) => {
|
|
13655
|
+
try {
|
|
13656
|
+
const prompt = getPrompt(id);
|
|
13657
|
+
if (!prompt)
|
|
13658
|
+
handleError(program2, `Prompt not found: ${id}`);
|
|
13659
|
+
const p = prompt;
|
|
13660
|
+
const { writeFileSync: writeFileSync2, readFileSync: readFileSync2, unlinkSync } = await import("fs");
|
|
13661
|
+
const { tmpdir } = await import("os");
|
|
13662
|
+
const { join: join7 } = await import("path");
|
|
13663
|
+
const frontmatter = [
|
|
13664
|
+
`---`,
|
|
13665
|
+
`title: ${p.title}`,
|
|
13666
|
+
`description: ${p.description ?? ""}`,
|
|
13667
|
+
`collection: ${p.collection}`,
|
|
13668
|
+
`tags: ${p.tags.join(", ")}`,
|
|
13669
|
+
`---`,
|
|
13670
|
+
``,
|
|
13671
|
+
p.body
|
|
13672
|
+
].join(`
|
|
13673
|
+
`);
|
|
13674
|
+
const tmpFile = join7(tmpdir(), `prompt-${p.id}.md`);
|
|
13675
|
+
writeFileSync2(tmpFile, frontmatter);
|
|
13676
|
+
const editor = process.env["EDITOR"] ?? process.env["VISUAL"] ?? "vi";
|
|
13677
|
+
const proc = Bun.spawn([editor, tmpFile], { stdin: "inherit", stdout: "inherit", stderr: "inherit" });
|
|
13678
|
+
await proc.exited;
|
|
13679
|
+
const edited = readFileSync2(tmpFile, "utf-8");
|
|
13680
|
+
unlinkSync(tmpFile);
|
|
13681
|
+
const fmMatch = edited.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/m);
|
|
13682
|
+
if (!fmMatch) {
|
|
13683
|
+
updatePrompt(p.id, { body: edited.trim(), changed_by: opts.agent });
|
|
13684
|
+
if (!isJson(program2))
|
|
13685
|
+
console.log(chalk4.green(`Updated ${chalk4.bold(p.slug)} (body only)`));
|
|
13686
|
+
return;
|
|
13687
|
+
}
|
|
13688
|
+
const fmLines = (fmMatch[1] ?? "").split(`
|
|
13689
|
+
`);
|
|
13690
|
+
const body = (fmMatch[2] ?? "").trimStart();
|
|
13691
|
+
const parsed = {};
|
|
13692
|
+
for (const line of fmLines) {
|
|
13693
|
+
const colon = line.indexOf(":");
|
|
13694
|
+
if (colon === -1)
|
|
13695
|
+
continue;
|
|
13696
|
+
parsed[line.slice(0, colon).trim()] = line.slice(colon + 1).trim();
|
|
13697
|
+
}
|
|
13698
|
+
updatePrompt(p.id, {
|
|
13699
|
+
title: parsed["title"] ?? p.title,
|
|
13700
|
+
body: body || p.body,
|
|
13701
|
+
description: parsed["description"] || (p.description != null ? p.description : undefined),
|
|
13702
|
+
collection: parsed["collection"] ?? p.collection,
|
|
13703
|
+
tags: parsed["tags"] ? parsed["tags"].split(",").map((t) => t.trim()).filter(Boolean) : p.tags,
|
|
13704
|
+
changed_by: opts.agent
|
|
13705
|
+
});
|
|
13706
|
+
if (isJson(program2))
|
|
13707
|
+
output(program2, getPrompt(p.id));
|
|
13708
|
+
else
|
|
13709
|
+
console.log(chalk4.green(`Updated ${chalk4.bold(p.slug)}`));
|
|
13710
|
+
} catch (e) {
|
|
13711
|
+
handleError(program2, e);
|
|
13712
|
+
}
|
|
13713
|
+
});
|
|
13714
|
+
program2.command("tag <id> [ops...]").description("Patch tags: +foo adds, -bar removes. Or --set foo,bar replaces all tags.").option("--set <tags>", "Replace all tags (comma-separated)").action((id, ops, opts) => {
|
|
13715
|
+
try {
|
|
13716
|
+
const prompt = getPrompt(id);
|
|
13717
|
+
if (!prompt)
|
|
13718
|
+
handleError(program2, `Prompt not found: ${id}`);
|
|
13719
|
+
const p = prompt;
|
|
13720
|
+
let tags;
|
|
13721
|
+
if (opts.set !== undefined) {
|
|
13722
|
+
tags = opts.set.split(",").map((t) => t.trim()).filter(Boolean);
|
|
13723
|
+
} else {
|
|
13724
|
+
tags = [...p.tags];
|
|
13725
|
+
for (const op of ops) {
|
|
13726
|
+
if (op.startsWith("+")) {
|
|
13727
|
+
const tag = op.slice(1);
|
|
13728
|
+
if (!tags.includes(tag))
|
|
13729
|
+
tags.push(tag);
|
|
13730
|
+
} else if (op.startsWith("-")) {
|
|
13731
|
+
const tag = op.slice(1);
|
|
13732
|
+
tags = tags.filter((t) => t !== tag);
|
|
13733
|
+
} else {
|
|
13734
|
+
if (!tags.includes(op))
|
|
13735
|
+
tags.push(op);
|
|
13736
|
+
}
|
|
13737
|
+
}
|
|
13738
|
+
}
|
|
13739
|
+
updatePrompt(p.id, { tags });
|
|
13740
|
+
if (isJson(program2))
|
|
13741
|
+
output(program2, { id: p.id, slug: p.slug, tags });
|
|
13742
|
+
else
|
|
13743
|
+
console.log(`${chalk4.bold(p.slug)} tags: ${tags.length > 0 ? chalk4.cyan(tags.join(", ")) : chalk4.gray("(none)")}`);
|
|
13744
|
+
} catch (e) {
|
|
13745
|
+
handleError(program2, e);
|
|
13746
|
+
}
|
|
13747
|
+
});
|
|
13748
|
+
program2.command("share <id>").description("Export a single prompt as a clean markdown snippet").option("--clipboard", "Copy to clipboard instead of printing").action(async (id, opts) => {
|
|
13749
|
+
try {
|
|
13750
|
+
const prompt = getPrompt(id);
|
|
13751
|
+
if (!prompt)
|
|
13752
|
+
handleError(program2, `Prompt not found: ${id}`);
|
|
13753
|
+
const p = prompt;
|
|
13754
|
+
const lines = [
|
|
13755
|
+
`# ${p.title}`,
|
|
13756
|
+
``
|
|
13757
|
+
];
|
|
13758
|
+
if (p.description)
|
|
13759
|
+
lines.push(`> ${p.description}`, ``);
|
|
13760
|
+
if (p.tags.length > 0)
|
|
13761
|
+
lines.push(`**Tags:** ${p.tags.join(", ")}`, ``);
|
|
13762
|
+
if (p.collection !== "default")
|
|
13763
|
+
lines.push(`**Collection:** ${p.collection}`, ``);
|
|
13764
|
+
lines.push("```", p.body, "```");
|
|
13765
|
+
const markdown = lines.join(`
|
|
13766
|
+
`);
|
|
13767
|
+
if (opts.clipboard) {
|
|
13768
|
+
const { platform: platform2 } = process;
|
|
13769
|
+
if (platform2 === "darwin") {
|
|
13770
|
+
const proc = Bun.spawn(["pbcopy"], { stdin: "pipe" });
|
|
13771
|
+
proc.stdin.write(markdown);
|
|
13772
|
+
proc.stdin.end();
|
|
13773
|
+
await proc.exited;
|
|
13774
|
+
} else if (platform2 === "linux") {
|
|
13775
|
+
try {
|
|
13776
|
+
const proc = Bun.spawn(["xclip", "-selection", "clipboard"], { stdin: "pipe" });
|
|
13777
|
+
proc.stdin.write(markdown);
|
|
13778
|
+
proc.stdin.end();
|
|
13779
|
+
await proc.exited;
|
|
13780
|
+
} catch {
|
|
13781
|
+
const proc = Bun.spawn(["xsel", "--clipboard", "--input"], { stdin: "pipe" });
|
|
13782
|
+
proc.stdin.write(markdown);
|
|
13783
|
+
proc.stdin.end();
|
|
13784
|
+
await proc.exited;
|
|
13785
|
+
}
|
|
13786
|
+
} else {
|
|
13787
|
+
handleError(program2, "Clipboard not supported on this platform.");
|
|
13788
|
+
}
|
|
13789
|
+
console.log(chalk4.green(`Copied ${chalk4.bold(p.slug)} to clipboard`));
|
|
13790
|
+
} else {
|
|
13791
|
+
if (isJson(program2))
|
|
13792
|
+
output(program2, { id: p.id, slug: p.slug, markdown });
|
|
13793
|
+
else
|
|
13794
|
+
console.log(markdown);
|
|
13795
|
+
}
|
|
13796
|
+
} catch (e) {
|
|
13797
|
+
handleError(program2, e);
|
|
13798
|
+
}
|
|
13799
|
+
});
|
|
13800
|
+
program2.command("validate <id>").description("Run lint checks and validate template variables for a single prompt").option("--var <kv>", "Simulate render with key=value (repeatable)", (val, acc) => {
|
|
13801
|
+
acc.push(val);
|
|
13802
|
+
return acc;
|
|
13803
|
+
}, []).option("--vars <json>", "Simulate render with JSON vars").action((id, opts) => {
|
|
13804
|
+
try {
|
|
13805
|
+
const prompt = getPrompt(id);
|
|
13806
|
+
if (!prompt)
|
|
13807
|
+
handleError(program2, `Prompt not found: ${id}`);
|
|
13808
|
+
const p = prompt;
|
|
13809
|
+
const lintIssues = lintPrompt(p);
|
|
13810
|
+
let exitCode = 0;
|
|
13811
|
+
if (isJson(program2)) {
|
|
13812
|
+
const result = { id: p.id, slug: p.slug, lint: lintIssues };
|
|
13813
|
+
if (p.is_template) {
|
|
13814
|
+
const vars = {};
|
|
13815
|
+
if (opts.vars)
|
|
13816
|
+
Object.assign(vars, JSON.parse(opts.vars));
|
|
13817
|
+
for (const kv of opts.var) {
|
|
13818
|
+
const eq = kv.indexOf("=");
|
|
13819
|
+
if (eq !== -1)
|
|
13820
|
+
vars[kv.slice(0, eq)] = kv.slice(eq + 1);
|
|
13821
|
+
}
|
|
13822
|
+
result["vars"] = validateVars(p.body, vars);
|
|
13823
|
+
}
|
|
13824
|
+
output(program2, result);
|
|
13825
|
+
return;
|
|
13826
|
+
}
|
|
13827
|
+
console.log(chalk4.bold(`Validating ${chalk4.green(p.slug)}:`));
|
|
13828
|
+
if (lintIssues.length === 0) {
|
|
13829
|
+
console.log(chalk4.green(" \u2713 Lint: no issues"));
|
|
13830
|
+
} else {
|
|
13831
|
+
for (const issue of lintIssues) {
|
|
13832
|
+
if (issue.severity === "error") {
|
|
13833
|
+
console.log(chalk4.red(` \u2717 [${issue.rule}] ${issue.message}`));
|
|
13834
|
+
exitCode = 1;
|
|
13835
|
+
} else if (issue.severity === "warn") {
|
|
13836
|
+
console.log(chalk4.yellow(` \u26A0 [${issue.rule}] ${issue.message}`));
|
|
13837
|
+
} else {
|
|
13838
|
+
console.log(chalk4.gray(` \u2139 [${issue.rule}] ${issue.message}`));
|
|
13839
|
+
}
|
|
13840
|
+
}
|
|
13841
|
+
}
|
|
13842
|
+
if (p.is_template) {
|
|
13843
|
+
const vars = {};
|
|
13844
|
+
if (opts.vars)
|
|
13845
|
+
Object.assign(vars, JSON.parse(opts.vars));
|
|
13846
|
+
for (const kv of opts.var) {
|
|
13847
|
+
const eq = kv.indexOf("=");
|
|
13848
|
+
if (eq !== -1)
|
|
13849
|
+
vars[kv.slice(0, eq)] = kv.slice(eq + 1);
|
|
13850
|
+
}
|
|
13851
|
+
const varInfo = extractVariableInfo(p.body);
|
|
13852
|
+
const { missing, extra, optional } = validateVars(p.body, vars);
|
|
13853
|
+
console.log(chalk4.bold(`
|
|
13854
|
+
Template variables:`));
|
|
13855
|
+
for (const v of varInfo) {
|
|
13856
|
+
const provided = v.name in vars;
|
|
13857
|
+
const sym = provided ? chalk4.green("\u2713") : v.required ? chalk4.red("\u2717") : chalk4.yellow("\u25CB");
|
|
13858
|
+
const val = provided ? chalk4.gray(` = "${vars[v.name]}"`) : v.default ? chalk4.gray(` (default: "${v.default}")`) : "";
|
|
13859
|
+
console.log(` ${sym} ${v.name}${val}`);
|
|
13860
|
+
}
|
|
13861
|
+
if (missing.length > 0) {
|
|
13862
|
+
console.log(chalk4.red(`
|
|
13863
|
+
Missing required vars: ${missing.join(", ")}`));
|
|
13864
|
+
exitCode = 1;
|
|
13865
|
+
}
|
|
13866
|
+
if (extra.length > 0)
|
|
13867
|
+
console.log(chalk4.yellow(` Extra vars (not in template): ${extra.join(", ")}`));
|
|
13868
|
+
if (optional.length > 0 && Object.keys(vars).length === 0)
|
|
13869
|
+
console.log(chalk4.gray(` Optional vars: ${optional.join(", ")}`));
|
|
13870
|
+
}
|
|
13871
|
+
if (exitCode === 0)
|
|
13872
|
+
console.log(chalk4.green(`
|
|
13873
|
+
\u2713 All checks passed`));
|
|
13874
|
+
else
|
|
13875
|
+
process.exit(exitCode);
|
|
13876
|
+
} catch (e) {
|
|
13877
|
+
handleError(program2, e);
|
|
13878
|
+
}
|
|
13879
|
+
});
|
|
13880
|
+
program2.command("bulk-move <collection> [ids...]").description("Move multiple prompts to a collection. Pass IDs as args or pipe one per line via stdin.").option("-y, --yes", "Skip confirmation").action(async (collection, ids, opts) => {
|
|
13881
|
+
try {
|
|
13882
|
+
let allIds = [...ids];
|
|
13883
|
+
if (process.stdin.isTTY === false && allIds.length === 0) {
|
|
13884
|
+
const chunks = [];
|
|
13885
|
+
for await (const chunk of process.stdin)
|
|
13886
|
+
chunks.push(chunk);
|
|
13887
|
+
allIds = Buffer.concat(chunks).toString("utf-8").split(`
|
|
13888
|
+
`).map((s) => s.trim()).filter(Boolean);
|
|
13889
|
+
}
|
|
13890
|
+
if (allIds.length === 0)
|
|
13891
|
+
handleError(program2, "No IDs provided.");
|
|
13892
|
+
if (!opts.yes && !isJson(program2)) {
|
|
13893
|
+
const { createInterface } = await import("readline");
|
|
13894
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
13895
|
+
await new Promise((resolve) => {
|
|
13896
|
+
rl.question(chalk4.yellow(`Move ${allIds.length} prompt(s) to "${collection}"? [y/N] `), (ans) => {
|
|
13897
|
+
rl.close();
|
|
13898
|
+
if (ans.toLowerCase() !== "y") {
|
|
13899
|
+
console.log("Cancelled.");
|
|
13900
|
+
process.exit(0);
|
|
13901
|
+
}
|
|
13902
|
+
resolve();
|
|
13903
|
+
});
|
|
13904
|
+
});
|
|
13905
|
+
}
|
|
13906
|
+
const results = [];
|
|
13907
|
+
for (const id of allIds) {
|
|
13908
|
+
try {
|
|
13909
|
+
movePrompt(id, collection);
|
|
13910
|
+
const p = getPrompt(id);
|
|
13911
|
+
results.push({ id, slug: p?.slug ?? id, ok: true });
|
|
13912
|
+
} catch (e) {
|
|
13913
|
+
results.push({ id, slug: id, ok: false, error: e instanceof Error ? e.message : String(e) });
|
|
13914
|
+
}
|
|
13915
|
+
}
|
|
13916
|
+
if (isJson(program2)) {
|
|
13917
|
+
output(program2, results);
|
|
13918
|
+
return;
|
|
13919
|
+
}
|
|
13920
|
+
const ok = results.filter((r) => r.ok);
|
|
13921
|
+
const fail = results.filter((r) => !r.ok);
|
|
13922
|
+
if (ok.length > 0)
|
|
13923
|
+
console.log(chalk4.green(`Moved ${ok.length} prompt(s) \u2192 ${chalk4.bold(collection)}`));
|
|
13924
|
+
for (const f of fail)
|
|
13925
|
+
console.error(chalk4.red(` \u2717 ${f.id}: ${f.error}`));
|
|
13926
|
+
} catch (e) {
|
|
13927
|
+
handleError(program2, e);
|
|
13928
|
+
}
|
|
13929
|
+
});
|
|
13930
|
+
program2.command("bulk-tag [ops...] [ids...]").description("Patch tags on multiple prompts. Leading ops start with + or -, rest are IDs. Or pipe IDs via stdin.").helpOption("-h, --help").addHelpText("after", `
|
|
13931
|
+
Examples:
|
|
13932
|
+
prompts bulk-tag +foo -bar PRMT-00001 PRMT-00002
|
|
13933
|
+
echo "PRMT-00001" | prompts bulk-tag +reviewed`).action(async (args) => {
|
|
13934
|
+
try {
|
|
13935
|
+
const ops = [];
|
|
13936
|
+
const ids = [];
|
|
13937
|
+
for (const a of args) {
|
|
13938
|
+
if (a.startsWith("+") || a.startsWith("-"))
|
|
13939
|
+
ops.push(a);
|
|
13940
|
+
else
|
|
13941
|
+
ids.push(a);
|
|
13942
|
+
}
|
|
13943
|
+
let allIds = [...ids];
|
|
13944
|
+
if (process.stdin.isTTY === false && allIds.length === 0) {
|
|
13945
|
+
const chunks = [];
|
|
13946
|
+
for await (const chunk of process.stdin)
|
|
13947
|
+
chunks.push(chunk);
|
|
13948
|
+
allIds = Buffer.concat(chunks).toString("utf-8").split(`
|
|
13949
|
+
`).map((s) => s.trim()).filter(Boolean);
|
|
13950
|
+
}
|
|
13951
|
+
if (allIds.length === 0)
|
|
13952
|
+
handleError(program2, "No IDs provided.");
|
|
13953
|
+
if (ops.length === 0)
|
|
13954
|
+
handleError(program2, "No tag ops provided. Use +tag to add, -tag to remove.");
|
|
13955
|
+
const results = [];
|
|
13956
|
+
for (const id of allIds) {
|
|
13957
|
+
const prompt = getPrompt(id);
|
|
13958
|
+
if (!prompt) {
|
|
13959
|
+
console.error(chalk4.red(` \u2717 Not found: ${id}`));
|
|
13960
|
+
continue;
|
|
13961
|
+
}
|
|
13962
|
+
let tags = [...prompt.tags];
|
|
13963
|
+
for (const op of ops) {
|
|
13964
|
+
if (op.startsWith("+")) {
|
|
13965
|
+
const t = op.slice(1);
|
|
13966
|
+
if (!tags.includes(t))
|
|
13967
|
+
tags.push(t);
|
|
13968
|
+
} else if (op.startsWith("-")) {
|
|
13969
|
+
const t = op.slice(1);
|
|
13970
|
+
tags = tags.filter((x) => x !== t);
|
|
13971
|
+
}
|
|
13972
|
+
}
|
|
13973
|
+
updatePrompt(prompt.id, { tags });
|
|
13974
|
+
results.push({ id: prompt.id, slug: prompt.slug, tags });
|
|
13975
|
+
}
|
|
13976
|
+
if (isJson(program2)) {
|
|
13977
|
+
output(program2, results);
|
|
13978
|
+
return;
|
|
13979
|
+
}
|
|
13980
|
+
console.log(chalk4.green(`Updated ${results.length} prompt(s)`));
|
|
13981
|
+
for (const r of results)
|
|
13982
|
+
console.log(` ${chalk4.bold(r.slug)} ${chalk4.cyan(r.tags.join(", ") || "(no tags)")}`);
|
|
13983
|
+
} catch (e) {
|
|
13984
|
+
handleError(program2, e);
|
|
13985
|
+
}
|
|
13986
|
+
});
|
|
13987
|
+
program2.command("bulk-delete [ids...]").alias("bulk-rm").description("Delete multiple prompts. Pass IDs as args or pipe one per line via stdin.").option("-y, --yes", "Skip confirmation").action(async (ids, opts) => {
|
|
13988
|
+
try {
|
|
13989
|
+
const { deletePrompt: deletePrompt2 } = await Promise.resolve().then(() => (init_prompts(), exports_prompts));
|
|
13990
|
+
let allIds = [...ids];
|
|
13991
|
+
if (process.stdin.isTTY === false && allIds.length === 0) {
|
|
13992
|
+
const chunks = [];
|
|
13993
|
+
for await (const chunk of process.stdin)
|
|
13994
|
+
chunks.push(chunk);
|
|
13995
|
+
allIds = Buffer.concat(chunks).toString("utf-8").split(`
|
|
13996
|
+
`).map((s) => s.trim()).filter(Boolean);
|
|
13997
|
+
}
|
|
13998
|
+
if (allIds.length === 0)
|
|
13999
|
+
handleError(program2, "No IDs provided.");
|
|
14000
|
+
if (!opts.yes && !isJson(program2)) {
|
|
14001
|
+
const { createInterface } = await import("readline");
|
|
14002
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
14003
|
+
await new Promise((resolve) => {
|
|
14004
|
+
rl.question(chalk4.red(`Delete ${allIds.length} prompt(s)? This cannot be undone. [y/N] `), (ans) => {
|
|
14005
|
+
rl.close();
|
|
14006
|
+
if (ans.toLowerCase() !== "y") {
|
|
14007
|
+
console.log("Cancelled.");
|
|
14008
|
+
process.exit(0);
|
|
14009
|
+
}
|
|
14010
|
+
resolve();
|
|
14011
|
+
});
|
|
14012
|
+
});
|
|
14013
|
+
}
|
|
14014
|
+
let deleted = 0;
|
|
14015
|
+
const errors = [];
|
|
14016
|
+
for (const id of allIds) {
|
|
14017
|
+
try {
|
|
14018
|
+
deletePrompt2(id);
|
|
14019
|
+
deleted++;
|
|
14020
|
+
} catch (e) {
|
|
14021
|
+
errors.push(`${id}: ${e instanceof Error ? e.message : String(e)}`);
|
|
14022
|
+
}
|
|
14023
|
+
}
|
|
14024
|
+
if (isJson(program2)) {
|
|
14025
|
+
output(program2, { deleted, errors });
|
|
14026
|
+
return;
|
|
14027
|
+
}
|
|
14028
|
+
console.log(chalk4.red(`Deleted ${deleted} prompt(s)`));
|
|
14029
|
+
for (const e of errors)
|
|
14030
|
+
console.error(chalk4.red(` \u2717 ${e}`));
|
|
14031
|
+
} catch (e) {
|
|
14032
|
+
handleError(program2, e);
|
|
14033
|
+
}
|
|
14034
|
+
});
|
|
14035
|
+
}
|
|
14036
|
+
|
|
14037
|
+
// src/cli/commands/config.ts
|
|
14038
|
+
init_prompts();
|
|
14039
|
+
import chalk5 from "chalk";
|
|
14040
|
+
import { homedir as homedir6 } from "os";
|
|
14041
|
+
import { join as join7, resolve } from "path";
|
|
14042
|
+
import { existsSync as existsSync6, readFileSync as readFileSync2, writeFileSync as writeFileSync2, readdirSync as readdirSync3, statSync } from "fs";
|
|
14043
|
+
var AGENT_CONFIGS = {
|
|
14044
|
+
claude: {
|
|
14045
|
+
global: ".claude/CLAUDE.md",
|
|
14046
|
+
local: "CLAUDE.md",
|
|
14047
|
+
label: "Claude Code"
|
|
14048
|
+
},
|
|
14049
|
+
agents: {
|
|
14050
|
+
global: ".agents/AGENTS.md",
|
|
14051
|
+
local: "AGENTS.md",
|
|
14052
|
+
label: "OpenAI Agents SDK"
|
|
14053
|
+
},
|
|
14054
|
+
gemini: {
|
|
14055
|
+
global: ".gemini/GEMINI.md",
|
|
14056
|
+
local: ".gemini/GEMINI.md",
|
|
14057
|
+
label: "Gemini CLI"
|
|
14058
|
+
},
|
|
14059
|
+
codex: {
|
|
14060
|
+
global: ".codex/CODEX.md",
|
|
14061
|
+
local: "CODEX.md",
|
|
14062
|
+
label: "OpenAI Codex CLI"
|
|
14063
|
+
},
|
|
14064
|
+
cursor: {
|
|
14065
|
+
global: ".cursor/rules",
|
|
14066
|
+
local: ".cursorrules",
|
|
14067
|
+
label: "Cursor"
|
|
14068
|
+
},
|
|
14069
|
+
aider: {
|
|
14070
|
+
global: ".aider/CONVENTIONS.md",
|
|
14071
|
+
local: ".aider.conventions.md",
|
|
14072
|
+
label: "Aider"
|
|
14073
|
+
}
|
|
14074
|
+
};
|
|
14075
|
+
function resolveConfigPath(agent, globalFlag) {
|
|
14076
|
+
const cfg = AGENT_CONFIGS[agent.toLowerCase()];
|
|
14077
|
+
if (!cfg)
|
|
14078
|
+
return null;
|
|
14079
|
+
if (globalFlag)
|
|
14080
|
+
return join7(homedir6(), cfg.global);
|
|
14081
|
+
return resolve(process.cwd(), cfg.local);
|
|
14082
|
+
}
|
|
14083
|
+
function registerConfigCommands(program2) {
|
|
14084
|
+
const configCmd = program2.command("config").description("Manage AI agent config files (CLAUDE.md, AGENTS.md, GEMINI.md, etc.)");
|
|
14085
|
+
configCmd.command("list").description("List all known config files (global + project)").action(() => {
|
|
14086
|
+
try {
|
|
14087
|
+
const rows = [];
|
|
14088
|
+
for (const [key, cfg] of Object.entries(AGENT_CONFIGS)) {
|
|
14089
|
+
const globalPath = join7(homedir6(), cfg.global);
|
|
14090
|
+
const localPath = resolve(process.cwd(), cfg.local);
|
|
14091
|
+
const globalExists = existsSync6(globalPath);
|
|
14092
|
+
const localExists = existsSync6(localPath);
|
|
14093
|
+
rows.push({
|
|
14094
|
+
agent: key,
|
|
14095
|
+
label: cfg.label,
|
|
14096
|
+
scope: "global",
|
|
14097
|
+
path: globalPath,
|
|
14098
|
+
exists: globalExists,
|
|
14099
|
+
size: globalExists ? statSync(globalPath).size : undefined
|
|
14100
|
+
});
|
|
14101
|
+
if (globalPath !== localPath) {
|
|
14102
|
+
rows.push({
|
|
14103
|
+
agent: key,
|
|
14104
|
+
label: cfg.label,
|
|
14105
|
+
scope: "project",
|
|
14106
|
+
path: localPath,
|
|
14107
|
+
exists: localExists,
|
|
14108
|
+
size: localExists ? statSync(localPath).size : undefined
|
|
14109
|
+
});
|
|
14110
|
+
}
|
|
14111
|
+
}
|
|
14112
|
+
if (isJson(program2)) {
|
|
14113
|
+
output(program2, rows);
|
|
14114
|
+
return;
|
|
14115
|
+
}
|
|
14116
|
+
const existingRows = rows.filter((r) => r.exists);
|
|
14117
|
+
const missingRows = rows.filter((r) => !r.exists);
|
|
14118
|
+
if (existingRows.length > 0) {
|
|
14119
|
+
console.log(chalk5.bold("Found:"));
|
|
14120
|
+
for (const r of existingRows) {
|
|
14121
|
+
const size = r.size !== undefined ? chalk5.gray(` (${r.size}B)`) : "";
|
|
14122
|
+
const scope = r.scope === "global" ? chalk5.blue("[global]") : chalk5.cyan("[project]");
|
|
14123
|
+
console.log(` ${scope} ${chalk5.bold(r.label)} ${chalk5.gray(r.path)}${size}`);
|
|
14124
|
+
}
|
|
14125
|
+
}
|
|
14126
|
+
if (missingRows.length > 0) {
|
|
14127
|
+
console.log(chalk5.dim(`
|
|
14128
|
+
Not present:`));
|
|
14129
|
+
for (const r of missingRows) {
|
|
14130
|
+
const scope = r.scope === "global" ? chalk5.blue("[global]") : chalk5.cyan("[project]");
|
|
14131
|
+
console.log(chalk5.gray(` ${scope} ${r.label} ${r.path}`));
|
|
14132
|
+
}
|
|
14133
|
+
}
|
|
14134
|
+
} catch (e) {
|
|
14135
|
+
handleError(program2, e);
|
|
14136
|
+
}
|
|
14137
|
+
});
|
|
14138
|
+
configCmd.command("get <agent>").description("Print the contents of an agent config file").option("-g, --global", "Use global (~/) config instead of project-local").action((agent, opts) => {
|
|
14139
|
+
try {
|
|
14140
|
+
const path = resolveConfigPath(agent, Boolean(opts.global));
|
|
14141
|
+
if (!path)
|
|
14142
|
+
handleError(program2, `Unknown agent "${agent}". Known: ${Object.keys(AGENT_CONFIGS).join(", ")}`);
|
|
14143
|
+
if (!existsSync6(path))
|
|
14144
|
+
handleError(program2, `Config file not found: ${path}`);
|
|
14145
|
+
const content = readFileSync2(path, "utf-8");
|
|
14146
|
+
if (isJson(program2))
|
|
14147
|
+
output(program2, { agent, path, content });
|
|
14148
|
+
else
|
|
14149
|
+
console.log(content);
|
|
14150
|
+
} catch (e) {
|
|
14151
|
+
handleError(program2, e);
|
|
14152
|
+
}
|
|
14153
|
+
});
|
|
14154
|
+
configCmd.command("edit <agent>").description("Open an agent config file in $EDITOR (creates if missing)").option("-g, --global", "Edit global (~/) config instead of project-local").action(async (agent, opts) => {
|
|
14155
|
+
try {
|
|
14156
|
+
const path = resolveConfigPath(agent, Boolean(opts.global));
|
|
14157
|
+
if (!path)
|
|
14158
|
+
handleError(program2, `Unknown agent "${agent}". Known: ${Object.keys(AGENT_CONFIGS).join(", ")}`);
|
|
14159
|
+
const { mkdirSync: mkdirSync4 } = await import("fs");
|
|
14160
|
+
const { dirname: dirname2 } = await import("path");
|
|
14161
|
+
mkdirSync4(dirname2(path), { recursive: true });
|
|
14162
|
+
if (!existsSync6(path))
|
|
14163
|
+
writeFileSync2(path, "", "utf-8");
|
|
14164
|
+
const editor = process.env["EDITOR"] ?? process.env["VISUAL"] ?? "vi";
|
|
14165
|
+
const proc = Bun.spawn([editor, path], { stdin: "inherit", stdout: "inherit", stderr: "inherit" });
|
|
14166
|
+
await proc.exited;
|
|
14167
|
+
console.log(chalk5.green(`Saved ${path}`));
|
|
14168
|
+
} catch (e) {
|
|
14169
|
+
handleError(program2, e);
|
|
14170
|
+
}
|
|
14171
|
+
});
|
|
14172
|
+
configCmd.command("inject <slug> <agent>").description("Append a saved prompt's body into an agent config file").option("-g, --global", "Inject into global (~/) config instead of project-local").option("--section <heading>", "Append under a markdown heading (creates if missing)").option("--replace", "Replace section content instead of appending").action(async (slug, agent, opts) => {
|
|
14173
|
+
try {
|
|
14174
|
+
const prompt = getPrompt(slug);
|
|
14175
|
+
if (!prompt)
|
|
14176
|
+
handleError(program2, `Prompt not found: ${slug}`);
|
|
14177
|
+
const path = resolveConfigPath(agent, Boolean(opts.global));
|
|
14178
|
+
if (!path)
|
|
14179
|
+
handleError(program2, `Unknown agent "${agent}". Known: ${Object.keys(AGENT_CONFIGS).join(", ")}`);
|
|
14180
|
+
const { mkdirSync: mkdirSync4 } = await import("fs");
|
|
14181
|
+
const { dirname: dirname2 } = await import("path");
|
|
14182
|
+
mkdirSync4(dirname2(path), { recursive: true });
|
|
14183
|
+
let existing = existsSync6(path) ? readFileSync2(path, "utf-8") : "";
|
|
14184
|
+
const injection = `
|
|
14185
|
+
${prompt.body}
|
|
14186
|
+
`;
|
|
14187
|
+
if (opts.section) {
|
|
14188
|
+
const heading = `## ${opts.section}`;
|
|
14189
|
+
const idx = existing.indexOf(heading);
|
|
14190
|
+
if (idx === -1) {
|
|
14191
|
+
existing = existing.trimEnd() + `
|
|
14192
|
+
|
|
14193
|
+
${heading}
|
|
14194
|
+
${injection}`;
|
|
14195
|
+
} else if (opts.replace) {
|
|
14196
|
+
const afterHeading = idx + heading.length;
|
|
14197
|
+
const nextSection = existing.indexOf(`
|
|
14198
|
+
## `, afterHeading);
|
|
14199
|
+
const sectionEnd = nextSection === -1 ? existing.length : nextSection;
|
|
14200
|
+
existing = existing.slice(0, afterHeading) + `
|
|
14201
|
+
${injection}` + existing.slice(sectionEnd);
|
|
14202
|
+
} else {
|
|
14203
|
+
const afterHeading = idx + heading.length;
|
|
14204
|
+
const nextSection = existing.indexOf(`
|
|
14205
|
+
## `, afterHeading);
|
|
14206
|
+
const insertAt = nextSection === -1 ? existing.length : nextSection;
|
|
14207
|
+
existing = existing.slice(0, insertAt).trimEnd() + `
|
|
14208
|
+
${injection}
|
|
14209
|
+
` + existing.slice(insertAt);
|
|
14210
|
+
}
|
|
14211
|
+
} else {
|
|
14212
|
+
existing = existing.trimEnd() + `
|
|
14213
|
+
${injection}`;
|
|
14214
|
+
}
|
|
14215
|
+
writeFileSync2(path, existing);
|
|
14216
|
+
if (isJson(program2))
|
|
14217
|
+
output(program2, { injected: true, slug: prompt.slug, path, section: opts.section });
|
|
14218
|
+
else
|
|
14219
|
+
console.log(chalk5.green(`Injected "${chalk5.bold(prompt.slug)}" into ${path}`));
|
|
14220
|
+
} catch (e) {
|
|
14221
|
+
handleError(program2, e);
|
|
14222
|
+
}
|
|
14223
|
+
});
|
|
14224
|
+
configCmd.command("set <agent>").description("Write content to an agent config file (reads from stdin or --body)").option("-g, --global", "Write to global (~/) config instead of project-local").option("--body <content>", "Content to write (use - or omit for stdin)").option("-y, --yes", "Skip confirmation when overwriting existing file").action(async (agent, opts) => {
|
|
14225
|
+
try {
|
|
14226
|
+
const path = resolveConfigPath(agent, Boolean(opts.global));
|
|
14227
|
+
if (!path)
|
|
14228
|
+
handleError(program2, `Unknown agent "${agent}". Known: ${Object.keys(AGENT_CONFIGS).join(", ")}`);
|
|
14229
|
+
let content = opts.body ?? "";
|
|
14230
|
+
if (!content) {
|
|
14231
|
+
const chunks = [];
|
|
14232
|
+
for await (const chunk of process.stdin)
|
|
14233
|
+
chunks.push(chunk);
|
|
14234
|
+
content = Buffer.concat(chunks).toString("utf-8");
|
|
14235
|
+
}
|
|
14236
|
+
if (existsSync6(path) && !opts.yes && !isJson(program2)) {
|
|
14237
|
+
const { createInterface } = await import("readline");
|
|
14238
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
14239
|
+
await new Promise((resolve2) => {
|
|
14240
|
+
rl.question(chalk5.yellow(`Overwrite existing ${path}? [y/N] `), (ans) => {
|
|
14241
|
+
rl.close();
|
|
14242
|
+
if (ans.toLowerCase() !== "y") {
|
|
14243
|
+
console.log("Cancelled.");
|
|
14244
|
+
process.exit(0);
|
|
14245
|
+
}
|
|
14246
|
+
resolve2();
|
|
14247
|
+
});
|
|
14248
|
+
});
|
|
14249
|
+
}
|
|
14250
|
+
const { mkdirSync: mkdirSync4 } = await import("fs");
|
|
14251
|
+
const { dirname: dirname2 } = await import("path");
|
|
14252
|
+
mkdirSync4(dirname2(path), { recursive: true });
|
|
14253
|
+
writeFileSync2(path, content);
|
|
14254
|
+
if (isJson(program2))
|
|
14255
|
+
output(program2, { written: true, agent, path });
|
|
14256
|
+
else
|
|
14257
|
+
console.log(chalk5.green(`Wrote ${path}`));
|
|
14258
|
+
} catch (e) {
|
|
14259
|
+
handleError(program2, e);
|
|
14260
|
+
}
|
|
14261
|
+
});
|
|
14262
|
+
configCmd.command("scan [workspace]").description("Scan repos in a workspace directory for missing/present agent config files").option("--depth <n>", "Max directory depth to search for git repos", "3").option("--agents <list>", "Comma-separated agents to check (default: all)").option("--missing-only", "Only show repos with missing configs").action((workspace, opts) => {
|
|
14263
|
+
try {
|
|
14264
|
+
let scanDir = function(dir, depth) {
|
|
14265
|
+
if (depth > maxDepth)
|
|
14266
|
+
return;
|
|
14267
|
+
try {
|
|
14268
|
+
const entries = readdirSync3(dir, { withFileTypes: true });
|
|
14269
|
+
if (entries.some((e) => e.name === ".git" && e.isDirectory())) {
|
|
14270
|
+
repos.push(dir);
|
|
14271
|
+
return;
|
|
14272
|
+
}
|
|
14273
|
+
for (const entry of entries) {
|
|
14274
|
+
if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
|
|
14275
|
+
scanDir(join7(dir, entry.name), depth + 1);
|
|
14276
|
+
}
|
|
14277
|
+
}
|
|
14278
|
+
} catch {}
|
|
14279
|
+
};
|
|
14280
|
+
const wsDir = workspace ? resolve(workspace) : resolve(homedir6(), "workspace");
|
|
14281
|
+
if (!existsSync6(wsDir))
|
|
14282
|
+
handleError(program2, `Workspace not found: ${wsDir}`);
|
|
14283
|
+
const maxDepth = parseInt(opts.depth ?? "3") || 3;
|
|
14284
|
+
const agentFilter = opts.agents ? opts.agents.split(",").map((a) => a.trim().toLowerCase()) : Object.keys(AGENT_CONFIGS);
|
|
14285
|
+
const repos = [];
|
|
14286
|
+
scanDir(wsDir, 0);
|
|
14287
|
+
const reports = [];
|
|
14288
|
+
for (const repo of repos) {
|
|
14289
|
+
const configs = {};
|
|
14290
|
+
let missingCount = 0;
|
|
14291
|
+
let presentCount = 0;
|
|
14292
|
+
for (const agentKey of agentFilter) {
|
|
14293
|
+
const cfg = AGENT_CONFIGS[agentKey];
|
|
14294
|
+
if (!cfg)
|
|
14295
|
+
continue;
|
|
14296
|
+
const localPath = join7(repo, cfg.local);
|
|
14297
|
+
const exists = existsSync6(localPath);
|
|
14298
|
+
configs[agentKey] = {
|
|
14299
|
+
present: exists,
|
|
14300
|
+
path: localPath,
|
|
14301
|
+
size: exists ? statSync(localPath).size : undefined
|
|
14302
|
+
};
|
|
14303
|
+
if (exists)
|
|
14304
|
+
presentCount++;
|
|
14305
|
+
else
|
|
14306
|
+
missingCount++;
|
|
14307
|
+
}
|
|
14308
|
+
if (!opts.missingOnly || missingCount > 0) {
|
|
14309
|
+
reports.push({ repo, configs, missing_count: missingCount, present_count: presentCount });
|
|
14310
|
+
}
|
|
14311
|
+
}
|
|
14312
|
+
if (isJson(program2)) {
|
|
14313
|
+
output(program2, { workspace: wsDir, repos_scanned: repos.length, reports });
|
|
14314
|
+
return;
|
|
14315
|
+
}
|
|
14316
|
+
console.log(chalk5.bold(`Scanned ${repos.length} repo(s) in ${wsDir}
|
|
14317
|
+
`));
|
|
14318
|
+
if (reports.length === 0) {
|
|
14319
|
+
console.log(chalk5.green("\u2713 All repos have all config files."));
|
|
14320
|
+
return;
|
|
14321
|
+
}
|
|
14322
|
+
for (const r of reports) {
|
|
14323
|
+
const rel = r.repo.replace(wsDir + "/", "");
|
|
14324
|
+
const status = r.missing_count === 0 ? chalk5.green(`\u2713 ${rel}`) : chalk5.yellow(`\u25B3 ${rel}`);
|
|
14325
|
+
console.log(status + chalk5.gray(` (${r.present_count} present, ${r.missing_count} missing)`));
|
|
14326
|
+
for (const [agent, info] of Object.entries(r.configs)) {
|
|
14327
|
+
const sym = info.present ? chalk5.green(" \u2713") : chalk5.red(" \u2717");
|
|
14328
|
+
const label = AGENT_CONFIGS[agent]?.label ?? agent;
|
|
14329
|
+
const detail = info.present ? chalk5.gray(` ${info.size}B`) : chalk5.gray(` ${info.path}`);
|
|
14330
|
+
console.log(`${sym} ${label}${detail}`);
|
|
14331
|
+
}
|
|
14332
|
+
console.log();
|
|
14333
|
+
}
|
|
14334
|
+
const totalMissing = reports.reduce((s, r) => s + r.missing_count, 0);
|
|
14335
|
+
if (totalMissing > 0) {
|
|
14336
|
+
console.log(chalk5.yellow(`${totalMissing} config file(s) missing across ${reports.filter((r) => r.missing_count > 0).length} repo(s)`));
|
|
14337
|
+
console.log(chalk5.gray(`Use \`prompts config inject <slug> <agent>\` to add config content from your saved prompts.`));
|
|
14338
|
+
} else {
|
|
14339
|
+
console.log(chalk5.green("\u2713 All checked repos have all config files."));
|
|
14340
|
+
}
|
|
14341
|
+
} catch (e) {
|
|
14342
|
+
handleError(program2, e);
|
|
14343
|
+
}
|
|
14344
|
+
});
|
|
14345
|
+
}
|
|
14346
|
+
|
|
13393
14347
|
// src/cli/index.tsx
|
|
13394
14348
|
var require2 = createRequire2(import.meta.url);
|
|
13395
14349
|
var pkg = require2("../../package.json");
|
|
13396
14350
|
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");
|
|
13397
14351
|
registerPromptCommands(program2);
|
|
13398
14352
|
registerVersionCommands(program2);
|
|
14353
|
+
registerQolCommands(program2);
|
|
14354
|
+
registerConfigCommands(program2);
|
|
13399
14355
|
program2.command("collections").description("List all collections").action(() => {
|
|
13400
14356
|
try {
|
|
13401
14357
|
const cols = listCollections();
|
|
@@ -13403,9 +14359,9 @@ program2.command("collections").description("List all collections").action(() =>
|
|
|
13403
14359
|
output(program2, cols);
|
|
13404
14360
|
} else {
|
|
13405
14361
|
for (const c of cols) {
|
|
13406
|
-
console.log(`${
|
|
14362
|
+
console.log(`${chalk6.bold(c.name)} ${chalk6.gray(`${c.prompt_count} prompt(s)`)}`);
|
|
13407
14363
|
if (c.description)
|
|
13408
|
-
console.log(
|
|
14364
|
+
console.log(chalk6.gray(" " + c.description));
|
|
13409
14365
|
}
|
|
13410
14366
|
}
|
|
13411
14367
|
} catch (e) {
|
|
@@ -13418,7 +14374,7 @@ program2.command("move <id> <collection>").description("Move a prompt to a diffe
|
|
|
13418
14374
|
if (isJson(program2))
|
|
13419
14375
|
output(program2, { moved: true, id, collection });
|
|
13420
14376
|
else
|
|
13421
|
-
console.log(`${
|
|
14377
|
+
console.log(`${chalk6.green("Moved")} ${id} \u2192 ${chalk6.bold(collection)}`);
|
|
13422
14378
|
} catch (e) {
|
|
13423
14379
|
handleError(program2, e);
|
|
13424
14380
|
}
|
|
@@ -13428,9 +14384,9 @@ program2.command("export").description("Export prompts as JSON").option("-c, --c
|
|
|
13428
14384
|
const data = exportToJson(opts["collection"]);
|
|
13429
14385
|
const json = JSON.stringify(data, null, 2);
|
|
13430
14386
|
if (opts["output"]) {
|
|
13431
|
-
const { writeFileSync:
|
|
13432
|
-
|
|
13433
|
-
console.log(
|
|
14387
|
+
const { writeFileSync: writeFileSync3 } = await import("fs");
|
|
14388
|
+
writeFileSync3(opts["output"], json);
|
|
14389
|
+
console.log(chalk6.green(`Exported ${data.prompts.length} prompt(s) to ${opts["output"]}`));
|
|
13434
14390
|
} else {
|
|
13435
14391
|
console.log(json);
|
|
13436
14392
|
}
|
|
@@ -13440,18 +14396,18 @@ program2.command("export").description("Export prompts as JSON").option("-c, --c
|
|
|
13440
14396
|
});
|
|
13441
14397
|
program2.command("import <file>").description("Import prompts from a JSON file").option("--agent <name>").action(async (file, opts) => {
|
|
13442
14398
|
try {
|
|
13443
|
-
const { readFileSync:
|
|
13444
|
-
const raw = JSON.parse(
|
|
14399
|
+
const { readFileSync: readFileSync3 } = await import("fs");
|
|
14400
|
+
const raw = JSON.parse(readFileSync3(file, "utf-8"));
|
|
13445
14401
|
const items = Array.isArray(raw) ? raw : raw.prompts ?? [];
|
|
13446
14402
|
const results = importFromJson(items, opts["agent"]);
|
|
13447
14403
|
if (isJson(program2))
|
|
13448
14404
|
output(program2, results);
|
|
13449
14405
|
else {
|
|
13450
|
-
console.log(
|
|
14406
|
+
console.log(chalk6.green(`Created: ${results.created}, Updated: ${results.updated}`));
|
|
13451
14407
|
if (results.errors.length > 0) {
|
|
13452
|
-
console.error(
|
|
14408
|
+
console.error(chalk6.red(`Errors: ${results.errors.length}`));
|
|
13453
14409
|
for (const e of results.errors)
|
|
13454
|
-
console.error(
|
|
14410
|
+
console.error(chalk6.red(` ${e.item}: ${e.error}`));
|
|
13455
14411
|
}
|
|
13456
14412
|
}
|
|
13457
14413
|
} catch (e) {
|
|
@@ -13464,19 +14420,19 @@ program2.command("stats").description("Show usage statistics").action(() => {
|
|
|
13464
14420
|
if (isJson(program2)) {
|
|
13465
14421
|
output(program2, stats);
|
|
13466
14422
|
} else {
|
|
13467
|
-
console.log(
|
|
14423
|
+
console.log(chalk6.bold("Prompt Stats"));
|
|
13468
14424
|
console.log(` Total: ${stats.total_prompts} Templates: ${stats.total_templates} Collections: ${stats.total_collections}`);
|
|
13469
14425
|
if (stats.most_used.length > 0) {
|
|
13470
|
-
console.log(
|
|
14426
|
+
console.log(chalk6.bold(`
|
|
13471
14427
|
Most used:`));
|
|
13472
14428
|
for (const p of stats.most_used)
|
|
13473
|
-
console.log(` ${
|
|
14429
|
+
console.log(` ${chalk6.green(p.slug)} ${chalk6.gray(`${p.use_count}\xD7`)}`);
|
|
13474
14430
|
}
|
|
13475
14431
|
if (stats.by_collection.length > 0) {
|
|
13476
|
-
console.log(
|
|
14432
|
+
console.log(chalk6.bold(`
|
|
13477
14433
|
By collection:`));
|
|
13478
14434
|
for (const c of stats.by_collection)
|
|
13479
|
-
console.log(` ${
|
|
14435
|
+
console.log(` ${chalk6.bold(c.collection)} ${chalk6.gray(`${c.count}`)}`);
|
|
13480
14436
|
}
|
|
13481
14437
|
}
|
|
13482
14438
|
} catch (e) {
|
|
@@ -13492,12 +14448,12 @@ program2.command("recent [n]").description("Show recently used prompts (default:
|
|
|
13492
14448
|
return;
|
|
13493
14449
|
}
|
|
13494
14450
|
if (prompts.length === 0) {
|
|
13495
|
-
console.log(
|
|
14451
|
+
console.log(chalk6.gray("No recently used prompts."));
|
|
13496
14452
|
return;
|
|
13497
14453
|
}
|
|
13498
14454
|
for (const p of prompts) {
|
|
13499
|
-
const ago =
|
|
13500
|
-
console.log(`${
|
|
14455
|
+
const ago = chalk6.gray(new Date(p.last_used_at).toLocaleString());
|
|
14456
|
+
console.log(`${chalk6.bold(p.id)} ${chalk6.green(p.slug)} ${p.title} ${ago}`);
|
|
13501
14457
|
}
|
|
13502
14458
|
} catch (e) {
|
|
13503
14459
|
handleError(program2, e);
|
|
@@ -13512,27 +14468,27 @@ program2.command("lint").description("Check prompt quality: missing descriptions
|
|
|
13512
14468
|
return;
|
|
13513
14469
|
}
|
|
13514
14470
|
if (results.length === 0) {
|
|
13515
|
-
console.log(
|
|
14471
|
+
console.log(chalk6.green("\u2713 All prompts pass lint."));
|
|
13516
14472
|
return;
|
|
13517
14473
|
}
|
|
13518
14474
|
let errors = 0, warns = 0, infos = 0;
|
|
13519
14475
|
for (const { prompt: p, issues } of results) {
|
|
13520
14476
|
console.log(`
|
|
13521
|
-
${
|
|
14477
|
+
${chalk6.bold(p.slug)} ${chalk6.gray(p.id)}`);
|
|
13522
14478
|
for (const issue of issues) {
|
|
13523
14479
|
if (issue.severity === "error") {
|
|
13524
|
-
console.log(
|
|
14480
|
+
console.log(chalk6.red(` \u2717 [${issue.rule}] ${issue.message}`));
|
|
13525
14481
|
errors++;
|
|
13526
14482
|
} else if (issue.severity === "warn") {
|
|
13527
|
-
console.log(
|
|
14483
|
+
console.log(chalk6.yellow(` \u26A0 [${issue.rule}] ${issue.message}`));
|
|
13528
14484
|
warns++;
|
|
13529
14485
|
} else {
|
|
13530
|
-
console.log(
|
|
14486
|
+
console.log(chalk6.gray(` \u2139 [${issue.rule}] ${issue.message}`));
|
|
13531
14487
|
infos++;
|
|
13532
14488
|
}
|
|
13533
14489
|
}
|
|
13534
14490
|
}
|
|
13535
|
-
console.log(
|
|
14491
|
+
console.log(chalk6.bold(`
|
|
13536
14492
|
${results.length} prompt(s) with issues \u2014 ${errors} errors, ${warns} warnings, ${infos} info`));
|
|
13537
14493
|
if (errors > 0)
|
|
13538
14494
|
process.exit(1);
|
|
@@ -13553,24 +14509,24 @@ program2.command("stale [days]").description("List prompts not used in N days (d
|
|
|
13553
14509
|
return;
|
|
13554
14510
|
}
|
|
13555
14511
|
if (expired.length > 0) {
|
|
13556
|
-
console.log(
|
|
14512
|
+
console.log(chalk6.red(`
|
|
13557
14513
|
Expired (${expired.length}):`));
|
|
13558
14514
|
for (const p of expired)
|
|
13559
|
-
console.log(
|
|
14515
|
+
console.log(chalk6.red(` \u2717 ${p.slug}`) + chalk6.gray(` expired ${new Date(p.expires_at).toLocaleDateString()}`));
|
|
13560
14516
|
}
|
|
13561
14517
|
if (stale.length === 0 && expired.length === 0) {
|
|
13562
|
-
console.log(
|
|
14518
|
+
console.log(chalk6.green(`No stale prompts (threshold: ${threshold} days).`));
|
|
13563
14519
|
return;
|
|
13564
14520
|
}
|
|
13565
14521
|
if (stale.length > 0) {
|
|
13566
|
-
console.log(
|
|
14522
|
+
console.log(chalk6.bold(`
|
|
13567
14523
|
Stale prompts (not used in ${threshold}+ days):`));
|
|
13568
14524
|
for (const p of stale) {
|
|
13569
|
-
const last = p.last_used_at ?
|
|
13570
|
-
console.log(` ${
|
|
14525
|
+
const last = p.last_used_at ? chalk6.gray(new Date(p.last_used_at).toLocaleDateString()) : chalk6.red("never");
|
|
14526
|
+
console.log(` ${chalk6.green(p.slug)} ${chalk6.gray(`${p.use_count}\xD7`)} last used: ${last}`);
|
|
13571
14527
|
}
|
|
13572
14528
|
}
|
|
13573
|
-
console.log(
|
|
14529
|
+
console.log(chalk6.gray(`
|
|
13574
14530
|
${stale.length} stale prompt(s)`));
|
|
13575
14531
|
} catch (e) {
|
|
13576
14532
|
handleError(program2, e);
|
|
@@ -13582,7 +14538,7 @@ program2.command("pin <id>").description("Pin a prompt so it always appears firs
|
|
|
13582
14538
|
if (isJson(program2))
|
|
13583
14539
|
output(program2, p);
|
|
13584
14540
|
else
|
|
13585
|
-
console.log(
|
|
14541
|
+
console.log(chalk6.yellow(`\uD83D\uDCCC Pinned ${chalk6.bold(p.slug)}`));
|
|
13586
14542
|
} catch (e) {
|
|
13587
14543
|
handleError(program2, e);
|
|
13588
14544
|
}
|
|
@@ -13593,7 +14549,7 @@ program2.command("unpin <id>").description("Unpin a prompt").action((id) => {
|
|
|
13593
14549
|
if (isJson(program2))
|
|
13594
14550
|
output(program2, p);
|
|
13595
14551
|
else
|
|
13596
|
-
console.log(
|
|
14552
|
+
console.log(chalk6.gray(`Unpinned ${chalk6.bold(p.slug)}`));
|
|
13597
14553
|
} catch (e) {
|
|
13598
14554
|
handleError(program2, e);
|
|
13599
14555
|
}
|
|
@@ -13625,7 +14581,7 @@ program2.command("copy <id>").description("Copy prompt body to clipboard and inc
|
|
|
13625
14581
|
if (isJson(program2))
|
|
13626
14582
|
output(program2, { copied: true, id: prompt.id, slug: prompt.slug });
|
|
13627
14583
|
else
|
|
13628
|
-
console.log(
|
|
14584
|
+
console.log(chalk6.green(`Copied ${chalk6.bold(prompt.slug)} to clipboard`));
|
|
13629
14585
|
} catch (e) {
|
|
13630
14586
|
handleError(program2, e);
|
|
13631
14587
|
}
|
|
@@ -13637,9 +14593,9 @@ projectCmd.command("create <name>").description("Create a new project").option("
|
|
|
13637
14593
|
if (isJson(program2))
|
|
13638
14594
|
output(program2, project);
|
|
13639
14595
|
else {
|
|
13640
|
-
console.log(`${
|
|
14596
|
+
console.log(`${chalk6.green("Created")} project ${chalk6.bold(project.name)} \u2014 ${chalk6.gray(project.slug)}`);
|
|
13641
14597
|
if (project.description)
|
|
13642
|
-
console.log(
|
|
14598
|
+
console.log(chalk6.gray(` ${project.description}`));
|
|
13643
14599
|
}
|
|
13644
14600
|
} catch (e) {
|
|
13645
14601
|
handleError(program2, e);
|
|
@@ -13653,13 +14609,13 @@ projectCmd.command("list").description("List all projects").action(() => {
|
|
|
13653
14609
|
return;
|
|
13654
14610
|
}
|
|
13655
14611
|
if (projects.length === 0) {
|
|
13656
|
-
console.log(
|
|
14612
|
+
console.log(chalk6.gray("No projects."));
|
|
13657
14613
|
return;
|
|
13658
14614
|
}
|
|
13659
14615
|
for (const p of projects) {
|
|
13660
|
-
console.log(`${
|
|
14616
|
+
console.log(`${chalk6.bold(p.name)} ${chalk6.gray(p.slug)} ${chalk6.cyan(`${p.prompt_count} prompt(s)`)}`);
|
|
13661
14617
|
if (p.description)
|
|
13662
|
-
console.log(
|
|
14618
|
+
console.log(chalk6.gray(` ${p.description}`));
|
|
13663
14619
|
}
|
|
13664
14620
|
} catch (e) {
|
|
13665
14621
|
handleError(program2, e);
|
|
@@ -13670,7 +14626,7 @@ projectCmd.command("get <id>").description("Get project details").action((id) =>
|
|
|
13670
14626
|
const project = getProject(id);
|
|
13671
14627
|
if (!project)
|
|
13672
14628
|
handleError(program2, `Project not found: ${id}`);
|
|
13673
|
-
output(program2, isJson(program2) ? project : `${
|
|
14629
|
+
output(program2, isJson(program2) ? project : `${chalk6.bold(project.name)} ${chalk6.gray(project.slug)} ${chalk6.cyan(`${project.prompt_count} prompt(s)`)}`);
|
|
13674
14630
|
} catch (e) {
|
|
13675
14631
|
handleError(program2, e);
|
|
13676
14632
|
}
|
|
@@ -13686,15 +14642,15 @@ projectCmd.command("prompts <id>").description("List all prompts for a project (
|
|
|
13686
14642
|
return;
|
|
13687
14643
|
}
|
|
13688
14644
|
if (prompts.length === 0) {
|
|
13689
|
-
console.log(
|
|
14645
|
+
console.log(chalk6.gray("No prompts."));
|
|
13690
14646
|
return;
|
|
13691
14647
|
}
|
|
13692
|
-
console.log(
|
|
14648
|
+
console.log(chalk6.bold(`Prompts for project: ${project.name}`));
|
|
13693
14649
|
for (const p of prompts) {
|
|
13694
|
-
const scope = p.project_id ?
|
|
14650
|
+
const scope = p.project_id ? chalk6.cyan(" [project]") : chalk6.gray(" [global]");
|
|
13695
14651
|
console.log(fmtPrompt(p) + scope);
|
|
13696
14652
|
}
|
|
13697
|
-
console.log(
|
|
14653
|
+
console.log(chalk6.gray(`
|
|
13698
14654
|
${prompts.length} prompt(s)`));
|
|
13699
14655
|
} catch (e) {
|
|
13700
14656
|
handleError(program2, e);
|
|
@@ -13708,14 +14664,14 @@ projectCmd.command("delete <id>").description("Delete a project (prompts become
|
|
|
13708
14664
|
if (!opts.yes && !isJson(program2)) {
|
|
13709
14665
|
const { createInterface } = await import("readline");
|
|
13710
14666
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
13711
|
-
await new Promise((
|
|
13712
|
-
rl.question(
|
|
14667
|
+
await new Promise((resolve2) => {
|
|
14668
|
+
rl.question(chalk6.yellow(`Delete project "${project.name}"? Prompts will become global. [y/N] `), (ans) => {
|
|
13713
14669
|
rl.close();
|
|
13714
14670
|
if (ans.toLowerCase() !== "y") {
|
|
13715
14671
|
console.log("Cancelled.");
|
|
13716
14672
|
process.exit(0);
|
|
13717
14673
|
}
|
|
13718
|
-
|
|
14674
|
+
resolve2();
|
|
13719
14675
|
});
|
|
13720
14676
|
});
|
|
13721
14677
|
}
|
|
@@ -13723,7 +14679,7 @@ projectCmd.command("delete <id>").description("Delete a project (prompts become
|
|
|
13723
14679
|
if (isJson(program2))
|
|
13724
14680
|
output(program2, { deleted: true, id: project.id });
|
|
13725
14681
|
else
|
|
13726
|
-
console.log(
|
|
14682
|
+
console.log(chalk6.red(`Deleted project ${project.name}`));
|
|
13727
14683
|
} catch (e) {
|
|
13728
14684
|
handleError(program2, e);
|
|
13729
14685
|
}
|
|
@@ -13736,15 +14692,15 @@ program2.command("audit").description("Check for orphaned project refs, empty co
|
|
|
13736
14692
|
return;
|
|
13737
14693
|
}
|
|
13738
14694
|
if (report.issues.length === 0) {
|
|
13739
|
-
console.log(
|
|
14695
|
+
console.log(chalk6.green("\u2713 No audit issues found."));
|
|
13740
14696
|
return;
|
|
13741
14697
|
}
|
|
13742
14698
|
for (const issue of report.issues) {
|
|
13743
|
-
const sym = issue.severity === "error" ?
|
|
13744
|
-
const slug = issue.slug ?
|
|
14699
|
+
const sym = issue.severity === "error" ? chalk6.red("\u2717") : issue.severity === "warn" ? chalk6.yellow("\u26A0") : chalk6.gray("\u2139");
|
|
14700
|
+
const slug = issue.slug ? chalk6.green(` ${issue.slug}`) : "";
|
|
13745
14701
|
console.log(`${sym}${slug} ${issue.message}`);
|
|
13746
14702
|
}
|
|
13747
|
-
console.log(
|
|
14703
|
+
console.log(chalk6.bold(`
|
|
13748
14704
|
${report.issues.length} issue(s) \u2014 ${report.errors} errors, ${report.warnings} warnings, ${report.info} info`));
|
|
13749
14705
|
if (report.errors > 0)
|
|
13750
14706
|
process.exit(1);
|
|
@@ -13761,12 +14717,12 @@ program2.command("unused").description("List prompts that have never been used (
|
|
|
13761
14717
|
return;
|
|
13762
14718
|
}
|
|
13763
14719
|
if (unused.length === 0) {
|
|
13764
|
-
console.log(
|
|
14720
|
+
console.log(chalk6.green("All prompts have been used at least once."));
|
|
13765
14721
|
return;
|
|
13766
14722
|
}
|
|
13767
|
-
console.log(
|
|
14723
|
+
console.log(chalk6.bold(`Unused prompts (${unused.length}):`));
|
|
13768
14724
|
for (const p of unused) {
|
|
13769
|
-
console.log(` ${fmtPrompt(p)} ${
|
|
14725
|
+
console.log(` ${fmtPrompt(p)} ${chalk6.gray(`created ${new Date(p.created_at).toLocaleDateString()}`)}`);
|
|
13770
14726
|
}
|
|
13771
14727
|
} catch (e) {
|
|
13772
14728
|
handleError(program2, e);
|
|
@@ -13780,12 +14736,12 @@ program2.command("trending").description("Most used prompts in the last N days")
|
|
|
13780
14736
|
return;
|
|
13781
14737
|
}
|
|
13782
14738
|
if (results.length === 0) {
|
|
13783
|
-
console.log(
|
|
14739
|
+
console.log(chalk6.gray("No usage data yet."));
|
|
13784
14740
|
return;
|
|
13785
14741
|
}
|
|
13786
|
-
console.log(
|
|
14742
|
+
console.log(chalk6.bold(`Trending (last ${opts["days"] ?? "7"} days):`));
|
|
13787
14743
|
for (const r of results) {
|
|
13788
|
-
console.log(` ${
|
|
14744
|
+
console.log(` ${chalk6.green(r.slug)} ${chalk6.bold(String(r.uses))}\xD7 ${chalk6.gray(r.title)}`);
|
|
13789
14745
|
}
|
|
13790
14746
|
} catch (e) {
|
|
13791
14747
|
handleError(program2, e);
|
|
@@ -13798,7 +14754,7 @@ program2.command("expire <id> [date]").description("Set expiry date for a prompt
|
|
|
13798
14754
|
if (isJson(program2))
|
|
13799
14755
|
output(program2, p);
|
|
13800
14756
|
else
|
|
13801
|
-
console.log(expiresAt ?
|
|
14757
|
+
console.log(expiresAt ? chalk6.yellow(`Expires ${p.slug} on ${new Date(expiresAt).toLocaleDateString()}`) : chalk6.gray(`Cleared expiry for ${p.slug}`));
|
|
13802
14758
|
} catch (e) {
|
|
13803
14759
|
handleError(program2, e);
|
|
13804
14760
|
}
|
|
@@ -13821,7 +14777,7 @@ program2.command("duplicate <id>").description("Clone a prompt with a new slug")
|
|
|
13821
14777
|
if (isJson(program2))
|
|
13822
14778
|
output(program2, prompt);
|
|
13823
14779
|
else
|
|
13824
|
-
console.log(`${
|
|
14780
|
+
console.log(`${chalk6.green("Duplicated")} ${chalk6.bold(p.slug)} \u2192 ${chalk6.bold(prompt.slug)}`);
|
|
13825
14781
|
} catch (e) {
|
|
13826
14782
|
handleError(program2, e);
|
|
13827
14783
|
}
|
|
@@ -13834,7 +14790,7 @@ program2.command("chain <id> [next]").description("Set the next prompt in a chai
|
|
|
13834
14790
|
if (isJson(program2))
|
|
13835
14791
|
output(program2, p);
|
|
13836
14792
|
else
|
|
13837
|
-
console.log(nextSlug ? `${
|
|
14793
|
+
console.log(nextSlug ? `${chalk6.green(p.slug)} \u2192 ${chalk6.bold(nextSlug)}` : chalk6.gray(`Cleared chain for ${p.slug}`));
|
|
13838
14794
|
return;
|
|
13839
14795
|
}
|
|
13840
14796
|
const prompt = getPrompt(id);
|
|
@@ -13852,7 +14808,7 @@ program2.command("chain <id> [next]").description("Set the next prompt in a chai
|
|
|
13852
14808
|
output(program2, chain);
|
|
13853
14809
|
return;
|
|
13854
14810
|
}
|
|
13855
|
-
console.log(chain.map((c) =>
|
|
14811
|
+
console.log(chain.map((c) => chalk6.green(c.slug)).join(chalk6.gray(" \u2192 ")));
|
|
13856
14812
|
} catch (e) {
|
|
13857
14813
|
handleError(program2, e);
|
|
13858
14814
|
}
|
|
@@ -13868,26 +14824,64 @@ program2.command("completion [shell]").description("Output shell completion scri
|
|
|
13868
14824
|
}
|
|
13869
14825
|
});
|
|
13870
14826
|
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) => {
|
|
13871
|
-
const { existsSync: existsSync5, mkdirSync: mkdirSync4 } = await import("fs");
|
|
13872
|
-
const { resolve, join:
|
|
13873
|
-
const watchDir =
|
|
14827
|
+
const { existsSync: existsSync5, mkdirSync: mkdirSync4, readFileSync: readFileSync3, statSync: statSync2 } = await import("fs");
|
|
14828
|
+
const { resolve: resolve2, join: join8, basename } = await import("path");
|
|
14829
|
+
const watchDir = resolve2(dir ?? join8(process.cwd(), ".prompts"));
|
|
13874
14830
|
if (!existsSync5(watchDir))
|
|
13875
14831
|
mkdirSync4(watchDir, { recursive: true });
|
|
13876
|
-
console.log(
|
|
14832
|
+
console.log(chalk6.bold(`Watching ${watchDir} for .md changes\u2026`) + chalk6.gray(" (Ctrl+C to stop)"));
|
|
13877
14833
|
const { importFromMarkdown: importFromMarkdown2 } = await Promise.resolve().then(() => (init_importer(), exports_importer));
|
|
13878
|
-
const {
|
|
14834
|
+
const { getPrompt: getPrompt2 } = await Promise.resolve().then(() => (init_prompts(), exports_prompts));
|
|
14835
|
+
const { searchPrompts: searchPrompts2 } = await Promise.resolve().then(() => (init_search(), exports_search));
|
|
14836
|
+
const knownFiles = new Map;
|
|
14837
|
+
try {
|
|
14838
|
+
const { readdirSync: readdirSync4 } = await import("fs");
|
|
14839
|
+
for (const f of readdirSync4(watchDir)) {
|
|
14840
|
+
if (f.endsWith(".md"))
|
|
14841
|
+
knownFiles.set(f, f.replace(/\.md$/, "").toLowerCase().replace(/[^a-z0-9]+/g, "-"));
|
|
14842
|
+
}
|
|
14843
|
+
} catch {}
|
|
13879
14844
|
const fsWatch = (await import("fs")).watch;
|
|
13880
14845
|
fsWatch(watchDir, { persistent: true }, async (_event, filename) => {
|
|
13881
14846
|
if (!filename?.endsWith(".md"))
|
|
13882
14847
|
return;
|
|
13883
|
-
const filePath =
|
|
14848
|
+
const filePath = join8(watchDir, filename);
|
|
14849
|
+
const ts = chalk6.gray(new Date().toLocaleTimeString());
|
|
13884
14850
|
try {
|
|
13885
|
-
const
|
|
14851
|
+
const exists = existsSync5(filePath) && (() => {
|
|
14852
|
+
try {
|
|
14853
|
+
statSync2(filePath);
|
|
14854
|
+
return true;
|
|
14855
|
+
} catch {
|
|
14856
|
+
return false;
|
|
14857
|
+
}
|
|
14858
|
+
})();
|
|
14859
|
+
if (!exists) {
|
|
14860
|
+
const slug = knownFiles.get(filename);
|
|
14861
|
+
if (slug) {
|
|
14862
|
+
const prompt = getPrompt2(slug) ?? searchPrompts2(slug).find((r) => r.prompt.slug === slug)?.prompt;
|
|
14863
|
+
if (prompt) {
|
|
14864
|
+
const { updatePrompt: updatePrompt2 } = await Promise.resolve().then(() => (init_prompts(), exports_prompts));
|
|
14865
|
+
updatePrompt2(prompt.id, {
|
|
14866
|
+
description: `[watch: source file deleted at ${new Date().toISOString()}] ${prompt.description ?? ""}`.trim(),
|
|
14867
|
+
tags: [...new Set([...prompt.tags, "watch-deleted"])]
|
|
14868
|
+
});
|
|
14869
|
+
console.log(`${chalk6.red("Deleted")} ${chalk6.bold(filename.replace(".md", ""))} ${ts}`);
|
|
14870
|
+
}
|
|
14871
|
+
knownFiles.delete(filename);
|
|
14872
|
+
}
|
|
14873
|
+
return;
|
|
14874
|
+
}
|
|
14875
|
+
const content = readFileSync3(filePath, "utf-8");
|
|
13886
14876
|
const result = importFromMarkdown2([{ filename, content }], opts["agent"]);
|
|
13887
|
-
const action = result.created > 0 ?
|
|
13888
|
-
|
|
14877
|
+
const action = result.created > 0 ? chalk6.green("Created") : chalk6.yellow("Updated");
|
|
14878
|
+
if (result.created > 0 || result.updated > 0) {
|
|
14879
|
+
const slug = basename(filename, ".md").toLowerCase().replace(/[^a-z0-9]+/g, "-");
|
|
14880
|
+
knownFiles.set(filename, slug);
|
|
14881
|
+
}
|
|
14882
|
+
console.log(`${action}: ${chalk6.bold(filename.replace(".md", ""))} ${ts}`);
|
|
13889
14883
|
} catch {
|
|
13890
|
-
console.error(
|
|
14884
|
+
console.error(chalk6.red(`Failed to import: ${filename}`));
|
|
13891
14885
|
}
|
|
13892
14886
|
});
|
|
13893
14887
|
await new Promise(() => {});
|
|
@@ -13900,17 +14894,17 @@ program2.command("import-slash-commands").description("Auto-scan .claude/command
|
|
|
13900
14894
|
return;
|
|
13901
14895
|
}
|
|
13902
14896
|
if (scanned.length === 0) {
|
|
13903
|
-
console.log(
|
|
14897
|
+
console.log(chalk6.gray("No slash command files found."));
|
|
13904
14898
|
return;
|
|
13905
14899
|
}
|
|
13906
|
-
console.log(
|
|
14900
|
+
console.log(chalk6.bold(`Scanned ${scanned.length} file(s):`));
|
|
13907
14901
|
for (const s of scanned)
|
|
13908
|
-
console.log(
|
|
14902
|
+
console.log(chalk6.gray(` ${s.source}/${s.file}`));
|
|
13909
14903
|
console.log(`
|
|
13910
|
-
${
|
|
14904
|
+
${chalk6.green(`Created: ${imported.created}`)} ${chalk6.yellow(`Updated: ${imported.updated}`)}`);
|
|
13911
14905
|
if (imported.errors.length > 0) {
|
|
13912
14906
|
for (const e of imported.errors)
|
|
13913
|
-
console.error(
|
|
14907
|
+
console.error(chalk6.red(` \u2717 ${e.item}: ${e.error}`));
|
|
13914
14908
|
}
|
|
13915
14909
|
} catch (e) {
|
|
13916
14910
|
handleError(program2, e);
|
|
@@ -13924,14 +14918,14 @@ program2.command("remove <id>").alias("rm").alias("uninstall").description("Remo
|
|
|
13924
14918
|
if (!opts.yes && !isJson(program2)) {
|
|
13925
14919
|
const { createInterface } = await import("readline");
|
|
13926
14920
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
13927
|
-
await new Promise((
|
|
13928
|
-
rl.question(
|
|
14921
|
+
await new Promise((resolve2) => {
|
|
14922
|
+
rl.question(chalk6.yellow(`Remove "${prompt.slug}"? [y/N] `), (ans) => {
|
|
13929
14923
|
rl.close();
|
|
13930
14924
|
if (ans.toLowerCase() !== "y") {
|
|
13931
14925
|
console.log("Cancelled.");
|
|
13932
14926
|
process.exit(0);
|
|
13933
14927
|
}
|
|
13934
|
-
|
|
14928
|
+
resolve2();
|
|
13935
14929
|
});
|
|
13936
14930
|
});
|
|
13937
14931
|
}
|
|
@@ -13939,7 +14933,7 @@ program2.command("remove <id>").alias("rm").alias("uninstall").description("Remo
|
|
|
13939
14933
|
if (isJson(program2))
|
|
13940
14934
|
output(program2, { deleted: true, id: prompt.id });
|
|
13941
14935
|
else
|
|
13942
|
-
console.log(
|
|
14936
|
+
console.log(chalk6.red(`Removed ${prompt.slug}`));
|
|
13943
14937
|
} catch (e) {
|
|
13944
14938
|
handleError(program2, e);
|
|
13945
14939
|
}
|
|
@@ -13959,7 +14953,7 @@ scheduleCmd.command("add <id> <cron>").description("Schedule a prompt to run on
|
|
|
13959
14953
|
output(program2, schedule);
|
|
13960
14954
|
return;
|
|
13961
14955
|
}
|
|
13962
|
-
console.log(
|
|
14956
|
+
console.log(chalk6.green(`Scheduled "${prompt.slug}" [${schedule.id}]`));
|
|
13963
14957
|
console.log(` Cron: ${cron}`);
|
|
13964
14958
|
console.log(` Next run: ${schedule.next_run_at}`);
|
|
13965
14959
|
} catch (e) {
|
|
@@ -13974,11 +14968,11 @@ scheduleCmd.command("list [id]").description("List schedules, optionally filtere
|
|
|
13974
14968
|
return;
|
|
13975
14969
|
}
|
|
13976
14970
|
if (!schedules.length) {
|
|
13977
|
-
console.log(
|
|
14971
|
+
console.log(chalk6.gray("No schedules."));
|
|
13978
14972
|
return;
|
|
13979
14973
|
}
|
|
13980
14974
|
for (const s of schedules) {
|
|
13981
|
-
console.log(`${
|
|
14975
|
+
console.log(`${chalk6.bold(s.id)} ${chalk6.cyan(s.prompt_slug)} cron:${s.cron} next:${s.next_run_at} runs:${s.run_count}`);
|
|
13982
14976
|
}
|
|
13983
14977
|
} catch (e) {
|
|
13984
14978
|
handleError(program2, e);
|
|
@@ -13990,7 +14984,7 @@ scheduleCmd.command("remove <scheduleId>").alias("delete").description("Remove a
|
|
|
13990
14984
|
if (isJson(program2))
|
|
13991
14985
|
output(program2, { deleted: true, id: scheduleId });
|
|
13992
14986
|
else
|
|
13993
|
-
console.log(
|
|
14987
|
+
console.log(chalk6.red(`Removed schedule ${scheduleId}`));
|
|
13994
14988
|
} catch (e) {
|
|
13995
14989
|
handleError(program2, e);
|
|
13996
14990
|
}
|
|
@@ -13999,7 +14993,7 @@ scheduleCmd.command("due").description("Show and execute all due schedules").opt
|
|
|
13999
14993
|
try {
|
|
14000
14994
|
const due = getDueSchedules();
|
|
14001
14995
|
if (!due.length) {
|
|
14002
|
-
console.log(
|
|
14996
|
+
console.log(chalk6.gray("No prompts due."));
|
|
14003
14997
|
return;
|
|
14004
14998
|
}
|
|
14005
14999
|
if (isJson(program2)) {
|
|
@@ -14007,13 +15001,13 @@ scheduleCmd.command("due").description("Show and execute all due schedules").opt
|
|
|
14007
15001
|
return;
|
|
14008
15002
|
}
|
|
14009
15003
|
for (const d of due) {
|
|
14010
|
-
console.log(
|
|
15004
|
+
console.log(chalk6.bold(`
|
|
14011
15005
|
[${d.id}] ${d.prompt_slug}`));
|
|
14012
|
-
console.log(
|
|
14013
|
-
console.log(
|
|
15006
|
+
console.log(chalk6.gray(`Next run: ${d.next_run_at} | Runs: ${d.run_count}`));
|
|
15007
|
+
console.log(chalk6.white(d.rendered));
|
|
14014
15008
|
}
|
|
14015
15009
|
if (!opts.dryRun)
|
|
14016
|
-
console.log(
|
|
15010
|
+
console.log(chalk6.green(`
|
|
14017
15011
|
\u2713 Marked ${due.length} schedule(s) as ran.`));
|
|
14018
15012
|
} catch (e) {
|
|
14019
15013
|
handleError(program2, e);
|
|
@@ -14036,7 +15030,7 @@ scheduleCmd.command("next <cron>").description("Preview when a cron expression w
|
|
|
14036
15030
|
output(program2, { cron, next_runs: runs });
|
|
14037
15031
|
return;
|
|
14038
15032
|
}
|
|
14039
|
-
console.log(
|
|
15033
|
+
console.log(chalk6.bold(`Next ${count} runs for "${cron}":`));
|
|
14040
15034
|
for (const r of runs)
|
|
14041
15035
|
console.log(` ${r}`);
|
|
14042
15036
|
} catch (e) {
|