moai-adk 0.9.0__py3-none-any.whl → 0.15.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.

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 +180 -41
  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 +928 -263
  26. moai_adk/templates/.claude/commands/alfred/1-plan.md +220 -68
  27. moai_adk/templates/.claude/commands/alfred/2-run.md +299 -51
  28. moai_adk/templates/.claude/commands/alfred/3-sync.md +452 -51
  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.1.dist-info}/METADATA +1253 -527
  140. {moai_adk-0.9.0.dist-info → moai_adk-0.15.1.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.1.dist-info}/WHEEL +0 -0
  185. {moai_adk-0.9.0.dist-info → moai_adk-0.15.1.dist-info}/entry_points.txt +0 -0
  186. {moai_adk-0.9.0.dist-info → moai_adk-0.15.1.dist-info}/licenses/LICENSE +0 -0
@@ -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
 
@@ -22,13 +22,13 @@ def get_jit_context(prompt: str, cwd: str) -> list[str]:
22
22
  If there is no matching pattern or file, an empty list []
23
23
 
24
24
  Patterns:
25
- - "/alfred:1-plan" → .moai/memory/spec-metadata.md
26
- - "/alfred:2-run" → .moai/memory/development-guide.md
25
+ - "/alfred:1-plan" → .claude/skills/moai-alfred-spec-metadata-extended/reference.md
26
+ - "/alfred:2-run" → .claude/skills/moai-alfred-dev-guide/reference.md
27
27
  - "test" → tests/ (if directory exists)
28
28
 
29
29
  Examples:
30
30
  >>> get_jit_context("/alfred:1-plan", "/project")
31
- ['.moai/memory/spec-metadata.md']
31
+ ['.claude/skills/moai-alfred-spec-metadata-extended/reference.md']
32
32
  >>> get_jit_context("implement test", "/project")
33
33
  ['tests/']
34
34
  >>> get_jit_context("unknown", "/project")
@@ -49,8 +49,8 @@ def get_jit_context(prompt: str, cwd: str) -> list[str]:
49
49
 
50
50
  # Pattern matching
51
51
  patterns = {
52
- "/alfred:1-plan": [".moai/memory/spec-metadata.md"],
53
- "/alfred:2-run": [".moai/memory/development-guide.md"],
52
+ "/alfred:1-plan": [".claude/skills/moai-alfred-spec-metadata-extended/reference.md"],
53
+ "/alfred:2-run": [".claude/skills/moai-alfred-dev-guide/reference.md"],
54
54
  "test": ["tests/"],
55
55
  }
56
56
 
@@ -70,6 +70,7 @@ def find_project_root(start_path: str | Path = ".") -> Path:
70
70
 
71
71
  class TimeoutError(Exception):
72
72
  """Signal-based timeout exception"""
73
+
73
74
  pass
74
75
 
75
76
 
@@ -86,6 +87,7 @@ def timeout_handler(seconds: int):
86
87
  Raises:
87
88
  TimeoutError: If operation exceeds timeout
88
89
  """
90
+
89
91
  def _handle_timeout(signum, frame):
90
92
  raise TimeoutError(f"Operation timed out after {seconds} seconds")
91
93
 
@@ -427,19 +429,10 @@ def get_version_check_config(cwd: str) -> dict[str, Any]:
427
429
  - REFACTOR: Add validation and error handling
428
430
  """
429
431
  # TTL mapping by frequency
430
- TTL_BY_FREQUENCY = {
431
- "always": 0,
432
- "daily": 24,
433
- "weekly": 168,
434
- "never": float('inf')
435
- }
432
+ ttl_by_frequency = {"always": 0, "daily": 24, "weekly": 168, "never": float("inf")}
436
433
 
437
434
  # Default configuration
438
- defaults = {
439
- "enabled": True,
440
- "frequency": "daily",
441
- "cache_ttl_hours": 24
442
- }
435
+ defaults = {"enabled": True, "frequency": "daily", "cache_ttl_hours": 24}
443
436
 
444
437
  # Find project root to ensure we read config from correct location
445
438
  project_root = find_project_root(cwd)
@@ -461,21 +454,17 @@ def get_version_check_config(cwd: str) -> dict[str, Any]:
461
454
  frequency = moai_config.get("update_check_frequency", defaults["frequency"])
