Coverage for src / moai_adk / core / project / validator.py: 23.08%
52 statements
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-20 20:52 +0900
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-20 20:52 +0900
1"""Project initialization validation module.
3Validates system requirements and installation results.
5SPEC-INIT-004 Enhancement:
6- Alfred command files validation (Phase 5)
7- Explicit missing files reporting
8- Required files verification checklist
9"""
11import shutil
12from pathlib import Path
15class ValidationError(Exception):
16 """Raised when validation fails."""
18 pass
21class ProjectValidator:
22 """Validate project initialization."""
24 # Required directory structure
25 REQUIRED_DIRECTORIES = [
26 ".moai/",
27 ".moai/project/",
28 ".moai/specs/",
29 ".moai/memory/",
30 ".claude/",
31 ".github/",
32 ]
34 # Required files
35 REQUIRED_FILES = [
36 ".moai/config/config.json",
37 "CLAUDE.md",
38 ]
40 # Required Alfred command files (SPEC-INIT-004)
41 REQUIRED_ALFRED_COMMANDS = [
42 "0-project.md",
43 "1-plan.md",
44 "2-run.md",
45 "3-sync.md",
46 ]
48 def validate_system_requirements(self) -> None:
49 """Verify system requirements.
51 Raises:
52 ValidationError: Raised when requirements are not satisfied.
53 """
54 # Ensure Git is installed
55 if not shutil.which("git"):
56 raise ValidationError("Git is not installed")
58 # Check Python version (3.10+)
59 import sys
61 if sys.version_info < (3, 10):
62 raise ValidationError(
63 f"Python 3.10+ required (current: {sys.version_info.major}.{sys.version_info.minor})"
64 )
66 def validate_project_path(self, project_path: Path) -> None:
67 """Verify the project path.
69 Args:
70 project_path: Project path.
72 Raises:
73 ValidationError: Raised when the path is invalid.
74 """
75 # Must be an absolute path
76 if not project_path.is_absolute():
77 raise ValidationError(f"Project path must be absolute: {project_path}")
79 # Parent directory must exist
80 if not project_path.parent.exists():
81 raise ValidationError(
82 f"Parent directory does not exist: {project_path.parent}"
83 )
85 # Prevent initialization inside the MoAI-ADK package
86 if self._is_inside_moai_package(project_path):
87 raise ValidationError("Cannot initialize inside MoAI-ADK package directory")
89 def validate_installation(self, project_path: Path) -> None:
90 """Validate installation results.
93 Args:
94 project_path: Project path.
96 Raises:
97 ValidationError: Raised when installation was incomplete.
98 """
99 # Verify required directories
100 for directory in self.REQUIRED_DIRECTORIES:
101 dir_path = project_path / directory
102 if not dir_path.exists():
103 raise ValidationError(f"Required directory not found: {directory}")
105 # Verify required files
106 for file in self.REQUIRED_FILES:
107 file_path = project_path / file
108 if not file_path.exists():
109 raise ValidationError(f"Required file not found: {file}")
111 moai_commands_dir = project_path / ".claude" / "commands" / "moai"
112 missing_commands = []
113 for cmd in self.REQUIRED_ALFRED_COMMANDS:
114 cmd_path = moai_commands_dir / cmd
115 if not cmd_path.exists():
116 missing_commands.append(cmd)
118 if missing_commands:
119 missing_list = ", ".join(missing_commands)
120 raise ValidationError(
121 f"Required Alfred command files not found: {missing_list}"
122 )
124 def _is_inside_moai_package(self, project_path: Path) -> bool:
125 """Determine whether the path is inside the MoAI-ADK package.
127 Args:
128 project_path: Path to check.
130 Returns:
131 True when the path resides within the package.
132 """
133 # The package root contains a pyproject.toml referencing moai-adk
134 current = project_path.resolve()
135 while current != current.parent:
136 pyproject = current / "pyproject.toml"
137 if pyproject.exists():
138 try:
139 content = pyproject.read_text(encoding="utf-8")
140 if 'name = "moai-adk"' in content or 'name = "moai-adk"' in content:
141 return True
142 except Exception:
143 pass
144 current = current.parent
145 return False