promptgraph-mcp 1.0.2 → 1.0.3
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/README.md +159 -159
- package/config.js +51 -51
- package/db.js +32 -32
- package/embedder.js +32 -32
- package/index.js +137 -137
- package/indexer.js +65 -65
- package/package.json +42 -42
- package/parser.js +47 -47
- package/pg-hook.js +46 -0
- package/search.js +58 -58
- package/watcher.js +50 -50
package/search.js
CHANGED
|
@@ -1,58 +1,58 @@
|
|
|
1
|
-
import { embed, cosineSimilarity } from './embedder.js';
|
|
2
|
-
import { getDb } from './db.js';
|
|
3
|
-
|
|
4
|
-
export async function search(query, topK = 5) {
|
|
5
|
-
const db = getDb();
|
|
6
|
-
const queryVec = await embed(query);
|
|
7
|
-
const skills = db.prepare('SELECT name, description, path, source, embedding FROM skills').all();
|
|
8
|
-
|
|
9
|
-
return skills
|
|
10
|
-
.map(skill => ({
|
|
11
|
-
name: skill.name,
|
|
12
|
-
description: skill.description,
|
|
13
|
-
path: skill.path,
|
|
14
|
-
source: skill.source,
|
|
15
|
-
score: cosineSimilarity(queryVec, JSON.parse(skill.embedding)),
|
|
16
|
-
}))
|
|
17
|
-
.sort((a, b) => b.score - a.score)
|
|
18
|
-
.slice(0, topK);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function getContext(name) {
|
|
22
|
-
const db = getDb();
|
|
23
|
-
const skill = db.prepare('SELECT * FROM skills WHERE name = ?').get(name);
|
|
24
|
-
if (!skill) return null;
|
|
25
|
-
const callees = db.prepare('SELECT to_skill FROM edges WHERE from_skill = ?').all(name).map(r => r.to_skill);
|
|
26
|
-
const callers = db.prepare('SELECT from_skill FROM edges WHERE to_skill = ?').all(name).map(r => r.from_skill);
|
|
27
|
-
return { ...skill, embedding: undefined, callees, callers };
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function getCallers(name) {
|
|
31
|
-
const db = getDb();
|
|
32
|
-
return db.prepare('SELECT from_skill FROM edges WHERE to_skill = ?').all(name).map(r => r.from_skill);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function getCallees(name) {
|
|
36
|
-
const db = getDb();
|
|
37
|
-
return db.prepare('SELECT to_skill FROM edges WHERE from_skill = ?').all(name).map(r => r.to_skill);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function getImpact(name) {
|
|
41
|
-
const db = getDb();
|
|
42
|
-
const visited = new Set();
|
|
43
|
-
const queue = [name];
|
|
44
|
-
while (queue.length) {
|
|
45
|
-
const cur = queue.shift();
|
|
46
|
-
if (visited.has(cur)) continue;
|
|
47
|
-
visited.add(cur);
|
|
48
|
-
const callers = db.prepare('SELECT from_skill FROM edges WHERE to_skill = ?').all(cur).map(r => r.from_skill);
|
|
49
|
-
queue.push(...callers);
|
|
50
|
-
}
|
|
51
|
-
visited.delete(name);
|
|
52
|
-
return [...visited];
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export function listAll() {
|
|
56
|
-
const db = getDb();
|
|
57
|
-
return db.prepare('SELECT name, description, source FROM skills ORDER BY name').all();
|
|
58
|
-
}
|
|
1
|
+
import { embed, cosineSimilarity } from './embedder.js';
|
|
2
|
+
import { getDb } from './db.js';
|
|
3
|
+
|
|
4
|
+
export async function search(query, topK = 5) {
|
|
5
|
+
const db = getDb();
|
|
6
|
+
const queryVec = await embed(query);
|
|
7
|
+
const skills = db.prepare('SELECT name, description, path, source, embedding FROM skills').all();
|
|
8
|
+
|
|
9
|
+
return skills
|
|
10
|
+
.map(skill => ({
|
|
11
|
+
name: skill.name,
|
|
12
|
+
description: skill.description,
|
|
13
|
+
path: skill.path,
|
|
14
|
+
source: skill.source,
|
|
15
|
+
score: cosineSimilarity(queryVec, JSON.parse(skill.embedding)),
|
|
16
|
+
}))
|
|
17
|
+
.sort((a, b) => b.score - a.score)
|
|
18
|
+
.slice(0, topK);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function getContext(name) {
|
|
22
|
+
const db = getDb();
|
|
23
|
+
const skill = db.prepare('SELECT * FROM skills WHERE name = ?').get(name);
|
|
24
|
+
if (!skill) return null;
|
|
25
|
+
const callees = db.prepare('SELECT to_skill FROM edges WHERE from_skill = ?').all(name).map(r => r.to_skill);
|
|
26
|
+
const callers = db.prepare('SELECT from_skill FROM edges WHERE to_skill = ?').all(name).map(r => r.from_skill);
|
|
27
|
+
return { ...skill, embedding: undefined, callees, callers };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function getCallers(name) {
|
|
31
|
+
const db = getDb();
|
|
32
|
+
return db.prepare('SELECT from_skill FROM edges WHERE to_skill = ?').all(name).map(r => r.from_skill);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function getCallees(name) {
|
|
36
|
+
const db = getDb();
|
|
37
|
+
return db.prepare('SELECT to_skill FROM edges WHERE from_skill = ?').all(name).map(r => r.to_skill);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function getImpact(name) {
|
|
41
|
+
const db = getDb();
|
|
42
|
+
const visited = new Set();
|
|
43
|
+
const queue = [name];
|
|
44
|
+
while (queue.length) {
|
|
45
|
+
const cur = queue.shift();
|
|
46
|
+
if (visited.has(cur)) continue;
|
|
47
|
+
visited.add(cur);
|
|
48
|
+
const callers = db.prepare('SELECT from_skill FROM edges WHERE to_skill = ?').all(cur).map(r => r.from_skill);
|
|
49
|
+
queue.push(...callers);
|
|
50
|
+
}
|
|
51
|
+
visited.delete(name);
|
|
52
|
+
return [...visited];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function listAll() {
|
|
56
|
+
const db = getDb();
|
|
57
|
+
return db.prepare('SELECT name, description, source FROM skills ORDER BY name').all();
|
|
58
|
+
}
|
package/watcher.js
CHANGED
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
import chokidar from 'chokidar';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import os from 'os';
|
|
4
|
-
import { indexFile } from './indexer.js';
|
|
5
|
-
import { getDb } from './db.js';
|
|
6
|
-
|
|
7
|
-
const SOURCES = [
|
|
8
|
-
{ dir: path.join(os.homedir(), '.claude', 'skills-store'), source: 'skills-store' },
|
|
9
|
-
{ dir: path.join(os.homedir(), '.claude', 'skills'), source: 'skills' },
|
|
10
|
-
];
|
|
11
|
-
|
|
12
|
-
export function startWatcher() {
|
|
13
|
-
const paths = SOURCES.map(s => s.dir);
|
|
14
|
-
|
|
15
|
-
const watcher = chokidar.watch(paths, {
|
|
16
|
-
ignored: /[/\\]\./,
|
|
17
|
-
persistent: true,
|
|
18
|
-
ignoreInitial: true,
|
|
19
|
-
awaitWriteFinish: { stabilityThreshold: 500 },
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
watcher.on('add', filePath => reindex(filePath));
|
|
23
|
-
watcher.on('change', filePath => reindex(filePath));
|
|
24
|
-
watcher.on('unlink', filePath => {
|
|
25
|
-
const name = path.basename(filePath, '.md');
|
|
26
|
-
const db = getDb();
|
|
27
|
-
db.prepare('DELETE FROM skills WHERE name = ?').run(name);
|
|
28
|
-
db.prepare('DELETE FROM edges WHERE from_skill = ? OR to_skill = ?').run(name, name);
|
|
29
|
-
console.error(`[PromptGraph] Removed: ${name}`);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
console.error('[PromptGraph] Watcher started');
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function getSource(filePath) {
|
|
36
|
-
for (const { dir, source } of SOURCES) {
|
|
37
|
-
if (filePath.startsWith(dir)) return source;
|
|
38
|
-
}
|
|
39
|
-
return 'unknown';
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async function reindex(filePath) {
|
|
43
|
-
if (!filePath.endsWith('.md')) return;
|
|
44
|
-
try {
|
|
45
|
-
await indexFile(filePath, getSource(filePath));
|
|
46
|
-
console.error(`[PromptGraph] Reindexed: ${path.basename(filePath)}`);
|
|
47
|
-
} catch (e) {
|
|
48
|
-
console.error(`[PromptGraph] Error reindexing ${filePath}: ${e.message}`);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
1
|
+
import chokidar from 'chokidar';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import { indexFile } from './indexer.js';
|
|
5
|
+
import { getDb } from './db.js';
|
|
6
|
+
|
|
7
|
+
const SOURCES = [
|
|
8
|
+
{ dir: path.join(os.homedir(), '.claude', 'skills-store'), source: 'skills-store' },
|
|
9
|
+
{ dir: path.join(os.homedir(), '.claude', 'skills'), source: 'skills' },
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
export function startWatcher() {
|
|
13
|
+
const paths = SOURCES.map(s => s.dir);
|
|
14
|
+
|
|
15
|
+
const watcher = chokidar.watch(paths, {
|
|
16
|
+
ignored: /[/\\]\./,
|
|
17
|
+
persistent: true,
|
|
18
|
+
ignoreInitial: true,
|
|
19
|
+
awaitWriteFinish: { stabilityThreshold: 500 },
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
watcher.on('add', filePath => reindex(filePath));
|
|
23
|
+
watcher.on('change', filePath => reindex(filePath));
|
|
24
|
+
watcher.on('unlink', filePath => {
|
|
25
|
+
const name = path.basename(filePath, '.md');
|
|
26
|
+
const db = getDb();
|
|
27
|
+
db.prepare('DELETE FROM skills WHERE name = ?').run(name);
|
|
28
|
+
db.prepare('DELETE FROM edges WHERE from_skill = ? OR to_skill = ?').run(name, name);
|
|
29
|
+
console.error(`[PromptGraph] Removed: ${name}`);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
console.error('[PromptGraph] Watcher started');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function getSource(filePath) {
|
|
36
|
+
for (const { dir, source } of SOURCES) {
|
|
37
|
+
if (filePath.startsWith(dir)) return source;
|
|
38
|
+
}
|
|
39
|
+
return 'unknown';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function reindex(filePath) {
|
|
43
|
+
if (!filePath.endsWith('.md')) return;
|
|
44
|
+
try {
|
|
45
|
+
await indexFile(filePath, getSource(filePath));
|
|
46
|
+
console.error(`[PromptGraph] Reindexed: ${path.basename(filePath)}`);
|
|
47
|
+
} catch (e) {
|
|
48
|
+
console.error(`[PromptGraph] Error reindexing ${filePath}: ${e.message}`);
|
|
49
|
+
}
|
|
50
|
+
}
|