Skip to content

Refaltor77/Cronlab

Repository files navigation

Cronlab

A scheduled task manager for macOS.
Built to automate Claude Code sessions via launchd.


Why Cronlab?

cron on macOS cannot access the Keychain. As a result, commands like claude -p "..." fail with Not logged in.

Cronlab uses launchd (the native macOS scheduler) which runs in the user context with full access to the Keychain, Homebrew PATH, and shell environment.

crontab  -->  no Keychain access  -->  "Not logged in"
launchd  -->  Keychain access     -->  Claude runs fine

Features

Feature Description
View List all tasks with active/inactive status, cron schedule, command
Create Form with individual cron fields, quick presets, live preview
Edit Inline editing of each task (schedule, command, label)
Enable/Disable Toggle on/off without deleting the task
Delete Double confirmation before removal
History Stdout/stderr logs from each execution

Screenshots

Task list

Main view with tasks showing active/inactive states, cron schedules, and commands.

Task List

Create form

Live cron expression preview, 9 quick presets, command and label fields.

Create Form

Inline edit mode

Click the pencil icon to edit the schedule, command, and label directly in the card.

Inline Edit

Execution history

View stdout/stderr logs from each task execution.

History


Installation

Prerequisites

  • macOS (uses launchd)
  • Node.js 20+
  • Claude Code installed (optional, for automated sessions)

Setup

# Clone the repo
git clone https://github.com/Refaltor77/Cronlab.git
cd Cronlab

# Install dependencies
npm install

# Start the server
npm run dev

Open http://localhost:3000.


Architecture

src/
  app/
    page.tsx                         # Main interface (React client component)
    layout.tsx                       # Layout with Geist font
    globals.css                      # Design system (white theme)
    api/
      crontab/
        route.ts                     # LaunchAgents CRUD (GET/POST/PUT/PATCH/DELETE)
        history/
          route.ts                   # Execution logs reader

~/Library/LaunchAgents/
  com.cronlab.task-*.plist           # Plist files generated by Cronlab

~/.cronlab/logs/
  task-*.stdout.log                  # Standard output for each task
  task-*.stderr.log                  # Error output for each task
  task-*.meta.json                   # Metadata (label)

How it works

  1. Create — Cronlab generates a .plist file in ~/Library/LaunchAgents/
  2. Loadlaunchctl load registers the plist with the macOS scheduler
  3. Executelaunchd runs the command via /bin/zsh -l -c (login shell = Keychain access)
  4. Log — stdout/stderr are redirected to ~/.cronlab/logs/
  5. Disablelaunchctl unload removes the job without deleting the file

Generated plist example

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>com.cronlab.task-1234567890</string>
    <key>ProgramArguments</key>
    <array>
      <string>/bin/zsh</string>
      <string>-l</string>
      <string>-c</string>
      <string>claude -p "Review open PRs" --dangerously-skip-permissions</string>
    </array>
    <key>StartCalendarInterval</key>
    <dict>
      <key>Hour</key>
      <integer>9</integer>
      <key>Minute</key>
      <integer>0</integer>
    </dict>
    <key>StandardOutPath</key>
    <string>~/.cronlab/logs/task-1234567890.stdout.log</string>
    <key>StandardErrorPath</key>
    <string>~/.cronlab/logs/task-1234567890.stderr.log</string>
  </dict>
</plist>

Usage examples

Daily code review every morning

Schedule :  0 9 * * 1-5
Command  :  claude -p "Review open PRs on this repo and summarize the changes" --dangerously-skip-permissions
Label    :  Daily Code Review

Health check every 15 minutes

Schedule :  */15 * * * *
Command  :  claude -p "Run the tests and verify the build passes" --dangerously-skip-permissions
Label    :  Health Check CI

Weekly report on Sundays

Schedule :  0 0 * * 0
Command  :  claude -p "Generate a summary of this week's commits" --dangerously-skip-permissions
Label    :  Weekly Report

Daily backup at midnight

Schedule :  0 0 * * *
Command  :  tar -czf ~/backups/project-$(date +%Y%m%d).tar.gz ~/my-project
Label    :  Project Backup

Log cleanup every Monday

Schedule :  0 6 * * 1
Command  :  find ~/.cronlab/logs -name "*.log" -mtime +30 -delete
Label    :  Log Cleanup

Cron expressions

Cronlab uses the standard 5-field cron syntax:

*    *    *    *    *
|    |    |    |    |
|    |    |    |    +-- Day of week (0-6, 0 = Sunday)
|    |    |    +------- Month (1-12)
|    |    +------------ Day of month (1-31)
|    +----------------- Hour (0-23)
+---------------------- Minute (0-59)

Available presets

Preset Expression Use case
Every minute * * * * * Testing and debug
Every 5 min */5 * * * * Frequent monitoring
Every 15 min */15 * * * * Health checks
Every hour 0 * * * * Hourly tasks
Every day 9 AM 0 9 * * * Daily standup
Every day midnight 0 0 * * * Nightly maintenance
Mon-Fri 9 AM 0 9 * * 1-5 Weekdays only
Every Sunday 0 0 * * 0 Weekly reports
1st of month 0 0 1 * * Monthly reports

API

All routes are under /api/crontab.

Method Endpoint Description
GET /api/crontab List all tasks
POST /api/crontab Create a new task
PUT /api/crontab Update an existing task
PATCH /api/crontab Enable/disable a task
DELETE /api/crontab Delete a task
GET /api/crontab/history Execution history

curl examples

# List tasks
curl http://localhost:3000/api/crontab

# Create a task
curl -X POST http://localhost:3000/api/crontab \
  -H "Content-Type: application/json" \
  -d '{
    "schedule": "0 9 * * 1-5",
    "command": "claude -p \"Review open PRs\" --dangerously-skip-permissions",
    "comment": "Daily Code Review"
  }'

# Disable a task
curl -X PATCH http://localhost:3000/api/crontab \
  -H "Content-Type: application/json" \
  -d '{"id": "task-123", "enabled": false}'

# Update a task
curl -X PUT http://localhost:3000/api/crontab \
  -H "Content-Type: application/json" \
  -d '{
    "id": "task-123",
    "schedule": "0 10 * * 1-5",
    "command": "claude -p \"New prompt\" --dangerously-skip-permissions",
    "comment": "Updated label"
  }'

# Delete a task
curl -X DELETE http://localhost:3000/api/crontab \
  -H "Content-Type: application/json" \
  -d '{"id": "task-123"}'

Tech stack

Component Technology
Framework Next.js 16 (App Router)
UI React 19, Tailwind CSS 4
Font Geist Sans + Geist Mono
Scheduler macOS launchd
Plist simple-plist
Runtime Node.js

Debugging

Check active LaunchAgents

launchctl list | grep cronlab

View job logs

cat ~/.cronlab/logs/task-XXXX.stdout.log
cat ~/.cronlab/logs/task-XXXX.stderr.log

List plist files

ls ~/Library/LaunchAgents/com.cronlab.*

Force reload a job

launchctl unload ~/Library/LaunchAgents/com.cronlab.task-XXXX.plist
launchctl load ~/Library/LaunchAgents/com.cronlab.task-XXXX.plist

License

MIT

About

Gestionnaire de taches planifiees macOS — automatise des sessions Claude Code via launchd

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors