Add add_dependency() to ClickUpClient and clickup_add_dependency chat tool
for setting "blocked by" relationships between tasks. New create-task-set
skill enables bundled task creation (LINKS, PR, NEW, OPT) with correct
fields, time estimates, due date offsets, and automatic dependencies.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
LLM plural check was firing for every non-exact keyword pair (e.g. "insert molding"
vs "cement plant lubrication"), causing timeouts and wasted API calls. Now requires
keywords to share all but one word before escalating to the LLM.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Execution brain timeout bumped from 15 min to 45 min default to avoid
content writing timeouts. BLM stays at 30 min. Both configurable via
CHEDDAH_TIMEOUT_EXECUTION_BRAIN / CHEDDAH_TIMEOUT_BLM env vars or
config.yaml timeouts section.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace emoji prefixes in ClickUp comments and notifications with
plain ASCII tags ([FAILED], [DONE], [WARNING], [STARTED], [OUTLINE]).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Windows cp1252 console encoding can't handle → (U+2192), causing
UnicodeEncodeError in logging. Replaced with -> in all runtime strings.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Substring and word-overlap matching caused cross-contamination between
similar keywords (e.g. "shaft manufacturing" matching "custom shaft
manufacturing"). Now only exact matches pass immediately; non-exact
pairs are checked via OpenRouter LLM call with session-level caching.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Thread-safe active execution registry in Scheduler tracks which tool
functions are currently blocking. New get_active_tasks chat tool reads
this registry plus loop timestamps to report running tasks, durations,
loop health, and a safe-to-restart verdict.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PR Topic now only drives headline angle and awareness/news framing.
Anchor text is derived from Company Name + Keyword (ClickUp Keyword field).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- "pipeline completed" → descriptive messages for link building and content
- "ClickUp task completed" → "{task} done — ran {tool} successfully"
- "Cora report completed" → "Cora report generated — ready for you to look at it"
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add set_custom_field_smart() to auto-resolve dropdown option UUIDs
- Extend create_task with priority, assignees, time_estimate params
- Expand clickup_create_task tool and CLI script with tags, due dates, custom fields
- Add _comment_distributed_tasks to post clear ClickUp comments on Cora distribution
(e.g. "Cora XLSX moved to cora-inbox" / "content-cora-inbox")
- Remove unused _find_all_todo_tasks; simplify AutoCora sibling matching
- Add tests for set_custom_field_smart dropdown and text fields
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New create_task() and find_list_in_folder() methods on ClickUpClient,
clickup_create_task chat tool, create-task skill, and CLI script for
creating tasks in a client's Overall list with Client/Work Category fields.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replaces all "Customer" field lookups with "Client" to match the new
space-wide dropdown, eliminating the 20+ duplicate list-level fields.
Includes migration script that populated 400 active tasks.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Content Creation tasks with a URL were incorrectly routed to the
optimization path. Now the scheduler sets content_type from Work
Category, and the tool routes on that. Chat callers fall back to
URL-based detection when content_type is empty.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Compute the final-content.html save path upfront (network share with
local fallback) and pass it in the Phase 2 prompt so the execution
brain writes directly to the share. Summary still saved separately
as final-content.md via _save_content.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The tool registry only forwarded clickup_task_id from args to ctx,
so clickup_task_status was silently filtered out. Phase 2 detection
in create_content never saw the original "outline approved" status.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The scheduler sets status to "automation underway" before the tool runs,
so create_content's API re-fetch never saw "outline approved". Now the
scheduler passes the pre-change status via clickup_task_status arg.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Bump Claude Code subprocess timeout from 5 to 15 minutes for longer content tasks
- Fix scheduler result file loop: unlink source if already exists in processed/ dir
- Pass outline save path to execution brain so it writes directly to network share
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a ClickUp task with a URL arrives at create_content, route it to the
new optimization pipeline instead of the outline-gate Phase 1/Phase 2 flow.
The pipeline runs 8 steps via the execution brain (scrape, deficit analysis,
entity filtering, template writing, test block generation, readability
rewrite, validation, surgical instructions doc) and uploads deliverables
(test_block.html, optimization_instructions.md, validation_report.json)
directly to ClickUp as attachments.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New watcher thread scans Z:/Cora-For-Human for post-macro Cora xlsx files,
matches them to ClickUp tasks by keyword, and copies to the appropriate
pipeline inbox (Z:/cora-inbox for Link Building, Z:/content-cora-inbox for
Content/OPO). Fixes issue where shared Cora reports left one pipeline's
tasks stuck in automation underway forever.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove all KV store reads/writes from task pipeline code. ClickUp is now
the single source of truth for task state. File location (processed/
subfolder) tracks file processing state. Loop timestamps use in-memory
dict on Scheduler.
Source changes:
- scheduler.py: Remove KV dedup, fallback sync path, docx extraction;
tools own their ClickUp sync; in-memory timestamps
- press_release.py: Remove KV state writes, log-only _set_status
- linkbuilding.py: Remove KV state writes, processed/ subfolder check
- content_creation.py: Phase detection via ClickUp API status, remove
KV phase/state tracking, _update_kv_state removed
- clickup_tool.py: Rewrite to query ClickUp API directly
- ui.py: Pipeline status polling is now a no-op
Test changes:
- test_scheduler.py: Remove KV dedup tests, remove fallback path test,
verify ClickUp API calls instead of KV state
- test_content_creation.py: Mock _get_clickup_client for phase detection,
verify ClickUp sync calls instead of KV assertions
- test_linkbuilding.py: Remove KV status test, verify ClickUp API calls
- test_clickup_tools.py: Rewrite for API-backed tools
- test_scheduler_helpers.py: Test in-memory timestamps
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When Phase 2 cannot find the outline file at any of the searched
locations, the task is now reset to its initial poll status in ClickUp
with a comment explaining the issue and suggesting a Phase 1 re-run.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When upload_attachment() returns False, the ClickUp comment now includes
a warning listing which files failed to upload and their local paths,
so Bryan can retrieve them manually.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 1 now writes OutlinePath to a ClickUp custom field via
set_custom_field_by_name(). Phase 2 reads it back with
get_custom_field_by_name(), falling back to convention path
({outline_dir}/{slug}/outline.md) if the field is empty.
Added get_task(), set_custom_field_by_name(), and
get_custom_field_by_name() helpers to ClickUpClient.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace single-day task filter with multi-pass sweep when no explicit
target_date: (1) due today, (2) overdue + current month tag, (3) last
month tag, (4) look-ahead 2 days. Deduplicate across passes.
Remove KV store from submit (dedup by job file existence) and result
poller (scan results/ folder directly, move to processed/ after handling).
Scheduler auto-submit no longer passes explicit target_date.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Content tasks now trigger from Cora xlsx files dropped in Z:/content-cora-inbox/
instead of auto-firing from ClickUp polling. The watcher fuzzy-matches files to
ClickUp tasks and auto-detects content type from URL presence (optimization vs
new content). Adds cli_flags support for service page hints.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 1 researches competitors and generates an outline via the execution brain,
saves it to a network/local path, and pauses for human review. Phase 2 picks up
the approved outline and writes full SEO-optimized content. ClickUp integration
maps "On Page Optimization" and "Content Creation" work categories, with
"outline approved" added to poll_statuses for automatic Phase 2 triggering.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Auto-submit Cora jobs for tasks due today on each autocora loop cycle
- Move ClickUp tasks to "automation underway" at submission time
- Default to blank URL for tasks missing IMSURL (new content)
- Use task Keyword field as project_name in folder watcher (not task name)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Automates Cora SEO report workflow: queries ClickUp for qualifying tasks,
submits jobs to a shared folder queue, polls for results, and updates task
statuses. Includes two tools (submit_autocora_jobs, poll_autocora_results),
a scheduler polling loop, and 30 tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Track per-call token usage and estimated costs across all OpenRouter models.
Switch planner agent from Claude Sonnet 4.6 ($3/$15 per M) to Grok 4.1 Fast
($0.20/$0.50 per M) for ~25x cost reduction. Add budget alerts, a dashboard
card, and a check_api_usage tool for visibility into spending.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New report_issue tool logs bugs/improvements to memory/improvement_requests.md
- Planner agent (Sonnet via OpenRouter) for architecture and debugging tasks
- Heartbeat checks for pending improvement requests to surface to Bryan
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tasks now show "automation underway" when the bot picks them up and "error"
on failure, replacing the old "in progress" / "to do" fallbacks that were
invisible on Bryan's ClickUp board. Folder watcher also syncs ClickUp status
on match, missing IMSURL, pipeline failure, success, and exceptions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove status filter from folder watcher so tasks in any open status
(including "internal review") are matched by keyword
- Add retry logic for stuck processing/blocked/unmatched KV states
- Fix notification ordering (newest first, limit 50) and date parsing
- Use BLM's own .venv Python instead of uv run for subprocess calls
- Document external tools venv convention in CLAUDE.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove placeholder URL fallback from ingest-cora args. Add early
validation in run_cora_backlinks and folder watcher — if IMSURL is
empty, block the task with a notification instead of running with a
fake URL. Update tests to pass money_site_url and add missing-URL test.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ClickUp "Client" custom field was deleted; switch all references to
"Customer" across config, tools, tests, and docs. Refine Overview tab:
rename Due Soon to Up Next (today/tomorrow, fallback next 5), expand
This Month to include both month tag and due-date-in-month matches.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New cheddahbot/api.py: FastAPI router with endpoints for tasks,
link building, press releases, agents, system health, notifications,
KV states, and cache management (all cached 5min)
- Rewrote dashboard/index.html: replaces all hardcoded data with JS
that fetches from /api/ endpoints. Tabs: Overview, Link Building,
Press Releases, By Company, System Health, Agents, Notifications
- Updated __main__.py: mounts API router, removes old inline
/api/linkbuilding/status endpoint
- Fixed run_link_building to reject empty LB Method instead of
defaulting to Cora Backlinks
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Ruff format: consistent dict/call wrapping in agent.py, db.py,
skills.py, delegate.py
- Replace clickup_approve_task/clickup_decline_task with
clickup_reset_task/clickup_reset_all (simpler state machine)
- Add kv_delete() method to Database
- Add due_date and field filter tests to test_clickup.py
- Update test_clickup_tools.py for new reset tools
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove f-prefix from strings with no placeholders
- Use list unpacking instead of concatenation
- Fix import sorting in test file
- Remove unused Path import
- Use contextlib.suppress instead of try/except/pass
- Wrap long lines to stay under 100 chars
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New linkbuilder agent that handles ClickUp "Link Building" tasks.
For each keyword/company, generates three content pieces via the
execution brain: a guest article (500-700 words), a directory
listing, and a social media post — each with proper SEO anchor
text and backlinks. Integrates with ClickUp for status updates,
comments, and file attachments.
- cheddahbot/tools/linkbuilding.py: build_links tool with full pipeline
- skills/linkbuilding.md: skill prompt for SEO content generation
- config.yaml: linkbuilder agent config + Link Building skill_map entry
- tests/test_linkbuilding.py: 36 tests covering helpers, prompts,
pipeline, file output, error handling, and ClickUp sync
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Removed clickup_task_id from write_press_releases function signature
so the LLM cannot see or fabricate a task ID. The parameter is now
passed through ctx by the ToolRegistry — the scheduler sets it in
args, and execute() moves it into the ctx dict before filtering.
Only system-injected task IDs can reach the tool.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The press release tool now handles its own ClickUp sync lifecycle when
a clickup_task_id is provided — sets status to "in progress" with a
starting comment, uploads docx attachments after creation, then sets
status to "internal review" with a completion comment. The scheduler
now passes clickup_task_id to tools and defers to tool-level sync when
detected, falling back to scheduler-level sync for other tools.
ToolRegistry.execute() now filters args to accepted params to prevent
TypeError when extra keys (like clickup_task_id) are passed to tools
that don't accept them.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New tool in delegate.py routes tasks to named agents via
agent.respond_to_prompt(). Includes thread-local depth counter
(max 3) to prevent infinite A->B->A delegation loops. Extended
ctx injection in ToolRegistry to include agent_registry. Wired
agent_registry into ToolRegistry from __main__.py.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Agent changes:
- Accept optional AgentConfig in __init__
- Add name property
- Filter tools via agent_config.tools whitelist in respond()
- Use agent-specific personality file when configured
- Pass agent name to skills registry for filtering
ToolRegistry changes:
- get_tools_schema() accepts filter_names parameter
- get_tools_description() accepts filter_names parameter
- When filter_names is None, all tools are returned (backward compat)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The _load_skill() function now strips YAML frontmatter (--- ... ---)
before returning skill content. This prevents the execution brain
from receiving metadata intended for the skill registry.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When config.shell.require_approval is True, run_command now refuses
execution and directs the user to delegate_task instead. The execution
brain (Claude Code CLI) has its own approval controls.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove unused modules that were never called at startup:
- cheddahbot/skills/__init__.py (dead @skill decorator system)
- cheddahbot/providers/__init__.py (empty placeholder)
- cheddahbot/tools/build_skill.py (depends on dead skills system)
- cheddahbot/tools/build_tool.py (security risk: generates arbitrary Python)
Also fix all pre-existing ruff lint errors across the codebase:
- Fix import sorting, unused imports, line length violations
- Fix type comparisons (use `is` instead of `==`)
- Fix implicit Optional types (dict -> dict | None)
- Fix unused variables, ambiguous variable names
- Apply ruff format for consistent style
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a press release is triggered via chat with a ClickUp task ID, the
tool now uploads .docx attachments, posts a result comment, and updates
task status — matching what the scheduler does automatically. ClickUp
sync is wrapped in try/except so failures don't lose PR results.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>