OPS ► MARKETING COMMAND CENTER skill

OPS on-demand: This skill should be used when the user asks to "klaviyo", "ads spend", or…

by Lifecycle-Innovations-Limited·MIT license·★ 543 Stars on the repo·GitHub ↗

Use now

Inside this skill

SKILL.md · the author's own words
# OPS ► MARKETING COMMAND CENTER

Load `ops-rules` before acting. Public repo (no personal data). Outbound: one draft → one approval → one send. If `AskUserQuestion` / `Work…

## DNS provisioning

`bin/ops-dns-provision` is the canonical DNS surface for any marketing project — it covers every record an end-to-end SaaS launch typically…

| Subcommand                                   | What it does…
| -------------------------------------------- | ------------------------------------------------------------------------------------------…
| `gsc <project> <domain>`                     | Google Search Console site verification — fetches token via `siteVerification/v1/token`, u…
| `meta-aem <project> <domain>`                | Meta Aggregated Event Measurement — reads `verification_string` from `/me/owned_domains`,…
| `apple-pay <project> <domain>`               | Two modes via `apple_pay.mode`: `static-file` (default — surfaces the `.well-known/apple-d…
| `spf <project> <domain>`                     | Builds `v=spf1 include:... <policy>` from `.esp.spf_includes`, upserts merge-safely at ape…

Word-for-word excerpt, not a result. Read the full SKILL.md ↓

Files of OPS ► MARKETING COMMAND CENTER

Lifecycle-Innovations-Limited/main1 file shown
SKILL.md
Show the full text208 lines

OPS ► MARKETING COMMAND CENTER

Load ops-rules before acting. Public repo (no personal data). Outbound: one draft → one approval → one send. If AskUserQuestion / Workflow are missing, follow Rule 10 in ops-rules (Hermes: numbered options / two-turn Telegram card; delegate_task).

DNS provisioning

bin/ops-dns-provision is the canonical DNS surface for any marketing project — it covers every record an end-to-end SaaS launch typically needs, all routed through scripts/lib/cloudflare-dns.sh for GET-first idempotency. Re-running is safe; OPS_DRY_RUN=1 prints planned API calls without firing.

Subcommand What it does
gsc <project> <domain> Google Search Console site verification — fetches token via siteVerification/v1/token, upserts TXT at apex, calls webResource?verificationMethod=DNS_TXT to verify.
meta-aem <project> <domain> Meta Aggregated Event Measurement — reads verification_string from /me/owned_domains, upserts facebook-domain-verification=<token> TXT at apex.
apple-pay <project> <domain> Two modes via apple_pay.mode: static-file (default — surfaces the .well-known/apple-developer-merchantid-domain-association deploy-hook path) or stripe-dns (Stripe POST /v1/payment_method_domains).
spf <project> <domain> Builds v=spf1 include:... <policy> from .esp.spf_includes, upserts merge-safely at apex (refuses to overwrite a foreign TXT lacking the v=spf1 marker).
dkim <project> <domain> ESP-keyed off .esp.provider. Resend implemented (parses records[] from POST /domains, CNAME upserts). Postmark/SES stubbed.
dmarc <project> <domain> _dmarc.<apex> TXT with v=DMARC1; p=<policy>; rua=<rua>. Defaults: policy=quarantine, rua=mailto:dmarc@<apex>.
mx <project> <domain> Provider template keyed off .inbound.provider: google-workspace (smtp.google.com pri 1) / resend-inbound / ses (region from .inbound.region).
klaviyo-sending <project> <domain> Klaviyo dedicated sending domain — POST /api/dedicated-sending-domains/, CNAME upserts.
audit <project> [--json] Read-only — for each row, queries CF and reports present / absent / conflicting.
provision-all <project> [--skip <row,row>] Idempotent full sweep.

Auth: CLOUDFLARE_API_TOKEN (Bearer, preferred) or CLOUDFLARE_API_KEY + CLOUDFLARE_EMAIL (Global key, fallback). Zone lookup: GET /zones?name=<apex> finds the zone ID. Record writes: GET-first by name+type, then PUT existing ID or POST new — never duplicate.

Apex resolver (cf_apex_for) handles co.uk-style 2nd-level TLDs (foo.co.uk → foo.co.uk) and strips proto/path/port.

Preferences schema additions

The new bin reads project-level config from $PREFS_PATH under marketing.projects.<key>:

