@oalacea/daemon 0.5.0 → 0.5.1

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 (38) hide show
  1. package/CHANGELOG.md +46 -38
  2. package/LICENSE +23 -23
  3. package/README.md +147 -141
  4. package/agents/deps-analyzer.js +366 -366
  5. package/agents/detector.js +570 -570
  6. package/agents/fix-engine.js +305 -305
  7. package/agents/lighthouse-scanner.js +405 -405
  8. package/agents/perf-analyzer.js +294 -294
  9. package/agents/perf-front-analyzer.js +229 -229
  10. package/agents/test-generator.js +387 -387
  11. package/agents/test-runner.js +318 -318
  12. package/bin/Dockerfile +75 -74
  13. package/bin/cli.js +449 -449
  14. package/lib/config.js +250 -250
  15. package/lib/docker.js +207 -207
  16. package/lib/reporter.js +297 -297
  17. package/package.json +34 -34
  18. package/prompts/DEPS_EFFICIENCY.md +558 -558
  19. package/prompts/E2E.md +491 -491
  20. package/prompts/EXECUTE.md +1060 -1060
  21. package/prompts/INTEGRATION_API.md +484 -484
  22. package/prompts/INTEGRATION_DB.md +425 -425
  23. package/prompts/PERF_API.md +433 -433
  24. package/prompts/PERF_DB.md +430 -430
  25. package/prompts/PERF_FRONT.md +357 -357
  26. package/prompts/REMEDIATION.md +482 -482
  27. package/prompts/UNIT.md +260 -260
  28. package/scripts/dev.js +106 -106
  29. package/templates/README.md +38 -38
  30. package/templates/k6/load-test.js +54 -54
  31. package/templates/playwright/e2e.spec.ts +61 -61
  32. package/templates/vitest/angular-component.test.ts +38 -38
  33. package/templates/vitest/api.test.ts +51 -51
  34. package/templates/vitest/component.test.ts +27 -27
  35. package/templates/vitest/hook.test.ts +36 -36
  36. package/templates/vitest/solid-component.test.ts +34 -34
  37. package/templates/vitest/svelte-component.test.ts +33 -33
  38. package/templates/vitest/vue-component.test.ts +39 -39
@@ -1,305 +1,305 @@
1
- /**
2
- * Daemon - Fix Engine
3
- *
4
- * Analyzes test failures and applies fixes.
5
- * Handles common test issues automatically.
6
- */
7
-
8
- const fs = require('fs');
9
- const path = require('path');
10
-
11
- /**
12
- * Analyze test failure and categorize
13
- */
14
- function categorizeFailure(errorOutput, testPath) {
15
- if (!errorOutput) return { category: 'unknown' };
16
-
17
- if (errorOutput.includes('Cannot find module')) {
18
- return { category: 'import', message: 'Module import issue' };
19
- }
20
-
21
- if (errorOutput.includes('is not defined') || errorOutput.includes('is not a function')) {
22
- return { category: 'mock', message: 'Missing mock' };
23
- }
24
-
25
- if (errorOutput.includes('timeout')) {
26
- return { category: 'timeout', message: 'Test timeout' };
27
- }
28
-
29
- if (errorOutput.includes('Expected') && errorOutput.includes('Received')) {
30
- return { category: 'assertion', message: 'Assertion failed' };
31
- }
32
-
33
- if (errorOutput.includes('Cannot read property')) {
34
- return { category: 'null', message: 'Null/undefined access' };
35
- }
36
-
37
- if (errorOutput.includes('ECONNREFUSED') || errorOutput.includes('connect')) {
38
- return { category: 'connection', message: 'Connection refused' };
39
- }
40
-
41
- return { category: 'unknown', message: 'Unknown error' };
42
- }
43
-
44
- /**
45
- * Fix import issues
46
- */
47
- function fixImportIssue(testPath, error) {
48
- const content = fs.readFileSync(testPath, 'utf-8');
49
- const missingModule = error.match(/Cannot find module ['"](.+)['"]/)?.[1];
50
-
51
- if (!missingModule) return false;
52
-
53
- // Check if it's a relative import issue
54
- if (missingModule.startsWith('@/')) {
55
- // Check if vitest.config.ts has the alias
56
- const vitestConfig = path.join(process.cwd(), 'vitest.config.ts');
57
- if (fs.existsSync(vitestConfig)) {
58
- const config = fs.readFileSync(vitestConfig, 'utf-8');
59
- if (!config.includes('resolve:') || !config.includes('alias:')) {
60
- return {
61
- fixType: 'config',
62
- message: 'Add path alias to vitest.config.ts',
63
- configFix: `
64
- export default defineConfig({
65
- resolve: {
66
- alias: {
67
- '@': path.resolve(__dirname, './src'),
68
- },
69
- },
70
- });`,
71
- };
72
- }
73
- }
74
- }
75
-
76
- return {
77
- fixType: 'import',
78
- message: `Create or update import for ${missingModule}`,
79
- };
80
- }
81
-
82
- /**
83
- * Fix mock issues
84
- */
85
- function fixMockIssue(testPath, error) {
86
- const content = fs.readFileSync(testPath, 'utf-8');
87
- const missingVar = error.match(/(.+) is not defined/)?.[1];
88
-
89
- if (!missingVar) return false;
90
-
91
- // Check if it's a hook/function that needs mocking
92
- if (missingVar.startsWith('use')) {
93
- return {
94
- fixType: 'mock',
95
- message: `Add mock for ${missingVar}`,
96
- mock: `vi.mock('@/hooks/${missingVar}', () => ({
97
- ${missingVar}: vi.fn(() => ({/* default return */})),
98
- }));`,
99
- };
100
- }
101
-
102
- return {
103
- fixType: 'mock',
104
- message: `Add mock for ${missingVar}`,
105
- };
106
- }
107
-
108
- /**
109
- * Fix assertion issues
110
- */
111
- function fixAssertionIssue(testPath, error) {
112
- const content = fs.readFileSync(testPath, 'utf-8');
113
-
114
- // Check for common issues
115
- if (error.includes('querySelector returned null')) {
116
- return {
117
- fixType: 'selector',
118
- message: 'Element not found - use queryBy or add waitFor',
119
- suggestion: 'Replace getBy with queryBy for optional elements, or add waitFor',
120
- };
121
- }
122
-
123
- if (error.includes('received null') || error.includes('received undefined')) {
124
- return {
125
- fixType: 'assertion',
126
- message: 'Element/property is null/undefined',
127
- suggestion: 'Check if component renders with required props',
128
- };
129
- }
130
-
131
- return {
132
- fixType: 'manual',
133
- message: 'Manual fix required',
134
- error,
135
- };
136
- }
137
-
138
- /**
139
- * Fix timeout issues
140
- */
141
- function fixTimeoutIssue(testPath, error) {
142
- return {
143
- fixType: 'timeout',
144
- message: 'Increase test timeout or fix async operation',
145
- suggestion: `Add timeout to test: it('test name', async () => { ... }, { timeout: 10000 })`,
146
- };
147
- }
148
-
149
- /**
150
- * Fix connection issues
151
- */
152
- function fixConnectionIssue(testPath, error) {
153
- return {
154
- fixType: 'mock',
155
- message: 'Mock external API calls',
156
- suggestion: 'Use vi.mock or MSW to mock fetch/axios calls',
157
- };
158
- }
159
-
160
- /**
161
- * Generate fix for a failing test
162
- */
163
- function generateFix(testPath, error) {
164
- const failure = categorizeFailure(error, testPath);
165
-
166
- switch (failure.category) {
167
- case 'import':
168
- return fixImportIssue(testPath, error);
169
- case 'mock':
170
- return fixMockIssue(testPath, error);
171
- case 'timeout':
172
- return fixTimeoutIssue(testPath, error);
173
- case 'assertion':
174
- return fixAssertionIssue(testPath, error);
175
- case 'connection':
176
- return fixConnectionIssue(testPath, error);
177
- default:
178
- return {
179
- fixType: 'manual',
180
- message: 'Manual analysis required',
181
- error,
182
- };
183
- }
184
- }
185
-
186
- /**
187
- * Apply fix to file
188
- */
189
- function applyFix(testPath, fix) {
190
- if (!fix) return false;
191
-
192
- const content = fs.readFileSync(testPath, 'utf-8');
193
- let newContent = content;
194
-
195
- switch (fix.fixType) {
196
- case 'mock':
197
- // Add mock at top of file after imports
198
- const importsEnd = content.indexOf(');', content.indexOf('import '));
199
- if (importsEnd > -1) {
200
- newContent = content.slice(0, importsEnd + 2) +
201
- '\n\n' + fix.mock +
202
- content.slice(importsEnd + 2);
203
- }
204
- break;
205
-
206
- case 'timeout':
207
- // Add timeout option to it() call
208
- newContent = content.replace(
209
- /it\('([^']+)',\s*(async\s*)?\(([^)]*)\)\s*=>/g,
210
- "it('$1', async ($2) => { ... }, { timeout: 10000 })"
211
- );
212
- break;
213
-
214
- case 'selector':
215
- // Suggest using queryBy instead of getBy
216
- newContent = content.replace(
217
- /getBy\(([^)]+)\)/g,
218
- 'queryBy($1) || getBy($1)'
219
- );
220
- break;
221
-
222
- default:
223
- return false;
224
- }
225
-
226
- if (newContent !== content) {
227
- fs.writeFileSync(testPath, newContent);
228
- return true;
229
- }
230
-
231
- return false;
232
- }
233
-
234
- /**
235
- * Fix all failing tests
236
- */
237
- function fixFailures(testResults) {
238
- const fixes = [];
239
-
240
- for (const result of testResults) {
241
- if (result.status === 'failed') {
242
- const fix = generateFix(result.file, result.error);
243
- fixes.push({
244
- file: result.file,
245
- error: result.error,
246
- fix,
247
- });
248
- }
249
- }
250
-
251
- return fixes;
252
- }
253
-
254
- /**
255
- * Create fix summary
256
- */
257
- function createFixSummary(fixes) {
258
- const summary = {
259
- total: fixes.length,
260
- applied: 0,
261
- manual: 0,
262
- failed: 0,
263
- details: [],
264
- };
265
-
266
- for (const fix of fixes) {
267
- const detail = {
268
- file: fix.file,
269
- type: fix.fix?.fixType || 'unknown',
270
- message: fix.fix?.message || 'No fix available',
271
- applied: false,
272
- };
273
-
274
- if (fix.fix?.fixType === 'manual') {
275
- summary.manual++;
276
- detail.applied = false;
277
- } else if (fix.fix) {
278
- // Try to apply fix
279
- // const applied = applyFix(fix.file, fix.fix);
280
- // if (applied) summary.applied++;
281
- // else summary.failed++;
282
- summary.manual++; // For safety, mark as manual
283
- detail.applied = false;
284
- } else {
285
- summary.failed++;
286
- }
287
-
288
- summary.details.push(detail);
289
- }
290
-
291
- return summary;
292
- }
293
-
294
- module.exports = {
295
- categorizeFailure,
296
- fixImportIssue,
297
- fixMockIssue,
298
- fixAssertionIssue,
299
- fixTimeoutIssue,
300
- fixConnectionIssue,
301
- generateFix,
302
- applyFix,
303
- fixFailures,
304
- createFixSummary,
305
- };
1
+ /**
2
+ * Daemon - Fix Engine
3
+ *
4
+ * Analyzes test failures and applies fixes.
5
+ * Handles common test issues automatically.
6
+ */
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+
11
+ /**
12
+ * Analyze test failure and categorize
13
+ */
14
+ function categorizeFailure(errorOutput, testPath) {
15
+ if (!errorOutput) return { category: 'unknown' };
16
+
17
+ if (errorOutput.includes('Cannot find module')) {
18
+ return { category: 'import', message: 'Module import issue' };
19
+ }
20
+
21
+ if (errorOutput.includes('is not defined') || errorOutput.includes('is not a function')) {
22
+ return { category: 'mock', message: 'Missing mock' };
23
+ }
24
+
25
+ if (errorOutput.includes('timeout')) {
26
+ return { category: 'timeout', message: 'Test timeout' };
27
+ }
28
+
29
+ if (errorOutput.includes('Expected') && errorOutput.includes('Received')) {
30
+ return { category: 'assertion', message: 'Assertion failed' };
31
+ }
32
+
33
+ if (errorOutput.includes('Cannot read property')) {
34
+ return { category: 'null', message: 'Null/undefined access' };
35
+ }
36
+
37
+ if (errorOutput.includes('ECONNREFUSED') || errorOutput.includes('connect')) {
38
+ return { category: 'connection', message: 'Connection refused' };
39
+ }
40
+
41
+ return { category: 'unknown', message: 'Unknown error' };
42
+ }
43
+
44
+ /**
45
+ * Fix import issues
46
+ */
47
+ function fixImportIssue(testPath, error) {
48
+ const content = fs.readFileSync(testPath, 'utf-8');
49
+ const missingModule = error.match(/Cannot find module ['"](.+)['"]/)?.[1];
50
+
51
+ if (!missingModule) return false;
52
+
53
+ // Check if it's a relative import issue
54
+ if (missingModule.startsWith('@/')) {
55
+ // Check if vitest.config.ts has the alias
56
+ const vitestConfig = path.join(process.cwd(), 'vitest.config.ts');
57
+ if (fs.existsSync(vitestConfig)) {
58
+ const config = fs.readFileSync(vitestConfig, 'utf-8');
59
+ if (!config.includes('resolve:') || !config.includes('alias:')) {
60
+ return {
61
+ fixType: 'config',
62
+ message: 'Add path alias to vitest.config.ts',
63
+ configFix: `
64
+ export default defineConfig({
65
+ resolve: {
66
+ alias: {
67
+ '@': path.resolve(__dirname, './src'),
68
+ },
69
+ },
70
+ });`,
71
+ };
72
+ }
73
+ }
74
+ }
75
+
76
+ return {
77
+ fixType: 'import',
78
+ message: `Create or update import for ${missingModule}`,
79
+ };
80
+ }
81
+
82
+ /**
83
+ * Fix mock issues
84
+ */
85
+ function fixMockIssue(testPath, error) {
86
+ const content = fs.readFileSync(testPath, 'utf-8');
87
+ const missingVar = error.match(/(.+) is not defined/)?.[1];
88
+
89
+ if (!missingVar) return false;
90
+
91
+ // Check if it's a hook/function that needs mocking
92
+ if (missingVar.startsWith('use')) {
93
+ return {
94
+ fixType: 'mock',
95
+ message: `Add mock for ${missingVar}`,
96
+ mock: `vi.mock('@/hooks/${missingVar}', () => ({
97
+ ${missingVar}: vi.fn(() => ({/* default return */})),
98
+ }));`,
99
+ };
100
+ }
101
+
102
+ return {
103
+ fixType: 'mock',
104
+ message: `Add mock for ${missingVar}`,
105
+ };
106
+ }
107
+
108
+ /**
109
+ * Fix assertion issues
110
+ */
111
+ function fixAssertionIssue(testPath, error) {
112
+ const content = fs.readFileSync(testPath, 'utf-8');
113
+
114
+ // Check for common issues
115
+ if (error.includes('querySelector returned null')) {
116
+ return {
117
+ fixType: 'selector',
118
+ message: 'Element not found - use queryBy or add waitFor',
119
+ suggestion: 'Replace getBy with queryBy for optional elements, or add waitFor',
120
+ };
121
+ }
122
+
123
+ if (error.includes('received null') || error.includes('received undefined')) {
124
+ return {
125
+ fixType: 'assertion',
126
+ message: 'Element/property is null/undefined',
127
+ suggestion: 'Check if component renders with required props',
128
+ };
129
+ }
130
+
131
+ return {
132
+ fixType: 'manual',
133
+ message: 'Manual fix required',
134
+ error,
135
+ };
136
+ }
137
+
138
+ /**
139
+ * Fix timeout issues
140
+ */
141
+ function fixTimeoutIssue(testPath, error) {
142
+ return {
143
+ fixType: 'timeout',
144
+ message: 'Increase test timeout or fix async operation',
145
+ suggestion: `Add timeout to test: it('test name', async () => { ... }, { timeout: 10000 })`,
146
+ };
147
+ }
148
+
149
+ /**
150
+ * Fix connection issues
151
+ */
152
+ function fixConnectionIssue(testPath, error) {
153
+ return {
154
+ fixType: 'mock',
155
+ message: 'Mock external API calls',
156
+ suggestion: 'Use vi.mock or MSW to mock fetch/axios calls',
157
+ };
158
+ }
159
+
160
+ /**
161
+ * Generate fix for a failing test
162
+ */
163
+ function generateFix(testPath, error) {
164
+ const failure = categorizeFailure(error, testPath);
165
+
166
+ switch (failure.category) {
167
+ case 'import':
168
+ return fixImportIssue(testPath, error);
169
+ case 'mock':
170
+ return fixMockIssue(testPath, error);
171
+ case 'timeout':
172
+ return fixTimeoutIssue(testPath, error);
173
+ case 'assertion':
174
+ return fixAssertionIssue(testPath, error);
175
+ case 'connection':
176
+ return fixConnectionIssue(testPath, error);
177
+ default:
178
+ return {
179
+ fixType: 'manual',
180
+ message: 'Manual analysis required',
181
+ error,
182
+ };
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Apply fix to file
188
+ */
189
+ function applyFix(testPath, fix) {
190
+ if (!fix) return false;
191
+
192
+ const content = fs.readFileSync(testPath, 'utf-8');
193
+ let newContent = content;
194
+
195
+ switch (fix.fixType) {
196
+ case 'mock':
197
+ // Add mock at top of file after imports
198
+ const importsEnd = content.indexOf(');', content.indexOf('import '));
199
+ if (importsEnd > -1) {
200
+ newContent = content.slice(0, importsEnd + 2) +
201
+ '\n\n' + fix.mock +
202
+ content.slice(importsEnd + 2);
203
+ }
204
+ break;
205
+
206
+ case 'timeout':
207
+ // Add timeout option to it() call
208
+ newContent = content.replace(
209
+ /it\('([^']+)',\s*(async\s*)?\(([^)]*)\)\s*=>/g,
210
+ "it('$1', async ($2) => { ... }, { timeout: 10000 })"
211
+ );
212
+ break;
213
+
214
+ case 'selector':
215
+ // Suggest using queryBy instead of getBy
216
+ newContent = content.replace(
217
+ /getBy\(([^)]+)\)/g,
218
+ 'queryBy($1) || getBy($1)'
219
+ );
220
+ break;
221
+
222
+ default:
223
+ return false;
224
+ }
225
+
226
+ if (newContent !== content) {
227
+ fs.writeFileSync(testPath, newContent);
228
+ return true;
229
+ }
230
+
231
+ return false;
232
+ }
233
+
234
+ /**
235
+ * Fix all failing tests
236
+ */
237
+ function fixFailures(testResults) {
238
+ const fixes = [];
239
+
240
+ for (const result of testResults) {
241
+ if (result.status === 'failed') {
242
+ const fix = generateFix(result.file, result.error);
243
+ fixes.push({
244
+ file: result.file,
245
+ error: result.error,
246
+ fix,
247
+ });
248
+ }
249
+ }
250
+
251
+ return fixes;
252
+ }
253
+
254
+ /**
255
+ * Create fix summary
256
+ */
257
+ function createFixSummary(fixes) {
258
+ const summary = {
259
+ total: fixes.length,
260
+ applied: 0,
261
+ manual: 0,
262
+ failed: 0,
263
+ details: [],
264
+ };
265
+
266
+ for (const fix of fixes) {
267
+ const detail = {
268
+ file: fix.file,
269
+ type: fix.fix?.fixType || 'unknown',
270
+ message: fix.fix?.message || 'No fix available',
271
+ applied: false,
272
+ };
273
+
274
+ if (fix.fix?.fixType === 'manual') {
275
+ summary.manual++;
276
+ detail.applied = false;
277
+ } else if (fix.fix) {
278
+ // Try to apply fix
279
+ // const applied = applyFix(fix.file, fix.fix);
280
+ // if (applied) summary.applied++;
281
+ // else summary.failed++;
282
+ summary.manual++; // For safety, mark as manual
283
+ detail.applied = false;
284
+ } else {
285
+ summary.failed++;
286
+ }
287
+
288
+ summary.details.push(detail);
289
+ }
290
+
291
+ return summary;
292
+ }
293
+
294
+ module.exports = {
295
+ categorizeFailure,
296
+ fixImportIssue,
297
+ fixMockIssue,
298
+ fixAssertionIssue,
299
+ fixTimeoutIssue,
300
+ fixConnectionIssue,
301
+ generateFix,
302
+ applyFix,
303
+ fixFailures,
304
+ createFixSummary,
305
+ };