AGENTS.md · git:20260504.2a64374 · 2026-05-04 · sha256 720aea01fac2935c
AGENTS.md git:20260504.2a64374A
Immutable. This exact content is served forever at /api/v1/blob/720aea01fac2935c.
# AGENTS.md
Guidance for AI agents working in the capabilities repo.
Manifest fields, component types (agents / skills / tools / MCP / workers), and discovery rules live in the public docs at [docs.dreadnode.io/capabilities/overview](https://docs.dreadnode.io/capabilities/overview/). Read those before adding or restructuring a capability — this file covers repo-local conventions only.
---
## Layout
All capabilities live under `capabilities/`. Each is a directory with `capability.yaml` at its root plus any combination of `agents/`, `skills/`, `tools/`, `mcp/`, `workers/`, `tests/`.
---
## Validation
```bash
just validate # every capability
pre-commit run --files <paths> # ruff, ruff-format, check-yaml, gitleaks
uv run --script <cap>/mcp/test_server.py # when the capability ships tests
```
Run these before declaring work complete. Don't bypass hooks — fix the underlying issue.
---
## Authoring LLM-facing tools
- **Name the knob, not the mode.** A flag toggles what gets registered, it isn't the resulting mode. `apollo: on/off` beats `readonly: on/off`.
- **Defaults match the prescribing skill.** If the skill tells agents to pass `active_only=True`, that's the default. A default that contradicts documented usage is a trap.
- **Uniform return shapes within a family.** Don't mix `str` and `dict` returns across sibling tools — it forces the LLM to pattern-match.
- **Raise for failures; no `{"error": str}` unions.** FastMCP surfaces raised exceptions as tool-call errors. Clean for the LLM, clean for the return type.
- **Plain dicts, null-omitted, over Pydantic.** LLMs pay tokens for every null-coerced empty field. Return `dict[str, Any]`, drop `None`/`""`/`[]`/`{}`, keep `0` and `False`.
- **Disambiguate sibling tools by name.** Encode actor + action + destination when tools are close. Overloading one verb (three `download*` variants) is a pick-the-wrong-one landmine.
- **After renames, grep the skill/agent/doc layer.** Markdown callers don't fail a build, and LLMs read that prose to form tool-call intent.
---
## Reviewing tool layers
When asked "is this defensible?" or "good enough?", audit unit-by-unit — name clarity, arg disambiguity, return shape, docstring usefulness, defaults vs. prescribed usage — and return the honest punch list. The question is an invitation to find what isn't working, not a request for a rubber stamp.
---
## Design rules
- **Don't propose deleting a feature with active consumers.** A tool referenced by a skill, agent, or downstream capability is a sibling surface, not cruft. Subtraction applies only when the feature has actually lost its purpose. Ask before excising.
- **Check existing patterns first.** If other capabilities, hooks, or MCP servers already solve the concern, follow that pattern. Diverge only with a concrete reason.
- **Simplest fix first.** Don't introduce abstractions, plumbing, or shield patterns unless asked.
---
## MUST
- Run `just validate` and pre-commit before declaring work complete
- Semver-bump `capability.yaml` on breaking changes to a capability's public surface
- Type hints on public Python functions
- Update skill / agent markdown when tool names or args change
## NEVER
- Commit directly to `main` — use a feature branch and PR
- Bypass pre-commit hooks (`--no-verify`) to ship
- Invent tool / agent / MCP names — grep first
- Delete a capability, skill, or tool without confirming downstream consumers
## Ask first
- Deleting files
- Renaming tools, agents, or skills (ripples into markdown callers)
- Changing capability-level flags or env var contracts