claude_hooks 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +33 -0
- data/README.md +86 -58
- data/example_dotclaude/hooks/entrypoints/user_prompt_submit.rb +2 -2
- data/lib/claude_hooks/base.rb +18 -2
- data/lib/claude_hooks/configuration.rb +97 -13
- data/lib/claude_hooks/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 518b315ef2a184008b91029cdb9735b77ceee67490618d9581be16ff0c42cdb2
|
4
|
+
data.tar.gz: c469281a8f36dce611c1aad3f60611d5951219d1718846739d711fc310219c6f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c500ced26412c6666d19cbef211afe43b89dcaa1b6282f6fdeb5ec24874981621d3ef7b742a1c3fc45d9f68c91e838d0c16a6e90fb63c81a6f90facfb0f23e11
|
7
|
+
data.tar.gz: db4d7cc2ca66d8d6cb7c3582cda5433f2b6882998f600a43cda170f58951246fd5394704b4ce6136c461138160c509d0e5bbec3390530329b754b11aa62911dc
|
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,39 @@ All notable changes to this project will be documented in this file.
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
7
|
|
8
|
+
## [0.2.0] - 2025-08-21
|
9
|
+
|
10
|
+
### Added
|
11
|
+
- **Dual Configuration System**: Support for both home-level (`$HOME/.claude`) and project-level (`$CLAUDE_PROJECT_DIR/.claude`) configurations
|
12
|
+
- **Configuration Merging**: Intelligent merging of home and project configs with configurable precedence
|
13
|
+
- New environment variable `CLAUDE_HOOKS_CONFIG_MERGE_STRATEGY` to control merge behavior ("project" or "home")
|
14
|
+
- New directory access methods: `home_claude_dir`, `project_claude_dir`
|
15
|
+
- New path utility methods: `home_path_for(path)`, `project_path_for(path)`
|
16
|
+
- Enhanced `path_for(path, base_dir=nil)` method with optional base directory parameter
|
17
|
+
- Comprehensive test suite for configuration functionality (`test/` directory)
|
18
|
+
- Configuration validation and edge case handling for missing `CLAUDE_PROJECT_DIR`
|
19
|
+
|
20
|
+
### Changed
|
21
|
+
- **Logs Location**: Logs now always go to `$HOME/.claude/{logDirectory}` regardless of active configuration
|
22
|
+
- Configuration loading now supports dual config file locations with intelligent merging
|
23
|
+
- Enhanced documentation with comprehensive dual configuration examples
|
24
|
+
- Updated API reference with new directory and path methods
|
25
|
+
|
26
|
+
### Deprecated
|
27
|
+
- `base_dir` method (still functional for backward compatibility)
|
28
|
+
- `RUBY_CLAUDE_HOOKS_BASE_DIR` environment variable (still supported as fallback)
|
29
|
+
|
30
|
+
### Fixed
|
31
|
+
- Graceful handling of undefined `CLAUDE_PROJECT_DIR` environment variable
|
32
|
+
- Proper path resolution when project directory is not available
|
33
|
+
- Backward compatibility maintained for all existing hook scripts
|
34
|
+
|
35
|
+
### Migration Notes
|
36
|
+
- Existing configurations continue to work without changes
|
37
|
+
- New projects can leverage dual configuration system
|
38
|
+
- `base_dir` and legacy `path_for` methods remain functional
|
39
|
+
- Environment variables maintain same precedence over config files
|
40
|
+
|
8
41
|
## [0.1.0] - 2025-08-17
|
9
42
|
|
10
43
|
### Added
|
data/README.md
CHANGED
@@ -4,6 +4,8 @@ A Ruby DSL (Domain Specific Language) for creating Claude Code hooks. This will
|
|
4
4
|
|
5
5
|
[**Why use this instead of writing bash, or simple ruby scripts?**](WHY.md)
|
6
6
|
|
7
|
+
> You might also be interested in my other project, a [Claude Code statusline](https://github.com/gabriel-dehan/claude_monitor_statusline) that shows your Claude usage in realtime ✨.
|
8
|
+
|
7
9
|
## 🚀 Quick Start
|
8
10
|
|
9
11
|
> [!TIP]
|
@@ -44,10 +46,11 @@ if __FILE__ == $0
|
|
44
46
|
end
|
45
47
|
```
|
46
48
|
|
47
|
-
3. ⚠️ **Make it executable
|
49
|
+
3. ⚠️ **Make it executable**
|
48
50
|
```bash
|
49
51
|
chmod +x add_context_after_prompt.rb
|
50
|
-
|
52
|
+
# Test it
|
53
|
+
echo '{"session_id":"test","prompt":"Hello!"}' | ./add_context_after_prompt.rb
|
51
54
|
```
|
52
55
|
|
53
56
|
4. **Register it in your `.claude/settings.json`**
|
@@ -87,6 +90,7 @@ Or add it to your Gemfile (you can add a Gemfile in your `.claude` directory if
|
|
87
90
|
```ruby
|
88
91
|
# .claude/Gemfile
|
89
92
|
source 'https://rubygems.org'
|
93
|
+
|
90
94
|
gem 'claude_hooks'
|
91
95
|
```
|
92
96
|
|
@@ -101,63 +105,91 @@ $ bundle install
|
|
101
105
|
|
102
106
|
### 🔧 Configuration
|
103
107
|
|
104
|
-
|
105
|
-
|
108
|
+
Claude Hooks supports both home-level (`$HOME/.claude`) and project-level (`$CLAUDE_PROJECT_DIR/.claude`) directories. Claude Hooks specific config files (`config/config.json`) found in either directory will be merged together.
|
106
109
|
|
107
|
-
|
110
|
+
| Directory | Description | Purpose |
|
111
|
+
|-----------|-------------|---------|
|
112
|
+
| `$HOME/.claude` | Home Claude directory | Global user settings and logs |
|
113
|
+
| `$CLAUDE_PROJECT_DIR/.claude` | Project Claude directory | Project-specific settings |
|
108
114
|
|
109
|
-
|
110
|
-
|
111
|
-
| `baseDir` | Base directory for all Claude files | `~/.claude` |
|
112
|
-
| `logDirectory` | Directory for logs (relative to baseDir) | `logs` |
|
115
|
+
> [!NOTE]
|
116
|
+
> Logs always go to `$HOME/.claude/{logDirectory}`
|
113
117
|
|
114
|
-
#### Environment Variables
|
118
|
+
#### Environment Variables
|
115
119
|
|
116
|
-
|
120
|
+
You can configure Claude Hooks through environment variables with the `RUBY_CLAUDE_HOOKS_` prefix:
|
117
121
|
|
118
122
|
```bash
|
119
|
-
|
120
|
-
export RUBY_CLAUDE_HOOKS_LOG_DIR="logs" # Default: logs (relative to
|
123
|
+
# Existing configuration options
|
124
|
+
export RUBY_CLAUDE_HOOKS_LOG_DIR="logs" # Default: logs (relative to HOME/.claude)
|
125
|
+
export CLAUDE_HOOKS_CONFIG_MERGE_STRATEGY="project" # Config merge strategy: "project" or "home", default: "project"
|
126
|
+
export RUBY_CLAUDE_HOOKS_BASE_DIR="~/.claude" # Deprecated: fallback base directory
|
121
127
|
|
122
|
-
#
|
128
|
+
# Any variable prefixed with RUBY_CLAUDE_HOOKS_ will also be available through the config object
|
123
129
|
export RUBY_CLAUDE_HOOKS_API_KEY="your-api-key"
|
124
130
|
export RUBY_CLAUDE_HOOKS_DEBUG_MODE="true"
|
125
131
|
export RUBY_CLAUDE_HOOKS_USER_NAME="Gabriel"
|
126
132
|
```
|
127
133
|
|
128
|
-
#### Configuration
|
134
|
+
#### Configuration Files
|
129
135
|
|
130
|
-
You can
|
131
|
-
The gem will read from it as fallback for any missing environment variables.
|
136
|
+
You can also use configuration files in any of the two locations:
|
132
137
|
|
138
|
+
**Home config** (`$HOME/.claude/config/config.json`):
|
133
139
|
```json
|
134
140
|
{
|
135
|
-
|
141
|
+
// Existing configuration option
|
136
142
|
"logDirectory": "logs",
|
137
|
-
|
138
|
-
"
|
143
|
+
// Custom configuration options
|
144
|
+
"apiKey": "your-global-api-key",
|
139
145
|
"userName": "Gabriel"
|
140
146
|
}
|
141
147
|
```
|
142
148
|
|
143
|
-
|
149
|
+
**Project config** (`$CLAUDE_PROJECT_DIR/.claude/config/config.json`):
|
150
|
+
```json
|
151
|
+
{
|
152
|
+
// Custom configuration option
|
153
|
+
"projectSpecificConfig": "someValue",
|
154
|
+
}
|
155
|
+
```
|
156
|
+
|
157
|
+
#### Configuration Merging
|
158
|
+
|
159
|
+
When both config files exist, they will be merged with configurable precedence:
|
160
|
+
|
161
|
+
- **Default (`project`)**: Project config values override home config values
|
162
|
+
- **Home precedence (`home`)**: Home config values override project config values
|
163
|
+
|
164
|
+
Set merge strategy: `export CLAUDE_HOOKS_CONFIG_MERGE_STRATEGY="home" | "project"` (default: "project")
|
165
|
+
|
166
|
+
> [!WARNING]
|
167
|
+
> Environment Variables > Merged Config Files
|
168
|
+
|
169
|
+
#### Accessing Configuration Variables
|
144
170
|
|
145
171
|
You can access any configuration value in your handlers:
|
146
172
|
|
147
173
|
```ruby
|
148
174
|
class MyHandler < ClaudeHooks::UserPromptSubmit
|
149
175
|
def call
|
150
|
-
# Access
|
151
|
-
log "
|
176
|
+
# Access directory paths
|
177
|
+
log "Home Claude dir: #{home_claude_dir}"
|
178
|
+
log "Project Claude dir: #{project_claude_dir}" # nil if CLAUDE_PROJECT_DIR not set
|
179
|
+
log "Base dir (deprecated): #{base_dir}"
|
152
180
|
log "Logs dir: #{config.logs_directory}"
|
153
181
|
|
182
|
+
# Path utilities
|
183
|
+
log "Home config path: #{home_path_for('config')}"
|
184
|
+
log "Project hooks path: #{project_path_for('hooks')}" # nil if no project dir
|
185
|
+
|
154
186
|
# Access custom config via method calls
|
155
187
|
log "API Key: #{config.api_key}"
|
156
188
|
log "Debug mode: #{config.debug_mode}"
|
157
189
|
log "User: #{config.user_name}"
|
158
190
|
|
159
191
|
# Or use get_config_value for more control
|
160
|
-
user_name = config.get_config_value('USER_NAME', 'userName'
|
192
|
+
user_name = config.get_config_value('USER_NAME', 'userName')
|
161
193
|
log "Username: #{user_name}"
|
162
194
|
|
163
195
|
output_data
|
@@ -165,18 +197,16 @@ class MyHandler < ClaudeHooks::UserPromptSubmit
|
|
165
197
|
end
|
166
198
|
```
|
167
199
|
|
168
|
-
**Configuration Priority:** Environment variables always take precedence over config file values.
|
169
|
-
|
170
200
|
## 📖 Table of Contents
|
171
201
|
|
172
202
|
- [Ruby DSL for Claude Code hooks](#ruby-dsl-for-claude-code-hooks)
|
173
203
|
- [🚀 Quick Start](#-quick-start)
|
174
204
|
- [📦 Installation](#-installation)
|
175
205
|
- [🔧 Configuration](#-configuration)
|
176
|
-
- [
|
177
|
-
- [
|
178
|
-
- [Configuration
|
179
|
-
- [Accessing
|
206
|
+
- [Environment Variables](#environment-variables)
|
207
|
+
- [Configuration Files](#configuration-files)
|
208
|
+
- [Configuration Merging](#configuration-merging)
|
209
|
+
- [Accessing Configuration Variables](#accessing-configuration-variables)
|
180
210
|
- [📖 Table of Contents](#-table-of-contents)
|
181
211
|
- [🏗️ Architecture](#️-architecture)
|
182
212
|
- [Core Components](#core-components)
|
@@ -184,7 +214,7 @@ end
|
|
184
214
|
- [🪝 Hook Types](#-hook-types)
|
185
215
|
- [🚀 Claude Hook Flow](#-claude-hook-flow)
|
186
216
|
- [A very simplified view of how a hook works in Claude Code](#a-very-simplified-view-of-how-a-hook-works-in-claude-code)
|
187
|
-
- [🔄 Claude Hook
|
217
|
+
- [🔄 Proposal: a more robust Claude Hook execution flow](#-proposal-a-more-robust-claude-hook-execution-flow)
|
188
218
|
- [Basic Hook Handler Structure](#basic-hook-handler-structure)
|
189
219
|
- [Input Fields](#input-fields)
|
190
220
|
- [📚 API Reference](#-api-reference)
|
@@ -192,7 +222,9 @@ end
|
|
192
222
|
- [Input Methods](#input-methods)
|
193
223
|
- [Output Methods](#output-methods)
|
194
224
|
- [Class Output Methods](#class-output-methods)
|
225
|
+
- [Configuration and Utility Methods](#configuration-and-utility-methods)
|
195
226
|
- [Utility Methods](#utility-methods)
|
227
|
+
- [Configuration Methods](#configuration-methods)
|
196
228
|
- [UserPromptSubmit API](#userpromptsubmit-api)
|
197
229
|
- [Input Methods](#input-methods-1)
|
198
230
|
- [Output Methods](#output-methods-1)
|
@@ -218,9 +250,6 @@ end
|
|
218
250
|
- [SessionStart API](#sessionstart-api)
|
219
251
|
- [Input Methods](#input-methods-8)
|
220
252
|
- [Output Methods](#output-methods-8)
|
221
|
-
- [Configuration and Utility Methods](#configuration-and-utility-methods)
|
222
|
-
- [Configuration Methods](#configuration-methods)
|
223
|
-
- [Utility Methods](#utility-methods-2)
|
224
253
|
- [📝 Logging](#-logging)
|
225
254
|
- [Log File Location](#log-file-location)
|
226
255
|
- [Log Output Format](#log-output-format)
|
@@ -298,10 +327,10 @@ The framework supports the following hook types:
|
|
298
327
|
|
299
328
|
```mermaid
|
300
329
|
graph LR
|
301
|
-
|
330
|
+
A[Hook triggers] --> B[JSON from STDIN] --> C[Hook does its thing] --> D[JSON to STDOUT or STDERR] --> E[Yields back to Claude Code] --> A
|
302
331
|
```
|
303
332
|
|
304
|
-
### 🔄 Claude Hook
|
333
|
+
### 🔄 Proposal: a more robust Claude Hook execution flow
|
305
334
|
|
306
335
|
1. An entrypoint for a hook is set in `~/.claude/settings.json`
|
307
336
|
2. Claude Code calls the entrypoint script (e.g., `hooks/entrypoints/pre_tool_use.rb`)
|
@@ -318,8 +347,8 @@ graph TD
|
|
318
347
|
C --> D[📋 Entrypoint<br />Parses JSON from STDIN]
|
319
348
|
D --> E[📋 Entrypoint<br />Calls hook handlers]
|
320
349
|
|
321
|
-
E --> F[📝 AppendContextRules.call<br/><em>Returns output_data</em>]
|
322
|
-
E --> G[📝 PromptGuard.call<br/><em>Returns output_data</em>]
|
350
|
+
E --> F[📝 Handler<br />AppendContextRules.call<br/><em>Returns output_data</em>]
|
351
|
+
E --> G[📝 Handler<br />PromptGuard.call<br/><em>Returns output_data</em>]
|
323
352
|
|
324
353
|
F --> J[📋 Entrypoint<br />Calls _ClaudeHooks::UserPromptSubmit.merge_outputs_ to 🔀 merge outputs]
|
325
354
|
G --> J
|
@@ -422,11 +451,30 @@ Each hook type provides a **class method** `merge_outputs` that will try to inte
|
|
422
451
|
|--------|-------------|
|
423
452
|
| `merge_outputs(*outputs_data)` | Intelligently merge multiple outputs into a single output |
|
424
453
|
|
454
|
+
### Configuration and Utility Methods
|
455
|
+
|
456
|
+
Available in all hooks via the base `ClaudeHooks::Base` class:
|
457
|
+
|
425
458
|
#### Utility Methods
|
426
459
|
| Method | Description |
|
427
460
|
|--------|-------------|
|
428
461
|
| `log(message, level: :info)` | Log to session-specific file (levels: :info, :warn, :error) |
|
429
462
|
|
463
|
+
#### Configuration Methods
|
464
|
+
| Method | Description |
|
465
|
+
|--------|-------------|
|
466
|
+
| `home_claude_dir` | Get the home Claude directory (`$HOME/.claude`) |
|
467
|
+
| `project_claude_dir` | Get the project Claude directory (`$CLAUDE_PROJECT_DIR/.claude`, or `nil`) |
|
468
|
+
| `home_path_for(relative_path)` | Get absolute path relative to home Claude directory |
|
469
|
+
| `project_path_for(relative_path)` | Get absolute path relative to project Claude directory (or `nil`) |
|
470
|
+
| `base_dir` | Get the base Claude directory (**deprecated**) |
|
471
|
+
| `path_for(relative_path, base_dir=nil)` | Get absolute path relative to specified or default base dir (**deprecated**) |
|
472
|
+
| `config` | Access the merged configuration object |
|
473
|
+
| `config.get_config_value(env_key, config_file_key, default)` | Get any config value with fallback |
|
474
|
+
| `config.logs_directory` | Get logs directory path (always under home directory) |
|
475
|
+
| `config.your_custom_key` | Access any custom config via method_missing |
|
476
|
+
|
477
|
+
|
430
478
|
### UserPromptSubmit API
|
431
479
|
|
432
480
|
Available when inheriting from `ClaudeHooks::UserPromptSubmit`:
|
@@ -560,26 +608,6 @@ Available when inheriting from `ClaudeHooks::SessionStart`:
|
|
560
608
|
| `add_context!(context)` | Alias for `add_additional_context!` |
|
561
609
|
| `empty_additional_context!` | Clear additional context |
|
562
610
|
|
563
|
-
### Configuration and Utility Methods
|
564
|
-
|
565
|
-
Available in all hooks via the base `ClaudeHooks::Base` class:
|
566
|
-
|
567
|
-
#### Configuration Methods
|
568
|
-
| Method | Description |
|
569
|
-
|--------|-------------|
|
570
|
-
| `base_dir` | Get the base Claude directory |
|
571
|
-
| `path_for(relative_path)` | Get absolute path relative to base dir |
|
572
|
-
| `config` | Access the full configuration object |
|
573
|
-
| `config.get_config_value(env_key, config_key, default)` | Get any config value with fallback |
|
574
|
-
| `config.logs_directory` | Get logs directory path |
|
575
|
-
| `config.your_custom_key` | Access any custom config via method_missing |
|
576
|
-
|
577
|
-
#### Utility Methods
|
578
|
-
| Method | Description |
|
579
|
-
|--------|-------------|
|
580
|
-
| `log(message, level: :info)` | Log to session-specific file (levels: :info, :warn, :error) |
|
581
|
-
| `log(level: :info) { block }` | Multiline logging with block support |
|
582
|
-
|
583
611
|
### 📝 Logging
|
584
612
|
|
585
613
|
`ClaudeHooks::Base` provides a **session logger** that will write logs to session-specific files.
|
@@ -2,8 +2,8 @@
|
|
2
2
|
|
3
3
|
require 'claude_hooks'
|
4
4
|
require 'json'
|
5
|
-
require_relative '../user_prompt_submit/append_rules'
|
6
|
-
require_relative '../user_prompt_submit/log_user_prompt'
|
5
|
+
require_relative '../handlers/user_prompt_submit/append_rules'
|
6
|
+
require_relative '../handlers/user_prompt_submit/log_user_prompt'
|
7
7
|
|
8
8
|
begin
|
9
9
|
# Read input from stdin
|
data/lib/claude_hooks/base.rb
CHANGED
@@ -111,8 +111,24 @@ module ClaudeHooks
|
|
111
111
|
config.base_dir
|
112
112
|
end
|
113
113
|
|
114
|
-
def
|
115
|
-
config.
|
114
|
+
def home_claude_dir
|
115
|
+
config.home_claude_dir
|
116
|
+
end
|
117
|
+
|
118
|
+
def project_claude_dir
|
119
|
+
config.project_claude_dir
|
120
|
+
end
|
121
|
+
|
122
|
+
def path_for(relative_path, base_directory = nil)
|
123
|
+
config.path_for(relative_path, base_directory)
|
124
|
+
end
|
125
|
+
|
126
|
+
def home_path_for(relative_path)
|
127
|
+
config.home_path_for(relative_path)
|
128
|
+
end
|
129
|
+
|
130
|
+
def project_path_for(relative_path)
|
131
|
+
config.project_path_for(relative_path)
|
116
132
|
end
|
117
133
|
|
118
134
|
# Supports both single messages and blocks for multiline logging
|
@@ -16,29 +16,75 @@ module ClaudeHooks
|
|
16
16
|
def reload!
|
17
17
|
@config = nil
|
18
18
|
@base_dir = nil
|
19
|
+
@home_claude_dir = nil
|
20
|
+
@project_claude_dir = nil
|
19
21
|
@config_file_path = nil
|
22
|
+
@home_config_file_path = nil
|
23
|
+
@project_config_file_path = nil
|
20
24
|
end
|
21
25
|
|
22
|
-
# Get the
|
26
|
+
# Get the home Claude directory (always ~/.claude)
|
27
|
+
def home_claude_dir
|
28
|
+
@home_claude_dir ||= File.expand_path('~/.claude')
|
29
|
+
end
|
30
|
+
|
31
|
+
# Get the project Claude directory (from CLAUDE_PROJECT_DIR/.claude)
|
32
|
+
# Returns nil if CLAUDE_PROJECT_DIR environment variable is not set
|
33
|
+
def project_claude_dir
|
34
|
+
@project_claude_dir ||= begin
|
35
|
+
project_dir = ENV['CLAUDE_PROJECT_DIR']
|
36
|
+
if project_dir
|
37
|
+
File.expand_path(File.join(project_dir, '.claude'))
|
38
|
+
else
|
39
|
+
nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Get the base directory from ENV or default (backward compatibility)
|
45
|
+
# This method will determine which base directory to use based on context
|
23
46
|
def base_dir
|
24
47
|
@base_dir ||= begin
|
48
|
+
# Check for legacy environment variable first
|
25
49
|
env_base_dir = ENV["#{ENV_PREFIX}BASE_DIR"]
|
26
|
-
|
50
|
+
if env_base_dir
|
51
|
+
File.expand_path(env_base_dir)
|
52
|
+
else
|
53
|
+
# Default to home directory for backward compatibility
|
54
|
+
home_claude_dir
|
55
|
+
end
|
27
56
|
end
|
28
57
|
end
|
29
58
|
|
30
59
|
# Get the full path for a file/directory relative to base_dir
|
31
|
-
|
32
|
-
|
60
|
+
# Can optionally specify which base directory to use
|
61
|
+
def path_for(relative_path, base_directory = nil)
|
62
|
+
base_directory ||= base_dir
|
63
|
+
File.join(base_directory, relative_path)
|
33
64
|
end
|
34
65
|
|
35
|
-
# Get the
|
66
|
+
# Get the full path for a file/directory relative to home_claude_dir
|
67
|
+
def home_path_for(relative_path)
|
68
|
+
File.join(home_claude_dir, relative_path)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Get the full path for a file/directory relative to project_claude_dir
|
72
|
+
# Returns nil if CLAUDE_PROJECT_DIR environment variable is not set
|
73
|
+
def project_path_for(relative_path)
|
74
|
+
if project_claude_dir
|
75
|
+
File.join(project_claude_dir, relative_path)
|
76
|
+
else
|
77
|
+
nil
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Get the log directory path (always relative to home_claude_dir)
|
36
82
|
def logs_directory
|
37
83
|
log_dir = get_config_value('LOG_DIR', 'logDirectory') || 'logs'
|
38
84
|
if log_dir.start_with?('/')
|
39
85
|
log_dir # Absolute path
|
40
86
|
else
|
41
|
-
|
87
|
+
File.join(home_claude_dir, log_dir) # Always relative to home_claude_dir
|
42
88
|
end
|
43
89
|
end
|
44
90
|
|
@@ -92,25 +138,63 @@ module ClaudeHooks
|
|
92
138
|
@config_file_path ||= path_for('config/config.json')
|
93
139
|
end
|
94
140
|
|
141
|
+
def home_config_file_path
|
142
|
+
@home_config_file_path ||= File.join(home_claude_dir, 'config/config.json')
|
143
|
+
end
|
144
|
+
|
145
|
+
def project_config_file_path
|
146
|
+
@project_config_file_path ||= begin
|
147
|
+
if project_claude_dir
|
148
|
+
File.join(project_claude_dir, 'config/config.json')
|
149
|
+
else
|
150
|
+
nil
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
95
155
|
def load_config
|
96
|
-
#
|
97
|
-
|
156
|
+
# Load and merge config files from both locations
|
157
|
+
merged_file_config = load_and_merge_config_files
|
98
158
|
|
99
159
|
# Merge with ENV variables
|
100
160
|
env_config = load_env_config
|
101
161
|
|
102
|
-
# ENV variables take precedence
|
103
|
-
|
162
|
+
# ENV variables take precedence over file configs
|
163
|
+
merged_file_config.merge(env_config)
|
164
|
+
end
|
165
|
+
|
166
|
+
def load_and_merge_config_files
|
167
|
+
home_config = load_config_file_from_path(home_config_file_path)
|
168
|
+
project_config = load_config_file_from_path(project_config_file_path) if project_config_file_path
|
169
|
+
|
170
|
+
# Determine merge strategy
|
171
|
+
merge_strategy = ENV['CLAUDE_HOOKS_CONFIG_MERGE_STRATEGY'] || 'project'
|
172
|
+
|
173
|
+
if project_config && merge_strategy == 'project'
|
174
|
+
# Project config takes precedence
|
175
|
+
home_config.merge(project_config)
|
176
|
+
elsif project_config && merge_strategy == 'home'
|
177
|
+
# Home config takes precedence
|
178
|
+
project_config.merge(home_config)
|
179
|
+
else
|
180
|
+
# Only home config exists or no project config
|
181
|
+
home_config
|
182
|
+
end
|
104
183
|
end
|
105
184
|
|
106
185
|
def load_config_file
|
107
186
|
config_file = config_file_path
|
187
|
+
load_config_file_from_path(config_file)
|
188
|
+
end
|
189
|
+
|
190
|
+
def load_config_file_from_path(config_file_path)
|
191
|
+
return {} unless config_file_path
|
108
192
|
|
109
|
-
if File.exist?(
|
193
|
+
if File.exist?(config_file_path)
|
110
194
|
begin
|
111
|
-
JSON.parse(File.read(
|
195
|
+
JSON.parse(File.read(config_file_path))
|
112
196
|
rescue JSON::ParserError => e
|
113
|
-
warn "Warning: Error parsing config file #{
|
197
|
+
warn "Warning: Error parsing config file #{config_file_path}: #{e.message}"
|
114
198
|
{}
|
115
199
|
end
|
116
200
|
else
|
data/lib/claude_hooks/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: claude_hooks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gabriel Dehan
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-08-
|
11
|
+
date: 2025-08-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|