promptgraph-mcp 1.5.25 → 1.5.26

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/index.js CHANGED
@@ -12,7 +12,7 @@ const args = process.argv.slice(2);
12
12
  const rawBin = process.argv[1]?.split(/[\\/]/).pop()?.replace(/\.js$/, '');
13
13
  const bin = (rawBin && rawBin !== 'index') ? rawBin : 'pg';
14
14
 
15
- const KNOWN_COMMANDS = new Set(['init', 'reindex', 'import', 'setup', 'validate', 'marketplace', 'doctor', 'help', '--help', '-h']);
15
+ const KNOWN_COMMANDS = new Set(['init', 'reindex', 'import', 'setup', 'validate', 'marketplace', 'doctor', 'search', 'help', '--help', '-h']);
16
16
 
17
17
  function showHelp() {
18
18
  console.log(
@@ -26,6 +26,7 @@ function showHelp() {
26
26
  const cmds = [
27
27
  ['init', 'First-time setup + index all skills'],
28
28
  ['reindex', 'Re-index all skills'],
29
+ ['search <query>', 'Search skills from the terminal'],
29
30
  ['import <owner/repo>', 'Import skills from GitHub'],
30
31
  ['marketplace [page]', 'Browse the community skill registry'],
31
32
  ['validate <file.md>', 'Validate a skill before publishing'],
@@ -213,6 +214,27 @@ if (args[0] === 'validate') {
213
214
  }
214
215
  }
215
216
 
217
+ if (args[0] === 'search') {
218
+ const query = args.slice(1).join(' ');
219
+ if (!query) { error('Usage: ' + bin + ' search <query>'); process.exit(1); }
220
+ const { search: searchSkills } = await import('./search.js');
221
+ const spin = (await import('./cli.js')).spinner('Searching...');
222
+ spin.start();
223
+ const results = await searchSkills(query, 10);
224
+ spin.stop();
225
+ if (!results.length) { info('No results for: ' + query); process.exit(0); }
226
+ const purple = chalk.hex('#7C3AED');
227
+ console.log();
228
+ results.forEach((s, i) => {
229
+ const score = chalk.dim((s.score * 100).toFixed(0) + '%');
230
+ console.log(' ' + chalk.dim(String(i + 1) + '.') + ' ' + chalk.bold.white(s.name) + ' ' + score);
231
+ console.log(' ' + chalk.dim(s.description || ''));
232
+ console.log(' ' + purple(s.source) + ' ' + chalk.dim(s.path));
233
+ console.log();
234
+ });
235
+ process.exit(0);
236
+ }
237
+
216
238
  if (args[0] === 'import') {
217
239
  const { importFromGitHub } = await import('./github-import.js');
218
240
  await importFromGitHub(args[1]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "promptgraph-mcp",
3
- "version": "1.5.25",
3
+ "version": "1.5.26",
4
4
  "main": "index.js",
5
5
  "type": "module",
6
6
  "bin": {
package/pg-hook.js CHANGED
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { embed, cosineSimilarity } from './embedder.js';
2
+ import { embed } from './embedder.js';
3
3
  import { getDb } from './db.js';
4
+ import { annSearch } from './ann.js';
5
+ import { cosineSimilarity } from './embedder.js';
4
6
 
5
7
  let input = '';
6
8
  process.stdin.on('data', d => input += d);
@@ -13,19 +15,34 @@ process.stdin.on('end', async () => {
13
15
  const queryVec = await embed(prompt);
14
16
  const db = getDb();
15
17
 
16
- // search over chunks, deduplicate by skill
17
- const chunks = db.prepare('SELECT skill_id, embedding FROM chunks').all();
18
- const bestBySkill = new Map();
19
- for (const chunk of chunks) {
20
- const score = cosineSimilarity(queryVec, JSON.parse(chunk.embedding));
21
- const prev = bestBySkill.get(chunk.skill_id);
22
- if (!prev || score > prev) bestBySkill.set(chunk.skill_id, score);
23
- }
18
+ // Use ANN index (fast). Fall back to brute-force only if ANN unavailable.
19
+ let topIds;
20
+ const annResults = await annSearch(queryVec, 15);
24
21
 
25
- const topIds = [...bestBySkill.entries()]
26
- .sort((a, b) => b[1] - a[1])
27
- .slice(0, 3)
28
- .filter(([, score]) => score > 0.55);
22
+ if (annResults && annResults.length > 0) {
23
+ const bestBySkill = new Map();
24
+ for (const r of annResults) {
25
+ const prev = bestBySkill.get(r.skill_id);
26
+ if (!prev || r.score > prev) bestBySkill.set(r.skill_id, r.score);
27
+ }
28
+ topIds = [...bestBySkill.entries()]
29
+ .sort((a, b) => b[1] - a[1])
30
+ .slice(0, 3)
31
+ .filter(([, score]) => score > 0.55);
32
+ } else {
33
+ // Fallback: brute-force (only before first reindex)
34
+ const chunks = db.prepare('SELECT skill_id, embedding FROM chunks').all();
35
+ const bestBySkill = new Map();
36
+ for (const chunk of chunks) {
37
+ const score = cosineSimilarity(queryVec, JSON.parse(chunk.embedding));
38
+ const prev = bestBySkill.get(chunk.skill_id);
39
+ if (!prev || score > prev) bestBySkill.set(chunk.skill_id, score);
40
+ }
41
+ topIds = [...bestBySkill.entries()]
42
+ .sort((a, b) => b[1] - a[1])
43
+ .slice(0, 3)
44
+ .filter(([, score]) => score > 0.55);
45
+ }
29
46
 
30
47
  if (topIds.length === 0) process.exit(0);
31
48
 
package/search.js CHANGED
@@ -108,7 +108,7 @@ export function getImpact(nameOrId) {
108
108
  const callers = db.prepare('SELECT from_skill FROM edges WHERE to_skill = ?').all(cur).map(r => r.from_skill);
109
109
  queue.push(...callers);
110
110
  }
111
- visited.delete(id);
111
+ visited.delete(res.id);
112
112
  return [...visited];
113
113
  }
114
114
 
package/watcher.js CHANGED
@@ -25,8 +25,11 @@ export function startWatcher() {
25
25
  }
26
26
 
27
27
  function getSource(filePath, config) {
28
- for (const { dir, source } of config.sources) {
29
- if (filePath.startsWith(dir)) return source;
28
+ const normFile = path.resolve(filePath);
29
+ // Sort longest-first so skills-store/marketplace wins over skills-store
30
+ const sorted = [...config.sources].sort((a, b) => b.dir.length - a.dir.length);
31
+ for (const { dir, source } of sorted) {
32
+ if (normFile.startsWith(path.resolve(dir))) return source;
30
33
  }
31
34
  return 'unknown';
32
35
  }