claude-flow-novice 2.18.22 → 2.18.24
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.
- package/.claude/CLAUDE.md +17 -0
- package/.claude/cfn-scripts/check-memory.sh +150 -0
- package/.claude/cfn-scripts/run-with-memory-limit.sh +91 -0
- package/.claude/commands/cfn-loop/cfn-loop-cli.md +9 -0
- package/.claude/commands/cfn-loop-task.md +11 -0
- package/.claude/commands/cfn-ruvector/cfn-codebase-reindex.md +23 -4
- package/.claude/commands/cfn-ruvector/cfn-codebase-search.md +10 -2
- package/.claude/commands/cfn-ruvector/cfn-detect-stale-docs.md +22 -4
- package/.claude/hooks/README.md +148 -148
- package/.claude/hooks/cfn-bash-search-hook.sh +87 -0
- package/.claude/hooks/cfn-smart-search-hook.sh +127 -0
- package/.claude/hooks/deprecated/cfn-SessionStart-cfn-load-openai-key.sh +48 -0
- package/.claude/hooks/post-commit-codebase-index +79 -45
- package/.claude/settings.json +20 -11
- package/.claude/skills/CLAUDE.md +70 -0
- package/.claude/skills/cfn-edit-safety/lib/hooks/security-scanner.sh +1 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/SKILL.md +37 -21
- package/.claude/skills/cfn-local-ruvector-accelerator/cfn-integration.sh +47 -6
- package/.claude/skills/cfn-local-ruvector-accelerator/src/cli/index.rs +2 -1
- package/.claude/skills/cfn-local-ruvector-accelerator/src/cli/init.rs +3 -3
- package/.claude/skills/cfn-local-ruvector-accelerator/src/cli/query.rs +1 -1
- package/.claude/skills/cfn-local-ruvector-accelerator/src/lib.rs +1 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/src/paths.rs +4 -2
- package/.claude/skills/cfn-local-ruvector-accelerator/src/search_engine.rs +11 -0
- package/.claude/skills/cfn-local-ruvector-accelerator/test_query_api.sh +102 -102
- package/CLAUDE.md +63 -373
- package/docs/CFN_LOOP_CLI_MODE.md +134 -0
- package/package.json +9 -5
- package/scripts/cfn-init.js +8 -2
- package/scripts/organize-root-files.sh +340 -340
- package/scripts/postinstall.js +120 -3
- package/test-epic-creator-security.sh +202 -202
- package/.claude/hooks/SessionStart:cfn-build-ruvector.sh +0 -28
- package/.claude/hooks/SessionStart:cfn-load-openai-key.sh +0 -35
- /package/.claude/hooks/{SessionStart-cfn-build-ruvector.sh → cfn-SessionStart-cfn-build-ruvector.sh} +0 -0
- /package/.claude/hooks/{cfn-load-cerebras-env.sh → deprecated/cfn-load-cerebras-env.sh} +0 -0
package/.claude/hooks/README.md
CHANGED
|
@@ -1,148 +1,148 @@
|
|
|
1
|
-
# Claude Code Hooks
|
|
2
|
-
|
|
3
|
-
This directory contains hooks that integrate with Claude Code's lifecycle events.
|
|
4
|
-
|
|
5
|
-
## cfn-precompact-enhanced.sh
|
|
6
|
-
|
|
7
|
-
Enhanced PreCompact hook that preserves context before conversation compaction (manual or auto).
|
|
8
|
-
|
|
9
|
-
### Features
|
|
10
|
-
|
|
11
|
-
**Context Preservation:**
|
|
12
|
-
- Captures current git state (branch, status, recent commits)
|
|
13
|
-
- Saves uncommitted and staged changes
|
|
14
|
-
- Records modified files
|
|
15
|
-
|
|
16
|
-
**Session Metrics:**
|
|
17
|
-
- Counts recently edited files
|
|
18
|
-
- Estimates session duration from git history
|
|
19
|
-
- Detects test execution and status
|
|
20
|
-
- Tracks tool usage (if transcript available)
|
|
21
|
-
- Approximates token usage
|
|
22
|
-
|
|
23
|
-
**CFN-Specific Detection:**
|
|
24
|
-
- Active CFN Loop tasks
|
|
25
|
-
- Running CFN Docker containers
|
|
26
|
-
- Redis coordination state
|
|
27
|
-
- Recent pre-edit backups
|
|
28
|
-
|
|
29
|
-
**Output:**
|
|
30
|
-
- Prints structured summary to stdout (injected into Claude's context)
|
|
31
|
-
- Saves detailed JSON log to `.artifacts/precompact/session-{timestamp}.json`
|
|
32
|
-
- Includes CLAUDE.md reminders for best practices
|
|
33
|
-
|
|
34
|
-
### Configuration
|
|
35
|
-
|
|
36
|
-
The hook is configured in `.claude/settings.json`:
|
|
37
|
-
|
|
38
|
-
```json
|
|
39
|
-
{
|
|
40
|
-
"hooks": {
|
|
41
|
-
"PreCompact": [
|
|
42
|
-
{
|
|
43
|
-
"matcher": "manual",
|
|
44
|
-
"hooks": [
|
|
45
|
-
{
|
|
46
|
-
"type": "command",
|
|
47
|
-
"command": "/bin/bash .claude/hooks/cfn-precompact-enhanced.sh"
|
|
48
|
-
}
|
|
49
|
-
]
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
"matcher": "auto",
|
|
53
|
-
"hooks": [
|
|
54
|
-
{
|
|
55
|
-
"type": "command",
|
|
56
|
-
"command": "/bin/bash .claude/hooks/cfn-precompact-enhanced.sh"
|
|
57
|
-
}
|
|
58
|
-
]
|
|
59
|
-
}
|
|
60
|
-
]
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
### Performance
|
|
66
|
-
|
|
67
|
-
- **Timeout Protection**: 9-second overall script timeout
|
|
68
|
-
- **Non-Blocking**: Always exits with code 0
|
|
69
|
-
- **Optimized for WSL2**: Limits `find` depth and targets specific directories
|
|
70
|
-
- **Graceful Degradation**: Continues if git/jq unavailable
|
|
71
|
-
|
|
72
|
-
### Testing
|
|
73
|
-
|
|
74
|
-
```bash
|
|
75
|
-
# Test without stdin
|
|
76
|
-
./.claude/hooks/cfn-precompact-enhanced.sh
|
|
77
|
-
|
|
78
|
-
# Test with simulated input
|
|
79
|
-
echo '{"type": "manual", "compact_type": "manual"}' | \
|
|
80
|
-
CLAUDE_PROJECT_DIR=$(pwd) bash .claude/hooks/cfn-precompact-enhanced.sh
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
### Output Example
|
|
84
|
-
|
|
85
|
-
```
|
|
86
|
-
=== PRE-COMPACT CONTEXT PRESERVATION ===
|
|
87
|
-
|
|
88
|
-
Compact Type: manual
|
|
89
|
-
Timestamp: 2025-12-10 04:46:26
|
|
90
|
-
|
|
91
|
-
Git State:
|
|
92
|
-
Branch: feature/auth-system
|
|
93
|
-
Uncommitted: 3 files
|
|
94
|
-
Modified: 2 files
|
|
95
|
-
Staged: 1 files
|
|
96
|
-
Last commit: abc123 - "Add JWT validation" (2 hours ago)
|
|
97
|
-
|
|
98
|
-
Session Summary:
|
|
99
|
-
Recent file edits: 5
|
|
100
|
-
Duration: 45 minutes
|
|
101
|
-
Tests run: true
|
|
102
|
-
Test status: passing
|
|
103
|
-
Tools used: 12 (Bash: 8, Edit: 4)
|
|
104
|
-
Approx tokens: ~800
|
|
105
|
-
|
|
106
|
-
Key Context:
|
|
107
|
-
- Working directory: /path/to/project
|
|
108
|
-
- Uncommitted changes
|
|
109
|
-
- 2 CFN container(s) running
|
|
110
|
-
|
|
111
|
-
Full context saved to: .artifacts/precompact/session-1765370786.json
|
|
112
|
-
|
|
113
|
-
=== CLAUDE.md REMINDERS ===
|
|
114
|
-
• Use CFN Loop for multi-step tasks
|
|
115
|
-
• Batch operations in single messages
|
|
116
|
-
• Pre-edit backup required before edits
|
|
117
|
-
• Run tests before commits
|
|
118
|
-
• Use service names in Docker networks
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
### Artifacts
|
|
122
|
-
|
|
123
|
-
Session data is preserved in:
|
|
124
|
-
- `.artifacts/precompact/session-{timestamp}.json` - Full JSON context
|
|
125
|
-
- `.artifacts/precompact/{timestamp}-git.diff` - Git diff (if changes exist)
|
|
126
|
-
- `.artifacts/precompact/{timestamp}-commits.txt` - Recent commit history
|
|
127
|
-
- `.artifacts/precompact/backup-{todo-file}-{timestamp}` - Copied todo files
|
|
128
|
-
|
|
129
|
-
### Environment Variables
|
|
130
|
-
|
|
131
|
-
- `CLAUDE_PROJECT_DIR` - Project root (defaults to `pwd`)
|
|
132
|
-
- `TRANSCRIPT_PATH` - Path to conversation transcript (optional, for tool usage stats)
|
|
133
|
-
- `INPUT` - JSON input from Claude Code (compact_type, custom_instructions)
|
|
134
|
-
|
|
135
|
-
### Troubleshooting
|
|
136
|
-
|
|
137
|
-
**Hook times out:**
|
|
138
|
-
- Check if running on Windows mount (should be Linux filesystem)
|
|
139
|
-
- Verify timeout settings in script (default 9 seconds)
|
|
140
|
-
- Reduce `find` depth or target fewer directories
|
|
141
|
-
|
|
142
|
-
**No JSON output:**
|
|
143
|
-
- Ensure `.artifacts/precompact/` directory exists
|
|
144
|
-
- Check for `jq` availability (gracefully degrades if missing)
|
|
145
|
-
|
|
146
|
-
**Line ending issues:**
|
|
147
|
-
- Convert to Unix line endings: `dos2unix .claude/hooks/cfn-precompact-enhanced.sh`
|
|
148
|
-
- Or use: `sed -i 's/\r$//' .claude/hooks/cfn-precompact-enhanced.sh`
|
|
1
|
+
# Claude Code Hooks
|
|
2
|
+
|
|
3
|
+
This directory contains hooks that integrate with Claude Code's lifecycle events.
|
|
4
|
+
|
|
5
|
+
## cfn-precompact-enhanced.sh
|
|
6
|
+
|
|
7
|
+
Enhanced PreCompact hook that preserves context before conversation compaction (manual or auto).
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
**Context Preservation:**
|
|
12
|
+
- Captures current git state (branch, status, recent commits)
|
|
13
|
+
- Saves uncommitted and staged changes
|
|
14
|
+
- Records modified files
|
|
15
|
+
|
|
16
|
+
**Session Metrics:**
|
|
17
|
+
- Counts recently edited files
|
|
18
|
+
- Estimates session duration from git history
|
|
19
|
+
- Detects test execution and status
|
|
20
|
+
- Tracks tool usage (if transcript available)
|
|
21
|
+
- Approximates token usage
|
|
22
|
+
|
|
23
|
+
**CFN-Specific Detection:**
|
|
24
|
+
- Active CFN Loop tasks
|
|
25
|
+
- Running CFN Docker containers
|
|
26
|
+
- Redis coordination state
|
|
27
|
+
- Recent pre-edit backups
|
|
28
|
+
|
|
29
|
+
**Output:**
|
|
30
|
+
- Prints structured summary to stdout (injected into Claude's context)
|
|
31
|
+
- Saves detailed JSON log to `.artifacts/precompact/session-{timestamp}.json`
|
|
32
|
+
- Includes CLAUDE.md reminders for best practices
|
|
33
|
+
|
|
34
|
+
### Configuration
|
|
35
|
+
|
|
36
|
+
The hook is configured in `.claude/settings.json`:
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"hooks": {
|
|
41
|
+
"PreCompact": [
|
|
42
|
+
{
|
|
43
|
+
"matcher": "manual",
|
|
44
|
+
"hooks": [
|
|
45
|
+
{
|
|
46
|
+
"type": "command",
|
|
47
|
+
"command": "/bin/bash .claude/hooks/cfn-precompact-enhanced.sh"
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"matcher": "auto",
|
|
53
|
+
"hooks": [
|
|
54
|
+
{
|
|
55
|
+
"type": "command",
|
|
56
|
+
"command": "/bin/bash .claude/hooks/cfn-precompact-enhanced.sh"
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Performance
|
|
66
|
+
|
|
67
|
+
- **Timeout Protection**: 9-second overall script timeout
|
|
68
|
+
- **Non-Blocking**: Always exits with code 0
|
|
69
|
+
- **Optimized for WSL2**: Limits `find` depth and targets specific directories
|
|
70
|
+
- **Graceful Degradation**: Continues if git/jq unavailable
|
|
71
|
+
|
|
72
|
+
### Testing
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Test without stdin
|
|
76
|
+
./.claude/hooks/cfn-precompact-enhanced.sh
|
|
77
|
+
|
|
78
|
+
# Test with simulated input
|
|
79
|
+
echo '{"type": "manual", "compact_type": "manual"}' | \
|
|
80
|
+
CLAUDE_PROJECT_DIR=$(pwd) bash .claude/hooks/cfn-precompact-enhanced.sh
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Output Example
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
=== PRE-COMPACT CONTEXT PRESERVATION ===
|
|
87
|
+
|
|
88
|
+
Compact Type: manual
|
|
89
|
+
Timestamp: 2025-12-10 04:46:26
|
|
90
|
+
|
|
91
|
+
Git State:
|
|
92
|
+
Branch: feature/auth-system
|
|
93
|
+
Uncommitted: 3 files
|
|
94
|
+
Modified: 2 files
|
|
95
|
+
Staged: 1 files
|
|
96
|
+
Last commit: abc123 - "Add JWT validation" (2 hours ago)
|
|
97
|
+
|
|
98
|
+
Session Summary:
|
|
99
|
+
Recent file edits: 5
|
|
100
|
+
Duration: 45 minutes
|
|
101
|
+
Tests run: true
|
|
102
|
+
Test status: passing
|
|
103
|
+
Tools used: 12 (Bash: 8, Edit: 4)
|
|
104
|
+
Approx tokens: ~800
|
|
105
|
+
|
|
106
|
+
Key Context:
|
|
107
|
+
- Working directory: /path/to/project
|
|
108
|
+
- Uncommitted changes
|
|
109
|
+
- 2 CFN container(s) running
|
|
110
|
+
|
|
111
|
+
Full context saved to: .artifacts/precompact/session-1765370786.json
|
|
112
|
+
|
|
113
|
+
=== CLAUDE.md REMINDERS ===
|
|
114
|
+
• Use CFN Loop for multi-step tasks
|
|
115
|
+
• Batch operations in single messages
|
|
116
|
+
• Pre-edit backup required before edits
|
|
117
|
+
• Run tests before commits
|
|
118
|
+
• Use service names in Docker networks
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Artifacts
|
|
122
|
+
|
|
123
|
+
Session data is preserved in:
|
|
124
|
+
- `.artifacts/precompact/session-{timestamp}.json` - Full JSON context
|
|
125
|
+
- `.artifacts/precompact/{timestamp}-git.diff` - Git diff (if changes exist)
|
|
126
|
+
- `.artifacts/precompact/{timestamp}-commits.txt` - Recent commit history
|
|
127
|
+
- `.artifacts/precompact/backup-{todo-file}-{timestamp}` - Copied todo files
|
|
128
|
+
|
|
129
|
+
### Environment Variables
|
|
130
|
+
|
|
131
|
+
- `CLAUDE_PROJECT_DIR` - Project root (defaults to `pwd`)
|
|
132
|
+
- `TRANSCRIPT_PATH` - Path to conversation transcript (optional, for tool usage stats)
|
|
133
|
+
- `INPUT` - JSON input from Claude Code (compact_type, custom_instructions)
|
|
134
|
+
|
|
135
|
+
### Troubleshooting
|
|
136
|
+
|
|
137
|
+
**Hook times out:**
|
|
138
|
+
- Check if running on Windows mount (should be Linux filesystem)
|
|
139
|
+
- Verify timeout settings in script (default 9 seconds)
|
|
140
|
+
- Reduce `find` depth or target fewer directories
|
|
141
|
+
|
|
142
|
+
**No JSON output:**
|
|
143
|
+
- Ensure `.artifacts/precompact/` directory exists
|
|
144
|
+
- Check for `jq` availability (gracefully degrades if missing)
|
|
145
|
+
|
|
146
|
+
**Line ending issues:**
|
|
147
|
+
- Convert to Unix line endings: `dos2unix .claude/hooks/cfn-precompact-enhanced.sh`
|
|
148
|
+
- Or use: `sed -i 's/\r$//' .claude/hooks/cfn-precompact-enhanced.sh`
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
INPUT=$(timeout 1 cat || echo "{}")
|
|
5
|
+
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
|
|
6
|
+
|
|
7
|
+
log() { echo "[$(date '+%H:%M:%S')] $*" >> /tmp/ruvector-bash-hook.log; }
|
|
8
|
+
|
|
9
|
+
# Load API key from .env if not set
|
|
10
|
+
load_api_key() {
|
|
11
|
+
if [[ -n "${OPENAI_API_KEY:-}" ]] && [[ "${OPENAI_API_KEY:-}" != "your_"* ]]; then
|
|
12
|
+
return 0
|
|
13
|
+
fi
|
|
14
|
+
local env_file="${CLAUDE_PROJECT_DIR:-.}/.env"
|
|
15
|
+
if [[ -f "$env_file" ]]; then
|
|
16
|
+
local key=$(grep "^OPENAI_API_KEY=" "$env_file" 2>/dev/null | cut -d= -f2- | tr -d '"' | tr -d "'" || true)
|
|
17
|
+
if [[ -n "$key" ]] && [[ "$key" != "your_"* ]]; then
|
|
18
|
+
export OPENAI_API_KEY="$key"
|
|
19
|
+
return 0
|
|
20
|
+
fi
|
|
21
|
+
fi
|
|
22
|
+
return 1
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
log "Bash hook: $CMD"
|
|
26
|
+
|
|
27
|
+
if echo "$CMD" | grep -qE "find\s+/mnt/c"; then
|
|
28
|
+
echo "🔴 BLOCKED: find on /mnt/c forbidden (memory leak). Use Glob tool instead." >&2
|
|
29
|
+
exit 2
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
if ! echo "$CMD" | grep -qiE "^\s*(grep|rg|find)\s|[|&;]\s*(grep|rg|find)\s"; then
|
|
33
|
+
exit 0
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
PATTERN=""
|
|
37
|
+
|
|
38
|
+
PATTERN=$(echo "$CMD" | grep -oE '(grep|rg)\s+[^|]+' | grep -oE '"[^"]+"' | head -1 | tr -d '"' || true)
|
|
39
|
+
|
|
40
|
+
if [ -z "$PATTERN" ]; then
|
|
41
|
+
PATTERN=$(echo "$CMD" | grep -oE "(grep|rg)\s+(-[a-zA-Z]+\s+)*([a-zA-Z_][a-zA-Z0-9_]*)" | awk '{print $NF}' || true)
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
if [ -z "$PATTERN" ]; then
|
|
45
|
+
PATTERN=$(echo "$CMD" | grep -oE '\-name\s+"[^"]+"' | sed 's/-name\s*"//' | tr -d '"' || true)
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
if [ -z "$PATTERN" ] || [ ${#PATTERN} -lt 3 ] || [[ "$PATTERN" == -* ]]; then
|
|
49
|
+
exit 0
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
DB_PATH="$HOME/.local/share/ruvector/index_v2.db"
|
|
53
|
+
PROJECT_ROOT="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
54
|
+
|
|
55
|
+
CONTEXT=""
|
|
56
|
+
|
|
57
|
+
# Try SQL first (fast, no API key)
|
|
58
|
+
if [ -f "$DB_PATH" ]; then
|
|
59
|
+
SAFE_PATTERN=$(echo "$PATTERN" | sed "s/'/''/g")
|
|
60
|
+
SAFE_ROOT=$(echo "$PROJECT_ROOT" | sed "s/'/''/g")
|
|
61
|
+
|
|
62
|
+
RESULTS=$(timeout 3 sqlite3 -separator ':' "$DB_PATH" \
|
|
63
|
+
"SELECT REPLACE(file_path, '$SAFE_ROOT/', ''), line_number, name FROM entities WHERE project_root = '$SAFE_ROOT' AND (name LIKE '%${SAFE_PATTERN}%' OR file_path LIKE '%${SAFE_PATTERN}%') LIMIT 6" 2>/dev/null || true)
|
|
64
|
+
|
|
65
|
+
if [ -n "$RESULTS" ]; then
|
|
66
|
+
CONTEXT="RuVector indexed matches for '$PATTERN':\n$RESULTS"
|
|
67
|
+
log "SQL context injected for: $PATTERN"
|
|
68
|
+
fi
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
# Fallback to semantic search if SQL found nothing
|
|
72
|
+
if [ -z "$CONTEXT" ] && command -v local-ruvector >/dev/null 2>&1; then
|
|
73
|
+
if load_api_key; then
|
|
74
|
+
log "SQL returned nothing, trying semantic search for: $PATTERN"
|
|
75
|
+
SEMANTIC=$(timeout 5 local-ruvector query "$PATTERN" --max-results 5 --threshold 0.3 2>/dev/null | sed 's/\x1b\[[0-9;]*m//g' | grep -v "^$" | grep -v "INFO\|ERROR\|WARN" | head -6 || true)
|
|
76
|
+
if [ -n "$SEMANTIC" ]; then
|
|
77
|
+
CONTEXT="RuVector semantic matches for '$PATTERN':\n$SEMANTIC"
|
|
78
|
+
log "Semantic context injected for: $PATTERN"
|
|
79
|
+
fi
|
|
80
|
+
fi
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
if [ -n "$CONTEXT" ]; then
|
|
84
|
+
echo "{\"additionalContext\":\"$CONTEXT\"}"
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
exit 0
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
exec 2>/tmp/ruvector-search-hook.log
|
|
4
|
+
|
|
5
|
+
INPUT=$(timeout 1 cat || echo "{}")
|
|
6
|
+
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty')
|
|
7
|
+
PATTERN=$(echo "$INPUT" | jq -r '.tool_input.pattern // empty')
|
|
8
|
+
|
|
9
|
+
log() {
|
|
10
|
+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> /tmp/ruvector-search-hook.log
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
# Load API key from .env if not set
|
|
14
|
+
load_api_key() {
|
|
15
|
+
if [[ -n "${OPENAI_API_KEY:-}" ]] && [[ "${OPENAI_API_KEY:-}" != "your_"* ]]; then
|
|
16
|
+
return 0
|
|
17
|
+
fi
|
|
18
|
+
local env_file="${CLAUDE_PROJECT_DIR:-.}/.env"
|
|
19
|
+
if [[ -f "$env_file" ]]; then
|
|
20
|
+
local key=$(grep "^OPENAI_API_KEY=" "$env_file" 2>/dev/null | cut -d= -f2- | tr -d '"' | tr -d "'" || true)
|
|
21
|
+
if [[ -n "$key" ]] && [[ "$key" != "your_"* ]]; then
|
|
22
|
+
export OPENAI_API_KEY="$key"
|
|
23
|
+
log "Loaded OPENAI_API_KEY from .env"
|
|
24
|
+
return 0
|
|
25
|
+
fi
|
|
26
|
+
fi
|
|
27
|
+
return 1
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
log "Hook triggered: tool=$TOOL_NAME pattern='$PATTERN'"
|
|
31
|
+
|
|
32
|
+
# Skip if no pattern or tool not Grep/Glob
|
|
33
|
+
if [[ -z "$PATTERN" || -z "$TOOL_NAME" ]]; then
|
|
34
|
+
log "Missing pattern or tool name, exiting"
|
|
35
|
+
exit 0
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
# Skip conditions
|
|
39
|
+
if [[ ${#PATTERN} -lt 3 ]]; then
|
|
40
|
+
log "Pattern too short, skipping"
|
|
41
|
+
exit 0
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
# Skip glob patterns (file discovery, not semantic)
|
|
45
|
+
if [[ "$PATTERN" == *"*"* ]] || [[ "$PATTERN" == *"?"* ]] || [[ "$PATTERN" == *"["* ]]; then
|
|
46
|
+
log "Pattern looks like glob, skipping"
|
|
47
|
+
exit 0
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
# Skip exact paths (contains / and . extension)
|
|
51
|
+
if [[ "$PATTERN" == *"/"* ]] && [[ "$PATTERN" == *"."* ]]; then
|
|
52
|
+
log "Pattern looks like exact path, skipping"
|
|
53
|
+
exit 0
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
CONTEXT=""
|
|
57
|
+
UNCOMMITTED=""
|
|
58
|
+
|
|
59
|
+
# Check uncommitted files
|
|
60
|
+
if command -v git >/dev/null 2>&1; then
|
|
61
|
+
UNCOMMITTED=$(timeout 2 git diff --name-only HEAD 2>/dev/null | grep -i "$PATTERN" || true)
|
|
62
|
+
if [[ -n "$UNCOMMITTED" ]]; then
|
|
63
|
+
log "Found uncommitted matches"
|
|
64
|
+
CONTEXT="Uncommitted files matching pattern:
|
|
65
|
+
$UNCOMMITTED
|
|
66
|
+
|
|
67
|
+
"
|
|
68
|
+
fi
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
# Query RuVector V2 SQL first (no API key needed)
|
|
72
|
+
RUVECTOR_RESULTS=""
|
|
73
|
+
DB_PATH="$HOME/.local/share/ruvector/index_v2.db"
|
|
74
|
+
PROJECT_ROOT="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
75
|
+
if [[ -f "$DB_PATH" ]]; then
|
|
76
|
+
log "Querying RuVector SQL for: $PATTERN (project: $PROJECT_ROOT)"
|
|
77
|
+
# Escape pattern for SQL LIKE
|
|
78
|
+
SAFE_PATTERN=$(echo "$PATTERN" | sed "s/'/''/g")
|
|
79
|
+
SAFE_ROOT=$(echo "$PROJECT_ROOT" | sed "s/'/''/g")
|
|
80
|
+
RUVECTOR_RESULTS=$(timeout 3 sqlite3 -separator ':' "$DB_PATH" \
|
|
81
|
+
"SELECT REPLACE(file_path, '$SAFE_ROOT/', ''), line_number, name FROM entities WHERE project_root = '$SAFE_ROOT' AND (name LIKE '%${SAFE_PATTERN}%' OR file_path LIKE '%${SAFE_PATTERN}%') LIMIT 8" 2>/dev/null | head -10 || true)
|
|
82
|
+
if [[ -n "$RUVECTOR_RESULTS" ]]; then
|
|
83
|
+
log "RuVector SQL returned results"
|
|
84
|
+
CONTEXT="${CONTEXT}RuVector indexed matches for '$PATTERN':
|
|
85
|
+
$RUVECTOR_RESULTS
|
|
86
|
+
|
|
87
|
+
"
|
|
88
|
+
fi
|
|
89
|
+
else
|
|
90
|
+
log "RuVector index not found at $DB_PATH"
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
# Fallback to semantic search if SQL found nothing and API key available
|
|
94
|
+
if [[ -z "$RUVECTOR_RESULTS" ]] && command -v local-ruvector >/dev/null 2>&1; then
|
|
95
|
+
if load_api_key; then
|
|
96
|
+
log "SQL returned nothing, trying semantic search for: $PATTERN"
|
|
97
|
+
# Strip ANSI codes from output
|
|
98
|
+
SEMANTIC_RESULTS=$(timeout 5 local-ruvector query "$PATTERN" --max-results 5 --threshold 0.3 2>/dev/null | sed 's/\x1b\[[0-9;]*m//g' | grep -v "^$" | grep -v "INFO\|ERROR\|WARN" | head -8 || true)
|
|
99
|
+
if [[ -n "$SEMANTIC_RESULTS" ]]; then
|
|
100
|
+
log "Semantic search returned results"
|
|
101
|
+
CONTEXT="${CONTEXT}RuVector semantic matches for '$PATTERN':
|
|
102
|
+
$SEMANTIC_RESULTS
|
|
103
|
+
|
|
104
|
+
"
|
|
105
|
+
else
|
|
106
|
+
log "Semantic search returned no results"
|
|
107
|
+
fi
|
|
108
|
+
else
|
|
109
|
+
log "No API key available for semantic search"
|
|
110
|
+
fi
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
# Output context if we have any
|
|
114
|
+
if [[ -n "$CONTEXT" ]]; then
|
|
115
|
+
# Try JSON output first
|
|
116
|
+
if command -v jq >/dev/null 2>&1; then
|
|
117
|
+
echo "$INPUT" | jq --arg context "$CONTEXT" '. + {additionalContext: $context}'
|
|
118
|
+
else
|
|
119
|
+
# Fallback to plain text
|
|
120
|
+
echo "$CONTEXT"
|
|
121
|
+
fi
|
|
122
|
+
log "Context injected successfully"
|
|
123
|
+
else
|
|
124
|
+
log "No additional context found"
|
|
125
|
+
fi
|
|
126
|
+
|
|
127
|
+
exit 0
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# SessionStart hook: Load OpenAI API key from root .env file
|
|
4
|
+
# This ensures OPENAI_API_KEY is available for embedding generation
|
|
5
|
+
#
|
|
6
|
+
# IMPORTANT: SessionStart hooks must write to CLAUDE_ENV_FILE to set env vars.
|
|
7
|
+
# JSON output and 'export' do NOT work - only CLAUDE_ENV_FILE persists.
|
|
8
|
+
|
|
9
|
+
set -e
|
|
10
|
+
|
|
11
|
+
# Path to root .env file
|
|
12
|
+
ROOT_ENV="${PROJECT_ROOT:-.}/.env"
|
|
13
|
+
|
|
14
|
+
# Check if CLAUDE_ENV_FILE is available (only in SessionStart hooks)
|
|
15
|
+
if [[ -z "$CLAUDE_ENV_FILE" ]]; then
|
|
16
|
+
echo "⚠️ CLAUDE_ENV_FILE not set - not running as SessionStart hook" >&2
|
|
17
|
+
exit 0
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
# Check if .env exists
|
|
21
|
+
if [[ ! -f "$ROOT_ENV" ]]; then
|
|
22
|
+
echo "⚠️ Warning: $ROOT_ENV not found. OpenAI embeddings will not work." >&2
|
|
23
|
+
exit 0
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
# Extract OPENAI_API_KEY from .env
|
|
27
|
+
if grep -q "^OPENAI_API_KEY=" "$ROOT_ENV"; then
|
|
28
|
+
OPENAI_KEY=$(grep "^OPENAI_API_KEY=" "$ROOT_ENV" | cut -d'=' -f2- | tr -d '"' | tr -d "'")
|
|
29
|
+
|
|
30
|
+
# Validate key format
|
|
31
|
+
if [[ -z "$OPENAI_KEY" ]]; then
|
|
32
|
+
echo "⚠️ Warning: OPENAI_API_KEY found but empty in $ROOT_ENV" >&2
|
|
33
|
+
exit 0
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
if [[ ! "$OPENAI_KEY" =~ ^sk- ]]; then
|
|
37
|
+
echo "⚠️ Warning: OPENAI_API_KEY invalid format (must start with 'sk-')" >&2
|
|
38
|
+
exit 0
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
# Write to CLAUDE_ENV_FILE - this is how SessionStart hooks set env vars
|
|
42
|
+
echo "export OPENAI_API_KEY=\"$OPENAI_KEY\"" >> "$CLAUDE_ENV_FILE"
|
|
43
|
+
echo "✅ Loaded OPENAI_API_KEY from .env (${OPENAI_KEY:0:10}...)" >&2
|
|
44
|
+
else
|
|
45
|
+
echo "⚠️ Warning: OPENAI_API_KEY not found in $ROOT_ENV. OpenAI embeddings will not work." >&2
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
exit 0
|