moai-adk 0.4.0__py3-none-any.whl → 0.4.4__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 (130) hide show
  1. moai_adk/__init__.py +2 -3
  2. moai_adk/cli/commands/init.py +10 -5
  3. moai_adk/cli/commands/update.py +274 -118
  4. moai_adk/cli/prompts/init_prompts.py +14 -18
  5. moai_adk/core/diagnostics/slash_commands.py +1 -1
  6. moai_adk/core/project/backup_utils.py +2 -11
  7. moai_adk/core/project/checker.py +2 -2
  8. moai_adk/core/project/phase_executor.py +11 -14
  9. moai_adk/core/project/validator.py +3 -2
  10. moai_adk/core/quality/__init__.py +1 -1
  11. moai_adk/core/quality/trust_checker.py +63 -63
  12. moai_adk/core/quality/validators/__init__.py +1 -1
  13. moai_adk/core/quality/validators/base_validator.py +1 -1
  14. moai_adk/core/template/backup.py +21 -8
  15. moai_adk/core/template/merger.py +14 -4
  16. moai_adk/core/template/processor.py +24 -5
  17. moai_adk/templates/.claude/agents/alfred/cc-manager.md +446 -424
  18. moai_adk/templates/.claude/agents/alfred/debug-helper.md +116 -103
  19. moai_adk/templates/.claude/agents/alfred/doc-syncer.md +130 -116
  20. moai_adk/templates/.claude/agents/alfred/git-manager.md +186 -174
  21. moai_adk/templates/.claude/agents/alfred/implementation-planner.md +227 -213
  22. moai_adk/templates/.claude/agents/alfred/project-manager.md +216 -128
  23. moai_adk/templates/.claude/agents/alfred/quality-gate.md +224 -209
  24. moai_adk/templates/.claude/agents/alfred/spec-builder.md +174 -160
  25. moai_adk/templates/.claude/agents/alfred/tag-agent.md +151 -139
  26. moai_adk/templates/.claude/agents/alfred/tdd-implementer.md +209 -196
  27. moai_adk/templates/.claude/agents/alfred/trust-checker.md +247 -233
  28. moai_adk/templates/.claude/commands/alfred/0-project.md +756 -640
  29. moai_adk/templates/.claude/commands/alfred/1-plan.md +343 -333
  30. moai_adk/templates/.claude/commands/alfred/2-run.md +297 -285
  31. moai_adk/templates/.claude/commands/alfred/3-sync.md +387 -356
  32. moai_adk/templates/.claude/hooks/alfred/README.md +52 -52
  33. moai_adk/templates/.claude/hooks/alfred/alfred_hooks.py +44 -48
  34. moai_adk/templates/.claude/hooks/alfred/core/__init__.py +17 -17
  35. moai_adk/templates/.claude/hooks/alfred/core/checkpoint.py +59 -59
  36. moai_adk/templates/.claude/hooks/alfred/core/context.py +19 -19
  37. moai_adk/templates/.claude/hooks/alfred/core/project.py +52 -52
  38. moai_adk/templates/.claude/hooks/alfred/handlers/__init__.py +1 -1
  39. moai_adk/templates/.claude/hooks/alfred/handlers/notification.py +4 -4
  40. moai_adk/templates/.claude/hooks/alfred/handlers/session.py +27 -27
  41. moai_adk/templates/.claude/hooks/alfred/handlers/tool.py +16 -17
  42. moai_adk/templates/.claude/hooks/alfred/handlers/user.py +11 -11
  43. moai_adk/templates/.claude/output-styles/alfred/agentic-coding.md +308 -307
  44. moai_adk/templates/.claude/output-styles/alfred/moai-adk-learning.md +297 -296
  45. moai_adk/templates/.claude/output-styles/alfred/study-with-alfred.md +191 -190
  46. moai_adk/templates/.claude/skills/moai-alfred-code-reviewer/SKILL.md +112 -0
  47. moai_adk/templates/.claude/skills/moai-alfred-debugger-pro/SKILL.md +103 -0
  48. moai_adk/templates/.claude/skills/moai-alfred-ears-authoring/SKILL.md +103 -0
  49. moai_adk/templates/.claude/skills/moai-alfred-git-workflow/SKILL.md +95 -0
  50. moai_adk/templates/.claude/skills/moai-alfred-language-detection/SKILL.md +99 -0
  51. moai_adk/templates/.claude/skills/moai-alfred-performance-optimizer/SKILL.md +105 -0
  52. moai_adk/templates/.claude/skills/moai-alfred-refactoring-coach/SKILL.md +97 -0
  53. moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-validation/SKILL.md +97 -0
  54. moai_adk/templates/.claude/skills/moai-alfred-tag-scanning/SKILL.md +90 -0
  55. moai_adk/templates/.claude/skills/moai-alfred-trust-validation/SKILL.md +99 -0
  56. moai_adk/templates/.claude/skills/moai-alfred-tui-survey/SKILL.md +87 -0
  57. moai_adk/templates/.claude/skills/moai-alfred-tui-survey/examples.md +62 -0
  58. moai_adk/templates/.claude/skills/moai-claude-code/SKILL.md +70 -43
  59. moai_adk/templates/.claude/skills/moai-claude-code/examples.md +141 -141
  60. moai_adk/templates/.claude/skills/moai-claude-code/reference.md +179 -165
  61. moai_adk/templates/.claude/skills/moai-claude-code/templates/agent-full.md +78 -78
  62. moai_adk/templates/.claude/skills/moai-claude-code/templates/command-full.md +90 -90
  63. moai_adk/templates/.claude/skills/moai-claude-code/templates/plugin-full.json +39 -25
  64. moai_adk/templates/.claude/skills/moai-claude-code/templates/settings-full.json +117 -74
  65. moai_adk/templates/.claude/skills/moai-claude-code/templates/skill-full.md +131 -134
  66. moai_adk/templates/.claude/skills/moai-domain-backend/SKILL.md +43 -12
  67. moai_adk/templates/.claude/skills/moai-domain-cli-tool/SKILL.md +43 -12
  68. moai_adk/templates/.claude/skills/moai-domain-data-science/SKILL.md +43 -12
  69. moai_adk/templates/.claude/skills/moai-domain-database/SKILL.md +43 -12
  70. moai_adk/templates/.claude/skills/moai-domain-devops/SKILL.md +43 -12
  71. moai_adk/templates/.claude/skills/moai-domain-frontend/SKILL.md +43 -12
  72. moai_adk/templates/.claude/skills/moai-domain-ml/SKILL.md +43 -11
  73. moai_adk/templates/.claude/skills/moai-domain-mobile-app/SKILL.md +43 -12
  74. moai_adk/templates/.claude/skills/moai-domain-security/SKILL.md +43 -12
  75. moai_adk/templates/.claude/skills/moai-domain-web-api/SKILL.md +43 -12
  76. moai_adk/templates/.claude/skills/moai-essentials-debug/SKILL.md +47 -11
  77. moai_adk/templates/.claude/skills/moai-essentials-perf/SKILL.md +47 -11
  78. moai_adk/templates/.claude/skills/moai-essentials-refactor/SKILL.md +51 -14
  79. moai_adk/templates/.claude/skills/moai-essentials-review/SKILL.md +46 -10
  80. moai_adk/templates/.claude/skills/moai-foundation-ears/SKILL.md +62 -25
  81. moai_adk/templates/.claude/skills/moai-foundation-git/SKILL.md +44 -17
  82. moai_adk/templates/.claude/skills/moai-foundation-langs/SKILL.md +44 -14
  83. moai_adk/templates/.claude/skills/moai-foundation-specs/SKILL.md +45 -13
  84. moai_adk/templates/.claude/skills/moai-foundation-tags/SKILL.md +46 -14
  85. moai_adk/templates/.claude/skills/moai-foundation-trust/SKILL.md +48 -8
  86. moai_adk/templates/.claude/skills/moai-lang-c/SKILL.md +44 -12
  87. moai_adk/templates/.claude/skills/moai-lang-clojure/SKILL.md +44 -12
  88. moai_adk/templates/.claude/skills/moai-lang-cpp/SKILL.md +44 -11
  89. moai_adk/templates/.claude/skills/moai-lang-csharp/SKILL.md +44 -11
  90. moai_adk/templates/.claude/skills/moai-lang-dart/SKILL.md +44 -12
  91. moai_adk/templates/.claude/skills/moai-lang-elixir/SKILL.md +44 -11
  92. moai_adk/templates/.claude/skills/moai-lang-go/SKILL.md +44 -11
  93. moai_adk/templates/.claude/skills/moai-lang-haskell/SKILL.md +44 -11
  94. moai_adk/templates/.claude/skills/moai-lang-java/SKILL.md +44 -12
  95. moai_adk/templates/.claude/skills/moai-lang-javascript/SKILL.md +44 -12
  96. moai_adk/templates/.claude/skills/moai-lang-julia/SKILL.md +44 -12
  97. moai_adk/templates/.claude/skills/moai-lang-kotlin/SKILL.md +44 -12
  98. moai_adk/templates/.claude/skills/moai-lang-lua/SKILL.md +44 -11
  99. moai_adk/templates/.claude/skills/moai-lang-php/SKILL.md +44 -11
  100. moai_adk/templates/.claude/skills/moai-lang-python/SKILL.md +44 -12
  101. moai_adk/templates/.claude/skills/moai-lang-r/SKILL.md +44 -11
  102. moai_adk/templates/.claude/skills/moai-lang-ruby/SKILL.md +44 -11
  103. moai_adk/templates/.claude/skills/moai-lang-rust/SKILL.md +44 -12
  104. moai_adk/templates/.claude/skills/moai-lang-scala/SKILL.md +44 -12
  105. moai_adk/templates/.claude/skills/moai-lang-shell/SKILL.md +44 -11
  106. moai_adk/templates/.claude/skills/moai-lang-sql/SKILL.md +44 -12
  107. moai_adk/templates/.claude/skills/moai-lang-swift/SKILL.md +44 -12
  108. moai_adk/templates/.claude/skills/moai-lang-typescript/SKILL.md +44 -12
  109. moai_adk/templates/.github/PULL_REQUEST_TEMPLATE.md +44 -43
  110. moai_adk/templates/.github/workflows/moai-gitflow.yml +36 -35
  111. moai_adk/templates/.moai/config.json +9 -6
  112. moai_adk/templates/.moai/memory/development-guide.md +220 -221
  113. moai_adk/templates/.moai/memory/gitflow-protection-policy.md +85 -85
  114. moai_adk/templates/.moai/memory/spec-metadata.md +229 -150
  115. moai_adk/templates/.moai/project/product.md +90 -90
  116. moai_adk/templates/.moai/project/structure.md +85 -85
  117. moai_adk/templates/.moai/project/tech.md +117 -117
  118. moai_adk/templates/CLAUDE.md +564 -709
  119. moai_adk-0.4.4.dist-info/METADATA +369 -0
  120. moai_adk-0.4.4.dist-info/RECORD +152 -0
  121. moai_adk/templates/.claude/commands/alfred/1-spec.md +0 -31
  122. moai_adk/templates/.claude/commands/alfred/2-build.md +0 -30
  123. moai_adk/templates/.claude/skills/scripts/standardize_skills.py +0 -166
  124. moai_adk/templates/.claude/skills/scripts/verify_standardization.sh +0 -43
  125. moai_adk/templates/.moai/hooks/pre-push.sample +0 -88
  126. moai_adk-0.4.0.dist-info/METADATA +0 -1816
  127. moai_adk-0.4.0.dist-info/RECORD +0 -145
  128. {moai_adk-0.4.0.dist-info → moai_adk-0.4.4.dist-info}/WHEEL +0 -0
  129. {moai_adk-0.4.0.dist-info → moai_adk-0.4.4.dist-info}/entry_points.txt +0 -0
  130. {moai_adk-0.4.0.dist-info → moai_adk-0.4.4.dist-info}/licenses/LICENSE +0 -0
