Skip to content
This repository was archived by the owner on Nov 24, 2025. It is now read-only.

Commit 0d8bdad

Browse files
committed
github review tab
1 parent 11ce1c2 commit 0d8bdad

File tree

14 files changed

+596
-79
lines changed

14 files changed

+596
-79
lines changed

package.json

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -85,42 +85,47 @@
8585
{
8686
"command": "twinny.openChat",
8787
"group": "navigation@0",
88-
"when": "view == twinny.sidebar && twinnyManageTemplates || view == twinny.sidebar && twinnyManageProviders || view == twinny.sidebar && twinnyConversationHistory || view == twinny.sidebar && twinnySymmetryTab"
88+
"when": "view == twinny.sidebar && twinnyManageTemplates || view == twinny.sidebar && twinnyManageProviders || view == twinny.sidebar && twinnyConversationHistory || view == twinny.sidebar && twinnySymmetryTab || view == twinny.sidebar && twinnyReviewTab"
8989
},
9090
{
91-
"command": "twinny.symmetry",
91+
"command": "twinny.review",
9292
"group": "navigation@1",
9393
"when": "view == twinny.sidebar"
9494
},
9595
{
96-
"command": "twinny.manageProviders",
96+
"command": "twinny.symmetry",
9797
"group": "navigation@2",
9898
"when": "view == twinny.sidebar"
9999
},
100100
{
101-
"command": "twinny.manageTemplates",
101+
"command": "twinny.manageProviders",
102102
"group": "navigation@3",
103103
"when": "view == twinny.sidebar"
104104
},
105+
{
106+
"command": "twinny.manageTemplates",
107+
"group": "navigation@4",
108+
"when": "view == twinny.sidebar"
109+
},
105110
{
106111
"command": "twinny.templates",
107112
"when": "view == twinny.sidebar",
108-
"group": "navigation@4"
113+
"group": "navigation@5"
109114
},
110115
{
111116
"command": "twinny.conversationHistory",
112117
"when": "view == twinny.sidebar",
113-
"group": "navigation@5"
118+
"group": "navigation@6"
114119
},
115120
{
116121
"command": "twinny.newConversation",
117122
"when": "view == twinny.sidebar",
118-
"group": "navigation@6"
123+
"group": "navigation@7"
119124
},
120125
{
121126
"command": "twinny.settings",
122127
"when": "view == twinny.sidebar",
123-
"group": "navigation@7"
128+
"group": "navigation@8"
124129
}
125130
]
126131
},
@@ -163,6 +168,10 @@
163168
"title": "Disable twinny",
164169
"shortTitle": "Disable twinny"
165170
},
171+
{
172+
"command": "twinny.getGitCommitMessage",
173+
"title": "Twinny - Generate commit message"
174+
},
166175
{
167176
"command": "twinny.sidebar.focus",
168177
"shortTitle": "Enable twinny sidebar",
@@ -174,6 +183,12 @@
174183
"title": "Open twinny settings",
175184
"icon": "$(gear)"
176185
},
186+
{
187+
"command": "twinny.review",
188+
"shortTitle": "twinny code reviewer",
189+
"title": "Code reviewer",
190+
"icon": "$(git-pull-request)"
191+
},
177192
{
178193
"command": "twinny.symmetry",
179194
"shortTitle": "twinny symmetry network",
@@ -376,6 +391,12 @@
376391
"type": "string",
377392
"description": "The symmetry master server key.",
378393
"default": "4b4a9cc325d134dee6679e9407420023531fd7e96c563f6c5d00fd5549b77435"
394+
},
395+
"twinny.githubToken": {
396+
"order": 27,
397+
"type": "string",
398+
"default": "",
399+
"description": "Your GitHub token. Used for fetching GitHub data."
379400
}
380401
}
381402
}

src/common/constants.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ export const EVENT_NAME = {
7878
twinnySetWorkspaceContext: 'twinny-set-workspace-context',
7979
twinnyStopGeneration: 'twinny-stop-generation',
8080
twinnyTextSelection: 'twinny-text-selection',
81-
twinnyGetWorkspaceContext: 'twinny-workspace-context'
81+
twinnyGetWorkspaceContext: 'twinny-workspace-context',
82+
twinnyGithhubReview: 'twinny-githhub-review'
8283
}
8384

