moai-adk 0.8.1__py3-none-any.whl → 0.8.2__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 (87) hide show
  1. moai_adk/cli/commands/update.py +15 -4
  2. moai_adk/core/tags/__init__.py +87 -0
  3. moai_adk/core/tags/ci_validator.py +435 -0
  4. moai_adk/core/tags/cli.py +283 -0
  5. moai_adk/core/tags/generator.py +109 -0
  6. moai_adk/core/tags/inserter.py +99 -0
  7. moai_adk/core/tags/mapper.py +126 -0
  8. moai_adk/core/tags/parser.py +76 -0
  9. moai_adk/core/tags/pre_commit_validator.py +355 -0
  10. moai_adk/core/tags/reporter.py +959 -0
  11. moai_adk/core/tags/tags.py +149 -0
  12. moai_adk/core/tags/validator.py +897 -0
  13. moai_adk/templates/.claude/agents/alfred/cc-manager.md +25 -2
  14. moai_adk/templates/.claude/agents/alfred/debug-helper.md +24 -12
  15. moai_adk/templates/.claude/agents/alfred/doc-syncer.md +19 -12
  16. moai_adk/templates/.claude/agents/alfred/git-manager.md +20 -12
  17. moai_adk/templates/.claude/agents/alfred/implementation-planner.md +19 -12
  18. moai_adk/templates/.claude/agents/alfred/project-manager.md +29 -2
  19. moai_adk/templates/.claude/agents/alfred/quality-gate.md +25 -2
  20. moai_adk/templates/.claude/agents/alfred/skill-factory.md +30 -2
  21. moai_adk/templates/.claude/agents/alfred/spec-builder.md +26 -11
  22. moai_adk/templates/.claude/agents/alfred/tag-agent.md +30 -8
  23. moai_adk/templates/.claude/agents/alfred/tdd-implementer.md +27 -12
  24. moai_adk/templates/.claude/agents/alfred/trust-checker.md +25 -2
  25. moai_adk/templates/.claude/commands/alfred/0-project.md +5 -0
  26. moai_adk/templates/.claude/commands/alfred/1-plan.md +17 -4
  27. moai_adk/templates/.claude/commands/alfred/2-run.md +7 -0
  28. moai_adk/templates/.claude/commands/alfred/3-sync.md +6 -0
  29. moai_adk/templates/.claude/hooks/alfred/.moai/cache/version-check.json +9 -0
  30. moai_adk/templates/.claude/hooks/alfred/README.md +258 -145
  31. moai_adk/templates/.claude/hooks/alfred/TROUBLESHOOTING.md +471 -0
  32. moai_adk/templates/.claude/hooks/alfred/alfred_hooks.py +92 -57
  33. moai_adk/templates/.claude/hooks/alfred/core/version_cache.py +198 -0
  34. moai_adk/templates/.claude/hooks/alfred/notification__handle_events.py +102 -0
  35. moai_adk/templates/.claude/hooks/alfred/post_tool__log_changes.py +102 -0
  36. moai_adk/templates/.claude/hooks/alfred/pre_tool__auto_checkpoint.py +108 -0
  37. moai_adk/templates/.claude/hooks/alfred/session_end__cleanup.py +102 -0
  38. moai_adk/templates/.claude/hooks/alfred/session_start__show_project_info.py +102 -0
  39. moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/project.py +269 -13
  40. moai_adk/templates/.claude/hooks/alfred/shared/core/version_cache.py +198 -0
  41. moai_adk/templates/.claude/hooks/alfred/{handlers → shared/handlers}/session.py +21 -7
  42. moai_adk/templates/.claude/hooks/alfred/stop__handle_interrupt.py +102 -0
  43. moai_adk/templates/.claude/hooks/alfred/subagent_stop__handle_subagent_end.py +102 -0
  44. moai_adk/templates/.claude/hooks/alfred/user_prompt__jit_load_docs.py +120 -0
  45. moai_adk/templates/.claude/settings.json +5 -5
  46. moai_adk/templates/.claude/skills/moai-foundation-ears/SKILL.md +9 -6
  47. moai_adk/templates/.claude/skills/moai-spec-authoring/README.md +56 -56
  48. moai_adk/templates/.claude/skills/moai-spec-authoring/SKILL.md +101 -100
  49. moai_adk/templates/.claude/skills/moai-spec-authoring/examples/validate-spec.sh +3 -3
  50. moai_adk/templates/.claude/skills/moai-spec-authoring/examples.md +219 -219
  51. moai_adk/templates/.claude/skills/moai-spec-authoring/reference.md +287 -287
  52. moai_adk/templates/.github/ISSUE_TEMPLATE/spec.yml +9 -11
  53. moai_adk/templates/.github/PULL_REQUEST_TEMPLATE.md +9 -21
  54. moai_adk/templates/.github/workflows/moai-release-create.yml +100 -0
  55. moai_adk/templates/.github/workflows/moai-release-pipeline.yml +182 -0
  56. moai_adk/templates/.github/workflows/release.yml +49 -0
  57. moai_adk/templates/.github/workflows/tag-report.yml +261 -0
  58. moai_adk/templates/.github/workflows/tag-validation.yml +176 -0
  59. moai_adk/templates/.moai/config.json +6 -1
  60. moai_adk/templates/.moai/hooks/install.sh +79 -0
  61. moai_adk/templates/.moai/hooks/pre-commit.sh +66 -0
  62. moai_adk/templates/CLAUDE.md +39 -40
  63. moai_adk/templates/src/moai_adk/core/__init__.py +5 -0
  64. moai_adk/templates/src/moai_adk/core/tags/__init__.py +87 -0
  65. moai_adk/templates/src/moai_adk/core/tags/ci_validator.py +435 -0
  66. moai_adk/templates/src/moai_adk/core/tags/cli.py +283 -0
  67. moai_adk/templates/src/moai_adk/core/tags/pre_commit_validator.py +355 -0
  68. moai_adk/templates/src/moai_adk/core/tags/reporter.py +959 -0
  69. moai_adk/templates/src/moai_adk/core/tags/validator.py +897 -0
  70. {moai_adk-0.8.1.dist-info → moai_adk-0.8.2.dist-info}/METADATA +226 -1
  71. {moai_adk-0.8.1.dist-info → moai_adk-0.8.2.dist-info}/RECORD +83 -50
  72. moai_adk/templates/.claude/hooks/alfred/HOOK_SCHEMA_VALIDATION.md +0 -313
  73. moai_adk/templates/.moai/memory/config-schema.md +0 -444
  74. moai_adk/templates/.moai/memory/gitflow-protection-policy.md +0 -220
  75. moai_adk/templates/.moai/memory/spec-metadata.md +0 -356
  76. /moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/__init__.py +0 -0
  77. /moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/checkpoint.py +0 -0
  78. /moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/context.py +0 -0
  79. /moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/tags.py +0 -0
  80. /moai_adk/templates/.claude/hooks/alfred/{handlers → shared/handlers}/__init__.py +0 -0
  81. /moai_adk/templates/.claude/hooks/alfred/{handlers → shared/handlers}/notification.py +0 -0
  82. /moai_adk/templates/.claude/hooks/alfred/{handlers → shared/handlers}/tool.py +0 -0
  83. /moai_adk/templates/.claude/hooks/alfred/{handlers → shared/handlers}/user.py +0 -0
  84. /moai_adk/templates/.moai/memory/{issue-label-mapping.md → ISSUE-LABEL-MAPPING.md} +0 -0
  85. {moai_adk-0.8.1.dist-info → moai_adk-0.8.2.dist-info}/WHEEL +0 -0
  86. {moai_adk-0.8.1.dist-info → moai_adk-0.8.2.dist-info}/entry_points.txt +0 -0
  87. {moai_adk-0.8.1.dist-info → moai_adk-0.8.2.dist-info}/licenses/LICENSE +0 -0
@@ -61,47 +61,43 @@ You are the SuperAgent **🎩 Alfred** of **🗿 MoAI-ADK**. Follow these core p
61
61
 
62
62
  ## 🌍 Alfred's Language Boundary Rule
63
63
 
64
- Alfred operates with a **crystal-clear three-layer language architecture** to support global users while keeping all Skills in English only:
64
+ Alfred operates with a **clear two-layer language architecture** to support global users while keeping the infrastructure in English:
65
65
 
66
- ### Layer 1: User Conversation
66
+ ### Layer 1: User Conversation & Dynamic Content
67
67
  **ALWAYS use user's `conversation_language` for ALL user-facing content:**
68
68
  - 🗣️ **Responses to user**: User's configured language (Korean, Japanese, Spanish, etc.)
69
69
  - 📝 **Explanations**: User's language
70
70
  - ❓ **Questions to user**: User's language
71
71
  - 💬 **All dialogue**: User's language
72
-
73
- ### Layer 2: Internal Operations
74
- **EVERYTHING internal MUST be in English:**
75
- - `Task(prompt="...")` invocations → **English**
76
- - `Skill("skill-name")` calls **English**
77
- - Sub-agent communication **English**
78
- - Error messages (internal) → **English**
72
+ - 📄 **Generated documents**: User's language (SPEC, reports, analysis)
73
+ - 🔧 **Task prompts**: User's language (passed directly to Sub-agents)
74
+ - 📨 **Sub-agent communication**: User's language
75
+
76
+ ### Layer 2: Static Infrastructure (English Only)
77
+ **MoAI-ADK package and templates stay in English:**
78
+ - `Skill("skill-name")` → **Skill names always English** (explicit invocation)
79
+ - `.claude/skills/` → **Skill content in English** (technical documentation standard)
80
+ - `.claude/agents/` → **Agent templates in English**
81
+ - `.claude/commands/` → **Command templates in English**
82
+ - Code comments → **English**
79
83
  - Git commit messages → **English**
