Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
597d122
llangchain packages update, compatibility for gemini-3 models, anthro…
rostilos Dec 29, 2025
ca529f9
anthropic + openai + google providers support, mcp client error hand…
rostilos Dec 29, 2025
e31b132
feat: Adjust temperature setting for ChatGoogleGenerativeAI and add s…
rostilos Dec 29, 2025
52701a2
feat: Add support for fetching full PR diffs in branch analysis and e…
rostilos Dec 29, 2025
296f746
feat: Add job skipping functionality and update job status handling
rostilos Dec 29, 2025
23c5c70
improved RAG indexing ( use llangchain language splitters )
rostilos Dec 29, 2025
d0f55cf
web-server dir structure fixes ( feature-first ). bitbucket-mcp -> vc…
rostilos Dec 29, 2025
9bd2ab7
package name typo fix
rostilos Dec 29, 2025
1c2c2f2
feat: Implement command authorization management with allowed users a…
rostilos Dec 29, 2025
00b8341
feat: Add error handling for PR analysis failures in webhook handlers
rostilos Dec 29, 2025
e9ecef0
incremental PR analysis instead of full PR reanalyze.
rostilos Dec 30, 2025
4967e32
feat: Adjust minimum delta diff size for incremental analysis and imp…
rostilos Dec 30, 2025
72e31cc
feat: Implement job deletion for ignored webhooks to prevent DB clutter
rostilos Dec 30, 2025
cd5aa00
feat: Add AST-based code chunking support with new splitter and confi…
rostilos Dec 30, 2025
e7998ac
feat: Update branch handling in RAG queries and improve alias managem…
rostilos Dec 30, 2025
46005f1
feat: Enhance language module mapping in AST splitter for improved dy…
rostilos Dec 30, 2025
0e121fd
feat: Add pagination support for branch issues and enhance issue coun…
rostilos Dec 30, 2025
997edf7
fix: Correct issue ID retrieval in BranchAnalysisProcessor for improv…
rostilos Dec 30, 2025
dc7c08c
feat: Update prompt builder to enhance error grouping and resolution …
rostilos Dec 30, 2025
dcb3a80
fix: Clarify instructions for error grouping in prompt builder to ens…
rostilos Dec 30, 2025
f67b6f5
feat: Implement JWT Bearer grant for refreshing Bitbucket Connect App…
rostilos Dec 30, 2025
1f233d3
feat: Add Issue Post-Processor service for line number validation and…
rostilos Dec 31, 2025
7cd9b05
Merge pull request #23 from rostilos/RAG-AST-based-chunking
rostilos Dec 31, 2025
2b3a710
feat: Enhance branch reconciliation by restoring missing diffs from p…
rostilos Dec 31, 2025
f4474fc
Merge pull request #24 from rostilos/RAG-AST-based-chunking
rostilos Dec 31, 2025
b7e5aff
feat: Update .gitignore to include __pycache__ directories and remove…
rostilos Dec 31, 2025
9694307
feat: Enhance issue analysis by adding detection info and filtering c…
rostilos Jan 2, 2026
aa48170
Merge pull request #25 from rostilos/RAG-AST-based-chunking
rostilos Jan 2, 2026
10259f8
feat: Add GitLab support with configuration, client, and API actions …
rostilos Jan 4, 2026
fb9dd19
Merge pull request #26 from rostilos/gitlab-support
rostilos Jan 4, 2026
ccab716
feat: Implement webhook management and VCS connection change function…
rostilos Jan 4, 2026
24f7b70
Merge pull request #27 from rostilos/bugfix/COC-001
rostilos Jan 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions deployment/config/java-shared/application.properties.sample
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ codecrow.frontend-url=http://localhost:8080
# Enable/disable email sending (set to false for development without SMTP)
codecrow.email.enabled=true

# Email templates frontend base url
codecrow.frontend.url=http://localhost:8080
Comment on lines +45 to +46
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Consolidate frontend URL properties to avoid configuration ambiguity.

