moai-adk 0.8.0__py3-none-any.whl → 0.15.0__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 (207) hide show
  1. moai_adk/cli/commands/init.py +14 -2
  2. moai_adk/cli/commands/update.py +229 -60
  3. moai_adk/core/config/migration.py +1 -1
  4. moai_adk/core/issue_creator.py +313 -0
  5. moai_adk/core/project/detector.py +201 -12
  6. moai_adk/core/project/initializer.py +62 -1
  7. moai_adk/core/project/phase_executor.py +48 -6
  8. moai_adk/core/tags/__init__.py +86 -0
  9. moai_adk/core/tags/ci_validator.py +463 -0
  10. moai_adk/core/tags/cli.py +283 -0
  11. moai_adk/core/tags/generator.py +109 -0
  12. moai_adk/core/tags/inserter.py +99 -0
  13. moai_adk/core/tags/mapper.py +126 -0
  14. moai_adk/core/tags/parser.py +76 -0
  15. moai_adk/core/tags/pre_commit_validator.py +393 -0
  16. moai_adk/core/tags/reporter.py +956 -0
  17. moai_adk/core/tags/tags.py +149 -0
  18. moai_adk/core/tags/validator.py +897 -0
  19. moai_adk/core/template_engine.py +268 -0
  20. moai_adk/templates/.claude/agents/alfred/backend-expert.md +319 -0
  21. moai_adk/templates/.claude/agents/alfred/cc-manager.md +25 -2
  22. moai_adk/templates/.claude/agents/alfred/debug-helper.md +24 -12
  23. moai_adk/templates/.claude/agents/alfred/devops-expert.md +464 -0
  24. moai_adk/templates/.claude/agents/alfred/doc-syncer.md +20 -13
  25. moai_adk/templates/.claude/agents/alfred/frontend-expert.md +357 -0
  26. moai_adk/templates/.claude/agents/alfred/git-manager.md +47 -16
  27. moai_adk/templates/.claude/agents/alfred/implementation-planner.md +95 -15
  28. moai_adk/templates/.claude/agents/alfred/project-manager.md +78 -12
  29. moai_adk/templates/.claude/agents/alfred/quality-gate.md +28 -5
  30. moai_adk/templates/.claude/agents/alfred/skill-factory.md +30 -2
  31. moai_adk/templates/.claude/agents/alfred/spec-builder.md +133 -13
  32. moai_adk/templates/.claude/agents/alfred/tag-agent.md +104 -8
  33. moai_adk/templates/.claude/agents/alfred/tdd-implementer.md +133 -16
  34. moai_adk/templates/.claude/agents/alfred/trust-checker.md +27 -4
  35. moai_adk/templates/.claude/agents/alfred/ui-ux-expert.md +571 -0
  36. moai_adk/templates/.claude/commands/alfred/0-project.md +466 -125
  37. moai_adk/templates/.claude/commands/alfred/1-plan.md +208 -71
  38. moai_adk/templates/.claude/commands/alfred/2-run.md +276 -55
  39. moai_adk/templates/.claude/commands/alfred/3-sync.md +439 -53
  40. moai_adk/templates/.claude/commands/alfred/9-feedback.md +149 -0
  41. moai_adk/templates/.claude/hooks/alfred/core/project.py +361 -29
  42. moai_adk/templates/.claude/hooks/alfred/core/timeout.py +136 -0
  43. moai_adk/templates/.claude/hooks/alfred/core/ttl_cache.py +108 -0
  44. moai_adk/templates/.claude/hooks/alfred/core/version_cache.py +198 -0
  45. moai_adk/templates/.claude/hooks/alfred/handlers/__init__.py +14 -6
  46. moai_adk/templates/.claude/hooks/alfred/post_tool__log_changes.py +94 -0
  47. moai_adk/templates/.claude/hooks/alfred/pre_tool__auto_checkpoint.py +100 -0
  48. moai_adk/templates/.claude/hooks/alfred/session_end__cleanup.py +94 -0
  49. moai_adk/templates/.claude/hooks/alfred/session_start__show_project_info.py +94 -0
  50. moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/__init__.py +2 -2
  51. moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/checkpoint.py +3 -3
  52. moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/context.py +5 -5
  53. moai_adk/templates/.claude/hooks/alfred/shared/core/project.py +749 -0
  54. moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/tags.py +55 -23
  55. moai_adk/templates/.claude/hooks/alfred/shared/core/version_cache.py +198 -0
  56. moai_adk/templates/.claude/hooks/alfred/shared/handlers/__init__.py +21 -0
  57. moai_adk/templates/.claude/hooks/alfred/shared/handlers/notification.py +154 -0
  58. moai_adk/templates/.claude/hooks/alfred/{handlers → shared/handlers}/session.py +28 -15
  59. moai_adk/templates/.claude/hooks/alfred/{handlers → shared/handlers}/tool.py +3 -6
  60. moai_adk/templates/.claude/hooks/alfred/{handlers → shared/handlers}/user.py +19 -0
  61. moai_adk/templates/.claude/hooks/alfred/user_prompt__jit_load_docs.py +112 -0
  62. moai_adk/templates/.claude/hooks/alfred/utils/__init__.py +1 -0
  63. moai_adk/templates/.claude/hooks/alfred/utils/timeout.py +161 -0
  64. moai_adk/templates/.claude/settings.json +5 -5
  65. moai_adk/templates/.claude/skills/moai-alfred-agent-guide/SKILL.md +70 -0
  66. moai_adk/templates/.claude/skills/moai-alfred-agent-guide/examples.md +62 -0
  67. moai_adk/templates/{.moai/memory/CLAUDE-AGENTS-GUIDE.md → .claude/skills/moai-alfred-agent-guide/reference.md} +34 -0
  68. moai_adk/templates/.claude/skills/moai-alfred-config-schema/SKILL.md +56 -0
  69. moai_adk/templates/.claude/skills/moai-alfred-config-schema/examples.md +28 -0
  70. moai_adk/templates/.claude/skills/moai-alfred-context-budget/SKILL.md +62 -0
  71. moai_adk/templates/.claude/skills/moai-alfred-context-budget/examples.md +28 -0
  72. moai_adk/templates/.claude/skills/moai-alfred-context-budget/reference.md +405 -0
  73. moai_adk/templates/.claude/skills/moai-alfred-dev-guide/SKILL.md +51 -0
  74. moai_adk/templates/.claude/skills/moai-alfred-dev-guide/examples.md +355 -0
  75. moai_adk/templates/.claude/skills/moai-alfred-dev-guide/reference.md +239 -0
  76. moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/SKILL.md +323 -0
  77. moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/examples.md +286 -0
  78. moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/reference.md +126 -0
  79. moai_adk/templates/.claude/skills/moai-alfred-gitflow-policy/SKILL.md +74 -0
  80. moai_adk/templates/.claude/skills/moai-alfred-gitflow-policy/examples.md +4 -0
  81. moai_adk/templates/.claude/skills/moai-alfred-gitflow-policy/reference.md +269 -0
  82. moai_adk/templates/.claude/skills/moai-alfred-issue-labels/SKILL.md +19 -0
  83. moai_adk/templates/.claude/skills/moai-alfred-issue-labels/examples.md +4 -0
  84. moai_adk/templates/.claude/skills/moai-alfred-issue-labels/reference.md +150 -0
  85. moai_adk/templates/.claude/skills/moai-alfred-persona-roles/SKILL.md +198 -0
  86. moai_adk/templates/.claude/skills/moai-alfred-persona-roles/examples.md +431 -0
  87. moai_adk/templates/.claude/skills/moai-alfred-persona-roles/reference.md +141 -0
  88. moai_adk/templates/.claude/skills/moai-alfred-practices/SKILL.md +89 -0
  89. moai_adk/templates/.claude/skills/moai-alfred-practices/examples.md +122 -0
  90. moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/SKILL.md +508 -0
  91. moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/examples.md +481 -0
  92. moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/reference.md +100 -0
  93. moai_adk/templates/.claude/skills/moai-alfred-reporting/SKILL.md +273 -0
  94. moai_adk/templates/.claude/skills/moai-alfred-rules/SKILL.md +77 -0
  95. moai_adk/templates/.claude/skills/moai-alfred-rules/examples.md +265 -0
  96. moai_adk/templates/.claude/skills/moai-alfred-session-state/SKILL.md +19 -0
  97. moai_adk/templates/.claude/skills/moai-alfred-session-state/examples.md +4 -0
  98. moai_adk/templates/.claude/skills/moai-alfred-session-state/reference.md +84 -0
  99. moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/README.md +137 -0
  100. moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/SKILL.md +219 -0
  101. moai_adk/templates/.claude/skills/{moai-spec-authoring → moai-alfred-spec-authoring}/examples/validate-spec.sh +3 -3
  102. moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/examples.md +541 -0
  103. moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/reference.md +622 -0
  104. moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-extended/SKILL.md +115 -0
  105. moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-extended/examples.md +4 -0
  106. moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-extended/reference.md +348 -0
  107. moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/SKILL.md +19 -0
  108. moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/examples.md +4 -0
  109. moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/reference.md +211 -0
  110. moai_adk/templates/.claude/skills/moai-alfred-workflow/SKILL.md +288 -0
  111. moai_adk/templates/.claude/skills/moai-cc-skill-descriptions/SKILL.md +19 -0
  112. moai_adk/templates/.claude/skills/moai-cc-skill-descriptions/examples.md +4 -0
  113. moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/SKILL.md +3 -3
  114. moai_adk/templates/.claude/skills/moai-design-systems/SKILL.md +802 -0
  115. moai_adk/templates/.claude/skills/moai-design-systems/examples.md +1238 -0
  116. moai_adk/templates/.claude/skills/moai-design-systems/reference.md +673 -0
  117. moai_adk/templates/.claude/skills/moai-domain-frontend/SKILL.md +17 -13
  118. moai_adk/templates/.claude/skills/moai-foundation-ears/SKILL.md +9 -6
  119. moai_adk/templates/.claude/skills/moai-lang-go/SKILL.md +15 -12
  120. moai_adk/templates/.claude/skills/moai-lang-java/SKILL.md +14 -12
  121. moai_adk/templates/.claude/skills/moai-lang-php/SKILL.md +14 -11
  122. moai_adk/templates/.claude/skills/moai-lang-python/SKILL.md +10 -8
  123. moai_adk/templates/.claude/skills/moai-lang-rust/SKILL.md +15 -12
  124. moai_adk/templates/.claude/skills/moai-lang-scala/SKILL.md +13 -11
  125. moai_adk/templates/.claude/skills/moai-lang-typescript/SKILL.md +16 -10
  126. moai_adk/templates/.claude/skills/moai-project-documentation.md +622 -0
  127. moai_adk/templates/.git-hooks/pre-push +143 -0
  128. moai_adk/templates/.github/workflows/c-tag-validation.yml +11 -0
  129. moai_adk/templates/.github/workflows/cpp-tag-validation.yml +11 -0
  130. moai_adk/templates/.github/workflows/csharp-tag-validation.yml +11 -0
  131. moai_adk/templates/.github/workflows/dart-tag-validation.yml +11 -0
  132. moai_adk/templates/.github/workflows/go-tag-validation.yml +130 -0
  133. moai_adk/templates/.github/workflows/java-tag-validation.yml +11 -0
  134. moai_adk/templates/.github/workflows/javascript-tag-validation.yml +135 -0
  135. moai_adk/templates/.github/workflows/kotlin-tag-validation.yml +11 -0
  136. moai_adk/templates/.github/workflows/moai-gitflow.yml +166 -3
  137. moai_adk/templates/.github/workflows/moai-release-create.yml +100 -0
  138. moai_adk/templates/.github/workflows/moai-release-pipeline.yml +188 -0
  139. moai_adk/templates/.github/workflows/php-tag-validation.yml +11 -0
  140. moai_adk/templates/.github/workflows/python-tag-validation.yml +118 -0
  141. moai_adk/templates/.github/workflows/release.yml +118 -0
  142. moai_adk/templates/.github/workflows/ruby-tag-validation.yml +11 -0
  143. moai_adk/templates/.github/workflows/rust-tag-validation.yml +11 -0
  144. moai_adk/templates/.github/workflows/shell-tag-validation.yml +11 -0
  145. moai_adk/templates/.github/workflows/spec-issue-sync.yml +206 -35
  146. moai_adk/templates/.github/workflows/swift-tag-validation.yml +11 -0
  147. moai_adk/templates/.github/workflows/tag-report.yml +269 -0
  148. moai_adk/templates/.github/workflows/tag-validation.yml +186 -0
  149. moai_adk/templates/.github/workflows/typescript-tag-validation.yml +154 -0
  150. moai_adk/templates/.moai/config.json +21 -2
  151. moai_adk/templates/CLAUDE.md +972 -78
  152. moai_adk/templates/workflows/go-tag-validation.yml +30 -0
  153. moai_adk/templates/workflows/javascript-tag-validation.yml +41 -0
  154. moai_adk/templates/workflows/python-tag-validation.yml +42 -0
  155. moai_adk/templates/workflows/typescript-tag-validation.yml +31 -0
  156. moai_adk/utils/banner.py +5 -5
  157. {moai_adk-0.8.0.dist-info → moai_adk-0.15.0.dist-info}/METADATA +1518 -161
  158. {moai_adk-0.8.0.dist-info → moai_adk-0.15.0.dist-info}/RECORD +183 -100
  159. moai_adk/templates/.claude/hooks/alfred/HOOK_SCHEMA_VALIDATION.md +0 -313
  160. moai_adk/templates/.claude/hooks/alfred/README.md +0 -230
  161. moai_adk/templates/.claude/hooks/alfred/alfred_hooks.py +0 -174
  162. moai_adk/templates/.claude/hooks/alfred/handlers/notification.py +0 -25
  163. moai_adk/templates/.claude/hooks/alfred/test_hook_output.py +0 -175
  164. moai_adk/templates/.claude/output-styles/alfred/agentic-coding.md +0 -640
  165. moai_adk/templates/.claude/output-styles/alfred/moai-adk-learning.md +0 -696
  166. moai_adk/templates/.claude/output-styles/alfred/study-with-alfred.md +0 -474
  167. moai_adk/templates/.claude/skills/moai-spec-authoring/README.md +0 -137
  168. moai_adk/templates/.claude/skills/moai-spec-authoring/SKILL.md +0 -218
  169. moai_adk/templates/.claude/skills/moai-spec-authoring/examples.md +0 -541
  170. moai_adk/templates/.claude/skills/moai-spec-authoring/reference.md +0 -622
  171. moai_adk/templates/.github/ISSUE_TEMPLATE/spec.yml +0 -176
  172. moai_adk/templates/.github/PULL_REQUEST_TEMPLATE.md +0 -69
  173. moai_adk/templates/.moai/memory/DEVELOPMENT-GUIDE.md +0 -344
  174. moai_adk/templates/.moai/memory/GITFLOW-PROTECTION-POLICY.md +0 -220
  175. moai_adk/templates/.moai/memory/SPEC-METADATA.md +0 -356
  176. moai_adk/templates/.moai/memory/config-schema.md +0 -444
  177. moai_adk/templates/.moai/memory/gitflow-protection-policy.md +0 -220
  178. moai_adk/templates/.moai/memory/spec-metadata.md +0 -356
  179. moai_adk/templates/.moai/project/product.md +0 -161
  180. moai_adk/templates/.moai/project/structure.md +0 -156
  181. moai_adk/templates/.moai/project/tech.md +0 -227
  182. moai_adk/templates/__init__.py +0 -2
  183. /moai_adk/templates/{.moai/memory/CONFIG-SCHEMA.md → .claude/skills/moai-alfred-config-schema/reference.md} +0 -0
  184. /moai_adk/templates/{.moai/memory/CLAUDE-PRACTICES.md → .claude/skills/moai-alfred-practices/reference.md} +0 -0
  185. /moai_adk/templates/{.moai/memory/CLAUDE-RULES.md → .claude/skills/moai-alfred-rules/reference.md} +0 -0
  186. /moai_adk/templates/{.moai/memory/SKILLS-DESCRIPTION-POLICY.md → .claude/skills/moai-cc-skill-descriptions/reference.md} +0 -0
  187. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/CHECKLIST.md +0 -0
  188. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/EXAMPLES.md +0 -0
  189. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/INTERACTIVE-DISCOVERY.md +0 -0
  190. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/METADATA.md +0 -0
  191. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/PARALLEL-ANALYSIS-REPORT.md +0 -0
  192. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/PYTHON-VERSION-MATRIX.md +0 -0
  193. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/SKILL-FACTORY-WORKFLOW.md +0 -0
  194. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/SKILL-UPDATE-ADVISOR.md +0 -0
  195. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/STEP-BY-STEP-GUIDE.md +0 -0
  196. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/STRUCTURE.md +0 -0
  197. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/WEB-RESEARCH.md +0 -0
  198. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/reference.md +0 -0
  199. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/scripts/generate-structure.sh +0 -0
  200. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/scripts/validate-skill.sh +0 -0
  201. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/templates/SKILL_TEMPLATE.md +0 -0
  202. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/templates/examples-template.md +0 -0
  203. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/templates/reference-template.md +0 -0
  204. /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/templates/scripts-template.sh +0 -0
  205. {moai_adk-0.8.0.dist-info → moai_adk-0.15.0.dist-info}/WHEEL +0 -0
  206. {moai_adk-0.8.0.dist-info → moai_adk-0.15.0.dist-info}/entry_points.txt +0 -0
  207. {moai_adk-0.8.0.dist-info → moai_adk-0.15.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,4 +1,5 @@
1
1
  # @CODE:INIT-003:PHASE | SPEC: .moai/specs/SPEC-INIT-003/spec.md | TEST: tests/unit/test_init_reinit.py
2
+ # @CODE:TEST-COVERAGE-001 | SPEC: SPEC-TEST-COVERAGE-001.md | TEST: tests/unit/test_phase_executor.py
2
3
  """Phase-based installation executor (SPEC-INIT-003 v0.4.2)
3
4
 
4
5
  Runs the project initialization across five phases:
@@ -7,6 +8,8 @@ Runs the project initialization across five phases:
7
8
  - Phase 3: Resource (copy templates while preserving user content)
8
9
  - Phase 4: Configuration (generate configuration files)
9
10
  - Phase 5: Validation (verify and finalize)
11
+
12
+ Test coverage includes 5-phase integration tests with backup, configuration, and validation
10
13
  """
11
14
 
12
15
  import json
@@ -142,7 +145,7 @@ class PhaseExecutor:
142
145
  # Set template variable context (if provided)
143
146
  if config:
144
147
  # @TAG:LANG-FIX-001:PY-CONFIG | Read language from nested config structure
145
- language_config = config.get("language", {})
148
+ language_config: dict[str, Any] = config.get("language", {})
146
149
  if not isinstance(language_config, dict):
147
150
  language_config = {}
148
151
 
@@ -193,6 +196,43 @@ class PhaseExecutor:
193
196
  "Phase 4: Generating configurations...", progress_callback
194
197
  )
195
198
 
199
+ # Read existing config to preserve user settings (Issue #165)
200
+ config_path = project_path / ".moai" / "config.json"
201
+ existing_config: dict[str, Any] = {}
202
+ if config_path.exists():
203
+ try:
204
+ with open(config_path, "r", encoding="utf-8") as f:
205
+ existing_config = json.load(f)
206
+ except (json.JSONDecodeError, OSError):
207
+ # If config reading fails, start fresh
208
+ existing_config = {}
209
+
210
+ # Merge user settings from existing config (preserve customization)
211
+ if existing_config:
212
+ # Preserve user.nickname if it exists
213
+ if "user" in existing_config and isinstance(existing_config.get("user"), dict):
214
+ if "user" not in config:
215
+ config["user"] = {}
216
+ user_config = config["user"]
217
+ if isinstance(user_config, dict):
218
+ existing_user = existing_config["user"]
219
+ if isinstance(existing_user, dict) and "nickname" in existing_user:
220
+ user_config["nickname"] = existing_user["nickname"]
221
+
222
+ # Preserve language settings if they exist
223
+ if "language" in existing_config and isinstance(existing_config.get("language"), dict):
224
+ if "language" not in config:
225
+ config["language"] = {}
226
+ lang_config = config["language"]
227
+ if isinstance(lang_config, dict):
228
+ existing_lang = existing_config["language"]
229
+ if isinstance(existing_lang, dict):
230
+ # Preserve conversation_language settings
231
+ if "conversation_language" in existing_lang:
232
+ lang_config["conversation_language"] = existing_lang["conversation_language"]
233
+ if "conversation_language_name" in existing_lang:
234
+ lang_config["conversation_language_name"] = existing_lang["conversation_language_name"]
235
+
196
236
  # Ensure project section exists and set defaults
