baseguard 1.0.0

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 (244) hide show
  1. package/.eslintrc.json +25 -0
  2. package/.prettierrc +8 -0
  3. package/README.md +94 -0
  4. package/bin/base.js +494 -0
  5. package/dist/ai/fix-manager.d.ts +67 -0
  6. package/dist/ai/fix-manager.d.ts.map +1 -0
  7. package/dist/ai/fix-manager.js +326 -0
  8. package/dist/ai/fix-manager.js.map +1 -0
  9. package/dist/ai/gemini-analyzer.d.ts +116 -0
  10. package/dist/ai/gemini-analyzer.d.ts.map +1 -0
  11. package/dist/ai/gemini-analyzer.js +572 -0
  12. package/dist/ai/gemini-analyzer.js.map +1 -0
  13. package/dist/ai/index.d.ts +4 -0
  14. package/dist/ai/index.d.ts.map +1 -0
  15. package/dist/ai/index.js +5 -0
  16. package/dist/ai/index.js.map +1 -0
  17. package/dist/ai/jules-implementer.d.ts +115 -0
  18. package/dist/ai/jules-implementer.d.ts.map +1 -0
  19. package/dist/ai/jules-implementer.js +387 -0
  20. package/dist/ai/jules-implementer.js.map +1 -0
  21. package/dist/commands/automation.d.ts +5 -0
  22. package/dist/commands/automation.d.ts.map +1 -0
  23. package/dist/commands/automation.js +305 -0
  24. package/dist/commands/automation.js.map +1 -0
  25. package/dist/commands/check.d.ts +9 -0
  26. package/dist/commands/check.d.ts.map +1 -0
  27. package/dist/commands/check.js +113 -0
  28. package/dist/commands/check.js.map +1 -0
  29. package/dist/commands/config.d.ts +11 -0
  30. package/dist/commands/config.d.ts.map +1 -0
  31. package/dist/commands/config.js +324 -0
  32. package/dist/commands/config.js.map +1 -0
  33. package/dist/commands/fix.d.ts +9 -0
  34. package/dist/commands/fix.d.ts.map +1 -0
  35. package/dist/commands/fix.js +207 -0
  36. package/dist/commands/fix.js.map +1 -0
  37. package/dist/commands/index.d.ts +6 -0
  38. package/dist/commands/index.d.ts.map +1 -0
  39. package/dist/commands/index.js +7 -0
  40. package/dist/commands/index.js.map +1 -0
  41. package/dist/commands/init.d.ts +9 -0
  42. package/dist/commands/init.d.ts.map +1 -0
  43. package/dist/commands/init.js +125 -0
  44. package/dist/commands/init.js.map +1 -0
  45. package/dist/core/api-key-manager.d.ts +83 -0
  46. package/dist/core/api-key-manager.d.ts.map +1 -0
  47. package/dist/core/api-key-manager.js +244 -0
  48. package/dist/core/api-key-manager.js.map +1 -0
  49. package/dist/core/baseguard.d.ts +46 -0
  50. package/dist/core/baseguard.d.ts.map +1 -0
  51. package/dist/core/baseguard.js +132 -0
  52. package/dist/core/baseguard.js.map +1 -0
  53. package/dist/core/baseline-checker.d.ts +63 -0
  54. package/dist/core/baseline-checker.d.ts.map +1 -0
  55. package/dist/core/baseline-checker.js +502 -0
  56. package/dist/core/baseline-checker.js.map +1 -0
  57. package/dist/core/cache-manager.d.ts +88 -0
  58. package/dist/core/cache-manager.d.ts.map +1 -0
  59. package/dist/core/cache-manager.js +213 -0
  60. package/dist/core/cache-manager.js.map +1 -0
  61. package/dist/core/configuration.d.ts +140 -0
  62. package/dist/core/configuration.d.ts.map +1 -0
  63. package/dist/core/configuration.js +474 -0
  64. package/dist/core/configuration.js.map +1 -0
  65. package/dist/core/directory-filter.d.ts +90 -0
  66. package/dist/core/directory-filter.d.ts.map +1 -0
  67. package/dist/core/directory-filter.js +319 -0
  68. package/dist/core/directory-filter.js.map +1 -0
  69. package/dist/core/error-handler.d.ts +110 -0
  70. package/dist/core/error-handler.d.ts.map +1 -0
  71. package/dist/core/error-handler.js +392 -0
  72. package/dist/core/error-handler.js.map +1 -0
  73. package/dist/core/file-processor.d.ts +80 -0
  74. package/dist/core/file-processor.d.ts.map +1 -0
  75. package/dist/core/file-processor.js +259 -0
  76. package/dist/core/file-processor.js.map +1 -0
  77. package/dist/core/gitignore-manager.d.ts +44 -0
  78. package/dist/core/gitignore-manager.d.ts.map +1 -0
  79. package/dist/core/gitignore-manager.js +147 -0
  80. package/dist/core/gitignore-manager.js.map +1 -0
  81. package/dist/core/index.d.ts +13 -0
  82. package/dist/core/index.d.ts.map +1 -0
  83. package/dist/core/index.js +13 -0
  84. package/dist/core/index.js.map +1 -0
  85. package/dist/core/lazy-loader.d.ts +68 -0
  86. package/dist/core/lazy-loader.d.ts.map +1 -0
  87. package/dist/core/lazy-loader.js +260 -0
  88. package/dist/core/lazy-loader.js.map +1 -0
  89. package/dist/core/memory-manager.d.ts +1 -0
  90. package/dist/core/memory-manager.d.ts.map +1 -0
  91. package/dist/core/memory-manager.js +2 -0
  92. package/dist/core/memory-manager.js.map +1 -0
  93. package/dist/core/startup-optimizer.d.ts +45 -0
  94. package/dist/core/startup-optimizer.d.ts.map +1 -0
  95. package/dist/core/startup-optimizer.js +140 -0
  96. package/dist/core/startup-optimizer.js.map +1 -0
  97. package/dist/git/automation-engine.d.ts +58 -0
  98. package/dist/git/automation-engine.d.ts.map +1 -0
  99. package/dist/git/automation-engine.js +318 -0
  100. package/dist/git/automation-engine.js.map +1 -0
  101. package/dist/git/github-manager.d.ts +71 -0
  102. package/dist/git/github-manager.d.ts.map +1 -0
  103. package/dist/git/github-manager.js +226 -0
  104. package/dist/git/github-manager.js.map +1 -0
  105. package/dist/git/hook-manager.d.ts +43 -0
  106. package/dist/git/hook-manager.d.ts.map +1 -0
  107. package/dist/git/hook-manager.js +191 -0
  108. package/dist/git/hook-manager.js.map +1 -0
  109. package/dist/git/index.d.ts +4 -0
  110. package/dist/git/index.d.ts.map +1 -0
  111. package/dist/git/index.js +5 -0
  112. package/dist/git/index.js.map +1 -0
  113. package/dist/index.d.ts +8 -0
  114. package/dist/index.d.ts.map +1 -0
  115. package/dist/index.js +9 -0
  116. package/dist/index.js.map +1 -0
  117. package/dist/parsers/feature-validator.d.ts +60 -0
  118. package/dist/parsers/feature-validator.d.ts.map +1 -0
  119. package/dist/parsers/feature-validator.js +483 -0
  120. package/dist/parsers/feature-validator.js.map +1 -0
  121. package/dist/parsers/index.d.ts +8 -0
  122. package/dist/parsers/index.d.ts.map +1 -0
  123. package/dist/parsers/index.js +9 -0
  124. package/dist/parsers/index.js.map +1 -0
  125. package/dist/parsers/parser-manager.d.ts +103 -0
  126. package/dist/parsers/parser-manager.d.ts.map +1 -0
  127. package/dist/parsers/parser-manager.js +321 -0
  128. package/dist/parsers/parser-manager.js.map +1 -0
  129. package/dist/parsers/parser.d.ts +23 -0
  130. package/dist/parsers/parser.d.ts.map +1 -0
  131. package/dist/parsers/parser.js +6 -0
  132. package/dist/parsers/parser.js.map +1 -0
  133. package/dist/parsers/react-parser.d.ts +22 -0
  134. package/dist/parsers/react-parser.d.ts.map +1 -0
  135. package/dist/parsers/react-parser.js +307 -0
  136. package/dist/parsers/react-parser.js.map +1 -0
  137. package/dist/parsers/svelte-parser.d.ts +33 -0
  138. package/dist/parsers/svelte-parser.d.ts.map +1 -0
  139. package/dist/parsers/svelte-parser.js +408 -0
  140. package/dist/parsers/svelte-parser.js.map +1 -0
  141. package/dist/parsers/vanilla-parser.d.ts +31 -0
  142. package/dist/parsers/vanilla-parser.d.ts.map +1 -0
  143. package/dist/parsers/vanilla-parser.js +590 -0
  144. package/dist/parsers/vanilla-parser.js.map +1 -0
  145. package/dist/parsers/vue-parser.d.ts +9 -0
  146. package/dist/parsers/vue-parser.d.ts.map +1 -0
  147. package/dist/parsers/vue-parser.js +16 -0
  148. package/dist/parsers/vue-parser.js.map +1 -0
  149. package/dist/terminal-header.d.ts +12 -0
  150. package/dist/terminal-header.js +45 -0
  151. package/dist/types/index.d.ts +83 -0
  152. package/dist/types/index.d.ts.map +1 -0
  153. package/dist/types/index.js +5 -0
  154. package/dist/types/index.js.map +1 -0
  155. package/dist/ui/components.d.ts +133 -0
  156. package/dist/ui/components.d.ts.map +1 -0
  157. package/dist/ui/components.js +482 -0
  158. package/dist/ui/components.js.map +1 -0
  159. package/dist/ui/help.d.ts +11 -0
  160. package/dist/ui/help.d.ts.map +1 -0
  161. package/dist/ui/help.js +161 -0
  162. package/dist/ui/help.js.map +1 -0
  163. package/dist/ui/index.d.ts +5 -0
  164. package/dist/ui/index.d.ts.map +1 -0
  165. package/dist/ui/index.js +5 -0
  166. package/dist/ui/index.js.map +1 -0
  167. package/dist/ui/prompts.d.ts +63 -0
  168. package/dist/ui/prompts.d.ts.map +1 -0
  169. package/dist/ui/prompts.js +611 -0
  170. package/dist/ui/prompts.js.map +1 -0
  171. package/dist/ui/terminal-header.d.ts +13 -0
  172. package/dist/ui/terminal-header.d.ts.map +1 -0
  173. package/dist/ui/terminal-header.js +46 -0
  174. package/dist/ui/terminal-header.js.map +1 -0
  175. package/package.json +80 -0
  176. package/src/ai/__tests__/gemini-analyzer.test.ts +181 -0
  177. package/src/ai/fix-manager.ts +362 -0
  178. package/src/ai/gemini-analyzer.ts +671 -0
  179. package/src/ai/index.ts +4 -0
  180. package/src/ai/jules-implementer.ts +459 -0
  181. package/src/commands/automation.ts +344 -0
  182. package/src/commands/check.ts +299 -0
  183. package/src/commands/config.ts +365 -0
  184. package/src/commands/fix.ts +234 -0
  185. package/src/commands/index.ts +6 -0
  186. package/src/commands/init.ts +142 -0
  187. package/src/commands/status.ts +0 -0
  188. package/src/core/api-key-manager.ts +298 -0
  189. package/src/core/baseguard.ts +742 -0
  190. package/src/core/baseline-checker.ts +563 -0
  191. package/src/core/cache-manager.ts +270 -0
  192. package/src/core/configuration-recovery.ts +676 -0
  193. package/src/core/configuration.ts +559 -0
  194. package/src/core/debug-logger.ts +590 -0
  195. package/src/core/directory-filter.ts +421 -0
  196. package/src/core/error-handler.ts +517 -0
  197. package/src/core/file-processor.ts +331 -0
  198. package/src/core/gitignore-manager.ts +169 -0
  199. package/src/core/graceful-degradation-manager.ts +596 -0
  200. package/src/core/index.ts +13 -0
  201. package/src/core/lazy-loader.ts +307 -0
  202. package/src/core/logger.ts +0 -0
  203. package/src/core/memory-manager.ts +294 -0
  204. package/src/core/startup-optimizer.ts +173 -0
  205. package/src/core/system-error-handler.ts +746 -0
  206. package/src/git/automation-engine.ts +361 -0
  207. package/src/git/github-manager.ts +260 -0
  208. package/src/git/hook-manager.ts +210 -0
  209. package/src/git/index.ts +4 -0
  210. package/src/index.ts +8 -0
  211. package/src/parsers/feature-validator.ts +559 -0
  212. package/src/parsers/index.ts +8 -0
  213. package/src/parsers/parser-manager.ts +419 -0
  214. package/src/parsers/parser.ts +26 -0
  215. package/src/parsers/react-parser-optimized.ts +161 -0
  216. package/src/parsers/react-parser.ts +359 -0
  217. package/src/parsers/svelte-parser.ts +506 -0
  218. package/src/parsers/vanilla-parser.ts +682 -0
  219. package/src/parsers/vue-parser.ts +472 -0
  220. package/src/types/index.ts +92 -0
  221. package/src/ui/components.ts +567 -0
  222. package/src/ui/help.ts +193 -0
  223. package/src/ui/index.ts +4 -0
  224. package/src/ui/prompts.ts +688 -0
  225. package/src/ui/terminal-header.ts +59 -0
  226. package/test-config-commands.js +56 -0
  227. package/test-header-simple.js +33 -0
  228. package/test-terminal-header.js +12 -0
  229. package/test-ui.js +29 -0
  230. package/tests/e2e/baseguard.e2e.test.ts +516 -0
  231. package/tests/e2e/cross-platform.e2e.test.ts +420 -0
  232. package/tests/e2e/git-integration.e2e.test.ts +487 -0
  233. package/tests/fixtures/react-project/package.json +14 -0
  234. package/tests/fixtures/react-project/src/App.css +76 -0
  235. package/tests/fixtures/react-project/src/App.tsx +77 -0
  236. package/tests/fixtures/svelte-project/package.json +11 -0
  237. package/tests/fixtures/svelte-project/src/App.svelte +369 -0
  238. package/tests/fixtures/vanilla-project/index.html +76 -0
  239. package/tests/fixtures/vanilla-project/script.js +331 -0
  240. package/tests/fixtures/vanilla-project/styles.css +359 -0
  241. package/tests/fixtures/vue-project/package.json +12 -0
  242. package/tests/fixtures/vue-project/src/App.vue +216 -0
  243. package/tsconfig.json +36 -0
  244. package/vitest.config.ts +10 -0
