Files
chenhg5-cc-connect/agent
Han 527220d924 feat(claudecode): respect Claude Code PermissionRequest hooks (#850)
* feat(claudecode): respect Claude Code PermissionRequest hooks

When cc-connect bridges Claude Code via --permission-prompt-tool stdio,
Claude Code's PermissionRequest hooks are executed but their results
are ignored — the control_request is sent on stdout regardless.

This fix adds a hook runner in the claudecode agent that independently
reads and executes the user's PermissionRequest hooks from
~/.claude/settings.json before forwarding permission requests to the
messaging platform. If a hook returns "allow" or "deny", the decision
is respected directly. If the hook returns "ask" or no hook matches,
the existing behavior (forward to platform) is preserved.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(claudecode): add CC_CONNECT_PERMISSION_HOOK_SKIP env var

When cc-connect spawns Claude Code, inject
CC_CONNECT_PERMISSION_HOOK_SKIP=1 into the subprocess environment.
PermissionRequest hooks can check this to skip expensive work (e.g.
LLM calls) on the Claude Code side, since the hook result is ignored
anyway when --permission-prompt-tool stdio is active.

cc-connect's own hook runner explicitly strips this env var so the
hook does real work only once — when cc-connect calls it directly.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(claudecode): improve cc_hooks protocol compat and test coverage

- Add hook_event_name field to hook stdin for Claude Code protocol compat
- Change hook timeout from 10s to 60s to match Claude Code's own timeout
- Log non-ENOENT errors when loading settings files for debuggability
- Return original data on unclosed block comments in stripJSONC
- Add tests for multi-entry fallthrough, settings merging, deny message
  field, CC_CONNECT_PERMISSION_HOOK_SKIP env stripping, timeout, and
  unclosed block comments

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs: add Claude Code PermissionRequest hooks section to usage guide

Explain that cc-connect re-runs hooks independently (hooks execute twice
per request) and document the CC_CONNECT_PERMISSION_HOOK_SKIP env var
for LLM-based hooks to avoid double token cost.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(claudecode): load hook settings once per session instead of caching

Replace 30s TTL cache with sync.Once — settings are read on first
tryHook call and reused for the session lifetime. Avoids repeated
JSON parsing on every permission request.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(claudecode): introduce hookContext for richer hook stdin payload

Bundle hook parameters into a hookContext struct and add fields that
match Claude Code's PermissionRequest hook input spec: permission_mode,
transcript_path, permission_suggestions, agent_id, agent_type. Optional
fields are omitted from stdin JSON when empty.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix: lint - errcheck WriteFile/MkdirAll, staticcheck QF1003 switch

* fix: all remaining errcheck WriteFile/MkdirAll in cc_hooks_test.go

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 23:27:46 +08:00
..