claude-code-marketplace 0.4.3 → 0.5.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-marketplace",
3
- "version": "0.4.3",
3
+ "version": "0.5.0",
4
4
  "description": "Web UI for browsing and managing Claude Code marketplace plugins",
5
5
  "main": "server.js",
6
6
  "bin": {
package/public/app.js CHANGED
@@ -476,6 +476,7 @@ async function showDetail(pluginId) {
476
476
  <div class="detail-section">
477
477
  <p class="detail-desc">${esc(plugin.description || 'No description')}</p>
478
478
  ${metaRow}
479
+ ${renderPluginMetadata(plugin)}
479
480
  </div>
480
481
  ${scopeSection}
481
482
  <div class="detail-section">
@@ -524,6 +525,42 @@ function renderScopeMatrix(plugin) {
524
525
  .join('')}</div>`;
525
526
  }
526
527
 
528
+ function shortUrl(url) {
529
+ return url
530
+ .replace(/^https?:\/\/(www\.)?/, '')
531
+ .replace(/\.git$/, '')
532
+ .replace(/\/$/, '');
533
+ }
534
+
535
+ function renderPluginMetadata(plugin) {
536
+ const meta = plugin.metadata || {};
537
+ const chips = [];
538
+ if (meta.category) chips.push(`<span class="meta-tag">${esc(meta.category)}</span>`);
539
+ if (meta.author) {
540
+ const name = typeof meta.author === 'object' ? meta.author.name : meta.author;
541
+ if (name) chips.push(`<span class="meta-chip">${esc(name)}</span>`);
542
+ }
543
+ if (meta.tags?.length) {
544
+ for (const t of meta.tags) chips.push(`<span class="meta-tag">${esc(t)}</span>`);
545
+ }
546
+ const links = [];
547
+ if (meta.homepage) {
548
+ links.push(
549
+ `<a class="meta-link" href="${esc(meta.homepage)}" target="_blank" rel="noopener">${esc(shortUrl(meta.homepage))}</a>`,
550
+ );
551
+ }
552
+ if (plugin.source && typeof plugin.source === 'string' && plugin.source.startsWith('http')) {
553
+ const href = plugin.source.replace(/\.git$/, '');
554
+ if (!meta.homepage || !plugin.source.includes(meta.homepage)) {
555
+ links.push(
556
+ `<a class="meta-link" href="${esc(href)}" target="_blank" rel="noopener">${esc(shortUrl(plugin.source))}</a>`,
557
+ );
558
+ }
559
+ }
560
+ if (!chips.length && !links.length) return '';
561
+ return `<div class="plugin-meta-bar">${chips.join('')}${links.join('')}</div>`;
562
+ }
563
+
527
564
  function renderDetailComponents(pluginId, comps, hasDirAccess) {
528
565
  const configFiles = comps._configFiles || {};
529
566
  const entries = Object.entries(comps).filter(
@@ -1025,7 +1062,11 @@ function filterPlugins(plugins) {
1025
1062
  }
1026
1063
  if (searchFilter) {
1027
1064
  result = result.filter(
1028
- (p) => p.name.toLowerCase().includes(searchFilter) || (p.description || '').toLowerCase().includes(searchFilter),
1065
+ (p) =>
1066
+ p.name.toLowerCase().includes(searchFilter) ||
1067
+ (p.description || '').toLowerCase().includes(searchFilter) ||
1068
+ (p.metadata?.category || '').toLowerCase().includes(searchFilter) ||
1069
+ (p.metadata?.tags || []).some((t) => t.toLowerCase().includes(searchFilter)),
1029
1070
  );
1030
1071
  }
1031
1072
  return result;
package/public/style.css CHANGED
@@ -781,6 +781,39 @@ body.light .scope-toggle.local {
781
781
  letter-spacing: 0.03em;
782
782
  }
783
783
 
784
+ .plugin-meta-bar {
785
+ display: flex;
786
+ align-items: center;
787
+ gap: 6px;
788
+ flex-wrap: wrap;
789
+ margin-top: 8px;
790
+ }
791
+ .meta-tag {
792
+ font-size: 9px;
793
+ padding: 2px 8px;
794
+ border-radius: 10px;
795
+ border: 1px solid var(--border);
796
+ color: var(--text-muted);
797
+ letter-spacing: 0.02em;
798
+ }
799
+ .meta-chip {
800
+ font-size: 10px;
801
+ color: var(--text-muted);
802
+ }
803
+ .meta-link {
804
+ font-size: 10px;
805
+ color: var(--text-muted);
806
+ text-decoration: none;
807
+ overflow: hidden;
808
+ text-overflow: ellipsis;
809
+ white-space: nowrap;
810
+ max-width: 260px;
811
+ }
812
+ .meta-link:hover {
813
+ color: var(--accent);
814
+ text-decoration: underline;
815
+ }
816
+
784
817
  /* === SCOPE MATRIX === */
785
818
 
786
819
  .scope-matrix {
package/server.js CHANGED
@@ -218,11 +218,22 @@ function loadMarketplaces() {
218
218
  const srcDir = path.resolve(installLocation, rawSource);
219
219
  if (fs.existsSync(srcDir)) originDir = srcDir;
220
220
  }
221
+ if (!originDir && typeof rawSource === 'object' && rawSource?.path) {
222
+ const srcDir = path.resolve(installLocation, rawSource.path);
223
+ if (fs.existsSync(srcDir)) originDir = srcDir;
224
+ }
221
225
  if (!originDir) {
222
226
  const pluginSubdir = path.join(installLocation, 'plugins', pd.name);
223
227
  if (fs.existsSync(pluginSubdir)) originDir = pluginSubdir;
224
228
  else if ((mData.plugins || []).length === 1) originDir = installLocation;
225
229
  }
230
+ if (!originDir) {
231
+ const cacheDir = path.join(PLUGINS_DIR, 'cache', name, pd.name);
232
+ if (fs.existsSync(cacheDir)) {
233
+ const latest = findLatestVersionDir(cacheDir);
234
+ if (latest) originDir = latest;
235
+ }
236
+ }
226
237
  }
227
238
 
228
239
  // Resolve plugin dir for filesystem-based component counts
@@ -250,11 +261,13 @@ function loadMarketplaces() {
250
261
  .find(d => d?.version && d.version !== 'unknown')?.version || null;
251
262
 
252
263
  let availableVersion = pd.version || null;
253
- if (!availableVersion && installLocation) {
254
- const sourceDir = pd.source || `plugins/${pd.name}`;
255
- const pluginJson = path.join(installLocation, typeof sourceDir === 'string' ? sourceDir : pd.name, '.claude-plugin', 'plugin.json');
256
- const pjData = readJsonSafe(pluginJson);
257
- if (pjData?.version) availableVersion = pjData.version;
264
+ if (!availableVersion) {
265
+ const versionDir = pluginDir || originDir;
266
+ if (versionDir) {
267
+ const pluginJson = path.join(versionDir, '.claude-plugin', 'plugin.json');
268
+ const pjData = readJsonSafe(pluginJson);
269
+ if (pjData?.version) availableVersion = pjData.version;
270
+ }
258
271
  }
259
272
  const hasUpdate = isInstalled && semverNewer(availableVersion, installedVersion);
260
273