@shnitzel/plugscout 0.3.11 → 0.3.13
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.
|
@@ -30,7 +30,7 @@ import { hasFlag, readCsvList, readFlag, readKinds, readLimit, readSort } from '
|
|
|
30
30
|
import { renderHomeScreen, renderInteractiveHome } from './ui/home.js';
|
|
31
31
|
import { handleMcp } from './mcp.js';
|
|
32
32
|
import { writeWebReport } from './ui/web-report.js';
|
|
33
|
-
import { checkForUpdateNow, maybeNotifyAboutUpdate, RELEASE_DOWNLOAD_URL } from './update-check.js';
|
|
33
|
+
import { applyUpdate, checkForUpdateNow, maybeNotifyAboutUpdate, RELEASE_DOWNLOAD_URL } from './update-check.js';
|
|
34
34
|
const COMMAND_ALIASES = {
|
|
35
35
|
home: 'home',
|
|
36
36
|
about: 'about',
|
|
@@ -891,12 +891,34 @@ async function handleClient(args) {
|
|
|
891
891
|
}
|
|
892
892
|
async function handleUpgrade(args) {
|
|
893
893
|
const subcommand = args[0] ?? 'check';
|
|
894
|
+
if (subcommand === 'apply') {
|
|
895
|
+
const result = await applyUpdate();
|
|
896
|
+
renderApplyResult(result);
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
894
899
|
if (subcommand !== 'check') {
|
|
895
|
-
throw new Error('Usage: upgrade check');
|
|
900
|
+
throw new Error('Usage: upgrade check | upgrade apply');
|
|
896
901
|
}
|
|
897
902
|
const result = await checkForUpdateNow();
|
|
898
903
|
renderUpgradeResult(result);
|
|
899
904
|
}
|
|
905
|
+
function renderApplyResult(result) {
|
|
906
|
+
if (result.status === 'upgraded') {
|
|
907
|
+
console.log(`PlugScout upgraded: v${result.fromVersion} -> v${result.toVersion}`);
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
if (result.status === 'already-latest') {
|
|
911
|
+
console.log(`PlugScout is already up to date (v${result.currentVersion}).`);
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
if (result.status === 'no-release') {
|
|
915
|
+
console.log('No published release found yet.');
|
|
916
|
+
console.log(`Releases: ${RELEASE_DOWNLOAD_URL}`);
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
console.log(`Upgrade failed: ${result.detail}`);
|
|
920
|
+
console.log(`Manual install: npm install -g @shnitzel/plugscout@latest`);
|
|
921
|
+
}
|
|
900
922
|
function renderUpgradeResult(result) {
|
|
901
923
|
if (result.status === 'no-release') {
|
|
902
924
|
console.log('No published release found yet.');
|
|
@@ -915,7 +937,7 @@ function renderUpgradeResult(result) {
|
|
|
915
937
|
return;
|
|
916
938
|
}
|
|
917
939
|
console.log(`New PlugScout version available: v${result.currentVersion} -> v${result.latestVersion}`);
|
|
918
|
-
console.log(`Download: ${RELEASE_DOWNLOAD_URL}`);
|
|
940
|
+
console.log(`Upgrade: plugscout upgrade apply | Download: ${RELEASE_DOWNLOAD_URL}`);
|
|
919
941
|
}
|
|
920
942
|
function sortRecommendations(recommendations, sort) {
|
|
921
943
|
const sorted = [...recommendations];
|
|
@@ -1043,6 +1065,7 @@ function printHelp() {
|
|
|
1043
1065
|
console.log(' web [--out .plugscout/report.html] [--kind ...] [--limit n] [--open]');
|
|
1044
1066
|
console.log(' client setup --client cursor|gemini|claude-desktop|windsurf|opencode|zed [--scope user|project] [--force]');
|
|
1045
1067
|
console.log(' upgrade check');
|
|
1068
|
+
console.log(' upgrade apply auto-install latest version via npm');
|
|
1046
1069
|
console.log(' help');
|
|
1047
1070
|
console.log('');
|
|
1048
1071
|
console.log('Kind aliases');
|
|
@@ -192,24 +192,27 @@ export async function renderInteractiveHome() {
|
|
|
192
192
|
const CTRL_C = '\u0003';
|
|
193
193
|
function render(firstRender) {
|
|
194
194
|
if (!firstRender) {
|
|
195
|
-
|
|
195
|
+
// Restore saved cursor position and clear everything below it.
|
|
196
|
+
// This avoids line-count arithmetic that breaks when descriptions wrap.
|
|
197
|
+
process.stdout.write('\x1b[u\x1b[0J');
|
|
196
198
|
}
|
|
197
199
|
for (let i = 0; i < menuItems.length; i++) {
|
|
198
200
|
const item = menuItems[i];
|
|
199
201
|
const prefix = i === selected ? ' \u276f ' : ' ';
|
|
200
|
-
process.stdout.write(
|
|
202
|
+
process.stdout.write(`${prefix}${item.label}\n`);
|
|
201
203
|
if (item.description) {
|
|
202
204
|
const firstLine = item.description.split('\n')[0];
|
|
203
|
-
process.stdout.write(
|
|
205
|
+
process.stdout.write(` \x1b[2m${firstLine}\x1b[0m\n`);
|
|
204
206
|
}
|
|
205
207
|
else {
|
|
206
|
-
process.stdout.write(
|
|
208
|
+
process.stdout.write('\n');
|
|
207
209
|
}
|
|
208
210
|
}
|
|
209
211
|
}
|
|
210
212
|
let running = true;
|
|
211
213
|
while (running) {
|
|
212
214
|
process.stdout.write('\n');
|
|
215
|
+
process.stdout.write('\x1b[s'); // save cursor — used by render(false) to redraw in-place
|
|
213
216
|
process.stdin.setRawMode(true);
|
|
214
217
|
process.stdin.resume();
|
|
215
218
|
process.stdin.setEncoding('utf8');
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
2
3
|
import semver from 'semver';
|
|
3
4
|
import { readJsonFile, writeJsonFile } from '../../lib/json.js';
|
|
4
5
|
import { getPackagePath, getStatePath } from '../../lib/paths.js';
|
|
@@ -80,7 +81,7 @@ export async function maybeNotifyAboutUpdate(options = {}) {
|
|
|
80
81
|
return;
|
|
81
82
|
}
|
|
82
83
|
console.log(`New PlugScout version available: v${currentVersion} -> v${state.latestVersion}`);
|
|
83
|
-
console.log(`Download: ${RELEASE_DOWNLOAD_URL}`);
|
|
84
|
+
console.log(`Upgrade: plugscout upgrade apply | Download: ${RELEASE_DOWNLOAD_URL}`);
|
|
84
85
|
await saveUpdateCheckState({
|
|
85
86
|
...state,
|
|
86
87
|
source: 'github-releases',
|
|
@@ -113,6 +114,32 @@ export async function checkForUpdateNow() {
|
|
|
113
114
|
}
|
|
114
115
|
return { status: 'up-to-date', currentVersion, latestVersion: result.latestVersion };
|
|
115
116
|
}
|
|
117
|
+
export async function applyUpdate() {
|
|
118
|
+
const currentVersion = await loadCurrentVersion();
|
|
119
|
+
const release = await lookupLatestReleaseVersion();
|
|
120
|
+
if (release.status === 'error') {
|
|
121
|
+
return { status: 'error', currentVersion, detail: 'Could not reach GitHub releases API.' };
|
|
122
|
+
}
|
|
123
|
+
if (release.status === 'no-release') {
|
|
124
|
+
return { status: 'no-release', currentVersion };
|
|
125
|
+
}
|
|
126
|
+
if (!isVersionNewer(release.latestVersion, currentVersion)) {
|
|
127
|
+
return { status: 'already-latest', currentVersion, latestVersion: release.latestVersion };
|
|
128
|
+
}
|
|
129
|
+
const pkg = '@shnitzel/plugscout@latest';
|
|
130
|
+
const result = spawnSync('npm', ['install', '-g', pkg], { stdio: 'inherit' });
|
|
131
|
+
if (result.status !== 0) {
|
|
132
|
+
return { status: 'error', currentVersion, detail: `npm install -g ${pkg} exited with ${result.status ?? 'signal'}` };
|
|
133
|
+
}
|
|
134
|
+
await saveUpdateCheckState({
|
|
135
|
+
...(await loadUpdateCheckState()),
|
|
136
|
+
source: 'github-releases',
|
|
137
|
+
lastCheckedAt: new Date().toISOString(),
|
|
138
|
+
latestVersion: release.latestVersion,
|
|
139
|
+
lastNotifiedVersion: release.latestVersion
|
|
140
|
+
});
|
|
141
|
+
return { status: 'upgraded', fromVersion: currentVersion, toVersion: release.latestVersion };
|
|
142
|
+
}
|
|
116
143
|
async function lookupLatestReleaseVersion() {
|
|
117
144
|
const controller = new AbortController();
|
|
118
145
|
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
package/package.json
CHANGED