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.
- 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/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/commands/analyze.js +3 -3
- package/dist/commands/analyze.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.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/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/{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/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/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.ts +175 -0
- package/src/analysis/strategies/git-strategy.ts +106 -0
- package/src/commands/analyze.ts +8 -9
- package/src/git/git.errors.ts +10 -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.ts → llm/llm.ts} +46 -107
- package/src/{prompts.ts → llm/prompts.ts} +30 -72
- package/src/llm/response-parser.ts +90 -0
- package/src/presenters/console-presenter.ts +152 -0
- 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.d.ts +0 -2
- package/dist/llm.test.d.ts.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
|
@@ -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,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 =
|
|
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 =
|
|
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 '
|
|
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, '-')
|
|
25
|
-
.replace(/^-+|-+$/g, '');
|
|
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;
|
|
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
|
-
|
package/src/adr/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './adr';
|