baseguard 1.0.1 → 1.0.3

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 (152) hide show
  1. package/.baseguardrc.example.json +64 -0
  2. package/.eslintrc.json +1 -1
  3. package/CHANGELOG.md +196 -0
  4. package/DEPLOYMENT.md +625 -0
  5. package/DEPLOYMENT_CHECKLIST.md +239 -0
  6. package/DEPLOYMENT_SUMMARY_v1.0.2.md +202 -0
  7. package/QUICK_START.md +134 -0
  8. package/README.md +447 -52
  9. package/RELEASE_NOTES_v1.0.2.md +434 -0
  10. package/bin/base.js +81 -61
  11. package/dist/ai/agentkit-orchestrator.d.ts +116 -0
  12. package/dist/ai/agentkit-orchestrator.d.ts.map +1 -0
  13. package/dist/ai/agentkit-orchestrator.js +417 -0
  14. package/dist/ai/agentkit-orchestrator.js.map +1 -0
  15. package/dist/ai/gemini-code-fixer.d.ts +85 -0
  16. package/dist/ai/gemini-code-fixer.d.ts.map +1 -0
  17. package/dist/ai/gemini-code-fixer.js +452 -0
  18. package/dist/ai/gemini-code-fixer.js.map +1 -0
  19. package/dist/ai/jules-implementer.d.ts +5 -4
  20. package/dist/ai/jules-implementer.d.ts.map +1 -1
  21. package/dist/ai/jules-implementer.js +6 -5
  22. package/dist/ai/jules-implementer.js.map +1 -1
  23. package/dist/ai/unified-code-fixer.d.ts +69 -0
  24. package/dist/ai/unified-code-fixer.d.ts.map +1 -0
  25. package/dist/ai/unified-code-fixer.js +289 -0
  26. package/dist/ai/unified-code-fixer.js.map +1 -0
  27. package/dist/commands/check.d.ts +3 -1
  28. package/dist/commands/check.d.ts.map +1 -1
  29. package/dist/commands/check.js +166 -34
  30. package/dist/commands/check.js.map +1 -1
  31. package/dist/commands/config.d.ts +4 -0
  32. package/dist/commands/config.d.ts.map +1 -1
  33. package/dist/commands/config.js +183 -0
  34. package/dist/commands/config.js.map +1 -1
  35. package/dist/commands/fix.d.ts.map +1 -1
  36. package/dist/commands/fix.js +89 -91
  37. package/dist/commands/fix.js.map +1 -1
  38. package/dist/commands/index.d.ts +1 -0
  39. package/dist/commands/index.d.ts.map +1 -1
  40. package/dist/commands/index.js +1 -0
  41. package/dist/commands/index.js.map +1 -1
  42. package/dist/commands/init.d.ts.map +1 -1
  43. package/dist/commands/init.js +16 -2
  44. package/dist/commands/init.js.map +1 -1
  45. package/dist/commands/status.d.ts +14 -0
  46. package/dist/commands/status.d.ts.map +1 -0
  47. package/dist/commands/status.js +254 -0
  48. package/dist/commands/status.js.map +1 -0
  49. package/dist/core/baseguard.d.ts +47 -5
  50. package/dist/core/baseguard.d.ts.map +1 -1
  51. package/dist/core/baseguard.js +506 -52
  52. package/dist/core/baseguard.js.map +1 -1
  53. package/dist/core/cache-manager.d.ts.map +1 -1
  54. package/dist/core/cache-manager.js +3 -1
  55. package/dist/core/cache-manager.js.map +1 -1
  56. package/dist/core/configuration-recovery.d.ts +116 -0
  57. package/dist/core/configuration-recovery.d.ts.map +1 -0
  58. package/dist/core/configuration-recovery.js +552 -0
  59. package/dist/core/configuration-recovery.js.map +1 -0
  60. package/dist/core/configuration.d.ts +4 -0
  61. package/dist/core/configuration.d.ts.map +1 -1
  62. package/dist/core/configuration.js +35 -0
  63. package/dist/core/configuration.js.map +1 -1
  64. package/dist/core/debug-logger.d.ts +181 -0
  65. package/dist/core/debug-logger.d.ts.map +1 -0
  66. package/dist/core/debug-logger.js +479 -0
  67. package/dist/core/debug-logger.js.map +1 -0
  68. package/dist/core/file-processor.d.ts.map +1 -1
  69. package/dist/core/file-processor.js +8 -2
  70. package/dist/core/file-processor.js.map +1 -1
  71. package/dist/core/graceful-degradation-manager.d.ts +123 -0
  72. package/dist/core/graceful-degradation-manager.d.ts.map +1 -0
  73. package/dist/core/graceful-degradation-manager.js +512 -0
  74. package/dist/core/graceful-degradation-manager.js.map +1 -0
  75. package/dist/core/index.d.ts +4 -0
  76. package/dist/core/index.d.ts.map +1 -1
  77. package/dist/core/index.js +4 -0
  78. package/dist/core/index.js.map +1 -1
  79. package/dist/core/logger.d.ts +1 -0
  80. package/dist/core/logger.d.ts.map +1 -0
  81. package/dist/core/logger.js +2 -0
  82. package/dist/core/logger.js.map +1 -0
  83. package/dist/core/memory-manager.d.ts +84 -0
  84. package/dist/core/memory-manager.d.ts.map +1 -1
  85. package/dist/core/memory-manager.js +236 -1
  86. package/dist/core/memory-manager.js.map +1 -1
  87. package/dist/core/startup-optimizer.d.ts +14 -0
  88. package/dist/core/startup-optimizer.d.ts.map +1 -1
  89. package/dist/core/startup-optimizer.js +74 -3
  90. package/dist/core/startup-optimizer.js.map +1 -1
  91. package/dist/core/system-error-handler.d.ts +65 -0
  92. package/dist/core/system-error-handler.d.ts.map +1 -0
  93. package/dist/core/system-error-handler.js +653 -0
  94. package/dist/core/system-error-handler.js.map +1 -0
  95. package/dist/git/github-manager.d.ts +5 -16
  96. package/dist/git/github-manager.d.ts.map +1 -1
  97. package/dist/git/github-manager.js +6 -61
  98. package/dist/git/github-manager.js.map +1 -1
  99. package/dist/parsers/react-parser-optimized.d.ts +16 -0
  100. package/dist/parsers/react-parser-optimized.d.ts.map +1 -0
  101. package/dist/parsers/react-parser-optimized.js +147 -0
  102. package/dist/parsers/react-parser-optimized.js.map +1 -0
  103. package/dist/parsers/react-parser.d.ts.map +1 -1
  104. package/dist/parsers/react-parser.js +17 -15
  105. package/dist/parsers/react-parser.js.map +1 -1
  106. package/dist/parsers/svelte-parser.d.ts.map +1 -1
  107. package/dist/parsers/svelte-parser.js +7 -3
  108. package/dist/parsers/svelte-parser.js.map +1 -1
  109. package/dist/parsers/vanilla-parser.d.ts.map +1 -1
  110. package/dist/parsers/vanilla-parser.js +7 -3
  111. package/dist/parsers/vanilla-parser.js.map +1 -1
  112. package/dist/parsers/vue-parser.d.ts +18 -0
  113. package/dist/parsers/vue-parser.d.ts.map +1 -1
  114. package/dist/parsers/vue-parser.js +387 -1
  115. package/dist/parsers/vue-parser.js.map +1 -1
  116. package/dist/types/index.d.ts +4 -0
  117. package/dist/types/index.d.ts.map +1 -1
  118. package/dist/ui/help.js +1 -1
  119. package/dist/ui/help.js.map +1 -1
  120. package/dist/ui/prompts.d.ts +7 -4
  121. package/dist/ui/prompts.d.ts.map +1 -1
  122. package/dist/ui/prompts.js +48 -55
  123. package/dist/ui/prompts.js.map +1 -1
  124. package/package.json +31 -6
  125. package/src/ai/__tests__/gemini-analyzer.test.ts +2 -2
  126. package/src/ai/agentkit-orchestrator.ts +534 -0
  127. package/src/ai/gemini-code-fixer.ts +540 -0
  128. package/src/ai/jules-implementer.ts +6 -5
  129. package/src/ai/unified-code-fixer.ts +347 -0
  130. package/src/commands/config.ts +126 -0
  131. package/src/commands/fix.ts +98 -94
  132. package/src/commands/init.ts +16 -2
  133. package/src/core/cache-manager.ts +4 -2
  134. package/src/core/configuration.ts +37 -0
  135. package/src/core/debug-logger.ts +2 -2
  136. package/src/core/file-processor.ts +10 -3
  137. package/src/core/index.ts +5 -1
  138. package/src/core/memory-manager.ts +4 -3
  139. package/src/core/startup-optimizer.ts +85 -3
  140. package/src/core/system-error-handler.ts +17 -11
  141. package/src/git/github-manager.ts +11 -79
  142. package/src/parsers/react-parser.ts +2 -2
  143. package/src/parsers/svelte-parser.ts +13 -9
  144. package/src/parsers/vanilla-parser.ts +18 -14
  145. package/src/parsers/vue-parser.ts +20 -14
  146. package/src/types/index.ts +4 -0
  147. package/src/ui/help.ts +1 -1
  148. package/src/ui/prompts.ts +54 -61
  149. package/test-build.js +41 -0
  150. package/tests/e2e/git-integration.e2e.test.ts +1 -1
  151. package/tsconfig.json +0 -1
  152. package/vitest.config.ts +4 -2
