promptgraph-mcp 1.5.18 → 1.5.20
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/cli.js +5 -0
- package/index.js +28 -23
- package/marketplace.js +15 -5
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -34,6 +34,11 @@ export function spinner(text) {
|
|
|
34
34
|
return ora({ text: colors.muted(text), spinner: 'dots', color: 'magenta' });
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
// Full clear including scrollback (console.clear leaves scrollback on Windows)
|
|
38
|
+
export function clearScreen() {
|
|
39
|
+
process.stdout.write('\x1b[2J\x1b[3J\x1b[H');
|
|
40
|
+
}
|
|
41
|
+
|
|
37
42
|
export function success(msg) {
|
|
38
43
|
console.log('\n' + colors.success('✓') + ' ' + msg);
|
|
39
44
|
}
|
package/index.js
CHANGED
|
@@ -90,7 +90,7 @@ if (args[0] === 'marketplace' && (args[1] === 'bundles' || args[1] === 'bundle')
|
|
|
90
90
|
spin.start();
|
|
91
91
|
const bundles = await browseBundles(1000);
|
|
92
92
|
spin.stop();
|
|
93
|
-
|
|
93
|
+
(await import('./cli.js')).clearScreen();
|
|
94
94
|
|
|
95
95
|
if (bundles?.error) { error(bundles.error); process.exit(1); }
|
|
96
96
|
|
|
@@ -134,11 +134,12 @@ if (args[0] === 'marketplace') {
|
|
|
134
134
|
const PER_PAGE = 10;
|
|
135
135
|
const page = Math.max(1, parseInt(args[1]) || 1);
|
|
136
136
|
|
|
137
|
+
const { clearScreen } = await import('./cli.js');
|
|
137
138
|
const spin = (await import('./cli.js')).spinner('Fetching registry...');
|
|
138
139
|
spin.start();
|
|
139
140
|
const all = await browseMarketplace(1000);
|
|
140
141
|
spin.stop();
|
|
141
|
-
|
|
142
|
+
clearScreen();
|
|
142
143
|
|
|
143
144
|
if (all?.error) {
|
|
144
145
|
error(all.error);
|
|
@@ -154,6 +155,7 @@ if (args[0] === 'marketplace') {
|
|
|
154
155
|
const startIdx = (page - 1) * PER_PAGE;
|
|
155
156
|
const slice = all.slice(startIdx, startIdx + PER_PAGE);
|
|
156
157
|
const purple = chalk.hex('#7C3AED');
|
|
158
|
+
const W = 60;
|
|
157
159
|
|
|
158
160
|
const wrap = (text, width, indent) => {
|
|
159
161
|
const words = (text || '').split(/\s+/);
|
|
@@ -164,38 +166,41 @@ if (args[0] === 'marketplace') {
|
|
|
164
166
|
else line += ' ' + w;
|
|
165
167
|
}
|
|
166
168
|
if (line.trim()) lines.push(line.trim());
|
|
167
|
-
return lines.map(l => indent + chalk.
|
|
169
|
+
return lines.map(l => indent + chalk.dim(l)).join('\n');
|
|
168
170
|
};
|
|
169
171
|
|
|
172
|
+
// header
|
|
170
173
|
console.log();
|
|
171
|
-
console.log(' ' + purple.bold('PromptGraph
|
|
172
|
-
console.log(' ' + chalk.
|
|
173
|
-
console.log(' ' +
|
|
174
|
-
console.log();
|
|
174
|
+
console.log(' ' + purple.bold('◆ PromptGraph') + chalk.dim(' · marketplace'));
|
|
175
|
+
console.log(' ' + chalk.dim(`${all.length} skills`) + chalk.dim(totalPages > 1 ? ` · page ${page}/${totalPages}` : ''));
|
|
176
|
+
console.log(chalk.dim(' ' + '━'.repeat(W)));
|
|
175
177
|
|
|
176
178
|
slice.forEach((s, i) => {
|
|
177
|
-
const
|
|
178
|
-
const
|
|
179
|
-
const
|
|
180
|
-
console.log(' ' + num + ' ' + chalk.white.bold(s.id) + ' ' + stars + ' ' + code);
|
|
181
|
-
console.log(wrap(s.description, 64, ' '));
|
|
182
|
-
if (s.tags?.length) console.log(' ' + purple(s.tags.map(t => '#' + t).join(' ')));
|
|
183
|
-
console.log(' ' + chalk.gray('install: tell your AI ') + chalk.cyan(`install ${s.code || s.id}`));
|
|
179
|
+
const n = chalk.dim(String(startIdx + i + 1).padStart(2));
|
|
180
|
+
const code = s.code ? chalk.hex('#A78BFA')(s.code) : '';
|
|
181
|
+
const stars = chalk.yellow('★') + chalk.dim(' ' + (s.stars || 0));
|
|
184
182
|
console.log();
|
|
183
|
+
// line 1: number, name ........ code, stars
|
|
184
|
+
const left = `${n} ${chalk.bold.white(s.id)}`;
|
|
185
|
+
console.log(' ' + left + ' ' + code + ' ' + stars);
|
|
186
|
+
// description
|
|
187
|
+
console.log(wrap(s.description, W - 6, ' '));
|
|
188
|
+
// tags
|
|
189
|
+
if (s.tags?.length) console.log(' ' + chalk.dim(s.tags.map(t => '#' + t).join(' ')));
|
|
185
190
|
});
|
|
186
191
|
|
|
187
|
-
console.log(
|
|
192
|
+
console.log();
|
|
193
|
+
console.log(chalk.dim(' ' + '━'.repeat(W)));
|
|
194
|
+
|
|
188
195
|
if (totalPages > 1) {
|
|
189
196
|
const nav = [];
|
|
190
|
-
if (page > 1) nav.push(chalk.cyan(`${bin} marketplace ${page - 1}`)
|
|
191
|
-
if (page < totalPages) nav.push(chalk.
|
|
192
|
-
console.log(' ' + nav.join('
|
|
193
|
-
console.log();
|
|
197
|
+
if (page > 1) nav.push(chalk.dim('‹ ') + chalk.cyan(`${bin} marketplace ${page - 1}`));
|
|
198
|
+
if (page < totalPages) nav.push(chalk.cyan(`${bin} marketplace ${page + 1}`) + chalk.dim(' ›'));
|
|
199
|
+
console.log(' ' + nav.join(' '));
|
|
194
200
|
}
|
|
195
|
-
console.log(' ' + chalk.
|
|
196
|
-
console.log(' ' + chalk.
|
|
197
|
-
console.log();
|
|
198
|
-
console.log(' ' + chalk.gray('Curated sets: ') + chalk.cyan(`${bin} marketplace bundles`) + chalk.gray(' · Publish: ') + chalk.cyan('/pg-publish <file>'));
|
|
201
|
+
console.log(' ' + chalk.dim('install ') + chalk.cyan('tell your AI:') + ' ' + chalk.white('install ') + chalk.hex('#A78BFA')(slice[0].code || slice[0].id));
|
|
202
|
+
console.log(' ' + chalk.dim('bundles ') + chalk.cyan(`${bin} marketplace bundles`));
|
|
203
|
+
console.log(' ' + chalk.dim('publish ') + chalk.cyan('tell your AI:') + ' ' + chalk.white('/pg-publish <file>'));
|
|
199
204
|
console.log();
|
|
200
205
|
process.exit(0);
|
|
201
206
|
}
|
package/marketplace.js
CHANGED
|
@@ -2,6 +2,7 @@ import fs from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import os from 'os';
|
|
4
4
|
import https from 'https';
|
|
5
|
+
import { createHash } from 'crypto';
|
|
5
6
|
import { spawnSync } from 'child_process';
|
|
6
7
|
import { getDb } from './db.js';
|
|
7
8
|
import { validateSkill } from './validator.js';
|
|
@@ -9,6 +10,12 @@ import { validateSkill } from './validator.js';
|
|
|
9
10
|
const REGISTRY_URL = 'https://raw.githubusercontent.com/NeiP4n/promptgraph-registry/main/registry.json';
|
|
10
11
|
const SKILLS_DIR = path.join(os.homedir(), '.claude', 'skills-store', 'marketplace');
|
|
11
12
|
|
|
13
|
+
// Deterministic short code from an id. Same id always yields the same code,
|
|
14
|
+
// so codes auto-generate — no need to assign them by hand.
|
|
15
|
+
export function codeFor(id) {
|
|
16
|
+
return 'pg-' + createHash('md5').update(String(id)).digest('hex').slice(0, 6);
|
|
17
|
+
}
|
|
18
|
+
|
|
12
19
|
// Robust fetch: try undici fetch, fall back to node:https (works where undici fails on Windows)
|
|
13
20
|
function httpGet(url) {
|
|
14
21
|
return new Promise((resolve, reject) => {
|
|
@@ -44,6 +51,7 @@ export async function browseMarketplace(topK = 20) {
|
|
|
44
51
|
const registry = JSON.parse(text);
|
|
45
52
|
if (!Array.isArray(registry.skills)) return { error: 'Invalid registry format' };
|
|
46
53
|
return registry.skills
|
|
54
|
+
.map(s => ({ ...s, code: s.code || codeFor(s.id) })) // auto-fill code
|
|
47
55
|
.sort((a, b) => (b.stars || 0) - (a.stars || 0))
|
|
48
56
|
.slice(0, topK);
|
|
49
57
|
} catch (e) {
|
|
@@ -56,15 +64,16 @@ export async function installSkill(query) {
|
|
|
56
64
|
const text = await fetchText(REGISTRY_URL);
|
|
57
65
|
const registry = JSON.parse(text);
|
|
58
66
|
const q = String(query).trim().toLowerCase();
|
|
59
|
-
// match by code, id, or name
|
|
67
|
+
// match by code (stored OR auto-generated), id, or name
|
|
60
68
|
const skill = registry.skills?.find(s =>
|
|
61
|
-
s.code
|
|
69
|
+
(s.code || codeFor(s.id)).toLowerCase() === q ||
|
|
62
70
|
s.id?.toLowerCase() === q ||
|
|
63
71
|
s.name?.toLowerCase() === q
|
|
64
72
|
);
|
|
65
73
|
if (!skill) {
|
|
66
|
-
|
|
67
|
-
|
|
74
|
+
const bundle = (registry.bundles || []).find(b =>
|
|
75
|
+
(b.code || codeFor(b.id)).toLowerCase() === q || b.id?.toLowerCase() === q
|
|
76
|
+
);
|
|
68
77
|
if (bundle) return { error: `"${query}" is a bundle. Use pg_bundle_install("${bundle.id}") instead.` };
|
|
69
78
|
return { error: `No skill matching "${query}" (try a code like pg-xxxxxx, an id, or a name)` };
|
|
70
79
|
}
|
|
@@ -89,6 +98,7 @@ export async function browseBundles(topK = 20) {
|
|
|
89
98
|
const registry = JSON.parse(text);
|
|
90
99
|
const bundles = registry.bundles || [];
|
|
91
100
|
return bundles
|
|
101
|
+
.map(b => ({ ...b, code: b.code || codeFor(b.id) }))
|
|
92
102
|
.sort((a, b) => (b.stars || 0) - (a.stars || 0))
|
|
93
103
|
.slice(0, topK);
|
|
94
104
|
} catch (e) {
|
|
@@ -102,7 +112,7 @@ export async function installBundle(bundleId) {
|
|
|
102
112
|
const registry = JSON.parse(text);
|
|
103
113
|
const q = String(bundleId).trim().toLowerCase();
|
|
104
114
|
const bundle = (registry.bundles || []).find(b =>
|
|
105
|
-
b.code
|
|
115
|
+
(b.code || codeFor(b.id)).toLowerCase() === q || b.id?.toLowerCase() === q || b.name?.toLowerCase() === q
|
|
106
116
|
);
|
|
107
117
|
if (!bundle) return { error: `No bundle matching "${bundleId}"` };
|
|
108
118
|
|