moai-adk 0.8.1__py3-none-any.whl → 0.8.2__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.

Files changed (87) hide show
  1. moai_adk/cli/commands/update.py +15 -4
  2. moai_adk/core/tags/__init__.py +87 -0
  3. moai_adk/core/tags/ci_validator.py +435 -0
  4. moai_adk/core/tags/cli.py +283 -0
  5. moai_adk/core/tags/generator.py +109 -0
  6. moai_adk/core/tags/inserter.py +99 -0
  7. moai_adk/core/tags/mapper.py +126 -0
  8. moai_adk/core/tags/parser.py +76 -0
  9. moai_adk/core/tags/pre_commit_validator.py +355 -0
  10. moai_adk/core/tags/reporter.py +959 -0
  11. moai_adk/core/tags/tags.py +149 -0
  12. moai_adk/core/tags/validator.py +897 -0
  13. moai_adk/templates/.claude/agents/alfred/cc-manager.md +25 -2
  14. moai_adk/templates/.claude/agents/alfred/debug-helper.md +24 -12
  15. moai_adk/templates/.claude/agents/alfred/doc-syncer.md +19 -12
  16. moai_adk/templates/.claude/agents/alfred/git-manager.md +20 -12
  17. moai_adk/templates/.claude/agents/alfred/implementation-planner.md +19 -12
  18. moai_adk/templates/.claude/agents/alfred/project-manager.md +29 -2
  19. moai_adk/templates/.claude/agents/alfred/quality-gate.md +25 -2
  20. moai_adk/templates/.claude/agents/alfred/skill-factory.md +30 -2
  21. moai_adk/templates/.claude/agents/alfred/spec-builder.md +26 -11
  22. moai_adk/templates/.claude/agents/alfred/tag-agent.md +30 -8
  23. moai_adk/templates/.claude/agents/alfred/tdd-implementer.md +27 -12
  24. moai_adk/templates/.claude/agents/alfred/trust-checker.md +25 -2
  25. moai_adk/templates/.claude/commands/alfred/0-project.md +5 -0
  26. moai_adk/templates/.claude/commands/alfred/1-plan.md +17 -4
  27. moai_adk/templates/.claude/commands/alfred/2-run.md +7 -0
  28. moai_adk/templates/.claude/commands/alfred/3-sync.md +6 -0
  29. moai_adk/templates/.claude/hooks/alfred/.moai/cache/version-check.json +9 -0
  30. moai_adk/templates/.claude/hooks/alfred/README.md +258 -145
  31. moai_adk/templates/.claude/hooks/alfred/TROUBLESHOOTING.md +471 -0
  32. moai_adk/templates/.claude/hooks/alfred/alfred_hooks.py +92 -57
  33. moai_adk/templates/.claude/hooks/alfred/core/version_cache.py +198 -0
  34. moai_adk/templates/.claude/hooks/alfred/notification__handle_events.py +102 -0
  35. moai_adk/templates/.claude/hooks/alfred/post_tool__log_changes.py +102 -0
  36. moai_adk/templates/.claude/hooks/alfred/pre_tool__auto_checkpoint.py +108 -0
  37. moai_adk/templates/.claude/hooks/alfred/session_end__cleanup.py +102 -0
  38. moai_adk/templates/.claude/hooks/alfred/session_start__show_project_info.py +102 -0
  39. moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/project.py +269 -13
  40. moai_adk/templates/.claude/hooks/alfred/shared/core/version_cache.py +198 -0
  41. moai_adk/templates/.claude/hooks/alfred/{handlers → shared/handlers}/session.py +21 -7
  42. moai_adk/templates/.claude/hooks/alfred/stop__handle_interrupt.py +102 -0
  43. moai_adk/templates/.claude/hooks/alfred/subagent_stop__handle_subagent_end.py +102 -0
  44. moai_adk/templates/.claude/hooks/alfred/user_prompt__jit_load_docs.py +120 -0
  45. moai_adk/templates/.claude/settings.json +5 -5
  46. moai_adk/templates/.claude/skills/moai-foundation-ears/SKILL.md +9 -6
  47. moai_adk/templates/.claude/skills/moai-spec-authoring/README.md +56 -56
  48. moai_adk/templates/.claude/skills/moai-spec-authoring/SKILL.md +101 -100
  49. moai_adk/templates/.claude/skills/moai-spec-authoring/examples/validate-spec.sh +3 -3
  50. moai_adk/templates/.claude/skills/moai-spec-authoring/examples.md +219 -219
  51. moai_adk/templates/.claude/skills/moai-spec-authoring/reference.md +287 -287
  52. moai_adk/templates/.github/ISSUE_TEMPLATE/spec.yml +9 -11
  53. moai_adk/templates/.github/PULL_REQUEST_TEMPLATE.md +9 -21
  54. moai_adk/templates/.github/workflows/moai-release-create.yml +100 -0
  55. moai_adk/templates/.github/workflows/moai-release-pipeline.yml +182 -0
  56. moai_adk/templates/.github/workflows/release.yml +49 -0
  57. moai_adk/templates/.github/workflows/tag-report.yml +261 -0
  58. moai_adk/templates/.github/workflows/tag-validation.yml +176 -0
  59. moai_adk/templates/.moai/config.json +6 -1
  60. moai_adk/templates/.moai/hooks/install.sh +79 -0
  61. moai_adk/templates/.moai/hooks/pre-commit.sh +66 -0
  62. moai_adk/templates/CLAUDE.md +39 -40
  63. moai_adk/templates/src/moai_adk/core/__init__.py +5 -0
  64. moai_adk/templates/src/moai_adk/core/tags/__init__.py +87 -0
  65. moai_adk/templates/src/moai_adk/core/tags/ci_validator.py +435 -0
  66. moai_adk/templates/src/moai_adk/core/tags/cli.py +283 -0
  67. moai_adk/templates/src/moai_adk/core/tags/pre_commit_validator.py +355 -0
  68. moai_adk/templates/src/moai_adk/core/tags/reporter.py +959 -0
  69. moai_adk/templates/src/moai_adk/core/tags/validator.py +897 -0
  70. {moai_adk-0.8.1.dist-info → moai_adk-0.8.2.dist-info}/METADATA +226 -1
  71. {moai_adk-0.8.1.dist-info → moai_adk-0.8.2.dist-info}/RECORD +83 -50
  72. moai_adk/templates/.claude/hooks/alfred/HOOK_SCHEMA_VALIDATION.md +0 -313
  73. moai_adk/templates/.moai/memory/config-schema.md +0 -444
  74. moai_adk/templates/.moai/memory/gitflow-protection-policy.md +0 -220
  75. moai_adk/templates/.moai/memory/spec-metadata.md +0 -356
  76. /moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/__init__.py +0 -0
  77. /moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/checkpoint.py +0 -0
  78. /moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/context.py +0 -0
  79. /moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/tags.py +0 -0
  80. /moai_adk/templates/.claude/hooks/alfred/{handlers → shared/handlers}/__init__.py +0 -0
  81. /moai_adk/templates/.claude/hooks/alfred/{handlers → shared/handlers}/notification.py +0 -0
  82. /moai_adk/templates/.claude/hooks/alfred/{handlers → shared/handlers}/tool.py +0 -0
  83. /moai_adk/templates/.claude/hooks/alfred/{handlers → shared/handlers}/user.py +0 -0
  84. /moai_adk/templates/.moai/memory/{issue-label-mapping.md → ISSUE-LABEL-MAPPING.md} +0 -0
  85. {moai_adk-0.8.1.dist-info → moai_adk-0.8.2.dist-info}/WHEEL +0 -0
  86. {moai_adk-0.8.1.dist-info → moai_adk-0.8.2.dist-info}/entry_points.txt +0 -0
  87. {moai_adk-0.8.1.dist-info → moai_adk-0.8.2.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)
