Codebuff

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 step
  • yield 'STEP_ALL' - Run until completion
  • return - End the agent's turn

Accessing Context:

  • agentState - Current agent state and message history
  • prompt - User's prompt to the agent
  • params - Additional parameters passed to the agent

Basic Structure

Create a new TypeScript file in .agents/ directory. This example uses the researcher as a spawnable subagent:

.agents/my-custom-agent.ts

typescript
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-sonnet-4.5',
outputMode: 'last_message',
includeMessageHistory: true,
toolNames: ['read_files', 'write_file', 'end_turn'],
spawnableAgents: ['codebuff/researcher@0.0.1'], // Use full name for built-in agents
inputSchema: {
prompt: {
type: 'string',
description: 'What documentation to create or update',
},
},
systemPrompt: `You are a documentation specialist.`,
instructionsPrompt:
"Write 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

Writes REST and GraphQL docs with examples. Uses the researcher for documentation lookup:

.agents/api-documenter.ts

typescript
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 write API documentation with examples, schemas, and error codes',
model: 'anthropic/claude-sonnet-4.5',
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 agents
inputSchema: {
prompt: {
type: 'string',
description: 'What API endpoints or schemas to document',
},
},
systemPrompt:
'You are an API documentation specialist. Write clear documentation for REST APIs and GraphQL schemas with examples, request/response formats, and error codes.',
instructionsPrompt:
'Analyze the specified API endpoints and create 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

Builds and reviews database migrations. Uses the reviewer for checking migrations:

.agents/migration-specialist.ts

typescript
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 reversible database migrations with proper indexing and rollback procedures',
model: 'anthropic/claude-sonnet-4.5',
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. Create 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)

LLM-based agents cover many tasks. Programmatic agents add 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. This example spawns thinker and reviewer subagents:

.agents/code-analyzer.ts

typescript
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-sonnet-4.5',
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 files
const { toolResult: files } = yield {
toolName: 'find_files',
input: { query: prompt },
}
// Read the most important files
if (files) {
const filePaths = JSON.parse(files).slice(0, 5)
yield {
toolName: 'read_files',
input: { paths: filePaths },
}
}
// Spawn a thinker for deep analysis
yield {
toolName: 'spawn_agents',
input: {
agents: [
{
agent_type: 'thinker',
prompt: `Analyze the code structure and suggest improvements for: ${prompt}`,
},
],
},
}
// Let the agent generate its response
yield 'STEP_ALL'
},
}
export default definition

Key Concepts for Programmatic Agents

1. Generator Function Basics

Your handleSteps function receives context and yields actions:

typescript
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:

typescript
const { toolResult, toolError } = yield {
toolName: 'read_files',
input: { paths: ['file1.ts', 'file2.ts'] }
}
if (toolError) {
// Handle error case
console.error('Failed to read files:', toolError)
} else {
// Use the result
const fileContent = JSON.parse(toolResult)
}

3. Control Flow Options

Control Flow:

  • yield 'STEP' - Run one LLM generation step
  • yield 'STEP_ALL' - Run until completion
  • return - End the agent's turn

4. Advanced Example: Conditional Workflow

typescript
handleSteps: function* ({ agentState, prompt, params }) {
// Step 1: Analyze the codebase
const { toolResult: analysis } = yield {
toolName: 'spawn_agents',
input: {
agents: [{
agent_type: 'thinker',
prompt: `Analyze: ${prompt}`
}]
}
}
// Step 2: Based on analysis, choose action
if (analysis?.includes('refactor')) {
// Get all files that need refactoring
const { toolResult: files } = yield {
toolName: 'find_files',
input: { query: 'needs refactoring' }
}
// Step 3: Refactor each file
for (const file of JSON.parse(files || '[]')) {
yield {
toolName: 'write_file',
input: {
path: file,
instructions: 'Refactor for better performance',
content: '// ... refactored code ...'
}
}
}
}
// Step 4: Final review
yield {
toolName: 'spawn_agents',
input: {
agents: [{
agent_type: 'reviewer',
prompt: 'Review all changes'
}]
}
}
// Let the agent summarize
yield '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