promptgraph-mcp 2.2.0 → 2.2.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 +47 -19
  2. package/package.json +1 -1
  3. package/tui.js +2 -6
package/index.js CHANGED
@@ -196,31 +196,31 @@ if (args[0] === 'marketplace') {
196
196
  const installedSet = new Set();
197
197
  try {
198
198
  const cfg = _lcMkt();
199
- for (const s of cfg.sources) {
200
- if (s.source.startsWith('github:')) installedSet.add(s.source.replace('github:', ''));
201
- if (s.source === 'marketplace') installedSet.add('marketplace');
202
- }
203
199
  const db = _getDbMkt();
204
- for (const row of db.prepare('SELECT id FROM skills').all()) installedSet.add(row.id);
205
- // Also add bundle IDs by matching repo names
200
+
201
+ // Collect installed skill IDs from DB
202
+ const dbSkillIds = new Set(db.prepare('SELECT id FROM skills').all().map(r => r.id));
203
+
204
+ // Build set of cloned repo names from config sources (exact: github:owner-repo)
205
+ const githubSources = new Set(
206
+ cfg.sources.filter(s => s.source.startsWith('github:')).map(s => s.source.replace('github:', '').toLowerCase())
207
+ );
208
+
206
209
  for (const b of (Array.isArray(bundles) ? bundles : [])) {
207
210
  if (b.repo_url) {
208
- const repoName = b.repo_url.split('/').join('-');
209
- if ([...installedSet].some(s => s.toLowerCase().includes(b.id.split('-').pop()))) {
210
- installedSet.add(b.id);
211
- }
212
- }
213
- }
214
- // match github sources to bundle ids
215
- for (const s of cfg.sources) {
216
- if (!s.source.startsWith('github:')) continue;
217
- const srcName = s.source.replace('github:', '').toLowerCase();
218
- for (const b of (Array.isArray(bundles) ? bundles : [])) {
219
- if (srcName.toLowerCase().includes(b.id.toLowerCase()) || b.id.toLowerCase().includes(srcName.split('-')[0])) {
211
+ // repo_url = "owner/repo" → cloned as "owner-repo" in github: source
212
+ const clonedName = b.repo_url.replace('/', '-').toLowerCase();
213
+ if (githubSources.has(clonedName)) installedSet.add(b.id);
214
+ } else if (Array.isArray(b.skills)) {
215
+ // skill-list bundle: installed if ALL skills are in DB
216
+ if (b.skills.length > 0 && b.skills.every(sid => dbSkillIds.has(sid))) {
220
217
  installedSet.add(b.id);
221
218
  }
222
219
  }
223
220
  }
221
+
222
+ // Individual skills
223
+ for (const id of dbSkillIds) installedSet.add(id);
224
224
  } catch {}
225
225
 
226
226
  const { runTUI } = await import('./tui.js');
@@ -246,12 +246,40 @@ if (args[0] === 'marketplace') {
246
246
 
247
247
  if (args[0] === 'validate') {
248
248
  const { validateSkill } = await import('./validator.js');
249
+ const { isSkillFile } = await import('./parser.js');
249
250
  const file = args[1];
250
251
  if (!file) { error('Usage: ' + bin + ' validate <skill.md>'); process.exit(1); }
252
+
253
+ const raw = fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : null;
254
+
255
+ // Show indexing score breakdown
256
+ if (raw) {
257
+ const { skillScore: _score } = await import('./parser.js').catch(() => ({}));
258
+ const willIndex = isSkillFile(file, raw);
259
+ const scoreLabel = willIndex ? chalk.green('✓ will be indexed') : chalk.red('✗ will be skipped by indexer');
260
+ console.log(chalk.bold('\n Indexing check: ') + scoreLabel);
261
+
262
+ // Show which signals were detected
263
+ const lines = raw.split('\n').filter(l => l.trim());
264
+ const signals = [];
265
+ try { const { data } = (await import('gray-matter')).default(raw); if (data.name) signals.push(chalk.green('+4 frontmatter name:')); } catch {}
266
+ if (/^#{1,3}\s+(steps?|usage|instructions?|how\s+to|when\s+to\s+use|workflow)/im.test(raw)) signals.push(chalk.green('+2 instructional headers (## Steps / ## Usage)'));
267
+ if (lines.filter(l => /^#{1,3}\s/.test(l)).some(h => /\b(run|use|fix|debug|check|create|deploy|scan|audit)\b/i.test(h))) signals.push(chalk.green('+2 imperative verbs in headers'));
268
+ if (raw.includes('```')) signals.push(chalk.green('+1 code block'));
269
+ if (lines.some(l => /^\d+\.\s/.test(l))) signals.push(chalk.green('+1 numbered list'));
270
+ if (lines.some(l => /^[-*+]\s/.test(l))) signals.push(chalk.green('+1 bullet list'));
271
+ const firstH = lines.find(l => /^#{1,3}\s/.test(l))?.replace(/^#+\s*/, '') || '';
272
+ if (/^(overview|introduction|about|background|welcome)/i.test(firstH)) signals.push(chalk.red('-3 first header looks like docs ("' + firstH + '")'));
273
+ if (signals.length) {
274
+ signals.forEach(s => console.log(' ' + s));
275
+ }
276
+ console.log();
277
+ }
278
+
251
279
  const result = validateSkill(file);
252
280
  result.warnings.forEach(w => console.log(chalk.yellow('⚠') + ' ' + chalk.gray(w)));
253
281
  if (result.ok) {
254
- success('Skill is valid');
282
+ success('Skill is valid — ready to publish');
255
283
  process.exit(0);
256
284
  } else {
257
285
  error('Validation failed:');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "promptgraph-mcp",
3
- "version": "2.2.0",
3
+ "version": "2.2.2",
4
4
  "main": "index.js",
5
5
  "type": "module",
6
6
  "bin": {
package/tui.js CHANGED
@@ -143,9 +143,7 @@ function render(state, installedSet = new Set()) {
143
143
  }
144
144
 
145
145
  // item row
146
- const isInstalled = item.type === 'bundle'
147
- ? installedSet.has(item.id) || installedSet.has(item.id.toLowerCase())
148
- : installedSet.has(item.id) || installedSet.has(item.code);
146
+ const isInstalled = installedSet.has(item.id) || (item.code && installedSet.has(item.code));
149
147
  const arrow = selected ? cyan('▶') : ' ';
150
148
  const type = item.type === 'bundle' ? blue('⊞') : dim('·');
151
149
  const badge = isInstalled ? green('✓') : ' ';
@@ -171,9 +169,7 @@ function render(state, installedSet = new Set()) {
171
169
  write(dim('─'.repeat(cols)) + CLEAR_EOL + '\n');
172
170
  const sel = items[cursor];
173
171
  if (sel && !searching) {
174
- const isInst = sel.type === 'bundle'
175
- ? installedSet.has(sel.id) || installedSet.has(sel.id.toLowerCase())
176
- : installedSet.has(sel.id) || installedSet.has(sel.code);
172
+ const isInst = installedSet.has(sel.id) || (sel.code && installedSet.has(sel.code));
177
173
  const installCmd = sel.type === 'bundle' ? `bundle install ${sel.id}` : `install ${sel.code || sel.id}`;
178
174
  const instLabel = isInst ? green(' ✓ installed') + dim(' ') : dim(' Enter') + chalk.white(' install') + dim(' ');
179
175
  write(instLabel + dim('Tab') + ' switch ' + dim('/') + ' search ' + dim('q') + ' quit' + CLEAR_EOL + '\n');