moai-adk 0.6.1__py3-none-any.whl → 0.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of moai-adk might be problematic. Click here for more details.
- moai_adk/cli/commands/update.py +567 -112
- moai_adk/core/config/__init__.py +13 -0
- moai_adk/core/config/migration.py +113 -0
- moai_adk/core/project/phase_executor.py +7 -1
- moai_adk/templates/.claude/agents/alfred/debug-helper.md +1 -1
- moai_adk/templates/.claude/agents/alfred/doc-syncer.md +1 -1
- moai_adk/templates/.claude/agents/alfred/git-manager.md +1 -1
- moai_adk/templates/.claude/agents/alfred/implementation-planner.md +1 -1
- moai_adk/templates/.claude/agents/alfred/project-manager.md +1 -1
- moai_adk/templates/.claude/agents/alfred/quality-gate.md +1 -1
- moai_adk/templates/.claude/agents/alfred/spec-builder.md +1 -1
- moai_adk/templates/.claude/agents/alfred/tag-agent.md +1 -1
- moai_adk/templates/.claude/agents/alfred/tdd-implementer.md +1 -1
- moai_adk/templates/.claude/agents/alfred/trust-checker.md +1 -1
- moai_adk/templates/.claude/commands/alfred/0-project.md +46 -29
- moai_adk/templates/.claude/commands/alfred/1-plan.md +102 -14
- moai_adk/templates/.claude/commands/alfred/2-run.md +25 -13
- moai_adk/templates/.claude/commands/alfred/3-sync.md +34 -17
- moai_adk/templates/.moai/config.json +4 -0
- moai_adk/templates/CLAUDE.md +1 -1
- {moai_adk-0.6.1.dist-info → moai_adk-0.7.0.dist-info}/METADATA +93 -15
- {moai_adk-0.6.1.dist-info → moai_adk-0.7.0.dist-info}/RECORD +25 -23
- {moai_adk-0.6.1.dist-info → moai_adk-0.7.0.dist-info}/WHEEL +0 -0
- {moai_adk-0.6.1.dist-info → moai_adk-0.7.0.dist-info}/entry_points.txt +0 -0
- {moai_adk-0.6.1.dist-info → moai_adk-0.7.0.dist-info}/licenses/LICENSE +0 -0
moai_adk/cli/commands/update.py
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
"""Update command
|
|
2
2
|
|
|
3
|
-
Update MoAI-ADK to the latest version available on PyPI
|
|
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
|
+
|
|
4
8
|
Includes:
|
|
5
|
-
-
|
|
6
|
-
-
|
|
9
|
+
- Automatic installer detection (uv tool, pipx, pip)
|
|
10
|
+
- Package upgrade with intelligent re-run prompts
|
|
11
|
+
- Template and configuration updates with performance optimization
|
|
7
12
|
- Backward compatibility validation
|
|
13
|
+
- 70-80% performance improvement for up-to-date projects
|
|
8
14
|
|
|
9
15
|
## Skill Invocation Guide (English-Only)
|
|
10
16
|
|
|
@@ -34,6 +40,7 @@ Includes:
|
|
|
34
40
|
from __future__ import annotations
|
|
35
41
|
|
|
36
42
|
import json
|
|
43
|
+
import subprocess
|
|
37
44
|
from datetime import datetime
|
|
38
45
|
from pathlib import Path
|
|
39
46
|
from typing import Any, cast
|
|
@@ -47,12 +54,158 @@ from moai_adk.core.template.processor import TemplateProcessor
|
|
|
47
54
|
|
|
48
55
|
console = Console()
|
|
49
56
|
|
|
57
|
+
# Constants for tool detection
|
|
58
|
+
TOOL_DETECTION_TIMEOUT = 5 # seconds
|
|
59
|
+
UV_TOOL_COMMAND = ["uv", "tool", "upgrade", "moai-adk"]
|
|
60
|
+
PIPX_COMMAND = ["pipx", "upgrade", "moai-adk"]
|
|
61
|
+
PIP_COMMAND = ["pip", "install", "--upgrade", "moai-adk"]
|
|
50
62
|
|
|
51
|
-
|
|
52
|
-
|
|
63
|
+
|
|
64
|
+
# @CODE:UPDATE-REFACTOR-002-004
|
|
65
|
+
# Custom exceptions for better error handling
|
|
66
|
+
class UpdateError(Exception):
|
|
67
|
+
"""Base exception for update operations."""
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class InstallerNotFoundError(UpdateError):
|
|
72
|
+
"""Raised when no package installer detected."""
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class NetworkError(UpdateError):
|
|
77
|
+
"""Raised when network operation fails."""
|
|
78
|
+
pass
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class UpgradeError(UpdateError):
|
|
82
|
+
"""Raised when package upgrade fails."""
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class TemplateSyncError(UpdateError):
|
|
87
|
+
"""Raised when template sync fails."""
|
|
88
|
+
pass
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _is_installed_via_uv_tool() -> bool:
|
|
92
|
+
"""Check if moai-adk installed via uv tool.
|
|
53
93
|
|
|
54
94
|
Returns:
|
|
55
|
-
|
|
95
|
+
True if uv tool list shows moai-adk, False otherwise
|
|
96
|
+
"""
|
|
97
|
+
try:
|
|
98
|
+
result = subprocess.run(
|
|
99
|
+
["uv", "tool", "list"],
|
|
100
|
+
capture_output=True,
|
|
101
|
+
text=True,
|
|
102
|
+
timeout=TOOL_DETECTION_TIMEOUT,
|
|
103
|
+
check=False
|
|
104
|
+
)
|
|
105
|
+
return result.returncode == 0 and "moai-adk" in result.stdout
|
|
106
|
+
except (FileNotFoundError, subprocess.TimeoutExpired, OSError):
|
|
107
|
+
return False
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _is_installed_via_pipx() -> bool:
|
|
111
|
+
"""Check if moai-adk installed via pipx.
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
True if pipx list shows moai-adk, False otherwise
|
|
115
|
+
"""
|
|
116
|
+
try:
|
|
117
|
+
result = subprocess.run(
|
|
118
|
+
["pipx", "list"],
|
|
119
|
+
capture_output=True,
|
|
120
|
+
text=True,
|
|
121
|
+
timeout=TOOL_DETECTION_TIMEOUT,
|
|
122
|
+
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"],
|
|
138
|
+
capture_output=True,
|
|
139
|
+
text=True,
|
|
140
|
+
timeout=TOOL_DETECTION_TIMEOUT,
|
|
141
|
+
check=False
|
|
142
|
+
)
|
|
143
|
+
return result.returncode == 0
|
|
144
|
+
except (FileNotFoundError, subprocess.TimeoutExpired, OSError):
|
|
145
|
+
return False
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
# @CODE:UPDATE-REFACTOR-002-001
|
|
149
|
+
def _detect_tool_installer() -> list[str] | None:
|
|
150
|
+
"""Detect which tool installed moai-adk.
|
|
151
|
+
|
|
152
|
+
Checks in priority order:
|
|
153
|
+
1. uv tool (most likely for MoAI-ADK users)
|
|
154
|
+
2. pipx
|
|
155
|
+
3. pip (fallback)
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
Command list [tool, ...args] ready for subprocess.run()
|
|
159
|
+
or None if detection fails
|
|
160
|
+
|
|
161
|
+
Examples:
|
|
162
|
+
>>> # If uv tool is detected:
|
|
163
|
+
>>> _detect_tool_installer()
|
|
164
|
+
['uv', 'tool', 'upgrade', 'moai-adk']
|
|
165
|
+
|
|
166
|
+
>>> # If pipx is detected:
|
|
167
|
+
>>> _detect_tool_installer()
|
|
168
|
+
['pipx', 'upgrade', 'moai-adk']
|
|
169
|
+
|
|
170
|
+
>>> # If only pip is available:
|
|
171
|
+
>>> _detect_tool_installer()
|
|
172
|
+
['pip', 'install', '--upgrade', 'moai-adk']
|
|
173
|
+
|
|
174
|
+
>>> # If none are detected:
|
|
175
|
+
>>> _detect_tool_installer()
|
|
176
|
+
None
|
|
177
|
+
"""
|
|
178
|
+
if _is_installed_via_uv_tool():
|
|
179
|
+
return UV_TOOL_COMMAND
|
|
180
|
+
elif _is_installed_via_pipx():
|
|
181
|
+
return PIPX_COMMAND
|
|
182
|
+
elif _is_installed_via_pip():
|
|
183
|
+
return PIP_COMMAND
|
|
184
|
+
else:
|
|
185
|
+
return None
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
# @CODE:UPDATE-REFACTOR-002-002
|
|
189
|
+
def _get_current_version() -> str:
|
|
190
|
+
"""Get currently installed moai-adk version.
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
Version string (e.g., "0.6.1")
|
|
194
|
+
|
|
195
|
+
Raises:
|
|
196
|
+
RuntimeError: If version cannot be determined
|
|
197
|
+
"""
|
|
198
|
+
return __version__
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _get_latest_version() -> str:
|
|
202
|
+
"""Fetch latest moai-adk version from PyPI.
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
Version string (e.g., "0.6.2")
|
|
206
|
+
|
|
207
|
+
Raises:
|
|
208
|
+
RuntimeError: If PyPI API unavailable or parsing fails
|
|
56
209
|
"""
|
|
57
210
|
try:
|
|
58
211
|
import urllib.error
|
|
@@ -61,10 +214,167 @@ def get_latest_version() -> str | None:
|
|
|
61
214
|
url = "https://pypi.org/pypi/moai-adk/json"
|
|
62
215
|
with urllib.request.urlopen(url, timeout=5) as response: # nosec B310 - URL is hardcoded HTTPS to PyPI API, no user input
|
|
63
216
|
data = json.loads(response.read().decode("utf-8"))
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
217
|
+
return cast(str, data["info"]["version"])
|
|
218
|
+
except (urllib.error.URLError, json.JSONDecodeError, KeyError, TimeoutError) as e:
|
|
219
|
+
raise RuntimeError(f"Failed to fetch latest version from PyPI: {e}") from e
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def _compare_versions(current: str, latest: str) -> int:
|
|
223
|
+
"""Compare semantic versions.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
current: Current version string
|
|
227
|
+
latest: Latest version string
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
-1 if current < latest (upgrade needed)
|
|
231
|
+
0 if current == latest (up to date)
|
|
232
|
+
1 if current > latest (unusual, already newer)
|
|
233
|
+
"""
|
|
234
|
+
current_v = version.parse(current)
|
|
235
|
+
latest_v = version.parse(latest)
|
|
236
|
+
|
|
237
|
+
if current_v < latest_v:
|
|
238
|
+
return -1
|
|
239
|
+
elif current_v == latest_v:
|
|
240
|
+
return 0
|
|
241
|
+
else:
|
|
242
|
+
return 1
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
# @CODE:UPDATE-REFACTOR-002-006
|
|
246
|
+
def _get_package_config_version() -> str:
|
|
247
|
+
"""Get the current package template version.
|
|
248
|
+
|
|
249
|
+
This returns the version of the currently installed moai-adk package,
|
|
250
|
+
which is the version of templates that this package provides.
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
Version string of the installed package (e.g., "0.6.1")
|
|
254
|
+
"""
|
|
255
|
+
# Package template version = current installed package version
|
|
256
|
+
# This is simple and reliable since templates are versioned with the package
|
|
257
|
+
return __version__
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
# @CODE:UPDATE-REFACTOR-002-007
|
|
261
|
+
def _get_project_config_version(project_path: Path) -> str:
|
|
262
|
+
"""Get current project config.json template version.
|
|
263
|
+
|
|
264
|
+
This reads the project's .moai/config.json to determine the current
|
|
265
|
+
template version that the project is configured with.
|
|
266
|
+
|
|
267
|
+
Args:
|
|
268
|
+
project_path: Project directory path (absolute)
|
|
269
|
+
|
|
270
|
+
Returns:
|
|
271
|
+
Version string from project's config.json (e.g., "0.6.1")
|
|
272
|
+
Returns "0.0.0" if template_version field not found (indicates no prior sync)
|
|
273
|
+
|
|
274
|
+
Raises:
|
|
275
|
+
ValueError: If config.json exists but cannot be parsed
|
|
276
|
+
"""
|
|
277
|
+
config_path = project_path / ".moai" / "config.json"
|
|
278
|
+
|
|
279
|
+
if not config_path.exists():
|
|
280
|
+
# No config yet, treat as version 0.0.0 (needs initial sync)
|
|
281
|
+
return "0.0.0"
|
|
282
|
+
|
|
283
|
+
try:
|
|
284
|
+
config_data = json.loads(config_path.read_text(encoding="utf-8"))
|
|
285
|
+
# Check for template_version in project section
|
|
286
|
+
template_version = config_data.get("project", {}).get("template_version")
|
|
287
|
+
if template_version:
|
|
288
|
+
return template_version
|
|
289
|
+
|
|
290
|
+
# Fallback to moai version if no template_version exists
|
|
291
|
+
moai_version = config_data.get("moai", {}).get("version")
|
|
292
|
+
if moai_version:
|
|
293
|
+
return moai_version
|
|
294
|
+
|
|
295
|
+
# If neither exists, this is a new/old project
|
|
296
|
+
return "0.0.0"
|
|
297
|
+
except json.JSONDecodeError as e:
|
|
298
|
+
raise ValueError(f"Failed to parse project config.json: {e}") from e
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def _execute_upgrade(installer_cmd: list[str]) -> bool:
|
|
302
|
+
"""Execute package upgrade using detected installer.
|
|
303
|
+
|
|
304
|
+
Args:
|
|
305
|
+
installer_cmd: Command list from _detect_tool_installer()
|
|
306
|
+
e.g., ["uv", "tool", "upgrade", "moai-adk"]
|
|
307
|
+
|
|
308
|
+
Returns:
|
|
309
|
+
True if upgrade succeeded, False otherwise
|
|
310
|
+
|
|
311
|
+
Raises:
|
|
312
|
+
subprocess.TimeoutExpired: If upgrade times out
|
|
313
|
+
"""
|
|
314
|
+
try:
|
|
315
|
+
result = subprocess.run(
|
|
316
|
+
installer_cmd,
|
|
317
|
+
capture_output=True,
|
|
318
|
+
text=True,
|
|
319
|
+
timeout=60,
|
|
320
|
+
check=False
|
|
321
|
+
)
|
|
322
|
+
return result.returncode == 0
|
|
323
|
+
except subprocess.TimeoutExpired:
|
|
324
|
+
raise # Re-raise timeout for caller to handle
|
|
325
|
+
except Exception:
|
|
326
|
+
return False
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def _sync_templates(project_path: Path, force: bool = False) -> bool:
|
|
330
|
+
"""Sync templates to project.
|
|
331
|
+
|
|
332
|
+
Args:
|
|
333
|
+
project_path: Project path (absolute)
|
|
334
|
+
force: Force update without backup
|
|
335
|
+
|
|
336
|
+
Returns:
|
|
337
|
+
True if sync succeeded, False otherwise
|
|
338
|
+
"""
|
|
339
|
+
try:
|
|
340
|
+
processor = TemplateProcessor(project_path)
|
|
341
|
+
|
|
342
|
+
# Load existing config
|
|
343
|
+
existing_config = _load_existing_config(project_path)
|
|
344
|
+
|
|
345
|
+
# Build context
|
|
346
|
+
context = _build_template_context(project_path, existing_config, __version__)
|
|
347
|
+
if context:
|
|
348
|
+
processor.set_context(context)
|
|
349
|
+
|
|
350
|
+
# Copy templates
|
|
351
|
+
processor.copy_templates(backup=False, silent=True)
|
|
352
|
+
|
|
353
|
+
# Preserve metadata
|
|
354
|
+
_preserve_project_metadata(project_path, context, existing_config, __version__)
|
|
355
|
+
_apply_context_to_file(processor, project_path / "CLAUDE.md")
|
|
356
|
+
|
|
357
|
+
# Set optimized=false
|
|
358
|
+
set_optimized_false(project_path)
|
|
359
|
+
|
|
360
|
+
return True
|
|
361
|
+
except Exception:
|
|
362
|
+
return False
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def get_latest_version() -> str | None:
|
|
366
|
+
"""Get the latest version from PyPI.
|
|
367
|
+
|
|
368
|
+
DEPRECATED: Use _get_latest_version() for new code.
|
|
369
|
+
This function is kept for backward compatibility.
|
|
370
|
+
|
|
371
|
+
Returns:
|
|
372
|
+
Latest version string, or None if fetch fails.
|
|
373
|
+
"""
|
|
374
|
+
try:
|
|
375
|
+
return _get_latest_version()
|
|
376
|
+
except RuntimeError:
|
|
377
|
+
# Return None if PyPI check fails (backward compatibility)
|
|
68
378
|
return None
|
|
69
379
|
|
|
70
380
|
|
|
@@ -180,7 +490,10 @@ def _preserve_project_metadata(
|
|
|
180
490
|
existing_config: dict[str, Any],
|
|
181
491
|
version_for_config: str,
|
|
182
492
|
) -> None:
|
|
183
|
-
"""Restore project-specific metadata in the new config.json.
|
|
493
|
+
"""Restore project-specific metadata in the new config.json.
|
|
494
|
+
|
|
495
|
+
Also updates template_version to track which template version is synchronized.
|
|
496
|
+
"""
|
|
184
497
|
config_path = project_path / ".moai" / "config.json"
|
|
185
498
|
if not config_path.exists():
|
|
186
499
|
return
|
|
@@ -215,6 +528,10 @@ def _preserve_project_metadata(
|
|
|
215
528
|
config_data.setdefault("moai", {})
|
|
216
529
|
config_data["moai"]["version"] = version_for_config
|
|
217
530
|
|
|
531
|
+
# @CODE:UPDATE-REFACTOR-002-008: Update template_version to track sync status
|
|
532
|
+
# This allows Stage 2 to compare package vs project template versions
|
|
533
|
+
project_data["template_version"] = version_for_config
|
|
534
|
+
|
|
218
535
|
config_path.write_text(
|
|
219
536
|
json.dumps(config_data, indent=2, ensure_ascii=False) + "\n",
|
|
220
537
|
encoding="utf-8"
|
|
@@ -240,6 +557,73 @@ def _apply_context_to_file(processor: TemplateProcessor, target_path: Path) -> N
|
|
|
240
557
|
target_path.write_text(substituted, encoding="utf-8")
|
|
241
558
|
|
|
242
559
|
|
|
560
|
+
# @CODE:UPDATE-REFACTOR-002-003
|
|
561
|
+
def _show_version_info(current: str, latest: str) -> None:
|
|
562
|
+
"""Display version information.
|
|
563
|
+
|
|
564
|
+
Args:
|
|
565
|
+
current: Current installed version
|
|
566
|
+
latest: Latest available version
|
|
567
|
+
"""
|
|
568
|
+
console.print("[cyan]🔍 Checking versions...[/cyan]")
|
|
569
|
+
console.print(f" Current version: {current}")
|
|
570
|
+
console.print(f" Latest version: {latest}")
|
|
571
|
+
|
|
572
|
+
|
|
573
|
+
# @CODE:UPDATE-REFACTOR-002-005
|
|
574
|
+
def _show_installer_not_found_help() -> None:
|
|
575
|
+
"""Show help when installer not found."""
|
|
576
|
+
console.print("[red]❌ Cannot detect package installer[/red]\n")
|
|
577
|
+
console.print("Installation method not detected. To update manually:\n")
|
|
578
|
+
console.print(" • If installed via uv tool:")
|
|
579
|
+
console.print(" [cyan]uv tool upgrade moai-adk[/cyan]\n")
|
|
580
|
+
console.print(" • If installed via pipx:")
|
|
581
|
+
console.print(" [cyan]pipx upgrade moai-adk[/cyan]\n")
|
|
582
|
+
console.print(" • If installed via pip:")
|
|
583
|
+
console.print(" [cyan]pip install --upgrade moai-adk[/cyan]\n")
|
|
584
|
+
console.print("Then run:")
|
|
585
|
+
console.print(" [cyan]moai-adk update --templates-only[/cyan]")
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
def _show_upgrade_failure_help(installer_cmd: list[str]) -> None:
|
|
589
|
+
"""Show help when upgrade fails.
|
|
590
|
+
|
|
591
|
+
Args:
|
|
592
|
+
installer_cmd: The installer command that failed
|
|
593
|
+
"""
|
|
594
|
+
console.print("[red]❌ Upgrade failed[/red]\n")
|
|
595
|
+
console.print("Troubleshooting:")
|
|
596
|
+
console.print(" 1. Check network connection")
|
|
597
|
+
console.print(f" 2. Clear cache: {installer_cmd[0]} cache clean")
|
|
598
|
+
console.print(f" 3. Try manually: {' '.join(installer_cmd)}")
|
|
599
|
+
console.print(" 4. Report issue: https://github.com/modu-ai/moai-adk/issues")
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
def _show_network_error_help() -> None:
|
|
603
|
+
"""Show help for network errors."""
|
|
604
|
+
console.print("[yellow]⚠️ Cannot reach PyPI to check latest version[/yellow]\n")
|
|
605
|
+
console.print("Options:")
|
|
606
|
+
console.print(" 1. Check network connection")
|
|
607
|
+
console.print(" 2. Try again with: [cyan]moai-adk update --force[/cyan]")
|
|
608
|
+
console.print(" 3. Skip version check: [cyan]moai-adk update --templates-only[/cyan]")
|
|
609
|
+
|
|
610
|
+
|
|
611
|
+
def _show_template_sync_failure_help() -> None:
|
|
612
|
+
"""Show help when template sync fails."""
|
|
613
|
+
console.print("[yellow]⚠️ Template sync failed[/yellow]\n")
|
|
614
|
+
console.print("Rollback options:")
|
|
615
|
+
console.print(" 1. Restore from backup: [cyan]cp -r .moai-backups/TIMESTAMP .moai/[/cyan]")
|
|
616
|
+
console.print(" 2. Skip backup and retry: [cyan]moai-adk update --force[/cyan]")
|
|
617
|
+
console.print(" 3. Report issue: https://github.com/modu-ai/moai-adk/issues")
|
|
618
|
+
|
|
619
|
+
|
|
620
|
+
def _show_timeout_error_help() -> None:
|
|
621
|
+
"""Show help for timeout errors."""
|
|
622
|
+
console.print("[red]❌ Error: Operation timed out[/red]\n")
|
|
623
|
+
console.print("Try again with:")
|
|
624
|
+
console.print(" [cyan]moai-adk update --yes --force[/cyan]")
|
|
625
|
+
|
|
626
|
+
|
|
243
627
|
@click.command()
|
|
244
628
|
@click.option(
|
|
245
629
|
"--path",
|
|
@@ -257,19 +641,41 @@ def _apply_context_to_file(processor: TemplateProcessor, target_path: Path) -> N
|
|
|
257
641
|
is_flag=True,
|
|
258
642
|
help="Only check version (do not update)"
|
|
259
643
|
)
|
|
260
|
-
|
|
261
|
-
""
|
|
644
|
+
@click.option(
|
|
645
|
+
"--templates-only",
|
|
646
|
+
is_flag=True,
|
|
647
|
+
help="Skip package upgrade, sync templates only"
|
|
648
|
+
)
|
|
649
|
+
@click.option(
|
|
650
|
+
"--yes",
|
|
651
|
+
is_flag=True,
|
|
652
|
+
help="Auto-confirm all prompts (CI/CD mode)"
|
|
653
|
+
)
|
|
654
|
+
def update(path: str, force: bool, check: bool, templates_only: bool, yes: bool) -> None:
|
|
655
|
+
"""Update command with 3-stage workflow (v0.6.3+).
|
|
656
|
+
|
|
657
|
+
Stage 1 (Package Version Check):
|
|
658
|
+
- Fetches current and latest versions from PyPI
|
|
659
|
+
- If current < latest: detects installer (uv tool, pipx, pip) and upgrades package
|
|
660
|
+
- Prompts user to re-run after upgrade completes
|
|
661
|
+
|
|
662
|
+
Stage 2 (Config Version Comparison - NEW in v0.6.3):
|
|
663
|
+
- Compares package template_version with project config.json template_version
|
|
664
|
+
- If versions match: skips Stage 3 (already up-to-date)
|
|
665
|
+
- Performance improvement: 70-80% faster for unchanged projects (3-4s vs 12-18s)
|
|
262
666
|
|
|
263
|
-
|
|
264
|
-
-
|
|
265
|
-
- .moai
|
|
266
|
-
-
|
|
267
|
-
- config.json
|
|
667
|
+
Stage 3 (Template Sync):
|
|
668
|
+
- Syncs templates only if versions differ
|
|
669
|
+
- Updates .claude/, .moai/, CLAUDE.md, config.json
|
|
670
|
+
- Preserves specs and reports
|
|
671
|
+
- Saves new template_version to config.json
|
|
268
672
|
|
|
269
673
|
Examples:
|
|
270
|
-
python -m moai_adk update
|
|
271
|
-
python -m moai_adk update --force
|
|
272
|
-
python -m moai_adk update --check
|
|
674
|
+
python -m moai_adk update # auto 3-stage workflow
|
|
675
|
+
python -m moai_adk update --force # force template sync
|
|
676
|
+
python -m moai_adk update --check # check version only
|
|
677
|
+
python -m moai_adk update --templates-only # skip package upgrade
|
|
678
|
+
python -m moai_adk update --yes # CI/CD mode (auto-confirm)
|
|
273
679
|
"""
|
|
274
680
|
try:
|
|
275
681
|
project_path = Path(path).resolve()
|
|
@@ -279,114 +685,163 @@ def update(path: str, force: bool, check: bool) -> None:
|
|
|
279
685
|
console.print("[yellow]⚠ Project not initialized[/yellow]")
|
|
280
686
|
raise click.Abort()
|
|
281
687
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
688
|
+
# Get versions (needed for --check and normal workflow, but not for --templates-only alone)
|
|
689
|
+
# Note: If --check is used, always fetch versions even if --templates-only is also present
|
|
690
|
+
if check or not templates_only:
|
|
691
|
+
try:
|
|
692
|
+
current = _get_current_version()
|
|
693
|
+
latest = _get_latest_version()
|
|
694
|
+
except RuntimeError as e:
|
|
695
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
696
|
+
if not force:
|
|
697
|
+
console.print("[yellow]⚠ Cannot check for updates. Use --force to update anyway.[/yellow]")
|
|
698
|
+
raise click.Abort()
|
|
699
|
+
# With --force, proceed to Stage 2 even if version check fails
|
|
700
|
+
current = __version__
|
|
701
|
+
latest = __version__
|
|
702
|
+
|
|
703
|
+
_show_version_info(current, latest)
|
|
704
|
+
|
|
705
|
+
# Step 1: Handle --check (preview mode, no changes) - takes priority
|
|
301
706
|
if check:
|
|
302
|
-
|
|
303
|
-
if
|
|
304
|
-
console.print("[yellow]
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
console.print("[green]✓ Development version (newer than PyPI)[/green]")
|
|
707
|
+
comparison = _compare_versions(current, latest)
|
|
708
|
+
if comparison < 0:
|
|
709
|
+
console.print(f"\n[yellow]📦 Update available: {current} → {latest}[/yellow]")
|
|
710
|
+
console.print(" Run 'moai-adk update' to upgrade")
|
|
711
|
+
elif comparison == 0:
|
|
712
|
+
console.print(f"[green]✓ Already up to date ({current})[/green]")
|
|
309
713
|
else:
|
|
310
|
-
console.print("[
|
|
714
|
+
console.print(f"[cyan]ℹ️ Dev version: {current} (latest: {latest})[/cyan]")
|
|
311
715
|
return
|
|
312
716
|
|
|
313
|
-
#
|
|
314
|
-
if
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
console.print("[
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
717
|
+
# Step 2: Handle --templates-only (skip upgrade, go straight to sync)
|
|
718
|
+
if templates_only:
|
|
719
|
+
console.print("[cyan]📄 Syncing templates only...[/cyan]")
|
|
720
|
+
try:
|
|
721
|
+
if not _sync_templates(project_path, force):
|
|
722
|
+
raise TemplateSyncError("Template sync returned False")
|
|
723
|
+
except TemplateSyncError:
|
|
724
|
+
console.print("[red]Error: Template sync failed[/red]")
|
|
725
|
+
_show_template_sync_failure_help()
|
|
726
|
+
raise click.Abort()
|
|
727
|
+
except Exception as e:
|
|
728
|
+
console.print(f"[red]Error: Template sync failed - {e}[/red]")
|
|
729
|
+
_show_template_sync_failure_help()
|
|
730
|
+
raise click.Abort()
|
|
731
|
+
|
|
732
|
+
console.print(" [green]✅ .claude/ update complete[/green]")
|
|
733
|
+
console.print(" [green]✅ .moai/ update complete (specs/reports preserved)[/green]")
|
|
734
|
+
console.print(" [green]🔄 CLAUDE.md merge complete[/green]")
|
|
735
|
+
console.print(" [green]🔄 config.json merge complete[/green]")
|
|
736
|
+
console.print("\n[green]✓ Template sync complete![/green]")
|
|
737
|
+
return
|
|
738
|
+
|
|
739
|
+
# Compare versions
|
|
740
|
+
comparison = _compare_versions(current, latest)
|
|
741
|
+
|
|
742
|
+
# Stage 1: Package Upgrade (if current < latest)
|
|
743
|
+
# @CODE:UPDATE-REFACTOR-002-009: Stage 1 - Package version check and upgrade
|
|
744
|
+
if comparison < 0:
|
|
745
|
+
console.print(f"\n[cyan]📦 Upgrading: {current} → {latest}[/cyan]")
|
|
746
|
+
|
|
747
|
+
# Confirm upgrade (unless --yes)
|
|
748
|
+
if not yes:
|
|
749
|
+
if not click.confirm(f"Upgrade {current} → {latest}?", default=True):
|
|
750
|
+
console.print("Cancelled")
|
|
342
751
|
return
|
|
343
752
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
753
|
+
# Detect installer
|
|
754
|
+
try:
|
|
755
|
+
installer_cmd = _detect_tool_installer()
|
|
756
|
+
if not installer_cmd:
|
|
757
|
+
raise InstallerNotFoundError("No package installer detected")
|
|
758
|
+
except InstallerNotFoundError:
|
|
759
|
+
_show_installer_not_found_help()
|
|
760
|
+
raise click.Abort()
|
|
761
|
+
|
|
762
|
+
# Display upgrade command
|
|
763
|
+
console.print(f"Running: {' '.join(installer_cmd)}")
|
|
764
|
+
|
|
765
|
+
# Execute upgrade with timeout handling
|
|
766
|
+
try:
|
|
767
|
+
upgrade_result = _execute_upgrade(installer_cmd)
|
|
768
|
+
if not upgrade_result:
|
|
769
|
+
raise UpgradeError(f"Upgrade command failed: {' '.join(installer_cmd)}")
|
|
770
|
+
except subprocess.TimeoutExpired:
|
|
771
|
+
_show_timeout_error_help()
|
|
772
|
+
raise click.Abort()
|
|
773
|
+
except UpgradeError:
|
|
774
|
+
_show_upgrade_failure_help(installer_cmd)
|
|
775
|
+
raise click.Abort()
|
|
776
|
+
|
|
777
|
+
# Prompt re-run
|
|
778
|
+
console.print("\n[green]✓ Upgrade complete![/green]")
|
|
779
|
+
console.print("[cyan]📢 Run 'moai-adk update' again to sync templates[/cyan]")
|
|
780
|
+
return
|
|
352
781
|
|
|
353
|
-
#
|
|
354
|
-
|
|
355
|
-
|
|
782
|
+
# Stage 2: Config Version Comparison
|
|
783
|
+
# @CODE:UPDATE-REFACTOR-002-010: Stage 2 - Compare template versions to determine if sync needed
|
|
784
|
+
console.print(f"✓ Package already up to date ({current})")
|
|
356
785
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
786
|
+
try:
|
|
787
|
+
package_config_version = _get_package_config_version()
|
|
788
|
+
project_config_version = _get_project_config_version(project_path)
|
|
789
|
+
except ValueError as e:
|
|
790
|
+
console.print(f"[yellow]⚠ Warning: {e}[/yellow]")
|
|
791
|
+
# On version detection error, proceed with template sync (safer choice)
|
|
792
|
+
package_config_version = __version__
|
|
793
|
+
project_config_version = "0.0.0"
|
|
794
|
+
|
|
795
|
+
console.print("\n[cyan]🔍 Comparing config versions...[/cyan]")
|
|
796
|
+
console.print(f" Package template: {package_config_version}")
|
|
797
|
+
console.print(f" Project config: {project_config_version}")
|
|
798
|
+
|
|
799
|
+
config_comparison = _compare_versions(package_config_version, project_config_version)
|
|
800
|
+
|
|
801
|
+
# If versions are equal, no sync needed
|
|
802
|
+
if config_comparison <= 0:
|
|
803
|
+
console.print(f"\n[green]✓ Project already has latest template version ({project_config_version})[/green]")
|
|
804
|
+
console.print("[cyan]ℹ️ Templates are up to date! No changes needed.[/cyan]")
|
|
805
|
+
return
|
|
360
806
|
|
|
361
|
-
|
|
807
|
+
# Stage 3: Template Sync (Only if package_config_version > project_config_version)
|
|
808
|
+
# @CODE:UPDATE-REFACTOR-002-011: Stage 3 - Template sync only if versions differ
|
|
809
|
+
console.print(f"\n[cyan]📄 Syncing templates ({project_config_version} → {package_config_version})...[/cyan]")
|
|
810
|
+
|
|
811
|
+
# Create backup unless --force
|
|
812
|
+
if not force:
|
|
813
|
+
console.print(" [cyan]💾 Creating backup...[/cyan]")
|
|
814
|
+
try:
|
|
815
|
+
processor = TemplateProcessor(project_path)
|
|
816
|
+
backup_path = processor.create_backup()
|
|
817
|
+
console.print(f" [green]✓ Backup: {backup_path.relative_to(project_path)}/[/green]")
|
|
818
|
+
except Exception as e:
|
|
819
|
+
console.print(f" [yellow]⚠ Backup failed: {e}[/yellow]")
|
|
820
|
+
console.print(" [yellow]⚠ Continuing without backup...[/yellow]")
|
|
821
|
+
else:
|
|
822
|
+
console.print(" [yellow]⚠ Skipping backup (--force)[/yellow]")
|
|
823
|
+
|
|
824
|
+
# Sync templates
|
|
825
|
+
try:
|
|
826
|
+
if not _sync_templates(project_path, force):
|
|
827
|
+
raise TemplateSyncError("Template sync returned False")
|
|
828
|
+
except TemplateSyncError:
|
|
829
|
+
console.print("[red]Error: Template sync failed[/red]")
|
|
830
|
+
_show_template_sync_failure_help()
|
|
831
|
+
raise click.Abort()
|
|
832
|
+
except Exception as e:
|
|
833
|
+
console.print(f"[red]Error: Template sync failed - {e}[/red]")
|
|
834
|
+
_show_template_sync_failure_help()
|
|
835
|
+
raise click.Abort()
|
|
362
836
|
|
|
363
837
|
console.print(" [green]✅ .claude/ update complete[/green]")
|
|
364
838
|
console.print(" [green]✅ .moai/ update complete (specs/reports preserved)[/green]")
|
|
365
839
|
console.print(" [green]🔄 CLAUDE.md merge complete[/green]")
|
|
366
840
|
console.print(" [green]🔄 config.json merge complete[/green]")
|
|
367
|
-
|
|
368
|
-
_preserve_project_metadata(project_path, context, existing_config, version_for_config)
|
|
369
|
-
_apply_context_to_file(processor, project_path / "CLAUDE.md")
|
|
370
|
-
|
|
371
|
-
# Phase 4: set optimized=false
|
|
372
|
-
set_optimized_false(project_path)
|
|
373
841
|
console.print(" [yellow]⚙️ Set optimized=false (optimization needed)[/yellow]")
|
|
374
842
|
|
|
375
843
|
console.print("\n[green]✓ Update complete![/green]")
|
|
376
|
-
|
|
377
|
-
console.print(
|
|
378
|
-
"[yellow]⚠ Python package still on older version.[/yellow]"
|
|
379
|
-
)
|
|
380
|
-
console.print(
|
|
381
|
-
"[cyan]Upgrade options:[/cyan]"
|
|
382
|
-
)
|
|
383
|
-
console.print(
|
|
384
|
-
" 1. uv tool (recommended): uv tool upgrade moai-adk"
|
|
385
|
-
)
|
|
386
|
-
console.print(
|
|
387
|
-
" 2. pip (legacy): pip install --upgrade moai-adk"
|
|
388
|
-
)
|
|
389
|
-
console.print("\n[cyan]ℹ️ Next step: Run /alfred:0-project update to optimize template changes[/cyan]")
|
|
844
|
+
console.print("[cyan]ℹ️ Next step: Run /alfred:0-project update to optimize template changes[/cyan]")
|
|
390
845
|
|
|
391
846
|
except Exception as e:
|
|
392
847
|
console.print(f"[red]✗ Update failed: {e}[/red]")
|