moai-adk 0.4.5__py3-none-any.whl → 0.20.1__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (433) hide show
  1. moai_adk/__init__.py +1 -1
  2. moai_adk/__main__.py +74 -1
  3. moai_adk/cli/commands/__init__.py +1 -1
  4. moai_adk/cli/commands/analyze.py +119 -0
  5. moai_adk/cli/commands/backup.py +25 -1
  6. moai_adk/cli/commands/doctor.py +31 -5
  7. moai_adk/cli/commands/improve_user_experience.py +307 -0
  8. moai_adk/cli/commands/init.py +111 -10
  9. moai_adk/cli/commands/status.py +33 -3
  10. moai_adk/cli/commands/update.py +921 -130
  11. moai_adk/cli/commands/validate_links.py +120 -0
  12. moai_adk/cli/prompts/init_prompts.py +22 -87
  13. moai_adk/core/analysis/__init__.py +9 -0
  14. moai_adk/core/analysis/session_analyzer.py +388 -0
  15. moai_adk/core/analysis/tag_chain_analyzer.py +344 -0
  16. moai_adk/core/analysis/tag_chain_repair.py +879 -0
  17. moai_adk/core/config/__init__.py +19 -0
  18. moai_adk/core/config/migration.py +235 -0
  19. moai_adk/core/git/__init__.py +1 -1
  20. moai_adk/core/git/branch.py +1 -1
  21. moai_adk/core/git/commit.py +1 -1
  22. moai_adk/core/git/manager.py +1 -1
  23. moai_adk/core/issue_creator.py +313 -0
  24. moai_adk/core/mcp/setup.py +56 -0
  25. moai_adk/core/mcp/setup_old.py +296 -0
  26. moai_adk/core/project/backup_utils.py +1 -1
  27. moai_adk/core/project/checker.py +2 -2
  28. moai_adk/core/project/detector.py +211 -12
  29. moai_adk/core/project/initializer.py +85 -15
  30. moai_adk/core/project/phase_executor.py +76 -13
  31. moai_adk/core/project/validator.py +13 -13
  32. moai_adk/core/quality/__init__.py +1 -1
  33. moai_adk/core/quality/trust_checker.py +1 -1
  34. moai_adk/core/quality/validators/__init__.py +1 -1
  35. moai_adk/core/quality/validators/base_validator.py +1 -1
  36. moai_adk/core/tags/__init__.py +86 -0
  37. moai_adk/core/tags/auto_corrector.py +693 -0
  38. moai_adk/core/tags/ci_validator.py +463 -0
  39. moai_adk/core/tags/cli.py +283 -0
  40. moai_adk/core/tags/generator.py +109 -0
  41. moai_adk/core/tags/inserter.py +99 -0
  42. moai_adk/core/tags/mapper.py +126 -0
  43. moai_adk/core/tags/parser.py +76 -0
  44. moai_adk/core/tags/policy_validator.py +580 -0
  45. moai_adk/core/tags/pre_commit_validator.py +421 -0
  46. moai_adk/core/tags/reporter.py +956 -0
  47. moai_adk/core/tags/rollback_manager.py +525 -0
  48. moai_adk/core/tags/tags.py +149 -0
  49. moai_adk/core/tags/validator.py +897 -0
  50. moai_adk/core/template/__init__.py +1 -1
  51. moai_adk/core/template/backup.py +1 -1
  52. moai_adk/core/template/merger.py +50 -1
  53. moai_adk/core/template/processor.py +119 -13
  54. moai_adk/core/template_engine.py +268 -0
  55. moai_adk/templates/.claude/agents/alfred/backend-expert.md +348 -0
  56. moai_adk/templates/.claude/agents/alfred/cc-manager.md +209 -944
  57. moai_adk/templates/.claude/agents/alfred/database-expert.md +352 -0
  58. moai_adk/templates/.claude/agents/alfred/debug-helper.md +34 -5
  59. moai_adk/templates/.claude/agents/alfred/devops-expert.md +464 -0
  60. moai_adk/templates/.claude/agents/alfred/doc-syncer.md +38 -8
  61. moai_adk/templates/.claude/agents/alfred/format-expert.md +469 -0
  62. moai_adk/templates/.claude/agents/alfred/frontend-expert.md +357 -0
  63. moai_adk/templates/.claude/agents/alfred/git-manager.md +128 -9
  64. moai_adk/templates/.claude/agents/alfred/implementation-planner.md +104 -6
  65. moai_adk/templates/.claude/agents/alfred/project-manager.md +88 -16
  66. moai_adk/templates/.claude/agents/alfred/quality-gate.md +36 -9
  67. moai_adk/templates/.claude/agents/alfred/security-expert.md +270 -0
  68. moai_adk/templates/.claude/agents/alfred/skill-factory.md +865 -0
  69. moai_adk/templates/.claude/agents/alfred/spec-builder.md +214 -43
  70. moai_adk/templates/.claude/agents/alfred/tag-agent.md +111 -9
  71. moai_adk/templates/.claude/agents/alfred/tdd-implementer.md +309 -160
  72. moai_adk/templates/.claude/agents/alfred/trust-checker.md +36 -7
  73. moai_adk/templates/.claude/agents/alfred/ui-ux-expert.md +605 -0
  74. moai_adk/templates/.claude/commands/alfred/0-project.md +393 -966
  75. moai_adk/templates/.claude/commands/alfred/1-plan.md +651 -367
  76. moai_adk/templates/.claude/commands/alfred/2-run.md +388 -241
  77. moai_adk/templates/.claude/commands/alfred/3-sync.md +1921 -410
  78. moai_adk/templates/.claude/commands/alfred/9-feedback.md +153 -0
  79. moai_adk/templates/.claude/commands/alfred/release-new.md +3604 -0
  80. moai_adk/templates/.claude/hooks/alfred/core/project.py +484 -20
  81. moai_adk/templates/.claude/hooks/alfred/core/timeout.py +136 -0
  82. moai_adk/templates/.claude/hooks/alfred/core/ttl_cache.py +108 -0
  83. moai_adk/templates/.claude/hooks/alfred/core/version_cache.py +198 -0
  84. moai_adk/templates/.claude/hooks/alfred/handlers/__init__.py +14 -6
  85. moai_adk/templates/.claude/hooks/alfred/post_tool__enable_streaming_ui.py +50 -0
  86. moai_adk/templates/.claude/hooks/alfred/post_tool__log_changes.py +93 -0
  87. moai_adk/templates/.claude/hooks/alfred/post_tool__tag_auto_corrector.py +407 -0
  88. moai_adk/templates/.claude/hooks/alfred/pre_tool__auto_checkpoint.py +99 -0
  89. moai_adk/templates/.claude/hooks/alfred/pre_tool__realtime_tag_monitor.py +335 -0
  90. moai_adk/templates/.claude/hooks/alfred/pre_tool__tag_policy_validator.py +325 -0
  91. moai_adk/templates/.claude/hooks/alfred/session_end__cleanup.py +93 -0
  92. moai_adk/templates/.claude/hooks/alfred/session_start__auto_cleanup.py +580 -0
  93. moai_adk/templates/.claude/hooks/alfred/session_start__show_project_info.py +298 -0
  94. moai_adk/templates/.claude/hooks/alfred/shared/core/__init__.py +170 -0
  95. moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/checkpoint.py +3 -3
  96. moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/context.py +5 -5
  97. moai_adk/templates/.claude/hooks/alfred/shared/core/project.py +749 -0
  98. moai_adk/templates/.claude/hooks/alfred/shared/core/tags.py +230 -0
  99. moai_adk/templates/.claude/hooks/alfred/shared/core/version_cache.py +198 -0
  100. moai_adk/templates/.claude/hooks/alfred/shared/handlers/__init__.py +21 -0
  101. moai_adk/templates/.claude/hooks/alfred/shared/handlers/daily_analysis.py +351 -0
  102. moai_adk/templates/.claude/hooks/alfred/shared/handlers/notification.py +154 -0
  103. moai_adk/templates/.claude/hooks/alfred/shared/handlers/session.py +174 -0
  104. moai_adk/templates/.claude/hooks/alfred/shared/handlers/tool.py +87 -0
  105. moai_adk/templates/.claude/hooks/alfred/shared/handlers/user.py +61 -0
  106. moai_adk/templates/.claude/hooks/alfred/user_prompt__jit_load_docs.py +111 -0
  107. moai_adk/templates/.claude/hooks/alfred/utils/__init__.py +1 -0
  108. moai_adk/templates/.claude/hooks/alfred/utils/hook_config.py +94 -0
  109. moai_adk/templates/.claude/hooks/alfred/utils/timeout.py +161 -0
  110. moai_adk/templates/.claude/output-styles/alfred/alfred-moai-adk-beginner.md +267 -0
  111. moai_adk/templates/.claude/output-styles/alfred/keating-personal-tutor.md +440 -0
  112. moai_adk/templates/.claude/output-styles/alfred/r2d2-agentic-coding.md +583 -0
  113. moai_adk/templates/.claude/settings.json +96 -14
  114. moai_adk/templates/.claude/skills/moai-alfred-agent-guide/SKILL.md +70 -0
  115. moai_adk/templates/.claude/skills/moai-alfred-agent-guide/examples.md +62 -0
  116. moai_adk/templates/.claude/skills/moai-alfred-agent-guide/reference.md +242 -0
  117. moai_adk/templates/.claude/skills/moai-alfred-ask-user-questions/SKILL.md +237 -0
  118. moai_adk/templates/.claude/skills/moai-alfred-ask-user-questions/examples.md +871 -0
  119. moai_adk/templates/.claude/skills/moai-alfred-ask-user-questions/reference.md +653 -0
  120. moai_adk/templates/.claude/skills/moai-alfred-clone-pattern/README.md +162 -0
  121. moai_adk/templates/.claude/skills/moai-alfred-clone-pattern/SKILL.md +227 -0
  122. moai_adk/templates/.claude/skills/moai-alfred-clone-pattern/examples.md +354 -0
  123. moai_adk/templates/.claude/skills/moai-alfred-clone-pattern/reference.md +158 -0
  124. moai_adk/templates/.claude/skills/moai-alfred-code-reviewer/SKILL.md +179 -79
  125. moai_adk/templates/.claude/skills/moai-alfred-code-reviewer/examples.md +117 -0
  126. moai_adk/templates/.claude/skills/moai-alfred-code-reviewer/scripts/pre-review-check.sh +62 -0
  127. moai_adk/templates/.claude/skills/moai-alfred-config-schema/SKILL.md +132 -0
  128. moai_adk/templates/.claude/skills/moai-alfred-config-schema/examples.md +28 -0
  129. moai_adk/templates/.claude/skills/moai-alfred-config-schema/reference.md +444 -0
  130. moai_adk/templates/.claude/skills/moai-alfred-context-budget/SKILL.md +62 -0
  131. moai_adk/templates/.claude/skills/moai-alfred-context-budget/examples.md +28 -0
  132. moai_adk/templates/.claude/skills/moai-alfred-context-budget/reference.md +405 -0
  133. moai_adk/templates/.claude/skills/moai-alfred-dev-guide/SKILL.md +51 -0
  134. moai_adk/templates/.claude/skills/moai-alfred-dev-guide/examples.md +355 -0
  135. moai_adk/templates/.claude/skills/moai-alfred-dev-guide/reference.md +239 -0
  136. moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/SKILL.md +323 -0
  137. moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/examples.md +286 -0
  138. moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/reference.md +126 -0
  139. moai_adk/templates/.claude/skills/moai-alfred-issue-labels/SKILL.md +229 -0
  140. moai_adk/templates/.claude/skills/moai-alfred-issue-labels/examples.md +4 -0
  141. moai_adk/templates/.claude/skills/moai-alfred-issue-labels/reference.md +150 -0
  142. moai_adk/templates/.claude/skills/moai-alfred-language-detection/SKILL.md +87 -73
  143. moai_adk/templates/.claude/skills/moai-alfred-language-detection/examples.md +29 -0
  144. moai_adk/templates/.claude/skills/moai-alfred-language-detection/reference.md +28 -0
  145. moai_adk/templates/.claude/skills/moai-alfred-personas/README.md +42 -0
  146. moai_adk/templates/.claude/skills/moai-alfred-personas/SKILL.md +429 -0
  147. moai_adk/templates/.claude/skills/moai-alfred-personas/examples.md +520 -0
  148. moai_adk/templates/.claude/skills/moai-alfred-personas/reference.md +405 -0
  149. moai_adk/templates/.claude/skills/moai-alfred-practices/SKILL.md +89 -0
  150. moai_adk/templates/.claude/skills/moai-alfred-practices/examples.md +122 -0
  151. moai_adk/templates/.claude/skills/moai-alfred-practices/reference.md +369 -0
  152. moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/SKILL.md +508 -0
  153. moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/examples.md +481 -0
  154. moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/reference.md +100 -0
  155. moai_adk/templates/.claude/skills/moai-alfred-rules/SKILL.md +77 -0
  156. moai_adk/templates/.claude/skills/moai-alfred-rules/examples.md +265 -0
  157. moai_adk/templates/.claude/skills/moai-alfred-rules/reference.md +539 -0
  158. moai_adk/templates/.claude/skills/moai-alfred-session-state/SKILL.md +320 -0
  159. moai_adk/templates/.claude/skills/moai-alfred-session-state/examples.md +4 -0
  160. moai_adk/templates/.claude/skills/moai-alfred-session-state/reference.md +84 -0
  161. moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/README.md +137 -0
  162. moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/SKILL.md +219 -0
  163. moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/examples/validate-spec.sh +161 -0
  164. moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/examples.md +541 -0
  165. moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/reference.md +622 -0
  166. moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/SKILL.md +19 -0
  167. moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/examples.md +4 -0
  168. moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/reference.md +211 -0
  169. moai_adk/templates/.claude/skills/moai-alfred-workflow/SKILL.md +288 -0
  170. moai_adk/templates/.claude/skills/moai-cc-agents/SKILL.md +269 -0
  171. moai_adk/templates/.claude/skills/moai-cc-agents/templates/agent-template.md +32 -0
  172. moai_adk/templates/.claude/skills/moai-cc-claude-md/SKILL.md +298 -0
  173. moai_adk/templates/.claude/skills/moai-cc-claude-md/templates/CLAUDE-template.md +26 -0
  174. moai_adk/templates/.claude/skills/moai-cc-commands/SKILL.md +307 -0
  175. moai_adk/templates/.claude/skills/moai-cc-commands/templates/command-template.md +21 -0
  176. moai_adk/templates/.claude/skills/moai-cc-hooks/SKILL.md +252 -0
  177. moai_adk/templates/.claude/skills/moai-cc-hooks/scripts/pre-bash-check.sh +19 -0
  178. moai_adk/templates/.claude/skills/moai-cc-hooks/scripts/preserve-permissions.sh +19 -0
  179. moai_adk/templates/.claude/skills/moai-cc-hooks/scripts/validate-bash-command.py +24 -0
  180. moai_adk/templates/.claude/skills/moai-cc-mcp-plugins/SKILL.md +199 -0
  181. moai_adk/templates/.claude/skills/moai-cc-mcp-plugins/templates/settings-mcp-template.json +39 -0
  182. moai_adk/templates/.claude/skills/moai-cc-memory/SKILL.md +316 -0
  183. moai_adk/templates/.claude/skills/moai-cc-memory/templates/session-summary-template.md +18 -0
  184. moai_adk/templates/.claude/skills/moai-cc-settings/SKILL.md +263 -0
  185. moai_adk/templates/.claude/skills/moai-cc-settings/templates/settings-complete-template.json +30 -0
  186. moai_adk/templates/.claude/skills/moai-cc-skill-factory/CHECKLIST.md +482 -0
  187. moai_adk/templates/.claude/skills/moai-cc-skill-factory/EXAMPLES.md +303 -0
  188. moai_adk/templates/.claude/skills/moai-cc-skill-factory/INTERACTIVE-DISCOVERY.md +524 -0
  189. moai_adk/templates/.claude/skills/moai-cc-skill-factory/METADATA.md +477 -0
  190. moai_adk/templates/.claude/skills/moai-cc-skill-factory/PARALLEL-ANALYSIS-REPORT.md +429 -0
  191. moai_adk/templates/.claude/skills/moai-cc-skill-factory/PYTHON-VERSION-MATRIX.md +391 -0
  192. moai_adk/templates/.claude/skills/moai-cc-skill-factory/SKILL-FACTORY-WORKFLOW.md +431 -0
  193. moai_adk/templates/.claude/skills/moai-cc-skill-factory/SKILL-UPDATE-ADVISOR.md +577 -0
  194. moai_adk/templates/.claude/skills/moai-cc-skill-factory/SKILL.md +273 -0
  195. moai_adk/templates/.claude/skills/moai-cc-skill-factory/STEP-BY-STEP-GUIDE.md +466 -0
  196. moai_adk/templates/.claude/skills/moai-cc-skill-factory/STRUCTURE.md +583 -0
  197. moai_adk/templates/.claude/skills/moai-cc-skill-factory/WEB-RESEARCH.md +526 -0
  198. moai_adk/templates/.claude/skills/moai-cc-skill-factory/reference.md +608 -0
  199. moai_adk/templates/.claude/skills/moai-cc-skill-factory/scripts/generate-structure.sh +328 -0
  200. moai_adk/templates/.claude/skills/moai-cc-skill-factory/scripts/validate-skill.sh +312 -0
  201. moai_adk/templates/.claude/skills/moai-cc-skill-factory/templates/SKILL_TEMPLATE.md +245 -0
  202. moai_adk/templates/.claude/skills/moai-cc-skill-factory/templates/examples-template.md +285 -0
  203. moai_adk/templates/.claude/skills/moai-cc-skill-factory/templates/reference-template.md +278 -0
  204. moai_adk/templates/.claude/skills/moai-cc-skill-factory/templates/scripts-template.sh +303 -0
  205. moai_adk/templates/.claude/skills/moai-cc-skills/SKILL.md +291 -0
  206. moai_adk/templates/.claude/skills/moai-cc-skills/templates/SKILL-template.md +15 -0
  207. moai_adk/templates/.claude/skills/moai-change-logger/SKILL.md +563 -0
  208. moai_adk/templates/.claude/skills/moai-design-systems/SKILL.md +802 -0
  209. moai_adk/templates/.claude/skills/moai-design-systems/examples.md +1238 -0
  210. moai_adk/templates/.claude/skills/moai-design-systems/reference.md +673 -0
  211. moai_adk/templates/.claude/skills/moai-domain-backend/SKILL.md +234 -43
  212. moai_adk/templates/.claude/skills/moai-domain-backend/examples.md +1633 -0
  213. moai_adk/templates/.claude/skills/moai-domain-backend/reference.md +660 -0
  214. moai_adk/templates/.claude/skills/moai-domain-cli-tool/SKILL.md +97 -69
  215. moai_adk/templates/.claude/skills/moai-domain-cli-tool/examples.md +29 -0
  216. moai_adk/templates/.claude/skills/moai-domain-cli-tool/reference.md +30 -0
  217. moai_adk/templates/.claude/skills/moai-domain-data-science/SKILL.md +97 -72
  218. moai_adk/templates/.claude/skills/moai-domain-data-science/examples.md +29 -0
  219. moai_adk/templates/.claude/skills/moai-domain-data-science/reference.md +30 -0
  220. moai_adk/templates/.claude/skills/moai-domain-database/SKILL.md +97 -74
  221. moai_adk/templates/.claude/skills/moai-domain-database/examples.md +29 -0
  222. moai_adk/templates/.claude/skills/moai-domain-database/reference.md +30 -0
  223. moai_adk/templates/.claude/skills/moai-domain-devops/SKILL.md +98 -74
  224. moai_adk/templates/.claude/skills/moai-domain-devops/examples.md +29 -0
  225. moai_adk/templates/.claude/skills/moai-domain-devops/reference.md +31 -0
  226. moai_adk/templates/.claude/skills/moai-domain-frontend/SKILL.md +102 -73
  227. moai_adk/templates/.claude/skills/moai-domain-frontend/examples.md +29 -0
  228. moai_adk/templates/.claude/skills/moai-domain-frontend/reference.md +31 -0
  229. moai_adk/templates/.claude/skills/moai-domain-ml/SKILL.md +97 -73
  230. moai_adk/templates/.claude/skills/moai-domain-ml/examples.md +29 -0
  231. moai_adk/templates/.claude/skills/moai-domain-ml/reference.md +30 -0
  232. moai_adk/templates/.claude/skills/moai-domain-mobile-app/SKILL.md +97 -67
  233. moai_adk/templates/.claude/skills/moai-domain-mobile-app/examples.md +29 -0
  234. moai_adk/templates/.claude/skills/moai-domain-mobile-app/reference.md +30 -0
  235. moai_adk/templates/.claude/skills/moai-domain-security/SKILL.md +97 -79
  236. moai_adk/templates/.claude/skills/moai-domain-security/examples.md +29 -0
  237. moai_adk/templates/.claude/skills/moai-domain-security/reference.md +30 -0
  238. moai_adk/templates/.claude/skills/moai-domain-web-api/SKILL.md +97 -71
  239. moai_adk/templates/.claude/skills/moai-domain-web-api/examples.md +29 -0
  240. moai_adk/templates/.claude/skills/moai-domain-web-api/reference.md +30 -0
  241. moai_adk/templates/.claude/skills/moai-essentials-debug/SKILL.md +265 -64
  242. moai_adk/templates/.claude/skills/moai-essentials-debug/examples.md +1064 -0
  243. moai_adk/templates/.claude/skills/moai-essentials-debug/reference.md +1047 -0
  244. moai_adk/templates/.claude/skills/moai-essentials-perf/SKILL.md +87 -78
  245. moai_adk/templates/.claude/skills/moai-essentials-perf/examples.md +29 -0
  246. moai_adk/templates/.claude/skills/moai-essentials-perf/reference.md +28 -0
  247. moai_adk/templates/.claude/skills/moai-essentials-refactor/SKILL.md +87 -70
  248. moai_adk/templates/.claude/skills/moai-essentials-refactor/examples.md +29 -0
  249. moai_adk/templates/.claude/skills/moai-essentials-refactor/reference.md +28 -0
  250. moai_adk/templates/.claude/skills/moai-essentials-review/SKILL.md +87 -86
  251. moai_adk/templates/.claude/skills/moai-essentials-review/examples.md +29 -0
  252. moai_adk/templates/.claude/skills/moai-essentials-review/reference.md +28 -0
  253. moai_adk/templates/.claude/skills/moai-foundation-ears/SKILL.md +80 -62
  254. moai_adk/templates/.claude/skills/moai-foundation-ears/examples.md +29 -0
  255. moai_adk/templates/.claude/skills/moai-foundation-ears/reference.md +28 -0
  256. moai_adk/templates/.claude/skills/moai-foundation-git/SKILL.md +207 -50
  257. moai_adk/templates/.claude/skills/moai-foundation-git/examples.md +29 -0
  258. moai_adk/templates/.claude/skills/moai-foundation-git/reference.md +29 -0
  259. moai_adk/templates/.claude/skills/moai-foundation-langs/SKILL.md +90 -71
  260. moai_adk/templates/.claude/skills/moai-foundation-langs/examples.md +29 -0
  261. moai_adk/templates/.claude/skills/moai-foundation-langs/reference.md +28 -0
  262. moai_adk/templates/.claude/skills/moai-foundation-specs/SKILL.md +78 -58
  263. moai_adk/templates/.claude/skills/moai-foundation-specs/examples.md +29 -0
  264. moai_adk/templates/.claude/skills/moai-foundation-specs/reference.md +28 -0
  265. moai_adk/templates/.claude/skills/moai-foundation-tags/SKILL.md +78 -51
  266. moai_adk/templates/.claude/skills/moai-foundation-tags/examples.md +29 -0
  267. moai_adk/templates/.claude/skills/moai-foundation-tags/reference.md +28 -0
  268. moai_adk/templates/.claude/skills/moai-foundation-trust/.!11330!examples.md +0 -0
  269. moai_adk/templates/.claude/skills/moai-foundation-trust/SKILL.md +253 -32
  270. moai_adk/templates/.claude/skills/moai-foundation-trust/examples.md +0 -0
  271. moai_adk/templates/.claude/skills/moai-foundation-trust/reference.md +1099 -0
  272. moai_adk/templates/.claude/skills/moai-jit-docs-enhanced/SKILL.md +460 -0
  273. moai_adk/templates/.claude/skills/moai-lang-c/SKILL.md +98 -74
  274. moai_adk/templates/.claude/skills/moai-lang-c/examples.md +29 -0
  275. moai_adk/templates/.claude/skills/moai-lang-c/reference.md +31 -0
  276. moai_adk/templates/.claude/skills/moai-lang-cpp/SKILL.md +98 -76
  277. moai_adk/templates/.claude/skills/moai-lang-cpp/examples.md +29 -0
  278. moai_adk/templates/.claude/skills/moai-lang-cpp/reference.md +31 -0
  279. moai_adk/templates/.claude/skills/moai-lang-csharp/SKILL.md +2358 -70
  280. moai_adk/templates/.claude/skills/moai-lang-csharp/examples.md +29 -0
  281. moai_adk/templates/.claude/skills/moai-lang-csharp/reference.md +30 -0
  282. moai_adk/templates/.claude/skills/moai-lang-dart/SKILL.md +2962 -68
  283. moai_adk/templates/.claude/skills/moai-lang-dart/examples.md +29 -0
  284. moai_adk/templates/.claude/skills/moai-lang-dart/reference.md +30 -0
  285. moai_adk/templates/.claude/skills/moai-lang-go/SKILL.md +1898 -70
  286. moai_adk/templates/.claude/skills/moai-lang-go/examples.md +29 -0
  287. moai_adk/templates/.claude/skills/moai-lang-go/reference.md +31 -0
  288. moai_adk/templates/.claude/skills/moai-lang-java/SKILL.md +1465 -68
  289. moai_adk/templates/.claude/skills/moai-lang-java/examples.md +29 -0
  290. moai_adk/templates/.claude/skills/moai-lang-java/reference.md +31 -0
  291. moai_adk/templates/.claude/skills/moai-lang-javascript/SKILL.md +2364 -66
  292. moai_adk/templates/.claude/skills/moai-lang-javascript/examples.md +29 -0
  293. moai_adk/templates/.claude/skills/moai-lang-javascript/reference.md +32 -0
  294. moai_adk/templates/.claude/skills/moai-lang-kotlin/SKILL.md +1630 -69
  295. moai_adk/templates/.claude/skills/moai-lang-kotlin/examples.md +29 -0
  296. moai_adk/templates/.claude/skills/moai-lang-kotlin/reference.md +31 -0
  297. moai_adk/templates/.claude/skills/moai-lang-php/SKILL.md +89 -61
  298. moai_adk/templates/.claude/skills/moai-lang-php/examples.md +29 -0
  299. moai_adk/templates/.claude/skills/moai-lang-php/reference.md +30 -0
  300. moai_adk/templates/.claude/skills/moai-lang-python/SKILL.md +735 -66
  301. moai_adk/templates/.claude/skills/moai-lang-python/examples.md +624 -0
  302. moai_adk/templates/.claude/skills/moai-lang-python/reference.md +316 -0
  303. moai_adk/templates/.claude/skills/moai-lang-r/SKILL.md +97 -73
  304. moai_adk/templates/.claude/skills/moai-lang-r/examples.md +29 -0
  305. moai_adk/templates/.claude/skills/moai-lang-r/reference.md +30 -0
  306. moai_adk/templates/.claude/skills/moai-lang-ruby/SKILL.md +98 -73
  307. moai_adk/templates/.claude/skills/moai-lang-ruby/examples.md +29 -0
  308. moai_adk/templates/.claude/skills/moai-lang-ruby/reference.md +31 -0
  309. moai_adk/templates/.claude/skills/moai-lang-rust/SKILL.md +1834 -70
  310. moai_adk/templates/.claude/skills/moai-lang-rust/examples.md +29 -0
  311. moai_adk/templates/.claude/skills/moai-lang-rust/reference.md +31 -0
  312. moai_adk/templates/.claude/skills/moai-lang-scala/SKILL.md +99 -74
  313. moai_adk/templates/.claude/skills/moai-lang-scala/examples.md +29 -0
  314. moai_adk/templates/.claude/skills/moai-lang-scala/reference.md +30 -0
  315. moai_adk/templates/.claude/skills/moai-lang-shell/SKILL.md +97 -74
  316. moai_adk/templates/.claude/skills/moai-lang-shell/examples.md +29 -0
  317. moai_adk/templates/.claude/skills/moai-lang-shell/reference.md +30 -0
  318. moai_adk/templates/.claude/skills/moai-lang-sql/SKILL.md +98 -74
  319. moai_adk/templates/.claude/skills/moai-lang-sql/examples.md +29 -0
  320. moai_adk/templates/.claude/skills/moai-lang-sql/reference.md +31 -0
  321. moai_adk/templates/.claude/skills/moai-lang-swift/SKILL.md +1959 -69
  322. moai_adk/templates/.claude/skills/moai-lang-swift/examples.md +29 -0
  323. moai_adk/templates/.claude/skills/moai-lang-swift/reference.md +30 -0
  324. moai_adk/templates/.claude/skills/moai-lang-template/SKILL.md +348 -0
  325. moai_adk/templates/.claude/skills/moai-lang-template/VARIABLES.md +98 -0
  326. moai_adk/templates/.claude/skills/moai-lang-typescript/SKILL.md +1230 -66
  327. moai_adk/templates/.claude/skills/moai-lang-typescript/examples.md +29 -0
  328. moai_adk/templates/.claude/skills/moai-lang-typescript/reference.md +34 -0
  329. moai_adk/templates/.claude/skills/moai-learning-optimizer/SKILL.md +575 -0
  330. moai_adk/templates/.claude/skills/moai-project-batch-questions/README.md +50 -0
  331. moai_adk/templates/.claude/skills/moai-project-batch-questions/SKILL.md +304 -0
  332. moai_adk/templates/.claude/skills/moai-project-batch-questions/examples.md +417 -0
  333. moai_adk/templates/.claude/skills/moai-project-batch-questions/reference.md +704 -0
  334. moai_adk/templates/.claude/skills/moai-project-config-manager/README.md +87 -0
  335. moai_adk/templates/.claude/skills/moai-project-config-manager/SKILL.md +552 -0
  336. moai_adk/templates/.claude/skills/moai-project-config-manager/examples.md +1109 -0
  337. moai_adk/templates/.claude/skills/moai-project-config-manager/reference.md +514 -0
  338. moai_adk/templates/.claude/skills/moai-project-config-manager/validate.py +106 -0
  339. moai_adk/templates/.claude/skills/moai-project-documentation/README.md +11 -0
  340. moai_adk/templates/.claude/skills/moai-project-documentation/SKILL.md +622 -0
  341. moai_adk/templates/.claude/skills/moai-project-documentation/examples.md +20 -0
  342. moai_adk/templates/.claude/skills/moai-project-documentation/reference.md +12 -0
  343. moai_adk/templates/.claude/skills/moai-project-language-initializer/README.md +152 -0
  344. moai_adk/templates/.claude/skills/moai-project-language-initializer/SKILL.md +285 -0
  345. moai_adk/templates/.claude/skills/moai-project-language-initializer/examples.md +333 -0
  346. moai_adk/templates/.claude/skills/moai-project-language-initializer/reference.md +386 -0
  347. moai_adk/templates/.claude/skills/moai-project-template-optimizer/README.md +49 -0
  348. moai_adk/templates/.claude/skills/moai-project-template-optimizer/SKILL.md +319 -0
  349. moai_adk/templates/.claude/skills/moai-project-template-optimizer/examples.md +58 -0
  350. moai_adk/templates/.claude/skills/moai-project-template-optimizer/reference.md +123 -0
  351. moai_adk/templates/.claude/skills/moai-session-info/SKILL.md +314 -0
  352. moai_adk/templates/.claude/skills/moai-streaming-ui/SKILL.md +552 -0
  353. moai_adk/templates/.claude/skills/moai-tag-policy-validator/SKILL.md +570 -0
  354. moai_adk/templates/.git-hooks/pre-commit +66 -0
  355. moai_adk/templates/.git-hooks/pre-push +255 -0
  356. moai_adk/templates/.github/workflows/c-tag-validation.yml +11 -0
  357. moai_adk/templates/.github/workflows/cpp-tag-validation.yml +11 -0
  358. moai_adk/templates/.github/workflows/csharp-tag-validation.yml +11 -0
  359. moai_adk/templates/.github/workflows/dart-tag-validation.yml +11 -0
  360. moai_adk/templates/.github/workflows/go-tag-validation.yml +130 -0
  361. moai_adk/templates/.github/workflows/java-tag-validation.yml +11 -0
  362. moai_adk/templates/.github/workflows/javascript-tag-validation.yml +135 -0
  363. moai_adk/templates/.github/workflows/kotlin-tag-validation.yml +11 -0
  364. moai_adk/templates/.github/workflows/moai-gitflow.yml +166 -3
  365. moai_adk/templates/.github/workflows/moai-release-create.yml +100 -0
  366. moai_adk/templates/.github/workflows/moai-release-pipeline.yml +188 -0
  367. moai_adk/templates/.github/workflows/php-tag-validation.yml +11 -0
  368. moai_adk/templates/.github/workflows/python-tag-validation.yml +118 -0
  369. moai_adk/templates/.github/workflows/release.yml +118 -0
  370. moai_adk/templates/.github/workflows/ruby-tag-validation.yml +11 -0
  371. moai_adk/templates/.github/workflows/rust-tag-validation.yml +11 -0
  372. moai_adk/templates/.github/workflows/shell-tag-validation.yml +11 -0
  373. moai_adk/templates/.github/workflows/spec-issue-sync.yml +338 -0
  374. moai_adk/templates/.github/workflows/swift-tag-validation.yml +11 -0
  375. moai_adk/templates/.github/workflows/tag-report.yml +269 -0
  376. moai_adk/templates/.github/workflows/tag-validation.yml +186 -0
  377. moai_adk/templates/.github/workflows/typescript-tag-validation.yml +154 -0
  378. moai_adk/templates/.mcp.json +31 -0
  379. moai_adk/templates/.moai/config.json +80 -7
  380. moai_adk/templates/CLAUDE.md +562 -546
  381. moai_adk/utils/banner.py +5 -5
  382. moai_adk/utils/common.py +294 -0
  383. moai_adk/utils/link_validator.py +235 -0
  384. moai_adk/utils/logger.py +8 -8
  385. moai_adk/utils/user_experience.py +451 -0
  386. moai_adk-0.20.1.dist-info/METADATA +233 -0
  387. moai_adk-0.20.1.dist-info/RECORD +404 -0
  388. moai_adk/templates/.claude/hooks/alfred/README.md +0 -230
  389. moai_adk/templates/.claude/hooks/alfred/alfred_hooks.py +0 -156
  390. moai_adk/templates/.claude/hooks/alfred/core/__init__.py +0 -85
  391. moai_adk/templates/.claude/hooks/alfred/handlers/notification.py +0 -25
  392. moai_adk/templates/.claude/hooks/alfred/handlers/session.py +0 -92
  393. moai_adk/templates/.claude/hooks/alfred/handlers/tool.py +0 -70
  394. moai_adk/templates/.claude/hooks/alfred/handlers/user.py +0 -41
  395. moai_adk/templates/.claude/output-styles/alfred/agentic-coding.md +0 -636
  396. moai_adk/templates/.claude/output-styles/alfred/moai-adk-learning.md +0 -692
  397. moai_adk/templates/.claude/output-styles/alfred/study-with-alfred.md +0 -470
  398. moai_adk/templates/.claude/skills/moai-alfred-debugger-pro/SKILL.md +0 -103
  399. moai_adk/templates/.claude/skills/moai-alfred-ears-authoring/SKILL.md +0 -103
  400. moai_adk/templates/.claude/skills/moai-alfred-git-workflow/SKILL.md +0 -95
  401. moai_adk/templates/.claude/skills/moai-alfred-performance-optimizer/SKILL.md +0 -105
  402. moai_adk/templates/.claude/skills/moai-alfred-refactoring-coach/SKILL.md +0 -97
  403. moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-validation/SKILL.md +0 -97
  404. moai_adk/templates/.claude/skills/moai-alfred-tag-scanning/SKILL.md +0 -90
  405. moai_adk/templates/.claude/skills/moai-alfred-trust-validation/SKILL.md +0 -99
  406. moai_adk/templates/.claude/skills/moai-alfred-tui-survey/SKILL.md +0 -87
  407. moai_adk/templates/.claude/skills/moai-alfred-tui-survey/examples.md +0 -62
  408. moai_adk/templates/.claude/skills/moai-claude-code/SKILL.md +0 -94
  409. moai_adk/templates/.claude/skills/moai-claude-code/examples.md +0 -513
  410. moai_adk/templates/.claude/skills/moai-claude-code/reference.md +0 -433
  411. moai_adk/templates/.claude/skills/moai-claude-code/templates/agent-full.md +0 -332
  412. moai_adk/templates/.claude/skills/moai-claude-code/templates/command-full.md +0 -384
  413. moai_adk/templates/.claude/skills/moai-claude-code/templates/plugin-full.json +0 -363
  414. moai_adk/templates/.claude/skills/moai-claude-code/templates/settings-full.json +0 -595
  415. moai_adk/templates/.claude/skills/moai-claude-code/templates/skill-full.md +0 -496
  416. moai_adk/templates/.claude/skills/moai-lang-clojure/SKILL.md +0 -100
  417. moai_adk/templates/.claude/skills/moai-lang-elixir/SKILL.md +0 -99
  418. moai_adk/templates/.claude/skills/moai-lang-haskell/SKILL.md +0 -100
  419. moai_adk/templates/.claude/skills/moai-lang-julia/SKILL.md +0 -98
  420. moai_adk/templates/.claude/skills/moai-lang-lua/SKILL.md +0 -98
  421. moai_adk/templates/.github/PULL_REQUEST_TEMPLATE.md +0 -69
  422. moai_adk/templates/.moai/memory/development-guide.md +0 -344
  423. moai_adk/templates/.moai/memory/gitflow-protection-policy.md +0 -220
  424. moai_adk/templates/.moai/memory/spec-metadata.md +0 -356
  425. moai_adk/templates/.moai/project/product.md +0 -161
  426. moai_adk/templates/.moai/project/structure.md +0 -156
  427. moai_adk/templates/.moai/project/tech.md +0 -227
  428. moai_adk/templates/__init__.py +0 -2
  429. moai_adk-0.4.5.dist-info/METADATA +0 -369
  430. moai_adk-0.4.5.dist-info/RECORD +0 -152
  431. {moai_adk-0.4.5.dist-info → moai_adk-0.20.1.dist-info}/WHEEL +0 -0
  432. {moai_adk-0.4.5.dist-info → moai_adk-0.20.1.dist-info}/entry_points.txt +0 -0
  433. {moai_adk-0.4.5.dist-info → moai_adk-0.20.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,7 +1,48 @@
1
- """Update command"""
1
+ """Update command
2
+
3
+ Update MoAI-ADK to the latest version available on PyPI with 3-stage workflow:
4
+ - Stage 1: Package version check (PyPI vs current)
5
+ - Stage 2: Config version comparison (template_version in config.json)
6
+ - Stage 3: Template sync (only if versions differ)
7
+
8
+ Includes:
9
+ - Automatic installer detection (uv tool, pipx, pip)
10
+ - Package upgrade with intelligent re-run prompts
11
+ - Template and configuration updates with performance optimization
12
+ - Backward compatibility validation
13
+ - 70-80% performance improvement for up-to-date projects
14
+
15
+ ## Skill Invocation Guide (English-Only)
16
+
17
+ ### Related Skills
18
+ - **moai-foundation-trust**: For post-update validation
19
+ - Trigger: After updating MoAI-ADK version
20
+ - Invocation: `Skill("moai-foundation-trust")` to verify all toolchains still work
21
+
22
+ - **moai-foundation-langs**: For language detection after update
23
+ - Trigger: After updating, confirm language stack is intact
24
+ - Invocation: `Skill("moai-foundation-langs")` to re-detect and validate language configuration
25
+
26
+ ### When to Invoke Skills in Related Workflows
27
+ 1. **After successful update**:
28
+ - Run `Skill("moai-foundation-trust")` to validate all TRUST 5 gates
29
+ - Run `Skill("moai-foundation-langs")` to confirm language toolchain still works
30
+ - Run project doctor command for full system validation
31
+
32
+ 2. **Before updating**:
33
+ - Create backup with `python -m moai_adk backup`
34
+ - Run `Skill("moai-foundation-tags")` to document current TAG state
35
+
36
+ 3. **If update fails**:
37
+ - Use backup to restore previous state
38
+ - Debug with `python -m moai_adk doctor --verbose`
39
+ """
40
+
2
41
  from __future__ import annotations
3
42
 
4
43
  import json
44
+ import logging
45
+ import subprocess
5
46
  from datetime import datetime
6
47
  from pathlib import Path
7
48
  from typing import Any, cast
@@ -14,13 +55,153 @@ from moai_adk import __version__
14
55
  from moai_adk.core.template.processor import TemplateProcessor
15
56
 
16
57
  console = Console()
58
+ logger = logging.getLogger(__name__)
17
59
 
60
+ # Constants for tool detection
61
+ TOOL_DETECTION_TIMEOUT = 5 # seconds
62
+ UV_TOOL_COMMAND = ["uv", "tool", "upgrade", "moai-adk"]
63
+ PIPX_COMMAND = ["pipx", "upgrade", "moai-adk"]
64
+ PIP_COMMAND = ["pip", "install", "--upgrade", "moai-adk"]
18
65
 
19
- def get_latest_version() -> str | None:
20
- """Get the latest version from PyPI.
66
+
67
+ # @CODE:UPDATE-TEMPLATE-004
68
+ # Custom exceptions for better error handling
69
+ class UpdateError(Exception):
70
+ """Base exception for update operations."""
71
+
72
+ pass
73
+
74
+
75
+ class InstallerNotFoundError(UpdateError):
76
+ """Raised when no package installer detected."""
77
+
78
+ pass
79
+
80
+
81
+ class NetworkError(UpdateError):
82
+ """Raised when network operation fails."""
83
+
84
+ pass
85
+
86
+
87
+ class UpgradeError(UpdateError):
88
+ """Raised when package upgrade fails."""
89
+
90
+ pass
91
+
92
+
93
+ class TemplateSyncError(UpdateError):
94
+ """Raised when template sync fails."""
95
+
96
+ pass
97
+
98
+
99
+ def _is_installed_via_uv_tool() -> bool:
100
+ """Check if moai-adk installed via uv tool.
21
101
 
22
102
  Returns:
23
- Latest version string, or None if fetch fails.
103
+ True if uv tool list shows moai-adk, False otherwise
104
+ """
105
+ try:
106
+ result = subprocess.run(
107
+ ["uv", "tool", "list"], capture_output=True, text=True, timeout=TOOL_DETECTION_TIMEOUT, check=False
108
+ )
109
+ return result.returncode == 0 and "moai-adk" in result.stdout
110
+ except (FileNotFoundError, subprocess.TimeoutExpired, OSError):
111
+ return False
112
+
113
+
114
+ def _is_installed_via_pipx() -> bool:
115
+ """Check if moai-adk installed via pipx.
116
+
117
+ Returns:
118
+ True if pipx list shows moai-adk, False otherwise
119
+ """
120
+ try:
121
+ result = subprocess.run(
122
+ ["pipx", "list"], capture_output=True, text=True, timeout=TOOL_DETECTION_TIMEOUT, check=False
123
+ )
124
+ return result.returncode == 0 and "moai-adk" in result.stdout
125
+ except (FileNotFoundError, subprocess.TimeoutExpired, OSError):
126
+ return False
127
+
128
+
129
+ def _is_installed_via_pip() -> bool:
130
+ """Check if moai-adk installed via pip.
131
+
132
+ Returns:
133
+ True if pip show finds moai-adk, False otherwise
134
+ """
135
+ try:
136
+ result = subprocess.run(
137
+ ["pip", "show", "moai-adk"], capture_output=True, text=True, timeout=TOOL_DETECTION_TIMEOUT, check=False
138
+ )
139
+ return result.returncode == 0
140
+ except (FileNotFoundError, subprocess.TimeoutExpired, OSError):
141
+ return False
142
+
143
+
144
+ # @CODE:UPDATE-CONTEXT-001
145
+ def _detect_tool_installer() -> list[str] | None:
146
+ """Detect which tool installed moai-adk.
147
+
148
+ Checks in priority order:
149
+ 1. uv tool (most likely for MoAI-ADK users)
150
+ 2. pipx
151
+ 3. pip (fallback)
152
+
153
+ Returns:
154
+ Command list [tool, ...args] ready for subprocess.run()
155
+ or None if detection fails
156
+
157
+ Examples:
158
+ >>> # If uv tool is detected:
159
+ >>> _detect_tool_installer()
160
+ ['uv', 'tool', 'upgrade', 'moai-adk']
161
+
162
+ >>> # If pipx is detected:
163
+ >>> _detect_tool_installer()
164
+ ['pipx', 'upgrade', 'moai-adk']
165
+
166
+ >>> # If only pip is available:
167
+ >>> _detect_tool_installer()
168
+ ['pip', 'install', '--upgrade', 'moai-adk']
169
+
170
+ >>> # If none are detected:
171
+ >>> _detect_tool_installer()
172
+ None
173
+ """
174
+ if _is_installed_via_uv_tool():
175
+ return UV_TOOL_COMMAND
176
+ elif _is_installed_via_pipx():
177
+ return PIPX_COMMAND
178
+ elif _is_installed_via_pip():
179
+ return PIP_COMMAND
180
+ else:
181
+ return None
182
+
183
+
184
+ # @CODE:UPDATE-VERSION-002
185
+ def _get_current_version() -> str:
186
+ """Get currently installed moai-adk version.
187
+
188
+ Returns:
189
+ Version string (e.g., "0.6.1")
190
+
191
+ Raises:
192
+ RuntimeError: If version cannot be determined
193
+ """
194
+ return __version__
195
+
196
+
197
+ def _get_latest_version() -> str:
198
+ """Fetch latest moai-adk version from PyPI.
199
+
200
+ Returns:
201
+ Version string (e.g., "0.6.2")
202
+
203
+ Raises:
204
+ RuntimeError: If PyPI API unavailable or parsing fails
24
205
  """
25
206
  try:
26
207
  import urllib.error
@@ -29,10 +210,387 @@ def get_latest_version() -> str | None:
29
210
  url = "https://pypi.org/pypi/moai-adk/json"
30
211
  with urllib.request.urlopen(url, timeout=5) as response: # nosec B310 - URL is hardcoded HTTPS to PyPI API, no user input
31
212
  data = json.loads(response.read().decode("utf-8"))
32
- version_str: str = cast(str, data["info"]["version"])
33
- return version_str
34
- except (urllib.error.URLError, json.JSONDecodeError, KeyError, TimeoutError):
35
- # Return None if PyPI check fails
213
+ return cast(str, data["info"]["version"])
214
+ except (urllib.error.URLError, json.JSONDecodeError, KeyError, TimeoutError) as e:
215
+ raise RuntimeError(f"Failed to fetch latest version from PyPI: {e}") from e
216
+
217
+
218
+ def _compare_versions(current: str, latest: str) -> int:
219
+ """Compare semantic versions.
220
+
221
+ Args:
222
+ current: Current version string
223
+ latest: Latest version string
224
+
225
+ Returns:
226
+ -1 if current < latest (upgrade needed)
227
+ 0 if current == latest (up to date)
228
+ 1 if current > latest (unusual, already newer)
229
+ """
230
+ current_v = version.parse(current)
231
+ latest_v = version.parse(latest)
232
+
233
+ if current_v < latest_v:
234
+ return -1
235
+ elif current_v == latest_v:
236
+ return 0
237
+ else:
238
+ return 1
239
+
240
+
241
+ # @CODE:UPDATE-SYNC-006
242
+ def _get_package_config_version() -> str:
243
+ """Get the current package template version.
244
+
245
+ This returns the version of the currently installed moai-adk package,
246
+ which is the version of templates that this package provides.
247
+
248
+ Returns:
249
+ Version string of the installed package (e.g., "0.6.1")
250
+ """
251
+ # Package template version = current installed package version
252
+ # This is simple and reliable since templates are versioned with the package
253
+ return __version__
254
+
255
+
256
+ # @CODE:UPDATE-PACKAGE-007
257
+ def _get_project_config_version(project_path: Path) -> str:
258
+ """Get current project config.json template version.
259
+
260
+ This reads the project's .moai/config.json to determine the current
261
+ template version that the project is configured with.
262
+
263
+ Args:
264
+ project_path: Project directory path (absolute)
265
+
266
+ Returns:
267
+ Version string from project's config.json (e.g., "0.6.1")
268
+ Returns "0.0.0" if template_version field not found (indicates no prior sync)
269
+
270
+ Raises:
271
+ ValueError: If config.json exists but cannot be parsed
272
+ """
273
+
274
+ def _is_placeholder(value: str) -> bool:
275
+ """Check if value contains unsubstituted template placeholders."""
276
+ return isinstance(value, str) and value.startswith("{{") and value.endswith("}}")
277
+
278
+ config_path = project_path / ".moai" / "config.json"
279
+
280
+ if not config_path.exists():
281
+ # No config yet, treat as version 0.0.0 (needs initial sync)
282
+ return "0.0.0"
283
+
284
+ try:
285
+ config_data = json.loads(config_path.read_text(encoding="utf-8"))
286
+ # Check for template_version in project section
287
+ template_version = config_data.get("project", {}).get("template_version")
288
+ if template_version and not _is_placeholder(template_version):
289
+ return template_version
290
+
291
+ # Fallback to moai version if no template_version exists
292
+ moai_version = config_data.get("moai", {}).get("version")
293
+ if moai_version and not _is_placeholder(moai_version):
294
+ return moai_version
295
+
296
+ # If values are placeholders or don't exist, treat as uninitialized (0.0.0 triggers sync)
297
+ return "0.0.0"
298
+ except json.JSONDecodeError as e:
299
+ raise ValueError(f"Failed to parse project config.json: {e}") from e
300
+
301
+
302
+ # @CODE:UPDATE-CACHE-001
303
+ def _detect_stale_cache(upgrade_output: str, current_version: str, latest_version: str) -> bool:
304
+ """
305
+ Detect if uv cache is stale by comparing versions.
306
+
307
+ A stale cache occurs when PyPI metadata is outdated, causing uv to incorrectly
308
+ report "Nothing to upgrade" even though a newer version exists. This function
309
+ detects this condition by:
310
+ 1. Checking if upgrade output contains "Nothing to upgrade"
311
+ 2. Verifying that latest version is actually newer than current version
312
+
313
+ Uses packaging.version.parse() for robust semantic version comparison that
314
+ handles pre-releases, dev versions, and other PEP 440 version formats correctly.
315
+
316
+ Args:
317
+ upgrade_output: Output from uv tool upgrade command
318
+ current_version: Currently installed version (string, e.g., "0.8.3")
319
+ latest_version: Latest version available on PyPI (string, e.g., "0.9.0")
320
+
321
+ Returns:
322
+ True if cache is stale (output shows "Nothing to upgrade" but current < latest),
323
+ False otherwise
324
+
325
+ Examples:
326
+ >>> _detect_stale_cache("Nothing to upgrade", "0.8.3", "0.9.0")
327
+ True
328
+ >>> _detect_stale_cache("Updated moai-adk", "0.8.3", "0.9.0")
329
+ False
330
+ >>> _detect_stale_cache("Nothing to upgrade", "0.9.0", "0.9.0")
331
+ False
332
+ """
333
+ # Check if output indicates no upgrade needed
334
+ if not upgrade_output or "Nothing to upgrade" not in upgrade_output:
335
+ return False
336
+
337
+ # Compare versions using packaging.version
338
+ try:
339
+ current_v = version.parse(current_version)
340
+ latest_v = version.parse(latest_version)
341
+ return current_v < latest_v
342
+ except (version.InvalidVersion, TypeError) as e:
343
+ # Graceful degradation: if version parsing fails, assume cache is not stale
344
+ logger.debug(f"Version parsing failed: {e}")
345
+ return False
346
+
347
+
348
+ # @CODE:UPDATE-CACHE-002
349
+ def _clear_uv_package_cache(package_name: str = "moai-adk") -> bool:
350
+ """
351
+ Clear uv cache for specific package.
352
+
353
+ Executes `uv cache clean <package>` with 10-second timeout to prevent
354
+ hanging on network issues. Provides user-friendly error handling for
355
+ various failure scenarios (timeout, missing uv, etc.).
356
+
357
+ Args:
358
+ package_name: Package name to clear cache for (default: "moai-adk")
359
+
360
+ Returns:
361
+ True if cache cleared successfully, False otherwise
362
+
363
+ Exceptions:
364
+ - subprocess.TimeoutExpired: Logged as warning, returns False
365
+ - FileNotFoundError: Logged as warning, returns False
366
+ - Exception: Logged as warning, returns False
367
+
368
+ Examples:
369
+ >>> _clear_uv_package_cache("moai-adk")
370
+ True # If uv cache clean succeeds
371
+ """
372
+ try:
373
+ result = subprocess.run(
374
+ ["uv", "cache", "clean", package_name],
375
+ capture_output=True,
376
+ text=True,
377
+ timeout=10, # 10 second timeout
378
+ check=False,
379
+ )
380
+
381
+ if result.returncode == 0:
382
+ logger.debug(f"UV cache cleared for {package_name}")
383
+ return True
384
+ else:
385
+ logger.warning(f"Failed to clear UV cache: {result.stderr}")
386
+ return False
387
+
388
+ except subprocess.TimeoutExpired:
389
+ logger.warning(f"UV cache clean timed out for {package_name}")
390
+ return False
391
+ except FileNotFoundError:
392
+ logger.warning("UV command not found. Is uv installed?")
393
+ return False
394
+ except Exception as e:
395
+ logger.warning(f"Unexpected error clearing cache: {e}")
396
+ return False
397
+
398
+
399
+ # @CODE:UPDATE-CACHE-003
400
+ def _execute_upgrade_with_retry(installer_cmd: list[str], package_name: str = "moai-adk") -> bool:
401
+ """
402
+ Execute upgrade with automatic cache retry on stale detection.
403
+
404
+ Implements a robust 7-stage upgrade flow that handles PyPI cache staleness:
405
+
406
+ Stage 1: First upgrade attempt (up to 60 seconds)
407
+ Stage 2: Check success condition (returncode=0 AND no "Nothing to upgrade")
408
+ Stage 3: Detect stale cache using _detect_stale_cache()
409
+ Stage 4: Show user feedback if stale cache detected
410
+ Stage 5: Clear cache using _clear_uv_package_cache()
411
+ Stage 6: Retry upgrade with same command
412
+ Stage 7: Return final result (success or failure)
413
+
414
+ Retry Logic:
415
+ - Only ONE retry is performed to prevent infinite loops
416
+ - Retry only happens if stale cache is detected AND cache clear succeeds
417
+ - Cache clear failures are reported to user with manual workaround
418
+
419
+ User Feedback:
420
+ - Shows emoji-based status messages for each stage
421
+ - Clear guidance on manual workaround if automatic retry fails
422
+ - All errors logged at WARNING level for debugging
423
+
424
+ Args:
425
+ installer_cmd: Command list from _detect_tool_installer()
426
+ e.g., ["uv", "tool", "upgrade", "moai-adk"]
427
+ package_name: Package name for cache clearing (default: "moai-adk")
428
+
429
+ Returns:
430
+ True if upgrade succeeded (either first attempt or after retry),
431
+ False otherwise
432
+
433
+ Examples:
434
+ >>> # First attempt succeeds
435
+ >>> _execute_upgrade_with_retry(["uv", "tool", "upgrade", "moai-adk"])
436
+ True
437
+
438
+ >>> # First attempt stale, retry succeeds
439
+ >>> _execute_upgrade_with_retry(["uv", "tool", "upgrade", "moai-adk"])
440
+ True # After cache clear and retry
441
+
442
+ Raises:
443
+ subprocess.TimeoutExpired: Re-raised if upgrade command times out
444
+ """
445
+ # Stage 1: First upgrade attempt
446
+ try:
447
+ result = subprocess.run(installer_cmd, capture_output=True, text=True, timeout=60, check=False)
448
+ except subprocess.TimeoutExpired:
449
+ raise # Re-raise timeout for caller to handle
450
+ except Exception:
451
+ return False
452
+
453
+ # Stage 2: Check if upgrade succeeded without stale cache
454
+ if result.returncode == 0 and "Nothing to upgrade" not in result.stdout:
455
+ return True
456
+
457
+ # Stage 3: Detect stale cache
458
+ try:
459
+ current_version = _get_current_version()
460
+ latest_version = _get_latest_version()
461
+ except RuntimeError:
462
+ # If version check fails, return original result
463
+ return result.returncode == 0
464
+
465
+ if _detect_stale_cache(result.stdout, current_version, latest_version):
466
+ # Stage 4: User feedback
467
+ console.print("[yellow]âš ī¸ Cache outdated, refreshing...[/yellow]")
468
+
469
+ # Stage 5: Clear cache
470
+ if _clear_uv_package_cache(package_name):
471
+ console.print("[cyan]â™ģī¸ Cache cleared, retrying upgrade...[/cyan]")
472
+
473
+ # Stage 6: Retry upgrade
474
+ try:
475
+ result = subprocess.run(installer_cmd, capture_output=True, text=True, timeout=60, check=False)
476
+
477
+ if result.returncode == 0:
478
+ return True
479
+ else:
480
+ console.print("[red]✗ Upgrade failed after retry[/red]")
481
+ return False
482
+ except subprocess.TimeoutExpired:
483
+ raise # Re-raise timeout
484
+ except Exception:
485
+ return False
486
+ else:
487
+ # Cache clear failed
488
+ console.print("[red]✗ Cache clear failed. Manual workaround:[/red]")
489
+ console.print(" [cyan]uv cache clean moai-adk && moai-adk update[/cyan]")
490
+ return False
491
+
492
+ # Stage 7: Cache is not stale, return original result
493
+ return result.returncode == 0
494
+
495
+
496
+ def _execute_upgrade(installer_cmd: list[str]) -> bool:
497
+ """Execute package upgrade using detected installer.
498
+
499
+ Args:
500
+ installer_cmd: Command list from _detect_tool_installer()
501
+ e.g., ["uv", "tool", "upgrade", "moai-adk"]
502
+
503
+ Returns:
504
+ True if upgrade succeeded, False otherwise
505
+
506
+ Raises:
507
+ subprocess.TimeoutExpired: If upgrade times out
508
+ """
509
+ try:
510
+ result = subprocess.run(installer_cmd, capture_output=True, text=True, timeout=60, check=False)
511
+ return result.returncode == 0
512
+ except subprocess.TimeoutExpired:
513
+ raise # Re-raise timeout for caller to handle
514
+ except Exception:
515
+ return False
516
+
517
+
518
+ def _sync_templates(project_path: Path, force: bool = False) -> bool:
519
+ """Sync templates to project with rollback mechanism.
520
+
521
+ Args:
522
+ project_path: Project path (absolute)
523
+ force: Force update without backup
524
+
525
+ Returns:
526
+ True if sync succeeded, False otherwise
527
+ """
528
+ from moai_adk.core.template.backup import TemplateBackup
529
+
530
+ backup_path = None
531
+ try:
532
+ processor = TemplateProcessor(project_path)
533
+
534
+ # Create pre-sync backup for rollback
535
+ if not force:
536
+ backup = TemplateBackup(project_path)
537
+ if backup.has_existing_files():
538
+ backup_path = backup.create_backup()
539
+ console.print(f"💾 Created backup: {backup_path.name}")
540
+
541
+ # Load existing config
542
+ existing_config = _load_existing_config(project_path)
543
+
544
+ # Build context
545
+ context = _build_template_context(project_path, existing_config, __version__)
546
+ if context:
547
+ processor.set_context(context)
548
+
549
+ # Copy templates
550
+ processor.copy_templates(backup=False, silent=True)
551
+
552
+ # Validate template substitution
553
+ validation_passed = _validate_template_substitution_with_rollback(project_path, backup_path)
554
+ if not validation_passed:
555
+ if backup_path:
556
+ console.print(f"[yellow]🔄 Rolling back to backup: {backup_path.name}[/yellow]")
557
+ backup.restore_backup(backup_path)
558
+ return False
559
+
560
+ # Preserve metadata
561
+ _preserve_project_metadata(project_path, context, existing_config, __version__)
562
+ _apply_context_to_file(processor, project_path / "CLAUDE.md")
563
+
564
+ # Set optimized=false
565
+ set_optimized_false(project_path)
566
+
567
+ return True
568
+ except Exception as e:
569
+ console.print(f"[red]✗ Template sync failed: {e}[/red]")
570
+ if backup_path:
571
+ console.print(f"[yellow]🔄 Rolling back to backup: {backup_path.name}[/yellow]")
572
+ try:
573
+ backup = TemplateBackup(project_path)
574
+ backup.restore_backup(backup_path)
575
+ console.print("[green]✅ Rollback completed[/green]")
576
+ except Exception as rollback_error:
577
+ console.print(f"[red]✗ Rollback failed: {rollback_error}[/red]")
578
+ return False
579
+
580
+
581
+ def get_latest_version() -> str | None:
582
+ """Get the latest version from PyPI.
583
+
584
+ DEPRECATED: Use _get_latest_version() for new code.
585
+ This function is kept for backward compatibility.
586
+
587
+ Returns:
588
+ Latest version string, or None if fetch fails.
589
+ """
590
+ try:
591
+ return _get_latest_version()
592
+ except RuntimeError:
593
+ # Return None if PyPI check fails (backward compatibility)
36
594
  return None
37
595
 
38
596
 
@@ -49,10 +607,7 @@ def set_optimized_false(project_path: Path) -> None:
49
607
  try:
50
608
  config_data = json.loads(config_path.read_text(encoding="utf-8"))
51
609
  config_data.setdefault("project", {})["optimized"] = False
52
- config_path.write_text(
53
- json.dumps(config_data, indent=2, ensure_ascii=False) + "\n",
54
- encoding="utf-8"
55
- )
610
+ config_path.write_text(json.dumps(config_data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
56
611
  except (json.JSONDecodeError, KeyError):
57
612
  # Ignore errors if config.json is invalid
58
613
  pass
@@ -103,22 +658,24 @@ def _build_template_context(
103
658
  version_for_config: str,
104
659
  ) -> dict[str, str]:
105
660
  """Build substitution context for template files."""
661
+ import platform
662
+
106
663
  project_section = _extract_project_section(existing_config)
107
664
 
108
665
  project_name = _coalesce(
109
666
  project_section.get("name"),
110
- existing_config.get("projectName"),
667
+ existing_config.get("projectName"), # Legacy fallback
111
668
  project_path.name,
112
669
  )
113
670
  project_mode = _coalesce(
114
671
  project_section.get("mode"),
115
- existing_config.get("mode"),
672
+ existing_config.get("mode"), # Legacy fallback
116
673
  default="personal",
117
674
  )
118
675
  project_description = _coalesce(
119
676
  project_section.get("description"),
120
- existing_config.get("projectDescription"),
121
- existing_config.get("description"),
677
+ existing_config.get("projectDescription"), # Legacy fallback
678
+ existing_config.get("description"), # Legacy fallback
122
679
  )
123
680
  project_version = _coalesce(
124
681
  project_section.get("version"),
@@ -132,6 +689,17 @@ def _build_template_context(
132
689
  default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
133
690
  )
134
691
 
692
+ # Detect OS for cross-platform Hook path configuration
693
+ hook_project_dir = (
694
+ "%CLAUDE_PROJECT_DIR%" if platform.system() == "Windows"
695
+ else "$CLAUDE_PROJECT_DIR"
696
+ )
697
+
698
+ # Extract language configuration
699
+ language_config = existing_config.get("language", {})
700
+ if not isinstance(language_config, dict):
701
+ language_config = {}
702
+
135
703
  return {
136
704
  "MOAI_VERSION": version_for_config,
137
705
  "PROJECT_NAME": project_name,
@@ -139,6 +707,12 @@ def _build_template_context(
139
707
  "PROJECT_DESCRIPTION": project_description,
140
708
  "PROJECT_VERSION": project_version,
141
709
  "CREATION_TIMESTAMP": created_at,
710
+ "HOOK_PROJECT_DIR": hook_project_dir,
711
+ "CONVERSATION_LANGUAGE": language_config.get("conversation_language", "en"),
712
+ "CONVERSATION_LANGUAGE_NAME": language_config.get("conversation_language_name", "English"),
713
+ "CODEBASE_LANGUAGE": project_section.get("language", "generic"),
714
+ "PROJECT_OWNER": project_section.get("author", "@user"),
715
+ "AUTHOR": project_section.get("author", "@user"),
142
716
  }
143
717
 
144
718
 
@@ -148,7 +722,10 @@ def _preserve_project_metadata(
148
722
  existing_config: dict[str, Any],
149
723
  version_for_config: str,
150
724
  ) -> None:
151
- """Restore project-specific metadata in the new config.json."""
725
+ """Restore project-specific metadata in the new config.json.
726
+
727
+ Also updates template_version to track which template version is synchronized.
728
+ """
152
729
  config_path = project_path / ".moai" / "config.json"
153
730
  if not config_path.exists():
154
731
  return
@@ -164,7 +741,6 @@ def _preserve_project_metadata(
164
741
  project_data["mode"] = context["PROJECT_MODE"]
165
742
  project_data["description"] = context["PROJECT_DESCRIPTION"]
166
743
  project_data["created_at"] = context["CREATION_TIMESTAMP"]
167
- project_data["moai_adk_version"] = version_for_config
168
744
 
169
745
  if "optimized" not in project_data and isinstance(existing_config, dict):
170
746
  existing_project = _extract_project_section(existing_config)
@@ -184,10 +760,11 @@ def _preserve_project_metadata(
184
760
  config_data.setdefault("moai", {})
185
761
  config_data["moai"]["version"] = version_for_config
186
762
 
187
- config_path.write_text(
188
- json.dumps(config_data, indent=2, ensure_ascii=False) + "\n",
189
- encoding="utf-8"
190
- )
763
+ # @CODE:UPDATE-VERSION-008: Update template_version to track sync status
764
+ # This allows Stage 2 to compare package vs project template versions
765
+ project_data["template_version"] = version_for_config
766
+
767
+ config_path.write_text(json.dumps(config_data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
191
768
 
192
769
 
193
770
  def _apply_context_to_file(processor: TemplateProcessor, target_path: Path) -> None:
@@ -209,36 +786,186 @@ def _apply_context_to_file(processor: TemplateProcessor, target_path: Path) -> N
209
786
  target_path.write_text(substituted, encoding="utf-8")
210
787
 
211
788
 
789
+ def _validate_template_substitution(project_path: Path) -> None:
790
+ """Validate that all template variables have been properly substituted."""
791
+ import re
792
+
793
+ # Files to check for unsubstituted variables
794
+ files_to_check = [
795
+ project_path / ".claude" / "settings.json",
796
+ project_path / "CLAUDE.md",
797
+ ]
798
+
799
+ issues_found = []
800
+
801
+ for file_path in files_to_check:
802
+ if not file_path.exists():
803
+ continue
804
+
805
+ try:
806
+ content = file_path.read_text(encoding='utf-8')
807
+ # Look for unsubstituted template variables
808
+ unsubstituted = re.findall(r'\{\{([A-Z_]+)\}\}', content)
809
+ if unsubstituted:
810
+ unique_vars = sorted(set(unsubstituted))
811
+ issues_found.append(f"{file_path.relative_to(project_path)}: {', '.join(unique_vars)}")
812
+ except Exception as e:
813
+ console.print(f"[yellow]âš ī¸ Could not validate {file_path.relative_to(project_path)}: {e}[/yellow]")
814
+
815
+ if issues_found:
816
+ console.print("[red]✗ Template substitution validation failed:[/red]")
817
+ for issue in issues_found:
818
+ console.print(f" {issue}")
819
+ console.print("[yellow]💡 Run '/alfred:0-project' to fix template variables[/yellow]")
820
+ else:
821
+ console.print("[green]✅ Template substitution validation passed[/green]")
822
+
823
+
824
+ def _validate_template_substitution_with_rollback(project_path: Path, backup_path: Path | None) -> bool:
825
+ """Validate template substitution with rollback capability.
826
+
827
+ Returns:
828
+ True if validation passed, False if failed (rollback handled by caller)
829
+ """
830
+ import re
831
+
832
+ # Files to check for unsubstituted variables
833
+ files_to_check = [
834
+ project_path / ".claude" / "settings.json",
835
+ project_path / "CLAUDE.md",
836
+ ]
837
+
838
+ issues_found = []
839
+
840
+ for file_path in files_to_check:
841
+ if not file_path.exists():
842
+ continue
843
+
844
+ try:
845
+ content = file_path.read_text(encoding='utf-8')
846
+ # Look for unsubstituted template variables
847
+ unsubstituted = re.findall(r'\{\{([A-Z_]+)\}\}', content)
848
+ if unsubstituted:
849
+ unique_vars = sorted(set(unsubstituted))
850
+ issues_found.append(f"{file_path.relative_to(project_path)}: {', '.join(unique_vars)}")
851
+ except Exception as e:
852
+ console.print(f"[yellow]âš ī¸ Could not validate {file_path.relative_to(project_path)}: {e}[/yellow]")
853
+
854
+ if issues_found:
855
+ console.print("[red]✗ Template substitution validation failed:[/red]")
856
+ for issue in issues_found:
857
+ console.print(f" {issue}")
858
+
859
+ if backup_path:
860
+ console.print("[yellow]🔄 Rolling back due to validation failure...[/yellow]")
861
+ else:
862
+ console.print("[yellow]💡 Run '/alfred:0-project' to fix template variables[/yellow]")
863
+ console.print("[red]âš ī¸ No backup available - manual fix required[/red]")
864
+
865
+ return False
866
+ else:
867
+ console.print("[green]✅ Template substitution validation passed[/green]")
868
+ return True
869
+
870
+
871
+ # @CODE:UPDATE-METADATA-003
872
+ def _show_version_info(current: str, latest: str) -> None:
873
+ """Display version information.
874
+
875
+ Args:
876
+ current: Current installed version
877
+ latest: Latest available version
878
+ """
879
+ console.print("[cyan]🔍 Checking versions...[/cyan]")
880
+ console.print(f" Current version: {current}")
881
+ console.print(f" Latest version: {latest}")
882
+
883
+
884
+ # @CODE:UPDATE-CONFIG-005
885
+ def _show_installer_not_found_help() -> None:
886
+ """Show help when installer not found."""
887
+ console.print("[red]❌ Cannot detect package installer[/red]\n")
888
+ console.print("Installation method not detected. To update manually:\n")
889
+ console.print(" â€ĸ If installed via uv tool:")
890
+ console.print(" [cyan]uv tool upgrade moai-adk[/cyan]\n")
891
+ console.print(" â€ĸ If installed via pipx:")
892
+ console.print(" [cyan]pipx upgrade moai-adk[/cyan]\n")
893
+ console.print(" â€ĸ If installed via pip:")
894
+ console.print(" [cyan]pip install --upgrade moai-adk[/cyan]\n")
895
+ console.print("Then run:")
896
+ console.print(" [cyan]moai-adk update --templates-only[/cyan]")
897
+
898
+
899
+ def _show_upgrade_failure_help(installer_cmd: list[str]) -> None:
900
+ """Show help when upgrade fails.
901
+
902
+ Args:
903
+ installer_cmd: The installer command that failed
904
+ """
905
+ console.print("[red]❌ Upgrade failed[/red]\n")
906
+ console.print("Troubleshooting:")
907
+ console.print(" 1. Check network connection")
908
+ console.print(f" 2. Clear cache: {installer_cmd[0]} cache clean")
909
+ console.print(f" 3. Try manually: {' '.join(installer_cmd)}")
910
+ console.print(" 4. Report issue: https://github.com/modu-ai/moai-adk/issues")
911
+
912
+
913
+ def _show_network_error_help() -> None:
914
+ """Show help for network errors."""
915
+ console.print("[yellow]âš ī¸ Cannot reach PyPI to check latest version[/yellow]\n")
916
+ console.print("Options:")
917
+ console.print(" 1. Check network connection")
918
+ console.print(" 2. Try again with: [cyan]moai-adk update --force[/cyan]")
919
+ console.print(" 3. Skip version check: [cyan]moai-adk update --templates-only[/cyan]")
920
+
921
+
922
+ def _show_template_sync_failure_help() -> None:
923
+ """Show help when template sync fails."""
924
+ console.print("[yellow]âš ī¸ Template sync failed[/yellow]\n")
925
+ console.print("Rollback options:")
926
+ console.print(" 1. Restore from backup: [cyan]cp -r .moai-backups/TIMESTAMP .moai/[/cyan]")
927
+ console.print(" 2. Skip backup and retry: [cyan]moai-adk update --force[/cyan]")
928
+ console.print(" 3. Report issue: https://github.com/modu-ai/moai-adk/issues")
929
+
930
+
931
+ def _show_timeout_error_help() -> None:
932
+ """Show help for timeout errors."""
933
+ console.print("[red]❌ Error: Operation timed out[/red]\n")
934
+ console.print("Try again with:")
935
+ console.print(" [cyan]moai-adk update --yes --force[/cyan]")
936
+
937
+
212
938
  @click.command()
213
- @click.option(
214
- "--path",
215
- type=click.Path(exists=True),
216
- default=".",
217
- help="Project path (default: current directory)"
218
- )
219
- @click.option(
220
- "--force",
221
- is_flag=True,
222
- help="Skip backup and force the update"
223
- )
224
- @click.option(
225
- "--check",
226
- is_flag=True,
227
- help="Only check version (do not update)"
228
- )
229
- def update(path: str, force: bool, check: bool) -> None:
230
- """Update template files to the latest version.
231
-
232
- Updates include:
233
- - .claude/ (fully replaced)
234
- - .moai/ (preserve specs and reports)
235
- - CLAUDE.md (merged)
236
- - config.json (smart merge)
939
+ @click.option("--path", type=click.Path(exists=True), default=".", help="Project path (default: current directory)")
940
+ @click.option("--force", is_flag=True, help="Skip backup and force the update")
941
+ @click.option("--check", is_flag=True, help="Only check version (do not update)")
942
+ @click.option("--templates-only", is_flag=True, help="Skip package upgrade, sync templates only")
943
+ @click.option("--yes", is_flag=True, help="Auto-confirm all prompts (CI/CD mode)")
944
+ def update(path: str, force: bool, check: bool, templates_only: bool, yes: bool) -> None:
945
+ """Update command with 3-stage workflow (v0.6.3+).
946
+
947
+ Stage 1 (Package Version Check):
948
+ - Fetches current and latest versions from PyPI
949
+ - If current < latest: detects installer (uv tool, pipx, pip) and upgrades package
950
+ - Prompts user to re-run after upgrade completes
951
+
952
+ Stage 2 (Config Version Comparison - NEW in v0.6.3):
953
+ - Compares package template_version with project config.json template_version
954
+ - If versions match: skips Stage 3 (already up-to-date)
955
+ - Performance improvement: 70-80% faster for unchanged projects (3-4s vs 12-18s)
956
+
957
+ Stage 3 (Template Sync):
958
+ - Syncs templates only if versions differ
959
+ - Updates .claude/, .moai/, CLAUDE.md, config.json
960
+ - Preserves specs and reports
961
+ - Saves new template_version to config.json
237
962
 
238
963
  Examples:
239
- python -m moai_adk update # update with backup
240
- python -m moai_adk update --force # update without backup
241
- python -m moai_adk update --check # check version only
964
+ python -m moai_adk update # auto 3-stage workflow
965
+ python -m moai_adk update --force # force template sync
966
+ python -m moai_adk update --check # check version only
967
+ python -m moai_adk update --templates-only # skip package upgrade
968
+ python -m moai_adk update --yes # CI/CD mode (auto-confirm)
242
969
  """
243
970
  try:
244
971
  project_path = Path(path).resolve()
@@ -248,106 +975,170 @@ def update(path: str, force: bool, check: bool) -> None:
248
975
  console.print("[yellow]⚠ Project not initialized[/yellow]")
249
976
  raise click.Abort()
250
977
 
251
- existing_config = _load_existing_config(project_path)
978
+ # Get versions (needed for --check and normal workflow, but not for --templates-only alone)
979
+ # Note: If --check is used, always fetch versions even if --templates-only is also present
980
+ if check or not templates_only:
981
+ try:
982
+ current = _get_current_version()
983
+ latest = _get_latest_version()
984
+ except RuntimeError as e:
985
+ console.print(f"[red]Error: {e}[/red]")
986
+ if not force:
987
+ console.print("[yellow]⚠ Cannot check for updates. Use --force to update anyway.[/yellow]")
988
+ raise click.Abort()
989
+ # With --force, proceed to Stage 2 even if version check fails
990
+ current = __version__
991
+ latest = __version__
252
992
 
253
- # Phase 1: check versions
254
- console.print("[cyan]🔍 Checking versions...[/cyan]")
255
- current_version = __version__
256
- latest_version = get_latest_version()
257
- version_for_config = current_version
258
-
259
- # Handle PyPI fetch failure
260
- if latest_version is None:
261
- console.print(f" Current version: {current_version}")
262
- console.print(" Latest version: [yellow]Unable to fetch from PyPI[/yellow]")
263
- if not force:
264
- console.print("[yellow]⚠ Cannot check for updates. Use --force to update anyway.[/yellow]")
265
- return
266
- else:
267
- console.print(f" Current version: {current_version}")
268
- console.print(f" Latest version: {latest_version}")
993
+ _show_version_info(current, latest)
269
994
 
995
+ # Step 1: Handle --check (preview mode, no changes) - takes priority
270
996
  if check:
271
- # Exit early when --check is provided
272
- if latest_version is None:
273
- console.print("[yellow]⚠ Unable to check for updates[/yellow]")
274
- elif version.parse(current_version) < version.parse(latest_version):
275
- console.print("[yellow]⚠ Update available[/yellow]")
276
- elif version.parse(current_version) > version.parse(latest_version):
277
- console.print("[green]✓ Development version (newer than PyPI)[/green]")
997
+ comparison = _compare_versions(current, latest)
998
+ if comparison < 0:
999
+ console.print(f"\n[yellow]đŸ“Ļ Update available: {current} → {latest}[/yellow]")
1000
+ console.print(" Run 'moai-adk update' to upgrade")
1001
+ elif comparison == 0:
1002
+ console.print(f"[green]✓ Already up to date ({current})[/green]")
278
1003
  else:
279
- console.print("[green]✓ Already up to date[/green]")
1004
+ console.print(f"[cyan]â„šī¸ Dev version: {current} (latest: {latest})[/cyan]")
280
1005
  return
281
1006
 
282
- # Check if update is needed (version only) - skip with --force
283
- if not force and latest_version is not None:
284
- current_ver = version.parse(current_version)
285
- latest_ver = version.parse(latest_version)
286
-
287
- # Don't update if current version is newer
288
- if current_ver > latest_ver:
289
- console.print("[green]✓ Development version (newer than PyPI)[/green]")
290
- return
291
- # If versions are equal, check if we need to proceed
292
- elif current_ver == latest_ver:
293
- # Check if optimized=false (need to update templates)
294
- config_path = project_path / ".moai" / "config.json"
295
- if config_path.exists():
296
- try:
297
- config_data = json.loads(config_path.read_text())
298
- is_optimized = config_data.get("project", {}).get("optimized", False)
299
-
300
- if is_optimized:
301
- # Already up to date and optimized - exit silently
302
- return
303
- else:
304
- # Proceed with template update (optimized=false)
305
- console.print("[yellow]⚠ Template optimization needed[/yellow]")
306
- except (json.JSONDecodeError, KeyError):
307
- # If config.json is invalid, proceed with update
308
- pass
309
- else:
310
- console.print("[green]✓ Already up to date[/green]")
1007
+ # Step 2: Handle --templates-only (skip upgrade, go straight to sync)
1008
+ if templates_only:
1009
+ console.print("[cyan]📄 Syncing templates only...[/cyan]")
1010
+ try:
1011
+ if not _sync_templates(project_path, force):
1012
+ raise TemplateSyncError("Template sync returned False")
1013
+ except TemplateSyncError:
1014
+ console.print("[red]Error: Template sync failed[/red]")
1015
+ _show_template_sync_failure_help()
1016
+ raise click.Abort()
1017
+ except Exception as e:
1018
+ console.print(f"[red]Error: Template sync failed - {e}[/red]")
1019
+ _show_template_sync_failure_help()
1020
+ raise click.Abort()
1021
+
1022
+ console.print(" [green]✅ .claude/ update complete[/green]")
1023
+ console.print(" [green]✅ .moai/ update complete (specs/reports preserved)[/green]")
1024
+ console.print(" [green]🔄 CLAUDE.md merge complete[/green]")
1025
+ console.print(" [green]🔄 config.json merge complete[/green]")
1026
+ console.print("\n[green]✓ Template sync complete![/green]")
1027
+ return
1028
+
1029
+ # Compare versions
1030
+ comparison = _compare_versions(current, latest)
1031
+
1032
+ # Stage 1: Package Upgrade (if current < latest)
1033
+ # @CODE:UPDATE-STAGE1-009
1034
+ if comparison < 0:
1035
+ console.print(f"\n[cyan]đŸ“Ļ Upgrading: {current} → {latest}[/cyan]")
1036
+
1037
+ # Confirm upgrade (unless --yes)
1038
+ if not yes:
1039
+ if not click.confirm(f"Upgrade {current} → {latest}?", default=True):
1040
+ console.print("Cancelled")
311
1041
  return
312
1042
 
313
- # Phase 2: create a backup unless --force
314
- if not force:
315
- console.print("\n[cyan]💾 Creating backup...[/cyan]")
316
- processor = TemplateProcessor(project_path)
317
- backup_path = processor.create_backup()
318
- console.print(f"[green]✓ Backup completed: {backup_path.relative_to(project_path)}/[/green]")
319
- else:
320
- console.print("\n[yellow]⚠ Skipping backup (--force)[/yellow]")
1043
+ # Detect installer
1044
+ try:
1045
+ installer_cmd = _detect_tool_installer()
1046
+ if not installer_cmd:
1047
+ raise InstallerNotFoundError("No package installer detected")
1048
+ except InstallerNotFoundError:
1049
+ _show_installer_not_found_help()
1050
+ raise click.Abort()
321
1051
 
322
- # Phase 3: update templates
323
- console.print("\n[cyan]📄 Updating templates...[/cyan]")
324
- processor = TemplateProcessor(project_path)
1052
+ # Display upgrade command
1053
+ console.print(f"Running: {' '.join(installer_cmd)}")
325
1054
 
326
- context = _build_template_context(project_path, existing_config, version_for_config)
327
- if context:
328
- processor.set_context(context)
1055
+ # Execute upgrade with timeout handling
1056
+ try:
1057
+ upgrade_result = _execute_upgrade(installer_cmd)
1058
+ if not upgrade_result:
1059
+ raise UpgradeError(f"Upgrade command failed: {' '.join(installer_cmd)}")
1060
+ except subprocess.TimeoutExpired:
1061
+ _show_timeout_error_help()
1062
+ raise click.Abort()
1063
+ except UpgradeError:
1064
+ _show_upgrade_failure_help(installer_cmd)
1065
+ raise click.Abort()
1066
+
1067
+ # Prompt re-run
1068
+ console.print("\n[green]✓ Upgrade complete![/green]")
1069
+ console.print("[cyan]đŸ“ĸ Run 'moai-adk update' again to sync templates[/cyan]")
1070
+ return
1071
+
1072
+ # Stage 2: Config Version Comparison
1073
+ # @CODE:UPDATE-STAGE2-010: Stage 2 - Compare template versions to determine if sync needed
1074
+ console.print(f"✓ Package already up to date ({current})")
329
1075
 
330
- processor.copy_templates(backup=False, silent=True) # Backup already handled
1076
+ try:
1077
+ package_config_version = _get_package_config_version()
1078
+ project_config_version = _get_project_config_version(project_path)
1079
+ except ValueError as e:
1080
+ console.print(f"[yellow]⚠ Warning: {e}[/yellow]")
1081
+ # On version detection error, proceed with template sync (safer choice)
1082
+ package_config_version = __version__
1083
+ project_config_version = "0.0.0"
1084
+
1085
+ console.print("\n[cyan]🔍 Comparing config versions...[/cyan]")
1086
+ console.print(f" Package template: {package_config_version}")
1087
+ console.print(f" Project config: {project_config_version}")
1088
+
1089
+ try:
1090
+ config_comparison = _compare_versions(package_config_version, project_config_version)
1091
+ except version.InvalidVersion as e:
1092
+ # Handle invalid version strings (e.g., unsubstituted template placeholders, corrupted configs)
1093
+ console.print(f"[yellow]⚠ Invalid version format in config: {e}[/yellow]")
1094
+ console.print("[cyan]â„šī¸ Forcing template sync to repair configuration...[/cyan]")
1095
+ # Force template sync by treating project version as outdated
1096
+ config_comparison = 1 # package_config_version > project_config_version
1097
+
1098
+ # If versions are equal, no sync needed
1099
+ if config_comparison <= 0:
1100
+ console.print(f"\n[green]✓ Project already has latest template version ({project_config_version})[/green]")
1101
+ console.print("[cyan]â„šī¸ Templates are up to date! No changes needed.[/cyan]")
1102
+ return
1103
+
1104
+ # Stage 3: Template Sync (Only if package_config_version > project_config_version)
1105
+ # @CODE:UPDATE-STAGE3-011: Stage 3 - Template sync only if versions differ
1106
+ console.print(f"\n[cyan]📄 Syncing templates ({project_config_version} → {package_config_version})...[/cyan]")
1107
+
1108
+ # Create backup unless --force
1109
+ if not force:
1110
+ console.print(" [cyan]💾 Creating backup...[/cyan]")
1111
+ try:
1112
+ processor = TemplateProcessor(project_path)
1113
+ backup_path = processor.create_backup()
1114
+ console.print(f" [green]✓ Backup: {backup_path.relative_to(project_path)}/[/green]")
1115
+ except Exception as e:
1116
+ console.print(f" [yellow]⚠ Backup failed: {e}[/yellow]")
1117
+ console.print(" [yellow]⚠ Continuing without backup...[/yellow]")
1118
+ else:
1119
+ console.print(" [yellow]⚠ Skipping backup (--force)[/yellow]")
1120
+
1121
+ # Sync templates
1122
+ try:
1123
+ if not _sync_templates(project_path, force):
1124
+ raise TemplateSyncError("Template sync returned False")
1125
+ except TemplateSyncError:
1126
+ console.print("[red]Error: Template sync failed[/red]")
1127
+ _show_template_sync_failure_help()
1128
+ raise click.Abort()
1129
+ except Exception as e:
1130
+ console.print(f"[red]Error: Template sync failed - {e}[/red]")
1131
+ _show_template_sync_failure_help()
1132
+ raise click.Abort()
331
1133
 
332
1134
  console.print(" [green]✅ .claude/ update complete[/green]")
333
1135
  console.print(" [green]✅ .moai/ update complete (specs/reports preserved)[/green]")
334
1136
  console.print(" [green]🔄 CLAUDE.md merge complete[/green]")
335
1137
  console.print(" [green]🔄 config.json merge complete[/green]")
336
-
337
- _preserve_project_metadata(project_path, context, existing_config, version_for_config)
338
- _apply_context_to_file(processor, project_path / "CLAUDE.md")
339
-
340
- # Phase 4: set optimized=false
341
- set_optimized_false(project_path)
342
1138
  console.print(" [yellow]âš™ī¸ Set optimized=false (optimization needed)[/yellow]")
343
1139
 
344
1140
  console.print("\n[green]✓ Update complete![/green]")
345
- if latest_version and version.parse(current_version) < version.parse(latest_version):
346
- console.print(
347
- "[yellow]⚠ Python package still on older version. "
348
- "Run 'pip install --upgrade moai-adk' to upgrade the CLI package.[/yellow]"
349
- )
350
- console.print("\n[cyan]â„šī¸ Next step: Run /alfred:0-project update to optimize template changes[/cyan]")
1141
+ console.print("[cyan]â„šī¸ Next step: Run /alfred:0-project update to optimize template changes[/cyan]")
351
1142
 
352
1143
  except Exception as e:
353
1144
  console.print(f"[red]✗ Update failed: {e}[/red]")