@shnitzel/plugscout 0.3.27 → 0.3.29
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.
|
@@ -480,7 +480,7 @@ async function handleList(args) {
|
|
|
480
480
|
if (details) {
|
|
481
481
|
console.log(renderCatalogDecisionDetails(filtered, policy, insights));
|
|
482
482
|
}
|
|
483
|
-
await promptResultBrowser(filtered.map((e) => ({ id: e.item.id, name: e.item.name })));
|
|
483
|
+
await promptResultBrowser(filtered.map((e) => ({ id: e.item.id, name: e.item.name, url: buildItemLinks(e.item)[0]?.url })));
|
|
484
484
|
}
|
|
485
485
|
async function handleShow(args) {
|
|
486
486
|
const id = readFlag(args, '--id');
|
|
@@ -560,7 +560,7 @@ async function handleSearch(args) {
|
|
|
560
560
|
name: entry.item.name
|
|
561
561
|
}))));
|
|
562
562
|
printHint('Use `show --id <catalog-id>` for full detail.');
|
|
563
|
-
await promptResultBrowser(matches.map((e) => ({ id: e.item.id, name: e.item.name })));
|
|
563
|
+
await promptResultBrowser(matches.map((e) => ({ id: e.item.id, name: e.item.name, url: buildItemLinks(e.item)[0]?.url })));
|
|
564
564
|
}
|
|
565
565
|
async function handleExplain(args) {
|
|
566
566
|
const kinds = readKinds(args);
|
|
@@ -681,12 +681,16 @@ async function handleTop(args) {
|
|
|
681
681
|
printHint('Review each suggestion before installing. Do not install blindly from rank alone.');
|
|
682
682
|
printHint(`Risk scale (lower is safer): ${formatRiskScale(policy)}`);
|
|
683
683
|
printHint('Use `show --id <catalog-id>` or `assess --id <catalog-id>` for deep inspection.');
|
|
684
|
+
const topCatalogItems = await loadCatalogItems();
|
|
685
|
+
const topCatalogMap = new Map(topCatalogItems.map((item) => [item.id, item]));
|
|
684
686
|
if (details) {
|
|
685
|
-
const
|
|
686
|
-
|
|
687
|
-
console.log(renderRecommendationDecisionDetails(safe, catalogMap, policy, insights));
|
|
687
|
+
const insights = await loadItemInsights();
|
|
688
|
+
console.log(renderRecommendationDecisionDetails(safe, topCatalogMap, policy, insights));
|
|
688
689
|
}
|
|
689
|
-
await promptResultBrowser(safe.map((e) =>
|
|
690
|
+
await promptResultBrowser(safe.map((e) => {
|
|
691
|
+
const item = topCatalogMap.get(e.id);
|
|
692
|
+
return { id: e.id, name: '', url: item ? buildItemLinks(item)[0]?.url : undefined };
|
|
693
|
+
}));
|
|
690
694
|
}
|
|
691
695
|
async function handleSync(args) {
|
|
692
696
|
const kinds = readKinds(args);
|
|
@@ -787,7 +791,12 @@ async function handleRecommend(args) {
|
|
|
787
791
|
}
|
|
788
792
|
printHint('Next: run `show --id <catalog-id>` or `install --id <catalog-id> --yes`.');
|
|
789
793
|
if (format !== 'json') {
|
|
790
|
-
|
|
794
|
+
const recCatalogItems = await loadCatalogItems();
|
|
795
|
+
const recCatalogMap = new Map(recCatalogItems.map((item) => [item.id, item]));
|
|
796
|
+
await promptResultBrowser(ranked.map((e) => {
|
|
797
|
+
const item = recCatalogMap.get(e.id);
|
|
798
|
+
return { id: e.id, name: '', url: item ? buildItemLinks(item)[0]?.url : undefined };
|
|
799
|
+
}));
|
|
791
800
|
}
|
|
792
801
|
}
|
|
793
802
|
async function handleWeb(args) {
|
|
@@ -1173,15 +1182,16 @@ async function promptResultBrowser(entries) {
|
|
|
1173
1182
|
const ENTER = '\r';
|
|
1174
1183
|
const CTRL_C = '';
|
|
1175
1184
|
const Q = 'q';
|
|
1176
|
-
//
|
|
1185
|
+
// Two-line navigator: id/name line + url line (blank if none). Always writes 2 lines so cursor math is stable.
|
|
1177
1186
|
function renderNavigator(firstRender) {
|
|
1178
1187
|
if (!firstRender) {
|
|
1179
|
-
moveCursor(process.stdout, 0, -
|
|
1188
|
+
moveCursor(process.stdout, 0, -2);
|
|
1180
1189
|
clearScreenDown(process.stdout);
|
|
1181
1190
|
}
|
|
1182
1191
|
const entry = entries[selected];
|
|
1183
1192
|
const label = entry.name ? `${entry.id} \x1b[90m${entry.name}\x1b[0m` : entry.id;
|
|
1184
1193
|
process.stdout.write(` \x1b[36m❯\x1b[0m ${label} \x1b[90m(${selected + 1}/${entries.length})\x1b[0m\n`);
|
|
1194
|
+
process.stdout.write(entry.url ? ` \x1b[90m${entry.url}\x1b[0m\n` : '\n');
|
|
1185
1195
|
}
|
|
1186
1196
|
// eslint-disable-next-line prefer-const
|
|
1187
1197
|
let running = true;
|
|
@@ -1511,6 +1521,13 @@ function buildItemLinks(item) {
|
|
|
1511
1521
|
add('npm', `https://www.npmjs.com/package/${encodeURIComponent(pkg)}`);
|
|
1512
1522
|
}
|
|
1513
1523
|
}
|
|
1524
|
+
// MCP items: io.github.<owner>/<repo> ID pattern → derive GitHub URL
|
|
1525
|
+
if (item.kind === 'mcp' && item.id.startsWith('mcp:io.github.')) {
|
|
1526
|
+
const ownerRepo = item.id.slice('mcp:io.github.'.length);
|
|
1527
|
+
if (ownerRepo.includes('/')) {
|
|
1528
|
+
add('Repository', `https://github.com/${ownerRepo}`);
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1514
1531
|
// install.url is the most direct link for the user
|
|
1515
1532
|
add('Install page', item.install.url);
|
|
1516
1533
|
// Metadata links — priority order
|
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import { spawn } from 'node:child_process';
|
|
3
3
|
import { createInterface, moveCursor, clearScreenDown } from 'node:readline';
|
|
4
|
-
import { loadQuarantine, loadWhitelist } from '../../../catalog/repository.js';
|
|
5
|
-
import { getStaleRegistries, loadSyncState } from '../../../catalog/sync-state.js';
|
|
6
4
|
import { getPackagePath } from '../../../lib/paths.js';
|
|
7
5
|
import { colors } from '../formatters/colors.js';
|
|
8
6
|
import { isSetUp, loadCatalogItems } from '../../../api/index.js';
|
|
9
7
|
export async function renderHomeScreen() {
|
|
10
8
|
const termCols = process.stdout.columns ?? 80;
|
|
11
9
|
const useCompact = termCols < 82;
|
|
12
|
-
const [logo, pkg, catalogStats
|
|
10
|
+
const [logo, pkg, catalogStats] = await Promise.all([
|
|
13
11
|
readLogo(useCompact),
|
|
14
12
|
readPackageMeta(),
|
|
15
13
|
readCatalogStats(),
|
|
16
|
-
readRuntimeStats()
|
|
17
14
|
]);
|
|
18
15
|
const lines = [];
|
|
19
16
|
const version = pkg.version ?? '0.0.0';
|
|
@@ -29,7 +26,6 @@ export async function renderHomeScreen() {
|
|
|
29
26
|
lines.push(colorIfTty('Catalog', colors.bold));
|
|
30
27
|
lines.push(colorIfTty(` items=${catalogStats.items} skill=${catalogStats.skill} mcp=${catalogStats.mcp} claude-plugin=${catalogStats.claudePlugin} claude-connector=${catalogStats.claudeConnector}`, colors.dim));
|
|
31
28
|
lines.push(colorIfTty(` copilot-extension=${catalogStats.copilotExtension} cursor-extension=${catalogStats.cursorExtension} gemini-extension=${catalogStats.geminiExtension}`, colors.dim));
|
|
32
|
-
lines.push(colorIfTty(` stale-registries=${runtimeStats.staleRegistries} whitelist=${runtimeStats.whitelist} quarantined=${runtimeStats.quarantined}`, colors.dim));
|
|
33
29
|
lines.push('');
|
|
34
30
|
lines.push(colorIfTty('Quick actions', colors.bold));
|
|
35
31
|
for (const cmd of [
|
|
@@ -113,14 +109,6 @@ async function readCatalogStats() {
|
|
|
113
109
|
});
|
|
114
110
|
return { items: items.length, skill, mcp, claudePlugin, claudeConnector, copilotExtension, cursorExtension, geminiExtension };
|
|
115
111
|
}
|
|
116
|
-
async function readRuntimeStats() {
|
|
117
|
-
const [syncState, whitelist, quarantine] = await Promise.all([loadSyncState(), loadWhitelist(), loadQuarantine()]);
|
|
118
|
-
return {
|
|
119
|
-
staleRegistries: getStaleRegistries(syncState).length,
|
|
120
|
-
whitelist: whitelist.size,
|
|
121
|
-
quarantined: quarantine.length
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
112
|
function colorIfTty(value, apply) {
|
|
125
113
|
if (!process.stdout.isTTY || process.env.NO_COLOR === '1') {
|
|
126
114
|
return value;
|
|
@@ -585,6 +585,12 @@ function buildItemLinks(item) {
|
|
|
585
585
|
add('npm', `https://www.npmjs.com/package/${encodeURIComponent(pkg)}`);
|
|
586
586
|
}
|
|
587
587
|
}
|
|
588
|
+
if (item.kind === 'mcp' && item.id.startsWith('mcp:io.github.')) {
|
|
589
|
+
const ownerRepo = item.id.slice('mcp:io.github.'.length);
|
|
590
|
+
if (ownerRepo.includes('/')) {
|
|
591
|
+
add('Repository', `https://github.com/${ownerRepo}`);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
588
594
|
add('Install page', item.install.url);
|
|
589
595
|
add('Repository', meta.repositoryUrl);
|
|
590
596
|
add('Repository', meta.githubUrl);
|
package/package.json
CHANGED