feat(hooks): move hooks config to dedicated hooks.json, cut .claude/ dependency - #51
Merged
Merged
Conversation
…dependency
BREAKING: .claude/ directories and settings.json hooks field are no
longer read. Run /import-claude-hooks to migrate existing Claude
Code hook configurations.
Config location (6 layers → 2 dedicated files):
~/.config/opencode/hooks.json (global, loaded once at startup)
.opencode/hooks.json (project, hot-reloaded via polling)
<worktree>/.opencode/hooks.json (worktree, hot-reloaded)
Key changes:
- loadChain reads hooks.json from OpenCode-owned dirs only (.claude/
and .local variants removed)
- Top-level event format (no wrapper); legacy {"hooks":{...}} tolerated
- readJSON filters via VALID_HOOK_EVENTS whitelist + Array.isArray
- Deprecation warning for hooks left in old settings.json
- Hot-reload: project-level only, interval polling (2s) instead of
fs.watch (WSL2 DrvFs reliability); detects file deletion
- Global hooks.json is startup-only (no hot-reload), restart required
- Built-in /import-claude-hooks command for QA-guided migration
- AGENTS.md managed section (<!-- Hooks_START/END -->) for agent
self-awareness of configured hooks
OpenSpec: hooks-config-independence (spec-driven, 43/43 tasks complete)
Tests: 21 hook tests (load-chain + settings-dedup + hot-reload)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two things reviewers and users MUST know:
.claude/directories andsettings.jsonhooks field are no longer read. Users with existing Claude Code hook configs need to run/import-claude-hooksto migrate. A one-time deprecation warning is logged if hooks are found in old locations.~/.config/opencode/hooks.jsonchanges require restart. Only project/worktreehooks.jsonfiles are hot-reloaded. Global hooks load once at startup.Summary
Establishes OpenCode's own hooks identity by moving hooks configuration from
settings.json(mixed with other settings, 6-layer chain including.claude/) to dedicatedhooks.jsonfiles in OpenCode-owned directories only.Config Location: 6 layers → 2 dedicated files
~/.config/opencode/hooks.json.opencode/hooks.json<worktree>/.opencode/hooks.jsonFormat: top-level event keys (no wrapper)
{ "PreToolUse": [{ "matcher": "Bash", "hooks": [{"type": "command", ...}] }], "SessionStart": [{ "matcher": "*", "hooks": [{"type": "command", ...}] }] }Legacy
{"hooks": {...}}wrapper tolerated via graceful degradation.Migration:
/import-claude-hooksBuilt-in slash command (agent-guided, zero new TypeScript):
~/.claude/settings.json,.claude/settings.json,.claude/settings.local.json.opencode/settings.jsonhooks field${CLAUDE_PLUGIN_ROOT}path migration (.claude/ → .opencode/)hooks.jsonfilesAGENTS.mdmanaged sectionAgent Self-Awareness
AGENTS.mdgains<!-- Hooks_START -->/<!-- Hooks_END -->managed section. The import command writes a summary table of active hooks. Since AGENTS.md is loaded as instructions at session start, the agent knows what hooks exist in its environment without readinghooks.jsondirectly — full config details read on-demand.Implementation Details
loadChain()refactor (settings.ts).claude/+ 3.localpath entriessettings.json→hooks.json<worktree>/.opencode/hooks.json)VALID_HOOK_EVENTSwhitelist +Array.isArrayfilter — defends against `` and non-event keys being treated as matchers (would cause TypeError on.length)Hot-reload (hot-reload.ts)
fs.watch— WSL2 DrvFs /mnt/* mounts have unreliable inotifymtime from >0 to 0triggers reload)HotReloadHandle.close()interface unchangedBuilt-in command (command/index.ts)
src/command/template/import-claude-hooks.txt— prompt templateDefault.IMPORT_HOOKSalongsideINIT,REVIEW,GOALTests: 21 hook tests (was 0)
load-chain.test.ts.claude/exclusion, $schema filtering, polling reloadsettings-dedup.test.tssettings-hot-reload.test.tsTotal: 21 pass, 0 fail, typecheck clean.
OpenSpec
Full spec-driven artifacts committed alongside code:
proposal.md— FABLE5 code-verifieddesign.md— 7 decisions (D1-D7)specs/hooks-config/spec.md— 5 requirements / 18 scenariostasks.md— 43/43 tasks completeReady to archive via
/opsx:archive hooks-config-independenceafter merge.