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
@@ -4,21 +4,150 @@
4
4
  Notification, Stop, SubagentStop event handling
5
5
  """
6
6
 
7
+ import json
8
+ from datetime import datetime
9
+ from pathlib import Path
10
+
7
11
  from core import HookPayload, HookResult
8
12
 
9
13
 
14
+ def _get_command_state_file(cwd: str) -> Path:
15
+ """Get the path to command state tracking file"""
16
+ state_dir = Path(cwd) / ".moai" / "memory"
17
+ state_dir.mkdir(parents=True, exist_ok=True)
18
+ return state_dir / "command-execution-state.json"
19
+
20
+
21
+ def _load_command_state(cwd: str) -> dict:
22
+ """Load current command execution state"""
23
+ try:
24
+ state_file = _get_command_state_file(cwd)
25
+ if state_file.exists():
26
+ with open(state_file, "r", encoding="utf-8") as f:
27
+ return json.load(f)
28
+ except Exception:
29
+ pass
30
+ return {"last_command": None, "last_timestamp": None, "is_running": False}
31
+
32
+
33
+ def _save_command_state(cwd: str, state: dict) -> None:
34
+ """Save command execution state"""
35
+ try:
36
+ state_file = _get_command_state_file(cwd)
37
+ with open(state_file, "w", encoding="utf-8") as f:
38
+ json.dump(state, f, indent=2)
39
+ except Exception:
40
+ pass
41
+
42
+
43
+ def _is_duplicate_command(current_cmd: str, last_cmd: str, last_timestamp: str) -> bool:
44
+ """Check if current command is a duplicate of the last one within 3 seconds"""
45
+ if not last_cmd or not last_timestamp or current_cmd != last_cmd:
46
+ return False
47
+
48
+ try:
49
+ last_time = datetime.fromisoformat(last_timestamp)
50
+ current_time = datetime.now()
51
+ time_diff = (current_time - last_time).total_seconds()
52
+ # Consider it a duplicate if same command within 3 seconds
53
+ return time_diff < 3
54
+ except Exception:
55
+ return False
56
+
57
+
10
58
  def handle_notification(payload: HookPayload) -> HookResult:
11
- """Notification event handler (default implementation)"""
59
+ """Notification event handler
60
+
61
+ Detects and warns about duplicate command executions
62
+ (When the same /alfred: command is triggered multiple times within 3 seconds)
63
+ """
64
+ cwd = payload.get("cwd", ".")
65
+ notification = payload.get("notification", {})
66
+
67
+ # Extract command information from notification
68
+ current_cmd = None
69
+ if isinstance(notification, dict):
70
+ # Check if notification contains command information
71
+ text = notification.get("text", "") or str(notification)
72
+ if "/alfred:" in text:
73
+ # Extract /alfred: command
74
+ import re
75
+
76
+ match = re.search(r"/alfred:\S+", text)
77
+ if match:
78
+ current_cmd = match.group()
79
+
80
+ if not current_cmd:
81
+ return HookResult()
82
+
83
+ # Load current state
84
+ state = _load_command_state(cwd)
85
+ last_cmd = state.get("last_command")
86
+ last_timestamp = state.get("last_timestamp")
87
+
88
+ # Check for duplicate
89
+ if _is_duplicate_command(current_cmd, last_cmd, last_timestamp):
90
+ warning_msg = (
91
+ f"⚠️ Duplicate command detected: '{current_cmd}' "
92
+ f"is running multiple times within 3 seconds.\n"
93
+ f"This may indicate a system issue. Check logs in `.moai/logs/command-invocations.log`"
94
+ )
95
+
96
+ # Update state - mark as duplicate detected
97
+ state["duplicate_detected"] = True
98
+ state["duplicate_command"] = current_cmd
99
+ state["duplicate_timestamp"] = datetime.now().isoformat()
100
+ _save_command_state(cwd, state)
101
+
102
+ return HookResult(system_message=warning_msg, continue_execution=True)
103
+
104
+ # Update state with current command
105
+ state["last_command"] = current_cmd
106
+ state["last_timestamp"] = datetime.now().isoformat()
107
+ state["is_running"] = True
108
+ state["duplicate_detected"] = False
109
+ _save_command_state(cwd, state)
110
+
12
111
  return HookResult()
13
112
 
14
113
 
15
114
  def handle_stop(payload: HookPayload) -> HookResult:
16
- """Stop event handler (default implementation)"""
115
+ """Stop event handler
116
+
117
+ Marks command execution as complete
118
+ """
119
+ cwd = payload.get("cwd", ".")
120
+ state = _load_command_state(cwd)
121
+ state["is_running"] = False
122
+ state["last_timestamp"] = datetime.now().isoformat()
123
+ _save_command_state(cwd, state)
124
+
17
125
  return HookResult()
18
126
 
19
127
 
20
128
  def handle_subagent_stop(payload: HookPayload) -> HookResult:
21
- """SubagentStop event handler (default implementation)"""
129
+ """SubagentStop event handler
130
+
131
+ Records when a sub-agent finishes execution
132
+ """
133
+ cwd = payload.get("cwd", ".")
134
+
135
+ # Extract subagent name if available
136
+ subagent_name = (
137
+ payload.get("subagent", {}).get("name")
138
+ if isinstance(payload.get("subagent"), dict)
139
+ else None
140
+ )
141
+
142
+ try:
143
+ state_file = _get_command_state_file(cwd).parent / "subagent-execution.log"
144
+ timestamp = datetime.now().isoformat()
145
+
146
+ with open(state_file, "a", encoding="utf-8") as f:
147
+ f.write(f"{timestamp} | Subagent Stop | {subagent_name}\n")
148
+ except Exception:
149
+ pass
150
+
22
151
  return HookResult()
23
152
 
24
153
 
@@ -6,7 +6,7 @@ SessionStart, SessionEnd event handling
6
6
 
7
7
  from core import HookPayload, HookResult
8
8
  from core.checkpoint import list_checkpoints
9
- from core.project import count_specs, detect_language, get_git_info, get_package_version_info
9
+ from core.project import count_specs, get_git_info, get_package_version_info
10
10
 
11
11
 
12
12
  def handle_session_start(payload: HookPayload) -> HookResult:
@@ -25,14 +25,14 @@ def handle_session_start(payload: HookPayload) -> HookResult:
25
25
 
26
26
  Message Format:
27
27
  🚀 MoAI-ADK Session Started
28
- Language: {language}
28
+ [Version: {version}] - optional if version check fails
29
29
  [Branch: {branch} ({commit hash})] - optional if git fails
30
30
  [Changes: {Number of Changed Files}] - optional if git fails
31
31
  [SPEC Progress: {Complete}/{Total} ({percent}%)] - optional if specs fail
32
32
  [Checkpoints: {number} available] - optional if checkpoint list fails
33
33
 
34
34
  Graceful Degradation Strategy:
35
- - CRITICAL: Language detection (must succeed - no try-except)
35
+ - OPTIONAL: Version info (skip if timeout/failure)
36
36
  - OPTIONAL: Git info (skip if timeout/failure)
37
37
  - OPTIONAL: SPEC progress (skip if timeout/failure)
38
38
  - OPTIONAL: Checkpoint list (skip if timeout/failure)
@@ -66,9 +66,6 @@ def handle_session_start(payload: HookPayload) -> HookResult:
66
66
 
67
67
  cwd = payload.get("cwd", ".")
68
68
 
69
- # CRITICAL: Language detection - MUST succeed (no try-except)
70
- language = detect_language(cwd)
71
-
72
69
  # OPTIONAL: Git info - skip if timeout/failure
73
70
  git_info = {}
74
71
  try:
@@ -119,13 +116,17 @@ def handle_session_start(payload: HookPayload) -> HookResult:
119
116
  # Check if this is a major version update
120
117
  if version_info.get("is_major_update"):
121
118
  # Major version warning
122
- lines.append(f" ⚠️ Major version update available: {version_info['current']} → {version_info['latest']}")
119
+ lines.append(
120
+ f" ⚠️ Major version update available: {version_info['current']} → {version_info['latest']}"
121
+ )
123
122
  lines.append(" Breaking changes detected. Review release notes:")
124
123
  if version_info.get("release_notes_url"):
125
124
  lines.append(f" 📝 {version_info['release_notes_url']}")
126
125
  else:
127
126
  # Regular update
128
- lines.append(f" 🗿 MoAI-ADK Ver: {version_info['current']} → {version_info['latest']} available ✨")
127
+ lines.append(
128
+ f" 🗿 MoAI-ADK Ver: {version_info['current']} → {version_info['latest']} available ✨"
129
+ )
129
130
  if version_info.get("release_notes_url"):
130
131
  lines.append(f" 📝 Release Notes: {version_info['release_notes_url']}")
131
132
 
@@ -136,8 +137,6 @@ def handle_session_start(payload: HookPayload) -> HookResult:
136
137
  # No update available - show current version only
137
138
  lines.append(f" 🗿 MoAI-ADK Ver: {version_info['current']}")
138
139
 
139
- # Add language info
140
- lines.append(f" 🐍 Language: {language}")
141
140
 
142
141
  # Add Git info only if available (not degraded)
143
142
  if git_info:
@@ -54,8 +54,7 @@ def handle_pre_tool_use(payload: HookPayload) -> HookResult:
54
54
  checkpoint_branch = create_checkpoint(cwd, operation_type)
55
55
  if checkpoint_branch != "checkpoint-failed":
56
56
  system_message = (
57
- f"🛡️ Checkpoint created: {checkpoint_branch}\n"
58
- f" Operation: {operation_type}"
57
+ f"🛡️ Checkpoint created: {checkpoint_branch}\n Operation: {operation_type}"
59
58
  )
60
59
  return HookResult(system_message=system_message, continue_execution=True)
61
60
  except Exception:
@@ -66,10 +65,8 @@ def handle_pre_tool_use(payload: HookPayload) -> HookResult:
66
65
  issues = scan_recent_changes_for_missing_tags(cwd)
67
66
  if issues:
68
67
  # Summarize first few issues for display
69
- preview = "\n".join(
70
- f" - {i.path} 기대 태그: {i.expected}" for i in issues[:5]
71
- )
72
- more = "" if len(issues) <= 5 else f"\n (외 {len(issues)-5}건 더 존재)"
68
+ preview = "\n".join(f" - {i.path} → 기대 태그: {i.expected}" for i in issues[:5])
69
+ more = "" if len(issues) <= 5 else f"\n (외 {len(issues) - 5}건 더 존재)"
73
70
  msg = (
74
71
  "⚠️ TAG 누락 감지: 생성/수정한 파일 중 @TAG가 없는 항목이 있습니다.\n"
75
72
  f"{preview}{more}\n"
@@ -4,6 +4,9 @@
4
4
  Handling the UserPromptSubmit event
5
5
  """
