promptgraph-mcp 2.6.5 → 2.6.7
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/commands/marketplace.js +5 -2
- package/github-import.js +5 -6
- package/marketplace.js +32 -10
- package/package.json +1 -1
- package/tui.js +12 -3
package/commands/marketplace.js
CHANGED
|
@@ -31,8 +31,11 @@ export default async function handler(args, bin) {
|
|
|
31
31
|
const { spinner: spin2 } = await import('../cli.js');
|
|
32
32
|
const sp = spin2('Fetching marketplace...');
|
|
33
33
|
sp.start();
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
try {
|
|
35
|
+
var [skills, bundles] = await Promise.all([browseMarketplace(1000), browseBundles(1000)]);
|
|
36
|
+
} finally {
|
|
37
|
+
sp.stop();
|
|
38
|
+
}
|
|
36
39
|
|
|
37
40
|
if (skills?.error) { error(skills.error); process.exit(1); }
|
|
38
41
|
|
package/github-import.js
CHANGED
|
@@ -582,14 +582,13 @@ export async function importFromGitHub(repoUrl) {
|
|
|
582
582
|
|
|
583
583
|
// Update skill count cache with real survivor count
|
|
584
584
|
const realCount = globSync(`${dest}/**/*.md`).length;
|
|
585
|
+
const cacheKey = url.replace(/\.git$/, '');
|
|
585
586
|
const cachePath = path.join(PROMPTGRAPH_DIR, 'skill-counts.json');
|
|
586
587
|
try {
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
fs.writeFileSync(cachePath, JSON.stringify(cache, null, 2));
|
|
592
|
-
}
|
|
588
|
+
fs.mkdirSync(path.dirname(cachePath), { recursive: true });
|
|
589
|
+
const cache = JSON.parse(fs.readFileSync(cachePath, 'utf8') || '{}');
|
|
590
|
+
cache[cacheKey] = { count: realCount, ts: Date.now() };
|
|
591
|
+
fs.writeFileSync(cachePath, JSON.stringify(cache, null, 2));
|
|
593
592
|
} catch {}
|
|
594
593
|
|
|
595
594
|
const { dir: localDir, label: localLabel } = detectSkillsDirLocal(dest);
|
package/marketplace.js
CHANGED
|
@@ -262,6 +262,15 @@ async function countRepoSkills(repoUrl) {
|
|
|
262
262
|
} catch { return null; }
|
|
263
263
|
}
|
|
264
264
|
|
|
265
|
+
// Count real .md files on disk for an installed bundle (always correct)
|
|
266
|
+
function localSkillCount(repoUrl) {
|
|
267
|
+
const repoName = repoUrl.replace(/^https?:\/\/github\.com\//, '').replace(/\.git$/, '').replace('/', '-');
|
|
268
|
+
const dest = path.join(SKILLS_STORE_DIR, 'github', repoName);
|
|
269
|
+
if (!fs.existsSync(dest)) return null;
|
|
270
|
+
const files = globSync(`${dest}/**/*.md`);
|
|
271
|
+
return files.length;
|
|
272
|
+
}
|
|
273
|
+
|
|
265
274
|
export async function browseBundles(topK = 20) {
|
|
266
275
|
try {
|
|
267
276
|
const text = await fetchText(REGISTRY_URL);
|
|
@@ -273,20 +282,33 @@ export async function browseBundles(topK = 20) {
|
|
|
273
282
|
|
|
274
283
|
await Promise.all(bundles.map(async b => {
|
|
275
284
|
if (!b.repo_url) return;
|
|
285
|
+
|
|
286
|
+
// 1. If installed locally — count real files on disk (always correct)
|
|
287
|
+
const local = localSkillCount(b.repo_url);
|
|
288
|
+
if (local !== null) {
|
|
289
|
+
if (b.skillCount !== local) {
|
|
290
|
+
b.skillCount = local;
|
|
291
|
+
cache[b.repo_url] = { count: local, ts: now };
|
|
292
|
+
changed = true;
|
|
293
|
+
}
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// 2. Not installed — use cached API count if fresh
|
|
276
298
|
const cached = cache[b.repo_url];
|
|
277
|
-
// Use cached count if fresh, else fetch from API
|
|
278
299
|
if (cached && (now - cached.ts) < SKILL_COUNT_TTL) {
|
|
279
300
|
b.skillCount = cached.count;
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// 3. Fetch from GitHub API
|
|
305
|
+
const count = await countRepoSkills(b.repo_url);
|
|
306
|
+
if (count !== null) {
|
|
307
|
+
b.skillCount = count;
|
|
308
|
+
cache[b.repo_url] = { count, ts: now };
|
|
309
|
+
changed = true;
|
|
280
310
|
} else {
|
|
281
|
-
|
|
282
|
-
if (count !== null) {
|
|
283
|
-
b.skillCount = count;
|
|
284
|
-
cache[b.repo_url] = { count, ts: now };
|
|
285
|
-
changed = true;
|
|
286
|
-
} else {
|
|
287
|
-
// API failed — use stale cache if exists, else keep registry value as fallback
|
|
288
|
-
b.skillCount = cached?.count ?? b.skillCount ?? 0;
|
|
289
|
-
}
|
|
311
|
+
b.skillCount = cached?.count ?? b.skillCount ?? 0;
|
|
290
312
|
}
|
|
291
313
|
}));
|
|
292
314
|
|
package/package.json
CHANGED
package/tui.js
CHANGED
|
@@ -22,8 +22,10 @@ const green = chalk.green;
|
|
|
22
22
|
const red = chalk.red;
|
|
23
23
|
const blue = chalk.blue;
|
|
24
24
|
const white = chalk.white;
|
|
25
|
+
const magenta = chalk.magenta;
|
|
25
26
|
|
|
26
27
|
const CAT_ICON = { Engineering:'🛠', 'AI Tools':'🤖', Coding:'💻', Creative:'🎨', Security:'🔒', Community:'🌐' };
|
|
28
|
+
const SPINNER_FRAMES = ['⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷']; // braille spinner
|
|
27
29
|
|
|
28
30
|
// ── helpers ──────────────────────────────────────────────────────────────────
|
|
29
31
|
|
|
@@ -113,8 +115,11 @@ function render(state, installedSet = new Set()) {
|
|
|
113
115
|
: dim(query ? query : 'type / to search, Tab to switch view');
|
|
114
116
|
write(searchLabel + searchVal + CLEAR_EOL + '\n');
|
|
115
117
|
|
|
116
|
-
// Row 4: separator (with optional status inline)
|
|
117
|
-
if (
|
|
118
|
+
// Row 4: separator (with optional status inline or spinner)
|
|
119
|
+
if (state.installing) {
|
|
120
|
+
const frame = SPINNER_FRAMES[Math.floor(Date.now() / 120) % SPINNER_FRAMES.length];
|
|
121
|
+
write(dim('─'.repeat(4)) + magenta(` ${frame} Installing… `) + CLEAR_EOL + '\n');
|
|
122
|
+
} else if (status) {
|
|
118
123
|
const msg = status.ok ? green(' ✓ ' + status.msg) : red(' ✗ ' + status.msg);
|
|
119
124
|
write(dim('─'.repeat(4)) + msg + CLEAR_EOL + '\n');
|
|
120
125
|
} else {
|
|
@@ -388,11 +393,15 @@ export async function runTUI(allSkills, allBundles, installFn, installedSet = ne
|
|
|
388
393
|
const sel = state.items[state.cursor];
|
|
389
394
|
if (!sel || state.installing) return;
|
|
390
395
|
state.installing = true;
|
|
391
|
-
|
|
396
|
+
// Live spinner — keeps rendering during long installs
|
|
397
|
+
const spinInterval = setInterval(() => render(state, installedSet), 120);
|
|
398
|
+
render(state, installedSet);
|
|
392
399
|
try {
|
|
393
400
|
await installFn(sel);
|
|
401
|
+
clearInterval(spinInterval);
|
|
394
402
|
setStatus(true, `Installed ${sel.id}`);
|
|
395
403
|
} catch (e) {
|
|
404
|
+
clearInterval(spinInterval);
|
|
396
405
|
setStatus(false, e.message.slice(0, 60));
|
|
397
406
|
} finally {
|
|
398
407
|
state.installing = false;
|