- 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>
232 lines
7.1 KiB
Markdown
232 lines
7.1 KiB
Markdown
---
|
||
name: create-scheduled-task
|
||
description: 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
|
||
|
||
```bash
|
||
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`)
|
||
|
||
```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:
|
||
```json
|
||
{
|
||
"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:
|
||
```json
|
||
{
|
||
"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`:
|
||
|
||
```ini
|
||
[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
|
||
|
||
```bash
|
||
systemctl --user daemon-reload
|
||
systemctl --user enable --now claude-scheduled@<task-name>.timer
|
||
```
|
||
|
||
## Managing Tasks
|
||
|
||
### List all scheduled tasks
|
||
```bash
|
||
systemctl --user list-timers 'claude-scheduled*'
|
||
```
|
||
|
||
### Manual test run
|
||
```bash
|
||
~/.config/claude-scheduled/runner.sh <task-name>
|
||
```
|
||
|
||
### Check logs
|
||
```bash
|
||
# 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
|
||
```bash
|
||
systemctl --user disable --now claude-scheduled@<task-name>.timer
|
||
```
|
||
|
||
### Disable ALL tasks (kill switch)
|
||
```bash
|
||
touch ~/.config/claude-scheduled/disabled
|
||
# To re-enable:
|
||
rm ~/.config/claude-scheduled/disabled
|
||
```
|
||
|
||
### Check task run history
|
||
```bash
|
||
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:
|
||
|
||
1. Reads `settings.json` for model, budget, tools, working dir
|
||
2. Reads `prompt.md` as the Claude prompt
|
||
3. Invokes `claude -p` with `--strict-mcp-config`, `--allowedTools`, `--no-session-persistence`, `--output-format json`
|
||
4. Unsets `CLAUDECODE` env var to allow nested sessions
|
||
5. Logs full output to `~/.local/share/claude-scheduled/logs/<task>/`
|
||
6. 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 `--allowedTools` to restrict to only what they need. No Bash, no Edit unless explicitly required.
|
||
- **Cost ceiling**: `max_budget_usd` is 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 `CLAUDECODE` so 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](https://git.manticorum.com/cal/claude-home/issues/2)
|