6
6
 
7
+ from datetime import datetime
8
+ from pathlib import Path
9
+
7
10
  from core import HookPayload, HookResult
8
11
  from core.context import get_jit_context
9
12
 
@@ -29,11 +32,27 @@ def handle_user_prompt_submit(payload: HookPayload) -> HookResult:
29
32
  - GREEN: Recommend documents by calling get_jit_context()
30
33
  - REFACTOR: Message conditional display (only when there is a file)
31
34
  - UPDATE: Migrated to Claude Code standard Hook schema with snake_case fields
35
+ - FEATURE: Command execution logging for tracking double-run debugging
32
36
  """
33
37
  user_prompt = payload.get("userPrompt", "")
34
38
  cwd = payload.get("cwd", ".")
35
39
  context_files = get_jit_context(user_prompt, cwd)
36
40
 
41
+ # Command execution logging (DEBUG feature for tracking invocations)
42
+ if user_prompt.startswith("/alfred:"):
43
+ try:
44
+ log_dir = Path(cwd) / ".moai" / "logs"
45
+ log_dir.mkdir(parents=True, exist_ok=True)
46
+
47
+ log_file = log_dir / "command-invocations.log"
48
+ timestamp = datetime.now().isoformat()
49
+
50
+ with open(log_file, "a", encoding="utf-8") as f:
51
+ f.write(f"{timestamp} | {user_prompt}\n")
52
+ except Exception:
53
+ # Silently fail if logging fails (don't interrupt main flow)
54
+ pass
55
+
37
56
  system_message = f"📎 Loaded {len(context_files)} context file(s)" if context_files else None
38
57
 
39
58
  return HookResult(system_message=system_message, context_files=context_files)
@@ -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-DOCS | SPEC: Individual hook files for better UX
3
3
  """UserPromptSubmit Hook: Just-In-Time Document Loading
