# Plugin Update

> Bring an existing brand's setup up to date with the latest plugin version — detects gaps since the user last ran brand-setup and fills them interactively.

- Skill: `fivebucksventures/plugin-update` (Agent Skill)
- Install (CLI): `npx skillmds@latest add fivebucksventures/plugin-update`
- Raw SKILL.md: https://api.skillmd.com/api/skills/fivebucksventures/plugin-update/raw
- Safety review: PASS (external: skill-scanner PASS, skillspector FAIL)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: fivebucksventures (https://skillmd.com/u/fivebucksventures)
- Updated: 2026-08-19
- Page: https://skillmd.com/skills/fivebucksventures/plugin-update

---


## Maintenance

| Agent | Version | Last Changed |
|---|---|---|
| Link | v2.20.1 | July 13, 2026 |

**Description:** Bring an existing brand's setup up to date with the latest plugin version — detects gaps since the user last ran brand-setup and fills them interactively

### Change Log

**v2.20.1** — July 13, 2026
- **Corrected the fb.ai tool count referenced in the key-scope check messaging (98 → 100), matching the same correction made across the other v2.20.0 skills.**

**v2.20.0** — July 12, 2026
- **fb.ai key scope check for existing brands.** Repointed the stale api-keys links to `/dashboard/api-keys` and added a `fivebucks_whoami` probe: keys generated before scoping are **legacy full-access keys and keep working** (empty `scopes` — no action needed), but a regenerated key missing `social_posts` will silently break the fb.ai-dependent skills. Reports the granted scopes + quota snapshot and surfaces `auth/key-expired`.

**v2.19.0** — July 03, 2026
- **Corrected Zernio ad-account discovery tool name (fixes the v2.18.0 migration bug).** v2.18.0 assumed Zernio just drops the `late_` prefix, but Zernio's tools are resource-prefixed. Step 2/Step 3 ad-account resolution now calls **`ads_list_ad_accounts`** (was `list_ad_accounts`). `profiles_list` / `accounts_list` were already correct.

**v2.18.0** — July 01, 2026
- **Migration support for the Zernio + DataforSEO gateway split-off (gateway v1.7.4/v1.7.5) and Argil retirement.** Step 1e adds Zernio (`profiles_list` — success = authorized) and DataforSEO (`keywords_data_google_ads_search_volume`, optional) connector probes. Step 3f renames every `${BRAND}_LATE_*` env var → `${BRAND}_ZERNIO_*`, removes the now-defunct `LATE_API_KEY` (Zernio is OAuth — no key), and updates the auto-discover tool names (`profiles_list` / `accounts_list` / `list_ad_accounts`, no longer gateway-routed). Step 3g adds a **Zernio (required, OAuth)** connector walkthrough and a **DataforSEO (optional, Basic Auth)** connector walkthrough. Step 3k adds a brand-action row mapping this release — reconnect Zernio + DataforSEO as their own custom connectors and re-run account discovery, since Zernio's own API may return different SocialAccount `_id`s than the old gateway-proxied Late API.

**v2.17.0** — June 21, 2026
- **Migration support for the Inbound Gig Engine completion (v2.17.0 Sales skills `gig-proposal-writer` / `n8n-workflow-builder` / `vsl-demo-producer`).** Step 1e adds an **n8n Cloud MCP** probe (`get_workflow_best_practices`) — optional, only needed by `n8n-workflow-builder`. Step 3g adds the n8n Cloud connect-walkthrough prompt. Step 3k maps the new `Inbound Gig Engine completed` changelog entry to its optional brand action (connect n8n Cloud + optional `${BRAND}_N8N_PROJECT`). The three skills are auto-detected from the shipped `skills-manifest.json` (Step 1 registry read); no per-brand context file is required beyond what `gig-prospector` already needs.

# Plugin Update — Catch Existing Brands Up to Latest Plugin Version

## Before Executing

Read `agents/link.md` before starting. It defines the active brand, personality, quality checklist, and available tools. Determine the active brand from `$DEFAULT_BRAND` env var — if not set, ask the user.

You are the upgrade agent for the Link marketing plugin. The user has already run `brand-setup` at some earlier plugin version, and the plugin has since added new requirements (mandatory files, env vars, MCPs, CLAUDE.md sections, etc.). Your job is to **detect what's missing** in the current brand setup and walk the user through filling **only those gaps** — never re-run steps that are already complete.

This skill is **idempotent**. Running it twice in a row should produce a clean "nothing to do" report on the second pass.

## When to invoke

- After upgrading to a new plugin version (e.g. `git pull` in the marketplace folder)
- When a brand was set up months ago and skills are throwing "missing file / missing env var" errors
- User says "update", "upgrade", "catch up", "what's missing", "plugin-update"

## How this differs from brand-setup

| Aspect | brand-setup | plugin-update |
|---|---|---|
| When | First time, new brand | Existing brand, after plugin upgrade |
| Greenfield | Yes — writes everything | No — only fills gaps |
| Order | Linear, all 10 steps | Skips anything already complete |
| Re-asks for known data | Yes | No — reuses what's on disk |

If `brands/{brand}/` does not exist at all, this skill exits and tells the user to run `/link-skills:brand-setup` first.

## What this skill does

When you upgrade the plugin (e.g. `git pull` in the marketplace folder), some skills will have new requirements — new brand context files, new env vars, new MCP integrations, new sections in `brand.md`. Plugin-update is the **idempotent catch-up runner**: it inspects every brand asset, env var, MCP probe, and `CLAUDE.md` setting against the current plugin's expectations, then walks you through filling **only the gaps**. It never re-asks for data that's already on disk.

**Estimated time:** 5–30 minutes for a typical run. A clean brand (no gaps) finishes in ~5 minutes (mostly automated inspection). A heavily-outdated brand (e.g. last set up 6 months ago, missing the v2.4.0 sales/finance/customer-success files) can take 30–60 minutes if you choose to fill all the new context files in this session.

**The 6 steps:**

| # | Step | Purpose | Time |
|---|---|---|---|
| 0 | Determine version gap | Read current plugin version + brand's last-applied version; build the changeset that needs catching up | ~30 sec (auto) |
| 1 | Detect current state | Inspect every brand file, env var, MCP connector, CLAUDE.md section — no user prompts, just look | 1–2 min (auto) |
| 2 | Show the gap report | Compact summary of what's present / missing / optional, grouped by category. One question: "want to fill these now?" | 1 min |
| 3 | Fill the gaps interactively | Walk through each missing item in order. Each fill delegates to the matching brand-setup sub-step | 2–60 min (variable) |
| 4 | Re-validate | Test only the integrations touched in Step 3 to confirm fixes worked | 1–2 min |
| 5 | Record version + email + Slack | Write current plugin version to `brand.md`, send completion email, DM Slack | 30 sec |

The skill is **idempotent** by design — running it twice in a row should produce a clean "nothing to do" report on the second pass. You can pause anywhere (e.g. say "skip" to a gap you don't want to fill now) and re-run later to pick up where you left off.

---

## Step 0 — Determine version gap

First the agent figures out **what changed** between the version your brand was set up at and the version you have installed now. It reads `version.ts` for the current installed version, looks at `brands/{brand}/brand.md` for the brand's last-applied version stamp, and reads every skill's maintenance section to build a per-skill version delta. The output is a list of what's new since the brand was last configured — the input that drives Step 3's brand-action mapping.

**Expect ~30 seconds.** Fully automated. Only asks you a question if the brand's last-applied version is missing from `brand.md` (in which case it asks you to recall when the brand was last set up).

### 0a. Read current plugin version

Open `plugins/link-skills/versions/version.ts` and read `DEFAULT_VERSION` (e.g. `v2.2.15`). This is the version the user has installed right now.

### 0b. Read last-applied version for this brand

Open `brands/{brand}/brand.md` and look for a `## Plugin Version` section:
- If present → `last_applied = that version string`
- If missing → ask the user: "What version were you on when you last ran brand-setup or plugin-update? (Run `git log --oneline -5` in this folder if unsure.)"
- If unknown → treat `last_applied = v2.0.0` (full audit)

### 0c. Read skill and agent maintenance sections

For each of the following files, read the `## Maintenance` block and extract the `Version` value from the table:

| File | Extract |
|---|---|
| `plugins/link-skills/agents/link.md` | Version |
| `plugins/link-skills/skills/brand-setup/SKILL.md` | Version |
| `plugins/link-skills/skills/content-generator/SKILL.md` | Version |
| `plugins/link-skills/skills/creative-designer/SKILL.md` | Version |
| `plugins/link-skills/skills/content-creation/SKILL.md` | Version |
| `plugins/link-skills/skills/social-calendar/SKILL.md` | Version |
| `plugins/link-skills/skills/digital-marketing-analyst/SKILL.md` | Version |
| `plugins/link-skills/skills/data-analysis/SKILL.md` | Version |
| `plugins/link-skills/skills/social-publisher/SKILL.md` | Version |
| `plugins/link-skills/skills/research-strategy/SKILL.md` | Version |
| `plugins/link-skills/skills/campaign-presenter/SKILL.md` | Version |
| `plugins/link-skills/skills/background-generator/SKILL.md` | Version |
| `plugins/link-skills/skills/plugin-update/SKILL.md` | Version |

### 0d. Build the version delta

Flag each skill/agent whose maintenance `Version` is **newer than `last_applied`**. These are the files that changed since the user's brand was last configured. Also flag any skill/agent that is **new** (did not exist at `last_applied` — compare the file list on disk against what version.ts history shows was introduced).

---

## Step 1 — Detect current state

Now the agent does a **read-only inspection sweep** of everything related to this brand — folder contents, brand.md sections, env vars, MCP connectors, CLAUDE.md, Notion DBs, template uploads, skill versions. No questions yet, no fixes — just look. Each sub-step (1a–1j) checks one category and tags items as present / missing / drift. The output of this step is the gap report you'll see in Step 2.

**Expect 1–2 minutes.** Mostly automated; a few MCP probes will hit external services (e.g. Notion, Apollo, Xero) which can be slow if connections are flaky.

Read everything that exists for the active brand. Do **not** prompt the user yet — just inspect.

### 1a. Brand folder contents

Check existence of each path under `brands/{brand}/`. Mark present/missing:

| Path | Required since | Status |
|---|---|---|
| `brand.md` | v2.0 | present / missing |
| `product.md` | v2.0 | present / missing |
| `audience.md` | v2.0 | present / missing |
| `competitors.md` | v2.0 | present / missing |
| `funnel.md` | v2.1.0 | present / missing |
| `avatars.md` | v2.1.0 | present / missing |
| `logo.png` | v2.0 | present / missing |
| `backgrounds/` | v2.0 (now empty by design) | present / missing |
| `design-system/` | v2.2.10 (optional — brand.md fallback since v2.2.15) | present / missing |
| `sales.md` | v2.4.0 | present / missing |
| `customer-success.md` | v2.4.0 | present / missing |
| `finance.md` | v2.4.0 | present / missing |
| `investors.md` | v2.4.0 (optional — only required if brand has raised external capital) | present / missing |
| `operations.md` | v2.4.0 (optional — meeting-analyzer falls back gracefully) | present / missing |

**Inline schema check — sales.md Booking URL.** If `sales.md` is present, open it and verify the `## Sender Persona` block contains a non-empty `Booking URL:` line. If missing → flag as ⚠ schema gap and route to `brand-setup` Step 5g Step A backfill (single-question fix — only the Booking URL is asked, no need to re-walk the rest of the sub-step). The `outreach-sequencer` standing-link fallback, the `customer-onboarder` kickoff fallback, and the `{link}` placeholder in Reply Routing all depend on this field; brands set up before it was introduced will silently use empty CTAs until backfilled.

**Inline schema check — sales.md Inbound Job Filters (v2.16.0).** If `sales.md` is present, check for a `## Inbound Job Filters` section. If missing → flag as ⏭ optional schema gap and route to the Step 3a targeted backfill (`brand-setup` Step 5g Step H — markets, platforms + account status, product.md-derived search keywords, budget/exclusions/cap). This section powers `gig-prospector`; without it that one skill can't run, but no existing skill breaks — so it's optional, not a required gap. Skip silently for brands that don't pursue freelance/marketplace work.

### 1b. brand.md sections

Read `brands/{brand}/brand.md` and check for these section headers (added in different versions):

| Section | Required since | Status |
|---|---|---|
| `## Tagline` | v2.0 | present / missing |
| `## Voice & Tone` | v2.0 | present / missing |
| `## Colors (HEX codes)` | v2.0 | present / missing |
| `## Locale` (Currency / Timezone / Meta USD exchange rate) | v2.2.0 | present / missing |
| `## Approved Phrases` | v2.0 | present / missing |
| `## Do NOT Say` | v2.0 | present / missing |
| `## Social Publishing` (Zernio profile + connected platforms) | v2.2.1 | present / missing |
| `## Content Strategy` (Primary channel + distribution platforms + cadence) | v2.14.0 | present / missing |

### 1c. funnel.md schema

Open `brands/{brand}/funnel.md` if present. Confirm it has:
- `## Stages (Google Ads)` table with GA4 event mappings
- `## Stages (Meta Ads)` table
- `## Cost Benchmarks` table
- No remaining `TBD` event names (if any → flag for re-mapping)

### 1d. Env vars in `.claude/settings.local.json`

Read `.claude/settings.local.json` (search up from cwd). Check the `env` block:

**Required:**
| Env var | Required since |
|---|---|
| `FIVEAGENTS_API_KEY` | v2.0 |
| `GEMINI_API_KEY` | v2.0 |
| `SLACK_NOTIFY_USER` | v2.0 |
| `REPORT_EMAIL` | v2.0 |
| `{BRAND}_ZERNIO_FB` / `_IG` / `_LI` | v2.2.1 |
| `{BRAND}_ZERNIO_TT` / `_TW` (TikTok / Twitter-X organic — `video-repurposer`, YouTube-First) | v2.14.0 |
| `{BRAND}_NOTION_DB` | v2.2.10 (social-calendar bootstraps if missing) |

> **Note:** Zernio (formerly Late) moved to its own OAuth MCP in gateway v1.7.4 — there is no longer a `LATE_API_KEY` env var. Authorization is handled by connecting the Zernio custom connector (Step 3g), not by saving a key.

**Optional:**
| Env var | Notes |
|---|---|
| `DATAFORSEO_LOGIN` / `DATAFORSEO_PASSWORD` | Keyword research |
| `DEFAULT_BRAND` | Workspace default brand |
| `{BRAND}_ZERNIO_GOOGLE_ADS` (Zernio SocialAccount `_id`) + `{BRAND}_ZERNIO_GOOGLE_ADS_CID` (Google Ads customer ID, 10-digit) | Both required for the Windsor.ai fallback in digital-marketing-analyst / data-analysis. Zernio's Google Ads tools take `account_id` AND `ad_account_id` — passing only the SocialAccount ID returns empty results. |
| `{BRAND}_ZERNIO_META_ADS_ACCOUNT_ID` | Meta Ads account ID for Windsor.ai fallback in digital-marketing-analyst / data-analysis (v2.5.0) |
| `{BRAND}_ZERNIO_LINKEDIN_ADS` (Zernio SocialAccount `_id`) + `{BRAND}_ZERNIO_LINKEDIN_ADS_CID` (LinkedIn sponsored account ID, numeric) | Both required for the LinkedIn Ads Windsor.ai fallback in digital-marketing-analyst / data-analysis. Same two-ID pattern as Google Ads — passing only the SocialAccount ID returns empty results. Optional: skip if the brand does not run LinkedIn Ads. |

**Auto-bootstrapped (no user action required at setup; created on first run of the relevant skill):**
| Env var | Bootstrapped by | Required since |
|---|---|---|
| `${BRAND}_CRM_DB` | apollo-lead-prospector / outreach-sequencer / proposal-generator | v2.4.0 |
| `${BRAND}_CUSTOMER_DB` | customer-onboarder / churn-predictor | v2.4.0 |
| `${BRAND}_INVOICE_TRACKER_DB` | invoice-collector | v2.4.0 |
| `${BRAND}_REPORTS_DB` | financial-reporter / investor-update-writer | v2.4.0 |
| `${BRAND}_COMPETITOR_DB` | competitor-monitor | v2.4.0 |
| `${BRAND}_MEETINGS_DB` | meeting-analyzer | v2.4.0 |
| `${BRAND}_ACTIONS_DB` | meeting-analyzer | v2.4.0 |
| `${BRAND}_PERFORMANCE_DB` | content-performance-analyst | v2.13.0 |
| `${BRAND}_TREND_DB` | trend-radar | v2.13.0 |
| `${BRAND}_GIGS_DB` | gig-prospector | v2.16.0 |

For each: if present + the matching DB exists in Notion → ✅ skip. If missing → not flagged as a gap (the skill will create on first run). The audit reports presence informationally only.

For each missing key, mark whether it's required or optional.

### 1e. MCP connectors

You cannot directly probe what the user has connected in Claude settings. Instead, **lazily test** each one with a low-cost call and treat connection errors as "not connected":

| MCP | Probe | Required since |
|---|---|---|
| Five Agents (gateway) | `fiveagents_log_run` (test entry) | v2.0 |
| Zernio (own MCP, OAuth) | `profiles_list` (success = authorized) | v2.18.0 (formerly Late, gateway-routed; split to its own custom connector in gateway v1.7.4 — `https://mcp.zernio.com/mcp`) |
| DataforSEO (own MCP, Basic Auth) | `keywords_data_google_ads_search_volume` with a trivial keyword (optional) | v2.18.0 (formerly gateway-routed; split to its own custom connector in gateway v1.7.5 — `https://mcp.dataforseo.com/mcp`; only needed for keyword research — never flag as a required gap) |
| Notion | `notion-search` query="ping" | v2.0 |
| Slack | `slack_search_users` query="me" | v2.0 |
| Gmail | `list_labels` | v2.0 |
| Google Calendar | `list_calendars` | v2.0 |
| Windsor.ai | `get_connectors` | v2.1.0 |
| Canva | `list-brand-kits` | v2.1.0 |
| Playwright | check `claude mcp list` (Bash) | v2.1.4 (website analysis in brand-setup Step 4 — template rendering now uses gateway) |
| Apollo.io | `apollo_users_api_profile` | v2.4.0 |
| Calendly | OAuth status check (Calendly MCP gates real tools behind authenticate) | v2.4.0 |
| Stripe | OAuth status check (Stripe MCP gates real tools behind authenticate) | v2.4.0 |
| Xero | `whoami` | v2.4.0 |
| PostHog | `user-get` | v2.4.0 (used by `churn-predictor`; `data-analysis` may already validate it — reuse if so) |
| Gamma | `get_themes` | v2.4.0 (used by `investor-update-writer`; `campaign-presenter` may already validate it — reuse if so) |
| n8n Cloud | `get_workflow_best_practices` (technique="list") | v2.17.0 (optional — used by `n8n-workflow-builder` for inbound-gig proof workflows; never flag as a required gap — only relevant if the brand pursues marketplace work) |
| Meta Ads MCP *(optional enhancement — limited rollout)* | attempt any `ads_*` tool call (e.g. `ads_get_ad_accounts`); 401 / not-found → not connected | v2.2.13 (Windsor.ai already covers Meta data fully when this isn't connected — never flag as a required gap) |

### 1f. Workspace CLAUDE.md

Check `CLAUDE.md` at the workspace root (alongside `brands/`).

| Item | Required since |
|---|---|
| File exists | v2.2.7 |
| Contains `## Agent Identity` block | v2.2.7 |
| `link.md` path is **absolute** (`os.path.isabs == True`) | **v2.2.10** |
| `link.md` path is correct (file exists at that location) | v2.2.7 |
| Contains `## Credential Loading` block with `load_credentials()` snippet | v2.2.7 |
| `## Active Brand` section names the current brand | v2.2.7 |
| Version stamp `<!-- link.md version: ... -->` present inside BEGIN/END markers | v2.3.0 |
| Version stamp matches current `agents/link.md` Maintenance version (will be refreshed in Step 3g regardless) | v2.3.0 |

### 1g. Claude Code settings

Read `.claude/settings.local.json` and `~/.claude/settings.json` for:

| Setting | Required since |
|---|---|
| Bypass permissions mode (`Allow bypass permissions mode`) | v2.2.3 |
| Domain Allowlist set to **All Domains** | v2.2.4 |
| Project Permission set to **Act without asking** | v2.2.5 |

These are UI settings — surface them to the user as a manual checklist if you can't read them programmatically.

### 1h. fb.ai social templates

Social templates live on fb.ai (brand-setup Step 4c), **not on disk** — there is no local folder, version hash, or gateway upload to reconcile. Detect which exist for this brand:

```
Use gateway MCP tool fivebucks_list_templates:
- fiveagents_api_key: ${FIVEAGENTS_API_KEY}
```

Record which of the four `type`s are present: `meta-carousel`, `meta-story`, `linkedin-post`, `meta-post`. If `FIVEBUCKS_API_KEY` isn't set (no fb.ai plan), skip this step — the brand uses the Gemini + Pillow fallback for all formats. Missing types are an install opportunity, not a failure (see Step 3c).

### 1i. fb.ai media library

Check whether the brand has any media uploaded via `fivebucks_list_media_folders`. If `FIVEBUCKS_API_KEY` isn't set, skip — record as N/A.

```
Use gateway MCP tool fivebucks_list_media_folders:
- fiveagents_api_key: ${FIVEAGENTS_API_KEY}
```

- Result is non-empty → ✅ media library populated.
- Result is empty → ⏭ no brand photos uploaded (offer to set up in Step 3d; skills fall back to Gemini image generation when empty).

### 1j. Notion DB for social calendar

If `{BRAND}_NOTION_DB` is set:
- `notion-fetch` it. If success and the returned object is a database with `Name` (title) property → OK.
- If the fetch returns `not_found` or the object is not a database → mark for re-bootstrap.

If `{BRAND}_NOTION_DB` is unset → mark as missing (social-calendar will bootstrap on first run, but we offer to do it now).

### 1k. Skill and agent version audit

Using the data collected in Step 0, build a version table. **Read actual versions from disk — do not use the example below as current data.**

*(Example format only — versions shown here are illustrative, not current):*

| File | Installed version | Changed since {last_applied}? |
|---|---|---|
| agents/link.md | v2.3.0 | ✅ yes |
| brand-setup | v2.2.15 | ✅ yes |
| content-generator | v2.2.15 | ✅ yes |
| creative-designer | v2.2.15 | ✅ yes |
| content-creation | v2.2.15 | ✅ yes |
| social-calendar | v2.2.15 | ✅ yes |
| background-generator | v2.2.15 | ✅ yes |
| digital-marketing-analyst | v2.3.0 | ✅ yes |
| data-analysis | v2.3.0 | ✅ yes |
| social-publisher | v2.2.5 | — no |
| research-strategy | v2.2.5 | — no |
| campaign-presenter | v2.2.5 | — no |
| plugin-update | v2.3.0 | ✅ yes (new) |
| `apollo-lead-prospector` | v2.4.0 | ✅ yes (new) |
| `outreach-sequencer` | v2.4.0 | ✅ yes (new) |
| `proposal-generator` | v2.4.0 | ✅ yes (new) |
| `customer-onboarder` | v2.4.0 | ✅ yes (new) |
| `churn-predictor` | v2.4.0 | ✅ yes (new) |
| `invoice-collector` | v2.4.0 | ✅ yes (new) |
| `financial-reporter` | v2.4.0 | ✅ yes (new) |
| `competitor-monitor` | v2.4.0 | ✅ yes (new) |
| `investor-update-writer` | v2.4.0 | ✅ yes (new) |
| `meeting-analyzer` | v2.4.0 | ✅ yes (new) |

Flag any skill that is entirely **new** (folder exists on disk but `last_applied` predates its introduction). For each changed/new skill, extract the relevant changelog bullets from its `### Change Log` section — these drive Step 3k.

**Registry source.** Read the plugin's `skills-manifest.json` — it ships **pre-generated** from each skill's frontmatter, and its sync is enforced at release time by `commit-to-git` Step 4f. plugin-update runs in the user's Cowork project and does **not** regenerate or `--check` it (that's a maintainer/CI step) — it just reads the shipped manifest for each skill's `area` / `deps`. Reading the manifest needs no tooling; it's plain JSON.

---

## Step 2 — Show the gap report

The agent shows you a **single compact summary** of every category checked in Step 1 — what's present (✅), what's missing required (❌), what's missing optional (⏭). You see the whole picture before any fix work starts, so you can scan it and decide whether to walk through the gaps now or come back later. Optional gaps (e.g. design-system, social templates, investors.md if you haven't raised) are clearly marked so you don't feel pressured to fill them.

**Expect 1 minute.** Single question at the end: "Want me to walk through these gaps now?" If you say no, the skill exits cleanly and the gap report is logged to memory so you can revisit later.

Build a **compact gap table** and show it to the user before doing anything. Group by category. Mark each item as ✅ (present) / ❌ (missing required) / ⏭ (optional missing). Example:

```
[{brand}] Plugin Update — Gap Report
Plugin version: {last_applied} → {DEFAULT_VERSION}

Skills/agents updated since {last_applied}
  ✅ agents/link.md       v2.7.0  social templates → fb.ai (fivebucks_* tools)
  ✅ brand-setup          v2.2.15  Step 4c rewrite (EDITMODE contracts); design-system/ now optional
  ✅ content-generator    v2.7.0  render via fb.ai fivebucks_* (Step 4c-template)
  ✅ creative-designer    v2.2.15  template-path → gateway; design-system/ optional
  ✅ content-creation     v2.2.15  _copy.json output; naming convention split
  ✅ social-calendar      v2.2.15  Direction column added (11 columns)
  ✅ plugin-update        v2.3.0   new skill

Brand context files
  ✅ brand.md, product.md, audience.md, competitors.md, funnel.md, avatars.md, logo.png
  ❌ sales.md                             ← v2.4.0 (apollo-lead-prospector / outreach-sequencer / proposal-generator)
  ❌ customer-success.md                  ← v2.4.0 (customer-onboarder / churn-predictor)
  ❌ finance.md                           ← v2.4.0 (invoice-collector / financial-reporter)
  ⏭ investors.md                         ← v2.4.0 optional (only required if brand has raised external capital)
  ⏭ operations.md                        ← v2.4.0 optional (only required if user processes meeting transcripts)
  ⏭ design-system/                       ← optional (recommended; brand.md fallback if absent)

fb.ai social templates (Step 1h — needs FIVEBUCKS_API_KEY)
  ⏭ meta-carousel / meta-story / linkedin-post / meta-post — which exist on fb.ai? (install via Step 3c)

fb.ai media library (Step 1i — needs FIVEBUCKS_API_KEY)
  ⏭ no media folders found — upload brand photos via Step 3d

brand.md sections
  ✅ Tagline / Voice & Tone / Colors / Approved Phrases / Do NOT Say
  ❌ Locale (Currency / Timezone / Meta USD)        ← v2.2.0
  ❌ Social Publishing (Zernio profile + accounts)  ← v2.2.1

Env vars (.claude/settings.local.json)
  ✅ FIVEAGENTS_API_KEY, GEMINI_API_KEY, SLACK_NOTIFY_USER, REPORT_EMAIL
  ❌ {BRAND}_ZERNIO_FB, {BRAND}_ZERNIO_IG, {BRAND}_ZERNIO_LI
  ❌ {BRAND}_NOTION_DB                    ← will be created by social-calendar on first run, or bootstrap now

MCP connectors
  ✅ Five Agents, Notion, Slack, Gmail, Google Calendar
  ❌ Zernio (required for social-publisher — reconnect as its own OAuth connector; formerly gateway-routed)
  ❌ Windsor.ai (required for digital-marketing-analyst)
  ❌ Canva (required for campaign-presenter)
  ❌ Playwright (required for website analysis in brand-setup Step 4)
  ❌ Apollo.io (required for apollo-lead-prospector, outreach-sequencer)
  ❌ Calendly (required for customer-onboarder, meeting-analyzer)
  ❌ Stripe (required for invoice-collector, financial-reporter)
  ❌ Xero (required for invoice-collector, financial-reporter)
  ⏭ DataforSEO (optional — keyword research; reconnect as its own Basic Auth connector)
  ⏭ PostHog (optional — churn-predictor falls back to support-ticket + login signals)
  ⏭ Gamma (optional — investor-update-writer needs it; skip if you don't deliver investor decks)
  ⏭ Meta Ads MCP (optional enhancement — Windsor.ai already covers Meta Ads fully)

Workspace CLAUDE.md
  ❌ link.md path is RELATIVE — must be absolute (v2.2.10)
  ✅ Agent Identity block
  ✅ Credential Loading block

Settings
  Manual: confirm Bypass permissions ON, Domain Allowlist = All Domains, Project = Act without asking

→ {N} required gaps · {M} optional gaps · ready to walk through them?
```

Ask the user:
> Want me to walk through these gaps now? I'll skip anything you say "skip" to. Anything I miss won't break — just won't be set up.

If the user says no / not now, exit cleanly and offer to re-run later. Log the gap report to memory either way.

---

## Step 3 — Fill the gaps interactively

The agent walks through each missing item in order, **only prompting for items that came back ❌ or ⏭ in Step 2** — never re-asking for known-good state. For most fills, the agent delegates to the matching brand-setup sub-step (e.g. missing `sales.md` triggers `brand-setup` Step 5g, a Zernio connector not authorized triggers brand-setup Step 7a-ii Zernio OAuth authorize flow). You can say "skip" to any optional gap.

**Expect 2–60 minutes** depending on how many gaps you have. A brand with 3 missing context files + 2 missing env vars + a CLAUDE.md path fix takes ~10 min. A brand with 10+ gaps and lots of optional templates can stretch to an hour. The skill saves progress as it goes, so you can interrupt and resume.

Walk through each missing item in this order. **Skip any that came back ✅ in Step 2** — never re-prompt for known-good state.

### 3a. Brand context files (only if missing)

For each missing file in `brands/{brand}/`:

**Pre-fill rule — applies to every v2.4.0 file below (sales / customer-success / finance / investors / operations).** In a plugin-update context, the brand has been around for a while, so the v2.0 files (`brand.md`, `product.md`, `audience.md`, `competitors.md`, `funnel.md`) almost always already exist with rich content. **You MUST follow the "Read existing context first" mapping table at the top of each brand-setup sub-step (Step 5g intro, 5h intro, 5i intro, 5j intro, 5k intro) before asking the user anything.** Read those source files, draft answers from them, then ask "Confirm or edit?" per block — never ask from scratch what's already on disk. In a plugin-update context this rule is essentially mandatory because the source data is virtually guaranteed to exist; in a fresh brand-setup run it is best-effort because the source files are being built up in the same session.

- `brand.md` / `audience.md` missing → run `brand-setup` Step 4 (Website Analysis) for this brand only. Re-extract from the website.
- `product.md` / `competitors.md` missing → run `/link-skills:research-strategy` to regenerate.
- `funnel.md` missing → run `brand-setup` Step 5 funnel block (ask user about funnel + GA4 events).
- `avatars.md` missing → run `brand-setup` Step 5 avatars block.
- `logo.png` missing → ask the user for the file path, copy into `brands/{brand}/logo.png`.
- `backgrounds/` missing → just `mkdir`. No content needed (Gemini generates fresh per post since v2.2.9).
- `sales.md` missing → run `brand-setup` Step 5g (Sales context). **Apply the Read-first pre-fill table at the top of Step 5g** — Sender Persona drafts from brand.md (founder name/title), ICP filters draft per persona from audience.md, disqualification blocklist drafts from competitors.md URLs, default tier per persona drafts from product.md Pricing. Only ask the user from scratch for fields that have no source.
- `sales.md` present but Sender Persona is missing `Booking URL:` (flagged by Step 1a inline schema check) → ask only the single question: "What's your public Calendly scheduling link (e.g. `https://calendly.com/you/intro-call`)?" Append `- Booking URL: {url}` to the Sender Persona block of `brands/{brand}/sales.md`. Do NOT re-run the full Step 5g — Sender Persona Name/Title/Signature/Photo are already populated; this is a targeted backfill only.
- `sales.md` present but missing `## Inbound Job Filters` (flagged by Step 1a inline schema check) → ask first: "Does this brand pursue freelance/marketplace work (Upwork, Freelancer.com, etc.)?" If no → skip (not a gap; `gig-prospector` simply won't run). If yes → run `brand-setup` Step 5g **Step H** only: confirm Markets, Platforms + account status, **draft Search Keywords from `product.md`** (never hardcode), then Budget Floor / Exclusions / Daily Cap. Append the `## Inbound Job Filters` section to `brands/{brand}/sales.md` — do NOT re-walk the rest of Step 5g. `${BRAND}_GIGS_DB` auto-bootstraps on `gig-prospector`'s first run.
- `customer-success.md` missing → run `brand-setup` Step 5h. **Apply the Read-first pre-fill table at the top of Step 5h** — Plan Tiers drafts from product.md Pricing, Onboarding Milestone trigger events draft from funnel.md GA4 events, Intervention Playbook copy drafts from brand.md voice. Only ask the user from scratch for fields that have no source.
- `finance.md` missing → run `brand-setup` Step 5i. **Apply the Read-first pre-fill table at the top of Step 5i** — Escalation Tone Ladder copy drafts from brand.md voice, currency / Alert Threshold formatting from brand.md `## Locale`, KPIs to Highlight defaults to MRR/ARR if product.md Pricing implies subscription else project margin / cash collected. Only ask the user from scratch for fields that have no source.
- `investors.md` missing → ask first: "Have you raised external capital?" If no → skip (not flagged as gap). If yes → run `brand-setup` Step 5j. **Apply the Read-first pre-fill table at the top of Step 5j** — Sections to Include KPIs draft from finance.md (if present), drafting tone falls back to brand.md voice if user pastes <2 prior updates, sender attribution drafts from sales.md Sender Persona. Only ask the user from scratch for the investor list and prior-updates archive (those have no other source).
- `operations.md` missing → ask first: "Do you run regular meetings (1:1s, standups, client calls) and want Claude to process transcripts?" If no → skip (not flagged as gap; meeting-analyzer falls back). If yes → run `brand-setup` Step 5k. **Apply the Read-first pre-fill table at the top of Step 5k** — Sales tasks owner drafts from sales.md Sender Persona, Customer issues owner drafts from customer-success.md CSM mentions, Finance tasks owner drafts from finance.md alert recipient. Only ask the user from scratch for owners that have no other-file source.
- `competitors.md` extension missing (existing file present but lacks `monitor_urls` / `track_pages` / `exec_team` per competitor) → run `brand-setup` Step 5l. Prompts user to add the new fields per existing competitor entry.

### 3b. design-system/ (optional — recommended)

**fb.ai context — show this before any fivebucks.ai link.** fivebucks.ai (fb.ai) is a **paid** product. Never give the user a `fivebucks.ai` URL or ask them to upload there (the optional upload below, plus Steps 3c and 3d all do) without first explaining what it is:
> **About fb.ai (fivebucks.ai):** it's an optional **paid subscription** that stores your **design system and social templates** in the cloud, so Claude can **automate your branded social posting** — generating and publishing fully on-brand Carousels, Stories, and IG / FB / LinkedIn posts for you. Your design system also lives locally for **free**, so you can start without paying; the fb.ai plan adds the hosted social templates and lets posts render in your exact brand colors and fonts. You can skip fb.ai entirely — Claude falls back to the colors and fonts in `brand.md` plus Gemini rendering, which stays fully supported.

If `brands/{brand}/design-system/` is missing, offer installation but accept "skip" — the brand.md colors and fonts are a fully functional fallback:

> Your brand doesn't have a Claude Design system installed. It's optional — skills fall back to the colors and fonts in `brand.md`. Installing it gives tighter visual consistency across all outputs. Want to set it up now?

If yes, walk the user through `brand-setup` Step 4b:
1. Open https://claude.ai/design → go to **Design System** → click **Create**
2. On the **"Set up your design system"** page, fill the attach form — **Company name and blurb**; optionally attach a GitHub repo / local code / a **.fig** file / **fonts, logos and assets**; and paste the colors, fonts, and voice already in `brands/{brand}/brand.md` into **Any other notes** — then click **Continue to generation**
3. On the generation prompt, paste a website-aware prompt so Claude validates against the live site. Substitute the brand's website URL (ask the user if it isn't already known, or pull it from `brand.md`):
   ```
   This is my website: {WEBSITE_URL} — please read it and use it to validate and fill in my colors, typography, components, spacing, and overall visual style.
   Confirm and refine against the site: colors (primary / secondary / accent), heading + body fonts, and brand voice from brand.md.
   Build a complete, reusable design system: color tokens, a type scale, buttons, cards, headers / nav, and spacing.
   ```
   Iterate until it feels on-brand.
4. Share → Download Project as .zip → unzip (leave it wherever it is — e.g. `~/Downloads/Acme Design System`; no need to move it)
5. Ask the user for the path to the unzipped folder, then request access to *that exact path* before reading it — the user approves it in a one-time prompt:
   ```
   mcp__cowork__request_cowork_directory(path="<the path the user gave>")
   ```
   Required in the Cowork harness (filesystem access is sandboxed to approved directories); request the folder the user named, not a guessed default. In local Claude Code this tool is absent and the filesystem is already accessible — skip the request. If the user declines, ask them to move the folder inside the project mount, then copy from there.
6. Once access is approved, copy the folder into `brands/{brand}/design-system/` (Python `shutil.copytree`, handling the single-inner-folder nested-zip case — see brand-setup Step 4b-C).

Verify `brands/{brand}/design-system/` exists and is non-empty before marking complete. If the user skips, mark as ⏭ — not a gap that blocks any skill.

**Optional fb.ai upload:** after the local copy is in place, ask whether the user also wants to upload the design system to fb.ai (only useful with a paid fb.ai plan — lets fb.ai social templates render with the brand's colors/fonts). If yes:
1. Go to https://www.fivebucks.ai/dashboard/api-keys — generate API Key if not done yet *(skip if already generated)*
2. Go to https://www.fivebucks.ai/dashboard/social-posts/brand-kit — click **Upload Design System Zip** and upload the ZIP.

The fb.ai upload is optional — the local `design-system/` already works everywhere. Skip is fine.

### 3c. Optional fb.ai social templates (offer, don't force)

From Step 1h you know which of the four `type`s exist on fb.ai. For any **missing** type the brand would benefit from, offer to set it up — but accept "skip":

> Want to set up any optional social templates? Pick any of the four: Meta Carousel (IG/FB carousel), Meta Story (IG/FB 9:16), LinkedIn Post (LinkedIn single-image), Meta Post (IG/FB single-image). They make published content more polished and need a paid fb.ai plan. Skip any channel you don't publish on — it falls back to Gemini + Pillow.

If yes, walk through the matching brand-setup sub-step (each = author in Claude Design → generate API Key at https://www.fivebucks.ai/dashboard/api-keys if not done yet → upload ZIP at https://www.fivebucks.ai/dashboard/social-posts/templates → verify via `fivebucks_list_templates`):
- Meta Carousel → `brand-setup` Step 4c-i
- Meta Story → `brand-setup` Step 4c-ii
- LinkedIn Post → `brand-setup` Step 4c-iii
- Meta Post → `brand-setup` Step 4c-iv

There is **no** local copy, version hash, or gateway-upload step to reconcile — templates live on fb.ai and are detected live. If `FIVEBUCKS_API_KEY` isn't set, skip this step (the brand uses the Gemini + Pillow fallback).

### 3d. Optional fb.ai media library (offer, don't force)

Check whether the brand has any media folders via `fivebucks_list_media_folders`. If the media library is empty and the user wants on-brand photos in generated posts, offer to set it up:

> The fb.ai media library is empty. Uploading brand photos lets skills pick on-brand visuals automatically. Want to add some now? You can always skip and upload later.

If yes:
> 1. Go to https://www.fivebucks.ai/dashboard/api-keys — generate API Key if not done yet *(skip if already generated)*
> 2. Go to https://www.fivebucks.ai/dashboard/social-posts/media
> 3. Create one folder per template type, named **exactly** as follows — `content-generator` matches photos to templates using these names:
>    - **LinkedIn Post** — photos for LinkedIn single-image posts
>    - **Meta Story** — photos for Instagram / Facebook Stories
>    - **Meta Carousel** — photos for Instagram / Facebook Carousels
>    - **Meta Post** — photos for Instagram / Facebook single-image posts
>
>    Only create folders for template types that exist on fb.ai. Then click **Upload Media** inside each folder to add your photos.

If the user skips, mark as ⏭ — skills fall back 

…(truncated)
