CheddahBot/clickup_runner
PeninsulaInd b19e221b8f Add Claude Code runner -- Phase 2: claude -p subprocess + ClickUp integration
Implements _dispatch_claude: reads skill files, builds prompts with task
context, runs claude -p as subprocess, uploads output files to ClickUp,
copies to NAS, advances stage/status, and posts structured error comments
on failure. Includes ntfy.sh notifications and generous max_turns defaults.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 09:34:54 -05:00
..
README.md Add Claude Code runner -- Phase 2: claude -p subprocess + ClickUp integration 2026-03-30 09:34:54 -05:00
__init__.py Add clickup_runner module -- Phase 1: skeleton + ClickUp polling 2026-03-30 09:14:22 -05:00
__main__.py Add Claude Code runner -- Phase 2: claude -p subprocess + ClickUp integration 2026-03-30 09:34:54 -05:00
claude_runner.py Add Claude Code runner -- Phase 2: claude -p subprocess + ClickUp integration 2026-03-30 09:34:54 -05:00
clickup_client.py Add clickup_runner module -- Phase 1: skeleton + ClickUp polling 2026-03-30 09:14:22 -05:00
config.py Add clickup_runner module -- Phase 1: skeleton + ClickUp polling 2026-03-30 09:14:22 -05:00
skill_map.py Add Claude Code runner -- Phase 2: claude -p subprocess + ClickUp integration 2026-03-30 09:34:54 -05:00
state.py Add clickup_runner module -- Phase 1: skeleton + ClickUp polling 2026-03-30 09:14:22 -05:00

README.md

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

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

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
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 "<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>

Logs

  • Console output: INFO level
  • File log: logs/clickup_runner.log (DEBUG level)
  • Run history: data/clickup_runner.db (run_log table)

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
uv run pytest tests/test_clickup_runner/test_skill_map.py -v