Add Claude Code SDK tutorials and examples (#195)

* Add Claude Code SDK tutorials and examples

This PR adds comprehensive tutorials and examples for the Claude Code SDK, including:
- Research agent implementation with web search capabilities
- Chief of Staff agent with multi-agent coordination
- Observability agent with Docker configuration
- Supporting utilities and documentation

The examples demonstrate key SDK features:
- Multi-turn conversations with ClaudeSDKClient
- Custom output styles and slash commands
- Hooks for automated actions and governance
- Script execution via Bash tool
- Multi-agent orchestration patterns

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: rodrigo olivares <rodrigoolivares@anthropic.com>
Co-authored-by: Alex Notov <zh@anthropic.com>
This commit is contained in:
Jiri De Jonghe
2025-09-12 15:04:34 -07:00
committed by GitHub
parent fa326a49e0
commit f26aa5891c
48 changed files with 3978 additions and 10 deletions

View File

@@ -28,4 +28,4 @@ Provide a clear summary with:
If all links look good, provide a brief confirmation.
**IMPORTANT: Post your review as a comment on the pull request using the command: `gh pr comment ${{ github.event.pull_request.number }} --body "your review content"`**
**IMPORTANT: Post your review as a comment on the pull request using the command: `gh pr comment $PR_NUMBER --body "your review content"`**

View File

@@ -16,4 +16,4 @@ Then check:
Provide clear, actionable feedback on any issues found.
**IMPORTANT: Post your findings as a comment on the pull request using the command: `gh pr comment ${{ github.event.pull_request.number }} --body "your findings"`**
**IMPORTANT: Post your findings as a comment on the pull request using the command: `gh pr comment $PR_NUMBER --body "your findings"`**

View File

@@ -41,4 +41,4 @@ Provide a clear summary with:
- ⚠️ Suggestions for improvement
- ❌ Critical issues that must be fixed
**IMPORTANT: Post your review as a comment on the pull request using the command: `gh pr comment ${{ github.event.pull_request.number }} --body "your review"`**
**IMPORTANT: Post your review as a comment on the pull request using the command: `gh pr comment $PR_NUMBER --body "your review"`**

View File

@@ -27,4 +27,8 @@ jobs:
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ secrets.GITHUB_TOKEN }}
prompt: "/link-review"
prompt: "/link-review"
claude_args: |
--allowedTools "Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(echo:*),Read,Glob,Grep,WebFetch"
env:
PR_NUMBER: ${{ github.event.pull_request.number }}

View File

@@ -26,4 +26,8 @@ jobs:
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ secrets.GITHUB_TOKEN }}
prompt: "/model-check"
prompt: "/model-check"
claude_args: |
--allowedTools "Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(echo:*),Read,Glob,Grep,WebFetch"
env:
PR_NUMBER: ${{ github.event.pull_request.number }}

View File

@@ -27,4 +27,8 @@ jobs:
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ secrets.GITHUB_TOKEN }}
prompt: "/notebook-review"
prompt: "/notebook-review"
claude_args: |
--allowedTools "Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(echo:*),Read,Glob,Grep,WebFetch"
env:
PR_NUMBER: ${{ github.event.pull_request.number }}

View File

