commit-analyzer 1.1.5 → 1.1.7
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/main.ts +0 -0
- package/package.json +2 -1
- package/.claude/settings.local.json +0 -23
- package/commits.csv +0 -2
- package/csv-to-report-prompt.md +0 -97
- package/eslint.config.mts +0 -45
- package/prompt.md +0 -69
- package/src/1.domain/analysis.ts +0 -93
- package/src/1.domain/analyzed-commit.ts +0 -97
- package/src/1.domain/application-error.ts +0 -32
- package/src/1.domain/category.ts +0 -52
- package/src/1.domain/commit-analysis-service.ts +0 -92
- package/src/1.domain/commit-hash.ts +0 -40
- package/src/1.domain/commit.ts +0 -99
- package/src/1.domain/date-formatting-service.ts +0 -81
- package/src/1.domain/date-range.ts +0 -76
- package/src/1.domain/report-generation-service.ts +0 -443
- package/src/2.application/analyze-commits.usecase.ts +0 -307
- package/src/2.application/generate-report.usecase.ts +0 -209
- package/src/2.application/llm-service.ts +0 -54
- package/src/2.application/resume-analysis.usecase.ts +0 -123
- package/src/3.presentation/analysis-repository.interface.ts +0 -27
- package/src/3.presentation/analyze-command.ts +0 -128
- package/src/3.presentation/cli-application.ts +0 -278
- package/src/3.presentation/command-handler.interface.ts +0 -4
- package/src/3.presentation/commit-analysis-controller.ts +0 -101
- package/src/3.presentation/commit-repository.interface.ts +0 -47
- package/src/3.presentation/console-formatter.ts +0 -129
- package/src/3.presentation/progress-repository.interface.ts +0 -49
- package/src/3.presentation/report-command.ts +0 -50
- package/src/3.presentation/resume-command.ts +0 -59
- package/src/3.presentation/storage-repository.interface.ts +0 -33
- package/src/3.presentation/storage-service.interface.ts +0 -32
- package/src/3.presentation/version-control-service.interface.ts +0 -46
- package/src/4.infrastructure/cache-service.ts +0 -271
- package/src/4.infrastructure/cached-analysis-repository.ts +0 -46
- package/src/4.infrastructure/claude-llm-adapter.ts +0 -124
- package/src/4.infrastructure/csv-service.ts +0 -252
- package/src/4.infrastructure/file-storage-repository.ts +0 -108
- package/src/4.infrastructure/file-system-storage-adapter.ts +0 -87
- package/src/4.infrastructure/gemini-llm-adapter.ts +0 -46
- package/src/4.infrastructure/git-adapter.ts +0 -143
- package/src/4.infrastructure/git-commit-repository.ts +0 -85
- package/src/4.infrastructure/json-progress-tracker.ts +0 -182
- package/src/4.infrastructure/llm-adapter-factory.ts +0 -26
- package/src/4.infrastructure/llm-adapter.ts +0 -485
- package/src/4.infrastructure/llm-analysis-repository.ts +0 -38
- package/src/4.infrastructure/openai-llm-adapter.ts +0 -57
- package/src/di.ts +0 -109
- package/src/main.ts +0 -63
- package/src/utils/app-paths.ts +0 -36
- package/src/utils/concurrency.ts +0 -81
- package/src/utils.ts +0 -77
- package/tsconfig.json +0 -25
package/dist/main.ts
ADDED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "commit-analyzer",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.7",
|
|
4
|
+
"files": ["dist"],
|
|
4
5
|
"description": "Analyze git commits and generate categories, summaries, and descriptions for each commit. Optionally generate a yearly breakdown report of your commit history.",
|
|
5
6
|
"main": "dist/main.ts",
|
|
6
7
|
"bin": {
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"permissions": {
|
|
3
|
-
"allow": [
|
|
4
|
-
"Bash(mkdir:*)",
|
|
5
|
-
"Bash(bun install:*)",
|
|
6
|
-
"Bash(bun run:*)",
|
|
7
|
-
"Bash(bun link:*)",
|
|
8
|
-
"Bash(npm run typecheck:*)",
|
|
9
|
-
"Bash(npm run lint)",
|
|
10
|
-
"Bash(npm run build:*)",
|
|
11
|
-
"Bash(npm run dev:*)",
|
|
12
|
-
"Bash(bun dev:*)",
|
|
13
|
-
"Bash(git log:*)",
|
|
14
|
-
"Bash(git checkout:*)",
|
|
15
|
-
"Bash(npx ts-node:*)",
|
|
16
|
-
"Bash(node:*)",
|
|
17
|
-
"Bash(npm start:*)",
|
|
18
|
-
"Bash(bun:*)"
|
|
19
|
-
],
|
|
20
|
-
"deny": [],
|
|
21
|
-
"ask": []
|
|
22
|
-
}
|
|
23
|
-
}
|
package/commits.csv
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
year,category,summary,description
|
|
2
|
-
2025,process,"Refactor project structure with numbered layers, path aliases, and automated","Major architectural reorganization moving from nested directories to numbered layer structure (1.domain, 2.application, 3.presentation, 4.infrastructure). Implements TypeScript path aliases (@domain, @app, @presentation, @infra) for cleaner imports and adds ESLint plugin for automatic import sorting and organization. All files renamed to kebab-case and imports updated throughout the codebase to use the new alias system. This improves code maintainability, reduces import path complexity, and establishes consistent file organization patterns."
|
package/csv-to-report-prompt.md
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
# LLM Prompt Template: CSV to Markdown Report Generation
|
|
2
|
-
|
|
3
|
-
This is the prompt template to be used by the LLM service to generate condensed
|
|
4
|
-
markdown reports from CSV commit analysis data.
|
|
5
|
-
|
|
6
|
-
## Prompt Template
|
|
7
|
-
|
|
8
|
-
```
|
|
9
|
-
Analyze the following CSV data containing git commit analysis results and generate a condensed markdown development summary report.
|
|
10
|
-
|
|
11
|
-
CSV DATA:
|
|
12
|
-
{csv_content}
|
|
13
|
-
|
|
14
|
-
INSTRUCTIONS:
|
|
15
|
-
1. Group the data by year (descending order, most recent first)
|
|
16
|
-
2. Within each year, group by category: Features, Process Improvements, and Tweaks & Bug Fixes
|
|
17
|
-
3. Consolidate similar items within each category to create readable summaries
|
|
18
|
-
4. Focus on what was accomplished rather than individual commit details
|
|
19
|
-
5. Use clear, professional language appropriate for stakeholders
|
|
20
|
-
|
|
21
|
-
CATEGORY MAPPING:
|
|
22
|
-
- "feature" → "Features" section
|
|
23
|
-
- "process" → "Processes" section
|
|
24
|
-
- "tweak" → "Tweaks & Bug Fixes" section
|
|
25
|
-
|
|
26
|
-
CONSOLIDATION GUIDELINES:
|
|
27
|
-
- Group similar features together (e.g., "authentication system improvements")
|
|
28
|
-
- Combine related bug fixes (e.g., "resolved 8 authentication issues")
|
|
29
|
-
- Summarize process changes by theme (e.g., "CI/CD pipeline enhancements")
|
|
30
|
-
- Use bullet points for individual items within categories
|
|
31
|
-
- Aim for 3-7 bullet points per category per year
|
|
32
|
-
- Include specific numbers when relevant (e.g., "15 bug fixes", "3 new features")
|
|
33
|
-
|
|
34
|
-
OUTPUT FORMAT:
|
|
35
|
-
Generate a markdown report with this exact structure:
|
|
36
|
-
|
|
37
|
-
```markdown
|
|
38
|
-
# Development Summary Report
|
|
39
|
-
|
|
40
|
-
## Commit Analysis
|
|
41
|
-
- **Total Commits**: [X] commits across [YEAR_RANGE]
|
|
42
|
-
- **[MOST_RECENT_YEAR]**: [X] commits ([X] features, [X] process, [X] tweaks)
|
|
43
|
-
- **[PREVIOUS_YEAR]**: [X] commits ([X] features, [X] process, [X] tweaks)
|
|
44
|
-
- [Continue for each year in the data]
|
|
45
|
-
|
|
46
|
-
## [YEAR]
|
|
47
|
-
### Features
|
|
48
|
-
- [Consolidated feature summary 1]
|
|
49
|
-
- [Consolidated feature summary 2]
|
|
50
|
-
- [Additional features as needed]
|
|
51
|
-
|
|
52
|
-
### Processes
|
|
53
|
-
- [Consolidated process improvement 1]
|
|
54
|
-
- [Consolidated process improvement 2]
|
|
55
|
-
- [Additional process items as needed]
|
|
56
|
-
|
|
57
|
-
### Tweaks & Bug Fixes
|
|
58
|
-
- [Consolidated tweak/fix summary 1]
|
|
59
|
-
- [Consolidated tweak/fix summary 2]
|
|
60
|
-
- [Additional tweaks/fixes as needed]
|
|
61
|
-
|
|
62
|
-
## [PREVIOUS YEAR]
|
|
63
|
-
[Repeat structure for each year in the data]
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
QUALITY REQUIREMENTS:
|
|
67
|
-
- Keep summaries concise but informative
|
|
68
|
-
- Use active voice and clear language
|
|
69
|
-
- Avoid technical jargon where possible
|
|
70
|
-
- Ensure each bullet point represents meaningful work
|
|
71
|
-
- Make the report valuable for both technical and non-technical readers
|
|
72
|
-
|
|
73
|
-
Generate the markdown report now:
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
## Implementation Notes
|
|
77
|
-
|
|
78
|
-
This prompt should be used in the `MarkdownReportGenerator` service with the following approach:
|
|
79
|
-
|
|
80
|
-
1. **Input Processing**: Replace `{csv_content}` with the actual CSV data read from the input file
|
|
81
|
-
2. **LLM Call**: Send this prompt to the configured LLM (Claude, Gemini, etc.)
|
|
82
|
-
3. **Response Parsing**: Extract the markdown content from the LLM response
|
|
83
|
-
4. **File Output**: Write the generated markdown to the specified output file
|
|
84
|
-
|
|
85
|
-
### Error Handling
|
|
86
|
-
- If LLM returns malformed response, retry up to MAX_RETRIES times
|
|
87
|
-
- Validate that the response contains properly formatted markdown
|
|
88
|
-
- Ensure all years from the CSV data are represented in the output
|
|
89
|
-
- Handle edge cases like empty categories or single-item categories
|
|
90
|
-
|
|
91
|
-
### Response Validation
|
|
92
|
-
The generated report should:
|
|
93
|
-
- Start with "# Development Summary Report"
|
|
94
|
-
- Have year sections in descending chronological order
|
|
95
|
-
- Include all three category sections for each year (even if empty)
|
|
96
|
-
- Use proper markdown formatting with ## for years and ### for categories
|
|
97
|
-
- Contain bullet points (-) for individual items
|
package/eslint.config.mts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import js from "@eslint/js"
|
|
2
|
-
import { defineConfig } from "eslint/config"
|
|
3
|
-
import simpleImportSort from "eslint-plugin-simple-import-sort"
|
|
4
|
-
import globals from "globals"
|
|
5
|
-
import tseslint from "typescript-eslint"
|
|
6
|
-
|
|
7
|
-
export default defineConfig([
|
|
8
|
-
{
|
|
9
|
-
files: ["**/*.{js,mjs,cjs,ts,mts,cts}"],
|
|
10
|
-
plugins: { js },
|
|
11
|
-
extends: ["js/recommended"],
|
|
12
|
-
languageOptions: { globals: globals.node },
|
|
13
|
-
},
|
|
14
|
-
tseslint.configs.recommended,
|
|
15
|
-
{
|
|
16
|
-
plugins: {
|
|
17
|
-
"simple-import-sort": simpleImportSort,
|
|
18
|
-
},
|
|
19
|
-
rules: {
|
|
20
|
-
"simple-import-sort/imports": [
|
|
21
|
-
"warn",
|
|
22
|
-
{
|
|
23
|
-
groups: [
|
|
24
|
-
// Side effect imports
|
|
25
|
-
["^\\u0000"],
|
|
26
|
-
// Packages. `react` related packages come first.
|
|
27
|
-
["^react", "^@?\\w"],
|
|
28
|
-
// Aliases (adjust these regexes for your aliases)
|
|
29
|
-
["^@domain"],
|
|
30
|
-
["^@app"],
|
|
31
|
-
["^@presentation"],
|
|
32
|
-
["^@infra"],
|
|
33
|
-
// Parent imports
|
|
34
|
-
["^\\.\\.(?!/?$)", "^\\.\\./?$"],
|
|
35
|
-
// Relative imports
|
|
36
|
-
["^\\./(?=.*/)(?!/?$)", "^\\.(?!/?$)", "^\\./?$"],
|
|
37
|
-
// Style imports
|
|
38
|
-
["^.+\\.s?css$"],
|
|
39
|
-
],
|
|
40
|
-
},
|
|
41
|
-
],
|
|
42
|
-
"simple-import-sort/exports": "warn",
|
|
43
|
-
},
|
|
44
|
-
},
|
|
45
|
-
])
|
package/prompt.md
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
# Prompt for Git Commit Analysis Program
|
|
2
|
-
|
|
3
|
-
Create a TypeScript/Node.js program that analyzes git commits and generates
|
|
4
|
-
categorized summaries.
|
|
5
|
-
|
|
6
|
-
The program should:
|
|
7
|
-
|
|
8
|
-
## Input Requirements
|
|
9
|
-
|
|
10
|
-
- Accept a list of git commit hashes as input (command line arguments or file)
|
|
11
|
-
- For each commit, extract the commit message, date, and diff
|
|
12
|
-
|
|
13
|
-
Core Functionality:
|
|
14
|
-
|
|
15
|
-
1. Git Integration:
|
|
16
|
-
Use git show and git diff to get commit details and changes
|
|
17
|
-
2. LLM Analysis:
|
|
18
|
-
Send commit message + diff to the claude cli for categorization.
|
|
19
|
-
3. CSV Export:
|
|
20
|
-
Generate output with columns:
|
|
21
|
-
year, category, summary, description
|
|
22
|
-
|
|
23
|
-
## LLM Prompt Template
|
|
24
|
-
|
|
25
|
-
Analyze this git commit and provide a categorization:
|
|
26
|
-
|
|
27
|
-
- COMMIT MESSAGE:
|
|
28
|
-
{commit_message}
|
|
29
|
-
- COMMIT DIFF:
|
|
30
|
-
{diff_content}
|
|
31
|
-
|
|
32
|
-
Based on the commit message and code changes, categorize this commit as one
|
|
33
|
-
of:
|
|
34
|
-
- "tweak":
|
|
35
|
-
Minor adjustments, bug fixes, small improvements
|
|
36
|
-
- "feature":
|
|
37
|
-
New functionality, major additions
|
|
38
|
-
- "process":
|
|
39
|
-
Build system, CI/CD, tooling, configuration changes
|
|
40
|
-
|
|
41
|
-
Provide:
|
|
42
|
-
1. Category:
|
|
43
|
-
[tweak|feature|process]
|
|
44
|
-
2. Summary:
|
|
45
|
-
One-line description (max 80 chars)
|
|
46
|
-
3. Description:
|
|
47
|
-
Detailed explanation (2-3 sentences)
|
|
48
|
-
|
|
49
|
-
Format as JSON:
|
|
50
|
-
```json
|
|
51
|
-
{
|
|
52
|
-
"category": "...",
|
|
53
|
-
"summary": "...",
|
|
54
|
-
"description": "..."
|
|
55
|
-
}
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
### Technical Implementation
|
|
59
|
-
|
|
60
|
-
- Use Node.js with TypeScript
|
|
61
|
-
- Extract timestamp from git commit
|
|
62
|
-
|
|
63
|
-
Output Format:
|
|
64
|
-
|
|
65
|
-
CSV with headers:
|
|
66
|
-
timestamp,category,summary,description
|
|
67
|
-
|
|
68
|
-
The program should be robust, handle edge cases, and provide clear error
|
|
69
|
-
messages for invalid commits or API failures.
|
package/src/1.domain/analysis.ts
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import { Category } from "./category"
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Domain entity representing the analysis of a commit
|
|
5
|
-
*/
|
|
6
|
-
export class Analysis {
|
|
7
|
-
private static readonly MAX_SUMMARY_LENGTH = 80
|
|
8
|
-
private static readonly MIN_DESCRIPTION_LENGTH = 10
|
|
9
|
-
private readonly category: Category
|
|
10
|
-
private readonly summary: string
|
|
11
|
-
private readonly description: string
|
|
12
|
-
|
|
13
|
-
constructor(params: {
|
|
14
|
-
category: Category
|
|
15
|
-
summary: string
|
|
16
|
-
description: string
|
|
17
|
-
}) {
|
|
18
|
-
const { category, summary, description } = params
|
|
19
|
-
this.category = category
|
|
20
|
-
this.summary = summary
|
|
21
|
-
this.description = description
|
|
22
|
-
|
|
23
|
-
if (!summary || summary.trim().length === 0) {
|
|
24
|
-
throw new Error("Summary cannot be empty")
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (!description || description.trim().length === 0) {
|
|
28
|
-
throw new Error("Description cannot be empty")
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (summary.length > Analysis.MAX_SUMMARY_LENGTH) {
|
|
32
|
-
throw new Error(
|
|
33
|
-
`Summary cannot exceed ${Analysis.MAX_SUMMARY_LENGTH} characters`,
|
|
34
|
-
)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (description.length < Analysis.MIN_DESCRIPTION_LENGTH) {
|
|
38
|
-
throw new Error(
|
|
39
|
-
`Description must be at least ${Analysis.MIN_DESCRIPTION_LENGTH} characters`,
|
|
40
|
-
)
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
getCategory(): Category {
|
|
45
|
-
return this.category
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
getSummary(): string {
|
|
49
|
-
return this.summary
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
getDescription(): string {
|
|
53
|
-
return this.description
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
getSummaryTruncated(): string {
|
|
57
|
-
return this.summary.length > Analysis.MAX_SUMMARY_LENGTH
|
|
58
|
-
? this.summary.substring(0, Analysis.MAX_SUMMARY_LENGTH - 3) + "..."
|
|
59
|
-
: this.summary
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
isFeatureAnalysis(): boolean {
|
|
63
|
-
return this.category.isFeature()
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
isTweakAnalysis(): boolean {
|
|
67
|
-
return this.category.isTweak()
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
isProcessAnalysis(): boolean {
|
|
71
|
-
return this.category.isProcess()
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
equals(other: Analysis): boolean {
|
|
75
|
-
return (
|
|
76
|
-
this.category.equals(other.category) &&
|
|
77
|
-
this.summary === other.summary &&
|
|
78
|
-
this.description === other.description
|
|
79
|
-
)
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
toPlainObject(): {
|
|
83
|
-
category: string
|
|
84
|
-
summary: string
|
|
85
|
-
description: string
|
|
86
|
-
} {
|
|
87
|
-
return {
|
|
88
|
-
category: this.category.getValue(),
|
|
89
|
-
summary: this.summary,
|
|
90
|
-
description: this.description,
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { Analysis } from "./analysis"
|
|
2
|
-
import { Commit } from "./commit"
|
|
3
|
-
import { CommitHash } from "./commit-hash"
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Domain entity representing a commit with its analysis
|
|
7
|
-
*/
|
|
8
|
-
export class AnalyzedCommit {
|
|
9
|
-
constructor(
|
|
10
|
-
private readonly commit: Commit,
|
|
11
|
-
private readonly analysis: Analysis,
|
|
12
|
-
) {
|
|
13
|
-
if (!commit) {
|
|
14
|
-
throw new Error("Commit is required")
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
if (!analysis) {
|
|
18
|
-
throw new Error("Analysis is required")
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
getCommit(): Commit {
|
|
23
|
-
return this.commit
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
getAnalysis(): Analysis {
|
|
27
|
-
return this.analysis
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
getHash(): CommitHash {
|
|
31
|
-
return this.commit.getHash()
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
getMessage(): string {
|
|
35
|
-
return this.commit.getMessage()
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
getDate(): Date {
|
|
39
|
-
return this.commit.getDate()
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
getYear(): number {
|
|
43
|
-
return this.commit.getYear()
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
getShortHash(length: number = 8): string {
|
|
47
|
-
return this.commit.getShortHash(length)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
isLargeChange(): boolean {
|
|
51
|
-
return this.commit.isLargeChange()
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
equals(other: AnalyzedCommit): boolean {
|
|
55
|
-
return (
|
|
56
|
-
this.commit.equals(other.commit) && this.analysis.equals(other.analysis)
|
|
57
|
-
)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
toCSVRow(): {
|
|
61
|
-
timestamp: string
|
|
62
|
-
category: string
|
|
63
|
-
summary: string
|
|
64
|
-
description: string
|
|
65
|
-
} {
|
|
66
|
-
return {
|
|
67
|
-
timestamp: this.getDate().toISOString(),
|
|
68
|
-
category: this.analysis.getCategory().getValue(),
|
|
69
|
-
summary: this.analysis.getSummary(),
|
|
70
|
-
description: this.analysis.getDescription(),
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
toReportData(): {
|
|
75
|
-
hash: string
|
|
76
|
-
shortHash: string
|
|
77
|
-
message: string
|
|
78
|
-
date: Date
|
|
79
|
-
year: number
|
|
80
|
-
category: string
|
|
81
|
-
summary: string
|
|
82
|
-
description: string
|
|
83
|
-
isLargeChange: boolean
|
|
84
|
-
} {
|
|
85
|
-
return {
|
|
86
|
-
hash: this.commit.getHash().getValue(),
|
|
87
|
-
shortHash: this.getShortHash(),
|
|
88
|
-
message: this.getMessage(),
|
|
89
|
-
date: this.getDate(),
|
|
90
|
-
year: this.getYear(),
|
|
91
|
-
category: this.analysis.getCategory().getValue(),
|
|
92
|
-
summary: this.analysis.getSummary(),
|
|
93
|
-
description: this.analysis.getDescription(),
|
|
94
|
-
isLargeChange: this.isLargeChange(),
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Base class for application errors
|
|
3
|
-
*/
|
|
4
|
-
export abstract class ApplicationError extends Error {
|
|
5
|
-
constructor(
|
|
6
|
-
message: string,
|
|
7
|
-
public readonly code: string,
|
|
8
|
-
) {
|
|
9
|
-
super(message)
|
|
10
|
-
this.name = "ApplicationError"
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Error for validation failures
|
|
16
|
-
*/
|
|
17
|
-
export class ValidationError extends ApplicationError {
|
|
18
|
-
constructor(message: string) {
|
|
19
|
-
super(message, "VALIDATION_ERROR")
|
|
20
|
-
this.name = "ValidationError"
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Error for not found resources
|
|
26
|
-
*/
|
|
27
|
-
export class NotFoundError extends ApplicationError {
|
|
28
|
-
constructor(resource: string, identifier: string) {
|
|
29
|
-
super(`${resource} not found: ${identifier}`, "NOT_FOUND")
|
|
30
|
-
this.name = "NotFoundError"
|
|
31
|
-
}
|
|
32
|
-
}
|
package/src/1.domain/category.ts
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Value object for commit analysis category
|
|
3
|
-
*/
|
|
4
|
-
export type CategoryType = "tweak" | "feature" | "process"
|
|
5
|
-
|
|
6
|
-
export class Category {
|
|
7
|
-
private static readonly VALID_CATEGORIES: CategoryType[] = ["tweak", "feature", "process"]
|
|
8
|
-
|
|
9
|
-
private constructor(private readonly value: CategoryType) {}
|
|
10
|
-
|
|
11
|
-
static create(category: string): Category {
|
|
12
|
-
if (!category || typeof category !== 'string') {
|
|
13
|
-
throw new Error('Category cannot be empty')
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const normalizedCategory = category.toLowerCase().trim() as CategoryType
|
|
17
|
-
|
|
18
|
-
if (!this.VALID_CATEGORIES.includes(normalizedCategory)) {
|
|
19
|
-
throw new Error(`Invalid category: ${category}. Must be one of: ${this.VALID_CATEGORIES.join(', ')}`)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return new Category(normalizedCategory)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
static fromType(category: CategoryType): Category {
|
|
26
|
-
return new Category(category)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
getValue(): CategoryType {
|
|
30
|
-
return this.value
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
equals(other: Category): boolean {
|
|
34
|
-
return this.value === other.value
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
toString(): string {
|
|
38
|
-
return this.value
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
isFeature(): boolean {
|
|
42
|
-
return this.value === "feature"
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
isTweak(): boolean {
|
|
46
|
-
return this.value === "tweak"
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
isProcess(): boolean {
|
|
50
|
-
return this.value === "process"
|
|
51
|
-
}
|
|
52
|
-
}
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { IAnalysisRepository } from "@presentation/analysis-repository.interface"
|
|
2
|
-
import { ICommitRepository } from "@presentation/commit-repository.interface"
|
|
3
|
-
|
|
4
|
-
import { AnalyzedCommit } from "./analyzed-commit"
|
|
5
|
-
import { Commit } from "./commit"
|
|
6
|
-
import { CommitHash } from "./commit-hash"
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Domain service for commit analysis operations
|
|
10
|
-
*/
|
|
11
|
-
export class CommitAnalysisService {
|
|
12
|
-
constructor(
|
|
13
|
-
private readonly commitRepository: ICommitRepository,
|
|
14
|
-
private readonly analysisRepository: IAnalysisRepository,
|
|
15
|
-
) {}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Analyzes a single commit
|
|
19
|
-
*/
|
|
20
|
-
async analyzeCommit(hash: CommitHash): Promise<AnalyzedCommit> {
|
|
21
|
-
const commit = await this.commitRepository.getByHash(hash)
|
|
22
|
-
const analysis = await this.analysisRepository.analyze(commit)
|
|
23
|
-
|
|
24
|
-
return new AnalyzedCommit(commit, analysis)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Analyzes multiple commits
|
|
29
|
-
*/
|
|
30
|
-
async analyzeCommits(hashes: CommitHash[]): Promise<AnalyzedCommit[]> {
|
|
31
|
-
const analyzedCommits: AnalyzedCommit[] = []
|
|
32
|
-
|
|
33
|
-
for (const hash of hashes) {
|
|
34
|
-
try {
|
|
35
|
-
const analyzedCommit = await this.analyzeCommit(hash)
|
|
36
|
-
analyzedCommits.push(analyzedCommit)
|
|
37
|
-
} catch (error) {
|
|
38
|
-
// Re-throw the error - let the application layer handle logging
|
|
39
|
-
throw new Error(
|
|
40
|
-
`Failed to analyze commit ${hash.getShortHash()}: ${error instanceof Error ? error.message : String(error)}`,
|
|
41
|
-
)
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return analyzedCommits
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Validates that all commit hashes exist
|
|
50
|
-
*/
|
|
51
|
-
async validateCommits(
|
|
52
|
-
hashes: CommitHash[],
|
|
53
|
-
): Promise<{ valid: CommitHash[]; invalid: CommitHash[] }> {
|
|
54
|
-
const valid: CommitHash[] = []
|
|
55
|
-
const invalid: CommitHash[] = []
|
|
56
|
-
|
|
57
|
-
for (const hash of hashes) {
|
|
58
|
-
const exists = await this.commitRepository.exists(hash)
|
|
59
|
-
if (exists) {
|
|
60
|
-
valid.push(hash)
|
|
61
|
-
} else {
|
|
62
|
-
invalid.push(hash)
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return { valid, invalid }
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Gets commits authored by the current user
|
|
71
|
-
*/
|
|
72
|
-
async getCurrentUserCommits(params?: {
|
|
73
|
-
limit?: number
|
|
74
|
-
since?: string
|
|
75
|
-
until?: string
|
|
76
|
-
}): Promise<Commit[]> {
|
|
77
|
-
const userEmail = await this.commitRepository.getCurrentUserEmail()
|
|
78
|
-
return this.commitRepository.getByAuthor({
|
|
79
|
-
authorEmail: userEmail,
|
|
80
|
-
limit: params?.limit,
|
|
81
|
-
since: params?.since,
|
|
82
|
-
until: params?.until,
|
|
83
|
-
})
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Checks if the analysis service is ready
|
|
88
|
-
*/
|
|
89
|
-
async isAnalysisServiceReady(): Promise<boolean> {
|
|
90
|
-
return this.analysisRepository.isAvailable()
|
|
91
|
-
}
|
|
92
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Value object for Git commit hash
|
|
3
|
-
*/
|
|
4
|
-
export class CommitHash {
|
|
5
|
-
private constructor(private readonly value: string) {}
|
|
6
|
-
|
|
7
|
-
static create(hash: string): CommitHash {
|
|
8
|
-
if (!hash || typeof hash !== 'string') {
|
|
9
|
-
throw new Error('Commit hash cannot be empty')
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
// Git short hash is minimum 4 characters, full hash is 40
|
|
13
|
-
if (hash.length < 4 || hash.length > 40) {
|
|
14
|
-
throw new Error('Invalid commit hash length')
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// Only hexadecimal characters allowed
|
|
18
|
-
if (!/^[a-f0-9]+$/i.test(hash)) {
|
|
19
|
-
throw new Error('Commit hash must contain only hexadecimal characters')
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return new CommitHash(hash)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
getValue(): string {
|
|
26
|
-
return this.value
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
getShortHash(length: number = 8): string {
|
|
30
|
-
return this.value.substring(0, Math.min(length, this.value.length))
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
equals(other: CommitHash): boolean {
|
|
34
|
-
return this.value === other.value
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
toString(): string {
|
|
38
|
-
return this.value
|
|
39
|
-
}
|
|
40
|
-
}
|