codebakers 1.0.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vscodeignore +18 -0
- package/LICENSE +21 -0
- package/README.md +88 -0
- package/codebakers-1.0.0.vsix +0 -0
- package/codebakers-1.0.10.vsix +0 -0
- package/codebakers-1.0.11.vsix +0 -0
- package/codebakers-1.0.12.vsix +0 -0
- package/codebakers-1.0.13.vsix +0 -0
- package/codebakers-1.0.14.vsix +0 -0
- package/codebakers-1.0.15.vsix +0 -0
- package/codebakers-1.0.16.vsix +0 -0
- package/codebakers-1.0.17.vsix +0 -0
- package/codebakers-1.0.18.vsix +0 -0
- package/codebakers-1.0.19.vsix +0 -0
- package/codebakers-1.0.20.vsix +0 -0
- package/codebakers-1.0.21.vsix +0 -0
- package/codebakers-1.0.22.vsix +0 -0
- package/codebakers-1.0.23.vsix +0 -0
- package/codebakers-1.0.24.vsix +0 -0
- package/codebakers-1.0.25.vsix +0 -0
- package/codebakers-1.0.26.vsix +0 -0
- package/codebakers-1.0.27.vsix +0 -0
- package/codebakers-1.0.28.vsix +0 -0
- package/codebakers-1.0.29.vsix +0 -0
- package/codebakers-1.0.30.vsix +0 -0
- package/codebakers-1.0.31.vsix +0 -0
- package/codebakers-1.0.32.vsix +0 -0
- package/codebakers-1.0.35.vsix +0 -0
- package/codebakers-1.0.36.vsix +0 -0
- package/codebakers-1.0.37.vsix +0 -0
- package/codebakers-1.0.38.vsix +0 -0
- package/codebakers-1.0.39.vsix +0 -0
- package/codebakers-1.0.40.vsix +0 -0
- package/codebakers-1.0.41.vsix +0 -0
- package/codebakers-1.0.42.vsix +0 -0
- package/codebakers-1.0.43.vsix +0 -0
- package/codebakers-1.0.44.vsix +0 -0
- package/codebakers-1.0.45.vsix +0 -0
- package/dist/extension.js +1394 -0
- package/esbuild.js +63 -0
- package/media/icon.png +0 -0
- package/media/icon.svg +7 -0
- package/nul +1 -0
- package/package.json +127 -0
- package/preview.html +547 -0
- package/src/ChatPanelProvider.ts +1815 -0
- package/src/ChatViewProvider.ts +749 -0
- package/src/CodeBakersClient.ts +1146 -0
- package/src/CodeValidator.ts +645 -0
- package/src/FileOperations.ts +410 -0
- package/src/ProjectContext.ts +526 -0
- package/src/extension.ts +332 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,749 @@
|
|
|
1
|
+
import * as vscode from 'vscode';
|
|
2
|
+
import { CodeBakersClient } from './CodeBakersClient';
|
|
3
|
+
import { ProjectContext } from './ProjectContext';
|
|
4
|
+
|
|
5
|
+
interface Message {
|
|
6
|
+
role: 'user' | 'assistant';
|
|
7
|
+
content: string;
|
|
8
|
+
timestamp: Date;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class ChatViewProvider implements vscode.WebviewViewProvider {
|
|
12
|
+
private _view?: vscode.WebviewView;
|
|
13
|
+
private _messages: Message[] = [];
|
|
14
|
+
private _conversationSummary: string = '';
|
|
15
|
+
|
|
16
|
+
constructor(
|
|
17
|
+
private readonly context: vscode.ExtensionContext,
|
|
18
|
+
private readonly client: CodeBakersClient,
|
|
19
|
+
private readonly projectContext: ProjectContext
|
|
20
|
+
) {}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Refresh the webview after login/logout
|
|
24
|
+
*/
|
|
25
|
+
refresh() {
|
|
26
|
+
if (this._view) {
|
|
27
|
+
this._initializeStatus();
|
|
28
|
+
this._updateWebview();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
resolveWebviewView(
|
|
33
|
+
webviewView: vscode.WebviewView,
|
|
34
|
+
_context: vscode.WebviewViewResolveContext,
|
|
35
|
+
_token: vscode.CancellationToken
|
|
36
|
+
) {
|
|
37
|
+
this._view = webviewView;
|
|
38
|
+
|
|
39
|
+
webviewView.webview.options = {
|
|
40
|
+
enableScripts: true,
|
|
41
|
+
localResourceRoots: [this.context.extensionUri]
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
webviewView.webview.html = this._getHtmlForWebview(webviewView.webview);
|
|
45
|
+
|
|
46
|
+
// Handle messages from the webview
|
|
47
|
+
webviewView.webview.onDidReceiveMessage(async (data) => {
|
|
48
|
+
switch (data.type) {
|
|
49
|
+
case 'sendMessage':
|
|
50
|
+
await this.sendMessage(data.message);
|
|
51
|
+
break;
|
|
52
|
+
case 'clearChat':
|
|
53
|
+
this._messages = [];
|
|
54
|
+
this._conversationSummary = '';
|
|
55
|
+
this._updateWebview();
|
|
56
|
+
break;
|
|
57
|
+
case 'runTool':
|
|
58
|
+
await this._executeTool(data.tool);
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Initialize plan and health on load (non-blocking)
|
|
64
|
+
this._initializeStatus();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private async _initializeStatus() {
|
|
68
|
+
if (!this._view) return;
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
// Get plan info (sync, always works)
|
|
72
|
+
const planInfo = this.client.getPlanInfo();
|
|
73
|
+
this._view.webview.postMessage({
|
|
74
|
+
type: 'updatePlan',
|
|
75
|
+
plan: planInfo.plan
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Only try to get health if user is logged in
|
|
79
|
+
if (!this.client.hasSessionToken()) {
|
|
80
|
+
// Show default health for non-logged in users
|
|
81
|
+
this._view.webview.postMessage({
|
|
82
|
+
type: 'updateHealth',
|
|
83
|
+
health: 0,
|
|
84
|
+
score: 0
|
|
85
|
+
});
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Get health status (async, might fail - that's OK)
|
|
90
|
+
try {
|
|
91
|
+
const health = await this.client.guardianStatus();
|
|
92
|
+
this._view.webview.postMessage({
|
|
93
|
+
type: 'updateHealth',
|
|
94
|
+
health: health.data?.health || 85,
|
|
95
|
+
score: health.data?.health || 85
|
|
96
|
+
});
|
|
97
|
+
} catch (healthError) {
|
|
98
|
+
// Health check failed - show default
|
|
99
|
+
console.warn('Health check failed:', healthError);
|
|
100
|
+
this._view.webview.postMessage({
|
|
101
|
+
type: 'updateHealth',
|
|
102
|
+
health: 85,
|
|
103
|
+
score: 85
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
} catch (error) {
|
|
107
|
+
console.error('Failed to initialize status:', error);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private async _executeTool(toolName: string) {
|
|
112
|
+
if (!this._view) return;
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
this._view.webview.postMessage({ type: 'typing', isTyping: true });
|
|
116
|
+
|
|
117
|
+
const result = await this.client.executeTool(toolName, {});
|
|
118
|
+
|
|
119
|
+
this._view.webview.postMessage({
|
|
120
|
+
type: 'toolResult',
|
|
121
|
+
tool: toolName,
|
|
122
|
+
result: result.data || result
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// If it's a health check, update the health bar
|
|
126
|
+
if (toolName === 'guardian_status' && result.data?.health) {
|
|
127
|
+
this._view.webview.postMessage({
|
|
128
|
+
type: 'updateHealth',
|
|
129
|
+
health: result.data.health,
|
|
130
|
+
score: result.data.health
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
} catch (error) {
|
|
134
|
+
this._view.webview.postMessage({
|
|
135
|
+
type: 'toolResult',
|
|
136
|
+
tool: toolName,
|
|
137
|
+
result: { error: error instanceof Error ? error.message : 'Tool execution failed' }
|
|
138
|
+
});
|
|
139
|
+
} finally {
|
|
140
|
+
this._view?.webview.postMessage({ type: 'typing', isTyping: false });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async sendMessage(userMessage: string) {
|
|
145
|
+
if (!this._view) return;
|
|
146
|
+
|
|
147
|
+
// Add user message to history
|
|
148
|
+
this._messages.push({
|
|
149
|
+
role: 'user',
|
|
150
|
+
content: userMessage,
|
|
151
|
+
timestamp: new Date()
|
|
152
|
+
});
|
|
153
|
+
this._updateWebview();
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
// Show typing indicator
|
|
157
|
+
this._view.webview.postMessage({ type: 'typing', isTyping: true });
|
|
158
|
+
|
|
159
|
+
// Get project context for perfect recall
|
|
160
|
+
const projectState = await this.projectContext.getProjectState();
|
|
161
|
+
|
|
162
|
+
// Build the context-aware prompt
|
|
163
|
+
const contextualizedMessages = await this._buildContextualizedMessages(userMessage, projectState);
|
|
164
|
+
|
|
165
|
+
// Call Claude via our API (with pattern enforcement)
|
|
166
|
+
const response = await this.client.chat(contextualizedMessages, projectState);
|
|
167
|
+
|
|
168
|
+
// Add assistant response
|
|
169
|
+
this._messages.push({
|
|
170
|
+
role: 'assistant',
|
|
171
|
+
content: response.content,
|
|
172
|
+
timestamp: new Date()
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// Update project context with any new information
|
|
176
|
+
if (response.projectUpdates) {
|
|
177
|
+
await this.projectContext.applyUpdates(response.projectUpdates);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Check if we need to summarize (context getting large)
|
|
181
|
+
if (this._messages.length > 20) {
|
|
182
|
+
await this._summarizeConversation();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
} catch (error) {
|
|
186
|
+
this._messages.push({
|
|
187
|
+
role: 'assistant',
|
|
188
|
+
content: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
189
|
+
timestamp: new Date()
|
|
190
|
+
});
|
|
191
|
+
} finally {
|
|
192
|
+
this._view?.webview.postMessage({ type: 'typing', isTyping: false });
|
|
193
|
+
this._updateWebview();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private async _buildContextualizedMessages(userMessage: string, projectState: any): Promise<any[]> {
|
|
198
|
+
const messages: any[] = [];
|
|
199
|
+
|
|
200
|
+
// Always include summary if we have one (perfect recall)
|
|
201
|
+
if (this._conversationSummary) {
|
|
202
|
+
messages.push({
|
|
203
|
+
role: 'system',
|
|
204
|
+
content: `Previous conversation summary:\n${this._conversationSummary}`
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Include relevant project context
|
|
209
|
+
if (projectState) {
|
|
210
|
+
messages.push({
|
|
211
|
+
role: 'system',
|
|
212
|
+
content: `Current project state:\n${JSON.stringify(projectState, null, 2)}`
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Include recent messages (last 10 for context)
|
|
217
|
+
const recentMessages = this._messages.slice(-10);
|
|
218
|
+
for (const msg of recentMessages) {
|
|
219
|
+
messages.push({
|
|
220
|
+
role: msg.role,
|
|
221
|
+
content: msg.content
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return messages;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
private async _summarizeConversation() {
|
|
229
|
+
// Summarize older messages to maintain context without using all tokens
|
|
230
|
+
const oldMessages = this._messages.slice(0, -10);
|
|
231
|
+
if (oldMessages.length === 0) return;
|
|
232
|
+
|
|
233
|
+
const summaryPrompt = `Summarize these conversation messages, keeping key decisions and context:\n${
|
|
234
|
+
oldMessages.map(m => `${m.role}: ${m.content}`).join('\n')
|
|
235
|
+
}`;
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
const summary = await this.client.summarize(summaryPrompt);
|
|
239
|
+
this._conversationSummary = summary;
|
|
240
|
+
|
|
241
|
+
// Keep only recent messages
|
|
242
|
+
this._messages = this._messages.slice(-10);
|
|
243
|
+
} catch (error) {
|
|
244
|
+
console.error('Failed to summarize conversation:', error);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
private _updateWebview() {
|
|
249
|
+
if (!this._view) return;
|
|
250
|
+
|
|
251
|
+
this._view.webview.postMessage({
|
|
252
|
+
type: 'updateMessages',
|
|
253
|
+
messages: this._messages.map(m => ({
|
|
254
|
+
role: m.role,
|
|
255
|
+
content: m.content,
|
|
256
|
+
timestamp: m.timestamp.toISOString()
|
|
257
|
+
}))
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
private _getHtmlForWebview(webview: vscode.Webview): string {
|
|
262
|
+
return `<!DOCTYPE html>
|
|
263
|
+
<html lang="en">
|
|
264
|
+
<head>
|
|
265
|
+
<meta charset="UTF-8">
|
|
266
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
267
|
+
<title>CodeBakers Chat</title>
|
|
268
|
+
<style>
|
|
269
|
+
* {
|
|
270
|
+
box-sizing: border-box;
|
|
271
|
+
margin: 0;
|
|
272
|
+
padding: 0;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
body {
|
|
276
|
+
font-family: var(--vscode-font-family);
|
|
277
|
+
font-size: var(--vscode-font-size);
|
|
278
|
+
color: var(--vscode-foreground);
|
|
279
|
+
background: var(--vscode-editor-background);
|
|
280
|
+
height: 100vh;
|
|
281
|
+
display: flex;
|
|
282
|
+
flex-direction: column;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.header {
|
|
286
|
+
padding: 12px;
|
|
287
|
+
border-bottom: 1px solid var(--vscode-panel-border);
|
|
288
|
+
display: flex;
|
|
289
|
+
align-items: center;
|
|
290
|
+
gap: 8px;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
.header-icon {
|
|
294
|
+
font-size: 20px;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.header-title {
|
|
298
|
+
font-weight: 600;
|
|
299
|
+
flex: 1;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.clear-btn {
|
|
303
|
+
background: transparent;
|
|
304
|
+
border: none;
|
|
305
|
+
color: var(--vscode-foreground);
|
|
306
|
+
cursor: pointer;
|
|
307
|
+
padding: 4px 8px;
|
|
308
|
+
border-radius: 4px;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
.clear-btn:hover {
|
|
312
|
+
background: var(--vscode-toolbar-hoverBackground);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
.messages {
|
|
316
|
+
flex: 1;
|
|
317
|
+
overflow-y: auto;
|
|
318
|
+
padding: 12px;
|
|
319
|
+
display: flex;
|
|
320
|
+
flex-direction: column;
|
|
321
|
+
gap: 12px;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.message {
|
|
325
|
+
max-width: 90%;
|
|
326
|
+
padding: 10px 14px;
|
|
327
|
+
border-radius: 12px;
|
|
328
|
+
line-height: 1.5;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
.message.user {
|
|
332
|
+
background: var(--vscode-button-background);
|
|
333
|
+
color: var(--vscode-button-foreground);
|
|
334
|
+
align-self: flex-end;
|
|
335
|
+
border-bottom-right-radius: 4px;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.message.assistant {
|
|
339
|
+
background: var(--vscode-editor-inactiveSelectionBackground);
|
|
340
|
+
align-self: flex-start;
|
|
341
|
+
border-bottom-left-radius: 4px;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.message pre {
|
|
345
|
+
background: var(--vscode-textCodeBlock-background);
|
|
346
|
+
padding: 8px;
|
|
347
|
+
border-radius: 4px;
|
|
348
|
+
overflow-x: auto;
|
|
349
|
+
margin: 8px 0;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
.message code {
|
|
353
|
+
font-family: var(--vscode-editor-font-family);
|
|
354
|
+
font-size: var(--vscode-editor-font-size);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.typing-indicator {
|
|
358
|
+
display: none;
|
|
359
|
+
align-self: flex-start;
|
|
360
|
+
padding: 10px 14px;
|
|
361
|
+
background: var(--vscode-editor-inactiveSelectionBackground);
|
|
362
|
+
border-radius: 12px;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
.typing-indicator.show {
|
|
366
|
+
display: flex;
|
|
367
|
+
gap: 4px;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.typing-dot {
|
|
371
|
+
width: 8px;
|
|
372
|
+
height: 8px;
|
|
373
|
+
background: var(--vscode-foreground);
|
|
374
|
+
border-radius: 50%;
|
|
375
|
+
animation: typing 1.4s infinite ease-in-out;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.typing-dot:nth-child(2) { animation-delay: 0.2s; }
|
|
379
|
+
.typing-dot:nth-child(3) { animation-delay: 0.4s; }
|
|
380
|
+
|
|
381
|
+
@keyframes typing {
|
|
382
|
+
0%, 60%, 100% { transform: translateY(0); opacity: 0.3; }
|
|
383
|
+
30% { transform: translateY(-4px); opacity: 1; }
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.input-area {
|
|
387
|
+
padding: 12px;
|
|
388
|
+
border-top: 1px solid var(--vscode-panel-border);
|
|
389
|
+
display: flex;
|
|
390
|
+
gap: 8px;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
.input-area textarea {
|
|
394
|
+
flex: 1;
|
|
395
|
+
background: var(--vscode-input-background);
|
|
396
|
+
color: var(--vscode-input-foreground);
|
|
397
|
+
border: 1px solid var(--vscode-input-border);
|
|
398
|
+
border-radius: 8px;
|
|
399
|
+
padding: 10px 12px;
|
|
400
|
+
font-family: inherit;
|
|
401
|
+
font-size: inherit;
|
|
402
|
+
resize: none;
|
|
403
|
+
min-height: 40px;
|
|
404
|
+
max-height: 120px;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
.input-area textarea:focus {
|
|
408
|
+
outline: none;
|
|
409
|
+
border-color: var(--vscode-focusBorder);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.send-btn {
|
|
413
|
+
background: var(--vscode-button-background);
|
|
414
|
+
color: var(--vscode-button-foreground);
|
|
415
|
+
border: none;
|
|
416
|
+
border-radius: 8px;
|
|
417
|
+
padding: 0 16px;
|
|
418
|
+
cursor: pointer;
|
|
419
|
+
font-weight: 500;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
.send-btn:hover {
|
|
423
|
+
background: var(--vscode-button-hoverBackground);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
.send-btn:disabled {
|
|
427
|
+
opacity: 0.5;
|
|
428
|
+
cursor: not-allowed;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
.footer {
|
|
432
|
+
padding: 8px 12px;
|
|
433
|
+
text-align: center;
|
|
434
|
+
font-size: 11px;
|
|
435
|
+
color: var(--vscode-descriptionForeground);
|
|
436
|
+
border-top: 1px solid var(--vscode-panel-border);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
.welcome {
|
|
440
|
+
flex: 1;
|
|
441
|
+
display: flex;
|
|
442
|
+
flex-direction: column;
|
|
443
|
+
align-items: center;
|
|
444
|
+
justify-content: center;
|
|
445
|
+
padding: 24px;
|
|
446
|
+
text-align: center;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
.welcome-icon {
|
|
450
|
+
font-size: 48px;
|
|
451
|
+
margin-bottom: 16px;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
.welcome-title {
|
|
455
|
+
font-size: 18px;
|
|
456
|
+
font-weight: 600;
|
|
457
|
+
margin-bottom: 8px;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
.welcome-text {
|
|
461
|
+
color: var(--vscode-descriptionForeground);
|
|
462
|
+
margin-bottom: 24px;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
.quick-actions {
|
|
466
|
+
display: flex;
|
|
467
|
+
flex-wrap: wrap;
|
|
468
|
+
gap: 8px;
|
|
469
|
+
justify-content: center;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
.quick-action {
|
|
473
|
+
background: var(--vscode-button-secondaryBackground);
|
|
474
|
+
color: var(--vscode-button-secondaryForeground);
|
|
475
|
+
border: none;
|
|
476
|
+
border-radius: 16px;
|
|
477
|
+
padding: 6px 14px;
|
|
478
|
+
cursor: pointer;
|
|
479
|
+
font-size: 12px;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
.quick-action:hover {
|
|
483
|
+
background: var(--vscode-button-secondaryHoverBackground);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
.health-bar {
|
|
487
|
+
padding: 6px 12px;
|
|
488
|
+
border-bottom: 1px solid var(--vscode-panel-border);
|
|
489
|
+
display: flex;
|
|
490
|
+
align-items: center;
|
|
491
|
+
gap: 8px;
|
|
492
|
+
font-size: 11px;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
.health-indicator {
|
|
496
|
+
width: 8px;
|
|
497
|
+
height: 8px;
|
|
498
|
+
border-radius: 50%;
|
|
499
|
+
background: #4caf50;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
.health-indicator.warning { background: #ff9800; }
|
|
503
|
+
.health-indicator.error { background: #f44336; }
|
|
504
|
+
|
|
505
|
+
.health-text {
|
|
506
|
+
flex: 1;
|
|
507
|
+
color: var(--vscode-descriptionForeground);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
.health-score {
|
|
511
|
+
font-weight: 600;
|
|
512
|
+
color: #4caf50;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
.tools-bar {
|
|
516
|
+
padding: 8px 12px;
|
|
517
|
+
border-top: 1px solid var(--vscode-panel-border);
|
|
518
|
+
display: flex;
|
|
519
|
+
gap: 6px;
|
|
520
|
+
flex-wrap: wrap;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
.tool-chip {
|
|
524
|
+
font-size: 10px;
|
|
525
|
+
padding: 4px 8px;
|
|
526
|
+
background: var(--vscode-button-secondaryBackground);
|
|
527
|
+
color: var(--vscode-button-secondaryForeground);
|
|
528
|
+
border: none;
|
|
529
|
+
border-radius: 12px;
|
|
530
|
+
cursor: pointer;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
.tool-chip:hover {
|
|
534
|
+
background: var(--vscode-button-secondaryHoverBackground);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
.tool-chip.active {
|
|
538
|
+
background: var(--vscode-button-background);
|
|
539
|
+
color: var(--vscode-button-foreground);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
.plan-badge {
|
|
543
|
+
font-size: 10px;
|
|
544
|
+
padding: 2px 8px;
|
|
545
|
+
background: var(--vscode-button-background);
|
|
546
|
+
color: var(--vscode-button-foreground);
|
|
547
|
+
border-radius: 10px;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
.plan-badge.trial {
|
|
551
|
+
background: #f0a030;
|
|
552
|
+
}
|
|
553
|
+
</style>
|
|
554
|
+
</head>
|
|
555
|
+
<body>
|
|
556
|
+
<div class="header">
|
|
557
|
+
<span class="header-icon">🍪</span>
|
|
558
|
+
<span class="header-title">CodeBakers</span>
|
|
559
|
+
<span class="plan-badge" id="planBadge">Pro</span>
|
|
560
|
+
<button class="clear-btn" onclick="clearChat()">Clear</button>
|
|
561
|
+
</div>
|
|
562
|
+
|
|
563
|
+
<div class="health-bar" id="healthBar">
|
|
564
|
+
<div class="health-indicator" id="healthIndicator"></div>
|
|
565
|
+
<span class="health-text">Project Health</span>
|
|
566
|
+
<span class="health-score" id="healthScore">--</span>
|
|
567
|
+
</div>
|
|
568
|
+
|
|
569
|
+
<div class="messages" id="messages">
|
|
570
|
+
<div class="welcome" id="welcome">
|
|
571
|
+
<span class="welcome-icon">🍪</span>
|
|
572
|
+
<div class="welcome-title">Welcome to CodeBakers</div>
|
|
573
|
+
<div class="welcome-text">AI-powered coding with production-ready patterns</div>
|
|
574
|
+
<div class="quick-actions">
|
|
575
|
+
<button class="quick-action" onclick="quickAction('/build')">🔨 Build Project</button>
|
|
576
|
+
<button class="quick-action" onclick="quickAction('/feature')">✨ Add Feature</button>
|
|
577
|
+
<button class="quick-action" onclick="quickAction('/audit')">🔍 Audit Code</button>
|
|
578
|
+
<button class="quick-action" onclick="runTool('guardian_status')">🛡️ Health Check</button>
|
|
579
|
+
</div>
|
|
580
|
+
</div>
|
|
581
|
+
|
|
582
|
+
<div class="typing-indicator" id="typing">
|
|
583
|
+
<div class="typing-dot"></div>
|
|
584
|
+
<div class="typing-dot"></div>
|
|
585
|
+
<div class="typing-dot"></div>
|
|
586
|
+
</div>
|
|
587
|
+
</div>
|
|
588
|
+
|
|
589
|
+
<div class="tools-bar">
|
|
590
|
+
<button class="tool-chip" onclick="runTool('guardian_status')">🛡️ Guardian</button>
|
|
591
|
+
<button class="tool-chip" onclick="runTool('list_patterns')">📋 Patterns</button>
|
|
592
|
+
<button class="tool-chip" onclick="runTool('run_tests')">🧪 Tests</button>
|
|
593
|
+
<button class="tool-chip" onclick="runTool('run_audit')">🔍 Audit</button>
|
|
594
|
+
<button class="tool-chip" onclick="runTool('ripple_check')">🌊 Ripple</button>
|
|
595
|
+
</div>
|
|
596
|
+
|
|
597
|
+
<div class="input-area">
|
|
598
|
+
<textarea
|
|
599
|
+
id="input"
|
|
600
|
+
placeholder="Ask CodeBakers anything..."
|
|
601
|
+
rows="1"
|
|
602
|
+
onkeydown="handleKeydown(event)"
|
|
603
|
+
oninput="autoResize(this)"
|
|
604
|
+
></textarea>
|
|
605
|
+
<button class="send-btn" id="sendBtn" onclick="sendMessage()">Send</button>
|
|
606
|
+
</div>
|
|
607
|
+
|
|
608
|
+
<div class="footer">
|
|
609
|
+
Powered by CodeBakers — a BotMakers Software
|
|
610
|
+
</div>
|
|
611
|
+
|
|
612
|
+
<script>
|
|
613
|
+
const vscode = acquireVsCodeApi();
|
|
614
|
+
const messagesEl = document.getElementById('messages');
|
|
615
|
+
const welcomeEl = document.getElementById('welcome');
|
|
616
|
+
const typingEl = document.getElementById('typing');
|
|
617
|
+
const inputEl = document.getElementById('input');
|
|
618
|
+
const sendBtn = document.getElementById('sendBtn');
|
|
619
|
+
|
|
620
|
+
function sendMessage() {
|
|
621
|
+
const message = inputEl.value.trim();
|
|
622
|
+
if (!message) return;
|
|
623
|
+
|
|
624
|
+
vscode.postMessage({ type: 'sendMessage', message });
|
|
625
|
+
inputEl.value = '';
|
|
626
|
+
inputEl.style.height = 'auto';
|
|
627
|
+
sendBtn.disabled = true;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
function quickAction(command) {
|
|
631
|
+
inputEl.value = command + ' ';
|
|
632
|
+
inputEl.focus();
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
function clearChat() {
|
|
636
|
+
vscode.postMessage({ type: 'clearChat' });
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
function runTool(toolName) {
|
|
640
|
+
vscode.postMessage({ type: 'runTool', tool: toolName });
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
function updateHealth(health, score) {
|
|
644
|
+
const indicator = document.getElementById('healthIndicator');
|
|
645
|
+
const scoreEl = document.getElementById('healthScore');
|
|
646
|
+
|
|
647
|
+
scoreEl.textContent = score + '%';
|
|
648
|
+
|
|
649
|
+
indicator.className = 'health-indicator';
|
|
650
|
+
if (score < 50) {
|
|
651
|
+
indicator.classList.add('error');
|
|
652
|
+
scoreEl.style.color = '#f44336';
|
|
653
|
+
} else if (score < 80) {
|
|
654
|
+
indicator.classList.add('warning');
|
|
655
|
+
scoreEl.style.color = '#ff9800';
|
|
656
|
+
} else {
|
|
657
|
+
scoreEl.style.color = '#4caf50';
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
function updatePlan(plan) {
|
|
662
|
+
const badge = document.getElementById('planBadge');
|
|
663
|
+
badge.textContent = plan.charAt(0).toUpperCase() + plan.slice(1);
|
|
664
|
+
badge.className = 'plan-badge';
|
|
665
|
+
if (plan === 'trial') {
|
|
666
|
+
badge.classList.add('trial');
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
function handleKeydown(e) {
|
|
671
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
672
|
+
e.preventDefault();
|
|
673
|
+
sendMessage();
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
function autoResize(el) {
|
|
678
|
+
el.style.height = 'auto';
|
|
679
|
+
el.style.height = Math.min(el.scrollHeight, 120) + 'px';
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
function renderMessage(msg) {
|
|
683
|
+
const div = document.createElement('div');
|
|
684
|
+
div.className = 'message ' + msg.role;
|
|
685
|
+
div.innerHTML = formatContent(msg.content);
|
|
686
|
+
return div;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
function formatContent(content) {
|
|
690
|
+
// Simple markdown-like formatting
|
|
691
|
+
return content
|
|
692
|
+
.replace(/\`\`\`(\\w*)\\n([\\s\\S]*?)\`\`\`/g, '<pre><code>$2</code></pre>')
|
|
693
|
+
.replace(/\`([^\`]+)\`/g, '<code>$1</code>')
|
|
694
|
+
.replace(/\\n/g, '<br>');
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
window.addEventListener('message', event => {
|
|
698
|
+
const data = event.data;
|
|
699
|
+
|
|
700
|
+
switch (data.type) {
|
|
701
|
+
case 'updateMessages':
|
|
702
|
+
// Hide welcome, show messages
|
|
703
|
+
if (data.messages.length > 0) {
|
|
704
|
+
welcomeEl.style.display = 'none';
|
|
705
|
+
} else {
|
|
706
|
+
welcomeEl.style.display = 'flex';
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// Clear and re-render messages
|
|
710
|
+
const existing = messagesEl.querySelectorAll('.message');
|
|
711
|
+
existing.forEach(el => el.remove());
|
|
712
|
+
|
|
713
|
+
data.messages.forEach(msg => {
|
|
714
|
+
messagesEl.insertBefore(renderMessage(msg), typingEl);
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
// Scroll to bottom
|
|
718
|
+
messagesEl.scrollTop = messagesEl.scrollHeight;
|
|
719
|
+
sendBtn.disabled = false;
|
|
720
|
+
break;
|
|
721
|
+
|
|
722
|
+
case 'typing':
|
|
723
|
+
typingEl.classList.toggle('show', data.isTyping);
|
|
724
|
+
messagesEl.scrollTop = messagesEl.scrollHeight;
|
|
725
|
+
break;
|
|
726
|
+
|
|
727
|
+
case 'updateHealth':
|
|
728
|
+
updateHealth(data.health, data.score);
|
|
729
|
+
break;
|
|
730
|
+
|
|
731
|
+
case 'updatePlan':
|
|
732
|
+
updatePlan(data.plan);
|
|
733
|
+
break;
|
|
734
|
+
|
|
735
|
+
case 'toolResult':
|
|
736
|
+
// Show tool result as a message
|
|
737
|
+
const resultDiv = document.createElement('div');
|
|
738
|
+
resultDiv.className = 'message assistant';
|
|
739
|
+
resultDiv.innerHTML = '<strong>🔧 ' + data.tool + '</strong><br>' + formatContent(JSON.stringify(data.result, null, 2));
|
|
740
|
+
messagesEl.insertBefore(resultDiv, typingEl);
|
|
741
|
+
messagesEl.scrollTop = messagesEl.scrollHeight;
|
|
742
|
+
break;
|
|
743
|
+
}
|
|
744
|
+
});
|
|
745
|
+
</script>
|
|
746
|
+
</body>
|
|
747
|
+
</html>`;
|
|
748
|
+
}
|
|
749
|
+
}
|