@@ -1,8 +1,8 @@
1
1
  # @CODE:INIT-003:PHASE | SPEC: .moai/specs/SPEC-INIT-003/spec.md | TEST: tests/unit/test_init_reinit.py
2
- """Phase-based installation executor (SPEC-INIT-003 v0.3.0)
2
+ """Phase-based installation executor (SPEC-INIT-003 v0.4.2)
3
3
 
4
4
  Runs the project initialization across five phases:
5
- - Phase 1: Preparation (create backup at .moai-backups/{timestamp}/, keep only latest)
5
+ - Phase 1: Preparation (create single backup at .moai-backups/backup/)
6
6
  - Phase 2: Directory (build directory structure)
7
7
  - Phase 3: Resource (copy templates while preserving user content)
8
8
  - Phase 4: Configuration (generate configuration files)
@@ -20,7 +20,6 @@ from rich.console import Console
20
20
 
21
21
  from moai_adk import __version__
22
22
  from moai_adk.core.project.backup_utils import (
23
- generate_backup_dir_name,
24
23
  get_backup_targets,
25
24
  has_any_moai_files,
26
25
  is_protected_path,
@@ -53,6 +52,7 @@ class PhaseExecutor:
53
52
  ".moai/memory/",
54
53
  ".claude/",
55
54
  ".claude/logs/",
55
+ ".github/",
56
56
  ]
