---
title: "Chapter 09: Subagents"
description: "Add the subagent system — specialized agents with different prompts and tools. Build a code-map generator and integrate the prompt system."
type: lesson
order: 9
chapter: "03-advanced"
---

# Chapter 09: Subagents

## What We're Building

Subagents are the key to managing context and specialization. Instead of one giant agent that knows everything, we spawn focused subagents for specific tasks. Each subagent:

- Has its own system prompt and tool set
- Runs in a nested `runTurn` loop
- Returns a result to the parent agent
- Has its own session ID in the logs (with parent tracking)

## Step 1: Build the Prompt System

First, refactor how prompts work. Paste:

> Clean up the prompt system. We want to support multiple prompts, for example code-explorer, or feature-planner, or tech-researcher, that will have access to different tools and write out different results. Each prompt should have a description describing what it's appropriate for, and a list of tools that it has access to. We want to add a sub-prompt tool that will create a new agent with a detailed message, and then the agent can return the result or ask more questions.

### The Prompt System Architecture

Prompts are defined as `.md` files in `src/prompts/` with YAML frontmatter:

```markdown
---
name: default
description: The default full-featured agent.
tools:
  [
    bash,
    list_files,
    read_file,
    write_file,
    replace_in_file,
    search_files,
    run_check,
    git_diff,
    download_url,
  ]
subagents: [feature-planner, tech-researcher, code-map, poet]
---

You are a helpful AI assistant capable of running bash commands and editing files...
```

The `src/prompts/index.ts` loader parses all `.md` files, extracts frontmatter, and builds `Prompt` objects:

```typescript
export interface Prompt {
  name: string;
  description: string;
  systemPrompt: string;
  tools: any[];
}

export function getPrompt(name: string): Prompt {
  if (!PROMPTS_CACHE) {
    PROMPTS_CACHE = loadPrompts();
  }
  return PROMPTS_CACHE[name] || PROMPTS_CACHE.default;
}
```

### Dynamic Tool Assignment

Each prompt specifies which tools it can access. Prompts that list `subagents` get an additional `run_agent` tool:

```typescript
const promptTools = TOOLS.filter((t) => toolNames.includes(t.function.name));

if (subagentNames.length > 0) {
  promptTools.push({
    type: "function",
    function: {
      name: "run_agent",
      description: "Delegate a task to a specialized subagent",
      parameters: {
        type: "object",
        properties: {
          agentName: {
            type: "string",
            enum: subagentNames,
            description: "The name of the subagent to run",
          },
          task: {
            type: "string",
            description: "The task description for the subagent",
          },
        },
        required: ["agentName", "task"],
      },
    },
  });
}
```

## Step 2: Implement Subagent Execution

The `run_agent` tool in `executeTool` calls `agentRunner`:

```typescript
case "run_agent":
  if (!agentRunner) throw new Error("Agent runner not provided");
  return agentRunner(args.agentName, args.task);
```

The `AgentRunner` is implemented inside `runTurn`:

```typescript
const agentRunner: AgentRunner = async (subAgentName: string, task: string) => {
  console.log(`\n--- [Subagent Start] ${subAgentName} ---`);
  console.log(`Task: ${task}\n`);

  const prompt = getPrompt(subAgentName);

  const subQuery: Message[] = [
    { role: "system", content: prompt.systemPrompt },
    { role: "user", content: task },
  ];

  const subLogger = logger ? logger.child(subAgentName) : undefined;

  const { messages } = await runTurn(
    subQuery,
    apiCaller,
    toolExecutor,
    prompt.tools,
    subLogger,
    subAgentName,
  );

  const lastMsg = messages[messages.length - 1];
  console.log(`\n--- [Subagent End] ${subAgentName} ---`);

  if (lastMsg.role === "assistant" && lastMsg.content) {
    return lastMsg.content;
  }
  return "Subagent completed without specific output.";
};
```

This creates a nested `runTurn` call with the subagent's prompt and tools. The result flows back into the parent agent's context.

## Step 3: Build the Code Map Agent

Paste:

