cadr-cli 2.0.0 → 2.0.2
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.
- package/dist/adr/adr.d.ts +17 -0
- package/dist/adr/adr.d.ts.map +1 -0
- package/dist/{adr.js → adr/adr.js} +4 -44
- package/dist/adr/adr.js.map +1 -0
- package/dist/adr/adr.test.d.ts +5 -0
- package/dist/{adr.test.d.ts.map → adr/adr.test.d.ts.map} +1 -1
- package/dist/{adr.test.js → adr/adr.test.js} +0 -14
- package/dist/adr/adr.test.js.map +1 -0
- package/dist/adr/index.d.ts +2 -0
- package/dist/adr/index.d.ts.map +1 -0
- package/dist/adr/index.js +18 -0
- package/dist/adr/index.js.map +1 -0
- package/dist/analysis/analysis.orchestrator.d.ts +14 -0
- package/dist/analysis/analysis.orchestrator.d.ts.map +1 -0
- package/dist/analysis/analysis.orchestrator.js +175 -0
- package/dist/analysis/analysis.orchestrator.js.map +1 -0
- package/dist/analysis/analysis.orchestrator.test.d.ts +2 -0
- package/dist/analysis/analysis.orchestrator.test.d.ts.map +1 -0
- package/dist/analysis/analysis.orchestrator.test.js +177 -0
- package/dist/analysis/analysis.orchestrator.test.js.map +1 -0
- package/dist/analysis/strategies/git-strategy.d.ts +22 -0
- package/dist/analysis/strategies/git-strategy.d.ts.map +1 -0
- package/dist/analysis/strategies/git-strategy.js +114 -0
- package/dist/analysis/strategies/git-strategy.js.map +1 -0
- package/dist/analysis/strategies/git-strategy.test.d.ts +2 -0
- package/dist/analysis/strategies/git-strategy.test.d.ts.map +1 -0
- package/dist/analysis/strategies/git-strategy.test.js +147 -0
- package/dist/analysis/strategies/git-strategy.test.js.map +1 -0
- package/dist/commands/analyze.js +3 -3
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/analyze.test.d.ts +2 -0
- package/dist/commands/analyze.test.d.ts.map +1 -0
- package/dist/commands/analyze.test.js +70 -0
- package/dist/commands/analyze.test.js.map +1 -0
- package/dist/commands/init.test.js +128 -2
- package/dist/commands/init.test.js.map +1 -1
- package/dist/config.test.js +167 -0
- package/dist/config.test.js.map +1 -1
- package/dist/git/git.errors.d.ts +6 -0
- package/dist/git/git.errors.d.ts.map +1 -0
- package/dist/git/git.errors.js +15 -0
- package/dist/git/git.errors.js.map +1 -0
- package/dist/git/git.errors.test.d.ts +2 -0
- package/dist/git/git.errors.test.d.ts.map +1 -0
- package/dist/git/git.errors.test.js +34 -0
- package/dist/git/git.errors.test.js.map +1 -0
- package/dist/git/git.operations.d.ts +12 -0
- package/dist/git/git.operations.d.ts.map +1 -0
- package/dist/git/git.operations.js +64 -0
- package/dist/git/git.operations.js.map +1 -0
- package/dist/git/git.operations.test.d.ts +2 -0
- package/dist/git/git.operations.test.d.ts.map +1 -0
- package/dist/git/git.operations.test.js +164 -0
- package/dist/git/git.operations.test.js.map +1 -0
- package/dist/git/index.d.ts +4 -0
- package/dist/git/index.d.ts.map +1 -0
- package/dist/git/index.js +19 -0
- package/dist/git/index.js.map +1 -0
- package/dist/llm/index.d.ts +3 -0
- package/dist/llm/index.d.ts.map +1 -0
- package/dist/llm/index.js +19 -0
- package/dist/llm/index.js.map +1 -0
- package/dist/llm/llm.d.ts +35 -0
- package/dist/llm/llm.d.ts.map +1 -0
- package/dist/{llm.js → llm/llm.js} +16 -58
- package/dist/llm/llm.js.map +1 -0
- package/dist/{llm.test.d.ts.map → llm/llm.test.d.ts.map} +1 -1
- package/dist/llm/llm.test.js +224 -0
- package/dist/llm/llm.test.js.map +1 -0
- package/dist/{prompts.d.ts → llm/prompts.d.ts} +1 -38
- package/dist/llm/prompts.d.ts.map +1 -0
- package/dist/{prompts.js → llm/prompts.js} +9 -54
- package/dist/llm/prompts.js.map +1 -0
- package/dist/llm/response-parser.d.ts +9 -0
- package/dist/llm/response-parser.d.ts.map +1 -0
- package/dist/llm/response-parser.js +67 -0
- package/dist/llm/response-parser.js.map +1 -0
- package/dist/llm/response-parser.test.d.ts +2 -0
- package/dist/llm/response-parser.test.d.ts.map +1 -0
- package/dist/llm/response-parser.test.js +134 -0
- package/dist/llm/response-parser.test.js.map +1 -0
- package/dist/presenters/console-presenter.d.ts +35 -0
- package/dist/presenters/console-presenter.d.ts.map +1 -0
- package/dist/presenters/console-presenter.js +114 -0
- package/dist/presenters/console-presenter.js.map +1 -0
- package/dist/presenters/console-presenter.test.d.ts +2 -0
- package/dist/presenters/console-presenter.test.d.ts.map +1 -0
- package/dist/presenters/console-presenter.test.js +227 -0
- package/dist/presenters/console-presenter.test.js.map +1 -0
- package/dist/version.test.d.ts +1 -2
- package/dist/version.test.d.ts.map +1 -1
- package/dist/version.test.js +29 -16
- package/dist/version.test.js.map +1 -1
- package/package.json +1 -1
- package/src/{adr.test.ts → adr/adr.test.ts} +10 -23
- package/src/{adr.ts → adr/adr.ts} +7 -48
- package/src/adr/index.ts +1 -0
- package/src/analysis/analysis.orchestrator.test.ts +237 -0
- package/src/analysis/analysis.orchestrator.ts +175 -0
- package/src/analysis/strategies/git-strategy.test.ts +210 -0
- package/src/analysis/strategies/git-strategy.ts +106 -0
- package/src/commands/analyze.test.ts +91 -0
- package/src/commands/analyze.ts +8 -9
- package/src/commands/init.test.ts +200 -5
- package/src/config.test.ts +232 -2
- package/src/git/git.errors.test.ts +43 -0
- package/src/git/git.errors.ts +10 -0
- package/src/git/git.operations.test.ts +222 -0
- package/src/git/git.operations.ts +85 -0
- package/src/git/index.ts +3 -0
- package/src/llm/index.ts +2 -0
- package/src/llm/llm.test.ts +315 -0
- package/src/{llm.ts → llm/llm.ts} +46 -107
- package/src/{prompts.ts → llm/prompts.ts} +30 -72
- package/src/llm/response-parser.test.ts +170 -0
- package/src/llm/response-parser.ts +90 -0
- package/src/presenters/console-presenter.test.ts +259 -0
- package/src/presenters/console-presenter.ts +152 -0
- package/src/version.test.ts +30 -16
- package/dist/adr.d.ts +0 -50
- package/dist/adr.d.ts.map +0 -1
- package/dist/adr.js.map +0 -1
- package/dist/adr.test.d.ts +0 -8
- package/dist/adr.test.js.map +0 -1
- package/dist/analysis.d.ts +0 -24
- package/dist/analysis.d.ts.map +0 -1
- package/dist/analysis.js +0 -281
- package/dist/analysis.js.map +0 -1
- package/dist/analysis.test.d.ts +0 -8
- package/dist/analysis.test.d.ts.map +0 -1
- package/dist/analysis.test.js +0 -351
- package/dist/analysis.test.js.map +0 -1
- package/dist/git.d.ts +0 -54
- package/dist/git.d.ts.map +0 -1
- package/dist/git.js +0 -204
- package/dist/git.js.map +0 -1
- package/dist/llm.d.ts +0 -73
- package/dist/llm.d.ts.map +0 -1
- package/dist/llm.js.map +0 -1
- package/dist/llm.test.js +0 -592
- package/dist/llm.test.js.map +0 -1
- package/dist/prompts.d.ts.map +0 -1
- package/dist/prompts.js.map +0 -1
- package/dist/prompts.test.d.ts +0 -2
- package/dist/prompts.test.d.ts.map +0 -1
- package/dist/prompts.test.js +0 -427
- package/dist/prompts.test.js.map +0 -1
- package/src/analysis.test.ts +0 -396
- package/src/analysis.ts +0 -262
- package/src/git.ts +0 -300
- package/src/llm.test.ts +0 -701
- package/src/prompts.test.ts +0 -515
- /package/dist/{llm.test.d.ts → llm/llm.test.d.ts} +0 -0
package/dist/llm.d.ts
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LLM Client Module
|
|
3
|
-
*
|
|
4
|
-
* Provider-based wrapper for analyzing code changes.
|
|
5
|
-
* Implements fail-open error handling per constitution requirements.
|
|
6
|
-
*/
|
|
7
|
-
import { AnalysisConfig } from './config';
|
|
8
|
-
/**
|
|
9
|
-
* Analysis request data structure
|
|
10
|
-
*/
|
|
11
|
-
export interface AnalysisRequest {
|
|
12
|
-
file_paths: string[];
|
|
13
|
-
diff_content: string;
|
|
14
|
-
repository_context: string;
|
|
15
|
-
analysis_prompt: string;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Analysis result from LLM
|
|
19
|
-
*/
|
|
20
|
-
export interface AnalysisResult {
|
|
21
|
-
is_significant: boolean;
|
|
22
|
-
reason: string;
|
|
23
|
-
confidence?: number;
|
|
24
|
-
timestamp: string;
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Analysis response including potential errors
|
|
28
|
-
*/
|
|
29
|
-
export interface AnalysisResponse {
|
|
30
|
-
result: AnalysisResult | null;
|
|
31
|
-
error?: string;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Analyze staged changes using OpenAI LLM
|
|
35
|
-
*
|
|
36
|
-
* @param config - Analysis configuration with API settings
|
|
37
|
-
* @param request - Analysis request with code changes
|
|
38
|
-
* @returns Promise resolving to analysis response with result or error
|
|
39
|
-
*/
|
|
40
|
-
export declare function analyzeChanges(config: AnalysisConfig, request: AnalysisRequest): Promise<AnalysisResponse>;
|
|
41
|
-
/**
|
|
42
|
-
* Generation request data structure
|
|
43
|
-
*/
|
|
44
|
-
export interface GenerationRequest {
|
|
45
|
-
file_paths: string[];
|
|
46
|
-
diff_content: string;
|
|
47
|
-
reason: string;
|
|
48
|
-
generation_prompt: string;
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Generation result from LLM
|
|
52
|
-
*/
|
|
53
|
-
export interface GenerationResult {
|
|
54
|
-
content: string;
|
|
55
|
-
title: string;
|
|
56
|
-
timestamp: string;
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Generation response including potential errors
|
|
60
|
-
*/
|
|
61
|
-
export interface GenerationResponse {
|
|
62
|
-
result: GenerationResult | null;
|
|
63
|
-
error?: string;
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Generate ADR content using LLM
|
|
67
|
-
*
|
|
68
|
-
* @param config - Analysis configuration with API settings
|
|
69
|
-
* @param request - Generation request with code changes
|
|
70
|
-
* @returns Promise resolving to generation response with result or error
|
|
71
|
-
*/
|
|
72
|
-
export declare function generateADRContent(config: AnalysisConfig, request: GenerationRequest): Promise<GenerationResponse>;
|
|
73
|
-
//# sourceMappingURL=llm.d.ts.map
|
package/dist/llm.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"llm.d.ts","sourceRoot":"","sources":["../src/llm.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAG1C;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;CACzB;AAUD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,cAAc,EAAE,OAAO,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,gBAAgB,CAAC,CAiK3B;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,kBAAkB,CAAC,CAsF7B"}
|
package/dist/llm.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"llm.js","sourceRoot":"","sources":["../src/llm.ts"],"names":[],"mappings":"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"}
|
package/dist/llm.test.js
DELETED
|
@@ -1,592 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const llm_1 = require("./llm");
|
|
7
|
-
const openai_1 = __importDefault(require("openai"));
|
|
8
|
-
const generative_ai_1 = require("@google/generative-ai");
|
|
9
|
-
// Mock OpenAI module
|
|
10
|
-
jest.mock('openai');
|
|
11
|
-
jest.mock('@google/generative-ai');
|
|
12
|
-
describe('LLM Client Module', () => {
|
|
13
|
-
const mockConfig = {
|
|
14
|
-
provider: 'openai',
|
|
15
|
-
analysis_model: 'gpt-4',
|
|
16
|
-
api_key_env: 'OPENAI_API_KEY',
|
|
17
|
-
timeout_seconds: 15
|
|
18
|
-
};
|
|
19
|
-
const mockRequest = {
|
|
20
|
-
file_paths: ['src/auth.ts', 'src/user.ts'],
|
|
21
|
-
diff_content: `
|
|
22
|
-
diff --git a/src/auth.ts b/src/auth.ts
|
|
23
|
-
+export function authenticateUser(token: string) {
|
|
24
|
-
+ return jwt.verify(token);
|
|
25
|
-
+}
|
|
26
|
-
`,
|
|
27
|
-
repository_context: 'my-app',
|
|
28
|
-
analysis_prompt: 'Analyze these changes...'
|
|
29
|
-
};
|
|
30
|
-
beforeEach(() => {
|
|
31
|
-
jest.clearAllMocks();
|
|
32
|
-
process.env.OPENAI_API_KEY = 'test-api-key';
|
|
33
|
-
});
|
|
34
|
-
afterEach(() => {
|
|
35
|
-
delete process.env.OPENAI_API_KEY;
|
|
36
|
-
});
|
|
37
|
-
describe('analyzeChanges', () => {
|
|
38
|
-
test('analyzes changes successfully with significant result', async () => {
|
|
39
|
-
const mockResponse = {
|
|
40
|
-
choices: [{
|
|
41
|
-
message: {
|
|
42
|
-
content: JSON.stringify({
|
|
43
|
-
is_significant: true,
|
|
44
|
-
reason: 'Introduces new authentication system'
|
|
45
|
-
})
|
|
46
|
-
}
|
|
47
|
-
}]
|
|
48
|
-
};
|
|
49
|
-
const mockCreate = jest.fn().mockResolvedValue(mockResponse);
|
|
50
|
-
openai_1.default.mockImplementation(() => ({
|
|
51
|
-
chat: {
|
|
52
|
-
completions: {
|
|
53
|
-
create: mockCreate
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}));
|
|
57
|
-
const response = await (0, llm_1.analyzeChanges)(mockConfig, mockRequest);
|
|
58
|
-
expect(response.result).not.toBeNull();
|
|
59
|
-
expect(response.error).toBeUndefined();
|
|
60
|
-
expect(response.result?.is_significant).toBe(true);
|
|
61
|
-
expect(response.result?.reason).toContain('authentication');
|
|
62
|
-
expect(response.result?.timestamp).toBeDefined();
|
|
63
|
-
expect(mockCreate).toHaveBeenCalledWith(expect.objectContaining({
|
|
64
|
-
model: 'gpt-4',
|
|
65
|
-
messages: expect.any(Array)
|
|
66
|
-
}), expect.objectContaining({
|
|
67
|
-
timeout: 15000
|
|
68
|
-
}));
|
|
69
|
-
});
|
|
70
|
-
test('analyzes changes successfully with non-significant result', async () => {
|
|
71
|
-
const mockResponse = {
|
|
72
|
-
choices: [{
|
|
73
|
-
message: {
|
|
74
|
-
content: JSON.stringify({
|
|
75
|
-
is_significant: false,
|
|
76
|
-
reason: 'Minor documentation updates'
|
|
77
|
-
})
|
|
78
|
-
}
|
|
79
|
-
}]
|
|
80
|
-
};
|
|
81
|
-
const mockCreate = jest.fn().mockResolvedValue(mockResponse);
|
|
82
|
-
openai_1.default.mockImplementation(() => ({
|
|
83
|
-
chat: {
|
|
84
|
-
completions: {
|
|
85
|
-
create: mockCreate
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}));
|
|
89
|
-
const response = await (0, llm_1.analyzeChanges)(mockConfig, mockRequest);
|
|
90
|
-
expect(response.result).not.toBeNull();
|
|
91
|
-
expect(response.error).toBeUndefined();
|
|
92
|
-
expect(response.result?.is_significant).toBe(false);
|
|
93
|
-
expect(response.result?.reason).toContain('documentation');
|
|
94
|
-
});
|
|
95
|
-
test('accepts empty reason when change is not significant', async () => {
|
|
96
|
-
const mockResponse = {
|
|
97
|
-
choices: [{
|
|
98
|
-
message: {
|
|
99
|
-
content: JSON.stringify({
|
|
100
|
-
is_significant: false,
|
|
101
|
-
reason: ''
|
|
102
|
-
})
|
|
103
|
-
}
|
|
104
|
-
}]
|
|
105
|
-
};
|
|
106
|
-
const mockCreate = jest.fn().mockResolvedValue(mockResponse);
|
|
107
|
-
openai_1.default.mockImplementation(() => ({
|
|
108
|
-
chat: {
|
|
109
|
-
completions: {
|
|
110
|
-
create: mockCreate
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}));
|
|
114
|
-
const response = await (0, llm_1.analyzeChanges)(mockConfig, mockRequest);
|
|
115
|
-
expect(response.result).not.toBeNull();
|
|
116
|
-
expect(response.error).toBeUndefined();
|
|
117
|
-
expect(response.result?.is_significant).toBe(false);
|
|
118
|
-
expect(response.result?.reason).toBe('');
|
|
119
|
-
});
|
|
120
|
-
test('rejects empty reason when change is significant', async () => {
|
|
121
|
-
const mockResponse = {
|
|
122
|
-
choices: [{
|
|
123
|
-
message: {
|
|
124
|
-
content: JSON.stringify({
|
|
125
|
-
is_significant: true,
|
|
126
|
-
reason: ''
|
|
127
|
-
})
|
|
128
|
-
}
|
|
129
|
-
}]
|
|
130
|
-
};
|
|
131
|
-
const mockCreate = jest.fn().mockResolvedValue(mockResponse);
|
|
132
|
-
openai_1.default.mockImplementation(() => ({
|
|
133
|
-
chat: {
|
|
134
|
-
completions: {
|
|
135
|
-
create: mockCreate
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}));
|
|
139
|
-
const response = await (0, llm_1.analyzeChanges)(mockConfig, mockRequest);
|
|
140
|
-
expect(response.result).toBeNull();
|
|
141
|
-
expect(response.error).toBeDefined();
|
|
142
|
-
expect(response.error).toContain('no reason');
|
|
143
|
-
});
|
|
144
|
-
test('handles API failures gracefully (fail-open)', async () => {
|
|
145
|
-
const mockCreate = jest.fn().mockRejectedValue(new Error('API Error'));
|
|
146
|
-
openai_1.default.mockImplementation(() => ({
|
|
147
|
-
chat: {
|
|
148
|
-
completions: {
|
|
149
|
-
create: mockCreate
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}));
|
|
153
|
-
const response = await (0, llm_1.analyzeChanges)(mockConfig, mockRequest);
|
|
154
|
-
// Fail-open: should return error, not throw
|
|
155
|
-
expect(response.result).toBeNull();
|
|
156
|
-
expect(response.error).toBeDefined();
|
|
157
|
-
expect(response.error).toContain('API error');
|
|
158
|
-
expect(mockCreate).toHaveBeenCalled();
|
|
159
|
-
});
|
|
160
|
-
test('handles rate limiting gracefully', async () => {
|
|
161
|
-
const rateLimitError = new Error('Rate limit exceeded');
|
|
162
|
-
rateLimitError.status = 429;
|
|
163
|
-
const mockCreate = jest.fn().mockRejectedValue(rateLimitError);
|
|
164
|
-
openai_1.default.mockImplementation(() => ({
|
|
165
|
-
chat: {
|
|
166
|
-
completions: {
|
|
167
|
-
create: mockCreate
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}));
|
|
171
|
-
const response = await (0, llm_1.analyzeChanges)(mockConfig, mockRequest);
|
|
172
|
-
expect(response.result).toBeNull();
|
|
173
|
-
expect(response.error).toBeDefined();
|
|
174
|
-
expect(response.error).toContain('Rate limit exceeded');
|
|
175
|
-
});
|
|
176
|
-
test('handles timeout gracefully', async () => {
|
|
177
|
-
const timeoutError = new Error('Request timeout');
|
|
178
|
-
timeoutError.code = 'ETIMEDOUT';
|
|
179
|
-
const mockCreate = jest.fn().mockRejectedValue(timeoutError);
|
|
180
|
-
openai_1.default.mockImplementation(() => ({
|
|
181
|
-
chat: {
|
|
182
|
-
completions: {
|
|
183
|
-
create: mockCreate
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}));
|
|
187
|
-
const response = await (0, llm_1.analyzeChanges)(mockConfig, mockRequest);
|
|
188
|
-
expect(response.result).toBeNull();
|
|
189
|
-
expect(response.error).toBeDefined();
|
|
190
|
-
});
|
|
191
|
-
test('validates response format and rejects invalid JSON', async () => {
|
|
192
|
-
const mockResponse = {
|
|
193
|
-
choices: [{
|
|
194
|
-
message: {
|
|
195
|
-
content: 'This is not valid JSON'
|
|
196
|
-
}
|
|
197
|
-
}]
|
|
198
|
-
};
|
|
199
|
-
const mockCreate = jest.fn().mockResolvedValue(mockResponse);
|
|
200
|
-
openai_1.default.mockImplementation(() => ({
|
|
201
|
-
chat: {
|
|
202
|
-
completions: {
|
|
203
|
-
create: mockCreate
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}));
|
|
207
|
-
const response = await (0, llm_1.analyzeChanges)(mockConfig, mockRequest);
|
|
208
|
-
expect(response.result).toBeNull();
|
|
209
|
-
expect(response.error).toBeDefined();
|
|
210
|
-
});
|
|
211
|
-
test('validates response format and rejects missing fields', async () => {
|
|
212
|
-
const mockResponse = {
|
|
213
|
-
choices: [{
|
|
214
|
-
message: {
|
|
215
|
-
content: JSON.stringify({
|
|
216
|
-
is_significant: true
|
|
217
|
-
// Missing 'reason' field
|
|
218
|
-
})
|
|
219
|
-
}
|
|
220
|
-
}]
|
|
221
|
-
};
|
|
222
|
-
const mockCreate = jest.fn().mockResolvedValue(mockResponse);
|
|
223
|
-
openai_1.default.mockImplementation(() => ({
|
|
224
|
-
chat: {
|
|
225
|
-
completions: {
|
|
226
|
-
create: mockCreate
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}));
|
|
230
|
-
const response = await (0, llm_1.analyzeChanges)(mockConfig, mockRequest);
|
|
231
|
-
expect(response.result).toBeNull();
|
|
232
|
-
expect(response.error).toBeDefined();
|
|
233
|
-
});
|
|
234
|
-
test('respects timeout configuration', async () => {
|
|
235
|
-
const customConfig = { ...mockConfig, timeout_seconds: 5 };
|
|
236
|
-
const mockCreate = jest.fn().mockResolvedValue({
|
|
237
|
-
choices: [{
|
|
238
|
-
message: {
|
|
239
|
-
content: JSON.stringify({
|
|
240
|
-
is_significant: true,
|
|
241
|
-
reason: 'Test'
|
|
242
|
-
})
|
|
243
|
-
}
|
|
244
|
-
}]
|
|
245
|
-
});
|
|
246
|
-
openai_1.default.mockImplementation(() => ({
|
|
247
|
-
chat: {
|
|
248
|
-
completions: {
|
|
249
|
-
create: mockCreate
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
}));
|
|
253
|
-
await (0, llm_1.analyzeChanges)(customConfig, mockRequest);
|
|
254
|
-
// Verify timeout was passed to OpenAI client
|
|
255
|
-
expect(mockCreate).toHaveBeenCalledWith(expect.objectContaining({
|
|
256
|
-
model: 'gpt-4',
|
|
257
|
-
messages: expect.any(Array)
|
|
258
|
-
}), expect.objectContaining({
|
|
259
|
-
timeout: 5000 // milliseconds
|
|
260
|
-
}));
|
|
261
|
-
});
|
|
262
|
-
test('returns null when API key is missing', async () => {
|
|
263
|
-
delete process.env.OPENAI_API_KEY;
|
|
264
|
-
const response = await (0, llm_1.analyzeChanges)(mockConfig, mockRequest);
|
|
265
|
-
expect(response.result).toBeNull();
|
|
266
|
-
expect(response.error).toBeDefined();
|
|
267
|
-
});
|
|
268
|
-
test('includes confidence score when provided by LLM', async () => {
|
|
269
|
-
const mockResponse = {
|
|
270
|
-
choices: [{
|
|
271
|
-
message: {
|
|
272
|
-
content: JSON.stringify({
|
|
273
|
-
is_significant: true,
|
|
274
|
-
reason: 'Major change',
|
|
275
|
-
confidence: 0.95
|
|
276
|
-
})
|
|
277
|
-
}
|
|
278
|
-
}]
|
|
279
|
-
};
|
|
280
|
-
const mockCreate = jest.fn().mockResolvedValue(mockResponse);
|
|
281
|
-
openai_1.default.mockImplementation(() => ({
|
|
282
|
-
chat: {
|
|
283
|
-
completions: {
|
|
284
|
-
create: mockCreate
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
}));
|
|
288
|
-
const response = await (0, llm_1.analyzeChanges)(mockConfig, mockRequest);
|
|
289
|
-
expect(response.result).not.toBeNull();
|
|
290
|
-
expect(response.error).toBeUndefined();
|
|
291
|
-
expect(response.result?.confidence).toBe(0.95);
|
|
292
|
-
});
|
|
293
|
-
test('supports Gemini provider with valid JSON response', async () => {
|
|
294
|
-
const geminiConfig = {
|
|
295
|
-
provider: 'gemini',
|
|
296
|
-
analysis_model: 'gemini-1.5-pro',
|
|
297
|
-
api_key_env: 'GEMINI_API_KEY',
|
|
298
|
-
timeout_seconds: 10,
|
|
299
|
-
};
|
|
300
|
-
generative_ai_1.GoogleGenerativeAI.mockImplementation(() => ({
|
|
301
|
-
getGenerativeModel: () => ({
|
|
302
|
-
generateContent: jest.fn().mockResolvedValue({
|
|
303
|
-
response: {
|
|
304
|
-
text: () => JSON.stringify({ is_significant: false, reason: '' }),
|
|
305
|
-
},
|
|
306
|
-
}),
|
|
307
|
-
}),
|
|
308
|
-
}));
|
|
309
|
-
process.env.GEMINI_API_KEY = 'test-api-key';
|
|
310
|
-
const response = await (0, llm_1.analyzeChanges)(geminiConfig, mockRequest);
|
|
311
|
-
expect(response.result).not.toBeNull();
|
|
312
|
-
expect(response.result?.is_significant).toBe(false);
|
|
313
|
-
delete process.env.GEMINI_API_KEY;
|
|
314
|
-
});
|
|
315
|
-
test('handles Gemini timeout gracefully', async () => {
|
|
316
|
-
const geminiConfig = {
|
|
317
|
-
provider: 'gemini',
|
|
318
|
-
analysis_model: 'gemini-1.5-pro',
|
|
319
|
-
api_key_env: 'GEMINI_API_KEY',
|
|
320
|
-
timeout_seconds: 1,
|
|
321
|
-
};
|
|
322
|
-
const slowPromise = new Promise((resolve) => setTimeout(() => resolve({
|
|
323
|
-
response: { text: () => JSON.stringify({ is_significant: true, reason: 'Test' }) }
|
|
324
|
-
}), 3000));
|
|
325
|
-
generative_ai_1.GoogleGenerativeAI.mockImplementation(() => ({
|
|
326
|
-
getGenerativeModel: () => ({
|
|
327
|
-
generateContent: jest.fn().mockReturnValue(slowPromise),
|
|
328
|
-
}),
|
|
329
|
-
}));
|
|
330
|
-
process.env.GEMINI_API_KEY = 'test-api-key';
|
|
331
|
-
const response = await (0, llm_1.analyzeChanges)(geminiConfig, mockRequest);
|
|
332
|
-
expect(response.result).toBeNull();
|
|
333
|
-
expect(response.error).toMatch(/timeout/i);
|
|
334
|
-
delete process.env.GEMINI_API_KEY;
|
|
335
|
-
});
|
|
336
|
-
});
|
|
337
|
-
describe('Prompt Construction', () => {
|
|
338
|
-
test('constructs proper prompt with file paths and diff', async () => {
|
|
339
|
-
const mockCreate = jest.fn().mockResolvedValue({
|
|
340
|
-
choices: [{
|
|
341
|
-
message: {
|
|
342
|
-
content: JSON.stringify({
|
|
343
|
-
is_significant: false,
|
|
344
|
-
reason: 'Test'
|
|
345
|
-
})
|
|
346
|
-
}
|
|
347
|
-
}]
|
|
348
|
-
});
|
|
349
|
-
openai_1.default.mockImplementation(() => ({
|
|
350
|
-
chat: {
|
|
351
|
-
completions: {
|
|
352
|
-
create: mockCreate
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}));
|
|
356
|
-
await (0, llm_1.analyzeChanges)(mockConfig, mockRequest);
|
|
357
|
-
expect(mockCreate).toHaveBeenCalled();
|
|
358
|
-
const callArgs = mockCreate.mock.calls[0][0];
|
|
359
|
-
expect(callArgs.messages).toBeDefined();
|
|
360
|
-
expect(callArgs.messages.length).toBeGreaterThan(0);
|
|
361
|
-
});
|
|
362
|
-
});
|
|
363
|
-
describe('generateADRContent', () => {
|
|
364
|
-
const mockGenerationRequest = {
|
|
365
|
-
file_paths: ['src/database.ts'],
|
|
366
|
-
diff_content: `
|
|
367
|
-
diff --git a/src/database.ts b/src/database.ts
|
|
368
|
-
+import pg from 'pg';
|
|
369
|
-
+export const database = new pg.Pool();
|
|
370
|
-
`,
|
|
371
|
-
reason: 'Introduces PostgreSQL as primary datastore',
|
|
372
|
-
generation_prompt: 'Generate an ADR for the following changes...'
|
|
373
|
-
};
|
|
374
|
-
beforeEach(() => {
|
|
375
|
-
process.env.OPENAI_API_KEY = 'test-api-key';
|
|
376
|
-
});
|
|
377
|
-
afterEach(() => {
|
|
378
|
-
delete process.env.OPENAI_API_KEY;
|
|
379
|
-
});
|
|
380
|
-
test('generates ADR content successfully', async () => {
|
|
381
|
-
const mockADRContent = `# Use PostgreSQL as Primary Datastore
|
|
382
|
-
|
|
383
|
-
* Status: accepted
|
|
384
|
-
* Date: 2025-10-21
|
|
385
|
-
|
|
386
|
-
## Context and Problem Statement
|
|
387
|
-
|
|
388
|
-
We need a reliable database for our application.
|
|
389
|
-
|
|
390
|
-
## Decision Drivers
|
|
391
|
-
|
|
392
|
-
* Need for ACID compliance
|
|
393
|
-
* Strong community support
|
|
394
|
-
* Excellent performance
|
|
395
|
-
|
|
396
|
-
## Considered Options
|
|
397
|
-
|
|
398
|
-
* PostgreSQL
|
|
399
|
-
* MySQL
|
|
400
|
-
* MongoDB
|
|
401
|
-
|
|
402
|
-
## Decision Outcome
|
|
403
|
-
|
|
404
|
-
Chosen option: "PostgreSQL", because it provides strong ACID guarantees and excellent JSON support.
|
|
405
|
-
|
|
406
|
-
### Consequences
|
|
407
|
-
|
|
408
|
-
* Good, because we get reliable transactions
|
|
409
|
-
* Good, because we have strong typing
|
|
410
|
-
* Bad, because requires more setup than NoSQL`;
|
|
411
|
-
const mockResponse = {
|
|
412
|
-
choices: [{
|
|
413
|
-
message: {
|
|
414
|
-
content: mockADRContent
|
|
415
|
-
}
|
|
416
|
-
}]
|
|
417
|
-
};
|
|
418
|
-
const mockCreate = jest.fn().mockResolvedValue(mockResponse);
|
|
419
|
-
openai_1.default.mockImplementation(() => ({
|
|
420
|
-
chat: {
|
|
421
|
-
completions: {
|
|
422
|
-
create: mockCreate
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
}));
|
|
426
|
-
const response = await (0, llm_1.generateADRContent)(mockConfig, mockGenerationRequest);
|
|
427
|
-
expect(response.result).not.toBeNull();
|
|
428
|
-
expect(response.error).toBeUndefined();
|
|
429
|
-
expect(response.result?.content).toBe(mockADRContent);
|
|
430
|
-
expect(response.result?.title).toBe('Use PostgreSQL as Primary Datastore');
|
|
431
|
-
expect(response.result?.timestamp).toBeDefined();
|
|
432
|
-
});
|
|
433
|
-
test('extracts title from markdown heading', async () => {
|
|
434
|
-
const mockADRContent = `# Switch to TypeScript
|
|
435
|
-
|
|
436
|
-
* Status: accepted
|
|
437
|
-
|
|
438
|
-
Some content...`;
|
|
439
|
-
const mockResponse = {
|
|
440
|
-
choices: [{
|
|
441
|
-
message: {
|
|
442
|
-
content: mockADRContent
|
|
443
|
-
}
|
|
444
|
-
}]
|
|
445
|
-
};
|
|
446
|
-
const mockCreate = jest.fn().mockResolvedValue(mockResponse);
|
|
447
|
-
openai_1.default.mockImplementation(() => ({
|
|
448
|
-
chat: {
|
|
449
|
-
completions: {
|
|
450
|
-
create: mockCreate
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
}));
|
|
454
|
-
const response = await (0, llm_1.generateADRContent)(mockConfig, mockGenerationRequest);
|
|
455
|
-
expect(response.result?.title).toBe('Switch to TypeScript');
|
|
456
|
-
});
|
|
457
|
-
test('handles missing title gracefully', async () => {
|
|
458
|
-
const mockADRContent = `Some content without a title heading
|
|
459
|
-
|
|
460
|
-
* Status: accepted`;
|
|
461
|
-
const mockResponse = {
|
|
462
|
-
choices: [{
|
|
463
|
-
message: {
|
|
464
|
-
content: mockADRContent
|
|
465
|
-
}
|
|
466
|
-
}]
|
|
467
|
-
};
|
|
468
|
-
const mockCreate = jest.fn().mockResolvedValue(mockResponse);
|
|
469
|
-
openai_1.default.mockImplementation(() => ({
|
|
470
|
-
chat: {
|
|
471
|
-
completions: {
|
|
472
|
-
create: mockCreate
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
}));
|
|
476
|
-
const response = await (0, llm_1.generateADRContent)(mockConfig, mockGenerationRequest);
|
|
477
|
-
expect(response.result?.title).toBe('Untitled Decision');
|
|
478
|
-
});
|
|
479
|
-
test('removes markdown code fences if present', async () => {
|
|
480
|
-
const mockADRContent = `# My Decision
|
|
481
|
-
|
|
482
|
-
* Status: accepted`;
|
|
483
|
-
const mockResponse = {
|
|
484
|
-
choices: [{
|
|
485
|
-
message: {
|
|
486
|
-
content: '```markdown\n' + mockADRContent + '\n```'
|
|
487
|
-
}
|
|
488
|
-
}]
|
|
489
|
-
};
|
|
490
|
-
const mockCreate = jest.fn().mockResolvedValue(mockResponse);
|
|
491
|
-
openai_1.default.mockImplementation(() => ({
|
|
492
|
-
chat: {
|
|
493
|
-
completions: {
|
|
494
|
-
create: mockCreate
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
}));
|
|
498
|
-
const response = await (0, llm_1.generateADRContent)(mockConfig, mockGenerationRequest);
|
|
499
|
-
expect(response.result?.content).toBe(mockADRContent);
|
|
500
|
-
expect(response.result?.content).not.toContain('```');
|
|
501
|
-
});
|
|
502
|
-
test('handles API failures gracefully', async () => {
|
|
503
|
-
const mockCreate = jest.fn().mockRejectedValue(new Error('API Error'));
|
|
504
|
-
openai_1.default.mockImplementation(() => ({
|
|
505
|
-
chat: {
|
|
506
|
-
completions: {
|
|
507
|
-
create: mockCreate
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
}));
|
|
511
|
-
const response = await (0, llm_1.generateADRContent)(mockConfig, mockGenerationRequest);
|
|
512
|
-
expect(response.result).toBeNull();
|
|
513
|
-
expect(response.error).toBeDefined();
|
|
514
|
-
expect(response.error).toContain('API error');
|
|
515
|
-
});
|
|
516
|
-
test('handles missing API key', async () => {
|
|
517
|
-
delete process.env.OPENAI_API_KEY;
|
|
518
|
-
const response = await (0, llm_1.generateADRContent)(mockConfig, mockGenerationRequest);
|
|
519
|
-
expect(response.result).toBeNull();
|
|
520
|
-
expect(response.error).toContain('API key not found');
|
|
521
|
-
});
|
|
522
|
-
test('handles timeout errors', async () => {
|
|
523
|
-
const mockCreate = jest.fn().mockRejectedValue({ code: 'ETIMEDOUT' });
|
|
524
|
-
openai_1.default.mockImplementation(() => ({
|
|
525
|
-
chat: {
|
|
526
|
-
completions: {
|
|
527
|
-
create: mockCreate
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
}));
|
|
531
|
-
const response = await (0, llm_1.generateADRContent)(mockConfig, mockGenerationRequest);
|
|
532
|
-
expect(response.result).toBeNull();
|
|
533
|
-
expect(response.error).toMatch(/timeout/i);
|
|
534
|
-
});
|
|
535
|
-
test('follows fail-open principle on all errors', async () => {
|
|
536
|
-
const mockCreate = jest.fn().mockRejectedValue(new Error('Unexpected error'));
|
|
537
|
-
openai_1.default.mockImplementation(() => ({
|
|
538
|
-
chat: {
|
|
539
|
-
completions: {
|
|
540
|
-
create: mockCreate
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
}));
|
|
544
|
-
// Should not throw, should return error response
|
|
545
|
-
await expect((0, llm_1.generateADRContent)(mockConfig, mockGenerationRequest)).resolves.toEqual({
|
|
546
|
-
result: null,
|
|
547
|
-
error: expect.any(String)
|
|
548
|
-
});
|
|
549
|
-
});
|
|
550
|
-
test('respects timeout configuration', async () => {
|
|
551
|
-
const mockCreate = jest.fn().mockResolvedValue({
|
|
552
|
-
choices: [{
|
|
553
|
-
message: {
|
|
554
|
-
content: '# Test\n\nContent'
|
|
555
|
-
}
|
|
556
|
-
}]
|
|
557
|
-
});
|
|
558
|
-
openai_1.default.mockImplementation(() => ({
|
|
559
|
-
chat: {
|
|
560
|
-
completions: {
|
|
561
|
-
create: mockCreate
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
}));
|
|
565
|
-
await (0, llm_1.generateADRContent)(mockConfig, mockGenerationRequest);
|
|
566
|
-
expect(mockCreate).toHaveBeenCalledWith(expect.any(Object), expect.objectContaining({
|
|
567
|
-
timeout: 15000 // 15 seconds from config
|
|
568
|
-
}));
|
|
569
|
-
});
|
|
570
|
-
test('uses same model as analysis', async () => {
|
|
571
|
-
const mockCreate = jest.fn().mockResolvedValue({
|
|
572
|
-
choices: [{
|
|
573
|
-
message: {
|
|
574
|
-
content: '# Test\n\nContent'
|
|
575
|
-
}
|
|
576
|
-
}]
|
|
577
|
-
});
|
|
578
|
-
openai_1.default.mockImplementation(() => ({
|
|
579
|
-
chat: {
|
|
580
|
-
completions: {
|
|
581
|
-
create: mockCreate
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
}));
|
|
585
|
-
await (0, llm_1.generateADRContent)(mockConfig, mockGenerationRequest);
|
|
586
|
-
expect(mockCreate).toHaveBeenCalledWith(expect.objectContaining({
|
|
587
|
-
model: 'gpt-4' // Same as analysis_model
|
|
588
|
-
}), expect.any(Object));
|
|
589
|
-
});
|
|
590
|
-
});
|
|
591
|
-
});
|
|
592
|
-
//# sourceMappingURL=llm.test.js.map
|