Skip to content

Conversation

@brendan-kellam
Copy link
Contributor

@brendan-kellam brendan-kellam commented Jan 22, 2026

Summary by CodeRabbit

  • New Features

    • Add GitHub repo onboarding (server API + client action) and ability to trigger indexing
    • Repository landing page with chat UI and repo-indexing guard
    • Repo status endpoint and polling for real-time indexing updates
    • Experimental Ask GH feature gated by an environment flag
  • Database

    • Added repo field to control auto-cleanup and a permission sync timestamp

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 22, 2026

Walkthrough

Adds an experimental AskGH feature: backend endpoint to add GitHub repos, a reusable repo-record builder, DB schema changes, index/job scheduling adjustments, frontend pages/components for repo display and indexing polling, and a feature-flagged layout toggle.

Changes

Cohort / File(s) Summary
Backend API & Route
packages/backend/src/api.ts
New POST /api/experimental/add-github-repo handler that validates owner/repo, fetches GitHub data via Octokit, builds a repo record, upserts via Prisma, schedules an INDEX job, and returns { jobId, repoId }.
Repo Record Creation
packages/backend/src/repoCompileUtils.ts
Added exported createGitHubRepoRecord(...) helper and refactored compileGithubConfig to use it; carries branches/tags and supports isAutoCleanupDisabled.
Index/Cleanup Scheduling
packages/backend/src/repoIndexManager.ts
scheduleCleanupJobs now filters repos with isAutoCleanupDisabled: false before scheduling cleanup.
Database Schema & Migration
packages/db/prisma/schema.prisma, packages/db/prisma/migrations/.../migration.sql
Added isAutoCleanupDisabled Boolean @default(false) and permissionSyncedAt DateTime? to Repo; migration SQL adds isAutoCleanupDisabled column with default false.
Frontend Server APIs & Routes
packages/web/src/app/[domain]/askgh/[owner]/[repo]/api.ts, packages/web/src/app/api/(server)/repo-status/[repoId]/route.ts
Added getRepoInfo(repoId) server action and a GET /api/repo-status/[repoId] route that returns repo info or ServiceError.
Frontend Client Actions
packages/web/src/features/workerApi/actions.ts
New addGithubRepo(owner, repo) client action that calls the experimental backend route and validates { jobId, repoId } response.
Frontend Pages & Components
packages/web/src/app/[domain]/askgh/layout.tsx, .../page.tsx, .../components/landingPage.tsx, .../components/repoIndexedGuard.tsx
Feature-gated AskGH layout; server page that resolves/creates repo and fetches repoInfo + language models; LandingPage chat UI; RepoIndexedGuard polls /api/repo-status until indexed.
Types
packages/web/src/app/[domain]/askgh/[owner]/[repo]/types.ts
Added RepoInfo type: `{ id, name, displayName
Env Config
packages/shared/src/env.server.ts
Added boolean EXPERIMENT_ASK_GH_ENABLED (default false) feature flag.
Minor Formatting
packages/web/src/app/[domain]/chat/components/landingPageChatBox.tsx, packages/web/src/middleware.ts
Small whitespace/newline formatting changes.

Sequence Diagram(s)

sequenceDiagram
    participant User as User/Client
    participant Page as GitHubRepoPage
    participant Backend as Backend API
    participant GitHub as GitHub API
    participant DB as Database
    participant Indexer as RepoIndexManager

    User->>Page: Navigate to /askgh/:owner/:repo
    Page->>DB: Query repo by external_id/external_codeHostUrl (SINGLE_TENANT_ORG_ID)
    alt repo not found
        Page->>Backend: POST /api/experimental/add-github-repo {owner, repo}
        Backend->>GitHub: Octokit fetch repository metadata
        GitHub-->>Backend: Return repo metadata
        Backend->>DB: Upsert Repo (createGitHubRepoRecord)
        DB-->>Backend: Return repoId
        Backend->>Indexer: Create INDEX job for repoId
        Indexer-->>Backend: Return jobId
        Backend-->>Page: { jobId, repoId }
    else repo found
        DB-->>Page: Return existing repoId
    end
    Page->>Page: Fetch repoInfo and language models
    Page->>User: Render UI (RepoIndexedGuard + LandingPage)
    alt repo indexed == false
        User->>RepoIndexedGuard: Poll /api/repo-status/{repoId}
        RepoIndexedGuard->>DB: getRepoInfo(repoId)
        DB-->>RepoIndexedGuard: Return updated RepoInfo
        RepoIndexedGuard->>User: Show indexing spinner until indexed
    else indexed
        RepoIndexedGuard->>User: Render chat UI
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested labels

sourcebot-team

Suggested reviewers

  • msukkari
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main feature: adding on-demand indexing support for GitHub repositories as part of the ask_gh experiment.

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

✨ Finishing touches
  • 📝 Generate docstrings

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.

@brendan-kellam
Copy link
Contributor Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Jan 22, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🤖 Fix all issues with AI agents
In
`@packages/db/prisma/migrations/20260121012745_add_is_auto_cleanup_disabled_flag_to_repo_table/migration.sql`:
- Around line 1-2: The migration adds isAutoCleanupDisabled to the Repo table
but omits the new Repo.permissionSyncedAt column from the Prisma schema; update
this migration to also add a nullable timestamp column named
"permissionSyncedAt" on table "Repo" (use the DB type matching Prisma DateTime,
e.g., TIMESTAMP WITH TIME ZONE / timestamptz) so the database matches the schema
and queries won’t fail at runtime, and ensure the column is nullable with no
default to reflect when permissions are un-synced.

In
`@packages/web/src/app/`[domain]/askgh/[owner]/[repo]/components/repoIndexedGuard.tsx:
- Around line 33-35: RepoIndexedGuard currently returns null when isError is
true, producing a blank screen; replace that null with a user-facing error
state: render a UI similar to the loading state (icon/spinner, clear error
message like "Failed to check indexing status", and a retry action). In the
isError branch of RepoIndexedGuard, show the message and a retry button that
triggers the same polling/refetch function used for the happy/loading path (or
calls the existing refetch/startPolling handler used by the hook or context); if
no refetch function exists yet, wire one up to re-run the status request so the
retry button can re-attempt polling. Ensure accessibility by providing aria-live
or role="alert" for the message.

In
`@packages/web/src/app/`[domain]/askgh/[owner]/[repo]/components/repoStatusDisplay.tsx:
- Around line 79-89: getRepoStatus currently assumes fetch returns a successful
JSON body; update the function to check response.ok after the fetch and handle
non-OK responses by attempting to parse JSON if possible (or falling back to
text) and returning/throwing a ServiceError with meaningful status and message;
also wrap response.json() in try/catch to handle invalid JSON and convert
parsing failures into a ServiceError. Ensure you update the return path of
getRepoStatus (which currently returns RepoInfo | ServiceError) to return a
consistent ServiceError object on non-OK or parse errors and keep returning
RepoInfo on success.
- Around line 34-37: The component currently returns null when isError is true,
producing a blank screen; update the error branch in the RepoStatusDisplay
component (the block checking isError) to render a minimal error state instead
of null — e.g., return a small accessible UI element (a div or span with
role="alert") that shows a user-facing message like "Failed to load repository
status" and, if available, an error retry button or link; ensure the element is
styled/structured consistently with other small notices in the app and includes
any available error details for debugging.
- Around line 54-71: The repo name is inserted raw into the regex-based
SearchBar default (defaults.query = `repo:^${repoInfo.name}$`), which allows
regex metacharacters in repoInfo.name to alter matches; fix by escaping regex
metacharacters before interpolation (e.g., add or use an escapeRegExp utility
and call it on repoInfo.name) and then build the query with the escaped value so
defaults.query becomes `repo:^${escapedName}$`; update references in
repoStatusDisplay (where repoInfo.name is used) and ensure the SearchBar
defaults.query uses the escapedName.

In `@packages/web/src/app/`[domain]/askgh/[owner]/[repo]/page.tsx:
- Around line 20-44: The current flow lets two requests pass
prisma.repo.findFirst and both call addGithubRepo, causing prisma.repo.create to
throw a unique-constraint error; update addGithubRepo to perform an upsert (use
prisma.repo.upsert with the unique key on
external_id/external_codeHostUrl/orgId) or wrap the existing prisma.repo.create
in a try/catch that detects Prisma unique-constraint errors and then performs
prisma.repo.findUnique/findFirst to return the existing repo id; ensure the
function still returns the same shape (response.repoId) and keep
ServiceErrorException usage for other failures so callers like the code that
awaits addGithubRepo continue to get a consistent result.

In `@packages/web/src/app/api/repo-status/`[repoId]/route.ts:
- Around line 10-16: The current validation uses parseInt on params.repoId
(variables: params, repoId, repoIdNum, parseInt) which allows partial strings
like "123abc" or hex "0x10"; update the check to first ensure repoId is a
strictly decimal integer string (e.g., match /^\d+$/) and only then convert to a
Number/parseInt and use that numeric value, returning the 400 response if the
regex fails; apply the same change to the other occurrence in the image route
file as well.
🧹 Nitpick comments (3)
packages/web/src/app/[domain]/askgh/[owner]/[repo]/components/repoIndexedGuard.tsx (1)

58-67: Guard against non‑JSON responses from /api/repo-status.
If the response isn’t JSON (e.g., unexpected 500 body), response.json() throws and you lose context. Catch parse errors and throw a clearer message.

♻️ Suggested refactor
-    const result = await fetch(
-        `/api/repo-status/${repoId}`,
-        {
-            method: 'GET',
-            headers: {
-                'Content-Type': 'application/json',
-            },
-        }
-    ).then(response => response.json());
-    return result as RepoInfo | ServiceError;
+    const response = await fetch(
+        `/api/repo-status/${repoId}`,
+        {
+            method: 'GET',
+            headers: {
+                'Content-Type': 'application/json',
+            },
+        }
+    );
+    const result = await response.json().catch(() => {
+        throw new Error(`Failed to parse repo-status response (${response.status})`);
+    });
+    return result as RepoInfo | ServiceError;
packages/web/src/app/[domain]/askgh/[owner]/[repo]/api.ts (1)

10-19: Drop unused jobs include to reduce query work.
The relation isn’t used in the RepoInfo mapping; removing it avoids extra join/order overhead.

♻️ Suggested refactor
-        const repo = await prisma.repo.findUnique({
-            where: { id: repoId },
-            include: {
-                jobs: {
-                    orderBy: {
-                        createdAt: 'desc',
-                    },
-                    take: 1,
-                },
-            },
-        });
+        const repo = await prisma.repo.findUnique({
+            where: { id: repoId },
+        });
packages/web/src/app/[domain]/askgh/[owner]/[repo]/components/repoStatusDisplay.tsx (1)

22-27: Align polling comment with actual field.

The comment mentions indexedAt, but the logic checks isIndexed. Please update the comment to avoid confusion.

♻️ Proposed fix
-            // If repo has been indexed before (indexedAt is not null), stop polling
+            // If repo is indexed, stop polling

@brendan-kellam brendan-kellam changed the title [wip] On demand indexing experiment(ask_gh): Support on demand indexing Jan 23, 2026
@brendan-kellam brendan-kellam changed the title experiment(ask_gh): Support on demand indexing experiment(ask_gh): Support on demand indexing of GitHub repositories Jan 23, 2026
@brendan-kellam brendan-kellam marked this pull request as ready for review January 23, 2026 01:02
@github-actions
Copy link
Contributor

@brendan-kellam your pull request is missing a changelog!

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@packages/backend/src/api.ts`:
- Line 38: All API routes in api.ts are registered without authentication; add a
centralized auth middleware and apply it to every route (e.g., wrap app.post
registrations) so handlers like experimental_addGithubRepo, syncConnection, and
indexRepo are protected; implement or reuse a function such as
ensureAuthenticated(req,res,next) and attach it to each route declaration (or to
the router/app globally) and, if feature gating is required, add a feature-flag
check (e.g., isFeatureEnabled('experimental') or env guard) inside either the
middleware or the individual handlers to block access when the flag is off.

