# ClickUp Runner Headless background service that polls ClickUp for tasks with the "Delegate to Claude" checkbox checked, routes them through a skill map based on task type + stage, runs Claude Code headless, and posts results back to ClickUp. ## Quick Start ```bash # Set required env vars (or put them in .env at repo root) export CLICKUP_API_TOKEN="pk_..." export CLICKUP_SPACE_ID="..." # Run the runner uv run python -m clickup_runner ``` ## How It Works 1. Every 720 seconds, polls all "Overall" lists in the ClickUp space 2. Finds tasks where: - "Delegate to Claude" checkbox is checked - Due date is today or earlier 3. Reads the task's Work Category and Stage fields 4. Looks up the skill route in `skill_map.py` 5. Dispatches to either: - **AutoCora handler** (for `run_cora` stage): submits a Cora job to the NAS queue *(Phase 3)* - **Claude Code handler**: runs `claude -p` with the skill file + task context as prompt 6. On success: uploads output files as ClickUp attachments, copies to NAS (best-effort), advances Stage, sets next status, posts summary comment 7. On error: sets Error checkbox, posts structured error comment (what failed, how to fix) 8. Always unchecks "Delegate to Claude" after processing ## Configuration Config is loaded from `clickup_runner.yaml` at the repo root (optional), with env var overrides. ### clickup_runner.yaml ```yaml clickup: space_id: "your_space_id" task_type_field_name: "Work Category" delegate_field_name: "Delegate to Claude" stage_field_name: "Stage" error_field_name: "Error" ai_working_status: "ai working" review_status: "review" autocora: jobs_dir: "//PennQnap1/SHARE1/AutoCora/jobs" results_dir: "//PennQnap1/SHARE1/AutoCora/results" xlsx_dir: "//PennQnap1/SHARE1/Cora72-for-macro" poll_interval_seconds: 120 nas: generated_dir: "//PennQnap1/SHARE1/generated" runner: poll_interval_seconds: 720 claude_timeout_seconds: 2700 max_turns_default: 10 ``` ### Environment Variables | Variable | Required | Description | |----------|----------|-------------| | `CLICKUP_API_TOKEN` | Yes | ClickUp API token | | `CLICKUP_SPACE_ID` | Yes | ClickUp space to poll | | `NTFY_ERROR_TOPIC` | No | ntfy.sh topic for error notifications | | `NTFY_SUCCESS_TOPIC` | No | ntfy.sh topic for success notifications | ## ClickUp Custom Fields Required These must exist in your ClickUp space: | Field | Type | Purpose | |-------|------|---------| | Delegate to Claude | Checkbox | Trigger -- checked = process this task | | Stage | Dropdown | Pipeline position (run_cora, outline, draft, etc.) | | Error | Checkbox | Flagged when processing fails | | Work Category | Dropdown | Task type (Content Creation, Press Release, etc.) | ## Skill Map The routing table lives in `skill_map.py`. Each task type has a sequence of stages, and each stage maps to either an AutoCora job or a Claude Code skill file. ### Content Creation ``` run_cora -> outline -> draft -> final ``` ### On Page Optimization ``` run_cora -> outline -> draft -> hidden div -> final ``` ### Press Release ``` draft -> final ``` ### Link Building ``` run_cora -> build -> final ``` ## Adding a New Task Type 1. Add an entry to `SKILL_MAP` in `skill_map.py` 2. Write the skill `.md` file(s) in `skills/` 3. Add the Work Category value in ClickUp 4. Add the Stage dropdown values in ClickUp ## Statuses | Status | Owner | Meaning | |--------|-------|---------| | To Do | Nobody | Not started | | In Progress | Human | Human is working on it | | Needs Input | Human | Blocked, needs info | | AI Working | Claude | Runner is processing | | Review | Human | Output ready for human review | | Client Review | Client | Sent to client | | Complete | Nobody | Done | ## Claude Code Runner (Phase 2) When a task routes to a Claude handler, the runner: 1. Sets status to "AI Working" 2. Reads the skill `.md` file from `skills/` 3. Builds a prompt with skill instructions + task context: - Task name, description, customer, target URL - ClickUp task link - Attached `.xlsx` Cora report URLs (if any) - Instructions to write output files to the working directory 4. Runs `claude -p "" --allowedTools "..." --max-turns N --permission-mode bypassPermissions --bare` 5. Collects all files Claude created in the temp working directory 6. Uploads files to ClickUp as attachments 7. Copies files to NAS at `//PennQnap1/SHARE1/generated/{customer}/` (best-effort) 8. Advances Stage, updates status, posts comment, unchecks Delegate to Claude 9. Sends ntfy.sh notification (if configured) On failure, it posts a structured error comment: ``` [ERROR] Claude processing failed -- What failed: How to fix: ``` ## Logs - Console output: INFO level - File log: `logs/clickup_runner.log` (DEBUG level) - Run history: `data/clickup_runner.db` (run_log table) ## Tests ```bash # Unit tests only (no credentials needed) uv run pytest tests/test_clickup_runner/ -m "not integration" # Full suite (needs CLICKUP_API_TOKEN) uv run pytest tests/test_clickup_runner/ # Specific test uv run pytest tests/test_clickup_runner/test_skill_map.py -v ```