{
  "marketing": {
    "projects": {
      "myapp": {
        "domain": "example.com",

        // Outbound email service provider (DKIM + SPF includes)
        "esp": {
          "provider": "resend", // "resend" | "postmark" | "ses"
          "credentials": "doppler:prd:RESEND_API_KEY", // cred-ref: "env:VAR" | "doppler:CFG:KEY"
          "spf_includes": ["_spf.resend.com", "_spf.klaviyo.com"], // optional; sensible defaults applied
          "spf_policy": "-all", // "-all" (strict) | "~all" (soft-fail)
        },

        // Inbound mail (MX)
        "inbound": {
          "provider": "google-workspace", // "google-workspace" | "resend-inbound" | "ses"
          "region": "us-east-1", // only used by resend-inbound / ses
        },

        // DMARC policy
        "dmarc": {
          "policy": "quarantine", // "none" | "quarantine" | "reject"
          "rua": "mailto:[email protected]", // defaults to mailto:dmarc@<apex>
        },

        // Optional Cloudflare account override (for accounts that own multiple zones)
        "dns": {
          "cloudflare_account_id": "<your-cf-account-id>",
        },

        // Apple Pay domain registration
        "apple_pay": {
          "enabled": true,
          "mode": "static-file", // default; "stripe-dns" registers via Stripe
        },

        // Cred-refs for individual rows
        "stripe": {
          "secret_key": "env:STRIPE_SECRET_KEY", // legacy DNS provisioner key
          "api_key": "env:STRIPE_API_KEY", // P3: used by ops-marketing-autopilot stripe ROAS gate
          "account_id": "acct_<id>", // optional, only when calling on behalf of a connected acct
        },
        "meta": { "access_token": "env:META_ACCESS_TOKEN" },
        "klaviyo": {
          "private_key": "doppler:prd:KLAVIYO_API_KEY", // legacy DNS row
          "api_key": "doppler:prd:KLAVIYO_API_KEY", // P3: used by gather_klaviyo_metrics
          "account_id": "<klaviyo-account-id>", // P3: optional
          "sending_subdomain": "em.example.com",
        },
      },
    },
  },
}

Defaults are applied bash-side via ${var:-default}, so all values are optional except domain (required by audit and provision-all).

P3 — perf-data wiring (autopilot)

bin/ops-marketing-autopilot reads four perf-data sources per pass and persists them to ${OPS_DATA_DIR}/state/autopilot/<project>-{ga4-conversions,gsc-signal,klaviyo,stripe}.json:

Source Prefs path Helper Purpose
GA4 conversions marketing.projects.<key>.ga4.{property_id, sa_key_file_ref} gather_ga4_conversions source/medium/campaign rows for the blended bandit reward
GSC search marketing.projects.<key>.gsc.site_url gather_gsc_signal rescue + ad-copy-hook candidate buckets
Klaviyo marketing.projects.<key>.klaviyo.{api_key, account_id} gather_klaviyo_metrics Placed Order revenue + flow inventory
Stripe marketing.projects.<key>.stripe.{api_key, account_id} gather_stripe_revenue UTM-attributed revenue per source/medium/campaign and per ad_id — ground-truth ROAS denominator + pause-rescue gate

Env knobs:

Env Default Effect
OPS_BANDIT_SOURCE blended meta → meta-only reward (legacy), ga4 → GA4 attribution only, blended → (meta + ga4)/2
OPS_PAUSE_ROAS_FLOOR 1.0 An ad with stripe_revenue ≥ floor × meta_spend is kept despite Meta CPL/CTR pause criteria
OPS_KLAVIYO_REVENUE_RATIO_FLAG 0.5 When klaviyo_revenue / paid_spend > flag, surface "email channel underweighted" in the daily report (no auto-shift)

UTM enforcement: every create_object campaign … call runs utm_validate (from scripts/lib/utm-validate.sh) on the derived (utm_source, utm_medium, utm_campaign) triple before any API mutation. Non-conforming names escalate + stage-only.

Quick examples
# Dry-run every row for a project
OPS_DRY_RUN=1 ops-dns-provision provision-all myapp

# Single row, ad-hoc domain (no prefs needed)
ops-dns-provision dmarc myapp example.com

