From 65766318d2073c412f446538e3261496239971ac Mon Sep 17 00:00:00 2001 From: Gaurav Dubey Date: Tue, 28 Jul 2026 02:48:08 +0530 Subject: [PATCH] fix(marketplace): use object source so Claude Cowork remote sync succeeds (#2542) Adding the ECC marketplace in Claude Desktop (Cowork) fails with "Marketplace Sync failed. Check the repository-URL and try again." Cowork's remote (server-side clone) sync rejects string/relative plugin sources: External plugin sources must be objects with a 'source' field (github, url, or git-subdir). The local `/plugin marketplace add` in the Claude Code CLI tolerates the "./" shorthand, which is why the CLI succeeds while Cowork fails. Convert the single plugin entry's source from "./" to the github object form. The ecc plugin lives at the repo root, so github (whole-repo) is the correct object source; no path or git-subdir is needed. All other fields (name, version, description, ...) are left at current main's values. Adds a regression assertion in tests/plugin-manifest.test.js pinning the source to exactly { source: 'github', repo: 'affaan-m/ECC' } via deepStrictEqual, per maintainer review, so an incomplete or partial object form cannot satisfy it. --- .claude-plugin/marketplace.json | 5 ++++- tests/plugin-manifest.test.js | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 0e81da30d8..abb544677c 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -10,7 +10,10 @@ "plugins": [ { "name": "ecc", - "source": "./", + "source": { + "source": "github", + "repo": "affaan-m/ECC" + }, "description": "Harness-native ECC operator layer - 67 agents, 281 skills, 94 legacy command shims, reusable hooks, rules, selective install profiles, and production-ready workflows for Claude Code, Codex, OpenCode, Cursor, and related agent harnesses", "version": "2.1.0", "author": { diff --git a/tests/plugin-manifest.test.js b/tests/plugin-manifest.test.js index 0121cfb73c..9f8f319a5c 100644 --- a/tests/plugin-manifest.test.js +++ b/tests/plugin-manifest.test.js @@ -250,6 +250,20 @@ test('claude marketplace.json plugin version matches package.json', () => { assert.strictEqual(claudeMarketplace.plugins[0].version, expectedVersion); }); +test('claude marketplace.json plugin source is a remote-sync object, not a string (#2542)', () => { + // Claude Cowork's REMOTE marketplace sync (server-side clone) rejects string + // sources such as "./" with: + // External plugin sources must be objects with a 'source' field + // (github, url, or git-subdir). + // Local `/plugin marketplace add` tolerates the string shorthand, which is why + // the CLI succeeds while Cowork fails. Keep the object form so remote sync works. + const src = claudeMarketplace.plugins[0].source; + assert.ok(src && typeof src === 'object' && !Array.isArray(src), `Expected plugins[0].source to be an object, got: ${JSON.stringify(src)}`); + // Assert the exact expected remote-sync object so an incomplete/partial object + // form (missing `repo`, wrong `source`, or extra keys) cannot satisfy the test. + assert.deepStrictEqual(src, { source: 'github', repo: 'affaan-m/ECC' }, `Expected plugins[0].source to be exactly { source: 'github', repo: 'affaan-m/ECC' }, got: ${JSON.stringify(src)}`); +}); + // ── Codex plugin manifest ───────────────────────────────────────────────────── // Per official docs: https://platform.openai.com/docs/codex/plugins // - .codex-plugin/plugin.json is the required manifest