This introduces a third property for the frontend URL with inconsistent naming:

  • Line 24: codecrow.frontend-url (dash notation)
  • Line 46: codecrow.frontend.url (dot notation, NEW)
  • Line 56: codecrow.email.frontend-url (dash notation)

The inconsistent naming convention (dash vs. dot) creates ambiguity about which property should be used in different contexts and increases the risk of misconfiguration.

🔎 Recommended approach

Consider consolidating to a single canonical property:

-# Email templates frontend base url
-codecrow.frontend.url=http://localhost:8080
-

Then reference the existing codecrow.frontend-url (line 24) throughout the codebase, or establish a clear convention (all dots or all dashes) and update all properties consistently.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Email templates frontend base url
codecrow.frontend.url=http://localhost:8080
🤖 Prompt for AI Agents
In deployment/config/java-shared/application.properties.sample around lines
45–46, there is a duplicate frontend URL property using dot notation
(codecrow.frontend.url) that conflicts with existing dash-notated properties
(codecrow.frontend-url at line 24 and codecrow.email.frontend-url at line 56);
remove the new codecrow.frontend.url entry and consolidate to a single canonical
key (use the existing codecrow.frontend-url), update codecrow.email.frontend-url
to reference or be renamed consistently to codecrow.frontend-url (or vice versa
if you choose a different canonical name), and then search the codebase/configs
to update any references to the old variants so all configs use the single
canonical property name.


# Sender email address and display name
codecrow.email.from=noreply@codecrow.io
codecrow.email.from-name=CodeCrow
Expand Down Expand Up @@ -158,6 +161,42 @@ codecrow.github.app.slug= - The URL-friendly app name (e.g., "codecrow")
codecrow.github.app.private-key-path= - Path to the private key .pem file
codecrow.github.app.webhook-secret= - Webhook secret for verification

# ============================================================================
# GitLab OAuth Application Configuration (for 1-click integration)
# ============================================================================
# Create a GitLab OAuth Application:
#
# For GitLab.com:
# 1. Go to https://gitlab.com/-/user_settings/applications
# 2. Click "Add new application"
#
# For Self-Hosted GitLab:
# 1. Go to https://your-gitlab-instance.com/-/user_settings/applications
# 2. Click "Add new application"
#
# Application settings:
# - Name: CodeCrow (or your preferred name)
# - Redirect URI: ${codecrow.web.base.url}/api/integrations/gitlab/app/callback
# Example: https://server.example.com/api/integrations/gitlab/app/callback
# - Confidential: Yes (checked)
# - Scopes (check all):
# * api - Full API access
# * read_user - Read authenticated user's profile
# * read_repository - Read repositories
# * write_repository - Write to repositories (for comments)
#
# After creation:
# - Copy "Application ID" to codecrow.gitlab.oauth.client-id
# - Copy "Secret" to codecrow.gitlab.oauth.client-secret
#
# Note: The redirect URI must match EXACTLY (including trailing slashes)
# ============================================================================
codecrow.gitlab.oauth.client-id=
codecrow.gitlab.oauth.client-secret=
# For self-hosted GitLab instances, set the base URL (leave empty for gitlab.com)
# Example: https://gitlab.mycompany.com
codecrow.gitlab.oauth.base-url=

# Google OAuth Configuration (for social login)
# Create OAuth 2.0 Client ID in Google Cloud Console:
# 1. Go to https://console.cloud.google.com/apis/credentials
Expand Down
2 changes: 2 additions & 0 deletions deployment/config/rag-pipeline/.env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ LLAMA_INDEX_CACHE_DIR=/tmp/.llama_index
RAG_MAX_CHUNKS_PER_INDEX=70000
RAG_MAX_FILES_PER_INDEX=40000

RAG_USE_AST_SPLITTER=true

# Alternative OpenRouter models (use full format with provider prefix):
# OPENROUTER_MODEL=openai/text-embedding-3-large # Higher quality, more expensive
# OPENROUTER_MODEL=openai/text-embedding-ada-002 # Legacy model
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.rostilos.codecrow.analysisengine.dto.request.ai;

