{
  "schemaVersion": "1.0",
  "item": {
    "slug": "add-analytics",
    "name": "Add Analytics",
    "source": "tencent",
    "type": "skill",
    "category": "数据分析",
    "sourceUrl": "https://clawhub.ai/jeftekhari/add-analytics",
    "canonicalUrl": "https://clawhub.ai/jeftekhari/add-analytics",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadMode": "redirect",
    "downloadUrl": "/downloads/add-analytics",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=add-analytics",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "installMethod": "Manual import",
    "extraction": "Extract archive",
    "prerequisites": [
      "OpenClaw"
    ],
    "packageFormat": "ZIP package",
    "includedAssets": [
      "SKILL.md"
    ],
    "primaryDoc": "SKILL.md",
    "quickSetup": [
      "Download the package from Yavira.",
      "Extract the archive and review SKILL.md first.",
      "Import or place the package into your OpenClaw setup."
    ],
    "agentAssist": {
      "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
      "steps": [
        "Download the package from Yavira.",
        "Extract it into a folder your agent can access.",
        "Paste one of the prompts below and point your agent at the extracted folder."
      ],
      "prompts": [
        {
          "label": "New install",
          "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
        },
        {
          "label": "Upgrade existing",
          "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
        }
      ]
    },
    "sourceHealth": {
      "source": "tencent",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-04-23T16:43:11.935Z",
      "expiresAt": "2026-04-30T16:43:11.935Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=4claw-imageboard",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=4claw-imageboard",
        "contentDisposition": "attachment; filename=\"4claw-imageboard-1.0.1.zip\"",
        "redirectLocation": null,
        "bodySnippet": null
      },
      "scope": "source",
      "summary": "Source download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this source.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/add-analytics"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    },
    "downloadPageUrl": "https://openagent3.xyz/downloads/add-analytics",
    "agentPageUrl": "https://openagent3.xyz/skills/add-analytics/agent",
    "manifestUrl": "https://openagent3.xyz/skills/add-analytics/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/add-analytics/agent.md"
  },
  "agentAssist": {
    "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
    "steps": [
      "Download the package from Yavira.",
      "Extract it into a folder your agent can access.",
      "Paste one of the prompts below and point your agent at the extracted folder."
    ],
    "prompts": [
      {
        "label": "New install",
        "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
      },
      {
        "label": "Upgrade existing",
        "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
      }
    ]
  },
  "documentation": {
    "source": "clawhub",
    "primaryDoc": "SKILL.md",
    "sections": [
      {
        "title": "Google Analytics 4 Setup Skill",
        "body": "You are setting up Google Analytics 4 (GA4) for a project. Follow this comprehensive guide to add analytics properly."
      },
      {
        "title": "Arguments",
        "body": "Parse the following from $ARGUMENTS:\n\nMeasurement ID: Format G-XXXXXXXXXX (required, ask if not provided)\n--events: Include custom event tracking helpers\n--consent: Include cookie consent integration\n--debug: Enable debug mode for development"
      },
      {
        "title": "Step 1: Detect Project Type",
        "body": "Scan the project to determine the framework/setup:\n\nPriority detection order:\n1. next.config.js/ts → Next.js\n2. nuxt.config.js/ts → Nuxt.js\n3. astro.config.mjs → Astro\n4. svelte.config.js → SvelteKit\n5. remix.config.js → Remix\n6. gatsby-config.js → Gatsby\n7. vite.config.js + src/App.vue → Vue + Vite\n8. vite.config.js + src/App.tsx → React + Vite\n9. angular.json → Angular\n10. package.json with \"react-scripts\" → Create React App\n11. index.html only → Plain HTML\n12. _app.tsx/jsx → Next.js (App Router check: app/ directory)\n\nAlso check for:\n\nTypeScript usage (tsconfig.json)\nExisting analytics (search for gtag, GA, analytics)\nPackage manager (pnpm-lock.yaml, yarn.lock, package-lock.json)"
      },
      {
        "title": "Step 2: Validate Measurement ID",
        "body": "The Measurement ID must:\n\nStart with G- (GA4 format)\nBe followed by exactly 10 alphanumeric characters\nExample: G-ABC1234567\n\nIf the user provides a UA- ID, inform them:\n\n\"You provided a Universal Analytics ID (UA-). GA4 uses Measurement IDs starting with 'G-'.\nUniversal Analytics was sunset in July 2024. You'll need to create a GA4 property at analytics.google.com\""
      },
      {
        "title": "Next.js (App Router - app/ directory)",
        "body": "Create app/layout.tsx modification or create components/GoogleAnalytics.tsx:\n\n// components/GoogleAnalytics.tsx\n'use client'\n\nimport Script from 'next/script'\n\ninterface GoogleAnalyticsProps {\n  measurementId: string\n}\n\nexport function GoogleAnalytics({ measurementId }: GoogleAnalyticsProps) {\n  return (\n    <>\n      <Script\n        src={`https://www.googletagmanager.com/gtag/js?id=${measurementId}`}\n        strategy=\"afterInteractive\"\n      />\n      <Script id=\"google-analytics\" strategy=\"afterInteractive\">\n        {`\n          window.dataLayer = window.dataLayer || [];\n          function gtag(){dataLayer.push(arguments);}\n          gtag('js', new Date());\n          gtag('config', '${measurementId}');\n        `}\n      </Script>\n    </>\n  )\n}\n\nAdd to root layout:\n\n// app/layout.tsx\nimport { GoogleAnalytics } from '@/components/GoogleAnalytics'\n\n// Add inside <body> or <html>:\n<GoogleAnalytics measurementId=\"G-XXXXXXXXXX\" />"
      },
      {
        "title": "Next.js (Pages Router - pages/ directory)",
        "body": "Modify pages/_app.tsx:\n\n// pages/_app.tsx\nimport type { AppProps } from 'next/app'\nimport Script from 'next/script'\n\nconst GA_MEASUREMENT_ID = process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID\n\nexport default function App({ Component, pageProps }: AppProps) {\n  return (\n    <>\n      <Script\n        src={`https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}`}\n        strategy=\"afterInteractive\"\n      />\n      <Script id=\"google-analytics\" strategy=\"afterInteractive\">\n        {`\n          window.dataLayer = window.dataLayer || [];\n          function gtag(){dataLayer.push(arguments);}\n          gtag('js', new Date());\n          gtag('config', '${GA_MEASUREMENT_ID}');\n        `}\n      </Script>\n      <Component {...pageProps} />\n    </>\n  )\n}"
      },
      {
        "title": "React (Vite/CRA)",
        "body": "Create src/lib/analytics.ts:\n\n// src/lib/analytics.ts\nexport const GA_MEASUREMENT_ID = import.meta.env.VITE_GA_MEASUREMENT_ID\n\ndeclare global {\n  interface Window {\n    gtag: (...args: unknown[]) => void\n    dataLayer: unknown[]\n  }\n}\n\nexport const initGA = () => {\n  if (typeof window === 'undefined') return\n\n  const script = document.createElement('script')\n  script.src = `https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}`\n  script.async = true\n  document.head.appendChild(script)\n\n  window.dataLayer = window.dataLayer || []\n  window.gtag = function gtag() {\n    window.dataLayer.push(arguments)\n  }\n  window.gtag('js', new Date())\n  window.gtag('config', GA_MEASUREMENT_ID)\n}\n\nexport const pageview = (url: string) => {\n  window.gtag('config', GA_MEASUREMENT_ID, {\n    page_path: url,\n  })\n}\n\nexport const event = (action: string, params?: Record<string, unknown>) => {\n  window.gtag('event', action, params)\n}\n\nInitialize in src/main.tsx:\n\nimport { initGA } from './lib/analytics'\n\n// Initialize before render\nif (import.meta.env.PROD) {\n  initGA()\n}"
      },
      {
        "title": "Vue 3 (Vite)",
        "body": "Create src/plugins/analytics.ts:\n\n// src/plugins/analytics.ts\nimport type { App } from 'vue'\nimport type { Router } from 'vue-router'\n\nconst GA_MEASUREMENT_ID = import.meta.env.VITE_GA_MEASUREMENT_ID\n\ndeclare global {\n  interface Window {\n    gtag: (...args: unknown[]) => void\n    dataLayer: unknown[]\n  }\n}\n\nexport const analyticsPlugin = {\n  install(app: App, { router }: { router: Router }) {\n    // Load gtag script\n    const script = document.createElement('script')\n    script.src = `https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}`\n    script.async = true\n    document.head.appendChild(script)\n\n    window.dataLayer = window.dataLayer || []\n    window.gtag = function gtag() {\n      window.dataLayer.push(arguments)\n    }\n    window.gtag('js', new Date())\n    window.gtag('config', GA_MEASUREMENT_ID)\n\n    // Track route changes\n    router.afterEach((to) => {\n      window.gtag('config', GA_MEASUREMENT_ID, {\n        page_path: to.fullPath,\n      })\n    })\n\n    // Provide global methods\n    app.config.globalProperties.$gtag = window.gtag\n  }\n}"
      },
      {
        "title": "Nuxt 3",
        "body": "Create plugins/analytics.client.ts:\n\n// plugins/analytics.client.ts\nexport default defineNuxtPlugin(() => {\n  const config = useRuntimeConfig()\n  const measurementId = config.public.gaMeasurementId\n\n  if (!measurementId) return\n\n  // Load gtag\n  useHead({\n    script: [\n      {\n        src: `https://www.googletagmanager.com/gtag/js?id=${measurementId}`,\n        async: true,\n      },\n      {\n        innerHTML: `\n          window.dataLayer = window.dataLayer || [];\n          function gtag(){dataLayer.push(arguments);}\n          gtag('js', new Date());\n          gtag('config', '${measurementId}');\n        `,\n      },\n    ],\n  })\n\n  // Track route changes\n  const router = useRouter()\n  router.afterEach((to) => {\n    window.gtag('config', measurementId, {\n      page_path: to.fullPath,\n    })\n  })\n})\n\nAdd to nuxt.config.ts:\n\nexport default defineNuxtConfig({\n  runtimeConfig: {\n    public: {\n      gaMeasurementId: process.env.NUXT_PUBLIC_GA_MEASUREMENT_ID,\n    },\n  },\n})"
      },
      {
        "title": "Astro",
        "body": "Create src/components/Analytics.astro:\n\n---\n// src/components/Analytics.astro\ninterface Props {\n  measurementId: string\n}\n\nconst { measurementId } = Astro.props\n---\n\n<script\n  is:inline\n  define:vars={{ measurementId }}\n  src={`https://www.googletagmanager.com/gtag/js?id=${measurementId}`}\n></script>\n\n<script is:inline define:vars={{ measurementId }}>\n  window.dataLayer = window.dataLayer || [];\n  function gtag() {\n    dataLayer.push(arguments);\n  }\n  gtag('js', new Date());\n  gtag('config', measurementId);\n</script>\n\nAdd to layout:\n\n---\nimport Analytics from '../components/Analytics.astro'\n---\n<html>\n  <head>\n    <Analytics measurementId=\"G-XXXXXXXXXX\" />\n  </head>\n</html>"
      },
      {
        "title": "SvelteKit",
        "body": "Create src/lib/analytics.ts and src/routes/+layout.svelte:\n\n// src/lib/analytics.ts\nimport { browser } from '$app/environment'\n\nexport const GA_MEASUREMENT_ID = import.meta.env.VITE_GA_MEASUREMENT_ID\n\nexport function initGA() {\n  if (!browser) return\n\n  const script = document.createElement('script')\n  script.src = `https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}`\n  script.async = true\n  document.head.appendChild(script)\n\n  window.dataLayer = window.dataLayer || []\n  window.gtag = function gtag() {\n    window.dataLayer.push(arguments)\n  }\n  window.gtag('js', new Date())\n  window.gtag('config', GA_MEASUREMENT_ID)\n}\n\nexport function trackPageview(url: string) {\n  if (!browser) return\n  window.gtag('config', GA_MEASUREMENT_ID, { page_path: url })\n}\n\n<!-- src/routes/+layout.svelte -->\n<script lang=\"ts\">\n  import { onMount } from 'svelte'\n  import { page } from '$app/stores'\n  import { initGA, trackPageview } from '$lib/analytics'\n\n  onMount(() => {\n    initGA()\n  })\n\n  $: if ($page.url.pathname) {\n    trackPageview($page.url.pathname)\n  }\n</script>\n\n<slot />"
      },
      {
        "title": "Plain HTML",
        "body": "Add to <head>:\n\n<!-- Google Analytics -->\n<script async src=\"https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX\"></script>\n<script>\n  window.dataLayer = window.dataLayer || [];\n  function gtag(){dataLayer.push(arguments);}\n  gtag('js', new Date());\n  gtag('config', 'G-XXXXXXXXXX');\n</script>"
      },
      {
        "title": "Step 4: Environment Variables",
        "body": "Create or update .env / .env.local:\n\n# For Next.js\nNEXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX\n\n# For Vite (React/Vue/Svelte)\nVITE_GA_MEASUREMENT_ID=G-XXXXXXXXXX\n\n# For Nuxt\nNUXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX\n\nAdd to .env.example if it exists (without the actual ID):\n\n# Google Analytics 4 Measurement ID\nNEXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX\n\nIMPORTANT: Add .env.local to .gitignore if not already present."
      },
      {
        "title": "Step 5: Event Tracking Helpers (if --events flag)",
        "body": "Create a comprehensive events utility:\n\n// lib/analytics-events.ts\n\n/**\n * GA4 Event Tracking Utilities\n *\n * Recommended events: https://support.google.com/analytics/answer/9267735\n */\n\ntype GTagEvent = {\n  action: string\n  category?: string\n  label?: string\n  value?: number\n  [key: string]: unknown\n}\n\n// Core event function\nexport const trackEvent = ({ action, category, label, value, ...rest }: GTagEvent) => {\n  if (typeof window === 'undefined' || !window.gtag) return\n\n  window.gtag('event', action, {\n    event_category: category,\n    event_label: label,\n    value,\n    ...rest,\n  })\n}\n\n// Engagement events\nexport const trackClick = (elementName: string, location?: string) => {\n  trackEvent({\n    action: 'click',\n    category: 'engagement',\n    label: elementName,\n    click_location: location,\n  })\n}\n\nexport const trackScroll = (percentage: number) => {\n  trackEvent({\n    action: 'scroll',\n    category: 'engagement',\n    value: percentage,\n  })\n}\n\n// Conversion events\nexport const trackSignUp = (method: string) => {\n  trackEvent({\n    action: 'sign_up',\n    method,\n  })\n}\n\nexport const trackLogin = (method: string) => {\n  trackEvent({\n    action: 'login',\n    method,\n  })\n}\n\nexport const trackPurchase = (params: {\n  transactionId: string\n  value: number\n  currency: string\n  items?: Array<{\n    itemId: string\n    itemName: string\n    price: number\n    quantity: number\n  }>\n}) => {\n  trackEvent({\n    action: 'purchase',\n    transaction_id: params.transactionId,\n    value: params.value,\n    currency: params.currency,\n    items: params.items,\n  })\n}\n\n// Content events\nexport const trackSearch = (searchTerm: string) => {\n  trackEvent({\n    action: 'search',\n    search_term: searchTerm,\n  })\n}\n\nexport const trackShare = (method: string, contentType: string, itemId: string) => {\n  trackEvent({\n    action: 'share',\n    method,\n    content_type: contentType,\n    item_id: itemId,\n  })\n}\n\n// Form events\nexport const trackFormStart = (formName: string) => {\n  trackEvent({\n    action: 'form_start',\n    form_name: formName,\n  })\n}\n\nexport const trackFormSubmit = (formName: string, success: boolean) => {\n  trackEvent({\n    action: 'form_submit',\n    form_name: formName,\n    success,\n  })\n}\n\n// Error tracking\nexport const trackError = (errorMessage: string, errorLocation?: string) => {\n  trackEvent({\n    action: 'exception',\n    description: errorMessage,\n    fatal: false,\n    error_location: errorLocation,\n  })\n}\n\n// Custom event builder for flexibility\nexport const createCustomEvent = (eventName: string) => {\n  return (params?: Record<string, unknown>) => {\n    trackEvent({\n      action: eventName,\n      ...params,\n    })\n  }\n}"
      },
      {
        "title": "Step 6: Cookie Consent Integration (if --consent flag)",
        "body": "Create a consent-aware wrapper:\n\n// lib/analytics-consent.ts\n\ntype ConsentState = 'granted' | 'denied'\n\ninterface ConsentConfig {\n  analytics_storage: ConsentState\n  ad_storage: ConsentState\n  ad_user_data: ConsentState\n  ad_personalization: ConsentState\n}\n\nconst CONSENT_COOKIE = 'analytics_consent'\n\n// Initialize with consent mode\nexport const initWithConsent = (measurementId: string) => {\n  if (typeof window === 'undefined') return\n\n  // Set default consent state (denied until user consents)\n  window.gtag('consent', 'default', {\n    analytics_storage: 'denied',\n    ad_storage: 'denied',\n    ad_user_data: 'denied',\n    ad_personalization: 'denied',\n    wait_for_update: 500, // Wait for consent banner\n  })\n\n  // Load gtag\n  const script = document.createElement('script')\n  script.src = `https://www.googletagmanager.com/gtag/js?id=${measurementId}`\n  script.async = true\n  document.head.appendChild(script)\n\n  window.dataLayer = window.dataLayer || []\n  window.gtag = function gtag() {\n    window.dataLayer.push(arguments)\n  }\n  window.gtag('js', new Date())\n  window.gtag('config', measurementId)\n\n  // Check for existing consent\n  const savedConsent = getCookie(CONSENT_COOKIE)\n  if (savedConsent) {\n    updateConsent(JSON.parse(savedConsent))\n  }\n}\n\n// Update consent when user makes a choice\nexport const updateConsent = (consent: Partial<ConsentConfig>) => {\n  if (typeof window === 'undefined' || !window.gtag) return\n\n  const consentState: ConsentConfig = {\n    analytics_storage: consent.analytics_storage || 'denied',\n    ad_storage: consent.ad_storage || 'denied',\n    ad_user_data: consent.ad_user_data || 'denied',\n    ad_personalization: consent.ad_personalization || 'denied',\n  }\n\n  window.gtag('consent', 'update', consentState)\n\n  // Save to cookie\n  setCookie(CONSENT_COOKIE, JSON.stringify(consentState), 365)\n}\n\n// Convenience functions\nexport const acceptAll = () => {\n  updateConsent({\n    analytics_storage: 'granted',\n    ad_storage: 'granted',\n    ad_user_data: 'granted',\n    ad_personalization: 'granted',\n  })\n}\n\nexport const acceptAnalyticsOnly = () => {\n  updateConsent({\n    analytics_storage: 'granted',\n    ad_storage: 'denied',\n    ad_user_data: 'denied',\n    ad_personalization: 'denied',\n  })\n}\n\nexport const denyAll = () => {\n  updateConsent({\n    analytics_storage: 'denied',\n    ad_storage: 'denied',\n    ad_user_data: 'denied',\n    ad_personalization: 'denied',\n  })\n}\n\n// Cookie utilities\nfunction setCookie(name: string, value: string, days: number) {\n  const date = new Date()\n  date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000)\n  document.cookie = `${name}=${value};expires=${date.toUTCString()};path=/;SameSite=Lax`\n}\n\nfunction getCookie(name: string): string | null {\n  const match = document.cookie.match(new RegExp(`(^| )${name}=([^;]+)`))\n  return match ? match[2] : null\n}"
      },
      {
        "title": "Step 7: Debug Mode (if --debug flag)",
        "body": "Add debug configuration:\n\n// For development, enable debug mode\nif (process.env.NODE_ENV === 'development') {\n  window.gtag('config', 'G-XXXXXXXXXX', {\n    debug_mode: true,\n  })\n}\n\nAlso recommend installing the Google Analytics Debugger Chrome extension."
      },
      {
        "title": "Step 8: TypeScript Declarations",
        "body": "Create types/gtag.d.ts if using TypeScript:\n\n// types/gtag.d.ts\ndeclare global {\n  interface Window {\n    gtag: Gtag.Gtag\n    dataLayer: object[]\n  }\n}\n\ndeclare namespace Gtag {\n  interface Gtag {\n    (command: 'config', targetId: string, config?: ConfigParams): void\n    (command: 'set', targetId: string, config: ConfigParams): void\n    (command: 'set', config: ConfigParams): void\n    (command: 'js', date: Date): void\n    (command: 'event', eventName: string, eventParams?: EventParams): void\n    (command: 'consent', consentArg: 'default' | 'update', consentParams: ConsentParams): void\n    (...args: unknown[]): void\n  }\n\n  interface ConfigParams {\n    page_title?: string\n    page_location?: string\n    page_path?: string\n    send_page_view?: boolean\n    debug_mode?: boolean\n    [key: string]: unknown\n  }\n\n  interface EventParams {\n    event_category?: string\n    event_label?: string\n    value?: number\n    [key: string]: unknown\n  }\n\n  interface ConsentParams {\n    analytics_storage?: 'granted' | 'denied'\n    ad_storage?: 'granted' | 'denied'\n    ad_user_data?: 'granted' | 'denied'\n    ad_personalization?: 'granted' | 'denied'\n    wait_for_update?: number\n  }\n}\n\nexport {}"
      },
      {
        "title": "Step 9: Verification Checklist",
        "body": "After implementation, verify:\n\nMeasurement ID is correct format (G-XXXXXXXXXX)\n Script loads in production (check Network tab)\n Real-time reports show activity in GA4 dashboard\n Page views are tracked on navigation\n No console errors related to gtag\n Environment variables are not committed to git\n TypeScript has no type errors (if applicable)"
      },
      {
        "title": "Step 10: Summary Output",
        "body": "After completing setup, provide the user with:\n\nFiles created/modified (list them)\nEnvironment variables needed (with example values)\nNext steps:\n\nAdd the Measurement ID to environment variables\nDeploy and verify in GA4 Real-time reports\nSet up conversions in GA4 dashboard\nConsider adding custom events for key user actions"
      },
      {
        "title": "Common Issues & Solutions",
        "body": "\"gtag is not defined\"\n\nScript hasn't loaded yet; ensure async loading is handled\n\nNo data in GA4\n\nCheck if ad blockers are preventing tracking\nVerify Measurement ID is correct\nCheck browser console for errors\n\nDouble page views\n\nSPA router sending duplicate events; implement deduplication\n\nGDPR Compliance\n\nAlways implement consent mode for EU users\nUse the --consent flag to add consent management"
      }
    ],
    "body": "Google Analytics 4 Setup Skill\n\nYou are setting up Google Analytics 4 (GA4) for a project. Follow this comprehensive guide to add analytics properly.\n\nArguments\n\nParse the following from $ARGUMENTS:\n\nMeasurement ID: Format G-XXXXXXXXXX (required, ask if not provided)\n--events: Include custom event tracking helpers\n--consent: Include cookie consent integration\n--debug: Enable debug mode for development\nStep 1: Detect Project Type\n\nScan the project to determine the framework/setup:\n\nPriority detection order:\n1. next.config.js/ts → Next.js\n2. nuxt.config.js/ts → Nuxt.js\n3. astro.config.mjs → Astro\n4. svelte.config.js → SvelteKit\n5. remix.config.js → Remix\n6. gatsby-config.js → Gatsby\n7. vite.config.js + src/App.vue → Vue + Vite\n8. vite.config.js + src/App.tsx → React + Vite\n9. angular.json → Angular\n10. package.json with \"react-scripts\" → Create React App\n11. index.html only → Plain HTML\n12. _app.tsx/jsx → Next.js (App Router check: app/ directory)\n\n\nAlso check for:\n\nTypeScript usage (tsconfig.json)\nExisting analytics (search for gtag, GA, analytics)\nPackage manager (pnpm-lock.yaml, yarn.lock, package-lock.json)\nStep 2: Validate Measurement ID\n\nThe Measurement ID must:\n\nStart with G- (GA4 format)\nBe followed by exactly 10 alphanumeric characters\nExample: G-ABC1234567\n\nIf the user provides a UA- ID, inform them:\n\n\"You provided a Universal Analytics ID (UA-). GA4 uses Measurement IDs starting with 'G-'. Universal Analytics was sunset in July 2024. You'll need to create a GA4 property at analytics.google.com\"\n\nStep 3: Implementation by Framework\nNext.js (App Router - app/ directory)\n\nCreate app/layout.tsx modification or create components/GoogleAnalytics.tsx:\n\n// components/GoogleAnalytics.tsx\n'use client'\n\nimport Script from 'next/script'\n\ninterface GoogleAnalyticsProps {\n  measurementId: string\n}\n\nexport function GoogleAnalytics({ measurementId }: GoogleAnalyticsProps) {\n  return (\n    <>\n      <Script\n        src={`https://www.googletagmanager.com/gtag/js?id=${measurementId}`}\n        strategy=\"afterInteractive\"\n      />\n      <Script id=\"google-analytics\" strategy=\"afterInteractive\">\n        {`\n          window.dataLayer = window.dataLayer || [];\n          function gtag(){dataLayer.push(arguments);}\n          gtag('js', new Date());\n          gtag('config', '${measurementId}');\n        `}\n      </Script>\n    </>\n  )\n}\n\n\nAdd to root layout:\n\n// app/layout.tsx\nimport { GoogleAnalytics } from '@/components/GoogleAnalytics'\n\n// Add inside <body> or <html>:\n<GoogleAnalytics measurementId=\"G-XXXXXXXXXX\" />\n\nNext.js (Pages Router - pages/ directory)\n\nModify pages/_app.tsx:\n\n// pages/_app.tsx\nimport type { AppProps } from 'next/app'\nimport Script from 'next/script'\n\nconst GA_MEASUREMENT_ID = process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID\n\nexport default function App({ Component, pageProps }: AppProps) {\n  return (\n    <>\n      <Script\n        src={`https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}`}\n        strategy=\"afterInteractive\"\n      />\n      <Script id=\"google-analytics\" strategy=\"afterInteractive\">\n        {`\n          window.dataLayer = window.dataLayer || [];\n          function gtag(){dataLayer.push(arguments);}\n          gtag('js', new Date());\n          gtag('config', '${GA_MEASUREMENT_ID}');\n        `}\n      </Script>\n      <Component {...pageProps} />\n    </>\n  )\n}\n\nReact (Vite/CRA)\n\nCreate src/lib/analytics.ts:\n\n// src/lib/analytics.ts\nexport const GA_MEASUREMENT_ID = import.meta.env.VITE_GA_MEASUREMENT_ID\n\ndeclare global {\n  interface Window {\n    gtag: (...args: unknown[]) => void\n    dataLayer: unknown[]\n  }\n}\n\nexport const initGA = () => {\n  if (typeof window === 'undefined') return\n\n  const script = document.createElement('script')\n  script.src = `https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}`\n  script.async = true\n  document.head.appendChild(script)\n\n  window.dataLayer = window.dataLayer || []\n  window.gtag = function gtag() {\n    window.dataLayer.push(arguments)\n  }\n  window.gtag('js', new Date())\n  window.gtag('config', GA_MEASUREMENT_ID)\n}\n\nexport const pageview = (url: string) => {\n  window.gtag('config', GA_MEASUREMENT_ID, {\n    page_path: url,\n  })\n}\n\nexport const event = (action: string, params?: Record<string, unknown>) => {\n  window.gtag('event', action, params)\n}\n\n\nInitialize in src/main.tsx:\n\nimport { initGA } from './lib/analytics'\n\n// Initialize before render\nif (import.meta.env.PROD) {\n  initGA()\n}\n\nVue 3 (Vite)\n\nCreate src/plugins/analytics.ts:\n\n// src/plugins/analytics.ts\nimport type { App } from 'vue'\nimport type { Router } from 'vue-router'\n\nconst GA_MEASUREMENT_ID = import.meta.env.VITE_GA_MEASUREMENT_ID\n\ndeclare global {\n  interface Window {\n    gtag: (...args: unknown[]) => void\n    dataLayer: unknown[]\n  }\n}\n\nexport const analyticsPlugin = {\n  install(app: App, { router }: { router: Router }) {\n    // Load gtag script\n    const script = document.createElement('script')\n    script.src = `https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}`\n    script.async = true\n    document.head.appendChild(script)\n\n    window.dataLayer = window.dataLayer || []\n    window.gtag = function gtag() {\n      window.dataLayer.push(arguments)\n    }\n    window.gtag('js', new Date())\n    window.gtag('config', GA_MEASUREMENT_ID)\n\n    // Track route changes\n    router.afterEach((to) => {\n      window.gtag('config', GA_MEASUREMENT_ID, {\n        page_path: to.fullPath,\n      })\n    })\n\n    // Provide global methods\n    app.config.globalProperties.$gtag = window.gtag\n  }\n}\n\nNuxt 3\n\nCreate plugins/analytics.client.ts:\n\n// plugins/analytics.client.ts\nexport default defineNuxtPlugin(() => {\n  const config = useRuntimeConfig()\n  const measurementId = config.public.gaMeasurementId\n\n  if (!measurementId) return\n\n  // Load gtag\n  useHead({\n    script: [\n      {\n        src: `https://www.googletagmanager.com/gtag/js?id=${measurementId}`,\n        async: true,\n      },\n      {\n        innerHTML: `\n          window.dataLayer = window.dataLayer || [];\n          function gtag(){dataLayer.push(arguments);}\n          gtag('js', new Date());\n          gtag('config', '${measurementId}');\n        `,\n      },\n    ],\n  })\n\n  // Track route changes\n  const router = useRouter()\n  router.afterEach((to) => {\n    window.gtag('config', measurementId, {\n      page_path: to.fullPath,\n    })\n  })\n})\n\n\nAdd to nuxt.config.ts:\n\nexport default defineNuxtConfig({\n  runtimeConfig: {\n    public: {\n      gaMeasurementId: process.env.NUXT_PUBLIC_GA_MEASUREMENT_ID,\n    },\n  },\n})\n\nAstro\n\nCreate src/components/Analytics.astro:\n\n---\n// src/components/Analytics.astro\ninterface Props {\n  measurementId: string\n}\n\nconst { measurementId } = Astro.props\n---\n\n<script\n  is:inline\n  define:vars={{ measurementId }}\n  src={`https://www.googletagmanager.com/gtag/js?id=${measurementId}`}\n></script>\n\n<script is:inline define:vars={{ measurementId }}>\n  window.dataLayer = window.dataLayer || [];\n  function gtag() {\n    dataLayer.push(arguments);\n  }\n  gtag('js', new Date());\n  gtag('config', measurementId);\n</script>\n\n\nAdd to layout:\n\n---\nimport Analytics from '../components/Analytics.astro'\n---\n<html>\n  <head>\n    <Analytics measurementId=\"G-XXXXXXXXXX\" />\n  </head>\n</html>\n\nSvelteKit\n\nCreate src/lib/analytics.ts and src/routes/+layout.svelte:\n\n// src/lib/analytics.ts\nimport { browser } from '$app/environment'\n\nexport const GA_MEASUREMENT_ID = import.meta.env.VITE_GA_MEASUREMENT_ID\n\nexport function initGA() {\n  if (!browser) return\n\n  const script = document.createElement('script')\n  script.src = `https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}`\n  script.async = true\n  document.head.appendChild(script)\n\n  window.dataLayer = window.dataLayer || []\n  window.gtag = function gtag() {\n    window.dataLayer.push(arguments)\n  }\n  window.gtag('js', new Date())\n  window.gtag('config', GA_MEASUREMENT_ID)\n}\n\nexport function trackPageview(url: string) {\n  if (!browser) return\n  window.gtag('config', GA_MEASUREMENT_ID, { page_path: url })\n}\n\n<!-- src/routes/+layout.svelte -->\n<script lang=\"ts\">\n  import { onMount } from 'svelte'\n  import { page } from '$app/stores'\n  import { initGA, trackPageview } from '$lib/analytics'\n\n  onMount(() => {\n    initGA()\n  })\n\n  $: if ($page.url.pathname) {\n    trackPageview($page.url.pathname)\n  }\n</script>\n\n<slot />\n\nPlain HTML\n\nAdd to <head>:\n\n<!-- Google Analytics -->\n<script async src=\"https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX\"></script>\n<script>\n  window.dataLayer = window.dataLayer || [];\n  function gtag(){dataLayer.push(arguments);}\n  gtag('js', new Date());\n  gtag('config', 'G-XXXXXXXXXX');\n</script>\n\nStep 4: Environment Variables\n\nCreate or update .env / .env.local:\n\n# For Next.js\nNEXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX\n\n# For Vite (React/Vue/Svelte)\nVITE_GA_MEASUREMENT_ID=G-XXXXXXXXXX\n\n# For Nuxt\nNUXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX\n\n\nAdd to .env.example if it exists (without the actual ID):\n\n# Google Analytics 4 Measurement ID\nNEXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX\n\n\nIMPORTANT: Add .env.local to .gitignore if not already present.\n\nStep 5: Event Tracking Helpers (if --events flag)\n\nCreate a comprehensive events utility:\n\n// lib/analytics-events.ts\n\n/**\n * GA4 Event Tracking Utilities\n *\n * Recommended events: https://support.google.com/analytics/answer/9267735\n */\n\ntype GTagEvent = {\n  action: string\n  category?: string\n  label?: string\n  value?: number\n  [key: string]: unknown\n}\n\n// Core event function\nexport const trackEvent = ({ action, category, label, value, ...rest }: GTagEvent) => {\n  if (typeof window === 'undefined' || !window.gtag) return\n\n  window.gtag('event', action, {\n    event_category: category,\n    event_label: label,\n    value,\n    ...rest,\n  })\n}\n\n// Engagement events\nexport const trackClick = (elementName: string, location?: string) => {\n  trackEvent({\n    action: 'click',\n    category: 'engagement',\n    label: elementName,\n    click_location: location,\n  })\n}\n\nexport const trackScroll = (percentage: number) => {\n  trackEvent({\n    action: 'scroll',\n    category: 'engagement',\n    value: percentage,\n  })\n}\n\n// Conversion events\nexport const trackSignUp = (method: string) => {\n  trackEvent({\n    action: 'sign_up',\n    method,\n  })\n}\n\nexport const trackLogin = (method: string) => {\n  trackEvent({\n    action: 'login',\n    method,\n  })\n}\n\nexport const trackPurchase = (params: {\n  transactionId: string\n  value: number\n  currency: string\n  items?: Array<{\n    itemId: string\n    itemName: string\n    price: number\n    quantity: number\n  }>\n}) => {\n  trackEvent({\n    action: 'purchase',\n    transaction_id: params.transactionId,\n    value: params.value,\n    currency: params.currency,\n    items: params.items,\n  })\n}\n\n// Content events\nexport const trackSearch = (searchTerm: string) => {\n  trackEvent({\n    action: 'search',\n    search_term: searchTerm,\n  })\n}\n\nexport const trackShare = (method: string, contentType: string, itemId: string) => {\n  trackEvent({\n    action: 'share',\n    method,\n    content_type: contentType,\n    item_id: itemId,\n  })\n}\n\n// Form events\nexport const trackFormStart = (formName: string) => {\n  trackEvent({\n    action: 'form_start',\n    form_name: formName,\n  })\n}\n\nexport const trackFormSubmit = (formName: string, success: boolean) => {\n  trackEvent({\n    action: 'form_submit',\n    form_name: formName,\n    success,\n  })\n}\n\n// Error tracking\nexport const trackError = (errorMessage: string, errorLocation?: string) => {\n  trackEvent({\n    action: 'exception',\n    description: errorMessage,\n    fatal: false,\n    error_location: errorLocation,\n  })\n}\n\n// Custom event builder for flexibility\nexport const createCustomEvent = (eventName: string) => {\n  return (params?: Record<string, unknown>) => {\n    trackEvent({\n      action: eventName,\n      ...params,\n    })\n  }\n}\n\nStep 6: Cookie Consent Integration (if --consent flag)\n\nCreate a consent-aware wrapper:\n\n// lib/analytics-consent.ts\n\ntype ConsentState = 'granted' | 'denied'\n\ninterface ConsentConfig {\n  analytics_storage: ConsentState\n  ad_storage: ConsentState\n  ad_user_data: ConsentState\n  ad_personalization: ConsentState\n}\n\nconst CONSENT_COOKIE = 'analytics_consent'\n\n// Initialize with consent mode\nexport const initWithConsent = (measurementId: string) => {\n  if (typeof window === 'undefined') return\n\n  // Set default consent state (denied until user consents)\n  window.gtag('consent', 'default', {\n    analytics_storage: 'denied',\n    ad_storage: 'denied',\n    ad_user_data: 'denied',\n    ad_personalization: 'denied',\n    wait_for_update: 500, // Wait for consent banner\n  })\n\n  // Load gtag\n  const script = document.createElement('script')\n  script.src = `https://www.googletagmanager.com/gtag/js?id=${measurementId}`\n  script.async = true\n  document.head.appendChild(script)\n\n  window.dataLayer = window.dataLayer || []\n  window.gtag = function gtag() {\n    window.dataLayer.push(arguments)\n  }\n  window.gtag('js', new Date())\n  window.gtag('config', measurementId)\n\n  // Check for existing consent\n  const savedConsent = getCookie(CONSENT_COOKIE)\n  if (savedConsent) {\n    updateConsent(JSON.parse(savedConsent))\n  }\n}\n\n// Update consent when user makes a choice\nexport const updateConsent = (consent: Partial<ConsentConfig>) => {\n  if (typeof window === 'undefined' || !window.gtag) return\n\n  const consentState: ConsentConfig = {\n    analytics_storage: consent.analytics_storage || 'denied',\n    ad_storage: consent.ad_storage || 'denied',\n    ad_user_data: consent.ad_user_data || 'denied',\n    ad_personalization: consent.ad_personalization || 'denied',\n  }\n\n  window.gtag('consent', 'update', consentState)\n\n  // Save to cookie\n  setCookie(CONSENT_COOKIE, JSON.stringify(consentState), 365)\n}\n\n// Convenience functions\nexport const acceptAll = () => {\n  updateConsent({\n    analytics_storage: 'granted',\n    ad_storage: 'granted',\n    ad_user_data: 'granted',\n    ad_personalization: 'granted',\n  })\n}\n\nexport const acceptAnalyticsOnly = () => {\n  updateConsent({\n    analytics_storage: 'granted',\n    ad_storage: 'denied',\n    ad_user_data: 'denied',\n    ad_personalization: 'denied',\n  })\n}\n\nexport const denyAll = () => {\n  updateConsent({\n    analytics_storage: 'denied',\n    ad_storage: 'denied',\n    ad_user_data: 'denied',\n    ad_personalization: 'denied',\n  })\n}\n\n// Cookie utilities\nfunction setCookie(name: string, value: string, days: number) {\n  const date = new Date()\n  date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000)\n  document.cookie = `${name}=${value};expires=${date.toUTCString()};path=/;SameSite=Lax`\n}\n\nfunction getCookie(name: string): string | null {\n  const match = document.cookie.match(new RegExp(`(^| )${name}=([^;]+)`))\n  return match ? match[2] : null\n}\n\nStep 7: Debug Mode (if --debug flag)\n\nAdd debug configuration:\n\n// For development, enable debug mode\nif (process.env.NODE_ENV === 'development') {\n  window.gtag('config', 'G-XXXXXXXXXX', {\n    debug_mode: true,\n  })\n}\n\n\nAlso recommend installing the Google Analytics Debugger Chrome extension.\n\nStep 8: TypeScript Declarations\n\nCreate types/gtag.d.ts if using TypeScript:\n\n// types/gtag.d.ts\ndeclare global {\n  interface Window {\n    gtag: Gtag.Gtag\n    dataLayer: object[]\n  }\n}\n\ndeclare namespace Gtag {\n  interface Gtag {\n    (command: 'config', targetId: string, config?: ConfigParams): void\n    (command: 'set', targetId: string, config: ConfigParams): void\n    (command: 'set', config: ConfigParams): void\n    (command: 'js', date: Date): void\n    (command: 'event', eventName: string, eventParams?: EventParams): void\n    (command: 'consent', consentArg: 'default' | 'update', consentParams: ConsentParams): void\n    (...args: unknown[]): void\n  }\n\n  interface ConfigParams {\n    page_title?: string\n    page_location?: string\n    page_path?: string\n    send_page_view?: boolean\n    debug_mode?: boolean\n    [key: string]: unknown\n  }\n\n  interface EventParams {\n    event_category?: string\n    event_label?: string\n    value?: number\n    [key: string]: unknown\n  }\n\n  interface ConsentParams {\n    analytics_storage?: 'granted' | 'denied'\n    ad_storage?: 'granted' | 'denied'\n    ad_user_data?: 'granted' | 'denied'\n    ad_personalization?: 'granted' | 'denied'\n    wait_for_update?: number\n  }\n}\n\nexport {}\n\nStep 9: Verification Checklist\n\nAfter implementation, verify:\n\n Measurement ID is correct format (G-XXXXXXXXXX)\n Script loads in production (check Network tab)\n Real-time reports show activity in GA4 dashboard\n Page views are tracked on navigation\n No console errors related to gtag\n Environment variables are not committed to git\n TypeScript has no type errors (if applicable)\nStep 10: Summary Output\n\nAfter completing setup, provide the user with:\n\nFiles created/modified (list them)\nEnvironment variables needed (with example values)\nNext steps:\nAdd the Measurement ID to environment variables\nDeploy and verify in GA4 Real-time reports\nSet up conversions in GA4 dashboard\nConsider adding custom events for key user actions\nCommon Issues & Solutions\n\n\"gtag is not defined\"\n\nScript hasn't loaded yet; ensure async loading is handled\n\nNo data in GA4\n\nCheck if ad blockers are preventing tracking\nVerify Measurement ID is correct\nCheck browser console for errors\n\nDouble page views\n\nSPA router sending duplicate events; implement deduplication\n\nGDPR Compliance\n\nAlways implement consent mode for EU users\nUse the --consent flag to add consent management"
  },
  "trust": {
    "sourceLabel": "tencent",
    "provenanceUrl": "https://clawhub.ai/jeftekhari/add-analytics",
    "publisherUrl": "https://clawhub.ai/jeftekhari/add-analytics",
    "owner": "jeftekhari",
    "version": "0.1.0",
    "license": null,
    "verificationStatus": "Indexed source record"
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/add-analytics",
    "downloadUrl": "https://openagent3.xyz/downloads/add-analytics",
    "agentUrl": "https://openagent3.xyz/skills/add-analytics/agent",
    "manifestUrl": "https://openagent3.xyz/skills/add-analytics/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/add-analytics/agent.md"
  }
}