vet

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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:
Mvet/cli/main.py | 4++++
Mvet/imbue_core/agents/agent_api/claude/client.py | 22++--------------------
Mvet/imbue_core/agents/agent_api/codex/client.py | 26+++-----------------------
Mvet/issue_identifiers/agentic_issue_collation.py | 9++++++---
Mvet/issue_identifiers/common.py | 3+++
Mvet/issue_identifiers/harnesses/agentic.py | 13++++++++++---
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)