From a8e3cacee3cc99e7ea2120a08af516ba02542c40 Mon Sep 17 00:00:00 2001 From: Kjell Tore Guttormsen Date: Fri, 17 Jul 2026 04:00:17 +0200 Subject: [PATCH] =?UTF-8?q?feat(linkedin-studio):=20N5=20=E2=80=94=20/link?= =?UTF-8?q?edin:trends=20discovery-kommando=20+=20trend-spotter=20pin-fjer?= =?UTF-8?q?ning=20+=20triage=20[skip-docs]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 Claude-Session: b69d02cd-d30d-478b-95a5-bd06113c648a --- CLAUDE.md | 9 +- README.md | 8 +- agents/trend-spotter.md | 1 - commands/linkedin.md | 2 + commands/trends.md | 138 +++++++++++++++++++++++++++++ docs/agents-capability-matrix.md | 2 +- scripts/test-runner.sh | 2 +- skills/linkedin-analytics/SKILL.md | 2 +- skills/linkedin-strategy/SKILL.md | 2 +- skills/linkedin-studio/SKILL.md | 2 +- 10 files changed, 154 insertions(+), 14 deletions(-) create mode 100644 commands/trends.md diff --git a/CLAUDE.md b/CLAUDE.md index 27dfc82..5a015bc 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,6 +1,6 @@ # LinkedIn Studio Plugin (v0.6.0) -Full-spectrum LinkedIn content engine — short-form feed posts, carousels, video scripts, and long-form newsletter editions — with the 2026 relevance-ranking model baked in. Maturity v0.6.0: M0 (per-user data dir `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/`, idempotent session-start migration — see `references/data-path-convention.md`) complete; all 29 command surfaces through the interactive quality-gate (`docs/hardening/log.md`) AND through independent cold-review (29/29, R2a–R5 — `docs/hardening/review*.md`). Remaining for v1.0.0: a GUI. Version history → `CHANGELOG.md`. +Full-spectrum LinkedIn content engine — short-form feed posts, carousels, video scripts, and long-form newsletter editions — with the 2026 relevance-ranking model baked in. Maturity v0.6.0: M0 (per-user data dir `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/`, idempotent session-start migration — see `references/data-path-convention.md`) complete; the 29 pre-0.7.0 command surfaces all through the interactive quality-gate (`docs/hardening/log.md`) AND through independent cold-review (29/29, R2a–R5 — `docs/hardening/review*.md`); `/linkedin:trends` (new, 0.7.0-dev) is not yet gated. Remaining for v1.0.0: a GUI. Version history → `CHANGELOG.md`. ## Architecture @@ -35,9 +35,9 @@ Full-spectrum LinkedIn content engine — short-form feed posts, carousels, vide **Hook editing:** edit `hooks/hooks.template.json` + `hooks/prompts/*.md`, then run `python3 hooks/scripts/compile-hooks.py`. Do not edit `hooks.json` directly. Prompts load at runtime via gatekeeper scripts; the compile step is only needed when adding `type: prompt` hooks. -## Commands (29) +## Commands (30) -All content commands auto-copy output to clipboard via `clipboard-helper.mjs`; interactive steps are minimized (angle/format/post-type inferred from context, max 2 questions per post). Organized into five journeys (Start · Create · Engage · Measure · Grow): `create`/`measure` are guided creation/analytics front-doors, `onboarding`/`strategy` the Start/Grow front-doors, and the 27 atomic commands are the execution tier. +All content commands auto-copy output to clipboard via `clipboard-helper.mjs`; interactive steps are minimized (angle/format/post-type inferred from context, max 2 questions per post). Organized into five journeys (Start · Create · Engage · Measure · Grow): `create`/`measure` are guided creation/analytics front-doors, `onboarding`/`strategy` the Start/Grow front-doors, and the 28 atomic commands are the execution tier. | Command | Purpose | |---------|---------| @@ -51,6 +51,7 @@ All content commands auto-copy output to clipboard via `clipboard-helper.mjs`; i | `/linkedin:post` | Full post creation (10-15 min) | | `/linkedin:quick` | 5-minute quick post (3-line formula) + 8 post-type templates | | `/linkedin:pipeline` | Full end-to-end content pipeline | +| `/linkedin:trends` | Trend discovery pass — delegates to `trend-spotter` (default long-form mode), persists to trend store + dated morning brief, triage per id (act/skip) | | `/linkedin:newsletter` | Long-form orchestrator (newsletter / essay / series article) — multi-session 18-phase pipeline with gates before prose (skeleton + spine), fact-check, editorial-review (Step 5.5), persona sweep, headless adversarial review (Step 6.5), and visual-assets — all before lock | | `/linkedin:headless-review` | Cold adversarial review package — 3 headless archetypes (`content-reviewer`, `language-reviewer`, `fact-reviewer`) + `persona-reviewer` on a FROZEN draft, no drafting-session context; operator-gated report (Step 6.5 standalone surface) | | `/linkedin:pivot` | Re-open a long-form edition after a late substantive change so cleared gates re-run before lock (pivot heuristic: >20% word-count or >2 new sections) | @@ -82,7 +83,7 @@ All content commands auto-copy output to clipboard via `clipboard-helper.mjs`; i | `content-planner` | Sonnet | Cyan | Content audit + weekly/monthly plans | | `network-builder` | Sonnet | Teal | Strategic networking + outreach | | `content-repurposer` | Sonnet | Purple | Format conversion + evergreen refresh | -| `trend-spotter` | Sonnet | White | Trending topics + opportunity scores | +| `trend-spotter` | (inherits session) | White | Trending topics + opportunity scores | | `voice-trainer` | Sonnet | Pink | Voice profile building + drift detection | | `differentiation-checker` | Sonnet | Gray | Originality scoring + commodity detection | | `post-feedback-monitor` | Opus | Lime | Post-publish 48h monitoring | diff --git a/README.md b/README.md index ddb7d4a..a6c1a4d 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ ![Version](https://img.shields.io/badge/version-0.6.0-blue) ![Platform](https://img.shields.io/badge/platform-Claude_Code_Plugin-purple) -![Commands](https://img.shields.io/badge/commands-29-green) +![Commands](https://img.shields.io/badge/commands-30-green) ![Agents](https://img.shields.io/badge/agents-19-orange) ![Hooks](https://img.shields.io/badge/hooks-9-red) ![Reference Docs](https://img.shields.io/badge/reference_docs-28-teal) @@ -22,7 +22,7 @@ This is not a shortcut. Hand the wheel to the AI and you land where everyone who > New here? Run `/linkedin:onboarding` — it walks you through profile optimization, personalization, and your first published post in one guided flow (~10 minutes). > [!NOTE] -> **Pre-1.0.** The earlier 1.0.0–4.1.0 numbering reflected ambition, not maturity. Honest about where it stands today: the **architecture workstream (M0) is done** — user data lives in a per-user data dir *outside* the plugin, with automatic migration — and all **29 command surfaces have passed both the interactive hardening gate (29/29) and independent cold-review (29/29)**. What remains for 1.0.0 is a GUI. See [CHANGELOG.md](CHANGELOG.md). +> **Pre-1.0.** The earlier 1.0.0–4.1.0 numbering reflected ambition, not maturity. Honest about where it stands today: the **architecture workstream (M0) is done** — user data lives in a per-user data dir *outside* the plugin, with automatic migration — and the **29 pre-0.7.0 command surfaces have all passed both the interactive hardening gate (29/29) and independent cold-review (29/29)** (`/linkedin:trends`, new in 0.7.0-dev, is not yet gated). What remains for 1.0.0 is a GUI. See [CHANGELOG.md](CHANGELOG.md). --- @@ -108,7 +108,7 @@ Run the onboarding wizard — it walks you through profile, setup, and your firs ## Commands -All 29 commands use colon notation: `/linkedin:post`, `/linkedin:quick`, etc. The surface is organized into five journeys (Start · Create · Engage · Measure · Grow); `/linkedin:create` and `/linkedin:measure` are guided front-doors that route you to the right command when you know the journey but not the exact command. Run `/linkedin` for the live router with your posting status. +All 30 commands use colon notation: `/linkedin:post`, `/linkedin:quick`, etc. The surface is organized into five journeys (Start · Create · Engage · Measure · Grow); `/linkedin:create` and `/linkedin:measure` are guided front-doors that route you to the right command when you know the journey but not the exact command. Run `/linkedin` for the live router with your posting status. ### Onboarding & Setup @@ -179,7 +179,7 @@ All 29 commands use colon notation: `/linkedin:post`, `/linkedin:quick`, etc. Th | `content-planner` | Sonnet | Weekly/monthly content calendars | | `network-builder` | Sonnet | Strategic networking + outreach | | `content-repurposer` | Sonnet | Format conversion + evergreen refresh | -| `trend-spotter` | Sonnet | Trending topics + opportunity scores | +| `trend-spotter` | (inherits session) | Trending topics + opportunity scores | | `voice-trainer` | Sonnet | Voice profile building + drift detection | | `differentiation-checker` | Sonnet | Originality scoring + commodity detection | | `video-scripter` | Sonnet | Video scripts with pacing + visual cues | diff --git a/agents/trend-spotter.md b/agents/trend-spotter.md index b44a872..83d2c8a 100644 --- a/agents/trend-spotter.md +++ b/agents/trend-spotter.md @@ -14,7 +14,6 @@ description: | Triggers on: "trending", "what should I post about", "scan for trends", "content opportunities", "trend digest", "what's new in my space", "timely topic", "first-mover", "opportunity scan". -model: sonnet color: white # No `tools:` allowlist by design (research-engine slice 2b). An explicit allowlist would # block every research MCP unless its `mcp____` name were hardcoded here — diff --git a/commands/linkedin.md b/commands/linkedin.md index cce8f30..2348af3 100644 --- a/commands/linkedin.md +++ b/commands/linkedin.md @@ -84,6 +84,7 @@ directly when you do. | `/linkedin:multiplatform` | Adapt content for Twitter/X, slides, YouTube (long-form → newsletter) | | `/linkedin:batch` | Create a full week of content in one session | | `/linkedin:pipeline` | End-to-end single-post workflow (idea → draft → schedule → analyze) | +| `/linkedin:trends` | Trend discovery pass — scan your sources, persist candidates + morning brief, triage per id | | `/linkedin:newsletter` | **Long-form spine.** Newsletter editions, essays, series articles. The single long-form entry point | | `/linkedin:headless-review` | Cold adversarial re-read of a FROZEN long-form draft before lock (ideally in a fresh session) | | `/linkedin:pivot` | Re-open a long-form edition after a late change so cleared gates re-run | @@ -161,6 +162,7 @@ If the user's intent is clear from context: - Mentions "react" or "this article" or "this url" or "turn this into" or "share this news" → Route to `/linkedin:react` - Mentions "quick" or "fast" → Route to `/linkedin:quick` - Mentions "pipeline" or "end to end" → Route to `/linkedin:pipeline` +- Mentions "trends" or "trending" or "discovery pass" or "morning brief" or "what should I write about" → Route to `/linkedin:trends` - Mentions "batch" or "week of content" → Route to `/linkedin:batch` - Mentions "calendar" or "schedule" or "queue" or "upcoming posts" or "what's scheduled" → Route to `/linkedin:calendar` - Mentions "publish" or "mark as published" or "posted today" or "just published" or "post is live" → Route to `/linkedin:calendar` (publish action) diff --git a/commands/trends.md b/commands/trends.md new file mode 100644 index 0000000..008bf92 --- /dev/null +++ b/commands/trends.md @@ -0,0 +1,138 @@ +--- +name: linkedin:trends +description: | + Run a trend discovery pass over the user's own content pillars and source list: + delegate the scan to the trend-spotter agent, make sure kept candidates are persisted + to the trend store (dedup) and the dated morning brief is written, then return a + triage-ranked candidate list the user resolves per id (act/skip). Default scoring mode + is long-form (chronicle/newsletter material); `--mode kortform` overrides for feed posts. + Use when the user wants a discovery pass, a trend scan, or a morning-brief refresh. + Triggers on: "linkedin trends", "trend discovery", "discovery pass", "run a trend scan", + "scan my sources", "morning brief", "refresh the brief", "trend sweep". +allowed-tools: + - Read + - Bash + - Task + - AskUserQuestion +--- + +# LinkedIn Trend Discovery + +You are a thin discovery orchestrator. The methodology — source tiers, research routing +(MCP-first), relevance scoring, angle selection — lives in the `trend-spotter` agent and in +the scoring SSOT `${CLAUDE_PLUGIN_ROOT}/references/trend-scoring-modes.md`. Do not restate +any of it here; your job is to invoke the pass correctly, verify its side effects actually +happened, and hand the user a triage-ready list. + +Data dir shorthand used below: `${DATA}` = `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}`. +Trends CLI shorthand: `CLI` = `cd "${CLAUDE_PLUGIN_ROOT}/scripts/trends" && node --import tsx src/cli.ts`. + +## Step 0: Parse flags + +All flags are optional, given after the command name: + +| Flag | Meaning | Default | +|------|---------|---------| +| `--mode kortform\|long-form` | Scoring mode (see the SSOT for what each rewards) | **long-form** — no arguments means a long-form discovery pass | +| `--fresh-days N` | Freshness window for the morning brief | CLI default (7) | +| `--brief-only` | Skip the discovery poll entirely; render the brief from the existing store | off | +| `--dry-run` | Poll + score, but persist nothing: no capture, no brief, no status writes, no last-run marker | off | + +Note the mode inversion deliberately: the **agent's** own default is kortform, this +**command's** default is long-form. That is why Step 2 must always pass the mode explicitly. + +## Step 1: Load context + +1. **Pillars:** Read `${DATA}/profile/user-profile.md` and extract the content pillars / + expertise areas. If the file does not exist, ask the user for their pillars before + proceeding (one question, comma-separated answer). +2. **Source list:** Resolve which list this pass will use — `${DATA}/trends/sources.md` if it + exists, otherwise the shipped defaults `${CLAUDE_PLUGIN_ROOT}/config/trends-sources.template.md`. + Tell the user which one applies. Do not read research-tooling or route research yourself — + the agent owns Research Routing (its own "Research Routing" section reads the profile's + `### Research Tooling` block); duplicating it here would drift. + +## Step 2: Run the discovery pass + +**If `--brief-only`:** skip the agent entirely — go to Step 3 and render the brief from the +existing store. + +Otherwise delegate to the trend-spotter agent — invoke it via `Task` with +`subagent_type: linkedin-studio:trend-spotter` (foreground). The prompt MUST state explicitly: + +- **The scoring mode** from Step 0 (default long-form). Never omit it — the agent falls back + to kortform when the caller is silent. +- The pillars and the resolved source-list path from Step 1. +- That this is a full digest run and the persistence steps are **mandatory, not optional**: + the agent must run its Step 4.5 (`capture` — persist kept candidates to the trend store, + batch, dedup, scores carried) and Step 4.6 (`brief` — write the dated morning brief, + passing the pillars, and `--fresh-days` if the user set it). +- If `--dry-run`: invert that — the agent must poll and score but **skip** capture and brief + entirely (nothing persisted). +- That the returned digest must include, per kept candidate: title, 2–3 sentence summary, + source URL(s), composite score + band, recommended angle, and matching pillar/series. + +## Step 3: Verify the side effects (skip on `--dry-run`) + +Trust but verify — the pass is only done when its artifacts exist: + +1. **Store:** `CLI status --json` — confirm the store mutated (captured count reflects the + run; on a no-new-trends day `{added: 0, merged: N}` is a fine outcome, not a failure). +2. **Brief:** confirm today's file exists: `${DATA}/trends/morning-brief/.md`. + If the agent captured but failed to render the brief, render it directly — the brief is + deterministic: `CLI brief --pillars ""` (add `--fresh-days N` if set). +3. If capture itself did not happen, say so plainly and report what the agent returned — + never present an unpersisted digest as if it were in the store. + +## Step 4: Present the triage-ranked list + +Present the candidates ranked highest composite first (the agent's digest already carries the +ranking — do not re-rank). Per candidate, the contract is: + +``` +N. [Title] (id: ) + Score: X.X — [Band] | Pillar: [pillar/series] + [2–3 sentence summary] + Source: [URL(s)] + Angle: [recommended angle] +``` + +Include each candidate's store id (shown in the brief and via `CLI list --json`) — the triage +step below resolves per id. On `--dry-run`, present the same list but say clearly that nothing +was persisted and there are no store ids to triage. + +## Step 5: Triage (skip on `--dry-run`) + +Resolve the top of the queue now instead of leaving it as homework. For the candidates in the +top bands (Immediate + High; cap at 8), use AskUserQuestion — one question per candidate, up +to 4 candidates per call, options: + +- **Act** — writing about it now/soon: mark handled so the brief stops re-surfacing it +- **Skip** — not for me: same effect, opposite verdict +- **Leave** — keep it in the queue untouched + +Then apply each decision through the store CLI, one call per resolved id: + +```bash +CLI act --id # or: skip --id +``` + +"Leave" means no call. Finish with a one-line summary: N acted, N skipped, N left in queue. +(The CLI currently takes one `--id` per call; a `selected` status and `--ids` batching are +planned upgrades — keep decisions per-id so this step absorbs them without contract change.) + +## Step 6: Write the last-run marker (skip on `--dry-run`) + +On completed runs (including `--brief-only`), stamp the marker so other surfaces can tell when +discovery last ran: + +```bash +mkdir -p "${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/trends" && \ + date -u +"%Y-%m-%dT%H:%M:%SZ" > "${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/trends/.last-run" +``` + +## Reference Files + +- `${CLAUDE_PLUGIN_ROOT}/references/trend-scoring-modes.md` — scoring SSOT (modes, weights, bands) +- `${CLAUDE_PLUGIN_ROOT}/config/trends-sources.template.md` — shipped source-list defaults (user override: `${DATA}/trends/sources.md`) +- `${CLAUDE_PLUGIN_ROOT}/agents/trend-spotter.md` — the discovery methodology this command invokes diff --git a/docs/agents-capability-matrix.md b/docs/agents-capability-matrix.md index 940e176..1417a98 100644 --- a/docs/agents-capability-matrix.md +++ b/docs/agents-capability-matrix.md @@ -13,7 +13,7 @@ | content-planner | Sonnet | Cyan | Weekly/monthly content calendars | | network-builder | Sonnet | Teal | Strategic networking and outreach | | content-repurposer | Sonnet | Purple | Format conversion and evergreen refresh | -| trend-spotter | Sonnet | White | Trending topics and opportunity scoring | +| trend-spotter | (inherits session) | White | Trending topics and opportunity scoring | | voice-trainer | Sonnet | Pink | Voice profile building and drift detection | | differentiation-checker | Sonnet | Gray | Originality scoring and commodity detection | | video-scripter | Sonnet | Violet | Video script creation with pacing and visual cues | diff --git a/scripts/test-runner.sh b/scripts/test-runner.sh index 7de38c3..ce7f2eb 100755 --- a/scripts/test-runner.sh +++ b/scripts/test-runner.sh @@ -91,7 +91,7 @@ warn() { echo -e "${YELLOW}⚠${NC} $1"; WARN=$((WARN + 1)); } # Source of truth: CLAUDE.md headers + STATE.md Telling. Bump these together # with the files when adding/removing an agent, command, reference, or skill. EXPECT_AGENTS=19 -EXPECT_COMMANDS=29 +EXPECT_COMMANDS=30 EXPECT_REFS=28 EXPECT_SKILLS=6 # Pre-M0 references/ baseline was 25. Every ref doc added since is NAMED below, so the diff --git a/skills/linkedin-analytics/SKILL.md b/skills/linkedin-analytics/SKILL.md index 3574699..5e36afb 100644 --- a/skills/linkedin-analytics/SKILL.md +++ b/skills/linkedin-analytics/SKILL.md @@ -37,7 +37,7 @@ This skill covers everything related to LinkedIn analytics, performance measurem | Agent | Model | Responsibility | |-------|-------|----------------| | `analytics-interpreter` | Sonnet | Audience pattern analysis + weekly/monthly performance reports (interpret/report modes) | -| `trend-spotter` | Sonnet | Trending topics + opportunity scores | +| `trend-spotter` | (inherits session) | Trending topics + opportunity scores | | `post-feedback-monitor` | Opus | Post-publish 48h monitoring, anomaly detection | --- diff --git a/skills/linkedin-strategy/SKILL.md b/skills/linkedin-strategy/SKILL.md index dbf0111..b96c70a 100644 --- a/skills/linkedin-strategy/SKILL.md +++ b/skills/linkedin-strategy/SKILL.md @@ -33,7 +33,7 @@ This skill covers long-term LinkedIn strategy, authority building, competitive i | Agent | Model | Responsibility | |-------|-------|----------------| | `strategy-advisor` | Sonnet | Growth recommendations based on phase | -| `trend-spotter` | Sonnet | Trending topics + opportunity scores | +| `trend-spotter` | (inherits session) | Trending topics + opportunity scores | --- diff --git a/skills/linkedin-studio/SKILL.md b/skills/linkedin-studio/SKILL.md index efec389..3f7c56d 100644 --- a/skills/linkedin-studio/SKILL.md +++ b/skills/linkedin-studio/SKILL.md @@ -153,7 +153,7 @@ These rules apply to ALL content created by any skill or command: | `content-planner` | Sonnet | Cyan | Content audit + weekly/monthly plans | | `network-builder` | Sonnet | Cyan | Strategic networking + outreach | | `content-repurposer` | Sonnet | Magenta | Format conversion + evergreen refresh | -| `trend-spotter` | Sonnet | Cyan | Trending topics + opportunity scores | +| `trend-spotter` | (inherits session) | Cyan | Trending topics + opportunity scores | | `voice-trainer` | Sonnet | Magenta | Voice profile building + drift detection | | `differentiation-checker` | Sonnet | Blue | Originality scoring + commodity detection | | `post-feedback-monitor` | Opus | Lime | Post-publish 48h monitoring, real-time interventions |