basic BMAD setup
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
---
|
||||
name: {name}
|
||||
description: '{description}'
|
||||
---
|
||||
# {title}
|
||||
|
||||
{purpose: one or two lines on what the script does and what the result is for.} The script does the work; this file says how to run it and what to do with what comes back.
|
||||
|
||||
## Run
|
||||
|
||||
```bash
|
||||
uv run {skill-root}/scripts/{script}.py {arguments}
|
||||
```
|
||||
|
||||
`--help` lists every option. {When to pass which option, one line each, only where the choice takes judgment.}
|
||||
|
||||
## Read the result
|
||||
|
||||
{The output shape: the JSON keys and what each means, or what the text report shows. What a good result looks like, and what each kind of finding asks you to do next.}
|
||||
|
||||
## When it fails
|
||||
|
||||
A non-zero exit prints one line on stderr saying why. Report that line to the user; do not do the job by hand instead. {Failures the user can fix and what to tell them: a missing input, a malformed file, a path outside the project.}
|
||||
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env python3
|
||||
# /// script
|
||||
# requires-python = ">=3.11"
|
||||
# ///
|
||||
"""{script} — {purpose}.
|
||||
|
||||
{What the script reads, what it writes, and the shape of its output. This docstring is the
|
||||
`--help` text, so it is the whole reference: say what each result means.}
|
||||
|
||||
Exit codes: 0 with the result as JSON on stdout; 1 with a one-line reason on stderr.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class ScriptError(Exception):
|
||||
"""A failure the user can act on. Its message is the one line printed on exit."""
|
||||
|
||||
|
||||
def run(path: Path, *, strict: bool = False) -> dict:
|
||||
"""{The work. Return the result as a dict so main() can print it as JSON.}"""
|
||||
if not path.exists():
|
||||
raise ScriptError(f"not found: {path}")
|
||||
return {"path": str(path), "strict": strict}
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
parser.add_argument("path", type=Path, help="{what the positional input is}")
|
||||
parser.add_argument("--strict", action="store_true", help="{what strict changes}")
|
||||
args = parser.parse_args(argv)
|
||||
try:
|
||||
result = run(args.path, strict=args.strict)
|
||||
except ScriptError as error:
|
||||
print(f"{parser.prog}: {error}", file=sys.stderr)
|
||||
return 1
|
||||
print(json.dumps(result, ensure_ascii=False, indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,38 @@
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
SCRIPT = Path(__file__).resolve().parents[1] / "{script}.py"
|
||||
|
||||
|
||||
def run(*args: str) -> subprocess.CompletedProcess[str]:
|
||||
"""Run the script as a subprocess, the way the agent does."""
|
||||
return subprocess.run([sys.executable, str(SCRIPT), *args], capture_output=True, text=True, check=False)
|
||||
|
||||
|
||||
class ScriptTest(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
tmp = tempfile.TemporaryDirectory()
|
||||
self.addCleanup(tmp.cleanup)
|
||||
self.root = Path(tmp.name)
|
||||
|
||||
def test_reports_the_result(self) -> None:
|
||||
fixture = self.root / "input.txt"
|
||||
fixture.write_text("{fixture contents}\n", encoding="utf-8")
|
||||
result = run(str(fixture))
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
data = json.loads(result.stdout)
|
||||
self.assertEqual(data["path"], str(fixture))
|
||||
|
||||
def test_fails_with_one_line(self) -> None:
|
||||
result = run(str(self.root / "missing.txt"))
|
||||
self.assertEqual(result.returncode, 1)
|
||||
self.assertEqual(result.stderr.count("\n"), 1, result.stderr)
|
||||
self.assertIn("not found", result.stderr)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user