Previously, results from prior runs (or the old CheddahBot system) would be processed without context, causing "unknown" keyword in comments and cascading errors. Now they are archived silently. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| README.md | ||
| __init__.py | ||
| __main__.py | ||
| autocora.py | ||
| claude_runner.py | ||
| clickup_client.py | ||
| config.py | ||
| skill_map.py | ||
| state.py | ||
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
Quick Reference: Running a Task
To delegate a task to the runner, it needs all three fields set or it will be skipped:
- Work Category -- which task type (dropdown)
- Stage -- which pipeline step to run (dropdown)
- 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
- Every 720 seconds, polls all "Overall" lists in the ClickUp space
- Checks for completed AutoCora jobs (result polling)
- Finds tasks where:
- "Delegate to Claude" checkbox is checked
- Due date is today or earlier
- Reads the task's Work Category and Stage fields
- Looks up the skill route in
skill_map.py - Dispatches to either:
- AutoCora handler (for
run_corastage): submits a Cora job to the NAS queue - Claude Code handler: runs
claude -pwith the skill file + task context as prompt
- AutoCora handler (for
- On success: uploads output files as ClickUp attachments, copies to NAS (best-effort), advances Stage, sets next status, posts summary comment
- On error: sets Error checkbox, posts structured error comment (what failed, how to fix)
- 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.) |
| 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
- Add an entry to
SKILL_MAPinskill_map.py - Write the skill
.mdfile(s) inskills/ - Add the Work Category value in ClickUp
- 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:
- Sets status to "AI Working"
- Reads the skill
.mdfile fromskills/ - Builds a prompt with skill instructions + task context:
- Task name, description, customer, target URL
- ClickUp task link
- Attached
.xlsxCora report URLs (if any) - Instructions to write output files to the working directory
- Runs
claude -p "<prompt>" --allowedTools "..." --max-turns N --permission-mode bypassPermissions --bare - Collects all files Claude created in the temp working directory
- Uploads files to ClickUp as attachments
- Copies files to NAS at
//PennQnap1/SHARE1/generated/{customer}/(best-effort) - Advances Stage, updates status, posts comment, unchecks Delegate to Claude
- 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)
- Reads the
KeywordandIMSURLcustom fields from the task - Sets status to "AI Working"
- Writes a job JSON file to
//PennQnap1/SHARE1/AutoCora/jobs/:{ "keyword": "CNC Machining", "url": "https://acme.com/cnc-machining", "task_ids": ["task_id"] } - Stores job metadata in the state DB for result polling
- Posts comment "Cora job submitted for keyword: ..."
- Unchecks "Delegate to Claude"
Result Polling (every poll cycle)
At the start of each cycle, the runner scans the results directory:
- Looks for
.resultfiles in//PennQnap1/SHARE1/AutoCora/results/ - Matches results to pending jobs via the state DB
- 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)
- On failure:
- Sets Error checkbox
- Posts structured error comment with failure reason
- Archives processed
.resultfiles toresults/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.logfor the full error - Verify the skill
.mdfile exists inskills/ - Verify
claudeCLI 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
.resultfiles 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.logfor 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