@untools/commitgen 0.2.3 → 0.2.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.
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/package.json +70 -0
- package/dist/src/commands/configure.d.ts +1 -0
- package/dist/src/commands/configure.js +96 -0
- package/dist/src/commands/configure.js.map +1 -0
- package/dist/src/config.d.ts +15 -0
- package/dist/src/config.js +56 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +649 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/providers/base.d.ts +9 -0
- package/dist/src/providers/base.js +107 -0
- package/dist/src/providers/base.js.map +1 -0
- package/dist/src/providers/index.d.ts +4 -0
- package/dist/src/providers/index.js +41 -0
- package/dist/src/providers/index.js.map +1 -0
- package/dist/src/providers/vercel-google.d.ts +10 -0
- package/dist/src/providers/vercel-google.js +113 -0
- package/dist/src/providers/vercel-google.js.map +1 -0
- package/dist/src/types.d.ts +46 -0
- package/dist/src/types.js +4 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/utils/commit-history.d.ts +35 -0
- package/dist/src/utils/commit-history.js +165 -0
- package/dist/src/utils/commit-history.js.map +1 -0
- package/dist/src/utils/issue-tracker.d.ts +43 -0
- package/dist/src/utils/issue-tracker.js +207 -0
- package/dist/src/utils/issue-tracker.js.map +1 -0
- package/dist/src/utils/loading.d.ts +14 -0
- package/dist/src/utils/loading.js +72 -0
- package/dist/src/utils/loading.js.map +1 -0
- package/dist/src/utils/multi-commit.d.ts +40 -0
- package/dist/src/utils/multi-commit.js +274 -0
- package/dist/src/utils/multi-commit.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { CommitMessage } from "../types";
|
|
2
|
+
export interface IssueReference {
|
|
3
|
+
platform: "jira" | "github" | "linear" | "gitlab" | "unknown";
|
|
4
|
+
issueKey: string;
|
|
5
|
+
issueType?: string;
|
|
6
|
+
branchName: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class IssueTrackerIntegration {
|
|
9
|
+
private cache;
|
|
10
|
+
private exec;
|
|
11
|
+
/**
|
|
12
|
+
* Extract issue reference from current branch
|
|
13
|
+
*/
|
|
14
|
+
extractIssueFromBranch(): IssueReference | null;
|
|
15
|
+
/**
|
|
16
|
+
* Parse issue reference from branch name
|
|
17
|
+
*/
|
|
18
|
+
private parseIssueFromBranch;
|
|
19
|
+
/**
|
|
20
|
+
* Infer issue type from branch name
|
|
21
|
+
*/
|
|
22
|
+
private inferIssueType;
|
|
23
|
+
/**
|
|
24
|
+
* Check if repository is GitLab
|
|
25
|
+
*/
|
|
26
|
+
private isGitLabRepo;
|
|
27
|
+
/**
|
|
28
|
+
* Append issue reference to commit message
|
|
29
|
+
*/
|
|
30
|
+
appendIssueToCommit(message: CommitMessage, issue: IssueReference): CommitMessage;
|
|
31
|
+
/**
|
|
32
|
+
* Suggest commit type based on issue type
|
|
33
|
+
*/
|
|
34
|
+
suggestTypeFromIssue(issue: IssueReference, defaultType: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* Get display string for issue
|
|
37
|
+
*/
|
|
38
|
+
getIssueDisplay(issue: IssueReference): string;
|
|
39
|
+
/**
|
|
40
|
+
* Clear cache (useful for testing or branch switches)
|
|
41
|
+
*/
|
|
42
|
+
clearCache(): void;
|
|
43
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ./src/utils/issue-tracker.ts
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.IssueTrackerIntegration = void 0;
|
|
5
|
+
const child_process_1 = require("child_process");
|
|
6
|
+
class IssueTrackerIntegration {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.cache = null;
|
|
9
|
+
}
|
|
10
|
+
exec(cmd) {
|
|
11
|
+
try {
|
|
12
|
+
return (0, child_process_1.execSync)(cmd, {
|
|
13
|
+
encoding: "utf8",
|
|
14
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
15
|
+
}).trim();
|
|
16
|
+
}
|
|
17
|
+
catch (_a) {
|
|
18
|
+
return "";
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Extract issue reference from current branch
|
|
23
|
+
*/
|
|
24
|
+
extractIssueFromBranch() {
|
|
25
|
+
// Check cache first
|
|
26
|
+
if (this.cache)
|
|
27
|
+
return this.cache;
|
|
28
|
+
const branchName = this.exec("git branch --show-current");
|
|
29
|
+
if (!branchName)
|
|
30
|
+
return null;
|
|
31
|
+
const issue = this.parseIssueFromBranch(branchName);
|
|
32
|
+
// Cache the result
|
|
33
|
+
this.cache = issue;
|
|
34
|
+
return issue;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Parse issue reference from branch name
|
|
38
|
+
*/
|
|
39
|
+
parseIssueFromBranch(branchName) {
|
|
40
|
+
// Jira pattern: PROJ-123 or feature/PROJ-123-description
|
|
41
|
+
const jiraMatch = branchName.match(/([A-Z]{2,10}-\d+)/);
|
|
42
|
+
if (jiraMatch) {
|
|
43
|
+
return {
|
|
44
|
+
platform: "jira",
|
|
45
|
+
issueKey: jiraMatch[1],
|
|
46
|
+
branchName,
|
|
47
|
+
issueType: this.inferIssueType(branchName),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
// GitHub issue pattern: #123 or issue-123 or 123-description
|
|
51
|
+
const githubMatch = branchName.match(/(?:issue[/-]|#)?(\d+)(?:[/-]|$)/i);
|
|
52
|
+
if (githubMatch) {
|
|
53
|
+
return {
|
|
54
|
+
platform: "github",
|
|
55
|
+
issueKey: `#${githubMatch[1]}`,
|
|
56
|
+
branchName,
|
|
57
|
+
issueType: this.inferIssueType(branchName),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
// Linear pattern: TEAM-123 or team/TEAM-123-description
|
|
61
|
+
const linearMatch = branchName.match(/([A-Z]{2,5}-\d+)/);
|
|
62
|
+
if (linearMatch) {
|
|
63
|
+
return {
|
|
64
|
+
platform: "linear",
|
|
65
|
+
issueKey: linearMatch[1],
|
|
66
|
+
branchName,
|
|
67
|
+
issueType: this.inferIssueType(branchName),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
// GitLab issue pattern: similar to GitHub
|
|
71
|
+
const gitlabMatch = branchName.match(/(?:issue[/-]|#)?(\d+)(?:[/-]|$)/i);
|
|
72
|
+
if (gitlabMatch && this.isGitLabRepo()) {
|
|
73
|
+
return {
|
|
74
|
+
platform: "gitlab",
|
|
75
|
+
issueKey: `#${gitlabMatch[1]}`,
|
|
76
|
+
branchName,
|
|
77
|
+
issueType: this.inferIssueType(branchName),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Infer issue type from branch name
|
|
84
|
+
*/
|
|
85
|
+
inferIssueType(branchName) {
|
|
86
|
+
const lower = branchName.toLowerCase();
|
|
87
|
+
if (lower.startsWith("feature/") || lower.includes("/feature/")) {
|
|
88
|
+
return "feature";
|
|
89
|
+
}
|
|
90
|
+
if (lower.startsWith("fix/") ||
|
|
91
|
+
lower.startsWith("bugfix/") ||
|
|
92
|
+
lower.includes("/fix/")) {
|
|
93
|
+
return "fix";
|
|
94
|
+
}
|
|
95
|
+
if (lower.startsWith("hotfix/") || lower.includes("/hotfix/")) {
|
|
96
|
+
return "hotfix";
|
|
97
|
+
}
|
|
98
|
+
if (lower.startsWith("refactor/") || lower.includes("/refactor/")) {
|
|
99
|
+
return "refactor";
|
|
100
|
+
}
|
|
101
|
+
if (lower.startsWith("docs/") || lower.includes("/docs/")) {
|
|
102
|
+
return "docs";
|
|
103
|
+
}
|
|
104
|
+
if (lower.startsWith("test/") || lower.includes("/test/")) {
|
|
105
|
+
return "test";
|
|
106
|
+
}
|
|
107
|
+
if (lower.startsWith("chore/") || lower.includes("/chore/")) {
|
|
108
|
+
return "chore";
|
|
109
|
+
}
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Check if repository is GitLab
|
|
114
|
+
*/
|
|
115
|
+
isGitLabRepo() {
|
|
116
|
+
const remote = this.exec("git config --get remote.origin.url");
|
|
117
|
+
return remote.includes("gitlab");
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Append issue reference to commit message
|
|
121
|
+
*/
|
|
122
|
+
appendIssueToCommit(message, issue) {
|
|
123
|
+
const enhanced = { ...message };
|
|
124
|
+
// Add issue reference based on platform conventions
|
|
125
|
+
switch (issue.platform) {
|
|
126
|
+
case "jira":
|
|
127
|
+
// Jira: Add to subject if not already there
|
|
128
|
+
if (!enhanced.subject.includes(issue.issueKey)) {
|
|
129
|
+
enhanced.subject = `${enhanced.subject} [${issue.issueKey}]`;
|
|
130
|
+
}
|
|
131
|
+
// Also add to footer
|
|
132
|
+
if (enhanced.body) {
|
|
133
|
+
enhanced.body += `\n\nJira: ${issue.issueKey}`;
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
enhanced.body = `Jira: ${issue.issueKey}`;
|
|
137
|
+
}
|
|
138
|
+
break;
|
|
139
|
+
case "github":
|
|
140
|
+
// GitHub: Add to footer for auto-linking
|
|
141
|
+
if (enhanced.body) {
|
|
142
|
+
enhanced.body += `\n\nCloses ${issue.issueKey}`;
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
enhanced.body = `Closes ${issue.issueKey}`;
|
|
146
|
+
}
|
|
147
|
+
break;
|
|
148
|
+
case "linear":
|
|
149
|
+
// Linear: Add to subject
|
|
150
|
+
if (!enhanced.subject.includes(issue.issueKey)) {
|
|
151
|
+
enhanced.subject = `${enhanced.subject} [${issue.issueKey}]`;
|
|
152
|
+
}
|
|
153
|
+
break;
|
|
154
|
+
case "gitlab":
|
|
155
|
+
// GitLab: Add to footer for auto-linking
|
|
156
|
+
if (enhanced.body) {
|
|
157
|
+
enhanced.body += `\n\nCloses ${issue.issueKey}`;
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
enhanced.body = `Closes ${issue.issueKey}`;
|
|
161
|
+
}
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
return enhanced;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Suggest commit type based on issue type
|
|
168
|
+
*/
|
|
169
|
+
suggestTypeFromIssue(issue, defaultType) {
|
|
170
|
+
if (!issue.issueType)
|
|
171
|
+
return defaultType;
|
|
172
|
+
const typeMap = {
|
|
173
|
+
feature: "feat",
|
|
174
|
+
fix: "fix",
|
|
175
|
+
bugfix: "fix",
|
|
176
|
+
hotfix: "fix",
|
|
177
|
+
refactor: "refactor",
|
|
178
|
+
docs: "docs",
|
|
179
|
+
test: "test",
|
|
180
|
+
chore: "chore",
|
|
181
|
+
};
|
|
182
|
+
return typeMap[issue.issueType] || defaultType;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Get display string for issue
|
|
186
|
+
*/
|
|
187
|
+
getIssueDisplay(issue) {
|
|
188
|
+
const platformEmoji = {
|
|
189
|
+
jira: "🎫",
|
|
190
|
+
github: "🐙",
|
|
191
|
+
linear: "📐",
|
|
192
|
+
gitlab: "🦊",
|
|
193
|
+
unknown: "🔗",
|
|
194
|
+
};
|
|
195
|
+
const emoji = platformEmoji[issue.platform] || "🔗";
|
|
196
|
+
const platform = issue.platform.charAt(0).toUpperCase() + issue.platform.slice(1);
|
|
197
|
+
return `${emoji} ${platform}: ${issue.issueKey}`;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Clear cache (useful for testing or branch switches)
|
|
201
|
+
*/
|
|
202
|
+
clearCache() {
|
|
203
|
+
this.cache = null;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
exports.IssueTrackerIntegration = IssueTrackerIntegration;
|
|
207
|
+
//# sourceMappingURL=issue-tracker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"issue-tracker.js","sourceRoot":"","sources":["../../../src/utils/issue-tracker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare class LoadingIndicator {
|
|
2
|
+
private frames;
|
|
3
|
+
private currentFrame;
|
|
4
|
+
private interval;
|
|
5
|
+
private message;
|
|
6
|
+
constructor(message?: string);
|
|
7
|
+
start(): void;
|
|
8
|
+
updateMessage(message: string): void;
|
|
9
|
+
stop(finalMessage?: string): void;
|
|
10
|
+
succeed(message: string): void;
|
|
11
|
+
fail(message: string): void;
|
|
12
|
+
warn(message: string): void;
|
|
13
|
+
}
|
|
14
|
+
export declare function withLoading<T>(message: string, operation: () => Promise<T>, successMessage?: string): Promise<T>;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.LoadingIndicator = void 0;
|
|
7
|
+
exports.withLoading = withLoading;
|
|
8
|
+
// ./src/utils/loading.ts
|
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
class LoadingIndicator {
|
|
11
|
+
constructor(message = "Loading...") {
|
|
12
|
+
this.frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
13
|
+
this.currentFrame = 0;
|
|
14
|
+
this.interval = null;
|
|
15
|
+
this.message = message;
|
|
16
|
+
}
|
|
17
|
+
start() {
|
|
18
|
+
// Hide cursor
|
|
19
|
+
process.stdout.write("\x1B[?25l");
|
|
20
|
+
this.interval = setInterval(() => {
|
|
21
|
+
const frame = this.frames[this.currentFrame];
|
|
22
|
+
process.stdout.write(`\r${chalk_1.default.cyan(frame)} ${chalk_1.default.gray(this.message)}`);
|
|
23
|
+
this.currentFrame = (this.currentFrame + 1) % this.frames.length;
|
|
24
|
+
}, 80);
|
|
25
|
+
}
|
|
26
|
+
updateMessage(message) {
|
|
27
|
+
this.message = message;
|
|
28
|
+
}
|
|
29
|
+
stop(finalMessage) {
|
|
30
|
+
if (this.interval) {
|
|
31
|
+
clearInterval(this.interval);
|
|
32
|
+
this.interval = null;
|
|
33
|
+
}
|
|
34
|
+
// Clear the line
|
|
35
|
+
process.stdout.write("\r\x1B[K");
|
|
36
|
+
// Show cursor
|
|
37
|
+
process.stdout.write("\x1B[?25h");
|
|
38
|
+
if (finalMessage) {
|
|
39
|
+
console.log(finalMessage);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
succeed(message) {
|
|
43
|
+
this.stop(chalk_1.default.green(`✓ ${message}`));
|
|
44
|
+
}
|
|
45
|
+
fail(message) {
|
|
46
|
+
this.stop(chalk_1.default.red(`✗ ${message}`));
|
|
47
|
+
}
|
|
48
|
+
warn(message) {
|
|
49
|
+
this.stop(chalk_1.default.yellow(`⚠ ${message}`));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
exports.LoadingIndicator = LoadingIndicator;
|
|
53
|
+
// Utility function for wrapping async operations with loading indicator
|
|
54
|
+
async function withLoading(message, operation, successMessage) {
|
|
55
|
+
const loader = new LoadingIndicator(message);
|
|
56
|
+
loader.start();
|
|
57
|
+
try {
|
|
58
|
+
const result = await operation();
|
|
59
|
+
if (successMessage) {
|
|
60
|
+
loader.succeed(successMessage);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
loader.stop();
|
|
64
|
+
}
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
loader.stop();
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=loading.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loading.js","sourceRoot":"","sources":["../../../src/utils/loading.ts"],"names":[],"mappings":";;;;;;AA6DA,kCAoBC;AAjFD,yBAAyB;AACzB,kDAA0B;AAE1B,MAAa,gBAAgB;IAM3B,YAAY,UAAkB,YAAY;QALlC,WAAM,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;QAC5D,iBAAY,GAAG,CAAC,CAAC;QACjB,aAAQ,GAA0B,IAAI,CAAC;QAI7C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,KAAK;QACH,cAAc;QACd,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAElC,IAAI,CAAC,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE;YAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAC7C,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,KAAK,eAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,eAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CACrD,CAAC;YACF,IAAI,CAAC,YAAY,GAAG,CAAC,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;QACnE,CAAC,EAAE,EAAE,CAAC,CAAC;IACT,CAAC;IAED,aAAa,CAAC,OAAe;QAC3B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,IAAI,CAAC,YAAqB;QACxB,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC7B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACvB,CAAC;QAED,iBAAiB;QACjB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAEjC,cAAc;QACd,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAElC,IAAI,YAAY,EAAE,CAAC;YACjB,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,OAAO,CAAC,OAAe;QACrB,IAAI,CAAC,IAAI,CAAC,eAAK,CAAC,KAAK,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC,CAAC;IACzC,CAAC;IAED,IAAI,CAAC,OAAe;QAClB,IAAI,CAAC,IAAI,CAAC,eAAK,CAAC,GAAG,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC,CAAC;IACvC,CAAC;IAED,IAAI,CAAC,OAAe;QAClB,IAAI,CAAC,IAAI,CAAC,eAAK,CAAC,MAAM,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC,CAAC;IAC1C,CAAC;CACF;AAvDD,4CAuDC;AAED,wEAAwE;AACjE,KAAK,UAAU,WAAW,CAC/B,OAAe,EACf,SAA2B,EAC3B,cAAuB;IAEvB,MAAM,MAAM,GAAG,IAAI,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC7C,MAAM,CAAC,KAAK,EAAE,CAAC;IAEf,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;QACjC,IAAI,cAAc,EAAE,CAAC;YACnB,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;QACjC,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,EAAE,CAAC;QAChB,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,EAAE,CAAC;QACd,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { CommitMessage, GitAnalysis } from "../types";
|
|
2
|
+
export interface CommitGroup {
|
|
3
|
+
files: string[];
|
|
4
|
+
analysis: GitAnalysis;
|
|
5
|
+
suggestedMessage: CommitMessage;
|
|
6
|
+
reason: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class MultiCommitAnalyzer {
|
|
9
|
+
private exec;
|
|
10
|
+
/**
|
|
11
|
+
* Analyze if changes should be split into multiple commits
|
|
12
|
+
*/
|
|
13
|
+
shouldSplit(analysis: GitAnalysis): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Identify distinct concerns in changed files
|
|
16
|
+
*/
|
|
17
|
+
private identifyConcerns;
|
|
18
|
+
/**
|
|
19
|
+
* Group files into logical commits
|
|
20
|
+
*/
|
|
21
|
+
groupFiles(analysis: GitAnalysis): CommitGroup[];
|
|
22
|
+
/**
|
|
23
|
+
* Create analysis for a specific file group
|
|
24
|
+
*/
|
|
25
|
+
private createGroupAnalysis;
|
|
26
|
+
/**
|
|
27
|
+
* Suggest commit message for a group
|
|
28
|
+
*/
|
|
29
|
+
private suggestMessageForGroup;
|
|
30
|
+
private inferScopeFromFiles;
|
|
31
|
+
/**
|
|
32
|
+
* Get human-readable reason for grouping
|
|
33
|
+
*/
|
|
34
|
+
private getGroupReason;
|
|
35
|
+
/**
|
|
36
|
+
* Sort groups in logical commit order
|
|
37
|
+
*/
|
|
38
|
+
private sortCommitGroups;
|
|
39
|
+
private getGroupKey;
|
|
40
|
+
}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ./src/utils/multi-commit.ts
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.MultiCommitAnalyzer = void 0;
|
|
5
|
+
const child_process_1 = require("child_process");
|
|
6
|
+
class MultiCommitAnalyzer {
|
|
7
|
+
exec(cmd) {
|
|
8
|
+
try {
|
|
9
|
+
return (0, child_process_1.execSync)(cmd, {
|
|
10
|
+
encoding: "utf8",
|
|
11
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
12
|
+
}).trim();
|
|
13
|
+
}
|
|
14
|
+
catch (_a) {
|
|
15
|
+
return "";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Analyze if changes should be split into multiple commits
|
|
20
|
+
*/
|
|
21
|
+
shouldSplit(analysis) {
|
|
22
|
+
const { filesChanged } = analysis;
|
|
23
|
+
// Don't split if only a few files
|
|
24
|
+
if (filesChanged.length < 4)
|
|
25
|
+
return false;
|
|
26
|
+
// Check for mixed concerns
|
|
27
|
+
const concerns = this.identifyConcerns(filesChanged);
|
|
28
|
+
// Split if we have 2+ distinct concerns
|
|
29
|
+
return concerns.size >= 2;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Identify distinct concerns in changed files
|
|
33
|
+
*/
|
|
34
|
+
identifyConcerns(files) {
|
|
35
|
+
const concerns = new Set();
|
|
36
|
+
for (const file of files) {
|
|
37
|
+
if (file.includes("test") ||
|
|
38
|
+
file.includes("spec") ||
|
|
39
|
+
file.includes("__tests__")) {
|
|
40
|
+
concerns.add("tests");
|
|
41
|
+
}
|
|
42
|
+
else if (file.includes("README") || file.endsWith(".md")) {
|
|
43
|
+
concerns.add("docs");
|
|
44
|
+
}
|
|
45
|
+
else if (file.includes("package.json") ||
|
|
46
|
+
file.includes("tsconfig") ||
|
|
47
|
+
file.includes(".config")) {
|
|
48
|
+
concerns.add("config");
|
|
49
|
+
}
|
|
50
|
+
else if (file.includes("component") || file.includes("Component")) {
|
|
51
|
+
concerns.add("components");
|
|
52
|
+
}
|
|
53
|
+
else if (file.includes("util") || file.includes("helper")) {
|
|
54
|
+
concerns.add("utils");
|
|
55
|
+
}
|
|
56
|
+
else if (file.includes("api") || file.includes("endpoint")) {
|
|
57
|
+
concerns.add("api");
|
|
58
|
+
}
|
|
59
|
+
else if (file.includes("style") ||
|
|
60
|
+
file.endsWith(".css") ||
|
|
61
|
+
file.endsWith(".scss")) {
|
|
62
|
+
concerns.add("styles");
|
|
63
|
+
}
|
|
64
|
+
else if (file.includes("type") || file.includes("interface")) {
|
|
65
|
+
concerns.add("types");
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
concerns.add("feature");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return concerns;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Group files into logical commits
|
|
75
|
+
*/
|
|
76
|
+
groupFiles(analysis) {
|
|
77
|
+
const { filesChanged } = analysis;
|
|
78
|
+
const groups = new Map();
|
|
79
|
+
// Priority order for grouping
|
|
80
|
+
const priorities = [
|
|
81
|
+
{
|
|
82
|
+
key: "config",
|
|
83
|
+
pattern: (f) => f.includes("package.json") ||
|
|
84
|
+
f.includes("tsconfig") ||
|
|
85
|
+
f.includes(".config"),
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
key: "types",
|
|
89
|
+
pattern: (f) => f.includes("type") || f.includes("interface"),
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
key: "tests",
|
|
93
|
+
pattern: (f) => f.includes("test") || f.includes("spec") || f.includes("__tests__"),
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
key: "docs",
|
|
97
|
+
pattern: (f) => f.includes("README") || f.endsWith(".md"),
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
key: "styles",
|
|
101
|
+
pattern: (f) => f.includes("style") || f.endsWith(".css") || f.endsWith(".scss"),
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
key: "components",
|
|
105
|
+
pattern: (f) => f.includes("component") || f.includes("Component"),
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
key: "api",
|
|
109
|
+
pattern: (f) => f.includes("api") || f.includes("endpoint"),
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
key: "utils",
|
|
113
|
+
pattern: (f) => f.includes("util") || f.includes("helper"),
|
|
114
|
+
},
|
|
115
|
+
];
|
|
116
|
+
// Group files by concern
|
|
117
|
+
const ungrouped = [];
|
|
118
|
+
for (const file of filesChanged) {
|
|
119
|
+
let grouped = false;
|
|
120
|
+
for (const { key, pattern } of priorities) {
|
|
121
|
+
if (pattern(file)) {
|
|
122
|
+
if (!groups.has(key)) {
|
|
123
|
+
groups.set(key, []);
|
|
124
|
+
}
|
|
125
|
+
groups.get(key).push(file);
|
|
126
|
+
grouped = true;
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (!grouped) {
|
|
131
|
+
ungrouped.push(file);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Group remaining files by directory
|
|
135
|
+
if (ungrouped.length > 0) {
|
|
136
|
+
const dirGroups = new Map();
|
|
137
|
+
for (const file of ungrouped) {
|
|
138
|
+
const dir = file.split("/")[0] || "root";
|
|
139
|
+
if (!dirGroups.has(dir)) {
|
|
140
|
+
dirGroups.set(dir, []);
|
|
141
|
+
}
|
|
142
|
+
dirGroups.get(dir).push(file);
|
|
143
|
+
}
|
|
144
|
+
dirGroups.forEach((files, dir) => {
|
|
145
|
+
groups.set(`feature-${dir}`, files);
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
// Convert to CommitGroup array
|
|
149
|
+
const commitGroups = [];
|
|
150
|
+
for (const [key, files] of groups.entries()) {
|
|
151
|
+
if (files.length === 0)
|
|
152
|
+
continue;
|
|
153
|
+
const groupAnalysis = this.createGroupAnalysis(files, analysis);
|
|
154
|
+
const suggestedMessage = this.suggestMessageForGroup(key, files);
|
|
155
|
+
const reason = this.getGroupReason(key, files);
|
|
156
|
+
commitGroups.push({
|
|
157
|
+
files,
|
|
158
|
+
analysis: groupAnalysis,
|
|
159
|
+
suggestedMessage,
|
|
160
|
+
reason,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
// Sort by logical commit order
|
|
164
|
+
return this.sortCommitGroups(commitGroups);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Create analysis for a specific file group
|
|
168
|
+
*/
|
|
169
|
+
createGroupAnalysis(files, fullAnalysis) {
|
|
170
|
+
// Get diff for specific files
|
|
171
|
+
const filesStr = files.map((f) => `"${f}"`).join(" ");
|
|
172
|
+
const diff = this.exec(`git diff --cached -- ${filesStr}`);
|
|
173
|
+
const additions = (diff.match(/^\+(?!\+)/gm) || []).length;
|
|
174
|
+
const deletions = (diff.match(/^-(?!-)/gm) || []).length;
|
|
175
|
+
return {
|
|
176
|
+
filesChanged: files,
|
|
177
|
+
additions,
|
|
178
|
+
deletions,
|
|
179
|
+
hasStaged: true,
|
|
180
|
+
hasUnstaged: fullAnalysis.hasUnstaged,
|
|
181
|
+
diff,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Suggest commit message for a group
|
|
186
|
+
*/
|
|
187
|
+
suggestMessageForGroup(key, files) {
|
|
188
|
+
const typeMap = {
|
|
189
|
+
config: { type: "chore", subject: "update configuration" },
|
|
190
|
+
types: { type: "feat", subject: "update type definitions" },
|
|
191
|
+
tests: { type: "test", subject: "add/update tests" },
|
|
192
|
+
docs: { type: "docs", subject: "update documentation" },
|
|
193
|
+
styles: { type: "style", subject: "update styles" },
|
|
194
|
+
components: { type: "feat", subject: "update components" },
|
|
195
|
+
api: { type: "feat", subject: "update API endpoints" },
|
|
196
|
+
utils: { type: "feat", subject: "update utility functions" },
|
|
197
|
+
};
|
|
198
|
+
const suggestion = typeMap[key] || {
|
|
199
|
+
type: "feat",
|
|
200
|
+
subject: "update files",
|
|
201
|
+
};
|
|
202
|
+
// Infer scope from files
|
|
203
|
+
const scope = this.inferScopeFromFiles(files);
|
|
204
|
+
return {
|
|
205
|
+
type: suggestion.type,
|
|
206
|
+
scope,
|
|
207
|
+
subject: suggestion.subject,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
inferScopeFromFiles(files) {
|
|
211
|
+
if (files.length === 0)
|
|
212
|
+
return undefined;
|
|
213
|
+
const dirs = files
|
|
214
|
+
.map((f) => f.split("/")[0])
|
|
215
|
+
.filter((d) => d && !d.startsWith("."));
|
|
216
|
+
if (dirs.length === 0)
|
|
217
|
+
return undefined;
|
|
218
|
+
const dirCount = dirs.reduce((acc, d) => {
|
|
219
|
+
acc[d] = (acc[d] || 0) + 1;
|
|
220
|
+
return acc;
|
|
221
|
+
}, {});
|
|
222
|
+
const mostCommon = Object.entries(dirCount).sort((a, b) => b[1] - a[1])[0];
|
|
223
|
+
return mostCommon[1] >= files.length * 0.6 ? mostCommon[0] : undefined;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Get human-readable reason for grouping
|
|
227
|
+
*/
|
|
228
|
+
getGroupReason(key, files) {
|
|
229
|
+
const reasonMap = {
|
|
230
|
+
config: "Configuration changes",
|
|
231
|
+
types: "Type definition updates",
|
|
232
|
+
tests: "Test additions/updates",
|
|
233
|
+
docs: "Documentation updates",
|
|
234
|
+
styles: "Styling changes",
|
|
235
|
+
components: "Component updates",
|
|
236
|
+
api: "API changes",
|
|
237
|
+
utils: "Utility function updates",
|
|
238
|
+
};
|
|
239
|
+
return reasonMap[key] || `${files.length} related files`;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Sort groups in logical commit order
|
|
243
|
+
*/
|
|
244
|
+
sortCommitGroups(groups) {
|
|
245
|
+
const order = [
|
|
246
|
+
"types",
|
|
247
|
+
"config",
|
|
248
|
+
"api",
|
|
249
|
+
"components",
|
|
250
|
+
"utils",
|
|
251
|
+
"styles",
|
|
252
|
+
"tests",
|
|
253
|
+
"docs",
|
|
254
|
+
];
|
|
255
|
+
return groups.sort((a, b) => {
|
|
256
|
+
const aKey = this.getGroupKey(a);
|
|
257
|
+
const bKey = this.getGroupKey(b);
|
|
258
|
+
const aIndex = order.indexOf(aKey);
|
|
259
|
+
const bIndex = order.indexOf(bKey);
|
|
260
|
+
if (aIndex === -1 && bIndex === -1)
|
|
261
|
+
return 0;
|
|
262
|
+
if (aIndex === -1)
|
|
263
|
+
return 1;
|
|
264
|
+
if (bIndex === -1)
|
|
265
|
+
return -1;
|
|
266
|
+
return aIndex - bIndex;
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
getGroupKey(group) {
|
|
270
|
+
return group.reason.toLowerCase().split(" ")[0];
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
exports.MultiCommitAnalyzer = MultiCommitAnalyzer;
|
|
274
|
+
//# sourceMappingURL=multi-commit.js.map
|