57
57
 
58
58
  def __init__(self, validator: ProjectValidator) -> None:
@@ -158,6 +158,7 @@ class PhaseExecutor:
158
158
  return [
159
159
  ".claude/",
160
160
  ".moai/",
161
+ ".github/",
161
162
  "CLAUDE.md",
162
163
  ".gitignore",
163
164
  ]
@@ -219,7 +220,7 @@ class PhaseExecutor:
219
220
  # @CODE:INIT-004:VERIFY-001 | Validate installation results
220
221
  # @CODE:INIT-004:VALIDATION-CHECK | Comprehensive installation validation
221
222
  # Verifies all required files including 4 Alfred command files:
222
- # - 0-project.md, 1-spec.md, 2-build.md, 3-sync.md
223
+ # - 0-project.md, 1-plan.md, 2-run.md, 3-sync.md
223
224
  self.validator.validate_installation(project_path)
224
225
 
225
226
  # Initialize Git for team mode
@@ -227,25 +228,21 @@ class PhaseExecutor:
227
228
  self._initialize_git(project_path)
228
229
 
229
230
  def _create_backup(self, project_path: Path) -> None:
230
- """Create a selective backup (v0.3.0).
231
+ """Create a single backup (v0.4.2).
231
232
 
232
- Keep only the latest backup in .moai-backups/{timestamp}/.
233
+ Maintains only one backup at .moai-backups/backup/.
233
234
 
