Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions plugins/hookify/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,11 +235,19 @@ Use environment variables instead of hardcoded values.
### Operators Reference

- `regex_match`: Pattern must match (most common)
- `contains`: String must contain pattern
- `not_regex_match`: Pattern must NOT match (for exclusion patterns)
- `contains`: String must contain value
- `equals`: Exact string match
- `not_contains`: String must NOT contain pattern
- `starts_with`: String starts with pattern
- `ends_with`: String ends with pattern
- `not_contains`: String must NOT contain value
- `starts_with`: String starts with value
- `ends_with`: String ends with value

**Note:** For non-regex operators, you can use `value` instead of `pattern` for clarity:
```yaml
- field: command
operator: contains
value: --force # More intuitive than "pattern" for literal strings
```

### Field Reference

Expand Down
10 changes: 8 additions & 2 deletions plugins/hookify/core/config_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,17 @@ class Condition:

@classmethod
def from_dict(cls, data: Dict[str, Any]) -> 'Condition':
"""Create Condition from dict."""
"""Create Condition from dict.

Supports both 'pattern' and 'value' keys for flexibility.
The 'value' key is more intuitive for non-regex operators.
"""
# Support both 'pattern' and 'value' keys
pattern = data.get('pattern') or data.get('value', '')
return cls(
field=data.get('field', ''),
operator=data.get('operator', 'regex_match'),
pattern=data.get('pattern', '')
pattern=pattern
)


Expand Down
2 changes: 2 additions & 0 deletions plugins/hookify/core/rule_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ def _check_condition(self, condition: Condition, tool_name: str,

if operator == 'regex_match':
return self._regex_match(pattern, field_value)
elif operator == 'not_regex_match':
return not self._regex_match(pattern, field_value)
elif operator == 'contains':
return pattern in field_value
elif operator == 'equals':
Expand Down
24 changes: 24 additions & 0 deletions plugins/hookify/examples/exclude-test-files.local.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
# Example: Warn on console.log, but exclude test/story files
name: console-log-prod-only
enabled: true
event: file
action: warn
conditions:
- field: file_path
operator: regex_match
pattern: \.(tsx?|jsx?)$
- field: file_path
operator: not_regex_match
pattern: (\.test\.|\.spec\.|\.stories\.|__mocks__|__tests__)
- field: new_text
operator: regex_match
pattern: console\.(log|debug|info)\(
---

**Console statement in production code!**

Remove `console.log/debug/info` before committing.

These are allowed in test and story files, which is why this rule uses
`not_regex_match` to exclude them.
7 changes: 5 additions & 2 deletions plugins/hookify/skills/writing-rules/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,13 @@ You're adding an API key to a .env file. Ensure this file is in .gitignore!
- For file: `file_path`, `new_text`, `old_text`, `content`
- `operator`: How to match
- `regex_match`: Regex pattern matching
- `not_regex_match`: Regex must NOT match (for exclusion patterns)
- `contains`: Substring check
- `equals`: Exact match
- `not_contains`: Substring must NOT be present
- `starts_with`: Prefix check
- `ends_with`: Suffix check
- `pattern`: Pattern or string to match
- `pattern` or `value`: Pattern or string to match (use `value` for non-regex operators for clarity)

**All conditions must match for rule to trigger.**

Expand Down Expand Up @@ -371,4 +372,6 @@ Warning message
- Prompt: `user_prompt`

**Operators:**
- `regex_match`, `contains`, `equals`, `not_contains`, `starts_with`, `ends_with`
- `regex_match`, `not_regex_match`, `contains`, `equals`, `not_contains`, `starts_with`, `ends_with`

**Tip:** Use `value` instead of `pattern` for non-regex operators for better readability.