OPS ► MARKETING COMMAND CENTER Runtime Context Before executing, load available context: 1. Preferences : Read - — display all timestamps correctly - , , , , — check userConfig keys before env vars - , , , , , — Google Ads credentials 2. Daemon health : Read - If is not null → surface it before running any channel queries 3. Secrets : Resolve API keys via userConfig → env vars → Doppler MCP ( ) → Doppler CLI fallback (see Credential Resolution section below) CLI/API Reference Klaviyo REST API | Endpoint | Method | Description | |----------|--------|-------------| | | GET | All lists + subscri…

\\t' read -r page device users; do\n printf \"| %-40s | %-8s | %-7s |\\n\" \"${page:0:40}\" \"$device\" \"$users\"\n done\n```\n\n### funnel\n\nAsk user for funnel steps via AskUserQuestion (free text). Default template uses session_start → page_view → purchase.\n\n```bash\n# NOTE: Uses v1alpha — breaking changes possible per Google's versioning policy\nGA4_TOKEN=$(gcloud auth application-default print-access-token 2>/dev/null)\n\n# Prompt for funnel type first\n# AskUserQuestion: \"Funnel mode?\" options [Closed funnel, Open funnel]\n\nIS_OPEN=$([ \"$FUNNEL_MODE\" = \"Open funnel\" ] && echo \"true\" || echo \"false\")\n\nRESULT=$(curl -s -X POST \"https://analyticsdata.googleapis.com/v1alpha/properties/${GA4_PROPERTY}:runFunnelReport\" \\\n -H \"Authorization: Bearer ${GA4_TOKEN}\" \\\n -H \"Content-Type: application/json\" \\\n -d \"{\n \\\"dateRanges\\\": [{\\\"startDate\\\": \\\"30daysAgo\\\", \\\"endDate\\\": \\\"today\\\"}],\n \\\"funnel\\\": {\n \\\"isOpenFunnel\\\": ${IS_OPEN},\n \\\"steps\\\": [\n {\n \\\"name\\\": \\\"Session Start\\\",\n \\\"filterExpression\\\": {\\\"funnelEventFilter\\\": {\\\"eventName\\\": \\\"session_start\\\"}}\n },\n {\n \\\"name\\\": \\\"Page View\\\",\n \\\"filterExpression\\\": {\\\"funnelEventFilter\\\": {\\\"eventName\\\": \\\"page_view\\\"}}\n },\n {\n \\\"name\\\": \\\"Purchase\\\",\n \\\"filterExpression\\\": {\\\"funnelEventFilter\\\": {\\\"eventName\\\": \\\"purchase\\\"}}\n }\n ]\n },\n \\\"funnelBreakdown\\\": {\n \\\"breakdownDimension\\\": {\\\"name\\\": \\\"deviceCategory\\\"},\n \\\"limit\\\": 4\n }\n }\")\n\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \" GA4 FUNNEL — Last 30 Days (${FUNNEL_MODE})\"\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \"\"\necho \"$RESULT\" | jq -r '\n .funnelTable.rows[]? |\n \"Step: \\(.dimensionValues[0].value) Users: \\(.metricValues[0].value) Completion: \\(.metricValues[1].value)% Abandoned: \\(.metricValues[2].value)\"\n' 2>/dev/null || echo \"No funnel data — ensure purchase events are firing in GA4.\"\n```\n\n### cohort\n\nWeekly cohort retention for users acquired in the past month, broken down by device.\n\n```bash\nGA4_TOKEN=$(gcloud auth application-default print-access-token 2>/dev/null)\nSTART_DATE=$(date -v-30d +%Y-%m-%d 2>/dev/null || date -d '30 days ago' +%Y-%m-%d)\nEND_DATE=$(date +%Y-%m-%d)\n\nRESULT=$(curl -s -X POST \"https://analyticsdata.googleapis.com/v1beta/properties/${GA4_PROPERTY}:runReport\" \\\n -H \"Authorization: Bearer ${GA4_TOKEN}\" \\\n -H \"Content-Type: application/json\" \\\n -d \"{\n \\\"dimensions\\\": [\n {\\\"name\\\": \\\"cohort\\\"},\n {\\\"name\\\": \\\"cohortNthWeek\\\"},\n {\\\"name\\\": \\\"deviceCategory\\\"}\n ],\n \\\"metrics\\\": [\n {\\\"name\\\": \\\"cohortActiveUsers\\\"},\n {\\\"name\\\": \\\"cohortRetentionFraction\\\"}\n ],\n \\\"cohortSpec\\\": {\n \\\"cohorts\\\": [{\n \\\"dimension\\\": \\\"firstSessionDate\\\",\n \\\"dateRange\\\": {\\\"startDate\\\": \\\"${START_DATE}\\\", \\\"endDate\\\": \\\"${END_DATE}\\\"}\n }],\n \\\"cohortsRange\\\": {\n \\\"granularity\\\": \\\"WEEKLY\\\",\n \\\"startOffset\\\": 0,\n \\\"endOffset\\\": 5\n }\n }\n }\")\n\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \" GA4 COHORT RETENTION — Last 30 Days\"\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \"\"\nprintf \"| %-12s | %-6s | %-8s | %-10s | %-10s |\\n\" \"Cohort\" \"Week\" \"Device\" \"Users\" \"Retention%\"\nprintf \"|%s|%s|%s|%s|%s|\\n\" \"--------------\" \"--------\" \"----------\" \"------------\" \"------------\"\necho \"$RESULT\" | jq -r '.rows[]? | [\n .dimensionValues[0].value,\n .dimensionValues[1].value,\n .dimensionValues[2].value,\n .metricValues[0].value,\n (.metricValues[1].value | tonumber * 100 | tostring | split(\".\")[0])\n] | @tsv' 2>/dev/null | \\\n while IFS=

OPS ► MARKETING COMMAND CENTER Runtime Context Before executing, load available context: 1. Preferences : Read - — display all timestamps correctly - , , , , — check userConfig keys before env vars - , , , , , — Google Ads credentials 2. Daemon health : Read - If is not null → surface it before running any channel queries 3. Secrets : Resolve API keys via userConfig → env vars → Doppler MCP ( ) → Doppler CLI fallback (see Credential Resolution section below) CLI/API Reference Klaviyo REST API | Endpoint | Method | Description | |----------|--------|-------------| | | GET | All lists + subscri…

\\t' read -r cohort week device users retention; do\n printf \"| %-12s | %-6s | %-8s | %-10s | %-10s |\\n\" \"$cohort\" \"$week\" \"$device\" \"$users\" \"${retention}%\"\n done\n```\n\n### audience\n\nAsync audience export: create → poll until ACTIVE → show user count.\n\n```bash\nGA4_TOKEN=$(gcloud auth application-default print-access-token 2>/dev/null)\n\n# Step 1: List available audiences so user can pick one\nAUDIENCES=$(curl -s \"https://analyticsadmin.googleapis.com/v1alpha/properties/${GA4_PROPERTY}/audiences\" \\\n -H \"Authorization: Bearer ${GA4_TOKEN}\")\necho \"Available audiences:\"\necho \"$AUDIENCES\" | jq -r '.audiences[]? | \"\\(.name | split(\"/\") | last): \\(.displayName)\"' 2>/dev/null\n\n# AskUserQuestion: \"Enter audience ID from list above:\" (free text)\n\n# Step 2: Create export\nEXPORT_RESP=$(curl -s -X POST \\\n \"https://analyticsdata.googleapis.com/v1beta/properties/${GA4_PROPERTY}/audienceExports\" \\\n -H \"Authorization: Bearer ${GA4_TOKEN}\" \\\n -H \"Content-Type: application/json\" \\\n -d \"{\n \\\"audience\\\": \\\"properties/${GA4_PROPERTY}/audiences/${AUDIENCE_ID}\\\",\n \\\"dimensions\\\": [\n {\\\"dimensionName\\\": \\\"deviceId\\\"},\n {\\\"dimensionName\\\": \\\"isAdsPersonalizationAllowed\\\"}\n ]\n }\")\nEXPORT_NAME=$(echo \"$EXPORT_RESP\" | jq -r '.name // empty')\n\nif [ -z \"$EXPORT_NAME\" ]; then\n echo \"Failed to create export: $(echo \"$EXPORT_RESP\" | jq -r '.error.message // \"unknown error\"')\"\n exit 0\nfi\n\necho \"Export created: ${EXPORT_NAME}\"\necho \"Polling for completion (small audiences: ~30s, large: up to 15 min)...\"\n\n# Step 3: Poll until ACTIVE or FAILED\nATTEMPTS=0\nwhile [ $ATTEMPTS -lt 60 ]; do\n STATUS_RESP=$(curl -s \"https://analyticsdata.googleapis.com/v1beta/${EXPORT_NAME}\" \\\n -H \"Authorization: Bearer ${GA4_TOKEN}\")\n STATE=$(echo \"$STATUS_RESP\" | jq -r '.state // \"UNKNOWN\"')\n PCT=$(echo \"$STATUS_RESP\" | jq -r '.percentageCompleted // 0')\n \n if [ \"$STATE\" = \"ACTIVE\" ]; then break; fi\n if [ \"$STATE\" = \"FAILED\" ]; then\n echo \"Export failed. Try again or check GA4 audience configuration.\"\n exit 0\n fi\n echo \" State: ${STATE} (${PCT}% complete)...\"\n sleep 10\n ATTEMPTS=$((ATTEMPTS + 1))\ndone\n\n# Step 4: Query results\nQUERY_RESP=$(curl -s -X POST \\\n \"https://analyticsdata.googleapis.com/v1beta/${EXPORT_NAME}:query\" \\\n -H \"Authorization: Bearer ${GA4_TOKEN}\")\nROW_COUNT=$(echo \"$QUERY_RESP\" | jq '.rowCount // 0')\n\necho \"\"\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \" GA4 AUDIENCE EXPORT\"\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \" Audience ID: ${AUDIENCE_ID}\"\necho \" Users exported: ${ROW_COUNT}\"\necho \" Ads-eligible: $(echo \"$QUERY_RESP\" | jq '[.audienceRows[]? | select(.dimensionValues[1].value == \"true\")] | length') users\"\necho \"\"\necho \"Export ready. Use this audience for retargeting in Meta or Google Ads.\"\n```\n\n### pivot\n\nMulti-dimensional pivot: channel group × device category × conversions.\n\n```bash\nGA4_TOKEN=$(gcloud auth application-default print-access-token 2>/dev/null)\n\nRESULT=$(curl -s -X POST \"https://analyticsdata.googleapis.com/v1beta/properties/${GA4_PROPERTY}:runPivotReport\" \\\n -H \"Authorization: Bearer ${GA4_TOKEN}\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"dateRanges\": [{\"startDate\": \"30daysAgo\", \"endDate\": \"today\"}],\n \"dimensions\": [\n {\"name\": \"sessionDefaultChannelGrouping\"},\n {\"name\": \"deviceCategory\"}\n ],\n \"metrics\": [\n {\"name\": \"sessions\"},\n {\"name\": \"conversions\"},\n {\"name\": \"totalRevenue\"}\n ],\n \"pivots\": [\n {\n \"fieldNames\": [\"sessionDefaultChannelGrouping\"],\n \"limit\": 6\n },\n {\n \"fieldNames\": [\"deviceCategory\"],\n \"limit\": 3\n }\n ]\n }')\n\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \" GA4 PIVOT — Channel × Device (Last 30 Days)\"\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \"\"\nprintf \"| %-20s | %-8s | %-10s | %-11s | %-10s |\\n\" \"Channel\" \"Device\" \"Sessions\" \"Conversions\" \"Revenue\"\nprintf \"|%s|%s|%s|%s|%s|\\n\" \"----------------------\" \"----------\" \"------------\" \"-------------\" \"------------\"\necho \"$RESULT\" | jq -r '.rows[]? | [\n .dimensionValues[0].value,\n .dimensionValues[1].value,\n .metricValues[0].value,\n .metricValues[1].value,\n (.metricValues[2].value | tonumber | . * 100 | round / 100 | tostring)\n] | @tsv' 2>/dev/null | \\\n while IFS=

OPS ► MARKETING COMMAND CENTER Runtime Context Before executing, load available context: 1. Preferences : Read - — display all timestamps correctly - , , , , — check userConfig keys before env vars - , , , , , — Google Ads credentials 2. Daemon health : Read - If is not null → surface it before running any channel queries 3. Secrets : Resolve API keys via userConfig → env vars → Doppler MCP ( ) → Doppler CLI fallback (see Credential Resolution section below) CLI/API Reference Klaviyo REST API | Endpoint | Method | Description | |----------|--------|-------------| | | GET | All lists + subscri…

\\t' read -r channel device sessions convs revenue; do\n printf \"| %-20s | %-8s | %-10s | %-11s | \\$%-9s |\\n\" \"${channel:0:20}\" \"$device\" \"$sessions\" \"$convs\" \"$revenue\"\n done\n```\n\n---\n\n## seo / gsc\n\nPull Google Search Console data.\n\n### Get access token (same gcloud ADC)\n```bash\nGSC_TOKEN=$(gcloud auth application-default print-access-token 2>/dev/null)\nGSC_SITE_ENCODED=$(python3 -c \"import urllib.parse; print(urllib.parse.quote('${GSC_SITE}', safe=''))\" 2>/dev/null || echo \"${GSC_SITE}\" | sed 's|:|%3A|g; s|/|%2F|g')\n```\n\n### Search performance (last 28 days)\n```bash\ncurl -s -X POST \"https://searchconsole.googleapis.com/webmasters/v3/sites/${GSC_SITE_ENCODED}/searchAnalytics/query\" \\\n -H \"Authorization: Bearer ${GSC_TOKEN}\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"startDate\": \"'$(date -v-28d +%Y-%m-%d 2>/dev/null || date -d '28 days ago' +%Y-%m-%d)'\",\n \"endDate\": \"'$(date +%Y-%m-%d)'\",\n \"dimensions\": [],\n \"rowLimit\": 1\n }' | jq '{clicks: .rows[0].clicks, impressions: .rows[0].impressions, ctr: .rows[0].ctr, position: .rows[0].position}'\n```\n\n### Top queries (last 28 days)\n```bash\ncurl -s -X POST \"https://searchconsole.googleapis.com/webmasters/v3/sites/${GSC_SITE_ENCODED}/searchAnalytics/query\" \\\n -H \"Authorization: Bearer ${GSC_TOKEN}\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"startDate\": \"'$(date -v-28d +%Y-%m-%d 2>/dev/null || date -d '28 days ago' +%Y-%m-%d)'\",\n \"endDate\": \"'$(date +%Y-%m-%d)'\",\n \"dimensions\": [\"query\"],\n \"rowLimit\": 20,\n \"dimensionFilterGroups\": []\n }' | jq '.rows[] | {query: .keys[0], clicks: .clicks, impressions: .impressions, position: (.position | floor)}'\n```\n\n### Top pages by clicks\n```bash\ncurl -s -X POST \"https://searchconsole.googleapis.com/webmasters/v3/sites/${GSC_SITE_ENCODED}/searchAnalytics/query\" \\\n -H \"Authorization: Bearer ${GSC_TOKEN}\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"startDate\": \"'$(date -v-28d +%Y-%m-%d 2>/dev/null || date -d '28 days ago' +%Y-%m-%d)'\",\n \"endDate\": \"'$(date +%Y-%m-%d)'\",\n \"dimensions\": [\"page\"],\n \"rowLimit\": 10\n }' | jq '.rows[] | {page: .keys[0], clicks: .clicks, impressions: .impressions, position: (.position | floor)}'\n```\n\nIf GSC not configured, output: `Search Console not configured — run /ops:marketing setup`.\n\n### Output format\n```\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n SEO (SEARCH CONSOLE) — last 28d\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n Clicks: [N]\n Impressions: [N]\n CTR: [X]%\n Avg Position: [X]\n\n TOP QUERIES\n [query] [clicks] clicks pos [N]\n ...\n\n TOP PAGES\n [url] [clicks] clicks [impressions] impr\n```\n\n---\n\n## social\n\nAggregate available social media metrics. Check which are configured.\n\n### Instagram (via Meta Graph API — same token as Meta Ads)\n```bash\n# Get Instagram Business Account ID linked to the ad account\ncurl -s \"https://graph.facebook.com/v18.0/me/accounts?fields=instagram_business_account\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" | jq '.data[].instagram_business_account.id' 2>/dev/null\n\n# Then pull media insights\ncurl -s \"https://graph.facebook.com/v18.0/${IG_ACCOUNT_ID}?fields=followers_count,media_count,profile_views\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" | jq '{followers: .followers_count, posts: .media_count, profile_views: .profile_views}'\n```\n\n### YouTube (if configured via gcloud)\n```bash\nYT_TOKEN=$(gcloud auth application-default print-access-token 2>/dev/null)\ncurl -s \"https://www.googleapis.com/youtube/v3/channels?part=statistics&mine=true\" \\\n -H \"Authorization: Bearer ${YT_TOKEN}\" | jq '.items[0].statistics | {subscribers: .subscriberCount, views: .viewCount, videos: .videoCount}'\n```\n\nShow `[not configured]` for any unconfigured channels rather than failing.\n\n### Output format\n```\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n SOCIAL MEDIA\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n Instagram: [N followers] [N posts] [N profile views]\n YouTube: [N subscribers] [N total views]\n TikTok: [not configured] — set TIKTOK_ACCESS_TOKEN\n```\n\n---\n\n## instagram\n\nInstagram publishing and insights via Instagram Graph API (same `META_TOKEN` as Meta Ads).\n\n**Prerequisites:**\n- `META_TOKEN` configured (same as Meta Ads)\n- Instagram Business account linked to a Facebook Page\n- `IG_ACCOUNT_ID` resolved via: `curl \"https://graph.facebook.com/v21.0/me/accounts?fields=instagram_business_account\" -H \"Authorization: Bearer ${META_TOKEN}\"` → `data[0].instagram_business_account.id`\n\n**Rate limit:** 200 API calls/hour per app. Demographics require 48h reporting delay. Media insights require account with >1,000 followers.\n\n**Resolve IG account ID at the start of every instagram invocation:**\n```bash\nIG_ACCOUNT_ID=$(claude plugin config get instagram_account_id 2>/dev/null)\nif [ -z \"$IG_ACCOUNT_ID\" ]; then\n IG_ACCOUNT_ID=$(curl -s \"https://graph.facebook.com/v21.0/me/accounts?fields=instagram_business_account\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" | jq -r '.data[0].instagram_business_account.id // empty')\n # Cache it\n [ -n \"$IG_ACCOUNT_ID\" ] && claude plugin config set instagram_account_id \"$IG_ACCOUNT_ID\" 2>/dev/null\nfi\nif [ -z \"$IG_ACCOUNT_ID\" ]; then\n echo \"Instagram Business account not linked to your Meta token. Ensure your Facebook Page has an Instagram Business account connected.\"\n exit 0\nfi\n```\n\nRoute `$ARGUMENTS` within instagram:\n\n| Input | Action |\n|---|---|\n| post \\\u003cIMAGE_URL\\> | Publish image post to feed |\n| reel \\\u003cVIDEO_URL\\> | Publish a Reel |\n| story \\\u003cIMAGE_URL\\|VIDEO_URL\\> | Publish a Story |\n| insights \\\u003cMEDIA_ID\\> | Per-post metrics |\n| account-insights [days] | Account-level reach + impressions |\n| demographics | Audience age/gender/location |\n\n### post\n\nPublish an image post (two-step: create container → publish).\n\nCollect via AskUserQuestion:\n1. Image URL (publicly accessible HTTPS URL) — free text\n2. Caption — free text\n\n```bash\n# Step 1: Create media container\nCONTAINER=$(curl -s -X POST \"https://graph.facebook.com/v21.0/${IG_ACCOUNT_ID}/media\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" \\\n -F \"image_url=${IMAGE_URL}\" \\\n -F \"caption=${CAPTION}\" \\\n -F \"media_type=IMAGE\")\nCONTAINER_ID=$(echo \"$CONTAINER\" | jq -r '.id // empty')\n\nif [ -z \"$CONTAINER_ID\" ]; then\n echo \"Failed to create media container: $(echo \"$CONTAINER\" | jq -r '.error.message // \"unknown error\"')\"\n exit 0\nfi\n\n# Step 2: Publish\nPUBLISH=$(curl -s -X POST \"https://graph.facebook.com/v21.0/${IG_ACCOUNT_ID}/media_publish\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" \\\n -F \"creation_id=${CONTAINER_ID}\")\nMEDIA_ID=$(echo \"$PUBLISH\" | jq -r '.id // empty')\n\nif [ -n \"$MEDIA_ID\" ]; then\n echo \"Post published (Media ID: ${MEDIA_ID}). View at https://www.instagram.com/ — may take 1-2 min to appear.\"\nelse\n echo \"Publish failed: $(echo \"$PUBLISH\" | jq -r '.error.message // \"unknown error\"')\"\nfi\n```\n\n### reel\n\nPublish a Reel. Video must be an HTTPS URL (MP4, H.264, max 15 min, min 500px width).\n\nCollect via AskUserQuestion:\n1. Video URL (HTTPS) — free text\n2. Caption — free text\n\n```bash\n# Step 1: Create video container (async — must poll for status)\nCONTAINER=$(curl -s -X POST \"https://graph.facebook.com/v21.0/${IG_ACCOUNT_ID}/media\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" \\\n -F \"media_type=REELS\" \\\n -F \"video_url=${VIDEO_URL}\" \\\n -F \"caption=${CAPTION}\" \\\n -F \"share_to_feed=true\")\nCONTAINER_ID=$(echo \"$CONTAINER\" | jq -r '.id // empty')\n\nif [ -z \"$CONTAINER_ID\" ]; then\n echo \"Failed to create Reel container: $(echo \"$CONTAINER\" | jq -r '.error.message // \"unknown error\"')\"\n exit 0\nfi\n\necho \"Reel uploading... polling for ready status.\"\n\n# Poll until status is FINISHED\nATTEMPTS=0\nwhile [ $ATTEMPTS -lt 30 ]; do\n STATUS=$(curl -s \"https://graph.facebook.com/v21.0/${CONTAINER_ID}?fields=status_code\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" | jq -r '.status_code // \"UNKNOWN\"')\n [ \"$STATUS\" = \"FINISHED\" ] && break\n [ \"$STATUS\" = \"ERROR\" ] && echo \"Reel processing failed.\" && exit 0\n sleep 10\n ATTEMPTS=$((ATTEMPTS + 1))\ndone\n\n# Step 2: Publish\nPUBLISH=$(curl -s -X POST \"https://graph.facebook.com/v21.0/${IG_ACCOUNT_ID}/media_publish\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" \\\n -F \"creation_id=${CONTAINER_ID}\")\nMEDIA_ID=$(echo \"$PUBLISH\" | jq -r '.id // empty')\n\nif [ -n \"$MEDIA_ID\" ]; then\n echo \"Reel published (Media ID: ${MEDIA_ID}). Reach and plays metrics available after 24-48h.\"\nelse\n echo \"Reel publish failed: $(echo \"$PUBLISH\" | jq -r '.error.message // \"unknown error\"')\"\nfi\n```\n\n### story\n\nPublish a Story (image or video, 24h expiry).\n\nCollect via AskUserQuestion:\n1. Content URL (HTTPS image or video) — free text\n2. Content type: `[Image story, Video story]`\n\n```bash\nif [ \"$CONTENT_TYPE\" = \"Video story\" ]; then\n MEDIA_TYPE=\"VIDEO\"\n URL_FIELD=\"video_url\"\nelse\n MEDIA_TYPE=\"IMAGE\"\n URL_FIELD=\"image_url\"\nfi\n\nCONTAINER=$(curl -s -X POST \"https://graph.facebook.com/v21.0/${IG_ACCOUNT_ID}/media\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" \\\n -F \"media_type=STORIES\" \\\n -F \"${URL_FIELD}=${CONTENT_URL}\")\nCONTAINER_ID=$(echo \"$CONTAINER\" | jq -r '.id // empty')\n\nif [ -z \"$CONTAINER_ID\" ]; then\n echo \"Failed to create Story container: $(echo \"$CONTAINER\" | jq -r '.error.message // \"unknown error\"')\"\n exit 0\nfi\n\nPUBLISH=$(curl -s -X POST \"https://graph.facebook.com/v21.0/${IG_ACCOUNT_ID}/media_publish\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" \\\n -F \"creation_id=${CONTAINER_ID}\")\nMEDIA_ID=$(echo \"$PUBLISH\" | jq -r '.id // empty')\n\nif [ -n \"$MEDIA_ID\" ]; then\n echo \"Story published (Media ID: ${MEDIA_ID}). Expires after 24 hours.\"\nelse\n echo \"Story publish failed: $(echo \"$PUBLISH\" | jq -r '.error.message // \"unknown error\"')\"\nfi\n```\n\n### insights \\\u003cMEDIA_ID\\>\n\nPer-post metrics. Note: reach, saves, shares deprecated for non-Reels video; plays only for Reels/video.\n\n```bash\nMETRICS=\"reach,saved,shares,comments_count,like_count,impressions\"\n# For Reels, add plays: detect via media_type field\nMEDIA_TYPE=$(curl -s \"https://graph.facebook.com/v21.0/${MEDIA_ID}?fields=media_type\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" | jq -r '.media_type // \"IMAGE\"')\n[ \"$MEDIA_TYPE\" = \"VIDEO\" ] || [ \"$MEDIA_TYPE\" = \"REEL\" ] && METRICS=\"${METRICS},plays\"\n\nRESULT=$(curl -s \"https://graph.facebook.com/v21.0/${MEDIA_ID}/insights?metric=${METRICS}&period=lifetime\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\")\n\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \" INSTAGRAM POST INSIGHTS — ${MEDIA_ID}\"\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \"\"\necho \"$RESULT\" | jq -r '.data[]? | \" \\(.name): \\(.values[0].value)\"' 2>/dev/null || \\\n echo \"No insights data — account must have >1,000 followers for insights access.\"\n```\n\n### account-insights [days]\n\nAccount-level reach and impressions. Default: last 7 days.\n\n```bash\nDAYS=\"${DAYS:-7}\"\nEND_DATE=$(date +%Y-%m-%d)\nSTART_DATE=$(date -v-${DAYS}d +%Y-%m-%d 2>/dev/null || date -d \"${DAYS} days ago\" +%Y-%m-%d)\n\nRESULT=$(curl -s \"https://graph.facebook.com/v21.0/${IG_ACCOUNT_ID}/insights?metric=reach,impressions,profile_views&period=day&since=${START_DATE}&until=${END_DATE}\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\")\n\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \" INSTAGRAM ACCOUNT INSIGHTS — Last ${DAYS} Days\"\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \" Note: Data reflects 24-48h reporting delay\"\necho \"\"\n\nREACH=$(echo \"$RESULT\" | jq '[.data[]? | select(.name == \"reach\") | .values[]?.value | tonumber] | add // 0')\nIMPRESSIONS=$(echo \"$RESULT\" | jq '[.data[]? | select(.name == \"impressions\") | .values[]?.value | tonumber] | add // 0')\nPROFILE_VIEWS=$(echo \"$RESULT\" | jq '[.data[]? | select(.name == \"profile_views\") | .values[]?.value | tonumber] | add // 0')\n\necho \" Reach: ${REACH}\"\necho \" Impressions: ${IMPRESSIONS}\"\necho \" Profile Views: ${PROFILE_VIEWS}\"\n```\n\n### demographics\n\nAudience breakdown by age/gender and top locations. Requires lifetime period (48h delay).\n\n```bash\nRESULT=$(curl -s \"https://graph.facebook.com/v21.0/${IG_ACCOUNT_ID}/insights?metric=audience_gender_age,audience_city,audience_country&period=lifetime\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\")\n\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \" INSTAGRAM DEMOGRAPHICS\"\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \" Note: Top 45 segments shown. Data has 48h delay.\"\necho \"\"\n\necho \"AGE / GENDER BREAKDOWN:\"\necho \"$RESULT\" | jq -r '.data[]? | select(.name == \"audience_gender_age\") | .values[0].value | to_entries[] | \" \\(.key): \\(.value)%\"' 2>/dev/null | head -20\n\necho \"\"\necho \"TOP CITIES:\"\necho \"$RESULT\" | jq -r '.data[]? | select(.name == \"audience_city\") | .values[0].value | to_entries | sort_by(-.value) | .[0:10][] | \" \\(.key): \\(.value)%\"' 2>/dev/null\n\necho \"\"\necho \"TOP COUNTRIES:\"\necho \"$RESULT\" | jq -r '.data[]? | select(.name == \"audience_country\") | .values[0].value | to_entries | sort_by(-.value) | .[0:10][] | \" \\(.key): \\(.value)%\"' 2>/dev/null\n```\n\n---\n\n## google-ads\n\n**Credential check**: If `GADS_DEV_TOKEN` or `GADS_REFRESH_TOKEN` is empty after resolution, print:\n`Warning: Google Ads not configured. Run /ops:setup marketing to set up credentials.`\nand stop.\n\n**Token refresh**: Run the access token refresh curl (from Credential Resolution above) at the start of every google-ads invocation. If `GADS_ACCESS_TOKEN` is null or \"null\", print:\n`Warning: Google Ads token refresh failed. Check client_id/client_secret/refresh_token in /ops:setup.`\nand stop.\n\nRoute `$ARGUMENTS` within the google-ads section:\n\n| Input | Action |\n|---|---|\n| (empty), dashboard, overview | Campaign performance dashboard (last 7 days) |\n| search-terms, terms | Search Terms Report with negative keyword candidates (last 30 days) |\n| budget-recs, recommendations, recs | Budget optimization recommendations from Google |\n| campaigns, manage | Campaign management — list, create, pause, enable, adjust budget |\n| keywords, kw, keyword-planner | Keyword Planner — discover keywords with volume and bid data |\n| ad-groups, ag | Ad group management — list, create, add/remove keywords, adjust bids |\n\n### Dashboard (default — no args, `dashboard`, `overview`)\n\n```bash\n# Campaign performance — last 7 days\nCAMPAIGNS=$(curl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/googleAds:searchStream\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary '{\n \"query\": \"SELECT campaign.id, campaign.name, campaign.status, campaign_budget.amount_micros, metrics.cost_micros, metrics.impressions, metrics.clicks, metrics.conversions, metrics.conversions_value FROM campaign WHERE segments.date DURING LAST_7_DAYS AND campaign.status != REMOVED ORDER BY metrics.cost_micros DESC LIMIT 20\"\n }')\n\n# Check for API error\nGADS_ERROR=$(echo \"$CAMPAIGNS\" | jq -r '.[0].error.message // empty' 2>/dev/null)\nif [ -n \"$GADS_ERROR\" ]; then\n echo \"Google Ads API error: ${GADS_ERROR}\"\n echo \"Check credentials with /ops:marketing setup.\"\n exit 0\nfi\n\n# Check for empty results\nCAMPAIGN_COUNT=$(echo \"$CAMPAIGNS\" | jq '[.[].results[]?] | length' 2>/dev/null || echo \"0\")\nif [ \"$CAMPAIGN_COUNT\" -eq 0 ]; then\n echo \"No active campaigns found in the last 7 days.\"\n exit 0\nfi\n\n# Compute totals\nTOTAL_SPEND=$(echo \"$CAMPAIGNS\" | jq '[.[].results[]?.metrics.costMicros // \"0\" | tonumber] | add / 1000000' 2>/dev/null)\nTOTAL_CONVERSIONS=$(echo \"$CAMPAIGNS\" | jq '[.[].results[]?.metrics.conversions // \"0\" | tonumber] | add' 2>/dev/null)\nTOTAL_VALUE=$(echo \"$CAMPAIGNS\" | jq '[.[].results[]?.metrics.conversionsValue // \"0\" | tonumber] | add' 2>/dev/null)\nOVERALL_ROAS=$(awk \"BEGIN { if (${TOTAL_SPEND:-0} > 0) printf \\\"%.2f\\\", ${TOTAL_VALUE:-0} / ${TOTAL_SPEND:-1}; else print \\\"—\\\" }\")\nTOTAL_SPEND_FMT=$(awk \"BEGIN { printf \\\"%.2f\\\", ${TOTAL_SPEND:-0} }\")\n\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \" GOOGLE ADS — Last 7 Days\"\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \"\"\necho \"Total Spend: \\${TOTAL_SPEND_FMT}\"\necho \"Total Conversions: ${TOTAL_CONVERSIONS}\"\necho \"Overall ROAS: ${OVERALL_ROAS}\"\necho \"\"\n\n# Print table header\nprintf \"| %-30s | %-8s | %-10s | %-10s | %-8s | %-8s | %-6s | %-6s | %-6s |\\n\" \\\n \"Campaign\" \"Status\" \"Budget/day\" \"Spend\" \"Impr\" \"Clicks\" \"CTR\" \"Conv\" \"ROAS\"\nprintf \"|%s|%s|%s|%s|%s|%s|%s|%s|%s|\\n\" \\\n \"--------------------------------\" \"----------\" \"------------\" \"------------\" \"----------\" \"----------\" \"--------\" \"--------\" \"--------\"\n\n# Print each campaign row\necho \"$CAMPAIGNS\" | jq -r '.[].results[]? | [\n .campaign.name,\n .campaign.status,\n (.campaignBudget.amountMicros // \"0\" | tonumber / 1000000),\n (.metrics.costMicros // \"0\" | tonumber / 1000000),\n (.metrics.impressions // \"0\" | tonumber),\n (.metrics.clicks // \"0\" | tonumber),\n (.metrics.conversions // \"0\" | tonumber),\n (.metrics.conversionsValue // \"0\" | tonumber),\n (.metrics.costMicros // \"0\" | tonumber)\n] | @tsv' 2>/dev/null | while IFS=

