moai-adk 0.4.0__py3-none-any.whl → 0.4.4__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 (130) hide show
  1. moai_adk/__init__.py +2 -3
  2. moai_adk/cli/commands/init.py +10 -5
  3. moai_adk/cli/commands/update.py +274 -118
  4. moai_adk/cli/prompts/init_prompts.py +14 -18
  5. moai_adk/core/diagnostics/slash_commands.py +1 -1
  6. moai_adk/core/project/backup_utils.py +2 -11
  7. moai_adk/core/project/checker.py +2 -2
  8. moai_adk/core/project/phase_executor.py +11 -14
  9. moai_adk/core/project/validator.py +3 -2
  10. moai_adk/core/quality/__init__.py +1 -1
  11. moai_adk/core/quality/trust_checker.py +63 -63
  12. moai_adk/core/quality/validators/__init__.py +1 -1
  13. moai_adk/core/quality/validators/base_validator.py +1 -1
  14. moai_adk/core/template/backup.py +21 -8
  15. moai_adk/core/template/merger.py +14 -4
  16. moai_adk/core/template/processor.py +24 -5
  17. moai_adk/templates/.claude/agents/alfred/cc-manager.md +446 -424
  18. moai_adk/templates/.claude/agents/alfred/debug-helper.md +116 -103
  19. moai_adk/templates/.claude/agents/alfred/doc-syncer.md +130 -116
  20. moai_adk/templates/.claude/agents/alfred/git-manager.md +186 -174
  21. moai_adk/templates/.claude/agents/alfred/implementation-planner.md +227 -213
  22. moai_adk/templates/.claude/agents/alfred/project-manager.md +216 -128
  23. moai_adk/templates/.claude/agents/alfred/quality-gate.md +224 -209
  24. moai_adk/templates/.claude/agents/alfred/spec-builder.md +174 -160
  25. moai_adk/templates/.claude/agents/alfred/tag-agent.md +151 -139
  26. moai_adk/templates/.claude/agents/alfred/tdd-implementer.md +209 -196
  27. moai_adk/templates/.claude/agents/alfred/trust-checker.md +247 -233
  28. moai_adk/templates/.claude/commands/alfred/0-project.md +756 -640
  29. moai_adk/templates/.claude/commands/alfred/1-plan.md +343 -333
  30. moai_adk/templates/.claude/commands/alfred/2-run.md +297 -285
  31. moai_adk/templates/.claude/commands/alfred/3-sync.md +387 -356
  32. moai_adk/templates/.claude/hooks/alfred/README.md +52 -52
  33. moai_adk/templates/.claude/hooks/alfred/alfred_hooks.py +44 -48
  34. moai_adk/templates/.claude/hooks/alfred/core/__init__.py +17 -17
  35. moai_adk/templates/.claude/hooks/alfred/core/checkpoint.py +59 -59
  36. moai_adk/templates/.claude/hooks/alfred/core/context.py +19 -19
  37. moai_adk/templates/.claude/hooks/alfred/core/project.py +52 -52
  38. moai_adk/templates/.claude/hooks/alfred/handlers/__init__.py +1 -1
  39. moai_adk/templates/.claude/hooks/alfred/handlers/notification.py +4 -4
  40. moai_adk/templates/.claude/hooks/alfred/handlers/session.py +27 -27
  41. moai_adk/templates/.claude/hooks/alfred/handlers/tool.py +16 -17
  42. moai_adk/templates/.claude/hooks/alfred/handlers/user.py +11 -11
  43. moai_adk/templates/.claude/output-styles/alfred/agentic-coding.md +308 -307
  44. moai_adk/templates/.claude/output-styles/alfred/moai-adk-learning.md +297 -296
  45. moai_adk/templates/.claude/output-styles/alfred/study-with-alfred.md +191 -190
  46. moai_adk/templates/.claude/skills/moai-alfred-code-reviewer/SKILL.md +112 -0
  47. moai_adk/templates/.claude/skills/moai-alfred-debugger-pro/SKILL.md +103 -0
  48. moai_adk/templates/.claude/skills/moai-alfred-ears-authoring/SKILL.md +103 -0
  49. moai_adk/templates/.claude/skills/moai-alfred-git-workflow/SKILL.md +95 -0
  50. moai_adk/templates/.claude/skills/moai-alfred-language-detection/SKILL.md +99 -0
  51. moai_adk/templates/.claude/skills/moai-alfred-performance-optimizer/SKILL.md +105 -0
  52. moai_adk/templates/.claude/skills/moai-alfred-refactoring-coach/SKILL.md +97 -0
  53. moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-validation/SKILL.md +97 -0
  54. moai_adk/templates/.claude/skills/moai-alfred-tag-scanning/SKILL.md +90 -0
  55. moai_adk/templates/.claude/skills/moai-alfred-trust-validation/SKILL.md +99 -0
  56. moai_adk/templates/.claude/skills/moai-alfred-tui-survey/SKILL.md +87 -0
  57. moai_adk/templates/.claude/skills/moai-alfred-tui-survey/examples.md +62 -0
  58. moai_adk/templates/.claude/skills/moai-claude-code/SKILL.md +70 -43
  59. moai_adk/templates/.claude/skills/moai-claude-code/examples.md +141 -141
  60. moai_adk/templates/.claude/skills/moai-claude-code/reference.md +179 -165
  61. moai_adk/templates/.claude/skills/moai-claude-code/templates/agent-full.md +78 -78
  62. moai_adk/templates/.claude/skills/moai-claude-code/templates/command-full.md +90 -90
  63. moai_adk/templates/.claude/skills/moai-claude-code/templates/plugin-full.json +39 -25
  64. moai_adk/templates/.claude/skills/moai-claude-code/templates/settings-full.json +117 -74
  65. moai_adk/templates/.claude/skills/moai-claude-code/templates/skill-full.md +131 -134
  66. moai_adk/templates/.claude/skills/moai-domain-backend/SKILL.md +43 -12
  67. moai_adk/templates/.claude/skills/moai-domain-cli-tool/SKILL.md +43 -12
  68. moai_adk/templates/.claude/skills/moai-domain-data-science/SKILL.md +43 -12
  69. moai_adk/templates/.claude/skills/moai-domain-database/SKILL.md +43 -12
  70. moai_adk/templates/.claude/skills/moai-domain-devops/SKILL.md +43 -12
  71. moai_adk/templates/.claude/skills/moai-domain-frontend/SKILL.md +43 -12
  72. moai_adk/templates/.claude/skills/moai-domain-ml/SKILL.md +43 -11
  73. moai_adk/templates/.claude/skills/moai-domain-mobile-app/SKILL.md +43 -12
  74. moai_adk/templates/.claude/skills/moai-domain-security/SKILL.md +43 -12
  75. moai_adk/templates/.claude/skills/moai-domain-web-api/SKILL.md +43 -12
  76. moai_adk/templates/.claude/skills/moai-essentials-debug/SKILL.md +47 -11
  77. moai_adk/templates/.claude/skills/moai-essentials-perf/SKILL.md +47 -11
  78. moai_adk/templates/.claude/skills/moai-essentials-refactor/SKILL.md +51 -14
  79. moai_adk/templates/.claude/skills/moai-essentials-review/SKILL.md +46 -10
  80. moai_adk/templates/.claude/skills/moai-foundation-ears/SKILL.md +62 -25
  81. moai_adk/templates/.claude/skills/moai-foundation-git/SKILL.md +44 -17
  82. moai_adk/templates/.claude/skills/moai-foundation-langs/SKILL.md +44 -14
  83. moai_adk/templates/.claude/skills/moai-foundation-specs/SKILL.md +45 -13
  84. moai_adk/templates/.claude/skills/moai-foundation-tags/SKILL.md +46 -14
  85. moai_adk/templates/.claude/skills/moai-foundation-trust/SKILL.md +48 -8
  86. moai_adk/templates/.claude/skills/moai-lang-c/SKILL.md +44 -12
  87. moai_adk/templates/.claude/skills/moai-lang-clojure/SKILL.md +44 -12
  88. moai_adk/templates/.claude/skills/moai-lang-cpp/SKILL.md +44 -11
  89. moai_adk/templates/.claude/skills/moai-lang-csharp/SKILL.md +44 -11
  90. moai_adk/templates/.claude/skills/moai-lang-dart/SKILL.md +44 -12
  91. moai_adk/templates/.claude/skills/moai-lang-elixir/SKILL.md +44 -11
  92. moai_adk/templates/.claude/skills/moai-lang-go/SKILL.md +44 -11
  93. moai_adk/templates/.claude/skills/moai-lang-haskell/SKILL.md +44 -11
  94. moai_adk/templates/.claude/skills/moai-lang-java/SKILL.md +44 -12
  95. moai_adk/templates/.claude/skills/moai-lang-javascript/SKILL.md +44 -12
  96. moai_adk/templates/.claude/skills/moai-lang-julia/SKILL.md +44 -12
  97. moai_adk/templates/.claude/skills/moai-lang-kotlin/SKILL.md +44 -12
  98. moai_adk/templates/.claude/skills/moai-lang-lua/SKILL.md +44 -11
  99. moai_adk/templates/.claude/skills/moai-lang-php/SKILL.md +44 -11
  100. moai_adk/templates/.claude/skills/moai-lang-python/SKILL.md +44 -12
  101. moai_adk/templates/.claude/skills/moai-lang-r/SKILL.md +44 -11
  102. moai_adk/templates/.claude/skills/moai-lang-ruby/SKILL.md +44 -11
  103. moai_adk/templates/.claude/skills/moai-lang-rust/SKILL.md +44 -12
  104. moai_adk/templates/.claude/skills/moai-lang-scala/SKILL.md +44 -12
  105. moai_adk/templates/.claude/skills/moai-lang-shell/SKILL.md +44 -11
  106. moai_adk/templates/.claude/skills/moai-lang-sql/SKILL.md +44 -12
  107. moai_adk/templates/.claude/skills/moai-lang-swift/SKILL.md +44 -12
  108. moai_adk/templates/.claude/skills/moai-lang-typescript/SKILL.md +44 -12
  109. moai_adk/templates/.github/PULL_REQUEST_TEMPLATE.md +44 -43
  110. moai_adk/templates/.github/workflows/moai-gitflow.yml +36 -35
  111. moai_adk/templates/.moai/config.json +9 -6
  112. moai_adk/templates/.moai/memory/development-guide.md +220 -221
  113. moai_adk/templates/.moai/memory/gitflow-protection-policy.md +85 -85
  114. moai_adk/templates/.moai/memory/spec-metadata.md +229 -150
  115. moai_adk/templates/.moai/project/product.md +90 -90
  116. moai_adk/templates/.moai/project/structure.md +85 -85
  117. moai_adk/templates/.moai/project/tech.md +117 -117
  118. moai_adk/templates/CLAUDE.md +564 -709
  119. moai_adk-0.4.4.dist-info/METADATA +369 -0
  120. moai_adk-0.4.4.dist-info/RECORD +152 -0
  121. moai_adk/templates/.claude/commands/alfred/1-spec.md +0 -31
  122. moai_adk/templates/.claude/commands/alfred/2-build.md +0 -30
  123. moai_adk/templates/.claude/skills/scripts/standardize_skills.py +0 -166
  124. moai_adk/templates/.claude/skills/scripts/verify_standardization.sh +0 -43
  125. moai_adk/templates/.moai/hooks/pre-push.sample +0 -88
  126. moai_adk-0.4.0.dist-info/METADATA +0 -1816
  127. moai_adk-0.4.0.dist-info/RECORD +0 -145
  128. {moai_adk-0.4.0.dist-info → moai_adk-0.4.4.dist-info}/WHEEL +0 -0
  129. {moai_adk-0.4.0.dist-info → moai_adk-0.4.4.dist-info}/entry_points.txt +0 -0
  130. {moai_adk-0.4.0.dist-info → moai_adk-0.4.4.dist-info}/licenses/LICENSE +0 -0
