@hasna/prompts 0.3.15 → 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/mcp/index.js CHANGED
@@ -15671,6 +15671,9 @@ function runAudit() {
15671
15671
  }
15672
15672
 
15673
15673
  // src/mcp/index.ts
15674
+ import { homedir as homedir6 } from "os";
15675
+ import { existsSync as fsExists, readFileSync as fsRead, writeFileSync as fsWrite, mkdirSync as fsMkdir, readdirSync as fsReaddir, statSync as fsStat } from "fs";
15676
+ import { join as pathJoin, resolve as pathResolve, dirname as pathDirname } from "path";
15674
15677
  var server = new McpServer({ name: "open-prompts", version: "0.1.0" });
15675
15678
  function ok(data) {
15676
15679
  return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
@@ -15963,8 +15966,8 @@ server.registerTool("prompts_export_as_skills", {
15963
15966
  try {
15964
15967
  const { mkdirSync: mkdirSync4, writeFileSync: writeFileSync2, existsSync: existsSync5 } = await import("fs");
15965
15968
  const { join: join7 } = await import("path");
15966
- const { homedir: homedir6 } = await import("os");
15967
- const skillsDir = target_dir ?? join7(homedir6(), ".claude", "skills");
15969
+ const { homedir: homedir7 } = await import("os");
15970
+ const skillsDir = target_dir ?? join7(homedir7(), ".claude", "skills");
15968
15971
  mkdirSync4(skillsDir, { recursive: true });
15969
15972
  const filter = collection ? { collection } : {};
15970
15973
  const allPrompts = listPrompts(filter);
@@ -16442,6 +16445,219 @@ server.registerTool("list_agents", {
16442
16445
  description: "List all registered agents.",
16443
16446
  inputSchema: {}
16444
16447
  }, async () => ok(listAgents()));
16448
+ server.registerTool("prompts_bulk_tag", {
16449
+ description: "Add and/or remove tags on multiple prompts in one call.",
16450
+ inputSchema: {
16451
+ ids: exports_external2.array(exports_external2.string()).describe("Prompt IDs or slugs"),
16452
+ add: exports_external2.array(exports_external2.string()).optional().describe("Tags to add"),
16453
+ remove: exports_external2.array(exports_external2.string()).optional().describe("Tags to remove")
16454
+ }
16455
+ }, async ({ ids, add = [], remove = [] }) => {
16456
+ const results = [];
16457
+ for (const idOrSlug of ids) {
16458
+ try {
16459
+ const prompt = getPrompt(idOrSlug);
16460
+ if (!prompt)
16461
+ continue;
16462
+ let tags = [...prompt.tags];
16463
+ for (const t of add) {
16464
+ if (!tags.includes(t))
16465
+ tags.push(t);
16466
+ }
16467
+ for (const t of remove) {
16468
+ tags = tags.filter((x) => x !== t);
16469
+ }
16470
+ updatePrompt(prompt.id, { tags });
16471
+ results.push({ id: prompt.id, slug: prompt.slug, tags });
16472
+ } catch {}
16473
+ }
16474
+ return ok({ updated: results.length, results });
16475
+ });
16476
+ server.registerTool("prompts_bulk_move", {
16477
+ description: "Move multiple prompts to a different collection.",
16478
+ inputSchema: {
16479
+ ids: exports_external2.array(exports_external2.string()).describe("Prompt IDs or slugs"),
16480
+ collection: exports_external2.string().describe("Target collection name")
16481
+ }
16482
+ }, async ({ ids, collection }) => {
16483
+ const results = [];
16484
+ for (const idOrSlug of ids) {
16485
+ try {
16486
+ movePrompt(idOrSlug, collection);
16487
+ const p = getPrompt(idOrSlug);
16488
+ results.push({ id: p?.id ?? idOrSlug, slug: p?.slug ?? idOrSlug, ok: true });
16489
+ } catch (e) {
16490
+ results.push({ id: idOrSlug, slug: idOrSlug, ok: false, error: e instanceof Error ? e.message : String(e) });
16491
+ }
16492
+ }
16493
+ return ok({ moved: results.filter((r) => r.ok).length, results });
16494
+ });
16495
+ var AGENT_CONFIGS_MCP = {
16496
+ claude: { global: ".claude/CLAUDE.md", local: "CLAUDE.md", label: "Claude Code" },
16497
+ agents: { global: ".agents/AGENTS.md", local: "AGENTS.md", label: "OpenAI Agents SDK" },
16498
+ gemini: { global: ".gemini/GEMINI.md", local: ".gemini/GEMINI.md", label: "Gemini CLI" },
16499
+ codex: { global: ".codex/CODEX.md", local: "CODEX.md", label: "OpenAI Codex CLI" },
16500
+ cursor: { global: ".cursor/rules", local: ".cursorrules", label: "Cursor" },
16501
+ aider: { global: ".aider/CONVENTIONS.md", local: ".aider.conventions.md", label: "Aider" }
16502
+ };
16503
+ function cfgPath(agent, global_) {
16504
+ const cfg = AGENT_CONFIGS_MCP[agent.toLowerCase()];
16505
+ if (!cfg)
16506
+ return null;
16507
+ return global_ ? pathJoin(homedir6(), cfg.global) : pathResolve(process.cwd(), cfg.local);
16508
+ }
16509
+ server.registerTool("prompts_config_list", {
16510
+ description: "List all known AI agent config files (CLAUDE.md, AGENTS.md, GEMINI.md, etc.) showing which exist globally and in the current project directory.",
16511
+ inputSchema: {}
16512
+ }, async () => {
16513
+ const rows = [];
16514
+ for (const [key, cfg] of Object.entries(AGENT_CONFIGS_MCP)) {
16515
+ const globalPath = pathJoin(homedir6(), cfg.global);
16516
+ const localPath = pathResolve(process.cwd(), cfg.local);
16517
+ rows.push({
16518
+ agent: key,
16519
+ label: cfg.label,
16520
+ global: { path: globalPath, exists: fsExists(globalPath), size: fsExists(globalPath) ? fsStat(globalPath).size : null },
16521
+ local: globalPath === localPath ? null : { path: localPath, exists: fsExists(localPath), size: fsExists(localPath) ? fsStat(localPath).size : null }
16522
+ });
16523
+ }
16524
+ return ok(rows);
16525
+ });
16526
+ server.registerTool("prompts_config_get", {
16527
+ description: "Read the contents of an AI agent config file (CLAUDE.md, AGENTS.md, etc.).",
16528
+ inputSchema: {
16529
+ agent: exports_external2.enum(["claude", "agents", "gemini", "codex", "cursor", "aider"]).describe("Which agent's config to read"),
16530
+ global: exports_external2.boolean().optional().default(false).describe("Read global (~/) config instead of project-local")
16531
+ }
16532
+ }, async ({ agent, global: g }) => {
16533
+ const path = cfgPath(agent, g ?? false);
16534
+ if (!path)
16535
+ return err(`Unknown agent: ${agent}`);
16536
+ if (!fsExists(path))
16537
+ return err(`Config file not found: ${path}`);
16538
+ return ok({ agent, path, content: fsRead(path, "utf-8") });
16539
+ });
16540
+ server.registerTool("prompts_config_set", {
16541
+ description: "Write content to an AI agent config file. Creates parent directories if needed.",
16542
+ inputSchema: {
16543
+ agent: exports_external2.enum(["claude", "agents", "gemini", "codex", "cursor", "aider"]).describe("Which agent's config to write"),
16544
+ content: exports_external2.string().describe("Full content to write"),
16545
+ global: exports_external2.boolean().optional().default(false).describe("Write to global (~/) config instead of project-local")
16546
+ }
16547
+ }, async ({ agent, content, global: g }) => {
16548
+ const path = cfgPath(agent, g ?? false);
16549
+ if (!path)
16550
+ return err(`Unknown agent: ${agent}`);
16551
+ fsMkdir(pathDirname(path), { recursive: true });
16552
+ fsWrite(path, content);
16553
+ return ok({ written: true, agent, path, bytes: content.length });
16554
+ });
16555
+ server.registerTool("prompts_config_inject", {
16556
+ description: "Append a saved prompt's body into an AI agent config file. Optionally inject under a specific markdown section heading.",
16557
+ inputSchema: {
16558
+ slug: exports_external2.string().describe("Prompt ID or slug to inject"),
16559
+ agent: exports_external2.enum(["claude", "agents", "gemini", "codex", "cursor", "aider"]).describe("Target agent config file"),
16560
+ global: exports_external2.boolean().optional().default(false).describe("Inject into global config instead of project-local"),
16561
+ section: exports_external2.string().optional().describe("Markdown heading to inject under (## Heading). Creates section if missing."),
16562
+ replace: exports_external2.boolean().optional().describe("Replace section content instead of appending (requires section)")
16563
+ }
16564
+ }, async ({ slug, agent, global: g, section, replace }) => {
16565
+ const prompt = getPrompt(slug);
16566
+ if (!prompt)
16567
+ return err(`Prompt not found: ${slug}`);
16568
+ const path = cfgPath(agent, g ?? false);
16569
+ if (!path)
16570
+ return err(`Unknown agent: ${agent}`);
16571
+ fsMkdir(pathDirname(path), { recursive: true });
16572
+ let existing = fsExists(path) ? fsRead(path, "utf-8") : "";
16573
+ const injection = `
16574
+ ${prompt.body}
16575
+ `;
16576
+ if (section) {
16577
+ const heading = `## ${section}`;
16578
+ const idx = existing.indexOf(heading);
16579
+ if (idx === -1) {
16580
+ existing = existing.trimEnd() + `
16581
+
16582
+ ${heading}
16583
+ ${injection}`;
16584
+ } else if (replace) {
16585
+ const afterHeading = idx + heading.length;
16586
+ const nextSection = existing.indexOf(`
16587
+ ## `, afterHeading);
16588
+ const sectionEnd = nextSection === -1 ? existing.length : nextSection;
16589
+ existing = existing.slice(0, afterHeading) + `
16590
+ ${injection}` + existing.slice(sectionEnd);
16591
+ } else {
16592
+ const afterHeading = idx + heading.length;
16593
+ const nextSection = existing.indexOf(`
16594
+ ## `, afterHeading);
16595
+ const insertAt = nextSection === -1 ? existing.length : nextSection;
16596
+ existing = existing.slice(0, insertAt).trimEnd() + `
16597
+ ${injection}
16598
+ ` + existing.slice(insertAt);
16599
+ }
16600
+ } else {
16601
+ existing = existing.trimEnd() + `
16602
+ ${injection}`;
16603
+ }
16604
+ fsWrite(path, existing);
16605
+ return ok({ injected: true, slug: prompt.slug, path, section: section ?? null });
16606
+ });
16607
+ server.registerTool("prompts_config_scan", {
16608
+ description: "Scan a workspace directory for git repos and report which AI agent config files are present or missing in each.",
16609
+ inputSchema: {
16610
+ workspace: exports_external2.string().optional().describe("Workspace directory to scan (default: ~/workspace)"),
16611
+ agents: exports_external2.array(exports_external2.string()).optional().describe("Agent names to check (default: all)"),
16612
+ depth: exports_external2.number().optional().default(3).describe("Max directory depth to scan for git repos"),
16613
+ missing_only: exports_external2.boolean().optional().describe("Only return repos with missing configs")
16614
+ }
16615
+ }, async ({ workspace, agents, depth = 3, missing_only }) => {
16616
+ const wsDir = workspace ? pathResolve(workspace) : pathResolve(homedir6(), "workspace");
16617
+ if (!fsExists(wsDir))
16618
+ return err(`Workspace not found: ${wsDir}`);
16619
+ const agentFilter = agents?.map((a) => a.toLowerCase()) ?? Object.keys(AGENT_CONFIGS_MCP);
16620
+ const repos = [];
16621
+ function scanDir(dir, d) {
16622
+ if (d > depth)
16623
+ return;
16624
+ try {
16625
+ const entries = fsReaddir(dir, { withFileTypes: true });
16626
+ if (entries.some((e) => e.name === ".git" && e.isDirectory())) {
16627
+ repos.push(dir);
16628
+ return;
16629
+ }
16630
+ for (const entry of entries) {
16631
+ if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
16632
+ scanDir(pathJoin(dir, entry.name), d + 1);
16633
+ }
16634
+ }
16635
+ } catch {}
16636
+ }
16637
+ scanDir(wsDir, 0);
16638
+ const reports = [];
16639
+ for (const repo of repos) {
16640
+ const configs = {};
16641
+ let missingCount = 0;
16642
+ let presentCount = 0;
16643
+ for (const key of agentFilter) {
16644
+ const cfg = AGENT_CONFIGS_MCP[key];
16645
+ if (!cfg)
16646
+ continue;
16647
+ const p = pathJoin(repo, cfg.local);
16648
+ const exists = fsExists(p);
16649
+ configs[key] = { present: exists, path: p, size: exists ? fsStat(p).size : null };
16650
+ if (exists)
16651
+ presentCount++;
16652
+ else
16653
+ missingCount++;
16654
+ }
16655
+ if (!missing_only || missingCount > 0) {
16656
+ reports.push({ repo, configs, missing_count: missingCount, present_count: presentCount });
16657
+ }
16658
+ }
16659
+ return ok({ workspace: wsDir, repos_scanned: repos.length, reports });
16660
+ });
16445
16661
  server.tool("send_feedback", "Send feedback about this service", { message: exports_external2.string(), email: exports_external2.string().optional(), category: exports_external2.enum(["bug", "feature", "general"]).optional() }, async (params) => {
16446
16662
  try {
16447
16663
  const db = getDatabase();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/prompts",
3
- "version": "0.3.15",
3
+ "version": "0.3.16",
4
4
  "description": "Reusable prompt library for AI agents — CLI + MCP server + REST API + web dashboard",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -80,4 +80,4 @@
80
80
  "@types/react": "^18.3.18",
81
81
  "typescript": "^5.7.3"
82
82
  }
83
- }
83
+ }