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 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
- const pullResult = spawnSync('git', ['-C', dest, 'pull', '--depth=1'], { stdio: 'inherit' });
61
- if (pullResult.status !== 0) {
62
- // Force-push or unrelated histories re-clone from scratch
63
- console.log(`Pull failed (force-push?), re-cloning ${repoName}...`);
64
- fs.rmSync(dest, { recursive: true, force: true });
65
- const cloneResult = spawnSync('git', ['clone', '--depth=1', url, dest], { stdio: 'inherit' });
66
- if (cloneResult.status !== 0) throw new Error(`git clone failed for ${url}`);
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('\nReindexing...');
99
- await indexAll();
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 skill = parseSkillFile(filePath, source);
204
- await indexBatch(db, [{ ...skill, hash: fileHash(filePath) }]);
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "promptgraph-mcp",
3
- "version": "2.0.8",
3
+ "version": "2.1.0",
4
4
  "main": "index.js",
5
5
  "type": "module",
6
6
  "bin": {