AGENTS.md asks for new objects rather than mutation. toggleQuote now returns the updated quote state (or null when the character is not a quote it toggles), skipOuterSpan returns it alongside the skipped index, and each scanner reassigns its own local state.
Dropping the previous-character check in toggleQuote let the `'` of `\'` close an ANSI-C `$'...'` string, so `echo $'it\'s'; (rm -rf /tmp/x)` left a quote open and the group was never extracted. Track `$'...'` in the quote state and consume backslash escapes inside it, as bash does, and cover the inner scanners (`(...)`, `$(...)`, `{ ...; }` bodies) with quotes ending in a backslash.
toggleQuote refused to toggle when the previous character was a backslash, but every caller already consumes a backslash and the character after it outside single quotes. A quote reached after a backslash is therefore the end of `'C:\'` or the end of `"C:\\"`, and both stayed open, so a `(...)` group or `{ ...; }` group after them was never extracted. `echo "C:\\" ; (npm run dev)` passed the dev-server block while `echo "C:" ; (npm run dev)` was blocked.
extractSubshellGroups and extractBraceGroups each carried their own copies of the quote-aware skipping for $(...), backtick and (...) spans, which is where the complexity and nesting warnings came from. The skipping now lives in findParenSpanEnd, findBacktickEnd and skipOuterSpan, with readSubshellBody and readBraceBody doing the body collection.
Behavior is unchanged: the same bodies come back in the same order, trailing backslashes and unterminated spans resolve the same way, and the three exports keep their signatures.
* fix(hooks,lib): fix hook detection and parsing edge cases
- auto-tmux-dev: dev\b -> dev(?![\w-]) so one-shot dev-build/dev-docs scripts
are not detached into tmux; align command shapes (yarn run dev, bun dev) with
pre-bash-dev-server-block.js DEV_PATTERN.
- pre-bash-commit-quality: skip obvious non-secret placeholders (env refs,
${...}, <...>, whitelisted tokens) in the api-key rule without suppressing
real high-entropy secrets; make -m message extraction quote- and
escaped-quote-aware so `-m "fix: \"x\""` / apostrophes are not truncated.
- pre-compact: annotate the CURRENT worktree's session (match **Worktree:** /
legacy **Project:**) instead of the newest *-session.tmp across all projects,
layered onto the LLM-summary flow from #2388; a present-but-blank Worktree
header is treated as non-legacy (no foreign project fallback).
- shell-substitution: stop double-appending a trailing backslash in an
unterminated backtick span.
- utils readStdinJson: on overflow, settle and resolve {} immediately (clear
timer + listeners) instead of waiting for end/timeout and parsing a partial
prefix; surface the overflow on stderr.
Regression tests added/extended (new tests/hooks/pre-compact.test.js).
Addresses review feedback on #2405. The earlier block-no-verify change was
dropped: its message-value skip on merge/cherry-pick/am/rebase would let
`git rebase -m --no-verify` bypass the hook (rebase's -m is the boolean
--merge), a false-negative worse than the contrived false-positive it fixed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(ci): align hook fixtures and drain oversized stdin
---------
Co-authored-by: djpjronline-netizen <276112803+djpjronline-netizen@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: haelyra <49814733+haelyra@users.noreply.github.com>