* fix(#3130): harden update.md npx invocations against cache-stale and token-routing Two failure modes with the old form: 1. Cache-stale: npx serves a cached older version (no --package= flag) 2. Token-routing: Bash-tool wrapper misroutes @ token in package@tag spec All three sibling invocations (local/global/unknown) now use: npx -y --package=get-shit-done-cc@latest -- get-shit-done-cc $ARGS --package= forces a fresh registry fetch; -- prevents token misrouting. Also fixes the manual-update hint in the error-exit block. Regression test: tests/bug-3130-update-npx-robust-invocation.test.cjs Suite: 6973/6973 pass. Closes #3130. * fix(lint): allow-test-rule for update.md structural contract test
This commit is contained in:
5
.changeset/fix-3130-update-npx-robust.md
Normal file
5
.changeset/fix-3130-update-npx-robust.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
type: Fixed
|
||||
pr: 3130
|
||||
---
|
||||
**`update.md` npx invocations hardened against cache-stale and Bash-tool token-routing failures** — the previous `npx -y get-shit-done-cc@latest` form had two failure modes: (1) npx serving a cached older version instead of `@latest`, and (2) Bash-tool wrappers misrouting the `@` token, producing `Unknown command: "get-shit-done-cc@latest"`. All three sibling invocations (local, global, unknown/fallback) now use `npx -y --package=get-shit-done-cc@latest -- get-shit-done-cc` — the `--package=` flag forces a fresh registry fetch and the `--` separator prevents token misrouting. Closes #3130.
|
||||
@@ -320,7 +320,7 @@ fi
|
||||
```text
|
||||
Couldn't check for updates (reason: {LATEST_REASON}, exit: {LATEST_STATUS}).
|
||||
|
||||
To update manually: `npx get-shit-done-cc --global`
|
||||
To update manually: `npx -y --package=get-shit-done-cc@latest -- get-shit-done-cc --global`
|
||||
```
|
||||
|
||||
Exit.
|
||||
@@ -521,17 +521,17 @@ RUNTIME_FLAG="--$TARGET_RUNTIME"
|
||||
|
||||
**If LOCAL install:**
|
||||
```bash
|
||||
npx -y get-shit-done-cc@latest "$RUNTIME_FLAG" --local
|
||||
npx -y --package=get-shit-done-cc@latest -- get-shit-done-cc "$RUNTIME_FLAG" --local
|
||||
```
|
||||
|
||||
**If GLOBAL install:**
|
||||
```bash
|
||||
npx -y get-shit-done-cc@latest "$RUNTIME_FLAG" --global
|
||||
npx -y --package=get-shit-done-cc@latest -- get-shit-done-cc "$RUNTIME_FLAG" --global
|
||||
```
|
||||
|
||||
**If UNKNOWN install:**
|
||||
```bash
|
||||
npx -y get-shit-done-cc@latest --claude --global
|
||||
npx -y --package=get-shit-done-cc@latest -- get-shit-done-cc --claude --global
|
||||
```
|
||||
|
||||
Capture output. If install fails, show error and exit.
|
||||
|
||||
48
tests/bug-3130-update-npx-robust-invocation.test.cjs
Normal file
48
tests/bug-3130-update-npx-robust-invocation.test.cjs
Normal file
@@ -0,0 +1,48 @@
|
||||
'use strict';
|
||||
// allow-test-rule: reads product workflow markdown (update.md) to verify structural invocation contract — not a source-grep test
|
||||
|
||||
// Regression guard for bug #3130.
|
||||
//
|
||||
// Two failure modes were observed with the pre-fix npx invocation form:
|
||||
// 1. Cache-stale: bare `npx -y get-shit-done-cc@latest` hits npx's local
|
||||
// cache and may pull an older version instead of @latest.
|
||||
// 2. Token-routing: Bash-tool wrappers misroute the `@` token in
|
||||
// `get-shit-done-cc@latest`, causing npm to error with
|
||||
// "Unknown command: get-shit-done-cc@latest".
|
||||
//
|
||||
// The robust form is:
|
||||
// npx -y --package=get-shit-done-cc@latest -- get-shit-done-cc $ARGS
|
||||
//
|
||||
// `--package=` forces a fresh registry fetch, bypassing the npx cache.
|
||||
// `--` clearly delineates npx flags from the run-command, preventing
|
||||
// Bash-tool @-token misrouting.
|
||||
|
||||
const { test } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const ROOT = path.join(__dirname, '..');
|
||||
const UPDATE_WF = path.join(ROOT, 'get-shit-done', 'workflows', 'update.md');
|
||||
|
||||
const src = fs.readFileSync(UPDATE_WF, 'utf8');
|
||||
|
||||
test('bug #3130: update.md contains no bare npx invocations (cache-stale form)', () => {
|
||||
// Any occurrence of `npx -y get-shit-done-cc@latest` without `--package=`
|
||||
// is the stale form that triggers the two failure modes.
|
||||
const stale = (src.match(/npx -y get-shit-done-cc@latest[^\n]*/g) || []);
|
||||
assert.deepEqual(
|
||||
stale,
|
||||
[],
|
||||
`Stale npx forms found in update.md (must use --package= form): ${stale.join('; ')}`,
|
||||
);
|
||||
});
|
||||
|
||||
test('bug #3130: update.md has >=3 robust npx invocations (--package= + -- separator)', () => {
|
||||
// Three sibling invocations: local, global, and unknown/fallback.
|
||||
const robust = (src.match(/npx -y --package=get-shit-done-cc@latest -- get-shit-done-cc/g) || []);
|
||||
assert.ok(
|
||||
robust.length >= 3,
|
||||
`Expected >=3 robust npx invocations in update.md, found ${robust.length}`,
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user