@@ -0,0 +1,746 @@
1
+ import chalk from 'chalk';
2
+ import { promises as fs } from 'fs';
3
+ import path from 'path';
4
+ import { UIComponents } from '../ui/components.js';
5
+
6
+ export interface SystemErrorContext {
7
+ operation: string;
8
+ file?: string;
9
+ line?: number;
10
+ details?: any;
11
+ timestamp?: Date;
12
+ userId?: string;
13
+ }
14
+
15
+ export interface RecoveryStrategy {
16
+ name: string;
17
+ description: string;
18
+ execute: () => Promise<boolean>;
19
+ priority: number;
20
+ }
21
+
22
+ export class SystemError extends Error {
23
+ constructor(
24
+ message: string,
25
+ public code: string,
26
+ public context?: SystemErrorContext,
27
+ public recoverable: boolean = true,
28
+ public severity: 'low' | 'medium' | 'high' | 'critical' = 'medium'
29
+ ) {
30
+ super(message);
31
+ this.name = 'SystemError';
32
+ this.context = {
33
+ ...context,
34
+ timestamp: new Date()
35
+ };
36
+ }
37
+ }
38
+
39
+ export class SystemErrorHandler {
40
+ private static retryAttempts = new Map<string, number>();
41
+ private static maxRetries = 3;
42
+ private static backoffBase = 1000; // 1 second
43
+ private static errorLog: SystemError[] = [];
44
+ private static maxLogSize = 100;
45
+ private static recoveryStrategies = new Map<string, RecoveryStrategy[]>();
46
+ private static gracefulDegradationEnabled = true;
47
+ private static offlineMode = false;
48
+
49
+ static async withRetry<T>(
50
+ operation: () => Promise<T>,
51
+ context: SystemErrorContext,
52
+ maxRetries: number = SystemErrorHandler.maxRetries,
53
+ customBackoff?: (attempt: number) => number
54
+ ): Promise<T> {
55
+ const key = `${context.operation}-${context.file || 'global'}`;
56
+ let attempts = SystemErrorHandler.retryAttempts.get(key) || 0;
57
+
58
+ try {
59
+ const result = await operation();
60
+ SystemErrorHandler.retryAttempts.delete(key); // Reset on success
61
+ return result;
62
+ } catch (error) {
63
+ attempts++;
64
+ SystemErrorHandler.retryAttempts.set(key, attempts);
65
+
66
+ const wrappedError = SystemErrorHandler.wrapError(error, context);
67
+ SystemErrorHandler.logError(wrappedError);
68
+
69
+ if (attempts >= maxRetries) {
70
+ SystemErrorHandler.retryAttempts.delete(key);
71
+
72
+ // Try recovery strategies before giving up
73
+ const recovered = await SystemErrorHandler.attemptRecovery(wrappedError);
74
+ if (!recovered) {
75
+ throw wrappedError;
76
+ }
77
+
78
+ // If recovery succeeded, try the operation once more
79
+ SystemErrorHandler.retryAttempts.delete(key);
80
+ return SystemErrorHandler.withRetry(operation, context, 1); // One more try after recovery
81
+ }
82
+
83
+ const delay = customBackoff ? customBackoff(attempts) : SystemErrorHandler.backoffBase * Math.pow(2, attempts - 1);
84
+ console.warn(chalk.yellow(`โš ๏ธ Retry ${attempts}/${maxRetries} for ${context.operation} in ${delay}ms`));
85
+
86
+ await SystemErrorHandler.sleep(delay);
87
+ return SystemErrorHandler.withRetry(operation, context, maxRetries, customBackoff);
88
+ }
89
+ }
90
+
91
+ static async handleGracefully<T>(
92
+ operation: () => Promise<T>,
93
+ fallback: T | (() => Promise<T>),
94
+ context: SystemErrorContext,
95
+ options: {
96
+ logError?: boolean;
97
+ showWarning?: boolean;
98
+ attemptRecovery?: boolean;
99
+ } = {}
100
+ ): Promise<T> {
101
+ const { logError = true, showWarning = true, attemptRecovery = true } = options;
102
+
103
+ try {
104
+ return await operation();
105
+ } catch (error) {
106
+ const wrappedError = SystemErrorHandler.wrapError(error, context);
107
+
108
+ if (logError) {
109
+ SystemErrorHandler.logError(wrappedError);
110
+ }
111
+
112
+ if (showWarning) {
113
+ console.warn(chalk.yellow(`โš ๏ธ ${context.operation} failed: ${error.message}`));
114
+ }
115
+
116
+ // Attempt recovery if enabled
117
+ if (attemptRecovery && wrappedError.recoverable) {
118
+ const recovered = await SystemErrorHandler.attemptRecovery(wrappedError);
119
+ if (recovered) {
120
+ try {
121
+ return await operation();
122
+ } catch (retryError) {
123
+ // Recovery didn't work, fall back
124
+ }
125
+ }
126
+ }
127
+
128
+ // Use fallback
129
+ const fallbackValue = typeof fallback === 'function' ? await (fallback as () => Promise<T>)() : fallback;
130
+
131
+ if (showWarning) {
132
+ console.warn(chalk.cyan('๐Ÿ”„ Using fallback strategy'));
133
+ }
134
+
135
+ return fallbackValue;
136
+ }
137
+ }
138
+
139
+ static wrapError(error: any, context: SystemErrorContext): SystemError {
140
+ if (error instanceof SystemError) {
141
+ return error;
142
+ }
143
+
144
+ let code = 'UNKNOWN_ERROR';
145
+ let recoverable = true;
146
+ let severity: 'low' | 'medium' | 'high' | 'critical' = 'medium';
147
+
148
+ // Categorize errors with enhanced detection
149
+ if (error.code === 'ENOENT') {
150
+ code = 'FILE_NOT_FOUND';
151
+ severity = 'low';
152
+ } else if (error.code === 'EACCES' || error.code === 'EPERM') {
153
+ code = 'PERMISSION_DENIED';
154
+ recoverable = false;
155
+ severity = 'high';
156
+ } else if (error.code === 'ENOTDIR') {
157
+ code = 'INVALID_PATH';
158
+ severity = 'medium';
159
+ } else if (error.name === 'SyntaxError') {
160
+ code = 'SYNTAX_ERROR';
161
+ severity = 'low';
162
+ } else if (error.code === 'NETWORK_ERROR' || error.code === 'ECONNREFUSED' || error.code === 'ENOTFOUND') {
163
+ code = 'NETWORK_ERROR';
164
+ severity = 'medium';
165
+ } else if (error.code === 'ETIMEDOUT') {
166
+ code = 'TIMEOUT_ERROR';
167
+ severity = 'medium';
168
+ } else if (error.name === 'TypeError' && error.message.includes('fetch')) {
169
+ code = 'API_ERROR';
170
+ severity = 'medium';
171
+ } else if (error.code === 'EMFILE' || error.code === 'ENFILE') {
172
+ code = 'TOO_MANY_FILES';
173
+ severity = 'high';
174
+ } else if (error.code === 'ENOSPC') {
175
+ code = 'DISK_FULL';
176
+ recoverable = false;
177
+ severity = 'critical';
178
+ } else if (error.message?.includes('out of memory') || error.code === 'ERR_MEMORY_ALLOCATION_FAILED') {
179
+ code = 'OUT_OF_MEMORY';
180
+ recoverable = false;
181
+ severity = 'critical';
182
+ } else if (error.message?.includes('configuration') || error.message?.includes('config')) {
183
+ code = 'CONFIGURATION_ERROR';
184
+ severity = 'medium';
185
+ } else if (error.message?.includes('parser') || error.message?.includes('parsing')) {
186
+ code = 'PARSER_ERROR';
187
+ severity = 'low';
188
+ }
189
+
190
+ return new SystemError(
191
+ error.message || 'An unknown error occurred',
192
+ code,
193
+ context,
194
+ recoverable,
195
+ severity
196
+ );
197
+ }
198
+
199
+ static displayError(error: SystemError, options: { verbose?: boolean; showRecovery?: boolean } = {}): void {
200
+ const { verbose = false, showRecovery = true } = options;
201
+
202
+ // Color based on severity
203
+ const severityColors = {
204
+ low: chalk.yellow,
205
+ medium: chalk.red,
206
+ high: chalk.redBright,
207
+ critical: chalk.bgRed.white
208
+ };
209
+
210
+ const colorFn = severityColors[error.severity];
211
+
212
+ console.error(colorFn('โŒ System Error:'), error.message);
213
+
214
+ if (error.context?.file) {
215
+ console.error(chalk.dim(` File: ${error.context.file}`));
216
+ }
217
+
218
+ if (error.context?.line) {
219
+ console.error(chalk.dim(` Line: ${error.context.line}`));
220
+ }
221
+
222
+ if (verbose && error.context?.details) {
223
+ console.error(chalk.dim(` Details: ${JSON.stringify(error.context.details, null, 2)}`));
224
+ }
225
+
226
+ // Show recovery options
227
+ if (showRecovery && error.recoverable) {
228
+ SystemErrorHandler.showRecoveryOptions(error);
229
+ }
230
+
231
+ // Provide helpful suggestions
232
+ SystemErrorHandler.provideSuggestions(error);
233
+ }
234
+
235
+ private static async attemptRecovery(error: SystemError): Promise<boolean> {
236
+ const strategies = SystemErrorHandler.recoveryStrategies.get(error.code) || [];
237
+
238
+ if (strategies.length === 0) {
239
+ return false;
240
+ }
241
+
242
+ console.log(chalk.cyan('๐Ÿ”ง Attempting automatic recovery...'));
243
+
244
+ // Sort strategies by priority
245
+ strategies.sort((a, b) => b.priority - a.priority);
246
+
247
+ for (const strategy of strategies) {
248
+ try {
249
+ console.log(chalk.dim(` Trying: ${strategy.description}`));
250
+ const success = await strategy.execute();
251
+
252
+ if (success) {
253
+ console.log(chalk.green(`โœ… Recovery successful: ${strategy.name}`));
254
+ return true;
255
+ }
256
+ } catch (recoveryError) {
257
+ console.log(chalk.dim(` Recovery failed: ${recoveryError.message}`));
258
+ }
259
+ }
260
+
261
+ console.log(chalk.yellow('โš ๏ธ Automatic recovery failed'));
262
+ return false;
263
+ }
264
+
265
+ private static showRecoveryOptions(error: SystemError): void {
266
+ const strategies = SystemErrorHandler.recoveryStrategies.get(error.code) || [];
267
+
268
+ if (strategies.length > 0) {
269
+ console.error(chalk.cyan('\n๐Ÿ”ง Available recovery options:'));
270
+ strategies.forEach((strategy, index) => {
271
+ console.error(chalk.cyan(` ${index + 1}. ${strategy.description}`));
272
+ });
273
+ }
274
+ }
275
+
276
+ private static provideSuggestions(error: SystemError): void {
277
+ const suggestions: string[] = [];
278
+
279
+ switch (error.code) {
280
+ case 'FILE_NOT_FOUND':
281
+ suggestions.push('Check if the file path is correct');
282
+ suggestions.push('Ensure the file exists and is accessible');
283
+ suggestions.push('Verify the working directory is correct');
284
+ break;
285
+ case 'PERMISSION_DENIED':
286
+ suggestions.push('Check file permissions with ls -la (Unix) or dir (Windows)');
287
+ suggestions.push('Run with appropriate privileges if needed');
288
+ suggestions.push('Ensure the file is not locked by another process');
289
+ break;
290
+ case 'SYNTAX_ERROR':
291
+ suggestions.push('Check the file syntax with your editor');
292
+ suggestions.push('Ensure the file is valid for its type');
293
+ suggestions.push('Look for missing brackets, quotes, or semicolons');
294
+ break;
295
+ case 'NETWORK_ERROR':
296
+ suggestions.push('Check your internet connection');
297
+ suggestions.push('Verify API endpoints are accessible');
298
+ suggestions.push('Consider running in offline mode with --offline flag');
299
+ suggestions.push('Check if you\'re behind a proxy or firewall');
300
+ break;
301
+ case 'API_ERROR':
302
+ suggestions.push('Verify your API keys are correct');
303
+ suggestions.push('Check API rate limits and quotas');
304
+ suggestions.push('Ensure the API service is available');
305
+ break;
306
+ case 'TIMEOUT_ERROR':
307
+ suggestions.push('Increase timeout with --timeout flag');
308
+ suggestions.push('Check network stability');
309
+ suggestions.push('Try again later if the service is overloaded');
310
+ break;
311
+ case 'TOO_MANY_FILES':
312
+ suggestions.push('Close unnecessary applications');
313
+ suggestions.push('Increase system file descriptor limits');
314
+ suggestions.push('Process files in smaller batches');
315
+ break;
316
+ case 'DISK_FULL':
317
+ suggestions.push('Free up disk space');
318
+ suggestions.push('Clean temporary files');
319
+ suggestions.push('Move files to another drive');
320
+ break;
321
+ case 'OUT_OF_MEMORY':
322
+ suggestions.push('Close other applications to free memory');
323
+ suggestions.push('Process files in smaller batches');
324
+ suggestions.push('Increase system memory if possible');
325
+ break;
326
+ case 'CONFIGURATION_ERROR':
327
+ suggestions.push('Run "base init" to reconfigure BaseGuard');
328
+ suggestions.push('Check .baseguardrc.json for syntax errors');
329
+ suggestions.push('Verify all required configuration fields are present');
330
+ break;
331
+ case 'PARSER_ERROR':
332
+ suggestions.push('Check file syntax and encoding');
333
+ suggestions.push('Ensure file extension matches content type');
334
+ suggestions.push('Try with a simpler version of the file');
335
+ break;
336
+ }
337
+
338
+ if (suggestions.length > 0) {
339
+ console.error(chalk.cyan('\n๐Ÿ’ก Suggestions:'));
340
+ suggestions.forEach(suggestion => {
341
+ console.error(chalk.cyan(` โ€ข ${suggestion}`));
342
+ });
343
+ }
344
+
345
+ // Show degradation options
346
+ if (SystemErrorHandler.gracefulDegradationEnabled) {
347
+ SystemErrorHandler.showDegradationOptions(error);
348
+ }
349
+ }
350
+
351
+ private static showDegradationOptions(error: SystemError): void {
352
+ const degradationOptions: string[] = [];
353
+
354
+ switch (error.code) {
355
+ case 'NETWORK_ERROR':
356
+ case 'API_ERROR':
357
+ degradationOptions.push('Continue with baseline-only checking (no AI analysis)');
358
+ degradationOptions.push('Use cached results if available');
359
+ degradationOptions.push('Switch to offline mode automatically');
360
+ break;
361
+ case 'SYNTAX_ERROR':
362
+ case 'PARSER_ERROR':
363
+ degradationOptions.push('Skip malformed files and continue with others');
364
+ degradationOptions.push('Use basic text parsing as fallback');
365
+ degradationOptions.push('Report parsing issues and continue');
366
+ break;
367
+ case 'TOO_MANY_FILES':
368
+ degradationOptions.push('Process files in smaller batches');
369
+ degradationOptions.push('Skip non-essential file types');
370
+ degradationOptions.push('Use streaming processing to reduce memory usage');
371
+ break;
372
+ case 'CONFIGURATION_ERROR':
373
+ degradationOptions.push('Use default configuration settings');
374
+ degradationOptions.push('Skip optional features that require configuration');
375
+ degradationOptions.push('Continue with basic compatibility checking');
376
+ break;
377
+ }
378
+
379
+ if (degradationOptions.length > 0) {
380
+ console.error(chalk.magenta('\n๐Ÿ”„ Graceful degradation options:'));
381
+ degradationOptions.forEach(option => {
382
+ console.error(chalk.magenta(` โ€ข ${option}`));
383
+ });
384
+ }
385
+ }
386
+
387
+ static logError(error: SystemError): void {
388
+ SystemErrorHandler.errorLog.push(error);
389
+
390
+ // Keep log size manageable
391
+ if (SystemErrorHandler.errorLog.length > SystemErrorHandler.maxLogSize) {
392
+ SystemErrorHandler.errorLog.shift();
393
+ }
394
+ }
395
+
396
+ static async saveErrorLog(filePath?: string): Promise<void> {
397
+ const logPath = filePath || path.join(process.cwd(), '.baseguard-errors.log');
398
+
399
+ const logData = {
400
+ timestamp: new Date().toISOString(),
401
+ version: process.env.npm_package_version || 'unknown',
402
+ platform: process.platform,
403
+ nodeVersion: process.version,
404
+ errors: SystemErrorHandler.errorLog.map(error => ({
405
+ message: error.message,
406
+ code: error.code,
407
+ severity: error.severity,
408
+ context: error.context,
409
+ recoverable: error.recoverable
410
+ }))
411
+ };
412
+
413
+ try {
414
+ await fs.writeFile(logPath, JSON.stringify(logData, null, 2));
415
+ console.log(chalk.dim(`Error log saved to: ${logPath}`));
416
+ } catch (writeError) {
417
+ console.warn(chalk.yellow(`Failed to save error log: ${writeError.message}`));
418
+ }
419
+ }
420
+
421
+ static getErrorSummary(): {
422
+ total: number;
423
+ bySeverity: Record<string, number>;
424
+ byCode: Record<string, number>;
425
+ recoverable: number;
426
+ critical: number;
427
+ } {
428
+ const summary = {
429
+ total: SystemErrorHandler.errorLog.length,
430
+ bySeverity: { low: 0, medium: 0, high: 0, critical: 0 },
431
+ byCode: {} as Record<string, number>,
432
+ recoverable: 0,
433
+ critical: 0
434
+ };
435
+
436
+ SystemErrorHandler.errorLog.forEach(error => {
437
+ summary.bySeverity[error.severity]++;
438
+ summary.byCode[error.code] = (summary.byCode[error.code] || 0) + 1;
439
+ if (error.recoverable) summary.recoverable++;
440
+ if (error.severity === 'critical') summary.critical++;
441
+ });
442
+
443
+ return summary;
444
+ }
445
+
446
+ static registerRecoveryStrategy(errorCode: string, strategy: RecoveryStrategy): void {
447
+ if (!SystemErrorHandler.recoveryStrategies.has(errorCode)) {
448
+ SystemErrorHandler.recoveryStrategies.set(errorCode, []);
449
+ }
450
+ SystemErrorHandler.recoveryStrategies.get(errorCode)!.push(strategy);
451
+ }
452
+
453
+ static enableGracefulDegradation(enabled: boolean = true): void {
454
+ SystemErrorHandler.gracefulDegradationEnabled = enabled;
455
+ }
456
+
457
+ static setOfflineMode(offline: boolean = true): void {
458
+ SystemErrorHandler.offlineMode = offline;
459
+ if (offline) {
460
+ console.log(chalk.cyan('๐Ÿ”Œ Offline mode enabled - AI features disabled'));
461
+ }
462
+ }
463
+
464
+ static isOfflineMode(): boolean {
465
+ return SystemErrorHandler.offlineMode || process.env.BASEGUARD_OFFLINE === 'true';
466
+ }
467
+
468
+ static clearErrorLog(): void {
469
+ SystemErrorHandler.errorLog = [];
470
+ }
471
+
472
+ static async createCorruptionRecovery(configPath: string): Promise<boolean> {
473
+ try {
474
+ // Backup corrupted config
475
+ const backupPath = `${configPath}.backup.${Date.now()}`;
476
+ await fs.copyFile(configPath, backupPath);
477
+ console.log(chalk.yellow(`Corrupted config backed up to: ${backupPath}`));
478
+
479
+ // Create minimal working config
480
+ const minimalConfig = {
481
+ version: '1.0.0',
482
+ targets: [{ browser: 'chrome', minVersion: 'baseline' }],
483
+ apiKeys: { jules: null, gemini: null },
484
+ automation: { enabled: false }
485
+ };
486
+
487
+ await fs.writeFile(configPath, JSON.stringify(minimalConfig, null, 2));
488
+ console.log(chalk.green('โœ… Created minimal working configuration'));
489
+ return true;
490
+ } catch (error) {
491
+ console.error(chalk.red('Failed to recover from configuration corruption'));
492
+ return false;
493
+ }
494
+ }
495
+
496
+ static async handleProcessSignals(): Promise<void> {
497
+ const gracefulShutdown = async (signal: string) => {
498
+ console.log(chalk.yellow(`\nโš ๏ธ Received ${signal}, shutting down gracefully...`));
499
+
500
+ // Save error log before exit
501
+ try {
502
+ await SystemErrorHandler.saveErrorLog();
503
+ } catch (error) {
504
+ // Ignore errors during shutdown
505
+ }
506
+
507
+ // Show error summary if there were issues
508
+ const summary = SystemErrorHandler.getErrorSummary();
509
+ if (summary.total > 0) {
510
+ console.log(chalk.cyan(`\n๐Ÿ“Š Session summary: ${summary.total} errors encountered`));
511
+ if (summary.critical > 0) {
512
+ console.log(chalk.red(` ${summary.critical} critical errors require attention`));
513
+ }
514
+ }
515
+
516
+ process.exit(0);
517
+ };
518
+
519
+ process.on('SIGINT', () => gracefulShutdown('SIGINT'));
520
+ process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
521
+
522
+ // Handle uncaught exceptions
523
+ process.on('uncaughtException', (error) => {
524
+ const systemError = SystemErrorHandler.wrapError(error, {
525
+ operation: 'uncaught-exception',
526
+ details: { stack: error.stack }
527
+ });
528
+
529
+ SystemErrorHandler.displayError(systemError, { verbose: true });
530
+ SystemErrorHandler.saveErrorLog().finally(() => {
531
+ process.exit(1);
532
+ });
533
+ });
534
+
535
+ // Handle unhandled promise rejections
536
+ process.on('unhandledRejection', (reason, promise) => {
537
+ const systemError = SystemErrorHandler.wrapError(reason, {
538
+ operation: 'unhandled-rejection',
539
+ details: { promise: promise.toString() }
540
+ });
541
+
542
+ SystemErrorHandler.displayError(systemError, { verbose: true });
543
+ SystemErrorHandler.saveErrorLog().finally(() => {
544
+ process.exit(1);
545
+ });
546
+ });
547
+ }
548
+
549
+ private static sleep(ms: number): Promise<void> {
550
+ return new Promise(resolve => setTimeout(resolve, ms));
551
+ }
552
+
553
+ // Initialize default recovery strategies
554
+ static initializeRecoveryStrategies(): void {
555
+ // File not found recovery
556
+ SystemErrorHandler.registerRecoveryStrategy('FILE_NOT_FOUND', {
557
+ name: 'Create missing directories',
558
+ description: 'Create missing parent directories',
559
+ priority: 3,
560
+ execute: async () => {
561
+ try {
562
+ // Try to create parent directories for common paths
563
+ const commonPaths = ['src', 'app', 'components', 'pages', '.baseguard'];
564
+ for (const dirPath of commonPaths) {
565
+ try {
566
+ await fs.mkdir(dirPath, { recursive: true });
567
+ } catch (error) {
568
+ // Ignore if directory already exists
569
+ }
570
+ }
571
+ return true;
572
+ } catch (error) {
573
+ return false;
574
+ }
575
+ }
576
+ });
577
+
578
+ // Network error recovery
579
+ SystemErrorHandler.registerRecoveryStrategy('NETWORK_ERROR', {
580
+ name: 'Switch to offline mode',
581
+ description: 'Continue with offline-only features',
582
+ priority: 2,
583
+ execute: async () => {
584
+ console.log(chalk.cyan('๐Ÿ”Œ Switching to offline mode...'));
585
+ SystemErrorHandler.setOfflineMode(true);
586
+ process.env.BASEGUARD_OFFLINE = 'true';
587
+ return true;
588
+ }
589
+ });
590
+
591
+ // API error recovery
592
+ SystemErrorHandler.registerRecoveryStrategy('API_ERROR', {
593
+ name: 'Use cached responses',
594
+ description: 'Fall back to cached API responses',
595
+ priority: 1,
596
+ execute: async () => {
597
+ try {
598
+ // Check if cache directory exists and has content
599
+ const cacheDir = path.join(process.cwd(), '.baseguard', 'cache');
600
+ const cacheExists = await fs.access(cacheDir).then(() => true).catch(() => false);
601
+
602
+ if (cacheExists) {
603
+ const files = await fs.readdir(cacheDir);
604
+ if (files.length > 0) {
605
+ console.log(chalk.cyan('๐Ÿ“ฆ Using cached API responses'));
606
+ return true;
607
+ }
608
+ }
609
+
610
+ // Enable offline mode as fallback
611
+ SystemErrorHandler.setOfflineMode(true);
612
+ return true;
613
+ } catch (error) {
614
+ return false;
615
+ }
616
+ }
617
+ });
618
+
619
+ // Syntax error recovery
620
+ SystemErrorHandler.registerRecoveryStrategy('SYNTAX_ERROR', {
621
+ name: 'Skip malformed file',
622
+ description: 'Skip the file with syntax errors and continue',
623
+ priority: 1,
624
+ execute: async () => {
625
+ console.log(chalk.cyan('โญ๏ธ Skipping malformed file and continuing...'));
626
+ return true;
627
+ }
628
+ });
629
+
630
+ // Configuration error recovery
631
+ SystemErrorHandler.registerRecoveryStrategy('CONFIGURATION_ERROR', {
632
+ name: 'Create minimal config',
633
+ description: 'Create a minimal working configuration',
634
+ priority: 2,
635
+ execute: async () => {
636
+ const configPath = path.join(process.cwd(), '.baseguardrc.json');
637
+ return await SystemErrorHandler.createCorruptionRecovery(configPath);
638
+ }
639
+ });
640
+
641
+ // Parser error recovery
642
+ SystemErrorHandler.registerRecoveryStrategy('PARSER_ERROR', {
643
+ name: 'Use fallback parser',
644
+ description: 'Try with a simpler parsing strategy',
645
+ priority: 1,
646
+ execute: async () => {
647
+ console.log(chalk.cyan('๐Ÿ”„ Using fallback parser strategy...'));
648
+ return true;
649
+ }
650
+ });
651
+
652
+ // Memory error recovery
653
+ SystemErrorHandler.registerRecoveryStrategy('OUT_OF_MEMORY', {
654
+ name: 'Reduce processing batch size',
655
+ description: 'Process files in smaller batches to reduce memory usage',
656
+ priority: 3,
657
+ execute: async () => {
658
+ console.log(chalk.cyan('๐Ÿง  Reducing memory usage by processing smaller batches...'));
659
+ process.env.BASEGUARD_BATCH_SIZE = '10'; // Reduce from default
660
+ return true;
661
+ }
662
+ });
663
+
664
+ // Too many files recovery
665
+ SystemErrorHandler.registerRecoveryStrategy('TOO_MANY_FILES', {
666
+ name: 'Limit file processing',
667
+ description: 'Reduce the number of files processed simultaneously',
668
+ priority: 2,
669
+ execute: async () => {
670
+ console.log(chalk.cyan('๐Ÿ“ Limiting concurrent file processing...'));
671
+ process.env.BASEGUARD_MAX_FILES = '100'; // Reduce from default
672
+ return true;
673
+ }
674
+ });
675
+
676
+ // Timeout error recovery
677
+ SystemErrorHandler.registerRecoveryStrategy('TIMEOUT_ERROR', {
678
+ name: 'Increase timeout and retry',
679
+ description: 'Increase timeout settings and retry the operation',
680
+ priority: 2,
681
+ execute: async () => {
682
+ console.log(chalk.cyan('โฑ๏ธ Increasing timeout settings...'));
683
+ process.env.BASEGUARD_TIMEOUT = '60000'; // 60 seconds
684
+ return true;
685
+ }
686
+ });
687
+
688
+ // Permission denied recovery
689
+ SystemErrorHandler.registerRecoveryStrategy('PERMISSION_DENIED', {
690
+ name: 'Skip protected files',
691
+ description: 'Skip files that cannot be accessed and continue',
692
+ priority: 1,
693
+ execute: async () => {
694
+ console.log(chalk.cyan('๐Ÿ”’ Skipping protected files and continuing...'));
695
+ return true;
696
+ }
697
+ });
698
+
699
+ // Disk full recovery
700
+ SystemErrorHandler.registerRecoveryStrategy('DISK_FULL', {
701
+ name: 'Clean temporary files',
702
+ description: 'Clean up temporary files to free space',
703
+ priority: 3,
704
+ execute: async () => {
705
+ try {
706
+ console.log(chalk.cyan('๐Ÿงน Cleaning temporary files...'));
707
+
708
+ // Clean BaseGuard temp files
709
+ const tempDir = path.join(process.cwd(), '.baseguard', 'temp');
710
+ try {
711
+ await fs.rm(tempDir, { recursive: true, force: true });
712
+ await fs.mkdir(tempDir, { recursive: true });
713
+ } catch (error) {
714
+ // Ignore if temp dir doesn't exist
715
+ }
716
+
717
+ // Clean system temp files (be careful here)
718
+ const systemTemp = process.env.TMPDIR || process.env.TEMP || '/tmp';
719
+ const baseguardTempPattern = path.join(systemTemp, 'baseguard-*');
720
+
721
+ try {
722
+ const { glob } = await import('glob');
723
+ const tempFiles = await glob(baseguardTempPattern);
724
+ for (const file of tempFiles) {
725
+ try {
726
+ await fs.rm(file, { recursive: true, force: true });
727
+ } catch (error) {
728
+ // Ignore individual file errors
729
+ }
730
+ }
731
+ } catch (error) {
732
+ // Ignore if glob fails
733
+ }
734
+
735
+ return true;
736
+ } catch (error) {
737
+ return false;
738
+ }
739
+ }
740
+ });
741
+ }
742
+ }
743
+
744
+ // Initialize recovery strategies and signal handlers when module loads
745
+ SystemErrorHandler.initializeRecoveryStrategies();
746
+ SystemErrorHandler.handleProcessSignals();