reviewflow 3.33.0 → 3.34.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 (84) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/main/routes.d.ts.map +1 -1
  3. package/dist/main/routes.js +4 -3
  4. package/dist/main/routes.js.map +1 -1
  5. package/dist/modules/claude-invocation/entities/claudeSession/claudeSession.guard.d.ts +1 -1
  6. package/dist/modules/claude-invocation/entities/claudeSession/claudeSession.schema.d.ts +2 -0
  7. package/dist/modules/claude-invocation/entities/claudeSession/claudeSession.schema.d.ts.map +1 -1
  8. package/dist/modules/claude-invocation/entities/claudeSession/claudeSession.schema.js +1 -1
  9. package/dist/modules/claude-invocation/entities/claudeSession/claudeSession.schema.js.map +1 -1
  10. package/dist/modules/{ember-chat/interface-adapters/gateways/emberStreamJson.parser.d.ts → claude-invocation/interface-adapters/gateways/transcriptStreamJson.parser.d.ts} +1 -1
  11. package/dist/modules/claude-invocation/interface-adapters/gateways/transcriptStreamJson.parser.d.ts.map +1 -0
  12. package/dist/modules/{ember-chat/interface-adapters/gateways/emberStreamJson.parser.js → claude-invocation/interface-adapters/gateways/transcriptStreamJson.parser.js} +1 -1
  13. package/dist/modules/claude-invocation/interface-adapters/gateways/transcriptStreamJson.parser.js.map +1 -0
  14. package/dist/modules/ember-chat/interface-adapters/gateways/emberAnswerTransport.claude.gateway.js +1 -1
  15. package/dist/modules/ember-chat/interface-adapters/gateways/emberAnswerTransport.claude.gateway.js.map +1 -1
  16. package/dist/modules/statistics-insights/entities/insight/aiInsightsSession.gateway.d.ts +13 -0
  17. package/dist/modules/statistics-insights/entities/insight/aiInsightsSession.gateway.d.ts.map +1 -0
  18. package/dist/modules/statistics-insights/entities/insight/aiInsightsSession.gateway.js +2 -0
  19. package/dist/modules/statistics-insights/entities/insight/aiInsightsSession.gateway.js.map +1 -0
  20. package/dist/modules/statistics-insights/interface-adapters/controllers/http/insights.routes.d.ts +4 -2
  21. package/dist/modules/statistics-insights/interface-adapters/controllers/http/insights.routes.d.ts.map +1 -1
  22. package/dist/modules/statistics-insights/interface-adapters/controllers/http/insights.routes.js +6 -4
  23. package/dist/modules/statistics-insights/interface-adapters/controllers/http/insights.routes.js.map +1 -1
  24. package/dist/modules/statistics-insights/interface-adapters/gateways/aiInsightsSession.claude.gateway.d.ts +31 -0
  25. package/dist/modules/statistics-insights/interface-adapters/gateways/aiInsightsSession.claude.gateway.d.ts.map +1 -0
  26. package/dist/modules/statistics-insights/interface-adapters/gateways/aiInsightsSession.claude.gateway.js +105 -0
  27. package/dist/modules/statistics-insights/interface-adapters/gateways/aiInsightsSession.claude.gateway.js.map +1 -0
  28. package/dist/modules/statistics-insights/usecases/insights/{generateAiInsights.usecase.d.ts → generateAiInsightsViaSession.usecase.d.ts} +7 -13
  29. package/dist/modules/statistics-insights/usecases/insights/generateAiInsightsViaSession.usecase.d.ts.map +1 -0
  30. package/dist/modules/statistics-insights/usecases/insights/generateAiInsightsViaSession.usecase.js +49 -0
  31. package/dist/modules/statistics-insights/usecases/insights/generateAiInsightsViaSession.usecase.js.map +1 -0
  32. package/dist/modules/statistics-insights/usecases/insights/parseAiInsightsResponse.d.ts +3 -0
  33. package/dist/modules/statistics-insights/usecases/insights/parseAiInsightsResponse.d.ts.map +1 -0
  34. package/dist/modules/statistics-insights/usecases/insights/parseAiInsightsResponse.js +20 -0
  35. package/dist/modules/statistics-insights/usecases/insights/parseAiInsightsResponse.js.map +1 -0
  36. package/dist/modules/statistics-insights/usecases/insights/persistAiInsights.usecase.d.ts +12 -0
  37. package/dist/modules/statistics-insights/usecases/insights/persistAiInsights.usecase.d.ts.map +1 -0
  38. package/dist/modules/statistics-insights/usecases/insights/persistAiInsights.usecase.js +14 -0
  39. package/dist/modules/statistics-insights/usecases/insights/persistAiInsights.usecase.js.map +1 -0
  40. package/dist/tests/acceptance/191-team-insights-bg-migration.acceptance.test.d.ts +2 -0
  41. package/dist/tests/acceptance/191-team-insights-bg-migration.acceptance.test.d.ts.map +1 -0
  42. package/dist/tests/acceptance/191-team-insights-bg-migration.acceptance.test.js +121 -0
  43. package/dist/tests/acceptance/191-team-insights-bg-migration.acceptance.test.js.map +1 -0
  44. package/dist/tests/stubs/aiInsightsSession.stub.d.ts +8 -0
  45. package/dist/tests/stubs/aiInsightsSession.stub.d.ts.map +1 -0
  46. package/dist/tests/stubs/aiInsightsSession.stub.js +15 -0
  47. package/dist/tests/stubs/aiInsightsSession.stub.js.map +1 -0
  48. package/dist/tests/units/architecture/noClaudePInProduction.test.js +0 -1
  49. package/dist/tests/units/architecture/noClaudePInProduction.test.js.map +1 -1
  50. package/dist/tests/units/interface-adapters/controllers/http/insights.routes.test.js +6 -4
  51. package/dist/tests/units/interface-adapters/controllers/http/insights.routes.test.js.map +1 -1
  52. package/dist/tests/units/modules/claude-invocation/gateways/transcriptStreamJson.parser.test.d.ts +2 -0
  53. package/dist/tests/units/modules/claude-invocation/gateways/transcriptStreamJson.parser.test.d.ts.map +1 -0
  54. package/dist/tests/units/modules/{ember-chat/gateways/emberStreamJson.parser.test.js → claude-invocation/gateways/transcriptStreamJson.parser.test.js} +2 -2
  55. package/dist/tests/units/modules/claude-invocation/gateways/transcriptStreamJson.parser.test.js.map +1 -0
  56. package/dist/tests/units/usecases/insights/generateAiInsightsViaSession.usecase.test.d.ts +2 -0
  57. package/dist/tests/units/usecases/insights/generateAiInsightsViaSession.usecase.test.d.ts.map +1 -0
  58. package/dist/tests/units/usecases/insights/generateAiInsightsViaSession.usecase.test.js +114 -0
  59. package/dist/tests/units/usecases/insights/generateAiInsightsViaSession.usecase.test.js.map +1 -0
  60. package/dist/tests/units/usecases/insights/parseAiInsightsResponse.test.d.ts +2 -0
  61. package/dist/tests/units/usecases/insights/parseAiInsightsResponse.test.d.ts.map +1 -0
  62. package/dist/tests/units/usecases/insights/parseAiInsightsResponse.test.js +45 -0
  63. package/dist/tests/units/usecases/insights/parseAiInsightsResponse.test.js.map +1 -0
  64. package/dist/tests/units/usecases/insights/persistAiInsights.usecase.test.d.ts +2 -0
  65. package/dist/tests/units/usecases/insights/persistAiInsights.usecase.test.d.ts.map +1 -0
  66. package/dist/tests/units/usecases/insights/persistAiInsights.usecase.test.js +87 -0
  67. package/dist/tests/units/usecases/insights/persistAiInsights.usecase.test.js.map +1 -0
  68. package/package.json +9 -2
  69. package/dist/frameworks/claude/claudeInsightsInvoker.d.ts +0 -3
  70. package/dist/frameworks/claude/claudeInsightsInvoker.d.ts.map +0 -1
  71. package/dist/frameworks/claude/claudeInsightsInvoker.js +0 -58
  72. package/dist/frameworks/claude/claudeInsightsInvoker.js.map +0 -1
  73. package/dist/modules/ember-chat/interface-adapters/gateways/emberStreamJson.parser.d.ts.map +0 -1
  74. package/dist/modules/ember-chat/interface-adapters/gateways/emberStreamJson.parser.js.map +0 -1
  75. package/dist/modules/statistics-insights/usecases/insights/generateAiInsights.usecase.d.ts.map +0 -1
  76. package/dist/modules/statistics-insights/usecases/insights/generateAiInsights.usecase.js +0 -65
  77. package/dist/modules/statistics-insights/usecases/insights/generateAiInsights.usecase.js.map +0 -1
  78. package/dist/tests/units/modules/ember-chat/gateways/emberStreamJson.parser.test.d.ts +0 -2
  79. package/dist/tests/units/modules/ember-chat/gateways/emberStreamJson.parser.test.d.ts.map +0 -1
  80. package/dist/tests/units/modules/ember-chat/gateways/emberStreamJson.parser.test.js.map +0 -1
  81. package/dist/tests/units/usecases/insights/generateAiInsights.usecase.test.d.ts +0 -2
  82. package/dist/tests/units/usecases/insights/generateAiInsights.usecase.test.d.ts.map +0 -1
  83. package/dist/tests/units/usecases/insights/generateAiInsights.usecase.test.js +0 -253
  84. package/dist/tests/units/usecases/insights/generateAiInsights.usecase.test.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseAiInsightsResponse.test.d.ts","sourceRoot":"","sources":["../../../../../src/tests/units/usecases/insights/parseAiInsightsResponse.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,45 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { parseAiInsightsResponse } from '../../../../modules/statistics-insights/usecases/insights/parseAiInsightsResponse.js';
3
+ const validAiResult = {
4
+ developers: [
5
+ {
6
+ developerName: 'alice',
7
+ title: 'Le Chirurgien du Code',
8
+ titleExplanation: 'Precise and methodical',
9
+ strengths: ['Excellent test coverage'],
10
+ weaknesses: ['Slow review turnaround'],
11
+ recommendations: ['Automate repetitive checks'],
12
+ summary: 'Alice is a meticulous developer.',
13
+ },
14
+ ],
15
+ team: {
16
+ summary: 'A well-balanced team.',
17
+ strengths: ['Strong testing culture'],
18
+ weaknesses: ['Documentation gaps'],
19
+ recommendations: ['Establish review guidelines'],
20
+ dynamics: 'Good team dynamics.',
21
+ },
22
+ generatedAt: '2026-03-15T10:00:00Z',
23
+ };
24
+ describe('parseAiInsightsResponse', () => {
25
+ it('parses a raw JSON answer into the insights shape', () => {
26
+ const result = parseAiInsightsResponse(JSON.stringify(validAiResult));
27
+ expect(result.developers[0].developerName).toBe('alice');
28
+ expect(result.team.summary).toBe('A well-balanced team.');
29
+ });
30
+ it('strips a ```json fence before parsing', () => {
31
+ const result = parseAiInsightsResponse('```json\n' + JSON.stringify(validAiResult) + '\n```');
32
+ expect(result.developers).toHaveLength(1);
33
+ });
34
+ it('strips a bare ``` fence before parsing', () => {
35
+ const result = parseAiInsightsResponse('```\n' + JSON.stringify(validAiResult) + '\n```');
36
+ expect(result.team.dynamics).toBe('Good team dynamics.');
37
+ });
38
+ it('throws on a non-JSON answer', () => {
39
+ expect(() => parseAiInsightsResponse('this is not JSON')).toThrow();
40
+ });
41
+ it('throws when the JSON does not match the insights schema', () => {
42
+ expect(() => parseAiInsightsResponse('{"developers":"nope"}')).toThrow();
43
+ });
44
+ });
45
+ //# sourceMappingURL=parseAiInsightsResponse.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseAiInsightsResponse.test.js","sourceRoot":"","sources":["../../../../../src/tests/units/usecases/insights/parseAiInsightsResponse.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,uBAAuB,EAAE,MAAM,4EAA4E,CAAC;AAGrH,MAAM,aAAa,GAAqB;IACtC,UAAU,EAAE;QACV;YACE,aAAa,EAAE,OAAO;YACtB,KAAK,EAAE,uBAAuB;YAC9B,gBAAgB,EAAE,wBAAwB;YAC1C,SAAS,EAAE,CAAC,yBAAyB,CAAC;YACtC,UAAU,EAAE,CAAC,wBAAwB,CAAC;YACtC,eAAe,EAAE,CAAC,4BAA4B,CAAC;YAC/C,OAAO,EAAE,kCAAkC;SAC5C;KACF;IACD,IAAI,EAAE;QACJ,OAAO,EAAE,uBAAuB;QAChC,SAAS,EAAE,CAAC,wBAAwB,CAAC;QACrC,UAAU,EAAE,CAAC,oBAAoB,CAAC;QAClC,eAAe,EAAE,CAAC,6BAA6B,CAAC;QAChD,QAAQ,EAAE,qBAAqB;KAChC;IACD,WAAW,EAAE,sBAAsB;CACpC,CAAC;AAEF,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACvC,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,MAAM,MAAM,GAAG,uBAAuB,CAAC,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC;QAEtE,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACzD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,MAAM,GAAG,uBAAuB,CAAC,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,CAAC;QAE9F,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,MAAM,GAAG,uBAAuB,CAAC,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,CAAC;QAE1F,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,CAAC,GAAG,EAAE,CAAC,uBAAuB,CAAC,kBAAkB,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;IACtE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,MAAM,CAAC,GAAG,EAAE,CAAC,uBAAuB,CAAC,uBAAuB,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;IAC3E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=persistAiInsights.usecase.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persistAiInsights.usecase.test.d.ts","sourceRoot":"","sources":["../../../../../src/tests/units/usecases/insights/persistAiInsights.usecase.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,87 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { persistAiInsightsResult } from '../../../../modules/statistics-insights/usecases/insights/persistAiInsights.usecase.js';
3
+ import { InMemoryStatsGateway } from '../../../../tests/stubs/stats.stub.js';
4
+ import { InMemoryInsightsGateway } from '../../../../tests/stubs/insights.stub.js';
5
+ import { ProjectStatsFactory, ReviewStatsFactory } from '../../../../tests/factories/projectStats.factory.js';
6
+ const validAiResult = {
7
+ developers: [
8
+ {
9
+ developerName: 'alice',
10
+ title: 'Le Chirurgien du Code',
11
+ titleExplanation: 'Precise and methodical',
12
+ strengths: ['Excellent test coverage'],
13
+ weaknesses: ['Slow review turnaround'],
14
+ recommendations: ['Automate repetitive checks'],
15
+ summary: 'Alice is a meticulous developer.',
16
+ },
17
+ ],
18
+ team: {
19
+ summary: 'A well-balanced team.',
20
+ strengths: ['Strong testing culture'],
21
+ weaknesses: ['Documentation gaps'],
22
+ recommendations: ['Establish review guidelines'],
23
+ dynamics: 'Good team dynamics.',
24
+ },
25
+ generatedAt: '2026-03-15T10:00:00Z',
26
+ };
27
+ describe('persistAiInsightsResult', () => {
28
+ let statsGateway;
29
+ let insightsGateway;
30
+ beforeEach(() => {
31
+ statsGateway = new InMemoryStatsGateway();
32
+ insightsGateway = new InMemoryInsightsGateway();
33
+ });
34
+ it('should save AI insights into persisted data', () => {
35
+ const reviews = [
36
+ ReviewStatsFactory.create({ id: 'r1', assignedBy: 'alice', mrNumber: 1, score: 8 }),
37
+ ];
38
+ statsGateway.saveProjectStats('/test/project', ProjectStatsFactory.withReviews(reviews));
39
+ persistAiInsightsResult({
40
+ projectPath: '/test/project',
41
+ aiInsights: validAiResult,
42
+ statsGateway,
43
+ insightsGateway,
44
+ });
45
+ const persisted = insightsGateway.loadPersistedInsights('/test/project');
46
+ expect(persisted).not.toBeNull();
47
+ expect(persisted?.aiInsights).toEqual(validAiResult);
48
+ });
49
+ it('should update review count at AI generation', () => {
50
+ const reviews = [
51
+ ReviewStatsFactory.create({ id: 'r1', assignedBy: 'alice', mrNumber: 1, score: 8 }),
52
+ ReviewStatsFactory.create({ id: 'r2', assignedBy: 'alice', mrNumber: 2, score: 7 }),
53
+ ];
54
+ statsGateway.saveProjectStats('/test/project', ProjectStatsFactory.withReviews(reviews));
55
+ persistAiInsightsResult({
56
+ projectPath: '/test/project',
57
+ aiInsights: validAiResult,
58
+ statsGateway,
59
+ insightsGateway,
60
+ });
61
+ const persisted = insightsGateway.loadPersistedInsights('/test/project');
62
+ expect(persisted?.reviewCountAtAiGeneration).toBe(2);
63
+ });
64
+ it('should preserve existing persisted data when updating with AI insights', () => {
65
+ const reviews = [
66
+ ReviewStatsFactory.create({ id: 'r1', assignedBy: 'alice', mrNumber: 1, score: 8 }),
67
+ ];
68
+ statsGateway.saveProjectStats('/test/project', ProjectStatsFactory.withReviews(reviews));
69
+ insightsGateway.savePersistedInsights('/test/project', {
70
+ developers: [],
71
+ processedReviewIds: ['r1'],
72
+ lastUpdated: '2026-03-15T10:00:00Z',
73
+ aiInsights: null,
74
+ reviewCountAtAiGeneration: 0,
75
+ });
76
+ persistAiInsightsResult({
77
+ projectPath: '/test/project',
78
+ aiInsights: validAiResult,
79
+ statsGateway,
80
+ insightsGateway,
81
+ });
82
+ const persisted = insightsGateway.loadPersistedInsights('/test/project');
83
+ expect(persisted?.processedReviewIds).toContain('r1');
84
+ expect(persisted?.aiInsights).toEqual(validAiResult);
85
+ });
86
+ });
87
+ //# sourceMappingURL=persistAiInsights.usecase.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persistAiInsights.usecase.test.js","sourceRoot":"","sources":["../../../../../src/tests/units/usecases/insights/persistAiInsights.usecase.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reviewflow",
3
- "version": "3.33.0",
3
+ "version": "3.34.0",
4
4
  "description": "AI-powered code review automation for GitLab/GitHub using Claude Code",
