gitpt 1.4.0 → 1.7.2
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/README.md +34 -160
- package/dist/commands/commit/commitFlags.js +7 -0
- package/dist/commands/commit/context/buildPrompt.js +16 -0
- package/dist/commands/commit/context/summaryPrompt.js +20 -0
- package/dist/commands/commit/context/systemPrompt.js +4 -1
- package/dist/commands/commit/context/userPrompt.js +4 -1
- package/dist/commands/commit/generateCommitMessage.js +16 -37
- package/dist/commands/commit/index.js +187 -178
- package/dist/commands/commit/summarizeDiff.js +193 -0
- package/dist/commands/config.js +24 -9
- package/dist/commands/hook/index.js +68 -0
- package/dist/commands/middleware/capabilitiesMiddleware/ghCapability.js +20 -20
- package/dist/commands/middleware/capabilitiesMiddleware/gitCapability.js +13 -10
- package/dist/commands/middleware/capabilitiesMiddleware/index.js +12 -11
- package/dist/commands/middleware/hasStagedChangesMiddleware.js +9 -6
- package/dist/commands/middleware/setupMiddleware/defaultModels.js +15 -0
- package/dist/commands/middleware/setupMiddleware/index.js +66 -38
- package/dist/commands/model.js +7 -4
- package/dist/commands/passthroughArgs.js +11 -0
- package/dist/commands/pr/context/systemPrompt.js +4 -1
- package/dist/commands/pr/context/userPrompt.js +4 -1
- package/dist/commands/pr/generatePRDetails.js +25 -31
- package/dist/commands/pr/getPRContext.js +37 -62
- package/dist/commands/pr/index.js +57 -62
- package/dist/commands/reset.js +26 -0
- package/dist/commands/review/index.js +31 -0
- package/dist/commands/setup.js +38 -13
- package/dist/config.js +63 -55
- package/dist/index.js +31 -62
- package/dist/llm/budget.js +10 -0
- package/dist/llm/index.js +12 -12
- package/dist/llm/providers/anthropic/index.js +31 -0
- package/dist/llm/providers/apiKey.js +41 -0
- package/dist/llm/providers/apple/client.js +77 -0
- package/dist/llm/providers/apple/index.js +69 -0
- package/dist/llm/providers/apple/models.js +29 -0
- package/dist/llm/providers/base.js +36 -0
- package/dist/llm/providers/local/index.js +84 -0
- package/dist/llm/providers/openai/index.js +19 -0
- package/dist/llm/providers/openaiCompatible.js +66 -0
- package/dist/llm/providers/openrouter/index.js +19 -0
- package/dist/llm/registry.js +39 -0
- package/dist/llm/setup/getAvailableModels.js +17 -0
- package/dist/llm/setup/selectModel.js +40 -0
- package/dist/llm/tokenCount.js +5 -0
- package/dist/package.js +67 -0
- package/dist/services/gh/createPullRequest.js +38 -58
- package/dist/services/gh/index.js +6 -3
- package/dist/services/gh/isAvailable.js +10 -8
- package/dist/services/git/executeGitAdd.js +11 -12
- package/dist/services/git/executeGitCommit.js +10 -11
- package/dist/services/git/getAmendChanges.js +23 -0
- package/dist/services/git/getChangedFiles.js +28 -61
- package/dist/services/git/getCommitsSinceBaseBranch.js +28 -56
- package/dist/services/git/getCurrentBranch.js +10 -8
- package/dist/services/git/getDefaultBranch.js +35 -60
- package/dist/services/git/getStagedChanges.js +10 -8
- package/dist/services/git/getStagedFiles.js +10 -9
- package/dist/services/git/hasStagedChanges.js +9 -8
- package/dist/services/git/index.js +17 -12
- package/dist/services/git/isAvailable.js +10 -8
- package/dist/services/git/isGitRepository.js +10 -8
- package/dist/utils/commitlint.js +97 -135
- package/dist/utils/formatBaseURL.js +7 -6
- package/dist/utils/maskApiKey.js +6 -7
- package/package.json +19 -5
- package/dist/commands/commit/context/systemPrompt.d.ts +0 -1
- package/dist/commands/commit/context/userPrompt.d.ts +0 -1
- package/dist/commands/commit/generateCommitMessage.d.ts +0 -1
- package/dist/commands/commit/index.d.ts +0 -7
- package/dist/commands/config.d.ts +0 -1
- package/dist/commands/middleware/capabilitiesMiddleware/ghCapability.d.ts +0 -1
- package/dist/commands/middleware/capabilitiesMiddleware/gitCapability.d.ts +0 -1
- package/dist/commands/middleware/capabilitiesMiddleware/index.d.ts +0 -3
- package/dist/commands/middleware/hasStagedChangesMiddleware.d.ts +0 -1
- package/dist/commands/middleware/setupMiddleware/getAvailableModels.d.ts +0 -4
- package/dist/commands/middleware/setupMiddleware/getAvailableModels.js +0 -11
- package/dist/commands/middleware/setupMiddleware/getOrUpdateApiKey.d.ts +0 -1
- package/dist/commands/middleware/setupMiddleware/getOrUpdateApiKey.js +0 -39
- package/dist/commands/middleware/setupMiddleware/index.d.ts +0 -4
- package/dist/commands/middleware/setupMiddleware/selectModel.d.ts +0 -5
- package/dist/commands/middleware/setupMiddleware/selectModel.js +0 -38
- package/dist/commands/middleware/setupMiddleware/setupLocalLLM.d.ts +0 -5
- package/dist/commands/middleware/setupMiddleware/setupLocalLLM.js +0 -60
- package/dist/commands/middleware/setupMiddleware/setupOpenRouter.d.ts +0 -2
- package/dist/commands/middleware/setupMiddleware/setupOpenRouter.js +0 -66
- package/dist/commands/middleware/setupMiddleware/types.d.ts +0 -13
- package/dist/commands/middleware/setupMiddleware/types.js +0 -1
- package/dist/commands/model.d.ts +0 -1
- package/dist/commands/pr/context/systemPrompt.d.ts +0 -1
- package/dist/commands/pr/context/userPrompt.d.ts +0 -1
- package/dist/commands/pr/generatePRDetails.d.ts +0 -4
- package/dist/commands/pr/getPRContext.d.ts +0 -1
- package/dist/commands/pr/index.d.ts +0 -10
- package/dist/commands/setup.d.ts +0 -3
- package/dist/config.d.ts +0 -18
- package/dist/index.d.ts +0 -2
- package/dist/llm/index.d.ts +0 -5
- package/dist/services/gh/createPullRequest.d.ts +0 -1
- package/dist/services/gh/index.d.ts +0 -4
- package/dist/services/gh/isAvailable.d.ts +0 -1
- package/dist/services/git/executeGitAdd.d.ts +0 -1
- package/dist/services/git/executeGitCommit.d.ts +0 -1
- package/dist/services/git/getChangedFiles.d.ts +0 -1
- package/dist/services/git/getCommitsSinceBaseBranch.d.ts +0 -1
- package/dist/services/git/getCurrentBranch.d.ts +0 -1
- package/dist/services/git/getDefaultBranch.d.ts +0 -1
- package/dist/services/git/getStagedChanges.d.ts +0 -1
- package/dist/services/git/getStagedFiles.d.ts +0 -1
- package/dist/services/git/hasStagedChanges.d.ts +0 -1
- package/dist/services/git/index.d.ts +0 -13
- package/dist/services/git/isAvailable.d.ts +0 -1
- package/dist/services/git/isGitRepository.d.ts +0 -1
- package/dist/utils/commitlint.d.ts +0 -25
- package/dist/utils/formatBaseURL.d.ts +0 -1
- package/dist/utils/maskApiKey.d.ts +0 -1
|
@@ -1,183 +1,192 @@
|
|
|
1
|
-
import
|
|
2
|
-
import inquirer from "inquirer";
|
|
1
|
+
import { git } from "../../services/git/index.js";
|
|
3
2
|
import { capabilitiesMiddleware } from "../middleware/capabilitiesMiddleware/index.js";
|
|
3
|
+
import { isDebug } from "../../config.js";
|
|
4
4
|
import { setupMiddleware } from "../middleware/setupMiddleware/index.js";
|
|
5
|
-
import {
|
|
6
|
-
import { hasCommitlintConfig, validateCommitMessage, } from "../../utils/commitlint.js";
|
|
5
|
+
import { hasCommitlintConfig, validateCommitMessage } from "../../utils/commitlint.js";
|
|
7
6
|
import { hasStagedChangesMiddleware } from "../middleware/hasStagedChangesMiddleware.js";
|
|
8
7
|
import { generateCommitMessage } from "./generateCommitMessage.js";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
8
|
+
import { prepareCommitContext } from "./summarizeDiff.js";
|
|
9
|
+
import { isAmend, skipsStagedGuard } from "./commitFlags.js";
|
|
10
|
+
import { passthroughArgs } from "../passthroughArgs.js";
|
|
11
|
+
import chalk from "chalk";
|
|
12
|
+
import inquirer from "inquirer";
|
|
13
|
+
//#region src/commands/commit/index.ts
|
|
14
|
+
var commitCommand = async (options, command) => {
|
|
15
|
+
if (options.debug) ({}).GITPT_DEBUG = "1";
|
|
16
|
+
capabilitiesMiddleware(["git"]);
|
|
17
|
+
await setupMiddleware();
|
|
18
|
+
const passthrough = passthroughArgs(command);
|
|
19
|
+
const amend = isAmend(passthrough);
|
|
20
|
+
const noEdit = options.edit === false;
|
|
21
|
+
if (!skipsStagedGuard(passthrough)) hasStagedChangesMiddleware();
|
|
22
|
+
if (amend && noEdit && !options.message) {
|
|
23
|
+
if (options.dryRun) {
|
|
24
|
+
console.log(chalk.yellow("[dry-run] Would amend the last commit (keeping its message). Nothing changed."));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
git.commit(null, [...passthrough, "--no-edit"]);
|
|
29
|
+
console.log(chalk.green("✓ Amended (kept the original message)."));
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error(chalk.red("Error:"), error instanceof Error ? error.message : String(error));
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
let commitMessage;
|
|
37
|
+
let context;
|
|
38
|
+
if (options.message) commitMessage = options.message;
|
|
39
|
+
else try {
|
|
40
|
+
const diff = amend ? git.getAmendChanges() : git.getStagedChanges();
|
|
41
|
+
if (!diff.trim()) {
|
|
42
|
+
console.error(chalk.red("Error: nothing to summarise. Provide a message with -m \"...\"."));
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
context = await prepareCommitContext(diff);
|
|
46
|
+
console.log(chalk.blue("Generating commit message..."));
|
|
47
|
+
let hasCommitlint = false;
|
|
48
|
+
try {
|
|
49
|
+
hasCommitlint = hasCommitlintConfig();
|
|
50
|
+
if (hasCommitlint) console.log(chalk.blue("Commitlint configuration detected. Generating message according to rules..."));
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.warn(chalk.yellow("Warning: Error detecting commitlint config, proceeding without commitlint validation."));
|
|
53
|
+
}
|
|
54
|
+
const startedAt = Date.now();
|
|
55
|
+
commitMessage = await generateCommitMessage(context);
|
|
56
|
+
if (isDebug()) console.log(chalk.gray(`⚙ debug · ~${Math.ceil(context.length / 3)} context tokens · generated in ${Date.now() - startedAt}ms`));
|
|
57
|
+
if (hasCommitlint) try {
|
|
58
|
+
console.log(chalk.blue("Validating commit message against commitlint rules..."));
|
|
59
|
+
let validMessage = false;
|
|
60
|
+
let attempts = 0;
|
|
61
|
+
const MAX_ATTEMPTS = 3;
|
|
62
|
+
let validationErrors;
|
|
63
|
+
while (!validMessage && attempts < MAX_ATTEMPTS) {
|
|
64
|
+
const validation = await validateCommitMessage(commitMessage);
|
|
65
|
+
if (validation.valid) {
|
|
66
|
+
console.log(chalk.green("✓ Commit message passed validation"));
|
|
67
|
+
validMessage = true;
|
|
68
|
+
break;
|
|
69
|
+
} else {
|
|
70
|
+
attempts++;
|
|
71
|
+
console.log(chalk.yellow(`Commit message failed validation. ${attempts < MAX_ATTEMPTS ? "Regenerating..." : "Max attempts reached."}`));
|
|
72
|
+
if (validation.errors) {
|
|
73
|
+
console.log(chalk.gray(validation.errors));
|
|
74
|
+
validationErrors = validation.errors;
|
|
75
|
+
}
|
|
76
|
+
if (attempts < MAX_ATTEMPTS) try {
|
|
77
|
+
commitMessage = await generateCommitMessage(context, validationErrors);
|
|
78
|
+
} catch (error) {
|
|
79
|
+
console.warn(chalk.yellow("Error regenerating message, breaking validation loop."));
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
const { action } = await inquirer.prompt([{
|
|
84
|
+
type: "list",
|
|
85
|
+
name: "action",
|
|
86
|
+
message: "Failed to generate a valid commit message after 3 attempts. What would you like to do?",
|
|
87
|
+
choices: [
|
|
88
|
+
{
|
|
89
|
+
name: "Proceed with invalid message",
|
|
90
|
+
value: "proceed"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: "Edit message manually",
|
|
94
|
+
value: "edit"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: "Abort commit",
|
|
98
|
+
value: "abort"
|
|
99
|
+
}
|
|
100
|
+
]
|
|
101
|
+
}]);
|
|
102
|
+
if (action === "abort") {
|
|
103
|
+
console.log(chalk.red("Commit aborted due to validation failures."));
|
|
104
|
+
process.exit(1);
|
|
105
|
+
} else if (action === "edit") options.edit = true;
|
|
106
|
+
else console.log(chalk.yellow("Proceeding with invalid message..."));
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
} catch (error) {
|
|
112
|
+
console.warn(chalk.yellow("Warning: Error during commitlint validation, proceeding without validation."));
|
|
113
|
+
console.warn(chalk.gray("This may be due to an ESM module cycle conflict or missing commitlint dependencies."));
|
|
114
|
+
}
|
|
115
|
+
console.log(chalk.green("✓ Commit message generated"));
|
|
116
|
+
console.log("");
|
|
117
|
+
console.log(chalk.cyan("Generated message:"));
|
|
118
|
+
console.log(commitMessage);
|
|
119
|
+
console.log("");
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.error(chalk.red("Error:"), error instanceof Error ? error.message : String(error));
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
if (options.dryRun) return;
|
|
125
|
+
if (options.edit !== false && !options.message && context !== void 0) {
|
|
126
|
+
let confirmed = false;
|
|
127
|
+
while (!confirmed) {
|
|
128
|
+
const { action } = await inquirer.prompt([{
|
|
129
|
+
type: "list",
|
|
130
|
+
name: "action",
|
|
131
|
+
message: "Use this commit message?",
|
|
132
|
+
choices: [
|
|
133
|
+
{
|
|
134
|
+
name: "Confirm and commit",
|
|
135
|
+
value: "confirm"
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
name: "Generate another (different wording)",
|
|
139
|
+
value: "another"
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: "Edit manually",
|
|
143
|
+
value: "edit"
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
name: "Cancel",
|
|
147
|
+
value: "cancel"
|
|
148
|
+
}
|
|
149
|
+
]
|
|
150
|
+
}]);
|
|
151
|
+
if (action === "confirm") confirmed = true;
|
|
152
|
+
else if (action === "another") {
|
|
153
|
+
commitMessage = (await generateCommitMessage(`${context}\n\nWrite a noticeably different commit message — a fresh wording or angle, not the same as before.`)).trim();
|
|
154
|
+
console.log("");
|
|
155
|
+
console.log(chalk.cyan("Generated message:"));
|
|
156
|
+
console.log(commitMessage);
|
|
157
|
+
console.log("");
|
|
158
|
+
} else if (action === "edit") {
|
|
159
|
+
commitMessage = (await inquirer.prompt([{
|
|
160
|
+
type: "editor",
|
|
161
|
+
name: "message",
|
|
162
|
+
message: "Edit commit message:",
|
|
163
|
+
default: commitMessage
|
|
164
|
+
}])).message;
|
|
165
|
+
confirmed = true;
|
|
166
|
+
} else {
|
|
167
|
+
console.log(chalk.gray("Commit cancelled."));
|
|
168
|
+
process.exit(0);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
try {
|
|
172
|
+
if (hasCommitlintConfig()) {
|
|
173
|
+
const validation = await validateCommitMessage(commitMessage);
|
|
174
|
+
if (!validation.valid) {
|
|
175
|
+
console.log(chalk.yellow("Warning: Edited message still has validation issues."));
|
|
176
|
+
if (validation.errors) console.log(chalk.gray(validation.errors));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
} catch (error) {
|
|
180
|
+
console.warn(chalk.yellow("Could not validate edited message, proceeding anyway."));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
try {
|
|
184
|
+
git.commit(commitMessage, passthrough);
|
|
185
|
+
console.log(chalk.green("✓ Changes committed successfully"));
|
|
186
|
+
} catch (error) {
|
|
187
|
+
console.error(chalk.red("Error:"), error instanceof Error ? error.message : String(error));
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
183
190
|
};
|
|
191
|
+
//#endregion
|
|
192
|
+
export { commitCommand };
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { getProvider } from "../../llm/registry.js";
|
|
2
|
+
import { systemPrompt } from "./context/systemPrompt.js";
|
|
3
|
+
import { userPrompt } from "./context/userPrompt.js";
|
|
4
|
+
import { countTokens } from "../../llm/tokenCount.js";
|
|
5
|
+
import { fitBudget } from "../../llm/budget.js";
|
|
6
|
+
import { summarySystemPrompt, summaryUserPrompt } from "./context/summaryPrompt.js";
|
|
7
|
+
import ora from "ora";
|
|
8
|
+
//#region src/commands/commit/summarizeDiff.ts
|
|
9
|
+
var MAX_REDUCE_PASSES = 3;
|
|
10
|
+
var LOW_SIGNAL_PATTERNS = [
|
|
11
|
+
{
|
|
12
|
+
test: /(^|\/)package-lock\.json$/,
|
|
13
|
+
note: "dependency lockfile updated"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
test: /(^|\/)npm-shrinkwrap\.json$/,
|
|
17
|
+
note: "dependency lockfile updated"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
test: /(^|\/)yarn\.lock$/,
|
|
21
|
+
note: "dependency lockfile updated"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
test: /(^|\/)pnpm-lock\.yaml$/,
|
|
25
|
+
note: "dependency lockfile updated"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
test: /\.lock$/,
|
|
29
|
+
note: "dependency lockfile updated"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
test: /(^|\/)(dist|build|out|coverage)\//,
|
|
33
|
+
note: "generated output updated"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
test: /\.min\.(js|css)$/,
|
|
37
|
+
note: "minified asset updated"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
test: /\.(snap)$/,
|
|
41
|
+
note: "test snapshot updated"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
test: /(^|\/)snapshots?\//,
|
|
45
|
+
note: "test snapshot updated"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
test: /\.(patch|diff)$/,
|
|
49
|
+
note: "patch file updated"
|
|
50
|
+
}
|
|
51
|
+
];
|
|
52
|
+
var lowSignalNote = (file) => LOW_SIGNAL_PATTERNS.find((p) => p.test.test(file))?.note ?? null;
|
|
53
|
+
var finalPromptTokens = (context) => countTokens(`${systemPrompt}\n\n${userPrompt(context)}`);
|
|
54
|
+
var splitIntoFileBlocks = (diff) => diff.split(/(?=^diff --git )/m).filter((part) => part.trim().length > 0);
|
|
55
|
+
var fileNameOf = (block) => {
|
|
56
|
+
const match = block.match(/^diff --git a\/(.+?) b\//m);
|
|
57
|
+
return match ? match[1] : "changes";
|
|
58
|
+
};
|
|
59
|
+
var truncateToBudget = (file, content, budget) => {
|
|
60
|
+
if (countTokens(content) <= budget) return content;
|
|
61
|
+
const marker = `\n... [truncated ${file} to fit context] ...\n`;
|
|
62
|
+
let keep = content.length;
|
|
63
|
+
let truncated = content;
|
|
64
|
+
while (countTokens(truncated) > budget && keep > 200) {
|
|
65
|
+
keep = Math.floor(keep * .7);
|
|
66
|
+
truncated = content.slice(0, keep) + marker;
|
|
67
|
+
}
|
|
68
|
+
return truncated;
|
|
69
|
+
};
|
|
70
|
+
var splitBlockByHunks = (file, content, budget) => {
|
|
71
|
+
const firstHunk = content.search(/^@@ /m);
|
|
72
|
+
if (firstHunk < 0) return [{
|
|
73
|
+
files: [file],
|
|
74
|
+
content: truncateToBudget(file, content, budget)
|
|
75
|
+
}];
|
|
76
|
+
const header = content.slice(0, firstHunk);
|
|
77
|
+
const hunks = content.slice(firstHunk).split(/(?=^@@ )/m).filter((h) => h.length > 0);
|
|
78
|
+
const chunks = [];
|
|
79
|
+
let current = "";
|
|
80
|
+
const flush = () => {
|
|
81
|
+
if (current) {
|
|
82
|
+
chunks.push({
|
|
83
|
+
files: [file],
|
|
84
|
+
content: truncateToBudget(file, header + current, budget)
|
|
85
|
+
});
|
|
86
|
+
current = "";
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
for (const hunk of hunks) {
|
|
90
|
+
if (current && countTokens(header + current + hunk) > budget) flush();
|
|
91
|
+
current += hunk;
|
|
92
|
+
}
|
|
93
|
+
flush();
|
|
94
|
+
return chunks;
|
|
95
|
+
};
|
|
96
|
+
var packChunks = (blocks, budget) => {
|
|
97
|
+
const chunks = [];
|
|
98
|
+
let files = [];
|
|
99
|
+
let content = "";
|
|
100
|
+
let tokens = 0;
|
|
101
|
+
const flush = () => {
|
|
102
|
+
if (content) {
|
|
103
|
+
chunks.push({
|
|
104
|
+
files,
|
|
105
|
+
content
|
|
106
|
+
});
|
|
107
|
+
files = [];
|
|
108
|
+
content = "";
|
|
109
|
+
tokens = 0;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
for (const block of blocks) {
|
|
113
|
+
if (block.tokens > budget) {
|
|
114
|
+
flush();
|
|
115
|
+
chunks.push(...splitBlockByHunks(block.file, block.content, budget));
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (content && tokens + block.tokens > budget) flush();
|
|
119
|
+
files.push(block.file);
|
|
120
|
+
content += block.content;
|
|
121
|
+
tokens += block.tokens;
|
|
122
|
+
}
|
|
123
|
+
flush();
|
|
124
|
+
return chunks;
|
|
125
|
+
};
|
|
126
|
+
var summarizeChunk = async (content) => {
|
|
127
|
+
const provider = getProvider();
|
|
128
|
+
return (await provider.complete({
|
|
129
|
+
system: summarySystemPrompt,
|
|
130
|
+
user: summaryUserPrompt(content),
|
|
131
|
+
maxTokens: provider.maxOutputTokens
|
|
132
|
+
})).trim();
|
|
133
|
+
};
|
|
134
|
+
var prepareCommitContext = async (diff) => {
|
|
135
|
+
const reserved = getProvider().maxOutputTokens;
|
|
136
|
+
const window = await getProvider().getContextWindow();
|
|
137
|
+
if (!Number.isFinite(window)) return diff;
|
|
138
|
+
const promptBudget = fitBudget(window, reserved);
|
|
139
|
+
if (finalPromptTokens(diff) <= promptBudget) return diff;
|
|
140
|
+
const spinner = ora({ text: "Analyzing diff size..." }).start();
|
|
141
|
+
try {
|
|
142
|
+
const blocks = splitIntoFileBlocks(diff).map((content) => ({
|
|
143
|
+
file: fileNameOf(content),
|
|
144
|
+
content,
|
|
145
|
+
tokens: countTokens(content)
|
|
146
|
+
}));
|
|
147
|
+
const totalTokens = blocks.reduce((sum, b) => sum + b.tokens, 0);
|
|
148
|
+
const sourceBlocks = blocks.filter((b) => !lowSignalNote(b.file));
|
|
149
|
+
const lowSignalLines = blocks.map((b) => {
|
|
150
|
+
const note = lowSignalNote(b.file);
|
|
151
|
+
return note ? `${b.file}: ${note}` : null;
|
|
152
|
+
}).filter((line) => line !== null);
|
|
153
|
+
const chunkBudget = fitBudget(window, reserved + countTokens(`${summarySystemPrompt}\n\n${summaryUserPrompt("")}`));
|
|
154
|
+
const chunks = packChunks(sourceBlocks, chunkBudget);
|
|
155
|
+
const condensedNote = lowSignalLines.length ? `, ${lowSignalLines.length} generated file(s) condensed` : "";
|
|
156
|
+
spinner.text = `Diff is large (~${totalTokens} tokens). Summarizing ${sourceBlocks.length} source file(s) in ${chunks.length} chunk(s)${condensedNote}...`;
|
|
157
|
+
const summaries = [];
|
|
158
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
159
|
+
const chunk = chunks[i];
|
|
160
|
+
spinner.text = `Summarizing to fit the context window (~${window} tokens) — chunk ${i + 1}/${chunks.length}...`;
|
|
161
|
+
summaries.push(await summarizeChunk(chunk.content));
|
|
162
|
+
}
|
|
163
|
+
const header = `The lines below are per-file summaries of a single commit. Treat them as one related change and write a commit message describing the overall change (not just one file):`;
|
|
164
|
+
let combined = [...summaries.filter(Boolean), ...lowSignalLines].join("\n");
|
|
165
|
+
const framed = () => `${header}\n${combined}`;
|
|
166
|
+
let pass = 0;
|
|
167
|
+
while (finalPromptTokens(framed()) > promptBudget && pass < MAX_REDUCE_PASSES) {
|
|
168
|
+
pass++;
|
|
169
|
+
const reduceChunks = packChunks(combined.split("\n").filter((line) => line.trim().length > 0).map((line) => ({
|
|
170
|
+
file: "summary",
|
|
171
|
+
content: `${line}\n`,
|
|
172
|
+
tokens: countTokens(line)
|
|
173
|
+
})), chunkBudget);
|
|
174
|
+
const reduced = [];
|
|
175
|
+
for (let i = 0; i < reduceChunks.length; i++) {
|
|
176
|
+
spinner.text = `Condensing summary (pass ${pass}, ${i + 1}/${reduceChunks.length})...`;
|
|
177
|
+
reduced.push(await summarizeChunk(reduceChunks[i].content));
|
|
178
|
+
}
|
|
179
|
+
combined = reduced.filter(Boolean).join("\n");
|
|
180
|
+
}
|
|
181
|
+
if (finalPromptTokens(framed()) > promptBudget) {
|
|
182
|
+
const headerTokens = countTokens(`${systemPrompt}\n\n${userPrompt(`${header}\n`)}`);
|
|
183
|
+
combined = truncateToBudget("summary", combined, promptBudget - headerTokens);
|
|
184
|
+
}
|
|
185
|
+
spinner.succeed(`Summarized ${blocks.length} file(s) to fit the context window: ~${totalTokens} → ~${countTokens(framed())} tokens`);
|
|
186
|
+
return framed();
|
|
187
|
+
} catch (error) {
|
|
188
|
+
spinner.fail("Failed to summarize diff");
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
//#endregion
|
|
193
|
+
export { prepareCommitContext };
|
package/dist/commands/config.js
CHANGED
|
@@ -1,11 +1,26 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { getConfig } from "../config.js";
|
|
1
|
+
import { getConfig, isDebug } from "../config.js";
|
|
3
2
|
import { maskApiKey } from "../utils/maskApiKey.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
//#region src/commands/config.ts
|
|
5
|
+
var DEBUG_ONLY = /* @__PURE__ */ new Set(["debug"]);
|
|
6
|
+
var configCommand = async () => {
|
|
7
|
+
console.log(chalk.blue("GitPT Configuration"));
|
|
8
|
+
const { apiKey, ...config } = getConfig();
|
|
9
|
+
const entries = {
|
|
10
|
+
apiKey: apiKey ? maskApiKey(apiKey) : void 0,
|
|
11
|
+
...config
|
|
12
|
+
};
|
|
13
|
+
const debug = isDebug();
|
|
14
|
+
const labelWidth = Math.max(...Object.keys(entries).map((key) => key.length));
|
|
15
|
+
for (const [key, value] of Object.entries(entries)) {
|
|
16
|
+
if (!debug && DEBUG_ONLY.has(key)) continue;
|
|
17
|
+
let display;
|
|
18
|
+
if (key === "apiKeys" && value && typeof value === "object") {
|
|
19
|
+
const masked = Object.entries(value).map(([provider, k]) => `${provider}: ${maskApiKey(k)}`);
|
|
20
|
+
display = masked.length ? masked.join(", ") : chalk.gray("—");
|
|
21
|
+
} else display = value === void 0 || value === "" ? chalk.gray("—") : String(value);
|
|
22
|
+
console.log(` ${chalk.cyan(key.padEnd(labelWidth))} ${display}`);
|
|
23
|
+
}
|
|
11
24
|
};
|
|
25
|
+
//#endregion
|
|
26
|
+
export { configCommand };
|