Skip to content
This repository was archived by the owner on Oct 11, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ Plateforme d'analyse de combats WvW. Les contrats et conventions détaillés viv

- Exécuter les outils Python via `uv run`; une invocation Python nue contourne l'environnement du workspace.
- Itérer avec les tests ciblés; la suite Python complète impose une couverture globale de 90 % et peut nécessiter les services Docker pour les tests d'intégration.
- Les skills BMAD vivent dans `.agents/skills/bmad-*` (chargés via `skills.paths` d'`opencode.json`) et le framework dans `_bmad/`. Le noyau (`resolve_config.py`, `resolve_customization.py`, `render_skill.py`, `memlog.py`) ne dépend que de la stdlib — vérifiable via `tests/scripts/test_bmad_framework.py`.
- opencode ne substitue aucune variable dans les skills (à la différence de Claude Code) : remplacer manuellement `{project-root}` par la racine du repo (`/home/roddy/Work/Gw2Analytics`) et `{skill-root}` par le répertoire du skill invoqué (le bloc « Base directory » de l'outil skill).

## Conventions that differ from defaults

Expand Down
3 changes: 3 additions & 0 deletions opencode.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
"plugin": ["@dietrichgebert/ponytail"],
"instructions": [],
"references": {},
"skills": {
"paths": [".agents/skills"]
},
"mcp": {
"postgres": {
"type": "local",
Expand Down
60 changes: 60 additions & 0 deletions tests/scripts/test_bmad_framework.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# ruff: noqa: S603
import json
import re
import subprocess
import sys
from pathlib import Path

import pytest
import yaml

ROOT = Path(__file__).resolve().parents[2]
BMAD_SCRIPTS = ROOT / "_bmad" / "scripts"
SKILLS_DIR = ROOT / ".agents" / "skills"

FRONTMATTER_RE = re.compile(r"\A---\n(.*?)\n---\n?", re.DOTALL)


@pytest.mark.parametrize(
"script",
["resolve_config.py", "resolve_customization.py", "render_skill.py", "memlog.py"],
)
def test_bmad_framework_script_is_invokable(script):
proc = subprocess.run(
[
sys.executable,
str(BMAD_SCRIPTS / script),
"--help",
],
check=False,
capture_output=True,
text=True,
)
assert proc.returncode == 0, proc.stderr


def test_bmad_framework_resolve_config_merges_project():
proc = subprocess.run(
[sys.executable, str(BMAD_SCRIPTS / "resolve_config.py"), "--project-root", str(ROOT)],
capture_output=True,
text=True,
check=False,
)
assert proc.returncode == 0, proc.stderr
config = json.loads(proc.stdout)
assert config["core"]["project_name"] == "Gw2Analytics"
assert "bmad-agent-analyst" in config["agents"]


def test_bmad_skills_frontmatter_is_opencode_loadable():
skills = sorted(dir_ for dir_ in SKILLS_DIR.iterdir() if dir_.is_dir())
assert skills, "no skills under .agents/skills/"
for skill_dir in skills:
skill_md = skill_dir / "SKILL.md"
assert skill_md.is_file(), f"{skill_dir.name}/SKILL.md missing"
match = FRONTMATTER_RE.match(skill_md.read_text())
assert match, f"{skill_dir.name}/SKILL.md has no frontmatter"
meta = yaml.safe_load(match.group(1))
assert isinstance(meta, dict), f"{skill_dir.name}: frontmatter not a mapping"
assert meta.get("name") == skill_dir.name, f"{skill_dir.name}: name mismatch"
assert meta.get("description"), f"{skill_dir.name}: description missing"
Loading