cadr-cli 2.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/dist/adr/adr.d.ts +17 -0
  2. package/dist/adr/adr.d.ts.map +1 -0
  3. package/dist/{adr.js → adr/adr.js} +4 -44
  4. package/dist/adr/adr.js.map +1 -0
  5. package/dist/adr/adr.test.d.ts +5 -0
  6. package/dist/{adr.test.d.ts.map → adr/adr.test.d.ts.map} +1 -1
  7. package/dist/{adr.test.js → adr/adr.test.js} +0 -14
  8. package/dist/adr/adr.test.js.map +1 -0
  9. package/dist/adr/index.d.ts +2 -0
  10. package/dist/adr/index.d.ts.map +1 -0
  11. package/dist/adr/index.js +18 -0
  12. package/dist/adr/index.js.map +1 -0
  13. package/dist/analysis/analysis.orchestrator.d.ts +14 -0
  14. package/dist/analysis/analysis.orchestrator.d.ts.map +1 -0
  15. package/dist/analysis/analysis.orchestrator.js +175 -0
  16. package/dist/analysis/analysis.orchestrator.js.map +1 -0
  17. package/dist/analysis/strategies/git-strategy.d.ts +22 -0
  18. package/dist/analysis/strategies/git-strategy.d.ts.map +1 -0
  19. package/dist/analysis/strategies/git-strategy.js +114 -0
  20. package/dist/analysis/strategies/git-strategy.js.map +1 -0
  21. package/dist/commands/analyze.js +3 -3
  22. package/dist/commands/analyze.js.map +1 -1
  23. package/dist/git/git.errors.d.ts +6 -0
  24. package/dist/git/git.errors.d.ts.map +1 -0
  25. package/dist/git/git.errors.js +15 -0
  26. package/dist/git/git.errors.js.map +1 -0
  27. package/dist/git/git.operations.d.ts +12 -0
  28. package/dist/git/git.operations.d.ts.map +1 -0
  29. package/dist/git/git.operations.js +64 -0
  30. package/dist/git/git.operations.js.map +1 -0
  31. package/dist/git/index.d.ts +4 -0
  32. package/dist/git/index.d.ts.map +1 -0
  33. package/dist/git/index.js +19 -0
  34. package/dist/git/index.js.map +1 -0
  35. package/dist/llm/index.d.ts +3 -0
  36. package/dist/llm/index.d.ts.map +1 -0
  37. package/dist/llm/index.js +19 -0
  38. package/dist/llm/index.js.map +1 -0
  39. package/dist/llm/llm.d.ts +35 -0
  40. package/dist/llm/llm.d.ts.map +1 -0
  41. package/dist/{llm.js → llm/llm.js} +16 -58
  42. package/dist/llm/llm.js.map +1 -0
  43. package/dist/{prompts.d.ts → llm/prompts.d.ts} +1 -38
  44. package/dist/llm/prompts.d.ts.map +1 -0
  45. package/dist/{prompts.js → llm/prompts.js} +9 -54
  46. package/dist/llm/prompts.js.map +1 -0
  47. package/dist/llm/response-parser.d.ts +9 -0
  48. package/dist/llm/response-parser.d.ts.map +1 -0
  49. package/dist/llm/response-parser.js +67 -0
  50. package/dist/llm/response-parser.js.map +1 -0
  51. package/dist/presenters/console-presenter.d.ts +35 -0
  52. package/dist/presenters/console-presenter.d.ts.map +1 -0
  53. package/dist/presenters/console-presenter.js +114 -0
  54. package/dist/presenters/console-presenter.js.map +1 -0
  55. package/package.json +1 -1
  56. package/src/{adr.test.ts → adr/adr.test.ts} +10 -23
  57. package/src/{adr.ts → adr/adr.ts} +7 -48
  58. package/src/adr/index.ts +1 -0
  59. package/src/analysis/analysis.orchestrator.ts +175 -0
  60. package/src/analysis/strategies/git-strategy.ts +106 -0
  61. package/src/commands/analyze.ts +8 -9
  62. package/src/git/git.errors.ts +10 -0
  63. package/src/git/git.operations.ts +85 -0
  64. package/src/git/index.ts +3 -0
  65. package/src/llm/index.ts +2 -0
  66. package/src/{llm.ts → llm/llm.ts} +46 -107
  67. package/src/{prompts.ts → llm/prompts.ts} +30 -72
  68. package/src/llm/response-parser.ts +90 -0
  69. package/src/presenters/console-presenter.ts +152 -0
  70. package/dist/adr.d.ts +0 -50
  71. package/dist/adr.d.ts.map +0 -1
  72. package/dist/adr.js.map +0 -1
  73. package/dist/adr.test.d.ts +0 -8
  74. package/dist/adr.test.js.map +0 -1
  75. package/dist/analysis.d.ts +0 -24
  76. package/dist/analysis.d.ts.map +0 -1
  77. package/dist/analysis.js +0 -281
  78. package/dist/analysis.js.map +0 -1
  79. package/dist/analysis.test.d.ts +0 -8
  80. package/dist/analysis.test.d.ts.map +0 -1
  81. package/dist/analysis.test.js +0 -351
  82. package/dist/analysis.test.js.map +0 -1
  83. package/dist/git.d.ts +0 -54
  84. package/dist/git.d.ts.map +0 -1
  85. package/dist/git.js +0 -204
  86. package/dist/git.js.map +0 -1
  87. package/dist/llm.d.ts +0 -73
  88. package/dist/llm.d.ts.map +0 -1
  89. package/dist/llm.js.map +0 -1
  90. package/dist/llm.test.d.ts +0 -2
  91. package/dist/llm.test.d.ts.map +0 -1
  92. package/dist/llm.test.js +0 -592
  93. package/dist/llm.test.js.map +0 -1
  94. package/dist/prompts.d.ts.map +0 -1
  95. package/dist/prompts.js.map +0 -1
  96. package/dist/prompts.test.d.ts +0 -2
  97. package/dist/prompts.test.d.ts.map +0 -1
  98. package/dist/prompts.test.js +0 -427
  99. package/dist/prompts.test.js.map +0 -1
  100. package/src/analysis.test.ts +0 -396
  101. package/src/analysis.ts +0 -262
  102. package/src/git.ts +0 -300
  103. package/src/llm.test.ts +0 -701
  104. package/src/prompts.test.ts +0 -515