5
5
  "type": "module",
6
6
  "main": "dist/main/server.js",
@@ -83,5 +83,12 @@
83
83
  "engines": {
84
84
  "node": ">=20.0.0"
85
85
  },
86
- "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
86
+ "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e",
87
+ "resolutions": {
88
+ "esbuild": "^0.25.0",
89
+ "vitest/vite": "^7.3.2",
90
+ "@vitest/coverage-v8/vite": "^7.3.2",
91
+ "@vitest/ui/vite": "^7.3.2",
92
+ "vite-node/vite": "^7.3.2"
93
+ }
87
94
  }
@@ -1,3 +0,0 @@
1
- import type { ClaudeInvoker } from '../../modules/statistics-insights/usecases/insights/generateAiInsights.usecase.js';
2
- export declare function createClaudeInsightsInvoker(): ClaudeInvoker;
3
- //# sourceMappingURL=claudeInsightsInvoker.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"claudeInsightsInvoker.d.ts","sourceRoot":"","sources":["../../../src/frameworks/claude/claudeInsightsInvoker.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+EAA+E,CAAC;AAInH,wBAAgB,2BAA2B,IAAI,aAAa,CA2D3D"}
@@ -1,58 +0,0 @@
1
- import { spawn } from 'node:child_process';
2
- import { resolveClaudePath } from '../../shared/services/claudePathResolver.js';
3
- import { getModel } from '../../frameworks/settings/runtimeSettings.js';
4
- const INSIGHTS_TIMEOUT_MS = 300000;
5
- export function createClaudeInsightsInvoker() {
6
- return (prompt) => {
7
- return new Promise((resolve, reject) => {
8
- const args = [
9
- '--print',
10
- '--model', getModel(),
11
- '--setting-sources', 'user',
12
- '-p', prompt,
13
- ];
14
- const childEnv = { ...process.env };
15
- childEnv.CLAUDECODE = undefined;
16
- const proc = spawn(resolveClaudePath(), args, {
17
- cwd: '/tmp',
18
- env: {
19
- ...childEnv,
20
- TERM: 'dumb',
21
- CI: 'true',
22
- },
23
- stdio: ['ignore', 'pipe', 'pipe'],
24
- });
25
- let stdout = '';
26
- let stderr = '';
27
- let timedOut = false;
28
- const timeout = setTimeout(() => {
29
- timedOut = true;
30
- proc.kill('SIGKILL');
31
- }, INSIGHTS_TIMEOUT_MS);
32
- proc.stdout.on('data', (data) => {
33
- stdout += data.toString();
34
- });
35
- proc.stderr.on('data', (data) => {
36
- stderr += data.toString();
37
- });
38
- proc.on('error', (error) => {
39
- clearTimeout(timeout);
40
- reject(new Error(`Failed to launch Claude CLI: ${error.message}`));
41
- });
42
- proc.on('close', (code) => {
43
- clearTimeout(timeout);
44
- if (timedOut) {
45
- reject(new Error('Claude CLI timed out (timeout 300s)'));
46
- return;
47
- }
48
- if (code === 0) {
49
- resolve(stdout);
50
- }
51
- else {
52
- reject(new Error(`Claude CLI exited with code ${code}: ${stderr.substring(0, 500)}`));
53
- }
54
- });
55
- });
56
- };
57
- }
58
- //# sourceMappingURL=claudeInsightsInvoker.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"claudeInsightsInvoker.js","sourceRoot":"","sources":["../../../src/frameworks/claude/claudeInsightsInvoker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,yCAAyC,CAAC;AAC5E,OAAO,EAAE,QAAQ,EAAE,MAAM,0CAA0C,CAAC;AAGpE,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAEnC,MAAM,UAAU,2BAA2B;IACzC,OAAO,CAAC,MAAc,EAAmB,EAAE;QACzC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,IAAI,GAAG;gBACX,SAAS;gBACT,SAAS,EAAE,QAAQ,EAAE;gBACrB,mBAAmB,EAAE,MAAM;gBAC3B,IAAI,EAAE,MAAM;aACb,CAAC;YAEF,MAAM,QAAQ,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;YACpC,QAAQ,CAAC,UAAU,GAAG,SAAS,CAAC;YAEhC,MAAM,IAAI,GAAG,KAAK,CAAC,iBAAiB,EAAE,EAAE,IAAI,EAAE;gBAC5C,GAAG,EAAE,MAAM;gBACX,GAAG,EAAE;oBACH,GAAG,QAAQ;oBACX,IAAI,EAAE,MAAM;oBACZ,EAAE,EAAE,MAAM;iBACX;gBACD,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;aAClC,CAAC,CAAC;YAEH,IAAI,MAAM,GAAG,EAAE,CAAC;YAChB,IAAI,MAAM,GAAG,EAAE,CAAC;YAChB,IAAI,QAAQ,GAAG,KAAK,CAAC;YAErB,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC9B,QAAQ,GAAG,IAAI,CAAC;gBAChB,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACvB,CAAC,EAAE,mBAAmB,CAAC,CAAC;YAExB,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE;gBACtC,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC5B,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE;gBACtC,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC5B,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;gBACzB,YAAY,CAAC,OAAO,CAAC,CAAC;gBACtB,MAAM,CAAC,IAAI,KAAK,CAAC,gCAAgC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YACrE,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;gBACxB,YAAY,CAAC,OAAO,CAAC,CAAC;gBACtB,IAAI,QAAQ,EAAE,CAAC;oBACb,MAAM,CAAC,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC,CAAC;oBACzD,OAAO;gBACT,CAAC;gBACD,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;oBACf,OAAO,CAAC,MAAM,CAAC,CAAC;gBAClB,CAAC;qBAAM,CAAC;oBACN,MAAM,CAAC,IAAI,KAAK,CAAC,+BAA+B,IAAI,KAAK,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;gBACxF,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"emberStreamJson.parser.d.ts","sourceRoot":"","sources":["../../../../../src/modules/ember-chat/interface-adapters/gateways/emberStreamJson.parser.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1B,OAAO,CAAC,EAAE;QACR,OAAO,CAAC,EAAE,KAAK,CAAC;YAAE,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,GAAG,MAAM,CAAC;QAC3D,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC7B,CAAC;CACH;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAUzE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,GAAG,IAAI,CAgBjE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAQ9D"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"emberStreamJson.parser.js","sourceRoot":"","sources":["../../../../../src/modules/ember-chat/interface-adapters/gateways/emberStreamJson.parser.ts"],"names":[],"mappings":"AAWA,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,IAAI,CAAC;QACH,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;YAClD,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAsB;IAChD,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QACnC,OAAO,KAAK,CAAC,IAAI,CAAC;IACpB,CAAC;IACD,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,OAAO,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtE,OAAO,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC;IAC1B,CAAC;IACD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC;IACvC,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,OAAO;aACjB,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC;aACvE,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC;aAC9B,IAAI,CAAC,EAAE,CAAC,CAAC;QACZ,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IACvC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAsB;IACnD,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;QAC7D,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,KAAK,eAAe,EAAE,CAAC;QACjE,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,KAAK,WAAW,IAAI,KAAK,CAAC,OAAO,EAAE,WAAW,KAAK,UAAU,CAAC;AACjF,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"generateAiInsights.usecase.d.ts","sourceRoot":"","sources":["../../../../../src/modules/statistics-insights/usecases/insights/generateAiInsights.usecase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+DAA+D,CAAC;AAClG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oEAAoE,CAAC;AAC1G,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kEAAkE,CAAC;AAC1G,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,uEAAuE,CAAC;AAC1H,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6DAA6D,CAAC;AACpG,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,8DAA8D,CAAC;AAK7F,MAAM,MAAM,aAAa,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAEhE,UAAU,uBAAuB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,YAAY,CAAC;IAC3B,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,4BAA4B,EAAE,4BAA4B,CAAC;IAC3D,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,aAAa,CAAC;IAC7B,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAsBD,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,uBAAuB,GAC7B,OAAO,CAAC,gBAAgB,CAAC,CAgD3B;AAED,UAAU,sBAAsB;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,gBAAgB,CAAC;IAC7B,YAAY,EAAE,YAAY,CAAC;IAC3B,eAAe,EAAE,eAAe,CAAC;CAClC;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,sBAAsB,GAAG,IAAI,CAa3E"}
@@ -1,65 +0,0 @@
1
- import { aiInsightsRawResponseSchema } from '../../../../modules/statistics-insights/entities/insight/aiInsight.schema.js';
2
- import { buildAiInsightsPrompt } from '../../../../modules/statistics-insights/usecases/insights/buildAiInsightsPrompt.js';
3
- import { computeInsightsWithPersistence } from '../../../../modules/statistics-insights/usecases/insights/computeInsightsWithPersistence.usecase.js';
4
- function stripMarkdownFences(text) {
5
- let cleaned = text.trim();
6
- if (cleaned.startsWith('```')) {
7
- const firstNewline = cleaned.indexOf('\n');
8
- if (firstNewline !== -1) {
9
- cleaned = cleaned.substring(firstNewline + 1);
10
- }
11
- }
12
- if (cleaned.endsWith('```')) {
13
- cleaned = cleaned.substring(0, cleaned.length - 3).trim();
14
- }
15
- return cleaned;
16
- }
17
- function parseClaudeResponse(rawOutput) {
18
- const cleaned = stripMarkdownFences(rawOutput);
19
- const parsed = JSON.parse(cleaned);
20
- return aiInsightsRawResponseSchema.parse(parsed);
21
- }
22
- export async function generateAiInsights(input) {
23
- const { projectPath, statsGateway, reviewFileGateway, reviewRequestTrackingGateway, logger, claudeInvoker, language, } = input;
24
- const stats = statsGateway.loadProjectStats(projectPath);
25
- if (!stats || stats.reviews.length === 0) {
26
- throw new Error('No review statistics available for this project');
27
- }
28
- const reviewFiles = await reviewFileGateway.listReviews(projectPath);
29
- const reviewContents = new Map();
30
- for (const reviewFile of reviewFiles) {
31
- const content = await reviewFileGateway.readReview(projectPath, reviewFile.filename);
32
- if (content) {
33
- reviewContents.set(reviewFile.mrNumber, content);
34
- }
35
- }
36
- const trackingData = reviewRequestTrackingGateway.loadTracking(projectPath);
37
- const trackedMrs = trackingData?.mrs ?? [];
38
- const prompt = buildAiInsightsPrompt({
39
- reviews: stats.reviews,
40
- reviewContents,
41
- trackedMrs,
42
- language,
43
- });
44
- logger.info({ promptLength: prompt.length }, 'Sending prompt to Claude for AI insights');
45
- const rawOutput = await claudeInvoker(prompt);
46
- logger.info({ outputLength: rawOutput.length }, 'Received Claude response for AI insights');
47
- const result = parseClaudeResponse(rawOutput);
48
- return {
49
- ...result,
50
- generatedAt: new Date().toISOString(),
51
- };
52
- }
53
- export function persistAiInsightsResult(input) {
54
- const { projectPath, aiInsights, statsGateway, insightsGateway } = input;
55
- const existingData = insightsGateway.loadPersistedInsights(projectPath);
56
- const stats = statsGateway.loadProjectStats(projectPath);
57
- const currentReviews = stats?.reviews ?? [];
58
- const upToDateResult = computeInsightsWithPersistence(currentReviews, existingData);
59
- insightsGateway.savePersistedInsights(projectPath, {
60
- ...upToDateResult.persistedData,
61
- aiInsights,
62
- reviewCountAtAiGeneration: upToDateResult.persistedData.processedReviewIds.length,
63
- });
64
- }
65
- //# sourceMappingURL=generateAiInsights.usecase.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"generateAiInsights.usecase.js","sourceRoot":"","sources":["../../../../../src/modules/statistics-insights/usecases/insights/generateAiInsights.usecase.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=emberStreamJson.parser.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"emberStreamJson.parser.test.d.ts","sourceRoot":"","sources":["../../../../../../src/tests/units/modules/ember-chat/gateways/emberStreamJson.parser.test.ts"],"names":[],"mappings":""}
@@ -1 +0,0 @@
1
- {"version":3,"file":"emberStreamJson.parser.test.js","sourceRoot":"","sources":["../../../../../../src/tests/units/modules/ember-chat/gateways/emberStreamJson.parser.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=generateAiInsights.usecase.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"generateAiInsights.usecase.test.d.ts","sourceRoot":"","sources":["../../../../../src/tests/units/usecases/insights/generateAiInsights.usecase.test.ts"],"names":[],"mappings":""}
@@ -1,253 +0,0 @@
1
- import { describe, it, expect, beforeEach } from 'vitest';
2
- import { generateAiInsights, persistAiInsightsResult } from '../../../../modules/statistics-insights/usecases/insights/generateAiInsights.usecase.js';
3
- import { InMemoryStatsGateway } from '../../../../tests/stubs/stats.stub.js';
4
- import { InMemoryReviewFileGateway } from '../../../../tests/stubs/reviewFile.stub.js';
5
- import { InMemoryReviewRequestTrackingGateway } from '../../../../tests/stubs/reviewRequestTracking.stub.js';
6
- import { InMemoryInsightsGateway } from '../../../../tests/stubs/insights.stub.js';
7
- import { createStubLogger } from '../../../../tests/stubs/logger.stub.js';
8
- import { ProjectStatsFactory, ReviewStatsFactory } from '../../../../tests/factories/projectStats.factory.js';
9
- import { TrackedMrFactory, MrTrackingDataFactory } from '../../../../tests/factories/trackedMr.factory.js';
10
- function createSuccessfulClaudeInvoker(result) {
11
- return async () => JSON.stringify(result);
12
- }
13
- function createFailingClaudeInvoker(errorMessage) {
14
- return async () => { throw new Error(errorMessage); };
15
- }
16
- const validAiResult = {
17
- developers: [
18
- {
19
- developerName: 'alice',
20
- title: 'Le Chirurgien du Code',
21
- titleExplanation: 'Precise and methodical',
22
- strengths: ['Excellent test coverage'],
23
- weaknesses: ['Slow review turnaround'],
24
- recommendations: ['Automate repetitive checks'],
25
- summary: 'Alice is a meticulous developer.',
26
- },
27
- ],
28
- team: {
29
- summary: 'A well-balanced team.',
30
- strengths: ['Strong testing culture'],
31
- weaknesses: ['Documentation gaps'],
32
- recommendations: ['Establish review guidelines'],
33
- dynamics: 'Good team dynamics.',
34
- },
35
- generatedAt: '2026-03-15T10:00:00Z',
36
- };
37
- describe('generateAiInsights', () => {
38
- let statsGateway;
39
- let reviewFileGateway;
40
- let reviewRequestTrackingGateway;
41
- beforeEach(() => {
42
- statsGateway = new InMemoryStatsGateway();
43
- reviewFileGateway = new InMemoryReviewFileGateway();
44
- reviewRequestTrackingGateway = new InMemoryReviewRequestTrackingGateway();
45
- });
46
- it('should return AI insights when Claude returns valid JSON', async () => {
47
- const reviews = [
48
- ReviewStatsFactory.create({ id: 'r1', assignedBy: 'alice', mrNumber: 1, score: 8 }),
49
- ];
50
- statsGateway.saveProjectStats('/test/project', ProjectStatsFactory.withReviews(reviews));
51
- const result = await generateAiInsights({
52
- projectPath: '/test/project',
53
- statsGateway,
54
- reviewFileGateway,
55
- reviewRequestTrackingGateway,
56
- logger: createStubLogger(),
57
- claudeInvoker: createSuccessfulClaudeInvoker(validAiResult),
58
- language: 'fr',
59
- });
60
- expect(result.developers).toHaveLength(1);
61
- expect(result.developers[0].developerName).toBe('alice');
62
- expect(result.team.summary).toBe('A well-balanced team.');
63
- expect(result.generatedAt).toBeDefined();
64
- });
65
- it('should throw when Claude invocation fails', async () => {
66
- const reviews = [
67
- ReviewStatsFactory.create({ assignedBy: 'alice', mrNumber: 1, score: 8 }),
68
- ];
69
- statsGateway.saveProjectStats('/test/project', ProjectStatsFactory.withReviews(reviews));
70
- await expect(generateAiInsights({
71
- projectPath: '/test/project',
72
- statsGateway,
73
- reviewFileGateway,
74
- reviewRequestTrackingGateway,
75
- logger: createStubLogger(),
76
- claudeInvoker: createFailingClaudeInvoker('Claude CLI not found'),
77
- language: 'fr',
78
- })).rejects.toThrow();
79
- });
80
- it('should throw when Claude returns invalid JSON', async () => {
81
- const reviews = [
82
- ReviewStatsFactory.create({ assignedBy: 'alice', mrNumber: 1, score: 8 }),
83
- ];
84
- statsGateway.saveProjectStats('/test/project', ProjectStatsFactory.withReviews(reviews));
85
- const invalidJsonInvoker = async () => 'this is not JSON';
86
- await expect(generateAiInsights({
87
- projectPath: '/test/project',
88
- statsGateway,
89
- reviewFileGateway,
90
- reviewRequestTrackingGateway,
91
- logger: createStubLogger(),
92
- claudeInvoker: invalidJsonInvoker,
93
- language: 'fr',
94
- })).rejects.toThrow();
95
- });
96
- it('should throw when no stats exist for the project', async () => {
97
- await expect(generateAiInsights({
98
- projectPath: '/empty/project',
99
- statsGateway,
100
- reviewFileGateway,
101
- reviewRequestTrackingGateway,
102
- logger: createStubLogger(),
103
- claudeInvoker: createSuccessfulClaudeInvoker(validAiResult),
104
- language: 'fr',
105
- })).rejects.toThrow();
106
- });
107
- it('should include review file content in the prompt sent to Claude', async () => {
108
- const reviews = [
109
- ReviewStatsFactory.create({ id: 'r1', assignedBy: 'alice', mrNumber: 42, score: 8 }),
110
- ];
111
- statsGateway.saveProjectStats('/test/project', ProjectStatsFactory.withReviews(reviews));
112
- const reviewContent = '# Code Review - MR !42\n\n## Synthèse Exécutive\n\n| Audit | Score |\n|---|---|\n| Testing | 9/10 |\n\n**Score Global : 8/10**\n\n## Constats Positifs\n\n### 1. Great test coverage\n';
113
- reviewFileGateway.addReview('/test/project', '2026-03-13-MR-42-review.md', reviewContent);
114
- let capturedPrompt = '';
115
- const capturingInvoker = async (prompt) => {
116
- capturedPrompt = prompt;
117
- return JSON.stringify(validAiResult);
118
- };
119
- await generateAiInsights({
120
- projectPath: '/test/project',
121
- statsGateway,
122
- reviewFileGateway,
123
- reviewRequestTrackingGateway,
124
- logger: createStubLogger(),
125
- claudeInvoker: capturingInvoker,
126
- language: 'fr',
127
- });
128
- expect(capturedPrompt).toContain('Synthèse Exécutive');
129
- expect(capturedPrompt).toContain('Score Global');
130
- expect(capturedPrompt).toContain('Great test coverage');
131
- });
132
- it('should include tracked MR data in the prompt', async () => {
133
- const reviews = [
134
- ReviewStatsFactory.create({ id: 'r1', assignedBy: 'alice', mrNumber: 1, score: 8 }),
135
- ];
136
- statsGateway.saveProjectStats('/test/project', ProjectStatsFactory.withReviews(reviews));
137
- const trackedMr = TrackedMrFactory.create({
138
- assignment: { username: 'alice', assignedAt: '2024-01-15T10:00:00Z' },
139
- totalReviews: 5,
140
- });
141
- reviewRequestTrackingGateway.saveTracking('/test/project', MrTrackingDataFactory.withMrs([trackedMr]));
142
- let capturedPrompt = '';
143
- const capturingInvoker = async (prompt) => {
144
- capturedPrompt = prompt;
145
- return JSON.stringify(validAiResult);
146
- };
147
- await generateAiInsights({
148
- projectPath: '/test/project',
149
- statsGateway,
150
- reviewFileGateway,
151
- reviewRequestTrackingGateway,
152
- logger: createStubLogger(),
153
- claudeInvoker: capturingInvoker,
154
- language: 'fr',
155
- });
156
- expect(capturedPrompt).toContain('alice');
157
- });
158
- it('should set generatedAt in the result', async () => {
159
- const reviews = [
160
- ReviewStatsFactory.create({ id: 'r1', assignedBy: 'alice', mrNumber: 1, score: 8 }),
161
- ];
162
- statsGateway.saveProjectStats('/test/project', ProjectStatsFactory.withReviews(reviews));
163
- const result = await generateAiInsights({
164
- projectPath: '/test/project',
165
- statsGateway,
166
- reviewFileGateway,
167
- reviewRequestTrackingGateway,
168
- logger: createStubLogger(),
169
- claudeInvoker: createSuccessfulClaudeInvoker(validAiResult),
170
- language: 'fr',
171
- });
172
- expect(result.generatedAt).toBeDefined();
173
- expect(new Date(result.generatedAt).getTime()).not.toBeNaN();
174
- });
175
- it('should handle Claude response with markdown fences around JSON', async () => {
176
- const reviews = [
177
- ReviewStatsFactory.create({ id: 'r1', assignedBy: 'alice', mrNumber: 1, score: 8 }),
178
- ];
179
- statsGateway.saveProjectStats('/test/project', ProjectStatsFactory.withReviews(reviews));
180
- const fencedInvoker = async () => '```json\n' + JSON.stringify(validAiResult) + '\n```';
181
- const result = await generateAiInsights({
182
- projectPath: '/test/project',
183
- statsGateway,
184
- reviewFileGateway,
185
- reviewRequestTrackingGateway,
186
- logger: createStubLogger(),
187
- claudeInvoker: fencedInvoker,
188
- language: 'fr',
189
- });
190
- expect(result.developers).toHaveLength(1);
191
- });
192
- });
193
- describe('persistAiInsightsResult', () => {
194
- let statsGateway;
195
- let insightsGateway;
196
- beforeEach(() => {
197
- statsGateway = new InMemoryStatsGateway();
198
- insightsGateway = new InMemoryInsightsGateway();
199
- });
200
- it('should save AI insights into persisted data', () => {
201
- const reviews = [
202
- ReviewStatsFactory.create({ id: 'r1', assignedBy: 'alice', mrNumber: 1, score: 8 }),
203
- ];
204
- statsGateway.saveProjectStats('/test/project', ProjectStatsFactory.withReviews(reviews));
205
- persistAiInsightsResult({
206
- projectPath: '/test/project',
207
- aiInsights: validAiResult,
208
- statsGateway,
209
- insightsGateway,
210
- });
211
- const persisted = insightsGateway.loadPersistedInsights('/test/project');
212
- expect(persisted).not.toBeNull();
213
- expect(persisted?.aiInsights).toEqual(validAiResult);
214
- });
215
- it('should update review count at AI generation', () => {
216
- const reviews = [
217
- ReviewStatsFactory.create({ id: 'r1', assignedBy: 'alice', mrNumber: 1, score: 8 }),
218
- ReviewStatsFactory.create({ id: 'r2', assignedBy: 'alice', mrNumber: 2, score: 7 }),
219
- ];
220
- statsGateway.saveProjectStats('/test/project', ProjectStatsFactory.withReviews(reviews));
221
- persistAiInsightsResult({
222
- projectPath: '/test/project',
223
- aiInsights: validAiResult,
224
- statsGateway,
225
- insightsGateway,
226
- });
227
- const persisted = insightsGateway.loadPersistedInsights('/test/project');
228
- expect(persisted?.reviewCountAtAiGeneration).toBe(2);
229
- });
230
- it('should preserve existing persisted data when updating with AI insights', () => {
231
- const reviews = [
232
- ReviewStatsFactory.create({ id: 'r1', assignedBy: 'alice', mrNumber: 1, score: 8 }),
233
- ];
234
- statsGateway.saveProjectStats('/test/project', ProjectStatsFactory.withReviews(reviews));
235
- insightsGateway.savePersistedInsights('/test/project', {
236
- developers: [],
237
- processedReviewIds: ['r1'],
238
- lastUpdated: '2026-03-15T10:00:00Z',
239
- aiInsights: null,
240
- reviewCountAtAiGeneration: 0,
241
- });
242
- persistAiInsightsResult({
243
- projectPath: '/test/project',
244
- aiInsights: validAiResult,
245
- statsGateway,
246
- insightsGateway,
247
- });
248
- const persisted = insightsGateway.loadPersistedInsights('/test/project');
249
- expect(persisted?.processedReviewIds).toContain('r1');
250
- expect(persisted?.aiInsights).toEqual(validAiResult);
251
- });
252
- });
253
- //# sourceMappingURL=generateAiInsights.usecase.test.js.map