- Add skills/save-doc/ skill - Add sessions/2121928.json - Delete cognitive-memory skill, memory-saver agent, save-memories command - Update CLAUDE.md, pr-reviewer, issue-worker agents - Update mcp-manager, create-scheduled-task, paper-dynasty skills - Update plugins (blocklist, installed, known_marketplaces, marketplaces) - Remove old session files Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
7.1 KiB
| name | description |
|---|---|
| create-scheduled-task | Create, manage, or debug headless Claude scheduled tasks that run on systemd timers. USE WHEN user says "create a scheduled task", "add a cron job for Claude", "schedule a task", "new scheduled task", "manage scheduled tasks", or wants Claude to do something automatically on a timer. |
Create Scheduled Task
When to Activate This Skill
- "Create a scheduled task for X"
- "Schedule Claude to do X every day"
- "Add a new automated task"
- "Debug a scheduled task"
- "List scheduled tasks"
- "Manage scheduled tasks"
System Overview
Headless Claude Code sessions triggered by systemd timers. Each task has its own prompt, MCP config, settings, and timer.
~/.config/claude-scheduled/
├── runner.sh # Universal task runner (DO NOT MODIFY)
├── disabled # Touch this file to globally disable all tasks
├── logs -> ~/.local/share/... # Symlink to log directory
└── tasks/
└── <task-name>/
├── prompt.md # What Claude should do
├── settings.json # Model, budget, tools, working dir
└── mcp.json # MCP servers this task needs (optional)
~/.config/systemd/user/
├── claude-scheduled@.service # Template unit (DO NOT MODIFY)
└── claude-scheduled@<task-name>.timer # One per task
Creating a New Task
Step 1: Create the task directory
mkdir -p ~/.config/claude-scheduled/tasks/<task-name>
mkdir -p ~/.local/share/claude-scheduled/logs/<task-name>
Step 2: Write the prompt (prompt.md)
Write a clear, structured prompt that tells Claude exactly what to do. Include:
- Specific instructions (repos to check, files to read, etc.)
- Desired output format (structured text or JSON)
Guidelines:
- Be explicit — headless Claude has no user to ask for clarification
- Specify output format so results are parseable in logs
- Keep prompts focused on a single concern
Step 3: Write settings (settings.json)
{
"model": "sonnet",
"effort": "medium",
"max_budget_usd": 0.75,
"allowed_tools": "<space-separated tool list>",
"working_dir": "/mnt/NV2/Development/claude-home",
"timeout_seconds": 300
}
Settings reference:
| Field | Default | Description |
|---|---|---|
model |
sonnet |
Model alias or full ID. Use sonnet for cost efficiency. |
effort |
medium |
low, medium, or high. Controls reasoning depth. |
max_budget_usd |
0.25 |
Per-session cost ceiling. Typical triage run: ~$0.20. |
allowed_tools |
Read(*) Glob(*) Grep(*) |
Space-separated tool allowlist. Principle of least privilege. |
working_dir |
/mnt/NV2/Development/claude-home |
cd here before running. Loads that project's CLAUDE.md. |
timeout_seconds |
300 |
Hard timeout. 300s (5 min) is usually sufficient. |
Common tool allowlists by task type:
Code analysis (read-only):
Read(*) Glob(*) Grep(*)
Step 4: Write MCP config (mcp.json) — if needed
Only include MCP servers the task actually needs. Use --strict-mcp-config (runner does this automatically when mcp.json exists).
Available MCP server configs to copy from:
Gitea:
{
"gitea-mcp": {
"type": "stdio",
"command": "/home/cal/.local/bin/gitea-mcp",
"args": ["-t", "stdio", "-host", "https://git.manticorum.com"],
"env": {
"GITEA_ACCESS_TOKEN": "<token from ~/.claude.json>"
}
}
}
n8n:
{
"n8n-mcp": {
"command": "npx",
"type": "stdio",
"args": ["n8n-mcp"],
"env": {
"MCP_MODE": "stdio",
"N8N_API_URL": "http://10.10.0.210:5678",
"N8N_API_KEY": "<token from ~/.claude/.mcp-full.json>"
}
}
}
Wrap in {"mcpServers": { ... }} structure.
Step 5: Create the systemd timer
Create ~/.config/systemd/user/claude-scheduled@<task-name>.timer:
[Unit]
Description=Claude Scheduled Task Timer: <task-name>
[Timer]
OnCalendar=<schedule>
Persistent=true
[Install]
WantedBy=timers.target
Common OnCalendar expressions:
| Schedule | Expression |
|---|---|
| Daily at 9am | *-*-* 09:00:00 |
| Every 6 hours | *-*-* 00/6:00:00 |
| Weekdays at 8am | Mon..Fri *-*-* 08:00:00 |
| Weekly Sunday 3am | Sun *-*-* 03:00:00 |
| Monthly 1st at midnight | *-*-01 00:00:00 |
Persistent=true means if the machine was off during a scheduled run, it catches up on next boot.
Step 6: Enable the timer
systemctl --user daemon-reload
systemctl --user enable --now claude-scheduled@<task-name>.timer
Managing Tasks
List all scheduled tasks
systemctl --user list-timers 'claude-scheduled*'
Manual test run
~/.config/claude-scheduled/runner.sh <task-name>
Check logs
# Latest log
ls -t ~/.local/share/claude-scheduled/logs/<task-name>/ | head -1 | xargs -I{} cat ~/.local/share/claude-scheduled/logs/<task-name>/{}
# Via journalctl (if triggered by systemd)
journalctl --user -u claude-scheduled@<task-name>.service --since today
Disable a single task
systemctl --user disable --now claude-scheduled@<task-name>.timer
Disable ALL tasks (kill switch)
touch ~/.config/claude-scheduled/disabled
# To re-enable:
rm ~/.config/claude-scheduled/disabled
Check task run history
ls -lt ~/.local/share/claude-scheduled/logs/<task-name>/
Existing Tasks
| Task | Schedule | Budget | Description |
|---|---|---|---|
backlog-triage |
Daily 09:00 | $0.75 | Scan Gitea issues across repos, prioritize, suggest focus |
sync-config |
Daily 02:00 | $0.25 | Sync ~/.claude and ~/dotfiles to Gitea (bash pre-check, Claude only on changes) |
How the Runner Works
runner.sh is the universal executor. For each task it:
- Reads
settings.jsonfor model, budget, tools, working dir - Reads
prompt.mdas the Claude prompt - Invokes
claude -pwith--strict-mcp-config,--allowedTools,--no-session-persistence,--output-format json - Unsets
CLAUDECODEenv var to allow nested sessions - Logs full output to
~/.local/share/claude-scheduled/logs/<task>/ - Rotates logs (keeps last 30 per task)
The runner does NOT need modification to add new tasks — just add files under tasks/ and a timer.
Key Constraints
- Read-only by default: Tasks should use
--allowedToolsto restrict to only what they need. No Bash, no Edit unless explicitly required. - Cost ceiling:
max_budget_usdis a hard limit per session. Typical Sonnet run with MCP tools: $0.15–0.30. - OAuth auth: Uses Cal's Claude Max subscription via OAuth. No API key needed.
- Nested sessions: The runner unsets
CLAUDECODEso it works from within a Claude session or from systemd. - Log retention: 30 logs per task, oldest auto-deleted.
Reference Files
- Runner:
~/.config/claude-scheduled/runner.sh - Template service:
~/.config/systemd/user/claude-scheduled@.service - Example task:
~/.config/claude-scheduled/tasks/backlog-triage/ - Gitea issue: claude-home#2