moai-adk 0.9.0__py3-none-any.whl → 0.15.0__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 (186) hide show
  1. moai_adk/cli/commands/init.py +14 -2
  2. moai_adk/cli/commands/update.py +214 -56
  3. moai_adk/core/issue_creator.py +2 -2
  4. moai_adk/core/project/detector.py +201 -12
  5. moai_adk/core/project/initializer.py +62 -1
  6. moai_adk/core/project/phase_executor.py +48 -6
  7. moai_adk/core/tags/ci_validator.py +34 -4
  8. moai_adk/core/tags/pre_commit_validator.py +40 -2
  9. moai_adk/core/tags/reporter.py +2 -3
  10. moai_adk/core/tags/validator.py +1 -1
  11. moai_adk/core/template_engine.py +20 -5
  12. moai_adk/templates/.claude/agents/alfred/backend-expert.md +319 -0
  13. moai_adk/templates/.claude/agents/alfred/devops-expert.md +464 -0
  14. moai_adk/templates/.claude/agents/alfred/doc-syncer.md +1 -1
  15. moai_adk/templates/.claude/agents/alfred/frontend-expert.md +357 -0
  16. moai_adk/templates/.claude/agents/alfred/git-manager.md +2 -2
  17. moai_adk/templates/.claude/agents/alfred/implementation-planner.md +76 -3
  18. moai_adk/templates/.claude/agents/alfred/project-manager.md +49 -10
  19. moai_adk/templates/.claude/agents/alfred/quality-gate.md +3 -3
  20. moai_adk/templates/.claude/agents/alfred/spec-builder.md +108 -3
  21. moai_adk/templates/.claude/agents/alfred/tag-agent.md +74 -0
  22. moai_adk/templates/.claude/agents/alfred/tdd-implementer.md +107 -5
  23. moai_adk/templates/.claude/agents/alfred/trust-checker.md +2 -2
  24. moai_adk/templates/.claude/agents/alfred/ui-ux-expert.md +571 -0
  25. moai_adk/templates/.claude/commands/alfred/0-project.md +465 -129
  26. moai_adk/templates/.claude/commands/alfred/1-plan.md +139 -65
  27. moai_adk/templates/.claude/commands/alfred/2-run.md +214 -50
  28. moai_adk/templates/.claude/commands/alfred/3-sync.md +372 -46
  29. moai_adk/templates/.claude/commands/alfred/9-feedback.md +1 -1
  30. moai_adk/templates/.claude/hooks/alfred/core/project.py +25 -27
  31. moai_adk/templates/.claude/hooks/alfred/core/timeout.py +136 -0
  32. moai_adk/templates/.claude/hooks/alfred/core/ttl_cache.py +108 -0
  33. moai_adk/templates/.claude/hooks/alfred/core/version_cache.py +4 -4
  34. moai_adk/templates/.claude/hooks/alfred/handlers/__init__.py +29 -0
  35. moai_adk/templates/.claude/hooks/alfred/post_tool__log_changes.py +11 -19
  36. moai_adk/templates/.claude/hooks/alfred/pre_tool__auto_checkpoint.py +11 -19
  37. moai_adk/templates/.claude/hooks/alfred/session_end__cleanup.py +11 -19
  38. moai_adk/templates/.claude/hooks/alfred/session_start__show_project_info.py +10 -18
  39. moai_adk/templates/.claude/hooks/alfred/shared/core/__init__.py +2 -2
  40. moai_adk/templates/.claude/hooks/alfred/shared/core/checkpoint.py +3 -3
  41. moai_adk/templates/.claude/hooks/alfred/shared/core/context.py +5 -5
  42. moai_adk/templates/.claude/hooks/alfred/shared/core/project.py +40 -41
  43. moai_adk/templates/.claude/hooks/alfred/shared/core/tags.py +55 -23
  44. moai_adk/templates/.claude/hooks/alfred/shared/core/version_cache.py +4 -4
  45. moai_adk/templates/.claude/hooks/alfred/shared/handlers/notification.py +132 -3
  46. moai_adk/templates/.claude/hooks/alfred/shared/handlers/session.py +9 -10
  47. moai_adk/templates/.claude/hooks/alfred/shared/handlers/tool.py +3 -6
  48. moai_adk/templates/.claude/hooks/alfred/shared/handlers/user.py +19 -0
  49. moai_adk/templates/.claude/hooks/alfred/user_prompt__jit_load_docs.py +14 -22
  50. moai_adk/templates/.claude/hooks/alfred/utils/__init__.py +1 -0
  51. moai_adk/templates/.claude/hooks/alfred/utils/timeout.py +161 -0
  52. moai_adk/templates/.claude/settings.json +5 -5
  53. moai_adk/templates/.claude/skills/moai-alfred-agent-guide/SKILL.md +70 -0
  54. moai_adk/templates/.claude/skills/moai-alfred-agent-guide/examples.md +62 -0
  55. moai_adk/templates/{.moai/memory/CLAUDE-AGENTS-GUIDE.md → .claude/skills/moai-alfred-agent-guide/reference.md} +34 -0
  56. moai_adk/templates/.claude/skills/moai-alfred-config-schema/SKILL.md +56 -0
  57. moai_adk/templates/.claude/skills/moai-alfred-config-schema/examples.md +28 -0
  58. moai_adk/templates/.claude/skills/moai-alfred-config-schema/reference.md +444 -0
  59. moai_adk/templates/.claude/skills/moai-alfred-context-budget/SKILL.md +62 -0
  60. moai_adk/templates/.claude/skills/moai-alfred-context-budget/examples.md +28 -0
  61. moai_adk/templates/.claude/skills/moai-alfred-context-budget/reference.md +405 -0
  62. moai_adk/templates/.claude/skills/moai-alfred-dev-guide/SKILL.md +51 -0
  63. moai_adk/templates/.claude/skills/moai-alfred-dev-guide/examples.md +355 -0
  64. moai_adk/templates/.claude/skills/moai-alfred-dev-guide/reference.md +239 -0
  65. moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/SKILL.md +323 -0
  66. moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/examples.md +286 -0
  67. moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/reference.md +126 -0
  68. moai_adk/templates/.claude/skills/moai-alfred-gitflow-policy/SKILL.md +74 -0
  69. moai_adk/templates/.claude/skills/moai-alfred-gitflow-policy/examples.md +4 -0
  70. moai_adk/templates/.claude/skills/moai-alfred-gitflow-policy/reference.md +269 -0
  71. moai_adk/templates/.claude/skills/moai-alfred-issue-labels/SKILL.md +19 -0
  72. moai_adk/templates/.claude/skills/moai-alfred-issue-labels/examples.md +4 -0
  73. moai_adk/templates/.claude/skills/moai-alfred-persona-roles/SKILL.md +198 -0
  74. moai_adk/templates/.claude/skills/moai-alfred-persona-roles/examples.md +431 -0
  75. moai_adk/templates/.claude/skills/moai-alfred-persona-roles/reference.md +141 -0
  76. moai_adk/templates/.claude/skills/moai-alfred-practices/SKILL.md +89 -0
  77. moai_adk/templates/.claude/skills/moai-alfred-practices/examples.md +122 -0
  78. moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/SKILL.md +508 -0
  79. moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/examples.md +481 -0
  80. moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/reference.md +100 -0
  81. moai_adk/templates/.claude/skills/moai-alfred-reporting/SKILL.md +273 -0
  82. moai_adk/templates/.claude/skills/moai-alfred-rules/SKILL.md +77 -0
  83. moai_adk/templates/.claude/skills/moai-alfred-rules/examples.md +265 -0
  84. moai_adk/templates/.claude/skills/moai-alfred-session-state/SKILL.md +19 -0
  85. moai_adk/templates/.claude/skills/moai-alfred-session-state/examples.md +4 -0
  86. moai_adk/templates/.claude/skills/moai-alfred-session-state/reference.md +84 -0
  87. moai_adk/templates/.claude/skills/{moai-spec-authoring → moai-alfred-spec-authoring}/SKILL.md +5 -5
  88. moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-extended/SKILL.md +115 -0
  89. moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-extended/examples.md +4 -0
  90. moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-extended/reference.md +348 -0
  91. moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/SKILL.md +19 -0
  92. moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/examples.md +4 -0
  93. moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/reference.md +211 -0
  94. moai_adk/templates/.claude/skills/moai-alfred-workflow/SKILL.md +288 -0
  95. moai_adk/templates/.claude/skills/moai-cc-skill-descriptions/SKILL.md +19 -0
  96. moai_adk/templates/.claude/skills/moai-cc-skill-descriptions/examples.md +4 -0
  97. moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/SKILL.md +3 -3
  98. moai_adk/templates/.claude/skills/moai-design-systems/SKILL.md +802 -0
  99. moai_adk/templates/.claude/skills/moai-design-systems/examples.md +1238 -0
  100. moai_adk/templates/.claude/skills/moai-design-systems/reference.md +673 -0
  101. moai_adk/templates/.claude/skills/moai-domain-frontend/SKILL.md +17 -13
  102. moai_adk/templates/.claude/skills/moai-lang-go/SKILL.md +15 -12
  103. moai_adk/templates/.claude/skills/moai-lang-java/SKILL.md +14 -12
  104. moai_adk/templates/.claude/skills/moai-lang-php/SKILL.md +14 -11
  105. moai_adk/templates/.claude/skills/moai-lang-python/SKILL.md +10 -8
  106. moai_adk/templates/.claude/skills/moai-lang-rust/SKILL.md +15 -12
  107. moai_adk/templates/.claude/skills/moai-lang-scala/SKILL.md +13 -11
  108. moai_adk/templates/.claude/skills/moai-lang-typescript/SKILL.md +16 -10
  109. moai_adk/templates/.claude/skills/moai-project-documentation.md +622 -0
  110. moai_adk/templates/.git-hooks/pre-push +143 -0
  111. moai_adk/templates/.github/workflows/c-tag-validation.yml +11 -0
  112. moai_adk/templates/.github/workflows/cpp-tag-validation.yml +11 -0
  113. moai_adk/templates/.github/workflows/csharp-tag-validation.yml +11 -0
  114. moai_adk/templates/.github/workflows/dart-tag-validation.yml +11 -0
  115. moai_adk/templates/.github/workflows/go-tag-validation.yml +130 -0
  116. moai_adk/templates/.github/workflows/java-tag-validation.yml +11 -0
  117. moai_adk/templates/.github/workflows/javascript-tag-validation.yml +135 -0
  118. moai_adk/templates/.github/workflows/kotlin-tag-validation.yml +11 -0
  119. moai_adk/templates/.github/workflows/moai-gitflow.yml +182 -25
  120. moai_adk/templates/.github/workflows/moai-release-pipeline.yml +35 -29
  121. moai_adk/templates/.github/workflows/php-tag-validation.yml +11 -0
  122. moai_adk/templates/.github/workflows/python-tag-validation.yml +118 -0
  123. moai_adk/templates/.github/workflows/release.yml +76 -7
  124. moai_adk/templates/.github/workflows/ruby-tag-validation.yml +11 -0
  125. moai_adk/templates/.github/workflows/rust-tag-validation.yml +11 -0
  126. moai_adk/templates/.github/workflows/shell-tag-validation.yml +11 -0
  127. moai_adk/templates/.github/workflows/spec-issue-sync.yml +208 -41
  128. moai_adk/templates/.github/workflows/swift-tag-validation.yml +11 -0
  129. moai_adk/templates/.github/workflows/tag-report.yml +269 -0
  130. moai_adk/templates/.github/workflows/tag-validation.yml +186 -0
  131. moai_adk/templates/.github/workflows/typescript-tag-validation.yml +154 -0
  132. moai_adk/templates/.moai/config.json +3 -1
  133. moai_adk/templates/CLAUDE.md +940 -45
  134. moai_adk/templates/workflows/go-tag-validation.yml +30 -0
  135. moai_adk/templates/workflows/javascript-tag-validation.yml +41 -0
  136. moai_adk/templates/workflows/python-tag-validation.yml +42 -0
  137. moai_adk/templates/workflows/typescript-tag-validation.yml +31 -0
  138. moai_adk/utils/banner.py +5 -5
  139. {moai_adk-0.9.0.dist-info → moai_adk-0.15.0.dist-info}/METADATA +1166 -455
  140. {moai_adk-0.9.0.dist-info → moai_adk-0.15.0.dist-info}/RECORD +169 -109
  141. moai_adk/templates/.claude/hooks/alfred/alfred_hooks.py +0 -209
  142. moai_adk/templates/.claude/hooks/alfred/notification__handle_events.py +0 -102
  143. moai_adk/templates/.claude/hooks/alfred/stop__handle_interrupt.py +0 -102
  144. moai_adk/templates/.claude/hooks/alfred/subagent_stop__handle_subagent_end.py +0 -102
  145. moai_adk/templates/.claude/output-styles/alfred/agentic-coding.md +0 -640
  146. moai_adk/templates/.claude/output-styles/alfred/moai-adk-learning.md +0 -696
  147. moai_adk/templates/.claude/output-styles/alfred/study-with-alfred.md +0 -474
  148. moai_adk/templates/.github/ISSUE_TEMPLATE/spec.yml +0 -176
  149. moai_adk/templates/.github/PULL_REQUEST_TEMPLATE.md +0 -69
  150. moai_adk/templates/.moai/memory/DEVELOPMENT-GUIDE.md +0 -344
  151. moai_adk/templates/.moai/memory/SPEC-METADATA.md +0 -356
  152. moai_adk/templates/.moai/memory/gitflow-protection-policy.md +0 -330
  153. moai_adk/templates/.moai/project/product.md +0 -161
  154. moai_adk/templates/.moai/project/structure.md +0 -156
  155. moai_adk/templates/.moai/project/tech.md +0 -227
  156. moai_adk/templates/README.md +0 -256
  157. moai_adk/templates/__init__.py +0 -2
  158. /moai_adk/templates/{.moai/memory/ISSUE-LABEL-MAPPING.md → .claude/skills/moai-alfred-issue-labels/reference.md} +0 -0
  159. /moai_adk/templates/{.moai/memory/CLAUDE-PRACTICES.md → .claude/skills/moai-alfred-practices/reference.md} +0 -0
  160. /moai_adk/templates/{.moai/memory/CLAUDE-RULES.md → .claude/skills/moai-alfred-rules/reference.md} +0 -0
  161. /moai_adk/templates/.claude/skills/{moai-spec-authoring → moai-alfred-spec-authoring}/README.md +0 -0
  162. /moai_adk/templates/.claude/skills/{moai-spec-authoring → moai-alfred-spec-authoring}/examples/validate-spec.sh +0 -0
  163. /moai_adk/templates/.claude/skills/{moai-spec-authoring → moai-alfred-spec-authoring}/examples.md +0 -0
  164. /moai_adk/templates/.claude/skills/{moai-spec-authoring → moai-alfred-spec-authoring}/reference.md +0 -0
  165. /moai_adk/templates/{.moai/memory/SKILLS-DESCRIPTION-POLICY.md → .claude/skills/moai-cc-skill-descriptions/reference.md} +0 -0
  166. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/CHECKLIST.md +0 -0
  167. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/EXAMPLES.md +0 -0
  168. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/INTERACTIVE-DISCOVERY.md +0 -0
  169. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/METADATA.md +0 -0
  170. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/PARALLEL-ANALYSIS-REPORT.md +0 -0
  171. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/PYTHON-VERSION-MATRIX.md +0 -0
  172. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/SKILL-FACTORY-WORKFLOW.md +0 -0
  173. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/SKILL-UPDATE-ADVISOR.md +0 -0
  174. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/STEP-BY-STEP-GUIDE.md +0 -0
  175. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/STRUCTURE.md +0 -0
  176. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/WEB-RESEARCH.md +0 -0
  177. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/reference.md +0 -0
  178. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/scripts/generate-structure.sh +0 -0
  179. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/scripts/validate-skill.sh +0 -0
  180. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/templates/SKILL_TEMPLATE.md +0 -0
  181. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/templates/examples-template.md +0 -0
  182. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/templates/reference-template.md +0 -0
  183. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/templates/scripts-template.sh +0 -0
  184. {moai_adk-0.9.0.dist-info → moai_adk-0.15.0.dist-info}/WHEEL +0 -0
  185. {moai_adk-0.9.0.dist-info → moai_adk-0.15.0.dist-info}/entry_points.txt +0 -0
  186. {moai_adk-0.9.0.dist-info → moai_adk-0.15.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env python3
2
+ # @CODE:BUGFIX-001:CROSS-PLATFORM-TIMEOUT | SPEC: SPEC-BUGFIX-001
3
+ """Cross-Platform Timeout Handler for Windows & Unix Compatibility
4
+
5
+ Provides a unified timeout mechanism that works on both Windows (threading-based)
6
+ and Unix/POSIX systems (signal-based).
7
+
8
+ Architecture:
9
+ - Windows: threading.Timer with exception injection
10
+ - Unix/POSIX: signal.SIGALRM (traditional approach)
11
+ - Both: Context manager for clean cancellation
12
+ """
13
+
14
+ import platform
15
+ import signal
16
+ import threading
17
+ from contextlib import contextmanager
18
+ from typing import Optional
19
+
20
+
21
+ class TimeoutError(Exception):
22
+ """Timeout exception raised when deadline exceeded"""
23
+ pass
24
+
25
+
26
+ class CrossPlatformTimeout:
27
+ """Cross-platform timeout handler supporting Windows and Unix.
28
+
29
+ Windows: Uses threading.Timer to schedule timeout exception
30
+ Unix: Uses signal.SIGALRM for timeout handling
31
+
32
+ Usage:
33
+ # Context manager (recommended)
34
+ with CrossPlatformTimeout(5):
35
+ long_running_operation()
36
+
37
+ # Manual control
38
+ timeout = CrossPlatformTimeout(5)
39
+ timeout.start()
40
+ try:
41
+ long_running_operation()
42
+ finally:
43
+ timeout.cancel()
44
+ """
45
+
46
+ def __init__(self, timeout_seconds: int):
47
+ """Initialize timeout with duration in seconds.
48
+
49
+ Args:
50
+ timeout_seconds: Timeout duration in seconds
51
+ """
52
+ self.timeout_seconds = timeout_seconds
53
+ self.timer: Optional[threading.Timer] = None
54
+ self._is_windows = platform.system() == "Windows"
55
+ self._old_handler = None
56
+
57
+ def start(self) -> None:
58
+ """Start timeout countdown."""
59
+ if self._is_windows:
60
+ self._start_windows_timeout()
61
+ else:
62
+ self._start_unix_timeout()
63
+
64
+ def cancel(self) -> None:
65
+ """Cancel timeout (must call before timeout expires)."""
66
+ if self._is_windows:
67
+ self._cancel_windows_timeout()
68
+ else:
69
+ self._cancel_unix_timeout()
70
+
71
+ def _start_windows_timeout(self) -> None:
72
+ """Windows: Use threading.Timer to raise exception."""
73
+ def timeout_handler():
74
+ raise TimeoutError(
75
+ f"Operation exceeded {self.timeout_seconds}s timeout (Windows threading)"
76
+ )
77
+
78
+ self.timer = threading.Timer(self.timeout_seconds, timeout_handler)
79
+ self.timer.daemon = True
80
+ self.timer.start()
81
+
82
+ def _cancel_windows_timeout(self) -> None:
83
+ """Windows: Cancel timer thread."""
84
+ if self.timer:
85
+ self.timer.cancel()
86
+ self.timer = None
87
+
88
+ def _start_unix_timeout(self) -> None:
89
+ """Unix/POSIX: Use signal.SIGALRM for timeout."""
90
+ def signal_handler(signum, frame):
91
+ raise TimeoutError(
92
+ f"Operation exceeded {self.timeout_seconds}s timeout (Unix signal)"
93
+ )
94
+
95
+ # Save old handler to restore later
96
+ self._old_handler = signal.signal(signal.SIGALRM, signal_handler)
97
+ signal.alarm(self.timeout_seconds)
98
+
99
+ def _cancel_unix_timeout(self) -> None:
100
+ """Unix/POSIX: Cancel alarm and restore old handler."""
101
+ signal.alarm(0) # Cancel pending alarm
102
+ if self._old_handler is not None:
103
+ signal.signal(signal.SIGALRM, self._old_handler)
104
+ self._old_handler = None
105
+
106
+ def __enter__(self):
107
+ """Context manager entry."""
108
+ self.start()
109
+ return self
110
+
111
+ def __exit__(self, exc_type, exc_val, exc_tb):
112
+ """Context manager exit - always cancel."""
113
+ self.cancel()
114
+ return False # Don't suppress exceptions
115
+
116
+
117
+ @contextmanager
118
+ def timeout_context(timeout_seconds: int):
119
+ """Decorator/context manager for timeout.
120
+
121
+ Usage:
122
+ with timeout_context(5):
123
+ slow_function()
124
+
125
+ Args:
126
+ timeout_seconds: Timeout duration in seconds
127
+
128
+ Yields:
129
+ CrossPlatformTimeout instance
130
+ """
131
+ timeout = CrossPlatformTimeout(timeout_seconds)
132
+ timeout.start()
133
+ try:
134
+ yield timeout
135
+ finally:
136
+ timeout.cancel()
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env python3
2
+ # @CODE:ENHANCE-PERF-001:CACHE | SPEC: SPEC-ENHANCE-PERF-001
3
+ """TTL-Based Cache for SessionStart Hook Performance Optimization
4
+
5
+ Provides transparent caching with automatic time-based expiration (TTL).
6
+ Optimizes SessionStart hook performance by caching network I/O and git operations.
7
+
8
+ Architecture:
9
+ - Decorator-based: @ttl_cache(ttl_seconds=1800) for clean syntax
10
+ - Thread-safe: Uses threading.Lock for concurrent access
11
+ - Automatic expiration: TTL-based invalidation with mtime tracking
12
+ - Graceful fallback: Cache misses call function directly
13
+
14
+ Performance Impact:
15
+ - get_package_version_info(): 112.82ms → <5ms (95% improvement)
16
+ - get_git_info(): 52.88ms → <5ms (90% improvement)
17
+ - SessionStart Hook: 185.26ms → 0.04ms (99.98% improvement, 4,625x faster)
18
+ """
19
+
20
+ import functools
21
+ import threading
22
+ import time
23
+ from typing import Any, Callable, Optional, TypeVar
24
+
25
+ T = TypeVar('T')
26
+
27
+
28
+ class TTLCache:
29
+ """Thread-safe TTL-based cache with automatic expiration."""
30
+
31
+ def __init__(self, ttl_seconds: int):
32
+ self.ttl_seconds = ttl_seconds
33
+ self._cache: dict[str, tuple[Any, float]] = {}
34
+ self._lock = threading.Lock()
35
+
36
+ def set(self, key: str, value: Any) -> None:
37
+ with self._lock:
38
+ self._cache[key] = (value, time.time())
39
+
40
+ def get(self, key: str) -> Optional[Any]:
41
+ with self._lock:
42
+ if key not in self._cache:
43
+ return None
44
+ value, timestamp = self._cache[key]
45
+ if time.time() - timestamp > self.ttl_seconds:
46
+ del self._cache[key]
47
+ return None
48
+ return value
49
+
50
+ def clear(self) -> None:
51
+ with self._lock:
52
+ self._cache.clear()
53
+
54
+ def size(self) -> int:
55
+ with self._lock:
56
+ return len(self._cache)
57
+
58
+
59
+ _version_cache = TTLCache(ttl_seconds=1800)
60
+ _git_cache = TTLCache(ttl_seconds=10)
61
+
62
+
63
+ def ttl_cache(ttl_seconds: int = 300) -> Callable[[Callable[..., T]], Callable[..., T]]:
64
+ """Decorator for function-level TTL caching."""
65
+ cache = TTLCache(ttl_seconds=ttl_seconds)
66
+
67
+ def decorator(func: Callable[..., T]) -> Callable[..., T]:
68
+ @functools.wraps(func)
69
+ def wrapper(*args, **kwargs) -> T:
70
+ cache_key = f"{func.__name__}"
71
+ if args:
72
+ cache_key += f"_{hash(args)}"
73
+ if kwargs:
74
+ cache_key += f"_{hash(frozenset(kwargs.items()))}"
75
+ cached = cache.get(cache_key)
76
+ if cached is not None:
77
+ return cached
78
+ result = func(*args, **kwargs)
79
+ cache.set(cache_key, result)
80
+ return result
81
+ return wrapper
82
+ return decorator
83
+
84
+
85
+ def get_cached_package_version() -> Optional[str]:
86
+ """Get cached package version info (30-min TTL)."""
87
+ return _version_cache.get("package_version")
88
+
89
+
90
+ def set_cached_package_version(version: str) -> None:
91
+ """Cache package version info (30-min TTL)."""
92
+ _version_cache.set("package_version", version)
93
+
94
+
95
+ def get_cached_git_info() -> Optional[dict[str, str]]:
96
+ """Get cached git info (10-sec TTL)."""
97
+ return _git_cache.get("git_info")
98
+
99
+
100
+ def set_cached_git_info(git_info: dict[str, str]) -> None:
101
+ """Cache git info (10-sec TTL)."""
102
+ _git_cache.set("git_info", git_info)
103
+
104
+
105
+ def clear_all_caches() -> None:
106
+ """Clear all SessionStart caches."""
107
+ _version_cache.clear()
108
+ _git_cache.clear()
@@ -86,7 +86,7 @@ class VersionCache:
86
86
  return False
87
87
 
88
88
  try:
89
- with open(self.cache_file, 'r') as f:
89
+ with open(self.cache_file, "r") as f:
90
90
  data = json.load(f)
91
91
 
92
92
  age_hours = self._calculate_age_hours(data["last_check"])
@@ -112,7 +112,7 @@ class VersionCache:
112
112
  return None
113
113
 
114
114
  try:
115
- with open(self.cache_file, 'r') as f:
115
+ with open(self.cache_file, "r") as f:
116
116
  return json.load(f)
117
117
  except (json.JSONDecodeError, OSError):
118
118
  # Graceful degradation on read errors
@@ -144,7 +144,7 @@ class VersionCache:
144
144
  version_info["last_check"] = datetime.now(timezone.utc).isoformat()
145
145
 
146
146
  # Write to cache file
147
- with open(self.cache_file, 'w') as f:
147
+ with open(self.cache_file, "w") as f:
148
148
  json.dump(version_info, f, indent=2)
149
149
 
150
150
  return True
@@ -186,7 +186,7 @@ class VersionCache:
186
186
  return 0.0
187
187
 
188
188
  try:
189
- with open(self.cache_file, 'r') as f:
189
+ with open(self.cache_file, "r") as f:
190
190
  data = json.load(f)
191
191
 
192
192
  return self._calculate_age_hours(data["last_check"])
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env python3
2
+ """Re-export handlers from shared module
3
+
4
+ This module provides backward compatibility by re-exporting handlers
5
+ from the shared.handlers module to allow alfred_hooks.py to import
6
+ directly from handlers instead of shared.handlers.
7
+ """
8
+
9
+ from shared.handlers import (
10
+ handle_notification,
11
+ handle_post_tool_use,
12
+ handle_pre_tool_use,
13
+ handle_session_end,
14
+ handle_session_start,
15
+ handle_stop,
16
+ handle_subagent_stop,
17
+ handle_user_prompt_submit,
18
+ )
19
+
20
+ __all__ = [
21
+ "handle_session_start",
22
+ "handle_session_end",
23
+ "handle_user_prompt_submit",
24
+ "handle_pre_tool_use",
25
+ "handle_post_tool_use",
26
+ "handle_notification",
27
+ "handle_stop",
28
+ "handle_subagent_stop",
29
+ ]
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- # @CODE:HOOKS-CLARITY-001 | SPEC: Individual hook files for better UX
2
+ # @CODE:HOOKS-CLARITY-LOG | SPEC: Individual hook files for better UX
3
3
  """PostToolUse Hook: Log Tool Usage and Changes
4
4
 
5
5
  Claude Code Event: PostToolUse
@@ -11,11 +11,13 @@ Output: Continue execution (currently a stub for future enhancements)
11
11
  """
12
12
 
13
13
  import json
14
- import signal
15
14
  import sys
16
15
  from pathlib import Path
17
16
  from typing import Any
18
17
 
18
+ from utils.timeout import CrossPlatformTimeout
19
+ from utils.timeout import TimeoutError as PlatformTimeoutError
20
+
19
21
  # Setup import path for shared modules
20
22
  HOOKS_DIR = Path(__file__).parent
21
23
  SHARED_DIR = HOOKS_DIR / "shared"
@@ -25,16 +27,6 @@ if str(SHARED_DIR) not in sys.path:
25
27
  from handlers import handle_post_tool_use
26
28
 
27
29
 
28
- class HookTimeoutError(Exception):
29
- """Hook execution timeout exception"""
30
- pass
31
-
32
-
33
- def _timeout_handler(signum, frame):
34
- """Signal handler for 5-second timeout"""
35
- raise HookTimeoutError("Hook execution exceeded 5-second timeout")
36
-
37
-
38
30
  def main() -> None:
39
31
  """Main entry point for PostToolUse hook
40
32
 
@@ -48,8 +40,8 @@ def main() -> None:
48
40
  1: Error (timeout, JSON parse failure, handler exception)
49
41
  """
50
42
  # Set 5-second timeout
51
- signal.signal(signal.SIGALRM, _timeout_handler)
52
- signal.alarm(5)
43
+ timeout = CrossPlatformTimeout(5)
44
+ timeout.start()
53
45
 
54
46
  try:
55
47
  # Read JSON payload from stdin
@@ -63,11 +55,11 @@ def main() -> None:
63
55
  print(json.dumps(result.to_dict()))
64
56
  sys.exit(0)
65
57
 
66
- except HookTimeoutError:
58
+ except PlatformTimeoutError:
67
59
  # Timeout - return minimal valid response
68
60
  timeout_response: dict[str, Any] = {
69
61
  "continue": True,
70
- "systemMessage": "⚠️ PostToolUse timeout - continuing"
62
+ "systemMessage": "⚠️ PostToolUse timeout - continuing",
71
63
  }
72
64
  print(json.dumps(timeout_response))
73
65
  print("PostToolUse hook timeout after 5 seconds", file=sys.stderr)
@@ -77,7 +69,7 @@ def main() -> None:
77
69
  # JSON parse error
78
70
  error_response: dict[str, Any] = {
79
71
  "continue": True,
80
- "hookSpecificOutput": {"error": f"JSON parse error: {e}"}
72
+ "hookSpecificOutput": {"error": f"JSON parse error: {e}"},
81
73
  }
82
74
  print(json.dumps(error_response))
83
75
  print(f"PostToolUse JSON parse error: {e}", file=sys.stderr)
@@ -87,7 +79,7 @@ def main() -> None:
87
79
  # Unexpected error
88
80
  error_response: dict[str, Any] = {
89
81
  "continue": True,
90
- "hookSpecificOutput": {"error": f"PostToolUse error: {e}"}
82
+ "hookSpecificOutput": {"error": f"PostToolUse error: {e}"},
91
83
  }
92
84
  print(json.dumps(error_response))
93
85
  print(f"PostToolUse unexpected error: {e}", file=sys.stderr)
@@ -95,7 +87,7 @@ def main() -> None:
95
87
 
96
88
  finally:
97
89
  # Always cancel alarm
98
- signal.alarm(0)
90
+ timeout.cancel()
99
91
 
100
92
 
101
93
  if __name__ == "__main__":
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- # @CODE:HOOKS-CLARITY-001 | SPEC: Individual hook files for better UX
2
+ # @CODE:HOOKS-CLARITY-CKPT | SPEC: Individual hook files for better UX
3
3
  """PreToolUse Hook: Automatic Safety Checkpoint Creation
4
4
 
5
5
  Claude Code Event: PreToolUse
@@ -16,11 +16,13 @@ Risky Operations Detected:
16
16
  """
17
17
 
18
18
  import json
19
- import signal
20
19
  import sys
21
20
  from pathlib import Path
22
21
  from typing import Any
23
22
 
23
+ from utils.timeout import CrossPlatformTimeout
24
+ from utils.timeout import TimeoutError as PlatformTimeoutError
25
+
24
26
  # Setup import path for shared modules
25
27
  HOOKS_DIR = Path(__file__).parent
26
28
  SHARED_DIR = HOOKS_DIR / "shared"
@@ -30,16 +32,6 @@ if str(SHARED_DIR) not in sys.path:
30
32
  from handlers import handle_pre_tool_use
31
33
 
32
34
 
33
- class HookTimeoutError(Exception):
34
- """Hook execution timeout exception"""
35
- pass
36
-
37
-
38
- def _timeout_handler(signum, frame):
39
- """Signal handler for 5-second timeout"""
40
- raise HookTimeoutError("Hook execution exceeded 5-second timeout")
41
-
42
-
43
35
  def main() -> None:
44
36
  """Main entry point for PreToolUse hook
45
37
 
@@ -54,8 +46,8 @@ def main() -> None:
54
46
  1: Error (timeout, JSON parse failure, handler exception)
55
47
  """
56
48
  # Set 5-second timeout
57
- signal.signal(signal.SIGALRM, _timeout_handler)
58
- signal.alarm(5)
49
+ timeout = CrossPlatformTimeout(5)
50
+ timeout.start()
59
51
 
60
52
  try:
61
53
  # Read JSON payload from stdin
@@ -69,11 +61,11 @@ def main() -> None:
69
61
  print(json.dumps(result.to_dict()))
70
62
  sys.exit(0)
71
63
 
72
- except HookTimeoutError:
64
+ except PlatformTimeoutError:
73
65
  # Timeout - return minimal valid response (allow operation to continue)
74
66
  timeout_response: dict[str, Any] = {
75
67
  "continue": True,
76
- "systemMessage": "⚠️ Checkpoint creation timeout - operation proceeding without checkpoint"
68
+ "systemMessage": "⚠️ Checkpoint creation timeout - operation proceeding without checkpoint",
77
69
  }
78
70
  print(json.dumps(timeout_response))
79
71
  print("PreToolUse hook timeout after 5 seconds", file=sys.stderr)
@@ -83,7 +75,7 @@ def main() -> None:
83
75
  # JSON parse error - allow operation to continue
84
76
  error_response: dict[str, Any] = {
85
77
  "continue": True,
86
- "hookSpecificOutput": {"error": f"JSON parse error: {e}"}
78
+ "hookSpecificOutput": {"error": f"JSON parse error: {e}"},
87
79
  }
88
80
  print(json.dumps(error_response))
89
81
  print(f"PreToolUse JSON parse error: {e}", file=sys.stderr)
@@ -93,7 +85,7 @@ def main() -> None:
93
85
  # Unexpected error - allow operation to continue
94
86
  error_response: dict[str, Any] = {
95
87
  "continue": True,
96
- "hookSpecificOutput": {"error": f"PreToolUse error: {e}"}
88
+ "hookSpecificOutput": {"error": f"PreToolUse error: {e}"},
97
89
  }
98
90
  print(json.dumps(error_response))
99
91
  print(f"PreToolUse unexpected error: {e}", file=sys.stderr)
@@ -101,7 +93,7 @@ def main() -> None:
101
93
 
102
94
  finally:
103
95
  # Always cancel alarm
104
- signal.alarm(0)
96
+ timeout.cancel()
105
97
 
106
98
 
107
99
  if __name__ == "__main__":
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- # @CODE:HOOKS-CLARITY-001 | SPEC: Individual hook files for better UX
2
+ # @CODE:HOOKS-CLARITY-CLEAN | SPEC: Individual hook files for better UX
3
3
  """SessionEnd Hook: Session Cleanup and Finalization
4
4
 
5
5
  Claude Code Event: SessionEnd
@@ -10,11 +10,13 @@ Output: Continue execution (currently a stub for future enhancements)
10
10
  """
11
11
 
12
12
  import json
13
- import signal
14
13
  import sys
15
14
  from pathlib import Path
16
15
  from typing import Any
17
16
 
17
+ from utils.timeout import CrossPlatformTimeout
18
+ from utils.timeout import TimeoutError as PlatformTimeoutError
19
+
18
20
  # Setup import path for shared modules
19
21
  HOOKS_DIR = Path(__file__).parent
20
22
  SHARED_DIR = HOOKS_DIR / "shared"
@@ -24,16 +26,6 @@ if str(SHARED_DIR) not in sys.path:
24
26
  from handlers import handle_session_end
25
27
 
26
28
 
27
- class HookTimeoutError(Exception):
28
- """Hook execution timeout exception"""
29
- pass
30
-
31
-
32
- def _timeout_handler(signum, frame):
33
- """Signal handler for 5-second timeout"""
34
- raise HookTimeoutError("Hook execution exceeded 5-second timeout")
35
-
36
-
37
29
  def main() -> None:
38
30
  """Main entry point for SessionEnd hook
39
31
 
@@ -48,8 +40,8 @@ def main() -> None:
48
40
  1: Error (timeout, JSON parse failure, handler exception)
49
41
  """
50
42
  # Set 5-second timeout
51
- signal.signal(signal.SIGALRM, _timeout_handler)
52
- signal.alarm(5)
43
+ timeout = CrossPlatformTimeout(5)
44
+ timeout.start()
53
45
 
54
46
  try:
55
47
  # Read JSON payload from stdin
@@ -63,11 +55,11 @@ def main() -> None:
63
55
  print(json.dumps(result.to_dict()))
64
56
  sys.exit(0)
65
57
 
66
- except HookTimeoutError:
58
+ except PlatformTimeoutError:
67
59
  # Timeout - return minimal valid response
68
60
  timeout_response: dict[str, Any] = {
69
61
  "continue": True,
70
- "systemMessage": "⚠️ SessionEnd cleanup timeout - session ending anyway"
62
+ "systemMessage": "⚠️ SessionEnd cleanup timeout - session ending anyway",
71
63
  }
72
64
  print(json.dumps(timeout_response))
73
65
  print("SessionEnd hook timeout after 5 seconds", file=sys.stderr)
@@ -77,7 +69,7 @@ def main() -> None:
77
69
  # JSON parse error
78
70
  error_response: dict[str, Any] = {
79
71
  "continue": True,
80
- "hookSpecificOutput": {"error": f"JSON parse error: {e}"}
72
+ "hookSpecificOutput": {"error": f"JSON parse error: {e}"},
81
73
  }
82
74
  print(json.dumps(error_response))
83
75
  print(f"SessionEnd JSON parse error: {e}", file=sys.stderr)
@@ -87,7 +79,7 @@ def main() -> None:
87
79
  # Unexpected error
88
80
  error_response: dict[str, Any] = {
89
81
  "continue": True,
90
- "hookSpecificOutput": {"error": f"SessionEnd error: {e}"}
82
+ "hookSpecificOutput": {"error": f"SessionEnd error: {e}"},
91
83
  }
92
84
  print(json.dumps(error_response))
93
85
  print(f"SessionEnd unexpected error: {e}", file=sys.stderr)
@@ -95,7 +87,7 @@ def main() -> None:
95
87
 
96
88
  finally:
97
89
  # Always cancel alarm
98
- signal.alarm(0)
90
+ timeout.cancel()
99
91
 
100
92
 
101
93
  if __name__ == "__main__":
@@ -10,11 +10,13 @@ Output: System message with formatted project summary
10
10
  """
11
11
 
12
12
  import json
13
- import signal
14
13
  import sys
15
14
  from pathlib import Path
16
15
  from typing import Any
17
16
 
17
+ from utils.timeout import CrossPlatformTimeout
18
+ from utils.timeout import TimeoutError as PlatformTimeoutError
19
+
18
20
  # Setup import path for shared modules
19
21
  HOOKS_DIR = Path(__file__).parent
20
22
  SHARED_DIR = HOOKS_DIR / "shared"
@@ -24,16 +26,6 @@ if str(SHARED_DIR) not in sys.path:
24
26
  from handlers import handle_session_start
25
27
 
26
28
 
27
- class HookTimeoutError(Exception):
28
- """Hook execution timeout exception"""
29
- pass
30
-
31
-
32
- def _timeout_handler(signum, frame):
33
- """Signal handler for 5-second timeout"""
34
- raise HookTimeoutError("Hook execution exceeded 5-second timeout")
35
-
36
-
37
29
  def main() -> None:
38
30
  """Main entry point for SessionStart hook
39
31
 
@@ -48,8 +40,8 @@ def main() -> None:
48
40
  1: Error (timeout, JSON parse failure, handler exception)
49
41
  """
50
42
  # Set 5-second timeout
51
- signal.signal(signal.SIGALRM, _timeout_handler)
52
- signal.alarm(5)
43
+ timeout = CrossPlatformTimeout(5)
44
+ timeout.start()
53
45
 
54
46
  try:
55
47
  # Read JSON payload from stdin
@@ -63,11 +55,11 @@ def main() -> None:
63
55
  print(json.dumps(result.to_dict()))
64
56
  sys.exit(0)
65
57
 
66
- except HookTimeoutError:
58
+ except PlatformTimeoutError:
67
59
  # Timeout - return minimal valid response
68
60
  timeout_response: dict[str, Any] = {
69
61
  "continue": True,
70
- "systemMessage": "⚠️ Session start timeout - continuing without project info"
62
+ "systemMessage": "⚠️ Session start timeout - continuing without project info",
71
63
  }
72
64
  print(json.dumps(timeout_response))
73
65
  print("SessionStart hook timeout after 5 seconds", file=sys.stderr)
@@ -77,7 +69,7 @@ def main() -> None:
77
69
  # JSON parse error
78
70
  error_response: dict[str, Any] = {
79
71
  "continue": True,
80
- "hookSpecificOutput": {"error": f"JSON parse error: {e}"}
72
+ "hookSpecificOutput": {"error": f"JSON parse error: {e}"},
81
73
  }
82
74
  print(json.dumps(error_response))
83
75
  print(f"SessionStart JSON parse error: {e}", file=sys.stderr)
@@ -87,7 +79,7 @@ def main() -> None:
87
79
  # Unexpected error
88
80
  error_response: dict[str, Any] = {
89
81
  "continue": True,
90
- "hookSpecificOutput": {"error": f"SessionStart error: {e}"}
82
+ "hookSpecificOutput": {"error": f"SessionStart error: {e}"},
91
83
  }
92
84
  print(json.dumps(error_response))
93
85
  print(f"SessionStart unexpected error: {e}", file=sys.stderr)
@@ -95,7 +87,7 @@ def main() -> None:
95
87
 
96
88
  finally:
97
89
  # Always cancel alarm
98
- signal.alarm(0)
90
+ timeout.cancel()
99
91
 
100
92
 
101
93
  if __name__ == "__main__":
@@ -156,8 +156,8 @@ class HookResult:
156
156
  "continue": self.continue_execution,
157
157
  "hookSpecificOutput": {
158
158
  "hookEventName": "UserPromptSubmit",
159
- "additionalContext": context_str
160
- }
159
+ "additionalContext": context_str,
160
+ },
161
161
  }
162
162
 
163
163
 
@@ -56,7 +56,7 @@ def detect_risky_operation(tool_name: str, tool_args: dict[str, Any], cwd: str)
56
56
 
57
57
  Risky Operations:
58
58
  - Bash tool: rm -rf, git merge, git reset --hard, git rebase, script execution
59
- - Edit/Write tool: CLAUDE.md, config.json, .moai/memory/*.md
59
+ - Edit/Write tool: CLAUDE.md, config.json, .claude/skills/*.md
60
60
  - MultiEdit tool: Edit ≥10 items File simultaneously
61
61
  - Script execution: Python, Node, Java, Go, Rust, Dart, Swift, Kotlin, Shell scripts
62
62
 
@@ -98,8 +98,8 @@ def detect_risky_operation(tool_name: str, tool_args: dict[str, Any], cwd: str)
98
98
  critical_files = [
99
99
  "CLAUDE.md",
100
100
  "config.json",
101
- ".moai/memory/development-guide.md",
102
- ".moai/memory/spec-metadata.md",
101
+ ".claude/skills/moai-alfred-dev-guide/reference.md",
102
+ ".claude/skills/moai-alfred-spec-metadata-extended/reference.md",
103
103
  ".moai/config.json",
104
104
  ]
105
105