Generate Configuration with Coding Agents¶
tuvl is designed to be written by an AI coding agent — Claude Code, Cursor, Copilot, or any agent that edits files in your repo. Because a tuvl backend is a closed-set contract rather than open-ended code, an agent has a small, validated target and generates working configuration on the first try.
To make that reliable, tuvl init scaffolds two things specifically for coding agents:
AGENTS.md— the framework rules and architectural invariants (the closed sets, routing rules, the model allowlist, the one-node-per-file rule). Most agents read this file automatically..agents/skills/— a set of procedural skills, one folder per task (each aSKILL.md), that walk the agent through how to perform a specific job correctly.
Together with the authoritative TUVL Agentic Manual, these give an agent everything it needs to emit valid models, workflows, nodes, and agents.
The bundled skills¶
Each skill is a short, imperative recipe the agent follows. tuvl init writes these under .agents/skills/<name>/SKILL.md:
| Skill | What the agent does |
|---|---|
create-database-model |
Define a ModelDefinition (table + auto-generated CRUD). |
implement-api-endpoint |
Create a Workflow with an HTTP trigger and routed steps. |
create-custom-python-node |
Add a one-per-file @node() functional runner. |
perform-database-operations |
Use ModelOp for CRUD inside a workflow. |
implement-llm-agent-step |
Add a single-call agent step with structured output. |
build-autonomous-agent |
Add an AutonomousAgent — a bounded tool-calling loop. |
invoke-external-api |
Call an external HTTP API with APICall. |
execute-mcp-tool |
Call an MCP server tool with MCP. |
Step-by-step¶
1. Scaffold a project¶
This writes AGENTS.md, .agents/skills/, and a runnable sample (models, a workflow, nodes, tests). To skip the agent scaffolding, pass --no-ai-skills.
Already have a project?
Add the agent context to an existing project without re-scaffolding — download both files from the home page and drop them in your repo root:
AGENTS.md— framework rulesskills.zip— unzip into.agents/skills/
2. Point your agent at the project¶
Open the project in your coding agent. It picks up AGENTS.md automatically (Claude Code, Cursor, and most agents read a root AGENTS.md). If yours doesn't, tell it once: "Read AGENTS.md and .agents/skills/ before generating any tuvl config."
3. Ask in plain language¶
Describe what you want; the agent maps it to a skill and fills the closed-set schema:
"Add a
Customermodel with name, email, and tier, then a workflow atPOST /api/ticketsthat triages a support ticket: an autonomous agent looks up the customer's orders and either resolves or escalates."
The agent uses create-database-model, then build-autonomous-agent, declaring its tools as other steps and mapping every outcome in routes:.
4. Validate before running¶
The contract is enforced at load time — validate the generated YAML:
This catches an invalid kind:, an unmapped signal in routes:, a tool ref that doesn't resolve, a model missing from spec.context.models, or a node filename that doesn't match its @node() name — before the engine boots.
5. Inspect and test the run¶
- Spectrum — step through the generated workflow in the Insight portal and watch each node (including every autonomous-agent iteration and tool call) execute live.
tuvl test— run LLM-as-a-Judge test cases. Stub an agent step's output to make a nondeterministic agent deterministic for assertions.
Set up your coding agent¶
AGENTS.md follows the open AGENTS.md standard, so most agents
pick it up automatically the moment you open the project; the procedural skills
live in .agents/skills/ and are referenced from it. The setup differs slightly
per tool, but the prompt is the same everywhere — for example:
"Read
AGENTS.mdand the relevant skill in.agents/skills/, then add aCustomermodel and aPOST /api/ticketsworkflow where an autonomous agent looks up the customer's orders and resolves or escalates. Runtuvl validatewhen you're done."
Claude Code¶
- Run
claudefrom the project root — Claude Code reads the rootAGENTS.md(andCLAUDE.md) automatically, and can open any.agents/skills/<name>/SKILL.mdon demand. - (Optional) surface the recipes as first-class Claude Code skills:
- Prompt it with the request above (or "use the build-autonomous-agent
skill to add a triage agent"), review the diff, then
tuvl validate.
Google Antigravity¶
- Open the project folder in Antigravity — it reads the root
AGENTS.mdas workspace rules/context for its agent. - (Optional) add
.agents/skills/to the workspace context so the agent can pull a specific recipe (e.g.build-autonomous-agent). - Give the agent panel the request above; it plans and edits the YAML directly.
Validate with
tuvl validate.
Cursor¶
Cursor reads a root AGENTS.md automatically. If you prefer, add a one-line
.cursor/rules entry — "Follow AGENTS.md and .agents/skills/ for all tuvl
config." — then prompt in chat / Composer.
GitHub Copilot¶
Copilot reads AGENTS.md. If you also keep .github/copilot-instructions.md,
add a line pointing it at AGENTS.md and .agents/skills/, then prompt in chat
or agent mode.
Codex CLI & other AGENTS.md-aware agents¶
Any agent that follows the AGENTS.md standard (OpenAI Codex CLI, Aider, Jules,
…) reads the file with no setup — open the repo and prompt. If yours doesn't,
say it once: "Read AGENTS.md and .agents/skills/ before generating any tuvl
config."
The rules your agent follows¶
AGENTS.md encodes the agentic contract as hard rules. The ones that matter most:
- Closed sets only. Step kinds are exactly:
Functional,Agent,AutonomousAgent,Router,APICall,MCP,ModelOp,Response,HumanInTheLoop. Document kinds and reserved context keys are likewise fixed. The agent never invents new ones. - Route every signal. Every non-
defaultsignal a step can emit must be mapped inroutes:. For anAutonomousAgent, that means everyoutcome.enumvalue plus the reserved exitsmax_iterations/budget_exceeded/error/aborted. - Allowlist every model. Any model a workflow touches must be listed in
spec.context.models. - One node per file. A
@node("name")runner must live innodes/name.py.
Because these are validated at load time, a generation that breaks a rule fails fast instead of shipping a subtle bug.
Generated example: an autonomous agent¶
A prompt like "triage the ticket: look up the order, then resolve or escalate" produces config of this shape — the agent's tools are other declared steps, and a downstream router does the deterministic branching:
- id: triage
kind: AutonomousAgent
agent:
model: default
steering: "Resolve the support ticket using the available tools."
max_iterations: 8
skills: # project-relative .md files injected into the system prompt
- .agents/skills/support-policy.md
tools:
- ref: lookup_order
description: "Fetch order details by order id." # optional — overrides lookup_order's own description:
parameters:
type: object
properties: { order_id: { type: string } }
required: [order_id]
outcome:
enum: [resolved, escalate]
output_key: agent_result
routes:
resolved: route_by_region # deterministic switch, NOT the agent
escalate: notify_manager
max_iterations: fallback_summary
budget_exceeded: fallback_summary
error: alert_ops
- id: route_by_region
kind: Router
match: { field: customer.region }
routes: { US: reply_us, EU: reply_eu, default: reply_other }
See Workflows → Step Kinds for the full schema of every kind.
Next steps¶
- The Agentic Contract — why closed-set generation works
- Workflows — step kinds and routing reference
- Custom Nodes — the
functionalescape hatch - Testing Workflows — validate generated config with LLM-as-a-Judge