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.
- moai_adk/__init__.py +2 -3
- moai_adk/cli/commands/init.py +10 -5
- moai_adk/cli/commands/update.py +274 -118
- moai_adk/cli/prompts/init_prompts.py +14 -18
- moai_adk/core/diagnostics/slash_commands.py +1 -1
- moai_adk/core/project/backup_utils.py +2 -11
- moai_adk/core/project/checker.py +2 -2
- moai_adk/core/project/phase_executor.py +11 -14
- moai_adk/core/project/validator.py +3 -2
- moai_adk/core/quality/__init__.py +1 -1
- moai_adk/core/quality/trust_checker.py +63 -63
- moai_adk/core/quality/validators/__init__.py +1 -1
- moai_adk/core/quality/validators/base_validator.py +1 -1
- moai_adk/core/template/backup.py +21 -8
- moai_adk/core/template/merger.py +14 -4
- moai_adk/core/template/processor.py +24 -5
- moai_adk/templates/.claude/agents/alfred/cc-manager.md +446 -424
- moai_adk/templates/.claude/agents/alfred/debug-helper.md +116 -103
- moai_adk/templates/.claude/agents/alfred/doc-syncer.md +130 -116
- moai_adk/templates/.claude/agents/alfred/git-manager.md +186 -174
- moai_adk/templates/.claude/agents/alfred/implementation-planner.md +227 -213
- moai_adk/templates/.claude/agents/alfred/project-manager.md +216 -128
- moai_adk/templates/.claude/agents/alfred/quality-gate.md +224 -209
- moai_adk/templates/.claude/agents/alfred/spec-builder.md +174 -160
- moai_adk/templates/.claude/agents/alfred/tag-agent.md +151 -139
- moai_adk/templates/.claude/agents/alfred/tdd-implementer.md +209 -196
- moai_adk/templates/.claude/agents/alfred/trust-checker.md +247 -233
- moai_adk/templates/.claude/commands/alfred/0-project.md +756 -640
- moai_adk/templates/.claude/commands/alfred/1-plan.md +343 -333
- moai_adk/templates/.claude/commands/alfred/2-run.md +297 -285
- moai_adk/templates/.claude/commands/alfred/3-sync.md +387 -356
- moai_adk/templates/.claude/hooks/alfred/README.md +52 -52
- moai_adk/templates/.claude/hooks/alfred/alfred_hooks.py +44 -48
- moai_adk/templates/.claude/hooks/alfred/core/__init__.py +17 -17
- moai_adk/templates/.claude/hooks/alfred/core/checkpoint.py +59 -59
- moai_adk/templates/.claude/hooks/alfred/core/context.py +19 -19
- moai_adk/templates/.claude/hooks/alfred/core/project.py +52 -52
- moai_adk/templates/.claude/hooks/alfred/handlers/__init__.py +1 -1
- moai_adk/templates/.claude/hooks/alfred/handlers/notification.py +4 -4
- moai_adk/templates/.claude/hooks/alfred/handlers/session.py +27 -27
- moai_adk/templates/.claude/hooks/alfred/handlers/tool.py +16 -17
- moai_adk/templates/.claude/hooks/alfred/handlers/user.py +11 -11
- moai_adk/templates/.claude/output-styles/alfred/agentic-coding.md +308 -307
- moai_adk/templates/.claude/output-styles/alfred/moai-adk-learning.md +297 -296
- moai_adk/templates/.claude/output-styles/alfred/study-with-alfred.md +191 -190
- moai_adk/templates/.claude/skills/moai-alfred-code-reviewer/SKILL.md +112 -0
- moai_adk/templates/.claude/skills/moai-alfred-debugger-pro/SKILL.md +103 -0
- moai_adk/templates/.claude/skills/moai-alfred-ears-authoring/SKILL.md +103 -0
- moai_adk/templates/.claude/skills/moai-alfred-git-workflow/SKILL.md +95 -0
- moai_adk/templates/.claude/skills/moai-alfred-language-detection/SKILL.md +99 -0
- moai_adk/templates/.claude/skills/moai-alfred-performance-optimizer/SKILL.md +105 -0
- moai_adk/templates/.claude/skills/moai-alfred-refactoring-coach/SKILL.md +97 -0
- moai_adk/templates/.claude/skills/moai-alfred-spec-metadata-validation/SKILL.md +97 -0
- moai_adk/templates/.claude/skills/moai-alfred-tag-scanning/SKILL.md +90 -0
- moai_adk/templates/.claude/skills/moai-alfred-trust-validation/SKILL.md +99 -0
- moai_adk/templates/.claude/skills/moai-alfred-tui-survey/SKILL.md +87 -0
- moai_adk/templates/.claude/skills/moai-alfred-tui-survey/examples.md +62 -0
- moai_adk/templates/.claude/skills/moai-claude-code/SKILL.md +70 -43
- moai_adk/templates/.claude/skills/moai-claude-code/examples.md +141 -141
- moai_adk/templates/.claude/skills/moai-claude-code/reference.md +179 -165
- moai_adk/templates/.claude/skills/moai-claude-code/templates/agent-full.md +78 -78
- moai_adk/templates/.claude/skills/moai-claude-code/templates/command-full.md +90 -90
- moai_adk/templates/.claude/skills/moai-claude-code/templates/plugin-full.json +39 -25
- moai_adk/templates/.claude/skills/moai-claude-code/templates/settings-full.json +117 -74
- moai_adk/templates/.claude/skills/moai-claude-code/templates/skill-full.md +131 -134
- moai_adk/templates/.claude/skills/moai-domain-backend/SKILL.md +43 -12
- moai_adk/templates/.claude/skills/moai-domain-cli-tool/SKILL.md +43 -12
- moai_adk/templates/.claude/skills/moai-domain-data-science/SKILL.md +43 -12
- moai_adk/templates/.claude/skills/moai-domain-database/SKILL.md +43 -12
- moai_adk/templates/.claude/skills/moai-domain-devops/SKILL.md +43 -12
- moai_adk/templates/.claude/skills/moai-domain-frontend/SKILL.md +43 -12
- moai_adk/templates/.claude/skills/moai-domain-ml/SKILL.md +43 -11
- moai_adk/templates/.claude/skills/moai-domain-mobile-app/SKILL.md +43 -12
- moai_adk/templates/.claude/skills/moai-domain-security/SKILL.md +43 -12
- moai_adk/templates/.claude/skills/moai-domain-web-api/SKILL.md +43 -12
- moai_adk/templates/.claude/skills/moai-essentials-debug/SKILL.md +47 -11
- moai_adk/templates/.claude/skills/moai-essentials-perf/SKILL.md +47 -11
- moai_adk/templates/.claude/skills/moai-essentials-refactor/SKILL.md +51 -14
- moai_adk/templates/.claude/skills/moai-essentials-review/SKILL.md +46 -10
- moai_adk/templates/.claude/skills/moai-foundation-ears/SKILL.md +62 -25
- moai_adk/templates/.claude/skills/moai-foundation-git/SKILL.md +44 -17
- moai_adk/templates/.claude/skills/moai-foundation-langs/SKILL.md +44 -14
- moai_adk/templates/.claude/skills/moai-foundation-specs/SKILL.md +45 -13
- moai_adk/templates/.claude/skills/moai-foundation-tags/SKILL.md +46 -14
- moai_adk/templates/.claude/skills/moai-foundation-trust/SKILL.md +48 -8
- moai_adk/templates/.claude/skills/moai-lang-c/SKILL.md +44 -12
- moai_adk/templates/.claude/skills/moai-lang-clojure/SKILL.md +44 -12
- moai_adk/templates/.claude/skills/moai-lang-cpp/SKILL.md +44 -11
- moai_adk/templates/.claude/skills/moai-lang-csharp/SKILL.md +44 -11
- moai_adk/templates/.claude/skills/moai-lang-dart/SKILL.md +44 -12
- moai_adk/templates/.claude/skills/moai-lang-elixir/SKILL.md +44 -11
- moai_adk/templates/.claude/skills/moai-lang-go/SKILL.md +44 -11
- moai_adk/templates/.claude/skills/moai-lang-haskell/SKILL.md +44 -11
- moai_adk/templates/.claude/skills/moai-lang-java/SKILL.md +44 -12
- moai_adk/templates/.claude/skills/moai-lang-javascript/SKILL.md +44 -12
- moai_adk/templates/.claude/skills/moai-lang-julia/SKILL.md +44 -12
- moai_adk/templates/.claude/skills/moai-lang-kotlin/SKILL.md +44 -12
- moai_adk/templates/.claude/skills/moai-lang-lua/SKILL.md +44 -11
- moai_adk/templates/.claude/skills/moai-lang-php/SKILL.md +44 -11
- moai_adk/templates/.claude/skills/moai-lang-python/SKILL.md +44 -12
- moai_adk/templates/.claude/skills/moai-lang-r/SKILL.md +44 -11
- moai_adk/templates/.claude/skills/moai-lang-ruby/SKILL.md +44 -11
- moai_adk/templates/.claude/skills/moai-lang-rust/SKILL.md +44 -12
- moai_adk/templates/.claude/skills/moai-lang-scala/SKILL.md +44 -12
- moai_adk/templates/.claude/skills/moai-lang-shell/SKILL.md +44 -11
- moai_adk/templates/.claude/skills/moai-lang-sql/SKILL.md +44 -12
- moai_adk/templates/.claude/skills/moai-lang-swift/SKILL.md +44 -12
- moai_adk/templates/.claude/skills/moai-lang-typescript/SKILL.md +44 -12
- moai_adk/templates/.github/PULL_REQUEST_TEMPLATE.md +44 -43
- moai_adk/templates/.github/workflows/moai-gitflow.yml +36 -35
- moai_adk/templates/.moai/config.json +9 -6
- moai_adk/templates/.moai/memory/development-guide.md +220 -221
- moai_adk/templates/.moai/memory/gitflow-protection-policy.md +85 -85
- moai_adk/templates/.moai/memory/spec-metadata.md +229 -150
- moai_adk/templates/.moai/project/product.md +90 -90
- moai_adk/templates/.moai/project/structure.md +85 -85
- moai_adk/templates/.moai/project/tech.md +117 -117
- moai_adk/templates/CLAUDE.md +564 -709
- moai_adk-0.4.4.dist-info/METADATA +369 -0
- moai_adk-0.4.4.dist-info/RECORD +152 -0
- moai_adk/templates/.claude/commands/alfred/1-spec.md +0 -31
- moai_adk/templates/.claude/commands/alfred/2-build.md +0 -30
- moai_adk/templates/.claude/skills/scripts/standardize_skills.py +0 -166
- moai_adk/templates/.claude/skills/scripts/verify_standardization.sh +0 -43
- moai_adk/templates/.moai/hooks/pre-push.sample +0 -88
- moai_adk-0.4.0.dist-info/METADATA +0 -1816
- moai_adk-0.4.0.dist-info/RECORD +0 -145
- {moai_adk-0.4.0.dist-info → moai_adk-0.4.4.dist-info}/WHEEL +0 -0
- {moai_adk-0.4.0.dist-info → moai_adk-0.4.4.dist-info}/entry_points.txt +0 -0
- {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
|
|
7
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
8
8
|
|
|
9
9
|
try:
|
|
10
10
|
__version__ = version("moai-adk")
|
|
11
11
|
except PackageNotFoundError:
|
|
12
|
-
|
|
13
|
-
__version__ = "0.4.0-dev"
|
|
12
|
+
__version__ = "0.4.1-dev"
|
|
14
13
|
|
|
15
14
|
__all__ = ["__version__"]
|
moai_adk/cli/commands/init.py
CHANGED
|
@@ -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"
|
|
70
|
-
default=
|
|
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
|
|
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
|
|
moai_adk/cli/commands/update.py
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
"""Update command
|
|
1
|
+
"""Update command"""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
2
4
|
import json
|
|
3
|
-
import
|
|
4
|
-
import
|
|
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
|
|
37
|
-
"""
|
|
39
|
+
def set_optimized_false(project_path: Path) -> None:
|
|
40
|
+
"""Set config.json's optimized field to false.
|
|
38
41
|
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
Args:
|
|
43
|
+
project_path: Project path (absolute).
|
|
41
44
|
"""
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
65
|
-
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
94
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
console.print(f"
|
|
109
|
-
|
|
110
|
-
|
|
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
|
|
227
|
+
help="Only check version (do not update)"
|
|
126
228
|
)
|
|
127
|
-
def update(check: bool) -> None:
|
|
128
|
-
"""
|
|
229
|
+
def update(path: str, force: bool, check: bool) -> None:
|
|
230
|
+
"""Update template files to the latest version.
|
|
129
231
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
-
|
|
137
|
-
|
|
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
|
-
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
170
|
-
if
|
|
171
|
-
|
|
172
|
-
|
|
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
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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:
|
|
179
|
-
|
|
322
|
+
# Phase 3: update templates
|
|
323
|
+
console.print("\n[cyan]📄 Updating templates...[/cyan]")
|
|
324
|
+
processor = TemplateProcessor(project_path)
|
|
180
325
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
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
|
|
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.
|
|
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
|
-
#
|
|
143
|
+
# 4. Author information (optional)
|
|
148
144
|
result = questionary.confirm(
|
|
149
145
|
"👤 Add author information? (optional)",
|
|
150
146
|
default=False,
|
|
@@ -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/
|
|
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:
|
moai_adk/core/project/checker.py
CHANGED
|
@@ -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"
|
|
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"
|