462
455
 
463
456
  # Validate frequency
464
- if frequency not in TTL_BY_FREQUENCY:
457
+ if frequency not in ttl_by_frequency:
465
458
  frequency = defaults["frequency"]
466
459
 
467
460
  # Calculate TTL from frequency
468
- cache_ttl_hours = TTL_BY_FREQUENCY[frequency]
461
+ cache_ttl_hours = ttl_by_frequency[frequency]
469
462
 
470
463
  # Allow explicit cache_ttl_hours override
471
464
  if "cache_ttl_hours" in version_check_config:
472
465
  cache_ttl_hours = version_check_config["cache_ttl_hours"]
473
466
 
474
- return {
475
- "enabled": enabled,
476
- "frequency": frequency,
477
- "cache_ttl_hours": cache_ttl_hours
478
- }
467
+ return {"enabled": enabled, "frequency": frequency, "cache_ttl_hours": cache_ttl_hours}
479
468
 
480
469
  except (OSError, json.JSONDecodeError, KeyError):
481
470
  # Config read or parse error - return defaults
@@ -570,36 +559,42 @@ def is_major_version_change(current: str, latest: str) -> bool:
570
559
  def get_package_version_info(cwd: str = ".") -> dict[str, Any]:
571
560
  """Check MoAI-ADK current and latest version with caching and offline support.
572
561
 
562
+ ⭐ CRITICAL GUARANTEE: This function ALWAYS returns the current installed version.
563
+ Network failures, cache issues, and timeouts NEVER result in "unknown" version.
564
+
573
565
  Execution flow:
574
- 1. Try to load from cache (< 50ms)
575
- 2. If cache invalid, check network
576
- 3. If network available, query PyPI
577
- 4. If network unavailable, return current version only
578
- 5. Save result to cache for next time
566
+ 1. Get current installed version (ALWAYS succeeds) ← CRITICAL
567
+ 2. Build minimal result with current version
568
+ 3. Try to load from cache (< 50ms) - optional enhancement
569
+ 4. If cache valid, return cached latest info
570
+ 5. If cache invalid/miss, optionally query PyPI - optional enhancement
571
+ 6. Save result to cache for next time - optional
579
572
 
580
573
  Args:
581
574
  cwd: Project root directory (for cache location)
582
575
 
583
576
  Returns:
584
577
  dict with keys:
585
- - "current": Current installed version
586
- - "latest": Latest version available on PyPI
578
+ - "current": Current installed version (ALWAYS valid, never empty)
579
+ - "latest": Latest version available on PyPI (may be "unknown")
587
580
  - "update_available": Boolean indicating if update is available
588
581
  - "upgrade_command": Recommended upgrade command (if update available)
589
- - "release_notes_url": URL to release notes (Phase 3)
590
- - "is_major_update": Boolean indicating major version change (Phase 3)
582
+ - "release_notes_url": URL to release notes
583
+ - "is_major_update": Boolean indicating major version change
591
584
 
592
- Note:
593
- - Cache hit (< 24 hours): Returns in ~20ms, no network access
594
- - Cache miss + online: Query PyPI (1s timeout), cache result
595
- - Cache miss + offline: Return current version only (~100ms)
596
- - Offline + cached: Return from cache in ~20ms
585
+ Guarantees:
586
+ - Cache hit (< 24 hours): Returns in ~20ms, no network access
587
+ - Cache miss + online: Query PyPI (1s timeout), cache result
588
+ - Cache miss + offline: Return current version only (~100ms)
589
+ - Network timeout: Returns current + "unknown" latest (~50ms) ✓
590
+ - Any exception: Always returns current version ✓
597
591
 
598
592
  TDD History:
599
593
  - RED: 5 test scenarios (network detection, cache integration, offline mode)
600
594
  - GREEN: Integrate VersionCache with network detection
601
595
  - REFACTOR: Extract cache directory constant, improve error handling
602
596
  - Phase 3: Add release_notes_url and is_major_update fields (@CODE:VERSION-INTEGRATE-FIELDS-001)
597
+ - Phase 4: CRITICAL FIX - Always guarantee current version return (@CODE:VERSION-ALWAYS-VALID-001)
603
598
  """
