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
moai_adk/cli/commands/init.py
CHANGED
|
@@ -167,7 +167,7 @@ def init(
|
|
|
167
167
|
else:
|
|
168
168
|
# Interactive mode: Simple notification
|
|
169
169
|
console.print("\n[cyan]🔄 Reinitializing project...[/cyan]")
|
|
170
|
-
console.print(" Backup will be created at .moai-backups/
|
|
170
|
+
console.print(" Backup will be created at .moai-backups/backup/\n")
|
|
171
171
|
|
|
172
172
|
# 5. Initialize project (Progress Bar with 5 phases)
|
|
173
173
|
# Always allow reinit (force mode by default)
|
|
@@ -175,6 +175,18 @@ def init(
|
|
|
175
175
|
|
|
176
176
|
# Reinit mode: set config.json optimized to false (v0.3.1+)
|
|
177
177
|
if is_reinit:
|
|
178
|
+
# Migration: Remove old hook files (Issue #163)
|
|
179
|
+
old_hook_files = [
|
|
180
|
+
".claude/hooks/alfred/session_start__startup.py", # v0.8.0 deprecated
|
|
181
|
+
]
|
|
182
|
+
for old_file in old_hook_files:
|
|
183
|
+
old_path = project_path / old_file
|
|
184
|
+
if old_path.exists():
|
|
185
|
+
try:
|
|
186
|
+
old_path.unlink() # Remove old file
|
|
187
|
+
except Exception:
|
|
188
|
+
pass # Ignore removal failures
|
|
189
|
+
|
|
178
190
|
config_path = project_path / ".moai" / "config.json"
|
|
179
191
|
if config_path.exists():
|
|
180
192
|
try:
|
|
@@ -255,7 +267,7 @@ def init(
|
|
|
255
267
|
if is_reinit:
|
|
256
268
|
console.print("\n[yellow]⚠️ Configuration Notice:[/yellow]")
|
|
257
269
|
console.print(" All template files have been [bold]force overwritten[/bold]")
|
|
258
|
-
console.print(" Previous files are backed up in [cyan].moai-backups/
|
|
270
|
+
console.print(" Previous files are backed up in [cyan].moai-backups/backup/[/cyan]")
|
|
259
271
|
console.print("\n [cyan]To merge your previous config:[/cyan]")
|
|
260
272
|
console.print(" Run [bold]/alfred:0-project[/bold] command in Claude Code")
|
|
261
273
|
console.print(" It will merge backup config when [dim]optimized=false[/dim]\n")
|
moai_adk/cli/commands/update.py
CHANGED
|
@@ -37,9 +37,11 @@ Includes:
|
|
|
37
37
|
- Use backup to restore previous state
|
|
38
38
|
- Debug with `python -m moai_adk doctor --verbose`
|
|
39
39
|
"""
|
|
40
|
+
|
|
40
41
|
from __future__ import annotations
|
|
41
42
|
|
|
42
43
|
import json
|
|
44
|
+
import logging
|
|
43
45
|
import subprocess
|
|
44
46
|
from datetime import datetime
|
|
45
47
|
from pathlib import Path
|
|
@@ -53,6 +55,7 @@ from moai_adk import __version__
|
|
|
53
55
|
from moai_adk.core.template.processor import TemplateProcessor
|
|
54
56
|
|
|
55
57
|
console = Console()
|
|
58
|
+
logger = logging.getLogger(__name__)
|
|
56
59
|
|
|
57
60
|
# Constants for tool detection
|
|
58
61
|
TOOL_DETECTION_TIMEOUT = 5 # seconds
|
|
@@ -65,26 +68,31 @@ PIP_COMMAND = ["pip", "install", "--upgrade", "moai-adk"]
|
|
|
65
68
|
# Custom exceptions for better error handling
|
|
66
69
|
class UpdateError(Exception):
|
|
67
70
|
"""Base exception for update operations."""
|
|
71
|
+
|
|
68
72
|
pass
|
|
69
73
|
|
|
70
74
|
|
|
71
75
|
class InstallerNotFoundError(UpdateError):
|
|
72
76
|
"""Raised when no package installer detected."""
|
|
77
|
+
|
|
73
78
|
pass
|
|
74
79
|
|
|
75
80
|
|
|
76
81
|
class NetworkError(UpdateError):
|
|
77
82
|
"""Raised when network operation fails."""
|
|
83
|
+
|
|
78
84
|
pass
|
|
79
85
|
|
|
80
86
|
|
|
81
87
|
class UpgradeError(UpdateError):
|
|
82
88
|
"""Raised when package upgrade fails."""
|
|
89
|
+
|
|
83
90
|
pass
|
|
84
91
|
|
|
85
92
|
|
|
86
93
|
class TemplateSyncError(UpdateError):
|
|
87
94
|
"""Raised when template sync fails."""
|
|
95
|
+
|
|
88
96
|
pass
|
|
89
97
|
|
|
90
98
|
|
|
@@ -96,11 +104,7 @@ def _is_installed_via_uv_tool() -> bool:
|
|
|
96
104
|
"""
|
|
97
105
|
try:
|
|
98
106
|
result = subprocess.run(
|
|
99
|
-
["uv", "tool", "list"],
|
|
100
|
-
capture_output=True,
|
|
101
|
-
text=True,
|
|
102
|
-
timeout=TOOL_DETECTION_TIMEOUT,
|
|
103
|
-
check=False
|
|
107
|
+
["uv", "tool", "list"], capture_output=True, text=True, timeout=TOOL_DETECTION_TIMEOUT, check=False
|
|
104
108
|
)
|
|
105
109
|
return result.returncode == 0 and "moai-adk" in result.stdout
|
|
106
110
|
except (FileNotFoundError, subprocess.TimeoutExpired, OSError):
|
|
@@ -115,11 +119,7 @@ def _is_installed_via_pipx() -> bool:
|
|
|
115
119
|
"""
|
|
116
120
|
try:
|
|
117
121
|
result = subprocess.run(
|
|
118
|
-
["pipx", "list"],
|
|
119
|
-
capture_output=True,
|
|
120
|
-
text=True,
|
|
121
|
-
timeout=TOOL_DETECTION_TIMEOUT,
|
|
122
|
-
check=False
|
|
122
|
+
["pipx", "list"], capture_output=True, text=True, timeout=TOOL_DETECTION_TIMEOUT, check=False
|
|
123
123
|
)
|
|
124
124
|
return result.returncode == 0 and "moai-adk" in result.stdout
|
|
125
125
|
except (FileNotFoundError, subprocess.TimeoutExpired, OSError):
|
|
@@ -134,11 +134,7 @@ def _is_installed_via_pip() -> bool:
|
|
|
134
134
|
"""
|
|
135
135
|
try:
|
|
136
136
|
result = subprocess.run(
|
|
137
|
-
["pip", "show", "moai-adk"],
|
|
138
|
-
capture_output=True,
|
|
139
|
-
text=True,
|
|
140
|
-
timeout=TOOL_DETECTION_TIMEOUT,
|
|
141
|
-
check=False
|
|
137
|
+
["pip", "show", "moai-adk"], capture_output=True, text=True, timeout=TOOL_DETECTION_TIMEOUT, check=False
|
|
142
138
|
)
|
|
143
139
|
return result.returncode == 0
|
|
144
140
|
except (FileNotFoundError, subprocess.TimeoutExpired, OSError):
|
|
@@ -274,6 +270,7 @@ def _get_project_config_version(project_path: Path) -> str:
|
|
|
274
270
|
Raises:
|
|
275
271
|
ValueError: If config.json exists but cannot be parsed
|
|
276
272
|
"""
|
|
273
|
+
|
|
277
274
|
def _is_placeholder(value: str) -> bool:
|
|
278
275
|
"""Check if value contains unsubstituted template placeholders."""
|
|
279
276
|
return isinstance(value, str) and value.startswith("{{") and value.endswith("}}")
|
|
@@ -302,6 +299,200 @@ def _get_project_config_version(project_path: Path) -> str:
|
|
|
302
299
|
raise ValueError(f"Failed to parse project config.json: {e}") from e
|
|
303
300
|
|
|
304
301
|
|
|
302
|
+
# @CODE:UPDATE-CACHE-FIX-001-001-DETECT-STALE
|
|
303
|
+
def _detect_stale_cache(upgrade_output: str, current_version: str, latest_version: str) -> bool:
|
|
304
|
+
"""
|
|
305
|
+
Detect if uv cache is stale by comparing versions.
|
|
306
|
+
|
|
307
|
+
A stale cache occurs when PyPI metadata is outdated, causing uv to incorrectly
|
|
308
|
+
report "Nothing to upgrade" even though a newer version exists. This function
|
|
309
|
+
detects this condition by:
|
|
310
|
+
1. Checking if upgrade output contains "Nothing to upgrade"
|
|
311
|
+
2. Verifying that latest version is actually newer than current version
|
|
312
|
+
|
|
313
|
+
Uses packaging.version.parse() for robust semantic version comparison that
|
|
314
|
+
handles pre-releases, dev versions, and other PEP 440 version formats correctly.
|
|
315
|
+
|
|
316
|
+
Args:
|
|
317
|
+
upgrade_output: Output from uv tool upgrade command
|
|
318
|
+
current_version: Currently installed version (string, e.g., "0.8.3")
|
|
319
|
+
latest_version: Latest version available on PyPI (string, e.g., "0.9.0")
|
|
320
|
+
|
|
321
|
+
Returns:
|
|
322
|
+
True if cache is stale (output shows "Nothing to upgrade" but current < latest),
|
|
323
|
+
False otherwise
|
|
324
|
+
|
|
325
|
+
Examples:
|
|
326
|
+
>>> _detect_stale_cache("Nothing to upgrade", "0.8.3", "0.9.0")
|
|
327
|
+
True
|
|
328
|
+
>>> _detect_stale_cache("Updated moai-adk", "0.8.3", "0.9.0")
|
|
329
|
+
False
|
|
330
|
+
>>> _detect_stale_cache("Nothing to upgrade", "0.9.0", "0.9.0")
|
|
331
|
+
False
|
|
332
|
+
"""
|
|
333
|
+
# Check if output indicates no upgrade needed
|
|
334
|
+
if not upgrade_output or "Nothing to upgrade" not in upgrade_output:
|
|
335
|
+
return False
|
|
336
|
+
|
|
337
|
+
# Compare versions using packaging.version
|
|
338
|
+
try:
|
|
339
|
+
current_v = version.parse(current_version)
|
|
340
|
+
latest_v = version.parse(latest_version)
|
|
341
|
+
return current_v < latest_v
|
|
342
|
+
except (version.InvalidVersion, TypeError) as e:
|
|
343
|
+
# Graceful degradation: if version parsing fails, assume cache is not stale
|
|
344
|
+
logger.debug(f"Version parsing failed: {e}")
|
|
345
|
+
return False
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
# @CODE:UPDATE-CACHE-FIX-001-002-CLEAR-SUCCESS
|
|
349
|
+
def _clear_uv_package_cache(package_name: str = "moai-adk") -> bool:
|
|
350
|
+
"""
|
|
351
|
+
Clear uv cache for specific package.
|
|
352
|
+
|
|
353
|
+
Executes `uv cache clean <package>` with 10-second timeout to prevent
|
|
354
|
+
hanging on network issues. Provides user-friendly error handling for
|
|
355
|
+
various failure scenarios (timeout, missing uv, etc.).
|
|
356
|
+
|
|
357
|
+
Args:
|
|
358
|
+
package_name: Package name to clear cache for (default: "moai-adk")
|
|
359
|
+
|
|
360
|
+
Returns:
|
|
361
|
+
True if cache cleared successfully, False otherwise
|
|
362
|
+
|
|
363
|
+
Exceptions:
|
|
364
|
+
- subprocess.TimeoutExpired: Logged as warning, returns False
|
|
365
|
+
- FileNotFoundError: Logged as warning, returns False
|
|
366
|
+
- Exception: Logged as warning, returns False
|
|
367
|
+
|
|
368
|
+
Examples:
|
|
369
|
+
>>> _clear_uv_package_cache("moai-adk")
|
|
370
|
+
True # If uv cache clean succeeds
|
|
371
|
+
"""
|
|
372
|
+
try:
|
|
373
|
+
result = subprocess.run(
|
|
374
|
+
["uv", "cache", "clean", package_name],
|
|
375
|
+
capture_output=True,
|
|
376
|
+
text=True,
|
|
377
|
+
timeout=10, # 10 second timeout
|
|
378
|
+
check=False,
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
if result.returncode == 0:
|
|
382
|
+
logger.debug(f"UV cache cleared for {package_name}")
|
|
383
|
+
return True
|
|
384
|
+
else:
|
|
385
|
+
logger.warning(f"Failed to clear UV cache: {result.stderr}")
|
|
386
|
+
return False
|
|
387
|
+
|
|
388
|
+
except subprocess.TimeoutExpired:
|
|
389
|
+
logger.warning(f"UV cache clean timed out for {package_name}")
|
|
390
|
+
return False
|
|
391
|
+
except FileNotFoundError:
|
|
392
|
+
logger.warning("UV command not found. Is uv installed?")
|
|
393
|
+
return False
|
|
394
|
+
except Exception as e:
|
|
395
|
+
logger.warning(f"Unexpected error clearing cache: {e}")
|
|
396
|
+
return False
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
# @CODE:UPDATE-CACHE-FIX-001-003-RETRY-LOGIC
|
|
400
|
+
def _execute_upgrade_with_retry(installer_cmd: list[str], package_name: str = "moai-adk") -> bool:
|
|
401
|
+
"""
|
|
402
|
+
Execute upgrade with automatic cache retry on stale detection.
|
|
403
|
+
|
|
404
|
+
Implements a robust 7-stage upgrade flow that handles PyPI cache staleness:
|
|
405
|
+
|
|
406
|
+
Stage 1: First upgrade attempt (up to 60 seconds)
|
|
407
|
+
Stage 2: Check success condition (returncode=0 AND no "Nothing to upgrade")
|
|
408
|
+
Stage 3: Detect stale cache using _detect_stale_cache()
|
|
409
|
+
Stage 4: Show user feedback if stale cache detected
|
|
410
|
+
Stage 5: Clear cache using _clear_uv_package_cache()
|
|
411
|
+
Stage 6: Retry upgrade with same command
|
|
412
|
+
Stage 7: Return final result (success or failure)
|
|
413
|
+
|
|
414
|
+
Retry Logic:
|
|
415
|
+
- Only ONE retry is performed to prevent infinite loops
|
|
416
|
+
- Retry only happens if stale cache is detected AND cache clear succeeds
|
|
417
|
+
- Cache clear failures are reported to user with manual workaround
|
|
418
|
+
|
|
419
|
+
User Feedback:
|
|
420
|
+
- Shows emoji-based status messages for each stage
|
|
421
|
+
- Clear guidance on manual workaround if automatic retry fails
|
|
422
|
+
- All errors logged at WARNING level for debugging
|
|
423
|
+
|
|
424
|
+
Args:
|
|
425
|
+
installer_cmd: Command list from _detect_tool_installer()
|
|
426
|
+
e.g., ["uv", "tool", "upgrade", "moai-adk"]
|
|
427
|
+
package_name: Package name for cache clearing (default: "moai-adk")
|
|
428
|
+
|
|
429
|
+
Returns:
|
|
430
|
+
True if upgrade succeeded (either first attempt or after retry),
|
|
431
|
+
False otherwise
|
|
432
|
+
|
|
433
|
+
Examples:
|
|
434
|
+
>>> # First attempt succeeds
|
|
435
|
+
>>> _execute_upgrade_with_retry(["uv", "tool", "upgrade", "moai-adk"])
|
|
436
|
+
True
|
|
437
|
+
|
|
438
|
+
>>> # First attempt stale, retry succeeds
|
|
439
|
+
>>> _execute_upgrade_with_retry(["uv", "tool", "upgrade", "moai-adk"])
|
|
440
|
+
True # After cache clear and retry
|
|
441
|
+
|
|
442
|
+
Raises:
|
|
443
|
+
subprocess.TimeoutExpired: Re-raised if upgrade command times out
|
|
444
|
+
"""
|
|
445
|
+
# Stage 1: First upgrade attempt
|
|
446
|
+
try:
|
|
447
|
+
result = subprocess.run(installer_cmd, capture_output=True, text=True, timeout=60, check=False)
|
|
448
|
+
except subprocess.TimeoutExpired:
|
|
449
|
+
raise # Re-raise timeout for caller to handle
|
|
450
|
+
except Exception:
|
|
451
|
+
return False
|
|
452
|
+
|
|
453
|
+
# Stage 2: Check if upgrade succeeded without stale cache
|
|
454
|
+
if result.returncode == 0 and "Nothing to upgrade" not in result.stdout:
|
|
455
|
+
return True
|
|
456
|
+
|
|
457
|
+
# Stage 3: Detect stale cache
|
|
458
|
+
try:
|
|
459
|
+
current_version = _get_current_version()
|
|
460
|
+
latest_version = _get_latest_version()
|
|
461
|
+
except RuntimeError:
|
|
462
|
+
# If version check fails, return original result
|
|
463
|
+
return result.returncode == 0
|
|
464
|
+
|
|
465
|
+
if _detect_stale_cache(result.stdout, current_version, latest_version):
|
|
466
|
+
# Stage 4: User feedback
|
|
467
|
+
console.print("[yellow]⚠️ Cache outdated, refreshing...[/yellow]")
|
|
468
|
+
|
|
469
|
+
# Stage 5: Clear cache
|
|
470
|
+
if _clear_uv_package_cache(package_name):
|
|
471
|
+
console.print("[cyan]♻️ Cache cleared, retrying upgrade...[/cyan]")
|
|
472
|
+
|
|
473
|
+
# Stage 6: Retry upgrade
|
|
474
|
+
try:
|
|
475
|
+
result = subprocess.run(installer_cmd, capture_output=True, text=True, timeout=60, check=False)
|
|
476
|
+
|
|
477
|
+
if result.returncode == 0:
|
|
478
|
+
return True
|
|
479
|
+
else:
|
|
480
|
+
console.print("[red]✗ Upgrade failed after retry[/red]")
|
|
481
|
+
return False
|
|
482
|
+
except subprocess.TimeoutExpired:
|
|
483
|
+
raise # Re-raise timeout
|
|
484
|
+
except Exception:
|
|
485
|
+
return False
|
|
486
|
+
else:
|
|
487
|
+
# Cache clear failed
|
|
488
|
+
console.print("[red]✗ Cache clear failed. Manual workaround:[/red]")
|
|
489
|
+
console.print(" [cyan]uv cache clean moai-adk && moai-adk update[/cyan]")
|
|
490
|
+
return False
|
|
491
|
+
|
|
492
|
+
# Stage 7: Cache is not stale, return original result
|
|
493
|
+
return result.returncode == 0
|
|
494
|
+
|
|
495
|
+
|
|
305
496
|
def _execute_upgrade(installer_cmd: list[str]) -> bool:
|
|
306
497
|
"""Execute package upgrade using detected installer.
|
|
307
498
|
|
|
@@ -316,13 +507,7 @@ def _execute_upgrade(installer_cmd: list[str]) -> bool:
|
|
|
316
507
|
subprocess.TimeoutExpired: If upgrade times out
|
|
317
508
|
"""
|
|
318
509
|
try:
|
|
319
|
-
result = subprocess.run(
|
|
320
|
-
installer_cmd,
|
|
321
|
-
capture_output=True,
|
|
322
|
-
text=True,
|
|
323
|
-
timeout=60,
|
|
324
|
-
check=False
|
|
325
|
-
)
|
|
510
|
+
result = subprocess.run(installer_cmd, capture_output=True, text=True, timeout=60, check=False)
|
|
326
511
|
return result.returncode == 0
|
|
327
512
|
except subprocess.TimeoutExpired:
|
|
328
513
|
raise # Re-raise timeout for caller to handle
|
|
@@ -395,10 +580,7 @@ def set_optimized_false(project_path: Path) -> None:
|
|
|
395
580
|
try:
|
|
396
581
|
config_data = json.loads(config_path.read_text(encoding="utf-8"))
|
|
397
582
|
config_data.setdefault("project", {})["optimized"] = False
|
|
398
|
-
config_path.write_text(
|
|
399
|
-
json.dumps(config_data, indent=2, ensure_ascii=False) + "\n",
|
|
400
|
-
encoding="utf-8"
|
|
401
|
-
)
|
|
583
|
+
config_path.write_text(json.dumps(config_data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
|
|
402
584
|
except (json.JSONDecodeError, KeyError):
|
|
403
585
|
# Ignore errors if config.json is invalid
|
|
404
586
|
pass
|
|
@@ -536,10 +718,7 @@ def _preserve_project_metadata(
|
|
|
536
718
|
# This allows Stage 2 to compare package vs project template versions
|
|
537
719
|
project_data["template_version"] = version_for_config
|
|
538
720
|
|
|
539
|
-
config_path.write_text(
|
|
540
|
-
json.dumps(config_data, indent=2, ensure_ascii=False) + "\n",
|
|
541
|
-
encoding="utf-8"
|
|
542
|
-
)
|
|
721
|
+
config_path.write_text(json.dumps(config_data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
|
|
543
722
|
|
|
544
723
|
|
|
545
724
|
def _apply_context_to_file(processor: TemplateProcessor, target_path: Path) -> None:
|
|
@@ -629,32 +808,11 @@ def _show_timeout_error_help() -> None:
|
|
|
629
808
|
|
|
630
809
|
|
|
631
810
|
@click.command()
|
|
632
|
-
@click.option(
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
)
|
|
638
|
-
@click.option(
|
|
639
|
-
"--force",
|
|
640
|
-
is_flag=True,
|
|
641
|
-
help="Skip backup and force the update"
|
|
642
|
-
)
|
|
643
|
-
@click.option(
|
|
644
|
-
"--check",
|
|
645
|
-
is_flag=True,
|
|
646
|
-
help="Only check version (do not update)"
|
|
647
|
-
)
|
|
648
|
-
@click.option(
|
|
649
|
-
"--templates-only",
|
|
650
|
-
is_flag=True,
|
|
651
|
-
help="Skip package upgrade, sync templates only"
|
|
652
|
-
)
|
|
653
|
-
@click.option(
|
|
654
|
-
"--yes",
|
|
655
|
-
is_flag=True,
|
|
656
|
-
help="Auto-confirm all prompts (CI/CD mode)"
|
|
657
|
-
)
|
|
811
|
+
@click.option("--path", type=click.Path(exists=True), default=".", help="Project path (default: current directory)")
|
|
812
|
+
@click.option("--force", is_flag=True, help="Skip backup and force the update")
|
|
813
|
+
@click.option("--check", is_flag=True, help="Only check version (do not update)")
|
|
814
|
+
@click.option("--templates-only", is_flag=True, help="Skip package upgrade, sync templates only")
|
|
815
|
+
@click.option("--yes", is_flag=True, help="Auto-confirm all prompts (CI/CD mode)")
|
|
658
816
|
def update(path: str, force: bool, check: bool, templates_only: bool, yes: bool) -> None:
|
|
659
817
|
"""Update command with 3-stage workflow (v0.6.3+).
|
|
660
818
|
|
moai_adk/core/issue_creator.py
CHANGED
|
@@ -59,7 +59,7 @@ class GitHubIssueCreator:
|
|
|
59
59
|
IssueType.BUG: ["bug", "reported"],
|
|
60
60
|
IssueType.FEATURE: ["feature-request", "enhancement"],
|
|
61
61
|
IssueType.IMPROVEMENT: ["improvement", "enhancement"],
|
|
62
|
-
IssueType.QUESTION: ["question", "help
|
|
62
|
+
IssueType.QUESTION: ["question", "help wanted"], # Fixed: "help-wanted" → "help wanted" (GitHub standard)
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
# Priority emoji
|
|
@@ -145,7 +145,7 @@ class GitHubIssueCreator:
|
|
|
145
145
|
# Collect labels
|
|
146
146
|
labels = self.LABEL_MAP.get(config.issue_type, []).copy()
|
|
147
147
|
if config.priority:
|
|
148
|
-
labels.append(
|
|
148
|
+
labels.append(config.priority.value) # Fixed: removed "priority-" prefix (use direct label names)
|
|
149
149
|
if config.category:
|
|
150
150
|
labels.append(f"category-{config.category.lower().replace(' ', '-')}")
|
|
151
151
|
if config.custom_labels:
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
# @CODE:CORE-PROJECT-001 | SPEC: SPEC-CORE-PROJECT-001.md | TEST: tests/unit/test_language_detector.py
|
|
2
2
|
# @CODE:LANG-DETECT-001 | SPEC: SPEC-LANG-DETECT-001.md | TEST: tests/unit/test_detector.py
|
|
3
|
+
# @CODE:LDE-EXTENDED-001 | SPEC: SPEC-LANGUAGE-DETECTION-EXTENDED-001/spec.md | TEST: tests/unit/test_language_detector_extended.py
|
|
3
4
|
"""Language detector module.
|
|
4
5
|
|
|
5
6
|
Automatically detects 20 programming languages.
|
|
7
|
+
|
|
8
|
+
Extended detection supports:
|
|
9
|
+
- 11 new languages: Ruby, PHP, Java, Rust, Dart, Swift, Kotlin, C#, C, C++, Shell
|
|
10
|
+
- 5 build tool detection: Maven, Gradle, CMake, SPM, dotnet
|
|
11
|
+
- Package manager detection: bundle, composer, cargo
|
|
12
|
+
- Priority conflict resolution for multi-language projects
|
|
6
13
|
"""
|
|
7
14
|
|
|
8
15
|
from pathlib import Path
|
|
@@ -16,7 +23,18 @@ class LanguageDetector:
|
|
|
16
23
|
"""
|
|
17
24
|
|
|
18
25
|
LANGUAGE_PATTERNS = {
|
|
19
|
-
#
|
|
26
|
+
# @CODE:LDE-PRIORITY-001 | SPEC: SPEC-LANGUAGE-DETECTION-EXTENDED-001/spec.md
|
|
27
|
+
# Priority order (highest to lowest):
|
|
28
|
+
# 1. Rust, 2. Dart, 3. Swift, 4. Kotlin, 5. C#, 6. Java, 7. Ruby, 8. PHP
|
|
29
|
+
# 9. Go, 10. Python, 11. TypeScript, 12. JavaScript, 13. C++, 14. C, 15. Shell
|
|
30
|
+
|
|
31
|
+
"rust": ["*.rs", "Cargo.toml"],
|
|
32
|
+
"dart": ["*.dart", "pubspec.yaml"],
|
|
33
|
+
"swift": ["*.swift", "Package.swift"],
|
|
34
|
+
"kotlin": ["*.kt", "build.gradle.kts"],
|
|
35
|
+
"csharp": ["*.cs", "*.csproj"],
|
|
36
|
+
"java": ["*.java", "pom.xml", "build.gradle"],
|
|
37
|
+
# Ruby moved for priority (Rails detection)
|
|
20
38
|
# @CODE:LANG-DETECT-RUBY-001 | SPEC: Issue #51 Language Detection Fix
|
|
21
39
|
"ruby": [
|
|
22
40
|
"*.rb",
|
|
@@ -26,7 +44,7 @@ class LanguageDetector:
|
|
|
26
44
|
"app/controllers/", # Rails: controller directory
|
|
27
45
|
"Rakefile" # Rails/Ruby: task file
|
|
28
46
|
],
|
|
29
|
-
# PHP
|
|
47
|
+
# PHP after Ruby (Laravel detection)
|
|
30
48
|
"php": [
|
|
31
49
|
"*.php",
|
|
32
50
|
"composer.json",
|
|
@@ -34,23 +52,18 @@ class LanguageDetector:
|
|
|
34
52
|
"app/", # Laravel: application directory
|
|
35
53
|
"bootstrap/laravel.php" # Laravel: bootstrap file
|
|
36
54
|
],
|
|
55
|
+
"go": ["*.go", "go.mod"],
|
|
37
56
|
"python": ["*.py", "pyproject.toml", "requirements.txt", "setup.py"],
|
|
38
57
|
"typescript": ["*.ts", "tsconfig.json"],
|
|
39
58
|
"javascript": ["*.js", "package.json"],
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
|
|
44
|
-
"swift": ["*.swift", "Package.swift"],
|
|
45
|
-
"kotlin": ["*.kt", "build.gradle.kts"],
|
|
46
|
-
"csharp": ["*.cs", "*.csproj"],
|
|
59
|
+
"cpp": ["*.cpp", "CMakeLists.txt"],
|
|
60
|
+
"c": ["*.c", "Makefile"],
|
|
61
|
+
"shell": ["*.sh", "*.bash"],
|
|
62
|
+
# Additional languages (lower priority)
|
|
47
63
|
"elixir": ["*.ex", "mix.exs"],
|
|
48
64
|
"scala": ["*.scala", "build.sbt"],
|
|
49
65
|
"clojure": ["*.clj", "project.clj"],
|
|
50
66
|
"haskell": ["*.hs", "*.cabal"],
|
|
51
|
-
"c": ["*.c", "Makefile"],
|
|
52
|
-
"cpp": ["*.cpp", "CMakeLists.txt"],
|
|
53
|
-
"shell": ["*.sh", "*.bash"],
|
|
54
67
|
"lua": ["*.lua"],
|
|
55
68
|
}
|
|
56
69
|
|
|
@@ -112,6 +125,182 @@ class LanguageDetector:
|
|
|
112
125
|
|
|
113
126
|
return False
|
|
114
127
|
|
|
128
|
+
def get_workflow_template_path(self, language: str) -> str:
|
|
129
|
+
"""Get the GitHub Actions workflow template path for a language.
|
|
130
|
+
|
|
131
|
+
@CODE:LDE-WORKFLOW-PATH-001 | SPEC: SPEC-LANGUAGE-DETECTION-EXTENDED-001/spec.md
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
language: Programming language name (lowercase).
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
Workflow template file path relative to templates directory.
|
|
138
|
+
|
|
139
|
+
Raises:
|
|
140
|
+
ValueError: If language is not supported for workflows.
|
|
141
|
+
"""
|
|
142
|
+
workflow_mapping = {
|
|
143
|
+
"python": ".github/workflows/python-tag-validation.yml",
|
|
144
|
+
"javascript": ".github/workflows/javascript-tag-validation.yml",
|
|
145
|
+
"typescript": ".github/workflows/typescript-tag-validation.yml",
|
|
146
|
+
"go": ".github/workflows/go-tag-validation.yml",
|
|
147
|
+
"ruby": ".github/workflows/ruby-tag-validation.yml",
|
|
148
|
+
"php": ".github/workflows/php-tag-validation.yml",
|
|
149
|
+
"java": ".github/workflows/java-tag-validation.yml",
|
|
150
|
+
"rust": ".github/workflows/rust-tag-validation.yml",
|
|
151
|
+
"dart": ".github/workflows/dart-tag-validation.yml",
|
|
152
|
+
"swift": ".github/workflows/swift-tag-validation.yml",
|
|
153
|
+
"kotlin": ".github/workflows/kotlin-tag-validation.yml",
|
|
154
|
+
"csharp": ".github/workflows/csharp-tag-validation.yml",
|
|
155
|
+
"c": ".github/workflows/c-tag-validation.yml",
|
|
156
|
+
"cpp": ".github/workflows/cpp-tag-validation.yml",
|
|
157
|
+
"shell": ".github/workflows/shell-tag-validation.yml",
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if language.lower() not in workflow_mapping:
|
|
161
|
+
raise ValueError(
|
|
162
|
+
f"Unsupported language: {language}. "
|
|
163
|
+
f"Supported languages: {', '.join(workflow_mapping.keys())}"
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
return workflow_mapping[language.lower()]
|
|
167
|
+
|
|
168
|
+
def detect_package_manager(self, path: str | Path = ".") -> str | None:
|
|
169
|
+
"""Detect the package manager for the detected language.
|
|
170
|
+
|
|
171
|
+
@CODE:LDE-PKG-MGR-001 | SPEC: SPEC-LANGUAGE-DETECTION-EXTENDED-001/spec.md
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
path: Directory to inspect.
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
Package manager name or None if not detected.
|
|
178
|
+
"""
|
|
179
|
+
path = Path(path)
|
|
180
|
+
|
|
181
|
+
# Ruby
|
|
182
|
+
if (path / "Gemfile").exists():
|
|
183
|
+
return "bundle"
|
|
184
|
+
|
|
185
|
+
# PHP
|
|
186
|
+
if (path / "composer.json").exists():
|
|
187
|
+
return "composer"
|
|
188
|
+
|
|
189
|
+
# Java/Kotlin
|
|
190
|
+
if (path / "pom.xml").exists():
|
|
191
|
+
return "maven"
|
|
192
|
+
if (path / "build.gradle").exists() or (path / "build.gradle.kts").exists():
|
|
193
|
+
return "gradle"
|
|
194
|
+
|
|
195
|
+
# Rust
|
|
196
|
+
if (path / "Cargo.toml").exists():
|
|
197
|
+
return "cargo"
|
|
198
|
+
|
|
199
|
+
# Dart/Flutter
|
|
200
|
+
if (path / "pubspec.yaml").exists():
|
|
201
|
+
return "dart_pub"
|
|
202
|
+
|
|
203
|
+
# Swift
|
|
204
|
+
if (path / "Package.swift").exists():
|
|
205
|
+
return "spm"
|
|
206
|
+
|
|
207
|
+
# C#
|
|
208
|
+
if list(path.glob("*.csproj")) or list(path.glob("*.sln")):
|
|
209
|
+
return "dotnet"
|
|
210
|
+
|
|
211
|
+
# Python
|
|
212
|
+
if (path / "pyproject.toml").exists():
|
|
213
|
+
return "pip"
|
|
214
|
+
|
|
215
|
+
# JavaScript/TypeScript (check in priority order)
|
|
216
|
+
# Check for lock files and package managers
|
|
217
|
+
if (path / "bun.lockb").exists():
|
|
218
|
+
return "bun"
|
|
219
|
+
elif (path / "pnpm-lock.yaml").exists():
|
|
220
|
+
return "pnpm"
|
|
221
|
+
elif (path / "yarn.lock").exists():
|
|
222
|
+
return "yarn"
|
|
223
|
+
elif (path / "package-lock.json").exists():
|
|
224
|
+
return "npm"
|
|
225
|
+
elif (path / "package.json").exists():
|
|
226
|
+
# Default to npm for package.json without lock files
|
|
227
|
+
return "npm"
|
|
228
|
+
|
|
229
|
+
# Go
|
|
230
|
+
if (path / "go.mod").exists():
|
|
231
|
+
return "go_modules"
|
|
232
|
+
|
|
233
|
+
return None
|
|
234
|
+
|
|
235
|
+
def detect_build_tool(self, path: str | Path = ".", language: str | None = None) -> str | None:
|
|
236
|
+
"""Detect the build tool for the detected language.
|
|
237
|
+
|
|
238
|
+
@CODE:LDE-BUILD-TOOL-001 | SPEC: SPEC-LANGUAGE-DETECTION-EXTENDED-001/spec.md
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
path: Directory to inspect.
|
|
242
|
+
language: Optional language hint for disambiguation.
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
Build tool name or None if not detected.
|
|
246
|
+
"""
|
|
247
|
+
path = Path(path)
|
|
248
|
+
|
|
249
|
+
# C/C++
|
|
250
|
+
if (path / "CMakeLists.txt").exists():
|
|
251
|
+
return "cmake"
|
|
252
|
+
if (path / "Makefile").exists():
|
|
253
|
+
return "make"
|
|
254
|
+
|
|
255
|
+
# Java/Kotlin
|
|
256
|
+
if language in ["java", "kotlin"]:
|
|
257
|
+
if (path / "pom.xml").exists():
|
|
258
|
+
return "maven"
|
|
259
|
+
if (path / "build.gradle").exists() or (path / "build.gradle.kts").exists():
|
|
260
|
+
return "gradle"
|
|
261
|
+
|
|
262
|
+
# Rust
|
|
263
|
+
if (path / "Cargo.toml").exists():
|
|
264
|
+
return "cargo"
|
|
265
|
+
|
|
266
|
+
# Swift
|
|
267
|
+
if (path / "Package.swift").exists():
|
|
268
|
+
return "spm"
|
|
269
|
+
if list(path.glob("*.xcodeproj")) or list(path.glob("*.xcworkspace")):
|
|
270
|
+
return "xcode"
|
|
271
|
+
|
|
272
|
+
# C#
|
|
273
|
+
if list(path.glob("*.csproj")) or list(path.glob("*.sln")):
|
|
274
|
+
return "dotnet"
|
|
275
|
+
|
|
276
|
+
return None
|
|
277
|
+
|
|
278
|
+
def get_supported_languages_for_workflows(self) -> list[str]:
|
|
279
|
+
"""Get the list of languages with dedicated CI/CD workflow support.
|
|
280
|
+
|
|
281
|
+
@CODE:LDE-SUPPORTED-LANGS-001 | SPEC: SPEC-LANGUAGE-DETECTION-EXTENDED-001/spec.md
|
|
282
|
+
|
|
283
|
+
Returns:
|
|
284
|
+
List of supported language names (15 total).
|
|
285
|
+
"""
|
|
286
|
+
return [
|
|
287
|
+
"python",
|
|
288
|
+
"javascript",
|
|
289
|
+
"typescript",
|
|
290
|
+
"go",
|
|
291
|
+
"ruby",
|
|
292
|
+
"php",
|
|
293
|
+
"java",
|
|
294
|
+
"rust",
|
|
295
|
+
"dart",
|
|
296
|
+
"swift",
|
|
297
|
+
"kotlin",
|
|
298
|
+
"csharp",
|
|
299
|
+
"c",
|
|
300
|
+
"cpp",
|
|
301
|
+
"shell",
|
|
302
|
+
]
|
|
303
|
+
|
|
115
304
|
|
|
116
305
|
def detect_project_language(path: str | Path = ".") -> str | None:
|
|
117
306
|
"""Detect the project language (helper).
|