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.
@@ -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(` (${validMdCount} valid skills${hasScripts ? ', has scripts 🔧' : ''})`));
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',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "promptgraph-mcp",
3
- "version": "2.9.47",
3
+ "version": "2.9.48",
4
4
  "files": [
5
5
  "*.js",
6
6
  "commands/",