import org.rostilos.codecrow.core.model.ai.AIProviderKey;
import org.rostilos.codecrow.core.model.codeanalysis.AnalysisMode;
import org.rostilos.codecrow.core.model.codeanalysis.AnalysisType;
import java.util.List;

Expand All @@ -24,4 +25,9 @@ public interface AiAnalysisRequest {
List<String> getChangedFiles();
List<String> getDiffSnippets();
String getRawDiff();

AnalysisMode getAnalysisMode();
String getDeltaDiff();
String getPreviousCommitHash();
String getCurrentCommitHash();
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import org.rostilos.codecrow.core.model.ai.AIConnection;
import org.rostilos.codecrow.core.model.ai.AIProviderKey;
import org.rostilos.codecrow.core.model.codeanalysis.AnalysisMode;
import org.rostilos.codecrow.core.model.codeanalysis.AnalysisType;
import org.rostilos.codecrow.core.model.codeanalysis.CodeAnalysis;
import org.rostilos.codecrow.core.model.project.ProjectVcsConnectionBinding;
Expand Down Expand Up @@ -37,6 +38,12 @@ public class AiAnalysisRequestImpl implements AiAnalysisRequest{
protected final String targetBranchName;
protected final String vcsProvider;
protected final String rawDiff;

// Incremental analysis fields
protected final AnalysisMode analysisMode;
protected final String deltaDiff;
protected final String previousCommitHash;
protected final String currentCommitHash;

protected AiAnalysisRequestImpl(Builder<?> builder) {
this.projectId = builder.projectId;
Expand All @@ -62,6 +69,11 @@ protected AiAnalysisRequestImpl(Builder<?> builder) {
this.targetBranchName = builder.targetBranchName;
this.vcsProvider = builder.vcsProvider;
this.rawDiff = builder.rawDiff;
// Incremental analysis fields
this.analysisMode = builder.analysisMode != null ? builder.analysisMode : AnalysisMode.FULL;
this.deltaDiff = builder.deltaDiff;
this.previousCommitHash = builder.previousCommitHash;
this.currentCommitHash = builder.currentCommitHash;
}

public Long getProjectId() {
Expand Down Expand Up @@ -153,6 +165,22 @@ public String getRawDiff() {
return rawDiff;
}

public AnalysisMode getAnalysisMode() {
return analysisMode;
}

public String getDeltaDiff() {
return deltaDiff;
}

public String getPreviousCommitHash() {
return previousCommitHash;
}

public String getCurrentCommitHash() {
return currentCommitHash;
}


public static Builder<?> builder() {
return new Builder<>();
Expand Down Expand Up @@ -183,6 +211,11 @@ public static class Builder<T extends Builder<T>> {
private String targetBranchName;
private String vcsProvider;
private String rawDiff;
// Incremental analysis fields
private AnalysisMode analysisMode;
private String deltaDiff;
private String previousCommitHash;
private String currentCommitHash;

protected Builder() {
}
Expand Down Expand Up @@ -301,6 +334,26 @@ public T withRawDiff(String rawDiff) {
return self();
}

public T withAnalysisMode(AnalysisMode analysisMode) {
this.analysisMode = analysisMode;
return self();
}

public T withDeltaDiff(String deltaDiff) {
this.deltaDiff = deltaDiff;
return self();
}

public T withPreviousCommitHash(String previousCommitHash) {
this.previousCommitHash = previousCommitHash;
return self();
}

public T withCurrentCommitHash(String currentCommitHash) {
this.currentCommitHash = currentCommitHash;
return self();
}

public AiAnalysisRequestImpl build() {
return new AiAnalysisRequestImpl(this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ public class BranchProcessRequest implements AnalysisProcessRequest {
@NotBlank(message = "Specify analysis type")
public AnalysisType analysisType;

/**
* Optional: The PR number that triggered this branch analysis (for pullrequest:fulfilled events).
* When provided, the PR diff will be used instead of commit diff to get ALL changed files.
* This ensures all files from the original PR are analyzed, not just merge commit changes.
*/
public Long sourcePrNumber;

/**
* Optional: ZIP archive of the repository for first-time full indexing in RAG pipeline.
* If provided, the entire repository will be indexed.
Expand All @@ -38,6 +45,8 @@ public String getCommitHash() {

public AnalysisType getAnalysisType() { return analysisType; }

public Long getSourcePrNumber() { return sourcePrNumber; }

public byte[] getArchive() {
return archive;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,19 +155,32 @@ public Map<String, Object> process(BranchProcessRequest request, Consumer<Map<St
consumer.accept(Map.of(
"type", "status",
"state", "fetching_diff",
"message", "Fetching commit diff"
"message", "Fetching diff"
));

EVcsProvider provider = getVcsProvider(project);
VcsOperationsService operationsService = vcsServiceFactory.getOperationsService(provider);

String rawDiff = operationsService.getCommitDiff(
client,
vcsInfo.workspace(),
vcsInfo.repoSlug(),
request.getCommitHash()
);
log.info("Fetched commit {} diff for branch analysis (no PR context)", request.getCommitHash());
String rawDiff;
// Use PR diff if sourcePrNumber is available (from pullrequest:fulfilled events)
// This ensures we get ALL files from the PR, not just the merge commit changes
if (request.getSourcePrNumber() != null) {
rawDiff = operationsService.getPullRequestDiff(
client,
vcsInfo.workspace(),
vcsInfo.repoSlug(),
String.valueOf(request.getSourcePrNumber())
);
log.info("Fetched PR #{} diff for branch analysis (contains all PR files)", request.getSourcePrNumber());
} else {
rawDiff = operationsService.getCommitDiff(
client,
vcsInfo.workspace(),
vcsInfo.repoSlug(),
request.getCommitHash()
);
log.info("Fetched commit {} diff for branch analysis (no PR context)", request.getCommitHash());
}

Set<String> changedFiles = parseFilePathsFromDiff(rawDiff);

Expand All @@ -181,7 +194,18 @@ public Map<String, Object> process(BranchProcessRequest request, Consumer<Map<St
Branch branch = createOrUpdateProjectBranch(project, request);

mapCodeAnalysisIssuesToBranch(changedFiles, branch, project);
reanalyzeCandidateIssues(changedFiles, branch, project, request, consumer);

// Always update branch issue counts after mapping (even on first analysis)
// Previously this was only done in reanalyzeCandidateIssues() which could be skipped
Branch refreshedBranch = branchRepository.findByIdWithIssues(branch.getId()).orElse(branch);
refreshedBranch.updateIssueCounts();
branchRepository.save(refreshedBranch);
log.info("Updated branch issue counts after mapping: total={}, high={}, medium={}, low={}, resolved={}",
refreshedBranch.getTotalIssues(), refreshedBranch.getHighSeverityCount(),
refreshedBranch.getMediumSeverityCount(), refreshedBranch.getLowSeverityCount(),
refreshedBranch.getResolvedCount());

reanalyzeCandidateIssues(changedFiles, refreshedBranch, project, request, consumer);

// Incremental RAG update for merged PR
performIncrementalRagUpdate(request, project, vcsInfo, rawDiff, consumer);
Expand Down Expand Up @@ -449,7 +473,7 @@ else if (issuesObj instanceof Map) {
}

private void processReconciledIssue(Map<String, Object> issueData, Branch branch, String commitHash) {
Object issueIdFromAi = issueData.get("issueId");
Object issueIdFromAi = issueData.get("id");
Long actualIssueId = null;

if (issueIdFromAi != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,33 @@ public interface VcsOperationsService {
*/
String getCommitDiff(OkHttpClient client, String workspace, String repoSlug, String commitHash) throws IOException;

/**
* Fetches the raw diff for a pull request.
* This returns ALL files changed in the PR, not just the merge commit.
*
* @param client authorized HTTP client
* @param workspace workspace or team/organization slug
* @param repoSlug repository slug
* @param prNumber pull request number
* @return raw unified diff as returned by VCS API
* @throws IOException on network / parsing errors
*/
String getPullRequestDiff(OkHttpClient client, String workspace, String repoSlug, String prNumber) throws IOException;

/**
* Fetches the diff between two commits (delta diff for incremental analysis).
* This is used to get only the changes made since the last analyzed commit.
*
* @param client authorized HTTP client
* @param workspace workspace or team/organization slug
* @param repoSlug repository slug
* @param baseCommitHash the base commit (previously analyzed commit)
* @param headCommitHash the head commit (current commit to analyze)
* @return raw unified diff between the two commits
* @throws IOException on network / parsing errors
*/
String getCommitRangeDiff(OkHttpClient client, String workspace, String repoSlug, String baseCommitHash, String headCommitHash) throws IOException;

/**
* Checks if a file exists in the specified branch.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* Utility class for filtering large files from diff content.
*
* Mirrors the behavior of org.rostilos.codecrow.mcp.filter.LargeContentFilter
* from bitbucket-mcp module to ensure consistent filtering across the system.
* from vcs-mcp module to ensure consistent filtering across the system.
* TODO: reuse it in mcp, DRY pattern + use threshold bytes from configuration
*/
public class DiffContentFilter {
Expand Down
2 changes: 2 additions & 0 deletions java-ecosystem/libs/core/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,12 @@
opens org.rostilos.codecrow.core.persistence.repository.ai to spring.core, spring.beans, spring.context;
exports org.rostilos.codecrow.core.persistence.repository.vcs;
exports org.rostilos.codecrow.core.model.vcs.config.github;
exports org.rostilos.codecrow.core.model.vcs.config.gitlab;
exports org.rostilos.codecrow.core.model.vcs.config.cloud;
exports org.rostilos.codecrow.core.model.vcs.config;
exports org.rostilos.codecrow.core.dto.bitbucket;
exports org.rostilos.codecrow.core.dto.github;
exports org.rostilos.codecrow.core.dto.gitlab;
exports org.rostilos.codecrow.core.dto.ai;
exports org.rostilos.codecrow.core.dto.user;
exports org.rostilos.codecrow.core.dto.project;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,19 @@ public record IssueDTO (
String pullRequestId,
String status, // open|resolved|ignored
OffsetDateTime createdAt,
String issueCategory
String issueCategory,
// Detection info - where was this issue first found
Long analysisId,
Long prNumber,
String commitHash,
OffsetDateTime detectedAt
) {
public static IssueDTO fromEntity(CodeAnalysisIssue issue) {
String categoryStr = issue.getIssueCategory() != null
? issue.getIssueCategory().name()
: IssueCategory.CODE_QUALITY.name();

var analysis = issue.getAnalysis();
return new IssueDTO(
String.valueOf(issue.getId()),
categoryStr,
Expand All @@ -37,11 +44,16 @@ public static IssueDTO fromEntity(CodeAnalysisIssue issue) {
issue.getLineNumber(),
null,
null,
issue.getAnalysis() == null ? null : issue.getAnalysis().getBranchName(),
issue.getAnalysis() == null || issue.getAnalysis().getPrNumber() == null ? null : String.valueOf(issue.getAnalysis().getPrNumber()),
analysis == null ? null : analysis.getBranchName(),
analysis == null || analysis.getPrNumber() == null ? null : String.valueOf(analysis.getPrNumber()),
issue.isResolved() ? "resolved" : "open",
issue.getCreatedAt(),
categoryStr
categoryStr,
// Detection info
analysis != null ? analysis.getId() : null,
analysis != null ? analysis.getPrNumber() : null,
analysis != null ? analysis.getCommitHash() : null,
issue.getCreatedAt()
);
}
}
Loading