Skip to content

feat: evaluate review area options#325

Open
CamilleLetavernier wants to merge 13 commits into
issues/287-sessionsListfrom
issues/290-reviews
Open

feat: evaluate review area options#325
CamilleLetavernier wants to merge 13 commits into
issues/287-sessionsListfrom
issues/290-reviews

Conversation

@CamilleLetavernier

Copy link
Copy Markdown

What it does

  • Add an AI Review view in the AI First perspective (First draft, to be refined and extended)
  • The AI can review a change set (Currently only supporting the active Worktree, but more providers can be added)
  • AI Review groups changes by topic and annotates each file to explain changes one by one
  • AI Review can optionally take an "Intent" input describing the task being reviewed (Task Context(s), Chat Session(s) summary or free text from the user)
  • (Placeholder) user can mark changes as Reviewed/Needs Work/Dismissed (These actions are not wired to anything)
image

How to test

  • Open a Workspace with a dirty worktree
  • Open the AI First perspective (may need to explicitly open AI Review view if the perspective was persisted with an older layout)
  • Optionally add an intent describing the task to review
  • Press review
  • Once the review is complete, check the description of each "Area" (Topic) and navigate to the related files
  • The files should be annotated with comments (bullet points on the left side) describing each section

Follow-ups

Breaking changes

  • This PR introduces breaking changes and requires careful review. If yes, the breaking changes section in the changelog has been updated.

Attribution

Review checklist

Reminder for reviewers

@CamilleLetavernier CamilleLetavernier changed the title Issues/290 reviews feat: evaluate review area options Jul 10, 2026
@CamilleLetavernier
CamilleLetavernier changed the base branch from ai-first to issues/287-sessionsList July 10, 2026 07:18

@EclipseSourceAI EclipseSourceAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Note

Autonomous AI review. Generated automatically, so it may contain mistakes; feel free to ignore any comment you disagree with (noting why helps future reviews).

An approval here does not mean the change is free of architectural issues; the overall architecture and design should still be reviewed by a human.

To get an updated review after pushing changes, re-request a review from this account.

Adds an AI Review view to the AI First perspective: reviews the git worktree change set, groups changes into "Areas", annotates files with per-hunk editor decorations, and accepts an optional "Intent" input. Solid draft: clean provider abstraction (ReviewChangeSetProvider via ContributionProvider), reuse of @theia/scm DiffComputer for hunks, and good test coverage (tests + lint pass).

Focus areas for a human: (1) review-diff-decorator.ts leaks decorations for diff editors, since clearDecorations can't reach decorations applied via applyToMonacoDiffEditor, so re-reviews accumulate stale annotations; (2) review-summary-agent.ts registers a prompt fragment it never uses and calls the model outside the Agent framework under an unregistered agent id, so its model/prompt aren't configurable; (3) the review storage preference is unregistered and ignores workspace scope. Remaining notes are i18n gaps and a minor filename-truncation bug. Nothing here blocks the draft, but the decoration leak is worth addressing before this is refined further.

}
}
this.appliedDecorations.clear();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

clearDecorations can't clear diff-editor decorations. getDecorationTarget returns undefined for MonacoDiffEditor, and applyToMonacoDiffEditor stores IDs under a diff-modified:* key that never matches decorationKey(target). So the map is emptied but the monaco decorations stay on the modified editor, and the next applyToMonacoDiffEditor reads oldDecorations: [], leaving the old ones orphaned. Result: stale/duplicated annotations on re-review with a diff open (the common path).

return undefined;
}
return editor;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This 15-line stream-of-consciousness comment describing what MonacoDiffEditor does internally should be trimmed to one line. The method just returns undefined for diff editors.

this.promptService.addBuiltInPromptFragment({
id: REVIEW_SUMMARY_PROMPT_ID,
template: reviewSummaryPromptTemplate.template,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This fragment is registered on every review but never used: buildPrompt sets systemMessage to reviewSummaryPromptTemplate.template directly, so PromptService and this call are dead. Either resolve the system prompt through the prompt service (so it becomes customizable) or drop the registration and the injection.


async reviewChangeSet(cs: ReviewChangeSet, intents?: ReviewIntent[]): Promise<ReviewResult> {
const diffs = this.collectDiffs(cs);
const model = await this.languageModelRegistry.selectLanguageModel({ agent: 'review-summary', purpose: 'chat', identifier: 'default/code' });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

agent: 'review-summary' refers to an agent that is never registered (this service is bound only toSelf, not as Agent), so its model and prompt aren't configurable in AI settings the way the sibling pr-reviewer agent is (

export class PRReviewAgent extends AbstractStreamParsingChatAgent {
id = PRReviewAgentId;
name = 'PR Reviewer';
languageModelRequirements: LanguageModelRequirement[] = [{
purpose: 'chat',
identifier: 'default/code',
}];
protected defaultLanguageModelPurpose: string = 'chat';
override description = nls.localize('theia/ai/ide/prReviewAgent/description',
'An AI-powered PR review agent that orchestrates a full code review workflow: fetches PR info, explores the codebase, ' +
'performs structured analysis, interactively walks the user through findings with diff viewers, and optionally creates a pending review on GitHub.');
override iconClass: string = 'codicon codicon-git-pull-request-go-to-changes';
override prompts = [{ id: PR_REVIEW_SYSTEM_PROMPT_ID, defaultVariant: prReviewSystemPrompt, variants: [] }];
protected override systemPromptId: string = PR_REVIEW_SYSTEM_PROMPT_ID;
override tags: string[] = [...this.tags, 'Alpha'];
). Worth registering as an Agent so it fits the framework. The file is also named *-agent.ts but exports ReviewSummaryService.

return undefined;
}
const values = this.preferenceService.inspect(REVIEW_STORAGE_DIRECTORY_PREF);
const configuredPath = values?.globalValue ?? values?.defaultValue ?? REVIEW_STORAGE_DEFAULT_DIR;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ai-features.review.storageDirectory isn't registered in any preference schema, so it can't be discovered/set via settings, and reading globalValue ?? defaultValue skips a workspace-level value. Register it in the schema and use preferenceService.get(REVIEW_STORAGE_DIRECTORY_PREF) which resolves scope precedence.


protected reviewFilename(id: string): string {
const sanitized = id.replace(/[^\p{L}\p{N}]/ug, '-').replace(/^-+|-+$/g, '');
const truncated = sanitized.length > 32 ? sanitized.slice(0, sanitized.indexOf('-', 32) || 32) : sanitized;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

sanitized.indexOf('-', 32) || 32 returns -1 when there is no dash past index 32, so slice(0, -1) drops the last character instead of truncating to 32. Guard the -1 case.

}));
changeSets.push({
id: `git-worktree:${repo.provider.rootUri}`,
label: 'Working tree changes',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

'Working tree changes' is user-facing but not localized. Wrap with nls.localize.

<div className='ai-review-changeset-header' onClick={() => this.toggleExpand(cs.id)}>
<span className={codicon(isExpanded ? 'chevron-down' : 'chevron-right')} />
<span className='ai-review-changeset-label'>{cs.label}</span>
<span className='ai-review-changeset-badge'>{cs.files.length} {cs.files.length === 1 ? 'file' : 'files'}</span>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

'file'/'files' are hardcoded. Localize the count label (nls supports plural placeholders).

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