promptgraph-mcp 2.1.5 → 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.
- package/index.js +38 -1
- package/package.json +1 -1
- package/tui.js +63 -35
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
|
@@ -75,65 +75,89 @@ function filterItems(items, query, tab) {
|
|
|
75
75
|
|
|
76
76
|
function render(state) {
|
|
77
77
|
const { cols, rows } = termSize();
|
|
78
|
-
const HEADER_ROWS =
|
|
78
|
+
const HEADER_ROWS = 5;
|
|
79
79
|
const FOOTER_ROWS = 3;
|
|
80
80
|
const LIST_ROWS = rows - HEADER_ROWS - FOOTER_ROWS;
|
|
81
|
+
const NAME_W = Math.max(20, Math.floor(cols * 0.28));
|
|
82
|
+
const DESC_W = cols - NAME_W - 28;
|
|
81
83
|
|
|
82
84
|
const { items, cursor, scroll, query, searching, tab, status } = state;
|
|
85
|
+
const skills = items.filter(i => i.type === 'skill').length;
|
|
86
|
+
const bundles = items.filter(i => i.type === 'bundle').length;
|
|
83
87
|
|
|
84
88
|
write(HOME);
|
|
85
89
|
|
|
86
90
|
// ── header ─────────────────────────────────────────────────────────────────
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
91
|
+
// Row 1: title bar
|
|
92
|
+
const titleText = ' ◆ PromptGraph Marketplace';
|
|
93
|
+
const tabParts = ['all', 'skills', 'bundles'].map(t =>
|
|
94
|
+
t === tab
|
|
95
|
+
? `\x1b[48;2;124;58;237m\x1b[97m ${t.toUpperCase()} \x1b[0m`
|
|
96
|
+
: dim(` ${t} `)
|
|
97
|
+
).join('');
|
|
98
|
+
const titleLine = purple.bold(titleText) + ' ' + tabParts;
|
|
99
|
+
write(titleLine + CLEAR_EOL + '\n');
|
|
100
|
+
|
|
101
|
+
// Row 2: counts
|
|
102
|
+
const countLine = dim(' ') +
|
|
103
|
+
(tab !== 'bundles' ? chalk.white(`${skills} skills`) + dim(' ') : '') +
|
|
104
|
+
(tab !== 'skills' ? chalk.blue(`${bundles} bundles`) : '') +
|
|
105
|
+
(query ? dim(' · filter: ') + cyan(query) : '');
|
|
106
|
+
write(countLine + CLEAR_EOL + '\n');
|
|
107
|
+
|
|
108
|
+
// Row 3: search bar
|
|
109
|
+
const searchLabel = searching ? green(' / ') : dim(' / ');
|
|
110
|
+
const cursor_blink = Math.floor(Date.now() / 500) % 2 ? '▌' : ' ';
|
|
111
|
+
const searchVal = searching
|
|
112
|
+
? white(query || '') + cursor_blink
|
|
113
|
+
: dim(query ? query : 'type / to search, Tab to switch view');
|
|
114
|
+
write(searchLabel + searchVal + CLEAR_EOL + '\n');
|
|
115
|
+
|
|
116
|
+
// Row 4: status / separator
|
|
117
|
+
if (status) {
|
|
118
|
+
const msg = status.ok ? green(' ✓ ' + status.msg) : red(' ✗ ' + status.msg);
|
|
119
|
+
write(msg + CLEAR_EOL + '\n');
|
|
120
|
+
} else {
|
|
121
|
+
write(dim('─'.repeat(cols)) + CLEAR_EOL + '\n');
|
|
122
|
+
}
|
|
101
123
|
write(dim('─'.repeat(cols)) + CLEAR_EOL + '\n');
|
|
102
124
|
|
|
103
125
|
// ── list ───────────────────────────────────────────────────────────────────
|
|
104
126
|
let lastCat = null;
|
|
105
|
-
let lineIdx = 0;
|
|
106
127
|
let rendered = 0;
|
|
107
128
|
|
|
108
129
|
for (let i = scroll; i < items.length && rendered < LIST_ROWS; i++) {
|
|
109
130
|
const item = items[i];
|
|
110
131
|
const selected = i === cursor;
|
|
111
|
-
const bg
|
|
112
|
-
const reset =
|
|
132
|
+
const bg = selected ? '\x1b[48;2;55;35;110m' : '';
|
|
133
|
+
const reset = '\x1b[0m';
|
|
113
134
|
|
|
114
|
-
// category header
|
|
135
|
+
// category header (only when ungrouped / mixed)
|
|
115
136
|
if (item.category !== lastCat) {
|
|
116
137
|
if (rendered >= LIST_ROWS) break;
|
|
117
138
|
const icon = CAT_ICON[item.category] || '📦';
|
|
118
|
-
write(bg
|
|
139
|
+
write((selected ? bg : '') + ' ' + purple.bold(icon + ' ' + item.category) + reset + CLEAR_EOL + '\n');
|
|
119
140
|
lastCat = item.category;
|
|
120
141
|
rendered++;
|
|
142
|
+
if (rendered >= LIST_ROWS) break;
|
|
121
143
|
}
|
|
122
144
|
|
|
123
|
-
if (rendered >= LIST_ROWS) break;
|
|
124
|
-
|
|
125
145
|
// item row
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
const
|
|
136
|
-
|
|
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('✓') : ' ';
|
|
152
|
+
const nameStr = truncate(item.name, NAME_W);
|
|
153
|
+
const namePad = nameStr.padEnd(NAME_W);
|
|
154
|
+
const nameCol = selected ? white.bold(namePad) : white(namePad);
|
|
155
|
+
const extra = item.type === 'bundle'
|
|
156
|
+
? (item.skillCount ? blue((item.skillCount + ' sk').padEnd(8)) : blue('GitHub '))
|
|
157
|
+
: dim((item.code || '').padEnd(8));
|
|
158
|
+
const desc = dim(truncate(item.description, Math.max(10, DESC_W)));
|
|
159
|
+
|
|
160
|
+
write(bg + ` ${arrow} ${type} ${badge} ${nameCol} ${extra} ${desc}` + reset + CLEAR_EOL + '\n');
|
|
137
161
|
rendered++;
|
|
138
162
|
}
|
|
139
163
|
|
|
@@ -147,8 +171,12 @@ function render(state) {
|
|
|
147
171
|
write(dim('─'.repeat(cols)) + CLEAR_EOL + '\n');
|
|
148
172
|
const sel = items[cursor];
|
|
149
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);
|
|
150
177
|
const installCmd = sel.type === 'bundle' ? `bundle install ${sel.id}` : `install ${sel.code || sel.id}`;
|
|
151
|
-
|
|
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');
|
|
152
180
|
write(dim(` → pg ${installCmd}`) + CLEAR_EOL + '\n');
|
|
153
181
|
} else if (searching) {
|
|
154
182
|
write(dim(' Type to filter ') + cyan('Enter') + dim(' confirm ') + cyan('Esc') + dim(' cancel') + CLEAR_EOL + '\n');
|
|
@@ -176,7 +204,7 @@ function clampScroll(state) {
|
|
|
176
204
|
|
|
177
205
|
// ── main ─────────────────────────────────────────────────────────────────────
|
|
178
206
|
|
|
179
|
-
export async function runTUI(allSkills, allBundles, installFn) {
|
|
207
|
+
export async function runTUI(allSkills, allBundles, installFn, installedSet = new Set()) {
|
|
180
208
|
const allItems = buildItems(allSkills, allBundles);
|
|
181
209
|
|
|
182
210
|
const state = {
|