promptgraph-mcp 2.1.6 → 2.1.8
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/index.js +38 -1
- package/package.json +1 -1
- package/tui.js +26 -18
package/index.js
CHANGED
|
@@ -182,6 +182,8 @@ if (args[0] === 'marketplace') {
|
|
|
182
182
|
process.exit(1);
|
|
183
183
|
}
|
|
184
184
|
const { browseMarketplace, browseBundles, installSkill, installBundle } = await import('./marketplace.js');
|
|
185
|
+
const { loadConfig: _lcMkt } = await import('./config.js');
|
|
186
|
+
const { getDb: _getDbMkt } = await import('./db.js');
|
|
185
187
|
const { spinner: spin2 } = await import('./cli.js');
|
|
186
188
|
const sp = spin2('Fetching marketplace...');
|
|
187
189
|
sp.start();
|
|
@@ -190,6 +192,37 @@ if (args[0] === 'marketplace') {
|
|
|
190
192
|
|
|
191
193
|
if (skills?.error) { error(skills.error); process.exit(1); }
|
|
192
194
|
|
|
195
|
+
// Build installed set: bundle IDs from config sources + skill IDs from DB
|
|
196
|
+
const installedSet = new Set();
|
|
197
|
+
try {
|
|
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
|
+
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
|
|
206
|
+
for (const b of (Array.isArray(bundles) ? bundles : [])) {
|
|
207
|
+
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])) {
|
|
220
|
+
installedSet.add(b.id);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
} catch {}
|
|
225
|
+
|
|
193
226
|
const { runTUI } = await import('./tui.js');
|
|
194
227
|
await runTUI(
|
|
195
228
|
Array.isArray(skills) ? skills : [],
|
|
@@ -198,11 +231,15 @@ if (args[0] === 'marketplace') {
|
|
|
198
231
|
if (item.type === 'bundle') {
|
|
199
232
|
const r = await installBundle(item.id);
|
|
200
233
|
if (r?.error) throw new Error(r.error);
|
|
234
|
+
installedSet.add(item.id);
|
|
201
235
|
} else {
|
|
202
236
|
const r = await installSkill(item.code || item.id);
|
|
203
237
|
if (r?.error) throw new Error(r.error);
|
|
238
|
+
installedSet.add(item.id);
|
|
239
|
+
if (item.code) installedSet.add(item.code);
|
|
204
240
|
}
|
|
205
|
-
}
|
|
241
|
+
},
|
|
242
|
+
installedSet
|
|
206
243
|
);
|
|
207
244
|
process.exit(0);
|
|
208
245
|
}
|
package/package.json
CHANGED
package/tui.js
CHANGED
|
@@ -73,7 +73,7 @@ function filterItems(items, query, tab) {
|
|
|
73
73
|
|
|
74
74
|
// ── render ────────────────────────────────────────────────────────────────────
|
|
75
75
|
|
|
76
|
-
function render(state) {
|
|
76
|
+
function render(state, installedSet = new Set()) {
|
|
77
77
|
const { cols, rows } = termSize();
|
|
78
78
|
const HEADER_ROWS = 5;
|
|
79
79
|
const FOOTER_ROWS = 3;
|
|
@@ -143,17 +143,21 @@ function render(state) {
|
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
// item row
|
|
146
|
-
const
|
|
147
|
-
|
|
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);
|
|
149
|
+
const arrow = selected ? cyan('▶') : ' ';
|
|
150
|
+
const type = item.type === 'bundle' ? blue('⊞') : dim('·');
|
|
151
|
+
const badge = isInstalled ? green('✓') : ' ';
|
|
148
152
|
const nameStr = truncate(item.name, NAME_W);
|
|
149
153
|
const namePad = nameStr.padEnd(NAME_W);
|
|
150
154
|
const nameCol = selected ? white.bold(namePad) : white(namePad);
|
|
151
|
-
const extra
|
|
155
|
+
const extra = item.type === 'bundle'
|
|
152
156
|
? (item.skillCount ? blue((item.skillCount + ' sk').padEnd(8)) : blue('GitHub '))
|
|
153
157
|
: dim((item.code || '').padEnd(8));
|
|
154
158
|
const desc = dim(truncate(item.description, Math.max(10, DESC_W)));
|
|
155
159
|
|
|
156
|
-
write(bg + ` ${arrow} ${type} ${nameCol} ${extra} ${desc}` + reset + CLEAR_EOL + '\n');
|
|
160
|
+
write(bg + ` ${arrow} ${type} ${badge} ${nameCol} ${extra} ${desc}` + reset + CLEAR_EOL + '\n');
|
|
157
161
|
rendered++;
|
|
158
162
|
}
|
|
159
163
|
|
|
@@ -167,8 +171,12 @@ function render(state) {
|
|
|
167
171
|
write(dim('─'.repeat(cols)) + CLEAR_EOL + '\n');
|
|
168
172
|
const sel = items[cursor];
|
|
169
173
|
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);
|
|
170
177
|
const installCmd = sel.type === 'bundle' ? `bundle install ${sel.id}` : `install ${sel.code || sel.id}`;
|
|
171
|
-
|
|
178
|
+
const instLabel = isInst ? green(' ✓ installed') + dim(' ') : dim(' Enter') + chalk.white(' install') + dim(' ');
|
|
179
|
+
write(instLabel + dim('Tab') + ' switch ' + dim('/') + ' search ' + dim('q') + ' quit' + CLEAR_EOL + '\n');
|
|
172
180
|
write(dim(` → pg ${installCmd}`) + CLEAR_EOL + '\n');
|
|
173
181
|
} else if (searching) {
|
|
174
182
|
write(dim(' Type to filter ') + cyan('Enter') + dim(' confirm ') + cyan('Esc') + dim(' cancel') + CLEAR_EOL + '\n');
|
|
@@ -196,7 +204,7 @@ function clampScroll(state) {
|
|
|
196
204
|
|
|
197
205
|
// ── main ─────────────────────────────────────────────────────────────────────
|
|
198
206
|
|
|
199
|
-
export async function runTUI(allSkills, allBundles, installFn) {
|
|
207
|
+
export async function runTUI(allSkills, allBundles, installFn, installedSet = new Set()) {
|
|
200
208
|
const allItems = buildItems(allSkills, allBundles);
|
|
201
209
|
|
|
202
210
|
const state = {
|
|
@@ -214,7 +222,7 @@ export async function runTUI(allSkills, allBundles, installFn) {
|
|
|
214
222
|
state.items = filterItems(allItems, q ?? state.query, t ?? state.tab);
|
|
215
223
|
if (state.cursor >= state.items.length) state.cursor = Math.max(0, state.items.length - 1);
|
|
216
224
|
clampScroll(state);
|
|
217
|
-
render(state);
|
|
225
|
+
render(state, installedSet);
|
|
218
226
|
}
|
|
219
227
|
|
|
220
228
|
// Setup terminal
|
|
@@ -239,8 +247,8 @@ export async function runTUI(allSkills, allBundles, installFn) {
|
|
|
239
247
|
function setStatus(ok, msg) {
|
|
240
248
|
state.status = { ok, msg };
|
|
241
249
|
clearTimeout(statusTimer);
|
|
242
|
-
render(state);
|
|
243
|
-
statusTimer = setTimeout(() => { state.status = null; render(state); }, 3000);
|
|
250
|
+
render(state, installedSet);
|
|
251
|
+
statusTimer = setTimeout(() => { state.status = null; render(state, installedSet); }, 3000);
|
|
244
252
|
}
|
|
245
253
|
|
|
246
254
|
// Keypress handler
|
|
@@ -273,7 +281,7 @@ export async function runTUI(allSkills, allBundles, installFn) {
|
|
|
273
281
|
|
|
274
282
|
if (key.name === 'slash' || ch === '/') {
|
|
275
283
|
state.searching = true;
|
|
276
|
-
render(state);
|
|
284
|
+
render(state, installedSet);
|
|
277
285
|
return;
|
|
278
286
|
}
|
|
279
287
|
|
|
@@ -289,33 +297,33 @@ export async function runTUI(allSkills, allBundles, installFn) {
|
|
|
289
297
|
if (key.name === 'up') {
|
|
290
298
|
if (state.cursor > 0) state.cursor--;
|
|
291
299
|
clampScroll(state);
|
|
292
|
-
render(state);
|
|
300
|
+
render(state, installedSet);
|
|
293
301
|
return;
|
|
294
302
|
}
|
|
295
303
|
|
|
296
304
|
if (key.name === 'down') {
|
|
297
305
|
if (state.cursor < state.items.length - 1) state.cursor++;
|
|
298
306
|
clampScroll(state);
|
|
299
|
-
render(state);
|
|
307
|
+
render(state, installedSet);
|
|
300
308
|
return;
|
|
301
309
|
}
|
|
302
310
|
|
|
303
311
|
if (key.name === 'pageup') {
|
|
304
312
|
state.cursor = Math.max(0, state.cursor - 10);
|
|
305
313
|
clampScroll(state);
|
|
306
|
-
render(state);
|
|
314
|
+
render(state, installedSet);
|
|
307
315
|
return;
|
|
308
316
|
}
|
|
309
317
|
|
|
310
318
|
if (key.name === 'pagedown') {
|
|
311
319
|
state.cursor = Math.min(state.items.length - 1, state.cursor + 10);
|
|
312
320
|
clampScroll(state);
|
|
313
|
-
render(state);
|
|
321
|
+
render(state, installedSet);
|
|
314
322
|
return;
|
|
315
323
|
}
|
|
316
324
|
|
|
317
|
-
if (key.name === 'home') { state.cursor = 0; state.scroll = 0; render(state); return; }
|
|
318
|
-
if (key.name === 'end') { state.cursor = state.items.length - 1; clampScroll(state); render(state); return; }
|
|
325
|
+
if (key.name === 'home') { state.cursor = 0; state.scroll = 0; render(state, installedSet); return; }
|
|
326
|
+
if (key.name === 'end') { state.cursor = state.items.length - 1; clampScroll(state); render(state, installedSet); return; }
|
|
319
327
|
|
|
320
328
|
if (key.name === 'return' || key.name === 'i') {
|
|
321
329
|
const sel = state.items[state.cursor];
|
|
@@ -341,7 +349,7 @@ export async function runTUI(allSkills, allBundles, installFn) {
|
|
|
341
349
|
});
|
|
342
350
|
|
|
343
351
|
// Resize handler
|
|
344
|
-
process.stdout.on('resize', () => { clampScroll(state); render(state); });
|
|
352
|
+
process.stdout.on('resize', () => { clampScroll(state); render(state, installedSet); });
|
|
345
353
|
|
|
346
354
|
// Keep alive
|
|
347
355
|
return new Promise(resolve => {
|