234
235
  Args:
235
236
  project_path: Project path.
236
237
  """
237
238
  # Define backup directory
238
239
  backups_dir = project_path / ".moai-backups"
240
+ backup_path = backups_dir / "backup"
239
241
 
240
- # Remove all existing backups (keep only latest)
241
- if backups_dir.exists():
242
- for item in backups_dir.iterdir():
243
- if item.is_dir():
244
- shutil.rmtree(item)
242
+ # Remove existing backup if present
243
+ if backup_path.exists():
244
+ shutil.rmtree(backup_path)
245
245
 
246
- # Create new backup directory (.moai-backups/{timestamp}/)
247
- timestamp = generate_backup_dir_name()
248
- backup_path = backups_dir / timestamp
249
246
  backup_path.mkdir(parents=True, exist_ok=True)
250
247
 
251
248
  # Collect backup targets
@@ -37,6 +37,7 @@ class ProjectValidator:
37
37
  ".moai/specs/",
38
38
  ".moai/memory/",
39
39
  ".claude/",
40
+ ".github/",
40
41
  ]
41
42
 
42
43
  # Required files
@@ -48,8 +49,8 @@ class ProjectValidator:
48
49
  # Required Alfred command files (SPEC-INIT-004)
49
50
  REQUIRED_ALFRED_COMMANDS = [
50
51
  "0-project.md",
51
- "1-spec.md",
52
- "2-build.md",
52
+ "1-plan.md",
53
+ "2-run.md",
53
54
  "3-sync.md",
54
55
  ]
55
56
 
@@ -1,5 +1,5 @@
1
1
  # @CODE:TRUST-001 | SPEC: SPEC-TRUST-001/spec.md | TEST: tests/unit/core/quality/
2
- """TRUST 원칙 자동 검증 시스템"""
2
+ """TRUST principle automated validation system"""
3
3
 
4
4
  from moai_adk.core.quality.trust_checker import TrustChecker
5
5
 
@@ -1,13 +1,13 @@
1
1
  # @CODE:TRUST-001 | SPEC: SPEC-TRUST-001/spec.md | TEST: tests/unit/core/quality/test_trust_checker.py
2
2
  """
3
- TRUST 원칙 통합 검증 시스템
4
-
5
- TRUST 5원칙:
6
- - T: Test First (테스트 커버리지 ≥85%)
7
- - R: Readable (파일 ≤300 LOC, 함수 ≤50 LOC, 매개변수 ≤5)
8
- - U: Unified (타입 안전성)
9
- - S: Secured (보안 취약점 스캔)
10
- - T: Trackable (TAG 체인 무결성)
3
+ Integrated TRUST principle validation system
4
+
5
+ TRUST 5 principles:
6
+ - T: Test First (test coverage ≥85%)
7
+ - R: Readable (file ≤300 LOC, function ≤50 LOC, parameters ≤5)
8
+ - U: Unified (type safety)
9
+ - S: Secured (vulnerability scanning)
10
+ - T: Trackable (TAG chain integrity)
11
11
  """
12
12
 
13
13
  import ast
@@ -18,7 +18,7 @@ from typing import Any
18
18
  from moai_adk.core.quality.validators.base_validator import ValidationResult
19
19
 
20
20
  # ========================================
21
- # 상수 정의 (의도를 드러내는 이름)
21
+ # Constants (descriptive names)
22
22
  # ========================================
23
23
  MIN_TEST_COVERAGE_PERCENT = 85
24
24
  MAX_FILE_LINES_OF_CODE = 300
@@ -26,20 +26,20 @@ MAX_FUNCTION_LINES_OF_CODE = 50
26
26
  MAX_FUNCTION_PARAMETERS = 5
27
27
  MAX_CYCLOMATIC_COMPLEXITY = 10
28
28
 
29
- # 파일 인코딩
29
+ # File encoding
30
30
  DEFAULT_FILE_ENCODING = "utf-8"
31
31
 
32
- # TAG 접두사
32
+ # TAG prefixes
33
33
  TAG_PREFIX_SPEC = "@SPEC:"
34
34
  TAG_PREFIX_CODE = "@CODE:"
35
35
  TAG_PREFIX_TEST = "@TEST:"
36
36
 
37
37
 
38
38
  class TrustChecker:
39
- """TRUST 원칙 통합 검증기"""
39
+ """Integrated TRUST principle validator"""
40
40
 
41
41
  def __init__(self):
42
- """TrustChecker 초기화"""
42
+ """Initialize TrustChecker"""
43
43
  self.results: dict[str, ValidationResult] = {}
44
44
 
45
45
  # ========================================
@@ -48,14 +48,14 @@ class TrustChecker:
48
48
 
49
49
  def validate_coverage(self, project_path: Path, coverage_data: dict[str, Any]) -> ValidationResult:
50
50
  """
51
- 테스트 커버리지 검증 (≥85%)
51
+ Validate test coverage (≥85%)
52
52
 
53
53
  Args:
54
- project_path: 프로젝트 경로
55
- coverage_data: 커버리지 데이터 (total_coverage, low_coverage_files)
54
+ project_path: Project path
55
+ coverage_data: Coverage data (total_coverage, low_coverage_files)
56
56
 
57
57
  Returns:
58
- ValidationResult: 검증 결과
58
+ ValidationResult: Validation result
59
59
  """
60
60
  total_coverage = coverage_data.get("total_coverage", 0)
61
61
 
@@ -64,7 +64,7 @@ class TrustChecker:
64
64
  passed=True, message=f"Test coverage: {total_coverage}% (Target: {MIN_TEST_COVERAGE_PERCENT}%)"
65
65
  )
66
66
 
67
- # 실패 상세 정보 생성
67
+ # Generate detailed information on failure
68
68
  low_files = coverage_data.get("low_coverage_files", [])
69
69
  details = f"Current coverage: {total_coverage}% (Target: {MIN_TEST_COVERAGE_PERCENT}%)\n"
70
70
  details += "Low coverage files:\n"
@@ -84,15 +84,15 @@ class TrustChecker:
84
84
 
85
85
  def validate_file_size(self, src_path: Path) -> ValidationResult:
86
86
  """
87
- 파일 크기 검증 (≤300 LOC)
87
+ Validate file size (≤300 LOC)
88
88
 
89
89
  Args:
90
- src_path: 소스 코드 디렉토리 경로
90
+ src_path: Source code directory path
91
91
 
92
92
  Returns:
93
- ValidationResult: 검증 결과
93
+ ValidationResult: Validation result
94
94
  """
95
- # 입력 검증 (보안 강화)
95
+ # Input validation (security)
96
96
  if not src_path.exists():
97
97
  return ValidationResult(passed=False, message=f"Source path does not exist: {src_path}", details="")
98
98
 
@@ -102,7 +102,7 @@ class TrustChecker:
102
102
  violations = []
103
103
 
104
104
  for py_file in src_path.rglob("*.py"):
105
- # 가드절 적용 (가독성 향상)
105
+ # Apply guard clause (improves readability)
106
106
  if py_file.name.startswith("test_"):
107
107
  continue
108
108
 
@@ -113,7 +113,7 @@ class TrustChecker:
113
113
  if loc > MAX_FILE_LINES_OF_CODE:
114
114
  violations.append(f"{py_file.name}: {loc} LOC (Limit: {MAX_FILE_LINES_OF_CODE})")
115
115
  except (UnicodeDecodeError, PermissionError):
116
- # 보안: 파일 접근 오류 처리
116
+ # Security: handle file access errors
117
117
  continue
118
118
 
119
119
  if not violations:
@@ -126,13 +126,13 @@ class TrustChecker:
126
126
 
127
127
  def validate_function_size(self, src_path: Path) -> ValidationResult:
128
128
  """
129
- 함수 크기 검증 (≤50 LOC)
129
+ Validate function size (≤50 LOC)
130
130
 
131
131
  Args:
132
- src_path: 소스 코드 디렉토리 경로
132
+ src_path: Source code directory path
133
133
 
134
134
  Returns:
135
- ValidationResult: 검증 결과
135
+ ValidationResult: Validation result
136
136
  """
137
137
  violations = []
138
138
 
@@ -147,11 +147,11 @@ class TrustChecker:
147
147
 
148
148
  for node in ast.walk(tree):
149
149
  if isinstance(node, ast.FunctionDef):
150
- # AST 라인 번호는 1-based
150
+ # AST line numbers are 1-based
151
151
  start_line = node.lineno
152
152
  end_line = node.end_lineno if node.end_lineno else start_line # type: ignore
153
153
 
154
- # 실제 함수 라인 계산 (데코레이터 제외)
154
+ # Compute actual function lines of code (decorators excluded)
155
155
  func_lines = lines[start_line - 1:end_line]
156
156
  func_loc = len(func_lines)
157
157
 
@@ -172,13 +172,13 @@ class TrustChecker:
172
172
 
173
173
  def validate_param_count(self, src_path: Path) -> ValidationResult:
174
174
  """
175
- 매개변수 개수 검증 (≤5)
175
+ Validate parameter count (≤5)
176
176
 
177
177
  Args:
178
- src_path: 소스 코드 디렉토리 경로
178
+ src_path: Source code directory path
179
179
 
180
180
  Returns:
181
- ValidationResult: 검증 결과
181
+ ValidationResult: Validation result
182
182
  """
183
183
  violations = []
184
184
 
@@ -211,13 +211,13 @@ class TrustChecker:
211
211
 
212
212
  def validate_complexity(self, src_path: Path) -> ValidationResult:
213
213
  """
214
- 순환 복잡도 검증 (≤10)
214
+ Validate cyclomatic complexity (≤10)
215
215
 
216
216
  Args:
