erosolar-cli 1.7.373 → 1.7.379

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 (99) hide show
  1. package/dist/capabilities/agentSpawningCapability.d.ts.map +1 -1
  2. package/dist/capabilities/agentSpawningCapability.js +29 -1
  3. package/dist/capabilities/agentSpawningCapability.js.map +1 -1
  4. package/dist/contracts/agent-schemas.json +105 -0
  5. package/dist/core/alphaZeroModular.d.ts +186 -0
  6. package/dist/core/alphaZeroModular.d.ts.map +1 -0
  7. package/dist/core/alphaZeroModular.js +755 -0
  8. package/dist/core/alphaZeroModular.js.map +1 -0
  9. package/dist/core/alphaZeroOrchestrator.d.ts +140 -0
  10. package/dist/core/alphaZeroOrchestrator.d.ts.map +1 -0
  11. package/dist/core/alphaZeroOrchestrator.js +418 -0
  12. package/dist/core/alphaZeroOrchestrator.js.map +1 -0
  13. package/dist/core/checkpoint.d.ts +76 -0
  14. package/dist/core/checkpoint.d.ts.map +1 -0
  15. package/dist/core/checkpoint.js +278 -0
  16. package/dist/core/checkpoint.js.map +1 -0
  17. package/dist/core/claudeCodeFeatures.d.ts +64 -0
  18. package/dist/core/claudeCodeFeatures.d.ts.map +1 -0
  19. package/dist/core/claudeCodeFeatures.js +163 -0
  20. package/dist/core/claudeCodeFeatures.js.map +1 -0
  21. package/dist/core/costTracker.d.ts +87 -0
  22. package/dist/core/costTracker.d.ts.map +1 -0
  23. package/dist/core/costTracker.js +285 -0
  24. package/dist/core/costTracker.js.map +1 -0
  25. package/dist/core/failureRecovery.d.ts +122 -0
  26. package/dist/core/failureRecovery.d.ts.map +1 -0
  27. package/dist/core/failureRecovery.js +477 -0
  28. package/dist/core/failureRecovery.js.map +1 -0
  29. package/dist/core/intelligentTestFlows.d.ts +126 -0
  30. package/dist/core/intelligentTestFlows.d.ts.map +1 -0
  31. package/dist/core/intelligentTestFlows.js +678 -0
  32. package/dist/core/intelligentTestFlows.js.map +1 -0
  33. package/dist/core/learningPersistence.d.ts +145 -0
  34. package/dist/core/learningPersistence.d.ts.map +1 -0
  35. package/dist/core/learningPersistence.js +477 -0
  36. package/dist/core/learningPersistence.js.map +1 -0
  37. package/dist/core/memorySystem.d.ts +67 -0
  38. package/dist/core/memorySystem.d.ts.map +1 -0
  39. package/dist/core/memorySystem.js +334 -0
  40. package/dist/core/memorySystem.js.map +1 -0
  41. package/dist/core/outputStyles.d.ts +48 -0
  42. package/dist/core/outputStyles.d.ts.map +1 -0
  43. package/dist/core/outputStyles.js +270 -0
  44. package/dist/core/outputStyles.js.map +1 -0
  45. package/dist/core/selfEvolution.d.ts +155 -0
  46. package/dist/core/selfEvolution.d.ts.map +1 -0
  47. package/dist/core/selfEvolution.js +1000 -0
  48. package/dist/core/selfEvolution.js.map +1 -0
  49. package/dist/core/selfImprovement.d.ts +141 -0
  50. package/dist/core/selfImprovement.d.ts.map +1 -0
  51. package/dist/core/selfImprovement.js +700 -0
  52. package/dist/core/selfImprovement.js.map +1 -0
  53. package/dist/core/updateManager.d.ts +62 -0
  54. package/dist/core/updateManager.d.ts.map +1 -0
  55. package/dist/core/updateManager.js +266 -0
  56. package/dist/core/updateManager.js.map +1 -0
  57. package/dist/shell/interactiveShell.d.ts +67 -0
  58. package/dist/shell/interactiveShell.d.ts.map +1 -1
  59. package/dist/shell/interactiveShell.js +1546 -3
  60. package/dist/shell/interactiveShell.js.map +1 -1
  61. package/dist/shell/keyboardShortcuts.d.ts +53 -0
  62. package/dist/shell/keyboardShortcuts.d.ts.map +1 -0
  63. package/dist/shell/keyboardShortcuts.js +163 -0
  64. package/dist/shell/keyboardShortcuts.js.map +1 -0
  65. package/dist/shell/terminalInput.d.ts +51 -1
  66. package/dist/shell/terminalInput.d.ts.map +1 -1
  67. package/dist/shell/terminalInput.js +223 -12
  68. package/dist/shell/terminalInput.js.map +1 -1
  69. package/dist/shell/terminalInputAdapter.d.ts +7 -1
  70. package/dist/shell/terminalInputAdapter.d.ts.map +1 -1
  71. package/dist/shell/terminalInputAdapter.js +7 -0
  72. package/dist/shell/terminalInputAdapter.js.map +1 -1
  73. package/dist/shell/vimMode.d.ts +66 -0
  74. package/dist/shell/vimMode.d.ts.map +1 -0
  75. package/dist/shell/vimMode.js +434 -0
  76. package/dist/shell/vimMode.js.map +1 -0
  77. package/dist/subagents/parallelAgentManager.d.ts +99 -0
  78. package/dist/subagents/parallelAgentManager.d.ts.map +1 -0
  79. package/dist/subagents/parallelAgentManager.js +249 -0
  80. package/dist/subagents/parallelAgentManager.js.map +1 -0
  81. package/dist/subagents/taskRunner.d.ts +6 -1
  82. package/dist/subagents/taskRunner.d.ts.map +1 -1
  83. package/dist/subagents/taskRunner.js +57 -2
  84. package/dist/subagents/taskRunner.js.map +1 -1
  85. package/dist/tools/planningTools.d.ts +9 -0
  86. package/dist/tools/planningTools.d.ts.map +1 -1
  87. package/dist/tools/planningTools.js +90 -0
  88. package/dist/tools/planningTools.js.map +1 -1
  89. package/dist/ui/display.d.ts +5 -0
  90. package/dist/ui/display.d.ts.map +1 -1
  91. package/dist/ui/display.js +14 -0
  92. package/dist/ui/display.js.map +1 -1
  93. package/dist/ui/shortcutsHelp.d.ts.map +1 -1
  94. package/dist/ui/shortcutsHelp.js +43 -12
  95. package/dist/ui/shortcutsHelp.js.map +1 -1
  96. package/dist/ui/unified/layout.d.ts.map +1 -1
  97. package/dist/ui/unified/layout.js +9 -9
  98. package/dist/ui/unified/layout.js.map +1 -1
  99. package/package.json +1 -1
