promptgraph-mcp 1.5.24 → 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 +23 -1
- package/package.json +4 -1
- package/pg-hook.js +30 -13
- package/search.js +1 -1
- package/watcher.js +5 -2
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.
|
|
3
|
+
"version": "1.5.26",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -47,5 +47,8 @@
|
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"vitest": "^4.1.8"
|
|
50
|
+
},
|
|
51
|
+
"overrides": {
|
|
52
|
+
"tar": "^7.5.16"
|
|
50
53
|
}
|
|
51
54
|
}
|
package/pg-hook.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { embed
|
|
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
|
-
//
|
|
17
|
-
|
|
18
|
-
const
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
29
|
-
|
|
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
|
}
|