diff --git a/.cursor-plugin/marketplace.json b/.cursor-plugin/marketplace.json new file mode 100644 index 0000000..860c199 --- /dev/null +++ b/.cursor-plugin/marketplace.json @@ -0,0 +1,12 @@ +{ + "name": "agent-commerce", + "owner": { "name": "devlab-group" }, + "metadata": { "description": "The Agent Commerce Gateway skill for coding agents." }, + "plugins": [ + { + "name": "agent-commerce-gateway", + "source": "./plugins/agent-commerce-gateway", + "description": "Integrate the Agent Commerce Gateway into an existing HTTP API: configure, start and verify it." + } + ] +} diff --git a/plugins/agent-commerce-gateway/.cursor-plugin/plugin.json b/plugins/agent-commerce-gateway/.cursor-plugin/plugin.json new file mode 100644 index 0000000..aacd354 --- /dev/null +++ b/plugins/agent-commerce-gateway/.cursor-plugin/plugin.json @@ -0,0 +1,15 @@ +{ + "name": "agent-commerce-gateway", + "displayName": "Agent Commerce Gateway", + "version": "2.7.2", + "description": "Put the Agent Commerce Gateway in front of an existing HTTP API so AI agents can call and pay for it with x402.", + "author": { "name": "devlab-group" }, + "homepage": "https://github.com/devlab-group/agent-commerce#readme", + "repository": "https://github.com/devlab-group/agent-commerce", + "license": "Apache-2.0", + "logo": ".claude-plugin/icon.png", + "keywords": ["x402", "mcp", "ai-agents", "payments", "agentic-commerce"], + "category": "developer-tools", + "tags": ["x402", "payments", "mcp"], + "skills": "./skills/" +} diff --git a/plugins/agent-commerce-gateway/README.md b/plugins/agent-commerce-gateway/README.md index 2b5a6b1..c12dea8 100644 --- a/plugins/agent-commerce-gateway/README.md +++ b/plugins/agent-commerce-gateway/README.md @@ -1,6 +1,6 @@ # Agent Commerce Gateway plugin -This plugin adds one skill, `agent-commerce`, that helps Claude put +This plugin adds one skill, `agent-commerce`, that helps a coding agent put [Agent Commerce Gateway](https://github.com/devlab-group/agent-commerce) (npm `@devlab.group/agent-commerce`) in front of an existing HTTP API. AI agents can then call the API's endpoints over HTTP or MCP and pay for them @@ -37,7 +37,7 @@ anywhere. ## Credentials the gateway uses The skill writes credentials only as `${VAR}` placeholders in the gateway's -`config.yaml`, and tells Claude never to ask for a secret in chat or write one +`config.yaml`, and tells the agent never to ask for a secret in chat or write one into a file, command or log. The user sets the values in the gateway's own `.env`, which the gateway CLI reads when it runs. Each value goes only to the party it belongs to: diff --git a/scripts/check-package-metadata.mjs b/scripts/check-package-metadata.mjs index a3408c4..32cc3c7 100644 --- a/scripts/check-package-metadata.mjs +++ b/scripts/check-package-metadata.mjs @@ -1,6 +1,6 @@ #!/usr/bin/env node /** - * Require package and Claude Code plugin links to point at the real repository. + * Require package, Claude Code plugin and Cursor plugin links to point at the real repository. * A wrong namespace could send private vulnerability reports to an attacker. * An allowlist rejects destinations a placeholder blocklist would miss. */ @@ -13,6 +13,8 @@ const manifestPath = resolve(root, 'package.json'); const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')); const pluginManifest = 'plugins/agent-commerce-gateway/.claude-plugin/plugin.json'; const plugin = JSON.parse(readFileSync(resolve(root, pluginManifest), 'utf8')); +const cursorManifest = 'plugins/agent-commerce-gateway/.cursor-plugin/plugin.json'; +const cursor = JSON.parse(readFileSync(resolve(root, cursorManifest), 'utf8')); const EXPECTED = 'github.com/devlab-group/agent-commerce'; @@ -24,6 +26,8 @@ const fields = [ [`${pluginManifest} homepage`, plugin.homepage], [`${pluginManifest} documentationUrl`, plugin.documentationUrl], [`${pluginManifest} supportUrl`, plugin.supportUrl], + [`${cursorManifest} repository`, cursor.repository], + [`${cursorManifest} homepage`, cursor.homepage], ]; // Anchored at the scheme and host, because npm renders these as links: a diff --git a/tests/unit/cli/skill.test.ts b/tests/unit/cli/skill.test.ts index b8b05ce..c211fb4 100644 --- a/tests/unit/cli/skill.test.ts +++ b/tests/unit/cli/skill.test.ts @@ -261,3 +261,46 @@ describe('Claude Code plugin', () => { expect(existsSync(join(pluginDir, 'package.json'))).toBe(false); }); }); + +describe('Cursor plugin', () => { + const plugin = readJson<{ + name: string; + displayName: string; + version: string; + description: string; + license: string; + logo: string; + skills: string; + }>(join(pluginDir, '.cursor-plugin', 'plugin.json')); + const claude = readJson<{ name: string; displayName: string; description: string }>( + join(pluginDir, '.claude-plugin', 'plugin.json'), + ); + const marketplace = readJson<{ name: string; plugins: { name: string; source: string }[] }>( + join(root, '.cursor-plugin', 'marketplace.json'), + ); + + it('releases with the package version and matches the Claude Code plugin', () => { + expect(plugin.version).toBe(manifest.version); + expect(plugin.license).toBe(manifest.license); + expect(plugin).toMatchObject({ + name: claude.name, + displayName: claude.displayName, + description: claude.description, + }); + }); + + it('is listed in the marketplace from the same folder', () => { + expect(marketplace.plugins).toEqual([ + expect.objectContaining({ name: plugin.name, source: './plugins/agent-commerce-gateway' }), + ]); + }); + + it('points at files inside the plugin folder', () => { + // Cursor requires relative manifest paths without `..` + for (const path of [plugin.logo, plugin.skills]) { + expect(path).not.toMatch(/^\/|^[a-z]+:|\.\./); + expect(existsSync(join(pluginDir, path))).toBe(true); + } + expect(existsSync(join(pluginDir, plugin.skills, 'agent-commerce', 'SKILL.md'))).toBe(true); + }); +});