OPS ► MARKETING COMMAND CENTER Runtime Context Before executing, load available context: 1. Preferences : Read - — display all timestamps correctly - , , , , — check userConfig keys before env vars - , , , , , — Google Ads credentials 2. Daemon health : Read - If is not null → surface it before running any channel queries 3. Secrets : Resolve API keys via userConfig → env vars → Doppler MCP ( ) → Doppler CLI fallback (see Credential Resolution section below) CLI/API Reference Klaviyo REST API | Endpoint | Method | Description | |----------|--------|-------------| | | GET | All lists + subscri…

\\t' read -r name status budget_raw spend_raw impr_raw clicks_raw conv_raw value_raw cost_raw; do\n BUDGET=$(awk \"BEGIN { printf \\\"%.2f\\\", ${budget_raw:-0} }\")\n SPEND=$(awk \"BEGIN { printf \\\"%.2f\\\", ${spend_raw:-0} }\")\n CTR=$(awk \"BEGIN { if (${impr_raw:-0} > 0) printf \\\"%.2f\\\", ${clicks_raw:-0} / ${impr_raw:-0} * 100; else print \\\"0.00\\\" }\")\n ROAS=$(awk \"BEGIN { if (${spend_raw:-0} > 0) printf \\\"%.2f\\\", ${value_raw:-0} / ${spend_raw:-1}; else print \\\"—\\\" }\")\n printf \"| %-30s | %-8s | \\$%-9s | \\$%-9s | %-8s | %-8s | %-5s%% | %-6s | %-6s |\\n\" \\\n \"${name:0:30}\" \"$status\" \"$BUDGET\" \"$SPEND\" \"$impr_raw\" \"$clicks_raw\" \"$CTR\" \"$conv_raw\" \"$ROAS\"\ndone\n```\n\n### Search Terms (`search-terms`, `terms`)\n\n```bash\nSEARCH_TERMS=$(curl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/googleAds:searchStream\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary '{\n \"query\": \"SELECT search_term_view.search_term, search_term_view.status, campaign.name, ad_group.name, metrics.impressions, metrics.clicks, metrics.cost_micros, metrics.conversions FROM search_term_view WHERE segments.date DURING LAST_30_DAYS AND metrics.impressions > 0 ORDER BY metrics.impressions DESC LIMIT 100\"\n }')\n\n# Check for API error\nGADS_ST_ERROR=$(echo \"$SEARCH_TERMS\" | jq -r '.[0].error.message // empty' 2>/dev/null)\nif [ -n \"$GADS_ST_ERROR\" ]; then\n echo \"Google Ads API error: ${GADS_ST_ERROR}\"\n echo \"Check credentials with /ops:marketing setup.\"\n exit 0\nfi\n\nTERM_COUNT=$(echo \"$SEARCH_TERMS\" | jq '[.[].results[]?] | length' 2>/dev/null || echo \"0\")\nif [ \"$TERM_COUNT\" -eq 0 ]; then\n echo \"No search term data found for the last 30 days.\"\n exit 0\nfi\n\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \" SEARCH TERMS REPORT — Last 30 Days\"\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \"\"\n\nprintf \"| %-30s | %-10s | %-20s | %-15s | %-6s | %-6s | %-7s | %-6s |\\n\" \\\n \"Search Term\" \"Status\" \"Campaign\" \"Ad Group\" \"Impr\" \"Clicks\" \"Cost\" \"Conv\"\nprintf \"|%s|%s|%s|%s|%s|%s|%s|%s|\\n\" \\\n \"--------------------------------\" \"------------\" \"----------------------\" \"-----------------\" \"--------\" \"--------\" \"---------\" \"--------\"\n\n# Status mapping and table rows\necho \"$SEARCH_TERMS\" | jq -r '.[].results[]? | [\n .searchTermView.searchTerm,\n .searchTermView.status,\n .campaign.name,\n .adGroup.name,\n (.metrics.impressions // \"0\" | tostring),\n (.metrics.clicks // \"0\" | tostring),\n (.metrics.costMicros // \"0\" | tonumber / 1000000 | tostring),\n (.metrics.conversions // \"0\" | tostring)\n] | @tsv' 2>/dev/null | while IFS=

OPS ► MARKETING COMMAND CENTER Runtime Context Before executing, load available context: 1. Preferences : Read - — display all timestamps correctly - , , , , — check userConfig keys before env vars - , , , , , — Google Ads credentials 2. Daemon health : Read - If is not null → surface it before running any channel queries 3. Secrets : Resolve API keys via userConfig → env vars → Doppler MCP ( ) → Doppler CLI fallback (see Credential Resolution section below) CLI/API Reference Klaviyo REST API | Endpoint | Method | Description | |----------|--------|-------------| | | GET | All lists + subscri…

\\t' read -r term status campaign adgroup impr clicks cost_raw conv; do\n case \"$status\" in\n ADDED) STATUS_LABEL=\"✓ Added\" ;;\n EXCLUDED) STATUS_LABEL=\"✗ Excluded\" ;;\n *) STATUS_LABEL=\"○ New\" ;;\n esac\n COST=$(awk \"BEGIN { printf \\\"%.2f\\\", ${cost_raw:-0} }\")\n printf \"| %-30s | %-10s | %-20s | %-15s | %-6s | %-6s | \\$%-6s | %-6s |\\n\" \\\n \"${term:0:30}\" \"$STATUS_LABEL\" \"${campaign:0:20}\" \"${adgroup:0:15}\" \"$impr\" \"$clicks\" \"$COST\" \"$conv\"\ndone\n\necho \"\"\necho \"Negative keyword candidates (high spend, zero conversions):\"\n\necho \"$SEARCH_TERMS\" | jq -r '.[].results[]? | select(\n (.metrics.conversions // \"0\" | tonumber) == 0 and\n (.metrics.costMicros // \"0\" | tonumber) > 1000000\n) | \" • \\(.searchTermView.searchTerm) — $\\(.metrics.costMicros | tonumber / 1000000 | tostring | split(\".\") | .[0] + \".\" + (.[1] // \"00\")[0:2])\"' 2>/dev/null || echo \" (none found)\"\n```\n\n### Budget Recommendations (`budget-recs`, `recommendations`, `recs`)\n\n```bash\nRECS=$(curl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/googleAds:searchStream\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary '{\n \"query\": \"SELECT recommendation.resource_name, recommendation.type, recommendation.campaign, recommendation.impact, recommendation.campaign_budget_recommendation FROM recommendation WHERE recommendation.type IN (CAMPAIGN_BUDGET, MOVE_UNUSED_BUDGET, MARGINAL_ROI_CAMPAIGN_BUDGET, FORECASTING_CAMPAIGN_BUDGET)\"\n }')\n\n# Check for API error\nGADS_REC_ERROR=$(echo \"$RECS\" | jq -r '.[0].error.message // empty' 2>/dev/null)\nif [ -n \"$GADS_REC_ERROR\" ]; then\n echo \"Google Ads API error: ${GADS_REC_ERROR}\"\n echo \"Check credentials with /ops:marketing setup.\"\n exit 0\nfi\n\nREC_COUNT=$(echo \"$RECS\" | jq '[.[].results[]?] | length' 2>/dev/null || echo \"0\")\nif [ \"$REC_COUNT\" -eq 0 ]; then\n echo \"No budget recommendations available. Google needs campaign data to generate recommendations.\"\n exit 0\nfi\n\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \" BUDGET RECOMMENDATIONS\"\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \"\"\n\nprintf \"| %-22s | %-25s | %-15s | %-13s | %-20s |\\n\" \\\n \"Type\" \"Campaign\" \"Current Budget\" \"Recommended\" \"Impact\"\nprintf \"|%s|%s|%s|%s|%s|\\n\" \\\n \"------------------------\" \"---------------------------\" \"-----------------\" \"---------------\" \"----------------------\"\n\necho \"$RECS\" | jq -r '.[].results[]? | [\n .recommendation.type,\n .recommendation.campaign,\n (.recommendation.campaignBudgetRecommendation.currentBudgetAmountMicros // \"0\" | tonumber / 1000000 | tostring),\n (.recommendation.campaignBudgetRecommendation.recommendedBudgetAmountMicros // \"0\" | tonumber / 1000000 | tostring),\n (.recommendation.impact.baseMetrics.impressions // \"0\" | tonumber | tostring),\n (.recommendation.impact.potentialMetrics.impressions // \"0\" | tonumber | tostring)\n] | @tsv' 2>/dev/null | while IFS=

OPS ► MARKETING COMMAND CENTER Runtime Context Before executing, load available context: 1. Preferences : Read - — display all timestamps correctly - , , , , — check userConfig keys before env vars - , , , , , — Google Ads credentials 2. Daemon health : Read - If is not null → surface it before running any channel queries 3. Secrets : Resolve API keys via userConfig → env vars → Doppler MCP ( ) → Doppler CLI fallback (see Credential Resolution section below) CLI/API Reference Klaviyo REST API | Endpoint | Method | Description | |----------|--------|-------------| | | GET | All lists + subscri…

\\t' read -r rec_type campaign current_raw recommended_raw base_impr_raw pot_impr_raw; do\n case \"$rec_type\" in\n CAMPAIGN_BUDGET) TYPE_LABEL=\"Increase Budget\" ;;\n MOVE_UNUSED_BUDGET) TYPE_LABEL=\"Move Unused Budget\" ;;\n MARGINAL_ROI_CAMPAIGN_BUDGET) TYPE_LABEL=\"Marginal ROI\" ;;\n FORECASTING_CAMPAIGN_BUDGET) TYPE_LABEL=\"Forecasting\" ;;\n *) TYPE_LABEL=\"$rec_type\" ;;\n esac\n CURRENT=$(awk \"BEGIN { printf \\\"%.2f\\\", ${current_raw:-0} }\")\n RECOMMENDED=$(awk \"BEGIN { printf \\\"%.2f\\\", ${recommended_raw:-0} }\")\n IMPACT=$(awk \"BEGIN {\n base = ${base_impr_raw:-0}; pot = ${pot_impr_raw:-0}\n if (base > 0) printf \\\"+%.0f%% impressions\\\", (pot - base) / base * 100\n else print \\\"—\\\"\n }\")\n printf \"| %-22s | %-25s | \\$%-14s | \\$%-12s | %-20s |\\n\" \\\n \"$TYPE_LABEL\" \"${campaign:0:25}\" \"$CURRENT\" \"$RECOMMENDED\" \"$IMPACT\"\ndone\n```\n\n### Campaign Management (`campaigns`, `manage`)\n\n**List campaigns:**\n\n```bash\ncurl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/googleAds:searchStream\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary '{\n \"query\": \"SELECT campaign.id, campaign.name, campaign.status, campaign.advertising_channel_type, campaign_budget.amount_micros FROM campaign WHERE campaign.status != REMOVED ORDER BY campaign.name LIMIT 50\"\n }'\n```\n\nOutput as table:\n```\n| # | Campaign ID | Name | Status | Channel | Budget/day |\n```\n\n**Create campaign** (`campaigns create`):\n\nCollect from user via AskUserQuestion (free text, one at a time):\n1. Campaign name\n2. Daily budget in dollars (convert to micros: `BUDGET_MICROS=$(awk \"BEGIN {printf \\\"%d\\\", $DOLLARS * 1000000}\")`)\n3. Channel type — AskUserQuestion with options: `[Search, Display, Shopping, Video]`\n Map to API values: `SEARCH`, `DISPLAY`, `SHOPPING`, `VIDEO`\n\nTwo-step mutate:\n\nStep 1 — Create budget:\n```bash\nBUDGET_RESP=$(curl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/campaignBudgets:mutate\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary \"{\n \\\"operations\\\": [{\n \\\"create\\\": {\n \\\"name\\\": \\\"Budget for ${CAMPAIGN_NAME}\\\",\n \\\"deliveryMethod\\\": \\\"STANDARD\\\",\n \\\"amountMicros\\\": \\\"${BUDGET_MICROS}\\\"\n }\n }]\n }\")\nBUDGET_RESOURCE=$(echo \"$BUDGET_RESP\" | jq -r '.results[0].resourceName')\n```\n\nStep 2 — Create campaign (always in PAUSED status for safety):\n```bash\ncurl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/campaigns:mutate\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary \"{\n \\\"operations\\\": [{\n \\\"create\\\": {\n \\\"name\\\": \\\"${CAMPAIGN_NAME}\\\",\n \\\"campaignBudget\\\": \\\"${BUDGET_RESOURCE}\\\",\n \\\"advertisingChannelType\\\": \\\"${CHANNEL_TYPE}\\\",\n \\\"status\\\": \\\"PAUSED\\\",\n \\\"manualCpc\\\": {},\n \\\"networkSettings\\\": {\n \\\"targetGoogleSearch\\\": true,\n \\\"targetSearchNetwork\\\": true,\n \\\"targetContentNetwork\\\": false\n }\n }\n }]\n }\"\n```\n\nPrint: `✓ Campaign \"${CAMPAIGN_NAME}\" created (status: PAUSED, budget: $XX.XX/day). Enable it with: /ops:marketing google-ads campaigns enable \u003cID>`\n\nIf error, parse `error.message` from response and display.\n\n**Pause campaign** (`campaigns pause \u003cID>`):\n\n⚠ **Rule 5 — confirm before pausing** via AskUserQuestion: `\"Pause campaign \u003cNAME> (ID: \u003cID>)?\"` with options `[Pause, Cancel]`.\n\n```bash\ncurl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/campaigns:mutate\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary \"{\n \\\"operations\\\": [{\n \\\"update\\\": {\n \\\"resourceName\\\": \\\"customers/${GADS_CUSTOMER_ID}/campaigns/${CAMPAIGN_ID}\\\",\n \\\"status\\\": \\\"PAUSED\\\"\n },\n \\\"updateMask\\\": \\\"status\\\"\n }]\n }\"\n```\n\nPrint: `✓ Campaign \u003cNAME> paused.`\n\n**Enable campaign** (`campaigns enable \u003cID>`):\n\nSame as pause but `\"status\": \"ENABLED\"`. No confirmation needed (enabling is not destructive).\n\n```bash\ncurl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/campaigns:mutate\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary \"{\n \\\"operations\\\": [{\n \\\"update\\\": {\n \\\"resourceName\\\": \\\"customers/${GADS_CUSTOMER_ID}/campaigns/${CAMPAIGN_ID}\\\",\n \\\"status\\\": \\\"ENABLED\\\"\n },\n \\\"updateMask\\\": \\\"status\\\"\n }]\n }\"\n```\n\nPrint: `✓ Campaign \u003cNAME> enabled.`\n\n**Adjust budget** (`campaigns budget \u003cID> \u003cAMOUNT>`):\n\nFirst, fetch the campaign's current budget resource name:\n```bash\nCAMPAIGN_DATA=$(curl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/googleAds:searchStream\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary \"{\\\"query\\\": \\\"SELECT campaign.campaign_budget, campaign_budget.amount_micros FROM campaign WHERE campaign.id = ${CAMPAIGN_ID}\\\"}\")\nBUDGET_RESOURCE=$(echo \"$CAMPAIGN_DATA\" | jq -r '.[0].results[0].campaign.campaignBudget // empty')\nCURRENT_BUDGET_MICROS=$(echo \"$CAMPAIGN_DATA\" | jq -r '.[0].results[0].campaignBudget.amountMicros // \"0\"')\nCURRENT_BUDGET=$(awk \"BEGIN { printf \\\"%.2f\\\", ${CURRENT_BUDGET_MICROS:-0} / 1000000 }\")\n```\n\nConfirm via AskUserQuestion: `\"Change budget from $\u003cCURRENT> to $\u003cNEW>/day?\"` with options `[Confirm, Cancel]`.\n\nThen update:\n```bash\nNEW_BUDGET_MICROS=$(awk \"BEGIN {printf \\\"%d\\\", ${NEW_AMOUNT} * 1000000}\")\ncurl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/campaignBudgets:mutate\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary \"{\n \\\"operations\\\": [{\n \\\"update\\\": {\n \\\"resourceName\\\": \\\"${BUDGET_RESOURCE}\\\",\n \\\"amountMicros\\\": \\\"${NEW_BUDGET_MICROS}\\\"\n },\n \\\"updateMask\\\": \\\"amountMicros\\\"\n }]\n }\"\n```\n\nPrint: `✓ Budget updated: $\u003cOLD>/day → $\u003cNEW>/day`\n\n### Keyword Planner (`keywords`, `kw`, `keyword-planner`)\n\n```bash\n# Collect seed keywords from user via AskUserQuestion (free text)\n# \"Enter seed keywords (comma-separated):\"\n# Split into JSON array for the request: KEYWORDS_JSON_ARRAY=$(echo \"$SEED_KEYWORDS\" | sed 's/,/\",\"/g' | sed 's/^/\"/;s/$/\"/')\n\ncurl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}:generateKeywordIdeas\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary \"{\n \\\"language\\\": \\\"languageConstants/1000\\\",\n \\\"geoTargetConstants\\\": [\\\"geoTargetConstants/2840\\\"],\n \\\"includeAdultKeywords\\\": false,\n \\\"keywordPlanNetwork\\\": \\\"GOOGLE_SEARCH\\\",\n \\\"keywordSeed\\\": {\n \\\"keywords\\\": [${KEYWORDS_JSON_ARRAY}]\n }\n }\"\n```\n\nLanguage `1000` = English, Geo `2840` = United States. These are defaults — if user has locale configured in preferences, use those instead. Other common values: UK=`2826`, Canada=`2124`, Australia=`2036`.\n\n**Output format:**\n\n```\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n KEYWORD IDEAS\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nSeeds: keyword1, keyword2\n\n| Keyword | Avg Monthly Searches | Competition | Low Bid | High Bid |\n|---------|---------------------|-------------|---------|----------|\n```\n\n- `avgMonthlySearches` displayed as-is (integer)\n- `competition` displayed as-is (`LOW`, `MEDIUM`, `HIGH`)\n- `lowTopOfPageBidMicros` and `highTopOfPageBidMicros` divided by 1,000,000 and displayed as `$X.XX`\n- Sort by `avgMonthlySearches` descending in the output\n\nIf no results, print: `No keyword ideas found for these seeds. Try different or broader keywords.`\n\n### Ad Group Management (`ad-groups`, `ag`)\n\n**List ad groups for a campaign** (`ad-groups list \u003cCAMPAIGN_ID>`):\n\n```bash\ncurl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/googleAds:searchStream\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary \"{\\\"query\\\": \\\"SELECT ad_group.id, ad_group.name, ad_group.status, ad_group.cpc_bid_micros FROM ad_group WHERE campaign.id = ${CAMPAIGN_ID} AND ad_group.status != REMOVED ORDER BY ad_group.name\\\"}\"\n```\n\nOutput:\n```\n| # | Ad Group ID | Name | Status | CPC Bid |\n```\n\n**Create ad group** (`ad-groups create \u003cCAMPAIGN_ID>`):\n\nCollect via AskUserQuestion:\n1. Ad group name (free text)\n2. Default CPC bid in dollars (free text, convert to micros)\n\n```bash\nBID_MICROS=$(awk \"BEGIN {printf \\\"%d\\\", ${BID_DOLLARS} * 1000000}\")\ncurl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/adGroups:mutate\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary \"{\n \\\"operations\\\": [{\n \\\"create\\\": {\n \\\"name\\\": \\\"${AD_GROUP_NAME}\\\",\n \\\"campaign\\\": \\\"customers/${GADS_CUSTOMER_ID}/campaigns/${CAMPAIGN_ID}\\\",\n \\\"status\\\": \\\"ENABLED\\\",\n \\\"type\\\": \\\"SEARCH_STANDARD\\\",\n \\\"cpcBidMicros\\\": \\\"${BID_MICROS}\\\"\n }\n }]\n }\"\n```\n\nPrint: `✓ Ad group \"${AD_GROUP_NAME}\" created in campaign ${CAMPAIGN_ID} (CPC bid: $X.XX)`\n\n**List keywords in ad group** (`ad-groups keywords \u003cAD_GROUP_ID>`):\n\n```bash\ncurl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/googleAds:searchStream\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary \"{\\\"query\\\": \\\"SELECT ad_group_criterion.criterion_id, ad_group_criterion.keyword.text, ad_group_criterion.keyword.match_type, ad_group_criterion.cpc_bid_micros, ad_group_criterion.status FROM ad_group_criterion WHERE ad_group.id = ${AD_GROUP_ID} AND ad_group_criterion.type = KEYWORD AND ad_group_criterion.status != REMOVED\\\"}\"\n```\n\nOutput:\n```\n| # | Keyword | Match Type | CPC Bid | Status |\n```\n\n**Add keyword to ad group** (`ad-groups add-keyword \u003cAD_GROUP_ID>`):\n\nCollect via AskUserQuestion:\n1. Keyword text (free text)\n2. Match type — AskUserQuestion options: `[Broad, Phrase, Exact]`\n Map: `BROAD`, `PHRASE`, `EXACT`\n3. CPC bid in dollars (free text, convert to micros) — optional, uses ad group default if not provided\n\n```bash\ncurl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/adGroupCriteria:mutate\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary \"{\n \\\"operations\\\": [{\n \\\"create\\\": {\n \\\"adGroup\\\": \\\"customers/${GADS_CUSTOMER_ID}/adGroups/${AD_GROUP_ID}\\\",\n \\\"status\\\": \\\"ENABLED\\\",\n \\\"keyword\\\": {\n \\\"text\\\": \\\"${KEYWORD_TEXT}\\\",\n \\\"matchType\\\": \\\"${MATCH_TYPE}\\\"\n }\n ${BID_MICROS:+,\\\"cpcBidMicros\\\": \\\"${BID_MICROS}\\\"}\n }\n }]\n }\"\n```\n\nPrint: `✓ Keyword \"${KEYWORD_TEXT}\" (${MATCH_TYPE}) added to ad group ${AD_GROUP_ID}`\n\nNote: Keywords are immutable after creation. To change match type or text, remove and recreate. To change bid only, use `ad-groups update-bid`.\n\n**Remove keyword** (`ad-groups remove-keyword \u003cAD_GROUP_ID> \u003cCRITERION_ID>`):\n\n⚠ **Rule 5 — confirm before removing** via AskUserQuestion: `\"Remove keyword \u003cTEXT> from ad group \u003cNAME>?\"` with options `[Remove, Cancel]`.\n\n```bash\ncurl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/adGroupCriteria:mutate\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary \"{\n \\\"operations\\\": [{\n \\\"remove\\\": \\\"customers/${GADS_CUSTOMER_ID}/adGroupCriteria/${AD_GROUP_ID}~${CRITERION_ID}\\\"\n }]\n }\"\n```\n\nPrint: `✓ Keyword removed from ad group.`\n\n**Update keyword bid** (`ad-groups update-bid \u003cAD_GROUP_ID> \u003cCRITERION_ID> \u003cBID>`):\n\n```bash\nNEW_BID_MICROS=$(awk \"BEGIN {printf \\\"%d\\\", ${NEW_BID} * 1000000}\")\ncurl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/adGroupCriteria:mutate\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary \"{\n \\\"operations\\\": [{\n \\\"update\\\": {\n \\\"resourceName\\\": \\\"customers/${GADS_CUSTOMER_ID}/adGroupCriteria/${AD_GROUP_ID}~${CRITERION_ID}\\\",\n \\\"cpcBidMicros\\\": \\\"${NEW_BID_MICROS}\\\"\n },\n \\\"updateMask\\\": \\\"cpcBidMicros\\\"\n }]\n }\"\n```\n\nPrint: `✓ Keyword bid updated to $X.XX`\n\n---\n\n## campaigns\n\nCross-channel campaign overview — unified view of active campaigns across all configured channels (Klaviyo, Meta Ads, Google Ads).\n\nRun in parallel:\n1. Klaviyo active campaigns (status: draft + scheduled + sending)\n2. Meta Ads active campaigns (status ACTIVE) — reuse `META_TOKEN` / `META_ACCOUNT`\n3. Google Ads active campaigns (status ENABLED) — reuse `GADS_*` credentials if configured\n4. Google Ads: refresh access token first (see Credential Resolution)\n\n```bash\n# Meta Ads campaigns\nMETA_CAMPAIGNS=$(curl -s \"https://graph.facebook.com/v20.0/${META_ACCOUNT}/campaigns?fields=name,status,daily_budget,lifetime_budget,objective&filtering=[{\\\"field\\\":\\\"effective_status\\\",\\\"operator\\\":\\\"IN\\\",\\\"value\\\":[\\\"ACTIVE\\\"]}]\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" 2>/dev/null)\n\n# Klaviyo campaigns\nKLAVIYO_CAMPAIGNS=$(curl -s \"https://a.klaviyo.com/api/campaigns/?filter=equals(messages.channel,'email')&sort=-created_at&page[size]=10\" \\\n -H \"Authorization: Klaviyo-API-Key ${KLAVIYO_KEY}\" \\\n -H \"revision: 2024-10-15\" 2>/dev/null)\n\n# Google Ads campaigns (if configured)\nGADS_CAMPAIGNS=\"\"\nif [ -n \"$GADS_ACCESS_TOKEN\" ] && [ \"$GADS_ACCESS_TOKEN\" != \"null\" ]; then\n GADS_CAMPAIGNS=$(curl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/googleAds:searchStream\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary '{\"query\": \"SELECT campaign.id, campaign.name, campaign.status, campaign_budget.amount_micros, metrics.cost_micros FROM campaign WHERE campaign.status = ENABLED ORDER BY metrics.cost_micros DESC LIMIT 10\"}' 2>/dev/null)\nfi\n```\n\n### Output format\n```\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n CROSS-CHANNEL CAMPAIGNS — active\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n EMAIL (Klaviyo)\n [campaign name] [status] [send date or \"scheduled for X\"]\n\n PAID — META ADS\n [campaign name] [status] $[daily_budget]/day [objective]\n\n PAID — GOOGLE ADS\n [campaign name] [status] $[budget]/day $[spend 7d]\n\n FLOWS (Always-on automation)\n [flow name] [trigger type] [status: live/draft]\n```\n\nFor any channel not configured, show `[not configured — /ops:marketing setup]`.\n\n---\n\n## optimize\n\nCross-platform ad optimization agent. Reads Meta + Google Ads data, computes blended ROAS, and recommends where to shift budget.\n\nSpawn the marketing optimizer agent:\n```\nAgent(prompt=\"Run the marketing optimizer agent. Read ops-marketing-dash data for Meta Ads and Google Ads spend/conversions/ROAS. Compute blended ROAS across platforms. Identify the highest-ROAS platform. Recommend budget shifts with specific dollar amounts. List top 3 actions by expected impact. Use the marketing-optimizer.md agent instructions.\", model=\"claude-sonnet-4-5\")\n```\n\nIf Agent Teams are available (`CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1`):\n```\nTeamCreate(\"optimizer\")\nAgent(team_name=\"optimizer\", name=\"marketing-optimizer\", prompt=\"You are the marketing optimizer. Read ops-marketing-dash pre-gathered data. Compute blended ROAS for Meta + Google. Recommend budget reallocation. Show unified attribution table.\")\n```\n\n### Output format\n```\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n AD OPTIMIZATION REPORT\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n Blended ROAS: [X]x (Meta: [X]x Google: [X]x)\n Total Spend: $[X] Total Revenue: $[X]\n\n RECOMMENDATIONS\n 1. [Action] Expected impact: [+X% ROAS / +$X revenue]\n 2. [Action] ...\n 3. [Action] ...\n\n BUDGET REALLOCATION\n Move $[X]/day from [Platform A] → [Platform B]\n Rationale: [X]x ROAS vs [X]x ROAS\n```\n\n---\n\n## attribution\n\nUnified attribution table showing spend, conversions, revenue, and ROAS side-by-side across all configured platforms.\n\n```bash\n# Gather all platform data in parallel\n# Meta Ads — last 7d\nMETA_DATA=$(curl -s \"https://graph.facebook.com/v20.0/${META_ACCOUNT}/insights?fields=spend,actions,action_values&date_preset=last_7d&level=account\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" 2>/dev/null)\nMETA_SPEND=$(echo \"$META_DATA\" | jq -r '.data[0].spend // \"0\"')\nMETA_CONV=$(echo \"$META_DATA\" | jq '[.data[0].actions[]? | select(.action_type == \"purchase\") | .value | tonumber] | add // 0' 2>/dev/null)\nMETA_REV=$(echo \"$META_DATA\" | jq '[.data[0].action_values[]? | select(.action_type == \"purchase\") | .value | tonumber] | add // 0' 2>/dev/null)\nMETA_ROAS=$(awk \"BEGIN { if (${META_SPEND:-0} > 0) printf \\\"%.2f\\\", ${META_REV:-0} / ${META_SPEND:-1}; else print \\\"—\\\" }\")\n\n# Google Ads — last 7d\nGADS_DATA=\"\"\nGADS_SPEND=\"0\"; GADS_CONV=\"0\"; GADS_REV=\"0\"; GADS_ROAS=\"—\"\nif [ -n \"$GADS_ACCESS_TOKEN\" ] && [ \"$GADS_ACCESS_TOKEN\" != \"null\" ]; then\n GADS_DATA=$(curl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/googleAds:searchStream\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary '{\"query\": \"SELECT metrics.cost_micros, metrics.conversions, metrics.conversions_value FROM customer WHERE segments.date DURING LAST_7_DAYS\"}' 2>/dev/null)\n GADS_SPEND=$(echo \"$GADS_DATA\" | jq '[.[].results[]?.metrics.costMicros // \"0\" | tonumber] | add / 1000000 // 0' 2>/dev/null || echo \"0\")\n GADS_CONV=$(echo \"$GADS_DATA\" | jq '[.[].results[]?.metrics.conversions // \"0\" | tonumber] | add // 0' 2>/dev/null || echo \"0\")\n GADS_REV=$(echo \"$GADS_DATA\" | jq '[.[].results[]?.metrics.conversionsValue // \"0\" | tonumber] | add // 0' 2>/dev/null || echo \"0\")\n GADS_ROAS=$(awk \"BEGIN { if (${GADS_SPEND:-0} > 0) printf \\\"%.2f\\\", ${GADS_REV:-0} / ${GADS_SPEND:-1}; else print \\\"—\\\" }\")\nfi\n\n# Klaviyo attributed revenue\nKLAVIYO_REV=\"—\"\n# (Pull from metric aggregates if needed — complex, show as note)\n\n# GA4 conversions + revenue\nGA4_DATA=$(curl -s -X POST \"https://analyticsdata.googleapis.com/v1beta/properties/${GA4_PROPERTY}:runReport\" \\\n -H \"Authorization: Bearer ${GA4_TOKEN}\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"dateRanges\": [{\"startDate\": \"7daysAgo\", \"endDate\": \"today\"}], \"metrics\": [{\"name\": \"conversions\"}, {\"name\": \"totalRevenue\"}]}' 2>/dev/null)\nGA4_CONV=$(echo \"$GA4_DATA\" | jq -r '.rows[0].metricValues[0].value // \"—\"')\nGA4_REV=$(echo \"$GA4_DATA\" | jq -r '.rows[0].metricValues[1].value // \"—\"')\n\nTOTAL_AD_SPEND=$(awk \"BEGIN { printf \\\"%.2f\\\", ${META_SPEND:-0} + ${GADS_SPEND:-0} }\")\nTOTAL_AD_REV=$(awk \"BEGIN { printf \\\"%.2f\\\", ${META_REV:-0} + ${GADS_REV:-0} }\")\nBLENDED_ROAS=$(awk \"BEGIN { if (${TOTAL_AD_SPEND:-0} > 0) printf \\\"%.2f\\\", ${TOTAL_AD_REV:-0} / ${TOTAL_AD_SPEND:-1}; else print \\\"—\\\" }\")\n\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \" UNIFIED ATTRIBUTION — Last 7 Days\"\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\nprintf \"| %-14s | %-10s | %-12s | %-12s | %-8s |\\n\" \"Platform\" \"Spend\" \"Conversions\" \"Revenue\" \"ROAS\"\nprintf \"|%s|%s|%s|%s|%s|\\n\" \"----------------\" \"------------\" \"--------------\" \"--------------\" \"----------\"\nprintf \"| %-14s | \\$%-9s | %-12s | \\$%-11s | %-8s |\\n\" \"Meta Ads\" \"$META_SPEND\" \"$META_CONV\" \"$META_REV\" \"${META_ROAS}x\"\nprintf \"| %-14s | \\$%-9s | %-12s | \\$%-11s | %-8s |\\n\" \"Google Ads\" \"$(printf \"%.2f\" ${GADS_SPEND})\" \"$GADS_CONV\" \"$(printf \"%.2f\" ${GADS_REV})\" \"${GADS_ROAS}x\"\nprintf \"| %-14s | %-10s | %-12s | %-12s | %-8s |\\n\" \"Klaviyo\" \"—\" \"—\" \"${KLAVIYO_REV}\" \"—\"\nprintf \"| %-14s | %-10s | %-12s | %-12s | %-8s |\\n\" \"GA4 (organic)\" \"—\" \"$GA4_CONV\" \"\\${GA4_REV}\" \"—\"\nprintf \"|%s|%s|%s|%s|%s|\\n\" \"----------------\" \"------------\" \"--------------\" \"--------------\" \"----------\"\nprintf \"| %-14s | \\$%-9s | %-12s | \\$%-11s | %-8s |\\n\" \"TOTAL (ads)\" \"$TOTAL_AD_SPEND\" \"—\" \"$TOTAL_AD_REV\" \"${BLENDED_ROAS}x\"\n```\n\n---\n\n## dashboard (default — no args)\n\nRun ALL sections in parallel, then render unified dashboard.\n\n```bash\n# Run the pre-gathered data script\n\"${CLAUDE_PLUGIN_ROOT}/bin/ops-marketing-dash\" 2>/dev/null\n```\n\nParse the JSON output and display:\n\n```\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n MARKETING DASHBOARD — [date]\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n Email (Klaviyo) [N] subs | [X]% open rate | $[X] attributed\n Paid (Meta) $[X] spent | [X]x ROAS | [N] purchases\n Paid (Google) $[X] spent | [X]x ROAS | [N] conversions\n Organic (GA4) [N] sessions | [X]% CVR | $[X] revenue\n SEO (GSC) [N] clicks | [N] impressions | [X] avg pos\n Instagram [N] followers | [N] reach (7d)\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n Marketing Health Score: [N]/100 ([status: Healthy/Warning/Critical])\n Blended ROAS: [X]x Top channel: [channel]\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n```\n\n**Marketing Health Score computation** (0-100, shown at bottom of dashboard):\n- Blended ROAS ≥ 3x: +30 pts; 1-3x: +15 pts; \u003c 1x: +0 pts\n- Email open rate ≥ 20%: +20 pts; 10-20%: +10 pts; \u003c 10%: +0 pts\n- Channel diversity (3+ platforms active): +20 pts; 2 platforms: +10 pts; 1: +0 pts\n- GA4 CVR ≥ 2%: +20 pts; 1-2%: +10 pts; \u003c 1%: +0 pts\n- Organic SEO clicks > 1,000/mo: +10 pts; 100-1000: +5 pts; \u003c 100: +0 pts\n\nStatus thresholds: ≥70 = Healthy, 40-69 = Warning, \u003c 40 = Critical.\n\nFor any channel with missing credentials, show `[not configured — /ops:marketing setup]`.\n\n---\n\n## setup\n\n**Before asking for anything**, auto-scan ALL sources for existing credentials. Run in a single background batch:\n\n```bash\n# Env vars\nprintenv KLAVIYO_API_KEY KLAVIYO_PRIVATE_KEY META_ACCESS_TOKEN FACEBOOK_ACCESS_TOKEN META_AD_ACCOUNT_ID GA4_PROPERTY_ID GA_MEASUREMENT_ID 2>/dev/null\nprintenv GOOGLE_ADS_DEVELOPER_TOKEN GOOGLE_ADS_CLIENT_ID GOOGLE_ADS_CLIENT_SECRET GOOGLE_ADS_REFRESH_TOKEN GOOGLE_ADS_CUSTOMER_ID 2>/dev/null\n\n# Shell profiles\ngrep -h 'KLAVIYO\\|META_\\|FACEBOOK\\|GA4\\|GA_MEASUREMENT\\|GOOGLE_ADS' ~/.zshrc ~/.bashrc ~/.zprofile ~/.envrc 2>/dev/null | grep -v '^#'\n\n# Doppler — ALL projects, ALL configs\nfor proj in $(doppler projects --json 2>/dev/null | jq -r '.[].slug'); do\n for cfg in dev stg prd; do\n doppler secrets --project \"$proj\" --config \"$cfg\" --json 2>/dev/null | \\\n jq -r --arg proj \"$proj\" --arg cfg \"$cfg\" 'to_entries[] | select(.key | test(\"KLAVIYO|META|FACEBOOK|GA4|GOOGLE|GOOGLE_ADS\"; \"i\")) | \"\\(.key)=\\(.value.computed) (doppler:\\($proj)/\\($cfg))\"'\n done\ndone\n\n# Dashlane — check for tokens in password entries\ndcli password klaviyo --output json 2>/dev/null | jq -r '.[] | select(.password != null and .password != \"\") | \"\\(.title): token found\"'\ndcli password facebook --output json 2>/dev/null | jq -r '.[] | select(.password != null and .password != \"\") | \"\\(.title): token found\"'\ndcli password meta --output json 2>/dev/null | jq -r '.[] | select(.password != null and .password != \"\") | \"\\(.title): token found\"'\ndcli password \"google ads\" --output json 2>/dev/null | jq -r '.[] | select(.password != null and .password != \"\") | \"\\(.title): token found\"'\n\n# Keychain\nsecurity find-generic-password -s \"klaviyo-api-key\" -w 2>/dev/null\nsecurity find-generic-password -s \"meta-ads-token\" -w 2>/dev/null\nsecurity find-generic-password -s \"google-ads-refresh-token\" -w 2>/dev/null\n\n# gcloud ADC (for GA4 + Search Console)\ngcloud auth application-default print-access-token 2>/dev/null | head -c 10 && echo \"...gcloud-ok\"\n\n# Chrome history — reveals account identity\nsqlite3 ~/Library/Application\\ Support/Google/Chrome/Default/History \\\n \"SELECT DISTINCT url FROM urls WHERE url LIKE '%klaviyo.com%' OR url LIKE '%analytics.google.com%' OR url LIKE '%business.facebook.com%' OR url LIKE '%search.google.com/search-console%' OR url LIKE '%ads.google.com%' ORDER BY last_visit_time DESC LIMIT 15\" 2>/dev/null\n\n# Existing prefs + userConfig\njq -r '.marketing // empty' \"$PREFS_PATH\" 2>/dev/null\n```\n\nPresent ALL findings before asking for anything. Only prompt for values NOT found in any source. Run all smoke tests with `run_in_background: true`.\n\n**Klaviyo:** If `KLAVIYO_PRIVATE_KEY` or Dashlane entry with `ck_*` key found, use it directly. Note: Klaviyo private keys start with `ck_` (older) or `pk_` (newer). Smoke test: `curl -s -H \"Authorization: Klaviyo-API-Key $KEY\" -H \"revision: 2024-10-15\" \"https://a.klaviyo.com/api/lists?page[size]=1\"`.\n\n**Meta Ads:** If found in Doppler, use directly. Need both `META_ACCESS_TOKEN` and `META_AD_ACCOUNT_ID`. Smoke test: `graph.facebook.com/v20.0/$AD_ACCOUNT_ID/campaigns?limit=1`.\n\n**GA4:** Only needs Property ID + gcloud ADC. If gcloud ADC not set up, run `gcloud auth application-default login` in background (opens browser). Check Chrome history for GA4 property URLs to auto-detect the property ID.\n\n**Search Console:** Only needs site URL + gcloud ADC. Check Chrome history for `search.google.com/search-console` URLs to auto-detect the site.\n\n**Google Ads:** More complex than other marketing credentials — requires 3 pieces: (1) developer token from Google Ads MCC → Tools & Settings → API Center, (2) OAuth2 client ID + secret from Google Cloud Console (Desktop app type, Google Ads API enabled), (3) refresh token via browser OAuth flow. Setup flow: collect developer token and client credentials first, then open browser auth URL, user pastes authorization code, exchange for refresh token via curl, then list accessible customer accounts and let user select. Smoke test: `curl -s -X GET \"https://googleads.googleapis.com/v23/customers:listAccessibleCustomers\" -H \"Authorization: Bearer $TOKEN\" -H \"developer-token: $DEV_TOKEN\"` — expect JSON with `resourceNames` array.\n\nSave via userConfig (preferred) or Doppler. Report: `[service] ✓ connected` or `[service] ✗ invalid key — [error]`.\n---","attachment_filenames":[],"attachments":[],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"OPS ► MARKETING COMMAND CENTER","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Runtime Context","type":"text"}]},{"type":"paragraph","content":[{"text":"Before executing, load available context:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Preferences","type":"text","marks":[{"type":"strong"}]},{"text":": Read ","type":"text"},{"text":"${CLAUDE_PLUGIN_DATA_DIR:-$HOME/.claude/plugins/data/ops-ops-marketplace}/preferences.json","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"timezone","type":"text","marks":[{"type":"code_inline"}]},{"text":" — display all timestamps correctly","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"klaviyo_private_key","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"meta_ads_token","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"meta_ad_account_id","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"ga4_property_id","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"google_search_console_site","type":"text","marks":[{"type":"code_inline"}]},{"text":" — check userConfig keys before env vars","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"google_ads_developer_token","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"google_ads_client_id","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"google_ads_client_secret","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"google_ads_refresh_token","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"google_ads_customer_id","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"google_ads_login_customer_id","type":"text","marks":[{"type":"code_inline"}]},{"text":" — Google Ads credentials","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Daemon health","type":"text","marks":[{"type":"strong"}]},{"text":": Read ","type":"text"},{"text":"${CLAUDE_PLUGIN_DATA_DIR}/daemon-health.json","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If ","type":"text"},{"text":"action_needed","type":"text","marks":[{"type":"code_inline"}]},{"text":" is not null → surface it before running any channel queries","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Secrets","type":"text","marks":[{"type":"strong"}]},{"text":": Resolve API keys via userConfig → env vars → Doppler MCP (","type":"text"},{"text":"mcp__doppler__*","type":"text","marks":[{"type":"code_inline"}]},{"text":") → Doppler CLI fallback (see Credential Resolution section below)","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"CLI/API Reference","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Klaviyo REST API","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Endpoint","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Method","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Description","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"https://a.klaviyo.com/api/lists/?fields[list]=name,id,profile_count","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"GET","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"All lists + subscriber counts","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"https://a.klaviyo.com/api/campaigns/?filter=equals(messages.channel,'email')&sort=-created_at","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"GET","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Recent campaigns","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"https://a.klaviyo.com/api/flows/?filter=equals(status,'live')","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"GET","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Active flows","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"https://a.klaviyo.com/api/metrics/","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"GET","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Available metrics","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"Auth header","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"Authorization: Klaviyo-API-Key ${KLAVIYO_KEY}","type":"text","marks":[{"type":"code_inline"}]},{"text":" | ","type":"text"},{"text":"Revision header","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"revision: 2024-10-15","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Meta Graph API","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Endpoint","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Method","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Description","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"https://graph.facebook.com/v18.0/${META_ACCOUNT}/insights?fields=spend,...&date_preset=last_7d","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"GET","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Account-level ad spend","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"https://graph.facebook.com/v18.0/${META_ACCOUNT}/campaigns?fields=name,status,insights{...}","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"GET","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Campaign breakdown","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"https://graph.facebook.com/v18.0/me/accounts?fields=instagram_business_account","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"GET","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Linked Instagram account","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"Auth header","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"Authorization: Bearer ${META_TOKEN}","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Google Analytics 4 (Data API)","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Endpoint","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Method","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Description","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"https://analyticsdata.googleapis.com/v1beta/properties/${GA4_PROPERTY}:runReport","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"POST","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Run custom report","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"Auth","type":"text","marks":[{"type":"strong"}]},{"text":": gcloud ADC — ","type":"text"},{"text":"GA4_TOKEN=$(gcloud auth application-default print-access-token)","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Google Search Console","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Endpoint","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Method","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Description","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"https://searchconsole.googleapis.com/webmasters/v3/sites/${GSC_SITE_ENCODED}/searchAnalytics/query","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"POST","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Search performance data","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"Auth","type":"text","marks":[{"type":"strong"}]},{"text":": Same gcloud ADC token as GA4","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Agent Teams support","type":"text"}]},{"type":"paragraph","content":[{"text":"If ","type":"text"},{"text":"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1","type":"text","marks":[{"type":"code_inline"}]},{"text":" is set, use ","type":"text"},{"text":"Agent Teams","type":"text","marks":[{"type":"strong"}]},{"text":" when gathering channel data in parallel. This enables:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Agents share context and can coordinate mid-flight","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"You can steer priorities in real-time","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Agents report progress as they complete","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Team setup","type":"text","marks":[{"type":"strong"}]},{"text":" (only when flag is enabled):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"TeamCreate(\"marketing-team\")\nAgent(team_name=\"marketing-team\", name=\"email-metrics\", prompt=\"Pull Klaviyo subscriber counts, campaign stats, and flow metrics\")\nAgent(team_name=\"marketing-team\", name=\"ads-metrics\", prompt=\"Pull Meta Ads spend, ROAS, and campaign breakdown\")\nAgent(team_name=\"marketing-team\", name=\"analytics-metrics\", prompt=\"Pull GA4 sessions, conversions, and traffic sources\")\nAgent(team_name=\"marketing-team\", name=\"seo-metrics\", prompt=\"Pull Search Console clicks, impressions, and top queries\")","type":"text"}]},{"type":"paragraph","content":[{"text":"If the flag is NOT set, use standard fire-and-forget subagents.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Credential Resolution","type":"text"}]},{"type":"paragraph","content":[{"text":"Resolve credentials in this order for each service:","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Klaviyo","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"KLAVIYO_KEY=\"${KLAVIYO_PRIVATE_KEY:-$(claude plugin config get klaviyo_private_key 2>/dev/null)}\"\nif [ -z \"$KLAVIYO_KEY\" ]; then\n KLAVIYO_KEY=\"$(doppler secrets get KLAVIYO_PRIVATE_KEY --plain 2>/dev/null)\"\nfi","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Meta Ads","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"META_TOKEN=\"${META_ADS_TOKEN:-$(claude plugin config get meta_ads_token 2>/dev/null)}\"\nMETA_ACCOUNT=\"${META_AD_ACCOUNT_ID:-$(claude plugin config get meta_ad_account_id 2>/dev/null)}\"\nif [ -z \"$META_TOKEN\" ]; then\n META_TOKEN=\"$(doppler secrets get META_ADS_TOKEN --plain 2>/dev/null)\"\nfi","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"GA4","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"GA4_PROPERTY=\"${GA4_PROPERTY_ID:-$(claude plugin config get ga4_property_id 2>/dev/null)}\"\n# GA4 uses gcloud application default credentials — check if configured:\ngcloud auth application-default print-access-token 2>/dev/null","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Google Search Console","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"GSC_SITE=\"${GOOGLE_SEARCH_CONSOLE_SITE:-$(claude plugin config get google_search_console_site 2>/dev/null)}\"\n# Uses same gcloud ADC as GA4","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Google Ads","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"GADS_API_VERSION=\"v23\"\nGADS_DEV_TOKEN=\"${GOOGLE_ADS_DEVELOPER_TOKEN:-$(claude plugin config get google_ads_developer_token 2>/dev/null)}\"\nGADS_CLIENT_ID=\"${GOOGLE_ADS_CLIENT_ID:-$(claude plugin config get google_ads_client_id 2>/dev/null)}\"\nGADS_CLIENT_SECRET=\"${GOOGLE_ADS_CLIENT_SECRET:-$(claude plugin config get google_ads_client_secret 2>/dev/null)}\"\nGADS_REFRESH_TOKEN=\"${GOOGLE_ADS_REFRESH_TOKEN:-$(claude plugin config get google_ads_refresh_token 2>/dev/null)}\"\nGADS_CUSTOMER_ID=\"${GOOGLE_ADS_CUSTOMER_ID:-$(claude plugin config get google_ads_customer_id 2>/dev/null)}\"\nGADS_LOGIN_CUSTOMER_ID=\"${GOOGLE_ADS_LOGIN_CUSTOMER_ID:-$(claude plugin config get google_ads_login_customer_id 2>/dev/null)}\"\n\n# Doppler fallback\nif [ -z \"$GADS_REFRESH_TOKEN\" ]; then\n GADS_REFRESH_TOKEN=\"$(doppler secrets get GOOGLE_ADS_REFRESH_TOKEN --plain 2>/dev/null)\"\nfi\nif [ -z \"$GADS_DEV_TOKEN\" ]; then\n GADS_DEV_TOKEN=\"$(doppler secrets get GOOGLE_ADS_DEVELOPER_TOKEN --plain 2>/dev/null)\"\nfi\n\n# Strip dashes from customer ID (API requires no dashes)\nGADS_CUSTOMER_ID=\"${GADS_CUSTOMER_ID//-/}\"\n\n# Refresh access token (expires in ~1 hour — always refresh before API calls)\nGADS_ACCESS_TOKEN=$(curl -s -X POST https://oauth2.googleapis.com/token \\\n --data \"client_id=${GADS_CLIENT_ID}\" \\\n --data \"client_secret=${GADS_CLIENT_SECRET}\" \\\n --data \"refresh_token=${GADS_REFRESH_TOKEN}\" \\\n --data \"grant_type=refresh_token\" | jq -r '.access_token')\n\n# Common headers for all Google Ads API calls\nGADS_HEADERS=(-H \"Content-Type: application/json\" -H \"Authorization: Bearer ${GADS_ACCESS_TOKEN}\" -H \"developer-token: ${GADS_DEV_TOKEN}\")\nif [ -n \"$GADS_LOGIN_CUSTOMER_ID\" ]; then\n GADS_HEADERS+=(-H \"login-customer-id: ${GADS_LOGIN_CUSTOMER_ID}\")\nfi","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Sub-command Routing","type":"text"}]},{"type":"paragraph","content":[{"text":"Route ","type":"text"},{"text":"$ARGUMENTS","type":"text","marks":[{"type":"code_inline"}]},{"text":" to the correct section below:","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Input","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Action","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"(empty), dashboard","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Run full marketing dashboard","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"email, klaviyo","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Klaviyo email metrics","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"ads, meta","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Meta Ads performance (read-only overview)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"meta-manage, meta create-campaign, meta target, meta creative, meta rules, meta audiences, meta advantage","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Meta Ads campaign management (see ## meta-manage section)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"google-ads, gads","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Google Ads dashboard + campaign management (see ## google-ads section)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"analytics, ga4","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"GA4 sessions + conversions","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"ga4 realtime, ga4 funnel, ga4 cohort, ga4 audience, ga4 pivot","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"GA4 advanced analytics (see ## ga4-advanced section)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"seo, gsc","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Search Console metrics","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"social","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Social media aggregator","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"instagram, instagram post, instagram reel, instagram story, instagram insights, instagram demographics","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Instagram publishing + insights (see ## instagram section)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"campaigns","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Cross-channel campaign overview (all platforms)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"optimize","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Cross-platform ad optimization agent","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"attribution","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Unified attribution table (Meta + Google + Klaviyo + GA4)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"setup","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Configure API keys","type":"text"}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"email / klaviyo","type":"text"}]},{"type":"paragraph","content":[{"text":"Pull Klaviyo metrics for last 30 days.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Subscriber count","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"curl -s \"https://a.klaviyo.com/api/lists/?fields[list]=name,id,profile_count\" \\\n -H \"Authorization: Klaviyo-API-Key ${KLAVIYO_KEY}\" \\\n -H \"revision: 2024-10-15\" | jq '.data[] | {name: .attributes.name, id: .id, count: .attributes.profile_count}'","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Recent campaigns (last 10)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"curl -s \"https://a.klaviyo.com/api/campaigns/?filter=equals(messages.channel,'email')&sort=-created_at&page[size]=10&fields[campaign]=name,status,created_at,send_time\" \\\n -H \"Authorization: Klaviyo-API-Key ${KLAVIYO_KEY}\" \\\n -H \"revision: 2024-10-15\" | jq '.data[] | {name: .attributes.name, status: .attributes.status, sent: .attributes.send_time}'","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Flow metrics (active flows)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"curl -s \"https://a.klaviyo.com/api/flows/?filter=equals(status,'live')&fields[flow]=name,status,created,trigger_type\" \\\n -H \"Authorization: Klaviyo-API-Key ${KLAVIYO_KEY}\" \\\n -H \"revision: 2024-10-15\" | jq '.data[] | {name: .attributes.name, trigger: .attributes.trigger_type}'","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Key email metrics (opens, clicks, revenue via metric aggregates)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Get metric IDs first\ncurl -s \"https://a.klaviyo.com/api/metrics/\" \\\n -H \"Authorization: Klaviyo-API-Key ${KLAVIYO_KEY}\" \\\n -H \"revision: 2024-10-15\" | jq '.data[] | select(.attributes.name | test(\"Opened Email|Clicked Email|Placed Order\")) | {name: .attributes.name, id: .id}'","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Output format","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n EMAIL (KLAVIYO) — last 30d\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n Lists: [list_name] — [N] subscribers\n Campaigns: [N sent] | [N drafts]\n Active Flows: [N]\n\n RECENT CAMPAIGNS\n [name] [status] sent [date]\n ...","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"ads / meta","type":"text"}]},{"type":"paragraph","content":[{"text":"Pull Meta Ads insights for the configured ad account.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Account-level spend (last 7 days)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"curl -s \"https://graph.facebook.com/v18.0/${META_ACCOUNT}/insights?fields=spend,impressions,clicks,ctr,cpc,actions,action_values&date_preset=last_7d&level=account\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" | jq '{spend: .data[0].spend, impressions: .data[0].impressions, clicks: .data[0].clicks, ctr: .data[0].ctr, cpc: .data[0].cpc}'","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Campaign breakdown (last 7 days)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"curl -s \"https://graph.facebook.com/v18.0/${META_ACCOUNT}/campaigns?fields=name,status,daily_budget,lifetime_budget,insights{spend,impressions,clicks,actions,action_values}&date_preset=last_7d\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" | jq '.data[] | {name: .name, status: .status, spend: .insights.data[0].spend}'","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"ROAS calculation","type":"text"}]},{"type":"paragraph","content":[{"text":"From ","type":"text"},{"text":"action_values","type":"text","marks":[{"type":"code_inline"}]},{"text":" array: extract ","type":"text"},{"text":"action_type == \"purchase\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" value, divide by spend.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Top performing ads (last 7d)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"curl -s \"https://graph.facebook.com/v18.0/${META_ACCOUNT}/ads?fields=name,adset_id,insights{spend,impressions,clicks,actions,action_values,ctr,cpc}&date_preset=last_7d&limit=10\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" | jq '.data | sort_by(.insights.data[0].spend | tonumber) | reverse | .[0:5] | .[] | {name: .name, spend: .insights.data[0].spend, ctr: .insights.data[0].ctr}'","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Output format","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n META ADS — last 7d\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n Spend: $[X]\n ROAS: [X]x\n Purchases: [N] ($[X] revenue)\n Impressions: [N] CTR: [X]%\n CPC: $[X]\n\n CAMPAIGNS\n [name] [status] $[spend] [roas]x ROAS\n ...\n\n TOP ADS (by spend)\n [name] $[spend] [ctr]% CTR","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"meta-manage","type":"text"}]},{"type":"paragraph","content":[{"text":"Full Meta Ads campaign management. Uses same ","type":"text"},{"text":"META_TOKEN","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"META_ACCOUNT","type":"text","marks":[{"type":"code_inline"}]},{"text":" credentials as read-only ","type":"text"},{"text":"ads","type":"text","marks":[{"type":"code_inline"}]},{"text":" section.","type":"text"}]},{"type":"paragraph","content":[{"text":"Credential check","type":"text","marks":[{"type":"strong"}]},{"text":": If ","type":"text"},{"text":"META_TOKEN","type":"text","marks":[{"type":"code_inline"}]},{"text":" is empty, print ","type":"text"},{"text":"Meta Ads not configured. Run /ops:marketing setup.","type":"text","marks":[{"type":"code_inline"}]},{"text":" and stop.","type":"text"}]},{"type":"paragraph","content":[{"text":"Route ","type":"text"},{"text":"$ARGUMENTS","type":"text","marks":[{"type":"code_inline"}]},{"text":" within meta-manage:","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Input","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Action","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"create-campaign","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Create a new campaign (always PAUSED)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"target \u003cADSET_ID>","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Configure ad set targeting","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"creative \u003cCAMPAIGN_ID>","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Upload image + create ad with copy","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"rules","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"List / create automation rules","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"audiences","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Create custom or lookalike audiences","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"advantage","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Create Advantage+ AI-optimized campaign","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"create-campaign","type":"text"}]},{"type":"paragraph","content":[{"text":"Collect via AskUserQuestion (max 4 options each call):","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Campaign objective — ","type":"text"},{"text":"[OUTCOME_TRAFFIC, OUTCOME_SALES, OUTCOME_LEADS, OUTCOME_AWARENESS]","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Daily budget in dollars (free text)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Campaign name (free text)","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Then confirm via AskUserQuestion: ","type":"text"},{"text":"\"Create Meta campaign '\u003cNAME>' with $\u003cBUDGET>/day budget?\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" options ","type":"text"},{"text":"[Create, Cancel]","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"BUDGET_CENTS=$(awk \"BEGIN {printf \\\"%d\\\", ${BUDGET_DOLLARS} * 100}\")\ncurl -s -X POST \"https://graph.facebook.com/v20.0/${META_ACCOUNT}/campaigns\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" \\\n -F \"name=${CAMPAIGN_NAME}\" \\\n -F \"objective=${OBJECTIVE}\" \\\n -F \"status=PAUSED\" \\\n -F \"special_ad_categories=[]\" \\\n -F \"daily_budget=${BUDGET_CENTS}\" | jq '{id: .id, error: .error.message}'","type":"text"}]},{"type":"paragraph","content":[{"text":"Print: ","type":"text"},{"text":"Campaign \"${CAMPAIGN_NAME}\" created (ID: \u003cID>, status: PAUSED, budget: $\u003cBUDGET>/day). Enable via Meta Ads Manager or add ad sets first.","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"paragraph","content":[{"text":"If error, print the error message.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"target \u003cADSET_ID>","type":"text"}]},{"type":"paragraph","content":[{"text":"Configure targeting for an existing ad set. Collect via AskUserQuestion:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Target countries (comma-separated ISO codes, e.g. ","type":"text"},{"text":"US,CA,GB","type":"text","marks":[{"type":"code_inline"}]},{"text":") — free text","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Age range: ","type":"text"},{"text":"[18-34, 25-54, 35-65, 18-65]","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Gender: ","type":"text"},{"text":"[All, Men only, Women only, Skip]","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Build geo_locations JSON\nGEO_JSON=$(echo \"$COUNTRIES\" | tr ',' '\\n' | jq -Rc '.' | jq -sc '{\"countries\": .}')\n\n# Build targeting spec\nTARGETING_JSON=$(jq -n \\\n --argjson geo \"$GEO_JSON\" \\\n --arg age_min \"$AGE_MIN\" \\\n --arg age_max \"$AGE_MAX\" \\\n '{\n geo_locations: $geo,\n age_min: ($age_min | tonumber),\n age_max: ($age_max | tonumber)\n }')\n\n# Add gender filter if requested\nif [ \"$GENDER\" = \"Men only\" ]; then\n TARGETING_JSON=$(echo \"$TARGETING_JSON\" | jq '. + {\"genders\": [1]}')\nelif [ \"$GENDER\" = \"Women only\" ]; then\n TARGETING_JSON=$(echo \"$TARGETING_JSON\" | jq '. + {\"genders\": [2]}')\nfi\n\ncurl -s -X POST \"https://graph.facebook.com/v20.0/${ADSET_ID}\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" \\\n -F \"targeting=${TARGETING_JSON}\" | jq '{success: .success, error: .error.message}'","type":"text"}]},{"type":"paragraph","content":[{"text":"Print: ","type":"text"},{"text":"Ad set ${ADSET_ID} targeting updated: ${COUNTRIES}, ages ${AGE_MIN}-${AGE_MAX}${GENDER_LABEL}.","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"creative \u003cCAMPAIGN_ID>","type":"text"}]},{"type":"paragraph","content":[{"text":"Upload an image and create an ad. Collect via AskUserQuestion:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Image file path or URL (free text)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Ad set ID to attach the ad to (free text)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Primary text (ad copy, free text — up to 125 characters recommended)","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Then collect headline (free text, up to 40 characters) via a second AskUserQuestion.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Step 1: Upload image\nif [[ \"$IMAGE_INPUT\" == http* ]]; then\n # Upload by URL\n UPLOAD_RESP=$(curl -s -X POST \"https://graph.facebook.com/v20.0/${META_ACCOUNT}/adimages\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" \\\n -F \"url=${IMAGE_INPUT}\")\nelse\n # Upload by file (multipart)\n UPLOAD_RESP=$(curl -s -X POST \"https://graph.facebook.com/v20.0/${META_ACCOUNT}/adimages\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" \\\n -F \"filename=@${IMAGE_INPUT}\")\nfi\nIMAGE_HASH=$(echo \"$UPLOAD_RESP\" | jq -r '.images | to_entries[0].value.hash // empty')\n\nif [ -z \"$IMAGE_HASH\" ]; then\n echo \"Image upload failed: $(echo \"$UPLOAD_RESP\" | jq -r '.error.message // \"unknown error\"')\"\n exit 0\nfi\n\n# Resolve the Facebook Page ID. Meta's `object_story_spec.page_id` requires a\n# real Page ID — the ad account ID (with `act_` stripped) is NOT a Page ID and\n# the API call will fail. Require META_PAGE_ID in env or plugin prefs.\nMETA_PAGE_ID=\"${META_PAGE_ID:-$(claude plugin config get meta_page_id 2>/dev/null || echo \"\")}\"\nif [ -z \"$META_PAGE_ID\" ]; then\n echo \"META_PAGE_ID is required to create an ad creative. Set it via:\"\n echo \" claude plugin config set meta_page_id \u003cyour_fb_page_id>\"\n echo \"Find your Page ID at https://www.facebook.com/\u003cyour-page>/about_profile_transparency\"\n exit 0\nfi\n\n# Step 2: Create ad creative\nCREATIVE_RESP=$(curl -s -X POST \"https://graph.facebook.com/v20.0/${META_ACCOUNT}/adcreatives\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" \\\n -F \"name=Creative for ${AD_NAME}\" \\\n -F \"object_story_spec={\\\"page_id\\\": \\\"${META_PAGE_ID}\\\", \\\"link_data\\\": {\\\"image_hash\\\": \\\"${IMAGE_HASH}\\\", \\\"message\\\": \\\"${PRIMARY_TEXT}\\\", \\\"name\\\": \\\"${HEADLINE}\\\"}}\")\nCREATIVE_ID=$(echo \"$CREATIVE_RESP\" | jq -r '.id // empty')\n\nif [ -z \"$CREATIVE_ID\" ]; then\n echo \"Creative creation failed: $(echo \"$CREATIVE_RESP\" | jq -r '.error.message // \"unknown error\"')\"\n exit 0\nfi\n\n# Step 3: Create ad (status PAUSED — Rule 5)\ncurl -s -X POST \"https://graph.facebook.com/v20.0/${META_ACCOUNT}/ads\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" \\\n -F \"name=${AD_NAME}\" \\\n -F \"adset_id=${ADSET_ID}\" \\\n -F \"creative={\\\"creative_id\\\": \\\"${CREATIVE_ID}\\\"}\" \\\n -F \"status=PAUSED\" | jq '{id: .id, error: .error.message}'","type":"text"}]},{"type":"paragraph","content":[{"text":"Print: ","type":"text"},{"text":"Ad created (ID: \u003cID>, creative: \u003cCREATIVE_ID>, status: PAUSED). Enable via Meta Ads Manager when ready.","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"rules","type":"text"}]},{"type":"paragraph","content":[{"text":"List existing rules or create a new automation rule.","type":"text"}]},{"type":"paragraph","content":[{"text":"List rules:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"curl -s \"https://graph.facebook.com/v20.0/${META_ACCOUNT}/adrules_library?fields=name,status,evaluation_spec,execution_spec\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" | jq '.data[] | {id: .id, name: .name, status: .status}'","type":"text"}]},{"type":"paragraph","content":[{"text":"Create rule","type":"text","marks":[{"type":"strong"}]},{"text":" (prompt via AskUserQuestion):","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Rule type: ","type":"text"},{"text":"[Pause low performers, Scale winners, Increase budget, Decrease budget]","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"paragraph","content":[{"text":"For \"Pause low performers\":","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Pause ads where CPA > $50 and spend > $20 in last 7 days\ncurl -s -X POST \"https://graph.facebook.com/v20.0/${META_ACCOUNT}/adrules_library\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"name\": \"Pause high CPA ads\",\n \"schedule_spec\": {\"schedule_type\": \"SEMI_HOURLY\"},\n \"evaluation_spec\": {\n \"evaluation_type\": \"SCHEDULE\",\n \"filters\": [\n {\"field\": \"cost_per_result\", \"value\": [50], \"operator\": \"GREATER_THAN\"},\n {\"field\": \"spent\", \"value\": [20], \"operator\": \"GREATER_THAN\"},\n {\"field\": \"entity_type\", \"value\": [\"AD\"], \"operator\": \"EQUAL\"},\n {\"field\": \"time_preset\", \"value\": [\"LAST_7_DAYS\"], \"operator\": \"EQUAL\"}\n ]\n },\n \"execution_spec\": {\n \"execution_type\": \"PAUSE\"\n },\n \"status\": \"ENABLED\"\n }' | jq '{id: .id, error: .error.message}'","type":"text"}]},{"type":"paragraph","content":[{"text":"For \"Scale winners\":","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Increase budget 20% for ad sets with ROAS > 3x in last 7 days\ncurl -s -X POST \"https://graph.facebook.com/v20.0/${META_ACCOUNT}/adrules_library\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"name\": \"Scale winning ad sets\",\n \"schedule_spec\": {\"schedule_type\": \"DAILY\"},\n \"evaluation_spec\": {\n \"evaluation_type\": \"SCHEDULE\",\n \"filters\": [\n {\"field\": \"purchase_roas\", \"value\": [3], \"operator\": \"GREATER_THAN\"},\n {\"field\": \"entity_type\", \"value\": [\"ADSET\"], \"operator\": \"EQUAL\"},\n {\"field\": \"time_preset\", \"value\": [\"LAST_7_DAYS\"], \"operator\": \"EQUAL\"}\n ]\n },\n \"execution_spec\": {\n \"execution_type\": \"INCREASE_BUDGET\",\n \"execution_options\": [{\"field\": \"budget_value\", \"value\": \"20\", \"operator\": \"PERCENTAGE\"}]\n },\n \"status\": \"ENABLED\"\n }' | jq '{id: .id, error: .error.message}'","type":"text"}]},{"type":"paragraph","content":[{"text":"Print: ","type":"text"},{"text":"Rule created (ID: \u003cID>). Runs semi-hourly and will auto-pause ads with CPA > $50.","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"audiences","type":"text"}]},{"type":"paragraph","content":[{"text":"Create Custom Audience or Lookalike Audience.","type":"text"}]},{"type":"paragraph","content":[{"text":"Prompt via AskUserQuestion:","type":"text","marks":[{"type":"strong"}]}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Audience type: ","type":"text"},{"text":"[Custom — website, Custom — customer list, Lookalike, Skip]","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"paragraph","content":[{"text":"Custom — website (Pixel-based):","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"curl -s -X POST \"https://graph.facebook.com/v20.0/${META_ACCOUNT}/customaudiences\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"name\": \"Website visitors — last 30 days\",\n \"subtype\": \"WEBSITE\",\n \"retention_days\": 30,\n \"rule\": {\"inclusions\": {\"operator\": \"or\", \"rules\": [{\"event_sources\": [{\"id\": \"\u003cPIXEL_ID>\", \"type\": \"pixel\"}], \"retention_seconds\": 2592000, \"filter\": {\"operator\": \"and\", \"filters\": [{\"field\": \"event\", \"operator\": \"eq\", \"value\": \"PageView\"}]}}]}}\n }' | jq '{id: .id, name: .name, error: .error.message}'","type":"text"}]},{"type":"paragraph","content":[{"text":"Note: Replace ","type":"text"},{"text":"\u003cPIXEL_ID>","type":"text","marks":[{"type":"code_inline"}]},{"text":" with actual pixel ID from Meta Events Manager.","type":"text"}]},{"type":"paragraph","content":[{"text":"Lookalike Audience","type":"text","marks":[{"type":"strong"}]},{"text":" (requires origin audience with min 100 matched profiles):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Prompt for origin audience ID via AskUserQuestion (free text)\ncurl -s -X POST \"https://graph.facebook.com/v20.0/${META_ACCOUNT}/customaudiences\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" \\\n -H \"Content-Type: application/json\" \\\n -d \"{\n \\\"name\\\": \\\"Lookalike — ${ORIGIN_AUDIENCE_NAME} 1%\\\",\n \\\"subtype\\\": \\\"LOOKALIKE\\\",\n \\\"origin_audience_id\\\": \\\"${ORIGIN_AUDIENCE_ID}\\\",\n \\\"lookalike_spec\\\": {\n \\\"country\\\": \\\"US\\\",\n \\\"ratio\\\": 0.01,\n \\\"type\\\": \\\"similarity\\\"\n }\n }\" | jq '{id: .id, name: .name, error: .error.message}'","type":"text"}]},{"type":"paragraph","content":[{"text":"Print: ","type":"text"},{"text":"Lookalike audience created (ID: \u003cID>). Typically takes 1-6 hours to populate.","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"advantage","type":"text"}]},{"type":"paragraph","content":[{"text":"Create an Advantage+ Shopping Campaign (AI-optimized).","type":"text"}]},{"type":"paragraph","content":[{"text":"Collect via AskUserQuestion:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Daily budget in dollars (free text)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Campaign name (free text)","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Then confirm: ","type":"text"},{"text":"\"Create Advantage+ campaign '\u003cNAME>' with $\u003cBUDGET>/day?\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" options ","type":"text"},{"text":"[Create, Cancel]","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"BUDGET_CENTS=$(awk \"BEGIN {printf \\\"%d\\\", ${BUDGET_DOLLARS} * 100}\")\ncurl -s -X POST \"https://graph.facebook.com/v20.0/${META_ACCOUNT}/campaigns\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" \\\n -H \"Content-Type: application/json\" \\\n -d \"{\n \\\"name\\\": \\\"${CAMPAIGN_NAME}\\\",\n \\\"objective\\\": \\\"OUTCOME_SALES\\\",\n \\\"status\\\": \\\"PAUSED\\\",\n \\\"special_ad_categories\\\": [],\n \\\"daily_budget\\\": ${BUDGET_CENTS},\n \\\"smart_promotion_type\\\": \\\"AUTOMATED_SHOPPING_ADS\\\"\n }\" | jq '{id: .id, error: .error.message}'","type":"text"}]},{"type":"paragraph","content":[{"text":"Print: ","type":"text"},{"text":"Advantage+ campaign \"${CAMPAIGN_NAME}\" created (ID: \u003cID>, status: PAUSED). Meta AI will optimize targeting and creative delivery once enabled.","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"analytics / ga4","type":"text"}]},{"type":"paragraph","content":[{"text":"Pull GA4 data via the Data API using gcloud ADC.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Get access token","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"GA4_TOKEN=$(gcloud auth application-default print-access-token 2>/dev/null)","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Sessions + conversions (last 7d)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"curl -s -X POST \"https://analyticsdata.googleapis.com/v1beta/properties/${GA4_PROPERTY}:runReport\" \\\n -H \"Authorization: Bearer ${GA4_TOKEN}\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"dateRanges\": [{\"startDate\": \"7daysAgo\", \"endDate\": \"today\"}],\n \"metrics\": [\n {\"name\": \"sessions\"},\n {\"name\": \"totalUsers\"},\n {\"name\": \"conversions\"},\n {\"name\": \"totalRevenue\"},\n {\"name\": \"bounceRate\"},\n {\"name\": \"averageSessionDuration\"}\n ]\n }' | jq '.rows[0].metricValues | {sessions: .[0].value, users: .[1].value, conversions: .[2].value, revenue: .[3].value, bounce_rate: .[4].value}'","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Traffic sources (last 7d)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"curl -s -X POST \"https://analyticsdata.googleapis.com/v1beta/properties/${GA4_PROPERTY}:runReport\" \\\n -H \"Authorization: Bearer ${GA4_TOKEN}\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"dateRanges\": [{\"startDate\": \"7daysAgo\", \"endDate\": \"today\"}],\n \"dimensions\": [{\"name\": \"sessionDefaultChannelGrouping\"}],\n \"metrics\": [{\"name\": \"sessions\"}, {\"name\": \"conversions\"}],\n \"orderBys\": [{\"metric\": {\"metricName\": \"sessions\"}, \"desc\": true}],\n \"limit\": 8\n }' | jq '.rows[] | {channel: .dimensionValues[0].value, sessions: .metricValues[0].value, conversions: .metricValues[1].value}'","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Top pages (last 7d)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"curl -s -X POST \"https://analyticsdata.googleapis.com/v1beta/properties/${GA4_PROPERTY}:runReport\" \\\n -H \"Authorization: Bearer ${GA4_TOKEN}\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"dateRanges\": [{\"startDate\": \"7daysAgo\", \"endDate\": \"today\"}],\n \"dimensions\": [{\"name\": \"pagePath\"}],\n \"metrics\": [{\"name\": \"screenPageViews\"}, {\"name\": \"averageSessionDuration\"}],\n \"orderBys\": [{\"metric\": {\"metricName\": \"screenPageViews\"}, \"desc\": true}],\n \"limit\": 10\n }' | jq '.rows[] | {page: .dimensionValues[0].value, views: .metricValues[0].value}'","type":"text"}]},{"type":"paragraph","content":[{"text":"If ","type":"text"},{"text":"GA4_TOKEN","type":"text","marks":[{"type":"code_inline"}]},{"text":" is empty or gcloud not available, output: ","type":"text"},{"text":"GA4 not configured — run /ops:marketing setup or configure gcloud ADC","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Output format","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n ANALYTICS (GA4) — last 7d\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n Sessions: [N] Users: [N]\n Conversions: [N] CVR: [X]%\n Revenue: $[X]\n Bounce Rate: [X]% Avg Session: [Xm Xs]\n\n TRAFFIC SOURCES\n [channel] [N sessions] [N conversions]\n ...\n\n TOP PAGES\n [path] [N views]","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"ga4-advanced","type":"text"}]},{"type":"paragraph","content":[{"text":"Advanced GA4 analytics: realtime, funnel, cohort, audience export, and pivot reports.","type":"text"}]},{"type":"paragraph","content":[{"text":"Credential check","type":"text","marks":[{"type":"strong"}]},{"text":": If ","type":"text"},{"text":"GA4_TOKEN","type":"text","marks":[{"type":"code_inline"}]},{"text":" is empty or ","type":"text"},{"text":"GA4_PROPERTY","type":"text","marks":[{"type":"code_inline"}]},{"text":" is missing, print ","type":"text"},{"text":"GA4 not configured — run /ops:marketing setup or configure gcloud ADC","type":"text","marks":[{"type":"code_inline"}]},{"text":" and stop.","type":"text"}]},{"type":"paragraph","content":[{"text":"Route ","type":"text"},{"text":"$ARGUMENTS","type":"text","marks":[{"type":"code_inline"}]},{"text":" within ga4-advanced (matches ","type":"text"},{"text":"ga4 \u003csub>","type":"text","marks":[{"type":"code_inline"}]},{"text":" pattern):","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Input","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Action","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"realtime","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Active users right now (last 30 min)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"funnel","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Conversion funnel with step visualization","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"cohort","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Cohort retention analysis by device","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"audience","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Async audience segment export","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"pivot","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Multi-dimensional pivot report","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"realtime","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"GA4_TOKEN=$(gcloud auth application-default print-access-token 2>/dev/null)\nRESULT=$(curl -s -X POST \"https://analyticsdata.googleapis.com/v1beta/properties/${GA4_PROPERTY}:runRealtimeReport\" \\\n -H \"Authorization: Bearer ${GA4_TOKEN}\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"minuteRanges\": [{\"startMinutesAgo\": 29, \"endMinutesAgo\": 0}],\n \"dimensions\": [\n {\"name\": \"unifiedScreenName\"},\n {\"name\": \"deviceCategory\"}\n ],\n \"metrics\": [{\"name\": \"activeUsers\"}],\n \"orderBys\": [{\"metric\": {\"metricName\": \"activeUsers\"}, \"desc\": true}],\n \"limit\": 10\n }')\n\nTOTAL=$(echo \"$RESULT\" | jq '[.rows[]?.metricValues[0].value | tonumber] | add // 0')\n\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \" GA4 REALTIME — Last 30 Minutes\"\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \"\"\necho \"Active Users Right Now: ${TOTAL}\"\necho \"\"\necho \"Top Pages:\"\nprintf \"| %-40s | %-8s | %-7s |\\n\" \"Page\" \"Device\" \"Users\"\nprintf \"|%s|%s|%s|\\n\" \"------------------------------------------\" \"----------\" \"---------\"\necho \"$RESULT\" | jq -r '.rows[]? | [.dimensionValues[0].value, .dimensionValues[1].value, .metricValues[0].value] | @tsv' 2>/dev/null | \\\n while IFS=

OPS ► MARKETING COMMAND CENTER Runtime Context Before executing, load available context: 1. Preferences : Read - — display all timestamps correctly - , , , , — check userConfig keys before env vars - , , , , , — Google Ads credentials 2. Daemon health : Read - If is not null → surface it before running any channel queries 3. Secrets : Resolve API keys via userConfig → env vars → Doppler MCP ( ) → Doppler CLI fallback (see Credential Resolution section below) CLI/API Reference Klaviyo REST API | Endpoint | Method | Description | |----------|--------|-------------| | | GET | All lists + subscri…

\\t' read -r page device users; do\n printf \"| %-40s | %-8s | %-7s |\\n\" \"${page:0:40}\" \"$device\" \"$users\"\n done","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"funnel","type":"text"}]},{"type":"paragraph","content":[{"text":"Ask user for funnel steps via AskUserQuestion (free text). Default template uses session_start → page_view → purchase.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# NOTE: Uses v1alpha — breaking changes possible per Google's versioning policy\nGA4_TOKEN=$(gcloud auth application-default print-access-token 2>/dev/null)\n\n# Prompt for funnel type first\n# AskUserQuestion: \"Funnel mode?\" options [Closed funnel, Open funnel]\n\nIS_OPEN=$([ \"$FUNNEL_MODE\" = \"Open funnel\" ] && echo \"true\" || echo \"false\")\n\nRESULT=$(curl -s -X POST \"https://analyticsdata.googleapis.com/v1alpha/properties/${GA4_PROPERTY}:runFunnelReport\" \\\n -H \"Authorization: Bearer ${GA4_TOKEN}\" \\\n -H \"Content-Type: application/json\" \\\n -d \"{\n \\\"dateRanges\\\": [{\\\"startDate\\\": \\\"30daysAgo\\\", \\\"endDate\\\": \\\"today\\\"}],\n \\\"funnel\\\": {\n \\\"isOpenFunnel\\\": ${IS_OPEN},\n \\\"steps\\\": [\n {\n \\\"name\\\": \\\"Session Start\\\",\n \\\"filterExpression\\\": {\\\"funnelEventFilter\\\": {\\\"eventName\\\": \\\"session_start\\\"}}\n },\n {\n \\\"name\\\": \\\"Page View\\\",\n \\\"filterExpression\\\": {\\\"funnelEventFilter\\\": {\\\"eventName\\\": \\\"page_view\\\"}}\n },\n {\n \\\"name\\\": \\\"Purchase\\\",\n \\\"filterExpression\\\": {\\\"funnelEventFilter\\\": {\\\"eventName\\\": \\\"purchase\\\"}}\n }\n ]\n },\n \\\"funnelBreakdown\\\": {\n \\\"breakdownDimension\\\": {\\\"name\\\": \\\"deviceCategory\\\"},\n \\\"limit\\\": 4\n }\n }\")\n\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \" GA4 FUNNEL — Last 30 Days (${FUNNEL_MODE})\"\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \"\"\necho \"$RESULT\" | jq -r '\n .funnelTable.rows[]? |\n \"Step: \\(.dimensionValues[0].value) Users: \\(.metricValues[0].value) Completion: \\(.metricValues[1].value)% Abandoned: \\(.metricValues[2].value)\"\n' 2>/dev/null || echo \"No funnel data — ensure purchase events are firing in GA4.\"","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"cohort","type":"text"}]},{"type":"paragraph","content":[{"text":"Weekly cohort retention for users acquired in the past month, broken down by device.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"GA4_TOKEN=$(gcloud auth application-default print-access-token 2>/dev/null)\nSTART_DATE=$(date -v-30d +%Y-%m-%d 2>/dev/null || date -d '30 days ago' +%Y-%m-%d)\nEND_DATE=$(date +%Y-%m-%d)\n\nRESULT=$(curl -s -X POST \"https://analyticsdata.googleapis.com/v1beta/properties/${GA4_PROPERTY}:runReport\" \\\n -H \"Authorization: Bearer ${GA4_TOKEN}\" \\\n -H \"Content-Type: application/json\" \\\n -d \"{\n \\\"dimensions\\\": [\n {\\\"name\\\": \\\"cohort\\\"},\n {\\\"name\\\": \\\"cohortNthWeek\\\"},\n {\\\"name\\\": \\\"deviceCategory\\\"}\n ],\n \\\"metrics\\\": [\n {\\\"name\\\": \\\"cohortActiveUsers\\\"},\n {\\\"name\\\": \\\"cohortRetentionFraction\\\"}\n ],\n \\\"cohortSpec\\\": {\n \\\"cohorts\\\": [{\n \\\"dimension\\\": \\\"firstSessionDate\\\",\n \\\"dateRange\\\": {\\\"startDate\\\": \\\"${START_DATE}\\\", \\\"endDate\\\": \\\"${END_DATE}\\\"}\n }],\n \\\"cohortsRange\\\": {\n \\\"granularity\\\": \\\"WEEKLY\\\",\n \\\"startOffset\\\": 0,\n \\\"endOffset\\\": 5\n }\n }\n }\")\n\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \" GA4 COHORT RETENTION — Last 30 Days\"\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \"\"\nprintf \"| %-12s | %-6s | %-8s | %-10s | %-10s |\\n\" \"Cohort\" \"Week\" \"Device\" \"Users\" \"Retention%\"\nprintf \"|%s|%s|%s|%s|%s|\\n\" \"--------------\" \"--------\" \"----------\" \"------------\" \"------------\"\necho \"$RESULT\" | jq -r '.rows[]? | [\n .dimensionValues[0].value,\n .dimensionValues[1].value,\n .dimensionValues[2].value,\n .metricValues[0].value,\n (.metricValues[1].value | tonumber * 100 | tostring | split(\".\")[0])\n] | @tsv' 2>/dev/null | \\\n while IFS=

OPS ► MARKETING COMMAND CENTER Runtime Context Before executing, load available context: 1. Preferences : Read - — display all timestamps correctly - , , , , — check userConfig keys before env vars - , , , , , — Google Ads credentials 2. Daemon health : Read - If is not null → surface it before running any channel queries 3. Secrets : Resolve API keys via userConfig → env vars → Doppler MCP ( ) → Doppler CLI fallback (see Credential Resolution section below) CLI/API Reference Klaviyo REST API | Endpoint | Method | Description | |----------|--------|-------------| | | GET | All lists + subscri…

\\t' read -r cohort week device users retention; do\n printf \"| %-12s | %-6s | %-8s | %-10s | %-10s |\\n\" \"$cohort\" \"$week\" \"$device\" \"$users\" \"${retention}%\"\n done","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"audience","type":"text"}]},{"type":"paragraph","content":[{"text":"Async audience export: create → poll until ACTIVE → show user count.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"GA4_TOKEN=$(gcloud auth application-default print-access-token 2>/dev/null)\n\n# Step 1: List available audiences so user can pick one\nAUDIENCES=$(curl -s \"https://analyticsadmin.googleapis.com/v1alpha/properties/${GA4_PROPERTY}/audiences\" \\\n -H \"Authorization: Bearer ${GA4_TOKEN}\")\necho \"Available audiences:\"\necho \"$AUDIENCES\" | jq -r '.audiences[]? | \"\\(.name | split(\"/\") | last): \\(.displayName)\"' 2>/dev/null\n\n# AskUserQuestion: \"Enter audience ID from list above:\" (free text)\n\n# Step 2: Create export\nEXPORT_RESP=$(curl -s -X POST \\\n \"https://analyticsdata.googleapis.com/v1beta/properties/${GA4_PROPERTY}/audienceExports\" \\\n -H \"Authorization: Bearer ${GA4_TOKEN}\" \\\n -H \"Content-Type: application/json\" \\\n -d \"{\n \\\"audience\\\": \\\"properties/${GA4_PROPERTY}/audiences/${AUDIENCE_ID}\\\",\n \\\"dimensions\\\": [\n {\\\"dimensionName\\\": \\\"deviceId\\\"},\n {\\\"dimensionName\\\": \\\"isAdsPersonalizationAllowed\\\"}\n ]\n }\")\nEXPORT_NAME=$(echo \"$EXPORT_RESP\" | jq -r '.name // empty')\n\nif [ -z \"$EXPORT_NAME\" ]; then\n echo \"Failed to create export: $(echo \"$EXPORT_RESP\" | jq -r '.error.message // \"unknown error\"')\"\n exit 0\nfi\n\necho \"Export created: ${EXPORT_NAME}\"\necho \"Polling for completion (small audiences: ~30s, large: up to 15 min)...\"\n\n# Step 3: Poll until ACTIVE or FAILED\nATTEMPTS=0\nwhile [ $ATTEMPTS -lt 60 ]; do\n STATUS_RESP=$(curl -s \"https://analyticsdata.googleapis.com/v1beta/${EXPORT_NAME}\" \\\n -H \"Authorization: Bearer ${GA4_TOKEN}\")\n STATE=$(echo \"$STATUS_RESP\" | jq -r '.state // \"UNKNOWN\"')\n PCT=$(echo \"$STATUS_RESP\" | jq -r '.percentageCompleted // 0')\n \n if [ \"$STATE\" = \"ACTIVE\" ]; then break; fi\n if [ \"$STATE\" = \"FAILED\" ]; then\n echo \"Export failed. Try again or check GA4 audience configuration.\"\n exit 0\n fi\n echo \" State: ${STATE} (${PCT}% complete)...\"\n sleep 10\n ATTEMPTS=$((ATTEMPTS + 1))\ndone\n\n# Step 4: Query results\nQUERY_RESP=$(curl -s -X POST \\\n \"https://analyticsdata.googleapis.com/v1beta/${EXPORT_NAME}:query\" \\\n -H \"Authorization: Bearer ${GA4_TOKEN}\")\nROW_COUNT=$(echo \"$QUERY_RESP\" | jq '.rowCount // 0')\n\necho \"\"\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \" GA4 AUDIENCE EXPORT\"\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \" Audience ID: ${AUDIENCE_ID}\"\necho \" Users exported: ${ROW_COUNT}\"\necho \" Ads-eligible: $(echo \"$QUERY_RESP\" | jq '[.audienceRows[]? | select(.dimensionValues[1].value == \"true\")] | length') users\"\necho \"\"\necho \"Export ready. Use this audience for retargeting in Meta or Google Ads.\"","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"pivot","type":"text"}]},{"type":"paragraph","content":[{"text":"Multi-dimensional pivot: channel group × device category × conversions.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"GA4_TOKEN=$(gcloud auth application-default print-access-token 2>/dev/null)\n\nRESULT=$(curl -s -X POST \"https://analyticsdata.googleapis.com/v1beta/properties/${GA4_PROPERTY}:runPivotReport\" \\\n -H \"Authorization: Bearer ${GA4_TOKEN}\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"dateRanges\": [{\"startDate\": \"30daysAgo\", \"endDate\": \"today\"}],\n \"dimensions\": [\n {\"name\": \"sessionDefaultChannelGrouping\"},\n {\"name\": \"deviceCategory\"}\n ],\n \"metrics\": [\n {\"name\": \"sessions\"},\n {\"name\": \"conversions\"},\n {\"name\": \"totalRevenue\"}\n ],\n \"pivots\": [\n {\n \"fieldNames\": [\"sessionDefaultChannelGrouping\"],\n \"limit\": 6\n },\n {\n \"fieldNames\": [\"deviceCategory\"],\n \"limit\": 3\n }\n ]\n }')\n\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \" GA4 PIVOT — Channel × Device (Last 30 Days)\"\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \"\"\nprintf \"| %-20s | %-8s | %-10s | %-11s | %-10s |\\n\" \"Channel\" \"Device\" \"Sessions\" \"Conversions\" \"Revenue\"\nprintf \"|%s|%s|%s|%s|%s|\\n\" \"----------------------\" \"----------\" \"------------\" \"-------------\" \"------------\"\necho \"$RESULT\" | jq -r '.rows[]? | [\n .dimensionValues[0].value,\n .dimensionValues[1].value,\n .metricValues[0].value,\n .metricValues[1].value,\n (.metricValues[2].value | tonumber | . * 100 | round / 100 | tostring)\n] | @tsv' 2>/dev/null | \\\n while IFS=

OPS ► MARKETING COMMAND CENTER Runtime Context Before executing, load available context: 1. Preferences : Read - — display all timestamps correctly - , , , , — check userConfig keys before env vars - , , , , , — Google Ads credentials 2. Daemon health : Read - If is not null → surface it before running any channel queries 3. Secrets : Resolve API keys via userConfig → env vars → Doppler MCP ( ) → Doppler CLI fallback (see Credential Resolution section below) CLI/API Reference Klaviyo REST API | Endpoint | Method | Description | |----------|--------|-------------| | | GET | All lists + subscri…

\\t' read -r channel device sessions convs revenue; do\n printf \"| %-20s | %-8s | %-10s | %-11s | \\$%-9s |\\n\" \"${channel:0:20}\" \"$device\" \"$sessions\" \"$convs\" \"$revenue\"\n done","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"seo / gsc","type":"text"}]},{"type":"paragraph","content":[{"text":"Pull Google Search Console data.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Get access token (same gcloud ADC)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"GSC_TOKEN=$(gcloud auth application-default print-access-token 2>/dev/null)\nGSC_SITE_ENCODED=$(python3 -c \"import urllib.parse; print(urllib.parse.quote('${GSC_SITE}', safe=''))\" 2>/dev/null || echo \"${GSC_SITE}\" | sed 's|:|%3A|g; s|/|%2F|g')","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Search performance (last 28 days)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"curl -s -X POST \"https://searchconsole.googleapis.com/webmasters/v3/sites/${GSC_SITE_ENCODED}/searchAnalytics/query\" \\\n -H \"Authorization: Bearer ${GSC_TOKEN}\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"startDate\": \"'$(date -v-28d +%Y-%m-%d 2>/dev/null || date -d '28 days ago' +%Y-%m-%d)'\",\n \"endDate\": \"'$(date +%Y-%m-%d)'\",\n \"dimensions\": [],\n \"rowLimit\": 1\n }' | jq '{clicks: .rows[0].clicks, impressions: .rows[0].impressions, ctr: .rows[0].ctr, position: .rows[0].position}'","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Top queries (last 28 days)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"curl -s -X POST \"https://searchconsole.googleapis.com/webmasters/v3/sites/${GSC_SITE_ENCODED}/searchAnalytics/query\" \\\n -H \"Authorization: Bearer ${GSC_TOKEN}\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"startDate\": \"'$(date -v-28d +%Y-%m-%d 2>/dev/null || date -d '28 days ago' +%Y-%m-%d)'\",\n \"endDate\": \"'$(date +%Y-%m-%d)'\",\n \"dimensions\": [\"query\"],\n \"rowLimit\": 20,\n \"dimensionFilterGroups\": []\n }' | jq '.rows[] | {query: .keys[0], clicks: .clicks, impressions: .impressions, position: (.position | floor)}'","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Top pages by clicks","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"curl -s -X POST \"https://searchconsole.googleapis.com/webmasters/v3/sites/${GSC_SITE_ENCODED}/searchAnalytics/query\" \\\n -H \"Authorization: Bearer ${GSC_TOKEN}\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"startDate\": \"'$(date -v-28d +%Y-%m-%d 2>/dev/null || date -d '28 days ago' +%Y-%m-%d)'\",\n \"endDate\": \"'$(date +%Y-%m-%d)'\",\n \"dimensions\": [\"page\"],\n \"rowLimit\": 10\n }' | jq '.rows[] | {page: .keys[0], clicks: .clicks, impressions: .impressions, position: (.position | floor)}'","type":"text"}]},{"type":"paragraph","content":[{"text":"If GSC not configured, output: ","type":"text"},{"text":"Search Console not configured — run /ops:marketing setup","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Output format","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n SEO (SEARCH CONSOLE) — last 28d\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n Clicks: [N]\n Impressions: [N]\n CTR: [X]%\n Avg Position: [X]\n\n TOP QUERIES\n [query] [clicks] clicks pos [N]\n ...\n\n TOP PAGES\n [url] [clicks] clicks [impressions] impr","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"social","type":"text"}]},{"type":"paragraph","content":[{"text":"Aggregate available social media metrics. Check which are configured.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Instagram (via Meta Graph API — same token as Meta Ads)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Get Instagram Business Account ID linked to the ad account\ncurl -s \"https://graph.facebook.com/v18.0/me/accounts?fields=instagram_business_account\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" | jq '.data[].instagram_business_account.id' 2>/dev/null\n\n# Then pull media insights\ncurl -s \"https://graph.facebook.com/v18.0/${IG_ACCOUNT_ID}?fields=followers_count,media_count,profile_views\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" | jq '{followers: .followers_count, posts: .media_count, profile_views: .profile_views}'","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"YouTube (if configured via gcloud)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"YT_TOKEN=$(gcloud auth application-default print-access-token 2>/dev/null)\ncurl -s \"https://www.googleapis.com/youtube/v3/channels?part=statistics&mine=true\" \\\n -H \"Authorization: Bearer ${YT_TOKEN}\" | jq '.items[0].statistics | {subscribers: .subscriberCount, views: .viewCount, videos: .videoCount}'","type":"text"}]},{"type":"paragraph","content":[{"text":"Show ","type":"text"},{"text":"[not configured]","type":"text","marks":[{"type":"code_inline"}]},{"text":" for any unconfigured channels rather than failing.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Output format","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n SOCIAL MEDIA\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n Instagram: [N followers] [N posts] [N profile views]\n YouTube: [N subscribers] [N total views]\n TikTok: [not configured] — set TIKTOK_ACCESS_TOKEN","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"instagram","type":"text"}]},{"type":"paragraph","content":[{"text":"Instagram publishing and insights via Instagram Graph API (same ","type":"text"},{"text":"META_TOKEN","type":"text","marks":[{"type":"code_inline"}]},{"text":" as Meta Ads).","type":"text"}]},{"type":"paragraph","content":[{"text":"Prerequisites:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"META_TOKEN","type":"text","marks":[{"type":"code_inline"}]},{"text":" configured (same as Meta Ads)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Instagram Business account linked to a Facebook Page","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"IG_ACCOUNT_ID","type":"text","marks":[{"type":"code_inline"}]},{"text":" resolved via: ","type":"text"},{"text":"curl \"https://graph.facebook.com/v21.0/me/accounts?fields=instagram_business_account\" -H \"Authorization: Bearer ${META_TOKEN}\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" → ","type":"text"},{"text":"data[0].instagram_business_account.id","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"paragraph","content":[{"text":"Rate limit:","type":"text","marks":[{"type":"strong"}]},{"text":" 200 API calls/hour per app. Demographics require 48h reporting delay. Media insights require account with >1,000 followers.","type":"text"}]},{"type":"paragraph","content":[{"text":"Resolve IG account ID at the start of every instagram invocation:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"IG_ACCOUNT_ID=$(claude plugin config get instagram_account_id 2>/dev/null)\nif [ -z \"$IG_ACCOUNT_ID\" ]; then\n IG_ACCOUNT_ID=$(curl -s \"https://graph.facebook.com/v21.0/me/accounts?fields=instagram_business_account\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" | jq -r '.data[0].instagram_business_account.id // empty')\n # Cache it\n [ -n \"$IG_ACCOUNT_ID\" ] && claude plugin config set instagram_account_id \"$IG_ACCOUNT_ID\" 2>/dev/null\nfi\nif [ -z \"$IG_ACCOUNT_ID\" ]; then\n echo \"Instagram Business account not linked to your Meta token. Ensure your Facebook Page has an Instagram Business account connected.\"\n exit 0\nfi","type":"text"}]},{"type":"paragraph","content":[{"text":"Route ","type":"text"},{"text":"$ARGUMENTS","type":"text","marks":[{"type":"code_inline"}]},{"text":" within instagram:","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Input","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Action","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"post \u003cIMAGE_URL>","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Publish image post to feed","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"reel \u003cVIDEO_URL>","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Publish a Reel","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"story \u003cIMAGE_URL|VIDEO_URL>","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Publish a Story","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"insights \u003cMEDIA_ID>","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Per-post metrics","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"account-insights [days]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Account-level reach + impressions","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"demographics","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Audience age/gender/location","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"post","type":"text"}]},{"type":"paragraph","content":[{"text":"Publish an image post (two-step: create container → publish).","type":"text"}]},{"type":"paragraph","content":[{"text":"Collect via AskUserQuestion:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Image URL (publicly accessible HTTPS URL) — free text","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Caption — free text","type":"text"}]}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Step 1: Create media container\nCONTAINER=$(curl -s -X POST \"https://graph.facebook.com/v21.0/${IG_ACCOUNT_ID}/media\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" \\\n -F \"image_url=${IMAGE_URL}\" \\\n -F \"caption=${CAPTION}\" \\\n -F \"media_type=IMAGE\")\nCONTAINER_ID=$(echo \"$CONTAINER\" | jq -r '.id // empty')\n\nif [ -z \"$CONTAINER_ID\" ]; then\n echo \"Failed to create media container: $(echo \"$CONTAINER\" | jq -r '.error.message // \"unknown error\"')\"\n exit 0\nfi\n\n# Step 2: Publish\nPUBLISH=$(curl -s -X POST \"https://graph.facebook.com/v21.0/${IG_ACCOUNT_ID}/media_publish\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" \\\n -F \"creation_id=${CONTAINER_ID}\")\nMEDIA_ID=$(echo \"$PUBLISH\" | jq -r '.id // empty')\n\nif [ -n \"$MEDIA_ID\" ]; then\n echo \"Post published (Media ID: ${MEDIA_ID}). View at https://www.instagram.com/ — may take 1-2 min to appear.\"\nelse\n echo \"Publish failed: $(echo \"$PUBLISH\" | jq -r '.error.message // \"unknown error\"')\"\nfi","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"reel","type":"text"}]},{"type":"paragraph","content":[{"text":"Publish a Reel. Video must be an HTTPS URL (MP4, H.264, max 15 min, min 500px width).","type":"text"}]},{"type":"paragraph","content":[{"text":"Collect via AskUserQuestion:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Video URL (HTTPS) — free text","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Caption — free text","type":"text"}]}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Step 1: Create video container (async — must poll for status)\nCONTAINER=$(curl -s -X POST \"https://graph.facebook.com/v21.0/${IG_ACCOUNT_ID}/media\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" \\\n -F \"media_type=REELS\" \\\n -F \"video_url=${VIDEO_URL}\" \\\n -F \"caption=${CAPTION}\" \\\n -F \"share_to_feed=true\")\nCONTAINER_ID=$(echo \"$CONTAINER\" | jq -r '.id // empty')\n\nif [ -z \"$CONTAINER_ID\" ]; then\n echo \"Failed to create Reel container: $(echo \"$CONTAINER\" | jq -r '.error.message // \"unknown error\"')\"\n exit 0\nfi\n\necho \"Reel uploading... polling for ready status.\"\n\n# Poll until status is FINISHED\nATTEMPTS=0\nwhile [ $ATTEMPTS -lt 30 ]; do\n STATUS=$(curl -s \"https://graph.facebook.com/v21.0/${CONTAINER_ID}?fields=status_code\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" | jq -r '.status_code // \"UNKNOWN\"')\n [ \"$STATUS\" = \"FINISHED\" ] && break\n [ \"$STATUS\" = \"ERROR\" ] && echo \"Reel processing failed.\" && exit 0\n sleep 10\n ATTEMPTS=$((ATTEMPTS + 1))\ndone\n\n# Step 2: Publish\nPUBLISH=$(curl -s -X POST \"https://graph.facebook.com/v21.0/${IG_ACCOUNT_ID}/media_publish\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" \\\n -F \"creation_id=${CONTAINER_ID}\")\nMEDIA_ID=$(echo \"$PUBLISH\" | jq -r '.id // empty')\n\nif [ -n \"$MEDIA_ID\" ]; then\n echo \"Reel published (Media ID: ${MEDIA_ID}). Reach and plays metrics available after 24-48h.\"\nelse\n echo \"Reel publish failed: $(echo \"$PUBLISH\" | jq -r '.error.message // \"unknown error\"')\"\nfi","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"story","type":"text"}]},{"type":"paragraph","content":[{"text":"Publish a Story (image or video, 24h expiry).","type":"text"}]},{"type":"paragraph","content":[{"text":"Collect via AskUserQuestion:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Content URL (HTTPS image or video) — free text","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Content type: ","type":"text"},{"text":"[Image story, Video story]","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"if [ \"$CONTENT_TYPE\" = \"Video story\" ]; then\n MEDIA_TYPE=\"VIDEO\"\n URL_FIELD=\"video_url\"\nelse\n MEDIA_TYPE=\"IMAGE\"\n URL_FIELD=\"image_url\"\nfi\n\nCONTAINER=$(curl -s -X POST \"https://graph.facebook.com/v21.0/${IG_ACCOUNT_ID}/media\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" \\\n -F \"media_type=STORIES\" \\\n -F \"${URL_FIELD}=${CONTENT_URL}\")\nCONTAINER_ID=$(echo \"$CONTAINER\" | jq -r '.id // empty')\n\nif [ -z \"$CONTAINER_ID\" ]; then\n echo \"Failed to create Story container: $(echo \"$CONTAINER\" | jq -r '.error.message // \"unknown error\"')\"\n exit 0\nfi\n\nPUBLISH=$(curl -s -X POST \"https://graph.facebook.com/v21.0/${IG_ACCOUNT_ID}/media_publish\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" \\\n -F \"creation_id=${CONTAINER_ID}\")\nMEDIA_ID=$(echo \"$PUBLISH\" | jq -r '.id // empty')\n\nif [ -n \"$MEDIA_ID\" ]; then\n echo \"Story published (Media ID: ${MEDIA_ID}). Expires after 24 hours.\"\nelse\n echo \"Story publish failed: $(echo \"$PUBLISH\" | jq -r '.error.message // \"unknown error\"')\"\nfi","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"insights \u003cMEDIA_ID>","type":"text"}]},{"type":"paragraph","content":[{"text":"Per-post metrics. Note: reach, saves, shares deprecated for non-Reels video; plays only for Reels/video.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"METRICS=\"reach,saved,shares,comments_count,like_count,impressions\"\n# For Reels, add plays: detect via media_type field\nMEDIA_TYPE=$(curl -s \"https://graph.facebook.com/v21.0/${MEDIA_ID}?fields=media_type\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" | jq -r '.media_type // \"IMAGE\"')\n[ \"$MEDIA_TYPE\" = \"VIDEO\" ] || [ \"$MEDIA_TYPE\" = \"REEL\" ] && METRICS=\"${METRICS},plays\"\n\nRESULT=$(curl -s \"https://graph.facebook.com/v21.0/${MEDIA_ID}/insights?metric=${METRICS}&period=lifetime\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\")\n\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \" INSTAGRAM POST INSIGHTS — ${MEDIA_ID}\"\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \"\"\necho \"$RESULT\" | jq -r '.data[]? | \" \\(.name): \\(.values[0].value)\"' 2>/dev/null || \\\n echo \"No insights data — account must have >1,000 followers for insights access.\"","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"account-insights [days]","type":"text"}]},{"type":"paragraph","content":[{"text":"Account-level reach and impressions. Default: last 7 days.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"DAYS=\"${DAYS:-7}\"\nEND_DATE=$(date +%Y-%m-%d)\nSTART_DATE=$(date -v-${DAYS}d +%Y-%m-%d 2>/dev/null || date -d \"${DAYS} days ago\" +%Y-%m-%d)\n\nRESULT=$(curl -s \"https://graph.facebook.com/v21.0/${IG_ACCOUNT_ID}/insights?metric=reach,impressions,profile_views&period=day&since=${START_DATE}&until=${END_DATE}\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\")\n\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \" INSTAGRAM ACCOUNT INSIGHTS — Last ${DAYS} Days\"\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \" Note: Data reflects 24-48h reporting delay\"\necho \"\"\n\nREACH=$(echo \"$RESULT\" | jq '[.data[]? | select(.name == \"reach\") | .values[]?.value | tonumber] | add // 0')\nIMPRESSIONS=$(echo \"$RESULT\" | jq '[.data[]? | select(.name == \"impressions\") | .values[]?.value | tonumber] | add // 0')\nPROFILE_VIEWS=$(echo \"$RESULT\" | jq '[.data[]? | select(.name == \"profile_views\") | .values[]?.value | tonumber] | add // 0')\n\necho \" Reach: ${REACH}\"\necho \" Impressions: ${IMPRESSIONS}\"\necho \" Profile Views: ${PROFILE_VIEWS}\"","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"demographics","type":"text"}]},{"type":"paragraph","content":[{"text":"Audience breakdown by age/gender and top locations. Requires lifetime period (48h delay).","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"RESULT=$(curl -s \"https://graph.facebook.com/v21.0/${IG_ACCOUNT_ID}/insights?metric=audience_gender_age,audience_city,audience_country&period=lifetime\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\")\n\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \" INSTAGRAM DEMOGRAPHICS\"\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \" Note: Top 45 segments shown. Data has 48h delay.\"\necho \"\"\n\necho \"AGE / GENDER BREAKDOWN:\"\necho \"$RESULT\" | jq -r '.data[]? | select(.name == \"audience_gender_age\") | .values[0].value | to_entries[] | \" \\(.key): \\(.value)%\"' 2>/dev/null | head -20\n\necho \"\"\necho \"TOP CITIES:\"\necho \"$RESULT\" | jq -r '.data[]? | select(.name == \"audience_city\") | .values[0].value | to_entries | sort_by(-.value) | .[0:10][] | \" \\(.key): \\(.value)%\"' 2>/dev/null\n\necho \"\"\necho \"TOP COUNTRIES:\"\necho \"$RESULT\" | jq -r '.data[]? | select(.name == \"audience_country\") | .values[0].value | to_entries | sort_by(-.value) | .[0:10][] | \" \\(.key): \\(.value)%\"' 2>/dev/null","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"google-ads","type":"text"}]},{"type":"paragraph","content":[{"text":"Credential check","type":"text","marks":[{"type":"strong"}]},{"text":": If ","type":"text"},{"text":"GADS_DEV_TOKEN","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"GADS_REFRESH_TOKEN","type":"text","marks":[{"type":"code_inline"}]},{"text":" is empty after resolution, print: ","type":"text"},{"text":"Warning: Google Ads not configured. Run /ops:setup marketing to set up credentials.","type":"text","marks":[{"type":"code_inline"}]},{"text":" and stop.","type":"text"}]},{"type":"paragraph","content":[{"text":"Token refresh","type":"text","marks":[{"type":"strong"}]},{"text":": Run the access token refresh curl (from Credential Resolution above) at the start of every google-ads invocation. If ","type":"text"},{"text":"GADS_ACCESS_TOKEN","type":"text","marks":[{"type":"code_inline"}]},{"text":" is null or \"null\", print: ","type":"text"},{"text":"Warning: Google Ads token refresh failed. Check client_id/client_secret/refresh_token in /ops:setup.","type":"text","marks":[{"type":"code_inline"}]},{"text":" and stop.","type":"text"}]},{"type":"paragraph","content":[{"text":"Route ","type":"text"},{"text":"$ARGUMENTS","type":"text","marks":[{"type":"code_inline"}]},{"text":" within the google-ads section:","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Input","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Action","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"(empty), dashboard, overview","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Campaign performance dashboard (last 7 days)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"search-terms, terms","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Search Terms Report with negative keyword candidates (last 30 days)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"budget-recs, recommendations, recs","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Budget optimization recommendations from Google","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"campaigns, manage","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Campaign management — list, create, pause, enable, adjust budget","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"keywords, kw, keyword-planner","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Keyword Planner — discover keywords with volume and bid data","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"ad-groups, ag","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Ad group management — list, create, add/remove keywords, adjust bids","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Dashboard (default — no args, ","type":"text"},{"text":"dashboard","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"overview","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Campaign performance — last 7 days\nCAMPAIGNS=$(curl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/googleAds:searchStream\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary '{\n \"query\": \"SELECT campaign.id, campaign.name, campaign.status, campaign_budget.amount_micros, metrics.cost_micros, metrics.impressions, metrics.clicks, metrics.conversions, metrics.conversions_value FROM campaign WHERE segments.date DURING LAST_7_DAYS AND campaign.status != REMOVED ORDER BY metrics.cost_micros DESC LIMIT 20\"\n }')\n\n# Check for API error\nGADS_ERROR=$(echo \"$CAMPAIGNS\" | jq -r '.[0].error.message // empty' 2>/dev/null)\nif [ -n \"$GADS_ERROR\" ]; then\n echo \"Google Ads API error: ${GADS_ERROR}\"\n echo \"Check credentials with /ops:marketing setup.\"\n exit 0\nfi\n\n# Check for empty results\nCAMPAIGN_COUNT=$(echo \"$CAMPAIGNS\" | jq '[.[].results[]?] | length' 2>/dev/null || echo \"0\")\nif [ \"$CAMPAIGN_COUNT\" -eq 0 ]; then\n echo \"No active campaigns found in the last 7 days.\"\n exit 0\nfi\n\n# Compute totals\nTOTAL_SPEND=$(echo \"$CAMPAIGNS\" | jq '[.[].results[]?.metrics.costMicros // \"0\" | tonumber] | add / 1000000' 2>/dev/null)\nTOTAL_CONVERSIONS=$(echo \"$CAMPAIGNS\" | jq '[.[].results[]?.metrics.conversions // \"0\" | tonumber] | add' 2>/dev/null)\nTOTAL_VALUE=$(echo \"$CAMPAIGNS\" | jq '[.[].results[]?.metrics.conversionsValue // \"0\" | tonumber] | add' 2>/dev/null)\nOVERALL_ROAS=$(awk \"BEGIN { if (${TOTAL_SPEND:-0} > 0) printf \\\"%.2f\\\", ${TOTAL_VALUE:-0} / ${TOTAL_SPEND:-1}; else print \\\"—\\\" }\")\nTOTAL_SPEND_FMT=$(awk \"BEGIN { printf \\\"%.2f\\\", ${TOTAL_SPEND:-0} }\")\n\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \" GOOGLE ADS — Last 7 Days\"\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \"\"\necho \"Total Spend: \\${TOTAL_SPEND_FMT}\"\necho \"Total Conversions: ${TOTAL_CONVERSIONS}\"\necho \"Overall ROAS: ${OVERALL_ROAS}\"\necho \"\"\n\n# Print table header\nprintf \"| %-30s | %-8s | %-10s | %-10s | %-8s | %-8s | %-6s | %-6s | %-6s |\\n\" \\\n \"Campaign\" \"Status\" \"Budget/day\" \"Spend\" \"Impr\" \"Clicks\" \"CTR\" \"Conv\" \"ROAS\"\nprintf \"|%s|%s|%s|%s|%s|%s|%s|%s|%s|\\n\" \\\n \"--------------------------------\" \"----------\" \"------------\" \"------------\" \"----------\" \"----------\" \"--------\" \"--------\" \"--------\"\n\n# Print each campaign row\necho \"$CAMPAIGNS\" | jq -r '.[].results[]? | [\n .campaign.name,\n .campaign.status,\n (.campaignBudget.amountMicros // \"0\" | tonumber / 1000000),\n (.metrics.costMicros // \"0\" | tonumber / 1000000),\n (.metrics.impressions // \"0\" | tonumber),\n (.metrics.clicks // \"0\" | tonumber),\n (.metrics.conversions // \"0\" | tonumber),\n (.metrics.conversionsValue // \"0\" | tonumber),\n (.metrics.costMicros // \"0\" | tonumber)\n] | @tsv' 2>/dev/null | while IFS=

OPS ► MARKETING COMMAND CENTER Runtime Context Before executing, load available context: 1. Preferences : Read - — display all timestamps correctly - , , , , — check userConfig keys before env vars - , , , , , — Google Ads credentials 2. Daemon health : Read - If is not null → surface it before running any channel queries 3. Secrets : Resolve API keys via userConfig → env vars → Doppler MCP ( ) → Doppler CLI fallback (see Credential Resolution section below) CLI/API Reference Klaviyo REST API | Endpoint | Method | Description | |----------|--------|-------------| | | GET | All lists + subscri…

\\t' read -r name status budget_raw spend_raw impr_raw clicks_raw conv_raw value_raw cost_raw; do\n BUDGET=$(awk \"BEGIN { printf \\\"%.2f\\\", ${budget_raw:-0} }\")\n SPEND=$(awk \"BEGIN { printf \\\"%.2f\\\", ${spend_raw:-0} }\")\n CTR=$(awk \"BEGIN { if (${impr_raw:-0} > 0) printf \\\"%.2f\\\", ${clicks_raw:-0} / ${impr_raw:-0} * 100; else print \\\"0.00\\\" }\")\n ROAS=$(awk \"BEGIN { if (${spend_raw:-0} > 0) printf \\\"%.2f\\\", ${value_raw:-0} / ${spend_raw:-1}; else print \\\"—\\\" }\")\n printf \"| %-30s | %-8s | \\$%-9s | \\$%-9s | %-8s | %-8s | %-5s%% | %-6s | %-6s |\\n\" \\\n \"${name:0:30}\" \"$status\" \"$BUDGET\" \"$SPEND\" \"$impr_raw\" \"$clicks_raw\" \"$CTR\" \"$conv_raw\" \"$ROAS\"\ndone","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Search Terms (","type":"text"},{"text":"search-terms","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"terms","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"SEARCH_TERMS=$(curl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/googleAds:searchStream\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary '{\n \"query\": \"SELECT search_term_view.search_term, search_term_view.status, campaign.name, ad_group.name, metrics.impressions, metrics.clicks, metrics.cost_micros, metrics.conversions FROM search_term_view WHERE segments.date DURING LAST_30_DAYS AND metrics.impressions > 0 ORDER BY metrics.impressions DESC LIMIT 100\"\n }')\n\n# Check for API error\nGADS_ST_ERROR=$(echo \"$SEARCH_TERMS\" | jq -r '.[0].error.message // empty' 2>/dev/null)\nif [ -n \"$GADS_ST_ERROR\" ]; then\n echo \"Google Ads API error: ${GADS_ST_ERROR}\"\n echo \"Check credentials with /ops:marketing setup.\"\n exit 0\nfi\n\nTERM_COUNT=$(echo \"$SEARCH_TERMS\" | jq '[.[].results[]?] | length' 2>/dev/null || echo \"0\")\nif [ \"$TERM_COUNT\" -eq 0 ]; then\n echo \"No search term data found for the last 30 days.\"\n exit 0\nfi\n\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \" SEARCH TERMS REPORT — Last 30 Days\"\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \"\"\n\nprintf \"| %-30s | %-10s | %-20s | %-15s | %-6s | %-6s | %-7s | %-6s |\\n\" \\\n \"Search Term\" \"Status\" \"Campaign\" \"Ad Group\" \"Impr\" \"Clicks\" \"Cost\" \"Conv\"\nprintf \"|%s|%s|%s|%s|%s|%s|%s|%s|\\n\" \\\n \"--------------------------------\" \"------------\" \"----------------------\" \"-----------------\" \"--------\" \"--------\" \"---------\" \"--------\"\n\n# Status mapping and table rows\necho \"$SEARCH_TERMS\" | jq -r '.[].results[]? | [\n .searchTermView.searchTerm,\n .searchTermView.status,\n .campaign.name,\n .adGroup.name,\n (.metrics.impressions // \"0\" | tostring),\n (.metrics.clicks // \"0\" | tostring),\n (.metrics.costMicros // \"0\" | tonumber / 1000000 | tostring),\n (.metrics.conversions // \"0\" | tostring)\n] | @tsv' 2>/dev/null | while IFS=

OPS ► MARKETING COMMAND CENTER Runtime Context Before executing, load available context: 1. Preferences : Read - — display all timestamps correctly - , , , , — check userConfig keys before env vars - , , , , , — Google Ads credentials 2. Daemon health : Read - If is not null → surface it before running any channel queries 3. Secrets : Resolve API keys via userConfig → env vars → Doppler MCP ( ) → Doppler CLI fallback (see Credential Resolution section below) CLI/API Reference Klaviyo REST API | Endpoint | Method | Description | |----------|--------|-------------| | | GET | All lists + subscri…

\\t' read -r term status campaign adgroup impr clicks cost_raw conv; do\n case \"$status\" in\n ADDED) STATUS_LABEL=\"✓ Added\" ;;\n EXCLUDED) STATUS_LABEL=\"✗ Excluded\" ;;\n *) STATUS_LABEL=\"○ New\" ;;\n esac\n COST=$(awk \"BEGIN { printf \\\"%.2f\\\", ${cost_raw:-0} }\")\n printf \"| %-30s | %-10s | %-20s | %-15s | %-6s | %-6s | \\$%-6s | %-6s |\\n\" \\\n \"${term:0:30}\" \"$STATUS_LABEL\" \"${campaign:0:20}\" \"${adgroup:0:15}\" \"$impr\" \"$clicks\" \"$COST\" \"$conv\"\ndone\n\necho \"\"\necho \"Negative keyword candidates (high spend, zero conversions):\"\n\necho \"$SEARCH_TERMS\" | jq -r '.[].results[]? | select(\n (.metrics.conversions // \"0\" | tonumber) == 0 and\n (.metrics.costMicros // \"0\" | tonumber) > 1000000\n) | \" • \\(.searchTermView.searchTerm) — $\\(.metrics.costMicros | tonumber / 1000000 | tostring | split(\".\") | .[0] + \".\" + (.[1] // \"00\")[0:2])\"' 2>/dev/null || echo \" (none found)\"","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Budget Recommendations (","type":"text"},{"text":"budget-recs","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"recommendations","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"recs","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"RECS=$(curl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/googleAds:searchStream\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary '{\n \"query\": \"SELECT recommendation.resource_name, recommendation.type, recommendation.campaign, recommendation.impact, recommendation.campaign_budget_recommendation FROM recommendation WHERE recommendation.type IN (CAMPAIGN_BUDGET, MOVE_UNUSED_BUDGET, MARGINAL_ROI_CAMPAIGN_BUDGET, FORECASTING_CAMPAIGN_BUDGET)\"\n }')\n\n# Check for API error\nGADS_REC_ERROR=$(echo \"$RECS\" | jq -r '.[0].error.message // empty' 2>/dev/null)\nif [ -n \"$GADS_REC_ERROR\" ]; then\n echo \"Google Ads API error: ${GADS_REC_ERROR}\"\n echo \"Check credentials with /ops:marketing setup.\"\n exit 0\nfi\n\nREC_COUNT=$(echo \"$RECS\" | jq '[.[].results[]?] | length' 2>/dev/null || echo \"0\")\nif [ \"$REC_COUNT\" -eq 0 ]; then\n echo \"No budget recommendations available. Google needs campaign data to generate recommendations.\"\n exit 0\nfi\n\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \" BUDGET RECOMMENDATIONS\"\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \"\"\n\nprintf \"| %-22s | %-25s | %-15s | %-13s | %-20s |\\n\" \\\n \"Type\" \"Campaign\" \"Current Budget\" \"Recommended\" \"Impact\"\nprintf \"|%s|%s|%s|%s|%s|\\n\" \\\n \"------------------------\" \"---------------------------\" \"-----------------\" \"---------------\" \"----------------------\"\n\necho \"$RECS\" | jq -r '.[].results[]? | [\n .recommendation.type,\n .recommendation.campaign,\n (.recommendation.campaignBudgetRecommendation.currentBudgetAmountMicros // \"0\" | tonumber / 1000000 | tostring),\n (.recommendation.campaignBudgetRecommendation.recommendedBudgetAmountMicros // \"0\" | tonumber / 1000000 | tostring),\n (.recommendation.impact.baseMetrics.impressions // \"0\" | tonumber | tostring),\n (.recommendation.impact.potentialMetrics.impressions // \"0\" | tonumber | tostring)\n] | @tsv' 2>/dev/null | while IFS=

OPS ► MARKETING COMMAND CENTER Runtime Context Before executing, load available context: 1. Preferences : Read - — display all timestamps correctly - , , , , — check userConfig keys before env vars - , , , , , — Google Ads credentials 2. Daemon health : Read - If is not null → surface it before running any channel queries 3. Secrets : Resolve API keys via userConfig → env vars → Doppler MCP ( ) → Doppler CLI fallback (see Credential Resolution section below) CLI/API Reference Klaviyo REST API | Endpoint | Method | Description | |----------|--------|-------------| | | GET | All lists + subscri…

\\t' read -r rec_type campaign current_raw recommended_raw base_impr_raw pot_impr_raw; do\n case \"$rec_type\" in\n CAMPAIGN_BUDGET) TYPE_LABEL=\"Increase Budget\" ;;\n MOVE_UNUSED_BUDGET) TYPE_LABEL=\"Move Unused Budget\" ;;\n MARGINAL_ROI_CAMPAIGN_BUDGET) TYPE_LABEL=\"Marginal ROI\" ;;\n FORECASTING_CAMPAIGN_BUDGET) TYPE_LABEL=\"Forecasting\" ;;\n *) TYPE_LABEL=\"$rec_type\" ;;\n esac\n CURRENT=$(awk \"BEGIN { printf \\\"%.2f\\\", ${current_raw:-0} }\")\n RECOMMENDED=$(awk \"BEGIN { printf \\\"%.2f\\\", ${recommended_raw:-0} }\")\n IMPACT=$(awk \"BEGIN {\n base = ${base_impr_raw:-0}; pot = ${pot_impr_raw:-0}\n if (base > 0) printf \\\"+%.0f%% impressions\\\", (pot - base) / base * 100\n else print \\\"—\\\"\n }\")\n printf \"| %-22s | %-25s | \\$%-14s | \\$%-12s | %-20s |\\n\" \\\n \"$TYPE_LABEL\" \"${campaign:0:25}\" \"$CURRENT\" \"$RECOMMENDED\" \"$IMPACT\"\ndone","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Campaign Management (","type":"text"},{"text":"campaigns","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"manage","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]},{"type":"paragraph","content":[{"text":"List campaigns:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"curl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/googleAds:searchStream\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary '{\n \"query\": \"SELECT campaign.id, campaign.name, campaign.status, campaign.advertising_channel_type, campaign_budget.amount_micros FROM campaign WHERE campaign.status != REMOVED ORDER BY campaign.name LIMIT 50\"\n }'","type":"text"}]},{"type":"paragraph","content":[{"text":"Output as table:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"| # | Campaign ID | Name | Status | Channel | Budget/day |","type":"text"}]},{"type":"paragraph","content":[{"text":"Create campaign","type":"text","marks":[{"type":"strong"}]},{"text":" (","type":"text"},{"text":"campaigns create","type":"text","marks":[{"type":"code_inline"}]},{"text":"):","type":"text"}]},{"type":"paragraph","content":[{"text":"Collect from user via AskUserQuestion (free text, one at a time):","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Campaign name","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Daily budget in dollars (convert to micros: ","type":"text"},{"text":"BUDGET_MICROS=$(awk \"BEGIN {printf \\\"%d\\\", $DOLLARS * 1000000}\")","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Channel type — AskUserQuestion with options: ","type":"text"},{"text":"[Search, Display, Shopping, Video]","type":"text","marks":[{"type":"code_inline"}]},{"text":" Map to API values: ","type":"text"},{"text":"SEARCH","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"DISPLAY","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"SHOPPING","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"VIDEO","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"paragraph","content":[{"text":"Two-step mutate:","type":"text"}]},{"type":"paragraph","content":[{"text":"Step 1 — Create budget:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"BUDGET_RESP=$(curl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/campaignBudgets:mutate\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary \"{\n \\\"operations\\\": [{\n \\\"create\\\": {\n \\\"name\\\": \\\"Budget for ${CAMPAIGN_NAME}\\\",\n \\\"deliveryMethod\\\": \\\"STANDARD\\\",\n \\\"amountMicros\\\": \\\"${BUDGET_MICROS}\\\"\n }\n }]\n }\")\nBUDGET_RESOURCE=$(echo \"$BUDGET_RESP\" | jq -r '.results[0].resourceName')","type":"text"}]},{"type":"paragraph","content":[{"text":"Step 2 — Create campaign (always in PAUSED status for safety):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"curl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/campaigns:mutate\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary \"{\n \\\"operations\\\": [{\n \\\"create\\\": {\n \\\"name\\\": \\\"${CAMPAIGN_NAME}\\\",\n \\\"campaignBudget\\\": \\\"${BUDGET_RESOURCE}\\\",\n \\\"advertisingChannelType\\\": \\\"${CHANNEL_TYPE}\\\",\n \\\"status\\\": \\\"PAUSED\\\",\n \\\"manualCpc\\\": {},\n \\\"networkSettings\\\": {\n \\\"targetGoogleSearch\\\": true,\n \\\"targetSearchNetwork\\\": true,\n \\\"targetContentNetwork\\\": false\n }\n }\n }]\n }\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Print: ","type":"text"},{"text":"✓ Campaign \"${CAMPAIGN_NAME}\" created (status: PAUSED, budget: $XX.XX/day). Enable it with: /ops:marketing google-ads campaigns enable \u003cID>","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"paragraph","content":[{"text":"If error, parse ","type":"text"},{"text":"error.message","type":"text","marks":[{"type":"code_inline"}]},{"text":" from response and display.","type":"text"}]},{"type":"paragraph","content":[{"text":"Pause campaign","type":"text","marks":[{"type":"strong"}]},{"text":" (","type":"text"},{"text":"campaigns pause \u003cID>","type":"text","marks":[{"type":"code_inline"}]},{"text":"):","type":"text"}]},{"type":"paragraph","content":[{"text":"⚠ ","type":"text"},{"text":"Rule 5 — confirm before pausing","type":"text","marks":[{"type":"strong"}]},{"text":" via AskUserQuestion: ","type":"text"},{"text":"\"Pause campaign \u003cNAME> (ID: \u003cID>)?\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" with options ","type":"text"},{"text":"[Pause, Cancel]","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"curl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/campaigns:mutate\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary \"{\n \\\"operations\\\": [{\n \\\"update\\\": {\n \\\"resourceName\\\": \\\"customers/${GADS_CUSTOMER_ID}/campaigns/${CAMPAIGN_ID}\\\",\n \\\"status\\\": \\\"PAUSED\\\"\n },\n \\\"updateMask\\\": \\\"status\\\"\n }]\n }\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Print: ","type":"text"},{"text":"✓ Campaign \u003cNAME> paused.","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"paragraph","content":[{"text":"Enable campaign","type":"text","marks":[{"type":"strong"}]},{"text":" (","type":"text"},{"text":"campaigns enable \u003cID>","type":"text","marks":[{"type":"code_inline"}]},{"text":"):","type":"text"}]},{"type":"paragraph","content":[{"text":"Same as pause but ","type":"text"},{"text":"\"status\": \"ENABLED\"","type":"text","marks":[{"type":"code_inline"}]},{"text":". No confirmation needed (enabling is not destructive).","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"curl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/campaigns:mutate\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary \"{\n \\\"operations\\\": [{\n \\\"update\\\": {\n \\\"resourceName\\\": \\\"customers/${GADS_CUSTOMER_ID}/campaigns/${CAMPAIGN_ID}\\\",\n \\\"status\\\": \\\"ENABLED\\\"\n },\n \\\"updateMask\\\": \\\"status\\\"\n }]\n }\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Print: ","type":"text"},{"text":"✓ Campaign \u003cNAME> enabled.","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"paragraph","content":[{"text":"Adjust budget","type":"text","marks":[{"type":"strong"}]},{"text":" (","type":"text"},{"text":"campaigns budget \u003cID> \u003cAMOUNT>","type":"text","marks":[{"type":"code_inline"}]},{"text":"):","type":"text"}]},{"type":"paragraph","content":[{"text":"First, fetch the campaign's current budget resource name:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"CAMPAIGN_DATA=$(curl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/googleAds:searchStream\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary \"{\\\"query\\\": \\\"SELECT campaign.campaign_budget, campaign_budget.amount_micros FROM campaign WHERE campaign.id = ${CAMPAIGN_ID}\\\"}\")\nBUDGET_RESOURCE=$(echo \"$CAMPAIGN_DATA\" | jq -r '.[0].results[0].campaign.campaignBudget // empty')\nCURRENT_BUDGET_MICROS=$(echo \"$CAMPAIGN_DATA\" | jq -r '.[0].results[0].campaignBudget.amountMicros // \"0\"')\nCURRENT_BUDGET=$(awk \"BEGIN { printf \\\"%.2f\\\", ${CURRENT_BUDGET_MICROS:-0} / 1000000 }\")","type":"text"}]},{"type":"paragraph","content":[{"text":"Confirm via AskUserQuestion: ","type":"text"},{"text":"\"Change budget from $\u003cCURRENT> to $\u003cNEW>/day?\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" with options ","type":"text"},{"text":"[Confirm, Cancel]","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"Then update:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"NEW_BUDGET_MICROS=$(awk \"BEGIN {printf \\\"%d\\\", ${NEW_AMOUNT} * 1000000}\")\ncurl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/campaignBudgets:mutate\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary \"{\n \\\"operations\\\": [{\n \\\"update\\\": {\n \\\"resourceName\\\": \\\"${BUDGET_RESOURCE}\\\",\n \\\"amountMicros\\\": \\\"${NEW_BUDGET_MICROS}\\\"\n },\n \\\"updateMask\\\": \\\"amountMicros\\\"\n }]\n }\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Print: ","type":"text"},{"text":"✓ Budget updated: $\u003cOLD>/day → $\u003cNEW>/day","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Keyword Planner (","type":"text"},{"text":"keywords","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"kw","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"keyword-planner","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Collect seed keywords from user via AskUserQuestion (free text)\n# \"Enter seed keywords (comma-separated):\"\n# Split into JSON array for the request: KEYWORDS_JSON_ARRAY=$(echo \"$SEED_KEYWORDS\" | sed 's/,/\",\"/g' | sed 's/^/\"/;s/$/\"/')\n\ncurl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}:generateKeywordIdeas\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary \"{\n \\\"language\\\": \\\"languageConstants/1000\\\",\n \\\"geoTargetConstants\\\": [\\\"geoTargetConstants/2840\\\"],\n \\\"includeAdultKeywords\\\": false,\n \\\"keywordPlanNetwork\\\": \\\"GOOGLE_SEARCH\\\",\n \\\"keywordSeed\\\": {\n \\\"keywords\\\": [${KEYWORDS_JSON_ARRAY}]\n }\n }\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Language ","type":"text"},{"text":"1000","type":"text","marks":[{"type":"code_inline"}]},{"text":" = English, Geo ","type":"text"},{"text":"2840","type":"text","marks":[{"type":"code_inline"}]},{"text":" = United States. These are defaults — if user has locale configured in preferences, use those instead. Other common values: UK=","type":"text"},{"text":"2826","type":"text","marks":[{"type":"code_inline"}]},{"text":", Canada=","type":"text"},{"text":"2124","type":"text","marks":[{"type":"code_inline"}]},{"text":", Australia=","type":"text"},{"text":"2036","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"Output format:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n KEYWORD IDEAS\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nSeeds: keyword1, keyword2\n\n| Keyword | Avg Monthly Searches | Competition | Low Bid | High Bid |\n|---------|---------------------|-------------|---------|----------|","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"avgMonthlySearches","type":"text","marks":[{"type":"code_inline"}]},{"text":" displayed as-is (integer)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"competition","type":"text","marks":[{"type":"code_inline"}]},{"text":" displayed as-is (","type":"text"},{"text":"LOW","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"MEDIUM","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"HIGH","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"lowTopOfPageBidMicros","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"highTopOfPageBidMicros","type":"text","marks":[{"type":"code_inline"}]},{"text":" divided by 1,000,000 and displayed as ","type":"text"},{"text":"$X.XX","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Sort by ","type":"text"},{"text":"avgMonthlySearches","type":"text","marks":[{"type":"code_inline"}]},{"text":" descending in the output","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"If no results, print: ","type":"text"},{"text":"No keyword ideas found for these seeds. Try different or broader keywords.","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Ad Group Management (","type":"text"},{"text":"ad-groups","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"ag","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]},{"type":"paragraph","content":[{"text":"List ad groups for a campaign","type":"text","marks":[{"type":"strong"}]},{"text":" (","type":"text"},{"text":"ad-groups list \u003cCAMPAIGN_ID>","type":"text","marks":[{"type":"code_inline"}]},{"text":"):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"curl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/googleAds:searchStream\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary \"{\\\"query\\\": \\\"SELECT ad_group.id, ad_group.name, ad_group.status, ad_group.cpc_bid_micros FROM ad_group WHERE campaign.id = ${CAMPAIGN_ID} AND ad_group.status != REMOVED ORDER BY ad_group.name\\\"}\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Output:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"| # | Ad Group ID | Name | Status | CPC Bid |","type":"text"}]},{"type":"paragraph","content":[{"text":"Create ad group","type":"text","marks":[{"type":"strong"}]},{"text":" (","type":"text"},{"text":"ad-groups create \u003cCAMPAIGN_ID>","type":"text","marks":[{"type":"code_inline"}]},{"text":"):","type":"text"}]},{"type":"paragraph","content":[{"text":"Collect via AskUserQuestion:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Ad group name (free text)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Default CPC bid in dollars (free text, convert to micros)","type":"text"}]}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"BID_MICROS=$(awk \"BEGIN {printf \\\"%d\\\", ${BID_DOLLARS} * 1000000}\")\ncurl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/adGroups:mutate\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary \"{\n \\\"operations\\\": [{\n \\\"create\\\": {\n \\\"name\\\": \\\"${AD_GROUP_NAME}\\\",\n \\\"campaign\\\": \\\"customers/${GADS_CUSTOMER_ID}/campaigns/${CAMPAIGN_ID}\\\",\n \\\"status\\\": \\\"ENABLED\\\",\n \\\"type\\\": \\\"SEARCH_STANDARD\\\",\n \\\"cpcBidMicros\\\": \\\"${BID_MICROS}\\\"\n }\n }]\n }\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Print: ","type":"text"},{"text":"✓ Ad group \"${AD_GROUP_NAME}\" created in campaign ${CAMPAIGN_ID} (CPC bid: $X.XX)","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"paragraph","content":[{"text":"List keywords in ad group","type":"text","marks":[{"type":"strong"}]},{"text":" (","type":"text"},{"text":"ad-groups keywords \u003cAD_GROUP_ID>","type":"text","marks":[{"type":"code_inline"}]},{"text":"):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"curl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/googleAds:searchStream\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary \"{\\\"query\\\": \\\"SELECT ad_group_criterion.criterion_id, ad_group_criterion.keyword.text, ad_group_criterion.keyword.match_type, ad_group_criterion.cpc_bid_micros, ad_group_criterion.status FROM ad_group_criterion WHERE ad_group.id = ${AD_GROUP_ID} AND ad_group_criterion.type = KEYWORD AND ad_group_criterion.status != REMOVED\\\"}\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Output:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"| # | Keyword | Match Type | CPC Bid | Status |","type":"text"}]},{"type":"paragraph","content":[{"text":"Add keyword to ad group","type":"text","marks":[{"type":"strong"}]},{"text":" (","type":"text"},{"text":"ad-groups add-keyword \u003cAD_GROUP_ID>","type":"text","marks":[{"type":"code_inline"}]},{"text":"):","type":"text"}]},{"type":"paragraph","content":[{"text":"Collect via AskUserQuestion:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Keyword text (free text)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Match type — AskUserQuestion options: ","type":"text"},{"text":"[Broad, Phrase, Exact]","type":"text","marks":[{"type":"code_inline"}]},{"text":" Map: ","type":"text"},{"text":"BROAD","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"PHRASE","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"EXACT","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"CPC bid in dollars (free text, convert to micros) — optional, uses ad group default if not provided","type":"text"}]}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"curl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/adGroupCriteria:mutate\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary \"{\n \\\"operations\\\": [{\n \\\"create\\\": {\n \\\"adGroup\\\": \\\"customers/${GADS_CUSTOMER_ID}/adGroups/${AD_GROUP_ID}\\\",\n \\\"status\\\": \\\"ENABLED\\\",\n \\\"keyword\\\": {\n \\\"text\\\": \\\"${KEYWORD_TEXT}\\\",\n \\\"matchType\\\": \\\"${MATCH_TYPE}\\\"\n }\n ${BID_MICROS:+,\\\"cpcBidMicros\\\": \\\"${BID_MICROS}\\\"}\n }\n }]\n }\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Print: ","type":"text"},{"text":"✓ Keyword \"${KEYWORD_TEXT}\" (${MATCH_TYPE}) added to ad group ${AD_GROUP_ID}","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"paragraph","content":[{"text":"Note: Keywords are immutable after creation. To change match type or text, remove and recreate. To change bid only, use ","type":"text"},{"text":"ad-groups update-bid","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"Remove keyword","type":"text","marks":[{"type":"strong"}]},{"text":" (","type":"text"},{"text":"ad-groups remove-keyword \u003cAD_GROUP_ID> \u003cCRITERION_ID>","type":"text","marks":[{"type":"code_inline"}]},{"text":"):","type":"text"}]},{"type":"paragraph","content":[{"text":"⚠ ","type":"text"},{"text":"Rule 5 — confirm before removing","type":"text","marks":[{"type":"strong"}]},{"text":" via AskUserQuestion: ","type":"text"},{"text":"\"Remove keyword \u003cTEXT> from ad group \u003cNAME>?\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" with options ","type":"text"},{"text":"[Remove, Cancel]","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"curl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/adGroupCriteria:mutate\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary \"{\n \\\"operations\\\": [{\n \\\"remove\\\": \\\"customers/${GADS_CUSTOMER_ID}/adGroupCriteria/${AD_GROUP_ID}~${CRITERION_ID}\\\"\n }]\n }\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Print: ","type":"text"},{"text":"✓ Keyword removed from ad group.","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"paragraph","content":[{"text":"Update keyword bid","type":"text","marks":[{"type":"strong"}]},{"text":" (","type":"text"},{"text":"ad-groups update-bid \u003cAD_GROUP_ID> \u003cCRITERION_ID> \u003cBID>","type":"text","marks":[{"type":"code_inline"}]},{"text":"):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"NEW_BID_MICROS=$(awk \"BEGIN {printf \\\"%d\\\", ${NEW_BID} * 1000000}\")\ncurl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/adGroupCriteria:mutate\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary \"{\n \\\"operations\\\": [{\n \\\"update\\\": {\n \\\"resourceName\\\": \\\"customers/${GADS_CUSTOMER_ID}/adGroupCriteria/${AD_GROUP_ID}~${CRITERION_ID}\\\",\n \\\"cpcBidMicros\\\": \\\"${NEW_BID_MICROS}\\\"\n },\n \\\"updateMask\\\": \\\"cpcBidMicros\\\"\n }]\n }\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Print: ","type":"text"},{"text":"✓ Keyword bid updated to $X.XX","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"campaigns","type":"text"}]},{"type":"paragraph","content":[{"text":"Cross-channel campaign overview — unified view of active campaigns across all configured channels (Klaviyo, Meta Ads, Google Ads).","type":"text"}]},{"type":"paragraph","content":[{"text":"Run in parallel:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Klaviyo active campaigns (status: draft + scheduled + sending)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Meta Ads active campaigns (status ACTIVE) — reuse ","type":"text"},{"text":"META_TOKEN","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"META_ACCOUNT","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Google Ads active campaigns (status ENABLED) — reuse ","type":"text"},{"text":"GADS_*","type":"text","marks":[{"type":"code_inline"}]},{"text":" credentials if configured","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Google Ads: refresh access token first (see Credential Resolution)","type":"text"}]}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Meta Ads campaigns\nMETA_CAMPAIGNS=$(curl -s \"https://graph.facebook.com/v20.0/${META_ACCOUNT}/campaigns?fields=name,status,daily_budget,lifetime_budget,objective&filtering=[{\\\"field\\\":\\\"effective_status\\\",\\\"operator\\\":\\\"IN\\\",\\\"value\\\":[\\\"ACTIVE\\\"]}]\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" 2>/dev/null)\n\n# Klaviyo campaigns\nKLAVIYO_CAMPAIGNS=$(curl -s \"https://a.klaviyo.com/api/campaigns/?filter=equals(messages.channel,'email')&sort=-created_at&page[size]=10\" \\\n -H \"Authorization: Klaviyo-API-Key ${KLAVIYO_KEY}\" \\\n -H \"revision: 2024-10-15\" 2>/dev/null)\n\n# Google Ads campaigns (if configured)\nGADS_CAMPAIGNS=\"\"\nif [ -n \"$GADS_ACCESS_TOKEN\" ] && [ \"$GADS_ACCESS_TOKEN\" != \"null\" ]; then\n GADS_CAMPAIGNS=$(curl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/googleAds:searchStream\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary '{\"query\": \"SELECT campaign.id, campaign.name, campaign.status, campaign_budget.amount_micros, metrics.cost_micros FROM campaign WHERE campaign.status = ENABLED ORDER BY metrics.cost_micros DESC LIMIT 10\"}' 2>/dev/null)\nfi","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Output format","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n CROSS-CHANNEL CAMPAIGNS — active\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n EMAIL (Klaviyo)\n [campaign name] [status] [send date or \"scheduled for X\"]\n\n PAID — META ADS\n [campaign name] [status] $[daily_budget]/day [objective]\n\n PAID — GOOGLE ADS\n [campaign name] [status] $[budget]/day $[spend 7d]\n\n FLOWS (Always-on automation)\n [flow name] [trigger type] [status: live/draft]","type":"text"}]},{"type":"paragraph","content":[{"text":"For any channel not configured, show ","type":"text"},{"text":"[not configured — /ops:marketing setup]","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"optimize","type":"text"}]},{"type":"paragraph","content":[{"text":"Cross-platform ad optimization agent. Reads Meta + Google Ads data, computes blended ROAS, and recommends where to shift budget.","type":"text"}]},{"type":"paragraph","content":[{"text":"Spawn the marketing optimizer agent:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"Agent(prompt=\"Run the marketing optimizer agent. Read ops-marketing-dash data for Meta Ads and Google Ads spend/conversions/ROAS. Compute blended ROAS across platforms. Identify the highest-ROAS platform. Recommend budget shifts with specific dollar amounts. List top 3 actions by expected impact. Use the marketing-optimizer.md agent instructions.\", model=\"claude-sonnet-4-5\")","type":"text"}]},{"type":"paragraph","content":[{"text":"If Agent Teams are available (","type":"text"},{"text":"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1","type":"text","marks":[{"type":"code_inline"}]},{"text":"):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"TeamCreate(\"optimizer\")\nAgent(team_name=\"optimizer\", name=\"marketing-optimizer\", prompt=\"You are the marketing optimizer. Read ops-marketing-dash pre-gathered data. Compute blended ROAS for Meta + Google. Recommend budget reallocation. Show unified attribution table.\")","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Output format","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n AD OPTIMIZATION REPORT\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n Blended ROAS: [X]x (Meta: [X]x Google: [X]x)\n Total Spend: $[X] Total Revenue: $[X]\n\n RECOMMENDATIONS\n 1. [Action] Expected impact: [+X% ROAS / +$X revenue]\n 2. [Action] ...\n 3. [Action] ...\n\n BUDGET REALLOCATION\n Move $[X]/day from [Platform A] → [Platform B]\n Rationale: [X]x ROAS vs [X]x ROAS","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"attribution","type":"text"}]},{"type":"paragraph","content":[{"text":"Unified attribution table showing spend, conversions, revenue, and ROAS side-by-side across all configured platforms.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Gather all platform data in parallel\n# Meta Ads — last 7d\nMETA_DATA=$(curl -s \"https://graph.facebook.com/v20.0/${META_ACCOUNT}/insights?fields=spend,actions,action_values&date_preset=last_7d&level=account\" \\\n -H \"Authorization: Bearer ${META_TOKEN}\" 2>/dev/null)\nMETA_SPEND=$(echo \"$META_DATA\" | jq -r '.data[0].spend // \"0\"')\nMETA_CONV=$(echo \"$META_DATA\" | jq '[.data[0].actions[]? | select(.action_type == \"purchase\") | .value | tonumber] | add // 0' 2>/dev/null)\nMETA_REV=$(echo \"$META_DATA\" | jq '[.data[0].action_values[]? | select(.action_type == \"purchase\") | .value | tonumber] | add // 0' 2>/dev/null)\nMETA_ROAS=$(awk \"BEGIN { if (${META_SPEND:-0} > 0) printf \\\"%.2f\\\", ${META_REV:-0} / ${META_SPEND:-1}; else print \\\"—\\\" }\")\n\n# Google Ads — last 7d\nGADS_DATA=\"\"\nGADS_SPEND=\"0\"; GADS_CONV=\"0\"; GADS_REV=\"0\"; GADS_ROAS=\"—\"\nif [ -n \"$GADS_ACCESS_TOKEN\" ] && [ \"$GADS_ACCESS_TOKEN\" != \"null\" ]; then\n GADS_DATA=$(curl -s -X POST \\\n \"https://googleads.googleapis.com/${GADS_API_VERSION}/customers/${GADS_CUSTOMER_ID}/googleAds:searchStream\" \\\n \"${GADS_HEADERS[@]}\" \\\n --data-binary '{\"query\": \"SELECT metrics.cost_micros, metrics.conversions, metrics.conversions_value FROM customer WHERE segments.date DURING LAST_7_DAYS\"}' 2>/dev/null)\n GADS_SPEND=$(echo \"$GADS_DATA\" | jq '[.[].results[]?.metrics.costMicros // \"0\" | tonumber] | add / 1000000 // 0' 2>/dev/null || echo \"0\")\n GADS_CONV=$(echo \"$GADS_DATA\" | jq '[.[].results[]?.metrics.conversions // \"0\" | tonumber] | add // 0' 2>/dev/null || echo \"0\")\n GADS_REV=$(echo \"$GADS_DATA\" | jq '[.[].results[]?.metrics.conversionsValue // \"0\" | tonumber] | add // 0' 2>/dev/null || echo \"0\")\n GADS_ROAS=$(awk \"BEGIN { if (${GADS_SPEND:-0} > 0) printf \\\"%.2f\\\", ${GADS_REV:-0} / ${GADS_SPEND:-1}; else print \\\"—\\\" }\")\nfi\n\n# Klaviyo attributed revenue\nKLAVIYO_REV=\"—\"\n# (Pull from metric aggregates if needed — complex, show as note)\n\n# GA4 conversions + revenue\nGA4_DATA=$(curl -s -X POST \"https://analyticsdata.googleapis.com/v1beta/properties/${GA4_PROPERTY}:runReport\" \\\n -H \"Authorization: Bearer ${GA4_TOKEN}\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"dateRanges\": [{\"startDate\": \"7daysAgo\", \"endDate\": \"today\"}], \"metrics\": [{\"name\": \"conversions\"}, {\"name\": \"totalRevenue\"}]}' 2>/dev/null)\nGA4_CONV=$(echo \"$GA4_DATA\" | jq -r '.rows[0].metricValues[0].value // \"—\"')\nGA4_REV=$(echo \"$GA4_DATA\" | jq -r '.rows[0].metricValues[1].value // \"—\"')\n\nTOTAL_AD_SPEND=$(awk \"BEGIN { printf \\\"%.2f\\\", ${META_SPEND:-0} + ${GADS_SPEND:-0} }\")\nTOTAL_AD_REV=$(awk \"BEGIN { printf \\\"%.2f\\\", ${META_REV:-0} + ${GADS_REV:-0} }\")\nBLENDED_ROAS=$(awk \"BEGIN { if (${TOTAL_AD_SPEND:-0} > 0) printf \\\"%.2f\\\", ${TOTAL_AD_REV:-0} / ${TOTAL_AD_SPEND:-1}; else print \\\"—\\\" }\")\n\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\necho \" UNIFIED ATTRIBUTION — Last 7 Days\"\necho \"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\"\nprintf \"| %-14s | %-10s | %-12s | %-12s | %-8s |\\n\" \"Platform\" \"Spend\" \"Conversions\" \"Revenue\" \"ROAS\"\nprintf \"|%s|%s|%s|%s|%s|\\n\" \"----------------\" \"------------\" \"--------------\" \"--------------\" \"----------\"\nprintf \"| %-14s | \\$%-9s | %-12s | \\$%-11s | %-8s |\\n\" \"Meta Ads\" \"$META_SPEND\" \"$META_CONV\" \"$META_REV\" \"${META_ROAS}x\"\nprintf \"| %-14s | \\$%-9s | %-12s | \\$%-11s | %-8s |\\n\" \"Google Ads\" \"$(printf \"%.2f\" ${GADS_SPEND})\" \"$GADS_CONV\" \"$(printf \"%.2f\" ${GADS_REV})\" \"${GADS_ROAS}x\"\nprintf \"| %-14s | %-10s | %-12s | %-12s | %-8s |\\n\" \"Klaviyo\" \"—\" \"—\" \"${KLAVIYO_REV}\" \"—\"\nprintf \"| %-14s | %-10s | %-12s | %-12s | %-8s |\\n\" \"GA4 (organic)\" \"—\" \"$GA4_CONV\" \"\\${GA4_REV}\" \"—\"\nprintf \"|%s|%s|%s|%s|%s|\\n\" \"----------------\" \"------------\" \"--------------\" \"--------------\" \"----------\"\nprintf \"| %-14s | \\$%-9s | %-12s | \\$%-11s | %-8s |\\n\" \"TOTAL (ads)\" \"$TOTAL_AD_SPEND\" \"—\" \"$TOTAL_AD_REV\" \"${BLENDED_ROAS}x\"","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"dashboard (default — no args)","type":"text"}]},{"type":"paragraph","content":[{"text":"Run ALL sections in parallel, then render unified dashboard.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Run the pre-gathered data script\n\"${CLAUDE_PLUGIN_ROOT}/bin/ops-marketing-dash\" 2>/dev/null","type":"text"}]},{"type":"paragraph","content":[{"text":"Parse the JSON output and display:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n MARKETING DASHBOARD — [date]\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n Email (Klaviyo) [N] subs | [X]% open rate | $[X] attributed\n Paid (Meta) $[X] spent | [X]x ROAS | [N] purchases\n Paid (Google) $[X] spent | [X]x ROAS | [N] conversions\n Organic (GA4) [N] sessions | [X]% CVR | $[X] revenue\n SEO (GSC) [N] clicks | [N] impressions | [X] avg pos\n Instagram [N] followers | [N] reach (7d)\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n Marketing Health Score: [N]/100 ([status: Healthy/Warning/Critical])\n Blended ROAS: [X]x Top channel: [channel]\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━","type":"text"}]},{"type":"paragraph","content":[{"text":"Marketing Health Score computation","type":"text","marks":[{"type":"strong"}]},{"text":" (0-100, shown at bottom of dashboard):","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Blended ROAS ≥ 3x: +30 pts; 1-3x: +15 pts; \u003c 1x: +0 pts","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Email open rate ≥ 20%: +20 pts; 10-20%: +10 pts; \u003c 10%: +0 pts","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Channel diversity (3+ platforms active): +20 pts; 2 platforms: +10 pts; 1: +0 pts","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"GA4 CVR ≥ 2%: +20 pts; 1-2%: +10 pts; \u003c 1%: +0 pts","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Organic SEO clicks > 1,000/mo: +10 pts; 100-1000: +5 pts; \u003c 100: +0 pts","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Status thresholds: ≥70 = Healthy, 40-69 = Warning, \u003c 40 = Critical.","type":"text"}]},{"type":"paragraph","content":[{"text":"For any channel with missing credentials, show ","type":"text"},{"text":"[not configured — /ops:marketing setup]","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"setup","type":"text"}]},{"type":"paragraph","content":[{"text":"Before asking for anything","type":"text","marks":[{"type":"strong"}]},{"text":", auto-scan ALL sources for existing credentials. Run in a single background batch:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Env vars\nprintenv KLAVIYO_API_KEY KLAVIYO_PRIVATE_KEY META_ACCESS_TOKEN FACEBOOK_ACCESS_TOKEN META_AD_ACCOUNT_ID GA4_PROPERTY_ID GA_MEASUREMENT_ID 2>/dev/null\nprintenv GOOGLE_ADS_DEVELOPER_TOKEN GOOGLE_ADS_CLIENT_ID GOOGLE_ADS_CLIENT_SECRET GOOGLE_ADS_REFRESH_TOKEN GOOGLE_ADS_CUSTOMER_ID 2>/dev/null\n\n# Shell profiles\ngrep -h 'KLAVIYO\\|META_\\|FACEBOOK\\|GA4\\|GA_MEASUREMENT\\|GOOGLE_ADS' ~/.zshrc ~/.bashrc ~/.zprofile ~/.envrc 2>/dev/null | grep -v '^#'\n\n# Doppler — ALL projects, ALL configs\nfor proj in $(doppler projects --json 2>/dev/null | jq -r '.[].slug'); do\n for cfg in dev stg prd; do\n doppler secrets --project \"$proj\" --config \"$cfg\" --json 2>/dev/null | \\\n jq -r --arg proj \"$proj\" --arg cfg \"$cfg\" 'to_entries[] | select(.key | test(\"KLAVIYO|META|FACEBOOK|GA4|GOOGLE|GOOGLE_ADS\"; \"i\")) | \"\\(.key)=\\(.value.computed) (doppler:\\($proj)/\\($cfg))\"'\n done\ndone\n\n# Dashlane — check for tokens in password entries\ndcli password klaviyo --output json 2>/dev/null | jq -r '.[] | select(.password != null and .password != \"\") | \"\\(.title): token found\"'\ndcli password facebook --output json 2>/dev/null | jq -r '.[] | select(.password != null and .password != \"\") | \"\\(.title): token found\"'\ndcli password meta --output json 2>/dev/null | jq -r '.[] | select(.password != null and .password != \"\") | \"\\(.title): token found\"'\ndcli password \"google ads\" --output json 2>/dev/null | jq -r '.[] | select(.password != null and .password != \"\") | \"\\(.title): token found\"'\n\n# Keychain\nsecurity find-generic-password -s \"klaviyo-api-key\" -w 2>/dev/null\nsecurity find-generic-password -s \"meta-ads-token\" -w 2>/dev/null\nsecurity find-generic-password -s \"google-ads-refresh-token\" -w 2>/dev/null\n\n# gcloud ADC (for GA4 + Search Console)\ngcloud auth application-default print-access-token 2>/dev/null | head -c 10 && echo \"...gcloud-ok\"\n\n# Chrome history — reveals account identity\nsqlite3 ~/Library/Application\\ Support/Google/Chrome/Default/History \\\n \"SELECT DISTINCT url FROM urls WHERE url LIKE '%klaviyo.com%' OR url LIKE '%analytics.google.com%' OR url LIKE '%business.facebook.com%' OR url LIKE '%search.google.com/search-console%' OR url LIKE '%ads.google.com%' ORDER BY last_visit_time DESC LIMIT 15\" 2>/dev/null\n\n# Existing prefs + userConfig\njq -r '.marketing // empty' \"$PREFS_PATH\" 2>/dev/null","type":"text"}]},{"type":"paragraph","content":[{"text":"Present ALL findings before asking for anything. Only prompt for values NOT found in any source. Run all smoke tests with ","type":"text"},{"text":"run_in_background: true","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"Klaviyo:","type":"text","marks":[{"type":"strong"}]},{"text":" If ","type":"text"},{"text":"KLAVIYO_PRIVATE_KEY","type":"text","marks":[{"type":"code_inline"}]},{"text":" or Dashlane entry with ","type":"text"},{"text":"ck_*","type":"text","marks":[{"type":"code_inline"}]},{"text":" key found, use it directly. Note: Klaviyo private keys start with ","type":"text"},{"text":"ck_","type":"text","marks":[{"type":"code_inline"}]},{"text":" (older) or ","type":"text"},{"text":"pk_","type":"text","marks":[{"type":"code_inline"}]},{"text":" (newer). Smoke test: ","type":"text"},{"text":"curl -s -H \"Authorization: Klaviyo-API-Key $KEY\" -H \"revision: 2024-10-15\" \"https://a.klaviyo.com/api/lists?page[size]=1\"","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"Meta Ads:","type":"text","marks":[{"type":"strong"}]},{"text":" If found in Doppler, use directly. Need both ","type":"text"},{"text":"META_ACCESS_TOKEN","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"META_AD_ACCOUNT_ID","type":"text","marks":[{"type":"code_inline"}]},{"text":". Smoke test: ","type":"text"},{"text":"graph.facebook.com/v20.0/$AD_ACCOUNT_ID/campaigns?limit=1","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"GA4:","type":"text","marks":[{"type":"strong"}]},{"text":" Only needs Property ID + gcloud ADC. If gcloud ADC not set up, run ","type":"text"},{"text":"gcloud auth application-default login","type":"text","marks":[{"type":"code_inline"}]},{"text":" in background (opens browser). Check Chrome history for GA4 property URLs to auto-detect the property ID.","type":"text"}]},{"type":"paragraph","content":[{"text":"Search Console:","type":"text","marks":[{"type":"strong"}]},{"text":" Only needs site URL + gcloud ADC. Check Chrome history for ","type":"text"},{"text":"search.google.com/search-console","type":"text","marks":[{"type":"code_inline"}]},{"text":" URLs to auto-detect the site.","type":"text"}]},{"type":"paragraph","content":[{"text":"Google Ads:","type":"text","marks":[{"type":"strong"}]},{"text":" More complex than other marketing credentials — requires 3 pieces: (1) developer token from Google Ads MCC → Tools & Settings → API Center, (2) OAuth2 client ID + secret from Google Cloud Console (Desktop app type, Google Ads API enabled), (3) refresh token via browser OAuth flow. Setup flow: collect developer token and client credentials first, then open browser auth URL, user pastes authorization code, exchange for refresh token via curl, then list accessible customer accounts and let user select. Smoke test: ","type":"text"},{"text":"curl -s -X GET \"https://googleads.googleapis.com/v23/customers:listAccessibleCustomers\" -H \"Authorization: Bearer $TOKEN\" -H \"developer-token: $DEV_TOKEN\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" — expect JSON with ","type":"text"},{"text":"resourceNames","type":"text","marks":[{"type":"code_inline"}]},{"text":" array.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Save via userConfig (preferred) or Doppler. Report: ","type":"text"},{"text":"[service] ✓ connected","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"[service] ✗ invalid key — [error]","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},"metadata":{"date":"2026-06-05","name":"ops-marketing","author":"@skillopedia","effort":"medium","source":{"stars":2998,"repo_name":"buildwithclaude","origin_url":"https://github.com/davepoon/buildwithclaude/blob/HEAD/plugins/claude-ops/skills/ops-marketing/SKILL.md","repo_owner":"davepoon","body_sha256":"36e6e8691851ceb44685fccb6f8f0b8f0cae886f076eefe93c602ee8f2ab8c72","cluster_key":"25ad5c1d4d39921d6a0a45405662d8f454992d2515be98ffe658c76c338f4706","clean_bundle":{"format":"clean-skill-bundle-v1","source":"davepoon/buildwithclaude/plugins/claude-ops/skills/ops-marketing/SKILL.md","bundle_sha256":"ced085453b98be5cb8a078729375d79c9d3b300db8dbd3685cf15cb0a216ef85","attachment_count":0,"text_attachments":0,"binary_attachments":0},"cluster_size":1,"skill_md_path":"plugins/claude-ops/skills/ops-marketing/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"data-analytics","category_label":"Data"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"data-analytics","maxTurns":40,"import_tag":"clean-skills-v1","description":"Marketing command center. Email campaigns (Klaviyo), paid ads (Meta/Google), analytics (GA4), SEO, and social media metrics. One dashboard for all marketing channels.","allowed-tools":["Bash","Read","Write","Grep","Glob","Agent","TeamCreate","SendMessage","AskUserQuestion","WebFetch","WebSearch"],"argument-hint":"[email|ads|analytics|seo|social|campaigns|setup]"}},"renderedAt":1782988438592}

OPS ► MARKETING COMMAND CENTER Runtime Context Before executing, load available context: 1. Preferences : Read - — display all timestamps correctly - , , , , — check userConfig keys before env vars - , , , , , — Google Ads credentials 2. Daemon health : Read - If is not null → surface it before running any channel queries 3. Secrets : Resolve API keys via userConfig → env vars → Doppler MCP ( ) → Doppler CLI fallback (see Credential Resolution section below) CLI/API Reference Klaviyo REST API | Endpoint | Method | Description | |----------|--------|-------------| | | GET | All lists + subscri…