80
- - All technical instructions → **English**
81
-
82
- ### Layer 3: Skills & Code
83
- **Skills maintain English-only for infinite scalability:**
84
- - Skill descriptions → **English only**
85
- - Skill examples → **English only**
86
- - Skill guides → **English only**
87
- - Code comments → **English only**
88
- - No multilingual versions needed! ✅
84
+ - @TAG identifiers → **English**
85
+ - Technical function/variable names → **English**
89
86
 
90
87
  ### Execution Flow Example
91
88
 
92
89
  ```
93
- User Input (any language): "Check code quality" / "コード品質をチェック" / "Verificar calidad del código"
90
+ User Input (any language): "코드 품질 검사해줘" / "Check code quality" / "コード品質をチェック"
94
91
 
95
- Alfred (internal translation): "Check code quality" (→ English)
92
+ Alfred (passes directly): Task(prompt="코드 품질 검사...", subagent_type="trust-checker")
96
93
 
97
- Invoke Sub-agent: Task(prompt="Validate TRUST 5 principles",
98
- subagent_type="trust-checker")
94
+ Sub-agent (receives Korean): Recognizes quality check task
99
95
 
100
- Sub-agent (receives English): Skill("moai-foundation-trust") ← 100% match!
96
+ Sub-agent (explicit call): Skill("moai-foundation-trust")
101
97
 
102
- Alfred (receives results): English TRUST report
98
+ Skill loads (English content): Sub-agent reads English Skill guidance
103
99
 
104
- Alfred (translates back): User's language response
100
+ Sub-agent generates output: Korean report based on user's language
105
101
 
106
102
  User Receives: Response in their configured language
107
103
  ```
@@ -109,23 +105,25 @@ User Receives: Response in their configured language
109
105
  ### Why This Pattern Works
110
106
 
111
107
  1. **Scalability**: Support any language without modifying 55 Skills
112
- 2. **Maintainability**: Skills stay in English (single source of truth)
113
- 3. **Reliability**: English keywords always match English Skill descriptions = 100% success rate
114
- 4. **Best Practice**: Follows standard i18n architecture (localized frontend, English backend lingua franca)
115
- 5. **Future-proof**: Add new languages instantly (Korean Japanese → Spanish → Russian, etc.)
108
+ 2. **Maintainability**: Skills stay in English (single source of truth, industry standard for technical docs)
109
+ 3. **Reliability**: **Explicit Skill() invocation** = 100% success rate (no keyword matching needed)
110
+ 4. **Simplicity**: No translation layer overhead, direct language pass-through
111
+ 5. **Future-proof**: Add new languages instantly without code changes
116
112
 
117
113
  ### Key Rules for Sub-agents
118
114
 
