moai-adk 0.8.1__py3-none-any.whl → 0.8.3__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/config/migration.py +1 -1
  3. moai_adk/core/issue_creator.py +7 -3
  4. moai_adk/core/tags/__init__.py +86 -0
  5. moai_adk/core/tags/ci_validator.py +433 -0
  6. moai_adk/core/tags/cli.py +283 -0
  7. moai_adk/core/tags/generator.py +109 -0
  8. moai_adk/core/tags/inserter.py +99 -0
  9. moai_adk/core/tags/mapper.py +126 -0
  10. moai_adk/core/tags/parser.py +76 -0
  11. moai_adk/core/tags/pre_commit_validator.py +355 -0
  12. moai_adk/core/tags/reporter.py +957 -0
  13. moai_adk/core/tags/tags.py +149 -0
  14. moai_adk/core/tags/validator.py +897 -0
  15. moai_adk/templates/.claude/agents/alfred/cc-manager.md +25 -2
  16. moai_adk/templates/.claude/agents/alfred/debug-helper.md +24 -12
  17. moai_adk/templates/.claude/agents/alfred/doc-syncer.md +19 -12
  18. moai_adk/templates/.claude/agents/alfred/git-manager.md +20 -12
  19. moai_adk/templates/.claude/agents/alfred/implementation-planner.md +19 -12
  20. moai_adk/templates/.claude/agents/alfred/project-manager.md +29 -2
  21. moai_adk/templates/.claude/agents/alfred/quality-gate.md +25 -2
  22. moai_adk/templates/.claude/agents/alfred/skill-factory.md +30 -2
  23. moai_adk/templates/.claude/agents/alfred/spec-builder.md +26 -11
  24. moai_adk/templates/.claude/agents/alfred/tag-agent.md +30 -8
  25. moai_adk/templates/.claude/agents/alfred/tdd-implementer.md +27 -12
  26. moai_adk/templates/.claude/agents/alfred/trust-checker.md +25 -2
  27. moai_adk/templates/.claude/commands/alfred/0-project.md +5 -0
  28. moai_adk/templates/.claude/commands/alfred/1-plan.md +82 -19
  29. moai_adk/templates/.claude/commands/alfred/2-run.md +72 -15
  30. moai_adk/templates/.claude/commands/alfred/3-sync.md +74 -14
  31. moai_adk/templates/.claude/hooks/alfred/.moai/cache/version-check.json +9 -0
  32. moai_adk/templates/.claude/hooks/alfred/README.md +258 -145
  33. moai_adk/templates/.claude/hooks/alfred/TROUBLESHOOTING.md +471 -0
  34. moai_adk/templates/.claude/hooks/alfred/alfred_hooks.py +92 -57
  35. moai_adk/templates/.claude/hooks/alfred/core/version_cache.py +198 -0
  36. moai_adk/templates/.claude/hooks/alfred/notification__handle_events.py +102 -0
  37. moai_adk/templates/.claude/hooks/alfred/post_tool__log_changes.py +102 -0
  38. moai_adk/templates/.claude/hooks/alfred/pre_tool__auto_checkpoint.py +108 -0
  39. moai_adk/templates/.claude/hooks/alfred/session_end__cleanup.py +102 -0
  40. moai_adk/templates/.claude/hooks/alfred/session_start__show_project_info.py +102 -0
  41. moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/project.py +286 -19
  42. moai_adk/templates/.claude/hooks/alfred/shared/core/version_cache.py +198 -0
  43. moai_adk/templates/.claude/hooks/alfred/{handlers → shared/handlers}/session.py +21 -7
  44. moai_adk/templates/.claude/hooks/alfred/stop__handle_interrupt.py +102 -0
  45. moai_adk/templates/.claude/hooks/alfred/subagent_stop__handle_subagent_end.py +102 -0
  46. moai_adk/templates/.claude/hooks/alfred/user_prompt__jit_load_docs.py +120 -0
  47. moai_adk/templates/.claude/settings.json +5 -5
  48. moai_adk/templates/.claude/skills/moai-foundation-ears/SKILL.md +9 -6
  49. moai_adk/templates/.claude/skills/moai-spec-authoring/README.md +56 -56
  50. moai_adk/templates/.claude/skills/moai-spec-authoring/SKILL.md +101 -100
  51. moai_adk/templates/.claude/skills/moai-spec-authoring/examples/validate-spec.sh +3 -3
  52. moai_adk/templates/.claude/skills/moai-spec-authoring/examples.md +219 -219
  53. moai_adk/templates/.claude/skills/moai-spec-authoring/reference.md +287 -287
  54. moai_adk/templates/.github/ISSUE_TEMPLATE/spec.yml +9 -11
  55. moai_adk/templates/.github/PULL_REQUEST_TEMPLATE.md +9 -21
  56. moai_adk/templates/.github/workflows/moai-release-create.yml +100 -0
  57. moai_adk/templates/.github/workflows/moai-release-pipeline.yml +182 -0
  58. moai_adk/templates/.github/workflows/release.yml +49 -0
  59. moai_adk/templates/.github/workflows/tag-report.yml +261 -0
  60. moai_adk/templates/.github/workflows/tag-validation.yml +176 -0
  61. moai_adk/templates/.moai/config.json +6 -1
  62. moai_adk/templates/.moai/hooks/install.sh +79 -0
  63. moai_adk/templates/.moai/hooks/pre-commit.sh +66 -0
  64. moai_adk/templates/CLAUDE.md +39 -40
  65. moai_adk/templates/src/moai_adk/core/__init__.py +5 -0
  66. moai_adk/templates/src/moai_adk/core/tags/__init__.py +86 -0
  67. moai_adk/templates/src/moai_adk/core/tags/ci_validator.py +433 -0
  68. moai_adk/templates/src/moai_adk/core/tags/cli.py +283 -0
  69. moai_adk/templates/src/moai_adk/core/tags/pre_commit_validator.py +355 -0
  70. moai_adk/templates/src/moai_adk/core/tags/reporter.py +957 -0
  71. moai_adk/templates/src/moai_adk/core/tags/validator.py +897 -0
  72. {moai_adk-0.8.1.dist-info → moai_adk-0.8.3.dist-info}/METADATA +240 -14
  73. {moai_adk-0.8.1.dist-info → moai_adk-0.8.3.dist-info}/RECORD +85 -50
  74. moai_adk/templates/.claude/hooks/alfred/HOOK_SCHEMA_VALIDATION.md +0 -313
  75. moai_adk/templates/.moai/memory/config-schema.md +0 -444
  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.3.dist-info}/WHEEL +0 -0
  86. {moai_adk-0.8.1.dist-info → moai_adk-0.8.3.dist-info}/entry_points.txt +0 -0
  87. {moai_adk-0.8.1.dist-info → moai_adk-0.8.3.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,355 @@
1
+ #!/usr/bin/env python3
2
+ # @CODE:DOC-TAG-004 | Component 1: Pre-commit TAG validator
3
+ """Pre-commit TAG validation module
4
+
5
+ This module provides validation functionality for TAG annotations:
6
+ - Format validation (@DOC:DOMAIN-TYPE-NNN)
7
+ - Duplicate TAG detection across files
8
+ - Orphan TAG detection (CODE without TEST, etc.)
9
+ - Git staged file scanning
10
+
11
+ Used by pre-commit hooks to ensure TAG quality.
12
+ """
13
+
14
+ import re
15
+ import subprocess
16
+ from dataclasses import dataclass, field
17
+ from pathlib import Path
18
+ from typing import Dict, List, Optional, Tuple
19
+
20
+
21
+ @dataclass
22
+ class ValidationError:
23
+ """Validation error with file location information"""
24
+ message: str
25
+ tag: str
26
+ locations: List[Tuple[str, int]] = field(default_factory=list)
27
+
28
+ def __str__(self) -> str:
29
+ loc_str = ", ".join([f"{f}:{l}" for f, l in self.locations])
30
+ return f"{self.message}: {self.tag} at {loc_str}"
31
+
32
+
33
+ @dataclass
34
+ class ValidationWarning:
35
+ """Validation warning with file location"""
36
+ message: str
37
+ tag: str
38
+ location: Tuple[str, int]
39
+
40
+ def __str__(self) -> str:
41
+ return f"{self.message}: {self.tag} at {self.location[0]}:{self.location[1]}"
42
+
43
+
44
+ @dataclass
45
+ class ValidationResult:
46
+ """Complete validation result"""
47
+ is_valid: bool
48
+ errors: List[ValidationError] = field(default_factory=list)
49
+ warnings: List[ValidationWarning] = field(default_factory=list)
50
+
51
+ def format(self) -> str:
52
+ """Format result for display"""
53
+ lines = []
54
+
55
+ if self.errors:
56
+ lines.append("Errors:")
57
+ for error in self.errors:
58
+ lines.append(f" - {error}")
59
+
60
+ if self.warnings:
61
+ lines.append("\nWarnings:")
62
+ for warning in self.warnings:
63
+ lines.append(f" - {warning}")
64
+
65
+ if not self.errors and not self.warnings:
66
+ lines.append("No issues found.")
67
+
68
+ return "\n".join(lines)
69
+
70
+
71
+ class PreCommitValidator:
72
+ """Pre-commit TAG validator
73
+
74
+ Validates TAG annotations in files:
75
+ - Format: @DOC:DOMAIN-TYPE-NNN
76
+ - No duplicates
77
+ - No orphans (CODE without TEST)
78
+
79
+ Args:
80
+ strict_mode: Treat warnings as errors
81
+ check_orphans: Enable orphan TAG detection
82
+ tag_pattern: Custom TAG regex pattern
83
+ """
84
+
85
+ # Default TAG pattern: @(SPEC|CODE|TEST|DOC):DOMAIN-NNN or DOMAIN-TYPE-NNN
86
+ # Matches formats like:
87
+ # - @CODE:AUTH-API-001 (domain-type-number)
88
+ # - @CODE:SPEC-001 (domain-number)
89
+ # - @TEST:USER-REG-001 (domain-type-number)
90
+ DEFAULT_TAG_PATTERN = r"@(SPEC|CODE|TEST|DOC):([A-Z]+(?:-[A-Z]+)*-\d{3})"
91
+
92
+ def __init__(
93
+ self,
94
+ strict_mode: bool = False,
95
+ check_orphans: bool = True,
96
+ tag_pattern: Optional[str] = None
97
+ ):
98
+ self.strict_mode = strict_mode
99
+ self.check_orphans = check_orphans
100
+ self.tag_pattern = re.compile(tag_pattern or self.DEFAULT_TAG_PATTERN)
101
+
102
+ def validate_format(self, tag: str) -> bool:
103
+ """Validate TAG format
104
+
105
+ Args:
106
+ tag: TAG string (e.g., "@CODE:AUTH-API-001")
107
+
108
+ Returns:
109
+ True if format is valid
110
+ """
111
+ return bool(self.tag_pattern.match(tag))
112
+
113
+ def extract_tags(self, content: str) -> List[str]:
114
+ """Extract all TAGs from content
115
+
116
+ Args:
117
+ content: File content
118
+
119
+ Returns:
120
+ List of TAG strings
121
+ """
122
+ matches = self.tag_pattern.findall(content)
123
+ # Convert tuples to full TAG strings
124
+ tags = [f"@{prefix}:{domain}" for prefix, domain in matches]
125
+ return tags
126
+
127
+ def validate_duplicates(self, files: List[str]) -> List[ValidationError]:
128
+ """Detect duplicate TAGs
129
+
130
+ Args:
131
+ files: List of file paths to scan
132
+
133
+ Returns:
134
+ List of validation errors for duplicates
135
+ """
136
+ errors: List[ValidationError] = []
137
+ tag_locations: Dict[str, List[Tuple[str, int]]] = {}
138
+
139
+ for filepath in files:
140
+ try:
141
+ path = Path(filepath)
142
+ if not path.exists() or not path.is_file():
143
+ continue
144
+
145
+ content = path.read_text(encoding="utf-8", errors="ignore")
146
+ lines = content.splitlines()
147
+
148
+ for line_num, line in enumerate(lines, start=1):
149
+ tags = self.extract_tags(line)
150
+ for tag in tags:
151
+ if tag not in tag_locations:
152
+ tag_locations[tag] = []
153
+ tag_locations[tag].append((filepath, line_num))
154
+
155
+ except Exception:
156
+ # Skip files that can't be read
157
+ continue
158
+
159
+ # Find duplicates
160
+ for tag, locations in tag_locations.items():
161
+ if len(locations) > 1:
162
+ errors.append(ValidationError(
163
+ message="Duplicate TAG found",
164
+ tag=tag,
165
+ locations=locations
166
+ ))
167
+
168
+ return errors
169
+
170
+ def validate_orphans(self, files: List[str]) -> List[ValidationWarning]:
171
+ """Detect orphan TAGs
172
+
173
+ Orphan TAGs are:
174
+ - @CODE without corresponding @TEST
175
+ - @TEST without corresponding @CODE
176
+ - @SPEC without implementation
177
+
178
+ Args:
179
+ files: List of file paths to scan
180
+
181
+ Returns:
182
+ List of validation warnings
183
+ """
184
+ if not self.check_orphans:
185
+ return []
186
+
187
+ warnings: List[ValidationWarning] = []
188
+
189
+ # Collect all TAGs by type and domain
190
+ tags_by_type: Dict[str, Dict[str, List[Tuple[str, int]]]] = {
191
+ "SPEC": {},
192
+ "CODE": {},
193
+ "TEST": {},
194
+ "DOC": {}
195
+ }
196
+
197
+ for filepath in files:
198
+ try:
199
+ path = Path(filepath)
200
+ if not path.exists() or not path.is_file():
201
+ continue
202
+
203
+ content = path.read_text(encoding="utf-8", errors="ignore")
204
+ lines = content.splitlines()
205
+
206
+ for line_num, line in enumerate(lines, start=1):
207
+ matches = self.tag_pattern.findall(line)
208
+ for prefix, domain in matches:
209
+ tag = f"@{prefix}:{domain}"
210
+ if domain not in tags_by_type[prefix]:
211
+ tags_by_type[prefix][domain] = []
212
+ tags_by_type[prefix][domain].append((filepath, line_num))
213
+
214
+ except Exception:
215
+ continue
216
+
217
+ # Check for orphans
218
+ # CODE without TEST
219
+ for domain, locations in tags_by_type["CODE"].items():
220
+ if domain not in tags_by_type["TEST"]:
221
+ for filepath, line_num in locations:
222
+ warnings.append(ValidationWarning(
223
+ message="CODE TAG without corresponding TEST",
224
+ tag=f"@CODE:{domain}",
225
+ location=(filepath, line_num)
226
+ ))
227
+
228
+ # TEST without CODE
229
+ for domain, locations in tags_by_type["TEST"].items():
230
+ if domain not in tags_by_type["CODE"]:
231
+ for filepath, line_num in locations:
232
+ warnings.append(ValidationWarning(
233
+ message="TEST TAG without corresponding CODE",
234
+ tag=f"@TEST:{domain}",
235
+ location=(filepath, line_num)
236
+ ))
237
+
238
+ return warnings
239
+
240
+ def get_staged_files(self, repo_path: str = ".") -> List[str]:
241
+ """Get list of staged files from git
242
+
243
+ Args:
244
+ repo_path: Git repository path
245
+
246
+ Returns:
247
+ List of staged file paths
248
+ """
249
+ try:
250
+ result = subprocess.run(
251
+ ["git", "diff", "--name-only", "--cached"],
252
+ cwd=repo_path,
253
+ capture_output=True,
254
+ text=True,
255
+ timeout=5,
256
+ check=True
257
+ )
258
+ files = [
259
+ line.strip()
260
+ for line in result.stdout.splitlines()
261
+ if line.strip()
262
+ ]
263
+ return files
264
+ except Exception:
265
+ return []
266
+
267
+ def validate_files(self, files: List[str]) -> ValidationResult:
268
+ """Validate list of files
269
+
270
+ Main validation method that runs all checks:
271
+ - Format validation
272
+ - Duplicate detection
273
+ - Orphan detection
274
+
275
+ Args:
276
+ files: List of file paths to validate
277
+
278
+ Returns:
279
+ ValidationResult with errors and warnings
280
+ """
281
+ if not files:
282
+ return ValidationResult(is_valid=True)
283
+
284
+ # Check for duplicates
285
+ errors = self.validate_duplicates(files)
286
+
287
+ # Check for orphans
288
+ warnings = self.validate_orphans(files)
289
+
290
+ # In strict mode, warnings become errors
291
+ if self.strict_mode and warnings:
292
+ is_valid = False
293
+ else:
294
+ is_valid = len(errors) == 0
295
+
296
+ return ValidationResult(
297
+ is_valid=is_valid,
298
+ errors=errors,
299
+ warnings=warnings
300
+ )
301
+
302
+
303
+ def main():
304
+ """CLI entry point for pre-commit hook"""
305
+ import argparse
306
+ import sys
307
+
308
+ parser = argparse.ArgumentParser(
309
+ description="Validate TAG annotations in git staged files"
310
+ )
311
+ parser.add_argument(
312
+ "--files",
313
+ nargs="*",
314
+ help="Files to validate (default: git staged files)"
315
+ )
316
+ parser.add_argument(
317
+ "--strict",
318
+ action="store_true",
319
+ help="Treat warnings as errors"
320
+ )
321
+ parser.add_argument(
322
+ "--no-orphan-check",
323
+ action="store_true",
324
+ help="Disable orphan TAG checking"
325
+ )
326
+
327
+ args = parser.parse_args()
328
+
329
+ validator = PreCommitValidator(
330
+ strict_mode=args.strict,
331
+ check_orphans=not args.no_orphan_check
332
+ )
333
+
334
+ # Get files to validate
335
+ if args.files:
336
+ files = args.files
337
+ else:
338
+ files = validator.get_staged_files()
339
+
340
+ if not files:
341
+ print("No files to validate.")
342
+ sys.exit(0)
343
+
344
+ # Run validation
345
+ result = validator.validate_files(files)
346
+
347
+ # Print results
348
+ print(result.format())
349
+
350
+ # Exit with error code if validation failed
351
+ sys.exit(0 if result.is_valid else 1)
352
+
353
+
354
+ if __name__ == "__main__":
355
+ main()