Skip to content

imjohnbo/schedule-agent

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ScheduleAgent

CI License: MIT Platform Swift

A native macOS app that schedules Claude Code agents to run on a timer and lets you review their output.

ScheduleAgent screenshot

Overview

ScheduleAgent lives in your Dock and executes claude --print prompts on a schedule you define. Each run's output, tool usage, and token count are captured and stored so you can review them later. The app keeps running while your screen is locked, so agents fire on schedule even when you're away.

Why ScheduleAgent vs Claude Code Desktop's built-in scheduled tasks

Claude Code Desktop added its own scheduled tasks feature. ScheduleAgent takes a different approach and goes further in a few key areas:

  • Token and cost tracking — every run records input and output tokens separately. Sort tasks by total token usage across all runs to see what's actually costing you.
  • Tool-use audit trail — each run shows exactly which tools were called, in order, with color-coded chips: green for allowed, red for blocked. Desktop just stalls on permission prompts with no audit record.
  • Immediate blocked-tool termination — if an agent tries a tool outside its allowed set, ScheduleAgent kills the process instantly and fires a critical notification. You know exactly what was blocked and why.
  • Test run in the editor — run any prompt with live streaming output before you save the task. Desktop has no draft mode.
  • Task search and sort — filter by name or prompt text; sort by last run, run count, or tokens used.
  • Standalone — works with just the claude CLI. No Claude Code Desktop installation required.

Use cases

# Name Schedule Prompt Tools
1 Daily standup digest Daily at 8:45am, Mon–Fri Look at git log for the last 24 hours across all branches. Write a 3-bullet standup summary to STANDUP.md — what shipped, what's in progress, any blockers you can infer. Read, Write, Bash
2 Dependency drift monitor Every 24h Check package.json (or go.mod, Gemfile, etc.) for dependencies that have newer versions available. If any are more than one major version behind, append a dated warning to DEPS_ALERT.md. Read, Write, Bash
3 Inbox zero assistant Every 1h Check my Gmail inbox via MCP. For any unread threads older than 2 hours that look like newsletters or automated notifications, draft a short summary and archive them. Leave anything that looks personal or urgent untouched. MCP (Gmail)
4 Log error watcher Every 15m Tail the last 200 lines of ~/logs/app.log. If you find any ERROR or FATAL lines not seen in the previous run (tracked in ~/logs/.last_seen_error), append them with a timestamp to ~/logs/error_digest.md and update the tracker. Read, Write, Bash
5 Weekly project summary Daily at 6pm, Fridays Review this week's git commits, any TODO comments added or removed, and open issues mentioned in ISSUES.md. Write a concise weekly summary to WEEKLY.md — wins, carry-overs, and one suggested focus for next week. Read, Write, Bash, Glob

Requirements

  • macOS 14+
  • Swift 5.9+ (Xcode 15+ or Swift toolchain)
  • claude CLI installed (auto-detected at ~/.local/bin/claude, /usr/local/bin/claude, or /opt/homebrew/bin/claude)

Installation

Run from source (development):

git clone https://github.com/imjohnbo/schedule-agent.git
cd schedule-agent
swift run

Build a distributable .app bundle:

chmod +x build.sh
./build.sh
open dist/ScheduleAgent.app
# Optionally: cp -r dist/ScheduleAgent.app /Applications/

The build script compiles a release binary, wraps it in a proper .app bundle, and ad-hoc signs it (required on Apple Silicon).

Usage

The main window

The window is split into two panes:

  • Left sidebar — lists all your tasks. The header shows the total task and run count, and has sort and add (+) controls. The sidebar can be hidden with the standard macOS toggle; when hidden, the sort and add controls disappear with it.
  • Right detail pane — shows the selected task's schedule info, live output during a run, and full run history.

The toolbar at the top right shows task-specific actions when a task is selected: Run Now, Enable/Disable (/⚡̸), and Edit ().

Adding a task

  1. Click + in the sidebar header
  2. Fill in:
    • Name — a label for the task
    • Prompt — the instruction sent to Claude
    • Schedule — interval (every N minutes/hours) or daily at a specific time and days of the week
    • Tool Permissions — which tools Claude is allowed to use (see below)
    • Working directory — where claude is invoked from (browse with the picker)
  3. Click Add

Running a task

Tasks fire automatically on their schedule. To run one immediately, select it and click Run Now in the toolbar.

The detail pane shows:

  • Live output — text streamed from Claude as it runs, with a live tool list updating as each tool is called
  • Run history — every past run with its timestamp, duration, token usage, and status badge; click a row to expand the full output and tool list

Enabling and disabling tasks

A disabled task won't fire on its schedule. To toggle a task:

  • Right-click a task in the sidebar → Enable / Disable
  • Or select the task and click the / ⚡̸ button in the toolbar

Disabled tasks show an off badge in the sidebar and a Disabled pill in the detail pane header.

Searching and sorting

Use the Search tasks bar at the top of the sidebar to filter by name or prompt text.

Click the ↑↓ icon in the sidebar header to sort by:

  • Name — alphabetical
  • Last Run — most recently run first
  • Run Count — most runs first
  • Tokens Used — most tokens consumed first

Authentication

ScheduleAgent passes your credentials to the claude CLI automatically:

  • Claude Code team plan (OAuth) — if you've run claude login in your terminal, the app finds your tokens in ~/.claude/ automatically.
  • API key — add your ANTHROPIC_API_KEY in Settings (⌘,). It's stored in the macOS Keychain and injected into each subprocess.

If neither is configured, a warning banner appears in the main window.

Prompts

Simple prompts (no tools needed)