@@ -0,0 +1,678 @@
1
+ /**
2
+ * Intelligent Test Flow Generator
3
+ *
4
+ * Creates highly complex, intelligent test flows for runtime verification:
5
+ * - Edge case detection and generation
6
+ * - State machine exploration
7
+ * - Fuzzing with smart mutations
8
+ * - Regression test generation
9
+ * - UI interaction simulation
10
+ *
11
+ * Principal Investigator: Bo Shang
12
+ */
13
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync } from 'node:fs';
14
+ import { join, basename, relative } from 'node:path';
15
+ import { homedir } from 'node:os';
16
+ // ============================================================================
17
+ // CONSTANTS
18
+ // ============================================================================
19
+ const TEST_FLOWS_DIR = join(homedir(), '.erosolar', 'test-flows');
20
+ const BUG_REPORTS_DIR = join(homedir(), '.erosolar', 'bug-reports');
21
+ const UI_UPDATES_FILE = join(homedir(), '.erosolar', 'ui-updates.json');
22
+ // Edge case categories
23
+ const EDGE_CASE_CATEGORIES = {
24
+ boundary: ['empty', 'null', 'undefined', 'zero', 'negative', 'max-int', 'min-int', 'overflow'],
25
+ string: ['empty-string', 'whitespace', 'unicode', 'emoji', 'very-long', 'special-chars', 'html-injection', 'sql-injection'],
26
+ array: ['empty-array', 'single-item', 'large-array', 'nested', 'circular-ref', 'mixed-types'],
27
+ async: ['timeout', 'race-condition', 'concurrent', 'retry-exhausted', 'network-error'],
28
+ state: ['uninitialized', 'partially-initialized', 'corrupted', 'stale', 'locked'],
29
+ file: ['not-found', 'permission-denied', 'corrupted', 'locked', 'symlink', 'very-large'],
30
+ };
31
+ // ============================================================================
32
+ // EDGE CASE GENERATOR
33
+ // ============================================================================
34
+ /**
35
+ * Generate edge cases for a given function/component
36
+ */
37
+ export function generateEdgeCases(targetName, paramTypes) {
38
+ const edgeCases = [];
39
+ let idCounter = 0;
40
+ for (const [param, type] of Object.entries(paramTypes)) {
41
+ const cases = getEdgeCasesForType(type);
42
+ for (const edgeCase of cases) {
43
+ edgeCases.push({
44
+ id: `${targetName}_edge_${++idCounter}`,
45
+ category: edgeCase.category,
46
+ description: `${param}: ${edgeCase.description}`,
47
+ input: { [param]: edgeCase.value },
48
+ expectedBehavior: edgeCase.expectedBehavior,
49
+ severity: edgeCase.severity,
50
+ detected: false,
51
+ });
52
+ }
53
+ }
54
+ return edgeCases;
55
+ }
56
+ function getEdgeCasesForType(type) {
57
+ const cases = [];
58
+ switch (type.toLowerCase()) {
59
+ case 'string':
60
+ cases.push({ category: 'boundary', description: 'empty string', value: '', expectedBehavior: 'Handle gracefully', severity: 'medium' }, { category: 'boundary', description: 'null', value: null, expectedBehavior: 'Reject or default', severity: 'high' }, { category: 'string', description: 'whitespace only', value: ' ', expectedBehavior: 'Trim or reject', severity: 'low' }, { category: 'string', description: 'unicode', value: '你好世界🌍', expectedBehavior: 'Handle correctly', severity: 'medium' }, { category: 'string', description: 'very long', value: 'x'.repeat(10000), expectedBehavior: 'Truncate or reject', severity: 'high' }, { category: 'string', description: 'special chars', value: '<script>alert(1)</script>', expectedBehavior: 'Escape or reject', severity: 'critical' }, { category: 'string', description: 'path traversal', value: '../../../etc/passwd', expectedBehavior: 'Reject', severity: 'critical' });
61
+ break;
62
+ case 'number':
63
+ cases.push({ category: 'boundary', description: 'zero', value: 0, expectedBehavior: 'Handle zero case', severity: 'medium' }, { category: 'boundary', description: 'negative', value: -1, expectedBehavior: 'Validate range', severity: 'medium' }, { category: 'boundary', description: 'max safe int', value: Number.MAX_SAFE_INTEGER, expectedBehavior: 'Handle large numbers', severity: 'high' }, { category: 'boundary', description: 'infinity', value: Infinity, expectedBehavior: 'Reject or handle', severity: 'high' }, { category: 'boundary', description: 'NaN', value: NaN, expectedBehavior: 'Reject NaN', severity: 'high' }, { category: 'boundary', description: 'float precision', value: 0.1 + 0.2, expectedBehavior: 'Handle float precision', severity: 'low' });
64
+ break;
65
+ case 'array':
66
+ cases.push({ category: 'array', description: 'empty array', value: [], expectedBehavior: 'Handle empty', severity: 'medium' }, { category: 'array', description: 'single item', value: [1], expectedBehavior: 'Handle single', severity: 'low' }, { category: 'array', description: 'large array', value: Array(10000).fill(0), expectedBehavior: 'Handle efficiently', severity: 'high' }, { category: 'array', description: 'null in array', value: [1, null, 3], expectedBehavior: 'Filter or handle', severity: 'medium' }, { category: 'array', description: 'undefined in array', value: [1, undefined, 3], expectedBehavior: 'Filter or handle', severity: 'medium' });
67
+ break;
68
+ case 'object':
69
+ cases.push({ category: 'boundary', description: 'null object', value: null, expectedBehavior: 'Reject or default', severity: 'high' }, { category: 'boundary', description: 'empty object', value: {}, expectedBehavior: 'Handle empty', severity: 'medium' }, { category: 'state', description: 'missing required', value: { partial: true }, expectedBehavior: 'Validate schema', severity: 'high' }, { category: 'array', description: 'deeply nested', value: { a: { b: { c: { d: { e: {} } } } } }, expectedBehavior: 'Handle depth', severity: 'medium' });
70
+ break;
71
+ default:
72
+ cases.push({ category: 'boundary', description: 'null', value: null, expectedBehavior: 'Handle null', severity: 'high' }, { category: 'boundary', description: 'undefined', value: undefined, expectedBehavior: 'Handle undefined', severity: 'high' });
73
+ }
74
+ return cases;
75
+ }
76
+ // ============================================================================
77
+ // TEST FLOW GENERATOR
78
+ // ============================================================================
79
+ /**
80
+ * Generate intelligent test flows for a component
81
+ */
82
+ export function generateTestFlows(workingDir, targetPath) {
83
+ const flows = [];
84
+ const fullPath = join(workingDir, targetPath);
85
+ if (!existsSync(fullPath)) {
86
+ return flows;
87
+ }
88
+ const content = readFileSync(fullPath, 'utf-8');
89
+ const fileName = basename(targetPath, '.ts');
90
+ // Extract function signatures
91
+ const functionMatches = content.matchAll(/(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(([^)]*)\)/g);
92
+ for (const match of functionMatches) {
93
+ const funcName = match[1];
94
+ const params = parseParams(match[2] ?? '');
95
+ // Generate basic flow
96
+ flows.push(createBasicTestFlow(fileName, funcName, params));
97
+ // Generate edge case flows
98
+ flows.push(...createEdgeCaseFlows(fileName, funcName, params));
99
+ // Generate stress test flow
100
+ if (params.length > 0) {
101
+ flows.push(createStressTestFlow(fileName, funcName, params));
102
+ }
103
+ }
104
+ // Extract class methods
105
+ const classMatches = content.matchAll(/(?:async\s+)?(\w+)\s*\(([^)]*)\)\s*(?::\s*[^{]+)?\s*\{/g);
106
+ for (const match of classMatches) {
107
+ const methodName = match[1];
108
+ if (methodName === 'constructor' || methodName.startsWith('_'))
109
+ continue;
110
+ const params = parseParams(match[2] ?? '');
111
+ flows.push(createBasicTestFlow(fileName, methodName, params));
112
+ }
113
+ return flows;
114
+ }
115
+ function parseParams(paramStr) {
116
+ if (!paramStr.trim())
117
+ return [];
118
+ const params = [];
119
+ const parts = paramStr.split(',');
120
+ for (const part of parts) {
121
+ const match = part.match(/(\w+)\s*[?]?\s*:\s*([^=,]+)/);
122
+ if (match) {
123
+ params.push({
124
+ name: match[1].trim(),
125
+ type: match[2].trim(),
126
+ });
127
+ }
128
+ }
129
+ return params;
130
+ }
131
+ function createBasicTestFlow(fileName, funcName, params) {
132
+ const steps = [];
133
+ // Setup step
134
+ steps.push({
135
+ order: 1,
136
+ action: 'setup',
137
+ input: {},
138
+ assertions: [],
139
+ });
140
+ // Execution step
141
+ const input = {};
142
+ for (const param of params) {
143
+ input[param.name] = getDefaultValue(param.type);
144
+ }
145
+ steps.push({
146
+ order: 2,
147
+ action: `call ${funcName}`,
148
+ input,
149
+ assertions: [
150
+ { type: 'resolves', target: 'result', message: 'Function should complete' },
151
+ ],
152
+ });
153
+ // Verification step
154
+ steps.push({
155
+ order: 3,
156
+ action: 'verify',
157
+ assertions: [
158
+ { type: 'truthy', target: 'result', message: 'Result should be defined' },
159
+ ],
160
+ });
161
+ return {
162
+ id: `${fileName}_${funcName}_basic`,
163
+ name: `${funcName} - Basic Test`,
164
+ description: `Verify basic functionality of ${funcName}`,
165
+ category: 'unit',
166
+ steps,
167
+ preconditions: ['Module is loaded'],
168
+ postconditions: ['No side effects'],
169
+ expectedOutcome: 'Function executes successfully',
170
+ complexity: 'simple',
171
+ priority: 50,
172
+ generatedAt: new Date().toISOString(),
173
+ };
174
+ }
175
+ function createEdgeCaseFlows(fileName, funcName, params) {
176
+ const flows = [];
177
+ for (const param of params) {
178
+ const edgeCases = getEdgeCasesForType(param.type);
179
+ for (const edge of edgeCases.slice(0, 3)) { // Limit to top 3 per param
180
+ const steps = [
181
+ {
182
+ order: 1,
183
+ action: `call ${funcName} with ${edge.description}`,
184
+ input: { [param.name]: edge.value },
185
+ assertions: [
186
+ { type: edge.severity === 'critical' ? 'throws' : 'resolves', target: 'result' },
187
+ ],
188
+ },
189
+ ];
190
+ flows.push({
191
+ id: `${fileName}_${funcName}_edge_${param.name}_${edge.category}`,
192
+ name: `${funcName} - Edge: ${edge.description}`,
193
+ description: `Test ${funcName} with ${edge.description} for ${param.name}`,
194
+ category: 'edge-case',
195
+ steps,
196
+ preconditions: [],
197
+ postconditions: [],
198
+ expectedOutcome: edge.expectedBehavior,
199
+ complexity: 'moderate',
200
+ priority: edge.severity === 'critical' ? 90 : edge.severity === 'high' ? 70 : 50,
201
+ generatedAt: new Date().toISOString(),
202
+ });
203
+ }
204
+ }
205
+ return flows;
206
+ }
207
+ function createStressTestFlow(fileName, funcName, params) {
208
+ const iterations = 1000;
209
+ const steps = [];
210
+ for (let i = 0; i < 5; i++) {
211
+ const input = {};
212
+ for (const param of params) {
213
+ input[param.name] = getRandomValue(param.type);
214
+ }
215
+ steps.push({
216
+ order: i + 1,
217
+ action: `stress iteration ${i + 1}`,
218
+ input,
219
+ timeout: 5000,
220
+ assertions: [
221
+ { type: 'resolves', target: 'result' },
222
+ ],
223
+ });
224
+ }
225
+ return {
226
+ id: `${fileName}_${funcName}_stress`,
227
+ name: `${funcName} - Stress Test`,
228
+ description: `Stress test ${funcName} with random inputs`,
229
+ category: 'stress',
230
+ steps,
231
+ preconditions: [],
232
+ postconditions: ['No memory leaks', 'Consistent performance'],
233
+ expectedOutcome: 'Function handles load without degradation',
234
+ complexity: 'extreme',
235
+ priority: 60,
236
+ generatedAt: new Date().toISOString(),
237
+ };
238
+ }
239
+ function getDefaultValue(type) {
240
+ switch (type.toLowerCase()) {
241
+ case 'string': return 'test';
242
+ case 'number': return 1;
243
+ case 'boolean': return true;
244
+ case 'array': return [];
245
+ case 'object': return {};
246
+ default: return null;
247
+ }
248
+ }
249
+ function getRandomValue(type) {
250
+ switch (type.toLowerCase()) {
251
+ case 'string': return Math.random().toString(36).substring(7);
252
+ case 'number': return Math.floor(Math.random() * 1000);
253
+ case 'boolean': return Math.random() > 0.5;
254
+ case 'array': return Array(Math.floor(Math.random() * 10)).fill(0).map(() => Math.random());
255
+ default: return null;
256
+ }
257
+ }
258
+ // ============================================================================
259
+ // BUG DETECTION
260
+ // ============================================================================
261
+ /**
262
+ * Analyze code for potential bugs
263
+ */
264
+ export function detectBugs(workingDir) {
265
+ const bugs = [];
266
+ const srcDir = join(workingDir, 'src');
267
+ if (!existsSync(srcDir))
268
+ return bugs;
269
+ const files = findFilesRecursive(srcDir, ['.ts']);
270
+ for (const file of files) {
271
+ const content = readFileSync(file, 'utf-8');
272
+ const relativePath = relative(workingDir, file);
273
+ // Detect potential bugs
274
+ bugs.push(...detectNullDereferencing(content, relativePath));
275
+ bugs.push(...detectAsyncIssues(content, relativePath));
276
+ bugs.push(...detectTypeCoercion(content, relativePath));
277
+ bugs.push(...detectResourceLeaks(content, relativePath));
278
+ bugs.push(...detectLogicErrors(content, relativePath));
279
+ }
280
+ // Sort by severity
281
+ const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
282
+ bugs.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);
283
+ return bugs;
284
+ }
285
+ function detectNullDereferencing(content, file) {
286
+ const bugs = [];
287
+ const lines = content.split('\n');
288
+ for (let i = 0; i < lines.length; i++) {
289
+ const line = lines[i];
290
+ // Pattern: accessing property without null check
291
+ if (line.match(/\w+\.\w+/) && !line.includes('?.') && !line.includes('!.')) {
292
+ // Check if there's a null check before this line
293
+ const prevLines = lines.slice(Math.max(0, i - 3), i).join('\n');
294
+ if (!prevLines.includes('!== null') && !prevLines.includes('!== undefined') &&
295
+ !prevLines.includes('!= null') && !prevLines.includes('if (')) {
296
+ // Could be a potential null dereference
297
+ const match = line.match(/(\w+)\.(\w+)/);
298
+ if (match && !['console', 'Math', 'JSON', 'Object', 'Array', 'String', 'Number', 'Date', 'process'].includes(match[1])) {
299
+ bugs.push({
300
+ id: `null_deref_${file}_${i + 1}`,
301
+ severity: 'medium',
302
+ type: 'edge-case',
303
+ title: 'Potential null dereference',
304
+ description: `Accessing ${match[1]}.${match[2]} without null check`,
305
+ reproSteps: ['Call function with null/undefined input'],
306
+ expectedBehavior: 'Should handle null gracefully',
307
+ actualBehavior: 'May throw TypeError',
308
+ file,
309
+ line: i + 1,
310
+ suggestedFix: `Add optional chaining: ${match[1]}?.${match[2]}`,
311
+ detectedAt: new Date().toISOString(),
312
+ });
313
+ }
314
+ }
315
+ }
316
+ }
317
+ return bugs.slice(0, 5); // Limit to prevent flooding
318
+ }
319
+ function detectAsyncIssues(content, file) {
320
+ const bugs = [];
321
+ const lines = content.split('\n');
322
+ for (let i = 0; i < lines.length; i++) {
323
+ const line = lines[i];
324
+ // Missing await on async call
325
+ if (line.match(/[^a]\w+Async\s*\(/) && !line.includes('await') && !line.includes('return')) {
326
+ bugs.push({
327
+ id: `missing_await_${file}_${i + 1}`,
328
+ severity: 'high',
329
+ type: 'logic',
330
+ title: 'Missing await on async function',
331
+ description: 'Async function called without await',
332
+ reproSteps: ['Call the function and check result'],
333
+ expectedBehavior: 'Should await async result',
334
+ actualBehavior: 'Returns Promise instead of resolved value',
335
+ file,
336
+ line: i + 1,
337
+ suggestedFix: 'Add await keyword',
338
+ detectedAt: new Date().toISOString(),
339
+ });
340
+ }
341
+ // Unhandled promise rejection
342
+ if (line.includes('.then(') && !content.slice(0, content.indexOf(line) + line.length + 200).includes('.catch(')) {
343
+ bugs.push({
344
+ id: `unhandled_promise_${file}_${i + 1}`,
345
+ severity: 'medium',
346
+ type: 'edge-case',
347
+ title: 'Unhandled promise rejection',
348
+ description: 'Promise chain without .catch() handler',
349
+ reproSteps: ['Cause the promise to reject'],
350
+ expectedBehavior: 'Should handle rejection',
351
+ actualBehavior: 'Unhandled promise rejection',
352
+ file,
353
+ line: i + 1,
354
+ suggestedFix: 'Add .catch() handler or use try/catch with await',
355
+ detectedAt: new Date().toISOString(),
356
+ });
357
+ }
358
+ }
359
+ return bugs.slice(0, 3);
360
+ }
361
+ function detectTypeCoercion(content, file) {
362
+ const bugs = [];
363
+ const lines = content.split('\n');
364
+ for (let i = 0; i < lines.length; i++) {
365
+ const line = lines[i];
366
+ // Loose equality
367
+ if (line.match(/[^!=]==[^=]/) && !line.includes('===')) {
368
+ bugs.push({
369
+ id: `loose_eq_${file}_${i + 1}`,
370
+ severity: 'low',
371
+ type: 'logic',
372
+ title: 'Loose equality comparison',
373
+ description: 'Using == instead of ===',
374
+ reproSteps: ['Compare values of different types'],
375
+ expectedBehavior: 'Strict type comparison',
376
+ actualBehavior: 'Type coercion may cause unexpected matches',
377
+ file,
378
+ line: i + 1,
379
+ suggestedFix: 'Use === for strict equality',
380
+ detectedAt: new Date().toISOString(),
381
+ });
382
+ }
383
+ }
384
+ return bugs.slice(0, 3);
385
+ }
386
+ function detectResourceLeaks(content, file) {
387
+ const bugs = [];
388
+ // File handle leaks
389
+ if (content.includes('openSync') && !content.includes('closeSync')) {
390
+ bugs.push({
391
+ id: `file_leak_${file}`,
392
+ severity: 'high',
393
+ type: 'performance',
394
+ title: 'Potential file handle leak',
395
+ description: 'File opened but not explicitly closed',
396
+ reproSteps: ['Call function repeatedly'],
397
+ expectedBehavior: 'File handles should be closed',
398
+ actualBehavior: 'May leak file handles',
399
+ file,
400
+ suggestedFix: 'Use try/finally or fs promises with proper cleanup',
401
+ detectedAt: new Date().toISOString(),
402
+ });
403
+ }
404
+ // Timer leaks
405
+ if ((content.includes('setInterval') || content.includes('setTimeout')) &&
406
+ !content.includes('clearInterval') && !content.includes('clearTimeout')) {
407
+ bugs.push({
408
+ id: `timer_leak_${file}`,
409
+ severity: 'medium',
410
+ type: 'performance',
411
+ title: 'Potential timer leak',
412
+ description: 'Timer set but never cleared',
413
+ reproSteps: ['Create and destroy component repeatedly'],
414
+ expectedBehavior: 'Timers should be cleared on cleanup',
415
+ actualBehavior: 'May accumulate timers',
416
+ file,
417
+ suggestedFix: 'Store timer ID and clear on cleanup',
418
+ detectedAt: new Date().toISOString(),
419
+ });
420
+ }
421
+ return bugs;
422
+ }
423
+ function detectLogicErrors(content, file) {
424
+ const bugs = [];
425
+ const lines = content.split('\n');
426
+ for (let i = 0; i < lines.length; i++) {
427
+ const line = lines[i];
428
+ // Always true/false conditions
429
+ if (line.match(/if\s*\(\s*true\s*\)/) || line.match(/if\s*\(\s*false\s*\)/)) {
430
+ bugs.push({
431
+ id: `const_cond_${file}_${i + 1}`,
432
+ severity: 'medium',
433
+ type: 'logic',
434
+ title: 'Constant condition',
435
+ description: 'Condition is always true or false',
436
+ reproSteps: ['Review the code'],
437
+ expectedBehavior: 'Dynamic condition',
438
+ actualBehavior: 'Dead code or always executed',
439
+ file,
440
+ line: i + 1,
441
+ suggestedFix: 'Remove dead code or fix condition',
442
+ detectedAt: new Date().toISOString(),
443
+ });
444
+ }
445
+ // Assignment in condition
446
+ if (line.match(/if\s*\([^=]*[^!=<>]=[^=][^)]*\)/)) {
447
+ bugs.push({
448
+ id: `assign_cond_${file}_${i + 1}`,
449
+ severity: 'high',
450
+ type: 'logic',
451
+ title: 'Assignment in condition',
452
+ description: 'Using = instead of == or === in condition',
453
+ reproSteps: ['Review the code'],
454
+ expectedBehavior: 'Comparison',
455
+ actualBehavior: 'Assignment (always truthy for non-null)',
456
+ file,
457
+ line: i + 1,
458
+ suggestedFix: 'Use === for comparison',
459
+ detectedAt: new Date().toISOString(),
460
+ });
461
+ }
462
+ }
463
+ return bugs.slice(0, 3);
464
+ }
465
+ // ============================================================================
466
+ // UI UPDATE DETECTION
467
+ // ============================================================================
468
+ /**
469
+ * Detect needed UI updates
470
+ */
471
+ export function detectUIUpdates(workingDir) {
472
+ const updates = [];
473
+ const uiDir = join(workingDir, 'src/ui');
474
+ if (!existsSync(uiDir))
475
+ return updates;
476
+ const files = findFilesRecursive(uiDir, ['.ts']);
477
+ for (const file of files) {
478
+ const content = readFileSync(file, 'utf-8');
479
+ const relativePath = relative(workingDir, file);
480
+ // Detect accessibility issues
481
+ if (content.includes('console.log') && file.includes('display')) {
482
+ updates.push({
483
+ component: basename(file, '.ts'),
484
+ type: 'accessibility',
485
+ description: 'Console output may not be screen-reader friendly',
486
+ before: 'console.log(...)',
487
+ after: 'Use structured output with ARIA labels',
488
+ file: relativePath,
489
+ });
490
+ }
491
+ // Detect hardcoded colors
492
+ const colorMatches = content.match(/['"`]#[0-9a-fA-F]{6}['"`]/g);
493
+ if (colorMatches && colorMatches.length > 3) {
494
+ updates.push({
495
+ component: basename(file, '.ts'),
496
+ type: 'style',
497
+ description: 'Hardcoded colors - should use theme',
498
+ before: colorMatches.slice(0, 3).join(', '),
499
+ after: 'Use theme.colors.* instead',
500
+ file: relativePath,
501
+ });
502
+ }
503
+ // Detect missing error handling in UI
504
+ if (content.includes('try') && !content.includes('showError') && !content.includes('displayError')) {
505
+ updates.push({
506
+ component: basename(file, '.ts'),
507
+ type: 'behavior',
508
+ description: 'Error handling without user feedback',
509
+ before: 'catch (error) { /* no UI feedback */ }',
510
+ after: 'catch (error) { showError(error.message); }',
511
+ file: relativePath,
512
+ });
513
+ }
514
+ // Detect long-running operations without loading state
515
+ if (content.includes('await') && !content.includes('loading') && !content.includes('spinner')) {
516
+ updates.push({
517
+ component: basename(file, '.ts'),
518
+ type: 'performance',
519
+ description: 'Async operation without loading indicator',
520
+ before: 'await longOperation()',
521
+ after: 'showLoading(); await longOperation(); hideLoading();',
522
+ file: relativePath,
523
+ });
524
+ }
525
+ }
526
+ return updates;
527
+ }
528
+ // ============================================================================
529
+ // PERSISTENCE
530
+ // ============================================================================
531
+ /**
532
+ * Save test flows to disk
533
+ */
534
+ export function saveTestFlows(flows) {
535
+ if (!existsSync(TEST_FLOWS_DIR)) {
536
+ mkdirSync(TEST_FLOWS_DIR, { recursive: true });
537
+ }
538
+ for (const flow of flows) {
539
+ const filePath = join(TEST_FLOWS_DIR, `${flow.id}.json`);
540
+ writeFileSync(filePath, JSON.stringify(flow, null, 2));
541
+ }
542
+ }
543
+ /**
544
+ * Load test flows from disk
545
+ */
546
+ export function loadTestFlows() {
547
+ if (!existsSync(TEST_FLOWS_DIR))
548
+ return [];
549
+ const flows = [];
550
+ const files = readdirSync(TEST_FLOWS_DIR).filter(f => f.endsWith('.json'));
551
+ for (const file of files) {
552
+ try {
553
+ const content = readFileSync(join(TEST_FLOWS_DIR, file), 'utf-8');
554
+ flows.push(JSON.parse(content));
555
+ }
556
+ catch {
557
+ // Skip invalid files
558
+ }
559
+ }
560
+ return flows;
561
+ }
562
+ /**
563
+ * Save bug reports
564
+ */
565
+ export function saveBugReports(bugs) {
566
+ if (!existsSync(BUG_REPORTS_DIR)) {
567
+ mkdirSync(BUG_REPORTS_DIR, { recursive: true });
568
+ }
569
+ for (const bug of bugs) {
570
+ const filePath = join(BUG_REPORTS_DIR, `${bug.id}.json`);
571
+ writeFileSync(filePath, JSON.stringify(bug, null, 2));
572
+ }
573
+ }
574
+ /**
575
+ * Save UI updates
576
+ */
577
+ export function saveUIUpdates(updates) {
578
+ const dir = join(homedir(), '.erosolar');
579
+ if (!existsSync(dir)) {
580
+ mkdirSync(dir, { recursive: true });
581
+ }
582
+ writeFileSync(UI_UPDATES_FILE, JSON.stringify(updates, null, 2));
583
+ }
584
+ // ============================================================================
585
+ // STATUS DISPLAY
586
+ // ============================================================================
587
+ /**
588
+ * Get comprehensive test/bug status
589
+ */
590
+ export function getTestFlowStatus(workingDir) {
591
+ const flows = loadTestFlows();
592
+ const bugs = detectBugs(workingDir);
593
+ const uiUpdates = detectUIUpdates(workingDir);
594
+ const lines = [];
595
+ lines.push('═══════════════════════════════════════════════════════════');
596
+ lines.push(' 🧪 Intelligent Test Flow System');
597
+ lines.push('═══════════════════════════════════════════════════════════');
598
+ lines.push('');
599
+ // Test flows summary
600
+ const flowsByCategory = {};
601
+ for (const flow of flows) {
602
+ flowsByCategory[flow.category] = (flowsByCategory[flow.category] || 0) + 1;
603
+ }
604
+ lines.push(`Total Test Flows: ${flows.length}`);
605
+ for (const [category, count] of Object.entries(flowsByCategory)) {
606
+ lines.push(` ${category}: ${count}`);
607
+ }
608
+ lines.push('');
609
+ // Bugs summary
610
+ const bugsBySeverity = {};
611
+ for (const bug of bugs) {
612
+ bugsBySeverity[bug.severity] = (bugsBySeverity[bug.severity] || 0) + 1;
613
+ }
614
+ lines.push(`Potential Bugs Detected: ${bugs.length}`);
615
+ if (bugsBySeverity['critical'])
616
+ lines.push(` 🔴 Critical: ${bugsBySeverity['critical']}`);
617
+ if (bugsBySeverity['high'])
618
+ lines.push(` 🟠 High: ${bugsBySeverity['high']}`);
619
+ if (bugsBySeverity['medium'])
620
+ lines.push(` 🟡 Medium: ${bugsBySeverity['medium']}`);
621
+ if (bugsBySeverity['low'])
622
+ lines.push(` ⚪ Low: ${bugsBySeverity['low']}`);
623
+ lines.push('');
624
+ // UI updates
625
+ lines.push(`UI Updates Needed: ${uiUpdates.length}`);
626
+ const uiByType = {};
627
+ for (const update of uiUpdates) {
628
+ uiByType[update.type] = (uiByType[update.type] || 0) + 1;
629
+ }
630
+ for (const [type, count] of Object.entries(uiByType)) {
631
+ lines.push(` ${type}: ${count}`);
632
+ }
633
+ lines.push('');
634
+ // Top bugs
635
+ if (bugs.length > 0) {
636
+ lines.push('Top Issues:');
637
+ for (const bug of bugs.slice(0, 5)) {
638
+ const icon = bug.severity === 'critical' ? '🔴' :
639
+ bug.severity === 'high' ? '🟠' :
640
+ bug.severity === 'medium' ? '🟡' : '⚪';
641
+ lines.push(` ${icon} ${bug.title}`);
642
+ lines.push(` ${bug.file}:${bug.line ?? '?'}`);
643
+ }
644
+ lines.push('');
645
+ }
646
+ lines.push('Commands:');
647
+ lines.push(' /test generate <file> - Generate test flows for file');
648
+ lines.push(' /test bugs - Detect potential bugs');
649
+ lines.push(' /test ui - Detect UI improvements');
650
+ lines.push(' /test run - Run generated tests');
651
+ lines.push('');
652
+ lines.push('═══════════════════════════════════════════════════════════');
653
+ return lines.join('\n');
654
+ }
655
+ // ============================================================================
656
+ // HELPERS
657
+ // ============================================================================
658
+ function findFilesRecursive(dir, extensions) {
659
+ const files = [];
660
+ try {
661
+ const entries = readdirSync(dir, { withFileTypes: true });
662
+ for (const entry of entries) {
663
+ const fullPath = join(dir, entry.name);
664
+ if (entry.isDirectory() && !entry.name.startsWith('.') &&
665
+ entry.name !== 'node_modules' && entry.name !== 'dist') {
666
+ files.push(...findFilesRecursive(fullPath, extensions));
667
+ }
668
+ else if (entry.isFile() && extensions.some(ext => entry.name.endsWith(ext))) {
669
+ files.push(fullPath);
670
+ }
671
+ }
672
+ }
673
+ catch {
674
+ // Ignore
675
+ }
676
+ return files;
677
+ }
678
+ //# sourceMappingURL=intelligentTestFlows.js.map