promptgraph-mcp 2.4.2 → 2.4.3

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.
Files changed (2) hide show
  1. package/github-import.js +66 -43
  2. package/package.json +1 -1
package/github-import.js CHANGED
@@ -34,53 +34,80 @@ function repoExists(ownerRepo) {
34
34
  });
35
35
  }
36
36
 
37
- // Validate all .md files in a repo's skills subdir against validateSkill().
38
- // Returns { ok, errors[], warnings[] }.
39
- export async function validateRepoSkills(ownerRepo) {
37
+ // Download one .md file, run validateSkill on it, return errors/warnings.
38
+ async function validateMdFile(file, tmpDir) {
40
39
  const errors = [];
41
40
  const warnings = [];
42
-
43
- const detected = await detectSkillsDirFromAPI(ownerRepo);
44
- if (!detected) {
45
- return { ok: false, errors: [`No skills directory found in ${ownerRepo}`], warnings: [] };
46
- }
47
-
48
- const subdir = detected.subdir;
49
- const listUrl = `https://api.github.com/repos/${ownerRepo}/contents/${subdir}`;
50
- let entries;
51
41
  try {
52
- const json = await httpsGet(listUrl);
53
- entries = JSON.parse(json);
42
+ const content = await httpsGet(file.download_url);
43
+ const tmpPath = path.join(tmpDir, file.name);
44
+ fs.writeFileSync(tmpPath, content);
45
+ const result = validateSkill(tmpPath);
46
+ if (!result.ok) {
47
+ errors.push(`${file.name}: ${result.errors.join('; ')}`);
48
+ }
49
+ if (result.warnings?.length) {
50
+ warnings.push(...result.warnings.map(w => `${file.name}: ${w}`));
51
+ }
52
+ fs.unlinkSync(tmpPath);
54
53
  } catch (e) {
55
- return { ok: false, errors: [`Failed to list ${subdir}/ contents: ${e.message}`], warnings: [] };
56
- }
57
-
58
- const mdFiles = entries.filter(e => e.type === 'file' && e.name.endsWith('.md'));
59
- if (mdFiles.length === 0) {
60
- return { ok: false, errors: [`No .md files found in ${subdir}/`], warnings: [] };
54
+ errors.push(`${file.name}: failed to validate ${e.message}`);
61
55
  }
56
+ return { errors, warnings };
57
+ }
62
58
 
59
+ // Validate all .md files in a repo's skills subdir against validateSkill().
60
+ // Falls back to root-level .md files if no skills subdirectory is found.
61
+ // Returns { ok, errors[], warnings[] }.
62
+ export async function validateRepoSkills(ownerRepo) {
63
+ const detected = await detectSkillsDirFromAPI(ownerRepo);
63
64
  const tmpDir = path.join(PROMPTGRAPH_DIR, '.validate-tmp');
64
65
  fs.mkdirSync(tmpDir, { recursive: true });
65
66
 
66
- for (const file of mdFiles) {
67
+ let mdFiles;
68
+ if (detected) {
69
+ // Has a skills subdirectory — list contents
70
+ const subdir = detected.subdir;
71
+ let entries;
67
72
  try {
68
- const content = await httpsGet(file.download_url);
69
- const tmpPath = path.join(tmpDir, file.name);
70
- fs.writeFileSync(tmpPath, content);
71
- const result = validateSkill(tmpPath);
72
- if (!result.ok) {
73
- errors.push(`${file.name}: ${result.errors.join('; ')}`);
74
- }
75
- if (result.warnings?.length) {
76
- warnings.push(...result.warnings.map(w => `${file.name}: ${w}`));
77
- }
78
- fs.unlinkSync(tmpPath);
73
+ const json = await httpsGet(`https://api.github.com/repos/${ownerRepo}/contents/${subdir}`);
74
+ entries = JSON.parse(json);
75
+ } catch (e) {
76
+ try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch {}
77
+ return { ok: false, errors: [`Failed to list ${subdir}/ contents: ${e.message}`], warnings: [] };
78
+ }
79
+ mdFiles = entries.filter(e => e.type === 'file' && e.name.endsWith('.md'));
80
+ } else {
81
+ // No skills subdir — fall back to root-level .md files
82
+ try {
83
+ const json = await httpsGet(`https://api.github.com/repos/${ownerRepo}/contents`);
84
+ const entries = JSON.parse(json);
85
+ mdFiles = entries.filter(e => e.type === 'file' && e.name.endsWith('.md'));
79
86
  } catch (e) {
80
- errors.push(`${file.name}: failed to validate ${e.message}`);
87
+ try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch {}
88
+ return { ok: false, errors: [`Failed to list repo root: ${e.message}`], warnings: [] };
81
89
  }
82
90
  }
83
91
 
92
+ if (!mdFiles || mdFiles.length === 0) {
93
+ try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch {}
94
+ return { ok: false, errors: [`No .md files found in ${ownerRepo}`], warnings: [] };
95
+ }
96
+
97
+ // Filter out docs-like filenames (README, LICENSE, CHANGELOG, etc.)
98
+ const SKIP_DOCS = /^(readme|license|changelog|contributing|code.?of.?conduct|security|authors|credits|install|faq|index|overview|summary|todo|notes|template|copying|warranty|funding|roadmap)/i;
99
+ const mdTrimmed = mdFiles.filter(f => !SKIP_DOCS.test(f.name.replace(/\.md$/i, '')));
100
+ const mdToValidate = mdTrimmed.length > 0 ? mdTrimmed : mdFiles;
101
+
102
+ let errors = [];
103
+ let warnings = [];
104
+
105
+ for (const file of mdToValidate) {
106
+ const r = await validateMdFile(file, tmpDir);
107
+ errors.push(...r.errors);
108
+ warnings.push(...r.warnings);
109
+ }
110
+
84
111
  try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch {}
85
112
 
86
113
  return { ok: errors.length === 0, errors, warnings };
@@ -266,17 +293,13 @@ export async function importFromGitHub(repoUrl) {
266
293
  skillsSubdir = detected?.subdir || null;
267
294
 
268
295
  if (!skillsSubdir) {
269
- throw new Error(
270
- `No skill subdirectory found in ${ownerRepo}.\n` +
271
- `Expected one of: ${SKILL_DIRS.join(', ')}\n` +
272
- `Or any subfolder with .md files.\n` +
273
- `Visit https://github.com/${ownerRepo} to check the repo structure.`
274
- );
296
+ console.log(`found: (root) — no skills subdirectory, using full clone`);
297
+ cloneOk = fullClone(url, dest);
298
+ } else {
299
+ console.log(`found: ${detected.label}/`);
300
+ console.log(`Sparse-cloning ${url} (${skillsSubdir}/ only)...`);
301
+ cloneOk = sparseClone(url, dest, skillsSubdir);
275
302
  }
276
-
277
- console.log(`found: ${detected.label}/`);
278
- console.log(`Sparse-cloning ${url} (${skillsSubdir}/ only)...`);
279
- cloneOk = sparseClone(url, dest, skillsSubdir);
280
303
  if (!cloneOk) {
281
304
  fs.rmSync(dest, { recursive: true, force: true });
282
305
  throw new Error(`Sparse-checkout failed for ${url}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "promptgraph-mcp",
3
- "version": "2.4.2",
3
+ "version": "2.4.3",
4
4
  "main": "index.js",
5
5
  "type": "module",
6
6
  "bin": {