Skip to main content

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

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

  1. Create a sweetie.yaml config 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.

  1. Fill in your Notion, Slack, and Github tokens (see Integration setup below).

  2. 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.

  1. Go to notion.so/my-integrations and create a new integration.
  2. Grant it Read content, Update content, and Insert content capabilities.
  3. Copy the Internal Integration Secret — this is your api_key.
  4. Open your task database in Notion. Make sure it has all required columns.
  5. To get your datasource ID, click the settings for that table view. In the menu, under Data source settings, select Manage data sources, find your data source, click ... menu, and Copy 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:

  1. Go to api.slack.com/apps and create a new app (or use the one from your webhook).
  2. Under OAuth & Permissions, add these Bot Token Scopes:
    • chat:write — to post messages and get thread timestamps
    • channels:history — to read thread replies
  3. Install (or reinstall) the app to your workspace and copy the Bot User OAuth Token (xoxb-...).
  4. Add the bot to your channel: Go to the channel in Slack and type /invite @YourAppName, or click the channel name → IntegrationsAdd apps. The bot must be in the channel to post messages and read replies.
  5. 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.

  1. Go to github.com/settings/tokens and create a Fine-grained personal access token.
  2. Grant it access to the repos Sweetie will work on.
  3. 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

  1. You add a task to Notion with status Queued
  2. Sweetie picks it up, sets status to In Progress, creates a sweetie-agent/<task-name> branch
  3. Claude Code implements the task
  4. If blocked → status Blocked, Slack notification with the question, moves to next task
  5. If done → runs tests (if configured), creates PR, status In Review
  6. You review the PR and merge

Unblocking a task

Three ways to unblock a blocked task:

  • Slack thread reply (if bot_token + channel configured) — 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:

  1. Leave review comments on the PR (inline or general)
  2. Optionally push your own commits to the branch
  3. 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 --fresh to 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 → IntegrationsAdd 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 repo scope 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sweetie-0.1.9.tar.gz (39.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sweetie-0.1.9-py3-none-any.whl (37.8 kB view details)

Uploaded Python 3

File details

Details for the file sweetie-0.1.9.tar.gz.

File metadata

  • Download URL: sweetie-0.1.9.tar.gz
  • Upload date:
  • Size: 39.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.3

File hashes

Hashes for sweetie-0.1.9.tar.gz
Algorithm Hash digest
SHA256 5803be7c7c571990f8557a278fe4aedba25a46e5a6c9f7803fdafeb81310c362
MD5 f1d9a4e924264e413dd300a0d806a8d3
BLAKE2b-256 b4ba931bf946aa93b6eed211535f0dfb8674ad75c5e786263240fe286de15d56

See more details on using hashes here.

File details

Details for the file sweetie-0.1.9-py3-none-any.whl.

File metadata

  • Download URL: sweetie-0.1.9-py3-none-any.whl
  • Upload date:
  • Size: 37.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.3

File hashes

Hashes for sweetie-0.1.9-py3-none-any.whl
Algorithm Hash digest
SHA256 4edb350b67a8303f9ee1fe2598d9cf2816f3fd260a996faece7085e452897425
MD5 43e1a4fc6252fe7de6e554f04068e963
BLAKE2b-256 1052fd87a500b9b54abb5ea3226a91dd2525c8a046977433ec0a95ef967c94ff

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page