moai_adk/__init__.py CHANGED
@@ -4,12 +4,11 @@
4
4
  SPEC-First TDD Framework with Alfred SuperAgent
5
5
  """
6
6
 
7
- from importlib.metadata import version, PackageNotFoundError
7
+ from importlib.metadata import PackageNotFoundError, version
8
8
 
9
9
  try:
10
10
  __version__ = version("moai-adk")
11
11
  except PackageNotFoundError:
12
- # Development mode fallback
13
- __version__ = "0.4.0-dev"
12
+ __version__ = "0.4.1-dev"
14
13
 
15
14
  __all__ = ["__version__"]
@@ -66,9 +66,9 @@ def create_progress_callback(progress: Progress, task_ids: Sequence[TaskID]):
66
66
  )
67
67
  @click.option(
68
68
  "--locale",
69
- type=click.Choice(["ko", "en", "ja", "zh"]),
70
- default="ko",
71
- help="Preferred language",
69
+ type=click.Choice(["ko", "en"]),
70
+ default=None,
71
+ help="Preferred language (default: en)",
72
72
  )
73
73
  @click.option(
74
74
  "--language",
@@ -95,13 +95,13 @@ def init(
95
95
  path: Project directory path (default: current directory)
96
96
  non_interactive: Skip prompts and use defaults
97
97
  mode: Project mode (personal/team)
98
- locale: Preferred language (ko/en/ja/zh)
98
+ locale: Preferred language (ko/en). When omitted, defaults to en.
99
99
  language: Programming language
100
100
  force: Force reinitialize without confirmation
101
101
  """