# JSON audit for CI healthcheck
ops-dns-provision audit myapp --json
# → {"project":"myapp","domain":"example.com","zone":"...","rows":{"gsc":"present","spf":"present","dmarc":"present","mx":"present","meta_aem":"absent","dkim":"unknown"}}

# Skip rows that need provider-specific manual setup first
ops-dns-provision provision-all myapp --skip dkim,klaviyo-sending

Quick start — autonomous mode

Run /ops:marketing <project> to point-and-go:

  1. ops-marketing-provision status --project <project> — what's missing
  2. For each missing channel, run ops-marketing-provision provision-<channel> --project <project> (interactive only if OAuth/keys missing; otherwise idempotent)
  3. Verify with ops-marketing-dash --project <project>
  4. If autopilot not yet enabled, enable: ops-marketing-autopilot --project <project> --first-run-dry

Provision a brand-new project end-to-end:

# One-shot: GA4 + GSC + Instagram + Google Ads — sequential, idempotent
ops-marketing-provision provision-all --project <project>

# Iterate every project in prefs
ops-marketing-provision provision-all --all-projects

# Or individually:
ops-marketing-provision provision-ga4         --project <project> \
  --domain <domain> --account-id <YOUR_GA4_ACCOUNT_ID>
ops-marketing-provision provision-gsc         --project <project> --site https://<domain>/
ops-marketing-provision provision-instagram   --project <project>   # auto-resolves via Meta token
ops-marketing-provision provision-google-ads  --project <project>   # 4-step OAuth flow

# Check results
ops-marketing-provision status --project <project> --json
ops-marketing-dash --project <project>

