Skip to content

refactor(main): extract repository analyze/process services#132

Merged
Mehdi-Bl merged 3 commits into
mainfrom
feat/backend-extract-repo-analyze-process-services
Feb 14, 2026
Merged

refactor(main): extract repository analyze/process services#132
Mehdi-Bl merged 3 commits into
mainfrom
feat/backend-extract-repo-analyze-process-services

Conversation

@Mehdi-Bl

@Mehdi-Bl Mehdi-Bl commented Feb 14, 2026

Copy link
Copy Markdown
Contributor

Summary

  • extract repository analysis logic from into
  • extract repository processing/output assembly logic into
  • keep IPC handlers in thin by delegating to services
  • add dedicated unit coverage for repository processing service and register it in

Validation

  • npm run lint
  • npm test -- --runInBand
  • npm run build:ts
  • make sonar
  • Sonar tool check: 0 open issues for project

Notes

  • external critical review identified and fixed a TypeScript build blocker ( self-reference) before push

Summary by Sourcery

Extract repository analysis and processing logic into dedicated services and wire them into the main IPC handlers.

Enhancements:

  • Introduce a repository analysis service to encapsulate config parsing, gitignore handling, and file tokenization logic.
  • Introduce a repository processing service to encapsulate repository output assembly, including headers, tree views, file content processing, and footers.
  • Thin main-process IPC handlers by delegating repository analyze/process responsibilities to the new services and supporting structured logging via callbacks.

Documentation:

  • Extend the tests catalog to document coverage for the new repository processing service tests.

Tests:

  • Add unit tests for the repository processing service covering markdown/xml output, tree-view generation, token visibility, and path/missing-file handling.
  • Update the test catalog documentation to include the new repository processing unit tests.

Summary by CodeRabbit

  • Refactor

    • Reorganized repository analysis and processing workflows into dedicated service modules, centralizing logic for improved maintainability and code organization.
  • Tests

    • Added comprehensive unit tests for repository processing functionality, validating file handling, tree structure generation, multiple export formats, and boundary conditions.

@coderabbitai

coderabbitai Bot commented Feb 14, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@Mehdi-Bl has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 20 minutes and 16 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📝 Walkthrough

Walkthrough

This PR refactors the main IPC handler in src/main/index.ts by extracting large inline repository analysis and processing logic into two new public services: analyzeRepository and processRepository. The changes consolidate duplicated logic, update related IPC types, and introduce new test coverage for the processing service.

Changes

Cohort / File(s) Summary
Main IPC Handler Refactoring
src/main/index.ts
Removed ~293 lines of inline analysis and processing logic, including FileAnalyzer, ContentProcessor instantiation, and tree-building helpers. Replaced repo:analyze and repo:process handlers to delegate to new analyzeRepository and processRepository services via callback-based logging.
Repository Analysis Service
src/main/services/repository-analyzer.ts
New public service exporting analyzeRepository function. Handles YAML config parsing, gitignore pattern resolution, file validation, binary detection, token counting, and per-file analysis. Returns filesInfo array, totalTokens, and skippedBinaryFiles count.
Repository Processing Service
src/main/services/repository-processing.ts
New public service exporting processRepository function. Orchestrates file processing, tree/header/footer generation, format handling (XML, Markdown), token counting, and output assembly. Includes internal helpers for path tree construction, tree rendering, and repository metadata generation.
IPC Type Updates
src/types/ipc
Removed public references to ConfigObject and FileInfo from AnalyzeRepositoryOptions and related types. Updated type signatures to reflect delegation to new services.
Test Coverage
tests/catalog.md, tests/unit/main/repository-processing.test.ts
Added 150+ lines of unit tests covering markdown/XML output formats, tree view generation, token counting, path boundary validation, and missing file handling. Updated test catalog to track new test file.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 Hoppy refactoring day!
Analysis and processing hops away,
From tangled inline vines to services neat,
The code now dances to a cleaner beat!

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: extracting repository analysis and processing logic into dedicated services, which aligns with the primary refactoring objective.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/backend-extract-repo-analyze-process-services

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sourcery-ai

sourcery-ai Bot commented Feb 14, 2026

Copy link
Copy Markdown

Reviewer's Guide

Refactors repository analysis and processing logic out of the main IPC handler into dedicated service modules, adds unit coverage for the new processing service, and wires the IPC layer to delegate to these services with injectable logging hooks.

Sequence diagram for repo:analyze IPC delegating to repository analyzer service

sequenceDiagram
  actor Renderer
  participant IpcMain
  participant RepositoryAnalyzer as RepositoryAnalyzerService
  participant GitignoreParser
  participant FileAnalyzer
  participant TokenCounter

  Renderer->>IpcMain: ipcMain.handle repo:analyze
  IpcMain->>RepositoryAnalyzer: analyzeRepository(rootPath, configContent, selectedFiles, gitignoreParser, onWarn, onInfo)

  RepositoryAnalyzer->>RepositoryAnalyzer: yaml.parse(configContent) -> ConfigObject
  RepositoryAnalyzer->>TokenCounter: new TokenCounter()
  RepositoryAnalyzer->>GitignoreParser: parseGitignore(rootPath)
  GitignoreParser-->>RepositoryAnalyzer: GitignorePatterns

  RepositoryAnalyzer->>FileAnalyzer: new FileAnalyzer(config, tokenCounter, gitignoreOptions)

  loop for each selected file
    RepositoryAnalyzer->>RepositoryAnalyzer: path.resolve(rootPath, filePath)
    RepositoryAnalyzer->>RepositoryAnalyzer: isPathWithinRoot(rootPath, resolvedPath)?
    alt outside root
      RepositoryAnalyzer-->>IpcMain: onWarn Skipping file outside current root directory
    else inside root
      RepositoryAnalyzer->>RepositoryAnalyzer: getRelativePath(resolvedPath, rootPath)
      RepositoryAnalyzer->>RepositoryAnalyzer: isBinaryFile(resolvedPath)?
      alt binary file
        RepositoryAnalyzer-->>IpcMain: onInfo Binary file detected
        RepositoryAnalyzer->>RepositoryAnalyzer: push FileInfo isBinary true
      else text file
        RepositoryAnalyzer->>FileAnalyzer: shouldProcessFile(relativePath)?
        alt should process
          RepositoryAnalyzer->>FileAnalyzer: analyzeFile(resolvedPath)
          FileAnalyzer-->>RepositoryAnalyzer: tokenCount | null
          alt tokenCount not null
            RepositoryAnalyzer->>RepositoryAnalyzer: push FileInfo tokens
          end
        end
      end
    end
  end

  RepositoryAnalyzer->>RepositoryAnalyzer: sort(filesInfo) by tokens desc
  RepositoryAnalyzer-->>IpcMain: AnalyzeRepositoryResult
  IpcMain-->>Renderer: filesInfo, totalTokens, skippedBinaryFiles
Loading

Sequence diagram for repo:process IPC delegating to repository processing service

sequenceDiagram
  actor Renderer
  participant IpcMain
  participant RepositoryProcessing as RepositoryProcessingService
  participant ContentProcessor
  participant TokenCounter

  Renderer->>IpcMain: ipcMain.handle repo:process
  IpcMain->>RepositoryProcessing: processRepository(rootPath, filesInfo, treeView, options, onWarn, onInfo)

  RepositoryProcessing->>TokenCounter: new TokenCounter()
  RepositoryProcessing->>ContentProcessor: new ContentProcessor(tokenCounter)
  RepositoryProcessing->>RepositoryProcessing: resolveRepositoryProcessingOptions(options)
  RepositoryProcessing-->>IpcMain: onInfo Processing with options

  RepositoryProcessing->>RepositoryProcessing: normalizedFilesInfo = filesInfo ?? []
  RepositoryProcessing->>RepositoryProcessing: buildRepositoryHeader(processingOptions, treeView, normalizedFilesInfo)

  loop for each fileInfo in normalizedFilesInfo
    alt fileInfo.path is falsy
      RepositoryProcessing-->>IpcMain: onWarn Skipping invalid file info entry
    else valid path
      RepositoryProcessing->>RepositoryProcessing: processRepositoryFile(rootPath, fileInfo, contentProcessor, processingOptions, onWarn)
      alt processedFile is null
        RepositoryProcessing->>RepositoryProcessing: skippedFiles++
      else processedFile not null
        RepositoryProcessing->>RepositoryProcessing: append processedFile.content
        RepositoryProcessing->>RepositoryProcessing: totalTokens += tokenCount
        RepositoryProcessing->>RepositoryProcessing: processedFiles++
      end
    end
  end

  RepositoryProcessing->>RepositoryProcessing: buildRepositoryFooter(processingOptions, summary)
  RepositoryProcessing-->>IpcMain: ProcessRepositoryResult
  IpcMain-->>Renderer: content, exportFormat, totals, filesInfo
Loading

Updated class diagram for repository analyzer and processing services

classDiagram
  class RepositoryAnalyzerService {
    <<service>>
    +AnalyzeRepositoryResult analyzeRepository(rootPath, configContent, selectedFiles, gitignoreParser, onWarn, onInfo)
  }

  class GitignoreParserLike {
    <<interface>>
    +GitignorePatterns parseGitignore(rootPath)
  }

  class FileAnalyzer {
    +FileAnalyzer(config, tokenCounter, options)
    +boolean shouldProcessFile(relativePath)
    +number~null analyzeFile(resolvedFilePath)
  }

  class TokenCounter {
    +TokenCounter()
    +number countTokens(content)
  }

  class RepositoryProcessingService {
    <<service>>
    +ProcessRepositoryResult processRepository(rootPath, filesInfo, treeView, options, onWarn, onInfo)
    -RepositoryProcessingOptions resolveRepositoryProcessingOptions(options)
    -string buildRepositoryHeader(processingOptions, treeView, filesInfo)
    -ProcessedRepositoryFileResult processRepositoryFile(rootPath, fileInfo, contentProcessor, processingOptions, onWarn)
    -string buildRepositoryFooter(processingOptions, summary)
  }

  class ContentProcessor {
    +ContentProcessor(tokenCounter)
    +string~null processFile(fullPath, relativePath, options)
  }

  class RepositoryProcessingOptions {
    +boolean showTokenCount
    +boolean includeTreeView
    +string exportFormat
  }

  class FileInfo {
    +string path
    +number tokens
    +boolean isBinary
  }

  class AnalyzeRepositoryResult {
    +FileInfo[] filesInfo
    +number totalTokens
    +number skippedBinaryFiles
  }

  class ProcessRepositoryResult {
    +string content
    +string exportFormat
    +number totalTokens
    +number processedFiles
    +number skippedFiles
    +FileInfo[] filesInfo
  }

  RepositoryAnalyzerService --> GitignoreParserLike : uses
  RepositoryAnalyzerService --> FileAnalyzer : creates
  RepositoryAnalyzerService --> TokenCounter : creates
  RepositoryAnalyzerService --> AnalyzeRepositoryResult : returns
  RepositoryAnalyzerService --> FileInfo : populates

  RepositoryProcessingService --> ContentProcessor : creates
  RepositoryProcessingService --> TokenCounter : creates
  RepositoryProcessingService --> RepositoryProcessingOptions : configures
  RepositoryProcessingService --> FileInfo : consumes
  RepositoryProcessingService --> ProcessRepositoryResult : returns

  ContentProcessor --> TokenCounter : uses
Loading

File-Level Changes

Change Details Files
Extract repository analysis logic into a dedicated service and delegate the IPC handler to it.
  • Introduce a repository-analyzer service that encapsulates YAML config parsing, gitignore handling, file analysis, and token aggregation.
  • Replace inline analysis logic in the 'repo:analyze' ipcMain handler with a call to the new analyzeRepository service, passing root path, config content, selected files, gitignore parser, and logging callbacks.
  • Preserve path-security checks and file sorting behavior while centralizing binary-file detection and skipping into the service.
src/main/index.ts
src/main/services/repository-analyzer.ts
Extract repository processing/output assembly into a dedicated service and delegate the IPC handler to it.
  • Move tree-view generation, header/footer construction, and per-file processing logic into a new repository-processing service.
  • Replace the inline 'repo:process' ipcMain handler implementation with a thin wrapper around processRepository that forwards root path, files info, tree view, options, and logging callbacks.
  • Refine tree-building helpers (path tree construction and rendering) into smaller pure functions and keep path-boundary and missing-file checks within the service.
src/main/index.ts
src/main/services/repository-processing.ts
Add targeted unit tests and testing catalog updates for the repository-processing service.
  • Create unit tests for the repository-processing service covering markdown output, tree view behavior, XML token attributes, and handling of invalid/missing/out-of-root file entries.
  • Update the test catalog to reference the new repository-processing tests and classify them under the stress-safety baseline list.
tests/unit/main/repository-processing.test.ts
tests/catalog.md

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

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Review Summary by Qodo

Extract repository analyze and process services

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Extract repository analysis logic into dedicated service module
• Extract repository processing/output assembly into dedicated service module
• Simplify IPC handlers by delegating to extracted services
• Add comprehensive unit test coverage for repository processing service
Diagram
flowchart LR
  A["IPC Handler<br/>repo:analyze"] -->|delegates| B["analyzeRepository<br/>service"]
  C["IPC Handler<br/>repo:process"] -->|delegates| D["processRepository<br/>service"]
  B -->|uses| E["FileAnalyzer<br/>TokenCounter"]
  D -->|uses| F["ContentProcessor<br/>Export Format"]
  G["Unit Tests"] -->|covers| D
Loading

Grey Divider

File Changes

1. src/main/index.ts Refactoring +25/-293

Delegate analysis/processing to service modules

• Removed inline repository analysis and processing logic from IPC handlers
• Removed imports for analysis utilities (FileAnalyzer, export-format helpers, yaml)
• Added imports for new service modules (analyzeRepository, processRepository)
• Simplified repo:analyze handler to delegate to analyzeRepository service
• Simplified repo:process handler to delegate to processRepository service
• Removed helper functions (generateTreeView, buildRepositoryHeader, buildRepositoryFooter,
 processRepositoryFile, resolveRepositoryProcessingOptions)

src/main/index.ts


2. src/main/services/repository-analyzer.ts ✨ Enhancement +104/-0

New repository analysis service module

• New service module extracting repository analysis logic
• Implements analyzeRepository function accepting root path, config, selected files, and gitignore
 parser
• Handles file analysis with token counting and binary file detection
• Supports optional logging callbacks (onWarn, onInfo)
• Returns FileInfo array with token counts and skipped binary file count

src/main/services/repository-analyzer.ts


3. src/main/services/repository-processing.ts ✨ Enhancement +262/-0

New repository processing service module

• New service module extracting repository processing and output assembly logic
• Implements processRepository function for generating formatted repository content
• Supports markdown and XML export formats with configurable options
• Includes tree view generation with path-based file structure rendering
• Handles file processing with validation, error handling, and optional logging callbacks
• Returns processed content with format, token counts, and file statistics

src/main/services/repository-processing.ts


View more (2)
4. tests/unit/main/repository-processing.test.ts 🧪 Tests +129/-0

Unit tests for repository processing service

• New comprehensive unit test suite for repository processing service
• Tests markdown output generation with invalid/missing/out-of-root file handling
• Tests tree view section generation when enabled
• Tests XML export format with token count attributes enabled by default
• Tests XML export format with token count attributes disabled
• Uses temporary file system for isolated test execution

tests/unit/main/repository-processing.test.ts


5. tests/catalog.md 📝 Documentation +2/-0

Update test catalog documentation

• Added entry for new repository-processing test file in unit tests section
• Added reference to repository-processing test in content/token pipeline changes section
• Documents test coverage for output assembly, tree/header/footer behavior, and path validation

tests/catalog.md


Grey Divider

Qodo Logo

@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello @Mehdi-Bl, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly refactors the main Electron process by extracting complex repository analysis and processing functionalities into separate, modular services. This improves the maintainability and testability of the codebase by adhering to the single responsibility principle, making the ipcMain handlers leaner and easier to understand. The change enhances the overall architecture by clearly separating concerns.

Highlights

  • Repository Analysis Logic Extraction: The core logic for analyzing repositories, including parsing configuration, handling gitignore patterns, and counting tokens, has been moved from src/main/index.ts into a new dedicated service: src/main/services/repository-analyzer.ts.
  • Repository Processing Logic Extraction: The logic responsible for processing repository files and assembling the final output (e.g., generating tree views, formatting content for Markdown or XML) has been extracted from src/main/index.ts into a new service: src/main/services/repository-processing.ts.
  • IPC Handler Simplification: The ipcMain handlers in src/main/index.ts for repository analysis and processing have been significantly simplified. They now act as thin wrappers, delegating their responsibilities to the newly created analyzeRepository and processRepository services.
  • New Unit Test Coverage: Dedicated unit tests have been added for the new repository-processing service to ensure its functionality, covering various output formats, tree view generation, and error handling scenarios.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • src/main/index.ts
    • Removed direct implementation of repository analysis and processing logic.
    • Delegated repository analysis to analyzeRepository service.
    • Delegated repository processing to processRepository service.
    • Removed numerous utility imports and helper functions that are now encapsulated within the new services.
  • src/main/services/repository-analyzer.ts
    • Added new file to encapsulate repository analysis logic, including config parsing, gitignore handling, and file token counting.
  • src/main/services/repository-processing.ts
    • Added new file to encapsulate repository processing and output assembly logic, including tree view generation and content formatting for different export formats.
  • tests/catalog.md
    • Updated documentation to include the new unit test for repository-processing.test.ts.
  • tests/unit/main/repository-processing.test.ts
    • Added new unit tests for the repository-processing service, covering markdown output, skipping invalid/missing files, tree view generation, and XML token attribute handling.
Activity
  • An external critical review identified and fixed a TypeScript build blocker related to self-references before the pull request was pushed.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@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 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location> `src/main/services/repository-analyzer.ts:30-32` </location>
<code_context>
+  onInfo?: (message: string) => void;
+};
+
+const EMPTY_GITIGNORE_PATTERNS: GitignorePatterns = {
+  excludePatterns: [],
+  includePatterns: [],
+};
+
</code_context>

<issue_to_address>
**issue (bug_risk):** Using a shared EMPTY_GITIGNORE_PATTERNS object may be risky if FileAnalyzer mutates it

Previously each call received a new `gitignorePatterns` object; now calls share `EMPTY_GITIGNORE_PATTERNS` when `use_gitignore === false`. If `FileAnalyzer` or downstream code mutates this object, state will leak between invocations. To keep the old behavior and avoid hidden coupling, either clone `EMPTY_GITIGNORE_PATTERNS` per call or construct a new object instead of reusing the shared one.
</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 src/main/services/repository-analyzer.ts Outdated

@gemini-code-assist gemini-code-assist 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.

Code Review

This is a solid refactoring that significantly improves the structure and maintainability of the main process logic. By extracting the repository analysis and processing concerns into dedicated services, the IPC handlers in src/main/index.ts have become much cleaner and easier to understand. The introduction of unit tests for the new repository-processing service is also a great addition.

I've found one high-severity bug in the new tree-generation logic within repository-processing.ts that can lead to incorrect tree view output in certain scenarios. I've provided a detailed comment with a suggested fix. I've also suggested an additional test case to cover this scenario and prevent future regressions.

Overall, this is a high-quality pull request that moves the codebase in the right direction. Once the identified issue is addressed, this will be ready to merge.

Comment on lines +49 to +72
const upsertPathPart = (tree: PathTree, part: string, isLeaf: boolean): PathTree | null => {
const existing = tree[part];
if (existing !== undefined) {
return existing;
}

const nextValue = isLeaf ? null : {};
tree[part] = nextValue;
return nextValue;
};

const addFilePathToTree = (pathTree: PathTree, filePath: string): void => {
const parts = filePath.split('/');
let currentLevel: PathTree = pathTree;

for (const [index, part] of parts.entries()) {
const isLeaf = index === parts.length - 1;
const nextLevel = upsertPathPart(currentLevel, part, isLeaf);
if (isLeaf || nextLevel === null) {
continue;
}
currentLevel = nextLevel;
}
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The current implementation for building the path tree has a logic issue when handling file paths where one is a prefix of another (e.g., a file a/b and another file a/b/c.txt). Because the files are sorted alphabetically, a/b is processed first, marking b as a leaf node. When a/b/c.txt is processed, the logic fails to convert b into a directory and add c.txt underneath it, resulting in an incorrect tree structure.

I suggest updating both upsertPathPart and addFilePathToTree to correctly handle this scenario by allowing a leaf node to be promoted to a directory node if needed.

const upsertPathPart = (tree: PathTree, part: string, isLeaf: boolean): PathTree | null => {
  const existing = tree[part];

  if (existing === null && !isLeaf) {
    // Path was a file, but now it's a directory path segment. Convert it.
    tree[part] = {};
    return tree[part] as PathTree;
  }

  if (existing !== undefined) {
    return existing;
  }

  const nextValue = isLeaf ? null : {};
  tree[part] = nextValue;
  return nextValue;
};

const addFilePathToTree = (pathTree: PathTree, filePath: string): void => {
  const parts = filePath.split('/');
  let currentLevel: PathTree = pathTree;

  for (const [index, part] of parts.entries()) {
    const isLeaf = index === parts.length - 1;
    const nextLevel = upsertPathPart(currentLevel, part, isLeaf);

    if (isLeaf || nextLevel === null) {
      // We've reached a leaf or a conflict we can't resolve. Stop descending.
      break;
    }

    currentLevel = nextLevel;
  }
};

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in 6c42908. The tree builder now promotes an existing leaf node to a directory when a deeper segment is encountered, and traversal flow was updated to avoid misplaced descendants.

};
};