604
599
  import importlib.util
605
600
  import urllib.error
@@ -613,13 +608,13 @@ def get_package_version_info(cwd: str = ".") -> dict[str, Any]:
613
608
  if spec and spec.loader:
614
609
  version_cache_module = importlib.util.module_from_spec(spec)
615
610
  spec.loader.exec_module(version_cache_module)
616
- VersionCache = version_cache_module.VersionCache
611
+ version_cache_class = version_cache_module.VersionCache
617
612
  else:
618
613
  # Skip caching if module can't be loaded
619
- VersionCache = None
614
+ version_cache_class = None
620
615
  except (ImportError, OSError):
621
616
  # Graceful degradation: skip caching on import errors
622
- VersionCache = None
617
+ version_cache_class = None
623
618
 
624
619
  # 1. Find project root (ensure cache is always in correct location)
625
620
  # This prevents creating .moai/cache in wrong locations when hooks run
@@ -628,7 +623,7 @@ def get_package_version_info(cwd: str = ".") -> dict[str, Any]:
628
623
 
629
624
  # 2. Initialize cache (skip if VersionCache couldn't be imported)
630
625
  cache_dir = project_root / CACHE_DIR_NAME
631
- version_cache = VersionCache(cache_dir) if VersionCache else None
626
+ version_cache = version_cache_class(cache_dir) if version_cache_class else None
632
627
 
633
628
  # 2. Get current installed version first (needed for cache validation)
634
629
  current_version = "unknown"
@@ -641,7 +636,7 @@ def get_package_version_info(cwd: str = ".") -> dict[str, Any]:
641
636
  "current": "dev",
642
637
  "latest": "unknown",
643
638
  "update_available": False,
644
- "upgrade_command": ""
639
+ "upgrade_command": "",
645
640
  }
646
641
 
647
642
  # 3. Try to load from cache (fast path with version validation)
@@ -664,7 +659,7 @@ def get_package_version_info(cwd: str = ".") -> dict[str, Any]:
664
659
  "current": current_version,
665
660
  "latest": "unknown",
666
661
  "update_available": False,
667
- "upgrade_command": ""
662
+ "upgrade_command": "",
668
663
  }
669
664
 
670
665
  # 5. Check if version check is enabled in config
@@ -695,7 +690,9 @@ def get_package_version_info(cwd: str = ".") -> dict[str, Any]:
695
690
  release_url = project_urls.get("Changelog", "")
696
691
  if not release_url:
697
692
  # Fallback to GitHub releases URL pattern
698
- release_url = f"https://github.com/modu-ai/moai-adk/releases/tag/v{result['latest']}"
693
+ release_url = (
694
+ f"https://github.com/modu-ai/moai-adk/releases/tag/v{result['latest']}"
695
+ )
699
696
  result["release_notes_url"] = release_url
700
697
  except (KeyError, AttributeError, TypeError):
701
698
  result["release_notes_url"] = None
@@ -719,10 +716,12 @@ def get_package_version_info(cwd: str = ".") -> dict[str, Any]:
719
716
 
720
717
  if latest_parts > current_parts:
721
718
  result["update_available"] = True
722
- result["upgrade_command"] = f"uv pip install --upgrade moai-adk>={result['latest']}"
719
+ result["upgrade_command"] = "uv tool upgrade moai-adk"
723
720
 
724
721
  # Detect major version change
725
- result["is_major_update"] = is_major_version_change(result["current"], result["latest"])
722
+ result["is_major_update"] = is_major_version_change(
723
+ result["current"], result["latest"]
724
+ )
726
725
  else:
727
726
  result["is_major_update"] = False
728
727
  except (ValueError, AttributeError):
@@ -38,9 +38,22 @@ from pathlib import Path
38
38
  from typing import Iterable, List, Optional
39
39
 
40
40
  DEFAULT_CODE_EXTS = (
41
- ".py", ".ts", ".tsx", ".js", ".jsx", ".go", ".rs",
42
- ".java", ".kt", ".rb", ".php", ".c", ".cpp", ".cs",
43
- ".swift", ".scala"
41
+ ".py",
42
+ ".ts",
43
+ ".tsx",
44
+ ".js",
45
+ ".jsx",
46
+ ".go",
47
+ ".rs",
48
+ ".java",
49
+ ".kt",
50
+ ".rb",
51
+ ".php",
52
+ ".c",
53
+ ".cpp",
54
+ ".cs",
55
+ ".swift",
56
+ ".scala",
44
57
  )
