diff --git a/clink/agents/base.py b/clink/agents/base.py index fface9c..4d821c3 100644 --- a/clink/agents/base.py +++ b/clink/agents/base.py @@ -6,6 +6,7 @@ import asyncio import logging import os import shlex +import shutil import tempfile import time from collections.abc import Sequence @@ -65,6 +66,17 @@ class BaseCLIAgent: # The runner simply executes the configured CLI command for the selected role. command = self._build_command(role=role) env = self._build_environment() + + # Resolve executable path for cross-platform compatibility (especially Windows) + executable_name = command[0] + resolved_executable = shutil.which(executable_name) + if resolved_executable is None: + raise CLIAgentError( + f"Executable '{executable_name}' not found in PATH for CLI '{self.client.name}'. " + f"Ensure the command is installed and accessible." + ) + command[0] = resolved_executable + sanitized_command = list(command) cwd = str(self.client.working_dir) if self.client.working_dir else None diff --git a/tests/test_clink_codex_agent.py b/tests/test_clink_codex_agent.py index e788ea6..cdee3cd 100644 --- a/tests/test_clink_codex_agent.py +++ b/tests/test_clink_codex_agent.py @@ -1,4 +1,5 @@ import asyncio +import shutil from pathlib import Path import pytest @@ -41,7 +42,11 @@ async def _run_agent_with_process(monkeypatch, agent, role, process): async def fake_create_subprocess_exec(*_args, **_kwargs): return process + def fake_which(executable_name): + return f"/usr/bin/{executable_name}" + monkeypatch.setattr(asyncio, "create_subprocess_exec", fake_create_subprocess_exec) + monkeypatch.setattr(shutil, "which", fake_which) return await agent.run(role=role, prompt="do something", files=[], images=[]) diff --git a/tests/test_clink_gemini_agent.py b/tests/test_clink_gemini_agent.py index e67ff2d..9165684 100644 --- a/tests/test_clink_gemini_agent.py +++ b/tests/test_clink_gemini_agent.py @@ -1,4 +1,5 @@ import asyncio +import shutil from pathlib import Path import pytest @@ -41,7 +42,11 @@ async def _run_agent_with_process(monkeypatch, agent, role, process): async def fake_create_subprocess_exec(*_args, **_kwargs): return process + def fake_which(executable_name): + return f"/usr/bin/{executable_name}" + monkeypatch.setattr(asyncio, "create_subprocess_exec", fake_create_subprocess_exec) + monkeypatch.setattr(shutil, "which", fake_which) return await agent.run(role=role, prompt="do something", files=[], images=[])