119
- **All 12 Sub-agents MUST receive English prompts**, regardless of user's conversation language:
115
+ **All 12 Sub-agents work in user's configured language:**
120
116
 
121
117
  | Sub-agent | Input Language | Output Language | Notes |
122
118
  |-----------|---|---|---|
123
- | spec-builder | **English** | English (reports to Alfred) | User requests translated to English before Task() call |
124
- | tdd-implementer | **English** | English | Receives English SPEC references |
125
- | doc-syncer | **English** | English | Processes English file descriptions |
126
- | implementation-planner | **English** | English | Architecture analysis in English |
127
- | debug-helper | **English** | English | Error analysis in English |
128
- | All others | **English** | English | Consistency across entire team |
119
+ | spec-builder | **User's language** | User's language | Invokes Skills explicitly: Skill("moai-foundation-ears") |
120
+ | tdd-implementer | **User's language** | User's language | Code comments in English, narratives in user's language |
121
+ | doc-syncer | **User's language** | User's language | Generated docs in user's language |
122
+ | implementation-planner | **User's language** | User's language | Architecture analysis in user's language |
123
+ | debug-helper | **User's language** | User's language | Error analysis in user's language |
124
+ | All others | **User's language** | User's language | Explicit Skill() invocation regardless of prompt language |
125
+
126
+ **CRITICAL**: Skills are invoked **explicitly** using `Skill("skill-name")` syntax, NOT auto-triggered by keywords.
129
127
 
130
128
  ---
131
129
 
@@ -305,10 +303,11 @@ Your project is ready. You can now run `/alfred:1-plan` to start planning specs.
305
303
  - `.moai/memory/`
306
304
  - `CLAUDE.md` (this file)
307
305
 
308
- **Rationale**: These files define system behavior, tool invocations, and internal communication. English ensures:
309
- 1. Skill trigger keywords always match English prompts (100% auto-invocation reliability)
310
- 2. Global maintainability without translation burden
311
- 3. Infinite language scalability (support any user language without code changes)
306
+ **Rationale**: These files define system behavior, tool invocations, and internal infrastructure. English ensures:
307
+ 1. **Industry standard**: Technical documentation in English (single source of truth)
308
+ 2. **Global maintainability**: No translation burden for 55 Skills, 12 agents, 4 commands
309
+ 3. **Infinite scalability**: Support any user language without modifying infrastructure
310
+ 4. **Reliable invocation**: Explicit Skill("name") calls work regardless of prompt language
312
311
 
313
312
  ---
314
313
 
