From af7ab71f484f35e133eea4db14b6094afff4b4b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Pe=C3=B1a?= Date: Sun, 10 May 2026 02:50:20 -0600 Subject: [PATCH 1/2] feat(rules): add warn-bash-mutation-without-leading-cd (v1.11.0) New PreToolUse warn rule on Bash that fires when a state-mutating command (git commit/push/mv/rm/reset/merge/revert, sed -i, gh pr create/merge/edit) is invoked without a leading `cd /` prefix. ## Why this rule exists Real bug pattern observed 2026-05-10 (DOJ-4007 PR-2/PR-3 cross-ref fixup): When working across multiple parallel git worktrees in the same session, an agent ran a sed + commit + push chain WITHOUT explicit `cd` to the target worktree. The previous bash invocation left cwd in a different worktree (PR-2's), so the new commit + push (intended for PR-3) silently landed on PR-2 branch instead. Required revert on PR-2 + correct re-apply on PR-3 + confused commit history. This warn rule surfaces that pattern at the Bash boundary so the agent gets a heads-up before committing to the wrong branch. ## Scope - Triggers on: `git commit/push/mv/rm/reset/merge/revert`, `sed -i`, `gh pr create/merge/edit` - Skips on: cd/pushd at command start (legitimate worktree-aware bash) - Skips on: read-only ops (`git status`, `git log`, `git diff`, `gh pr list`, `gh pr view`) - Bypass marker: `cd-worktree-rule` (use when working with absolute paths or after explicit `pwd` verification) ## Tests 15 tests added, all PASS: - 7 warns (commit, push, mv, sed -i, gh pr create, gh pr merge, reset --hard) - 7 passes (cd prefix, pushd prefix, status, log, diff, gh pr list, gh pr view) - 1 bypass (with cd-worktree-rule marker) Total test suite now 192/192 (was 177). ## Memory backing `~/.claude/.../memory/feedback_cd_between_worktrees.md` (saved 2026-05-10) captures the bug history + how-to-apply guidance. Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude-plugin/marketplace.json | 4 +- .claude-plugin/plugin.json | 2 +- hooks/rules/rules.json | 168 ++++++++++++++++++++++++++++++++ hooks/rules/rules.yaml | 120 +++++++++++++++++++++++ package.json | 2 +- 5 files changed, 292 insertions(+), 4 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index b6b2184..b16ae20 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -1,7 +1,7 @@ { "$schema": "https://anthropic.com/claude-code/marketplace.schema.json", "name": "make-no-mistakes", - "version": "1.10.0", + "version": "1.11.0", "description": "The disciplined dev lifecycle — implement issues, review PRs, sync releases, test E2E, manage sessions, and stash secrets via OS-native prompts. One plugin to make no mistakes.", "owner": { "name": "Luis Andres Pena Castillo", @@ -11,7 +11,7 @@ { "name": "make-no-mistakes", "description": "Dev lifecycle orchestrator: disciplined Linear issue execution with worktree isolation, PR review with Greptile gating, team release sync, E2E test generation and execution, test suite previewer, security pentesting, MoSCoW + RICE prioritization, cross-platform secret stash via OS-native GUI prompts (zenity / kdialog / osascript / Get-Credential), and session management. 18 commands, 6 auto-activating skills, 2 specialized agents.", - "version": "1.10.0", + "version": "1.11.0", "author": { "name": "Luis Andres Pena Castillo", "email": "lapc506@users.noreply.github.com" diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index d84a645..4372ccb 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "make-no-mistakes", - "version": "1.10.0", + "version": "1.11.0", "description": "The disciplined dev lifecycle — implement issues, review PRs, sync releases, test E2E, manage sessions, stash secrets, and enforce manifest-driven tool-call hooks. One plugin to make no mistakes.", "author": { "name": "Luis Andres Pena Castillo", diff --git a/hooks/rules/rules.json b/hooks/rules/rules.json index 3c5f3e1..f62ccab 100644 --- a/hooks/rules/rules.json +++ b/hooks/rules/rules.json @@ -2402,5 +2402,173 @@ "expected_exit": 0 } ] + }, + { + "id": "warn-bash-mutation-without-leading-cd", + "description": "Warn when bash mutation commands (git commit/push/mv/rm/reset/merge/revert, sed -i, gh pr create/merge) don't start with explicit `cd /` — prevents commits landing on the wrong branch when working across multiple parallel worktrees", + "applies_to": [ + "Bash" + ], + "match": [ + { + "field": "command", + "pattern": "git[[:space:]]+(commit|push|mv|rm[[:space:]]|reset|revert|merge)|sed[[:space:]]+-i[[:space:]]|gh[[:space:]]+pr[[:space:]]+(create|merge|edit)" + }, + { + "field": "command", + "not_pattern": "^[[:space:]]*(cd|pushd)[[:space:]]+" + } + ], + "action": "warn", + "bypass_marker": "cd-worktree-rule", + "memory_ref": "feedback_cd_between_worktrees.md", + "references": [ + "DOJ-4007 PR-2/PR-3 cross-ref fixup (2026-05-10) — bug that motivated this rule" + ], + "message": "WARN: bash mutation command (git commit/push/mv/rm/reset/merge/revert,\nsed -i, or gh pr create/merge/edit) detected without a leading\n`cd /` prefix.\n\nPer feedback_cd_between_worktrees.md: when working across multiple\nparallel git worktrees in the same session, EVERY bash invocation that\ntouches a specific worktree MUST start with explicit\n`cd /full/path/to/worktree && ...`.\n\nBug pattern this prevents (real, 2026-05-10):\n- Two parallel worktrees for two PRs (PR-2 + PR-3 of same repo).\n- First worktree's sed + commit + push: correct (cwd was PR-2).\n- Second worktree's sed + commit + push: missing cd → still in PR-2 cwd\n → commit landed on PR-2 branch instead of PR-3 branch.\n- Required revert on PR-2 + correct re-apply on PR-3. Confused commit\n history visible to reviewers.\n\nIf the command operates on absolute paths or doesn't need cwd context,\nor you've intentionally verified cwd via `pwd` immediately before,\nadd \"# hook-bypass: cd-worktree-rule\" to acknowledge.\n", + "tests": [ + { + "name": "warns-on-git-commit-no-cd", + "input": { + "tool_input": { + "command": "git add foo.md && git commit -m \"fix\"" + } + }, + "expected_exit": 0, + "expected_stderr_contains": "warn-bash-mutation-without-leading-cd" + }, + { + "name": "warns-on-git-push-no-cd", + "input": { + "tool_input": { + "command": "git push origin main" + } + }, + "expected_exit": 0, + "expected_stderr_contains": "warn-bash-mutation-without-leading-cd" + }, + { + "name": "warns-on-git-mv-no-cd", + "input": { + "tool_input": { + "command": "git mv content/old content/new" + } + }, + "expected_exit": 0, + "expected_stderr_contains": "warn-bash-mutation-without-leading-cd" + }, + { + "name": "warns-on-sed-i-no-cd", + "input": { + "tool_input": { + "command": "sed -i s/foo/bar/g content/file.md" + } + }, + "expected_exit": 0, + "expected_stderr_contains": "warn-bash-mutation-without-leading-cd" + }, + { + "name": "warns-on-gh-pr-create-no-cd", + "input": { + "tool_input": { + "command": "gh pr create --base main --title foo" + } + }, + "expected_exit": 0, + "expected_stderr_contains": "warn-bash-mutation-without-leading-cd" + }, + { + "name": "warns-on-gh-pr-merge-no-cd", + "input": { + "tool_input": { + "command": "gh pr merge 27 --squash --delete-branch" + } + }, + "expected_exit": 0, + "expected_stderr_contains": "warn-bash-mutation-without-leading-cd" + }, + { + "name": "warns-on-git-reset-hard-no-cd", + "input": { + "tool_input": { + "command": "git reset --hard origin/main" + } + }, + "expected_exit": 0, + "expected_stderr_contains": "warn-bash-mutation-without-leading-cd" + }, + { + "name": "passes-with-cd-prefix", + "input": { + "tool_input": { + "command": "cd /home/user/repo && git commit -m fix && git push" + } + }, + "expected_exit": 0 + }, + { + "name": "passes-with-pushd-prefix", + "input": { + "tool_input": { + "command": "pushd /home/user/repo && git commit -m fix" + } + }, + "expected_exit": 0 + }, + { + "name": "passes-on-git-status", + "input": { + "tool_input": { + "command": "git status" + } + }, + "expected_exit": 0 + }, + { + "name": "passes-on-git-log", + "input": { + "tool_input": { + "command": "git log --oneline -3" + } + }, + "expected_exit": 0 + }, + { + "name": "passes-on-git-diff", + "input": { + "tool_input": { + "command": "git diff --stat" + } + }, + "expected_exit": 0 + }, + { + "name": "passes-on-gh-pr-list", + "input": { + "tool_input": { + "command": "gh pr list --author=@me" + } + }, + "expected_exit": 0 + }, + { + "name": "passes-on-gh-pr-view", + "input": { + "tool_input": { + "command": "gh pr view 27 --json mergeable" + } + }, + "expected_exit": 0 + }, + { + "name": "passes-with-bypass-marker", + "input": { + "tool_input": { + "command": "git push origin main # hook-bypass: cd-worktree-rule" + } + }, + "expected_exit": 0 + } + ] } ] diff --git a/hooks/rules/rules.yaml b/hooks/rules/rules.yaml index b5f622e..f17483d 100644 --- a/hooks/rules/rules.yaml +++ b/hooks/rules/rules.yaml @@ -1919,3 +1919,123 @@ tool_input: command: 'gh pr create --body "Run bun run dev on localhost:5173 # hook-bypass: localhost-in-pr-body-allowed"' expected_exit: 0 + +- id: warn-bash-mutation-without-leading-cd + description: Warn when bash mutation commands (git commit/push/mv/rm/reset/merge/revert, sed -i, gh pr create/merge) don't start with explicit `cd /` — prevents commits landing on the wrong branch when working across multiple parallel worktrees + applies_to: [Bash] + match: + # Positive: command contains a state-mutating op + - field: command + pattern: 'git[[:space:]]+(commit|push|mv|rm[[:space:]]|reset|revert|merge)|sed[[:space:]]+-i[[:space:]]|gh[[:space:]]+pr[[:space:]]+(create|merge|edit)' + # Negative: command does NOT begin with `cd ` or `pushd ` + - field: command + not_pattern: '^[[:space:]]*(cd|pushd)[[:space:]]+' + action: warn + bypass_marker: cd-worktree-rule + memory_ref: feedback_cd_between_worktrees.md + references: + - "DOJ-4007 PR-2/PR-3 cross-ref fixup (2026-05-10) — bug that motivated this rule" + message: | + WARN: bash mutation command (git commit/push/mv/rm/reset/merge/revert, + sed -i, or gh pr create/merge/edit) detected without a leading + `cd /` prefix. + + Per feedback_cd_between_worktrees.md: when working across multiple + parallel git worktrees in the same session, EVERY bash invocation that + touches a specific worktree MUST start with explicit + `cd /full/path/to/worktree && ...`. + + Bug pattern this prevents (real, 2026-05-10): + - Two parallel worktrees for two PRs (PR-2 + PR-3 of same repo). + - First worktree's sed + commit + push: correct (cwd was PR-2). + - Second worktree's sed + commit + push: missing cd → still in PR-2 cwd + → commit landed on PR-2 branch instead of PR-3 branch. + - Required revert on PR-2 + correct re-apply on PR-3. Confused commit + history visible to reviewers. + + If the command operates on absolute paths or doesn't need cwd context, + or you've intentionally verified cwd via `pwd` immediately before, + add "# hook-bypass: cd-worktree-rule" to acknowledge. + tests: + - name: warns-on-git-commit-no-cd + input: + tool_input: + command: 'git add foo.md && git commit -m "fix"' + expected_exit: 0 + expected_stderr_contains: 'warn-bash-mutation-without-leading-cd' + - name: warns-on-git-push-no-cd + input: + tool_input: + command: 'git push origin main' + expected_exit: 0 + expected_stderr_contains: 'warn-bash-mutation-without-leading-cd' + - name: warns-on-git-mv-no-cd + input: + tool_input: + command: 'git mv content/old content/new' + expected_exit: 0 + expected_stderr_contains: 'warn-bash-mutation-without-leading-cd' + - name: warns-on-sed-i-no-cd + input: + tool_input: + command: 'sed -i s/foo/bar/g content/file.md' + expected_exit: 0 + expected_stderr_contains: 'warn-bash-mutation-without-leading-cd' + - name: warns-on-gh-pr-create-no-cd + input: + tool_input: + command: 'gh pr create --base main --title foo' + expected_exit: 0 + expected_stderr_contains: 'warn-bash-mutation-without-leading-cd' + - name: warns-on-gh-pr-merge-no-cd + input: + tool_input: + command: 'gh pr merge 27 --squash --delete-branch' + expected_exit: 0 + expected_stderr_contains: 'warn-bash-mutation-without-leading-cd' + - name: warns-on-git-reset-hard-no-cd + input: + tool_input: + command: 'git reset --hard origin/main' + expected_exit: 0 + expected_stderr_contains: 'warn-bash-mutation-without-leading-cd' + - name: passes-with-cd-prefix + input: + tool_input: + command: 'cd /home/user/repo && git commit -m fix && git push' + expected_exit: 0 + - name: passes-with-pushd-prefix + input: + tool_input: + command: 'pushd /home/user/repo && git commit -m fix' + expected_exit: 0 + - name: passes-on-git-status + input: + tool_input: + command: 'git status' + expected_exit: 0 + - name: passes-on-git-log + input: + tool_input: + command: 'git log --oneline -3' + expected_exit: 0 + - name: passes-on-git-diff + input: + tool_input: + command: 'git diff --stat' + expected_exit: 0 + - name: passes-on-gh-pr-list + input: + tool_input: + command: 'gh pr list --author=@me' + expected_exit: 0 + - name: passes-on-gh-pr-view + input: + tool_input: + command: 'gh pr view 27 --json mergeable' + expected_exit: 0 + - name: passes-with-bypass-marker + input: + tool_input: + command: 'git push origin main # hook-bypass: cd-worktree-rule' + expected_exit: 0 diff --git a/package.json b/package.json index 555c610..f43955e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lapc506/make-no-mistakes", - "version": "1.10.0", + "version": "1.11.0", "description": "The disciplined dev lifecycle — implement issues, review PRs, sync releases, test E2E, manage sessions, stash secrets, and enforce manifest-driven tool-call hooks (no SSH+DB, no manual prod, no minified build, no secret leaks, Slack format). OpenCode + Claude Code plugin.", "type": "module", "main": "./dist/index.js", From 37725d3b8f3cb36ee840e7a23f139349fb7d7b80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Pe=C3=B1a?= Date: Sun, 10 May 2026 03:29:27 -0600 Subject: [PATCH 2/2] fixup(rules): address Greptile review feedback on warn-bash-mutation-without-leading-cd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Greptile review on PR #19 (3/5 confidence) flagged 4 gaps: 1. **Relative cd bypassed the rule** — `not_pattern` accepted any `cd `, including `cd worktree-dir` (relative). Per the rule's docstring, an absolute path is required because if cwd is already in the wrong worktree, a relative `cd` resolves to the wrong target. Tightened pattern to require `cd /...`, `cd ~/...`, or `cd $VAR/...` (the 3 forms of absolute-or-known-absolute paths). 2. **Missing `git rm` test** — added `warns-on-git-rm-no-cd`. 3. **Sed pattern missed `-i.bak` and `--in-place`** — extended pattern to: `sed -i `, `sed -i.bak`, `sed -ibackup`, `sed --in-place `. Added 2 tests. 4. **Description omitted `gh pr edit`** — corrected to `create/merge/edit` and widened to mention long-form sed variants. ## Test additions (7 new tests, 199 total now) WARNS: - `warns-on-git-rm-no-cd` - `warns-on-sed-i-bak-no-cd` (`sed -i.bak ...`) - `warns-on-sed-in-place-long-form-no-cd` (`sed --in-place ...`) - `warns-on-git-commit-with-relative-cd` (`cd worktree-dir && git commit`) - `warns-on-git-push-with-dotdot-cd` (`cd ../sibling && git push`) PASSES: - `passes-with-tilde-cd-prefix` (`cd ~/repo && git commit`) - `passes-with-env-var-cd-prefix` (`cd $WORKTREE && git push`) All 199/199 tests pass after the changes. Co-Authored-By: Claude Opus 4.7 (1M context) --- hooks/rules/rules.json | 74 ++++++++++++++++++++++++++++++++++++++++-- hooks/rules/rules.yaml | 54 +++++++++++++++++++++++++++--- 2 files changed, 120 insertions(+), 8 deletions(-) diff --git a/hooks/rules/rules.json b/hooks/rules/rules.json index f62ccab..a7780da 100644 --- a/hooks/rules/rules.json +++ b/hooks/rules/rules.json @@ -2405,18 +2405,18 @@ }, { "id": "warn-bash-mutation-without-leading-cd", - "description": "Warn when bash mutation commands (git commit/push/mv/rm/reset/merge/revert, sed -i, gh pr create/merge) don't start with explicit `cd /` — prevents commits landing on the wrong branch when working across multiple parallel worktrees", + "description": "Warn when bash mutation commands (git commit/push/mv/rm/reset/merge/revert, sed -i / sed --in-place, gh pr create/merge/edit) don't start with an absolute-path `cd /<...>` (or `pushd /<...>`, `cd ~/<...>`, `cd $VAR/<...>`) — prevents commits landing on the wrong branch when working across multiple parallel worktrees", "applies_to": [ "Bash" ], "match": [ { "field": "command", - "pattern": "git[[:space:]]+(commit|push|mv|rm[[:space:]]|reset|revert|merge)|sed[[:space:]]+-i[[:space:]]|gh[[:space:]]+pr[[:space:]]+(create|merge|edit)" + "pattern": "git[[:space:]]+(commit|push|mv|rm[[:space:]]|reset|revert|merge)|sed[[:space:]]+-i([[:space:]]|[^[:space:]-])|sed[[:space:]]+--in-place[[:space:]]|gh[[:space:]]+pr[[:space:]]+(create|merge|edit)" }, { "field": "command", - "not_pattern": "^[[:space:]]*(cd|pushd)[[:space:]]+" + "not_pattern": "^[[:space:]]*(cd|pushd)[[:space:]]+(/|~|\\$)" } ], "action": "warn", @@ -2457,6 +2457,16 @@ "expected_exit": 0, "expected_stderr_contains": "warn-bash-mutation-without-leading-cd" }, + { + "name": "warns-on-git-rm-no-cd", + "input": { + "tool_input": { + "command": "git rm path/to/file.md" + } + }, + "expected_exit": 0, + "expected_stderr_contains": "warn-bash-mutation-without-leading-cd" + }, { "name": "warns-on-sed-i-no-cd", "input": { @@ -2467,6 +2477,46 @@ "expected_exit": 0, "expected_stderr_contains": "warn-bash-mutation-without-leading-cd" }, + { + "name": "warns-on-sed-i-bak-no-cd", + "input": { + "tool_input": { + "command": "sed -i.bak s/foo/bar/g content/file.md" + } + }, + "expected_exit": 0, + "expected_stderr_contains": "warn-bash-mutation-without-leading-cd" + }, + { + "name": "warns-on-sed-in-place-long-form-no-cd", + "input": { + "tool_input": { + "command": "sed --in-place s/foo/bar/g content/file.md" + } + }, + "expected_exit": 0, + "expected_stderr_contains": "warn-bash-mutation-without-leading-cd" + }, + { + "name": "warns-on-git-commit-with-relative-cd", + "input": { + "tool_input": { + "command": "cd worktree-dir && git commit -m \"fix\"" + } + }, + "expected_exit": 0, + "expected_stderr_contains": "warn-bash-mutation-without-leading-cd" + }, + { + "name": "warns-on-git-push-with-dotdot-cd", + "input": { + "tool_input": { + "command": "cd ../sibling-dir && git push" + } + }, + "expected_exit": 0, + "expected_stderr_contains": "warn-bash-mutation-without-leading-cd" + }, { "name": "warns-on-gh-pr-create-no-cd", "input": { @@ -2515,6 +2565,24 @@ }, "expected_exit": 0 }, + { + "name": "passes-with-tilde-cd-prefix", + "input": { + "tool_input": { + "command": "cd ~/repo && git commit -m fix" + } + }, + "expected_exit": 0 + }, + { + "name": "passes-with-env-var-cd-prefix", + "input": { + "tool_input": { + "command": "cd $WORKTREE && git push" + } + }, + "expected_exit": 0 + }, { "name": "passes-on-git-status", "input": { diff --git a/hooks/rules/rules.yaml b/hooks/rules/rules.yaml index f17483d..b90e75f 100644 --- a/hooks/rules/rules.yaml +++ b/hooks/rules/rules.yaml @@ -1921,15 +1921,19 @@ expected_exit: 0 - id: warn-bash-mutation-without-leading-cd - description: Warn when bash mutation commands (git commit/push/mv/rm/reset/merge/revert, sed -i, gh pr create/merge) don't start with explicit `cd /` — prevents commits landing on the wrong branch when working across multiple parallel worktrees + description: Warn when bash mutation commands (git commit/push/mv/rm/reset/merge/revert, sed -i / sed --in-place, gh pr create/merge/edit) don't start with an absolute-path `cd /<...>` (or `pushd /<...>`, `cd ~/<...>`, `cd $VAR/<...>`) — prevents commits landing on the wrong branch when working across multiple parallel worktrees applies_to: [Bash] match: - # Positive: command contains a state-mutating op + # Positive: command contains a state-mutating op. + # sed pattern covers: `sed -i `, `sed -i.bak`, `sed -ibackup`, and long form `sed --in-place`. - field: command - pattern: 'git[[:space:]]+(commit|push|mv|rm[[:space:]]|reset|revert|merge)|sed[[:space:]]+-i[[:space:]]|gh[[:space:]]+pr[[:space:]]+(create|merge|edit)' - # Negative: command does NOT begin with `cd ` or `pushd ` + pattern: 'git[[:space:]]+(commit|push|mv|rm[[:space:]]|reset|revert|merge)|sed[[:space:]]+-i([[:space:]]|[^[:space:]-])|sed[[:space:]]+--in-place[[:space:]]|gh[[:space:]]+pr[[:space:]]+(create|merge|edit)' + # Negative: command must START with `cd ` or `pushd `. + # Accepts: cd /full/path, pushd /full/path, cd ~/path, cd $VAR/path (env vars assumed absolute). + # Rejects: cd worktree-dir (relative path — DOESN'T fix the cwd-confusion bug because if + # cwd is already in the wrong worktree, `cd relative-dir` resolves to the wrong place). - field: command - not_pattern: '^[[:space:]]*(cd|pushd)[[:space:]]+' + not_pattern: '^[[:space:]]*(cd|pushd)[[:space:]]+(/|~|\$)' action: warn bypass_marker: cd-worktree-rule memory_ref: feedback_cd_between_worktrees.md @@ -1975,12 +1979,42 @@ command: 'git mv content/old content/new' expected_exit: 0 expected_stderr_contains: 'warn-bash-mutation-without-leading-cd' + - name: warns-on-git-rm-no-cd + input: + tool_input: + command: 'git rm path/to/file.md' + expected_exit: 0 + expected_stderr_contains: 'warn-bash-mutation-without-leading-cd' - name: warns-on-sed-i-no-cd input: tool_input: command: 'sed -i s/foo/bar/g content/file.md' expected_exit: 0 expected_stderr_contains: 'warn-bash-mutation-without-leading-cd' + - name: warns-on-sed-i-bak-no-cd + input: + tool_input: + command: 'sed -i.bak s/foo/bar/g content/file.md' + expected_exit: 0 + expected_stderr_contains: 'warn-bash-mutation-without-leading-cd' + - name: warns-on-sed-in-place-long-form-no-cd + input: + tool_input: + command: 'sed --in-place s/foo/bar/g content/file.md' + expected_exit: 0 + expected_stderr_contains: 'warn-bash-mutation-without-leading-cd' + - name: warns-on-git-commit-with-relative-cd + input: + tool_input: + command: 'cd worktree-dir && git commit -m "fix"' + expected_exit: 0 + expected_stderr_contains: 'warn-bash-mutation-without-leading-cd' + - name: warns-on-git-push-with-dotdot-cd + input: + tool_input: + command: 'cd ../sibling-dir && git push' + expected_exit: 0 + expected_stderr_contains: 'warn-bash-mutation-without-leading-cd' - name: warns-on-gh-pr-create-no-cd input: tool_input: @@ -2009,6 +2043,16 @@ tool_input: command: 'pushd /home/user/repo && git commit -m fix' expected_exit: 0 + - name: passes-with-tilde-cd-prefix + input: + tool_input: + command: 'cd ~/repo && git commit -m fix' + expected_exit: 0 + - name: passes-with-env-var-cd-prefix + input: + tool_input: + command: 'cd $WORKTREE && git push' + expected_exit: 0 - name: passes-on-git-status input: tool_input: