baseguard 1.0.3 → 1.0.5

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.
Files changed (169) hide show
  1. package/.baseguardrc.example.json +63 -63
  2. package/.eslintrc.json +24 -24
  3. package/.prettierrc +7 -7
  4. package/CHANGELOG.md +195 -195
  5. package/DEPLOYMENT.md +624 -624
  6. package/DEPLOYMENT_CHECKLIST.md +239 -239
  7. package/DEPLOYMENT_SUMMARY_v1.0.2.md +202 -202
  8. package/QUICK_START.md +134 -134
  9. package/README.md +488 -488
  10. package/RELEASE_NOTES_v1.0.2.md +434 -434
  11. package/bin/base.js +627 -627
  12. package/dist/ai/fix-manager.d.ts.map +1 -1
  13. package/dist/ai/fix-manager.js +1 -1
  14. package/dist/ai/fix-manager.js.map +1 -1
  15. package/dist/ai/gemini-analyzer.d.ts.map +1 -1
  16. package/dist/ai/gemini-analyzer.js +29 -35
  17. package/dist/ai/gemini-analyzer.js.map +1 -1
  18. package/dist/ai/gemini-code-fixer.d.ts.map +1 -1
  19. package/dist/ai/gemini-code-fixer.js +58 -58
  20. package/dist/ai/gemini-code-fixer.js.map +1 -1
  21. package/dist/ai/jules-implementer.d.ts +3 -0
  22. package/dist/ai/jules-implementer.d.ts.map +1 -1
  23. package/dist/ai/jules-implementer.js +63 -32
  24. package/dist/ai/jules-implementer.js.map +1 -1
  25. package/dist/ai/unified-code-fixer.js.map +1 -1
  26. package/dist/commands/check.d.ts.map +1 -1
  27. package/dist/commands/check.js +1 -1
  28. package/dist/commands/check.js.map +1 -1
  29. package/dist/commands/config.js +2 -1
  30. package/dist/commands/config.js.map +1 -1
  31. package/dist/commands/fix.d.ts.map +1 -1
  32. package/dist/commands/fix.js +48 -15
  33. package/dist/commands/fix.js.map +1 -1
  34. package/dist/core/api-key-manager.js +2 -2
  35. package/dist/core/api-key-manager.js.map +1 -1
  36. package/dist/core/baseguard.d.ts +1 -0
  37. package/dist/core/baseguard.d.ts.map +1 -1
  38. package/dist/core/baseguard.js +13 -10
  39. package/dist/core/baseguard.js.map +1 -1
  40. package/dist/core/baseline-checker.d.ts.map +1 -1
  41. package/dist/core/baseline-checker.js +8 -5
  42. package/dist/core/baseline-checker.js.map +1 -1
  43. package/dist/core/configuration-recovery.d.ts.map +1 -1
  44. package/dist/core/configuration-recovery.js +1 -1
  45. package/dist/core/configuration-recovery.js.map +1 -1
  46. package/dist/core/debug-logger.d.ts.map +1 -1
  47. package/dist/core/debug-logger.js +1 -1
  48. package/dist/core/debug-logger.js.map +1 -1
  49. package/dist/core/error-handler.d.ts.map +1 -1
  50. package/dist/core/error-handler.js +2 -1
  51. package/dist/core/error-handler.js.map +1 -1
  52. package/dist/core/gitignore-manager.js +5 -5
  53. package/dist/core/graceful-degradation-manager.d.ts.map +1 -1
  54. package/dist/core/graceful-degradation-manager.js +16 -16
  55. package/dist/core/graceful-degradation-manager.js.map +1 -1
  56. package/dist/core/lazy-loader.d.ts.map +1 -1
  57. package/dist/core/lazy-loader.js +9 -2
  58. package/dist/core/lazy-loader.js.map +1 -1
  59. package/dist/core/memory-manager.d.ts +0 -3
  60. package/dist/core/memory-manager.d.ts.map +1 -1
  61. package/dist/core/memory-manager.js.map +1 -1
  62. package/dist/core/parser-worker.d.ts +2 -0
  63. package/dist/core/parser-worker.d.ts.map +1 -0
  64. package/dist/core/parser-worker.js +19 -0
  65. package/dist/core/parser-worker.js.map +1 -0
  66. package/dist/core/startup-optimizer.d.ts.map +1 -1
  67. package/dist/core/startup-optimizer.js +4 -8
  68. package/dist/core/startup-optimizer.js.map +1 -1
  69. package/dist/core/system-error-handler.d.ts.map +1 -1
  70. package/dist/core/system-error-handler.js.map +1 -1
  71. package/dist/git/automation-engine.d.ts.map +1 -1
  72. package/dist/git/automation-engine.js +5 -4
  73. package/dist/git/automation-engine.js.map +1 -1
  74. package/dist/git/github-manager.d.ts.map +1 -1
  75. package/dist/git/github-manager.js.map +1 -1
  76. package/dist/git/hook-manager.js +5 -5
  77. package/dist/git/hook-manager.js.map +1 -1
  78. package/dist/parsers/parser-manager.d.ts.map +1 -1
  79. package/dist/parsers/parser-manager.js +1 -1
  80. package/dist/parsers/parser-manager.js.map +1 -1
  81. package/dist/parsers/svelte-parser.js +1 -1
  82. package/dist/parsers/svelte-parser.js.map +1 -1
  83. package/dist/parsers/vanilla-parser.d.ts.map +1 -1
  84. package/dist/parsers/vanilla-parser.js.map +1 -1
  85. package/dist/parsers/vue-parser.d.ts.map +1 -1
  86. package/dist/parsers/vue-parser.js.map +1 -1
  87. package/dist/ui/components.d.ts +1 -1
  88. package/dist/ui/components.d.ts.map +1 -1
  89. package/dist/ui/components.js +11 -11
  90. package/dist/ui/components.js.map +1 -1
  91. package/dist/ui/terminal-header.js +14 -14
  92. package/package.json +105 -105
  93. package/src/ai/__tests__/gemini-analyzer.test.ts +180 -180
  94. package/src/ai/agentkit-orchestrator.ts +533 -533
  95. package/src/ai/fix-manager.ts +362 -362
  96. package/src/ai/gemini-analyzer.ts +665 -671
  97. package/src/ai/gemini-code-fixer.ts +539 -540
  98. package/src/ai/index.ts +3 -3
  99. package/src/ai/jules-implementer.ts +504 -460
  100. package/src/ai/unified-code-fixer.ts +347 -347
  101. package/src/commands/automation.ts +343 -343
  102. package/src/commands/check.ts +298 -299
  103. package/src/commands/config.ts +584 -583
  104. package/src/commands/fix.ts +269 -238
  105. package/src/commands/index.ts +6 -6
  106. package/src/commands/init.ts +155 -155
  107. package/src/commands/status.ts +306 -306
  108. package/src/core/api-key-manager.ts +298 -298
  109. package/src/core/baseguard.ts +757 -756
  110. package/src/core/baseline-checker.ts +566 -563
  111. package/src/core/cache-manager.ts +271 -271
  112. package/src/core/configuration-recovery.ts +672 -673
  113. package/src/core/configuration.ts +595 -595
  114. package/src/core/debug-logger.ts +590 -590
  115. package/src/core/directory-filter.ts +420 -420
  116. package/src/core/error-handler.ts +518 -517
  117. package/src/core/file-processor.ts +337 -337
  118. package/src/core/gitignore-manager.ts +168 -168
  119. package/src/core/graceful-degradation-manager.ts +596 -596
  120. package/src/core/index.ts +16 -16
  121. package/src/core/lazy-loader.ts +317 -307
  122. package/src/core/memory-manager.ts +290 -295
  123. package/src/core/parser-worker.ts +33 -0
  124. package/src/core/startup-optimizer.ts +246 -255
  125. package/src/core/system-error-handler.ts +755 -756
  126. package/src/git/automation-engine.ts +361 -361
  127. package/src/git/github-manager.ts +190 -192
  128. package/src/git/hook-manager.ts +210 -210
  129. package/src/git/index.ts +3 -3
  130. package/src/index.ts +7 -7
  131. package/src/parsers/feature-validator.ts +558 -558
  132. package/src/parsers/index.ts +7 -7
  133. package/src/parsers/parser-manager.ts +418 -419
  134. package/src/parsers/parser.ts +25 -25
  135. package/src/parsers/react-parser-optimized.ts +160 -160
  136. package/src/parsers/react-parser.ts +358 -358
  137. package/src/parsers/svelte-parser.ts +510 -510
  138. package/src/parsers/vanilla-parser.ts +685 -686
  139. package/src/parsers/vue-parser.ts +476 -478
  140. package/src/types/index.ts +95 -95
  141. package/src/ui/components.ts +567 -567
  142. package/src/ui/help.ts +192 -192
  143. package/src/ui/index.ts +3 -3
  144. package/src/ui/prompts.ts +680 -680
  145. package/src/ui/terminal-header.ts +58 -58
  146. package/test-build.js +40 -40
  147. package/test-config-commands.js +55 -55
  148. package/test-header-simple.js +32 -32
  149. package/test-terminal-header.js +11 -11
  150. package/test-ui.js +28 -28
  151. package/tests/e2e/baseguard.e2e.test.ts +515 -515
  152. package/tests/e2e/cross-platform.e2e.test.ts +419 -419
  153. package/tests/e2e/git-integration.e2e.test.ts +486 -486
  154. package/tests/fixtures/react-project/package.json +13 -13
  155. package/tests/fixtures/react-project/src/App.css +75 -75
  156. package/tests/fixtures/react-project/src/App.tsx +76 -76
  157. package/tests/fixtures/svelte-project/package.json +10 -10
  158. package/tests/fixtures/svelte-project/src/App.svelte +368 -368
  159. package/tests/fixtures/vanilla-project/index.html +75 -75
  160. package/tests/fixtures/vanilla-project/script.js +330 -330
  161. package/tests/fixtures/vanilla-project/styles.css +358 -358
  162. package/tests/fixtures/vue-project/package.json +11 -11
  163. package/tests/fixtures/vue-project/src/App.vue +215 -215
  164. package/tmp-smoke/.baseguard/backups/config-2026-02-19T12-04-11-067Z-auto.json +30 -0
  165. package/tmp-smoke/src/bad.css +3 -0
  166. package/tsconfig.json +34 -34
  167. package/vitest.config.ts +11 -11
  168. package/dist/terminal-header.d.ts +0 -12
  169. package/dist/terminal-header.js +0 -45
