Skip to content

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 a SKILL.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

uv tool install tuvl
tuvl init my-app --sample
cd my-app

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:

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 Customer model with name, email, and tier, then a workflow at POST /api/tickets that 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:

tuvl validate

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.md and the relevant skill in .agents/skills/, then add a Customer model and a POST /api/tickets workflow where an autonomous agent looks up the customer's orders and resolves or escalates. Run tuvl validate when you're done."

Claude Code

  1. Run claude from the project root — Claude Code reads the root AGENTS.md (and CLAUDE.md) automatically, and can open any .agents/skills/<name>/SKILL.md on demand.
  2. (Optional) surface the recipes as first-class Claude Code skills:
    mkdir -p .claude && ln -s ../.agents/skills .claude/skills
    
  3. 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

  1. Open the project folder in Antigravity — it reads the root AGENTS.md as workspace rules/context for its agent.
  2. (Optional) add .agents/skills/ to the workspace context so the agent can pull a specific recipe (e.g. build-autonomous-agent).
  3. 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-default signal a step can emit must be mapped in routes:. For an AutonomousAgent, that means every outcome.enum value plus the reserved exits max_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 in nodes/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:

workflows/triage_ticket.yaml
- 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