@imdeadpool/guardex 7.0.5 → 7.0.6

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/README.md CHANGED
@@ -373,6 +373,10 @@ npm pack --dry-run
373
373
 
374
374
  ## Release notes
375
375
 
376
+ ### v7.0.6
377
+
378
+ - **Fixed: self-updater lied about success.** `gx`'s update prompt runs `npm i -g @imdeadpool/guardex@latest` and previously trusted npm's exit code. When npm's resolution cache made it report "changed 1 package" without actually overwriting the files (a known quirk triggered when the user just bumped from N-1 → N in the same session, or with a warm metadata cache), the prompt kept re-firing on every subsequent `gx` invocation because the on-disk `package.json` was still stale. `gx` now re-reads the globally installed `package.json` after the `@latest` install returns, compares its `version` field to the advertised latest, and if they don't match runs a pinned retry `npm i -g @imdeadpool/guardex@<latest>` to force the cache past the obstructing entry. If the pinned retry also fails to advance the on-disk version, the user gets a clear hint (`npm root -g && npm cache verify`) instead of a silent loop.
379
+
376
380
  ### v7.0.5
377
381
 
378
382
  - **Added: `oh-my-claude` to `gx status` global-toolchain check.** The Claude-side mirror of `oh-my-codex` is now reported alongside the existing services (`oh-my-codex`, `@fission-ai/openspec`, `@imdeadpool/codex-account-switcher`, `gh`). Users who have not yet installed it will see a clear "inactive" line instead of silent omission, matching the existing codex detection contract.
@@ -3265,9 +3265,71 @@ function maybeSelfUpdateBeforeStatus() {
3265
3265
  return;
3266
3266
  }
3267
3267
 
3268
+ // Verify the install actually advanced the on-disk version. npm sometimes
3269
+ // reports "changed 1 package" with status 0 while leaving the old files
3270
+ // in place (version resolution cache / dedupe quirks). If the installed
3271
+ // version doesn't match check.latest, retry with the pinned version so
3272
+ // npm bypasses whatever heuristic made it skip the upgrade.
3273
+ const postInstallVersion = readInstalledGuardexVersion();
3274
+ if (postInstallVersion != null && postInstallVersion !== check.latest) {
3275
+ console.log(
3276
+ `[${TOOL_NAME}] Installed version is still ${postInstallVersion} (expected ${check.latest}). ` +
3277
+ `Retrying with pinned version ${check.latest}…`,
3278
+ );
3279
+ const pinnedResult = run(
3280
+ NPM_BIN,
3281
+ ['i', '-g', `${packageJson.name}@${check.latest}`],
3282
+ { stdio: 'inherit' },
3283
+ );
3284
+ if (pinnedResult.status !== 0) {
3285
+ console.log(
3286
+ `[${TOOL_NAME}] ⚠️ Pinned retry failed. Run manually: ${NPM_BIN} i -g ${packageJson.name}@${check.latest}`,
3287
+ );
3288
+ return;
3289
+ }
3290
+ const pinnedVersion = readInstalledGuardexVersion();
3291
+ if (pinnedVersion != null && pinnedVersion !== check.latest) {
3292
+ console.log(
3293
+ `[${TOOL_NAME}] ⚠️ On-disk version still ${pinnedVersion} after pinned retry. ` +
3294
+ `Investigate: ${NPM_BIN} root -g && ${NPM_BIN} cache verify`,
3295
+ );
3296
+ return;
3297
+ }
3298
+ }
3299
+
3268
3300
  console.log(`[${TOOL_NAME}] ✅ Updated to latest published version.`);
3269
3301
  }
3270
3302
 
3303
+ function readInstalledGuardexVersion() {
3304
+ // Resolves the globally-installed package's on-disk version so we can
3305
+ // verify npm actually wrote new bytes. Uses `npm root -g` to locate the
3306
+ // global install root so we don't accidentally read the running source
3307
+ // tree (which is the file the CLI was spawned from — that IS the global
3308
+ // copy in the normal case, but a bump should be visible via a fresh read
3309
+ // either way). Returns null if we can't determine it.
3310
+ try {
3311
+ const rootResult = run(NPM_BIN, ['root', '-g'], { timeout: 5000 });
3312
+ if (rootResult.status !== 0) {
3313
+ return null;
3314
+ }
3315
+ const globalRoot = String(rootResult.stdout || '').trim();
3316
+ if (!globalRoot) {
3317
+ return null;
3318
+ }
3319
+ const installedPkgPath = path.join(globalRoot, packageJson.name, 'package.json');
3320
+ if (!fs.existsSync(installedPkgPath)) {
3321
+ return null;
3322
+ }
3323
+ const parsed = JSON.parse(fs.readFileSync(installedPkgPath, 'utf8'));
3324
+ if (parsed && typeof parsed.version === 'string') {
3325
+ return parsed.version;
3326
+ }
3327
+ } catch (error) {
3328
+ return null;
3329
+ }
3330
+ return null;
3331
+ }
3332
+
3271
3333
  function checkForOpenSpecPackageUpdate() {
3272
3334
  if (envFlagEnabled('GUARDEX_SKIP_OPENSPEC_UPDATE_CHECK')) {
3273
3335
  return { checked: false, reason: 'disabled' };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imdeadpool/guardex",
3
- "version": "7.0.5",
3
+ "version": "7.0.6",
4
4
  "description": "GuardeX: the Guardian T-Rex for your repo, with hardened multi-agent git guardrails.",
5
5
  "license": "MIT",
6
6
  "preferGlobal": true,