@@ -3,7 +3,7 @@ import { UIComponents } from '../ui/index.js';
3
3
  import { BaseGuard } from '../core/index.js';
4
4
  import { ConfigurationManager } from '../core/configuration.js';
5
5
  import { ErrorHandler } from '../core/error-handler.js';
6
- import { JulesImplementer } from '../ai/jules-implementer.js';
6
+ import { UnifiedCodeFixer } from '../ai/unified-code-fixer.js';
7
7
  import { GeminiAnalyzer } from '../ai/gemini-analyzer.js';
8
8
  import { glob } from 'glob';
9
9
 
@@ -34,30 +34,34 @@ export async function fix(options: {
34
34
 
35
35
  // Initialize services
36
36
  const baseGuard = new BaseGuard(config);
37
- const julesImplementer = new JulesImplementer(config.apiKeys.jules);
37
+ const unifiedCodeFixer = new UnifiedCodeFixer(config);
38
38
  const geminiAnalyzer = new GeminiAnalyzer(config.apiKeys.gemini);
39
39
 
40
- // Check GitHub integration
41
- const isGitHubSetup = await julesImplementer.isGitHubIntegrationSetup();
42
- if (!isGitHubSetup) {
43
- console.log(chalk.yellow('⚠️ Jules GitHub integration not set up'));
44
-
45
- const { default: inquirer } = await import('inquirer');
46
- const { setupNow } = await inquirer.prompt([
47
- {
48
- type: 'confirm',
49
- name: 'setupNow',
50
- message: 'Would you like to set up Jules GitHub integration now?',
51
- default: true
52
- }
53
- ]);
54
-
55
- if (setupNow) {
56
- await julesImplementer.setupGitHubIntegration();
57
- } else {
58
- console.log(chalk.yellow('GitHub integration required for Jules fixing. Exiting.'));
59
- process.exit(0);
60
- }
40
+ // Show agent status and recommendations
41
+ console.log(chalk.cyan('🤖 Coding Agent Status:'));
42
+ const agentStatus = await unifiedCodeFixer.getAgentStatus();
43
+
44
+ const primaryAvailable = agentStatus.primary === 'jules' ? agentStatus.jules.available : agentStatus.gemini.available;
45
+ const fallbackAvailable = agentStatus.fallback === 'jules' ? agentStatus.jules.available : agentStatus.gemini.available;
46
+
47
+ console.log(` Primary: ${agentStatus.primary} ${primaryAvailable ? '✅' : '❌'}`);
48
+ console.log(` Fallback: ${agentStatus.fallback} ${fallbackAvailable ? '' : '❌'}`);
49
+
50
+ if (!primaryAvailable && !fallbackAvailable) {
51
+ console.log(chalk.red('\n❌ No coding agents are available'));
52
+ console.log(chalk.cyan('💡 Configure API keys to enable code fixing:'));
53
+ console.log(chalk.cyan(' • Run "base config set-keys" to configure API keys'));
54
+ console.log(chalk.cyan(' • Get Gemini API key: https://aistudio.google.com'));
55
+ console.log(chalk.cyan(' • Get Jules API key: https://jules.google.com/settings#api'));
56
+ process.exit(1);
57
+ }
58
+
59
+ // Show agent-specific information
60
+ if (agentStatus.primary === 'jules' && agentStatus.jules.repoDetected) {
61
+ console.log(chalk.cyan('🔗 GitHub repository detected - Jules available for autonomous fixing'));
62
+ } else if (agentStatus.primary === 'gemini' || !agentStatus.jules.repoDetected) {
63
+ console.log(chalk.cyan('💎 Using Gemini 2.5 Pro for local file fixing'));
64
+ console.log(chalk.dim(' This works with any local files, no GitHub required'));
61
65
  }
62
66
 
63
67
  // Step 1: Check for violations
@@ -102,88 +106,88 @@ export async function fix(options: {
102
106
  return;
103
107
  }
104
108
 
105
- // Step 3: Generate and apply fixes with Jules
106
- console.log(chalk.cyan('\n🤖 Generating fixes with Jules AI...'));
107
- const results = await julesImplementer.generateAndApplyFixes(violations, analyses);
109
+ // Step 3: Generate fixes with unified code fixer
110
+ console.log(chalk.cyan(`\n🤖 Generating fixes with ${agentStatus.primary}...`));
108
111
 
109
- // Show results
110
- console.log(chalk.cyan('\n📊 Fix Results:\n'));
112
+ const fixes = [];
113
+ let successCount = 0;
114
+ let failedCount = 0;
111
115
 
112
- if (results.applied.length > 0) {
113
- console.log(chalk.green(`✅ Applied ${results.applied.length} fixes:`));
114
- results.applied.forEach(fix => {
115
- console.log(chalk.green(` • ${fix.filePath} - ${fix.violation.feature}`));
116
- });
117
- console.log();
116
+ for (let i = 0; i < violations.length; i++) {
117
+ const violation = violations[i];
118
+ const analysis = analyses[i];
119
+
120
+ if (!violation || !analysis) continue;
121
+
122
+ try {
123
+ console.log(chalk.cyan(`\n🔧 Fixing ${violation.feature} in ${violation.file}...`));
124
+
125
+ const fix = await unifiedCodeFixer.generateFix(violation, analysis);
126
+ fixes.push(fix);
127
+ successCount++;
128
+
129
+ console.log(chalk.green(`✅ Fix generated (confidence: ${Math.round(fix.confidence * 100)}%)`));
130
+
131
+ // Show preview if not in auto mode
132
+ if (!options.auto) {
133
+ console.log(chalk.dim('\nPreview:'));
134
+ console.log(chalk.dim(fix.preview.substring(0, 200) + (fix.preview.length > 200 ? '...' : '')));
135
+ }
136
+
137
+ } catch (error) {
138
+ failedCount++;
139
+ console.log(chalk.red(`❌ Failed to generate fix: ${error instanceof Error ? error.message : 'Unknown error'}`));
140
+ }
118
141
  }
119
142
 
120
- if (results.skipped.length > 0) {
121
- console.log(chalk.yellow(`⏭️ Skipped ${results.skipped.length} fixes:`));
122
- results.skipped.forEach(fix => {
123
- console.log(chalk.yellow(` • ${fix.filePath} - ${fix.violation.feature}`));
124
- });
125
- console.log();
143
+ if (fixes.length === 0) {
144
+ console.log(chalk.yellow('\n⚠️ No fixes were generated'));
145
+ return;
126
146
  }
127
147
 
128
- if (results.failed.length > 0) {
129
- console.log(chalk.red(`❌ Failed ${results.failed.length} fixes:`));
130
- results.failed.forEach(({ fix, error }) => {
131
- console.log(chalk.red(` • ${fix.filePath} - ${fix.violation.feature}: ${error}`));
132
- });
133
- console.log();
148
+ // Show results summary
149
+ console.log(chalk.cyan('\n📊 Fix Generation Results:\n'));
150
+ console.log(chalk.green(`✅ Generated ${successCount} fixes`));
151
+ if (failedCount > 0) {
152
+ console.log(chalk.red(`❌ Failed to generate ${failedCount} fixes`));
134
153
  }
135
154
 
136
- // Show rollback option if fixes were applied
137
- if (results.applied.length > 0) {
138
- const { default: inquirer } = await import('inquirer');
139
- const { showRollback } = await inquirer.prompt([
140
- {
141
- type: 'confirm',
142
- name: 'showRollback',
143
- message: 'Would you like to see rollback options?',
144
- default: false
145
- }
146
- ]);
147
-
148
- if (showRollback) {
149
- const { rollbackAction } = await inquirer.prompt([
150
- {
151
- type: 'list',
152
- name: 'rollbackAction',
153
- message: 'Rollback options:',
154
- choices: [
155
- { name: 'Keep all fixes', value: 'keep' },
156
- { name: 'Rollback all fixes', value: 'rollback_all' },
157
- { name: 'Rollback specific fixes', value: 'rollback_specific' }
158
- ]
159
- }
160
- ]);
155
+ // Show fixes with previews
156
+ if (!options.auto && fixes.length > 0) {
157
+ console.log(chalk.cyan('\n🔍 Generated Fixes:\n'));
158
+ fixes.forEach((fix, index) => {
159
+ console.log(chalk.white(`${index + 1}. ${fix.violation.feature} in ${fix.filePath}`));
160
+ console.log(chalk.dim(` Confidence: ${Math.round(fix.confidence * 100)}%`));
161
+ console.log(chalk.dim(` Strategy: ${fix.analysis.fixStrategy}`));
161
162
 
162
- if (rollbackAction === 'rollback_all') {
163
- await julesImplementer.rollbackAllFixes();
164
- UIComponents.showSuccessBox('All fixes have been rolled back');
165
- } else if (rollbackAction === 'rollback_specific') {
166
- const appliedFiles = julesImplementer.getAppliedFixes();
167
- const { filesToRollback } = await inquirer.prompt([
168
- {
169
- type: 'checkbox',
170
- name: 'filesToRollback',
171
- message: 'Select fixes to rollback:',
172
- choices: appliedFiles.map(file => ({ name: file, value: file }))
173
- }
174
- ]);
175
-
176
- for (const file of filesToRollback) {
177
- await julesImplementer.rollbackFix(file);
178
- }
179
-
180
- if (filesToRollback.length > 0) {
181
- UIComponents.showSuccessBox(`Rolled back ${filesToRollback.length} fixes`);
182
- }
163
+ // Show a snippet of the explanation
164
+ const explanation = fix.explanation?.split('\n')[0] || 'No explanation available';
165
+ if (explanation.length > 80) {
166
+ console.log(chalk.dim(` ${explanation.substring(0, 80)}...`));
167
+ } else {
168
+ console.log(chalk.dim(` ${explanation}`));
183
169
  }
184
- }
170
+ console.log();
171
+ });
185
172
  }
186
173
 
174
+ // Show summary
175
+ console.log(chalk.cyan(`\n📈 Summary: ${successCount}/${violations.length} fixes generated successfully`));
176
+
177
+ if (failedCount > 0) {
178
+ console.log(chalk.yellow(`\n⚠️ ${failedCount} fixes failed to generate. This may be due to:`));
179
+ console.log(chalk.yellow(' • Complex compatibility issues requiring manual review'));
180
+ console.log(chalk.yellow(' • API rate limits or temporary service issues'));
181
+ console.log(chalk.yellow(' • Files that couldn\'t be read or analyzed'));
182
+ }
183
+
184
+ // Show next steps
185
+ console.log(chalk.cyan('\n💡 Next Steps:'));
186
+ console.log(chalk.cyan(' • Review the generated fixes above'));
187
+ console.log(chalk.cyan(' • Apply fixes manually to your code'));
188
+ console.log(chalk.cyan(' • Test your application after applying fixes'));
189
+ console.log(chalk.cyan(' • Run "base check" to verify fixes resolve violations'));
190
+
187
191
  UIComponents.showSuccessBox('Fix process completed!');
188
192
 
189
193
  } catch (error) {
@@ -203,7 +207,7 @@ export async function fix(options: {
203
207
  UIComponents.showList([
204
208
  'Run "base init" to set up BaseGuard configuration',
205
209
  'Configure API keys with "base config set-keys"',
206
- 'Check GitHub integration for Jules fixing'
210
+ 'Ensure you are in a GitHub repository for Jules fixing'
207
211
  ]);
208
212
  } else if (apiError.type === 'network') {
209
213
  UIComponents.showList([
@@ -69,11 +69,10 @@ export async function init(options: {
69
69
  }
70
70
  }
71
71
 
72
- // Set up API keys if not skipped
72
+ // Set up API keys and coding agent if not skipped
73
73
  if (!options.skipApiKeys) {
74
74
  spinner.stop();
75
75
  const apiKeys = await Prompts.setupApiKeys();
76
- spinner.start();
77
76
 
78
77
  if (apiKeys.julesApiKey) {
79
78
  config.apiKeys.jules = apiKeys.julesApiKey;
@@ -81,6 +80,21 @@ export async function init(options: {
81
80
  if (apiKeys.geminiApiKey) {
82
81
  config.apiKeys.gemini = apiKeys.geminiApiKey;
83
82
  }
83
+
84
+ // Configure coding agent based on available keys
85
+ if (apiKeys.julesApiKey && apiKeys.geminiApiKey) {
86
+ const codingAgentChoice = await Prompts.chooseCodingAgent();
87
+ config.codingAgent.primary = codingAgentChoice.primary;
88
+ config.codingAgent.fallback = codingAgentChoice.fallback;
89
+ } else if (apiKeys.julesApiKey) {
90
+ config.codingAgent.primary = 'jules';
91
+ config.codingAgent.fallback = 'jules';
92
+ } else if (apiKeys.geminiApiKey) {
93
+ config.codingAgent.primary = 'gemini';
94
+ config.codingAgent.fallback = 'gemini';
95
+ }
96
+
97
+ spinner.start();
84
98
  }
85
99
 
86
100
  await ConfigurationManager.save(config);
@@ -28,8 +28,10 @@ export class LRUCache<K, V> {
28
28
  this.cache.delete(key);
29
29
  } else if (this.cache.size >= this.maxSize) {
30
30
  // Remove least recently used (first item)
31
- const firstKey = this.cache.keys().next().value;
32
- this.cache.delete(firstKey);
31
+ const firstKey = this.cache.keys().next().value as K;
32
+ if (firstKey !== undefined) {
33
+ this.cache.delete(firstKey);
34
+ }
33
35
  }
34
36
  this.cache.set(key, value);
35
37
  }
@@ -77,6 +77,10 @@ export class ConfigurationManager {
77
77
  jules: null,
78
78
  gemini: null
79
79
  },
80
+ codingAgent: {
81
+ primary: 'gemini', // 'jules' or 'gemini'
82
+ fallback: 'gemini' // fallback when primary fails
83
+ },
80
84
  automation: {
81
85
  enabled: false,
82
86
  trigger: 'pre-commit',
@@ -119,6 +123,10 @@ export class ConfigurationManager {
119
123
  jules: config.apiKeys?.jules || null,
120
124
  gemini: config.apiKeys?.gemini || null
121
125
  },
126
+ codingAgent: {
127
+ primary: this.validateCodingAgent(config.codingAgent?.primary) || defaultConfig.codingAgent.primary,
128
+ fallback: this.validateCodingAgent(config.codingAgent?.fallback) || defaultConfig.codingAgent.fallback
129
+ },
122
130
  automation: {
123
131
  enabled: config.automation?.enabled ?? defaultConfig.automation.enabled,
124
132
  trigger: this.validateTrigger(config.automation?.trigger) || defaultConfig.automation.trigger,
@@ -194,6 +202,16 @@ export class ConfigurationManager {
194
202
  return null;
195
203
  }
196
204
 
205
+ /**
206
+ * Validate coding agent selection
207
+ */
208
+ private static validateCodingAgent(agent: any): 'jules' | 'gemini' | null {
209
+ if (agent === 'jules' || agent === 'gemini') {
210
+ return agent;
211
+ }
212
+ return null;
213
+ }
214
+
197
215
  /**
198
216
  * Parse browser target string (e.g., "chrome 100", "safari baseline")
199
217
  */
@@ -437,6 +455,18 @@ export class ConfigurationManager {
437
455
  }
438
456
  }
439
457
 
458
+ // Validate coding agent
459
+ if (!config.codingAgent || typeof config.codingAgent !== 'object') {
460
+ errors.push('Coding agent configuration must be an object');
461
+ } else {
462
+ if (config.codingAgent.primary !== 'jules' && config.codingAgent.primary !== 'gemini') {
463
+ errors.push('Primary coding agent must be "jules" or "gemini"');
464
+ }
465
+ if (config.codingAgent.fallback !== 'jules' && config.codingAgent.fallback !== 'gemini') {
466
+ errors.push('Fallback coding agent must be "jules" or "gemini"');
467
+ }
468
+ }
469
+
440
470
  // Validate automation
441
471
  if (!config.automation || typeof config.automation !== 'object') {
442
472
  errors.push('Automation configuration must be an object');
@@ -481,6 +511,13 @@ export class ConfigurationManager {
481
511
  migratedConfig.apiKeys.gemini = config.apiKeys.gemini || null;
482
512
  }
483
513
 
514
+ if (config.codingAgent) {
515
+ migratedConfig.codingAgent = {
516
+ primary: this.validateCodingAgent(config.codingAgent.primary) || 'gemini',
517
+ fallback: this.validateCodingAgent(config.codingAgent.fallback) || 'gemini'
518
+ };
519
+ }
520
+
484
521
  if (config.automation) {
485
522
  migratedConfig.automation = {
486
523
  ...migratedConfig.automation,
@@ -130,8 +130,8 @@ export class DebugLogger {
130
130
  }
131
131
 
132
132
  this.currentSession.endTime = new Date();
133
- this.currentSession.summary.totalDuration =
134
- this.currentSession.endTime.getTime() - this.currentSession.startTime.getTime();
133
+ const duration = this.currentSession.endTime.getTime() - this.currentSession.startTime.getTime();
134
+ this.currentSession.summary.performance.totalDuration = duration;
135
135
 
136
136
  this.info('session', `Debug session ended: ${this.currentSession.id}`);
137
137
 
@@ -103,12 +103,15 @@ export class FileProcessor {
103
103
  );
104
104
 
105
105
  batchResults.forEach((result, index) => {
106
+ const task = batch[index];
107
+ if (!task) return;
108
+
106
109
  if (result.status === 'fulfilled') {
107
110
  allFeatures.push(...result.value);
108
111
  // Cache the result
109
- this.cacheManager.setCachedParseResult(batch[index].filePath, result.value);
112
+ this.cacheManager.setCachedParseResult(task.filePath, result.value);
110
113
  } else {
111
- console.warn(`Failed to process ${batch[index].filePath}: ${result.reason}`);
114
+ console.warn(`Failed to process ${task.filePath}: ${result.reason}`);
112
115
  }
113
116
  });
114
117
 
@@ -222,7 +225,11 @@ export class FileProcessor {
222
225
  /**
223
226
  * Assign task to worker
224
227
  */
225
- private async assignTaskToWorker(worker: Worker, task: WorkerTask): Promise<void> {
228
+ private async assignTaskToWorker(worker: Worker | undefined, task: WorkerTask): Promise<void> {
229
+ if (!worker) {
230
+ throw new Error('Worker is not available');
231
+ }
232
+
226
233
  return new Promise((resolve) => {
227
234
  const timeout = setTimeout(() => {
228
235
  console.warn(`Worker task ${task.id} timed out`);
package/src/core/index.ts CHANGED
@@ -10,4 +10,8 @@ export { FileProcessor } from './file-processor.js';
10
10
  export { DirectoryFilter } from './directory-filter.js';
11
11
  export { LazyLoader } from './lazy-loader.js';
12
12
  export { MemoryManager } from './memory-manager.js';
13
- export { StartupOptimizer } from './startup-optimizer.js';
13
+ export { StartupOptimizer } from './startup-optimizer.js';
14
+ export { SystemErrorHandler } from './system-error-handler.js';
15
+ export { GracefulDegradationManager } from './graceful-degradation-manager.js';
16
+ export { ConfigurationRecovery } from './configuration-recovery.js';
17
+ export { logger, DebugLogger } from './debug-logger.js';
@@ -1,9 +1,9 @@
1
1
  import { createReadStream } from 'fs';
2
2
  import { createInterface } from 'readline';
3
3
 
4
- // Add global type declaration for gc
4
+ // Use Node.js built-in gc type
5
5
  declare global {
6
- var gc: (() => void) | undefined;
6
+ var gc: NodeJS.GCFunction | undefined;
7
7
  }
8
8
 
9
9
  /**
@@ -291,4 +291,5 @@ class ViolationTracker {
291
291
  this.violations.clear();
292
292
  this.fileIndex.clear();
293
293
  this.nextFileId = 0;
294
- }
294
+ }
295
+ }
@@ -4,6 +4,8 @@
4
4
  export class StartupOptimizer {
5
5
  private static startTime = Date.now();
6
6
  private static initialized = false;
7
+ private static gcInterval: NodeJS.Timeout | null = null;
8
+ private static memoryInterval: NodeJS.Timeout | null = null;
7
9
 
8
10
  /**
9
11
  * Initialize BaseGuard with optimized startup
@@ -25,7 +27,7 @@ export class StartupOptimizer {
25
27
  this.initialized = true;
26
28
 
27
29
  const initTime = Date.now() - startTime;
28
- if (initTime > 200) {
30
+ if (initTime > 1000) {
29
31
  console.warn(`Slow startup detected: ${initTime}ms`);
30
32
  }
31
33
  } catch (error) {
@@ -57,7 +59,7 @@ export class StartupOptimizer {
57
59
  // Enable garbage collection hints if available
58
60
  if (global.gc) {
59
61
  // Set up periodic GC for long-running processes
60
- setInterval(() => {
62
+ this.gcInterval = setInterval(() => {
61
63
  const { MemoryManager } = require('./memory-manager.js');
62
64
  const memCheck = MemoryManager.checkMemoryUsage();
63
65
 
@@ -124,6 +126,16 @@ export class StartupOptimizer {
124
126
  const { LazyLoader } = require('./lazy-loader.js');
125
127
  const { MemoryManager } = require('./memory-manager.js');
126
128
 
129
+ // Clear intervals
130
+ if (this.gcInterval) {
131
+ clearInterval(this.gcInterval);
132
+ this.gcInterval = null;
133
+ }
134
+ if (this.memoryInterval) {
135
+ clearInterval(this.memoryInterval);
136
+ this.memoryInterval = null;
137
+ }
138
+
127
139
  // Clear caches
128
140
  LazyLoader.clearCache();
129
141
 
@@ -145,7 +157,7 @@ export class StartupOptimizer {
145
157
  const metrics = this.getStartupMetrics();
146
158
 
147
159
  // Check startup time
148
- if (metrics.totalStartupTime > 500) {
160
+ if (metrics.totalStartupTime > 2000) {
149
161
  issues.push(`Slow startup: ${metrics.totalStartupTime}ms`);
150
162
  recommendations.push('Consider reducing dependencies or using lazy loading');
151
163
  }
@@ -170,4 +182,74 @@ export class StartupOptimizer {
170
182
  recommendations
171
183
  };
172
184
  }
185
+
186
+ /**
187
+ * Optimize memory usage by reducing object overhead
188
+ */
189
+ static optimizeMemoryUsage(): void {
190
+ // Enable V8 memory optimizations if available
191
+ if (process.env.NODE_ENV !== 'development') {
192
+ // Set V8 flags for better memory management
193
+ process.env.NODE_OPTIONS = (process.env.NODE_OPTIONS || '') + ' --max-old-space-size=512 --optimize-for-size';
194
+ }
195
+
196
+ // Setup memory monitoring
197
+ if (global.gc) {
198
+ this.memoryInterval = setInterval(() => {
199
+ const usage = process.memoryUsage();
200
+ const heapUsedMB = usage.heapUsed / 1024 / 1024;
201
+
202
+ // Force GC if memory usage is high
203
+ if (heapUsedMB > 100 && global.gc) {
204
+ global.gc();
205
+ }
206
+ }, 30000);
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Reduce startup time by deferring non-critical operations
212
+ */
213
+ static async deferNonCriticalOperations(): Promise<void> {
214
+ // Defer heavy operations until after startup
215
+ setTimeout(async () => {
216
+ try {
217
+ // Preload remaining dependencies
218
+ const { LazyLoader } = await import('./lazy-loader.js');
219
+ LazyLoader.preloadCommon();
220
+
221
+ // Initialize caches
222
+ const { CacheManager } = await import('./cache-manager.js');
223
+ // Cache initialization would happen here
224
+
225
+ // Cleanup old logs
226
+ const { logger } = await import('./debug-logger.js');
227
+ logger.cleanupOldLogs().catch(() => {});
228
+ } catch (error) {
229
+ // Ignore errors in deferred operations
230
+ }
231
+ }, 100); // Defer by 100ms
232
+ }
233
+
234
+ /**
235
+ * Optimize startup by preloading critical dependencies
236
+ */
237
+ static async optimizeStartup(): Promise<void> {
238
+ // Start loading critical dependencies in background
239
+ const { LazyLoader } = await import('./lazy-loader.js');
240
+
241
+ const criticalLoads = [
242
+ LazyLoader.getWebFeatures().catch(() => {}),
243
+ LazyLoader.getBabelParser().catch(() => {})
244
+ ];
245
+
246
+ // Don't wait for all to complete, just start the process
247
+ Promise.all(criticalLoads);
248
+
249
+ // Setup memory optimizations
250
+ this.optimizeMemoryUsage();
251
+
252
+ // Defer non-critical operations
253
+ this.deferNonCriticalOperations();
254
+ }
173
255
  }
@@ -499,22 +499,28 @@ export class SystemErrorHandler {
499
499
 
500
500
  static async handleProcessSignals(): Promise<void> {
501
501
  const gracefulShutdown = async (signal: string) => {
502
- console.log(chalk.yellow(`\n⚠️ Received ${signal}, shutting down gracefully...`));
502
+ // Only log for non-interactive signals (SIGTERM, not SIGINT from Ctrl+C)
503
+ if (signal !== 'SIGINT') {
504
+ console.log(chalk.yellow(`\n⚠️ Received ${signal}, shutting down gracefully...`));
505
+ }
503
506
 
504
- // Save error log before exit
507
+ // Cleanup intervals and timers
505
508
  try {
506
- await SystemErrorHandler.saveErrorLog();
509
+ const { StartupOptimizer } = await import('./startup-optimizer.js');
510
+ StartupOptimizer.cleanup();
507
511
  } catch (error) {
508
- // Ignore errors during shutdown
512
+ // Ignore cleanup errors
509
513
  }
510
514
 
511
- // Show error summary if there were issues
512
- const summary = SystemErrorHandler.getErrorSummary();
513
- if (summary.total > 0) {
514
- console.log(chalk.cyan(`\n📊 Session summary: ${summary.total} errors encountered`));
515
- if (summary.critical > 0) {
516
- console.log(chalk.red(` ${summary.critical} critical errors require attention`));
517
- }
515
+ // Save error log before exit (but don't wait too long)
516
+ try {
517
+ const savePromise = SystemErrorHandler.saveErrorLog();
518
+ await Promise.race([
519
+ savePromise,
520
+ new Promise(resolve => setTimeout(resolve, 100)) // Max 100ms wait
521
+ ]);
522
+ } catch (error) {
523
+ // Ignore errors during shutdown
518
524
  }
519
525
 
520
526
  process.exit(0);