moai-adk 0.15.1__py3-none-any.whl โ†’ 0.32.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of moai-adk might be problematic. Click here for more details.

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