promptgraph-mcp 2.0.0 → 2.0.2

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 (3) hide show
  1. package/index.js +1 -1
  2. package/package.json +1 -1
  3. package/parser.js +57 -4
package/index.js CHANGED
@@ -114,7 +114,7 @@ if (args[0] === 'marketplace' && (args[1] === 'bundles' || args[1] === 'bundle')
114
114
  bundles.forEach((b, i) => {
115
115
  const stars = b.stars > 0 ? chalk.yellow('★ ' + b.stars) : chalk.gray('★ 0');
116
116
  const countLabel = b.repo_url
117
- ? chalk.blue((b.skillCount ? b.skillCount + ' skills' : '?') + ' · GitHub')
117
+ ? chalk.blue((b.skillCount ? b.skillCount + ' skills · ' : '') + 'GitHub')
118
118
  : chalk.gray((b.skills?.length || 0) + ' skills');
119
119
  console.log(' ' + chalk.gray((i + 1) + '.') + ' ' + chalk.white.bold(b.id) + ' ' + stars + ' ' + countLabel);
120
120
  console.log(wrapB(b.description, 64, ' '));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "promptgraph-mcp",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "main": "index.js",
5
5
  "type": "module",
6
6
  "bin": {
package/parser.js CHANGED
@@ -5,12 +5,65 @@ import matter from 'gray-matter';
5
5
  // match /skill-name but not URLs (http://, https://, etc.)
6
6
  const SKILL_REF_RE = /(?<!https?:|ftp:)(?<![a-zA-Z0-9])\/([a-z0-9][a-z0-9-]{2,})/g;
7
7
 
8
- // files that are likely not skills
9
- const SKIP_FILENAMES = new Set(['readme', 'changelog', 'license', 'contributing', 'code-of-conduct', 'security', 'authors', 'credits']);
8
+ // Filenames that are never skills — docs, meta, legal files
9
+ const SKIP_FILENAMES = new Set([
10
+ 'readme', 'changelog', 'license', 'contributing', 'code-of-conduct',
11
+ 'security', 'authors', 'credits', 'install', 'installation', 'usage',
12
+ 'engagements', 'contributing', 'contributors', 'maintainers',
13
+ 'acknowledgements', 'faq', 'glossary', 'index', 'overview', 'summary',
14
+ 'roadmap', 'todo', 'notes', 'template', 'example', 'sample', 'demo',
15
+ 'getting-started', 'quickstart', 'guide', 'tutorial', 'walkthrough',
16
+ 'architecture', 'design', 'spec', 'specification', 'requirements',
17
+ 'privacy', 'terms', 'disclaimer', 'notice', 'copying', 'warranty',
18
+ 'codeofconduct', 'pull_request_template', 'issue_template', 'funding',
19
+ ]);
20
+
21
+ // Path segments that indicate the file is NOT a skill
22
+ const SKIP_DIRS = new Set([
23
+ '.github', 'docs', 'doc', 'documentation', 'examples', 'example',
24
+ 'tests', 'test', '__tests__', 'spec', 'fixtures', 'assets', 'images',
25
+ 'img', 'screenshots', 'media', 'static', 'public', 'dist', 'build',
26
+ 'node_modules', 'vendor', 'third_party',
27
+ ]);
10
28
 
11
29
  export function isSkillFile(filePath) {
12
- const base = filePath.split(/[\\/]/).pop().replace(/\.md$/i, '').toLowerCase();
13
- return !SKIP_FILENAMES.has(base);
30
+ const parts = filePath.replace(/\\/g, '/').split('/');
31
+ const base = parts[parts.length - 1].replace(/\.md$/i, '').toLowerCase();
32
+
33
+ // Skip by filename
34
+ if (SKIP_FILENAMES.has(base)) return false;
35
+
36
+ // Skip if any parent directory is in the skip list
37
+ for (const part of parts.slice(0, -1)) {
38
+ if (SKIP_DIRS.has(part.toLowerCase())) return false;
39
+ }
40
+
41
+ // Read and check content quality
42
+ try {
43
+ const raw = fs.readFileSync(filePath, 'utf8');
44
+
45
+ // Too short to be a real skill
46
+ if (raw.length < 150) return false;
47
+
48
+ // Has valid frontmatter with name → definitely a skill
49
+ try {
50
+ const { data } = matter(raw);
51
+ if (data.name && typeof data.name === 'string') return true;
52
+ } catch {}
53
+
54
+ // No frontmatter — check if it looks like instructions (not pure docs)
55
+ // Must have some imperative/instructional content, not just markdown prose
56
+ const lines = raw.split('\n').filter(l => l.trim());
57
+ const hasCodeBlock = raw.includes('```');
58
+ const hasBullets = lines.some(l => /^[-*]\s/.test(l));
59
+ const hasNumbered = lines.some(l => /^\d+\.\s/.test(l));
60
+ const hasHeaders = lines.filter(l => /^#{1,3}\s/.test(l)).length >= 2;
61
+
62
+ // Must look structured, not just a prose document
63
+ return (hasCodeBlock || hasBullets || hasNumbered) && hasHeaders;
64
+ } catch {
65
+ return false;
66
+ }
14
67
  }
15
68
 
16
69
  export function parseSkillFile(filePath, source, opts = {}) {