Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/hooks.json
Original file line number Diff line number Diff line change
@@ -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.json at plugin root)",
"hooks": {
"PreToolUse": [
{
Expand Down
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
2 changes: 1 addition & 1 deletion .codex-plugin/hooks.json
Original file line number Diff line number Diff line change
@@ -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.json at plugin root)",
"hooks": {
"PreToolUse": [
{
Expand Down
2 changes: 1 addition & 1 deletion .codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

#### Checklist
<!--- Put an `x` in all the boxes that apply: -->
- [ ] 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)
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.claude/*.local.md
.codex/*.local.md
.gemini/*.local.md
.agents/*.local.md
.DS_Store
*.log
internal-v3.yaml
14 changes: 7 additions & 7 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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.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.

## Marketplace Compatibility

Expand All @@ -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 <repo-url>` 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 has no marketplace file. The root `plugin.json` is the Antigravity manifest; Antigravity CLI installs via `agy plugin install <repo-url>` 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 `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

Expand All @@ -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.
- **Skill attribution header**: Every API request must also include `X-Spotify-Ads-Skill: <skill-name>`, where `<skill-name>` is the directory name of the active skill (e.g. `campaigns`, `dashboard`, `report`) or `request-builder` for the agent. Set `SKILL_HEADER="X-Spotify-Ads-Skill: <skill-name>"` and include `-H "$SKILL_HEADER"` on all curl commands. This enables per-skill invocation counts and error rates in API logs.
- **`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.
Expand Down
16 changes: 6 additions & 10 deletions GEMINI.md → ANTIGRAVITY.md
Original file line number Diff line number Diff line change
@@ -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.

Expand All @@ -14,24 +14,20 @@ 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:

```bash
cp hooks/gemini-hooks.json hooks/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`.

## 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.
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
12 changes: 6 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand All @@ -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

Expand All @@ -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.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

Expand Down
Loading