diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 0eecb87..d170f15 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -9,7 +9,7 @@ { "name": "autodev", "description": "Autonomous development workflow skills for coding agents", - "version": "6.1.3", + "version": "6.1.4", "source": "./", "author": { "name": "Jon Langevin", diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index e2668eb..08802bc 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "autodev", "description": "Autonomous development workflow skills for coding agents: design, review, planning, execution, monitoring, and retrospectives", - "version": "6.1.3", + "version": "6.1.4", "author": { "name": "Jon Langevin", "email": "jon@gocodealone.com" diff --git a/.cursor-plugin/plugin.json b/.cursor-plugin/plugin.json index dbdd0a5..093bd1d 100644 --- a/.cursor-plugin/plugin.json +++ b/.cursor-plugin/plugin.json @@ -2,7 +2,7 @@ "name": "autodev", "displayName": "Autonomous Dev Kit", "description": "Autonomous development workflow skills for coding agents", - "version": "6.1.3", + "version": "6.1.4", "author": { "name": "Jon Langevin", "email": "jon@gocodealone.com" diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index f88b083..3d6d915 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,5 +1,12 @@ # Autonomous Dev Kit Release Notes +## v6.1.4 — 2026-05-28 + +PreToolUse guard quote-strip extended to all destructive-command checks. + +- `hooks/pre-tool-scope-guard`: the force-push, history-rewrite, locked-plan-push, and default-branch-push checks now operate on the quote-stripped form of the Bash `tool_input.command` (`cmd_no_quotes`, already computed for the SUPERPOWERS_ self-bypass check). Previously these four checks scanned the raw command, which produced false-positive blocks when a destructive command appeared as a documentation example inside a quoted heredoc body — e.g. `gh pr create --body "$(cat </dev/null || true) if [ "$current_branch" = "main" ] || [ "$current_branch" = "master" ]; then block "Current branch is '${current_branch}' (the default branch). Pushing directly to main or master is blocked during autonomous pipeline execution. Switch to a feature branch first. To override, set SUPERPOWERS_ALLOW_DEFAULT_BRANCH=1." diff --git a/tests/hook-contracts.sh b/tests/hook-contracts.sh index 1591a9a..7baedce 100755 --- a/tests/hook-contracts.sh +++ b/tests/hook-contracts.sh @@ -92,6 +92,33 @@ test_session_start_json() { assert_hook_context_json "session-start" "SessionStart" "$output" } +test_pre_tool_scope_guard_does_not_block_force_push_inside_quoted_string() { + # Destructive-command regexes must use the quote-stripped form of the + # tool_input.command so that a documentation example inside a quoted + # heredoc body doesn't trigger a false-positive force-push block. + # Regression for the session-time block of PR #47 creation: the PR body + # quoted `git push --force origin main` verbatim and the hook matched it. + local tmp stdout_file stderr_file status + tmp="$(mktemp -d)" + trap 'rm -rf "$tmp"' RETURN + stdout_file="$tmp/out" + stderr_file="$tmp/err" + payload='{"tool_name":"Bash","tool_input":{"command":"gh pr create --title hi --body \"Example to avoid: git push --force origin main\""},"cwd":"'"$tmp"'"}' + set +e + printf '%s' "$payload" | hooks/pre-tool-scope-guard >"$stdout_file" 2>"$stderr_file" + status=$? + set -e + if [ "$status" != "0" ]; then + fail "pre-tool-scope-guard: must not block force-push mention inside quoted string, exit ${status} stdout: $(cat "$stdout_file") stderr: $(cat "$stderr_file")" + return + fi + if grep -q '"decision":"block"' "$stdout_file"; then + fail "pre-tool-scope-guard: blocked force-push mention inside quoted string (false positive). stdout: $(cat "$stdout_file")" + return + fi + pass "pre-tool-scope-guard: does not block force-push mentions inside quoted strings" +} + test_pre_tool_scope_guard_block_exits_zero_with_stderr_reason() { # When pre-tool-scope-guard blocks a Bash command, it must: # (1) exit 0 -- both Claude Code and Codex ignore stdout JSON on exit 2 @@ -1477,6 +1504,7 @@ test_pretool_allows_locked_plan_text_edit test_subagent_allows_non_manifest_plan_backport test_subagent_scope_guard_ignores_unattributed_workspace_lock test_subagent_scope_guard_blocks_attributed_drift +test_pre_tool_scope_guard_does_not_block_force_push_inside_quoted_string test_pre_tool_scope_guard_block_exits_zero_with_stderr_reason test_subagent_scope_guard_block_exits_zero_with_stderr_reason test_scope_lock_claim_writes_session_attribution