cadr-cli 0.0.1 → 1.9.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.
Files changed (121) hide show
  1. package/dist/adr.d.ts +50 -0
  2. package/dist/adr.d.ts.map +1 -0
  3. package/dist/adr.js +156 -0
  4. package/dist/adr.js.map +1 -0
  5. package/dist/adr.test.d.ts +8 -0
  6. package/dist/adr.test.d.ts.map +1 -0
  7. package/dist/adr.test.js +256 -0
  8. package/dist/adr.test.js.map +1 -0
  9. package/dist/analysis.d.ts +24 -0
  10. package/dist/analysis.d.ts.map +1 -0
  11. package/dist/analysis.js +281 -0
  12. package/dist/analysis.js.map +1 -0
  13. package/dist/analysis.test.d.ts +8 -0
  14. package/dist/analysis.test.d.ts.map +1 -0
  15. package/dist/analysis.test.js +351 -0
  16. package/dist/analysis.test.js.map +1 -0
  17. package/dist/commands/analyze.d.ts +14 -0
  18. package/dist/commands/analyze.d.ts.map +1 -0
  19. package/dist/commands/analyze.js +56 -0
  20. package/dist/commands/analyze.js.map +1 -0
  21. package/dist/commands/init.d.ts +12 -0
  22. package/dist/commands/init.d.ts.map +1 -0
  23. package/dist/commands/init.js +93 -0
  24. package/dist/commands/init.js.map +1 -0
  25. package/dist/commands/init.test.d.ts +2 -0
  26. package/dist/commands/init.test.d.ts.map +1 -0
  27. package/dist/commands/init.test.js +56 -0
  28. package/dist/commands/init.test.js.map +1 -0
  29. package/dist/config.d.ts +40 -0
  30. package/dist/config.d.ts.map +1 -0
  31. package/dist/config.js +208 -0
  32. package/dist/config.js.map +1 -0
  33. package/dist/config.test.d.ts +2 -0
  34. package/dist/config.test.d.ts.map +1 -0
  35. package/dist/config.test.js +97 -0
  36. package/dist/config.test.js.map +1 -0
  37. package/dist/git.d.ts +42 -0
  38. package/dist/git.d.ts.map +1 -1
  39. package/dist/git.js +157 -0
  40. package/dist/git.js.map +1 -1
  41. package/dist/index.d.ts +2 -3
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +78 -62
  44. package/dist/index.js.map +1 -1
  45. package/dist/index.test.d.ts +2 -0
  46. package/dist/index.test.d.ts.map +1 -0
  47. package/dist/index.test.js +51 -0
  48. package/dist/index.test.js.map +1 -0
  49. package/dist/llm.d.ts +73 -0
  50. package/dist/llm.d.ts.map +1 -0
  51. package/dist/llm.js +263 -0
  52. package/dist/llm.js.map +1 -0
  53. package/dist/llm.test.d.ts +2 -0
  54. package/dist/llm.test.d.ts.map +1 -0
  55. package/dist/llm.test.js +592 -0
  56. package/dist/llm.test.js.map +1 -0
  57. package/dist/logger.d.ts.map +1 -1
  58. package/dist/logger.js +5 -3
  59. package/dist/logger.js.map +1 -1
  60. package/dist/logger.test.d.ts +2 -0
  61. package/dist/logger.test.d.ts.map +1 -0
  62. package/dist/logger.test.js +78 -0
  63. package/dist/logger.test.js.map +1 -0
  64. package/dist/prompts.d.ts +49 -0
  65. package/dist/prompts.d.ts.map +1 -0
  66. package/dist/prompts.js +195 -0
  67. package/dist/prompts.js.map +1 -0
  68. package/dist/prompts.test.d.ts +2 -0
  69. package/dist/prompts.test.d.ts.map +1 -0
  70. package/dist/prompts.test.js +427 -0
  71. package/dist/prompts.test.js.map +1 -0
  72. package/dist/providers/gemini.d.ts +3 -0
  73. package/dist/providers/gemini.d.ts.map +1 -0
  74. package/dist/providers/gemini.js +39 -0
  75. package/dist/providers/gemini.js.map +1 -0
  76. package/dist/providers/index.d.ts +2 -0
  77. package/dist/providers/index.d.ts.map +1 -0
  78. package/dist/providers/index.js +6 -0
  79. package/dist/providers/index.js.map +1 -0
  80. package/dist/providers/openai.d.ts +3 -0
  81. package/dist/providers/openai.d.ts.map +1 -0
  82. package/dist/providers/openai.js +25 -0
  83. package/dist/providers/openai.js.map +1 -0
  84. package/dist/providers/registry.d.ts +4 -0
  85. package/dist/providers/registry.d.ts.map +1 -0
  86. package/dist/providers/registry.js +16 -0
  87. package/dist/providers/registry.js.map +1 -0
  88. package/dist/providers/types.d.ts +12 -0
  89. package/dist/providers/types.d.ts.map +1 -0
  90. package/dist/providers/types.js +5 -0
  91. package/dist/providers/types.js.map +1 -0
  92. package/dist/version.test.d.ts +3 -0
  93. package/dist/version.test.d.ts.map +1 -0
  94. package/dist/version.test.js +25 -0
  95. package/dist/version.test.js.map +1 -0
  96. package/package.json +14 -5
  97. package/src/adr.test.ts +278 -0
  98. package/src/adr.ts +136 -0
  99. package/src/analysis.test.ts +396 -0
  100. package/src/analysis.ts +262 -0
  101. package/src/commands/analyze.ts +56 -0
  102. package/src/commands/init.test.ts +27 -0
  103. package/src/commands/init.ts +99 -0
  104. package/src/config.test.ts +79 -0
  105. package/src/config.ts +214 -0
  106. package/src/git.ts +240 -0
  107. package/src/index.test.ts +59 -0
  108. package/src/index.ts +80 -60
  109. package/src/llm.test.ts +701 -0
  110. package/src/llm.ts +344 -0
  111. package/src/logger.test.ts +90 -0
  112. package/src/logger.ts +6 -3
  113. package/src/prompts.test.ts +515 -0
  114. package/src/prompts.ts +174 -0
  115. package/src/providers/gemini.ts +41 -0
  116. package/src/providers/index.ts +1 -0
  117. package/src/providers/openai.ts +22 -0
  118. package/src/providers/registry.ts +16 -0
  119. package/src/providers/types.ts +14 -0
  120. package/src/version.test.ts +29 -0
  121. package/bin/cadr.js +0 -16
@@ -0,0 +1,281 @@
1
+ "use strict";
2
+ /**
3
+ * Analysis Orchestration Module
4
+ *
5
+ * Coordinates the complete analysis flow: config loading, git operations,
6
+ * prompt formatting, LLM analysis, and result display.
7
+ * Implements fail-open principle per constitution requirements.
8
+ */
9
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ var desc = Object.getOwnPropertyDescriptor(m, k);
12
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
+ desc = { enumerable: true, get: function() { return m[k]; } };
14
+ }
15
+ Object.defineProperty(o, k2, desc);
16
+ }) : (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ o[k2] = m[k];
19
+ }));
20
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
22
+ }) : function(o, v) {
23
+ o["default"] = v;
24
+ });
25
+ var __importStar = (this && this.__importStar) || (function () {
26
+ var ownKeys = function(o) {
27
+ ownKeys = Object.getOwnPropertyNames || function (o) {
28
+ var ar = [];
29
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
30
+ return ar;
31
+ };
32
+ return ownKeys(o);
33
+ };
34
+ return function (mod) {
35
+ if (mod && mod.__esModule) return mod;
36
+ var result = {};
37
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
38
+ __setModuleDefault(result, mod);
39
+ return result;
40
+ };
41
+ })();
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.runAnalysis = runAnalysis;
44
+ const config_1 = require("./config");
45
+ const git_1 = require("./git");
46
+ const prompts_1 = require("./prompts");
47
+ const llm_1 = require("./llm");
48
+ const logger_1 = require("./logger");
49
+ const adr_1 = require("./adr");
50
+ const path = __importStar(require("path"));
51
+ /**
52
+ * Run complete analysis workflow
53
+ *
54
+ * This function orchestrates the entire analysis process:
55
+ * 1. Load configuration
56
+ * 2. Get changed files and diff based on options
57
+ * 3. Format LLM prompt
58
+ * 4. Call LLM for analysis
59
+ * 5. Display results
60
+ *
61
+ * Follows fail-open principle: always exits cleanly, never throws.
62
+ *
63
+ * @param diffOptions - Options specifying which changes to analyze (defaults to all uncommitted)
64
+ */
65
+ async function runAnalysis(diffOptions = { mode: 'all' }) {
66
+ try {
67
+ logger_1.loggerInstance.info('Starting analysis workflow');
68
+ // Step 1: Load configuration
69
+ const configPath = (0, config_1.getDefaultConfigPath)();
70
+ const config = await (0, config_1.loadConfig)(configPath);
71
+ if (!config) {
72
+ // eslint-disable-next-line no-console
73
+ console.error('\n❌ Configuration Error');
74
+ // eslint-disable-next-line no-console
75
+ console.error('Configuration file not found or invalid.');
76
+ // eslint-disable-next-line no-console
77
+ console.error('\n💡 Run `cadr init` to create a configuration file.\n');
78
+ return;
79
+ }
80
+ // Step 2: Get changed files based on diff options
81
+ let changedFiles;
82
+ try {
83
+ changedFiles = await (0, git_1.getChangedFiles)(diffOptions);
84
+ }
85
+ catch (error) {
86
+ if (error instanceof git_1.GitError) {
87
+ // eslint-disable-next-line no-console
88
+ console.error(`\n❌ Git Error: ${error.message}\n`);
89
+ }
90
+ else {
91
+ // eslint-disable-next-line no-console
92
+ console.error('\n❌ Failed to read changed files\n');
93
+ }
94
+ logger_1.loggerInstance.error('Failed to get changed files', { error, mode: diffOptions.mode });
95
+ return;
96
+ }
97
+ // Check if there are changed files
98
+ const modeText = diffOptions.mode === 'staged' ? 'staged' :
99
+ diffOptions.mode === 'branch-diff' ? `between ${diffOptions.base || 'origin/main'} and ${diffOptions.head || 'HEAD'}` :
100
+ 'uncommitted';
101
+ if (changedFiles.length === 0) {
102
+ // eslint-disable-next-line no-console
103
+ console.log(`\nℹ️ No changes to analyze ${diffOptions.mode === 'branch-diff' ? modeText : `(${modeText})`}`);
104
+ if (diffOptions.mode === 'staged') {
105
+ // eslint-disable-next-line no-console
106
+ console.log('💡 Stage some files first:');
107
+ // eslint-disable-next-line no-console
108
+ console.log(' git add <files>');
109
+ // eslint-disable-next-line no-console
110
+ console.log(' cadr analyze --staged\n');
111
+ }
112
+ else if (diffOptions.mode === 'branch-diff') {
113
+ // eslint-disable-next-line no-console
114
+ console.log('💡 No changes found between specified git references.\n');
115
+ }
116
+ else {
117
+ // eslint-disable-next-line no-console
118
+ console.log('💡 Make some changes first, then run:');
119
+ // eslint-disable-next-line no-console
120
+ console.log(' cadr analyze\n');
121
+ }
122
+ return;
123
+ }
124
+ // Display files being analyzed
125
+ const fileCountText = diffOptions.mode === 'branch-diff' ?
126
+ `${changedFiles.length} file${changedFiles.length === 1 ? '' : 's'} changed ${modeText}` :
127
+ `${changedFiles.length} ${modeText} file${changedFiles.length === 1 ? '' : 's'}`;
128
+ // eslint-disable-next-line no-console
129
+ console.log(`\n📝 Analyzing ${fileCountText}:`);
130
+ changedFiles.forEach((file) => {
131
+ // eslint-disable-next-line no-console
132
+ console.log(` • ${file}`);
133
+ });
134
+ // eslint-disable-next-line no-console
135
+ console.log('');
136
+ // Step 3: Get diff content
137
+ let diffContent;
138
+ try {
139
+ diffContent = await (0, git_1.getDiff)(diffOptions);
140
+ }
141
+ catch (error) {
142
+ // eslint-disable-next-line no-console
143
+ console.error('\n❌ Failed to read diff content\n');
144
+ logger_1.loggerInstance.error('Failed to get diff', { error, mode: diffOptions.mode });
145
+ return;
146
+ }
147
+ // Check if diff is empty
148
+ if (!diffContent || diffContent.trim().length === 0) {
149
+ // eslint-disable-next-line no-console
150
+ console.log('\nℹ️ No diff content found\n');
151
+ return;
152
+ }
153
+ // Step 4: Format prompt
154
+ const repositoryContext = path.basename(process.cwd());
155
+ const prompt = (0, prompts_1.formatPrompt)(prompts_1.ANALYSIS_PROMPT_V1, {
156
+ file_paths: changedFiles,
157
+ diff_content: diffContent,
158
+ });
159
+ // Display analysis start
160
+ const analysisText = diffOptions.mode === 'staged' ? 'staged changes' :
161
+ diffOptions.mode === 'branch-diff' ? 'changes' :
162
+ 'uncommitted changes';
163
+ // eslint-disable-next-line no-console
164
+ console.log(`🔍 Analyzing ${analysisText} for architectural significance...\n`);
165
+ // eslint-disable-next-line no-console
166
+ console.log(`🤖 Sending to ${config.provider} ${config.analysis_model}...\n`);
167
+ // Step 5: Call LLM for analysis
168
+ const response = await (0, llm_1.analyzeChanges)(config, {
169
+ file_paths: changedFiles,
170
+ diff_content: diffContent,
171
+ repository_context: repositoryContext,
172
+ analysis_prompt: prompt,
173
+ });
174
+ // Step 6: Display results
175
+ if (!response.result || response.error) {
176
+ // eslint-disable-next-line no-console
177
+ console.error('\n❌ Analysis failed');
178
+ // eslint-disable-next-line no-console
179
+ console.error(`\n${response.error || 'Unknown error occurred'}\n`);
180
+ return;
181
+ }
182
+ const result = response.result;
183
+ // Display analysis result
184
+ // eslint-disable-next-line no-console
185
+ console.log('✅ Analysis Complete\n');
186
+ if (result.is_significant) {
187
+ // eslint-disable-next-line no-console
188
+ console.log('📊 Result: ✨ ARCHITECTURALLY SIGNIFICANT');
189
+ // eslint-disable-next-line no-console
190
+ console.log(`💭 Reasoning: ${result.reason}\n`);
191
+ if (result.confidence) {
192
+ // eslint-disable-next-line no-console
193
+ console.log(`🎯 Confidence: ${(result.confidence * 100).toFixed(0)}%\n`);
194
+ }
195
+ // Prompt user for ADR generation
196
+ const shouldGenerate = await (0, prompts_1.promptForGeneration)(result.reason);
197
+ if (shouldGenerate) {
198
+ // eslint-disable-next-line no-console
199
+ console.log('\n🧠 Generating ADR draft...\n');
200
+ // Format generation prompt
201
+ const generationPrompt = (0, prompts_1.formatGenerationPrompt)({
202
+ file_paths: changedFiles,
203
+ diff_content: diffContent,
204
+ });
205
+ // Call LLM to generate ADR content
206
+ const generationResponse = await (0, llm_1.generateADRContent)(config, {
207
+ file_paths: changedFiles,
208
+ diff_content: diffContent,
209
+ reason: result.reason,
210
+ generation_prompt: generationPrompt,
211
+ });
212
+ if (!generationResponse.result || generationResponse.error) {
213
+ // eslint-disable-next-line no-console
214
+ console.error('\n❌ ADR generation failed');
215
+ // eslint-disable-next-line no-console
216
+ console.error(`\n${generationResponse.error || 'Unknown error occurred'}\n`);
217
+ logger_1.loggerInstance.error('ADR generation failed', { error: generationResponse.error });
218
+ }
219
+ else {
220
+ // Save ADR to file
221
+ const saveResult = (0, adr_1.saveADR)(generationResponse.result.content, generationResponse.result.title);
222
+ if (saveResult.success && saveResult.filePath) {
223
+ // eslint-disable-next-line no-console
224
+ console.log('✅ Success! Draft ADR created\n');
225
+ // eslint-disable-next-line no-console
226
+ console.log(`📄 File: ${saveResult.filePath}\n`);
227
+ // eslint-disable-next-line no-console
228
+ console.log('💡 Next steps:');
229
+ // eslint-disable-next-line no-console
230
+ console.log(' 1. Review and refine the generated ADR');
231
+ // eslint-disable-next-line no-console
232
+ console.log(' 2. Commit it alongside your code changes\n');
233
+ logger_1.loggerInstance.info('ADR generation workflow completed successfully', {
234
+ filePath: saveResult.filePath,
235
+ title: generationResponse.result.title,
236
+ });
237
+ }
238
+ else {
239
+ // eslint-disable-next-line no-console
240
+ console.error('\n❌ Failed to save ADR');
241
+ // eslint-disable-next-line no-console
242
+ console.error(`\n${saveResult.error || 'Unknown error occurred'}\n`);
243
+ logger_1.loggerInstance.error('Failed to save ADR', { error: saveResult.error });
244
+ }
245
+ }
246
+ }
247
+ else {
248
+ // User declined generation
249
+ // eslint-disable-next-line no-console
250
+ console.log('\n📋 Skipping ADR generation');
251
+ // eslint-disable-next-line no-console
252
+ console.log('🎯 Recommendation: Consider documenting this decision manually.\n');
253
+ }
254
+ }
255
+ else {
256
+ // eslint-disable-next-line no-console
257
+ console.log('📊 Result: ℹ️ NOT ARCHITECTURALLY SIGNIFICANT');
258
+ // eslint-disable-next-line no-console
259
+ console.log(`💭 Reasoning: ${result.reason}\n`);
260
+ if (result.confidence) {
261
+ // eslint-disable-next-line no-console
262
+ console.log(`🎯 Confidence: ${(result.confidence * 100).toFixed(0)}%\n`);
263
+ }
264
+ // eslint-disable-next-line no-console
265
+ console.log('✅ No ADR needed for these changes.\n');
266
+ }
267
+ logger_1.loggerInstance.info('Analysis workflow completed successfully', {
268
+ is_significant: result.is_significant,
269
+ file_count: changedFiles.length,
270
+ });
271
+ }
272
+ catch (error) {
273
+ // Final catch-all for any unexpected errors (fail-open)
274
+ logger_1.loggerInstance.error('Unexpected error in analysis workflow', { error });
275
+ // eslint-disable-next-line no-console
276
+ console.error('\n❌ An unexpected error occurred');
277
+ // eslint-disable-next-line no-console
278
+ console.error('Please check the logs for more details.\n');
279
+ }
280
+ }
281
+ //# sourceMappingURL=analysis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analysis.js","sourceRoot":"","sources":["../src/analysis.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Analysis Module Integration Tests
3
+ *
4
+ * Tests for the complete analysis workflow including generation.
5
+ * Following TDD: These tests are written BEFORE implementation.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=analysis.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analysis.test.d.ts","sourceRoot":"","sources":["../src/analysis.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
@@ -0,0 +1,351 @@
1
+ "use strict";
2
+ /**
3
+ * Analysis Module Integration Tests
4
+ *
5
+ * Tests for the complete analysis workflow including generation.
6
+ * Following TDD: These tests are written BEFORE implementation.
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ /* eslint-disable no-console */
43
+ const analysis_1 = require("./analysis");
44
+ const config = __importStar(require("./config"));
45
+ const git = __importStar(require("./git"));
46
+ const llm = __importStar(require("./llm"));
47
+ const prompts = __importStar(require("./prompts"));
48
+ const adr = __importStar(require("./adr"));
49
+ // Mock all dependencies
50
+ jest.mock('./config');
51
+ jest.mock('./git');
52
+ jest.mock('./llm');
53
+ jest.mock('./prompts');
54
+ jest.mock('./adr');
55
+ describe('Analysis with Generation Integration', () => {
56
+ const mockConfig = {
57
+ provider: 'openai',
58
+ analysis_model: 'gpt-4',
59
+ api_key_env: 'OPENAI_API_KEY',
60
+ timeout_seconds: 15
61
+ };
62
+ const mockDiffOptions = { mode: 'staged' };
63
+ beforeEach(() => {
64
+ jest.clearAllMocks();
65
+ // Mock console methods to silence output during tests
66
+ jest.spyOn(console, 'log').mockImplementation();
67
+ jest.spyOn(console, 'error').mockImplementation();
68
+ // Default mocks
69
+ config.loadConfig.mockResolvedValue(mockConfig);
70
+ git.getChangedFiles.mockResolvedValue([
71
+ 'src/database.ts',
72
+ 'src/config.ts'
73
+ ]);
74
+ git.getDiff.mockResolvedValue(`
75
+ diff --git a/src/database.ts b/src/database.ts
76
+ +import pg from 'pg';
77
+ +export const database = new pg.Pool();
78
+ `);
79
+ });
80
+ afterEach(() => {
81
+ jest.restoreAllMocks();
82
+ });
83
+ describe('runAnalysis without generation', () => {
84
+ test('completes successfully when change is not significant', async () => {
85
+ llm.analyzeChanges.mockResolvedValue({
86
+ result: {
87
+ is_significant: false,
88
+ reason: 'Minor code formatting changes',
89
+ timestamp: new Date().toISOString()
90
+ },
91
+ error: undefined
92
+ });
93
+ await (0, analysis_1.runAnalysis)(mockDiffOptions);
94
+ expect(config.loadConfig).toHaveBeenCalled();
95
+ expect(git.getChangedFiles).toHaveBeenCalledWith(mockDiffOptions);
96
+ expect(git.getDiff).toHaveBeenCalledWith(mockDiffOptions);
97
+ expect(llm.analyzeChanges).toHaveBeenCalled();
98
+ // Should not prompt for generation if not significant
99
+ expect(prompts.promptForGeneration).not.toHaveBeenCalled();
100
+ });
101
+ test('handles missing configuration gracefully', async () => {
102
+ config.loadConfig.mockResolvedValue(null);
103
+ await (0, analysis_1.runAnalysis)(mockDiffOptions);
104
+ expect(console.error).toHaveBeenCalledWith(expect.stringContaining('Configuration'));
105
+ expect(git.getChangedFiles).not.toHaveBeenCalled();
106
+ });
107
+ test('handles no changed files gracefully', async () => {
108
+ git.getChangedFiles.mockResolvedValue([]);
109
+ await (0, analysis_1.runAnalysis)(mockDiffOptions);
110
+ expect(console.log).toHaveBeenCalledWith(expect.stringContaining('No changes'));
111
+ expect(git.getDiff).not.toHaveBeenCalled();
112
+ });
113
+ test('handles git errors gracefully', async () => {
114
+ git.getChangedFiles.mockRejectedValue(new Error('Git error'));
115
+ await (0, analysis_1.runAnalysis)(mockDiffOptions);
116
+ expect(console.error).toHaveBeenCalledWith(expect.stringContaining('Failed'));
117
+ });
118
+ });
119
+ describe('runAnalysis with generation - user confirms', () => {
120
+ beforeEach(() => {
121
+ llm.analyzeChanges.mockResolvedValue({
122
+ result: {
123
+ is_significant: true,
124
+ reason: 'Introduces PostgreSQL as primary datastore',
125
+ timestamp: new Date().toISOString()
126
+ },
127
+ error: undefined
128
+ });
129
+ prompts.promptForGeneration.mockResolvedValue(true); // User confirms
130
+ });
131
+ test('prompts for generation when change is significant', async () => {
132
+ llm.generateADRContent.mockResolvedValue({
133
+ result: {
134
+ content: '# Use PostgreSQL\n\n* Status: accepted',
135
+ title: 'Use PostgreSQL',
136
+ timestamp: new Date().toISOString()
137
+ },
138
+ error: undefined
139
+ });
140
+ adr.saveADR.mockReturnValue({
141
+ success: true,
142
+ filePath: 'docs/adr/0001-use-postgresql.md'
143
+ });
144
+ await (0, analysis_1.runAnalysis)(mockDiffOptions);
145
+ expect(prompts.promptForGeneration).toHaveBeenCalledWith('Introduces PostgreSQL as primary datastore');
146
+ });
147
+ test('generates ADR when user confirms', async () => {
148
+ llm.generateADRContent.mockResolvedValue({
149
+ result: {
150
+ content: '# Use PostgreSQL\n\n* Status: accepted',
151
+ title: 'Use PostgreSQL',
152
+ timestamp: new Date().toISOString()
153
+ },
154
+ error: undefined
155
+ });
156
+ adr.saveADR.mockReturnValue({
157
+ success: true,
158
+ filePath: 'docs/adr/0001-use-postgresql.md'
159
+ });
160
+ await (0, analysis_1.runAnalysis)(mockDiffOptions);
161
+ expect(llm.generateADRContent).toHaveBeenCalled();
162
+ const generationCall = llm.generateADRContent.mock.calls[0];
163
+ expect(generationCall[0]).toEqual(mockConfig);
164
+ expect(generationCall[1]).toMatchObject({
165
+ reason: 'Introduces PostgreSQL as primary datastore'
166
+ });
167
+ });
168
+ test('saves ADR file after successful generation', async () => {
169
+ const mockADRContent = '# Use PostgreSQL\n\n* Status: accepted\n\n## Context\n\nWe need a database.';
170
+ llm.generateADRContent.mockResolvedValue({
171
+ result: {
172
+ content: mockADRContent,
173
+ title: 'Use PostgreSQL',
174
+ timestamp: new Date().toISOString()
175
+ },
176
+ error: undefined
177
+ });
178
+ adr.saveADR.mockReturnValue({
179
+ success: true,
180
+ filePath: 'docs/adr/0001-use-postgresql.md'
181
+ });
182
+ await (0, analysis_1.runAnalysis)(mockDiffOptions);
183
+ expect(adr.saveADR).toHaveBeenCalledWith(mockADRContent, 'Use PostgreSQL');
184
+ });
185
+ test('displays success message with file path', async () => {
186
+ const filePath = 'docs/adr/0001-use-postgresql.md';
187
+ llm.generateADRContent.mockResolvedValue({
188
+ result: {
189
+ content: '# Use PostgreSQL\n\n* Status: accepted',
190
+ title: 'Use PostgreSQL',
191
+ timestamp: new Date().toISOString()
192
+ },
193
+ error: undefined
194
+ });
195
+ adr.saveADR.mockReturnValue({
196
+ success: true,
197
+ filePath
198
+ });
199
+ await (0, analysis_1.runAnalysis)(mockDiffOptions);
200
+ expect(console.log).toHaveBeenCalledWith(expect.stringContaining('Success'));
201
+ expect(console.log).toHaveBeenCalledWith(expect.stringContaining(filePath));
202
+ });
203
+ test('displays next steps after successful generation', async () => {
204
+ llm.generateADRContent.mockResolvedValue({
205
+ result: {
206
+ content: '# Use PostgreSQL\n\n* Status: accepted',
207
+ title: 'Use PostgreSQL',
208
+ timestamp: new Date().toISOString()
209
+ },
210
+ error: undefined
211
+ });
212
+ adr.saveADR.mockReturnValue({
213
+ success: true,
214
+ filePath: 'docs/adr/0001-use-postgresql.md'
215
+ });
216
+ await (0, analysis_1.runAnalysis)(mockDiffOptions);
217
+ expect(console.log).toHaveBeenCalledWith(expect.stringContaining('Next steps'));
218
+ expect(console.log).toHaveBeenCalledWith(expect.stringContaining('Review'));
219
+ expect(console.log).toHaveBeenCalledWith(expect.stringContaining('Commit'));
220
+ });
221
+ });
222
+ describe('runAnalysis with generation - user declines', () => {
223
+ beforeEach(() => {
224
+ llm.analyzeChanges.mockResolvedValue({
225
+ result: {
226
+ is_significant: true,
227
+ reason: 'Introduces Redis caching layer',
228
+ timestamp: new Date().toISOString()
229
+ },
230
+ error: undefined
231
+ });
232
+ prompts.promptForGeneration.mockResolvedValue(false); // User declines
233
+ });
234
+ test('skips generation when user declines', async () => {
235
+ await (0, analysis_1.runAnalysis)(mockDiffOptions);
236
+ expect(prompts.promptForGeneration).toHaveBeenCalled();
237
+ expect(llm.generateADRContent).not.toHaveBeenCalled();
238
+ expect(adr.saveADR).not.toHaveBeenCalled();
239
+ });
240
+ test('displays skip message when user declines', async () => {
241
+ await (0, analysis_1.runAnalysis)(mockDiffOptions);
242
+ expect(console.log).toHaveBeenCalledWith(expect.stringContaining('Skipping'));
243
+ expect(console.log).toHaveBeenCalledWith(expect.stringContaining('manual'));
244
+ });
245
+ });
246
+ describe('runAnalysis with generation - error handling', () => {
247
+ beforeEach(() => {
248
+ llm.analyzeChanges.mockResolvedValue({
249
+ result: {
250
+ is_significant: true,
251
+ reason: 'Introduces Kafka event streaming',
252
+ timestamp: new Date().toISOString()
253
+ },
254
+ error: undefined
255
+ });
256
+ prompts.promptForGeneration.mockResolvedValue(true);
257
+ });
258
+ test('handles generation errors gracefully', async () => {
259
+ llm.generateADRContent.mockResolvedValue({
260
+ result: null,
261
+ error: 'API rate limit exceeded'
262
+ });
263
+ await (0, analysis_1.runAnalysis)(mockDiffOptions);
264
+ expect(console.error).toHaveBeenCalledWith(expect.stringContaining('generation failed'));
265
+ expect(console.error).toHaveBeenCalledWith(expect.stringContaining('rate limit'));
266
+ expect(adr.saveADR).not.toHaveBeenCalled();
267
+ });
268
+ test('handles file save errors gracefully', async () => {
269
+ llm.generateADRContent.mockResolvedValue({
270
+ result: {
271
+ content: '# Use Kafka\n\n* Status: accepted',
272
+ title: 'Use Kafka',
273
+ timestamp: new Date().toISOString()
274
+ },
275
+ error: undefined
276
+ });
277
+ adr.saveADR.mockReturnValue({
278
+ success: false,
279
+ error: 'Permission denied'
280
+ });
281
+ await (0, analysis_1.runAnalysis)(mockDiffOptions);
282
+ expect(console.error).toHaveBeenCalledWith(expect.stringContaining('Failed to save'));
283
+ expect(console.error).toHaveBeenCalledWith(expect.stringContaining('Permission denied'));
284
+ });
285
+ test('continues workflow on generation error (fail-open)', async () => {
286
+ llm.generateADRContent.mockResolvedValue({
287
+ result: null,
288
+ error: 'Network error'
289
+ });
290
+ // Should complete without throwing
291
+ await expect((0, analysis_1.runAnalysis)(mockDiffOptions)).resolves.not.toThrow();
292
+ });
293
+ test('continues workflow on save error (fail-open)', async () => {
294
+ llm.generateADRContent.mockResolvedValue({
295
+ result: {
296
+ content: '# Decision\n\n* Status: accepted',
297
+ title: 'Decision',
298
+ timestamp: new Date().toISOString()
299
+ },
300
+ error: undefined
301
+ });
302
+ adr.saveADR.mockReturnValue({
303
+ success: false,
304
+ error: 'Disk full'
305
+ });
306
+ // Should complete without throwing
307
+ await expect((0, analysis_1.runAnalysis)(mockDiffOptions)).resolves.not.toThrow();
308
+ });
309
+ });
310
+ describe('runAnalysis - complete workflow', () => {
311
+ test('completes full happy path workflow', async () => {
312
+ // Analysis detects significance
313
+ llm.analyzeChanges.mockResolvedValue({
314
+ result: {
315
+ is_significant: true,
316
+ reason: 'Introduces GraphQL API layer',
317
+ timestamp: new Date().toISOString()
318
+ },
319
+ error: undefined
320
+ });
321
+ // User confirms
322
+ prompts.promptForGeneration.mockResolvedValue(true);
323
+ // Generation succeeds
324
+ llm.generateADRContent.mockResolvedValue({
325
+ result: {
326
+ content: '# Use GraphQL\n\n* Status: accepted',
327
+ title: 'Use GraphQL',
328
+ timestamp: new Date().toISOString()
329
+ },
330
+ error: undefined
331
+ });
332
+ // Save succeeds
333
+ adr.saveADR.mockReturnValue({
334
+ success: true,
335
+ filePath: 'docs/adr/0001-use-graphql.md'
336
+ });
337
+ await (0, analysis_1.runAnalysis)(mockDiffOptions);
338
+ // Verify complete workflow executed
339
+ expect(config.loadConfig).toHaveBeenCalled();
340
+ expect(git.getChangedFiles).toHaveBeenCalled();
341
+ expect(git.getDiff).toHaveBeenCalled();
342
+ expect(llm.analyzeChanges).toHaveBeenCalled();
343
+ expect(prompts.promptForGeneration).toHaveBeenCalled();
344
+ expect(llm.generateADRContent).toHaveBeenCalled();
345
+ expect(adr.saveADR).toHaveBeenCalled();
346
+ // Verify success output
347
+ expect(console.log).toHaveBeenCalledWith(expect.stringContaining('Success'));
348
+ });
349
+ });
350
+ });
351
+ //# sourceMappingURL=analysis.test.js.map