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
5 changes: 1 addition & 4 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,10 @@
"name": "Microsoft",
"email": "opensource@microsoft.com"
},
"metadata": {
"pluginRoot": "./plugins"
},
"plugins": [
{
"name": "conductor",
"source": "./conductor",
"source": "./plugins/conductor",
"description": "Validate, run, and execute Conductor multi-agent YAML workflows. Adds the conductor skill that knows the workflow schema, CLI commands, and execution model.",
"author": { "name": "Microsoft" },
"homepage": "https://github.com/microsoft/conductor",
Expand Down
4 changes: 4 additions & 0 deletions changelog.d/587.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
**Claude Code plugin marketplace**: `/plugin install conductor@conductor` no
longer fails with `Source path does not exist`. The marketplace entry now points
at `./plugins/conductor`, relative to the repository root as Claude Code
resolves it, and the unused `metadata.pluginRoot` is removed.
22 changes: 22 additions & 0 deletions tests/test_plugins/test_marketplace.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import pytest

from conductor.plugins.errors import PluginNotFoundError, PluginSourceError
from conductor.plugins.manifest import is_plugin_root
from conductor.plugins.marketplace import find_marketplace_manifest, read_marketplace

from .conftest import make_marketplace, make_plugin
Expand Down Expand Up @@ -430,3 +431,24 @@ def test_plugin_key_narrowing_finds_a_plugin_published_only_in_the_secondary_cat
marketplace = read_marketplace(tmp_path, name="acme", plugin="solo")

assert marketplace.plugins == {"solo": tmp_path / "dist" / "copilot" / "solo"}


class TestShippedMarketplace:
"""The marketplace this repository ships to Claude Code must install."""

def test_claude_catalog_sources_exist_under_the_repo_root(self) -> None:
# Claude Code resolves a relative `source` from the marketplace root
# (the directory holding `.claude-plugin/`) and ignores
# `metadata.pluginRoot` for a `./` path, so a source written relative
# to `pluginRoot` fails at install time with "Source path does not
# exist" (issue #587).
repo_root = Path(__file__).resolve().parents[2]
catalog = json.loads(
(repo_root / ".claude-plugin" / "marketplace.json").read_text(encoding="utf-8")
)

assert catalog["plugins"]
for entry in catalog["plugins"]:
source = entry["source"]
assert isinstance(source, str) and source.startswith("./"), entry["name"]
assert is_plugin_root(repo_root / source), f"{entry['name']}: {source}"
Loading