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