File: creating-new-agents.md | Updated: 11/18/2025
Create specialized agents from scratch using TypeScript files in the .agents/ directory.
Types:
handleStepsControl Flow:
yield 'STEP' - Run one LLM generation stepyield 'STEP_ALL' - Run until completionreturn - End the agent's turnAccessing Context:
agentState - Current agent state and message historyprompt - User's prompt to the agentparams - Additional parameters passed to the agentCreate a new TypeScript file in .agents/ directory:
.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-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 agents
inputSchema: {
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
Specialized for documenting REST APIs and GraphQL schemas:
.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 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 agents
inputSchema: {
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
Specialized for creating and reviewing database 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 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
🎯 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.
Use TypeScript generator functions with the handleSteps field to control execution:
.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-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 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
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...
}
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)
}
Control Flow:
yield 'STEP' - Run one LLM generation stepyield 'STEP_ALL' - Run until completionreturn - End the agent's turntypescript
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'
}
Use Programmatic (handleSteps) when:
Use LLM-based (prompts only) when:
Customizing Agents Agent Reference
Toggle menu