diff --git a/plugins/blocklist.json b/plugins/blocklist.json index 7b5145e..16d9535 100644 --- a/plugins/blocklist.json +++ b/plugins/blocklist.json @@ -1,5 +1,5 @@ { - "fetchedAt": "2026-02-28T18:10:38.336Z", + "fetchedAt": "2026-03-01T05:51:34.878Z", "plugins": [ { "plugin": "code-review@claude-plugins-official", diff --git a/plugins/known_marketplaces.json b/plugins/known_marketplaces.json index eaca289..f155cbf 100644 --- a/plugins/known_marketplaces.json +++ b/plugins/known_marketplaces.json @@ -13,6 +13,6 @@ "repo": "anthropics/claude-code" }, "installLocation": "/home/cal/.claude/plugins/marketplaces/claude-code-plugins", - "lastUpdated": "2026-02-28T18:11:46.614Z" + "lastUpdated": "2026-03-01T06:14:11.879Z" } } \ No newline at end of file diff --git a/skills/create-scheduled-task/SKILL.md b/skills/create-scheduled-task/SKILL.md new file mode 100644 index 0000000..dfe3906 --- /dev/null +++ b/skills/create-scheduled-task/SKILL.md @@ -0,0 +1,256 @@ +--- +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/ + └── / + ├── 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@.timer # One per task +``` + +## Creating a New Task + +### Step 1: Create the task directory + +```bash +mkdir -p ~/.config/claude-scheduled/tasks/ +mkdir -p ~/.local/share/claude-scheduled/logs/ +``` + +### 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) +- Any cognitive-memory operations (recall context, store results) + +**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": "", + "graph": "default", + "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. | +| `graph` | `default` | Cognitive-memory graph for storing results. | +| `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:** + +Read-only triage (Gitea + memory): +``` +mcp__gitea-mcp__list_repo_issues mcp__gitea-mcp__get_issue_by_index mcp__gitea-mcp__list_repo_labels mcp__gitea-mcp__list_repo_pull_requests mcp__cognitive-memory__memory_recall mcp__cognitive-memory__memory_search mcp__cognitive-memory__memory_store mcp__cognitive-memory__memory_episode +``` + +Code analysis (read-only): +``` +Read(*) Glob(*) Grep(*) +``` + +Memory maintenance: +``` +mcp__cognitive-memory__memory_recall mcp__cognitive-memory__memory_search mcp__cognitive-memory__memory_store mcp__cognitive-memory__memory_relate mcp__cognitive-memory__memory_reflect mcp__cognitive-memory__memory_episode +``` + +### 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": "" + } + } +} +``` + +Cognitive Memory: +```json +{ + "cognitive-memory": { + "command": "python3", + "type": "stdio", + "args": ["/mnt/NV2/Development/cognitive-memory/mcp_server.py"], + "env": {} + } +} +``` + +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": "" + } + } +} +``` + +Wrap in `{"mcpServers": { ... }}` structure. + +### Step 5: Create the systemd timer + +Create `~/.config/systemd/user/claude-scheduled@.timer`: + +```ini +[Unit] +Description=Claude Scheduled Task Timer: + +[Timer] +OnCalendar= +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@.timer +``` + +## Managing Tasks + +### List all scheduled tasks +```bash +systemctl --user list-timers 'claude-scheduled*' +``` + +### Manual test run +```bash +~/.config/claude-scheduled/runner.sh +``` + +### Check logs +```bash +# Latest log +ls -t ~/.local/share/claude-scheduled/logs// | head -1 | xargs -I{} cat ~/.local/share/claude-scheduled/logs//{} + +# Via journalctl (if triggered by systemd) +journalctl --user -u claude-scheduled@.service --since today +``` + +### Disable a single task +```bash +systemctl --user disable --now claude-scheduled@.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// +``` + +## Existing Tasks + +| Task | Schedule | Budget | Description | +|------|----------|--------|-------------| +| `backlog-triage` | Daily 09:00 | $0.75 | Scan Gitea issues across repos, prioritize, suggest focus | + +## 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//` +6. Stores a summary to cognitive-memory as a workflow + episode +7. 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)