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.
Files changed (62) hide show
  1. package/.claude/settings.local.json +11 -1
  2. package/README.md +33 -2
  3. package/commits.csv +2 -0
  4. package/eslint.config.mts +45 -0
  5. package/package.json +17 -9
  6. package/src/1.domain/analysis.ts +93 -0
  7. package/src/1.domain/analyzed-commit.ts +97 -0
  8. package/src/1.domain/application-error.ts +32 -0
  9. package/src/1.domain/category.ts +52 -0
  10. package/src/1.domain/commit-analysis-service.ts +92 -0
  11. package/src/1.domain/commit-hash.ts +40 -0
  12. package/src/1.domain/commit.ts +99 -0
  13. package/src/1.domain/date-formatting-service.ts +81 -0
  14. package/src/1.domain/date-range.ts +76 -0
  15. package/src/1.domain/report-generation-service.ts +292 -0
  16. package/src/2.application/analyze-commits.usecase.ts +307 -0
  17. package/src/2.application/generate-report.usecase.ts +204 -0
  18. package/src/2.application/llm-service.ts +54 -0
  19. package/src/2.application/resume-analysis.usecase.ts +123 -0
  20. package/src/3.presentation/analysis-repository.interface.ts +27 -0
  21. package/src/3.presentation/analyze-command.ts +128 -0
  22. package/src/3.presentation/cli-application.ts +255 -0
  23. package/src/3.presentation/command-handler.interface.ts +4 -0
  24. package/src/3.presentation/commit-analysis-controller.ts +101 -0
  25. package/src/3.presentation/commit-repository.interface.ts +47 -0
  26. package/src/3.presentation/console-formatter.ts +129 -0
  27. package/src/3.presentation/progress-repository.interface.ts +49 -0
  28. package/src/3.presentation/report-command.ts +50 -0
  29. package/src/3.presentation/resume-command.ts +59 -0
  30. package/src/3.presentation/storage-repository.interface.ts +33 -0
  31. package/src/3.presentation/storage-service.interface.ts +32 -0
  32. package/src/3.presentation/version-control-service.interface.ts +41 -0
  33. package/src/4.infrastructure/cache-service.ts +271 -0
  34. package/src/4.infrastructure/cached-analysis-repository.ts +46 -0
  35. package/src/4.infrastructure/claude-llm-adapter.ts +124 -0
  36. package/src/4.infrastructure/csv-service.ts +206 -0
  37. package/src/4.infrastructure/file-storage-repository.ts +108 -0
  38. package/src/4.infrastructure/file-system-storage-adapter.ts +87 -0
  39. package/src/4.infrastructure/gemini-llm-adapter.ts +46 -0
  40. package/src/4.infrastructure/git-adapter.ts +116 -0
  41. package/src/4.infrastructure/git-commit-repository.ts +85 -0
  42. package/src/4.infrastructure/json-progress-tracker.ts +182 -0
  43. package/src/4.infrastructure/llm-adapter-factory.ts +26 -0
  44. package/src/4.infrastructure/llm-adapter.ts +455 -0
  45. package/src/4.infrastructure/llm-analysis-repository.ts +38 -0
  46. package/src/4.infrastructure/openai-llm-adapter.ts +57 -0
  47. package/src/di.ts +108 -0
  48. package/src/main.ts +63 -0
  49. package/src/utils/app-paths.ts +36 -0
  50. package/src/utils/concurrency.ts +81 -0
  51. package/src/utils.ts +77 -0
  52. package/tsconfig.json +7 -1
  53. package/src/cli.ts +0 -170
  54. package/src/csv-reader.ts +0 -180
  55. package/src/csv.ts +0 -40
  56. package/src/errors.ts +0 -49
  57. package/src/git.ts +0 -112
  58. package/src/index.ts +0 -395
  59. package/src/llm.ts +0 -396
  60. package/src/progress.ts +0 -84
  61. package/src/report-generator.ts +0 -286
  62. package/src/types.ts +0 -24
@@ -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
- }