@shnitzel/plugscout 0.3.11 → 0.3.12

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');
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shnitzel/plugscout",
3
- "version": "0.3.11",
3
+ "version": "0.3.12",
4
4
  "description": "Claude plugins + Claude connectors + Copilot extensions + Skills + MCP security intelligence framework",
5
5
  "private": false,
6
6
  "type": "module",