promptgraph-mcp 2.8.3 → 2.8.5

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 +25 -5
  2. package/package.json +1 -1
package/github-import.js CHANGED
@@ -643,15 +643,23 @@ function detectSubdirFromTree(repoRoot) {
643
643
  }
644
644
  }
645
645
 
646
+ // No known skill dir found — check if repo root has .md files itself
647
+ const rootMdCount = entries.filter(e => e.endsWith('.md')).length;
648
+ if (rootMdCount >= 2) return null; // root has skills — take everything
649
+
650
+ // Otherwise check subdirs: if most non-skip subdirs have .md files, take root (all dirs)
646
651
  const skipSet = new Set([...SKIP_DIRS_API]);
647
652
  const candidates = entries.filter(e => !skipSet.has(e.toLowerCase()) && !e.includes('.'));
653
+ let dirsWithMd = 0;
648
654
  let best = null, bestCount = 0;
649
655
  for (const dir of candidates) {
650
- const sub = spawnSync('git', ['-C', repoRoot, 'ls-tree', '--name-only', `HEAD:${dir}`], { encoding: 'utf8', stdio: 'pipe' });
656
+ const sub = spawnSync('git', ['-C', repoRoot, 'ls-tree', '-r', '--name-only', `HEAD:${dir}`], { encoding: 'utf8', stdio: 'pipe' });
651
657
  if (sub.status !== 0) continue;
652
658
  const mdCount = sub.stdout.split('\n').filter(f => f.endsWith('.md')).length;
653
- if (mdCount >= 1 && mdCount > bestCount) { best = dir; bestCount = mdCount; }
659
+ if (mdCount >= 1) { dirsWithMd++; if (mdCount > bestCount) { best = dir; bestCount = mdCount; } }
654
660
  }
661
+ // If multiple dirs have .md files — they're all skill categories, take root
662
+ if (dirsWithMd > 1) return null;
655
663
  return best;
656
664
  }
657
665
 
@@ -670,18 +678,23 @@ export async function importFromGitHubLight(repoUrl) {
670
678
  fs.mkdirSync(destBase, { recursive: true });
671
679
 
672
680
  const gitEnv = { ...process.env, GIT_TERMINAL_PROMPT: '0' };
681
+ const prog = (msg) => process.stderr.write(`\r\x1b[K ${msg}`);
673
682
 
674
683
  // Step 1: treeless clone — gets file tree instantly, no blob download, no API
675
- const init = spawnSync('git', ['clone', '--depth=1', '--filter=blob:none', '--no-checkout', cloneUrl, destBase], { stdio: 'pipe', env: gitEnv, timeout: 60000 });
684
+ prog(`Cloning ${ownerRepo}...`);
685
+ const init = spawnSync('git', ['clone', '--depth=1', '--filter=blob:none', '--no-checkout', '--progress', cloneUrl, destBase], { stdio: 'pipe', env: gitEnv, timeout: 120000 });
676
686
  if (init.status !== 0) {
687
+ process.stderr.write('\n');
677
688
  fs.rmSync(destBase, { recursive: true, force: true });
678
689
  throw new Error(`Failed to clone ${ownerRepo}: ${(init.stderr?.toString() || '').trim().slice(0, 120)}`);
679
690
  }
680
691
 
681
692
  // Step 2: detect skills subdir from local tree — zero API calls
693
+ prog(`Detecting skill directory...`);
682
694
  const subdir = detectSubdirFromTree(destBase);
683
695
 
684
696
  // Step 3: sparse-checkout only the skills subdir .md files
697
+ prog(`Setting up sparse checkout${subdir ? ` (${subdir}/)` : ''}...`);
685
698
  spawnSync('git', ['-C', destBase, 'sparse-checkout', 'init'], { stdio: 'pipe', env: gitEnv });
686
699
  if (subdir) {
687
700
  spawnSync('git', ['-C', destBase, 'sparse-checkout', 'set', '--no-cone', `${subdir}/*.md`, `${subdir}/**/*.md`], { stdio: 'pipe', env: gitEnv });
@@ -690,16 +703,20 @@ export async function importFromGitHubLight(repoUrl) {
690
703
  }
691
704
 
692
705
  // Step 4: checkout to materialize only the selected files
693
- const co = spawnSync('git', ['-C', destBase, 'checkout'], { stdio: 'pipe', env: gitEnv, timeout: 60000 });
706
+ prog(`Downloading .md files...`);
707
+ const co = spawnSync('git', ['-C', destBase, 'checkout'], { stdio: 'pipe', env: gitEnv, timeout: 120000 });
694
708
  if (co.status !== 0) {
709
+ process.stderr.write('\n');
695
710
  fs.rmSync(destBase, { recursive: true, force: true });
696
711
  throw new Error(`Checkout failed for ${ownerRepo}`);
697
712
  }
698
713
  // Force blob materialization (needed for partial clones on Windows)
699
- spawnSync('git', ['-C', destBase, 'checkout', 'HEAD', '--', '.'], { stdio: 'pipe', env: gitEnv, timeout: 60000 });
714
+ spawnSync('git', ['-C', destBase, 'checkout', 'HEAD', '--', '.'], { stdio: 'pipe', env: gitEnv, timeout: 120000 });
715
+ process.stderr.write('\n');
700
716
 
701
717
  // Step 5: filter out non-skill files locally
702
718
  const allMd = globSync(`${destBase}/**/*.md`);
719
+ prog(`Filtering ${allMd.length} files...`);
703
720
  let removed = 0;
704
721
  for (const fp of allMd) {
705
722
  if (!isSkillFile(fp)) { try { fs.unlinkSync(fp); removed++; } catch {} }
@@ -707,6 +724,7 @@ export async function importFromGitHubLight(repoUrl) {
707
724
  if (removed > 0) removeEmptyDirs(destBase);
708
725
 
709
726
  const remaining = globSync(`${destBase}/**/*.md`);
727
+ prog(`Validating ${remaining.length} skill files...`);
710
728
  let removedV = 0;
711
729
  for (const fp of remaining) {
712
730
  const v = validateSkill(fp);
@@ -714,7 +732,9 @@ export async function importFromGitHubLight(repoUrl) {
714
732
  }
715
733
  if (removedV > 0) removeEmptyDirs(destBase);
716
734
 
735
+ prog(`Running classifier...`);
717
736
  await classifierCleanup(destBase);
737
+ process.stderr.write('\n');
718
738
 
719
739
  const realCount = globSync(`${destBase}/**/*.md`).length;
720
740
  const cacheKey = url.replace(/\.git$/, '');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "promptgraph-mcp",
3
- "version": "2.8.3",
3
+ "version": "2.8.5",
4
4
  "files": [
5
5
  "*.js",
6
6
  "commands/",