@@ -1,347 +1,347 @@
1
- import type { Violation, Analysis, Fix, Configuration } from '../types/index.js';
2
- import { JulesImplementer } from './jules-implementer.js';
3
- import { GeminiCodeFixer } from './gemini-code-fixer.js';
4
- import { ErrorHandler, APIError, ErrorType } from '../core/error-handler.js';
5
- import { logger } from '../core/debug-logger.js';
6
- import chalk from 'chalk';
7
-
8
- /**
9
- * Unified code fixer that can use either Jules or Gemini based on configuration
10
- */
11
- export class UnifiedCodeFixer {
12
- private config: Configuration;
13
- private julesImplementer?: JulesImplementer;
14
- private geminiCodeFixer?: GeminiCodeFixer;
15
- private categoryLogger: ReturnType<typeof logger.createCategoryLogger>;
16
-
17
- constructor(config: Configuration) {
18
- this.config = config;
19
- this.categoryLogger = logger.createCategoryLogger('unified-code-fixer');
20
-
21
- // Initialize available agents based on API keys
22
- this.initializeAgents();
23
- }
24
-
25
- /**
26
- * Initialize coding agents based on available API keys
27
- */
28
- private initializeAgents(): void {
29
- if (this.config.apiKeys.jules) {
30
- this.julesImplementer = new JulesImplementer(this.config.apiKeys.jules);
31
- this.categoryLogger.debug('Jules implementer initialized');
32
- }
33
-
34
- if (this.config.apiKeys.gemini) {
35
- this.geminiCodeFixer = new GeminiCodeFixer(this.config.apiKeys.gemini);
36
- this.categoryLogger.debug('Gemini code fixer initialized');
37
- }
38
- }
39
-
40
- /**
41
- * Generate a fix using the configured primary agent with fallback
42
- */
43
- async generateFix(violation: Violation, analysis: Analysis): Promise<Fix> {
44
- const primaryAgent = this.config.codingAgent.primary;
45
- const fallbackAgent = this.config.codingAgent.fallback;
46
-
47
- this.categoryLogger.info('Generating fix', {
48
- feature: violation.feature,
49
- primaryAgent,
50
- fallbackAgent
51
- });
52
-
53
- try {
54
- // Try primary agent first
55
- const fix = await this.generateFixWithAgent(violation, analysis, primaryAgent);
56
-
57
- this.categoryLogger.info('Fix generated successfully with primary agent', {
58
- agent: primaryAgent,
59
- confidence: fix.confidence
60
- });
61
-
62
- return fix;
63
-
64
- } catch (primaryError) {
65
- this.categoryLogger.warn('Primary agent failed, trying fallback', {
66
- primaryAgent,
67
- fallbackAgent,
68
- error: primaryError instanceof Error ? primaryError.message : 'Unknown error'
69
- });
70
-
71
- // Try fallback agent if different from primary
72
- if (fallbackAgent !== primaryAgent) {
73
- try {
74
- const fix = await this.generateFixWithAgent(violation, analysis, fallbackAgent);
75
-
76
- this.categoryLogger.info('Fix generated successfully with fallback agent', {
77
- agent: fallbackAgent,
78
- confidence: fix.confidence
79
- });
80
-
81
- // Add note about fallback usage
82
- fix.explanation = `[Generated using ${fallbackAgent} as fallback]\n\n${fix.explanation}`;
83
-
84
- return fix;
85
-
86
- } catch (fallbackError) {
87
- this.categoryLogger.error('Both agents failed', {
88
- primaryError: primaryError instanceof Error ? primaryError.message : 'Unknown error',
89
- fallbackError: fallbackError instanceof Error ? fallbackError.message : 'Unknown error'
90
- });
91
-
92
- throw new Error(`Both coding agents failed. Primary (${primaryAgent}): ${primaryError instanceof Error ? primaryError.message : 'Unknown error'}. Fallback (${fallbackAgent}): ${fallbackError instanceof Error ? fallbackError.message : 'Unknown error'}`);
93
- }
94
- } else {
95
- throw primaryError;
96
- }
97
- }
98
- }
99
-
100
- /**
101
- * Generate fix with specific agent
102
- */
103
- private async generateFixWithAgent(
104
- violation: Violation,
105
- analysis: Analysis,
106
- agent: 'jules' | 'gemini'
107
- ): Promise<Fix> {
108
- switch (agent) {
109
- case 'jules':
110
- return await this.generateFixWithJules(violation, analysis);
111
-
112
- case 'gemini':
113
- return await this.generateFixWithGemini(violation, analysis);
114
-
115
- default:
116
- throw new Error(`Unknown coding agent: ${agent}`);
117
- }
118
- }
119
-
120
- /**
121
- * Generate fix using Jules
122
- */
123
- private async generateFixWithJules(violation: Violation, analysis: Analysis): Promise<Fix> {
124
- if (!this.julesImplementer) {
125
- throw new APIError(
126
- 'Jules API key not configured',
127
- ErrorType.AUTHENTICATION,
128
- {
129
- suggestions: [
130
- 'Run "base config set-keys" to configure Jules API key',
131
- 'Get Jules API key from https://jules.google.com/settings#api',
132
- 'Switch to Gemini agent with "base config coding-agent gemini"'
133
- ]
134
- }
135
- );
136
- }
137
-
138
- // Check if repository is detected for Jules
139
- const isRepoDetected = await this.julesImplementer.isRepositoryDetected();
140
- if (!isRepoDetected) {
141
- throw new APIError(
142
- 'Jules requires a GitHub repository',
143
- ErrorType.CONFIGURATION,
144
- {
145
- suggestions: [
146
- 'Ensure you are in a git repository with GitHub remote',
147
- 'Jules only works with GitHub repositories',
148
- 'Switch to Gemini agent for local files: "base config coding-agent gemini"'
149
- ]
150
- }
151
- );
152
- }
153
-
154
- return await this.julesImplementer.generateFix(violation, analysis);
155
- }
156
-
157
- /**
158
- * Generate fix using Gemini
159
- */
160
- private async generateFixWithGemini(violation: Violation, analysis: Analysis): Promise<Fix> {
161
- if (!this.geminiCodeFixer) {
162
- throw new APIError(
163
- 'Gemini API key not configured',
164
- ErrorType.AUTHENTICATION,
165
- {
166
- suggestions: [
167
- 'Run "base config set-keys" to configure Gemini API key',
168
- 'Get Gemini API key from https://aistudio.google.com',
169
- 'Switch to Jules agent with "base config coding-agent jules"'
170
- ]
171
- }
172
- );
173
- }
174
-
175
- return await this.geminiCodeFixer.generateFix(violation, analysis);
176
- }
177
-
178
- /**
179
- * Generate multiple fix options using available agents
180
- */
181
- async generateFixOptions(violation: Violation, analysis: Analysis): Promise<Fix[]> {
182
- const fixes: Fix[] = [];
183
-
184
- try {
185
- // Try primary agent
186
- const primaryFix = await this.generateFix(violation, analysis);
187
- fixes.push(primaryFix);
188
-
189
- // If primary agent has low confidence, try the other agent for alternatives
190
- if (primaryFix.confidence < 0.7) {
191
- const alternativeAgent = this.config.codingAgent.primary === 'jules' ? 'gemini' : 'jules';
192
-
193
- try {
194
- const alternativeFix = await this.generateFixWithAgent(violation, analysis, alternativeAgent);
195
- alternativeFix.explanation = `[Alternative approach using ${alternativeAgent}]\n\n${alternativeFix.explanation}`;
196
- fixes.push(alternativeFix);
197
-
198
- this.categoryLogger.info('Generated alternative fix', {
199
- primaryAgent: this.config.codingAgent.primary,
200
- alternativeAgent,
201
- primaryConfidence: primaryFix.confidence,
202
- alternativeConfidence: alternativeFix.confidence
203
- });
204
-
205
- } catch (error) {
206
- this.categoryLogger.debug('Alternative agent failed', {
207
- agent: alternativeAgent,
208
- error: error instanceof Error ? error.message : 'Unknown error'
209
- });
210
- }
211
- }
212
-
213
- } catch (error) {
214
- this.categoryLogger.error('Failed to generate fix options', { error });
215
- throw error;
216
- }
217
-
218
- return fixes;
219
- }
220
-
221
- /**
222
- * Get agent capabilities and status
223
- */
224
- async getAgentStatus(): Promise<{
225
- jules: { available: boolean; configured: boolean; repoDetected?: boolean; error?: string };
226
- gemini: { available: boolean; configured: boolean; error?: string };
227
- primary: string;
228
- fallback: string;
229
- }> {
230
- const status = {
231
- jules: { available: false, configured: !!this.config.apiKeys.jules, error: undefined as string | undefined, repoDetected: undefined as boolean | undefined },
232
- gemini: { available: false, configured: !!this.config.apiKeys.gemini, error: undefined as string | undefined },
233
- primary: this.config.codingAgent.primary,
234
- fallback: this.config.codingAgent.fallback
235
- };
236
-
237
- // Test Jules availability
238
- if (this.julesImplementer) {
239
- try {
240
- const connectionTest = await this.julesImplementer.testConnection();
241
- status.jules.available = connectionTest.success;
242
- if (!connectionTest.success) {
243
- status.jules.error = connectionTest.error;
244
- }
245
-
246
- // Check repository detection
247
- status.jules.repoDetected = await this.julesImplementer.isRepositoryDetected();
248
-
249
- } catch (error) {
250
- status.jules.error = error instanceof Error ? error.message : 'Unknown error';
251
- }
252
- }
253
-
254
- // Test Gemini availability
255
- if (this.geminiCodeFixer) {
256
- try {
257
- const connectionTest = await this.geminiCodeFixer.testConnection();
258
- status.gemini.available = connectionTest.success;
259
- if (!connectionTest.success) {
260
- status.gemini.error = connectionTest.error;
261
- }
262
- } catch (error) {
263
- status.gemini.error = error instanceof Error ? error.message : 'Unknown error';
264
- }
265
- }
266
-
267
- return status;
268
- }
269
-
270
- /**
271
- * Show agent comparison and recommendations
272
- */
273
- showAgentComparison(): void {
274
- console.log(chalk.cyan('\nšŸ¤– Coding Agent Comparison\n'));
275
-
276
- console.log(chalk.white('Jules (Google\'s Autonomous Coding Agent):'));
277
- console.log(chalk.green(' āœ… Autonomous operation in cloud VMs'));
278
- console.log(chalk.green(' āœ… Full repository context understanding'));
279
- console.log(chalk.green(' āœ… Asynchronous processing'));
280
- console.log(chalk.green(' āœ… Integrated with GitHub workflows'));
281
- console.log(chalk.red(' āŒ Requires GitHub repository'));
282
- console.log(chalk.red(' āŒ Cannot work with local/uncommitted files'));
283
- console.log(chalk.red(' āŒ Slower (asynchronous processing)'));
284
-
285
- console.log(chalk.white('\nGemini 2.5 Pro (Direct API Integration):'));
286
- console.log(chalk.green(' āœ… Works with any files (GitHub or not)'));
287
- console.log(chalk.green(' āœ… Immediate processing'));
288
- console.log(chalk.green(' āœ… Works with uncommitted/local files'));
289
- console.log(chalk.green(' āœ… Grounded with real-time web search'));
290
- console.log(chalk.yellow(' āš ļø Requires manual code application'));
291
- console.log(chalk.yellow(' āš ļø Limited to single-file context'));
292
-
293
- console.log(chalk.cyan('\nšŸ’” Recommendations:'));
294
- console.log(chalk.cyan(' • Use Jules for: GitHub repositories, complex multi-file changes'));
295
- console.log(chalk.cyan(' • Use Gemini for: Local development, quick fixes, non-GitHub projects'));
296
- console.log(chalk.cyan(' • Configure both for maximum flexibility'));
297
- }
298
-
299
- /**
300
- * Switch primary coding agent
301
- */
302
- async switchPrimaryAgent(agent: 'jules' | 'gemini'): Promise<void> {
303
- this.config.codingAgent.primary = agent;
304
- this.categoryLogger.info('Switched primary coding agent', { agent });
305
-
306
- console.log(chalk.green(`āœ… Primary coding agent switched to ${agent}`));
307
-
308
- // Show agent-specific setup instructions
309
- if (agent === 'jules' && !this.config.apiKeys.jules) {
310
- console.log(chalk.yellow('\nāš ļø Jules API key not configured'));
311
- console.log(chalk.cyan('Run "base config set-keys" to configure Jules API key'));
312
- } else if (agent === 'gemini' && !this.config.apiKeys.gemini) {
313
- console.log(chalk.yellow('\nāš ļø Gemini API key not configured'));
314
- console.log(chalk.cyan('Run "base config set-keys" to configure Gemini API key'));
315
- }
316
- }
317
-
318
- /**
319
- * Get recommended agent based on current context
320
- */
321
- async getRecommendedAgent(): Promise<{ agent: 'jules' | 'gemini'; reason: string }> {
322
- // Check if we're in a GitHub repository
323
- const isGitHubRepo = this.julesImplementer ? await this.julesImplementer.isRepositoryDetected() : false;
324
-
325
- if (isGitHubRepo && this.config.apiKeys.jules) {
326
- return {
327
- agent: 'jules',
328
- reason: 'GitHub repository detected and Jules is configured'
329
- };
330
- } else if (this.config.apiKeys.gemini) {
331
- return {
332
- agent: 'gemini',
333
- reason: isGitHubRepo ? 'Gemini works with any files including local changes' : 'Not in a GitHub repository, Gemini works with local files'
334
- };
335
- } else if (this.config.apiKeys.jules) {
336
- return {
337
- agent: 'jules',
338
- reason: 'Only Jules is configured'
339
- };
340
- } else {
341
- return {
342
- agent: 'gemini',
343
- reason: 'Default recommendation for local development'
344
- };
345
- }
346
- }
347
- }
1
+ import type { Violation, Analysis, Fix, Configuration } from '../types/index.js';
2
+ import { JulesImplementer } from './jules-implementer.js';
3
+ import { GeminiCodeFixer } from './gemini-code-fixer.js';
4
+ import { APIError, ErrorType } from '../core/error-handler.js';
5
+ import { logger } from '../core/debug-logger.js';
6
+ import chalk from 'chalk';
7
+
8
+ /**
9
+ * Unified code fixer that can use either Jules or Gemini based on configuration
10
+ */
11
+ export class UnifiedCodeFixer {
12
+ private config: Configuration;
13
+ private julesImplementer?: JulesImplementer;
14
+ private geminiCodeFixer?: GeminiCodeFixer;
15
+ private categoryLogger: ReturnType<typeof logger.createCategoryLogger>;
16
+
17
+ constructor(config: Configuration) {
18
+ this.config = config;
19
+ this.categoryLogger = logger.createCategoryLogger('unified-code-fixer');
20
+
21
+ // Initialize available agents based on API keys
22
+ this.initializeAgents();
23
+ }
24
+
25
+ /**
26
+ * Initialize coding agents based on available API keys
27
+ */
28
+ private initializeAgents(): void {
29
+ if (this.config.apiKeys.jules) {
30
+ this.julesImplementer = new JulesImplementer(this.config.apiKeys.jules);
31
+ this.categoryLogger.debug('Jules implementer initialized');
32
+ }
33
+
34
+ if (this.config.apiKeys.gemini) {
35
+ this.geminiCodeFixer = new GeminiCodeFixer(this.config.apiKeys.gemini);
36
+ this.categoryLogger.debug('Gemini code fixer initialized');
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Generate a fix using the configured primary agent with fallback
42
+ */
43
+ async generateFix(violation: Violation, analysis: Analysis): Promise<Fix> {
44
+ const primaryAgent = this.config.codingAgent.primary;
45
+ const fallbackAgent = this.config.codingAgent.fallback;
46
+
47
+ this.categoryLogger.info('Generating fix', {
48
+ feature: violation.feature,
49
+ primaryAgent,
50
+ fallbackAgent
51
+ });
52
+
53
+ try {
54
+ // Try primary agent first
55
+ const fix = await this.generateFixWithAgent(violation, analysis, primaryAgent);
56
+
57
+ this.categoryLogger.info('Fix generated successfully with primary agent', {
58
+ agent: primaryAgent,
59
+ confidence: fix.confidence
60
+ });
61
+
62
+ return fix;
63
+
64
+ } catch (primaryError) {
65
+ this.categoryLogger.warn('Primary agent failed, trying fallback', {
66
+ primaryAgent,
67
+ fallbackAgent,
68
+ error: primaryError instanceof Error ? primaryError.message : 'Unknown error'
69
+ });
70
+
71
+ // Try fallback agent if different from primary
72
+ if (fallbackAgent !== primaryAgent) {
73
+ try {
74
+ const fix = await this.generateFixWithAgent(violation, analysis, fallbackAgent);
75
+
76
+ this.categoryLogger.info('Fix generated successfully with fallback agent', {
77
+ agent: fallbackAgent,
78
+ confidence: fix.confidence
79
+ });
80
+
81
+ // Add note about fallback usage
82
+ fix.explanation = `[Generated using ${fallbackAgent} as fallback]\n\n${fix.explanation}`;
83
+
84
+ return fix;
85
+
86
+ } catch (fallbackError) {
87
+ this.categoryLogger.error('Both agents failed', {
88
+ primaryError: primaryError instanceof Error ? primaryError.message : 'Unknown error',
89
+ fallbackError: fallbackError instanceof Error ? fallbackError.message : 'Unknown error'
90
+ });
91
+
92
+ throw new Error(`Both coding agents failed. Primary (${primaryAgent}): ${primaryError instanceof Error ? primaryError.message : 'Unknown error'}. Fallback (${fallbackAgent}): ${fallbackError instanceof Error ? fallbackError.message : 'Unknown error'}`);
93
+ }
94
+ } else {
95
+ throw primaryError;
96
+ }
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Generate fix with specific agent
102
+ */
103
+ private async generateFixWithAgent(
104
+ violation: Violation,
105
+ analysis: Analysis,
106
+ agent: 'jules' | 'gemini'
107
+ ): Promise<Fix> {
108
+ switch (agent) {
109
+ case 'jules':
110
+ return await this.generateFixWithJules(violation, analysis);
111
+
112
+ case 'gemini':
113
+ return await this.generateFixWithGemini(violation, analysis);
114
+
115
+ default:
116
+ throw new Error(`Unknown coding agent: ${agent}`);
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Generate fix using Jules
122
+ */
123
+ private async generateFixWithJules(violation: Violation, analysis: Analysis): Promise<Fix> {
124
+ if (!this.julesImplementer) {
125
+ throw new APIError(
126
+ 'Jules API key not configured',
127
+ ErrorType.AUTHENTICATION,
128
+ {
129
+ suggestions: [
130
+ 'Run "base config set-keys" to configure Jules API key',
131
+ 'Get Jules API key from https://jules.google.com/settings#api',
132
+ 'Switch to Gemini agent with "base config coding-agent gemini"'
133
+ ]
134
+ }
135
+ );
136
+ }
137
+
138
+ // Check if repository is detected for Jules
139
+ const isRepoDetected = await this.julesImplementer.isRepositoryDetected();
140
+ if (!isRepoDetected) {
141
+ throw new APIError(
142
+ 'Jules requires a GitHub repository',
143
+ ErrorType.CONFIGURATION,
144
+ {
145
+ suggestions: [
146
+ 'Ensure you are in a git repository with GitHub remote',
147
+ 'Jules only works with GitHub repositories',
148
+ 'Switch to Gemini agent for local files: "base config coding-agent gemini"'
149
+ ]
150
+ }
151
+ );
152
+ }
153
+
154
+ return await this.julesImplementer.generateFix(violation, analysis);
155
+ }
156
+
157
+ /**
158
+ * Generate fix using Gemini
159
+ */
160
+ private async generateFixWithGemini(violation: Violation, analysis: Analysis): Promise<Fix> {
161
+ if (!this.geminiCodeFixer) {
162
+ throw new APIError(
163
+ 'Gemini API key not configured',
164
+ ErrorType.AUTHENTICATION,
165
+ {
166
+ suggestions: [
167
+ 'Run "base config set-keys" to configure Gemini API key',
168
+ 'Get Gemini API key from https://aistudio.google.com',
169
+ 'Switch to Jules agent with "base config coding-agent jules"'
170
+ ]
171
+ }
172
+ );
173
+ }
174
+
175
+ return await this.geminiCodeFixer.generateFix(violation, analysis);
176
+ }
177
+
178
+ /**
179
+ * Generate multiple fix options using available agents
180
+ */
181
+ async generateFixOptions(violation: Violation, analysis: Analysis): Promise<Fix[]> {
182
+ const fixes: Fix[] = [];
183
+
184
+ try {
185
+ // Try primary agent
186
+ const primaryFix = await this.generateFix(violation, analysis);
187
+ fixes.push(primaryFix);
188
+
189
+ // If primary agent has low confidence, try the other agent for alternatives
190
+ if (primaryFix.confidence < 0.7) {
191
+ const alternativeAgent = this.config.codingAgent.primary === 'jules' ? 'gemini' : 'jules';
192
+
193
+ try {
194
+ const alternativeFix = await this.generateFixWithAgent(violation, analysis, alternativeAgent);
195
+ alternativeFix.explanation = `[Alternative approach using ${alternativeAgent}]\n\n${alternativeFix.explanation}`;
196
+ fixes.push(alternativeFix);
197
+
198
+ this.categoryLogger.info('Generated alternative fix', {
199
+ primaryAgent: this.config.codingAgent.primary,
200
+ alternativeAgent,
201
+ primaryConfidence: primaryFix.confidence,
202
+ alternativeConfidence: alternativeFix.confidence
203
+ });
204
+
205
+ } catch (error) {
206
+ this.categoryLogger.debug('Alternative agent failed', {
207
+ agent: alternativeAgent,
208
+ error: error instanceof Error ? error.message : 'Unknown error'
209
+ });
210
+ }
211
+ }
212
+
213
+ } catch (error) {
214
+ this.categoryLogger.error('Failed to generate fix options', { error });
215
+ throw error;
216
+ }
217
+
218
+ return fixes;
219
+ }
220
+
221
+ /**
222
+ * Get agent capabilities and status
223
+ */
224
+ async getAgentStatus(): Promise<{
225
+ jules: { available: boolean; configured: boolean; repoDetected?: boolean; error?: string };
226
+ gemini: { available: boolean; configured: boolean; error?: string };
227
+ primary: string;
228
+ fallback: string;
229
+ }> {
230
+ const status = {
231
+ jules: { available: false, configured: !!this.config.apiKeys.jules, error: undefined as string | undefined, repoDetected: undefined as boolean | undefined },
232
+ gemini: { available: false, configured: !!this.config.apiKeys.gemini, error: undefined as string | undefined },
233
+ primary: this.config.codingAgent.primary,
234
+ fallback: this.config.codingAgent.fallback
235
+ };
236
+
237
+ // Test Jules availability
238
+ if (this.julesImplementer) {
239
+ try {
240
+ const connectionTest = await this.julesImplementer.testConnection();
241
+ status.jules.available = connectionTest.success;
242
+ if (!connectionTest.success) {
243
+ status.jules.error = connectionTest.error;
244
+ }
245
+
246
+ // Check repository detection
247
+ status.jules.repoDetected = await this.julesImplementer.isRepositoryDetected();
248
+
249
+ } catch (error) {
250
+ status.jules.error = error instanceof Error ? error.message : 'Unknown error';
251
+ }
252
+ }
253
+
254
+ // Test Gemini availability
255
+ if (this.geminiCodeFixer) {
256
+ try {
257
+ const connectionTest = await this.geminiCodeFixer.testConnection();
258
+ status.gemini.available = connectionTest.success;
259
+ if (!connectionTest.success) {
260
+ status.gemini.error = connectionTest.error;
261
+ }
262
+ } catch (error) {
263
+ status.gemini.error = error instanceof Error ? error.message : 'Unknown error';
264
+ }
265
+ }
266
+
267
+ return status;
268
+ }
269
+
270
+ /**
271
+ * Show agent comparison and recommendations
272
+ */
273
+ showAgentComparison(): void {
274
+ console.log(chalk.cyan('\nšŸ¤– Coding Agent Comparison\n'));
275
+
276
+ console.log(chalk.white('Jules (Google\'s Autonomous Coding Agent):'));
277
+ console.log(chalk.green(' āœ… Autonomous operation in cloud VMs'));
278
+ console.log(chalk.green(' āœ… Full repository context understanding'));
279
+ console.log(chalk.green(' āœ… Asynchronous processing'));
280
+ console.log(chalk.green(' āœ… Integrated with GitHub workflows'));
281
+ console.log(chalk.red(' āŒ Requires GitHub repository'));
282
+ console.log(chalk.red(' āŒ Cannot work with local/uncommitted files'));
283
+ console.log(chalk.red(' āŒ Slower (asynchronous processing)'));
284
+
285
+ console.log(chalk.white('\nGemini 2.5 Pro (Direct API Integration):'));
286
+ console.log(chalk.green(' āœ… Works with any files (GitHub or not)'));
287
+ console.log(chalk.green(' āœ… Immediate processing'));
288
+ console.log(chalk.green(' āœ… Works with uncommitted/local files'));
289
+ console.log(chalk.green(' āœ… Grounded with real-time web search'));
290
+ console.log(chalk.yellow(' āš ļø Requires manual code application'));
291
+ console.log(chalk.yellow(' āš ļø Limited to single-file context'));
292
+
293
+ console.log(chalk.cyan('\nšŸ’” Recommendations:'));
294
+ console.log(chalk.cyan(' • Use Jules for: GitHub repositories, complex multi-file changes'));
295
+ console.log(chalk.cyan(' • Use Gemini for: Local development, quick fixes, non-GitHub projects'));
296
+ console.log(chalk.cyan(' • Configure both for maximum flexibility'));
297
+ }
298
+
299
+ /**
300
+ * Switch primary coding agent
301
+ */
302
+ async switchPrimaryAgent(agent: 'jules' | 'gemini'): Promise<void> {
303
+ this.config.codingAgent.primary = agent;
304
+ this.categoryLogger.info('Switched primary coding agent', { agent });
305
+
306
+ console.log(chalk.green(`āœ… Primary coding agent switched to ${agent}`));
307
+
308
+ // Show agent-specific setup instructions
309
+ if (agent === 'jules' && !this.config.apiKeys.jules) {
310
+ console.log(chalk.yellow('\nāš ļø Jules API key not configured'));
311
+ console.log(chalk.cyan('Run "base config set-keys" to configure Jules API key'));
312
+ } else if (agent === 'gemini' && !this.config.apiKeys.gemini) {
313
+ console.log(chalk.yellow('\nāš ļø Gemini API key not configured'));
314
+ console.log(chalk.cyan('Run "base config set-keys" to configure Gemini API key'));
315
+ }
316
+ }
317
+
318
+ /**
319
+ * Get recommended agent based on current context
320
+ */
321
+ async getRecommendedAgent(): Promise<{ agent: 'jules' | 'gemini'; reason: string }> {
322
+ // Check if we're in a GitHub repository
323
+ const isGitHubRepo = this.julesImplementer ? await this.julesImplementer.isRepositoryDetected() : false;
324
+
325
+ if (isGitHubRepo && this.config.apiKeys.jules) {
326
+ return {
327
+ agent: 'jules',
328
+ reason: 'GitHub repository detected and Jules is configured'
329
+ };
330
+ } else if (this.config.apiKeys.gemini) {
331
+ return {
332
+ agent: 'gemini',
333
+ reason: isGitHubRepo ? 'Gemini works with any files including local changes' : 'Not in a GitHub repository, Gemini works with local files'
334
+ };
335
+ } else if (this.config.apiKeys.jules) {
336
+ return {
337
+ agent: 'jules',
338
+ reason: 'Only Jules is configured'
339
+ };
340
+ } else {
341
+ return {
342
+ agent: 'gemini',
343
+ reason: 'Default recommendation for local development'
344
+ };
345
+ }
346
+ }
347
+ }