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.
- moai_adk/cli/commands/init.py +14 -2
- moai_adk/cli/commands/update.py +214 -56
- moai_adk/core/issue_creator.py +2 -2
- moai_adk/core/project/detector.py +201 -12
- moai_adk/core/project/initializer.py +62 -1
- moai_adk/core/project/phase_executor.py +48 -6
- moai_adk/core/tags/ci_validator.py +34 -4
- moai_adk/core/tags/pre_commit_validator.py +40 -2
- moai_adk/core/tags/reporter.py +2 -3
- moai_adk/core/tags/validator.py +1 -1
- moai_adk/core/template_engine.py +20 -5
- moai_adk/templates/.claude/agents/alfred/backend-expert.md +319 -0
- moai_adk/templates/.claude/agents/alfred/devops-expert.md +464 -0
- moai_adk/templates/.claude/agents/alfred/doc-syncer.md +1 -1
- moai_adk/templates/.claude/agents/alfred/frontend-expert.md +357 -0
- moai_adk/templates/.claude/agents/alfred/git-manager.md +2 -2
- moai_adk/templates/.claude/agents/alfred/implementation-planner.md +76 -3
- moai_adk/templates/.claude/agents/alfred/project-manager.md +49 -10
- moai_adk/templates/.claude/agents/alfred/quality-gate.md +3 -3
- moai_adk/templates/.claude/agents/alfred/spec-builder.md +180 -41
- moai_adk/templates/.claude/agents/alfred/tag-agent.md +74 -0
- moai_adk/templates/.claude/agents/alfred/tdd-implementer.md +107 -5
- moai_adk/templates/.claude/agents/alfred/trust-checker.md +2 -2
- moai_adk/templates/.claude/agents/alfred/ui-ux-expert.md +571 -0
- moai_adk/templates/.claude/commands/alfred/0-project.md +928 -263
- moai_adk/templates/.claude/commands/alfred/1-plan.md +220 -68
- moai_adk/templates/.claude/commands/alfred/2-run.md +299 -51
- moai_adk/templates/.claude/commands/alfred/3-sync.md +452 -51
- moai_adk/templates/.claude/commands/alfred/9-feedback.md +1 -1
- moai_adk/templates/.claude/hooks/alfred/core/project.py +25 -27
- moai_adk/templates/.claude/hooks/alfred/core/timeout.py +136 -0
- moai_adk/templates/.claude/hooks/alfred/core/ttl_cache.py +108 -0
- moai_adk/templates/.claude/hooks/alfred/core/version_cache.py +4 -4
- moai_adk/templates/.claude/hooks/alfred/handlers/__init__.py +29 -0
- moai_adk/templates/.claude/hooks/alfred/post_tool__log_changes.py +11 -19
- moai_adk/templates/.claude/hooks/alfred/pre_tool__auto_checkpoint.py +11 -19
- moai_adk/templates/.claude/hooks/alfred/session_end__cleanup.py +11 -19
- moai_adk/templates/.claude/hooks/alfred/session_start__show_project_info.py +10 -18
- moai_adk/templates/.claude/hooks/alfred/shared/core/__init__.py +2 -2
- moai_adk/templates/.claude/hooks/alfred/shared/core/checkpoint.py +3 -3
- moai_adk/templates/.claude/hooks/alfred/shared/core/context.py +5 -5
- moai_adk/templates/.claude/hooks/alfred/shared/core/project.py +40 -41
- moai_adk/templates/.claude/hooks/alfred/shared/core/tags.py +55 -23
- moai_adk/templates/.claude/hooks/alfred/shared/core/version_cache.py +4 -4
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/notification.py +132 -3
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/session.py +9 -10
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/tool.py +3 -6
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/user.py +19 -0
- moai_adk/templates/.claude/hooks/alfred/user_prompt__jit_load_docs.py +14 -22
- moai_adk/templates/.claude/hooks/alfred/utils/__init__.py +1 -0
- moai_adk/templates/.claude/hooks/alfred/utils/timeout.py +161 -0
- moai_adk/templates/.claude/settings.json +5 -5
- moai_adk/templates/.claude/skills/moai-alfred-agent-guide/SKILL.md +70 -0
- moai_adk/templates/.claude/skills/moai-alfred-agent-guide/examples.md +62 -0
- moai_adk/templates/{.moai/memory/CLAUDE-AGENTS-GUIDE.md → .claude/skills/moai-alfred-agent-guide/reference.md} +34 -0
- moai_adk/templates/.claude/skills/moai-alfred-config-schema/SKILL.md +56 -0
- moai_adk/templates/.claude/skills/moai-alfred-config-schema/examples.md +28 -0
- moai_adk/templates/.claude/skills/moai-alfred-config-schema/reference.md +444 -0
- moai_adk/templates/.claude/skills/moai-alfred-context-budget/SKILL.md +62 -0
- moai_adk/templates/.claude/skills/moai-alfred-context-budget/examples.md +28 -0
- moai_adk/templates/.claude/skills/moai-alfred-context-budget/reference.md +405 -0
- moai_adk/templates/.claude/skills/moai-alfred-dev-guide/SKILL.md +51 -0
- moai_adk/templates/.claude/skills/moai-alfred-dev-guide/examples.md +355 -0
- moai_adk/templates/.claude/skills/moai-alfred-dev-guide/reference.md +239 -0
- moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/SKILL.md +323 -0
- moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/examples.md +286 -0
- moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/reference.md +126 -0
- moai_adk/templates/.claude/skills/moai-alfred-gitflow-policy/SKILL.md +74 -0
- moai_adk/templates/.claude/skills/moai-alfred-gitflow-policy/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-gitflow-policy/reference.md +269 -0
- moai_adk/templates/.claude/skills/moai-alfred-issue-labels/SKILL.md +19 -0
- moai_adk/templates/.claude/skills/moai-alfred-issue-labels/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-persona-roles/SKILL.md +198 -0
- moai_adk/templates/.claude/skills/moai-alfred-persona-roles/examples.md +431 -0
- moai_adk/templates/.claude/skills/moai-alfred-persona-roles/reference.md +141 -0
- moai_adk/templates/.claude/skills/moai-alfred-practices/SKILL.md +89 -0
- moai_adk/templates/.claude/skills/moai-alfred-practices/examples.md +122 -0
- moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/SKILL.md +508 -0
- moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/examples.md +481 -0
- moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/reference.md +100 -0
- moai_adk/templates/.claude/skills/moai-alfred-reporting/SKILL.md +273 -0
- moai_adk/templates/.claude/skills/moai-alfred-rules/SKILL.md +77 -0
- moai_adk/templates/.claude/skills/moai-alfred-rules/examples.md +265 -0
- moai_adk/templates/.claude/skills/moai-alfred-session-state/SKILL.md +19 -0
- moai_adk/templates/.claude/skills/moai-alfred-session-state/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-session-state/reference.md +84 -0
- moai_adk/templates/.claude/skills/{moai-spec-authoring → moai-alfred-spec-authoring}/SKILL.md +5 -5
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-extended/SKILL.md +115 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-extended/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-extended/reference.md +348 -0
- moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/SKILL.md +19 -0
- moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/reference.md +211 -0
- moai_adk/templates/.claude/skills/moai-alfred-workflow/SKILL.md +288 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-descriptions/SKILL.md +19 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-descriptions/examples.md +4 -0
- moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/SKILL.md +3 -3
- moai_adk/templates/.claude/skills/moai-design-systems/SKILL.md +802 -0
- moai_adk/templates/.claude/skills/moai-design-systems/examples.md +1238 -0
- moai_adk/templates/.claude/skills/moai-design-systems/reference.md +673 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/SKILL.md +17 -13
- moai_adk/templates/.claude/skills/moai-lang-go/SKILL.md +15 -12
- moai_adk/templates/.claude/skills/moai-lang-java/SKILL.md +14 -12
- moai_adk/templates/.claude/skills/moai-lang-php/SKILL.md +14 -11
- moai_adk/templates/.claude/skills/moai-lang-python/SKILL.md +10 -8
- moai_adk/templates/.claude/skills/moai-lang-rust/SKILL.md +15 -12
- moai_adk/templates/.claude/skills/moai-lang-scala/SKILL.md +13 -11
- moai_adk/templates/.claude/skills/moai-lang-typescript/SKILL.md +16 -10
- moai_adk/templates/.claude/skills/moai-project-documentation.md +622 -0
- moai_adk/templates/.git-hooks/pre-push +143 -0
- moai_adk/templates/.github/workflows/c-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/cpp-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/csharp-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/dart-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/go-tag-validation.yml +130 -0
- moai_adk/templates/.github/workflows/java-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/javascript-tag-validation.yml +135 -0
- moai_adk/templates/.github/workflows/kotlin-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/moai-gitflow.yml +182 -25
- moai_adk/templates/.github/workflows/moai-release-pipeline.yml +35 -29
- moai_adk/templates/.github/workflows/php-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/python-tag-validation.yml +118 -0
- moai_adk/templates/.github/workflows/release.yml +76 -7
- moai_adk/templates/.github/workflows/ruby-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/rust-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/shell-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/spec-issue-sync.yml +208 -41
- moai_adk/templates/.github/workflows/swift-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/tag-report.yml +269 -0
- moai_adk/templates/.github/workflows/tag-validation.yml +186 -0
- moai_adk/templates/.github/workflows/typescript-tag-validation.yml +154 -0
- moai_adk/templates/.moai/config.json +3 -1
- moai_adk/templates/CLAUDE.md +940 -45
- moai_adk/templates/workflows/go-tag-validation.yml +30 -0
- moai_adk/templates/workflows/javascript-tag-validation.yml +41 -0
- moai_adk/templates/workflows/python-tag-validation.yml +42 -0
- moai_adk/templates/workflows/typescript-tag-validation.yml +31 -0
- moai_adk/utils/banner.py +5 -5
- {moai_adk-0.9.0.dist-info → moai_adk-0.15.1.dist-info}/METADATA +1253 -527
- {moai_adk-0.9.0.dist-info → moai_adk-0.15.1.dist-info}/RECORD +169 -109
- moai_adk/templates/.claude/hooks/alfred/alfred_hooks.py +0 -209
- moai_adk/templates/.claude/hooks/alfred/notification__handle_events.py +0 -102
- moai_adk/templates/.claude/hooks/alfred/stop__handle_interrupt.py +0 -102
- moai_adk/templates/.claude/hooks/alfred/subagent_stop__handle_subagent_end.py +0 -102
- moai_adk/templates/.claude/output-styles/alfred/agentic-coding.md +0 -640
- moai_adk/templates/.claude/output-styles/alfred/moai-adk-learning.md +0 -696
- moai_adk/templates/.claude/output-styles/alfred/study-with-alfred.md +0 -474
- moai_adk/templates/.github/ISSUE_TEMPLATE/spec.yml +0 -176
- moai_adk/templates/.github/PULL_REQUEST_TEMPLATE.md +0 -69
- moai_adk/templates/.moai/memory/DEVELOPMENT-GUIDE.md +0 -344
- moai_adk/templates/.moai/memory/SPEC-METADATA.md +0 -356
- moai_adk/templates/.moai/memory/gitflow-protection-policy.md +0 -330
- moai_adk/templates/.moai/project/product.md +0 -161
- moai_adk/templates/.moai/project/structure.md +0 -156
- moai_adk/templates/.moai/project/tech.md +0 -227
- moai_adk/templates/README.md +0 -256
- moai_adk/templates/__init__.py +0 -2
- /moai_adk/templates/{.moai/memory/ISSUE-LABEL-MAPPING.md → .claude/skills/moai-alfred-issue-labels/reference.md} +0 -0
- /moai_adk/templates/{.moai/memory/CLAUDE-PRACTICES.md → .claude/skills/moai-alfred-practices/reference.md} +0 -0
- /moai_adk/templates/{.moai/memory/CLAUDE-RULES.md → .claude/skills/moai-alfred-rules/reference.md} +0 -0
- /moai_adk/templates/.claude/skills/{moai-spec-authoring → moai-alfred-spec-authoring}/README.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-spec-authoring → moai-alfred-spec-authoring}/examples/validate-spec.sh +0 -0
- /moai_adk/templates/.claude/skills/{moai-spec-authoring → moai-alfred-spec-authoring}/examples.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-spec-authoring → moai-alfred-spec-authoring}/reference.md +0 -0
- /moai_adk/templates/{.moai/memory/SKILLS-DESCRIPTION-POLICY.md → .claude/skills/moai-cc-skill-descriptions/reference.md} +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/CHECKLIST.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/EXAMPLES.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/INTERACTIVE-DISCOVERY.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/METADATA.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/PARALLEL-ANALYSIS-REPORT.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/PYTHON-VERSION-MATRIX.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/SKILL-FACTORY-WORKFLOW.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/SKILL-UPDATE-ADVISOR.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/STEP-BY-STEP-GUIDE.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/STRUCTURE.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/WEB-RESEARCH.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/reference.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/scripts/generate-structure.sh +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/scripts/validate-skill.sh +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/templates/SKILL_TEMPLATE.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/templates/examples-template.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/templates/reference-template.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/templates/scripts-template.sh +0 -0
- {moai_adk-0.9.0.dist-info → moai_adk-0.15.1.dist-info}/WHEEL +0 -0
- {moai_adk-0.9.0.dist-info → moai_adk-0.15.1.dist-info}/entry_points.txt +0 -0
- {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-
|
|
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
|
-
|
|
52
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
52
|
-
|
|
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
|
|
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
|
-
|
|
90
|
+
timeout.cancel()
|
|
99
91
|
|
|
100
92
|
|
|
101
93
|
if __name__ == "__main__":
|
|
@@ -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, .
|
|
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
|
-
".
|
|
102
|
-
".
|
|
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" → .
|
|
26
|
-
- "/alfred:2-run" → .
|
|
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
|
-
['.
|
|
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": [".
|
|
53
|
-
"/alfred:2-run": [".
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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.
|
|
575
|
-
2.
|
|
576
|
-
3.
|
|
577
|
-
4. If
|
|
578
|
-
5.
|
|
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
|
|
590
|
-
- "is_major_update": Boolean indicating major version change
|
|
582
|
+
- "release_notes_url": URL to release notes
|
|
583
|
+
- "is_major_update": Boolean indicating major version change
|
|
591
584
|
|
|
592
|
-
|
|
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
|
-
-
|
|
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
|
-
|
|
611
|
+
version_cache_class = version_cache_module.VersionCache
|
|
617
612
|
else:
|
|
618
613
|
# Skip caching if module can't be loaded
|
|
619
|
-
|
|
614
|
+
version_cache_class = None
|
|
620
615
|
except (ImportError, OSError):
|
|
621
616
|
# Graceful degradation: skip caching on import errors
|
|
622
|
-
|
|
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 =
|
|
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 =
|
|
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"] =
|
|
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(
|
|
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",
|
|
42
|
-
".
|
|
43
|
-
".
|
|
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",
|
|
99
|
-
"
|
|
100
|
-
"**/*.test.
|
|
101
|
-
"**/*.
|
|
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/**",
|
|
116
|
-
"
|
|
117
|
-
"
|
|
118
|
-
"
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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"])
|