moai-adk 0.9.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 +214 -56
- moai_adk/core/issue_creator.py +2 -2
- 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/ci_validator.py +34 -4
- moai_adk/core/tags/pre_commit_validator.py +40 -2
- moai_adk/core/tags/reporter.py +2 -3
- moai_adk/core/tags/validator.py +1 -1
- moai_adk/core/template_engine.py +20 -5
- moai_adk/templates/.claude/agents/alfred/backend-expert.md +319 -0
- moai_adk/templates/.claude/agents/alfred/devops-expert.md +464 -0
- moai_adk/templates/.claude/agents/alfred/doc-syncer.md +1 -1
- moai_adk/templates/.claude/agents/alfred/frontend-expert.md +357 -0
- moai_adk/templates/.claude/agents/alfred/git-manager.md +2 -2
- moai_adk/templates/.claude/agents/alfred/implementation-planner.md +76 -3
- moai_adk/templates/.claude/agents/alfred/project-manager.md +49 -10
- moai_adk/templates/.claude/agents/alfred/quality-gate.md +3 -3
- moai_adk/templates/.claude/agents/alfred/spec-builder.md +108 -3
- moai_adk/templates/.claude/agents/alfred/tag-agent.md +74 -0
- moai_adk/templates/.claude/agents/alfred/tdd-implementer.md +107 -5
- moai_adk/templates/.claude/agents/alfred/trust-checker.md +2 -2
- moai_adk/templates/.claude/agents/alfred/ui-ux-expert.md +571 -0
- moai_adk/templates/.claude/commands/alfred/0-project.md +465 -129
- moai_adk/templates/.claude/commands/alfred/1-plan.md +139 -65
- moai_adk/templates/.claude/commands/alfred/2-run.md +214 -50
- moai_adk/templates/.claude/commands/alfred/3-sync.md +372 -46
- moai_adk/templates/.claude/commands/alfred/9-feedback.md +1 -1
- moai_adk/templates/.claude/hooks/alfred/core/project.py +25 -27
- 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 +4 -4
- moai_adk/templates/.claude/hooks/alfred/handlers/__init__.py +29 -0
- moai_adk/templates/.claude/hooks/alfred/post_tool__log_changes.py +11 -19
- moai_adk/templates/.claude/hooks/alfred/pre_tool__auto_checkpoint.py +11 -19
- moai_adk/templates/.claude/hooks/alfred/session_end__cleanup.py +11 -19
- moai_adk/templates/.claude/hooks/alfred/session_start__show_project_info.py +10 -18
- moai_adk/templates/.claude/hooks/alfred/shared/core/__init__.py +2 -2
- moai_adk/templates/.claude/hooks/alfred/shared/core/checkpoint.py +3 -3
- moai_adk/templates/.claude/hooks/alfred/shared/core/context.py +5 -5
- moai_adk/templates/.claude/hooks/alfred/shared/core/project.py +40 -41
- moai_adk/templates/.claude/hooks/alfred/shared/core/tags.py +55 -23
- moai_adk/templates/.claude/hooks/alfred/shared/core/version_cache.py +4 -4
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/notification.py +132 -3
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/session.py +9 -10
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/tool.py +3 -6
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/user.py +19 -0
- moai_adk/templates/.claude/hooks/alfred/user_prompt__jit_load_docs.py +14 -22
- 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-config-schema/reference.md +444 -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-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-spec-authoring → moai-alfred-spec-authoring}/SKILL.md +5 -5
- 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-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 +182 -25
- moai_adk/templates/.github/workflows/moai-release-pipeline.yml +35 -29
- 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 +76 -7
- 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 +208 -41
- 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 +3 -1
- moai_adk/templates/CLAUDE.md +940 -45
- 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.9.0.dist-info → moai_adk-0.15.0.dist-info}/METADATA +1166 -455
- {moai_adk-0.9.0.dist-info → moai_adk-0.15.0.dist-info}/RECORD +169 -109
- moai_adk/templates/.claude/hooks/alfred/alfred_hooks.py +0 -209
- moai_adk/templates/.claude/hooks/alfred/notification__handle_events.py +0 -102
- moai_adk/templates/.claude/hooks/alfred/stop__handle_interrupt.py +0 -102
- moai_adk/templates/.claude/hooks/alfred/subagent_stop__handle_subagent_end.py +0 -102
- 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/.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/SPEC-METADATA.md +0 -356
- moai_adk/templates/.moai/memory/gitflow-protection-policy.md +0 -330
- 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/README.md +0 -256
- moai_adk/templates/__init__.py +0 -2
- /moai_adk/templates/{.moai/memory/ISSUE-LABEL-MAPPING.md → .claude/skills/moai-alfred-issue-labels/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/.claude/skills/{moai-spec-authoring → moai-alfred-spec-authoring}/README.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-spec-authoring → moai-alfred-spec-authoring}/examples/validate-spec.sh +0 -0
- /moai_adk/templates/.claude/skills/{moai-spec-authoring → moai-alfred-spec-authoring}/examples.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-spec-authoring → moai-alfred-spec-authoring}/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.9.0.dist-info → moai_adk-0.15.0.dist-info}/WHEEL +0 -0
- {moai_adk-0.9.0.dist-info → moai_adk-0.15.0.dist-info}/entry_points.txt +0 -0
- {moai_adk-0.9.0.dist-info → moai_adk-0.15.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -11,7 +11,9 @@ Phase-based 5-step initialization process:
|
|
|
11
11
|
5. Validation: Verification and finalization
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
|
+
import json
|
|
14
15
|
import time
|
|
16
|
+
from datetime import datetime
|
|
15
17
|
from pathlib import Path
|
|
16
18
|
|
|
17
19
|
from moai_adk.core.project.phase_executor import PhaseExecutor, ProgressCallback
|
|
@@ -55,6 +57,62 @@ class ProjectInitializer:
|
|
|
55
57
|
self.validator = ProjectValidator()
|
|
56
58
|
self.executor = PhaseExecutor(self.validator)
|
|
57
59
|
|
|
60
|
+
def _create_memory_files(self) -> list[str]:
|
|
61
|
+
"""Create runtime session and memory files (auto-generated per user/session)
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
List of created memory files
|
|
65
|
+
|
|
66
|
+
@CODE:INIT-MEMORY-001 | Auto-generate session memory files
|
|
67
|
+
"""
|
|
68
|
+
memory_dir = self.path / ".moai" / "memory"
|
|
69
|
+
memory_dir.mkdir(parents=True, exist_ok=True)
|
|
70
|
+
created_files = []
|
|
71
|
+
|
|
72
|
+
# 1. project-notes.json - Project tracking notes (empty on init)
|
|
73
|
+
project_notes = {
|
|
74
|
+
"tech_debt": [],
|
|
75
|
+
"performance_bottlenecks": [],
|
|
76
|
+
"recent_patterns": {
|
|
77
|
+
"frequent_file_edits": [],
|
|
78
|
+
"test_failures": [],
|
|
79
|
+
"git_operations": "daily commits, feature branches"
|
|
80
|
+
},
|
|
81
|
+
"next_priorities": []
|
|
82
|
+
}
|
|
83
|
+
project_notes_file = memory_dir / "project-notes.json"
|
|
84
|
+
project_notes_file.write_text(json.dumps(project_notes, indent=2))
|
|
85
|
+
created_files.append(str(project_notes_file))
|
|
86
|
+
|
|
87
|
+
# 2. session-hint.json - Last session state
|
|
88
|
+
session_hint = {
|
|
89
|
+
"last_command": None,
|
|
90
|
+
"command_timestamp": None,
|
|
91
|
+
"hours_ago": None,
|
|
92
|
+
"active_spec": None,
|
|
93
|
+
"current_branch": "main"
|
|
94
|
+
}
|
|
95
|
+
session_hint_file = memory_dir / "session-hint.json"
|
|
96
|
+
session_hint_file.write_text(json.dumps(session_hint, indent=2))
|
|
97
|
+
created_files.append(str(session_hint_file))
|
|
98
|
+
|
|
99
|
+
# 3. user-patterns.json - User preferences and expertise
|
|
100
|
+
user_patterns = {
|
|
101
|
+
"tech_preferences": {},
|
|
102
|
+
"expertise_signals": {
|
|
103
|
+
"ask_question_skip_rate": 0.0,
|
|
104
|
+
"custom_workflows": 0,
|
|
105
|
+
"estimated_level": "beginner"
|
|
106
|
+
},
|
|
107
|
+
"skip_questions": [],
|
|
108
|
+
"last_updated": datetime.now().isoformat() + "Z"
|
|
109
|
+
}
|
|
110
|
+
user_patterns_file = memory_dir / "user-patterns.json"
|
|
111
|
+
user_patterns_file.write_text(json.dumps(user_patterns, indent=2))
|
|
112
|
+
created_files.append(str(user_patterns_file))
|
|
113
|
+
|
|
114
|
+
return created_files
|
|
115
|
+
|
|
58
116
|
def initialize(
|
|
59
117
|
self,
|
|
60
118
|
mode: str = "personal",
|
|
@@ -144,6 +202,9 @@ class ProjectInitializer:
|
|
|
144
202
|
self.path, mode, progress_callback
|
|
145
203
|
)
|
|
146
204
|
|
|
205
|
+
# Phase 6: Create runtime memory files (auto-generated per user/session)
|
|
206
|
+
memory_files = self._create_memory_files()
|
|
207
|
+
|
|
147
208
|
# Generate result
|
|
148
209
|
duration = int((time.time() - start_time) * 1000) # ms
|
|
149
210
|
return InstallationResult(
|
|
@@ -153,7 +214,7 @@ class ProjectInitializer:
|
|
|
153
214
|
mode=mode,
|
|
154
215
|
locale=locale,
|
|
155
216
|
duration=duration,
|
|
156
|
-
created_files=resource_files + config_files,
|
|
217
|
+
created_files=resource_files + config_files + memory_files,
|
|
157
218
|
)
|
|
158
219
|
|
|
159
220
|
except Exception as e:
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# @CODE:INIT-003:PHASE | SPEC: .moai/specs/SPEC-INIT-003/spec.md | TEST: tests/unit/test_init_reinit.py
|
|
2
|
+
# @CODE:TEST-COVERAGE-001 | SPEC: SPEC-TEST-COVERAGE-001.md | TEST: tests/unit/test_phase_executor.py
|
|
2
3
|
"""Phase-based installation executor (SPEC-INIT-003 v0.4.2)
|
|
3
4
|
|
|
4
5
|
Runs the project initialization across five phases:
|
|
@@ -7,6 +8,8 @@ Runs the project initialization across five phases:
|
|
|
7
8
|
- Phase 3: Resource (copy templates while preserving user content)
|
|
8
9
|
- Phase 4: Configuration (generate configuration files)
|
|
9
10
|
- Phase 5: Validation (verify and finalize)
|
|
11
|
+
|
|
12
|
+
Test coverage includes 5-phase integration tests with backup, configuration, and validation
|
|
10
13
|
"""
|
|
11
14
|
|
|
12
15
|
import json
|
|
@@ -142,7 +145,7 @@ class PhaseExecutor:
|
|
|
142
145
|
# Set template variable context (if provided)
|
|
143
146
|
if config:
|
|
144
147
|
# @TAG:LANG-FIX-001:PY-CONFIG | Read language from nested config structure
|
|
145
|
-
language_config = config.get("language", {})
|
|
148
|
+
language_config: dict[str, Any] = config.get("language", {})
|
|
146
149
|
if not isinstance(language_config, dict):
|
|
147
150
|
language_config = {}
|
|
148
151
|
|
|
@@ -193,6 +196,43 @@ class PhaseExecutor:
|
|
|
193
196
|
"Phase 4: Generating configurations...", progress_callback
|
|
194
197
|
)
|
|
195
198
|
|
|
199
|
+
# Read existing config to preserve user settings (Issue #165)
|
|
200
|
+
config_path = project_path / ".moai" / "config.json"
|
|
201
|
+
existing_config: dict[str, Any] = {}
|
|
202
|
+
if config_path.exists():
|
|
203
|
+
try:
|
|
204
|
+
with open(config_path, "r", encoding="utf-8") as f:
|
|
205
|
+
existing_config = json.load(f)
|
|
206
|
+
except (json.JSONDecodeError, OSError):
|
|
207
|
+
# If config reading fails, start fresh
|
|
208
|
+
existing_config = {}
|
|
209
|
+
|
|
210
|
+
# Merge user settings from existing config (preserve customization)
|
|
211
|
+
if existing_config:
|
|
212
|
+
# Preserve user.nickname if it exists
|
|
213
|
+
if "user" in existing_config and isinstance(existing_config.get("user"), dict):
|
|
214
|
+
if "user" not in config:
|
|
215
|
+
config["user"] = {}
|
|
216
|
+
user_config = config["user"]
|
|
217
|
+
if isinstance(user_config, dict):
|
|
218
|
+
existing_user = existing_config["user"]
|
|
219
|
+
if isinstance(existing_user, dict) and "nickname" in existing_user:
|
|
220
|
+
user_config["nickname"] = existing_user["nickname"]
|
|
221
|
+
|
|
222
|
+
# Preserve language settings if they exist
|
|
223
|
+
if "language" in existing_config and isinstance(existing_config.get("language"), dict):
|
|
224
|
+
if "language" not in config:
|
|
225
|
+
config["language"] = {}
|
|
226
|
+
lang_config = config["language"]
|
|
227
|
+
if isinstance(lang_config, dict):
|
|
228
|
+
existing_lang = existing_config["language"]
|
|
229
|
+
if isinstance(existing_lang, dict):
|
|
230
|
+
# Preserve conversation_language settings
|
|
231
|
+
if "conversation_language" in existing_lang:
|
|
232
|
+
lang_config["conversation_language"] = existing_lang["conversation_language"]
|
|
233
|
+
if "conversation_language_name" in existing_lang:
|
|
234
|
+
lang_config["conversation_language_name"] = existing_lang["conversation_language_name"]
|
|
235
|
+
|
|
196
236
|
# Ensure project section exists and set defaults
|
|
197
237
|
if "project" not in config:
|
|
198
238
|
config["project"] = {}
|
|
@@ -202,7 +242,6 @@ class PhaseExecutor:
|
|
|
202
242
|
project_config["optimized"] = False # Default value
|
|
203
243
|
|
|
204
244
|
# Write config.json
|
|
205
|
-
config_path = project_path / ".moai" / "config.json"
|
|
206
245
|
with open(config_path, "w", encoding="utf-8") as f:
|
|
207
246
|
json.dump(config, f, indent=2, ensure_ascii=False)
|
|
208
247
|
|
|
@@ -216,9 +255,8 @@ class PhaseExecutor:
|
|
|
216
255
|
) -> None:
|
|
217
256
|
"""Phase 5: validation and wrap-up.
|
|
218
257
|
|
|
219
|
-
@CODE:INIT-
|
|
258
|
+
@CODE:INIT-PHASE-001 | Phase 5 verification logic
|
|
220
259
|
@REQ:VALIDATION-001 | SPEC-INIT-004: Verify required files after initialization completion
|
|
221
|
-
@CODE:INIT-004:PHASE5-INTEGRATION | Integration of validation in Phase 5
|
|
222
260
|
|
|
223
261
|
Args:
|
|
224
262
|
project_path: Project path.
|
|
@@ -230,8 +268,8 @@ class PhaseExecutor:
|
|
|
230
268
|
"Phase 5: Validation and finalization...", progress_callback
|
|
231
269
|
)
|
|
232
270
|
|
|
233
|
-
#
|
|
234
|
-
#
|
|
271
|
+
# Validate installation results
|
|
272
|
+
# Comprehensive installation validation
|
|
235
273
|
# Verifies all required files including 4 Alfred command files:
|
|
236
274
|
# - 0-project.md, 1-plan.md, 2-run.md, 3-sync.md
|
|
237
275
|
self.validator.validate_installation(project_path)
|
|
@@ -312,8 +350,12 @@ class PhaseExecutor:
|
|
|
312
350
|
cwd=project_path,
|
|
313
351
|
check=True,
|
|
314
352
|
capture_output=True,
|
|
353
|
+
timeout=30, # Default timeout for git operations
|
|
315
354
|
)
|
|
316
355
|
# Intentionally avoid printing to keep progress output clean
|
|
356
|
+
except subprocess.TimeoutExpired:
|
|
357
|
+
# Timeout is non-fatal
|
|
358
|
+
pass
|
|
317
359
|
except subprocess.CalledProcessError:
|
|
318
360
|
# Only log on error; failures are non-fatal
|
|
319
361
|
pass
|
|
@@ -16,6 +16,8 @@ import os
|
|
|
16
16
|
from typing import Any, Dict, List, Optional
|
|
17
17
|
|
|
18
18
|
import requests
|
|
19
|
+
from requests.adapters import HTTPAdapter
|
|
20
|
+
from urllib3.util import Retry
|
|
19
21
|
|
|
20
22
|
from .pre_commit_validator import (
|
|
21
23
|
PreCommitValidator,
|
|
@@ -87,16 +89,44 @@ class CIValidator(PreCommitValidator):
|
|
|
87
89
|
'Accept': 'application/vnd.github.v3+json'
|
|
88
90
|
}
|
|
89
91
|
|
|
92
|
+
# Create session with retry strategy
|
|
93
|
+
session = requests.Session()
|
|
94
|
+
retry = Retry(
|
|
95
|
+
total=3,
|
|
96
|
+
backoff_factor=0.5,
|
|
97
|
+
status_forcelist=[500, 502, 503, 504],
|
|
98
|
+
allowed_methods=["GET"]
|
|
99
|
+
)
|
|
100
|
+
adapter = HTTPAdapter(max_retries=retry)
|
|
101
|
+
session.mount("https://", adapter)
|
|
102
|
+
|
|
90
103
|
try:
|
|
91
|
-
|
|
104
|
+
# Dual timeout: (connect_timeout, read_timeout)
|
|
105
|
+
response = session.get(
|
|
106
|
+
url,
|
|
107
|
+
headers=headers,
|
|
108
|
+
timeout=(5, 10)
|
|
109
|
+
)
|
|
92
110
|
response.raise_for_status()
|
|
93
111
|
|
|
94
112
|
files_data = response.json()
|
|
95
113
|
return [file_info['filename'] for file_info in files_data]
|
|
96
114
|
|
|
97
|
-
except
|
|
98
|
-
#
|
|
115
|
+
except requests.exceptions.Timeout:
|
|
116
|
+
# Network timeout - return empty list gracefully
|
|
117
|
+
return []
|
|
118
|
+
except requests.exceptions.HTTPError as e:
|
|
119
|
+
# HTTP error (4xx, 5xx)
|
|
120
|
+
if e.response.status_code == 404:
|
|
121
|
+
# PR not found
|
|
122
|
+
return []
|
|
123
|
+
# Other HTTP errors: log but continue
|
|
124
|
+
return []
|
|
125
|
+
except requests.exceptions.RequestException:
|
|
126
|
+
# Network/connection errors
|
|
99
127
|
return []
|
|
128
|
+
finally:
|
|
129
|
+
session.close()
|
|
100
130
|
|
|
101
131
|
def validate_pr_changes(
|
|
102
132
|
self,
|
|
@@ -260,7 +290,7 @@ class CIValidator(PreCommitValidator):
|
|
|
260
290
|
tag = error.tag
|
|
261
291
|
message = error.message
|
|
262
292
|
locations = ', '.join([
|
|
263
|
-
f"`{f}:{
|
|
293
|
+
f"`{f}:{line}`" for f, line in error.locations[:3]
|
|
264
294
|
])
|
|
265
295
|
if len(error.locations) > 3:
|
|
266
296
|
locations += f" (+{len(error.locations) - 3} more)"
|
|
@@ -26,7 +26,7 @@ class ValidationError:
|
|
|
26
26
|
locations: List[Tuple[str, int]] = field(default_factory=list)
|
|
27
27
|
|
|
28
28
|
def __str__(self) -> str:
|
|
29
|
-
loc_str = ", ".join([f"{f}:{
|
|
29
|
+
loc_str = ", ".join([f"{f}:{line}" for f, line in self.locations])
|
|
30
30
|
return f"{self.message}: {self.tag} at {loc_str}"
|
|
31
31
|
|
|
32
32
|
|
|
@@ -98,6 +98,37 @@ class PreCommitValidator:
|
|
|
98
98
|
self.strict_mode = strict_mode
|
|
99
99
|
self.check_orphans = check_orphans
|
|
100
100
|
self.tag_pattern = re.compile(tag_pattern or self.DEFAULT_TAG_PATTERN)
|
|
101
|
+
# Document files to exclude from TAG validation
|
|
102
|
+
self.excluded_file_patterns = [
|
|
103
|
+
r"\.md$", # Markdown files
|
|
104
|
+
r"README", # README files
|
|
105
|
+
r"CHANGELOG", # CHANGELOG files
|
|
106
|
+
r"CONTRIBUTING", # CONTRIBUTING files
|
|
107
|
+
r"LICENSE", # LICENSE files
|
|
108
|
+
r"\.txt$", # Text files
|
|
109
|
+
r"\.rst$", # ReStructuredText files
|
|
110
|
+
r"test_.*\.py$", # Test files (test_*.py)
|
|
111
|
+
r".*_test\.py$", # Test files (*_test.py)
|
|
112
|
+
r"tests/", # Files in tests/ directory
|
|
113
|
+
r"validator\.py$", # Validator files (contain example TAGs in docstrings)
|
|
114
|
+
]
|
|
115
|
+
|
|
116
|
+
def should_validate_file(self, filepath: str) -> bool:
|
|
117
|
+
"""Check if file should be validated for TAGs
|
|
118
|
+
|
|
119
|
+
Document files (*.md, README, CONTRIBUTING, etc.) are excluded
|
|
120
|
+
because they often contain example TAGs that are not actual code.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
filepath: File path to check
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
True if file should be validated, False if excluded
|
|
127
|
+
"""
|
|
128
|
+
for pattern in self.excluded_file_patterns:
|
|
129
|
+
if re.search(pattern, filepath):
|
|
130
|
+
return False
|
|
131
|
+
return True
|
|
101
132
|
|
|
102
133
|
def validate_format(self, tag: str) -> bool:
|
|
103
134
|
"""Validate TAG format
|
|
@@ -137,6 +168,10 @@ class PreCommitValidator:
|
|
|
137
168
|
tag_locations: Dict[str, List[Tuple[str, int]]] = {}
|
|
138
169
|
|
|
139
170
|
for filepath in files:
|
|
171
|
+
# Skip document files (*.md, README, CONTRIBUTING, etc.)
|
|
172
|
+
if not self.should_validate_file(filepath):
|
|
173
|
+
continue
|
|
174
|
+
|
|
140
175
|
try:
|
|
141
176
|
path = Path(filepath)
|
|
142
177
|
if not path.exists() or not path.is_file():
|
|
@@ -195,6 +230,10 @@ class PreCommitValidator:
|
|
|
195
230
|
}
|
|
196
231
|
|
|
197
232
|
for filepath in files:
|
|
233
|
+
# Skip document files (*.md, README, CONTRIBUTING, etc.)
|
|
234
|
+
if not self.should_validate_file(filepath):
|
|
235
|
+
continue
|
|
236
|
+
|
|
198
237
|
try:
|
|
199
238
|
path = Path(filepath)
|
|
200
239
|
if not path.exists() or not path.is_file():
|
|
@@ -206,7 +245,6 @@ class PreCommitValidator:
|
|
|
206
245
|
for line_num, line in enumerate(lines, start=1):
|
|
207
246
|
matches = self.tag_pattern.findall(line)
|
|
208
247
|
for prefix, domain in matches:
|
|
209
|
-
tag = f"@{prefix}:{domain}"
|
|
210
248
|
if domain not in tags_by_type[prefix]:
|
|
211
249
|
tags_by_type[prefix][domain] = []
|
|
212
250
|
tags_by_type[prefix][domain].append((filepath, line_num))
|
moai_adk/core/tags/reporter.py
CHANGED
|
@@ -155,7 +155,7 @@ class InventoryGenerator:
|
|
|
155
155
|
Returns:
|
|
156
156
|
List of TagInventory objects
|
|
157
157
|
"""
|
|
158
|
-
inventory = []
|
|
158
|
+
inventory: list[TagInventory] = []
|
|
159
159
|
root = Path(root_path)
|
|
160
160
|
|
|
161
161
|
if not root.exists() or not root.is_dir():
|
|
@@ -224,7 +224,6 @@ class InventoryGenerator:
|
|
|
224
224
|
|
|
225
225
|
for tag_type, domain in matches:
|
|
226
226
|
tag_id = domain
|
|
227
|
-
full_tag = f"@{tag_type}:{domain}"
|
|
228
227
|
|
|
229
228
|
# Extract context (±2 lines)
|
|
230
229
|
context_lines = []
|
|
@@ -421,7 +420,7 @@ class MatrixGenerator:
|
|
|
421
420
|
spec = "1" if row["SPEC"] else "0"
|
|
422
421
|
code = "1" if row["CODE"] else "0"
|
|
423
422
|
test = "1" if row["TEST"] else "0"
|
|
424
|
-
|
|
423
|
+
"1" if row["DOC"] else "0"
|
|
425
424
|
completion = f"{matrix.completion_percentages[domain]:.1f}"
|
|
426
425
|
|
|
427
426
|
lines.append(f"{domain},{spec},{code},{test},{completion}")
|
moai_adk/core/tags/validator.py
CHANGED
moai_adk/core/template_engine.py
CHANGED
|
@@ -31,13 +31,17 @@ class TemplateEngine:
|
|
|
31
31
|
- File-based and string-based template rendering
|
|
32
32
|
"""
|
|
33
33
|
|
|
34
|
-
def __init__(self, strict_undefined: bool =
|
|
34
|
+
def __init__(self, strict_undefined: bool = True):
|
|
35
35
|
"""
|
|
36
36
|
Initialize the template engine.
|
|
37
37
|
|
|
38
38
|
Args:
|
|
39
|
-
strict_undefined: If True, raise error on undefined variables.
|
|
39
|
+
strict_undefined: If True, raise error on undefined variables (default: True).
|
|
40
40
|
If False, render undefined variables as empty strings.
|
|
41
|
+
|
|
42
|
+
Note:
|
|
43
|
+
Changed to strict_undefined=True (v0.10.2+) for safer template rendering.
|
|
44
|
+
Variables must be explicitly provided to avoid silent template failures.
|
|
41
45
|
"""
|
|
42
46
|
self.strict_undefined = strict_undefined
|
|
43
47
|
self.undefined_behavior = StrictUndefined if strict_undefined else None
|
|
@@ -241,13 +245,24 @@ class TemplateVariableValidator:
|
|
|
241
245
|
if var_name not in variables:
|
|
242
246
|
errors.append(f"Missing required variable: {var_name}")
|
|
243
247
|
elif not isinstance(variables[var_name], var_type):
|
|
244
|
-
|
|
248
|
+
actual_type = type(variables[var_name]).__name__
|
|
249
|
+
errors.append(
|
|
250
|
+
f"Invalid type for {var_name}: "
|
|
251
|
+
f"expected {var_type.__name__}, got {actual_type}"
|
|
252
|
+
)
|
|
245
253
|
|
|
246
254
|
# Check optional variables (if present)
|
|
247
255
|
for var_name, var_type in cls.OPTIONAL_VARIABLES.items():
|
|
248
256
|
if var_name in variables:
|
|
249
257
|
if not isinstance(variables[var_name], var_type):
|
|
250
|
-
|
|
251
|
-
|
|
258
|
+
if isinstance(var_type, tuple):
|
|
259
|
+
type_names = " or ".join(t.__name__ for t in var_type)
|
|
260
|
+
else:
|
|
261
|
+
type_names = var_type.__name__
|
|
262
|
+
actual_type = type(variables[var_name]).__name__
|
|
263
|
+
errors.append(
|
|
264
|
+
f"Invalid type for {var_name}: "
|
|
265
|
+
f"expected {type_names}, got {actual_type}"
|
|
266
|
+
)
|
|
252
267
|
|
|
253
268
|
return len(errors) == 0, errors
|