commit f3a58da0d25da346e2a0be8fdde466d3d83879cf
parent 5a4ce934d63d1ca5cd106e5d32c8121f95fd8f39
Author: andrewlaack-collab <andrew.laack@imbue.com>
Date: Wed, 25 Feb 2026 22:59:01 -0600
improved messaging for codex / cc missing errors (#154)
* improved messaging for codex / cc missing
* FormatS
* Renaming
* Better messaging
* Safer messaging
---------
Co-authored-by: Andrew Laack <andrew@laack.co>
Diffstat:
6 files changed, 28 insertions(+), 49 deletions(-)
diff --git a/vet/cli/main.py b/vet/cli/main.py
@@ -25,6 +25,7 @@ from vet.cli.config.schema import ModelsConfig
from vet.formatters import OUTPUT_FIELDS
from vet.formatters import OUTPUT_FORMATS
from vet.formatters import validate_output_fields
+from vet.imbue_core.agents.agent_api.errors import AgentCLINotFoundError
from vet.imbue_core.data_types import AgentHarnessType
from vet.imbue_core.data_types import IssueCode
from vet.imbue_core.data_types import get_valid_issue_code_values
@@ -628,6 +629,9 @@ def main(argv: list[str] | None = None) -> int:
conversation_history=conversation_history,
extra_context=extra_context,
)
+ except AgentCLINotFoundError as e:
+ print(f"vet: {e}", file=sys.stderr)
+ return 2
# TODO: This should be refactored so we only need to handle prompt too long errors when context is overfilled.
except (PromptTooLongError, BadAPIRequestError) as e:
if _is_context_overflow(e):
diff --git a/vet/imbue_core/agents/agent_api/claude/client.py b/vet/imbue_core/agents/agent_api/claude/client.py
@@ -114,27 +114,9 @@ class ClaudeCodeClient(RealAgentClient[ClaudeCodeOptions]):
node_installed = shutil.which("node") is not None
if not node_installed:
- raise AgentCLINotFoundError(
- "\n".join(
- [
- "Claude Code requires Node.js, which is not installed.",
- "Install Node.js from: https://nodejs.org/",
- "\nAfter installing Node.js, install Claude Code:",
- " npm install -g @anthropic-ai/claude-code",
- ]
- )
- )
+ raise AgentCLINotFoundError("Claude Code CLI not found. Node.js is required but not installed.")
- raise AgentCLINotFoundError(
- "\n".join(
- [
- "Claude Code not found. Install with:",
- " npm install -g @anthropic-ai/claude-code",
- "\nIf already installed locally, try:",
- ' export PATH="$HOME/node_modules/.bin:$PATH"',
- ]
- )
- )
+ raise AgentCLINotFoundError("Claude Code CLI not found. Ensure it is installed and available on your PATH.")
@classmethod
def _build_cli_cmd(cls, options: ClaudeCodeOptions) -> list[str]:
diff --git a/vet/imbue_core/agents/agent_api/codex/client.py b/vet/imbue_core/agents/agent_api/codex/client.py
@@ -96,29 +96,9 @@ class CodexClient(RealAgentClient[CodexOptions]):
npm_installed = shutil.which("npm") is not None
if not node_installed or not npm_installed:
- raise AgentCLINotFoundError(
- "\n".join(
- [
- "Codex CLI requires Node.js and npm, which may not be installed.",
- "Install Node.js from: https://nodejs.org/",
- "\nAfter installing Node.js, install Codex CLI:",
- " npm install -g @openai/codex",
- ]
- )
- )
-
- raise AgentCLINotFoundError(
- "\n".join(
- [
- "Codex CLI not found. Install with:",
- " npm install -g @openai/codex",
- "\nOr via Homebrew:",
- " brew install codex",
- "\nIf already installed locally, try:",
- ' export PATH="$HOME/node_modules/.bin:$PATH"',
- ]
- )
- )
+ raise AgentCLINotFoundError("Codex CLI not found. Node.js and npm are required but may not be installed.")
+
+ raise AgentCLINotFoundError("Codex CLI not found. Ensure it is installed and available on your PATH.")
@classmethod
def _build_cli_cmd(cls, options: CodexOptions) -> list[str]:
diff --git a/vet/issue_identifiers/agentic_issue_collation.py b/vet/issue_identifiers/agentic_issue_collation.py
@@ -158,9 +158,12 @@ def collate_issues_with_agent(
combined_issues_string,
guides_by_issue_code,
)
- claude_response = generate_response_from_agent(collation_prompt, options)
- assert claude_response is not None
- response_text, collation_messages = claude_response
+ agent_response = generate_response_from_agent(collation_prompt, options)
+ if agent_response is None:
+ raise RuntimeError(
+ "Agentic issue collation failed: no response received from agent CLI." " Re-run with --verbose for details."
+ )
+ response_text, collation_messages = agent_response
collation_raw_messages = tuple(json.dumps(message.model_dump()) for message in collation_messages)
collation_invocation_info = extract_invocation_info_from_messages(collation_messages)
diff --git a/vet/issue_identifiers/common.py b/vet/issue_identifiers/common.py
@@ -21,6 +21,7 @@ from vet.imbue_core.agents.agent_api.data_types import AgentResultMessage
from vet.imbue_core.agents.agent_api.data_types import AgentTextBlock
from vet.imbue_core.agents.agent_api.data_types import AgentToolName
from vet.imbue_core.agents.agent_api.data_types import READ_ONLY_TOOLS
+from vet.imbue_core.agents.agent_api.errors import AgentCLINotFoundError
from vet.imbue_core.agents.llm_apis.anthropic_data_types import AnthropicCachingInfo
from vet.imbue_core.agents.llm_apis.data_types import CostedLanguageModelResponse
from vet.imbue_core.async_monkey_patches import log_exception
@@ -228,6 +229,8 @@ def generate_response_from_agent(prompt: str, options: AgentOptions) -> tuple[st
assistant_messages.append(message)
elif isinstance(message, AgentResultMessage):
result_message = message
+ except AgentCLINotFoundError:
+ raise
except Exception as e:
log_exception(e, "Agent API call failed")
return None
diff --git a/vet/issue_identifiers/harnesses/agentic.py b/vet/issue_identifiers/harnesses/agentic.py
@@ -14,6 +14,7 @@ from loguru import logger
from vet.imbue_core.agents.agent_api.data_types import AgentMessage
from vet.imbue_core.agents.agent_api.data_types import AgentOptions
+from vet.imbue_core.agents.agent_api.errors import AgentCLINotFoundError
from vet.imbue_core.async_monkey_patches import log_exception
from vet.imbue_core.data_types import AgenticPhase
from vet.imbue_core.data_types import IssueCode
@@ -269,6 +270,8 @@ class _AgenticIssueIdentifier(IssueIdentifier[CommitInputs]):
for task in concurrent.futures.as_completed(tasks):
try:
result = task.result()
+ except AgentCLINotFoundError:
+ raise
except Exception as e:
log_exception(e, "Error processing issue type: {e}", e=e)
continue
@@ -297,9 +300,13 @@ class _AgenticIssueIdentifier(IssueIdentifier[CommitInputs]):
return IssueIdentificationDebugInfo(llm_responses=tuple(llm_responses))
else:
prompt = self._get_prompt(project_context, config, identifier_inputs)
- claude_response = generate_response_from_agent(prompt, options)
- assert claude_response is not None
- response_text, messages = claude_response
+ agent_response = generate_response_from_agent(prompt, options)
+ if agent_response is None:
+ raise RuntimeError(
+ "Agentic issue identification failed: no response received from agent CLI."
+ " Re-run with --verbose for details."
+ )
+ response_text, messages = agent_response
message_dumps = tuple(json.dumps(message.model_dump()) for message in messages)
invocation_info = extract_invocation_info_from_messages(messages)