217
- src_path: 소스 코드 디렉토리 경로
217
+ src_path: Source code directory path
218
218
 
219
219
  Returns:
220
- ValidationResult: 검증 결과
220
+ ValidationResult: Validation result
221
221
  """
222
222
  violations = []
223
223
 
@@ -250,23 +250,23 @@ class TrustChecker:
250
250
 
251
251
  def _calculate_complexity(self, node: ast.FunctionDef) -> int:
252
252
  """
253
- 순환 복잡도 계산 (McCabe complexity)
253
+ Calculate cyclomatic complexity (McCabe complexity)
254
254
 
255
255
  Args:
256
- node: 함수 AST 노드
256
+ node: Function AST node
257
257
 
258
258
  Returns:
259
- int: 순환 복잡도
259
+ int: Cyclomatic complexity
260
260
  """
261
261
  complexity = 1
262
262
  for child in ast.walk(node):
263
- # 분기문마다 +1
263
+ # Add 1 for each branching statement
264
264
  if isinstance(child, (ast.If, ast.While, ast.For, ast.ExceptHandler, ast.With)):
265
265
  complexity += 1
266
- # and/or 연산자마다 +1
266
+ # Add 1 for each and/or operator
267
267
  elif isinstance(child, ast.BoolOp):
268
268
  complexity += len(child.values) - 1
269
- # elif 이미 ast.If 카운트되므로 별도 처리 불필요
269
+ # elif is already counted as ast.If, no extra handling needed
270
270
  return complexity
271
271
 
272
272
  # ========================================
@@ -275,22 +275,22 @@ class TrustChecker:
275
275
 
276
276
  def validate_tag_chain(self, project_path: Path) -> ValidationResult:
277
277
  """
278
- TAG 체인 완전성 검증
278
+ Validate TAG chain completeness
279
279
 
280
280
  Args:
281
- project_path: 프로젝트 경로
281
+ project_path: Project path
282
282
 
283
283
  Returns:
284
- ValidationResult: 검증 결과
284
+ ValidationResult: Validation result
285
285
  """
286
286
  specs_dir = project_path / ".moai" / "specs"
287
287
  src_dir = project_path / "src"
288
288
 
289
- # TAG 스캔
289
+ # Scan for TAGs
290
290
  spec_tags = self._scan_tags(specs_dir, "@SPEC:")
291
291
  code_tags = self._scan_tags(src_dir, "@CODE:")
292
292
 
293
- # 체인 검증
293
+ # Validate the chain
294
294
  broken_chains = []
295
295
  for code_tag in code_tags:
296
296
  tag_id = code_tag.split(":")[-1]
@@ -307,13 +307,13 @@ class TrustChecker:
307
307
 
308
308
  def detect_orphan_tags(self, project_path: Path) -> list[str]:
309
309
  """
310
- 고아 TAG 탐지
310
+ Detect orphan TAGs
311
311
 
312
312
  Args:
313
- project_path: 프로젝트 경로
313
+ project_path: Project path
314
314
 
315
315
  Returns:
316
- list[str]: 고아 TAG 목록
316
+ list[str]: List of orphan TAGs
317
317
  """
318
318
  specs_dir = project_path / ".moai" / "specs"
319
319
  src_dir = project_path / "src"
@@ -331,14 +331,14 @@ class TrustChecker:
331
331
 
332
332
  def _scan_tags(self, directory: Path, tag_prefix: str) -> list[str]:
333
333
  """
334
- 디렉토리에서 TAG 스캔
334
+ Scan for TAGs in a directory
335
335
 
336
336
  Args:
337
- directory: 스캔할 디렉토리
338
- tag_prefix: TAG 접두사 (예: "@SPEC:", "@CODE:")
337
+ directory: Directory to scan
338
+ tag_prefix: TAG prefix (for example, "@SPEC:", "@CODE:")
339
339
 
340
340
  Returns:
341
- list[str]: 발견된 TAG 목록
341
+ list[str]: List of discovered TAGs
342
342
  """
343
343
  if not directory.exists():
344
344
  return []
@@ -362,25 +362,25 @@ class TrustChecker:
362
362
 
363
363
  def generate_report(self, results: dict[str, Any], format: str = "markdown") -> str:
364
364
  """
365
- 검증 결과 보고서 생성
365
+ Generate validation report
366
366
 
367
367
  Args:
368
- results: 검증 결과 딕셔너리
369
- format: 보고서 형식 ("markdown" 또는 "json")
368
+ results: Validation result dictionary
369
+ format: Report format ("markdown" or "json")
370
370
 
371
371
  Returns:
