From 7ad9cae5d6efe030d620504ab74892c050741b4d Mon Sep 17 00:00:00 2001 From: Mihir Yavalkar Date: Fri, 3 Jul 2026 14:59:03 -0400 Subject: [PATCH 1/3] Replace Gemini CLI support with Antigravity CLI Google deprecated Gemini CLI in favor of Antigravity CLI (June 2026). This migrates the plugin to the new platform: renamed manifests (plugin.json, ANTIGRAVITY.md, hooks/antigravity-hooks.json), unified PreToolUse hook event, .agents/ settings directory, and updated install commands (agy plugin install). Version bumped to 1.7.0. Co-Authored-By: Claude Opus 4.6 (1M context) --- .claude-plugin/hooks.json | 2 +- .claude-plugin/plugin.json | 2 +- .codex-plugin/hooks.json | 2 +- .codex-plugin/plugin.json | 2 +- .github/PULL_REQUEST_TEMPLATE.md | 2 +- .gitignore | 2 +- AGENTS.md | 14 +++--- GEMINI.md => ANTIGRAVITY.md | 14 +++--- CHANGELOG.md | 22 +++++++++ CONTRIBUTING.md | 12 ++--- README.md | 28 +++++------ agents/spotify-ads-request-builder.md | 12 ++--- hooks/antigravity-hooks.json | 16 +++++++ hooks/check-token.sh | 48 +++++-------------- hooks/gemini-hooks.json | 18 ------- gemini-extension.json => plugin.json | 4 +- skills/ads/SKILL.md | 12 ++--- skills/api-reference/SKILL.md | 12 ++--- .../examples/aggregate-report.md | 2 +- .../examples/full-campaign-flow.md | 2 +- skills/assets/SKILL.md | 12 ++--- skills/build-campaign/SKILL.md | 12 ++--- skills/bulk/SKILL.md | 12 ++--- skills/campaign-strategy/SKILL.md | 6 +-- skills/campaigns/SKILL.md | 12 ++--- skills/clone/SKILL.md | 12 ++--- skills/configure/SKILL.md | 14 +++--- skills/dashboard/SKILL.md | 12 ++--- skills/drafts/SKILL.md | 12 ++--- skills/export/SKILL.md | 12 ++--- skills/monitor/SKILL.md | 12 ++--- skills/report/SKILL.md | 12 ++--- templates/settings-template.md | 2 +- tests/test-scenarios.md | 10 ++-- 34 files changed, 188 insertions(+), 192 deletions(-) rename GEMINI.md => ANTIGRAVITY.md (66%) create mode 100644 hooks/antigravity-hooks.json delete mode 100644 hooks/gemini-hooks.json rename gemini-extension.json => plugin.json (78%) diff --git a/.claude-plugin/hooks.json b/.claude-plugin/hooks.json index 9a6542b..fd4efa1 100644 --- a/.claude-plugin/hooks.json +++ b/.claude-plugin/hooks.json @@ -1,5 +1,5 @@ { - "description": "Auto-refresh expired Spotify OAuth tokens before API calls (Claude Code and Codex; Gemini uses hooks/hooks.json)", + "description": "Auto-refresh expired Spotify OAuth tokens before API calls (Claude Code and Codex; Antigravity uses hooks/antigravity-hooks.json)", "hooks": { "PreToolUse": [ { diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 96151c1..df990bf 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "spotify-ads-api", - "version": "1.6.0", + "version": "1.7.0", "description": "Manage Spotify ad campaigns with natural language. Create campaigns, ad sets, ads, pull reports, and handle OAuth — all through conversation.", "author": { "name": "Alex Murphy" diff --git a/.codex-plugin/hooks.json b/.codex-plugin/hooks.json index 9a6542b..fd4efa1 100644 --- a/.codex-plugin/hooks.json +++ b/.codex-plugin/hooks.json @@ -1,5 +1,5 @@ { - "description": "Auto-refresh expired Spotify OAuth tokens before API calls (Claude Code and Codex; Gemini uses hooks/hooks.json)", + "description": "Auto-refresh expired Spotify OAuth tokens before API calls (Claude Code and Codex; Antigravity uses hooks/antigravity-hooks.json)", "hooks": { "PreToolUse": [ { diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 5f747f0..fa528f5 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "spotify-ads-api", - "version": "1.6.0", + "version": "1.7.0", "description": "Manage Spotify ad campaigns with natural language. Create campaigns, ad sets, ads, pull reports, and handle OAuth — all through conversation.", "author": { "name": "Alex Murphy" diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 6da44de..b04055c 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -4,7 +4,7 @@ #### Checklist -- [ ] Tested against the Spotify Ads API with Codex, Claude Code, or Gemini CLI +- [ ] Tested against the Spotify Ads API with Codex, Claude Code, or Antigravity CLI - [ ] Existing skills still work as expected - [ ] SKILL.md frontmatter is valid (name, description, allowed-tools) - [ ] README or CHANGELOG updated (if user-facing change) diff --git a/.gitignore b/.gitignore index ce21cdb..f84b64b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ .claude/*.local.md .codex/*.local.md -.gemini/*.local.md +.agents/*.local.md .DS_Store *.log internal-v3.yaml diff --git a/AGENTS.md b/AGENTS.md index ed7470e..01f29f3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,7 +4,7 @@ This is the canonical instruction file for agents working in this repository. Co ## What This Is -A Codex, Claude Code, and Gemini CLI plugin package for the Spotify Ads API v3. All source files are markdown — there is no compiled code, no package manager, no build step, no tests. The plugin translates natural language into REST API calls for managing campaigns, ad sets, ads, assets, audiences, and reporting. +A Codex, Claude Code, and Antigravity CLI plugin package for the Spotify Ads API v3. All source files are markdown — there is no compiled code, no package manager, no build step, no tests. The plugin translates natural language into REST API calls for managing campaigns, ad sets, ads, assets, audiences, and reporting. ## Architecture @@ -25,9 +25,9 @@ The plugin follows the agent plugin structure with four component types: - `skills/clone/` — Clone campaigns or ad sets by reading the source hierarchy and recreating entities with modifications - `skills/api-reference/` — Comprehensive API v3 reference documentation with `references/` (endpoints, schemas, enums) and `examples/` (full flows). Activates automatically when the Spotify Ads API is mentioned. - **Agent** (`agents/spotify-ads-request-builder.md`) — A natural language agent that triggers automatically when users describe advertising tasks conversationally. Handles multi-step operations (campaign -> ad set -> ad) by chaining API calls and passing IDs between steps. -- **Hooks** — Per-platform hook configs invoking `hooks/check-token.sh` to automatically refresh expired OAuth tokens before Spotify API calls. `hooks/gemini-hooks.json` contains the Gemini `BeforeTool` event (users copy it to `hooks/hooks.json` after install — see GEMINI.md). `.claude-plugin/hooks.json` and `.codex-plugin/hooks.json` contain the Claude/Codex `PreToolUse` event, declared via the `hooks` field in each platform's `plugin.json`. The Gemini hooks ship under a non-default filename to prevent Claude Code from auto-loading `hooks/hooks.json` (Claude scans the plugin tree and rejects the `BeforeTool` event name). The timeout units differ: seconds for `PreToolUse`, milliseconds for `BeforeTool`. -- **Commands** (`commands/configure.toml`) — A Gemini CLI custom command exposing `/configure` as an explicit entry point to the configure skill. Other skills auto-activate on Gemini via its native Agent Skills support. -- **Settings** (`.codex/spotify-ads-api.local.md`, `.claude/spotify-ads-api.local.md`, or `.gemini/spotify-ads-api.local.md`, with each platform preferring its own file and falling back to the other two) — Per-user local config with YAML frontmatter storing OAuth credentials (access_token, refresh_token, client_id, token_expires_at), ad_account_id, and auto_execute. The client_secret is stored in the macOS Keychain (service: `spotify-ads-api-client-secret`, account: `spotify-ads-api`), not in this file. Template lives in `templates/settings-template.md`. These files are gitignored. +- **Hooks** — Per-platform hook configs invoking `hooks/check-token.sh` to automatically refresh expired OAuth tokens before Spotify API calls. `hooks/antigravity-hooks.json` contains the Antigravity CLI `PreToolUse` event (users copy it to `.agents/hooks.json` after install — see ANTIGRAVITY.md). `.claude-plugin/hooks.json` and `.codex-plugin/hooks.json` contain the Claude/Codex `PreToolUse` event, declared via the `hooks` field in each platform's `plugin.json`. The Antigravity hooks ship under a non-default filename to prevent Claude Code from auto-loading them. +- **Commands** (`commands/configure.toml`) — An Antigravity CLI custom command exposing `/configure` as an explicit entry point to the configure skill. Other skills auto-activate on Antigravity via its native Agent Skills support. +- **Settings** (`.codex/spotify-ads-api.local.md`, `.claude/spotify-ads-api.local.md`, or `.agents/spotify-ads-api.local.md`, with each platform preferring its own file and falling back to the other two) — Per-user local config with YAML frontmatter storing OAuth credentials (access_token, refresh_token, client_id, token_expires_at), ad_account_id, and auto_execute. The client_secret is stored in the macOS Keychain (service: `spotify-ads-api-client-secret`, account: `spotify-ads-api`), not in this file. Template lives in `templates/settings-template.md`. These files are gitignored. ## Marketplace Compatibility @@ -36,9 +36,9 @@ Keep both marketplace files intentional and in sync: - `.agents/plugins/marketplace.json` is the Codex-facing catalog used by `codex plugin marketplace add spotify/ads-agentic-tools`. It must include Codex's `interface.displayName`, `policy.installation`, `policy.authentication`, and `category` metadata. Because this plugin lives at the repository root, use a Git-backed root plugin source (`"source": "url"` with the repository URL) instead of a local `source.path` of `"./"`; Codex treats that local root path as empty and skips the plugin. - `.claude-plugin/marketplace.json` is the Claude Code-compatible catalog. Claude Code requires marketplace metadata at that path and requires a top-level `owner`. To keep the file portable, use the stricter Claude-valid schema: local plugin sources should use the relative string form (`"source": "./"` for this repo-root plugin), and Codex-only marketplace fields such as top-level `interface` or plugin `policy` should not be added to this file. The Claude plugin manifest `.claude-plugin/plugin.json` must also avoid Codex-only manifest fields such as `interface`; keep display metadata in `.codex-plugin/plugin.json` and the Codex marketplace. -- Gemini CLI has no marketplace file. The root `gemini-extension.json` is the Gemini manifest; `gemini extensions install ` reads it directly, and Gemini auto-discovers the `skills/`, `commands/`, and `hooks/` directories at the extension root. Keep its `name`, `version`, and `description` in sync with the other two manifests, and do not add Gemini-only fields (such as `contextFileName`) to the Claude or Codex manifests. +- Antigravity CLI has no marketplace file. The root `plugin.json` is the Antigravity manifest; `agy plugin install ` reads it directly, and Antigravity auto-discovers the `skills/`, `commands/`, and `hooks/` directories at the plugin root. Keep its `name`, `version`, and `description` in sync with the other two manifests, and do not add Antigravity-only fields (such as `contextFileName`) to the Claude or Codex manifests. -When updating marketplace metadata, keep the plugin name, source path, category, description, and user-facing display name aligned wherever each schema supports those fields. The plugin `version` must be bumped in all three manifests together: `.claude-plugin/plugin.json`, `.codex-plugin/plugin.json`, and `gemini-extension.json`. +When updating marketplace metadata, keep the plugin name, source path, category, description, and user-facing display name aligned wherever each schema supports those fields. The plugin `version` must be bumped in all three manifests together: `.claude-plugin/plugin.json`, `.codex-plugin/plugin.json`, and `plugin.json`. ## API Conventions to Know @@ -55,7 +55,7 @@ These non-obvious API quirks were discovered through real testing and are critic - **Report field name** is `fields`, not `report_fields`. - **No DELETE** on campaigns/ad sets/ads — use status changes (ARCHIVED, PAUSED). - **Base URL**: `https://api-partner.spotify.com/ads/v3`. -- **Tracking header**: Every API request must include the SDK tracking header. On Codex, read the `version` from `.codex-plugin/plugin.json` and set `SDK_HEADER="X-Spotify-Ads-Sdk: codex-plugin/$PLUGIN_VERSION"`. On Claude, read `.claude-plugin/plugin.json` and use `claude-code-plugin/$PLUGIN_VERSION`. On Gemini, read `gemini-extension.json` (extension root) and use `gemini-cli-extension/$PLUGIN_VERSION`. Include `-H "$SDK_HEADER"` on all curl commands. +- **Tracking header**: Every API request must include the SDK tracking header. On Codex, read the `version` from `.codex-plugin/plugin.json` and set `SDK_HEADER="X-Spotify-Ads-Sdk: codex-plugin/$PLUGIN_VERSION"`. On Claude, read `.claude-plugin/plugin.json` and use `claude-code-plugin/$PLUGIN_VERSION`. On Antigravity, read `plugin.json` (plugin root) and use `antigravity-cli-plugin/$PLUGIN_VERSION`. Include `-H "$SDK_HEADER"` on all curl commands. - **`entity_status_type` must match `entity_type`** in `aggregate_reports` queries. For example, use `entity_status_type=AD_SET` when `entity_type=AD_SET` — using `entity_status_type=CAMPAIGN` with `entity_type=AD_SET` causes a filter validation error. - **Audience estimates**: The build-campaign and ads skills run `POST /estimates/audience` before creating ad sets to validate targeting. This catches "min audience threshold" errors before they happen. diff --git a/GEMINI.md b/ANTIGRAVITY.md similarity index 66% rename from GEMINI.md rename to ANTIGRAVITY.md index da635fc..ee2d2b9 100644 --- a/GEMINI.md +++ b/ANTIGRAVITY.md @@ -1,10 +1,10 @@ -# Spotify Ads API Extension +# Spotify Ads API Plugin Manage Spotify ad campaigns with natural language. Capabilities are packaged as Agent Skills (campaigns, ads, assets, reporting, dashboards, monitoring, bulk operations, cloning, exports, and a full campaign builder). Describe what you want — e.g. "show my campaign dashboard" or "create a campaign for my podcast" — or run `/skills list` to see everything available. ## Settings -Read and write per-user configuration in `.gemini/spotify-ads-api.local.md` (YAML frontmatter: `access_token`, `refresh_token`, `token_expires_at`, `client_id`, `ad_account_id`, `environment`, `auto_execute`). If that file does not exist, fall back to `.claude/spotify-ads-api.local.md`, then `.codex/spotify-ads-api.local.md`. +Read and write per-user configuration in `.agents/spotify-ads-api.local.md` (YAML frontmatter: `access_token`, `refresh_token`, `token_expires_at`, `client_id`, `ad_account_id`, `environment`, `auto_execute`). If that file does not exist, fall back to `.claude/spotify-ads-api.local.md`, then `.codex/spotify-ads-api.local.md`. Never commit these files. The `client_secret` is stored in the macOS Keychain (service: `spotify-ads-api-client-secret`, account: `spotify-ads-api`), not in the settings file. @@ -14,24 +14,24 @@ Run `/configure` (or ask to "configure Spotify Ads API credentials") to set up O ## SDK Tracking Header -Every Spotify Ads API request must include the SDK tracking header. Read the `version` from this extension's `gemini-extension.json` and set: +Every Spotify Ads API request must include the SDK tracking header. Read the `version` from this plugin's `plugin.json` and set: ```bash -SDK_HEADER="X-Spotify-Ads-Sdk: gemini-cli-extension/$PLUGIN_VERSION" +SDK_HEADER="X-Spotify-Ads-Sdk: antigravity-cli-plugin/$PLUGIN_VERSION" ``` Include `-H "$SDK_HEADER"` on all curl commands to `api-partner.spotify.com`. ## Token Expiry -A BeforeTool hook refreshes expired OAuth tokens automatically before API calls. Gemini CLI auto-discovers hooks at the fixed path `hooks/hooks.json`, but the file ships as `hooks/gemini-hooks.json` to avoid conflicts with Claude Code's hook loader. After installing the extension, activate the hook once: +A PreToolUse hook refreshes expired OAuth tokens automatically before API calls. Antigravity CLI auto-discovers hooks at the fixed path `.agents/hooks.json`, but the file ships as `hooks/antigravity-hooks.json` to avoid conflicts with Claude Code's hook loader. After installing the plugin, activate the hook once: ```bash -cp hooks/gemini-hooks.json hooks/hooks.json +cp hooks/antigravity-hooks.json .agents/hooks.json ``` If a request still returns 401, refresh the token per the configure skill's instructions or re-run `/configure`. ## Contributors -If you are editing this repository itself (not using the extension), `AGENTS.md` is the canonical instruction file — read it before making changes. +If you are editing this repository itself (not using the plugin), `AGENTS.md` is the canonical instruction file — read it before making changes. diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ce9da1..ec72218 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,27 @@ # Changelog +## [1.7.0] - 2026-07-03 + +### Added +- Antigravity CLI plugin support replacing Gemini CLI extension: root `plugin.json` manifest, `ANTIGRAVITY.md` context file, and `hooks/antigravity-hooks.json` hook config using `PreToolUse` event +- `.agents/spotify-ads-api.local.md` settings path for Antigravity CLI (gitignored) + +### Changed +- `gemini-extension.json` → `plugin.json` (Antigravity manifest filename) +- `GEMINI.md` → `ANTIGRAVITY.md` (Antigravity context file) +- `hooks/gemini-hooks.json` → `hooks/antigravity-hooks.json`; hook event migrated from `BeforeTool` to `PreToolUse` and tool matcher from `run_shell_command` to `run_command` +- Settings directory changed from `.gemini/` to `.agents/` across all skills, agent, and hook +- SDK tracking header product changed from `gemini-cli-extension` to `antigravity-cli-plugin` +- Plugin version synced across `.claude-plugin/plugin.json`, `.codex-plugin/plugin.json`, and `plugin.json`, all bumped to 1.7.0 +- `check-token.sh` updated to detect Antigravity CLI platform and emit compatible hook output +- All documentation, skills, and agent updated to reference Antigravity CLI + +### Removed +- Gemini CLI extension support (deprecated by Google in favor of Antigravity CLI) +- `gemini-extension.json` manifest +- `GEMINI.md` context file +- `hooks/gemini-hooks.json` hook config + ## [1.5.0] - 2026-06-10 ### Added diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6839c29..412eb42 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,7 +8,7 @@ Look for issues labeled [`good first issue`](https://github.com/spotify/ads-agen ## Building the Project -This is a Codex, Claude Code, and Gemini CLI plugin package made mostly of markdown files. There is no build step, no package manager, and no compiled code. +This is a Codex, Claude Code, and Antigravity CLI plugin package made mostly of markdown files. There is no build step, no package manager, and no compiled code. To run the plugin locally: @@ -29,16 +29,16 @@ To run the plugin locally: claude --plugin-dir /path/to/ads-agentic-tools ``` - For Gemini CLI: + For Antigravity CLI: ```bash - gemini extensions link /path/to/ads-agentic-tools + agy plugin link /path/to/ads-agentic-tools ``` 3. Configure credentials: ``` /spotify-ads-api:configure ``` - (`/configure` on Gemini CLI) + (`/configure` on Antigravity) ## Workflow @@ -57,10 +57,10 @@ We follow the [GitHub Flow Workflow](https://guides.github.com/introduction/flow There is no automated test suite. Before submitting a pull request: -- Verify your changes work against the Spotify Ads API on at least one supported platform (Codex, Claude Code, or Gemini CLI) +- Verify your changes work against the Spotify Ads API on at least one supported platform (Codex, Claude Code, or Antigravity CLI) - Confirm that existing skills (`/spotify-ads-api:campaigns`, `/spotify-ads-api:ads`, etc.) still function correctly - If adding a new skill, include a `SKILL.md` following the patterns in existing skill directories -- If touching `hooks/`, note there are three per-platform hook configs: `hooks/gemini-hooks.json` (Gemini, `BeforeTool`), `.claude-plugin/hooks.json` (Claude), and `.codex-plugin/hooks.json` (Codex) — all calling `check-token.sh`. Test the token-refresh hook on all three platforms +- If touching `hooks/`, note there are three per-platform hook configs: `hooks/antigravity-hooks.json` (Antigravity, `PreToolUse`), `.claude-plugin/hooks.json` (Claude), and `.codex-plugin/hooks.json` (Codex) — all calling `check-token.sh`. Test the token-refresh hook on all three platforms ## Style diff --git a/README.md b/README.md index b11f7c3..cc736c0 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # Spotify Ads Agentic Tools -A Codex, Claude Code, and Gemini CLI plugin package that lets you manage Spotify advertising campaigns through natural language. Create campaigns, target audiences, launch ads, and pull performance reports — all by describing what you want in plain English. +A Codex, Claude Code, and Antigravity CLI plugin package that lets you manage Spotify advertising campaigns through natural language. Create campaigns, target audiences, launch ads, and pull performance reports — all by describing what you want in plain English. Check out our post on the [Spotify Engineering Blog](https://engineering.atspotify.com/2026/5/spotify-ads-api-claude-plugins). ## Prerequisites -- Codex, [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code), or [Gemini CLI](https://geminicli.com/) +- Codex, [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code), or [Antigravity CLI](https://antigravity.google/) - A [Spotify Developer](https://developer.spotify.com/) account with an ads-enabled app - A Spotify Ads ad account ID - Python 3.8+ (for automated OAuth flow; optional — manual flow available as fallback) @@ -31,13 +31,13 @@ Restart Codex after adding the marketplace. Then open the plugin directory in th Use `codex plugin marketplace upgrade` later to refresh installed marketplace sources. -### Gemini CLI +### Antigravity CLI ```bash -gemini extensions install https://github.com/spotify/ads-agentic-tools +agy plugin install https://github.com/spotify/ads-agentic-tools ``` -Restart Gemini CLI, then verify with `/extensions`. On Gemini, skills activate automatically from natural language (or browse them with `/skills list`); run `/configure` for first-time setup instead of `/spotify-ads-api:configure`. Note: automatic OAuth token refresh uses the macOS Keychain, so auto-refresh is macOS-only. +Restart Antigravity CLI, then verify with `/plugins`. On Antigravity, skills activate automatically from natural language (or browse them with `/skills list`); run `/configure` for first-time setup instead of `/spotify-ads-api:configure`. Note: automatic OAuth token refresh uses the macOS Keychain, so auto-refresh is macOS-only. ## Install from source @@ -66,14 +66,14 @@ Use a source checkout for local development or testing unreleased changes. alias claude-ads='claude --plugin-dir /path/to/ads-agentic-tools' ``` -4. For Gemini CLI, link the checkout as a local extension: +4. For Antigravity CLI, link the checkout as a local plugin: ```bash - gemini extensions link "$(pwd)" + agy plugin link "$(pwd)" ``` - The link is a symlink, so source changes are picked up on the next Gemini CLI restart. + The link is a symlink, so source changes are picked up on the next Antigravity CLI restart. -The repository includes platform-specific marketplace metadata: `.agents/plugins/marketplace.json` for Codex and `.claude-plugin/marketplace.json` for Claude Code. Gemini CLI has no marketplace file — it installs directly from the repository using the root `gemini-extension.json` manifest. Keep all three manifests in sync when changing plugin metadata. +The repository includes platform-specific marketplace metadata: `.agents/plugins/marketplace.json` for Codex and `.claude-plugin/marketplace.json` for Claude Code. Antigravity CLI has no marketplace file — it installs directly from the repository using the root `plugin.json` manifest. Keep all three manifests in sync when changing plugin metadata. ## Configure @@ -87,7 +87,7 @@ The repository includes platform-specific marketplace metadata: `.agents/plugins ``` /spotify-ads-api:configure ``` - (On Gemini CLI, run `/configure` instead — the skill names below all apply, but the slash-command prefix is Claude Code/Codex syntax.) + (On Antigravity CLI, run `/configure` instead — the skill names below all apply, but the slash-command prefix is Claude Code/Codex syntax.) This opens your browser for Spotify authorization, then saves your tokens locally with automatic refresh. 3. Create your first campaign: @@ -110,7 +110,7 @@ Run `/spotify-ads-api:configure token `. Accepts a pre-obtained acce ## Available Skills -Skill names below use Claude Code/Codex slash-command syntax. On Gemini CLI, the same skills activate automatically from natural language (browse them with `/skills list`), and setup is `/configure`. +Skill names below use Claude Code/Codex slash-command syntax. On Antigravity CLI, the same skills activate automatically from natural language (browse them with `/skills list`), and setup is `/configure`. | Skill | Description | |-------|-------------| @@ -148,7 +148,7 @@ The plugin includes an agent that interprets natural language requests automatic ## Configuration Reference -Settings are stored in `.codex/spotify-ads-api.local.md` on Codex, `.claude/spotify-ads-api.local.md` on Claude, and `.gemini/spotify-ads-api.local.md` on Gemini. Each platform falls back to the other settings files if its preferred file does not exist. All three paths are gitignored. +Settings are stored in `.codex/spotify-ads-api.local.md` on Codex, `.claude/spotify-ads-api.local.md` on Claude, and `.agents/spotify-ads-api.local.md` on Antigravity. Each platform falls back to the other settings files if its preferred file does not exist. All three paths are gitignored. | Field | Description | Default | |-------|-------------|---------| @@ -178,8 +178,8 @@ Your targeting is too narrow for the selected ad format. Try broadening the age **"Asset stuck in PROCESSING"** Large files may take longer to transcode. Check status with `/spotify-ads-api:assets get `. If status is REJECTED, the file may not meet format requirements. -**Skill not activating on Gemini CLI** -Run `/skills list` to confirm the extension's skills loaded, and `/extensions` to confirm the extension is enabled. Restart Gemini CLI after installing or linking. +**Skill not activating on Antigravity CLI** +Run `/skills list` to confirm the plugin's skills loaded, and `/plugins` to confirm the plugin is enabled. Restart Antigravity CLI after installing or linking. ## License diff --git a/agents/spotify-ads-request-builder.md b/agents/spotify-ads-request-builder.md index 2cdadcc..a3315cd 100644 --- a/agents/spotify-ads-request-builder.md +++ b/agents/spotify-ads-request-builder.md @@ -53,12 +53,12 @@ You are a Spotify Ads API specialist that translates natural language advertisin **Startup Process:** 1. Read `access_token`, `ad_account_id`, and `auto_execute` from the active platform settings file: - - Codex: prefer `.codex/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.gemini/spotify-ads-api.local.md`. - - Claude: prefer `.claude/spotify-ads-api.local.md`, then fall back to `.codex/spotify-ads-api.local.md`, then `.gemini/spotify-ads-api.local.md`. - - Gemini: prefer `.gemini/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.codex/spotify-ads-api.local.md`. -2. If no settings file exists, inform the user to run the configure skill first (`/spotify-ads-api:configure` on Claude/Codex, `/configure` on Gemini) and stop + - Codex: prefer `.codex/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.agents/spotify-ads-api.local.md`. + - Claude: prefer `.claude/spotify-ads-api.local.md`, then fall back to `.codex/spotify-ads-api.local.md`, then `.agents/spotify-ads-api.local.md`. + - Antigravity: prefer `.agents/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.codex/spotify-ads-api.local.md`. +2. If no settings file exists, inform the user to run the configure skill first (`/spotify-ads-api:configure` on Claude/Codex, `/configure` on Antigravity) and stop 3. Base URL: `https://api-partner.spotify.com/ads/v3` -4. Read the active platform manifest for the plugin `version`: `.codex-plugin/plugin.json` on Codex, `.claude-plugin/plugin.json` on Claude, or `gemini-extension.json` (extension root) on Gemini. Set `SDK_PRODUCT` to `codex-plugin` on Codex, `claude-code-plugin` on Claude, or `gemini-cli-extension` on Gemini, then set `SDK_HEADER="X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION"`. Include `-H "$SDK_HEADER"` on all API requests +4. Read the active platform manifest for the plugin `version`: `.codex-plugin/plugin.json` on Codex, `.claude-plugin/plugin.json` on Claude, or `plugin.json` (plugin root) on Antigravity. Set `SDK_PRODUCT` to `codex-plugin` on Codex, `claude-code-plugin` on Claude, or `antigravity-cli-plugin` on Antigravity, then set `SDK_HEADER="X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION"`. Include `-H "$SDK_HEADER"` on all API requests **Request Building Process:** 1. Analyze the user's natural language request @@ -177,7 +177,7 @@ curl -s -w "\nHTTP_STATUS:%{http_code}" -H "Authorization: Bearer $TOKEN" \ Always check the `HTTP_STATUS:` line first before interpreting the response. **Error Handling:** -- If the API returns a **401 Unauthorized**, the token is likely expired. If the plugin has OAuth credentials configured (refresh_token, client_id in settings, client_secret in keychain), the pre-tool hook should auto-refresh. If auto-refresh didn't occur, suggest running the configure skill (`/spotify-ads-api:configure` on Claude/Codex, `/configure` on Gemini) to re-authenticate. +- If the API returns a **401 Unauthorized**, the token is likely expired. If the plugin has OAuth credentials configured (refresh_token, client_id in settings, client_secret in keychain), the pre-tool hook should auto-refresh. If auto-refresh didn't occur, suggest running the configure skill (`/spotify-ads-api:configure` on Claude/Codex, `/configure` on Antigravity) to re-authenticate. - If the API returns other errors, read the error message and explain what went wrong in plain language - Suggest fixes for common errors (missing fields, budget too low, targeting too narrow, etc.) - Never retry automatically on 4xx errors — explain the issue to the user diff --git a/hooks/antigravity-hooks.json b/hooks/antigravity-hooks.json new file mode 100644 index 0000000..83597de --- /dev/null +++ b/hooks/antigravity-hooks.json @@ -0,0 +1,16 @@ +{ + "spotify-ads-token-refresh": { + "PreToolUse": [ + { + "matcher": "run_command", + "hooks": [ + { + "type": "command", + "command": "bash \"${pluginPath}${/}hooks${/}check-token.sh\"", + "timeout": 30 + } + ] + } + ] + } +} diff --git a/hooks/check-token.sh b/hooks/check-token.sh index 6b0570c..4b719a6 100755 --- a/hooks/check-token.sh +++ b/hooks/check-token.sh @@ -1,7 +1,7 @@ #!/bin/bash set -uo pipefail -# Spotify Ads API pre-tool hook (PreToolUse on Claude/Codex, BeforeTool on Gemini) +# Spotify Ads API pre-tool hook (PreToolUse on Claude/Codex/Antigravity) # # Auto-refreshes expired OAuth tokens before API calls @@ -27,11 +27,9 @@ if ! command -v jq &>/dev/null; then exit 0 fi -# Detect platform: Gemini fires this as a BeforeTool hook (and sets no -# *_PROJECT_DIR env vars); Claude/Codex fire it as PreToolUse -hook_event=$(printf '%s' "$input" | jq -r '.hook_event_name // ""') -if [ "$hook_event" = "BeforeTool" ]; then - PLATFORM="gemini" +# Detect platform from env vars +if [ -n "${ANTIGRAVITY_PROJECT_DIR:-}" ]; then + PLATFORM="antigravity" elif [ -n "${CODEX_PROJECT_DIR:-}" ]; then PLATFORM="codex" elif [ -n "${CLAUDE_PROJECT_DIR:-}" ]; then @@ -40,21 +38,15 @@ else PLATFORM="codex" fi -PROJECT_DIR="${CODEX_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-$PWD}}" -if [ "$PLATFORM" = "gemini" ]; then - stdin_cwd=$(printf '%s' "$input" | jq -r '.cwd // ""') - if [ -n "$stdin_cwd" ]; then - PROJECT_DIR="$stdin_cwd" - fi -fi +PROJECT_DIR="${ANTIGRAVITY_PROJECT_DIR:-${CODEX_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-$PWD}}}" find_settings_file() { local order dir candidate case "$PLATFORM" in - gemini) order=".gemini .claude .codex" ;; - claude) order=".claude .codex .gemini" ;; - *) order=".codex .claude .gemini" ;; + antigravity) order=".agents .claude .codex" ;; + claude) order=".claude .codex .agents" ;; + *) order=".codex .claude .agents" ;; esac for dir in $order; do @@ -108,7 +100,7 @@ if [ -n "$SETTINGS_FILE" ] && [ -f "$SETTINGS_FILE" ]; then if [ "$needs_refresh" = true ]; then if [ -z "$refresh_token" ] || [ -z "$client_id" ] || [ -z "$client_secret" ]; then - system_message="Spotify API token may be expired but no refresh credentials are configured. Run the configure skill (/spotify-ads-api:configure on Claude/Codex, /configure on Gemini) to set up OAuth." + system_message="Spotify API token may be expired but no refresh credentials are configured. Run the configure skill (/spotify-ads-api:configure on Claude/Codex, /configure on Antigravity) to set up OAuth." else REFRESH_SCRIPT="${PLUGIN_ROOT}/skills/configure/scripts/refresh-token.py" if refresh_result=$(python3 "$REFRESH_SCRIPT" \ @@ -142,32 +134,16 @@ if [ -n "$SETTINGS_FILE" ] && [ -f "$SETTINGS_FILE" ]; then system_message="Spotify API token was expired and has been refreshed automatically." fi else - system_message="Failed to refresh Spotify API token. Run the configure skill (/spotify-ads-api:configure on Claude/Codex, /configure on Gemini) to re-authenticate." + system_message="Failed to refresh Spotify API token. Run the configure skill (/spotify-ads-api:configure on Claude/Codex, /configure on Antigravity) to re-authenticate." fi fi fi fi # --- Emit output --- -# Gemini merges hookSpecificOutput.tool_input into the model's tool args; -# Claude/Codex expect permissionDecision/updatedInput instead. +# All platforms (Claude, Codex, Antigravity) use permissionDecision/updatedInput. if [[ "$modified_command" != "$command" ]]; then - if [ "$PLATFORM" = "gemini" ]; then - if [ -n "$system_message" ]; then - jq -n --arg cmd "$modified_command" --arg msg "$system_message" '{ - "hookSpecificOutput": { - "tool_input": {"command": $cmd} - }, - "systemMessage": $msg - }' 2>/dev/null - else - jq -n --arg cmd "$modified_command" '{ - "hookSpecificOutput": { - "tool_input": {"command": $cmd} - } - }' 2>/dev/null - fi - elif [ -n "$system_message" ]; then + if [ -n "$system_message" ]; then jq -n --arg cmd "$modified_command" --arg msg "$system_message" '{ "hookSpecificOutput": { "permissionDecision": "allow", diff --git a/hooks/gemini-hooks.json b/hooks/gemini-hooks.json deleted file mode 100644 index 2f01da4..0000000 --- a/hooks/gemini-hooks.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "description": "Auto-refresh expired Spotify OAuth tokens before API calls (Gemini CLI; Claude Code and Codex use hooks/claude-hooks.json)", - "hooks": { - "BeforeTool": [ - { - "matcher": "run_shell_command", - "hooks": [ - { - "type": "command", - "name": "spotify-ads-token-refresh", - "command": "bash \"${extensionPath}${/}hooks${/}check-token.sh\"", - "timeout": 30000 - } - ] - } - ] - } -} diff --git a/gemini-extension.json b/plugin.json similarity index 78% rename from gemini-extension.json rename to plugin.json index 3482201..db06dec 100644 --- a/gemini-extension.json +++ b/plugin.json @@ -1,6 +1,6 @@ { "name": "spotify-ads-api", - "version": "1.6.0", + "version": "1.7.0", "description": "Manage Spotify ad campaigns with natural language. Create campaigns, ad sets, and ads, pull reports, upload creative assets, and handle OAuth — all through conversation.", - "contextFileName": "GEMINI.md" + "contextFileName": "ANTIGRAVITY.md" } diff --git a/skills/ads/SKILL.md b/skills/ads/SKILL.md index c523877..09e07f4 100644 --- a/skills/ads/SKILL.md +++ b/skills/ads/SKILL.md @@ -12,13 +12,13 @@ Manage ad sets and ads via the Spotify Ads API. Read settings from the active pl ## Setup 1. Read `access_token`, `ad_account_id`, and `auto_execute` from the active platform settings file: - - Codex: prefer `.codex/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.gemini/spotify-ads-api.local.md`. - - Claude: prefer `.claude/spotify-ads-api.local.md`, then fall back to `.codex/spotify-ads-api.local.md`, then `.gemini/spotify-ads-api.local.md`. - - Gemini: prefer `.gemini/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.codex/spotify-ads-api.local.md`. + - Codex: prefer `.codex/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.agents/spotify-ads-api.local.md`. + - Claude: prefer `.claude/spotify-ads-api.local.md`, then fall back to `.codex/spotify-ads-api.local.md`, then `.agents/spotify-ads-api.local.md`. + - Antigravity: prefer `.agents/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.codex/spotify-ads-api.local.md`. 2. Base URL: `https://api-partner.spotify.com/ads/v3` -3. If no settings file exists, instruct the user to run the configure skill first (`/spotify-ads-api:configure` on Claude/Codex, `/configure` on Gemini). -4. Read the active platform manifest for the plugin `version`: `.codex-plugin/plugin.json` on Codex, `.claude-plugin/plugin.json` on Claude, or `gemini-extension.json` (extension root) on Gemini. -5. Set `SDK_PRODUCT` to `codex-plugin` on Codex, `claude-code-plugin` on Claude, or `gemini-cli-extension` on Gemini. Set `SDK_HEADER="X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION"` and include `-H "$SDK_HEADER"` on all API requests. +3. If no settings file exists, instruct the user to run the configure skill first (`/spotify-ads-api:configure` on Claude/Codex, `/configure` on Antigravity). +4. Read the active platform manifest for the plugin `version`: `.codex-plugin/plugin.json` on Codex, `.claude-plugin/plugin.json` on Claude, or `plugin.json` (plugin root) on Antigravity. +5. Set `SDK_PRODUCT` to `codex-plugin` on Codex, `claude-code-plugin` on Claude, or `antigravity-cli-plugin` on Antigravity. Set `SDK_HEADER="X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION"` and include `-H "$SDK_HEADER"` on all API requests. ## Parsing Arguments diff --git a/skills/api-reference/SKILL.md b/skills/api-reference/SKILL.md index c6c1ab8..67cd872 100644 --- a/skills/api-reference/SKILL.md +++ b/skills/api-reference/SKILL.md @@ -25,11 +25,11 @@ X-Spotify-Ads-Sdk: / Use the active platform SDK product and plugin version: - Codex: read `.codex-plugin/plugin.json`, set `SDK_PRODUCT="codex-plugin"`. - Claude: read `.claude-plugin/plugin.json`, set `SDK_PRODUCT="claude-code-plugin"`. -- Gemini: read `gemini-extension.json` (extension root), set `SDK_PRODUCT="gemini-cli-extension"`. +- Antigravity: read `plugin.json` (plugin root), set `SDK_PRODUCT="antigravity-cli-plugin"`. Set `SDK_HEADER="X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION"` and include `-H "$SDK_HEADER"` on all API requests. -To set up authentication, run the configure skill (`/spotify-ads-api:configure` on Claude/Codex, `/configure` on Gemini), which supports OAuth 2.0 with automatic token refresh, manual OAuth, or direct token input. +To set up authentication, run the configure skill (`/spotify-ads-api:configure` on Claude/Codex, `/configure` on Antigravity), which supports OAuth 2.0 with automatic token refresh, manual OAuth, or direct token input. ## Resource Hierarchy @@ -145,16 +145,16 @@ create drafts → edit → validate → publish. ## Making API Calls Read the user's plugin settings from the active platform settings file created by the configure skill: -- Codex: prefer `.codex/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.gemini/spotify-ads-api.local.md`. -- Claude: prefer `.claude/spotify-ads-api.local.md`, then fall back to `.codex/spotify-ads-api.local.md`, then `.gemini/spotify-ads-api.local.md`. -- Gemini: prefer `.gemini/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.codex/spotify-ads-api.local.md`. +- Codex: prefer `.codex/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.agents/spotify-ads-api.local.md`. +- Claude: prefer `.claude/spotify-ads-api.local.md`, then fall back to `.codex/spotify-ads-api.local.md`, then `.agents/spotify-ads-api.local.md`. +- Antigravity: prefer `.agents/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.codex/spotify-ads-api.local.md`. Use the settings file to get: - `access_token` — Bearer token for authentication - `ad_account_id` — Default ad account ID - `auto_execute` — Whether to execute API calls automatically or present them first (default: false) -If the settings file does not exist, instruct the user to run the configure skill first (`/spotify-ads-api:configure` on Claude/Codex, `/configure` on Gemini). +If the settings file does not exist, instruct the user to run the configure skill first (`/spotify-ads-api:configure` on Claude/Codex, `/configure` on Antigravity). Construct curl commands using the appropriate base URL. Example: diff --git a/skills/api-reference/examples/aggregate-report.md b/skills/api-reference/examples/aggregate-report.md index 2380a29..ffda5b8 100644 --- a/skills/api-reference/examples/aggregate-report.md +++ b/skills/api-reference/examples/aggregate-report.md @@ -1,6 +1,6 @@ # Example: Pulling an Aggregate Report -**Note:** All curl examples below assume `SDK_HEADER="X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION"`, where `SDK_PRODUCT` is `codex-plugin` on Codex, `claude-code-plugin` on Claude, and `gemini-cli-extension` on Gemini. +**Note:** All curl examples below assume `SDK_HEADER="X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION"`, where `SDK_PRODUCT` is `codex-plugin` on Codex, `claude-code-plugin` on Claude, and `antigravity-cli-plugin` on Antigravity. This example shows how to pull aggregated campaign performance metrics. diff --git a/skills/api-reference/examples/full-campaign-flow.md b/skills/api-reference/examples/full-campaign-flow.md index 7753c3d..4ada877 100644 --- a/skills/api-reference/examples/full-campaign-flow.md +++ b/skills/api-reference/examples/full-campaign-flow.md @@ -1,6 +1,6 @@ # Example: Full Campaign Setup Flow -**Note:** All curl examples below assume `SDK_HEADER="X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION"`, where `SDK_PRODUCT` is `codex-plugin` on Codex, `claude-code-plugin` on Claude, and `gemini-cli-extension` on Gemini. +**Note:** All curl examples below assume `SDK_HEADER="X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION"`, where `SDK_PRODUCT` is `codex-plugin` on Codex, `claude-code-plugin` on Claude, and `antigravity-cli-plugin` on Antigravity. This example shows the complete sequence of API calls to create a campaign, ad set, and ad. diff --git a/skills/assets/SKILL.md b/skills/assets/SKILL.md index eee86c1..7decaf7 100644 --- a/skills/assets/SKILL.md +++ b/skills/assets/SKILL.md @@ -12,13 +12,13 @@ Upload, list, retrieve, and archive creative assets (audio, video, images) for u ## Setup 1. Read `access_token`, `ad_account_id`, and `auto_execute` from the active platform settings file: - - Codex: prefer `.codex/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.gemini/spotify-ads-api.local.md`. - - Claude: prefer `.claude/spotify-ads-api.local.md`, then fall back to `.codex/spotify-ads-api.local.md`, then `.gemini/spotify-ads-api.local.md`. - - Gemini: prefer `.gemini/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.codex/spotify-ads-api.local.md`. + - Codex: prefer `.codex/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.agents/spotify-ads-api.local.md`. + - Claude: prefer `.claude/spotify-ads-api.local.md`, then fall back to `.codex/spotify-ads-api.local.md`, then `.agents/spotify-ads-api.local.md`. + - Antigravity: prefer `.agents/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.codex/spotify-ads-api.local.md`. 2. Base URL: `https://api-partner.spotify.com/ads/v3` -3. If no settings file exists, instruct the user to run the configure skill first (`/spotify-ads-api:configure` on Claude/Codex, `/configure` on Gemini). -4. Read the active platform manifest for the plugin `version`: `.codex-plugin/plugin.json` on Codex, `.claude-plugin/plugin.json` on Claude, or `gemini-extension.json` (extension root) on Gemini. -5. Set `SDK_PRODUCT` to `codex-plugin` on Codex, `claude-code-plugin` on Claude, or `gemini-cli-extension` on Gemini. Set `SDK_HEADER="X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION"` and include `-H "$SDK_HEADER"` on all API requests. +3. If no settings file exists, instruct the user to run the configure skill first (`/spotify-ads-api:configure` on Claude/Codex, `/configure` on Antigravity). +4. Read the active platform manifest for the plugin `version`: `.codex-plugin/plugin.json` on Codex, `.claude-plugin/plugin.json` on Claude, or `plugin.json` (plugin root) on Antigravity. +5. Set `SDK_PRODUCT` to `codex-plugin` on Codex, `claude-code-plugin` on Claude, or `antigravity-cli-plugin` on Antigravity. Set `SDK_HEADER="X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION"` and include `-H "$SDK_HEADER"` on all API requests. ## Parsing Arguments diff --git a/skills/build-campaign/SKILL.md b/skills/build-campaign/SKILL.md index d612e34..da28610 100644 --- a/skills/build-campaign/SKILL.md +++ b/skills/build-campaign/SKILL.md @@ -28,13 +28,13 @@ Only use the direct creation flow below if the user explicitly asks to skip draf ## Setup 1. Read `access_token`, `ad_account_id`, and `auto_execute` from the active platform settings file: - - Codex: prefer `.codex/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.gemini/spotify-ads-api.local.md`. - - Claude: prefer `.claude/spotify-ads-api.local.md`, then fall back to `.codex/spotify-ads-api.local.md`, then `.gemini/spotify-ads-api.local.md`. - - Gemini: prefer `.gemini/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.codex/spotify-ads-api.local.md`. + - Codex: prefer `.codex/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.agents/spotify-ads-api.local.md`. + - Claude: prefer `.claude/spotify-ads-api.local.md`, then fall back to `.codex/spotify-ads-api.local.md`, then `.agents/spotify-ads-api.local.md`. + - Antigravity: prefer `.agents/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.codex/spotify-ads-api.local.md`. 2. Base URL: `https://api-partner.spotify.com/ads/v3` -3. If no settings file exists, instruct the user to run the configure skill first (`/spotify-ads-api:configure` on Claude/Codex, `/configure` on Gemini). -4. Read the active platform manifest for the plugin `version`: `.codex-plugin/plugin.json` on Codex, `.claude-plugin/plugin.json` on Claude, or `gemini-extension.json` (extension root) on Gemini. -5. Set `SDK_PRODUCT` to `codex-plugin` on Codex, `claude-code-plugin` on Claude, or `gemini-cli-extension` on Gemini. Set `SDK_HEADER="X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION"` and include `-H "$SDK_HEADER"` on all API requests. +3. If no settings file exists, instruct the user to run the configure skill first (`/spotify-ads-api:configure` on Claude/Codex, `/configure` on Antigravity). +4. Read the active platform manifest for the plugin `version`: `.codex-plugin/plugin.json` on Codex, `.claude-plugin/plugin.json` on Claude, or `plugin.json` (plugin root) on Antigravity. +5. Set `SDK_PRODUCT` to `codex-plugin` on Codex, `claude-code-plugin` on Claude, or `antigravity-cli-plugin` on Antigravity. Set `SDK_HEADER="X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION"` and include `-H "$SDK_HEADER"` on all API requests. ## Step 1: Parse the Campaign Description diff --git a/skills/bulk/SKILL.md b/skills/bulk/SKILL.md index a8c003f..428ad71 100644 --- a/skills/bulk/SKILL.md +++ b/skills/bulk/SKILL.md @@ -12,13 +12,13 @@ Apply batch changes to multiple entities in a single workflow. All operations fo ## Setup 1. Read `access_token`, `ad_account_id`, and `auto_execute` from the active platform settings file: - - Codex: prefer `.codex/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.gemini/spotify-ads-api.local.md`. - - Claude: prefer `.claude/spotify-ads-api.local.md`, then fall back to `.codex/spotify-ads-api.local.md`, then `.gemini/spotify-ads-api.local.md`. - - Gemini: prefer `.gemini/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.codex/spotify-ads-api.local.md`. + - Codex: prefer `.codex/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.agents/spotify-ads-api.local.md`. + - Claude: prefer `.claude/spotify-ads-api.local.md`, then fall back to `.codex/spotify-ads-api.local.md`, then `.agents/spotify-ads-api.local.md`. + - Antigravity: prefer `.agents/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.codex/spotify-ads-api.local.md`. 2. Base URL: `https://api-partner.spotify.com/ads/v3` -3. If no settings file exists, instruct the user to run the configure skill first (`/spotify-ads-api:configure` on Claude/Codex, `/configure` on Gemini). -4. Read the active platform manifest for the plugin `version`: `.codex-plugin/plugin.json` on Codex, `.claude-plugin/plugin.json` on Claude, or `gemini-extension.json` (extension root) on Gemini. -5. Set `SDK_PRODUCT` to `codex-plugin` on Codex, `claude-code-plugin` on Claude, or `gemini-cli-extension` on Gemini. Set `SDK_HEADER="X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION"` and include `-H "$SDK_HEADER"` on all API requests. +3. If no settings file exists, instruct the user to run the configure skill first (`/spotify-ads-api:configure` on Claude/Codex, `/configure` on Antigravity). +4. Read the active platform manifest for the plugin `version`: `.codex-plugin/plugin.json` on Codex, `.claude-plugin/plugin.json` on Claude, or `plugin.json` (plugin root) on Antigravity. +5. Set `SDK_PRODUCT` to `codex-plugin` on Codex, `claude-code-plugin` on Claude, or `antigravity-cli-plugin` on Antigravity. Set `SDK_HEADER="X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION"` and include `-H "$SDK_HEADER"` on all API requests. ## Parsing Arguments diff --git a/skills/campaign-strategy/SKILL.md b/skills/campaign-strategy/SKILL.md index de9303d..bd9dbe3 100644 --- a/skills/campaign-strategy/SKILL.md +++ b/skills/campaign-strategy/SKILL.md @@ -47,9 +47,9 @@ If budget, dates, or market are missing, make a conservative recommendation and 5. Apply execution conventions. - Read settings from the active platform settings file: - - Codex: prefer `.codex/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.gemini/spotify-ads-api.local.md`. - - Claude: prefer `.claude/spotify-ads-api.local.md`, then fall back to `.codex/spotify-ads-api.local.md`, then `.gemini/spotify-ads-api.local.md`. - - Gemini: prefer `.gemini/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.codex/spotify-ads-api.local.md`. + - Codex: prefer `.codex/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.agents/spotify-ads-api.local.md`. + - Claude: prefer `.claude/spotify-ads-api.local.md`, then fall back to `.codex/spotify-ads-api.local.md`, then `.agents/spotify-ads-api.local.md`. + - Antigravity: prefer `.agents/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.codex/spotify-ads-api.local.md`. - Base URL: `https://api-partner.spotify.com/ads/v3`. - Include `Authorization: Bearer $TOKEN` and `X-Spotify-Ads-Sdk: /` on API calls. - Include `-w "\nHTTP_STATUS:%{http_code}"` on all API curl commands except file uploads. diff --git a/skills/campaigns/SKILL.md b/skills/campaigns/SKILL.md index 8818b7d..f106e37 100644 --- a/skills/campaigns/SKILL.md +++ b/skills/campaigns/SKILL.md @@ -12,13 +12,13 @@ Manage campaigns via the Spotify Ads API. Read settings from the active platform ## Setup 1. Read `access_token`, `ad_account_id`, and `auto_execute` from the active platform settings file: - - Codex: prefer `.codex/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.gemini/spotify-ads-api.local.md`. - - Claude: prefer `.claude/spotify-ads-api.local.md`, then fall back to `.codex/spotify-ads-api.local.md`, then `.gemini/spotify-ads-api.local.md`. - - Gemini: prefer `.gemini/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.codex/spotify-ads-api.local.md`. + - Codex: prefer `.codex/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.agents/spotify-ads-api.local.md`. + - Claude: prefer `.claude/spotify-ads-api.local.md`, then fall back to `.codex/spotify-ads-api.local.md`, then `.agents/spotify-ads-api.local.md`. + - Antigravity: prefer `.agents/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.codex/spotify-ads-api.local.md`. 2. Base URL: `https://api-partner.spotify.com/ads/v3` -3. If no settings file exists, instruct the user to run the configure skill first (`/spotify-ads-api:configure` on Claude/Codex, `/configure` on Gemini). -4. Read the active platform manifest for the plugin `version`: `.codex-plugin/plugin.json` on Codex, `.claude-plugin/plugin.json` on Claude, or `gemini-extension.json` (extension root) on Gemini. -5. Set `SDK_PRODUCT` to `codex-plugin` on Codex, `claude-code-plugin` on Claude, or `gemini-cli-extension` on Gemini. Set `SDK_HEADER="X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION"` and include `-H "$SDK_HEADER"` on all API requests. +3. If no settings file exists, instruct the user to run the configure skill first (`/spotify-ads-api:configure` on Claude/Codex, `/configure` on Antigravity). +4. Read the active platform manifest for the plugin `version`: `.codex-plugin/plugin.json` on Codex, `.claude-plugin/plugin.json` on Claude, or `plugin.json` (plugin root) on Antigravity. +5. Set `SDK_PRODUCT` to `codex-plugin` on Codex, `claude-code-plugin` on Claude, or `antigravity-cli-plugin` on Antigravity. Set `SDK_HEADER="X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION"` and include `-H "$SDK_HEADER"` on all API requests. ## Operations diff --git a/skills/clone/SKILL.md b/skills/clone/SKILL.md index cbd5142..786236d 100644 --- a/skills/clone/SKILL.md +++ b/skills/clone/SKILL.md @@ -14,13 +14,13 @@ Clone an existing campaign or ad set by reading its full hierarchy and recreatin ## Setup 1. Read `access_token`, `ad_account_id`, and `auto_execute` from the active platform settings file: - - Codex: prefer `.codex/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.gemini/spotify-ads-api.local.md`. - - Claude: prefer `.claude/spotify-ads-api.local.md`, then fall back to `.codex/spotify-ads-api.local.md`, then `.gemini/spotify-ads-api.local.md`. - - Gemini: prefer `.gemini/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.codex/spotify-ads-api.local.md`. + - Codex: prefer `.codex/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.agents/spotify-ads-api.local.md`. + - Claude: prefer `.claude/spotify-ads-api.local.md`, then fall back to `.codex/spotify-ads-api.local.md`, then `.agents/spotify-ads-api.local.md`. + - Antigravity: prefer `.agents/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.codex/spotify-ads-api.local.md`. 2. Base URL: `https://api-partner.spotify.com/ads/v3` -3. If no settings file exists, instruct the user to run the configure skill first (`/spotify-ads-api:configure` on Claude/Codex, `/configure` on Gemini). -4. Read the active platform manifest for the plugin `version`: `.codex-plugin/plugin.json` on Codex, `.claude-plugin/plugin.json` on Claude, or `gemini-extension.json` (extension root) on Gemini. -5. Set `SDK_PRODUCT` to `codex-plugin` on Codex, `claude-code-plugin` on Claude, or `gemini-cli-extension` on Gemini. Set `SDK_HEADER="X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION"` and include `-H "$SDK_HEADER"` on all API requests. +3. If no settings file exists, instruct the user to run the configure skill first (`/spotify-ads-api:configure` on Claude/Codex, `/configure` on Antigravity). +4. Read the active platform manifest for the plugin `version`: `.codex-plugin/plugin.json` on Codex, `.claude-plugin/plugin.json` on Claude, or `plugin.json` (plugin root) on Antigravity. +5. Set `SDK_PRODUCT` to `codex-plugin` on Codex, `claude-code-plugin` on Claude, or `antigravity-cli-plugin` on Antigravity. Set `SDK_HEADER="X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION"` and include `-H "$SDK_HEADER"` on all API requests. ## Parsing Arguments diff --git a/skills/configure/SKILL.md b/skills/configure/SKILL.md index 4c343aa..2d0b622 100644 --- a/skills/configure/SKILL.md +++ b/skills/configure/SKILL.md @@ -22,7 +22,7 @@ Full OAuth 2.0 authorization flow with automatic token refresh. 1. Choose the active settings file: - Codex: write `.codex/spotify-ads-api.local.md`. - Claude: write `.claude/spotify-ads-api.local.md`. - - Gemini: write `.gemini/spotify-ads-api.local.md`. + - Antigravity: write `.agents/spotify-ads-api.local.md`. Read that file if it exists. If it does not exist, read another platform's settings file as defaults, but do not overwrite it unless the user asks. 2. Prompt the user for OAuth credentials using AskUserQuestion: @@ -37,7 +37,7 @@ security add-generic-password -a "spotify-ads-api" -s "spotify-ads-api-client-se **Do NOT write client_secret to the settings file.** It must only be stored in the keychain. -4. Attempt the automated OAuth flow by running the helper script. On Gemini, no plugin-root env var is set — this skill's files live at `/skills/configure/`, so set `PLUGIN_ROOT` to the extension root (two directories up from this skill's directory) instead of using the snippet below. +4. Attempt the automated OAuth flow by running the helper script. On Antigravity, no plugin-root env var is set — this skill's files live at `/skills/configure/`, so set `PLUGIN_ROOT` to the plugin root (two directories up from this skill's directory) instead of using the snippet below. ```bash PLUGIN_ROOT="${CODEX_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-$PWD}}" @@ -76,7 +76,7 @@ uv run "${PLUGIN_ROOT}/skills/configure/scripts/oauth-flow.py" \ 9. Write the active platform settings file (see Settings File Format below). -10. Read the active platform manifest for the plugin `version`: `.codex-plugin/plugin.json` on Codex, `.claude-plugin/plugin.json` on Claude, or `gemini-extension.json` (extension root) on Gemini. Set `SDK_PRODUCT` to `codex-plugin` on Codex, `claude-code-plugin` on Claude, or `gemini-cli-extension` on Gemini, then set `SDK_HEADER="X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION"`. +10. Read the active platform manifest for the plugin `version`: `.codex-plugin/plugin.json` on Codex, `.claude-plugin/plugin.json` on Claude, or `plugin.json` (plugin root) on Antigravity. Set `SDK_PRODUCT` to `codex-plugin` on Codex, `claude-code-plugin` on Claude, or `antigravity-cli-plugin` on Antigravity, then set `SDK_HEADER="X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION"`. 11. Verify with a test API call: ```bash @@ -133,7 +133,7 @@ Legacy direct token mode for users who already have an access token. 1. Accept the access token from the argument. -2. Warn the user: "Direct token mode — this token will expire in ~1 hour with no automatic refresh. For auto-refresh, re-run the configure skill in oauth mode (`/spotify-ads-api:configure oauth` on Claude/Codex, `/configure oauth` on Gemini) using your client credentials." +2. Warn the user: "Direct token mode — this token will expire in ~1 hour with no automatic refresh. For auto-refresh, re-run the configure skill in oauth mode (`/spotify-ads-api:configure oauth` on Claude/Codex, `/configure oauth` on Antigravity) using your client credentials." 3. Read existing settings or prompt for: - **ad_account_id** (required) — Use the same businesses → ad accounts discovery flow as the oauth mode (`GET /businesses` then `GET /businesses/{business_id}/ad_accounts`), or ask the user to paste it. @@ -145,7 +145,7 @@ Legacy direct token mode for users who already have an access token. ## Settings File Format -Write the active platform settings file in this exact format (`.codex/spotify-ads-api.local.md` on Codex, `.claude/spotify-ads-api.local.md` on Claude, `.gemini/spotify-ads-api.local.md` on Gemini): +Write the active platform settings file in this exact format (`.codex/spotify-ads-api.local.md` on Codex, `.claude/spotify-ads-api.local.md` on Claude, `.agents/spotify-ads-api.local.md` on Antigravity): ```markdown --- @@ -179,8 +179,8 @@ Report the test API call result: ## Security Notes -- The settings file is gitignored via `.codex/*.local.md`, `.claude/*.local.md`, and `.gemini/*.local.md`. -- If the active settings directory (`.codex/`, `.claude/`, or `.gemini/`) doesn't exist, create it. +- The settings file is gitignored via `.codex/*.local.md`, `.claude/*.local.md`, and `.agents/*.local.md`. +- If the active settings directory (`.codex/`, `.claude/`, or `.agents/`) doesn't exist, create it. - **client_secret is stored in the macOS Keychain**, not in the settings file. Use `security find-generic-password -a "spotify-ads-api" -s "spotify-ads-api-client-secret" -w` to retrieve it when needed. - Never log or display the full access token or client_secret — show only the last 8 characters for confirmation. - Never write client_secret to the settings file or any other plaintext file. diff --git a/skills/dashboard/SKILL.md b/skills/dashboard/SKILL.md index 8550db9..08164e7 100644 --- a/skills/dashboard/SKILL.md +++ b/skills/dashboard/SKILL.md @@ -12,13 +12,13 @@ Quick performance overview with metrics, spend, and pacing for active campaigns. ## Setup 1. Read `access_token`, `ad_account_id`, and `auto_execute` from the active platform settings file: - - Codex: prefer `.codex/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.gemini/spotify-ads-api.local.md`. - - Claude: prefer `.claude/spotify-ads-api.local.md`, then fall back to `.codex/spotify-ads-api.local.md`, then `.gemini/spotify-ads-api.local.md`. - - Gemini: prefer `.gemini/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.codex/spotify-ads-api.local.md`. + - Codex: prefer `.codex/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.agents/spotify-ads-api.local.md`. + - Claude: prefer `.claude/spotify-ads-api.local.md`, then fall back to `.codex/spotify-ads-api.local.md`, then `.agents/spotify-ads-api.local.md`. + - Antigravity: prefer `.agents/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.codex/spotify-ads-api.local.md`. 2. Base URL: `https://api-partner.spotify.com/ads/v3` -3. If no settings file exists, instruct the user to run the configure skill first (`/spotify-ads-api:configure` on Claude/Codex, `/configure` on Gemini). -4. Read the active platform manifest for the plugin `version`: `.codex-plugin/plugin.json` on Codex, `.claude-plugin/plugin.json` on Claude, or `gemini-extension.json` (extension root) on Gemini. -5. Set `SDK_PRODUCT` to `codex-plugin` on Codex, `claude-code-plugin` on Claude, or `gemini-cli-extension` on Gemini. Set `SDK_HEADER="X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION"` and include `-H "$SDK_HEADER"` on all API requests. +3. If no settings file exists, instruct the user to run the configure skill first (`/spotify-ads-api:configure` on Claude/Codex, `/configure` on Antigravity). +4. Read the active platform manifest for the plugin `version`: `.codex-plugin/plugin.json` on Codex, `.claude-plugin/plugin.json` on Claude, or `plugin.json` (plugin root) on Antigravity. +5. Set `SDK_PRODUCT` to `codex-plugin` on Codex, `claude-code-plugin` on Claude, or `antigravity-cli-plugin` on Antigravity. Set `SDK_HEADER="X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION"` and include `-H "$SDK_HEADER"` on all API requests. ## Parsing Arguments diff --git a/skills/drafts/SKILL.md b/skills/drafts/SKILL.md index 0b32472..0c0be4e 100644 --- a/skills/drafts/SKILL.md +++ b/skills/drafts/SKILL.md @@ -23,13 +23,13 @@ The alternative (direct entity creation via `/campaigns`, `/ad_sets`, `/ads`) va ## Setup 1. Read `access_token`, `ad_account_id`, and `auto_execute` from the active platform settings file: - - Codex: prefer `.codex/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.gemini/spotify-ads-api.local.md`. - - Claude: prefer `.claude/spotify-ads-api.local.md`, then fall back to `.codex/spotify-ads-api.local.md`, then `.gemini/spotify-ads-api.local.md`. - - Gemini: prefer `.gemini/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.codex/spotify-ads-api.local.md`. + - Codex: prefer `.codex/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.agents/spotify-ads-api.local.md`. + - Claude: prefer `.claude/spotify-ads-api.local.md`, then fall back to `.codex/spotify-ads-api.local.md`, then `.agents/spotify-ads-api.local.md`. + - Antigravity: prefer `.agents/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.codex/spotify-ads-api.local.md`. 2. Base URL: `https://api-partner.spotify.com/ads/v3` -3. If no settings file exists, instruct the user to run the configure skill first (`/spotify-ads-api:configure` on Claude/Codex, `/configure` on Gemini). -4. Read the active platform manifest for the plugin `version`: `.codex-plugin/plugin.json` on Codex, `.claude-plugin/plugin.json` on Claude, or `gemini-extension.json` (extension root) on Gemini. -5. Set `SDK_PRODUCT` to `codex-plugin` on Codex, `claude-code-plugin` on Claude, or `gemini-cli-extension` on Gemini. Set `SDK_HEADER="X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION"` and include `-H "$SDK_HEADER"` on all API requests. +3. If no settings file exists, instruct the user to run the configure skill first (`/spotify-ads-api:configure` on Claude/Codex, `/configure` on Antigravity). +4. Read the active platform manifest for the plugin `version`: `.codex-plugin/plugin.json` on Codex, `.claude-plugin/plugin.json` on Claude, or `plugin.json` (plugin root) on Antigravity. +5. Set `SDK_PRODUCT` to `codex-plugin` on Codex, `claude-code-plugin` on Claude, or `antigravity-cli-plugin` on Antigravity. Set `SDK_HEADER="X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION"` and include `-H "$SDK_HEADER"` on all API requests. ## Operations diff --git a/skills/export/SKILL.md b/skills/export/SKILL.md index c8ef622..f1f2896 100644 --- a/skills/export/SKILL.md +++ b/skills/export/SKILL.md @@ -12,13 +12,13 @@ Export campaign hierarchies to CSV for offline review, combining entity data wit ## Setup 1. Read `access_token`, `ad_account_id`, and `auto_execute` from the active platform settings file: - - Codex: prefer `.codex/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.gemini/spotify-ads-api.local.md`. - - Claude: prefer `.claude/spotify-ads-api.local.md`, then fall back to `.codex/spotify-ads-api.local.md`, then `.gemini/spotify-ads-api.local.md`. - - Gemini: prefer `.gemini/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.codex/spotify-ads-api.local.md`. + - Codex: prefer `.codex/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.agents/spotify-ads-api.local.md`. + - Claude: prefer `.claude/spotify-ads-api.local.md`, then fall back to `.codex/spotify-ads-api.local.md`, then `.agents/spotify-ads-api.local.md`. + - Antigravity: prefer `.agents/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.codex/spotify-ads-api.local.md`. 2. Base URL: `https://api-partner.spotify.com/ads/v3` -3. If no settings file exists, instruct the user to run the configure skill first (`/spotify-ads-api:configure` on Claude/Codex, `/configure` on Gemini). -4. Read the active platform manifest for the plugin `version`: `.codex-plugin/plugin.json` on Codex, `.claude-plugin/plugin.json` on Claude, or `gemini-extension.json` (extension root) on Gemini. -5. Set `SDK_PRODUCT` to `codex-plugin` on Codex, `claude-code-plugin` on Claude, or `gemini-cli-extension` on Gemini. Set `SDK_HEADER="X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION"` and include `-H "$SDK_HEADER"` on all API requests. +3. If no settings file exists, instruct the user to run the configure skill first (`/spotify-ads-api:configure` on Claude/Codex, `/configure` on Antigravity). +4. Read the active platform manifest for the plugin `version`: `.codex-plugin/plugin.json` on Codex, `.claude-plugin/plugin.json` on Claude, or `plugin.json` (plugin root) on Antigravity. +5. Set `SDK_PRODUCT` to `codex-plugin` on Codex, `claude-code-plugin` on Claude, or `antigravity-cli-plugin` on Antigravity. Set `SDK_HEADER="X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION"` and include `-H "$SDK_HEADER"` on all API requests. ## Parsing Arguments diff --git a/skills/monitor/SKILL.md b/skills/monitor/SKILL.md index c811a69..116412e 100644 --- a/skills/monitor/SKILL.md +++ b/skills/monitor/SKILL.md @@ -12,13 +12,13 @@ Diagnose delivery problems across active campaigns. Goes beyond the dashboard by ## Setup 1. Read `access_token`, `ad_account_id`, and `auto_execute` from the active platform settings file: - - Codex: prefer `.codex/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.gemini/spotify-ads-api.local.md`. - - Claude: prefer `.claude/spotify-ads-api.local.md`, then fall back to `.codex/spotify-ads-api.local.md`, then `.gemini/spotify-ads-api.local.md`. - - Gemini: prefer `.gemini/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.codex/spotify-ads-api.local.md`. + - Codex: prefer `.codex/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.agents/spotify-ads-api.local.md`. + - Claude: prefer `.claude/spotify-ads-api.local.md`, then fall back to `.codex/spotify-ads-api.local.md`, then `.agents/spotify-ads-api.local.md`. + - Antigravity: prefer `.agents/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.codex/spotify-ads-api.local.md`. 2. Base URL: `https://api-partner.spotify.com/ads/v3` -3. If no settings file exists, instruct the user to run the configure skill first (`/spotify-ads-api:configure` on Claude/Codex, `/configure` on Gemini). -4. Read the active platform manifest for the plugin `version`: `.codex-plugin/plugin.json` on Codex, `.claude-plugin/plugin.json` on Claude, or `gemini-extension.json` (extension root) on Gemini. -5. Set `SDK_PRODUCT` to `codex-plugin` on Codex, `claude-code-plugin` on Claude, or `gemini-cli-extension` on Gemini. Set `SDK_HEADER="X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION"` and include `-H "$SDK_HEADER"` on all API requests. +3. If no settings file exists, instruct the user to run the configure skill first (`/spotify-ads-api:configure` on Claude/Codex, `/configure` on Antigravity). +4. Read the active platform manifest for the plugin `version`: `.codex-plugin/plugin.json` on Codex, `.claude-plugin/plugin.json` on Claude, or `plugin.json` (plugin root) on Antigravity. +5. Set `SDK_PRODUCT` to `codex-plugin` on Codex, `claude-code-plugin` on Claude, or `antigravity-cli-plugin` on Antigravity. Set `SDK_HEADER="X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION"` and include `-H "$SDK_HEADER"` on all API requests. ## Parsing Arguments diff --git a/skills/report/SKILL.md b/skills/report/SKILL.md index 7de437b..2662c6a 100644 --- a/skills/report/SKILL.md +++ b/skills/report/SKILL.md @@ -12,13 +12,13 @@ Pull reporting data from the Spotify Ads API. Read settings from the active plat ## Setup 1. Read `access_token`, `ad_account_id`, and `auto_execute` from the active platform settings file: - - Codex: prefer `.codex/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.gemini/spotify-ads-api.local.md`. - - Claude: prefer `.claude/spotify-ads-api.local.md`, then fall back to `.codex/spotify-ads-api.local.md`, then `.gemini/spotify-ads-api.local.md`. - - Gemini: prefer `.gemini/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.codex/spotify-ads-api.local.md`. + - Codex: prefer `.codex/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.agents/spotify-ads-api.local.md`. + - Claude: prefer `.claude/spotify-ads-api.local.md`, then fall back to `.codex/spotify-ads-api.local.md`, then `.agents/spotify-ads-api.local.md`. + - Antigravity: prefer `.agents/spotify-ads-api.local.md`, then fall back to `.claude/spotify-ads-api.local.md`, then `.codex/spotify-ads-api.local.md`. 2. Base URL: `https://api-partner.spotify.com/ads/v3` -3. If no settings file exists, instruct the user to run the configure skill first (`/spotify-ads-api:configure` on Claude/Codex, `/configure` on Gemini). -4. Read the active platform manifest for the plugin `version`: `.codex-plugin/plugin.json` on Codex, `.claude-plugin/plugin.json` on Claude, or `gemini-extension.json` (extension root) on Gemini. -5. Set `SDK_PRODUCT` to `codex-plugin` on Codex, `claude-code-plugin` on Claude, or `gemini-cli-extension` on Gemini. Set `SDK_HEADER="X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION"` and include `-H "$SDK_HEADER"` on all API requests. +3. If no settings file exists, instruct the user to run the configure skill first (`/spotify-ads-api:configure` on Claude/Codex, `/configure` on Antigravity). +4. Read the active platform manifest for the plugin `version`: `.codex-plugin/plugin.json` on Codex, `.claude-plugin/plugin.json` on Claude, or `plugin.json` (plugin root) on Antigravity. +5. Set `SDK_PRODUCT` to `codex-plugin` on Codex, `claude-code-plugin` on Claude, or `antigravity-cli-plugin` on Antigravity. Set `SDK_HEADER="X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION"` and include `-H "$SDK_HEADER"` on all API requests. ## Operations diff --git a/templates/settings-template.md b/templates/settings-template.md index e01f40c..d584a39 100644 --- a/templates/settings-template.md +++ b/templates/settings-template.md @@ -12,7 +12,7 @@ auto_execute: false Local configuration for the spotify-ads-api plugin. Store this file at `.codex/spotify-ads-api.local.md` on Codex, `.claude/spotify-ads-api.local.md` -on Claude, or `.gemini/spotify-ads-api.local.md` on Gemini. +on Claude, or `.agents/spotify-ads-api.local.md` on Antigravity. Do not commit this file to version control. Client secret is stored in the macOS Keychain, not in this file. diff --git a/tests/test-scenarios.md b/tests/test-scenarios.md index 0c4fded..1dcb937 100644 --- a/tests/test-scenarios.md +++ b/tests/test-scenarios.md @@ -9,13 +9,13 @@ **Variables used in curl examples below:** - `$TOKEN` — OAuth access token from settings - `$BASE_URL` — `https://api-partner.spotify.com/ads/v3` -- `$SDK_HEADER` — `X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION`, where `SDK_PRODUCT` is `codex-plugin` on Codex, `claude-code-plugin` on Claude, and `gemini-cli-extension` on Gemini +- `$SDK_HEADER` — `X-Spotify-Ads-Sdk: $SDK_PRODUCT/$PLUGIN_VERSION`, where `SDK_PRODUCT` is `codex-plugin` on Codex, `claude-code-plugin` on Claude, and `antigravity-cli-plugin` on Antigravity --- ## Scenario 1: Configure OAuth -**Prompt:** `/spotify-ads-api:configure` (`/configure` on Gemini) +**Prompt:** `/spotify-ads-api:configure` (`/configure` on Antigravity) **Quirks tested:** OAuth flow, settings file creation, token validation @@ -24,7 +24,7 @@ 2. Runs `oauth-flow.py` to open browser and complete authorization 3. Parses JSON output with `access_token`, `refresh_token`, `expires_in` 4. Prompts for `ad_account_id`, `auto_execute` -5. Writes the active platform settings file (`.codex/spotify-ads-api.local.md` on Codex, `.claude/spotify-ads-api.local.md` on Claude, `.gemini/spotify-ads-api.local.md` on Gemini) with all fields +5. Writes the active platform settings file (`.codex/spotify-ads-api.local.md` on Codex, `.claude/spotify-ads-api.local.md` on Claude, `.agents/spotify-ads-api.local.md` on Antigravity) with all fields 6. Verifies token with test API call **Success criteria:** @@ -328,11 +328,11 @@ curl -s -w "\nHTTP_STATUS:%{http_code}" -X POST -H "Authorization: Bearer Date: Fri, 3 Jul 2026 15:22:20 -0400 Subject: [PATCH 2/3] Fix Antigravity env vars and hook path variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Antigravity CLI inherits Gemini's variable names: ${extensionPath} for hook commands (not ${pluginPath}) and GEMINI_PROJECT_DIR for the project directory env var. Also corrects hooks copy instruction — agy plugin install auto-discovers hooks.json at the plugin root. Co-Authored-By: Claude Opus 4.6 (1M context) --- AGENTS.md | 2 +- ANTIGRAVITY.md | 4 ++-- hooks/antigravity-hooks.json | 2 +- hooks/check-token.sh | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 01f29f3..8830d57 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -25,7 +25,7 @@ The plugin follows the agent plugin structure with four component types: - `skills/clone/` — Clone campaigns or ad sets by reading the source hierarchy and recreating entities with modifications - `skills/api-reference/` — Comprehensive API v3 reference documentation with `references/` (endpoints, schemas, enums) and `examples/` (full flows). Activates automatically when the Spotify Ads API is mentioned. - **Agent** (`agents/spotify-ads-request-builder.md`) — A natural language agent that triggers automatically when users describe advertising tasks conversationally. Handles multi-step operations (campaign -> ad set -> ad) by chaining API calls and passing IDs between steps. -- **Hooks** — Per-platform hook configs invoking `hooks/check-token.sh` to automatically refresh expired OAuth tokens before Spotify API calls. `hooks/antigravity-hooks.json` contains the Antigravity CLI `PreToolUse` event (users copy it to `.agents/hooks.json` after install — see ANTIGRAVITY.md). `.claude-plugin/hooks.json` and `.codex-plugin/hooks.json` contain the Claude/Codex `PreToolUse` event, declared via the `hooks` field in each platform's `plugin.json`. The Antigravity hooks ship under a non-default filename to prevent Claude Code from auto-loading them. +- **Hooks** — Per-platform hook configs invoking `hooks/check-token.sh` to automatically refresh expired OAuth tokens before Spotify API calls. `hooks/antigravity-hooks.json` contains the Antigravity CLI `PreToolUse` event; when installed via `agy plugin install`, Antigravity auto-discovers `hooks.json` at the plugin root, but for local development with `agy plugin link`, users copy it to the repo root as `hooks.json` (see ANTIGRAVITY.md). `.claude-plugin/hooks.json` and `.codex-plugin/hooks.json` contain the Claude/Codex `PreToolUse` event, declared via the `hooks` field in each platform's `plugin.json`. The Antigravity hooks ship under a non-default filename to prevent Claude Code from auto-loading them. - **Commands** (`commands/configure.toml`) — An Antigravity CLI custom command exposing `/configure` as an explicit entry point to the configure skill. Other skills auto-activate on Antigravity via its native Agent Skills support. - **Settings** (`.codex/spotify-ads-api.local.md`, `.claude/spotify-ads-api.local.md`, or `.agents/spotify-ads-api.local.md`, with each platform preferring its own file and falling back to the other two) — Per-user local config with YAML frontmatter storing OAuth credentials (access_token, refresh_token, client_id, token_expires_at), ad_account_id, and auto_execute. The client_secret is stored in the macOS Keychain (service: `spotify-ads-api-client-secret`, account: `spotify-ads-api`), not in this file. Template lives in `templates/settings-template.md`. These files are gitignored. diff --git a/ANTIGRAVITY.md b/ANTIGRAVITY.md index ee2d2b9..d763d7f 100644 --- a/ANTIGRAVITY.md +++ b/ANTIGRAVITY.md @@ -24,10 +24,10 @@ Include `-H "$SDK_HEADER"` on all curl commands to `api-partner.spotify.com`. ## Token Expiry -A PreToolUse hook refreshes expired OAuth tokens automatically before API calls. Antigravity CLI auto-discovers hooks at the fixed path `.agents/hooks.json`, but the file ships as `hooks/antigravity-hooks.json` to avoid conflicts with Claude Code's hook loader. After installing the plugin, activate the hook once: +A PreToolUse hook refreshes expired OAuth tokens automatically before API calls. When installed via `agy plugin install`, Antigravity auto-discovers `hooks.json` at the plugin root. For local development with `agy plugin link`, the hook file ships as `hooks/antigravity-hooks.json` to avoid conflicts with Claude Code's hook loader. Activate it once: ```bash -cp hooks/antigravity-hooks.json .agents/hooks.json +cp hooks/antigravity-hooks.json hooks.json ``` If a request still returns 401, refresh the token per the configure skill's instructions or re-run `/configure`. diff --git a/hooks/antigravity-hooks.json b/hooks/antigravity-hooks.json index 83597de..3502942 100644 --- a/hooks/antigravity-hooks.json +++ b/hooks/antigravity-hooks.json @@ -6,7 +6,7 @@ "hooks": [ { "type": "command", - "command": "bash \"${pluginPath}${/}hooks${/}check-token.sh\"", + "command": "bash \"${extensionPath}${/}hooks${/}check-token.sh\"", "timeout": 30 } ] diff --git a/hooks/check-token.sh b/hooks/check-token.sh index 4b719a6..935a225 100755 --- a/hooks/check-token.sh +++ b/hooks/check-token.sh @@ -27,8 +27,8 @@ if ! command -v jq &>/dev/null; then exit 0 fi -# Detect platform from env vars -if [ -n "${ANTIGRAVITY_PROJECT_DIR:-}" ]; then +# Detect platform from env vars (Antigravity uses GEMINI_* env vars) +if [ -n "${GEMINI_PROJECT_DIR:-}" ]; then PLATFORM="antigravity" elif [ -n "${CODEX_PROJECT_DIR:-}" ]; then PLATFORM="codex" @@ -38,7 +38,7 @@ else PLATFORM="codex" fi -PROJECT_DIR="${ANTIGRAVITY_PROJECT_DIR:-${CODEX_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-$PWD}}}" +PROJECT_DIR="${GEMINI_PROJECT_DIR:-${CODEX_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-$PWD}}}" find_settings_file() { local order dir candidate From 253085bc736d2ffd0ea27a6feba6f51d887c97ce Mon Sep 17 00:00:00 2001 From: Mihir Yavalkar Date: Tue, 21 Jul 2026 15:24:42 -0400 Subject: [PATCH 3/3] Fix Antigravity hook output format and discovery path Address review feedback: - Use documented PreToolUse output schema (decision/reason) instead of made-up allow_tool/systemMessage fields - Move hooks.json to plugin root for auto-discovery by both Antigravity CLI and Antigravity 2.0 - Remove redundant hooks/antigravity-hooks.json - Update docs to reference both Antigravity CLI and 2.0 Co-Authored-By: Claude Opus 4.6 (1M context) --- .claude-plugin/hooks.json | 2 +- .codex-plugin/hooks.json | 2 +- AGENTS.md | 4 +- ANTIGRAVITY.md | 6 +- CONTRIBUTING.md | 2 +- hooks/antigravity-hooks.json => hooks.json | 0 hooks/check-token.sh | 74 ++++++++++++++-------- 7 files changed, 53 insertions(+), 37 deletions(-) rename hooks/antigravity-hooks.json => hooks.json (100%) diff --git a/.claude-plugin/hooks.json b/.claude-plugin/hooks.json index fd4efa1..7ac2829 100644 --- a/.claude-plugin/hooks.json +++ b/.claude-plugin/hooks.json @@ -1,5 +1,5 @@ { - "description": "Auto-refresh expired Spotify OAuth tokens before API calls (Claude Code and Codex; Antigravity uses hooks/antigravity-hooks.json)", + "description": "Auto-refresh expired Spotify OAuth tokens before API calls (Claude Code and Codex; Antigravity uses hooks.json at plugin root)", "hooks": { "PreToolUse": [ { diff --git a/.codex-plugin/hooks.json b/.codex-plugin/hooks.json index fd4efa1..7ac2829 100644 --- a/.codex-plugin/hooks.json +++ b/.codex-plugin/hooks.json @@ -1,5 +1,5 @@ { - "description": "Auto-refresh expired Spotify OAuth tokens before API calls (Claude Code and Codex; Antigravity uses hooks/antigravity-hooks.json)", + "description": "Auto-refresh expired Spotify OAuth tokens before API calls (Claude Code and Codex; Antigravity uses hooks.json at plugin root)", "hooks": { "PreToolUse": [ { diff --git a/AGENTS.md b/AGENTS.md index 5269be7..9f36bf0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -25,7 +25,7 @@ The plugin follows the agent plugin structure with four component types: - `skills/clone/` — Clone campaigns or ad sets by reading the source hierarchy and recreating entities with modifications - `skills/api-reference/` — Comprehensive API v3 reference documentation with `references/` (endpoints, schemas, enums) and `examples/` (full flows). Activates automatically when the Spotify Ads API is mentioned. - **Agent** (`agents/spotify-ads-request-builder.md`) — A natural language agent that triggers automatically when users describe advertising tasks conversationally. Handles multi-step operations (campaign -> ad set -> ad) by chaining API calls and passing IDs between steps. -- **Hooks** — Per-platform hook configs invoking `hooks/check-token.sh` to automatically refresh expired OAuth tokens before Spotify API calls. `hooks/antigravity-hooks.json` contains the Antigravity CLI `PreToolUse` event; when installed via `agy plugin install`, Antigravity auto-discovers `hooks.json` at the plugin root, but for local development with `agy plugin link`, users copy it to the repo root as `hooks.json` (see ANTIGRAVITY.md). `.claude-plugin/hooks.json` and `.codex-plugin/hooks.json` contain the Claude/Codex `PreToolUse` event, declared via the `hooks` field in each platform's `plugin.json`. The Antigravity hooks ship under a non-default filename to prevent Claude Code from auto-loading them. +- **Hooks** — Per-platform hook configs invoking `hooks/check-token.sh` to automatically refresh expired OAuth tokens before Spotify API calls. `hooks.json` at the plugin root contains the Antigravity `PreToolUse` event, auto-discovered by both Antigravity CLI (`agy plugin install`) and Antigravity 2.0 (`.agents/plugins/` workspace discovery). `.claude-plugin/hooks.json` and `.codex-plugin/hooks.json` contain the Claude/Codex `PreToolUse` event, declared via the `hooks` field in each platform's `plugin.json`. Note: the hook payload and response formats differ across platforms — Claude/Codex use `.tool_input.command` and support command rewriting via `updatedInput`, while Antigravity uses `.toolCall.args.CommandLine` and only supports allow/deny with `decision`/`reason` (the hook refreshes the token in the settings file and tells the agent to re-read it). - **Commands** (`commands/configure.toml`) — An Antigravity CLI custom command exposing `/configure` as an explicit entry point to the configure skill. Other skills auto-activate on Antigravity via its native Agent Skills support. - **Settings** (`.codex/spotify-ads-api.local.md`, `.claude/spotify-ads-api.local.md`, or `.agents/spotify-ads-api.local.md`, with each platform preferring its own file and falling back to the other two) — Per-user local config with YAML frontmatter storing OAuth credentials (access_token, refresh_token, client_id, token_expires_at), ad_account_id, and auto_execute. The client_secret is stored in the macOS Keychain (service: `spotify-ads-api-client-secret`, account: `spotify-ads-api`), not in this file. Template lives in `templates/settings-template.md`. These files are gitignored. @@ -36,7 +36,7 @@ Keep both marketplace files intentional and in sync: - `.agents/plugins/marketplace.json` is the Codex-facing catalog used by `codex plugin marketplace add spotify/ads-agentic-tools`. It must include Codex's `interface.displayName`, `policy.installation`, `policy.authentication`, and `category` metadata. Because this plugin lives at the repository root, use a Git-backed root plugin source (`"source": "url"` with the repository URL) instead of a local `source.path` of `"./"`; Codex treats that local root path as empty and skips the plugin. - `.claude-plugin/marketplace.json` is the Claude Code-compatible catalog. Claude Code requires marketplace metadata at that path and requires a top-level `owner`. To keep the file portable, use the stricter Claude-valid schema: local plugin sources should use the relative string form (`"source": "./"` for this repo-root plugin), and Codex-only marketplace fields such as top-level `interface` or plugin `policy` should not be added to this file. The Claude plugin manifest `.claude-plugin/plugin.json` must also avoid Codex-only manifest fields such as `interface`; keep display metadata in `.codex-plugin/plugin.json` and the Codex marketplace. -- Antigravity CLI has no marketplace file. The root `plugin.json` is the Antigravity manifest; `agy plugin install ` reads it directly, and Antigravity auto-discovers the `skills/`, `commands/`, and `hooks/` directories at the plugin root. Keep its `name`, `version`, and `description` in sync with the other two manifests, and do not add Antigravity-only fields (such as `contextFileName`) to the Claude or Codex manifests. +- Antigravity has no marketplace file. The root `plugin.json` is the Antigravity manifest; Antigravity CLI installs via `agy plugin install ` and Antigravity 2.0 discovers plugins placed in `.agents/plugins/` (workspace) or `~/.gemini/config/plugins/` (global). Both versions auto-discover `skills/`, `commands/`, and `hooks.json` at the plugin root. Keep `name`, `version`, and `description` in sync with the other two manifests, and do not add Antigravity-only fields (such as `contextFileName`) to the Claude or Codex manifests. When updating marketplace metadata, keep the plugin name, source path, category, description, and user-facing display name aligned wherever each schema supports those fields. The plugin `version` must be bumped in all three manifests together: `.claude-plugin/plugin.json`, `.codex-plugin/plugin.json`, and `plugin.json`. diff --git a/ANTIGRAVITY.md b/ANTIGRAVITY.md index d763d7f..1416029 100644 --- a/ANTIGRAVITY.md +++ b/ANTIGRAVITY.md @@ -24,11 +24,7 @@ Include `-H "$SDK_HEADER"` on all curl commands to `api-partner.spotify.com`. ## Token Expiry -A PreToolUse hook refreshes expired OAuth tokens automatically before API calls. When installed via `agy plugin install`, Antigravity auto-discovers `hooks.json` at the plugin root. For local development with `agy plugin link`, the hook file ships as `hooks/antigravity-hooks.json` to avoid conflicts with Claude Code's hook loader. Activate it once: - -```bash -cp hooks/antigravity-hooks.json hooks.json -``` +A PreToolUse hook refreshes expired OAuth tokens automatically before API calls. Both Antigravity CLI (`agy plugin install`) and Antigravity 2.0 auto-discover `hooks.json` at the plugin root. If a request still returns 401, refresh the token per the configure skill's instructions or re-run `/configure`. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 412eb42..62631fe 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -60,7 +60,7 @@ There is no automated test suite. Before submitting a pull request: - Verify your changes work against the Spotify Ads API on at least one supported platform (Codex, Claude Code, or Antigravity CLI) - Confirm that existing skills (`/spotify-ads-api:campaigns`, `/spotify-ads-api:ads`, etc.) still function correctly - If adding a new skill, include a `SKILL.md` following the patterns in existing skill directories -- If touching `hooks/`, note there are three per-platform hook configs: `hooks/antigravity-hooks.json` (Antigravity, `PreToolUse`), `.claude-plugin/hooks.json` (Claude), and `.codex-plugin/hooks.json` (Codex) — all calling `check-token.sh`. Test the token-refresh hook on all three platforms +- If touching `hooks/`, note there are three per-platform hook configs: `hooks.json` at plugin root (Antigravity CLI and 2.0, `PreToolUse`), `.claude-plugin/hooks.json` (Claude), and `.codex-plugin/hooks.json` (Codex) — all calling `check-token.sh`. Test the token-refresh hook on all three platforms ## Style diff --git a/hooks/antigravity-hooks.json b/hooks.json similarity index 100% rename from hooks/antigravity-hooks.json rename to hooks.json diff --git a/hooks/check-token.sh b/hooks/check-token.sh index 935a225..0f96335 100755 --- a/hooks/check-token.sh +++ b/hooks/check-token.sh @@ -3,7 +3,9 @@ set -uo pipefail # Spotify Ads API pre-tool hook (PreToolUse on Claude/Codex/Antigravity) # -# Auto-refreshes expired OAuth tokens before API calls +# Auto-refreshes expired OAuth tokens before API calls. +# Claude/Codex payload: .tool_input.command; supports command rewriting. +# Antigravity payload: .toolCall.args.CommandLine; decision allow/deny only (no rewrite). SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)" PLUGIN_ROOT="$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd)" @@ -27,18 +29,17 @@ if ! command -v jq &>/dev/null; then exit 0 fi -# Detect platform from env vars (Antigravity uses GEMINI_* env vars) -if [ -n "${GEMINI_PROJECT_DIR:-}" ]; then - PLATFORM="antigravity" -elif [ -n "${CODEX_PROJECT_DIR:-}" ]; then +# Detect platform from env vars; Antigravity sets none of the known +# *_PROJECT_DIR vars, so it falls through to the default. +if [ -n "${CODEX_PROJECT_DIR:-}" ]; then PLATFORM="codex" elif [ -n "${CLAUDE_PROJECT_DIR:-}" ]; then PLATFORM="claude" else - PLATFORM="codex" + PLATFORM="antigravity" fi -PROJECT_DIR="${GEMINI_PROJECT_DIR:-${CODEX_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-$PWD}}}" +PROJECT_DIR="${CODEX_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-$PWD}}" find_settings_file() { local order dir candidate @@ -58,8 +59,17 @@ find_settings_file() { done } -# Extract the bash command from tool input -command=$(printf '%s' "$input" | jq -r '.tool_input.command // .tool_input.cmd // .input.command // .input.cmd // ""') +# Extract the command from tool input (different field paths per platform) +# Claude/Codex: .tool_input.command +# Antigravity: .toolCall.args.CommandLine +command=$(printf '%s' "$input" | jq -r ' + .tool_input.command // + .tool_input.cmd // + .input.command // + .input.cmd // + .toolCall.args.CommandLine // + .toolCall.args.command // + ""') if [[ -z "$command" ]] || [[ "$command" != *"api-partner.spotify.com"* ]]; then exit 0 fi @@ -131,7 +141,7 @@ if [ -n "$SETTINGS_FILE" ] && [ -f "$SETTINGS_FILE" ]; then if [ -n "$access_token" ]; then modified_command="${modified_command//$access_token/$new_token}" fi - system_message="Spotify API token was expired and has been refreshed automatically." + system_message="Spotify API token was expired and has been refreshed automatically. Re-read the access_token from the settings file before retrying." fi else system_message="Failed to refresh Spotify API token. Run the configure skill (/spotify-ads-api:configure on Claude/Codex, /configure on Antigravity) to re-authenticate." @@ -141,26 +151,36 @@ if [ -n "$SETTINGS_FILE" ] && [ -f "$SETTINGS_FILE" ]; then fi # --- Emit output --- -# All platforms (Claude, Codex, Antigravity) use permissionDecision/updatedInput. -if [[ "$modified_command" != "$command" ]]; then +# Claude/Codex: permissionDecision + updatedInput to rewrite the command. +# Antigravity: decision + reason (no command rewriting support). +if [ "$PLATFORM" = "antigravity" ]; then if [ -n "$system_message" ]; then - jq -n --arg cmd "$modified_command" --arg msg "$system_message" '{ - "hookSpecificOutput": { - "permissionDecision": "allow", - "updatedInput": {"command": $cmd} - }, - "systemMessage": $msg - }' 2>/dev/null - else - jq -n --arg cmd "$modified_command" '{ - "hookSpecificOutput": { - "permissionDecision": "allow", - "updatedInput": {"command": $cmd} - } + jq -n --arg msg "$system_message" '{ + "decision": "allow", + "reason": $msg }' 2>/dev/null fi -elif [ -n "$system_message" ]; then - jq -n --arg msg "$system_message" '{"systemMessage": $msg}' 2>/dev/null +else + if [[ "$modified_command" != "$command" ]]; then + if [ -n "$system_message" ]; then + jq -n --arg cmd "$modified_command" --arg msg "$system_message" '{ + "hookSpecificOutput": { + "permissionDecision": "allow", + "updatedInput": {"command": $cmd} + }, + "systemMessage": $msg + }' 2>/dev/null + else + jq -n --arg cmd "$modified_command" '{ + "hookSpecificOutput": { + "permissionDecision": "allow", + "updatedInput": {"command": $cmd} + } + }' 2>/dev/null + fi + elif [ -n "$system_message" ]; then + jq -n --arg msg "$system_message" '{"systemMessage": $msg}' 2>/dev/null + fi fi exit 0