moai-adk 0.15.1__py3-none-any.whl โ 0.32.8__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 -6
- moai_adk/__main__.py +136 -21
- moai_adk/cli/__init__.py +6 -2
- moai_adk/cli/commands/__init__.py +1 -4
- moai_adk/cli/commands/analyze.py +116 -0
- moai_adk/cli/commands/doctor.py +17 -5
- moai_adk/cli/commands/init.py +105 -47
- moai_adk/cli/commands/language.py +248 -0
- moai_adk/cli/commands/status.py +8 -13
- moai_adk/cli/commands/update.py +1734 -65
- moai_adk/cli/main.py +3 -2
- moai_adk/cli/prompts/init_prompts.py +144 -91
- 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 +672 -0
- moai_adk/cli/worktree/exceptions.py +89 -0
- moai_adk/cli/worktree/manager.py +490 -0
- moai_adk/cli/worktree/models.py +65 -0
- moai_adk/cli/worktree/registry.py +128 -0
- moai_adk/core/PHASE2_OPTIMIZATIONS.md +467 -0
- moai_adk/core/__init__.py +0 -1
- moai_adk/core/analysis/__init__.py +9 -0
- moai_adk/core/analysis/session_analyzer.py +400 -0
- moai_adk/core/claude_integration.py +393 -0
- moai_adk/core/command_helpers.py +270 -0
- moai_adk/core/comprehensive_monitoring_system.py +1183 -0
- moai_adk/core/config/__init__.py +6 -0
- moai_adk/core/config/auto_spec_config.py +340 -0
- moai_adk/core/config/migration.py +147 -16
- moai_adk/core/config/unified.py +436 -0
- moai_adk/core/context_manager.py +273 -0
- moai_adk/core/diagnostics/slash_commands.py +0 -1
- moai_adk/core/enterprise_features.py +1404 -0
- moai_adk/core/error_recovery_system.py +1902 -0
- moai_adk/core/event_driven_hook_system.py +1371 -0
- moai_adk/core/git/__init__.py +8 -1
- moai_adk/core/git/branch.py +0 -1
- moai_adk/core/git/branch_manager.py +2 -10
- moai_adk/core/git/checkpoint.py +1 -7
- moai_adk/core/git/commit.py +0 -1
- moai_adk/core/git/conflict_detector.py +413 -0
- moai_adk/core/git/event_detector.py +3 -5
- moai_adk/core/git/manager.py +91 -2
- moai_adk/core/hooks/post_tool_auto_spec_completion.py +901 -0
- moai_adk/core/input_validation_middleware.py +1006 -0
- moai_adk/core/integration/__init__.py +22 -0
- moai_adk/core/integration/engine.py +157 -0
- moai_adk/core/integration/integration_tester.py +226 -0
- moai_adk/core/integration/models.py +88 -0
- moai_adk/core/integration/utils.py +211 -0
- moai_adk/core/issue_creator.py +20 -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.py +202 -0
- moai_adk/core/language_config_resolver.py +485 -0
- moai_adk/core/language_validator.py +543 -0
- moai_adk/core/mcp/setup.py +116 -0
- moai_adk/core/merge/__init__.py +9 -0
- moai_adk/core/merge/analyzer.py +481 -0
- moai_adk/core/migration/__init__.py +18 -0
- moai_adk/core/migration/alfred_to_moai_migrator.py +383 -0
- moai_adk/core/migration/backup_manager.py +277 -0
- moai_adk/core/migration/custom_element_scanner.py +358 -0
- moai_adk/core/migration/file_migrator.py +209 -0
- moai_adk/core/migration/interactive_checkbox_ui.py +488 -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 +139 -0
- moai_adk/core/migration/version_migrator.py +228 -0
- moai_adk/core/performance/__init__.py +6 -0
- moai_adk/core/performance/cache_system.py +316 -0
- moai_adk/core/performance/parallel_processor.py +116 -0
- moai_adk/core/phase_optimized_hook_scheduler.py +879 -0
- moai_adk/core/project/__init__.py +0 -1
- moai_adk/core/project/backup_utils.py +2 -7
- moai_adk/core/project/checker.py +2 -4
- moai_adk/core/project/detector.py +17 -39
- moai_adk/core/project/initializer.py +170 -33
- moai_adk/core/project/phase_executor.py +398 -68
- moai_adk/core/project/validator.py +7 -32
- moai_adk/core/quality/__init__.py +1 -1
- moai_adk/core/quality/trust_checker.py +37 -101
- moai_adk/core/quality/validators/__init__.py +1 -1
- moai_adk/core/quality/validators/base_validator.py +1 -1
- moai_adk/core/realtime_monitoring_dashboard.py +1724 -0
- moai_adk/core/robust_json_parser.py +611 -0
- moai_adk/core/rollback_manager.py +918 -0
- moai_adk/core/session_manager.py +651 -0
- moai_adk/core/skill_loading_system.py +579 -0
- moai_adk/core/spec/confidence_scoring.py +680 -0
- moai_adk/core/spec/ears_template_engine.py +1247 -0
- moai_adk/core/spec/quality_validator.py +687 -0
- moai_adk/core/spec_status_manager.py +478 -0
- moai_adk/core/template/__init__.py +0 -1
- moai_adk/core/template/backup.py +82 -17
- moai_adk/core/template/config.py +112 -40
- moai_adk/core/template/languages.py +0 -1
- moai_adk/core/template/merger.py +75 -26
- moai_adk/core/template/processor.py +750 -72
- moai_adk/core/template_engine.py +90 -48
- moai_adk/core/template_variable_synchronizer.py +417 -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 +429 -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 +557 -0
- 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 +676 -0
- moai_adk/foundation/trust/validation_checklist.py +1573 -0
- 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/__init__.py +38 -0
- moai_adk/statusline/alfred_detector.py +105 -0
- moai_adk/statusline/config.py +373 -0
- moai_adk/statusline/enhanced_output_style_detector.py +372 -0
- moai_adk/statusline/git_collector.py +190 -0
- moai_adk/statusline/main.py +264 -0
- moai_adk/statusline/metrics_tracker.py +78 -0
- moai_adk/statusline/renderer.py +383 -0
- moai_adk/statusline/update_checker.py +129 -0
- moai_adk/statusline/version_reader.py +741 -0
- moai_adk/templates/.claude/agents/moai/ai-nano-banana.md +549 -0
- moai_adk/templates/.claude/agents/moai/builder-agent.md +445 -0
- moai_adk/templates/.claude/agents/moai/builder-command.md +1132 -0
- moai_adk/templates/.claude/agents/moai/builder-skill.md +601 -0
- moai_adk/templates/.claude/agents/moai/expert-backend.md +831 -0
- moai_adk/templates/.claude/agents/moai/expert-database.md +774 -0
- moai_adk/templates/.claude/agents/moai/expert-debug.md +396 -0
- moai_adk/templates/.claude/agents/moai/expert-devops.md +711 -0
- moai_adk/templates/.claude/agents/moai/expert-frontend.md +666 -0
- moai_adk/templates/.claude/agents/moai/expert-security.md +474 -0
- moai_adk/templates/.claude/agents/moai/expert-uiux.md +1038 -0
- moai_adk/templates/.claude/agents/moai/manager-claude-code.md +429 -0
- moai_adk/templates/.claude/agents/moai/manager-docs.md +570 -0
- moai_adk/templates/.claude/agents/moai/manager-git.md +937 -0
- moai_adk/templates/.claude/agents/moai/manager-project.md +891 -0
- moai_adk/templates/.claude/agents/moai/manager-quality.md +598 -0
- moai_adk/templates/.claude/agents/moai/manager-spec.md +713 -0
- moai_adk/templates/.claude/agents/moai/manager-strategy.md +600 -0
- moai_adk/templates/.claude/agents/moai/manager-tdd.md +603 -0
- moai_adk/templates/.claude/agents/moai/mcp-context7.md +369 -0
- moai_adk/templates/.claude/agents/moai/mcp-figma.md +1567 -0
- moai_adk/templates/.claude/agents/moai/mcp-notion.md +749 -0
- moai_adk/templates/.claude/agents/moai/mcp-playwright.md +427 -0
- moai_adk/templates/.claude/agents/moai/mcp-sequential-thinking.md +994 -0
- moai_adk/templates/.claude/commands/moai/0-project.md +1143 -0
- moai_adk/templates/.claude/commands/moai/1-plan.md +1435 -0
- moai_adk/templates/.claude/commands/moai/2-run.md +883 -0
- moai_adk/templates/.claude/commands/moai/3-sync.md +993 -0
- moai_adk/templates/.claude/commands/moai/9-feedback.md +314 -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/__init__.py +85 -0
- moai_adk/templates/.claude/hooks/{alfred/shared/core โ moai/lib}/checkpoint.py +9 -36
- moai_adk/templates/.claude/hooks/moai/lib/common.py +131 -0
- moai_adk/templates/.claude/hooks/moai/lib/config_manager.py +446 -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/git_operations_manager.py +590 -0
- moai_adk/templates/.claude/hooks/moai/lib/language_validator.py +317 -0
- moai_adk/templates/.claude/hooks/moai/lib/models.py +102 -0
- moai_adk/templates/.claude/hooks/moai/lib/path_utils.py +28 -0
- moai_adk/templates/.claude/hooks/{alfred/shared/core โ moai/lib}/project.py +63 -44
- moai_adk/templates/.claude/hooks/moai/lib/test_hooks_improvements.py +443 -0
- moai_adk/templates/.claude/hooks/{alfred/core โ moai/lib}/timeout.py +40 -16
- moai_adk/templates/.claude/hooks/moai/lib/unified_timeout_manager.py +530 -0
- moai_adk/templates/.claude/hooks/moai/session_end__auto_cleanup.py +862 -0
- moai_adk/templates/.claude/hooks/moai/session_start__show_project_info.py +921 -0
- moai_adk/templates/.claude/output-styles/moai/r2d2.md +380 -0
- moai_adk/templates/.claude/output-styles/moai/yoda.md +338 -0
- moai_adk/templates/.claude/settings.json +78 -50
- moai_adk/templates/.claude/skills/moai-docs-generation/SKILL.md +247 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/README.md +44 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/api-documentation.md +130 -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 +178 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/user-guides.md +147 -0
- moai_adk/templates/.claude/skills/moai-domain-backend/SKILL.md +312 -283
- moai_adk/templates/.claude/skills/moai-domain-database/SKILL.md +291 -94
- 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-frontend/SKILL.md +469 -101
- moai_adk/templates/.claude/skills/moai-domain-uiux/SKILL.md +453 -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/design-system-tokens.md +405 -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 +491 -0
- moai_adk/templates/.claude/skills/moai-formats-data/modules/README.md +98 -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-foundation-claude/SKILL.md +201 -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-custom-slash-commands-official.md +729 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-hooks-official.md +560 -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-settings-official.md +663 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-skills-official.md +113 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-sub-agents-official.md +238 -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-context/SKILL.md +438 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/SKILL.md +515 -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 +346 -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-patterns.md +757 -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/progressive-disclosure.md +649 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/spec-first-tdd.md +864 -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 +981 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/SKILL.md +362 -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-integration-mcp/SKILL.md +352 -0
- moai_adk/templates/.claude/skills/moai-integration-mcp/modules/README.md +52 -0
- moai_adk/templates/.claude/skills/moai-integration-mcp/modules/error-handling.md +334 -0
- moai_adk/templates/.claude/skills/moai-integration-mcp/modules/integration-patterns.md +310 -0
- moai_adk/templates/.claude/skills/moai-integration-mcp/modules/security-authentication.md +256 -0
- moai_adk/templates/.claude/skills/moai-integration-mcp/modules/server-architecture.md +253 -0
- moai_adk/templates/.claude/skills/moai-lang-unified/README.md +133 -0
- moai_adk/templates/.claude/skills/moai-lang-unified/SKILL.md +296 -0
- moai_adk/templates/.claude/skills/moai-lang-unified/examples.md +1269 -0
- moai_adk/templates/.claude/skills/moai-lang-unified/reference.md +331 -0
- moai_adk/templates/.claude/skills/moai-library-mermaid/SKILL.md +298 -0
- moai_adk/templates/.claude/skills/moai-library-mermaid/advanced-patterns.md +465 -0
- moai_adk/templates/.claude/skills/moai-library-mermaid/examples.md +270 -0
- moai_adk/templates/.claude/skills/moai-library-mermaid/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 +316 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/advanced-patterns.md +336 -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 +17 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/configuration.md +57 -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/optimization.md +303 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/SKILL.md +370 -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-baas/README.md +186 -0
- moai_adk/templates/.claude/skills/moai-platform-baas/SKILL.md +290 -0
- moai_adk/templates/.claude/skills/moai-platform-baas/examples.md +1225 -0
- moai_adk/templates/.claude/skills/moai-platform-baas/reference.md +567 -0
- moai_adk/templates/.claude/skills/moai-platform-baas/scripts/provider-selector.py +323 -0
- moai_adk/templates/.claude/skills/moai-platform-baas/templates/stack-config.yaml +204 -0
- moai_adk/templates/.claude/skills/moai-workflow-jit-docs/SKILL.md +446 -0
- moai_adk/templates/.claude/skills/moai-workflow-jit-docs/advanced-patterns.md +379 -0
- moai_adk/templates/.claude/skills/moai-workflow-jit-docs/optimization.md +286 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/README.md +190 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/SKILL.md +387 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/__init__.py +520 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/complete_workflow_demo_fixed.py +574 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/examples/complete_project_setup.py +317 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/examples/complete_workflow_demo.py +663 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/examples/config-migration-example.json +190 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/examples/question-examples.json +135 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/examples/quick_start.py +196 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/__init__.py +17 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/advanced-patterns.md +158 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/ask_user_integration.py +340 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/batch_questions.py +713 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/config_manager.py +538 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/documentation_manager.py +1336 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/language_initializer.py +730 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/migration_manager.py +608 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/template_optimizer.py +1005 -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 +1362 -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 +71 -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-project/test_integration_simple.py +436 -0
- moai_adk/templates/.claude/skills/moai-workflow-templates/SKILL.md +374 -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-testing/LICENSE.txt +202 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/SKILL.md +453 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/advanced-patterns.md +576 -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/modules/README.md +220 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/ai-debugging.md +845 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review.md +1416 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization.md +1234 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/smart-refactoring.md +1243 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd-context7.md +1260 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/optimization.md +505 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/reference/playwright-best-practices.md +57 -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-worktree/SKILL.md +410 -0
- moai_adk/templates/.claude/skills/moai-worktree/examples.md +606 -0
- moai_adk/templates/.claude/skills/moai-worktree/modules/integration-patterns.md +982 -0
- moai_adk/templates/.claude/skills/moai-worktree/modules/parallel-development.md +778 -0
- moai_adk/templates/.claude/skills/moai-worktree/modules/worktree-commands.md +646 -0
- moai_adk/templates/.claude/skills/moai-worktree/modules/worktree-management.md +782 -0
- moai_adk/templates/.claude/skills/moai-worktree/reference.md +357 -0
- moai_adk/templates/.git-hooks/pre-commit +128 -0
- moai_adk/templates/.git-hooks/pre-push +220 -13
- moai_adk/templates/.github/workflows/ci-universal.yml +513 -0
- moai_adk/templates/.github/workflows/security-secrets-check.yml +179 -0
- moai_adk/templates/.github/workflows/spec-issue-sync.yml +0 -1
- moai_adk/templates/.gitignore +197 -13
- moai_adk/templates/.mcp.json +20 -0
- moai_adk/templates/.moai/cache/personalization.json +10 -0
- moai_adk/templates/.moai/config/config.yaml +344 -0
- moai_adk/templates/.moai/config/presets/manual.yaml +28 -0
- moai_adk/templates/.moai/config/presets/personal.yaml +30 -0
- moai_adk/templates/.moai/config/presets/team.yaml +33 -0
- moai_adk/templates/.moai/config/questions/_schema.yaml +79 -0
- moai_adk/templates/.moai/config/questions/tab1-user.yaml +108 -0
- moai_adk/templates/.moai/config/questions/tab2-project.yaml +122 -0
- moai_adk/templates/.moai/config/questions/tab3-git.yaml +542 -0
- moai_adk/templates/.moai/config/questions/tab4-quality.yaml +167 -0
- moai_adk/templates/.moai/config/questions/tab5-system.yaml +152 -0
- moai_adk/templates/.moai/config/sections/git-strategy.yaml +40 -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 +15 -0
- moai_adk/templates/.moai/config/sections/system.yaml +14 -0
- moai_adk/templates/.moai/config/sections/user.yaml +5 -0
- moai_adk/templates/.moai/config/statusline-config.yaml +86 -0
- moai_adk/templates/.moai/scripts/setup-glm.py +136 -0
- moai_adk/templates/CLAUDE.md +310 -1050
- moai_adk/utils/__init__.py +24 -2
- moai_adk/utils/banner.py +7 -11
- moai_adk/utils/common.py +294 -0
- moai_adk/utils/link_validator.py +241 -0
- moai_adk/utils/logger.py +4 -9
- moai_adk/utils/safe_file_reader.py +206 -0
- moai_adk/{templates/.claude/hooks/alfred/utils โ utils}/timeout.py +8 -9
- moai_adk/utils/toon_utils.py +256 -0
- moai_adk/version.py +22 -0
- moai_adk-0.32.8.dist-info/METADATA +2478 -0
- moai_adk-0.32.8.dist-info/RECORD +396 -0
- {moai_adk-0.15.1.dist-info โ moai_adk-0.32.8.dist-info}/WHEEL +1 -1
- {moai_adk-0.15.1.dist-info โ moai_adk-0.32.8.dist-info}/entry_points.txt +1 -0
- moai_adk/cli/commands/backup.py +0 -80
- moai_adk/core/tags/__init__.py +0 -86
- moai_adk/core/tags/ci_validator.py +0 -463
- moai_adk/core/tags/cli.py +0 -283
- moai_adk/core/tags/generator.py +0 -109
- moai_adk/core/tags/inserter.py +0 -99
- moai_adk/core/tags/mapper.py +0 -126
- moai_adk/core/tags/parser.py +0 -76
- moai_adk/core/tags/pre_commit_validator.py +0 -393
- moai_adk/core/tags/reporter.py +0 -956
- moai_adk/core/tags/tags.py +0 -149
- moai_adk/core/tags/validator.py +0 -897
- moai_adk/templates/.claude/agents/alfred/backend-expert.md +0 -319
- moai_adk/templates/.claude/agents/alfred/cc-manager.md +0 -316
- moai_adk/templates/.claude/agents/alfred/debug-helper.md +0 -208
- moai_adk/templates/.claude/agents/alfred/devops-expert.md +0 -464
- moai_adk/templates/.claude/agents/alfred/doc-syncer.md +0 -214
- moai_adk/templates/.claude/agents/alfred/frontend-expert.md +0 -357
- moai_adk/templates/.claude/agents/alfred/git-manager.md +0 -406
- moai_adk/templates/.claude/agents/alfred/implementation-planner.md +0 -423
- moai_adk/templates/.claude/agents/alfred/project-manager.md +0 -312
- moai_adk/templates/.claude/agents/alfred/quality-gate.md +0 -343
- moai_adk/templates/.claude/agents/alfred/skill-factory.md +0 -865
- moai_adk/templates/.claude/agents/alfred/spec-builder.md +0 -426
- moai_adk/templates/.claude/agents/alfred/tag-agent.md +0 -361
- moai_adk/templates/.claude/agents/alfred/tdd-implementer.md +0 -428
- moai_adk/templates/.claude/agents/alfred/trust-checker.md +0 -375
- moai_adk/templates/.claude/agents/alfred/ui-ux-expert.md +0 -571
- moai_adk/templates/.claude/commands/alfred/0-project.md +0 -1854
- moai_adk/templates/.claude/commands/alfred/1-plan.md +0 -880
- moai_adk/templates/.claude/commands/alfred/2-run.md +0 -793
- moai_adk/templates/.claude/commands/alfred/3-sync.md +0 -1084
- moai_adk/templates/.claude/commands/alfred/9-feedback.md +0 -149
- moai_adk/templates/.claude/hooks/alfred/core/project.py +0 -748
- moai_adk/templates/.claude/hooks/alfred/core/ttl_cache.py +0 -108
- moai_adk/templates/.claude/hooks/alfred/core/version_cache.py +0 -198
- moai_adk/templates/.claude/hooks/alfred/handlers/__init__.py +0 -29
- moai_adk/templates/.claude/hooks/alfred/post_tool__log_changes.py +0 -94
- moai_adk/templates/.claude/hooks/alfred/pre_tool__auto_checkpoint.py +0 -100
- moai_adk/templates/.claude/hooks/alfred/session_end__cleanup.py +0 -94
- moai_adk/templates/.claude/hooks/alfred/session_start__show_project_info.py +0 -94
- moai_adk/templates/.claude/hooks/alfred/shared/core/__init__.py +0 -170
- moai_adk/templates/.claude/hooks/alfred/shared/core/context.py +0 -67
- moai_adk/templates/.claude/hooks/alfred/shared/core/tags.py +0 -230
- moai_adk/templates/.claude/hooks/alfred/shared/core/version_cache.py +0 -198
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/__init__.py +0 -21
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/notification.py +0 -154
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/session.py +0 -174
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/tool.py +0 -87
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/user.py +0 -61
- moai_adk/templates/.claude/hooks/alfred/user_prompt__jit_load_docs.py +0 -112
- moai_adk/templates/.claude/hooks/alfred/utils/__init__.py +0 -1
- moai_adk/templates/.claude/skills/moai-alfred-agent-guide/SKILL.md +0 -70
- moai_adk/templates/.claude/skills/moai-alfred-agent-guide/examples.md +0 -62
- moai_adk/templates/.claude/skills/moai-alfred-agent-guide/reference.md +0 -242
- moai_adk/templates/.claude/skills/moai-alfred-config-schema/SKILL.md +0 -56
- moai_adk/templates/.claude/skills/moai-alfred-config-schema/examples.md +0 -28
- moai_adk/templates/.claude/skills/moai-alfred-config-schema/reference.md +0 -444
- moai_adk/templates/.claude/skills/moai-alfred-context-budget/SKILL.md +0 -62
- moai_adk/templates/.claude/skills/moai-alfred-context-budget/examples.md +0 -28
- moai_adk/templates/.claude/skills/moai-alfred-context-budget/reference.md +0 -405
- moai_adk/templates/.claude/skills/moai-alfred-dev-guide/SKILL.md +0 -51
- moai_adk/templates/.claude/skills/moai-alfred-dev-guide/examples.md +0 -355
- moai_adk/templates/.claude/skills/moai-alfred-dev-guide/reference.md +0 -239
- moai_adk/templates/.claude/skills/moai-alfred-ears-authoring/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-alfred-ears-authoring/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-alfred-ears-authoring/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/SKILL.md +0 -323
- moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/examples.md +0 -286
- moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/reference.md +0 -126
- moai_adk/templates/.claude/skills/moai-alfred-git-workflow/SKILL.md +0 -122
- moai_adk/templates/.claude/skills/moai-alfred-git-workflow/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-alfred-git-workflow/reference.md +0 -29
- moai_adk/templates/.claude/skills/moai-alfred-gitflow-policy/SKILL.md +0 -74
- moai_adk/templates/.claude/skills/moai-alfred-gitflow-policy/examples.md +0 -4
- moai_adk/templates/.claude/skills/moai-alfred-gitflow-policy/reference.md +0 -269
- moai_adk/templates/.claude/skills/moai-alfred-interactive-questions/SKILL.md +0 -237
- moai_adk/templates/.claude/skills/moai-alfred-interactive-questions/examples.md +0 -615
- moai_adk/templates/.claude/skills/moai-alfred-interactive-questions/reference.md +0 -653
- moai_adk/templates/.claude/skills/moai-alfred-issue-labels/SKILL.md +0 -19
- moai_adk/templates/.claude/skills/moai-alfred-issue-labels/examples.md +0 -4
- moai_adk/templates/.claude/skills/moai-alfred-issue-labels/reference.md +0 -150
- moai_adk/templates/.claude/skills/moai-alfred-language-detection/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-alfred-language-detection/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-alfred-language-detection/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-alfred-persona-roles/SKILL.md +0 -198
- moai_adk/templates/.claude/skills/moai-alfred-persona-roles/examples.md +0 -431
- moai_adk/templates/.claude/skills/moai-alfred-persona-roles/reference.md +0 -141
- moai_adk/templates/.claude/skills/moai-alfred-practices/SKILL.md +0 -89
- moai_adk/templates/.claude/skills/moai-alfred-practices/examples.md +0 -122
- moai_adk/templates/.claude/skills/moai-alfred-practices/reference.md +0 -369
- moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/SKILL.md +0 -508
- moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/examples.md +0 -481
- moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/reference.md +0 -100
- moai_adk/templates/.claude/skills/moai-alfred-reporting/SKILL.md +0 -273
- moai_adk/templates/.claude/skills/moai-alfred-rules/SKILL.md +0 -77
- moai_adk/templates/.claude/skills/moai-alfred-rules/examples.md +0 -265
- moai_adk/templates/.claude/skills/moai-alfred-rules/reference.md +0 -539
- moai_adk/templates/.claude/skills/moai-alfred-session-state/SKILL.md +0 -19
- moai_adk/templates/.claude/skills/moai-alfred-session-state/examples.md +0 -4
- moai_adk/templates/.claude/skills/moai-alfred-session-state/reference.md +0 -84
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/README.md +0 -137
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/SKILL.md +0 -219
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/examples/validate-spec.sh +0 -161
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/examples.md +0 -541
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/reference.md +0 -622
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-extended/SKILL.md +0 -115
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-extended/examples.md +0 -4
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-extended/reference.md +0 -348
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-validation/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-validation/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-validation/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-alfred-tag-scanning/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-alfred-tag-scanning/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-alfred-tag-scanning/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/SKILL.md +0 -19
- moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/examples.md +0 -4
- moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/reference.md +0 -211
- moai_adk/templates/.claude/skills/moai-alfred-trust-validation/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-alfred-trust-validation/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-alfred-trust-validation/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-alfred-workflow/SKILL.md +0 -288
- moai_adk/templates/.claude/skills/moai-cc-agents/SKILL.md +0 -269
- moai_adk/templates/.claude/skills/moai-cc-agents/templates/agent-template.md +0 -32
- moai_adk/templates/.claude/skills/moai-cc-claude-md/SKILL.md +0 -298
- moai_adk/templates/.claude/skills/moai-cc-claude-md/templates/CLAUDE-template.md +0 -26
- moai_adk/templates/.claude/skills/moai-cc-commands/SKILL.md +0 -307
- moai_adk/templates/.claude/skills/moai-cc-commands/templates/command-template.md +0 -21
- moai_adk/templates/.claude/skills/moai-cc-hooks/SKILL.md +0 -252
- moai_adk/templates/.claude/skills/moai-cc-hooks/scripts/pre-bash-check.sh +0 -19
- moai_adk/templates/.claude/skills/moai-cc-hooks/scripts/preserve-permissions.sh +0 -19
- moai_adk/templates/.claude/skills/moai-cc-hooks/scripts/validate-bash-command.py +0 -24
- moai_adk/templates/.claude/skills/moai-cc-mcp-plugins/SKILL.md +0 -199
- moai_adk/templates/.claude/skills/moai-cc-mcp-plugins/templates/settings-mcp-template.json +0 -39
- moai_adk/templates/.claude/skills/moai-cc-memory/SKILL.md +0 -316
- moai_adk/templates/.claude/skills/moai-cc-memory/templates/session-summary-template.md +0 -18
- moai_adk/templates/.claude/skills/moai-cc-settings/SKILL.md +0 -263
- moai_adk/templates/.claude/skills/moai-cc-settings/templates/settings-complete-template.json +0 -30
- moai_adk/templates/.claude/skills/moai-cc-skill-descriptions/SKILL.md +0 -19
- moai_adk/templates/.claude/skills/moai-cc-skill-descriptions/examples.md +0 -4
- moai_adk/templates/.claude/skills/moai-cc-skill-descriptions/reference.md +0 -218
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/CHECKLIST.md +0 -482
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/EXAMPLES.md +0 -278
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/INTERACTIVE-DISCOVERY.md +0 -524
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/METADATA.md +0 -477
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/PARALLEL-ANALYSIS-REPORT.md +0 -429
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/PYTHON-VERSION-MATRIX.md +0 -391
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/SKILL-FACTORY-WORKFLOW.md +0 -431
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/SKILL-UPDATE-ADVISOR.md +0 -577
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/SKILL.md +0 -271
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/STEP-BY-STEP-GUIDE.md +0 -466
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/STRUCTURE.md +0 -583
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/WEB-RESEARCH.md +0 -526
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/reference.md +0 -465
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/scripts/generate-structure.sh +0 -328
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/scripts/validate-skill.sh +0 -312
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/templates/SKILL_TEMPLATE.md +0 -245
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/templates/examples-template.md +0 -285
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/templates/reference-template.md +0 -278
- moai_adk/templates/.claude/skills/moai-cc-skill-factory/templates/scripts-template.sh +0 -303
- moai_adk/templates/.claude/skills/moai-cc-skills/SKILL.md +0 -291
- moai_adk/templates/.claude/skills/moai-cc-skills/templates/SKILL-template.md +0 -15
- moai_adk/templates/.claude/skills/moai-design-systems/SKILL.md +0 -802
- moai_adk/templates/.claude/skills/moai-design-systems/examples.md +0 -1238
- moai_adk/templates/.claude/skills/moai-design-systems/reference.md +0 -673
- moai_adk/templates/.claude/skills/moai-domain-backend/examples.md +0 -1633
- moai_adk/templates/.claude/skills/moai-domain-backend/reference.md +0 -660
- moai_adk/templates/.claude/skills/moai-domain-cli-tool/SKILL.md +0 -123
- moai_adk/templates/.claude/skills/moai-domain-cli-tool/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-domain-cli-tool/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-domain-data-science/SKILL.md +0 -123
- moai_adk/templates/.claude/skills/moai-domain-data-science/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-domain-data-science/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-domain-database/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-domain-database/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-domain-devops/SKILL.md +0 -124
- moai_adk/templates/.claude/skills/moai-domain-devops/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-domain-devops/reference.md +0 -31
- moai_adk/templates/.claude/skills/moai-domain-frontend/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-domain-frontend/reference.md +0 -31
- moai_adk/templates/.claude/skills/moai-domain-ml/SKILL.md +0 -123
- moai_adk/templates/.claude/skills/moai-domain-ml/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-domain-ml/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-domain-mobile-app/SKILL.md +0 -123
- moai_adk/templates/.claude/skills/moai-domain-mobile-app/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-domain-mobile-app/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-domain-security/SKILL.md +0 -123
- moai_adk/templates/.claude/skills/moai-domain-security/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-domain-security/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-domain-web-api/SKILL.md +0 -123
- moai_adk/templates/.claude/skills/moai-domain-web-api/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-domain-web-api/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-essentials-debug/SKILL.md +0 -303
- moai_adk/templates/.claude/skills/moai-essentials-debug/examples.md +0 -1064
- moai_adk/templates/.claude/skills/moai-essentials-debug/reference.md +0 -1047
- moai_adk/templates/.claude/skills/moai-essentials-perf/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-essentials-perf/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-essentials-perf/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-essentials-refactor/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-essentials-refactor/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-essentials-refactor/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-essentials-review/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-essentials-review/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-essentials-review/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-foundation-ears/SKILL.md +0 -116
- moai_adk/templates/.claude/skills/moai-foundation-ears/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-foundation-ears/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-foundation-git/SKILL.md +0 -122
- moai_adk/templates/.claude/skills/moai-foundation-git/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-foundation-git/reference.md +0 -29
- moai_adk/templates/.claude/skills/moai-foundation-langs/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-foundation-langs/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-foundation-langs/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-foundation-specs/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-foundation-specs/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-foundation-specs/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-foundation-tags/SKILL.md +0 -113
- moai_adk/templates/.claude/skills/moai-foundation-tags/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-foundation-tags/reference.md +0 -28
- moai_adk/templates/.claude/skills/moai-foundation-trust/SKILL.md +0 -307
- moai_adk/templates/.claude/skills/moai-foundation-trust/examples.md +0 -0
- moai_adk/templates/.claude/skills/moai-foundation-trust/reference.md +0 -1099
- moai_adk/templates/.claude/skills/moai-lang-c/SKILL.md +0 -124
- moai_adk/templates/.claude/skills/moai-lang-c/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-c/reference.md +0 -31
- moai_adk/templates/.claude/skills/moai-lang-cpp/SKILL.md +0 -124
- moai_adk/templates/.claude/skills/moai-lang-cpp/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-cpp/reference.md +0 -31
- moai_adk/templates/.claude/skills/moai-lang-csharp/SKILL.md +0 -123
- moai_adk/templates/.claude/skills/moai-lang-csharp/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-csharp/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-lang-dart/SKILL.md +0 -123
- moai_adk/templates/.claude/skills/moai-lang-dart/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-dart/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-lang-go/SKILL.md +0 -127
- moai_adk/templates/.claude/skills/moai-lang-go/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-go/reference.md +0 -31
- moai_adk/templates/.claude/skills/moai-lang-java/SKILL.md +0 -126
- moai_adk/templates/.claude/skills/moai-lang-java/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-java/reference.md +0 -31
- moai_adk/templates/.claude/skills/moai-lang-javascript/SKILL.md +0 -125
- moai_adk/templates/.claude/skills/moai-lang-javascript/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-javascript/reference.md +0 -32
- moai_adk/templates/.claude/skills/moai-lang-kotlin/SKILL.md +0 -124
- moai_adk/templates/.claude/skills/moai-lang-kotlin/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-kotlin/reference.md +0 -31
- moai_adk/templates/.claude/skills/moai-lang-php/SKILL.md +0 -126
- moai_adk/templates/.claude/skills/moai-lang-php/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-php/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-lang-python/SKILL.md +0 -433
- moai_adk/templates/.claude/skills/moai-lang-python/examples.md +0 -624
- moai_adk/templates/.claude/skills/moai-lang-python/reference.md +0 -316
- moai_adk/templates/.claude/skills/moai-lang-r/SKILL.md +0 -123
- moai_adk/templates/.claude/skills/moai-lang-r/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-r/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-lang-ruby/SKILL.md +0 -124
- moai_adk/templates/.claude/skills/moai-lang-ruby/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-ruby/reference.md +0 -31
- moai_adk/templates/.claude/skills/moai-lang-rust/SKILL.md +0 -127
- moai_adk/templates/.claude/skills/moai-lang-rust/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-rust/reference.md +0 -31
- moai_adk/templates/.claude/skills/moai-lang-scala/SKILL.md +0 -125
- moai_adk/templates/.claude/skills/moai-lang-scala/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-scala/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-lang-shell/SKILL.md +0 -123
- moai_adk/templates/.claude/skills/moai-lang-shell/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-shell/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-lang-sql/SKILL.md +0 -124
- moai_adk/templates/.claude/skills/moai-lang-sql/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-sql/reference.md +0 -31
- moai_adk/templates/.claude/skills/moai-lang-swift/SKILL.md +0 -123
- moai_adk/templates/.claude/skills/moai-lang-swift/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-swift/reference.md +0 -30
- moai_adk/templates/.claude/skills/moai-lang-typescript/SKILL.md +0 -133
- moai_adk/templates/.claude/skills/moai-lang-typescript/examples.md +0 -29
- moai_adk/templates/.claude/skills/moai-lang-typescript/reference.md +0 -34
- moai_adk/templates/.claude/skills/moai-project-documentation.md +0 -622
- moai_adk/templates/.github/workflows/c-tag-validation.yml +0 -11
- moai_adk/templates/.github/workflows/cpp-tag-validation.yml +0 -11
- moai_adk/templates/.github/workflows/csharp-tag-validation.yml +0 -11
- moai_adk/templates/.github/workflows/dart-tag-validation.yml +0 -11
- moai_adk/templates/.github/workflows/go-tag-validation.yml +0 -130
- moai_adk/templates/.github/workflows/java-tag-validation.yml +0 -11
- moai_adk/templates/.github/workflows/javascript-tag-validation.yml +0 -135
- moai_adk/templates/.github/workflows/kotlin-tag-validation.yml +0 -11
- moai_adk/templates/.github/workflows/moai-gitflow.yml +0 -419
- moai_adk/templates/.github/workflows/moai-release-create.yml +0 -100
- moai_adk/templates/.github/workflows/moai-release-pipeline.yml +0 -188
- moai_adk/templates/.github/workflows/php-tag-validation.yml +0 -11
- moai_adk/templates/.github/workflows/python-tag-validation.yml +0 -118
- moai_adk/templates/.github/workflows/release.yml +0 -118
- moai_adk/templates/.github/workflows/ruby-tag-validation.yml +0 -11
- moai_adk/templates/.github/workflows/rust-tag-validation.yml +0 -11
- moai_adk/templates/.github/workflows/shell-tag-validation.yml +0 -11
- moai_adk/templates/.github/workflows/swift-tag-validation.yml +0 -11
- moai_adk/templates/.github/workflows/tag-report.yml +0 -269
- moai_adk/templates/.github/workflows/tag-validation.yml +0 -186
- moai_adk/templates/.github/workflows/typescript-tag-validation.yml +0 -154
- moai_adk/templates/.moai/config.json +0 -115
- moai_adk/templates/workflows/go-tag-validation.yml +0 -30
- moai_adk/templates/workflows/javascript-tag-validation.yml +0 -41
- moai_adk/templates/workflows/python-tag-validation.yml +0 -42
- moai_adk/templates/workflows/typescript-tag-validation.yml +0 -31
- moai_adk-0.15.1.dist-info/METADATA +0 -3094
- moai_adk-0.15.1.dist-info/RECORD +0 -365
- {moai_adk-0.15.1.dist-info โ moai_adk-0.32.8.dist-info}/licenses/LICENSE +0 -0
moai_adk/cli/commands/update.py
CHANGED
|
@@ -13,6 +13,7 @@ Includes:
|
|
|
13
13
|
- 70-80% performance improvement for up-to-date projects
|
|
14
14
|
|
|
15
15
|
## Skill Invocation Guide (English-Only)
|
|
16
|
+
# mypy: disable-error-code=return-value
|
|
16
17
|
|
|
17
18
|
### Related Skills
|
|
18
19
|
- **moai-foundation-trust**: For post-update validation
|
|
@@ -25,33 +26,44 @@ Includes:
|
|
|
25
26
|
|
|
26
27
|
### When to Invoke Skills in Related Workflows
|
|
27
28
|
1. **After successful update**:
|
|
28
|
-
- Run `Skill("moai-foundation-trust")` to validate all TRUST
|
|
29
|
+
- Run `Skill("moai-foundation-trust")` to validate all TRUST 4 gates
|
|
29
30
|
- Run `Skill("moai-foundation-langs")` to confirm language toolchain still works
|
|
30
31
|
- Run project doctor command for full system validation
|
|
31
32
|
|
|
32
33
|
2. **Before updating**:
|
|
33
34
|
- Create backup with `python -m moai_adk backup`
|
|
34
|
-
- Run `Skill("moai-foundation-tags")` to document current TAG state
|
|
35
35
|
|
|
36
36
|
3. **If update fails**:
|
|
37
37
|
- Use backup to restore previous state
|
|
38
38
|
- Debug with `python -m moai_adk doctor --verbose`
|
|
39
39
|
"""
|
|
40
40
|
|
|
41
|
+
# type: ignore
|
|
42
|
+
|
|
41
43
|
from __future__ import annotations
|
|
42
44
|
|
|
43
45
|
import json
|
|
44
46
|
import logging
|
|
47
|
+
import shutil
|
|
45
48
|
import subprocess
|
|
46
49
|
from datetime import datetime
|
|
47
50
|
from pathlib import Path
|
|
48
|
-
from typing import Any, cast
|
|
51
|
+
from typing import Any, Union, cast
|
|
49
52
|
|
|
50
53
|
import click
|
|
54
|
+
import yaml
|
|
51
55
|
from packaging import version
|
|
52
56
|
from rich.console import Console
|
|
53
57
|
|
|
54
58
|
from moai_adk import __version__
|
|
59
|
+
from moai_adk.core.merge import MergeAnalyzer
|
|
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
|
|
55
67
|
from moai_adk.core.template.processor import TemplateProcessor
|
|
56
68
|
|
|
57
69
|
console = Console()
|
|
@@ -64,7 +76,6 @@ PIPX_COMMAND = ["pipx", "upgrade", "moai-adk"]
|
|
|
64
76
|
PIP_COMMAND = ["pip", "install", "--upgrade", "moai-adk"]
|
|
65
77
|
|
|
66
78
|
|
|
67
|
-
# @CODE:UPDATE-REFACTOR-002-004
|
|
68
79
|
# Custom exceptions for better error handling
|
|
69
80
|
class UpdateError(Exception):
|
|
70
81
|
"""Base exception for update operations."""
|
|
@@ -96,6 +107,45 @@ class TemplateSyncError(UpdateError):
|
|
|
96
107
|
pass
|
|
97
108
|
|
|
98
109
|
|
|
110
|
+
def _get_config_path(project_path: Path) -> tuple[Path, bool]:
|
|
111
|
+
"""Get config file path, preferring YAML over JSON.
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
Tuple of (config_path, is_yaml)
|
|
115
|
+
"""
|
|
116
|
+
yaml_path = project_path / ".moai" / "config" / "config.yaml"
|
|
117
|
+
json_path = project_path / ".moai" / "config" / "config.json"
|
|
118
|
+
|
|
119
|
+
if yaml_path.exists():
|
|
120
|
+
return yaml_path, True
|
|
121
|
+
return json_path, False
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _load_config(config_path: Path) -> dict[str, Any]:
|
|
125
|
+
"""Load config from YAML or JSON file."""
|
|
126
|
+
if not config_path.exists():
|
|
127
|
+
return {}
|
|
128
|
+
|
|
129
|
+
is_yaml = config_path.suffix in (".yaml", ".yml")
|
|
130
|
+
content = config_path.read_text(encoding="utf-8")
|
|
131
|
+
|
|
132
|
+
if is_yaml:
|
|
133
|
+
return yaml.safe_load(content) or {}
|
|
134
|
+
return json.loads(content)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _save_config(config_path: Path, config_data: dict[str, Any]) -> None:
|
|
138
|
+
"""Save config to YAML or JSON file."""
|
|
139
|
+
is_yaml = config_path.suffix in (".yaml", ".yml")
|
|
140
|
+
|
|
141
|
+
if is_yaml:
|
|
142
|
+
content = yaml.safe_dump(config_data, default_flow_style=False, allow_unicode=True, sort_keys=False)
|
|
143
|
+
else:
|
|
144
|
+
content = json.dumps(config_data, indent=2, ensure_ascii=False) + "\n"
|
|
145
|
+
|
|
146
|
+
config_path.write_text(content, encoding="utf-8")
|
|
147
|
+
|
|
148
|
+
|
|
99
149
|
def _is_installed_via_uv_tool() -> bool:
|
|
100
150
|
"""Check if moai-adk installed via uv tool.
|
|
101
151
|
|
|
@@ -104,7 +154,11 @@ def _is_installed_via_uv_tool() -> bool:
|
|
|
104
154
|
"""
|
|
105
155
|
try:
|
|
106
156
|
result = subprocess.run(
|
|
107
|
-
["uv", "tool", "list"],
|
|
157
|
+
["uv", "tool", "list"],
|
|
158
|
+
capture_output=True,
|
|
159
|
+
text=True,
|
|
160
|
+
timeout=TOOL_DETECTION_TIMEOUT,
|
|
161
|
+
check=False,
|
|
108
162
|
)
|
|
109
163
|
return result.returncode == 0 and "moai-adk" in result.stdout
|
|
110
164
|
except (FileNotFoundError, subprocess.TimeoutExpired, OSError):
|
|
@@ -119,7 +173,11 @@ def _is_installed_via_pipx() -> bool:
|
|
|
119
173
|
"""
|
|
120
174
|
try:
|
|
121
175
|
result = subprocess.run(
|
|
122
|
-
["pipx", "list"],
|
|
176
|
+
["pipx", "list"],
|
|
177
|
+
capture_output=True,
|
|
178
|
+
text=True,
|
|
179
|
+
timeout=TOOL_DETECTION_TIMEOUT,
|
|
180
|
+
check=False,
|
|
123
181
|
)
|
|
124
182
|
return result.returncode == 0 and "moai-adk" in result.stdout
|
|
125
183
|
except (FileNotFoundError, subprocess.TimeoutExpired, OSError):
|
|
@@ -134,14 +192,17 @@ def _is_installed_via_pip() -> bool:
|
|
|
134
192
|
"""
|
|
135
193
|
try:
|
|
136
194
|
result = subprocess.run(
|
|
137
|
-
["pip", "show", "moai-adk"],
|
|
195
|
+
["pip", "show", "moai-adk"],
|
|
196
|
+
capture_output=True,
|
|
197
|
+
text=True,
|
|
198
|
+
timeout=TOOL_DETECTION_TIMEOUT,
|
|
199
|
+
check=False,
|
|
138
200
|
)
|
|
139
201
|
return result.returncode == 0
|
|
140
202
|
except (FileNotFoundError, subprocess.TimeoutExpired, OSError):
|
|
141
203
|
return False
|
|
142
204
|
|
|
143
205
|
|
|
144
|
-
# @CODE:UPDATE-REFACTOR-002-001
|
|
145
206
|
def _detect_tool_installer() -> list[str] | None:
|
|
146
207
|
"""Detect which tool installed moai-adk.
|
|
147
208
|
|
|
@@ -181,7 +242,6 @@ def _detect_tool_installer() -> list[str] | None:
|
|
|
181
242
|
return None
|
|
182
243
|
|
|
183
244
|
|
|
184
|
-
# @CODE:UPDATE-REFACTOR-002-002
|
|
185
245
|
def _get_current_version() -> str:
|
|
186
246
|
"""Get currently installed moai-adk version.
|
|
187
247
|
|
|
@@ -238,7 +298,6 @@ def _compare_versions(current: str, latest: str) -> int:
|
|
|
238
298
|
return 1
|
|
239
299
|
|
|
240
300
|
|
|
241
|
-
# @CODE:UPDATE-REFACTOR-002-006
|
|
242
301
|
def _get_package_config_version() -> str:
|
|
243
302
|
"""Get the current package template version.
|
|
244
303
|
|
|
@@ -253,11 +312,10 @@ def _get_package_config_version() -> str:
|
|
|
253
312
|
return __version__
|
|
254
313
|
|
|
255
314
|
|
|
256
|
-
# @CODE:UPDATE-REFACTOR-002-007
|
|
257
315
|
def _get_project_config_version(project_path: Path) -> str:
|
|
258
316
|
"""Get current project config.json template version.
|
|
259
317
|
|
|
260
|
-
This reads the project's .moai/config.json to determine the current
|
|
318
|
+
This reads the project's .moai/config/config.json to determine the current
|
|
261
319
|
template version that the project is configured with.
|
|
262
320
|
|
|
263
321
|
Args:
|
|
@@ -268,38 +326,397 @@ def _get_project_config_version(project_path: Path) -> str:
|
|
|
268
326
|
Returns "0.0.0" if template_version field not found (indicates no prior sync)
|
|
269
327
|
|
|
270
328
|
Raises:
|
|
271
|
-
ValueError: If config
|
|
329
|
+
ValueError: If config file exists but cannot be parsed
|
|
272
330
|
"""
|
|
273
331
|
|
|
274
|
-
def
|
|
332
|
+
def _is_placeholder_val(value: str) -> bool:
|
|
275
333
|
"""Check if value contains unsubstituted template placeholders."""
|
|
276
334
|
return isinstance(value, str) and value.startswith("{{") and value.endswith("}}")
|
|
277
335
|
|
|
278
|
-
config_path = project_path
|
|
336
|
+
config_path, _ = _get_config_path(project_path)
|
|
279
337
|
|
|
280
338
|
if not config_path.exists():
|
|
281
339
|
# No config yet, treat as version 0.0.0 (needs initial sync)
|
|
282
340
|
return "0.0.0"
|
|
283
341
|
|
|
284
342
|
try:
|
|
285
|
-
config_data =
|
|
343
|
+
config_data = _load_config(config_path)
|
|
286
344
|
# Check for template_version in project section
|
|
287
345
|
template_version = config_data.get("project", {}).get("template_version")
|
|
288
|
-
if template_version and not
|
|
346
|
+
if template_version and not _is_placeholder_val(template_version):
|
|
289
347
|
return template_version
|
|
290
348
|
|
|
291
349
|
# Fallback to moai version if no template_version exists
|
|
292
350
|
moai_version = config_data.get("moai", {}).get("version")
|
|
293
|
-
if moai_version and not
|
|
351
|
+
if moai_version and not _is_placeholder_val(moai_version):
|
|
294
352
|
return moai_version
|
|
295
353
|
|
|
296
354
|
# If values are placeholders or don't exist, treat as uninitialized (0.0.0 triggers sync)
|
|
297
355
|
return "0.0.0"
|
|
298
|
-
except json.JSONDecodeError as e:
|
|
299
|
-
raise ValueError(f"Failed to parse project config
|
|
356
|
+
except (json.JSONDecodeError, yaml.YAMLError) as e:
|
|
357
|
+
raise ValueError(f"Failed to parse project config: {e}") from e
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def _ask_merge_strategy(yes: bool = False) -> str:
|
|
361
|
+
"""
|
|
362
|
+
Ask user to choose merge strategy via CLI prompt.
|
|
363
|
+
|
|
364
|
+
Args:
|
|
365
|
+
yes: If True, auto-select "auto" (for --yes flag)
|
|
366
|
+
|
|
367
|
+
Returns:
|
|
368
|
+
"auto" or "manual"
|
|
369
|
+
"""
|
|
370
|
+
if yes:
|
|
371
|
+
return "auto"
|
|
372
|
+
|
|
373
|
+
console.print("\n[cyan]๐ Choose merge strategy:[/cyan]")
|
|
374
|
+
console.print("[cyan] [1] Auto-merge (default)[/cyan]")
|
|
375
|
+
console.print("[dim] โ Template installs fresh + user changes preserved + minimal conflicts[/dim]")
|
|
376
|
+
console.print("[cyan] [2] Manual merge[/cyan]")
|
|
377
|
+
console.print("[dim] โ Backup preserved + merge guide generated + you control merging[/dim]")
|
|
378
|
+
|
|
379
|
+
response = click.prompt("Select [1 or 2]", default="1")
|
|
380
|
+
if response == "2":
|
|
381
|
+
return "manual"
|
|
382
|
+
return "auto"
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
def _generate_manual_merge_guide(backup_path: Path, template_path: Path, project_path: Path) -> Path:
|
|
386
|
+
"""
|
|
387
|
+
Generate comprehensive merge guide for manual merging.
|
|
388
|
+
|
|
389
|
+
Args:
|
|
390
|
+
backup_path: Path to backup directory
|
|
391
|
+
template_path: Path to template directory
|
|
392
|
+
project_path: Project root path
|
|
393
|
+
|
|
394
|
+
Returns:
|
|
395
|
+
Path to generated merge guide
|
|
396
|
+
"""
|
|
397
|
+
guide_dir = project_path / ".moai" / "guides"
|
|
398
|
+
guide_dir.mkdir(parents=True, exist_ok=True)
|
|
399
|
+
|
|
400
|
+
guide_path = guide_dir / "merge-guide.md"
|
|
401
|
+
|
|
402
|
+
# Find changed files
|
|
403
|
+
changed_files = []
|
|
404
|
+
backup_claude = backup_path / ".claude"
|
|
405
|
+
backup_path / ".moai"
|
|
406
|
+
|
|
407
|
+
# Compare .claude/
|
|
408
|
+
if backup_claude.exists():
|
|
409
|
+
for file in backup_claude.rglob("*"):
|
|
410
|
+
if file.is_file():
|
|
411
|
+
rel_path = file.relative_to(backup_path)
|
|
412
|
+
current_file = project_path / rel_path
|
|
413
|
+
if current_file.exists():
|
|
414
|
+
if file.read_text(encoding="utf-8", errors="ignore") != current_file.read_text(
|
|
415
|
+
encoding="utf-8", errors="ignore"
|
|
416
|
+
):
|
|
417
|
+
changed_files.append(f" - {rel_path}")
|
|
418
|
+
else:
|
|
419
|
+
changed_files.append(f" - {rel_path} (new)")
|
|
420
|
+
|
|
421
|
+
# Generate guide
|
|
422
|
+
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
423
|
+
guide_content = f"""# Merge Guide - Manual Merge Mode
|
|
424
|
+
|
|
425
|
+
**Generated**: {timestamp}
|
|
426
|
+
**Backup Location**: `{backup_path.relative_to(project_path)}/`
|
|
427
|
+
|
|
428
|
+
## Summary
|
|
429
|
+
|
|
430
|
+
During this update, the following files were changed:
|
|
431
|
+
|
|
432
|
+
{chr(10).join(changed_files) if changed_files else " (No changes detected)"}
|
|
433
|
+
|
|
434
|
+
## How to Merge
|
|
435
|
+
|
|
436
|
+
### Option 1: Using diff (Terminal)
|
|
437
|
+
|
|
438
|
+
```bash
|
|
439
|
+
# Compare specific files
|
|
440
|
+
diff {backup_path.name}/.claude/settings.json .claude/settings.json
|
|
441
|
+
|
|
442
|
+
# View all differences
|
|
443
|
+
diff -r {backup_path.name}/ .
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
### Option 2: Using Visual Merge Tool
|
|
447
|
+
|
|
448
|
+
```bash
|
|
449
|
+
# macOS/Linux - Using meld
|
|
450
|
+
meld {backup_path.relative_to(project_path)}/ .
|
|
451
|
+
|
|
452
|
+
# Using VSCode
|
|
453
|
+
code --diff {backup_path.relative_to(project_path)}/.claude/settings.json .claude/settings.json
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
### Option 3: Manual Line-by-Line
|
|
457
|
+
|
|
458
|
+
1. Open backup file in your editor
|
|
459
|
+
2. Open current file side-by-side
|
|
460
|
+
3. Manually copy your customizations
|
|
461
|
+
|
|
462
|
+
## Key Files to Review
|
|
463
|
+
|
|
464
|
+
### .claude/settings.json
|
|
465
|
+
- Contains MCP servers, hooks, environment variables
|
|
466
|
+
- **Action**: Restore any custom MCP servers and environment variables
|
|
467
|
+
- **Location**: {backup_path.relative_to(project_path)}/.claude/settings.json
|
|
468
|
+
|
|
469
|
+
### .moai/config/config.json
|
|
470
|
+
- Contains project configuration and metadata
|
|
471
|
+
- **Action**: Verify user-specific settings are preserved
|
|
472
|
+
- **Location**: {backup_path.relative_to(project_path)}/.moai/config/config.json
|
|
473
|
+
|
|
474
|
+
### .claude/commands/, .claude/agents/, .claude/hooks/
|
|
475
|
+
- Contains custom scripts and automation
|
|
476
|
+
- **Action**: Restore any custom scripts outside of /moai/ folders
|
|
477
|
+
- **Location**: {backup_path.relative_to(project_path)}/.claude/
|
|
478
|
+
|
|
479
|
+
## Migration Checklist
|
|
480
|
+
|
|
481
|
+
- [ ] Compare `.claude/settings.json`
|
|
482
|
+
- [ ] Restore custom MCP servers
|
|
483
|
+
- [ ] Restore environment variables
|
|
484
|
+
- [ ] Verify hooks are properly configured
|
|
485
|
+
|
|
486
|
+
- [ ] Review `.moai/config/config.json`
|
|
487
|
+
- [ ] Check version was updated
|
|
488
|
+
- [ ] Verify user settings preserved
|
|
489
|
+
|
|
490
|
+
- [ ] Restore custom scripts
|
|
491
|
+
- [ ] Any custom commands outside /moai/
|
|
492
|
+
- [ ] Any custom agents outside /moai/
|
|
493
|
+
- [ ] Any custom hooks outside /moai/
|
|
494
|
+
|
|
495
|
+
- [ ] Run tests
|
|
496
|
+
```bash
|
|
497
|
+
uv run pytest
|
|
498
|
+
moai-adk validate
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
- [ ] Commit changes
|
|
502
|
+
```bash
|
|
503
|
+
git add .
|
|
504
|
+
git commit -m "merge: Update templates with manual merge"
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
## Rollback if Needed
|
|
508
|
+
|
|
509
|
+
If you want to cancel and restore the backup:
|
|
510
|
+
|
|
511
|
+
```bash
|
|
512
|
+
# Restore everything from backup
|
|
513
|
+
cp -r {backup_path.relative_to(project_path)}/.claude .
|
|
514
|
+
cp -r {backup_path.relative_to(project_path)}/.moai .
|
|
515
|
+
cp {backup_path.relative_to(project_path)}/CLAUDE.md .
|
|
516
|
+
|
|
517
|
+
# Or restore specific files
|
|
518
|
+
cp {backup_path.relative_to(project_path)}/.claude/settings.json .claude/
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
## Questions?
|
|
522
|
+
|
|
523
|
+
If you encounter merge conflicts or issues:
|
|
524
|
+
|
|
525
|
+
1. Check the backup folder for original files
|
|
526
|
+
2. Compare line-by-line using diff tools
|
|
527
|
+
3. Consult documentation: https://adk.mo.ai.kr/update-merge
|
|
528
|
+
|
|
529
|
+
---
|
|
530
|
+
|
|
531
|
+
**Backup**: `{backup_path}/`
|
|
532
|
+
**Generated**: {timestamp}
|
|
533
|
+
"""
|
|
534
|
+
|
|
535
|
+
guide_path.write_text(guide_content, encoding="utf-8")
|
|
536
|
+
logger.info(f"โ
Merge guide created: {guide_path}")
|
|
537
|
+
return guide_path
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
def _migrate_legacy_logs(project_path: Path, dry_run: bool = False) -> bool:
|
|
541
|
+
"""Migrate legacy log files to unified directory structure.
|
|
542
|
+
|
|
543
|
+
Creates new unified directory structure (.moai/docs/, .moai/logs/archive/) and
|
|
544
|
+
migrates files from legacy locations to new unified structure:
|
|
545
|
+
- .moai/memory/last-session-state.json โ .moai/logs/sessions/
|
|
546
|
+
- .moai/error_logs/ โ .moai/logs/errors/
|
|
547
|
+
- .moai/reports/ โ .moai/docs/reports/
|
|
548
|
+
|
|
549
|
+
Args:
|
|
550
|
+
project_path: Project directory path (absolute)
|
|
551
|
+
dry_run: If True, only simulate migration without making changes
|
|
552
|
+
|
|
553
|
+
Returns:
|
|
554
|
+
True if migration succeeded or no migration needed, False otherwise
|
|
555
|
+
|
|
556
|
+
Raises:
|
|
557
|
+
Exception: If migration fails during actual execution
|
|
558
|
+
"""
|
|
559
|
+
try:
|
|
560
|
+
# Define source and target directories
|
|
561
|
+
legacy_memory = project_path / ".moai" / "memory"
|
|
562
|
+
legacy_error_logs = project_path / ".moai" / "error_logs"
|
|
563
|
+
legacy_reports = project_path / ".moai" / "reports"
|
|
564
|
+
|
|
565
|
+
# Create new unified directory structure
|
|
566
|
+
new_logs_dir = project_path / ".moai" / "logs"
|
|
567
|
+
new_docs_dir = project_path / ".moai" / "docs"
|
|
568
|
+
new_sessions_dir = new_logs_dir / "sessions"
|
|
569
|
+
new_errors_dir = new_logs_dir / "errors"
|
|
570
|
+
new_archive_dir = new_logs_dir / "archive"
|
|
571
|
+
new_docs_reports_dir = new_docs_dir / "reports"
|
|
572
|
+
|
|
573
|
+
migration_log = []
|
|
574
|
+
files_migrated = 0
|
|
575
|
+
files_skipped = 0
|
|
576
|
+
|
|
577
|
+
# Check if any legacy directories exist
|
|
578
|
+
has_legacy_files = legacy_memory.exists() or legacy_error_logs.exists() or legacy_reports.exists()
|
|
579
|
+
|
|
580
|
+
if not has_legacy_files:
|
|
581
|
+
if not dry_run:
|
|
582
|
+
# Create new directory structure anyway for consistency
|
|
583
|
+
new_logs_dir.mkdir(parents=True, exist_ok=True)
|
|
584
|
+
new_docs_dir.mkdir(parents=True, exist_ok=True)
|
|
585
|
+
new_sessions_dir.mkdir(parents=True, exist_ok=True)
|
|
586
|
+
new_errors_dir.mkdir(parents=True, exist_ok=True)
|
|
587
|
+
new_archive_dir.mkdir(parents=True, exist_ok=True)
|
|
588
|
+
new_docs_reports_dir.mkdir(parents=True, exist_ok=True)
|
|
589
|
+
return True
|
|
590
|
+
|
|
591
|
+
if dry_run:
|
|
592
|
+
console.print("[cyan]๐ Legacy log migration (dry run):[/cyan]")
|
|
593
|
+
|
|
594
|
+
# Create new directories if not dry run
|
|
595
|
+
if not dry_run:
|
|
596
|
+
new_logs_dir.mkdir(parents=True, exist_ok=True)
|
|
597
|
+
new_docs_dir.mkdir(parents=True, exist_ok=True)
|
|
598
|
+
new_sessions_dir.mkdir(parents=True, exist_ok=True)
|
|
599
|
+
new_errors_dir.mkdir(parents=True, exist_ok=True)
|
|
600
|
+
new_archive_dir.mkdir(parents=True, exist_ok=True)
|
|
601
|
+
new_docs_reports_dir.mkdir(parents=True, exist_ok=True)
|
|
602
|
+
|
|
603
|
+
# Migration 1: .moai/memory/last-session-state.json โ .moai/logs/sessions/
|
|
604
|
+
if legacy_memory.exists():
|
|
605
|
+
session_file = legacy_memory / "last-session-state.json"
|
|
606
|
+
if session_file.exists():
|
|
607
|
+
target_file = new_sessions_dir / "last-session-state.json"
|
|
608
|
+
|
|
609
|
+
if target_file.exists():
|
|
610
|
+
files_skipped += 1
|
|
611
|
+
migration_log.append(f"Skipped: {session_file.relative_to(project_path)} (target already exists)")
|
|
612
|
+
else:
|
|
613
|
+
if not dry_run:
|
|
614
|
+
shutil.copy2(session_file, target_file)
|
|
615
|
+
# Preserve original timestamp
|
|
616
|
+
shutil.copystat(session_file, target_file)
|
|
617
|
+
src_path = session_file.relative_to(project_path)
|
|
618
|
+
dst_path = target_file.relative_to(project_path)
|
|
619
|
+
migration_log.append(f"Migrated: {src_path} โ {dst_path}")
|
|
620
|
+
else:
|
|
621
|
+
src_path = session_file.relative_to(project_path)
|
|
622
|
+
dst_path = target_file.relative_to(project_path)
|
|
623
|
+
migration_log.append(f"Would migrate: {src_path} โ {dst_path}")
|
|
624
|
+
files_migrated += 1
|
|
625
|
+
|
|
626
|
+
# Migration 2: .moai/error_logs/ โ .moai/logs/errors/
|
|
627
|
+
if legacy_error_logs.exists() and legacy_error_logs.is_dir():
|
|
628
|
+
for error_file in legacy_error_logs.rglob("*"):
|
|
629
|
+
if error_file.is_file():
|
|
630
|
+
relative_path = error_file.relative_to(legacy_error_logs)
|
|
631
|
+
target_file = new_errors_dir / relative_path
|
|
632
|
+
|
|
633
|
+
# Ensure target directory exists
|
|
634
|
+
if not dry_run:
|
|
635
|
+
target_file.parent.mkdir(parents=True, exist_ok=True)
|
|
636
|
+
|
|
637
|
+
if target_file.exists():
|
|
638
|
+
files_skipped += 1
|
|
639
|
+
error_path = error_file.relative_to(project_path)
|
|
640
|
+
migration_log.append(f"Skipped: {error_path} (target already exists)")
|
|
641
|
+
else:
|
|
642
|
+
if not dry_run:
|
|
643
|
+
shutil.copy2(error_file, target_file)
|
|
644
|
+
shutil.copystat(error_file, target_file)
|
|
645
|
+
error_path = error_file.relative_to(project_path)
|
|
646
|
+
target_path = target_file.relative_to(project_path)
|
|
647
|
+
migration_log.append(f"Migrated: {error_path} โ {target_path}")
|
|
648
|
+
else:
|
|
649
|
+
error_path = error_file.relative_to(project_path)
|
|
650
|
+
target_path = target_file.relative_to(project_path)
|
|
651
|
+
migration_log.append(f"Would migrate: {error_path} โ {target_path}")
|
|
652
|
+
files_migrated += 1
|
|
653
|
+
|
|
654
|
+
# Migration 3: .moai/reports/ โ .moai/docs/reports/
|
|
655
|
+
if legacy_reports.exists() and legacy_reports.is_dir():
|
|
656
|
+
for report_file in legacy_reports.rglob("*"):
|
|
657
|
+
if report_file.is_file():
|
|
658
|
+
relative_path = report_file.relative_to(legacy_reports)
|
|
659
|
+
target_file = new_docs_reports_dir / relative_path
|
|
660
|
+
|
|
661
|
+
# Ensure target directory exists
|
|
662
|
+
if not dry_run:
|
|
663
|
+
target_file.parent.mkdir(parents=True, exist_ok=True)
|
|
664
|
+
|
|
665
|
+
if target_file.exists():
|
|
666
|
+
files_skipped += 1
|
|
667
|
+
report_path = report_file.relative_to(project_path)
|
|
668
|
+
migration_log.append(f"Skipped: {report_path} (target already exists)")
|
|
669
|
+
else:
|
|
670
|
+
if not dry_run:
|
|
671
|
+
shutil.copy2(report_file, target_file)
|
|
672
|
+
shutil.copystat(report_file, target_file)
|
|
673
|
+
report_path = report_file.relative_to(project_path)
|
|
674
|
+
target_path = target_file.relative_to(project_path)
|
|
675
|
+
migration_log.append(f"Migrated: {report_path} โ {target_path}")
|
|
676
|
+
else:
|
|
677
|
+
report_path = report_file.relative_to(project_path)
|
|
678
|
+
target_path = target_file.relative_to(project_path)
|
|
679
|
+
migration_log.append(f"Would migrate: {report_path} โ {target_path}")
|
|
680
|
+
files_migrated += 1
|
|
681
|
+
|
|
682
|
+
# Create migration log
|
|
683
|
+
migration_log_path = new_logs_dir / "migration-log.json"
|
|
684
|
+
if not dry_run and files_migrated > 0:
|
|
685
|
+
migration_data = {
|
|
686
|
+
"migration_timestamp": datetime.now().isoformat(),
|
|
687
|
+
"moai_adk_version": __version__,
|
|
688
|
+
"files_migrated": files_migrated,
|
|
689
|
+
"files_skipped": files_skipped,
|
|
690
|
+
"migration_log": migration_log,
|
|
691
|
+
"legacy_directories_found": [
|
|
692
|
+
str(d.relative_to(project_path))
|
|
693
|
+
for d in [legacy_memory, legacy_error_logs, legacy_reports]
|
|
694
|
+
if d.exists()
|
|
695
|
+
],
|
|
696
|
+
}
|
|
697
|
+
json_content = json.dumps(migration_data, indent=2, ensure_ascii=False)
|
|
698
|
+
migration_log_path.write_text(json_content + "\n", encoding="utf-8")
|
|
699
|
+
|
|
700
|
+
# Display results
|
|
701
|
+
if files_migrated > 0 or files_skipped > 0:
|
|
702
|
+
if dry_run:
|
|
703
|
+
console.print(f" [yellow]Would migrate {files_migrated} files, skip {files_skipped} files[/yellow]")
|
|
704
|
+
else:
|
|
705
|
+
console.print(f" [green]โ Migrated {files_migrated} legacy log files[/green]")
|
|
706
|
+
if files_skipped > 0:
|
|
707
|
+
console.print(f" [yellow]โ Skipped {files_skipped} files (already exist)[/yellow]")
|
|
708
|
+
console.print(f" [dim] Migration log: {migration_log_path.relative_to(project_path)}[/dim]")
|
|
709
|
+
elif has_legacy_files:
|
|
710
|
+
console.print(" [dim] No files to migrate[/dim]")
|
|
711
|
+
|
|
712
|
+
return True
|
|
713
|
+
|
|
714
|
+
except Exception as e:
|
|
715
|
+
console.print(f" [red]โ Log migration failed: {e}[/red]")
|
|
716
|
+
logger.error(f"Legacy log migration failed: {e}", exc_info=True)
|
|
717
|
+
return False
|
|
300
718
|
|
|
301
719
|
|
|
302
|
-
# @CODE:UPDATE-CACHE-FIX-001-001-DETECT-STALE
|
|
303
720
|
def _detect_stale_cache(upgrade_output: str, current_version: str, latest_version: str) -> bool:
|
|
304
721
|
"""
|
|
305
722
|
Detect if uv cache is stale by comparing versions.
|
|
@@ -345,7 +762,6 @@ def _detect_stale_cache(upgrade_output: str, current_version: str, latest_versio
|
|
|
345
762
|
return False
|
|
346
763
|
|
|
347
764
|
|
|
348
|
-
# @CODE:UPDATE-CACHE-FIX-001-002-CLEAR-SUCCESS
|
|
349
765
|
def _clear_uv_package_cache(package_name: str = "moai-adk") -> bool:
|
|
350
766
|
"""
|
|
351
767
|
Clear uv cache for specific package.
|
|
@@ -396,7 +812,6 @@ def _clear_uv_package_cache(package_name: str = "moai-adk") -> bool:
|
|
|
396
812
|
return False
|
|
397
813
|
|
|
398
814
|
|
|
399
|
-
# @CODE:UPDATE-CACHE-FIX-001-003-RETRY-LOGIC
|
|
400
815
|
def _execute_upgrade_with_retry(installer_cmd: list[str], package_name: str = "moai-adk") -> bool:
|
|
401
816
|
"""
|
|
402
817
|
Execute upgrade with automatic cache retry on stale detection.
|
|
@@ -472,7 +887,13 @@ def _execute_upgrade_with_retry(installer_cmd: list[str], package_name: str = "m
|
|
|
472
887
|
|
|
473
888
|
# Stage 6: Retry upgrade
|
|
474
889
|
try:
|
|
475
|
-
result = subprocess.run(
|
|
890
|
+
result = subprocess.run(
|
|
891
|
+
installer_cmd,
|
|
892
|
+
capture_output=True,
|
|
893
|
+
text=True,
|
|
894
|
+
timeout=60,
|
|
895
|
+
check=False,
|
|
896
|
+
)
|
|
476
897
|
|
|
477
898
|
if result.returncode == 0:
|
|
478
899
|
return True
|
|
@@ -515,19 +936,590 @@ def _execute_upgrade(installer_cmd: list[str]) -> bool:
|
|
|
515
936
|
return False
|
|
516
937
|
|
|
517
938
|
|
|
518
|
-
def
|
|
519
|
-
"""
|
|
939
|
+
def _preserve_user_settings(project_path: Path) -> dict[str, Path | None]:
|
|
940
|
+
"""Back up user-specific settings files before template sync.
|
|
941
|
+
|
|
942
|
+
Args:
|
|
943
|
+
project_path: Project directory path
|
|
944
|
+
|
|
945
|
+
Returns:
|
|
946
|
+
Dictionary with backup paths of preserved files
|
|
947
|
+
"""
|
|
948
|
+
preserved = {}
|
|
949
|
+
claude_dir = project_path / ".claude"
|
|
950
|
+
|
|
951
|
+
# Preserve settings.local.json (user MCP and GLM configuration)
|
|
952
|
+
settings_local = claude_dir / "settings.local.json"
|
|
953
|
+
if settings_local.exists():
|
|
954
|
+
try:
|
|
955
|
+
backup_dir = project_path / ".moai-backups" / "settings-backup"
|
|
956
|
+
backup_dir.mkdir(parents=True, exist_ok=True)
|
|
957
|
+
backup_path = backup_dir / "settings.local.json"
|
|
958
|
+
backup_path.write_text(settings_local.read_text(encoding="utf-8"))
|
|
959
|
+
preserved["settings.local.json"] = backup_path
|
|
960
|
+
console.print(" [cyan]๐พ Backed up user settings[/cyan]")
|
|
961
|
+
except Exception as e:
|
|
962
|
+
logger.warning(f"Failed to backup settings.local.json: {e}")
|
|
963
|
+
preserved["settings.local.json"] = None
|
|
964
|
+
else:
|
|
965
|
+
preserved["settings.local.json"] = None
|
|
966
|
+
|
|
967
|
+
return preserved
|
|
968
|
+
|
|
969
|
+
|
|
970
|
+
def _restore_user_settings(project_path: Path, preserved: dict[str, Path | None]) -> bool:
|
|
971
|
+
"""Restore user-specific settings files after template sync.
|
|
972
|
+
|
|
973
|
+
Args:
|
|
974
|
+
project_path: Project directory path
|
|
975
|
+
preserved: Dictionary of backup paths from _preserve_user_settings()
|
|
976
|
+
|
|
977
|
+
Returns:
|
|
978
|
+
True if restoration succeeded, False otherwise
|
|
979
|
+
"""
|
|
980
|
+
claude_dir = project_path / ".claude"
|
|
981
|
+
claude_dir.mkdir(parents=True, exist_ok=True)
|
|
982
|
+
|
|
983
|
+
success = True
|
|
984
|
+
|
|
985
|
+
# Restore settings.local.json
|
|
986
|
+
if preserved.get("settings.local.json"):
|
|
987
|
+
try:
|
|
988
|
+
backup_path = preserved["settings.local.json"]
|
|
989
|
+
settings_local = claude_dir / "settings.local.json"
|
|
990
|
+
settings_local.write_text(backup_path.read_text(encoding="utf-8"))
|
|
991
|
+
console.print(" [cyan]โ Restored user settings[/cyan]")
|
|
992
|
+
except Exception as e:
|
|
993
|
+
console.print(f" [yellow]โ ๏ธ Failed to restore settings.local.json: {e}[/yellow]")
|
|
994
|
+
logger.warning(f"Failed to restore settings.local.json: {e}")
|
|
995
|
+
success = False
|
|
996
|
+
|
|
997
|
+
return success
|
|
998
|
+
|
|
999
|
+
|
|
1000
|
+
def _get_template_skill_names() -> set[str]:
|
|
1001
|
+
"""Get set of skill folder names from installed template.
|
|
1002
|
+
|
|
1003
|
+
Returns:
|
|
1004
|
+
Set of skill folder names that are part of the template package.
|
|
1005
|
+
"""
|
|
1006
|
+
template_path = Path(__file__).parent.parent.parent / "templates"
|
|
1007
|
+
skills_path = template_path / ".claude" / "skills"
|
|
1008
|
+
|
|
1009
|
+
if not skills_path.exists():
|
|
1010
|
+
return set()
|
|
1011
|
+
|
|
1012
|
+
return {d.name for d in skills_path.iterdir() if d.is_dir()}
|
|
1013
|
+
|
|
1014
|
+
|
|
1015
|
+
def _get_template_command_names() -> set[str]:
|
|
1016
|
+
"""Get set of command file names from installed template.
|
|
1017
|
+
|
|
1018
|
+
Returns:
|
|
1019
|
+
Set of .md command file names from .claude/commands/moai/ in template.
|
|
1020
|
+
"""
|
|
1021
|
+
template_path = Path(__file__).parent.parent.parent / "templates"
|
|
1022
|
+
commands_path = template_path / ".claude" / "commands" / "moai"
|
|
1023
|
+
|
|
1024
|
+
if not commands_path.exists():
|
|
1025
|
+
return set()
|
|
1026
|
+
|
|
1027
|
+
return {f.name for f in commands_path.iterdir() if f.is_file() and f.suffix == ".md"}
|
|
1028
|
+
|
|
1029
|
+
|
|
1030
|
+
def _get_template_agent_names() -> set[str]:
|
|
1031
|
+
"""Get set of agent file names from installed template.
|
|
1032
|
+
|
|
1033
|
+
Returns:
|
|
1034
|
+
Set of agent file names from .claude/agents/ in template.
|
|
1035
|
+
"""
|
|
1036
|
+
template_path = Path(__file__).parent.parent.parent / "templates"
|
|
1037
|
+
agents_path = template_path / ".claude" / "agents"
|
|
1038
|
+
|
|
1039
|
+
if not agents_path.exists():
|
|
1040
|
+
return set()
|
|
1041
|
+
|
|
1042
|
+
return {f.name for f in agents_path.iterdir() if f.is_file()}
|
|
1043
|
+
|
|
1044
|
+
|
|
1045
|
+
def _get_template_hook_names() -> set[str]:
|
|
1046
|
+
"""Get set of hook file names from installed template.
|
|
1047
|
+
|
|
1048
|
+
Returns:
|
|
1049
|
+
Set of .py hook file names from .claude/hooks/moai/ in template.
|
|
1050
|
+
"""
|
|
1051
|
+
template_path = Path(__file__).parent.parent.parent / "templates"
|
|
1052
|
+
hooks_path = template_path / ".claude" / "hooks" / "moai"
|
|
1053
|
+
|
|
1054
|
+
if not hooks_path.exists():
|
|
1055
|
+
return set()
|
|
1056
|
+
|
|
1057
|
+
return {f.name for f in hooks_path.iterdir() if f.is_file() and f.suffix == ".py"}
|
|
1058
|
+
|
|
1059
|
+
|
|
1060
|
+
def _detect_custom_commands(project_path: Path, template_commands: set[str]) -> list[str]:
|
|
1061
|
+
"""Detect custom commands NOT in template (user-created).
|
|
1062
|
+
|
|
1063
|
+
Args:
|
|
1064
|
+
project_path: Project path (absolute)
|
|
1065
|
+
template_commands: Set of template command file names
|
|
1066
|
+
|
|
1067
|
+
Returns:
|
|
1068
|
+
Sorted list of custom command file names.
|
|
1069
|
+
"""
|
|
1070
|
+
commands_path = project_path / ".claude" / "commands" / "moai"
|
|
1071
|
+
|
|
1072
|
+
if not commands_path.exists():
|
|
1073
|
+
return []
|
|
1074
|
+
|
|
1075
|
+
project_commands = {f.name for f in commands_path.iterdir() if f.is_file() and f.suffix == ".md"}
|
|
1076
|
+
custom_commands = project_commands - template_commands
|
|
1077
|
+
|
|
1078
|
+
return sorted(custom_commands)
|
|
1079
|
+
|
|
1080
|
+
|
|
1081
|
+
def _detect_custom_agents(project_path: Path, template_agents: set[str]) -> list[str]:
|
|
1082
|
+
"""Detect custom agents NOT in template (user-created).
|
|
1083
|
+
|
|
1084
|
+
Args:
|
|
1085
|
+
project_path: Project path (absolute)
|
|
1086
|
+
template_agents: Set of template agent file names
|
|
1087
|
+
|
|
1088
|
+
Returns:
|
|
1089
|
+
Sorted list of custom agent file names.
|
|
1090
|
+
"""
|
|
1091
|
+
agents_path = project_path / ".claude" / "agents"
|
|
1092
|
+
|
|
1093
|
+
if not agents_path.exists():
|
|
1094
|
+
return []
|
|
1095
|
+
|
|
1096
|
+
project_agents = {f.name for f in agents_path.iterdir() if f.is_file()}
|
|
1097
|
+
custom_agents = project_agents - template_agents
|
|
1098
|
+
|
|
1099
|
+
return sorted(custom_agents)
|
|
1100
|
+
|
|
1101
|
+
|
|
1102
|
+
def _detect_custom_hooks(project_path: Path, template_hooks: set[str]) -> list[str]:
|
|
1103
|
+
"""Detect custom hooks NOT in template (user-created).
|
|
1104
|
+
|
|
1105
|
+
Args:
|
|
1106
|
+
project_path: Project path (absolute)
|
|
1107
|
+
template_hooks: Set of template hook file names
|
|
1108
|
+
|
|
1109
|
+
Returns:
|
|
1110
|
+
Sorted list of custom hook file names.
|
|
1111
|
+
"""
|
|
1112
|
+
hooks_path = project_path / ".claude" / "hooks" / "moai"
|
|
1113
|
+
|
|
1114
|
+
if not hooks_path.exists():
|
|
1115
|
+
return []
|
|
1116
|
+
|
|
1117
|
+
project_hooks = {f.name for f in hooks_path.iterdir() if f.is_file() and f.suffix == ".py"}
|
|
1118
|
+
custom_hooks = project_hooks - template_hooks
|
|
1119
|
+
|
|
1120
|
+
return sorted(custom_hooks)
|
|
1121
|
+
|
|
1122
|
+
|
|
1123
|
+
def _group_custom_files_by_type(
|
|
1124
|
+
custom_commands: list[str],
|
|
1125
|
+
custom_agents: list[str],
|
|
1126
|
+
custom_hooks: list[str],
|
|
1127
|
+
) -> dict[str, list[str]]:
|
|
1128
|
+
"""Group custom files by type for UI display.
|
|
1129
|
+
|
|
1130
|
+
Args:
|
|
1131
|
+
custom_commands: List of custom command file names
|
|
1132
|
+
custom_agents: List of custom agent file names
|
|
1133
|
+
custom_hooks: List of custom hook file names
|
|
1134
|
+
|
|
1135
|
+
Returns:
|
|
1136
|
+
Dictionary with keys: commands, agents, hooks
|
|
1137
|
+
"""
|
|
1138
|
+
return {
|
|
1139
|
+
"commands": custom_commands,
|
|
1140
|
+
"agents": custom_agents,
|
|
1141
|
+
"hooks": custom_hooks,
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
|
|
1145
|
+
def _prompt_custom_files_restore(
|
|
1146
|
+
custom_commands: list[str],
|
|
1147
|
+
custom_agents: list[str],
|
|
1148
|
+
custom_hooks: list[str],
|
|
1149
|
+
yes: bool = False,
|
|
1150
|
+
) -> dict[str, list[str]]:
|
|
1151
|
+
"""Interactive fuzzy checkbox for custom files restore with search support.
|
|
1152
|
+
|
|
1153
|
+
Args:
|
|
1154
|
+
custom_commands: List of custom command file names
|
|
1155
|
+
custom_agents: List of custom agent file names
|
|
1156
|
+
custom_hooks: List of custom hook file names
|
|
1157
|
+
yes: Auto-confirm flag (skips restoration in CI/CD mode)
|
|
1158
|
+
|
|
1159
|
+
Returns:
|
|
1160
|
+
Dictionary with selected files grouped by type.
|
|
1161
|
+
"""
|
|
1162
|
+
# If no custom files, skip UI
|
|
1163
|
+
if not (custom_commands or custom_agents or custom_hooks):
|
|
1164
|
+
return {
|
|
1165
|
+
"commands": [],
|
|
1166
|
+
"agents": [],
|
|
1167
|
+
"hooks": [],
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
# In --yes mode, skip restoration (safest default)
|
|
1171
|
+
if yes:
|
|
1172
|
+
console.print("\n[dim] Skipping custom files restoration (--yes mode)[/dim]\n")
|
|
1173
|
+
return {
|
|
1174
|
+
"commands": [],
|
|
1175
|
+
"agents": [],
|
|
1176
|
+
"hooks": [],
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
# Try to use new UI, fallback to questionary if import fails
|
|
1180
|
+
try:
|
|
1181
|
+
from moai_adk.cli.ui.prompts import create_grouped_choices, fuzzy_checkbox
|
|
1182
|
+
|
|
1183
|
+
# Build grouped choices for fuzzy checkbox
|
|
1184
|
+
groups: dict[str, list[dict[str, str]]] = {}
|
|
1185
|
+
|
|
1186
|
+
if custom_commands:
|
|
1187
|
+
groups["Commands (.claude/commands/moai/)"] = [
|
|
1188
|
+
{"name": cmd, "value": f"cmd:{cmd}"} for cmd in custom_commands
|
|
1189
|
+
]
|
|
1190
|
+
|
|
1191
|
+
if custom_agents:
|
|
1192
|
+
groups["Agents (.claude/agents/)"] = [{"name": agent, "value": f"agent:{agent}"} for agent in custom_agents]
|
|
1193
|
+
|
|
1194
|
+
if custom_hooks:
|
|
1195
|
+
groups["Hooks (.claude/hooks/moai/)"] = [{"name": hook, "value": f"hook:{hook}"} for hook in custom_hooks]
|
|
1196
|
+
|
|
1197
|
+
choices = create_grouped_choices(groups)
|
|
1198
|
+
|
|
1199
|
+
console.print("\n[#DA7756]๐ฆ Custom files detected in backup:[/#DA7756]")
|
|
1200
|
+
console.print("[dim] Use fuzzy search to find files quickly[/dim]\n")
|
|
1201
|
+
|
|
1202
|
+
selected = fuzzy_checkbox(
|
|
1203
|
+
"Select custom files to restore:",
|
|
1204
|
+
choices=choices,
|
|
1205
|
+
instruction="[Space] Toggle [Tab] All [Enter] Confirm [Type to search]",
|
|
1206
|
+
)
|
|
1207
|
+
|
|
1208
|
+
except ImportError:
|
|
1209
|
+
# Fallback to questionary if new UI not available
|
|
1210
|
+
import questionary
|
|
1211
|
+
from questionary import Choice, Separator
|
|
1212
|
+
|
|
1213
|
+
choices_legacy: list[Union[Separator, Choice]] = []
|
|
1214
|
+
|
|
1215
|
+
if custom_commands:
|
|
1216
|
+
choices_legacy.append(Separator("Commands (.claude/commands/moai/)"))
|
|
1217
|
+
for cmd in custom_commands:
|
|
1218
|
+
choices_legacy.append(Choice(title=cmd, value=f"cmd:{cmd}"))
|
|
1219
|
+
|
|
1220
|
+
if custom_agents:
|
|
1221
|
+
choices_legacy.append(Separator("Agents (.claude/agents/)"))
|
|
1222
|
+
for agent in custom_agents:
|
|
1223
|
+
choices_legacy.append(Choice(title=agent, value=f"agent:{agent}"))
|
|
1224
|
+
|
|
1225
|
+
if custom_hooks:
|
|
1226
|
+
choices_legacy.append(Separator("Hooks (.claude/hooks/moai/)"))
|
|
1227
|
+
for hook in custom_hooks:
|
|
1228
|
+
choices_legacy.append(Choice(title=hook, value=f"hook:{hook}"))
|
|
1229
|
+
|
|
1230
|
+
console.print("\n[cyan]๐ฆ Custom files detected in backup:[/cyan]")
|
|
1231
|
+
console.print("[dim] Select files to restore (none selected by default)[/dim]\n")
|
|
1232
|
+
|
|
1233
|
+
selected = questionary.checkbox(
|
|
1234
|
+
"Select custom files to restore:",
|
|
1235
|
+
choices=choices_legacy,
|
|
1236
|
+
).ask()
|
|
1237
|
+
|
|
1238
|
+
# Parse results
|
|
1239
|
+
result_commands = []
|
|
1240
|
+
result_agents = []
|
|
1241
|
+
result_hooks = []
|
|
1242
|
+
|
|
1243
|
+
if selected:
|
|
1244
|
+
for item in selected:
|
|
1245
|
+
if item.startswith("cmd:"):
|
|
1246
|
+
result_commands.append(item[4:])
|
|
1247
|
+
elif item.startswith("agent:"):
|
|
1248
|
+
result_agents.append(item[6:])
|
|
1249
|
+
elif item.startswith("hook:"):
|
|
1250
|
+
result_hooks.append(item[5:])
|
|
1251
|
+
|
|
1252
|
+
return {
|
|
1253
|
+
"commands": result_commands,
|
|
1254
|
+
"agents": result_agents,
|
|
1255
|
+
"hooks": result_hooks,
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
|
|
1259
|
+
def _restore_custom_files(
|
|
1260
|
+
project_path: Path,
|
|
1261
|
+
backup_path: Path,
|
|
1262
|
+
selected_commands: list[str],
|
|
1263
|
+
selected_agents: list[str],
|
|
1264
|
+
selected_hooks: list[str],
|
|
1265
|
+
) -> bool:
|
|
1266
|
+
"""Restore selected custom files from backup to project.
|
|
1267
|
+
|
|
1268
|
+
Args:
|
|
1269
|
+
project_path: Project directory path
|
|
1270
|
+
backup_path: Backup directory path
|
|
1271
|
+
selected_commands: List of command files to restore
|
|
1272
|
+
selected_agents: List of agent files to restore
|
|
1273
|
+
selected_hooks: List of hook files to restore
|
|
1274
|
+
|
|
1275
|
+
Returns:
|
|
1276
|
+
True if all restorations succeeded, False otherwise.
|
|
1277
|
+
"""
|
|
1278
|
+
import shutil
|
|
1279
|
+
|
|
1280
|
+
success = True
|
|
1281
|
+
|
|
1282
|
+
# Restore commands
|
|
1283
|
+
if selected_commands:
|
|
1284
|
+
commands_dst = project_path / ".claude" / "commands" / "moai"
|
|
1285
|
+
commands_dst.mkdir(parents=True, exist_ok=True)
|
|
1286
|
+
|
|
1287
|
+
for cmd_file in selected_commands:
|
|
1288
|
+
src = backup_path / ".claude" / "commands" / "moai" / cmd_file
|
|
1289
|
+
dst = commands_dst / cmd_file
|
|
1290
|
+
|
|
1291
|
+
if src.exists():
|
|
1292
|
+
try:
|
|
1293
|
+
shutil.copy2(src, dst)
|
|
1294
|
+
except Exception as e:
|
|
1295
|
+
logger.warning(f"Failed to restore command {cmd_file}: {e}")
|
|
1296
|
+
success = False
|
|
1297
|
+
else:
|
|
1298
|
+
logger.warning(f"Command file not in backup: {cmd_file}")
|
|
1299
|
+
success = False
|
|
1300
|
+
|
|
1301
|
+
# Restore agents
|
|
1302
|
+
if selected_agents:
|
|
1303
|
+
agents_dst = project_path / ".claude" / "agents"
|
|
1304
|
+
agents_dst.mkdir(parents=True, exist_ok=True)
|
|
1305
|
+
|
|
1306
|
+
for agent_file in selected_agents:
|
|
1307
|
+
src = backup_path / ".claude" / "agents" / agent_file
|
|
1308
|
+
dst = agents_dst / agent_file
|
|
1309
|
+
|
|
1310
|
+
if src.exists():
|
|
1311
|
+
try:
|
|
1312
|
+
shutil.copy2(src, dst)
|
|
1313
|
+
except Exception as e:
|
|
1314
|
+
logger.warning(f"Failed to restore agent {agent_file}: {e}")
|
|
1315
|
+
success = False
|
|
1316
|
+
else:
|
|
1317
|
+
logger.warning(f"Agent file not in backup: {agent_file}")
|
|
1318
|
+
success = False
|
|
1319
|
+
|
|
1320
|
+
# Restore hooks
|
|
1321
|
+
if selected_hooks:
|
|
1322
|
+
hooks_dst = project_path / ".claude" / "hooks" / "moai"
|
|
1323
|
+
hooks_dst.mkdir(parents=True, exist_ok=True)
|
|
1324
|
+
|
|
1325
|
+
for hook_file in selected_hooks:
|
|
1326
|
+
src = backup_path / ".claude" / "hooks" / "moai" / hook_file
|
|
1327
|
+
dst = hooks_dst / hook_file
|
|
1328
|
+
|
|
1329
|
+
if src.exists():
|
|
1330
|
+
try:
|
|
1331
|
+
shutil.copy2(src, dst)
|
|
1332
|
+
except Exception as e:
|
|
1333
|
+
logger.warning(f"Failed to restore hook {hook_file}: {e}")
|
|
1334
|
+
success = False
|
|
1335
|
+
else:
|
|
1336
|
+
logger.warning(f"Hook file not in backup: {hook_file}")
|
|
1337
|
+
success = False
|
|
1338
|
+
|
|
1339
|
+
return success
|
|
1340
|
+
|
|
1341
|
+
|
|
1342
|
+
def _detect_custom_skills(project_path: Path, template_skills: set[str]) -> list[str]:
|
|
1343
|
+
"""Detect skills NOT in template (user-created).
|
|
1344
|
+
|
|
1345
|
+
Args:
|
|
1346
|
+
project_path: Project path (absolute)
|
|
1347
|
+
template_skills: Set of template skill names
|
|
1348
|
+
|
|
1349
|
+
Returns:
|
|
1350
|
+
Sorted list of custom skill names.
|
|
1351
|
+
"""
|
|
1352
|
+
skills_path = project_path / ".claude" / "skills"
|
|
1353
|
+
|
|
1354
|
+
if not skills_path.exists():
|
|
1355
|
+
return []
|
|
1356
|
+
|
|
1357
|
+
project_skills = {d.name for d in skills_path.iterdir() if d.is_dir()}
|
|
1358
|
+
custom_skills = project_skills - template_skills
|
|
1359
|
+
|
|
1360
|
+
return sorted(custom_skills)
|
|
1361
|
+
|
|
1362
|
+
|
|
1363
|
+
def _prompt_skill_restore(custom_skills: list[str], yes: bool = False) -> list[str]:
|
|
1364
|
+
"""Interactive fuzzy checkbox for skill restore with search support.
|
|
1365
|
+
|
|
1366
|
+
Args:
|
|
1367
|
+
custom_skills: List of custom skill names
|
|
1368
|
+
yes: Auto-confirm flag (skips restoration in CI/CD mode)
|
|
1369
|
+
|
|
1370
|
+
Returns:
|
|
1371
|
+
List of skills user selected to restore.
|
|
1372
|
+
"""
|
|
1373
|
+
if not custom_skills:
|
|
1374
|
+
return []
|
|
1375
|
+
|
|
1376
|
+
console.print("\n[#DA7756]๐ฆ Custom skills detected in backup:[/#DA7756]")
|
|
1377
|
+
for skill in custom_skills:
|
|
1378
|
+
console.print(f" โข {skill}")
|
|
1379
|
+
console.print()
|
|
1380
|
+
|
|
1381
|
+
if yes:
|
|
1382
|
+
console.print("[dim] Skipping restoration (--yes mode)[/dim]\n")
|
|
1383
|
+
return []
|
|
1384
|
+
|
|
1385
|
+
# Try new UI, fallback to questionary
|
|
1386
|
+
try:
|
|
1387
|
+
from moai_adk.cli.ui.prompts import fuzzy_checkbox
|
|
1388
|
+
|
|
1389
|
+
choices = [{"name": skill, "value": skill} for skill in custom_skills]
|
|
1390
|
+
|
|
1391
|
+
selected = fuzzy_checkbox(
|
|
1392
|
+
"Select skills to restore (type to search):",
|
|
1393
|
+
choices=choices,
|
|
1394
|
+
instruction="[Space] Toggle [Tab] All [Enter] Confirm [Type to search]",
|
|
1395
|
+
)
|
|
1396
|
+
|
|
1397
|
+
except ImportError:
|
|
1398
|
+
import questionary
|
|
1399
|
+
|
|
1400
|
+
selected = questionary.checkbox(
|
|
1401
|
+
"Select skills to restore (none selected by default):",
|
|
1402
|
+
choices=[questionary.Choice(title=skill, checked=False) for skill in custom_skills],
|
|
1403
|
+
).ask()
|
|
1404
|
+
|
|
1405
|
+
return selected if selected else []
|
|
1406
|
+
|
|
1407
|
+
|
|
1408
|
+
def _restore_selected_skills(skills: list[str], backup_path: Path, project_path: Path) -> bool:
|
|
1409
|
+
"""Restore selected skills from backup.
|
|
1410
|
+
|
|
1411
|
+
Args:
|
|
1412
|
+
skills: List of skill names to restore
|
|
1413
|
+
backup_path: Backup directory path
|
|
1414
|
+
project_path: Project path (absolute)
|
|
1415
|
+
|
|
1416
|
+
Returns:
|
|
1417
|
+
True if all restorations succeeded.
|
|
1418
|
+
"""
|
|
1419
|
+
import shutil
|
|
1420
|
+
|
|
1421
|
+
if not skills:
|
|
1422
|
+
return True
|
|
1423
|
+
|
|
1424
|
+
console.print("\n[cyan]๐ฅ Restoring selected skills...[/cyan]")
|
|
1425
|
+
skills_dst = project_path / ".claude" / "skills"
|
|
1426
|
+
skills_dst.mkdir(parents=True, exist_ok=True)
|
|
1427
|
+
|
|
1428
|
+
success = True
|
|
1429
|
+
for skill_name in skills:
|
|
1430
|
+
src = backup_path / ".claude" / "skills" / skill_name
|
|
1431
|
+
dst = skills_dst / skill_name
|
|
1432
|
+
|
|
1433
|
+
if src.exists():
|
|
1434
|
+
try:
|
|
1435
|
+
shutil.copytree(src, dst, dirs_exist_ok=True)
|
|
1436
|
+
console.print(f" [green]โ Restored: {skill_name}[/green]")
|
|
1437
|
+
except Exception as e:
|
|
1438
|
+
console.print(f" [red]โ Failed: {skill_name} - {e}[/red]")
|
|
1439
|
+
success = False
|
|
1440
|
+
else:
|
|
1441
|
+
console.print(f" [yellow]โ Not in backup: {skill_name}[/yellow]")
|
|
1442
|
+
success = False
|
|
1443
|
+
|
|
1444
|
+
return success
|
|
1445
|
+
|
|
1446
|
+
|
|
1447
|
+
def _show_post_update_guidance(backup_path: Path) -> None:
|
|
1448
|
+
"""Show post-update guidance for running /moai:0-project update.
|
|
1449
|
+
|
|
1450
|
+
Args:
|
|
1451
|
+
backup_path: Backup directory path for reference
|
|
1452
|
+
"""
|
|
1453
|
+
console.print("\n" + "[cyan]" + "=" * 60 + "[/cyan]")
|
|
1454
|
+
console.print("[green]โ
Update complete![/green]")
|
|
1455
|
+
console.print("\n[yellow]๐ IMPORTANT - Next step:[/yellow]")
|
|
1456
|
+
console.print(" Run [cyan]/moai:0-project update[/cyan] in Claude Code")
|
|
1457
|
+
console.print("\n This will:")
|
|
1458
|
+
console.print(" โข Merge your settings with new templates")
|
|
1459
|
+
console.print(" โข Validate configuration compatibility")
|
|
1460
|
+
console.print("\n[dim]๐ก Personal instructions should go in CLAUDE.local.md[/dim]")
|
|
1461
|
+
console.print(f"[dim]๐ Backup location: {backup_path}[/dim]")
|
|
1462
|
+
console.print("[cyan]" + "=" * 60 + "[/cyan]\n")
|
|
1463
|
+
|
|
1464
|
+
|
|
1465
|
+
def _sync_templates(project_path: Path, force: bool = False, yes: bool = False) -> bool:
|
|
1466
|
+
"""Sync templates to project with rollback mechanism.
|
|
520
1467
|
|
|
521
1468
|
Args:
|
|
522
1469
|
project_path: Project path (absolute)
|
|
523
1470
|
force: Force update without backup
|
|
1471
|
+
yes: Auto-confirm flag (skips interactive prompts)
|
|
524
1472
|
|
|
525
1473
|
Returns:
|
|
526
1474
|
True if sync succeeded, False otherwise
|
|
527
1475
|
"""
|
|
1476
|
+
from moai_adk.core.template.backup import TemplateBackup
|
|
1477
|
+
|
|
1478
|
+
backup_path = None
|
|
528
1479
|
try:
|
|
1480
|
+
# NEW: Detect custom files and skills BEFORE backup/sync
|
|
1481
|
+
template_skills = _get_template_skill_names()
|
|
1482
|
+
_detect_custom_skills(project_path, template_skills)
|
|
1483
|
+
|
|
1484
|
+
# Detect custom commands, agents, and hooks
|
|
1485
|
+
template_commands = _get_template_command_names()
|
|
1486
|
+
_detect_custom_commands(project_path, template_commands)
|
|
1487
|
+
|
|
1488
|
+
template_agents = _get_template_agent_names()
|
|
1489
|
+
_detect_custom_agents(project_path, template_agents)
|
|
1490
|
+
|
|
1491
|
+
template_hooks = _get_template_hook_names()
|
|
1492
|
+
_detect_custom_hooks(project_path, template_hooks)
|
|
1493
|
+
|
|
529
1494
|
processor = TemplateProcessor(project_path)
|
|
530
1495
|
|
|
1496
|
+
# Create pre-sync backup for rollback
|
|
1497
|
+
if not force:
|
|
1498
|
+
backup = TemplateBackup(project_path)
|
|
1499
|
+
if backup.has_existing_files():
|
|
1500
|
+
backup_path = backup.create_backup()
|
|
1501
|
+
console.print(f"๐พ Created backup: {backup_path.name}")
|
|
1502
|
+
|
|
1503
|
+
# Merge analysis using Claude Code headless mode
|
|
1504
|
+
try:
|
|
1505
|
+
analyzer = MergeAnalyzer(project_path)
|
|
1506
|
+
# Template source path from installed package
|
|
1507
|
+
template_path = Path(__file__).parent.parent.parent / "templates"
|
|
1508
|
+
|
|
1509
|
+
console.print("\n[cyan]๐ Starting merge analysis (max 2 mins)...[/cyan]")
|
|
1510
|
+
console.print("[dim] Analyzing intelligent merge with Claude Code.[/dim]")
|
|
1511
|
+
console.print("[dim] Please wait...[/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
|
+
|
|
531
1523
|
# Load existing config
|
|
532
1524
|
existing_config = _load_existing_config(project_path)
|
|
533
1525
|
|
|
@@ -536,9 +1528,37 @@ def _sync_templates(project_path: Path, force: bool = False) -> bool:
|
|
|
536
1528
|
if context:
|
|
537
1529
|
processor.set_context(context)
|
|
538
1530
|
|
|
539
|
-
# Copy templates
|
|
1531
|
+
# Copy templates (including moai folder)
|
|
540
1532
|
processor.copy_templates(backup=False, silent=True)
|
|
541
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
|
+
|
|
1554
|
+
# Validate template substitution
|
|
1555
|
+
validation_passed = _validate_template_substitution_with_rollback(project_path, backup_path)
|
|
1556
|
+
if not validation_passed:
|
|
1557
|
+
if backup_path:
|
|
1558
|
+
console.print(f"[yellow]๐ Rolling back to backup: {backup_path.name}[/yellow]")
|
|
1559
|
+
backup.restore_backup(backup_path)
|
|
1560
|
+
return False
|
|
1561
|
+
|
|
542
1562
|
# Preserve metadata
|
|
543
1563
|
_preserve_project_metadata(project_path, context, existing_config, __version__)
|
|
544
1564
|
_apply_context_to_file(processor, project_path / "CLAUDE.md")
|
|
@@ -546,8 +1566,56 @@ def _sync_templates(project_path: Path, force: bool = False) -> bool:
|
|
|
546
1566
|
# Set optimized=false
|
|
547
1567
|
set_optimized_false(project_path)
|
|
548
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
|
+
|
|
549
1608
|
return True
|
|
550
|
-
except Exception:
|
|
1609
|
+
except Exception as e:
|
|
1610
|
+
console.print(f"[red]โ Template sync failed: {e}[/red]")
|
|
1611
|
+
if backup_path:
|
|
1612
|
+
console.print(f"[yellow]๐ Rolling back to backup: {backup_path.name}[/yellow]")
|
|
1613
|
+
try:
|
|
1614
|
+
backup = TemplateBackup(project_path)
|
|
1615
|
+
backup.restore_backup(backup_path)
|
|
1616
|
+
console.print("[green]โ
Rollback completed[/green]")
|
|
1617
|
+
except Exception as rollback_error:
|
|
1618
|
+
console.print(f"[red]โ Rollback failed: {rollback_error}[/red]")
|
|
551
1619
|
return False
|
|
552
1620
|
|
|
553
1621
|
|
|
@@ -568,32 +1636,32 @@ def get_latest_version() -> str | None:
|
|
|
568
1636
|
|
|
569
1637
|
|
|
570
1638
|
def set_optimized_false(project_path: Path) -> None:
|
|
571
|
-
"""Set config
|
|
1639
|
+
"""Set config's optimized field to false.
|
|
572
1640
|
|
|
573
1641
|
Args:
|
|
574
1642
|
project_path: Project path (absolute).
|
|
575
1643
|
"""
|
|
576
|
-
config_path = project_path
|
|
1644
|
+
config_path, _ = _get_config_path(project_path)
|
|
577
1645
|
if not config_path.exists():
|
|
578
1646
|
return
|
|
579
1647
|
|
|
580
1648
|
try:
|
|
581
|
-
config_data =
|
|
1649
|
+
config_data = _load_config(config_path)
|
|
582
1650
|
config_data.setdefault("project", {})["optimized"] = False
|
|
583
|
-
config_path
|
|
584
|
-
except (json.JSONDecodeError, KeyError):
|
|
585
|
-
# Ignore errors if config
|
|
1651
|
+
_save_config(config_path, config_data)
|
|
1652
|
+
except (json.JSONDecodeError, yaml.YAMLError, KeyError):
|
|
1653
|
+
# Ignore errors if config is invalid
|
|
586
1654
|
pass
|
|
587
1655
|
|
|
588
1656
|
|
|
589
1657
|
def _load_existing_config(project_path: Path) -> dict[str, Any]:
|
|
590
|
-
"""Load existing config
|
|
591
|
-
config_path = project_path
|
|
1658
|
+
"""Load existing config (YAML or JSON) if available."""
|
|
1659
|
+
config_path, _ = _get_config_path(project_path)
|
|
592
1660
|
if config_path.exists():
|
|
593
1661
|
try:
|
|
594
|
-
return
|
|
595
|
-
except json.JSONDecodeError:
|
|
596
|
-
console.print("[yellow]โ Existing config
|
|
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]")
|
|
597
1665
|
return {}
|
|
598
1666
|
|
|
599
1667
|
|
|
@@ -631,6 +1699,8 @@ def _build_template_context(
|
|
|
631
1699
|
version_for_config: str,
|
|
632
1700
|
) -> dict[str, str]:
|
|
633
1701
|
"""Build substitution context for template files."""
|
|
1702
|
+
import platform
|
|
1703
|
+
|
|
634
1704
|
project_section = _extract_project_section(existing_config)
|
|
635
1705
|
|
|
636
1706
|
project_name = _coalesce(
|
|
@@ -660,13 +1730,106 @@ def _build_template_context(
|
|
|
660
1730
|
default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
|
661
1731
|
)
|
|
662
1732
|
|
|
1733
|
+
# Detect OS for cross-platform Hook path configuration
|
|
1734
|
+
hook_project_dir = "%CLAUDE_PROJECT_DIR%" if platform.system() == "Windows" else "$CLAUDE_PROJECT_DIR"
|
|
1735
|
+
|
|
1736
|
+
# Extract and resolve language configuration using centralized resolver
|
|
1737
|
+
try:
|
|
1738
|
+
from moai_adk.core.language_config_resolver import get_resolver
|
|
1739
|
+
|
|
1740
|
+
# Use language resolver to get complete configuration
|
|
1741
|
+
resolver = get_resolver(str(project_path))
|
|
1742
|
+
resolved_config = resolver.resolve_config()
|
|
1743
|
+
|
|
1744
|
+
# Extract language configuration with environment variable priority
|
|
1745
|
+
language_config = {
|
|
1746
|
+
"conversation_language": resolved_config.get("conversation_language", "en"),
|
|
1747
|
+
"conversation_language_name": resolved_config.get("conversation_language_name", "English"),
|
|
1748
|
+
"agent_prompt_language": resolved_config.get("agent_prompt_language", "en"),
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1751
|
+
# Extract user personalization
|
|
1752
|
+
user_name = resolved_config.get("user_name", "")
|
|
1753
|
+
personalized_greeting = resolver.get_personalized_greeting(resolved_config)
|
|
1754
|
+
config_source = resolved_config.get("config_source", "config_file")
|
|
1755
|
+
|
|
1756
|
+
except ImportError:
|
|
1757
|
+
# Fallback to basic language config extraction if resolver not available
|
|
1758
|
+
language_config = existing_config.get("language", {})
|
|
1759
|
+
if not isinstance(language_config, dict):
|
|
1760
|
+
language_config = {}
|
|
1761
|
+
|
|
1762
|
+
user_name = existing_config.get("user", {}).get("name", "")
|
|
1763
|
+
conv_lang = language_config.get("conversation_language")
|
|
1764
|
+
personalized_greeting = f"{user_name}๋" if user_name and conv_lang == "ko" else user_name
|
|
1765
|
+
config_source = "config_file"
|
|
1766
|
+
|
|
1767
|
+
# Enhanced version formatting (matches TemplateProcessor.get_enhanced_version_context)
|
|
1768
|
+
def format_short_version(v: str) -> str:
|
|
1769
|
+
"""Remove 'v' prefix if present."""
|
|
1770
|
+
return v[1:] if v.startswith("v") else v
|
|
1771
|
+
|
|
1772
|
+
def format_display_version(v: str) -> str:
|
|
1773
|
+
"""Format display version with proper formatting."""
|
|
1774
|
+
if v == "unknown":
|
|
1775
|
+
return "MoAI-ADK unknown version"
|
|
1776
|
+
elif v.startswith("v"):
|
|
1777
|
+
return f"MoAI-ADK {v}"
|
|
1778
|
+
else:
|
|
1779
|
+
return f"MoAI-ADK v{v}"
|
|
1780
|
+
|
|
1781
|
+
def format_trimmed_version(v: str, max_length: int = 10) -> str:
|
|
1782
|
+
"""Format version with maximum length for UI displays."""
|
|
1783
|
+
if v == "unknown":
|
|
1784
|
+
return "unknown"
|
|
1785
|
+
clean_version = v[1:] if v.startswith("v") else v
|
|
1786
|
+
if len(clean_version) > max_length:
|
|
1787
|
+
return clean_version[:max_length]
|
|
1788
|
+
return clean_version
|
|
1789
|
+
|
|
1790
|
+
def format_semver_version(v: str) -> str:
|
|
1791
|
+
"""Format version as semantic version."""
|
|
1792
|
+
if v == "unknown":
|
|
1793
|
+
return "0.0.0"
|
|
1794
|
+
clean_version = v[1:] if v.startswith("v") else v
|
|
1795
|
+
import re
|
|
1796
|
+
|
|
1797
|
+
semver_match = re.match(r"^(\d+\.\d+\.\d+)", clean_version)
|
|
1798
|
+
if semver_match:
|
|
1799
|
+
return semver_match.group(1)
|
|
1800
|
+
return "0.0.0"
|
|
1801
|
+
|
|
663
1802
|
return {
|
|
664
1803
|
"MOAI_VERSION": version_for_config,
|
|
1804
|
+
"MOAI_VERSION_SHORT": format_short_version(version_for_config),
|
|
1805
|
+
"MOAI_VERSION_DISPLAY": format_display_version(version_for_config),
|
|
1806
|
+
"MOAI_VERSION_TRIMMED": format_trimmed_version(version_for_config),
|
|
1807
|
+
"MOAI_VERSION_SEMVER": format_semver_version(version_for_config),
|
|
1808
|
+
"MOAI_VERSION_VALID": "true" if version_for_config != "unknown" else "false",
|
|
1809
|
+
"MOAI_VERSION_SOURCE": "config_cached",
|
|
665
1810
|
"PROJECT_NAME": project_name,
|
|
666
1811
|
"PROJECT_MODE": project_mode,
|
|
667
1812
|
"PROJECT_DESCRIPTION": project_description,
|
|
668
1813
|
"PROJECT_VERSION": project_version,
|
|
669
1814
|
"CREATION_TIMESTAMP": created_at,
|
|
1815
|
+
"PROJECT_DIR": hook_project_dir,
|
|
1816
|
+
"CONVERSATION_LANGUAGE": language_config.get("conversation_language", "en"),
|
|
1817
|
+
"CONVERSATION_LANGUAGE_NAME": language_config.get("conversation_language_name", "English"),
|
|
1818
|
+
"AGENT_PROMPT_LANGUAGE": language_config.get("agent_prompt_language", "en"),
|
|
1819
|
+
"GIT_COMMIT_MESSAGES_LANGUAGE": language_config.get("git_commit_messages", "en"),
|
|
1820
|
+
"CODE_COMMENTS_LANGUAGE": language_config.get("code_comments", "en"),
|
|
1821
|
+
"DOCUMENTATION_LANGUAGE": language_config.get(
|
|
1822
|
+
"documentation", language_config.get("conversation_language", "en")
|
|
1823
|
+
),
|
|
1824
|
+
"ERROR_MESSAGES_LANGUAGE": language_config.get(
|
|
1825
|
+
"error_messages", language_config.get("conversation_language", "en")
|
|
1826
|
+
),
|
|
1827
|
+
"USER_NAME": user_name,
|
|
1828
|
+
"PERSONALIZED_GREETING": personalized_greeting,
|
|
1829
|
+
"LANGUAGE_CONFIG_SOURCE": config_source,
|
|
1830
|
+
"CODEBASE_LANGUAGE": project_section.get("language", "generic"),
|
|
1831
|
+
"PROJECT_OWNER": project_section.get("author", "@user"),
|
|
1832
|
+
"AUTHOR": project_section.get("author", "@user"),
|
|
670
1833
|
}
|
|
671
1834
|
|
|
672
1835
|
|
|
@@ -676,18 +1839,18 @@ def _preserve_project_metadata(
|
|
|
676
1839
|
existing_config: dict[str, Any],
|
|
677
1840
|
version_for_config: str,
|
|
678
1841
|
) -> None:
|
|
679
|
-
"""Restore project-specific metadata in the new config.
|
|
1842
|
+
"""Restore project-specific metadata in the new config (YAML or JSON).
|
|
680
1843
|
|
|
681
1844
|
Also updates template_version to track which template version is synchronized.
|
|
682
1845
|
"""
|
|
683
|
-
config_path = project_path
|
|
1846
|
+
config_path, _ = _get_config_path(project_path)
|
|
684
1847
|
if not config_path.exists():
|
|
685
1848
|
return
|
|
686
1849
|
|
|
687
1850
|
try:
|
|
688
|
-
config_data =
|
|
689
|
-
except json.JSONDecodeError:
|
|
690
|
-
console.print("[red]โ Failed to parse config
|
|
1851
|
+
config_data = _load_config(config_path)
|
|
1852
|
+
except (json.JSONDecodeError, yaml.YAMLError):
|
|
1853
|
+
console.print("[red]โ Failed to parse config after template copy[/red]")
|
|
691
1854
|
return
|
|
692
1855
|
|
|
693
1856
|
project_data = config_data.setdefault("project", {})
|
|
@@ -714,11 +1877,10 @@ def _preserve_project_metadata(
|
|
|
714
1877
|
config_data.setdefault("moai", {})
|
|
715
1878
|
config_data["moai"]["version"] = version_for_config
|
|
716
1879
|
|
|
717
|
-
# @CODE:UPDATE-REFACTOR-002-008: Update template_version to track sync status
|
|
718
1880
|
# This allows Stage 2 to compare package vs project template versions
|
|
719
1881
|
project_data["template_version"] = version_for_config
|
|
720
1882
|
|
|
721
|
-
config_path
|
|
1883
|
+
_save_config(config_path, config_data)
|
|
722
1884
|
|
|
723
1885
|
|
|
724
1886
|
def _apply_context_to_file(processor: TemplateProcessor, target_path: Path) -> None:
|
|
@@ -740,7 +1902,88 @@ def _apply_context_to_file(processor: TemplateProcessor, target_path: Path) -> N
|
|
|
740
1902
|
target_path.write_text(substituted, encoding="utf-8")
|
|
741
1903
|
|
|
742
1904
|
|
|
743
|
-
|
|
1905
|
+
def _validate_template_substitution(project_path: Path) -> None:
|
|
1906
|
+
"""Validate that all template variables have been properly substituted."""
|
|
1907
|
+
import re
|
|
1908
|
+
|
|
1909
|
+
# Files to check for unsubstituted variables
|
|
1910
|
+
files_to_check = [
|
|
1911
|
+
project_path / ".claude" / "settings.json",
|
|
1912
|
+
project_path / "CLAUDE.md",
|
|
1913
|
+
]
|
|
1914
|
+
|
|
1915
|
+
issues_found = []
|
|
1916
|
+
|
|
1917
|
+
for file_path in files_to_check:
|
|
1918
|
+
if not file_path.exists():
|
|
1919
|
+
continue
|
|
1920
|
+
|
|
1921
|
+
try:
|
|
1922
|
+
content = file_path.read_text(encoding="utf-8")
|
|
1923
|
+
# Look for unsubstituted template variables
|
|
1924
|
+
unsubstituted = re.findall(r"\{\{([A-Z_]+)\}\}", content)
|
|
1925
|
+
if unsubstituted:
|
|
1926
|
+
unique_vars = sorted(set(unsubstituted))
|
|
1927
|
+
issues_found.append(f"{file_path.relative_to(project_path)}: {', '.join(unique_vars)}")
|
|
1928
|
+
except Exception as e:
|
|
1929
|
+
console.print(f"[yellow]โ ๏ธ Could not validate {file_path.relative_to(project_path)}: {e}[/yellow]")
|
|
1930
|
+
|
|
1931
|
+
if issues_found:
|
|
1932
|
+
console.print("[red]โ Template substitution validation failed:[/red]")
|
|
1933
|
+
for issue in issues_found:
|
|
1934
|
+
console.print(f" {issue}")
|
|
1935
|
+
console.print("[yellow]๐ก Run '/moai:0-project' to fix template variables[/yellow]")
|
|
1936
|
+
else:
|
|
1937
|
+
console.print("[green]โ
Template substitution validation passed[/green]")
|
|
1938
|
+
|
|
1939
|
+
|
|
1940
|
+
def _validate_template_substitution_with_rollback(project_path: Path, backup_path: Path | None) -> bool:
|
|
1941
|
+
"""Validate template substitution with rollback capability.
|
|
1942
|
+
|
|
1943
|
+
Returns:
|
|
1944
|
+
True if validation passed, False if failed (rollback handled by caller)
|
|
1945
|
+
"""
|
|
1946
|
+
import re
|
|
1947
|
+
|
|
1948
|
+
# Files to check for unsubstituted variables
|
|
1949
|
+
files_to_check = [
|
|
1950
|
+
project_path / ".claude" / "settings.json",
|
|
1951
|
+
project_path / "CLAUDE.md",
|
|
1952
|
+
]
|
|
1953
|
+
|
|
1954
|
+
issues_found = []
|
|
1955
|
+
|
|
1956
|
+
for file_path in files_to_check:
|
|
1957
|
+
if not file_path.exists():
|
|
1958
|
+
continue
|
|
1959
|
+
|
|
1960
|
+
try:
|
|
1961
|
+
content = file_path.read_text(encoding="utf-8")
|
|
1962
|
+
# Look for unsubstituted template variables
|
|
1963
|
+
unsubstituted = re.findall(r"\{\{([A-Z_]+)\}\}", content)
|
|
1964
|
+
if unsubstituted:
|
|
1965
|
+
unique_vars = sorted(set(unsubstituted))
|
|
1966
|
+
issues_found.append(f"{file_path.relative_to(project_path)}: {', '.join(unique_vars)}")
|
|
1967
|
+
except Exception as e:
|
|
1968
|
+
console.print(f"[yellow]โ ๏ธ Could not validate {file_path.relative_to(project_path)}: {e}[/yellow]")
|
|
1969
|
+
|
|
1970
|
+
if issues_found:
|
|
1971
|
+
console.print("[red]โ Template substitution validation failed:[/red]")
|
|
1972
|
+
for issue in issues_found:
|
|
1973
|
+
console.print(f" {issue}")
|
|
1974
|
+
|
|
1975
|
+
if backup_path:
|
|
1976
|
+
console.print("[yellow]๐ Rolling back due to validation failure...[/yellow]")
|
|
1977
|
+
else:
|
|
1978
|
+
console.print("[yellow]๐ก Run '/moai:0-project' to fix template variables[/yellow]")
|
|
1979
|
+
console.print("[red]โ ๏ธ No backup available - manual fix required[/red]")
|
|
1980
|
+
|
|
1981
|
+
return False
|
|
1982
|
+
else:
|
|
1983
|
+
console.print("[green]โ
Template substitution validation passed[/green]")
|
|
1984
|
+
return True
|
|
1985
|
+
|
|
1986
|
+
|
|
744
1987
|
def _show_version_info(current: str, latest: str) -> None:
|
|
745
1988
|
"""Display version information.
|
|
746
1989
|
|
|
@@ -753,7 +1996,6 @@ def _show_version_info(current: str, latest: str) -> None:
|
|
|
753
1996
|
console.print(f" Latest version: {latest}")
|
|
754
1997
|
|
|
755
1998
|
|
|
756
|
-
# @CODE:UPDATE-REFACTOR-002-005
|
|
757
1999
|
def _show_installer_not_found_help() -> None:
|
|
758
2000
|
"""Show help when installer not found."""
|
|
759
2001
|
console.print("[red]โ Cannot detect package installer[/red]\n")
|
|
@@ -807,14 +2049,94 @@ def _show_timeout_error_help() -> None:
|
|
|
807
2049
|
console.print(" [cyan]moai-adk update --yes --force[/cyan]")
|
|
808
2050
|
|
|
809
2051
|
|
|
2052
|
+
def _execute_migration_if_needed(project_path: Path, yes: bool = False) -> bool:
|
|
2053
|
+
"""Check and execute migration if needed.
|
|
2054
|
+
|
|
2055
|
+
Args:
|
|
2056
|
+
project_path: Project directory path
|
|
2057
|
+
yes: Auto-confirm without prompting
|
|
2058
|
+
|
|
2059
|
+
Returns:
|
|
2060
|
+
True if no migration needed or migration succeeded, False if migration failed
|
|
2061
|
+
"""
|
|
2062
|
+
try:
|
|
2063
|
+
migrator = VersionMigrator(project_path)
|
|
2064
|
+
|
|
2065
|
+
# Check if migration is needed
|
|
2066
|
+
if not migrator.needs_migration():
|
|
2067
|
+
return True
|
|
2068
|
+
|
|
2069
|
+
# Get migration info
|
|
2070
|
+
info = migrator.get_migration_info()
|
|
2071
|
+
console.print("\n[cyan]๐ Migration Required[/cyan]")
|
|
2072
|
+
console.print(f" Current version: {info['current_version']}")
|
|
2073
|
+
console.print(f" Target version: {info['target_version']}")
|
|
2074
|
+
console.print(f" Files to migrate: {info['file_count']}")
|
|
2075
|
+
console.print()
|
|
2076
|
+
console.print(" This will migrate configuration files to new locations:")
|
|
2077
|
+
console.print(" โข .moai/config.json โ .moai/config/config.json")
|
|
2078
|
+
console.print(" โข .claude/statusline-config.yaml โ .moai/config/statusline-config.yaml")
|
|
2079
|
+
console.print()
|
|
2080
|
+
console.print(" A backup will be created automatically.")
|
|
2081
|
+
console.print()
|
|
2082
|
+
|
|
2083
|
+
# Confirm with user (unless --yes)
|
|
2084
|
+
if not yes:
|
|
2085
|
+
if not click.confirm("Do you want to proceed with migration?", default=True):
|
|
2086
|
+
console.print("[yellow]โ ๏ธ Migration skipped. Some features may not work correctly.[/yellow]")
|
|
2087
|
+
console.print("[cyan]๐ก Run 'moai-adk migrate' manually when ready[/cyan]")
|
|
2088
|
+
return False
|
|
2089
|
+
|
|
2090
|
+
# Execute migration
|
|
2091
|
+
console.print("[cyan]๐ Starting migration...[/cyan]")
|
|
2092
|
+
success = migrator.migrate_to_v024(dry_run=False, cleanup=True)
|
|
2093
|
+
|
|
2094
|
+
if success:
|
|
2095
|
+
console.print("[green]โ
Migration completed successfully![/green]")
|
|
2096
|
+
return True
|
|
2097
|
+
else:
|
|
2098
|
+
console.print("[red]โ Migration failed[/red]")
|
|
2099
|
+
console.print("[cyan]๐ก Use 'moai-adk migrate --rollback' to restore from backup[/cyan]")
|
|
2100
|
+
return False
|
|
2101
|
+
|
|
2102
|
+
except Exception as e:
|
|
2103
|
+
console.print(f"[red]โ Migration error: {e}[/red]")
|
|
2104
|
+
logger.error(f"Migration failed: {e}", exc_info=True)
|
|
2105
|
+
return False
|
|
2106
|
+
|
|
2107
|
+
|
|
810
2108
|
@click.command()
|
|
811
|
-
@click.option(
|
|
2109
|
+
@click.option(
|
|
2110
|
+
"--path",
|
|
2111
|
+
type=click.Path(exists=True),
|
|
2112
|
+
default=".",
|
|
2113
|
+
help="Project path (default: current directory)",
|
|
2114
|
+
)
|
|
812
2115
|
@click.option("--force", is_flag=True, help="Skip backup and force the update")
|
|
813
2116
|
@click.option("--check", is_flag=True, help="Only check version (do not update)")
|
|
814
2117
|
@click.option("--templates-only", is_flag=True, help="Skip package upgrade, sync templates only")
|
|
815
2118
|
@click.option("--yes", is_flag=True, help="Auto-confirm all prompts (CI/CD mode)")
|
|
816
|
-
|
|
817
|
-
""
|
|
2119
|
+
@click.option(
|
|
2120
|
+
"--merge",
|
|
2121
|
+
"merge_strategy",
|
|
2122
|
+
flag_value="auto",
|
|
2123
|
+
help="Auto-merge: Apply template + preserve user changes",
|
|
2124
|
+
)
|
|
2125
|
+
@click.option(
|
|
2126
|
+
"--manual",
|
|
2127
|
+
"merge_strategy",
|
|
2128
|
+
flag_value="manual",
|
|
2129
|
+
help="Manual merge: Preserve backup, generate merge guide",
|
|
2130
|
+
)
|
|
2131
|
+
def update(
|
|
2132
|
+
path: str,
|
|
2133
|
+
force: bool,
|
|
2134
|
+
check: bool,
|
|
2135
|
+
templates_only: bool,
|
|
2136
|
+
yes: bool,
|
|
2137
|
+
merge_strategy: str | None,
|
|
2138
|
+
) -> None:
|
|
2139
|
+
"""Update command with 3-stage workflow + merge strategy selection (v0.26.0+).
|
|
818
2140
|
|
|
819
2141
|
Stage 1 (Package Version Check):
|
|
820
2142
|
- Fetches current and latest versions from PyPI
|
|
@@ -826,18 +2148,34 @@ def update(path: str, force: bool, check: bool, templates_only: bool, yes: bool)
|
|
|
826
2148
|
- If versions match: skips Stage 3 (already up-to-date)
|
|
827
2149
|
- Performance improvement: 70-80% faster for unchanged projects (3-4s vs 12-18s)
|
|
828
2150
|
|
|
829
|
-
Stage 3 (Template Sync):
|
|
2151
|
+
Stage 3 (Template Sync with Merge Strategy - NEW in v0.26.0):
|
|
830
2152
|
- Syncs templates only if versions differ
|
|
2153
|
+
- User chooses merge strategy:
|
|
2154
|
+
* Auto-merge (default): Template + preserved user changes
|
|
2155
|
+
* Manual merge: Backup + comprehensive merge guide (full control)
|
|
831
2156
|
- Updates .claude/, .moai/, CLAUDE.md, config.json
|
|
832
2157
|
- Preserves specs and reports
|
|
833
2158
|
- Saves new template_version to config.json
|
|
834
2159
|
|
|
835
2160
|
Examples:
|
|
836
|
-
python -m moai_adk update #
|
|
837
|
-
python -m moai_adk update --
|
|
2161
|
+
python -m moai_adk update # interactive merge strategy selection
|
|
2162
|
+
python -m moai_adk update --merge # auto-merge (template + user changes)
|
|
2163
|
+
python -m moai_adk update --manual # manual merge (backup + guide)
|
|
2164
|
+
python -m moai_adk update --force # force template sync (no backup)
|
|
838
2165
|
python -m moai_adk update --check # check version only
|
|
839
2166
|
python -m moai_adk update --templates-only # skip package upgrade
|
|
840
|
-
python -m moai_adk update --yes # CI/CD mode (auto-confirm)
|
|
2167
|
+
python -m moai_adk update --yes # CI/CD mode (auto-confirm + auto-merge)
|
|
2168
|
+
|
|
2169
|
+
Merge Strategies:
|
|
2170
|
+
--merge: Auto-merge applies template + preserves your changes (default)
|
|
2171
|
+
Generated files: backup, merge report
|
|
2172
|
+
--manual: Manual merge preserves backup + generates comprehensive guide
|
|
2173
|
+
Generated files: backup, merge guide
|
|
2174
|
+
|
|
2175
|
+
Generated Files:
|
|
2176
|
+
- Backup: .moai-backups/pre-update-backup_{timestamp}/
|
|
2177
|
+
- Report: .moai/reports/merge-report.md (auto-merge only)
|
|
2178
|
+
- Guide: .moai/guides/merge-guide.md (manual merge only)
|
|
841
2179
|
"""
|
|
842
2180
|
try:
|
|
843
2181
|
project_path = Path(path).resolve()
|
|
@@ -851,8 +2189,20 @@ def update(path: str, force: bool, check: bool, templates_only: bool, yes: bool)
|
|
|
851
2189
|
# Note: If --check is used, always fetch versions even if --templates-only is also present
|
|
852
2190
|
if check or not templates_only:
|
|
853
2191
|
try:
|
|
854
|
-
|
|
855
|
-
|
|
2192
|
+
# Try to use new spinner UI
|
|
2193
|
+
try:
|
|
2194
|
+
from moai_adk.cli.ui.progress import SpinnerContext
|
|
2195
|
+
|
|
2196
|
+
with SpinnerContext("Checking for updates...") as spinner:
|
|
2197
|
+
current = _get_current_version()
|
|
2198
|
+
spinner.update("Fetching latest version from PyPI...")
|
|
2199
|
+
latest = _get_latest_version()
|
|
2200
|
+
spinner.success("Version check complete")
|
|
2201
|
+
except ImportError:
|
|
2202
|
+
# Fallback to simple console output
|
|
2203
|
+
console.print("[dim]Checking for updates...[/dim]")
|
|
2204
|
+
current = _get_current_version()
|
|
2205
|
+
latest = _get_latest_version()
|
|
856
2206
|
except RuntimeError as e:
|
|
857
2207
|
console.print(f"[red]Error: {e}[/red]")
|
|
858
2208
|
if not force:
|
|
@@ -879,8 +2229,13 @@ def update(path: str, force: bool, check: bool, templates_only: bool, yes: bool)
|
|
|
879
2229
|
# Step 2: Handle --templates-only (skip upgrade, go straight to sync)
|
|
880
2230
|
if templates_only:
|
|
881
2231
|
console.print("[cyan]๐ Syncing templates only...[/cyan]")
|
|
2232
|
+
|
|
2233
|
+
# Preserve user-specific settings before sync
|
|
2234
|
+
console.print(" [cyan]๐พ Preserving user settings...[/cyan]")
|
|
2235
|
+
preserved_settings = _preserve_user_settings(project_path)
|
|
2236
|
+
|
|
882
2237
|
try:
|
|
883
|
-
if not _sync_templates(project_path, force):
|
|
2238
|
+
if not _sync_templates(project_path, force, yes):
|
|
884
2239
|
raise TemplateSyncError("Template sync returned False")
|
|
885
2240
|
except TemplateSyncError:
|
|
886
2241
|
console.print("[red]Error: Template sync failed[/red]")
|
|
@@ -891,6 +2246,9 @@ def update(path: str, force: bool, check: bool, templates_only: bool, yes: bool)
|
|
|
891
2246
|
_show_template_sync_failure_help()
|
|
892
2247
|
raise click.Abort()
|
|
893
2248
|
|
|
2249
|
+
# Restore user-specific settings after sync
|
|
2250
|
+
_restore_user_settings(project_path, preserved_settings)
|
|
2251
|
+
|
|
894
2252
|
console.print(" [green]โ
.claude/ update complete[/green]")
|
|
895
2253
|
console.print(" [green]โ
.moai/ update complete (specs/reports preserved)[/green]")
|
|
896
2254
|
console.print(" [green]๐ CLAUDE.md merge complete[/green]")
|
|
@@ -902,7 +2260,6 @@ def update(path: str, force: bool, check: bool, templates_only: bool, yes: bool)
|
|
|
902
2260
|
comparison = _compare_versions(current, latest)
|
|
903
2261
|
|
|
904
2262
|
# Stage 1: Package Upgrade (if current < latest)
|
|
905
|
-
# @CODE:UPDATE-REFACTOR-002-009: Stage 1 - Package version check and upgrade
|
|
906
2263
|
if comparison < 0:
|
|
907
2264
|
console.print(f"\n[cyan]๐ฆ Upgrading: {current} โ {latest}[/cyan]")
|
|
908
2265
|
|
|
@@ -941,10 +2298,20 @@ def update(path: str, force: bool, check: bool, templates_only: bool, yes: bool)
|
|
|
941
2298
|
console.print("[cyan]๐ข Run 'moai-adk update' again to sync templates[/cyan]")
|
|
942
2299
|
return
|
|
943
2300
|
|
|
944
|
-
# Stage
|
|
945
|
-
# @CODE:UPDATE-REFACTOR-002-010: Stage 2 - Compare template versions to determine if sync needed
|
|
2301
|
+
# Stage 1.5: Migration Check (NEW in v0.24.0)
|
|
946
2302
|
console.print(f"โ Package already up to date ({current})")
|
|
947
2303
|
|
|
2304
|
+
# Execute migration if needed
|
|
2305
|
+
if not _execute_migration_if_needed(project_path, yes):
|
|
2306
|
+
console.print("[yellow]โ ๏ธ Update continuing without migration[/yellow]")
|
|
2307
|
+
console.print("[cyan]๐ก Some features may require migration to work correctly[/cyan]")
|
|
2308
|
+
|
|
2309
|
+
# Migrate config.json โ config.yaml (v0.32.0+)
|
|
2310
|
+
console.print("\n[cyan]๐ Checking for config format migration...[/cyan]")
|
|
2311
|
+
if not _migrate_config_json_to_yaml(project_path):
|
|
2312
|
+
console.print("[yellow]โ ๏ธ Config migration failed, continuing with existing format[/yellow]")
|
|
2313
|
+
|
|
2314
|
+
# Stage 2: Config Version Comparison
|
|
948
2315
|
try:
|
|
949
2316
|
package_config_version = _get_package_config_version()
|
|
950
2317
|
project_config_version = _get_project_config_version(project_path)
|
|
@@ -974,9 +2341,52 @@ def update(path: str, force: bool, check: bool, templates_only: bool, yes: bool)
|
|
|
974
2341
|
return
|
|
975
2342
|
|
|
976
2343
|
# Stage 3: Template Sync (Only if package_config_version > project_config_version)
|
|
977
|
-
# @CODE:UPDATE-REFACTOR-002-011: Stage 3 - Template sync only if versions differ
|
|
978
2344
|
console.print(f"\n[cyan]๐ Syncing templates ({project_config_version} โ {package_config_version})...[/cyan]")
|
|
979
2345
|
|
|
2346
|
+
# Determine merge strategy (default: auto-merge)
|
|
2347
|
+
final_merge_strategy = merge_strategy or "auto"
|
|
2348
|
+
|
|
2349
|
+
# Handle merge strategy
|
|
2350
|
+
if final_merge_strategy == "manual":
|
|
2351
|
+
# Manual merge mode: Create full backup + generate guide, no template sync
|
|
2352
|
+
console.print("\n[cyan]๐ Manual merge mode selected[/cyan]")
|
|
2353
|
+
|
|
2354
|
+
# Create full project backup
|
|
2355
|
+
console.print(" [cyan]๐พ Creating full project backup...[/cyan]")
|
|
2356
|
+
try:
|
|
2357
|
+
from moai_adk.core.migration.backup_manager import BackupManager
|
|
2358
|
+
|
|
2359
|
+
backup_manager = BackupManager(project_path)
|
|
2360
|
+
full_backup_path = backup_manager.create_full_project_backup(description="pre-update-backup")
|
|
2361
|
+
console.print(f" [green]โ Backup: {full_backup_path.relative_to(project_path)}/[/green]")
|
|
2362
|
+
|
|
2363
|
+
# Generate merge guide
|
|
2364
|
+
console.print(" [cyan]๐ Generating merge guide...[/cyan]")
|
|
2365
|
+
template_path = Path(__file__).parent.parent.parent / "templates"
|
|
2366
|
+
guide_path = _generate_manual_merge_guide(full_backup_path, template_path, project_path)
|
|
2367
|
+
console.print(f" [green]โ Guide: {guide_path.relative_to(project_path)}[/green]")
|
|
2368
|
+
|
|
2369
|
+
# Summary
|
|
2370
|
+
console.print("\n[green]โ Manual merge setup complete![/green]")
|
|
2371
|
+
console.print(f"[cyan]๐ Backup location: {full_backup_path.relative_to(project_path)}/[/cyan]")
|
|
2372
|
+
console.print(f"[cyan]๐ Merge guide: {guide_path.relative_to(project_path)}[/cyan]")
|
|
2373
|
+
console.print("\n[yellow]โ ๏ธ Next steps:[/yellow]")
|
|
2374
|
+
console.print("[yellow] 1. Review the merge guide[/yellow]")
|
|
2375
|
+
console.print("[yellow] 2. Compare files using diff or visual tools[/yellow]")
|
|
2376
|
+
console.print("[yellow] 3. Manually merge your customizations[/yellow]")
|
|
2377
|
+
console.print("[yellow] 4. Test and commit changes[/yellow]")
|
|
2378
|
+
|
|
2379
|
+
except Exception as e:
|
|
2380
|
+
console.print(f"[red]Error: Manual merge setup failed - {e}[/red]")
|
|
2381
|
+
raise click.Abort()
|
|
2382
|
+
|
|
2383
|
+
return
|
|
2384
|
+
|
|
2385
|
+
# Auto merge mode: Preserve user-specific settings before sync
|
|
2386
|
+
console.print("\n[cyan]๐ Auto-merge mode selected[/cyan]")
|
|
2387
|
+
console.print(" [cyan]๐พ Preserving user settings...[/cyan]")
|
|
2388
|
+
preserved_settings = _preserve_user_settings(project_path)
|
|
2389
|
+
|
|
980
2390
|
# Create backup unless --force
|
|
981
2391
|
if not force:
|
|
982
2392
|
console.print(" [cyan]๐พ Creating backup...[/cyan]")
|
|
@@ -990,10 +2400,14 @@ def update(path: str, force: bool, check: bool, templates_only: bool, yes: bool)
|
|
|
990
2400
|
else:
|
|
991
2401
|
console.print(" [yellow]โ Skipping backup (--force)[/yellow]")
|
|
992
2402
|
|
|
993
|
-
# Sync templates
|
|
2403
|
+
# Sync templates (NO spinner - user interaction may be required)
|
|
2404
|
+
# SpinnerContext blocks stdin, causing hang when click.confirm() is called
|
|
994
2405
|
try:
|
|
995
|
-
|
|
2406
|
+
console.print(" [cyan]Syncing templates...[/cyan]")
|
|
2407
|
+
if not _sync_templates(project_path, force, yes):
|
|
996
2408
|
raise TemplateSyncError("Template sync returned False")
|
|
2409
|
+
_restore_user_settings(project_path, preserved_settings)
|
|
2410
|
+
console.print(" [green]โ Template sync complete[/green]")
|
|
997
2411
|
except TemplateSyncError:
|
|
998
2412
|
console.print("[red]Error: Template sync failed[/red]")
|
|
999
2413
|
_show_template_sync_failure_help()
|
|
@@ -1010,8 +2424,263 @@ def update(path: str, force: bool, check: bool, templates_only: bool, yes: bool)
|
|
|
1010
2424
|
console.print(" [yellow]โ๏ธ Set optimized=false (optimization needed)[/yellow]")
|
|
1011
2425
|
|
|
1012
2426
|
console.print("\n[green]โ Update complete![/green]")
|
|
1013
|
-
console.print("[cyan]โน๏ธ Next step: Run /
|
|
2427
|
+
console.print("[cyan]โน๏ธ Next step: Run /moai:0-project update to optimize template changes[/cyan]")
|
|
1014
2428
|
|
|
1015
2429
|
except Exception as e:
|
|
1016
2430
|
console.print(f"[red]โ Update failed: {e}[/red]")
|
|
1017
2431
|
raise click.ClickException(str(e)) from e
|
|
2432
|
+
|
|
2433
|
+
|
|
2434
|
+
def _handle_custom_element_restoration(project_path: Path, backup_path: Path | None, yes: bool = False) -> None:
|
|
2435
|
+
"""Handle custom element restoration using the enhanced system.
|
|
2436
|
+
|
|
2437
|
+
This function provides an improved interface for restoring user-created custom elements
|
|
2438
|
+
(agents, commands, skills, hooks) from backup during MoAI-ADK updates.
|
|
2439
|
+
|
|
2440
|
+
Key improvements:
|
|
2441
|
+
- Preserves unselected elements (fixes disappearing issue)
|
|
2442
|
+
- Only overwrites/creates selected elements from backup
|
|
2443
|
+
- Interactive checkbox selection with arrow key navigation
|
|
2444
|
+
- Includes all categories (Agents, Commands, Skills, Hooks)
|
|
2445
|
+
|
|
2446
|
+
Args:
|
|
2447
|
+
project_path: Path to the MoAI-ADK project directory
|
|
2448
|
+
backup_path: Path to the backup directory (None if no backup)
|
|
2449
|
+
yes: Whether to automatically accept defaults (non-interactive mode)
|
|
2450
|
+
"""
|
|
2451
|
+
if not backup_path:
|
|
2452
|
+
# No backup available, cannot restore
|
|
2453
|
+
return
|
|
2454
|
+
|
|
2455
|
+
try:
|
|
2456
|
+
# Create scanner to find custom elements in backup (not current project)
|
|
2457
|
+
backup_scanner = create_custom_element_scanner(backup_path)
|
|
2458
|
+
|
|
2459
|
+
# Get count of custom elements in backup
|
|
2460
|
+
backup_element_count = backup_scanner.get_element_count()
|
|
2461
|
+
|
|
2462
|
+
if backup_element_count == 0:
|
|
2463
|
+
# No custom elements found in backup
|
|
2464
|
+
console.print("[green]โ No custom elements found in backup to restore[/green]")
|
|
2465
|
+
return
|
|
2466
|
+
|
|
2467
|
+
# Create enhanced user selection UI
|
|
2468
|
+
ui = create_user_selection_ui(project_path)
|
|
2469
|
+
|
|
2470
|
+
console.print(f"\n[cyan]๐ Found {backup_element_count} custom elements in backup[/cyan]")
|
|
2471
|
+
|
|
2472
|
+
# If yes mode is enabled, restore all elements automatically
|
|
2473
|
+
if yes:
|
|
2474
|
+
console.print(f"[cyan]๐ Auto-restoring {backup_element_count} custom elements...[/cyan]")
|
|
2475
|
+
backup_custom_elements = backup_scanner.scan_custom_elements()
|
|
2476
|
+
selected_elements = []
|
|
2477
|
+
|
|
2478
|
+
# Collect all element paths from backup
|
|
2479
|
+
for element_type, elements in backup_custom_elements.items():
|
|
2480
|
+
if element_type == "skills":
|
|
2481
|
+
for skill in elements:
|
|
2482
|
+
selected_elements.append(str(skill.path))
|
|
2483
|
+
else:
|
|
2484
|
+
for element_path in elements:
|
|
2485
|
+
selected_elements.append(str(element_path))
|
|
2486
|
+
else:
|
|
2487
|
+
# Interactive mode - prompt user for selection using enhanced UI
|
|
2488
|
+
selected_elements = ui.prompt_user_selection(backup_available=True)
|
|
2489
|
+
|
|
2490
|
+
if not selected_elements:
|
|
2491
|
+
console.print("[yellow]โ No elements selected for restoration[/yellow]")
|
|
2492
|
+
console.print("[green]โ All existing custom elements will be preserved[/green]")
|
|
2493
|
+
return
|
|
2494
|
+
|
|
2495
|
+
# Confirm selection
|
|
2496
|
+
if not ui.confirm_selection(selected_elements):
|
|
2497
|
+
console.print("[yellow]โ Restoration cancelled by user[/yellow]")
|
|
2498
|
+
console.print("[green]โ All existing custom elements will be preserved[/green]")
|
|
2499
|
+
return
|
|
2500
|
+
|
|
2501
|
+
# Perform selective restoration - ONLY restore selected elements
|
|
2502
|
+
if selected_elements:
|
|
2503
|
+
console.print(f"[cyan]๐ Restoring {len(selected_elements)} selected elements from backup...[/cyan]")
|
|
2504
|
+
restorer = create_selective_restorer(project_path, backup_path)
|
|
2505
|
+
success, stats = restorer.restore_elements(selected_elements)
|
|
2506
|
+
|
|
2507
|
+
if success:
|
|
2508
|
+
console.print(f"[green]โ
Successfully restored {stats['success']} custom elements[/green]")
|
|
2509
|
+
console.print("[green]โ All unselected elements remain preserved[/green]")
|
|
2510
|
+
else:
|
|
2511
|
+
console.print(f"[yellow]โ ๏ธ Partial restoration: {stats['success']}/{stats['total']} elements[/yellow]")
|
|
2512
|
+
if stats["failed"] > 0:
|
|
2513
|
+
console.print(f"[red]โ Failed to restore {stats['failed']} elements[/red]")
|
|
2514
|
+
console.print("[yellow]โ ๏ธ All other elements remain preserved[/yellow]")
|
|
2515
|
+
else:
|
|
2516
|
+
console.print("[green]โ No elements selected, all custom elements preserved[/green]")
|
|
2517
|
+
|
|
2518
|
+
except Exception as e:
|
|
2519
|
+
console.print(f"[yellow]โ ๏ธ Custom element restoration failed: {e}[/yellow]")
|
|
2520
|
+
logger.warning(f"Custom element restoration error: {e}")
|
|
2521
|
+
console.print("[yellow]โ ๏ธ All existing custom elements remain as-is[/yellow]")
|
|
2522
|
+
# Don't fail the entire update process, just log the error
|
|
2523
|
+
pass
|
|
2524
|
+
|
|
2525
|
+
|
|
2526
|
+
def _cleanup_legacy_presets(project_path: Path) -> None:
|
|
2527
|
+
"""Remove legacy JSON preset files (not the entire directory).
|
|
2528
|
+
|
|
2529
|
+
This function cleans up obsolete .json preset files in .moai/config/presets/
|
|
2530
|
+
that may exist from previous versions of MoAI-ADK. The directory itself
|
|
2531
|
+
and .yaml files are preserved since they are part of the current template.
|
|
2532
|
+
|
|
2533
|
+
Args:
|
|
2534
|
+
project_path: Project directory path (absolute)
|
|
2535
|
+
"""
|
|
2536
|
+
presets_dir = project_path / ".moai" / "config" / "presets"
|
|
2537
|
+
|
|
2538
|
+
if not presets_dir.exists() or not presets_dir.is_dir():
|
|
2539
|
+
return
|
|
2540
|
+
|
|
2541
|
+
# Only remove legacy .json files, preserve .yaml files and the directory
|
|
2542
|
+
json_files = list(presets_dir.glob("*.json"))
|
|
2543
|
+
if not json_files:
|
|
2544
|
+
return
|
|
2545
|
+
|
|
2546
|
+
removed_count = 0
|
|
2547
|
+
for json_file in json_files:
|
|
2548
|
+
try:
|
|
2549
|
+
json_file.unlink()
|
|
2550
|
+
removed_count += 1
|
|
2551
|
+
logger.debug(f"Removed legacy preset file: {json_file}")
|
|
2552
|
+
except Exception as e:
|
|
2553
|
+
logger.warning(f"Failed to remove legacy preset {json_file}: {e}")
|
|
2554
|
+
|
|
2555
|
+
if removed_count > 0:
|
|
2556
|
+
console.print(f" [cyan]๐งน Cleaned up {removed_count} legacy JSON preset files[/cyan]")
|
|
2557
|
+
logger.info(f"Removed {removed_count} legacy JSON preset files from {presets_dir}")
|
|
2558
|
+
|
|
2559
|
+
|
|
2560
|
+
def _migrate_config_json_to_yaml(project_path: Path) -> bool:
|
|
2561
|
+
"""Migrate legacy config.json to config.yaml format.
|
|
2562
|
+
|
|
2563
|
+
This function:
|
|
2564
|
+
1. Checks if config.json exists
|
|
2565
|
+
2. Converts it to config.yaml using YAML format
|
|
2566
|
+
3. Removes the old config.json file
|
|
2567
|
+
4. Also migrates preset files from JSON to YAML
|
|
2568
|
+
|
|
2569
|
+
Args:
|
|
2570
|
+
project_path: Project directory path (absolute)
|
|
2571
|
+
|
|
2572
|
+
Returns:
|
|
2573
|
+
bool: True if migration successful or not needed, False on error
|
|
2574
|
+
"""
|
|
2575
|
+
try:
|
|
2576
|
+
import yaml
|
|
2577
|
+
except ImportError:
|
|
2578
|
+
console.print(" [yellow]โ ๏ธ PyYAML not available, skipping config migration[/yellow]")
|
|
2579
|
+
return True # Not a critical error
|
|
2580
|
+
|
|
2581
|
+
config_dir = project_path / ".moai" / "config"
|
|
2582
|
+
json_path = config_dir / "config.json"
|
|
2583
|
+
yaml_path = config_dir / "config.yaml"
|
|
2584
|
+
|
|
2585
|
+
# Check if migration needed
|
|
2586
|
+
if not json_path.exists():
|
|
2587
|
+
# No JSON file, migration not needed
|
|
2588
|
+
return True
|
|
2589
|
+
|
|
2590
|
+
if yaml_path.exists():
|
|
2591
|
+
# YAML already exists, just remove JSON
|
|
2592
|
+
try:
|
|
2593
|
+
json_path.unlink()
|
|
2594
|
+
console.print(" [cyan]๐ Removed legacy config.json (YAML version exists)[/cyan]")
|
|
2595
|
+
logger.info(f"Removed legacy config.json: {json_path}")
|
|
2596
|
+
return True
|
|
2597
|
+
except Exception as e:
|
|
2598
|
+
console.print(f" [yellow]โ ๏ธ Failed to remove legacy config.json: {e}[/yellow]")
|
|
2599
|
+
logger.warning(f"Failed to remove {json_path}: {e}")
|
|
2600
|
+
return True # Not critical
|
|
2601
|
+
|
|
2602
|
+
# Perform migration
|
|
2603
|
+
try:
|
|
2604
|
+
# Read JSON config
|
|
2605
|
+
with open(json_path, "r", encoding="utf-8") as f:
|
|
2606
|
+
config_data = json.load(f)
|
|
2607
|
+
|
|
2608
|
+
# Write YAML config
|
|
2609
|
+
with open(yaml_path, "w", encoding="utf-8") as f:
|
|
2610
|
+
yaml.safe_dump(
|
|
2611
|
+
config_data,
|
|
2612
|
+
f,
|
|
2613
|
+
default_flow_style=False,
|
|
2614
|
+
allow_unicode=True,
|
|
2615
|
+
sort_keys=False,
|
|
2616
|
+
)
|
|
2617
|
+
|
|
2618
|
+
# Remove old JSON file
|
|
2619
|
+
json_path.unlink()
|
|
2620
|
+
|
|
2621
|
+
console.print(" [green]โ Migrated config.json โ config.yaml[/green]")
|
|
2622
|
+
logger.info(f"Migrated config from JSON to YAML: {json_path} โ {yaml_path}")
|
|
2623
|
+
|
|
2624
|
+
# Migrate preset files if they exist
|
|
2625
|
+
_migrate_preset_files_to_yaml(config_dir)
|
|
2626
|
+
|
|
2627
|
+
return True
|
|
2628
|
+
|
|
2629
|
+
except Exception as e:
|
|
2630
|
+
console.print(f" [red]โ Config migration failed: {e}[/red]")
|
|
2631
|
+
logger.error(f"Failed to migrate config.json to YAML: {e}")
|
|
2632
|
+
return False
|
|
2633
|
+
|
|
2634
|
+
|
|
2635
|
+
def _migrate_preset_files_to_yaml(config_dir: Path) -> None:
|
|
2636
|
+
"""Migrate preset files from JSON to YAML format.
|
|
2637
|
+
|
|
2638
|
+
Args:
|
|
2639
|
+
config_dir: .moai/config directory path
|
|
2640
|
+
"""
|
|
2641
|
+
try:
|
|
2642
|
+
import yaml
|
|
2643
|
+
except ImportError:
|
|
2644
|
+
return
|
|
2645
|
+
|
|
2646
|
+
presets_dir = config_dir / "presets"
|
|
2647
|
+
if not presets_dir.exists():
|
|
2648
|
+
return
|
|
2649
|
+
|
|
2650
|
+
migrated_count = 0
|
|
2651
|
+
for json_file in presets_dir.glob("*.json"):
|
|
2652
|
+
yaml_file = json_file.with_suffix(".yaml")
|
|
2653
|
+
|
|
2654
|
+
# Skip if YAML already exists
|
|
2655
|
+
if yaml_file.exists():
|
|
2656
|
+
# Just remove the JSON file
|
|
2657
|
+
try:
|
|
2658
|
+
json_file.unlink()
|
|
2659
|
+
migrated_count += 1
|
|
2660
|
+
except Exception as e:
|
|
2661
|
+
logger.warning(f"Failed to remove {json_file}: {e}")
|
|
2662
|
+
continue
|
|
2663
|
+
|
|
2664
|
+
# Migrate JSON โ YAML
|
|
2665
|
+
try:
|
|
2666
|
+
with open(json_file, "r", encoding="utf-8") as f:
|
|
2667
|
+
preset_data = json.load(f)
|
|
2668
|
+
|
|
2669
|
+
with open(yaml_file, "w", encoding="utf-8") as f:
|
|
2670
|
+
yaml.safe_dump(
|
|
2671
|
+
preset_data,
|
|
2672
|
+
f,
|
|
2673
|
+
default_flow_style=False,
|
|
2674
|
+
allow_unicode=True,
|
|
2675
|
+
sort_keys=False,
|
|
2676
|
+
)
|
|
2677
|
+
|
|
2678
|
+
json_file.unlink()
|
|
2679
|
+
migrated_count += 1
|
|
2680
|
+
|
|
2681
|
+
except Exception as e:
|
|
2682
|
+
logger.warning(f"Failed to migrate preset {json_file}: {e}")
|
|
2683
|
+
|
|
2684
|
+
if migrated_count > 0:
|
|
2685
|
+
console.print(f" [cyan]๐ Migrated {migrated_count} preset file(s) to YAML[/cyan]")
|
|
2686
|
+
logger.info(f"Migrated {migrated_count} preset files to YAML")
|