Skip to content

gh comment integration#53

Open
sesceu wants to merge 3 commits intosbarfurth:mainfrom
sesceu:main
Open

gh comment integration#53
sesceu wants to merge 3 commits intosbarfurth:mainfrom
sesceu:main

Conversation

@sesceu
Copy link

@sesceu sesceu commented Feb 13, 2026

GitHub Pull Request Comment Integration

This PR introduces full integration with GitHub Pull Request comments directly within VS Code, allowing users to view, reply to, and resolve comment threads without leaving the editor.

Features

  • Inline Comments: Displays GitHub PR review comments in the editor for the active file.
  • Review Threads: Supports threaded conversations, mapping them correctly to lines in the working copy.
  • Interactive:
    • Reply: Post replies to existing threads.
    • Resolve: Mark threads as resolved directly from VS Code.
  • Polling: Automatically polls for new comments and threads (configurable interval).
  • Robustness: Handles unstable network conditions and malformed CLI output gracefully.

Configuration & Authentication

The extension relies on the gh CLI for communication with GitHub. You can configure authentication in two ways:

1. Default gh Auth

If you already use gh on the command line, simply run:
gh auth login
The extension will automatically use your existing gh credentials if the environment variable is correctly injected into vscode (or vscode-server) or access is configured through gh's config files.

2. Personal Access Token (PAT)

If you prefer to use a specific token or need to support multiple organizations with different credentials, you can configure tokens in your VS Code settings:

  • ukemi.githubToken: A default PAT for all repositories.
  • ukemi.githubTokens: A map of organization names to PATs (e.g., {"my-org": "ghp_..."}).

Required Access Rights

If using a Personal Access Token (PAT), ensuring the correct scopes is critical for full functionality:

Classic PAT

  • repo (Full control of private repositories) - Required for all operations.

Fine-grained PAT

  • Repository access: "All repositories" or specific repositories.
  • Permissions:
    • Pull requests (Read and Write) - Required to create and read comments.
    • Contents (Read and Write) - Required to resolve threads (via GraphQL resolveReviewThread mutation) and to read file content for diff context.
    • Metadata (Read) - Mandatory for all fine-grained tokens.

Settings

  • ukemi.githubPRPollInterval: Polling interval in minutes (default: 5). Set to 0 to disable background polling.
  • ukemi.githubPRSearchLimit: Number of parent commits to search when trying to associate the working copy with a PR (default: 10).

- Refactor `CommentControllerManager` to update comment threads incrementally instead of recreating them, reducing UI flickering.
- Optimize GitHub polling in `main.ts` to use `setInterval` with proper cancellation and configuration listeners, replacing recursive `setTimeout`.
- Harden `GitHubRepository` against malformed JSON responses from the `gh` CLI.
- Fix test setup in `runTest.ts` to use robust `jj new 'root()'` syntax to avoid ambiguous revision errors.
@sesceu sesceu changed the title WIP: gh comment integration gh comment integration Feb 24, 2026
@sbarfurth sbarfurth self-requested a review February 24, 2026 09:47
@sesceu
Copy link
Author

sesceu commented Feb 25, 2026

I'm currently testing this, so I will put my experiences here (and later provide fixes):

  • Sometime, when typing a comment, the view is being refreshed and the comment is (it seems) re-created, so whatever I typed is lost. We should probably be more careful when to delete and re-create comments.
  • I'm currently running into an issue where resolving a thread doesn't work. The UI is optimistic, and closes the comment (and re-opens if after the next sync), which I think is okay. but a) I'm pretty sure the PAT did work previously, so something might be wrong here, and b) it's bad UI. We might want to disable the resolve-buttons once we realize that resolving is not possible, and add a tooltip telling the user to check their token.
  • I had a case, where I was shown two comments in a thread in vscode, while on Github there was only one. Maybe one was deleted in Github, but still provided? Need to check if the comment object has a corresponding field.

Copy link
Owner

@sbarfurth sbarfurth left a comment

Choose a reason for hiding this comment

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

Not a super detailed review since it's a lot of code. thanks for this, it seems really useful!

Comment on lines +560 to +571
"ukemi.githubToken": {
"type": "string",
"default": "",
"description": "GitHub Personal Access Token (PAT) for fetching PR comments. If not set, the gh CLI's default authentication will be used.",
"scope": "resource"
},
"ukemi.githubTokens": {
"type": "object",
"default": {},
"description": "A map of GitHub organization names to Personal Access Tokens (PATs). Useful for working across multiple organizations with different auth requirements. PAT tokens need read/write access to Pull requests and Contents.",
"scope": "resource"
},
Copy link
Owner

Choose a reason for hiding this comment

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

Remove both of these. Storing tokens in VSCode settings is not a good idea. None of the built-in GitHub extensions have this kind of storage. Either we rely entirely on gh CLI or we build a mechanism where the extension has its own OAuth flow.

},
"ukemi.githubPRSearchLimit": {
"type": "number",
"default": 10,
Copy link
Owner

Choose a reason for hiding this comment

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

I think the default should be 1. I'd expect people to have their working copy right on top of the latest branch state. How was 10 chosen?

},
"ukemi.githubPRPollInterval": {
"type": "number",
"default": 5,
Copy link
Owner

Choose a reason for hiding this comment

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

Does that seem like a long time? I always felt the regular GitHub extension was pretty snappy, so this seems like it would be pretty annoying. Just wondering how you came up with the number.


export class CommentControllerManager {
private commentController: vscode.CommentController;
private prCache = new Map<string, GHPR | null>(); // rev -> PR
Copy link
Owner

Choose a reason for hiding this comment

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

What does it mean for the cache to point to null? Seems like that should just be ejected.

import { getLogger } from "./logger";
import { ChildProcess } from "child_process";

export interface GHComment {
Copy link
Owner

Choose a reason for hiding this comment

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

No need to prefix everything with GH. This is in github.ts, so it's implicitly namespaced.

}
}

private mapToVSCodeComment(ghComment: GHComment): vscode.Comment {
Copy link
Owner

Choose a reason for hiding this comment

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

Feels like this should be a function in the github.ts file next to the Comment interface


private displayComments(uri: vscode.Uri, fileComments: GHComment[], threads: GHThreadInfo[], prNumber: number, repoSlug: string, _clearExisting = true) {
// We group comments by line number because the GitHub REST API returns a flat list of comments.
// While GraphQL provides thread threads, we currently rely on the REST data for comment content.
Copy link
Owner

Choose a reason for hiding this comment

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

What is "thread threads"?

this.githubRepository.invalidateAuthCache();
}

private displayComments(uri: vscode.Uri, fileComments: GHComment[], threads: GHThreadInfo[], prNumber: number, repoSlug: string, _clearExisting = true) {
Copy link
Owner

Choose a reason for hiding this comment

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

Make this more usable at the callsite by providing args as an object

this.githubRepository.invalidateAuthCache();
}

private displayComments(uri: vscode.Uri, fileComments: GHComment[], threads: GHThreadInfo[], prNumber: number, repoSlug: string, _clearExisting = true) {
Copy link
Owner

Choose a reason for hiding this comment

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

Why the underscore on clear existing?

// Create or update threads
for (const [line, lineComments] of threadsByLine) {
// Sort comments by creation date
lineComments.sort((a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime());
Copy link
Owner

Choose a reason for hiding this comment

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

This sorts in place inside the threadsByLine. That seems confusing to me. Can you sort a copy of the array instead?

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