From 7153e65ae6b13672d2b3a69af3e4f8a8ff113ada Mon Sep 17 00:00:00 2001 From: PeninsulaInd Date: Mon, 16 Feb 2026 13:21:04 -0600 Subject: [PATCH] Add clickup_query_tasks tool and Press Advantage API integration plan 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 --- cheddahbot/tools/clickup_tool.py | 66 ++++++++++++++++++++++++ plans/press_advantage_api_integration.md | 60 +++++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 plans/press_advantage_api_integration.md diff --git a/cheddahbot/tools/clickup_tool.py b/cheddahbot/tools/clickup_tool.py index 9fb1ee4..871d72f 100644 --- a/cheddahbot/tools/clickup_tool.py +++ b/cheddahbot/tools/clickup_tool.py @@ -10,6 +10,20 @@ from . import tool log = logging.getLogger(__name__) +def _get_clickup_client(ctx: dict): + """Create a ClickUpClient from the agent's config.""" + from ..clickup import ClickUpClient + + cfg = ctx["config"].clickup + if not cfg.api_token: + return None + return ClickUpClient( + api_token=cfg.api_token, + workspace_id=cfg.workspace_id, + task_type_field_name=cfg.task_type_field_name, + ) + + def _get_clickup_states(db) -> dict[str, dict]: """Load all tracked ClickUp task states from kv_store.""" pairs = db.kv_scan("clickup:task:") @@ -26,6 +40,58 @@ def _get_clickup_states(db) -> dict[str, dict]: return states +@tool( + "clickup_query_tasks", + "Query ClickUp live for tasks. Optionally filter by status (e.g. 'to do', 'in progress') " + "and/or task type (e.g. 'Press Release'). Returns task name, ID, status, type, due date, " + "and custom fields directly from the ClickUp API.", + category="clickup", +) +def clickup_query_tasks(status: str = "", task_type: str = "", ctx: dict = None) -> str: + """Query ClickUp API for tasks, optionally filtered by status and task type.""" + client = _get_clickup_client(ctx) + if not client: + return "Error: ClickUp API token not configured." + + cfg = ctx["config"].clickup + if not cfg.space_id: + return "Error: ClickUp space_id not configured." + + try: + statuses = [status] if status else None + tasks = client.get_tasks_from_space(cfg.space_id, statuses=statuses) + except Exception as e: + return f"Error querying ClickUp: {e}" + finally: + client.close() + + if task_type: + tasks = [t for t in tasks if t.task_type.lower() == task_type.lower()] + + if not tasks: + filters = [] + if status: + filters.append(f"status='{status}'") + if task_type: + filters.append(f"type='{task_type}'") + filter_str = " with " + ", ".join(filters) if filters else "" + return f"No tasks found{filter_str}." + + lines = [] + for t in tasks: + parts = [f"**{t.name}** (ID: {t.id})"] + parts.append(f" Status: {t.status} | Type: {t.task_type or '—'}") + # Show custom fields that have values + fields = {k: v for k, v in t.custom_fields.items() if v} + if fields: + field_strs = [f"{k}: {v}" for k, v in fields.items()] + parts.append(f" Fields: {', '.join(field_strs)}") + parts.append(f" URL: {t.url}") + lines.append("\n".join(parts)) + + return f"**ClickUp Tasks ({len(lines)}):**\n\n" + "\n\n".join(lines) + + @tool( "clickup_list_tasks", "List ClickUp tasks that Cheddah is tracking. Optionally filter by internal state " diff --git a/plans/press_advantage_api_integration.md b/plans/press_advantage_api_integration.md new file mode 100644 index 0000000..60672a2 --- /dev/null +++ b/plans/press_advantage_api_integration.md @@ -0,0 +1,60 @@ +# Press Advantage API Integration Plan + +## Status: Blocked — waiting on PA support to fix API access + +API key is in `.env` as `PRESS_ADVANTAGE_API`. Auth works (`api_token` query param) but returns "account is cancelled or past due" on all endpoints. Emailed PA support. + +Test release: #81505 (draft) + +## Workflow Overview + +### Track 1: No Client Approval +1. `write_press_releases` tool generates 2 PRs + schemas (DONE) +2. ClickUp integration saves PRs as attachments + Google Doc (separate work, not CheddahBot) +3. Bryan picks one in chat ("I like PR A") +4. `submit_press_release` tool uploads to Press Advantage via API +5. Done + +### Track 2: Client Approval Required +1. `write_press_releases` tool generates 2 PRs + schemas (DONE) +2. ClickUp integration saves PRs as attachments + Google Doc (separate work, not CheddahBot) +3. Bryan picks one, sends to client for review +4. **Waiting period** — auto-generate weekly nag email to Bryan's partner to follow up with client +5. Client approves (comes back in the doc) +6. `submit_press_release` tool uploads to Press Advantage via API +7. Done + +## What To Build + +### 1. `submit_press_release` tool +- New `@tool` in `cheddahbot/tools/` +- Takes: PR text (or file path), headline, organization_id, distribution type +- Calls `POST /api/customers/releases/with_content.json` +- Params: `release[organization_id]`, `release[title]`, `release[body]`, `release[distribution]`, `release[schedule_distribution]` +- Returns: release ID, status +- Need to figure out org ID mapping (company name → PA org ID) + +### 2. Org ID mapping +- `GET /api/customers/organizations.json` lists all orgs with IDs +- Could cache this or add a lookup tool +- Or add PA org IDs to `skills/companies.md` + +### 3. Weekly nag emails (Track 2) +- Time-driven, not chat-driven +- Options: CheddahBot heartbeat check, ClickUp automation, or cron +- Needs state tracking: which PRs are awaiting client approval and since when + +## API Reference (key endpoints) + +| Endpoint | Method | Purpose | +|----------|--------|---------| +| `/api/customers/releases/with_content.json` | POST | Submit finished PR | +| `/api/customers/releases.json` | GET | List all releases | +| `/api/customers/releases/{id}.json` | GET | Get release status | +| `/api/customers/releases/{id}/approve_content.json` | POST | Approve for distribution | +| `/api/customers/releases/{id}/built_urls.json` | GET | Get published URLs | +| `/api/customers/organizations.json` | GET | List orgs (get org IDs) | + +## Auth +- Query param: `?api_token=` +- Key stored in `.env` as `PRESS_ADVANTAGE_API`