249 lines
7.6 KiB
Markdown
249 lines
7.6 KiB
Markdown
# 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. Checks for completed AutoCora jobs (result polling)
|
|
3. Finds tasks where:
|
|
- "Delegate to Claude" checkbox is checked
|
|
- Due date is today or earlier
|
|
4. Reads the task's Work Category and Stage fields
|
|
5. Looks up the skill route in `skill_map.py`
|
|
6. Dispatches to either:
|
|
- **AutoCora handler** (for `run_cora` stage): submits a Cora job to the NAS queue
|
|
- **Claude Code handler**: runs `claude -p` with the skill file + task context as prompt
|
|
7. On success: uploads output files as ClickUp attachments, copies to NAS (best-effort),
|
|
advances Stage, sets next status, posts summary comment
|
|
8. On error: sets Error checkbox, posts structured error comment (what failed, how to fix)
|
|
9. 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.) |
|
|
| Keyword | Text | SEO keyword for Cora analysis (required for run_cora stage) |
|
|
| IMSURL | URL | Target money-site URL (used in prompts and Cora jobs) |
|
|
| Client | Dropdown | Client name (used for NAS file organization and prompts) |
|
|
|
|
## 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 Handler
|
|
|
|
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 "<prompt>" --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: <error details>
|
|
|
|
How to fix: <instructions>
|
|
```
|
|
|
|
## AutoCora Handler
|
|
|
|
AutoCora jobs are asynchronous -- submission and result polling happen on
|
|
separate poll cycles.
|
|
|
|
### Submission (when a `run_cora` task is found)
|
|
|
|
1. Reads the `Keyword` and `IMSURL` custom fields from the task
|
|
2. Sets status to "AI Working"
|
|
3. Writes a job JSON file to `//PennQnap1/SHARE1/AutoCora/jobs/`:
|
|
```json
|
|
{
|
|
"keyword": "CNC Machining",
|
|
"url": "https://acme.com/cnc-machining",
|
|
"task_ids": ["task_id"]
|
|
}
|
|
```
|
|
4. Stores job metadata in the state DB for result polling
|
|
5. Posts comment "Cora job submitted for keyword: ..."
|
|
6. Unchecks "Delegate to Claude"
|
|
|
|
### Result Polling (every poll cycle)
|
|
|
|
At the start of each cycle, the runner scans the results directory:
|
|
|
|
1. Looks for `.result` files in `//PennQnap1/SHARE1/AutoCora/results/`
|
|
2. Matches results to pending jobs via the state DB
|
|
3. On **success**:
|
|
- Advances Stage to the next stage (e.g. run_cora -> outline)
|
|
- Sets status to "review"
|
|
- Posts comment with keyword and .xlsx location
|
|
- Clears Error checkbox
|
|
- **Does NOT re-check Delegate to Claude** (human reviews first)
|
|
4. On **failure**:
|
|
- Sets Error checkbox
|
|
- Posts structured error comment with failure reason
|
|
5. Archives processed `.result` files to `results/processed/`
|
|
|
|
### .xlsx Skip
|
|
|
|
If a task at `run_cora` stage already has an `.xlsx` attachment, the runner
|
|
skips Cora submission and advances directly to the next stage.
|
|
|
|
## Logs
|
|
|
|
- Console output: INFO level
|
|
- File log: `logs/clickup_runner.log` (DEBUG level)
|
|
- Run history: `data/clickup_runner.db` (run_log table + kv_store for AutoCora jobs)
|
|
|
|
## Troubleshooting
|
|
|
|
### Task not being picked up
|
|
- Check that "Delegate to Claude" is checked
|
|
- Check that the due date is today or earlier
|
|
- Check that Work Category and Stage are set and valid
|
|
- Check that the task is in an "Overall" list
|
|
|
|
### Claude errors
|
|
- Check `logs/clickup_runner.log` for the full error
|
|
- Verify the skill `.md` file exists in `skills/`
|
|
- Verify `claude` CLI is on PATH
|
|
- Check the Error comment on the ClickUp task for fix instructions
|
|
|
|
### AutoCora not producing results
|
|
- Verify the NAS is mounted and accessible
|
|
- Check that job files appear in `//PennQnap1/SHARE1/AutoCora/jobs/`
|
|
- Check the AutoCora worker logs on the NAS
|
|
- Look for `.result` files in `//PennQnap1/SHARE1/AutoCora/results/`
|
|
|
|
### NAS copy failures
|
|
- NAS copy is best-effort and won't block the pipeline
|
|
- Check that `//PennQnap1/SHARE1/generated/` is accessible
|
|
- Check `logs/clickup_runner.log` for copy warnings
|
|
|
|
## 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 file
|
|
uv run pytest tests/test_clickup_runner/test_autocora.py -v
|
|
```
|