promptgraph-mcp 2.1.6 → 2.1.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.
Files changed (3) hide show
  1. package/index.js +38 -1
  2. package/package.json +1 -1
  3. package/tui.js +14 -6
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "promptgraph-mcp",
3
- "version": "2.1.6",
3
+ "version": "2.1.7",
4
4
  "main": "index.js",
5
5
  "type": "module",
6
6
  "bin": {
package/tui.js CHANGED
@@ -143,17 +143,21 @@ function render(state) {
143
143
  }
144
144
 
145
145
  // item row
146
- const arrow = selected ? cyan('') : ' ';
147
- const type = item.type === 'bundle' ? blue('⊞') : dim('·');
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 = item.type === 'bundle'
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
- write(dim(` Enter`) + ' install ' + dim('Tab') + ' switch ' + dim('/') + ' search ' + dim('q') + ' quit' + CLEAR_EOL + '\n');
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 = {