promptgraph-mcp 2.0.8 → 2.1.0
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/github-import.js +14 -10
- package/indexer.js +54 -2
- package/package.json +1 -1
package/github-import.js
CHANGED
|
@@ -4,7 +4,7 @@ import os from 'os';
|
|
|
4
4
|
import fs from 'fs';
|
|
5
5
|
import https from 'https';
|
|
6
6
|
import { globSync } from 'glob';
|
|
7
|
-
import { indexAll } from './indexer.js';
|
|
7
|
+
import { indexAll, indexSource } from './indexer.js';
|
|
8
8
|
import { loadConfig, saveConfig, SKILLS_STORE_DIR } from './config.js';
|
|
9
9
|
|
|
10
10
|
function repoExists(repoUrl) {
|
|
@@ -57,13 +57,17 @@ export async function importFromGitHub(repoUrl) {
|
|
|
57
57
|
|
|
58
58
|
if (fs.existsSync(dest)) {
|
|
59
59
|
console.log(`Updating ${repoName}...`);
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
60
|
+
// fetch + reset handles force-pushes and unrelated histories without re-cloning
|
|
61
|
+
const fetch = spawnSync('git', ['-C', dest, 'fetch', '--depth=1', 'origin'], { stdio: 'inherit' });
|
|
62
|
+
if (fetch.status !== 0) throw new Error(`git fetch failed for ${repoName}`);
|
|
63
|
+
const reset = spawnSync('git', ['-C', dest, 'reset', '--hard', 'origin/HEAD'], { stdio: 'pipe' });
|
|
64
|
+
if (reset.status !== 0) {
|
|
65
|
+
// fallback: try origin/main then origin/master
|
|
66
|
+
const main = spawnSync('git', ['-C', dest, 'reset', '--hard', 'origin/main'], { stdio: 'pipe' });
|
|
67
|
+
if (main.status !== 0) {
|
|
68
|
+
const master = spawnSync('git', ['-C', dest, 'reset', '--hard', 'origin/master'], { stdio: 'inherit' });
|
|
69
|
+
if (master.status !== 0) throw new Error(`git reset failed for ${repoName}`);
|
|
70
|
+
}
|
|
67
71
|
}
|
|
68
72
|
} else {
|
|
69
73
|
console.log(`Cloning ${url}...`);
|
|
@@ -95,7 +99,7 @@ export async function importFromGitHub(repoUrl) {
|
|
|
95
99
|
console.log(`Indexing from: ${skillsDir}`);
|
|
96
100
|
}
|
|
97
101
|
|
|
98
|
-
console.log(
|
|
99
|
-
await
|
|
102
|
+
console.log();
|
|
103
|
+
await indexSource(skillsDir, repoSource);
|
|
100
104
|
console.log(`Done! Imported from ${repoName}/${label}`);
|
|
101
105
|
}
|
package/indexer.js
CHANGED
|
@@ -200,6 +200,58 @@ export async function indexAll() {
|
|
|
200
200
|
|
|
201
201
|
export async function indexFile(filePath, source) {
|
|
202
202
|
const db = getDb();
|
|
203
|
-
const
|
|
204
|
-
|
|
203
|
+
const raw = fs.readFileSync(filePath, 'utf8');
|
|
204
|
+
const hash = createHash('md5').update(raw).digest('hex');
|
|
205
|
+
const skill = parseSkillFile(filePath, source, { raw });
|
|
206
|
+
await indexBatch(db, [{ ...skill, hash }]);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Index only one source directory — much faster than indexAll after a bundle install
|
|
210
|
+
export async function indexSource(dir, sourceName) {
|
|
211
|
+
const db = getDb();
|
|
212
|
+
const files = globSync(`${dir}/**/*.md`);
|
|
213
|
+
const total = files.length;
|
|
214
|
+
info(`Indexing ${chalk.white.bold(total)} files from ${sourceName}...`);
|
|
215
|
+
|
|
216
|
+
const dbByPath = new Map();
|
|
217
|
+
for (const row of db.prepare('SELECT id, path, hash FROM skills WHERE source = ?').all(sourceName)) {
|
|
218
|
+
dbByPath.set(row.path, row);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Remove skills from this source whose files are gone
|
|
222
|
+
const existingPaths = new Set(files.map(f => path.resolve(f)));
|
|
223
|
+
for (const [, row] of dbByPath) {
|
|
224
|
+
if (!existingPaths.has(path.resolve(row.path))) {
|
|
225
|
+
db.prepare('DELETE FROM skills WHERE id = ?').run(row.id);
|
|
226
|
+
db.prepare('DELETE FROM chunks WHERE skill_id = ?').run(row.id);
|
|
227
|
+
db.prepare('DELETE FROM edges WHERE from_skill = ? OR to_skill = ?').run(row.id, row.id);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
let count = 0, skipped = 0, errors = 0, batch = [];
|
|
232
|
+
const start = Date.now();
|
|
233
|
+
|
|
234
|
+
for (const file of files) {
|
|
235
|
+
try {
|
|
236
|
+
const raw = fs.readFileSync(file, 'utf8');
|
|
237
|
+
const hash = createHash('md5').update(raw).digest('hex');
|
|
238
|
+
const dbRow = dbByPath.get(file);
|
|
239
|
+
if (dbRow?.hash === hash) { skipped++; count++; continue; }
|
|
240
|
+
if (!isSkillFile(file, raw)) { skipped++; count++; continue; }
|
|
241
|
+
const parsed = parseSkillFile(file, sourceName, { raw });
|
|
242
|
+
batch.push({ ...parsed, hash });
|
|
243
|
+
if (batch.length >= BATCH_SIZE) {
|
|
244
|
+
await indexBatch(db, batch);
|
|
245
|
+
count += batch.length; batch = [];
|
|
246
|
+
progress(count, total, { skipped, errors });
|
|
247
|
+
}
|
|
248
|
+
} catch (e) { errors++; count++; }
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (batch.length > 0) { await indexBatch(db, batch); count += batch.length; }
|
|
252
|
+
progress(total, total, { skipped, errors });
|
|
253
|
+
progressDone();
|
|
254
|
+
await buildAnnIndex();
|
|
255
|
+
const elapsed = ((Date.now() - start) / 1000).toFixed(1);
|
|
256
|
+
success(`Indexed ${chalk.white.bold(count)} skills from ${sourceName} ${chalk.gray(`(${skipped} skipped, ${elapsed}s)`)}`);
|
|
205
257
|
}
|