- Rewrote skills/press_release_prompt.md for headless claude -p execution: generates 7 headlines, self-judges top 2, writes TWO complete press releases with different angles, generates JSON-LD schema for each - Output: 5 files named by headline (e.g. "Company Headline Here.txt/.json") - Merged schema generation from press-release-schema.md into single skill - Added "Introduces" to headline tone trigger words - Removed --bare flag from claude_runner.py (breaks OAuth auth) - Documented headline tone trigger words in clickup-task-creation.md 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
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