> Create a code explorer agent that is in the prompt directory. You will write out a very detailed prompt that can be run over and over again. The prompt looks through the entire codebase and documents it. The format should be structured and optimized for LLMs to read.

The agent creates `src/prompts/code-map.md` — a specialized agent that:

1. Explores directory structure with `list_files`
2. Reads every source and test file with `read_file`
3. Analyzes architecture, module relationships, types
4. Identifies security issues, test gaps, code quality problems
5. Writes a comprehensive `docs/YYYY-MM-DD-code-map.md` file

The output format is specifically designed for LLM consumption:

```markdown
---
type: code-map
date: 2025-04-15
---

# Codebase Map

## Summary

[2 sentence summary]

## Architecture

[High-level explanation]

## Main Modules

- **Agent Core**: Handles conversation loop and subagent delegation
- **Tools**: Filesystem, system, search, web, git operations

## Critical Issues (Priority Order)

1. **Security**: bash tool allows arbitrary command execution
2. **Data Integrity**: replace_in_file reads entire file into memory
3. **Usability**: search_files clips at 50 matches

## File Map

### src/agent.ts

- **Methods**:
  - `runTurn(history, apiCaller, toolExecutor, tools, logger, agentName): TurnResult`
- **Dependencies**: lib/api.ts, tools/index.ts
```

## Step 4: Integrate Everything

Now wire it all together:

> Integrate the prompt system with the subagent system, and make sure that the list of prompts, subagents, and tools is dynamically specified for each prompt.

## Step 5: Verify Subagent Logging

> examine the log system and make sure that entries are logged immediately after response, and that we are correctly tracking how subagent logs map to the parent agent

The `child()` method on `SessionLogger` creates a new session with `parentId` linking back to the parent:

```json
// Parent message
{"sessionId":"abc123","parentId":null,"agentName":"main","message":{...}}

// Subagent messages (same file, different sessionId, parentId=abc123)
{"sessionId":"def456","parentId":"abc123","agentName":"code-map","message":{...}}
{"sessionId":"def456","parentId":"abc123","agentName":"code-map","message":{...}}
```

## Step 6: The Full Prompt Arsenal

By the end of this chapter, you have these agents:

| Agent               | File                 | Tools                                           | Purpose                  |
| ------------------- | -------------------- | ----------------------------------------------- | ------------------------ |
| **default**         | `default.md`         | bash, files, search, check, diff, download      | General coding           |
| **code-map**        | `code-map.md`        | list_files, read_file, search_files, write_file | Document codebase        |
| **tech-researcher** | `tech-researcher.md` | search, download, tavily, ask_user, write_file  | Web research             |
| **feature-planner** | `feature-planner.md` | list_files, read_file, search_files, write_file | Plan features            |
| **poet**            | `poet.md`            | ask_user                                        | Interactive poem writing |

### The Poet — Human-in-the-Loop Test

The `poet` agent demonstrates subagent→user interaction:

```markdown
---
name: poet
description: A subagent that asks for your name and a color, then writes a poem.
tools: [ask_user]
---

You are a poetic assistant. Your goal is to write a poem for the user.
You MUST follow this EXACT sequence:

1. CALL the `ask_user` tool with the question "What is your name?".
2. After receiving the name, CALL the `ask_user` tool with the question "What is your favorite color?".
3. ONLY AFTER you have both the name and the color, write a short poem.
4. You MUST use the `ask_user` tool. Do not guess or make up a name or color.
5. Provide the final poem as your response.
```

Test it:

```
Can you write me a poem?
```

---

## The Agent Tree

When the default agent spawns subagents, the structure looks like:

```
main (default prompt, all tools)
├── code-map (explore-only tools)
│   └── writes docs/2025-04-15-code-map.md
├── tech-researcher (search tools)
│   └── writes reports/2025-04-15-topic-researcher.md
├── feature-planner (read-only tools)
│   └── writes plan.md
└── poet (ask_user only)
    └── returns poem text
```

Each branch runs independently. The parent agent collects results and continues the conversation.

---

[← Chapter 08](/build-ai-coding-agent/03-advanced/08-ui/) · [Next: Chapter 10 →](/build-ai-coding-agent/03-advanced/10-skills/)
