Skip to content

🚀 DevOS AI V2: CLI AI Codebase Analyzer#1

Merged
dsk-dev-ai merged 1 commit into
mainfrom
devos-v2
Apr 5, 2026
Merged

🚀 DevOS AI V2: CLI AI Codebase Analyzer#1
dsk-dev-ai merged 1 commit into
mainfrom
devos-v2

Conversation

@dsk-dev-ai

@dsk-dev-ai dsk-dev-ai commented Apr 5, 2026

Copy link
Copy Markdown
Owner

🚀 DevOS AI V2

Features

  • Explain codebases using AI
  • Intelligent search
  • Debug assistant
  • Multi-LLM support

Architecture

CLI → Core → LLM → Agents

Notes

  • Clean modular system
  • Ready for open source contributions

Summary by Sourcery

Introduce a new CLI-driven AI assistant for analyzing codebases, searching code, and debugging errors using multiple LLM providers.

New Features:

  • Add pluggable LLM provider module with OpenRouter, Google Gemini, and auto-failover routing.
  • Add core engine to rank and extract key source files into a structured analysis prompt for the LLM.
  • Add search agent to find relevant code snippets for a query and have the LLM explain their functionality and location.
  • Add debug agent that reads an error file and guides root-cause analysis and fixes via the LLM.
  • Add CLI entrypoint supporting explain, search, and debug commands with optional model selection flags.

Enhancements:

  • Define parser utilities to discover and read relevant code files while ignoring common non-code and infrastructure directories.

Chores:

  • Remove the previous CLI script in favor of the new structured CLI architecture.

@sourcery-ai

sourcery-ai Bot commented Apr 5, 2026

Copy link
Copy Markdown

Reviewer's Guide

Introduces 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 flows

sequenceDiagram
    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
Loading

Updated class diagram for core, agents, and LLM provider modules

classDiagram
    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
Loading

File-Level Changes

Change Details Files
Add LLM provider abstraction with OpenRouter and Google Gemini support and a unified ask_llm interface, including auto-failover and basic response validation.
  • Load API keys from environment via python-dotenv and expose OPENROUTER_API_KEY and GOOGLE_API_KEY constants.
  • Implement ask_openrouter and ask_google helpers that call each provider’s chat/generative APIs with fixed models, handle timeouts, and parse JSON responses into plain text.
  • Introduce ask_llm wrapper that normalizes responses (trimming, minimum length), routes by model flag (openrouter, google, auto), includes simple provider failover in auto mode, and returns user-facing error strings for invalid/empty responses.
llm/provider.py
Implement repository parsing utilities to collect relevant source files and build a bounded context string for LLM prompts.
  • Define IGNORE_DIRS and EXTENSIONS to filter out non-code and noisy directories (e.g., .venv, node_modules, frontend) and restrict to key source file types (.py, .js, .ts, .tsx).
  • Implement get_code_files to recursively walk a repo path, pruning ignored directories and returning matching code file paths.
  • Implement read_files to read a small prefix of selected files (with max_chars limit) and concatenate them into a labeled context block suitable for LLM prompts.
core/parser.py
Add a core analysis engine that ranks code files by heuristic priority and constructs structured prompts for explaining a codebase.
  • Define PRIORITY keywords and rank_files to score and sort files based on filename patterns (e.g., main, app, engine, cli, api, core, llm).
  • Implement build_context to select the top-ranked files from the repo and assemble their contents via read_files, producing the analysis context for the LLM.
  • Implement build_prompt to wrap context and a question into a strict, structured system prompt describing rules, task, and required response sections (Purpose, Architecture, Key Components, Execution Flow).
core/engine.py
Add a search agent that performs text-based code search across the repo and uses the LLM to synthesize a technical explanation of results.
  • Use get_code_files to scan the repo, then open each file and collect up to five matching snippets around lines that contain the query (case-insensitive), including basic file/line metadata.
  • If no matches are found, return an explicit failure message instead of calling the LLM.
  • Build a detailed analysis prompt instructing the LLM to focus on actual code logic and provide a structured answer (relevant functionality, locations, behavior, and summary), then invoke ask_llm and post-process errors/empty responses.
agents/search_agent.py
Add a debug agent that sends an error file’s contents to the LLM for structured debugging guidance.
  • Read the specified error file, handling missing or empty files with explicit error messages.
  • Construct a concise prompt asking the LLM to identify root cause, explanation, step-by-step fix, and example fix based on the error text.
  • Call ask_llm with the prompt and return either the LLM’s answer or a failure message if no response is produced.
agents/debug_agent.py
Introduce a new CLI entrypoint that wires together explain, search, and debug flows, delegating computation to the engine and agents and supporting model selection.
  • Implement explain(path, model) to build repository context and an explanation prompt, then call ask_llm via the core engine.
  • Implement a main() function that parses commands and arguments from sys.argv, supports an optional --model flag with openrouter
google
Remove the previous CLI entrypoint script in favor of the new modular CLI.
  • Delete the old cli/devos.py file that previously served as the CLI interface (implementation not shown in this diff).
cli/devos.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@dsk-dev-ai
dsk-dev-ai merged commit b6ff789 into main Apr 5, 2026
2 checks passed

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 3 issues, and left some high level feedback:

  • 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.
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>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread llm/provider.py

return None

except:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
  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.

Comment thread agents/search_agent.py
if len(matches) >= 5:
break

except:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
  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).

Comment thread cli/main.py
Comment on lines +37 to +38
elif command == "search":
result = search_code(sys.argv[2], sys.argv[3], model)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant