diff --git a/docs/INVENTORY.md b/docs/INVENTORY.md index 211c7d70..1a8bf039 100644 --- a/docs/INVENTORY.md +++ b/docs/INVENTORY.md @@ -173,7 +173,7 @@ Full roster at `commands/gsd/*.md`. The groupings below mirror `docs/COMMANDS.md --- -## Workflows (80 shipped) +## Workflows (81 shipped) Full roster at `get-shit-done/workflows/*.md`. Workflows are thin orchestrators that commands reference internally; most are not read directly by end users. Rows below map each workflow file to its role (derived from the `` block) and, where applicable, to the command that invokes it. diff --git a/get-shit-done/workflows/update.md b/get-shit-done/workflows/update.md index 94a873b2..d4712d3f 100644 --- a/get-shit-done/workflows/update.md +++ b/get-shit-done/workflows/update.md @@ -396,7 +396,7 @@ First, resolve the config directory (`RUNTIME_DIR`) from the install scope detected in `get_installed_version`: ```bash -# RUNTIME_DIR is the resolved config directory (e.g. ~/.claude, ~/.config/opencode) +# RUNTIME_DIR is the resolved config directory (e.g. ~/.config/opencode, ~/.gemini) # It should already be set from get_installed_version as GLOBAL_DIR or LOCAL_DIR. # Use the appropriate variable based on INSTALL_SCOPE. if [ "$INSTALL_SCOPE" = "LOCAL" ]; then diff --git a/tests/bug-2470-update-md-claude-path.test.cjs b/tests/bug-2470-update-md-claude-path.test.cjs new file mode 100644 index 00000000..0475f694 --- /dev/null +++ b/tests/bug-2470-update-md-claude-path.test.cjs @@ -0,0 +1,36 @@ +'use strict'; + +/** + * Regression test for #2470. + * + * update.md is installed into every runtime directory including .gemini, .codex, + * .opencode, etc. The installer's scanForLeakedPaths() uses the regex + * /(?:~|\$HOME)\/\.claude\b/g to detect unresolved .claude path references after + * copyWithPathReplacement() runs. The replacer handles "~/.claude/" (trailing slash) + * but not "~/.claude" (bare, no trailing slash) — so any bare reference in + * update.md would slip through and trigger the installer warning for non-Claude runtimes. + */ + +const { test, describe } = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('fs'); +const path = require('path'); + +const UPDATE_MD = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'update.md'); + +describe('update.md — no bare ~.claude path references (#2470)', () => { + const content = fs.readFileSync(UPDATE_MD, 'utf-8'); + + test('update.md does not contain bare ~/\\.claude (without trailing slash)', () => { + // This is the exact pattern from the installer's scanForLeakedPaths(): + // /(?:~|\$HOME)\/\.claude\b/g + // The replacer handles ~/\.claude\/ (with trailing slash) but misses bare ~/\.claude + // so we must not have bare references in the source file. + const matches = content.match(/(?:~|\$HOME)\/\.claude(?!\/)/g); + assert.strictEqual( + matches, + null, + `update.md must not contain bare ~/\.claude (without trailing slash) — installer scanner flags these as unresolved path refs: ${JSON.stringify(matches)}` + ); + }); +});