use crate::common::TestProject;
use anyhow::Result;
#[tokio::test]
async fn test_explicit_tool_in_transitive_dependency() -> Result<()> {
agpm_cli::test_utils::init_test_logging(None);
let project = TestProject::new().await?;
let community_repo = project.create_source_repo("community").await?;
community_repo
.add_resource(
"snippets/agents",
"backend-engineer-python",
r#"---
agpm:
templating: false
---
# Backend Engineer Best Practices - Python
- Follow PEP 8 style guide
- Use type hints
- Write comprehensive tests with pytest
- Document all public APIs with docstrings
"#,
)
.await?;
community_repo
.add_resource(
"snippets/agents",
"backend-engineer-rust",
r#"---
agpm:
templating: false
---
# Backend Engineer Best Practices - Rust
- Follow Rust idioms
- Use Result<T, E> for error handling
- Write comprehensive tests with #[test]
- Document all public APIs with /// comments
"#,
)
.await?;
community_repo
.add_resource(
"claude-code/agents",
"backend-engineer",
r#"---
name: backend-engineer
description: Backend development specialist
color: green
agpm:
version: "1.0.0"
templating: true
dependencies:
snippets:
- name: backend-engineer-base
path: ../../snippets/agents/backend-engineer-{{ agpm.project.language }}.md
version: "snippet-agent-backend-engineer-{{ agpm.project.language }}-^v1.0.0"
tool: agpm # EXPLICIT tool - should NOT inherit claude-code from parent
install: false
---
{{ agpm.deps.snippets.backend_engineer_base.content }}
## Additional Context
This agent is designed for Claude Code for {{ agpm.project.language }} development.
"#,
)
.await?;
community_repo.commit_all("Add backend resources with cross-tool dependency")?;
community_repo.tag_version("claude-code-agent-backend-engineer-v1.0.0")?;
community_repo.tag_version("snippet-agent-backend-engineer-python-v1.0.0")?;
community_repo.tag_version("snippet-agent-backend-engineer-rust-v1.0.0")?;
let source_url = community_repo.bare_file_url(project.sources_path()).await?;
let manifest = format!(
r#"[sources]
community = "{}"
[agents]
backend-engineer-python = {{ source = "community", path = "claude-code/agents/backend-engineer.md", version = "claude-code-agent-backend-engineer-^v1.0.0", filename = "backend-engineer-python.md", template_vars = {{ project = {{ language = "python" }} }} }}
backend-engineer-rust = {{ source = "community", path = "claude-code/agents/backend-engineer.md", version = "claude-code-agent-backend-engineer-^v1.0.0", filename = "backend-engineer-rust.md", template_vars = {{ project = {{ language = "rust" }} }} }}
"#,
source_url
);
project.write_manifest(&manifest).await?;
let output = project.run_agpm(&["install"])?;
if !output.success {
eprintln!("==== INSTALL FAILED ====");
eprintln!("STDERR:\n{}", output.stderr);
eprintln!("STDOUT:\n{}", output.stdout);
eprintln!("=======================");
}
assert!(
output.success,
"Install should succeed when transitive dependency has explicit tool.\n\
Before fix: Would fail because template context lookup inherited parent's tool \
(claude-code) instead of using explicit tool (agpm) from frontmatter.\n\
Stderr:\n{}",
output.stderr
);
let python_agent = tokio::fs::read_to_string(
project.project_path().join(".claude/agents/agpm/backend-engineer-python.md"),
)
.await?;
let rust_agent = tokio::fs::read_to_string(
project.project_path().join(".claude/agents/agpm/backend-engineer-rust.md"),
)
.await?;
assert!(
python_agent.contains("Backend Engineer Best Practices - Python"),
"Python variant should contain embedded python snippet content. Got:\n{}",
python_agent
);
assert!(
python_agent.contains("Follow PEP 8 style guide"),
"Python variant should contain specific python content"
);
assert!(
rust_agent.contains("Backend Engineer Best Practices - Rust"),
"Rust variant should contain embedded rust snippet content. Got:\n{}",
rust_agent
);
assert!(
rust_agent.contains("Follow Rust idioms"),
"Rust variant should contain specific rust content"
);
let lockfile_content = project.read_lockfile().await?;
assert!(
lockfile_content.contains(r#"name = "claude-code/agents/backend-engineer""#),
"Lockfile should contain parent agent"
);
assert!(
lockfile_content.contains(r#"tool = "claude-code""#),
"Parent agent should have tool = claude-code"
);
let variant_count =
lockfile_content.matches(r#"name = "claude-code/agents/backend-engineer""#).count();
assert!(
variant_count >= 2,
"Lockfile should have at least 2 variants of the agent (python and rust). Found {}",
variant_count
);
assert!(
lockfile_content.contains(r#"tool = "agpm""#),
"Lockfile should contain resources with tool = agpm (the transitive snippets)"
);
Ok(())
}