promptgraph-mcp 2.6.4 → 2.6.6

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
@@ -429,36 +429,50 @@ function detectSkillsDirLocal(repoRoot) {
429
429
  return { dir: repoRoot, label: '(root)', sparse: false };
430
430
  }
431
431
 
432
- // If the embedding classifier is trained, remove files classified as non-skills
432
+ // Remove files classified as non-skills by embedding classifier (only if model is trained)
433
433
  async function classifierCleanup(dest) {
434
434
  const mdFiles = globSync(`${dest}/**/*.md`);
435
435
  if (mdFiles.length === 0) return;
436
436
 
437
437
  const parsed = [];
438
438
  const fileMap = [];
439
+ let heuristicRemoved = 0;
440
+
439
441
  for (const fp of mdFiles) {
440
442
  try {
441
443
  const raw = fs.readFileSync(fp, 'utf8');
442
444
  if (!isSkillFile(fp, raw)) continue;
445
+ if (!seemsLikeSkill(raw)) {
446
+ try { fs.unlinkSync(fp); heuristicRemoved++; } catch {}
447
+ continue;
448
+ }
443
449
  parsed.push(parseSkillFile(fp, '', { raw }));
444
450
  fileMap.push(fp);
445
451
  } catch {}
446
452
  }
447
453
 
448
- if (parsed.length === 0) return;
454
+ if (heuristicRemoved > 0) {
455
+ console.log(`Removed ${heuristicRemoved} files by content heuristic`);
456
+ }
457
+
458
+ if (parsed.length === 0) {
459
+ if (heuristicRemoved > 0) removeEmptyDirs(dest);
460
+ return;
461
+ }
449
462
 
450
463
  const filtered = await filterWithClassifier(parsed);
451
464
  const keptPaths = new Set(filtered.map(s => s.path));
452
465
 
453
- let removed = 0;
466
+ let classifierRemoved = 0;
454
467
  for (const fp of fileMap) {
455
468
  if (!keptPaths.has(fp)) {
456
- try { fs.unlinkSync(fp); removed++; } catch {}
469
+ try { fs.unlinkSync(fp); classifierRemoved++; } catch {}
457
470
  }
458
471
  }
459
472
 
460
- if (removed > 0) {
461
- console.log(`Removed ${removed} files classified as non-skills`);
473
+ const totalRemoved = heuristicRemoved + classifierRemoved;
474
+ if (totalRemoved > 0) {
475
+ console.log(`Removed ${totalRemoved} non-skill files (heuristic: ${heuristicRemoved}, classifier: ${classifierRemoved})`);
462
476
  removeEmptyDirs(dest);
463
477
  }
464
478
  }
@@ -566,6 +580,17 @@ export async function importFromGitHub(repoUrl) {
566
580
 
567
581
  await classifierCleanup(dest);
568
582
 
583
+ // Update skill count cache with real survivor count
584
+ const realCount = globSync(`${dest}/**/*.md`).length;
585
+ const cacheKey = url.replace(/\.git$/, '');
586
+ const cachePath = path.join(PROMPTGRAPH_DIR, 'skill-counts.json');
587
+ try {
588
+ fs.mkdirSync(path.dirname(cachePath), { recursive: true });
589
+ const cache = JSON.parse(fs.readFileSync(cachePath, 'utf8') || '{}');
590
+ cache[cacheKey] = { count: realCount, ts: Date.now() };
591
+ fs.writeFileSync(cachePath, JSON.stringify(cache, null, 2));
592
+ } catch {}
593
+
569
594
  const { dir: localDir, label: localLabel } = detectSkillsDirLocal(dest);
570
595
  // Prefer the known skillsSubdir (from API detection or sparse patterns) as the
571
596
  // canonical skills directory — it's more accurate than local heuristics for
package/marketplace.js CHANGED
@@ -262,6 +262,15 @@ async function countRepoSkills(repoUrl) {
262
262
  } catch { return null; }
263
263
  }
264
264
 
265
+ // Count real .md files on disk for an installed bundle (always correct)
266
+ function localSkillCount(repoUrl) {
267
+ const repoName = repoUrl.replace(/^https?:\/\/github\.com\//, '').replace(/\.git$/, '').replace('/', '-');
268
+ const dest = path.join(SKILLS_STORE_DIR, 'github', repoName);
269
+ if (!fs.existsSync(dest)) return null;
270
+ const files = globSync(`${dest}/**/*.md`);
271
+ return files.length;
272
+ }
273
+
265
274
  export async function browseBundles(topK = 20) {
266
275
  try {
267
276
  const text = await fetchText(REGISTRY_URL);
@@ -273,20 +282,33 @@ export async function browseBundles(topK = 20) {
273
282
 
274
283
  await Promise.all(bundles.map(async b => {
275
284
  if (!b.repo_url) return;
285
+
286
+ // 1. If installed locally — count real files on disk (always correct)
287
+ const local = localSkillCount(b.repo_url);
288
+ if (local !== null) {
289
+ if (b.skillCount !== local) {
290
+ b.skillCount = local;
291
+ cache[b.repo_url] = { count: local, ts: now };
292
+ changed = true;
293
+ }
294
+ return;
295
+ }
296
+
297
+ // 2. Not installed — use cached API count if fresh
276
298
  const cached = cache[b.repo_url];
277
- // Use cached count if fresh, else fetch from API
278
299
  if (cached && (now - cached.ts) < SKILL_COUNT_TTL) {
279
300
  b.skillCount = cached.count;
301
+ return;
302
+ }
303
+
304
+ // 3. Fetch from GitHub API
305
+ const count = await countRepoSkills(b.repo_url);
306
+ if (count !== null) {
307
+ b.skillCount = count;
308
+ cache[b.repo_url] = { count, ts: now };
309
+ changed = true;
280
310
  } else {
281
- const count = await countRepoSkills(b.repo_url);
282
- if (count !== null) {
283
- b.skillCount = count;
284
- cache[b.repo_url] = { count, ts: now };
285
- changed = true;
286
- } else {
287
- // API failed — use stale cache if exists, else keep registry value as fallback
288
- b.skillCount = cached?.count ?? b.skillCount ?? 0;
289
- }
311
+ b.skillCount = cached?.count ?? b.skillCount ?? 0;
290
312
  }
291
313
  }));
292
314
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "promptgraph-mcp",
3
- "version": "2.6.4",
3
+ "version": "2.6.6",
4
4
  "files": [
5
5
  "*.js",
6
6
  "commands/",