372
- str: 보고서 문자열
372
+ str: Report string
373
373
  """
374
374
  if format == "json":
375
375
  return json.dumps(results, indent=2)
376
376
 
377
- # Markdown 형식
377
+ # Markdown format
378
378
  report = "# TRUST Validation Report\n\n"
379
379
 
380
380
  for category, result in results.items():
381
381
  status = "✅ PASS" if result.get("passed", False) else "❌ FAIL"
382
382
  value = result.get('value', 'N/A')
383
- # 숫자인 경우 % 기호 추가
383
+ # Add % suffix when the value is numeric
384
384
  if isinstance(value, (int, float)):
385
385
  value_str = f"{value}%"
386
386
  else:
@@ -398,13 +398,13 @@ class TrustChecker:
398
398
 
399
399
  def select_tools(self, project_path: Path) -> dict[str, str]:
400
400
  """
401
- 언어별 도구 자동 선택
401
+ Automatically select tools by language
402
402
 
403
403
  Args:
404
- project_path: 프로젝트 경로
404
+ project_path: Project path
405
405
 
406
406
  Returns:
407
- dict[str, str]: 선택된 도구 딕셔너리
407
+ dict[str, str]: Selected tool dictionary
408
408
  """
409
409
  config_path = project_path / ".moai" / "config.json"
410
410
  if not config_path.exists():
@@ -432,7 +432,7 @@ class TrustChecker:
432
432
  "type_checker": "tsc",
433
433
  }
434
434
 
435
- # 기본값 (Python)
435
+ # Default (Python)
436
436
  return {
437
437
  "test_framework": "pytest",
438
438
  "coverage_tool": "coverage.py",
@@ -1,5 +1,5 @@
1
1
  # @CODE:TRUST-001 | SPEC: SPEC-TRUST-001/spec.md
2
- """TRUST 검증기 패키지"""
2
+ """TRUST validator package"""
3
3
 
4
4
  from moai_adk.core.quality.validators.base_validator import ValidationResult
5
5
 
@@ -7,7 +7,7 @@ from typing import Any
7
7
 
8
8
  @dataclass
9
9
  class ValidationResult:
10
- """검증 결과 데이터 클래스"""
10
+ """Validation result data class"""
11
11
 
12
12
  passed: bool
13
13
  message: str
@@ -7,7 +7,6 @@ Creates and manages backups to protect user data during template updates.
7
7
  from __future__ import annotations
8
8
 
9
9
  import shutil
10
- from datetime import datetime
11
10
  from pathlib import Path
12
11
 
13
12
 
@@ -27,7 +26,15 @@ class TemplateBackup:
27
26
  target_path: Project path (absolute).
28
27
  """
29
28
  self.target_path = target_path.resolve()
30
- self.backup_dir = self.target_path / ".moai-backups"
29
+
30
+ @property
31
+ def backup_dir(self) -> Path:
32
+ """Get the backup directory path.
33
+
34
+ Returns:
35
+ Path to .moai-backups directory.
36
+ """
37
+ return self.target_path / ".moai-backups"
31
38
 
32
39
  def has_existing_files(self) -> bool:
33
40
  """Check whether backup-worthy files already exist.
@@ -37,21 +44,27 @@ class TemplateBackup:
37
44
  """
38
45
  return any(
39
46
  (self.target_path / item).exists()
40
- for item in [".moai", ".claude", "CLAUDE.md"]
47
+ for item in [".moai", ".claude", ".github", "CLAUDE.md"]
41
48
  )
42
49
 
43
50
  def create_backup(self) -> Path:
44
- """Create a timestamped backup.
51
+ """Create a single backup (always at .moai-backups/backup/).
52
+
53
+ Existing backups are overwritten to maintain only one backup copy.
45
54
 
46
55
  Returns:
47
- Backup path (for example, .moai-backups/20250110-143025/).
56
+ Backup path (always .moai-backups/backup/).
48
57
  """
49
- timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
50
- backup_path = self.target_path / ".moai-backups" / timestamp
58
+ backup_path = self.target_path / ".moai-backups" / "backup"
59
+
60
+ # Remove existing backup if present
61
+ if backup_path.exists():
62
+ shutil.rmtree(backup_path)
63
+
51
64
  backup_path.mkdir(parents=True, exist_ok=True)
52
65
 
53
66
  # Copy backup targets
54
- for item in [".moai", ".claude", "CLAUDE.md"]:
67
+ for item in [".moai", ".claude", ".github", "CLAUDE.md"]:
55
68
  src = self.target_path / item
56
69
  if not src.exists():
57
70
  continue
@@ -15,6 +15,8 @@ from typing import Any
15
15
  class TemplateMerger:
16
16
  """Encapsulate template merging logic."""
17
17
 
18
+ PROJECT_INFO_HEADERS = ("## Project Information", "## 프로젝트 정보")
19
+
18
20
  def __init__(self, target_path: Path) -> None:
19
21
  """Initialize the merger.
20
22
 
@@ -28,15 +30,15 @@ class TemplateMerger:
28
30
 
29
31
  Rules:
30
32
  - Use the latest template structure/content.
31
- - Preserve the existing "## 프로젝트 정보" section.
33
+ - Preserve the existing "## Project Information" section.
32
34
 
33
35
  Args:
34
36
  template_path: Template CLAUDE.md.