Any text-generation task works without enabling tool permissions:

Summarize the 3 most important things I should know about my current project by reading README.md and any recent CHANGELOG entries. Be brief.

Set the working directory to your project root.

Prompts that use tools

Tasks that read files, write files, or run shell commands need tool permissions configured. Example:

Look at the git log for the last 24 hours. Summarize what changed and append a dated entry to DAILY_LOG.md. If DAILY_LOG.md doesn't exist, create it.

For this prompt you'd allow Read, Write, and Bash.

Prompts with MCP servers

MCP servers configured in your ~/.claude/settings.json are available to scheduled agents — the same environment you use interactively. Reference them in your prompt naturally:

Use the Linear MCP to find all open bugs assigned to me and write a summary to BUGS.md

Add the MCP tool names (e.g. mcp__linear__list_issues) in the Additional tools field in the task editor.

Tool Permissions

Each task has one of three permission modes, configured in the task editor:

Mode Behaviour CLI flag
Ask every time Claude pauses before any tool use. Will block unattended runs. (none)
Allow specific tools Auto-approve only the tools you check; Claude still asks for anything else. --allowedTools Read,Write,…
Allow all tools Never prompts. Claude can read, write, and run shell commands freely. --dangerously-skip-permissions

Common tools

Tool What it does
Read Read files
Write Create or overwrite files
Edit Edit existing files in place
MultiEdit Multiple edits in one step
Bash Run shell commands
Glob Find files by pattern
Grep Search file contents
WebFetch Fetch a URL
WebSearch Search the web
TodoWrite Manage task lists

For MCP tools, type their names in the Additional tools field (comma-separated), e.g. mcp__github__create_issue, mcp__linear__create_issue.

Hover over any tool checkbox in the editor for a description.

Tool tracking

Every run records which tools were used and whether each was allowed or blocked. After a run completes, expand the row in the run history to see color-coded chips:

  • Green — tool ran successfully
  • Red — tool was denied

If a tool is blocked mid-run, the agent is terminated immediately and a critical notification fires: "⛔ Task blocked — Agent tried to use Bash, which isn't permitted. Run ended early."

Token usage

Each completed run displays token usage (e.g. 12.4k tok) in the run history row alongside the duration. Input and output tokens are tracked separately and summed. Tasks can be sorted by total tokens used across all runs.

Scheduling

Two schedule types are supported:

Interval — fire every N minutes or hours:

  • 5 min, 15 min, 30 min, 1h, 2h, 4h, 8h, 24h

Daily — fire at a specific wall-clock time on selected days of the week:

  • Hour and minute picker + day-of-week toggles (Sun–Sat)

The scheduler checks for due tasks every 60 seconds, so daily tasks have at most a 1-minute drift.

The app continues scheduling while your screen is locked. For tasks to run across reboots, enable Launch at login in Settings (⌘,).

Data storage

All data is stored in ~/Library/Application Support/ScheduleAgent/:

File Contents
tasks.json Task definitions (name, prompt, schedule, permissions)
runs.json Run history (up to 2,000 most recent runs)

The Anthropic API key (if set) is stored in the macOS Keychain under anthropic-api-key.

Settings

Open Settings (⌘,) to:

  • API Key — store your ANTHROPIC_API_KEY in the Keychain (status dot confirms it's saved)
  • Launch at login — registers the app as a login item via SMAppService so it starts automatically after reboot

Architecture

Built with Swift/SwiftUI and Swift Package Manager (no Xcode project required).

Sources/ScheduleAgent/
├── App/
│   ├── ScheduleAgentApp.swift     # @main — WindowGroup + Feed + Settings scenes
│   └── AppDelegate.swift          # Activation policy, notification permission
├── Models/
│   ├── ScheduledTask.swift        # Task model, ScheduleType, PermissionMode
│   └── RunRecord.swift            # Run history, ToolUseRecord, RunStatus, token fields
├── Managers/
│   ├── TaskStore.swift            # JSON persistence (tasks + runs)
│   ├── ScheduleEngine.swift       # 60-second Timer, fires due tasks
│   ├── TaskRunner.swift           # Spawns claude --print, parses stream-json
│   └── KeychainHelper.swift       # Keychain CRUD for API key
└── Views/
    ├── TaskListView.swift          # NavigationSplitView, sidebar with header
    ├── TaskDetailView.swift        # Meta row, live output, tool chips, run history
    ├── TaskEditSheet.swift         # Create/edit form, NSOpenPanel dir picker
    ├── SettingsView.swift          # API key, launch at login
    └── RunRecordRow.swift          # Expandable run row with tool list + token count

How agent invocation works

TaskRunner spawns the claude binary as a subprocess:

claude --print "<prompt>" --no-session-persistence \
       --output-format stream-json --verbose \
       [--allowedTools Tool1,Tool2 | --dangerously-skip-permissions]

The subprocess always has HOME set explicitly so the CLI finds OAuth tokens in ~/.claude/ regardless of how the app was launched. If an API key is stored in the Keychain and ANTHROPIC_API_KEY isn't already in the environment, it is injected before launch.

The stream-json output is parsed line-by-line on a background serial queue:

  • assistant message events → extract text content for the live output display
  • tool_use content blocks → record which tool was requested
  • tool result events → determine if the tool succeeded or was blocked
  • result event → final response text + usage field for input/output token counts

If a permission block is detected (tool result with is_error: true and permission-related text, or an unresolved tool use at process exit), the process is terminated immediately and the run is marked blocked.

About

Scheduled background agents for Claude Code. Run prompts on a cron and manage tasks from a native macOS app.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors