promptgraph-mcp 2.6.8 → 2.7.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 +5 -15
- package/marketplace.js +28 -2
- package/package.json +1 -1
- package/validator.js +5 -5
package/github-import.js
CHANGED
|
@@ -436,43 +436,33 @@ async function classifierCleanup(dest) {
|
|
|
436
436
|
|
|
437
437
|
const parsed = [];
|
|
438
438
|
const fileMap = [];
|
|
439
|
-
let heuristicRemoved = 0;
|
|
440
439
|
|
|
441
440
|
for (const fp of mdFiles) {
|
|
442
441
|
try {
|
|
443
442
|
const raw = fs.readFileSync(fp, 'utf8');
|
|
444
443
|
if (!isSkillFile(fp, raw)) continue;
|
|
445
|
-
if (!seemsLikeSkill(raw)) {
|
|
446
|
-
try { fs.unlinkSync(fp); heuristicRemoved++; } catch {}
|
|
447
|
-
continue;
|
|
448
|
-
}
|
|
449
444
|
parsed.push(parseSkillFile(fp, '', { raw }));
|
|
450
445
|
fileMap.push(fp);
|
|
451
446
|
} catch {}
|
|
452
447
|
}
|
|
453
448
|
|
|
454
|
-
if (heuristicRemoved > 0) {
|
|
455
|
-
console.log(`Removed ${heuristicRemoved} files by content heuristic`);
|
|
456
|
-
}
|
|
457
|
-
|
|
458
449
|
if (parsed.length === 0) {
|
|
459
|
-
|
|
450
|
+
removeEmptyDirs(dest);
|
|
460
451
|
return;
|
|
461
452
|
}
|
|
462
453
|
|
|
463
454
|
const filtered = await filterWithClassifier(parsed);
|
|
464
455
|
const keptPaths = new Set(filtered.map(s => s.path));
|
|
465
456
|
|
|
466
|
-
let
|
|
457
|
+
let removed = 0;
|
|
467
458
|
for (const fp of fileMap) {
|
|
468
459
|
if (!keptPaths.has(fp)) {
|
|
469
|
-
try { fs.unlinkSync(fp);
|
|
460
|
+
try { fs.unlinkSync(fp); removed++; } catch {}
|
|
470
461
|
}
|
|
471
462
|
}
|
|
472
463
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
console.log(`Removed ${totalRemoved} non-skill files (heuristic: ${heuristicRemoved}, classifier: ${classifierRemoved})`);
|
|
464
|
+
if (removed > 0) {
|
|
465
|
+
console.log(`Removed ${removed} non-skill files (classifier)`);
|
|
476
466
|
removeEmptyDirs(dest);
|
|
477
467
|
}
|
|
478
468
|
}
|
package/marketplace.js
CHANGED
|
@@ -13,6 +13,7 @@ import { isSkillFile } from './parser.js';
|
|
|
13
13
|
|
|
14
14
|
const REGISTRY_URL = 'https://raw.githubusercontent.com/NeiP4n/promptgraph-registry/main/registry.json';
|
|
15
15
|
const SKILL_COUNT_CACHE = path.join(PROMPTGRAPH_DIR, 'skill-counts.json');
|
|
16
|
+
const DEAD_REPOS_FILE = path.join(PROMPTGRAPH_DIR, 'dead-repos.json');
|
|
16
17
|
const SKILLS_DIR = path.join(SKILLS_STORE_DIR, 'marketplace');
|
|
17
18
|
|
|
18
19
|
// Atomically write content to dest via tmp — cleans up on failure
|
|
@@ -143,6 +144,21 @@ function writeSkillCountCache(data) {
|
|
|
143
144
|
} catch {}
|
|
144
145
|
}
|
|
145
146
|
|
|
147
|
+
function readDeadRepos() {
|
|
148
|
+
try { return JSON.parse(fs.readFileSync(DEAD_REPOS_FILE, 'utf8')); } catch { return []; }
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function markDeadRepo(repoUrl) {
|
|
152
|
+
try {
|
|
153
|
+
const dead = readDeadRepos();
|
|
154
|
+
if (!dead.includes(repoUrl)) {
|
|
155
|
+
dead.push(repoUrl);
|
|
156
|
+
fs.mkdirSync(path.dirname(DEAD_REPOS_FILE), { recursive: true });
|
|
157
|
+
fs.writeFileSync(DEAD_REPOS_FILE, JSON.stringify(dead, null, 2));
|
|
158
|
+
}
|
|
159
|
+
} catch {}
|
|
160
|
+
}
|
|
161
|
+
|
|
146
162
|
async function fetchText(url) {
|
|
147
163
|
const cacheFile = path.join(PROMPTGRAPH_DIR, 'registry-cache.json');
|
|
148
164
|
const isRegistry = url === REGISTRY_URL;
|
|
@@ -275,7 +291,10 @@ export async function browseBundles(topK = 20) {
|
|
|
275
291
|
try {
|
|
276
292
|
const text = await fetchText(REGISTRY_URL);
|
|
277
293
|
const registry = JSON.parse(text);
|
|
278
|
-
const
|
|
294
|
+
const deadRepos = new Set(readDeadRepos());
|
|
295
|
+
const bundles = (registry.bundles || []).filter(b =>
|
|
296
|
+
validateRegistryEntry(b).ok && !deadRepos.has(b.repo_url)
|
|
297
|
+
);
|
|
279
298
|
const cache = readSkillCountCache();
|
|
280
299
|
const now = Date.now();
|
|
281
300
|
let changed = false;
|
|
@@ -346,7 +365,14 @@ export async function installBundle(bundleId) {
|
|
|
346
365
|
if (!bundle) return { error: `No bundle matching "${bundleId}"` };
|
|
347
366
|
|
|
348
367
|
if (bundle.repo_url) {
|
|
349
|
-
|
|
368
|
+
try {
|
|
369
|
+
await importFromGitHub(bundle.repo_url);
|
|
370
|
+
} catch (e) {
|
|
371
|
+
if (e.message && e.message.includes('No valid skills')) {
|
|
372
|
+
markDeadRepo(bundle.repo_url);
|
|
373
|
+
}
|
|
374
|
+
throw e;
|
|
375
|
+
}
|
|
350
376
|
return { success: true, bundle: bundle.name, type: 'repo_import', repo_url: bundle.repo_url };
|
|
351
377
|
}
|
|
352
378
|
|
package/package.json
CHANGED
package/validator.js
CHANGED
|
@@ -50,22 +50,22 @@ export function validateSkill(filePath) {
|
|
|
50
50
|
return { ok: false, errors, warnings };
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
// name
|
|
53
|
+
// name — derive from filename if missing (handles plain .md repos)
|
|
54
54
|
if (!data.name) {
|
|
55
|
-
|
|
55
|
+
warnings.push('Missing frontmatter "name" — derived from filename');
|
|
56
56
|
} else if (typeof data.name !== 'string') {
|
|
57
57
|
errors.push('Field "name" must be a string');
|
|
58
58
|
} else if (!NAME_RE.test(data.name)) {
|
|
59
59
|
errors.push(`Invalid name "${data.name}". Use lowercase, digits, hyphens (2-64 chars).`);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
// description
|
|
62
|
+
// description — derive from first paragraph if missing
|
|
63
63
|
if (!data.description) {
|
|
64
|
-
|
|
64
|
+
warnings.push('Missing frontmatter "description" — derived from content');
|
|
65
65
|
} else if (typeof data.description !== 'string') {
|
|
66
66
|
errors.push('Field "description" must be a string');
|
|
67
67
|
} else if (data.description.trim().length < MIN_DESCRIPTION_LENGTH) {
|
|
68
|
-
|
|
68
|
+
warnings.push(`Description very short (${data.description.trim().length} chars).`);
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
// body must have real instruction content
|