moai-adk 0.4.5__py3-none-any.whl → 0.20.1__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.
- moai_adk/__init__.py +1 -1
- moai_adk/__main__.py +74 -1
- moai_adk/cli/commands/__init__.py +1 -1
- moai_adk/cli/commands/analyze.py +119 -0
- moai_adk/cli/commands/backup.py +25 -1
- moai_adk/cli/commands/doctor.py +31 -5
- moai_adk/cli/commands/improve_user_experience.py +307 -0
- moai_adk/cli/commands/init.py +111 -10
- moai_adk/cli/commands/status.py +33 -3
- moai_adk/cli/commands/update.py +921 -130
- moai_adk/cli/commands/validate_links.py +120 -0
- moai_adk/cli/prompts/init_prompts.py +22 -87
- moai_adk/core/analysis/__init__.py +9 -0
- moai_adk/core/analysis/session_analyzer.py +388 -0
- moai_adk/core/analysis/tag_chain_analyzer.py +344 -0
- moai_adk/core/analysis/tag_chain_repair.py +879 -0
- moai_adk/core/config/__init__.py +19 -0
- moai_adk/core/config/migration.py +235 -0
- moai_adk/core/git/__init__.py +1 -1
- moai_adk/core/git/branch.py +1 -1
- moai_adk/core/git/commit.py +1 -1
- moai_adk/core/git/manager.py +1 -1
- moai_adk/core/issue_creator.py +313 -0
- moai_adk/core/mcp/setup.py +56 -0
- moai_adk/core/mcp/setup_old.py +296 -0
- moai_adk/core/project/backup_utils.py +1 -1
- moai_adk/core/project/checker.py +2 -2
- moai_adk/core/project/detector.py +211 -12
- moai_adk/core/project/initializer.py +85 -15
- moai_adk/core/project/phase_executor.py +76 -13
- moai_adk/core/project/validator.py +13 -13
- moai_adk/core/quality/__init__.py +1 -1
- moai_adk/core/quality/trust_checker.py +1 -1
- moai_adk/core/quality/validators/__init__.py +1 -1
- moai_adk/core/quality/validators/base_validator.py +1 -1
- moai_adk/core/tags/__init__.py +86 -0
- moai_adk/core/tags/auto_corrector.py +693 -0
- moai_adk/core/tags/ci_validator.py +463 -0
- moai_adk/core/tags/cli.py +283 -0
- moai_adk/core/tags/generator.py +109 -0
- moai_adk/core/tags/inserter.py +99 -0
- moai_adk/core/tags/mapper.py +126 -0
- moai_adk/core/tags/parser.py +76 -0
- moai_adk/core/tags/policy_validator.py +580 -0
- moai_adk/core/tags/pre_commit_validator.py +421 -0
- moai_adk/core/tags/reporter.py +956 -0
- moai_adk/core/tags/rollback_manager.py +525 -0
- moai_adk/core/tags/tags.py +149 -0
- moai_adk/core/tags/validator.py +897 -0
- moai_adk/core/template/__init__.py +1 -1
- moai_adk/core/template/backup.py +1 -1
- moai_adk/core/template/merger.py +50 -1
- moai_adk/core/template/processor.py +119 -13
- moai_adk/core/template_engine.py +268 -0
- moai_adk/templates/.claude/agents/alfred/backend-expert.md +348 -0
- moai_adk/templates/.claude/agents/alfred/cc-manager.md +209 -944
- moai_adk/templates/.claude/agents/alfred/database-expert.md +352 -0
- moai_adk/templates/.claude/agents/alfred/debug-helper.md +34 -5
- moai_adk/templates/.claude/agents/alfred/devops-expert.md +464 -0
- moai_adk/templates/.claude/agents/alfred/doc-syncer.md +38 -8
- moai_adk/templates/.claude/agents/alfred/format-expert.md +469 -0
- moai_adk/templates/.claude/agents/alfred/frontend-expert.md +357 -0
- moai_adk/templates/.claude/agents/alfred/git-manager.md +128 -9
- moai_adk/templates/.claude/agents/alfred/implementation-planner.md +104 -6
- moai_adk/templates/.claude/agents/alfred/project-manager.md +88 -16
- moai_adk/templates/.claude/agents/alfred/quality-gate.md +36 -9
- moai_adk/templates/.claude/agents/alfred/security-expert.md +270 -0
- moai_adk/templates/.claude/agents/alfred/skill-factory.md +865 -0
- moai_adk/templates/.claude/agents/alfred/spec-builder.md +214 -43
- moai_adk/templates/.claude/agents/alfred/tag-agent.md +111 -9
- moai_adk/templates/.claude/agents/alfred/tdd-implementer.md +309 -160
- moai_adk/templates/.claude/agents/alfred/trust-checker.md +36 -7
- moai_adk/templates/.claude/agents/alfred/ui-ux-expert.md +605 -0
- moai_adk/templates/.claude/commands/alfred/0-project.md +393 -966
- moai_adk/templates/.claude/commands/alfred/1-plan.md +651 -367
- moai_adk/templates/.claude/commands/alfred/2-run.md +388 -241
- moai_adk/templates/.claude/commands/alfred/3-sync.md +1921 -410
- moai_adk/templates/.claude/commands/alfred/9-feedback.md +153 -0
- moai_adk/templates/.claude/commands/alfred/release-new.md +3604 -0
- moai_adk/templates/.claude/hooks/alfred/core/project.py +484 -20
- moai_adk/templates/.claude/hooks/alfred/core/timeout.py +136 -0
- moai_adk/templates/.claude/hooks/alfred/core/ttl_cache.py +108 -0
- moai_adk/templates/.claude/hooks/alfred/core/version_cache.py +198 -0
- moai_adk/templates/.claude/hooks/alfred/handlers/__init__.py +14 -6
- moai_adk/templates/.claude/hooks/alfred/post_tool__enable_streaming_ui.py +50 -0
- moai_adk/templates/.claude/hooks/alfred/post_tool__log_changes.py +93 -0
- moai_adk/templates/.claude/hooks/alfred/post_tool__tag_auto_corrector.py +407 -0
- moai_adk/templates/.claude/hooks/alfred/pre_tool__auto_checkpoint.py +99 -0
- moai_adk/templates/.claude/hooks/alfred/pre_tool__realtime_tag_monitor.py +335 -0
- moai_adk/templates/.claude/hooks/alfred/pre_tool__tag_policy_validator.py +325 -0
- moai_adk/templates/.claude/hooks/alfred/session_end__cleanup.py +93 -0
- moai_adk/templates/.claude/hooks/alfred/session_start__auto_cleanup.py +580 -0
- moai_adk/templates/.claude/hooks/alfred/session_start__show_project_info.py +298 -0
- moai_adk/templates/.claude/hooks/alfred/shared/core/__init__.py +170 -0
- moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/checkpoint.py +3 -3
- moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/context.py +5 -5
- moai_adk/templates/.claude/hooks/alfred/shared/core/project.py +749 -0
- moai_adk/templates/.claude/hooks/alfred/shared/core/tags.py +230 -0
- moai_adk/templates/.claude/hooks/alfred/shared/core/version_cache.py +198 -0
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/__init__.py +21 -0
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/daily_analysis.py +351 -0
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/notification.py +154 -0
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/session.py +174 -0
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/tool.py +87 -0
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/user.py +61 -0
- moai_adk/templates/.claude/hooks/alfred/user_prompt__jit_load_docs.py +111 -0
- moai_adk/templates/.claude/hooks/alfred/utils/__init__.py +1 -0
- moai_adk/templates/.claude/hooks/alfred/utils/hook_config.py +94 -0
- moai_adk/templates/.claude/hooks/alfred/utils/timeout.py +161 -0
- moai_adk/templates/.claude/output-styles/alfred/alfred-moai-adk-beginner.md +267 -0
- moai_adk/templates/.claude/output-styles/alfred/keating-personal-tutor.md +440 -0
- moai_adk/templates/.claude/output-styles/alfred/r2d2-agentic-coding.md +583 -0
- moai_adk/templates/.claude/settings.json +96 -14
- moai_adk/templates/.claude/skills/moai-alfred-agent-guide/SKILL.md +70 -0
- moai_adk/templates/.claude/skills/moai-alfred-agent-guide/examples.md +62 -0
- moai_adk/templates/.claude/skills/moai-alfred-agent-guide/reference.md +242 -0
- moai_adk/templates/.claude/skills/moai-alfred-ask-user-questions/SKILL.md +237 -0
- moai_adk/templates/.claude/skills/moai-alfred-ask-user-questions/examples.md +871 -0
- moai_adk/templates/.claude/skills/moai-alfred-ask-user-questions/reference.md +653 -0
- moai_adk/templates/.claude/skills/moai-alfred-clone-pattern/README.md +162 -0
- moai_adk/templates/.claude/skills/moai-alfred-clone-pattern/SKILL.md +227 -0
- moai_adk/templates/.claude/skills/moai-alfred-clone-pattern/examples.md +354 -0
- moai_adk/templates/.claude/skills/moai-alfred-clone-pattern/reference.md +158 -0
- moai_adk/templates/.claude/skills/moai-alfred-code-reviewer/SKILL.md +179 -79
- moai_adk/templates/.claude/skills/moai-alfred-code-reviewer/examples.md +117 -0
- moai_adk/templates/.claude/skills/moai-alfred-code-reviewer/scripts/pre-review-check.sh +62 -0
- moai_adk/templates/.claude/skills/moai-alfred-config-schema/SKILL.md +132 -0
- moai_adk/templates/.claude/skills/moai-alfred-config-schema/examples.md +28 -0
- moai_adk/templates/.claude/skills/moai-alfred-config-schema/reference.md +444 -0
- moai_adk/templates/.claude/skills/moai-alfred-context-budget/SKILL.md +62 -0
- moai_adk/templates/.claude/skills/moai-alfred-context-budget/examples.md +28 -0
- moai_adk/templates/.claude/skills/moai-alfred-context-budget/reference.md +405 -0
- moai_adk/templates/.claude/skills/moai-alfred-dev-guide/SKILL.md +51 -0
- moai_adk/templates/.claude/skills/moai-alfred-dev-guide/examples.md +355 -0
- moai_adk/templates/.claude/skills/moai-alfred-dev-guide/reference.md +239 -0
- moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/SKILL.md +323 -0
- moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/examples.md +286 -0
- moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/reference.md +126 -0
- moai_adk/templates/.claude/skills/moai-alfred-issue-labels/SKILL.md +229 -0
- moai_adk/templates/.claude/skills/moai-alfred-issue-labels/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-issue-labels/reference.md +150 -0
- moai_adk/templates/.claude/skills/moai-alfred-language-detection/SKILL.md +87 -73
- moai_adk/templates/.claude/skills/moai-alfred-language-detection/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-alfred-language-detection/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-alfred-personas/README.md +42 -0
- moai_adk/templates/.claude/skills/moai-alfred-personas/SKILL.md +429 -0
- moai_adk/templates/.claude/skills/moai-alfred-personas/examples.md +520 -0
- moai_adk/templates/.claude/skills/moai-alfred-personas/reference.md +405 -0
- moai_adk/templates/.claude/skills/moai-alfred-practices/SKILL.md +89 -0
- moai_adk/templates/.claude/skills/moai-alfred-practices/examples.md +122 -0
- moai_adk/templates/.claude/skills/moai-alfred-practices/reference.md +369 -0
- moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/SKILL.md +508 -0
- moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/examples.md +481 -0
- moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/reference.md +100 -0
- moai_adk/templates/.claude/skills/moai-alfred-rules/SKILL.md +77 -0
- moai_adk/templates/.claude/skills/moai-alfred-rules/examples.md +265 -0
- moai_adk/templates/.claude/skills/moai-alfred-rules/reference.md +539 -0
- moai_adk/templates/.claude/skills/moai-alfred-session-state/SKILL.md +320 -0
- moai_adk/templates/.claude/skills/moai-alfred-session-state/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-session-state/reference.md +84 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/README.md +137 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/SKILL.md +219 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/examples/validate-spec.sh +161 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/examples.md +541 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/reference.md +622 -0
- moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/SKILL.md +19 -0
- moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/reference.md +211 -0
- moai_adk/templates/.claude/skills/moai-alfred-workflow/SKILL.md +288 -0
- moai_adk/templates/.claude/skills/moai-cc-agents/SKILL.md +269 -0
- moai_adk/templates/.claude/skills/moai-cc-agents/templates/agent-template.md +32 -0
- moai_adk/templates/.claude/skills/moai-cc-claude-md/SKILL.md +298 -0
- moai_adk/templates/.claude/skills/moai-cc-claude-md/templates/CLAUDE-template.md +26 -0
- moai_adk/templates/.claude/skills/moai-cc-commands/SKILL.md +307 -0
- moai_adk/templates/.claude/skills/moai-cc-commands/templates/command-template.md +21 -0
- moai_adk/templates/.claude/skills/moai-cc-hooks/SKILL.md +252 -0
- moai_adk/templates/.claude/skills/moai-cc-hooks/scripts/pre-bash-check.sh +19 -0
- moai_adk/templates/.claude/skills/moai-cc-hooks/scripts/preserve-permissions.sh +19 -0
- moai_adk/templates/.claude/skills/moai-cc-hooks/scripts/validate-bash-command.py +24 -0
- moai_adk/templates/.claude/skills/moai-cc-mcp-plugins/SKILL.md +199 -0
- moai_adk/templates/.claude/skills/moai-cc-mcp-plugins/templates/settings-mcp-template.json +39 -0
- moai_adk/templates/.claude/skills/moai-cc-memory/SKILL.md +316 -0
- moai_adk/templates/.claude/skills/moai-cc-memory/templates/session-summary-template.md +18 -0
- moai_adk/templates/.claude/skills/moai-cc-settings/SKILL.md +263 -0
- moai_adk/templates/.claude/skills/moai-cc-settings/templates/settings-complete-template.json +30 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/CHECKLIST.md +482 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/EXAMPLES.md +303 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/INTERACTIVE-DISCOVERY.md +524 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/METADATA.md +477 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/PARALLEL-ANALYSIS-REPORT.md +429 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/PYTHON-VERSION-MATRIX.md +391 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/SKILL-FACTORY-WORKFLOW.md +431 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/SKILL-UPDATE-ADVISOR.md +577 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/SKILL.md +273 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/STEP-BY-STEP-GUIDE.md +466 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/STRUCTURE.md +583 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/WEB-RESEARCH.md +526 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/reference.md +608 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/scripts/generate-structure.sh +328 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/scripts/validate-skill.sh +312 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/templates/SKILL_TEMPLATE.md +245 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/templates/examples-template.md +285 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/templates/reference-template.md +278 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/templates/scripts-template.sh +303 -0
- moai_adk/templates/.claude/skills/moai-cc-skills/SKILL.md +291 -0
- moai_adk/templates/.claude/skills/moai-cc-skills/templates/SKILL-template.md +15 -0
- moai_adk/templates/.claude/skills/moai-change-logger/SKILL.md +563 -0
- moai_adk/templates/.claude/skills/moai-design-systems/SKILL.md +802 -0
- moai_adk/templates/.claude/skills/moai-design-systems/examples.md +1238 -0
- moai_adk/templates/.claude/skills/moai-design-systems/reference.md +673 -0
- moai_adk/templates/.claude/skills/moai-domain-backend/SKILL.md +234 -43
- moai_adk/templates/.claude/skills/moai-domain-backend/examples.md +1633 -0
- moai_adk/templates/.claude/skills/moai-domain-backend/reference.md +660 -0
- moai_adk/templates/.claude/skills/moai-domain-cli-tool/SKILL.md +97 -69
- moai_adk/templates/.claude/skills/moai-domain-cli-tool/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-cli-tool/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-domain-data-science/SKILL.md +97 -72
- moai_adk/templates/.claude/skills/moai-domain-data-science/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-data-science/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-domain-database/SKILL.md +97 -74
- moai_adk/templates/.claude/skills/moai-domain-database/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-database/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-domain-devops/SKILL.md +98 -74
- moai_adk/templates/.claude/skills/moai-domain-devops/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-devops/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/SKILL.md +102 -73
- moai_adk/templates/.claude/skills/moai-domain-frontend/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-domain-ml/SKILL.md +97 -73
- moai_adk/templates/.claude/skills/moai-domain-ml/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-ml/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-domain-mobile-app/SKILL.md +97 -67
- moai_adk/templates/.claude/skills/moai-domain-mobile-app/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-mobile-app/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-domain-security/SKILL.md +97 -79
- moai_adk/templates/.claude/skills/moai-domain-security/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-security/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-domain-web-api/SKILL.md +97 -71
- moai_adk/templates/.claude/skills/moai-domain-web-api/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-domain-web-api/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-essentials-debug/SKILL.md +265 -64
- moai_adk/templates/.claude/skills/moai-essentials-debug/examples.md +1064 -0
- moai_adk/templates/.claude/skills/moai-essentials-debug/reference.md +1047 -0
- moai_adk/templates/.claude/skills/moai-essentials-perf/SKILL.md +87 -78
- moai_adk/templates/.claude/skills/moai-essentials-perf/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-essentials-perf/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-essentials-refactor/SKILL.md +87 -70
- moai_adk/templates/.claude/skills/moai-essentials-refactor/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-essentials-refactor/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-essentials-review/SKILL.md +87 -86
- moai_adk/templates/.claude/skills/moai-essentials-review/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-essentials-review/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-foundation-ears/SKILL.md +80 -62
- moai_adk/templates/.claude/skills/moai-foundation-ears/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-foundation-ears/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-foundation-git/SKILL.md +207 -50
- moai_adk/templates/.claude/skills/moai-foundation-git/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-foundation-git/reference.md +29 -0
- moai_adk/templates/.claude/skills/moai-foundation-langs/SKILL.md +90 -71
- moai_adk/templates/.claude/skills/moai-foundation-langs/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-foundation-langs/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-foundation-specs/SKILL.md +78 -58
- moai_adk/templates/.claude/skills/moai-foundation-specs/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-foundation-specs/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-foundation-tags/SKILL.md +78 -51
- moai_adk/templates/.claude/skills/moai-foundation-tags/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-foundation-tags/reference.md +28 -0
- moai_adk/templates/.claude/skills/moai-foundation-trust/.!11330!examples.md +0 -0
- moai_adk/templates/.claude/skills/moai-foundation-trust/SKILL.md +253 -32
- moai_adk/templates/.claude/skills/moai-foundation-trust/examples.md +0 -0
- moai_adk/templates/.claude/skills/moai-foundation-trust/reference.md +1099 -0
- moai_adk/templates/.claude/skills/moai-jit-docs-enhanced/SKILL.md +460 -0
- moai_adk/templates/.claude/skills/moai-lang-c/SKILL.md +98 -74
- moai_adk/templates/.claude/skills/moai-lang-c/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-c/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-cpp/SKILL.md +98 -76
- moai_adk/templates/.claude/skills/moai-lang-cpp/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-cpp/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/SKILL.md +2358 -70
- moai_adk/templates/.claude/skills/moai-lang-csharp/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-lang-dart/SKILL.md +2962 -68
- moai_adk/templates/.claude/skills/moai-lang-dart/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-dart/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-lang-go/SKILL.md +1898 -70
- moai_adk/templates/.claude/skills/moai-lang-go/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-go/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-java/SKILL.md +1465 -68
- moai_adk/templates/.claude/skills/moai-lang-java/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-java/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-javascript/SKILL.md +2364 -66
- moai_adk/templates/.claude/skills/moai-lang-javascript/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-javascript/reference.md +32 -0
- moai_adk/templates/.claude/skills/moai-lang-kotlin/SKILL.md +1630 -69
- moai_adk/templates/.claude/skills/moai-lang-kotlin/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-kotlin/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-php/SKILL.md +89 -61
- moai_adk/templates/.claude/skills/moai-lang-php/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-php/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-lang-python/SKILL.md +735 -66
- moai_adk/templates/.claude/skills/moai-lang-python/examples.md +624 -0
- moai_adk/templates/.claude/skills/moai-lang-python/reference.md +316 -0
- moai_adk/templates/.claude/skills/moai-lang-r/SKILL.md +97 -73
- moai_adk/templates/.claude/skills/moai-lang-r/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-r/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-lang-ruby/SKILL.md +98 -73
- moai_adk/templates/.claude/skills/moai-lang-ruby/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-ruby/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-rust/SKILL.md +1834 -70
- moai_adk/templates/.claude/skills/moai-lang-rust/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-rust/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/SKILL.md +99 -74
- moai_adk/templates/.claude/skills/moai-lang-scala/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-lang-shell/SKILL.md +97 -74
- moai_adk/templates/.claude/skills/moai-lang-shell/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-shell/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-lang-sql/SKILL.md +98 -74
- moai_adk/templates/.claude/skills/moai-lang-sql/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-sql/reference.md +31 -0
- moai_adk/templates/.claude/skills/moai-lang-swift/SKILL.md +1959 -69
- moai_adk/templates/.claude/skills/moai-lang-swift/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-swift/reference.md +30 -0
- moai_adk/templates/.claude/skills/moai-lang-template/SKILL.md +348 -0
- moai_adk/templates/.claude/skills/moai-lang-template/VARIABLES.md +98 -0
- moai_adk/templates/.claude/skills/moai-lang-typescript/SKILL.md +1230 -66
- moai_adk/templates/.claude/skills/moai-lang-typescript/examples.md +29 -0
- moai_adk/templates/.claude/skills/moai-lang-typescript/reference.md +34 -0
- moai_adk/templates/.claude/skills/moai-learning-optimizer/SKILL.md +575 -0
- moai_adk/templates/.claude/skills/moai-project-batch-questions/README.md +50 -0
- moai_adk/templates/.claude/skills/moai-project-batch-questions/SKILL.md +304 -0
- moai_adk/templates/.claude/skills/moai-project-batch-questions/examples.md +417 -0
- moai_adk/templates/.claude/skills/moai-project-batch-questions/reference.md +704 -0
- moai_adk/templates/.claude/skills/moai-project-config-manager/README.md +87 -0
- moai_adk/templates/.claude/skills/moai-project-config-manager/SKILL.md +552 -0
- moai_adk/templates/.claude/skills/moai-project-config-manager/examples.md +1109 -0
- moai_adk/templates/.claude/skills/moai-project-config-manager/reference.md +514 -0
- moai_adk/templates/.claude/skills/moai-project-config-manager/validate.py +106 -0
- moai_adk/templates/.claude/skills/moai-project-documentation/README.md +11 -0
- moai_adk/templates/.claude/skills/moai-project-documentation/SKILL.md +622 -0
- moai_adk/templates/.claude/skills/moai-project-documentation/examples.md +20 -0
- moai_adk/templates/.claude/skills/moai-project-documentation/reference.md +12 -0
- moai_adk/templates/.claude/skills/moai-project-language-initializer/README.md +152 -0
- moai_adk/templates/.claude/skills/moai-project-language-initializer/SKILL.md +285 -0
- moai_adk/templates/.claude/skills/moai-project-language-initializer/examples.md +333 -0
- moai_adk/templates/.claude/skills/moai-project-language-initializer/reference.md +386 -0
- moai_adk/templates/.claude/skills/moai-project-template-optimizer/README.md +49 -0
- moai_adk/templates/.claude/skills/moai-project-template-optimizer/SKILL.md +319 -0
- moai_adk/templates/.claude/skills/moai-project-template-optimizer/examples.md +58 -0
- moai_adk/templates/.claude/skills/moai-project-template-optimizer/reference.md +123 -0
- moai_adk/templates/.claude/skills/moai-session-info/SKILL.md +314 -0
- moai_adk/templates/.claude/skills/moai-streaming-ui/SKILL.md +552 -0
- moai_adk/templates/.claude/skills/moai-tag-policy-validator/SKILL.md +570 -0
- moai_adk/templates/.git-hooks/pre-commit +66 -0
- moai_adk/templates/.git-hooks/pre-push +255 -0
- moai_adk/templates/.github/workflows/c-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/cpp-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/csharp-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/dart-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/go-tag-validation.yml +130 -0
- moai_adk/templates/.github/workflows/java-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/javascript-tag-validation.yml +135 -0
- moai_adk/templates/.github/workflows/kotlin-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/moai-gitflow.yml +166 -3
- moai_adk/templates/.github/workflows/moai-release-create.yml +100 -0
- moai_adk/templates/.github/workflows/moai-release-pipeline.yml +188 -0
- moai_adk/templates/.github/workflows/php-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/python-tag-validation.yml +118 -0
- moai_adk/templates/.github/workflows/release.yml +118 -0
- moai_adk/templates/.github/workflows/ruby-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/rust-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/shell-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/spec-issue-sync.yml +338 -0
- moai_adk/templates/.github/workflows/swift-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/tag-report.yml +269 -0
- moai_adk/templates/.github/workflows/tag-validation.yml +186 -0
- moai_adk/templates/.github/workflows/typescript-tag-validation.yml +154 -0
- moai_adk/templates/.mcp.json +31 -0
- moai_adk/templates/.moai/config.json +80 -7
- moai_adk/templates/CLAUDE.md +562 -546
- moai_adk/utils/banner.py +5 -5
- moai_adk/utils/common.py +294 -0
- moai_adk/utils/link_validator.py +235 -0
- moai_adk/utils/logger.py +8 -8
- moai_adk/utils/user_experience.py +451 -0
- moai_adk-0.20.1.dist-info/METADATA +233 -0
- moai_adk-0.20.1.dist-info/RECORD +404 -0
- moai_adk/templates/.claude/hooks/alfred/README.md +0 -230
- moai_adk/templates/.claude/hooks/alfred/alfred_hooks.py +0 -156
- moai_adk/templates/.claude/hooks/alfred/core/__init__.py +0 -85
- moai_adk/templates/.claude/hooks/alfred/handlers/notification.py +0 -25
- moai_adk/templates/.claude/hooks/alfred/handlers/session.py +0 -92
- moai_adk/templates/.claude/hooks/alfred/handlers/tool.py +0 -70
- moai_adk/templates/.claude/hooks/alfred/handlers/user.py +0 -41
- moai_adk/templates/.claude/output-styles/alfred/agentic-coding.md +0 -636
- moai_adk/templates/.claude/output-styles/alfred/moai-adk-learning.md +0 -692
- moai_adk/templates/.claude/output-styles/alfred/study-with-alfred.md +0 -470
- moai_adk/templates/.claude/skills/moai-alfred-debugger-pro/SKILL.md +0 -103
- moai_adk/templates/.claude/skills/moai-alfred-ears-authoring/SKILL.md +0 -103
- moai_adk/templates/.claude/skills/moai-alfred-git-workflow/SKILL.md +0 -95
- moai_adk/templates/.claude/skills/moai-alfred-performance-optimizer/SKILL.md +0 -105
- moai_adk/templates/.claude/skills/moai-alfred-refactoring-coach/SKILL.md +0 -97
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-validation/SKILL.md +0 -97
- moai_adk/templates/.claude/skills/moai-alfred-tag-scanning/SKILL.md +0 -90
- moai_adk/templates/.claude/skills/moai-alfred-trust-validation/SKILL.md +0 -99
- moai_adk/templates/.claude/skills/moai-alfred-tui-survey/SKILL.md +0 -87
- moai_adk/templates/.claude/skills/moai-alfred-tui-survey/examples.md +0 -62
- moai_adk/templates/.claude/skills/moai-claude-code/SKILL.md +0 -94
- moai_adk/templates/.claude/skills/moai-claude-code/examples.md +0 -513
- moai_adk/templates/.claude/skills/moai-claude-code/reference.md +0 -433
- moai_adk/templates/.claude/skills/moai-claude-code/templates/agent-full.md +0 -332
- moai_adk/templates/.claude/skills/moai-claude-code/templates/command-full.md +0 -384
- moai_adk/templates/.claude/skills/moai-claude-code/templates/plugin-full.json +0 -363
- moai_adk/templates/.claude/skills/moai-claude-code/templates/settings-full.json +0 -595
- moai_adk/templates/.claude/skills/moai-claude-code/templates/skill-full.md +0 -496
- moai_adk/templates/.claude/skills/moai-lang-clojure/SKILL.md +0 -100
- moai_adk/templates/.claude/skills/moai-lang-elixir/SKILL.md +0 -99
- moai_adk/templates/.claude/skills/moai-lang-haskell/SKILL.md +0 -100
- moai_adk/templates/.claude/skills/moai-lang-julia/SKILL.md +0 -98
- moai_adk/templates/.claude/skills/moai-lang-lua/SKILL.md +0 -98
- moai_adk/templates/.github/PULL_REQUEST_TEMPLATE.md +0 -69
- moai_adk/templates/.moai/memory/development-guide.md +0 -344
- moai_adk/templates/.moai/memory/gitflow-protection-policy.md +0 -220
- moai_adk/templates/.moai/memory/spec-metadata.md +0 -356
- moai_adk/templates/.moai/project/product.md +0 -161
- moai_adk/templates/.moai/project/structure.md +0 -156
- moai_adk/templates/.moai/project/tech.md +0 -227
- moai_adk/templates/__init__.py +0 -2
- moai_adk-0.4.5.dist-info/METADATA +0 -369
- moai_adk-0.4.5.dist-info/RECORD +0 -152
- {moai_adk-0.4.5.dist-info → moai_adk-0.20.1.dist-info}/WHEEL +0 -0
- {moai_adk-0.4.5.dist-info → moai_adk-0.20.1.dist-info}/entry_points.txt +0 -0
- {moai_adk-0.4.5.dist-info → moai_adk-0.20.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,897 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# @CODE:TAG-VALIDATOR-001 | @SPEC:DOC-TAG-001
|
|
3
|
+
"""Central TAG validation system for MoAI-ADK
|
|
4
|
+
|
|
5
|
+
This module provides a unified, extensible validation engine that:
|
|
6
|
+
- Validates TAG format, duplicates, orphans, and chain integrity
|
|
7
|
+
- Supports multiple validator types with priority ordering
|
|
8
|
+
- Generates reports in multiple formats (detailed, summary, JSON)
|
|
9
|
+
- Provides CLI integration via moai-adk validate-tags
|
|
10
|
+
|
|
11
|
+
Architecture:
|
|
12
|
+
CentralValidator (orchestrator)
|
|
13
|
+
├── DuplicateValidator (priority: 90)
|
|
14
|
+
├── OrphanValidator (priority: 50)
|
|
15
|
+
├── ChainValidator (priority: 30)
|
|
16
|
+
└── FormatValidator (priority: 100)
|
|
17
|
+
|
|
18
|
+
Usage:
|
|
19
|
+
config = ValidationConfig(strict_mode=True)
|
|
20
|
+
validator = CentralValidator(config=config)
|
|
21
|
+
result = validator.validate_directory("/path/to/project")
|
|
22
|
+
report = validator.create_report(result, format="json")
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
import json
|
|
26
|
+
import re
|
|
27
|
+
import time
|
|
28
|
+
from abc import ABC, abstractmethod
|
|
29
|
+
from dataclasses import dataclass, field
|
|
30
|
+
from datetime import datetime
|
|
31
|
+
from pathlib import Path
|
|
32
|
+
from typing import Any, Dict, List, Optional, Set, Tuple
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class ValidationConfig:
|
|
37
|
+
"""Configuration for validation behavior
|
|
38
|
+
|
|
39
|
+
Attributes:
|
|
40
|
+
strict_mode: Treat warnings as errors (block on warnings)
|
|
41
|
+
check_duplicates: Enable duplicate TAG detection
|
|
42
|
+
check_orphans: Enable orphan TAG detection (CODE without TEST)
|
|
43
|
+
check_chain_integrity: Enable SPEC→CODE→TEST→DOC chain validation
|
|
44
|
+
allowed_file_types: List of file extensions to validate (e.g., ["py", "js"])
|
|
45
|
+
ignore_patterns: List of glob patterns to ignore (e.g., [".git/*", "*.pyc"])
|
|
46
|
+
report_format: Default report format (detailed|summary|json)
|
|
47
|
+
"""
|
|
48
|
+
strict_mode: bool = False
|
|
49
|
+
check_duplicates: bool = True
|
|
50
|
+
check_orphans: bool = True
|
|
51
|
+
check_chain_integrity: bool = True
|
|
52
|
+
allowed_file_types: List[str] = field(default_factory=lambda: [
|
|
53
|
+
"py", "js", "ts", "jsx", "tsx", "md", "txt", "yml", "yaml", "json"
|
|
54
|
+
])
|
|
55
|
+
ignore_patterns: List[str] = field(default_factory=lambda: [
|
|
56
|
+
".git/*", "node_modules/*", "__pycache__/*", "*.pyc", ".venv/*", "venv/*"
|
|
57
|
+
])
|
|
58
|
+
report_format: str = "detailed"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclass
|
|
62
|
+
class ValidationIssue:
|
|
63
|
+
"""Validation issue with severity, type, and location
|
|
64
|
+
|
|
65
|
+
Attributes:
|
|
66
|
+
severity: Issue severity (error|warning|info)
|
|
67
|
+
type: Issue type (duplicate|orphan|chain|format)
|
|
68
|
+
tag: TAG string (e.g., "@CODE:TEST-001")
|
|
69
|
+
message: Human-readable issue description
|
|
70
|
+
locations: List of (file, line) tuples where issue occurs
|
|
71
|
+
suggestion: How to fix the issue
|
|
72
|
+
"""
|
|
73
|
+
severity: str # error|warning|info
|
|
74
|
+
type: str # duplicate|orphan|chain|format
|
|
75
|
+
tag: str
|
|
76
|
+
message: str
|
|
77
|
+
locations: List[Tuple[str, int]] = field(default_factory=list)
|
|
78
|
+
suggestion: str = ""
|
|
79
|
+
|
|
80
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
81
|
+
"""Convert to dictionary for JSON serialization"""
|
|
82
|
+
return {
|
|
83
|
+
"severity": self.severity,
|
|
84
|
+
"type": self.type,
|
|
85
|
+
"tag": self.tag,
|
|
86
|
+
"message": self.message,
|
|
87
|
+
"locations": [
|
|
88
|
+
{"file": f, "line": line} for f, line in self.locations
|
|
89
|
+
],
|
|
90
|
+
"suggestion": self.suggestion
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@dataclass
|
|
95
|
+
class ValidationStatistics:
|
|
96
|
+
"""Validation statistics
|
|
97
|
+
|
|
98
|
+
Attributes:
|
|
99
|
+
total_files_scanned: Number of files scanned
|
|
100
|
+
total_tags_found: Total TAG count across all files
|
|
101
|
+
total_issues: Total issues found (errors + warnings)
|
|
102
|
+
error_count: Number of errors
|
|
103
|
+
warning_count: Number of warnings
|
|
104
|
+
coverage_percentage: Percentage of SPEC tags with CODE implementation
|
|
105
|
+
"""
|
|
106
|
+
total_files_scanned: int
|
|
107
|
+
total_tags_found: int
|
|
108
|
+
total_issues: int
|
|
109
|
+
error_count: int
|
|
110
|
+
warning_count: int
|
|
111
|
+
coverage_percentage: float
|
|
112
|
+
|
|
113
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
114
|
+
"""Convert to dictionary for JSON serialization"""
|
|
115
|
+
return {
|
|
116
|
+
"total_files_scanned": self.total_files_scanned,
|
|
117
|
+
"total_tags_found": self.total_tags_found,
|
|
118
|
+
"total_issues": self.total_issues,
|
|
119
|
+
"error_count": self.error_count,
|
|
120
|
+
"warning_count": self.warning_count,
|
|
121
|
+
"coverage_percentage": self.coverage_percentage
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@dataclass
|
|
126
|
+
class CentralValidationResult:
|
|
127
|
+
"""Complete validation result from CentralValidator
|
|
128
|
+
|
|
129
|
+
Attributes:
|
|
130
|
+
is_valid: Overall validation status (False if errors, or warnings in strict mode)
|
|
131
|
+
issues: All issues (errors + warnings combined)
|
|
132
|
+
errors: Error-level issues only
|
|
133
|
+
warnings: Warning-level issues only
|
|
134
|
+
statistics: Validation statistics
|
|
135
|
+
timestamp: When validation was performed
|
|
136
|
+
execution_time_ms: Validation execution time in milliseconds
|
|
137
|
+
"""
|
|
138
|
+
is_valid: bool
|
|
139
|
+
issues: List[ValidationIssue]
|
|
140
|
+
errors: List[ValidationIssue]
|
|
141
|
+
warnings: List[ValidationIssue]
|
|
142
|
+
statistics: ValidationStatistics
|
|
143
|
+
timestamp: datetime
|
|
144
|
+
execution_time_ms: float
|
|
145
|
+
|
|
146
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
147
|
+
"""Convert to dictionary for JSON serialization"""
|
|
148
|
+
return {
|
|
149
|
+
"is_valid": self.is_valid,
|
|
150
|
+
"issues": [issue.to_dict() for issue in self.issues],
|
|
151
|
+
"errors": [error.to_dict() for error in self.errors],
|
|
152
|
+
"warnings": [warning.to_dict() for warning in self.warnings],
|
|
153
|
+
"statistics": self.statistics.to_dict(),
|
|
154
|
+
"timestamp": self.timestamp.isoformat(),
|
|
155
|
+
"execution_time_ms": self.execution_time_ms
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class TagValidator(ABC):
|
|
160
|
+
"""Abstract base class for all validators
|
|
161
|
+
|
|
162
|
+
All validators must extend this class and implement:
|
|
163
|
+
- validate(): Perform validation and return issues
|
|
164
|
+
- get_name(): Return validator name
|
|
165
|
+
- get_priority(): Return priority (higher = runs first)
|
|
166
|
+
"""
|
|
167
|
+
|
|
168
|
+
# Default TAG pattern: @(SPEC|CODE|TEST|DOC):DOMAIN-TYPE-NNN
|
|
169
|
+
TAG_PATTERN = re.compile(r"@(SPEC|CODE|TEST|DOC):([A-Z]+(?:-[A-Z]+)*-\d{3})")
|
|
170
|
+
|
|
171
|
+
@abstractmethod
|
|
172
|
+
def validate(self, files: List[str]) -> List[ValidationIssue]:
|
|
173
|
+
"""Validate files and return list of issues
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
files: List of file paths to validate
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
List of ValidationIssue objects
|
|
180
|
+
"""
|
|
181
|
+
pass
|
|
182
|
+
|
|
183
|
+
@abstractmethod
|
|
184
|
+
def get_name(self) -> str:
|
|
185
|
+
"""Return validator name"""
|
|
186
|
+
pass
|
|
187
|
+
|
|
188
|
+
@abstractmethod
|
|
189
|
+
def get_priority(self) -> int:
|
|
190
|
+
"""Return validator priority (higher = runs first)
|
|
191
|
+
|
|
192
|
+
Priority guidelines:
|
|
193
|
+
- 100: Format validation (must pass before other checks)
|
|
194
|
+
- 90: Duplicate detection (errors that block)
|
|
195
|
+
- 50: Orphan detection (warnings)
|
|
196
|
+
- 30: Chain integrity (warnings)
|
|
197
|
+
"""
|
|
198
|
+
pass
|
|
199
|
+
|
|
200
|
+
def extract_tags_from_file(self, filepath: str) -> List[Tuple[str, str, int]]:
|
|
201
|
+
"""Extract TAGs from a file
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
filepath: Path to file to scan
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
List of (tag_type, domain, line_number) tuples
|
|
208
|
+
Example: [("CODE", "USER-REG-001", 10), ("TEST", "USER-REG-001", 25)]
|
|
209
|
+
"""
|
|
210
|
+
try:
|
|
211
|
+
path = Path(filepath)
|
|
212
|
+
if not path.exists() or not path.is_file():
|
|
213
|
+
return []
|
|
214
|
+
|
|
215
|
+
content = path.read_text(encoding="utf-8", errors="ignore")
|
|
216
|
+
lines = content.splitlines()
|
|
217
|
+
|
|
218
|
+
tags = []
|
|
219
|
+
for line_num, line in enumerate(lines, start=1):
|
|
220
|
+
matches = self.TAG_PATTERN.findall(line)
|
|
221
|
+
for tag_type, domain in matches:
|
|
222
|
+
tags.append((tag_type, domain, line_num))
|
|
223
|
+
|
|
224
|
+
return tags
|
|
225
|
+
|
|
226
|
+
except Exception:
|
|
227
|
+
return []
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
class DuplicateValidator(TagValidator):
|
|
231
|
+
"""Validator for duplicate TAG detection
|
|
232
|
+
|
|
233
|
+
Detects duplicate TAGs within same file or across multiple files.
|
|
234
|
+
Returns error-level issues for all duplicates found.
|
|
235
|
+
|
|
236
|
+
Priority: 90 (high - errors that must be fixed)
|
|
237
|
+
"""
|
|
238
|
+
|
|
239
|
+
def validate(self, files: List[str]) -> List[ValidationIssue]:
|
|
240
|
+
"""Detect duplicate TAGs across all files
|
|
241
|
+
|
|
242
|
+
Args:
|
|
243
|
+
files: List of file paths to validate
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
List of ValidationIssue objects for duplicates
|
|
247
|
+
"""
|
|
248
|
+
issues: List[ValidationIssue] = []
|
|
249
|
+
tag_locations: Dict[str, List[Tuple[str, int]]] = {}
|
|
250
|
+
|
|
251
|
+
# Collect all TAGs and their locations
|
|
252
|
+
for filepath in files:
|
|
253
|
+
tags = self.extract_tags_from_file(filepath)
|
|
254
|
+
for tag_type, domain, line_num in tags:
|
|
255
|
+
full_tag = f"@{tag_type}:{domain}"
|
|
256
|
+
if full_tag not in tag_locations:
|
|
257
|
+
tag_locations[full_tag] = []
|
|
258
|
+
tag_locations[full_tag].append((filepath, line_num))
|
|
259
|
+
|
|
260
|
+
# Find duplicates (tags with more than one location)
|
|
261
|
+
for tag, locations in tag_locations.items():
|
|
262
|
+
if len(locations) > 1:
|
|
263
|
+
issues.append(ValidationIssue(
|
|
264
|
+
severity="error",
|
|
265
|
+
type="duplicate",
|
|
266
|
+
tag=tag,
|
|
267
|
+
message=f"Duplicate TAG found in {len(locations)} locations",
|
|
268
|
+
locations=locations,
|
|
269
|
+
suggestion="Remove duplicate TAG declarations. Each TAG must be unique across the codebase."
|
|
270
|
+
))
|
|
271
|
+
|
|
272
|
+
return issues
|
|
273
|
+
|
|
274
|
+
def get_name(self) -> str:
|
|
275
|
+
"""Return validator name"""
|
|
276
|
+
return "DuplicateValidator"
|
|
277
|
+
|
|
278
|
+
def get_priority(self) -> int:
|
|
279
|
+
"""Return high priority (errors block early)"""
|
|
280
|
+
return 90
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
class OrphanValidator(TagValidator):
|
|
284
|
+
"""Validator for orphan TAG detection
|
|
285
|
+
|
|
286
|
+
Detects orphan TAGs:
|
|
287
|
+
- @CODE without corresponding @TEST
|
|
288
|
+
- @TEST without corresponding @CODE
|
|
289
|
+
- @SPEC without implementation (@CODE)
|
|
290
|
+
|
|
291
|
+
Returns warning-level issues for all orphans found.
|
|
292
|
+
|
|
293
|
+
Priority: 50 (medium - warnings that should be addressed)
|
|
294
|
+
"""
|
|
295
|
+
|
|
296
|
+
def validate(self, files: List[str]) -> List[ValidationIssue]:
|
|
297
|
+
"""Detect orphan TAGs across all files
|
|
298
|
+
|
|
299
|
+
Args:
|
|
300
|
+
files: List of file paths to validate
|
|
301
|
+
|
|
302
|
+
Returns:
|
|
303
|
+
List of ValidationIssue objects for orphans
|
|
304
|
+
"""
|
|
305
|
+
issues: List[ValidationIssue] = []
|
|
306
|
+
|
|
307
|
+
# Collect all TAGs by type and domain
|
|
308
|
+
tags_by_type: Dict[str, Dict[str, List[Tuple[str, int]]]] = {
|
|
309
|
+
"SPEC": {},
|
|
310
|
+
"CODE": {},
|
|
311
|
+
"TEST": {},
|
|
312
|
+
"DOC": {}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
for filepath in files:
|
|
316
|
+
tags = self.extract_tags_from_file(filepath)
|
|
317
|
+
for tag_type, domain, line_num in tags:
|
|
318
|
+
if domain not in tags_by_type[tag_type]:
|
|
319
|
+
tags_by_type[tag_type][domain] = []
|
|
320
|
+
tags_by_type[tag_type][domain].append((filepath, line_num))
|
|
321
|
+
|
|
322
|
+
# Check for orphans: CODE without TEST
|
|
323
|
+
for domain, locations in tags_by_type["CODE"].items():
|
|
324
|
+
if domain not in tags_by_type["TEST"]:
|
|
325
|
+
for filepath, line_num in locations:
|
|
326
|
+
issues.append(ValidationIssue(
|
|
327
|
+
severity="warning",
|
|
328
|
+
type="orphan",
|
|
329
|
+
tag=f"@CODE:{domain}",
|
|
330
|
+
message="CODE TAG without corresponding TEST",
|
|
331
|
+
locations=[(filepath, line_num)],
|
|
332
|
+
suggestion=f"Add @TEST:{domain} to test this code implementation"
|
|
333
|
+
))
|
|
334
|
+
|
|
335
|
+
# Check for orphans: TEST without CODE
|
|
336
|
+
for domain, locations in tags_by_type["TEST"].items():
|
|
337
|
+
if domain not in tags_by_type["CODE"]:
|
|
338
|
+
for filepath, line_num in locations:
|
|
339
|
+
issues.append(ValidationIssue(
|
|
340
|
+
severity="warning",
|
|
341
|
+
type="orphan",
|
|
342
|
+
tag=f"@TEST:{domain}",
|
|
343
|
+
message="TEST TAG without corresponding CODE",
|
|
344
|
+
locations=[(filepath, line_num)],
|
|
345
|
+
suggestion=f"Add @CODE:{domain} for the implementation being tested"
|
|
346
|
+
))
|
|
347
|
+
|
|
348
|
+
return issues
|
|
349
|
+
|
|
350
|
+
def get_name(self) -> str:
|
|
351
|
+
"""Return validator name"""
|
|
352
|
+
return "OrphanValidator"
|
|
353
|
+
|
|
354
|
+
def get_priority(self) -> int:
|
|
355
|
+
"""Return medium priority"""
|
|
356
|
+
return 50
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
class ChainValidator(TagValidator):
|
|
360
|
+
"""Validator for TAG chain integrity (NEW in Component 3)
|
|
361
|
+
|
|
362
|
+
Validates complete SPEC→CODE→TEST→DOC chain:
|
|
363
|
+
- @SPEC should have corresponding @CODE (implementation)
|
|
364
|
+
- @CODE should have corresponding @TEST (test coverage)
|
|
365
|
+
- Complete chain optionally includes @DOC (documentation)
|
|
366
|
+
|
|
367
|
+
Returns warning-level issues for incomplete chains.
|
|
368
|
+
|
|
369
|
+
Priority: 30 (low - runs after duplicates and orphans)
|
|
370
|
+
"""
|
|
371
|
+
|
|
372
|
+
def validate(self, files: List[str]) -> List[ValidationIssue]:
|
|
373
|
+
"""Validate TAG chain integrity
|
|
374
|
+
|
|
375
|
+
Args:
|
|
376
|
+
files: List of file paths to validate
|
|
377
|
+
|
|
378
|
+
Returns:
|
|
379
|
+
List of ValidationIssue objects for chain problems
|
|
380
|
+
"""
|
|
381
|
+
issues: List[ValidationIssue] = []
|
|
382
|
+
|
|
383
|
+
# Collect all TAGs by type
|
|
384
|
+
tags_by_type: Dict[str, Set[str]] = {
|
|
385
|
+
"SPEC": set(),
|
|
386
|
+
"CODE": set(),
|
|
387
|
+
"TEST": set(),
|
|
388
|
+
"DOC": set()
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
for filepath in files:
|
|
392
|
+
tags = self.extract_tags_from_file(filepath)
|
|
393
|
+
for tag_type, domain, _ in tags:
|
|
394
|
+
tags_by_type[tag_type].add(domain)
|
|
395
|
+
|
|
396
|
+
# Check SPEC→CODE chain
|
|
397
|
+
for spec_domain in tags_by_type["SPEC"]:
|
|
398
|
+
if spec_domain not in tags_by_type["CODE"]:
|
|
399
|
+
issues.append(ValidationIssue(
|
|
400
|
+
severity="warning",
|
|
401
|
+
type="chain",
|
|
402
|
+
tag=f"@SPEC:{spec_domain}",
|
|
403
|
+
message="SPEC without CODE implementation",
|
|
404
|
+
locations=[],
|
|
405
|
+
suggestion=f"Implement @CODE:{spec_domain} for this specification"
|
|
406
|
+
))
|
|
407
|
+
|
|
408
|
+
# Check CODE→SPEC chain (reverse: CODE should have SPEC)
|
|
409
|
+
for code_domain in tags_by_type["CODE"]:
|
|
410
|
+
if code_domain not in tags_by_type["SPEC"]:
|
|
411
|
+
issues.append(ValidationIssue(
|
|
412
|
+
severity="warning",
|
|
413
|
+
type="chain",
|
|
414
|
+
tag=f"@CODE:{code_domain}",
|
|
415
|
+
message="CODE implementation without SPEC",
|
|
416
|
+
locations=[],
|
|
417
|
+
suggestion=f"Add @SPEC:{code_domain} to document the specification"
|
|
418
|
+
))
|
|
419
|
+
|
|
420
|
+
# Check TEST→SPEC chain (reverse: TEST should have SPEC)
|
|
421
|
+
for test_domain in tags_by_type["TEST"]:
|
|
422
|
+
if test_domain not in tags_by_type["SPEC"]:
|
|
423
|
+
issues.append(ValidationIssue(
|
|
424
|
+
severity="warning",
|
|
425
|
+
type="chain",
|
|
426
|
+
tag=f"@TEST:{test_domain}",
|
|
427
|
+
message="TEST without SPEC",
|
|
428
|
+
locations=[],
|
|
429
|
+
suggestion=f"Add @SPEC:{test_domain} to document what is being tested"
|
|
430
|
+
))
|
|
431
|
+
|
|
432
|
+
# Check SPEC+CODE without TEST chain
|
|
433
|
+
spec_with_code = tags_by_type["SPEC"] & tags_by_type["CODE"]
|
|
434
|
+
for domain in spec_with_code:
|
|
435
|
+
if domain not in tags_by_type["TEST"]:
|
|
436
|
+
issues.append(ValidationIssue(
|
|
437
|
+
severity="warning",
|
|
438
|
+
type="chain",
|
|
439
|
+
tag=f"@CODE:{domain}",
|
|
440
|
+
message="CODE implementation without TEST",
|
|
441
|
+
locations=[],
|
|
442
|
+
suggestion=f"Add @TEST:{domain} to test this implementation"
|
|
443
|
+
))
|
|
444
|
+
|
|
445
|
+
# Check for documentation (info level - optional)
|
|
446
|
+
complete_implementations = tags_by_type["SPEC"] & tags_by_type["CODE"] & tags_by_type["TEST"]
|
|
447
|
+
for domain in complete_implementations:
|
|
448
|
+
if domain not in tags_by_type["DOC"]:
|
|
449
|
+
issues.append(ValidationIssue(
|
|
450
|
+
severity="info",
|
|
451
|
+
type="chain",
|
|
452
|
+
tag=f"@SPEC:{domain}",
|
|
453
|
+
message="Complete implementation without documentation",
|
|
454
|
+
locations=[],
|
|
455
|
+
suggestion=f"Consider adding @DOC:{domain} to document this feature"
|
|
456
|
+
))
|
|
457
|
+
|
|
458
|
+
return issues
|
|
459
|
+
|
|
460
|
+
def get_name(self) -> str:
|
|
461
|
+
"""Return validator name"""
|
|
462
|
+
return "ChainValidator"
|
|
463
|
+
|
|
464
|
+
def get_priority(self) -> int:
|
|
465
|
+
"""Return low priority (runs after other checks)"""
|
|
466
|
+
return 30
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
class FormatValidator(TagValidator):
|
|
470
|
+
"""Validator for TAG format validation
|
|
471
|
+
|
|
472
|
+
Validates TAG format: @PREFIX:DOMAIN-TYPE-NNN
|
|
473
|
+
Returns error-level issues for invalid formats.
|
|
474
|
+
|
|
475
|
+
Priority: 100 (highest - format must be valid before other checks)
|
|
476
|
+
"""
|
|
477
|
+
|
|
478
|
+
def validate(self, files: List[str]) -> List[ValidationIssue]:
|
|
479
|
+
"""Validate TAG format in all files
|
|
480
|
+
|
|
481
|
+
Args:
|
|
482
|
+
files: List of file paths to validate
|
|
483
|
+
|
|
484
|
+
Returns:
|
|
485
|
+
List of ValidationIssue objects for format problems
|
|
486
|
+
"""
|
|
487
|
+
# Format validation is implicitly done by TAG_PATTERN regex
|
|
488
|
+
# Invalid formats won't be extracted, so no issues to report here
|
|
489
|
+
# This validator is a placeholder for future format-specific checks
|
|
490
|
+
return []
|
|
491
|
+
|
|
492
|
+
def get_name(self) -> str:
|
|
493
|
+
"""Return validator name"""
|
|
494
|
+
return "FormatValidator"
|
|
495
|
+
|
|
496
|
+
def get_priority(self) -> int:
|
|
497
|
+
"""Return highest priority"""
|
|
498
|
+
return 100
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
class CentralValidator:
|
|
502
|
+
"""Central validation orchestrator
|
|
503
|
+
|
|
504
|
+
Coordinates multiple validators to provide unified validation:
|
|
505
|
+
1. Registers default validators (Duplicate, Orphan, Chain, Format)
|
|
506
|
+
2. Runs validators in priority order (high to low)
|
|
507
|
+
3. Collects all issues and generates statistics
|
|
508
|
+
4. Creates formatted reports in multiple formats
|
|
509
|
+
|
|
510
|
+
Usage:
|
|
511
|
+
config = ValidationConfig(strict_mode=True)
|
|
512
|
+
validator = CentralValidator(config=config)
|
|
513
|
+
result = validator.validate_directory("/path/to/project")
|
|
514
|
+
print(validator.create_report(result, format="summary"))
|
|
515
|
+
|
|
516
|
+
Args:
|
|
517
|
+
config: ValidationConfig object (default: ValidationConfig())
|
|
518
|
+
"""
|
|
519
|
+
|
|
520
|
+
def __init__(self, config: Optional[ValidationConfig] = None):
|
|
521
|
+
"""Initialize CentralValidator with configuration
|
|
522
|
+
|
|
523
|
+
Args:
|
|
524
|
+
config: ValidationConfig object (default: ValidationConfig())
|
|
525
|
+
"""
|
|
526
|
+
self.config = config or ValidationConfig()
|
|
527
|
+
self.validators: List[TagValidator] = []
|
|
528
|
+
|
|
529
|
+
# Register default validators based on configuration
|
|
530
|
+
if self.config.check_duplicates:
|
|
531
|
+
self.register_validator(DuplicateValidator())
|
|
532
|
+
|
|
533
|
+
if self.config.check_orphans:
|
|
534
|
+
self.register_validator(OrphanValidator())
|
|
535
|
+
|
|
536
|
+
if self.config.check_chain_integrity:
|
|
537
|
+
self.register_validator(ChainValidator())
|
|
538
|
+
|
|
539
|
+
# Always register format validator
|
|
540
|
+
self.register_validator(FormatValidator())
|
|
541
|
+
|
|
542
|
+
# Sort validators by priority (high to low)
|
|
543
|
+
self.validators.sort(key=lambda v: v.get_priority(), reverse=True)
|
|
544
|
+
|
|
545
|
+
def register_validator(self, validator: TagValidator) -> None:
|
|
546
|
+
"""Register a custom validator
|
|
547
|
+
|
|
548
|
+
Args:
|
|
549
|
+
validator: TagValidator instance to register
|
|
550
|
+
"""
|
|
551
|
+
self.validators.append(validator)
|
|
552
|
+
# Re-sort by priority
|
|
553
|
+
self.validators.sort(key=lambda v: v.get_priority(), reverse=True)
|
|
554
|
+
|
|
555
|
+
def get_validators(self) -> List[TagValidator]:
|
|
556
|
+
"""Get list of registered validators
|
|
557
|
+
|
|
558
|
+
Returns:
|
|
559
|
+
List of TagValidator instances sorted by priority
|
|
560
|
+
"""
|
|
561
|
+
return self.validators.copy()
|
|
562
|
+
|
|
563
|
+
def _should_scan_file(self, filepath: str) -> bool:
|
|
564
|
+
"""Check if file should be scanned based on configuration
|
|
565
|
+
|
|
566
|
+
Args:
|
|
567
|
+
filepath: Path to file
|
|
568
|
+
|
|
569
|
+
Returns:
|
|
570
|
+
True if file should be scanned
|
|
571
|
+
"""
|
|
572
|
+
path = Path(filepath)
|
|
573
|
+
|
|
574
|
+
# Check file extension
|
|
575
|
+
if self.config.allowed_file_types:
|
|
576
|
+
suffix = path.suffix.lstrip(".")
|
|
577
|
+
if suffix and suffix not in self.config.allowed_file_types:
|
|
578
|
+
return False
|
|
579
|
+
|
|
580
|
+
# Check ignore patterns
|
|
581
|
+
for pattern in self.config.ignore_patterns:
|
|
582
|
+
# Simple pattern matching (can be enhanced with fnmatch)
|
|
583
|
+
pattern_clean = pattern.replace("/*", "").replace("*", "")
|
|
584
|
+
if pattern_clean in str(path):
|
|
585
|
+
return False
|
|
586
|
+
|
|
587
|
+
return True
|
|
588
|
+
|
|
589
|
+
def _collect_files_from_directory(self, directory: str) -> List[str]:
|
|
590
|
+
"""Collect all files from directory recursively
|
|
591
|
+
|
|
592
|
+
Args:
|
|
593
|
+
directory: Directory path to scan
|
|
594
|
+
|
|
595
|
+
Returns:
|
|
596
|
+
List of file paths that should be scanned
|
|
597
|
+
"""
|
|
598
|
+
dir_path = Path(directory)
|
|
599
|
+
if not dir_path.exists() or not dir_path.is_dir():
|
|
600
|
+
return []
|
|
601
|
+
|
|
602
|
+
files = []
|
|
603
|
+
for path in dir_path.rglob("*"):
|
|
604
|
+
if path.is_file() and self._should_scan_file(str(path)):
|
|
605
|
+
files.append(str(path))
|
|
606
|
+
|
|
607
|
+
return files
|
|
608
|
+
|
|
609
|
+
def validate_files(self, files: List[str]) -> CentralValidationResult:
|
|
610
|
+
"""Validate list of files
|
|
611
|
+
|
|
612
|
+
Main validation method that:
|
|
613
|
+
1. Runs all registered validators in priority order
|
|
614
|
+
2. Collects all issues (errors + warnings)
|
|
615
|
+
3. Generates statistics
|
|
616
|
+
4. Returns CentralValidationResult
|
|
617
|
+
|
|
618
|
+
Args:
|
|
619
|
+
files: List of file paths to validate
|
|
620
|
+
|
|
621
|
+
Returns:
|
|
622
|
+
CentralValidationResult with all issues and statistics
|
|
623
|
+
"""
|
|
624
|
+
start_time = time.time()
|
|
625
|
+
|
|
626
|
+
# Filter files based on configuration
|
|
627
|
+
files_to_scan = [f for f in files if self._should_scan_file(f)]
|
|
628
|
+
|
|
629
|
+
# Run all validators
|
|
630
|
+
all_issues: List[ValidationIssue] = []
|
|
631
|
+
for validator in self.validators:
|
|
632
|
+
issues = validator.validate(files_to_scan)
|
|
633
|
+
all_issues.extend(issues)
|
|
634
|
+
|
|
635
|
+
# Separate errors and warnings
|
|
636
|
+
errors = [issue for issue in all_issues if issue.severity == "error"]
|
|
637
|
+
warnings = [issue for issue in all_issues if issue.severity == "warning"]
|
|
638
|
+
|
|
639
|
+
# Calculate statistics
|
|
640
|
+
total_tags = 0
|
|
641
|
+
spec_tags = set()
|
|
642
|
+
code_tags = set()
|
|
643
|
+
|
|
644
|
+
for filepath in files_to_scan:
|
|
645
|
+
try:
|
|
646
|
+
path = Path(filepath)
|
|
647
|
+
if not path.exists():
|
|
648
|
+
continue
|
|
649
|
+
content = path.read_text(encoding="utf-8", errors="ignore")
|
|
650
|
+
matches = TagValidator.TAG_PATTERN.findall(content)
|
|
651
|
+
total_tags += len(matches)
|
|
652
|
+
|
|
653
|
+
for tag_type, domain in matches:
|
|
654
|
+
if tag_type == "SPEC":
|
|
655
|
+
spec_tags.add(domain)
|
|
656
|
+
elif tag_type == "CODE":
|
|
657
|
+
code_tags.add(domain)
|
|
658
|
+
except Exception:
|
|
659
|
+
continue
|
|
660
|
+
|
|
661
|
+
# Calculate coverage percentage (SPEC tags with CODE implementation)
|
|
662
|
+
if spec_tags:
|
|
663
|
+
implemented_specs = spec_tags & code_tags
|
|
664
|
+
coverage_percentage = (len(implemented_specs) / len(spec_tags)) * 100
|
|
665
|
+
else:
|
|
666
|
+
coverage_percentage = 0.0 if code_tags else 100.0
|
|
667
|
+
|
|
668
|
+
statistics = ValidationStatistics(
|
|
669
|
+
total_files_scanned=len(files_to_scan),
|
|
670
|
+
total_tags_found=total_tags,
|
|
671
|
+
total_issues=len(all_issues),
|
|
672
|
+
error_count=len(errors),
|
|
673
|
+
warning_count=len(warnings),
|
|
674
|
+
coverage_percentage=round(coverage_percentage, 2)
|
|
675
|
+
)
|
|
676
|
+
|
|
677
|
+
# Determine if validation passed
|
|
678
|
+
if self.config.strict_mode:
|
|
679
|
+
is_valid = len(errors) == 0 and len(warnings) == 0
|
|
680
|
+
else:
|
|
681
|
+
is_valid = len(errors) == 0
|
|
682
|
+
|
|
683
|
+
execution_time_ms = (time.time() - start_time) * 1000
|
|
684
|
+
|
|
685
|
+
return CentralValidationResult(
|
|
686
|
+
is_valid=is_valid,
|
|
687
|
+
issues=all_issues,
|
|
688
|
+
errors=errors,
|
|
689
|
+
warnings=warnings,
|
|
690
|
+
statistics=statistics,
|
|
691
|
+
timestamp=datetime.now(),
|
|
692
|
+
execution_time_ms=round(execution_time_ms, 2)
|
|
693
|
+
)
|
|
694
|
+
|
|
695
|
+
def validate_directory(self, directory: str, config: Optional[ValidationConfig] = None) -> CentralValidationResult:
|
|
696
|
+
"""Validate all files in a directory recursively
|
|
697
|
+
|
|
698
|
+
Args:
|
|
699
|
+
directory: Directory path to scan
|
|
700
|
+
config: Optional custom configuration (overrides instance config)
|
|
701
|
+
|
|
702
|
+
Returns:
|
|
703
|
+
CentralValidationResult
|
|
704
|
+
"""
|
|
705
|
+
# Temporarily override config if provided
|
|
706
|
+
original_config = self.config
|
|
707
|
+
if config:
|
|
708
|
+
self.config = config
|
|
709
|
+
|
|
710
|
+
# Collect files from directory
|
|
711
|
+
files = self._collect_files_from_directory(directory)
|
|
712
|
+
|
|
713
|
+
# Validate files
|
|
714
|
+
result = self.validate_files(files)
|
|
715
|
+
|
|
716
|
+
# Restore original config
|
|
717
|
+
self.config = original_config
|
|
718
|
+
|
|
719
|
+
return result
|
|
720
|
+
|
|
721
|
+
def create_report(self, result: CentralValidationResult, format: str = "detailed") -> str:
|
|
722
|
+
"""Create formatted validation report
|
|
723
|
+
|
|
724
|
+
Args:
|
|
725
|
+
result: CentralValidationResult from validation
|
|
726
|
+
format: Report format (detailed|summary|json)
|
|
727
|
+
|
|
728
|
+
Returns:
|
|
729
|
+
Formatted report string
|
|
730
|
+
"""
|
|
731
|
+
if format == "json":
|
|
732
|
+
return json.dumps(result.to_dict(), indent=2)
|
|
733
|
+
elif format == "summary":
|
|
734
|
+
return self._create_summary_report(result)
|
|
735
|
+
else: # detailed
|
|
736
|
+
return self._create_detailed_report(result)
|
|
737
|
+
|
|
738
|
+
def _create_summary_report(self, result: CentralValidationResult) -> str:
|
|
739
|
+
"""Create summary report
|
|
740
|
+
|
|
741
|
+
Args:
|
|
742
|
+
result: CentralValidationResult
|
|
743
|
+
|
|
744
|
+
Returns:
|
|
745
|
+
Summary report string
|
|
746
|
+
"""
|
|
747
|
+
lines = []
|
|
748
|
+
lines.append("=" * 60)
|
|
749
|
+
lines.append("TAG Validation Summary")
|
|
750
|
+
lines.append("=" * 60)
|
|
751
|
+
lines.append("")
|
|
752
|
+
|
|
753
|
+
# Status
|
|
754
|
+
if result.is_valid:
|
|
755
|
+
lines.append("✅ Validation PASSED")
|
|
756
|
+
else:
|
|
757
|
+
lines.append("❌ Validation FAILED")
|
|
758
|
+
lines.append("")
|
|
759
|
+
|
|
760
|
+
# Statistics
|
|
761
|
+
stats = result.statistics
|
|
762
|
+
lines.append(f"Files Scanned: {stats.total_files_scanned}")
|
|
763
|
+
lines.append(f"Tags Found: {stats.total_tags_found}")
|
|
764
|
+
lines.append(f"Total Issues: {stats.total_issues}")
|
|
765
|
+
lines.append(f" - Errors: {stats.error_count}")
|
|
766
|
+
lines.append(f" - Warnings: {stats.warning_count}")
|
|
767
|
+
lines.append(f"Coverage: {stats.coverage_percentage:.1f}%")
|
|
768
|
+
lines.append("")
|
|
769
|
+
|
|
770
|
+
# Execution time
|
|
771
|
+
lines.append(f"Execution Time: {result.execution_time_ms:.2f} ms")
|
|
772
|
+
lines.append("")
|
|
773
|
+
|
|
774
|
+
return "\n".join(lines)
|
|
775
|
+
|
|
776
|
+
def _create_detailed_report(self, result: CentralValidationResult) -> str:
|
|
777
|
+
"""Create detailed report with all issues
|
|
778
|
+
|
|
779
|
+
Args:
|
|
780
|
+
result: CentralValidationResult
|
|
781
|
+
|
|
782
|
+
Returns:
|
|
783
|
+
Detailed report string
|
|
784
|
+
"""
|
|
785
|
+
lines = []
|
|
786
|
+
lines.append("=" * 60)
|
|
787
|
+
lines.append("TAG Validation Report")
|
|
788
|
+
lines.append("=" * 60)
|
|
789
|
+
lines.append("")
|
|
790
|
+
|
|
791
|
+
# Status
|
|
792
|
+
if result.is_valid:
|
|
793
|
+
lines.append("✅ Validation PASSED")
|
|
794
|
+
else:
|
|
795
|
+
lines.append("❌ Validation FAILED")
|
|
796
|
+
lines.append("")
|
|
797
|
+
|
|
798
|
+
# Errors
|
|
799
|
+
if result.errors:
|
|
800
|
+
lines.append("ERRORS:")
|
|
801
|
+
lines.append("-" * 60)
|
|
802
|
+
for error in result.errors:
|
|
803
|
+
lines.append(f" {error.tag}")
|
|
804
|
+
lines.append(f" Type: {error.type}")
|
|
805
|
+
lines.append(f" Message: {error.message}")
|
|
806
|
+
if error.locations:
|
|
807
|
+
lines.append(" Locations:")
|
|
808
|
+
for filepath, line_num in error.locations:
|
|
809
|
+
lines.append(f" - {filepath}:{line_num}")
|
|
810
|
+
if error.suggestion:
|
|
811
|
+
lines.append(f" Suggestion: {error.suggestion}")
|
|
812
|
+
lines.append("")
|
|
813
|
+
|
|
814
|
+
# Warnings
|
|
815
|
+
if result.warnings:
|
|
816
|
+
lines.append("WARNINGS:")
|
|
817
|
+
lines.append("-" * 60)
|
|
818
|
+
for warning in result.warnings:
|
|
819
|
+
lines.append(f" {warning.tag}")
|
|
820
|
+
lines.append(f" Type: {warning.type}")
|
|
821
|
+
lines.append(f" Message: {warning.message}")
|
|
822
|
+
if warning.locations:
|
|
823
|
+
lines.append(" Locations:")
|
|
824
|
+
for filepath, line_num in warning.locations:
|
|
825
|
+
lines.append(f" - {filepath}:{line_num}")
|
|
826
|
+
if warning.suggestion:
|
|
827
|
+
lines.append(f" Suggestion: {warning.suggestion}")
|
|
828
|
+
lines.append("")
|
|
829
|
+
|
|
830
|
+
# Statistics
|
|
831
|
+
lines.append("STATISTICS:")
|
|
832
|
+
lines.append("-" * 60)
|
|
833
|
+
stats = result.statistics
|
|
834
|
+
lines.append(f"Files Scanned: {stats.total_files_scanned}")
|
|
835
|
+
lines.append(f"Tags Found: {stats.total_tags_found}")
|
|
836
|
+
lines.append(f"Total Issues: {stats.total_issues}")
|
|
837
|
+
lines.append(f" - Errors: {stats.error_count}")
|
|
838
|
+
lines.append(f" - Warnings: {stats.warning_count}")
|
|
839
|
+
lines.append(f"Spec→Code Coverage: {stats.coverage_percentage:.1f}%")
|
|
840
|
+
lines.append(f"Execution Time: {result.execution_time_ms:.2f} ms")
|
|
841
|
+
lines.append("")
|
|
842
|
+
|
|
843
|
+
return "\n".join(lines)
|
|
844
|
+
|
|
845
|
+
def export_for_reporting(self, result: CentralValidationResult) -> Dict[str, Any]:
|
|
846
|
+
"""Export validation result in format compatible with ReportGenerator
|
|
847
|
+
|
|
848
|
+
This method bridges Component 3 (Validation) with Component 4 (Reporting).
|
|
849
|
+
Exports validation results as structured data for integration with
|
|
850
|
+
automated reporting workflows.
|
|
851
|
+
|
|
852
|
+
Args:
|
|
853
|
+
result: CentralValidationResult from validation
|
|
854
|
+
|
|
855
|
+
Returns:
|
|
856
|
+
Dictionary with validation data formatted for reporting:
|
|
857
|
+
{
|
|
858
|
+
"timestamp": ISO timestamp,
|
|
859
|
+
"is_valid": bool,
|
|
860
|
+
"statistics": {
|
|
861
|
+
"total_files_scanned": int,
|
|
862
|
+
"total_tags_found": int,
|
|
863
|
+
"total_issues": int,
|
|
864
|
+
"error_count": int,
|
|
865
|
+
"warning_count": int,
|
|
866
|
+
"coverage_percentage": float
|
|
867
|
+
},
|
|
868
|
+
"issues_by_type": {
|
|
869
|
+
"duplicate": [...],
|
|
870
|
+
"orphan": [...],
|
|
871
|
+
"chain": [...],
|
|
872
|
+
"format": [...]
|
|
873
|
+
},
|
|
874
|
+
"execution_time_ms": float
|
|
875
|
+
}
|
|
876
|
+
"""
|
|
877
|
+
# Group issues by type
|
|
878
|
+
issues_by_type: Dict[str, List[Dict[str, Any]]] = {
|
|
879
|
+
"duplicate": [],
|
|
880
|
+
"orphan": [],
|
|
881
|
+
"chain": [],
|
|
882
|
+
"format": []
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
for issue in result.issues:
|
|
886
|
+
issues_by_type[issue.type].append(issue.to_dict())
|
|
887
|
+
|
|
888
|
+
# Build export data
|
|
889
|
+
export_data = {
|
|
890
|
+
"timestamp": result.timestamp.isoformat(),
|
|
891
|
+
"is_valid": result.is_valid,
|
|
892
|
+
"statistics": result.statistics.to_dict(),
|
|
893
|
+
"issues_by_type": issues_by_type,
|
|
894
|
+
"execution_time_ms": result.execution_time_ms
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
return export_data
|