promptgraph-mcp 2.2.9 → 2.3.1
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 +5 -4
- package/index.js +39 -1
- package/package.json +1 -1
- package/tui.js +42 -4
package/github-import.js
CHANGED
|
@@ -62,8 +62,8 @@ function sparseClone(url, dest, subdir) {
|
|
|
62
62
|
git(['sparse-checkout', 'init', '--cone'], dest, 'pipe');
|
|
63
63
|
git(['sparse-checkout', 'set', subdir], dest, 'pipe');
|
|
64
64
|
|
|
65
|
-
// 3. fetch + checkout (depth=1)
|
|
66
|
-
const fetch = git(['fetch', '--depth=1', 'origin'], dest);
|
|
65
|
+
// 3. fetch + checkout (depth=1, skip large blobs)
|
|
66
|
+
const fetch = git(['fetch', '--depth=1', '--filter=blob:none', 'origin'], dest);
|
|
67
67
|
if (fetch.status !== 0) return false;
|
|
68
68
|
|
|
69
69
|
// Try HEAD, then main, then master
|
|
@@ -90,16 +90,17 @@ function sparseUpdate(dest, subdir) {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
// Fallback: full clone (when no subdir found)
|
|
93
|
+
// --filter=blob:none skips large binaries (images, zips) — only fetches text files
|
|
93
94
|
function fullClone(url, dest) {
|
|
94
95
|
if (fs.existsSync(dest)) {
|
|
95
|
-
const fetch = git(['fetch', '--depth=1', 'origin'], dest);
|
|
96
|
+
const fetch = git(['fetch', '--depth=1', '--filter=blob:none', 'origin'], dest);
|
|
96
97
|
if (fetch.status !== 0) return false;
|
|
97
98
|
for (const ref of ['origin/HEAD', 'origin/main', 'origin/master']) {
|
|
98
99
|
if (git(['reset', '--hard', ref], dest, 'pipe').status === 0) return true;
|
|
99
100
|
}
|
|
100
101
|
return false;
|
|
101
102
|
}
|
|
102
|
-
return git(['clone', '--depth=1', url, dest]).status === 0;
|
|
103
|
+
return git(['clone', '--depth=1', '--filter=blob:none', url, dest]).status === 0;
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
// After clone: detect actual skills dir on disk
|
package/index.js
CHANGED
|
@@ -244,6 +244,9 @@ if (args[0] === 'marketplace') {
|
|
|
244
244
|
} catch {}
|
|
245
245
|
|
|
246
246
|
const { runTUI } = await import('./tui.js');
|
|
247
|
+
const { loadConfig: _lcR, saveConfig: _scR, SKILLS_STORE_DIR: _ssR } = await import('./config.js');
|
|
248
|
+
const { getDb: _getDbR } = await import('./db.js');
|
|
249
|
+
|
|
247
250
|
await runTUI(
|
|
248
251
|
Array.isArray(skills) ? skills : [],
|
|
249
252
|
Array.isArray(bundles) ? bundles : [],
|
|
@@ -259,7 +262,42 @@ if (args[0] === 'marketplace') {
|
|
|
259
262
|
if (item.code) installedSet.add(item.code);
|
|
260
263
|
}
|
|
261
264
|
},
|
|
262
|
-
installedSet
|
|
265
|
+
installedSet,
|
|
266
|
+
async (item) => {
|
|
267
|
+
const cfg = _lcR();
|
|
268
|
+
const db = _getDbR();
|
|
269
|
+
if (item.type === 'bundle' && item.repo_url) {
|
|
270
|
+
// Remove cloned directory
|
|
271
|
+
const owner = item.repo_url.split('/')[0];
|
|
272
|
+
const repo = item.repo_url.split('/')[1];
|
|
273
|
+
const clonedName = `${owner}-${repo}`;
|
|
274
|
+
const clonedDir = path.join(_ssR, 'github', clonedName);
|
|
275
|
+
if (fs.existsSync(clonedDir)) fs.rmSync(clonedDir, { recursive: true, force: true });
|
|
276
|
+
// Remove from config + DB
|
|
277
|
+
const src = `github:${clonedName}`;
|
|
278
|
+
cfg.sources = cfg.sources.filter(s => s.source !== src && !s.dir.startsWith(clonedDir));
|
|
279
|
+
_scR(cfg);
|
|
280
|
+
db.prepare('DELETE FROM skills WHERE source = ?').run(src);
|
|
281
|
+
db.prepare('DELETE FROM chunks WHERE skill_id NOT IN (SELECT id FROM skills)').run();
|
|
282
|
+
} else if (item.type === 'bundle') {
|
|
283
|
+
// skill-list bundle — remove each skill file
|
|
284
|
+
const mktDir = path.join(_ssR, 'marketplace');
|
|
285
|
+
for (const sid of (item.skills || [])) {
|
|
286
|
+
const row = db.prepare('SELECT path FROM skills WHERE id = ?').get(sid);
|
|
287
|
+
if (row?.path && fs.existsSync(row.path)) fs.unlinkSync(row.path);
|
|
288
|
+
db.prepare('DELETE FROM skills WHERE id = ?').run(sid);
|
|
289
|
+
db.prepare('DELETE FROM chunks WHERE skill_id = ?').run(sid);
|
|
290
|
+
}
|
|
291
|
+
} else {
|
|
292
|
+
// individual skill
|
|
293
|
+
const row = db.prepare('SELECT path FROM skills WHERE id = ?').get(item.id);
|
|
294
|
+
if (row?.path && fs.existsSync(row.path)) fs.unlinkSync(row.path);
|
|
295
|
+
db.prepare('DELETE FROM skills WHERE id = ?').run(item.id);
|
|
296
|
+
db.prepare('DELETE FROM chunks WHERE skill_id = ?').run(item.id);
|
|
297
|
+
}
|
|
298
|
+
installedSet.delete(item.id);
|
|
299
|
+
if (item.code) installedSet.delete(item.code);
|
|
300
|
+
}
|
|
263
301
|
);
|
|
264
302
|
process.exit(0);
|
|
265
303
|
}
|
package/package.json
CHANGED
package/tui.js
CHANGED
|
@@ -171,17 +171,23 @@ function render(state, installedSet = new Set()) {
|
|
|
171
171
|
// ── footer ─────────────────────────────────────────────────────────────────
|
|
172
172
|
write(dim('─'.repeat(cols)) + CLEAR_EOL + '\n');
|
|
173
173
|
const sel = items[cursor];
|
|
174
|
-
if (sel && !searching) {
|
|
174
|
+
if (sel && !searching && !state.confirming) {
|
|
175
175
|
const isInst = installedSet.has(sel.id) || (sel.code && installedSet.has(sel.code));
|
|
176
176
|
const installCmd = sel.type === 'bundle' ? `bundle install ${sel.id}` : `install ${sel.code || sel.id}`;
|
|
177
|
-
const instLabel = isInst
|
|
177
|
+
const instLabel = isInst
|
|
178
|
+
? green(' ✓ installed') + dim(' ') + dim('d') + chalk.red(' remove') + dim(' ')
|
|
179
|
+
: dim(' Enter') + chalk.white(' install') + dim(' ');
|
|
178
180
|
write(instLabel + dim('Tab') + ' switch ' + dim('/') + ' search ' + dim('q') + ' quit' + CLEAR_EOL + '\n');
|
|
179
181
|
write(dim(` → pg ${installCmd}`) + CLEAR_EOL + '\n');
|
|
182
|
+
} else if (state.confirming) {
|
|
183
|
+
write(chalk.red.bold(' Remove ') + chalk.white(state.confirming.name) + chalk.red('? ') +
|
|
184
|
+
chalk.white.bold('[y]') + chalk.gray('es ') + chalk.white.bold('[n]') + chalk.gray('o') + CLEAR_EOL + '\n');
|
|
185
|
+
write(CLEAR_EOL + '\n');
|
|
180
186
|
} else if (searching) {
|
|
181
187
|
write(dim(' Type to filter ') + cyan('Enter') + dim(' confirm ') + cyan('Esc') + dim(' cancel') + CLEAR_EOL + '\n');
|
|
182
188
|
write(CLEAR_EOL + '\n');
|
|
183
189
|
} else {
|
|
184
|
-
write(dim(' ↑↓ navigate Enter install Tab switch / search q quit') + CLEAR_EOL + '\n');
|
|
190
|
+
write(dim(' ↑↓ navigate Enter install d remove Tab switch / search q quit') + CLEAR_EOL + '\n');
|
|
185
191
|
write(CLEAR_EOL + '\n');
|
|
186
192
|
}
|
|
187
193
|
}
|
|
@@ -224,13 +230,14 @@ function clampScroll(state) {
|
|
|
224
230
|
|
|
225
231
|
// ── main ─────────────────────────────────────────────────────────────────────
|
|
226
232
|
|
|
227
|
-
export async function runTUI(allSkills, allBundles, installFn, installedSet = new Set()) {
|
|
233
|
+
export async function runTUI(allSkills, allBundles, installFn, installedSet = new Set(), removeFn = async () => {}) {
|
|
228
234
|
const allItems = buildItems(allSkills, allBundles);
|
|
229
235
|
|
|
230
236
|
const state = {
|
|
231
237
|
tab: 'all',
|
|
232
238
|
query: '',
|
|
233
239
|
searching: false,
|
|
240
|
+
confirming: null, // { id, name, type, repoUrl }
|
|
234
241
|
cursor: 0,
|
|
235
242
|
scroll: 0,
|
|
236
243
|
items: allItems,
|
|
@@ -293,6 +300,27 @@ export async function runTUI(allSkills, allBundles, installFn, installedSet = ne
|
|
|
293
300
|
return;
|
|
294
301
|
}
|
|
295
302
|
|
|
303
|
+
// Confirm-delete mode
|
|
304
|
+
if (state.confirming) {
|
|
305
|
+
if (ch === 'y' || ch === 'Y') {
|
|
306
|
+
const item = state.confirming;
|
|
307
|
+
state.confirming = null;
|
|
308
|
+
setStatus(null, `Removing ${item.name}…`);
|
|
309
|
+
try {
|
|
310
|
+
await removeFn(item);
|
|
311
|
+
installedSet.delete(item.id);
|
|
312
|
+
if (item.code) installedSet.delete(item.code);
|
|
313
|
+
setStatus(true, `Removed ${item.name}`);
|
|
314
|
+
} catch (e) {
|
|
315
|
+
setStatus(false, e.message.slice(0, 60));
|
|
316
|
+
}
|
|
317
|
+
} else {
|
|
318
|
+
state.confirming = null;
|
|
319
|
+
render(state, installedSet);
|
|
320
|
+
}
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
|
|
296
324
|
// Normal mode
|
|
297
325
|
if (key.name === 'q' || (key.ctrl && key.name === 'c')) {
|
|
298
326
|
cleanup();
|
|
@@ -345,6 +373,16 @@ export async function runTUI(allSkills, allBundles, installFn, installedSet = ne
|
|
|
345
373
|
if (key.name === 'home') { state.cursor = 0; state.scroll = 0; render(state, installedSet); return; }
|
|
346
374
|
if (key.name === 'end') { state.cursor = state.items.length - 1; clampScroll(state); render(state, installedSet); return; }
|
|
347
375
|
|
|
376
|
+
if (ch === 'd' || ch === 'D') {
|
|
377
|
+
const sel = state.items[state.cursor];
|
|
378
|
+
if (!sel) return;
|
|
379
|
+
const isInst = installedSet.has(sel.id) || (sel.code && installedSet.has(sel.code));
|
|
380
|
+
if (!isInst) { setStatus(false, 'Not installed'); return; }
|
|
381
|
+
state.confirming = { id: sel.id, name: sel.name, type: sel.type, code: sel.code, repo_url: sel.repo_url };
|
|
382
|
+
render(state, installedSet);
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
|
|
348
386
|
if (key.name === 'return' || key.name === 'i') {
|
|
349
387
|
const sel = state.items[state.cursor];
|
|
350
388
|
if (!sel || state.installing) return;
|