Repository navigation
feat: add GitHub local project integration and enhance local attachments handling: - #324
Conversation
…nts handling: - Introduced a new API route for creating local projects from GitHub repositories, allowing users to link their GitHub projects directly. - Implemented functionality to upsert local projects, ensuring existing projects are updated with the latest data. - Enhanced local attachments handling in session messages, increasing the maximum number of local attachments from 8 to 16. - Updated the local project context to provide clearer guidance on using attached files during sessions. - Added a .gitignore file to exclude Vercel-specific files from version control.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
|
Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configuration
⛔ Files ignored due to path filters (2)
📒 Files selected for processing (31)
✨ Finishing Touches
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. Comment |
|
| const account = await terminalPrisma.account.findFirst({ | ||
| where: { providerId: "github", user: { email: { equals: user.email, mode: "insensitive" } } }, | ||
| select: { accessToken: true, accessTokenExpiresAt: true }, |
There was a problem hiding this comment.
If the caller's GitHub authorization is unavailable, this fallback selects a terminal account's token by their stored email. updateUserProfile lets the caller change that email without proving ownership. They can also create a local project with any repositoryFullName and paths; unlike indexGithubRepository, packGithubProjectForTurn never checks repository ownership. Together, these paths allow reads of another person's private files. Require a verified account link and check repository access before fetching.
How this was verified: An authenticated profile update controls the email used to select the terminal GitHub token, and caller-supplied project paths reach
getContentwithout a repository ownership check.
| if (selection.kind === "none") { | ||
| setLocalProject(null) | ||
| setVirtualProjectIndex(null) |
There was a problem hiding this comment.
Cleared projects still upload files
After opening a local folder, selecting “No project” only clears localProject and virtualProject. The old memoryState, memoryHandle, and cached file handles remain active. Switching to GitHub leaves them active too. Both workspace getters still prefer the old folder, so the next runTurn binds it again and automatically uploads its files. Clear or replace the active folder state when switching, and pack only the selected project.
How this was verified: The selection leaves the old folder handles intact, the workspace getters return that folder first, and the next turn sends files read through those handles.
| const imageNotes = localAttachments | ||
| .filter((file) => file.kind === "image" && file.dataBase64) | ||
| .map((file) => `Image attachment “${file.name}” (${file.mediaType}, ${file.size} bytes) was provided as base64 and is available to vision-capable routes.`) | ||
| if (imageNotes.length) { | ||
| text += `\n\n${imageNotes.join("\n")}` | ||
| } | ||
| if (mode === "agent") { | ||
| text += `\n\n[Nova agent mode] Implement now. Prefer full file replacements with path= fences for every file you change. Do not reply with only a plan.` | ||
| } | ||
| userMessage.content = text |
There was a problem hiding this comment.
Uploaded images lose their pixels
Image attachments now become filename and size notes rather than image parts. No image bytes reach streamHarnessChat, despite the note saying they are available to vision routes. flattenHarnessContent also drops image parts from other callers. Screenshot questions therefore stop working even on vision-capable models. Preserve image parts through both functions and fix string conversion only on routes that need it.
| const labelRoot = state.displayName || state.rootName | ||
| // GitHub-backed virtual projects still use local: ids for path search UX; | ||
| // content is resolved via attachments or GitHub references on the server. | ||
| return matched.slice(0, limit).map((entry) => localFileReference(entry, labelRoot, state.projectId)) |
There was a problem hiding this comment.
GitHub file mentions cannot attach
GitHub-backed virtual entries now receive local: IDs. The file picker puts them before remote results, but Composer.pickReference treats every local: ID as a browser-folder file. With no folder handle, selecting one returns “Local file is no longer available” and never attaches it. Emit a server-resolvable GitHub reference or add a GitHub read path for these entries.
| export function isLikelyTextProjectPath(path: string): boolean { | ||
| const base = path.toLowerCase().split("/").pop() || "" | ||
| if (CONFIG_NAMES.has(base)) return true | ||
| return SOURCE_EXT.test(base) | ||
| || /\.(json|ya?ml|toml|svg|graphql|gql|sql|prisma|env|sh|html)$/i.test(base) |
There was a problem hiding this comment.
Packing skips supported source languages
indexGithubRepository indexes Python, Go, Rust, Java, and other supported files through isReferenceFile, but this second filter removes them before fetching. A project containing main.py and requirements.txt sends the requirements but never the Python source, even when the request names main.py. Use the same supported-file rules for indexing and packing, and include these languages in SOURCE_EXT.
| const existing = (await listLocalProjects(userId)).find( | ||
| (project) => project.repositoryFullName?.toLowerCase() === fullName.toLowerCase(), | ||
| ) |
There was a problem hiding this comment.
Refresh can create duplicate projects
This lookup searches only the recent-project list. listLocalProjects returns 50 active projects, so an older match is missed and duplicated. Concurrent requests can also both miss the project and create separate rows because there is no unique repository key. Look up the repository directly and make the create-or-update operation safe for concurrent requests.
|
|
||
| return { | ||
| paths, | ||
| truncated: Boolean(tree.truncated) || (tree.tree?.length ?? 0) > paths.length, |
There was a problem hiding this comment.
Complete indexes appear partial
tree.tree includes directories and unsupported files, while paths contains only supported blobs. Comparing their lengths marks a complete repository as truncated whenever it contains a directory or an ignored file. Users and the model then receive a false partial-index warning. Compare the supported path count before and after the limit, while retaining GitHub's tree.truncated flag.
|
|
||
| <button | ||
| type="button" | ||
| onClick={() => onNavigate("/settings?section=profile")} |
There was a problem hiding this comment.
GitHub management opens the profile
“Manage the GitHub installation” opens the profile page, which only shows account details and account deletion guidance. It has no GitHub connection or installation controls. Users with missing repositories reach a page that cannot help them. Point the button to the actual GitHub setup flow.
| <button | ||
| type="button" | ||
| className="flex size-7 items-center justify-center rounded-md text-[#5c5c5c] hover:bg-white/[0.05] hover:text-[#c8c8c8]" | ||
| aria-label="Project actions" | ||
| > | ||
| <MoreHorizontal className="size-3.5" /> |
| const packedAttachments = await packLocalProjectForTurn( | ||
| content, | ||
| opts?.localAttachments ?? [], | ||
| ) |
There was a problem hiding this comment.
Automatic context needs a budget
Every turn now automatically adds up to 16 source files, but packing only checks file counts and bytes. Local reads retain up to 120,000 characters per file, so nearly 1.92 million characters can be appended after buildTurnContext applies its 24,000-character history limit. This can overwhelm smaller models and sharply increase input cost without the user choosing those attachments. Give automatic packing a total text budget and report files shortened or omitted.
Description
Type of change
How Has This Been Tested?
Please describe the tests that you ran to verify your changes.
bun testpassesbun run typecheckpassesbun run lintpasses (if applicable)Checklist: