commit-analyzer 1.0.2 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +11 -1
- package/README.md +33 -2
- package/commits.csv +2 -0
- package/eslint.config.mts +45 -0
- package/package.json +17 -9
- package/src/1.domain/analysis.ts +93 -0
- package/src/1.domain/analyzed-commit.ts +97 -0
- package/src/1.domain/application-error.ts +32 -0
- package/src/1.domain/category.ts +52 -0
- package/src/1.domain/commit-analysis-service.ts +92 -0
- package/src/1.domain/commit-hash.ts +40 -0
- package/src/1.domain/commit.ts +99 -0
- package/src/1.domain/date-formatting-service.ts +81 -0
- package/src/1.domain/date-range.ts +76 -0
- package/src/1.domain/report-generation-service.ts +292 -0
- package/src/2.application/analyze-commits.usecase.ts +307 -0
- package/src/2.application/generate-report.usecase.ts +204 -0
- package/src/2.application/llm-service.ts +54 -0
- package/src/2.application/resume-analysis.usecase.ts +123 -0
- package/src/3.presentation/analysis-repository.interface.ts +27 -0
- package/src/3.presentation/analyze-command.ts +128 -0
- package/src/3.presentation/cli-application.ts +255 -0
- package/src/3.presentation/command-handler.interface.ts +4 -0
- package/src/3.presentation/commit-analysis-controller.ts +101 -0
- package/src/3.presentation/commit-repository.interface.ts +47 -0
- package/src/3.presentation/console-formatter.ts +129 -0
- package/src/3.presentation/progress-repository.interface.ts +49 -0
- package/src/3.presentation/report-command.ts +50 -0
- package/src/3.presentation/resume-command.ts +59 -0
- package/src/3.presentation/storage-repository.interface.ts +33 -0
- package/src/3.presentation/storage-service.interface.ts +32 -0
- package/src/3.presentation/version-control-service.interface.ts +41 -0
- package/src/4.infrastructure/cache-service.ts +271 -0
- package/src/4.infrastructure/cached-analysis-repository.ts +46 -0
- package/src/4.infrastructure/claude-llm-adapter.ts +124 -0
- package/src/4.infrastructure/csv-service.ts +206 -0
- package/src/4.infrastructure/file-storage-repository.ts +108 -0
- package/src/4.infrastructure/file-system-storage-adapter.ts +87 -0
- package/src/4.infrastructure/gemini-llm-adapter.ts +46 -0
- package/src/4.infrastructure/git-adapter.ts +116 -0
- package/src/4.infrastructure/git-commit-repository.ts +85 -0
- package/src/4.infrastructure/json-progress-tracker.ts +182 -0
- package/src/4.infrastructure/llm-adapter-factory.ts +26 -0
- package/src/4.infrastructure/llm-adapter.ts +455 -0
- package/src/4.infrastructure/llm-analysis-repository.ts +38 -0
- package/src/4.infrastructure/openai-llm-adapter.ts +57 -0
- package/src/di.ts +108 -0
- package/src/main.ts +63 -0
- package/src/utils/app-paths.ts +36 -0
- package/src/utils/concurrency.ts +81 -0
- package/src/utils.ts +77 -0
- package/tsconfig.json +7 -1
- package/src/cli.ts +0 -170
- package/src/csv-reader.ts +0 -180
- package/src/csv.ts +0 -40
- package/src/errors.ts +0 -49
- package/src/git.ts +0 -112
- package/src/index.ts +0 -395
- package/src/llm.ts +0 -396
- package/src/progress.ts +0 -84
- package/src/report-generator.ts +0 -286
- package/src/types.ts +0 -24
package/src/report-generator.ts
DELETED
|
@@ -1,286 +0,0 @@
|
|
|
1
|
-
import { writeFileSync } from "fs"
|
|
2
|
-
import { CSVReaderService, ParsedCSVRow } from "./csv-reader"
|
|
3
|
-
import { LLMService } from "./llm"
|
|
4
|
-
|
|
5
|
-
export class MarkdownReportGenerator {
|
|
6
|
-
/**
|
|
7
|
-
* Generate a markdown report from CSV data
|
|
8
|
-
*/
|
|
9
|
-
static async generateReport(
|
|
10
|
-
csvFilePath: string,
|
|
11
|
-
outputPath: string,
|
|
12
|
-
): Promise<void> {
|
|
13
|
-
console.log(`Reading CSV data from ${csvFilePath}...`)
|
|
14
|
-
|
|
15
|
-
// Read and parse CSV data
|
|
16
|
-
const csvData = CSVReaderService.readCSV(csvFilePath)
|
|
17
|
-
|
|
18
|
-
if (csvData.length === 0) {
|
|
19
|
-
throw new Error("No data found in CSV file")
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// Get statistics for logging
|
|
23
|
-
const stats = CSVReaderService.getStatistics(csvData)
|
|
24
|
-
console.log(
|
|
25
|
-
`Found ${stats.totalRows} commits spanning ${stats.yearRange.min}-${stats.yearRange.max}`,
|
|
26
|
-
)
|
|
27
|
-
console.log(
|
|
28
|
-
`Categories: ${stats.categoryBreakdown.feature} features, ${stats.categoryBreakdown.process} process, ${stats.categoryBreakdown.tweak} tweaks`,
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
console.log("Generating condensed report...")
|
|
32
|
-
|
|
33
|
-
// Generate commit analysis section programmatically
|
|
34
|
-
const analysisSection = this.generateAnalysisSection(csvData)
|
|
35
|
-
|
|
36
|
-
// Convert CSV data to string format for LLM (for content summarization only)
|
|
37
|
-
const csvContent = this.convertToCSVString(csvData)
|
|
38
|
-
|
|
39
|
-
// Generate detailed yearly summaries using LLM
|
|
40
|
-
const yearlyContent = await this.generateYearlySummariesWithLLM(csvContent)
|
|
41
|
-
|
|
42
|
-
// Combine analysis section with LLM-generated content
|
|
43
|
-
const reportContent = `# Development Summary Report\n\n${analysisSection}\n\n${yearlyContent}`
|
|
44
|
-
|
|
45
|
-
// Write to output file
|
|
46
|
-
writeFileSync(outputPath, reportContent, "utf8")
|
|
47
|
-
|
|
48
|
-
console.log(`Report generated: ${outputPath}`)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Convert parsed CSV data back to CSV string format for LLM consumption
|
|
53
|
-
*/
|
|
54
|
-
private static convertToCSVString(data: ParsedCSVRow[]): string {
|
|
55
|
-
const header = "year,category,summary,description"
|
|
56
|
-
const rows = data.map((row) =>
|
|
57
|
-
[
|
|
58
|
-
row.year,
|
|
59
|
-
this.escapeCsvField(row.category),
|
|
60
|
-
this.escapeCsvField(row.summary),
|
|
61
|
-
this.escapeCsvField(row.description),
|
|
62
|
-
].join(","),
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
return [header, ...rows].join("\n")
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Escape CSV fields that contain commas, quotes, or newlines
|
|
70
|
-
*/
|
|
71
|
-
private static escapeCsvField(field: string): string {
|
|
72
|
-
if (field.includes(",") || field.includes('"') || field.includes("\n")) {
|
|
73
|
-
return `"${field.replace(/"/g, '""')}"`
|
|
74
|
-
}
|
|
75
|
-
return field
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Generate commit analysis section with accurate counts
|
|
80
|
-
*/
|
|
81
|
-
private static generateAnalysisSection(csvData: ParsedCSVRow[]): string {
|
|
82
|
-
const stats = CSVReaderService.getStatistics(csvData)
|
|
83
|
-
|
|
84
|
-
// Group data by year for detailed breakdown
|
|
85
|
-
const yearlyStats = csvData.reduce(
|
|
86
|
-
(acc, row) => {
|
|
87
|
-
if (!acc[row.year]) {
|
|
88
|
-
acc[row.year] = { tweak: 0, feature: 0, process: 0, total: 0 }
|
|
89
|
-
}
|
|
90
|
-
acc[row.year][row.category]++
|
|
91
|
-
acc[row.year].total++
|
|
92
|
-
return acc
|
|
93
|
-
},
|
|
94
|
-
{} as Record<
|
|
95
|
-
number,
|
|
96
|
-
{ tweak: number; feature: number; process: number; total: number }
|
|
97
|
-
>,
|
|
98
|
-
)
|
|
99
|
-
|
|
100
|
-
// Sort years in descending order
|
|
101
|
-
const sortedYears = Object.keys(yearlyStats)
|
|
102
|
-
.map(Number)
|
|
103
|
-
.sort((a, b) => b - a)
|
|
104
|
-
|
|
105
|
-
let analysisContent = `## Commit Analysis\n`
|
|
106
|
-
analysisContent += `- **Total Commits**: ${stats.totalRows} commits across ${stats.yearRange.min}-${stats.yearRange.max}\n`
|
|
107
|
-
|
|
108
|
-
// Add year-by-year breakdown
|
|
109
|
-
for (const year of sortedYears) {
|
|
110
|
-
const yearData = yearlyStats[year]
|
|
111
|
-
analysisContent += `- **${year}**: ${yearData.total} commits (${yearData.feature} features, ${yearData.process} process, ${yearData.tweak} tweaks)\n`
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return analysisContent
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Generate yearly summaries using LLM service
|
|
119
|
-
*/
|
|
120
|
-
private static async generateYearlySummariesWithLLM(
|
|
121
|
-
csvContent: string,
|
|
122
|
-
): Promise<string> {
|
|
123
|
-
const prompt = this.buildReportPrompt(csvContent)
|
|
124
|
-
|
|
125
|
-
try {
|
|
126
|
-
// Use the same retry logic as commit analysis
|
|
127
|
-
const response = await this.callLLMWithRetry(prompt)
|
|
128
|
-
return this.parseReportResponse(response)
|
|
129
|
-
} catch (error) {
|
|
130
|
-
throw new Error(
|
|
131
|
-
`Failed to generate report: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
132
|
-
)
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Build the prompt for report generation based on the template
|
|
138
|
-
*/
|
|
139
|
-
private static buildReportPrompt(csvContent: string): string {
|
|
140
|
-
return `Analyze the following CSV data containing git commit analysis results and generate a condensed markdown development summary report.
|
|
141
|
-
|
|
142
|
-
CSV DATA:
|
|
143
|
-
${csvContent}
|
|
144
|
-
|
|
145
|
-
INSTRUCTIONS:
|
|
146
|
-
1. Group the data by year (descending order, most recent first)
|
|
147
|
-
2. Within each year, group by category: Features, Process Improvements, and Tweaks & Bug Fixes
|
|
148
|
-
3. Consolidate similar items within each category to create readable summaries
|
|
149
|
-
4. Focus on what was accomplished rather than individual commit details
|
|
150
|
-
5. Use clear, professional language appropriate for stakeholders
|
|
151
|
-
|
|
152
|
-
CATEGORY MAPPING:
|
|
153
|
-
- "feature" → "Features" section
|
|
154
|
-
- "process" → "Processes" section
|
|
155
|
-
- "tweak" → "Tweaks & Bug Fixes" section
|
|
156
|
-
|
|
157
|
-
CONSOLIDATION GUIDELINES:
|
|
158
|
-
- Group similar features together (e.g., "authentication system improvements")
|
|
159
|
-
- Combine related bug fixes (e.g., "resolved 8 authentication issues")
|
|
160
|
-
- Summarize process changes by theme (e.g., "CI/CD pipeline enhancements")
|
|
161
|
-
- Use bullet points for individual items within categories
|
|
162
|
-
- Aim for 3-7 bullet points per category per year
|
|
163
|
-
- Include specific numbers when relevant (e.g., "15 bug fixes", "3 new features")
|
|
164
|
-
|
|
165
|
-
OUTPUT FORMAT:
|
|
166
|
-
Generate yearly summary sections with this exact structure (DO NOT include the main title or commit analysis section):
|
|
167
|
-
|
|
168
|
-
\`\`\`markdown
|
|
169
|
-
## [YEAR]
|
|
170
|
-
### Features
|
|
171
|
-
- [Consolidated feature summary 1]
|
|
172
|
-
- [Consolidated feature summary 2]
|
|
173
|
-
- [Additional features as needed]
|
|
174
|
-
|
|
175
|
-
### Processes
|
|
176
|
-
- [Consolidated process improvement 1]
|
|
177
|
-
- [Consolidated process improvement 2]
|
|
178
|
-
- [Additional process items as needed]
|
|
179
|
-
|
|
180
|
-
### Tweaks & Bug Fixes
|
|
181
|
-
- [Consolidated tweak/fix summary 1]
|
|
182
|
-
- [Consolidated tweak/fix summary 2]
|
|
183
|
-
- [Additional tweaks/fixes as needed]
|
|
184
|
-
|
|
185
|
-
## [PREVIOUS YEAR]
|
|
186
|
-
[Repeat structure for each year in the data]
|
|
187
|
-
\`\`\`
|
|
188
|
-
|
|
189
|
-
QUALITY REQUIREMENTS:
|
|
190
|
-
- Keep summaries concise but informative
|
|
191
|
-
- Use active voice and clear language
|
|
192
|
-
- Avoid technical jargon where possible
|
|
193
|
-
- Ensure each bullet point represents meaningful work
|
|
194
|
-
- Make the report valuable for both technical and non-technical readers
|
|
195
|
-
|
|
196
|
-
Generate the markdown report now:`
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Call LLM with retry logic similar to commit analysis
|
|
201
|
-
*/
|
|
202
|
-
private static async callLLMWithRetry(prompt: string): Promise<string> {
|
|
203
|
-
const maxRetries = LLMService.getMaxRetries()
|
|
204
|
-
let lastError: Error | null = null
|
|
205
|
-
|
|
206
|
-
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
207
|
-
try {
|
|
208
|
-
// Create a mock commit object for the LLM service
|
|
209
|
-
const mockCommit = {
|
|
210
|
-
hash: "report-generation",
|
|
211
|
-
message: "Generate report from CSV data",
|
|
212
|
-
date: new Date(),
|
|
213
|
-
diff: prompt,
|
|
214
|
-
year: new Date().getFullYear(),
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// Use the existing LLM service but intercept the response
|
|
218
|
-
const { execSync } = require("child_process")
|
|
219
|
-
const currentModel = LLMService.getModel()
|
|
220
|
-
|
|
221
|
-
console.log(` - Using model: ${currentModel}`)
|
|
222
|
-
console.log(
|
|
223
|
-
` - Processing ${prompt.split("\n").length} lines of CSV data`,
|
|
224
|
-
)
|
|
225
|
-
|
|
226
|
-
const output = execSync(currentModel, {
|
|
227
|
-
input: prompt,
|
|
228
|
-
encoding: "utf8",
|
|
229
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
230
|
-
timeout: 120000, // Longer timeout for report generation
|
|
231
|
-
})
|
|
232
|
-
|
|
233
|
-
return output.trim()
|
|
234
|
-
} catch (error) {
|
|
235
|
-
lastError = error instanceof Error ? error : new Error("Unknown error")
|
|
236
|
-
|
|
237
|
-
console.log(` - Error generating report:`)
|
|
238
|
-
console.log(` Attempt: ${attempt}/${maxRetries}`)
|
|
239
|
-
console.log(` Error: ${lastError.message}`)
|
|
240
|
-
|
|
241
|
-
if (attempt < maxRetries) {
|
|
242
|
-
const delay = Math.min(5000 * Math.pow(2, attempt - 1), 30000)
|
|
243
|
-
console.log(` Retrying in ${delay / 1000}s...`)
|
|
244
|
-
await this.sleep(delay)
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
throw new Error(
|
|
250
|
-
`Failed to generate report after ${maxRetries} attempts: ${lastError?.message || "Unknown error"}`,
|
|
251
|
-
)
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* Parse the LLM response to extract the yearly summary content
|
|
256
|
-
*/
|
|
257
|
-
private static parseReportResponse(response: string): string {
|
|
258
|
-
// Look for markdown block first
|
|
259
|
-
const markdownMatch = response.match(/```markdown\s*([\s\S]*?)\s*```/)
|
|
260
|
-
if (markdownMatch) {
|
|
261
|
-
return markdownMatch[1].trim()
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// If no markdown block, look for content starting with "##" (yearly sections)
|
|
265
|
-
const yearSectionMatch = response.match(/^(##\s+\d{4}[\s\S]*)/m)
|
|
266
|
-
if (yearSectionMatch) {
|
|
267
|
-
return yearSectionMatch[1].trim()
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// If no clear structure found, return the entire response but log a warning
|
|
271
|
-
console.log(
|
|
272
|
-
" - Warning: Could not find structured yearly sections in LLM response",
|
|
273
|
-
)
|
|
274
|
-
console.log(` - Response preview: ${response.substring(0, 200)}...`)
|
|
275
|
-
|
|
276
|
-
return response.trim()
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* Sleep utility for retry delays
|
|
281
|
-
*/
|
|
282
|
-
private static sleep(ms: number): Promise<void> {
|
|
283
|
-
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
package/src/types.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
export interface CommitInfo {
|
|
2
|
-
hash: string
|
|
3
|
-
message: string
|
|
4
|
-
date: Date
|
|
5
|
-
diff: string
|
|
6
|
-
year: number
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export interface LLMAnalysis {
|
|
10
|
-
category: "tweak" | "feature" | "process"
|
|
11
|
-
summary: string
|
|
12
|
-
description: string
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface AnalyzedCommit extends CommitInfo {
|
|
16
|
-
analysis: LLMAnalysis
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface CSVRow {
|
|
20
|
-
year: number
|
|
21
|
-
category: string
|
|
22
|
-
summary: string
|
|
23
|
-
description: string
|
|
24
|
-
}
|