197
237
  if "project" not in config:
198
238
  config["project"] = {}
@@ -202,7 +242,6 @@ class PhaseExecutor:
202
242
  project_config["optimized"] = False # Default value
203
243
 
204
244
  # Write config.json
205
- config_path = project_path / ".moai" / "config.json"
206
245
  with open(config_path, "w", encoding="utf-8") as f:
207
246
  json.dump(config, f, indent=2, ensure_ascii=False)
208
247
 
@@ -216,9 +255,8 @@ class PhaseExecutor:
216
255
  ) -> None:
217
256
  """Phase 5: validation and wrap-up.
218
257
 
219
- @CODE:INIT-004:PHASE5 | Phase 5 verification logic
258
+ @CODE:INIT-PHASE-001 | Phase 5 verification logic
220
259
  @REQ:VALIDATION-001 | SPEC-INIT-004: Verify required files after initialization completion
221
- @CODE:INIT-004:PHASE5-INTEGRATION | Integration of validation in Phase 5
222
260
 
223
261
  Args:
224
262
  project_path: Project path.
@@ -230,8 +268,8 @@ class PhaseExecutor:
230
268
  "Phase 5: Validation and finalization...", progress_callback
231
269
  )
232
270
 
233
- # @CODE:INIT-004:VERIFY-001 | Validate installation results
234
- # @CODE:INIT-004:VALIDATION-CHECK | Comprehensive installation validation
271
+ # Validate installation results
272
+ # Comprehensive installation validation
235
273
  # Verifies all required files including 4 Alfred command files:
236
274
  # - 0-project.md, 1-plan.md, 2-run.md, 3-sync.md
237
275
  self.validator.validate_installation(project_path)
@@ -312,8 +350,12 @@ class PhaseExecutor:
312
350
  cwd=project_path,
313
351
  check=True,
314
352
  capture_output=True,
353
+ timeout=30, # Default timeout for git operations
315
354
  )
316
355
  # Intentionally avoid printing to keep progress output clean
356
+ except subprocess.TimeoutExpired:
357
+ # Timeout is non-fatal
358
+ pass
317
359
  except subprocess.CalledProcessError:
318
360
  # Only log on error; failures are non-fatal
319
361
  pass
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env python3
2
+ # @CODE:DOC-TAG-004 | TAG validation core module (Components 1, 2, 3 & 4)
3
+ """TAG validation and management for MoAI-ADK
4
+
5
+ This module provides TAG validation functionality for:
6
+ - Pre-commit hook validation (Component 1)
7
+ - CI/CD pipeline validation (Component 2)
8
+ - Central validation system (Component 3)
9
+ - Documentation & Reporting (Component 4)
10
+ - TAG format checking
11
+ - Duplicate detection
12
+ - Orphan detection
13
+ - Chain integrity validation
14
+ """
15
+
16
+ # Component 1: Pre-commit validator
17
+ # Component 2: CI/CD validator
18
+ from .ci_validator import CIValidator
19
+ from .pre_commit_validator import (
20
+ PreCommitValidator,
21
+ ValidationError,
22
+ ValidationResult,
23
+ ValidationWarning,
24
+ )
25
+
26
+ # Component 4: Documentation & Reporting
27
+ from .reporter import (
28
+ CoverageAnalyzer,
29
+ CoverageMetrics,
30
+ InventoryGenerator,
31
+ MatrixGenerator,
32
+ ReportFormatter,
33
+ ReportGenerator,
34
+ ReportResult,
35
+ StatisticsGenerator,
36
+ StatisticsReport,
37
+ TagInventory,
38
+ TagMatrix,
39
+ )
40
+
41
+ # Component 3: Central validation system
42
+ from .validator import (
43
+ CentralValidationResult,
44
+ CentralValidator,
45
+ ChainValidator,
46
+ DuplicateValidator,
47
+ FormatValidator,
48
+ OrphanValidator,
49
+ TagValidator,
50
+ ValidationConfig,
51
+ ValidationIssue,
52
+ ValidationStatistics,
53
+ )
54
+
55
+ __all__ = [
56
+ # Component 1
57
+ "PreCommitValidator",
58
+ "ValidationResult",
59
+ "ValidationError",
60
+ "ValidationWarning",
61
+ # Component 2
62
+ "CIValidator",
63
+ # Component 3
64
+ "ValidationConfig",
65
+ "TagValidator",
66
+ "DuplicateValidator",
67
+ "OrphanValidator",
68
+ "ChainValidator",
69
+ "FormatValidator",
70
+ "CentralValidator",
71
+ "CentralValidationResult",
72
+ "ValidationIssue",
73
+ "ValidationStatistics",
74
+ # Component 4
75
+ "TagInventory",
76
+ "TagMatrix",
77
+ "InventoryGenerator",
78
+ "MatrixGenerator",
79
+ "CoverageAnalyzer",
80
+ "StatisticsGenerator",
81
+ "ReportFormatter",
82
+ "ReportGenerator",
83
+ "CoverageMetrics",
84
+ "StatisticsReport",
85
+ "ReportResult",
86
+ ]
@@ -0,0 +1,463 @@
1
+ #!/usr/bin/env python3
2
+ # @CODE:DOC-TAG-004 | Component 2: CI/CD pipeline TAG validator
3
+ """CI/CD TAG validation module for GitHub Actions
4
+
5
+ This module extends PreCommitValidator for CI/CD environments:
6
+ - Fetches PR changed files via GitHub API
7
+ - Generates structured validation reports (JSON/markdown)
8
+ - Posts validation results as PR comments
9
+ - Supports strict mode (block merge on warnings) and info mode
10
+
11
+ Used by GitHub Actions workflow to validate TAGs on every PR.
12
+ """
13
+
14
+ import json
15
+ import os
16
+ from typing import Any, Dict, List, Optional
17
+
18
+ import requests
19
+ from requests.adapters import HTTPAdapter
20
+ from urllib3.util import Retry
21
+
22
+ from .pre_commit_validator import (
23
+ PreCommitValidator,
24
+ ValidationResult,
25
+ )
26
+
27
+
28
+ class CIValidator(PreCommitValidator):
29
+ """CI/CD TAG validator for GitHub Actions
30
+
31
+ Extends PreCommitValidator with CI/CD-specific features:
32
+ - GitHub API integration for PR file detection
33
+ - Structured report generation for automation
34
+ - Markdown comment formatting for PR feedback
35
+ - Environment variable support for GitHub Actions
36
+
37
+ Args:
38
+ github_token: GitHub API token (default: from GITHUB_TOKEN env)
39
+ repo_owner: Repository owner (default: from GITHUB_REPOSITORY env)
40
+ repo_name: Repository name (default: from GITHUB_REPOSITORY env)
41
+ strict_mode: Treat warnings as errors
42
+ check_orphans: Enable orphan TAG detection
43
+ tag_pattern: Custom TAG regex pattern
44
+ """
45
+
46
+ def __init__(
47
+ self,
48
+ github_token: Optional[str] = None,
49
+ repo_owner: Optional[str] = None,
50
+ repo_name: Optional[str] = None,
51
+ strict_mode: bool = False,
52
+ check_orphans: bool = True,
53
+ tag_pattern: Optional[str] = None
54
+ ):
55
+ super().__init__(strict_mode, check_orphans, tag_pattern)
56
+
57
+ # GitHub configuration from environment or parameters
58
+ self.github_token = github_token or os.environ.get('GITHUB_TOKEN', '')
59
+
60
+ # Parse repo info from GITHUB_REPOSITORY (format: "owner/repo")
61
+ repo_full = os.environ.get('GITHUB_REPOSITORY', '')
62
+ if '/' in repo_full and not repo_owner and not repo_name:
63
+ parts = repo_full.split('/', 1)
64
+ self.repo_owner = parts[0]
65
+ self.repo_name = parts[1]
66
+ else:
67
+ self.repo_owner = repo_owner or ''
68
+ self.repo_name = repo_name or ''
69
+
70
+ def get_pr_changed_files(self, pr_number: int) -> List[str]:
71
+ """Fetch list of changed files in a PR via GitHub API
72
+
73
+ Args:
74
+ pr_number: Pull request number
75
+
76
+ Returns:
77
+ List of relative file paths changed in the PR
78
+ """
79
+ if not self.github_token or not self.repo_owner or not self.repo_name:
80
+ return []
81
+
82
+ url = (
83
+ f"https://api.github.com/repos/"
84
+ f"{self.repo_owner}/{self.repo_name}/pulls/{pr_number}/files"
85
+ )
86
+
87
+ headers = {
88
+ 'Authorization': f'Bearer {self.github_token}',
89
+ 'Accept': 'application/vnd.github.v3+json'
90
+ }
91
+
92
+ # Create session with retry strategy
93
+ session = requests.Session()
94
+ retry = Retry(
95
+ total=3,
96
+ backoff_factor=0.5,
97
+ status_forcelist=[500, 502, 503, 504],
98
+ allowed_methods=["GET"]
99
+ )
100
+ adapter = HTTPAdapter(max_retries=retry)
101
+ session.mount("https://", adapter)
102
+
103
+ try:
104
+ # Dual timeout: (connect_timeout, read_timeout)
105
+ response = session.get(
106
+ url,
107
+ headers=headers,
108
+ timeout=(5, 10)
109
+ )
110
+ response.raise_for_status()
111
+
112
+ files_data = response.json()
113
+ return [file_info['filename'] for file_info in files_data]
114
+
115
+ except requests.exceptions.Timeout:
116
+ # Network timeout - return empty list gracefully
117
+ return []
118
+ except requests.exceptions.HTTPError as e:
119
+ # HTTP error (4xx, 5xx)
120
+ if e.response.status_code == 404:
121
+ # PR not found
122
+ return []
123
+ # Other HTTP errors: log but continue
124
+ return []
125
+ except requests.exceptions.RequestException:
126
+ # Network/connection errors
127
+ return []
128
+ finally:
129
+ session.close()
130
+
131
+ def validate_pr_changes(
132
+ self,
133
+ pr_number: int,
134
+ base_branch: str = "main"
135
+ ) -> ValidationResult:
136
+ """Validate TAG annotations in PR changed files
137
+
138
+ Main CI/CD validation method:
139
+ 1. Fetch changed files from GitHub API
140
+ 2. Run validation checks on those files
141
+ 3. Return structured validation result
142
+
143
+ Args:
144
+ pr_number: Pull request number
145
+ base_branch: Base branch name (not used currently)
146
+
147
+ Returns:
148
+ ValidationResult with errors and warnings
149
+ """
150
+ # Get PR changed files
151
+ files = self.get_pr_changed_files(pr_number)
152
+
153
+ if not files:
154
+ return ValidationResult(is_valid=True)
155
+
156
+ # Validate the changed files
157
+ return self.validate_files(files)
158
+
159
+ def generate_report(self, result: ValidationResult) -> Dict[str, Any]:
160
+ """Generate structured validation report
161
+
162
+ Creates JSON-serializable report with:
163
+ - Status (success/failure/success_with_warnings)
164
+ - Error details (message, tag, locations)
165
+ - Warning details (message, tag, location)
166
+ - Statistics (counts)
167
+ - Configuration (strict_mode)
168
+
169
+ Args:
170
+ result: ValidationResult from validation
171
+
172
+ Returns:
173
+ Dictionary with structured report data
174
+ """
175
+ # Determine status
176
+ if not result.is_valid:
177
+ status = 'failure'
178
+ elif result.warnings:
179
+ status = 'success_with_warnings'
180
+ else:
181
+ status = 'success'
182
+
183
+ # Build error list
184
+ errors = []
185
+ for error in result.errors:
186
+ errors.append({
187
+ 'message': error.message,
188
+ 'tag': error.tag,
189
+ 'locations': [
190
+ {'file': filepath, 'line': line_num}
191
+ for filepath, line_num in error.locations
192
+ ]
193
+ })
194
+
195
+ # Build warning list
196
+ warnings = []
197
+ for warning in result.warnings:
198
+ warnings.append({
199
+ 'message': warning.message,
200
+ 'tag': warning.tag,
201
+ 'location': {
202
+ 'file': warning.location[0],
203
+ 'line': warning.location[1]
204
+ }
205
+ })
206
+
207
+ # Calculate statistics
208
+ statistics = {
209
+ 'total_errors': len(result.errors),
210
+ 'total_warnings': len(result.warnings),
211
+ 'total_issues': len(result.errors) + len(result.warnings)
212
+ }
213
+
214
+ # Build complete report
215
+ report = {
216
+ 'status': status,
217
+ 'is_valid': result.is_valid,
218
+ 'strict_mode': self.strict_mode,
219
+ 'summary': self._generate_summary(result),
220
+ 'errors': errors,
221
+ 'warnings': warnings,
222
+ 'statistics': statistics
223
+ }
224
+
225
+ return report
226
+
227
+ def _generate_summary(self, result: ValidationResult) -> str:
228
+ """Generate human-readable summary text
229
+
230
+ Args:
231
+ result: ValidationResult
232
+
233
+ Returns:
234
+ Summary string
235
+ """
236
+ if result.is_valid and not result.warnings:
237
+ return "All TAG validations passed. No issues found."
238
+ elif result.is_valid and result.warnings:
239
+ return f"Validation passed with {len(result.warnings)} warning(s)."
240
+ else:
241
+ return f"Validation failed with {len(result.errors)} error(s)."
242
+
243
+ def format_pr_comment(
244
+ self,
245
+ result: ValidationResult,
246
+ pr_url: str
247
+ ) -> str:
248
+ """Format validation result as markdown PR comment
249
+
250
+ Creates formatted markdown comment with:
251
+ - Status indicator (emoji)
252
+ - Summary message
253
+ - Error/warning table
254
+ - Action items
255
+ - Documentation links
256
+
257
+ Args:
258
+ result: ValidationResult from validation
259
+ pr_url: URL of the pull request
260
+
261
+ Returns:
262
+ Markdown-formatted comment string
263
+ """
264
+ lines = []
265
+
266
+ # Header with status indicator
267
+ if result.is_valid and not result.warnings:
268
+ lines.append("## ✅ TAG Validation Passed")
269
+ lines.append("")
270
+ lines.append("All TAG annotations are valid. No issues found.")
271
+ elif result.is_valid and result.warnings:
272
+ lines.append("## ⚠️ TAG Validation Passed with Warnings")
273
+ lines.append("")
274
+ lines.append(f"Validation passed but found {len(result.warnings)} warning(s).")
275
+ else:
276
+ lines.append("## ❌ TAG Validation Failed")
277
+ lines.append("")
278
+ lines.append(f"Found {len(result.errors)} error(s) that must be fixed.")
279
+
280
+ lines.append("")
281
+
282
+ # Error table
283
+ if result.errors:
284
+ lines.append("### Errors")
285
+ lines.append("")
286
+ lines.append("| TAG | Issue | Location |")
287
+ lines.append("|-----|-------|----------|")
288
+
289
+ for error in result.errors:
290
+ tag = error.tag
291
+ message = error.message
292
+ locations = ', '.join([
293
+ f"`{f}:{line}`" for f, line in error.locations[:3]
294
+ ])
295
+ if len(error.locations) > 3:
296
+ locations += f" (+{len(error.locations) - 3} more)"
297
+
298
+ lines.append(f"| `{tag}` | {message} | {locations} |")
299
+
300
+ lines.append("")
301
+
302
+ # Warning table
303
+ if result.warnings:
304
+ lines.append("### Warnings")
305
+ lines.append("")
306
+ lines.append("| TAG | Issue | Location |")
307
+ lines.append("|-----|-------|----------|")
308
+
309
+ for warning in result.warnings:
310
+ tag = warning.tag
311
+ message = warning.message
312
+ location = f"`{warning.location[0]}:{warning.location[1]}`"
313
+
314
+ lines.append(f"| `{tag}` | {message} | {location} |")
315
+
316
+ lines.append("")
317
+
318
+ # Action items
319
+ if result.errors or result.warnings:
320
+ lines.append("### How to Fix")
321
+ lines.append("")
322
+
323
+ if result.errors:
324
+ lines.append("**Errors (must fix):**")
325
+ lines.append("- Remove duplicate TAG declarations")
326
+ lines.append("- Ensure TAGs follow format: `@PREFIX:DOMAIN-TYPE-NNN`")
327
+ lines.append("")
328
+
329
+ if result.warnings:
330
+ lines.append("**Warnings (recommended):**")
331
+ lines.append("- Add corresponding TEST tags for CODE tags")
332
+ lines.append("- Add corresponding CODE tags for TEST tags")
333
+ lines.append("- Complete TAG chain: SPEC → CODE → TEST → DOC")
334
+ lines.append("")
335
+
336
+ # Documentation link
337
+ lines.append("---")
338
+ lines.append("")
339
+ lines.append("📚 **Documentation:** [TAG System Guide](.moai/memory/tag-system-guide.md)")
340
+ lines.append("")
341
+ lines.append(f"🔗 **PR:** {pr_url}")
342
+
343
+ return "\n".join(lines)
344
+
345
+ def get_pr_number_from_event(self) -> Optional[int]:
346
+ """Extract PR number from GitHub Actions event file
347
+
348
+ Reads GITHUB_EVENT_PATH to get PR number from event payload.
349
+
350
+ Returns:
351
+ PR number or None if not found
352
+ """
353
+ event_path = os.environ.get('GITHUB_EVENT_PATH')
354
+ if not event_path:
355
+ return None
356
+
357
+ try:
358
+ with open(event_path, 'r') as f:
359
+ event_data = json.load(f)
360
+ return event_data.get('pull_request', {}).get('number')
361
+ except Exception:
362
+ return None
363
+
364
+ def generate_tag_report_link(self, pr_number: int) -> str:
365
+ """Generate link to TAG reports for this PR
366
+
367
+ Integration point with Component 4 (Reporting).
368
+ Provides link to automated TAG reports generated by GitHub Actions.
369
+
370
+ Args:
371
+ pr_number: Pull request number
372
+
373
+ Returns:
374
+ Markdown link to TAG reports
375
+ """
376
+ # Link to GitHub Actions artifacts or docs directory
377
+ if self.repo_owner and self.repo_name:
378
+ docs_url = (
379
+ f"https://github.com/{self.repo_owner}/{self.repo_name}/tree/main/docs"
380
+ )
381
+ return f"📊 [View TAG Reports]({docs_url})"
382
+ else:
383
+ return "📊 TAG Reports: See docs/ directory"
384
+
385
+
386
+ def main():
387
+ """CLI entry point for CI/CD validation"""
388
+ import argparse
389
+ import sys
390
+
391
+ parser = argparse.ArgumentParser(
392
+ description="Validate TAG annotations in GitHub PR"
393
+ )
394
+ parser.add_argument(
395
+ "--pr-number",
396
+ type=int,
397
+ help="Pull request number (default: from GitHub Actions event)"
398
+ )
399
+ parser.add_argument(
400
+ "--strict",
401
+ action="store_true",
402
+ help="Treat warnings as errors"
403
+ )
404
+ parser.add_argument(
405
+ "--no-orphan-check",
406
+ action="store_true",
407
+ help="Disable orphan TAG checking"
408
+ )
409
+ parser.add_argument(
410
+ "--output-json",
411
+ help="Output report to JSON file"
412
+ )
413
+ parser.add_argument(
414
+ "--output-comment",
415
+ help="Output PR comment to file"
416
+ )
417
+
418
+ args = parser.parse_args()
419
+
420
+ validator = CIValidator(
421
+ strict_mode=args.strict,
422
+ check_orphans=not args.no_orphan_check
423
+ )
424
+
425
+ # Get PR number
426
+ pr_number = args.pr_number
427
+ if not pr_number:
428
+ pr_number = validator.get_pr_number_from_event()
429
+
430
+ if not pr_number:
431
+ print("Error: Could not determine PR number", file=sys.stderr)
432
+ sys.exit(1)
433
+
434
+ # Run validation
435
+ result = validator.validate_pr_changes(pr_number)
436
+
437
+ # Generate report
438
+ report = validator.generate_report(result)
439
+
440
+ # Output JSON report if requested
441
+ if args.output_json:
442
+ with open(args.output_json, 'w') as f:
443
+ json.dump(report, f, indent=2)
444
+
445
+ # Output PR comment if requested
446
+ if args.output_comment:
447
+ pr_url = (
448
+ f"https://github.com/{validator.repo_owner}/"
449
+ f"{validator.repo_name}/pull/{pr_number}"
450
+ )
451
+ comment = validator.format_pr_comment(result, pr_url)
452
+ with open(args.output_comment, 'w') as f:
453
+ f.write(comment)
454
+
455
+ # Print summary
456
+ print(result.format())
457
+
458
+ # Exit with error code if validation failed
459
+ sys.exit(0 if result.is_valid else 1)
460
+
461
+
462
+ if __name__ == "__main__":
463
+ main()