moai-adk 0.25.4__py3-none-any.whl โ 0.41.0__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 +2 -5
- moai_adk/__main__.py +114 -82
- moai_adk/cli/__init__.py +6 -1
- moai_adk/cli/commands/__init__.py +1 -3
- moai_adk/cli/commands/analyze.py +5 -16
- moai_adk/cli/commands/doctor.py +6 -18
- moai_adk/cli/commands/init.py +60 -126
- moai_adk/cli/commands/language.py +14 -35
- moai_adk/cli/commands/status.py +9 -15
- moai_adk/cli/commands/update.py +1560 -190
- moai_adk/cli/prompts/init_prompts.py +112 -56
- moai_adk/cli/spec_status.py +263 -0
- moai_adk/cli/ui/__init__.py +44 -0
- moai_adk/cli/ui/progress.py +422 -0
- moai_adk/cli/ui/prompts.py +389 -0
- moai_adk/cli/ui/theme.py +129 -0
- moai_adk/cli/worktree/__init__.py +27 -0
- moai_adk/cli/worktree/__main__.py +31 -0
- moai_adk/cli/worktree/cli.py +683 -0
- moai_adk/cli/worktree/exceptions.py +89 -0
- moai_adk/cli/worktree/manager.py +493 -0
- moai_adk/cli/worktree/models.py +65 -0
- moai_adk/cli/worktree/registry.py +422 -0
- moai_adk/core/PHASE2_OPTIMIZATIONS.md +467 -0
- moai_adk/core/analysis/session_analyzer.py +17 -56
- moai_adk/core/claude_integration.py +26 -54
- moai_adk/core/command_helpers.py +10 -10
- moai_adk/core/comprehensive_monitoring_system.py +1183 -0
- moai_adk/core/config/migration.py +19 -9
- moai_adk/core/config/unified.py +610 -0
- moai_adk/core/context_manager.py +6 -12
- moai_adk/core/enterprise_features.py +1404 -0
- moai_adk/core/error_recovery_system.py +747 -116
- moai_adk/core/event_driven_hook_system.py +1371 -0
- moai_adk/core/git/__init__.py +8 -0
- moai_adk/core/git/branch_manager.py +3 -11
- moai_adk/core/git/checkpoint.py +1 -3
- moai_adk/core/git/conflict_detector.py +422 -0
- moai_adk/core/git/event_detector.py +16 -5
- moai_adk/core/git/manager.py +91 -1
- moai_adk/core/input_validation_middleware.py +1006 -0
- moai_adk/core/integration/engine.py +6 -18
- moai_adk/core/integration/integration_tester.py +10 -9
- moai_adk/core/integration/utils.py +1 -1
- moai_adk/core/issue_creator.py +10 -28
- moai_adk/core/jit_context_loader.py +956 -0
- moai_adk/core/jit_enhanced_hook_manager.py +1987 -0
- moai_adk/core/language_config_resolver.py +578 -0
- moai_adk/core/language_validator.py +28 -41
- moai_adk/core/mcp/setup.py +15 -12
- moai_adk/core/merge/__init__.py +9 -0
- moai_adk/core/merge/analyzer.py +666 -0
- moai_adk/core/migration/alfred_to_moai_migrator.py +389 -0
- moai_adk/core/migration/backup_manager.py +131 -12
- moai_adk/core/migration/custom_element_scanner.py +358 -0
- moai_adk/core/migration/file_migrator.py +181 -18
- moai_adk/core/migration/interactive_checkbox_ui.py +499 -0
- moai_adk/core/migration/selective_restorer.py +470 -0
- moai_adk/core/migration/template_utils.py +74 -0
- moai_adk/core/migration/user_selection_ui.py +338 -0
- moai_adk/core/migration/version_detector.py +127 -27
- moai_adk/core/migration/version_migrator.py +47 -12
- moai_adk/core/performance/cache_system.py +8 -10
- moai_adk/core/phase_optimized_hook_scheduler.py +879 -0
- moai_adk/core/project/backup_utils.py +9 -1
- moai_adk/core/project/checker.py +2 -4
- moai_adk/core/project/detector.py +1 -3
- moai_adk/core/project/initializer.py +142 -93
- moai_adk/core/project/phase_executor.py +130 -111
- moai_adk/core/project/validator.py +6 -12
- moai_adk/core/quality/trust_checker.py +39 -37
- moai_adk/core/realtime_monitoring_dashboard.py +1724 -0
- moai_adk/core/robust_json_parser.py +611 -0
- moai_adk/core/rollback_manager.py +116 -164
- moai_adk/core/session_manager.py +10 -26
- moai_adk/core/skill_loading_system.py +579 -0
- moai_adk/core/spec_status_manager.py +64 -74
- moai_adk/core/template/backup.py +48 -22
- moai_adk/core/template/config.py +142 -45
- moai_adk/core/template/merger.py +45 -27
- moai_adk/core/template/processor.py +391 -152
- moai_adk/core/template_engine.py +83 -41
- moai_adk/core/template_variable_synchronizer.py +431 -0
- moai_adk/core/unified_permission_manager.py +745 -0
- moai_adk/core/user_behavior_analytics.py +851 -0
- moai_adk/core/version_sync.py +477 -0
- moai_adk/foundation/__init__.py +56 -0
- moai_adk/foundation/backend.py +1027 -0
- moai_adk/foundation/database.py +1115 -0
- moai_adk/foundation/devops.py +1585 -0
- moai_adk/foundation/ears.py +431 -0
- moai_adk/foundation/frontend.py +870 -0
- moai_adk/foundation/git/commit_templates.py +4 -12
- moai_adk/foundation/git.py +376 -0
- moai_adk/foundation/langs.py +484 -0
- moai_adk/foundation/ml_ops.py +1162 -0
- moai_adk/foundation/testing.py +1524 -0
- moai_adk/foundation/trust/trust_principles.py +23 -72
- moai_adk/foundation/trust/validation_checklist.py +57 -162
- moai_adk/project/__init__.py +0 -0
- moai_adk/project/configuration.py +1084 -0
- moai_adk/project/documentation.py +566 -0
- moai_adk/project/schema.py +447 -0
- moai_adk/statusline/alfred_detector.py +1 -3
- moai_adk/statusline/config.py +18 -6
- moai_adk/statusline/enhanced_output_style_detector.py +23 -15
- moai_adk/statusline/main.py +109 -15
- moai_adk/statusline/renderer.py +85 -69
- moai_adk/statusline/update_checker.py +3 -9
- moai_adk/statusline/version_reader.py +140 -46
- moai_adk/templates/.claude/agents/moai/ai-nano-banana.md +716 -0
- moai_adk/templates/.claude/agents/moai/builder-agent.md +480 -0
- moai_adk/templates/.claude/agents/moai/builder-command.md +1194 -0
- moai_adk/templates/.claude/agents/moai/builder-plugin.md +753 -0
- moai_adk/templates/.claude/agents/moai/builder-skill.md +672 -0
- moai_adk/templates/.claude/agents/moai/expert-backend.md +903 -0
- moai_adk/templates/.claude/agents/moai/expert-database.md +782 -0
- moai_adk/templates/.claude/agents/moai/expert-debug.md +407 -0
- moai_adk/templates/.claude/agents/moai/expert-devops.md +722 -0
- moai_adk/templates/.claude/agents/moai/expert-frontend.md +727 -0
- moai_adk/templates/.claude/agents/moai/expert-performance.md +661 -0
- moai_adk/templates/.claude/agents/moai/expert-refactoring.md +218 -0
- moai_adk/templates/.claude/agents/moai/expert-security.md +525 -0
- moai_adk/templates/.claude/agents/moai/expert-testing.md +737 -0
- moai_adk/templates/.claude/agents/moai/expert-uiux.md +1047 -0
- moai_adk/templates/.claude/agents/moai/manager-claude-code.md +438 -0
- moai_adk/templates/.claude/agents/moai/manager-docs.md +578 -0
- moai_adk/templates/.claude/agents/moai/manager-git.md +1092 -0
- moai_adk/templates/.claude/agents/moai/manager-project.md +897 -0
- moai_adk/templates/.claude/agents/moai/manager-quality.md +640 -0
- moai_adk/templates/.claude/agents/moai/manager-spec.md +815 -0
- moai_adk/templates/.claude/agents/moai/manager-strategy.md +786 -0
- moai_adk/templates/.claude/agents/moai/manager-tdd.md +797 -0
- moai_adk/templates/.claude/agents/moai/mcp-context7.md +464 -0
- moai_adk/templates/.claude/agents/moai/mcp-figma.md +1588 -0
- moai_adk/templates/.claude/agents/moai/mcp-notion.md +795 -0
- moai_adk/templates/.claude/agents/moai/mcp-playwright.md +475 -0
- moai_adk/templates/.claude/agents/moai/mcp-sequential-thinking.md +1038 -0
- moai_adk/templates/.claude/commands/moai/0-project.md +1339 -0
- moai_adk/templates/.claude/commands/moai/1-plan.md +1367 -0
- moai_adk/templates/.claude/commands/moai/2-run.md +825 -0
- moai_adk/templates/.claude/commands/moai/3-sync.md +1358 -0
- moai_adk/templates/.claude/commands/moai/9-feedback.md +320 -0
- moai_adk/templates/.claude/hooks/__init__.py +8 -0
- moai_adk/templates/.claude/hooks/moai/__init__.py +8 -0
- moai_adk/templates/.claude/hooks/moai/lib/README.md +143 -0
- moai_adk/templates/.claude/hooks/moai/lib/__init__.py +104 -0
- moai_adk/templates/.claude/hooks/moai/lib/checkpoint.py +247 -0
- moai_adk/templates/.claude/hooks/moai/lib/common.py +161 -0
- moai_adk/templates/.claude/hooks/moai/lib/config_manager.py +444 -0
- moai_adk/templates/.claude/hooks/moai/lib/config_validator.py +639 -0
- moai_adk/templates/.claude/hooks/moai/lib/example_config.json +104 -0
- moai_adk/templates/.claude/hooks/moai/lib/exceptions.py +171 -0
- moai_adk/templates/.claude/hooks/moai/lib/git_operations_manager.py +590 -0
- moai_adk/templates/.claude/hooks/moai/lib/language_validator.py +407 -0
- moai_adk/templates/.claude/hooks/moai/lib/models.py +104 -0
- moai_adk/templates/.claude/hooks/moai/lib/path_utils.py +219 -0
- moai_adk/templates/.claude/hooks/moai/lib/project.py +777 -0
- moai_adk/templates/.claude/hooks/moai/lib/test_hooks_improvements.py +443 -0
- moai_adk/templates/.claude/hooks/moai/lib/timeout.py +160 -0
- moai_adk/templates/.claude/hooks/moai/lib/tool_registry.py +804 -0
- moai_adk/templates/.claude/hooks/moai/lib/unified_timeout_manager.py +542 -0
- moai_adk/templates/.claude/hooks/moai/post_tool__ast_grep_scan.py +256 -0
- moai_adk/templates/.claude/hooks/moai/post_tool__code_formatter.py +253 -0
- moai_adk/templates/.claude/hooks/moai/post_tool__linter.py +307 -0
- moai_adk/templates/.claude/hooks/moai/pre_tool__security_guard.py +231 -0
- moai_adk/templates/.claude/hooks/moai/session_end__auto_cleanup.py +866 -0
- moai_adk/templates/.claude/hooks/moai/session_start__show_project_info.py +1125 -0
- moai_adk/templates/.claude/output-styles/moai/r2d2.md +643 -0
- moai_adk/templates/.claude/output-styles/moai/yoda.md +359 -0
- moai_adk/templates/.claude/settings.json +209 -0
- moai_adk/templates/.claude/skills/moai-ai-nano-banana/SKILL.md +302 -0
- moai_adk/templates/.claude/skills/moai-ai-nano-banana/examples.md +431 -0
- moai_adk/templates/.claude/skills/moai-ai-nano-banana/reference.md +139 -0
- moai_adk/templates/.claude/skills/moai-ai-nano-banana/scripts/batch_generate.py +560 -0
- moai_adk/templates/.claude/skills/moai-ai-nano-banana/scripts/generate_image.py +362 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/SKILL.md +302 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/examples.md +252 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/README.md +56 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/api-documentation.md +120 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/code-documentation.md +152 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/multi-format-output.md +185 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/user-guides.md +207 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/reference.md +234 -0
- moai_adk/templates/.claude/skills/moai-domain-backend/SKILL.md +324 -0
- moai_adk/templates/.claude/skills/moai-domain-backend/examples.md +718 -0
- moai_adk/templates/.claude/skills/moai-domain-backend/reference.md +464 -0
- moai_adk/templates/.claude/skills/moai-domain-database/SKILL.md +326 -0
- moai_adk/templates/.claude/skills/moai-domain-database/examples.md +830 -0
- moai_adk/templates/.claude/skills/moai-domain-database/modules/README.md +53 -0
- moai_adk/templates/.claude/skills/moai-domain-database/modules/mongodb.md +231 -0
- moai_adk/templates/.claude/skills/moai-domain-database/modules/postgresql.md +169 -0
- moai_adk/templates/.claude/skills/moai-domain-database/modules/redis.md +262 -0
- moai_adk/templates/.claude/skills/moai-domain-database/reference.md +545 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/SKILL.md +194 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/examples.md +968 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/modules/component-architecture.md +723 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/modules/nextjs16-patterns.md +713 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/modules/performance-optimization.md +694 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/modules/react19-patterns.md +591 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/modules/state-management.md +680 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/modules/vue35-patterns.md +802 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/reference.md +664 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/SKILL.md +454 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/examples.md +560 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/modules/accessibility-wcag.md +260 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/modules/component-architecture.md +228 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/modules/icon-libraries.md +401 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/modules/theming-system.md +373 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/reference.md +243 -0
- moai_adk/templates/.claude/skills/moai-formats-data/SKILL.md +280 -0
- moai_adk/templates/.claude/skills/moai-formats-data/examples.md +804 -0
- moai_adk/templates/.claude/skills/moai-formats-data/modules/README.md +327 -0
- moai_adk/templates/.claude/skills/moai-formats-data/modules/SKILL-MODULARIZATION-TEMPLATE.md +278 -0
- moai_adk/templates/.claude/skills/moai-formats-data/modules/caching-performance.md +459 -0
- moai_adk/templates/.claude/skills/moai-formats-data/modules/data-validation.md +485 -0
- moai_adk/templates/.claude/skills/moai-formats-data/modules/json-optimization.md +374 -0
- moai_adk/templates/.claude/skills/moai-formats-data/modules/toon-encoding.md +308 -0
- moai_adk/templates/.claude/skills/moai-formats-data/reference.md +585 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/SKILL.md +243 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/examples.md +732 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/advanced-agent-patterns.md +370 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/best-practices-checklist.md +616 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-cli-reference-official.md +420 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-custom-slash-commands-official.md +729 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-devcontainers-official.md +381 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-discover-plugins-official.md +379 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-headless-official.md +378 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-hooks-official.md +670 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-iam-official.md +635 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-memory-official.md +543 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-plugin-marketplaces-official.md +308 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-plugins-official.md +432 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-sandboxing-official.md +282 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-settings-official.md +663 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-skills-official.md +467 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-statusline-official.md +293 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-sub-agents-official.md +420 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/complete-configuration-guide.md +175 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/skill-examples.md +1674 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/skill-formatting-guide.md +729 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/sub-agents/sub-agent-examples.md +1513 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/sub-agents/sub-agent-formatting-guide.md +1086 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/sub-agents/sub-agent-integration-patterns.md +1100 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference.md +209 -0
- moai_adk/templates/.claude/skills/moai-foundation-context/SKILL.md +449 -0
- moai_adk/templates/.claude/skills/moai-foundation-context/examples.md +1048 -0
- moai_adk/templates/.claude/skills/moai-foundation-context/reference.md +246 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/SKILL.md +414 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/examples.md +358 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/README.md +296 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/agents-reference.md +359 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/commands-reference.md +432 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/delegation-advanced.md +279 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/delegation-implementation.md +267 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/delegation-patterns.md +228 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/execution-rules.md +687 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/modular-system.md +665 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/patterns.md +22 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/progressive-disclosure.md +649 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/spec-ears-format.md +200 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/spec-first-tdd.md +171 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/spec-tdd-implementation.md +275 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/token-optimization.md +708 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/trust-5-framework.md +239 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/trust-5-implementation.md +244 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/trust-5-validation.md +219 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/reference.md +478 -0
- moai_adk/templates/.claude/skills/moai-foundation-philosopher/SKILL.md +311 -0
- moai_adk/templates/.claude/skills/moai-foundation-philosopher/examples.md +228 -0
- moai_adk/templates/.claude/skills/moai-foundation-philosopher/modules/assumption-matrix.md +80 -0
- moai_adk/templates/.claude/skills/moai-foundation-philosopher/modules/cognitive-bias.md +199 -0
- moai_adk/templates/.claude/skills/moai-foundation-philosopher/modules/first-principles.md +140 -0
- moai_adk/templates/.claude/skills/moai-foundation-philosopher/modules/trade-off-analysis.md +154 -0
- moai_adk/templates/.claude/skills/moai-foundation-philosopher/reference.md +157 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/SKILL.md +363 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/examples.md +1232 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/modules/best-practices.md +261 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/modules/integration-patterns.md +194 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/modules/proactive-analysis.md +229 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/modules/trust5-validation.md +169 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/reference.md +1266 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/scripts/quality-gate.sh +668 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/templates/github-actions-quality.yml +481 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/templates/quality-config.yaml +519 -0
- moai_adk/templates/.claude/skills/moai-lang-cpp/SKILL.md +430 -0
- moai_adk/templates/.claude/skills/moai-lang-cpp/examples.md +17 -0
- moai_adk/templates/.claude/skills/moai-lang-cpp/modules/advanced-patterns.md +401 -0
- moai_adk/templates/.claude/skills/moai-lang-cpp/reference.md +17 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/SKILL.md +193 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/examples.md +585 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/modules/aspnet-core.md +627 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/modules/blazor-components.md +767 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/modules/cqrs-validation.md +626 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/modules/csharp12-features.md +580 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/modules/efcore-patterns.md +622 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/reference.md +403 -0
- moai_adk/templates/.claude/skills/moai-lang-elixir/SKILL.md +394 -0
- moai_adk/templates/.claude/skills/moai-lang-elixir/examples.md +17 -0
- moai_adk/templates/.claude/skills/moai-lang-elixir/modules/advanced-patterns.md +531 -0
- moai_adk/templates/.claude/skills/moai-lang-elixir/reference.md +17 -0
- moai_adk/templates/.claude/skills/moai-lang-flutter/SKILL.md +472 -0
- moai_adk/templates/.claude/skills/moai-lang-flutter/examples.md +1090 -0
- moai_adk/templates/.claude/skills/moai-lang-flutter/reference.md +686 -0
- moai_adk/templates/.claude/skills/moai-lang-go/SKILL.md +377 -0
- moai_adk/templates/.claude/skills/moai-lang-go/examples.md +919 -0
- moai_adk/templates/.claude/skills/moai-lang-go/reference.md +737 -0
- moai_adk/templates/.claude/skills/moai-lang-java/SKILL.md +386 -0
- moai_adk/templates/.claude/skills/moai-lang-java/examples.md +864 -0
- moai_adk/templates/.claude/skills/moai-lang-java/reference.md +291 -0
- moai_adk/templates/.claude/skills/moai-lang-javascript/SKILL.md +418 -0
- moai_adk/templates/.claude/skills/moai-lang-javascript/examples.md +973 -0
- moai_adk/templates/.claude/skills/moai-lang-javascript/reference.md +1543 -0
- moai_adk/templates/.claude/skills/moai-lang-kotlin/SKILL.md +383 -0
- moai_adk/templates/.claude/skills/moai-lang-kotlin/examples.md +1006 -0
- moai_adk/templates/.claude/skills/moai-lang-kotlin/reference.md +562 -0
- moai_adk/templates/.claude/skills/moai-lang-php/SKILL.md +504 -0
- moai_adk/templates/.claude/skills/moai-lang-php/examples.md +17 -0
- moai_adk/templates/.claude/skills/moai-lang-php/modules/advanced-patterns.md +538 -0
- moai_adk/templates/.claude/skills/moai-lang-php/reference.md +17 -0
- moai_adk/templates/.claude/skills/moai-lang-python/SKILL.md +489 -0
- moai_adk/templates/.claude/skills/moai-lang-python/examples.md +977 -0
- moai_adk/templates/.claude/skills/moai-lang-python/reference.md +804 -0
- moai_adk/templates/.claude/skills/moai-lang-r/SKILL.md +389 -0
- moai_adk/templates/.claude/skills/moai-lang-r/examples.md +17 -0
- moai_adk/templates/.claude/skills/moai-lang-r/modules/advanced-patterns.md +489 -0
- moai_adk/templates/.claude/skills/moai-lang-r/reference.md +17 -0
- moai_adk/templates/.claude/skills/moai-lang-ruby/SKILL.md +432 -0
- moai_adk/templates/.claude/skills/moai-lang-ruby/examples.md +17 -0
- moai_adk/templates/.claude/skills/moai-lang-ruby/modules/advanced-patterns.md +309 -0
- moai_adk/templates/.claude/skills/moai-lang-ruby/modules/testing-patterns.md +306 -0
- moai_adk/templates/.claude/skills/moai-lang-ruby/reference.md +17 -0
- moai_adk/templates/.claude/skills/moai-lang-rust/SKILL.md +377 -0
- moai_adk/templates/.claude/skills/moai-lang-rust/examples.md +659 -0
- moai_adk/templates/.claude/skills/moai-lang-rust/reference.md +504 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/SKILL.md +211 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/examples.md +633 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/modules/akka-actors.md +479 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/modules/cats-effect.md +489 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/modules/functional-programming.md +460 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/modules/spark-data.md +498 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/modules/zio-patterns.md +541 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/reference.md +423 -0
- moai_adk/templates/.claude/skills/moai-lang-swift/SKILL.md +191 -0
- moai_adk/templates/.claude/skills/moai-lang-swift/examples.md +918 -0
- moai_adk/templates/.claude/skills/moai-lang-swift/modules/combine-reactive.md +256 -0
- moai_adk/templates/.claude/skills/moai-lang-swift/modules/concurrency.md +270 -0
- moai_adk/templates/.claude/skills/moai-lang-swift/modules/swift6-features.md +265 -0
- moai_adk/templates/.claude/skills/moai-lang-swift/modules/swiftui-patterns.md +314 -0
- moai_adk/templates/.claude/skills/moai-lang-swift/reference.md +672 -0
- moai_adk/templates/.claude/skills/moai-lang-typescript/SKILL.md +364 -0
- moai_adk/templates/.claude/skills/moai-lang-typescript/examples.md +1089 -0
- moai_adk/templates/.claude/skills/moai-lang-typescript/reference.md +731 -0
- moai_adk/templates/.claude/skills/moai-library-mermaid/SKILL.md +164 -0
- moai_adk/templates/.claude/skills/moai-library-mermaid/examples.md +270 -0
- moai_adk/templates/.claude/skills/moai-library-mermaid/modules/advanced-patterns.md +465 -0
- moai_adk/templates/.claude/skills/moai-library-mermaid/modules/optimization.md +440 -0
- moai_adk/templates/.claude/skills/moai-library-mermaid/reference.md +228 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/SKILL.md +335 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/examples.md +592 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/advanced-deployment-patterns.md +182 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/advanced-patterns.md +336 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/configuration.md +350 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/content-architecture-optimization.md +162 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/deployment.md +52 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/framework-core-configuration.md +186 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/i18n-setup.md +55 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/mdx-components.md +52 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/optimization.md +303 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/reference.md +379 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/SKILL.md +373 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/examples.md +575 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/modules/advanced-patterns.md +394 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/modules/optimization.md +278 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/modules/shadcn-components.md +457 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/modules/shadcn-theming.md +373 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/reference.md +74 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/SKILL.md +283 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/examples.md +2446 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/adaptive-mfa.md +233 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/akamai-integration.md +214 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/application-credentials.md +280 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/attack-protection-log-events.md +224 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/attack-protection-overview.md +140 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/bot-detection.md +144 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/breached-password-detection.md +187 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/brute-force-protection.md +189 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/certifications.md +282 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/compliance-overview.md +263 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/continuous-session-protection.md +307 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/customize-mfa.md +177 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/dpop-implementation.md +283 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/fapi-implementation.md +259 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/gdpr-compliance.md +313 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/guardian-configuration.md +269 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/highly-regulated-identity.md +272 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/jwt-fundamentals.md +248 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/mdl-verification.md +210 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/mfa-api-management.md +278 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/mfa-factors.md +226 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/mfa-overview.md +174 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/mtls-sender-constraining.md +316 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/ropg-flow-mfa.md +216 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/security-center.md +325 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/security-guidance.md +277 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/state-parameters.md +177 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/step-up-authentication.md +251 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/suspicious-ip-throttling.md +240 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/tenant-access-control.md +179 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/webauthn-fido.md +235 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/reference.md +224 -0
- moai_adk/templates/.claude/skills/moai-platform-clerk/SKILL.md +425 -0
- moai_adk/templates/.claude/skills/moai-platform-clerk/modules/advanced-patterns.md +417 -0
- moai_adk/templates/.claude/skills/moai-platform-clerk/reference.md +273 -0
- moai_adk/templates/.claude/skills/moai-platform-convex/SKILL.md +228 -0
- moai_adk/templates/.claude/skills/moai-platform-convex/examples.md +506 -0
- moai_adk/templates/.claude/skills/moai-platform-convex/modules/auth-integration.md +421 -0
- moai_adk/templates/.claude/skills/moai-platform-convex/modules/file-storage.md +474 -0
- moai_adk/templates/.claude/skills/moai-platform-convex/modules/reactive-queries.md +302 -0
- moai_adk/templates/.claude/skills/moai-platform-convex/modules/server-functions.md +452 -0
- moai_adk/templates/.claude/skills/moai-platform-convex/reference.md +385 -0
- moai_adk/templates/.claude/skills/moai-platform-firebase-auth/SKILL.md +249 -0
- moai_adk/templates/.claude/skills/moai-platform-firebase-auth/examples.md +514 -0
- moai_adk/templates/.claude/skills/moai-platform-firebase-auth/modules/custom-claims.md +374 -0
- moai_adk/templates/.claude/skills/moai-platform-firebase-auth/modules/phone-auth.md +372 -0
- moai_adk/templates/.claude/skills/moai-platform-firebase-auth/modules/social-auth.md +339 -0
- moai_adk/templates/.claude/skills/moai-platform-firebase-auth/reference.md +382 -0
- moai_adk/templates/.claude/skills/moai-platform-firestore/SKILL.md +230 -0
- moai_adk/templates/.claude/skills/moai-platform-firestore/examples.md +445 -0
- moai_adk/templates/.claude/skills/moai-platform-firestore/modules/offline-cache.md +392 -0
- moai_adk/templates/.claude/skills/moai-platform-firestore/modules/realtime-listeners.md +441 -0
- moai_adk/templates/.claude/skills/moai-platform-firestore/modules/security-rules.md +352 -0
- moai_adk/templates/.claude/skills/moai-platform-firestore/modules/transactions.md +452 -0
- moai_adk/templates/.claude/skills/moai-platform-firestore/reference.md +322 -0
- moai_adk/templates/.claude/skills/moai-platform-neon/SKILL.md +205 -0
- moai_adk/templates/.claude/skills/moai-platform-neon/examples.md +470 -0
- moai_adk/templates/.claude/skills/moai-platform-neon/modules/auto-scaling.md +349 -0
- moai_adk/templates/.claude/skills/moai-platform-neon/modules/branching-workflows.md +354 -0
- moai_adk/templates/.claude/skills/moai-platform-neon/modules/connection-pooling.md +412 -0
- moai_adk/templates/.claude/skills/moai-platform-neon/modules/pitr-backups.md +458 -0
- moai_adk/templates/.claude/skills/moai-platform-neon/reference.md +272 -0
- moai_adk/templates/.claude/skills/moai-platform-railway/SKILL.md +223 -0
- moai_adk/templates/.claude/skills/moai-platform-railway/examples.md +539 -0
- moai_adk/templates/.claude/skills/moai-platform-railway/modules/docker-deployment.md +261 -0
- moai_adk/templates/.claude/skills/moai-platform-railway/modules/multi-service.md +291 -0
- moai_adk/templates/.claude/skills/moai-platform-railway/modules/networking-domains.md +338 -0
- moai_adk/templates/.claude/skills/moai-platform-railway/modules/volumes-storage.md +353 -0
- moai_adk/templates/.claude/skills/moai-platform-railway/reference.md +374 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/SKILL.md +206 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/examples.md +502 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/modules/auth-integration.md +384 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/modules/edge-functions.md +371 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/modules/postgresql-pgvector.md +231 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/modules/realtime-presence.md +354 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/modules/row-level-security.md +286 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/modules/storage-cdn.md +319 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/modules/typescript-patterns.md +453 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/reference.md +284 -0
- moai_adk/templates/.claude/skills/moai-platform-vercel/SKILL.md +209 -0
- moai_adk/templates/.claude/skills/moai-platform-vercel/examples.md +502 -0
- moai_adk/templates/.claude/skills/moai-platform-vercel/modules/analytics-speed.md +348 -0
- moai_adk/templates/.claude/skills/moai-platform-vercel/modules/deployment-config.md +344 -0
- moai_adk/templates/.claude/skills/moai-platform-vercel/modules/edge-functions.md +222 -0
- moai_adk/templates/.claude/skills/moai-platform-vercel/modules/isr-caching.md +306 -0
- moai_adk/templates/.claude/skills/moai-platform-vercel/modules/kv-storage.md +399 -0
- moai_adk/templates/.claude/skills/moai-platform-vercel/reference.md +360 -0
- moai_adk/templates/.claude/skills/moai-plugin-builder/SKILL.md +473 -0
- moai_adk/templates/.claude/skills/moai-plugin-builder/examples.md +621 -0
- moai_adk/templates/.claude/skills/moai-plugin-builder/modules/migration.md +341 -0
- moai_adk/templates/.claude/skills/moai-plugin-builder/modules/validation.md +373 -0
- moai_adk/templates/.claude/skills/moai-plugin-builder/reference.md +464 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/SKILL.md +306 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/examples.md +1099 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/modules/language-specific.md +307 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/modules/pattern-syntax.md +237 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/modules/refactoring-patterns.md +260 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/modules/security-rules.md +239 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/reference.md +288 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/languages/go.yml +90 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/languages/python.yml +101 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/languages/typescript.yml +83 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/quality/complexity-check.yml +94 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/quality/deprecated-apis.yml +84 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/security/secrets-detection.yml +89 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/security/sql-injection.yml +45 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/security/xss-prevention.yml +50 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/sgconfig.yml +54 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/SKILL.md +214 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/examples.md +697 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/index.md +96 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/modules/configure/acp.md +115 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/modules/configure/agents.md +241 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/modules/configure/commands.md +197 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/modules/configure/custom-tools.md +197 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/modules/configure/formatters.md +164 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/modules/configure/keybinds.md +150 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/modules/configure/lsp-servers.md +156 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/modules/configure/mcp-servers.md +214 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/modules/configure/models.md +197 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/modules/configure/permissions.md +162 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/modules/configure/rules.md +129 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/modules/configure/skills.md +192 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/modules/configure/themes.md +200 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/modules/configure/tools.md +169 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/modules/core/config.md +211 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/modules/core/enterprise.md +68 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/modules/core/intro.md +127 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/modules/core/migration-1.0.md +82 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/modules/core/network.md +72 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/modules/core/providers.md +310 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/modules/core/troubleshooting.md +124 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/modules/develop/ecosystem.md +75 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/modules/develop/plugins.md +218 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/modules/develop/sdk.md +266 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/modules/develop/server.md +207 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/modules/usage/cli.md +159 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/modules/usage/github.md +181 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/modules/usage/gitlab.md +122 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/modules/usage/ide.md +74 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/modules/usage/share.md +106 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/modules/usage/tui.md +129 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/modules/usage/zen.md +118 -0
- moai_adk/templates/.claude/skills/moai-tool-opencode/reference.md +790 -0
- moai_adk/templates/.claude/skills/moai-workflow-jit-docs/SKILL.md +211 -0
- moai_adk/templates/.claude/skills/moai-workflow-jit-docs/examples.md +544 -0
- moai_adk/templates/.claude/skills/moai-workflow-jit-docs/modules/advanced-patterns.md +379 -0
- moai_adk/templates/.claude/skills/moai-workflow-jit-docs/modules/optimization.md +286 -0
- moai_adk/templates/.claude/skills/moai-workflow-jit-docs/reference.md +307 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/README.md +190 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/SKILL.md +260 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/examples.md +547 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/reference.md +275 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/schemas/config-schema.json +316 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/schemas/tab_schema.json +1434 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/config-template.json +71 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/doc-templates/product-template.md +44 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/doc-templates/structure-template.md +48 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/doc-templates/tech-template.md +92 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/config-manager-setup.json +109 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/language-initializer.json +228 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/menu-project-config.json +130 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/project-batch-questions.json +97 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/spec-workflow-setup.json +150 -0
- moai_adk/templates/.claude/skills/moai-workflow-spec/SKILL.md +336 -0
- moai_adk/templates/.claude/skills/moai-workflow-spec/examples.md +900 -0
- moai_adk/templates/.claude/skills/moai-workflow-spec/modules/advanced-patterns.md +237 -0
- moai_adk/templates/.claude/skills/moai-workflow-spec/reference.md +704 -0
- moai_adk/templates/.claude/skills/moai-workflow-templates/SKILL.md +386 -0
- moai_adk/templates/.claude/skills/moai-workflow-templates/examples.md +552 -0
- moai_adk/templates/.claude/skills/moai-workflow-templates/modules/code-templates.md +124 -0
- moai_adk/templates/.claude/skills/moai-workflow-templates/modules/feedback-templates.md +100 -0
- moai_adk/templates/.claude/skills/moai-workflow-templates/modules/template-optimizer.md +138 -0
- moai_adk/templates/.claude/skills/moai-workflow-templates/reference.md +346 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/LICENSE.txt +202 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/SKILL.md +307 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/examples/ai-powered-testing.py +294 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/examples/console_logging.py +35 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/examples/element_discovery.py +40 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/examples/static_html_automation.py +34 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/examples.md +672 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/README.md +269 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/advanced-patterns.md +576 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/ai-debugging.md +302 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/context7-integration.md +286 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/review-workflows.md +500 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework/relevance-analysis.md +154 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework/safety-analysis.md +148 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework/scoring-algorithms.md +196 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework/timeliness-analysis.md +168 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework/truthfulness-analysis.md +136 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework/usability-analysis.md +153 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework.md +257 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review.md +263 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/code-review/analysis-patterns.md +340 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/code-review/core-classes.md +299 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/code-review/tool-integration.md +380 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/debugging/debugging-workflows.md +451 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/debugging/error-analysis.md +442 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/optimization.md +505 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance/optimization-patterns.md +473 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance/profiling-techniques.md +481 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization/ai-optimization.md +241 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization/bottleneck-detection.md +397 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization/optimization-plan.md +315 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization/profiler-core.md +277 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization/real-time-monitoring.md +187 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization.md +327 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/quality-metrics.md +415 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/refactoring/ai-workflows.md +620 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/refactoring/patterns.md +692 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/security-analysis.md +429 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/smart-refactoring.md +313 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/static-analysis.md +438 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd/core-classes.md +397 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd-context7/advanced-features.md +494 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd-context7/red-green-refactor.md +316 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd-context7/test-generation.md +471 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd-context7/test-patterns.md +371 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd-context7.md +265 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/trust5-validation.md +428 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/reference/playwright-best-practices.md +57 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/reference.md +440 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/scripts/with_server.py +218 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/templates/alfred-integration.md +376 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/workflows/enterprise-testing-workflow.py +571 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/SKILL.md +227 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/examples.md +606 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/integration-patterns.md +149 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/moai-adk-integration.md +245 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/parallel-advanced.md +310 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/parallel-development.md +202 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/parallel-workflows.md +302 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/registry-architecture.md +271 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/resource-optimization.md +300 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/tools-integration.md +280 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/troubleshooting.md +397 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/worktree-commands.md +296 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/worktree-management.md +217 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/reference.md +357 -0
- moai_adk/templates/.git-hooks/pre-commit +103 -41
- moai_adk/templates/.git-hooks/pre-push +244 -31
- moai_adk/templates/.github/workflows/ci-universal.yml +513 -0
- moai_adk/templates/.github/workflows/security-secrets-check.yml +179 -0
- moai_adk/templates/.gitignore +181 -44
- moai_adk/templates/.lsp.json +152 -0
- moai_adk/templates/.mcp.json +6 -15
- moai_adk/templates/.moai/config/config.yaml +58 -0
- moai_adk/templates/.moai/config/multilingual-triggers.yaml +213 -0
- moai_adk/templates/.moai/config/questions/_schema.yaml +174 -0
- moai_adk/templates/.moai/config/questions/tab0-init.yaml +259 -0
- moai_adk/templates/.moai/config/questions/tab1-user.yaml +107 -0
- moai_adk/templates/.moai/config/questions/tab2-project.yaml +79 -0
- moai_adk/templates/.moai/config/questions/tab3-git.yaml +632 -0
- moai_adk/templates/.moai/config/questions/tab4-quality.yaml +183 -0
- moai_adk/templates/.moai/config/questions/tab5-system.yaml +96 -0
- moai_adk/templates/.moai/config/sections/git-strategy.yaml +116 -0
- moai_adk/templates/.moai/config/sections/language.yaml +11 -0
- moai_adk/templates/.moai/config/sections/project.yaml +13 -0
- moai_adk/templates/.moai/config/sections/quality.yaml +18 -0
- moai_adk/templates/.moai/config/sections/system.yaml +24 -0
- moai_adk/templates/.moai/config/sections/user.yaml +5 -0
- moai_adk/templates/.moai/config/statusline-config.yaml +92 -0
- moai_adk/templates/.moai/scripts/setup-glm.py +136 -0
- moai_adk/templates/CLAUDE.md +804 -499
- moai_adk/utils/__init__.py +24 -1
- moai_adk/utils/banner.py +7 -10
- moai_adk/utils/common.py +49 -30
- moai_adk/utils/link_validator.py +4 -12
- moai_adk/utils/safe_file_reader.py +2 -6
- moai_adk/utils/timeout.py +160 -0
- moai_adk/utils/toon_utils.py +256 -0
- moai_adk/version.py +22 -0
- moai_adk-0.41.0.dist-info/METADATA +3274 -0
- moai_adk-0.41.0.dist-info/RECORD +683 -0
- {moai_adk-0.25.4.dist-info โ moai_adk-0.41.0.dist-info}/WHEEL +1 -1
- {moai_adk-0.25.4.dist-info โ moai_adk-0.41.0.dist-info}/entry_points.txt +1 -0
- moai_adk/cli/commands/backup.py +0 -82
- moai_adk/cli/commands/improve_user_experience.py +0 -348
- moai_adk/cli/commands/migrate.py +0 -158
- moai_adk/cli/commands/validate_links.py +0 -118
- moai_adk/core/config/auto_spec_config.py +0 -346
- moai_adk/core/hooks/post_tool_auto_spec_completion.py +0 -925
- moai_adk/core/spec/confidence_scoring.py +0 -749
- moai_adk/core/spec/ears_template_engine.py +0 -1182
- moai_adk/core/spec/quality_validator.py +0 -721
- moai_adk/templates/.github/workflows/moai-gitflow.yml +0 -413
- moai_adk/templates/.github/workflows/moai-release-create.yml +0 -100
- moai_adk/templates/.github/workflows/moai-release-pipeline.yml +0 -188
- moai_adk/utils/user_experience.py +0 -531
- moai_adk-0.25.4.dist-info/METADATA +0 -2279
- moai_adk-0.25.4.dist-info/RECORD +0 -112
- {moai_adk-0.25.4.dist-info โ moai_adk-0.41.0.dist-info}/licenses/LICENSE +0 -0
moai_adk/cli/commands/update.py
CHANGED
|
@@ -44,18 +44,28 @@ from __future__ import annotations
|
|
|
44
44
|
|
|
45
45
|
import json
|
|
46
46
|
import logging
|
|
47
|
+
import shutil
|
|
47
48
|
import subprocess
|
|
48
49
|
from datetime import datetime
|
|
49
50
|
from pathlib import Path
|
|
50
|
-
from typing import Any, cast
|
|
51
|
+
from typing import Any, Union, cast
|
|
51
52
|
|
|
52
53
|
import click
|
|
54
|
+
import yaml
|
|
53
55
|
from packaging import version
|
|
54
56
|
from rich.console import Console
|
|
55
57
|
|
|
56
58
|
from moai_adk import __version__
|
|
59
|
+
from moai_adk.core.merge import MergeAnalyzer
|
|
57
60
|
from moai_adk.core.migration import VersionMigrator
|
|
61
|
+
from moai_adk.core.migration.alfred_to_moai_migrator import AlfredToMoaiMigrator
|
|
62
|
+
|
|
63
|
+
# Import new custom element restoration modules
|
|
64
|
+
from moai_adk.core.migration.custom_element_scanner import create_custom_element_scanner
|
|
65
|
+
from moai_adk.core.migration.selective_restorer import create_selective_restorer
|
|
66
|
+
from moai_adk.core.migration.user_selection_ui import create_user_selection_ui
|
|
58
67
|
from moai_adk.core.template.processor import TemplateProcessor
|
|
68
|
+
from moai_adk.utils.common import reset_stdin
|
|
59
69
|
|
|
60
70
|
console = Console()
|
|
61
71
|
logger = logging.getLogger(__name__)
|
|
@@ -98,6 +108,45 @@ class TemplateSyncError(UpdateError):
|
|
|
98
108
|
pass
|
|
99
109
|
|
|
100
110
|
|
|
111
|
+
def _get_config_path(project_path: Path) -> tuple[Path, bool]:
|
|
112
|
+
"""Get config file path, preferring YAML over JSON.
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
Tuple of (config_path, is_yaml)
|
|
116
|
+
"""
|
|
117
|
+
yaml_path = project_path / ".moai" / "config" / "config.yaml"
|
|
118
|
+
json_path = project_path / ".moai" / "config" / "config.json"
|
|
119
|
+
|
|
120
|
+
if yaml_path.exists():
|
|
121
|
+
return yaml_path, True
|
|
122
|
+
return json_path, False
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _load_config(config_path: Path) -> dict[str, Any]:
|
|
126
|
+
"""Load config from YAML or JSON file."""
|
|
127
|
+
if not config_path.exists():
|
|
128
|
+
return {}
|
|
129
|
+
|
|
130
|
+
is_yaml = config_path.suffix in (".yaml", ".yml")
|
|
131
|
+
content = config_path.read_text(encoding="utf-8")
|
|
132
|
+
|
|
133
|
+
if is_yaml:
|
|
134
|
+
return yaml.safe_load(content) or {}
|
|
135
|
+
return json.loads(content)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _save_config(config_path: Path, config_data: dict[str, Any]) -> None:
|
|
139
|
+
"""Save config to YAML or JSON file."""
|
|
140
|
+
is_yaml = config_path.suffix in (".yaml", ".yml")
|
|
141
|
+
|
|
142
|
+
if is_yaml:
|
|
143
|
+
content = yaml.safe_dump(config_data, default_flow_style=False, allow_unicode=True, sort_keys=False)
|
|
144
|
+
else:
|
|
145
|
+
content = json.dumps(config_data, indent=2, ensure_ascii=False) + "\n"
|
|
146
|
+
|
|
147
|
+
config_path.write_text(content, encoding="utf-8")
|
|
148
|
+
|
|
149
|
+
|
|
101
150
|
def _is_installed_via_uv_tool() -> bool:
|
|
102
151
|
"""Check if moai-adk installed via uv tool.
|
|
103
152
|
|
|
@@ -220,9 +269,7 @@ def _get_latest_version() -> str:
|
|
|
220
269
|
import urllib.request
|
|
221
270
|
|
|
222
271
|
url = "https://pypi.org/pypi/moai-adk/json"
|
|
223
|
-
with urllib.request.urlopen(
|
|
224
|
-
url, timeout=5
|
|
225
|
-
) as response: # nosec B310 - URL is hardcoded HTTPS to PyPI API, no user input
|
|
272
|
+
with urllib.request.urlopen(url, timeout=5) as response: # nosec B310 - URL is hardcoded HTTPS to PyPI API, no user input
|
|
226
273
|
data = json.loads(response.read().decode("utf-8"))
|
|
227
274
|
return cast(str, data["info"]["version"])
|
|
228
275
|
except (urllib.error.URLError, json.JSONDecodeError, KeyError, TimeoutError) as e:
|
|
@@ -280,42 +327,398 @@ def _get_project_config_version(project_path: Path) -> str:
|
|
|
280
327
|
Returns "0.0.0" if template_version field not found (indicates no prior sync)
|
|
281
328
|
|
|
282
329
|
Raises:
|
|
283
|
-
ValueError: If config
|
|
330
|
+
ValueError: If config file exists but cannot be parsed
|
|
284
331
|
"""
|
|
285
332
|
|
|
286
|
-
def
|
|
333
|
+
def _is_placeholder_val(value: str) -> bool:
|
|
287
334
|
"""Check if value contains unsubstituted template placeholders."""
|
|
288
|
-
return (
|
|
289
|
-
isinstance(value, str) and value.startswith("{{") and value.endswith("}}")
|
|
290
|
-
)
|
|
335
|
+
return isinstance(value, str) and value.startswith("{{") and value.endswith("}}")
|
|
291
336
|
|
|
292
|
-
config_path = project_path
|
|
337
|
+
config_path, _ = _get_config_path(project_path)
|
|
293
338
|
|
|
294
339
|
if not config_path.exists():
|
|
295
340
|
# No config yet, treat as version 0.0.0 (needs initial sync)
|
|
296
341
|
return "0.0.0"
|
|
297
342
|
|
|
298
343
|
try:
|
|
299
|
-
config_data =
|
|
344
|
+
config_data = _load_config(config_path)
|
|
300
345
|
# Check for template_version in project section
|
|
301
346
|
template_version = config_data.get("project", {}).get("template_version")
|
|
302
|
-
if template_version and not
|
|
347
|
+
if template_version and not _is_placeholder_val(template_version):
|
|
303
348
|
return template_version
|
|
304
349
|
|
|
305
350
|
# Fallback to moai version if no template_version exists
|
|
306
351
|
moai_version = config_data.get("moai", {}).get("version")
|
|
307
|
-
if moai_version and not
|
|
352
|
+
if moai_version and not _is_placeholder_val(moai_version):
|
|
308
353
|
return moai_version
|
|
309
354
|
|
|
310
355
|
# If values are placeholders or don't exist, treat as uninitialized (0.0.0 triggers sync)
|
|
311
356
|
return "0.0.0"
|
|
312
|
-
except json.JSONDecodeError as e:
|
|
313
|
-
raise ValueError(f"Failed to parse project config
|
|
357
|
+
except (json.JSONDecodeError, yaml.YAMLError) as e:
|
|
358
|
+
raise ValueError(f"Failed to parse project config: {e}") from e
|
|
314
359
|
|
|
315
360
|
|
|
316
|
-
def
|
|
317
|
-
|
|
318
|
-
|
|
361
|
+
def _ask_merge_strategy(yes: bool = False) -> str:
|
|
362
|
+
"""
|
|
363
|
+
Ask user to choose merge strategy via CLI prompt.
|
|
364
|
+
|
|
365
|
+
Args:
|
|
366
|
+
yes: If True, auto-select "auto" (for --yes flag)
|
|
367
|
+
|
|
368
|
+
Returns:
|
|
369
|
+
"auto" or "manual"
|
|
370
|
+
"""
|
|
371
|
+
if yes:
|
|
372
|
+
return "auto"
|
|
373
|
+
|
|
374
|
+
console.print("\n[cyan]๐ Choose merge strategy:[/cyan]")
|
|
375
|
+
console.print("[cyan] [1] Auto-merge (default)[/cyan]")
|
|
376
|
+
console.print("[dim] โ Template installs fresh + user changes preserved + minimal conflicts[/dim]")
|
|
377
|
+
console.print("[cyan] [2] Manual merge[/cyan]")
|
|
378
|
+
console.print("[dim] โ Backup preserved + merge guide generated + you control merging[/dim]")
|
|
379
|
+
|
|
380
|
+
response = click.prompt("Select [1 or 2]", default="1")
|
|
381
|
+
if response == "2":
|
|
382
|
+
return "manual"
|
|
383
|
+
return "auto"
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def _generate_manual_merge_guide(backup_path: Path, template_path: Path, project_path: Path) -> Path:
|
|
387
|
+
"""
|
|
388
|
+
Generate comprehensive merge guide for manual merging.
|
|
389
|
+
|
|
390
|
+
Args:
|
|
391
|
+
backup_path: Path to backup directory
|
|
392
|
+
template_path: Path to template directory
|
|
393
|
+
project_path: Project root path
|
|
394
|
+
|
|
395
|
+
Returns:
|
|
396
|
+
Path to generated merge guide
|
|
397
|
+
"""
|
|
398
|
+
guide_dir = project_path / ".moai" / "guides"
|
|
399
|
+
guide_dir.mkdir(parents=True, exist_ok=True)
|
|
400
|
+
|
|
401
|
+
guide_path = guide_dir / "merge-guide.md"
|
|
402
|
+
|
|
403
|
+
# Find changed files
|
|
404
|
+
changed_files = []
|
|
405
|
+
backup_claude = backup_path / ".claude"
|
|
406
|
+
backup_path / ".moai"
|
|
407
|
+
|
|
408
|
+
# Compare .claude/
|
|
409
|
+
if backup_claude.exists():
|
|
410
|
+
for file in backup_claude.rglob("*"):
|
|
411
|
+
if file.is_file():
|
|
412
|
+
rel_path = file.relative_to(backup_path)
|
|
413
|
+
current_file = project_path / rel_path
|
|
414
|
+
if current_file.exists():
|
|
415
|
+
if file.read_text(encoding="utf-8", errors="ignore") != current_file.read_text(
|
|
416
|
+
encoding="utf-8", errors="ignore"
|
|
417
|
+
):
|
|
418
|
+
changed_files.append(f" - {rel_path}")
|
|
419
|
+
else:
|
|
420
|
+
changed_files.append(f" - {rel_path} (new)")
|
|
421
|
+
|
|
422
|
+
# Generate guide
|
|
423
|
+
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
424
|
+
guide_content = f"""# Merge Guide - Manual Merge Mode
|
|
425
|
+
|
|
426
|
+
**Generated**: {timestamp}
|
|
427
|
+
**Backup Location**: `{backup_path.relative_to(project_path)}/`
|
|
428
|
+
|
|
429
|
+
## Summary
|
|
430
|
+
|
|
431
|
+
During this update, the following files were changed:
|
|
432
|
+
|
|
433
|
+
{chr(10).join(changed_files) if changed_files else " (No changes detected)"}
|
|
434
|
+
|
|
435
|
+
## How to Merge
|
|
436
|
+
|
|
437
|
+
### Option 1: Using diff (Terminal)
|
|
438
|
+
|
|
439
|
+
```bash
|
|
440
|
+
# Compare specific files
|
|
441
|
+
diff {backup_path.name}/.claude/settings.json .claude/settings.json
|
|
442
|
+
|
|
443
|
+
# View all differences
|
|
444
|
+
diff -r {backup_path.name}/ .
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
### Option 2: Using Visual Merge Tool
|
|
448
|
+
|
|
449
|
+
```bash
|
|
450
|
+
# macOS/Linux - Using meld
|
|
451
|
+
meld {backup_path.relative_to(project_path)}/ .
|
|
452
|
+
|
|
453
|
+
# Using VSCode
|
|
454
|
+
code --diff {backup_path.relative_to(project_path)}/.claude/settings.json .claude/settings.json
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
### Option 3: Manual Line-by-Line
|
|
458
|
+
|
|
459
|
+
1. Open backup file in your editor
|
|
460
|
+
2. Open current file side-by-side
|
|
461
|
+
3. Manually copy your customizations
|
|
462
|
+
|
|
463
|
+
## Key Files to Review
|
|
464
|
+
|
|
465
|
+
### .claude/settings.json
|
|
466
|
+
- Contains MCP servers, hooks, environment variables
|
|
467
|
+
- **Action**: Restore any custom MCP servers and environment variables
|
|
468
|
+
- **Location**: {backup_path.relative_to(project_path)}/.claude/settings.json
|
|
469
|
+
|
|
470
|
+
### .moai/config/config.json
|
|
471
|
+
- Contains project configuration and metadata
|
|
472
|
+
- **Action**: Verify user-specific settings are preserved
|
|
473
|
+
- **Location**: {backup_path.relative_to(project_path)}/.moai/config/config.json
|
|
474
|
+
|
|
475
|
+
### .claude/commands/, .claude/agents/, .claude/hooks/
|
|
476
|
+
- Contains custom scripts and automation
|
|
477
|
+
- **Action**: Restore any custom scripts outside of /moai/ folders
|
|
478
|
+
- **Location**: {backup_path.relative_to(project_path)}/.claude/
|
|
479
|
+
|
|
480
|
+
## Migration Checklist
|
|
481
|
+
|
|
482
|
+
- [ ] Compare `.claude/settings.json`
|
|
483
|
+
- [ ] Restore custom MCP servers
|
|
484
|
+
- [ ] Restore environment variables
|
|
485
|
+
- [ ] Verify hooks are properly configured
|
|
486
|
+
|
|
487
|
+
- [ ] Review `.moai/config/config.json`
|
|
488
|
+
- [ ] Check version was updated
|
|
489
|
+
- [ ] Verify user settings preserved
|
|
490
|
+
|
|
491
|
+
- [ ] Restore custom scripts
|
|
492
|
+
- [ ] Any custom commands outside /moai/
|
|
493
|
+
- [ ] Any custom agents outside /moai/
|
|
494
|
+
- [ ] Any custom hooks outside /moai/
|
|
495
|
+
|
|
496
|
+
- [ ] Run tests
|
|
497
|
+
```bash
|
|
498
|
+
uv run pytest
|
|
499
|
+
moai-adk validate
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
- [ ] Commit changes
|
|
503
|
+
```bash
|
|
504
|
+
git add .
|
|
505
|
+
git commit -m "merge: Update templates with manual merge"
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
## Rollback if Needed
|
|
509
|
+
|
|
510
|
+
If you want to cancel and restore the backup:
|
|
511
|
+
|
|
512
|
+
```bash
|
|
513
|
+
# Restore everything from backup
|
|
514
|
+
cp -r {backup_path.relative_to(project_path)}/.claude .
|
|
515
|
+
cp -r {backup_path.relative_to(project_path)}/.moai .
|
|
516
|
+
cp {backup_path.relative_to(project_path)}/CLAUDE.md .
|
|
517
|
+
|
|
518
|
+
# Or restore specific files
|
|
519
|
+
cp {backup_path.relative_to(project_path)}/.claude/settings.json .claude/
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
## Questions?
|
|
523
|
+
|
|
524
|
+
If you encounter merge conflicts or issues:
|
|
525
|
+
|
|
526
|
+
1. Check the backup folder for original files
|
|
527
|
+
2. Compare line-by-line using diff tools
|
|
528
|
+
3. Consult documentation: https://adk.mo.ai.kr/update-merge
|
|
529
|
+
|
|
530
|
+
---
|
|
531
|
+
|
|
532
|
+
**Backup**: `{backup_path}/`
|
|
533
|
+
**Generated**: {timestamp}
|
|
534
|
+
"""
|
|
535
|
+
|
|
536
|
+
guide_path.write_text(guide_content, encoding="utf-8")
|
|
537
|
+
logger.info(f"โ
Merge guide created: {guide_path}")
|
|
538
|
+
return guide_path
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
def _migrate_legacy_logs(project_path: Path, dry_run: bool = False) -> bool:
|
|
542
|
+
"""Migrate legacy log files to unified directory structure.
|
|
543
|
+
|
|
544
|
+
Creates new unified directory structure (.moai/docs/, .moai/logs/archive/) and
|
|
545
|
+
migrates files from legacy locations to new unified structure:
|
|
546
|
+
- .moai/memory/last-session-state.json โ .moai/logs/sessions/
|
|
547
|
+
- .moai/error_logs/ โ .moai/logs/errors/
|
|
548
|
+
- .moai/reports/ โ .moai/docs/reports/
|
|
549
|
+
|
|
550
|
+
Args:
|
|
551
|
+
project_path: Project directory path (absolute)
|
|
552
|
+
dry_run: If True, only simulate migration without making changes
|
|
553
|
+
|
|
554
|
+
Returns:
|
|
555
|
+
True if migration succeeded or no migration needed, False otherwise
|
|
556
|
+
|
|
557
|
+
Raises:
|
|
558
|
+
Exception: If migration fails during actual execution
|
|
559
|
+
"""
|
|
560
|
+
try:
|
|
561
|
+
# Define source and target directories
|
|
562
|
+
legacy_memory = project_path / ".moai" / "memory"
|
|
563
|
+
legacy_error_logs = project_path / ".moai" / "error_logs"
|
|
564
|
+
legacy_reports = project_path / ".moai" / "reports"
|
|
565
|
+
|
|
566
|
+
# Create new unified directory structure
|
|
567
|
+
new_logs_dir = project_path / ".moai" / "logs"
|
|
568
|
+
new_docs_dir = project_path / ".moai" / "docs"
|
|
569
|
+
new_sessions_dir = new_logs_dir / "sessions"
|
|
570
|
+
new_errors_dir = new_logs_dir / "errors"
|
|
571
|
+
new_archive_dir = new_logs_dir / "archive"
|
|
572
|
+
new_docs_reports_dir = new_docs_dir / "reports"
|
|
573
|
+
|
|
574
|
+
migration_log = []
|
|
575
|
+
files_migrated = 0
|
|
576
|
+
files_skipped = 0
|
|
577
|
+
|
|
578
|
+
# Check if any legacy directories exist
|
|
579
|
+
has_legacy_files = legacy_memory.exists() or legacy_error_logs.exists() or legacy_reports.exists()
|
|
580
|
+
|
|
581
|
+
if not has_legacy_files:
|
|
582
|
+
if not dry_run:
|
|
583
|
+
# Create new directory structure anyway for consistency
|
|
584
|
+
new_logs_dir.mkdir(parents=True, exist_ok=True)
|
|
585
|
+
new_docs_dir.mkdir(parents=True, exist_ok=True)
|
|
586
|
+
new_sessions_dir.mkdir(parents=True, exist_ok=True)
|
|
587
|
+
new_errors_dir.mkdir(parents=True, exist_ok=True)
|
|
588
|
+
new_archive_dir.mkdir(parents=True, exist_ok=True)
|
|
589
|
+
new_docs_reports_dir.mkdir(parents=True, exist_ok=True)
|
|
590
|
+
return True
|
|
591
|
+
|
|
592
|
+
if dry_run:
|
|
593
|
+
console.print("[cyan]๐ Legacy log migration (dry run):[/cyan]")
|
|
594
|
+
|
|
595
|
+
# Create new directories if not dry run
|
|
596
|
+
if not dry_run:
|
|
597
|
+
new_logs_dir.mkdir(parents=True, exist_ok=True)
|
|
598
|
+
new_docs_dir.mkdir(parents=True, exist_ok=True)
|
|
599
|
+
new_sessions_dir.mkdir(parents=True, exist_ok=True)
|
|
600
|
+
new_errors_dir.mkdir(parents=True, exist_ok=True)
|
|
601
|
+
new_archive_dir.mkdir(parents=True, exist_ok=True)
|
|
602
|
+
new_docs_reports_dir.mkdir(parents=True, exist_ok=True)
|
|
603
|
+
|
|
604
|
+
# Migration 1: .moai/memory/last-session-state.json โ .moai/logs/sessions/
|
|
605
|
+
if legacy_memory.exists():
|
|
606
|
+
session_file = legacy_memory / "last-session-state.json"
|
|
607
|
+
if session_file.exists():
|
|
608
|
+
target_file = new_sessions_dir / "last-session-state.json"
|
|
609
|
+
|
|
610
|
+
if target_file.exists():
|
|
611
|
+
files_skipped += 1
|
|
612
|
+
migration_log.append(f"Skipped: {session_file.relative_to(project_path)} (target already exists)")
|
|
613
|
+
else:
|
|
614
|
+
if not dry_run:
|
|
615
|
+
shutil.copy2(session_file, target_file)
|
|
616
|
+
# Preserve original timestamp
|
|
617
|
+
shutil.copystat(session_file, target_file)
|
|
618
|
+
src_path = session_file.relative_to(project_path)
|
|
619
|
+
dst_path = target_file.relative_to(project_path)
|
|
620
|
+
migration_log.append(f"Migrated: {src_path} โ {dst_path}")
|
|
621
|
+
else:
|
|
622
|
+
src_path = session_file.relative_to(project_path)
|
|
623
|
+
dst_path = target_file.relative_to(project_path)
|
|
624
|
+
migration_log.append(f"Would migrate: {src_path} โ {dst_path}")
|
|
625
|
+
files_migrated += 1
|
|
626
|
+
|
|
627
|
+
# Migration 2: .moai/error_logs/ โ .moai/logs/errors/
|
|
628
|
+
if legacy_error_logs.exists() and legacy_error_logs.is_dir():
|
|
629
|
+
for error_file in legacy_error_logs.rglob("*"):
|
|
630
|
+
if error_file.is_file():
|
|
631
|
+
relative_path = error_file.relative_to(legacy_error_logs)
|
|
632
|
+
target_file = new_errors_dir / relative_path
|
|
633
|
+
|
|
634
|
+
# Ensure target directory exists
|
|
635
|
+
if not dry_run:
|
|
636
|
+
target_file.parent.mkdir(parents=True, exist_ok=True)
|
|
637
|
+
|
|
638
|
+
if target_file.exists():
|
|
639
|
+
files_skipped += 1
|
|
640
|
+
error_path = error_file.relative_to(project_path)
|
|
641
|
+
migration_log.append(f"Skipped: {error_path} (target already exists)")
|
|
642
|
+
else:
|
|
643
|
+
if not dry_run:
|
|
644
|
+
shutil.copy2(error_file, target_file)
|
|
645
|
+
shutil.copystat(error_file, target_file)
|
|
646
|
+
error_path = error_file.relative_to(project_path)
|
|
647
|
+
target_path = target_file.relative_to(project_path)
|
|
648
|
+
migration_log.append(f"Migrated: {error_path} โ {target_path}")
|
|
649
|
+
else:
|
|
650
|
+
error_path = error_file.relative_to(project_path)
|
|
651
|
+
target_path = target_file.relative_to(project_path)
|
|
652
|
+
migration_log.append(f"Would migrate: {error_path} โ {target_path}")
|
|
653
|
+
files_migrated += 1
|
|
654
|
+
|
|
655
|
+
# Migration 3: .moai/reports/ โ .moai/docs/reports/
|
|
656
|
+
if legacy_reports.exists() and legacy_reports.is_dir():
|
|
657
|
+
for report_file in legacy_reports.rglob("*"):
|
|
658
|
+
if report_file.is_file():
|
|
659
|
+
relative_path = report_file.relative_to(legacy_reports)
|
|
660
|
+
target_file = new_docs_reports_dir / relative_path
|
|
661
|
+
|
|
662
|
+
# Ensure target directory exists
|
|
663
|
+
if not dry_run:
|
|
664
|
+
target_file.parent.mkdir(parents=True, exist_ok=True)
|
|
665
|
+
|
|
666
|
+
if target_file.exists():
|
|
667
|
+
files_skipped += 1
|
|
668
|
+
report_path = report_file.relative_to(project_path)
|
|
669
|
+
migration_log.append(f"Skipped: {report_path} (target already exists)")
|
|
670
|
+
else:
|
|
671
|
+
if not dry_run:
|
|
672
|
+
shutil.copy2(report_file, target_file)
|
|
673
|
+
shutil.copystat(report_file, target_file)
|
|
674
|
+
report_path = report_file.relative_to(project_path)
|
|
675
|
+
target_path = target_file.relative_to(project_path)
|
|
676
|
+
migration_log.append(f"Migrated: {report_path} โ {target_path}")
|
|
677
|
+
else:
|
|
678
|
+
report_path = report_file.relative_to(project_path)
|
|
679
|
+
target_path = target_file.relative_to(project_path)
|
|
680
|
+
migration_log.append(f"Would migrate: {report_path} โ {target_path}")
|
|
681
|
+
files_migrated += 1
|
|
682
|
+
|
|
683
|
+
# Create migration log
|
|
684
|
+
migration_log_path = new_logs_dir / "migration-log.json"
|
|
685
|
+
if not dry_run and files_migrated > 0:
|
|
686
|
+
migration_data = {
|
|
687
|
+
"migration_timestamp": datetime.now().isoformat(),
|
|
688
|
+
"moai_adk_version": __version__,
|
|
689
|
+
"files_migrated": files_migrated,
|
|
690
|
+
"files_skipped": files_skipped,
|
|
691
|
+
"migration_log": migration_log,
|
|
692
|
+
"legacy_directories_found": [
|
|
693
|
+
str(d.relative_to(project_path))
|
|
694
|
+
for d in [legacy_memory, legacy_error_logs, legacy_reports]
|
|
695
|
+
if d.exists()
|
|
696
|
+
],
|
|
697
|
+
}
|
|
698
|
+
json_content = json.dumps(migration_data, indent=2, ensure_ascii=False)
|
|
699
|
+
migration_log_path.write_text(json_content + "\n", encoding="utf-8")
|
|
700
|
+
|
|
701
|
+
# Display results
|
|
702
|
+
if files_migrated > 0 or files_skipped > 0:
|
|
703
|
+
if dry_run:
|
|
704
|
+
console.print(f" [yellow]Would migrate {files_migrated} files, skip {files_skipped} files[/yellow]")
|
|
705
|
+
else:
|
|
706
|
+
console.print(f" [green]โ Migrated {files_migrated} legacy log files[/green]")
|
|
707
|
+
if files_skipped > 0:
|
|
708
|
+
console.print(f" [yellow]โ Skipped {files_skipped} files (already exist)[/yellow]")
|
|
709
|
+
console.print(f" [dim] Migration log: {migration_log_path.relative_to(project_path)}[/dim]")
|
|
710
|
+
elif has_legacy_files:
|
|
711
|
+
console.print(" [dim] No files to migrate[/dim]")
|
|
712
|
+
|
|
713
|
+
return True
|
|
714
|
+
|
|
715
|
+
except Exception as e:
|
|
716
|
+
console.print(f" [red]โ Log migration failed: {e}[/red]")
|
|
717
|
+
logger.error(f"Legacy log migration failed: {e}", exc_info=True)
|
|
718
|
+
return False
|
|
719
|
+
|
|
720
|
+
|
|
721
|
+
def _detect_stale_cache(upgrade_output: str, current_version: str, latest_version: str) -> bool:
|
|
319
722
|
"""
|
|
320
723
|
Detect if uv cache is stale by comparing versions.
|
|
321
724
|
|
|
@@ -410,9 +813,7 @@ def _clear_uv_package_cache(package_name: str = "moai-adk") -> bool:
|
|
|
410
813
|
return False
|
|
411
814
|
|
|
412
815
|
|
|
413
|
-
def _execute_upgrade_with_retry(
|
|
414
|
-
installer_cmd: list[str], package_name: str = "moai-adk"
|
|
415
|
-
) -> bool:
|
|
816
|
+
def _execute_upgrade_with_retry(installer_cmd: list[str], package_name: str = "moai-adk") -> bool:
|
|
416
817
|
"""
|
|
417
818
|
Execute upgrade with automatic cache retry on stale detection.
|
|
418
819
|
|
|
@@ -459,9 +860,7 @@ def _execute_upgrade_with_retry(
|
|
|
459
860
|
"""
|
|
460
861
|
# Stage 1: First upgrade attempt
|
|
461
862
|
try:
|
|
462
|
-
result = subprocess.run(
|
|
463
|
-
installer_cmd, capture_output=True, text=True, timeout=60, check=False
|
|
464
|
-
)
|
|
863
|
+
result = subprocess.run(installer_cmd, capture_output=True, text=True, timeout=60, check=False)
|
|
465
864
|
except subprocess.TimeoutExpired:
|
|
466
865
|
raise # Re-raise timeout for caller to handle
|
|
467
866
|
except Exception:
|
|
@@ -530,9 +929,7 @@ def _execute_upgrade(installer_cmd: list[str]) -> bool:
|
|
|
530
929
|
subprocess.TimeoutExpired: If upgrade times out
|
|
531
930
|
"""
|
|
532
931
|
try:
|
|
533
|
-
result = subprocess.run(
|
|
534
|
-
installer_cmd, capture_output=True, text=True, timeout=60, check=False
|
|
535
|
-
)
|
|
932
|
+
result = subprocess.run(installer_cmd, capture_output=True, text=True, timeout=60, check=False)
|
|
536
933
|
return result.returncode == 0
|
|
537
934
|
except subprocess.TimeoutExpired:
|
|
538
935
|
raise # Re-raise timeout for caller to handle
|
|
@@ -540,12 +937,539 @@ def _execute_upgrade(installer_cmd: list[str]) -> bool:
|
|
|
540
937
|
return False
|
|
541
938
|
|
|
542
939
|
|
|
543
|
-
def
|
|
940
|
+
def _preserve_user_settings(project_path: Path) -> dict[str, Path | None]:
|
|
941
|
+
"""Back up user-specific settings files before template sync.
|
|
942
|
+
|
|
943
|
+
Args:
|
|
944
|
+
project_path: Project directory path
|
|
945
|
+
|
|
946
|
+
Returns:
|
|
947
|
+
Dictionary with backup paths of preserved files
|
|
948
|
+
"""
|
|
949
|
+
preserved = {}
|
|
950
|
+
claude_dir = project_path / ".claude"
|
|
951
|
+
|
|
952
|
+
# Preserve settings.local.json (user MCP and GLM configuration)
|
|
953
|
+
settings_local = claude_dir / "settings.local.json"
|
|
954
|
+
if settings_local.exists():
|
|
955
|
+
try:
|
|
956
|
+
backup_dir = project_path / ".moai-backups" / "settings-backup"
|
|
957
|
+
backup_dir.mkdir(parents=True, exist_ok=True)
|
|
958
|
+
backup_path = backup_dir / "settings.local.json"
|
|
959
|
+
backup_path.write_text(settings_local.read_text(encoding="utf-8"))
|
|
960
|
+
preserved["settings.local.json"] = backup_path
|
|
961
|
+
console.print(" [cyan]๐พ Backed up user settings[/cyan]")
|
|
962
|
+
except Exception as e:
|
|
963
|
+
logger.warning(f"Failed to backup settings.local.json: {e}")
|
|
964
|
+
preserved["settings.local.json"] = None
|
|
965
|
+
else:
|
|
966
|
+
preserved["settings.local.json"] = None
|
|
967
|
+
|
|
968
|
+
return preserved
|
|
969
|
+
|
|
970
|
+
|
|
971
|
+
def _restore_user_settings(project_path: Path, preserved: dict[str, Path | None]) -> bool:
|
|
972
|
+
"""Restore user-specific settings files after template sync.
|
|
973
|
+
|
|
974
|
+
Args:
|
|
975
|
+
project_path: Project directory path
|
|
976
|
+
preserved: Dictionary of backup paths from _preserve_user_settings()
|
|
977
|
+
|
|
978
|
+
Returns:
|
|
979
|
+
True if restoration succeeded, False otherwise
|
|
980
|
+
"""
|
|
981
|
+
claude_dir = project_path / ".claude"
|
|
982
|
+
claude_dir.mkdir(parents=True, exist_ok=True)
|
|
983
|
+
|
|
984
|
+
success = True
|
|
985
|
+
|
|
986
|
+
# Restore settings.local.json
|
|
987
|
+
if preserved.get("settings.local.json"):
|
|
988
|
+
try:
|
|
989
|
+
backup_path = preserved["settings.local.json"]
|
|
990
|
+
settings_local = claude_dir / "settings.local.json"
|
|
991
|
+
settings_local.write_text(backup_path.read_text(encoding="utf-8"))
|
|
992
|
+
console.print(" [cyan]โ Restored user settings[/cyan]")
|
|
993
|
+
except Exception as e:
|
|
994
|
+
console.print(f" [yellow]โ ๏ธ Failed to restore settings.local.json: {e}[/yellow]")
|
|
995
|
+
logger.warning(f"Failed to restore settings.local.json: {e}")
|
|
996
|
+
success = False
|
|
997
|
+
|
|
998
|
+
return success
|
|
999
|
+
|
|
1000
|
+
|
|
1001
|
+
def _get_template_skill_names() -> set[str]:
|
|
1002
|
+
"""Get set of skill folder names from installed template.
|
|
1003
|
+
|
|
1004
|
+
Returns:
|
|
1005
|
+
Set of skill folder names that are part of the template package.
|
|
1006
|
+
"""
|
|
1007
|
+
template_path = Path(__file__).parent.parent.parent / "templates"
|
|
1008
|
+
skills_path = template_path / ".claude" / "skills"
|
|
1009
|
+
|
|
1010
|
+
if not skills_path.exists():
|
|
1011
|
+
return set()
|
|
1012
|
+
|
|
1013
|
+
return {d.name for d in skills_path.iterdir() if d.is_dir()}
|
|
1014
|
+
|
|
1015
|
+
|
|
1016
|
+
def _get_template_command_names() -> set[str]:
|
|
1017
|
+
"""Get set of command file names from installed template.
|
|
1018
|
+
|
|
1019
|
+
Returns:
|
|
1020
|
+
Set of .md command file names from .claude/commands/moai/ in template.
|
|
1021
|
+
"""
|
|
1022
|
+
template_path = Path(__file__).parent.parent.parent / "templates"
|
|
1023
|
+
commands_path = template_path / ".claude" / "commands" / "moai"
|
|
1024
|
+
|
|
1025
|
+
if not commands_path.exists():
|
|
1026
|
+
return set()
|
|
1027
|
+
|
|
1028
|
+
return {f.name for f in commands_path.iterdir() if f.is_file() and f.suffix == ".md"}
|
|
1029
|
+
|
|
1030
|
+
|
|
1031
|
+
def _get_template_agent_names() -> set[str]:
|
|
1032
|
+
"""Get set of agent file names from installed template.
|
|
1033
|
+
|
|
1034
|
+
Returns:
|
|
1035
|
+
Set of agent file names from .claude/agents/ in template.
|
|
1036
|
+
"""
|
|
1037
|
+
template_path = Path(__file__).parent.parent.parent / "templates"
|
|
1038
|
+
agents_path = template_path / ".claude" / "agents"
|
|
1039
|
+
|
|
1040
|
+
if not agents_path.exists():
|
|
1041
|
+
return set()
|
|
1042
|
+
|
|
1043
|
+
return {f.name for f in agents_path.iterdir() if f.is_file()}
|
|
1044
|
+
|
|
1045
|
+
|
|
1046
|
+
def _get_template_hook_names() -> set[str]:
|
|
1047
|
+
"""Get set of hook file names from installed template.
|
|
1048
|
+
|
|
1049
|
+
Returns:
|
|
1050
|
+
Set of .py hook file names from .claude/hooks/moai/ in template.
|
|
1051
|
+
"""
|
|
1052
|
+
template_path = Path(__file__).parent.parent.parent / "templates"
|
|
1053
|
+
hooks_path = template_path / ".claude" / "hooks" / "moai"
|
|
1054
|
+
|
|
1055
|
+
if not hooks_path.exists():
|
|
1056
|
+
return set()
|
|
1057
|
+
|
|
1058
|
+
return {f.name for f in hooks_path.iterdir() if f.is_file() and f.suffix == ".py"}
|
|
1059
|
+
|
|
1060
|
+
|
|
1061
|
+
def _detect_custom_commands(project_path: Path, template_commands: set[str]) -> list[str]:
|
|
1062
|
+
"""Detect custom commands NOT in template (user-created).
|
|
1063
|
+
|
|
1064
|
+
Args:
|
|
1065
|
+
project_path: Project path (absolute)
|
|
1066
|
+
template_commands: Set of template command file names
|
|
1067
|
+
|
|
1068
|
+
Returns:
|
|
1069
|
+
Sorted list of custom command file names.
|
|
1070
|
+
"""
|
|
1071
|
+
commands_path = project_path / ".claude" / "commands" / "moai"
|
|
1072
|
+
|
|
1073
|
+
if not commands_path.exists():
|
|
1074
|
+
return []
|
|
1075
|
+
|
|
1076
|
+
project_commands = {f.name for f in commands_path.iterdir() if f.is_file() and f.suffix == ".md"}
|
|
1077
|
+
custom_commands = project_commands - template_commands
|
|
1078
|
+
|
|
1079
|
+
return sorted(custom_commands)
|
|
1080
|
+
|
|
1081
|
+
|
|
1082
|
+
def _detect_custom_agents(project_path: Path, template_agents: set[str]) -> list[str]:
|
|
1083
|
+
"""Detect custom agents NOT in template (user-created).
|
|
1084
|
+
|
|
1085
|
+
Args:
|
|
1086
|
+
project_path: Project path (absolute)
|
|
1087
|
+
template_agents: Set of template agent file names
|
|
1088
|
+
|
|
1089
|
+
Returns:
|
|
1090
|
+
Sorted list of custom agent file names.
|
|
1091
|
+
"""
|
|
1092
|
+
agents_path = project_path / ".claude" / "agents"
|
|
1093
|
+
|
|
1094
|
+
if not agents_path.exists():
|
|
1095
|
+
return []
|
|
1096
|
+
|
|
1097
|
+
project_agents = {f.name for f in agents_path.iterdir() if f.is_file()}
|
|
1098
|
+
custom_agents = project_agents - template_agents
|
|
1099
|
+
|
|
1100
|
+
return sorted(custom_agents)
|
|
1101
|
+
|
|
1102
|
+
|
|
1103
|
+
def _detect_custom_hooks(project_path: Path, template_hooks: set[str]) -> list[str]:
|
|
1104
|
+
"""Detect custom hooks NOT in template (user-created).
|
|
1105
|
+
|
|
1106
|
+
Args:
|
|
1107
|
+
project_path: Project path (absolute)
|
|
1108
|
+
template_hooks: Set of template hook file names
|
|
1109
|
+
|
|
1110
|
+
Returns:
|
|
1111
|
+
Sorted list of custom hook file names.
|
|
1112
|
+
"""
|
|
1113
|
+
hooks_path = project_path / ".claude" / "hooks" / "moai"
|
|
1114
|
+
|
|
1115
|
+
if not hooks_path.exists():
|
|
1116
|
+
return []
|
|
1117
|
+
|
|
1118
|
+
project_hooks = {f.name for f in hooks_path.iterdir() if f.is_file() and f.suffix == ".py"}
|
|
1119
|
+
custom_hooks = project_hooks - template_hooks
|
|
1120
|
+
|
|
1121
|
+
return sorted(custom_hooks)
|
|
1122
|
+
|
|
1123
|
+
|
|
1124
|
+
def _group_custom_files_by_type(
|
|
1125
|
+
custom_commands: list[str],
|
|
1126
|
+
custom_agents: list[str],
|
|
1127
|
+
custom_hooks: list[str],
|
|
1128
|
+
) -> dict[str, list[str]]:
|
|
1129
|
+
"""Group custom files by type for UI display.
|
|
1130
|
+
|
|
1131
|
+
Args:
|
|
1132
|
+
custom_commands: List of custom command file names
|
|
1133
|
+
custom_agents: List of custom agent file names
|
|
1134
|
+
custom_hooks: List of custom hook file names
|
|
1135
|
+
|
|
1136
|
+
Returns:
|
|
1137
|
+
Dictionary with keys: commands, agents, hooks
|
|
1138
|
+
"""
|
|
1139
|
+
return {
|
|
1140
|
+
"commands": custom_commands,
|
|
1141
|
+
"agents": custom_agents,
|
|
1142
|
+
"hooks": custom_hooks,
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
|
|
1146
|
+
def _prompt_custom_files_restore(
|
|
1147
|
+
custom_commands: list[str],
|
|
1148
|
+
custom_agents: list[str],
|
|
1149
|
+
custom_hooks: list[str],
|
|
1150
|
+
yes: bool = False,
|
|
1151
|
+
) -> dict[str, list[str]]:
|
|
1152
|
+
"""Interactive fuzzy checkbox for custom files restore with search support.
|
|
1153
|
+
|
|
1154
|
+
Args:
|
|
1155
|
+
custom_commands: List of custom command file names
|
|
1156
|
+
custom_agents: List of custom agent file names
|
|
1157
|
+
custom_hooks: List of custom hook file names
|
|
1158
|
+
yes: Auto-confirm flag (skips restoration in CI/CD mode)
|
|
1159
|
+
|
|
1160
|
+
Returns:
|
|
1161
|
+
Dictionary with selected files grouped by type.
|
|
1162
|
+
"""
|
|
1163
|
+
# If no custom files, skip UI
|
|
1164
|
+
if not (custom_commands or custom_agents or custom_hooks):
|
|
1165
|
+
return {
|
|
1166
|
+
"commands": [],
|
|
1167
|
+
"agents": [],
|
|
1168
|
+
"hooks": [],
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
# In --yes mode, skip restoration (safest default)
|
|
1172
|
+
if yes:
|
|
1173
|
+
console.print("\n[dim] Skipping custom files restoration (--yes mode)[/dim]\n")
|
|
1174
|
+
return {
|
|
1175
|
+
"commands": [],
|
|
1176
|
+
"agents": [],
|
|
1177
|
+
"hooks": [],
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
# Try to use new UI, fallback to questionary if import fails
|
|
1181
|
+
try:
|
|
1182
|
+
from moai_adk.cli.ui.prompts import create_grouped_choices, fuzzy_checkbox
|
|
1183
|
+
|
|
1184
|
+
# Build grouped choices for fuzzy checkbox
|
|
1185
|
+
groups: dict[str, list[dict[str, str]]] = {}
|
|
1186
|
+
|
|
1187
|
+
if custom_commands:
|
|
1188
|
+
groups["Commands (.claude/commands/moai/)"] = [
|
|
1189
|
+
{"name": cmd, "value": f"cmd:{cmd}"} for cmd in custom_commands
|
|
1190
|
+
]
|
|
1191
|
+
|
|
1192
|
+
if custom_agents:
|
|
1193
|
+
groups["Agents (.claude/agents/)"] = [{"name": agent, "value": f"agent:{agent}"} for agent in custom_agents]
|
|
1194
|
+
|
|
1195
|
+
if custom_hooks:
|
|
1196
|
+
groups["Hooks (.claude/hooks/moai/)"] = [{"name": hook, "value": f"hook:{hook}"} for hook in custom_hooks]
|
|
1197
|
+
|
|
1198
|
+
choices = create_grouped_choices(groups)
|
|
1199
|
+
|
|
1200
|
+
console.print("\n[#DA7756]๐ฆ Custom files detected in backup:[/#DA7756]")
|
|
1201
|
+
console.print("[dim] Use fuzzy search to find files quickly[/dim]\n")
|
|
1202
|
+
|
|
1203
|
+
selected = fuzzy_checkbox(
|
|
1204
|
+
"Select custom files to restore:",
|
|
1205
|
+
choices=choices,
|
|
1206
|
+
instruction="[Space] Toggle [Tab] All [Enter] Confirm [Type to search]",
|
|
1207
|
+
)
|
|
1208
|
+
|
|
1209
|
+
except ImportError:
|
|
1210
|
+
# Fallback to questionary if new UI not available
|
|
1211
|
+
import questionary
|
|
1212
|
+
from questionary import Choice, Separator
|
|
1213
|
+
|
|
1214
|
+
choices_legacy: list[Union[Separator, Choice]] = []
|
|
1215
|
+
|
|
1216
|
+
if custom_commands:
|
|
1217
|
+
choices_legacy.append(Separator("Commands (.claude/commands/moai/)"))
|
|
1218
|
+
for cmd in custom_commands:
|
|
1219
|
+
choices_legacy.append(Choice(title=cmd, value=f"cmd:{cmd}"))
|
|
1220
|
+
|
|
1221
|
+
if custom_agents:
|
|
1222
|
+
choices_legacy.append(Separator("Agents (.claude/agents/)"))
|
|
1223
|
+
for agent in custom_agents:
|
|
1224
|
+
choices_legacy.append(Choice(title=agent, value=f"agent:{agent}"))
|
|
1225
|
+
|
|
1226
|
+
if custom_hooks:
|
|
1227
|
+
choices_legacy.append(Separator("Hooks (.claude/hooks/moai/)"))
|
|
1228
|
+
for hook in custom_hooks:
|
|
1229
|
+
choices_legacy.append(Choice(title=hook, value=f"hook:{hook}"))
|
|
1230
|
+
|
|
1231
|
+
console.print("\n[cyan]๐ฆ Custom files detected in backup:[/cyan]")
|
|
1232
|
+
console.print("[dim] Select files to restore (none selected by default)[/dim]\n")
|
|
1233
|
+
|
|
1234
|
+
selected = questionary.checkbox(
|
|
1235
|
+
"Select custom files to restore:",
|
|
1236
|
+
choices=choices_legacy,
|
|
1237
|
+
).ask()
|
|
1238
|
+
|
|
1239
|
+
# Parse results
|
|
1240
|
+
result_commands = []
|
|
1241
|
+
result_agents = []
|
|
1242
|
+
result_hooks = []
|
|
1243
|
+
|
|
1244
|
+
if selected:
|
|
1245
|
+
for item in selected:
|
|
1246
|
+
if item.startswith("cmd:"):
|
|
1247
|
+
result_commands.append(item[4:])
|
|
1248
|
+
elif item.startswith("agent:"):
|
|
1249
|
+
result_agents.append(item[6:])
|
|
1250
|
+
elif item.startswith("hook:"):
|
|
1251
|
+
result_hooks.append(item[5:])
|
|
1252
|
+
|
|
1253
|
+
return {
|
|
1254
|
+
"commands": result_commands,
|
|
1255
|
+
"agents": result_agents,
|
|
1256
|
+
"hooks": result_hooks,
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
|
|
1260
|
+
def _restore_custom_files(
|
|
1261
|
+
project_path: Path,
|
|
1262
|
+
backup_path: Path,
|
|
1263
|
+
selected_commands: list[str],
|
|
1264
|
+
selected_agents: list[str],
|
|
1265
|
+
selected_hooks: list[str],
|
|
1266
|
+
) -> bool:
|
|
1267
|
+
"""Restore selected custom files from backup to project.
|
|
1268
|
+
|
|
1269
|
+
Args:
|
|
1270
|
+
project_path: Project directory path
|
|
1271
|
+
backup_path: Backup directory path
|
|
1272
|
+
selected_commands: List of command files to restore
|
|
1273
|
+
selected_agents: List of agent files to restore
|
|
1274
|
+
selected_hooks: List of hook files to restore
|
|
1275
|
+
|
|
1276
|
+
Returns:
|
|
1277
|
+
True if all restorations succeeded, False otherwise.
|
|
1278
|
+
"""
|
|
1279
|
+
import shutil
|
|
1280
|
+
|
|
1281
|
+
success = True
|
|
1282
|
+
|
|
1283
|
+
# Restore commands
|
|
1284
|
+
if selected_commands:
|
|
1285
|
+
commands_dst = project_path / ".claude" / "commands" / "moai"
|
|
1286
|
+
commands_dst.mkdir(parents=True, exist_ok=True)
|
|
1287
|
+
|
|
1288
|
+
for cmd_file in selected_commands:
|
|
1289
|
+
src = backup_path / ".claude" / "commands" / "moai" / cmd_file
|
|
1290
|
+
dst = commands_dst / cmd_file
|
|
1291
|
+
|
|
1292
|
+
if src.exists():
|
|
1293
|
+
try:
|
|
1294
|
+
shutil.copy2(src, dst)
|
|
1295
|
+
except Exception as e:
|
|
1296
|
+
logger.warning(f"Failed to restore command {cmd_file}: {e}")
|
|
1297
|
+
success = False
|
|
1298
|
+
else:
|
|
1299
|
+
logger.warning(f"Command file not in backup: {cmd_file}")
|
|
1300
|
+
success = False
|
|
1301
|
+
|
|
1302
|
+
# Restore agents
|
|
1303
|
+
if selected_agents:
|
|
1304
|
+
agents_dst = project_path / ".claude" / "agents"
|
|
1305
|
+
agents_dst.mkdir(parents=True, exist_ok=True)
|
|
1306
|
+
|
|
1307
|
+
for agent_file in selected_agents:
|
|
1308
|
+
src = backup_path / ".claude" / "agents" / agent_file
|
|
1309
|
+
dst = agents_dst / agent_file
|
|
1310
|
+
|
|
1311
|
+
if src.exists():
|
|
1312
|
+
try:
|
|
1313
|
+
shutil.copy2(src, dst)
|
|
1314
|
+
except Exception as e:
|
|
1315
|
+
logger.warning(f"Failed to restore agent {agent_file}: {e}")
|
|
1316
|
+
success = False
|
|
1317
|
+
else:
|
|
1318
|
+
logger.warning(f"Agent file not in backup: {agent_file}")
|
|
1319
|
+
success = False
|
|
1320
|
+
|
|
1321
|
+
# Restore hooks
|
|
1322
|
+
if selected_hooks:
|
|
1323
|
+
hooks_dst = project_path / ".claude" / "hooks" / "moai"
|
|
1324
|
+
hooks_dst.mkdir(parents=True, exist_ok=True)
|
|
1325
|
+
|
|
1326
|
+
for hook_file in selected_hooks:
|
|
1327
|
+
src = backup_path / ".claude" / "hooks" / "moai" / hook_file
|
|
1328
|
+
dst = hooks_dst / hook_file
|
|
1329
|
+
|
|
1330
|
+
if src.exists():
|
|
1331
|
+
try:
|
|
1332
|
+
shutil.copy2(src, dst)
|
|
1333
|
+
except Exception as e:
|
|
1334
|
+
logger.warning(f"Failed to restore hook {hook_file}: {e}")
|
|
1335
|
+
success = False
|
|
1336
|
+
else:
|
|
1337
|
+
logger.warning(f"Hook file not in backup: {hook_file}")
|
|
1338
|
+
success = False
|
|
1339
|
+
|
|
1340
|
+
return success
|
|
1341
|
+
|
|
1342
|
+
|
|
1343
|
+
def _detect_custom_skills(project_path: Path, template_skills: set[str]) -> list[str]:
|
|
1344
|
+
"""Detect skills NOT in template (user-created).
|
|
1345
|
+
|
|
1346
|
+
Args:
|
|
1347
|
+
project_path: Project path (absolute)
|
|
1348
|
+
template_skills: Set of template skill names
|
|
1349
|
+
|
|
1350
|
+
Returns:
|
|
1351
|
+
Sorted list of custom skill names.
|
|
1352
|
+
"""
|
|
1353
|
+
skills_path = project_path / ".claude" / "skills"
|
|
1354
|
+
|
|
1355
|
+
if not skills_path.exists():
|
|
1356
|
+
return []
|
|
1357
|
+
|
|
1358
|
+
project_skills = {d.name for d in skills_path.iterdir() if d.is_dir()}
|
|
1359
|
+
custom_skills = project_skills - template_skills
|
|
1360
|
+
|
|
1361
|
+
return sorted(custom_skills)
|
|
1362
|
+
|
|
1363
|
+
|
|
1364
|
+
def _prompt_skill_restore(custom_skills: list[str], yes: bool = False) -> list[str]:
|
|
1365
|
+
"""Interactive fuzzy checkbox for skill restore with search support.
|
|
1366
|
+
|
|
1367
|
+
Args:
|
|
1368
|
+
custom_skills: List of custom skill names
|
|
1369
|
+
yes: Auto-confirm flag (skips restoration in CI/CD mode)
|
|
1370
|
+
|
|
1371
|
+
Returns:
|
|
1372
|
+
List of skills user selected to restore.
|
|
1373
|
+
"""
|
|
1374
|
+
if not custom_skills:
|
|
1375
|
+
return []
|
|
1376
|
+
|
|
1377
|
+
console.print("\n[#DA7756]๐ฆ Custom skills detected in backup:[/#DA7756]")
|
|
1378
|
+
for skill in custom_skills:
|
|
1379
|
+
console.print(f" โข {skill}")
|
|
1380
|
+
console.print()
|
|
1381
|
+
|
|
1382
|
+
if yes:
|
|
1383
|
+
console.print("[dim] Skipping restoration (--yes mode)[/dim]\n")
|
|
1384
|
+
return []
|
|
1385
|
+
|
|
1386
|
+
# Try new UI, fallback to questionary
|
|
1387
|
+
try:
|
|
1388
|
+
from moai_adk.cli.ui.prompts import fuzzy_checkbox
|
|
1389
|
+
|
|
1390
|
+
choices = [{"name": skill, "value": skill} for skill in custom_skills]
|
|
1391
|
+
|
|
1392
|
+
selected = fuzzy_checkbox(
|
|
1393
|
+
"Select skills to restore (type to search):",
|
|
1394
|
+
choices=choices,
|
|
1395
|
+
instruction="[Space] Toggle [Tab] All [Enter] Confirm [Type to search]",
|
|
1396
|
+
)
|
|
1397
|
+
|
|
1398
|
+
except ImportError:
|
|
1399
|
+
import questionary
|
|
1400
|
+
|
|
1401
|
+
selected = questionary.checkbox(
|
|
1402
|
+
"Select skills to restore (none selected by default):",
|
|
1403
|
+
choices=[questionary.Choice(title=skill, checked=False) for skill in custom_skills],
|
|
1404
|
+
).ask()
|
|
1405
|
+
|
|
1406
|
+
return selected if selected else []
|
|
1407
|
+
|
|
1408
|
+
|
|
1409
|
+
def _restore_selected_skills(skills: list[str], backup_path: Path, project_path: Path) -> bool:
|
|
1410
|
+
"""Restore selected skills from backup.
|
|
1411
|
+
|
|
1412
|
+
Args:
|
|
1413
|
+
skills: List of skill names to restore
|
|
1414
|
+
backup_path: Backup directory path
|
|
1415
|
+
project_path: Project path (absolute)
|
|
1416
|
+
|
|
1417
|
+
Returns:
|
|
1418
|
+
True if all restorations succeeded.
|
|
1419
|
+
"""
|
|
1420
|
+
import shutil
|
|
1421
|
+
|
|
1422
|
+
if not skills:
|
|
1423
|
+
return True
|
|
1424
|
+
|
|
1425
|
+
console.print("\n[cyan]๐ฅ Restoring selected skills...[/cyan]")
|
|
1426
|
+
skills_dst = project_path / ".claude" / "skills"
|
|
1427
|
+
skills_dst.mkdir(parents=True, exist_ok=True)
|
|
1428
|
+
|
|
1429
|
+
success = True
|
|
1430
|
+
for skill_name in skills:
|
|
1431
|
+
src = backup_path / ".claude" / "skills" / skill_name
|
|
1432
|
+
dst = skills_dst / skill_name
|
|
1433
|
+
|
|
1434
|
+
if src.exists():
|
|
1435
|
+
try:
|
|
1436
|
+
shutil.copytree(src, dst, dirs_exist_ok=True)
|
|
1437
|
+
console.print(f" [green]โ Restored: {skill_name}[/green]")
|
|
1438
|
+
except Exception as e:
|
|
1439
|
+
console.print(f" [red]โ Failed: {skill_name} - {e}[/red]")
|
|
1440
|
+
success = False
|
|
1441
|
+
else:
|
|
1442
|
+
console.print(f" [yellow]โ Not in backup: {skill_name}[/yellow]")
|
|
1443
|
+
success = False
|
|
1444
|
+
|
|
1445
|
+
return success
|
|
1446
|
+
|
|
1447
|
+
|
|
1448
|
+
def _show_post_update_guidance(backup_path: Path) -> None:
|
|
1449
|
+
"""Show post-update guidance for running /moai:0-project update.
|
|
1450
|
+
|
|
1451
|
+
Args:
|
|
1452
|
+
backup_path: Backup directory path for reference
|
|
1453
|
+
"""
|
|
1454
|
+
console.print("\n" + "[cyan]" + "=" * 60 + "[/cyan]")
|
|
1455
|
+
console.print("[green]โ
Update complete![/green]")
|
|
1456
|
+
console.print("\n[yellow]๐ IMPORTANT - Next step:[/yellow]")
|
|
1457
|
+
console.print(" Run [cyan]/moai:0-project update[/cyan] in Claude Code")
|
|
1458
|
+
console.print("\n This will:")
|
|
1459
|
+
console.print(" โข Merge your settings with new templates")
|
|
1460
|
+
console.print(" โข Validate configuration compatibility")
|
|
1461
|
+
console.print("\n[dim]๐ก Personal instructions should go in CLAUDE.local.md[/dim]")
|
|
1462
|
+
console.print(f"[dim]๐ Backup location: {backup_path}[/dim]")
|
|
1463
|
+
console.print("[cyan]" + "=" * 60 + "[/cyan]\n")
|
|
1464
|
+
|
|
1465
|
+
|
|
1466
|
+
def _sync_templates(project_path: Path, force: bool = False, yes: bool = False) -> bool:
|
|
544
1467
|
"""Sync templates to project with rollback mechanism.
|
|
545
1468
|
|
|
546
1469
|
Args:
|
|
547
1470
|
project_path: Project path (absolute)
|
|
548
1471
|
force: Force update without backup
|
|
1472
|
+
yes: Auto-confirm flag (skips interactive prompts)
|
|
549
1473
|
|
|
550
1474
|
Returns:
|
|
551
1475
|
True if sync succeeded, False otherwise
|
|
@@ -554,6 +1478,20 @@ def _sync_templates(project_path: Path, force: bool = False) -> bool:
|
|
|
554
1478
|
|
|
555
1479
|
backup_path = None
|
|
556
1480
|
try:
|
|
1481
|
+
# NEW: Detect custom files and skills BEFORE backup/sync
|
|
1482
|
+
template_skills = _get_template_skill_names()
|
|
1483
|
+
_detect_custom_skills(project_path, template_skills)
|
|
1484
|
+
|
|
1485
|
+
# Detect custom commands, agents, and hooks
|
|
1486
|
+
template_commands = _get_template_command_names()
|
|
1487
|
+
_detect_custom_commands(project_path, template_commands)
|
|
1488
|
+
|
|
1489
|
+
template_agents = _get_template_agent_names()
|
|
1490
|
+
_detect_custom_agents(project_path, template_agents)
|
|
1491
|
+
|
|
1492
|
+
template_hooks = _get_template_hook_names()
|
|
1493
|
+
_detect_custom_hooks(project_path, template_hooks)
|
|
1494
|
+
|
|
557
1495
|
processor = TemplateProcessor(project_path)
|
|
558
1496
|
|
|
559
1497
|
# Create pre-sync backup for rollback
|
|
@@ -563,6 +1501,25 @@ def _sync_templates(project_path: Path, force: bool = False) -> bool:
|
|
|
563
1501
|
backup_path = backup.create_backup()
|
|
564
1502
|
console.print(f"๐พ Created backup: {backup_path.name}")
|
|
565
1503
|
|
|
1504
|
+
# Merge analysis using Pure Python semantic heuristics
|
|
1505
|
+
try:
|
|
1506
|
+
analyzer = MergeAnalyzer(project_path)
|
|
1507
|
+
# Template source path from installed package
|
|
1508
|
+
template_path = Path(__file__).parent.parent.parent / "templates"
|
|
1509
|
+
|
|
1510
|
+
console.print("\n[cyan]๐ Starting merge analysis...[/cyan]")
|
|
1511
|
+
console.print("[dim] Analyzing templates with semantic heuristics.[/dim]\n")
|
|
1512
|
+
analysis = analyzer.analyze_merge(backup_path, template_path)
|
|
1513
|
+
|
|
1514
|
+
# Ask user confirmation
|
|
1515
|
+
if not analyzer.ask_user_confirmation(analysis):
|
|
1516
|
+
console.print("[yellow]โ ๏ธ User cancelled the update.[/yellow]")
|
|
1517
|
+
backup.restore_backup(backup_path)
|
|
1518
|
+
return False
|
|
1519
|
+
except Exception as e:
|
|
1520
|
+
console.print(f"[yellow]โ ๏ธ Merge analysis failed: {e}[/yellow]")
|
|
1521
|
+
console.print("[yellow]Proceeding with automatic merge.[/yellow]")
|
|
1522
|
+
|
|
566
1523
|
# Load existing config
|
|
567
1524
|
existing_config = _load_existing_config(project_path)
|
|
568
1525
|
|
|
@@ -571,18 +1528,34 @@ def _sync_templates(project_path: Path, force: bool = False) -> bool:
|
|
|
571
1528
|
if context:
|
|
572
1529
|
processor.set_context(context)
|
|
573
1530
|
|
|
574
|
-
# Copy templates
|
|
1531
|
+
# Copy templates (including moai folder)
|
|
575
1532
|
processor.copy_templates(backup=False, silent=True)
|
|
576
1533
|
|
|
1534
|
+
# Stage 1.5: Alfred โ Moai migration (AFTER template sync)
|
|
1535
|
+
# Execute migration after template copy (moai folders must exist first)
|
|
1536
|
+
migrator = AlfredToMoaiMigrator(project_path)
|
|
1537
|
+
if migrator.needs_migration():
|
|
1538
|
+
console.print("\n[cyan]๐ Migrating folder structure: Alfred โ Moai[/cyan]")
|
|
1539
|
+
try:
|
|
1540
|
+
if not migrator.execute_migration(backup_path):
|
|
1541
|
+
console.print("[red]โ Alfred โ Moai migration failed[/red]")
|
|
1542
|
+
if backup_path:
|
|
1543
|
+
console.print("[yellow]๐ Restoring from backup...[/yellow]")
|
|
1544
|
+
backup = TemplateBackup(project_path)
|
|
1545
|
+
backup.restore_backup(backup_path)
|
|
1546
|
+
return False
|
|
1547
|
+
except Exception as e:
|
|
1548
|
+
console.print(f"[red]โ Error during migration: {e}[/red]")
|
|
1549
|
+
if backup_path:
|
|
1550
|
+
backup = TemplateBackup(project_path)
|
|
1551
|
+
backup.restore_backup(backup_path)
|
|
1552
|
+
return False
|
|
1553
|
+
|
|
577
1554
|
# Validate template substitution
|
|
578
|
-
validation_passed = _validate_template_substitution_with_rollback(
|
|
579
|
-
project_path, backup_path
|
|
580
|
-
)
|
|
1555
|
+
validation_passed = _validate_template_substitution_with_rollback(project_path, backup_path)
|
|
581
1556
|
if not validation_passed:
|
|
582
1557
|
if backup_path:
|
|
583
|
-
console.print(
|
|
584
|
-
f"[yellow]๐ Rolling back to backup: {backup_path.name}[/yellow]"
|
|
585
|
-
)
|
|
1558
|
+
console.print(f"[yellow]๐ Rolling back to backup: {backup_path.name}[/yellow]")
|
|
586
1559
|
backup.restore_backup(backup_path)
|
|
587
1560
|
return False
|
|
588
1561
|
|
|
@@ -593,13 +1566,50 @@ def _sync_templates(project_path: Path, force: bool = False) -> bool:
|
|
|
593
1566
|
# Set optimized=false
|
|
594
1567
|
set_optimized_false(project_path)
|
|
595
1568
|
|
|
1569
|
+
# Update companyAnnouncements in settings.local.json
|
|
1570
|
+
try:
|
|
1571
|
+
import sys
|
|
1572
|
+
|
|
1573
|
+
utils_dir = (
|
|
1574
|
+
Path(__file__).parent.parent.parent / "templates" / ".claude" / "hooks" / "moai" / "shared" / "utils"
|
|
1575
|
+
)
|
|
1576
|
+
|
|
1577
|
+
if utils_dir.exists():
|
|
1578
|
+
sys.path.insert(0, str(utils_dir))
|
|
1579
|
+
try:
|
|
1580
|
+
from announcement_translator import auto_translate_and_update
|
|
1581
|
+
|
|
1582
|
+
console.print("[cyan]Updating announcements...[/cyan]")
|
|
1583
|
+
auto_translate_and_update(project_path)
|
|
1584
|
+
console.print("[green]โ Announcements updated[/green]")
|
|
1585
|
+
except Exception as e:
|
|
1586
|
+
console.print(f"[yellow]โ ๏ธ Announcement update failed: {e}[/yellow]")
|
|
1587
|
+
finally:
|
|
1588
|
+
sys.path.remove(str(utils_dir))
|
|
1589
|
+
|
|
1590
|
+
except Exception as e:
|
|
1591
|
+
console.print(f"[yellow]โ ๏ธ Announcement module not available: {e}[/yellow]")
|
|
1592
|
+
|
|
1593
|
+
# NEW: Interactive custom element restore using new system
|
|
1594
|
+
_handle_custom_element_restoration(project_path, backup_path, yes)
|
|
1595
|
+
|
|
1596
|
+
# NEW: Migrate legacy logs to unified structure
|
|
1597
|
+
console.print("\n[cyan]๐ Migrating legacy log files...[/cyan]")
|
|
1598
|
+
if not _migrate_legacy_logs(project_path):
|
|
1599
|
+
console.print("[yellow]โ ๏ธ Legacy log migration failed, but update continuing[/yellow]")
|
|
1600
|
+
|
|
1601
|
+
# Clean up legacy presets directory
|
|
1602
|
+
_cleanup_legacy_presets(project_path)
|
|
1603
|
+
|
|
1604
|
+
# NEW: Show post-update guidance
|
|
1605
|
+
if backup_path:
|
|
1606
|
+
_show_post_update_guidance(backup_path)
|
|
1607
|
+
|
|
596
1608
|
return True
|
|
597
1609
|
except Exception as e:
|
|
598
1610
|
console.print(f"[red]โ Template sync failed: {e}[/red]")
|
|
599
1611
|
if backup_path:
|
|
600
|
-
console.print(
|
|
601
|
-
f"[yellow]๐ Rolling back to backup: {backup_path.name}[/yellow]"
|
|
602
|
-
)
|
|
1612
|
+
console.print(f"[yellow]๐ Rolling back to backup: {backup_path.name}[/yellow]")
|
|
603
1613
|
try:
|
|
604
1614
|
backup = TemplateBackup(project_path)
|
|
605
1615
|
backup.restore_backup(backup_path)
|
|
@@ -626,47 +1636,38 @@ def get_latest_version() -> str | None:
|
|
|
626
1636
|
|
|
627
1637
|
|
|
628
1638
|
def set_optimized_false(project_path: Path) -> None:
|
|
629
|
-
"""Set config
|
|
1639
|
+
"""Set config's optimized field to false.
|
|
630
1640
|
|
|
631
1641
|
Args:
|
|
632
1642
|
project_path: Project path (absolute).
|
|
633
1643
|
"""
|
|
634
|
-
config_path = project_path
|
|
1644
|
+
config_path, _ = _get_config_path(project_path)
|
|
635
1645
|
if not config_path.exists():
|
|
636
1646
|
return
|
|
637
1647
|
|
|
638
1648
|
try:
|
|
639
|
-
config_data =
|
|
1649
|
+
config_data = _load_config(config_path)
|
|
640
1650
|
config_data.setdefault("project", {})["optimized"] = False
|
|
641
|
-
config_path
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
)
|
|
645
|
-
except (json.JSONDecodeError, KeyError):
|
|
646
|
-
# Ignore errors if config.json is invalid
|
|
1651
|
+
_save_config(config_path, config_data)
|
|
1652
|
+
except (json.JSONDecodeError, yaml.YAMLError, KeyError):
|
|
1653
|
+
# Ignore errors if config is invalid
|
|
647
1654
|
pass
|
|
648
1655
|
|
|
649
1656
|
|
|
650
1657
|
def _load_existing_config(project_path: Path) -> dict[str, Any]:
|
|
651
|
-
"""Load existing config
|
|
652
|
-
config_path = project_path
|
|
1658
|
+
"""Load existing config (YAML or JSON) if available."""
|
|
1659
|
+
config_path, _ = _get_config_path(project_path)
|
|
653
1660
|
if config_path.exists():
|
|
654
1661
|
try:
|
|
655
|
-
return
|
|
656
|
-
except json.JSONDecodeError:
|
|
657
|
-
console.print(
|
|
658
|
-
"[yellow]โ Existing config.json could not be parsed. Proceeding with defaults.[/yellow]"
|
|
659
|
-
)
|
|
1662
|
+
return _load_config(config_path)
|
|
1663
|
+
except (json.JSONDecodeError, yaml.YAMLError):
|
|
1664
|
+
console.print("[yellow]โ Existing config could not be parsed. Proceeding with defaults.[/yellow]")
|
|
660
1665
|
return {}
|
|
661
1666
|
|
|
662
1667
|
|
|
663
1668
|
def _is_placeholder(value: Any) -> bool:
|
|
664
1669
|
"""Check if a string value is an unsubstituted template placeholder."""
|
|
665
|
-
return (
|
|
666
|
-
isinstance(value, str)
|
|
667
|
-
and value.strip().startswith("{{")
|
|
668
|
-
and value.strip().endswith("}}")
|
|
669
|
-
)
|
|
1670
|
+
return isinstance(value, str) and value.strip().startswith("{{") and value.strip().endswith("}}")
|
|
670
1671
|
|
|
671
1672
|
|
|
672
1673
|
def _coalesce(*values: Any, default: str = "") -> str:
|
|
@@ -730,19 +1731,90 @@ def _build_template_context(
|
|
|
730
1731
|
)
|
|
731
1732
|
|
|
732
1733
|
# Detect OS for cross-platform Hook path configuration
|
|
733
|
-
hook_project_dir = (
|
|
734
|
-
"%CLAUDE_PROJECT_DIR%"
|
|
735
|
-
if platform.system() == "Windows"
|
|
736
|
-
else "$CLAUDE_PROJECT_DIR"
|
|
737
|
-
)
|
|
1734
|
+
hook_project_dir = "%CLAUDE_PROJECT_DIR%" if platform.system() == "Windows" else "$CLAUDE_PROJECT_DIR"
|
|
738
1735
|
|
|
739
|
-
#
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
1736
|
+
# Detect OS for cross-platform statusline command
|
|
1737
|
+
# Windows: Use python -m for better PATH compatibility
|
|
1738
|
+
# Unix: Use moai-adk directly (assumes installed via uv tool)
|
|
1739
|
+
if platform.system() == "Windows":
|
|
1740
|
+
statusline_command = "python -m moai_adk statusline"
|
|
1741
|
+
else:
|
|
1742
|
+
statusline_command = "moai-adk statusline"
|
|
1743
|
+
|
|
1744
|
+
# Extract and resolve language configuration using centralized resolver
|
|
1745
|
+
try:
|
|
1746
|
+
from moai_adk.core.language_config_resolver import get_resolver
|
|
1747
|
+
|
|
1748
|
+
# Use language resolver to get complete configuration
|
|
1749
|
+
resolver = get_resolver(str(project_path))
|
|
1750
|
+
resolved_config = resolver.resolve_config()
|
|
1751
|
+
|
|
1752
|
+
# Extract language configuration with environment variable priority
|
|
1753
|
+
language_config = {
|
|
1754
|
+
"conversation_language": resolved_config.get("conversation_language", "en"),
|
|
1755
|
+
"conversation_language_name": resolved_config.get("conversation_language_name", "English"),
|
|
1756
|
+
"agent_prompt_language": resolved_config.get("agent_prompt_language", "en"),
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
# Extract user personalization
|
|
1760
|
+
user_name = resolved_config.get("user_name", "")
|
|
1761
|
+
personalized_greeting = resolver.get_personalized_greeting(resolved_config)
|
|
1762
|
+
config_source = resolved_config.get("config_source", "config_file")
|
|
1763
|
+
|
|
1764
|
+
except ImportError:
|
|
1765
|
+
# Fallback to basic language config extraction if resolver not available
|
|
1766
|
+
language_config = existing_config.get("language", {})
|
|
1767
|
+
if not isinstance(language_config, dict):
|
|
1768
|
+
language_config = {}
|
|
1769
|
+
|
|
1770
|
+
user_name = existing_config.get("user", {}).get("name", "")
|
|
1771
|
+
conv_lang = language_config.get("conversation_language")
|
|
1772
|
+
personalized_greeting = f"{user_name}๋" if user_name and conv_lang == "ko" else user_name
|
|
1773
|
+
config_source = "config_file"
|
|
1774
|
+
|
|
1775
|
+
# Enhanced version formatting (matches TemplateProcessor.get_enhanced_version_context)
|
|
1776
|
+
def format_short_version(v: str) -> str:
|
|
1777
|
+
"""Remove 'v' prefix if present."""
|
|
1778
|
+
return v[1:] if v.startswith("v") else v
|
|
1779
|
+
|
|
1780
|
+
def format_display_version(v: str) -> str:
|
|
1781
|
+
"""Format display version with proper formatting."""
|
|
1782
|
+
if v == "unknown":
|
|
1783
|
+
return "MoAI-ADK unknown version"
|
|
1784
|
+
elif v.startswith("v"):
|
|
1785
|
+
return f"MoAI-ADK {v}"
|
|
1786
|
+
else:
|
|
1787
|
+
return f"MoAI-ADK v{v}"
|
|
1788
|
+
|
|
1789
|
+
def format_trimmed_version(v: str, max_length: int = 10) -> str:
|
|
1790
|
+
"""Format version with maximum length for UI displays."""
|
|
1791
|
+
if v == "unknown":
|
|
1792
|
+
return "unknown"
|
|
1793
|
+
clean_version = v[1:] if v.startswith("v") else v
|
|
1794
|
+
if len(clean_version) > max_length:
|
|
1795
|
+
return clean_version[:max_length]
|
|
1796
|
+
return clean_version
|
|
1797
|
+
|
|
1798
|
+
def format_semver_version(v: str) -> str:
|
|
1799
|
+
"""Format version as semantic version."""
|
|
1800
|
+
if v == "unknown":
|
|
1801
|
+
return "0.0.0"
|
|
1802
|
+
clean_version = v[1:] if v.startswith("v") else v
|
|
1803
|
+
import re
|
|
1804
|
+
|
|
1805
|
+
semver_match = re.match(r"^(\d+\.\d+\.\d+)", clean_version)
|
|
1806
|
+
if semver_match:
|
|
1807
|
+
return semver_match.group(1)
|
|
1808
|
+
return "0.0.0"
|
|
743
1809
|
|
|
744
1810
|
return {
|
|
745
1811
|
"MOAI_VERSION": version_for_config,
|
|
1812
|
+
"MOAI_VERSION_SHORT": format_short_version(version_for_config),
|
|
1813
|
+
"MOAI_VERSION_DISPLAY": format_display_version(version_for_config),
|
|
1814
|
+
"MOAI_VERSION_TRIMMED": format_trimmed_version(version_for_config),
|
|
1815
|
+
"MOAI_VERSION_SEMVER": format_semver_version(version_for_config),
|
|
1816
|
+
"MOAI_VERSION_VALID": "true" if version_for_config != "unknown" else "false",
|
|
1817
|
+
"MOAI_VERSION_SOURCE": "config_cached",
|
|
746
1818
|
"PROJECT_NAME": project_name,
|
|
747
1819
|
"PROJECT_MODE": project_mode,
|
|
748
1820
|
"PROJECT_DESCRIPTION": project_description,
|
|
@@ -750,12 +1822,23 @@ def _build_template_context(
|
|
|
750
1822
|
"CREATION_TIMESTAMP": created_at,
|
|
751
1823
|
"PROJECT_DIR": hook_project_dir,
|
|
752
1824
|
"CONVERSATION_LANGUAGE": language_config.get("conversation_language", "en"),
|
|
753
|
-
"CONVERSATION_LANGUAGE_NAME": language_config.get(
|
|
754
|
-
|
|
1825
|
+
"CONVERSATION_LANGUAGE_NAME": language_config.get("conversation_language_name", "English"),
|
|
1826
|
+
"AGENT_PROMPT_LANGUAGE": language_config.get("agent_prompt_language", "en"),
|
|
1827
|
+
"GIT_COMMIT_MESSAGES_LANGUAGE": language_config.get("git_commit_messages", "en"),
|
|
1828
|
+
"CODE_COMMENTS_LANGUAGE": language_config.get("code_comments", "en"),
|
|
1829
|
+
"DOCUMENTATION_LANGUAGE": language_config.get(
|
|
1830
|
+
"documentation", language_config.get("conversation_language", "en")
|
|
755
1831
|
),
|
|
1832
|
+
"ERROR_MESSAGES_LANGUAGE": language_config.get(
|
|
1833
|
+
"error_messages", language_config.get("conversation_language", "en")
|
|
1834
|
+
),
|
|
1835
|
+
"USER_NAME": user_name,
|
|
1836
|
+
"PERSONALIZED_GREETING": personalized_greeting,
|
|
1837
|
+
"LANGUAGE_CONFIG_SOURCE": config_source,
|
|
756
1838
|
"CODEBASE_LANGUAGE": project_section.get("language", "generic"),
|
|
757
1839
|
"PROJECT_OWNER": project_section.get("author", "@user"),
|
|
758
1840
|
"AUTHOR": project_section.get("author", "@user"),
|
|
1841
|
+
"STATUSLINE_COMMAND": statusline_command,
|
|
759
1842
|
}
|
|
760
1843
|
|
|
761
1844
|
|
|
@@ -765,18 +1848,18 @@ def _preserve_project_metadata(
|
|
|
765
1848
|
existing_config: dict[str, Any],
|
|
766
1849
|
version_for_config: str,
|
|
767
1850
|
) -> None:
|
|
768
|
-
"""Restore project-specific metadata in the new config.
|
|
1851
|
+
"""Restore project-specific metadata in the new config (YAML or JSON).
|
|
769
1852
|
|
|
770
1853
|
Also updates template_version to track which template version is synchronized.
|
|
771
1854
|
"""
|
|
772
|
-
config_path = project_path
|
|
1855
|
+
config_path, _ = _get_config_path(project_path)
|
|
773
1856
|
if not config_path.exists():
|
|
774
1857
|
return
|
|
775
1858
|
|
|
776
1859
|
try:
|
|
777
|
-
config_data =
|
|
778
|
-
except json.JSONDecodeError:
|
|
779
|
-
console.print("[red]โ Failed to parse config
|
|
1860
|
+
config_data = _load_config(config_path)
|
|
1861
|
+
except (json.JSONDecodeError, yaml.YAMLError):
|
|
1862
|
+
console.print("[red]โ Failed to parse config after template copy[/red]")
|
|
780
1863
|
return
|
|
781
1864
|
|
|
782
1865
|
project_data = config_data.setdefault("project", {})
|
|
@@ -796,9 +1879,7 @@ def _preserve_project_metadata(
|
|
|
796
1879
|
if locale:
|
|
797
1880
|
project_data["locale"] = locale
|
|
798
1881
|
|
|
799
|
-
language = _coalesce(
|
|
800
|
-
existing_project.get("language"), existing_config.get("language")
|
|
801
|
-
)
|
|
1882
|
+
language = _coalesce(existing_project.get("language"), existing_config.get("language"))
|
|
802
1883
|
if language:
|
|
803
1884
|
project_data["language"] = language
|
|
804
1885
|
|
|
@@ -808,9 +1889,7 @@ def _preserve_project_metadata(
|
|
|
808
1889
|
# This allows Stage 2 to compare package vs project template versions
|
|
809
1890
|
project_data["template_version"] = version_for_config
|
|
810
1891
|
|
|
811
|
-
config_path
|
|
812
|
-
json.dumps(config_data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8"
|
|
813
|
-
)
|
|
1892
|
+
_save_config(config_path, config_data)
|
|
814
1893
|
|
|
815
1894
|
|
|
816
1895
|
def _apply_context_to_file(processor: TemplateProcessor, target_path: Path) -> None:
|
|
@@ -823,9 +1902,7 @@ def _apply_context_to_file(processor: TemplateProcessor, target_path: Path) -> N
|
|
|
823
1902
|
except UnicodeDecodeError:
|
|
824
1903
|
return
|
|
825
1904
|
|
|
826
|
-
substituted, warnings = processor._substitute_variables(
|
|
827
|
-
content
|
|
828
|
-
) # pylint: disable=protected-access
|
|
1905
|
+
substituted, warnings = processor._substitute_variables(content) # pylint: disable=protected-access
|
|
829
1906
|
if warnings:
|
|
830
1907
|
console.print("[yellow]โ Template warnings:[/yellow]")
|
|
831
1908
|
for warning in warnings:
|
|
@@ -856,28 +1933,20 @@ def _validate_template_substitution(project_path: Path) -> None:
|
|
|
856
1933
|
unsubstituted = re.findall(r"\{\{([A-Z_]+)\}\}", content)
|
|
857
1934
|
if unsubstituted:
|
|
858
1935
|
unique_vars = sorted(set(unsubstituted))
|
|
859
|
-
issues_found.append(
|
|
860
|
-
f"{file_path.relative_to(project_path)}: {', '.join(unique_vars)}"
|
|
861
|
-
)
|
|
1936
|
+
issues_found.append(f"{file_path.relative_to(project_path)}: {', '.join(unique_vars)}")
|
|
862
1937
|
except Exception as e:
|
|
863
|
-
console.print(
|
|
864
|
-
f"[yellow]โ ๏ธ Could not validate {file_path.relative_to(project_path)}: {e}[/yellow]"
|
|
865
|
-
)
|
|
1938
|
+
console.print(f"[yellow]โ ๏ธ Could not validate {file_path.relative_to(project_path)}: {e}[/yellow]")
|
|
866
1939
|
|
|
867
1940
|
if issues_found:
|
|
868
1941
|
console.print("[red]โ Template substitution validation failed:[/red]")
|
|
869
1942
|
for issue in issues_found:
|
|
870
1943
|
console.print(f" {issue}")
|
|
871
|
-
console.print(
|
|
872
|
-
"[yellow]๐ก Run '/alfred:0-project' to fix template variables[/yellow]"
|
|
873
|
-
)
|
|
1944
|
+
console.print("[yellow]๐ก Run '/moai:0-project' to fix template variables[/yellow]")
|
|
874
1945
|
else:
|
|
875
1946
|
console.print("[green]โ
Template substitution validation passed[/green]")
|
|
876
1947
|
|
|
877
1948
|
|
|
878
|
-
def _validate_template_substitution_with_rollback(
|
|
879
|
-
project_path: Path, backup_path: Path | None
|
|
880
|
-
) -> bool:
|
|
1949
|
+
def _validate_template_substitution_with_rollback(project_path: Path, backup_path: Path | None) -> bool:
|
|
881
1950
|
"""Validate template substitution with rollback capability.
|
|
882
1951
|
|
|
883
1952
|
Returns:
|
|
@@ -903,13 +1972,9 @@ def _validate_template_substitution_with_rollback(
|
|
|
903
1972
|
unsubstituted = re.findall(r"\{\{([A-Z_]+)\}\}", content)
|
|
904
1973
|
if unsubstituted:
|
|
905
1974
|
unique_vars = sorted(set(unsubstituted))
|
|
906
|
-
issues_found.append(
|
|
907
|
-
f"{file_path.relative_to(project_path)}: {', '.join(unique_vars)}"
|
|
908
|
-
)
|
|
1975
|
+
issues_found.append(f"{file_path.relative_to(project_path)}: {', '.join(unique_vars)}")
|
|
909
1976
|
except Exception as e:
|
|
910
|
-
console.print(
|
|
911
|
-
f"[yellow]โ ๏ธ Could not validate {file_path.relative_to(project_path)}: {e}[/yellow]"
|
|
912
|
-
)
|
|
1977
|
+
console.print(f"[yellow]โ ๏ธ Could not validate {file_path.relative_to(project_path)}: {e}[/yellow]")
|
|
913
1978
|
|
|
914
1979
|
if issues_found:
|
|
915
1980
|
console.print("[red]โ Template substitution validation failed:[/red]")
|
|
@@ -917,13 +1982,9 @@ def _validate_template_substitution_with_rollback(
|
|
|
917
1982
|
console.print(f" {issue}")
|
|
918
1983
|
|
|
919
1984
|
if backup_path:
|
|
920
|
-
console.print(
|
|
921
|
-
"[yellow]๐ Rolling back due to validation failure...[/yellow]"
|
|
922
|
-
)
|
|
1985
|
+
console.print("[yellow]๐ Rolling back due to validation failure...[/yellow]")
|
|
923
1986
|
else:
|
|
924
|
-
console.print(
|
|
925
|
-
"[yellow]๐ก Run '/alfred:0-project' to fix template variables[/yellow]"
|
|
926
|
-
)
|
|
1987
|
+
console.print("[yellow]๐ก Run '/moai:0-project' to fix template variables[/yellow]")
|
|
927
1988
|
console.print("[red]โ ๏ธ No backup available - manual fix required[/red]")
|
|
928
1989
|
|
|
929
1990
|
return False
|
|
@@ -978,18 +2039,14 @@ def _show_network_error_help() -> None:
|
|
|
978
2039
|
console.print("Options:")
|
|
979
2040
|
console.print(" 1. Check network connection")
|
|
980
2041
|
console.print(" 2. Try again with: [cyan]moai-adk update --force[/cyan]")
|
|
981
|
-
console.print(
|
|
982
|
-
" 3. Skip version check: [cyan]moai-adk update --templates-only[/cyan]"
|
|
983
|
-
)
|
|
2042
|
+
console.print(" 3. Skip version check: [cyan]moai-adk update --templates-only[/cyan]")
|
|
984
2043
|
|
|
985
2044
|
|
|
986
2045
|
def _show_template_sync_failure_help() -> None:
|
|
987
2046
|
"""Show help when template sync fails."""
|
|
988
2047
|
console.print("[yellow]โ ๏ธ Template sync failed[/yellow]\n")
|
|
989
2048
|
console.print("Rollback options:")
|
|
990
|
-
console.print(
|
|
991
|
-
" 1. Restore from backup: [cyan]cp -r .moai-backups/TIMESTAMP .moai/[/cyan]"
|
|
992
|
-
)
|
|
2049
|
+
console.print(" 1. Restore from backup: [cyan]cp -r .moai-backups/TIMESTAMP .moai/[/cyan]")
|
|
993
2050
|
console.print(" 2. Skip backup and retry: [cyan]moai-adk update --force[/cyan]")
|
|
994
2051
|
console.print(" 3. Report issue: https://github.com/modu-ai/moai-adk/issues")
|
|
995
2052
|
|
|
@@ -1027,24 +2084,17 @@ def _execute_migration_if_needed(project_path: Path, yes: bool = False) -> bool:
|
|
|
1027
2084
|
console.print()
|
|
1028
2085
|
console.print(" This will migrate configuration files to new locations:")
|
|
1029
2086
|
console.print(" โข .moai/config.json โ .moai/config/config.json")
|
|
1030
|
-
console.print(
|
|
1031
|
-
" โข .claude/statusline-config.yaml โ .moai/config/statusline-config.yaml"
|
|
1032
|
-
)
|
|
2087
|
+
console.print(" โข .claude/statusline-config.yaml โ .moai/config/statusline-config.yaml")
|
|
1033
2088
|
console.print()
|
|
1034
2089
|
console.print(" A backup will be created automatically.")
|
|
1035
2090
|
console.print()
|
|
1036
2091
|
|
|
1037
2092
|
# Confirm with user (unless --yes)
|
|
1038
2093
|
if not yes:
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
console.print(
|
|
1043
|
-
"[yellow]โ ๏ธ Migration skipped. Some features may not work correctly.[/yellow]"
|
|
1044
|
-
)
|
|
1045
|
-
console.print(
|
|
1046
|
-
"[cyan]๐ก Run 'moai-adk migrate' manually when ready[/cyan]"
|
|
1047
|
-
)
|
|
2094
|
+
reset_stdin() # Reset stdin before interactive prompt
|
|
2095
|
+
if not click.confirm("Do you want to proceed with migration?", default=True):
|
|
2096
|
+
console.print("[yellow]โ ๏ธ Migration skipped. Some features may not work correctly.[/yellow]")
|
|
2097
|
+
console.print("[cyan]๐ก Run 'moai-adk migrate' manually when ready[/cyan]")
|
|
1048
2098
|
return False
|
|
1049
2099
|
|
|
1050
2100
|
# Execute migration
|
|
@@ -1056,9 +2106,7 @@ def _execute_migration_if_needed(project_path: Path, yes: bool = False) -> bool:
|
|
|
1056
2106
|
return True
|
|
1057
2107
|
else:
|
|
1058
2108
|
console.print("[red]โ Migration failed[/red]")
|
|
1059
|
-
console.print(
|
|
1060
|
-
"[cyan]๐ก Use 'moai-adk migrate --rollback' to restore from backup[/cyan]"
|
|
1061
|
-
)
|
|
2109
|
+
console.print("[cyan]๐ก Use 'moai-adk migrate --rollback' to restore from backup[/cyan]")
|
|
1062
2110
|
return False
|
|
1063
2111
|
|
|
1064
2112
|
except Exception as e:
|
|
@@ -1076,14 +2124,29 @@ def _execute_migration_if_needed(project_path: Path, yes: bool = False) -> bool:
|
|
|
1076
2124
|
)
|
|
1077
2125
|
@click.option("--force", is_flag=True, help="Skip backup and force the update")
|
|
1078
2126
|
@click.option("--check", is_flag=True, help="Only check version (do not update)")
|
|
2127
|
+
@click.option("--templates-only", is_flag=True, help="Skip package upgrade, sync templates only")
|
|
2128
|
+
@click.option("--yes", is_flag=True, help="Auto-confirm all prompts (CI/CD mode)")
|
|
1079
2129
|
@click.option(
|
|
1080
|
-
"--
|
|
2130
|
+
"--merge",
|
|
2131
|
+
"merge_strategy",
|
|
2132
|
+
flag_value="auto",
|
|
2133
|
+
help="Auto-merge: Apply template + preserve user changes",
|
|
2134
|
+
)
|
|
2135
|
+
@click.option(
|
|
2136
|
+
"--manual",
|
|
2137
|
+
"merge_strategy",
|
|
2138
|
+
flag_value="manual",
|
|
2139
|
+
help="Manual merge: Preserve backup, generate merge guide",
|
|
1081
2140
|
)
|
|
1082
|
-
@click.option("--yes", is_flag=True, help="Auto-confirm all prompts (CI/CD mode)")
|
|
1083
2141
|
def update(
|
|
1084
|
-
path: str,
|
|
2142
|
+
path: str,
|
|
2143
|
+
force: bool,
|
|
2144
|
+
check: bool,
|
|
2145
|
+
templates_only: bool,
|
|
2146
|
+
yes: bool,
|
|
2147
|
+
merge_strategy: str | None,
|
|
1085
2148
|
) -> None:
|
|
1086
|
-
"""Update command with 3-stage workflow (v0.
|
|
2149
|
+
"""Update command with 3-stage workflow + merge strategy selection (v0.26.0+).
|
|
1087
2150
|
|
|
1088
2151
|
Stage 1 (Package Version Check):
|
|
1089
2152
|
- Fetches current and latest versions from PyPI
|
|
@@ -1095,18 +2158,34 @@ def update(
|
|
|
1095
2158
|
- If versions match: skips Stage 3 (already up-to-date)
|
|
1096
2159
|
- Performance improvement: 70-80% faster for unchanged projects (3-4s vs 12-18s)
|
|
1097
2160
|
|
|
1098
|
-
Stage 3 (Template Sync):
|
|
2161
|
+
Stage 3 (Template Sync with Merge Strategy - NEW in v0.26.0):
|
|
1099
2162
|
- Syncs templates only if versions differ
|
|
2163
|
+
- User chooses merge strategy:
|
|
2164
|
+
* Auto-merge (default): Template + preserved user changes
|
|
2165
|
+
* Manual merge: Backup + comprehensive merge guide (full control)
|
|
1100
2166
|
- Updates .claude/, .moai/, CLAUDE.md, config.json
|
|
1101
2167
|
- Preserves specs and reports
|
|
1102
2168
|
- Saves new template_version to config.json
|
|
1103
2169
|
|
|
1104
2170
|
Examples:
|
|
1105
|
-
python -m moai_adk update #
|
|
1106
|
-
python -m moai_adk update --
|
|
2171
|
+
python -m moai_adk update # interactive merge strategy selection
|
|
2172
|
+
python -m moai_adk update --merge # auto-merge (template + user changes)
|
|
2173
|
+
python -m moai_adk update --manual # manual merge (backup + guide)
|
|
2174
|
+
python -m moai_adk update --force # force template sync (no backup)
|
|
1107
2175
|
python -m moai_adk update --check # check version only
|
|
1108
2176
|
python -m moai_adk update --templates-only # skip package upgrade
|
|
1109
|
-
python -m moai_adk update --yes # CI/CD mode (auto-confirm)
|
|
2177
|
+
python -m moai_adk update --yes # CI/CD mode (auto-confirm + auto-merge)
|
|
2178
|
+
|
|
2179
|
+
Merge Strategies:
|
|
2180
|
+
--merge: Auto-merge applies template + preserves your changes (default)
|
|
2181
|
+
Generated files: backup, merge report
|
|
2182
|
+
--manual: Manual merge preserves backup + generates comprehensive guide
|
|
2183
|
+
Generated files: backup, merge guide
|
|
2184
|
+
|
|
2185
|
+
Generated Files:
|
|
2186
|
+
- Backup: .moai-backups/pre-update-backup_{timestamp}/
|
|
2187
|
+
- Report: .moai/reports/merge-report.md (auto-merge only)
|
|
2188
|
+
- Guide: .moai/guides/merge-guide.md (manual merge only)
|
|
1110
2189
|
"""
|
|
1111
2190
|
try:
|
|
1112
2191
|
project_path = Path(path).resolve()
|
|
@@ -1120,14 +2199,24 @@ def update(
|
|
|
1120
2199
|
# Note: If --check is used, always fetch versions even if --templates-only is also present
|
|
1121
2200
|
if check or not templates_only:
|
|
1122
2201
|
try:
|
|
1123
|
-
|
|
1124
|
-
|
|
2202
|
+
# Try to use new spinner UI
|
|
2203
|
+
try:
|
|
2204
|
+
from moai_adk.cli.ui.progress import SpinnerContext
|
|
2205
|
+
|
|
2206
|
+
with SpinnerContext("Checking for updates...") as spinner:
|
|
2207
|
+
current = _get_current_version()
|
|
2208
|
+
spinner.update("Fetching latest version from PyPI...")
|
|
2209
|
+
latest = _get_latest_version()
|
|
2210
|
+
spinner.success("Version check complete")
|
|
2211
|
+
except ImportError:
|
|
2212
|
+
# Fallback to simple console output
|
|
2213
|
+
console.print("[dim]Checking for updates...[/dim]")
|
|
2214
|
+
current = _get_current_version()
|
|
2215
|
+
latest = _get_latest_version()
|
|
1125
2216
|
except RuntimeError as e:
|
|
1126
2217
|
console.print(f"[red]Error: {e}[/red]")
|
|
1127
2218
|
if not force:
|
|
1128
|
-
console.print(
|
|
1129
|
-
"[yellow]โ Cannot check for updates. Use --force to update anyway.[/yellow]"
|
|
1130
|
-
)
|
|
2219
|
+
console.print("[yellow]โ Cannot check for updates. Use --force to update anyway.[/yellow]")
|
|
1131
2220
|
raise click.Abort()
|
|
1132
2221
|
# With --force, proceed to Stage 2 even if version check fails
|
|
1133
2222
|
current = __version__
|
|
@@ -1139,23 +2228,24 @@ def update(
|
|
|
1139
2228
|
if check:
|
|
1140
2229
|
comparison = _compare_versions(current, latest)
|
|
1141
2230
|
if comparison < 0:
|
|
1142
|
-
console.print(
|
|
1143
|
-
f"\n[yellow]๐ฆ Update available: {current} โ {latest}[/yellow]"
|
|
1144
|
-
)
|
|
2231
|
+
console.print(f"\n[yellow]๐ฆ Update available: {current} โ {latest}[/yellow]")
|
|
1145
2232
|
console.print(" Run 'moai-adk update' to upgrade")
|
|
1146
2233
|
elif comparison == 0:
|
|
1147
2234
|
console.print(f"[green]โ Already up to date ({current})[/green]")
|
|
1148
2235
|
else:
|
|
1149
|
-
console.print(
|
|
1150
|
-
f"[cyan]โน๏ธ Dev version: {current} (latest: {latest})[/cyan]"
|
|
1151
|
-
)
|
|
2236
|
+
console.print(f"[cyan]โน๏ธ Dev version: {current} (latest: {latest})[/cyan]")
|
|
1152
2237
|
return
|
|
1153
2238
|
|
|
1154
2239
|
# Step 2: Handle --templates-only (skip upgrade, go straight to sync)
|
|
1155
2240
|
if templates_only:
|
|
1156
2241
|
console.print("[cyan]๐ Syncing templates only...[/cyan]")
|
|
2242
|
+
|
|
2243
|
+
# Preserve user-specific settings before sync
|
|
2244
|
+
console.print(" [cyan]๐พ Preserving user settings...[/cyan]")
|
|
2245
|
+
preserved_settings = _preserve_user_settings(project_path)
|
|
2246
|
+
|
|
1157
2247
|
try:
|
|
1158
|
-
if not _sync_templates(project_path, force):
|
|
2248
|
+
if not _sync_templates(project_path, force, yes):
|
|
1159
2249
|
raise TemplateSyncError("Template sync returned False")
|
|
1160
2250
|
except TemplateSyncError:
|
|
1161
2251
|
console.print("[red]Error: Template sync failed[/red]")
|
|
@@ -1166,10 +2256,11 @@ def update(
|
|
|
1166
2256
|
_show_template_sync_failure_help()
|
|
1167
2257
|
raise click.Abort()
|
|
1168
2258
|
|
|
2259
|
+
# Restore user-specific settings after sync
|
|
2260
|
+
_restore_user_settings(project_path, preserved_settings)
|
|
2261
|
+
|
|
1169
2262
|
console.print(" [green]โ
.claude/ update complete[/green]")
|
|
1170
|
-
console.print(
|
|
1171
|
-
" [green]โ
.moai/ update complete (specs/reports preserved)[/green]"
|
|
1172
|
-
)
|
|
2263
|
+
console.print(" [green]โ
.moai/ update complete (specs/reports preserved)[/green]")
|
|
1173
2264
|
console.print(" [green]๐ CLAUDE.md merge complete[/green]")
|
|
1174
2265
|
console.print(" [green]๐ config.json merge complete[/green]")
|
|
1175
2266
|
console.print("\n[green]โ Template sync complete![/green]")
|
|
@@ -1184,6 +2275,7 @@ def update(
|
|
|
1184
2275
|
|
|
1185
2276
|
# Confirm upgrade (unless --yes)
|
|
1186
2277
|
if not yes:
|
|
2278
|
+
reset_stdin() # Reset stdin before interactive prompt
|
|
1187
2279
|
if not click.confirm(f"Upgrade {current} โ {latest}?", default=True):
|
|
1188
2280
|
console.print("Cancelled")
|
|
1189
2281
|
return
|
|
@@ -1204,9 +2296,7 @@ def update(
|
|
|
1204
2296
|
try:
|
|
1205
2297
|
upgrade_result = _execute_upgrade(installer_cmd)
|
|
1206
2298
|
if not upgrade_result:
|
|
1207
|
-
raise UpgradeError(
|
|
1208
|
-
f"Upgrade command failed: {' '.join(installer_cmd)}"
|
|
1209
|
-
)
|
|
2299
|
+
raise UpgradeError(f"Upgrade command failed: {' '.join(installer_cmd)}")
|
|
1210
2300
|
except subprocess.TimeoutExpired:
|
|
1211
2301
|
_show_timeout_error_help()
|
|
1212
2302
|
raise click.Abort()
|
|
@@ -1216,9 +2306,7 @@ def update(
|
|
|
1216
2306
|
|
|
1217
2307
|
# Prompt re-run
|
|
1218
2308
|
console.print("\n[green]โ Upgrade complete![/green]")
|
|
1219
|
-
console.print(
|
|
1220
|
-
"[cyan]๐ข Run 'moai-adk update' again to sync templates[/cyan]"
|
|
1221
|
-
)
|
|
2309
|
+
console.print("[cyan]๐ข Run 'moai-adk update' again to sync templates[/cyan]")
|
|
1222
2310
|
return
|
|
1223
2311
|
|
|
1224
2312
|
# Stage 1.5: Migration Check (NEW in v0.24.0)
|
|
@@ -1227,9 +2315,12 @@ def update(
|
|
|
1227
2315
|
# Execute migration if needed
|
|
1228
2316
|
if not _execute_migration_if_needed(project_path, yes):
|
|
1229
2317
|
console.print("[yellow]โ ๏ธ Update continuing without migration[/yellow]")
|
|
1230
|
-
console.print(
|
|
1231
|
-
|
|
1232
|
-
|
|
2318
|
+
console.print("[cyan]๐ก Some features may require migration to work correctly[/cyan]")
|
|
2319
|
+
|
|
2320
|
+
# Migrate config.json โ config.yaml (v0.32.0+)
|
|
2321
|
+
console.print("\n[cyan]๐ Checking for config format migration...[/cyan]")
|
|
2322
|
+
if not _migrate_config_json_to_yaml(project_path):
|
|
2323
|
+
console.print("[yellow]โ ๏ธ Config migration failed, continuing with existing format[/yellow]")
|
|
1233
2324
|
|
|
1234
2325
|
# Stage 2: Config Version Comparison
|
|
1235
2326
|
try:
|
|
@@ -1246,32 +2337,66 @@ def update(
|
|
|
1246
2337
|
console.print(f" Project config: {project_config_version}")
|
|
1247
2338
|
|
|
1248
2339
|
try:
|
|
1249
|
-
config_comparison = _compare_versions(
|
|
1250
|
-
package_config_version, project_config_version
|
|
1251
|
-
)
|
|
2340
|
+
config_comparison = _compare_versions(package_config_version, project_config_version)
|
|
1252
2341
|
except version.InvalidVersion as e:
|
|
1253
2342
|
# Handle invalid version strings (e.g., unsubstituted template placeholders, corrupted configs)
|
|
1254
2343
|
console.print(f"[yellow]โ Invalid version format in config: {e}[/yellow]")
|
|
1255
|
-
console.print(
|
|
1256
|
-
"[cyan]โน๏ธ Forcing template sync to repair configuration...[/cyan]"
|
|
1257
|
-
)
|
|
2344
|
+
console.print("[cyan]โน๏ธ Forcing template sync to repair configuration...[/cyan]")
|
|
1258
2345
|
# Force template sync by treating project version as outdated
|
|
1259
2346
|
config_comparison = 1 # package_config_version > project_config_version
|
|
1260
2347
|
|
|
1261
2348
|
# If versions are equal, no sync needed
|
|
1262
2349
|
if config_comparison <= 0:
|
|
1263
|
-
console.print(
|
|
1264
|
-
|
|
1265
|
-
)
|
|
1266
|
-
console.print(
|
|
1267
|
-
"[cyan]โน๏ธ Templates are up to date! No changes needed.[/cyan]"
|
|
1268
|
-
)
|
|
2350
|
+
console.print(f"\n[green]โ Project already has latest template version ({project_config_version})[/green]")
|
|
2351
|
+
console.print("[cyan]โน๏ธ Templates are up to date! No changes needed.[/cyan]")
|
|
1269
2352
|
return
|
|
1270
2353
|
|
|
1271
2354
|
# Stage 3: Template Sync (Only if package_config_version > project_config_version)
|
|
1272
|
-
console.print(
|
|
1273
|
-
|
|
1274
|
-
)
|
|
2355
|
+
console.print(f"\n[cyan]๐ Syncing templates ({project_config_version} โ {package_config_version})...[/cyan]")
|
|
2356
|
+
|
|
2357
|
+
# Determine merge strategy (default: auto-merge)
|
|
2358
|
+
final_merge_strategy = merge_strategy or "auto"
|
|
2359
|
+
|
|
2360
|
+
# Handle merge strategy
|
|
2361
|
+
if final_merge_strategy == "manual":
|
|
2362
|
+
# Manual merge mode: Create full backup + generate guide, no template sync
|
|
2363
|
+
console.print("\n[cyan]๐ Manual merge mode selected[/cyan]")
|
|
2364
|
+
|
|
2365
|
+
# Create full project backup
|
|
2366
|
+
console.print(" [cyan]๐พ Creating full project backup...[/cyan]")
|
|
2367
|
+
try:
|
|
2368
|
+
from moai_adk.core.migration.backup_manager import BackupManager
|
|
2369
|
+
|
|
2370
|
+
backup_manager = BackupManager(project_path)
|
|
2371
|
+
full_backup_path = backup_manager.create_full_project_backup(description="pre-update-backup")
|
|
2372
|
+
console.print(f" [green]โ Backup: {full_backup_path.relative_to(project_path)}/[/green]")
|
|
2373
|
+
|
|
2374
|
+
# Generate merge guide
|
|
2375
|
+
console.print(" [cyan]๐ Generating merge guide...[/cyan]")
|
|
2376
|
+
template_path = Path(__file__).parent.parent.parent / "templates"
|
|
2377
|
+
guide_path = _generate_manual_merge_guide(full_backup_path, template_path, project_path)
|
|
2378
|
+
console.print(f" [green]โ Guide: {guide_path.relative_to(project_path)}[/green]")
|
|
2379
|
+
|
|
2380
|
+
# Summary
|
|
2381
|
+
console.print("\n[green]โ Manual merge setup complete![/green]")
|
|
2382
|
+
console.print(f"[cyan]๐ Backup location: {full_backup_path.relative_to(project_path)}/[/cyan]")
|
|
2383
|
+
console.print(f"[cyan]๐ Merge guide: {guide_path.relative_to(project_path)}[/cyan]")
|
|
2384
|
+
console.print("\n[yellow]โ ๏ธ Next steps:[/yellow]")
|
|
2385
|
+
console.print("[yellow] 1. Review the merge guide[/yellow]")
|
|
2386
|
+
console.print("[yellow] 2. Compare files using diff or visual tools[/yellow]")
|
|
2387
|
+
console.print("[yellow] 3. Manually merge your customizations[/yellow]")
|
|
2388
|
+
console.print("[yellow] 4. Test and commit changes[/yellow]")
|
|
2389
|
+
|
|
2390
|
+
except Exception as e:
|
|
2391
|
+
console.print(f"[red]Error: Manual merge setup failed - {e}[/red]")
|
|
2392
|
+
raise click.Abort()
|
|
2393
|
+
|
|
2394
|
+
return
|
|
2395
|
+
|
|
2396
|
+
# Auto merge mode: Preserve user-specific settings before sync
|
|
2397
|
+
console.print("\n[cyan]๐ Auto-merge mode selected[/cyan]")
|
|
2398
|
+
console.print(" [cyan]๐พ Preserving user settings...[/cyan]")
|
|
2399
|
+
preserved_settings = _preserve_user_settings(project_path)
|
|
1275
2400
|
|
|
1276
2401
|
# Create backup unless --force
|
|
1277
2402
|
if not force:
|
|
@@ -1279,19 +2404,21 @@ def update(
|
|
|
1279
2404
|
try:
|
|
1280
2405
|
processor = TemplateProcessor(project_path)
|
|
1281
2406
|
backup_path = processor.create_backup()
|
|
1282
|
-
console.print(
|
|
1283
|
-
f" [green]โ Backup: {backup_path.relative_to(project_path)}/[/green]"
|
|
1284
|
-
)
|
|
2407
|
+
console.print(f" [green]โ Backup: {backup_path.relative_to(project_path)}/[/green]")
|
|
1285
2408
|
except Exception as e:
|
|
1286
2409
|
console.print(f" [yellow]โ Backup failed: {e}[/yellow]")
|
|
1287
2410
|
console.print(" [yellow]โ Continuing without backup...[/yellow]")
|
|
1288
2411
|
else:
|
|
1289
2412
|
console.print(" [yellow]โ Skipping backup (--force)[/yellow]")
|
|
1290
2413
|
|
|
1291
|
-
# Sync templates
|
|
2414
|
+
# Sync templates (NO spinner - user interaction may be required)
|
|
2415
|
+
# SpinnerContext blocks stdin, causing hang when click.confirm() is called
|
|
1292
2416
|
try:
|
|
1293
|
-
|
|
2417
|
+
console.print(" [cyan]Syncing templates...[/cyan]")
|
|
2418
|
+
if not _sync_templates(project_path, force, yes):
|
|
1294
2419
|
raise TemplateSyncError("Template sync returned False")
|
|
2420
|
+
_restore_user_settings(project_path, preserved_settings)
|
|
2421
|
+
console.print(" [green]โ Template sync complete[/green]")
|
|
1295
2422
|
except TemplateSyncError:
|
|
1296
2423
|
console.print("[red]Error: Template sync failed[/red]")
|
|
1297
2424
|
_show_template_sync_failure_help()
|
|
@@ -1302,20 +2429,263 @@ def update(
|
|
|
1302
2429
|
raise click.Abort()
|
|
1303
2430
|
|
|
1304
2431
|
console.print(" [green]โ
.claude/ update complete[/green]")
|
|
1305
|
-
console.print(
|
|
1306
|
-
" [green]โ
.moai/ update complete (specs/reports preserved)[/green]"
|
|
1307
|
-
)
|
|
2432
|
+
console.print(" [green]โ
.moai/ update complete (specs/reports preserved)[/green]")
|
|
1308
2433
|
console.print(" [green]๐ CLAUDE.md merge complete[/green]")
|
|
1309
2434
|
console.print(" [green]๐ config.json merge complete[/green]")
|
|
1310
|
-
console.print(
|
|
1311
|
-
" [yellow]โ๏ธ Set optimized=false (optimization needed)[/yellow]"
|
|
1312
|
-
)
|
|
2435
|
+
console.print(" [yellow]โ๏ธ Set optimized=false (optimization needed)[/yellow]")
|
|
1313
2436
|
|
|
1314
2437
|
console.print("\n[green]โ Update complete![/green]")
|
|
1315
|
-
console.print(
|
|
1316
|
-
"[cyan]โน๏ธ Next step: Run /alfred:0-project update to optimize template changes[/cyan]"
|
|
1317
|
-
)
|
|
2438
|
+
console.print("[cyan]โน๏ธ Next step: Run /moai:0-project update to optimize template changes[/cyan]")
|
|
1318
2439
|
|
|
1319
2440
|
except Exception as e:
|
|
1320
2441
|
console.print(f"[red]โ Update failed: {e}[/red]")
|
|
1321
2442
|
raise click.ClickException(str(e)) from e
|
|
2443
|
+
|
|
2444
|
+
|
|
2445
|
+
def _handle_custom_element_restoration(project_path: Path, backup_path: Path | None, yes: bool = False) -> None:
|
|
2446
|
+
"""Handle custom element restoration using the enhanced system.
|
|
2447
|
+
|
|
2448
|
+
This function provides an improved interface for restoring user-created custom elements
|
|
2449
|
+
(agents, commands, skills, hooks) from backup during MoAI-ADK updates.
|
|
2450
|
+
|
|
2451
|
+
Key improvements:
|
|
2452
|
+
- Preserves unselected elements (fixes disappearing issue)
|
|
2453
|
+
- Only overwrites/creates selected elements from backup
|
|
2454
|
+
- Interactive checkbox selection with arrow key navigation
|
|
2455
|
+
- Includes all categories (Agents, Commands, Skills, Hooks)
|
|
2456
|
+
|
|
2457
|
+
Args:
|
|
2458
|
+
project_path: Path to the MoAI-ADK project directory
|
|
2459
|
+
backup_path: Path to the backup directory (None if no backup)
|
|
2460
|
+
yes: Whether to automatically accept defaults (non-interactive mode)
|
|
2461
|
+
"""
|
|
2462
|
+
if not backup_path:
|
|
2463
|
+
# No backup available, cannot restore
|
|
2464
|
+
return
|
|
2465
|
+
|
|
2466
|
+
try:
|
|
2467
|
+
# Create scanner to find custom elements in backup (not current project)
|
|
2468
|
+
backup_scanner = create_custom_element_scanner(backup_path)
|
|
2469
|
+
|
|
2470
|
+
# Get count of custom elements in backup
|
|
2471
|
+
backup_element_count = backup_scanner.get_element_count()
|
|
2472
|
+
|
|
2473
|
+
if backup_element_count == 0:
|
|
2474
|
+
# No custom elements found in backup
|
|
2475
|
+
console.print("[green]โ No custom elements found in backup to restore[/green]")
|
|
2476
|
+
return
|
|
2477
|
+
|
|
2478
|
+
# Create enhanced user selection UI
|
|
2479
|
+
# IMPORTANT: Use backup_path, not project_path!
|
|
2480
|
+
# At this point, custom elements in project have been deleted by copy_templates().
|
|
2481
|
+
# The UI must scan the BACKUP to find elements available for restoration.
|
|
2482
|
+
ui = create_user_selection_ui(backup_path)
|
|
2483
|
+
|
|
2484
|
+
console.print(f"\n[cyan]๐ Found {backup_element_count} custom elements in backup[/cyan]")
|
|
2485
|
+
|
|
2486
|
+
# If yes mode is enabled, restore all elements automatically
|
|
2487
|
+
if yes:
|
|
2488
|
+
console.print(f"[cyan]๐ Auto-restoring {backup_element_count} custom elements...[/cyan]")
|
|
2489
|
+
backup_custom_elements = backup_scanner.scan_custom_elements()
|
|
2490
|
+
selected_elements = []
|
|
2491
|
+
|
|
2492
|
+
# Collect all element paths from backup
|
|
2493
|
+
for element_type, elements in backup_custom_elements.items():
|
|
2494
|
+
if element_type == "skills":
|
|
2495
|
+
for skill in elements:
|
|
2496
|
+
selected_elements.append(str(skill.path))
|
|
2497
|
+
else:
|
|
2498
|
+
for element_path in elements:
|
|
2499
|
+
selected_elements.append(str(element_path))
|
|
2500
|
+
else:
|
|
2501
|
+
# Interactive mode - prompt user for selection using enhanced UI
|
|
2502
|
+
selected_elements = ui.prompt_user_selection(backup_available=True)
|
|
2503
|
+
|
|
2504
|
+
if not selected_elements:
|
|
2505
|
+
console.print("[yellow]โ No elements selected for restoration[/yellow]")
|
|
2506
|
+
console.print("[green]โ All existing custom elements will be preserved[/green]")
|
|
2507
|
+
return
|
|
2508
|
+
|
|
2509
|
+
# Confirm selection
|
|
2510
|
+
if not ui.confirm_selection(selected_elements):
|
|
2511
|
+
console.print("[yellow]โ Restoration cancelled by user[/yellow]")
|
|
2512
|
+
console.print("[green]โ All existing custom elements will be preserved[/green]")
|
|
2513
|
+
return
|
|
2514
|
+
|
|
2515
|
+
# Perform selective restoration - ONLY restore selected elements
|
|
2516
|
+
if selected_elements:
|
|
2517
|
+
console.print(f"[cyan]๐ Restoring {len(selected_elements)} selected elements from backup...[/cyan]")
|
|
2518
|
+
restorer = create_selective_restorer(project_path, backup_path)
|
|
2519
|
+
success, stats = restorer.restore_elements(selected_elements)
|
|
2520
|
+
|
|
2521
|
+
if success:
|
|
2522
|
+
console.print(f"[green]โ
Successfully restored {stats['success']} custom elements[/green]")
|
|
2523
|
+
console.print("[green]โ All unselected elements remain preserved[/green]")
|
|
2524
|
+
else:
|
|
2525
|
+
console.print(f"[yellow]โ ๏ธ Partial restoration: {stats['success']}/{stats['total']} elements[/yellow]")
|
|
2526
|
+
if stats["failed"] > 0:
|
|
2527
|
+
console.print(f"[red]โ Failed to restore {stats['failed']} elements[/red]")
|
|
2528
|
+
console.print("[yellow]โ ๏ธ All other elements remain preserved[/yellow]")
|
|
2529
|
+
else:
|
|
2530
|
+
console.print("[green]โ No elements selected, all custom elements preserved[/green]")
|
|
2531
|
+
|
|
2532
|
+
except Exception as e:
|
|
2533
|
+
console.print(f"[yellow]โ ๏ธ Custom element restoration failed: {e}[/yellow]")
|
|
2534
|
+
logger.warning(f"Custom element restoration error: {e}")
|
|
2535
|
+
console.print("[yellow]โ ๏ธ All existing custom elements remain as-is[/yellow]")
|
|
2536
|
+
# Don't fail the entire update process, just log the error
|
|
2537
|
+
pass
|
|
2538
|
+
|
|
2539
|
+
|
|
2540
|
+
def _cleanup_legacy_presets(project_path: Path) -> None:
|
|
2541
|
+
"""Remove legacy presets directory entirely.
|
|
2542
|
+
|
|
2543
|
+
This function removes the entire .moai/config/presets/ directory as it is
|
|
2544
|
+
no longer used. All preset settings are now consolidated in sections/git-strategy.yaml.
|
|
2545
|
+
|
|
2546
|
+
Args:
|
|
2547
|
+
project_path: Project directory path (absolute)
|
|
2548
|
+
"""
|
|
2549
|
+
import shutil
|
|
2550
|
+
|
|
2551
|
+
presets_dir = project_path / ".moai" / "config" / "presets"
|
|
2552
|
+
|
|
2553
|
+
if not presets_dir.exists() or not presets_dir.is_dir():
|
|
2554
|
+
return
|
|
2555
|
+
|
|
2556
|
+
try:
|
|
2557
|
+
# Remove entire presets directory (no longer needed)
|
|
2558
|
+
shutil.rmtree(presets_dir)
|
|
2559
|
+
console.print(" [cyan]๐งน Removed legacy presets directory (now in sections/git-strategy.yaml)[/cyan]")
|
|
2560
|
+
logger.info(f"Removed legacy presets directory: {presets_dir}")
|
|
2561
|
+
except Exception as e:
|
|
2562
|
+
logger.warning(f"Failed to remove legacy presets directory {presets_dir}: {e}")
|
|
2563
|
+
|
|
2564
|
+
|
|
2565
|
+
def _migrate_config_json_to_yaml(project_path: Path) -> bool:
|
|
2566
|
+
"""Migrate legacy config.json to config.yaml format.
|
|
2567
|
+
|
|
2568
|
+
This function:
|
|
2569
|
+
1. Checks if config.json exists
|
|
2570
|
+
2. Converts it to config.yaml using YAML format
|
|
2571
|
+
3. Removes the old config.json file
|
|
2572
|
+
4. Also migrates preset files from JSON to YAML
|
|
2573
|
+
|
|
2574
|
+
Args:
|
|
2575
|
+
project_path: Project directory path (absolute)
|
|
2576
|
+
|
|
2577
|
+
Returns:
|
|
2578
|
+
bool: True if migration successful or not needed, False on error
|
|
2579
|
+
"""
|
|
2580
|
+
try:
|
|
2581
|
+
import yaml
|
|
2582
|
+
except ImportError:
|
|
2583
|
+
console.print(" [yellow]โ ๏ธ PyYAML not available, skipping config migration[/yellow]")
|
|
2584
|
+
return True # Not a critical error
|
|
2585
|
+
|
|
2586
|
+
config_dir = project_path / ".moai" / "config"
|
|
2587
|
+
json_path = config_dir / "config.json"
|
|
2588
|
+
yaml_path = config_dir / "config.yaml"
|
|
2589
|
+
|
|
2590
|
+
# Check if migration needed
|
|
2591
|
+
if not json_path.exists():
|
|
2592
|
+
# No JSON file, migration not needed
|
|
2593
|
+
return True
|
|
2594
|
+
|
|
2595
|
+
if yaml_path.exists():
|
|
2596
|
+
# YAML already exists, just remove JSON
|
|
2597
|
+
try:
|
|
2598
|
+
json_path.unlink()
|
|
2599
|
+
console.print(" [cyan]๐ Removed legacy config.json (YAML version exists)[/cyan]")
|
|
2600
|
+
logger.info(f"Removed legacy config.json: {json_path}")
|
|
2601
|
+
return True
|
|
2602
|
+
except Exception as e:
|
|
2603
|
+
console.print(f" [yellow]โ ๏ธ Failed to remove legacy config.json: {e}[/yellow]")
|
|
2604
|
+
logger.warning(f"Failed to remove {json_path}: {e}")
|
|
2605
|
+
return True # Not critical
|
|
2606
|
+
|
|
2607
|
+
# Perform migration
|
|
2608
|
+
try:
|
|
2609
|
+
# Read JSON config
|
|
2610
|
+
with open(json_path, "r", encoding="utf-8") as f:
|
|
2611
|
+
config_data = json.load(f)
|
|
2612
|
+
|
|
2613
|
+
# Write YAML config
|
|
2614
|
+
with open(yaml_path, "w", encoding="utf-8") as f:
|
|
2615
|
+
yaml.safe_dump(
|
|
2616
|
+
config_data,
|
|
2617
|
+
f,
|
|
2618
|
+
default_flow_style=False,
|
|
2619
|
+
allow_unicode=True,
|
|
2620
|
+
sort_keys=False,
|
|
2621
|
+
)
|
|
2622
|
+
|
|
2623
|
+
# Remove old JSON file
|
|
2624
|
+
json_path.unlink()
|
|
2625
|
+
|
|
2626
|
+
console.print(" [green]โ Migrated config.json โ config.yaml[/green]")
|
|
2627
|
+
logger.info(f"Migrated config from JSON to YAML: {json_path} โ {yaml_path}")
|
|
2628
|
+
|
|
2629
|
+
# Migrate preset files if they exist
|
|
2630
|
+
_migrate_preset_files_to_yaml(config_dir)
|
|
2631
|
+
|
|
2632
|
+
return True
|
|
2633
|
+
|
|
2634
|
+
except Exception as e:
|
|
2635
|
+
console.print(f" [red]โ Config migration failed: {e}[/red]")
|
|
2636
|
+
logger.error(f"Failed to migrate config.json to YAML: {e}")
|
|
2637
|
+
return False
|
|
2638
|
+
|
|
2639
|
+
|
|
2640
|
+
def _migrate_preset_files_to_yaml(config_dir: Path) -> None:
|
|
2641
|
+
"""Migrate preset files from JSON to YAML format.
|
|
2642
|
+
|
|
2643
|
+
Args:
|
|
2644
|
+
config_dir: .moai/config directory path
|
|
2645
|
+
"""
|
|
2646
|
+
try:
|
|
2647
|
+
import yaml
|
|
2648
|
+
except ImportError:
|
|
2649
|
+
return
|
|
2650
|
+
|
|
2651
|
+
presets_dir = config_dir / "presets"
|
|
2652
|
+
if not presets_dir.exists():
|
|
2653
|
+
return
|
|
2654
|
+
|
|
2655
|
+
migrated_count = 0
|
|
2656
|
+
for json_file in presets_dir.glob("*.json"):
|
|
2657
|
+
yaml_file = json_file.with_suffix(".yaml")
|
|
2658
|
+
|
|
2659
|
+
# Skip if YAML already exists
|
|
2660
|
+
if yaml_file.exists():
|
|
2661
|
+
# Just remove the JSON file
|
|
2662
|
+
try:
|
|
2663
|
+
json_file.unlink()
|
|
2664
|
+
migrated_count += 1
|
|
2665
|
+
except Exception as e:
|
|
2666
|
+
logger.warning(f"Failed to remove {json_file}: {e}")
|
|
2667
|
+
continue
|
|
2668
|
+
|
|
2669
|
+
# Migrate JSON โ YAML
|
|
2670
|
+
try:
|
|
2671
|
+
with open(json_file, "r", encoding="utf-8") as f:
|
|
2672
|
+
preset_data = json.load(f)
|
|
2673
|
+
|
|
2674
|
+
with open(yaml_file, "w", encoding="utf-8") as f:
|
|
2675
|
+
yaml.safe_dump(
|
|
2676
|
+
preset_data,
|
|
2677
|
+
f,
|
|
2678
|
+
default_flow_style=False,
|
|
2679
|
+
allow_unicode=True,
|
|
2680
|
+
sort_keys=False,
|
|
2681
|
+
)
|
|
2682
|
+
|
|
2683
|
+
json_file.unlink()
|
|
2684
|
+
migrated_count += 1
|
|
2685
|
+
|
|
2686
|
+
except Exception as e:
|
|
2687
|
+
logger.warning(f"Failed to migrate preset {json_file}: {e}")
|
|
2688
|
+
|
|
2689
|
+
if migrated_count > 0:
|
|
2690
|
+
console.print(f" [cyan]๐ Migrated {migrated_count} preset file(s) to YAML[/cyan]")
|
|
2691
|
+
logger.info(f"Migrated {migrated_count} preset files to YAML")
|