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 +31 -6
- package/marketplace.js +32 -10
- package/package.json +1 -1
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
|
-
//
|
|
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 (
|
|
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
|
|
466
|
+
let classifierRemoved = 0;
|
|
454
467
|
for (const fp of fileMap) {
|
|
455
468
|
if (!keptPaths.has(fp)) {
|
|
456
|
-
try { fs.unlinkSync(fp);
|
|
469
|
+
try { fs.unlinkSync(fp); classifierRemoved++; } catch {}
|
|
457
470
|
}
|
|
458
471
|
}
|
|
459
472
|
|
|
460
|
-
|
|
461
|
-
|
|
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
|
-
|
|
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
|
|