@@ -53,6 +53,7 @@ Setup sys.path for package imports
53
53
  """
54
54
 
55
55
  import json
56
+ import signal
56
57
  import sys
57
58
  from pathlib import Path
58
59
  from typing import Any
@@ -75,11 +76,22 @@ if str(HOOKS_DIR) not in sys.path:
75
76
  sys.path.insert(0, str(HOOKS_DIR))
76
77
 
77
78
 
79
+ class HookTimeoutError(Exception):
80
+ """Hook execution timeout exception"""
81
+ pass
82
+
83
+
84
+ def _hook_timeout_handler(signum, frame):
85
+ """Signal handler for global hook timeout"""
86
+ raise HookTimeoutError("Hook execution exceeded 5-second timeout")
87
+
88
+
78
89
  def main() -> None:
79
- """Main entry point - Claude Code Hook script
90
+ """Main entry point - Claude Code Hook script with GLOBAL TIMEOUT PROTECTION
80
91
 
81
92
  Receives the event name as a CLI argument and reads the JSON payload through stdin.
82
93
  Calls the handler appropriate for the event and outputs the results to stdout as JSON.
94
+ Enforces a 5-second global timeout to prevent subprocess hangs from freezing Claude Code.
83
95
 
84
96
  🛠️ Usage:
85
97
  python alfred_hooks.py <event_name> < payload.json
@@ -91,7 +103,7 @@ def main() -> None:
91
103
 
92
104
  🚦 Exit Codes:
93
105
  - 0: Success
94
- - 1: Error (no arguments, JSON parsing failure, exception thrown)
106
+ - 1: Error (timeout, no arguments, JSON parsing failure, exception thrown)
95
107
 
96
108
  📝 Examples:
97
109
  $ echo '{"cwd": "."}' | python alfred_hooks.py SessionStart
@@ -102,73 +114,96 @@ def main() -> None:
102
114
  - JSON I/O processing through stdin/stdout
103
115
  - Print error message to stderr
104
116
  - UserPromptSubmit uses a special output schema (hookEventName + additionalContext)
117
+ - CRITICAL: 5-second global timeout prevents Claude Code freeze on subprocess hang
105
118
 
106
119
  🧪 TDD History:
107
120
  - RED: Event routing, JSON I/O, error handling testing
108
121
  - GREEN: Handler map-based routing implementation
109
122
  - REFACTOR: Error message clarification, exit code standardization, UserPromptSubmit schema separation
110
- """
111
- # Check for event argument
112
- if len(sys.argv) < 2:
113
- print("Usage: alfred_hooks.py <event>", file=sys.stderr)
114
- sys.exit(1)
123
+ - HOTFIX: Added global SIGALRM timeout to prevent subprocess hang (Issue #66)
115
124
 
116
- event_name = sys.argv[1]
125
+ @TAG:HOOKS-TIMEOUT-001
126
+ """
127
+ # Set global 5-second timeout for entire hook execution
128
+ signal.signal(signal.SIGALRM, _hook_timeout_handler)
129
+ signal.alarm(5)
117
130
 
118
131
  try:
119
- # Read JSON from stdin
120
- input_data = sys.stdin.read()
121
- # Handle empty stdin gracefully (return empty dict)
122
- if not input_data or not input_data.strip():
123
- data = {}
124
- else:
125
- data = json.loads(input_data)
126
-
127
- cwd = data.get("cwd", ".")
128
-
129
- # Route to appropriate handler
130
- handlers = {
131
- "SessionStart": handle_session_start,
132
- "UserPromptSubmit": handle_user_prompt_submit,
133
- "SessionEnd": handle_session_end,
134
- "PreToolUse": handle_pre_tool_use,
135
- "PostToolUse": handle_post_tool_use,
136
- "Notification": handle_notification,
137
- "Stop": handle_stop,
138
- "SubagentStop": handle_subagent_stop,
139
- }
140
-
141
- handler = handlers.get(event_name)
142
- result = handler({"cwd": cwd, **data}) if handler else HookResult()
143
-
144
- # Output Hook result as JSON
145
- # Note: UserPromptSubmit uses to_user_prompt_submit_dict() for special schema
146
- if event_name == "UserPromptSubmit":
147
- print(json.dumps(result.to_user_prompt_submit_dict()))
148
- else:
149
- print(json.dumps(result.to_dict()))
150
-
151
- sys.exit(0)
152
-
153
- except json.JSONDecodeError as e:
154
- # Return valid Hook response even on JSON parse error
155
- error_response: dict[str, Any] = {
132
+ # Check for event argument
133
+ if len(sys.argv) < 2:
134
+ print("Usage: alfred_hooks.py <event>", file=sys.stderr)
135
+ sys.exit(1)
136
+
137
+ event_name = sys.argv[1]
138
+
139
+ try:
140
+ # Read JSON from stdin
141
+ input_data = sys.stdin.read()
142
+ # Handle empty stdin gracefully (return empty dict)
143
+ if not input_data or not input_data.strip():
144
+ data = {}
145
+ else:
146
+ data = json.loads(input_data)
147
+
148
+ cwd = data.get("cwd", ".")
149
+
150
+ # Route to appropriate handler
151
+ handlers = {
152
+ "SessionStart": handle_session_start,
153
+ "UserPromptSubmit": handle_user_prompt_submit,
154
+ "SessionEnd": handle_session_end,
155
+ "PreToolUse": handle_pre_tool_use,
156
+ "PostToolUse": handle_post_tool_use,
157
+ "Notification": handle_notification,
158
+ "Stop": handle_stop,
159
+ "SubagentStop": handle_subagent_stop,
160
+ }
161
+
162
+ handler = handlers.get(event_name)
163
+ result = handler({"cwd": cwd, **data}) if handler else HookResult()
164
+
165
+ # Output Hook result as JSON
166
+ # Note: UserPromptSubmit uses to_user_prompt_submit_dict() for special schema
167
+ if event_name == "UserPromptSubmit":
168
+ print(json.dumps(result.to_user_prompt_submit_dict()))
169
+ else:
170
+ print(json.dumps(result.to_dict()))
171
+
172
+ sys.exit(0)
173
+
174
+ except json.JSONDecodeError as e:
175
+ # Return valid Hook response even on JSON parse error
176
+ error_response: dict[str, Any] = {
177
+ "continue": True,
178
+ "hookSpecificOutput": {"error": f"JSON parse error: {e}"}
179
+ }
180
+ print(json.dumps(error_response))
181
+ print(f"JSON parse error: {e}", file=sys.stderr)
182
+ sys.exit(1)
183
+ except Exception as e:
184
+ # Return valid Hook response even on unexpected error
185
+ error_response: dict[str, Any] = {
186
+ "continue": True,
187
+ "hookSpecificOutput": {"error": f"Hook error: {e}"}
188
+ }
189
+ print(json.dumps(error_response))
190
+ print(f"Unexpected error: {e}", file=sys.stderr)
191
+ sys.exit(1)
192
+
193
+ except HookTimeoutError:
194
+ # CRITICAL: Hook took too long - return minimal valid response to prevent Claude Code freeze
195
+ timeout_response: dict[str, Any] = {
156
196
  "continue": True,
157
- "hookSpecificOutput": {"error": f"JSON parse error: {e}"}
197
+ "systemMessage": "⚠️ Hook execution timeout - continuing without session info"
158
198
  }
159
- print(json.dumps(error_response))
160
- print(f"JSON parse error: {e}", file=sys.stderr)
161
- sys.exit(1)
162
- except Exception as e:
163
- # Return valid Hook response even on unexpected error
164
- error_response: dict[str, Any] = {
165
- "continue": True,
166
- "hookSpecificOutput": {"error": f"Hook error: {e}"}
167
- }
168
- print(json.dumps(error_response))
169
- print(f"Unexpected error: {e}", file=sys.stderr)
199
+ print(json.dumps(timeout_response))
200
+ print("Hook timeout after 5 seconds", file=sys.stderr)
170
201
  sys.exit(1)
171
202
 
203
+ finally:
204
+ # Always cancel the alarm to prevent signal leakage
205
+ signal.alarm(0)
206
+
172
207
 
173
208
  if __name__ == "__main__":
174
209
  main()