Building a Project Manager Subagent in Claude Code

A worked subagent definition for Claude Code that triages your board, writes plans on cards and files what it notices, with a tool list and a hook that stop it from closing work.

7 min read

A project manager subagent in Claude Code is a Markdown file in .claude/agents/ whose tool list lets it read your board, comment, file new cards and write plans, and nothing else: no editing code, no shell, no closing work. You call it when the board needs tidying (“use the pm subagent to triage To Do”), it does the reading in its own context window, and you get back a short report while the board itself carries the detail. Below is a complete definition, what each line does, and the one guard a tool list alone cannot give you.

If subagents are new to you, Claude Code Task tool vs subagents explains how Claude delegates to them and what they can see. This post assumes that and gets straight to the file.

What the PM subagent is for

Keep its job narrow. A PM subagent is good at the work around the work:

  • Triage: read new cards in To Do, flag likely duplicates, suggest a kind and a priority, and ask in a comment for whatever a report is missing.
  • Planning: for the top cards in Next Up, read the relevant code and write a plan on the card: steps, files, traps, and how it will be checked.
  • Noticing: file a new card for anything it spots along the way, after searching for an existing one.
  • Reporting: tell you what is stuck in In Progress and which Completed cards have no testing recorded.

What it must not do is decide that work is finished. Closing a card is a judgement about someone else’s work, and it is the move that is most expensive to get wrong quietly.

The file

This assumes the board is connected in Claude Code as an MCP server named fenbs (the Claude Code integration page has the one command). MCP tools are named (opens in a new tab) mcp__<server>__<tool>, so the board’s fenbs_comment tool becomes mcp__fenbs__fenbs_comment in a tool list.

.claude/agents/pm.md
---
name: pm
description: Project manager for the fenbs board. Use to triage To Do, write plans on cards in Next Up, report what is stuck, or file issues noticed during other work. Never use it to implement or close work.
tools: Read, Grep, Glob, mcp__fenbs__fenbs_whoami, mcp__fenbs__fenbs_get_context, mcp__fenbs__fenbs_list_items, mcp__fenbs__fenbs_get_item, mcp__fenbs__fenbs_search, mcp__fenbs__fenbs_list_projects, mcp__fenbs__fenbs_comment, mcp__fenbs__fenbs_create_item, mcp__fenbs__fenbs_update_item
model: sonnet
hooks:
  PreToolUse:
    - matcher: "mcp__fenbs__fenbs_(update|create)_item"
      hooks:
        - type: command
          command: "./.claude/hooks/pm-no-lane-moves.sh"
---

You are the project manager for this repository's fenbs board. You do not write code.

Start every run with fenbs_whoami, then fenbs_get_context. Follow the context notes.

Triage (To Do): for each new card, check fenbs_search for duplicates and comment the
likely match. Suggest a kind and a priority in a comment. If the problem does not say
what, why and where, comment the exact question to ask. Do not rewrite the problem.

Plans (Next Up): read the code the card points at, then set the plan field with
fenbs_update_item: steps, files to change, traps, and how it will be checked.
Replace the whole plan; never put a plan in the problem.

Noticing: search before filing. File new cards with fenbs_create_item, kind set, and a
problem that says what, why and where (file:line). If it answers created: false, comment
on the match instead.

You never move a card between lanes and never mark anything Completed.

Finish with a report of at most ten lines: each card you touched, by ref, and what you did.

Line by line

name and description are the only required fields. Claude reads the description to decide when to delegate, so it is written as an instruction, including when not to use it.

tools is an allowlist. Leave it out and a subagent inherits every tool (opens in a new tab) in the main conversation, MCP tools included. List it and the subagent gets only what is named: here, three read-only file tools so it can look at code while planning, and nine board tools. There is no Edit, Write or Bash, so it cannot change the repository however it is prompted. There is no fenbs_delete_item or fenbs_delete_context_note, so it cannot remove anything from the board.

model picks the model for this subagent. Triage and planning read a lot and write a little, so a mid-sized model is a reasonable default; use inherit to run on whatever the main session uses.

The body is the subagent’s system prompt. It does not see your conversation, only this prompt, the delegation message Claude writes, and your CLAUDE.md files, so everything it needs to know about how you run the board goes here or in the board’s AI context.

The guard a tool list cannot give you

There is a gap. Writing a plan needs fenbs_update_item, and the same tool can change a card’s lane. A tool list works at the level of whole tools, so it cannot allow the plan and forbid the move. The prompt says “never move a card”, but a prompt shapes what Claude tries; it is not a control.