102
102
  try:
103
103
  # 1. Print banner
104
- print_banner()
104
+ print_banner(__version__)
105
105
 
106
106
  # 2. Check current directory mode
107
107
  is_current_dir = path == "."
@@ -114,6 +114,7 @@ def init(
114
114
  f"\n[cyan]🚀 Initializing project at {project_path}...[/cyan]\n"
115
115
  )
116
116
  project_name = project_path.name if is_current_dir else path
117
+ locale = locale or "en"
117
118
  else:
118
119
  # Interactive Mode
119
120
  print_welcome_message()
@@ -123,6 +124,7 @@ def init(
123
124
  project_name=None if is_current_dir else path,
124
125
  is_current_dir=is_current_dir,
125
126
  project_path=project_path,
127
+ initial_locale=locale,
126
128
  )
127
129
 
128
130
  # Override with prompt answers
@@ -133,6 +135,9 @@ def init(
133
135
 
134
136
  console.print("\n[cyan]🚀 Starting installation...[/cyan]\n")
135
137
 
138
+ if locale is None:
139
+ locale = answers["locale"]
140
+
136
141
  # 4. Check for reinitialization (SPEC-INIT-003 v0.3.0) - DEFAULT TO FORCE MODE
137
142
  initializer = ProjectInitializer(project_path)
138
143
 
@@ -1,13 +1,17 @@
1
- """Update command - Upgrade moai-adk package to the latest version"""
1
+ """Update command"""
2
+ from __future__ import annotations
3
+
2
4
  import json
3
- import subprocess
4
- import sys
5
+ from datetime import datetime
6
+ from pathlib import Path
7
+ from typing import Any, cast
5
8
 
6
9
  import click
7
10
  from packaging import version
8
11
  from rich.console import Console
9
12
 
10
13
  from moai_adk import __version__
14
+ from moai_adk.core.template.processor import TemplateProcessor
11
15
 
12
16
  console = Console()
13
17
 
@@ -21,7 +25,6 @@ def get_latest_version() -> str | None:
21
25
  try:
22
26
  import urllib.error
23
27
  import urllib.request
24
- from typing import cast
25
28
 
26
29
  url = "https://pypi.org/pypi/moai-adk/json"
27
30
  with urllib.request.urlopen(url, timeout=5) as response: # nosec B310 - URL is hardcoded HTTPS to PyPI API, no user input
@@ -33,166 +36,319 @@ def get_latest_version() -> str | None:
33
36
  return None
34
37
 
35
38
 
36
- def detect_install_method() -> str:
37
- """Detect how moai-adk was installed.
39
+ def set_optimized_false(project_path: Path) -> None:
40
+ """Set config.json's optimized field to false.
38
41
 
39
- Returns:
40
- 'uv-tool', 'uv-pip', or 'pip'
42
+ Args:
43
+ project_path: Project path (absolute).
41
44
  """
42
- # Check if installed via uv tool
43
- try:
44
- result = subprocess.run(
45
- ["uv", "tool", "list"],
46
- capture_output=True,
47
- text=True,
48
- timeout=5,
49
- check=False
50
- )
51
- if result.returncode == 0 and "moai-adk" in result.stdout:
52
- return "uv-tool"
53
- except (FileNotFoundError, subprocess.TimeoutExpired):
54
- pass
45
+ config_path = project_path / ".moai" / "config.json"
46
+ if not config_path.exists():
47
+ return
55
48
 
56
- # Check if uv is available (for uv pip)
57
49
  try:
58
- subprocess.run(
59
- ["uv", "--version"],
60
- capture_output=True,
61
- timeout=5,
62
- check=False
50
+ config_data = json.loads(config_path.read_text(encoding="utf-8"))
51
+ 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"
63
55
  )
64
- return "uv-pip"
65
- except (FileNotFoundError, subprocess.TimeoutExpired):
56
+ except (json.JSONDecodeError, KeyError):
57
+ # Ignore errors if config.json is invalid
66
58
  pass
67
59
 
68
- # Default to pip
69
- return "pip"
70
60
 
61
+ def _load_existing_config(project_path: Path) -> dict[str, Any]:
62
+ """Load existing config.json if available."""
63
+ config_path = project_path / ".moai" / "config.json"
64
+ if config_path.exists():
65
+ try:
66
+ return json.loads(config_path.read_text(encoding="utf-8"))
67
+ except json.JSONDecodeError:
68
+ console.print("[yellow]⚠ Existing config.json could not be parsed. Proceeding with defaults.[/yellow]")
69
+ return {}
70
+
71
+
72
+ def _is_placeholder(value: Any) -> bool:
73
+ """Check if a string value is an unsubstituted template placeholder."""
74
+ return isinstance(value, str) and value.strip().startswith("{{") and value.strip().endswith("}}")
75
+
76
+
77
+ def _coalesce(*values: Any, default: str = "") -> str:
78
+ """Return the first non-empty, non-placeholder string value."""
79
+ for value in values:
80
+ if isinstance(value, str):
81
+ if not value.strip():
82
+ continue
83
+ if _is_placeholder(value):
84
+ continue
85
+ return value
86
+ for value in values:
87
+ if value is not None and not isinstance(value, str):
88
+ return str(value)
89
+ return default
90
+
91
+
92
+ def _extract_project_section(config: dict[str, Any]) -> dict[str, Any]:
93
+ """Return the nested project section if present."""
94
+ project_section = config.get("project")
95
+ if isinstance(project_section, dict):
96
+ return project_section
97
+ return {}
98
+
99
+
100
+ def _build_template_context(
101
+ project_path: Path,
102
+ existing_config: dict[str, Any],
103
+ version_for_config: str,
104
+ ) -> dict[str, str]:
105
+ """Build substitution context for template files."""
106
+ project_section = _extract_project_section(existing_config)
107
+
108
+ project_name = _coalesce(
109
+ project_section.get("name"),
110
+ existing_config.get("projectName"),
111
+ project_path.name,
112
+ )
113
+ project_mode = _coalesce(
114
+ project_section.get("mode"),
115
+ existing_config.get("mode"),
116
+ default="personal",
117
+ )
118
+ project_description = _coalesce(
119
+ project_section.get("description"),
120
+ existing_config.get("projectDescription"),
121
+ existing_config.get("description"),
122
+ )
123
+ project_version = _coalesce(
124
+ project_section.get("version"),
125
+ existing_config.get("projectVersion"),
126
+ existing_config.get("version"),
127
+ default="0.1.0",
128
+ )
129
+ created_at = _coalesce(
130
+ project_section.get("created_at"),
131
+ existing_config.get("created_at"),
132
+ default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
133
+ )
134
+
135
+ return {
136
+ "MOAI_VERSION": version_for_config,
137
+ "PROJECT_NAME": project_name,
138
+ "PROJECT_MODE": project_mode,
139
+ "PROJECT_DESCRIPTION": project_description,
140
+ "PROJECT_VERSION": project_version,
141
+ "CREATION_TIMESTAMP": created_at,
142
+ }
71
143
 
72
- def upgrade_package(install_method: str, target_version: str) -> bool:
73
- """Upgrade moai-adk package.
74
-
75
- Args:
76
- install_method: 'uv-tool', 'uv-pip', or 'pip'
77
- target_version: Target version to upgrade to
78
144
 
79
- Returns:
80
- True if successful, False otherwise
81
- """
82
- commands = {
83
- "uv-tool": ["uv", "tool", "upgrade", "moai-adk"],
84
- "uv-pip": ["uv", "pip", "install", "--upgrade", "moai-adk"],
85
- "pip": [sys.executable, "-m", "pip", "install", "--upgrade", "moai-adk"],
86
- }
145
+ def _preserve_project_metadata(
146
+ project_path: Path,
147
+ context: dict[str, str],
148
+ existing_config: dict[str, Any],
149
+ version_for_config: str,
150
+ ) -> None:
151
+ """Restore project-specific metadata in the new config.json."""
152
+ config_path = project_path / ".moai" / "config.json"
153
+ if not config_path.exists():
154
+ return
87
155
 
88
- cmd = commands.get(install_method)
89
- if not cmd:
90
- return False
156
+ try:
157
+ config_data = json.loads(config_path.read_text(encoding="utf-8"))
158
+ except json.JSONDecodeError:
159
+ console.print("[red]✗ Failed to parse config.json after template copy[/red]")
160
+ return
161
+
162
+ project_data = config_data.setdefault("project", {})
163
+ project_data["name"] = context["PROJECT_NAME"]
164
+ project_data["mode"] = context["PROJECT_MODE"]
165
+ project_data["description"] = context["PROJECT_DESCRIPTION"]
166
+ project_data["created_at"] = context["CREATION_TIMESTAMP"]
167
+ project_data["moai_adk_version"] = version_for_config
168
+
169
+ if "optimized" not in project_data and isinstance(existing_config, dict):
170
+ existing_project = _extract_project_section(existing_config)
171
+ if isinstance(existing_project, dict) and "optimized" in existing_project:
172
+ project_data["optimized"] = bool(existing_project["optimized"])
173
+
174
+ # Preserve locale and language preferences when possible
175
+ existing_project = _extract_project_section(existing_config)
176
+ locale = _coalesce(existing_project.get("locale"), existing_config.get("locale"))
177
+ if locale:
178
+ project_data["locale"] = locale
179
+
180
+ language = _coalesce(existing_project.get("language"), existing_config.get("language"))
181
+ if language:
182
+ project_data["language"] = language
183
+
184
+ config_data.setdefault("moai", {})
185
+ config_data["moai"]["version"] = version_for_config
186
+
187
+ config_path.write_text(
188
+ json.dumps(config_data, indent=2, ensure_ascii=False) + "\n",
189
+ encoding="utf-8"
190
+ )
191
+
192
+
193
+ def _apply_context_to_file(processor: TemplateProcessor, target_path: Path) -> None:
194
+ """Apply the processor context to an existing file (post-merge pass)."""
195
+ if not processor.context or not target_path.exists():
196
+ return
91
197
 
92
198
  try:
93
- console.print(f"\n[cyan]📦 Upgrading via {install_method}...[/cyan]")
94
- console.print(f"[dim] Command: {' '.join(cmd)}[/dim]")
95
-
96
- result = subprocess.run(
97
- cmd,
98
- capture_output=True,
99
- text=True,
100
- timeout=120,
101
- check=False
102
- )
199
+ content = target_path.read_text(encoding="utf-8")
200
+ except UnicodeDecodeError:
201
+ return
103
202
 
104
- if result.returncode == 0:
105
- console.print(f"[green]✓ Upgraded to version {target_version}[/green]")
106
- return True
107
- else:
108
- console.print(f"[red]✗ Upgrade failed[/red]")
109
- if result.stderr:
110
- console.print(f"[dim]{result.stderr.strip()}[/dim]")
111
- return False
112
-
113
- except subprocess.TimeoutExpired:
114
- console.print("[red]✗ Upgrade timeout[/red]")
115
- return False
116
- except Exception as e:
117
- console.print(f"[red]✗ Upgrade error: {e}[/red]")
118
- return False
203
+ substituted, warnings = processor._substitute_variables(content) # pylint: disable=protected-access
204
+ if warnings:
205
+ console.print("[yellow]⚠ Template warnings:[/yellow]")
206
+ for warning in warnings:
207
+ console.print(f" {warning}")
208
+
209
+ target_path.write_text(substituted, encoding="utf-8")
119
210
 
120
211
 
121
212
  @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
+ )
122
224
  @click.option(
123
225
  "--check",
124
226
  is_flag=True,
125
- help="Only check version (do not upgrade)"
227
+ help="Only check version (do not update)"
126
228
  )
127
- def update(check: bool) -> None:
128
- """Upgrade moai-adk package to the latest version.
229
+ def update(path: str, force: bool, check: bool) -> None:
230
+ """Update template files to the latest version.
129
231
 
130
- This command automatically detects the installation method
131
- (uv tool, uv pip, or pip) and upgrades the package accordingly.
132
-
133
- For template updates, use 'moai-adk init .' instead.
232
+ Updates include:
233
+ - .claude/ (fully replaced)
234
+ - .moai/ (preserve specs and reports)
235
+ - CLAUDE.md (merged)
236
+ - config.json (smart merge)
134
237
 
135
238
  Examples:
136
- moai-adk update # Upgrade to latest version
137
- moai-adk update --check # Check version only
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
138
242
  """
139
243
  try:
140
- # Phase 1: Check versions
244
+ project_path = Path(path).resolve()
245
+
246
+ # Verify the project is initialized
247
+ if not (project_path / ".moai").exists():
248
+ console.print("[yellow]⚠ Project not initialized[/yellow]")
249
+ raise click.Abort()
250
+
251
+ existing_config = _load_existing_config(project_path)
252
+
253
+ # Phase 1: check versions
141
254
  console.print("[cyan]🔍 Checking versions...[/cyan]")
142
255
  current_version = __version__
143
256
  latest_version = get_latest_version()
257
+ version_for_config = current_version
144
258
 
145
259
  # Handle PyPI fetch failure
146
260
  if latest_version is None:
147
261
  console.print(f" Current version: {current_version}")
148
262
  console.print(" Latest version: [yellow]Unable to fetch from PyPI[/yellow]")
149
- console.print("[yellow]⚠ Cannot check for updates[/yellow]")
150
- return
151
-
152
- console.print(f" Current version: {current_version}")
153
- console.print(f" Latest version: {latest_version}")
154
-
155
- # Parse versions
156
- current_ver = version.parse(current_version)
157
- latest_ver = version.parse(latest_version)
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}")
158
269
 
