commit-analyzer 1.1.4 → 1.1.6

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 (55) hide show
  1. package/README.md +164 -82
  2. package/dist/main.ts +0 -0
  3. package/package.json +2 -1
  4. package/.claude/settings.local.json +0 -23
  5. package/commits.csv +0 -2
  6. package/csv-to-report-prompt.md +0 -97
  7. package/eslint.config.mts +0 -45
  8. package/prompt.md +0 -69
  9. package/src/1.domain/analysis.ts +0 -93
  10. package/src/1.domain/analyzed-commit.ts +0 -97
  11. package/src/1.domain/application-error.ts +0 -32
  12. package/src/1.domain/category.ts +0 -52
  13. package/src/1.domain/commit-analysis-service.ts +0 -92
  14. package/src/1.domain/commit-hash.ts +0 -40
  15. package/src/1.domain/commit.ts +0 -99
  16. package/src/1.domain/date-formatting-service.ts +0 -81
  17. package/src/1.domain/date-range.ts +0 -76
  18. package/src/1.domain/report-generation-service.ts +0 -443
  19. package/src/2.application/analyze-commits.usecase.ts +0 -307
  20. package/src/2.application/generate-report.usecase.ts +0 -209
  21. package/src/2.application/llm-service.ts +0 -54
  22. package/src/2.application/resume-analysis.usecase.ts +0 -123
  23. package/src/3.presentation/analysis-repository.interface.ts +0 -27
  24. package/src/3.presentation/analyze-command.ts +0 -128
  25. package/src/3.presentation/cli-application.ts +0 -278
  26. package/src/3.presentation/command-handler.interface.ts +0 -4
  27. package/src/3.presentation/commit-analysis-controller.ts +0 -101
  28. package/src/3.presentation/commit-repository.interface.ts +0 -47
  29. package/src/3.presentation/console-formatter.ts +0 -129
  30. package/src/3.presentation/progress-repository.interface.ts +0 -49
  31. package/src/3.presentation/report-command.ts +0 -50
  32. package/src/3.presentation/resume-command.ts +0 -59
  33. package/src/3.presentation/storage-repository.interface.ts +0 -33
  34. package/src/3.presentation/storage-service.interface.ts +0 -32
  35. package/src/3.presentation/version-control-service.interface.ts +0 -46
  36. package/src/4.infrastructure/cache-service.ts +0 -271
  37. package/src/4.infrastructure/cached-analysis-repository.ts +0 -46
  38. package/src/4.infrastructure/claude-llm-adapter.ts +0 -124
  39. package/src/4.infrastructure/csv-service.ts +0 -252
  40. package/src/4.infrastructure/file-storage-repository.ts +0 -108
  41. package/src/4.infrastructure/file-system-storage-adapter.ts +0 -87
  42. package/src/4.infrastructure/gemini-llm-adapter.ts +0 -46
  43. package/src/4.infrastructure/git-adapter.ts +0 -143
  44. package/src/4.infrastructure/git-commit-repository.ts +0 -85
  45. package/src/4.infrastructure/json-progress-tracker.ts +0 -182
  46. package/src/4.infrastructure/llm-adapter-factory.ts +0 -26
  47. package/src/4.infrastructure/llm-adapter.ts +0 -485
  48. package/src/4.infrastructure/llm-analysis-repository.ts +0 -38
  49. package/src/4.infrastructure/openai-llm-adapter.ts +0 -57
  50. package/src/di.ts +0 -109
  51. package/src/main.ts +0 -63
  52. package/src/utils/app-paths.ts +0 -36
  53. package/src/utils/concurrency.ts +0 -81
  54. package/src/utils.ts +0 -77
  55. package/tsconfig.json +0 -25