@@ -0,0 +1,5 @@
1
+ """MoAI-ADK core modules
2
+
3
+ This package contains core functionality for MoAI-ADK including:
4
+ - TAG validation and management
5
+ """
@@ -0,0 +1,87 @@
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
+ from .pre_commit_validator import (
18
+ PreCommitValidator,
19
+ ValidationResult,
20
+ ValidationError,
21
+ ValidationWarning,
22
+ )
23
+
24
+ # Component 2: CI/CD validator
25
+ from .ci_validator import CIValidator
26
+
27
+ # Component 3: Central validation system
28
+ from .validator import (
29
+ ValidationConfig,
30
+ TagValidator,
31
+ DuplicateValidator,
32
+ OrphanValidator,
33
+ ChainValidator,
34
+ FormatValidator,
35
+ CentralValidator,
36
+ CentralValidationResult,
37
+ ValidationIssue,
38
+ ValidationStatistics,
39
+ )
40
+
41
+ # Component 4: Documentation & Reporting
42
+ from .reporter import (
43
+ TagInventory,
44
+ TagMatrix,
45
+ InventoryGenerator,
46
+ MatrixGenerator,
47
+ CoverageAnalyzer,
48
+ StatisticsGenerator,
49
+ ReportFormatter,
50
+ ReportGenerator,
51
+ CoverageMetrics,
52
+ StatisticsReport,
53
+ ReportResult,
54
+ )
55
+
56
+ __all__ = [
57
+ # Component 1
58
+ "PreCommitValidator",
59
+ "ValidationResult",
60
+ "ValidationError",
61
+ "ValidationWarning",
62
+ # Component 2
63
+ "CIValidator",
64
+ # Component 3
65
+ "ValidationConfig",
66
+ "TagValidator",
67
+ "DuplicateValidator",
68
+ "OrphanValidator",
69
+ "ChainValidator",
70
+ "FormatValidator",
71
+ "CentralValidator",
72
+ "CentralValidationResult",
73
+ "ValidationIssue",
74
+ "ValidationStatistics",
75
+ # Component 4
76
+ "TagInventory",
77
+ "TagMatrix",
78
+ "InventoryGenerator",
79
+ "MatrixGenerator",
80
+ "CoverageAnalyzer",
81
+ "StatisticsGenerator",
82
+ "ReportFormatter",
83
+ "ReportGenerator",
84
+ "CoverageMetrics",
85
+ "StatisticsReport",
86
+ "ReportResult",
87
+ ]
@@ -0,0 +1,435 @@
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 pathlib import Path
17
+ from typing import List, Dict, Any, Optional, Tuple
18
+ import requests
19
+
20
+ from .pre_commit_validator import (
21
+ PreCommitValidator,
22
+ ValidationResult,
23
+ ValidationError,
24
+ ValidationWarning,
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
+ try:
93
+ response = requests.get(url, headers=headers, timeout=10)
94
+ response.raise_for_status()
95
+
96
+ files_data = response.json()
97
+ return [file_info['filename'] for file_info in files_data]
98
+
99
+ except Exception:
100
+ # Return empty list on any error (network, auth, etc.)
101
+ return []
102
+
103
+ def validate_pr_changes(
104
+ self,
105
+ pr_number: int,
106
+ base_branch: str = "main"
107
+ ) -> ValidationResult:
108
+ """Validate TAG annotations in PR changed files
109
+
110
+ Main CI/CD validation method:
111
+ 1. Fetch changed files from GitHub API
112
+ 2. Run validation checks on those files
113
+ 3. Return structured validation result
114
+
115
+ Args:
116
+ pr_number: Pull request number
117
+ base_branch: Base branch name (not used currently)
118
+
119
+ Returns:
120
+ ValidationResult with errors and warnings
121
+ """
122
+ # Get PR changed files
123
+ files = self.get_pr_changed_files(pr_number)
124
+
125
+ if not files:
126
+ return ValidationResult(is_valid=True)
127
+
128
+ # Validate the changed files
129
+ return self.validate_files(files)
130
+
131
+ def generate_report(self, result: ValidationResult) -> Dict[str, Any]:
132
+ """Generate structured validation report
133
+
134
+ Creates JSON-serializable report with:
135
+ - Status (success/failure/success_with_warnings)
136
+ - Error details (message, tag, locations)
137
+ - Warning details (message, tag, location)
138
+ - Statistics (counts)
139
+ - Configuration (strict_mode)
140
+
141
+ Args:
142
+ result: ValidationResult from validation
143
+
144
+ Returns:
145
+ Dictionary with structured report data
146
+ """
147
+ # Determine status
148
+ if not result.is_valid:
149
+ status = 'failure'
150
+ elif result.warnings:
151
+ status = 'success_with_warnings'
152
+ else:
153
+ status = 'success'
154
+
155
+ # Build error list
156
+ errors = []
157
+ for error in result.errors:
158
+ errors.append({
159
+ 'message': error.message,
160
+ 'tag': error.tag,
161
+ 'locations': [
162
+ {'file': filepath, 'line': line_num}
163
+ for filepath, line_num in error.locations
164
+ ]
165
+ })
166
+
167
+ # Build warning list
168
+ warnings = []
169
+ for warning in result.warnings:
170
+ warnings.append({
171
+ 'message': warning.message,
172
+ 'tag': warning.tag,
173
+ 'location': {
174
+ 'file': warning.location[0],
175
+ 'line': warning.location[1]
176
+ }
177
+ })
178
+
179
+ # Calculate statistics
180
+ statistics = {
181
+ 'total_errors': len(result.errors),
182
+ 'total_warnings': len(result.warnings),
183
+ 'total_issues': len(result.errors) + len(result.warnings)
184
+ }
185
+
186
+ # Build complete report
187
+ report = {
188
+ 'status': status,
189
+ 'is_valid': result.is_valid,
190
+ 'strict_mode': self.strict_mode,
191
+ 'summary': self._generate_summary(result),
192
+ 'errors': errors,
193
+ 'warnings': warnings,
194
+ 'statistics': statistics
195
+ }
196
+
197
+ return report
198
+
199
+ def _generate_summary(self, result: ValidationResult) -> str:
200
+ """Generate human-readable summary text
201
+
202
+ Args:
203
+ result: ValidationResult
204
+
205
+ Returns:
206
+ Summary string
207
+ """
208
+ if result.is_valid and not result.warnings:
209
+ return "All TAG validations passed. No issues found."
210
+ elif result.is_valid and result.warnings:
211
+ return f"Validation passed with {len(result.warnings)} warning(s)."
212
+ else:
213
+ return f"Validation failed with {len(result.errors)} error(s)."
214
+
215
+ def format_pr_comment(
216
+ self,
217
+ result: ValidationResult,
218
+ pr_url: str
219
+ ) -> str:
220
+ """Format validation result as markdown PR comment
221
+
222
+ Creates formatted markdown comment with:
223
+ - Status indicator (emoji)
224
+ - Summary message
225
+ - Error/warning table
226
+ - Action items
227
+ - Documentation links
228
+
229
+ Args:
230
+ result: ValidationResult from validation
231
+ pr_url: URL of the pull request
232
+
233
+ Returns:
234
+ Markdown-formatted comment string
235
+ """
236
+ lines = []
237
+
238
+ # Header with status indicator
239
+ if result.is_valid and not result.warnings:
240
+ lines.append("## ✅ TAG Validation Passed")
241
+ lines.append("")
242
+ lines.append("All TAG annotations are valid. No issues found.")
243
+ elif result.is_valid and result.warnings:
244
+ lines.append("## ⚠️ TAG Validation Passed with Warnings")
245
+ lines.append("")
246
+ lines.append(f"Validation passed but found {len(result.warnings)} warning(s).")
247
+ else:
248
+ lines.append("## ❌ TAG Validation Failed")
249
+ lines.append("")
250
+ lines.append(f"Found {len(result.errors)} error(s) that must be fixed.")
251
+
252
+ lines.append("")
253
+
254
+ # Error table
255
+ if result.errors:
256
+ lines.append("### Errors")
257
+ lines.append("")
258
+ lines.append("| TAG | Issue | Location |")
259
+ lines.append("|-----|-------|----------|")
260
+
261
+ for error in result.errors:
262
+ tag = error.tag
263
+ message = error.message
264
+ locations = ', '.join([
265
+ f"`{f}:{l}`" for f, l in error.locations[:3]
266
+ ])
267
+ if len(error.locations) > 3:
268
+ locations += f" (+{len(error.locations) - 3} more)"
269
+
270
+ lines.append(f"| `{tag}` | {message} | {locations} |")
271
+
272
+ lines.append("")
273
+
274
+ # Warning table
275
+ if result.warnings:
276
+ lines.append("### Warnings")
277
+ lines.append("")
278
+ lines.append("| TAG | Issue | Location |")
279
+ lines.append("|-----|-------|----------|")
280
+
281
+ for warning in result.warnings:
282
+ tag = warning.tag
283
+ message = warning.message
284
+ location = f"`{warning.location[0]}:{warning.location[1]}`"
285
+
286
+ lines.append(f"| `{tag}` | {message} | {location} |")
287
+
288
+ lines.append("")
289
+
290
+ # Action items
291
+ if result.errors or result.warnings:
292
+ lines.append("### How to Fix")
293
+ lines.append("")
294
+
295
+ if result.errors:
296
+ lines.append("**Errors (must fix):**")
297
+ lines.append("- Remove duplicate TAG declarations")
298
+ lines.append("- Ensure TAGs follow format: `@PREFIX:DOMAIN-TYPE-NNN`")
299
+ lines.append("")
300
+
301
+ if result.warnings:
302
+ lines.append("**Warnings (recommended):**")
303
+ lines.append("- Add corresponding TEST tags for CODE tags")
304
+ lines.append("- Add corresponding CODE tags for TEST tags")
305
+ lines.append("- Complete TAG chain: SPEC → CODE → TEST → DOC")
306
+ lines.append("")
307
+
308
+ # Documentation link
309
+ lines.append("---")
310
+ lines.append("")
311
+ lines.append("📚 **Documentation:** [TAG System Guide](.moai/memory/tag-system-guide.md)")
312
+ lines.append("")
313
+ lines.append(f"🔗 **PR:** {pr_url}")
314
+
315
+ return "\n".join(lines)
316
+
317
+ def get_pr_number_from_event(self) -> Optional[int]:
318
+ """Extract PR number from GitHub Actions event file
319
+
320
+ Reads GITHUB_EVENT_PATH to get PR number from event payload.
321
+
322
+ Returns:
323
+ PR number or None if not found
324
+ """
325
+ event_path = os.environ.get('GITHUB_EVENT_PATH')
326
+ if not event_path:
327
+ return None
328
+
329
+ try:
330
+ with open(event_path, 'r') as f:
331
+ event_data = json.load(f)
332
+ return event_data.get('pull_request', {}).get('number')
333
+ except Exception:
334
+ return None
335
+
336
+ def generate_tag_report_link(self, pr_number: int) -> str:
337
+ """Generate link to TAG reports for this PR
338
+
339
+ Integration point with Component 4 (Reporting).
340
+ Provides link to automated TAG reports generated by GitHub Actions.
341
+
342
+ Args:
343
+ pr_number: Pull request number
344
+
345
+ Returns:
346
+ Markdown link to TAG reports
347
+ """
348
+ # Link to GitHub Actions artifacts or docs directory
349
+ if self.repo_owner and self.repo_name:
350
+ docs_url = (
351
+ f"https://github.com/{self.repo_owner}/{self.repo_name}/tree/main/docs"
352
+ )
353
+ return f"📊 [View TAG Reports]({docs_url})"
354
+ else:
355
+ return "📊 TAG Reports: See docs/ directory"
356
+
357
+
358
+ def main():
359
+ """CLI entry point for CI/CD validation"""
360
+ import sys
361
+ import argparse
362
+
363
+ parser = argparse.ArgumentParser(
364
+ description="Validate TAG annotations in GitHub PR"
365
+ )
366
+ parser.add_argument(
367
+ "--pr-number",
368
+ type=int,
369
+ help="Pull request number (default: from GitHub Actions event)"
370
+ )
371
+ parser.add_argument(
372
+ "--strict",
373
+ action="store_true",
374
+ help="Treat warnings as errors"
375
+ )
376
+ parser.add_argument(
377
+ "--no-orphan-check",
378
+ action="store_true",
379
+ help="Disable orphan TAG checking"
380
+ )
381
+ parser.add_argument(
382
+ "--output-json",
383
+ help="Output report to JSON file"
384
+ )
385
+ parser.add_argument(
386
+ "--output-comment",
387
+ help="Output PR comment to file"
388
+ )
389
+
390
+ args = parser.parse_args()
391
+
392
+ validator = CIValidator(
393
+ strict_mode=args.strict,
394
+ check_orphans=not args.no_orphan_check
395
+ )
396
+
397
+ # Get PR number
398
+ pr_number = args.pr_number
399
+ if not pr_number:
400
+ pr_number = validator.get_pr_number_from_event()
401
+
402
+ if not pr_number:
403
+ print("Error: Could not determine PR number", file=sys.stderr)
404
+ sys.exit(1)
405
+
406
+ # Run validation
407
+ result = validator.validate_pr_changes(pr_number)
408
+
409
+ # Generate report
410
+ report = validator.generate_report(result)
411
+
412
+ # Output JSON report if requested
413
+ if args.output_json:
414
+ with open(args.output_json, 'w') as f:
415
+ json.dump(report, f, indent=2)
416
+
417
+ # Output PR comment if requested
418
+ if args.output_comment:
419
+ pr_url = (
420
+ f"https://github.com/{validator.repo_owner}/"
421
+ f"{validator.repo_name}/pull/{pr_number}"
422
+ )
423
+ comment = validator.format_pr_comment(result, pr_url)
424
+ with open(args.output_comment, 'w') as f:
425
+ f.write(comment)
426
+
427
+ # Print summary
428
+ print(result.format())
429
+
430
+ # Exit with error code if validation failed
431
+ sys.exit(0 if result.is_valid else 1)
432
+
433
+
434
+ if __name__ == "__main__":
435
+ main()