Skip to content

Conversation

@taltas
Copy link
Contributor

@taltas taltas commented Jan 16, 2026

Summary

This PR implements a Claude Code-compatible hooks system for Roo Code, enabling users to run custom shell commands at key lifecycle events during AI assistant interactions.

Features

Core Hooks System

  • 8+ Lifecycle Events: PreToolUse, PostToolUse, ToolError, UserPromptSubmit, PermissionRequest, NotificationReceived, Stop, SubAgentStop
  • Blocking Capabilities: PreToolUse, PermissionRequest, and UserPromptSubmit hooks can block/modify execution
  • Claude Code Compatibility: Compatible JSON stdin format, environment variables, and exit codes

Configuration

  • Project-level hooks via .roo/hooks.json
  • Global hooks via extension settings
  • Mode-specific hook configurations
  • Pattern matching for tool/path filtering

Components

  • HookConfigLoader: Loads and merges hooks from project/global/mode configs
  • HookExecutor: Executes hooks as subprocesses with JSON input/output
  • HookMatcher: Matches events against hook matchers (tool names, patterns)
  • HookManager: Orchestrates hook discovery and execution
  • ToolExecutionHooks: Integration with tool execution lifecycle

Security

  • Hooks run with 60-second default timeout
  • Cannot bypass tool approval flow
  • User-controlled configuration only

Documentation

See hooks-prd.md for full PRD including:

  • Detailed requirements and user stories
  • Configuration schema
  • Execution semantics
  • Security model
  • Test plan

Testing

All components include comprehensive unit tests:

  • HookConfigLoader.spec.ts
  • HookExecutor.spec.ts
  • HookManager.spec.ts
  • HookMatcher.spec.ts
  • webviewMessageHandler.hooks.spec.ts

Related

Addresses community request for event-driven automation (Discussion #6147)


Important

Implements a Claude Code-compatible hooks system in Roo Code, enabling custom shell command execution at lifecycle events, with configuration, management, and UI integration.

  • Behavior:
    • Implements a hooks system compatible with Claude Code, handling events like PreToolUse, PostToolUse, ToolError, etc.
    • Supports blocking/modifying execution for certain events.
    • Configurable via .roo/hooks.json and global settings.
  • Components:
    • HookManager, HookExecutor, HookMatcher, ToolExecutionHooks classes for managing and executing hooks.
    • HookConfigLoader for loading and merging hook configurations.
  • UI:
    • Adds HooksSettings component to manage hooks in the settings UI.
    • Displays hook execution history and allows enabling/disabling hooks.
  • Testing:
    • Comprehensive tests for hook configuration, execution, and UI components.
    • Tests for HookManager, HookExecutor, HookMatcher, and HookConfigLoader.

This description was created by Ellipsis for 22eda99. You can customize this summary. It will automatically update as commits are pushed.

Add lifecycle hooks for tool execution with 12 event types:
- PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest
- SessionStart, SessionEnd, Stop, SubagentStart, SubagentStop
- UserPromptSubmit, Notification, PreCompact

Core implementation:
- HookManager service with config loading, validation, and execution
- HookExecutor for shell command execution with timeout, stdin JSON, env vars
- HookConfigLoader with project/global/mode-specific precedence
- HookMatcher for pattern matching (exact, regex, glob)
- ToolExecutionHooks adapter for pipeline interception

Tool pipeline integration:
- PreToolUse hooks can block or modify tool input
- PostToolUse/PostToolUseFailure for non-blocking notifications
- PermissionRequest hooks before approval prompts

Webview UI:
- Hooks tab in Settings with enable/disable toggles
- Hook Activity log with real-time status updates
- Reload config and open folder actions

Includes comprehensive tests for all components.

Relates to: Claude Code hooks feature parity
@taltas taltas requested review from cte, jr and mrubens as code owners January 16, 2026 19:57
@dosubot dosubot bot added size:XXL This PR changes 1000+ lines, ignoring generated files. Enhancement New feature or request labels Jan 16, 2026
@roomote
Copy link
Contributor

roomote bot commented Jan 16, 2026

Rooviewer Clock   See task on Roo Cloud

Review of commit c0d8507 complete. New commits clean up files that should not have been in the branch and add i18n translations for hook-triggered messages. No new issues introduced. Previously identified issues remain unresolved.

  • Dead code in ClineProvider.getHooksStateForWebview(): meaningless ternary expression on line 2280
  • Console spam in expandGroupPatterns: warns for all non-group patterns including valid tool names
Previous reviews

Mention @roomote in a comment to request specific changes to this pull request or fix all unresolved issues.

timestamp: exec.timestamp.toISOString(),
hookId: exec.hook.id,
event: exec.event,
toolName: exec.result.hook.matcher ? undefined : undefined, // Tool name is in context, not easily accessible here
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ternary expression always evaluates to undefined regardless of the condition (exec.result.hook.matcher ? undefined : undefined). The comment suggests this was intentional because extracting the tool name is difficult, but the condition serves no purpose and makes the code misleading. Consider simplifying to just toolName: undefined to make the intent clear, or actually extracting the tool name if it's available in the execution context.

Suggested change
toolName: exec.result.hook.matcher ? undefined : undefined, // Tool name is in context, not easily accessible here
toolName: undefined, // Tool name is in context, not easily accessible here

Fix it with Roo Code or mention @roomote and request a fix.

taltas added 16 commits January 16, 2026 17:10
Update Hooks UI to explicitly document that matchers use Roo Code internal tool IDs (e.g. write_to_file, execute_command) rather than display labels.
Add regression test ensuring display labels do not unintentionally match internal IDs.
Add open project/global folder buttons at bottom
Move reload to bottom
Add enable-all toggle (with new message type)
Convert hooks list to accordion + per-hook logs
- Add switch, status dot, and delete button to HooksSettings UI
- Implement backend support for hook deletion
- Add safeWriteText utility
- Update translation strings
- Add UI and backend tests for new functionality
Implement hooks experimental flag with conditional UI rendering and backend functionality gating.
Fix missing experimental settings translations for hooks feature.
Fix hook discovery issue by initializing HookManager when experiment is toggled on.
Move the periodic hooks configuration reload from the HooksSettings component to the main App component. This ensures that hooks are reloaded every 5 seconds whenever the feature is enabled, regardless of which settings tab is currently active.
- Add 'Create New Hook' button above 'Open Global Folder' button
- Remove borders from hook items to match flat MCP styling
- Implement hooksCreateNew message handler to create example.yaml
- Fix YAML template indentation to use standard 2-space format
- Add translation strings for new UI elements
- Add hooksEnabled check to all ToolExecutionHooks methods
- Hooks do not execute when master toggle is off
- Update Task.ts to inject hooksEnabled getter
- Add hooksEnabled to GlobalSettings schema and ExtensionState
- UI hides hook list when master toggle is disabled
- Add comprehensive tests for toggle enforcement (15 tests)
Add hook ID to each activity log entry for easier identification of which
hook was triggered. Styled with monospace font and link color.
Add hook-related UI strings to all 17 supported locales:
- de, es, fr, ca, hi, id, it, ja, ko, nl, pl, pt-BR, ru, tr, vi, zh-CN, zh-TW

Includes translations for:
- hooks section (~50 keys)
- experimental.HOOKS feature toggle
Replace Pickaxe with FishingHook icon from lucide-react.
Upgrade lucide-react from 0.518.0 to 0.562.0 for FishingHook support.
Remove hooks from experimental flags and make it a core feature:
- Remove HOOKS from ExperimentId enum and experimentsSchema
- Initialize HookManager unconditionally in ClineProvider
- Remove experiment checks from webviewMessageHandler
- Always show Hooks tab in SettingsView
- Add dynamic initialization with file watchers (no restart required)
- Update translations to remove restart requirement text
- Add comprehensive tests for dynamic hook initialization

The master 'Enable Hooks' toggle in HooksSettings controls execution.
- Add getToolsForGroup() helper to expand group names to tool lists
- Update HookMatcher to support group names like "edit", "read", "browser"
- Group names are case-insensitive and expand to all tools in the group
- Unknown group names log a warning but don't break the hook
- Add comprehensive tests for group expansion functionality

Supported groups: read, edit, browser, command, mcp, modes
Comment on lines 33 to 37
} else {
// Not a group, keep as-is, but warn if it looks like it was intended as a group
console.warn(`Unknown tool group "${pattern}". Treating as literal tool name.`)
return pattern
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This console.warn is triggered for every pattern that isn't a known group name, including valid tool names like read_file, apply_diff, etc. The comment says "warn if it looks like it was intended as a group" but the code warns unconditionally. This will cause console spam when users configure hooks for specific tools.

Consider removing the warning entirely, or only warning for patterns that closely resemble group names (e.g., levenshtein distance to known groups).

Suggested change
} else {
// Not a group, keep as-is, but warn if it looks like it was intended as a group
console.warn(`Unknown tool group "${pattern}". Treating as literal tool name.`)
return pattern
}
} else {
// Not a group - this is a normal tool name, keep as-is
return pattern
}