@@ -1,485 +0,0 @@
1
- import { CategoryType } from "@domain/category"
2
-
3
- import { ILLMService } from "@app/llm-service"
4
-
5
- import { sleep } from "../utils"
6
-
7
- export abstract class LLMAdapter implements ILLMService {
8
- protected static readonly DEFAULT_TIMEOUT = 60000
9
- protected static readonly MAX_RETRIES = parseInt(
10
- process.env.LLM_MAX_RETRIES || "3",
11
- 10,
12
- )
13
- protected static readonly INITIAL_RETRY_DELAY = parseInt(
14
- process.env.LLM_INITIAL_RETRY_DELAY || "5000",
15
- 10,
16
- )
17
- protected static readonly MAX_RETRY_DELAY = parseInt(
18
- process.env.LLM_MAX_RETRY_DELAY || "30000",
19
- 10,
20
- )
21
- protected static readonly RETRY_MULTIPLIER = parseFloat(
22
- process.env.LLM_RETRY_MULTIPLIER || "2",
23
- )
24
-
25
- protected model: string = ""
26
- protected verbose: boolean = false
27
- protected retryEnabled: boolean = true
28
-
29
- protected abstract getMaxPromptLength(): number
30
-
31
- abstract detectAvailableModels(): Promise<string[]>
32
- abstract isAvailable(): Promise<boolean>
33
- protected abstract executeModelCommand(prompt: string): Promise<string>
34
-
35
- setLLM(llm: string): void {
36
- this.model = llm
37
- }
38
-
39
- // Deprecated: Use setLLM instead
40
- setModel(model: string): void {
41
- this.setLLM(model)
42
- }
43
-
44
- setVerbose(verbose: boolean): void {
45
- this.verbose = verbose
46
- }
47
-
48
- setRetryEnabled(enabled: boolean): void {
49
- this.retryEnabled = enabled
50
- }
51
-
52
- getMaxRetries(): number {
53
- return LLMAdapter.MAX_RETRIES
54
- }
55
-
56
- async analyzeCommit(
57
- message: string,
58
- diff: string,
59
- ): Promise<{
60
- category: CategoryType
61
- summary: string
62
- description: string
63
- }> {
64
- const prompt = this.buildPrompt(message, diff)
65
- let lastError: Error | null = null
66
-
67
- if (!this.retryEnabled) {
68
- try {
69
- const output = await this.executeModelCommand(prompt)
70
- return this.parseResponse(output)
71
- } catch (error) {
72
- throw new Error(
73
- `Failed to analyze commit: ${error instanceof Error ? error.message : "Unknown error"}`,
74
- )
75
- }
76
- }
77
-
78
- for (let attempt = 1; attempt <= LLMAdapter.MAX_RETRIES; attempt++) {
79
- try {
80
- const output = await this.executeModelCommand(prompt)
81
- return this.parseResponse(output)
82
- } catch (error) {
83
- lastError = error instanceof Error ? error : new Error("Unknown error")
84
-
85
- if (attempt < LLMAdapter.MAX_RETRIES) {
86
- const delay = Math.min(
87
- LLMAdapter.INITIAL_RETRY_DELAY *
88
- Math.pow(LLMAdapter.RETRY_MULTIPLIER, attempt - 1),
89
- LLMAdapter.MAX_RETRY_DELAY,
90
- )
91
-
92
- if (this.verbose) {
93
- console.log(
94
- ` - Attempt ${attempt}/${LLMAdapter.MAX_RETRIES} failed. Retrying in ${delay / 1000}s...`,
95
- )
96
- }
97
-
98
- await sleep(delay)
99
- }
100
- }
101
- }
102
-
103
- throw new Error(
104
- `Failed to analyze commit after ${LLMAdapter.MAX_RETRIES} attempts: ${lastError?.message || "Unknown error"}`,
105
- )
106
- }
107
-
108
- protected buildPrompt(commitMessage: string, diff: string): string {
109
- return `Analyze this git commit and provide a categorization.
110
-
111
- COMMIT MESSAGE:
112
- ${commitMessage}
113
-
114
- COMMIT DIFF:
115
- ${diff}
116
-
117
- Based on the commit message and code changes, categorize this commit as one of:
118
- - "tweak": Minor adjustments, bug fixes, small improvements
119
- - "feature": New functionality, major additions
120
- - "process": Build system, CI/CD, tooling, configuration changes
121
-
122
- IMPORTANT: You must respond with ONLY a valid JSON object in this exact format:
123
-
124
- \`\`\`json
125
- {
126
- "category": "tweak|feature|process",
127
- "summary": "One-line description (max 80 characters)",
128
- "description": "Detailed explanation in 2-3 sentences"
129
- }
130
- \`\`\`
131
-
132
- Do not include any other text outside the JSON code block.`
133
- }
134
-
135
- protected parseResponse(response: string): {
136
- category: CategoryType
137
- summary: string
138
- description: string
139
- } {
140
- try {
141
- const jsonMatch = response.match(/```json\s*([\s\S]*?)\s*```/)
142
- if (!jsonMatch) {
143
- throw new Error("No JSON block found in response")
144
- }
145
-
146
- const jsonString = jsonMatch[1].trim()
147
- const parsed = JSON.parse(jsonString)
148
-
149
- const { category, summary, description } = parsed
150
-
151
- if (!this.isValidCategory(category)) {
152
- throw new Error(`Invalid category: ${category}`)
153
- }
154
-
155
- if (!summary || !description) {
156
- throw new Error("Missing required fields in response")
157
- }
158
-
159
- return {
160
- category: category as CategoryType,
161
- summary: summary.substring(0, 80), // Ensure max length
162
- description: description,
163
- }
164
- } catch (error) {
165
- if (this.verbose) {
166
- console.log(
167
- ` - Raw LLM response (first 2000 chars): ${response.substring(0, 2000)}`,
168
- )
169
- if (response.length > 2000) {
170
- console.log(
171
- ` - Response truncated (total length: ${response.length} chars)`,
172
- )
173
- }
174
- }
175
-
176
- throw new Error(
177
- `Failed to parse LLM response: ${error instanceof Error ? error.message : "Unknown error"}`,
178
- )
179
- }
180
- }
181
-
182
- protected isValidCategory(category: string): category is CategoryType {
183
- return ["tweak", "feature", "process"].includes(category)
184
- }
185
-
186
- async generateYearlySummariesFromCSV(csvContent: string): Promise<string> {
187
- return this.generateTimePeriodSummariesFromCSV(csvContent, 'yearly')
188
- }
189
-
190
- async generateTimePeriodSummariesFromCSV(csvContent: string, period: 'daily' | 'weekly' | 'monthly' | 'quarterly' | 'yearly'): Promise<string> {
191
- const prompt = this.buildTimePeriodReportPrompt(csvContent, period)
192
- let lastError: Error | null = null
193
-
194
- if (!this.retryEnabled) {
195
- try {
196
- const output = await this.executeModelCommand(prompt)
197
- return this.parseReportResponse(output)
198
- } catch (error) {
199
- throw new Error(
200
- `Failed to generate report: ${error instanceof Error ? error.message : "Unknown error"}`,
201
- )
202
- }
203
- }
204
-
205
- for (let attempt = 1; attempt <= LLMAdapter.MAX_RETRIES; attempt++) {
206
- try {
207
- const output = await this.executeModelCommand(prompt)
208
- return this.parseReportResponse(output)
209
- } catch (error) {
210
- lastError = error instanceof Error ? error : new Error("Unknown error")
211
-
212
- if (this.verbose) {
213
- console.log(` - Error generating report:`)
214
- console.log(` Attempt: ${attempt}/${LLMAdapter.MAX_RETRIES}`)
215
- console.log(` Error: ${lastError.message}`)
216
- }
217
-
218
- if (attempt < LLMAdapter.MAX_RETRIES) {
219
- const delay = Math.min(
220
- LLMAdapter.INITIAL_RETRY_DELAY *
221
- Math.pow(LLMAdapter.RETRY_MULTIPLIER, attempt - 1),
222
- LLMAdapter.MAX_RETRY_DELAY,
223
- )
224
- if (this.verbose) {
225
- console.log(` Retrying in ${delay / 1000}s...`)
226
- }
227
- await sleep(delay)
228
- }
229
- }
230
- }
231
-
232
- throw new Error(
233
- `Failed to generate report after ${LLMAdapter.MAX_RETRIES} attempts: ${lastError?.message || "Unknown error"}`,
234
- )
235
- }
236
-
237
- private buildReportPrompt(csvContent: string): string {
238
- return `Analyze the following CSV data containing git commit analysis results and generate a condensed markdown development summary report.
239
-
240
- CSV DATA:
241
- ${csvContent}
242
-
243
- INSTRUCTIONS:
244
- 1. Group the data by year (descending order, most recent first)
245
- 2. Within each year, group by category: Features, Process Improvements, and Tweaks & Bug Fixes
246
- 3. Use the 'commit_count' and 'date_range' columns to understand the scope and timeline of work
247
- 4. Consolidate similar items within each category to create readable summaries
248
- 5. Focus on what was accomplished rather than individual commit details
249
- 6. Use clear, professional language appropriate for stakeholders
250
- 7. Pay attention to recurring themes and patterns across commits
251
-
252
- CATEGORY MAPPING:
253
- - "feature" → "Features" section
254
- - "process" → "Processes" section
255
- - "tweak" → "Tweaks & Bug Fixes" section
256
-
257
- CONSOLIDATION GUIDELINES:
258
- - FIRST: Extract common themes and keywords from commit summaries within each category
259
- - SECOND: Identify and merge duplicate or highly similar work items (e.g., multiple "fix auth bug" commits become "resolved authentication issues")
260
- - Group similar features together by theme (e.g., "authentication system improvements", "payment processing enhancements")
261
- - Combine related bug fixes by area/system (e.g., "resolved 8 authentication issues", "fixed 5 database connection problems")
262
- - Summarize process changes by theme (e.g., "CI/CD pipeline enhancements", "testing infrastructure improvements")
263
- - Use bullet points for individual consolidated items within categories
264
- - Aim for 3-7 bullet points per category per year
265
- - Include specific numbers when relevant (e.g., "15 bug fixes", "3 new features")
266
- - Avoid listing near-identical items separately - consolidate them into meaningful groups
267
-
268
- OUTPUT FORMAT:
269
- Generate yearly summary sections with this exact structure (DO NOT include the main title or commit analysis section):
270
-
271
- \`\`\`markdown
272
- ## [YEAR]
273
- ### Features
274
- - [Consolidated feature summary 1]
275
- - [Consolidated feature summary 2]
276
- - [Additional features as needed]
277
-
278
- ### Processes
279
- - [Consolidated process improvement 1]
280
- - [Consolidated process improvement 2]
281
- - [Additional process items as needed]
282
-
283
- ### Tweaks & Bug Fixes
284
- - [Consolidated tweak/fix summary 1]
285
- - [Consolidated tweak/fix summary 2]
286
- - [Additional tweaks/fixes as needed]
287
-
288
- ## [PREVIOUS YEAR]
289
- [Repeat structure for each year in the data]
290
- \`\`\`
291
-
292
- QUALITY REQUIREMENTS:
293
- - Keep summaries concise but informative
294
- - Use active voice and clear language
295
- - Avoid technical jargon where possible
296
- - Ensure each bullet point represents meaningful work
297
- - Make the report valuable for both technical and non-technical readers
298
- - Focus on business impact and user value rather than technical implementation details
299
- - When consolidating, preserve the most important aspects from similar commits
300
- - Use progressive disclosure: start with high-level themes, then add specific details
301
-
302
- CONTEXT ANALYSIS:
303
- Before consolidating, analyze the commit data for:
304
- 1. Common file patterns or system areas being modified
305
- 2. Recurring keywords in commit messages that indicate related work
306
- 3. Sequential commits that build upon each other
307
- 4. Bug fixes that address the same underlying issue
308
-
309
- Generate the markdown report now:`
310
- }
311
-
312
- private buildTimePeriodReportPrompt(csvContent: string, period: 'daily' | 'weekly' | 'monthly' | 'quarterly' | 'yearly'): string {
313
- const periodDisplayName = this.getPeriodDisplayName(period)
314
- const sectionHeader = this.getSectionHeader(period)
315
-
316
- return `Analyze the following CSV data containing git commit analysis results and generate a condensed markdown development summary report.
317
-
318
- CSV DATA:
319
- ${csvContent}
320
-
321
- INSTRUCTIONS:
322
- 1. Group the data by ${periodDisplayName} (descending order, most recent first)
323
- 2. Within each ${periodDisplayName.toLowerCase()}, group by category: Features, Process Improvements, and Tweaks & Bug Fixes
324
- 3. Use the 'commit_count' and 'similar_commits' columns to understand related work and consolidation opportunities
325
- 4. Consolidate similar items within each category to create readable summaries
326
- 5. Focus on what was accomplished rather than individual commit details
327
- 6. Use clear, professional language appropriate for stakeholders
328
- 7. Only include sections for time periods that have commits
329
- 8. Pay attention to recurring themes and patterns across commits
330
-
331
- CATEGORY MAPPING:
332
- - "feature" → "Features" section
333
- - "process" → "Processes" section
334
- - "tweak" → "Tweaks & Bug Fixes" section
335
-
336
- CONSOLIDATION GUIDELINES:
337
- - FIRST: Extract common themes and keywords from commit summaries within each category
338
- - SECOND: Identify and merge duplicate or highly similar work items (e.g., multiple "fix auth bug" commits become "resolved authentication issues")
339
- - Group similar features together by theme (e.g., "authentication system improvements", "payment processing enhancements")
340
- - Combine related bug fixes by area/system (e.g., "resolved 8 authentication issues", "fixed 5 database connection problems")
341
- - Summarize process changes by theme (e.g., "CI/CD pipeline enhancements", "testing infrastructure improvements")
342
- - Use bullet points for individual consolidated items within categories
343
- - Aim for 3-7 bullet points per category per ${periodDisplayName.toLowerCase()}
344
- - Include specific numbers when relevant (e.g., "15 bug fixes", "3 new features")
345
- - Avoid listing near-identical items separately - consolidate them into meaningful groups
346
-
347
- OUTPUT FORMAT:
348
- Generate ${periodDisplayName.toLowerCase()} summary sections with this exact structure (DO NOT include the main title or commit analysis section):
349
-
350
- \`\`\`markdown
351
- ${sectionHeader}
352
- ### Features
353
- - [Consolidated feature summary 1]
354
- - [Consolidated feature summary 2]
355
- - [Additional features as needed]
356
-
357
- ### Processes
358
- - [Consolidated process improvement 1]
359
- - [Consolidated process improvement 2]
360
- - [Additional process items as needed]
361
-
362
- ### Tweaks & Bug Fixes
363
- - [Consolidated tweak/fix summary 1]
364
- - [Consolidated tweak/fix summary 2]
365
- - [Additional tweaks/fixes as needed]
366
-
367
- ${this.getPreviousPeriodExample(period)}
368
- [Repeat structure for each ${periodDisplayName.toLowerCase()} in the data]
369
- \`\`\`
370
-
371
- QUALITY REQUIREMENTS:
372
- - Keep summaries concise but informative
373
- - Use active voice and clear language
374
- - Avoid technical jargon where possible
375
- - Ensure each bullet point represents meaningful work
376
- - Make the report valuable for both technical and non-technical readers
377
- - Focus on business impact and user value rather than technical implementation details
378
- - When consolidating, preserve the most important aspects from similar commits
379
- - Use progressive disclosure: start with high-level themes, then add specific details
380
-
381
- CONTEXT ANALYSIS:
382
- Before consolidating, analyze the commit data for:
383
- 1. Common file patterns or system areas being modified
384
- 2. Recurring keywords in commit messages that indicate related work
385
- 3. Sequential commits that build upon each other
386
- 4. Bug fixes that address the same underlying issue
387
-
388
- Generate the markdown report now:`
389
- }
390
-
391
- private getPeriodDisplayName(period: 'daily' | 'weekly' | 'monthly' | 'quarterly' | 'yearly'): string {
392
- switch (period) {
393
- case 'daily': return 'Daily Period'
394
- case 'weekly': return 'Week'
395
- case 'monthly': return 'Month'
396
- case 'quarterly': return 'Quarter'
397
- case 'yearly': return 'Year'
398
- }
399
- }
400
-
401
- private getSectionHeader(period: 'daily' | 'weekly' | 'monthly' | 'quarterly' | 'yearly'): string {
402
- switch (period) {
403
- case 'daily': return '## [DATE] [TIME_OF_DAY]'
404
- case 'weekly': return '## [WEEK_RANGE]'
405
- case 'monthly': return '## [MONTH] [YEAR]'
406
- case 'quarterly': return '## [QUARTER] [YEAR]'
407
- case 'yearly': return '## [YEAR]'
408
- }
409
- }
410
-
411
- private getPreviousPeriodExample(period: 'daily' | 'weekly' | 'monthly' | 'quarterly' | 'yearly'): string {
412
- switch (period) {
413
- case 'daily': return '## [PREVIOUS_DATE] [TIME_OF_DAY]'
414
- case 'weekly': return '## [PREVIOUS_WEEK_RANGE]'
415
- case 'monthly': return '## [PREVIOUS_MONTH] [YEAR]'
416
- case 'quarterly': return '## [PREVIOUS_QUARTER] [YEAR]'
417
- case 'yearly': return '## [PREVIOUS_YEAR]'
418
- }
419
- }
420
-
421
- private parseReportResponse(response: string): string {
422
- // Look for markdown block first
423
- const markdownMatch = response.match(/```markdown\s*([\s\S]*?)\s*```/)
424
- if (markdownMatch) {
425
- return markdownMatch[1].trim()
426
- }
427
-
428
- // If no markdown block, look for content starting with "##" (yearly sections)
429
- const yearSectionMatch = response.match(/^(##\s+\d{4}[\s\S]*)/m)
430
- if (yearSectionMatch) {
431
- return yearSectionMatch[1].trim()
432
- }
433
-
434
- // If no clear structure found, return the entire response but log a warning
435
- if (this.verbose) {
436
- console.log(
437
- " - Warning: Could not find structured yearly sections in LLM response",
438
- )
439
- console.log(` - Response preview: ${response.substring(0, 200)}...`)
440
- }
441
-
442
- return response.trim()
443
- }
444
-
445
- protected truncatePrompt(prompt: string): string {
446
- const maxLength = this.getMaxPromptLength()
447
- if (prompt.length <= maxLength) {
448
- return prompt
449
- }
450
-
451
- // Find the diff section and truncate it
452
- const diffStartIndex = prompt.indexOf("COMMIT DIFF:")
453
- if (diffStartIndex === -1) {
454
- return prompt
455
- }
456
-
457
- const beforeDiff = prompt.substring(0, diffStartIndex)
458
- const afterDiffHeader = prompt.substring(diffStartIndex)
459
- const diffHeaderEnd = afterDiffHeader.indexOf("\n") + 1
460
- const diffHeader = afterDiffHeader.substring(0, diffHeaderEnd)
461
- const diffContent = afterDiffHeader.substring(diffHeaderEnd)
462
-
463
- // Calculate how much space we have for the diff
464
- const overhead = beforeDiff.length + diffHeader.length + 500 // Leave some buffer
465
- const maxDiffLength = Math.max(1000, maxLength - overhead)
466
-
467
- if (diffContent.length > maxDiffLength) {
468
- const truncatedDiff = diffContent.substring(0, maxDiffLength)
469
- const truncationNotice =
470
- "\n\n[DIFF TRUNCATED - Original length: " +
471
- diffContent.length +
472
- " characters]"
473
-
474
- return (
475
- beforeDiff +
476
- diffHeader +
477
- truncatedDiff +
478
- truncationNotice +
479
- "\n\nBased on the commit message and code changes, categorize this commit..."
480
- )
481
- }
482
-
483
- return prompt
484
- }
485
- }
@@ -1,38 +0,0 @@
1
- import { Analysis } from "@domain/analysis"
2
- import { Category } from "@domain/category"
3
- import { Commit } from "@domain/commit"
4
-
5
- import { ILLMService } from "@app/llm-service"
6
-
7
- import { IAnalysisRepository } from "@presentation/analysis-repository.interface"
8
-
9
- export class LLMAnalysisRepository implements IAnalysisRepository {
10
- constructor(private readonly llmService: ILLMService) {}
11
-
12
- async analyze(commit: Commit): Promise<Analysis> {
13
- const result = await this.llmService.analyzeCommit(
14
- commit.getMessage(),
15
- commit.getDiff(),
16
- )
17
-
18
- const category = Category.fromType(result.category)
19
-
20
- return new Analysis({
21
- category,
22
- summary: result.summary,
23
- description: result.description,
24
- })
25
- }
26
-
27
- async isAvailable(): Promise<boolean> {
28
- return this.llmService.isAvailable()
29
- }
30
-
31
- getMaxRetries(): number {
32
- return this.llmService.getMaxRetries()
33
- }
34
-
35
- setVerbose(verbose: boolean): void {
36
- this.llmService.setVerbose(verbose)
37
- }
38
- }
@@ -1,57 +0,0 @@
1
- import { execSync } from "child_process"
2
-
3
- import { LLMAdapter } from "./llm-adapter"
4
-
5
- export class OpenAILLMAdapter extends LLMAdapter {
6
- private static readonly MAX_PROMPT_LENGTH = parseInt(
7
- process.env.OPENAI_MAX_PROMPT_LENGTH || "100000",
8
- 10,
9
- )
10
-
11
- protected getMaxPromptLength(): number {
12
- return OpenAILLMAdapter.MAX_PROMPT_LENGTH
13
- }
14
-
15
- async detectAvailableModels(): Promise<string[]> {
16
- try {
17
- execSync("command -v codex", { stdio: "ignore" })
18
- return ["codex -q", "openai"]
19
- } catch {
20
- return []
21
- }
22
- }
23
-
24
- async isAvailable(): Promise<boolean> {
25
- const available = await this.detectAvailableModels()
26
- return available.length > 0
27
- }
28
-
29
- protected async executeModelCommand(prompt: string): Promise<string> {
30
- const truncatedPrompt = this.truncatePrompt(prompt)
31
-
32
- if (this.verbose) {
33
- console.log(` - Prompt length: ${truncatedPrompt.length} characters`)
34
- }
35
-
36
- const modelCommand = this.model || "codex -q"
37
-
38
- // Handle codex -q quirk: requires prompt as command line argument
39
- if (modelCommand.includes("-q")) {
40
- const escapedPrompt = truncatedPrompt.replace(/"/g, '\\"')
41
- return execSync(`${modelCommand} "${escapedPrompt}"`, {
42
- encoding: "utf8",
43
- stdio: ["pipe", "pipe", "pipe"],
44
- timeout: LLMAdapter.DEFAULT_TIMEOUT,
45
- })
46
- }
47
-
48
- // Fallback for non-quiet mode commands
49
- return execSync(modelCommand, {
50
- input: truncatedPrompt,
51
- encoding: "utf8",
52
- stdio: ["pipe", "pipe", "pipe"],
53
- timeout: LLMAdapter.DEFAULT_TIMEOUT,
54
- })
55
- }
56
- }
57
-
package/src/di.ts DELETED
@@ -1,109 +0,0 @@
1
- import { CommitAnalysisService } from "@domain/commit-analysis-service"
2
- import { DateFormattingService } from "@domain/date-formatting-service"
3
- import { ReportGenerationService } from "@domain/report-generation-service"
4
-
5
- import { AnalyzeCommitsUseCase } from "@app/analyze-commits.usecase"
6
- import { GenerateReportUseCase } from "@app/generate-report.usecase"
7
- import { ResumeAnalysisUseCase } from "@app/resume-analysis.usecase"
8
-
9
- import { AnalyzeCommand } from "@presentation/analyze-command"
10
- import { CLIApplication } from "@presentation/cli-application"
11
- import { CommitAnalysisController } from "@presentation/commit-analysis-controller"
12
- import { ReportCommand } from "@presentation/report-command"
13
- import { ResumeCommand } from "@presentation/resume-command"
14
-
15
- import { CacheService } from "@infra/cache-service"
16
- import { CachedAnalysisRepository } from "@infra/cached-analysis-repository"
17
- import { FileStorageRepository } from "@infra/file-storage-repository"
18
- import { FileSystemStorageAdapter } from "@infra/file-system-storage-adapter"
19
- import { GitAdapter } from "@infra/git-adapter"
20
- import { GitCommitRepository } from "@infra/git-commit-repository"
21
- import { JSONProgressTracker } from "@infra/json-progress-tracker"
22
- import { LLMAdapterFactory } from "@infra/llm-adapter-factory"
23
- import { LLMAnalysisRepository } from "@infra/llm-analysis-repository"
24
-
25
- export interface DIContainerOptions {
26
- llm?: string
27
- noCache?: boolean
28
- }
29
-
30
- export class DIContainer {
31
- private readonly gitAdapter = new GitAdapter()
32
- private readonly llmAdapter = LLMAdapterFactory.create(this.options?.llm)
33
- private readonly storageAdapter = new FileSystemStorageAdapter()
34
- private readonly cacheService = (() => {
35
- const service = new CacheService()
36
- if (this.options?.noCache) {
37
- service.setCacheEnabled(false)
38
- }
39
- return service
40
- })()
41
- private readonly progressTracker = new JSONProgressTracker(
42
- this.storageAdapter,
43
- )
44
-
45
- private readonly commitRepository = new GitCommitRepository(this.gitAdapter)
46
- private readonly llmAnalysisRepository = new LLMAnalysisRepository(
47
- this.llmAdapter,
48
- )
49
- private readonly analysisRepository = this.options?.noCache
50
- ? this.llmAnalysisRepository
51
- : new CachedAnalysisRepository(
52
- this.llmAnalysisRepository,
53
- this.cacheService,
54
- )
55
- private readonly storageRepository = new FileStorageRepository(
56
- this.storageAdapter,
57
- )
58
-
59
- private readonly commitAnalysisService = new CommitAnalysisService(
60
- this.commitRepository,
61
- this.analysisRepository,
62
- )
63
- private readonly reportGenerationService = new ReportGenerationService()
64
- private readonly dateFormattingService = new DateFormattingService()
65
-
66
- private readonly analyzeCommitsUseCase = new AnalyzeCommitsUseCase(
67
- this.commitAnalysisService,
68
- this.progressTracker,
69
- this.storageRepository,
70
- )
71
-
72
- private readonly generateReportUseCase = new GenerateReportUseCase(
73
- this.reportGenerationService,
74
- this.storageRepository,
75
- this.llmAdapter,
76
- this.dateFormattingService,
77
- this.gitAdapter,
78
- )
79
-
80
- private readonly resumeAnalysisUseCase = new ResumeAnalysisUseCase(
81
- this.progressTracker,
82
- this.analyzeCommitsUseCase,
83
- )
84
-
85
- private readonly analyzeCommand = new AnalyzeCommand(
86
- this.analyzeCommitsUseCase,
87
- this.commitAnalysisService,
88
- this.commitRepository,
89
- )
90
- private readonly reportCommand = new ReportCommand(this.generateReportUseCase)
91
- private readonly resumeCommand = new ResumeCommand(this.resumeAnalysisUseCase)
92
-
93
- private readonly controller = new CommitAnalysisController(
94
- this.analyzeCommand,
95
- this.reportCommand,
96
- this.resumeCommand,
97
- this.progressTracker,
98
- this.cacheService,
99
- this.commitRepository,
100
- )
101
-
102
- private readonly application = new CLIApplication(this.controller)
103
-
104
- constructor(private readonly options?: DIContainerOptions) {}
105
-
106
- getApplication(): CLIApplication {
107
- return this.application
108
- }
109
- }