From 23445f7e4b5966ec878e1681f5be947ba68bac35 Mon Sep 17 00:00:00 2001 From: Dmitry Sergeev Date: Fri, 6 Nov 2026 18:02:40 +0100 Subject: [PATCH] chore: move the Claude Code plugin into its own folder The plugin was the repository root, so installing it copied the whole repository, and its package.json and lockfile put a dependency install on every user. Anthropic's plugin directory also checks everything in the plugin folder: the root holds more than 512 files, files over 256 KiB and binaries, which hold a submission for manual review. The plugin now lives in plugins/agent-commerce-gateway with its manifest, the agent-commerce skill and a README for the directory listing. It is renamed agent-commerce-gateway and the marketplace points at the folder; the skill keeps its name and content. The manifest gains displayName, documentationUrl and supportUrl, and the metadata check covers the two new links. --- .claude-plugin/marketplace.json | 4 +- README.md | 4 +- .../.claude-plugin}/plugin.json | 5 +- plugins/agent-commerce-gateway/README.md | 61 +++++++++++++++++++ .../skills}/agent-commerce/SKILL.md | 0 .../skills}/agent-commerce/assets/config.yaml | 0 .../agent-commerce/references/config.md | 0 .../agent-commerce/references/production.md | 0 .../references/troubleshooting.md | 0 scripts/check-package-metadata.mjs | 9 ++- tests/unit/cli/skill.test.ts | 15 +++-- 11 files changed, 85 insertions(+), 13 deletions(-) rename {.claude-plugin => plugins/agent-commerce-gateway/.claude-plugin}/plugin.json (67%) create mode 100644 plugins/agent-commerce-gateway/README.md rename {skills => plugins/agent-commerce-gateway/skills}/agent-commerce/SKILL.md (100%) rename {skills => plugins/agent-commerce-gateway/skills}/agent-commerce/assets/config.yaml (100%) rename {skills => plugins/agent-commerce-gateway/skills}/agent-commerce/references/config.md (100%) rename {skills => plugins/agent-commerce-gateway/skills}/agent-commerce/references/production.md (100%) rename {skills => plugins/agent-commerce-gateway/skills}/agent-commerce/references/troubleshooting.md (100%) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 703d114..b00eb7f 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -4,8 +4,8 @@ "description": "The Agent Commerce Gateway skill for coding agents.", "plugins": [ { - "name": "agent-commerce", - "source": ".", + "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/README.md b/README.md index 55cf809..e0adc37 100644 --- a/README.md +++ b/README.md @@ -179,7 +179,7 @@ claims and pending-task takes, then check that exactly one caller wins. ## Integrate with an AI coding agent The repository includes an experimental [Agent Skills](https://agentskills.io) -skill, [`skills/agent-commerce`](skills/agent-commerce/SKILL.md), for adding the +skill, [`agent-commerce`](plugins/agent-commerce-gateway/skills/agent-commerce/SKILL.md), for adding the gateway to an existing API. It covers Base Sepolia configuration, startup and checks through an unpaid 402 challenge. Its instructions keep secrets out of chat and leave payment signing to the user. The skill has automated CLI and @@ -188,7 +188,7 @@ config checks. ```text # Claude Code /plugin marketplace add devlab-group/agent-commerce -/plugin install agent-commerce@agent-commerce +/plugin install agent-commerce-gateway@agent-commerce # Codex, Cursor and other agents (a third-party installer) DISABLE_TELEMETRY=1 npx skills add devlab-group/agent-commerce --skill agent-commerce diff --git a/.claude-plugin/plugin.json b/plugins/agent-commerce-gateway/.claude-plugin/plugin.json similarity index 67% rename from .claude-plugin/plugin.json rename to plugins/agent-commerce-gateway/.claude-plugin/plugin.json index c01aeb1..f162b98 100644 --- a/.claude-plugin/plugin.json +++ b/plugins/agent-commerce-gateway/.claude-plugin/plugin.json @@ -1,10 +1,13 @@ { - "name": "agent-commerce", + "name": "agent-commerce-gateway", + "displayName": "Agent Commerce Gateway", "version": "2.6.0", "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", "url": "https://github.com/devlab-group" }, "homepage": "https://github.com/devlab-group/agent-commerce#readme", "repository": "https://github.com/devlab-group/agent-commerce", + "documentationUrl": "https://github.com/devlab-group/agent-commerce#readme", + "supportUrl": "https://github.com/devlab-group/agent-commerce/issues", "license": "Apache-2.0", "keywords": ["x402", "mcp", "ai-agents", "payments", "agentic-commerce"] } diff --git a/plugins/agent-commerce-gateway/README.md b/plugins/agent-commerce-gateway/README.md new file mode 100644 index 0000000..7d5f9c7 --- /dev/null +++ b/plugins/agent-commerce-gateway/README.md @@ -0,0 +1,61 @@ +# Agent Commerce Gateway plugin + +This plugin adds one skill, `agent-commerce`, that helps Claude 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 +with x402 stablecoin payments, which go straight to the merchant's wallet. + +## What the skill does + +- Reads the API project in the working directory (its stack, routes and any + OpenAPI file), or asks for the API's base URL and endpoints. +- Asks which endpoints agents may call, which of them are paid and at what + price in USDC, the merchant wallet address, and how to keep callers from + bypassing the gateway. It never chooses prices. +- Creates an `agent-commerce/` directory with its own `package.json`, installs + the gateway there, and writes `config.yaml` and `.env.example`. +- Runs the gateway's `validate`, `start` and `doctor` commands, and checks that + an unpaid call to a paid endpoint returns HTTP 402. + +## What it runs, sends and fetches + +- `npm install @devlab.group/agent-commerce`, and the optional peer packages + the configuration enables (such as `@x402/core`, `@x402/evm`, `viem` and + `@modelcontextprotocol/server`), from the npm registry. +- The locally installed gateway CLI through `npx --no -- agent-commerce`, + which refuses to download a missing package. +- `curl` requests to the local gateway, by default on `127.0.0.1:8080`. +- The gateway it configures calls the merchant's API, an x402 facilitator + (by default `https://x402.org/facilitator`) and a chain RPC endpoint (by + default `https://base-sepolia-rpc.publicnode.com`, the Base Sepolia + testnet). + +The plugin has no hooks, MCP servers or scripts of its own, and sends no data +anywhere. + +## Safety rules in the skill + +- It never asks for a secret in chat or writes one into a file, command or + log. Secrets are `${VAR}` placeholders that the user fills in `.env`. +- It never generates, requests or handles a private key, seed phrase or + wallet. +- It asks first before changing application code, overwriting or deleting + files, deploying, or touching mainnet. + +## Requirements + +Node.js 22 or later, npm 10 or later, and `@devlab.group/agent-commerce` +2.2.0 or later. + +## Install in Claude Code + +```text +/plugin marketplace add devlab-group/agent-commerce +/plugin install agent-commerce-gateway@agent-commerce +``` + +## License + +Apache-2.0. Source and documentation: +https://github.com/devlab-group/agent-commerce diff --git a/skills/agent-commerce/SKILL.md b/plugins/agent-commerce-gateway/skills/agent-commerce/SKILL.md similarity index 100% rename from skills/agent-commerce/SKILL.md rename to plugins/agent-commerce-gateway/skills/agent-commerce/SKILL.md diff --git a/skills/agent-commerce/assets/config.yaml b/plugins/agent-commerce-gateway/skills/agent-commerce/assets/config.yaml similarity index 100% rename from skills/agent-commerce/assets/config.yaml rename to plugins/agent-commerce-gateway/skills/agent-commerce/assets/config.yaml diff --git a/skills/agent-commerce/references/config.md b/plugins/agent-commerce-gateway/skills/agent-commerce/references/config.md similarity index 100% rename from skills/agent-commerce/references/config.md rename to plugins/agent-commerce-gateway/skills/agent-commerce/references/config.md diff --git a/skills/agent-commerce/references/production.md b/plugins/agent-commerce-gateway/skills/agent-commerce/references/production.md similarity index 100% rename from skills/agent-commerce/references/production.md rename to plugins/agent-commerce-gateway/skills/agent-commerce/references/production.md diff --git a/skills/agent-commerce/references/troubleshooting.md b/plugins/agent-commerce-gateway/skills/agent-commerce/references/troubleshooting.md similarity index 100% rename from skills/agent-commerce/references/troubleshooting.md rename to plugins/agent-commerce-gateway/skills/agent-commerce/references/troubleshooting.md diff --git a/scripts/check-package-metadata.mjs b/scripts/check-package-metadata.mjs index 5836b76..a3408c4 100644 --- a/scripts/check-package-metadata.mjs +++ b/scripts/check-package-metadata.mjs @@ -11,7 +11,8 @@ import { fileURLToPath } from 'node:url'; const root = resolve(dirname(fileURLToPath(import.meta.url)), '..'); const manifestPath = resolve(root, 'package.json'); const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')); -const plugin = JSON.parse(readFileSync(resolve(root, '.claude-plugin', 'plugin.json'), 'utf8')); +const pluginManifest = 'plugins/agent-commerce-gateway/.claude-plugin/plugin.json'; +const plugin = JSON.parse(readFileSync(resolve(root, pluginManifest), 'utf8')); const EXPECTED = 'github.com/devlab-group/agent-commerce'; @@ -19,8 +20,10 @@ const fields = [ ['repository.url', manifest.repository?.url], ['bugs.url', manifest.bugs?.url], ['homepage', manifest.homepage], - ['.claude-plugin/plugin.json repository', plugin.repository], - ['.claude-plugin/plugin.json homepage', plugin.homepage], + [`${pluginManifest} repository`, plugin.repository], + [`${pluginManifest} homepage`, plugin.homepage], + [`${pluginManifest} documentationUrl`, plugin.documentationUrl], + [`${pluginManifest} supportUrl`, plugin.supportUrl], ]; // 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 8b23830..09b1a12 100644 --- a/tests/unit/cli/skill.test.ts +++ b/tests/unit/cli/skill.test.ts @@ -14,7 +14,8 @@ import { RawConfigSchema } from '../../../src/config/schema/raw'; import { createCapturingIo } from './fixtures'; const root = resolve(dirname(fileURLToPath(import.meta.url)), '../../..'); -const skillDir = join(root, 'skills', 'agent-commerce'); +const pluginDir = join(root, 'plugins', 'agent-commerce-gateway'); +const skillDir = join(pluginDir, 'skills', 'agent-commerce'); const readJson = (path: string): T => JSON.parse(readFileSync(path, 'utf8')) as T; const manifest = readJson<{ name: string; version: string; license: string }>( join(root, 'package.json'), @@ -234,7 +235,7 @@ describe('agent skill', () => { describe('Claude Code plugin', () => { const plugin = readJson<{ name: string; version: string; license: string }>( - join(root, '.claude-plugin', 'plugin.json'), + join(pluginDir, '.claude-plugin', 'plugin.json'), ); const marketplace = readJson<{ name: string; plugins: { name: string; source: string }[] }>( join(root, '.claude-plugin', 'marketplace.json'), @@ -246,10 +247,14 @@ describe('Claude Code plugin', () => { expect(plugin.license).toBe(manifest.license); }); - it('is listed in the marketplace from the repository root, beside the skill', () => { + it('is listed in the marketplace from its own folder, which holds the skill', () => { + // Installs copy only the plugin folder: the skill must be inside it, and + // the repository's package.json and lockfile must stay outside it expect(marketplace.plugins).toEqual([ - expect.objectContaining({ name: plugin.name, source: '.' }), + expect.objectContaining({ name: plugin.name, source: './plugins/agent-commerce-gateway' }), ]); - expect(existsSync(join(root, 'skills', plugin.name, 'SKILL.md'))).toBe(true); + expect(existsSync(join(skillDir, 'SKILL.md'))).toBe(true); + expect(existsSync(join(pluginDir, 'README.md'))).toBe(true); + expect(existsSync(join(pluginDir, 'package.json'))).toBe(false); }); });