diff --git a/nori-rs/acp/Cargo.toml b/nori-rs/acp/Cargo.toml index 9a0897fba..26a80f797 100644 --- a/nori-rs/acp/Cargo.toml +++ b/nori-rs/acp/Cargo.toml @@ -38,6 +38,7 @@ diffy = { workspace = true } chrono = { workspace = true } uuid = { workspace = true, features = ["v4"] } sha2 = { workspace = true } +which = { workspace = true } [target.'cfg(unix)'.dependencies] libc = { workspace = true } diff --git a/nori-rs/acp/src/config/types/tests.rs b/nori-rs/acp/src/config/types/tests.rs index 0bdfa1093..e8e286b62 100644 --- a/nori-rs/acp/src/config/types/tests.rs +++ b/nori-rs/acp/src/config/types/tests.rs @@ -1023,7 +1023,7 @@ name = "Claude Code" slug = "claude-code" [agents.distribution.npx] -package = "@zed-industries/claude-agent-acp" +package = "@agentclientprotocol/claude-agent-acp" "#, ) .unwrap(); @@ -1033,7 +1033,7 @@ package = "@zed-industries/claude-agent-acp" assert!(config.agents[0].distribution.npx.is_some()); assert_eq!( config.agents[0].distribution.npx.as_ref().unwrap().package, - "@zed-industries/claude-agent-acp" + "@agentclientprotocol/claude-agent-acp" ); } @@ -1130,7 +1130,7 @@ name = "Claude Code" slug = "claude-code" [agents.distribution.npx] -package = "@zed-industries/claude-agent-acp" +package = "@agentclientprotocol/claude-agent-acp" [[agents]] name = "Kimi" @@ -1217,7 +1217,7 @@ fn test_agent_distribution_resolve_rejects_multiple() { fn test_agent_distribution_resolve_npx() { let dist = AgentDistributionToml { npx: Some(PackageDistribution { - package: "@zed-industries/claude-agent-acp".to_string(), + package: "@agentclientprotocol/claude-agent-acp".to_string(), args: vec![], }), ..Default::default() @@ -1225,7 +1225,7 @@ fn test_agent_distribution_resolve_npx() { let resolved = dist.resolve().unwrap(); assert!(matches!(resolved, ResolvedDistribution::Npx { .. })); if let ResolvedDistribution::Npx { package, args } = resolved { - assert_eq!(package, "@zed-industries/claude-agent-acp"); + assert_eq!(package, "@agentclientprotocol/claude-agent-acp"); assert!(args.is_empty()); } } diff --git a/nori-rs/acp/src/registry.rs b/nori-rs/acp/src/registry.rs index 20fe0d8e0..3ea0ef2ca 100644 --- a/nori-rs/acp/src/registry.rs +++ b/nori-rs/acp/src/registry.rs @@ -97,8 +97,10 @@ impl AgentKind { /// Get the ACP adapter package name for launching this agent pub fn acp_package(&self) -> &'static str { match self { - // Claude and Codex use Zed's ACP adapters - AgentKind::ClaudeCode => "@zed-industries/claude-agent-acp", + // @latest forces bunx to resolve the new scope instead of a stale + // @zed-industries cache entry with the same unscoped package name. + AgentKind::ClaudeCode => "@agentclientprotocol/claude-agent-acp@latest", + // Codex uses Zed's ACP adapter AgentKind::Codex => "@zed-industries/codex-acp", // Gemini has native ACP support AgentKind::Gemini => "@google/gemini-cli", @@ -703,7 +705,7 @@ pub fn get_agent_config(agent_name: &str) -> Result { let package_manager = detect_preferred_package_manager(); let (command, args) = match agent { - // Claude and Codex use Zed's ACP adapters + // Claude and Codex use external ACP adapters AgentKind::ClaudeCode | AgentKind::Codex => ( package_manager.command().to_string(), vec![agent.acp_package().to_string()], @@ -718,12 +720,28 @@ pub fn get_agent_config(agent_name: &str) -> Result { ), }; + // Workaround: the v0.30.0+ Claude ACP adapter resolves its native + // binary via require.resolve() on platform-specific optional deps. + // On glibc Linux, bunx installs both musl and glibc variants; the + // adapter tries musl first, require.resolve succeeds (file exists), + // but execution fails (no musl loader). Point CLAUDE_CODE_EXECUTABLE + // at the system binary to bypass the broken resolution. + let mut env = HashMap::new(); + if agent == AgentKind::ClaudeCode + && let Ok(path) = which::which("claude") + { + env.insert( + "CLAUDE_CODE_EXECUTABLE".to_string(), + path.to_string_lossy().to_string(), + ); + } + return Ok(AcpAgentConfig { agent, provider_slug: agent.slug().to_string(), command, args, - env: HashMap::new(), + env, provider_info: AcpProviderInfo { name: format!("{} ACP", agent.display_name()), ..Default::default() @@ -999,11 +1017,10 @@ mod tests { "Command should be npx or bunx, got: {}", config.command ); - // Uses Zed's ACP adapter assert!( config .args - .contains(&"@zed-industries/claude-agent-acp".to_string()) + .contains(&"@agentclientprotocol/claude-agent-acp@latest".to_string()) ); assert_eq!(config.provider_info.name, "Claude Code ACP"); }