v1.6.25
fix(hook): content-heuristic drift refresh for claude-code/cursor/codex registrars
Closes operator-reported "Claude Code effort column on the dashboard is empty
for every recent session despite the v1.6.22 sidecar pipeline being in place."
~/.claude/settings.json had hooks registered to a stale npm-bundled observer
v1.4.30 binary (pre-dates the v1.6.22 recordClaudecodeEffort write), so every
PreToolUse / PostToolUse fire silently no-op'd on the sidecar. observer doctor
detected the drift (⚠ hooks.binary) but the on-start autoRegisterHooks self-
heal refused to refresh because findClaudeGroupWithObserver /
hasConflictingClaudeHook used a byte-exact startsWith(h.Command, binaryPath)
detector — a stale observer at a different binary path failed the prefix
match and got mis-classified as a foreign third-party hook. Same gap in
findCodexGroupWithObserver, hasConflictingCodexHook, hasStaleObserverEntry,
filterStaleObserverEntries, hasCursorConflict. The Windows registrar already
used the right pattern — content-heuristic on the hook claude-code token
sequence regardless of binary path prefix (isObserverWindowsClaudeEntry) — so
cross-binary upgrades on Windows refreshed silently while the Linux/default path
stayed broken.
Fix (internal/hook/register.go): three new content-heuristic helpers —
isObserverClaudeEntry, isObserverCursorEntry, isObserverCodexEntry — each
matching the hook <tool> token. Seven existing helpers rewritten to use them
and the now-redundant binaryPath parameter dropped. Trade-off documented inline:
a third-party command containing the literal hook <tool> substring would be
misclassified as observer and silently rewritten — same trade-off the Windows
registrar has accepted in production since v1.6.22; the syntax is distinctive
enough that accidental collision is essentially impossible.
After the fix ships, every future observer start self-heals stale-binary
entries automatically without --force. Historical sessions cannot retroactively
populate effort_level — effort lives only on hook stdin at fire-time and the
v1.4.30 binary swallowed those payloads.
fix(hook): single-quote binary path in Linux/default registrars for Git Bash on Windows
Closes follow-on diagnosis after the drift-refresh landed and Claude Code hook
fires still exited 127 with stderr /usr/bin/bash: line 1: D:programsxsuperbased- observerbinobserver.exe: command not found — note the stripped backslashes in
the error. Root cause: Claude Code on Windows always invokes hook commands
through Git Bash (per docs/claudecode-hook-capture.md), regardless of which
terminal launched the app. The Linux/default registerClaudeCode /
registerCursor / registerCodex registrars wrote the binary path unquoted
(D:\programsx\...\observer.exe ...), and Git Bash interpreted the backslashes
as shell escape sequences (\p → p, \s → s, etc.), producing
D:programsx...observer.exe and failing to find the binary. This is the
inverse of the MSYS_NO_PATHCONV bug already fixed for claude-code-windows
— that one was about path PREFIXING (Git Bash auto-translating /-rooted args);
this one is about ESCAPE STRIPPING. Affects only the Windows-native observer ↔
Windows-native Claude Code path, which neither the Linux registrar (assumes
POSIX) nor the claude-code-windows registrar (assumes WSL bridge) covered.
Fix (internal/hook/register.go): wrap r.opts.BinaryPath via
shellQuoteIfNeeded in all three Linux/default registrars' cmd construction.
Windows paths get single-quoted ('D:\programsx\...\observer.exe'); POSIX
paths with no special chars pass through unchanged, preserving existing test
fixtures + diff hygiene on Linux/macOS hosts. Single-quote is the same idiom
configFlagSuffix already uses for the --config <path> argument. Bonus: also
fixes paths containing spaces (C:\Program Files\observer\observer.exe) which
previously split on whitespace in argv.
End-to-end verified live on Windows: 26 claudecode_effort sidecar rows
captured across 4 sessions while the maintainer's normal Claude Code workflow
continued, all carrying the correct xhigh effort level matching the dropdown
selection.
Tests
Six new tests in internal/hook/register_test.go. Three cross-binary-refresh
tests (TestRegisterClaudeCodeCrossBinaryPathRefresh,
TestRegisterCursorCrossBinaryPathRefresh,
TestRegisterCodexCrossBinaryPathRefresh) install against /tmp/observer-A,
re-register against /usr/local/bin/observer, and assert the file refreshes
silently. Three Windows-quote tests
(TestRegisterClaudeCodeQuotesWindowsBinaryPath,
TestRegisterCursorQuotesWindowsBinaryPath,
TestRegisterCodexQuotesWindowsBinaryPath) register with a D:\...\observer.exe
BinaryPath and assert the JSON-decoded command starts with the single-quoted
shape. Existing TestRegisterClaudeCodeConflict / TestRegisterCodexConflict
still pass — genuine foreign commands lack the hook <tool> token and remain
flagged.
Downloads
Pre-built binaries for each supported platform are attached below. Linux variants bundle antigravity-bridge.exe next to the observer binary for WSL2 users of the Antigravity adapter.
| Platform | Asset |
|---|---|
| Linux x86_64 | observer-v1.6.25-linux-x64.tar.gz |
| Linux arm64 | observer-v1.6.25-linux-arm64.tar.gz |
| macOS x86_64 (Intel) | observer-v1.6.25-darwin-x64.tar.gz |
| macOS arm64 (Apple Silicon) | observer-v1.6.25-darwin-arm64.tar.gz |
| Windows x86_64 | observer-v1.6.25-win32-x64.zip |
Verify with sha256sum -c SHA256SUMS (or shasum -a 256 -c SHA256SUMS on macOS) from the directory containing the downloads.
Also available via npm: npm install -g @superbased/observer@1.6.25