moai-adk 0.15.0__py3-none-any.whl → 0.25.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of moai-adk might be problematic. Click here for more details.
- moai_adk/__init__.py +1 -2
- moai_adk/__main__.py +85 -2
- moai_adk/cli/__init__.py +0 -1
- moai_adk/cli/commands/__init__.py +0 -1
- moai_adk/cli/commands/analyze.py +127 -0
- moai_adk/cli/commands/backup.py +5 -3
- moai_adk/cli/commands/doctor.py +35 -11
- moai_adk/cli/commands/improve_user_experience.py +348 -0
- moai_adk/cli/commands/init.py +150 -23
- moai_adk/cli/commands/language.py +269 -0
- moai_adk/cli/commands/migrate.py +158 -0
- moai_adk/cli/commands/status.py +13 -12
- moai_adk/cli/commands/update.py +364 -60
- moai_adk/cli/commands/validate_links.py +118 -0
- moai_adk/cli/main.py +3 -2
- moai_adk/cli/prompts/init_prompts.py +79 -82
- moai_adk/core/__init__.py +0 -1
- moai_adk/core/analysis/__init__.py +9 -0
- moai_adk/core/analysis/session_analyzer.py +439 -0
- moai_adk/core/claude_integration.py +421 -0
- moai_adk/core/command_helpers.py +270 -0
- moai_adk/core/config/__init__.py +6 -0
- moai_adk/core/config/auto_spec_config.py +346 -0
- moai_adk/core/config/migration.py +133 -12
- moai_adk/core/context_manager.py +279 -0
- moai_adk/core/diagnostics/slash_commands.py +0 -1
- moai_adk/core/error_recovery_system.py +1289 -0
- moai_adk/core/git/__init__.py +0 -1
- moai_adk/core/git/branch.py +0 -1
- moai_adk/core/git/branch_manager.py +4 -4
- moai_adk/core/git/checkpoint.py +1 -5
- moai_adk/core/git/commit.py +0 -1
- moai_adk/core/git/event_detector.py +3 -5
- moai_adk/core/git/manager.py +0 -1
- moai_adk/core/hooks/post_tool_auto_spec_completion.py +925 -0
- moai_adk/core/integration/__init__.py +22 -0
- moai_adk/core/integration/engine.py +169 -0
- moai_adk/core/integration/integration_tester.py +225 -0
- moai_adk/core/integration/models.py +88 -0
- moai_adk/core/integration/utils.py +211 -0
- moai_adk/core/issue_creator.py +28 -18
- moai_adk/core/language_config.py +202 -0
- moai_adk/core/language_validator.py +556 -0
- moai_adk/core/mcp/setup.py +113 -0
- moai_adk/core/migration/__init__.py +18 -0
- moai_adk/core/migration/backup_manager.py +208 -0
- moai_adk/core/migration/file_migrator.py +218 -0
- moai_adk/core/migration/version_detector.py +143 -0
- moai_adk/core/migration/version_migrator.py +228 -0
- moai_adk/core/performance/__init__.py +6 -0
- moai_adk/core/performance/cache_system.py +318 -0
- moai_adk/core/performance/parallel_processor.py +116 -0
- moai_adk/core/project/__init__.py +0 -1
- moai_adk/core/project/backup_utils.py +2 -7
- moai_adk/core/project/checker.py +3 -3
- moai_adk/core/project/detector.py +20 -40
- moai_adk/core/project/initializer.py +42 -17
- moai_adk/core/project/phase_executor.py +415 -58
- moai_adk/core/project/validator.py +6 -25
- moai_adk/core/quality/__init__.py +1 -1
- moai_adk/core/quality/trust_checker.py +64 -110
- moai_adk/core/quality/validators/__init__.py +1 -1
- moai_adk/core/quality/validators/base_validator.py +1 -1
- moai_adk/core/rollback_manager.py +993 -0
- moai_adk/core/session_manager.py +667 -0
- moai_adk/core/spec/confidence_scoring.py +749 -0
- moai_adk/core/spec/ears_template_engine.py +1182 -0
- moai_adk/core/spec/quality_validator.py +721 -0
- moai_adk/core/spec_status_manager.py +488 -0
- moai_adk/core/template/__init__.py +0 -1
- moai_adk/core/template/backup.py +41 -1
- moai_adk/core/template/config.py +11 -12
- moai_adk/core/template/languages.py +0 -1
- moai_adk/core/template/merger.py +79 -22
- moai_adk/core/template/processor.py +614 -40
- moai_adk/core/template_engine.py +36 -27
- moai_adk/foundation/git/commit_templates.py +565 -0
- moai_adk/foundation/trust/trust_principles.py +725 -0
- moai_adk/foundation/trust/validation_checklist.py +1678 -0
- moai_adk/statusline/__init__.py +38 -0
- moai_adk/statusline/alfred_detector.py +107 -0
- moai_adk/statusline/config.py +364 -0
- moai_adk/statusline/enhanced_output_style_detector.py +364 -0
- moai_adk/statusline/git_collector.py +190 -0
- moai_adk/statusline/main.py +228 -0
- moai_adk/statusline/metrics_tracker.py +78 -0
- moai_adk/statusline/renderer.py +327 -0
- moai_adk/statusline/update_checker.py +135 -0
- moai_adk/statusline/version_reader.py +647 -0
- moai_adk/templates/.git-hooks/pre-commit +66 -0
- moai_adk/templates/.git-hooks/pre-push +116 -4
- moai_adk/templates/.github/workflows/moai-gitflow.yml +1 -7
- moai_adk/templates/.github/workflows/spec-issue-sync.yml +0 -1
- moai_adk/templates/.gitignore +44 -0
- moai_adk/templates/.mcp.json +22 -0
- moai_adk/templates/CLAUDE.md +450 -1071
- moai_adk/utils/__init__.py +0 -1
- moai_adk/utils/banner.py +0 -1
- moai_adk/utils/common.py +308 -0
- moai_adk/utils/link_validator.py +249 -0
- moai_adk/utils/logger.py +4 -9
- moai_adk/utils/safe_file_reader.py +210 -0
- moai_adk/utils/user_experience.py +531 -0
- moai_adk-0.25.4.dist-info/METADATA +2279 -0
- moai_adk-0.25.4.dist-info/RECORD +112 -0
- moai_adk/core/tags/__init__.py +0 -86
- moai_adk/core/tags/ci_validator.py +0 -463
- moai_adk/core/tags/cli.py +0 -283
- moai_adk/core/tags/generator.py +0 -109
- moai_adk/core/tags/inserter.py +0 -99
- moai_adk/core/tags/mapper.py +0 -126
- moai_adk/core/tags/parser.py +0 -76
- moai_adk/core/tags/pre_commit_validator.py +0 -393
- moai_adk/core/tags/reporter.py +0 -956
- moai_adk/core/tags/tags.py +0 -149
- moai_adk/core/tags/validator.py +0 -897
- moai_adk/templates/.claude/agents/alfred/backend-expert.md +0 -319
- moai_adk/templates/.claude/agents/alfred/cc-manager.md +0 -316
- moai_adk/templates/.claude/agents/alfred/debug-helper.md +0 -208
- moai_adk/templates/.claude/agents/alfred/devops-expert.md +0 -464
- moai_adk/templates/.claude/agents/alfred/doc-syncer.md +0 -214
- moai_adk/templates/.claude/agents/alfred/frontend-expert.md +0 -357
- moai_adk/templates/.claude/agents/alfred/git-manager.md +0 -406
- moai_adk/templates/.claude/agents/alfred/implementation-planner.md +0 -423
- moai_adk/templates/.claude/agents/alfred/project-manager.md +0 -312
- moai_adk/templates/.claude/agents/alfred/quality-gate.md +0 -343
- moai_adk/templates/.claude/agents/alfred/skill-factory.md +0 -865
- moai_adk/templates/.claude/agents/alfred/spec-builder.md +0 -392
- moai_adk/templates/.claude/agents/alfred/tag-agent.md +0 -361
- moai_adk/templates/.claude/agents/alfred/tdd-implementer.md +0 -428
- moai_adk/templates/.claude/agents/alfred/trust-checker.md +0 -375
- moai_adk/templates/.claude/agents/alfred/ui-ux-expert.md +0 -571
- moai_adk/templates/.claude/commands/alfred/0-project.md +0 -1525
- moai_adk/templates/.claude/commands/alfred/1-plan.md +0 -802
- moai_adk/templates/.claude/commands/alfred/2-run.md +0 -709
- moai_adk/templates/.claude/commands/alfred/3-sync.md +0 -1009
- moai_adk/templates/.claude/commands/alfred/9-feedback.md +0 -149
- moai_adk/templates/.claude/hooks/alfred/core/project.py +0 -748
- moai_adk/templates/.claude/hooks/alfred/core/timeout.py +0 -136
- moai_adk/templates/.claude/hooks/alfred/core/ttl_cache.py +0 -108
- moai_adk/templates/.claude/hooks/alfred/core/version_cache.py +0 -198
- moai_adk/templates/.claude/hooks/alfred/handlers/__init__.py +0 -29
- moai_adk/templates/.claude/hooks/alfred/post_tool__log_changes.py +0 -94
- moai_adk/templates/.claude/hooks/alfred/pre_tool__auto_checkpoint.py +0 -100
- moai_adk/templates/.claude/hooks/alfred/session_end__cleanup.py +0 -94
- moai_adk/templates/.claude/hooks/alfred/session_start__show_project_info.py +0 -94
- moai_adk/templates/.claude/hooks/alfred/shared/core/__init__.py +0 -170
- moai_adk/templates/.claude/hooks/alfred/shared/core/checkpoint.py +0 -271
- moai_adk/templates/.claude/hooks/alfred/shared/core/context.py +0 -67
- moai_adk/templates/.claude/hooks/alfred/shared/core/project.py +0 -749
- moai_adk/templates/.claude/hooks/alfred/shared/core/tags.py +0 -230
- moai_adk/templates/.claude/hooks/alfred/shared/core/version_cache.py +0 -198
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/__init__.py +0 -21
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/notification.py +0 -154
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/session.py +0 -174
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/tool.py +0 -87
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/user.py +0 -61
- moai_adk/templates/.claude/hooks/alfred/user_prompt__jit_load_docs.py +0 -112
- moai_adk/templates/.claude/hooks/alfred/utils/__init__.py +0 -1
- moai_adk/templates/.claude/hooks/alfred/utils/timeout.py +0 -161
- moai_adk/templates/.claude/settings.json +0 -144
- moai_adk/templates/.claude/skills/moai-alfred-agent-guide/SKILL.md +0 -70
- moai_adk/templates/.claude/skills/moai-alfred-agent-guide/examples.md +0 -62
- moai_adk/templates/.claude/skills/moai-alfred-agent-guide/reference.md +0 -242
- moai_adk/templates/.claude/skills/moai-alfred-config-schema/SKILL.md +0 -56
- moai_adk/templates/.claude/skills/moai-alfred-config-schema/examples.md +0 -28
- moai_adk/templates/.claude/skills/moai-alfred-config-schema/reference.md +0 -444
- moai_adk/templates/.claude/skills/moai-alfred-context-budget/SKILL.md +0 -62
- moai_adk/templates/.claude/skills/moai-alfred-context-budget/examples.md +0 -28
- moai_adk/templates/.claude/skills/moai-alfred-context-budget/reference.md +0 -405
- moai_adk/templates/.claude/skills/moai-alfred-dev-guide/SKILL.md +0 -51
- moai_adk/templates/.claude/skills/moai-alfred-dev-guide/examples.md +0 -355
- moai_adk/templates/.claude/skills/moai-alfred-dev-guide/reference.md +0 -239
- moai_adk/templates/.claude/skills/moai-alfred-ears-authoring/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-alfred-ears-authoring/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-alfred-ears-authoring/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/SKILL.md +0 -323
- moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/examples.md +0 -286
- moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/reference.md +0 -126
- moai_adk/templates/.claude/skills/moai-alfred-git-workflow/SKILL.md +0 -122
- moai_adk/templates/.claude/skills/moai-alfred-git-workflow/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-alfred-git-workflow/reference.md +0 -29
- moai_adk/templates/.claude/skills/moai-alfred-gitflow-policy/SKILL.md +0 -74
- moai_adk/templates/.claude/skills/moai-alfred-gitflow-policy/examples.md +0 -4
- moai_adk/templates/.claude/skills/moai-alfred-gitflow-policy/reference.md +0 -269
- moai_adk/templates/.claude/skills/moai-alfred-interactive-questions/SKILL.md +0 -237
- moai_adk/templates/.claude/skills/moai-alfred-interactive-questions/examples.md +0 -615
- moai_adk/templates/.claude/skills/moai-alfred-interactive-questions/reference.md +0 -653
- moai_adk/templates/.claude/skills/moai-alfred-issue-labels/SKILL.md +0 -19
- moai_adk/templates/.claude/skills/moai-alfred-issue-labels/examples.md +0 -4
- moai_adk/templates/.claude/skills/moai-alfred-issue-labels/reference.md +0 -150
- moai_adk/templates/.claude/skills/moai-alfred-language-detection/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-alfred-language-detection/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-alfred-language-detection/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-alfred-persona-roles/SKILL.md +0 -198
- moai_adk/templates/.claude/skills/moai-alfred-persona-roles/examples.md +0 -431
- moai_adk/templates/.claude/skills/moai-alfred-persona-roles/reference.md +0 -141
- moai_adk/templates/.claude/skills/moai-alfred-practices/SKILL.md +0 -89
- moai_adk/templates/.claude/skills/moai-alfred-practices/examples.md +0 -122
- moai_adk/templates/.claude/skills/moai-alfred-practices/reference.md +0 -369
- moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/SKILL.md +0 -508
- moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/examples.md +0 -481
- moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/reference.md +0 -100
- moai_adk/templates/.claude/skills/moai-alfred-reporting/SKILL.md +0 -273
- moai_adk/templates/.claude/skills/moai-alfred-rules/SKILL.md +0 -77
- moai_adk/templates/.claude/skills/moai-alfred-rules/examples.md +0 -265
- moai_adk/templates/.claude/skills/moai-alfred-rules/reference.md +0 -539
- moai_adk/templates/.claude/skills/moai-alfred-session-state/SKILL.md +0 -19
- moai_adk/templates/.claude/skills/moai-alfred-session-state/examples.md +0 -4
- moai_adk/templates/.claude/skills/moai-alfred-session-state/reference.md +0 -84
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/README.md +0 -137
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/SKILL.md +0 -219
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/examples/validate-spec.sh +0 -161
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/examples.md +0 -541
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/reference.md +0 -622
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-extended/SKILL.md +0 -115
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-extended/examples.md +0 -4
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-extended/reference.md +0 -348
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-validation/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-validation/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-validation/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-alfred-tag-scanning/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-alfred-tag-scanning/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-alfred-tag-scanning/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/SKILL.md +0 -19
- moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/examples.md +0 -4
- moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/reference.md +0 -211
- moai_adk/templates/.claude/skills/moai-alfred-trust-validation/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-alfred-trust-validation/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-alfred-trust-validation/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-alfred-workflow/SKILL.md +0 -288
- moai_adk/templates/.claude/skills/moai-cc-agents/SKILL.md +0 -269
- moai_adk/templates/.claude/skills/moai-cc-agents/templates/agent-template.md +0 -32
- moai_adk/templates/.claude/skills/moai-cc-claude-md/SKILL.md +0 -298
- moai_adk/templates/.claude/skills/moai-cc-claude-md/templates/CLAUDE-template.md +0 -26
- moai_adk/templates/.claude/skills/moai-cc-commands/SKILL.md +0 -307
- moai_adk/templates/.claude/skills/moai-cc-commands/templates/command-template.md +0 -21
- moai_adk/templates/.claude/skills/moai-cc-hooks/SKILL.md +0 -252
- moai_adk/templates/.claude/skills/moai-cc-hooks/scripts/pre-bash-check.sh +0 -19
- moai_adk/templates/.claude/skills/moai-cc-hooks/scripts/preserve-permissions.sh +0 -19
- moai_adk/templates/.claude/skills/moai-cc-hooks/scripts/validate-bash-command.py +0 -24
- moai_adk/templates/.claude/skills/moai-cc-mcp-plugins/SKILL.md +0 -199
- moai_adk/templates/.claude/skills/moai-cc-mcp-plugins/templates/settings-mcp-template.json +0 -39
- moai_adk/templates/.claude/skills/moai-cc-memory/SKILL.md +0 -316
- moai_adk/templates/.claude/skills/moai-cc-memory/templates/session-summary-template.md +0 -18
- moai_adk/templates/.claude/skills/moai-cc-settings/SKILL.md +0 -263
- moai_adk/templates/.claude/skills/moai-cc-settings/templates/settings-complete-template.json +0 -30
- moai_adk/templates/.claude/skills/moai-cc-skill-descriptions/SKILL.md +0 -19
- moai_adk/templates/.claude/skills/moai-cc-skill-descriptions/examples.md +0 -4
- moai_adk/templates/.claude/skills/moai-cc-skill-descriptions/reference.md +0 -218
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/CHECKLIST.md +0 -482
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/EXAMPLES.md +0 -278
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/INTERACTIVE-DISCOVERY.md +0 -524
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/METADATA.md +0 -477
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/PARALLEL-ANALYSIS-REPORT.md +0 -429
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/PYTHON-VERSION-MATRIX.md +0 -391
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/SKILL-FACTORY-WORKFLOW.md +0 -431
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/SKILL-UPDATE-ADVISOR.md +0 -577
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/SKILL.md +0 -271
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/STEP-BY-STEP-GUIDE.md +0 -466
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/STRUCTURE.md +0 -583
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/WEB-RESEARCH.md +0 -526
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/reference.md +0 -465
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/scripts/generate-structure.sh +0 -328
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/scripts/validate-skill.sh +0 -312
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/templates/SKILL_TEMPLATE.md +0 -245
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/templates/examples-template.md +0 -285
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/templates/reference-template.md +0 -278
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/templates/scripts-template.sh +0 -303
- moai_adk/templates/.claude/skills/moai-cc-skills/SKILL.md +0 -291
- moai_adk/templates/.claude/skills/moai-cc-skills/templates/SKILL-template.md +0 -15
- moai_adk/templates/.claude/skills/moai-design-systems/SKILL.md +0 -802
- moai_adk/templates/.claude/skills/moai-design-systems/examples.md +0 -1238
- moai_adk/templates/.claude/skills/moai-design-systems/reference.md +0 -673
- moai_adk/templates/.claude/skills/moai-domain-backend/SKILL.md +0 -290
- moai_adk/templates/.claude/skills/moai-domain-backend/examples.md +0 -1633
- moai_adk/templates/.claude/skills/moai-domain-backend/reference.md +0 -660
- moai_adk/templates/.claude/skills/moai-domain-cli-tool/SKILL.md +0 -123
- moai_adk/templates/.claude/skills/moai-domain-cli-tool/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-domain-cli-tool/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-domain-data-science/SKILL.md +0 -123
- moai_adk/templates/.claude/skills/moai-domain-data-science/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-domain-data-science/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-domain-database/SKILL.md +0 -123
- moai_adk/templates/.claude/skills/moai-domain-database/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-domain-database/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-domain-devops/SKILL.md +0 -124
- moai_adk/templates/.claude/skills/moai-domain-devops/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-domain-devops/reference.md +0 -31
- moai_adk/templates/.claude/skills/moai-domain-frontend/SKILL.md +0 -128
- moai_adk/templates/.claude/skills/moai-domain-frontend/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-domain-frontend/reference.md +0 -31
- moai_adk/templates/.claude/skills/moai-domain-ml/SKILL.md +0 -123
- moai_adk/templates/.claude/skills/moai-domain-ml/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-domain-ml/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-domain-mobile-app/SKILL.md +0 -123
- moai_adk/templates/.claude/skills/moai-domain-mobile-app/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-domain-mobile-app/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-domain-security/SKILL.md +0 -123
- moai_adk/templates/.claude/skills/moai-domain-security/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-domain-security/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-domain-web-api/SKILL.md +0 -123
- moai_adk/templates/.claude/skills/moai-domain-web-api/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-domain-web-api/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-essentials-debug/SKILL.md +0 -303
- moai_adk/templates/.claude/skills/moai-essentials-debug/examples.md +0 -1064
- moai_adk/templates/.claude/skills/moai-essentials-debug/reference.md +0 -1047
- moai_adk/templates/.claude/skills/moai-essentials-perf/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-essentials-perf/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-essentials-perf/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-essentials-refactor/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-essentials-refactor/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-essentials-refactor/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-essentials-review/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-essentials-review/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-essentials-review/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-foundation-ears/SKILL.md +0 -116
- moai_adk/templates/.claude/skills/moai-foundation-ears/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-foundation-ears/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-foundation-git/SKILL.md +0 -122
- moai_adk/templates/.claude/skills/moai-foundation-git/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-foundation-git/reference.md +0 -29
- moai_adk/templates/.claude/skills/moai-foundation-langs/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-foundation-langs/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-foundation-langs/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-foundation-specs/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-foundation-specs/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-foundation-specs/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-foundation-tags/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-foundation-tags/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-foundation-tags/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-foundation-trust/SKILL.md +0 -307
- moai_adk/templates/.claude/skills/moai-foundation-trust/examples.md +0 -0
- moai_adk/templates/.claude/skills/moai-foundation-trust/reference.md +0 -1099
- moai_adk/templates/.claude/skills/moai-lang-c/SKILL.md +0 -124
- moai_adk/templates/.claude/skills/moai-lang-c/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-c/reference.md +0 -31
- moai_adk/templates/.claude/skills/moai-lang-cpp/SKILL.md +0 -124
- moai_adk/templates/.claude/skills/moai-lang-cpp/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-cpp/reference.md +0 -31
- moai_adk/templates/.claude/skills/moai-lang-csharp/SKILL.md +0 -123
- moai_adk/templates/.claude/skills/moai-lang-csharp/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-csharp/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-lang-dart/SKILL.md +0 -123
- moai_adk/templates/.claude/skills/moai-lang-dart/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-dart/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-lang-go/SKILL.md +0 -127
- moai_adk/templates/.claude/skills/moai-lang-go/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-go/reference.md +0 -31
- moai_adk/templates/.claude/skills/moai-lang-java/SKILL.md +0 -126
- moai_adk/templates/.claude/skills/moai-lang-java/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-java/reference.md +0 -31
- moai_adk/templates/.claude/skills/moai-lang-javascript/SKILL.md +0 -125
- moai_adk/templates/.claude/skills/moai-lang-javascript/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-javascript/reference.md +0 -32
- moai_adk/templates/.claude/skills/moai-lang-kotlin/SKILL.md +0 -124
- moai_adk/templates/.claude/skills/moai-lang-kotlin/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-kotlin/reference.md +0 -31
- moai_adk/templates/.claude/skills/moai-lang-php/SKILL.md +0 -126
- moai_adk/templates/.claude/skills/moai-lang-php/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-php/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-lang-python/SKILL.md +0 -433
- moai_adk/templates/.claude/skills/moai-lang-python/examples.md +0 -624
- moai_adk/templates/.claude/skills/moai-lang-python/reference.md +0 -316
- moai_adk/templates/.claude/skills/moai-lang-r/SKILL.md +0 -123
- moai_adk/templates/.claude/skills/moai-lang-r/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-r/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-lang-ruby/SKILL.md +0 -124
- moai_adk/templates/.claude/skills/moai-lang-ruby/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-ruby/reference.md +0 -31
- moai_adk/templates/.claude/skills/moai-lang-rust/SKILL.md +0 -127
- moai_adk/templates/.claude/skills/moai-lang-rust/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-rust/reference.md +0 -31
- moai_adk/templates/.claude/skills/moai-lang-scala/SKILL.md +0 -125
- moai_adk/templates/.claude/skills/moai-lang-scala/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-scala/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-lang-shell/SKILL.md +0 -123
- moai_adk/templates/.claude/skills/moai-lang-shell/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-shell/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-lang-sql/SKILL.md +0 -124
- moai_adk/templates/.claude/skills/moai-lang-sql/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-sql/reference.md +0 -31
- moai_adk/templates/.claude/skills/moai-lang-swift/SKILL.md +0 -123
- moai_adk/templates/.claude/skills/moai-lang-swift/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-swift/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-lang-typescript/SKILL.md +0 -133
- moai_adk/templates/.claude/skills/moai-lang-typescript/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-typescript/reference.md +0 -34
- moai_adk/templates/.claude/skills/moai-project-documentation.md +0 -622
- moai_adk/templates/.github/workflows/c-tag-validation.yml +0 -11
- moai_adk/templates/.github/workflows/cpp-tag-validation.yml +0 -11
- moai_adk/templates/.github/workflows/csharp-tag-validation.yml +0 -11
- moai_adk/templates/.github/workflows/dart-tag-validation.yml +0 -11
- moai_adk/templates/.github/workflows/go-tag-validation.yml +0 -130
- moai_adk/templates/.github/workflows/java-tag-validation.yml +0 -11
- moai_adk/templates/.github/workflows/javascript-tag-validation.yml +0 -135
- moai_adk/templates/.github/workflows/kotlin-tag-validation.yml +0 -11
- moai_adk/templates/.github/workflows/php-tag-validation.yml +0 -11
- moai_adk/templates/.github/workflows/python-tag-validation.yml +0 -118
- moai_adk/templates/.github/workflows/release.yml +0 -118
- moai_adk/templates/.github/workflows/ruby-tag-validation.yml +0 -11
- moai_adk/templates/.github/workflows/rust-tag-validation.yml +0 -11
- moai_adk/templates/.github/workflows/shell-tag-validation.yml +0 -11
- moai_adk/templates/.github/workflows/swift-tag-validation.yml +0 -11
- moai_adk/templates/.github/workflows/tag-report.yml +0 -269
- moai_adk/templates/.github/workflows/tag-validation.yml +0 -186
- moai_adk/templates/.github/workflows/typescript-tag-validation.yml +0 -154
- moai_adk/templates/.moai/config.json +0 -115
- moai_adk/templates/workflows/go-tag-validation.yml +0 -30
- moai_adk/templates/workflows/javascript-tag-validation.yml +0 -41
- moai_adk/templates/workflows/python-tag-validation.yml +0 -42
- moai_adk/templates/workflows/typescript-tag-validation.yml +0 -31
- moai_adk-0.15.0.dist-info/METADATA +0 -3079
- moai_adk-0.15.0.dist-info/RECORD +0 -365
- {moai_adk-0.15.0.dist-info → moai_adk-0.25.4.dist-info}/WHEEL +0 -0
- {moai_adk-0.15.0.dist-info → moai_adk-0.25.4.dist-info}/entry_points.txt +0 -0
- {moai_adk-0.15.0.dist-info → moai_adk-0.25.4.dist-info}/licenses/LICENSE +0 -0
moai_adk/utils/__init__.py
CHANGED
moai_adk/utils/banner.py
CHANGED
moai_adk/utils/common.py
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Common Utilities
|
|
3
|
+
Common utility functions
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import asyncio
|
|
7
|
+
import json
|
|
8
|
+
import logging
|
|
9
|
+
import re
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Dict, List, Optional
|
|
14
|
+
from urllib.parse import urlparse
|
|
15
|
+
|
|
16
|
+
import aiohttp
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class HTTPResponse:
|
|
23
|
+
"""HTTP response data"""
|
|
24
|
+
|
|
25
|
+
status_code: int
|
|
26
|
+
url: str
|
|
27
|
+
load_time: float
|
|
28
|
+
success: bool
|
|
29
|
+
error_message: Optional[str] = None
|
|
30
|
+
timestamp: datetime = field(default_factory=datetime.now)
|
|
31
|
+
|
|
32
|
+
def __post_init__(self):
|
|
33
|
+
if self.timestamp is None:
|
|
34
|
+
self.timestamp = datetime.now()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class HTTPClient:
|
|
38
|
+
"""HTTP client utility"""
|
|
39
|
+
|
|
40
|
+
def __init__(self, max_concurrent: int = 5, timeout: int = 10):
|
|
41
|
+
self.max_concurrent = max_concurrent
|
|
42
|
+
self.timeout = timeout
|
|
43
|
+
self.session: Optional[aiohttp.ClientSession] = None
|
|
44
|
+
|
|
45
|
+
async def __aenter__(self):
|
|
46
|
+
"""Async context manager entry"""
|
|
47
|
+
connector = aiohttp.TCPConnector(limit=self.max_concurrent)
|
|
48
|
+
timeout = aiohttp.ClientTimeout(total=self.timeout)
|
|
49
|
+
self.session = aiohttp.ClientSession(
|
|
50
|
+
connector=connector,
|
|
51
|
+
timeout=timeout,
|
|
52
|
+
headers={
|
|
53
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
|
54
|
+
},
|
|
55
|
+
)
|
|
56
|
+
return self
|
|
57
|
+
|
|
58
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
59
|
+
"""Async context manager exit"""
|
|
60
|
+
if self.session:
|
|
61
|
+
await self.session.close()
|
|
62
|
+
|
|
63
|
+
async def fetch_url(self, url: str) -> HTTPResponse:
|
|
64
|
+
"""Fetch single URL"""
|
|
65
|
+
try:
|
|
66
|
+
if self.session is None:
|
|
67
|
+
return HTTPResponse(
|
|
68
|
+
status_code=0,
|
|
69
|
+
url=url,
|
|
70
|
+
load_time=0,
|
|
71
|
+
success=False,
|
|
72
|
+
error_message="Session not initialized",
|
|
73
|
+
)
|
|
74
|
+
start_time = asyncio.get_event_loop().time()
|
|
75
|
+
async with self.session.get(url, allow_redirects=True) as response:
|
|
76
|
+
load_time = asyncio.get_event_loop().time() - start_time
|
|
77
|
+
success = 200 <= response.status < 300
|
|
78
|
+
return HTTPResponse(
|
|
79
|
+
status_code=response.status,
|
|
80
|
+
url=str(response.url),
|
|
81
|
+
load_time=load_time,
|
|
82
|
+
success=success,
|
|
83
|
+
)
|
|
84
|
+
except asyncio.TimeoutError:
|
|
85
|
+
return HTTPResponse(
|
|
86
|
+
status_code=0,
|
|
87
|
+
url=url,
|
|
88
|
+
load_time=self.timeout,
|
|
89
|
+
success=False,
|
|
90
|
+
error_message="Request timeout",
|
|
91
|
+
)
|
|
92
|
+
except aiohttp.ClientError as e:
|
|
93
|
+
return HTTPResponse(
|
|
94
|
+
status_code=0,
|
|
95
|
+
url=url,
|
|
96
|
+
load_time=0.0,
|
|
97
|
+
success=False,
|
|
98
|
+
error_message=f"HTTP client error: {str(e)}",
|
|
99
|
+
)
|
|
100
|
+
except Exception as e:
|
|
101
|
+
return HTTPResponse(
|
|
102
|
+
status_code=0,
|
|
103
|
+
url=url,
|
|
104
|
+
load_time=0.0,
|
|
105
|
+
success=False,
|
|
106
|
+
error_message=f"Unexpected error: {str(e)}",
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
async def fetch_urls(self, urls: List[str]) -> List[HTTPResponse]:
|
|
110
|
+
"""Fetch multiple URLs concurrently"""
|
|
111
|
+
async with self:
|
|
112
|
+
tasks = [self.fetch_url(url) for url in urls]
|
|
113
|
+
return await asyncio.gather(*tasks)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def extract_links_from_text(text: str, base_url: Optional[str] = None) -> List[str]:
|
|
117
|
+
"""Extract links from text"""
|
|
118
|
+
links = []
|
|
119
|
+
|
|
120
|
+
# Markdown link pattern: [text](url)
|
|
121
|
+
markdown_pattern = r"\[([^\]]+)\]\(([^)]+)\)"
|
|
122
|
+
markdown_matches = re.findall(markdown_pattern, text)
|
|
123
|
+
|
|
124
|
+
for match in markdown_matches:
|
|
125
|
+
url = match[1]
|
|
126
|
+
# Convert relative URLs to absolute URLs
|
|
127
|
+
if url.startswith(("http://", "https://")):
|
|
128
|
+
links.append(url)
|
|
129
|
+
elif base_url and url.startswith("/"):
|
|
130
|
+
links.append(f"{base_url}{url}")
|
|
131
|
+
elif base_url and not url.startswith(("http://", "https://", "#")):
|
|
132
|
+
links.append(f"{base_url}/{url.rstrip('/')}")
|
|
133
|
+
|
|
134
|
+
# General URL pattern
|
|
135
|
+
url_pattern = r'https?://[^\s<>"\'()]+'
|
|
136
|
+
url_matches = re.findall(url_pattern, text)
|
|
137
|
+
links.extend(url_matches)
|
|
138
|
+
|
|
139
|
+
logger.info(f"Found {len(links)} links in text")
|
|
140
|
+
return list(set(links)) # Remove duplicates
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def is_valid_url(url: str) -> bool:
|
|
144
|
+
"""Validate URL"""
|
|
145
|
+
try:
|
|
146
|
+
result = urlparse(url)
|
|
147
|
+
return all([result.scheme, result.netloc])
|
|
148
|
+
except Exception:
|
|
149
|
+
return False
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def create_report_path(base_path: Path, suffix: str = "report") -> Path:
|
|
153
|
+
"""Create report file path"""
|
|
154
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
155
|
+
filename = f"{suffix}_{timestamp}.md"
|
|
156
|
+
return base_path / filename
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def format_duration(seconds: float) -> str:
|
|
160
|
+
"""Convert time (seconds) to readable format"""
|
|
161
|
+
if seconds < 1:
|
|
162
|
+
return f"{seconds*1000:.0f}ms"
|
|
163
|
+
elif seconds < 60:
|
|
164
|
+
return f"{seconds:.1f}s"
|
|
165
|
+
elif seconds < 3600:
|
|
166
|
+
minutes = int(seconds // 60)
|
|
167
|
+
remaining_seconds = seconds % 60
|
|
168
|
+
return f"{minutes}m {remaining_seconds:.0f}s"
|
|
169
|
+
else:
|
|
170
|
+
hours = int(seconds // 3600)
|
|
171
|
+
remaining_minutes = int((seconds % 3600) // 60)
|
|
172
|
+
return f"{hours}h {remaining_minutes}m"
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def calculate_score(
|
|
176
|
+
values: List[float], weights: Optional[List[float]] = None
|
|
177
|
+
) -> float:
|
|
178
|
+
"""Calculate weighted average score"""
|
|
179
|
+
if not values:
|
|
180
|
+
return 0.0
|
|
181
|
+
|
|
182
|
+
if weights is None:
|
|
183
|
+
weights = [1.0] * len(values)
|
|
184
|
+
|
|
185
|
+
if len(values) != len(weights):
|
|
186
|
+
raise ValueError("Values and weights must have the same length")
|
|
187
|
+
|
|
188
|
+
weighted_sum = sum(v * w for v, w in zip(values, weights))
|
|
189
|
+
total_weight = sum(weights)
|
|
190
|
+
|
|
191
|
+
return weighted_sum / total_weight if total_weight > 0 else 0.0
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def get_summary_stats(numbers: List[float]) -> Dict[str, float]:
|
|
195
|
+
"""Calculate basic statistics"""
|
|
196
|
+
if not numbers:
|
|
197
|
+
return {"mean": 0.0, "min": 0.0, "max": 0.0, "std": 0.0}
|
|
198
|
+
|
|
199
|
+
mean = sum(numbers) / len(numbers)
|
|
200
|
+
min_val = min(numbers)
|
|
201
|
+
max_val = max(numbers)
|
|
202
|
+
|
|
203
|
+
# Calculate standard deviation
|
|
204
|
+
if len(numbers) > 1:
|
|
205
|
+
variance = sum((x - mean) ** 2 for x in numbers) / (len(numbers) - 1)
|
|
206
|
+
std_dev = variance**0.5
|
|
207
|
+
else:
|
|
208
|
+
std_dev = 0.0
|
|
209
|
+
|
|
210
|
+
return {"mean": mean, "min": min_val, "max": max_val, "std": std_dev}
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
class RateLimiter:
|
|
214
|
+
"""Request rate limiter"""
|
|
215
|
+
|
|
216
|
+
def __init__(self, max_requests: int = 10, time_window: int = 60):
|
|
217
|
+
self.max_requests = max_requests
|
|
218
|
+
self.time_window = time_window
|
|
219
|
+
self.requests: List[datetime] = []
|
|
220
|
+
|
|
221
|
+
def can_make_request(self) -> bool:
|
|
222
|
+
"""Check if request can be made"""
|
|
223
|
+
now = datetime.now()
|
|
224
|
+
|
|
225
|
+
# Remove old requests
|
|
226
|
+
self.requests = [
|
|
227
|
+
req_time
|
|
228
|
+
for req_time in self.requests
|
|
229
|
+
if (now - req_time).total_seconds() < self.time_window
|
|
230
|
+
]
|
|
231
|
+
|
|
232
|
+
return len(self.requests) < self.max_requests
|
|
233
|
+
|
|
234
|
+
def add_request(self):
|
|
235
|
+
"""Add request record"""
|
|
236
|
+
if self.can_make_request():
|
|
237
|
+
self.requests.append(datetime.now())
|
|
238
|
+
else:
|
|
239
|
+
raise RateLimitError(
|
|
240
|
+
f"Rate limit exceeded: {self.max_requests} requests per {self.time_window}s"
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
async def wait_if_needed(self):
|
|
244
|
+
"""Wait until request can be made"""
|
|
245
|
+
if not self.can_make_request():
|
|
246
|
+
oldest_request = min(self.requests)
|
|
247
|
+
wait_time = (
|
|
248
|
+
self.time_window - (datetime.now() - oldest_request).total_seconds()
|
|
249
|
+
)
|
|
250
|
+
if wait_time > 0:
|
|
251
|
+
logger.info(f"Rate limiting: waiting {wait_time:.1f}s")
|
|
252
|
+
await asyncio.sleep(wait_time)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
class RateLimitError(Exception):
|
|
256
|
+
"""Rate limit error"""
|
|
257
|
+
|
|
258
|
+
pass
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def load_hook_timeout() -> int:
|
|
262
|
+
"""
|
|
263
|
+
Load Hook timeout setting from .moai/config/config.json
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
int: timeout value (milliseconds), returns default 5000 if not configured
|
|
267
|
+
"""
|
|
268
|
+
try:
|
|
269
|
+
config_path = Path(".moai/config/config.json")
|
|
270
|
+
if not config_path.exists():
|
|
271
|
+
return 5000 # Default value
|
|
272
|
+
|
|
273
|
+
with open(config_path, "r", encoding="utf-8") as f:
|
|
274
|
+
config = json.load(f)
|
|
275
|
+
|
|
276
|
+
# Get timeout_ms value from hooks section
|
|
277
|
+
hooks_config = config.get("hooks", {})
|
|
278
|
+
timeout_ms = hooks_config.get("timeout_ms", 5000)
|
|
279
|
+
|
|
280
|
+
return int(timeout_ms)
|
|
281
|
+
except (json.JSONDecodeError, FileNotFoundError, KeyError, ValueError):
|
|
282
|
+
logger.warning("Failed to load hook timeout from config, using default 5000ms")
|
|
283
|
+
return 5000
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def get_graceful_degradation() -> bool:
|
|
287
|
+
"""
|
|
288
|
+
Load graceful_degradation setting from .moai/config/config.json
|
|
289
|
+
|
|
290
|
+
Returns:
|
|
291
|
+
bool: graceful_degradation setting value, returns default True if not configured
|
|
292
|
+
"""
|
|
293
|
+
try:
|
|
294
|
+
config_path = Path(".moai/config/config.json")
|
|
295
|
+
if not config_path.exists():
|
|
296
|
+
return True # Default value
|
|
297
|
+
|
|
298
|
+
with open(config_path, "r", encoding="utf-8") as f:
|
|
299
|
+
config = json.load(f)
|
|
300
|
+
|
|
301
|
+
# Get graceful_degradation value from hooks section
|
|
302
|
+
hooks_config = config.get("hooks", {})
|
|
303
|
+
return hooks_config.get("graceful_degradation", True)
|
|
304
|
+
except (json.JSONDecodeError, FileNotFoundError, KeyError):
|
|
305
|
+
logger.warning(
|
|
306
|
+
"Failed to load graceful_degradation from config, using default True"
|
|
307
|
+
)
|
|
308
|
+
return True
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Link Validation Utilities
|
|
3
|
+
Online documentation link validation utilities
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import asyncio
|
|
7
|
+
import logging
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import List, Optional
|
|
12
|
+
|
|
13
|
+
from moai_adk.utils.common import (
|
|
14
|
+
HTTPClient,
|
|
15
|
+
create_report_path,
|
|
16
|
+
extract_links_from_text,
|
|
17
|
+
is_valid_url,
|
|
18
|
+
)
|
|
19
|
+
from moai_adk.utils.safe_file_reader import SafeFileReader
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class LinkResult:
|
|
26
|
+
"""Link validation result"""
|
|
27
|
+
|
|
28
|
+
url: str
|
|
29
|
+
status_code: int
|
|
30
|
+
is_valid: bool
|
|
31
|
+
response_time: float
|
|
32
|
+
error_message: Optional[str] = None
|
|
33
|
+
checked_at: datetime = field(default_factory=datetime.now)
|
|
34
|
+
|
|
35
|
+
def __post_init__(self):
|
|
36
|
+
if self.checked_at is None:
|
|
37
|
+
self.checked_at = datetime.now()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass
|
|
41
|
+
class ValidationResult:
|
|
42
|
+
"""Overall validation result"""
|
|
43
|
+
|
|
44
|
+
total_links: int
|
|
45
|
+
valid_links: int
|
|
46
|
+
invalid_links: int
|
|
47
|
+
results: List[LinkResult]
|
|
48
|
+
completed_at: datetime = field(default_factory=datetime.now)
|
|
49
|
+
|
|
50
|
+
def __post_init__(self):
|
|
51
|
+
if self.completed_at is None:
|
|
52
|
+
self.completed_at = datetime.now()
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def success_rate(self) -> float:
|
|
56
|
+
"""Calculate success rate"""
|
|
57
|
+
if self.total_links == 0:
|
|
58
|
+
return 0.0
|
|
59
|
+
return (self.valid_links / self.total_links) * 100
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class LinkValidator(HTTPClient):
|
|
63
|
+
"""Online documentation link validator"""
|
|
64
|
+
|
|
65
|
+
def __init__(self, max_concurrent: int = 5, timeout: int = 10):
|
|
66
|
+
super().__init__(max_concurrent, timeout)
|
|
67
|
+
|
|
68
|
+
def extract_links_from_file(self, file_path: Path) -> List[str]:
|
|
69
|
+
"""Extract all links from file (using safe file reading)"""
|
|
70
|
+
if not file_path.exists():
|
|
71
|
+
logger.warning(f"File does not exist: {file_path}")
|
|
72
|
+
return []
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
reader = SafeFileReader()
|
|
76
|
+
content = reader.read_text(file_path)
|
|
77
|
+
if content is None:
|
|
78
|
+
logger.error(f"Unable to read file: {file_path}")
|
|
79
|
+
return []
|
|
80
|
+
|
|
81
|
+
base_url = "https://adk.mo.ai.kr"
|
|
82
|
+
links = extract_links_from_text(content, base_url)
|
|
83
|
+
logger.info(f"Found {len(links)} links in file: {file_path}")
|
|
84
|
+
return links
|
|
85
|
+
except Exception as e:
|
|
86
|
+
logger.error(f"Error during link extraction: {e}")
|
|
87
|
+
return []
|
|
88
|
+
|
|
89
|
+
async def validate_link(self, url: str) -> LinkResult:
|
|
90
|
+
"""Validate single link"""
|
|
91
|
+
try:
|
|
92
|
+
# URL validity check
|
|
93
|
+
if not is_valid_url(url):
|
|
94
|
+
return LinkResult(
|
|
95
|
+
url=url,
|
|
96
|
+
status_code=0,
|
|
97
|
+
is_valid=False,
|
|
98
|
+
response_time=0.0,
|
|
99
|
+
error_message="Invalid URL format",
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# HTTP request
|
|
103
|
+
response = await self.fetch_url(url)
|
|
104
|
+
|
|
105
|
+
return LinkResult(
|
|
106
|
+
url=url,
|
|
107
|
+
status_code=response.status_code,
|
|
108
|
+
is_valid=response.success,
|
|
109
|
+
response_time=response.load_time,
|
|
110
|
+
error_message=response.error_message,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
except Exception as e:
|
|
114
|
+
return LinkResult(
|
|
115
|
+
url=url,
|
|
116
|
+
status_code=0,
|
|
117
|
+
is_valid=False,
|
|
118
|
+
response_time=0.0,
|
|
119
|
+
error_message=f"Unexpected error: {str(e)}",
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
async def validate_all_links(self, links: List[str]) -> ValidationResult:
|
|
123
|
+
"""Validate all links"""
|
|
124
|
+
results = []
|
|
125
|
+
|
|
126
|
+
# Split into link groups (concurrency control)
|
|
127
|
+
semaphore = asyncio.Semaphore(self.max_concurrent)
|
|
128
|
+
|
|
129
|
+
async def validate_with_semaphore(link: str):
|
|
130
|
+
async with semaphore:
|
|
131
|
+
result = await self.validate_link(link)
|
|
132
|
+
results.append(result)
|
|
133
|
+
# Log progress
|
|
134
|
+
logger.info(
|
|
135
|
+
f"Validation complete: {link} -> {result.status_code} ({result.is_valid})"
|
|
136
|
+
)
|
|
137
|
+
return result
|
|
138
|
+
|
|
139
|
+
# Validate all links asynchronously
|
|
140
|
+
tasks = [validate_with_semaphore(link) for link in links]
|
|
141
|
+
await asyncio.gather(*tasks)
|
|
142
|
+
|
|
143
|
+
# Analyze results
|
|
144
|
+
valid_links = sum(1 for r in results if r.is_valid)
|
|
145
|
+
invalid_links = len(results) - valid_links
|
|
146
|
+
|
|
147
|
+
return ValidationResult(
|
|
148
|
+
total_links=len(results),
|
|
149
|
+
valid_links=valid_links,
|
|
150
|
+
invalid_links=invalid_links,
|
|
151
|
+
results=results,
|
|
152
|
+
completed_at=datetime.now(),
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
def generate_report(self, validation_result: ValidationResult) -> str:
|
|
156
|
+
"""Generate validation report"""
|
|
157
|
+
from moai_adk.utils.common import get_summary_stats
|
|
158
|
+
|
|
159
|
+
report = []
|
|
160
|
+
report.append("# Online Documentation Link Validation Report")
|
|
161
|
+
report.append(
|
|
162
|
+
f"**Validation Time**: {validation_result.completed_at.strftime('%Y-%m-%d %H:%M:%S')}"
|
|
163
|
+
)
|
|
164
|
+
report.append(f"**Total Links**: {validation_result.total_links}")
|
|
165
|
+
report.append(f"**Valid Links**: {validation_result.valid_links}")
|
|
166
|
+
report.append(f"**Invalid Links**: {validation_result.invalid_links}")
|
|
167
|
+
report.append(f"**Success Rate**: {validation_result.success_rate:.1f}%")
|
|
168
|
+
report.append("")
|
|
169
|
+
|
|
170
|
+
# Statistics
|
|
171
|
+
if validation_result.results:
|
|
172
|
+
response_times = [r.response_time for r in validation_result.results]
|
|
173
|
+
stats = get_summary_stats(response_times)
|
|
174
|
+
report.append("## 📊 Statistics")
|
|
175
|
+
report.append("")
|
|
176
|
+
report.append(f"- Average Response Time: {stats['mean']:.2f}s")
|
|
177
|
+
report.append(f"- Minimum Response Time: {stats['min']:.2f}s")
|
|
178
|
+
report.append(f"- Maximum Response Time: {stats['max']:.2f}s")
|
|
179
|
+
report.append(f"- Standard Deviation: {stats['std']:.2f}s")
|
|
180
|
+
report.append("")
|
|
181
|
+
|
|
182
|
+
# Failed links detailed report
|
|
183
|
+
if validation_result.invalid_links > 0:
|
|
184
|
+
report.append("## ❌ Failed Links")
|
|
185
|
+
report.append("")
|
|
186
|
+
|
|
187
|
+
for result in validation_result.results:
|
|
188
|
+
if not result.is_valid:
|
|
189
|
+
report.append(f"- **{result.url}**")
|
|
190
|
+
report.append(f" - Status Code: {result.status_code}")
|
|
191
|
+
report.append(f" - Response Time: {result.response_time:.2f}s")
|
|
192
|
+
if result.error_message:
|
|
193
|
+
report.append(f" - Error: {result.error_message}")
|
|
194
|
+
report.append("")
|
|
195
|
+
|
|
196
|
+
# Successful links summary
|
|
197
|
+
if validation_result.valid_links > 0:
|
|
198
|
+
report.append("## ✅ Successful Links")
|
|
199
|
+
report.append("")
|
|
200
|
+
report.append(
|
|
201
|
+
f"Total of {validation_result.valid_links} links validated successfully."
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
return "\n".join(report)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def validate_readme_links(readme_path: Optional[Path] = None) -> ValidationResult:
|
|
208
|
+
"""Validate all links in README file"""
|
|
209
|
+
if readme_path is None:
|
|
210
|
+
readme_path = Path("README.ko.md")
|
|
211
|
+
|
|
212
|
+
validator = LinkValidator(max_concurrent=3, timeout=8)
|
|
213
|
+
|
|
214
|
+
# Extract links from README file
|
|
215
|
+
links = validator.extract_links_from_file(readme_path)
|
|
216
|
+
|
|
217
|
+
if not links:
|
|
218
|
+
logger.warning("No links to validate")
|
|
219
|
+
return ValidationResult(
|
|
220
|
+
total_links=0, valid_links=0, invalid_links=0, results=[]
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
logger.info(f"Validating total of {len(links)} links...")
|
|
224
|
+
|
|
225
|
+
# Perform asynchronous validation
|
|
226
|
+
result = asyncio.run(validator.validate_all_links(links))
|
|
227
|
+
|
|
228
|
+
# Generate and save report
|
|
229
|
+
report = validator.generate_report(result)
|
|
230
|
+
report_path = create_report_path(Path("."), "link_validation")
|
|
231
|
+
report_path.write_text(report, encoding="utf-8")
|
|
232
|
+
logger.info(f"Report saved to: {report_path}")
|
|
233
|
+
|
|
234
|
+
return result
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
if __name__ == "__main__":
|
|
238
|
+
# Execute README file link validation
|
|
239
|
+
result = validate_readme_links()
|
|
240
|
+
|
|
241
|
+
# Print results
|
|
242
|
+
validator = LinkValidator()
|
|
243
|
+
report = validator.generate_report(result)
|
|
244
|
+
print(report)
|
|
245
|
+
|
|
246
|
+
# Save to file
|
|
247
|
+
report_path = Path("link_validation_report.md")
|
|
248
|
+
report_path.write_text(report, encoding="utf-8")
|
|
249
|
+
print(f"\nReport saved to: {report_path}")
|
moai_adk/utils/logger.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# @CODE:LOGGING-001 | SPEC: SPEC-LOGGING-001/spec.md | TEST: tests/unit/test_logger.py
|
|
2
1
|
"""
|
|
3
2
|
Logging system built on Python's logging module
|
|
4
3
|
|
|
@@ -36,10 +35,12 @@ class SensitiveDataFilter(logging.Filter):
|
|
|
36
35
|
API Key: ***REDACTED***
|
|
37
36
|
"""
|
|
38
37
|
|
|
39
|
-
# @CODE:LOGGING-001:DOMAIN - Define sensitive data patterns
|
|
40
38
|
PATTERNS = [
|
|
41
39
|
(r"sk-[a-zA-Z0-9]+", "***REDACTED***"), # API Key
|
|
42
|
-
(
|
|
40
|
+
(
|
|
41
|
+
r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b",
|
|
42
|
+
"***REDACTED***",
|
|
43
|
+
), # Email
|
|
43
44
|
(r"(?i)(password|passwd|pwd)[\s:=]+\S+", r"\1: ***REDACTED***"), # Password
|
|
44
45
|
]
|
|
45
46
|
|
|
@@ -107,7 +108,6 @@ def setup_logger(
|
|
|
107
108
|
- Sensitive data (API Key, Email, Password) is automatically masked.
|
|
108
109
|
- Existing handlers are removed to prevent duplicates.
|
|
109
110
|
"""
|
|
110
|
-
# @CODE:LOGGING-001:DOMAIN - Determine logging level
|
|
111
111
|
if level is None:
|
|
112
112
|
env = os.getenv("MOAI_ENV", "").lower()
|
|
113
113
|
level_map = {
|
|
@@ -117,31 +117,26 @@ def setup_logger(
|
|
|
117
117
|
}
|
|
118
118
|
level = level_map.get(env, logging.INFO)
|
|
119
119
|
|
|
120
|
-
# @CODE:LOGGING-001:INFRA - Create and configure logger
|
|
121
120
|
logger = logging.getLogger(name)
|
|
122
121
|
logger.setLevel(level)
|
|
123
122
|
logger.handlers.clear() # Remove existing handlers to avoid duplicates
|
|
124
123
|
|
|
125
|
-
# @CODE:LOGGING-001:INFRA - Ensure log directory exists
|
|
126
124
|
if log_dir is None:
|
|
127
125
|
log_dir = ".moai/logs"
|
|
128
126
|
log_path = Path(log_dir)
|
|
129
127
|
log_path.mkdir(parents=True, exist_ok=True)
|
|
130
128
|
|
|
131
|
-
# @CODE:LOGGING-001:INFRA - Define log format
|
|
132
129
|
formatter = logging.Formatter(
|
|
133
130
|
fmt="[%(asctime)s] [%(levelname)s] [%(name)s] %(message)s",
|
|
134
131
|
datefmt="%Y-%m-%d %H:%M:%S",
|
|
135
132
|
)
|
|
136
133
|
|
|
137
|
-
# @CODE:LOGGING-001:INFRA - Console handler
|
|
138
134
|
console_handler = logging.StreamHandler()
|
|
139
135
|
console_handler.setLevel(level)
|
|
140
136
|
console_handler.setFormatter(formatter)
|
|
141
137
|
console_handler.addFilter(SensitiveDataFilter())
|
|
142
138
|
logger.addHandler(console_handler)
|
|
143
139
|
|
|
144
|
-
# @CODE:LOGGING-001:INFRA - File handler
|
|
145
140
|
log_file = log_path / "moai.log"
|
|
146
141
|
file_handler = logging.FileHandler(log_file, encoding="utf-8")
|
|
147
142
|
file_handler.setLevel(level)
|