haematite-cli 0.21.1

The haematite command: serve a store, its door and Studio, answer MCP, query with jq, and host the store's assistant seat
Documentation
use std::fs;
use std::os::unix::fs::{PermissionsExt, symlink};
use std::path::Path;

use serde_json::{Value, json};

use super::{Fixture, TestResult, install};

fn installed_json(fixture: &Fixture, name: &str) -> Result<Value, Box<dyn std::error::Error>> {
    let path = fixture.bundle.join(name);
    assert!(path.is_file(), "bundle payload missing: {name}");
    assert_eq!(fs::metadata(&path)?.permissions().mode() & 0o777, 0o600);
    Ok(serde_json::from_slice(&fs::read(path)?)?)
}

#[test]
fn bundle_plugin_mcp_uses_existing_absolute_paths_under_the_canonical_destination() -> TestResult {
    let fixture = Fixture::new()?;
    let alias = fixture.directory.path().join("bundle-parent");
    symlink(
        fixture.bundle.parent().ok_or("bundle parent missing")?,
        &alias,
    )?;
    let report = install(&fixture.binary, &fixture.config, &alias.join("bundle"))?;
    assert_eq!(
        report.directory,
        fixture.bundle.to_str().ok_or("bundle path")?
    );
    let mcp = installed_json(&fixture, ".mcp.json")?;
    let server = &mcp["mcpServers"]["haematite"];
    assert_eq!(
        server,
        &json!({
            "command": fixture.bundle.join("bin/haematite"),
            "args": ["mcp", "--config", fixture.bundle.join("serve.json")]
        })
    );
    for value in [&server["command"], &server["args"][2]] {
        let path = Path::new(value.as_str().ok_or("MCP path missing")?);
        assert!(path.is_absolute());
        assert!(path.is_file());
    }
    Ok(())
}

#[test]
fn bundle_plugin_codex_manifest_registers_the_bundled_skills_and_mcp_file() -> TestResult {
    let fixture = Fixture::new()?;
    install(&fixture.binary, &fixture.config, &fixture.bundle)?;
    let plugin = installed_json(&fixture, ".codex-plugin/plugin.json")?;
    assert_eq!(plugin["name"], "haematite");
    assert_eq!(plugin["skills"], "./skills/");
    assert_eq!(plugin["mcpServers"], "./.mcp.json");
    assert!(fixture.bundle.join("skills/haematite/SKILL.md").is_file());
    assert!(fixture.bundle.join(".mcp.json").is_file());
    Ok(())
}

#[test]
fn bundle_plugin_claude_marketplace_points_at_its_own_directory() -> TestResult {
    let fixture = Fixture::new()?;
    install(&fixture.binary, &fixture.config, &fixture.bundle)?;
    let marketplace = installed_json(&fixture, ".claude-plugin/marketplace.json")?;
    let plugin: Value = serde_json::from_str(crate::mcp::schema::skill::MANIFEST)?;
    assert_eq!(
        marketplace,
        json!({
            "name": "haematite",
            "description": plugin["description"],
            "owner": {"name": "Ablative"},
            "plugins": [{
                "name": "haematite", "source": "./", "description": plugin["description"]
            }]
        })
    );
    Ok(())
}

#[test]
fn bundle_plugin_codex_marketplace_points_at_its_own_directory() -> TestResult {
    let fixture = Fixture::new()?;
    install(&fixture.binary, &fixture.config, &fixture.bundle)?;
    let marketplace = installed_json(&fixture, ".agents/plugins/marketplace.json")?;
    assert_eq!(
        marketplace,
        json!({
            "name": "haematite",
            "interface": {"displayName": "Haematite"},
            "plugins": [{
                "name": "haematite", "source": {"source": "local", "path": "./"},
                "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"},
                "category": "Productivity"
            }]
        })
    );
    Ok(())
}