In `@packages/web/src/app/`[domain]/askgh/layout.tsx:
- Around line 4-16: AskGHLayout currently accesses params synchronously which
breaks in Next.js 15; make the layout async, change the params type to a Promise
(e.g. params: Promise<{ domain: string }>), await params inside AskGHLayout to
extract domain (const { domain } = await params) and then use domain in the
redirect and return; update the AskGHLayout function signature to async and
adjust any type annotations accordingly.
♻️ Duplicate comments (1)
packages/web/src/app/api/(server)/repo-status/[repoId]/route.ts (1)

10-16: Tighten repoId validation to reject partial/hex inputs.

parseInt accepts values like 123abc (parses to 123) or 0x10 (parses to 16), which could lead to unintended repo lookups. Use strict numeric validation.

🔧 Suggested fix
-    const repoIdNum = parseInt(repoId);
-
-    if (isNaN(repoIdNum)) {
+    if (!/^\d+$/.test(repoId)) {
+        return new Response("Invalid repo ID", { status: 400 });
+    }
+    const repoIdNum = Number(repoId);
+    if (!Number.isSafeInteger(repoIdNum)) {
         return new Response("Invalid repo ID", { status: 400 });
     }
🧹 Nitpick comments (1)
packages/backend/src/api.ts (1)

110-114: Consider authenticated Octokit to avoid low rate limits / missing private repos.
If this endpoint will be used beyond trivial testing, unauthenticated requests will quickly hit GitHub’s public rate limits and won’t allow private repo lookups. Consider injecting a token via env config.

🔧 Example
-        const octokit = new Octokit();
+        const octokit = new Octokit({
+            auth: process.env.GITHUB_TOKEN,
+        });

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@packages/backend/src/api.ts`:
- Around line 100-103: The schema for request validation (the const schema =
z.object({...}).strict()) currently allows empty or whitespace-only owner/repo
strings; update the owner and repo validators to z.string().trim().min(1) so
they reject blank input before calling the GitHub API, keeping the surrounding
z.object(...) and .strict() intact; ensure both the owner and repo properties
are changed.
- Around line 111-137: Replace the unauthenticated, unhandled GitHub call by
instantiating Octokit with process.env.GITHUB_TOKEN when present (use the
Octokit constructor) and wrap the octokit.rest.repos.get(...) call in a
try/catch; on error inspect err.status (or err.statusCode) and return matching
HTTP responses for 404 (res.status(404)), 403 (res.status(403)), and 429
(res.status(429)) with a helpful message, and for other errors return a 502/500
as appropriate; keep the subsequent createGitHubRepoRecord(...),
prisma.repo.upsert(...) and repoIndexManager.createJobs(...) logic but only run
them after a successful repo fetch.
♻️ Duplicate comments (1)
packages/backend/src/api.ts (1)

37-39: Endpoint gating still missing (previously flagged).

This experimental route is still registered without auth/feature-flag protection, matching the earlier review finding.

Comment on lines +100 to +103
const schema = z.object({
owner: z.string(),
repo: z.string(),
}).strict();
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the file around the specified lines
cat -n packages/backend/src/api.ts | sed -n '95,110p'

Repository: sourcebot-dev/sourcebot

Length of output: 698


🏁 Script executed:

# Let's also check the full context of this schema usage
cat -n packages/backend/src/api.ts | sed -n '90,120p'

Repository: sourcebot-dev/sourcebot

Length of output: 1265


🌐 Web query:

Zod string trim() min() validation API documentation

💡 Result:

In Zod, both .trim() and .min() are chainable methods on z.string() schemas:

  • .trim(): a transformation that trims leading/trailing whitespace. Example shown in the official “Strings” guide: z.string().trim(); (commented “trim whitespace”). [1]
  • .min(length, options?): a validation that enforces a minimum string length. Example: z.string().min(5); and with a custom message: z.string().min(5, { message: "Must be 5 or more characters long" });. [1]

Typical usage to require a non-empty string after trimming:

import { z } from "zod";

const schema = z.string().trim().min(1, { message: "Required" });

(Chaining is the standard Zod API style—e.g., z.string().min(5).max(10).toLowerCase()). [2]

Sources: Zod strings guide [1], Zod v4 package docs [2].


Tighten owner/repo validation.

Empty strings (including whitespace-only strings) currently pass validation and would fail when passed to the GitHub API. Add .trim().min(1) to both fields to validate early and prevent unnecessary API calls.

🔧 Suggested fix
-        const schema = z.object({
-            owner: z.string(),
-            repo: z.string(),
-        }).strict();
+        const schema = z.object({
+            owner: z.string().trim().min(1),
+            repo: z.string().trim().min(1),
+        }).strict();
📝 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
const schema = z.object({
owner: z.string(),
repo: z.string(),
}).strict();
const schema = z.object({
owner: z.string().trim().min(1),
repo: z.string().trim().min(1),
}).strict();
🤖 Prompt for AI Agents
In `@packages/backend/src/api.ts` around lines 100 - 103, The schema for request
validation (the const schema = z.object({...}).strict()) currently allows empty
or whitespace-only owner/repo strings; update the owner and repo validators to
z.string().trim().min(1) so they reject blank input before calling the GitHub
API, keeping the surrounding z.object(...) and .strict() intact; ensure both the
owner and repo properties are changed.

Comment on lines +111 to +137
const octokit = new Octokit();
const response = await octokit.rest.repos.get({
owner: parsed.data.owner,
repo: parsed.data.repo,
});

const record = createGitHubRepoRecord({
repo: response.data,
hostUrl: 'https://github.com',
isAutoCleanupDisabled: true,
});

const repo = await this.prisma.repo.upsert({
where: {
external_id_external_codeHostUrl_orgId: {
external_id: record.external_id,
external_codeHostUrl: record.external_codeHostUrl,
orgId: SINGLE_TENANT_ORG_ID,
}
},
update: record,
create: record,
});

const [jobId ] = await this.repoIndexManager.createJobs([repo], RepoIndexingJobType.INDEX);

res.status(200).json({ jobId, repoId: repo.id });
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

🧩 Analysis chain

🏁 Script executed:

# Check if file exists and view the content around lines 111-137
if [ -f "packages/backend/src/api.ts" ]; then
  echo "File found. Checking lines 111-137:"
  sed -n '111,137p' packages/backend/src/api.ts
  echo ""
  echo "Broader context (lines 100-150):"
  wc -l packages/backend/src/api.ts
else
  echo "File not found. Searching for api.ts files:"
  find . -name "api.ts" -type f 2>/dev/null | head -20
fi

Repository: sourcebot-dev/sourcebot

Length of output: 1094


🏁 Script executed:

# Check for imports and logger availability
if [ -f "packages/backend/src/api.ts" ]; then
  echo "=== Imports and class context ==="
  head -50 packages/backend/src/api.ts
fi

Repository: sourcebot-dev/sourcebot

Length of output: 1925


🏁 Script executed:

# Search for existing error handling patterns in the codebase
echo "=== Searching for octokit usage patterns ==="
rg -A 5 "new Octokit" --type ts packages/backend/src/

Repository: sourcebot-dev/sourcebot

Length of output: 805


🌐 Web query:

Octokit JavaScript error handling status codes 404 403 429 rate limit

💡 Result:

In Octokit JS, GitHub API failures are typically thrown as a RequestError with:

  • error.status = HTTP status code
  • error.response = { status, url, headers, data } (when a response exists) (npm.io)

How to interpret / handle the status codes

404 Not Found

  • Resource/route doesn’t exist or GitHub is intentionally hiding a private/inaccessible resource behind a 404 (common pattern). Handle as “missing or not accessible,” and double-check owner/repo names, endpoint, and auth scopes.

403 Forbidden

  • Usually “insufficient permissions / policy” or rate limiting (GitHub may return 403 or 429 when you exceed primary/secondary rate limits). (docs.github.com)
  • For rate limiting, inspect headers and message (see below).

429 Too Many Requests

  • Rate limiting (primary or secondary). GitHub documents that exceeding rate limits can return 403 or 429. (docs.github.com)

Rate limit signals to check (403/429)

GitHub’s guidance when rate-limited: (docs.github.com)

  • x-ratelimit-remaining: 0 ⇒ wait until x-ratelimit-reset (UTC epoch seconds)
  • retry-after (if present) ⇒ wait that many seconds (often for secondary rate limits)
  • Secondary rate limit responses include a message indicating you exceeded a secondary rate limit; if no retry-after, GitHub recommends waiting at least ~60 seconds, then backing off more if it persists. (docs.github.com)

Practical Octokit pattern

try {
  await octokit.request("GET /repos/{owner}/{repo}", { owner, repo });
} catch (error) {
  if (!error.status) throw error; // not an Octokit HTTP error

  const status = error.status;
  const headers = error.response?.headers || {};
  const message = error.response?.data?.message;

  if (status === 404) {
    // not found OR not accessible
  } else if (status === 403 || status === 429) {
    const remaining = headers["x-ratelimit-remaining"];
    const reset = headers["x-ratelimit-reset"];
    const retryAfter = headers["retry-after"];
    // decide whether this is rate limit vs permissions by checking remaining/message
  } else {
    // other HTTP errors
  }
}

Best practice: let Octokit throttle for you

If you want automatic handling/retries for rate limits, use @octokit/plugin-throttling, which provides hooks like onRateLimit and onSecondaryRateLimit and computes retryAfter from retry-after / x-ratelimit-reset. (github.com)

Citations:


Handle GitHub API failures explicitly and prefer authenticated Octokit.

The octokit.rest.repos.get() call at lines 116–119 has no error handling. GitHub API failures will surface as 500s instead of appropriate status codes, and unauthenticated calls are rate-limited to 60 requests/hour (vs. 5000 authenticated), which will cause silent failures. Use a configured GITHUB_TOKEN when available and catch specific status codes (404, 403, 429) to return correct HTTP responses.

🛠️ Proposed handling
-        const octokit = new Octokit();
-        const response = await octokit.rest.repos.get({
-            owner: parsed.data.owner,
-            repo: parsed.data.repo,
-        });
+        const octokit = process.env.GITHUB_TOKEN
+            ? new Octokit({ auth: process.env.GITHUB_TOKEN })
+            : new Octokit();
+
+        let response;
+        try {
+            response = await octokit.rest.repos.get({
+                owner: parsed.data.owner,
+                repo: parsed.data.repo,
+            });
+        } catch (err: any) {
+            const status = err?.status;
+            if (status === 404) {
+                res.status(404).json({ error: 'GitHub repo not found' });
+                return;
+            }
+            if (status === 401 || status === 403) {
+                res.status(403).json({ error: 'GitHub access denied' });
+                return;
+            }
+            if (status === 429) {
+                res.status(429).json({ error: 'GitHub rate limit exceeded' });
+                return;
+            }
+            logger.error({ err }, 'GitHub API request failed');
+            res.status(502).json({ error: 'GitHub API error' });
+            return;
+        }
🤖 Prompt for AI Agents
In `@packages/backend/src/api.ts` around lines 111 - 137, Replace the
unauthenticated, unhandled GitHub call by instantiating Octokit with
process.env.GITHUB_TOKEN when present (use the Octokit constructor) and wrap the
octokit.rest.repos.get(...) call in a try/catch; on error inspect err.status (or
err.statusCode) and return matching HTTP responses for 404 (res.status(404)),
403 (res.status(403)), and 429 (res.status(429)) with a helpful message, and for
other errors return a 502/500 as appropriate; keep the subsequent
createGitHubRepoRecord(...), prisma.repo.upsert(...) and
repoIndexManager.createJobs(...) logic but only run them after a successful repo
fetch.

@brendan-kellam brendan-kellam merged commit 204fc82 into main Jan 23, 2026
9 checks passed
@brendan-kellam brendan-kellam deleted the bkellam/gh-on-demand-indexing-SOU-109 branch January 23, 2026 01:14
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.

2 participants