diff --git a/.changeset/fix-3130-update-npx-robust.md b/.changeset/fix-3130-update-npx-robust.md new file mode 100644 index 000000000..0fc8827eb --- /dev/null +++ b/.changeset/fix-3130-update-npx-robust.md @@ -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. diff --git a/get-shit-done/workflows/update.md b/get-shit-done/workflows/update.md index 5a14cb199..6ff794b23 100644 --- a/get-shit-done/workflows/update.md +++ b/get-shit-done/workflows/update.md @@ -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. diff --git a/tests/bug-3130-update-npx-robust-invocation.test.cjs b/tests/bug-3130-update-npx-robust-invocation.test.cjs new file mode 100644 index 000000000..6ca4807f1 --- /dev/null +++ b/tests/bug-3130-update-npx-robust-invocation.test.cjs @@ -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}`, + ); +});