4
4
 
5
5
  Claude Code Event: UserPromptSubmit
@@ -10,11 +10,13 @@ Output: additionalContext with document path suggestions
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_user_prompt_submit
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 UserPromptSubmit hook
39
31
 
@@ -57,8 +49,8 @@ def main() -> None:
57
49
  }
58
50
  """
59
51
  # Set 5-second timeout
60
- signal.signal(signal.SIGALRM, _timeout_handler)
61
- signal.alarm(5)
52
+ timeout = CrossPlatformTimeout(5)
53
+ timeout.start()
62
54
 
63
55
  try:
64
56
  # Read JSON payload from stdin
@@ -72,14 +64,14 @@ def main() -> None:
72
64
  print(json.dumps(result.to_user_prompt_submit_dict()))
73
65
  sys.exit(0)
74
66
 
75
- except HookTimeoutError:
67
+ except PlatformTimeoutError:
76
68
  # Timeout - return minimal valid response
77
69
  timeout_response: dict[str, Any] = {
78
70
  "continue": True,
79
71
  "hookSpecificOutput": {
80
72
  "hookEventName": "UserPromptSubmit",
81
- "additionalContext": "⚠️ JIT context timeout - continuing without suggestions"
82
- }
73
+ "additionalContext": "⚠️ JIT context timeout - continuing without suggestions",
74
+ },
83
75
  }
84
76
  print(json.dumps(timeout_response))
85
77
  print("UserPromptSubmit hook timeout after 5 seconds", file=sys.stderr)
@@ -91,8 +83,8 @@ def main() -> None:
91
83
  "continue": True,
92
84
  "hookSpecificOutput": {
93
85
  "hookEventName": "UserPromptSubmit",
94
- "error": f"JSON parse error: {e}"
95
- }
86
+ "error": f"JSON parse error: {e}",
87
+ },
96
88
  }
97
89
  print(json.dumps(error_response))
98
90
  print(f"UserPromptSubmit JSON parse error: {e}", file=sys.stderr)
@@ -104,8 +96,8 @@ def main() -> None:
104
96
  "continue": True,
105
97
  "hookSpecificOutput": {
106
98
  "hookEventName": "UserPromptSubmit",
107
- "error": f"UserPromptSubmit error: {e}"
108
- }
99
+ "error": f"UserPromptSubmit error: {e}",
100
+ },
109
101
  }
110
102
  print(json.dumps(error_response))
111
103
  print(f"UserPromptSubmit unexpected error: {e}", file=sys.stderr)
@@ -113,7 +105,7 @@ def main() -> None:
113
105
 
114
106
  finally:
115
107
  # Always cancel alarm
116
- signal.alarm(0)
108
+ timeout.cancel()
117
109
 
118
110
 
119
111
  if __name__ == "__main__":
@@ -0,0 +1 @@
1
+ """Utility modules for Alfred hooks."""
@@ -0,0 +1,161 @@
1
+ #!/usr/bin/env python3
2
+ # @CODE:TIMEOUT-001 | SPEC: SPEC-TIMEOUT-HANDLING-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 Callable, 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: float, callback: Optional[Callable] = None):
47
+ """Initialize timeout with duration in seconds.
48
+
49
+ Args:
50
+ timeout_seconds: Timeout duration in seconds (float or int)
51
+ callback: Optional callback to execute before raising TimeoutError
52
+ """
53
+ # Convert float to int for signal.alarm()
54
+ self.timeout_seconds = timeout_seconds
55
+ self.timeout_seconds_int = max(1, int(timeout_seconds)) # signal.alarm requires >=1
56
+ self.callback = callback
57
+ self.timer: Optional[threading.Timer] = None
58
+ self._is_windows = platform.system() == "Windows"
59
+ self._old_handler = None
60
+
61
+ def start(self) -> None:
62
+ """Start timeout countdown."""
63
+ # Handle zero/negative timeouts
64
+ if self.timeout_seconds <= 0:
65
+ if self.timeout_seconds == 0:
66
+ # Zero timeout: raise immediately
67
+ if self.callback:
68
+ self.callback()
69
+ raise TimeoutError("Timeout of 0 seconds exceeded immediately")
70
+ else:
71
+ # Negative timeout: don't timeout at all
72
+ return
73
+
74
+ if self._is_windows:
75
+ self._start_windows_timeout()
76
+ else:
77
+ self._start_unix_timeout()
78
+
79
+ def cancel(self) -> None:
80
+ """Cancel timeout (must call before timeout expires)."""
81
+ if self._is_windows:
82
+ self._cancel_windows_timeout()
83
+ else:
84
+ self._cancel_unix_timeout()
85
+
86
+ def _start_windows_timeout(self) -> None:
87
+ """Windows: Use threading.Timer to raise exception."""
88
+ def timeout_handler():
89
+ if self.callback:
90
+ self.callback()
91
+ raise TimeoutError(
92
+ f"Operation exceeded {self.timeout_seconds}s timeout (Windows threading)"
93
+ )
94
+
95
+ self.timer = threading.Timer(self.timeout_seconds, timeout_handler)
96
+ self.timer.daemon = True
97
+ self.timer.start()
98
+
99
+ def _cancel_windows_timeout(self) -> None:
100
+ """Windows: Cancel timer thread."""
101
+ if self.timer:
102
+ self.timer.cancel()
103
+ self.timer = None
104
+
105
+ def _start_unix_timeout(self) -> None:
106
+ """Unix/POSIX: Use signal.SIGALRM for timeout."""
107
+ def signal_handler(signum, frame):
108
+ if self.callback:
109
+ try:
110
+ self.callback()
111
+ except Exception:
112
+ # Ignore callback exceptions, timeout error takes precedence
113
+ pass
114
+ raise TimeoutError(
115
+ f"Operation exceeded {self.timeout_seconds}s timeout (Unix signal)"
116
+ )
117
+
118
+ # Save old handler to restore later
119
+ self._old_handler = signal.signal(signal.SIGALRM, signal_handler)
120
+ # Use integer timeout_seconds_int for signal.alarm()
121
+ signal.alarm(self.timeout_seconds_int)
122
+
123
+ def _cancel_unix_timeout(self) -> None:
124
+ """Unix/POSIX: Cancel alarm and restore old handler."""
125
+ signal.alarm(0) # Cancel pending alarm
126
+ if self._old_handler is not None:
127
+ signal.signal(signal.SIGALRM, self._old_handler)
128
+ self._old_handler = None
129
+
130
+ def __enter__(self):
131
+ """Context manager entry."""
132
+ self.start()
133
+ return self
134
+
135
+ def __exit__(self, exc_type, exc_val, exc_tb):
136
+ """Context manager exit - always cancel."""
137
+ self.cancel()
138
+ return False # Don't suppress exceptions
139
+
140
+
141
+ @contextmanager
142
+ def timeout_context(timeout_seconds: float, callback: Optional[Callable] = None):
143
+ """Decorator/context manager for timeout.
144
+
145
+ Usage:
146
+ with timeout_context(5):
147
+ slow_function()
148
+
149
+ Args:
150
+ timeout_seconds: Timeout duration in seconds (float or int)
151
+ callback: Optional callback to execute before raising TimeoutError
152
+
153
+ Yields:
154
+ CrossPlatformTimeout instance
155
+ """
156
+ timeout = CrossPlatformTimeout(timeout_seconds, callback=callback)
157
+ timeout.start()
158
+ try:
159
+ yield timeout
160
+ finally:
161
+ timeout.cancel()
@@ -10,7 +10,7 @@
10
10
  {
11
11
  "hooks": [
12
12
  {
13
- "command": "uv run \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/alfred/session_start__show_project_info.py",
13
+ "command": "uv run .claude/hooks/alfred/session_start__show_project_info.py",
14
14
  "type": "command"
15
15
  }
16
16
  ]
@@ -20,7 +20,7 @@
20
20
  {
21
21
  "hooks": [
22
22
  {
23
- "command": "uv run \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/alfred/pre_tool__auto_checkpoint.py",
23
+ "command": "uv run .claude/hooks/alfred/pre_tool__auto_checkpoint.py",
24
24
  "type": "command"
25
25
  }
26
26
  ],
@@ -31,7 +31,7 @@
31
31
  {
32
32
  "hooks": [
33
33
  {
34
- "command": "uv run \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/alfred/user_prompt__jit_load_docs.py",
34
+ "command": "uv run .claude/hooks/alfred/user_prompt__jit_load_docs.py",
35
35
  "type": "command"
36
36
  }
37
37
  ]
@@ -41,7 +41,7 @@
41
41
  {
42
42
  "hooks": [
43
43
  {
44
- "command": "uv run \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/alfred/session_end__cleanup.py",
44
+ "command": "uv run .claude/hooks/alfred/session_end__cleanup.py",
45
45
  "type": "command"
46
46
  }
47
47
  ]
@@ -51,7 +51,7 @@
51
51
  {
52
52
  "hooks": [
53
53
  {
54
- "command": "uv run \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/alfred/post_tool__log_changes.py",
54
+ "command": "uv run .claude/hooks/alfred/post_tool__log_changes.py",
55
55
  "type": "command"
56
56
  }
57
57
  ],
@@ -0,0 +1,70 @@
1
+ ---
2
+ name: moai-alfred-agent-guide
3
+ description: "19-agent team structure, decision trees for agent selection, Haiku vs Sonnet model selection, and agent collaboration principles. Use when deciding which sub-agent to invoke, understanding team responsibilities, or learning multi-agent orchestration."
4
+ allowed-tools: "Read, Glob, Grep"
5
+ ---
6
+
7
+ ## What It Does
8
+
9
+ MoAI-ADK의 19개 Sub-agent 아키텍처, 어떤 agent를 선택할지 결정하는 트리, Haiku/Sonnet 모델 선택 기준을 정의합니다.
10
+
11
+ ## When to Use
12
+
13
+ - ✅ 어떤 sub-agent를 invoke할지 불명확
14
+ - ✅ Agent 책임 범위 학습
15
+ - ✅ Haiku vs Sonnet 모델 선택 필요
16
+ - ✅ Multi-agent 협업 패턴 이해
17
+
18
+ ## Agent Team at a Glance
19
+
20
+ ### 10 Core Sub-agents (Sonnet)
21
+ - spec-builder: SPEC 작성
22
+ - tdd-implementer: TDD 구현 (RED → GREEN → REFACTOR)
23
+ - doc-syncer: 문서 동기화
24
+ - implementation-planner: 구현 전략
25
+ - debug-helper: 오류 분석
26
+ - quality-gate: TRUST 5 검증
27
+ - tag-agent: TAG 체인 검증
28
+ - git-manager: Git 워크플로우
29
+ - Explore: 코드베이스 탐색
30
+ - Plan: 작업 계획
31
+
32
+ ### 4 Expert Agents (Sonnet - Proactively Triggered)
33
+ - **backend-expert**: Backend 아키텍처, API 설계, 데이터베이스
34
+ - **frontend-expert**: Frontend 아키텍처, 컴포넌트 설계, 상태 관리
35
+ - **devops-expert**: DevOps 전략, 배포, 인프라
36
+ - **ui-ux-expert**: UI/UX 설계, 접근성, 디자인 시스템 (Figma MCP)
37
+
38
+ ### 6 Specialist Agents (Haiku)
39
+ - project-manager: 프로젝트 초기화
40
+ - skill-factory: Skill 생성/최적화
41
+ - cc-manager: Claude Code 설정
42
+ - cc-hooks: Hook 시스템
43
+ - cc-mcp-plugins: MCP 서버
44
+ - trust-checker: TRUST 검증
45
+
46
+ ## Agent Selection Decision Tree
47
+
48
+ ```
49
+ Task Type?
50
+ ├─ SPEC 작성/검증 → spec-builder
51
+ ├─ TDD 구현 → tdd-implementer
52
+ ├─ 문서 동기화 → doc-syncer
53
+ ├─ 구현 계획 → implementation-planner
54
+ ├─ 오류 분석 → debug-helper
55
+ ├─ 품질 검증 → quality-gate + Skill("moai-foundation-trust")
56
+ ├─ 코드베이스 탐색 → Explore
57
+ ├─ Git 워크플로우 → git-manager
58
+ └─ 전체 프로젝트 계획 → Plan
59
+ ```
60
+
61
+ ## Model Selection
62
+
63
+ - **Sonnet**: Complex reasoning (spec-builder, tdd-implementer, implementation-planner)
64
+ - **Haiku**: Fast execution (project-manager, quality-gate, git-manager)
65
+
66
+ ---
67
+
68
+ Learn more in `reference.md` for complete agent responsibilities, collaboration patterns, and advanced orchestration strategies.
69
+
70
+ **Related Skills**: moai-alfred-rules, moai-alfred-practices