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,749 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Project metadata utilities
|
|
3
|
+
|
|
4
|
+
Project information inquiry (language, Git, SPEC progress, etc.)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import signal
|
|
9
|
+
import socket
|
|
10
|
+
import subprocess
|
|
11
|
+
from contextlib import contextmanager
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
# Cache directory for version check results
|
|
16
|
+
CACHE_DIR_NAME = ".moai/cache"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def find_project_root(start_path: str | Path = ".") -> Path:
|
|
20
|
+
"""Find MoAI-ADK project root by searching upward for .moai/config.json
|
|
21
|
+
|
|
22
|
+
Traverses up the directory tree until it finds .moai/config.json or CLAUDE.md,
|
|
23
|
+
which indicates the project root. This ensures cache and other files are
|
|
24
|
+
always created in the correct location, regardless of where hooks execute.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
start_path: Starting directory (default: current directory)
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
Project root Path. If not found, returns start_path as absolute path.
|
|
31
|
+
|
|
32
|
+
Examples:
|
|
33
|
+
>>> find_project_root(".")
|
|
34
|
+
Path("/Users/user/my-project")
|
|
35
|
+
>>> find_project_root(".claude/hooks/alfred")
|
|
36
|
+
Path("/Users/user/my-project") # Found root 3 levels up
|
|
37
|
+
|
|
38
|
+
Notes:
|
|
39
|
+
- Searches for .moai/config.json first (most reliable)
|
|
40
|
+
- Falls back to CLAUDE.md if config.json not found
|
|
41
|
+
- Max depth: 10 levels up (prevent infinite loop)
|
|
42
|
+
- Returns absolute path for consistency
|
|
43
|
+
|
|
44
|
+
TDD History:
|
|
45
|
+
- RED: 4 test scenarios (root, nested, not found, symlinks)
|
|
46
|
+
- GREEN: Minimal upward search with .moai/config.json detection
|
|
47
|
+
- REFACTOR: Add CLAUDE.md fallback, max depth limit, absolute path return
|
|
48
|
+
"""
|
|
49
|
+
current = Path(start_path).resolve()
|
|
50
|
+
max_depth = 10 # Prevent infinite loop
|
|
51
|
+
|
|
52
|
+
for _ in range(max_depth):
|
|
53
|
+
# Check for .moai/config.json (primary indicator)
|
|
54
|
+
if (current / ".moai" / "config.json").exists():
|
|
55
|
+
return current
|
|
56
|
+
|
|
57
|
+
# Check for CLAUDE.md (secondary indicator)
|
|
58
|
+
if (current / "CLAUDE.md").exists():
|
|
59
|
+
return current
|
|
60
|
+
|
|
61
|
+
# Move up one level
|
|
62
|
+
parent = current.parent
|
|
63
|
+
if parent == current: # Reached filesystem root
|
|
64
|
+
break
|
|
65
|
+
current = parent
|
|
66
|
+
|
|
67
|
+
# Not found - return start_path as absolute
|
|
68
|
+
return Path(start_path).resolve()
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class TimeoutError(Exception):
|
|
72
|
+
"""Signal-based timeout exception"""
|
|
73
|
+
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@contextmanager
|
|
78
|
+
def timeout_handler(seconds: int):
|
|
79
|
+
"""Hard timeout using SIGALRM (works on Unix systems including macOS)
|
|
80
|
+
|
|
81
|
+
This uses kernel-level signal to interrupt ANY blocking operation,
|
|
82
|
+
even if subprocess.run() timeout fails on macOS.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
seconds: Timeout duration in seconds
|
|
86
|
+
|
|
87
|
+
Raises:
|
|
88
|
+
TimeoutError: If operation exceeds timeout
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
def _handle_timeout(signum, frame):
|
|
92
|
+
raise TimeoutError(f"Operation timed out after {seconds} seconds")
|
|
93
|
+
|
|
94
|
+
# Set the signal handler
|
|
95
|
+
old_handler = signal.signal(signal.SIGALRM, _handle_timeout)
|
|
96
|
+
signal.alarm(seconds)
|
|
97
|
+
try:
|
|
98
|
+
yield
|
|
99
|
+
finally:
|
|
100
|
+
signal.alarm(0) # Disable alarm
|
|
101
|
+
signal.signal(signal.SIGALRM, old_handler)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def detect_language(cwd: str) -> str:
|
|
105
|
+
"""Detect project language (supports 20 items languages)
|
|
106
|
+
|
|
107
|
+
Browse the File system to detect your project's main development language.
|
|
108
|
+
First, check configuration files such as pyproject.toml and tsconfig.json.
|
|
109
|
+
Apply TypeScript first principles (if tsconfig.json exists).
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
cwd: Project root directory path (both absolute and relative paths are possible)
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
Detected language name (lowercase). If detection fails, "Unknown Language" is returned.
|
|
116
|
+
Supported languages: python, typescript, javascript, java, go, rust,
|
|
117
|
+
dart, swift, kotlin, php, ruby, elixir, scala,
|
|
118
|
+
clojure, cpp, c, csharp, haskell, shell, lua
|
|
119
|
+
|
|
120
|
+
Examples:
|
|
121
|
+
>>> detect_language("/path/to/python/project")
|
|
122
|
+
'python'
|
|
123
|
+
>>> detect_language("/path/to/typescript/project")
|
|
124
|
+
'typescript'
|
|
125
|
+
>>> detect_language("/path/to/unknown/project")
|
|
126
|
+
'Unknown Language'
|
|
127
|
+
|
|
128
|
+
TDD History:
|
|
129
|
+
- RED: Write a 21 items language detection test (20 items language + 1 items unknown)
|
|
130
|
+
- GREEN: 20 items language + unknown implementation, all tests passed
|
|
131
|
+
- REFACTOR: Optimize file inspection order, apply TypeScript priority principle
|
|
132
|
+
"""
|
|
133
|
+
cwd_path = Path(cwd)
|
|
134
|
+
|
|
135
|
+
# Language detection mapping
|
|
136
|
+
language_files = {
|
|
137
|
+
"pyproject.toml": "python",
|
|
138
|
+
"tsconfig.json": "typescript",
|
|
139
|
+
"package.json": "javascript",
|
|
140
|
+
"pom.xml": "java",
|
|
141
|
+
"go.mod": "go",
|
|
142
|
+
"Cargo.toml": "rust",
|
|
143
|
+
"pubspec.yaml": "dart",
|
|
144
|
+
"Package.swift": "swift",
|
|
145
|
+
"build.gradle.kts": "kotlin",
|
|
146
|
+
"composer.json": "php",
|
|
147
|
+
"Gemfile": "ruby",
|
|
148
|
+
"mix.exs": "elixir",
|
|
149
|
+
"build.sbt": "scala",
|
|
150
|
+
"project.clj": "clojure",
|
|
151
|
+
"CMakeLists.txt": "cpp",
|
|
152
|
+
"Makefile": "c",
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
# Check standard language files
|
|
156
|
+
for file_name, language in language_files.items():
|
|
157
|
+
if (cwd_path / file_name).exists():
|
|
158
|
+
# Special handling for package.json - prefer typescript if tsconfig exists
|
|
159
|
+
if file_name == "package.json" and (cwd_path / "tsconfig.json").exists():
|
|
160
|
+
return "typescript"
|
|
161
|
+
return language
|
|
162
|
+
|
|
163
|
+
# Check for C# project files (*.csproj)
|
|
164
|
+
if any(cwd_path.glob("*.csproj")):
|
|
165
|
+
return "csharp"
|
|
166
|
+
|
|
167
|
+
# Check for Haskell project files (*.cabal)
|
|
168
|
+
if any(cwd_path.glob("*.cabal")):
|
|
169
|
+
return "haskell"
|
|
170
|
+
|
|
171
|
+
# Check for Shell scripts (*.sh)
|
|
172
|
+
if any(cwd_path.glob("*.sh")):
|
|
173
|
+
return "shell"
|
|
174
|
+
|
|
175
|
+
# Check for Lua files (*.lua)
|
|
176
|
+
if any(cwd_path.glob("*.lua")):
|
|
177
|
+
return "lua"
|
|
178
|
+
|
|
179
|
+
return "Unknown Language"
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _run_git_command(args: list[str], cwd: str, timeout: int = 2) -> str:
|
|
183
|
+
"""Git command execution with HARD timeout protection
|
|
184
|
+
|
|
185
|
+
Safely execute Git commands and return output.
|
|
186
|
+
Uses SIGALRM (kernel-level interrupt) to handle macOS subprocess timeout bug.
|
|
187
|
+
Eliminates code duplication and provides consistent error handling.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
args: Git command argument list (git adds automatically)
|
|
191
|
+
cwd: Execution directory path
|
|
192
|
+
timeout: Timeout (seconds, default 2 seconds)
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
Git command output (stdout, removing leading and trailing spaces)
|
|
196
|
+
|
|
197
|
+
Raises:
|
|
198
|
+
subprocess.TimeoutExpired: Timeout exceeded (via TimeoutError)
|
|
199
|
+
subprocess.CalledProcessError: Git command failed
|
|
200
|
+
|
|
201
|
+
Examples:
|
|
202
|
+
>>> _run_git_command(["branch", "--show-current"], ".")
|
|
203
|
+
'main'
|
|
204
|
+
|
|
205
|
+
TDD History:
|
|
206
|
+
- RED: Git command hang scenario test
|
|
207
|
+
- GREEN: SIGALRM-based timeout implementation
|
|
208
|
+
- REFACTOR: Exception conversion to subprocess.TimeoutExpired
|
|
209
|
+
"""
|
|
210
|
+
try:
|
|
211
|
+
with timeout_handler(timeout):
|
|
212
|
+
result = subprocess.run(
|
|
213
|
+
["git"] + args,
|
|
214
|
+
cwd=cwd,
|
|
215
|
+
capture_output=True,
|
|
216
|
+
text=True,
|
|
217
|
+
check=False, # Don't raise on non-zero exit - we'll check manually
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
# Check exit code manually
|
|
221
|
+
if result.returncode != 0:
|
|
222
|
+
raise subprocess.CalledProcessError(
|
|
223
|
+
result.returncode, ["git"] + args, result.stdout, result.stderr
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
return result.stdout.strip()
|
|
227
|
+
|
|
228
|
+
except TimeoutError:
|
|
229
|
+
# Convert to subprocess.TimeoutExpired for consistent error handling
|
|
230
|
+
raise subprocess.TimeoutExpired(["git"] + args, timeout)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def get_git_info(cwd: str) -> dict[str, Any]:
|
|
234
|
+
"""Gather Git repository information
|
|
235
|
+
|
|
236
|
+
View the current status of a Git repository.
|
|
237
|
+
Returns the branch name, commit hash, number of changes, and last commit message.
|
|
238
|
+
If it is not a Git repository, it returns an empty dictionary.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
cwd: Project root directory path
|
|
242
|
+
|
|
243
|
+
Returns:
|
|
244
|
+
Git information dictionary. Includes the following keys:
|
|
245
|
+
- branch: Current branch name (str)
|
|
246
|
+
- commit: Current commit hash (str, full hash)
|
|
247
|
+
- changes: Number of changed files (int, staged + unstaged)
|
|
248
|
+
- last_commit: Last commit message (str, subject only)
|
|
249
|
+
|
|
250
|
+
Empty dictionary {} if it is not a Git repository or the query fails.
|
|
251
|
+
|
|
252
|
+
Examples:
|
|
253
|
+
>>> get_git_info("/path/to/git/repo")
|
|
254
|
+
{'branch': 'main', 'commit': 'abc123...', 'changes': 3, 'last_commit': 'Fix bug'}
|
|
255
|
+
>>> get_git_info("/path/to/non-git")
|
|
256
|
+
{}
|
|
257
|
+
|
|
258
|
+
Notes:
|
|
259
|
+
- Timeout: 2 seconds for each Git command
|
|
260
|
+
- Security: Safe execution with subprocess.run(shell=False)
|
|
261
|
+
- Error handling: Returns an empty dictionary in case of all exceptions
|
|
262
|
+
- Commit message limited to 50 characters for display purposes
|
|
263
|
+
|
|
264
|
+
TDD History:
|
|
265
|
+
- RED: 3 items scenario test (Git repo, non-Git, error)
|
|
266
|
+
- GREEN: Implementation of subprocess-based Git command execution
|
|
267
|
+
- REFACTOR: Add timeout (2 seconds), strengthen exception handling, remove duplicates with helper function
|
|
268
|
+
- UPDATE: Added last_commit message field for SessionStart display
|
|
269
|
+
"""
|
|
270
|
+
try:
|
|
271
|
+
# Check if it's a git repository
|
|
272
|
+
_run_git_command(["rev-parse", "--git-dir"], cwd)
|
|
273
|
+
|
|
274
|
+
# Get branch name, commit hash, and changes
|
|
275
|
+
branch = _run_git_command(["branch", "--show-current"], cwd)
|
|
276
|
+
commit = _run_git_command(["rev-parse", "HEAD"], cwd)
|
|
277
|
+
status_output = _run_git_command(["status", "--short"], cwd)
|
|
278
|
+
changes = len([line for line in status_output.splitlines() if line])
|
|
279
|
+
|
|
280
|
+
# Get last commit message (subject only, limited to 50 chars)
|
|
281
|
+
last_commit = _run_git_command(["log", "-1", "--format=%s"], cwd)
|
|
282
|
+
if len(last_commit) > 50:
|
|
283
|
+
last_commit = last_commit[:47] + "..."
|
|
284
|
+
|
|
285
|
+
return {
|
|
286
|
+
"branch": branch,
|
|
287
|
+
"commit": commit,
|
|
288
|
+
"changes": changes,
|
|
289
|
+
"last_commit": last_commit,
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
except (subprocess.TimeoutExpired, subprocess.CalledProcessError, FileNotFoundError):
|
|
293
|
+
return {}
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def count_specs(cwd: str) -> dict[str, int]:
|
|
297
|
+
"""SPEC File count and progress calculation
|
|
298
|
+
|
|
299
|
+
Browse the .moai/specs/ directory to find the number of SPEC Files and
|
|
300
|
+
Counts the number of SPECs with status: completed.
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
cwd: Project root directory path (or any subdirectory, will search upward)
|
|
304
|
+
|
|
305
|
+
Returns:
|
|
306
|
+
SPEC progress dictionary. Includes the following keys:
|
|
307
|
+
- completed: Number of completed SPECs (int)
|
|
308
|
+
- total: total number of SPECs (int)
|
|
309
|
+
- percentage: completion percentage (int, 0~100)
|
|
310
|
+
|
|
311
|
+
All 0 if .moai/specs/ directory does not exist
|
|
312
|
+
|
|
313
|
+
Examples:
|
|
314
|
+
>>> count_specs("/path/to/project")
|
|
315
|
+
{'completed': 2, 'total': 5, 'percentage': 40}
|
|
316
|
+
>>> count_specs("/path/to/no-specs")
|
|
317
|
+
{'completed': 0, 'total': 0, 'percentage': 0}
|
|
318
|
+
|
|
319
|
+
Notes:
|
|
320
|
+
- SPEC File Location: .moai/specs/SPEC-{ID}/spec.md
|
|
321
|
+
- Completion condition: Include "status: completed" in YAML front matter
|
|
322
|
+
- If parsing fails, the SPEC is considered incomplete.
|
|
323
|
+
- Automatically finds project root to locate .moai/specs/
|
|
324
|
+
|
|
325
|
+
TDD History:
|
|
326
|
+
- RED: 5 items scenario test (0/0, 2/5, 5/5, no directory, parsing error)
|
|
327
|
+
- GREEN: SPEC search with Path.iterdir(), YAML parsing implementation
|
|
328
|
+
- REFACTOR: Strengthened exception handling, improved percentage calculation safety
|
|
329
|
+
- UPDATE: Add project root detection for consistent path resolution
|
|
330
|
+
"""
|
|
331
|
+
# Find project root to ensure we read specs from correct location
|
|
332
|
+
project_root = find_project_root(cwd)
|
|
333
|
+
specs_dir = project_root / ".moai" / "specs"
|
|
334
|
+
|
|
335
|
+
if not specs_dir.exists():
|
|
336
|
+
return {"completed": 0, "total": 0, "percentage": 0}
|
|
337
|
+
|
|
338
|
+
completed = 0
|
|
339
|
+
total = 0
|
|
340
|
+
|
|
341
|
+
for spec_dir in specs_dir.iterdir():
|
|
342
|
+
if not spec_dir.is_dir() or not spec_dir.name.startswith("SPEC-"):
|
|
343
|
+
continue
|
|
344
|
+
|
|
345
|
+
spec_file = spec_dir / "spec.md"
|
|
346
|
+
if not spec_file.exists():
|
|
347
|
+
continue
|
|
348
|
+
|
|
349
|
+
total += 1
|
|
350
|
+
|
|
351
|
+
# Parse YAML front matter
|
|
352
|
+
try:
|
|
353
|
+
content = spec_file.read_text()
|
|
354
|
+
if content.startswith("---"):
|
|
355
|
+
yaml_end = content.find("---", 3)
|
|
356
|
+
if yaml_end > 0:
|
|
357
|
+
yaml_content = content[3:yaml_end]
|
|
358
|
+
if "status: completed" in yaml_content:
|
|
359
|
+
completed += 1
|
|
360
|
+
except (OSError, UnicodeDecodeError):
|
|
361
|
+
# File read failure or encoding error - considered incomplete
|
|
362
|
+
pass
|
|
363
|
+
|
|
364
|
+
percentage = int(completed / total * 100) if total > 0 else 0
|
|
365
|
+
|
|
366
|
+
return {
|
|
367
|
+
"completed": completed,
|
|
368
|
+
"total": total,
|
|
369
|
+
"percentage": percentage,
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def get_project_language(cwd: str) -> str:
|
|
374
|
+
"""Determine the primary project language (prefers config.json).
|
|
375
|
+
|
|
376
|
+
Args:
|
|
377
|
+
cwd: Project root directory (or any subdirectory, will search upward).
|
|
378
|
+
|
|
379
|
+
Returns:
|
|
380
|
+
Language string in lower-case.
|
|
381
|
+
|
|
382
|
+
Notes:
|
|
383
|
+
- Reads ``.moai/config.json`` first for a quick answer.
|
|
384
|
+
- Falls back to ``detect_language`` if configuration is missing.
|
|
385
|
+
- Automatically finds project root to locate .moai/config.json
|
|
386
|
+
"""
|
|
387
|
+
# Find project root to ensure we read config from correct location
|
|
388
|
+
project_root = find_project_root(cwd)
|
|
389
|
+
config_path = project_root / ".moai" / "config.json"
|
|
390
|
+
if config_path.exists():
|
|
391
|
+
try:
|
|
392
|
+
config = json.loads(config_path.read_text())
|
|
393
|
+
lang = config.get("language", "")
|
|
394
|
+
if lang:
|
|
395
|
+
return lang
|
|
396
|
+
except (OSError, json.JSONDecodeError):
|
|
397
|
+
# Fall back to detection on parse errors
|
|
398
|
+
pass
|
|
399
|
+
|
|
400
|
+
# Fall back to the original language detection routine (use project root)
|
|
401
|
+
return detect_language(str(project_root))
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
# @CODE:CONFIG-INTEGRATION-001
|
|
405
|
+
def get_version_check_config(cwd: str) -> dict[str, Any]:
|
|
406
|
+
"""Read version check configuration from .moai/config.json
|
|
407
|
+
|
|
408
|
+
Returns version check settings with sensible defaults.
|
|
409
|
+
Supports frequency-based cache TTL configuration.
|
|
410
|
+
|
|
411
|
+
Args:
|
|
412
|
+
cwd: Project root directory path
|
|
413
|
+
|
|
414
|
+
Returns:
|
|
415
|
+
dict with keys:
|
|
416
|
+
- "enabled": Boolean (default: True)
|
|
417
|
+
- "frequency": "always" | "daily" | "weekly" | "never" (default: "daily")
|
|
418
|
+
- "cache_ttl_hours": TTL in hours based on frequency
|
|
419
|
+
|
|
420
|
+
Frequency to TTL mapping:
|
|
421
|
+
- "always": 0 hours (no caching)
|
|
422
|
+
- "daily": 24 hours
|
|
423
|
+
- "weekly": 168 hours (7 days)
|
|
424
|
+
- "never": infinity (never check)
|
|
425
|
+
|
|
426
|
+
TDD History:
|
|
427
|
+
- RED: 8 test scenarios (defaults, custom, disabled, TTL, etc.)
|
|
428
|
+
- GREEN: Minimal config reading with defaults
|
|
429
|
+
- REFACTOR: Add validation and error handling
|
|
430
|
+
"""
|
|
431
|
+
# TTL mapping by frequency
|
|
432
|
+
ttl_by_frequency = {"always": 0, "daily": 24, "weekly": 168, "never": float("inf")}
|
|
433
|
+
|
|
434
|
+
# Default configuration
|
|
435
|
+
defaults = {"enabled": True, "frequency": "daily", "cache_ttl_hours": 24}
|
|
436
|
+
|
|
437
|
+
# Find project root to ensure we read config from correct location
|
|
438
|
+
project_root = find_project_root(cwd)
|
|
439
|
+
config_path = project_root / ".moai" / "config.json"
|
|
440
|
+
if not config_path.exists():
|
|
441
|
+
return defaults
|
|
442
|
+
|
|
443
|
+
try:
|
|
444
|
+
config = json.loads(config_path.read_text())
|
|
445
|
+
|
|
446
|
+
# Extract moai.version_check section
|
|
447
|
+
moai_config = config.get("moai", {})
|
|
448
|
+
version_check_config = moai_config.get("version_check", {})
|
|
449
|
+
|
|
450
|
+
# Read enabled flag (default: True)
|
|
451
|
+
enabled = version_check_config.get("enabled", defaults["enabled"])
|
|
452
|
+
|
|
453
|
+
# Read frequency (default: "daily")
|
|
454
|
+
frequency = moai_config.get("update_check_frequency", defaults["frequency"])
|
|
455
|
+
|
|
456
|
+
# Validate frequency
|
|
457
|
+
if frequency not in ttl_by_frequency:
|
|
458
|
+
frequency = defaults["frequency"]
|
|
459
|
+
|
|
460
|
+
# Calculate TTL from frequency
|
|
461
|
+
cache_ttl_hours = ttl_by_frequency[frequency]
|
|
462
|
+
|
|
463
|
+
# Allow explicit cache_ttl_hours override
|
|
464
|
+
if "cache_ttl_hours" in version_check_config:
|
|
465
|
+
cache_ttl_hours = version_check_config["cache_ttl_hours"]
|
|
466
|
+
|
|
467
|
+
return {"enabled": enabled, "frequency": frequency, "cache_ttl_hours": cache_ttl_hours}
|
|
468
|
+
|
|
469
|
+
except (OSError, json.JSONDecodeError, KeyError):
|
|
470
|
+
# Config read or parse error - return defaults
|
|
471
|
+
return defaults
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
# @CODE:NETWORK-DETECT-001
|
|
475
|
+
def is_network_available(timeout_seconds: float = 0.1) -> bool:
|
|
476
|
+
"""Quick network availability check using socket.
|
|
477
|
+
|
|
478
|
+
Does NOT check PyPI specifically, just basic connectivity.
|
|
479
|
+
Returns immediately on success (< 50ms typically).
|
|
480
|
+
Returns False on any error without raising exceptions.
|
|
481
|
+
|
|
482
|
+
Args:
|
|
483
|
+
timeout_seconds: Socket timeout in seconds (default 0.1s)
|
|
484
|
+
|
|
485
|
+
Returns:
|
|
486
|
+
True if network appears available, False otherwise
|
|
487
|
+
|
|
488
|
+
Examples:
|
|
489
|
+
>>> is_network_available()
|
|
490
|
+
True # Network is available
|
|
491
|
+
>>> is_network_available(timeout_seconds=0.001)
|
|
492
|
+
False # Timeout too short, returns False
|
|
493
|
+
|
|
494
|
+
TDD History:
|
|
495
|
+
- RED: 3 test scenarios (success, failure, timeout)
|
|
496
|
+
- GREEN: Minimal socket.create_connection implementation
|
|
497
|
+
- REFACTOR: Add error handling for all exception types
|
|
498
|
+
"""
|
|
499
|
+
try:
|
|
500
|
+
# Try connecting to Google's public DNS server (8.8.8.8:53)
|
|
501
|
+
# This is a reliable host that's typically reachable
|
|
502
|
+
connection = socket.create_connection(("8.8.8.8", 53), timeout=timeout_seconds)
|
|
503
|
+
connection.close()
|
|
504
|
+
return True
|
|
505
|
+
except (socket.timeout, OSError, Exception):
|
|
506
|
+
# Any connection error means network is unavailable
|
|
507
|
+
# This includes: timeout, connection refused, network unreachable, etc.
|
|
508
|
+
return False
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
# @CODE:VERSION-DETECT-MAJOR-001
|
|
512
|
+
def is_major_version_change(current: str, latest: str) -> bool:
|
|
513
|
+
"""Detect if version change is a major version bump.
|
|
514
|
+
|
|
515
|
+
A major version change is when the first (major) component increases:
|
|
516
|
+
- 0.8.1 → 1.0.0: True (0 → 1)
|
|
517
|
+
- 1.2.3 → 2.0.0: True (1 → 2)
|
|
518
|
+
- 0.8.1 → 0.9.0: False (0 → 0, minor changed)
|
|
519
|
+
- 1.2.3 → 1.3.0: False (1 → 1)
|
|
520
|
+
|
|
521
|
+
Args:
|
|
522
|
+
current: Current version string (e.g., "0.8.1")
|
|
523
|
+
latest: Latest version string (e.g., "1.0.0")
|
|
524
|
+
|
|
525
|
+
Returns:
|
|
526
|
+
True if major version increased, False otherwise
|
|
527
|
+
|
|
528
|
+
Examples:
|
|
529
|
+
>>> is_major_version_change("0.8.1", "1.0.0")
|
|
530
|
+
True
|
|
531
|
+
>>> is_major_version_change("0.8.1", "0.9.0")
|
|
532
|
+
False
|
|
533
|
+
>>> is_major_version_change("dev", "1.0.0")
|
|
534
|
+
False # Invalid versions return False
|
|
535
|
+
|
|
536
|
+
TDD History:
|
|
537
|
+
- RED: 4 test scenarios (0→1, 1→2, minor, invalid)
|
|
538
|
+
- GREEN: Minimal version parsing and comparison
|
|
539
|
+
- REFACTOR: Improve error handling for invalid versions
|
|
540
|
+
"""
|
|
541
|
+
try:
|
|
542
|
+
# Parse version strings into integer components
|
|
543
|
+
current_parts = [int(x) for x in current.split(".")]
|
|
544
|
+
latest_parts = [int(x) for x in latest.split(".")]
|
|
545
|
+
|
|
546
|
+
# Compare major version (first component)
|
|
547
|
+
if len(current_parts) >= 1 and len(latest_parts) >= 1:
|
|
548
|
+
return latest_parts[0] > current_parts[0]
|
|
549
|
+
|
|
550
|
+
# If parsing succeeds but empty, no major change
|
|
551
|
+
return False
|
|
552
|
+
|
|
553
|
+
except (ValueError, AttributeError, IndexError):
|
|
554
|
+
# Invalid version format - return False (no exception)
|
|
555
|
+
return False
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
# @CODE:VERSION-CACHE-INTEGRATION-001
|
|
559
|
+
def get_package_version_info(cwd: str = ".") -> dict[str, Any]:
|
|
560
|
+
"""Check MoAI-ADK current and latest version with caching and offline support.
|
|
561
|
+
|
|
562
|
+
⭐ CRITICAL GUARANTEE: This function ALWAYS returns the current installed version.
|
|
563
|
+
Network failures, cache issues, and timeouts NEVER result in "unknown" version.
|
|
564
|
+
|
|
565
|
+
Execution flow:
|
|
566
|
+
1. Get current installed version (ALWAYS succeeds) ← CRITICAL
|
|
567
|
+
2. Build minimal result with current version
|
|
568
|
+
3. Try to load from cache (< 50ms) - optional enhancement
|
|
569
|
+
4. If cache valid, return cached latest info
|
|
570
|
+
5. If cache invalid/miss, optionally query PyPI - optional enhancement
|
|
571
|
+
6. Save result to cache for next time - optional
|
|
572
|
+
|
|
573
|
+
Args:
|
|
574
|
+
cwd: Project root directory (for cache location)
|
|
575
|
+
|
|
576
|
+
Returns:
|
|
577
|
+
dict with keys:
|
|
578
|
+
- "current": Current installed version (ALWAYS valid, never empty)
|
|
579
|
+
- "latest": Latest version available on PyPI (may be "unknown")
|
|
580
|
+
- "update_available": Boolean indicating if update is available
|
|
581
|
+
- "upgrade_command": Recommended upgrade command (if update available)
|
|
582
|
+
- "release_notes_url": URL to release notes
|
|
583
|
+
- "is_major_update": Boolean indicating major version change
|
|
584
|
+
|
|
585
|
+
Guarantees:
|
|
586
|
+
- Cache hit (< 24 hours): Returns in ~20ms, no network access ✓
|
|
587
|
+
- Cache miss + online: Query PyPI (1s timeout), cache result ✓
|
|
588
|
+
- Cache miss + offline: Return current version only (~100ms) ✓
|
|
589
|
+
- Network timeout: Returns current + "unknown" latest (~50ms) ✓
|
|
590
|
+
- Any exception: Always returns current version ✓
|
|
591
|
+
|
|
592
|
+
TDD History:
|
|
593
|
+
- RED: 5 test scenarios (network detection, cache integration, offline mode)
|
|
594
|
+
- GREEN: Integrate VersionCache with network detection
|
|
595
|
+
- REFACTOR: Extract cache directory constant, improve error handling
|
|
596
|
+
- Phase 3: Add release_notes_url and is_major_update fields (@CODE:VERSION-INTEGRATE-FIELDS-001)
|
|
597
|
+
- Phase 4: CRITICAL FIX - Always guarantee current version return (@CODE:VERSION-ALWAYS-VALID-001)
|
|
598
|
+
"""
|
|
599
|
+
import importlib.util
|
|
600
|
+
import urllib.error
|
|
601
|
+
import urllib.request
|
|
602
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
603
|
+
|
|
604
|
+
# Import VersionCache from the same directory (using dynamic import for testing compatibility)
|
|
605
|
+
try:
|
|
606
|
+
version_cache_path = Path(__file__).parent / "version_cache.py"
|
|
607
|
+
spec = importlib.util.spec_from_file_location("version_cache", version_cache_path)
|
|
608
|
+
if spec and spec.loader:
|
|
609
|
+
version_cache_module = importlib.util.module_from_spec(spec)
|
|
610
|
+
spec.loader.exec_module(version_cache_module)
|
|
611
|
+
version_cache_class = version_cache_module.VersionCache
|
|
612
|
+
else:
|
|
613
|
+
# Skip caching if module can't be loaded
|
|
614
|
+
version_cache_class = None
|
|
615
|
+
except (ImportError, OSError):
|
|
616
|
+
# Graceful degradation: skip caching on import errors
|
|
617
|
+
version_cache_class = None
|
|
618
|
+
|
|
619
|
+
# 1. Find project root (ensure cache is always in correct location)
|
|
620
|
+
# This prevents creating .moai/cache in wrong locations when hooks run
|
|
621
|
+
# from subdirectories like .claude/hooks/alfred/
|
|
622
|
+
project_root = find_project_root(cwd)
|
|
623
|
+
|
|
624
|
+
# 2. Initialize cache (skip if VersionCache couldn't be imported)
|
|
625
|
+
cache_dir = project_root / CACHE_DIR_NAME
|
|
626
|
+
version_cache = version_cache_class(cache_dir) if version_cache_class else None
|
|
627
|
+
|
|
628
|
+
# 2. Get current installed version first (needed for cache validation)
|
|
629
|
+
current_version = "unknown"
|
|
630
|
+
try:
|
|
631
|
+
current_version = version("moai-adk")
|
|
632
|
+
except PackageNotFoundError:
|
|
633
|
+
current_version = "dev"
|
|
634
|
+
# Dev mode - skip cache and return immediately
|
|
635
|
+
return {
|
|
636
|
+
"current": "dev",
|
|
637
|
+
"latest": "unknown",
|
|
638
|
+
"update_available": False,
|
|
639
|
+
"upgrade_command": "",
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
# 3. Try to load from cache (fast path with version validation)
|
|
643
|
+
if version_cache and version_cache.is_valid():
|
|
644
|
+
cached_info = version_cache.load()
|
|
645
|
+
if cached_info:
|
|
646
|
+
# Only use cache if the cached version matches current installed version
|
|
647
|
+
# This prevents stale cache when package is upgraded locally
|
|
648
|
+
if cached_info.get("current") == current_version:
|
|
649
|
+
# Ensure new fields exist for backward compatibility
|
|
650
|
+
if "release_notes_url" not in cached_info:
|
|
651
|
+
# Add missing fields to old cached data
|
|
652
|
+
cached_info.setdefault("release_notes_url", None)
|
|
653
|
+
cached_info.setdefault("is_major_update", False)
|
|
654
|
+
return cached_info
|
|
655
|
+
# else: cache is stale (version changed), fall through to re-check
|
|
656
|
+
|
|
657
|
+
# 4. Cache miss or stale - need to query PyPI
|
|
658
|
+
result = {
|
|
659
|
+
"current": current_version,
|
|
660
|
+
"latest": "unknown",
|
|
661
|
+
"update_available": False,
|
|
662
|
+
"upgrade_command": "",
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
# 5. Check if version check is enabled in config
|
|
666
|
+
config = get_version_check_config(cwd)
|
|
667
|
+
if not config["enabled"]:
|
|
668
|
+
# Version check disabled - return only current version
|
|
669
|
+
return result
|
|
670
|
+
|
|
671
|
+
# 6. Check network before PyPI query
|
|
672
|
+
if not is_network_available():
|
|
673
|
+
# Offline mode - return current version only
|
|
674
|
+
return result
|
|
675
|
+
|
|
676
|
+
# 7. Network available - query PyPI
|
|
677
|
+
pypi_data = None
|
|
678
|
+
try:
|
|
679
|
+
with timeout_handler(1):
|
|
680
|
+
url = "https://pypi.org/pypi/moai-adk/json"
|
|
681
|
+
headers = {"Accept": "application/json"}
|
|
682
|
+
req = urllib.request.Request(url, headers=headers)
|
|
683
|
+
with urllib.request.urlopen(req, timeout=0.8) as response:
|
|
684
|
+
pypi_data = json.load(response)
|
|
685
|
+
result["latest"] = pypi_data.get("info", {}).get("version", "unknown")
|
|
686
|
+
|
|
687
|
+
# Extract release notes URL from project_urls
|
|
688
|
+
try:
|
|
689
|
+
project_urls = pypi_data.get("info", {}).get("project_urls", {})
|
|
690
|
+
release_url = project_urls.get("Changelog", "")
|
|
691
|
+
if not release_url:
|
|
692
|
+
# Fallback to GitHub releases URL pattern
|
|
693
|
+
release_url = (
|
|
694
|
+
f"https://github.com/modu-ai/moai-adk/releases/tag/v{result['latest']}"
|
|
695
|
+
)
|
|
696
|
+
result["release_notes_url"] = release_url
|
|
697
|
+
except (KeyError, AttributeError, TypeError):
|
|
698
|
+
result["release_notes_url"] = None
|
|
699
|
+
|
|
700
|
+
except (urllib.error.URLError, TimeoutError, Exception):
|
|
701
|
+
# PyPI query failed - return current version
|
|
702
|
+
result["release_notes_url"] = None
|
|
703
|
+
pass
|
|
704
|
+
|
|
705
|
+
# 7. Compare versions (simple comparison)
|
|
706
|
+
if result["current"] != "unknown" and result["latest"] != "unknown":
|
|
707
|
+
try:
|
|
708
|
+
# Parse versions for comparison
|
|
709
|
+
current_parts = [int(x) for x in result["current"].split(".")]
|
|
710
|
+
latest_parts = [int(x) for x in result["latest"].split(".")]
|
|
711
|
+
|
|
712
|
+
# Pad shorter version with zeros
|
|
713
|
+
max_len = max(len(current_parts), len(latest_parts))
|
|
714
|
+
current_parts.extend([0] * (max_len - len(current_parts)))
|
|
715
|
+
latest_parts.extend([0] * (max_len - len(latest_parts)))
|
|
716
|
+
|
|
717
|
+
if latest_parts > current_parts:
|
|
718
|
+
result["update_available"] = True
|
|
719
|
+
result["upgrade_command"] = "uv tool upgrade moai-adk"
|
|
720
|
+
|
|
721
|
+
# Detect major version change
|
|
722
|
+
result["is_major_update"] = is_major_version_change(
|
|
723
|
+
result["current"], result["latest"]
|
|
724
|
+
)
|
|
725
|
+
else:
|
|
726
|
+
result["is_major_update"] = False
|
|
727
|
+
except (ValueError, AttributeError):
|
|
728
|
+
# Version parsing failed - skip comparison
|
|
729
|
+
result["is_major_update"] = False
|
|
730
|
+
pass
|
|
731
|
+
|
|
732
|
+
# 8. Save result to cache (if caching is available)
|
|
733
|
+
if version_cache:
|
|
734
|
+
version_cache.save(result)
|
|
735
|
+
|
|
736
|
+
return result
|
|
737
|
+
|
|
738
|
+
|
|
739
|
+
__all__ = [
|
|
740
|
+
"find_project_root",
|
|
741
|
+
"detect_language",
|
|
742
|
+
"get_git_info",
|
|
743
|
+
"count_specs",
|
|
744
|
+
"get_project_language",
|
|
745
|
+
"get_version_check_config",
|
|
746
|
+
"is_network_available",
|
|
747
|
+
"is_major_version_change",
|
|
748
|
+
"get_package_version_info",
|
|
749
|
+
]
|