35
37
  existing_path: Existing CLAUDE.md.
36
38
  """
37
- # Extract the existing "## 프로젝트 정보" section
39
+ # Extract the existing project information section
38
40
  existing_content = existing_path.read_text(encoding="utf-8")
39
- project_info_start = existing_content.find("## 프로젝트 정보")
41
+ project_info_start, _ = self._find_project_info_section(existing_content)
40
42
  project_info = ""
41
43
  if project_info_start != -1:
42
44
  # Extract until EOF
@@ -48,7 +50,7 @@ class TemplateMerger:
48
50
  # Merge when project info exists
49
51
  if project_info:
50
52
  # Remove the project info section from the template
51
- template_project_start = template_content.find("## 프로젝트 정보")
53
+ template_project_start, _ = self._find_project_info_section(template_content)
52
54
  if template_project_start != -1:
53
55
  template_content = template_content[:template_project_start].rstrip()
54
56
 
@@ -59,6 +61,14 @@ class TemplateMerger:
59
61
  # No project info; copy the template as-is
60
62
  shutil.copy2(template_path, existing_path)
61
63
 
64
+ def _find_project_info_section(self, content: str) -> tuple[int, str | None]:
65
+ """Find the project information header in the given content."""
66
+ for header in self.PROJECT_INFO_HEADERS:
67
+ index = content.find(header)
68
+ if index != -1:
69
+ return index, header
70
+ return -1, None
71
+
62
72
  def merge_gitignore(self, template_path: Path, existing_path: Path) -> None:
63
73
  """.gitignore merge.
64
74
 
@@ -178,6 +178,7 @@ class TemplateProcessor:
178
178
 
179
179
  self._copy_claude(silent)
180
180
  self._copy_moai(silent)
181
+ self._copy_github(silent)
181
182
  self._copy_claude_md(silent)
182
183
  self._copy_gitignore(silent)
183
184
 
@@ -237,7 +238,7 @@ class TemplateProcessor:
237
238
  Strategy:
238
239
  - Alfred folders (commands/agents/hooks/output-styles/alfred) → copy wholesale (delete & overwrite)
239
240
  * Creates individual backup before deletion for safety
240
- * Commands: 0-project.md, 1-spec.md, 2-build.md, 3-sync.md
241
+ * Commands: 0-project.md, 1-plan.md, 2-run.md, 3-sync.md
241
242
  - Other files/folders → copy individually (preserve existing)
242
243
  """
243
244
  src = self.template_root / ".claude"
@@ -256,7 +257,7 @@ class TemplateProcessor:
256
257
  # Alfred folders to copy wholesale (overwrite)
257
258
  alfred_folders = [
258
259
  "hooks/alfred",
259
- "commands/alfred", # Contains 0-project.md, 1-spec.md, 2-build.md, 3-sync.md
260
+ "commands/alfred", # Contains 0-project.md, 1-plan.md, 2-run.md, 3-sync.md
260
261
  "output-styles/alfred",
261
262
  "agents/alfred",
262
263
  ]
@@ -356,8 +357,26 @@ class TemplateProcessor:
356
357
  if not silent:
357
358
  console.print(" ✅ .moai/ copy complete (variables substituted)")
358
359
 
360
+ def _copy_github(self, silent: bool = False) -> None:
361
+ """.github/ directory copy with variable substitution."""
362
+ src = self.template_root / ".github"
363
+ dst = self.target_path / ".github"
364
+
365
+ if not src.exists():
366
+ if not silent:
367
+ console.print("⚠️ .github/ template not found")
368
+ return
369
+
370
+ if dst.exists():
371
+ shutil.rmtree(dst)
372
+
373
+ self._copy_dir_with_substitution(src, dst)
374
+
375
+ if not silent:
376
+ console.print(" ✅ .github/ copy complete (variables substituted)")
377
+
359
378
  def _copy_claude_md(self, silent: bool = False) -> None:
360
- """Copy CLAUDE.md with smart merge (preserves "## 프로젝트 정보" section)."""
379
+ """Copy CLAUDE.md with smart merge (preserves \"## Project Information\" section)."""
361
380
  src = self.template_root / "CLAUDE.md"
362
381
  dst = self.target_path / "CLAUDE.md"
363
382
 
@@ -366,11 +385,11 @@ class TemplateProcessor:
366
385
  console.print("⚠️ CLAUDE.md template not found")
367
386
  return
368
387
 
369
- # Smart merge: preserve existing "## 프로젝트 정보" section
388
+ # Smart merge: preserve existing "## Project Information" section
370
389
  if dst.exists():
371
390
  self._merge_claude_md(src, dst)
372
391
  if not silent:
373
- console.print(" 🔄 CLAUDE.md merged (프로젝트 정보 preserved)")
392
+ console.print(" 🔄 CLAUDE.md merged (project information preserved)")
374
393
  else:
375
394
  # First time: just copy
376
395
  self._copy_file_with_substitution(src, dst)