promptgraph-mcp 2.9.9 → 2.9.11

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/README.md CHANGED
@@ -9,6 +9,33 @@ Instead of loading every `.md` skill into context, Claude calls `pg_search` and
9
9
 
10
10
  ---
11
11
 
12
+ ## Installation
13
+
14
+ **Requirements:** Node.js 18+ — [nodejs.org](https://nodejs.org)
15
+
16
+ ```bash
17
+ npm install -g promptgraph-mcp@latest
18
+ ```
19
+
20
+ Check version:
21
+ ```bash
22
+ pg --version
23
+ ```
24
+
25
+ Update to latest:
26
+ ```bash
27
+ pg update
28
+ # or
29
+ npm install -g promptgraph-mcp@latest
30
+ ```
31
+
32
+ Uninstall:
33
+ ```bash
34
+ npm uninstall -g promptgraph-mcp
35
+ ```
36
+
37
+ ---
38
+
12
39
  ## Quick Start
13
40
 
14
41
  ```bash
package/index.js CHANGED
@@ -18,7 +18,7 @@ const args = process.argv.slice(2);
18
18
  const rawBin = process.argv[1]?.split(/[\\/]/).pop()?.replace(/\.js$/, '');
19
19
  const bin = (rawBin && rawBin !== 'index') ? rawBin : 'pg';
20
20
 
21
- const KNOWN_COMMANDS = new Set(['init', 'reindex', 'update', 'import', 'install', 'setup', 'validate', 'marketplace', 'doctor', 'search', 'help', '--help', '-h', 'bundle', 'status', 'train']);
21
+ const KNOWN_COMMANDS = new Set(['init', 'reindex', 'update', 'import', 'install', 'setup', 'validate', 'marketplace', 'doctor', 'search', 'help', '--help', '-h', '--version', '-v', 'version', 'bundle', 'status', 'train']);
22
22
 
23
23
  function showHelp() {
24
24
  console.log(
@@ -57,6 +57,14 @@ if (args[0] === 'help' || args[0] === '--help' || args[0] === '-h') {
57
57
  process.exit(0);
58
58
  }
59
59
 
60
+ // Version flag
61
+ if (args[0] === '--version' || args[0] === '-v' || args[0] === 'version') {
62
+ const { createRequire } = await import('module');
63
+ const _pkg = createRequire(import.meta.url)('./package.json');
64
+ console.log(_pkg.version);
65
+ process.exit(0);
66
+ }
67
+
60
68
  // No args: if launched from an interactive terminal, show help.
61
69
  // If stdin is a pipe (i.e. an MCP client like Claude), fall through and
62
70
  // start the server — NEVER print to stdout here, it corrupts JSON-RPC.
package/marketplace.js CHANGED
@@ -359,9 +359,21 @@ async function _findBundle(bundleId) {
359
359
  const text = await fetchText(REGISTRY_URL);
360
360
  const registry = JSON.parse(text);
361
361
  const q = String(bundleId).trim().toLowerCase();
362
+ const valid = (registry.bundles || []).filter(b => validateRegistryEntry(b).ok);
362
363
  const validSkills = (registry.skills || []).filter(s => validateRegistryEntry(s).ok);
363
- const bundle = (registry.bundles || []).filter(b => validateRegistryEntry(b).ok).find(b =>
364
- (b.code || codeFor(b.id)).toLowerCase() === q || b.id?.toLowerCase() === q || b.name?.toLowerCase() === q
364
+ // 1. Exact match (code, id, name)
365
+ let bundle = valid.find(b =>
366
+ (b.code || codeFor(b.id)).toLowerCase() === q ||
367
+ b.id?.toLowerCase() === q ||
368
+ b.name?.toLowerCase() === q
369
+ );
370
+ // 2. Partial name/id match (starts with)
371
+ if (!bundle) bundle = valid.find(b =>
372
+ b.name?.toLowerCase().startsWith(q) || b.id?.toLowerCase().startsWith(q)
373
+ );
374
+ // 3. Contains match
375
+ if (!bundle) bundle = valid.find(b =>
376
+ b.name?.toLowerCase().includes(q) || b.id?.toLowerCase().includes(q)
365
377
  );
366
378
  if (!bundle) return { error: `No bundle matching "${bundleId}"` };
367
379
  return { bundle, validSkills };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "promptgraph-mcp",
3
- "version": "2.9.9",
3
+ "version": "2.9.11",
4
4
  "files": [
5
5
  "*.js",
6
6
  "commands/",
package/tui.js CHANGED
@@ -46,13 +46,11 @@ function truncate(s, n) {
46
46
 
47
47
  function buildItems(skills, bundles) {
48
48
  const items = [];
49
- // skills
50
49
  for (const s of skills) {
51
- items.push({ type: 'skill', id: s.id, name: s.name || s.id, description: s.description || '', category: s.category || 'Community', tags: s.tags || [], stars: s.stars || 0, code: s.code });
50
+ items.push({ type: 'skill', id: s.id, name: s.name || s.id, description: s.description || '', category: s.category || 'Community', tags: s.tags || [], stars: s.stars || 0, code: s.code, created_at: s.created_at || null });
52
51
  }
53
- // bundles
54
52
  for (const b of bundles) {
55
- items.push({ type: 'bundle', id: b.id, name: b.name || b.id, description: b.description || '', category: b.category || 'Community', tags: b.tags || [], stars: b.stars || 0, skillCount: b.skillCount, repo_url: b.repo_url, skills: b.skills, created_at: b.created_at });
53
+ items.push({ type: 'bundle', id: b.id, name: b.name || b.id, description: b.description || '', category: b.category || 'Community', tags: b.tags || [], stars: b.stars || 0, skillCount: b.skillCount, repo_url: b.repo_url, skills: b.skills, created_at: b.created_at || null });
56
54
  }
57
55
  return items;
58
56
  }
@@ -61,6 +59,11 @@ function filterItems(items, query, tab) {
61
59
  let filtered = items;
62
60
  if (tab === 'skills') filtered = items.filter(i => i.type === 'skill');
63
61
  if (tab === 'bundles') filtered = items.filter(i => i.type === 'bundle');
62
+ if (tab === 'recent') {
63
+ filtered = items
64
+ .filter(i => i.created_at)
65
+ .sort((a, b) => (b.created_at > a.created_at ? 1 : b.created_at < a.created_at ? -1 : 0));
66
+ }
64
67
  if (query) {
65
68
  const q = query.toLowerCase();
66
69
  filtered = filtered.filter(i =>
@@ -84,7 +87,7 @@ function render(state, installedSet = new Set()) {
84
87
  const DATE_W = 8;
85
88
  const DESC_W = cols - NAME_W - 28 - DATE_W;
86
89
 
87
- const { items, cursor, scroll, query, searching, tab, status } = state;
90
+ const { items, allItems, cursor, scroll, query, searching, tab, status } = state;
88
91
  const skills = items.filter(i => i.type === 'skill').length;
89
92
  const bundles = items.filter(i => i.type === 'bundle').length;
90
93
 
@@ -93,7 +96,7 @@ function render(state, installedSet = new Set()) {
93
96
  // ── header ─────────────────────────────────────────────────────────────────
94
97
  // Row 1: title bar
95
98
  const titleText = ' ◆ PromptGraph Marketplace';
96
- const tabParts = ['all', 'skills', 'bundles'].map(t =>
99
+ const tabParts = ['all', 'skills', 'bundles', 'recent'].map(t =>
97
100
  t === tab
98
101
  ? `\x1b[48;2;124;58;237m\x1b[97m ${t.toUpperCase()} \x1b[0m`
99
102
  : dim(` ${t} `)
@@ -102,9 +105,12 @@ function render(state, installedSet = new Set()) {
102
105
  write(titleLine + CLEAR_EOL + '\n');
103
106
 
104
107
  // Row 2: counts
108
+ const recentCount = (allItems || items).filter(i => i.created_at).length;
105
109
  const countLine = dim(' ') +
106
- (tab !== 'bundles' ? chalk.white(`${skills} skills`) + dim(' ') : '') +
107
- (tab !== 'skills' ? chalk.blue(`${bundles} bundles`) : '') +
110
+ (tab === 'recent'
111
+ ? chalk.yellow(`${items.length} recent`)
112
+ : (tab !== 'bundles' ? chalk.white(`${skills} skills`) + dim(' ') : '') +
113
+ (tab !== 'skills' ? chalk.blue(`${bundles} bundles`) : '')) +
108
114
  (query ? dim(' · filter: ') + cyan(query) : '');
109
115
  write(countLine + CLEAR_EOL + '\n');
110
116
 
@@ -137,8 +143,8 @@ function render(state, installedSet = new Set()) {
137
143
  const bg = selected ? '\x1b[48;2;55;35;110m' : '';
138
144
  const reset = '\x1b[0m';
139
145
 
140
- // category header (only when ungrouped / mixed)
141
- if (item.category !== lastCat) {
146
+ // category header (skip in recent tab — sorted by date, not category)
147
+ if (tab !== 'recent' && item.category !== lastCat) {
142
148
  if (rendered >= LIST_ROWS) break;
143
149
  const icon = CAT_ICON[item.category] || '📦';
144
150
  write((selected ? bg : '') + ' ' + purple.bold(icon + ' ' + item.category) + reset + CLEAR_EOL + '\n');
@@ -185,7 +191,7 @@ function render(state, installedSet = new Set()) {
185
191
  const instLabel = isInst
186
192
  ? green(' ✓ installed') + dim(' ') + dim('d') + chalk.red(' remove') + dim(' ')
187
193
  : dim(' Enter') + chalk.white(' install') + dim(' ');
188
- write(instLabel + dim('Tab') + ' switch ' + dim('/') + ' search ' + dim('q') + ' quit' + CLEAR_EOL + '\n');
194
+ write(instLabel + dim('Tab') + ' all/skills/bundles/recent ' + dim('/') + ' search ' + dim('q') + ' quit' + CLEAR_EOL + '\n');
189
195
  const ghUrl = sel.repo_url ? chalk.hex('#3B82F6')(` ↗ github.com/${sel.repo_url}`) : '';
190
196
  write(dim(` → pg ${installCmd}`) + ghUrl + CLEAR_EOL + '\n');
191
197
  } else if (state.confirming) {
@@ -250,6 +256,7 @@ export async function runTUI(allSkills, allBundles, installFn, installedSet = ne
250
256
  cursor: 0,
251
257
  scroll: 0,
252
258
  items: allItems,
259
+ allItems,
253
260
  status: null,
254
261
  };
255
262
 
@@ -342,7 +349,7 @@ export async function runTUI(allSkills, allBundles, installFn, installedSet = ne
342
349
  }
343
350
 
344
351
  if (key.name === 'tab') {
345
- const tabs = ['all', 'skills', 'bundles'];
352
+ const tabs = ['all', 'skills', 'bundles', 'recent'];
346
353
  state.tab = tabs[(tabs.indexOf(state.tab) + 1) % tabs.length];
347
354
  state.cursor = 0;
348
355
  state.scroll = 0;