moai-adk 0.3.13__py3-none-any.whl → 0.4.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of moai-adk might be problematic. Click here for more details.
- moai_adk/__init__.py +1 -1
- moai_adk/__main__.py +1 -1
- moai_adk/cli/commands/__init__.py +1 -1
- moai_adk/cli/commands/doctor.py +2 -2
- moai_adk/cli/commands/init.py +10 -5
- moai_adk/cli/commands/status.py +1 -1
- moai_adk/cli/commands/update.py +210 -8
- moai_adk/cli/prompts/init_prompts.py +15 -19
- moai_adk/core/__init__.py +1 -1
- moai_adk/core/diagnostics/slash_commands.py +1 -1
- moai_adk/core/git/branch.py +1 -1
- moai_adk/core/git/manager.py +1 -1
- moai_adk/core/project/backup_utils.py +1 -0
- moai_adk/core/project/phase_executor.py +3 -1
- moai_adk/core/project/validator.py +3 -2
- moai_adk/core/quality/__init__.py +1 -1
- moai_adk/core/quality/trust_checker.py +1 -1
- moai_adk/core/quality/validators/__init__.py +1 -1
- moai_adk/core/quality/validators/base_validator.py +1 -1
- moai_adk/core/template/__init__.py +1 -1
- moai_adk/core/template/backup.py +12 -3
- moai_adk/core/template/config.py +24 -0
- moai_adk/core/template/languages.py +1 -1
- moai_adk/core/template/merger.py +74 -4
- moai_adk/core/template/processor.py +62 -14
- moai_adk/templates/.claude/agents/alfred/cc-manager.md +765 -191
- 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 +205 -125
- 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 +856 -355
- moai_adk/templates/.claude/commands/alfred/1-plan.md +572 -0
- moai_adk/templates/.claude/commands/alfred/2-run.md +470 -0
- moai_adk/templates/.claude/commands/alfred/3-sync.md +366 -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 +30 -51
- 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 +94 -0
- moai_adk/templates/.claude/skills/moai-claude-code/examples.md +513 -0
- moai_adk/templates/.claude/skills/moai-claude-code/reference.md +433 -0
- moai_adk/templates/.claude/skills/moai-claude-code/templates/agent-full.md +332 -0
- moai_adk/templates/.claude/skills/moai-claude-code/templates/command-full.md +384 -0
- moai_adk/templates/.claude/skills/moai-claude-code/templates/plugin-full.json +363 -0
- moai_adk/templates/.claude/skills/moai-claude-code/templates/settings-full.json +595 -0
- moai_adk/templates/.claude/skills/moai-claude-code/templates/skill-full.md +496 -0
- moai_adk/templates/.claude/skills/moai-domain-backend/SKILL.md +99 -0
- moai_adk/templates/.claude/skills/moai-domain-cli-tool/SKILL.md +95 -0
- moai_adk/templates/.claude/skills/moai-domain-data-science/SKILL.md +98 -0
- moai_adk/templates/.claude/skills/moai-domain-database/SKILL.md +100 -0
- moai_adk/templates/.claude/skills/moai-domain-devops/SKILL.md +100 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/SKILL.md +99 -0
- moai_adk/templates/.claude/skills/moai-domain-ml/SKILL.md +99 -0
- moai_adk/templates/.claude/skills/moai-domain-mobile-app/SKILL.md +93 -0
- moai_adk/templates/.claude/skills/moai-domain-security/SKILL.md +105 -0
- moai_adk/templates/.claude/skills/moai-domain-web-api/SKILL.md +97 -0
- moai_adk/templates/.claude/skills/moai-essentials-debug/SKILL.md +102 -0
- moai_adk/templates/.claude/skills/moai-essentials-perf/SKILL.md +104 -0
- moai_adk/templates/.claude/skills/moai-essentials-refactor/SKILL.md +96 -0
- moai_adk/templates/.claude/skills/moai-essentials-review/SKILL.md +112 -0
- moai_adk/templates/.claude/skills/moai-foundation-ears/SKILL.md +98 -0
- moai_adk/templates/.claude/skills/moai-foundation-git/SKILL.md +90 -0
- moai_adk/templates/.claude/skills/moai-foundation-langs/SKILL.md +94 -0
- moai_adk/templates/.claude/skills/moai-foundation-specs/SKILL.md +93 -0
- moai_adk/templates/.claude/skills/moai-foundation-tags/SKILL.md +86 -0
- moai_adk/templates/.claude/skills/moai-foundation-trust/SKILL.md +86 -0
- moai_adk/templates/.claude/skills/moai-lang-c/SKILL.md +100 -0
- moai_adk/templates/.claude/skills/moai-lang-clojure/SKILL.md +100 -0
- moai_adk/templates/.claude/skills/moai-lang-cpp/SKILL.md +102 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/SKILL.md +100 -0
- moai_adk/templates/.claude/skills/moai-lang-dart/SKILL.md +98 -0
- moai_adk/templates/.claude/skills/moai-lang-elixir/SKILL.md +99 -0
- moai_adk/templates/.claude/skills/moai-lang-go/SKILL.md +100 -0
- moai_adk/templates/.claude/skills/moai-lang-haskell/SKILL.md +100 -0
- moai_adk/templates/.claude/skills/moai-lang-java/SKILL.md +98 -0
- moai_adk/templates/.claude/skills/moai-lang-javascript/SKILL.md +96 -0
- moai_adk/templates/.claude/skills/moai-lang-julia/SKILL.md +98 -0
- moai_adk/templates/.claude/skills/moai-lang-kotlin/SKILL.md +99 -0
- moai_adk/templates/.claude/skills/moai-lang-lua/SKILL.md +98 -0
- moai_adk/templates/.claude/skills/moai-lang-php/SKILL.md +98 -0
- moai_adk/templates/.claude/skills/moai-lang-python/SKILL.md +96 -0
- moai_adk/templates/.claude/skills/moai-lang-r/SKILL.md +99 -0
- moai_adk/templates/.claude/skills/moai-lang-ruby/SKILL.md +99 -0
- moai_adk/templates/.claude/skills/moai-lang-rust/SKILL.md +100 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/SKILL.md +100 -0
- moai_adk/templates/.claude/skills/moai-lang-shell/SKILL.md +100 -0
- moai_adk/templates/.claude/skills/moai-lang-sql/SKILL.md +100 -0
- moai_adk/templates/.claude/skills/moai-lang-swift/SKILL.md +99 -0
- moai_adk/templates/.claude/skills/moai-lang-typescript/SKILL.md +96 -0
- 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 +149 -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 +354 -573
- moai_adk/templates/__init__.py +1 -1
- moai_adk/utils/__init__.py +1 -1
- moai_adk/utils/banner.py +7 -7
- moai_adk/utils/logger.py +1 -1
- moai_adk-0.4.1.dist-info/METADATA +303 -0
- moai_adk-0.4.1.dist-info/RECORD +152 -0
- moai_adk/templates/.claude/commands/alfred/1-spec.md +0 -532
- moai_adk/templates/.claude/commands/alfred/2-build.md +0 -432
- moai_adk/templates/.moai/hooks/pre-push.sample +0 -88
- moai_adk-0.3.13.dist-info/METADATA +0 -1586
- moai_adk-0.3.13.dist-info/RECORD +0 -90
- {moai_adk-0.3.13.dist-info → moai_adk-0.4.1.dist-info}/WHEEL +0 -0
- {moai_adk-0.3.13.dist-info → moai_adk-0.4.1.dist-info}/entry_points.txt +0 -0
- {moai_adk-0.3.13.dist-info → moai_adk-0.4.1.dist-info}/licenses/LICENSE +0 -0
moai_adk/__init__.py
CHANGED
moai_adk/__main__.py
CHANGED
moai_adk/cli/commands/doctor.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
# @CODE:CLI-001 | SPEC: SPEC-CLI-001.md | TEST: tests/unit/test_doctor.py
|
|
2
|
-
# @CODE:CLAUDE-COMMANDS-001:CLI | SPEC: SPEC-CLAUDE-COMMANDS-001.md | TEST: tests/unit/test_slash_commands.py
|
|
1
|
+
# @CODE:CLI-001 | SPEC: SPEC-CLI-001/spec.md | TEST: tests/unit/test_doctor.py
|
|
2
|
+
# @CODE:CLAUDE-COMMANDS-001:CLI | SPEC: SPEC-CLAUDE-COMMANDS-001/spec.md | TEST: tests/unit/test_slash_commands.py
|
|
3
3
|
"""MoAI-ADK doctor command
|
|
4
4
|
|
|
5
5
|
System diagnostics command:
|
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/status.py
CHANGED
moai_adk/cli/commands/update.py
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
"""Update command"""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
2
4
|
import json
|
|
5
|
+
from datetime import datetime
|
|
3
6
|
from pathlib import Path
|
|
7
|
+
from typing import Any, cast
|
|
4
8
|
|
|
5
9
|
import click
|
|
6
10
|
from packaging import version
|
|
@@ -25,12 +29,186 @@ def get_latest_version() -> str | None:
|
|
|
25
29
|
url = "https://pypi.org/pypi/moai-adk/json"
|
|
26
30
|
with urllib.request.urlopen(url, timeout=5) as response: # nosec B310 - URL is hardcoded HTTPS to PyPI API, no user input
|
|
27
31
|
data = json.loads(response.read().decode("utf-8"))
|
|
28
|
-
|
|
32
|
+
version_str: str = cast(str, data["info"]["version"])
|
|
33
|
+
return version_str
|
|
29
34
|
except (urllib.error.URLError, json.JSONDecodeError, KeyError, TimeoutError):
|
|
30
35
|
# Return None if PyPI check fails
|
|
31
36
|
return None
|
|
32
37
|
|
|
33
38
|
|
|
39
|
+
def set_optimized_false(project_path: Path) -> None:
|
|
40
|
+
"""Set config.json's optimized field to false.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
project_path: Project path (absolute).
|
|
44
|
+
"""
|
|
45
|
+
config_path = project_path / ".moai" / "config.json"
|
|
46
|
+
if not config_path.exists():
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
try:
|
|
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"
|
|
55
|
+
)
|
|
56
|
+
except (json.JSONDecodeError, KeyError):
|
|
57
|
+
# Ignore errors if config.json is invalid
|
|
58
|
+
pass
|
|
59
|
+
|
|
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
|
+
}
|
|
143
|
+
|
|
144
|
+
|
|
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
|
|
155
|
+
|
|
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
|
|
197
|
+
|
|
198
|
+
try:
|
|
199
|
+
content = target_path.read_text(encoding="utf-8")
|
|
200
|
+
except UnicodeDecodeError:
|
|
201
|
+
return
|
|
202
|
+
|
|
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")
|
|
210
|
+
|
|
211
|
+
|
|
34
212
|
@click.command()
|
|
35
213
|
@click.option(
|
|
36
214
|
"--path",
|
|
@@ -70,10 +248,13 @@ def update(path: str, force: bool, check: bool) -> None:
|
|
|
70
248
|
console.print("[yellow]⚠ Project not initialized[/yellow]")
|
|
71
249
|
raise click.Abort()
|
|
72
250
|
|
|
251
|
+
existing_config = _load_existing_config(project_path)
|
|
252
|
+
|
|
73
253
|
# Phase 1: check versions
|
|
74
254
|
console.print("[cyan]🔍 Checking versions...[/cyan]")
|
|
75
255
|
current_version = __version__
|
|
76
256
|
latest_version = get_latest_version()
|
|
257
|
+
version_for_config = current_version
|
|
77
258
|
|
|
78
259
|
# Handle PyPI fetch failure
|
|
79
260
|
if latest_version is None:
|
|
@@ -98,14 +279,18 @@ def update(path: str, force: bool, check: bool) -> None:
|
|
|
98
279
|
console.print("[green]✓ Already up to date[/green]")
|
|
99
280
|
return
|
|
100
281
|
|
|
101
|
-
# Check if update is needed (version
|
|
282
|
+
# Check if update is needed (version only) - skip with --force
|
|
102
283
|
if not force and latest_version is not None:
|
|
103
284
|
current_ver = version.parse(current_version)
|
|
104
285
|
latest_ver = version.parse(latest_version)
|
|
105
286
|
|
|
106
|
-
# Don't update if current version is newer
|
|
107
|
-
if current_ver
|
|
108
|
-
|
|
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)
|
|
109
294
|
config_path = project_path / ".moai" / "config.json"
|
|
110
295
|
if config_path.exists():
|
|
111
296
|
try:
|
|
@@ -116,9 +301,8 @@ def update(path: str, force: bool, check: bool) -> None:
|
|
|
116
301
|
# Already up to date and optimized - exit silently
|
|
117
302
|
return
|
|
118
303
|
else:
|
|
119
|
-
|
|
120
|
-
console.print("[
|
|
121
|
-
return
|
|
304
|
+
# Proceed with template update (optimized=false)
|
|
305
|
+
console.print("[yellow]⚠ Template optimization needed[/yellow]")
|
|
122
306
|
except (json.JSONDecodeError, KeyError):
|
|
123
307
|
# If config.json is invalid, proceed with update
|
|
124
308
|
pass
|
|
@@ -138,6 +322,11 @@ def update(path: str, force: bool, check: bool) -> None:
|
|
|
138
322
|
# Phase 3: update templates
|
|
139
323
|
console.print("\n[cyan]📄 Updating templates...[/cyan]")
|
|
140
324
|
processor = TemplateProcessor(project_path)
|
|
325
|
+
|
|
326
|
+
context = _build_template_context(project_path, existing_config, version_for_config)
|
|
327
|
+
if context:
|
|
328
|
+
processor.set_context(context)
|
|
329
|
+
|
|
141
330
|
processor.copy_templates(backup=False, silent=True) # Backup already handled
|
|
142
331
|
|
|
143
332
|
console.print(" [green]✅ .claude/ update complete[/green]")
|
|
@@ -145,7 +334,20 @@ def update(path: str, force: bool, check: bool) -> None:
|
|
|
145
334
|
console.print(" [green]🔄 CLAUDE.md merge complete[/green]")
|
|
146
335
|
console.print(" [green]🔄 config.json merge complete[/green]")
|
|
147
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
|
+
|
|
148
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]")
|
|
149
351
|
|
|
150
352
|
except Exception as e:
|
|
151
353
|
console.print(f"[red]✗ Update failed: {e}[/red]")
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @CODE:CLI-PROMPTS-001 | SPEC: SPEC-CLI-001.md
|
|
1
|
+
# @CODE:CLI-PROMPTS-001 | SPEC: SPEC-CLI-001/spec.md
|
|
2
2
|
"""Project initialization prompts
|
|
3
3
|
|
|
4
4
|
Collect interactive project settings
|
|
@@ -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']} (CLI 옵션으로 지정됨)"
|
|
97
|
+
)
|
|
98
|
+
else:
|
|
99
|
+
console.print(
|
|
100
|
+
"[cyan]🌐 Preferred Language:[/cyan] en (기본값, /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,
|
moai_adk/core/__init__.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
# @CODE:PY314-001 | SPEC: SPEC-PY314-001.md | TEST: tests/unit/test_foundation.py
|
|
1
|
+
# @CODE:PY314-001 | SPEC: SPEC-PY314-001/spec.md | TEST: tests/unit/test_foundation.py
|
|
2
2
|
"""Core module: primary business logic"""
|
moai_adk/core/git/branch.py
CHANGED
moai_adk/core/git/manager.py
CHANGED
|
@@ -53,6 +53,7 @@ class PhaseExecutor:
|
|
|
53
53
|
".moai/memory/",
|
|
54
54
|
".claude/",
|
|
55
55
|
".claude/logs/",
|
|
56
|
+
".github/",
|
|
56
57
|
]
|
|
57
58
|
|
|
58
59
|
def __init__(self, validator: ProjectValidator) -> None:
|
|
@@ -158,6 +159,7 @@ class PhaseExecutor:
|
|
|
158
159
|
return [
|
|
159
160
|
".claude/",
|
|
160
161
|
".moai/",
|
|
162
|
+
".github/",
|
|
161
163
|
"CLAUDE.md",
|
|
162
164
|
".gitignore",
|
|
163
165
|
]
|
|
@@ -219,7 +221,7 @@ class PhaseExecutor:
|
|
|
219
221
|
# @CODE:INIT-004:VERIFY-001 | Validate installation results
|
|
220
222
|
# @CODE:INIT-004:VALIDATION-CHECK | Comprehensive installation validation
|
|
221
223
|
# Verifies all required files including 4 Alfred command files:
|
|
222
|
-
# - 0-project.md, 1-
|
|
224
|
+
# - 0-project.md, 1-plan.md, 2-run.md, 3-sync.md
|
|
223
225
|
self.validator.validate_installation(project_path)
|
|
224
226
|
|
|
225
227
|
# Initialize Git for team mode
|
|
@@ -37,6 +37,7 @@ class ProjectValidator:
|
|
|
37
37
|
".moai/specs/",
|
|
38
38
|
".moai/memory/",
|
|
39
39
|
".claude/",
|
|
40
|
+
".github/",
|
|
40
41
|
]
|
|
41
42
|
|
|
42
43
|
# Required files
|
|
@@ -48,8 +49,8 @@ class ProjectValidator:
|
|
|
48
49
|
# Required Alfred command files (SPEC-INIT-004)
|
|
49
50
|
REQUIRED_ALFRED_COMMANDS = [
|
|
50
51
|
"0-project.md",
|
|
51
|
-
"1-
|
|
52
|
-
"2-
|
|
52
|
+
"1-plan.md",
|
|
53
|
+
"2-run.md",
|
|
53
54
|
"3-sync.md",
|
|
54
55
|
]
|
|
55
56
|
|
moai_adk/core/template/backup.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @CODE:TEMPLATE-001 | SPEC: SPEC-INIT-003.md | Chain: TEMPLATE-001
|
|
1
|
+
# @CODE:TEMPLATE-001 | SPEC: SPEC-INIT-003/spec.md | Chain: TEMPLATE-001
|
|
2
2
|
"""Template backup manager (SPEC-INIT-003 v0.3.0).
|
|
3
3
|
|
|
4
4
|
Creates and manages backups to protect user data during template updates.
|
|
@@ -28,6 +28,15 @@ class TemplateBackup:
|
|
|
28
28
|
"""
|
|
29
29
|
self.target_path = target_path.resolve()
|
|
30
30
|
|
|
31
|
+
@property
|
|
32
|
+
def backup_dir(self) -> Path:
|
|
33
|
+
"""Get the backup directory path.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
Path to .moai-backups directory.
|
|
37
|
+
"""
|
|
38
|
+
return self.target_path / ".moai-backups"
|
|
39
|
+
|
|
31
40
|
def has_existing_files(self) -> bool:
|
|
32
41
|
"""Check whether backup-worthy files already exist.
|
|
33
42
|
|
|
@@ -36,7 +45,7 @@ class TemplateBackup:
|
|
|
36
45
|
"""
|
|
37
46
|
return any(
|
|
38
47
|
(self.target_path / item).exists()
|
|
39
|
-
for item in [".moai", ".claude", "CLAUDE.md"]
|
|
48
|
+
for item in [".moai", ".claude", ".github", "CLAUDE.md"]
|
|
40
49
|
)
|
|
41
50
|
|
|
42
51
|
def create_backup(self) -> Path:
|
|
@@ -50,7 +59,7 @@ class TemplateBackup:
|
|
|
50
59
|
backup_path.mkdir(parents=True, exist_ok=True)
|
|
51
60
|
|
|
52
61
|
# Copy backup targets
|
|
53
|
-
for item in [".moai", ".claude", "CLAUDE.md"]:
|
|
62
|
+
for item in [".moai", ".claude", ".github", "CLAUDE.md"]:
|
|
54
63
|
src = self.target_path / item
|
|
55
64
|
if not src.exists():
|
|
56
65
|
continue
|
moai_adk/core/template/config.py
CHANGED
|
@@ -93,3 +93,27 @@ class ConfigManager:
|
|
|
93
93
|
result[key] = value
|
|
94
94
|
|
|
95
95
|
return result
|
|
96
|
+
|
|
97
|
+
@staticmethod
|
|
98
|
+
def set_optimized(config_path: Path, value: bool) -> None:
|
|
99
|
+
"""Set the optimized field in config.json.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
config_path: Path to config.json.
|
|
103
|
+
value: Value to set (True or False).
|
|
104
|
+
"""
|
|
105
|
+
if not config_path.exists():
|
|
106
|
+
return
|
|
107
|
+
|
|
108
|
+
try:
|
|
109
|
+
with open(config_path, encoding="utf-8") as f:
|
|
110
|
+
config = json.load(f)
|
|
111
|
+
|
|
112
|
+
config.setdefault("project", {})["optimized"] = value
|
|
113
|
+
|
|
114
|
+
with open(config_path, "w", encoding="utf-8") as f:
|
|
115
|
+
json.dump(config, f, ensure_ascii=False, indent=2)
|
|
116
|
+
f.write("\n") # Add trailing newline
|
|
117
|
+
except (json.JSONDecodeError, KeyError, OSError):
|
|
118
|
+
# Ignore errors if config.json is invalid or inaccessible
|
|
119
|
+
pass
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @CODE:CORE-PROJECT-001 | SPEC: SPEC-CORE-PROJECT-001.md | TEST: tests/unit/test_language_mapping.py
|
|
1
|
+
# @CODE:CORE-PROJECT-001 | SPEC: SPEC-CORE-PROJECT-001/spec.md | TEST: tests/unit/test_language_mapping.py
|
|
2
2
|
"""Template mapping by language.
|
|
3
3
|
|
|
4
4
|
Defines template paths for 20 programming languages.
|