moai-adk 0.8.0__py3-none-any.whl → 0.15.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/init.py +14 -2
- moai_adk/cli/commands/update.py +229 -60
- moai_adk/core/config/migration.py +1 -1
- moai_adk/core/issue_creator.py +313 -0
- moai_adk/core/project/detector.py +201 -12
- moai_adk/core/project/initializer.py +62 -1
- moai_adk/core/project/phase_executor.py +48 -6
- moai_adk/core/tags/__init__.py +86 -0
- moai_adk/core/tags/ci_validator.py +463 -0
- moai_adk/core/tags/cli.py +283 -0
- moai_adk/core/tags/generator.py +109 -0
- moai_adk/core/tags/inserter.py +99 -0
- moai_adk/core/tags/mapper.py +126 -0
- moai_adk/core/tags/parser.py +76 -0
- moai_adk/core/tags/pre_commit_validator.py +393 -0
- moai_adk/core/tags/reporter.py +956 -0
- moai_adk/core/tags/tags.py +149 -0
- moai_adk/core/tags/validator.py +897 -0
- moai_adk/core/template_engine.py +268 -0
- moai_adk/templates/.claude/agents/alfred/backend-expert.md +319 -0
- moai_adk/templates/.claude/agents/alfred/cc-manager.md +25 -2
- moai_adk/templates/.claude/agents/alfred/debug-helper.md +24 -12
- moai_adk/templates/.claude/agents/alfred/devops-expert.md +464 -0
- moai_adk/templates/.claude/agents/alfred/doc-syncer.md +20 -13
- moai_adk/templates/.claude/agents/alfred/frontend-expert.md +357 -0
- moai_adk/templates/.claude/agents/alfred/git-manager.md +47 -16
- moai_adk/templates/.claude/agents/alfred/implementation-planner.md +95 -15
- moai_adk/templates/.claude/agents/alfred/project-manager.md +78 -12
- moai_adk/templates/.claude/agents/alfred/quality-gate.md +28 -5
- moai_adk/templates/.claude/agents/alfred/skill-factory.md +30 -2
- moai_adk/templates/.claude/agents/alfred/spec-builder.md +133 -13
- moai_adk/templates/.claude/agents/alfred/tag-agent.md +104 -8
- moai_adk/templates/.claude/agents/alfred/tdd-implementer.md +133 -16
- moai_adk/templates/.claude/agents/alfred/trust-checker.md +27 -4
- moai_adk/templates/.claude/agents/alfred/ui-ux-expert.md +571 -0
- moai_adk/templates/.claude/commands/alfred/0-project.md +466 -125
- moai_adk/templates/.claude/commands/alfred/1-plan.md +208 -71
- moai_adk/templates/.claude/commands/alfred/2-run.md +276 -55
- moai_adk/templates/.claude/commands/alfred/3-sync.md +439 -53
- moai_adk/templates/.claude/commands/alfred/9-feedback.md +149 -0
- moai_adk/templates/.claude/hooks/alfred/core/project.py +361 -29
- moai_adk/templates/.claude/hooks/alfred/core/timeout.py +136 -0
- moai_adk/templates/.claude/hooks/alfred/core/ttl_cache.py +108 -0
- moai_adk/templates/.claude/hooks/alfred/core/version_cache.py +198 -0
- moai_adk/templates/.claude/hooks/alfred/handlers/__init__.py +14 -6
- moai_adk/templates/.claude/hooks/alfred/post_tool__log_changes.py +94 -0
- moai_adk/templates/.claude/hooks/alfred/pre_tool__auto_checkpoint.py +100 -0
- moai_adk/templates/.claude/hooks/alfred/session_end__cleanup.py +94 -0
- moai_adk/templates/.claude/hooks/alfred/session_start__show_project_info.py +94 -0
- moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/__init__.py +2 -2
- moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/checkpoint.py +3 -3
- moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/context.py +5 -5
- moai_adk/templates/.claude/hooks/alfred/shared/core/project.py +749 -0
- moai_adk/templates/.claude/hooks/alfred/{core → shared/core}/tags.py +55 -23
- moai_adk/templates/.claude/hooks/alfred/shared/core/version_cache.py +198 -0
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/__init__.py +21 -0
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/notification.py +154 -0
- moai_adk/templates/.claude/hooks/alfred/{handlers → shared/handlers}/session.py +28 -15
- moai_adk/templates/.claude/hooks/alfred/{handlers → shared/handlers}/tool.py +3 -6
- moai_adk/templates/.claude/hooks/alfred/{handlers → shared/handlers}/user.py +19 -0
- moai_adk/templates/.claude/hooks/alfred/user_prompt__jit_load_docs.py +112 -0
- moai_adk/templates/.claude/hooks/alfred/utils/__init__.py +1 -0
- moai_adk/templates/.claude/hooks/alfred/utils/timeout.py +161 -0
- moai_adk/templates/.claude/settings.json +5 -5
- moai_adk/templates/.claude/skills/moai-alfred-agent-guide/SKILL.md +70 -0
- moai_adk/templates/.claude/skills/moai-alfred-agent-guide/examples.md +62 -0
- moai_adk/templates/{.moai/memory/CLAUDE-AGENTS-GUIDE.md → .claude/skills/moai-alfred-agent-guide/reference.md} +34 -0
- moai_adk/templates/.claude/skills/moai-alfred-config-schema/SKILL.md +56 -0
- moai_adk/templates/.claude/skills/moai-alfred-config-schema/examples.md +28 -0
- moai_adk/templates/.claude/skills/moai-alfred-context-budget/SKILL.md +62 -0
- moai_adk/templates/.claude/skills/moai-alfred-context-budget/examples.md +28 -0
- moai_adk/templates/.claude/skills/moai-alfred-context-budget/reference.md +405 -0
- moai_adk/templates/.claude/skills/moai-alfred-dev-guide/SKILL.md +51 -0
- moai_adk/templates/.claude/skills/moai-alfred-dev-guide/examples.md +355 -0
- moai_adk/templates/.claude/skills/moai-alfred-dev-guide/reference.md +239 -0
- moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/SKILL.md +323 -0
- moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/examples.md +286 -0
- moai_adk/templates/.claude/skills/moai-alfred-expertise-detection/reference.md +126 -0
- moai_adk/templates/.claude/skills/moai-alfred-gitflow-policy/SKILL.md +74 -0
- moai_adk/templates/.claude/skills/moai-alfred-gitflow-policy/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-gitflow-policy/reference.md +269 -0
- moai_adk/templates/.claude/skills/moai-alfred-issue-labels/SKILL.md +19 -0
- moai_adk/templates/.claude/skills/moai-alfred-issue-labels/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-issue-labels/reference.md +150 -0
- moai_adk/templates/.claude/skills/moai-alfred-persona-roles/SKILL.md +198 -0
- moai_adk/templates/.claude/skills/moai-alfred-persona-roles/examples.md +431 -0
- moai_adk/templates/.claude/skills/moai-alfred-persona-roles/reference.md +141 -0
- moai_adk/templates/.claude/skills/moai-alfred-practices/SKILL.md +89 -0
- moai_adk/templates/.claude/skills/moai-alfred-practices/examples.md +122 -0
- moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/SKILL.md +508 -0
- moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/examples.md +481 -0
- moai_adk/templates/.claude/skills/moai-alfred-proactive-suggestions/reference.md +100 -0
- moai_adk/templates/.claude/skills/moai-alfred-reporting/SKILL.md +273 -0
- moai_adk/templates/.claude/skills/moai-alfred-rules/SKILL.md +77 -0
- moai_adk/templates/.claude/skills/moai-alfred-rules/examples.md +265 -0
- moai_adk/templates/.claude/skills/moai-alfred-session-state/SKILL.md +19 -0
- moai_adk/templates/.claude/skills/moai-alfred-session-state/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-session-state/reference.md +84 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/README.md +137 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/SKILL.md +219 -0
- moai_adk/templates/.claude/skills/{moai-spec-authoring → moai-alfred-spec-authoring}/examples/validate-spec.sh +3 -3
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/examples.md +541 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-authoring/reference.md +622 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-extended/SKILL.md +115 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-extended/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-extended/reference.md +348 -0
- moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/SKILL.md +19 -0
- moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/examples.md +4 -0
- moai_adk/templates/.claude/skills/moai-alfred-todowrite-pattern/reference.md +211 -0
- moai_adk/templates/.claude/skills/moai-alfred-workflow/SKILL.md +288 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-descriptions/SKILL.md +19 -0
- moai_adk/templates/.claude/skills/moai-cc-skill-descriptions/examples.md +4 -0
- moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/SKILL.md +3 -3
- moai_adk/templates/.claude/skills/moai-design-systems/SKILL.md +802 -0
- moai_adk/templates/.claude/skills/moai-design-systems/examples.md +1238 -0
- moai_adk/templates/.claude/skills/moai-design-systems/reference.md +673 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/SKILL.md +17 -13
- moai_adk/templates/.claude/skills/moai-foundation-ears/SKILL.md +9 -6
- moai_adk/templates/.claude/skills/moai-lang-go/SKILL.md +15 -12
- moai_adk/templates/.claude/skills/moai-lang-java/SKILL.md +14 -12
- moai_adk/templates/.claude/skills/moai-lang-php/SKILL.md +14 -11
- moai_adk/templates/.claude/skills/moai-lang-python/SKILL.md +10 -8
- moai_adk/templates/.claude/skills/moai-lang-rust/SKILL.md +15 -12
- moai_adk/templates/.claude/skills/moai-lang-scala/SKILL.md +13 -11
- moai_adk/templates/.claude/skills/moai-lang-typescript/SKILL.md +16 -10
- moai_adk/templates/.claude/skills/moai-project-documentation.md +622 -0
- moai_adk/templates/.git-hooks/pre-push +143 -0
- moai_adk/templates/.github/workflows/c-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/cpp-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/csharp-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/dart-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/go-tag-validation.yml +130 -0
- moai_adk/templates/.github/workflows/java-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/javascript-tag-validation.yml +135 -0
- moai_adk/templates/.github/workflows/kotlin-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/moai-gitflow.yml +166 -3
- moai_adk/templates/.github/workflows/moai-release-create.yml +100 -0
- moai_adk/templates/.github/workflows/moai-release-pipeline.yml +188 -0
- moai_adk/templates/.github/workflows/php-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/python-tag-validation.yml +118 -0
- moai_adk/templates/.github/workflows/release.yml +118 -0
- moai_adk/templates/.github/workflows/ruby-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/rust-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/shell-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/spec-issue-sync.yml +206 -35
- moai_adk/templates/.github/workflows/swift-tag-validation.yml +11 -0
- moai_adk/templates/.github/workflows/tag-report.yml +269 -0
- moai_adk/templates/.github/workflows/tag-validation.yml +186 -0
- moai_adk/templates/.github/workflows/typescript-tag-validation.yml +154 -0
- moai_adk/templates/.moai/config.json +21 -2
- moai_adk/templates/CLAUDE.md +972 -78
- moai_adk/templates/workflows/go-tag-validation.yml +30 -0
- moai_adk/templates/workflows/javascript-tag-validation.yml +41 -0
- moai_adk/templates/workflows/python-tag-validation.yml +42 -0
- moai_adk/templates/workflows/typescript-tag-validation.yml +31 -0
- moai_adk/utils/banner.py +5 -5
- {moai_adk-0.8.0.dist-info → moai_adk-0.15.0.dist-info}/METADATA +1518 -161
- {moai_adk-0.8.0.dist-info → moai_adk-0.15.0.dist-info}/RECORD +183 -100
- moai_adk/templates/.claude/hooks/alfred/HOOK_SCHEMA_VALIDATION.md +0 -313
- moai_adk/templates/.claude/hooks/alfred/README.md +0 -230
- moai_adk/templates/.claude/hooks/alfred/alfred_hooks.py +0 -174
- moai_adk/templates/.claude/hooks/alfred/handlers/notification.py +0 -25
- moai_adk/templates/.claude/hooks/alfred/test_hook_output.py +0 -175
- moai_adk/templates/.claude/output-styles/alfred/agentic-coding.md +0 -640
- moai_adk/templates/.claude/output-styles/alfred/moai-adk-learning.md +0 -696
- moai_adk/templates/.claude/output-styles/alfred/study-with-alfred.md +0 -474
- moai_adk/templates/.claude/skills/moai-spec-authoring/README.md +0 -137
- moai_adk/templates/.claude/skills/moai-spec-authoring/SKILL.md +0 -218
- moai_adk/templates/.claude/skills/moai-spec-authoring/examples.md +0 -541
- moai_adk/templates/.claude/skills/moai-spec-authoring/reference.md +0 -622
- moai_adk/templates/.github/ISSUE_TEMPLATE/spec.yml +0 -176
- moai_adk/templates/.github/PULL_REQUEST_TEMPLATE.md +0 -69
- moai_adk/templates/.moai/memory/DEVELOPMENT-GUIDE.md +0 -344
- moai_adk/templates/.moai/memory/GITFLOW-PROTECTION-POLICY.md +0 -220
- moai_adk/templates/.moai/memory/SPEC-METADATA.md +0 -356
- moai_adk/templates/.moai/memory/config-schema.md +0 -444
- moai_adk/templates/.moai/memory/gitflow-protection-policy.md +0 -220
- moai_adk/templates/.moai/memory/spec-metadata.md +0 -356
- moai_adk/templates/.moai/project/product.md +0 -161
- moai_adk/templates/.moai/project/structure.md +0 -156
- moai_adk/templates/.moai/project/tech.md +0 -227
- moai_adk/templates/__init__.py +0 -2
- /moai_adk/templates/{.moai/memory/CONFIG-SCHEMA.md → .claude/skills/moai-alfred-config-schema/reference.md} +0 -0
- /moai_adk/templates/{.moai/memory/CLAUDE-PRACTICES.md → .claude/skills/moai-alfred-practices/reference.md} +0 -0
- /moai_adk/templates/{.moai/memory/CLAUDE-RULES.md → .claude/skills/moai-alfred-rules/reference.md} +0 -0
- /moai_adk/templates/{.moai/memory/SKILLS-DESCRIPTION-POLICY.md → .claude/skills/moai-cc-skill-descriptions/reference.md} +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/CHECKLIST.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/EXAMPLES.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/INTERACTIVE-DISCOVERY.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/METADATA.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/PARALLEL-ANALYSIS-REPORT.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/PYTHON-VERSION-MATRIX.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/SKILL-FACTORY-WORKFLOW.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/SKILL-UPDATE-ADVISOR.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/STEP-BY-STEP-GUIDE.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/STRUCTURE.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/WEB-RESEARCH.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/reference.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/scripts/generate-structure.sh +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/scripts/validate-skill.sh +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/templates/SKILL_TEMPLATE.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/templates/examples-template.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/templates/reference-template.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-skill-factory → moai-cc-skill-factory}/templates/scripts-template.sh +0 -0
- {moai_adk-0.8.0.dist-info → moai_adk-0.15.0.dist-info}/WHEEL +0 -0
- {moai_adk-0.8.0.dist-info → moai_adk-0.15.0.dist-info}/entry_points.txt +0 -0
- {moai_adk-0.8.0.dist-info → moai_adk-0.15.0.dist-info}/licenses/LICENSE +0 -0
moai_adk/cli/commands/init.py
CHANGED
|
@@ -167,7 +167,7 @@ def init(
|
|
|
167
167
|
else:
|
|
168
168
|
# Interactive mode: Simple notification
|
|
169
169
|
console.print("\n[cyan]🔄 Reinitializing project...[/cyan]")
|
|
170
|
-
console.print(" Backup will be created at .moai-backups/
|
|
170
|
+
console.print(" Backup will be created at .moai-backups/backup/\n")
|
|
171
171
|
|
|
172
172
|
# 5. Initialize project (Progress Bar with 5 phases)
|
|
173
173
|
# Always allow reinit (force mode by default)
|
|
@@ -175,6 +175,18 @@ def init(
|
|
|
175
175
|
|
|
176
176
|
# Reinit mode: set config.json optimized to false (v0.3.1+)
|
|
177
177
|
if is_reinit:
|
|
178
|
+
# Migration: Remove old hook files (Issue #163)
|
|
179
|
+
old_hook_files = [
|
|
180
|
+
".claude/hooks/alfred/session_start__startup.py", # v0.8.0 deprecated
|
|
181
|
+
]
|
|
182
|
+
for old_file in old_hook_files:
|
|
183
|
+
old_path = project_path / old_file
|
|
184
|
+
if old_path.exists():
|
|
185
|
+
try:
|
|
186
|
+
old_path.unlink() # Remove old file
|
|
187
|
+
except Exception:
|
|
188
|
+
pass # Ignore removal failures
|
|
189
|
+
|
|
178
190
|
config_path = project_path / ".moai" / "config.json"
|
|
179
191
|
if config_path.exists():
|
|
180
192
|
try:
|
|
@@ -255,7 +267,7 @@ def init(
|
|
|
255
267
|
if is_reinit:
|
|
256
268
|
console.print("\n[yellow]⚠️ Configuration Notice:[/yellow]")
|
|
257
269
|
console.print(" All template files have been [bold]force overwritten[/bold]")
|
|
258
|
-
console.print(" Previous files are backed up in [cyan].moai-backups/
|
|
270
|
+
console.print(" Previous files are backed up in [cyan].moai-backups/backup/[/cyan]")
|
|
259
271
|
console.print("\n [cyan]To merge your previous config:[/cyan]")
|
|
260
272
|
console.print(" Run [bold]/alfred:0-project[/bold] command in Claude Code")
|
|
261
273
|
console.print(" It will merge backup config when [dim]optimized=false[/dim]\n")
|
moai_adk/cli/commands/update.py
CHANGED
|
@@ -37,9 +37,11 @@ Includes:
|
|
|
37
37
|
- Use backup to restore previous state
|
|
38
38
|
- Debug with `python -m moai_adk doctor --verbose`
|
|
39
39
|
"""
|
|
40
|
+
|
|
40
41
|
from __future__ import annotations
|
|
41
42
|
|
|
42
43
|
import json
|
|
44
|
+
import logging
|
|
43
45
|
import subprocess
|
|
44
46
|
from datetime import datetime
|
|
45
47
|
from pathlib import Path
|
|
@@ -53,6 +55,7 @@ from moai_adk import __version__
|
|
|
53
55
|
from moai_adk.core.template.processor import TemplateProcessor
|
|
54
56
|
|
|
55
57
|
console = Console()
|
|
58
|
+
logger = logging.getLogger(__name__)
|
|
56
59
|
|
|
57
60
|
# Constants for tool detection
|
|
58
61
|
TOOL_DETECTION_TIMEOUT = 5 # seconds
|
|
@@ -65,26 +68,31 @@ PIP_COMMAND = ["pip", "install", "--upgrade", "moai-adk"]
|
|
|
65
68
|
# Custom exceptions for better error handling
|
|
66
69
|
class UpdateError(Exception):
|
|
67
70
|
"""Base exception for update operations."""
|
|
71
|
+
|
|
68
72
|
pass
|
|
69
73
|
|
|
70
74
|
|
|
71
75
|
class InstallerNotFoundError(UpdateError):
|
|
72
76
|
"""Raised when no package installer detected."""
|
|
77
|
+
|
|
73
78
|
pass
|
|
74
79
|
|
|
75
80
|
|
|
76
81
|
class NetworkError(UpdateError):
|
|
77
82
|
"""Raised when network operation fails."""
|
|
83
|
+
|
|
78
84
|
pass
|
|
79
85
|
|
|
80
86
|
|
|
81
87
|
class UpgradeError(UpdateError):
|
|
82
88
|
"""Raised when package upgrade fails."""
|
|
89
|
+
|
|
83
90
|
pass
|
|
84
91
|
|
|
85
92
|
|
|
86
93
|
class TemplateSyncError(UpdateError):
|
|
87
94
|
"""Raised when template sync fails."""
|
|
95
|
+
|
|
88
96
|
pass
|
|
89
97
|
|
|
90
98
|
|
|
@@ -96,11 +104,7 @@ def _is_installed_via_uv_tool() -> bool:
|
|
|
96
104
|
"""
|
|
97
105
|
try:
|
|
98
106
|
result = subprocess.run(
|
|
99
|
-
["uv", "tool", "list"],
|
|
100
|
-
capture_output=True,
|
|
101
|
-
text=True,
|
|
102
|
-
timeout=TOOL_DETECTION_TIMEOUT,
|
|
103
|
-
check=False
|
|
107
|
+
["uv", "tool", "list"], capture_output=True, text=True, timeout=TOOL_DETECTION_TIMEOUT, check=False
|
|
104
108
|
)
|
|
105
109
|
return result.returncode == 0 and "moai-adk" in result.stdout
|
|
106
110
|
except (FileNotFoundError, subprocess.TimeoutExpired, OSError):
|
|
@@ -115,11 +119,7 @@ def _is_installed_via_pipx() -> bool:
|
|
|
115
119
|
"""
|
|
116
120
|
try:
|
|
117
121
|
result = subprocess.run(
|
|
118
|
-
["pipx", "list"],
|
|
119
|
-
capture_output=True,
|
|
120
|
-
text=True,
|
|
121
|
-
timeout=TOOL_DETECTION_TIMEOUT,
|
|
122
|
-
check=False
|
|
122
|
+
["pipx", "list"], capture_output=True, text=True, timeout=TOOL_DETECTION_TIMEOUT, check=False
|
|
123
123
|
)
|
|
124
124
|
return result.returncode == 0 and "moai-adk" in result.stdout
|
|
125
125
|
except (FileNotFoundError, subprocess.TimeoutExpired, OSError):
|
|
@@ -134,11 +134,7 @@ def _is_installed_via_pip() -> bool:
|
|
|
134
134
|
"""
|
|
135
135
|
try:
|
|
136
136
|
result = subprocess.run(
|
|
137
|
-
["pip", "show", "moai-adk"],
|
|
138
|
-
capture_output=True,
|
|
139
|
-
text=True,
|
|
140
|
-
timeout=TOOL_DETECTION_TIMEOUT,
|
|
141
|
-
check=False
|
|
137
|
+
["pip", "show", "moai-adk"], capture_output=True, text=True, timeout=TOOL_DETECTION_TIMEOUT, check=False
|
|
142
138
|
)
|
|
143
139
|
return result.returncode == 0
|
|
144
140
|
except (FileNotFoundError, subprocess.TimeoutExpired, OSError):
|
|
@@ -274,6 +270,11 @@ def _get_project_config_version(project_path: Path) -> str:
|
|
|
274
270
|
Raises:
|
|
275
271
|
ValueError: If config.json exists but cannot be parsed
|
|
276
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
|
+
|
|
277
278
|
config_path = project_path / ".moai" / "config.json"
|
|
278
279
|
|
|
279
280
|
if not config_path.exists():
|
|
@@ -284,20 +285,214 @@ def _get_project_config_version(project_path: Path) -> str:
|
|
|
284
285
|
config_data = json.loads(config_path.read_text(encoding="utf-8"))
|
|
285
286
|
# Check for template_version in project section
|
|
286
287
|
template_version = config_data.get("project", {}).get("template_version")
|
|
287
|
-
if template_version:
|
|
288
|
+
if template_version and not _is_placeholder(template_version):
|
|
288
289
|
return template_version
|
|
289
290
|
|
|
290
291
|
# Fallback to moai version if no template_version exists
|
|
291
292
|
moai_version = config_data.get("moai", {}).get("version")
|
|
292
|
-
if moai_version:
|
|
293
|
+
if moai_version and not _is_placeholder(moai_version):
|
|
293
294
|
return moai_version
|
|
294
295
|
|
|
295
|
-
# If
|
|
296
|
+
# If values are placeholders or don't exist, treat as uninitialized (0.0.0 triggers sync)
|
|
296
297
|
return "0.0.0"
|
|
297
298
|
except json.JSONDecodeError as e:
|
|
298
299
|
raise ValueError(f"Failed to parse project config.json: {e}") from e
|
|
299
300
|
|
|
300
301
|
|
|
302
|
+
# @CODE:UPDATE-CACHE-FIX-001-001-DETECT-STALE
|
|
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-FIX-001-002-CLEAR-SUCCESS
|
|
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-FIX-001-003-RETRY-LOGIC
|
|
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
|
+
|
|
301
496
|
def _execute_upgrade(installer_cmd: list[str]) -> bool:
|
|
302
497
|
"""Execute package upgrade using detected installer.
|
|
303
498
|
|
|
@@ -312,13 +507,7 @@ def _execute_upgrade(installer_cmd: list[str]) -> bool:
|
|
|
312
507
|
subprocess.TimeoutExpired: If upgrade times out
|
|
313
508
|
"""
|
|
314
509
|
try:
|
|
315
|
-
result = subprocess.run(
|
|
316
|
-
installer_cmd,
|
|
317
|
-
capture_output=True,
|
|
318
|
-
text=True,
|
|
319
|
-
timeout=60,
|
|
320
|
-
check=False
|
|
321
|
-
)
|
|
510
|
+
result = subprocess.run(installer_cmd, capture_output=True, text=True, timeout=60, check=False)
|
|
322
511
|
return result.returncode == 0
|
|
323
512
|
except subprocess.TimeoutExpired:
|
|
324
513
|
raise # Re-raise timeout for caller to handle
|
|
@@ -391,10 +580,7 @@ def set_optimized_false(project_path: Path) -> None:
|
|
|
391
580
|
try:
|
|
392
581
|
config_data = json.loads(config_path.read_text(encoding="utf-8"))
|
|
393
582
|
config_data.setdefault("project", {})["optimized"] = False
|
|
394
|
-
config_path.write_text(
|
|
395
|
-
json.dumps(config_data, indent=2, ensure_ascii=False) + "\n",
|
|
396
|
-
encoding="utf-8"
|
|
397
|
-
)
|
|
583
|
+
config_path.write_text(json.dumps(config_data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
|
|
398
584
|
except (json.JSONDecodeError, KeyError):
|
|
399
585
|
# Ignore errors if config.json is invalid
|
|
400
586
|
pass
|
|
@@ -532,10 +718,7 @@ def _preserve_project_metadata(
|
|
|
532
718
|
# This allows Stage 2 to compare package vs project template versions
|
|
533
719
|
project_data["template_version"] = version_for_config
|
|
534
720
|
|
|
535
|
-
config_path.write_text(
|
|
536
|
-
json.dumps(config_data, indent=2, ensure_ascii=False) + "\n",
|
|
537
|
-
encoding="utf-8"
|
|
538
|
-
)
|
|
721
|
+
config_path.write_text(json.dumps(config_data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
|
|
539
722
|
|
|
540
723
|
|
|
541
724
|
def _apply_context_to_file(processor: TemplateProcessor, target_path: Path) -> None:
|
|
@@ -625,32 +808,11 @@ def _show_timeout_error_help() -> None:
|
|
|
625
808
|
|
|
626
809
|
|
|
627
810
|
@click.command()
|
|
628
|
-
@click.option(
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
)
|
|
634
|
-
@click.option(
|
|
635
|
-
"--force",
|
|
636
|
-
is_flag=True,
|
|
637
|
-
help="Skip backup and force the update"
|
|
638
|
-
)
|
|
639
|
-
@click.option(
|
|
640
|
-
"--check",
|
|
641
|
-
is_flag=True,
|
|
642
|
-
help="Only check version (do not update)"
|
|
643
|
-
)
|
|
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
|
-
)
|
|
811
|
+
@click.option("--path", type=click.Path(exists=True), default=".", help="Project path (default: current directory)")
|
|
812
|
+
@click.option("--force", is_flag=True, help="Skip backup and force the update")
|
|
813
|
+
@click.option("--check", is_flag=True, help="Only check version (do not update)")
|
|
814
|
+
@click.option("--templates-only", is_flag=True, help="Skip package upgrade, sync templates only")
|
|
815
|
+
@click.option("--yes", is_flag=True, help="Auto-confirm all prompts (CI/CD mode)")
|
|
654
816
|
def update(path: str, force: bool, check: bool, templates_only: bool, yes: bool) -> None:
|
|
655
817
|
"""Update command with 3-stage workflow (v0.6.3+).
|
|
656
818
|
|
|
@@ -796,7 +958,14 @@ def update(path: str, force: bool, check: bool, templates_only: bool, yes: bool)
|
|
|
796
958
|
console.print(f" Package template: {package_config_version}")
|
|
797
959
|
console.print(f" Project config: {project_config_version}")
|
|
798
960
|
|
|
799
|
-
|
|
961
|
+
try:
|
|
962
|
+
config_comparison = _compare_versions(package_config_version, project_config_version)
|
|
963
|
+
except version.InvalidVersion as e:
|
|
964
|
+
# Handle invalid version strings (e.g., unsubstituted template placeholders, corrupted configs)
|
|
965
|
+
console.print(f"[yellow]⚠ Invalid version format in config: {e}[/yellow]")
|
|
966
|
+
console.print("[cyan]ℹ️ Forcing template sync to repair configuration...[/cyan]")
|
|
967
|
+
# Force template sync by treating project version as outdated
|
|
968
|
+
config_comparison = 1 # package_config_version > project_config_version
|
|
800
969
|
|
|
801
970
|
# If versions are equal, no sync needed
|
|
802
971
|
if config_comparison <= 0:
|
|
@@ -34,7 +34,7 @@ def migrate_config_to_nested_structure(config: dict[str, Any]) -> dict[str, Any]
|
|
|
34
34
|
if "conversation_language" in config and "language" not in config:
|
|
35
35
|
# Extract conversation language from legacy location
|
|
36
36
|
conversation_language = config.pop("conversation_language", "en")
|
|
37
|
-
|
|
37
|
+
config.pop("locale", None) # Remove legacy locale field
|
|
38
38
|
|
|
39
39
|
# Map language codes to language names
|
|
40
40
|
language_names = {
|