@@ -41,9 +41,46 @@ jobs:
uv run ruff format **/*.ipynb --check || true
- name: Validate notebook structure
id: validate
run: |
uv run python scripts/validate_notebooks.py
uv run python scripts/validate_notebooks.py | tee validation_output.txt
# Check if validation found issues
if grep -q "❌" validation_output.txt; then
echo "has_issues=true" >> $GITHUB_OUTPUT
exit 1
else
echo "has_issues=false" >> $GITHUB_OUTPUT
fi
continue-on-error: true
- name: Summarize validation issues with Claude
if: github.event_name == 'pull_request' && steps.validate.outputs.has_issues == 'true'
uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ secrets.GITHUB_TOKEN }}
prompt: |
The notebook validation found these issues:
```
$(cat validation_output.txt)
```
Create a helpful PR comment that:
- Summarizes the validation issues found
- Groups similar issues together (e.g., "7 notebooks have empty cells")
- Explains why empty cells are problematic and how to fix them (delete them or add content)
- If there are error outputs, explain they should be cleared before committing
- Uses friendly, constructive language
- Includes specific notebook names and cell numbers for reference
Format as a nice GitHub comment with markdown. Use emoji sparingly for clarity.
Post using: gh pr comment $PR_NUMBER --body "your comment"
claude_args: |
--allowedTools "Bash(gh pr comment:*),Bash(cat:*),Read"
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
# Only run API tests on main branch or for maintainers (costs money)
- name: Execute notebooks (API Testing)
if: |

View File

@@ -0,0 +1,9 @@
# GitHub Personal Access Token
# Required for the GitHub MCP server in 02_The_observability_agent.ipynb
# Create a token at: https://github.com/settings/tokens
GITHUB_TOKEN="your-github-personal-access-token-here"
# Anthropic API Key
# Required for using Claude SDK
# Get your key at: https://console.anthropic.com/settings/keys
ANTHROPIC_API_KEY="sk-ant-api03-your-api-key-here"

26
claude_code_sdk/.gitignore vendored Normal file
View File

@@ -0,0 +1,26 @@
# Python-generated files
__pycache__/
*.py[oc]
build/
dist/
wheels/
*.egg-info
# Virtual environments
.venv
.env
.python-version
# Claude Code settings
.claude-code/
/CLAUDE.md
# Package manager lock files (optional for tutorials)
uv.lock
# Jupyter notebook checkpoints
.ipynb_checkpoints/
*.ipynb_checkpoints/
# macOS
.DS_Store

View File

@@ -0,0 +1,321 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "94449849",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from dotenv import load_dotenv\n",
"from utils.agent_visualizer import print_activity\n",
"\n",
"from claude_code_sdk import ClaudeCodeOptions, ClaudeSDKClient, query\n",
"\n",
"load_dotenv()"
]
},
{
"cell_type": "markdown",
"id": "0d4a77a4",
"metadata": {},
"source": [
"# 00 - The One-Liner Research Agent\n",
"\n",
"PREFACE: We highly recommend reading [Building effective agents](https://www.anthropic.com/engineering/building-effective-agents) or [How we built our multi-agent research system](https://www.anthropic.com/engineering/built-multi-agent-research-system) in case you haven't. They are great reads and we will assume some basic understanding of agents! \n",
"\n",
"In this notebook we build our own (re)search agent, which is inherently a great use-case because of a few reasons:\n",
"- The input to our system is not sufficient to produce an output, meaning there needs to be interaction with external systems (e.g., the internet)\n",
"- There is no predefined workflow we can use since it is unclear what the agent will discover during its research\n",
"\n",
"Instead, a research agent requires the flexibility to explore unexpected leads and change direction based on what it finds. In its simplest form, a research agent can be an agent that simply searches the internet and summarizes it for you. \n",
"\n",
"Below, we'll implement a basic research agent with just a few lines of code. We provide Claude with exactly one tool which the Claude Code SDK contains straight out of the box: [web search tool](https://docs.anthropic.com/en/docs/agents-and-tools/tool-use/web-search-tool). \n",
"\n",
"> Check [here](https://docs.anthropic.com/en/docs/claude-code/settings#tools-available-to-claude) for a list of Claude Code's readily available tools"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "b00890fb",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"🤖 Thinking...\n",
"🤖 Using: WebSearch()\n",
"✓ Tool completed\n",
"🤖 Thinking...\n"
]
}
],
"source": [
"messages = []\n",
"async for msg in query(\n",
" prompt=\"Research the latest trends in AI agents and give me a brief summary\",\n",
" options=ClaudeCodeOptions(model=\"claude-sonnet-4-20250514\", allowed_tools=[\"WebSearch\"]),\n",
"):\n",
" print_activity(msg)\n",
" messages.append(msg)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "293437f4",
"metadata": {},
"outputs": [],
"source": [
"print(\n",
" f\"\\nResult:\\n{messages[-1].result if hasattr(messages[-1], 'result') and messages[-1].result else messages[-2].content[0].text}\"\n",
")"
]
},
{
"cell_type": "markdown",
"id": "6b888772",
"metadata": {},
"source": [
"And that's all it takes! Just like that we have a research agent that can go and browse the web to answer (to the best of its ability, at least) any question you throw at it.\n",
"\n",
"Note that in our query we provided the argument `options`. Here we define the configuration, the capabilities and limitations of our agent. For example, we provide our agent with the ability to search the web by passing ```allowed_tool=[\"WebSearch\"]```.\n",
"\n",
"More specifically, `allowed_tools` is a list of tools that Claude will be able to use without any approvals. The rest of the tools are still available, but Claude will ask for approval to use them. That said, certain tools like `Read` and other base read-only tools are always allowed. If you want any tool to be removed from Claude's context, add it to `disallowed_tools` instead.\n",
"\n",
"Now, to more closely inspect the actions our agent took, we have provided the ```visualize_conversation``` function."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1d7c6d90",
"metadata": {},
"outputs": [],
"source": [
"from utils.agent_visualizer import visualize_conversation\n",
"\n",
"visualize_conversation(messages)"
]
},
{
"cell_type": "markdown",
"id": "22426729",
"metadata": {},
"source": [
"### Supercharging our agent\n",
"\n",
"So far, we have laid out a very simple (maybe naive) implementation to illustrate how you can start leveraging the SDK to build a research agent. However, there are various ways we can improve our agent to turn it production ready. Let's cover a few of them:\n",
"\n",
"1. Notice how before we only sent one query? In many systems, a human will look at the output of the system, potentially assigning a follow up task. Just like text completions, if we want to send multiple queries to the agent (e.g., 1. analyze abc, 2. make xyz based on your analysis) we would have to copy over the entire analysis context in our second query. Instead, we can **[use the ClaudeSDKClient](https://docs.anthropic.com/en/docs/claude-code/sdk/sdk-python#1-the-claudesdkclient-class-recommended)** to maintain the conversation context for us.\n",
"\n",
"2. Another great way of steering the system is **providing a system prompt**, akin to a system prompt used for text completions. To learn how to write a good system prompt for a research agent, we recommend looking [here](https://github.com/anthropics/anthropic-cookbook/tree/main/patterns/agents/prompts).\n",
"\n",
"3. **Leveraging the `Read` tool** to enable multimodal input. This tool allows Claude to analyze charts, infographics, and complex system diagrams."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "fa4c4d8f",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"🤖 Thinking...\n",
"🤖 Using: Read()\n",
"✓ Tool completed\n",
"🤖 Thinking...\n",
"🤖 Using: Bash()\n",
"✓ Tool completed\n",
"🤖 Thinking...\n",
"🤖 Using: Read()\n",
"✓ Tool completed\n",
"🤖 Thinking...\n",
"🤖 Thinking...\n",
"🤖 Using: WebSearch()\n",
"✓ Tool completed\n",
"🤖 Thinking...\n"
]
}
],
"source": [
"messages = []\n",
"async with ClaudeSDKClient(\n",
" options=ClaudeCodeOptions(\n",
" model=\"claude-sonnet-4-20250514\",\n",
" cwd=\"research_agent\",\n",
" system_prompt=\"You are a research agent specialized in AI\",\n",
" allowed_tools=[\"WebSearch\", \"Read\"],\n",
" )\n",
") as research_agent:\n",
" await research_agent.query(\"Analyze the chart in research_agent/projects_claude.png\")\n",
" async for msg in research_agent.receive_response():\n",
" print_activity(msg)\n",
" messages.append(msg)\n",
"\n",
" await research_agent.query(\"Use a single websearch to investigate the insights from the chart.\")\n",
" async for msg in research_agent.receive_response():\n",
" print_activity(msg)\n",
" messages.append(msg)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7971eae4-3ff1-48d8-99ef-fecca7332163",
"metadata": {},
"outputs": [],
"source": [
"visualize_conversation(messages)"
]
},
{
"cell_type": "markdown",
"id": "38256581",
"metadata": {},
"source": [
"### The Research Agent leaves Jupyter\n",
"\n",
"Finally, to be able to use the agent outside our notebook, we must put it in a Python script. A lightweight implementation of our research agent can be found in `research_agent/agent.py`. We define three functions:\n",
"- `print_activity()` - Shows what the agent is doing in real-time\n",
"- `get_activity_text()` - Extracts activity text for custom handlers\n",
"- `send_query()` - Main function for sending and handlingqueries with built-in activity display\n",
"\n",
"This agent can now be used in any Python script!"
]
},
{
"cell_type": "markdown",
"id": "e220b5c7-463b-4171-b687-b1ec974958de",
"metadata": {},
"source": [
"First an example to test a one-off query to the agent:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3c2ca449-7a36-4b67-af47-fdb68fb3e36b",
"metadata": {},
"outputs": [],
"source": [
"from research_agent.agent import send_query\n",
"\n",
"result = await send_query(\"What is the Claude Code SDK? Only do one websearch and be concise\")\n",
"print(f\"\\nResult: {result}\\n\")"
]
},
{
"cell_type": "markdown",
"id": "466155ec-9f54-49d4-83cb-00032b077147",
"metadata": {},
"source": [
"Now we test out a multi-turn conversation that reuses the same conversation:"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "38ba1eda",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"🤖 Thinking...\n",
"🤖 Using: WebSearch()\n",
"✓ Tool completed\n",
"🤖 Thinking...\n",
"\n",
"-----\n",
"\n",
"Initial research: Anthropic is an AI safety and research company founded in 2021 by former OpenAI researchers, including siblings Dario and Daniela Amodei. The company develops Claude, a family of large language models (LLMs) designed to be helpful, harmless, and honest.\n",
"\n",
"**Key points:**\n",
"- **Mission**: Build reliable, interpretable, and steerable AI systems with a focus on AI safety\n",
"- **Main product**: Claude AI assistant (which you're currently using!)\n",
"- **Structure**: Public benefit corporation balancing profit with humanity's long-term benefit\n",
"- **Funding**: Backed by major investments from Amazon ($8B total) and Google ($2B)\n",
"- **Focus areas**: AI safety, natural language processing, human feedback, and responsible AI development\n",
"\n",
"Anthropic positions itself as a \"safety-first\" AI lab, emphasizing the responsible development of AI systems to serve humanity's long-term well-being.\n",
"\n"
]
}
],
"source": [
"result1 = await send_query(\"What is Anthropic? Only do one websearch and be concise\")\n",
"print(f\"\\n-----\\n\\nInitial research: {result1}\\n\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "36931a9e",
"metadata": {},
"outputs": [],
"source": [
"# Continue the conversation to dig deeper by setting continue_conversation=True\n",
"result2 = await send_query(\n",
" \"What are some of their products?\",\n",
" continue_conversation=True,\n",
")\n",
"print(f\"\\n-----\\n\\nFollow-up: {result2}\\n\")"
]
},
{
"cell_type": "markdown",
"id": "5344587c",
"metadata": {},
"source": [
"## Conclusion\n",
"\n",
"We've demonstrated how the Claude Code SDK enables you to build a functional research agent in just a few lines of code. By leveraging the built-in WebSearch tool, we created an agent capable of autonomous information gathering and synthesis. We also explored how the\n",
"ClaudeSDKClient maintains conversation context across multiple queries and how to incorporate multimodal capabilities through the Read tool.\n",
"\n",
"This foundation in basic agentic workflows prepares you for more sophisticated implementations. In the next notebook, we'll advance to building a Chief of Staff agent that coordinates multiple specialized subagents, implements custom output styles for different\n",
"stakeholders, and uses hooks for governance and compliance tracking.\n",
"\n",
"Next: [01_The_chief_of_staff_agent.ipynb](01_The_chief_of_staff_agent.ipynb) - Learn how to orchestrate complex multi-agent systems with enterprise-grade features.\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python (cc-sdk-tutorial)",
"language": "python",
"name": "cc-sdk-tutorial"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@@ -0,0 +1,672 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from dotenv import load_dotenv\n",
"from utils.agent_visualizer import print_activity, visualize_conversation\n",
"\n",
"from claude_code_sdk import ClaudeCodeOptions, ClaudeSDKClient\n",
"\n",
"load_dotenv()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 01 - The Chief of Staff Agent\n",
"\n",
"#### Introduction\n",
"\n",
"In notebook 00, we built a simple research agent. In this notebook, we'll incrementally introduce key Claude Code SDK features for building comprehensive agents. For each introduced feature, we'll explain:\n",
"- **What**: what the feature is\n",
"- **Why**: what the feature can do and why you would want to use it\n",
"- **How**: a minimal implementation showing how to use it\n",
"\n",
"If you are familiar with Claude Code, you'll notice how the SDK brings feature parity and enables you to leverage all of Claude Code's capabilities in a programmatic headless manner.\n",
"\n",
"#### Scenario\n",
"\n",
"Throughout this notebook, we'll build an **AI Chief of Staff** for a 50-person startup that just raised $10M Series A. The CEO needs data-driven insights to balance aggressive growth with financial sustainability.\n",
"\n",
"Our final Chief of Staff agent will:\n",
"- **Coordinate specialized subagents** for different domains\n",
"- **Aggregate insights** from multiple sources\n",
"- **Provide executive summaries** with actionable recommendations"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Basic Features"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Feature 0: Memory with [CLAUDE.md](https://www.anthropic.com/engineering/claude-code-best-practices)\n",
"\n",
"**What**: `CLAUDE.md` files serve as persistent memory and instructions for your agent. When present in the project directory, Claude Code automatically reads and incorporates this context when you initialize your agent.\n",
"\n",
"**Why**: Instead of repeatedly providing project context, team preferences, or standards in each interaction, you can define them once in `CLAUDE.md`. This ensures consistent behavior and reduces token usage by avoiding redundant explanations.\n",
"\n",
"**How**: \n",
"- Have a `CLAUDE.md` file in the working directory - in our example: `chief_of_staff_agent/CLAUDE.md`\n",
"- Set the `cwd` argument of your ClaudeSDKClient to point to directory of your CLAUDE.md file"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"async with ClaudeSDKClient(\n",
" options=ClaudeCodeOptions(\n",
" model=\"claude-sonnet-4-20250514\",\n",
" cwd=\"chief_of_staff_agent\", # Points to subdirectory with our CLAUDE.md\n",
" )\n",
") as agent:\n",
" await agent.query(\"What's our current runway?\")\n",
" async for msg in agent.receive_response():\n",
" if hasattr(msg, \"result\"):\n",
" print(msg.result)\n",
"# The agent should know from the CLAUDE.md file: $500K burn, 20 months runway"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Feature 1: The Bash tool for Python Script Execution\n",
"\n",
"**What**: The Bash tool allows your agent to (among other things) run Python scripts directly, enabling access to procedural knowledge, complex computations, data analysis and other integrations that go beyond the agent's native capabilities.\n",
"\n",
"**Why**: Our Chief of Staff might need to process data files, run financial models or generate visualizations based on this data. These are all good scenarios for using the Bash tool.\n",
"\n",
"**How**: Have your Python scripts set-up in a place where your agent can reach them and add some context on what they are and how they can be called. If the scripts are meant for your chief of staff agent, add this context to its CLAUDE.md file and if they are meant for one your subagents, add said context to their MD files (more details on this later). For this tutorial, we added five toy examples to `chief_of_staff_agent/scripts`:\n",
"1. `hiring_impact.py`: Calculates how new engineering hires affect burn rate, runway, and cash position. Essential for the `financial-analyst` subagent to model hiring scenarios against the $500K monthly burn and 20-month runway.\n",
"2. `talent_scorer.py`: Scores candidates on technical skills, experience, culture fit, and salary expectations using weighted criteria. Core tool for the `recruiter` subagent to rank engineering candidates against TechStart's $180-220K senior engineer benchmarks.\n",
"3. `simple_calculation.py`: Performs quick financial calculations for runway, burn rate, and quarterly metrics. Utility script for chief of staff to get instant metrics without complex modeling.\n",
"4. `financial_forecast.py`: Models ARR growth scenarios (base/optimistic/pessimistic) given the current $2.4M ARR growing at 15% MoM.Critical for `financial-analyst` to project Series B readiness and validate the $30M fundraising target.\n",
"5. `decision_matrix.py`: Creates weighted decision matrices for strategic choices like the SmartDev acquisition or office expansion. Helps chief of staff systematically evaluate complex decisions with multiple stakeholders and criteria."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"async with ClaudeSDKClient(\n",
" options=ClaudeCodeOptions(\n",
" model=\"claude-sonnet-4-20250514\",\n",
" allowed_tools=[\"Bash\", \"Read\"],\n",
" cwd=\"chief_of_staff_agent\", # Points to subdirectory where our agent is defined\n",
" )\n",
") as agent:\n",
" await agent.query(\n",
" \"Use your simple calculation script with a total runway of 2904829 and a monthly burn of 121938.\"\n",
" )\n",
" async for msg in agent.receive_response():\n",
" print_activity(msg)\n",
" if hasattr(msg, \"result\"):\n",
" print(\"\\n\")\n",
" print(msg.result)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Feature 2: Output Styles\n",
"\n",
"**What**: Output styles allow you to use different output styles for different audiences. Each style is defined in a markdown file.\n",
"\n",
"**Why**: Your agent might be used by people of different levels of expertise or they might have different priorities. Your output style can help differentiate between these segments without having to create a separate agent.\n",
"\n",
"**How**:\n",
"- Configure a markdown file per style in `chief_of_staff_agent/.claude/output-styles/`. For example, check out the Executive Ouput style in `.claude/output-styles/executive.md`. Output styles are defined with a simple frontmatter including two fields: name and description. Note: Make sure the name in the frontmatter matches exactly the file's name (case sensitive)\n",
"\n",
"> **IMPORTANT**: Output styles modify the system prompt that Claude Code has underneath, leaving out the parts focused on software engineering and giving you more control for your specific use case beyond software engineering work."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"🤖 Thinking...\n",
"🤖 Thinking...\n"
]
}
],
"source": [
"messages_executive = []\n",
"async with ClaudeSDKClient(\n",
" options=ClaudeCodeOptions(\n",
" model=\"claude-sonnet-4-20250514\",\n",
" cwd=\"chief_of_staff_agent\",\n",
" settings='{\"outputStyle\": \"executive\"}',\n",
" )\n",
") as agent:\n",
" await agent.query(\"Tell me in two sentences about your writing output style.\")\n",
" async for msg in agent.receive_response():\n",
" print_activity(msg)\n",
" messages_executive.append(msg)\n",
"\n",
"messages_technical = []\n",
"async with ClaudeSDKClient(\n",
" options=ClaudeCodeOptions(\n",
" model=\"claude-sonnet-4-20250514\",\n",
" cwd=\"chief_of_staff_agent\",\n",
" settings='{\"outputStyle\": \"technical\"}',\n",
" )\n",
") as agent:\n",
" await agent.query(\"Tell me in two sentences about your writing output style.\")\n",
" async for msg in agent.receive_response():\n",
" print_activity(msg)\n",
" messages_technical.append(msg)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(messages_executive[-1].result)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(messages_technical[-1].result)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Feature 3: Plan Mode - Strategic Planning Without Execution\n",
"\n",
"**What**: Plan mode instructs the agent to create a detailed execution plan without performing any actions. The agent analyzes requirements, proposes solutions, and outlines steps, but doesn't modify files, execute commands, or make changes.\n",
"\n",
"**Why**: Complex tasks benefit from upfront planning to reduce errors, enable review and improve coordination. After the planning phase, the agent will have a red thread to follow throughout its execution.\n",
"\n",
"**How**: Just set `permission_mode=\"plan\"`\n",
"\n",
"> Note: this feature shines in Claude Code but still needs to be fully adapted for headless applications with the SDK. Namely, the agent will try calling its `ExitPlanMode()` tool, which is only relevant in the interactive mode. In this case, you can send up a follow-up query with `continue_conversation=True` for the agent to execute its plan in context."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"🤖 Thinking...\n",
"🤖 Using: Read()\n",
"✓ Tool completed\n",
"🤖 Using: Glob()\n",
"✓ Tool completed\n",
"🤖 Using: Read()\n",
"✓ Tool completed\n",
"🤖 Using: Glob()\n",
"✓ Tool completed\n",
"🤖 Using: Read()\n",
"✓ Tool completed\n",
"🤖 Using: Read()\n",
"✓ Tool completed\n",
"🤖 Using: Glob()\n",
"✓ Tool completed\n",
"🤖 Thinking...\n",
"🤖 Using: ExitPlanMode()\n",
"✓ Tool completed\n"
]
}
],
"source": [
"messages = []\n",
"async with (\n",
" ClaudeSDKClient(\n",
" options=ClaudeCodeOptions(\n",
" model=\"claude-opus-4-1-20250805\", # We're using Opus for this as Opus truly shines when it comes to planning!\n",
" permission_mode=\"plan\",\n",
" )\n",
" ) as agent\n",
"):\n",
" await agent.query(\"Restructure our engineering team for AI focus.\")\n",
" async for msg in agent.receive_response():\n",
" print_activity(msg)\n",
" messages.append(msg)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n"
]
}
],
"source": [
"print(messages[-1].result)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As mentioned above, the agent will stop after creating its plan, if you want it to execute on its plan, you need to send a new query with `continue_conversation=True` and removing `permission_mode=\"plan\"` "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Advanced Features"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Feature 4: Custom Slash Commands\n",
"\n",
"> Note: slash commands are syntactic sugar for users, not new agent capabilities\n",
"\n",
"**What**: Custom slash commands are predefined prompt templates that users can trigger with shorthand syntax (e.g., `/budget-impact`). These are **user-facing shortcuts**, not agent capabilities. Think of them as keyboard shortcuts that expand into full, well-crafted prompts.\n",
"\n",
"**Why**: Your Chief of Staff will handle recurring executive questions. Instead of users typing complex prompts repeatedly, they can use already vetted prompts. This improves consistency and standardization.\n",
"\n",
"**How**:\n",
"- Define a markdown file in `.claude/commands/`. For example, we defined one in `.claude/commands/slash-command-test.md`. Notice how the command is defined: frontmatter with two fields (name, description) and the expanded prompt with an option to include arguments passed on in the query.\n",
"- You can add parameters to your prompt using `{{args}}`\n",
"- The user uses the slash command in their prompt"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"🤖 Thinking...\n"
]
}
],
"source": [
"# User types: \"/slash-command-test this is a test\"\n",
"# -> behind the scenes EXPANDS to the prompt in .claude/commands/slash-command-test.md\n",
"# In this case the expanded prompt says to simply reverse the sentence word wise\n",
"\n",
"messages = []\n",
"async with ClaudeSDKClient(\n",
" options=ClaudeCodeOptions(model=\"claude-sonnet-4-20250514\", cwd=\"chief_of_staff_agent\")\n",
") as agent:\n",
" await agent.query(\"/slash-command-test this is a test\")\n",
" async for msg in agent.receive_response():\n",
" print_activity(msg)\n",
" messages.append(msg)"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"test a is this\n"
]
}
],
"source": [
"print(messages[-1].result)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Feature 5: Hooks - Automated Deterministic Actions\n",
"\n",
"**What**: Hooks are Python scripts that you can set to execute automatically, among other events, before (pre) or after (post) specific tool calls. Hooks run **deterministically**, making them perfect for validation and audit trails.\n",
"\n",
"**Why**: Imagine scenarios where you want to make sure that your agent has some guardrails (e.g., prevent dangerous operations) or when you want to have an audit trail. Hooks are ideal in combination with agents to allow them enough freedom to achieve their task, while still making sure that the agents behave in a safe way.\n",
"\n",
"**How**:\n",
"- Define hook scripts in `.claude/hooks/` -> _what_ is the behaviour that should be executed when a hook is triggered\n",
"- Define hook configuration in `.claude/settings.local.json` -> _when_ should a hook be triggered\n",
"- In this case, our hooks are configured to watch specific tool calls (WebSearch, Write, Edit, etc.)\n",
"- When those tools are called, the hook script either runs first (pre tool use hook) or after (post tool use hook)\n",
"\n",
"**Example: Report Tracking for Compliance**\n",
"\n",
"A hook to log Write/Edit operations on financial reports for audit and compliance purposes.\n",
"The hook is defined in `chief_of_staff_agent/.claude/hooks/report-tracker.py` and the logic that enforces it is in `chief_of_staff/.claude/settings.local.json`:\n",
"\n",
"\n",
"```json\n",
" \"hooks\": {\n",
" \"PostToolUse\": [\n",
" {\n",
" \"matcher\": \"Write|Edit\",\n",
" \"hooks\": [\n",
" {\n",
" \"type\": \"command\",\n",
" \"command\": \"$CLAUDE_PROJECT_DIR/.claude/hooks/report-tracker.py\"\n",
" }\n",
" ]\n",
" }\n",
" ]\n",
" }\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"🤖 Thinking...\n",
"🤖 Using: TodoWrite()\n",
"✓ Tool completed\n",
"🤖 Thinking...\n",
"🤖 Using: TodoWrite()\n",
"✓ Tool completed\n",
"🤖 Using: Bash()\n",
"✓ Tool completed\n",
"🤖 Thinking...\n",
"🤖 Using: TodoWrite()\n",
"✓ Tool completed\n",
"🤖 Using: Bash()\n",
"✓ Tool completed\n",
"🤖 Thinking...\n",
"🤖 Using: TodoWrite()\n",
"✓ Tool completed\n",
"🤖 Using: Write()\n",
"✓ Tool completed\n",
"🤖 Using: TodoWrite()\n",
"✓ Tool completed\n",
"🤖 Thinking...\n"
]
}
],
"source": [
"messages = []\n",
"async with ClaudeSDKClient(\n",
" options=ClaudeCodeOptions(\n",
" model=\"claude-sonnet-4-20250514\",\n",
" cwd=\"chief_of_staff_agent\",\n",
" allowed_tools=[\"Bash\", \"Write\", \"Edit\", \"MultiEdit\"],\n",
" )\n",
") as agent:\n",
" await agent.query(\n",
" \"Create a quick Q2 financial forecast report with our current burn rate and runway projections. Save it to our /output_reports folder.\"\n",
" )\n",
" async for msg in agent.receive_response():\n",
" print_activity(msg)\n",
" messages.append(msg)\n",
"\n",
"# The hook will track this in audit/report_history.json"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you now navigate to `./chief_of_staff_agent/audit/report_history.json`, you will find that it has logged that the agent has created and/or made changes to your report. The generated report itself you can find at `./chief_of_staff_agent/output_reports/`."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Feature 6: Subagents via Task Tool\n",
"\n",
"**What**: The Task tool enables your agent to delegate specialized work to other subagents. These subagents each have their own instructions, tools, and expertise.\n",
"\n",
"**Why**: Adding subagents opens up a lot of possibilities:\n",
"1. Specialization: each subagent is an expert in their domain\n",
"2. Separate context: subagents have their own conversation history and tools\n",
"3. Parallellization: multiple subagents can work simultaneously on different aspects.\n",
"\n",
"**How**:\n",
"- Add `\"Task\"` to allowed_tools\n",
"- Use a system prompt to instruct your agent how to delegate tasks (you can also define this its CLAUDE.md more generally)\n",
"- Create a markdown file for each agent in `.claude/agents/`. For example, check the one for `.claude/agents/financial-analyst.md` and notice how a (sub)agent can be defined with such an easy and intuitive markdown file: frontmatter with three fields (name, description, and tools) and its system prompt. The description is useful for the main chief of staff agent to know when to invoke each subagent."
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"🤖 Thinking...\n",
"🤖 Using: Task()\n",
"🤖 Using: Bash()\n",
"🤖 Using: Read()\n",
"✓ Tool completed\n",
"✓ Tool completed\n",
"🤖 Using: Bash()\n",
"🤖 Using: Bash()\n",
"✓ Tool completed\n",
"✓ Tool completed\n",
"🤖 Using: Read()\n",
"🤖 Using: Read()\n",
"🤖 Using: Read()\n",
"✓ Tool completed\n",
"✓ Tool completed\n",
"✓ Tool completed\n",
"🤖 Using: Bash()\n",
"✓ Tool completed\n",
"🤖 Using: Bash()\n",
"✓ Tool completed\n",
"✓ Tool completed\n",
"🤖 Thinking...\n"
]
}
],
"source": [
"messages = []\n",
"async with ClaudeSDKClient(\n",
" options=ClaudeCodeOptions(\n",
" model=\"claude-sonnet-4-20250514\",\n",
" allowed_tools=[\"Task\"], # this enables our Chief agent to invoke subagents\n",
" system_prompt=\"Delegate financial questions to the financial-analyst subagent. Do not try to answer these questions yourself.\",\n",
" cwd=\"chief_of_staff_agent\",\n",
" )\n",
") as agent:\n",
" await agent.query(\"Should we hire 5 engineers? Analyze the financial impact.\")\n",
" async for msg in agent.receive_response():\n",
" print_activity(msg)\n",
" messages.append(msg)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"visualize_conversation(messages)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Here, when our main agent decides to use a subagent, it will:\n",
" 1. Call the Task tool with parameters like:\n",
" ```json\n",
" {\n",
" \"description\": \"Analyze hiring impact\",\n",
" \"prompt\": \"Analyze the financial impact of hiring 5 engineers...\",\n",
" \"subagent_type\": \"financial-analyst\"\n",
" }\n",
" ```\n",
" 2. The Task tool executes the subagent in a separate context\n",
" 3. Return results to main Chief of Staff agent to continue processing"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Putting It All Together"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's now put everything we've seen together. We will ask our agent to determine the financial impact of hiring 3 senior engineers and write their insights to `output_reports/hiring_decision.md`. This demonstrates all the features seen above:\n",
"- **Bash Tool**: Used to execute the `hiring_impact.py` script to determine the impact of hiring new engineers\n",
"- **Memory**: Reads `CLAUDE.md` in directory as context to understand the current budgets, runway, revenue and other relevant information\n",
"- **Output style**: Different output styles, defined in `chief_of_staff_agent/.claude/output-styles`\n",
"- **Custom Slash Commands**: Uses the shortcut `/budget-impact` that expands to full prompt defined in `chief_of_staff_agent/.claude/commands`\n",
"- **Subagents**: Our `/budget_impact` command guides the chief of staff agent to invoke the financial-analyst subagent defined in `chief_of_staff_agent/.claude/agents` \n",
"- **Hooks**: Hooks are defined in `chief_of_staff_agent/.claude/hooks` and configured in `chief_of_staff_agent/.claude/settings.local.json`\n",
" - If one of our agents is updating the financial report, the hook should log this edit/write activity in the `chief_of_staff_agent/audit/report_history.json` logfile\n",
" - If the financial analyst subagent will invoke the `hiring_impact.py` script, this will be logged in `chief_of_staff_agent/audit/tool_usage_log.json` logfile\n",
"\n",
"- **Plan Mode**: If you want the chief of staff to come up with a plan for you to approve before taking any action, uncomment the commented line below\n",
"\n",
"To have this ready to go, we have encapsulated the agent loop in a python file, similar to what we did in the previous notebook. Check out the agent.py file in the `chief_of_staff_agent` subdirectory. \n",
"\n",
"All in all, our `send_query()` function takes in 4 parameters (prompt, continue_conversation, permission_mode, and output_style), everything else is set up in the agent file, namely: system prompt, max turns, allowed tools, and the working directory.\n",
"\n",
"To better visualize how this all comes together, check out these [flow and architecture diagrams that Claude made for us :)](./chief_of_staff_agent/flow_diagram.md)\n"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"🤖 Thinking...\n",
"🤖 Using: Task()\n",
"🤖 Using: Bash()\n",
"✓ Tool completed\n",
"🤖 Using: Read()\n",
"🤖 Using: Read()\n",
"🤖 Using: Read()\n",
"✓ Tool completed\n",
"✓ Tool completed\n",
"✓ Tool completed\n",
"✓ Tool completed\n",
"🤖 Thinking...\n",
"🤖 Using: Write()\n",
"✓ Tool completed\n",
"🤖 Thinking...\n"
]
}
],
"source": [
"from chief_of_staff_agent.agent import send_query\n",
"\n",
"result, messages = await send_query(\n",
" \"/budget-impact hiring 3 senior engineers. Save your insights by updating the 'hiring_decision.md' file in /output_reports or creating a new file there\",\n",
" # permission_mode=\"plan\", # Enable this to use planning mode\n",
" output_style=\"executive\",\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"visualize_conversation(messages)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Conclusion\n",
"\n",
"We've demonstrated how the Claude Code SDK enables you to build sophisticated multi-agent systems with enterprise-grade features. Starting from basic script execution with the Bash tool, we progressively introduced advanced capabilities including persistent memory with CLAUDE.md, custom output styles for different audiences, strategic planning mode, slash commands for user convenience, compliance hooks for guardrailing, and subagent coordination for specialized tasks.\n",
"\n",
"By combining these features, we created an AI Chief of Staff capable of handling complex executive decision-making workflows. The system delegates financial analysis to specialized subagents, maintains audit trails through hooks, adapts communication styles for different stakeholders, and provides actionable insights backed by data-driven analysis.\n",
"\n",
"This foundation in advanced agentic patterns and multi-agent orchestration prepares you for building production-ready enterprise systems. In the next notebook, we'll explore how to connect our agents to external services through Model Context Protocol (MCP) servers, dramatically expanding their capabilities beyond the built-in tools.\n",
"\n",
"Next: [02_Connecting_to_MCP_servers.ipynb](02_Connecting_to_MCP_servers.ipynb) - Learn how to extend your agents with custom integrations and external data sources through MCP."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python (cc-sdk-tutorial)",
"language": "python",
"name": "cc-sdk-tutorial"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.13"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@@ -0,0 +1,453 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 2,
"id": "7e7958dd",
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"from typing import Any\n",
"\n",
"from dotenv import load_dotenv\n",
"from utils.agent_visualizer import print_activity\n",
"\n",
"from claude_code_sdk import ClaudeCodeOptions, ClaudeSDKClient"
]
},
{
"cell_type": "markdown",
"id": "ea47572b",
"metadata": {},
"source": [
"# 02 - The Observability Agent"
]
},
{
"cell_type": "markdown",
"id": "08cc95b6",
"metadata": {},
"source": "In the previous notebooks we have built a basic research agent and a Chief of Staff multi-agent framework. While the agents we have built are already powerful, they were still limited in what they could do: the web search agent is limited to searching the internet and our Chief of Staff agent was limited to interacting with its own filesystem.\n\nThis is a serious constraint: real-world agents often need to interact with other systems like databases, APIs, file systems, and other specialized services. [MCP (Model Context Protocol)](https://modelcontextprotocol.io/docs/getting-started/intro) is an open-source standard for AI-tool integrations that allows for an easy connection between our agents and these external systems. In this notebook, we will explore how to connect MCP servers to our agent.\n\n**Need more details on MCP?** For comprehensive setup instructions, configuration best practices, and troubleshooting tips, see the [Claude Code MCP documentation](https://docs.anthropic.com/en/docs/claude-code/mcp)."
},
{
"cell_type": "markdown",
"id": "95247d94",
"metadata": {},
"source": [
"## Introduction to the MCP Server\n",
"### 1. The Git MCP server\n",
"\n",
"Let's first give our agent the ability to understand and work with Git repositories. By adding the [Git MCP server](https://github.com/modelcontextprotocol/servers/tree/main/src/git) to our agent, it gains access to 13 Git-specific tools that let it examine commit history, check file changes, create branches, and even make commits. This transforms our agent from a passive observer into an active participant in your development workflow. In this example, we'll configure the agent to explore a repository's history using only Git tools. This is pretty simple, but knowing this, it is not difficult to imagine agents that can automatically create pull requests, analyze code evolution patterns, or help manage complex Git workflows across multiple repositories."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "21de60c4",
"metadata": {},
"outputs": [],
"source": [
"# define our git MCP server (it was downloaded when you ran uv sync as it is defined in the pyproject.toml file)\n",
"git_mcp: dict[str, Any] = {\n",
" \"git\": {\n",
" \"command\": \"uv\",\n",
" \"args\": [\"run\", \"python\", \"-m\", \"mcp_server_git\", \"--repository\", os.getcwd()],\n",
" }\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "23aa5a3d",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"🤖 Thinking...\n",
"🤖 Using: mcp__git()\n",
"✓ Tool completed\n",
"🤖 Thinking...\n",
"🤖 Using: Bash()\n",
"🤖 Using: Bash()\n",
"🤖 Using: Bash()\n",
"✓ Tool completed\n",
"✓ Tool completed\n",
"✓ Tool completed\n",
"🤖 Thinking...\n"
]
}
],
"source": [
"messages = []\n",
"async with (\n",
" ClaudeSDKClient(\n",
" options=ClaudeCodeOptions(\n",
" model=\"claude-sonnet-4-20250514\",\n",
" mcp_servers=git_mcp,\n",
" allowed_tools=[\n",
" \"mcp__git\"\n",
" ], # For MCP tools, in allowed tools we must add the mcp__serverName__toolName format or mcp__serverName to enable all\n",
" permission_mode=\"acceptEdits\", # auto-accept file edit permissions\n",
" )\n",
" ) as agent\n",
"):\n",
" await agent.query(\n",
" \"Use ONLY your git mcp tools to quickly explore this repo's history and gimme a brief summary.\"\n",
" )\n",
" async for msg in agent.receive_response():\n",
" print_activity(msg)\n",
" messages.append(msg)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "691e0812",
"metadata": {},
"outputs": [],
"source": [
"print(f\"\\nResult:\\n{messages[-1].result}\")"
]
},
{
"cell_type": "markdown",
"id": "346a8754",
"metadata": {},
"source": [
"### 2. The GitHub MCP server"
]
},
{
"cell_type": "markdown",
"id": "1361ba84",
"metadata": {},
"source": [
"Now let's level up from local Git operations to full GitHub platform integration. By switching to the [official GitHub MCP server](https://github.com/github/github-mcp-server/tree/main), our agent gains access to over 100 tools that interact with GitHub's entire ecosystem from managing issues and pull requests to monitoring CI/CD workflows and analyzing code security alerts. This server can work with both public and private repositories, giving your agent the ability to automate complex GitHub workflows that would typically require multiple manual steps."
]
},
{
"cell_type": "markdown",
"id": "7fdb4aa2",
"metadata": {},
"source": "#### Step 1: Set up your GitHub Token\n\nYou need a GitHub Personal Access Token. Get one [here](https://github.com/settings/personal-access-tokens/new) and put in the .env file as ```GITHUB_TOKEN=\"<token>\"```\n> Note: When getting your token, select \"Fine-grained\" token with the default options (i.e., public repos, no account permissions), that'll be the easiest way to get this demo working.\n\nAlso, for this example you will have to have [Docker](https://www.docker.com/products/docker-desktop/) running on your machine. Docker is required because the GitHub MCP server runs in a containerized environment for security and isolation.\n\n**Docker Quick Setup:**\n- Install Docker Desktop from [docker.com](https://www.docker.com/products/docker-desktop/)\n- Ensure Docker is running (you'll see the Docker icon in your system tray)\n- Verify with `docker --version` in your terminal\n- **Troubleshooting:** If Docker won't start, check that virtualization is enabled in your BIOS. For detailed setup instructions, see the [Docker documentation](https://docs.docker.com/get-docker/)\n\n#### Step 2: Define the mcp server and start the agent loop!"
},
{
"cell_type": "code",
"execution_count": 6,
"id": "c1e65281",
"metadata": {},
"outputs": [],
"source": [
"# define our github mcp server\n",
"load_dotenv(override=True)\n",
"github_mcp: dict[str, Any] = {\n",
" \"github\": {\n",
" \"command\": \"docker\",\n",
" \"args\": [\n",
" \"run\",\n",
" \"-i\",\n",
" \"--rm\",\n",
" \"-e\",\n",
" \"GITHUB_PERSONAL_ACCESS_TOKEN\",\n",
" \"ghcr.io/github/github-mcp-server\",\n",
" ],\n",
" \"env\": {\"GITHUB_PERSONAL_ACCESS_TOKEN\": os.environ.get(\"GITHUB_TOKEN\")},\n",
" }\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "e4c524c1",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"🤖 Thinking...\n",
"🤖 Using: mcp__github__search_repositories()\n",
"✓ Tool completed\n",
"🤖 Thinking...\n",
"🤖 Using: mcp__github__get_file_contents()\n",
"✓ Tool completed\n",
"🤖 Thinking...\n"
]
}
],
"source": [
"# run our agent\n",
"messages = []\n",
"async with ClaudeSDKClient(\n",
" options=ClaudeCodeOptions(\n",
" model=\"claude-sonnet-4-20250514\",\n",
" mcp_servers=github_mcp,\n",
" allowed_tools=[\"mcp__github\"],\n",
" permission_mode=\"acceptEdits\", # auto-accept permissions\n",
" )\n",
") as agent:\n",
" await agent.query(\n",
" \"Use ONLY your GitHub MCP tools to search for the anthropics/claude-code-sdk-python repository and and give me a couple facts about it\"\n",
" )\n",
" async for msg in agent.receive_response():\n",
" print_activity(msg)\n",
" messages.append(msg)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4e0ac04f",
"metadata": {},
"outputs": [],
"source": [
"print(f\"\\nResult:\\n{messages[-1].result}\")"
]
},
{
"cell_type": "markdown",
"id": "2a788ed6",
"metadata": {},
"source": [
"## Real use case: An observability agent\n",
"\n",
"Now, with such simple setup we can already have an agent acting as self-healing software system!"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "c8edb208",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"🤖 Thinking...\n",
"🤖 Using: TodoWrite()\n",
"✓ Tool completed\n",
"🤖 Thinking...\n",
"🤖 Using: TodoWrite()\n",
"✓ Tool completed\n",
"🤖 Using: mcp__github__list_workflows()\n",
"✓ Tool completed\n",
"🤖 Thinking...\n",
"🤖 Using: mcp__github__list_workflow_runs()\n",
"✓ Tool completed\n",
"🤖 Thinking...\n",
"🤖 Using: TodoWrite()\n",
"✓ Tool completed\n",
"🤖 Thinking...\n",
"🤖 Using: mcp__github__get_workflow_run()\n",
"✓ Tool completed\n",
"🤖 Thinking...\n",
"🤖 Using: TodoWrite()\n",
"✓ Tool completed\n",
"🤖 Using: mcp__github__get_job_logs()\n",
"✓ Tool completed\n",
"🤖 Thinking...\n",
"🤖 Using: mcp__github__list_workflow_jobs()\n",
"✓ Tool completed\n",
"🤖 Thinking...\n",
"🤖 Using: WebFetch()\n",
"✓ Tool completed\n",
"🤖 Thinking...\n",
"🤖 Using: mcp__github__get_job_logs()\n",
"✓ Tool completed\n",
"🤖 Thinking...\n",
"🤖 Using: mcp__github__get_job_logs()\n",
"✓ Tool completed\n",
"🤖 Thinking...\n",
"🤖 Using: TodoWrite()\n",
"✓ Tool completed\n",
"🤖 Using: mcp__github__get_job_logs()\n",
"✓ Tool completed\n",
"🤖 Thinking...\n",
"🤖 Using: TodoWrite()\n",
"✓ Tool completed\n",
"🤖 Using: TodoWrite()\n",
"✓ Tool completed\n",
"🤖 Thinking...\n"
]
}
],
"source": [
"load_dotenv(override=True)\n",
"\n",
"prompt = \"\"\"Monitor the GitHub Actions workflows for facebook/react.\n",
"Look at the last triggered CI pipeline. \n",
"1. Analyze the trigger for the pipeline\n",
"2. Identify whether the pipeline passed or not\n",
"3. If it failed, explain which test failed\n",
"4. Identify whether human involvement is required\n",
"\n",
"IMPORTANT: Do not raise a PR, issue, or bug on github yet. Just give me a summary of your findings and plan.\n",
"\n",
"Focus on the 'CI' workflow specifically. Use your Github MCP server tools!\"\"\"\n",
"\n",
"github_mcp: dict[str, Any] = {\n",
" \"github\": {\n",
" \"command\": \"docker\",\n",
" \"args\": [\n",
" \"run\",\n",
" \"-i\",\n",
" \"--rm\",\n",
" \"-e\",\n",
" \"GITHUB_PERSONAL_ACCESS_TOKEN\",\n",
" \"ghcr.io/github/github-mcp-server\",\n",
" ],\n",
" \"env\": {\"GITHUB_PERSONAL_ACCESS_TOKEN\": os.environ.get(\"GITHUB_TOKEN\")},\n",
" }\n",
"}\n",
"\n",
"messages = []\n",
"async with ClaudeSDKClient(\n",
" options=ClaudeCodeOptions(\n",
" model=\"claude-sonnet-4-20250514\",\n",
" mcp_servers=github_mcp,\n",
" allowed_tools=[\"mcp__github\"],\n",
" permission_mode=\"acceptEdits\",\n",
" )\n",
") as agent:\n",
" await agent.query(prompt)\n",
" async for msg in agent.receive_response():\n",
" print_activity(msg)\n",
" messages.append(msg)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "49a39ed7",
"metadata": {},
"outputs": [],
"source": [
"print(f\"\\nResult:\\n{messages[-1].result}\")"
]
},
{
"cell_type": "markdown",
"id": "80cd321f",
"metadata": {},
"source": [
"### Observability Agent as Module\n",
"\n",
"The `observability_agent/agent.py` file contains the same minimal helper functions as the research agent or chief of staff agent, just enhanced for GitHub monitoring. \n",
"\n",
"As before, to use it as a module in your Python code:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "97074fe7",
"metadata": {},
"outputs": [],
"source": [
"from observability_agent.agent import send_query\n",
"\n",
"result = await send_query(\n",
" \"Check the CI status for the last 2 runs in anthropics/claude-code-sdk-python. Just do 3 tool calls, be efficient.\"\n",
")\n",
"print(f\"Monitoring result: {result}\")"
]
},
{
"cell_type": "markdown",
"id": "0c1578dc",
"metadata": {},
"source": [
"We can do multi-turn conversations with this agent as well:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7914f8db",
"metadata": {},
"outputs": [],
"source": [
"# Example 2: Multi-turn conversation for deeper monitoring\n",
"result1 = await send_query(\"What's the current CI status for facebook/react?\")\n",
"print(f\"Initial check: {result1[:250]}...\\n\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8014a701",
"metadata": {},
"outputs": [],
"source": [
"# Continue the conversation to dig deeper\n",
"result2 = await send_query(\n",
" \"Are there any flaky tests in the recent failures? You can only make one tool call.\",\n",
" continue_conversation=True,\n",
")\n",
"print(f\"Follow-up analysis: {result2[:250]}...\")"
]
},
{
"cell_type": "markdown",
"id": "5a2f48f7",
"metadata": {},
"source": [
"## Conclusion\n",
"\n",
"We've demonstrated how the Claude Code SDK enables seamless integration with external systems through the Model Context Protocol (MCP). Starting with local Git operations through the Git MCP server, we progressively expanded to full GitHub platform integration with access to over 100 GitHub-specific tools. This transformed our agent from a local assistant into a powerful observability system capable of monitoring workflows, analyzing CI/CD failures, and providing actionable insights for production systems.\n",
"\n",
"By connecting MCP servers to our agent, we created an autonomous observability system that monitors GitHub Actions workflows, distinguishes between real failures and security restrictions, and provides detailed analysis of test failures. The system demonstrates how agents can actively participate in your DevOps workflow, moving from passive monitoring to intelligent incident response.\n",
"\n",
"This concludes, for now, our journey through the Claude Code SDK tutorial series. We've progressed from simple research agents to sophisticated multi-agent orchestration, and finally to external system integration through MCP. Together, these patterns provide the foundation for building production-ready agentic systems that can handle real-world complexity while maintaining governance, compliance, and observability.\n",
"\n",
"### What You've Learned Across All Notebooks\n",
"\n",
"**From Notebook 00 (Research Agent)**\n",
"- Core SDK fundamentals with `query()` and `ClaudeSDKClient`\n",
"- Basic tool usage with WebSearch and Read\n",
"- Simple agent loops and conversation management\n",
"\n",
"**From Notebook 01 (Chief of Staff)**\n",
"- Advanced features: memory, output styles, planning mode\n",
"- Multi-agent coordination through subagents\n",
"- Governance through hooks and custom commands\n",
"- Enterprise-ready agent architectures\n",
"\n",
"**From Notebook 02 (Observability Agent)**\n",
"- External system integration via MCP servers\n",
"- Real-time monitoring and incident response\n",
"- Production workflow automation\n",
"- Scalable agent deployment patterns\n",
"\n",
"The complete implementations for all three agents are available in their respective directories (`research_agent/`, `chief_of_staff_agent/`, `observability_agent/`), ready to serve as inspiration for integrations into your production systems."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python (cc-sdk-tutorial)",
"language": "python",
"name": "cc-sdk-tutorial"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

127
claude_code_sdk/README.md Normal file
View File

@@ -0,0 +1,127 @@
# Building Powerful Agents with the Claude Code SDK
A tutorial series demonstrating how to build sophisticated general-purpose agentic systems using the [Claude Code SDK](https://github.com/anthropics/claude-code-sdk-python), progressing from simple research agents to multi-agent orchestration with external system integration.
## Getting Started
#### 1. Install uv, [node](https://nodejs.org/en/download/), and the Claude Code CLI (if you haven't already)
```curl -LsSf https://astral.sh/uv/install.sh | sh ```
```npm install -g @anthropic-ai/claude-code```
#### 2. Clone and set up the project
```git clone https://github.com/anthropics/anthropic-cookbook.git ```
```cd anthropic-cookbook/claude_code_sdk```
```uv sync ```
#### 3. Register venv as Jupyter kernel so that you can use it in the notebooks
```uv run python -m ipykernel install --user --name="cc-sdk-tutorial" --display-name "Python (cc-sdk-tutorial)" ```
#### 4. Anthropic API Key
1. Visit [console.anthropic.com](https://console.anthropic.com/dashboard)
2. Sign up or log in to your account
3. Click on "Get API keys"
4. Copy the key and paste it into your `.env` file as ```ANTHROPIC_API_KEY=```
#### 5. GitHub Token for Notebook 02
If you plan to work through the Observability Agent notebook:
1. Get a GitHub Personal Access Token [here](https://github.com/settings/personal-access-tokens/new)
2. Select "Fine-grained" token with default options (public repos, no account permissions)
3. Add it to your `.env` file as `GITHUB_TOKEN="<token>"`
4. Ensure [Docker](https://www.docker.com/products/docker-desktop/) is running on your machine
## Tutorial Series Overview
This tutorial series takes you on a journey from basic agent implementation to sophisticated multi-agent systems capable of handling real-world complexity. Each notebook builds upon the previous one, introducing new concepts and capabilities while maintaining practical, production-ready implementations.
### What You'll Learn
Through this series, you'll be exposed to:
- **Core SDK fundamentals** with `query()` and the `ClaudeSDKClient` & `ClaudeCodeOptions` interfaces in the Python SDK
- **Tool usage patterns** from basic WebSearch to complex MCP server integration
- **Multi-agent orchestration** with specialized subagents and coordination
- **Enterprise features** by leveraging hooks for compliance tracking and audit trails
- **External system integration** via Model Context Protocol (MCP)
Note: This tutorial assumes you have some level of familiarity with Claude Code. Ideally, if you have been using Claude Code to supercharge your coding tasks and would like to leverage its raw agentic power for tasks beyond Software Engineering, this tutorial will help you get started.
## Notebook Structure & Content
### [Notebook 00: The One-Liner Research Agent](00_The_one_liner_research_agent.ipynb)
Start your journey with a simple yet powerful research agent built in just a few lines of code. This notebook introduces core SDK concepts and demonstrates how the Claude Code SDK enables autonomous information gathering and synthesis.
**Key Concepts:**
- Basic agent loops with `query()` and async iteration
- WebSearch tool for autonomous research
- Multimodal capabilities with the Read tool
- Conversation context management with `ClaudeSDKClient`
- System prompts for agent specialization
### [Notebook 01: The Chief of Staff Agent](01_The_chief_of_staff_agent.ipynb)
Build a comprehensive AI Chief of Staff for a startup CEO, showcasing advanced SDK features for production environments. This notebook demonstrates how to create sophisticated agent architectures with governance, compliance, and specialized expertise.
**Key Features Explored:**
- **Memory & Context:** Persistent instructions with CLAUDE.md files
- **Output Styles:** Tailored communication for different audiences
- **Plan Mode:** Strategic planning without execution for complex tasks
- **Custom Slash Commands:** User-friendly shortcuts for common operations
- **Hooks:** Automated compliance tracking and audit trails
- **Subagent Orchestration:** Coordinating specialized agents for domain expertise
- **Bash Tool Integration:** Python script execution for procedural knowledge and complex computations
### [Notebook 02: The Observability Agent](02_The_observability_agent.ipynb)
Expand beyond local capabilities by connecting agents to external systems through the Model Context Protocol. Transform your agent from a passive observer into an active participant in DevOps workflows.
**Advanced Capabilities:**
- **Git MCP Server:** 13+ tools for repository analysis and version control
- **GitHub MCP Server:** 100+ tools for complete GitHub platform integration
- **Real-time Monitoring:** CI/CD pipeline analysis and failure detection
- **Intelligent Incident Response:** Automated root cause analysis
- **Production Workflow Automation:** From monitoring to actionable insights
## Complete Agent Implementations
Each notebook includes an agent implementation in its respective directory:
- **`research_agent/`** - Autonomous research agent with web search and multimodal analysis
- **`chief_of_staff_agent/`** - Multi-agent executive assistant with financial modeling and compliance
- **`observability_agent/`** - DevOps monitoring agent with GitHub integration
## Background
### The Evolution of Claude Code SDK
Claude Code has emerged as one of Anthropic's most successful products, but not just for its SOTA coding capabilities. Its true breakthrough lies in something more fundamental: **Claude is exceptionally good at agentic work**.
What makes Claude Code special isn't just code understanding; it's the ability to:
- Break down complex tasks into manageable steps autonomously
- Use tools effectively and make intelligent decisions about which tools to use and when
- Maintain context and memory across long-running tasks
- Recover gracefully from errors and adapt approaches when needed
- Know when to ask for clarification versus when to proceed with reasonable assumptions
These capabilities have made Claude Code the closest thing to a "bare metal" harness for Claude's raw agentic power: a minimal yet complete and sophisticated interface that lets the model's capabilities shine with the least possible overhead.
### Beyond Coding: The Agent Builder's Toolkit
Originally an internal tool built by Anthropic engineers to accelerate development workflows, the SDK's public release revealed unexpected potential. After the release of the Claude Code SDK and its GitHub integration, developers began using it for tasks far beyond coding:
- **Research agents** that gather and synthesize information across multiple sources
- **Data analysis agents** that explore datasets and generate insights
- **Workflow automation agents** that handle repetitive business processes
- **Monitoring and observability agents** that watch systems and respond to issues
- **Content generation agents** that create and refine various types of content
The pattern was clear: the SDK had inadvertently become an effective agent-building framework. Its architecture, designed to handle software development complexity, proved remarkably well-suited for general-purpose agent creation.
This tutorial series demonstrates how to leverage the Claude Code SDK to build highly efficient agents for any domain or use case, from simple automation to complex enterprise systems.
## Contributing
Found an issue or have a suggestion? Please open an issue or submit a pull request!

View File

@@ -0,0 +1,82 @@
---
name: financial-analyst
description: Financial analysis expert specializing in startup metrics, burn rate, runway calculations, and investment decisions. Use proactively for any budget, financial projections, or cost analysis questions.
tools: Read, Bash, WebSearch
---
You are a senior financial analyst for TechStart Inc, a fast-growing B2B SaaS startup. Your expertise spans financial modeling, burn rate optimization, unit economics, and strategic financial planning.
## Your Responsibilities
1. **Financial Analysis**
- Calculate and monitor burn rate, runway, and cash position
- Analyze unit economics (CAC, LTV, payback period)
- Create financial projections and scenarios
- Evaluate ROI on major decisions
2. **Budget Management**
- Track departmental budgets and spending
- Identify cost optimization opportunities
- Forecast future cash needs
- Analyze hiring impact on burn rate
3. **Strategic Planning**
- Model different growth scenarios
- Evaluate acquisition opportunities
- Assess fundraising needs and timing
- Analyze competitive positioning from financial perspective
## Available Data
You have access to:
- Financial data in `financial_data/` directory:
- `burn_rate.csv`: Monthly burn rate trends
- `revenue_forecast.json`: Revenue projections
- `hiring_costs.csv`: Compensation data by role
- Company context in CLAUDE.md
- Python scripts for financial calculations (via Bash) in the `scripts/` folder:
- `python scripts/hiring_impact.py <num_engineers> [salary]` - Calculate hiring impact on burn/runway
- `python scripts/financial_forecast.py` - Advanced financial modeling
- `python scripts/decision_matrix.py` - Strategic decision framework
## Using the Hiring Impact Tool
When asked about hiring engineers, ALWAYS use the hiring_impact.py tool:
```bash
python scripts/hiring_impact.py 3 200000 # For 3 engineers at $200K each
python scripts/hiring_impact.py 5 # Uses default $200K salary
```
The tool provides:
- Monthly burn rate increase
- New runway calculation
- Velocity impact estimate
- Risk-based recommendation
## Decision Framework
When analyzing financial decisions, always consider:
1. Impact on runway (must maintain >12 months)
2. Effect on key metrics (burn multiple, growth efficiency)
3. ROI and payback period
4. Risk factors and mitigation strategies
5. Alternative scenarios and sensitivity analysis
## Output Guidelines
- Lead with the most critical insight
- Provide specific numbers and timeframes
- Include confidence levels for projections
- Highlight key assumptions
- Recommend clear action items
- Flag any risks or concerns
## Example Analyses
**Hiring Decision:**
"Adding 3 senior engineers at $200K each will increase monthly burn by $50K, reducing runway from 20 to 18 months. However, faster product development could accelerate revenue growth by 20%, reaching cash flow positive 3 months earlier."
**Acquisition Analysis:**
"Acquiring SmartDev for $8M would consume 80% of cash reserves, reducing runway to 4 months. Would need immediate Series B or revenue synergies of >$500K/month to justify."
Remember: Always ground recommendations in data and provide multiple scenarios when uncertainty is high.

View File

@@ -0,0 +1,89 @@
---
name: recruiter
description: Technical recruiting specialist focused on startup hiring, talent pipeline management, and candidate evaluation. Use proactively for hiring decisions, team composition analysis, and talent market insights.
tools: Read, WebSearch, Bash
---
You are an expert technical recruiter specializing in startup talent acquisition. You understand both the technical requirements and cultural fit needed for a fast-growing startup environment.
## Your Responsibilities
1. **Talent Pipeline Management**
- Source and evaluate technical candidates
- Manage interview scheduling and coordination
- Track candidate pipeline metrics
- Build relationships with passive candidates
2. **Hiring Strategy**
- Recommend optimal team composition
- Analyze market rates and compensation
- Advise on senior vs. junior hire tradeoffs
- Identify skill gaps in current team
3. **Candidate Evaluation**
- Review technical portfolios and GitHub profiles
- Assess culture fit and startup readiness
- Coordinate technical assessments
- Provide hiring recommendations
4. **Market Intelligence**
- Track talent availability by role and location
- Monitor competitor hiring and compensation
- Identify emerging skill requirements
- Advise on remote vs. in-office strategies
## Available Scripts
You have access to:
- WebSearch for researching candidates and market rates
- Python scripts for talent scoring (via Bash) in `scripts/talent_scorer.py`
- Company hiring data in `financial_data/hiring_costs.csv`
- Team structure information in CLAUDE.md
## Evaluation Criteria
When assessing candidates, consider:
1. **Technical Skills** (via GitHub analysis)
- Code quality and consistency
- Open source contributions
- Technology stack alignment
- Problem-solving approach
2. **Startup Fit**
- Comfort with ambiguity
- Ownership mentality
- Growth mindset
- Collaboration skills
3. **Team Dynamics**
- Complementary skills to existing team
- Mentorship potential (senior) or coachability (junior)
- Cultural add vs. cultural fit
- Long-term retention likelihood
## Hiring Recommendations Format
**For Individual Candidates:**
"Strong hire. Senior backend engineer with 8 years experience, deep expertise in our stack (Python, PostgreSQL, AWS). GitHub shows consistent high-quality contributions. Asking $210K, which is within our range. Can mentor juniors and own authentication service rebuild."
**For Hiring Strategy:**
"Recommend 2 senior + 3 junior engineers over 5 mid-level. Seniors provide immediate impact and mentorship, juniors offer growth potential and lower burn. Total cost: $950K/year vs. $900K for mid-levels, but better long-term team development."
## Interview Process
Standard pipeline for engineering roles:
1. Recruiter screen (30 min) - culture fit, motivation
2. Technical screen (60 min) - coding exercise
3. System design (90 min) - architecture discussion
4. Team fit (45 min) - with potential teammates
5. Executive chat (30 min) - with CEO/CTO
## Key Metrics to Track
- Time to hire: Target <30 days
- Offer acceptance rate: Target >80%
- Quality of hire: 90-day retention >95%
- Pipeline velocity: 5 qualified candidates per opening
- Diversity metrics: 30% underrepresented groups
Remember: In a startup, every hire significantly impacts culture and runway. Optimize for high-impact individuals who can grow with the company.

View File

@@ -0,0 +1,16 @@
---
name: budget-impact
description: Analyze the financial impact of a decision on budget, burn rate, and runway
---
Use the financial-analyst subagent to analyze the budget impact of: {{args}}
Provide a comprehensive analysis including:
1. Total cost (one-time and recurring)
2. Impact on monthly burn rate
3. Change in runway (months)
4. ROI analysis if applicable
5. Alternative options to consider
6. Risk factors
Format the response with clear sections and specific numbers. Include a final recommendation.

View File

@@ -0,0 +1,6 @@
---
name: slash-command-test
description: example of how a slash-command works
---
Reverse the following sentence word wise: {{args}}

View File

@@ -0,0 +1,36 @@
---
name: strategic-brief
description: Generate a comprehensive strategic brief by coordinating analysis from both financial and talent perspectives
---
Create a strategic brief on: {{args}}
Coordinate with both the financial-analyst and recruiter subagents to provide:
## Executive Summary
- Key recommendation (1-2 sentences)
- Critical metrics impact
## Financial Analysis (via financial-analyst)
- Cost/investment required
- ROI and payback period
- Impact on runway and burn rate
- Financial risks and mitigation
## Talent Perspective (via recruiter)
- Team capabilities required
- Hiring implications
- Retention considerations
- Competitive talent landscape
## Strategic Recommendation
- Recommended action plan
- Success metrics
- Timeline and milestones
- Risk mitigation strategies
## Alternative Options
- At least 2 alternative approaches
- Pros/cons of each
Format for board-level presentation with clear sections and data-driven insights.

View File

@@ -0,0 +1,20 @@
---
name: talent-scan
description: Scan the talent market for specific roles and provide hiring recommendations
---
Use the recruiter subagent to perform a talent market scan for: {{args}}
Analyze and report on:
1. Talent availability in target markets
2. Competitive salary ranges
3. Required skills and experience levels
4. Estimated time to hire
5. Recommended sourcing channels
6. Top candidate profiles (if using GitHub)
Provide specific recommendations on:
- Senior vs. junior hiring mix
- Remote vs. in-office strategy
- Compensation packages
- Interview process optimizations

View File

@@ -0,0 +1,95 @@
#!/usr/bin/env python3
"""
PostToolUse hook: Tracks ALL file writes and edits
Maintains history of all document changes for compliance
"""
import json
import os
import sys
from datetime import datetime
def track_report(tool_name, tool_input, tool_response):
"""Log ALL file creation/modification for audit trail"""
# Debug: Log that hook was called
print(f"🔍 Hook called for tool: {tool_name}", file=sys.stderr)
# Get file path from tool input
file_path = tool_input.get("file_path", "")
if not file_path:
print("⚠️ No file_path in tool_input", file=sys.stderr)
return
print(f"📝 Tracking file: {file_path}", file=sys.stderr)
# Track ALL file writes/edits (no filtering)
# Prepare history file path
history_file = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "../../audit/report_history.json"
)
try:
# Load existing history or create new
if os.path.exists(history_file):
with open(history_file) as f:
history = json.load(f)
else:
history = {"reports": []}
# Determine action type
action = "created" if tool_name == "Write" else "modified"
# Calculate word count if content available
content = tool_input.get("content", "") or tool_input.get("new_string", "")
word_count = len(content.split()) if content else 0
# Create history entry
entry = {
"timestamp": datetime.now().isoformat(),
"file": os.path.basename(file_path),
"path": file_path,
"action": action,
"word_count": word_count,
"tool": tool_name,
}
# Add to history
history["reports"].append(entry)
# Keep only last 50 entries
history["reports"] = history["reports"][-50:]
# Save updated history
os.makedirs(os.path.dirname(history_file), exist_ok=True)
with open(history_file, "w") as f:
json.dump(history, f, indent=2)
print(f"📊 File tracked: {os.path.basename(file_path)} ({action})")
except Exception as e:
print(f"Report tracking error: {e}", file=sys.stderr)
# Main execution
if __name__ == "__main__":
try:
# Read input from stdin
input_data = json.load(sys.stdin)
tool_name = input_data.get("tool_name", "")
tool_input = input_data.get("tool_input", {})
tool_response = input_data.get("tool_response", {})
# Track the report
track_report(tool_name, tool_input, tool_response)
# Always exit successfully
sys.exit(0)
except Exception as e:
print(f"Hook error: {e}", file=sys.stderr)
sys.exit(0)

View File

@@ -0,0 +1,98 @@
#!/usr/bin/env python3
"""
PostToolUse hook: Logs when Python scripts are executed via the Bash tool
Distinguishes between:
- Tools: The Claude SDK tools (Bash, Write, Edit, etc.)
- Scripts: Python scripts executed through the Bash tool
"""
import json
import os
import sys
from datetime import datetime
def log_script_usage(tool_name, tool_input, tool_response):
"""Log execution of Python scripts via Bash tool"""
# Only track Bash tool (which is used to execute scripts)
if tool_name != "Bash":
return
# Get the command from tool input
command = tool_input.get("command", "")
# Check if it's executing a Python script from scripts/ directory
# Support both: "python scripts/file.py" and "./scripts/file.py"
import re
# Try to match either pattern: python scripts/... or ./scripts/... or scripts/...
script_match = re.search(r"(?:python\s+)?(?:\./)?scripts/(\w+\.py)", command)
if not script_match:
return
# Only proceed if it's a scripts/ directory execution
if "scripts/" not in command:
return
script_file = script_match.group(1)
# Prepare log file path
log_file = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "../../audit/script_usage_log.json"
)
try:
# Load existing log or create new
if os.path.exists(log_file):
with open(log_file) as f:
log_data = json.load(f)
else:
log_data = {"script_executions": []}
# Create log entry
entry = {
"timestamp": datetime.now().isoformat(),
"script": script_file,
"command": command,
"description": tool_input.get("description", "No description"),
"tool_used": "Bash", # The tool used to execute the script
"success": tool_response.get("success", True) if tool_response else True,
}
# Add to log
log_data["script_executions"].append(entry)
# Keep only last 100 entries
log_data["script_executions"] = log_data["script_executions"][-100:]
# Save updated log
os.makedirs(os.path.dirname(log_file), exist_ok=True)
with open(log_file, "w") as f:
json.dump(log_data, f, indent=2)
print(f"📜 Script executed: {script_file}")
except Exception as e:
print(f"Script logging error: {e}", file=sys.stderr)
# Main execution
if __name__ == "__main__":
try:
# Read input from stdin
input_data = json.load(sys.stdin)
tool_name = input_data.get("tool_name", "")
tool_input = input_data.get("tool_input", {})
tool_response = input_data.get("tool_response", {})
# Log the script usage (when executed via Bash tool)
log_script_usage(tool_name, tool_input, tool_response)
# Always exit successfully
sys.exit(0)
except Exception as e:
print(f"Hook error: {e}", file=sys.stderr)
sys.exit(0)

View File

@@ -0,0 +1,50 @@
---
name: executive
description: Concise, KPI-focused communication for C-suite executives
---
## Communication Principles
- **Lead with the decision or recommendation** - First sentence should be the key takeaway
- **Use bullet points** - Maximum 3-5 points per section
- **Numbers over narrative** - Specific metrics, percentages, and timeframes
- **Action-oriented** - Clear next steps and decisions required
- **Visual hierarchy** - Bold key numbers and dates
## Format Template
**RECOMMENDATION:** [One sentence decision/action]
**KEY METRICS:**
- Metric 1: **X%** change
- Metric 2: **$Y** impact
- Metric 3: **Z months** timeline
**RATIONALE:** [2-3 sentences max]
**NEXT STEPS:**
1. Immediate action
2. Near-term action
3. Follow-up required
**RISKS:** [If any, one line each]
## Example Output
**RECOMMENDATION:** Hire 3 senior engineers now to maintain product velocity.
**KEY METRICS:**
- Burn increase: **$50K/month**
- Runway impact: **-2 months** (20→18)
- ROI: **3.2x** in 12 months
**RATIONALE:** Engineering bottleneck is delaying feature launches. Senior hires can mentor juniors and own critical services. Delay risks losing 2 enterprise deals worth $500K ARR.
**NEXT STEPS:**
1. Approve budget increase today
2. Begin sourcing immediately
3. First hire by end of month
**RISKS:** Competing offers from FAANG may require 10% salary premium.
Remember: Executives have 30 seconds to read this. Make every word count.

View File

@@ -0,0 +1,86 @@
---
name: technical
description: Detailed, data-rich analysis for technical teams and analysts
---
You are providing detailed technical analysis with comprehensive data and methodologies.
## Communication Principles
- **Data-first approach** - Include all relevant metrics and calculations
- **Methodology transparency** - Explain how you arrived at conclusions
- **Multiple scenarios** - Show sensitivity analysis and edge cases
- **Technical depth** - Include formulas, assumptions, and constraints
- **Structured sections** - Clear organization for deep-dive analysis
## Format Template
### Analysis Overview
[Brief context and scope]
### Methodology
- Data sources used
- Key assumptions
- Calculation methods
- Confidence intervals
### Detailed Findings
#### Finding 1: [Title]
- **Data Points:**
- Metric A: value ± margin
- Metric B: value (methodology)
- Metric C: trend analysis
- **Analysis:** [Detailed explanation]
- **Implications:** [Technical consequences]
#### Finding 2: [Continue pattern]
### Scenario Analysis
| Scenario | Variable 1 | Variable 2 | Outcome | Probability |
|----------|-----------|-----------|---------|-------------|
| Base | X | Y | Z | 60% |
| Optimistic| X+20% | Y+10% | Z+35% | 25% |
| Pessimistic| X-15% | Y-20% | Z-40% | 15% |
### Technical Recommendations
1. **Primary:** [Detailed action with rationale]
2. **Alternative:** [Backup approach with tradeoffs]
3. **Monitoring:** [Metrics to track]
### Appendix
- Formulas used
- Raw data tables
- Additional charts/visualizations
## Example Output
### Hiring Impact Analysis
#### Methodology
- Data: 6 months historical burn rate, 120 comparable salary datapoints
- Model: Linear regression with seasonal adjustment
- Confidence: 85% (±10% margin on projections)
#### Financial Impact
- **Base Salary Cost:** $600K/year (3 × $200K)
- **Loaded Cost:** $780K/year (1.3x multiplier for benefits, taxes, equipment)
- **Monthly Burn Increase:** $65K ($780K / 12)
- **Runway Impact:**
- Current: 20 months at $500K/month = $10M remaining
- New: $10M / $565K = 17.7 months (-2.3 months)
#### Productivity Analysis
- **Current Velocity:** 15 story points/sprint
- **Projected with Seniors:** 22 points/sprint (+46%)
- **Break-even:** Month 8 (when productivity gains offset costs)
- **NPV:** $1.2M over 24 months at 10% discount rate
### Sensitivity Analysis
| Salary Range | Productivity Gain | NPV | Runway Impact |
|-------------|------------------|-----|---------------|
| $180K (-10%) | +40% | $950K | -2.0 months |
| $200K (base) | +46% | $1.2M | -2.3 months |
| $220K (+10%) | +50% | $1.3M | -2.6 months |
Remember: Technical audience wants to verify your work. Show your math.

View File

@@ -0,0 +1,95 @@
# CLAUDE.md - Chief of Staff Context
## Company Overview
- **Company**: TechStart Inc
- **Stage**: Series A (Closed $10M in January 2024)
- **Industry**: B2B SaaS - AI-powered developer tools
- **Founded**: 2022
- **HQ**: San Francisco, CA
## Financial Snapshot
- **Monthly Burn Rate**: $500,000
- **Current Runway**: 20 months (until September 2025)
- **ARR**: $2.4M (growing 15% MoM)
- **Cash in Bank**: $10M
- **Revenue per Employee**: $48K
## Team Structure
- **Total Headcount**: 50
- **Engineering**: 25 (50%)
- Backend: 12
- Frontend: 8
- DevOps/SRE: 5
- **Sales & Marketing**: 12 (24%)
- **Product**: 5 (10%)
- **Operations**: 5 (10%)
- **Executive**: 3 (6%)
## Key Metrics
- **Customer Count**: 120 enterprise customers
- **NPS Score**: 72
- **Monthly Churn**: 2.5%
- **CAC**: $15,000
- **LTV**: $85,000
- **CAC Payback Period**: 10 months
## Current Priorities (Q2 2024)
1. **Hiring**: Add 10 engineers to accelerate product development
2. **Product**: Launch AI code review feature by end of Q2
3. **Sales**: Expand into European market
4. **Fundraising**: Begin Series B conversations (target: $30M)
## Compensation Benchmarks
- **Senior Engineer**: $180K - $220K + 0.1-0.3% equity
- **Junior Engineer**: $100K - $130K + 0.05-0.1% equity
- **Engineering Manager**: $200K - $250K + 0.3-0.5% equity
- **VP Engineering**: $250K - $300K + 0.5-1% equity
## Board Composition
- **CEO**: Sarah Chen (Founder)
- **Investor 1**: Mark Williams (Sequoia Capital)
- **Investor 2**: Jennifer Park (Andreessen Horowitz)
- **Independent**: Michael Torres (Former CTO of GitHub)
## Competitive Landscape
- **Main Competitors**: DevTools AI, CodeAssist Pro, SmartDev Inc
- **Our Differentiation**: Superior AI accuracy, 10x faster processing
- **Market Size**: $5B (growing 25% annually)
## Recent Decisions
- Approved hiring 3 senior backend engineers (March 2024)
- Launched freemium tier (February 2024)
- Opened European entity (January 2024)
- Closed Series A funding (January 2024)
## Upcoming Decisions
- Whether to acquire competitor SmartDev Inc ($8M asking price)
- Hiring plan for Q3 (engineering vs. sales focus)
- Office expansion vs. remote-first strategy
- Stock option refresh for early employees
## Risk Factors
- High dependency on AWS (70% of COGS)
- Key engineer retention (3 critical team members)
- Increasing competition from Big Tech
- Potential economic downturn impact on enterprise sales
## Available Scripts
### simple_calculation.py
Quick financial metrics calculator for runway and burn rate analysis.
Script located at `./scripts/simple_calculation.py`
**Usage:**
```bash
python scripts/simple_calculation.py <total_runway> <monthly_burn>
```
**Example:**
```bash
python scripts/simple_calculation.py 10000000 500000
```
**Output:** JSON with monthly_burn, runway_months, total_runway_dollars, quarterly_burn, and burn_rate_daily
Remember: As Chief of Staff, you have access to financial data in the financial_data/ directory and can delegate specialized analysis to your subagents (financial-analyst and recruiter).

View File

@@ -0,0 +1,122 @@
"""
Chief of Staff Agent
"""
import asyncio
import json
import os
from collections.abc import Callable
from typing import Any, Literal
from dotenv import load_dotenv
from claude_code_sdk import ClaudeCodeOptions, ClaudeSDKClient
load_dotenv()
def get_activity_text(msg) -> str | None:
"""Extract activity text from a message"""
try:
if "Assistant" in msg.__class__.__name__:
if hasattr(msg, "content") and msg.content:
first_content = msg.content[0] if isinstance(msg.content, list) else msg.content
if hasattr(first_content, "name"):
return f"🤖 Using: {first_content.name}()"
return "🤖 Thinking..."
elif "User" in msg.__class__.__name__:
return "✓ Tool completed"
except (AttributeError, IndexError):
pass
return None
def print_activity(msg) -> None:
"""Print activity to console"""
activity = get_activity_text(msg)
if activity:
print(activity)
async def send_query(
prompt: str,
continue_conversation: bool = False,
permission_mode: Literal["default", "plan", "acceptEdits"] = "default",
output_style: str | None = None,
activity_handler: Callable[[Any], None | Any] = print_activity,
) -> tuple[str | None, list]:
"""
Send a query to the Chief of Staff agent with all features integrated.
Args:
prompt: The query to send (can include slash commands like /budget-impact)
activity_handler: Callback for activity updates (default: print_activity)
continue_conversation: Continue the previous conversation if True
permission_mode: "default" (execute), "plan" (think only), or "acceptEdits"
output_style: Override output style (e.g., "executive", "technical", "board-report")
Returns:
Tuple of (result, messages) - result is the final text, messages is the full conversation
Features automatically included/leveraged:
- Memory: CLAUDE.md context loaded from chief_of_staff/CLAUDE.md
- Subagents: financial-analyst and recruiter via Task tool (defined in .claude/agents)
- Custom scripts: Python scripts in tools/ via Bash
- Slash commands: Expanded from .claude/commands/
- Output styles: Custom output styles defined in .claude/output-styles
- Hooks: Triggered based on settings.local.json, defined in .claude/hooks
"""
system_prompt = """You are the Chief of Staff for TechStart Inc, a 50-person startup.
Apart from your tools and two subagents, you also have custom Python scripts in the scripts/ directory you can run with Bash:
- python scripts/financial_forecast.py: Advanced financial modeling
- python scripts/talent_scorer.py: Candidate scoring algorithm
- python scripts/decision_matrix.py: Strategic decision framework
You have access to company data in the financial_data/ directory.
"""
# build options with optional output style
options_dict = {
"model": "claude-sonnet-4-20250514",
"allowed_tools": [
"Task", # enables subagent delegation
"Read",
"Write",
"Edit",
"Bash",
"WebSearch",
],
"continue_conversation": continue_conversation,
"system_prompt": system_prompt,
"permission_mode": permission_mode,
"cwd": os.path.dirname(os.path.abspath(__file__)),
}
# add output style if specified
if output_style:
options_dict["settings"] = json.dumps({"outputStyle": output_style})
options = ClaudeCodeOptions(**options_dict)
result = None
messages = [] # this is to append the messages ONLY for this agent turn
try:
async with ClaudeSDKClient(options=options) as agent:
await agent.query(prompt=prompt)
async for msg in agent.receive_response():
messages.append(msg)
if asyncio.iscoroutinefunction(activity_handler):
await activity_handler(msg)
else:
activity_handler(msg)
if hasattr(msg, "result"):
result = msg.result
except Exception as e:
print(f"❌ Query error: {e}")
raise
return result, messages

View File

@@ -0,0 +1,20 @@
{
"reports": [
{
"timestamp": "2025-09-12T11:23:27.090810",
"file": "Q2_2024_Financial_Forecast.md",
"path": "/Users/rodrigoolivares/code/cc-sdk-tutorial/chief_of_staff_agent/output_reports/Q2_2024_Financial_Forecast.md",
"action": "created",
"word_count": 437,
"tool": "Write"
},
{
"timestamp": "2025-09-12T11:28:54.692744",
"file": "hiring_decision.md",
"path": "/Users/rodrigoolivares/code/cc-sdk-tutorial/chief_of_staff_agent/output_reports/hiring_decision.md",
"action": "created",
"word_count": 720,
"tool": "Write"
}
]
}

View File

@@ -0,0 +1,36 @@
{
"script_executions": [
{
"timestamp": "2025-09-12T11:09:48.519841",
"script": "simple_calculation.py",
"command": "python scripts/simple_calculation.py 2904829 121938",
"description": "Run runway calculation with provided values",
"tool_used": "Bash",
"success": true
},
{
"timestamp": "2025-09-12T11:12:16.209491",
"script": "simple_calculation.py",
"command": "python scripts/simple_calculation.py 2904829 121938",
"description": "Run runway calculation with $2.9M runway and $122K monthly burn",
"tool_used": "Bash",
"success": true
},
{
"timestamp": "2025-09-12T11:23:02.810007",
"script": "simple_calculation.py",
"command": "python scripts/simple_calculation.py 10000000 500000",
"description": "Calculate financial metrics using our script",
"tool_used": "Bash",
"success": true
},
{
"timestamp": "2025-09-12T11:27:59.994999",
"script": "hiring_impact.py",
"command": "python scripts/hiring_impact.py 3 200000",
"description": "Calculate hiring impact for 3 engineers at $200K",
"tool_used": "Bash",
"success": true
}
]
}

View File

@@ -0,0 +1,7 @@
Month,Burn_Rate,Headcount,Revenue,Net_Burn
2024-01,450000,45,180000,270000
2024-02,475000,47,195000,280000
2024-03,490000,49,210000,280000
2024-04,500000,50,240000,260000
2024-05,510000,51,265000,245000
2024-06,525000,53,290000,235000
1 Month Burn_Rate Headcount Revenue Net_Burn
2 2024-01 450000 45 180000 270000
3 2024-02 475000 47 195000 280000
4 2024-03 490000 49 210000 280000
5 2024-04 500000 50 240000 260000
6 2024-05 510000 51 265000 245000
7 2024-06 525000 53 290000 235000

View File

@@ -0,0 +1,11 @@
Role,Level,Base_Salary,Equity_Percent,Total_Comp,Recruiting_Fee,Onboarding_Cost,Monthly_Loaded_Cost
Backend Engineer,Senior,200000,0.002,220000,30000,5000,18333
Backend Engineer,Junior,115000,0.0008,125000,17250,5000,10417
Frontend Engineer,Senior,190000,0.002,210000,28500,5000,17500
Frontend Engineer,Junior,110000,0.0008,120000,16500,5000,10000
DevOps Engineer,Senior,210000,0.0025,235000,31500,5000,19583
ML Engineer,Senior,240000,0.003,270000,36000,5000,22500
Engineering Manager,Manager,225000,0.004,260000,33750,5000,21667
Product Manager,Senior,180000,0.002,200000,27000,5000,16667
Designer,Senior,170000,0.0015,185000,25500,5000,15417
Data Scientist,Senior,200000,0.002,220000,30000,5000,18333
1 Role Level Base_Salary Equity_Percent Total_Comp Recruiting_Fee Onboarding_Cost Monthly_Loaded_Cost
2 Backend Engineer Senior 200000 0.002 220000 30000 5000 18333
3 Backend Engineer Junior 115000 0.0008 125000 17250 5000 10417
4 Frontend Engineer Senior 190000 0.002 210000 28500 5000 17500
5 Frontend Engineer Junior 110000 0.0008 120000 16500 5000 10000
6 DevOps Engineer Senior 210000 0.0025 235000 31500 5000 19583
7 ML Engineer Senior 240000 0.003 270000 36000 5000 22500
8 Engineering Manager Manager 225000 0.004 260000 33750 5000 21667
9 Product Manager Senior 180000 0.002 200000 27000 5000 16667
10 Designer Senior 170000 0.0015 185000 25500 5000 15417
11 Data Scientist Senior 200000 0.002 220000 30000 5000 18333

View File

@@ -0,0 +1,53 @@
{
"current_arr": 2400000,
"growth_rate_monthly": 0.15,
"forecast_months": [
{
"month": "2024-07",
"projected_arr": 2760000,
"new_customers": 12,
"churn_risk": "low"
},
{
"month": "2024-08",
"projected_arr": 3174000,
"new_customers": 15,
"churn_risk": "low"
},
{
"month": "2024-09",
"projected_arr": 3650000,
"new_customers": 18,
"churn_risk": "medium"
},
{
"month": "2024-10",
"projected_arr": 4197500,
"new_customers": 20,
"churn_risk": "medium"
},
{
"month": "2024-11",
"projected_arr": 4827125,
"new_customers": 22,
"churn_risk": "low"
},
{
"month": "2024-12",
"projected_arr": 5551194,
"new_customers": 25,
"churn_risk": "low"
}
],
"key_assumptions": [
"Maintain 15% MoM growth rate",
"Product-market fit remains strong",
"No major competitor enters market",
"Engineering team scales as planned"
],
"risks": [
"Economic downturn could reduce enterprise spending",
"Key customer concentration (top 5 = 30% of revenue)",
"Technical debt may slow feature development"
]
}

View File

@@ -0,0 +1,49 @@
# Chief of Staff Agent Architecture
```mermaid
graph TD
User[User] --> Chief[Chief of Staff Agent]
Chief --> Memory[CLAUDE.md]
Chief --> FinData[financial_data/]
Chief --> Tools
Chief --> Commands[Slash Commands]
Chief --> Styles[Output Styles]
Chief --> Hooks[Hooks]
Tools --> Task[Task Tool]
Task --> FA[Financial Analyst]
Task --> Recruiter[Recruiter]
FA --> Scripts1[Python Scripts]
Recruiter --> Scripts2[Python Scripts]
style Chief fill:#f9f,stroke:#333,stroke-width:3px
style Task fill:#bbf,stroke:#333,stroke-width:2px
style FA fill:#bfb,stroke:#333,stroke-width:2px
style Recruiter fill:#bfb,stroke:#333,stroke-width:2px
```
## Expected Agent Communication Flow
```mermaid
sequenceDiagram
participant User
participant Chief as Chief of Staff
participant Task as Task Tool
participant FA as Financial Analyst
participant Scripts as Python Scripts
participant Hooks as Post-Write Hook
User->>Chief: /budget-impact hiring 5 engineers
Chief->>Chief: Expand slash command
Chief->>Task: Delegate financial analysis
Task->>FA: Analyze hiring impact
FA->>Scripts: Execute hiring_impact.py
Scripts-->>FA: Return analysis results
FA->>FA: Generate report
FA-->>Task: Return findings
Task-->>Chief: Subagent results
Chief->>Chief: Write report to disk
Chief->>Hooks: Trigger post-write hook
Hooks->>Hooks: Log to audit trail
Chief-->>User: Executive summary
```

View File

@@ -0,0 +1,83 @@
# Q2 2024 Financial Forecast Report
**TechStart Inc - Chief of Staff Analysis**
*Report Date: September 12, 2025*
## Executive Summary
TechStart Inc maintains a strong financial position in Q2 2024 with 20 months of runway and accelerating revenue growth. Current burn rate is well-controlled at $500K monthly while revenue grows 15% month-over-month.
## Current Financial Position (as of Q2 2024)
### Cash & Runway
- **Cash in Bank**: $10,000,000
- **Monthly Burn Rate**: $500,000
- **Daily Burn Rate**: $16,667
- **Current Runway**: 20 months (until September 2025)
- **Quarterly Burn**: $1,500,000
### Revenue Metrics
- **Annual Recurring Revenue (ARR)**: $2,400,000
- **Monthly Growth Rate**: 15%
- **Revenue per Employee**: $48,000
- **Customer Count**: 120 enterprise customers
## Q2 2024 Projections
### Cash Flow Forecast
| Month | Starting Cash | Monthly Burn | Revenue Growth | Ending Cash |
|-------|---------------|--------------|----------------|-------------|
| Apr 2024 | $10,000,000 | $500,000 | +15% | $9,500,000 |
| May 2024 | $9,500,000 | $500,000 | +15% | $9,000,000 |
| Jun 2024 | $9,000,000 | $500,000 | +15% | $8,500,000 |
### Key Financial Ratios
- **CAC/LTV Ratio**: 0.18 (Healthy - under 0.3)
- **CAC Payback Period**: 10 months
- **Monthly Churn**: 2.5%
- **Gross Revenue Retention**: 97.5%
## Strategic Initiatives Impact
### Planned Hiring (10 Engineers)
- **Additional Monthly Cost**: ~$150,000
- **Revised Burn Rate**: $650,000/month
- **Adjusted Runway**: 15.4 months
### European Expansion
- **Setup Costs**: ~$200,000 (one-time)
- **Ongoing Operations**: ~$50,000/month additional
## Series B Fundraising Timeline
- **Target Amount**: $30,000,000
- **Recommended Timing**: Begin conversations in Q3 2024
- **Runway Buffer**: Maintain 12+ months post-raise
## Risk Assessment
### High Priority Risks
1. **AWS Dependency**: 70% of COGS tied to single provider
2. **Key Personnel Risk**: 3 critical engineers requiring retention packages
3. **Market Competition**: Big Tech entering space
### Mitigation Strategies
- Diversify cloud providers (reduce AWS to <50%)
- Implement equity refresh program for key employees
- Accelerate product development to maintain competitive advantage
## Recommendations
### Immediate Actions (Q2 2024)
1. **Accelerate Hiring**: Front-load engineering hires to maximize development time
2. **Revenue Optimization**: Focus on enterprise expansion (higher LTV)
3. **Cost Management**: Implement quarterly budget reviews
### Medium-term Strategy (Q3-Q4 2024)
1. **Series B Preparation**: Begin investor conversations by July 2024
2. **Market Expansion**: Validate European demand before major investment
3. **Product Development**: Prioritize AI code review feature completion
## Financial Health Score: 8.5/10
- **Strengths**: Strong runway, growing ARR, healthy unit economics
- **Areas for Improvement**: AWS dependency, competitive positioning
---
*This report was generated using current financial data and company metrics. For detailed analysis or specific scenarios, consult with the financial-analyst agent.*

View File

@@ -0,0 +1,139 @@
# Budget Impact Analysis: Hiring 3 Senior Engineers
**Date**: Q2 2024
**Analysis Type**: Financial Impact Assessment
**Decision**: Hiring 3 Senior Engineers
---
## Executive Summary
**Recommendation**: **PROCEED WITH HIRING** - Moderate risk with high upside potential
The analysis shows that hiring 3 senior engineers will increase our monthly burn rate by 13% ($65K/month) while reducing runway by 2.3 months. However, given our strong 15% MoM revenue growth and strategic product timeline, this investment is justified and positions us well for continued growth.
---
## 1. Total Cost Analysis
### Base Compensation (Per Engineer)
- **Base Salary**: $200,000 annually ($16,667 monthly)
- **Equity**: 0.2% (median of 0.1-0.3% range)
- **Total Compensation**: $220,000 annually
### Fully Loaded Costs (3 Engineers)
- **Annual Base Salaries**: $600,000
- **Monthly Base Salaries**: $50,000
- **Benefits & Overhead** (30%): $180,000 annually / $15,000 monthly
- **Total Annual Loaded Cost**: $780,000
- **Total Monthly Loaded Cost**: $65,000
### One-Time Costs
- **Recruiting Fees**: $90,000 (3 x $30,000)
- **Equipment & Onboarding**: $15,000 (3 x $5,000)
- **Total One-Time Investment**: $105,000
---
## 2. Burn Rate Impact
- **Current Monthly Burn**: $500,000
- **New Monthly Burn**: $565,000
- **Percentage Increase**: 13%
- **Quarterly Impact**: $195,000 additional burn per quarter
---
## 3. Runway Analysis
- **Current Runway**: 20 months (until September 2025)
- **New Runway**: 17.7 months (until February 2025)
- **Runway Reduction**: 2.3 months
- **Break-even Timeline**: With 15% MoM revenue growth, break-even moves from ~18 months to ~15 months
---
## 4. ROI Considerations
### Engineering Capacity Impact
- **Current Engineering Team**: 25 engineers
- **New Team Size**: 28 engineers (+12% capacity)
- **Estimated Velocity Increase**: 9% (from hiring tool calculation)
### Revenue Impact Potential
Based on revenue forecast data:
- Current ARR: $2.4M (growing 15% MoM)
- Projected ARR by Dec 2024: $5.55M
- **If new engineers accelerate growth by 2-3%**: Additional $300K-500K ARR by year-end
- **Time to Productivity**: 2-3 months for senior engineers
### Financial Returns
- **Monthly ARR increase needed to justify**: $65,000 / 12 = $5,417
- **Annual ARR increase needed**: $780,000
- **Current trajectory already supports this** with strong 15% MoM growth
---
## 5. Alternative Options Considered
### Option A: Staggered Hiring
- Hire 1 engineer now, 2 in Q3
- Reduces immediate burn impact by $43K monthly
- Allows validation of productivity gains
### Option B: Contract vs Full-Time
- 3 senior contractors at ~$150/hour (65% premium)
- Monthly cost: ~$78K (20% higher)
- More flexibility but higher cost and less commitment
### Option C: Mixed Seniority
- 2 senior engineers + 1 junior engineer
- Reduces monthly cost by ~$7K
- May slow initial velocity but builds pipeline
---
## 6. Risk Factors
### Financial Risks
- **Fundraising Pressure**: Runway reduction puts more pressure on Series B timing
- **Market Conditions**: Economic downturn could affect enterprise sales growth
- **Revenue Concentration**: Top 5 customers represent 30% of revenue
### Operational Risks
- **Integration Time**: 2-3 months before full productivity
- **Management Bandwidth**: 28 engineers may stress current EM capacity
- **Technical Debt**: May slow feature development despite more engineers
---
## Final Recommendation & Action Plan
### Key Supporting Factors:
1. **Strong Revenue Growth**: 15% MoM growth provides confidence in ROI
2. **Healthy Runway Buffer**: 17.7 months still provides adequate runway
3. **Strategic Timing**: Q2 product launch timing aligns with hiring needs
4. **Market Opportunity**: $5B growing market supports aggressive investment
### Recommended Approach:
1. **Hire all 3 engineers immediately** to maximize Q2 product impact
2. **Accelerate Series B conversations** to Q4 2024 (vs. original timeline)
3. **Set aggressive revenue targets**: 18% MoM growth to justify investment
4. **Monitor closely**: Weekly burn rate tracking for first 3 months
### Success Metrics:
- Achieve 18% MoM revenue growth by Q4 2024
- Launch AI code review feature on schedule
- Maintain runway above 15 months through year-end
- Begin Series B fundraising by October 2024
---
## Next Steps
1. **Immediate**: Begin recruitment process for 3 senior backend engineers
2. **Week 1**: Update board on hiring decision and runway impact
3. **Month 1**: Establish weekly burn rate monitoring dashboard
4. **Month 3**: Evaluate productivity impact and adjust revenue targets
5. **Month 6**: Initiate Series B fundraising conversations
This hiring decision aligns with TechStart's growth trajectory and positions the company to capitalize on market opportunity while maintaining financial discipline.

View File

@@ -0,0 +1,241 @@
#!/usr/bin/env python3
"""
Decision Matrix Tool - Strategic decision framework for complex choices
Custom Python script for the Chief of Staff agent
"""
import argparse
import json
def create_decision_matrix(options: list[dict], criteria: list[dict]) -> dict:
"""Create a weighted decision matrix for strategic choices"""
results = {"options": [], "winner": None, "analysis": {}}
for option in options:
option_scores = {
"name": option["name"],
"scores": {},
"weighted_scores": {},
"total": 0,
"pros": [],
"cons": [],
"verdict": "",
}
# Calculate scores for each criterion
for criterion in criteria:
crit_name = criterion["name"]
weight = criterion["weight"]
# Get score for this option on this criterion (1-10)
score = option.get(crit_name, 5)
weighted = score * weight
option_scores["scores"][crit_name] = score
option_scores["weighted_scores"][crit_name] = round(weighted, 2)
option_scores["total"] += weighted
# Track pros and cons
if score >= 8:
option_scores["pros"].append(f"Excellent {crit_name}")
elif score >= 6:
option_scores["pros"].append(f"Good {crit_name}")
elif score <= 3:
option_scores["cons"].append(f"Poor {crit_name}")
elif score <= 5:
option_scores["cons"].append(f"Weak {crit_name}")
option_scores["total"] = round(option_scores["total"], 2)
# Generate verdict
if option_scores["total"] >= 8:
option_scores["verdict"] = "STRONGLY RECOMMENDED"
elif option_scores["total"] >= 6.5:
option_scores["verdict"] = "RECOMMENDED"
elif option_scores["total"] >= 5:
option_scores["verdict"] = "ACCEPTABLE"
else:
option_scores["verdict"] = "NOT RECOMMENDED"
results["options"].append(option_scores)
# Find winner
results["options"].sort(key=lambda x: x["total"], reverse=True)
results["winner"] = results["options"][0]["name"]
# Generate analysis
results["analysis"] = generate_analysis(results["options"])
return results
def generate_analysis(options: list[dict]) -> dict:
"""Generate strategic analysis of the decision"""
analysis = {
"clear_winner": False,
"margin": 0,
"recommendation": "",
"key_differentiators": [],
"risks": [],
}
if len(options) >= 2:
margin = options[0]["total"] - options[1]["total"]
analysis["margin"] = round(margin, 2)
analysis["clear_winner"] = margin > 1.5
if analysis["clear_winner"]:
analysis["recommendation"] = (
f"Strongly recommend {options[0]['name']} with {margin:.1f} point advantage"
)
elif margin > 0.5:
analysis["recommendation"] = (
f"Recommend {options[0]['name']} but consider {options[1]['name']} as viable alternative"
)
else:
analysis["recommendation"] = (
f"Close decision between {options[0]['name']} and {options[1]['name']} - consider additional factors"
)
# Find key differentiators
top = options[0]
for criterion in top["scores"]:
if top["scores"][criterion] >= 8:
analysis["key_differentiators"].append(criterion)
# Identify risks
if top["total"] < 6:
analysis["risks"].append("Overall score below recommended threshold")
if len(top["cons"]) > len(top["pros"]):
analysis["risks"].append("More weaknesses than strengths")
return analysis
def main():
parser = argparse.ArgumentParser(description="Strategic decision matrix tool")
parser.add_argument("--scenario", type=str, help="Predefined scenario")
parser.add_argument("--input", type=str, help="JSON file with options and criteria")
parser.add_argument("--format", choices=["json", "text"], default="text")
args = parser.parse_args()
# Default scenario: Build vs Buy vs Partner
if args.scenario == "build-buy-partner":
options = [
{
"name": "Build In-House",
"cost": 3, # 1-10, higher is better (so 3 = high cost)
"time_to_market": 2, # 2 = slow
"control": 10, # 10 = full control
"quality": 8, # 8 = high quality potential
"scalability": 9, # 9 = very scalable
"risk": 3, # 3 = high risk
},
{
"name": "Buy Solution",
"cost": 5,
"time_to_market": 9,
"control": 4,
"quality": 7,
"scalability": 6,
"risk": 7,
},
{
"name": "Strategic Partnership",
"cost": 7,
"time_to_market": 7,
"control": 6,
"quality": 7,
"scalability": 8,
"risk": 5,
},
]
criteria = [
{"name": "cost", "weight": 0.20},
{"name": "time_to_market", "weight": 0.25},
{"name": "control", "weight": 0.15},
{"name": "quality", "weight": 0.20},
{"name": "scalability", "weight": 0.10},
{"name": "risk", "weight": 0.10},
]
elif args.input:
with open(args.input) as f:
data = json.load(f)
options = data["options"]
criteria = data["criteria"]
else:
# Default hiring scenario
options = [
{
"name": "Hire 3 Senior Engineers",
"cost": 4,
"productivity": 9,
"time_to_impact": 8,
"team_growth": 7,
"runway_impact": 3,
},
{
"name": "Hire 5 Junior Engineers",
"cost": 7,
"productivity": 5,
"time_to_impact": 4,
"team_growth": 9,
"runway_impact": 5,
},
]
criteria = [
{"name": "cost", "weight": 0.25},
{"name": "productivity", "weight": 0.30},
{"name": "time_to_impact", "weight": 0.20},
{"name": "team_growth", "weight": 0.15},
{"name": "runway_impact", "weight": 0.10},
]
matrix = create_decision_matrix(options, criteria)
if args.format == "json":
print(json.dumps(matrix, indent=2))
else:
# Text output
print("🎯 STRATEGIC DECISION MATRIX")
print("=" * 60)
print("\nOPTIONS EVALUATED:")
for i, opt in enumerate(matrix["options"], 1):
print(f"\n{i}. {opt['name']}")
print("-" * 40)
print(f" Total Score: {opt['total']}/10 - {opt['verdict']}")
print(" Strengths:")
for pro in opt["pros"][:3]:
print(f"{pro}")
if opt["cons"]:
print(" Weaknesses:")
for con in opt["cons"][:3]:
print(f"{con}")
print("\n" + "=" * 60)
print("RECOMMENDATION:")
print("-" * 40)
analysis = matrix["analysis"]
print(f"Winner: {matrix['winner']}")
print(f"Margin: {analysis['margin']} points")
print(f"\n{analysis['recommendation']}")
if analysis["key_differentiators"]:
print(f"\nKey advantages: {', '.join(analysis['key_differentiators'])}")
if analysis["risks"]:
print("\n⚠️ Risks to consider:")
for risk in analysis["risks"]:
print(f" - {risk}")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,128 @@
#!/usr/bin/env python3
"""
Financial Forecast Tool - Advanced financial modeling for strategic decisions
Custom Python tool executed via Bash by the Chief of Staff agent
"""
import argparse
import json
def forecast_financials(current_arr, growth_rate, months, burn_rate):
"""Generate financial forecast with multiple scenarios"""
forecasts = {"base_case": [], "optimistic": [], "pessimistic": [], "metrics": {}}
# Base case
arr = current_arr
for month in range(1, months + 1):
arr = arr * (1 + growth_rate)
monthly_revenue = arr / 12
net_burn = burn_rate - monthly_revenue
runway = -1 if net_burn <= 0 else (10_000_000 / net_burn) # Assuming $10M in bank
forecasts["base_case"].append(
{
"month": month,
"arr": round(arr),
"monthly_revenue": round(monthly_revenue),
"net_burn": round(net_burn),
"runway_months": round(runway, 1) if runway > 0 else "infinite",
}
)
# Optimistic (1.5x growth)
arr = current_arr
for month in range(1, months + 1):
arr = arr * (1 + growth_rate * 1.5)
forecasts["optimistic"].append({"month": month, "arr": round(arr)})
# Pessimistic (0.5x growth)
arr = current_arr
for month in range(1, months + 1):
arr = arr * (1 + growth_rate * 0.5)
forecasts["pessimistic"].append({"month": month, "arr": round(arr)})
# Key metrics
forecasts["metrics"] = {
"months_to_profitability": calculate_profitability_date(forecasts["base_case"]),
"cash_required": calculate_cash_needed(forecasts["base_case"]),
"break_even_arr": burn_rate * 12,
"current_burn_multiple": round(burn_rate / (current_arr / 12), 2),
}
return forecasts
def calculate_profitability_date(forecast):
"""Find when company becomes profitable"""
for entry in forecast:
if entry["net_burn"] <= 0:
return entry["month"]
return -1 # Not profitable in forecast period
def calculate_cash_needed(forecast):
"""Calculate total cash needed until profitability"""
total_burn = 0
for entry in forecast:
if entry["net_burn"] > 0:
total_burn += entry["net_burn"]
else:
break
return round(total_burn)
def main():
parser = argparse.ArgumentParser(description="Financial forecasting tool")
parser.add_argument("--arr", type=float, default=2400000, help="Current ARR")
parser.add_argument("--growth", type=float, default=0.15, help="Monthly growth rate")
parser.add_argument("--months", type=int, default=12, help="Forecast period")
parser.add_argument("--burn", type=float, default=500000, help="Monthly burn rate")
parser.add_argument("--format", choices=["json", "text"], default="text", help="Output format")
args = parser.parse_args()
forecast = forecast_financials(args.arr, args.growth, args.months, args.burn)
if args.format == "json":
print(json.dumps(forecast, indent=2))
else:
# Text output for human reading
print("📊 FINANCIAL FORECAST")
print("=" * 50)
print(f"Current ARR: ${args.arr:,.0f}")
print(f"Growth Rate: {args.growth * 100:.1f}% monthly")
print(f"Burn Rate: ${args.burn:,.0f}/month")
print()
print("BASE CASE PROJECTION:")
print("-" * 30)
for i in [2, 5, 11]: # Show months 3, 6, 12
if i < len(forecast["base_case"]):
m = forecast["base_case"][i]
print(f"Month {m['month']:2}: ARR ${m['arr']:,} | Runway {m['runway_months']}")
print()
print("KEY METRICS:")
print("-" * 30)
metrics = forecast["metrics"]
if metrics["months_to_profitability"] > 0:
print(f"Profitability: Month {metrics['months_to_profitability']}")
else:
print("Profitability: Not in forecast period")
print(f"Cash Needed: ${metrics['cash_required']:,}")
print(f"Burn Multiple: {metrics['current_burn_multiple']}x")
print()
print("SCENARIO ANALYSIS:")
print("-" * 30)
last_base = forecast["base_case"][-1]["arr"]
last_opt = forecast["optimistic"][-1]["arr"]
last_pess = forecast["pessimistic"][-1]["arr"]
print(f"12-Month ARR: ${last_pess:,} to ${last_opt:,}")
print(f"Range: {((last_opt - last_pess) / last_base * 100):.0f}% variance")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,95 @@
#!/usr/bin/env python3
"""
Hiring Impact Calculator for TechStart Inc
Calculates the financial impact of hiring engineers
"""
import json
import sys
def calculate_hiring_impact(num_engineers, salary_per_engineer=200000):
"""
Calculate the financial impact of hiring engineers.
Args:
num_engineers: Number of engineers to hire
salary_per_engineer: Annual salary per engineer (default: $200K)
Returns:
Dictionary with financial impact metrics
"""
# Current financials (from CLAUDE.md)
CURRENT_BURN_MONTHLY = 500000 # $500K/month
CURRENT_RUNWAY_MONTHS = 20 # 20 months
CASH_IN_BANK = 10000000 # $10M
# Calculate loaded cost (salary + benefits + taxes = salary * 1.3)
annual_loaded_cost_per_engineer = salary_per_engineer * 1.3
monthly_cost_per_engineer = annual_loaded_cost_per_engineer / 12
# Total monthly cost increase
total_monthly_increase = monthly_cost_per_engineer * num_engineers
# New burn rate
new_burn_monthly = CURRENT_BURN_MONTHLY + total_monthly_increase
# New runway
new_runway_months = CASH_IN_BANK / new_burn_monthly
runway_reduction_months = CURRENT_RUNWAY_MONTHS - new_runway_months
# Calculate potential revenue impact (assumption: engineers increase velocity by 15%)
velocity_increase = 0.15 * num_engineers / 5 # Assuming 5 engineers = 15% increase
# Recommendation
if runway_reduction_months > 3:
recommendation = "HIGH RISK: Significant runway reduction. Consider phased hiring."
elif runway_reduction_months > 1.5:
recommendation = "MODERATE RISK: Manageable if revenue growth accelerates."
else:
recommendation = "LOW RISK: Minimal impact on runway. Proceed if talent is available."
return {
"num_engineers": num_engineers,
"salary_per_engineer": salary_per_engineer,
"monthly_cost_per_engineer": round(monthly_cost_per_engineer, 2),
"total_monthly_increase": round(total_monthly_increase, 2),
"current_burn_monthly": CURRENT_BURN_MONTHLY,
"new_burn_monthly": round(new_burn_monthly, 2),
"current_runway_months": CURRENT_RUNWAY_MONTHS,
"new_runway_months": round(new_runway_months, 2),
"runway_reduction_months": round(runway_reduction_months, 2),
"velocity_increase_percent": round(velocity_increase * 100, 1),
"recommendation": recommendation,
}
def main():
# Parse command line arguments
if len(sys.argv) < 2:
print("Usage: python hiring_impact.py <num_engineers> [salary_per_engineer]")
sys.exit(1)
num_engineers = int(sys.argv[1])
salary = int(sys.argv[2]) if len(sys.argv) > 2 else 200000
# Calculate impact
impact = calculate_hiring_impact(num_engineers, salary)
# Output as JSON for easy parsing
print(json.dumps(impact, indent=2))
# Also print summary
print("\n=== HIRING IMPACT SUMMARY ===")
print(f"Hiring {impact['num_engineers']} engineers at ${impact['salary_per_engineer']:,}/year")
print(f"Monthly burn increase: ${impact['total_monthly_increase']:,.0f}")
print(f"New burn rate: ${impact['new_burn_monthly']:,.0f}/month")
print(
f"Runway change: {impact['current_runway_months']:.1f}{impact['new_runway_months']:.1f} months"
)
print(f"Velocity increase: +{impact['velocity_increase_percent']}%")
print(f"\n{impact['recommendation']}")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,42 @@
#!/usr/bin/env python3
"""
Simple script to demonstrate Bash tool usage from an agent.
Calculates basic metrics that an AI Chief of Staff might need.
"""
import json
import sys
def calculate_metrics(total_runway, monthly_burn):
"""Calculate key financial metrics."""
runway_months = total_runway / monthly_burn
quarterly_burn = monthly_burn * 3
metrics = {
"monthly_burn": monthly_burn,
"runway_months": runway_months,
"total_runway_dollars": total_runway,
"quarterly_burn": quarterly_burn,
"burn_rate_daily": round(monthly_burn / 30, 2),
}
return metrics
if __name__ == "__main__":
if len(sys.argv) < 3:
print("Usage: python simple_calculation.py <total_runway> <monthly_burn>")
sys.exit(1)
try:
runway = float(sys.argv[1])
burn = float(sys.argv[2])
results = calculate_metrics(runway, burn)
print(json.dumps(results, indent=2))
except ValueError:
print("Error: Arguments must be numbers")
sys.exit(1)

View File

@@ -0,0 +1,174 @@
#!/usr/bin/env python3
"""
Talent Scorer Tool - Evaluate and rank candidates based on multiple criteria
Custom Python tool for the Recruiter subagent
"""
import argparse
import json
def score_candidate(candidate: dict) -> dict:
"""Score a candidate based on weighted criteria"""
weights = {
"technical_skills": 0.30,
"experience_years": 0.20,
"startup_experience": 0.15,
"education": 0.10,
"culture_fit": 0.15,
"salary_fit": 0.10,
}
scores = {}
# Technical skills (0-100)
tech_match = candidate.get("tech_skills_match", 70)
scores["technical_skills"] = min(100, tech_match)
# Experience (0-100, peaks at 8 years)
years = candidate.get("years_experience", 5)
if years <= 2:
scores["experience_years"] = 40
elif years <= 5:
scores["experience_years"] = 70
elif years <= 8:
scores["experience_years"] = 90
else:
scores["experience_years"] = 85 # Slight decline for overqualified
# Startup experience (0-100)
scores["startup_experience"] = 100 if candidate.get("has_startup_exp", False) else 50
# Education (0-100)
education = candidate.get("education", "bachelors")
edu_scores = {"high_school": 40, "bachelors": 70, "masters": 85, "phd": 90}
scores["education"] = edu_scores.get(education, 70)
# Culture fit (0-100)
scores["culture_fit"] = candidate.get("culture_score", 75)
# Salary fit (0-100, penalize if too high or too low)
salary = candidate.get("salary_expectation", 150000)
target = candidate.get("target_salary", 160000)
diff_pct = abs(salary - target) / target
scores["salary_fit"] = max(0, 100 - (diff_pct * 200))
# Calculate weighted total
total = sum(scores[k] * weights[k] for k in weights)
return {
"name": candidate.get("name", "Unknown"),
"total_score": round(total, 1),
"scores": scores,
"recommendation": get_recommendation(total),
"risk_factors": identify_risks(candidate, scores),
}
def get_recommendation(score: float) -> str:
"""Generate hiring recommendation based on score"""
if score >= 85:
return "STRONG HIRE - Extend offer immediately"
elif score >= 75:
return "HIRE - Good candidate, proceed with offer"
elif score >= 65:
return "MAYBE - Consider if no better options"
elif score >= 50:
return "WEAK - Significant concerns, likely pass"
else:
return "NO HIRE - Does not meet requirements"
def identify_risks(candidate: dict, scores: dict) -> list[str]:
"""Identify potential risk factors"""
risks = []
if scores["technical_skills"] < 60:
risks.append("Technical skills below requirement")
if candidate.get("years_experience", 0) < 2:
risks.append("Limited experience, will need mentorship")
if not candidate.get("has_startup_exp", False):
risks.append("No startup experience, may struggle with ambiguity")
if scores["salary_fit"] < 50:
risks.append("Salary expectations misaligned")
if candidate.get("notice_period_days", 14) > 30:
risks.append(f"Long notice period: {candidate.get('notice_period_days')} days")
return risks
def rank_candidates(candidates: list[dict]) -> list[dict]:
"""Rank multiple candidates"""
scored = [score_candidate(c) for c in candidates]
return sorted(scored, key=lambda x: x["total_score"], reverse=True)
def main():
parser = argparse.ArgumentParser(description="Candidate scoring tool")
parser.add_argument("--input", type=str, help="JSON file with candidate data")
parser.add_argument("--name", type=str, help="Candidate name")
parser.add_argument("--years", type=int, default=5, help="Years of experience")
parser.add_argument("--tech-match", type=int, default=70, help="Technical skills match (0-100)")
parser.add_argument("--salary", type=int, default=150000, help="Salary expectation")
parser.add_argument("--startup", action="store_true", help="Has startup experience")
parser.add_argument("--format", choices=["json", "text"], default="text")
args = parser.parse_args()
if args.input:
# Score multiple candidates from file
with open(args.input) as f:
candidates = json.load(f)
results = rank_candidates(candidates)
else:
# Score single candidate from args
candidate = {
"name": args.name or "Candidate",
"years_experience": args.years,
"tech_skills_match": args.tech_match,
"salary_expectation": args.salary,
"has_startup_exp": args.startup,
"target_salary": 160000,
"culture_score": 75,
"education": "bachelors",
}
results = [score_candidate(candidate)]
if args.format == "json":
print(json.dumps(results, indent=2))
else:
# Text output
print("🎯 CANDIDATE EVALUATION")
print("=" * 50)
for i, result in enumerate(results, 1):
print(f"\n#{i}. {result['name']}")
print("-" * 30)
print(f"Overall Score: {result['total_score']}/100")
print(f"Recommendation: {result['recommendation']}")
print("\nScores by Category:")
for category, score in result["scores"].items():
print(f" {category.replace('_', ' ').title()}: {score:.0f}/100")
if result["risk_factors"]:
print("\n⚠️ Risk Factors:")
for risk in result["risk_factors"]:
print(f" - {risk}")
if len(results) > 1:
print("\n" + "=" * 50)
print("RANKING SUMMARY:")
for i, r in enumerate(results[:3], 1):
print(
f"{i}. {r['name']}: {r['total_score']:.1f} - {r['recommendation'].split(' - ')[0]}"
)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,111 @@
"""
Observability Agent - GitHub monitoring with MCP servers
Built on top of the research agent pattern
"""
import asyncio
import os
from collections.abc import Callable
from typing import Any
from dotenv import load_dotenv
from claude_code_sdk import ClaudeCodeOptions, ClaudeSDKClient
load_dotenv()
def get_activity_text(msg) -> str | None:
"""Extract activity text from a message"""
try:
if "Assistant" in msg.__class__.__name__:
if hasattr(msg, "content") and msg.content:
first_content = msg.content[0] if isinstance(msg.content, list) else msg.content
if hasattr(first_content, "name"):
return f"🤖 Using: {first_content.name}()"
return "🤖 Thinking..."
elif "User" in msg.__class__.__name__:
return "✓ Tool completed"
except (AttributeError, IndexError):
pass
return None
def print_activity(msg) -> None:
"""Print activity to console"""
activity = get_activity_text(msg)
if activity:
print(activity)
# Pre-configured GitHub MCP server
GITHUB_MCP_SERVER = {
"github": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-e",
"GITHUB_PERSONAL_ACCESS_TOKEN",
"ghcr.io/github/github-mcp-server",
],
"env": {"GITHUB_PERSONAL_ACCESS_TOKEN": os.environ.get("GITHUB_TOKEN")},
}
}
async def send_query(
prompt: str,
activity_handler: Callable[[Any], None | Any] = print_activity,
continue_conversation: bool = False,
mcp_servers: dict[str, Any] | None = None,
use_github: bool = True,
) -> str | None:
"""
Send a query to the observability agent with MCP server support.
Args:
prompt: The query to send
activity_handler: Callback for activity updates
continue_conversation: Continue the previous conversation if True
mcp_servers: Custom MCP servers configuration
use_github: Include GitHub MCP server (default: True)
Returns:
The final result text or None if no result
"""
# Build MCP servers config
servers = {}
if use_github and os.environ.get("GITHUB_TOKEN"):
servers.update(GITHUB_MCP_SERVER)
if mcp_servers:
servers.update(mcp_servers)
options = ClaudeCodeOptions(
model="claude-sonnet-4-20250514",
allowed_tools=["mcp__github", "WebSearch", "Read"],
continue_conversation=continue_conversation,
system_prompt="You are an observability agent specialized in monitoring GitHub repositories and CI/CD workflows",
mcp_servers=servers if servers else None,
permission_mode="acceptEdits",
)
result = None
try:
async with ClaudeSDKClient(options=options) as agent:
await agent.query(prompt=prompt)
async for msg in agent.receive_response():
if asyncio.iscoroutinefunction(activity_handler):
await activity_handler(msg)
else:
activity_handler(msg)
if hasattr(msg, "result"):
result = msg.result
except Exception as e:
print(f"❌ Query error: {e}")
raise
return result

View File

@@ -0,0 +1,36 @@
# Observability Agent Architecture
```mermaid
graph TD
User[User] --> Agent[Observability Agent]
Agent --> GitHub[GitHub MCP Server]
Agent --> Tools[Tools]
Tools --> WebSearch[WebSearch]
Tools --> Read[Read Files]
GitHub --> Docker[Docker Container]
Docker --> API[GitHub API]
style Agent fill:#f9f,stroke:#333,stroke-width:3px
style GitHub fill:#bbf,stroke:#333,stroke-width:2px
```
# Communication Flow Diagram
```mermaid
sequenceDiagram
participant User
participant Agent
participant MCP as GitHub MCP
participant API as GitHub API
User->>Agent: Query about repo
Agent->>MCP: Connect via Docker
Agent->>MCP: Request data
MCP->>API: Fetch info
API-->>MCP: Return data
MCP-->>Agent: Process results
Agent-->>User: Display answer
```

View File

@@ -0,0 +1,36 @@
FROM python:3.11
WORKDIR /app
# Install system dependencies including Docker CLI
RUN apt-get update && apt-get install -y \
curl \
git \
ca-certificates \
gnupg \
&& install -m 0755 -d /etc/apt/keyrings \
&& curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg \
&& echo "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \
"$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" > /etc/apt/sources.list.d/docker.list \
&& apt-get update \
&& apt-get install -y docker-ce-cli \
&& curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
&& apt-get install -y nodejs \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
RUN npm install -g @anthropic-ai/claude-code
COPY observability_agent ./observability_agent
RUN pip install --no-cache-dir \
claude-code-sdk \
fastapi \
python-dotenv \
uvicorn[standard] \
mcp-server-git
RUN claude --version
EXPOSE 8000
CMD ["python", "-m", "observability_agent.web.app"]

View File

@@ -0,0 +1,18 @@
services:
observability-agent:
build:
context: ../..
dockerfile: observability_agent/docker/Dockerfile
ports:
- "8001:8000" # Different port to avoid conflict with research agent
env_file:
- ../../.env
environment:
- DOCKER_HOST=unix:///var/run/docker.sock
volumes:
# Mount Docker socket for MCP server containers
- /var/run/docker.sock:/var/run/docker.sock
dns:
- 8.8.8.8
- 8.8.4.4
restart: unless-stopped

View File

@@ -0,0 +1,12 @@
[project]
name = "cc-sdk-tutorial"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"claude-code-sdk>=0.0.20",
"ipykernel>=6.29.5",
"mcp-server-git>=2025.1.14",
"python-dotenv>=1.1.1",
]

View File

@@ -0,0 +1,87 @@
"""
Research Agent - Using Claude SDK with built-in session management
"""
import asyncio
from collections.abc import Callable
from typing import Any
from dotenv import load_dotenv
from claude_code_sdk import ClaudeCodeOptions, ClaudeSDKClient
load_dotenv()
def get_activity_text(msg) -> str | None:
"""Extract activity text from a message"""
try:
if "Assistant" in msg.__class__.__name__:
# Check if content exists and has items
if hasattr(msg, "content") and msg.content:
first_content = msg.content[0] if isinstance(msg.content, list) else msg.content
if hasattr(first_content, "name"):
return f"🤖 Using: {first_content.name}()"
return "🤖 Thinking..."
elif "User" in msg.__class__.__name__:
return "✓ Tool completed"
except (AttributeError, IndexError):
pass
return None
def print_activity(msg) -> None:
"""Print activity to console"""
activity = get_activity_text(msg)
if activity:
print(activity)
async def send_query(
prompt: str,
activity_handler: Callable[[Any], None | Any] = print_activity,
continue_conversation: bool = False,
) -> str | None:
"""
Send a query using the Claude SDK with minimal overhead.
Args:
prompt: The query to send
activity_handler: Callback for activity updates
continue_conversation: Continue the previous conversation if True
Note:
For the activity_handler - we support both sync and async handlers
to make the module work in different contexts:
- Sync handlers (like print_activity) for simple console output
- Async handlers for web apps that need WebSocket/network I/O
In production, you'd typically use just one type based on your needs
Returns:
The final result text or None if no result
"""
options = ClaudeCodeOptions(
model="claude-sonnet-4-20250514",
allowed_tools=["WebSearch", "Read"],
continue_conversation=continue_conversation,
system_prompt="You are a research agent specialized in AI",
)
result = None
try:
async with ClaudeSDKClient(options=options) as agent:
await agent.query(prompt=prompt)
async for msg in agent.receive_response():
if asyncio.iscoroutinefunction(activity_handler):
await activity_handler(msg)
else:
activity_handler(msg)
if hasattr(msg, "result"):
result = msg.result
except Exception as e:
print(f"❌ Query error: {e}")
raise
return result

View File

@@ -0,0 +1,32 @@
# Research Agent Architecture
```mermaid
graph TD
User[User] --> Agent[Research Agent]
Agent --> Tools[Tools]
Tools --> WebSearch[WebSearch]
Tools --> Read[Read Files/Images]
style Agent fill:#f9f,stroke:#333,stroke-width:3px
style Tools fill:#bbf,stroke:#333,stroke-width:2px
```
# Communication Flow Diagram
```mermaid
sequenceDiagram
participant User
participant Agent
participant Tools
User->>Agent: Query
loop Until Complete
Agent->>Agent: Think
Agent->>Tools: Search/Read
Tools-->>Agent: Results
end
Agent-->>User: Answer
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 742 KiB

View File

@@ -0,0 +1,106 @@
def print_activity(msg):
if "Assistant" in msg.__class__.__name__:
print(
f"🤖 {'Using: ' + msg.content[0].name + '()' if hasattr(msg.content[0], 'name') else 'Thinking...'}"
)
elif "User" in msg.__class__.__name__:
print("✓ Tool completed")
def print_final_result(messages):
"""Print the final agent result and cost information"""
# Get the result message (last message)
result_msg = messages[-1]
# Find the last assistant message with actual content
for msg in reversed(messages):
if msg.__class__.__name__ == "AssistantMessage" and msg.content:
# Check if it has text content (not just tool use)
for block in msg.content:
if hasattr(block, "text"):
print(f"\n📝 Final Result:\n{block.text}")
break
break
# Print cost if available
if hasattr(result_msg, "total_cost_usd"):
print(f"\n📊 Cost: ${result_msg.total_cost_usd:.2f}")
# Print duration if available
if hasattr(result_msg, "duration_ms"):
print(f"⏱️ Duration: {result_msg.duration_ms / 1000:.2f}s")
def visualize_conversation(messages):
"""Create a visual representation of the entire agent conversation"""
print("\n" + "=" * 60)
print("🤖 AGENT CONVERSATION TIMELINE")
print("=" * 60 + "\n")
for i, msg in enumerate(messages):
msg_type = msg.__class__.__name__
if msg_type == "SystemMessage":
print("⚙️ System Initialized")
if hasattr(msg, "data") and "session_id" in msg.data:
print(f" Session: {msg.data['session_id'][:8]}...")
print()
elif msg_type == "AssistantMessage":
print("🤖 Assistant:")
if msg.content:
for block in msg.content:
if hasattr(block, "text"):
# Text response
text = block.text[:500] + "..." if len(block.text) > 500 else block.text
print(f" 💬 {text}")
elif hasattr(block, "name"):
# Tool use
tool_name = block.name
print(f" 🔧 Using tool: {tool_name}")
# Show key parameters for certain tools
if hasattr(block, "input") and block.input:
if tool_name == "WebSearch" and "query" in block.input:
print(f' Query: "{block.input["query"]}"')
elif tool_name == "TodoWrite" and "todos" in block.input:
todos = block.input["todos"]
in_progress = [t for t in todos if t["status"] == "in_progress"]
completed = [t for t in todos if t["status"] == "completed"]
print(
f" 📋 {len(completed)} completed, {len(in_progress)} in progress"
)
print()
elif msg_type == "UserMessage":
if msg.content and isinstance(msg.content, list):
for result in msg.content:
if isinstance(result, dict) and result.get("type") == "tool_result":
print("👤 Tool Result Received")
tool_id = result.get("tool_use_id", "unknown")[:8]
print(f" ID: {tool_id}...")
# Show result summary
if "content" in result:
content = result["content"]
if isinstance(content, str):
# Show more of the content
summary = content[:500] + "..." if len(content) > 500 else content
print(f" 📥 {summary}")
print()
elif msg_type == "ResultMessage":
print("✅ Conversation Complete")
if hasattr(msg, "num_turns"):
print(f" Turns: {msg.num_turns}")
if hasattr(msg, "total_cost_usd"):
print(f" Cost: ${msg.total_cost_usd:.2f}")
if hasattr(msg, "duration_ms"):
print(f" Duration: {msg.duration_ms / 1000:.2f}s")
if hasattr(msg, "usage"):
usage = msg.usage
total_tokens = usage.get("input_tokens", 0) + usage.get("output_tokens", 0)
print(f" Tokens: {total_tokens:,}")
print()
print("=" * 60 + "\n")

View File

@@ -56,8 +56,8 @@ def main():
else:
print("\n⚠️ Found issues that should be fixed in a separate PR")
# For POC, return 0 even with issues to show detection without blocking
sys.exit(0)
# Exit with error if issues found
sys.exit(1 if has_issues else 0)
if __name__ == "__main__":