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>
Adds PressAdvantageClient API wrapper and submit_press_release tool that
posts finished press releases to PA as drafts. Auto-constructs SEO links
(brand+keyword → IMSURL, company name → SocialURL/GBP/homepage) with
fuzzy anchor matching and warnings when phrases can't be found. The PR
writing prompt now requests anchor text phrases and validates them after
generation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Press releases now auto-generate .docx files alongside .txt for native
Google Docs import. New email_file chat tool sends files via Gmail SMTP
with app password auth, auto-converting .txt to .docx before sending.
Also includes Press Advantage API config and submit_press_release tool.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The press release pipeline takes 2-4 minutes per run with no feedback.
This adds a DB-polled status box (gr.Timer every 3s) that shows the
current step (e.g. "Step 3/4: Writing press release 1/2 — headline...")
and auto-hides when the pipeline completes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New tool queries ClickUp API live for tasks, with optional status and
task_type filters. Fixes the execution brain instantiation bug by
providing a proper @tool wrapper around ClickUpClient.
Also saves the Press Advantage API integration plan for future work
once API access is restored.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Four chat-facing tools: clickup_list_tasks (list/filter tracked tasks),
clickup_task_status (detailed state view), clickup_approve_task and
clickup_decline_task (approval flow for tasks awaiting permission).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements write_press_releases tool that generates 7 headlines via chat
brain, AI-judges the best 2, writes 2 full press releases via execution
brain, and generates JSON-LD schemas via Sonnet with WebSearch. Saves all
output files to data/generated/press_releases/.
Also adds tools/model pass-through in agent and LLM layers, fixes Windows
command line length limit by piping prompts via stdin, and updates model
references to current versions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Chat brain uses OpenAI-compatible APIs (OpenRouter/Ollama/LM Studio) for
all UI conversations, giving full control over system prompts so the
Cheddah personality works correctly. Execution brain uses Claude Code CLI
for heartbeat, scheduled tasks, and delegated system-level work.
- Split llm.py: chat() routes through OpenAI-compat only, new execute()
calls Claude CLI with Bash/Read/Edit/Write/Glob/Grep tools
- Add chat_model config field (default: openai/gpt-4o-mini)
- Add delegate_task tool bridging chat brain to execution brain
- Scheduler/heartbeat now use execute_task() for real CLI power
- UI dropdown shows chat-only models with custom value support
- Updated model list to current OpenRouter top models (Feb 2026)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Multi-model AI assistant with Gradio UI, persistent memory, 15+ tools,
and meta-tools for runtime tool creation. Routes Claude models through
Claude Code SDK (Max subscription), cloud models through OpenRouter,
and local models through Ollama/LM Studio.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>