Skip to content

Commit 002cd42

Browse files
authored
Initial commit
0 parents  commit 002cd42

112 files changed

Lines changed: 4880 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.claude/CLAUDE.md‎

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## What This Is
6+
7+
A monorepo containing three parallel implementations of a **Starter Agent** for Slack built with Bolt for Python. All implementations are functionally identical from the Slack user's perspective but use different AI agent frameworks:
8+
9+
- `claude-agent-sdk/` -- Built with **Claude Agent SDK**
10+
- `openai-agents-sdk/` -- Built with **OpenAI Agents SDK**
11+
- `pydantic-ai/` -- Built with **Pydantic AI**
12+
13+
This is a minimal starter template. It includes one example tool (emoji reactions) and optional Slack MCP Server integration.
14+
15+
## Commands
16+
17+
All commands must be run from within the respective project directory (`claude-agent-sdk/`, `openai-agents-sdk/`, or `pydantic-ai/`).
18+
19+
```sh
20+
# Run the app (requires .env with OPENAI_API_KEY or ANTHROPIC_API_KEY; Slack tokens optional with CLI)
21+
slack run # via Slack CLI
22+
python3 app.py # directly
23+
24+
# Lint and format (CI runs these on push to main and all PRs)
25+
ruff check .
26+
ruff format --check .
27+
28+
# Run tests
29+
pytest
30+
```
31+
32+
## Monorepo Structure
33+
34+
```
35+
.github/ # Shared CI workflows and dependabot config
36+
claude-agent-sdk/ # Claude Agent SDK implementation
37+
openai-agents-sdk/ # OpenAI Agents SDK implementation
38+
pydantic-ai/ # Pydantic AI implementation
39+
```
40+
41+
CI runs ruff lint/format checks against all directories via a matrix strategy in `.github/workflows/ruff.yml`. Dependabot monitors `requirements.txt` in all directories independently.
42+
43+
## Architecture (shared across all implementations)
44+
45+
Three-layer design: **app.py** -> **listeners/** -> **agent/**
46+
47+
**Entry point (`app.py`)** initializes Bolt with Socket Mode and calls `register_listeners(app)`.
48+
49+
**Listeners** are organized by Slack platform feature:
50+
- `listeners/events/` -- `app_home_opened`, `app_mentioned`, `message`
51+
- `listeners/actions/` -- `feedback_buttons`
52+
53+
Each sub-package has a `register(app)` function called from `listeners/__init__.py`.
54+
55+
**AgentDeps** (`agent/deps.py`) is a dataclass carrying `client`, `user_id`, `channel_id`, `thread_ts`. Constructed in each listener handler and passed to the agent at runtime.
56+
57+
**Conversation history** (`thread_context/store.py`) is a thread-safe in-memory dict keyed by `(channel_id, thread_ts)` with TTL-based cleanup. This enables multi-turn context.
58+
59+
**Handler flow** (DM, mention): get history from store -> run agent -> post response in thread with feedback blocks -> store updated messages.
60+
61+
## Key Differences Between Implementations
62+
63+
| Aspect | Claude Agent SDK | OpenAI Agents SDK | Pydantic AI |
64+
|--------|-----------------|-------------------|-------------|
65+
| Agent file | `agent/agent.py` | `agent/agent.py` | `agent/agent.py` |
66+
| App type | `AsyncApp` (fully async) | `App` (sync) | `App` (sync) |
67+
| Agent definition | `ClaudeSDKClient` with `ClaudeAgentOptions` | `Agent[AgentDeps](model="gpt-4.1-mini")` | `Agent(deps_type=AgentDeps)` |
68+
| Model config | Managed by SDK (Claude models) | Set directly on agent constructor | `get_model()` selects provider at runtime (Anthropic preferred) |
69+
| Tool definition | `@tool` decorated functions via MCP server | `@function_tool` decorated functions | Plain async functions |
70+
| Tool context param | `args` dict (no context param) | `RunContextWrapper[AgentDeps]` | `RunContext[AgentDeps]` |
71+
| Execution | `await run_agent(text, session_id=...)` | `Runner.run_sync(agent, input=..., context=...)` | `agent.run_sync(text, model=..., deps=..., message_history=...)` |
72+
| Result output | `response_text` from collected `TextBlock.text` | `result.final_output` | `result.output` |
73+
| Conversation history | Session-based via `resume` (server-side) | `list` stored locally | `list[ModelMessage]` stored locally |
74+
| API key env var | `ANTHROPIC_API_KEY` | `OPENAI_API_KEY` | `ANTHROPIC_API_KEY` or `OPENAI_API_KEY` |
75+
| Feedback blocks | Native `FeedbackButtonsElement` | Native `FeedbackButtonsElement` | Native `FeedbackButtonsElement` |

‎.claude/commands/unvendor-bolt.md‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
Switch from the vendored bolt-python wheel to the latest published `slack-bolt` package on PyPI.
2+
3+
## Steps
4+
5+
1. Look up the latest published version of `slack-bolt`:
6+
```
7+
curl -s https://pypi.org/pypi/slack-bolt/json | python3 -c "import sys,json; print(json.load(sys.stdin)['info']['version'])"
8+
```
9+
10+
2. Delete the `vendor/` directory:
11+
```
12+
rm -rf vendor
13+
```
14+
15+
3. In all three `requirements.txt` files (`claude-agent-sdk/requirements.txt`, `openai-agents-sdk/requirements.txt`, `pydantic-ai/requirements.txt`), replace the `../vendor/slack_bolt-*.whl` line with `slack-bolt>=<version>` using the version from step 1.
16+
17+
4. In all three `pyproject.toml` files (`claude-agent-sdk/pyproject.toml`, `openai-agents-sdk/pyproject.toml`, `pydantic-ai/pyproject.toml`), replace the comment `# slack-bolt is installed from a vendored whl — see ../vendor/` with an actual dependency `"slack-bolt>=<version>"` using the version from step 1.
18+
19+
5. For each implementation directory, if a `.venv` virtual environment exists, activate it and install the updated requirements:
20+
```
21+
if [ -f <dir>/.venv/bin/activate ]; then
22+
source <dir>/.venv/bin/activate && pip install -r <dir>/requirements.txt
23+
fi
24+
```
25+
26+
6. If `.github/dependabot.yml` has an `ignore` rule for `slack-bolt`, remove it so Dependabot can manage updates.
27+
28+
7. Update `README.md` — replace the "Local Development" section that describes vendored bolt with:
29+
```
30+
## Local Development
31+
32+
This repo uses [`slack-bolt`](https://pypi.org/project/slack-bolt/) from PyPI.
33+
```
34+
35+
8. Report the version change to the user. Do NOT commit — let the user review first.

‎.claude/commands/vendor-bolt.md‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
Update the vendored bolt-python wheel to the latest commit on the `main` branch.
2+
3+
## Steps
4+
5+
1. If `.bolt-python-build/` exists, `cd` into it and run `git pull`. Otherwise, clone bolt-python:
6+
```
7+
git clone --depth 1 https://github.com/slackapi/bolt-python.git .bolt-python-build
8+
```
9+
10+
2. `cd .bolt-python-build` and get the HEAD commit SHA (short form, 7 chars):
11+
```
12+
SHA=$(git rev-parse --short HEAD)
13+
```
14+
15+
3. Read the current version from `slack_bolt/version.py` and patch it to append `+<SHA>` as a PEP 440 local version identifier:
16+
```
17+
sed -i '' "s/__version__ = \"\(.*\)\"/__version__ = \"\1+$SHA\"/" slack_bolt/version.py
18+
```
19+
20+
4. Build the wheel:
21+
```
22+
python -m build --wheel
23+
```
24+
25+
5. Remove any old `.whl` files from `vendor/` and copy the new one in:
26+
```
27+
rm -f ../vendor/slack_bolt-*.whl
28+
cp dist/slack_bolt-*.whl ../vendor/
29+
```
30+
31+
6. Update the whl filename in all three `requirements.txt` files (`claude-agent-sdk/requirements.txt`, `openai-agents-sdk/requirements.txt`, `pydantic-ai/requirements.txt`). Replace the existing `../vendor/slack_bolt-*.whl` line with the new filename.
32+
33+
7. Clean up the build directory:
34+
```
35+
cd .. && rm -rf .bolt-python-build
36+
```
37+
38+
8. Report the old version/SHA vs new version/SHA to the user. Do NOT commit — let the user review first.

‎.github/CODEOWNERS‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Salesforce Open Source project configuration
2+
# Learn more: https://github.com/salesforce/oss-template
3+
#ECCN:Open Source
4+
#GUSINFO:Open Source,Open Source Workflow
5+
6+
# Code owners are the default owners for everything in
7+
# this repository. The owners listed below will be requested for
8+
# review when a pull request is opened.
9+
# To set code owner(s), replace the team below with either
10+
# their GitHub username(s) or a GitHub team.
11+
* @slack-samples/maintainers

‎.github/MAINTAINERS_GUIDE.md‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Maintainers Guide
2+
3+
## Using Bolt Framework Development Builds
4+
5+
This repo vendors a pre-release build of [`slack-bolt`](https://github.com/slackapi/bolt-python) from the `main` branch. The `.whl` wheel lives in `vendor/` and is referenced by each implementation's `requirements.txt`.
6+
7+
Two [Claude Code slash commands](https://docs.anthropic.com/en/docs/claude-code/slash-commands) are available to manage this:
8+
9+
### `/vendor-bolt` — Update the vendored wheel
10+
11+
Clones bolt-python `main`, builds a wheel with a local version identifier (e.g. `1.2.3+abc1234`), replaces the wheel in `vendor/`, and updates all `requirements.txt` references. Use this to pull in the latest unreleased bolt-python changes.
12+
13+
### `/unvendor-bolt` — Switch to the published PyPI package
14+
15+
Removes the `vendor/` directory, replaces the wheel reference in all `requirements.txt` and `pyproject.toml` files with the latest `slack-bolt` version from PyPI, and re-enables Dependabot updates. Use this when the vendored changes have been released and the repo should track the public package.
16+
17+
Both commands leave changes uncommitted for review.

‎.github/dependabot.yml‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: "pip"
4+
directories:
5+
- "/"
6+
- "/openai-agents-sdk"
7+
- "/pydantic-ai"
8+
- "/claude-agent-sdk"
9+
schedule:
10+
interval: "weekly"
11+
labels:
12+
- "pip"
13+
- "dependencies"
14+
- package-ecosystem: "github-actions"
15+
directory: "/"
16+
schedule:
17+
interval: "weekly"

‎.github/workflows/dependencies.yml‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: Merge updates to dependencies
2+
on:
3+
pull_request:
4+
jobs:
5+
dependabot:
6+
name: "@dependabot"
7+
if: github.event.pull_request.user.login == 'dependabot[bot]'
8+
runs-on: ubuntu-latest
9+
permissions:
10+
contents: write
11+
pull-requests: write
12+
steps:
13+
- name: Collect metadata
14+
id: metadata
15+
uses: dependabot/fetch-metadata@25dd0e34f4fe68f24cc83900b1fe3fe149efef98 # v3.1.0
16+
with:
17+
github-token: "${{ secrets.GITHUB_TOKEN }}"
18+
- name: Approve
19+
if: steps.metadata.outputs.update-type == 'version-update:semver-patch' || steps.metadata.outputs.update-type == 'version-update:semver-minor'
20+
run: gh pr review --approve "$PR_URL"
21+
env:
22+
PR_URL: ${{github.event.pull_request.html_url}}
23+
GH_TOKEN: ${{secrets.GITHUB_TOKEN}}
24+
- name: Automerge
25+
if: steps.metadata.outputs.update-type == 'version-update:semver-patch' || steps.metadata.outputs.update-type == 'version-update:semver-minor'
26+
run: gh pr merge --auto --squash "$PR_URL"
27+
env:
28+
PR_URL: ${{ github.event.pull_request.html_url }}
29+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

‎.github/workflows/ruff.yml‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: Linting and formatting validation using ruff
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
8+
jobs:
9+
lint:
10+
runs-on: ubuntu-latest
11+
timeout-minutes: 5
12+
strategy:
13+
matrix:
14+
directory:
15+
- openai-agents-sdk
16+
- pydantic-ai
17+
- claude-agent-sdk
18+
defaults:
19+
run:
20+
working-directory: ${{ matrix.directory }}
21+
22+
steps:
23+
- uses: actions/checkout@v7
24+
- name: Set up Python
25+
uses: actions/setup-python@v7
26+
with:
27+
python-version: "3.13"
28+
- name: Install dependencies
29+
run: |
30+
pip install -U pip
31+
pip install -r requirements.txt
32+
- name: Lint and check formatting with ruff
33+
run: |
34+
ruff check .
35+
ruff format --check .

‎.github/workflows/tests.yml‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: Run all the unit tests
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
8+
jobs:
9+
build:
10+
runs-on: ubuntu-latest
11+
timeout-minutes: 5
12+
strategy:
13+
matrix:
14+
directory:
15+
- openai-agents-sdk
16+
- pydantic-ai
17+
- claude-agent-sdk
18+
defaults:
19+
run:
20+
working-directory: ${{ matrix.directory }}
21+
22+
steps:
23+
- uses: actions/checkout@v7
24+
- name: Set up Python
25+
uses: actions/setup-python@v7
26+
with:
27+
python-version: "3.13"
28+
- name: Install dependencies
29+
run: |
30+
pip install -U pip
31+
pip install -r requirements.txt
32+
pip install ".[test]"
33+
- name: Run all tests
34+
run: |
35+
pytest

‎.gitignore‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Build
2+
.bolt-python-build/
3+
4+
# Claude Code
5+
.claude/*.local.json
6+
7+
# Git
8+
.worktrees/
9+
10+
# macOS
11+
.DS_Store
12+
13+
# Slack
14+
*/.slack/apps.json
15+
*/.slack/apps.dev.json

0 commit comments

Comments
 (0)