Add link building API endpoint and skill file

- /api/linkbuilding/status endpoint returns pending, in-progress,
  completed, and failed pipeline states for dashboard consumption
- skills/linkbuilding.md with YAML frontmatter linking tools and agents
- Skill body documents workflow, triggers, default flags, and ClickUp fields

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
cora-start
PeninsulaInd 2026-02-19 20:09:00 -06:00
parent 90e79b77ab
commit 0f4c77adc9
2 changed files with 152 additions and 7 deletions

View File

@ -1,6 +1,8 @@
"""Entry point: python -m cheddahbot""" """Entry point: python -m cheddahbot"""
import json
import logging import logging
from pathlib import Path
from .agent import Agent from .agent import Agent
from .agent_registry import AgentRegistry from .agent_registry import AgentRegistry
@ -131,13 +133,108 @@ def main():
log.warning("Scheduler not available: %s", e) log.warning("Scheduler not available: %s", e)
log.info("Launching Gradio UI on %s:%s...", config.host, config.port) log.info("Launching Gradio UI on %s:%s...", config.host, config.port)
app = create_ui(registry, config, default_llm, notification_bus=notification_bus) blocks = create_ui(registry, config, default_llm, notification_bus=notification_bus)
app.launch(
server_name=config.host, # Build a parent FastAPI app so we can mount the dashboard alongside Gradio.
server_port=config.port, # Inserting routes into blocks.app before launch() doesn't work because
pwa=True, # launch()/mount_gradio_app() replaces the internal App instance.
show_error=True, import gradio as gr
import uvicorn
from fastapi import FastAPI
from fastapi.responses import RedirectResponse
from starlette.staticfiles import StaticFiles
fastapi_app = FastAPI()
# Mount the dashboard as static files (must come before Gradio's catch-all)
dashboard_dir = Path(__file__).resolve().parent.parent / "dashboard"
if dashboard_dir.is_dir():
# Redirect /dashboard (no trailing slash) → /dashboard/
@fastapi_app.get("/dashboard")
async def _dashboard_redirect():
return RedirectResponse(url="/dashboard/")
fastapi_app.mount(
"/dashboard",
StaticFiles(directory=str(dashboard_dir), html=True),
name="dashboard",
) )
log.info("Dashboard mounted at /dashboard/ (serving %s)", dashboard_dir)
# Link building status API endpoint (for dashboard consumption)
@fastapi_app.get("/api/linkbuilding/status")
async def linkbuilding_status():
"""Return link building pipeline status for dashboard consumption."""
result = {
"pending_cora_runs": [],
"in_progress": [],
"completed": [],
"failed": [],
}
# Query KV store for tracked link building states
try:
for key, value in db.kv_scan("linkbuilding:watched:"):
try:
state = json.loads(value)
entry = {
"filename": state.get("filename", key.split(":")[-1]),
"status": state.get("status", "unknown"),
"task_id": state.get("task_id", ""),
}
if state.get("status") == "completed":
entry["completed_at"] = state.get("completed_at", "")
result["completed"].append(entry)
elif state.get("status") == "failed":
entry["error"] = state.get("error", "")
entry["failed_at"] = state.get("failed_at", "")
result["failed"].append(entry)
elif state.get("status") == "processing":
entry["started_at"] = state.get("started_at", "")
result["in_progress"].append(entry)
except json.JSONDecodeError:
pass
except Exception as e:
log.warning("Error reading linkbuilding KV state: %s", e)
# Query ClickUp for pending tasks (to do + Link Building + Cora Backlinks)
if config.clickup.enabled:
try:
from .clickup import ClickUpClient
cu = ClickUpClient(
api_token=config.clickup.api_token,
workspace_id=config.clickup.workspace_id,
task_type_field_name=config.clickup.task_type_field_name,
)
try:
tasks = cu.get_tasks_from_space(
config.clickup.space_id, statuses=["to do"]
)
for task in tasks:
if task.task_type != "Link Building":
continue
lb_method = task.custom_fields.get("LB Method", "")
if lb_method and lb_method != "Cora Backlinks":
continue
result["pending_cora_runs"].append({
"keyword": task.custom_fields.get("Keyword", ""),
"url": task.custom_fields.get("IMSURL", ""),
"client": task.custom_fields.get("Client", ""),
"task_id": task.id,
"task_name": task.name,
})
finally:
cu.close()
except Exception as e:
log.warning("Error querying ClickUp for pending link building: %s", e)
return result
# Mount Gradio at the root
gr.mount_gradio_app(fastapi_app, blocks, path="/", pwa=True, show_error=True)
uvicorn.run(fastapi_app, host=config.host, port=config.port)
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -0,0 +1,48 @@
---
name: link-building
description: Automated link building pipeline using Big-Link-Man CLI. Orchestrates CORA report ingestion and content generation for tiered link building campaigns. Use when the user asks about link building, Cora backlinks, content generation for SEO, or managing the link building pipeline.
tools: [run_link_building, run_cora_backlinks, blm_ingest_cora, blm_generate_batch, scan_cora_folder]
agents: [link_builder, default]
---
# Link Building Pipeline
This skill automates the link building workflow using the Big-Link-Man CLI tool. It handles CORA report ingestion and batch content generation for tiered backlink campaigns.
## Workflow
### Full Pipeline (Cora Backlinks)
1. **Ingest CORA Report**: Parse a `.xlsx` CORA report to create a project with keyword data, entities, and related searches
2. **Generate Content Batch**: Produce tiered content articles based on the ingested project data
### Triggers
- **Folder Watcher**: Automatically picks up new `.xlsx` files from `Z:/cora-inbox` and matches them to ClickUp tasks by keyword
- **Chat Command**: User can directly request link building via chat (e.g., "Run link building for precision-cnc-machining.xlsx")
- **ClickUp Task**: Tasks with Work Category = "Link Building" are tracked and executed when a matching CORA file appears
### Default Flags
- `-m` (money site URL): Always passed to prevent interactive prompts
- `-bp` (branded plus ratio): Defaults to 0.7 unless overridden
- `--continue-on-error`: Always set on generate-batch
### Available Tools
- `run_link_building` — Orchestrator that routes to the correct pipeline based on LB Method
- `run_cora_backlinks` — Full pipeline: ingest + generate
- `blm_ingest_cora` — Standalone ingest (creates project, returns job file)
- `blm_generate_batch` — Standalone generate (processes existing job file)
- `scan_cora_folder` — Check what's in the watch folder and their processing status
### ClickUp Integration
Link building tasks use these custom fields:
- **LB Method**: Pipeline type (currently "Cora Backlinks")
- **Keyword**: Target keyword (used for file matching)
- **IMSURL**: Money site URL
- **CustomAnchors**: Comma-separated anchor text overrides
- **BrandedPlusRatio**: Override for branded+ ratio
- **CLIFlags**: Additional CLI flags
- **CoraFile**: Path to .xlsx file (set by agent after match)