@shnitzel/plugscout 0.3.22 → 0.3.24
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.
|
@@ -1143,79 +1143,106 @@ function describeRiskPosture(posture) {
|
|
|
1143
1143
|
async function promptResultBrowser(entries) {
|
|
1144
1144
|
if (!process.stdout.isTTY || entries.length === 0)
|
|
1145
1145
|
return;
|
|
1146
|
-
const WINDOW = 8;
|
|
1147
1146
|
let selected = 0;
|
|
1148
|
-
let linesDrawn = 0;
|
|
1149
1147
|
const ENTER = '\r';
|
|
1150
1148
|
const CTRL_C = '';
|
|
1151
1149
|
const Q = 'q';
|
|
1152
|
-
|
|
1153
|
-
function
|
|
1154
|
-
function visibleStart() {
|
|
1155
|
-
return Math.max(0, Math.min(selected - Math.floor(WINDOW / 2), entries.length - WINDOW));
|
|
1156
|
-
}
|
|
1157
|
-
function renderBrowser(firstRender) {
|
|
1150
|
+
// Single-line navigator — no second table. The rendered results above are the reference.
|
|
1151
|
+
function renderNavigator(firstRender) {
|
|
1158
1152
|
if (!firstRender) {
|
|
1159
|
-
moveCursor(process.stdout, 0, -
|
|
1153
|
+
moveCursor(process.stdout, 0, -1);
|
|
1160
1154
|
clearScreenDown(process.stdout);
|
|
1161
1155
|
}
|
|
1162
|
-
|
|
1163
|
-
const
|
|
1164
|
-
|
|
1165
|
-
for (let i = start; i < end; i++) {
|
|
1166
|
-
const prefix = i === selected ? ' ❯ ' : ' ';
|
|
1167
|
-
const nameSuffix = entries[i].name ? ` ${entries[i].name}` : '';
|
|
1168
|
-
const line = `${prefix}${entries[i].id}${nameSuffix}`;
|
|
1169
|
-
process.stdout.write(`${line}\n`);
|
|
1170
|
-
drawn += 1;
|
|
1171
|
-
}
|
|
1172
|
-
if (entries.length > WINDOW) {
|
|
1173
|
-
process.stdout.write(`\x1b[2m (${selected + 1}/${entries.length})\x1b[0m\n`);
|
|
1174
|
-
drawn += 1;
|
|
1175
|
-
}
|
|
1176
|
-
linesDrawn = drawn;
|
|
1156
|
+
const entry = entries[selected];
|
|
1157
|
+
const label = entry.name ? `${entry.id} \x1b[90m${entry.name}\x1b[0m` : entry.id;
|
|
1158
|
+
process.stdout.write(` \x1b[36m❯\x1b[0m ${label} \x1b[90m(${selected + 1}/${entries.length})\x1b[0m\n`);
|
|
1177
1159
|
}
|
|
1178
1160
|
// eslint-disable-next-line prefer-const
|
|
1179
1161
|
let running = true;
|
|
1180
1162
|
let firstLoop = true;
|
|
1181
1163
|
while (running) {
|
|
1182
1164
|
if (firstLoop) {
|
|
1183
|
-
process.stdout.write('\x1b[
|
|
1165
|
+
process.stdout.write('\x1b[90m ↑↓ navigate ⏎ inspect q/Esc skip\x1b[0m\n');
|
|
1184
1166
|
firstLoop = false;
|
|
1185
1167
|
}
|
|
1186
1168
|
else {
|
|
1187
|
-
process.stdout.write('\
|
|
1169
|
+
process.stdout.write('\x1b[90m ↑↓ navigate ⏎ inspect another q/Esc done\x1b[0m\n');
|
|
1188
1170
|
}
|
|
1189
|
-
linesDrawn = 0;
|
|
1190
1171
|
process.stdin.setRawMode(true);
|
|
1191
1172
|
process.stdin.resume();
|
|
1192
1173
|
process.stdin.setEncoding('utf8');
|
|
1193
|
-
|
|
1174
|
+
renderNavigator(true);
|
|
1194
1175
|
const action = await new Promise((resolve) => {
|
|
1195
|
-
|
|
1176
|
+
let escTimer = null;
|
|
1177
|
+
let pendingEsc = false;
|
|
1178
|
+
function doExit() {
|
|
1179
|
+
if (escTimer) {
|
|
1180
|
+
clearTimeout(escTimer);
|
|
1181
|
+
escTimer = null;
|
|
1182
|
+
}
|
|
1183
|
+
process.stdin.removeListener('data', onKey);
|
|
1184
|
+
process.stdin.setRawMode(false);
|
|
1185
|
+
process.stdin.pause();
|
|
1186
|
+
process.stdout.write('\n');
|
|
1187
|
+
resolve({ exit: true });
|
|
1188
|
+
}
|
|
1189
|
+
function onKey(key) {
|
|
1190
|
+
// Handle second byte of a split escape sequence (e.g. \x1b then [A)
|
|
1191
|
+
if (pendingEsc) {
|
|
1192
|
+
pendingEsc = false;
|
|
1193
|
+
if (escTimer) {
|
|
1194
|
+
clearTimeout(escTimer);
|
|
1195
|
+
escTimer = null;
|
|
1196
|
+
}
|
|
1197
|
+
if (key === '[A') {
|
|
1198
|
+
selected = (selected - 1 + entries.length) % entries.length;
|
|
1199
|
+
renderNavigator(false);
|
|
1200
|
+
return;
|
|
1201
|
+
}
|
|
1202
|
+
if (key === '[B') {
|
|
1203
|
+
selected = (selected + 1) % entries.length;
|
|
1204
|
+
renderNavigator(false);
|
|
1205
|
+
return;
|
|
1206
|
+
}
|
|
1207
|
+
// Standalone Esc followed by something unexpected — still exit
|
|
1208
|
+
doExit();
|
|
1209
|
+
return;
|
|
1210
|
+
}
|
|
1196
1211
|
if (key === CTRL_C || key === Q) {
|
|
1197
|
-
|
|
1198
|
-
process.stdin.setRawMode(false);
|
|
1199
|
-
process.stdin.pause();
|
|
1200
|
-
process.stdout.write('\n');
|
|
1201
|
-
resolve({ exit: true });
|
|
1212
|
+
doExit();
|
|
1202
1213
|
}
|
|
1203
|
-
else if (
|
|
1214
|
+
else if (key === '\x1b[A' || key === '\x1bOA') {
|
|
1215
|
+
// Single-chunk arrow up (most terminals)
|
|
1204
1216
|
selected = (selected - 1 + entries.length) % entries.length;
|
|
1205
|
-
|
|
1217
|
+
renderNavigator(false);
|
|
1206
1218
|
}
|
|
1207
|
-
else if (
|
|
1219
|
+
else if (key === '\x1b[B' || key === '\x1bOB') {
|
|
1220
|
+
// Single-chunk arrow down (most terminals)
|
|
1208
1221
|
selected = (selected + 1) % entries.length;
|
|
1209
|
-
|
|
1222
|
+
renderNavigator(false);
|
|
1223
|
+
}
|
|
1224
|
+
else if (key === '\x1b') {
|
|
1225
|
+
// Could be standalone Esc or first byte of split arrow sequence
|
|
1226
|
+
pendingEsc = true;
|
|
1227
|
+
escTimer = setTimeout(() => {
|
|
1228
|
+
pendingEsc = false;
|
|
1229
|
+
escTimer = null;
|
|
1230
|
+
doExit();
|
|
1231
|
+
}, 40);
|
|
1210
1232
|
}
|
|
1211
1233
|
else if (key === ENTER) {
|
|
1234
|
+
if (escTimer) {
|
|
1235
|
+
clearTimeout(escTimer);
|
|
1236
|
+
escTimer = null;
|
|
1237
|
+
}
|
|
1212
1238
|
process.stdin.removeListener('data', onKey);
|
|
1213
1239
|
process.stdin.setRawMode(false);
|
|
1214
1240
|
process.stdin.pause();
|
|
1215
1241
|
process.stdout.write('\n');
|
|
1216
1242
|
resolve({ exit: false, id: entries[selected].id });
|
|
1217
1243
|
}
|
|
1218
|
-
}
|
|
1244
|
+
}
|
|
1245
|
+
process.stdin.on('data', onKey);
|
|
1219
1246
|
});
|
|
1220
1247
|
if (action.exit) {
|
|
1221
1248
|
running = false;
|
|
@@ -39,7 +39,7 @@ export async function renderHomeScreen() {
|
|
|
39
39
|
'plugscout sync --dry-run',
|
|
40
40
|
'plugscout help',
|
|
41
41
|
]) {
|
|
42
|
-
lines.push(` ${colorIfTty(cmd, colors.
|
|
42
|
+
lines.push(` ${colorIfTty(cmd, colors.cyan)}`);
|
|
43
43
|
}
|
|
44
44
|
lines.push('');
|
|
45
45
|
lines.push(colorIfTty('Examples', colors.bold));
|
|
@@ -49,7 +49,7 @@ export async function renderHomeScreen() {
|
|
|
49
49
|
'plugscout search github',
|
|
50
50
|
'plugscout show --id claude-connector:asana',
|
|
51
51
|
]) {
|
|
52
|
-
lines.push(` ${colorIfTty(cmd, colors.
|
|
52
|
+
lines.push(` ${colorIfTty(cmd, colors.cyan)}`);
|
|
53
53
|
}
|
|
54
54
|
lines.push('');
|
|
55
55
|
lines.push(colorIfTty('Kind aliases', colors.bold));
|
|
@@ -279,7 +279,7 @@ export async function renderInteractiveHome() {
|
|
|
279
279
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
280
280
|
process.stdin.resume();
|
|
281
281
|
const id = await new Promise((res) => {
|
|
282
|
-
rl.question('
|
|
282
|
+
rl.question(' Catalog ID (e.g. mcp:github, skill:code-review, cursor-extension:gitlens): ', (answer) => {
|
|
283
283
|
rl.close();
|
|
284
284
|
res(answer.trim());
|
|
285
285
|
});
|
package/package.json
CHANGED