skills/google-ads-query/SKILL.md
Query any Google Ads account via GAQL. Use when the user asks about Google Ads campaigns, conversions, keywords, ad performance, or bidding. Triggered by: Google Ads, campaign, conversion, PPC, CPC, keyword, click, impression, ROAS, CPA.
npx skillsauth add aivokone/ak-skills-ops google-ads-queryInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Check prerequisites before querying:
Script path resolution: Before running any script, determine the correct base path. Check in this order:
.claude/skills/google-ads-query/scripts/ — project-local install (run from repository root)~/.claude/skills/google-ads-query/scripts/ — global installUse whichever path exists. All script paths below use the project-local form; substitute the global path if that's where scripts are found.
.claude/skills/google-ads-query/scripts/.venv/ must existgoogle-ads.yaml must be findable (see Config section)# 1. Install dependencies
bash .claude/skills/google-ads-query/scripts/setup.sh
# 2. Create google-ads.yaml in project root or ~/.config/google-ads-query/
# (see Config section for template)
# 3. Test (direct argument is fine here — no special characters)
gads "SELECT customer.id FROM customer"
When running inside the Claude Code sandbox, network access is restricted. Add the following domains to ~/.claude/settings.json under sandbox.network.allowedDomains:
Setup (pip install):
pypi.orgfiles.pythonhosted.orgRuntime (Google Ads API):
googleads.googleapis.comoauth2.googleapis.comThe script searches for google-ads.yaml in this order:
--config CLI parameter (explicit path)./google-ads.yaml (project root)~/.config/google-ads-query/google-ads.yaml (user-level)Template (google-ads.yaml):
# Service account authentication:
developer_token: "YOUR_TOKEN"
json_key_file_path: "/path/to/service-account.json"
customer_id: "YOUR_CUSTOMER_ID"
use_proto_plus: true
# OR OAuth2 authentication:
# developer_token: "YOUR_TOKEN"
# client_id: "YOUR_CLIENT_ID.apps.googleusercontent.com"
# client_secret: "YOUR_SECRET"
# refresh_token: "1//YOUR_REFRESH_TOKEN"
# customer_id: "YOUR_CUSTOMER_ID"
# use_proto_plus: true
# Optional (manager accounts):
# login_customer_id: "MANAGER_CUSTOMER_ID"
Use the gads alias for queries:
alias gads='.claude/skills/google-ads-query/scripts/.venv/bin/python3 .claude/skills/google-ads-query/scripts/query.py'
IMPORTANT: Always pipe queries via stdin. Use echo "QUERY" | gads - for all queries. This avoids shell escaping failures with dots in field names, single quotes in WHERE clauses, and other GAQL punctuation. Direct argument passing (gads "QUERY") is fragile and should only be used for trivial test queries like gads "SELECT customer.id FROM customer".
# Standard query (pipe via stdin to avoid shell escaping issues)
echo "SELECT campaign.name, metrics.clicks FROM campaign" | gads -
# Query with single quotes in WHERE clause
echo "SELECT campaign.name FROM campaign WHERE campaign.status != 'REMOVED'" | gads -
# Override customer ID
echo "SELECT campaign.name FROM campaign" | gads --customer-id 9876543210 -
# Explicit config path
echo "SELECT campaign.name FROM campaign" | gads --config /path/to/google-ads.yaml -
# Direct argument (only for trivial test queries with no special characters)
gads "SELECT customer.id FROM customer"
Direct argument mode: Passing the query as a positional argument (gads "QUERY") works only for simple queries with no single quotes, dots in WHERE clauses, or other special GAQL characters. For all real queries, use stdin (echo "..." | gads -).
Output: JSON array to stdout. Enums returned as names (e.g., "ENABLED", not 2).
Errors: JSON to stderr + exit code 1.
These fields may cause issues depending on API version:
campaign.start_date, campaign.end_date — may be unrecognizedconversion_action.type — can return cryptic errorsconversion_action.tag_snippets — repeated composite, may fail in some versionsThe script handles repeated fields (both scalar and composite) correctly via protobuf serialization. If a query fails, remove suspected fields one at a time to isolate the issue.
echo "SELECT campaign.id, campaign.name, campaign.status,
campaign.advertising_channel_type, campaign.bidding_strategy_type,
metrics.impressions, metrics.clicks, metrics.cost_micros,
metrics.conversions, metrics.all_conversions
FROM campaign
WHERE campaign.status != 'REMOVED'" | gads -
echo "SELECT conversion_action.id, conversion_action.name,
conversion_action.status, conversion_action.category,
conversion_action.origin, conversion_action.counting_type,
conversion_action.include_in_conversions_metric,
conversion_action.primary_for_goal
FROM conversion_action" | gads -
echo "SELECT campaign.id, campaign.name,
campaign_conversion_goal.category, campaign_conversion_goal.origin,
campaign_conversion_goal.biddable
FROM campaign_conversion_goal
WHERE campaign.id = CAMPAIGN_ID" | gads -
echo "SELECT campaign.id, campaign.name,
conversion_goal_campaign_config.goal_config_level
FROM conversion_goal_campaign_config" | gads -
echo "SELECT campaign.name, metrics.impressions, metrics.clicks,
metrics.cost_micros, metrics.conversions, segments.date
FROM campaign
WHERE segments.date >= 'YYYY-MM-DD' AND segments.date <= 'YYYY-MM-DD'
AND campaign.status != 'REMOVED'" | gads -
echo "SELECT ad_group.id, ad_group.name, ad_group.status,
ad_group.cpc_bid_micros, campaign.name
FROM ad_group
WHERE campaign.status != 'REMOVED'" | gads -
echo "SELECT ad_group_criterion.keyword.text,
ad_group_criterion.keyword.match_type,
ad_group_criterion.status, metrics.clicks,
metrics.impressions, metrics.cost_micros
FROM keyword_view
WHERE campaign.status != 'REMOVED'" | gads -
echo "SELECT search_term_view.search_term, metrics.clicks,
metrics.impressions, metrics.cost_micros, metrics.conversions
FROM search_term_view
WHERE segments.date >= 'YYYY-MM-DD' AND segments.date <= 'YYYY-MM-DD'" | gads -
The script returns enum values as human-readable names (e.g., "ENABLED", "MAXIMIZE_CONVERSIONS"), so you typically don't need to decode numeric codes. For raw numeric references, see references/enums.md.
Key enum values for GAQL WHERE clauses (these use string names):
| Field | WHERE value | Meaning |
|-------|-------------|---------|
| campaign.status | 'ENABLED' | Active campaign |
| campaign.status | 'PAUSED' | Paused campaign |
| campaign.status | 'REMOVED' | Deleted campaign |
conversion_tracking_status and auto_tagging_enabledconversion_action — list actions, check status, primary_for_goal, include_in_conversions_metriccampaign_conversion_goal for the campaign — verify biddable goals match campaign objectiveconversion_goal_campaign_config — check if campaign uses campaign-level or account-level goalsmetrics.conversions (primary only) vs metrics.all_conversions (includes secondary)metrics.cost_micros is in micros (divide by 1,000,000 for currency)'REMOVED'), not numeric codessegments.date requires both >= and <= boundsgoal_config_level = CAMPAIGNtools
Create, edit, and debug SwiftBar menu bar plugins for macOS. ALWAYS use this skill when the user wants to put anything in the macOS menu bar — whether they say "menu bar plugin", "status bar widget", "menu bar item", or just want to show live data, counters, status indicators, or monitoring info in the macOS top bar. Also triggers for SwiftBar, BitBar, xbar by name, editing or debugging existing menu bar plugin scripts (.sh/.py with | parameters and --- separators), or any request to build a script that outputs formatted lines for a menu bar app. This is the go-to skill whenever macOS menu bar customization is involved, even if the user doesn't mention SwiftBar specifically — if they want a script-based menu bar item on macOS, this skill applies.
tools
Use this skill FIRST for any Seravo-hosted WordPress task. Trigger when the user mentions Seravo, production/shadow, deploy, Git-based deploys, SSH, DDEV, DB sync/import/export, or Seravo custom wp-* commands (wp-backup*, wp-purge-cache, wp-list-env). Start by identifying the environment (production vs shadow vs local) and enforce safety: agents must not run destructive or state-changing commands on production. When production write is needed, provide exact copy-paste commands for the user. Use Seravo-specific CLI and workflows instead of generic WP guidance to avoid wrong commands and risky prod changes.
development
Systematic PR fix loop — checks feedback from all channels (conversation, inline, reviews), fixes code, posts fix reports, and loops until no new feedback remains. All operations through provided scripts.
development
Local docs cache for the project. Always check docs/reference/ before fetching externally. Use for init (set up cache), update (refresh docs), lookup (local-first search), and save (persist fetched docs). If external docs are fetched (Context7/web), save a tailored topic file to docs/reference/ and add a verbal pointer in AGENTS.md.