Add sssf skill, installable via the skills CLI

Port the sssf skill from ~/.agents/skills/sssf into this repo so it can be
distributed and installed with the skills CLI (skills add INDigitalStudio/skills
--skill sssf).

- Copy the skill (SKILL.md, cookbooks, references, scripts, templates, and the
  visualizer app source) into sssf/.
- Gitignore build/runtime artifacts: the visualizer's node_modules/ and dist/,
  Python bytecode, and the machine-specific repos.json.
- Make the skill location-independent: install.py now stamps the skill's real
  path into the stamped justfile's skill_dir (replacing the hardcoded
  ~/.agents/skills/sssf), so 'just obs' finds the visualizer wherever the CLI
  installed the skill.
- Update cookbooks to use <skill>/scripts/... instead of the hardcoded path,
  and document the skills CLI install command.
- Update the repo README with install instructions.
This commit is contained in:
INDigitalStudio 2026-08-09 21:00:28 +00:00
parent a42608f602
commit 2cc766aabe
98 changed files with 12508 additions and 0 deletions

View file

@ -0,0 +1,79 @@
#!/usr/bin/env -S uv run
# /// script
# dependencies = ["pydantic", "python-dotenv", "pyyaml", "rich"]
# ///
"""ADW Build Test — implement, then verify; failures flow back into the builder.
Usage:
uv run adws/adw_build_test.py "<prompt or path/to/prompt.md>" [--config adws/adw_sssf_config/sssf.config.yaml] [--adw-id a1b2c3d4]
Phases: engineer(request) -> builder -> code(test) [-> builder(fix) -> code(test) ... bounded]
Testing is CODE. The suite's command is written down in adw_modules/quality.py,
so running it needs no judgement — only repairing it does. Failures reach the
builder as an envelope through `quality.as_envelope`, which is the same door an
agent's report came through, so the repair loop is unchanged.
A failing suite does NOT fail its phase: the runner did its job, the code is
what failed. It fails the run, checked at the end, after the bounded fix loop
has had its chances.
"""
import argparse
import sys
from adw_modules import agents, gates, quality, session, utils
from adw_modules.data_types import AgentCall, BuildOutput, PhaseParams
REQUIRED_AGENTS = ["builder"]
MAX_FIX_LOOPS = 3
def main(prompt: str, config: str = "adws/adw_sssf_config/sssf.config.yaml", adw_id: str | None = None) -> int:
cfg = agents.load_config(config)
agents.validate(cfg, REQUIRED_AGENTS)
run = session.ensure(cfg, adw_id)
def record(ph, result) -> None:
passed = sum(1 for check in result.checks if check.passed)
ph.log(passed=result.passed, checks=f"{passed}/{len(result.checks)}",
artifacts=", ".join(result.artifacts))
with run.phase(PhaseParams(name="request", kind="engineer", owner=run.engineer,
description="Capture the incoming ask")) as ph:
ph.log(input=prompt)
with run.phase(PhaseParams(name="build", kind="agent", owner="builder",
description="Implement the request")) as ph:
previous = ph.call(AgentCall(output_type=BuildOutput, prompt=prompt,
gates=[gates.diff_matches_claims]))
test = None
for i in range(1, MAX_FIX_LOOPS + 1):
with run.phase(PhaseParams(name=f"test_{i}", kind="code", owner="quality",
description="Run the suite — a known command, so code runs "
"it and no agent has to rediscover it")) as ph:
test = quality.run_tests(run)
record(ph, test)
if test.passed:
break
with run.phase(PhaseParams(name=f"fix_{i}", kind="agent", owner="builder", retries=1,
description="Repair what the suite reported, from its "
"verbatim output")) as ph:
previous = ph.call(AgentCall(output_type=BuildOutput, prompt=prompt,
previous=quality.as_envelope(test, "tests"),
gates=[gates.diff_matches_claims]))
return run.finish(accepted=test is not None and test.passed,
reason=f"the suite still failed after {MAX_FIX_LOOPS} fix attempt(s)")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("prompt", help="inline text or a path to a prompt file")
parser.add_argument("--config", default="adws/adw_sssf_config/sssf.config.yaml")
parser.add_argument("--adw-id", default=None, help="join or pin an existing session")
args = parser.parse_args()
sys.exit(main(utils.resolve_prompt(args.prompt), args.config, args.adw_id))