You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: aihook/AGENTS.md
+60-23Lines changed: 60 additions & 23 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,25 +2,34 @@
2
2
3
3
## Project Overview
4
4
5
-
`aihook` is a validator for Claude Code hooks that enforces shell scripting best practices. The primary use case is validating that `cd` commands are always executed within subshells.
5
+
`aihook` is a toolkit for Claude Code hooks that provides validators and behavior modifiers. It includes:
6
+
- Shell validation hook to enforce `cd` commands are always executed within subshells
7
+
- Prevent-stop hook to prevent Claude from stopping prematurely
8
+
- Global `--install` flag to add hooks to `~/.claude/settings.json`
6
9
7
10
## Project Structure
8
11
9
12
```
10
13
aihook/
11
14
├── main.go # CLI entry point (Cobra setup)
12
15
├── pkg/
16
+
│ ├── installer/
17
+
│ │ ├── installer.go # Hook installation to ~/.claude/settings.json
18
+
│ │ └── installer_test.go # Installer tests
13
19
│ └── validator/
14
20
│ ├── validator.go # Core validation logic
15
21
│ └── validator_test.go # Comprehensive unit tests
22
+
├── hook_response_test.go # Tests for hook response functions
23
+
├── integration_test.go # Integration tests
16
24
├── README.md # User documentation
17
25
└── AGENTS.md # This file
18
26
```
19
27
20
28
**Separation of concerns:**
21
-
-`main.go`: Cobra CLI setup, flag handling, output formatting
29
+
-`main.go`: Cobra CLI setup, flag handling, output formatting, hook commands
22
30
-`pkg/validator`: Pure validation logic (AST walking, cd detection)
23
-
- Tests are in the validator package, testing the pure logic
31
+
-`pkg/installer`: Installation logic for adding hooks to Claude settings
32
+
- Tests are in each package, testing the pure logic
24
33
25
34
## Development Guidelines
26
35
@@ -33,35 +42,50 @@ Always run tests and build from the repository root:
33
42
go test ./aihook/...
34
43
35
44
# Build
36
-
go build ./aihook
45
+
go build -o /tmp/aihook ./aihook
37
46
38
47
# Run
39
-
./aihook shell < script.sh
48
+
/tmp/aihook shell < script.sh
49
+
/tmp/aihook prevent-stop --install
40
50
```
41
51
42
52
### Code Structure
43
53
44
-
-`main.go` (86 lines) - CLI entry point with Cobra framework
-`InstallHook()` function to add hooks to `~/.claude/settings.json`
68
+
- Handles creating the settings file if it doesn't exist
69
+
- Prevents duplicate hook installation
70
+
71
+
### Available Commands
72
+
73
+
1.**`shell`**: Validates Bash commands to ensure `cd` is only used in subshells
74
+
-`--claude`: Output in JSON format
75
+
-`--block-on-cd`: Deny execution when violations found
76
+
77
+
2.**`prevent-stop`**: Prevents Claude from stopping prematurely
78
+
- Returns `continue: false` with a message telling Claude to keep working
79
+
80
+
3.**`--install` (global flag)**: Installs the hook to `~/.claude/settings.json`
58
81
59
82
### Key Implementation Details
60
83
61
84
1.**Shell Parsing**: Uses `mvdan.cc/sh/v3/syntax` for accurate shell script parsing
62
85
2.**AST Walking**: Traverses the syntax tree to find `cd` commands and track subshell context
63
86
3.**Subshell Detection**: Handles both explicit subshells `(...)` and command substitutions `$(...)`
64
87
4.**Output Formats**: Supports both human-readable and Claude Code JSON formats
88
+
5.**Hook Installation**: Modifies `~/.claude/settings.json` to add hooks
65
89
66
90
### Testing Philosophy
67
91
@@ -72,6 +96,9 @@ Tests cover:
72
96
- Edge cases (loops, conditionals, functions)
73
97
- Invalid shell syntax
74
98
- Multiple cd commands in one script
99
+
- Hook installation to settings file
100
+
- Duplicate installation prevention
101
+
- Hook response JSON structure
75
102
76
103
All tests must pass before any changes are committed.
77
104
@@ -81,29 +108,39 @@ All tests must pass before any changes are committed.
81
108
2.**Command Substitution**: `$(...)` IS a subshell and cd is allowed inside
82
109
3.**Here Documents**: Content inside here-docs should not be parsed as commands
83
110
84
-
### Claude Code Hook Format
111
+
### Claude Code Hook Response Format
85
112
86
-
When `--claude` flag is used, output must be JSON with:
87
-
-`exit_code`: Integer (0 for success, 2 for violations, 1 for errors)
88
-
-`message`: String containing the human-readable message
113
+
For PreToolUse hooks:
114
+
```json
115
+
{
116
+
"continue": true,
117
+
"hookSpecificOutput": {
118
+
"hookEventName": "PreToolUse",
119
+
"permissionDecision": "allow|deny",
120
+
"permissionDecisionReason": "..."
121
+
}
122
+
}
123
+
```
89
124
90
-
Example:
125
+
For Stop hooks (prevent-stop):
91
126
```json
92
127
{
93
-
"exit_code": 2,
94
-
"message": "Found cd commands outside subshells:\n Line 1: 'cd' command found outside subshell\n\nAll 'cd' commands must be in a subshell. Example:\n # Bad: cd /tmp && ls\n # Good: (cd /tmp && ls)\n"
128
+
"continue": false,
129
+
"stopReason": "Keep working!",
130
+
"systemMessage": "Continue working on the task..."
95
131
}
96
132
```
97
133
98
134
## Adding New Hook Types
99
135
100
-
When adding new hook types in the future:
136
+
When adding new hook types:
101
137
102
138
1. Add a new subcommand in `main.go`
103
139
2. Create a new validator in `pkg/validator` if needed
104
-
3. Add comprehensive unit tests in the validator package
105
-
4. Update README.md with usage examples
106
-
5. Update this AGENTS.md with implementation notes
140
+
3. Add support in `pkg/installer` for the new event type
141
+
4. Add comprehensive unit tests
142
+
5. Update README.md with usage examples
143
+
6. Update this AGENTS.md with implementation notes
Copy file name to clipboardExpand all lines: aihook/README.md
+84-24Lines changed: 84 additions & 24 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,22 +1,18 @@
1
1
# aihook
2
2
3
-
A validator for Claude Code hooks that enforces shell scripting best practices.
3
+
A toolkit for Claude Code hooks including validators and behavior modifiers.
4
4
5
5
## Overview
6
6
7
-
`aihook` is a command-line tool designed to validate shell scripts for use in Claude Code PreToolUse hooks. It parses shell syntax and enforces specific rules to ensure code quality and consistency.
7
+
`aihook` is a command-line tool that provides various hooks for Claude Code. It can validate shell scripts and modify Claude's behavior (like preventing premature stopping).
8
8
9
9
## Features
10
10
11
-
-**PreToolUse Hook**: Validates Bash commands before execution to forbid `cd` invocations outside subshells
11
+
-**Shell Validation Hook**: Validates Bash commands before execution to forbid `cd` invocations outside subshells
12
+
-**Prevent-Stop Hook**: Prevents Claude from stopping prematurely, forcing it to continue working
13
+
-**Global Install**: Use `--install` flag to add hooks to `~/.claude/settings.json` automatically
12
14
-**Shell Parser**: Uses `mvdan.cc/sh/v3/syntax` for accurate shell script parsing
13
-
-**Claude Code Integration**: Supports `--claude` flag for JSON output compatible with Claude Code hooks
-`--install`: Install hook to global Claude settings (`~/.claude/settings.json`) instead of running it
103
+
104
+
Shell hook flags:
66
105
-`--claude`: Output in JSON format compatible with Claude Code hooks
67
106
-`--block-on-cd`: When set, returns exit code 2 for violations (blocks execution). Without this flag, violations are reported but execution is not blocked (exit code 0)
68
107
@@ -87,14 +126,20 @@ All 'cd' commands must be in a subshell. Example:
87
126
#### Claude Code Hook Format (`--claude`)
88
127
```json
89
128
{
90
-
"exit_code": 2,
91
-
"message": "Found cd commands outside subshells:\n Line 1: 'cd' command found outside subshell\n\nAll 'cd' commands must be in a subshell. Example:\n # Bad: cd /tmp && ls\n # Good: (cd /tmp && ls)\n"
129
+
"continue": true,
130
+
"hookSpecificOutput": {
131
+
"hookEventName": "PreToolUse",
132
+
"permissionDecision": "deny",
133
+
"permissionDecisionReason": "Found cd commands outside subshells..."
134
+
}
92
135
}
93
136
```
94
137
95
138
## Claude Code Integration
96
139
97
-
To use `aihook` as a Claude Code PreToolUse hook that validates Bash commands, add this to your `.claude/settings.json`:
140
+
### Manual Configuration
141
+
142
+
To use `aihook` as a Claude Code hook, add this to your `.claude/settings.json`:
98
143
99
144
```json
100
145
{
@@ -109,20 +154,35 @@ To use `aihook` as a Claude Code PreToolUse hook that validates Bash commands, a
109
154
}
110
155
]
111
156
}
157
+
],
158
+
"Stop": [
159
+
{
160
+
"matcher": "stop",
161
+
"hooks": [
162
+
{
163
+
"type": "command",
164
+
"command": "aihook prevent-stop"
165
+
}
166
+
]
167
+
}
112
168
]
113
169
}
114
170
}
115
171
```
116
172
117
-
The hook will:
118
-
- Match any Bash tool invocation (via the `"Bash"` matcher)
119
-
- Receive the bash command script on stdin
120
-
- Validate that all `cd` commands are in subshells
121
-
- With `--block-on-cd`: Return exit code 2 to block execution when violations are found
122
-
- Without `--block-on-cd`: Report violations but allow execution (exit code 0)
123
-
- Use `--claude` flag to output in the expected JSON format
173
+
### Automatic Installation
174
+
175
+
Use the `--install` flag to automatically add hooks to your global Claude settings:
176
+
177
+
```bash
178
+
# Install prevent-stop hook
179
+
aihook prevent-stop --install
180
+
181
+
# Install shell hook with cd blocking
182
+
aihook shell --install --block-on-cd
183
+
```
124
184
125
-
For more flexible matching, you can use regex patterns like `"Bash.*cd"` to only check Bash commands containing `cd`.
185
+
The hooks will be added to `~/.claude/settings.json`, creating the file if it doesn't exist.
126
186
127
187
## Why Forbid cd Outside Subshells?
128
188
@@ -143,13 +203,13 @@ By requiring `cd` in subshells `(cd /path && command)`, you ensure:
0 commit comments