45
58
 
46
59
 
@@ -88,35 +101,49 @@ def _load_rules(cwd: str) -> List[Rule]:
88
101
 
89
102
  # Defaults (ordered)
90
103
  return [
91
- Rule(
92
- include=[".moai/specs/**", "**/SPEC-*/spec.md"],
93
- expect="@SPEC:",
94
- exclude=[]
95
- ),
104
+ Rule(include=[".moai/specs/**", "**/SPEC-*/spec.md"], expect="@SPEC:", exclude=[]),
96
105
  Rule(
97
106
  include=[
98
- "**/*_test.py", "**/test_*.py", "**/*.test.ts",
99
- "**/*.test.tsx", "**/*.test.js", "**/*.test.jsx",
100
- "**/*.test.go", "**/*.test.rs", "**/*.spec.ts",
101
- "**/*.spec.tsx", "tests/**"
107
+ "**/*_test.py",
108
+ "**/test_*.py",
109
+ "**/*.test.ts",
110
+ "**/*.test.tsx",
111
+ "**/*.test.js",
112
+ "**/*.test.jsx",
113
+ "**/*.test.go",
114
+ "**/*.test.rs",
115
+ "**/*.spec.ts",
116
+ "**/*.spec.tsx",
117
+ "tests/**",
102
118
  ],
103
119
  expect="@TEST:",
104
- exclude=[".claude/**"]
120
+ exclude=[".claude/**"],
105
121
  ),
106
122
  Rule(
107
123
  include=["docs/**/*.md", "**/README.md", "**/*.api.md"],
108
124
  expect="@DOC:",
109
- exclude=[".claude/**"]
125
+ exclude=[".claude/**"],
110
126
  ),
111
127
  Rule(
112
128
  include=["**/*"],
113
129
  expect="@CODE:",
114
130
  exclude=[
115
- "tests/**", "docs/**", ".moai/**", ".claude/**",
116
- "**/*.md", "**/*.json", "**/*.yml", "**/*.yaml",
117
- "**/*.toml", "**/*.lock", "**/*.svg", "**/*.png",
118
- "**/*.jpg", "**/*.jpeg", "**/*.gif"
119
- ]
131
+ "tests/**",
132
+ "docs/**",
133
+ ".moai/**",
134
+ ".claude/**",
135
+ "**/*.md",
136
+ "**/*.json",
137
+ "**/*.yml",
138
+ "**/*.yaml",
139
+ "**/*.toml",
140
+ "**/*.lock",
141
+ "**/*.svg",
142
+ "**/*.png",
143
+ "**/*.jpg",
144
+ "**/*.jpeg",
145
+ "**/*.gif",
146
+ ],
120
147
  ),
121
148
  ]
122
149
 
@@ -147,17 +174,22 @@ def _iter_recent_changes(cwd: str) -> Iterable[Path]:
147
174
  # Staged files
148
175
  r1 = subprocess.run(
149
176
  ["git", "diff", "--name-only", "--cached"],
150
- cwd=cwd, capture_output=True, text=True, timeout=1
177
+ cwd=cwd,
178
+ capture_output=True,
179
+ text=True,
180
+ timeout=1,
151
181
  )
152
182
  # Modified (unstaged) tracked files
153
183
  r2 = subprocess.run(
154
- ["git", "ls-files", "-m"],
155
- cwd=cwd, capture_output=True, text=True, timeout=1
184
+ ["git", "ls-files", "-m"], cwd=cwd, capture_output=True, text=True, timeout=1
156
185
  )
157
186
  # Untracked (other) files respecting .gitignore
158
187
  r3 = subprocess.run(
159
188
  ["git", "ls-files", "-o", "--exclude-standard"],
160
- cwd=cwd, capture_output=True, text=True, timeout=1
189
+ cwd=cwd,
190
+ capture_output=True,
191
+ text=True,
192
+ timeout=1,
161
193
  )
162
194
  names = set()
163
195
  if r1.returncode == 0:
@@ -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"])