Fix it with Roo Code or mention @roomote and request a fix.

…group matchers

- Replace raw config display with interactive controls for Events, Matchers, and Timeout

- Add backend support for updating hook configurations via hooksUpdateHook message

- Implement atomic YAML/JSON writing with event migration logic (moving hooks between keys)

- Add multi-select checkboxes for lifecycle events (PreToolUse, PostToolUse, etc.)

- Add tool group matchers (read, edit, etc.) with custom pattern fallback

- Add timeout preset dropdown (15s - 60m)

- Add comprehensive tests for config writing and UI interaction
- Update hooksSetAllEnabled tests to match global hooksEnabled toggle behavior\n- Add IHookManager.updateHook to test mocks after interface change\n- Narrow hooksUpdateHook events to HookEventType via schema validation
…ase 2)

- Refactor YAML structure to be hook-centric (array-based) instead of event-keyed

- Remove version key from schema and code

- Add "Copy Hook" functionality to duplicate hooks

- Enhance Settings UI:

  - Add event tooltips with descriptions

  - Add editable hook ID field with validation

  - Add multi-line command editing

  - Add "Copy" button

- Update config loader to support both new and legacy formats (read-only compatibility)

- Update config writer to always emit new format

- Add comprehensive tests for migration, copy logic, and new UI components
- Keep multiple events for same hook id when definitions match\n- Expand matcher groups when combined with | (e.g. read|edit)\n- Add regression tests for both cases
Send hook-centric enabledHooks state to webview (one entry per id) and expose aggregated events[] for UI rendering. Adds regression test for ClineProvider hooks state shaping.
Stream hook stdout/stderr into chat with terminal-style HookExecution blocks.\n\nAdds hookExecutionOutputStatus protocol and persisted hook_execution rows with truncated summaries.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Enhancement New feature or request size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

Status: Triage

Development

Successfully merging this pull request may close these issues.

1 participant