use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::sync::atomic::{AtomicU32, Ordering};
static COUNTER: AtomicU32 = AtomicU32::new(0);
struct Sandbox {
base: PathBuf,
source: PathBuf,
mind_home: PathBuf,
claude_home: PathBuf,
}
struct Run {
stdout: String,
stderr: String,
success: bool,
}
impl Sandbox {
fn build(name: &str, with_fixture: bool) -> Sandbox {
let n = COUNTER.fetch_add(1, Ordering::SeqCst);
let base =
std::env::temp_dir().join(format!("mind-install-items-{}-{n}", std::process::id()));
let _ = std::fs::remove_dir_all(&base);
let source = base.join(name);
let sb = Sandbox {
base: base.clone(),
source: source.clone(),
mind_home: base.join("mind"),
claude_home: base.join("claude"),
};
if with_fixture {
write_file(
&source.join("skills/review/SKILL.md"),
"---\nname: review\ndescription: Review the diff\n---\n# review\n",
);
write_file(
&source.join("agents/dev.md"),
"---\nname: dev\ndescription: Dev agent\n---\n# dev\n",
);
write_file(
&source.join("rules/style.md"),
"---\ndescription: Style rule\n---\n# style\n",
);
} else {
write_file(&source.join("README.md"), "# registry\n");
}
git_init(&source);
sb
}
fn new(name: &str) -> Sandbox {
Sandbox::build(name, true)
}
fn bare(name: &str) -> Sandbox {
Sandbox::build(name, false)
}
fn source_spec(&self) -> String {
self.source.to_string_lossy().into_owned()
}
fn mind(&self, args: &[&str]) -> Run {
let mut cmd = Command::new(env!("CARGO_BIN_EXE_mind"));
cmd.args(args)
.env("MIND_HOME", &self.mind_home)
.env("CLAUDE_HOME", &self.claude_home)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.stdin(Stdio::null());
let out = cmd.output().expect("run mind");
Run {
stdout: String::from_utf8_lossy(&out.stdout).into_owned(),
stderr: String::from_utf8_lossy(&out.stderr).into_owned(),
success: out.status.success(),
}
}
fn write_and_commit(&self, rel: &str, contents: &str) {
write_file(&self.source.join(rel), contents);
git_commit(&self.source);
}
}
impl Drop for Sandbox {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.base);
}
}
fn write_file(path: &Path, contents: &str) {
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
std::fs::write(path, contents).unwrap();
}
fn git_init(dir: &Path) {
for args in [
vec!["-c", "init.defaultBranch=main", "init", "-q"],
vec!["config", "user.email", "t@t"],
vec!["config", "user.name", "t"],
vec!["add", "-A"],
vec!["commit", "-qm", "initial"],
] {
let status = Command::new("git")
.args(&args)
.current_dir(dir)
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.expect("run git");
assert!(status.success(), "git {args:?} in {dir:?}");
}
}
fn git_commit(dir: &Path) {
for args in [vec!["add", "-A"], vec!["commit", "-qm", "fixture"]] {
let _ = Command::new("git")
.args(&args)
.current_dir(dir)
.stdout(Stdio::null())
.stderr(Stdio::null())
.status();
}
}
#[test]
fn install_items_subset_is_offered_not_others() {
let nested = Sandbox::new("nested");
let registry = Sandbox::bare("registry");
registry.write_and_commit(
"mind.toml",
&format!(
"[discover]\nsources = [{{ source = {:?}, install-items = [\"skill:review\"] }}]\n",
nested.source_spec()
),
);
let r = registry.mind(&["meld", ®istry.source_spec(), "--yes"]);
assert!(r.success, "meld failed: {} {}", r.stdout, r.stderr);
assert!(
registry.claude_home.join("skills/review").exists(),
"skill:review (in install-items) must be installed: {:?}",
registry.claude_home
);
assert!(
!registry.claude_home.join("agents/dev.md").exists(),
"agent:dev (not in install-items) must NOT be installed"
);
assert!(
!registry.claude_home.join("rules/style.md").exists(),
"rule:style (not in install-items) must NOT be installed"
);
let probe = registry.mind(&["probe"]);
assert!(
probe.stdout.contains("agent:dev"),
"agent:dev must still be available via probe: {}",
probe.stdout
);
}
#[test]
fn install_items_other_items_remain_available_and_learnable() {
let nested = Sandbox::new("nested");
let registry = Sandbox::bare("registry");
registry.write_and_commit(
"mind.toml",
&format!(
"[discover]\nsources = [{{ source = {:?}, install-items = [\"skill:review\"] }}]\n",
nested.source_spec()
),
);
let r = registry.mind(&["meld", ®istry.source_spec(), "--yes"]);
assert!(r.success, "meld failed: {}", r.stderr);
let learn = registry.mind(&["learn", "agent:dev"]);
assert!(
learn.success,
"explicitly learning a non-listed item must succeed: {} {}",
learn.stdout, learn.stderr
);
assert!(
registry.claude_home.join("agents/dev.md").exists(),
"agent:dev must be installed after explicit `learn`"
);
}
#[test]
fn install_items_yes_flag_installs_subset_non_interactively() {
let nested = Sandbox::new("nested");
let registry = Sandbox::bare("registry");
registry.write_and_commit(
"mind.toml",
&format!(
"[discover]\nsources = [{{ source = {:?}, install-items = [\"skill:review\", \"agent:dev\"] }}]\n",
nested.source_spec()
),
);
let r = registry.mind(&["meld", ®istry.source_spec(), "--yes"]);
assert!(r.success, "meld --yes failed: {}", r.stderr);
assert!(
registry.claude_home.join("skills/review").exists(),
"skill:review must be installed with --yes"
);
assert!(
registry.claude_home.join("agents/dev.md").exists(),
"agent:dev must be installed with --yes"
);
assert!(
!registry.claude_home.join("rules/style.md").exists(),
"rule:style (not in install-items) must not be installed"
);
}
#[test]
fn install_items_link_only_installs_nothing() {
let nested = Sandbox::new("nested");
let registry = Sandbox::bare("registry");
registry.write_and_commit(
"mind.toml",
&format!(
"[discover]\nsources = [{{ source = {:?}, install-items = [\"skill:review\"] }}]\n",
nested.source_spec()
),
);
let r = registry.mind(&["meld", ®istry.source_spec(), "--link-only"]);
assert!(r.success, "meld --link-only failed: {}", r.stderr);
assert!(
!registry.claude_home.join("skills/review").exists(),
"--link-only must not install any items, even when install-items is set"
);
let sources = registry.mind(&["recall", "--sources"]);
assert!(
sources.stdout.contains("nested"),
"nested source must be registered under --link-only: {}",
sources.stdout
);
}
#[test]
fn install_items_empty_installs_nothing() {
let nested = Sandbox::new("nested");
let registry = Sandbox::bare("registry");
registry.write_and_commit(
"mind.toml",
&format!(
"[discover]\nsources = [{{ source = {:?}, install-items = [] }}]\n",
nested.source_spec()
),
);
let r = registry.mind(&["meld", ®istry.source_spec(), "--yes"]);
assert!(r.success, "meld failed: {}", r.stderr);
assert!(
!registry.claude_home.join("skills/review").exists(),
"install-items = [] must not install any items"
);
assert!(
!registry.claude_home.join("agents/dev.md").exists(),
"install-items = [] must not install any items"
);
let probe = registry.mind(&["probe"]);
assert!(
probe.stdout.contains("skill:review"),
"items must still be available after install-items = []: {}",
probe.stdout
);
}
#[test]
fn recursive_overrides_install_items_installs_all() {
let nested = Sandbox::new("nested");
let registry = Sandbox::bare("registry");
registry.write_and_commit(
"mind.toml",
&format!(
"[discover]\nsources = [{{ source = {:?}, install-items = [\"skill:review\"] }}]\n",
nested.source_spec()
),
);
let r = registry.mind(&["meld", ®istry.source_spec(), "--recursive", "--yes"]);
assert!(r.success, "meld --recursive failed: {}", r.stderr);
assert!(
registry.claude_home.join("skills/review").exists(),
"skill:review must be installed under --recursive"
);
assert!(
registry.claude_home.join("agents/dev.md").exists(),
"agent:dev must be installed under --recursive (overrides install-items)"
);
assert!(
registry.claude_home.join("rules/style.md").exists(),
"rule:style must be installed under --recursive (overrides install-items)"
);
}
#[test]
fn install_items_non_tty_without_yes_installs_nothing_but_notes() {
let nested = Sandbox::new("nested");
let registry = Sandbox::bare("registry");
registry.write_and_commit(
"mind.toml",
&format!(
"[discover]\nsources = [{{ source = {:?}, install-items = [\"skill:review\"] }}]\n",
nested.source_spec()
),
);
let r = registry.mind(&["meld", ®istry.source_spec()]);
assert!(r.success, "meld failed: {} {}", r.stdout, r.stderr);
assert!(
!registry.claude_home.join("skills/review").exists(),
"non-TTY meld without --yes must NOT install the subset"
);
let combined = format!("{}{}", r.stdout, r.stderr);
assert!(
combined.contains("learn") && combined.contains("nested"),
"a note should point at `mind learn` for the nested source: {combined}"
);
}
#[test]
fn install_items_remeld_honors_subset() {
let nested = Sandbox::new("nested");
let registry = Sandbox::bare("registry");
registry.write_and_commit(
"mind.toml",
&format!(
"[discover]\nsources = [{{ source = {:?}, install-items = [\"skill:review\"] }}]\n",
nested.source_spec()
),
);
let first = registry.mind(&["meld", ®istry.source_spec(), "--link-only"]);
assert!(first.success, "initial meld failed: {}", first.stderr);
assert!(
!registry.claude_home.join("skills/review").exists(),
"nothing should be installed after --link-only meld"
);
let second = registry.mind(&["meld", ®istry.source_spec(), "--yes"]);
assert!(
second.success,
"re-meld failed: {} {}",
second.stdout, second.stderr
);
assert!(
registry.claude_home.join("skills/review").exists(),
"re-meld must honor install-items and install the subset"
);
assert!(
!registry.claude_home.join("agents/dev.md").exists(),
"re-meld must not install items outside install-items"
);
assert!(
!registry.claude_home.join("rules/style.md").exists(),
"re-meld must not install items outside install-items"
);
}
#[test]
fn install_items_unknown_ref_errors_at_meld() {
let nested = Sandbox::new("nested");
let registry = Sandbox::bare("registry");
registry.write_and_commit(
"mind.toml",
&format!(
"[discover]\nsources = [{{ source = {:?}, install-items = [\"skill:nonexistent\"] }}]\n",
nested.source_spec()
),
);
let r = registry.mind(&["meld", ®istry.source_spec()]);
assert!(
!r.success,
"meld with an unknown install-items ref must fail"
);
let combined = format!("{}{}", r.stdout, r.stderr);
assert!(
combined.contains("nonexistent"),
"error must name the bad ref: {combined}"
);
}
#[test]
fn install_items_wrong_kind_ref_errors_at_meld() {
let nested = Sandbox::new("nested");
let registry = Sandbox::bare("registry");
registry.write_and_commit(
"mind.toml",
&format!(
"[discover]\nsources = [{{ source = {:?}, install-items = [\"agent:review\"] }}]\n",
nested.source_spec()
),
);
let r = registry.mind(&["meld", ®istry.source_spec()]);
assert!(
!r.success,
"a wrong-kind ref (agent:review when review is a skill) must fail at meld"
);
let combined = format!("{}{}", r.stdout, r.stderr);
assert!(
combined.contains("review"),
"error must name the bad ref: {combined}"
);
}
#[test]
fn install_items_one_bad_ref_in_list_aborts_and_installs_nothing() {
let nested = Sandbox::new("nested");
let registry = Sandbox::bare("registry");
registry.write_and_commit(
"mind.toml",
&format!(
"[discover]\nsources = [{{ source = {:?}, install-items = [\"skill:review\", \"skill:ghost\"] }}]\n",
nested.source_spec()
),
);
let r = registry.mind(&["meld", ®istry.source_spec(), "--yes"]);
assert!(
!r.success,
"a list containing one unknown ref must fail the meld"
);
let combined = format!("{}{}", r.stdout, r.stderr);
assert!(
combined.contains("ghost"),
"error must name the bad ref: {combined}"
);
assert!(
!registry.claude_home.join("skills/review").exists(),
"a bad ref aborts before install; the valid item must not be installed"
);
}
#[test]
fn install_items_prefixed_name_ref_is_rejected() {
let nested = Sandbox::new("nested");
let registry = Sandbox::bare("registry");
registry.write_and_commit(
"mind.toml",
&format!(
"[discover]\nsources = [{{ source = {:?}, as = \"pfx\", install-items = [\"skill:pfx:review\"] }}]\n",
nested.source_spec()
),
);
let r = registry.mind(&["meld", ®istry.source_spec()]);
assert!(
!r.success,
"a ref written with the prefix (skill:pfx:review) is not a bare name and must fail"
);
let combined = format!("{}{}", r.stdout, r.stderr);
assert!(
combined.contains("pfx:review"),
"error must name the bad ref: {combined}"
);
}
#[test]
fn install_items_bare_ref_accepted_despite_prefix_in_effect() {
let nested = Sandbox::new("nested");
let registry = Sandbox::bare("registry");
registry.write_and_commit(
"mind.toml",
&format!(
"[discover]\nsources = [{{ source = {:?}, as = \"pfx\", install-items = [\"skill:review\"] }}]\n",
nested.source_spec()
),
);
let r = registry.mind(&["meld", ®istry.source_spec(), "--link-only"]);
assert!(
r.success,
"a bare ref must be accepted when a prefix is in effect: {} {}",
r.stdout, r.stderr
);
}
#[test]
fn install_items_prefix_applied_at_install_time() {
let nested = Sandbox::new("nested");
let registry = Sandbox::bare("registry");
registry.write_and_commit(
"mind.toml",
&format!(
"[discover]\nsources = [{{ source = {:?}, as = \"pfx\", install-items = [\"skill:review\"] }}]\n",
nested.source_spec()
),
);
let r = registry.mind(&["meld", ®istry.source_spec(), "--yes"]);
assert!(r.success, "meld failed: {}", r.stderr);
assert!(
registry.claude_home.join("skills/pfx:review").exists(),
"prefixed name pfx:review must be installed: {:?}",
registry.claude_home
);
assert!(
!registry.claude_home.join("skills/review").exists(),
"bare name review must not exist when prefix is in effect"
);
}
#[test]
fn install_true_and_install_items_is_toml_error() {
let nested = Sandbox::new("nested");
let registry = Sandbox::bare("registry");
registry.write_and_commit(
"mind.toml",
&format!(
"[discover]\nsources = [{{ source = {:?}, install = true, install-items = [\"skill:review\"] }}]\n",
nested.source_spec()
),
);
let r = registry.mind(&["meld", ®istry.source_spec()]);
assert!(
!r.success,
"meld with install = true + non-empty install-items must fail"
);
let combined = format!("{}{}", r.stdout, r.stderr);
assert!(
combined.contains("mutually exclusive"),
"error must mention 'mutually exclusive': {combined}"
);
}
#[test]
fn install_true_with_empty_install_items_is_not_an_error() {
let nested = Sandbox::new("nested");
let registry = Sandbox::bare("registry");
registry.write_and_commit(
"mind.toml",
&format!(
"[discover]\nsources = [{{ source = {:?}, install = true, install-items = [] }}]\n",
nested.source_spec()
),
);
let r = registry.mind(&["meld", ®istry.source_spec()]);
assert!(
r.success,
"meld with install = true + empty install-items must succeed: {}",
r.stderr
);
}
#[test]
fn install_items_governs_when_present_regardless_of_install_flag() {
let nested_a = Sandbox::new("nested-a"); let nested_b = Sandbox::bare("nested-b"); nested_b.write_and_commit(
"skills/special/SKILL.md",
"---\nname: special\ndescription: Special skill\n---\n# special\n",
);
let registry = Sandbox::bare("registry");
registry.write_and_commit(
"mind.toml",
&format!(
"[discover]\nsources = [\n {{ source = {:?}, install-items = [\"skill:review\"] }},\n {{ source = {:?}, install = true }}\n]\n",
nested_a.source_spec(),
nested_b.source_spec()
),
);
let r = registry.mind(&["meld", ®istry.source_spec(), "--yes"]);
assert!(r.success, "meld failed: {}", r.stderr);
assert!(
registry.claude_home.join("skills/review").exists(),
"skill:review (in install-items) must be installed"
);
assert!(
!registry.claude_home.join("agents/dev.md").exists(),
"agent:dev (not in install-items for nested-a) must not be installed"
);
assert!(
registry.claude_home.join("skills/special").exists(),
"skill:special from nested-b (install = true) must be installed"
);
}