159
- # Check mode
160
270
  if check:
161
- if current_ver < latest_ver:
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):
162
275
  console.print("[yellow]⚠ Update available[/yellow]")
163
- elif current_ver > latest_ver:
276
+ elif version.parse(current_version) > version.parse(latest_version):
164
277
  console.print("[green]✓ Development version (newer than PyPI)[/green]")
165
278
  else:
166
279
  console.print("[green]✓ Already up to date[/green]")
167
280
  return
168
281
 
169
- # Check if upgrade needed
170
- if current_ver >= latest_ver:
171
- console.print("[green]✓ Already up to date[/green]")
172
- return
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)
173
286
 
174
- # Phase 2: Detect install method
175
- install_method = detect_install_method()
176
- console.print(f"\n[cyan]🔎 Detected installation method: {install_method}[/cyan]")
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]")
311
+ return
312
+
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]")
177
321
 
178
- # Phase 3: Upgrade package
179
- success = upgrade_package(install_method, latest_version)
322
+ # Phase 3: update templates
323
+ console.print("\n[cyan]📄 Updating templates...[/cyan]")
324
+ processor = TemplateProcessor(project_path)
180
325
 
181
- if success:
182
- console.print("\n[green]✓ Update complete![/green]")
183
- console.print("\n[dim]💡 For template updates, run: moai-adk init .[/dim]")
184
- else:
185
- console.print("\n[yellow]⚠ Upgrade failed. Please try manually:[/yellow]")
186
- if install_method == "uv-tool":
187
- console.print(" uv tool upgrade moai-adk")
188
- elif install_method == "uv-pip":
189
- console.print(" uv pip install --upgrade moai-adk")
190
- else:
191
- console.print(" pip install --upgrade moai-adk")
192
- raise click.Abort()
326
+ context = _build_template_context(project_path, existing_config, version_for_config)
327
+ if context:
328
+ processor.set_context(context)
329
+
330
+ processor.copy_templates(backup=False, silent=True) # Backup already handled
331
+
332
+ console.print(" [green]✅ .claude/ update complete[/green]")
333
+ console.print(" [green]✅ .moai/ update complete (specs/reports preserved)[/green]")
334
+ console.print(" [green]🔄 CLAUDE.md merge complete[/green]")
335
+ 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
+ console.print(" [yellow]⚙️ Set optimized=false (optimization needed)[/yellow]")
343
+
344
+ 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]")
193
351
 
