🚀 DevOS AI V2: CLI AI Codebase Analyzer#1
Conversation
Reviewer's GuideIntroduces a new modular CLI-driven AI codebase analysis system with multi-LLM support (OpenRouter/Google), including code discovery/parsing, contextual prompt building, search-based analysis, and error debugging agents, while removing the previous CLI entrypoint. Sequence diagram for CLI explain and search flowssequenceDiagram
actor Developer
participant CliMain
participant CoreEngine
participant CoreParser
participant SearchAgent
participant DebugAgent
participant LlmProvider
participant OpenRouter
participant GoogleGemini
Developer->>CliMain: run devos explain <path> --model
CliMain->>CoreEngine: build_context(path)
CoreEngine->>CoreParser: get_code_files(path)
CoreParser-->>CoreEngine: code_files
CoreEngine->>CoreEngine: rank_files(files)
CoreEngine->>CoreParser: read_files(top_files)
CoreParser-->>CoreEngine: context
CoreEngine-->>CliMain: context
CliMain->>CoreEngine: build_prompt(context, question)
CoreEngine-->>CliMain: prompt
CliMain->>LlmProvider: ask_llm(prompt, model)
alt model openrouter or auto first
LlmProvider->>OpenRouter: POST chat completion
OpenRouter-->>LlmProvider: response
else fallback to google
LlmProvider->>GoogleGemini: POST generateContent
GoogleGemini-->>LlmProvider: response
end
LlmProvider-->>CliMain: explanation
CliMain-->>Developer: print explanation
Developer->>CliMain: run devos search <path> "query" --model
CliMain->>SearchAgent: search_code(path, query, model)
SearchAgent->>CoreParser: get_code_files(path)
CoreParser-->>SearchAgent: code_files
SearchAgent->>SearchAgent: scan files, build context
SearchAgent->>LlmProvider: ask_llm(prompt, model)
LlmProvider-->>SearchAgent: analysis
SearchAgent-->>CliMain: result
CliMain-->>Developer: print search analysis
Updated class diagram for core, agents, and LLM provider modulesclassDiagram
class CliMain {
+explain(path, model)
+main()
}
class CoreEngine {
+rank_files(files)
+build_context(repo_path)
+build_prompt(context, question)
}
class CoreParser {
+get_code_files(repo_path)
+read_files(files, max_chars)
}
class LlmProvider {
+ask_openrouter(prompt)
+ask_google(prompt)
+ask_llm(prompt, model)
}
class SearchAgent {
+search_code(repo_path, query, model)
}
class DebugAgent {
+debug_error(file_path, model)
}
CliMain --> CoreEngine : uses
CliMain --> SearchAgent : uses
CliMain --> DebugAgent : uses
CliMain --> LlmProvider : uses
CoreEngine --> CoreParser : uses
CoreEngine --> LlmProvider : uses
SearchAgent --> CoreParser : uses
SearchAgent --> LlmProvider : uses
DebugAgent --> LlmProvider : uses
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 3 issues, and left some high level feedback:
- Several functions use bare
except:blocks (e.g., inllm/provider.py,agents/search_agent.py,core/parser.py), which can hide unexpected errors; consider catching specific exceptions and at least logging or surfacing the failure reason. - The
ask_llmAPI encodes error states as human-readable strings (with❌) and callers detect errors via substring checks; using a structured return type (e.g., result + error fields) or exceptions would make error handling more robust and less brittle. - The hard-coded ignore list and extensions in
core/parser.py(e.g., excludingapps,frontend,infrastructure) makes the analyzer opinionated; you may want to make these directories and file types configurable via CLI flags or environment variables for broader applicability.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Several functions use bare `except:` blocks (e.g., in `llm/provider.py`, `agents/search_agent.py`, `core/parser.py`), which can hide unexpected errors; consider catching specific exceptions and at least logging or surfacing the failure reason.
- The `ask_llm` API encodes error states as human-readable strings (with `❌`) and callers detect errors via substring checks; using a structured return type (e.g., result + error fields) or exceptions would make error handling more robust and less brittle.
- The hard-coded ignore list and extensions in `core/parser.py` (e.g., excluding `apps`, `frontend`, `infrastructure`) makes the analyzer opinionated; you may want to make these directories and file types configurable via CLI flags or environment variables for broader applicability.
## Individual Comments
### Comment 1
<location path="llm/provider.py" line_range="36" />
<code_context>
+ if len(matches) >= 5:
+ break
+
+ except:
+ continue
+
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Catch more specific exceptions and handle HTTP/JSON errors explicitly.
A bare `except` will hide all errors, including programmer mistakes like `TypeError`/`KeyError`, and turns HTTP/JSON failures into `None`, making them indistinguishable from a legitimate “no matches” case. Catch `requests.RequestException` and `ValueError` (for JSON) explicitly, and check `res.status_code` before `res.json()` so you can distinguish network/HTTP errors from valid but empty responses.
Suggested implementation:
```python
except (requests.RequestException, ValueError) as exc:
# Network/HTTP or JSON parsing error: skip this iteration but keep
# programmer errors (TypeError, KeyError, etc.) visible.
continue
```
1. Ensure `import requests` is present at the top of `llm/provider.py` so `requests.RequestException` is available.
2. In the same `try` block above this `except`, before calling `res.json()`, add an explicit status-code check, e.g.:
- `if res.status_code != 200: raise requests.RequestException(f"Unexpected status {res.status_code}")`
This will make HTTP failures flow into the new `except (requests.RequestException, ValueError)` branch instead of being silently treated as a successful “no matches” case.
3. If you want to propagate HTTP/JSON failures to callers instead of silently skipping them, you can re-raise after optional logging inside the `except` block instead of `continue`.
</issue_to_address>
### Comment 2
<location path="agents/search_agent.py" line_range="29" />
<code_context>
+ if len(matches) >= 5:
+ break
+
+ except:
+ continue
+
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Bare `except` hides all read errors when scanning files.
Catching every exception here will hide real read issues (e.g., permission, corruption, encoding) and make failures hard to diagnose. Instead, catch specific exceptions like `OSError`/`IOError` and `UnicodeDecodeError`, and log or record skipped files so these errors remain visible.
Suggested implementation:
```python
except (OSError, UnicodeDecodeError) as exc:
logging.warning("Skipping file %s due to read error: %s", file, exc)
continue
```
1. Ensure `logging` is imported at the top of `agents/search_agent.py`:
- `import logging`
2. Optionally configure logging (if not already configured elsewhere in the application) to ensure warnings are visible (e.g., basicConfig or the project’s logging setup).
</issue_to_address>
### Comment 3
<location path="cli/main.py" line_range="37-38" />
<code_context>
+ if command == "explain":
+ result = explain(sys.argv[2], model)
+
+ elif command == "search":
+ result = search_code(sys.argv[2], sys.argv[3], model)
+
+ elif command == "debug":
</code_context>
<issue_to_address>
**issue (bug_risk):** Argument parsing for `search` is brittle when `--model` is used or the query is missing.
This relies on the query always being at `sys.argv[3]`, so a call like `search path --model google "foo"` will pass `"--model"` as the query. And because there’s no check that `sys.argv[3]` exists, a missing query will raise `IndexError`. Consider using `argparse` or at least deriving the query index from the position of `--model` and validating that the required arguments are present before indexing.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
|
|
||
| return None | ||
|
|
||
| except: |
There was a problem hiding this comment.
suggestion (bug_risk): Catch more specific exceptions and handle HTTP/JSON errors explicitly.
A bare except will hide all errors, including programmer mistakes like TypeError/KeyError, and turns HTTP/JSON failures into None, making them indistinguishable from a legitimate “no matches” case. Catch requests.RequestException and ValueError (for JSON) explicitly, and check res.status_code before res.json() so you can distinguish network/HTTP errors from valid but empty responses.
Suggested implementation:
except (requests.RequestException, ValueError) as exc:
# Network/HTTP or JSON parsing error: skip this iteration but keep
# programmer errors (TypeError, KeyError, etc.) visible.
continue- Ensure
import requestsis present at the top ofllm/provider.pysorequests.RequestExceptionis available. - In the same
tryblock above thisexcept, before callingres.json(), add an explicit status-code check, e.g.:if res.status_code != 200: raise requests.RequestException(f"Unexpected status {res.status_code}")
This will make HTTP failures flow into the newexcept (requests.RequestException, ValueError)branch instead of being silently treated as a successful “no matches” case.
- If you want to propagate HTTP/JSON failures to callers instead of silently skipping them, you can re-raise after optional logging inside the
exceptblock instead ofcontinue.
| if len(matches) >= 5: | ||
| break | ||
|
|
||
| except: |
There was a problem hiding this comment.
suggestion (bug_risk): Bare except hides all read errors when scanning files.
Catching every exception here will hide real read issues (e.g., permission, corruption, encoding) and make failures hard to diagnose. Instead, catch specific exceptions like OSError/IOError and UnicodeDecodeError, and log or record skipped files so these errors remain visible.
Suggested implementation:
except (OSError, UnicodeDecodeError) as exc:
logging.warning("Skipping file %s due to read error: %s", file, exc)
continue- Ensure
loggingis imported at the top ofagents/search_agent.py:import logging
- Optionally configure logging (if not already configured elsewhere in the application) to ensure warnings are visible (e.g., basicConfig or the project’s logging setup).
| elif command == "search": | ||
| result = search_code(sys.argv[2], sys.argv[3], model) |
There was a problem hiding this comment.
issue (bug_risk): Argument parsing for search is brittle when --model is used or the query is missing.
This relies on the query always being at sys.argv[3], so a call like search path --model google "foo" will pass "--model" as the query. And because there’s no check that sys.argv[3] exists, a missing query will raise IndexError. Consider using argparse or at least deriving the query index from the position of --model and validating that the required arguments are present before indexing.
🚀 DevOS AI V2
Features
Architecture
CLI → Core → LLM → Agents
Notes
Summary by Sourcery
Introduce a new CLI-driven AI assistant for analyzing codebases, searching code, and debugging errors using multiple LLM providers.
New Features:
Enhancements:
Chores: