1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
use std::path::Path;

use color_eyre::eyre::{Result, WrapErr, bail, eyre};
use serde::Deserialize;

const DEFAULT_PATH: &str = ".claude-plugin/marketplace.json";

#[derive(Debug, Deserialize)]
pub struct Marketplace {
    pub plugins: Vec<MarketplacePlugin>,
}

#[derive(Debug, Deserialize)]
pub struct MarketplacePlugin {
    pub name: String,
    pub source: PluginSource,
}

/// A plugin source in marketplace.json.
///
/// Can be a relative path string (e.g. `"./plugins/my-plugin"`) or an object
/// describing an external source (github, git url, npm, pip). Pinch only
/// supports relative paths since it clones the marketplace repo directly.
#[derive(Debug, Deserialize)]
#[serde(untagged)]
pub enum PluginSource {
    /// Relative path within the marketplace repo.
    Path(String),
    /// External source (github, url, npm, pip) — not supported by pinch.
    External(serde_json::Value),
}

/// Find the source path for a plugin by name in the marketplace.json.
pub fn find_plugin_source(
    repo_path: &Path,
    plugin_name: &str,
    marketplace_override: Option<&str>,
) -> Result<String> {
    let manifest_path = repo_path.join(marketplace_override.unwrap_or(DEFAULT_PATH));

    let contents = fs_err::read_to_string(&manifest_path)?;

    let marketplace: Marketplace = serde_json::from_str(&contents)
        .wrap_err_with(|| format!("failed to parse {}", manifest_path.display()))?;

    let plugin = marketplace
        .plugins
        .iter()
        .find(|mp| mp.name == plugin_name)
        .ok_or_else(|| {
            eyre!(
                "plugin '{}' not found in {}",
                plugin_name,
                manifest_path.display()
            )
        })?;

    match &plugin.source {
        PluginSource::Path(path) => Ok(path.clone()),
        PluginSource::External(value) => {
            bail!(
                "plugin '{}': external source {} — pinch only supports relative path sources",
                plugin_name,
                value
            );
        }
    }
}