DazedTL/util/skills/setup.py
DazedAnon 38ca8e44be fix(workflow): scope game tools and externalize prompts
- Install UberWolf CLI only for detected WOLF games
- Move static clipboard prompts into editable shipped skills
- Improve plugin and Ace translation audit workflows
- Add regression coverage for engine filtering and prompt loading
2026-07-25 14:23:27 -05:00

75 lines
2.5 KiB
Python

"""Load editable clipboard skills from data/skills/."""
from __future__ import annotations
from pathlib import Path
from util.paths import SKILLS_DIR
_ENGINE_MARKERS = {
"rpgmaker": ("<!-- engine:rpgmaker -->", "<!-- /engine:rpgmaker -->"),
"wolf": ("<!-- engine:wolf -->", "<!-- /engine:wolf -->"),
}
def _read_skill_file(name: str) -> str:
path = SKILLS_DIR / name
if not path.is_file():
raise FileNotFoundError(f"Skill file missing: {path}")
return path.read_text(encoding="utf-8")
def _extract_engine_section(text: str, engine: str) -> str:
"""Keep shared body + the requested engine block; drop other engine blocks."""
start_tag, end_tag = _ENGINE_MARKERS.get(engine, (None, None))
if not start_tag:
return text
result_parts: list[str] = []
pos = 0
while pos < len(text):
next_starts = [
(text.find(marker[0], pos), eng, marker)
for eng, marker in _ENGINE_MARKERS.items()
if text.find(marker[0], pos) != -1
]
if not next_starts:
result_parts.append(text[pos:])
break
next_starts.sort(key=lambda x: x[0])
idx, eng, (s_tag, e_tag) = next_starts[0]
result_parts.append(text[pos:idx])
end_idx = text.find(e_tag, idx)
if end_idx == -1:
raise ValueError(f"Unclosed engine block {s_tag} in skill file")
block_body = text[idx + len(s_tag) : end_idx]
if eng == engine:
result_parts.append(block_body.strip("\n") + "\n")
pos = end_idx + len(e_tag)
return "".join(result_parts).strip() + "\n"
def load_project_setup(engine: str = "rpgmaker", *, prepend: str = "") -> str:
"""Load the Project Setup clipboard skill for *engine*."""
raw = _read_skill_file("project_setup.md")
body = _extract_engine_section(raw, engine)
if prepend:
return prepend.rstrip() + "\n\n" + body
return body
def load_clipboard_skill(name: str) -> str:
"""Load a static clipboard prompt from ``data/skills``.
Read on every copy so edits made in the Skills tab take effect immediately.
Only a plain Markdown filename is accepted to keep callers inside the shipped
skills directory.
"""
path_name = Path(name)
if path_name.name != name or path_name.suffix.casefold() != ".md":
raise ValueError(f"Invalid clipboard skill filename: {name!r}")
return _read_skill_file(name).strip() + "\n"
def skills_dir() -> Path:
return SKILLS_DIR