moai-adk 0.7.0__py3-none-any.whl → 0.8.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/core/issue_creator.py +309 -0
- moai_adk/core/project/phase_executor.py +1 -2
- moai_adk/core/template_engine.py +253 -0
- moai_adk/templates/.claude/agents/alfred/cc-manager.md +1 -1
- moai_adk/templates/.claude/agents/alfred/debug-helper.md +2 -2
- moai_adk/templates/.claude/agents/alfred/doc-syncer.md +2 -2
- moai_adk/templates/.claude/agents/alfred/git-manager.md +27 -4
- moai_adk/templates/.claude/agents/alfred/implementation-planner.md +2 -2
- moai_adk/templates/.claude/agents/alfred/project-manager.md +6 -6
- moai_adk/templates/.claude/agents/alfred/quality-gate.md +2 -2
- moai_adk/templates/.claude/agents/alfred/skill-factory.md +7 -7
- moai_adk/templates/.claude/agents/alfred/spec-builder.md +2 -2
- moai_adk/templates/.claude/agents/alfred/tag-agent.md +2 -2
- moai_adk/templates/.claude/agents/alfred/tdd-implementer.md +2 -2
- moai_adk/templates/.claude/agents/alfred/trust-checker.md +2 -2
- moai_adk/templates/.claude/commands/alfred/0-project.md +9 -9
- moai_adk/templates/.claude/commands/alfred/1-plan.md +3 -3
- moai_adk/templates/.claude/commands/alfred/2-run.md +4 -4
- moai_adk/templates/.claude/commands/alfred/3-sync.md +5 -5
- moai_adk/templates/.claude/commands/alfred/9-feedback.md +149 -0
- moai_adk/templates/.claude/hooks/alfred/core/project.py +145 -13
- moai_adk/templates/.claude/hooks/alfred/handlers/session.py +90 -20
- moai_adk/templates/.claude/output-styles/alfred/agentic-coding.md +1 -1
- moai_adk/templates/.claude/output-styles/alfred/moai-adk-learning.md +1 -1
- moai_adk/templates/.claude/output-styles/alfred/study-with-alfred.md +1 -1
- moai_adk/templates/.github/ISSUE_TEMPLATE/spec.yml +5 -3
- moai_adk/templates/.github/PULL_REQUEST_TEMPLATE.md +20 -8
- moai_adk/templates/.github/workflows/moai-gitflow.yml +22 -16
- moai_adk/templates/.github/workflows/spec-issue-sync.yml +10 -6
- moai_adk/templates/.moai/config.json +12 -0
- moai_adk/templates/.moai/docs/quick-issue-creation-guide.md +219 -0
- moai_adk/templates/.moai/memory/issue-label-mapping.md +150 -0
- moai_adk/templates/CLAUDE.md +67 -1
- {moai_adk-0.7.0.dist-info → moai_adk-0.8.1.dist-info}/METADATA +123 -1
- {moai_adk-0.7.0.dist-info → moai_adk-0.8.1.dist-info}/RECORD +38 -34
- moai_adk/templates/.claude/hooks/alfred/test_hook_output.py +0 -175
- {moai_adk-0.7.0.dist-info → moai_adk-0.8.1.dist-info}/WHEEL +0 -0
- {moai_adk-0.7.0.dist-info → moai_adk-0.8.1.dist-info}/entry_points.txt +0 -0
- {moai_adk-0.7.0.dist-info → moai_adk-0.8.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -5,11 +5,44 @@ Project information inquiry (language, Git, SPEC progress, etc.)
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import json
|
|
8
|
+
import signal
|
|
8
9
|
import subprocess
|
|
10
|
+
from contextlib import contextmanager
|
|
9
11
|
from pathlib import Path
|
|
10
12
|
from typing import Any
|
|
11
13
|
|
|
12
14
|
|
|
15
|
+
class TimeoutError(Exception):
|
|
16
|
+
"""Signal-based timeout exception"""
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@contextmanager
|
|
21
|
+
def timeout_handler(seconds: int):
|
|
22
|
+
"""Hard timeout using SIGALRM (works on Unix systems including macOS)
|
|
23
|
+
|
|
24
|
+
This uses kernel-level signal to interrupt ANY blocking operation,
|
|
25
|
+
even if subprocess.run() timeout fails on macOS.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
seconds: Timeout duration in seconds
|
|
29
|
+
|
|
30
|
+
Raises:
|
|
31
|
+
TimeoutError: If operation exceeds timeout
|
|
32
|
+
"""
|
|
33
|
+
def _handle_timeout(signum, frame):
|
|
34
|
+
raise TimeoutError(f"Operation timed out after {seconds} seconds")
|
|
35
|
+
|
|
36
|
+
# Set the signal handler
|
|
37
|
+
old_handler = signal.signal(signal.SIGALRM, _handle_timeout)
|
|
38
|
+
signal.alarm(seconds)
|
|
39
|
+
try:
|
|
40
|
+
yield
|
|
41
|
+
finally:
|
|
42
|
+
signal.alarm(0) # Disable alarm
|
|
43
|
+
signal.signal(signal.SIGALRM, old_handler)
|
|
44
|
+
|
|
45
|
+
|
|
13
46
|
def detect_language(cwd: str) -> str:
|
|
14
47
|
"""Detect project language (supports 20 items languages)
|
|
15
48
|
|
|
@@ -89,9 +122,10 @@ def detect_language(cwd: str) -> str:
|
|
|
89
122
|
|
|
90
123
|
|
|
91
124
|
def _run_git_command(args: list[str], cwd: str, timeout: int = 2) -> str:
|
|
92
|
-
"""Git command execution
|
|
125
|
+
"""Git command execution with HARD timeout protection
|
|
93
126
|
|
|
94
127
|
Safely execute Git commands and return output.
|
|
128
|
+
Uses SIGALRM (kernel-level interrupt) to handle macOS subprocess timeout bug.
|
|
95
129
|
Eliminates code duplication and provides consistent error handling.
|
|
96
130
|
|
|
97
131
|
Args:
|
|
@@ -103,29 +137,46 @@ def _run_git_command(args: list[str], cwd: str, timeout: int = 2) -> str:
|
|
|
103
137
|
Git command output (stdout, removing leading and trailing spaces)
|
|
104
138
|
|
|
105
139
|
Raises:
|
|
106
|
-
subprocess.TimeoutExpired: Timeout exceeded
|
|
140
|
+
subprocess.TimeoutExpired: Timeout exceeded (via TimeoutError)
|
|
107
141
|
subprocess.CalledProcessError: Git command failed
|
|
108
142
|
|
|
109
143
|
Examples:
|
|
110
144
|
>>> _run_git_command(["branch", "--show-current"], ".")
|
|
111
145
|
'main'
|
|
146
|
+
|
|
147
|
+
TDD History:
|
|
148
|
+
- RED: Git command hang scenario test
|
|
149
|
+
- GREEN: SIGALRM-based timeout implementation
|
|
150
|
+
- REFACTOR: Exception conversion to subprocess.TimeoutExpired
|
|
112
151
|
"""
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
152
|
+
try:
|
|
153
|
+
with timeout_handler(timeout):
|
|
154
|
+
result = subprocess.run(
|
|
155
|
+
["git"] + args,
|
|
156
|
+
cwd=cwd,
|
|
157
|
+
capture_output=True,
|
|
158
|
+
text=True,
|
|
159
|
+
check=False, # Don't raise on non-zero exit - we'll check manually
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
# Check exit code manually
|
|
163
|
+
if result.returncode != 0:
|
|
164
|
+
raise subprocess.CalledProcessError(
|
|
165
|
+
result.returncode, ["git"] + args, result.stdout, result.stderr
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
return result.stdout.strip()
|
|
169
|
+
|
|
170
|
+
except TimeoutError:
|
|
171
|
+
# Convert to subprocess.TimeoutExpired for consistent error handling
|
|
172
|
+
raise subprocess.TimeoutExpired(["git"] + args, timeout)
|
|
122
173
|
|
|
123
174
|
|
|
124
175
|
def get_git_info(cwd: str) -> dict[str, Any]:
|
|
125
176
|
"""Gather Git repository information
|
|
126
177
|
|
|
127
178
|
View the current status of a Git repository.
|
|
128
|
-
Returns the branch name, commit hash,
|
|
179
|
+
Returns the branch name, commit hash, number of changes, and last commit message.
|
|
129
180
|
If it is not a Git repository, it returns an empty dictionary.
|
|
130
181
|
|
|
131
182
|
Args:
|
|
@@ -136,12 +187,13 @@ def get_git_info(cwd: str) -> dict[str, Any]:
|
|
|
136
187
|
- branch: Current branch name (str)
|
|
137
188
|
- commit: Current commit hash (str, full hash)
|
|
138
189
|
- changes: Number of changed files (int, staged + unstaged)
|
|
190
|
+
- last_commit: Last commit message (str, subject only)
|
|
139
191
|
|
|
140
192
|
Empty dictionary {} if it is not a Git repository or the query fails.
|
|
141
193
|
|
|
142
194
|
Examples:
|
|
143
195
|
>>> get_git_info("/path/to/git/repo")
|
|
144
|
-
{'branch': 'main', 'commit': 'abc123...', 'changes': 3}
|
|
196
|
+
{'branch': 'main', 'commit': 'abc123...', 'changes': 3, 'last_commit': 'Fix bug'}
|
|
145
197
|
>>> get_git_info("/path/to/non-git")
|
|
146
198
|
{}
|
|
147
199
|
|
|
@@ -149,11 +201,13 @@ def get_git_info(cwd: str) -> dict[str, Any]:
|
|
|
149
201
|
- Timeout: 2 seconds for each Git command
|
|
150
202
|
- Security: Safe execution with subprocess.run(shell=False)
|
|
151
203
|
- Error handling: Returns an empty dictionary in case of all exceptions
|
|
204
|
+
- Commit message limited to 50 characters for display purposes
|
|
152
205
|
|
|
153
206
|
TDD History:
|
|
154
207
|
- RED: 3 items scenario test (Git repo, non-Git, error)
|
|
155
208
|
- GREEN: Implementation of subprocess-based Git command execution
|
|
156
209
|
- REFACTOR: Add timeout (2 seconds), strengthen exception handling, remove duplicates with helper function
|
|
210
|
+
- UPDATE: Added last_commit message field for SessionStart display
|
|
157
211
|
"""
|
|
158
212
|
try:
|
|
159
213
|
# Check if it's a git repository
|
|
@@ -165,10 +219,16 @@ def get_git_info(cwd: str) -> dict[str, Any]:
|
|
|
165
219
|
status_output = _run_git_command(["status", "--short"], cwd)
|
|
166
220
|
changes = len([line for line in status_output.splitlines() if line])
|
|
167
221
|
|
|
222
|
+
# Get last commit message (subject only, limited to 50 chars)
|
|
223
|
+
last_commit = _run_git_command(["log", "-1", "--format=%s"], cwd)
|
|
224
|
+
if len(last_commit) > 50:
|
|
225
|
+
last_commit = last_commit[:47] + "..."
|
|
226
|
+
|
|
168
227
|
return {
|
|
169
228
|
"branch": branch,
|
|
170
229
|
"commit": commit,
|
|
171
230
|
"changes": changes,
|
|
231
|
+
"last_commit": last_commit,
|
|
172
232
|
}
|
|
173
233
|
|
|
174
234
|
except (subprocess.TimeoutExpired, subprocess.CalledProcessError, FileNotFoundError):
|
|
@@ -276,9 +336,81 @@ def get_project_language(cwd: str) -> str:
|
|
|
276
336
|
return detect_language(cwd)
|
|
277
337
|
|
|
278
338
|
|
|
339
|
+
def get_package_version_info() -> dict[str, Any]:
|
|
340
|
+
"""Check MoAI-ADK current and latest version from PyPI
|
|
341
|
+
|
|
342
|
+
Compares the installed version with the latest version available on PyPI.
|
|
343
|
+
Returns version information for SessionStart hook to display update recommendations.
|
|
344
|
+
|
|
345
|
+
Returns:
|
|
346
|
+
dict with keys:
|
|
347
|
+
- "current": Current installed version
|
|
348
|
+
- "latest": Latest version available on PyPI
|
|
349
|
+
- "update_available": Boolean indicating if update is available
|
|
350
|
+
- "upgrade_command": Recommended upgrade command (if update available)
|
|
351
|
+
|
|
352
|
+
Note:
|
|
353
|
+
- Has 1-second timeout to avoid blocking SessionStart
|
|
354
|
+
- Returns graceful fallback if PyPI check fails
|
|
355
|
+
- Handles version parsing gracefully
|
|
356
|
+
"""
|
|
357
|
+
import urllib.error
|
|
358
|
+
import urllib.request
|
|
359
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
360
|
+
|
|
361
|
+
result = {
|
|
362
|
+
"current": "unknown",
|
|
363
|
+
"latest": "unknown",
|
|
364
|
+
"update_available": False,
|
|
365
|
+
"upgrade_command": ""
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
# Get current version
|
|
369
|
+
try:
|
|
370
|
+
result["current"] = version("moai-adk")
|
|
371
|
+
except PackageNotFoundError:
|
|
372
|
+
result["current"] = "dev"
|
|
373
|
+
return result
|
|
374
|
+
|
|
375
|
+
# Get latest version from PyPI (with 1-second timeout)
|
|
376
|
+
try:
|
|
377
|
+
with timeout_handler(1):
|
|
378
|
+
url = "https://pypi.org/pypi/moai-adk/json"
|
|
379
|
+
headers = {"Accept": "application/json"}
|
|
380
|
+
req = urllib.request.Request(url, headers=headers)
|
|
381
|
+
with urllib.request.urlopen(req, timeout=0.8) as response:
|
|
382
|
+
data = json.load(response)
|
|
383
|
+
result["latest"] = data.get("info", {}).get("version", "unknown")
|
|
384
|
+
except (urllib.error.URLError, TimeoutError, Exception):
|
|
385
|
+
# Network error or timeout - return with unknown latest version
|
|
386
|
+
return result
|
|
387
|
+
|
|
388
|
+
# Compare versions (simple comparison)
|
|
389
|
+
if result["current"] != "unknown" and result["latest"] != "unknown":
|
|
390
|
+
try:
|
|
391
|
+
# Parse versions for comparison
|
|
392
|
+
current_parts = [int(x) for x in result["current"].split(".")]
|
|
393
|
+
latest_parts = [int(x) for x in result["latest"].split(".")]
|
|
394
|
+
|
|
395
|
+
# Pad shorter version with zeros
|
|
396
|
+
max_len = max(len(current_parts), len(latest_parts))
|
|
397
|
+
current_parts.extend([0] * (max_len - len(current_parts)))
|
|
398
|
+
latest_parts.extend([0] * (max_len - len(latest_parts)))
|
|
399
|
+
|
|
400
|
+
if latest_parts > current_parts:
|
|
401
|
+
result["update_available"] = True
|
|
402
|
+
result["upgrade_command"] = f"uv pip install --upgrade moai-adk>={result['latest']}"
|
|
403
|
+
except (ValueError, AttributeError):
|
|
404
|
+
# Version parsing failed - skip comparison
|
|
405
|
+
pass
|
|
406
|
+
|
|
407
|
+
return result
|
|
408
|
+
|
|
409
|
+
|
|
279
410
|
__all__ = [
|
|
280
411
|
"detect_language",
|
|
281
412
|
"get_git_info",
|
|
282
413
|
"count_specs",
|
|
283
414
|
"get_project_language",
|
|
415
|
+
"get_package_version_info",
|
|
284
416
|
]
|
|
@@ -6,14 +6,16 @@ SessionStart, SessionEnd event handling
|
|
|
6
6
|
|
|
7
7
|
from core import HookPayload, HookResult
|
|
8
8
|
from core.checkpoint import list_checkpoints
|
|
9
|
-
from core.project import count_specs, detect_language, get_git_info
|
|
9
|
+
from core.project import count_specs, detect_language, get_git_info, get_package_version_info
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
def handle_session_start(payload: HookPayload) -> HookResult:
|
|
13
|
-
"""SessionStart event handler
|
|
13
|
+
"""SessionStart event handler with GRACEFUL DEGRADATION
|
|
14
14
|
|
|
15
15
|
When Claude Code Session starts, it displays a summary of project status.
|
|
16
16
|
You can check the language, Git status, SPEC progress, and checkpoint list at a glance.
|
|
17
|
+
All optional operations are wrapped in try-except to ensure hook completes quickly even if
|
|
18
|
+
Git commands, file I/O, or other operations timeout or fail.
|
|
17
19
|
|
|
18
20
|
Args:
|
|
19
21
|
payload: Claude Code event payload (cwd key required)
|
|
@@ -24,15 +26,23 @@ def handle_session_start(payload: HookPayload) -> HookResult:
|
|
|
24
26
|
Message Format:
|
|
25
27
|
🚀 MoAI-ADK Session Started
|
|
26
28
|
Language: {language}
|
|
27
|
-
Branch: {branch} ({commit hash})
|
|
28
|
-
Changes: {Number of Changed Files}
|
|
29
|
-
SPEC Progress: {Complete}/{Total} ({percent}%)
|
|
30
|
-
Checkpoints: {number} available
|
|
29
|
+
[Branch: {branch} ({commit hash})] - optional if git fails
|
|
30
|
+
[Changes: {Number of Changed Files}] - optional if git fails
|
|
31
|
+
[SPEC Progress: {Complete}/{Total} ({percent}%)] - optional if specs fail
|
|
32
|
+
[Checkpoints: {number} available] - optional if checkpoint list fails
|
|
33
|
+
|
|
34
|
+
Graceful Degradation Strategy:
|
|
35
|
+
- CRITICAL: Language detection (must succeed - no try-except)
|
|
36
|
+
- OPTIONAL: Git info (skip if timeout/failure)
|
|
37
|
+
- OPTIONAL: SPEC progress (skip if timeout/failure)
|
|
38
|
+
- OPTIONAL: Checkpoint list (skip if timeout/failure)
|
|
39
|
+
- Always display SOMETHING to user, never return empty message
|
|
31
40
|
|
|
32
41
|
Note:
|
|
33
42
|
- Claude Code processes SessionStart in several stages (clear → compact)
|
|
34
43
|
- Display message only at "compact" stage to prevent duplicate output
|
|
35
44
|
- "clear" step returns minimal result (empty hookSpecificOutput)
|
|
45
|
+
- CRITICAL: All optional operations must complete within 2-3 seconds total
|
|
36
46
|
|
|
37
47
|
TDD History:
|
|
38
48
|
- RED: Session startup message format test
|
|
@@ -40,8 +50,10 @@ def handle_session_start(payload: HookPayload) -> HookResult:
|
|
|
40
50
|
- REFACTOR: Improved message format, improved readability, added checkpoint list
|
|
41
51
|
- FIX: Prevent duplicate output of clear step (only compact step is displayed)
|
|
42
52
|
- UPDATE: Migrated to Claude Code standard Hook schema
|
|
53
|
+
- HOTFIX: Add graceful degradation for timeout scenarios (Issue #66)
|
|
43
54
|
|
|
44
55
|
@TAG:CHECKPOINT-EVENT-001
|
|
56
|
+
@TAG:HOOKS-TIMEOUT-001
|
|
45
57
|
"""
|
|
46
58
|
# Claude Code SessionStart runs in several stages (clear, compact, etc.)
|
|
47
59
|
# Ignore the "clear" stage and output messages only at the "compact" stage
|
|
@@ -51,32 +63,90 @@ def handle_session_start(payload: HookPayload) -> HookResult:
|
|
|
51
63
|
return HookResult(continue_execution=True)
|
|
52
64
|
|
|
53
65
|
cwd = payload.get("cwd", ".")
|
|
66
|
+
|
|
67
|
+
# CRITICAL: Language detection - MUST succeed (no try-except)
|
|
54
68
|
language = detect_language(cwd)
|
|
55
|
-
git_info = get_git_info(cwd)
|
|
56
|
-
specs = count_specs(cwd)
|
|
57
|
-
checkpoints = list_checkpoints(cwd, max_count=10)
|
|
58
69
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
70
|
+
# OPTIONAL: Git info - skip if timeout/failure
|
|
71
|
+
git_info = {}
|
|
72
|
+
try:
|
|
73
|
+
git_info = get_git_info(cwd)
|
|
74
|
+
except Exception:
|
|
75
|
+
# Graceful degradation - continue without git info
|
|
76
|
+
pass
|
|
77
|
+
|
|
78
|
+
# OPTIONAL: SPEC progress - skip if timeout/failure
|
|
79
|
+
specs = {"completed": 0, "total": 0, "percentage": 0}
|
|
80
|
+
try:
|
|
81
|
+
specs = count_specs(cwd)
|
|
82
|
+
except Exception:
|
|
83
|
+
# Graceful degradation - continue without spec info
|
|
84
|
+
pass
|
|
85
|
+
|
|
86
|
+
# OPTIONAL: Checkpoint list - skip if timeout/failure
|
|
87
|
+
checkpoints = []
|
|
88
|
+
try:
|
|
89
|
+
checkpoints = list_checkpoints(cwd, max_count=10)
|
|
90
|
+
except Exception:
|
|
91
|
+
# Graceful degradation - continue without checkpoints
|
|
92
|
+
pass
|
|
93
|
+
|
|
94
|
+
# OPTIONAL: Package version info - skip if timeout/failure
|
|
95
|
+
version_info = {}
|
|
96
|
+
try:
|
|
97
|
+
version_info = get_package_version_info()
|
|
98
|
+
except Exception:
|
|
99
|
+
# Graceful degradation - continue without version info
|
|
100
|
+
pass
|
|
101
|
+
|
|
102
|
+
# Build message with available information
|
|
103
|
+
branch = git_info.get("branch", "N/A") if git_info else "N/A"
|
|
104
|
+
commit = git_info.get("commit", "N/A")[:7] if git_info else "N/A"
|
|
105
|
+
changes = git_info.get("changes", 0) if git_info else 0
|
|
106
|
+
spec_progress = f"{specs['completed']}/{specs['total']}" if specs["total"] > 0 else "0/0"
|
|
63
107
|
|
|
64
108
|
# system_message: displayed directly to the user
|
|
65
109
|
lines = [
|
|
66
110
|
"🚀 MoAI-ADK Session Started",
|
|
67
|
-
|
|
68
|
-
f" Branch: {branch} ({commit})",
|
|
69
|
-
f" Changes: {changes}",
|
|
70
|
-
f" SPEC Progress: {spec_progress} ({specs['percentage']}%)",
|
|
111
|
+
"", # Blank line after title
|
|
71
112
|
]
|
|
72
113
|
|
|
114
|
+
# Add version info first (at the top, right after title)
|
|
115
|
+
if version_info and version_info.get("current") != "unknown":
|
|
116
|
+
version_line = f" 🗿 MoAI-ADK Ver: {version_info['current']}"
|
|
117
|
+
if version_info.get("update_available"):
|
|
118
|
+
version_line += f" → {version_info['latest']} available ✨"
|
|
119
|
+
lines.append(version_line)
|
|
120
|
+
|
|
121
|
+
# Add upgrade recommendation if update is available
|
|
122
|
+
if version_info.get("update_available") and version_info.get("upgrade_command"):
|
|
123
|
+
lines.append(f" ⬆️ Upgrade: {version_info['upgrade_command']}")
|
|
124
|
+
|
|
125
|
+
# Add language info
|
|
126
|
+
lines.append(f" 🐍 Language: {language}")
|
|
127
|
+
|
|
128
|
+
# Add Git info only if available (not degraded)
|
|
129
|
+
if git_info:
|
|
130
|
+
lines.append(f" 🌿 Branch: {branch} ({commit})")
|
|
131
|
+
lines.append(f" 📝 Changes: {changes}")
|
|
132
|
+
|
|
133
|
+
# Add last commit message if available
|
|
134
|
+
last_commit = git_info.get("last_commit", "")
|
|
135
|
+
if last_commit:
|
|
136
|
+
lines.append(f" 🔨 Last: {last_commit}")
|
|
137
|
+
|
|
73
138
|
# Add Checkpoint list (show only the latest 3 items)
|
|
74
139
|
if checkpoints:
|
|
75
|
-
lines.append(f" Checkpoints: {len(checkpoints)} available")
|
|
140
|
+
lines.append(f" 🗂️ Checkpoints: {len(checkpoints)} available")
|
|
76
141
|
for cp in reversed(checkpoints[-3:]): # Latest 3 items
|
|
77
142
|
branch_short = cp["branch"].replace("before-", "")
|
|
78
|
-
lines.append(f"
|
|
79
|
-
lines.append("
|
|
143
|
+
lines.append(f" 📌 {branch_short}")
|
|
144
|
+
lines.append("") # Blank line before restore command
|
|
145
|
+
lines.append(" ↩️ Restore: /alfred:0-project restore")
|
|
146
|
+
|
|
147
|
+
# Add SPEC progress only if available (not degraded) - at the bottom
|
|
148
|
+
if specs["total"] > 0:
|
|
149
|
+
lines.append(f" 📋 SPEC Progress: {spec_progress} ({specs['percentage']}%)")
|
|
80
150
|
|
|
81
151
|
system_message = "\n".join(lines)
|
|
82
152
|
|
|
@@ -8,7 +8,7 @@ description: "Agent-based coding mode that integrates hands-on development and c
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# Agentic Coding
|
|
11
|
-
> Interactive prompts rely on `
|
|
11
|
+
> Interactive prompts rely on `AskUserQuestion tool (documented in moai-alfred-interactive-questions skill)` so AskUserQuestion renders TUI selection menus for user surveys and approvals.
|
|
12
12
|
|
|
13
13
|
**Audience**: Professional developers, team leaders, architects
|
|
14
14
|
|
|
@@ -8,7 +8,7 @@ description: "Learning mode to easily learn MoAI-ADK concepts and workflow"
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# MoAI ADK Learning
|
|
11
|
-
> Interactive prompts rely on `
|
|
11
|
+
> Interactive prompts rely on `AskUserQuestion tool (documented in moai-alfred-interactive-questions skill)` so AskUserQuestion renders TUI selection menus for user surveys and approvals.
|
|
12
12
|
|
|
13
13
|
**Audience**: Developers new to MoAI-ADK
|
|
14
14
|
|
|
@@ -8,7 +8,7 @@ description: "Learning mode to easily learn new skills with Alfred"
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# Study with Alfred
|
|
11
|
-
> Interactive prompts rely on `
|
|
11
|
+
> Interactive prompts rely on `AskUserQuestion tool (documented in moai-alfred-interactive-questions skill)` so AskUserQuestion renders TUI selection menus for user surveys and approvals.
|
|
12
12
|
|
|
13
13
|
**Audience**: Developers looking to learn new technologies/languages/frameworks
|
|
14
14
|
|
|
@@ -170,7 +170,9 @@ body:
|
|
|
170
170
|
value: |
|
|
171
171
|
## 📚 Reference
|
|
172
172
|
|
|
173
|
-
- **SPEC Metadata Guide**: See
|
|
174
|
-
- **EARS Syntax Guide**: See
|
|
173
|
+
- **SPEC Metadata Guide**: See `{{DOCS_DIR}}/spec-metadata.md`
|
|
174
|
+
- **EARS Syntax Guide**: See `{{DOCS_DIR}}/development-guide.md`
|
|
175
175
|
- **Acceptance Criteria**: Use Given-When-Then (Gherkin) format
|
|
176
|
-
|
|
176
|
+
{% if ENABLE_TAG_SYSTEM -%}
|
|
177
|
+
- **Traceability System**: @SPEC, @TEST, @CODE, @DOC traceability
|
|
178
|
+
{% endif -%}
|
|
@@ -1,20 +1,25 @@
|
|
|
1
|
-
#
|
|
1
|
+
# {{PROJECT_NAME}} GitFlow PR
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Full GitFlow Transparency — the agent auto-fills information
|
|
4
4
|
|
|
5
5
|
## 📝 SPEC Info
|
|
6
6
|
|
|
7
7
|
- Related SPEC: `SPEC-AUTH-001` (e.g., JWT authentication system)
|
|
8
|
-
- Directory:
|
|
8
|
+
- Directory: `{{SPEC_DIR}}/SPEC-AUTH-001/`
|
|
9
|
+
{% if ENABLE_TAG_SYSTEM -%}
|
|
9
10
|
- @TAG Links: @SPEC:AUTH-001 @CODE:AUTH-001 (auto-tagging)
|
|
11
|
+
{% endif -%}
|
|
10
12
|
|
|
11
13
|
## ✅ SPEC Quality Checks
|
|
12
14
|
|
|
13
15
|
- [ ] YAML Front Matter: 7 required fields (id, version, status, created, updated, author, priority)
|
|
14
16
|
- [ ] HISTORY Section: Record versioned change log (include v0.0.1 INITIAL)
|
|
15
17
|
- [ ] EARS Requirements: Ubiquitous, Event-driven, State-driven, Optional, Constraints
|
|
18
|
+
{% if ENABLE_TAG_SYSTEM -%}
|
|
16
19
|
- [ ] @SPEC:ID TAG: Include TAG in doc and check duplicates (`rg "@SPEC:<ID>" -n`)
|
|
20
|
+
{% endif -%}
|
|
17
21
|
|
|
22
|
+
{% if ENABLE_ALFRED_COMMANDS -%}
|
|
18
23
|
## 🤖 Automated Validation Status
|
|
19
24
|
|
|
20
25
|
<!-- The checklist below is auto-updated by the agent -->
|
|
@@ -25,24 +30,29 @@
|
|
|
25
30
|
- [ ] spec-builder: Complete EARS spec and create feature branch
|
|
26
31
|
- [ ] code-builder: Finish TDD RED-GREEN-REFACTOR
|
|
27
32
|
- [ ] doc-syncer: Sync Living Documents and mark PR Ready
|
|
33
|
+
{% endif -%}
|
|
28
34
|
|
|
35
|
+
{% if ENABLE_TRUST_5 -%}
|
|
29
36
|
## 📊 Quality Metrics (auto-calculated)
|
|
30
37
|
|
|
31
38
|
- TRUST 5 Principles: ✅ Compliant
|
|
32
39
|
- Test Coverage: XX% (target ≥ 85%)
|
|
40
|
+
{% if ENABLE_TAG_SYSTEM -%}
|
|
33
41
|
- @TAG Traceability: 100%
|
|
42
|
+
{% endif -%}
|
|
43
|
+
{% endif -%}
|
|
34
44
|
|
|
35
45
|
## 🌍 Locale Settings
|
|
36
46
|
|
|
37
|
-
- Project Language:
|
|
47
|
+
- Project Language: {{CONVERSATION_LANGUAGE}}
|
|
38
48
|
- Commit Messages: <!-- generated automatically according to locale -->
|
|
39
49
|
|
|
40
50
|
## 🎯 Changes
|
|
41
51
|
|
|
42
|
-
<!--
|
|
52
|
+
<!-- auto-fills TDD results -->
|
|
43
53
|
|
|
44
54
|
### 🔴 RED (Test Authoring)
|
|
45
|
-
- Test File: `
|
|
55
|
+
- Test File: `{{TEST_DIR}}/auth/service.test.ts`
|
|
46
56
|
- Test Description: [describe the failing test]
|
|
47
57
|
|
|
48
58
|
### 🟢 GREEN (Implementation)
|
|
@@ -58,12 +68,14 @@
|
|
|
58
68
|
|
|
59
69
|
- [ ] Update README
|
|
60
70
|
- [ ] Sync API docs
|
|
71
|
+
{% if ENABLE_TAG_SYSTEM -%}
|
|
61
72
|
- [ ] Update TAG index
|
|
73
|
+
{% endif -%}
|
|
62
74
|
- [ ] Update HISTORY section (SPEC docs)
|
|
63
75
|
|
|
64
76
|
---
|
|
65
77
|
|
|
66
|
-
🚀
|
|
78
|
+
🚀 {{PROJECT_NAME}}: Professional development via a 3-stage pipeline!
|
|
67
79
|
|
|
68
|
-
Reviewers: Check
|
|
80
|
+
Reviewers: Check quality compliance and SPEC metadata completeness only.
|
|
69
81
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
name:
|
|
1
|
+
name: "{{PROJECT_NAME}} GitFlow Automation"
|
|
2
2
|
|
|
3
|
-
#
|
|
3
|
+
# {{PROJECT_NAME}} 3-stage pipeline: spec → build → sync
|
|
4
4
|
# Full GitFlow transparency — no Git expertise needed
|
|
5
5
|
|
|
6
6
|
on:
|
|
@@ -11,7 +11,7 @@ on:
|
|
|
11
11
|
|
|
12
12
|
jobs:
|
|
13
13
|
moai-pipeline:
|
|
14
|
-
name:
|
|
14
|
+
name: "{{PROJECT_NAME}} Pipeline"
|
|
15
15
|
runs-on: ubuntu-latest
|
|
16
16
|
steps:
|
|
17
17
|
- name: Checkout
|
|
@@ -86,13 +86,13 @@ jobs:
|
|
|
86
86
|
distribution: "temurin"
|
|
87
87
|
java-version: "21"
|
|
88
88
|
|
|
89
|
-
# TRUST 5 Principles — automated validation
|
|
90
|
-
|
|
91
|
-
- name: 🧭
|
|
89
|
+
# TRUST 5 Principles — automated validation (if enabled)
|
|
90
|
+
{% if ENABLE_TRUST_5 -%}
|
|
91
|
+
- name: 🧭 Quality Principles Check
|
|
92
92
|
run: |
|
|
93
|
-
echo "✅
|
|
94
|
-
echo " -
|
|
95
|
-
|
|
93
|
+
echo "✅ Quality validation is performed"
|
|
94
|
+
echo " - Automated quality checks enabled"
|
|
95
|
+
{% endif -%}
|
|
96
96
|
|
|
97
97
|
# Ignore test failures on Draft PRs; fail CI on Ready PRs
|
|
98
98
|
- name: Run language-aware tests
|
|
@@ -221,11 +221,14 @@ jobs:
|
|
|
221
221
|
fi
|
|
222
222
|
fi
|
|
223
223
|
|
|
224
|
-
# TAG system validation (
|
|
225
|
-
|
|
224
|
+
# TAG system validation (if enabled)
|
|
225
|
+
{% if ENABLE_TAG_SYSTEM -%}
|
|
226
|
+
- name: 🏷️ Traceability Validation
|
|
226
227
|
run: |
|
|
227
|
-
echo "✅
|
|
228
|
+
echo "✅ Traceability validation is handled automatically"
|
|
229
|
+
{% endif -%}
|
|
228
230
|
|
|
231
|
+
{% if ENABLE_ALFRED_COMMANDS -%}
|
|
229
232
|
# Run per-branch stages
|
|
230
233
|
- name: 📝 SPEC Stage (feature branch)
|
|
231
234
|
if: startsWith(github.ref, 'refs/heads/feature/')
|
|
@@ -239,7 +242,9 @@ jobs:
|
|
|
239
242
|
run: |
|
|
240
243
|
echo "📝 Draft PR: TDD implementation stage"
|
|
241
244
|
echo "- code-builder agent runs RED-GREEN-REFACTOR"
|
|
242
|
-
|
|
245
|
+
{% if ENABLE_TRUST_5 -%}
|
|
246
|
+
echo "- Validate Quality Principles compliance"
|
|
247
|
+
{% endif -%}
|
|
243
248
|
|
|
244
249
|
- name: 📚 SYNC Stage (Ready PR)
|
|
245
250
|
if: github.event.pull_request.draft == false && github.event.action == 'ready_for_review'
|
|
@@ -247,10 +252,11 @@ jobs:
|
|
|
247
252
|
echo "✅ Ready PR: documentation sync stage"
|
|
248
253
|
echo "- doc-syncer agent synchronizes Living Documents"
|
|
249
254
|
echo "- PR is ready for review"
|
|
255
|
+
{% endif -%}
|
|
250
256
|
|
|
251
257
|
# Final result report
|
|
252
|
-
- name:
|
|
258
|
+
- name: "🎯 {{PROJECT_NAME}} Pipeline Complete"
|
|
253
259
|
run: |
|
|
254
|
-
echo "
|
|
255
|
-
echo "✨ Professional workflow
|
|
260
|
+
echo "{{PROJECT_NAME}} GitFlow automation complete"
|
|
261
|
+
echo "✨ Professional workflow enabled"
|
|
256
262
|
|
|
@@ -3,7 +3,7 @@ name: 📋 SPEC Issue Sync
|
|
|
3
3
|
on:
|
|
4
4
|
pull_request:
|
|
5
5
|
paths:
|
|
6
|
-
- '
|
|
6
|
+
- '{{SPEC_DIR}}/SPEC-*/spec.md'
|
|
7
7
|
types: [opened, synchronize]
|
|
8
8
|
|
|
9
9
|
permissions:
|
|
@@ -33,18 +33,18 @@ jobs:
|
|
|
33
33
|
pwd
|
|
34
34
|
ls -la
|
|
35
35
|
|
|
36
|
-
echo "=== Debug: Looking for
|
|
37
|
-
ls -la
|
|
36
|
+
echo "=== Debug: Looking for {{SPEC_DIR}} ==="
|
|
37
|
+
ls -la {{SPEC_DIR}}/ 2>&1 || echo "{{SPEC_DIR}}/ directory not found"
|
|
38
38
|
|
|
39
39
|
# Find SPEC files in the PR
|
|
40
40
|
echo "=== Debug: Searching for SPEC files ==="
|
|
41
41
|
find . -name "spec.md" -type f 2>&1 || echo "find command failed"
|
|
42
42
|
|
|
43
|
-
spec_file=$(find
|
|
43
|
+
spec_file=$(find {{SPEC_DIR}} -name "spec.md" -type f 2>&1 | head -1)
|
|
44
44
|
echo "Found spec_file: [$spec_file]"
|
|
45
45
|
|
|
46
46
|
if [ -z "$spec_file" ]; then
|
|
47
|
-
echo "⚠️ No SPEC file found in
|
|
47
|
+
echo "⚠️ No SPEC file found in {{SPEC_DIR}}"
|
|
48
48
|
echo "Exiting gracefully (this is expected if no SPEC files changed)"
|
|
49
49
|
exit 0
|
|
50
50
|
fi
|
|
@@ -59,7 +59,11 @@ jobs:
|
|
|
59
59
|
spec_priority=$(grep "^priority:" "$spec_file" | sed 's/^priority: *//' | tr -d ' "')
|
|
60
60
|
|
|
61
61
|
# Extract title from H1 heading
|
|
62
|
+
{% if ENABLE_TAG_SYSTEM -%}
|
|
62
63
|
spec_title=$(grep "^# @SPEC:" "$spec_file" | sed 's/^# @SPEC:[^:]*: //')
|
|
64
|
+
{% else -%}
|
|
65
|
+
spec_title=$(grep "^# " "$spec_file" | head -1 | sed 's/^# //')
|
|
66
|
+
{% endif -%}
|
|
63
67
|
|
|
64
68
|
echo "✅ Extracted SPEC metadata:"
|
|
65
69
|
echo " ID: $spec_id"
|
|
@@ -158,7 +162,7 @@ jobs:
|
|
|
158
162
|
echo "This SPEC has been synchronized to GitHub Issue."
|
|
159
163
|
echo ""
|
|
160
164
|
echo "📋 **Issue**: [#$issue_num - SPEC-$spec_id: $spec_title](../issues/$issue_num)"
|
|
161
|
-
echo "🔗 **SPEC File**:
|
|
165
|
+
echo "🔗 **SPEC File**: \`{{SPEC_DIR}}/SPEC-$spec_id/spec.md\`"
|
|
162
166
|
echo ""
|
|
163
167
|
echo "The issue will be automatically updated as you modify the SPEC document."
|
|
164
168
|
} > /tmp/pr_comment.txt
|