@@ -0,0 +1,9 @@
1
+ export interface ParsedAnalysisResponse {
2
+ is_significant: boolean;
3
+ reason: string;
4
+ confidence?: number;
5
+ }
6
+ export declare function parseAnalysisResponse(responseContent: string): ParsedAnalysisResponse;
7
+ export declare function extractTitleFromMarkdown(content: string): string;
8
+ export declare function parseLLMResponse<T>(responseContent: string, validator: (parsed: unknown) => T): T;
9
+ //# sourceMappingURL=response-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response-parser.d.ts","sourceRoot":"","sources":["../../src/llm/response-parser.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,sBAAsB;IACrC,cAAc,EAAE,OAAO,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,qBAAqB,CAAC,eAAe,EAAE,MAAM,GAAG,sBAAsB,CA0CrF;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAUhE;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,eAAe,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,CAAC,GAAG,CAAC,CAyBjG"}
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseAnalysisResponse = parseAnalysisResponse;
4
+ exports.extractTitleFromMarkdown = extractTitleFromMarkdown;
5
+ exports.parseLLMResponse = parseLLMResponse;
6
+ const logger_1 = require("../logger");
7
+ function parseAnalysisResponse(responseContent) {
8
+ let jsonContent = responseContent.trim();
9
+ const codeBlockMatch = jsonContent.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
10
+ if (codeBlockMatch) {
11
+ jsonContent = codeBlockMatch[1].trim();
12
+ }
13
+ const jsonMatch = jsonContent.match(/\{[\s\S]*\}/);
14
+ if (jsonMatch) {
15
+ jsonContent = jsonMatch[0];
16
+ }
17
+ const parsedResponse = JSON.parse(jsonContent);
18
+ if (typeof parsedResponse.is_significant !== 'boolean' ||
19
+ typeof parsedResponse.reason !== 'string') {
20
+ throw new Error(`Invalid response format. Expected {is_significant: boolean, reason: string}, got: ${JSON.stringify(parsedResponse).substring(0, 150)}...`);
21
+ }
22
+ if (parsedResponse.is_significant && !parsedResponse.reason) {
23
+ throw new Error('LLM indicated significant change but provided no reason');
24
+ }
25
+ const result = {
26
+ is_significant: parsedResponse.is_significant,
27
+ reason: parsedResponse.reason,
28
+ };
29
+ if (typeof parsedResponse.confidence === 'number' &&
30
+ parsedResponse.confidence >= 0 &&
31
+ parsedResponse.confidence <= 1) {
32
+ result.confidence = parsedResponse.confidence;
33
+ }
34
+ return result;
35
+ }
36
+ function extractTitleFromMarkdown(content) {
37
+ let cleanedContent = content.trim();
38
+ const codeBlockMatch = cleanedContent.match(/```(?:markdown|md)?\s*\n?([\s\S]*?)\n?```/);
39
+ if (codeBlockMatch) {
40
+ cleanedContent = codeBlockMatch[1].trim();
41
+ }
42
+ const titleMatch = cleanedContent.match(/^#\s+(.+)$/m);
43
+ return titleMatch ? titleMatch[1].trim() : 'Untitled Decision';
44
+ }
45
+ function parseLLMResponse(responseContent, validator) {
46
+ try {
47
+ let jsonContent = responseContent.trim();
48
+ const codeBlockMatch = jsonContent.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
49
+ if (codeBlockMatch) {
50
+ jsonContent = codeBlockMatch[1].trim();
51
+ }
52
+ const jsonMatch = jsonContent.match(/\{[\s\S]*\}/);
53
+ if (jsonMatch) {
54
+ jsonContent = jsonMatch[0];
55
+ }
56
+ const parsed = JSON.parse(jsonContent);
57
+ return validator(parsed);
58
+ }
59
+ catch (parseError) {
60
+ logger_1.loggerInstance.warn('Failed to parse LLM response as JSON', {
61
+ error: parseError,
62
+ response: responseContent,
63
+ });
64
+ throw new Error(`Failed to parse LLM response as JSON. Response was:\n${responseContent.substring(0, 200)}...`);
65
+ }
66
+ }
67
+ //# sourceMappingURL=response-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response-parser.js","sourceRoot":"","sources":["../../src/llm/response-parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ export interface AnalysisSummary {
2
+ fileCount: number;
3
+ mode: string;
4
+ isSignificant: boolean;
5
+ reason: string;
6
+ confidence?: number;
7
+ filePath?: string;
8
+ title?: string;
9
+ }
10
+ export interface DiffOptions {
11
+ mode: 'staged' | 'all' | 'branch-diff';
12
+ base?: string;
13
+ head?: string;
14
+ }
15
+ export declare class ConsolePresenter {
16
+ showConfigError(): void;
17
+ showGitError(message: string): void;
18
+ showReadFilesError(): void;
19
+ showNoChanges(options: DiffOptions): void;
20
+ showAnalyzingFiles(files: string[], options: DiffOptions): void;
21
+ showNoDiffContent(): void;
22
+ showSendingToLLM(options: DiffOptions, provider: string, model: string): void;
23
+ showAnalysisFailed(error?: string): void;
24
+ showAnalysisComplete(): void;
25
+ showSignificantResult(summary: AnalysisSummary): void;
26
+ showNotSignificantResult(summary: AnalysisSummary): void;
27
+ showGeneratingADR(): void;
28
+ showGenerationFailed(error?: string): void;
29
+ showADRSuccess(filePath: string): void;
30
+ showADRSaveError(error?: string): void;
31
+ showSkippingGeneration(): void;
32
+ showUnexpectedError(): void;
33
+ }
34
+ export declare const presenter: ConsolePresenter;
35
+ //# sourceMappingURL=console-presenter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"console-presenter.d.ts","sourceRoot":"","sources":["../../src/presenters/console-presenter.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,OAAO,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,GAAG,KAAK,GAAG,aAAa,CAAC;IACvC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,qBAAa,gBAAgB;IAC3B,eAAe,IAAI,IAAI;IAMvB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAInC,kBAAkB,IAAI,IAAI;IAI1B,aAAa,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAwBzC,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI;IAoB/D,iBAAiB,IAAI,IAAI;IAIzB,gBAAgB,CAAC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAY7E,kBAAkB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;IAKxC,oBAAoB,IAAI,IAAI;IAI5B,qBAAqB,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI;IAQrD,wBAAwB,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI;IASxD,iBAAiB,IAAI,IAAI;IAIzB,oBAAoB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;IAK1C,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAQtC,gBAAgB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;IAKtC,sBAAsB,IAAI,IAAI;IAK9B,mBAAmB,IAAI,IAAI;CAI5B;AAED,eAAO,MAAM,SAAS,kBAAyB,CAAC"}
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.presenter = exports.ConsolePresenter = void 0;
4
+ class ConsolePresenter {
5
+ showConfigError() {
6
+ console.error('\n❌ Configuration Error');
7
+ console.error('Configuration file not found or invalid.');
8
+ console.error('\n💡 Run `cadr init` to create a configuration file.\n');
9
+ }
10
+ showGitError(message) {
11
+ console.error(`\n❌ Git Error: ${message}\n`);
12
+ }
13
+ showReadFilesError() {
14
+ console.error('\n❌ Failed to read changed files\n');
15
+ }
16
+ showNoChanges(options) {
17
+ const modeText = options.mode === 'staged'
18
+ ? 'staged'
19
+ : options.mode === 'branch-diff'
20
+ ? `between ${options.base || 'origin/main'} and ${options.head || 'HEAD'}`
21
+ : 'uncommitted';
22
+ console.log(`\nℹ️ No changes to analyze ${options.mode === 'branch-diff' ? modeText : `(${modeText})`}`);
23
+ if (options.mode === 'staged') {
24
+ console.log('💡 Stage some files first:');
25
+ console.log(' git add <files>');
26
+ console.log(' cadr analyze --staged\n');
27
+ }
28
+ else if (options.mode === 'branch-diff') {
29
+ console.log('💡 No changes found between specified git references.\n');
30
+ }
31
+ else {
32
+ console.log('💡 Make some changes first, then run:');
33
+ console.log(' cadr analyze\n');
34
+ }
35
+ }
36
+ showAnalyzingFiles(files, options) {
37
+ const modeText = options.mode === 'staged'
38
+ ? 'staged'
39
+ : options.mode === 'branch-diff'
40
+ ? `between ${options.base || 'origin/main'} and ${options.head || 'HEAD'}`
41
+ : 'uncommitted';
42
+ const fileCountText = options.mode === 'branch-diff'
43
+ ? `${files.length} file${files.length === 1 ? '' : 's'} changed ${modeText}`
44
+ : `${files.length} ${modeText} file${files.length === 1 ? '' : 's'}`;
45
+ console.log(`\n📝 Analyzing ${fileCountText}:`);
46
+ files.forEach((file) => {
47
+ console.log(` • ${file}`);
48
+ });
49
+ console.log('');
50
+ }
51
+ showNoDiffContent() {
52
+ console.log('\nℹ️ No diff content found\n');
53
+ }
54
+ showSendingToLLM(options, provider, model) {
55
+ const analysisText = options.mode === 'staged'
56
+ ? 'staged changes'
57
+ : options.mode === 'branch-diff'
58
+ ? 'changes'
59
+ : 'uncommitted changes';
60
+ console.log(`🔍 Analyzing ${analysisText} for architectural significance...\n`);
61
+ console.log(`🤖 Sending to ${provider} ${model}...\n`);
62
+ }
63
+ showAnalysisFailed(error) {
64
+ console.error('\n❌ Analysis failed');
65
+ console.error(`\n${error || 'Unknown error occurred'}\n`);
66
+ }
67
+ showAnalysisComplete() {
68
+ console.log('✅ Analysis Complete\n');
69
+ }
70
+ showSignificantResult(summary) {
71
+ console.log('📊 Result: ✨ ARCHITECTURALLY SIGNIFICANT');
72
+ console.log(`💭 Reasoning: ${summary.reason}\n`);
73
+ if (summary.confidence) {
74
+ console.log(`🎯 Confidence: ${(summary.confidence * 100).toFixed(0)}%\n`);
75
+ }
76
+ }
77
+ showNotSignificantResult(summary) {
78
+ console.log('📊 Result: ℹ️ NOT ARCHITECTURALLY SIGNIFICANT');
79
+ console.log(`💭 Reasoning: ${summary.reason}\n`);
80
+ if (summary.confidence) {
81
+ console.log(`🎯 Confidence: ${(summary.confidence * 100).toFixed(0)}%\n`);
82
+ }
83
+ console.log('✅ No ADR needed for these changes.\n');
84
+ }
85
+ showGeneratingADR() {
86
+ console.log('\n🧠 Generating ADR draft...\n');
87
+ }
88
+ showGenerationFailed(error) {
89
+ console.error('\n❌ ADR generation failed');
90
+ console.error(`\n${error || 'Unknown error occurred'}\n`);
91
+ }
92
+ showADRSuccess(filePath) {
93
+ console.log('✅ Success! Draft ADR created\n');
94
+ console.log(`📄 File: ${filePath}\n`);
95
+ console.log('💡 Next steps:');
96
+ console.log(' 1. Review and refine the generated ADR');
97
+ console.log(' 2. Commit it alongside your code changes\n');
98
+ }
99
+ showADRSaveError(error) {
100
+ console.error('\n❌ Failed to save ADR');
101
+ console.error(`\n${error || 'Unknown error occurred'}\n`);
102
+ }
103
+ showSkippingGeneration() {
104
+ console.log('\n📋 Skipping ADR generation');
105
+ console.log('🎯 Recommendation: Consider documenting this decision manually.\n');
106
+ }
107
+ showUnexpectedError() {
108
+ console.error('\n❌ An unexpected error occurred');
109
+ console.error('Please check the logs for more details.\n');
110
+ }
111
+ }
112
+ exports.ConsolePresenter = ConsolePresenter;
113
+ exports.presenter = new ConsolePresenter();
114
+ //# sourceMappingURL=console-presenter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"console-presenter.js","sourceRoot":"","sources":["../../src/presenters/console-presenter.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cadr-cli",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "Continuous Architectural Decision Records - Automatically capture ADRs as you code",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -1,8 +1,5 @@
1
1
  /**
2
2
  * ADR File Management Module Tests
3
- *
4
- * Tests for ADR file creation, numbering, and management.
5
- * Following TDD: These tests are written BEFORE implementation.
6
3
  */