194
- except click.Abort:
195
- raise
196
352
  except Exception as e:
197
353
  console.print(f"[red]✗ Update failed: {e}[/red]")
198
354
  raise click.ClickException(str(e)) from e
@@ -18,7 +18,7 @@ class ProjectSetupAnswers(TypedDict):
18
18
 
19
19
  project_name: str
20
20
  mode: str # personal | team
21
- locale: str # ko | en | ja | zh
21
+ locale: str # ko | en
22
22
  language: str | None
23
23
  author: str
24
24
 
@@ -27,6 +27,7 @@ def prompt_project_setup(
27
27
  project_name: str | None = None,
28
28
  is_current_dir: bool = False,
29
29
  project_path: Path | None = None,
30
+ initial_locale: str | None = None,
30
31
  ) -> ProjectSetupAnswers:
31
32
  """Project setup prompt
32
33
 
@@ -34,6 +35,7 @@ def prompt_project_setup(
34
35
  project_name: Project name (asks when None)
35
36
  is_current_dir: Whether the current directory is being used
36
37
  project_path: Project path (used to derive the name)
38
+ initial_locale: Preferred locale provided via CLI (optional)
37
39
 
38
40
  Returns:
39
41
  Project setup answers
@@ -88,23 +90,17 @@ def prompt_project_setup(
88
90
  if result is None:
89
91
  raise KeyboardInterrupt
90
92
  answers["mode"] = result
93
+ answers["locale"] = initial_locale or "en"
94
+ if initial_locale:
95
+ console.print(
96
+ f"[cyan]🌐 Preferred Language:[/cyan] {answers['locale']} (specified via CLI option)"
97
+ )
98
+ else:
99
+ console.print(
100
+ "[cyan]🌐 Preferred Language:[/cyan] en (default, changeable in /alfred:0-project)"
101
+ )
91
102
 
92
- # 3. Locale
93
- result = questionary.select(
94
- "🌐 Preferred Language:",
95
- choices=[
96
- questionary.Choice("Korean", value="ko"),
97
- questionary.Choice("English", value="en"),
98
- questionary.Choice("Japanese", value="ja"),
99
- questionary.Choice("Chinese", value="zh"),
100
- ],
101
- default="ko",
102
- ).ask()
103
- if result is None:
104
- raise KeyboardInterrupt
105
- answers["locale"] = result
106
-
107
- # 4. Programming language (auto-detect or manual)
103
+ # 3. Programming language (auto-detect or manual)
108
104
  result = questionary.confirm(
109
105
  "🔍 Auto-detect programming language?",
110
106
  default=True,
@@ -144,7 +140,7 @@ def prompt_project_setup(
144
140
  raise KeyboardInterrupt
145
141
  answers["language"] = result
146
142
 
147
- # 5. Author information (optional)
143
+ # 4. Author information (optional)
148
144
  result = questionary.confirm(
149
145
  "👤 Add author information? (optional)",
150
146
  default=False,
@@ -11,7 +11,7 @@ Functions:
11
11
 
12
12
  from pathlib import Path
13
13
 
14
- import yaml
14
+ import yaml # type: ignore[import-untyped]
15
15
 
16
16
 
17
17
  def validate_command_file(file_path: Path) -> dict:
@@ -3,10 +3,9 @@
3
3
 
4
4
  Selective backup strategy:
5
5
  - Back up only the required files (OR condition)
6
- - Backup path: .moai-backups/{timestamp}/ (v0.3.0)
6
+ - Backup path: .moai-backups/backup/ (v0.4.2)
7
7
  """
