CheddahBot/clickup_runner/README.md

10 KiB

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

# 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

Quick Reference: Running a Task

To delegate a task to the runner, it needs all three fields set or it will be skipped:

  1. Work Category -- which task type (dropdown)
  2. Stage -- which pipeline step to run (dropdown)
  3. Delegate to Claude -- checked (checkbox)

Plus the task must have a due date <= today and be in an "Overall" list.

Initial Stage by Task Type

Task Type First Stage What it does Full pipeline
Content Creation run_cora Submits Cora analysis run_cora -> outline -> draft
On Page Optimization run_cora Submits Cora analysis run_cora -> outline -> draft -> hidden div
Press Release draft Writes full PR + schemas draft (single stage)
Link Building run_cora Submits Cora analysis run_cora -> build

After Each Stage

The runner unchecks "Delegate to Claude" and sets status to Review so you can check the output. To continue to the next stage, review the output, then re-check "Delegate to Claude". The Stage field is advanced automatically.

Required Custom Fields by Task Type

Task Type Required Fields
Content Creation Keyword, IMSURL (for run_cora)
On Page Optimization Keyword, IMSURL (for run_cora)
Press Release Keyword, IMSURL
Link Building Keyword, IMSURL, CLIFlags (--tier1-count N)

If Something Goes Wrong

The runner sets the Error checkbox, posts a comment explaining what failed and how to fix it, unchecks "Delegate to Claude", and sets status to Review. Fix the issue, then re-check "Delegate to Claude" to retry.


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

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/Cora-For-Human"
  poll_interval_seconds: 120

blm:
  blm_dir: "E:/dev/Big-Link-Man"
  timeout_seconds: 1800

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
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/:
    {
      "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.

The Link Building build stage runs Big-Link-Man directly -- no Claude needed.

  1. Sets status to "AI Working"
  2. Looks for a Cora .xlsx in //PennQnap1/SHARE1/Cora-For-Human/ matching the task keyword
  3. Runs ingest-cora via BLM's own venv Python (E:/dev/Big-Link-Man/.venv/Scripts/python.exe)
  4. Runs generate-batch with --continue-on-error
  5. On success: advances Stage to final, posts summary comment, unchecks Delegate to Claude
  6. On failure: sets Error checkbox, posts structured error comment

BLM credentials are injected from BLM_USERNAME and BLM_PASSWORD env vars.

Cora .xlsx Files

All Cora reports live in one folder: //PennQnap1/SHARE1/Cora-For-Human/

  • AutoCora drops finished reports here
  • All handlers (BLM, content, OPT) read from this folder by keyword match
  • Files are not moved after processing -- they stay in place so multiple task types can use the same report
  • A separate cleanup script can move old files to processed/ when no open tasks match the keyword

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

# 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