The control is a hook. Subagent frontmatter can carry hooks that run only while that subagent is active, and a PreToolUse hook that exits with code 2 blocks the call and shows the subagent its message. This one refuses any update that tries to change a card’s lane, and any new card filed straight into a lane other than To Do (backlog in the API), because fenbs_create_item takes a lane too:

.claude/hooks/pm-no-lane-moves.sh
#!/bin/bash
# Block the pm subagent from moving cards. It may still set the plan, priority or kind.
# Updates may not name a lane at all; new cards may only go into To Do (backlog).
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.tool_name')
LANE=$(echo "$INPUT" | jq -r '.tool_input.lane // empty')
if [ -n "$LANE" ] && { [ "$TOOL" = "mcp__fenbs__fenbs_update_item" ] || [ "$LANE" != "backlog" ]; }; then
  echo "The pm subagent does not move cards. Comment your recommendation instead." >&2
  exit 2
fi
exit 0

Make it executable with chmod +x on macOS and Linux. On Windows, write the script in PowerShell and add shell: powershell to the hook entry, as the Claude Code hooks documentation (opens in a new tab) describes. The script needs jq installed. Test it once by asking the subagent to move a card and checking that it comes back with the refusal and a comment instead.

Blocking every lane change, not only moves to Completed, is deliberate. A PM that can push cards into In Progress can start work nobody agreed to, and one that can drag cards back to To Do can quietly undo a colleague’s decision. It recommends; people move.

The board’s own limits still apply

Claude Code’s controls sit on your machine. The board has its own, and they apply whichever subagent is calling. On fenbs the connection acts as the person who approved it, narrowed to the scopes they ticked, and it can never do something that person could not. A refusal comes back as a sentence naming the missing permission and the role, which the subagent can quote in its report.

On a team board you can go further. The MCP guide describes giving an assistant a role of its own, narrower than yours, from the board’s AI Assistants tab. Moving tasks between lanes is its own permission on fenbs, separate from adding and editing tasks, so a role that can edit and comment but not move makes the rule above hold on the server as well as on your laptop. Roles and permissions for humans and AI agents covers what each permission allows.

One thing to know about attribution

Every subagent in a session shares the session’s connection, so the board’s History records the PM subagent’s changes as Claude acting for you, the same as the main conversation’s. Ask the subagent to start each comment with “pm:” and its work stays easy to pick out. See audit trail for AI agents for how History reads.

Using it day to day

  1. Morning: “Use the pm subagent to triage To Do.” It reads new cards, comments duplicates and missing detail, and returns a ten-line report.
  2. Before a working session: “Have pm write plans for the top three cards in Next Up.” The plans land on the cards, where the next session, or a colleague, reads them.
  3. During other work: when the main conversation notices a problem outside its job, it hands it to pm to search and file, so the backlog gains a proper card instead of a line in a transcript.
  4. End of day: “Ask pm what is stuck in In Progress and which Completed cards have no testing recorded.”

You make the moves. The PM subagent’s value is that by the time you do, every card has a plan, every duplicate has been pointed out, and nothing it noticed has been lost. The rest of the rhythm, what goes in CLAUDE.md for the main session, is in a task-tracking workflow for Claude Code.

Set it up

Connect the board: Claude Code integration. Choose the connection’s scopes: assistant tokens and scopes. What plan, problem and testing mean on a card: how it works.

Questions people ask.

Where do I put a Claude Code project manager subagent?

In .claude/agents/pm.md in the project, so it can be committed and shared with the team, or in ~/.claude/agents/ to have it in every project. It is a Markdown file with YAML frontmatter; only name and description are required.

How do I give a subagent access to MCP tools?

Subagents inherit the main conversation’s MCP tools unless you set tools. To allow only some, list them by their full names in the form mcp__server__tool, for example mcp__fenbs__fenbs_comment.

Can I stop a subagent using one argument of a tool it is allowed?

Not with the tools list, which works on whole tools. Use a PreToolUse hook in the subagent’s frontmatter: it receives the tool input as JSON and blocks the call by exiting with code 2.

Will the board show which subagent made a change?

The board records changes by the connection, so every subagent in a session appears as Claude acting for you. Have each subagent sign its comments with its name to tell them apart.

Start with one thing.

There is nothing to set up first. Write one line and you’ve started.