8
8
 
9
- from datetime import datetime
10
9
  from pathlib import Path
11
10
 
12
11
  # Backup targets (OR condition - back up when any exist)
@@ -15,6 +14,7 @@ BACKUP_TARGETS = [
15
14
  ".moai/project/",
16
15
  ".moai/memory/",
17
16
  ".claude/",
17
+ ".github/",
18
18
  "CLAUDE.md",
19
19
  ]
20
20
 
@@ -58,15 +58,6 @@ def get_backup_targets(project_path: Path) -> list[str]:
58
58
  return targets
59
59
 
60
60
 
61
- def generate_backup_dir_name() -> str:
62
- """Generate a timestamp-based backup directory name (v0.3.0).
63
-
64
- Returns:
65
- Timestamp formatted as YYYYMMDD-HHMMSS.
66
- Note: callers use .moai-backups/{timestamp}/ format.
67
- """
68
- timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
69
- return timestamp
70
61
 
71
62
 
72
63
  def is_protected_path(rel_path: Path) -> bool:
@@ -298,5 +298,5 @@ def get_permission_fix_message(path: str) -> str:
298
298
  Platform-specific fix instructions.
299
299
  """
300
300
  if platform.system() == "Windows":
301
- return f"관리자 권한으로 실행하거나 '{path}' 디렉토리 속성에서 권한을 확인하세요"
302
- return f"chmod 755 {path} 실행 재시도하세요"
301
+ return f"Run with administrator privileges or verify permissions in the properties of the '{path}' directory"
302
+ return f"Run 'chmod 755 {path}' and try again"