Skip to content

Competitor Intelligence

Sam Renders edited this page May 17, 2026 · 1 revision

Competitor Intelligence (v2.3)

A self-discovering, LLM-driven competitive-intelligence pipeline. Free signals + Sonnet curation, $0 incremental cost, configurable per brand. Replaces the v2.2.x "weekly Tavily dump + Sonnet synth" with breadth that rivals commercial CI platforms (Crayon/Klue/Kompyte).


At a glance

What How often Where it lands
Discovery (Tavily) weekly, cached 30 days competitor_state.json .last_discovery
5 signal collectors per competitor parallel per weekly run competitor_state/events.jsonl
severity:high alerts every 10 min via competitor-alert cron Telegram + reports/.../alerts.log
severity:med digest daily 17:00 via competitor-daily cron reports/.../daily-YYYY-MM-DD.md + Telegram
Strategic synthesis Mon 10:00 via competitor-intel cron reports/.../YYYY-MM-DD_<brand>.md + Telegram

Signal sources

All in scripts/lib/competitor/. Bash + jq + curl, no external deps.

Collector Source Severity heuristic
reddit-search.sh Reddit JSON API (no auth) score>100 + comments>20 = high; score>20 or complaint/vs/alternative keywords = med
hn-search.sh HN Algolia API (no auth) points>100 + comments>30 = high; Show HN / Launch HN / is hiring = med
appstore-lookup.sh iTunes Lookup API (no auth) version released within last 7d = high; >10k ratings = med
jobs-feed.sh Greenhouse + Lever public APIs VP/Head/Director/Chief/Founding = high; new geo or new team = med
page-diff.sh curl + normalize + SHA-256 + diff pricing page change with money token ($///mo) = high; other pricing/features/changelog = med

page-diff.sh retains 12 snapshots per URL kind per competitor. State at competitor_state/<brand>/<comp>/snapshots/<kind>.<sha8>.txt with <kind>.latest symlink.


Severity routing

event-router.sh reads JSONL events on stdin and:

  1. Appends every event to competitor_state/events.jsonl (append-only audit log).
  2. Branches by .severity:
    • high → also writes to queue/immediate.jsonl — drained every 10 min by ops-competitor-alert.sh. Produces 🔥 [HIGH] {brand} {competitor} {kind}: {snippet} alerts. Telegram push + alerts.log.
    • medqueue/daily.jsonl — drained at 17:00 (timezone-aware) by ops-cron-competitor-daily.sh. Groups by competitor, top 3–5 snippets per competitor, single Telegram message ≤4000 chars.
    • low → no queue entry. Surfaces in weekly strategic synthesis only.

Weekly strategic synthesis

ops-cron-competitor-intel.sh fires Mon 10:00 in the configured timezone. Pipeline:

  1. Discovery — Tavily, only if cached last_discovery > 30 days ago. Cuts ~60% of Tavily spend.
  2. Per-competitor signal collection — all 5 collectors run in parallel background jobs per known competitor with 30-second timeout guards. Page-diff URLs are read from preferences.json .competitor_intel.urls.<competitor>.
  3. Brand mentions — Tavily news 7-day window for own brand → router.
  4. Read 7-day event window from events.jsonl (jq epoch filter).
  5. LLM synthesisclaude_invoke Sonnet 4.6 against the event log (cap 100k chars). Three-strategy JSON extraction (fenced block → bare regex → bullet-list scrape). Raw synthesis ALWAYS persisted to reports/competitor-intel/YYYY-MM-DD_<brand>-synthesis.md before extraction so partial output is never lost.
  6. Update state — extracted competitor list (deduped, capped at max_competitors) + last_run.
  7. Disk report — polished digest to reports/competitor-intel/YYYY-MM-DD_<brand>.md + latest-<brand>.md symlink. Telegram push if creds present (additive only).

Output sections: NEW entrants, Competitor moves this week, Brand signal, Threats & opportunities.


Consumers (v2.3.1 — wired across the plugin)

The shared lib scripts/lib/competitor/context.sh exposes four functions, all jq-only and cheap to call:

Function Returns
competitor_context [--brand X] [--window-days N] [--severity S] Aggregated JSON: brands, per-brand state + latest report path, events grouped by severity, queue sizes
competitor_briefing_line One-line summary for briefings (Healify: 2 alerts · 5 med deltas · last run …)
competitor_priority_items --top N Bullet list of top-N high-severity events for priority advisors
competitor_vertical_slice <role> Role-filtered slice. Roles: marketing, ecom, ceo, cfo, coo, cto
Surface What you see
/ops:go COMPETITOR row: alerts count, last_run, top-3 event snippets. Mobile: comp: N alerts (top: …)
/ops:next Priority 2 (between fires + comms): REACT: <competitor> <source> changed — see latest-<brand>.md
/ops:marketing PRICING MOVES + FUNDING/NEWS + SENTIMENT sections
/ops:ecom APP RELEASES + PRODUCT/PRICING CHANGES sections
/ops:yolo CEO/CTO/CFO/COO Each agent loads its role-specific vertical slice and folds it into their analysis
Skills Reference#opscompetitors /ops:competitors Dedicated dashboard + drill-down + add-url + alerts + refresh
bin/ops-competitors Standalone CLI — runnable outside Claude Code

All consumers skip silently when competitor-intel is unconfigured. Mobile-mode compressed throughout (Rule 7).


Configuration

In preferences.json:

{
  "competitor_intel": {
    "brand_name": "Healify",
    "category": "AI health coaching apps",
    "max_competitors": 5,
    "report_timezone": "Europe/Amsterdam",
    "app_store": true,
    "urls": {
      "Noom": {
        "pricing": "https://www.noom.com/plans/",
        "features": "https://www.noom.com/how-it-works/",
        "careers": "https://www.noom.com/careers/"
      }
    }
  }
}

Required env

  • TAVILY_API_KEY — free tier 1000 searches/month covers ~30 brands. Get one at https://tavily.com.

Optional env

  • TELEGRAM_BOT_TOKEN + TELEGRAM_CHAT_ID — for push delivery. Without these, reports persist to disk only.

Daemon services

Three cron entries in daemon-services.json (auto-added by /ops:setup):

{
  "competitor-intel":  { "enabled": true, "cron": "0 10 * * 1" },
  "competitor-alert":  { "enabled": true, "cron": "*/10 * * * *" },
  "competitor-daily":  { "enabled": true, "cron": "0 17 * * *" }
}

State + reports layout

$DATA_DIR/                                            (~/.claude/plugins/data/ops-ops-marketplace)
├── competitor_state.json                             # per-brand competitors + last_run + last_discovery
├── competitor_state/
│   ├── events.jsonl                                  # append-only audit log of all signal events
│   ├── queue/
│   │   ├── immediate.jsonl                           # severity:high, drained every 10 min
│   │   ├── daily.jsonl                               # severity:med, drained 17:00
│   │   └── immediate.processed.jsonl                 # archive of processed alerts
│   └── <brand-slug>/<competitor-slug>/snapshots/
│       ├── pricing.<sha8>.txt                        # page-diff snapshots
│       └── pricing.latest                            # → symlink to current
└── reports/competitor-intel/
    ├── YYYY-MM-DD_<brand>.md                         # weekly digest
    ├── YYYY-MM-DD_<brand>-synthesis.md               # raw LLM synthesis (preserved before extraction)
    ├── latest-<brand>.md                             # → symlink to latest weekly
    ├── daily-YYYY-MM-DD.md                           # daily med-severity roll-up
    └── alerts.log                                    # every high-severity alert (rolling)

Cost at scale

At 10 configured brands:

  • Tavily: ~13 calls/wk total (discovery cached 30 days; per-competitor news only fires when no other signal landed that week)
  • Sonnet: ~320k tokens/month total against Max-OAuth (no API billing)
  • Embedding (if v2.4 adds dedup): local all-MiniLM-L6-v2 — free, or OpenAI text-embedding-3-small at ~$0.02/wk
  • HTTP scraping: $0

Total $0 incremental at portfolio scale.


Operational commands

/ops:competitors                              # dashboard — all brands
/ops:competitors Healify                      # drill-down — 30d timeline for one brand
/ops:competitors refresh                      # manually trigger weekly cron now
/ops:competitors refresh Healify              # refresh a specific brand
/ops:competitors add-url Healify Noom pricing https://www.noom.com/plans/
/ops:competitors alerts                       # tail last 20 of alerts.log

Or via the standalone CLI:

ops-competitors                               # same subcommands as the skill

What's NOT in v2.3 (deferred to v2.4)

  • Embedding-based dedup of news items (collapse 5 versions of same story → 1)
  • Live Notion DB sync (per-competitor pages auto-updated)
  • Self-improving feedback loop (Telegram 👍/👎 reactions → signal weight tuning)
  • Auto-discovery of new signal sources from LLM proposals
  • Multi-brand portfolio dashboard view

What we explicitly DON'T build

  • G2 official API — paywalled, not worth it at this scale
  • SimilarWeb traffic data — paywalled
  • Crunchbase API — $$$
  • Anything that requires keeping a vendor in the loop

Free signals + LLM curation beats their value at small/medium portfolio scale.


Source: scripts/lib/competitor/ (collectors + router + context lib), scripts/ops-cron-competitor-intel.sh (weekly), scripts/ops-competitor-alert.sh (immediate), scripts/ops-cron-competitor-daily.sh (daily), bin/ops-competitors (CLI), skills/ops-competitors/SKILL.md (skill).

Changelog: shipped in v2.3.0 (producer) + v2.3.1 (consumers) + v2.3.2 (docs). See Changelog for full history.

Clone this wiki locally