moai-adk 0.8.0__py3-none-any.whl → 1.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- moai_adk/__init__.py +2 -6
- moai_adk/__main__.py +267 -21
- moai_adk/astgrep/__init__.py +37 -0
- moai_adk/astgrep/analyzer.py +522 -0
- moai_adk/astgrep/models.py +124 -0
- moai_adk/astgrep/rules.py +179 -0
- moai_adk/cli/__init__.py +6 -2
- moai_adk/cli/commands/__init__.py +1 -4
- moai_adk/cli/commands/analyze.py +125 -0
- moai_adk/cli/commands/doctor.py +24 -6
- moai_adk/cli/commands/init.py +437 -57
- moai_adk/cli/commands/language.py +254 -0
- moai_adk/cli/commands/rank.py +449 -0
- moai_adk/cli/commands/status.py +15 -14
- moai_adk/cli/commands/switch.py +325 -0
- moai_adk/cli/commands/update.py +2195 -93
- moai_adk/cli/main.py +3 -2
- moai_adk/cli/prompts/init_prompts.py +457 -108
- moai_adk/cli/prompts/translations/__init__.py +573 -0
- 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 +448 -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 +788 -0
- moai_adk/cli/worktree/exceptions.py +89 -0
- moai_adk/cli/worktree/manager.py +648 -0
- moai_adk/cli/worktree/models.py +65 -0
- moai_adk/cli/worktree/registry.py +422 -0
- moai_adk/core/PHASE2_OPTIMIZATIONS.md +467 -0
- moai_adk/core/__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/migration.py +148 -17
- moai_adk/core/config/unified.py +617 -0
- moai_adk/core/context_manager.py +273 -0
- moai_adk/core/credentials.py +264 -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 +1920 -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 +422 -0
- moai_adk/core/git/event_detector.py +16 -7
- moai_adk/core/git/manager.py +91 -2
- 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 +305 -0
- 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 +578 -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 +666 -0
- moai_adk/core/migration/__init__.py +18 -0
- moai_adk/core/migration/alfred_to_moai_migrator.py +389 -0
- moai_adk/core/migration/backup_manager.py +327 -0
- moai_adk/core/migration/custom_element_scanner.py +358 -0
- moai_adk/core/migration/file_migrator.py +381 -0
- moai_adk/core/migration/interactive_checkbox_ui.py +499 -0
- moai_adk/core/migration/selective_restorer.py +470 -0
- moai_adk/core/migration/template_utils.py +74 -0
- moai_adk/core/migration/user_selection_ui.py +338 -0
- moai_adk/core/migration/version_detector.py +243 -0
- moai_adk/core/migration/version_migrator.py +263 -0
- moai_adk/core/model_allocator.py +241 -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 +13 -8
- moai_adk/core/project/checker.py +2 -4
- moai_adk/core/project/detector.py +189 -22
- moai_adk/core/project/initializer.py +177 -29
- moai_adk/core/project/phase_executor.py +482 -48
- moai_adk/core/project/validator.py +22 -32
- moai_adk/core/quality/__init__.py +1 -1
- moai_adk/core/quality/trust_checker.py +66 -110
- 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 +953 -0
- moai_adk/core/session_manager.py +651 -0
- moai_adk/core/skill_loading_system.py +579 -0
- moai_adk/core/spec_status_manager.py +478 -0
- moai_adk/core/template/__init__.py +0 -1
- moai_adk/core/template/backup.py +168 -21
- moai_adk/core/template/config.py +141 -45
- moai_adk/core/template/languages.py +0 -1
- moai_adk/core/template/merger.py +107 -32
- moai_adk/core/template/processor.py +1075 -74
- moai_adk/core/template_engine.py +319 -0
- moai_adk/core/template_variable_synchronizer.py +431 -0
- moai_adk/core/unified_permission_manager.py +745 -0
- moai_adk/core/user_behavior_analytics.py +851 -0
- moai_adk/core/version_sync.py +477 -0
- moai_adk/foundation/__init__.py +37 -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/__init__.py +376 -0
- moai_adk/foundation/git/commit_templates.py +557 -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/loop/__init__.py +54 -0
- moai_adk/loop/controller.py +305 -0
- moai_adk/loop/feedback.py +230 -0
- moai_adk/loop/state.py +209 -0
- moai_adk/loop/storage.py +220 -0
- moai_adk/lsp/__init__.py +70 -0
- moai_adk/lsp/client.py +320 -0
- moai_adk/lsp/models.py +261 -0
- moai_adk/lsp/protocol.py +404 -0
- moai_adk/lsp/server_manager.py +248 -0
- moai_adk/project/__init__.py +0 -0
- moai_adk/project/configuration.py +1091 -0
- moai_adk/project/documentation.py +566 -0
- moai_adk/project/schema.py +447 -0
- moai_adk/py.typed +0 -0
- moai_adk/ralph/__init__.py +37 -0
- moai_adk/ralph/engine.py +307 -0
- moai_adk/rank/__init__.py +21 -0
- moai_adk/rank/auth.py +425 -0
- moai_adk/rank/client.py +557 -0
- moai_adk/rank/config.py +147 -0
- moai_adk/rank/hook.py +1503 -0
- moai_adk/rank/py.typed +0 -0
- moai_adk/statusline/__init__.py +41 -0
- moai_adk/statusline/alfred_detector.py +105 -0
- moai_adk/statusline/config.py +376 -0
- moai_adk/statusline/enhanced_output_style_detector.py +372 -0
- moai_adk/statusline/git_collector.py +190 -0
- moai_adk/statusline/main.py +341 -0
- moai_adk/statusline/memory_collector.py +268 -0
- moai_adk/statusline/metrics_tracker.py +78 -0
- moai_adk/statusline/renderer.py +359 -0
- moai_adk/statusline/update_checker.py +129 -0
- moai_adk/statusline/version_reader.py +741 -0
- moai_adk/tag_system/__init__.py +48 -0
- moai_adk/tag_system/atomic_ops.py +117 -0
- moai_adk/tag_system/linkage.py +335 -0
- moai_adk/tag_system/parser.py +176 -0
- moai_adk/tag_system/validator.py +200 -0
- moai_adk/templates/.claude/agents/moai/builder-agent.md +490 -0
- moai_adk/templates/.claude/agents/moai/builder-command.md +1218 -0
- moai_adk/templates/.claude/agents/moai/builder-plugin.md +763 -0
- moai_adk/templates/.claude/agents/moai/builder-skill.md +682 -0
- moai_adk/templates/.claude/agents/moai/expert-backend.md +963 -0
- moai_adk/templates/.claude/agents/moai/expert-debug.md +407 -0
- moai_adk/templates/.claude/agents/moai/expert-devops.md +722 -0
- moai_adk/templates/.claude/agents/moai/expert-frontend.md +748 -0
- moai_adk/templates/.claude/agents/moai/expert-performance.md +661 -0
- moai_adk/templates/.claude/agents/moai/expert-refactoring.md +228 -0
- moai_adk/templates/.claude/agents/moai/expert-security.md +525 -0
- moai_adk/templates/.claude/agents/moai/expert-testing.md +737 -0
- moai_adk/templates/.claude/agents/moai/manager-claude-code.md +438 -0
- moai_adk/templates/.claude/agents/moai/manager-docs.md +578 -0
- moai_adk/templates/.claude/agents/moai/manager-git.md +1092 -0
- moai_adk/templates/.claude/agents/moai/manager-project.md +971 -0
- moai_adk/templates/.claude/agents/moai/manager-quality.md +641 -0
- moai_adk/templates/.claude/agents/moai/manager-spec.md +815 -0
- moai_adk/templates/.claude/agents/moai/manager-strategy.md +811 -0
- moai_adk/templates/.claude/agents/moai/manager-tdd.md +797 -0
- moai_adk/templates/.claude/commands/moai/0-project.md +438 -0
- moai_adk/templates/.claude/commands/moai/1-plan.md +1447 -0
- moai_adk/templates/.claude/commands/moai/2-run.md +850 -0
- moai_adk/templates/.claude/commands/moai/3-sync.md +1398 -0
- moai_adk/templates/.claude/commands/moai/9-feedback.md +330 -0
- moai_adk/templates/.claude/commands/moai/alfred.md +339 -0
- moai_adk/templates/.claude/commands/moai/cancel-loop.md +163 -0
- moai_adk/templates/.claude/commands/moai/fix.md +264 -0
- moai_adk/templates/.claude/commands/moai/loop.md +363 -0
- moai_adk/templates/.claude/hooks/__init__.py +8 -0
- moai_adk/templates/.claude/hooks/moai/__init__.py +8 -0
- moai_adk/templates/.claude/hooks/moai/lib/README.md +143 -0
- moai_adk/templates/.claude/hooks/moai/lib/__init__.py +41 -0
- moai_adk/templates/.claude/hooks/moai/lib/alfred_detector.py +105 -0
- moai_adk/templates/.claude/hooks/moai/lib/atomic_write.py +122 -0
- moai_adk/templates/.claude/hooks/{alfred/core → moai/lib}/checkpoint.py +13 -37
- moai_adk/templates/.claude/hooks/moai/lib/common.py +161 -0
- moai_adk/templates/.claude/hooks/moai/lib/config.py +376 -0
- moai_adk/templates/.claude/hooks/moai/lib/config_manager.py +442 -0
- moai_adk/templates/.claude/hooks/moai/lib/config_validator.py +639 -0
- moai_adk/templates/.claude/hooks/moai/lib/enhanced_output_style_detector.py +372 -0
- moai_adk/templates/.claude/hooks/moai/lib/example_config.json +104 -0
- moai_adk/templates/.claude/hooks/moai/lib/exceptions.py +171 -0
- moai_adk/templates/.claude/hooks/moai/lib/file_utils.py +95 -0
- moai_adk/templates/.claude/hooks/moai/lib/git_collector.py +190 -0
- moai_adk/templates/.claude/hooks/moai/lib/git_operations_manager.py +592 -0
- moai_adk/templates/.claude/hooks/moai/lib/language_detector.py +298 -0
- moai_adk/templates/.claude/hooks/moai/lib/language_validator.py +417 -0
- moai_adk/templates/.claude/hooks/moai/lib/main.py +341 -0
- moai_adk/templates/.claude/hooks/moai/lib/memory_collector.py +268 -0
- moai_adk/templates/.claude/hooks/moai/lib/metrics_tracker.py +78 -0
- moai_adk/templates/.claude/hooks/moai/lib/models.py +104 -0
- moai_adk/templates/.claude/hooks/moai/lib/path_utils.py +219 -0
- moai_adk/templates/.claude/hooks/moai/lib/project.py +777 -0
- moai_adk/templates/.claude/hooks/moai/lib/renderer.py +359 -0
- moai_adk/templates/.claude/hooks/moai/lib/tag_linkage.py +333 -0
- moai_adk/templates/.claude/hooks/moai/lib/tag_parser.py +176 -0
- moai_adk/templates/.claude/hooks/moai/lib/tag_validator.py +200 -0
- moai_adk/templates/.claude/hooks/moai/lib/test_hooks_improvements.py +443 -0
- moai_adk/templates/.claude/hooks/moai/lib/timeout.py +160 -0
- moai_adk/templates/.claude/hooks/moai/lib/tool_registry.py +896 -0
- moai_adk/templates/.claude/hooks/moai/lib/unified_timeout_manager.py +542 -0
- moai_adk/templates/.claude/hooks/moai/lib/update_checker.py +129 -0
- moai_adk/templates/.claude/hooks/moai/lib/version_reader.py +741 -0
- moai_adk/templates/.claude/hooks/moai/post_tool__ast_grep_scan.py +276 -0
- moai_adk/templates/.claude/hooks/moai/post_tool__code_formatter.py +255 -0
- moai_adk/templates/.claude/hooks/moai/post_tool__coverage_guard.py +325 -0
- moai_adk/templates/.claude/hooks/moai/post_tool__linter.py +315 -0
- moai_adk/templates/.claude/hooks/moai/post_tool__lsp_diagnostic.py +508 -0
- moai_adk/templates/.claude/hooks/moai/pre_commit__tag_validator.py +287 -0
- moai_adk/templates/.claude/hooks/moai/pre_tool__security_guard.py +268 -0
- moai_adk/templates/.claude/hooks/moai/pre_tool__tdd_enforcer.py +208 -0
- moai_adk/templates/.claude/hooks/moai/session_end__auto_cleanup.py +894 -0
- moai_adk/templates/.claude/hooks/moai/session_end__rank_submit.py +69 -0
- moai_adk/templates/.claude/hooks/moai/session_start__show_project_info.py +1170 -0
- moai_adk/templates/.claude/hooks/moai/shared/utils/announcement_translator.py +206 -0
- moai_adk/templates/.claude/hooks/moai/stop__loop_controller.py +621 -0
- moai_adk/templates/.claude/output-styles/moai/alfred.md +758 -0
- moai_adk/templates/.claude/output-styles/moai/r2d2.md +643 -0
- moai_adk/templates/.claude/output-styles/moai/yoda.md +359 -0
- moai_adk/templates/.claude/settings.json +177 -72
- moai_adk/templates/.claude/skills/moai-docs-generation/SKILL.md +303 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/examples.md +252 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/README.md +56 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/api-documentation.md +120 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/code-documentation.md +152 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/multi-format-output.md +185 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/user-guides.md +207 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/reference.md +234 -0
- moai_adk/templates/.claude/skills/moai-domain-backend/SKILL.md +132 -281
- moai_adk/templates/.claude/skills/moai-domain-backend/examples.md +610 -1525
- moai_adk/templates/.claude/skills/moai-domain-backend/reference.md +423 -619
- moai_adk/templates/.claude/skills/moai-domain-database/SKILL.md +133 -77
- moai_adk/templates/.claude/skills/moai-domain-database/examples.md +817 -16
- moai_adk/templates/.claude/skills/moai-domain-database/modules/README.md +53 -0
- moai_adk/templates/.claude/skills/moai-domain-database/modules/mongodb.md +231 -0
- moai_adk/templates/.claude/skills/moai-domain-database/modules/postgresql.md +169 -0
- moai_adk/templates/.claude/skills/moai-domain-database/modules/redis.md +262 -0
- moai_adk/templates/.claude/skills/moai-domain-database/reference.md +532 -17
- moai_adk/templates/.claude/skills/moai-domain-frontend/SKILL.md +91 -78
- moai_adk/templates/.claude/skills/moai-domain-frontend/examples.md +955 -16
- moai_adk/templates/.claude/skills/moai-domain-frontend/modules/component-architecture.md +723 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/modules/nextjs16-patterns.md +713 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/modules/performance-optimization.md +694 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/modules/react19-patterns.md +591 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/modules/state-management.md +680 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/modules/vue35-patterns.md +802 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/reference.md +651 -18
- moai_adk/templates/.claude/skills/moai-domain-uiux/SKILL.md +234 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/examples.md +560 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/modules/accessibility-wcag.md +260 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/modules/component-architecture.md +228 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/modules/icon-libraries.md +401 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/modules/theming-system.md +373 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/reference.md +243 -0
- moai_adk/templates/.claude/skills/moai-formats-data/SKILL.md +189 -0
- moai_adk/templates/.claude/skills/moai-formats-data/examples.md +804 -0
- moai_adk/templates/.claude/skills/moai-formats-data/modules/README.md +327 -0
- moai_adk/templates/.claude/skills/moai-formats-data/modules/SKILL-MODULARIZATION-TEMPLATE.md +278 -0
- moai_adk/templates/.claude/skills/moai-formats-data/modules/caching-performance.md +459 -0
- moai_adk/templates/.claude/skills/moai-formats-data/modules/data-validation.md +485 -0
- moai_adk/templates/.claude/skills/moai-formats-data/modules/json-optimization.md +374 -0
- moai_adk/templates/.claude/skills/moai-formats-data/modules/toon-encoding.md +308 -0
- moai_adk/templates/.claude/skills/moai-formats-data/reference.md +585 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/SKILL.md +225 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/examples.md +732 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/advanced-agent-patterns.md +370 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/best-practices-checklist.md +616 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-cli-reference-official.md +420 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-custom-slash-commands-official.md +739 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-devcontainers-official.md +381 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-discover-plugins-official.md +379 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-headless-official.md +378 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-hooks-official.md +670 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-iam-official.md +635 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-memory-official.md +543 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-plugin-marketplaces-official.md +308 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-plugins-official.md +640 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-sandboxing-official.md +282 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-settings-official.md +663 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-skills-official.md +467 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-statusline-official.md +293 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-sub-agents-official.md +420 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/complete-configuration-guide.md +175 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/skill-examples.md +1674 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/skill-formatting-guide.md +729 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/sub-agents/sub-agent-examples.md +1513 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/sub-agents/sub-agent-formatting-guide.md +1086 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/sub-agents/sub-agent-integration-patterns.md +1100 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference.md +209 -0
- moai_adk/templates/.claude/skills/moai-foundation-context/SKILL.md +221 -0
- moai_adk/templates/.claude/skills/moai-foundation-context/examples.md +1048 -0
- moai_adk/templates/.claude/skills/moai-foundation-context/reference.md +246 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/SKILL.md +242 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/examples.md +358 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/README.md +296 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/agents-reference.md +359 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/commands-reference.md +432 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/delegation-advanced.md +279 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/delegation-implementation.md +267 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/delegation-patterns.md +228 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/execution-rules.md +687 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/modular-system.md +665 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/patterns.md +22 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/progressive-disclosure.md +649 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/spec-ears-format.md +200 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/spec-first-tdd.md +171 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/spec-tdd-implementation.md +275 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/token-optimization.md +708 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/trust-5-framework.md +239 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/trust-5-implementation.md +244 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/trust-5-validation.md +219 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/reference.md +478 -0
- moai_adk/templates/.claude/skills/moai-foundation-philosopher/SKILL.md +311 -0
- moai_adk/templates/.claude/skills/moai-foundation-philosopher/examples.md +228 -0
- moai_adk/templates/.claude/skills/moai-foundation-philosopher/modules/assumption-matrix.md +80 -0
- moai_adk/templates/.claude/skills/moai-foundation-philosopher/modules/cognitive-bias.md +199 -0
- moai_adk/templates/.claude/skills/moai-foundation-philosopher/modules/first-principles.md +140 -0
- moai_adk/templates/.claude/skills/moai-foundation-philosopher/modules/trade-off-analysis.md +154 -0
- moai_adk/templates/.claude/skills/moai-foundation-philosopher/reference.md +157 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/SKILL.md +180 -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-framework-electron/SKILL.md +288 -0
- moai_adk/templates/.claude/skills/moai-framework-electron/examples.md +2082 -0
- moai_adk/templates/.claude/skills/moai-framework-electron/reference.md +1649 -0
- moai_adk/templates/.claude/skills/moai-lang-cpp/SKILL.md +96 -77
- moai_adk/templates/.claude/skills/moai-lang-cpp/examples.md +1226 -16
- moai_adk/templates/.claude/skills/moai-lang-cpp/modules/advanced-patterns.md +401 -0
- moai_adk/templates/.claude/skills/moai-lang-cpp/reference.md +1119 -14
- moai_adk/templates/.claude/skills/moai-lang-csharp/SKILL.md +80 -79
- moai_adk/templates/.claude/skills/moai-lang-csharp/examples.md +572 -16
- moai_adk/templates/.claude/skills/moai-lang-csharp/modules/aspnet-core.md +627 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/modules/blazor-components.md +767 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/modules/cqrs-validation.md +626 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/modules/csharp12-features.md +580 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/modules/efcore-patterns.md +622 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/reference.md +388 -15
- moai_adk/templates/.claude/skills/moai-lang-elixir/SKILL.md +135 -0
- moai_adk/templates/.claude/skills/moai-lang-elixir/examples.md +1171 -0
- moai_adk/templates/.claude/skills/moai-lang-elixir/modules/advanced-patterns.md +531 -0
- moai_adk/templates/.claude/skills/moai-lang-elixir/reference.md +889 -0
- moai_adk/templates/.claude/skills/moai-lang-flutter/SKILL.md +104 -0
- moai_adk/templates/.claude/skills/moai-lang-flutter/examples.md +1090 -0
- moai_adk/templates/.claude/skills/moai-lang-flutter/reference.md +686 -0
- moai_adk/templates/.claude/skills/moai-lang-go/SKILL.md +153 -80
- moai_adk/templates/.claude/skills/moai-lang-go/examples.md +906 -16
- moai_adk/templates/.claude/skills/moai-lang-go/reference.md +721 -15
- moai_adk/templates/.claude/skills/moai-lang-java/SKILL.md +117 -80
- moai_adk/templates/.claude/skills/moai-lang-java/examples.md +851 -16
- moai_adk/templates/.claude/skills/moai-lang-java/reference.md +278 -18
- moai_adk/templates/.claude/skills/moai-lang-javascript/SKILL.md +133 -79
- moai_adk/templates/.claude/skills/moai-lang-javascript/examples.md +960 -16
- moai_adk/templates/.claude/skills/moai-lang-javascript/reference.md +1528 -17
- moai_adk/templates/.claude/skills/moai-lang-kotlin/SKILL.md +100 -79
- moai_adk/templates/.claude/skills/moai-lang-kotlin/examples.md +993 -16
- moai_adk/templates/.claude/skills/moai-lang-kotlin/reference.md +549 -18
- moai_adk/templates/.claude/skills/moai-lang-php/SKILL.md +135 -76
- moai_adk/templates/.claude/skills/moai-lang-php/examples.md +1595 -16
- moai_adk/templates/.claude/skills/moai-lang-php/modules/advanced-patterns.md +538 -0
- moai_adk/templates/.claude/skills/moai-lang-php/reference.md +1309 -16
- moai_adk/templates/.claude/skills/moai-lang-python/SKILL.md +141 -341
- moai_adk/templates/.claude/skills/moai-lang-python/examples.md +849 -496
- moai_adk/templates/.claude/skills/moai-lang-python/reference.md +731 -243
- moai_adk/templates/.claude/skills/moai-lang-r/SKILL.md +134 -76
- moai_adk/templates/.claude/skills/moai-lang-r/examples.md +1141 -16
- moai_adk/templates/.claude/skills/moai-lang-r/modules/advanced-patterns.md +489 -0
- moai_adk/templates/.claude/skills/moai-lang-r/reference.md +1074 -17
- moai_adk/templates/.claude/skills/moai-lang-ruby/SKILL.md +136 -77
- moai_adk/templates/.claude/skills/moai-lang-ruby/examples.md +1093 -16
- moai_adk/templates/.claude/skills/moai-lang-ruby/modules/advanced-patterns.md +309 -0
- moai_adk/templates/.claude/skills/moai-lang-ruby/modules/testing-patterns.md +306 -0
- moai_adk/templates/.claude/skills/moai-lang-ruby/reference.md +1010 -17
- moai_adk/templates/.claude/skills/moai-lang-rust/SKILL.md +112 -78
- moai_adk/templates/.claude/skills/moai-lang-rust/examples.md +646 -16
- moai_adk/templates/.claude/skills/moai-lang-rust/reference.md +491 -18
- moai_adk/templates/.claude/skills/moai-lang-scala/SKILL.md +113 -75
- moai_adk/templates/.claude/skills/moai-lang-scala/examples.md +620 -16
- moai_adk/templates/.claude/skills/moai-lang-scala/modules/akka-actors.md +479 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/modules/cats-effect.md +489 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/modules/functional-programming.md +460 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/modules/spark-data.md +498 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/modules/zio-patterns.md +541 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/reference.md +410 -17
- moai_adk/templates/.claude/skills/moai-lang-swift/SKILL.md +88 -83
- moai_adk/templates/.claude/skills/moai-lang-swift/examples.md +905 -16
- moai_adk/templates/.claude/skills/moai-lang-swift/modules/combine-reactive.md +256 -0
- moai_adk/templates/.claude/skills/moai-lang-swift/modules/concurrency.md +270 -0
- moai_adk/templates/.claude/skills/moai-lang-swift/modules/swift6-features.md +265 -0
- moai_adk/templates/.claude/skills/moai-lang-swift/modules/swiftui-patterns.md +314 -0
- moai_adk/templates/.claude/skills/moai-lang-swift/reference.md +659 -17
- moai_adk/templates/.claude/skills/moai-lang-typescript/SKILL.md +115 -82
- moai_adk/templates/.claude/skills/moai-lang-typescript/examples.md +1076 -16
- moai_adk/templates/.claude/skills/moai-lang-typescript/reference.md +718 -21
- moai_adk/templates/.claude/skills/moai-library-mermaid/SKILL.md +145 -0
- moai_adk/templates/.claude/skills/moai-library-mermaid/examples.md +270 -0
- moai_adk/templates/.claude/skills/moai-library-mermaid/modules/advanced-patterns.md +465 -0
- moai_adk/templates/.claude/skills/moai-library-mermaid/modules/optimization.md +440 -0
- moai_adk/templates/.claude/skills/moai-library-mermaid/reference.md +228 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/SKILL.md +143 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/examples.md +592 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/advanced-deployment-patterns.md +182 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/advanced-patterns.md +336 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/configuration.md +350 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/content-architecture-optimization.md +162 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/deployment.md +52 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/framework-core-configuration.md +186 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/i18n-setup.md +55 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/mdx-components.md +52 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/optimization.md +303 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/reference.md +379 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/SKILL.md +175 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/examples.md +575 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/modules/advanced-patterns.md +394 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/modules/optimization.md +278 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/modules/shadcn-components.md +457 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/modules/shadcn-theming.md +373 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/reference.md +74 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/SKILL.md +284 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/examples.md +2446 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/adaptive-mfa.md +233 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/akamai-integration.md +214 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/application-credentials.md +280 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/attack-protection-log-events.md +224 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/attack-protection-overview.md +140 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/bot-detection.md +144 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/breached-password-detection.md +187 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/brute-force-protection.md +189 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/certifications.md +282 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/compliance-overview.md +263 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/continuous-session-protection.md +307 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/customize-mfa.md +177 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/dpop-implementation.md +283 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/fapi-implementation.md +259 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/gdpr-compliance.md +313 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/guardian-configuration.md +269 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/highly-regulated-identity.md +272 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/jwt-fundamentals.md +248 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/mdl-verification.md +210 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/mfa-api-management.md +278 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/mfa-factors.md +226 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/mfa-overview.md +174 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/mtls-sender-constraining.md +316 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/ropg-flow-mfa.md +216 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/security-center.md +325 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/security-guidance.md +277 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/state-parameters.md +177 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/step-up-authentication.md +251 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/suspicious-ip-throttling.md +240 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/tenant-access-control.md +179 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/webauthn-fido.md +235 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/reference.md +224 -0
- moai_adk/templates/.claude/skills/moai-platform-clerk/SKILL.md +135 -0
- moai_adk/templates/.claude/skills/moai-platform-clerk/examples.md +1426 -0
- moai_adk/templates/.claude/skills/moai-platform-clerk/modules/advanced-patterns.md +417 -0
- moai_adk/templates/.claude/skills/moai-platform-clerk/reference.md +273 -0
- moai_adk/templates/.claude/skills/moai-platform-convex/SKILL.md +158 -0
- moai_adk/templates/.claude/skills/moai-platform-convex/examples.md +506 -0
- moai_adk/templates/.claude/skills/moai-platform-convex/modules/auth-integration.md +421 -0
- moai_adk/templates/.claude/skills/moai-platform-convex/modules/file-storage.md +474 -0
- moai_adk/templates/.claude/skills/moai-platform-convex/modules/reactive-queries.md +302 -0
- moai_adk/templates/.claude/skills/moai-platform-convex/modules/server-functions.md +452 -0
- moai_adk/templates/.claude/skills/moai-platform-convex/reference.md +385 -0
- moai_adk/templates/.claude/skills/moai-platform-firebase-auth/SKILL.md +166 -0
- moai_adk/templates/.claude/skills/moai-platform-firebase-auth/examples.md +514 -0
- moai_adk/templates/.claude/skills/moai-platform-firebase-auth/modules/custom-claims.md +374 -0
- moai_adk/templates/.claude/skills/moai-platform-firebase-auth/modules/phone-auth.md +372 -0
- moai_adk/templates/.claude/skills/moai-platform-firebase-auth/modules/social-auth.md +339 -0
- moai_adk/templates/.claude/skills/moai-platform-firebase-auth/reference.md +382 -0
- moai_adk/templates/.claude/skills/moai-platform-firestore/SKILL.md +127 -0
- moai_adk/templates/.claude/skills/moai-platform-firestore/examples.md +445 -0
- moai_adk/templates/.claude/skills/moai-platform-firestore/modules/offline-cache.md +392 -0
- moai_adk/templates/.claude/skills/moai-platform-firestore/modules/realtime-listeners.md +441 -0
- moai_adk/templates/.claude/skills/moai-platform-firestore/modules/security-rules.md +352 -0
- moai_adk/templates/.claude/skills/moai-platform-firestore/modules/transactions.md +452 -0
- moai_adk/templates/.claude/skills/moai-platform-firestore/reference.md +322 -0
- moai_adk/templates/.claude/skills/moai-platform-neon/SKILL.md +156 -0
- moai_adk/templates/.claude/skills/moai-platform-neon/examples.md +470 -0
- moai_adk/templates/.claude/skills/moai-platform-neon/modules/auto-scaling.md +349 -0
- moai_adk/templates/.claude/skills/moai-platform-neon/modules/branching-workflows.md +354 -0
- moai_adk/templates/.claude/skills/moai-platform-neon/modules/connection-pooling.md +412 -0
- moai_adk/templates/.claude/skills/moai-platform-neon/modules/pitr-backups.md +458 -0
- moai_adk/templates/.claude/skills/moai-platform-neon/reference.md +272 -0
- moai_adk/templates/.claude/skills/moai-platform-railway/SKILL.md +146 -0
- moai_adk/templates/.claude/skills/moai-platform-railway/examples.md +539 -0
- moai_adk/templates/.claude/skills/moai-platform-railway/modules/docker-deployment.md +261 -0
- moai_adk/templates/.claude/skills/moai-platform-railway/modules/multi-service.md +291 -0
- moai_adk/templates/.claude/skills/moai-platform-railway/modules/networking-domains.md +338 -0
- moai_adk/templates/.claude/skills/moai-platform-railway/modules/volumes-storage.md +353 -0
- moai_adk/templates/.claude/skills/moai-platform-railway/reference.md +374 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/SKILL.md +141 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/examples.md +502 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/modules/auth-integration.md +384 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/modules/edge-functions.md +371 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/modules/postgresql-pgvector.md +231 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/modules/realtime-presence.md +354 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/modules/row-level-security.md +286 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/modules/storage-cdn.md +319 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/modules/typescript-patterns.md +453 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/reference.md +284 -0
- moai_adk/templates/.claude/skills/moai-platform-vercel/SKILL.md +132 -0
- moai_adk/templates/.claude/skills/moai-platform-vercel/examples.md +502 -0
- moai_adk/templates/.claude/skills/moai-platform-vercel/modules/analytics-speed.md +348 -0
- moai_adk/templates/.claude/skills/moai-platform-vercel/modules/deployment-config.md +344 -0
- moai_adk/templates/.claude/skills/moai-platform-vercel/modules/edge-functions.md +222 -0
- moai_adk/templates/.claude/skills/moai-platform-vercel/modules/isr-caching.md +306 -0
- moai_adk/templates/.claude/skills/moai-platform-vercel/modules/kv-storage.md +399 -0
- moai_adk/templates/.claude/skills/moai-platform-vercel/reference.md +360 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/SKILL.md +193 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/examples.md +1099 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/modules/language-specific.md +307 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/modules/pattern-syntax.md +237 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/modules/refactoring-patterns.md +260 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/modules/security-rules.md +239 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/reference.md +288 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/languages/go.yml +90 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/languages/python.yml +101 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/languages/typescript.yml +83 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/quality/complexity-check.yml +94 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/quality/deprecated-apis.yml +84 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/security/secrets-detection.yml +89 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/security/sql-injection.yml +45 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/security/xss-prevention.yml +50 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/sgconfig.yml +54 -0
- moai_adk/templates/.claude/skills/moai-workflow-jit-docs/SKILL.md +251 -0
- moai_adk/templates/.claude/skills/moai-workflow-jit-docs/examples.md +544 -0
- moai_adk/templates/.claude/skills/moai-workflow-jit-docs/modules/advanced-patterns.md +379 -0
- moai_adk/templates/.claude/skills/moai-workflow-jit-docs/modules/optimization.md +286 -0
- moai_adk/templates/.claude/skills/moai-workflow-jit-docs/reference.md +307 -0
- moai_adk/templates/.claude/skills/moai-workflow-loop/SKILL.md +197 -0
- moai_adk/templates/.claude/skills/moai-workflow-loop/examples.md +1063 -0
- moai_adk/templates/.claude/skills/moai-workflow-loop/reference.md +1414 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/README.md +190 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/SKILL.md +287 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/examples.md +547 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/reference.md +275 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/schemas/config-schema.json +316 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/schemas/tab_schema.json +1434 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/config-template.json +71 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/doc-templates/product-template.md +44 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/doc-templates/structure-template.md +48 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/doc-templates/tech-template.md +92 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/config-manager-setup.json +109 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/language-initializer.json +228 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/menu-project-config.json +130 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/project-batch-questions.json +97 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/spec-workflow-setup.json +150 -0
- moai_adk/templates/.claude/skills/moai-workflow-spec/SKILL.md +337 -0
- moai_adk/templates/.claude/skills/moai-workflow-spec/examples.md +900 -0
- moai_adk/templates/.claude/skills/moai-workflow-spec/modules/advanced-patterns.md +237 -0
- moai_adk/templates/.claude/skills/moai-workflow-spec/reference.md +704 -0
- moai_adk/templates/.claude/skills/moai-workflow-templates/SKILL.md +270 -0
- moai_adk/templates/.claude/skills/moai-workflow-templates/examples.md +552 -0
- moai_adk/templates/.claude/skills/moai-workflow-templates/modules/code-templates.md +124 -0
- moai_adk/templates/.claude/skills/moai-workflow-templates/modules/feedback-templates.md +100 -0
- moai_adk/templates/.claude/skills/moai-workflow-templates/modules/template-optimizer.md +138 -0
- moai_adk/templates/.claude/skills/moai-workflow-templates/reference.md +346 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/LICENSE.txt +202 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/SKILL.md +269 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/examples/ai-powered-testing.py +294 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/examples/console_logging.py +35 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/examples/element_discovery.py +40 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/examples/static_html_automation.py +34 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/examples.md +672 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/README.md +269 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/advanced-patterns.md +576 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/ai-debugging.md +302 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/context7-integration.md +286 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/review-workflows.md +500 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework/relevance-analysis.md +154 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework/safety-analysis.md +148 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework/scoring-algorithms.md +196 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework/timeliness-analysis.md +168 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework/truthfulness-analysis.md +136 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework/usability-analysis.md +153 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework.md +257 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review.md +263 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/code-review/analysis-patterns.md +340 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/code-review/core-classes.md +299 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/code-review/tool-integration.md +380 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/debugging/debugging-workflows.md +451 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/debugging/error-analysis.md +442 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/optimization.md +505 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance/optimization-patterns.md +473 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance/profiling-techniques.md +481 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization/ai-optimization.md +241 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization/bottleneck-detection.md +397 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization/optimization-plan.md +315 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization/profiler-core.md +277 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization/real-time-monitoring.md +187 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization.md +327 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/quality-metrics.md +415 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/refactoring/ai-workflows.md +620 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/refactoring/patterns.md +692 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/security-analysis.md +429 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/smart-refactoring.md +313 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/static-analysis.md +438 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd/core-classes.md +397 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd-context7/advanced-features.md +494 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd-context7/red-green-refactor.md +316 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd-context7/test-generation.md +471 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd-context7/test-patterns.md +371 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd-context7.md +265 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/trust5-validation.md +428 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/reference/playwright-best-practices.md +57 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/reference.md +440 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/scripts/with_server.py +218 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/templates/alfred-integration.md +376 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/workflows/enterprise-testing-workflow.py +571 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/SKILL.md +228 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/examples.md +606 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/integration-patterns.md +149 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/moai-adk-integration.md +245 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/parallel-advanced.md +310 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/parallel-development.md +202 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/parallel-workflows.md +302 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/registry-architecture.md +271 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/resource-optimization.md +300 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/tools-integration.md +280 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/troubleshooting.md +397 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/worktree-commands.md +296 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/worktree-management.md +217 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/reference.md +357 -0
- moai_adk/templates/.git-hooks/pre-commit +128 -0
- moai_adk/templates/.git-hooks/pre-push +468 -0
- moai_adk/templates/.github/workflows/ci-universal.yml +1314 -0
- moai_adk/templates/.github/workflows/security-secrets-check.yml +179 -0
- moai_adk/templates/.github/workflows/spec-issue-sync.yml +206 -36
- moai_adk/templates/.gitignore +152 -13
- moai_adk/templates/.lsp.json +152 -0
- moai_adk/templates/.mcp.json +13 -0
- moai_adk/templates/.moai/announcements/en.json +18 -0
- moai_adk/templates/.moai/announcements/ja.json +18 -0
- moai_adk/templates/.moai/announcements/ko.json +18 -0
- moai_adk/templates/.moai/announcements/zh.json +18 -0
- moai_adk/templates/.moai/config/config.yaml +64 -0
- moai_adk/templates/.moai/config/multilingual-triggers.yaml +213 -0
- moai_adk/templates/.moai/config/sections/git-strategy.yaml +116 -0
- moai_adk/templates/.moai/config/sections/language.yaml +11 -0
- moai_adk/templates/.moai/config/sections/llm.yaml +41 -0
- moai_adk/templates/.moai/config/sections/pricing.yaml +30 -0
- moai_adk/templates/.moai/config/sections/project.yaml +13 -0
- moai_adk/templates/.moai/config/sections/quality.yaml +55 -0
- moai_adk/templates/.moai/config/sections/ralph.yaml +55 -0
- moai_adk/templates/.moai/config/sections/system.yaml +59 -0
- moai_adk/templates/.moai/config/sections/user.yaml +5 -0
- moai_adk/templates/.moai/config/statusline-config.yaml +92 -0
- moai_adk/templates/.moai/llm-configs/glm.json +22 -0
- moai_adk/templates/CLAUDE.ja.md +343 -0
- moai_adk/templates/CLAUDE.ko.md +343 -0
- moai_adk/templates/CLAUDE.md +274 -246
- moai_adk/templates/CLAUDE.zh.md +343 -0
- moai_adk/utils/__init__.py +24 -2
- moai_adk/utils/banner.py +9 -13
- moai_adk/utils/common.py +331 -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/utils/timeout.py +160 -0
- moai_adk/utils/toon_utils.py +256 -0
- moai_adk/version.py +22 -0
- moai_adk-1.1.0.dist-info/METADATA +2443 -0
- moai_adk-1.1.0.dist-info/RECORD +701 -0
- {moai_adk-0.8.0.dist-info → moai_adk-1.1.0.dist-info}/WHEEL +1 -1
- moai_adk-1.1.0.dist-info/entry_points.txt +5 -0
- moai_adk-1.1.0.dist-info/licenses/LICENSE +99 -0
- moai_adk/cli/commands/backup.py +0 -80
- moai_adk/templates/.claude/agents/alfred/cc-manager.md +0 -293
- moai_adk/templates/.claude/agents/alfred/debug-helper.md +0 -196
- moai_adk/templates/.claude/agents/alfred/doc-syncer.md +0 -207
- moai_adk/templates/.claude/agents/alfred/git-manager.md +0 -375
- moai_adk/templates/.claude/agents/alfred/implementation-planner.md +0 -343
- moai_adk/templates/.claude/agents/alfred/project-manager.md +0 -246
- moai_adk/templates/.claude/agents/alfred/quality-gate.md +0 -320
- moai_adk/templates/.claude/agents/alfred/skill-factory.md +0 -837
- moai_adk/templates/.claude/agents/alfred/spec-builder.md +0 -272
- moai_adk/templates/.claude/agents/alfred/tag-agent.md +0 -265
- moai_adk/templates/.claude/agents/alfred/tdd-implementer.md +0 -311
- moai_adk/templates/.claude/agents/alfred/trust-checker.md +0 -352
- moai_adk/templates/.claude/commands/alfred/0-project.md +0 -1184
- moai_adk/templates/.claude/commands/alfred/1-plan.md +0 -665
- moai_adk/templates/.claude/commands/alfred/2-run.md +0 -488
- moai_adk/templates/.claude/commands/alfred/3-sync.md +0 -623
- moai_adk/templates/.claude/hooks/alfred/HOOK_SCHEMA_VALIDATION.md +0 -313
- moai_adk/templates/.claude/hooks/alfred/README.md +0 -230
- moai_adk/templates/.claude/hooks/alfred/alfred_hooks.py +0 -174
- moai_adk/templates/.claude/hooks/alfred/core/__init__.py +0 -170
- moai_adk/templates/.claude/hooks/alfred/core/context.py +0 -67
- moai_adk/templates/.claude/hooks/alfred/core/project.py +0 -416
- moai_adk/templates/.claude/hooks/alfred/core/tags.py +0 -198
- moai_adk/templates/.claude/hooks/alfred/handlers/__init__.py +0 -21
- moai_adk/templates/.claude/hooks/alfred/handlers/notification.py +0 -25
- moai_adk/templates/.claude/hooks/alfred/handlers/session.py +0 -161
- moai_adk/templates/.claude/hooks/alfred/handlers/tool.py +0 -90
- moai_adk/templates/.claude/hooks/alfred/handlers/user.py +0 -42
- moai_adk/templates/.claude/hooks/alfred/test_hook_output.py +0 -175
- moai_adk/templates/.claude/output-styles/alfred/agentic-coding.md +0 -640
- moai_adk/templates/.claude/output-styles/alfred/moai-adk-learning.md +0 -696
- moai_adk/templates/.claude/output-styles/alfred/study-with-alfred.md +0 -474
- 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-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-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-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-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-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-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-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-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-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-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 -113
- 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-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-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-skill-factory/CHECKLIST.md +0 -482
- moai_adk/templates/.claude/skills/moai-skill-factory/EXAMPLES.md +0 -278
- moai_adk/templates/.claude/skills/moai-skill-factory/INTERACTIVE-DISCOVERY.md +0 -524
- moai_adk/templates/.claude/skills/moai-skill-factory/METADATA.md +0 -477
- moai_adk/templates/.claude/skills/moai-skill-factory/PARALLEL-ANALYSIS-REPORT.md +0 -429
- moai_adk/templates/.claude/skills/moai-skill-factory/PYTHON-VERSION-MATRIX.md +0 -391
- moai_adk/templates/.claude/skills/moai-skill-factory/SKILL-FACTORY-WORKFLOW.md +0 -431
- moai_adk/templates/.claude/skills/moai-skill-factory/SKILL-UPDATE-ADVISOR.md +0 -577
- moai_adk/templates/.claude/skills/moai-skill-factory/SKILL.md +0 -271
- moai_adk/templates/.claude/skills/moai-skill-factory/STEP-BY-STEP-GUIDE.md +0 -466
- moai_adk/templates/.claude/skills/moai-skill-factory/STRUCTURE.md +0 -583
- moai_adk/templates/.claude/skills/moai-skill-factory/WEB-RESEARCH.md +0 -526
- moai_adk/templates/.claude/skills/moai-skill-factory/reference.md +0 -465
- moai_adk/templates/.claude/skills/moai-skill-factory/scripts/generate-structure.sh +0 -328
- moai_adk/templates/.claude/skills/moai-skill-factory/scripts/validate-skill.sh +0 -312
- moai_adk/templates/.claude/skills/moai-skill-factory/templates/SKILL_TEMPLATE.md +0 -245
- moai_adk/templates/.claude/skills/moai-skill-factory/templates/examples-template.md +0 -285
- moai_adk/templates/.claude/skills/moai-skill-factory/templates/reference-template.md +0 -278
- moai_adk/templates/.claude/skills/moai-skill-factory/templates/scripts-template.sh +0 -303
- moai_adk/templates/.claude/skills/moai-spec-authoring/README.md +0 -137
- moai_adk/templates/.claude/skills/moai-spec-authoring/SKILL.md +0 -218
- moai_adk/templates/.claude/skills/moai-spec-authoring/examples/validate-spec.sh +0 -161
- moai_adk/templates/.claude/skills/moai-spec-authoring/examples.md +0 -541
- moai_adk/templates/.claude/skills/moai-spec-authoring/reference.md +0 -622
- moai_adk/templates/.github/ISSUE_TEMPLATE/spec.yml +0 -176
- moai_adk/templates/.github/PULL_REQUEST_TEMPLATE.md +0 -69
- moai_adk/templates/.github/workflows/moai-gitflow.yml +0 -256
- moai_adk/templates/.moai/config.json +0 -96
- moai_adk/templates/.moai/memory/CLAUDE-AGENTS-GUIDE.md +0 -208
- moai_adk/templates/.moai/memory/CLAUDE-PRACTICES.md +0 -369
- moai_adk/templates/.moai/memory/CLAUDE-RULES.md +0 -539
- moai_adk/templates/.moai/memory/CONFIG-SCHEMA.md +0 -444
- moai_adk/templates/.moai/memory/DEVELOPMENT-GUIDE.md +0 -344
- moai_adk/templates/.moai/memory/GITFLOW-PROTECTION-POLICY.md +0 -220
- moai_adk/templates/.moai/memory/SKILLS-DESCRIPTION-POLICY.md +0 -218
- moai_adk/templates/.moai/memory/SPEC-METADATA.md +0 -356
- moai_adk/templates/.moai/memory/config-schema.md +0 -444
- moai_adk/templates/.moai/memory/gitflow-protection-policy.md +0 -220
- moai_adk/templates/.moai/memory/spec-metadata.md +0 -356
- moai_adk/templates/.moai/project/product.md +0 -161
- moai_adk/templates/.moai/project/structure.md +0 -156
- moai_adk/templates/.moai/project/tech.md +0 -227
- moai_adk/templates/__init__.py +0 -2
- moai_adk-0.8.0.dist-info/METADATA +0 -1722
- moai_adk-0.8.0.dist-info/RECORD +0 -282
- moai_adk-0.8.0.dist-info/entry_points.txt +0 -2
- moai_adk-0.8.0.dist-info/licenses/LICENSE +0 -21
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,34 +26,56 @@ 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
|
+
|
|
41
|
+
# type: ignore
|
|
42
|
+
|
|
40
43
|
from __future__ import annotations
|
|
41
44
|
|
|
42
45
|
import json
|
|
46
|
+
import logging
|
|
47
|
+
import os
|
|
48
|
+
import shutil
|
|
43
49
|
import subprocess
|
|
50
|
+
import sys
|
|
44
51
|
from datetime import datetime
|
|
45
52
|
from pathlib import Path
|
|
46
|
-
from typing import Any, cast
|
|
53
|
+
from typing import Any, Union, cast
|
|
47
54
|
|
|
48
55
|
import click
|
|
56
|
+
import yaml
|
|
49
57
|
from packaging import version
|
|
50
58
|
from rich.console import Console
|
|
51
59
|
|
|
52
60
|
from moai_adk import __version__
|
|
61
|
+
from moai_adk.core.merge import MergeAnalyzer
|
|
62
|
+
from moai_adk.core.migration import VersionMigrator
|
|
63
|
+
from moai_adk.core.migration.alfred_to_moai_migrator import AlfredToMoaiMigrator
|
|
64
|
+
|
|
65
|
+
# Import new custom element restoration modules
|
|
66
|
+
from moai_adk.core.migration.custom_element_scanner import create_custom_element_scanner
|
|
67
|
+
from moai_adk.core.migration.selective_restorer import create_selective_restorer
|
|
68
|
+
from moai_adk.core.migration.user_selection_ui import create_user_selection_ui
|
|
53
69
|
from moai_adk.core.template.processor import TemplateProcessor
|
|
70
|
+
from moai_adk.utils.common import reset_stdin
|
|
54
71
|
|
|
55
|
-
|
|
72
|
+
# Force UTF-8 encoding for Windows compatibility
|
|
73
|
+
# Windows PowerShell/Console uses 'charmap' by default, which can't encode emojis
|
|
74
|
+
if sys.platform == "win32":
|
|
75
|
+
console = Console(force_terminal=True, legacy_windows=False)
|
|
76
|
+
else:
|
|
77
|
+
console = Console()
|
|
78
|
+
logger = logging.getLogger(__name__)
|
|
56
79
|
|
|
57
80
|
# Constants for tool detection
|
|
58
81
|
TOOL_DETECTION_TIMEOUT = 5 # seconds
|
|
@@ -61,33 +84,76 @@ PIPX_COMMAND = ["pipx", "upgrade", "moai-adk"]
|
|
|
61
84
|
PIP_COMMAND = ["pip", "install", "--upgrade", "moai-adk"]
|
|
62
85
|
|
|
63
86
|
|
|
64
|
-
# @CODE:UPDATE-REFACTOR-002-004
|
|
65
87
|
# Custom exceptions for better error handling
|
|
66
88
|
class UpdateError(Exception):
|
|
67
89
|
"""Base exception for update operations."""
|
|
90
|
+
|
|
68
91
|
pass
|
|
69
92
|
|
|
70
93
|
|
|
71
94
|
class InstallerNotFoundError(UpdateError):
|
|
72
95
|
"""Raised when no package installer detected."""
|
|
96
|
+
|
|
73
97
|
pass
|
|
74
98
|
|
|
75
99
|
|
|
76
100
|
class NetworkError(UpdateError):
|
|
77
101
|
"""Raised when network operation fails."""
|
|
102
|
+
|
|
78
103
|
pass
|
|
79
104
|
|
|
80
105
|
|
|
81
106
|
class UpgradeError(UpdateError):
|
|
82
107
|
"""Raised when package upgrade fails."""
|
|
108
|
+
|
|
83
109
|
pass
|
|
84
110
|
|
|
85
111
|
|
|
86
112
|
class TemplateSyncError(UpdateError):
|
|
87
113
|
"""Raised when template sync fails."""
|
|
114
|
+
|
|
88
115
|
pass
|
|
89
116
|
|
|
90
117
|
|
|
118
|
+
def _get_config_path(project_path: Path) -> tuple[Path, bool]:
|
|
119
|
+
"""Get config file path, preferring YAML over JSON.
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
Tuple of (config_path, is_yaml)
|
|
123
|
+
"""
|
|
124
|
+
yaml_path = project_path / ".moai" / "config" / "config.yaml"
|
|
125
|
+
json_path = project_path / ".moai" / "config" / "config.json"
|
|
126
|
+
|
|
127
|
+
if yaml_path.exists():
|
|
128
|
+
return yaml_path, True
|
|
129
|
+
return json_path, False
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _load_config(config_path: Path) -> dict[str, Any]:
|
|
133
|
+
"""Load config from YAML or JSON file."""
|
|
134
|
+
if not config_path.exists():
|
|
135
|
+
return {}
|
|
136
|
+
|
|
137
|
+
is_yaml = config_path.suffix in (".yaml", ".yml")
|
|
138
|
+
content = config_path.read_text(encoding="utf-8")
|
|
139
|
+
|
|
140
|
+
if is_yaml:
|
|
141
|
+
return yaml.safe_load(content) or {}
|
|
142
|
+
return json.loads(content)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _save_config(config_path: Path, config_data: dict[str, Any]) -> None:
|
|
146
|
+
"""Save config to YAML or JSON file."""
|
|
147
|
+
is_yaml = config_path.suffix in (".yaml", ".yml")
|
|
148
|
+
|
|
149
|
+
if is_yaml:
|
|
150
|
+
content = yaml.safe_dump(config_data, default_flow_style=False, allow_unicode=True, sort_keys=False)
|
|
151
|
+
else:
|
|
152
|
+
content = json.dumps(config_data, indent=2, ensure_ascii=False) + "\n"
|
|
153
|
+
|
|
154
|
+
config_path.write_text(content, encoding="utf-8")
|
|
155
|
+
|
|
156
|
+
|
|
91
157
|
def _is_installed_via_uv_tool() -> bool:
|
|
92
158
|
"""Check if moai-adk installed via uv tool.
|
|
93
159
|
|
|
@@ -100,7 +166,7 @@ def _is_installed_via_uv_tool() -> bool:
|
|
|
100
166
|
capture_output=True,
|
|
101
167
|
text=True,
|
|
102
168
|
timeout=TOOL_DETECTION_TIMEOUT,
|
|
103
|
-
check=False
|
|
169
|
+
check=False,
|
|
104
170
|
)
|
|
105
171
|
return result.returncode == 0 and "moai-adk" in result.stdout
|
|
106
172
|
except (FileNotFoundError, subprocess.TimeoutExpired, OSError):
|
|
@@ -119,7 +185,7 @@ def _is_installed_via_pipx() -> bool:
|
|
|
119
185
|
capture_output=True,
|
|
120
186
|
text=True,
|
|
121
187
|
timeout=TOOL_DETECTION_TIMEOUT,
|
|
122
|
-
check=False
|
|
188
|
+
check=False,
|
|
123
189
|
)
|
|
124
190
|
return result.returncode == 0 and "moai-adk" in result.stdout
|
|
125
191
|
except (FileNotFoundError, subprocess.TimeoutExpired, OSError):
|
|
@@ -138,14 +204,13 @@ def _is_installed_via_pip() -> bool:
|
|
|
138
204
|
capture_output=True,
|
|
139
205
|
text=True,
|
|
140
206
|
timeout=TOOL_DETECTION_TIMEOUT,
|
|
141
|
-
check=False
|
|
207
|
+
check=False,
|
|
142
208
|
)
|
|
143
209
|
return result.returncode == 0
|
|
144
210
|
except (FileNotFoundError, subprocess.TimeoutExpired, OSError):
|
|
145
211
|
return False
|
|
146
212
|
|
|
147
213
|
|
|
148
|
-
# @CODE:UPDATE-REFACTOR-002-001
|
|
149
214
|
def _detect_tool_installer() -> list[str] | None:
|
|
150
215
|
"""Detect which tool installed moai-adk.
|
|
151
216
|
|
|
@@ -185,7 +250,6 @@ def _detect_tool_installer() -> list[str] | None:
|
|
|
185
250
|
return None
|
|
186
251
|
|
|
187
252
|
|
|
188
|
-
# @CODE:UPDATE-REFACTOR-002-002
|
|
189
253
|
def _get_current_version() -> str:
|
|
190
254
|
"""Get currently installed moai-adk version.
|
|
191
255
|
|
|
@@ -207,10 +271,10 @@ def _get_latest_version() -> str:
|
|
|
207
271
|
Raises:
|
|
208
272
|
RuntimeError: If PyPI API unavailable or parsing fails
|
|
209
273
|
"""
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
import urllib.request
|
|
274
|
+
import urllib.error
|
|
275
|
+
import urllib.request
|
|
213
276
|
|
|
277
|
+
try:
|
|
214
278
|
url = "https://pypi.org/pypi/moai-adk/json"
|
|
215
279
|
with urllib.request.urlopen(url, timeout=5) as response: # nosec B310 - URL is hardcoded HTTPS to PyPI API, no user input
|
|
216
280
|
data = json.loads(response.read().decode("utf-8"))
|
|
@@ -242,7 +306,6 @@ def _compare_versions(current: str, latest: str) -> int:
|
|
|
242
306
|
return 1
|
|
243
307
|
|
|
244
308
|
|
|
245
|
-
# @CODE:UPDATE-REFACTOR-002-006
|
|
246
309
|
def _get_package_config_version() -> str:
|
|
247
310
|
"""Get the current package template version.
|
|
248
311
|
|
|
@@ -257,11 +320,10 @@ def _get_package_config_version() -> str:
|
|
|
257
320
|
return __version__
|
|
258
321
|
|
|
259
322
|
|
|
260
|
-
# @CODE:UPDATE-REFACTOR-002-007
|
|
261
323
|
def _get_project_config_version(project_path: Path) -> str:
|
|
262
324
|
"""Get current project config.json template version.
|
|
263
325
|
|
|
264
|
-
This reads the project's .moai/config.json to determine the current
|
|
326
|
+
This reads the project's .moai/config/config.json to determine the current
|
|
265
327
|
template version that the project is configured with.
|
|
266
328
|
|
|
267
329
|
Args:
|
|
@@ -272,30 +334,592 @@ def _get_project_config_version(project_path: Path) -> str:
|
|
|
272
334
|
Returns "0.0.0" if template_version field not found (indicates no prior sync)
|
|
273
335
|
|
|
274
336
|
Raises:
|
|
275
|
-
ValueError: If config
|
|
337
|
+
ValueError: If config file exists but cannot be parsed
|
|
276
338
|
"""
|
|
277
|
-
|
|
339
|
+
|
|
340
|
+
def _is_placeholder_val(value: str) -> bool:
|
|
341
|
+
"""Check if value contains unsubstituted template placeholders."""
|
|
342
|
+
return isinstance(value, str) and value.startswith("{{") and value.endswith("}}")
|
|
343
|
+
|
|
344
|
+
config_path, _ = _get_config_path(project_path)
|
|
278
345
|
|
|
279
346
|
if not config_path.exists():
|
|
280
347
|
# No config yet, treat as version 0.0.0 (needs initial sync)
|
|
281
348
|
return "0.0.0"
|
|
282
349
|
|
|
283
350
|
try:
|
|
284
|
-
config_data =
|
|
351
|
+
config_data = _load_config(config_path)
|
|
285
352
|
# Check for template_version in project section
|
|
286
353
|
template_version = config_data.get("project", {}).get("template_version")
|
|
287
|
-
if template_version:
|
|
354
|
+
if template_version and not _is_placeholder_val(template_version):
|
|
288
355
|
return template_version
|
|
289
356
|
|
|
290
357
|
# Fallback to moai version if no template_version exists
|
|
291
358
|
moai_version = config_data.get("moai", {}).get("version")
|
|
292
|
-
if moai_version:
|
|
359
|
+
if moai_version and not _is_placeholder_val(moai_version):
|
|
293
360
|
return moai_version
|
|
294
361
|
|
|
295
|
-
# If
|
|
362
|
+
# If values are placeholders or don't exist, treat as uninitialized (0.0.0 triggers sync)
|
|
296
363
|
return "0.0.0"
|
|
297
|
-
except json.JSONDecodeError as e:
|
|
298
|
-
raise ValueError(f"Failed to parse project config
|
|
364
|
+
except (json.JSONDecodeError, yaml.YAMLError) as e:
|
|
365
|
+
raise ValueError(f"Failed to parse project config: {e}") from e
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def _ask_merge_strategy(yes: bool = False) -> str:
|
|
369
|
+
"""
|
|
370
|
+
Ask user to choose merge strategy via CLI prompt.
|
|
371
|
+
|
|
372
|
+
Args:
|
|
373
|
+
yes: If True, auto-select "auto" (for --yes flag)
|
|
374
|
+
|
|
375
|
+
Returns:
|
|
376
|
+
"auto" or "manual"
|
|
377
|
+
"""
|
|
378
|
+
if yes:
|
|
379
|
+
return "auto"
|
|
380
|
+
|
|
381
|
+
console.print("\n[cyan]🔀 Choose merge strategy:[/cyan]")
|
|
382
|
+
console.print("[cyan] [1] Auto-merge (default)[/cyan]")
|
|
383
|
+
console.print("[dim] → Template installs fresh + user changes preserved + minimal conflicts[/dim]")
|
|
384
|
+
console.print("[cyan] [2] Manual merge[/cyan]")
|
|
385
|
+
console.print("[dim] → Backup preserved + merge guide generated + you control merging[/dim]")
|
|
386
|
+
|
|
387
|
+
response = click.prompt("Select [1 or 2]", default="1")
|
|
388
|
+
if response == "2":
|
|
389
|
+
return "manual"
|
|
390
|
+
return "auto"
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
def _generate_manual_merge_guide(backup_path: Path, template_path: Path, project_path: Path) -> Path:
|
|
394
|
+
"""
|
|
395
|
+
Generate comprehensive merge guide for manual merging.
|
|
396
|
+
|
|
397
|
+
Args:
|
|
398
|
+
backup_path: Path to backup directory
|
|
399
|
+
template_path: Path to template directory
|
|
400
|
+
project_path: Project root path
|
|
401
|
+
|
|
402
|
+
Returns:
|
|
403
|
+
Path to generated merge guide
|
|
404
|
+
"""
|
|
405
|
+
guide_dir = project_path / ".moai" / "guides"
|
|
406
|
+
guide_dir.mkdir(parents=True, exist_ok=True)
|
|
407
|
+
|
|
408
|
+
guide_path = guide_dir / "merge-guide.md"
|
|
409
|
+
|
|
410
|
+
# Find changed files
|
|
411
|
+
changed_files = []
|
|
412
|
+
backup_claude = backup_path / ".claude"
|
|
413
|
+
backup_path / ".moai"
|
|
414
|
+
|
|
415
|
+
# Compare .claude/
|
|
416
|
+
if backup_claude.exists():
|
|
417
|
+
for file in backup_claude.rglob("*"):
|
|
418
|
+
if file.is_file():
|
|
419
|
+
rel_path = file.relative_to(backup_path)
|
|
420
|
+
current_file = project_path / rel_path
|
|
421
|
+
if current_file.exists():
|
|
422
|
+
if file.read_text(encoding="utf-8", errors="ignore") != current_file.read_text(
|
|
423
|
+
encoding="utf-8", errors="ignore"
|
|
424
|
+
):
|
|
425
|
+
changed_files.append(f" - {rel_path}")
|
|
426
|
+
else:
|
|
427
|
+
changed_files.append(f" - {rel_path} (new)")
|
|
428
|
+
|
|
429
|
+
# Generate guide
|
|
430
|
+
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
431
|
+
guide_content = f"""# Merge Guide - Manual Merge Mode
|
|
432
|
+
|
|
433
|
+
**Generated**: {timestamp}
|
|
434
|
+
**Backup Location**: `{backup_path.relative_to(project_path)}/`
|
|
435
|
+
|
|
436
|
+
## Summary
|
|
437
|
+
|
|
438
|
+
During this update, the following files were changed:
|
|
439
|
+
|
|
440
|
+
{chr(10).join(changed_files) if changed_files else " (No changes detected)"}
|
|
441
|
+
|
|
442
|
+
## How to Merge
|
|
443
|
+
|
|
444
|
+
### Option 1: Using diff (Terminal)
|
|
445
|
+
|
|
446
|
+
```bash
|
|
447
|
+
# Compare specific files
|
|
448
|
+
diff {backup_path.name}/.claude/settings.json .claude/settings.json
|
|
449
|
+
|
|
450
|
+
# View all differences
|
|
451
|
+
diff -r {backup_path.name}/ .
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### Option 2: Using Visual Merge Tool
|
|
455
|
+
|
|
456
|
+
```bash
|
|
457
|
+
# macOS/Linux - Using meld
|
|
458
|
+
meld {backup_path.relative_to(project_path)}/ .
|
|
459
|
+
|
|
460
|
+
# Using VSCode
|
|
461
|
+
code --diff {backup_path.relative_to(project_path)}/.claude/settings.json .claude/settings.json
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
### Option 3: Manual Line-by-Line
|
|
465
|
+
|
|
466
|
+
1. Open backup file in your editor
|
|
467
|
+
2. Open current file side-by-side
|
|
468
|
+
3. Manually copy your customizations
|
|
469
|
+
|
|
470
|
+
## Key Files to Review
|
|
471
|
+
|
|
472
|
+
### .claude/settings.json
|
|
473
|
+
- Contains MCP servers, hooks, environment variables
|
|
474
|
+
- **Action**: Restore any custom MCP servers and environment variables
|
|
475
|
+
- **Location**: {backup_path.relative_to(project_path)}/.claude/settings.json
|
|
476
|
+
|
|
477
|
+
### .moai/config/config.json
|
|
478
|
+
- Contains project configuration and metadata
|
|
479
|
+
- **Action**: Verify user-specific settings are preserved
|
|
480
|
+
- **Location**: {backup_path.relative_to(project_path)}/.moai/config/config.json
|
|
481
|
+
|
|
482
|
+
### .claude/commands/, .claude/agents/, .claude/hooks/
|
|
483
|
+
- Contains custom scripts and automation
|
|
484
|
+
- **Action**: Restore any custom scripts outside of /moai/ folders
|
|
485
|
+
- **Location**: {backup_path.relative_to(project_path)}/.claude/
|
|
486
|
+
|
|
487
|
+
## Migration Checklist
|
|
488
|
+
|
|
489
|
+
- [ ] Compare `.claude/settings.json`
|
|
490
|
+
- [ ] Restore custom MCP servers
|
|
491
|
+
- [ ] Restore environment variables
|
|
492
|
+
- [ ] Verify hooks are properly configured
|
|
493
|
+
|
|
494
|
+
- [ ] Review `.moai/config/config.json`
|
|
495
|
+
- [ ] Check version was updated
|
|
496
|
+
- [ ] Verify user settings preserved
|
|
497
|
+
|
|
498
|
+
- [ ] Restore custom scripts
|
|
499
|
+
- [ ] Any custom commands outside /moai/
|
|
500
|
+
- [ ] Any custom agents outside /moai/
|
|
501
|
+
- [ ] Any custom hooks outside /moai/
|
|
502
|
+
|
|
503
|
+
- [ ] Run tests
|
|
504
|
+
```bash
|
|
505
|
+
uv run pytest
|
|
506
|
+
moai-adk validate
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
- [ ] Commit changes
|
|
510
|
+
```bash
|
|
511
|
+
git add .
|
|
512
|
+
git commit -m "merge: Update templates with manual merge"
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
## Rollback if Needed
|
|
516
|
+
|
|
517
|
+
If you want to cancel and restore the backup:
|
|
518
|
+
|
|
519
|
+
```bash
|
|
520
|
+
# Restore everything from backup
|
|
521
|
+
cp -r {backup_path.relative_to(project_path)}/.claude .
|
|
522
|
+
cp -r {backup_path.relative_to(project_path)}/.moai .
|
|
523
|
+
cp {backup_path.relative_to(project_path)}/CLAUDE.md .
|
|
524
|
+
|
|
525
|
+
# Or restore specific files
|
|
526
|
+
cp {backup_path.relative_to(project_path)}/.claude/settings.json .claude/
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
## Questions?
|
|
530
|
+
|
|
531
|
+
If you encounter merge conflicts or issues:
|
|
532
|
+
|
|
533
|
+
1. Check the backup folder for original files
|
|
534
|
+
2. Compare line-by-line using diff tools
|
|
535
|
+
3. Consult documentation: https://adk.mo.ai.kr/update-merge
|
|
536
|
+
|
|
537
|
+
---
|
|
538
|
+
|
|
539
|
+
**Backup**: `{backup_path}/`
|
|
540
|
+
**Generated**: {timestamp}
|
|
541
|
+
"""
|
|
542
|
+
|
|
543
|
+
guide_path.write_text(guide_content, encoding="utf-8")
|
|
544
|
+
logger.info(f"✅ Merge guide created: {guide_path}")
|
|
545
|
+
return guide_path
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
def _migrate_legacy_logs(project_path: Path, dry_run: bool = False) -> bool:
|
|
549
|
+
"""Migrate legacy log files to unified directory structure.
|
|
550
|
+
|
|
551
|
+
Creates new unified directory structure (.moai/docs/, .moai/logs/archive/) and
|
|
552
|
+
migrates files from legacy locations to new unified structure:
|
|
553
|
+
- .moai/memory/last-session-state.json → .moai/logs/sessions/
|
|
554
|
+
- .moai/error_logs/ → .moai/logs/errors/
|
|
555
|
+
- .moai/reports/ → .moai/docs/reports/
|
|
556
|
+
|
|
557
|
+
Args:
|
|
558
|
+
project_path: Project directory path (absolute)
|
|
559
|
+
dry_run: If True, only simulate migration without making changes
|
|
560
|
+
|
|
561
|
+
Returns:
|
|
562
|
+
True if migration succeeded or no migration needed, False otherwise
|
|
563
|
+
|
|
564
|
+
Raises:
|
|
565
|
+
Exception: If migration fails during actual execution
|
|
566
|
+
"""
|
|
567
|
+
try:
|
|
568
|
+
# Define source and target directories
|
|
569
|
+
legacy_memory = project_path / ".moai" / "memory"
|
|
570
|
+
legacy_error_logs = project_path / ".moai" / "error_logs"
|
|
571
|
+
legacy_reports = project_path / ".moai" / "reports"
|
|
572
|
+
|
|
573
|
+
# Create new unified directory structure
|
|
574
|
+
new_logs_dir = project_path / ".moai" / "logs"
|
|
575
|
+
new_docs_dir = project_path / ".moai" / "docs"
|
|
576
|
+
new_sessions_dir = new_logs_dir / "sessions"
|
|
577
|
+
new_errors_dir = new_logs_dir / "errors"
|
|
578
|
+
new_archive_dir = new_logs_dir / "archive"
|
|
579
|
+
new_docs_reports_dir = new_docs_dir / "reports"
|
|
580
|
+
|
|
581
|
+
migration_log = []
|
|
582
|
+
files_migrated = 0
|
|
583
|
+
files_skipped = 0
|
|
584
|
+
|
|
585
|
+
# Check if any legacy directories exist
|
|
586
|
+
has_legacy_files = legacy_memory.exists() or legacy_error_logs.exists() or legacy_reports.exists()
|
|
587
|
+
|
|
588
|
+
if not has_legacy_files:
|
|
589
|
+
if not dry_run:
|
|
590
|
+
# Create new directory structure anyway for consistency
|
|
591
|
+
new_logs_dir.mkdir(parents=True, exist_ok=True)
|
|
592
|
+
new_docs_dir.mkdir(parents=True, exist_ok=True)
|
|
593
|
+
new_sessions_dir.mkdir(parents=True, exist_ok=True)
|
|
594
|
+
new_errors_dir.mkdir(parents=True, exist_ok=True)
|
|
595
|
+
new_archive_dir.mkdir(parents=True, exist_ok=True)
|
|
596
|
+
new_docs_reports_dir.mkdir(parents=True, exist_ok=True)
|
|
597
|
+
return True
|
|
598
|
+
|
|
599
|
+
if dry_run:
|
|
600
|
+
console.print("[cyan]🔍 Legacy log migration (dry run):[/cyan]")
|
|
601
|
+
|
|
602
|
+
# Create new directories if not dry run
|
|
603
|
+
if not dry_run:
|
|
604
|
+
new_logs_dir.mkdir(parents=True, exist_ok=True)
|
|
605
|
+
new_docs_dir.mkdir(parents=True, exist_ok=True)
|
|
606
|
+
new_sessions_dir.mkdir(parents=True, exist_ok=True)
|
|
607
|
+
new_errors_dir.mkdir(parents=True, exist_ok=True)
|
|
608
|
+
new_archive_dir.mkdir(parents=True, exist_ok=True)
|
|
609
|
+
new_docs_reports_dir.mkdir(parents=True, exist_ok=True)
|
|
610
|
+
|
|
611
|
+
# Migration 1: .moai/memory/last-session-state.json → .moai/logs/sessions/
|
|
612
|
+
if legacy_memory.exists():
|
|
613
|
+
session_file = legacy_memory / "last-session-state.json"
|
|
614
|
+
if session_file.exists():
|
|
615
|
+
target_file = new_sessions_dir / "last-session-state.json"
|
|
616
|
+
|
|
617
|
+
if target_file.exists():
|
|
618
|
+
files_skipped += 1
|
|
619
|
+
migration_log.append(f"Skipped: {session_file.relative_to(project_path)} (target already exists)")
|
|
620
|
+
else:
|
|
621
|
+
if not dry_run:
|
|
622
|
+
shutil.copy2(session_file, target_file)
|
|
623
|
+
# Preserve original timestamp
|
|
624
|
+
shutil.copystat(session_file, target_file)
|
|
625
|
+
src_path = session_file.relative_to(project_path)
|
|
626
|
+
dst_path = target_file.relative_to(project_path)
|
|
627
|
+
migration_log.append(f"Migrated: {src_path} → {dst_path}")
|
|
628
|
+
else:
|
|
629
|
+
src_path = session_file.relative_to(project_path)
|
|
630
|
+
dst_path = target_file.relative_to(project_path)
|
|
631
|
+
migration_log.append(f"Would migrate: {src_path} → {dst_path}")
|
|
632
|
+
files_migrated += 1
|
|
633
|
+
|
|
634
|
+
# Migration 2: .moai/error_logs/ → .moai/logs/errors/
|
|
635
|
+
if legacy_error_logs.exists() and legacy_error_logs.is_dir():
|
|
636
|
+
for error_file in legacy_error_logs.rglob("*"):
|
|
637
|
+
if error_file.is_file():
|
|
638
|
+
relative_path = error_file.relative_to(legacy_error_logs)
|
|
639
|
+
target_file = new_errors_dir / relative_path
|
|
640
|
+
|
|
641
|
+
# Ensure target directory exists
|
|
642
|
+
if not dry_run:
|
|
643
|
+
target_file.parent.mkdir(parents=True, exist_ok=True)
|
|
644
|
+
|
|
645
|
+
if target_file.exists():
|
|
646
|
+
files_skipped += 1
|
|
647
|
+
error_path = error_file.relative_to(project_path)
|
|
648
|
+
migration_log.append(f"Skipped: {error_path} (target already exists)")
|
|
649
|
+
else:
|
|
650
|
+
if not dry_run:
|
|
651
|
+
shutil.copy2(error_file, target_file)
|
|
652
|
+
shutil.copystat(error_file, target_file)
|
|
653
|
+
error_path = error_file.relative_to(project_path)
|
|
654
|
+
target_path = target_file.relative_to(project_path)
|
|
655
|
+
migration_log.append(f"Migrated: {error_path} → {target_path}")
|
|
656
|
+
else:
|
|
657
|
+
error_path = error_file.relative_to(project_path)
|
|
658
|
+
target_path = target_file.relative_to(project_path)
|
|
659
|
+
migration_log.append(f"Would migrate: {error_path} → {target_path}")
|
|
660
|
+
files_migrated += 1
|
|
661
|
+
|
|
662
|
+
# Migration 3: .moai/reports/ → .moai/docs/reports/
|
|
663
|
+
if legacy_reports.exists() and legacy_reports.is_dir():
|
|
664
|
+
for report_file in legacy_reports.rglob("*"):
|
|
665
|
+
if report_file.is_file():
|
|
666
|
+
relative_path = report_file.relative_to(legacy_reports)
|
|
667
|
+
target_file = new_docs_reports_dir / relative_path
|
|
668
|
+
|
|
669
|
+
# Ensure target directory exists
|
|
670
|
+
if not dry_run:
|
|
671
|
+
target_file.parent.mkdir(parents=True, exist_ok=True)
|
|
672
|
+
|
|
673
|
+
if target_file.exists():
|
|
674
|
+
files_skipped += 1
|
|
675
|
+
report_path = report_file.relative_to(project_path)
|
|
676
|
+
migration_log.append(f"Skipped: {report_path} (target already exists)")
|
|
677
|
+
else:
|
|
678
|
+
if not dry_run:
|
|
679
|
+
shutil.copy2(report_file, target_file)
|
|
680
|
+
shutil.copystat(report_file, target_file)
|
|
681
|
+
report_path = report_file.relative_to(project_path)
|
|
682
|
+
target_path = target_file.relative_to(project_path)
|
|
683
|
+
migration_log.append(f"Migrated: {report_path} → {target_path}")
|
|
684
|
+
else:
|
|
685
|
+
report_path = report_file.relative_to(project_path)
|
|
686
|
+
target_path = target_file.relative_to(project_path)
|
|
687
|
+
migration_log.append(f"Would migrate: {report_path} → {target_path}")
|
|
688
|
+
files_migrated += 1
|
|
689
|
+
|
|
690
|
+
# Create migration log
|
|
691
|
+
migration_log_path = new_logs_dir / "migration-log.json"
|
|
692
|
+
if not dry_run and files_migrated > 0:
|
|
693
|
+
migration_data = {
|
|
694
|
+
"migration_timestamp": datetime.now().isoformat(),
|
|
695
|
+
"moai_adk_version": __version__,
|
|
696
|
+
"files_migrated": files_migrated,
|
|
697
|
+
"files_skipped": files_skipped,
|
|
698
|
+
"migration_log": migration_log,
|
|
699
|
+
"legacy_directories_found": [
|
|
700
|
+
str(d.relative_to(project_path))
|
|
701
|
+
for d in [legacy_memory, legacy_error_logs, legacy_reports]
|
|
702
|
+
if d.exists()
|
|
703
|
+
],
|
|
704
|
+
}
|
|
705
|
+
json_content = json.dumps(migration_data, indent=2, ensure_ascii=False)
|
|
706
|
+
migration_log_path.write_text(json_content + "\n", encoding="utf-8")
|
|
707
|
+
|
|
708
|
+
# Display results
|
|
709
|
+
if files_migrated > 0 or files_skipped > 0:
|
|
710
|
+
if dry_run:
|
|
711
|
+
console.print(f" [yellow]Would migrate {files_migrated} files, skip {files_skipped} files[/yellow]")
|
|
712
|
+
else:
|
|
713
|
+
console.print(f" [green]✓ Migrated {files_migrated} legacy log files[/green]")
|
|
714
|
+
if files_skipped > 0:
|
|
715
|
+
console.print(f" [yellow]⚠ Skipped {files_skipped} files (already exist)[/yellow]")
|
|
716
|
+
console.print(f" [dim] Migration log: {migration_log_path.relative_to(project_path)}[/dim]")
|
|
717
|
+
elif has_legacy_files:
|
|
718
|
+
console.print(" [dim] No files to migrate[/dim]")
|
|
719
|
+
|
|
720
|
+
return True
|
|
721
|
+
|
|
722
|
+
except Exception as e:
|
|
723
|
+
console.print(f" [red]✗ Log migration failed: {e}[/red]")
|
|
724
|
+
logger.error(f"Legacy log migration failed: {e}", exc_info=True)
|
|
725
|
+
return False
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
def _detect_stale_cache(upgrade_output: str, current_version: str, latest_version: str) -> bool:
|
|
729
|
+
"""
|
|
730
|
+
Detect if uv cache is stale by comparing versions.
|
|
731
|
+
|
|
732
|
+
A stale cache occurs when PyPI metadata is outdated, causing uv to incorrectly
|
|
733
|
+
report "Nothing to upgrade" even though a newer version exists. This function
|
|
734
|
+
detects this condition by:
|
|
735
|
+
1. Checking if upgrade output contains "Nothing to upgrade"
|
|
736
|
+
2. Verifying that latest version is actually newer than current version
|
|
737
|
+
|
|
738
|
+
Uses packaging.version.parse() for robust semantic version comparison that
|
|
739
|
+
handles pre-releases, dev versions, and other PEP 440 version formats correctly.
|
|
740
|
+
|
|
741
|
+
Args:
|
|
742
|
+
upgrade_output: Output from uv tool upgrade command
|
|
743
|
+
current_version: Currently installed version (string, e.g., "0.8.3")
|
|
744
|
+
latest_version: Latest version available on PyPI (string, e.g., "0.9.0")
|
|
745
|
+
|
|
746
|
+
Returns:
|
|
747
|
+
True if cache is stale (output shows "Nothing to upgrade" but current < latest),
|
|
748
|
+
False otherwise
|
|
749
|
+
|
|
750
|
+
Examples:
|
|
751
|
+
>>> _detect_stale_cache("Nothing to upgrade", "0.8.3", "0.9.0")
|
|
752
|
+
True
|
|
753
|
+
>>> _detect_stale_cache("Updated moai-adk", "0.8.3", "0.9.0")
|
|
754
|
+
False
|
|
755
|
+
>>> _detect_stale_cache("Nothing to upgrade", "0.9.0", "0.9.0")
|
|
756
|
+
False
|
|
757
|
+
"""
|
|
758
|
+
# Check if output indicates no upgrade needed
|
|
759
|
+
if not upgrade_output or "Nothing to upgrade" not in upgrade_output:
|
|
760
|
+
return False
|
|
761
|
+
|
|
762
|
+
# Compare versions using packaging.version
|
|
763
|
+
try:
|
|
764
|
+
current_v = version.parse(current_version)
|
|
765
|
+
latest_v = version.parse(latest_version)
|
|
766
|
+
return current_v < latest_v
|
|
767
|
+
except (version.InvalidVersion, TypeError) as e:
|
|
768
|
+
# Graceful degradation: if version parsing fails, assume cache is not stale
|
|
769
|
+
logger.debug(f"Version parsing failed: {e}")
|
|
770
|
+
return False
|
|
771
|
+
|
|
772
|
+
|
|
773
|
+
def _clear_uv_package_cache(package_name: str = "moai-adk") -> bool:
|
|
774
|
+
"""
|
|
775
|
+
Clear uv cache for specific package.
|
|
776
|
+
|
|
777
|
+
Executes `uv cache clean <package>` with 10-second timeout to prevent
|
|
778
|
+
hanging on network issues. Provides user-friendly error handling for
|
|
779
|
+
various failure scenarios (timeout, missing uv, etc.).
|
|
780
|
+
|
|
781
|
+
Args:
|
|
782
|
+
package_name: Package name to clear cache for (default: "moai-adk")
|
|
783
|
+
|
|
784
|
+
Returns:
|
|
785
|
+
True if cache cleared successfully, False otherwise
|
|
786
|
+
|
|
787
|
+
Exceptions:
|
|
788
|
+
- subprocess.TimeoutExpired: Logged as warning, returns False
|
|
789
|
+
- FileNotFoundError: Logged as warning, returns False
|
|
790
|
+
- Exception: Logged as warning, returns False
|
|
791
|
+
|
|
792
|
+
Examples:
|
|
793
|
+
>>> _clear_uv_package_cache("moai-adk")
|
|
794
|
+
True # If uv cache clean succeeds
|
|
795
|
+
"""
|
|
796
|
+
try:
|
|
797
|
+
result = subprocess.run(
|
|
798
|
+
["uv", "cache", "clean", package_name],
|
|
799
|
+
capture_output=True,
|
|
800
|
+
text=True,
|
|
801
|
+
timeout=10, # 10 second timeout
|
|
802
|
+
check=False,
|
|
803
|
+
)
|
|
804
|
+
|
|
805
|
+
if result.returncode == 0:
|
|
806
|
+
logger.debug(f"UV cache cleared for {package_name}")
|
|
807
|
+
return True
|
|
808
|
+
else:
|
|
809
|
+
logger.warning(f"Failed to clear UV cache: {result.stderr}")
|
|
810
|
+
return False
|
|
811
|
+
|
|
812
|
+
except subprocess.TimeoutExpired:
|
|
813
|
+
logger.warning(f"UV cache clean timed out for {package_name}")
|
|
814
|
+
return False
|
|
815
|
+
except FileNotFoundError:
|
|
816
|
+
logger.warning("UV command not found. Is uv installed?")
|
|
817
|
+
return False
|
|
818
|
+
except Exception as e:
|
|
819
|
+
logger.warning(f"Unexpected error clearing cache: {e}")
|
|
820
|
+
return False
|
|
821
|
+
|
|
822
|
+
|
|
823
|
+
def _execute_upgrade_with_retry(installer_cmd: list[str], package_name: str = "moai-adk") -> bool:
|
|
824
|
+
"""
|
|
825
|
+
Execute upgrade with automatic cache retry on stale detection.
|
|
826
|
+
|
|
827
|
+
Implements a robust 7-stage upgrade flow that handles PyPI cache staleness:
|
|
828
|
+
|
|
829
|
+
Stage 1: First upgrade attempt (up to 60 seconds)
|
|
830
|
+
Stage 2: Check success condition (returncode=0 AND no "Nothing to upgrade")
|
|
831
|
+
Stage 3: Detect stale cache using _detect_stale_cache()
|
|
832
|
+
Stage 4: Show user feedback if stale cache detected
|
|
833
|
+
Stage 5: Clear cache using _clear_uv_package_cache()
|
|
834
|
+
Stage 6: Retry upgrade with same command
|
|
835
|
+
Stage 7: Return final result (success or failure)
|
|
836
|
+
|
|
837
|
+
Retry Logic:
|
|
838
|
+
- Only ONE retry is performed to prevent infinite loops
|
|
839
|
+
- Retry only happens if stale cache is detected AND cache clear succeeds
|
|
840
|
+
- Cache clear failures are reported to user with manual workaround
|
|
841
|
+
|
|
842
|
+
User Feedback:
|
|
843
|
+
- Shows emoji-based status messages for each stage
|
|
844
|
+
- Clear guidance on manual workaround if automatic retry fails
|
|
845
|
+
- All errors logged at WARNING level for debugging
|
|
846
|
+
|
|
847
|
+
Args:
|
|
848
|
+
installer_cmd: Command list from _detect_tool_installer()
|
|
849
|
+
e.g., ["uv", "tool", "upgrade", "moai-adk"]
|
|
850
|
+
package_name: Package name for cache clearing (default: "moai-adk")
|
|
851
|
+
|
|
852
|
+
Returns:
|
|
853
|
+
True if upgrade succeeded (either first attempt or after retry),
|
|
854
|
+
False otherwise
|
|
855
|
+
|
|
856
|
+
Examples:
|
|
857
|
+
>>> # First attempt succeeds
|
|
858
|
+
>>> _execute_upgrade_with_retry(["uv", "tool", "upgrade", "moai-adk"])
|
|
859
|
+
True
|
|
860
|
+
|
|
861
|
+
>>> # First attempt stale, retry succeeds
|
|
862
|
+
>>> _execute_upgrade_with_retry(["uv", "tool", "upgrade", "moai-adk"])
|
|
863
|
+
True # After cache clear and retry
|
|
864
|
+
|
|
865
|
+
Raises:
|
|
866
|
+
subprocess.TimeoutExpired: Re-raised if upgrade command times out
|
|
867
|
+
"""
|
|
868
|
+
# Stage 1: First upgrade attempt
|
|
869
|
+
try:
|
|
870
|
+
result = subprocess.run(installer_cmd, capture_output=True, text=True, timeout=60, check=False)
|
|
871
|
+
except subprocess.TimeoutExpired:
|
|
872
|
+
raise # Re-raise timeout for caller to handle
|
|
873
|
+
except Exception:
|
|
874
|
+
return False
|
|
875
|
+
|
|
876
|
+
# Stage 2: Check if upgrade succeeded without stale cache
|
|
877
|
+
if result.returncode == 0 and "Nothing to upgrade" not in result.stdout:
|
|
878
|
+
return True
|
|
879
|
+
|
|
880
|
+
# Stage 3: Detect stale cache
|
|
881
|
+
try:
|
|
882
|
+
current_version = _get_current_version()
|
|
883
|
+
latest_version = _get_latest_version()
|
|
884
|
+
except RuntimeError:
|
|
885
|
+
# If version check fails, return original result
|
|
886
|
+
return result.returncode == 0
|
|
887
|
+
|
|
888
|
+
if _detect_stale_cache(result.stdout, current_version, latest_version):
|
|
889
|
+
# Stage 4: User feedback
|
|
890
|
+
console.print("[yellow]⚠️ Cache outdated, refreshing...[/yellow]")
|
|
891
|
+
|
|
892
|
+
# Stage 5: Clear cache
|
|
893
|
+
if _clear_uv_package_cache(package_name):
|
|
894
|
+
console.print("[cyan]♻️ Cache cleared, retrying upgrade...[/cyan]")
|
|
895
|
+
|
|
896
|
+
# Stage 6: Retry upgrade
|
|
897
|
+
try:
|
|
898
|
+
result = subprocess.run(
|
|
899
|
+
installer_cmd,
|
|
900
|
+
capture_output=True,
|
|
901
|
+
text=True,
|
|
902
|
+
timeout=60,
|
|
903
|
+
check=False,
|
|
904
|
+
)
|
|
905
|
+
|
|
906
|
+
if result.returncode == 0:
|
|
907
|
+
return True
|
|
908
|
+
else:
|
|
909
|
+
console.print("[red]✗ Upgrade failed after retry[/red]")
|
|
910
|
+
return False
|
|
911
|
+
except subprocess.TimeoutExpired:
|
|
912
|
+
raise # Re-raise timeout
|
|
913
|
+
except Exception:
|
|
914
|
+
return False
|
|
915
|
+
else:
|
|
916
|
+
# Cache clear failed
|
|
917
|
+
console.print("[red]✗ Cache clear failed. Manual workaround:[/red]")
|
|
918
|
+
console.print(" [cyan]uv cache clean moai-adk && moai-adk update[/cyan]")
|
|
919
|
+
return False
|
|
920
|
+
|
|
921
|
+
# Stage 7: Cache is not stale, return original result
|
|
922
|
+
return result.returncode == 0
|
|
299
923
|
|
|
300
924
|
|
|
301
925
|
def _execute_upgrade(installer_cmd: list[str]) -> bool:
|
|
@@ -312,13 +936,7 @@ def _execute_upgrade(installer_cmd: list[str]) -> bool:
|
|
|
312
936
|
subprocess.TimeoutExpired: If upgrade times out
|
|
313
937
|
"""
|
|
314
938
|
try:
|
|
315
|
-
result = subprocess.run(
|
|
316
|
-
installer_cmd,
|
|
317
|
-
capture_output=True,
|
|
318
|
-
text=True,
|
|
319
|
-
timeout=60,
|
|
320
|
-
check=False
|
|
321
|
-
)
|
|
939
|
+
result = subprocess.run(installer_cmd, capture_output=True, text=True, timeout=60, check=False)
|
|
322
940
|
return result.returncode == 0
|
|
323
941
|
except subprocess.TimeoutExpired:
|
|
324
942
|
raise # Re-raise timeout for caller to handle
|
|
@@ -326,19 +944,584 @@ def _execute_upgrade(installer_cmd: list[str]) -> bool:
|
|
|
326
944
|
return False
|
|
327
945
|
|
|
328
946
|
|
|
329
|
-
def
|
|
330
|
-
"""
|
|
947
|
+
def _preserve_user_settings(project_path: Path) -> dict[str, Path | None]:
|
|
948
|
+
"""Back up user-specific settings files before template sync.
|
|
949
|
+
|
|
950
|
+
Args:
|
|
951
|
+
project_path: Project directory path
|
|
952
|
+
|
|
953
|
+
Returns:
|
|
954
|
+
Dictionary with backup paths of preserved files
|
|
955
|
+
"""
|
|
956
|
+
preserved = {}
|
|
957
|
+
claude_dir = project_path / ".claude"
|
|
958
|
+
|
|
959
|
+
# Preserve settings.local.json (user MCP and GLM configuration)
|
|
960
|
+
settings_local = claude_dir / "settings.local.json"
|
|
961
|
+
if settings_local.exists():
|
|
962
|
+
try:
|
|
963
|
+
backup_dir = project_path / ".moai-backups" / "settings-backup"
|
|
964
|
+
backup_dir.mkdir(parents=True, exist_ok=True)
|
|
965
|
+
backup_path = backup_dir / "settings.local.json"
|
|
966
|
+
backup_path.write_text(settings_local.read_text(encoding="utf-8"))
|
|
967
|
+
preserved["settings.local.json"] = backup_path
|
|
968
|
+
console.print(" [cyan]💾 Backed up user settings[/cyan]")
|
|
969
|
+
except Exception as e:
|
|
970
|
+
logger.warning(f"Failed to backup settings.local.json: {e}")
|
|
971
|
+
preserved["settings.local.json"] = None
|
|
972
|
+
else:
|
|
973
|
+
preserved["settings.local.json"] = None
|
|
974
|
+
|
|
975
|
+
return preserved
|
|
976
|
+
|
|
977
|
+
|
|
978
|
+
def _restore_user_settings(project_path: Path, preserved: dict[str, Path | None]) -> bool:
|
|
979
|
+
"""Restore user-specific settings files after template sync.
|
|
980
|
+
|
|
981
|
+
Args:
|
|
982
|
+
project_path: Project directory path
|
|
983
|
+
preserved: Dictionary of backup paths from _preserve_user_settings()
|
|
984
|
+
|
|
985
|
+
Returns:
|
|
986
|
+
True if restoration succeeded, False otherwise
|
|
987
|
+
"""
|
|
988
|
+
claude_dir = project_path / ".claude"
|
|
989
|
+
claude_dir.mkdir(parents=True, exist_ok=True)
|
|
990
|
+
|
|
991
|
+
success = True
|
|
992
|
+
|
|
993
|
+
# Restore settings.local.json
|
|
994
|
+
backup_path = preserved.get("settings.local.json")
|
|
995
|
+
if backup_path is not None:
|
|
996
|
+
try:
|
|
997
|
+
settings_local = claude_dir / "settings.local.json"
|
|
998
|
+
settings_local.write_text(backup_path.read_text(encoding="utf-8"))
|
|
999
|
+
console.print(" [cyan]✓ Restored user settings[/cyan]")
|
|
1000
|
+
except Exception as e:
|
|
1001
|
+
console.print(f" [yellow]⚠️ Failed to restore settings.local.json: {e}[/yellow]")
|
|
1002
|
+
logger.warning(f"Failed to restore settings.local.json: {e}")
|
|
1003
|
+
success = False
|
|
1004
|
+
|
|
1005
|
+
return success
|
|
1006
|
+
|
|
1007
|
+
|
|
1008
|
+
def _get_template_skill_names() -> set[str]:
|
|
1009
|
+
"""Get set of skill folder names from installed template.
|
|
1010
|
+
|
|
1011
|
+
Returns:
|
|
1012
|
+
Set of skill folder names that are part of the template package.
|
|
1013
|
+
"""
|
|
1014
|
+
template_path = Path(__file__).parent.parent.parent / "templates"
|
|
1015
|
+
skills_path = template_path / ".claude" / "skills"
|
|
1016
|
+
|
|
1017
|
+
if not skills_path.exists():
|
|
1018
|
+
return set()
|
|
1019
|
+
|
|
1020
|
+
return {d.name for d in skills_path.iterdir() if d.is_dir()}
|
|
1021
|
+
|
|
1022
|
+
|
|
1023
|
+
def _get_template_command_names() -> set[str]:
|
|
1024
|
+
"""Get set of command file names from installed template.
|
|
1025
|
+
|
|
1026
|
+
Returns:
|
|
1027
|
+
Set of .md command file names from .claude/commands/moai/ in template.
|
|
1028
|
+
"""
|
|
1029
|
+
template_path = Path(__file__).parent.parent.parent / "templates"
|
|
1030
|
+
commands_path = template_path / ".claude" / "commands" / "moai"
|
|
1031
|
+
|
|
1032
|
+
if not commands_path.exists():
|
|
1033
|
+
return set()
|
|
1034
|
+
|
|
1035
|
+
return {f.name for f in commands_path.iterdir() if f.is_file() and f.suffix == ".md"}
|
|
1036
|
+
|
|
1037
|
+
|
|
1038
|
+
def _get_template_agent_names() -> set[str]:
|
|
1039
|
+
"""Get set of agent file names from installed template.
|
|
1040
|
+
|
|
1041
|
+
Returns:
|
|
1042
|
+
Set of agent file names from .claude/agents/ in template.
|
|
1043
|
+
"""
|
|
1044
|
+
template_path = Path(__file__).parent.parent.parent / "templates"
|
|
1045
|
+
agents_path = template_path / ".claude" / "agents"
|
|
1046
|
+
|
|
1047
|
+
if not agents_path.exists():
|
|
1048
|
+
return set()
|
|
1049
|
+
|
|
1050
|
+
return {f.name for f in agents_path.iterdir() if f.is_file()}
|
|
1051
|
+
|
|
1052
|
+
|
|
1053
|
+
def _get_template_hook_names() -> set[str]:
|
|
1054
|
+
"""Get set of hook file names from installed template.
|
|
1055
|
+
|
|
1056
|
+
Returns:
|
|
1057
|
+
Set of .py hook file names from .claude/hooks/moai/ in template.
|
|
1058
|
+
"""
|
|
1059
|
+
template_path = Path(__file__).parent.parent.parent / "templates"
|
|
1060
|
+
hooks_path = template_path / ".claude" / "hooks" / "moai"
|
|
1061
|
+
|
|
1062
|
+
if not hooks_path.exists():
|
|
1063
|
+
return set()
|
|
1064
|
+
|
|
1065
|
+
return {f.name for f in hooks_path.iterdir() if f.is_file() and f.suffix == ".py"}
|
|
1066
|
+
|
|
1067
|
+
|
|
1068
|
+
def _detect_custom_commands(project_path: Path, template_commands: set[str]) -> list[str]:
|
|
1069
|
+
"""Detect custom commands NOT in template (user-created).
|
|
1070
|
+
|
|
1071
|
+
Args:
|
|
1072
|
+
project_path: Project path (absolute)
|
|
1073
|
+
template_commands: Set of template command file names
|
|
1074
|
+
|
|
1075
|
+
Returns:
|
|
1076
|
+
Sorted list of custom command file names.
|
|
1077
|
+
"""
|
|
1078
|
+
commands_path = project_path / ".claude" / "commands" / "moai"
|
|
1079
|
+
|
|
1080
|
+
if not commands_path.exists():
|
|
1081
|
+
return []
|
|
1082
|
+
|
|
1083
|
+
project_commands = {f.name for f in commands_path.iterdir() if f.is_file() and f.suffix == ".md"}
|
|
1084
|
+
custom_commands = project_commands - template_commands
|
|
1085
|
+
|
|
1086
|
+
return sorted(custom_commands)
|
|
1087
|
+
|
|
1088
|
+
|
|
1089
|
+
def _detect_custom_agents(project_path: Path, template_agents: set[str]) -> list[str]:
|
|
1090
|
+
"""Detect custom agents NOT in template (user-created).
|
|
1091
|
+
|
|
1092
|
+
Args:
|
|
1093
|
+
project_path: Project path (absolute)
|
|
1094
|
+
template_agents: Set of template agent file names
|
|
1095
|
+
|
|
1096
|
+
Returns:
|
|
1097
|
+
Sorted list of custom agent file names.
|
|
1098
|
+
"""
|
|
1099
|
+
agents_path = project_path / ".claude" / "agents"
|
|
1100
|
+
|
|
1101
|
+
if not agents_path.exists():
|
|
1102
|
+
return []
|
|
1103
|
+
|
|
1104
|
+
project_agents = {f.name for f in agents_path.iterdir() if f.is_file()}
|
|
1105
|
+
custom_agents = project_agents - template_agents
|
|
1106
|
+
|
|
1107
|
+
return sorted(custom_agents)
|
|
1108
|
+
|
|
1109
|
+
|
|
1110
|
+
def _detect_custom_hooks(project_path: Path, template_hooks: set[str]) -> list[str]:
|
|
1111
|
+
"""Detect custom hooks NOT in template (user-created).
|
|
1112
|
+
|
|
1113
|
+
Args:
|
|
1114
|
+
project_path: Project path (absolute)
|
|
1115
|
+
template_hooks: Set of template hook file names
|
|
1116
|
+
|
|
1117
|
+
Returns:
|
|
1118
|
+
Sorted list of custom hook file names.
|
|
1119
|
+
"""
|
|
1120
|
+
hooks_path = project_path / ".claude" / "hooks" / "moai"
|
|
1121
|
+
|
|
1122
|
+
if not hooks_path.exists():
|
|
1123
|
+
return []
|
|
1124
|
+
|
|
1125
|
+
project_hooks = {f.name for f in hooks_path.iterdir() if f.is_file() and f.suffix == ".py"}
|
|
1126
|
+
custom_hooks = project_hooks - template_hooks
|
|
1127
|
+
|
|
1128
|
+
return sorted(custom_hooks)
|
|
1129
|
+
|
|
1130
|
+
|
|
1131
|
+
def _group_custom_files_by_type(
|
|
1132
|
+
custom_commands: list[str],
|
|
1133
|
+
custom_agents: list[str],
|
|
1134
|
+
custom_hooks: list[str],
|
|
1135
|
+
) -> dict[str, list[str]]:
|
|
1136
|
+
"""Group custom files by type for UI display.
|
|
1137
|
+
|
|
1138
|
+
Args:
|
|
1139
|
+
custom_commands: List of custom command file names
|
|
1140
|
+
custom_agents: List of custom agent file names
|
|
1141
|
+
custom_hooks: List of custom hook file names
|
|
1142
|
+
|
|
1143
|
+
Returns:
|
|
1144
|
+
Dictionary with keys: commands, agents, hooks
|
|
1145
|
+
"""
|
|
1146
|
+
return {
|
|
1147
|
+
"commands": custom_commands,
|
|
1148
|
+
"agents": custom_agents,
|
|
1149
|
+
"hooks": custom_hooks,
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
|
|
1153
|
+
def _prompt_custom_files_restore(
|
|
1154
|
+
custom_commands: list[str],
|
|
1155
|
+
custom_agents: list[str],
|
|
1156
|
+
custom_hooks: list[str],
|
|
1157
|
+
yes: bool = False,
|
|
1158
|
+
) -> dict[str, list[str]]:
|
|
1159
|
+
"""Interactive fuzzy checkbox for custom files restore with search support.
|
|
1160
|
+
|
|
1161
|
+
Args:
|
|
1162
|
+
custom_commands: List of custom command file names
|
|
1163
|
+
custom_agents: List of custom agent file names
|
|
1164
|
+
custom_hooks: List of custom hook file names
|
|
1165
|
+
yes: Auto-confirm flag (skips restoration in CI/CD mode)
|
|
1166
|
+
|
|
1167
|
+
Returns:
|
|
1168
|
+
Dictionary with selected files grouped by type.
|
|
1169
|
+
"""
|
|
1170
|
+
# If no custom files, skip UI
|
|
1171
|
+
if not (custom_commands or custom_agents or custom_hooks):
|
|
1172
|
+
return {
|
|
1173
|
+
"commands": [],
|
|
1174
|
+
"agents": [],
|
|
1175
|
+
"hooks": [],
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
# In --yes mode, skip restoration (safest default)
|
|
1179
|
+
if yes:
|
|
1180
|
+
console.print("\n[dim] Skipping custom files restoration (--yes mode)[/dim]\n")
|
|
1181
|
+
return {
|
|
1182
|
+
"commands": [],
|
|
1183
|
+
"agents": [],
|
|
1184
|
+
"hooks": [],
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
# Try to use new UI, fallback to questionary if import fails
|
|
1188
|
+
try:
|
|
1189
|
+
from moai_adk.cli.ui.prompts import create_grouped_choices, fuzzy_checkbox
|
|
1190
|
+
|
|
1191
|
+
# Build grouped choices for fuzzy checkbox
|
|
1192
|
+
groups: dict[str, list[dict[str, str]]] = {}
|
|
1193
|
+
|
|
1194
|
+
if custom_commands:
|
|
1195
|
+
groups["Commands (.claude/commands/moai/)"] = [
|
|
1196
|
+
{"name": cmd, "value": f"cmd:{cmd}"} for cmd in custom_commands
|
|
1197
|
+
]
|
|
1198
|
+
|
|
1199
|
+
if custom_agents:
|
|
1200
|
+
groups["Agents (.claude/agents/)"] = [{"name": agent, "value": f"agent:{agent}"} for agent in custom_agents]
|
|
1201
|
+
|
|
1202
|
+
if custom_hooks:
|
|
1203
|
+
groups["Hooks (.claude/hooks/moai/)"] = [{"name": hook, "value": f"hook:{hook}"} for hook in custom_hooks]
|
|
1204
|
+
|
|
1205
|
+
choices = create_grouped_choices(groups)
|
|
1206
|
+
|
|
1207
|
+
console.print("\n[#DA7756]📦 Custom files detected in backup:[/#DA7756]")
|
|
1208
|
+
console.print("[dim] Use fuzzy search to find files quickly[/dim]\n")
|
|
1209
|
+
|
|
1210
|
+
selected = fuzzy_checkbox(
|
|
1211
|
+
"Select custom files to restore:",
|
|
1212
|
+
choices=choices,
|
|
1213
|
+
instruction="[Space] Toggle [Tab] All [Enter] Confirm [Type to search]",
|
|
1214
|
+
)
|
|
1215
|
+
|
|
1216
|
+
except ImportError:
|
|
1217
|
+
# Fallback to questionary if new UI not available
|
|
1218
|
+
import questionary
|
|
1219
|
+
from questionary import Choice, Separator
|
|
1220
|
+
|
|
1221
|
+
choices_legacy: list[Union[Separator, Choice]] = []
|
|
1222
|
+
|
|
1223
|
+
if custom_commands:
|
|
1224
|
+
choices_legacy.append(Separator("Commands (.claude/commands/moai/)"))
|
|
1225
|
+
for cmd in custom_commands:
|
|
1226
|
+
choices_legacy.append(Choice(title=cmd, value=f"cmd:{cmd}"))
|
|
1227
|
+
|
|
1228
|
+
if custom_agents:
|
|
1229
|
+
choices_legacy.append(Separator("Agents (.claude/agents/)"))
|
|
1230
|
+
for agent in custom_agents:
|
|
1231
|
+
choices_legacy.append(Choice(title=agent, value=f"agent:{agent}"))
|
|
1232
|
+
|
|
1233
|
+
if custom_hooks:
|
|
1234
|
+
choices_legacy.append(Separator("Hooks (.claude/hooks/moai/)"))
|
|
1235
|
+
for hook in custom_hooks:
|
|
1236
|
+
choices_legacy.append(Choice(title=hook, value=f"hook:{hook}"))
|
|
1237
|
+
|
|
1238
|
+
console.print("\n[cyan]📦 Custom files detected in backup:[/cyan]")
|
|
1239
|
+
console.print("[dim] Select files to restore (none selected by default)[/dim]\n")
|
|
1240
|
+
|
|
1241
|
+
selected = questionary.checkbox(
|
|
1242
|
+
"Select custom files to restore:",
|
|
1243
|
+
choices=choices_legacy,
|
|
1244
|
+
).ask()
|
|
1245
|
+
|
|
1246
|
+
# Parse results
|
|
1247
|
+
result_commands = []
|
|
1248
|
+
result_agents = []
|
|
1249
|
+
result_hooks = []
|
|
1250
|
+
|
|
1251
|
+
if selected:
|
|
1252
|
+
for item in selected:
|
|
1253
|
+
if item.startswith("cmd:"):
|
|
1254
|
+
result_commands.append(item[4:])
|
|
1255
|
+
elif item.startswith("agent:"):
|
|
1256
|
+
result_agents.append(item[6:])
|
|
1257
|
+
elif item.startswith("hook:"):
|
|
1258
|
+
result_hooks.append(item[5:])
|
|
1259
|
+
|
|
1260
|
+
return {
|
|
1261
|
+
"commands": result_commands,
|
|
1262
|
+
"agents": result_agents,
|
|
1263
|
+
"hooks": result_hooks,
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
|
|
1267
|
+
def _restore_custom_files(
|
|
1268
|
+
project_path: Path,
|
|
1269
|
+
backup_path: Path,
|
|
1270
|
+
selected_commands: list[str],
|
|
1271
|
+
selected_agents: list[str],
|
|
1272
|
+
selected_hooks: list[str],
|
|
1273
|
+
) -> bool:
|
|
1274
|
+
"""Restore selected custom files from backup to project.
|
|
1275
|
+
|
|
1276
|
+
Args:
|
|
1277
|
+
project_path: Project directory path
|
|
1278
|
+
backup_path: Backup directory path
|
|
1279
|
+
selected_commands: List of command files to restore
|
|
1280
|
+
selected_agents: List of agent files to restore
|
|
1281
|
+
selected_hooks: List of hook files to restore
|
|
1282
|
+
|
|
1283
|
+
Returns:
|
|
1284
|
+
True if all restorations succeeded, False otherwise.
|
|
1285
|
+
"""
|
|
1286
|
+
import shutil
|
|
1287
|
+
|
|
1288
|
+
success = True
|
|
1289
|
+
|
|
1290
|
+
# Restore commands
|
|
1291
|
+
if selected_commands:
|
|
1292
|
+
commands_dst = project_path / ".claude" / "commands" / "moai"
|
|
1293
|
+
commands_dst.mkdir(parents=True, exist_ok=True)
|
|
1294
|
+
|
|
1295
|
+
for cmd_file in selected_commands:
|
|
1296
|
+
src = backup_path / ".claude" / "commands" / "moai" / cmd_file
|
|
1297
|
+
dst = commands_dst / cmd_file
|
|
1298
|
+
|
|
1299
|
+
if src.exists():
|
|
1300
|
+
try:
|
|
1301
|
+
shutil.copy2(src, dst)
|
|
1302
|
+
except Exception as e:
|
|
1303
|
+
logger.warning(f"Failed to restore command {cmd_file}: {e}")
|
|
1304
|
+
success = False
|
|
1305
|
+
else:
|
|
1306
|
+
logger.warning(f"Command file not in backup: {cmd_file}")
|
|
1307
|
+
success = False
|
|
1308
|
+
|
|
1309
|
+
# Restore agents
|
|
1310
|
+
if selected_agents:
|
|
1311
|
+
agents_dst = project_path / ".claude" / "agents"
|
|
1312
|
+
agents_dst.mkdir(parents=True, exist_ok=True)
|
|
1313
|
+
|
|
1314
|
+
for agent_file in selected_agents:
|
|
1315
|
+
src = backup_path / ".claude" / "agents" / agent_file
|
|
1316
|
+
dst = agents_dst / agent_file
|
|
1317
|
+
|
|
1318
|
+
if src.exists():
|
|
1319
|
+
try:
|
|
1320
|
+
shutil.copy2(src, dst)
|
|
1321
|
+
except Exception as e:
|
|
1322
|
+
logger.warning(f"Failed to restore agent {agent_file}: {e}")
|
|
1323
|
+
success = False
|
|
1324
|
+
else:
|
|
1325
|
+
logger.warning(f"Agent file not in backup: {agent_file}")
|
|
1326
|
+
success = False
|
|
1327
|
+
|
|
1328
|
+
# Restore hooks
|
|
1329
|
+
if selected_hooks:
|
|
1330
|
+
hooks_dst = project_path / ".claude" / "hooks" / "moai"
|
|
1331
|
+
hooks_dst.mkdir(parents=True, exist_ok=True)
|
|
1332
|
+
|
|
1333
|
+
for hook_file in selected_hooks:
|
|
1334
|
+
src = backup_path / ".claude" / "hooks" / "moai" / hook_file
|
|
1335
|
+
dst = hooks_dst / hook_file
|
|
1336
|
+
|
|
1337
|
+
if src.exists():
|
|
1338
|
+
try:
|
|
1339
|
+
shutil.copy2(src, dst)
|
|
1340
|
+
except Exception as e:
|
|
1341
|
+
logger.warning(f"Failed to restore hook {hook_file}: {e}")
|
|
1342
|
+
success = False
|
|
1343
|
+
else:
|
|
1344
|
+
logger.warning(f"Hook file not in backup: {hook_file}")
|
|
1345
|
+
success = False
|
|
1346
|
+
|
|
1347
|
+
return success
|
|
1348
|
+
|
|
1349
|
+
|
|
1350
|
+
def _detect_custom_skills(project_path: Path, template_skills: set[str]) -> list[str]:
|
|
1351
|
+
"""Detect skills NOT in template (user-created).
|
|
1352
|
+
|
|
1353
|
+
Args:
|
|
1354
|
+
project_path: Project path (absolute)
|
|
1355
|
+
template_skills: Set of template skill names
|
|
1356
|
+
|
|
1357
|
+
Returns:
|
|
1358
|
+
Sorted list of custom skill names.
|
|
1359
|
+
"""
|
|
1360
|
+
skills_path = project_path / ".claude" / "skills"
|
|
1361
|
+
|
|
1362
|
+
if not skills_path.exists():
|
|
1363
|
+
return []
|
|
1364
|
+
|
|
1365
|
+
project_skills = {d.name for d in skills_path.iterdir() if d.is_dir()}
|
|
1366
|
+
custom_skills = project_skills - template_skills
|
|
1367
|
+
|
|
1368
|
+
return sorted(custom_skills)
|
|
1369
|
+
|
|
1370
|
+
|
|
1371
|
+
def _prompt_skill_restore(custom_skills: list[str], yes: bool = False) -> list[str]:
|
|
1372
|
+
"""Interactive fuzzy checkbox for skill restore with search support.
|
|
1373
|
+
|
|
1374
|
+
Args:
|
|
1375
|
+
custom_skills: List of custom skill names
|
|
1376
|
+
yes: Auto-confirm flag (skips restoration in CI/CD mode)
|
|
1377
|
+
|
|
1378
|
+
Returns:
|
|
1379
|
+
List of skills user selected to restore.
|
|
1380
|
+
"""
|
|
1381
|
+
if not custom_skills:
|
|
1382
|
+
return []
|
|
1383
|
+
|
|
1384
|
+
console.print("\n[#DA7756]📦 Custom skills detected in backup:[/#DA7756]")
|
|
1385
|
+
for skill in custom_skills:
|
|
1386
|
+
console.print(f" • {skill}")
|
|
1387
|
+
console.print()
|
|
1388
|
+
|
|
1389
|
+
if yes:
|
|
1390
|
+
console.print("[dim] Skipping restoration (--yes mode)[/dim]\n")
|
|
1391
|
+
return []
|
|
1392
|
+
|
|
1393
|
+
# Try new UI, fallback to questionary
|
|
1394
|
+
try:
|
|
1395
|
+
from moai_adk.cli.ui.prompts import fuzzy_checkbox
|
|
1396
|
+
|
|
1397
|
+
choices = [{"name": skill, "value": skill} for skill in custom_skills]
|
|
1398
|
+
|
|
1399
|
+
selected = fuzzy_checkbox(
|
|
1400
|
+
"Select skills to restore (type to search):",
|
|
1401
|
+
choices=choices,
|
|
1402
|
+
instruction="[Space] Toggle [Tab] All [Enter] Confirm [Type to search]",
|
|
1403
|
+
)
|
|
1404
|
+
|
|
1405
|
+
except ImportError:
|
|
1406
|
+
import questionary
|
|
1407
|
+
|
|
1408
|
+
selected = questionary.checkbox(
|
|
1409
|
+
"Select skills to restore (none selected by default):",
|
|
1410
|
+
choices=[questionary.Choice(title=skill, checked=False) for skill in custom_skills],
|
|
1411
|
+
).ask()
|
|
1412
|
+
|
|
1413
|
+
return selected if selected else []
|
|
1414
|
+
|
|
1415
|
+
|
|
1416
|
+
def _restore_selected_skills(skills: list[str], backup_path: Path, project_path: Path) -> bool:
|
|
1417
|
+
"""Restore selected skills from backup.
|
|
1418
|
+
|
|
1419
|
+
Args:
|
|
1420
|
+
skills: List of skill names to restore
|
|
1421
|
+
backup_path: Backup directory path
|
|
1422
|
+
project_path: Project path (absolute)
|
|
1423
|
+
|
|
1424
|
+
Returns:
|
|
1425
|
+
True if all restorations succeeded.
|
|
1426
|
+
"""
|
|
1427
|
+
import shutil
|
|
1428
|
+
|
|
1429
|
+
if not skills:
|
|
1430
|
+
return True
|
|
1431
|
+
|
|
1432
|
+
console.print("\n[cyan]📥 Restoring selected skills...[/cyan]")
|
|
1433
|
+
skills_dst = project_path / ".claude" / "skills"
|
|
1434
|
+
skills_dst.mkdir(parents=True, exist_ok=True)
|
|
1435
|
+
|
|
1436
|
+
success = True
|
|
1437
|
+
for skill_name in skills:
|
|
1438
|
+
src = backup_path / ".claude" / "skills" / skill_name
|
|
1439
|
+
dst = skills_dst / skill_name
|
|
1440
|
+
|
|
1441
|
+
if src.exists():
|
|
1442
|
+
try:
|
|
1443
|
+
shutil.copytree(src, dst, dirs_exist_ok=True)
|
|
1444
|
+
console.print(f" [green]✓ Restored: {skill_name}[/green]")
|
|
1445
|
+
except Exception as e:
|
|
1446
|
+
console.print(f" [red]✗ Failed: {skill_name} - {e}[/red]")
|
|
1447
|
+
success = False
|
|
1448
|
+
else:
|
|
1449
|
+
console.print(f" [yellow]⚠ Not in backup: {skill_name}[/yellow]")
|
|
1450
|
+
success = False
|
|
1451
|
+
|
|
1452
|
+
return success
|
|
1453
|
+
|
|
1454
|
+
|
|
1455
|
+
def _show_post_update_guidance(backup_path: Path) -> None:
|
|
1456
|
+
"""Show post-update completion message.
|
|
1457
|
+
|
|
1458
|
+
Args:
|
|
1459
|
+
backup_path: Backup directory path for reference
|
|
1460
|
+
"""
|
|
1461
|
+
console.print("\n" + "[cyan]" + "=" * 60 + "[/cyan]")
|
|
1462
|
+
console.print("[green]✅ Update complete![/green]")
|
|
1463
|
+
console.print("\n[dim]💡 Personal instructions should go in CLAUDE.local.md[/dim]")
|
|
1464
|
+
console.print(f"[dim]📂 Backup location: {backup_path}[/dim]")
|
|
1465
|
+
console.print("[cyan]" + "=" * 60 + "[/cyan]\n")
|
|
1466
|
+
|
|
1467
|
+
|
|
1468
|
+
def _sync_templates(project_path: Path, force: bool = False, yes: bool = False) -> bool:
|
|
1469
|
+
"""Sync templates to project with rollback mechanism.
|
|
331
1470
|
|
|
332
1471
|
Args:
|
|
333
1472
|
project_path: Project path (absolute)
|
|
334
1473
|
force: Force update without backup
|
|
1474
|
+
yes: Auto-confirm flag (skips interactive prompts)
|
|
335
1475
|
|
|
336
1476
|
Returns:
|
|
337
1477
|
True if sync succeeded, False otherwise
|
|
338
1478
|
"""
|
|
1479
|
+
from moai_adk.core.template.backup import TemplateBackup
|
|
1480
|
+
|
|
1481
|
+
backup_path = None
|
|
339
1482
|
try:
|
|
1483
|
+
# NEW: Detect custom files and skills BEFORE backup/sync
|
|
1484
|
+
template_skills = _get_template_skill_names()
|
|
1485
|
+
_detect_custom_skills(project_path, template_skills)
|
|
1486
|
+
|
|
1487
|
+
# Detect custom commands, agents, and hooks
|
|
1488
|
+
template_commands = _get_template_command_names()
|
|
1489
|
+
_detect_custom_commands(project_path, template_commands)
|
|
1490
|
+
|
|
1491
|
+
template_agents = _get_template_agent_names()
|
|
1492
|
+
_detect_custom_agents(project_path, template_agents)
|
|
1493
|
+
|
|
1494
|
+
template_hooks = _get_template_hook_names()
|
|
1495
|
+
_detect_custom_hooks(project_path, template_hooks)
|
|
1496
|
+
|
|
340
1497
|
processor = TemplateProcessor(project_path)
|
|
341
1498
|
|
|
1499
|
+
# Create pre-sync backup for rollback
|
|
1500
|
+
if not force:
|
|
1501
|
+
backup = TemplateBackup(project_path)
|
|
1502
|
+
if backup.has_existing_files():
|
|
1503
|
+
backup_path = backup.create_backup()
|
|
1504
|
+
console.print(f"💾 Created backup: {backup_path.name}")
|
|
1505
|
+
|
|
1506
|
+
# Merge analysis using Pure Python semantic heuristics
|
|
1507
|
+
try:
|
|
1508
|
+
analyzer = MergeAnalyzer(project_path)
|
|
1509
|
+
# Template source path from installed package
|
|
1510
|
+
template_path = Path(__file__).parent.parent.parent / "templates"
|
|
1511
|
+
|
|
1512
|
+
console.print("\n[cyan]🔍 Starting merge analysis...[/cyan]")
|
|
1513
|
+
console.print("[dim] Analyzing templates with semantic heuristics.[/dim]\n")
|
|
1514
|
+
analysis = analyzer.analyze_merge(backup_path, template_path)
|
|
1515
|
+
|
|
1516
|
+
# Ask user confirmation
|
|
1517
|
+
if not analyzer.ask_user_confirmation(analysis):
|
|
1518
|
+
console.print("[yellow]⚠️ User cancelled the update.[/yellow]")
|
|
1519
|
+
backup.restore_backup(backup_path)
|
|
1520
|
+
return False
|
|
1521
|
+
except Exception as e:
|
|
1522
|
+
console.print(f"[yellow]⚠️ Merge analysis failed: {e}[/yellow]")
|
|
1523
|
+
console.print("[yellow]Proceeding with automatic merge.[/yellow]")
|
|
1524
|
+
|
|
342
1525
|
# Load existing config
|
|
343
1526
|
existing_config = _load_existing_config(project_path)
|
|
344
1527
|
|
|
@@ -347,9 +1530,38 @@ def _sync_templates(project_path: Path, force: bool = False) -> bool:
|
|
|
347
1530
|
if context:
|
|
348
1531
|
processor.set_context(context)
|
|
349
1532
|
|
|
350
|
-
# Copy templates
|
|
1533
|
+
# Copy templates (including moai folder)
|
|
351
1534
|
processor.copy_templates(backup=False, silent=True)
|
|
352
1535
|
|
|
1536
|
+
# Stage 1.5: Alfred → Moai migration (AFTER template sync)
|
|
1537
|
+
# Execute migration after template copy (moai folders must exist first)
|
|
1538
|
+
migrator = AlfredToMoaiMigrator(project_path)
|
|
1539
|
+
if migrator.needs_migration():
|
|
1540
|
+
console.print("\n[cyan]🔄 Migrating folder structure: Alfred → Moai[/cyan]")
|
|
1541
|
+
try:
|
|
1542
|
+
if not migrator.execute_migration(backup_path):
|
|
1543
|
+
console.print("[red]❌ Alfred → Moai migration failed[/red]")
|
|
1544
|
+
if backup_path:
|
|
1545
|
+
console.print("[yellow]🔄 Restoring from backup...[/yellow]")
|
|
1546
|
+
backup = TemplateBackup(project_path)
|
|
1547
|
+
backup.restore_backup(backup_path)
|
|
1548
|
+
return False
|
|
1549
|
+
except Exception as e:
|
|
1550
|
+
console.print(f"[red]❌ Error during migration: {e}[/red]")
|
|
1551
|
+
if backup_path:
|
|
1552
|
+
backup = TemplateBackup(project_path)
|
|
1553
|
+
backup.restore_backup(backup_path)
|
|
1554
|
+
return False
|
|
1555
|
+
|
|
1556
|
+
# Validate template substitution
|
|
1557
|
+
validation_passed = _validate_template_substitution_with_rollback(project_path, backup_path)
|
|
1558
|
+
if not validation_passed:
|
|
1559
|
+
if backup_path:
|
|
1560
|
+
console.print(f"[yellow]🔄 Rolling back to backup: {backup_path.name}[/yellow]")
|
|
1561
|
+
backup = TemplateBackup(project_path)
|
|
1562
|
+
backup.restore_backup(backup_path)
|
|
1563
|
+
return False
|
|
1564
|
+
|
|
353
1565
|
# Preserve metadata
|
|
354
1566
|
_preserve_project_metadata(project_path, context, existing_config, __version__)
|
|
355
1567
|
_apply_context_to_file(processor, project_path / "CLAUDE.md")
|
|
@@ -357,8 +1569,59 @@ def _sync_templates(project_path: Path, force: bool = False) -> bool:
|
|
|
357
1569
|
# Set optimized=false
|
|
358
1570
|
set_optimized_false(project_path)
|
|
359
1571
|
|
|
1572
|
+
# Update companyAnnouncements in settings.local.json
|
|
1573
|
+
try:
|
|
1574
|
+
import sys
|
|
1575
|
+
|
|
1576
|
+
utils_dir = (
|
|
1577
|
+
Path(__file__).parent.parent.parent / "templates" / ".claude" / "hooks" / "moai" / "shared" / "utils"
|
|
1578
|
+
)
|
|
1579
|
+
|
|
1580
|
+
if utils_dir.exists():
|
|
1581
|
+
sys.path.insert(0, str(utils_dir))
|
|
1582
|
+
try:
|
|
1583
|
+
from announcement_translator import auto_translate_and_update # type: ignore[import-not-found]
|
|
1584
|
+
|
|
1585
|
+
console.print("[cyan]Updating announcements...[/cyan]")
|
|
1586
|
+
auto_translate_and_update(project_path)
|
|
1587
|
+
console.print("[green]✓ Announcements updated[/green]")
|
|
1588
|
+
except Exception as e:
|
|
1589
|
+
console.print(f"[yellow]⚠️ Announcement update failed: {e}[/yellow]")
|
|
1590
|
+
finally:
|
|
1591
|
+
sys.path.remove(str(utils_dir))
|
|
1592
|
+
|
|
1593
|
+
except Exception as e:
|
|
1594
|
+
console.print(f"[yellow]⚠️ Announcement module not available: {e}[/yellow]")
|
|
1595
|
+
|
|
1596
|
+
# NEW: Interactive custom element restore using new system
|
|
1597
|
+
_handle_custom_element_restoration(project_path, backup_path, yes)
|
|
1598
|
+
|
|
1599
|
+
# NEW: Migrate legacy logs to unified structure
|
|
1600
|
+
console.print("\n[cyan]📁 Migrating legacy log files...[/cyan]")
|
|
1601
|
+
if not _migrate_legacy_logs(project_path):
|
|
1602
|
+
console.print("[yellow]⚠️ Legacy log migration failed, but update continuing[/yellow]")
|
|
1603
|
+
|
|
1604
|
+
# Clean up legacy presets directory
|
|
1605
|
+
_cleanup_legacy_presets(project_path)
|
|
1606
|
+
|
|
1607
|
+
# Clean up CLI redesign obsolete files (v0.41+)
|
|
1608
|
+
_cleanup_cli_redesign_obsolete_files(project_path)
|
|
1609
|
+
|
|
1610
|
+
# NEW: Show post-update guidance
|
|
1611
|
+
if backup_path:
|
|
1612
|
+
_show_post_update_guidance(backup_path)
|
|
1613
|
+
|
|
360
1614
|
return True
|
|
361
|
-
except Exception:
|
|
1615
|
+
except Exception as e:
|
|
1616
|
+
console.print(f"[red]✗ Template sync failed: {e}[/red]")
|
|
1617
|
+
if backup_path:
|
|
1618
|
+
console.print(f"[yellow]🔄 Rolling back to backup: {backup_path.name}[/yellow]")
|
|
1619
|
+
try:
|
|
1620
|
+
backup = TemplateBackup(project_path)
|
|
1621
|
+
backup.restore_backup(backup_path)
|
|
1622
|
+
console.print("[green]✅ Rollback completed[/green]")
|
|
1623
|
+
except Exception as rollback_error:
|
|
1624
|
+
console.print(f"[red]✗ Rollback failed: {rollback_error}[/red]")
|
|
362
1625
|
return False
|
|
363
1626
|
|
|
364
1627
|
|
|
@@ -379,35 +1642,32 @@ def get_latest_version() -> str | None:
|
|
|
379
1642
|
|
|
380
1643
|
|
|
381
1644
|
def set_optimized_false(project_path: Path) -> None:
|
|
382
|
-
"""Set config
|
|
1645
|
+
"""Set config's optimized field to false.
|
|
383
1646
|
|
|
384
1647
|
Args:
|
|
385
1648
|
project_path: Project path (absolute).
|
|
386
1649
|
"""
|
|
387
|
-
config_path = project_path
|
|
1650
|
+
config_path, _ = _get_config_path(project_path)
|
|
388
1651
|
if not config_path.exists():
|
|
389
1652
|
return
|
|
390
1653
|
|
|
391
1654
|
try:
|
|
392
|
-
config_data =
|
|
1655
|
+
config_data = _load_config(config_path)
|
|
393
1656
|
config_data.setdefault("project", {})["optimized"] = False
|
|
394
|
-
config_path
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
)
|
|
398
|
-
except (json.JSONDecodeError, KeyError):
|
|
399
|
-
# Ignore errors if config.json is invalid
|
|
1657
|
+
_save_config(config_path, config_data)
|
|
1658
|
+
except (json.JSONDecodeError, yaml.YAMLError, KeyError):
|
|
1659
|
+
# Ignore errors if config is invalid
|
|
400
1660
|
pass
|
|
401
1661
|
|
|
402
1662
|
|
|
403
1663
|
def _load_existing_config(project_path: Path) -> dict[str, Any]:
|
|
404
|
-
"""Load existing config
|
|
405
|
-
config_path = project_path
|
|
1664
|
+
"""Load existing config (YAML or JSON) if available."""
|
|
1665
|
+
config_path, _ = _get_config_path(project_path)
|
|
406
1666
|
if config_path.exists():
|
|
407
1667
|
try:
|
|
408
|
-
return
|
|
409
|
-
except json.JSONDecodeError:
|
|
410
|
-
console.print("[yellow]⚠ Existing config
|
|
1668
|
+
return _load_config(config_path)
|
|
1669
|
+
except (json.JSONDecodeError, yaml.YAMLError):
|
|
1670
|
+
console.print("[yellow]⚠ Existing config could not be parsed. Proceeding with defaults.[/yellow]")
|
|
411
1671
|
return {}
|
|
412
1672
|
|
|
413
1673
|
|
|
@@ -445,6 +1705,8 @@ def _build_template_context(
|
|
|
445
1705
|
version_for_config: str,
|
|
446
1706
|
) -> dict[str, str]:
|
|
447
1707
|
"""Build substitution context for template files."""
|
|
1708
|
+
import platform
|
|
1709
|
+
|
|
448
1710
|
project_section = _extract_project_section(existing_config)
|
|
449
1711
|
|
|
450
1712
|
project_name = _coalesce(
|
|
@@ -474,13 +1736,115 @@ def _build_template_context(
|
|
|
474
1736
|
default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
|
475
1737
|
)
|
|
476
1738
|
|
|
1739
|
+
# Detect OS for cross-platform Hook path configuration
|
|
1740
|
+
hook_project_dir = "%CLAUDE_PROJECT_DIR%" if platform.system() == "Windows" else "$CLAUDE_PROJECT_DIR"
|
|
1741
|
+
|
|
1742
|
+
# Detect OS for cross-platform statusline command
|
|
1743
|
+
# Windows: Use python -m for better PATH compatibility
|
|
1744
|
+
# Unix: Use moai-adk directly (assumes installed via uv tool)
|
|
1745
|
+
if platform.system() == "Windows":
|
|
1746
|
+
statusline_command = "python -m moai_adk statusline"
|
|
1747
|
+
else:
|
|
1748
|
+
statusline_command = "moai-adk statusline"
|
|
1749
|
+
|
|
1750
|
+
# Extract and resolve language configuration using centralized resolver
|
|
1751
|
+
try:
|
|
1752
|
+
from moai_adk.core.language_config_resolver import get_resolver
|
|
1753
|
+
|
|
1754
|
+
# Use language resolver to get complete configuration
|
|
1755
|
+
resolver = get_resolver(str(project_path))
|
|
1756
|
+
resolved_config = resolver.resolve_config()
|
|
1757
|
+
|
|
1758
|
+
# Extract language configuration with environment variable priority
|
|
1759
|
+
language_config = {
|
|
1760
|
+
"conversation_language": resolved_config.get("conversation_language", "en"),
|
|
1761
|
+
"conversation_language_name": resolved_config.get("conversation_language_name", "English"),
|
|
1762
|
+
"agent_prompt_language": resolved_config.get("agent_prompt_language", "en"),
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
# Extract user personalization
|
|
1766
|
+
user_name = resolved_config.get("user_name", "")
|
|
1767
|
+
personalized_greeting = resolver.get_personalized_greeting(resolved_config)
|
|
1768
|
+
config_source = resolved_config.get("config_source", "config_file")
|
|
1769
|
+
|
|
1770
|
+
except ImportError:
|
|
1771
|
+
# Fallback to basic language config extraction if resolver not available
|
|
1772
|
+
language_config = existing_config.get("language", {})
|
|
1773
|
+
if not isinstance(language_config, dict):
|
|
1774
|
+
language_config = {}
|
|
1775
|
+
|
|
1776
|
+
user_name = existing_config.get("user", {}).get("name", "")
|
|
1777
|
+
conv_lang = language_config.get("conversation_language")
|
|
1778
|
+
personalized_greeting = f"{user_name}님" if user_name and conv_lang == "ko" else user_name
|
|
1779
|
+
config_source = "config_file"
|
|
1780
|
+
|
|
1781
|
+
# Enhanced version formatting (matches TemplateProcessor.get_enhanced_version_context)
|
|
1782
|
+
def format_short_version(v: str) -> str:
|
|
1783
|
+
"""Remove 'v' prefix if present."""
|
|
1784
|
+
return v[1:] if v.startswith("v") else v
|
|
1785
|
+
|
|
1786
|
+
def format_display_version(v: str) -> str:
|
|
1787
|
+
"""Format display version with proper formatting."""
|
|
1788
|
+
if v == "unknown":
|
|
1789
|
+
return "MoAI-ADK unknown version"
|
|
1790
|
+
elif v.startswith("v"):
|
|
1791
|
+
return f"MoAI-ADK {v}"
|
|
1792
|
+
else:
|
|
1793
|
+
return f"MoAI-ADK v{v}"
|
|
1794
|
+
|
|
1795
|
+
def format_trimmed_version(v: str, max_length: int = 10) -> str:
|
|
1796
|
+
"""Format version with maximum length for UI displays."""
|
|
1797
|
+
if v == "unknown":
|
|
1798
|
+
return "unknown"
|
|
1799
|
+
clean_version = v[1:] if v.startswith("v") else v
|
|
1800
|
+
if len(clean_version) > max_length:
|
|
1801
|
+
return clean_version[:max_length]
|
|
1802
|
+
return clean_version
|
|
1803
|
+
|
|
1804
|
+
def format_semver_version(v: str) -> str:
|
|
1805
|
+
"""Format version as semantic version."""
|
|
1806
|
+
if v == "unknown":
|
|
1807
|
+
return "0.0.0"
|
|
1808
|
+
clean_version = v[1:] if v.startswith("v") else v
|
|
1809
|
+
import re
|
|
1810
|
+
|
|
1811
|
+
semver_match = re.match(r"^(\d+\.\d+\.\d+)", clean_version)
|
|
1812
|
+
if semver_match:
|
|
1813
|
+
return semver_match.group(1)
|
|
1814
|
+
return "0.0.0"
|
|
1815
|
+
|
|
477
1816
|
return {
|
|
478
1817
|
"MOAI_VERSION": version_for_config,
|
|
1818
|
+
"MOAI_VERSION_SHORT": format_short_version(version_for_config),
|
|
1819
|
+
"MOAI_VERSION_DISPLAY": format_display_version(version_for_config),
|
|
1820
|
+
"MOAI_VERSION_TRIMMED": format_trimmed_version(version_for_config),
|
|
1821
|
+
"MOAI_VERSION_SEMVER": format_semver_version(version_for_config),
|
|
1822
|
+
"MOAI_VERSION_VALID": "true" if version_for_config != "unknown" else "false",
|
|
1823
|
+
"MOAI_VERSION_SOURCE": "config_cached",
|
|
479
1824
|
"PROJECT_NAME": project_name,
|
|
480
1825
|
"PROJECT_MODE": project_mode,
|
|
481
1826
|
"PROJECT_DESCRIPTION": project_description,
|
|
482
1827
|
"PROJECT_VERSION": project_version,
|
|
483
1828
|
"CREATION_TIMESTAMP": created_at,
|
|
1829
|
+
"PROJECT_DIR": hook_project_dir,
|
|
1830
|
+
"CONVERSATION_LANGUAGE": language_config.get("conversation_language", "en"),
|
|
1831
|
+
"CONVERSATION_LANGUAGE_NAME": language_config.get("conversation_language_name", "English"),
|
|
1832
|
+
"AGENT_PROMPT_LANGUAGE": language_config.get("agent_prompt_language", "en"),
|
|
1833
|
+
"GIT_COMMIT_MESSAGES_LANGUAGE": language_config.get("git_commit_messages", "en"),
|
|
1834
|
+
"CODE_COMMENTS_LANGUAGE": language_config.get("code_comments", "en"),
|
|
1835
|
+
"DOCUMENTATION_LANGUAGE": language_config.get(
|
|
1836
|
+
"documentation", language_config.get("conversation_language", "en")
|
|
1837
|
+
),
|
|
1838
|
+
"ERROR_MESSAGES_LANGUAGE": language_config.get(
|
|
1839
|
+
"error_messages", language_config.get("conversation_language", "en")
|
|
1840
|
+
),
|
|
1841
|
+
"USER_NAME": user_name,
|
|
1842
|
+
"PERSONALIZED_GREETING": personalized_greeting,
|
|
1843
|
+
"LANGUAGE_CONFIG_SOURCE": config_source,
|
|
1844
|
+
"CODEBASE_LANGUAGE": project_section.get("language", "generic"),
|
|
1845
|
+
"PROJECT_OWNER": project_section.get("author", "@user"),
|
|
1846
|
+
"AUTHOR": project_section.get("author", "@user"),
|
|
1847
|
+
"STATUSLINE_COMMAND": statusline_command,
|
|
484
1848
|
}
|
|
485
1849
|
|
|
486
1850
|
|
|
@@ -490,18 +1854,18 @@ def _preserve_project_metadata(
|
|
|
490
1854
|
existing_config: dict[str, Any],
|
|
491
1855
|
version_for_config: str,
|
|
492
1856
|
) -> None:
|
|
493
|
-
"""Restore project-specific metadata in the new config.
|
|
1857
|
+
"""Restore project-specific metadata in the new config (YAML or JSON).
|
|
494
1858
|
|
|
495
1859
|
Also updates template_version to track which template version is synchronized.
|
|
496
1860
|
"""
|
|
497
|
-
config_path = project_path
|
|
1861
|
+
config_path, _ = _get_config_path(project_path)
|
|
498
1862
|
if not config_path.exists():
|
|
499
1863
|
return
|
|
500
1864
|
|
|
501
1865
|
try:
|
|
502
|
-
config_data =
|
|
503
|
-
except json.JSONDecodeError:
|
|
504
|
-
console.print("[red]✗ Failed to parse config
|
|
1866
|
+
config_data = _load_config(config_path)
|
|
1867
|
+
except (json.JSONDecodeError, yaml.YAMLError):
|
|
1868
|
+
console.print("[red]✗ Failed to parse config after template copy[/red]")
|
|
505
1869
|
return
|
|
506
1870
|
|
|
507
1871
|
project_data = config_data.setdefault("project", {})
|
|
@@ -528,14 +1892,10 @@ def _preserve_project_metadata(
|
|
|
528
1892
|
config_data.setdefault("moai", {})
|
|
529
1893
|
config_data["moai"]["version"] = version_for_config
|
|
530
1894
|
|
|
531
|
-
# @CODE:UPDATE-REFACTOR-002-008: Update template_version to track sync status
|
|
532
1895
|
# This allows Stage 2 to compare package vs project template versions
|
|
533
1896
|
project_data["template_version"] = version_for_config
|
|
534
1897
|
|
|
535
|
-
config_path
|
|
536
|
-
json.dumps(config_data, indent=2, ensure_ascii=False) + "\n",
|
|
537
|
-
encoding="utf-8"
|
|
538
|
-
)
|
|
1898
|
+
_save_config(config_path, config_data)
|
|
539
1899
|
|
|
540
1900
|
|
|
541
1901
|
def _apply_context_to_file(processor: TemplateProcessor, target_path: Path) -> None:
|
|
@@ -557,7 +1917,88 @@ def _apply_context_to_file(processor: TemplateProcessor, target_path: Path) -> N
|
|
|
557
1917
|
target_path.write_text(substituted, encoding="utf-8")
|
|
558
1918
|
|
|
559
1919
|
|
|
560
|
-
|
|
1920
|
+
def _validate_template_substitution(project_path: Path) -> None:
|
|
1921
|
+
"""Validate that all template variables have been properly substituted."""
|
|
1922
|
+
import re
|
|
1923
|
+
|
|
1924
|
+
# Files to check for unsubstituted variables
|
|
1925
|
+
files_to_check = [
|
|
1926
|
+
project_path / ".claude" / "settings.json",
|
|
1927
|
+
project_path / "CLAUDE.md",
|
|
1928
|
+
]
|
|
1929
|
+
|
|
1930
|
+
issues_found = []
|
|
1931
|
+
|
|
1932
|
+
for file_path in files_to_check:
|
|
1933
|
+
if not file_path.exists():
|
|
1934
|
+
continue
|
|
1935
|
+
|
|
1936
|
+
try:
|
|
1937
|
+
content = file_path.read_text(encoding="utf-8")
|
|
1938
|
+
# Look for unsubstituted template variables
|
|
1939
|
+
unsubstituted = re.findall(r"\{\{([A-Z_]+)\}\}", content)
|
|
1940
|
+
if unsubstituted:
|
|
1941
|
+
unique_vars = sorted(set(unsubstituted))
|
|
1942
|
+
issues_found.append(f"{file_path.relative_to(project_path)}: {', '.join(unique_vars)}")
|
|
1943
|
+
except Exception as e:
|
|
1944
|
+
console.print(f"[yellow]⚠️ Could not validate {file_path.relative_to(project_path)}: {e}[/yellow]")
|
|
1945
|
+
|
|
1946
|
+
if issues_found:
|
|
1947
|
+
console.print("[red]✗ Template substitution validation failed:[/red]")
|
|
1948
|
+
for issue in issues_found:
|
|
1949
|
+
console.print(f" {issue}")
|
|
1950
|
+
console.print("[yellow]💡 Check .moai/config/ files for missing variable values[/yellow]")
|
|
1951
|
+
else:
|
|
1952
|
+
console.print("[green]✅ Template substitution validation passed[/green]")
|
|
1953
|
+
|
|
1954
|
+
|
|
1955
|
+
def _validate_template_substitution_with_rollback(project_path: Path, backup_path: Path | None) -> bool:
|
|
1956
|
+
"""Validate template substitution with rollback capability.
|
|
1957
|
+
|
|
1958
|
+
Returns:
|
|
1959
|
+
True if validation passed, False if failed (rollback handled by caller)
|
|
1960
|
+
"""
|
|
1961
|
+
import re
|
|
1962
|
+
|
|
1963
|
+
# Files to check for unsubstituted variables
|
|
1964
|
+
files_to_check = [
|
|
1965
|
+
project_path / ".claude" / "settings.json",
|
|
1966
|
+
project_path / "CLAUDE.md",
|
|
1967
|
+
]
|
|
1968
|
+
|
|
1969
|
+
issues_found = []
|
|
1970
|
+
|
|
1971
|
+
for file_path in files_to_check:
|
|
1972
|
+
if not file_path.exists():
|
|
1973
|
+
continue
|
|
1974
|
+
|
|
1975
|
+
try:
|
|
1976
|
+
content = file_path.read_text(encoding="utf-8")
|
|
1977
|
+
# Look for unsubstituted template variables
|
|
1978
|
+
unsubstituted = re.findall(r"\{\{([A-Z_]+)\}\}", content)
|
|
1979
|
+
if unsubstituted:
|
|
1980
|
+
unique_vars = sorted(set(unsubstituted))
|
|
1981
|
+
issues_found.append(f"{file_path.relative_to(project_path)}: {', '.join(unique_vars)}")
|
|
1982
|
+
except Exception as e:
|
|
1983
|
+
console.print(f"[yellow]⚠️ Could not validate {file_path.relative_to(project_path)}: {e}[/yellow]")
|
|
1984
|
+
|
|
1985
|
+
if issues_found:
|
|
1986
|
+
console.print("[red]✗ Template substitution validation failed:[/red]")
|
|
1987
|
+
for issue in issues_found:
|
|
1988
|
+
console.print(f" {issue}")
|
|
1989
|
+
|
|
1990
|
+
if backup_path:
|
|
1991
|
+
console.print("[yellow]🔄 Rolling back due to validation failure...[/yellow]")
|
|
1992
|
+
else:
|
|
1993
|
+
console.print("[yellow]💡 Check .moai/config/ files for missing variable values[/yellow]")
|
|
1994
|
+
console.print("[red]⚠️ No backup available - manual fix required[/red]")
|
|
1995
|
+
|
|
1996
|
+
return False
|
|
1997
|
+
else:
|
|
1998
|
+
console.print("[green]✅ Template substitution validation passed[/green]")
|
|
1999
|
+
return True
|
|
2000
|
+
|
|
2001
|
+
|
|
561
2002
|
def _show_version_info(current: str, latest: str) -> None:
|
|
562
2003
|
"""Display version information.
|
|
563
2004
|
|
|
@@ -570,7 +2011,6 @@ def _show_version_info(current: str, latest: str) -> None:
|
|
|
570
2011
|
console.print(f" Latest version: {latest}")
|
|
571
2012
|
|
|
572
2013
|
|
|
573
|
-
# @CODE:UPDATE-REFACTOR-002-005
|
|
574
2014
|
def _show_installer_not_found_help() -> None:
|
|
575
2015
|
"""Show help when installer not found."""
|
|
576
2016
|
console.print("[red]❌ Cannot detect package installer[/red]\n")
|
|
@@ -624,35 +2064,103 @@ def _show_timeout_error_help() -> None:
|
|
|
624
2064
|
console.print(" [cyan]moai-adk update --yes --force[/cyan]")
|
|
625
2065
|
|
|
626
2066
|
|
|
2067
|
+
def _execute_migration_if_needed(project_path: Path, yes: bool = False) -> bool:
|
|
2068
|
+
"""Check and execute migration if needed.
|
|
2069
|
+
|
|
2070
|
+
Args:
|
|
2071
|
+
project_path: Project directory path
|
|
2072
|
+
yes: Auto-confirm without prompting
|
|
2073
|
+
|
|
2074
|
+
Returns:
|
|
2075
|
+
True if no migration needed or migration succeeded, False if migration failed
|
|
2076
|
+
"""
|
|
2077
|
+
try:
|
|
2078
|
+
migrator = VersionMigrator(project_path)
|
|
2079
|
+
|
|
2080
|
+
# Check if migration is needed
|
|
2081
|
+
if not migrator.needs_migration():
|
|
2082
|
+
return True
|
|
2083
|
+
|
|
2084
|
+
# Get migration info
|
|
2085
|
+
info = migrator.get_migration_info()
|
|
2086
|
+
console.print("\n[cyan]🔄 Migration Required[/cyan]")
|
|
2087
|
+
console.print(f" Current version: {info['current_version']}")
|
|
2088
|
+
console.print(f" Target version: {info['target_version']}")
|
|
2089
|
+
console.print(f" Files to migrate: {info['file_count']}")
|
|
2090
|
+
console.print()
|
|
2091
|
+
console.print(" This will migrate configuration files to new locations:")
|
|
2092
|
+
console.print(" • .moai/config.json → .moai/config/config.json")
|
|
2093
|
+
console.print(" • .claude/statusline-config.yaml → .moai/config/statusline-config.yaml")
|
|
2094
|
+
console.print()
|
|
2095
|
+
console.print(" A backup will be created automatically.")
|
|
2096
|
+
console.print()
|
|
2097
|
+
|
|
2098
|
+
# Confirm with user (unless --yes)
|
|
2099
|
+
if not yes:
|
|
2100
|
+
reset_stdin() # Reset stdin before interactive prompt
|
|
2101
|
+
if not click.confirm("Do you want to proceed with migration?", default=True):
|
|
2102
|
+
console.print("[yellow]⚠️ Migration skipped. Some features may not work correctly.[/yellow]")
|
|
2103
|
+
console.print("[cyan]💡 Run 'moai-adk migrate' manually when ready[/cyan]")
|
|
2104
|
+
return False
|
|
2105
|
+
|
|
2106
|
+
# Execute migration
|
|
2107
|
+
console.print("[cyan]🚀 Starting migration...[/cyan]")
|
|
2108
|
+
success = migrator.migrate_to_v024(dry_run=False, cleanup=True)
|
|
2109
|
+
|
|
2110
|
+
if success:
|
|
2111
|
+
console.print("[green]✅ Migration completed successfully![/green]")
|
|
2112
|
+
return True
|
|
2113
|
+
else:
|
|
2114
|
+
console.print("[red]❌ Migration failed[/red]")
|
|
2115
|
+
console.print("[cyan]💡 Use 'moai-adk migrate --rollback' to restore from backup[/cyan]")
|
|
2116
|
+
return False
|
|
2117
|
+
|
|
2118
|
+
except Exception as e:
|
|
2119
|
+
console.print(f"[red]❌ Migration error: {e}[/red]")
|
|
2120
|
+
logger.error(f"Migration failed: {e}", exc_info=True)
|
|
2121
|
+
return False
|
|
2122
|
+
|
|
2123
|
+
|
|
627
2124
|
@click.command()
|
|
628
2125
|
@click.option(
|
|
629
2126
|
"--path",
|
|
630
2127
|
type=click.Path(exists=True),
|
|
631
2128
|
default=".",
|
|
632
|
-
help="Project path (default: current directory)"
|
|
633
|
-
)
|
|
634
|
-
@click.option(
|
|
635
|
-
"--force",
|
|
636
|
-
is_flag=True,
|
|
637
|
-
help="Skip backup and force the update"
|
|
2129
|
+
help="Project path (default: current directory)",
|
|
638
2130
|
)
|
|
2131
|
+
@click.option("--force", is_flag=True, help="Skip backup and force the update")
|
|
2132
|
+
@click.option("--check", is_flag=True, help="Only check version (do not update)")
|
|
2133
|
+
@click.option("--templates-only", is_flag=True, help="Skip package upgrade, sync templates only")
|
|
2134
|
+
@click.option("--yes", is_flag=True, help="Auto-confirm all prompts (CI/CD mode)")
|
|
639
2135
|
@click.option(
|
|
640
|
-
"
|
|
2136
|
+
"-c",
|
|
2137
|
+
"--config",
|
|
2138
|
+
"edit_config",
|
|
641
2139
|
is_flag=True,
|
|
642
|
-
help="
|
|
2140
|
+
help="Edit project configuration (same as init wizard)",
|
|
643
2141
|
)
|
|
644
2142
|
@click.option(
|
|
645
|
-
"--
|
|
646
|
-
|
|
647
|
-
|
|
2143
|
+
"--merge",
|
|
2144
|
+
"merge_strategy",
|
|
2145
|
+
flag_value="auto",
|
|
2146
|
+
help="Auto-merge: Apply template + preserve user changes",
|
|
648
2147
|
)
|
|
649
2148
|
@click.option(
|
|
650
|
-
"--
|
|
651
|
-
|
|
652
|
-
|
|
2149
|
+
"--manual",
|
|
2150
|
+
"merge_strategy",
|
|
2151
|
+
flag_value="manual",
|
|
2152
|
+
help="Manual merge: Preserve backup, generate merge guide",
|
|
653
2153
|
)
|
|
654
|
-
def update(
|
|
655
|
-
|
|
2154
|
+
def update(
|
|
2155
|
+
path: str,
|
|
2156
|
+
force: bool,
|
|
2157
|
+
check: bool,
|
|
2158
|
+
templates_only: bool,
|
|
2159
|
+
yes: bool,
|
|
2160
|
+
edit_config: bool,
|
|
2161
|
+
merge_strategy: str | None,
|
|
2162
|
+
) -> None:
|
|
2163
|
+
"""Update command with 3-stage workflow + merge strategy selection (v0.26.0+).
|
|
656
2164
|
|
|
657
2165
|
Stage 1 (Package Version Check):
|
|
658
2166
|
- Fetches current and latest versions from PyPI
|
|
@@ -664,33 +2172,73 @@ def update(path: str, force: bool, check: bool, templates_only: bool, yes: bool)
|
|
|
664
2172
|
- If versions match: skips Stage 3 (already up-to-date)
|
|
665
2173
|
- Performance improvement: 70-80% faster for unchanged projects (3-4s vs 12-18s)
|
|
666
2174
|
|
|
667
|
-
Stage 3 (Template Sync):
|
|
2175
|
+
Stage 3 (Template Sync with Merge Strategy - NEW in v0.26.0):
|
|
668
2176
|
- Syncs templates only if versions differ
|
|
2177
|
+
- User chooses merge strategy:
|
|
2178
|
+
* Auto-merge (default): Template + preserved user changes
|
|
2179
|
+
* Manual merge: Backup + comprehensive merge guide (full control)
|
|
669
2180
|
- Updates .claude/, .moai/, CLAUDE.md, config.json
|
|
670
2181
|
- Preserves specs and reports
|
|
671
2182
|
- Saves new template_version to config.json
|
|
672
2183
|
|
|
673
2184
|
Examples:
|
|
674
|
-
python -m moai_adk update #
|
|
675
|
-
python -m moai_adk update --
|
|
2185
|
+
python -m moai_adk update # interactive merge strategy selection
|
|
2186
|
+
python -m moai_adk update --merge # auto-merge (template + user changes)
|
|
2187
|
+
python -m moai_adk update --manual # manual merge (backup + guide)
|
|
2188
|
+
python -m moai_adk update --force # force template sync (no backup)
|
|
676
2189
|
python -m moai_adk update --check # check version only
|
|
677
2190
|
python -m moai_adk update --templates-only # skip package upgrade
|
|
678
|
-
python -m moai_adk update --yes # CI/CD mode (auto-confirm)
|
|
2191
|
+
python -m moai_adk update --yes # CI/CD mode (auto-confirm + auto-merge)
|
|
2192
|
+
|
|
2193
|
+
Merge Strategies:
|
|
2194
|
+
--merge: Auto-merge applies template + preserves your changes (default)
|
|
2195
|
+
Generated files: backup, merge report
|
|
2196
|
+
--manual: Manual merge preserves backup + generates comprehensive guide
|
|
2197
|
+
Generated files: backup, merge guide
|
|
2198
|
+
|
|
2199
|
+
Generated Files:
|
|
2200
|
+
- Backup: .moai-backups/pre-update-backup_{timestamp}/
|
|
2201
|
+
- Report: .moai/reports/merge-report.md (auto-merge only)
|
|
2202
|
+
- Guide: .moai/guides/merge-guide.md (manual merge only)
|
|
679
2203
|
"""
|
|
680
2204
|
try:
|
|
681
|
-
|
|
2205
|
+
# Use os.getcwd() for consistent path resolution in test environments
|
|
2206
|
+
if path == ".":
|
|
2207
|
+
project_path = Path(os.getcwd()).resolve()
|
|
2208
|
+
else:
|
|
2209
|
+
project_path = Path(path).resolve()
|
|
682
2210
|
|
|
683
2211
|
# Verify the project is initialized
|
|
684
2212
|
if not (project_path / ".moai").exists():
|
|
685
2213
|
console.print("[yellow]⚠ Project not initialized[/yellow]")
|
|
686
2214
|
raise click.Abort()
|
|
687
2215
|
|
|
2216
|
+
# Handle --config / -c mode (edit configuration only, no template updates)
|
|
2217
|
+
if edit_config:
|
|
2218
|
+
_edit_configuration(project_path)
|
|
2219
|
+
return
|
|
2220
|
+
|
|
688
2221
|
# Get versions (needed for --check and normal workflow, but not for --templates-only alone)
|
|
689
2222
|
# Note: If --check is used, always fetch versions even if --templates-only is also present
|
|
2223
|
+
# Initialize with defaults to satisfy type checker (will be set in the block below if needed)
|
|
2224
|
+
current: str = __version__
|
|
2225
|
+
latest: str = __version__
|
|
690
2226
|
if check or not templates_only:
|
|
691
2227
|
try:
|
|
692
|
-
|
|
693
|
-
|
|
2228
|
+
# Try to use new spinner UI
|
|
2229
|
+
try:
|
|
2230
|
+
from moai_adk.cli.ui.progress import SpinnerContext
|
|
2231
|
+
|
|
2232
|
+
with SpinnerContext("Checking for updates...") as spinner:
|
|
2233
|
+
current = _get_current_version()
|
|
2234
|
+
spinner.update("Fetching latest version from PyPI...")
|
|
2235
|
+
latest = _get_latest_version()
|
|
2236
|
+
spinner.success("Version check complete")
|
|
2237
|
+
except ImportError:
|
|
2238
|
+
# Fallback to simple console output
|
|
2239
|
+
console.print("[dim]Checking for updates...[/dim]")
|
|
2240
|
+
current = _get_current_version()
|
|
2241
|
+
latest = _get_latest_version()
|
|
694
2242
|
except RuntimeError as e:
|
|
695
2243
|
console.print(f"[red]Error: {e}[/red]")
|
|
696
2244
|
if not force:
|
|
@@ -717,8 +2265,13 @@ def update(path: str, force: bool, check: bool, templates_only: bool, yes: bool)
|
|
|
717
2265
|
# Step 2: Handle --templates-only (skip upgrade, go straight to sync)
|
|
718
2266
|
if templates_only:
|
|
719
2267
|
console.print("[cyan]📄 Syncing templates only...[/cyan]")
|
|
2268
|
+
|
|
2269
|
+
# Preserve user-specific settings before sync
|
|
2270
|
+
console.print(" [cyan]💾 Preserving user settings...[/cyan]")
|
|
2271
|
+
preserved_settings = _preserve_user_settings(project_path)
|
|
2272
|
+
|
|
720
2273
|
try:
|
|
721
|
-
if not _sync_templates(project_path, force):
|
|
2274
|
+
if not _sync_templates(project_path, force, yes):
|
|
722
2275
|
raise TemplateSyncError("Template sync returned False")
|
|
723
2276
|
except TemplateSyncError:
|
|
724
2277
|
console.print("[red]Error: Template sync failed[/red]")
|
|
@@ -729,6 +2282,9 @@ def update(path: str, force: bool, check: bool, templates_only: bool, yes: bool)
|
|
|
729
2282
|
_show_template_sync_failure_help()
|
|
730
2283
|
raise click.Abort()
|
|
731
2284
|
|
|
2285
|
+
# Restore user-specific settings after sync
|
|
2286
|
+
_restore_user_settings(project_path, preserved_settings)
|
|
2287
|
+
|
|
732
2288
|
console.print(" [green]✅ .claude/ update complete[/green]")
|
|
733
2289
|
console.print(" [green]✅ .moai/ update complete (specs/reports preserved)[/green]")
|
|
734
2290
|
console.print(" [green]🔄 CLAUDE.md merge complete[/green]")
|
|
@@ -740,12 +2296,12 @@ def update(path: str, force: bool, check: bool, templates_only: bool, yes: bool)
|
|
|
740
2296
|
comparison = _compare_versions(current, latest)
|
|
741
2297
|
|
|
742
2298
|
# Stage 1: Package Upgrade (if current < latest)
|
|
743
|
-
# @CODE:UPDATE-REFACTOR-002-009: Stage 1 - Package version check and upgrade
|
|
744
2299
|
if comparison < 0:
|
|
745
2300
|
console.print(f"\n[cyan]📦 Upgrading: {current} → {latest}[/cyan]")
|
|
746
2301
|
|
|
747
2302
|
# Confirm upgrade (unless --yes)
|
|
748
2303
|
if not yes:
|
|
2304
|
+
reset_stdin() # Reset stdin before interactive prompt
|
|
749
2305
|
if not click.confirm(f"Upgrade {current} → {latest}?", default=True):
|
|
750
2306
|
console.print("Cancelled")
|
|
751
2307
|
return
|
|
@@ -779,10 +2335,20 @@ def update(path: str, force: bool, check: bool, templates_only: bool, yes: bool)
|
|
|
779
2335
|
console.print("[cyan]📢 Run 'moai-adk update' again to sync templates[/cyan]")
|
|
780
2336
|
return
|
|
781
2337
|
|
|
782
|
-
# Stage
|
|
783
|
-
# @CODE:UPDATE-REFACTOR-002-010: Stage 2 - Compare template versions to determine if sync needed
|
|
2338
|
+
# Stage 1.5: Migration Check (NEW in v0.24.0)
|
|
784
2339
|
console.print(f"✓ Package already up to date ({current})")
|
|
785
2340
|
|
|
2341
|
+
# Execute migration if needed
|
|
2342
|
+
if not _execute_migration_if_needed(project_path, yes):
|
|
2343
|
+
console.print("[yellow]⚠️ Update continuing without migration[/yellow]")
|
|
2344
|
+
console.print("[cyan]💡 Some features may require migration to work correctly[/cyan]")
|
|
2345
|
+
|
|
2346
|
+
# Migrate config.json → config.yaml (v0.32.0+)
|
|
2347
|
+
console.print("\n[cyan]🔍 Checking for config format migration...[/cyan]")
|
|
2348
|
+
if not _migrate_config_json_to_yaml(project_path):
|
|
2349
|
+
console.print("[yellow]⚠️ Config migration failed, continuing with existing format[/yellow]")
|
|
2350
|
+
|
|
2351
|
+
# Stage 2: Config Version Comparison
|
|
786
2352
|
try:
|
|
787
2353
|
package_config_version = _get_package_config_version()
|
|
788
2354
|
project_config_version = _get_project_config_version(project_path)
|
|
@@ -796,7 +2362,14 @@ def update(path: str, force: bool, check: bool, templates_only: bool, yes: bool)
|
|
|
796
2362
|
console.print(f" Package template: {package_config_version}")
|
|
797
2363
|
console.print(f" Project config: {project_config_version}")
|
|
798
2364
|
|
|
799
|
-
|
|
2365
|
+
try:
|
|
2366
|
+
config_comparison = _compare_versions(package_config_version, project_config_version)
|
|
2367
|
+
except version.InvalidVersion as e:
|
|
2368
|
+
# Handle invalid version strings (e.g., unsubstituted template placeholders, corrupted configs)
|
|
2369
|
+
console.print(f"[yellow]⚠ Invalid version format in config: {e}[/yellow]")
|
|
2370
|
+
console.print("[cyan]ℹ️ Forcing template sync to repair configuration...[/cyan]")
|
|
2371
|
+
# Force template sync by treating project version as outdated
|
|
2372
|
+
config_comparison = 1 # package_config_version > project_config_version
|
|
800
2373
|
|
|
801
2374
|
# If versions are equal, no sync needed
|
|
802
2375
|
if config_comparison <= 0:
|
|
@@ -805,9 +2378,52 @@ def update(path: str, force: bool, check: bool, templates_only: bool, yes: bool)
|
|
|
805
2378
|
return
|
|
806
2379
|
|
|
807
2380
|
# Stage 3: Template Sync (Only if package_config_version > project_config_version)
|
|
808
|
-
# @CODE:UPDATE-REFACTOR-002-011: Stage 3 - Template sync only if versions differ
|
|
809
2381
|
console.print(f"\n[cyan]📄 Syncing templates ({project_config_version} → {package_config_version})...[/cyan]")
|
|
810
2382
|
|
|
2383
|
+
# Determine merge strategy (default: auto-merge)
|
|
2384
|
+
final_merge_strategy = merge_strategy or "auto"
|
|
2385
|
+
|
|
2386
|
+
# Handle merge strategy
|
|
2387
|
+
if final_merge_strategy == "manual":
|
|
2388
|
+
# Manual merge mode: Create full backup + generate guide, no template sync
|
|
2389
|
+
console.print("\n[cyan]🔀 Manual merge mode selected[/cyan]")
|
|
2390
|
+
|
|
2391
|
+
# Create full project backup
|
|
2392
|
+
console.print(" [cyan]💾 Creating full project backup...[/cyan]")
|
|
2393
|
+
try:
|
|
2394
|
+
from moai_adk.core.migration.backup_manager import BackupManager
|
|
2395
|
+
|
|
2396
|
+
backup_manager = BackupManager(project_path)
|
|
2397
|
+
full_backup_path = backup_manager.create_full_project_backup(description="pre-update-backup")
|
|
2398
|
+
console.print(f" [green]✓ Backup: {full_backup_path.relative_to(project_path)}/[/green]")
|
|
2399
|
+
|
|
2400
|
+
# Generate merge guide
|
|
2401
|
+
console.print(" [cyan]📋 Generating merge guide...[/cyan]")
|
|
2402
|
+
template_path = Path(__file__).parent.parent.parent / "templates"
|
|
2403
|
+
guide_path = _generate_manual_merge_guide(full_backup_path, template_path, project_path)
|
|
2404
|
+
console.print(f" [green]✓ Guide: {guide_path.relative_to(project_path)}[/green]")
|
|
2405
|
+
|
|
2406
|
+
# Summary
|
|
2407
|
+
console.print("\n[green]✓ Manual merge setup complete![/green]")
|
|
2408
|
+
console.print(f"[cyan]📍 Backup location: {full_backup_path.relative_to(project_path)}/[/cyan]")
|
|
2409
|
+
console.print(f"[cyan]📋 Merge guide: {guide_path.relative_to(project_path)}[/cyan]")
|
|
2410
|
+
console.print("\n[yellow]⚠️ Next steps:[/yellow]")
|
|
2411
|
+
console.print("[yellow] 1. Review the merge guide[/yellow]")
|
|
2412
|
+
console.print("[yellow] 2. Compare files using diff or visual tools[/yellow]")
|
|
2413
|
+
console.print("[yellow] 3. Manually merge your customizations[/yellow]")
|
|
2414
|
+
console.print("[yellow] 4. Test and commit changes[/yellow]")
|
|
2415
|
+
|
|
2416
|
+
except Exception as e:
|
|
2417
|
+
console.print(f"[red]Error: Manual merge setup failed - {e}[/red]")
|
|
2418
|
+
raise click.Abort()
|
|
2419
|
+
|
|
2420
|
+
return
|
|
2421
|
+
|
|
2422
|
+
# Auto merge mode: Preserve user-specific settings before sync
|
|
2423
|
+
console.print("\n[cyan]🔀 Auto-merge mode selected[/cyan]")
|
|
2424
|
+
console.print(" [cyan]💾 Preserving user settings...[/cyan]")
|
|
2425
|
+
preserved_settings = _preserve_user_settings(project_path)
|
|
2426
|
+
|
|
811
2427
|
# Create backup unless --force
|
|
812
2428
|
if not force:
|
|
813
2429
|
console.print(" [cyan]💾 Creating backup...[/cyan]")
|
|
@@ -815,16 +2431,28 @@ def update(path: str, force: bool, check: bool, templates_only: bool, yes: bool)
|
|
|
815
2431
|
processor = TemplateProcessor(project_path)
|
|
816
2432
|
backup_path = processor.create_backup()
|
|
817
2433
|
console.print(f" [green]✓ Backup: {backup_path.relative_to(project_path)}/[/green]")
|
|
2434
|
+
|
|
2435
|
+
# Clean up old backups (keep last 5)
|
|
2436
|
+
from moai_adk.core.template.backup import TemplateBackup
|
|
2437
|
+
|
|
2438
|
+
template_backup = TemplateBackup(project_path)
|
|
2439
|
+
deleted_count = template_backup.cleanup_old_backups(keep_count=5)
|
|
2440
|
+
if deleted_count > 0:
|
|
2441
|
+
console.print(f" [cyan]🧹 Cleaned up {deleted_count} old backup(s)[/cyan]")
|
|
818
2442
|
except Exception as e:
|
|
819
2443
|
console.print(f" [yellow]⚠ Backup failed: {e}[/yellow]")
|
|
820
2444
|
console.print(" [yellow]⚠ Continuing without backup...[/yellow]")
|
|
821
2445
|
else:
|
|
822
2446
|
console.print(" [yellow]⚠ Skipping backup (--force)[/yellow]")
|
|
823
2447
|
|
|
824
|
-
# Sync templates
|
|
2448
|
+
# Sync templates (NO spinner - user interaction may be required)
|
|
2449
|
+
# SpinnerContext blocks stdin, causing hang when click.confirm() is called
|
|
825
2450
|
try:
|
|
826
|
-
|
|
2451
|
+
console.print(" [cyan]Syncing templates...[/cyan]")
|
|
2452
|
+
if not _sync_templates(project_path, force, yes):
|
|
827
2453
|
raise TemplateSyncError("Template sync returned False")
|
|
2454
|
+
_restore_user_settings(project_path, preserved_settings)
|
|
2455
|
+
console.print(" [green]✓ Template sync complete[/green]")
|
|
828
2456
|
except TemplateSyncError:
|
|
829
2457
|
console.print("[red]Error: Template sync failed[/red]")
|
|
830
2458
|
_show_template_sync_failure_help()
|
|
@@ -841,8 +2469,482 @@ def update(path: str, force: bool, check: bool, templates_only: bool, yes: bool)
|
|
|
841
2469
|
console.print(" [yellow]⚙️ Set optimized=false (optimization needed)[/yellow]")
|
|
842
2470
|
|
|
843
2471
|
console.print("\n[green]✓ Update complete![/green]")
|
|
844
|
-
|
|
2472
|
+
|
|
2473
|
+
# Prompt for MoAI Rank hook installation if eligible
|
|
2474
|
+
try:
|
|
2475
|
+
from moai_adk.rank.hook import prompt_hook_installation
|
|
2476
|
+
|
|
2477
|
+
prompt_hook_installation(console=console)
|
|
2478
|
+
except ImportError:
|
|
2479
|
+
pass # rank module not available
|
|
845
2480
|
|
|
846
2481
|
except Exception as e:
|
|
847
2482
|
console.print(f"[red]✗ Update failed: {e}[/red]")
|
|
848
2483
|
raise click.ClickException(str(e)) from e
|
|
2484
|
+
|
|
2485
|
+
|
|
2486
|
+
def _handle_custom_element_restoration(project_path: Path, backup_path: Path | None, yes: bool = False) -> None:
|
|
2487
|
+
"""Handle custom element restoration using the enhanced system.
|
|
2488
|
+
|
|
2489
|
+
This function provides an improved interface for restoring user-created custom elements
|
|
2490
|
+
(agents, commands, skills, hooks) from backup during MoAI-ADK updates.
|
|
2491
|
+
|
|
2492
|
+
Key improvements:
|
|
2493
|
+
- Preserves unselected elements (fixes disappearing issue)
|
|
2494
|
+
- Only overwrites/creates selected elements from backup
|
|
2495
|
+
- Interactive checkbox selection with arrow key navigation
|
|
2496
|
+
- Includes all categories (Agents, Commands, Skills, Hooks)
|
|
2497
|
+
|
|
2498
|
+
Args:
|
|
2499
|
+
project_path: Path to the MoAI-ADK project directory
|
|
2500
|
+
backup_path: Path to the backup directory (None if no backup)
|
|
2501
|
+
yes: Whether to automatically accept defaults (non-interactive mode)
|
|
2502
|
+
"""
|
|
2503
|
+
if not backup_path:
|
|
2504
|
+
# No backup available, cannot restore
|
|
2505
|
+
return
|
|
2506
|
+
|
|
2507
|
+
try:
|
|
2508
|
+
# Create scanner to find custom elements in backup (not current project)
|
|
2509
|
+
backup_scanner = create_custom_element_scanner(backup_path)
|
|
2510
|
+
|
|
2511
|
+
# Get count of custom elements in backup
|
|
2512
|
+
backup_element_count = backup_scanner.get_element_count()
|
|
2513
|
+
|
|
2514
|
+
if backup_element_count == 0:
|
|
2515
|
+
# No custom elements found in backup
|
|
2516
|
+
console.print("[green]✓ No custom elements found in backup to restore[/green]")
|
|
2517
|
+
return
|
|
2518
|
+
|
|
2519
|
+
# Create enhanced user selection UI
|
|
2520
|
+
# IMPORTANT: Use backup_path, not project_path!
|
|
2521
|
+
# At this point, custom elements in project have been deleted by copy_templates().
|
|
2522
|
+
# The UI must scan the BACKUP to find elements available for restoration.
|
|
2523
|
+
ui = create_user_selection_ui(backup_path)
|
|
2524
|
+
|
|
2525
|
+
console.print(f"\n[cyan]🔍 Found {backup_element_count} custom elements in backup[/cyan]")
|
|
2526
|
+
|
|
2527
|
+
# If yes mode is enabled, restore all elements automatically
|
|
2528
|
+
if yes:
|
|
2529
|
+
console.print(f"[cyan]🔄 Auto-restoring {backup_element_count} custom elements...[/cyan]")
|
|
2530
|
+
backup_custom_elements = backup_scanner.scan_custom_elements()
|
|
2531
|
+
selected_elements = []
|
|
2532
|
+
|
|
2533
|
+
# Collect all element paths from backup
|
|
2534
|
+
for element_type, elements in backup_custom_elements.items():
|
|
2535
|
+
if element_type == "skills":
|
|
2536
|
+
for skill in elements:
|
|
2537
|
+
selected_elements.append(str(skill.path))
|
|
2538
|
+
else:
|
|
2539
|
+
for element_path in elements:
|
|
2540
|
+
selected_elements.append(str(element_path))
|
|
2541
|
+
else:
|
|
2542
|
+
# Interactive mode - prompt user for selection using enhanced UI
|
|
2543
|
+
selected_elements = ui.prompt_user_selection(backup_available=True)
|
|
2544
|
+
|
|
2545
|
+
if not selected_elements:
|
|
2546
|
+
console.print("[yellow]⚠ No elements selected for restoration[/yellow]")
|
|
2547
|
+
console.print("[green]✓ All existing custom elements will be preserved[/green]")
|
|
2548
|
+
return
|
|
2549
|
+
|
|
2550
|
+
# Confirm selection
|
|
2551
|
+
if not ui.confirm_selection(selected_elements):
|
|
2552
|
+
console.print("[yellow]⚠ Restoration cancelled by user[/yellow]")
|
|
2553
|
+
console.print("[green]✓ All existing custom elements will be preserved[/green]")
|
|
2554
|
+
return
|
|
2555
|
+
|
|
2556
|
+
# Perform selective restoration - ONLY restore selected elements
|
|
2557
|
+
if selected_elements:
|
|
2558
|
+
console.print(f"[cyan]🔄 Restoring {len(selected_elements)} selected elements from backup...[/cyan]")
|
|
2559
|
+
restorer = create_selective_restorer(project_path, backup_path)
|
|
2560
|
+
success, stats = restorer.restore_elements(selected_elements)
|
|
2561
|
+
|
|
2562
|
+
if success:
|
|
2563
|
+
console.print(f"[green]✅ Successfully restored {stats['success']} custom elements[/green]")
|
|
2564
|
+
console.print("[green]✓ All unselected elements remain preserved[/green]")
|
|
2565
|
+
else:
|
|
2566
|
+
console.print(f"[yellow]⚠️ Partial restoration: {stats['success']}/{stats['total']} elements[/yellow]")
|
|
2567
|
+
if stats["failed"] > 0:
|
|
2568
|
+
console.print(f"[red]❌ Failed to restore {stats['failed']} elements[/red]")
|
|
2569
|
+
console.print("[yellow]⚠️ All other elements remain preserved[/yellow]")
|
|
2570
|
+
else:
|
|
2571
|
+
console.print("[green]✓ No elements selected, all custom elements preserved[/green]")
|
|
2572
|
+
|
|
2573
|
+
except Exception as e:
|
|
2574
|
+
console.print(f"[yellow]⚠️ Custom element restoration failed: {e}[/yellow]")
|
|
2575
|
+
logger.warning(f"Custom element restoration error: {e}")
|
|
2576
|
+
console.print("[yellow]⚠️ All existing custom elements remain as-is[/yellow]")
|
|
2577
|
+
# Don't fail the entire update process, just log the error
|
|
2578
|
+
pass
|
|
2579
|
+
|
|
2580
|
+
|
|
2581
|
+
def _cleanup_legacy_presets(project_path: Path) -> None:
|
|
2582
|
+
"""Remove legacy presets directory entirely.
|
|
2583
|
+
|
|
2584
|
+
This function removes the entire .moai/config/presets/ directory as it is
|
|
2585
|
+
no longer used. All preset settings are now consolidated in sections/git-strategy.yaml.
|
|
2586
|
+
|
|
2587
|
+
Args:
|
|
2588
|
+
project_path: Project directory path (absolute)
|
|
2589
|
+
"""
|
|
2590
|
+
import shutil
|
|
2591
|
+
|
|
2592
|
+
presets_dir = project_path / ".moai" / "config" / "presets"
|
|
2593
|
+
|
|
2594
|
+
if not presets_dir.exists() or not presets_dir.is_dir():
|
|
2595
|
+
return
|
|
2596
|
+
|
|
2597
|
+
try:
|
|
2598
|
+
# Remove entire presets directory (no longer needed)
|
|
2599
|
+
shutil.rmtree(presets_dir)
|
|
2600
|
+
console.print(" [cyan]🧹 Removed legacy presets directory (now in sections/git-strategy.yaml)[/cyan]")
|
|
2601
|
+
logger.info(f"Removed legacy presets directory: {presets_dir}")
|
|
2602
|
+
except Exception as e:
|
|
2603
|
+
logger.warning(f"Failed to remove legacy presets directory {presets_dir}: {e}")
|
|
2604
|
+
|
|
2605
|
+
|
|
2606
|
+
def _cleanup_cli_redesign_obsolete_files(project_path: Path, dry_run: bool = False) -> int:
|
|
2607
|
+
"""
|
|
2608
|
+
Remove obsolete files from CLI redesign migration (v0.41+).
|
|
2609
|
+
|
|
2610
|
+
Cleans up:
|
|
2611
|
+
- .moai/scripts/setup-glm.py (replaced by moai init CLI)
|
|
2612
|
+
- .moai/config/questions/ (replaced by moai init CLI prompts)
|
|
2613
|
+
- .moai/scripts/ (if empty after cleanup)
|
|
2614
|
+
|
|
2615
|
+
Args:
|
|
2616
|
+
project_path: Project directory path
|
|
2617
|
+
dry_run: If True, only simulate cleanup
|
|
2618
|
+
|
|
2619
|
+
Returns:
|
|
2620
|
+
Number of items cleaned up
|
|
2621
|
+
"""
|
|
2622
|
+
obsolete_items = [
|
|
2623
|
+
".moai/scripts/setup-glm.py",
|
|
2624
|
+
".moai/config/questions",
|
|
2625
|
+
]
|
|
2626
|
+
|
|
2627
|
+
cleaned_count = 0
|
|
2628
|
+
|
|
2629
|
+
for relative_path in obsolete_items:
|
|
2630
|
+
full_path = project_path / relative_path
|
|
2631
|
+
|
|
2632
|
+
if not full_path.exists():
|
|
2633
|
+
continue
|
|
2634
|
+
|
|
2635
|
+
try:
|
|
2636
|
+
if dry_run:
|
|
2637
|
+
console.print(f" [dim]Would remove: {relative_path}[/dim]")
|
|
2638
|
+
cleaned_count += 1
|
|
2639
|
+
else:
|
|
2640
|
+
if full_path.is_dir():
|
|
2641
|
+
shutil.rmtree(full_path)
|
|
2642
|
+
console.print(f" [cyan]Removed obsolete directory: {relative_path}[/cyan]")
|
|
2643
|
+
else:
|
|
2644
|
+
full_path.unlink()
|
|
2645
|
+
console.print(f" [cyan]Removed obsolete file: {relative_path}[/cyan]")
|
|
2646
|
+
logger.info(f"Cleaned up obsolete: {relative_path}")
|
|
2647
|
+
cleaned_count += 1
|
|
2648
|
+
except Exception as e:
|
|
2649
|
+
logger.warning(f"Failed to clean up {relative_path}: {e}")
|
|
2650
|
+
|
|
2651
|
+
# Remove .moai/scripts/ if empty
|
|
2652
|
+
scripts_dir = project_path / ".moai" / "scripts"
|
|
2653
|
+
if scripts_dir.exists() and scripts_dir.is_dir():
|
|
2654
|
+
try:
|
|
2655
|
+
if not any(scripts_dir.iterdir()):
|
|
2656
|
+
if dry_run:
|
|
2657
|
+
console.print(" [dim]Would remove: .moai/scripts (empty)[/dim]")
|
|
2658
|
+
else:
|
|
2659
|
+
shutil.rmtree(scripts_dir)
|
|
2660
|
+
console.print(" [cyan]Removed empty .moai/scripts directory[/cyan]")
|
|
2661
|
+
logger.info("Removed empty .moai/scripts directory")
|
|
2662
|
+
cleaned_count += 1
|
|
2663
|
+
except Exception as e:
|
|
2664
|
+
logger.warning(f"Failed to remove empty scripts directory: {e}")
|
|
2665
|
+
|
|
2666
|
+
return cleaned_count
|
|
2667
|
+
|
|
2668
|
+
|
|
2669
|
+
def _migrate_config_json_to_yaml(project_path: Path) -> bool:
|
|
2670
|
+
"""Migrate legacy config.json to config.yaml format.
|
|
2671
|
+
|
|
2672
|
+
This function:
|
|
2673
|
+
1. Checks if config.json exists
|
|
2674
|
+
2. Converts it to config.yaml using YAML format
|
|
2675
|
+
3. Removes the old config.json file
|
|
2676
|
+
4. Also migrates preset files from JSON to YAML
|
|
2677
|
+
|
|
2678
|
+
Args:
|
|
2679
|
+
project_path: Project directory path (absolute)
|
|
2680
|
+
|
|
2681
|
+
Returns:
|
|
2682
|
+
bool: True if migration successful or not needed, False on error
|
|
2683
|
+
"""
|
|
2684
|
+
try:
|
|
2685
|
+
import yaml
|
|
2686
|
+
except ImportError:
|
|
2687
|
+
console.print(" [yellow]⚠️ PyYAML not available, skipping config migration[/yellow]")
|
|
2688
|
+
return True # Not a critical error
|
|
2689
|
+
|
|
2690
|
+
config_dir = project_path / ".moai" / "config"
|
|
2691
|
+
json_path = config_dir / "config.json"
|
|
2692
|
+
yaml_path = config_dir / "config.yaml"
|
|
2693
|
+
|
|
2694
|
+
# Check if migration needed
|
|
2695
|
+
if not json_path.exists():
|
|
2696
|
+
# No JSON file, migration not needed
|
|
2697
|
+
return True
|
|
2698
|
+
|
|
2699
|
+
if yaml_path.exists():
|
|
2700
|
+
# YAML already exists, just remove JSON
|
|
2701
|
+
try:
|
|
2702
|
+
json_path.unlink()
|
|
2703
|
+
console.print(" [cyan]🔄 Removed legacy config.json (YAML version exists)[/cyan]")
|
|
2704
|
+
logger.info(f"Removed legacy config.json: {json_path}")
|
|
2705
|
+
return True
|
|
2706
|
+
except Exception as e:
|
|
2707
|
+
console.print(f" [yellow]⚠️ Failed to remove legacy config.json: {e}[/yellow]")
|
|
2708
|
+
logger.warning(f"Failed to remove {json_path}: {e}")
|
|
2709
|
+
return True # Not critical
|
|
2710
|
+
|
|
2711
|
+
# Perform migration
|
|
2712
|
+
try:
|
|
2713
|
+
# Read JSON config
|
|
2714
|
+
with open(json_path, "r", encoding="utf-8") as f:
|
|
2715
|
+
config_data = json.load(f)
|
|
2716
|
+
|
|
2717
|
+
# Write YAML config
|
|
2718
|
+
with open(yaml_path, "w", encoding="utf-8") as f:
|
|
2719
|
+
yaml.safe_dump(
|
|
2720
|
+
config_data,
|
|
2721
|
+
f,
|
|
2722
|
+
default_flow_style=False,
|
|
2723
|
+
allow_unicode=True,
|
|
2724
|
+
sort_keys=False,
|
|
2725
|
+
)
|
|
2726
|
+
|
|
2727
|
+
# Remove old JSON file
|
|
2728
|
+
json_path.unlink()
|
|
2729
|
+
|
|
2730
|
+
console.print(" [green]✓ Migrated config.json → config.yaml[/green]")
|
|
2731
|
+
logger.info(f"Migrated config from JSON to YAML: {json_path} → {yaml_path}")
|
|
2732
|
+
|
|
2733
|
+
# Migrate preset files if they exist
|
|
2734
|
+
_migrate_preset_files_to_yaml(config_dir)
|
|
2735
|
+
|
|
2736
|
+
return True
|
|
2737
|
+
|
|
2738
|
+
except Exception as e:
|
|
2739
|
+
console.print(f" [red]✗ Config migration failed: {e}[/red]")
|
|
2740
|
+
logger.error(f"Failed to migrate config.json to YAML: {e}")
|
|
2741
|
+
return False
|
|
2742
|
+
|
|
2743
|
+
|
|
2744
|
+
def _migrate_preset_files_to_yaml(config_dir: Path) -> None:
|
|
2745
|
+
"""Migrate preset files from JSON to YAML format.
|
|
2746
|
+
|
|
2747
|
+
Args:
|
|
2748
|
+
config_dir: .moai/config directory path
|
|
2749
|
+
"""
|
|
2750
|
+
try:
|
|
2751
|
+
import yaml
|
|
2752
|
+
except ImportError:
|
|
2753
|
+
return
|
|
2754
|
+
|
|
2755
|
+
presets_dir = config_dir / "presets"
|
|
2756
|
+
if not presets_dir.exists():
|
|
2757
|
+
return
|
|
2758
|
+
|
|
2759
|
+
migrated_count = 0
|
|
2760
|
+
for json_file in presets_dir.glob("*.json"):
|
|
2761
|
+
yaml_file = json_file.with_suffix(".yaml")
|
|
2762
|
+
|
|
2763
|
+
# Skip if YAML already exists
|
|
2764
|
+
if yaml_file.exists():
|
|
2765
|
+
# Just remove the JSON file
|
|
2766
|
+
try:
|
|
2767
|
+
json_file.unlink()
|
|
2768
|
+
migrated_count += 1
|
|
2769
|
+
except Exception as e:
|
|
2770
|
+
logger.warning(f"Failed to remove {json_file}: {e}")
|
|
2771
|
+
continue
|
|
2772
|
+
|
|
2773
|
+
# Migrate JSON → YAML
|
|
2774
|
+
try:
|
|
2775
|
+
with open(json_file, "r", encoding="utf-8") as f:
|
|
2776
|
+
preset_data = json.load(f)
|
|
2777
|
+
|
|
2778
|
+
with open(yaml_file, "w", encoding="utf-8") as f:
|
|
2779
|
+
yaml.safe_dump(
|
|
2780
|
+
preset_data,
|
|
2781
|
+
f,
|
|
2782
|
+
default_flow_style=False,
|
|
2783
|
+
allow_unicode=True,
|
|
2784
|
+
sort_keys=False,
|
|
2785
|
+
)
|
|
2786
|
+
|
|
2787
|
+
json_file.unlink()
|
|
2788
|
+
migrated_count += 1
|
|
2789
|
+
|
|
2790
|
+
except Exception as e:
|
|
2791
|
+
logger.warning(f"Failed to migrate preset {json_file}: {e}")
|
|
2792
|
+
|
|
2793
|
+
if migrated_count > 0:
|
|
2794
|
+
console.print(f" [cyan]🔄 Migrated {migrated_count} preset file(s) to YAML[/cyan]")
|
|
2795
|
+
logger.info(f"Migrated {migrated_count} preset files to YAML")
|
|
2796
|
+
|
|
2797
|
+
|
|
2798
|
+
def _load_current_settings(project_path: Path) -> dict[str, Any]:
|
|
2799
|
+
"""Load current settings from section YAML files.
|
|
2800
|
+
|
|
2801
|
+
Args:
|
|
2802
|
+
project_path: Project directory path
|
|
2803
|
+
|
|
2804
|
+
Returns:
|
|
2805
|
+
Dictionary with current settings from all section files
|
|
2806
|
+
"""
|
|
2807
|
+
sections_dir = project_path / ".moai" / "config" / "sections"
|
|
2808
|
+
settings: dict[str, Any] = {}
|
|
2809
|
+
|
|
2810
|
+
section_files = [
|
|
2811
|
+
("language", "language.yaml"),
|
|
2812
|
+
("project", "project.yaml"),
|
|
2813
|
+
("pricing", "pricing.yaml"),
|
|
2814
|
+
("git_strategy", "git-strategy.yaml"),
|
|
2815
|
+
("user", "user.yaml"),
|
|
2816
|
+
("llm", "llm.yaml"),
|
|
2817
|
+
]
|
|
2818
|
+
|
|
2819
|
+
for key, filename in section_files:
|
|
2820
|
+
file_path = sections_dir / filename
|
|
2821
|
+
if file_path.exists():
|
|
2822
|
+
try:
|
|
2823
|
+
data = yaml.safe_load(file_path.read_text(encoding="utf-8"))
|
|
2824
|
+
if data:
|
|
2825
|
+
settings[key] = data
|
|
2826
|
+
except yaml.YAMLError:
|
|
2827
|
+
pass
|
|
2828
|
+
|
|
2829
|
+
return settings
|
|
2830
|
+
|
|
2831
|
+
|
|
2832
|
+
def _show_current_config(project_path: Path) -> None:
|
|
2833
|
+
"""Display current configuration summary in a panel.
|
|
2834
|
+
|
|
2835
|
+
Args:
|
|
2836
|
+
project_path: Project directory path
|
|
2837
|
+
"""
|
|
2838
|
+
from rich.panel import Panel
|
|
2839
|
+
|
|
2840
|
+
settings = _load_current_settings(project_path)
|
|
2841
|
+
|
|
2842
|
+
# Extract values with defaults
|
|
2843
|
+
project_name = settings.get("project", {}).get("project", {}).get("name", project_path.name)
|
|
2844
|
+
if isinstance(settings.get("project", {}).get("project"), dict):
|
|
2845
|
+
project_name = settings["project"]["project"].get("name", project_path.name)
|
|
2846
|
+
elif "project" in settings and "name" in settings.get("project", {}).get("project", {}):
|
|
2847
|
+
project_name = settings["project"]["project"]["name"]
|
|
2848
|
+
else:
|
|
2849
|
+
# Try alternative structure
|
|
2850
|
+
project_name = settings.get("project", {}).get("name", project_path.name)
|
|
2851
|
+
|
|
2852
|
+
user_name = settings.get("user", {}).get("user", {}).get("name", "")
|
|
2853
|
+
if not user_name and isinstance(settings.get("user"), dict):
|
|
2854
|
+
user_name = settings.get("user", {}).get("name", "")
|
|
2855
|
+
|
|
2856
|
+
conv_lang = settings.get("language", {}).get("language", {}).get("conversation_language", "en")
|
|
2857
|
+
conv_lang_name = settings.get("language", {}).get("language", {}).get("conversation_language_name", "English")
|
|
2858
|
+
|
|
2859
|
+
# GLM-only simplified flow - service is always GLM
|
|
2860
|
+
glm_pricing_plan = settings.get("pricing", {}).get("service", {}).get("glm_pricing_plan", "basic")
|
|
2861
|
+
|
|
2862
|
+
git_mode = settings.get("git_strategy", {}).get("git_strategy", {}).get("mode", "personal")
|
|
2863
|
+
|
|
2864
|
+
llm_mode = settings.get("llm", {}).get("llm", {}).get("mode", "claude-only")
|
|
2865
|
+
|
|
2866
|
+
# Build display content
|
|
2867
|
+
lines = [
|
|
2868
|
+
f"📁 Project: [cyan]{project_name}[/cyan]",
|
|
2869
|
+
]
|
|
2870
|
+
if user_name:
|
|
2871
|
+
lines.append(f"👤 User: [cyan]{user_name}[/cyan]")
|
|
2872
|
+
|
|
2873
|
+
lines.append("")
|
|
2874
|
+
lines.append(f"🌐 Language: [green]{conv_lang}[/green] ({conv_lang_name})")
|
|
2875
|
+
lines.append("🔧 Service: [green]GLM CodePlan[/green]" + (f" ({glm_pricing_plan})" if glm_pricing_plan else ""))
|
|
2876
|
+
lines.append(f"🔀 Git: [green]{git_mode}[/green]")
|
|
2877
|
+
lines.append(f"🤖 LLM Mode: [green]{llm_mode}[/green]")
|
|
2878
|
+
|
|
2879
|
+
content = "\n".join(lines)
|
|
2880
|
+
console.print(Panel(content, title="[yellow]Current Configuration[/yellow]", border_style="cyan"))
|
|
2881
|
+
|
|
2882
|
+
|
|
2883
|
+
def _edit_configuration(project_path: Path) -> None:
|
|
2884
|
+
"""Interactive configuration editing using init prompts.
|
|
2885
|
+
|
|
2886
|
+
Args:
|
|
2887
|
+
project_path: Project directory path
|
|
2888
|
+
"""
|
|
2889
|
+
from moai_adk.cli.commands.init import _save_additional_config
|
|
2890
|
+
from moai_adk.cli.prompts.init_prompts import prompt_project_setup
|
|
2891
|
+
|
|
2892
|
+
console.print("\n[cyan]⚙️ Configuration Edit Mode[/cyan]")
|
|
2893
|
+
console.print("[dim]Edit your project settings (same as init wizard)[/dim]\n")
|
|
2894
|
+
|
|
2895
|
+
# Show current config
|
|
2896
|
+
_show_current_config(project_path)
|
|
2897
|
+
console.print()
|
|
2898
|
+
|
|
2899
|
+
# Load current settings to pre-fill
|
|
2900
|
+
settings = _load_current_settings(project_path)
|
|
2901
|
+
|
|
2902
|
+
# Extract current locale for pre-fill
|
|
2903
|
+
current_locale = settings.get("language", {}).get("language", {}).get("conversation_language", "en")
|
|
2904
|
+
project_name = project_path.name
|
|
2905
|
+
|
|
2906
|
+
# Try to get project name from settings
|
|
2907
|
+
project_data = settings.get("project", {})
|
|
2908
|
+
if isinstance(project_data.get("project"), dict):
|
|
2909
|
+
project_name = project_data["project"].get("name", project_path.name)
|
|
2910
|
+
elif "name" in project_data:
|
|
2911
|
+
project_name = project_data.get("name", project_path.name)
|
|
2912
|
+
|
|
2913
|
+
# Run interactive prompt
|
|
2914
|
+
try:
|
|
2915
|
+
answers = prompt_project_setup(
|
|
2916
|
+
project_name=project_name,
|
|
2917
|
+
is_current_dir=True,
|
|
2918
|
+
project_path=project_path,
|
|
2919
|
+
initial_locale=current_locale,
|
|
2920
|
+
)
|
|
2921
|
+
except KeyboardInterrupt:
|
|
2922
|
+
console.print("\n[yellow]Configuration edit cancelled.[/yellow]")
|
|
2923
|
+
return
|
|
2924
|
+
|
|
2925
|
+
if not answers:
|
|
2926
|
+
console.print("[yellow]No changes made.[/yellow]")
|
|
2927
|
+
return
|
|
2928
|
+
|
|
2929
|
+
# Save configuration to section files
|
|
2930
|
+
# Note: API keys are not modified in config edit mode (pass None to preserve existing)
|
|
2931
|
+
# GLM-only flow: service_type is always "glm", glm_pricing_plan defaults to "basic"
|
|
2932
|
+
_save_additional_config(
|
|
2933
|
+
project_path=project_path,
|
|
2934
|
+
project_name=answers.get("project_name", project_name),
|
|
2935
|
+
locale=answers.get("locale", current_locale),
|
|
2936
|
+
user_name=answers.get("user_name", ""),
|
|
2937
|
+
service_type="glm", # Always GLM in simplified flow
|
|
2938
|
+
pricing_plan=None, # Not used in GLM-only flow
|
|
2939
|
+
glm_pricing_plan="basic", # Default GLM pricing plan
|
|
2940
|
+
anthropic_api_key=None, # Not used in GLM-only flow
|
|
2941
|
+
glm_api_key=None, # Preserve existing key
|
|
2942
|
+
git_mode=answers.get("git_mode", "personal"),
|
|
2943
|
+
github_username=answers.get("github_username"),
|
|
2944
|
+
git_commit_lang=answers.get("git_commit_lang", "en"),
|
|
2945
|
+
code_comment_lang=answers.get("code_comment_lang", "en"),
|
|
2946
|
+
doc_lang=answers.get("doc_lang", "en"),
|
|
2947
|
+
)
|
|
2948
|
+
|
|
2949
|
+
console.print("\n[green]✓ Configuration updated successfully![/green]")
|
|
2950
|
+
console.print("[dim]Changes saved to .moai/config/sections/[/dim]")
|