7
4
 
8
5
  import * as fs from 'fs';
@@ -20,14 +17,12 @@ describe('ADR Module', () => {
20
17
  const testDir = path.join(__dirname, '../test-adrs');
21
18
 
22
19
  beforeEach(() => {
23
- // Clean up test directory before each test
24
20
  if (fs.existsSync(testDir)) {
25
21
  fs.rmSync(testDir, { recursive: true, force: true });
26
22
  }
27
23
  });
28
24
 
29
25
  afterEach(() => {
30
- // Clean up test directory after each test
31
26
  if (fs.existsSync(testDir)) {
32
27
  fs.rmSync(testDir, { recursive: true, force: true });
33
28
  }
@@ -187,14 +182,15 @@ describe('ADR Module', () => {
187
182
  expect(result1.filePath).toContain('0001-first.md');
188
183
  expect(result2.filePath).toContain('0002-second.md');
189
184
  expect(result3.filePath).toContain('0003-third.md');
190
-
185
+
191
186
  expect(fs.existsSync(result1.filePath!)).toBe(true);
192
187
  expect(fs.existsSync(result2.filePath!)).toBe(true);
193
188
  expect(fs.existsSync(result3.filePath!)).toBe(true);
194
189
  });
195
190
 
196
191
  it('saves correct content to file', () => {
197
- const content = '# My Decision\n\n* Status: accepted\n* Date: 2025-10-21\n\n## Context\n\nSome context here.';
192
+ const content =
193
+ '# My Decision\n\n* Status: accepted\n* Date: 2025-10-21\n\n## Context\n\nSome context here.';
198
194
  const result = saveADR(content, 'My Decision', testDir);
199
195
 
200
196
  const savedContent = fs.readFileSync(result.filePath!, 'utf-8');
@@ -202,7 +198,6 @@ describe('ADR Module', () => {
202
198
  });
203
199
 
204
200
  it('uses default directory when not specified', () => {
205
- // Clean up default directory
206
201
  const defaultPath = path.join(process.cwd(), DEFAULT_ADR_DIR);
207
202
  if (fs.existsSync(defaultPath)) {
208
203
  fs.rmSync(defaultPath, { recursive: true, force: true });
@@ -213,44 +208,38 @@ describe('ADR Module', () => {
213
208
 
214
209
  expect(result.success).toBe(true);
215
210
  expect(result.filePath).toContain(DEFAULT_ADR_DIR);
216
-
217
- // Cleanup
211
+
218
212
  if (fs.existsSync(defaultPath)) {
219
213
  fs.rmSync(defaultPath, { recursive: true, force: true });
220
214
  }
221
215
  });
222
216
 
223
217
  it('handles file write errors gracefully', () => {
224
- // Create a read-only directory to trigger write error
225
218
  fs.mkdirSync(testDir, { recursive: true });
226
-
227
- // Make directory read-only (if not Windows)
219
+
228
220
  if (process.platform !== 'win32') {
229
221
  fs.chmodSync(testDir, 0o444);
230
-
222
+
231
223
  const content = '# Test\n\nContent';
232
224
  const result = saveADR(content, 'Test', testDir);
233
225
 
234
226
  expect(result.success).toBe(false);
235
227
  expect(result.error).toBeDefined();
236
228
  expect(result.filePath).toBeUndefined();
237
-
238
- // Restore permissions for cleanup
229
+
239
230
  fs.chmodSync(testDir, 0o755);
240
231
  } else {
241
- // On Windows, just verify error handling structure exists
242
- // Skip actual permission test as Windows permissions work differently
243
232
  const content = '# Test\n\nContent';
244
233
  const result = saveADR(content, 'Test', testDir);
245
-
246
- // Should succeed on Windows since we can't easily simulate permission errors
234
+
247
235
  expect(result).toHaveProperty('success');
248
236
  expect(typeof result.success).toBe('boolean');
249
237
  }
250
238
  });
251
239
 
252
240
  it('handles long titles correctly', () => {
253
- const longTitle = 'This is a very long title that should still work correctly when converted to a filename slug';
241
+ const longTitle =
242
+ 'This is a very long title that should still work correctly when converted to a filename slug';
254
243
  const content = `# ${longTitle}\n\nContent`;
255
244
  const result = saveADR(content, longTitle, testDir);
256
245
 
@@ -263,7 +252,6 @@ describe('ADR Module', () => {
263
252
  const result = saveADR(content, 'Decision with émojis 🚀', testDir);
264
253
 
265
254
  expect(result.success).toBe(true);
266
- // Should strip unicode to safe characters
267
255
  expect(result.filePath).toMatch(/0001-.+\.md$/);
268
256
  });
269
257
  });
@@ -275,4 +263,3 @@ describe('ADR Module', () => {
275
263
  });
276
264
  });
277
265
  });
278
-
@@ -1,46 +1,32 @@
1
1
  /**
2
2
  * ADR File Management Module
3
- *
3
+ *
4
4
  * Handles creation and management of Architectural Decision Record files.
5
5
  * Follows MADR (Markdown Architectural Decision Records) format.
6
6
  */
7
7
 
8
8
  import * as fs from 'fs';
9
9
  import * as path from 'path';
10
- import { loggerInstance as logger } from './logger';
10
+ import { loggerInstance as logger } from '../logger';
11
11
 
12
- /**
13
- * Default ADR directory relative to project root
14
- */
15
12
  export const DEFAULT_ADR_DIR = 'docs/adr';
16
13
 
17
- /**
18
- * Convert a title to a filename-safe slug
19
- * Example: "Use PostgreSQL for Storage" -> "use-postgresql-for-storage"
20
- */
21
14
  export function titleToSlug(title: string): string {
22
15
  return title
23
16
  .toLowerCase()
24
- .replace(/[^a-z0-9]+/g, '-') // Replace non-alphanumeric with hyphens
25
- .replace(/^-+|-+$/g, ''); // Remove leading/trailing hyphens
17
+ .replace(/[^a-z0-9]+/g, '-')
18
+ .replace(/^-+|-+$/g, '');
26
19
  }
27
20
 
28
- /**
29
- * Get the next ADR number by scanning existing ADR files
30
- *
31
- * @param adrDir - Directory containing ADR files
32
- * @returns Next available ADR number (e.g., 1, 2, 3...)
33
- */
34
21
  export function getNextADRNumber(adrDir: string): number {
35
22
  try {
36
23
  if (!fs.existsSync(adrDir)) {
37
- return 1; // First ADR
24
+ return 1;
38
25
  }
39
26
 
40
27
  const files = fs.readdirSync(adrDir);
41
28
  const adrNumbers: number[] = [];
42
29
 
43
- // Extract numbers from existing ADR files (format: 0001-title.md)
44
30
  for (const file of files) {
45
31
  const match = file.match(/^(\d{4})-/);
46
32
  if (match) {
@@ -52,7 +38,6 @@ export function getNextADRNumber(adrDir: string): number {
52
38
  return 1;
53
39
  }
54
40
 
55
- // Return next number after the highest existing number
56
41
  return Math.max(...adrNumbers) + 1;
57
42
  } catch (error) {
58
43
  logger.warn('Failed to scan existing ADRs, defaulting to 1', { error, adrDir });
@@ -60,11 +45,6 @@ export function getNextADRNumber(adrDir: string): number {
60
45
  }
61
46
  }
62
47
 
63
- /**
64
- * Ensure ADR directory exists, create if it doesn't
65
- *
66
- * @param adrDir - Directory path to ensure exists
67
- */
68
48
  export function ensureADRDirectory(adrDir: string): void {
69
49
  if (!fs.existsSync(adrDir)) {
70
50
  logger.info('Creating ADR directory', { adrDir });
@@ -72,56 +52,36 @@ export function ensureADRDirectory(adrDir: string): void {
72
52
  }
73
53
  }
74
54
 
75
- /**
76
- * Generate ADR filename from number and title
77
- *
78
- * @param number - ADR number (will be zero-padded to 4 digits)
79
- * @param title - ADR title (will be converted to slug)
80
- * @returns Filename like "0001-use-postgresql-for-storage.md"
81
- */
82
55
  export function generateADRFilename(number: number, title: string): string {
83
56
  const paddedNumber = String(number).padStart(4, '0');
84
57
  const slug = titleToSlug(title);
85
58
  return `${paddedNumber}-${slug}.md`;
86
59
  }
87
60
 
88
- /**
89
- * Save ADR content to file
90
- *
91
- * @param content - Full markdown content of the ADR
92
- * @param title - Title extracted from ADR content
93
- * @param adrDir - Directory to save ADR in (defaults to docs/adr)
94
- * @returns Object with success status, file path, and any error
95
- */
96
61
  export function saveADR(
97
62
  content: string,
98
63
  title: string,
99
64
  adrDir: string = DEFAULT_ADR_DIR
100
65
  ): { success: boolean; filePath?: string; error?: string } {
101
66
  try {
102
- // Ensure directory exists
103
67
  ensureADRDirectory(adrDir);
104
68
 
105
- // Get next ADR number
106
69
  const adrNumber = getNextADRNumber(adrDir);
107
70
 
108
- // Generate filename
109
71
  const filename = generateADRFilename(adrNumber, title);
110
72
  const filePath = path.join(adrDir, filename);
111
73
 
112
- // Check if file already exists (shouldn't happen, but safety check)
113
74
  if (fs.existsSync(filePath)) {
114
75
  logger.warn('ADR file already exists, using alternative name', { filePath });
115
76
  const alternativeFilename = generateADRFilename(adrNumber + 1, title);
116
77
  const alternativeFilePath = path.join(adrDir, alternativeFilename);
117
-
78
+
118
79
  fs.writeFileSync(alternativeFilePath, content, 'utf-8');
119
80
  logger.info('ADR saved successfully', { filePath: alternativeFilePath });
120
-
81
+
121
82
  return { success: true, filePath: alternativeFilePath };
122
83
  }
123
84
 
124
- // Write ADR content to file
125
85
  fs.writeFileSync(filePath, content, 'utf-8');
126
86
 
127
87
  logger.info('ADR saved successfully', { filePath, adrNumber });
@@ -133,4 +93,3 @@ export function saveADR(
133
93
  return { success: false, error: errorMessage };
134
94
  }
135
95
  }
136
-
@@ -0,0 +1 @@
1
+ export * from './adr';