Autonomous AI coding agent that works off a task queue, switches between tasks when blocked, and delivers PRs
Project description
SWEetie
sweetie: Self-managing Worker for Engineering Excellence, Tasks, Implementation, and Execution. AKA your own personal AI SWE, AKA SWE-etie
Autonomous AI coding agent that pulls tasks from Notion, works on them via Claude Code, notifies you on Slack when blocked, switches to the next task, and delivers PRs when done.
How it works
Poll Notion → Claim task → Create branch → Run Claude Code
↓
Success? → Run tests → Create PR
Blocked? → Notify Slack → Pick next task
Sweetie runs as a single-process daemon. It polls your Notion task board, picks the highest-priority queued task, creates a branch, and hands it to Claude Code. If Claude gets stuck, Sweetie messages you on Slack and moves on to the next task. When a task is done, it opens a PR for your review.
Prerequisites
- Python 3.11+
- Claude Code CLI installed and authenticated
Install
# From PyPI (recommended)
pip install sweetie
# Or with pipx for an isolated install
pipx install sweetie
# Or from source
git clone https://github.com/maryanngong/sweetie.git
cd sweetie
pip install -e .
Quick start
- Create a
sweetie.yamlconfig file in your workspace. If you create it in the repo you want Sweetie to work on, make sure to add it to your .gitignore so sensitive credentials do not get pushed to git.
cd /path/to/my-repo
# If you installed from source, copy the example:
cp /path/to/sweetie/sweetie.example.yaml sweetie.yaml
# If you installed from PyPI, create one manually or download the example:
curl -O https://raw.githubusercontent.com/maryanngong/sweetie/main/sweetie.example.yaml
mv sweetie.example.yaml sweetie.yaml
Sweetie looks for sweetie.yaml in the current directory when you run sweetie start. You can also specify a path with sweetie start --config /path/to/sweetie.yaml.
-
Fill in your Notion, Slack, and Github tokens (see Integration setup below).
-
Start sweetie from inside your repo:
cd /path/to/my-repo
sweetie start
Integration setup
Sweetie connects to three services. Here's what you need for each.
Notion
What you need: An API key and a database ID.
- Go to notion.so/my-integrations and create a new integration.
- Grant it Read content, Update content, and Insert content capabilities.
- Copy the Internal Integration Secret — this is your
api_key. - Open your task database in Notion. Make sure it has all required columns.
- To get your datasource ID, click the settings for that table view. In the menu, under
Data source settings, selectManage data sources, find your data source, click...menu, andCopy data source ID.
notion:
api_key: "ntn_..." # or "${NOTION_API_KEY}" to use an env var
database_id: "313b5941-b260-1234-aabb-000b06a6f452"
Database columns: Your Notion database needs at minimum these columns (names are configurable via columns:):
| Column | Type | Description |
|---|---|---|
| Task | Title | Task name |
| Description | Rich Text | What to implement |
| Status | Status | Queued, In Progress, Blocked, In Review, Done, Failed |
| Priority | Select | P0, P1, P2 (or your own values) |
Sweetie will also read/write these optional columns:
| Column | Type | Used by |
|---|---|---|
| Agent Branch | Rich Text | Sweetie will write the git branch it created |
| Agent PR | URL | Sweetie will write the link to the PR |
| Agent Notes | Rich Text | Sweetie will write status updates and summaries |
| Blocked Reason | Rich Text | Sweetie will write the question it needs answered |
| Cost | Number | Sweetie writes API cost in USD |
| Slack Thread TS | Rich Text | Sweetie will write Slack thread timestamp for reply monitoring |
| Slack Channel | Rich Text | Sweetie will write Slack channel ID for reply monitoring |
| Repo | Rich Text | You can specify a path to the repo for this task |
| Test Command | Rich Text | You can override the test command per task |
If your column names or status values differ from the defaults, map them in config:
notion:
columns:
title: "Name" # default: "Task"
status: "Status"
status_values:
queued: "Ready" # default: "Queued"
in_progress: "Working" # default: "In Progress"
priority_values: ["High", "Medium", "Low"] # default: ["P0", "P1", "P2"]
Slack
Sweetie uses Slack to notify you when tasks are started, completed, blocked, or failed.
Basic setup (notifications only): Create a Slack incoming webhook and add the URL to config:
slack:
webhook_url: "https://hooks.slack.com/services/..."
Thread reply monitoring (recommended): If you want to unblock tasks by replying directly in the Slack thread, you also need a Slack Bot Token:
- Go to api.slack.com/apps and create a new app (or use the one from your webhook).
- Under OAuth & Permissions, add these Bot Token Scopes:
chat:write— to post messages and get thread timestampschannels:history— to read thread replies
- Install (or reinstall) the app to your workspace and copy the Bot User OAuth Token (
xoxb-...). - Add the bot to your channel: Go to the channel in Slack and type
/invite @YourAppName, or click the channel name → Integrations → Add apps. The bot must be in the channel to post messages and read replies. - Get the channel ID: right-click the channel name → View channel details → copy the ID at the bottom (e.g.
C0AF0LZEEFF).
Note: If you add scopes later, you must reinstall the app to your workspace for the new scopes to take effect. Slack requires this after any scope change.
slack:
webhook_url: "https://hooks.slack.com/services/..."
bot_token: "xoxb-..." # or "${SLACK_BOT_TOKEN}"
channel: "C0AF0LZEE64"
When a task is blocked, Sweetie will post a message with the question. Reply in the thread, and Sweetie will automatically pick up your answer and resume the task when it looks for its next task.
GitHub
Sweetie needs a GitHub token to push branches and create PRs.
- Go to github.com/settings/tokens and create a Fine-grained personal access token.
- Grant it access to the repos Sweetie will work on.
- Under Repository permissions, enable:
- Contents — Read and write (to push branches)
- Pull requests — Read and write (to create PRs)
Provide the token in one of these ways (checked in order):
# Option 1: In sweetie.yaml
github:
token: "github_pat_..." # or "${GITHUB_TOKEN}"
# Option 2: Environment variable
export GITHUB_TOKEN="github_pat_..."
# or
export GH_TOKEN="github_pat_..."
Usage
# Start the daemon from your repo directory
cd /path/to/my-repo
sweetie start
# Start with a clean session (resets cost tracking, blocked tasks, etc.)
sweetie start --fresh
# Start with a custom config path
sweetie start --config ./my-config.yaml
# Start with debug logging
sweetie start --verbose
# Validate your config file
sweetie validate
# Check current agent status (blocked tasks, cost, etc.)
sweetie status
# Re-queue a blocked task from the CLI
sweetie unblock 303b5941 # prefix match on task ID
# Reset session state
sweetie reset
File locations
| File | Location | Purpose |
|---|---|---|
sweetie.yaml |
Created by you - Current directory (or --config path) |
Configuration — tokens, settings, repo config |
~/.sweetie/state.json |
Home directory | Written by Sweetie - Session state — active task, blocked tasks, cost tracking |
The config file lives with your repo so different projects can have different settings. The state file is global so sweetie status works from any directory.
Important: sweetie.yaml contains sensitive tokens. Add it to your .gitignore:
echo "sweetie.yaml" >> .gitignore
How tasks flow
- You add a task to Notion with status Queued
- Sweetie picks it up, sets status to In Progress, creates a
sweetie-agent/<task-name>branch - Claude Code implements the task
- If blocked → status Blocked, Slack notification with the question, moves to next task
- If done → runs tests (if configured), creates PR, status In Review
- You review the PR and merge
Unblocking a task
Three ways to unblock a blocked task:
- Slack thread reply (if
bot_token+channelconfigured) — reply in the thread and Sweetie picks it up automatically - CLI — run
sweetie unblock <task-id> - Notion — change the status back to Queued manually
Re-queuing after PR review
If you review a PR and want changes:
- Leave review comments on the PR (inline or general)
- Optionally push your own commits to the branch
- Set the task status back to Queued in Notion
Sweetie will pick it up, pull the latest branch, fetch all PR comments, and pass them to Claude as context.
Model selection
Choose which Claude model Sweetie uses in your config:
agent:
model: "opus" # Options: "sonnet", "opus", "haiku"
- opus — most capable, best for complex multi-file tasks, highest cost
- sonnet — good balance of capability and cost (default)
- haiku — fastest and cheapest, good for simple/straightforward tasks
Budget limits
Sweetie tracks API costs and enforces two budget limits:
agent:
max_cost_per_task_usd: 5.00 # Max spend on a single task
max_cost_per_session_usd: 50.00 # Max total spend before auto-shutdown
- Per-task budget — if a single task exceeds this, Sweetie stops working on it and marks it failed.
- Per-session budget — once the total spend across all tasks hits this limit, Sweetie sends a Slack notification and shuts down. Use
sweetie start --freshto reset the session cost counter.
You can check current spend at any time with sweetie status.
Configuration reference
Full example in sweetie.example.yaml. Key sections:
# Agent behavior
agent:
model: "sonnet" # Claude model (sonnet, opus, haiku)
max_turns_per_task: 50 # Max Claude turns before stopping
max_retries_on_test_fail: 2 # Retry count when tests fail
max_cost_per_task_usd: 5.00 # Budget per task
max_cost_per_session_usd: 50 # Total session budget
poll_interval_seconds: 30 # How often to poll Notion for new tasks
loop_delay_seconds: 5 # Pause between main loop iterations
# Per-repo settings (optional — defaults to current directory)
repos:
"/path/to/repo":
test_command: "npm test" # Run after task completion
lint_command: "npm run lint" # Optional lint step
base_branch: "main" # Branch to base work off of
allowed_tools: # Extra Bash commands Claude can use
- "npm install"
# Safety guardrails
safety:
blocked_bash_patterns: # Commands Claude is never allowed to run
- "rm -rf"
- "git push --force"
- "DROP TABLE"
require_tests_pass: true # Block PR if tests fail
require_lint_pass: false # Block PR if lint fails
Environment variable interpolation
Any string value in sweetie.yaml can reference environment variables with ${VAR_NAME}:
notion:
api_key: "${NOTION_API_KEY}"
slack:
webhook_url: "${SLACK_WEBHOOK_URL}"
github:
token: "${GITHUB_TOKEN}"
Troubleshooting
Slack thread replies aren't picked up
Sweetie needs bot_token and channel in your Slack config to monitor thread replies. Without them, blocked notifications go via webhook (which is send-only) and Sweetie can't read replies.
slack:
webhook_url: "https://hooks.slack.com/services/..."
bot_token: "xoxb-..." # Required for thread monitoring
channel: "C0AF0LZEE64" # Channel ID, not name
Slack API error: "missing_scope"
Your Slack bot token is missing required scopes. Go to api.slack.com/apps → your app → OAuth & Permissions → add chat:write and channels:history under Bot Token Scopes. After adding scopes, you must reinstall the app to your workspace for them to take effect.
Slack bot not posting messages
The bot must be added to the channel. In Slack, go to the channel and type /invite @YourAppName, or click the channel name → Integrations → Add apps.
PR creation fails with 404 for outside collaborators
GitHub fine-grained PATs can only access repos owned by the token creator or their organizations. If the collaborator is an outside contributor (not an org member), the repo won't appear when creating a fine-grained token. Two fixes:
- Invite them as an org member (not just repo collaborator) so the repo appears in their fine-grained PAT settings
- Use a classic PAT with the
reposcope instead — classic tokens work on any repo the user has access to
PR creation fails with "not all refs are readable"
This happens when your git token does not have access to the repo or if GitHub hasn't finished processing a push. Sweetie retries automatically (3 attempts with backoff), but if it still fails, try running sweetie start again — the task will be re-attempted.
Claude can't write files or use tools
Claude Code in pipe mode (-p) requires tools to be explicitly allowed. Sweetie passes a default set of allowed tools (Read, Write, Edit, Bash, Glob, Grep, WebSearch, WebFetch, NotebookEdit). If you've set allowed_tools in your repo config, make sure it includes everything your Claude setup needs.
Notion Cost column errors
The Cost column in your Notion database must be of type Number. If it's set to Rich Text or another type, Sweetie will fail when writing cost data.
License
MIT License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sweetie-0.1.4.tar.gz.
File metadata
- Download URL: sweetie-0.1.4.tar.gz
- Upload date:
- Size: 36.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
47d3f4b127976b164a3258491525d5e002c5b5907db9fe6d1ad8b1c2f1d4a199
|
|
| MD5 |
2fa8362812c5bb378dd3f3d09e5692b2
|
|
| BLAKE2b-256 |
25ec0e2af1e570aa632771b89d6281ea6439d81febdba80e72577edda7a29830
|
File details
Details for the file sweetie-0.1.4-py3-none-any.whl.
File metadata
- Download URL: sweetie-0.1.4-py3-none-any.whl
- Upload date:
- Size: 35.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
50fb1c6f66aa2e3fb913eb66a4397344e2a490cf1fecfbd2ab548e9ecc980f2c
|
|
| MD5 |
926c2c64e10d155804bb2af86c8f2d87
|
|
| BLAKE2b-256 |
3e1b74631d36e00bcca301a479aa57ea52d1a4eb7b4b03eb43bfc789fd267edd
|