Competitor Brief
Keep a small, durable picture of each tracked competitor and surface only what
changed since last time. The value is signal, not a re-scrape: a good brief is
short, dated, and sourced.
State lives in the admin-copilot plugin's storage and is read/written through
the admin_copilot_prefs tool from the included admin-copilot-prefs
skill, called via skill_execute — never edit files by hand:
- The tracked list:
list_competitors, add_competitor, remove_competitor.
- Per-competitor last-run snapshots:
get_competitor_snapshot,
save_competitor_snapshot.
If skill_execute reports admin_copilot_prefs as unknown or not
allowed, the admin-copilot-prefs skill has not projected into this
session — the state actions will not become reachable by exploring, and
searching the filesystem for the storage files will not find them (the path
is internal). Try skill_load with skill: "admin-copilot-prefs" once; if
it still fails, do not loop or hand-write state. Stop and report one
line: "Competitor brief can't run yet — the admin-copilot state tool isn't
loading; restart the assistant to fix it." Do not fabricate a brief from
memory.
When the user is curating the list
- Add:
admin_copilot_prefs add_competitor with name and, when known,
domain, watch_urls (pricing / changelog / blog / careers pages worth
checking), and keywords (topics to weight — e.g. "pricing", "enterprise",
"funding").
- Remove / list:
remove_competitor / list_competitors.
- Confirm the resulting list back to the user.
Producing a brief (per run)
Load state. list_competitors, and for each one get_competitor_snapshot
to recall what you saw last time (sources + findings + lastRunAt).
Gather current signal for each competitor:
- Fetch each
watch_url with web_fetch; run a web_search for the
competitor name plus its keywords and recent dates.
- For a deeper periodic pass (e.g. monthly, or when the user asks for depth),
escalate to the deep-research harness instead of light fetches. Keep the
weekly run light and cheap.
Cheap change pre-filter. For each fetched page, hash the text and compare
to the stored source hash to skip pages that did not change at all:
# prints the sha256 of normalized stdin text
echo "$PAGE_TEXT" | bun "$SKILL_DIR/scripts/page-hash.ts"
A matching hash means the page is byte-stable — skip deep analysis of it.
A new or changed hash means read it closely. The hash is only a pre-filter;
you decide what is material.
Judge materiality. Compare new signal against the prior findings. Surface
only genuine changes: new/changed pricing, product launches or major
changelog entries, funding or M&A, notable hiring (esp. leadership), and
positioning/messaging shifts. Ignore cosmetic edits and re-phrasings.
Suggest a next move (don't take it). A finding that just sits there is half
the value. For each material finding, decide whether it warrants a concrete
response — and if so, prepare it, never transmit it:
- A competitor pricing or positioning shift sales should know → a draft
note to the sales/team channel (e.g. a
slack draft), handed over for the
user to send.
- A launch or capability worth a closer look → propose a deeper
deep-research
pass, or a calendar hold to dig in.
- Something worth keeping in the user's own context → a one-line
remember.
Only attach an action when there's a real one; most findings are just signal.
No action ever leaves the building on its own — every note is a draft the user
sends, consistent with the no-send rule below.
Write the new snapshot. save_competitor_snapshot with the full object so
next run can diff against it:
{
"competitor": "Example Co",
"lastRunAt": "<ISO timestamp>",
"sources": [
{ "url": "https://example.com/pricing", "contentHash": "<sha256>", "fetchedAt": "<ISO>", "summary": "1-line state" }
],
"findings": [
{ "title": "Launched X tier", "detail": "...", "category": "launch", "sourceUrl": "https://...", "firstSeenAt": "<ISO>" }
]
}
Use the system clock for timestamps (date -u +%Y-%m-%dT%H:%M:%SZ).
Deliver the brief. Group by competitor; lead with the most material item;
one line each, every claim linked to its source. Where a finding earned a
suggested move in step 4, append it on its own line as an offer the user can
accept by name ("draft a heads-up to #sales", "dig into their new tier") — the
prepared draft or proposal, never an action already taken. Respect the
configured delivery channel (admin_copilot_prefs get_prefs →
competitorBrief.channel): in-app means reply directly; for slack/email, hand
off to the relevant channel (the notifications or messaging skill).
Empty weeks
If nothing material changed, still report — one line: "No material competitor
changes this week (checked N competitors)." Silence reads as a broken job and
erodes trust. Always save the refreshed snapshot even when findings are empty so
hashes stay current.
Rules
- Only changes worth a busy person's attention. When in doubt, leave it out.
- Every finding cites a source URL. No source, no finding.
- This skill never sends email or messages on its own — delivery to slack/email
goes through the dedicated channel skills, which carry their own confirmation.
- Keep weekly runs cheap; reserve deep-research for explicit deep passes.
1---2name: competitor-brief3description: Recurring competitor and market monitoring. Tracks a saved list of competitors, checks their sites and recent news each run, diffs against the last run, and reports only what materially changed — pricing, launches, funding, hiring, positioning. Use for "track competitors", "competitive intel", "what changed with <competitor>", managing the tracked list, or the weekly competitor brief.4---56# Competitor Brief78Keep a small, durable picture of each tracked competitor and surface **only what9changed** since last time. The value is signal, not a re-scrape: a good brief is10short, dated, and sourced.1112State lives in the `admin-copilot` plugin's storage and is read/written through13the **`admin_copilot_prefs`** tool from the included `admin-copilot-prefs`14skill, called via `skill_execute` — never edit files by hand:1516- The tracked list: `list_competitors`, `add_competitor`, `remove_competitor`.17- Per-competitor last-run snapshots: `get_competitor_snapshot`,18 `save_competitor_snapshot`.1920> **If `skill_execute` reports `admin_copilot_prefs` as unknown or not21> allowed,** the `admin-copilot-prefs` skill has not projected into this22> session — the state actions will not become reachable by exploring, and23> searching the filesystem for the storage files will not find them (the path24> is internal). Try `skill_load` with `skill: "admin-copilot-prefs"` once; if25> it still fails, do **not** loop or hand-write state. Stop and report one26> line: "Competitor brief can't run yet — the admin-copilot state tool isn't27> loading; restart the assistant to fix it." Do not fabricate a brief from28> memory.2930## When the user is curating the list3132- **Add:** `admin_copilot_prefs` `add_competitor` with `name` and, when known,33 `domain`, `watch_urls` (pricing / changelog / blog / careers pages worth34 checking), and `keywords` (topics to weight — e.g. "pricing", "enterprise",35 "funding").36- **Remove / list:** `remove_competitor` / `list_competitors`.37- Confirm the resulting list back to the user.3839## Producing a brief (per run)40411. **Load state.** `list_competitors`, and for each one `get_competitor_snapshot`42 to recall what you saw last time (sources + findings + `lastRunAt`).432. **Gather current signal** for each competitor:44 - Fetch each `watch_url` with `web_fetch`; run a `web_search` for the45 competitor name plus its `keywords` and recent dates.46 - For a deeper periodic pass (e.g. monthly, or when the user asks for depth),47 escalate to the **deep-research** harness instead of light fetches. Keep the48 weekly run light and cheap.493. **Cheap change pre-filter.** For each fetched page, hash the text and compare50 to the stored source hash to skip pages that did not change at all:5152 ```bash53 # prints the sha256 of normalized stdin text54 echo "$PAGE_TEXT" | bun "$SKILL_DIR/scripts/page-hash.ts"55 ```5657 A matching hash means the page is byte-stable — skip deep analysis of it.58 A new or changed hash means read it closely. The hash is only a pre-filter;59 **you decide what is material.**604. **Judge materiality.** Compare new signal against the prior findings. Surface61 only genuine changes: new/changed pricing, product launches or major62 changelog entries, funding or M&A, notable hiring (esp. leadership), and63 positioning/messaging shifts. Ignore cosmetic edits and re-phrasings.6465 **Suggest a next move (don't take it).** A finding that just sits there is half66 the value. For each material finding, decide whether it warrants a concrete67 response — and if so, *prepare* it, never transmit it:68 - A competitor pricing or positioning shift sales should know → a **draft**69 note to the sales/team channel (e.g. a `slack` draft), handed over for the70 user to send.71 - A launch or capability worth a closer look → propose a deeper `deep-research`72 pass, or a calendar hold to dig in.73 - Something worth keeping in the user's own context → a one-line `remember`.7475 Only attach an action when there's a real one; most findings are just signal.76 No action ever leaves the building on its own — every note is a draft the user77 sends, consistent with the no-send rule below.785. **Write the new snapshot.** `save_competitor_snapshot` with the full object so79 next run can diff against it:8081 ```json82 {83 "competitor": "Example Co",84 "lastRunAt": "<ISO timestamp>",85 "sources": [86 { "url": "https://example.com/pricing", "contentHash": "<sha256>", "fetchedAt": "<ISO>", "summary": "1-line state" }87 ],88 "findings": [89 { "title": "Launched X tier", "detail": "...", "category": "launch", "sourceUrl": "https://...", "firstSeenAt": "<ISO>" }90 ]91 }92 ```9394 Use the system clock for timestamps (`date -u +%Y-%m-%dT%H:%M:%SZ`).956. **Deliver the brief.** Group by competitor; lead with the most material item;96 one line each, every claim linked to its source. Where a finding earned a97 suggested move in step 4, append it on its own line as an offer the user can98 accept by name ("draft a heads-up to #sales", "dig into their new tier") — the99 prepared draft or proposal, never an action already taken. Respect the100 configured delivery channel (`admin_copilot_prefs` `get_prefs` →101 `competitorBrief.channel`): in-app means reply directly; for slack/email, hand102 off to the relevant channel (the `notifications` or messaging skill).103104## Empty weeks105106If nothing material changed, **still report** — one line: "No material competitor107changes this week (checked N competitors)." Silence reads as a broken job and108erodes trust. Always save the refreshed snapshot even when findings are empty so109hashes stay current.110111## Rules112113- Only changes worth a busy person's attention. When in doubt, leave it out.114- Every finding cites a source URL. No source, no finding.115- This skill never sends email or messages on its own — delivery to slack/email116 goes through the dedicated channel skills, which carry their own confirmation.117- Keep weekly runs cheap; reserve deep-research for explicit deep passes.