promptgraph-mcp 2.2.5 → 2.2.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/index.js +21 -18
- package/package.json +1 -1
- package/tui.js +26 -5
package/index.js
CHANGED
|
@@ -208,35 +208,38 @@ if (args[0] === 'marketplace') {
|
|
|
208
208
|
|
|
209
209
|
if (skills?.error) { error(skills.error); process.exit(1); }
|
|
210
210
|
|
|
211
|
-
// Build installed set
|
|
211
|
+
// Build installed set — source of truth is the FILESYSTEM, not DB/config
|
|
212
212
|
const installedSet = new Set();
|
|
213
213
|
try {
|
|
214
214
|
const cfg = _lcMkt();
|
|
215
215
|
const db = _getDbMkt();
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
const dbSkillIds = new Set(db.prepare('SELECT id FROM skills').all().map(r => r.id));
|
|
219
|
-
|
|
220
|
-
// Build set of cloned repo names from config sources (exact: github:owner-repo)
|
|
221
|
-
const githubSources = new Set(
|
|
222
|
-
cfg.sources.filter(s => s.source.startsWith('github:')).map(s => s.source.replace('github:', '').toLowerCase())
|
|
223
|
-
);
|
|
216
|
+
const { SKILLS_STORE_DIR } = await import('./config.js');
|
|
217
|
+
const githubDir = path.join(SKILLS_STORE_DIR, 'github');
|
|
224
218
|
|
|
225
219
|
for (const b of (Array.isArray(bundles) ? bundles : [])) {
|
|
226
220
|
if (b.repo_url) {
|
|
227
|
-
//
|
|
228
|
-
const
|
|
229
|
-
|
|
221
|
+
// Check actual cloned directory exists and has files
|
|
222
|
+
const owner = b.repo_url.split('/')[0];
|
|
223
|
+
const repo = b.repo_url.split('/')[1];
|
|
224
|
+
const clonedName = `${owner}-${repo}`;
|
|
225
|
+
const clonedDir = path.join(githubDir, clonedName);
|
|
226
|
+
const dirExists = fs.existsSync(clonedDir) &&
|
|
227
|
+
fs.readdirSync(clonedDir).length > 0; // not empty
|
|
228
|
+
if (dirExists) installedSet.add(b.id);
|
|
230
229
|
} else if (Array.isArray(b.skills)) {
|
|
231
|
-
// skill-list bundle:
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
230
|
+
// skill-list bundle: check files exist on disk via DB path column
|
|
231
|
+
const allOnDisk = b.skills.every(sid => {
|
|
232
|
+
const row = db.prepare('SELECT path FROM skills WHERE id = ?').get(sid);
|
|
233
|
+
return row && fs.existsSync(row.path);
|
|
234
|
+
});
|
|
235
|
+
if (b.skills.length > 0 && allOnDisk) installedSet.add(b.id);
|
|
235
236
|
}
|
|
236
237
|
}
|
|
237
238
|
|
|
238
|
-
// Individual skills
|
|
239
|
-
for (const
|
|
239
|
+
// Individual marketplace skills — check file exists on disk
|
|
240
|
+
for (const row of db.prepare('SELECT id, path FROM skills WHERE source = ?').all('marketplace')) {
|
|
241
|
+
if (fs.existsSync(row.path)) installedSet.add(row.id);
|
|
242
|
+
}
|
|
240
243
|
} catch {}
|
|
241
244
|
|
|
242
245
|
const { runTUI } = await import('./tui.js');
|
package/package.json
CHANGED
package/tui.js
CHANGED
|
@@ -153,9 +153,9 @@ function render(state, installedSet = new Set()) {
|
|
|
153
153
|
? item.skillCount
|
|
154
154
|
? blue((item.skillCount + ' sk').padEnd(8))
|
|
155
155
|
: item.repo_url
|
|
156
|
-
?
|
|
156
|
+
? chalk.hex('#3B82F6')('↗ GitHub')
|
|
157
157
|
: dim(((item.skills?.length || 0) + ' sk').padEnd(8))
|
|
158
|
-
:
|
|
158
|
+
: chalk.hex('#A78BFA')((item.code || '').padEnd(10));
|
|
159
159
|
const desc = dim(truncate(item.description, Math.max(10, DESC_W)));
|
|
160
160
|
|
|
161
161
|
write(bg + ` ${arrow} ${type} ${badge} ${nameCol} ${extra} ${desc}` + reset + CLEAR_EOL + '\n');
|
|
@@ -188,16 +188,37 @@ function render(state, installedSet = new Set()) {
|
|
|
188
188
|
|
|
189
189
|
// ── clamp scroll ─────────────────────────────────────────────────────────────
|
|
190
190
|
|
|
191
|
+
// Count how many screen rows items[start..end] occupy (including category headers)
|
|
192
|
+
function countVisibleRows(items, start, end) {
|
|
193
|
+
let rows = 0;
|
|
194
|
+
let lastCat = start > 0 ? items[start - 1]?.category : null;
|
|
195
|
+
for (let i = start; i < end && i < items.length; i++) {
|
|
196
|
+
if (items[i].category !== lastCat) { rows++; lastCat = items[i].category; }
|
|
197
|
+
rows++;
|
|
198
|
+
}
|
|
199
|
+
return rows;
|
|
200
|
+
}
|
|
201
|
+
|
|
191
202
|
function clampScroll(state) {
|
|
192
203
|
const { rows } = termSize();
|
|
193
204
|
const HEADER_ROWS = 4, FOOTER_ROWS = 3;
|
|
194
205
|
const LIST_ROWS = rows - HEADER_ROWS - FOOTER_ROWS;
|
|
195
206
|
const { cursor, items } = state;
|
|
196
207
|
|
|
197
|
-
// ensure cursor visible — approximate (category headers add extra rows)
|
|
198
|
-
if (cursor < state.scroll) state.scroll = cursor;
|
|
199
|
-
if (cursor >= state.scroll + LIST_ROWS - 2) state.scroll = cursor - LIST_ROWS + 3;
|
|
200
208
|
if (state.scroll < 0) state.scroll = 0;
|
|
209
|
+
if (state.scroll > cursor) state.scroll = cursor;
|
|
210
|
+
|
|
211
|
+
// Check if cursor is visible from current scroll
|
|
212
|
+
const visibleRows = countVisibleRows(items, state.scroll, cursor + 1);
|
|
213
|
+
if (visibleRows > LIST_ROWS - 1) {
|
|
214
|
+
// Cursor is below visible area — scroll forward until it fits
|
|
215
|
+
while (state.scroll < cursor) {
|
|
216
|
+
state.scroll++;
|
|
217
|
+
const v = countVisibleRows(items, state.scroll, cursor + 1);
|
|
218
|
+
if (v <= LIST_ROWS - 1) break;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
201
222
|
if (state.scroll >= items.length) state.scroll = Math.max(0, items.length - 1);
|
|
202
223
|
}
|
|
203
224
|
|