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.mdplugins/link-skills/skills/plugin-update/SKILL.md
GitHubMaintenance
| 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-keysand added afivebucks_whoamiprobe: keys generated before scoping are legacy full-access keys and keep working (emptyscopes— no action needed), but a regenerated key missingsocial_postswill silently break the fb.ai-dependent skills. Reports the granted scopes + quota snapshot and surfacesauth/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 callsads_list_ad_accounts(waslist_ad_accounts).profiles_list/accounts_listwere 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-defunctLATE_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_ids 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 byn8n-workflow-builder. Step 3g adds the n8n Cloud connect-walkthrough prompt. Step 3k maps the newInbound Gig Engine completedchangelog entry to its optional brand action (connect n8n Cloud + optional${BRAND}_N8N_PROJECT). The three skills are auto-detected from the shippedskills-manifest.json(Step 1 registry read); no per-brand context file is required beyond whatgig-prospectoralready 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 pullin 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 -5in 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 Benchmarkstable- No remaining
TBDevent 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_KEYenv 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 types 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-fetchit. If success and the returned object is a database withName(title) property → OK.- If the fetch returns
not_foundor 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.mdmissing → runbrand-setupStep 4 (Website Analysis) for this brand only. Re-extract from the website.product.md/competitors.mdmissing → run/link-skills:research-strategyto regenerate.funnel.mdmissing → runbrand-setupStep 5 funnel block (ask user about funnel + GA4 events).avatars.mdmissing → runbrand-setupStep 5 avatars block.logo.pngmissing → ask the user for the file path, copy intobrands/{brand}/logo.png.backgrounds/missing → justmkdir. No content needed (Gemini generates fresh per post since v2.2.9).sales.mdmissing → runbrand-setupStep 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.mdpresent but Sender Persona is missingBooking 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 ofbrands/{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.mdpresent 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-prospectorsimply won't run). If yes → runbrand-setupStep 5g Step H only: confirm Markets, Platforms + account status, draft Search Keywords fromproduct.md(never hardcode), then Budget Floor / Exclusions / Daily Cap. Append the## Inbound Job Filterssection tobrands/{brand}/sales.md— do NOT re-walk the rest of Step 5g.${BRAND}_GIGS_DBauto-bootstraps ongig-prospector's first run.customer-success.mdmissing → runbrand-setupStep 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.mdmissing → runbrand-setupStep 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.mdmissing → ask first: "Have you raised external capital?" If no → skip (not flagged as gap). If yes → runbrand-setupStep 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.mdmissing → 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 → runbrand-setupStep 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.mdextension missing (existing file present but lacksmonitor_urls/track_pages/exec_teamper competitor) → runbrand-setupStep 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.mdplus 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:
- Open https://claude.ai/design → go to Design System → click Create
- 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.mdinto Any other notes — then click Continue to generation - 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):
Iterate until it feels on-brand.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. - Share → Download Project as .zip → unzip (leave it wherever it is — e.g.
~/Downloads/Acme Design System; no need to move it) - 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:
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.mcp__cowork__request_cowork_directory(path="<the path the user gave>") - Once access is approved, copy the folder into
brands/{brand}/design-system/(Pythonshutil.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:
- Go to https://www.fivebucks.ai/dashboard/api-keys — generate API Key if not done yet (skip if already generated)
- 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 types 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-setupStep 4c-i - Meta Story →
brand-setupStep 4c-ii - LinkedIn Post →
brand-setupStep 4c-iii - Meta Post →
brand-setupStep 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:
Go to https://www.fivebucks.ai/dashboard/api-keys — generate API Key if not done yet (skip if already generated)
Create one folder per template type, named exactly as follows —
content-generatormatches 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 to Gemini image generation. If FIVEBUCKS_API_KEY isn't set, skip this step entirely.
3e. brand.md sections (only the missing ones)
For each missing section header in brand.md, append it without rewriting existing content:
- Locale missing — extract Currency / Timezone / Meta USD exchange rate from the website (re-use Step 4 logic). Append a
## Localesection tobrand.md. - Social Publishing missing — re-run
brand-setupStep 7b Step D (auto-discover Zernio profile + connected accounts via the Zernio MCP toolsprofiles_list+accounts_list). Append a## Social Publishingsection. - Content Strategy missing — run
brand-setupStep 4a. Ask the user: (1) Primary channel:youtubeorstatic? (2) Ifyoutube: which platforms for distribution, which are currently connected, clips per video. Write the## Content Strategysection tobrand.md. This section is read bysocial-calendarat runtime to select planning mode — without it,social-calendardefaults to static mode.
3f. Env vars (only the missing ones)
For each missing required env var, ask the user for the value and append/update .claude/settings.local.json under env. Preserve all existing keys — never overwrite the file wholesale.
For missing {BRAND}_ZERNIO_FB/IG/LI/TT/TW: re-run the Zernio MCP tools profiles_list + accounts_list to auto-discover. (_TT = TikTok, _TW = Twitter/X — organic publishing IDs needed by video-repurposer for YouTube-First clip publishing; skip any platform not connected to Zernio.)
Legacy ${BRAND}_LATE_* → ${BRAND}_ZERNIO_* rename. Brands set up before gateway v1.7.4 have their Zernio account IDs stored under the old ${BRAND}_LATE_* prefix (and a now-defunct LATE_API_KEY). For every ${BRAND}_LATE_* key in the env block (_FB / _IG / _LI / _TT / _TW / _GOOGLE_ADS / _GOOGLE_ADS_CID / _META_ADS_ACCOUNT_ID / _LINKEDIN_ADS / _LINKEDIN_ADS_CID), copy the value to the matching ${BRAND}_ZERNIO_* key and delete the old key. Also delete LATE_API_KEY if present — Zernio is now an OAuth connector with no API key.
For missing {BRAND}_NOTION_DB: ask the user if they want to bootstrap now (calls notion-create-database per social-calendar Step 3a) or defer to first social-calendar run.
For missing {BRAND}_ZERNIO_GOOGLE_ADS, {BRAND}_ZERNIO_GOOGLE_ADS_CID, {BRAND}_ZERNIO_META_ADS_ACCOUNT_ID, {BRAND}_ZERNIO_LINKEDIN_ADS, or {BRAND}_ZERNIO_LINKEDIN_ADS_CID: auto-discover via the Zernio MCP. SocialAccount IDs need no user input; customer / sponsored-account IDs fall back to a prompt only when Zernio rate-limits or returns nothing.
Step 1 — Zernio SocialAccount IDs (no user input):
Use Zernio MCP tool `accounts_list`:
→ Find entry where platform is "googleads" or "google" → save _id as ${BRAND}_ZERNIO_GOOGLE_ADS
→ Find entry where platform is "metaads" or "facebook" → save _id as ${BRAND}_ZERNIO_META_ADS_ACCOUNT_ID
→ Find entry where platform is "linkedinads" or "linkedin_ads" (NOT plain "linkedin", which is the
organic LinkedIn page saved as ${BRAND}_ZERNIO_LI) → save _id as ${BRAND}_ZERNIO_LINKEDIN_ADS
Step 2 — Google Ads customer ID (only if ${BRAND}_ZERNIO_GOOGLE_ADS_CID is missing):
Use Zernio MCP tool `ads_list_ad_accounts`:
- account_id: ${BRAND}_ZERNIO_GOOGLE_ADS
2a. Accounts returned → take the customer ID (numeric) from the first entry and save as
${BRAND}_ZERNIO_GOOGLE_ADS_CID.
2b. Empty response OR 429 rate-limit → ask the user:
> "What is your Google Ads customer ID? You can find it in the top-right corner
> of your Google Ads account (format: XXX-XXX-XXXX)."
Strip dashes before saving.
Step 3 — LinkedIn sponsored account ID (only if ${BRAND}_ZERNIO_LINKEDIN_ADS is set
and ${BRAND}_ZERNIO_LINKEDIN_ADS_CID is missing):
Use Zernio MCP tool `ads_list_ad_accounts`:
- account_id: ${BRAND}_ZERNIO_LINKEDIN_ADS
3a. Accounts returned → take the numeric sponsored account ID from the first entry and save as
${BRAND}_ZERNIO_LINKEDIN_ADS_CID.
3b. Empty response OR 429 rate-limit → ask the user:
> "What is your LinkedIn sponsored account ID? Open Campaign Manager → look at the URL or
> top-left account picker — it's a numeric ID (e.g. 517258773)."
Save the digits only.
Step 4 — Legacy rename (handle before Step 1):
If ${BRAND}_ZERNIO_GOOGLE_ADS_ACCOUNT_ID or legacy ${BRAND}_LATE_GOOGLE_ADS exists but
${BRAND}_ZERNIO_GOOGLE_ADS does not, copy the value across, delete the old key, then run
Step 2 to populate _CID. (See also the blanket `${BRAND}_LATE_*` → `${BRAND}_ZERNIO_*`
rename above — apply that first.)
${BRAND}_ZERNIO_GOOGLE_ADS, ${BRAND}_ZERNIO_META_ADS_ACCOUNT_ID, and ${BRAND}_ZERNIO_LINKEDIN_ADS are SocialAccount IDs (Zernio-side _ids); ${BRAND}_ZERNIO_GOOGLE_ADS_CID and ${BRAND}_ZERNIO_LINKEDIN_ADS_CID are the actual platform-side account IDs (Google Ads customer ID, LinkedIn sponsored account ID). Zernio's Google Ads and LinkedIn Ads tools require both the SocialAccount ID AND the platform-side ID — passing only the SocialAccount ID returns empty results. Only save the vars for platforms that are returned. If the Zernio connector is not authorized, skip silently and note: "Windsor.ai fallback ads account IDs not set — connect Zernio first." If LinkedIn Ads is not in accounts_list, skip Step 3 — most brands do not run LinkedIn Ads and the analyst skills tolerate the gap.
After updating .claude/settings.local.json, also store the remaining vaultable API keys (GEMINI_API_KEY, FIVEBUCKS_API_KEY) in the gateway vault via fiveagents_store_credential (mapping per brand-setup Step 7b vault table). Zernio (OAuth) and DataforSEO (own Basic-Auth connector) are not gateway-vaulted — no fiveagents_store_credential for them.
fb.ai key — check its scopes (new in v2.20.0)
fb.ai API keys are now project-scoped with 10 capabilities, and can carry an expiry. Existing keys generated before this change are legacy keys with full access — they keep working, no action needed. But it's worth confirming, because a key regenerated with boxes unticked will silently break skills.
If FIVEBUCKS_API_KEY is set, probe it:
Use gateway MCP tool fivebucks_whoami:
- fiveagents_api_key: ${FIVEAGENTS_API_KEY}
Read the response and report:
scopes: [](empty) → a legacy full-access key. ✅ Everything works. No action.scopeslists capabilities → a scoped key. Checksocial_postsis present — without it,content-generator,creative-designer,social-publisher,social-calendarand the other fb.ai-dependent skills will fail withauth/insufficient-scope. If it's missing, tell the user to regenerate at https://www.fivebucks.ai/dashboard/api-keys with the box ticked, then re-store it viafiveagents_store_credential(servicefivebucks).auth/key-expired(401) → the key expired. Regenerate and re-store.- Also surface the returned
quotasnapshot so the user knows what they have left.
Mention what the key now unlocks beyond social templates — SEO research, article generation, publishing, site audits, traffic monitoring, and cold-email lead gen (100 tools). A brand that only ticked Social Posts is leaving the rest on the table.
For the auto-bootstrapped DB env vars (${BRAND}_CRM_DB, ${BRAND}_CUSTOMER_DB, ${BRAND}_INVOICE_TRACKER_DB, ${BRAND}_REPORTS_DB, ${BRAND}_COMPETITOR_DB, ${BRAND}_MEETINGS_DB, ${BRAND}_ACTIONS_DB, ${BRAND}_PERFORMANCE_DB, ${BRAND}_TREND_DB):
- If env var is missing → no action. The skill that depends on it will create the Notion DB on first run and persist the ID to
.claude/settings.local.json(same pattern as${BRAND}_NOTION_DBfor social-calendar). - If env var is present but the Notion DB has been deleted → ask the user: "Re-bootstrap now or defer to first run?" Both paths work; bootstrap-now avoids a confusing error on next skill run.
Surface this informationally in the gap report — do not flag as a required gap.
3g. MCP connectors
For every MCP flagged ❌ or ⏭ in Step 1e, walk the user through the specific connector flow below — do not just list them in the gap report and move on. The user's brand-setup run is the only place these prompts appear in full; if plugin-update silently skips them, the user will end up with skills that error on first use because their MCP isn't connected.
Iterate through the missing MCPs in this order. For each one, post the prompt verbatim, wait for the user to confirm (or say "skip"), then re-probe. Mark complete only when the probe call succeeds.
Connected Apps (OAuth via Settings → Connected Apps):
Notion not connected (required — content calendar, briefs, auto-bootstrapped DBs):
Notion isn't connected — used for the content calendar, strategy briefs, and the auto-bootstrapped CRM / customer / invoice / report / competitor / meeting / actions / performance / trend / gigs DBs. Go to Settings → Connected Apps → Notion → Authorize, then tell me when done.
Slack not connected (required — skill-run notifications):
Slack isn't connected — used for "skill done" DMs after each run. Go to Settings → Connected Apps → Slack → Authorize, then tell me when done.
Gmail not connected (required — email reading + report delivery):
Gmail isn't connected — used for reading inbound replies and delivering reports. Go to Settings → Connected Apps → Gmail → Authorize, then tell me when done.
Google Calendar not connected (required — scheduling content drops & meetings):
Google Calendar isn't connected — used for scheduling content drops and meetings. Go to Settings → Connected Apps → Google Calendar → Authorize, then tell me when done.
Windsor.ai not connected (mandatory — Google Ads + GA4 + Meta Ads): walk all 3 steps, do not collapse to one prompt:
Windsor.ai isn't connected — this is the universal source for Google Ads + GA4 + Meta Ads (Facebook + Instagram).
- If you don't have an account yet, sign up free at https://windsor.ai/register
- In your Windsor dashboard, connect all three: Google Ads, GA4, and Meta Ads (Facebook Ads). All three are required — Meta Ads in Windsor is mandatory regardless of whether the optional Meta Ads MCP is added.
- Then in Claude: Settings → Connected Apps → Windsor.ai → Authorize. Tell me when done.
After the user confirms, re-run
get_connectorsand verify the result includes Google Ads, GA4, and Facebook. If any of the three is missing, ask the user to connect the missing one in their Windsor dashboard before marking complete.Canva not connected (used by
campaign-presenter):Canva isn't connected — used for campaign decks and pitch presentations. Go to Settings → Connected Apps → Canva → Authorize, then tell me when done. (Skip if you don't deliver Canva decks —
campaign-presenterwill be unconfigurable for this brand.)
Business-operations MCPs (added in v2.4.0):
Apollo.io not connected (used by
apollo-lead-prospector,outreach-sequencer):Apollo.io isn't connected — used for lead enrichment, contact search, and email-sequence injection. Go to Settings → Connected Apps → Apollo.io → Authorize, then tell me when done. (Skip if you don't run outbound — those two skills will be unconfigurable for this brand.)
Calendly not connected (used by
customer-onboarder,meeting-analyzer):Calendly isn't connected — used for kickoff scheduling and meeting metadata pulls. Go to Settings → Connected Apps → Calendly → Authorize, then tell me when done. (Skip if you don't use Calendly —
customer-onboarderfalls back to manual scheduling andmeeting-analyzerreads transcripts directly.)Stripe not connected (used by
invoice-collector,financial-reporter):Stripe isn't connected — used for invoice status and payment data. Go to Settings → Connected Apps → Stripe → Authorize, then complete the OAuth flow it gates the real tools behind. Tell me when both
authenticateandcomplete_authenticationhave succeeded. (Skip if you bill outside Stripe —invoice-collectorandfinancial-reporterwill be unconfigurable for this brand.)Xero not connected (used by
invoice-collectorfor invoice sync,financial-reporterfor P&L):Xero isn't connected — used for invoice status sync and pulling the P&L. Go to Settings → Connected Apps → Xero → Authorize, then tell me when done. (Skip if you don't use Xero —
financial-reporterwill fall back to Stripe-only data.)PostHog not connected (used by
churn-predictorfor product-usage signals):PostHog isn't connected — used by
churn-predictorfor product-usage signals (login frequency, feature engagement). Go to Settings → Connected Apps → PostHog → Authorize, then tell me when done. (Skip if you don't use PostHog —churn-predictorfalls back to support-ticket + login-frequency signals only.)Gamma not connected (used by
investor-update-writerfor investor decks):Gamma isn't connected — used by
investor-update-writerfor monthly investor decks. Go to Settings → Connected Apps → Gamma → Authorize, then tell me when done. (Skip if you don't deliver investor updates —investor-update-writerwill be unconfigurable for this brand.)n8n Cloud not connected (optional — used by
n8n-workflow-builderfor inbound-gig proof workflows):n8n Cloud isn't connected — used by
n8n-workflow-builder(the Prove phase of the Inbound Gig Engine) to build the proof-of-concept automation that backs a freelance bid. Go to Settings → Connected Apps → n8n Cloud → Authorize, then tell me when done. (Skip if you don't pursue marketplace work or don't demo automations —n8n-workflow-builderwill be unconfigurable, butgig-prospectorandgig-proposal-writerstill run.) Optionally, to send these workflows to a specific n8n project, give me the project ID and I'll save it as${BRAND}_N8N_PROJECT; otherwise they go to your default project.
Custom connectors (Settings → Connectors → Add custom connector):
Zernio not connected / not authorized (required — social publishing + ads account discovery; formerly gateway-routed as "Late", split to its own MCP in gateway v1.7.4):
Zernio (formerly Late) is now its own connector — it's used to publish social posts and to discover your ad-account IDs. Go to Settings → Connectors → Add custom connector, name
Zernio, URLhttps://mcp.zernio.com/mcp, then sign in with your Zernio account (OAuth). Tell me when done. (If you set this brand up before this change, you can also delete the oldLATE_API_KEYfrom.claude/settings.local.jsonafter authorizing — Zernio no longer uses an API key.)After the user confirms, re-run the Step 1e probe (
profiles_list); mark complete only when it succeeds. If aLATE_API_KEYstill exists in theenvblock, delete it (Zernio is OAuth-only now).DataforSEO not connected (optional — keyword research only; formerly gateway-routed, split to its own MCP in gateway v1.7.5):
(Optional) DataforSEO is now its own connector — used only for keyword research (search volume, suggestions). If you do keyword research: Settings → Connectors → Add custom connector, name
DataforSEO, URLhttps://mcp.dataforseo.com/mcp, then authenticate with Basic Auth using yourDATAFORSEO_LOGIN/DATAFORSEO_PASSWORDcredentials. Skip if you don't do keyword research — nothing else depends on it. Tell me whether you added it or skipped. (No env rename needed — the login/password env vars are unchanged.)Meta Ads MCP not connected (optional enhancement — limited rollout):
(Optional) Meta has an official MCP server that gives skills slightly more direct Marketing API access than Windsor.ai. If you want to try it: Settings → Connectors → Add custom connector, name
Meta Ads, URLhttps://mcp.facebook.com/ads, sign in with the Facebook/Meta Business account that owns the brand's ad accounts. If "Add custom connector" isn't visible or sign-in fails — no problem, your Windsor.ai connection already covers Meta Ads fully. Tell me whether you added it or skipped.- If the user added it successfully → save
META_ADS_SOURCE=meta_ads_mcpto.claude/settings.local.jsonenvblock. - If the user skipped or couldn't add it → leave
META_ADS_SOURCEunset. Downstream skills default to Windsor for Meta data. Do not flag as a required gap — this is purely an enhancement.
- If the user added it successfully → save
CLI-installed MCPs:
- Playwright not connected (required for website analysis in
brand-setupStep 4):
Then ask the user to restart Claude Code and confirmclaude mcp add playwright -- npx @playwright/mcp@latest/mcpshowsplaywright.
After every walkthrough, re-run the corresponding probe from Step 1e and only mark the gap closed when the probe succeeds. If the user said "skip", record it as ⏭ in the final summary so they can revisit later.
3h. CLAUDE.md (re-run brand-setup Step 9)
First — always re-embed the full agents/link.md body between the BEGIN/END markers, regardless of any other CLAUDE.md gaps. Refreshing the stamp comment alone is wrong — after a git pull that adds new skills (e.g. v2.4.0's 10 business-ops skills), the stamp will read "v2.4.0" while the embedded body still describes v2.3.x as a "marketing agent" with no Apollo / Stripe / Xero / etc. mentions. Always replace the body too.
import glob, os, re, datetime
# Locate agents/link.md (same search order as brand-setup Step 9a)
config_dir = os.environ.get("CLAUDE_CONFIG_DIR")
patterns = []
if config_dir:
patterns.append(os.path.join(config_dir, "**/agents/link.md"))
patterns.extend([
os.path.expanduser("~/.claude/**/agents/link.md"),
os.path.expandvars(r"%APPDATA%\Claude\**\agents\link.md"),
os.path.expanduser("~/Library/Application Support/Claude/**/agents/link.md"),
])
found = [f for p in patterns for f in glob.glob(p, recursive=True)]
link_md_path = os.path.abspath(os.path.realpath(found[0])) if found else None
if not link_md_path:
raise RuntimeError("Could not locate agents/link.md — ask user for the full path.")
# Read current link.md from disk + strip YAML frontmatter (same as brand-setup Step 9a)
link_md_content = open(link_md_path, encoding='utf-8').read()
link_md_body = re.sub(r'^---\s*\n.*?\n---\s*\n', '', link_md_content, count=1, flags=re.DOTALL).lstrip()
# Extract version from the Maintenance table
version_match = re.search(r'\|\s*Link\s*\|\s*(v[\S]+)\s*\|\s*([^|\n]+)\s*\|', link_md_content)
link_version = version_match.group(1).strip() if version_match else "unknown"
link_version_date = version_match.group(2).strip() if version_match else "unknown"
embed_date = datetime.date.today().isoformat()
# Build the full replacement block (stamp + body), wrapped in the existing BEGIN/END markers
new_block = (
f"<!-- BEGIN agents/link.md (embedded by brand-setup) -->\n"
f"<!-- link.md version: {link_version} | Last Changed: {link_version_date} | Embedded: {embed_date} -->\n"
f"\n"
f"{link_md_body}\n"
f"\n"
f"<!-- END agents/link.md -->"
)
# Replace EVERYTHING between (and including) the markers — body and stamp together
claude_md = open("CLAUDE.md", encoding='utf-8').read()
if "<!-- BEGIN agents/link.md (embedded by brand-setup) -->" in claude_md and "<!-- END agents/link.md -->" in claude_md:
claude_md_new = re.sub(
r'<!-- BEGIN agents/link\.md \(embedded by brand-setup\) -->.*?<!-- END agents/link\.md -->',
lambda m: new_block, # lambda avoids re.sub's backslash interpretation in link_md_body
claude_md,
count=1,
flags=re.DOTALL,
)
else:
# Markers absent — fall through to the "Missing entirely" branch below; do not partial-write here.
claude_md_new = claude_md
if claude_md_new != claude_md:
# Show the diff to the user before writing (per the rule at the bottom of this step)
open("CLAUDE.md", "w", encoding='utf-8').write(claude_md_new)
This runs unconditionally every time plugin-update executes — it is the equivalent of what brand-setup Step 9b does on first install. After a git pull, both the version stamp and the embedded link.md body are refreshed, so CLAUDE.md always reflects the currently installed version's full content (skills table, MCP list, context-file rows, etc.).
Verification after re-embed — confirm the stamp matches agents/link.md's current Maintenance version AND that a string unique to the new version appears in the embedded body. Example: after upgrading to v2.4.0, grep CLAUDE.md for apollo-lead-prospector (one of the 10 new skills); if absent, the body did not get re-embedded and you should retry the regex with the full text re-read.
Then patch any remaining CLAUDE.md gaps — only the affected lines:
- Missing entirely → create from scratch (full brand-setup Step 9 flow, including version stamp).
- Relative path → resolve absolute path (Step 9a) and replace the line in place. Validate
os.path.isabsbefore writing. Do not rewrite the rest of the file. - Missing Credential Loading block → append it under the existing Agent Identity block.
- Active Brand mismatch → update the brand line; if multi-brand, append rather than replace.
Show a unified diff to the user before writing any changes to CLAUDE.md.
Finally — refresh the ## Visual System block in CLAUDE.md (equivalent of brand-setup Step 9c). This runs unconditionally every time plugin-update executes — it brings stale or missing Visual System blocks up to date (e.g. brands set up before Step 9c was introduced, or before the media library row was added in v2.8.0).
Detection (best-effort — skip any call that requires FIVEBUCKS_API_KEY when the key is unset):
from pathlib import Path
brand_root = Path("brands") / brand # `brand` is the active brand slug
def folder_status(name):
p = brand_root / name
return "installed" if (p.is_dir() and any(p.iterdir())) else "not installed"
design_system_status = folder_status("design-system") # local — the crucial baseline
# Optional fb.ai mirror — call fivebucks_get_brand_kit (skip if FIVEBUCKS_API_KEY unset).
# design_system_fbai = "uploaded" if result is non-null, else "not uploaded"
# Social templates — call fivebucks_list_templates; collect each returned `type` (skip if no key).
fb_template_types = [] # e.g. ["meta-carousel", "linkedin-post"], or [] if none / no fb.ai key
# Media library — call fivebucks_list_media_folders (skip if no key).
# media_status = "installed" if any folders exist, else "not installed"
Build the block using the same template as brand-setup Step 9c (substituting {design_system_status}, {design_system_fbai}, {fb_template_types}, {media_status}), then inject idempotently:
- If
CLAUDE.mdalready contains<!-- BEGIN visual-system (managed by brand-setup Step 9c) -->and<!-- END visual-system (managed by brand-setup Step 9c) -->markers, replace everything between (and including) those markers with the freshly built block. - Otherwise, append the new block to the end of
CLAUDE.mdpreceded by a blank line.
Show the user a concise status line:
✅
CLAUDE.mdVisual System section refreshed:
- design-system (local): {design_system_status} · fb.ai mirror: {design_system_fbai}
- fb.ai social templates: {fb_template_types or "none"}
- media library: {media_status}
3i. Settings (manual)
For UI-only settings, show a checklist and ask the user to confirm each one:
Please confirm in Claude settings:
- Settings → Claude Code → Allow bypass permissions mode = ON
- Settings → Capabilities → Domain Allowlist = All Domains
- Project → Permission = Act without asking Reply "all set" when done.
3j. Funnel TBD events
If funnel.md had TBD entries in Step 1c and Windsor.ai is now connected, re-run the GA4 event-discovery flow from brand-setup Step 8 #16 and patch in the confirmed event names.
3k. Version-specific brand actions
For each skill/agent flagged as changed in Step 1k, read its ### Change Log bullets and map them to required brand configuration actions. Use this decision table:
| Changelog entry | Brand action required |
|---|---|
| Direction column added (content-generator / social-calendar v2.2.15) | ✅ Check Notion Social Calendar DB has a Direction select column at position 10; add it if missing |
Social templates migrated to fb.ai — fivebucks_* tools (v2.7.0) |
✅ Run Step 1h (fivebucks_list_templates) to see which types exist on fb.ai; offer missing ones via Step 3c. The old local social-meta-*-template/ folders, template_upload/template_list, and version hashes no longer apply. |
| design-system/ MANDATORY → OPTIONAL (brand-setup v2.2.15) | ❌ No action — constraint relaxed |
| _copy.json output added (content-generator / content-creation v2.2.15) | ❌ No action — additive output format |
| Naming convention split: social vs non-social (content-creation v2.2.15) | ❌ No action — future outputs only |
| Meta Ads framing reversed — Windsor.ai MANDATORY (brand-setup v2.2.13) | ✅ Confirm Windsor.ai has Meta Ads (Facebook) connected; if not → Step 3f |
| DEFAULT_BRAND + {BRAND}_NOTION_DB env vars (brand-setup v2.2.12) | ✅ Check env block for both; add if missing |
| CLAUDE.md embeds agents/link.md (brand-setup v2.2.11) | ✅ Check CLAUDE.md for embedded link.md content (BEGIN/END markers); if absent → Step 3h |
| date_preset → last_30dT (digital-marketing-analyst v2.2.8) | ❌ No action — runtime parameter, no brand config |
apollo-lead-prospector introduced (link-skills v2.4.0) |
✅ Check brands/{brand}/sales.md exists. If not → run brand-setup Step 5g sales sub-step |
outreach-sequencer introduced (link-skills v2.4.0) |
✅ Same as apollo-lead-prospector — sales.md must exist |
proposal-generator introduced (link-skills v2.4.0) |
✅ Same as apollo-lead-prospector — sales.md must exist |
customer-onboarder introduced (link-skills v2.4.0) |
✅ Check brands/{brand}/customer-success.md exists. If not → run brand-setup Step 5h |
churn-predictor introduced (link-skills v2.4.0) |
✅ Same as customer-onboarder — customer-success.md must exist |
invoice-collector introduced (link-skills v2.4.0) |
✅ Check brands/{brand}/finance.md exists. If not → run brand-setup Step 5i |
financial-reporter introduced (link-skills v2.4.0) |
✅ Same as invoice-collector — finance.md must exist |
investor-update-writer introduced (link-skills v2.4.0) |
✅ Check brands/{brand}/investors.md exists. If not → ask if brand has raised external capital; if yes → run brand-setup Step 5j |
competitor-monitor introduced (link-skills v2.4.0) |
✅ Check brands/{brand}/competitors.md has the new monitor_urls/track_pages/exec_team fields per competitor. If not → run brand-setup Step 5l (extension) |
meeting-analyzer introduced (link-skills v2.4.0) |
⏭ Optional — ask user if they want operations.md set up. If yes → brand-setup Step 5k |
digital-marketing-analyst v2.3.0 / data-analysis v2.3.0 — Windsor.ai fallback via Zernio (v2.5.0–v2.5.1) |
✅ Check ${BRAND}_ZERNIO_GOOGLE_ADS (Zernio SocialAccount _id) + ${BRAND}_ZERNIO_GOOGLE_ADS_CID (Google Ads customer ID) + ${BRAND}_ZERNIO_META_ADS_ACCOUNT_ID + ${BRAND}_ZERNIO_LINKEDIN_ADS + ${BRAND}_ZERNIO_LINKEDIN_ADS_CID in env. Both Google vars and both LinkedIn vars are required pairs for Zernio ads calls. LinkedIn vars are optional — skip if the brand doesn't run LinkedIn Ads. If missing → run Step 3f auto-discover (also handles the legacy ${BRAND}_LATE_* → ${BRAND}_ZERNIO_* rename, including ${BRAND}_ZERNIO_GOOGLE_ADS_ACCOUNT_ID/legacy ${BRAND}_LATE_GOOGLE_ADS → ${BRAND}_ZERNIO_GOOGLE_ADS) |
social-calendar YouTube-First Mode added (v2.14.0) |
✅ Check brands/{brand}/brand.md has ## Content Strategy section. If missing → run brand-setup Step 4a to define primary channel. Without it, social-calendar defaults to static mode regardless of the brand's actual publishing workflow. |
trend-radar synthesis requirement added (v2.14.0) |
✅ No brand file action required — the skill now synthesizes competitor research + web research before writing to Trend DB. Existing competitors.md already serves as the competitor input. |
gig-prospector introduced (link-skills v2.16.0) |
⏭ Optional — ask if the brand pursues freelance/marketplace work. If yes → check brands/{brand}/sales.md has a ## Inbound Job Filters section; if missing → run brand-setup Step 5g Step H (markets, platforms + account status, product.md-derived keywords, budget/exclusions/cap). ${BRAND}_GIGS_DB auto-bootstraps on first run; FREELANCER_OAUTH_TOKEN is an optional env var that enables the Freelancer.com API source. No existing skill breaks if skipped. |
Inbound Gig Engine completed (link-skills v2.17.0) |
⏭ Optional — only for brands pursuing marketplace work (those with ## Inbound Job Filters). gig-proposal-writer and vsl-demo-producer need no new connector (Notion + Slack + Claude in Chrome, already covered). n8n-workflow-builder needs the n8n Cloud MCP — if the brand wants to demo automations, run the Step 3g n8n Cloud walkthrough; optionally set ${BRAND}_N8N_PROJECT. No env var auto-bootstraps beyond ${BRAND}_GIGS_DB (already created by gig-prospector). No existing skill breaks if skipped. |
| Zernio + DataforSEO split off the gateway; Argil retired (link-skills v2.18.0) | ✅ Zernio (required): reconnect as its own MCP — Settings → Connectors → Add custom connector → https://mcp.zernio.com/mcp → sign in with your Zernio account (OAuth). Then rename existing ${BRAND}_LATE_* env vars → ${BRAND}_ZERNIO_* (Step 3f) and delete the now-unused LATE_API_KEY. Re-run account discovery (profiles_list / accounts_list) — Zernio's own API may return different SocialAccount _ids than the old gateway-proxied Late API, so don't assume they carry over. DataforSEO (optional): if the brand uses keyword research, reconnect as its own MCP — Add custom connector → https://mcp.dataforseo.com/mcp → Basic Auth with DATAFORSEO_LOGIN/DATAFORSEO_PASSWORD (no env rename needed). Gemini image model: no brand action — runtime default changed to gemini-3.1-flash-image, no env/file impact. Argil (avatar video) retired with no replacement — no action. |
For changelog entries not in this table, apply judgment: if the change touches a per-brand configuration file (brand.md, funnel.md, .claude/settings.local.json, CLAUDE.md) → flag for review. If it is a skill-internal logic change → no brand action needed.
Show the user only the rows where brand action is required:
Version-specific actions ({N} required)
✅ Direction column — checking Notion DB...
✅ Template upload check — reading brand.md Social Templates...
✅ Windsor.ai Meta Ads — re-probing connector...
Step 4 — Re-validate
After you've filled the selected gaps, the agent runs a focused validation pass — only re-testing the integrations that were touched in Step 3 (not re-running the full Step 1 inspection). The output is a summary table of what was fixed, what was skipped, and what passed validation. If any fix failed validation (e.g. you re-authorized Windsor.ai but the probe still 401s), the agent flags it for retry.
Expect 1–2 minutes. No questions unless a validation fails.
After all selected gaps are filled, run a focused validation pass — only test integrations that were touched in Step 3. Reuse the test calls from brand-setup Step 8.
4a. Show what was fixed
| Gap | Action taken | Status |
|---|---|---|
| design-system/ missing | Installed via Claude Design | ✅ |
| Locale section in brand.md | Extracted from site, appended | ✅ |
| Zernio connector not authorized | User authorized OAuth connector (no key saved/vaulted) | ✅ |
| Windsor.ai not connected | User authorized, probe passed | ✅ |
| CLAUDE.md link.md path relative | Replaced with absolute path | ✅ |
| meta-carousel template (fb.ai) | User chose to skip | ⏭ |
| ... | ... | ... |
This is the diagnostic view — useful for "what changed?" but not what the user actually wants to know.
4b. Agent Readiness Summary (business-friendly)
The "what was fixed" table answers a technical question. The owner wants the business answer: which agents will run on my brand starting today, which still need a fix, and which did I skip? Step 4b produces that answer.
Build the readiness matrix using the exact spec in brand-setup Step 8d (translation table, status rules, display format, JSON schema, and the 8d-iv rule for deriving connected_tools[] from the generated skills-manifest.json). The only difference is where the inputs come from:
| Input | brand-setup Step 8d source | plugin-update Step 4b source |
|---|---|---|
| MCP connection state | Step 8a–8c-bis probe results | Step 1e probe results, updated by any Step 3f re-probes |
| Brand context file presence | Step 5/6 generation outcomes | Step 1a inspection, updated by any Step 3a fills |
| Env vars / Notion DBs | Step 7b save + Step 8d validation | Step 1d inspection, updated by any Step 3e fills |
In other words: take the Step 1 audit, overlay every successful fix from Step 3, and compute the matrix from the post-fix state. Skills the user explicitly skipped during Step 3 (e.g. "skip Apollo, I don't run outbound") map to ⏭ in the matrix; skills that had a missing dep neither fixed nor skipped map to ❌.
Print the matrix to chat in the same exact format documented in brand-setup Step 8d-iii. Save the structured agent_readiness[] + readiness_summary object — Step 5b reads it directly into the email payload.
Do not proceed to Step 5 until the readiness matrix has been printed to chat and saved.
Step 5 — Record version + completion email + Slack
Final step — wrap up. The agent writes the current plugin version into brands/{brand}/brand.md ## Plugin Version section so future plugin-update runs know where to start their delta from. It then sends a completion email and Slack DM summarizing what was fixed, what was skipped, and any gaps that remain. After this step, the brand is fully caught up to the current plugin version.
Expect 30 seconds. No questions.
5a. Write plugin version to brand.md
Append or update the ## Plugin Version section in brands/{brand}/brand.md:
## Plugin Version
Last applied: {DEFAULT_VERSION} — {today's date}
If the section already exists, replace the Last applied: line in place — do not rewrite anything else. This is the anchor that future plugin-update runs use in Step 0b to determine the delta.
5b. Send completion email and Slack DM
Send completion email and Slack DM. Reuse the brand-setup.ts server-side template via tag: "brand-setup" — the same template handles upgrades.
Use gateway MCP tool `fiveagents_send_email`:
- fiveagents_api_key: ${FIVEAGENTS_API_KEY}
- to: ${REPORT_EMAIL}
- subject: "🔧 Plugin update complete — {brand}"
- html_body: JSON.stringify(payload)
- tag: "brand-setup"
JSON payload mirrors the brand-setup Step 10 schema:
agent_readiness[]+readiness_summary— primary block; reuse the structured object saved in Step 4b verbatim. Same schema as brand-setup. The server-side template renders this as the headline section.files[]andconnections[]— secondary diagnostic blocks; only contain items that were touched in Step 3 (not the full inventory).Add a top-level
mode: "update"field so the template (or downstream telemetry) can distinguish a fresh setup from an upgrade.Add a top-level
brand_namefield — the properly cased brand display name (e.g."Five Agents"), NOT the slug. Read it from the first#heading inbrands/{brand}/brand.mdbefore building the payload (same approach as brand-setup Step 10):import re brand_md = open(f"brands/{brand}/brand.md", encoding="utf-8").read() m = re.search(r'^#\s+(.+?)\s*$', brand_md, re.MULTILINE) brand_name = m.group(1).strip() if m else brandThe template renders
brand_namein the email title;brand(the slug) stays as a separate field for filesystem references.
Slack DM (lead with readiness, integration deltas as secondary diagnostic):
🔧 [{brand}] Plugin update applied
Agent Readiness (20 total):
✅ {N_ready} ready · ⚠️ {N_degraded} works with limitations · ❌ {N_not_ready} need fixing · ⏭ {N_skipped} skipped
{If N_not_ready > 0:}
Top fixes to unlock more agents:
• {Agent name 1} — {one-line fix from agent_readiness[].fix}
• {Agent name 2} — {one-line fix}
(full list in the email)
This update: {N} gaps fixed · {M} skipped
Detail in email
Cap the "top fixes" list at 3. If N_not_ready == 0, omit the Top fixes block; lead with "🎉 Every connected agent is configured and ready to run."
Quality Checklist
- Step 0 read
version.tsDEFAULT_VERSION and brand.md## Plugin Versionbefore doing anything - Step 0 read every skill/agent maintenance section and built the version delta table
- Step 1 ran a full inspection without prompting the user
- Step 1a checked all 5 new brand-context files (sales.md, customer-success.md, finance.md, investors.md, operations.md) with optional annotations applied to investors.md and operations.md
- Step 1d checked the 10 auto-bootstrapped DB env vars (incl. v2.13.0
${BRAND}_PERFORMANCE_DB,${BRAND}_TREND_DBand v2.16.0${BRAND}_GIGS_DB) without flagging missing ones as required gaps - Step 1a inline schema check for sales.md
## Inbound Job Filtersran; missing section offered as an optional Step 3a backfill (gig-prospector) — not flagged as a required gap - Step 1e probed all v2.4.0 / v2.2.13 / v2.17.0 / v2.18.0 MCP rows (Apollo.io, Calendly, Stripe, Xero, PostHog, Gamma, optional n8n Cloud, optional Meta Ads MCP, Zernio, optional DataforSEO)
- Step 3f walked the user through any ❌ or missing env vars, renamed any legacy
{BRAND}_LATE_*→{BRAND}_ZERNIO_*(and deletedLATE_API_KEY), and ran auto-discover for{BRAND}_ZERNIO_*vars where applicable - Step 3g walked the user through every ❌/⏭ MCP with the explicit per-MCP prompt — never silently skipped a missing connector
- Step 1i probed
fivebucks_list_media_folders(whenFIVEBUCKS_API_KEYset) and recorded media library state - Step 1k produced a version table showing which skills/agents changed since
last_applied - Step 2 gap report opened with the version delta block (updated skills listed with changelog summaries)
- Step 3 only walked through items marked ❌ or offered ⏭ — never re-asked for known-good state
- Step 3a fill handlers ran the matching brand-setup Step 5g–5l sub-step for any missing brand context file
- Step 3k mapped changelog entries to brand actions and only surfaced rows requiring action
- Step 3k changelog → brand-action mapping covered all changelog→action rows
- All file writes were patches (preserve existing content), not full rewrites
- Step 3h refreshed the
<!-- link.md version: ... -->stamp in CLAUDE.md unconditionally - Step 3h refreshed the
## Visual Systemblock in CLAUDE.md (brand-setup Step 9c equivalent) — probed design-system/, fivebucks_get_brand_kit, fivebucks_list_templates, fivebucks_list_media_folders and injected/replaced the markers - CLAUDE.md
link.mdpath is absolute (os.path.isabs == True) after the run -
.claude/settings.local.jsonretained all pre-existing keys - Re-validation in Step 4 only tested touched integrations
- Idempotent — a second immediate run produces zero gaps and
last_applied == DEFAULT_VERSION -
fivebucks_list_templatescalled in Step 1h (whenFIVEBUCKS_API_KEYset) to detect which template types exist on fb.ai - Missing template types offered via Step 3c (author in Claude Design → fb.ai dashboard upload); skips accepted
- Step 5a wrote
## Plugin Versionto brand.md with the current DEFAULT_VERSION - Agent run logged to dashboard
Final Step — Log to Dashboard
Use gateway MCP tool `fiveagents_log_run`:
- fiveagents_api_key: ${FIVEAGENTS_API_KEY}
- skill: "plugin-update"
- brand: "<active-brand>"
- status: "<success|partial|failed>"
- summary: "<1 line, <200 chars — e.g. 'Filled 5 gaps; design-system installed; Windsor.ai connected'>"
- started_at: "<ISO timestamp>"
- completed_at: "<ISO timestamp>"
- metrics: {
"date": "YYYY-MM-DD",
"plugin_version": "<from versions/version.ts DEFAULT_VERSION>",
"gaps_detected": 0,
"gaps_fixed": 0,
"gaps_skipped": 0,
"fixes": [
{ "category": "files", "item": "design-system/", "action": "installed", "status": "ok" },
{ "category": "mcp", "item": "Zernio", "action": "authorized", "status": "ok" },
{ "category": "mcp", "item": "Windsor.ai", "action": "user_connected", "status": "ok" },
{ "category": "claude_md", "item": "link.md path", "action": "replaced_with_absolute", "status": "ok" }
]
}
Status values: success (all required gaps fixed), partial (some required gaps still missing — user skipped), failed (skill errored).