8485
export const TWINNY_COMMAND_NAME = {
@@ -87,6 +88,7 @@ export const TWINNY_COMMAND_NAME = {
8788
conversationHistory: 'twinny.conversationHistory',
8889
disable: 'twinny.disable',
8990
enable: 'twinny.enable',
91+
review: 'twinny.review',
9092
explain: 'twinny.explain',
9193
focusSidebar: 'twinny.sidebar.focus',
9294
generateDocs: 'twinny.generateDocs',
@@ -151,7 +153,9 @@ export const WORKSPACE_STORAGE_KEY = {
151153
selectedTemplates: 'selectedTemplates',
152154
selection: 'selection',
153155
showEmbeddingOptions: 'showEmbeddingOptions',
154-
showProviders: 'showProviders'
156+
showProviders: 'showProviders',
157+
reviewOwner: 'reviewOwner',
158+
reviewRepo: 'reviewRepo',
155159
}
156160

157161
export const EXTENSION_SETTING_KEY = {
@@ -166,6 +170,7 @@ export const EXTENSION_CONTEXT_NAME = {
166170
twinnyGeneratingText: 'twinnyGeneratingText',
167171
twinnyManageProviders: 'twinnyManageProviders',
168172
twinnyManageTemplates: 'twinnyManageTemplates',
173+
twinnyReviewTab: 'twinnyReviewTab',
169174
twinnyRerankThreshold: 'twinnyRerankThreshold',
170175
twinnyMaxChunkSize: 'twinnyMaxChunkSize',
171176
twinnyMinChunkSize: 'twinnyMinChunkSize',
@@ -186,6 +191,7 @@ export const WEBUI_TABS = {
186191
chat: 'chat',
187192
history: 'history',
188193
providers: 'providers',
194+
review: 'review',
189195
settings: 'templates',
190196
symmetry: 'symmetry'
191197
}
@@ -242,6 +248,11 @@ export const DEFAULT_PROVIDER_FORM_VALUES = {
242248
type: 'chat'
243249
}
244250

251+
export const GITHUB_EVENT_NAME = {
252+
getPullRequests: 'github.getPullRequests',
253+
getPullRequestReview: 'github.getPullRequestReview'
254+
}
255+
245256
export const TITLE_GENERATION_PROMPT_MESAGE = `
246257
Generate a title for this conversation in under 10 words.
247258
It should not contain any special characters or quotes.

src/common/types.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ export interface ClientMessage<T = string | boolean | Message[], Y = unknown> {
6666
key?: string
6767
}
6868

69+
export type ClientMessageWithData = ClientMessage<string | boolean> &
70+
ClientMessage<Message[]> &
71+
ClientMessage<GithubPullRequestMessage>
72+
6973
export interface ServerMessage<T = LanguageType> {
7074
type: string
7175
value: {
@@ -81,6 +85,12 @@ export interface Message {
8185
content: string | undefined
8286
}
8387

88+
export interface GithubPullRequestMessage {
89+
owner: string
90+
repo: string
91+
number: number
92+
}
93+
8494
export interface Conversation {
8595
id?: string
8696
title?: string

src/extension/conversation-history.ts

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -29,30 +29,30 @@ import { SymmetryService } from './symmetry-service'
2929
type Conversations = Record<string, Conversation> | undefined
3030

3131
export class ConversationHistory {
32-
private _context: ExtensionContext
33-
private _config = workspace.getConfiguration('twinny')
34-
private _keepAlive = this._config.get('keepAlive') as string | number
32+
public config = workspace.getConfiguration('twinny')
33+
public context: ExtensionContext
34+
public keepAlive = this.config.get('keepAlive') as string | number
35+
public temperature = this.config.get('temperature') as number
36+
public webView: Webview
3537
private _sessionManager: SessionManager | undefined
3638
private _symmetryService: SymmetryService
37-
private _temperature = this._config.get('temperature') as number
3839
private _title = ''
39-
private _webView: Webview
4040

4141
constructor(
4242
context: ExtensionContext,
4343
webView: Webview,
4444
sessionManager: SessionManager | undefined,
4545
symmetryService: SymmetryService
4646
) {
47-
this._context = context
48-
this._webView = webView
47+
this.context = context
48+
this.webView = webView
4949
this._sessionManager = sessionManager
5050
this._symmetryService = symmetryService
5151
this.setUpEventListeners()
5252
}
5353

5454
setUpEventListeners() {
55-
this._webView?.onDidReceiveMessage(
55+
this.webView?.onDidReceiveMessage(
5656
(message: ClientMessage<Conversation>) => {
5757
this.handleMessage(message)
5858
}
@@ -62,7 +62,7 @@ export class ConversationHistory {
6262
SYMMETRY_EMITTER_KEY.conversationTitle,
6363
(completion: string) => {
6464
const activeConversation = this.getActiveConversation()
65-
this._webView?.postMessage({
65+
this.webView?.postMessage({
6666
type: CONVERSATION_EVENT_NAME.getActiveConversation,
6767
value: {
6868
data: {
@@ -130,13 +130,13 @@ export class ConversationHistory {
130130
})
131131
}
132132

133-
private getProvider = () => {
134-
return this._context?.globalState.get<TwinnyProvider>(
133+
public getProvider = () => {
134+
return this.context?.globalState.get<TwinnyProvider>(
135135
ACTIVE_CHAT_PROVIDER_STORAGE_KEY
136136
)
137137
}
138138

139-
private buildStreamRequest(messages?: Message[]) {
139+
public buildStreamRequest(messages?: Message[]) {
140140
const provider = this.getProvider()
141141

142142
if (!provider || !messages?.length) return
@@ -156,15 +156,15 @@ export class ConversationHistory {
156156
const requestBody = createStreamRequestBody(provider.provider, {
157157
model: provider.modelName,
158158
numPredictChat: 100,
159-
temperature: this._temperature,
159+
temperature: this.temperature,
160160
messages: [
161161
...messages,
162162
{
163163
role: 'user',
164164
content: TITLE_GENERATION_PROMPT_MESAGE
165165
}
166166
],
167-
keepAlive: this._keepAlive
167+
keepAlive: this.keepAlive
168168
})
169169

170170
return { requestOptions, requestBody }
@@ -178,7 +178,7 @@ export class ConversationHistory {
178178

179179
getAllConversations() {
180180
const conversations = this.getConversations() || {}
181-
this._webView?.postMessage({
181+
this.webView?.postMessage({
182182
type: CONVERSATION_EVENT_NAME.getConversations,
183183
value: {
184184
data: conversations
@@ -187,33 +187,33 @@ export class ConversationHistory {
187187
}
188188

189189
getConversations(): Conversations {
190-
const conversations = this._context.globalState.get<
190+
const conversations = this.context.globalState.get<
191191
Record<string, Conversation>
192192
>(CONVERSATION_STORAGE_KEY)
193193
return conversations
194194
}
195195

196196
resetConversation() {
197-
this._context.globalState.update(ACTIVE_CONVERSATION_STORAGE_KEY, undefined)
197+
this.context.globalState.update(ACTIVE_CONVERSATION_STORAGE_KEY, undefined)
198198
this.setActiveConversation(undefined)
199199
}
200200

201201
updateConversation(conversation: Conversation) {
202202
const conversations = this.getConversations() || {}
203203
if (!conversation.id) return
204-
this._context.globalState.update(CONVERSATION_STORAGE_KEY, {
204+
this.context.globalState.update(CONVERSATION_STORAGE_KEY, {
205205
...conversations,
206206
[conversation.id]: conversation
207207
})
208208
this.setActiveConversation(conversation)
209209
}
210210

211211
setActiveConversation(conversation: Conversation | undefined) {
212-
this._context.globalState.update(
212+
this.context.globalState.update(
213213
ACTIVE_CONVERSATION_STORAGE_KEY,
214214
conversation
215215
)
216-
this._webView?.postMessage({
216+
this.webView?.postMessage({
217217
type: CONVERSATION_EVENT_NAME.getActiveConversation,
218218
value: {
219219
data: conversation
@@ -224,7 +224,7 @@ export class ConversationHistory {
224224

225225
getActiveConversation() {
226226
const conversation: Conversation | undefined =
227-
this._context.globalState.get(ACTIVE_CONVERSATION_STORAGE_KEY)
227+
this.context.globalState.get(ACTIVE_CONVERSATION_STORAGE_KEY)
228228
this.setActiveConversation(conversation)
229229
return conversation
230230
}
@@ -233,15 +233,15 @@ export class ConversationHistory {
233233
const conversations = this.getConversations() || {}
234234
if (!conversation?.id) return
235235
delete conversations[conversation.id]
236-
this._context.globalState.update(CONVERSATION_STORAGE_KEY, {
236+
this.context.globalState.update(CONVERSATION_STORAGE_KEY, {
237237
...conversations
238238
})
239239
this.setActiveConversation(undefined)
240240
this.getAllConversations()
241241
}
242242

243243
clearAllConversations() {
244-
this._context.globalState.update(CONVERSATION_STORAGE_KEY, {})
244+
this.context.globalState.update(CONVERSATION_STORAGE_KEY, {})
245245
this.setActiveConversation(undefined)
246246
}
247247

@@ -287,7 +287,7 @@ export class ConversationHistory {
287287
messages: conversation.messages
288288
}
289289
conversations[id] = newConversation
290-
this._context.globalState.update(CONVERSATION_STORAGE_KEY, conversations)
290+
this.context.globalState.update(CONVERSATION_STORAGE_KEY, conversations)
291291
this.setActiveConversation(newConversation)
292292
this._title = ''
293293
}

src/extension/providers/base.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,13 @@ import { Logger } from '../../common/logger'
3737
import { DiffManager } from '../diff'
3838
import { ProviderManager } from '../provider-manager'
3939
import { FileTreeProvider } from '../tree'
40+
import { GithubService as ReviewService } from '../review-service'
4041

4142
const logger = new Logger()
4243

4344
export class BaseProvider {
44-
public context: vscode.ExtensionContext
45-
public webView?: vscode.Webview
46-
public conversationHistory: ConversationHistory | undefined = undefined
47-
private _config = vscode.workspace.getConfiguration('twinny')
4845
private _chatService: ChatService | undefined = undefined
46+
private _config = vscode.workspace.getConfiguration('twinny')
4947
private _diffManager = new DiffManager()
5048
private _embeddingDatabase: EmbeddingDatabase | undefined
5149
private _fileTreeProvider: FileTreeProvider
@@ -55,6 +53,10 @@ export class BaseProvider {
5553
private _symmetryService?: SymmetryService | undefined
5654
private _templateDir: string | undefined
5755
private _templateProvider: TemplateProvider
56+
public context: vscode.ExtensionContext
57+
public conversationHistory: ConversationHistory | undefined = undefined
58+
public reviewService: ReviewService | undefined = undefined
59+
public webView?: vscode.Webview
5860

5961
constructor(
6062
context: vscode.ExtensionContext,
@@ -99,6 +101,14 @@ export class BaseProvider {
99101
this._symmetryService
100102
)
101103

104+
this.reviewService = new ReviewService(
105+
this.context,
106+
this.webView,
107+
this._sessionManager,
108+
this._symmetryService,
109+
this._templateDir
110+
)
111+
102112
new ProviderManager(this.context, this.webView)
103113

104114
vscode.window.onDidChangeActiveColorTheme(() => {
@@ -201,7 +211,9 @@ export class BaseProvider {
201211
}
202212
if (!this._embeddingDatabase) return
203213
for (const dir of dirs) {
204-
(await this._embeddingDatabase.injestDocuments(dir.uri.fsPath)).populateDatabase()
214+
(
215+
await this._embeddingDatabase.injestDocuments(dir.uri.fsPath)
216+
).populateDatabase()
205217
}
206218
}
207219

0 commit comments

Comments
 (0)