promptgraph-mcp 2.9.47 → 2.9.48
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/commands/bundle.js +24 -2
- package/github-import.js +42 -0
- package/package.json +1 -1
package/commands/bundle.js
CHANGED
|
@@ -107,14 +107,36 @@ export default async function handler(args, bin) {
|
|
|
107
107
|
);
|
|
108
108
|
process.exit(1);
|
|
109
109
|
}
|
|
110
|
-
const { validMdCount, hasScripts } = detected;
|
|
111
|
-
console.log(chalk.green(`found: ${detected.label}/`) + chalk.gray(` (
|
|
110
|
+
const { validMdCount, hasScripts: hasScriptsQuick } = detected;
|
|
111
|
+
console.log(chalk.green(`found: ${detected.label}/`) + chalk.gray(` (~${validMdCount} skills by name)`));
|
|
112
112
|
|
|
113
113
|
if (validMdCount === 0) {
|
|
114
114
|
error(`Cannot publish: ${repo}/${detected.label} has no skills that pass validation.\n All .md files were filtered (README/changelog/docs/etc).`);
|
|
115
115
|
process.exit(1);
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
// Deep validation: fetch each file via raw (free) + validateSkill on content
|
|
119
|
+
const { deepValidateRepo: _deepVal } = await import('../github-import.js');
|
|
120
|
+
process.stdout.write(chalk.gray(` Validating skill content (${validMdCount} files)... `));
|
|
121
|
+
let deepResult;
|
|
122
|
+
try {
|
|
123
|
+
deepResult = await _deepVal(repo, detected.subdir, (done, total) => {
|
|
124
|
+
process.stdout.write(`\r Validating skill content: ${done}/${total}... `);
|
|
125
|
+
});
|
|
126
|
+
} catch (e) {
|
|
127
|
+
process.stdout.write('\n');
|
|
128
|
+
error(`Validation failed: ${e.message}`); process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
process.stdout.write('\n');
|
|
131
|
+
|
|
132
|
+
if (deepResult.passed === 0) {
|
|
133
|
+
error(`Cannot publish: all ${deepResult.total} skill files failed content validation (too short, security patterns, etc).`);
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const hasScripts = hasScriptsQuick || deepResult.hasScripts;
|
|
138
|
+
console.log(chalk.green(` ✓ ${deepResult.passed}/${deepResult.total} skills valid${hasScripts ? ' 🔧 scripts detected' : ''}`));
|
|
139
|
+
|
|
118
140
|
const name = repo.split('/')[1].replace(/-/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
|
|
119
141
|
const id = repo.replace('/', '-').toLowerCase();
|
|
120
142
|
const bundle = {
|
package/github-import.js
CHANGED
|
@@ -265,6 +265,48 @@ async function detectSkillsDirFromAPI(ownerRepo) {
|
|
|
265
265
|
return null; // repo not found or inaccessible
|
|
266
266
|
}
|
|
267
267
|
|
|
268
|
+
// Deep-validate a repo bundle via 1 API call (tree) + raw file fetches (not rate-limited).
|
|
269
|
+
// Returns { passed, total, hasScripts } — passed = skills that survive validateSkill().
|
|
270
|
+
export async function deepValidateRepo(ownerRepo, subdir, onProgress) {
|
|
271
|
+
const treeJson = await httpsGet(`https://api.github.com/repos/${ownerRepo}/git/trees/HEAD?recursive=1`);
|
|
272
|
+
const tree = JSON.parse(treeJson);
|
|
273
|
+
|
|
274
|
+
const prefix = subdir ? `${subdir}/` : '';
|
|
275
|
+
const allBlobs = (tree.tree || []).filter(f => f.type === 'blob');
|
|
276
|
+
|
|
277
|
+
const mdFiles = allBlobs.filter(f =>
|
|
278
|
+
f.path.startsWith(prefix) &&
|
|
279
|
+
f.path.endsWith('.md') &&
|
|
280
|
+
!SKIP_RE.test(path.basename(f.path, '.md').toLowerCase())
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
const hasScripts = allBlobs.some(f =>
|
|
284
|
+
f.path.startsWith(prefix) && SCRIPT_EXTS.has(path.extname(f.path).toLowerCase())
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
const tmpDir = fs.mkdtempSync(path.join(PROMPTGRAPH_DIR, 'pg-val-'));
|
|
288
|
+
let passed = 0;
|
|
289
|
+
const total = mdFiles.length;
|
|
290
|
+
|
|
291
|
+
try {
|
|
292
|
+
for (let i = 0; i < mdFiles.length; i++) {
|
|
293
|
+
const f = mdFiles[i];
|
|
294
|
+
if (onProgress) onProgress(i + 1, total);
|
|
295
|
+
try {
|
|
296
|
+
const rawUrl = `https://raw.githubusercontent.com/${ownerRepo}/HEAD/${f.path}`;
|
|
297
|
+
const content = await streamDownload(rawUrl);
|
|
298
|
+
const tmpFile = path.join(tmpDir, `skill-${i}.md`);
|
|
299
|
+
fs.writeFileSync(tmpFile, content);
|
|
300
|
+
if (validateSkill(tmpFile).ok) passed++;
|
|
301
|
+
} catch {}
|
|
302
|
+
}
|
|
303
|
+
} finally {
|
|
304
|
+
try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch {}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return { passed, total, hasScripts };
|
|
308
|
+
}
|
|
309
|
+
|
|
268
310
|
const SKIP_DIRS_API = new Set([
|
|
269
311
|
'.github', 'docs', 'doc', 'documentation', 'examples', 'example',
|
|
270
312
|
'tests', 'test', 'assets', 'images', 'img', 'media', 'static',
|