Skip to content

Implement Slack-First Code Review System#1

Open
tuhinkanti wants to merge 4 commits intomainfrom
slack-code-review-system-3220839199327988510
Open

Implement Slack-First Code Review System#1
tuhinkanti wants to merge 4 commits intomainfrom
slack-code-review-system-3220839199327988510

Conversation

@tuhinkanti
Copy link
Owner

@tuhinkanti tuhinkanti commented Jan 28, 2026

This PR implements the "Slack-First Code Review System" as described in the plan.
It establishes a bridge service that receives GitHub Pull Request webhooks, analyzes the changes using Google Gemini AI, and posts interactive notifications to Slack.
Users can then review diffs, approve PRs, and add comments directly from the Slack interface.

Key Components:

  • Bridge Server: Built with Node.js, Express, and Bolt.js.
  • GitHub Ingest: Verifies webhooks and fetches PR diffs.
  • AI Analysis: Uses Gemini to generate summaries and risk assessments.
  • Slack UI: Utilizes Block Kit for rich messages and Modals for code review.

PR created automatically by Jules for task 3220839199327988510 started by @tuhinkanti


Open with Devin

- Initialize TypeScript project with Bolt.js and Octokit
- Implement GitHub webhook handler with signature verification
- Add AI agent using Gemini for PR analysis (Summary & Risk Score)
- Create Slack notification system with interactive buttons
- Implement Slack-to-GitHub actions: Review Diff, Approve, and Comment

Co-authored-by: tuhinkanti <5673224+tuhinkanti@users.noreply.github.com>
@google-labs-jules
Copy link

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

Copy link

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 potential issue.

View issue and 5 additional flags in Devin Review.

Open in Devin Review

- Initialize TypeScript project with Bolt.js, Octokit, and Google Gemini
- Implement GitHub webhook handler with signature verification and async processing
- Integrate AI agent for PR analysis (Summary & Risk Score)
- Create Slack notification system with interactive buttons
- Implement Slack-to-GitHub actions: Review Diff (with pagination), Approve, and Comment
- Fixes for ESM configuration and webhook timeouts

Co-authored-by: tuhinkanti <5673224+tuhinkanti@users.noreply.github.com>
Copy link

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 2 new potential issues.

View issues and 7 additional flags in Devin Review.

Open in Devin Review

}

Diff:
${diff.substring(0, 30000)} // Truncate to avoid token limits if necessary

Choose a reason for hiding this comment

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

🟡 JavaScript comment accidentally included in AI prompt string

The JavaScript comment // Truncate to avoid token limits if necessary is inside a template literal and gets included in the prompt sent to the Gemini AI model.

Click to expand

Code at src/ai/agent.ts:31:

${diff.substring(0, 30000)} // Truncate to avoid token limits if necessary

Since this is inside a template literal (backticks), the comment text is not treated as a JavaScript comment but as literal string content. This means every AI request includes the text // Truncate to avoid token limits if necessary as part of the prompt, which:

  1. Wastes tokens
  2. Could confuse the AI model's response
  3. Looks unprofessional if the AI references it

Expected: The comment should be outside the template literal or removed.
Actual: The comment text is sent as part of the prompt to Gemini.

Recommendation: Move the comment outside the template literal: ${diff.substring(0, 30000)} ; // Truncate to avoid token limits

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

});

await client.chat.postMessage({
channel: body.channel?.id || '',

Choose a reason for hiding this comment

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

🔴 Empty channel ID passed to Slack API when channel context is missing

When body.channel is undefined (which can occur in certain Slack interaction contexts), the code passes an empty string as the channel ID to Slack API methods, causing the API call to fail.

Click to expand

Code at src/slack/handlers.ts:23 and src/slack/handlers.ts:30:

channel: body.channel?.id || '',

The Slack API does not accept an empty string as a channel ID. When body.channel is undefined, this fallback to '' will cause chat.postMessage and chat.postEphemeral to fail with an API error like channel_not_found.

Expected: Either validate that channel exists before making the call, or handle the missing channel case gracefully.
Actual: An empty string is passed, causing the Slack API call to fail.

Recommendation: Add a guard clause to check if body.channel?.id exists before attempting to post messages, or store the channel ID in the button value metadata when creating the initial message.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

google-labs-jules bot and others added 2 commits January 28, 2026 05:53
- Initialize TypeScript project with Bolt.js, Octokit, and Google Gemini
- Implement GitHub webhook handler with signature verification and async processing
- Integrate AI agent for PR analysis (Summary & Risk Score)
- Create Slack notification system with interactive buttons
- Implement Slack-to-GitHub actions: Review Diff (with pagination), Approve, and Comment
- Enhance Comment flow to post confirmation in Slack threads
- Fixes for ESM configuration and webhook timeouts

Co-authored-by: tuhinkanti <5673224+tuhinkanti@users.noreply.github.com>
- Initialize TypeScript project with Bolt.js, Octokit, and Google Gemini
- Implement GitHub webhook handler with signature verification and async processing
- Integrate AI agent for PR analysis (Summary & Risk Score)
- Create Slack notification system with interactive buttons
- Implement Slack-to-GitHub actions: Review Diff (with pagination), Approve, and Comment
- Enhance Comment flow to post confirmation in Slack threads
- Fix modal title length issue

Co-authored-by: tuhinkanti <5673224+tuhinkanti@users.noreply.github.com>
Copy link

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View issue and 14 additional flags in Devin Review.

Open in Devin Review

// Clean up the response to ensure it's valid JSON
const jsonString = responseText.replace(/```json/g, '').replace(/```/g, '').trim();

return JSON.parse(jsonString);

Choose a reason for hiding this comment

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

🟡 Unvalidated AI response can crash application with malformed JSON structure

The analyzeDiff function parses the AI response and returns it directly without validating that it conforms to the expected AnalysisResult interface.

Click to expand

How it happens

At src/ai/agent.ts:41, the AI response is parsed and returned directly:

const jsonString = responseText.replace(/```json/g, '').replace(/```/g, '').trim();
return JSON.parse(jsonString);

If the AI returns JSON that doesn't match the expected structure (e.g., {"summary": "text", "risk": "Unknown"} instead of {"summary": ["text"], "risk": "Low"}), the code at src/github/handlers.ts:53 will fail:

text: `*Summary:*\n${Array.isArray(analysis.summary) ? analysis.summary.map((s: string) => `• ${s}`).join('\n') : analysis.summary}`

While there's a fallback for non-array summary, if risk is not one of the expected values or fields are missing entirely, downstream code could behave unexpectedly.

Impact

Malformed AI responses could cause runtime errors or display incorrect information in Slack messages.

Recommendation: Validate the parsed JSON structure before returning. Ensure summary is an array, risk is one of the expected values, and reason exists. Fall back to the error response if validation fails.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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

Comments