moai-adk 0.8.3__py3-none-any.whl → 0.9.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/cli/commands/update.py +214 -56
- moai_adk/core/tags/pre_commit_validator.py +0 -1
- moai_adk/core/tags/reporter.py +1 -2
- moai_adk/templates/.claude/hooks/alfred/shared/handlers/user.py +19 -0
- moai_adk/templates/.github/workflows/tag-validation.yml +2 -2
- moai_adk/templates/src/moai_adk/core/tags/pre_commit_validator.py +0 -1
- moai_adk/templates/src/moai_adk/core/tags/reporter.py +1 -2
- {moai_adk-0.8.3.dist-info → moai_adk-0.9.1.dist-info}/METADATA +35 -1
- {moai_adk-0.8.3.dist-info → moai_adk-0.9.1.dist-info}/RECORD +12 -13
- moai_adk/templates/.moai/memory/CONFIG-SCHEMA.md +0 -444
- {moai_adk-0.8.3.dist-info → moai_adk-0.9.1.dist-info}/WHEEL +0 -0
- {moai_adk-0.8.3.dist-info → moai_adk-0.9.1.dist-info}/entry_points.txt +0 -0
- {moai_adk-0.8.3.dist-info → moai_adk-0.9.1.dist-info}/licenses/LICENSE +0 -0
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
|
|
|
@@ -206,7 +206,6 @@ class PreCommitValidator:
|
|
|
206
206
|
for line_num, line in enumerate(lines, start=1):
|
|
207
207
|
matches = self.tag_pattern.findall(line)
|
|
208
208
|
for prefix, domain in matches:
|
|
209
|
-
tag = f"@{prefix}:{domain}"
|
|
210
209
|
if domain not in tags_by_type[prefix]:
|
|
211
210
|
tags_by_type[prefix][domain] = []
|
|
212
211
|
tags_by_type[prefix][domain].append((filepath, line_num))
|
moai_adk/core/tags/reporter.py
CHANGED
|
@@ -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}")
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
Handling the UserPromptSubmit event
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
7
10
|
from core import HookPayload, HookResult
|
|
8
11
|
from core.context import get_jit_context
|
|
9
12
|
|
|
@@ -29,11 +32,27 @@ def handle_user_prompt_submit(payload: HookPayload) -> HookResult:
|
|
|
29
32
|
- GREEN: Recommend documents by calling get_jit_context()
|
|
30
33
|
- REFACTOR: Message conditional display (only when there is a file)
|
|
31
34
|
- UPDATE: Migrated to Claude Code standard Hook schema with snake_case fields
|
|
35
|
+
- FEATURE: Command execution logging for tracking double-run debugging
|
|
32
36
|
"""
|
|
33
37
|
user_prompt = payload.get("userPrompt", "")
|
|
34
38
|
cwd = payload.get("cwd", ".")
|
|
35
39
|
context_files = get_jit_context(user_prompt, cwd)
|
|
36
40
|
|
|
41
|
+
# Command execution logging (DEBUG feature for tracking invocations)
|
|
42
|
+
if user_prompt.startswith("/alfred:"):
|
|
43
|
+
try:
|
|
44
|
+
log_dir = Path(cwd) / ".moai" / "logs"
|
|
45
|
+
log_dir.mkdir(parents=True, exist_ok=True)
|
|
46
|
+
|
|
47
|
+
log_file = log_dir / "command-invocations.log"
|
|
48
|
+
timestamp = datetime.now().isoformat()
|
|
49
|
+
|
|
50
|
+
with open(log_file, "a", encoding="utf-8") as f:
|
|
51
|
+
f.write(f"{timestamp} | {user_prompt}\n")
|
|
52
|
+
except Exception:
|
|
53
|
+
# Silently fail if logging fails (don't interrupt main flow)
|
|
54
|
+
pass
|
|
55
|
+
|
|
37
56
|
system_message = f"📎 Loaded {len(context_files)} context file(s)" if context_files else None
|
|
38
57
|
|
|
39
58
|
return HookResult(system_message=system_message, context_files=context_files)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
name: TAG Validation
|
|
2
2
|
|
|
3
|
-
# @DOC:DOC-TAG-
|
|
3
|
+
# @DOC:DOC-TAG-005 | Component 2: CI/CD workflow for TAG validation (template)
|
|
4
4
|
# Validates TAG annotations on every PR to ensure quality and consistency
|
|
5
5
|
|
|
6
6
|
on:
|
|
@@ -26,7 +26,7 @@ jobs:
|
|
|
26
26
|
- name: Setup Python
|
|
27
27
|
uses: actions/setup-python@v5
|
|
28
28
|
with:
|
|
29
|
-
python-version: '3.
|
|
29
|
+
python-version: '3.13'
|
|
30
30
|
|
|
31
31
|
- name: Install uv
|
|
32
32
|
uses: astral-sh/setup-uv@v5
|
|
@@ -206,7 +206,6 @@ class PreCommitValidator:
|
|
|
206
206
|
for line_num, line in enumerate(lines, start=1):
|
|
207
207
|
matches = self.tag_pattern.findall(line)
|
|
208
208
|
for prefix, domain in matches:
|
|
209
|
-
tag = f"@{prefix}:{domain}"
|
|
210
209
|
if domain not in tags_by_type[prefix]:
|
|
211
210
|
tags_by_type[prefix][domain] = []
|
|
212
211
|
tags_by_type[prefix][domain].append((filepath, line_num))
|
|
@@ -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}")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: moai-adk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.1
|
|
4
4
|
Summary: MoAI Agentic Development Kit - SPEC-First TDD with Alfred SuperAgent & Complete Skills v2.0
|
|
5
5
|
Project-URL: Homepage, https://github.com/modu-ai/moai-adk
|
|
6
6
|
Project-URL: Repository, https://github.com/modu-ai/moai-adk
|
|
@@ -829,6 +829,40 @@ This issue has been **fixed** in v0.8.1+. If you're experiencing this:
|
|
|
829
829
|
- Code now gracefully handles InvalidVersion exceptions
|
|
830
830
|
- Added comprehensive unit tests for placeholder detection
|
|
831
831
|
|
|
832
|
+
#### Problem: UV Tool Upgrade Shows "Nothing to upgrade" Despite New Version
|
|
833
|
+
|
|
834
|
+
**Status**: Automatically fixed in v0.9.1+ with cache refresh retry
|
|
835
|
+
|
|
836
|
+
**Symptoms:**
|
|
837
|
+
- `moai-adk update` shows "Nothing to upgrade"
|
|
838
|
+
- PyPI has a newer version available
|
|
839
|
+
- Caused by stale UV cache metadata
|
|
840
|
+
|
|
841
|
+
**Automatic Fix (v0.9.1+):**
|
|
842
|
+
The system now automatically detects stale cache and retries:
|
|
843
|
+
1. Detects "Nothing to upgrade" message
|
|
844
|
+
2. Compares installed version vs PyPI latest version
|
|
845
|
+
3. Clears UV cache: `uv cache clean moai-adk`
|
|
846
|
+
4. Automatically retries upgrade
|
|
847
|
+
5. Completes in a single command run
|
|
848
|
+
|
|
849
|
+
**Manual Workaround (if needed):**
|
|
850
|
+
```bash
|
|
851
|
+
# Clear UV cache and retry
|
|
852
|
+
uv cache clean moai-adk && moai-adk update
|
|
853
|
+
```
|
|
854
|
+
|
|
855
|
+
**Technical Details:**
|
|
856
|
+
- Root cause: UV caches PyPI metadata that can become outdated
|
|
857
|
+
- Detection: Compares version strings when "Nothing to upgrade" appears
|
|
858
|
+
- Retry limit: Maximum 1 retry to prevent infinite loops
|
|
859
|
+
- Timeout: 10 seconds for cache clear operation
|
|
860
|
+
|
|
861
|
+
**References:**
|
|
862
|
+
- SPEC: @SPEC:UPDATE-CACHE-FIX-001
|
|
863
|
+
- Implementation: @CODE:UPDATE-CACHE-FIX-001-001, @CODE:UPDATE-CACHE-FIX-001-002, @CODE:UPDATE-CACHE-FIX-001-003
|
|
864
|
+
- Tests: @TEST:UPDATE-CACHE-FIX-001
|
|
865
|
+
|
|
832
866
|
### Contributing Tests
|
|
833
867
|
|
|
834
868
|
When adding new features, always include tests:
|
|
@@ -7,7 +7,7 @@ moai_adk/cli/commands/backup.py,sha256=jKdm9P55RIIdaBLhXYDQdbn2ThQDVDrc9_No48uHB
|
|
|
7
7
|
moai_adk/cli/commands/doctor.py,sha256=keyU2PwwiUGuQViQVDlXKCqLmi6F1JDW3JEOOY64wgk,9831
|
|
8
8
|
moai_adk/cli/commands/init.py,sha256=XidvHLZD-7_ZoHk-LcKmJaj78hhAjj7yB6LCBKHxx40,11459
|
|
9
9
|
moai_adk/cli/commands/status.py,sha256=FQgzz7GYKk1W-w08xBg1A1bziSGsE0qvXhQJrPjow8o,3796
|
|
10
|
-
moai_adk/cli/commands/update.py,sha256=
|
|
10
|
+
moai_adk/cli/commands/update.py,sha256=ijCj3ssT8t1y7a7bAx0ebcx5KI_3kRXXEK-3lUnfRao,38107
|
|
11
11
|
moai_adk/cli/prompts/__init__.py,sha256=a4_ctS4KEvGtmM9j7z8XIlMkpftohjVb9woUwZu38gE,136
|
|
12
12
|
moai_adk/cli/prompts/init_prompts.py,sha256=OZ_T-b4XfkyXQsKXTwLcDYwmLbaf0k5oZfbi_asTH9M,5226
|
|
13
13
|
moai_adk/core/__init__.py,sha256=1sJO-PHEKF1NmYjeOPPPzn_HRgYln3CKlCpUH4E2Jrs,129
|
|
@@ -42,8 +42,8 @@ moai_adk/core/tags/generator.py,sha256=xVopAAez8K5pNSHWZOGZXEWYIre53lTtfHUlR7i-v
|
|
|
42
42
|
moai_adk/core/tags/inserter.py,sha256=_1lsMTyyrjGidAKqi4y925msHxK6gHLAsu3O8-O2vFM,2823
|
|
43
43
|
moai_adk/core/tags/mapper.py,sha256=ldcfMFzsk-ESKpXAptYydqx2NZ951utqKOXZVTHMeIE,3955
|
|
44
44
|
moai_adk/core/tags/parser.py,sha256=3LpNcL47xT3u4b3Fdf9wYnltChDrNAEV4EtnHqLN1MA,2098
|
|
45
|
-
moai_adk/core/tags/pre_commit_validator.py,sha256=
|
|
46
|
-
moai_adk/core/tags/reporter.py,sha256=
|
|
45
|
+
moai_adk/core/tags/pre_commit_validator.py,sha256=Y1G8qtuRjCaHGjQ-Nxn8k2UGOcHRd3EfdZrSqCqHAY4,10230
|
|
46
|
+
moai_adk/core/tags/reporter.py,sha256=fn9kc0FNNvaep5cXbA2RkYxbJsw7eq2PHbrZ_XFB1Wc,29301
|
|
47
47
|
moai_adk/core/tags/tags.py,sha256=Fylt030vrDqhSWivwRbxxwu_uQhilei0SFk_xVzy9UI,4248
|
|
48
48
|
moai_adk/core/tags/validator.py,sha256=B3xUDubfyT5RlPM3GLYktfe1EQTEdxOrDhD230_etoA,31138
|
|
49
49
|
moai_adk/core/template/__init__.py,sha256=BB65Zkar2Oywt1_RFtQKIup6HXq20VNQrYqXD_Qp1_g,354
|
|
@@ -96,7 +96,7 @@ moai_adk/templates/.claude/hooks/alfred/shared/handlers/__init__.py,sha256=j5L7n
|
|
|
96
96
|
moai_adk/templates/.claude/hooks/alfred/shared/handlers/notification.py,sha256=8TcEqGlz4EpLf2lpouaIuhVbpKOVeY31KPmIq2snt9U,662
|
|
97
97
|
moai_adk/templates/.claude/hooks/alfred/shared/handlers/session.py,sha256=WkTiwgwce089eUkY1cT7WdXpSqpbmj-IIWTdRd3Ihqk,7278
|
|
98
98
|
moai_adk/templates/.claude/hooks/alfred/shared/handlers/tool.py,sha256=0bNtAf2M7nupTAkcrEiZMBCmD2f-dLjjZa5p1CWiNyM,3383
|
|
99
|
-
moai_adk/templates/.claude/hooks/alfred/shared/handlers/user.py,sha256=
|
|
99
|
+
moai_adk/templates/.claude/hooks/alfred/shared/handlers/user.py,sha256=okgzK5qwGjl_i2O4IWoH-7GaxQ2Q_kutjhnVnk0eDzE,2107
|
|
100
100
|
moai_adk/templates/.claude/output-styles/alfred/agentic-coding.md,sha256=xxqVTxNwBEQahaerqeFYDEAErPNLw_-W1orWXYLuwtU,19702
|
|
101
101
|
moai_adk/templates/.claude/output-styles/alfred/moai-adk-learning.md,sha256=Et9BlQkja_jOIVp7NLBVxCGU7H0H-qoEe_9LNaVis3k,17583
|
|
102
102
|
moai_adk/templates/.claude/output-styles/alfred/study-with-alfred.md,sha256=XpIj9c4jqOOlZxn57Ujj8o7VIpAx_ftddwwd0fxIsio,12632
|
|
@@ -285,7 +285,7 @@ moai_adk/templates/.github/workflows/moai-release-pipeline.yml,sha256=kVpRk_OFgz
|
|
|
285
285
|
moai_adk/templates/.github/workflows/release.yml,sha256=5h4-npHKU-t1mnOQePYcpkcdGCN_560-Obu5tUCyNe0,1382
|
|
286
286
|
moai_adk/templates/.github/workflows/spec-issue-sync.yml,sha256=GIYeQQvwcoi0r-o85nEbSSLwT5EsrSEzJKLhLMX7GPk,6483
|
|
287
287
|
moai_adk/templates/.github/workflows/tag-report.yml,sha256=zZ33OmLnRjBfWbnnYx_chpV_iY3rncCAbcVP708JQoE,9273
|
|
288
|
-
moai_adk/templates/.github/workflows/tag-validation.yml,sha256=
|
|
288
|
+
moai_adk/templates/.github/workflows/tag-validation.yml,sha256=1JEbUzc0vHL4C2LTs0Q7rBREmrV5N5zM2WBYJ1NMCAI,6769
|
|
289
289
|
moai_adk/templates/.moai/config.json,sha256=r7TtDLtS06YDP7EsqsghNfKCNkltVxVbmmSJHwQQV-s,2950
|
|
290
290
|
moai_adk/templates/.moai/docs/quick-issue-creation-guide.md,sha256=tFJqsNr9SpXlGao6HXXm6I8FmC7yCkUo9cu6lE1GaVA,5460
|
|
291
291
|
moai_adk/templates/.moai/hooks/install.sh,sha256=P1-RtiHp7m2QIUUcHQ_oHB1xnqRQnD5QePb2iUo7qfA,2112
|
|
@@ -293,7 +293,6 @@ moai_adk/templates/.moai/hooks/pre-commit.sh,sha256=86UIhXMqJ4i24mKrLdzvyWXYSMK_
|
|
|
293
293
|
moai_adk/templates/.moai/memory/CLAUDE-AGENTS-GUIDE.md,sha256=37Qj5DYcyUniLM1g8IU7UWFI_16tutZrcAkJc4E5i20,15876
|
|
294
294
|
moai_adk/templates/.moai/memory/CLAUDE-PRACTICES.md,sha256=Tf3q68X1DiA3MkIBYu7AMXoeparYrDRpyqVI5Nw92OY,12653
|
|
295
295
|
moai_adk/templates/.moai/memory/CLAUDE-RULES.md,sha256=S9GODGRzwwleOmROVtBDa471Ok5NyQLWIkaO_4peHhU,19783
|
|
296
|
-
moai_adk/templates/.moai/memory/CONFIG-SCHEMA.md,sha256=XmvogEnGedRItVc9OABKr4lDNEZhcKZQ3Qa6KlfxE3E,12477
|
|
297
296
|
moai_adk/templates/.moai/memory/DEVELOPMENT-GUIDE.md,sha256=SAcue1J5-DEJpygDnTgp_ex-ok2E4lbcykBuBiC7tGs,14534
|
|
298
297
|
moai_adk/templates/.moai/memory/GITFLOW-PROTECTION-POLICY.md,sha256=tm2ZOmNMvomAhh4_AGTUNjmSsc-eKzo1SGshojx2QL0,6228
|
|
299
298
|
moai_adk/templates/.moai/memory/ISSUE-LABEL-MAPPING.md,sha256=bKzC2v1ZZWyng0eHSj9oFQ9w9xCbadPe9T9IYWub7wM,3824
|
|
@@ -308,14 +307,14 @@ moai_adk/templates/src/moai_adk/core/__init__.py,sha256=rbztODLKJooQ4ah3_Pjfk9wN
|
|
|
308
307
|
moai_adk/templates/src/moai_adk/core/tags/__init__.py,sha256=nmZKs4eblrOzPg8JOHNkoaRzhPcUDGmMwvnUcyMZwGY,1985
|
|
309
308
|
moai_adk/templates/src/moai_adk/core/tags/ci_validator.py,sha256=PScVKPROu12mnu02nFzi66i-1pdbbnoitGzheI1COV8,13679
|
|
310
309
|
moai_adk/templates/src/moai_adk/core/tags/cli.py,sha256=1VWRT1k62FS5Yyzllat-nyLR3jqvGdyDkj1uOwPfavA,7344
|
|
311
|
-
moai_adk/templates/src/moai_adk/core/tags/pre_commit_validator.py,sha256=
|
|
312
|
-
moai_adk/templates/src/moai_adk/core/tags/reporter.py,sha256=
|
|
310
|
+
moai_adk/templates/src/moai_adk/core/tags/pre_commit_validator.py,sha256=Y1G8qtuRjCaHGjQ-Nxn8k2UGOcHRd3EfdZrSqCqHAY4,10230
|
|
311
|
+
moai_adk/templates/src/moai_adk/core/tags/reporter.py,sha256=fn9kc0FNNvaep5cXbA2RkYxbJsw7eq2PHbrZ_XFB1Wc,29301
|
|
313
312
|
moai_adk/templates/src/moai_adk/core/tags/validator.py,sha256=B3xUDubfyT5RlPM3GLYktfe1EQTEdxOrDhD230_etoA,31138
|
|
314
313
|
moai_adk/utils/__init__.py,sha256=VnVfQzzKHvKw4bNdEw5xdscnRQYFrnr-v_TOBr3naPs,225
|
|
315
314
|
moai_adk/utils/banner.py,sha256=znppKd5yo-tTqgyhgPVJjstrTrfcy_v3X1_RFQxP4Fk,1878
|
|
316
315
|
moai_adk/utils/logger.py,sha256=g-m07PGKjK2bKRIInfSn6m-024Bedai-pV_WjZKDeu8,5064
|
|
317
|
-
moai_adk-0.
|
|
318
|
-
moai_adk-0.
|
|
319
|
-
moai_adk-0.
|
|
320
|
-
moai_adk-0.
|
|
321
|
-
moai_adk-0.
|
|
316
|
+
moai_adk-0.9.1.dist-info/METADATA,sha256=BdmPP_9_s7nEwqrb13ZBJfIVdu3p4QHMrK8Ru93Uz_c,82377
|
|
317
|
+
moai_adk-0.9.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
318
|
+
moai_adk-0.9.1.dist-info/entry_points.txt,sha256=P9no1794UipqH72LP-ltdyfVd_MeB1WKJY_6-JQgV3U,52
|
|
319
|
+
moai_adk-0.9.1.dist-info/licenses/LICENSE,sha256=M1M2b07fWcSWRM6_P3wbZKndZvyfHyYk_Wu9bS8F7o8,1069
|
|
320
|
+
moai_adk-0.9.1.dist-info/RECORD,,
|
|
@@ -1,444 +0,0 @@
|
|
|
1
|
-
# MoAI-ADK config.json Schema Documentation
|
|
2
|
-
|
|
3
|
-
> **Purpose**: This document defines the official schema structure for `.moai/config.json` file, which serves as the single source of truth for all MoAI-ADK project configuration.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## 📋 Schema Overview
|
|
8
|
-
|
|
9
|
-
The `.moai/config.json` file contains all project-level configuration for MoAI-ADK, including:
|
|
10
|
-
- Project metadata and settings
|
|
11
|
-
- User preferences (language, nickname)
|
|
12
|
-
- Git workflow strategies
|
|
13
|
-
- TAG system configuration
|
|
14
|
-
- Constitution principles
|
|
15
|
-
|
|
16
|
-
---
|
|
17
|
-
|
|
18
|
-
## 🏗️ Schema Structure
|
|
19
|
-
|
|
20
|
-
### Top-Level Sections
|
|
21
|
-
|
|
22
|
-
```json
|
|
23
|
-
{
|
|
24
|
-
"_meta": { /* Metadata about the config file itself */ },
|
|
25
|
-
"moai": { /* MoAI-ADK version info */ },
|
|
26
|
-
"project": { /* Project-specific settings */ },
|
|
27
|
-
"user": { /* User preferences */ },
|
|
28
|
-
"constitution": { /* Development principles */ },
|
|
29
|
-
"git_strategy": { /* Git workflow configuration */ },
|
|
30
|
-
"pipeline": { /* Command pipeline settings */ },
|
|
31
|
-
"tags": { /* TAG system configuration */ }
|
|
32
|
-
}
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
---
|
|
36
|
-
|
|
37
|
-
## 📖 Section Definitions
|
|
38
|
-
|
|
39
|
-
### 1. `_meta` Section
|
|
40
|
-
|
|
41
|
-
**Purpose**: Internal metadata for config structure tracking and TAG traceability.
|
|
42
|
-
|
|
43
|
-
**Schema**:
|
|
44
|
-
```json
|
|
45
|
-
{
|
|
46
|
-
"_meta": {
|
|
47
|
-
"@CODE:CONFIG-STRUCTURE-001": "@DOC:JSON-CONFIG-001",
|
|
48
|
-
"@SPEC:PROJECT-CONFIG-001": "@SPEC:MOAI-CONFIG-001"
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
**Fields**:
|
|
54
|
-
- `@CODE:*`: TAG references to code implementation
|
|
55
|
-
- `@SPEC:*`: TAG references to specification documents
|
|
56
|
-
|
|
57
|
-
---
|
|
58
|
-
|
|
59
|
-
### 2. `moai` Section
|
|
60
|
-
|
|
61
|
-
**Purpose**: Track MoAI-ADK package version for compatibility checks.
|
|
62
|
-
|
|
63
|
-
**Schema**:
|
|
64
|
-
```json
|
|
65
|
-
{
|
|
66
|
-
"moai": {
|
|
67
|
-
"version": "0.4.6"
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
**Fields**:
|
|
73
|
-
- `version` (string, required): MoAI-ADK version in SemVer format (e.g., "0.4.6")
|
|
74
|
-
|
|
75
|
-
---
|
|
76
|
-
|
|
77
|
-
### 3. `project` Section
|
|
78
|
-
|
|
79
|
-
**Purpose**: Core project settings including name, mode, language preferences, and optimization status.
|
|
80
|
-
|
|
81
|
-
**Schema**:
|
|
82
|
-
```json
|
|
83
|
-
{
|
|
84
|
-
"project": {
|
|
85
|
-
"name": "MoAI-ADK",
|
|
86
|
-
"description": "MoAI-Agentic Development Kit",
|
|
87
|
-
"mode": "personal",
|
|
88
|
-
"locale": "ko",
|
|
89
|
-
"conversation_language": "ko",
|
|
90
|
-
"conversation_language_name": "한국어",
|
|
91
|
-
"language": "python",
|
|
92
|
-
"moai_adk_version": "0.4.6",
|
|
93
|
-
"initialized": true,
|
|
94
|
-
"optimized": true,
|
|
95
|
-
"optimized_at": "2025-10-22T12:45:00Z",
|
|
96
|
-
"created_at": "2025-10-21 23:59:19"
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
**Fields**:
|
|
102
|
-
- `name` (string, required): Project name
|
|
103
|
-
- `description` (string, optional): Brief project description
|
|
104
|
-
- `mode` (enum, required): Project mode — `"personal"` | `"team"`
|
|
105
|
-
- `locale` (string, required): System locale code (e.g., `"ko"`, `"en"`, `"ja"`)
|
|
106
|
-
- `conversation_language` (string, required): Language code for all Alfred dialogs and documentation (e.g., `"ko"`, `"en"`, `"ja"`, `"zh"`)
|
|
107
|
-
- `conversation_language_name` (string, required): Display name of conversation language (e.g., `"한국어"`, `"English"`)
|
|
108
|
-
- `language` (string, required): Primary codebase language (e.g., `"python"`, `"typescript"`)
|
|
109
|
-
- `moai_adk_version` (string, required): MoAI-ADK version this project was initialized with
|
|
110
|
-
- `initialized` (boolean, required): Whether project initialization completed
|
|
111
|
-
- `optimized` (boolean, required): Whether template optimization completed
|
|
112
|
-
- `optimized_at` (string, optional): ISO 8601 timestamp of optimization completion
|
|
113
|
-
- `created_at` (string, required): Project creation timestamp
|
|
114
|
-
|
|
115
|
-
**Notes**:
|
|
116
|
-
- `conversation_language` is set during `/alfred:0-project` STEP 0.1
|
|
117
|
-
- `language` is auto-detected during `/alfred:0-project` STEP 1 or can be manually specified
|
|
118
|
-
|
|
119
|
-
---
|
|
120
|
-
|
|
121
|
-
### 4. `user` Section (NEW in v0.4.6)
|
|
122
|
-
|
|
123
|
-
**Purpose**: User-specific preferences for personalized Alfred experience.
|
|
124
|
-
|
|
125
|
-
**Schema**:
|
|
126
|
-
```json
|
|
127
|
-
{
|
|
128
|
-
"user": {
|
|
129
|
-
"nickname": "GOOS오라버니"
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
**Fields**:
|
|
135
|
-
- `nickname` (string, required): User's chosen nickname for personalized communication
|
|
136
|
-
- Length: 1-50 characters
|
|
137
|
-
- Allows emoji, spaces, and special characters
|
|
138
|
-
- Set during `/alfred:0-project` STEP 0.2
|
|
139
|
-
- Used by all sub-agents to address the user (e.g., "안녕하세요, GOOS오라버니님!")
|
|
140
|
-
- Displayed in `CLAUDE.md` under "## 프로젝트 정보 | Project Information"
|
|
141
|
-
|
|
142
|
-
**Usage**:
|
|
143
|
-
- Alfred and all sub-agents receive `user_nickname` as a context parameter
|
|
144
|
-
- Language-specific honorifics are applied automatically (e.g., "님" in Korean)
|
|
145
|
-
- Enhances user experience by personalizing all interactions
|
|
146
|
-
|
|
147
|
-
---
|
|
148
|
-
|
|
149
|
-
### 5. `constitution` Section
|
|
150
|
-
|
|
151
|
-
**Purpose**: Define development principles and quality gates enforced by MoAI-ADK.
|
|
152
|
-
|
|
153
|
-
**Schema**:
|
|
154
|
-
```json
|
|
155
|
-
{
|
|
156
|
-
"constitution": {
|
|
157
|
-
"enforce_tdd": true,
|
|
158
|
-
"require_tags": true,
|
|
159
|
-
"test_coverage_target": 85,
|
|
160
|
-
"simplicity_threshold": 5,
|
|
161
|
-
"principles": {
|
|
162
|
-
"simplicity": {
|
|
163
|
-
"max_projects": 5,
|
|
164
|
-
"notes": "Default recommendation. Adjust in .moai/config.json or via SPEC/ADR with documented rationale based on project size."
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
**Fields**:
|
|
172
|
-
- `enforce_tdd` (boolean, required): Whether TDD workflow is mandatory
|
|
173
|
-
- `require_tags` (boolean, required): Whether @TAG annotations are required
|
|
174
|
-
- `test_coverage_target` (integer, required): Minimum test coverage percentage (default: 85)
|
|
175
|
-
- `simplicity_threshold` (integer, required): Max number of concurrent projects (default: 5)
|
|
176
|
-
- `principles` (object, optional): Additional principle definitions
|
|
177
|
-
|
|
178
|
-
---
|
|
179
|
-
|
|
180
|
-
### 6. `git_strategy` Section
|
|
181
|
-
|
|
182
|
-
**Purpose**: Configure Git workflow automation based on project mode.
|
|
183
|
-
|
|
184
|
-
**Schema**:
|
|
185
|
-
```json
|
|
186
|
-
{
|
|
187
|
-
"git_strategy": {
|
|
188
|
-
"personal": {
|
|
189
|
-
"auto_checkpoint": "event-driven",
|
|
190
|
-
"checkpoint_events": ["delete", "refactor", "merge", "script", "critical-file"],
|
|
191
|
-
"checkpoint_type": "local-branch",
|
|
192
|
-
"max_checkpoints": 10,
|
|
193
|
-
"cleanup_days": 7,
|
|
194
|
-
"push_to_remote": false,
|
|
195
|
-
"auto_commit": true,
|
|
196
|
-
"branch_prefix": "feature/",
|
|
197
|
-
"develop_branch": "develop",
|
|
198
|
-
"main_branch": "main"
|
|
199
|
-
},
|
|
200
|
-
"team": {
|
|
201
|
-
"use_gitflow": true,
|
|
202
|
-
"auto_pr": true,
|
|
203
|
-
"draft_pr": true,
|
|
204
|
-
"feature_prefix": "feature/SPEC-",
|
|
205
|
-
"develop_branch": "develop",
|
|
206
|
-
"main_branch": "main"
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
**Personal Mode Fields**:
|
|
213
|
-
- `auto_checkpoint` (enum): `"event-driven"` | `"manual"` | `"disabled"`
|
|
214
|
-
- `checkpoint_events` (array): List of events triggering auto-checkpoint
|
|
215
|
-
- `checkpoint_type` (enum): `"local-branch"` | `"git-stash"` | `"backup-dir"`
|
|
216
|
-
- `max_checkpoints` (integer): Max number of checkpoints to retain
|
|
217
|
-
- `cleanup_days` (integer): Days before old checkpoints are removed
|
|
218
|
-
- `push_to_remote` (boolean): Whether to push checkpoints to remote
|
|
219
|
-
- `auto_commit` (boolean): Auto-commit changes after TDD cycles
|
|
220
|
-
- `branch_prefix` (string): Prefix for feature branches
|
|
221
|
-
- `develop_branch` (string): Name of develop branch
|
|
222
|
-
- `main_branch` (string): Name of main/production branch
|
|
223
|
-
|
|
224
|
-
**Team Mode Fields**:
|
|
225
|
-
- `use_gitflow` (boolean): Enable GitFlow branching strategy
|
|
226
|
-
- `auto_pr` (boolean): Auto-create PR after feature completion
|
|
227
|
-
- `draft_pr` (boolean): Create PRs in draft mode by default
|
|
228
|
-
- `feature_prefix` (string): Prefix for feature branches (includes SPEC ID)
|
|
229
|
-
- `develop_branch` (string): Name of develop branch
|
|
230
|
-
- `main_branch` (string): Name of main/production branch
|
|
231
|
-
|
|
232
|
-
---
|
|
233
|
-
|
|
234
|
-
### 7. `pipeline` Section
|
|
235
|
-
|
|
236
|
-
**Purpose**: Define available Alfred commands and track current workflow stage.
|
|
237
|
-
|
|
238
|
-
**Schema**:
|
|
239
|
-
```json
|
|
240
|
-
{
|
|
241
|
-
"pipeline": {
|
|
242
|
-
"available_commands": [
|
|
243
|
-
"/alfred:0-project",
|
|
244
|
-
"/alfred:1-plan",
|
|
245
|
-
"/alfred:2-run",
|
|
246
|
-
"/alfred:3-sync"
|
|
247
|
-
],
|
|
248
|
-
"current_stage": "initialized"
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
**Fields**:
|
|
254
|
-
- `available_commands` (array, required): List of available Alfred commands
|
|
255
|
-
- `current_stage` (string, required): Current workflow stage (e.g., `"initialized"`, `"planning"`, `"building"`, `"syncing"`)
|
|
256
|
-
|
|
257
|
-
---
|
|
258
|
-
|
|
259
|
-
### 8. `tags` Section
|
|
260
|
-
|
|
261
|
-
**Purpose**: Configure TAG system behavior and storage policy.
|
|
262
|
-
|
|
263
|
-
**Schema**:
|
|
264
|
-
```json
|
|
265
|
-
{
|
|
266
|
-
"tags": {
|
|
267
|
-
"storage_type": "code_scan",
|
|
268
|
-
"auto_sync": true,
|
|
269
|
-
"categories": ["REQ", "DESIGN", "TASK", "TEST", "FEATURE", "API", "UI", "DATA"],
|
|
270
|
-
"code_scan_policy": {
|
|
271
|
-
"no_intermediate_cache": true,
|
|
272
|
-
"realtime_validation": true,
|
|
273
|
-
"scan_tools": ["rg", "grep"],
|
|
274
|
-
"scan_command": "rg '@TAG' -n",
|
|
275
|
-
"philosophy": "The source of truth for TAGs lives in the code itself"
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
**Fields**:
|
|
282
|
-
- `storage_type` (enum, required): `"code_scan"` | `"registry_file"`
|
|
283
|
-
- `auto_sync` (boolean, required): Auto-sync TAGs during `/alfred:3-sync`
|
|
284
|
-
- `categories` (array, required): List of valid TAG categories
|
|
285
|
-
- `code_scan_policy` (object, required): Configuration for code-first TAG scanning
|
|
286
|
-
- `no_intermediate_cache` (boolean): Never use intermediate TAG cache files
|
|
287
|
-
- `realtime_validation` (boolean): Validate TAGs on every operation
|
|
288
|
-
- `scan_tools` (array): Tools used for TAG scanning (e.g., `["rg", "grep"]`)
|
|
289
|
-
- `scan_command` (string): Command to scan for TAGs
|
|
290
|
-
- `philosophy` (string): Description of TAG philosophy
|
|
291
|
-
|
|
292
|
-
---
|
|
293
|
-
|
|
294
|
-
## 🔄 Schema Evolution
|
|
295
|
-
|
|
296
|
-
### Version History
|
|
297
|
-
|
|
298
|
-
| Version | Date | Changes |
|
|
299
|
-
| ------- | ---------- | ------------------------------------------------------ |
|
|
300
|
-
| v0.4.6 | 2025-10-23 | Added `user` section with `nickname` field |
|
|
301
|
-
| v0.4.2 | 2025-10-22 | Added `conversation_language` and `language_name` fields |
|
|
302
|
-
| v0.4.0 | 2025-10-20 | Initial schema documentation |
|
|
303
|
-
|
|
304
|
-
### Migration Guide
|
|
305
|
-
|
|
306
|
-
**From v0.4.5 → v0.4.6**:
|
|
307
|
-
- Add `user` section with `nickname` field
|
|
308
|
-
- No breaking changes; existing configs remain compatible
|
|
309
|
-
|
|
310
|
-
**Example Migration**:
|
|
311
|
-
```json
|
|
312
|
-
// Before (v0.4.5)
|
|
313
|
-
{
|
|
314
|
-
"project": {
|
|
315
|
-
"conversation_language": "ko",
|
|
316
|
-
"conversation_language_name": "한국어"
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// After (v0.4.6)
|
|
321
|
-
{
|
|
322
|
-
"project": {
|
|
323
|
-
"conversation_language": "ko",
|
|
324
|
-
"conversation_language_name": "한국어"
|
|
325
|
-
},
|
|
326
|
-
"user": {
|
|
327
|
-
"nickname": "GOOS오라버니"
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
```
|
|
331
|
-
|
|
332
|
-
---
|
|
333
|
-
|
|
334
|
-
## 📚 Related Documentation
|
|
335
|
-
|
|
336
|
-
- **Template Processing**: `/src/moai_adk/cli/template_processor.py`
|
|
337
|
-
- **Project Initialization**: `/.claude/commands/alfred/0-project.md`
|
|
338
|
-
- **project-manager Agent**: `/.claude/agents/alfred/project-manager.md`
|
|
339
|
-
- **CLAUDE.md Template**: `/src/moai_adk/templates/CLAUDE.md`
|
|
340
|
-
|
|
341
|
-
---
|
|
342
|
-
|
|
343
|
-
## ✅ Validation Rules
|
|
344
|
-
|
|
345
|
-
### Required Fields per Section
|
|
346
|
-
|
|
347
|
-
**Minimum Valid Config**:
|
|
348
|
-
```json
|
|
349
|
-
{
|
|
350
|
-
"moai": {
|
|
351
|
-
"version": "0.4.6"
|
|
352
|
-
},
|
|
353
|
-
"project": {
|
|
354
|
-
"name": "MyProject",
|
|
355
|
-
"mode": "personal",
|
|
356
|
-
"locale": "en",
|
|
357
|
-
"conversation_language": "en",
|
|
358
|
-
"conversation_language_name": "English",
|
|
359
|
-
"language": "python",
|
|
360
|
-
"initialized": true
|
|
361
|
-
},
|
|
362
|
-
"user": {
|
|
363
|
-
"nickname": "Developer"
|
|
364
|
-
},
|
|
365
|
-
"constitution": {
|
|
366
|
-
"enforce_tdd": true,
|
|
367
|
-
"require_tags": true,
|
|
368
|
-
"test_coverage_target": 85
|
|
369
|
-
},
|
|
370
|
-
"tags": {
|
|
371
|
-
"storage_type": "code_scan",
|
|
372
|
-
"auto_sync": true
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
```
|
|
376
|
-
|
|
377
|
-
### Validation Checklist
|
|
378
|
-
|
|
379
|
-
- [ ] `moai.version` matches installed package version
|
|
380
|
-
- [ ] `project.mode` is either `"personal"` or `"team"`
|
|
381
|
-
- [ ] `project.conversation_language` is a valid ISO 639-1 code
|
|
382
|
-
- [ ] `user.nickname` length is between 1-50 characters
|
|
383
|
-
- [ ] `constitution.test_coverage_target` is between 0-100
|
|
384
|
-
- [ ] `tags.storage_type` is valid enum value
|
|
385
|
-
- [ ] All required fields are present
|
|
386
|
-
|
|
387
|
-
---
|
|
388
|
-
|
|
389
|
-
## 🛠️ Usage Examples
|
|
390
|
-
|
|
391
|
-
### Reading Config in Python
|
|
392
|
-
|
|
393
|
-
```python
|
|
394
|
-
import json
|
|
395
|
-
from pathlib import Path
|
|
396
|
-
|
|
397
|
-
def read_config():
|
|
398
|
-
config_path = Path(".moai/config.json")
|
|
399
|
-
with open(config_path, "r", encoding="utf-8") as f:
|
|
400
|
-
return json.load(f)
|
|
401
|
-
|
|
402
|
-
config = read_config()
|
|
403
|
-
user_nickname = config.get("user", {}).get("nickname", "Developer")
|
|
404
|
-
conversation_language = config["project"]["conversation_language"]
|
|
405
|
-
```
|
|
406
|
-
|
|
407
|
-
### Updating User Nickname
|
|
408
|
-
|
|
409
|
-
```python
|
|
410
|
-
import json
|
|
411
|
-
from pathlib import Path
|
|
412
|
-
|
|
413
|
-
def update_user_nickname(new_nickname: str):
|
|
414
|
-
config_path = Path(".moai/config.json")
|
|
415
|
-
|
|
416
|
-
# Read existing config
|
|
417
|
-
with open(config_path, "r", encoding="utf-8") as f:
|
|
418
|
-
config = json.load(f)
|
|
419
|
-
|
|
420
|
-
# Update user section
|
|
421
|
-
if "user" not in config:
|
|
422
|
-
config["user"] = {}
|
|
423
|
-
config["user"]["nickname"] = new_nickname
|
|
424
|
-
|
|
425
|
-
# Write back
|
|
426
|
-
with open(config_path, "w", encoding="utf-8") as f:
|
|
427
|
-
json.dump(config, f, indent=2, ensure_ascii=False)
|
|
428
|
-
```
|
|
429
|
-
|
|
430
|
-
---
|
|
431
|
-
|
|
432
|
-
## 📝 Notes
|
|
433
|
-
|
|
434
|
-
- **SSOT**: This config file is the single source of truth for all project settings
|
|
435
|
-
- **No Duplication**: Never duplicate config values in code or other files
|
|
436
|
-
- **Encoding**: Always use UTF-8 encoding to support international characters in nicknames and language names
|
|
437
|
-
- **Validation**: Always validate config structure before reading values
|
|
438
|
-
- **Migration**: When adding new fields, provide sensible defaults for backwards compatibility
|
|
439
|
-
|
|
440
|
-
---
|
|
441
|
-
|
|
442
|
-
**Last Updated**: 2025-10-23
|
|
443
|
-
**Version**: v0.4.6
|
|
444
|
-
**Maintainer**: cc-manager
|
|
File without changes
|
|
File without changes
|
|
File without changes
|