The Problem
Every major AI agent framework has arrived at the same idea: skills. A text file — usually Markdown — describing what an agent should do. "Here's how to query a database." "Here's how to send an email." "Here's how to deploy to production."
Skills are a great start. They're portable. They're human-readable. They're sharable. But they have a fundamental flaw:
A skill tells an AI what to do. It does not tell it how. When you hand an LLM a text file and say "do this," you're trusting the model to interpret, implement, and execute correctly — every single time. It won't.
You'll see this play out in predictable stages:
- Skills launch. Everyone's excited. "Just write Markdown and the AI does it!"
- Quality issues emerge. The same skill produces different results each run. Edge cases break.
- Plugins get added. "OK, let the skill call deterministic code." Now you have two files — a skill AND a plugin.
- Security surfaces. Malicious skills appear. There's no contract, no structure — just freeform text the AI trusts blindly.
- Everyone realizes: You need documentation AND code AND a contract, all in one portable unit.
We've been there. We solved it over a year ago.
The Single File Agent
A Single File Agent is exactly what it sounds like: one file containing everything an agent needs to exist — its documentation, its metadata contract, and its deterministic code.
All three layers live in a single .py or .ts file.
The documentation IS the file. The contract IS the file. The code IS the file.
There is no separation to manage, no files to keep in sync, no drift between what
the skill says and what the code does.
import json
from openrappter.agents.basic_agent import BasicAgent
class WeatherPoetAgent(BasicAgent):
def __init__(self):
self.name = 'WeatherPoet'
self.metadata = {
"name": self.name,
"description": "Fetches weather and writes haiku poetry",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "City name to get weather for"}
},
"required": []
}
}
super().__init__(name=self.name, metadata=self.metadata)
def perform(self, **kwargs):
query = kwargs.get('query', '')
weather = self.fetch_weather(query)
haiku = self.compose_haiku(weather)
return json.dumps({
"status": "success",
"haiku": haiku,
"data_slush": {"mood": weather["condition"], "temp_f": weather["temp"]}
})
The metadata contract is defined right in the constructor using a native Python dict. No parsing, no magic — just code that speaks for itself.
Why This Matters
❌ Skills Alone
- AI interprets freely → inconsistent results
- No version control on behavior
- Security is "trust the text file"
- Documentation drifts from implementation
- Need plugins for determinism (now it's 2+ files)
- Can't test a Markdown file
✅ Single File Agents
- Deterministic perform() — same input, same output
- Semantic versioning in the contract
- Auditable code with bounded behavior
- Docs and code are literally the same file
- One file. Always one file.
- Standard unit tests, standard CI/CD
The Five Principles
Deterministic
An agent's perform() does the same thing every time for the same inputs. The AI orchestrator decides when to call it — the agent decides how to execute.
Self-Describing
The file contains its own documentation, its own contract, and its own implementation. Drop it anywhere and it works. No external manifest, no companion files.
Bounded
The metadata dict defines exactly what parameters the agent accepts. The code defines exactly what it does with them. There are no surprises — an agent can't go through doors it wasn't given.
Evolvable
Single file agents can be generated, molted, and versioned through natural language. "Make my news agent also include Reddit." The agent evolves — the contract stays clean.
Chainable
Agents output data_slush — curated signals from live results that feed into the next agent's context automatically. No LLM interpreter needed between steps.
How It Works
Python
The metadata contract is defined as a native Python dict in the constructor. No YAML, no config files, no magic parsing — just straightforward code.
class ShellAgent(BasicAgent):
def __init__(self):
self.name = 'Shell'
self.metadata = {
"name": self.name,
"description": "Execute shell commands and file operations",
"parameters": {
"type": "object",
"properties": {
"command": {"type": "string", "description": "Shell command to execute"}
},
"required": []
}
}
super().__init__(name=self.name, metadata=self.metadata)
def perform(self, **kwargs):
# self.context → enriched by data sloshing
...
TypeScript
TypeScript defines metadata as a native object passed to the super() constructor.
Same pattern, same clarity — no YAML, no static strings to parse.
export class ShellAgent extends BasicAgent {
constructor() {
const metadata: AgentMetadata = {
name: 'Shell',
description: 'Execute shell commands and file operations',
parameters: {
type: 'object',
properties: { command: { type: 'string', description: 'Shell command to execute' } },
required: []
}
};
super('Shell', metadata);
}
async perform(kwargs: Record<string, unknown>): Promise<string> {
// this.context → enriched by data sloshing
...
}
}
Agent Generation
The pattern gets more powerful when agents generate other agents.
The LearnNew meta-agent creates single file agents from natural language:
LearnNew generates a complete single file agent — metadata, documentation, and working code —
writes it to the agents/ directory, and hot-loads it for immediate use.
The generated agent follows the exact same pattern. It's self-describing from birth.
Because the output is a single file with a deterministic contract, generated agents are:
- Publishable — push directly to RappterHub (no separate manifest to write)
- Testable — standard unit tests against
perform() - Auditable — read one file to understand everything the agent can do
- Moltable — evolve through feedback: "add Reddit sources" → new generation, same contract
Implicit Context: Data Sloshing
Every single file agent inherits data sloshing — automatic context enrichment
before perform() runs. The agent wakes up already knowing:
- ⏰ Temporal — time of day, day of week, fiscal period, urgency signals
- 🧠 Memory — relevant past interactions surfaced by overlap
- 🎯 Query — specificity, ownership hints, temporal references
- 👤 Behavioral — user's technical level, brevity preference
- 🧊 Upstream Slush — live signals from the previous agent in a chain
The agent doesn't ask for this context. It doesn't configure it. It's just there,
in self.context, before the first line of perform() executes.
This is what makes the pattern biological — the agent wakes up oriented, not blank.
The Roadmap Is Already Playing Out
Watch the AI ecosystem over the next few months. You'll see this exact progression:
- ✅ Skills — flat text files. Already mainstream.
- 🔄 Plugins — deterministic code called by skills. Arriving now.
- 🔜 Unified format — skill + plugin + contract merged into one file.
- 🔜 Agent registries — sharing and installing single file agents.
- 🔜 Agent generation — AI creating agents that create agents.
We're already at step 5. The single file agent pattern has been running in production for over a year — generating agents, chaining them, molting them through feedback loops, and deploying them across endpoints.
There is no separation to manage. No skill file that drifts from the code. No manifest that gets out of date. No plugin that doesn't match the schema. One file. One truth. Deterministic, portable, evolvable.
RappterHub: The Registry
RappterHub is the package registry built on the single file agent standard.
Publishing is simple — your agent file already contains its manifest, documentation, and code.
No separate AGENT.md or package.json needed.
# Publish your single file agent
$ rappterhub publish ./my_agent.py
# Others install it with one command
$ rappterhub install kody-w/weather-poet
✓ Installed weather-poet v1.0.0
# It works immediately — no configuration
$ openrappter --exec WeatherPoet "Tokyo"