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.
- package/github-import.js +25 -5
- 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
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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$/, '');
|