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.
119 lines
4.3 KiB
Python
119 lines
4.3 KiB
Python
#!/usr/bin/env -S uv run
|
|
# /// script
|
|
# dependencies = []
|
|
# ///
|
|
"""make_adw — generate a new one-shot ADW script from agents in the config.
|
|
|
|
Usage:
|
|
uv run <skill>/scripts/make_adw.py --name review_docs --agents scout,builder
|
|
|
|
Each named agent becomes one agent phase, chained by envelope. Starter agents
|
|
map to their concrete output types; unknown agents get GenericOutput (define a
|
|
concrete type in adw_modules/data_types.py and swap it in).
|
|
"""
|
|
|
|
import argparse
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
OUTPUT_TYPES = {"planner": "PlanOutput", "builder": "BuildOutput",
|
|
"scout": "ScoutOutput",
|
|
"reviewer": "ReviewOutput", "documenter": "DocumentOutput"}
|
|
|
|
HEADER = '''#!/usr/bin/env -S uv run
|
|
# /// script
|
|
# dependencies = ["pydantic", "python-dotenv", "pyyaml"]
|
|
# ///
|
|
"""ADW {title} — generated by make_adw.
|
|
|
|
Usage:
|
|
uv run adws/adw_{name}.py "<prompt or path/to/prompt.md>" [--config adws/adw_sssf_config/sssf.config.yaml] [--adw-id a1b2c3d4]
|
|
|
|
Phases: engineer(request) -> {chain}
|
|
"""
|
|
|
|
import argparse
|
|
import sys
|
|
|
|
from adw_modules import agents, gates, session, utils
|
|
from adw_modules.data_types import AgentCall, PhaseParams, {imports}
|
|
|
|
REQUIRED_AGENTS = {agents_list}
|
|
|
|
|
|
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)
|
|
|
|
with run.phase(PhaseParams(name="request", kind="engineer", owner=run.engineer,
|
|
description="Capture the incoming ask")) as ph:
|
|
ph.log(input=prompt)
|
|
|
|
previous = None
|
|
{phases}
|
|
return 0 if run.succeeded else 1
|
|
|
|
|
|
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))
|
|
'''
|
|
|
|
PHASE = ''' # TODO: replace this description — say what THIS phase does and why.
|
|
with run.phase(PhaseParams(name="{name}", kind="agent", owner="{agent}",
|
|
description="Run {agent} over the request and hand its envelope on")) as ph:
|
|
previous = ph.call(AgentCall(output_type={output_type}, prompt=prompt,
|
|
previous=previous,
|
|
gates=[gates.artifacts_exist]))
|
|
'''
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--name", required=True, help="snake_case adw name")
|
|
parser.add_argument("--agents", required=True, help="comma-separated agent names, in order")
|
|
parser.add_argument("--force", action="store_true")
|
|
args = parser.parse_args()
|
|
|
|
agent_names = [a.strip() for a in args.agents.split(",") if a.strip()]
|
|
if not agent_names:
|
|
print("no agents given")
|
|
return 1
|
|
|
|
types = [OUTPUT_TYPES.get(a, "GenericOutput") for a in agent_names]
|
|
seen: dict[str, int] = {}
|
|
phases = []
|
|
for agent, output_type in zip(agent_names, types):
|
|
seen[agent] = seen.get(agent, 0) + 1
|
|
phase_name = agent if seen[agent] == 1 else f"{agent}_{seen[agent]}"
|
|
phases.append(PHASE.format(name=phase_name, agent=agent, output_type=output_type))
|
|
|
|
body = HEADER.format(
|
|
title=args.name.replace("_", " ").title(),
|
|
name=args.name,
|
|
chain=" -> ".join(agent_names),
|
|
imports=", ".join(sorted(set(types))),
|
|
agents_list=repr(sorted(set(agent_names))),
|
|
phases="\n".join(phases),
|
|
)
|
|
|
|
dest = Path.cwd() / "adws" / f"adw_{args.name}.py"
|
|
if dest.exists() and not args.force:
|
|
print(f"{dest} already exists — use --force to overwrite")
|
|
return 1
|
|
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
dest.write_text(body)
|
|
print(f"wrote {dest}")
|
|
print("next: replace each phase description — a generated one says nothing, "
|
|
"and the description is the only intent the trace ever shows")
|
|
print(f"run: uv run adws/adw_{args.name}.py \"your request\"")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|