provision-instagram requires marketing.projects.<key>.meta.access_token (and optional meta.app_secret for appsecret_proof signing — required when the app's "Require App Secret" setting is on, which is the default for all system-user tokens). The verb is fully idempotent: smoke-tests an existing instagram.account_id before making any API calls; pass --force to re-resolve.

provision-google-ads is a 4-step flow (each step is a no-op if the credential already exists):

  1. Developer token — scans env + Doppler. If missing, writes a pending-state JSON at ${OPS_DATA_DIR}/state/marketing-provision/<project>-google-ads-pending.json and exits 1. Apply at https://ads.google.com/aw/apicenter (24–48h approval) then re-run.
  2. OAuth client — scans env + Doppler. If missing, prints Cloud Console URL for creating a Desktop OAuth client.
  3. Refresh token — launches a localhost HTTP server on :8080 (120s timeout), opens the Google consent URL, captures the auth code, exchanges for refresh_token, writes to Doppler as GOOGLE_ADS_<PROJECT_UPPER>_REFRESH_TOKEN.
  4. Customer ID — calls v24/customers:listAccessibleCustomers, auto-detects MCC manager accounts (sets login_customer_id), writes customer_id to prefs.

Pass --skip-if-pending to skip when a dev-token application is in-flight (used by provision-all to keep the chain unblocked).

Set OPS_MARKETING_DRY_RUN=1 to print planned API calls without executing.

Runtime Context

Before executing, load available context:

  1. Preferences: Read ${CLAUDE_PLUGIN_DATA_DIR:-$HOME/.claude/plugins/data/ops-ops-marketplace}/preferences.json

    • timezone — display all timestamps correctly
    • klaviyo_private_key, meta_ads_token, meta_ad_account_id, ga4_property_id, google_search_console_site — check userConfig keys before env vars
    • google_ads_developer_token, google_ads_client_id, google_ads_client_secret, google_ads_refresh_token, google_ads_customer_id, google_ads_login_customer_id — Google Ads credentials
  2. Daemon health: Read ${CLAUDE_PLUGIN_DATA_DIR}/daemon-health.json

    • If action_needed is not null → surface it before running any channel queries
  3. Secrets: Resolve API keys via userConfig → env vars → Doppler MCP (mcp__doppler__*) → Doppler CLI fallback (see Credential Resolution section below)

Additional resources

Channel, CLI, and edge-case detail lives in references/ next to this skill. Read those files before acting on a matching channel or sub-command. Do not skip them.

1---
2name: ops-marketing
3description: "OPS on-demand: This skill should be used when the user asks to \"klaviyo\", \"ads spend\", or…"
4argument-hint: '<project> [email|ads|analytics|seo|social|campaigns|setup|autopilot ...]'
5allowed-tools:
6 - Bash
7 - Read
8 - Write
9 - Grep
10 - Glob
11 - Agent
12 - TeamCreate
13 - SendMessage
14 - AskUserQuestion
15 - WebFetch
16 - WebSearch
17effort: medium
18maxTurns: 40
19context: fork
20---
21 
22# OPS ► MARKETING COMMAND CENTER
23 
24Load `ops-rules` before acting. Public repo (no personal data). Outbound: one draft → one approval → one send. If `AskUserQuestion` / `Workflow` are missing, follow Rule 10 in `ops-rules` (Hermes: numbered options / two-turn Telegram card; `delegate_task`).
25 
26## DNS provisioning
27 
28`bin/ops-dns-provision` is the canonical DNS surface for any marketing project — it covers every record an end-to-end SaaS launch typically needs, all routed through `scripts/lib/cloudflare-dns.sh` for GET-first idempotency. Re-running is safe; `OPS_DRY_RUN=1` prints planned API calls without firing.
29 
30| Subcommand | What it does |
31| -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
32| `gsc <project> <domain>` | Google Search Console site verification — fetches token via `siteVerification/v1/token`, upserts TXT at apex, calls `webResource?verificationMethod=DNS_TXT` to verify. |
33| `meta-aem <project> <domain>` | Meta Aggregated Event Measurement — reads `verification_string` from `/me/owned_domains`, upserts `facebook-domain-verification=<token>` TXT at apex. |
34| `apple-pay <project> <domain>` | Two modes via `apple_pay.mode`: `static-file` (default — surfaces the `.well-known/apple-developer-merchantid-domain-association` deploy-hook path) or `stripe-dns` (Stripe `POST /v1/payment_method_domains`). |
35| `spf <project> <domain>` | Builds `v=spf1 include:... <policy>` from `.esp.spf_includes`, upserts merge-safely at apex (refuses to overwrite a foreign TXT lacking the `v=spf1` marker). |
36| `dkim <project> <domain>` | ESP-keyed off `.esp.provider`. Resend implemented (parses `records[]` from `POST /domains`, CNAME upserts). Postmark/SES stubbed. |
37| `dmarc <project> <domain>` | `_dmarc.<apex>` TXT with `v=DMARC1; p=<policy>; rua=<rua>`. Defaults: `policy=quarantine`, `rua=mailto:dmarc@<apex>`. |
38| `mx <project> <domain>` | Provider template keyed off `.inbound.provider`: `google-workspace` (smtp.google.com pri 1) / `resend-inbound` / `ses` (region from `.inbound.region`). |
39| `klaviyo-sending <project> <domain>` | Klaviyo dedicated sending domain — `POST /api/dedicated-sending-domains/`, CNAME upserts. |
40| `audit <project> [--json]` | Read-only — for each row, queries CF and reports `present` / `absent` / `conflicting`. |
41| `provision-all <project> [--skip <row,row>]` | Idempotent full sweep. |
42 
43**Auth**: `CLOUDFLARE_API_TOKEN` (Bearer, preferred) or `CLOUDFLARE_API_KEY` + `CLOUDFLARE_EMAIL` (Global key, fallback). Zone lookup: `GET /zones?name=<apex>` finds the zone ID. Record writes: GET-first by `name+type`, then PUT existing ID or POST new — never duplicate.
44 
45**Apex resolver** (`cf_apex_for`) handles co.uk-style 2nd-level TLDs (foo.co.uk → foo.co.uk) and strips proto/path/port.
46 
47### Preferences schema additions
48 
49The new bin reads project-level config from `$PREFS_PATH` under `marketing.projects.<key>`:
50 
51```jsonc
52{
53 "marketing": {
54 "projects": {
55 "myapp": {
56 "domain": "example.com",
57 
58 // Outbound email service provider (DKIM + SPF includes)
59 "esp": {
60 "provider": "resend", // "resend" | "postmark" | "ses"
61 "credentials": "doppler:prd:RESEND_API_KEY", // cred-ref: "env:VAR" | "doppler:CFG:KEY"
62 "spf_includes": ["_spf.resend.com", "_spf.klaviyo.com"], // optional; sensible defaults applied
63 "spf_policy": "-all", // "-all" (strict) | "~all" (soft-fail)
64 },
65 
66 // Inbound mail (MX)
67 "inbound": {
68 "provider": "google-workspace", // "google-workspace" | "resend-inbound" | "ses"
69 "region": "us-east-1", // only used by resend-inbound / ses
70 },
71 
72 // DMARC policy
73 "dmarc": {
74 "policy": "quarantine", // "none" | "quarantine" | "reject"
75 "rua": "mailto:[email protected]", // defaults to mailto:dmarc@<apex>
76 },
77 
78 // Optional Cloudflare account override (for accounts that own multiple zones)
79 "dns": {
80 "cloudflare_account_id": "<your-cf-account-id>",
81 },
82 
83 // Apple Pay domain registration
84 "apple_pay": {
85 "enabled": true,
86 "mode": "static-file", // default; "stripe-dns" registers via Stripe
87 },
88 
89 // Cred-refs for individual rows
90 "stripe": {
91 "secret_key": "env:STRIPE_SECRET_KEY", // legacy DNS provisioner key
92 "api_key": "env:STRIPE_API_KEY", // P3: used by ops-marketing-autopilot stripe ROAS gate
93 "account_id": "acct_<id>", // optional, only when calling on behalf of a connected acct
94 },
95 "meta": { "access_token": "env:META_ACCESS_TOKEN" },
96 "klaviyo": {
97 "private_key": "doppler:prd:KLAVIYO_API_KEY", // legacy DNS row
98 "api_key": "doppler:prd:KLAVIYO_API_KEY", // P3: used by gather_klaviyo_metrics
99 "account_id": "<klaviyo-account-id>", // P3: optional
100 "sending_subdomain": "em.example.com",
101 },
102 },
103 },
104 },
105}
106```
107 
108Defaults are applied bash-side via `${var:-default}`, so all values are optional except `domain` (required by `audit` and `provision-all`).
109 
110### P3 — perf-data wiring (autopilot)
111 
112`bin/ops-marketing-autopilot` reads four perf-data sources per pass and persists them to `${OPS_DATA_DIR}/state/autopilot/<project>-{ga4-conversions,gsc-signal,klaviyo,stripe}.json`:
113 
114| Source | Prefs path | Helper | Purpose |
115| --------------- | ------------------------------------------------------------- | ------------------------ | ----------------------------------------------------------------------------------------------------------------------- |
116| GA4 conversions | `marketing.projects.<key>.ga4.{property_id, sa_key_file_ref}` | `gather_ga4_conversions` | source/medium/campaign rows for the blended bandit reward |
117| GSC search | `marketing.projects.<key>.gsc.site_url` | `gather_gsc_signal` | rescue + ad-copy-hook candidate buckets |
118| Klaviyo | `marketing.projects.<key>.klaviyo.{api_key, account_id}` | `gather_klaviyo_metrics` | Placed Order revenue + flow inventory |
119| Stripe | `marketing.projects.<key>.stripe.{api_key, account_id}` | `gather_stripe_revenue` | UTM-attributed revenue per `source/medium/campaign` and per `ad_id` — ground-truth ROAS denominator + pause-rescue gate |
120 
121Env knobs:
122 
123| Env | Default | Effect |
124| -------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------- |
125| `OPS_BANDIT_SOURCE` | `blended` | `meta` → meta-only reward (legacy), `ga4` → GA4 attribution only, `blended` → `(meta + ga4)/2` |
126| `OPS_PAUSE_ROAS_FLOOR` | `1.0` | An ad with `stripe_revenue ≥ floor × meta_spend` is kept despite Meta CPL/CTR pause criteria |
127| `OPS_KLAVIYO_REVENUE_RATIO_FLAG` | `0.5` | When `klaviyo_revenue / paid_spend > flag`, surface "email channel underweighted" in the daily report (no auto-shift) |
128 
129UTM enforcement: every `create_object campaign …` call runs `utm_validate` (from `scripts/lib/utm-validate.sh`) on the derived `(utm_source, utm_medium, utm_campaign)` triple before any API mutation. Non-conforming names escalate + stage-only.
130 
131### Quick examples
132 
133```bash
134# Dry-run every row for a project
135OPS_DRY_RUN=1 ops-dns-provision provision-all myapp
136 
137# Single row, ad-hoc domain (no prefs needed)
138ops-dns-provision dmarc myapp example.com
139 
140# JSON audit for CI healthcheck
141ops-dns-provision audit myapp --json
142# → {"project":"myapp","domain":"example.com","zone":"...","rows":{"gsc":"present","spf":"present","dmarc":"present","mx":"present","meta_aem":"absent","dkim":"unknown"}}
143 
144# Skip rows that need provider-specific manual setup first
145ops-dns-provision provision-all myapp --skip dkim,klaviyo-sending
146```
147 
148## Quick start — autonomous mode
149 
150Run `/ops:marketing <project>` to point-and-go:
151 
1521. `ops-marketing-provision status --project <project>` — what's missing
1532. For each missing channel, run `ops-marketing-provision provision-<channel> --project <project>` (interactive only if OAuth/keys missing; otherwise idempotent)
1543. Verify with `ops-marketing-dash --project <project>`
1554. If autopilot not yet enabled, enable: `ops-marketing-autopilot --project <project> --first-run-dry`
156 
157Provision a brand-new project end-to-end:
158 
159```bash
160# One-shot: GA4 + GSC + Instagram + Google Ads — sequential, idempotent
161ops-marketing-provision provision-all --project <project>
162 
163# Iterate every project in prefs
164ops-marketing-provision provision-all --all-projects
165 
166# Or individually:
167ops-marketing-provision provision-ga4 --project <project> \
168 --domain <domain> --account-id <YOUR_GA4_ACCOUNT_ID>
169ops-marketing-provision provision-gsc --project <project> --site https://<domain>/
170ops-marketing-provision provision-instagram --project <project> # auto-resolves via Meta token
171ops-marketing-provision provision-google-ads --project <project> # 4-step OAuth flow
172 
173# Check results
174ops-marketing-provision status --project <project> --json
175ops-marketing-dash --project <project>
176```
177 
178`provision-instagram` requires `marketing.projects.<key>.meta.access_token` (and optional `meta.app_secret` for `appsecret_proof` signing — required when the app's "Require App Secret" setting is on, which is the default for all system-user tokens). The verb is fully idempotent: smoke-tests an existing `instagram.account_id` before making any API calls; pass `--force` to re-resolve.
179 
180`provision-google-ads` is a 4-step flow (each step is a no-op if the credential already exists):
181 
1821. **Developer token** — scans env + Doppler. If missing, writes a pending-state JSON at `${OPS_DATA_DIR}/state/marketing-provision/<project>-google-ads-pending.json` and exits 1. Apply at <https://ads.google.com/aw/apicenter> (24–48h approval) then re-run.
1832. **OAuth client** — scans env + Doppler. If missing, prints Cloud Console URL for creating a Desktop OAuth client.
1843. **Refresh token** — launches a localhost HTTP server on `:8080` (120s timeout), opens the Google consent URL, captures the auth code, exchanges for `refresh_token`, writes to Doppler as `GOOGLE_ADS_<PROJECT_UPPER>_REFRESH_TOKEN`.
1854. **Customer ID** — calls `v24/customers:listAccessibleCustomers`, auto-detects MCC manager accounts (sets `login_customer_id`), writes `customer_id` to prefs.
186 
187Pass `--skip-if-pending` to skip when a dev-token application is in-flight (used by `provision-all` to keep the chain unblocked).
188 
189Set `OPS_MARKETING_DRY_RUN=1` to print planned API calls without executing.
190 
191## Runtime Context
192 
193Before executing, load available context:
194 
1951. **Preferences**: Read `${CLAUDE_PLUGIN_DATA_DIR:-$HOME/.claude/plugins/data/ops-ops-marketplace}/preferences.json`
196 - `timezone` — display all timestamps correctly
197 - `klaviyo_private_key`, `meta_ads_token`, `meta_ad_account_id`, `ga4_property_id`, `google_search_console_site` — check userConfig keys before env vars
198 - `google_ads_developer_token`, `google_ads_client_id`, `google_ads_client_secret`, `google_ads_refresh_token`, `google_ads_customer_id`, `google_ads_login_customer_id` — Google Ads credentials
199 
2002. **Daemon health**: Read `${CLAUDE_PLUGIN_DATA_DIR}/daemon-health.json`
201 - If `action_needed` is not null → surface it before running any channel queries
202 
2033. **Secrets**: Resolve API keys via userConfig → env vars → Doppler MCP (`mcp__doppler__*`) → Doppler CLI fallback (see Credential Resolution section below)
204 
205## Additional resources
206 
207Channel, CLI, and edge-case detail lives in `references/` next to this skill. Read those files before acting on a matching channel or sub-command. Do not skip them.
208 

Discussion