moai-adk 0.9.0__py3-none-any.whl → 0.9.1__py3-none-any.whl
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.
Potentially problematic release.
This version of moai-adk might be problematic. Click here for more details.
- moai_adk/cli/commands/update.py +214 -56
- moai_adk/core/tags/pre_commit_validator.py +0 -1
- moai_adk/core/tags/reporter.py +1 -2
- moai_adk/templates/.claude/hooks/alfred/.moai/cache/version-check.json +9 -0
- moai_adk/templates/.claude/hooks/alfred/README.md +343 -0
- moai_adk/templates/.claude/hooks/alfred/TROUBLESHOOTING.md +471 -0
- moai_adk/templates/.claude/hooks/alfred/shared/core/project.py +10 -77
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/user.py +19 -0
- moai_adk/templates/.github/workflows/tag-report.yml +261 -0
- moai_adk/templates/.github/workflows/tag-validation.yml +176 -0
- moai_adk/templates/.moai/docs/quick-issue-creation-guide.md +219 -0
- moai_adk/templates/.moai/hooks/install.sh +79 -0
- moai_adk/templates/.moai/hooks/pre-commit.sh +66 -0
- moai_adk/templates/.moai/memory/GITFLOW-PROTECTION-POLICY.md +220 -0
- moai_adk/templates/.moai/memory/gitflow-protection-policy.md +30 -140
- moai_adk/templates/.moai/memory/spec-metadata.md +356 -0
- moai_adk/templates/src/moai_adk/core/__init__.py +5 -0
- moai_adk/templates/src/moai_adk/core/tags/__init__.py +86 -0
- moai_adk/templates/src/moai_adk/core/tags/ci_validator.py +433 -0
- moai_adk/templates/src/moai_adk/core/tags/cli.py +283 -0
- moai_adk/templates/src/moai_adk/core/tags/pre_commit_validator.py +354 -0
- moai_adk/templates/src/moai_adk/core/tags/reporter.py +956 -0
- moai_adk/templates/src/moai_adk/core/tags/validator.py +897 -0
- {moai_adk-0.9.0.dist-info → moai_adk-0.9.1.dist-info}/METADATA +69 -333
- {moai_adk-0.9.0.dist-info → moai_adk-0.9.1.dist-info}/RECORD +28 -13
- moai_adk/templates/.claude/hooks/alfred/core/project.py +0 -750
- moai_adk/templates/README.md +0 -256
- {moai_adk-0.9.0.dist-info → moai_adk-0.9.1.dist-info}/WHEEL +0 -0
- {moai_adk-0.9.0.dist-info → moai_adk-0.9.1.dist-info}/entry_points.txt +0 -0
- {moai_adk-0.9.0.dist-info → moai_adk-0.9.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
# Alfred Hooks Troubleshooting Guide
|
|
2
|
+
|
|
3
|
+
This guide helps you diagnose and resolve common issues with MoAI-ADK's Alfred hooks system.
|
|
4
|
+
|
|
5
|
+
## Quick Diagnosis
|
|
6
|
+
|
|
7
|
+
Run this command to verify hook integrity:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Test hook execution
|
|
11
|
+
echo '{"cwd": "."}' | uv run .claude/hooks/alfred/alfred_hooks.py SessionStart
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
**Expected output**: JSON with `"continue": true` and a system message
|
|
15
|
+
**If failed**: See error-specific sections below
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Error 1: "Hook not found"
|
|
20
|
+
|
|
21
|
+
### Symptom
|
|
22
|
+
```
|
|
23
|
+
error: Failed to spawn: `.claude/hooks/alfred/alfred_hooks.py`
|
|
24
|
+
Caused by: No such file or directory (os error 2)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Root Causes
|
|
28
|
+
1. **Project initialized with older MoAI-ADK version** (before hooks system)
|
|
29
|
+
2. **Hooks directory deleted accidentally**
|
|
30
|
+
3. **Template copy failed during `/alfred:0-project` initialization**
|
|
31
|
+
4. **Working directory mismatch** (Claude Code started from wrong directory)
|
|
32
|
+
|
|
33
|
+
### Solutions
|
|
34
|
+
|
|
35
|
+
#### Solution 1: Update Project (Recommended)
|
|
36
|
+
```bash
|
|
37
|
+
# Re-run project initialization to restore hooks
|
|
38
|
+
/alfred:0-project
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
#### Solution 2: Manual Template Copy
|
|
42
|
+
```bash
|
|
43
|
+
# Copy hooks from MoAI-ADK installation
|
|
44
|
+
python3 -c "
|
|
45
|
+
import moai_adk
|
|
46
|
+
from pathlib import Path
|
|
47
|
+
import shutil
|
|
48
|
+
|
|
49
|
+
template_dir = Path(moai_adk.__file__).parent / 'templates' / '.claude' / 'hooks' / 'alfred'
|
|
50
|
+
target_dir = Path('.claude/hooks/alfred')
|
|
51
|
+
target_dir.parent.mkdir(parents=True, exist_ok=True)
|
|
52
|
+
shutil.copytree(template_dir, target_dir, dirs_exist_ok=True)
|
|
53
|
+
print(f'Copied hooks to {target_dir}')
|
|
54
|
+
"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
#### Solution 3: Verify File Existence
|
|
58
|
+
```bash
|
|
59
|
+
# Check all required files exist
|
|
60
|
+
ls -la .claude/hooks/alfred/
|
|
61
|
+
ls -la .claude/hooks/alfred/handlers/
|
|
62
|
+
ls -la .claude/hooks/alfred/core/
|
|
63
|
+
|
|
64
|
+
# Expected files:
|
|
65
|
+
# .claude/hooks/alfred/alfred_hooks.py
|
|
66
|
+
# .claude/hooks/alfred/handlers/__init__.py
|
|
67
|
+
# .claude/hooks/alfred/handlers/session.py
|
|
68
|
+
# .claude/hooks/alfred/handlers/user.py
|
|
69
|
+
# .claude/hooks/alfred/handlers/tool.py
|
|
70
|
+
# .claude/hooks/alfred/handlers/notification.py
|
|
71
|
+
# .claude/hooks/alfred/core/__init__.py
|
|
72
|
+
# .claude/hooks/alfred/core/project.py
|
|
73
|
+
# .claude/hooks/alfred/core/context.py
|
|
74
|
+
# .claude/hooks/alfred/core/checkpoint.py
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Error 2: "Import error: No module named 'handlers'"
|
|
80
|
+
|
|
81
|
+
### Symptom
|
|
82
|
+
```python
|
|
83
|
+
ImportError: No module named 'handlers'
|
|
84
|
+
ModuleNotFoundError: No module named 'core'
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Root Causes
|
|
88
|
+
1. **Corrupted hooks directory** (partial copy, missing `__init__.py`)
|
|
89
|
+
2. **Python path resolution issue** (sys.path manipulation failed)
|
|
90
|
+
3. **Missing handler/core modules**
|
|
91
|
+
|
|
92
|
+
### Solutions
|
|
93
|
+
|
|
94
|
+
#### Solution 1: Verify Module Structure
|
|
95
|
+
```bash
|
|
96
|
+
# Check all __init__.py files exist
|
|
97
|
+
find .claude/hooks/alfred -name "__init__.py"
|
|
98
|
+
|
|
99
|
+
# Expected output:
|
|
100
|
+
# .claude/hooks/alfred/handlers/__init__.py
|
|
101
|
+
# .claude/hooks/alfred/core/__init__.py
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
#### Solution 2: Test Imports Manually
|
|
105
|
+
```bash
|
|
106
|
+
cd .claude/hooks/alfred && python3 -c "
|
|
107
|
+
import sys
|
|
108
|
+
from pathlib import Path
|
|
109
|
+
sys.path.insert(0, str(Path.cwd()))
|
|
110
|
+
|
|
111
|
+
from handlers import handle_session_start
|
|
112
|
+
from core import HookResult
|
|
113
|
+
print('✅ Imports successful')
|
|
114
|
+
"
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
#### Solution 3: Re-initialize Hooks
|
|
118
|
+
```bash
|
|
119
|
+
# Force re-copy from template
|
|
120
|
+
/alfred:0-project update --force
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Error 3: "Hook execution timeout"
|
|
126
|
+
|
|
127
|
+
### Symptom
|
|
128
|
+
```
|
|
129
|
+
Hook timeout after 5 seconds
|
|
130
|
+
⚠️ Hook execution timeout - continuing without session info
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Root Causes
|
|
134
|
+
1. **Slow Git operations** (large repository, many branches)
|
|
135
|
+
2. **Slow file I/O** (network drive, slow disk)
|
|
136
|
+
3. **Heavy SPEC counting** (many `.moai/specs/` directories)
|
|
137
|
+
4. **Subprocess hang** (rare, usually indicates system issue)
|
|
138
|
+
|
|
139
|
+
### Solutions
|
|
140
|
+
|
|
141
|
+
#### Solution 1: Identify Slow Operations
|
|
142
|
+
```bash
|
|
143
|
+
# Time hook execution
|
|
144
|
+
time echo '{"cwd": "."}' | uv run .claude/hooks/alfred/alfred_hooks.py SessionStart
|
|
145
|
+
|
|
146
|
+
# If >5 seconds, check:
|
|
147
|
+
# - Git repository size: du -sh .git
|
|
148
|
+
# - SPEC count: find .moai/specs -type d -name "SPEC-*" | wc -l
|
|
149
|
+
# - Disk I/O: iotop (Linux) or sudo fs_usage (macOS)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
#### Solution 2: Increase Timeout (Temporary Workaround)
|
|
153
|
+
```bash
|
|
154
|
+
# Edit alfred_hooks.py line 129
|
|
155
|
+
# Change: signal.alarm(5) # 5 seconds
|
|
156
|
+
# To: signal.alarm(10) # 10 seconds
|
|
157
|
+
|
|
158
|
+
# Location: .claude/hooks/alfred/alfred_hooks.py
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**Note**: This is a workaround. File an issue if hooks consistently timeout.
|
|
162
|
+
|
|
163
|
+
#### Solution 3: Disable Slow Features (Not Currently Supported)
|
|
164
|
+
**Future enhancement**: Add `.moai/config.json` option to disable expensive checks:
|
|
165
|
+
```json
|
|
166
|
+
{
|
|
167
|
+
"hooks": {
|
|
168
|
+
"session_start": {
|
|
169
|
+
"skip_git_info": false,
|
|
170
|
+
"skip_spec_count": false
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Error 4: "JSON parse error"
|
|
179
|
+
|
|
180
|
+
### Symptom
|
|
181
|
+
```
|
|
182
|
+
JSON parse error: Expecting value: line 1 column 1 (char 0)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Root Causes
|
|
186
|
+
1. **Empty stdin** (Claude Code passed no payload)
|
|
187
|
+
2. **Invalid JSON format** (malformed payload)
|
|
188
|
+
3. **Encoding issues** (non-UTF-8 characters)
|
|
189
|
+
|
|
190
|
+
### Solutions
|
|
191
|
+
|
|
192
|
+
#### Solution 1: Test with Minimal Payload
|
|
193
|
+
```bash
|
|
194
|
+
# Empty payload (should work - returns empty dict)
|
|
195
|
+
echo '' | uv run .claude/hooks/alfred/alfred_hooks.py SessionStart
|
|
196
|
+
|
|
197
|
+
# Minimal valid payload
|
|
198
|
+
echo '{"cwd": "."}' | uv run .claude/hooks/alfred/alfred_hooks.py SessionStart
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
#### Solution 2: Check Claude Code Version
|
|
202
|
+
```bash
|
|
203
|
+
# Ensure Claude Code is up-to-date
|
|
204
|
+
claude-code --version
|
|
205
|
+
|
|
206
|
+
# Update if needed
|
|
207
|
+
pip install --upgrade claude-code
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## Error 5: "Permission denied"
|
|
213
|
+
|
|
214
|
+
### Symptom
|
|
215
|
+
```
|
|
216
|
+
PermissionError: [Errno 13] Permission denied: '.claude/hooks/alfred/alfred_hooks.py'
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Root Causes
|
|
220
|
+
1. **File not executable** (missing execute permission)
|
|
221
|
+
2. **Directory permissions** (parent directory not readable)
|
|
222
|
+
3. **Ownership issues** (file owned by different user)
|
|
223
|
+
|
|
224
|
+
### Solutions
|
|
225
|
+
|
|
226
|
+
#### Solution 1: Fix Permissions
|
|
227
|
+
```bash
|
|
228
|
+
# Make executable
|
|
229
|
+
chmod +x .claude/hooks/alfred/alfred_hooks.py
|
|
230
|
+
|
|
231
|
+
# Fix directory permissions
|
|
232
|
+
chmod -R u+rX .claude/hooks/alfred/
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
#### Solution 2: Check Ownership
|
|
236
|
+
```bash
|
|
237
|
+
# Check file owner
|
|
238
|
+
ls -l .claude/hooks/alfred/alfred_hooks.py
|
|
239
|
+
|
|
240
|
+
# Change owner if needed (replace USER with your username)
|
|
241
|
+
sudo chown -R USER:USER .claude/hooks/alfred/
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## Error 6: "UV environment issues"
|
|
247
|
+
|
|
248
|
+
### Symptom
|
|
249
|
+
```
|
|
250
|
+
uv: command not found
|
|
251
|
+
error: failed to create virtualenv
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Root Causes
|
|
255
|
+
1. **UV not installed**
|
|
256
|
+
2. **UV not in PATH**
|
|
257
|
+
3. **Virtual environment corruption**
|
|
258
|
+
|
|
259
|
+
### Solutions
|
|
260
|
+
|
|
261
|
+
#### Solution 1: Install/Update UV
|
|
262
|
+
```bash
|
|
263
|
+
# Install UV
|
|
264
|
+
pip install uv
|
|
265
|
+
|
|
266
|
+
# Or update
|
|
267
|
+
pip install --upgrade uv
|
|
268
|
+
|
|
269
|
+
# Verify
|
|
270
|
+
uv --version
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
#### Solution 2: Check PATH
|
|
274
|
+
```bash
|
|
275
|
+
# Find UV installation
|
|
276
|
+
which uv
|
|
277
|
+
|
|
278
|
+
# Add to PATH if needed
|
|
279
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
#### Solution 3: Rebuild Virtual Environment
|
|
283
|
+
```bash
|
|
284
|
+
# Remove existing environment
|
|
285
|
+
rm -rf .venv
|
|
286
|
+
|
|
287
|
+
# Recreate
|
|
288
|
+
uv venv
|
|
289
|
+
uv sync
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## Platform-Specific Issues
|
|
295
|
+
|
|
296
|
+
### macOS: SIGALRM Not Working
|
|
297
|
+
**Note**: SIGALRM is fully supported on macOS. If timeout protection isn't working:
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
# Verify Python version (3.8+ required)
|
|
301
|
+
python3 --version
|
|
302
|
+
|
|
303
|
+
# Test signal module
|
|
304
|
+
python3 -c "import signal; print(hasattr(signal, 'SIGALRM'))"
|
|
305
|
+
# Expected: True
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Windows: SIGALRM Not Available
|
|
309
|
+
**Known Limitation**: SIGALRM is not available on Windows.
|
|
310
|
+
|
|
311
|
+
**Workaround**: Hooks must complete in <2 seconds (no timeout protection).
|
|
312
|
+
|
|
313
|
+
**Alternative**: Use threading-based timeout (future enhancement):
|
|
314
|
+
```python
|
|
315
|
+
import threading
|
|
316
|
+
|
|
317
|
+
def run_with_timeout(func, timeout=5):
|
|
318
|
+
result = []
|
|
319
|
+
def wrapper():
|
|
320
|
+
result.append(func())
|
|
321
|
+
|
|
322
|
+
thread = threading.Thread(target=wrapper)
|
|
323
|
+
thread.start()
|
|
324
|
+
thread.join(timeout)
|
|
325
|
+
|
|
326
|
+
if thread.is_alive():
|
|
327
|
+
raise TimeoutError("Function timeout")
|
|
328
|
+
return result[0]
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Linux: Signal Conflicts
|
|
332
|
+
If other tools use SIGALRM (rare), hooks may conflict.
|
|
333
|
+
|
|
334
|
+
**Diagnosis**:
|
|
335
|
+
```bash
|
|
336
|
+
# Check for signal handlers
|
|
337
|
+
python3 -c "
|
|
338
|
+
import signal
|
|
339
|
+
print('Current SIGALRM handler:', signal.getsignal(signal.SIGALRM))
|
|
340
|
+
"
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## Advanced Debugging
|
|
346
|
+
|
|
347
|
+
### Enable Hook Logging
|
|
348
|
+
```bash
|
|
349
|
+
# Create custom hook wrapper with logging
|
|
350
|
+
cat > .claude/hooks/alfred/debug_hooks.sh <<'EOF'
|
|
351
|
+
#!/bin/bash
|
|
352
|
+
EVENT=$1
|
|
353
|
+
INPUT=$(cat)
|
|
354
|
+
|
|
355
|
+
echo "[$(date)] Event: $EVENT" >> /tmp/alfred_hooks.log
|
|
356
|
+
echo "[$(date)] Input: $INPUT" >> /tmp/alfred_hooks.log
|
|
357
|
+
|
|
358
|
+
OUTPUT=$(echo "$INPUT" | uv run .claude/hooks/alfred/alfred_hooks.py "$EVENT" 2>&1)
|
|
359
|
+
EXIT_CODE=$?
|
|
360
|
+
|
|
361
|
+
echo "[$(date)] Output: $OUTPUT" >> /tmp/alfred_hooks.log
|
|
362
|
+
echo "[$(date)] Exit: $EXIT_CODE" >> /tmp/alfred_hooks.log
|
|
363
|
+
|
|
364
|
+
echo "$OUTPUT"
|
|
365
|
+
exit $EXIT_CODE
|
|
366
|
+
EOF
|
|
367
|
+
|
|
368
|
+
chmod +x .claude/hooks/alfred/debug_hooks.sh
|
|
369
|
+
|
|
370
|
+
# Update settings.json to use debug wrapper
|
|
371
|
+
# "command": "uv run \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/alfred/debug_hooks.sh SessionStart"
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### Monitor Hook Performance
|
|
375
|
+
```bash
|
|
376
|
+
# Benchmark all hook events
|
|
377
|
+
for event in SessionStart UserPromptSubmit PreToolUse PostToolUse SessionEnd; do
|
|
378
|
+
echo "Testing $event..."
|
|
379
|
+
time echo '{"cwd": "."}' | uv run .claude/hooks/alfred/alfred_hooks.py $event > /dev/null
|
|
380
|
+
done
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### Validate Hook Output Schema
|
|
384
|
+
```bash
|
|
385
|
+
# Test output format
|
|
386
|
+
OUTPUT=$(echo '{"cwd": "."}' | uv run .claude/hooks/alfred/alfred_hooks.py SessionStart)
|
|
387
|
+
|
|
388
|
+
# Check required fields
|
|
389
|
+
echo "$OUTPUT" | python3 -c "
|
|
390
|
+
import json, sys
|
|
391
|
+
data = json.load(sys.stdin)
|
|
392
|
+
assert 'continue' in data, 'Missing continue field'
|
|
393
|
+
assert isinstance(data['continue'], bool), 'continue must be boolean'
|
|
394
|
+
print('✅ Valid hook output schema')
|
|
395
|
+
"
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
---
|
|
399
|
+
|
|
400
|
+
## Getting Help
|
|
401
|
+
|
|
402
|
+
### Collect Diagnostic Information
|
|
403
|
+
```bash
|
|
404
|
+
# Create diagnostic report
|
|
405
|
+
cat > /tmp/hooks_diagnostic.txt <<EOF
|
|
406
|
+
=== MoAI-ADK Hooks Diagnostic Report ===
|
|
407
|
+
Date: $(date)
|
|
408
|
+
|
|
409
|
+
=== Environment ===
|
|
410
|
+
OS: $(uname -s)
|
|
411
|
+
Python: $(python3 --version)
|
|
412
|
+
UV: $(uv --version 2>&1)
|
|
413
|
+
Claude Code: $(claude-code --version 2>&1)
|
|
414
|
+
|
|
415
|
+
=== File Structure ===
|
|
416
|
+
$(find .claude/hooks/alfred -type f 2>&1)
|
|
417
|
+
|
|
418
|
+
=== Permissions ===
|
|
419
|
+
$(ls -la .claude/hooks/alfred/ 2>&1)
|
|
420
|
+
|
|
421
|
+
=== Hook Test ===
|
|
422
|
+
$(echo '{"cwd": "."}' | uv run .claude/hooks/alfred/alfred_hooks.py SessionStart 2>&1)
|
|
423
|
+
|
|
424
|
+
=== Settings ===
|
|
425
|
+
$(cat .claude/settings.json 2>&1 | grep -A 10 "SessionStart")
|
|
426
|
+
EOF
|
|
427
|
+
|
|
428
|
+
cat /tmp/hooks_diagnostic.txt
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
### Report Issues
|
|
432
|
+
1. **GitHub Issues**: https://github.com/modu-ai/moai-adk/issues
|
|
433
|
+
2. **Include**: Diagnostic report (above) + error message + steps to reproduce
|
|
434
|
+
3. **Security**: Do NOT include secrets, API keys, or sensitive paths
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
438
|
+
## Preventive Maintenance
|
|
439
|
+
|
|
440
|
+
### Regular Health Checks
|
|
441
|
+
```bash
|
|
442
|
+
# Add to .git/hooks/post-merge
|
|
443
|
+
cat > .git/hooks/post-merge <<'EOF'
|
|
444
|
+
#!/bin/bash
|
|
445
|
+
# Verify hooks after pulling updates
|
|
446
|
+
echo '{"cwd": "."}' | uv run .claude/hooks/alfred/alfred_hooks.py SessionStart > /dev/null 2>&1
|
|
447
|
+
if [ $? -ne 0 ]; then
|
|
448
|
+
echo "⚠️ Alfred hooks failed - run /alfred:0-project update"
|
|
449
|
+
fi
|
|
450
|
+
EOF
|
|
451
|
+
|
|
452
|
+
chmod +x .git/hooks/post-merge
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
### Keep MoAI-ADK Updated
|
|
456
|
+
```bash
|
|
457
|
+
# Check version
|
|
458
|
+
python3 -c "import moai_adk; print(moai_adk.__version__)"
|
|
459
|
+
|
|
460
|
+
# Update
|
|
461
|
+
pip install --upgrade moai-adk
|
|
462
|
+
|
|
463
|
+
# Re-initialize project
|
|
464
|
+
/alfred:0-project update
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
---
|
|
468
|
+
|
|
469
|
+
**Last Updated**: 2025-10-29
|
|
470
|
+
**Applies To**: MoAI-ADK v0.7.0+
|
|
471
|
+
**Hooks Architecture Version**: Hybrid Modular (9 modules)
|
|
@@ -16,58 +16,6 @@ from typing import Any
|
|
|
16
16
|
CACHE_DIR_NAME = ".moai/cache"
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
def find_project_root(start_path: str | Path = ".") -> Path:
|
|
20
|
-
"""Find MoAI-ADK project root by searching upward for .moai/config.json
|
|
21
|
-
|
|
22
|
-
Traverses up the directory tree until it finds .moai/config.json or CLAUDE.md,
|
|
23
|
-
which indicates the project root. This ensures cache and other files are
|
|
24
|
-
always created in the correct location, regardless of where hooks execute.
|
|
25
|
-
|
|
26
|
-
Args:
|
|
27
|
-
start_path: Starting directory (default: current directory)
|
|
28
|
-
|
|
29
|
-
Returns:
|
|
30
|
-
Project root Path. If not found, returns start_path as absolute path.
|
|
31
|
-
|
|
32
|
-
Examples:
|
|
33
|
-
>>> find_project_root(".")
|
|
34
|
-
Path("/Users/user/my-project")
|
|
35
|
-
>>> find_project_root(".claude/hooks/alfred")
|
|
36
|
-
Path("/Users/user/my-project") # Found root 3 levels up
|
|
37
|
-
|
|
38
|
-
Notes:
|
|
39
|
-
- Searches for .moai/config.json first (most reliable)
|
|
40
|
-
- Falls back to CLAUDE.md if config.json not found
|
|
41
|
-
- Max depth: 10 levels up (prevent infinite loop)
|
|
42
|
-
- Returns absolute path for consistency
|
|
43
|
-
|
|
44
|
-
TDD History:
|
|
45
|
-
- RED: 4 test scenarios (root, nested, not found, symlinks)
|
|
46
|
-
- GREEN: Minimal upward search with .moai/config.json detection
|
|
47
|
-
- REFACTOR: Add CLAUDE.md fallback, max depth limit, absolute path return
|
|
48
|
-
"""
|
|
49
|
-
current = Path(start_path).resolve()
|
|
50
|
-
max_depth = 10 # Prevent infinite loop
|
|
51
|
-
|
|
52
|
-
for _ in range(max_depth):
|
|
53
|
-
# Check for .moai/config.json (primary indicator)
|
|
54
|
-
if (current / ".moai" / "config.json").exists():
|
|
55
|
-
return current
|
|
56
|
-
|
|
57
|
-
# Check for CLAUDE.md (secondary indicator)
|
|
58
|
-
if (current / "CLAUDE.md").exists():
|
|
59
|
-
return current
|
|
60
|
-
|
|
61
|
-
# Move up one level
|
|
62
|
-
parent = current.parent
|
|
63
|
-
if parent == current: # Reached filesystem root
|
|
64
|
-
break
|
|
65
|
-
current = parent
|
|
66
|
-
|
|
67
|
-
# Not found - return start_path as absolute
|
|
68
|
-
return Path(start_path).resolve()
|
|
69
|
-
|
|
70
|
-
|
|
71
19
|
class TimeoutError(Exception):
|
|
72
20
|
"""Signal-based timeout exception"""
|
|
73
21
|
pass
|
|
@@ -298,7 +246,7 @@ def count_specs(cwd: str) -> dict[str, int]:
|
|
|
298
246
|
Counts the number of SPECs with status: completed.
|
|
299
247
|
|
|
300
248
|
Args:
|
|
301
|
-
cwd: Project root directory path
|
|
249
|
+
cwd: Project root directory path
|
|
302
250
|
|
|
303
251
|
Returns:
|
|
304
252
|
SPEC progress dictionary. Includes the following keys:
|
|
@@ -316,19 +264,15 @@ def count_specs(cwd: str) -> dict[str, int]:
|
|
|
316
264
|
|
|
317
265
|
Notes:
|
|
318
266
|
- SPEC File Location: .moai/specs/SPEC-{ID}/spec.md
|
|
319
|
-
- Completion condition: Include
|
|
267
|
+
- Completion condition: Include “status: completed” in YAML front matter
|
|
320
268
|
- If parsing fails, the SPEC is considered incomplete.
|
|
321
|
-
- Automatically finds project root to locate .moai/specs/
|
|
322
269
|
|
|
323
270
|
TDD History:
|
|
324
271
|
- RED: 5 items scenario test (0/0, 2/5, 5/5, no directory, parsing error)
|
|
325
272
|
- GREEN: SPEC search with Path.iterdir(), YAML parsing implementation
|
|
326
273
|
- REFACTOR: Strengthened exception handling, improved percentage calculation safety
|
|
327
|
-
- UPDATE: Add project root detection for consistent path resolution
|
|
328
274
|
"""
|
|
329
|
-
|
|
330
|
-
project_root = find_project_root(cwd)
|
|
331
|
-
specs_dir = project_root / ".moai" / "specs"
|
|
275
|
+
specs_dir = Path(cwd) / ".moai" / "specs"
|
|
332
276
|
|
|
333
277
|
if not specs_dir.exists():
|
|
334
278
|
return {"completed": 0, "total": 0, "percentage": 0}
|
|
@@ -372,7 +316,7 @@ def get_project_language(cwd: str) -> str:
|
|
|
372
316
|
"""Determine the primary project language (prefers config.json).
|
|
373
317
|
|
|
374
318
|
Args:
|
|
375
|
-
cwd: Project root directory
|
|
319
|
+
cwd: Project root directory.
|
|
376
320
|
|
|
377
321
|
Returns:
|
|
378
322
|
Language string in lower-case.
|
|
@@ -380,11 +324,8 @@ def get_project_language(cwd: str) -> str:
|
|
|
380
324
|
Notes:
|
|
381
325
|
- Reads ``.moai/config.json`` first for a quick answer.
|
|
382
326
|
- Falls back to ``detect_language`` if configuration is missing.
|
|
383
|
-
- Automatically finds project root to locate .moai/config.json
|
|
384
327
|
"""
|
|
385
|
-
|
|
386
|
-
project_root = find_project_root(cwd)
|
|
387
|
-
config_path = project_root / ".moai" / "config.json"
|
|
328
|
+
config_path = Path(cwd) / ".moai" / "config.json"
|
|
388
329
|
if config_path.exists():
|
|
389
330
|
try:
|
|
390
331
|
config = json.loads(config_path.read_text())
|
|
@@ -395,8 +336,8 @@ def get_project_language(cwd: str) -> str:
|
|
|
395
336
|
# Fall back to detection on parse errors
|
|
396
337
|
pass
|
|
397
338
|
|
|
398
|
-
# Fall back to the original language detection routine
|
|
399
|
-
return detect_language(
|
|
339
|
+
# Fall back to the original language detection routine
|
|
340
|
+
return detect_language(cwd)
|
|
400
341
|
|
|
401
342
|
|
|
402
343
|
# @CODE:CONFIG-INTEGRATION-001
|
|
@@ -441,9 +382,7 @@ def get_version_check_config(cwd: str) -> dict[str, Any]:
|
|
|
441
382
|
"cache_ttl_hours": 24
|
|
442
383
|
}
|
|
443
384
|
|
|
444
|
-
|
|
445
|
-
project_root = find_project_root(cwd)
|
|
446
|
-
config_path = project_root / ".moai" / "config.json"
|
|
385
|
+
config_path = Path(cwd) / ".moai" / "config.json"
|
|
447
386
|
if not config_path.exists():
|
|
448
387
|
return defaults
|
|
449
388
|
|
|
@@ -621,13 +560,8 @@ def get_package_version_info(cwd: str = ".") -> dict[str, Any]:
|
|
|
621
560
|
# Graceful degradation: skip caching on import errors
|
|
622
561
|
VersionCache = None
|
|
623
562
|
|
|
624
|
-
# 1.
|
|
625
|
-
|
|
626
|
-
# from subdirectories like .claude/hooks/alfred/
|
|
627
|
-
project_root = find_project_root(cwd)
|
|
628
|
-
|
|
629
|
-
# 2. Initialize cache (skip if VersionCache couldn't be imported)
|
|
630
|
-
cache_dir = project_root / CACHE_DIR_NAME
|
|
563
|
+
# 1. Initialize cache (skip if VersionCache couldn't be imported)
|
|
564
|
+
cache_dir = Path(cwd) / CACHE_DIR_NAME
|
|
631
565
|
version_cache = VersionCache(cache_dir) if VersionCache else None
|
|
632
566
|
|
|
633
567
|
# 2. Get current installed version first (needed for cache validation)
|
|
@@ -738,7 +672,6 @@ def get_package_version_info(cwd: str = ".") -> dict[str, Any]:
|
|
|
738
672
|
|
|
739
673
|
|
|
740
674
|
__all__ = [
|
|
741
|
-
"find_project_root",
|
|
742
675
|
"detect_language",
|
|
743
676
|
"get_git_info",
|
|
744
677
|
"count_specs",
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
Handling the UserPromptSubmit event
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
7
10
|
from core import HookPayload, HookResult
|
|
8
11
|
from core.context import get_jit_context
|
|
9
12
|
|
|
@@ -29,11 +32,27 @@ def handle_user_prompt_submit(payload: HookPayload) -> HookResult:
|
|
|
29
32
|
- GREEN: Recommend documents by calling get_jit_context()
|
|
30
33
|
- REFACTOR: Message conditional display (only when there is a file)
|
|
31
34
|
- UPDATE: Migrated to Claude Code standard Hook schema with snake_case fields
|
|
35
|
+
- FEATURE: Command execution logging for tracking double-run debugging
|
|
32
36
|
"""
|
|
33
37
|
user_prompt = payload.get("userPrompt", "")
|
|
34
38
|
cwd = payload.get("cwd", ".")
|
|
35
39
|
context_files = get_jit_context(user_prompt, cwd)
|
|
36
40
|
|
|
41
|
+
# Command execution logging (DEBUG feature for tracking invocations)
|
|
42
|
+
if user_prompt.startswith("/alfred:"):
|
|
43
|
+
try:
|
|
44
|
+
log_dir = Path(cwd) / ".moai" / "logs"
|
|
45
|
+
log_dir.mkdir(parents=True, exist_ok=True)
|
|
46
|
+
|
|
47
|
+
log_file = log_dir / "command-invocations.log"
|
|
48
|
+
timestamp = datetime.now().isoformat()
|
|
49
|
+
|
|
50
|
+
with open(log_file, "a", encoding="utf-8") as f:
|
|
51
|
+
f.write(f"{timestamp} | {user_prompt}\n")
|
|
52
|
+
except Exception:
|
|
53
|
+
# Silently fail if logging fails (don't interrupt main flow)
|
|
54
|
+
pass
|
|
55
|
+
|
|
37
56
|
system_message = f"📎 Loaded {len(context_files)} context file(s)" if context_files else None
|
|
38
57
|
|
|
39
58
|
return HookResult(system_message=system_message, context_files=context_files)
|