describe('repository-processing service', () => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

These are great tests! To make them even more robust, consider adding a test case for the tree view generation that specifically handles paths where one is a prefix of another (e.g., files at a/b and a/b/c.txt). This would help catch the edge case in the tree building logic I mentioned in another comment and prevent future regressions.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added in 6c42908: a regression test (keeps nested tree structure stable when a path prefix appears as a file entry) in tests/unit/main/repository-processing.test.ts.

@github-actions

github-actions Bot commented Feb 14, 2026

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues found.

Scanned Files

None

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e6482fb1c4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +51 to +52
if (existing !== undefined) {
return existing;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Handle leaf-to-branch upgrades in path tree

When includeTreeView is enabled, this helper keeps an existing null node unchanged and returns it, so a later nested path cannot descend into that segment. With filesInfo like src and src/index.js, the nested file is attached at the wrong level and the rendered tree is incorrect. The previous logic upgraded a leaf to an object in this case; please handle existing === null && !isLeaf so prefix-collision inputs still produce a valid hierarchy.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in 239e6a2. Tree nodes now support leaf-to-branch promotion and the regression is covered by keeps nested tree structure stable when a path prefix appears as a file entry.

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Feb 14, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (1) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Prototype-pollutable path tree 🐞 Bug ⛨ Security
Description
repository-processing builds a tree using a plain object keyed by path segments derived from
repository file/folder names; segments like __proto__ / constructor can mutate prototypes or
traverse into Object.prototype, leading to crashes or unpredictable output when rendering the tree
view. This can be triggered by opening a repo containing such folder/file names.
Code

src/main/services/repository-processing.ts[R49-71]

+const upsertPathPart = (tree: PathTree, part: string, isLeaf: boolean): PathTree | null => {
+  const existing = tree[part];
+  if (existing !== undefined) {
+    return existing;
+  }
+
+  const nextValue = isLeaf ? null : {};
+  tree[part] = nextValue;
+  return nextValue;
+};
+
+const addFilePathToTree = (pathTree: PathTree, filePath: string): void => {
+  const parts = filePath.split('/');
+  let currentLevel: PathTree = pathTree;
+
+  for (const [index, part] of parts.entries()) {
+    const isLeaf = index === parts.length - 1;
+    const nextLevel = upsertPathPart(currentLevel, part, isLeaf);
+    if (isLeaf || nextLevel === null) {
+      continue;
+    }
+    currentLevel = nextLevel;
+  }
Evidence
The tree is a plain object ({}) and is written to with tree[part] = … where part comes from
filePath.split('/'). filePath ultimately comes from filesystem paths (directory entries) and is
only normalized for slashes, not sanitized against special object keys.

src/main/services/repository-processing.ts[37-57]
src/main/services/repository-processing.ts[60-71]
src/main/services/repository-analyzer.ts[60-74]
src/utils/filter-utils.ts[10-13]
src/main/services/directory-tree.ts[192-200]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`repository-processing` constructs a directory tree using `{}` nodes and assigns child nodes via `tree[part]`, where `part` comes from repository path segments. Path segments like `__proto__` / `constructor` can interact with JavaScript object prototype semantics, causing prototype pollution or traversal into `Object.prototype`, which can crash or corrupt tree rendering.
### Issue Context
File paths originate from filesystem directory entries and are only normalized for slash direction (not sanitized). This means a repository can legitimately contain a folder named `__proto__`.
### Fix Focus Areas
- src/main/services/repository-processing.ts[37-110]
- src/utils/filter-utils.ts[10-13]
### Suggested approach
- Prefer `Map&amp;amp;lt;string, PathTreeNode&amp;amp;gt;` for nodes, or ensure every node is created with `Object.create(null)`.
- Optionally add a small guard that rejects or escapes path segments equal to `__proto__`, `prototype`, or `constructor` before insertion.
- Add a unit test that includes a `filesInfo` entry like `__proto__/x.txt` and asserts tree rendering is stable and does not pollute global objects.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Unstructured console.* logging 📘 Rule violation ⛨ Security
Description
The new repository analyze/process flow logs via console.info/console.warn with free-form
strings and arbitrary metadata, which is not structured (e.g., JSON) for auditing/monitoring. This
can also inadvertently leak sensitive local context (e.g., file paths/options) into logs.
Code

src/main/services/repository-processing.ts[R203-207]

+  const tokenCounter = new TokenCounter();
+  const contentProcessor = new ContentProcessor(tokenCounter);
+  const processingOptions = resolveRepositoryProcessingOptions(options);
+  onInfo?.('Processing with options:', processingOptions);
+
Evidence
PR Compliance ID 5 requires structured logs and prohibits sensitive data exposure in logs. The new
service emits an onInfo log with a plain-text message plus a raw object (processingOptions), and
the IPC handler wires onInfo/onWarn directly to console.*, producing unstructured output.

Rule 5: Generic: Secure Logging Practices
src/main/services/repository-processing.ts[203-207]
src/main/index.ts[276-286]
src/main/index.ts[308-318]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The repository analyze/process refactor introduces/continues unstructured logging via `console.info` / `console.warn`, including logging arbitrary metadata objects. Compliance requires structured (e.g., JSON) logs and avoiding sensitive data in logs.
## Issue Context
- `processRepository()` emits `onInfo?.(&amp;amp;#x27;Processing with options:&amp;amp;#x27;, processingOptions)`.
- `src/main/index.ts` wires `onInfo`/`onWarn` to `console.*`, resulting in unstructured output.
## Fix Focus Areas
- src/main/services/repository-processing.ts[203-207]
- src/main/index.ts[276-286]
- src/main/index.ts[308-318]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Tree view sort can throw 🐞 Bug ⛯ Reliability
Description
When includeTreeView is enabled and the caller passes malformed filesInfo (e.g., null
entries), tree generation can throw during sorting because the comparator accesses a.path before
any null/shape checks. This contradicts later logic that explicitly tolerates invalid entries and
can make repo:process fail early.
Code

src/main/services/repository-processing.ts[R74-83]

+const buildPathTree = (filesInfo: FileInfo[]): PathTree => {
+  const pathTree: PathTree = {};
+  const sortedFiles = [...filesInfo].sort((a, b) => a.path.localeCompare(b.path));
+
+  for (const file of sortedFiles) {
+    if (!file?.path) {
+      continue;
+    }
+    addFilePathToTree(pathTree, file.path);
+  }
Evidence
buildRepositoryHeader may call generateTreeView(filesInfo) before the main processing loop
filters invalid entries. buildPathTree sorts via a.path.localeCompare(b.path), which will throw
if a/b are null/undefined or lack path.

src/main/services/repository-processing.ts[122-124]
src/main/services/repository-processing.ts[74-83]
src/main/services/repository-processing.ts[208-223]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
With `includeTreeView` enabled, `buildRepositoryHeader()` can call `generateTreeView(filesInfo)` before `processRepository()` filters malformed `filesInfo` entries. `buildPathTree()` sorts using `a.path.localeCompare(...)`, which will throw if any entry is `null`/missing `path`.
### Issue Context
`processRepository()` already treats invalid entries as skippable, but that logic happens after header construction.
### Fix Focus Areas
- src/main/services/repository-processing.ts[74-86]
- src/main/services/repository-processing.ts[195-224]
### Suggested approach
- Build a `validFilesInfo` array first:
- filter: `filesInfo.filter((f): f is FileInfo =&amp;amp;gt; Boolean(f?.path))`
- optionally normalize `path` (e.g., replace backslashes)
- Use `validFilesInfo` for header tree generation and main loop iteration.
- Add a unit test that sets `options: { includeTreeView: true }` and passes a `null` file entry, asserting the service returns successfully and includes the tree view section.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Advisory comments

✅ 4. Leaf collision misbuilds tree 🐞 Bug ✓ Correctness
Description
addFilePathToTree mishandles cases where an existing segment is a leaf (null) but later inputs
require it to be a directory, causing subsequent segments to be added at the wrong level. While
uncommon with well-formed filesystem-derived inputs, it reduces robustness against
inconsistent/hostile filesInfo payloads.
Code

src/main/services/repository-processing.ts[R60-71]

+const addFilePathToTree = (pathTree: PathTree, filePath: string): void => {
+  const parts = filePath.split('/');
+  let currentLevel: PathTree = pathTree;
+
+  for (const [index, part] of parts.entries()) {
+    const isLeaf = index === parts.length - 1;
+    const nextLevel = upsertPathPart(currentLevel, part, isLeaf);
+    if (isLeaf || nextLevel === null) {
+      continue;
+    }
+    currentLevel = nextLevel;
+  }
Evidence
If upsertPathPart returns null for a non-leaf segment, the loop continues without updating
currentLevel, so remaining segments are inserted into the wrong node (often the root).

src/main/services/repository-processing.ts[49-57]
src/main/services/repository-processing.ts[64-71]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The path-tree insertion logic doesn’t recover when a segment already exists as a leaf (`null`) but a later path needs it as a directory. In that case, the algorithm continues without descending and inserts subsequent segments at the wrong level.
### Issue Context
This is unlikely with strictly filesystem-valid file lists, but the IPC/service is exposed to inputs that may be inconsistent.
### Fix Focus Areas
- src/main/services/repository-processing.ts[49-71]
### Suggested approach
- Update `upsertPathPart`:
- if `existing === null &amp;amp;amp;&amp;amp;amp; !isLeaf`, replace with a new subtree node (preferably `Object.create(null)` or `new Map()` depending on the chosen structure) and return it.
- Consider adding a unit test that includes `filesInfo` paths like `a` and `a/b.txt` and asserts the tree includes `b.txt` under `a` (or define/lock desired behavior).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment on lines +49 to +71
const upsertPathPart = (tree: PathTree, part: string, isLeaf: boolean): PathTree | null => {
const existing = tree[part];
if (existing !== undefined) {
return existing;
}

const nextValue = isLeaf ? null : {};
tree[part] = nextValue;
return nextValue;
};

const addFilePathToTree = (pathTree: PathTree, filePath: string): void => {
const parts = filePath.split('/');
let currentLevel: PathTree = pathTree;

for (const [index, part] of parts.entries()) {
const isLeaf = index === parts.length - 1;
const nextLevel = upsertPathPart(currentLevel, part, isLeaf);
if (isLeaf || nextLevel === null) {
continue;
}
currentLevel = nextLevel;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Prototype-pollutable path tree 🐞 Bug ⛨ Security

repository-processing builds a tree using a plain object keyed by path segments derived from
repository file/folder names; segments like __proto__ / constructor can mutate prototypes or
traverse into Object.prototype, leading to crashes or unpredictable output when rendering the tree
view. This can be triggered by opening a repo containing such folder/file names.
Agent Prompt
### Issue description
`repository-processing` constructs a directory tree using `{}` nodes and assigns child nodes via `tree[part]`, where `part` comes from repository path segments. Path segments like `__proto__` / `constructor` can interact with JavaScript object prototype semantics, causing prototype pollution or traversal into `Object.prototype`, which can crash or corrupt tree rendering.

### Issue Context
File paths originate from filesystem directory entries and are only normalized for slash direction (not sanitized). This means a repository can legitimately contain a folder named `__proto__`.

### Fix Focus Areas
- src/main/services/repository-processing.ts[37-110]
- src/utils/filter-utils.ts[10-13]

### Suggested approach
- Prefer `Map<string, PathTreeNode>` for nodes, or ensure every node is created with `Object.create(null)`.
- Optionally add a small guard that rejects or escapes path segments equal to `__proto__`, `prototype`, or `constructor` before insertion.
- Add a unit test that includes a `filesInfo` entry like `__proto__/x.txt` and asserts tree rendering is stable and does not pollute global objects.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in 239e6a2. Path-tree nodes now use Object.create(null) via createPathTreeNode, including promotions/insertions, and a regression test covers special segments (__proto__, constructor) to ensure no prototype mutation.

@sonarqubecloud

Copy link
Copy Markdown

@Mehdi-Bl Mehdi-Bl enabled auto-merge (squash) February 14, 2026 22:58
@Mehdi-Bl Mehdi-Bl merged commit a926214 into main Feb 14, 2026
25 checks passed
@Mehdi-Bl Mehdi-Bl deleted the feat/backend-extract-repo-analyze-process-services branch February 14, 2026 22:59
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