Creating New Agents
Create specialized agents from scratch using TypeScript files in the .agents/
directory.
Types:
- LLM-based - Use prompts and language models
- Programmatic - Use TypeScript generator functions with
handleSteps
Control Flow:
yield 'STEP'
- Run one LLM generation stepyield 'STEP_ALL'
- Run until completionreturn
- End the agent's turn
Accessing Context:
agentState
- Current agent state and message historyprompt
- User's prompt to the agentparams
- Additional parameters passed to the agent
Basic Structure
Create a new TypeScript file in .agents/
directory:
.agents/my-custom-agent.ts
import { AgentDefinition } from './types/agent-definition'const definition: AgentDefinition = {id: "my-custom-agent",version: "1.0.0",displayName: "My Custom Agent",spawnerPrompt: "Spawn this agent for specialized workflow tasks requiring custom logic",model: "anthropic/claude-4-sonnet-20250522",outputMode: "last_message",includeMessageHistory: true,toolNames: ["read_files", "write_file", "end_turn"],spawnableAgents: ["codebuff/researcher@0.0.1"], // Use full name for built-in agentsinputSchema: {prompt: {type: "string",description: "What documentation to create or update"}},systemPrompt: `You are a documentation specialist.`,instructionsPrompt: "Create comprehensive documentation based on the user's request. Research existing code and patterns first.",stepPrompt: "Continue working on the documentation. Use end_turn when complete."}export default definition
Domain-Specific Examples
API Documentation Agent
Specialized for documenting REST APIs and GraphQL schemas:
.agents/api-documenter.ts
import { AgentDefinition } from './types/agent-definition'const definition: AgentDefinition = {id: "api-documenter",version: "1.0.0",displayName: "API Documentation Specialist",spawnerPrompt: "Spawn this agent to create comprehensive API documentation with examples, schemas, and error codes",model: "anthropic/claude-4-sonnet-20250522",outputMode: "last_message",includeMessageHistory: true,toolNames: ["read_files", "code_search", "write_file", "spawn_agents", "end_turn"],spawnableAgents: ["codebuff/researcher@0.0.1"], // Use full name for built-in agentsinputSchema: {prompt: {type: "string",description: "What API endpoints or schemas to document"}},systemPrompt: "You are an API documentation specialist. Create clear, comprehensive documentation for REST APIs and GraphQL schemas with examples, request/response formats, and error codes.",instructionsPrompt: "Analyze the specified API endpoints and create detailed documentation including examples, parameters, and response schemas.",stepPrompt: "Continue documenting the API. Include practical examples and edge cases. Use end_turn when complete."}export default definition
Database Migration Agent
Specialized for creating and reviewing database migrations:
.agents/migration-specialist.ts
import { AgentDefinition } from './types/agent-definition'const definition: AgentDefinition = {id: "migration-specialist",version: "1.0.0",displayName: "Database Migration Specialist",spawnerPrompt: "Spawn this agent to create safe, reversible database migrations with proper indexing and rollback procedures",model: "anthropic/claude-4-sonnet-20250522",outputMode: "last_message",includeMessageHistory: true,toolNames: ["read_files","write_file","code_search","run_terminal_command","end_turn"],spawnableAgents: ["codebuff/reviewer@0.0.1"],systemPrompt: "You are a database migration specialist. Your goal is to create safe, reversible database migrations with proper indexing and rollback procedures.",instructionsPrompt: "Create a database migration for the requested schema changes. Ensure it's reversible and includes proper indexing.",stepPrompt: "Continue working on the migration. Test it if possible and spawn a reviewer to check for issues."}export default definition
Programmatic Agents (Advanced)
🎯 This is where Codebuff agents become truly powerful! While LLM-based agents work well for many tasks, programmatic agents give you precise control over complex workflows, while still letting you tap into LLMs when you want them.
Why Use Programmatic Agents?
- Deterministic workflows - Guarantee specific steps happen in order
- Dynamic decision making - Branch based on your own logic
- Complex orchestration - Coordinate multiple agents and tools with logic
- State management - Maintain state across multiple agent steps
How It Works
Use TypeScript generator functions with the handleSteps
field to control execution:
.agents/code-analyzer.ts
import { AgentDefinition } from './types/agent-definition'const definition: AgentDefinition = {id: "code-analyzer",displayName: "Code Analysis Expert",spawnerPrompt: "Spawn for deep code analysis and refactoring suggestions",model: "anthropic/claude-4-sonnet-20250522",toolNames: ["read_files", "code_search", "spawn_agents", "write_file"],spawnableAgents: ["codebuff/thinker@0.0.1", "codebuff/reviewer@0.0.1"],handleSteps: function* ({ agentState, prompt, params }) {// First, find relevant filesconst { toolResult: files } = yield {toolName: 'find_files',input: { query: prompt }}// Read the most important filesif (files) {const filePaths = JSON.parse(files).slice(0, 5)yield {toolName: 'read_files',input: { paths: filePaths }}}// Spawn a thinker for deep analysisyield {toolName: 'spawn_agents',input: {agents: [{agent_type: 'thinker',prompt: `Analyze the code structure and suggest improvements for: ${prompt}`}]}}// Let the agent generate its responseyield 'STEP_ALL'}}export default definition
Key Concepts for Programmatic Agents
1. Generator Function Basics
Your handleSteps
function receives context and yields actions:
handleSteps: function* ({ agentState, prompt, params }) {// agentState: Current conversation and agent state// prompt: What the user asked this agent to do// params: Additional parameters passed to the agent// Your logic here...}
2. Yielding Tool Calls
Execute tools and get their results:
const { toolResult, toolError } = yield {toolName: 'read_files',input: { paths: ['file1.ts', 'file2.ts'] }}if (toolError) {// Handle error caseconsole.error('Failed to read files:', toolError)} else {// Use the resultconst fileContent = JSON.parse(toolResult)}
3. Control Flow Options
Control Flow:
yield 'STEP'
- Run one LLM generation stepyield 'STEP_ALL'
- Run until completionreturn
- End the agent's turn
4. Advanced Example: Conditional Workflow
handleSteps: function* ({ agentState, prompt, params }) {// Step 1: Analyze the codebaseconst { toolResult: analysis } = yield {toolName: 'spawn_agents',input: {agents: [{agent_type: 'thinker',prompt: `Analyze: ${prompt}`}]}}// Step 2: Based on analysis, choose actionif (analysis?.includes('refactor')) {// Get all files that need refactoringconst { toolResult: files } = yield {toolName: 'find_files',input: { query: 'needs refactoring' }}// Step 3: Refactor each filefor (const file of JSON.parse(files || '[]')) {yield {toolName: 'write_file',input: {path: file,instructions: 'Refactor for better performance',content: '// ... refactored code ...'}}}}// Step 4: Final reviewyield {toolName: 'spawn_agents',input: {agents: [{agent_type: 'reviewer',prompt: 'Review all changes'}]}}// Let the agent summarizeyield 'STEP_ALL'}
When to Choose Programmatic vs LLM-based
Use Programmatic (handleSteps
) when:
- You need guaranteed execution order
- Decisions depend on specific file contents
- Complex multi-step workflows with branching
- Integration with external systems
- Error recovery is critical
Use LLM-based (prompts only) when:
- Task is straightforward
- Agent needs creative freedom
- Natural language understanding is key
- Workflow is simple and linear