gitpt 1.6.1 → 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 -167
- package/dist/commands/commit/commitFlags.js +7 -0
- package/dist/commands/commit/context/buildPrompt.js +13 -10
- package/dist/commands/commit/context/summaryPrompt.js +5 -2
- package/dist/commands/commit/context/systemPrompt.js +4 -1
- package/dist/commands/commit/context/userPrompt.js +4 -1
- package/dist/commands/commit/generateCommitMessage.js +14 -17
- package/dist/commands/commit/index.js +186 -179
- package/dist/commands/commit/summarizeDiff.js +183 -162
- 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 +14 -10
- package/dist/commands/middleware/setupMiddleware/index.js +65 -71
- 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 +24 -23
- package/dist/commands/pr/getPRContext.js +37 -62
- package/dist/commands/pr/index.js +57 -62
- package/dist/commands/reset.js +23 -23
- package/dist/commands/review/index.js +31 -0
- package/dist/commands/setup.js +38 -13
- package/dist/config.js +63 -60
- package/dist/index.js +30 -67
- package/dist/llm/budget.js +10 -0
- package/dist/llm/index.js +12 -7
- package/dist/llm/providers/anthropic/index.js +30 -30
- package/dist/llm/providers/apiKey.js +36 -35
- package/dist/llm/providers/apple/client.js +73 -83
- package/dist/llm/providers/apple/index.js +64 -72
- package/dist/llm/providers/apple/models.js +27 -19
- package/dist/llm/providers/base.js +36 -36
- package/dist/llm/providers/local/index.js +79 -91
- package/dist/llm/providers/openai/index.js +19 -16
- package/dist/llm/providers/openaiCompatible.js +59 -62
- package/dist/llm/providers/openrouter/index.js +18 -15
- package/dist/llm/registry.js +30 -34
- package/dist/llm/setup/getAvailableModels.js +15 -9
- package/dist/llm/setup/selectModel.js +36 -44
- package/dist/llm/tokenCount.js +4 -3
- 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 +14 -4
- package/dist/commands/commit/context/buildPrompt.d.ts +0 -4
- package/dist/commands/commit/context/summaryPrompt.d.ts +0 -2
- 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/commit/summarizeDiff.d.ts +0 -1
- 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/defaultModels.d.ts +0 -8
- package/dist/commands/middleware/setupMiddleware/index.d.ts +0 -4
- 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/reset.d.ts +0 -3
- package/dist/commands/setup.d.ts +0 -3
- package/dist/config.d.ts +0 -14
- package/dist/index.d.ts +0 -2
- package/dist/llm/client.d.ts +0 -24
- package/dist/llm/client.js +0 -1
- package/dist/llm/index.d.ts +0 -6
- package/dist/llm/providers/anthropic/index.d.ts +0 -9
- package/dist/llm/providers/apiKey.d.ts +0 -3
- package/dist/llm/providers/apple/client.d.ts +0 -3
- package/dist/llm/providers/apple/index.d.ts +0 -13
- package/dist/llm/providers/apple/models.d.ts +0 -14
- package/dist/llm/providers/base.d.ts +0 -30
- package/dist/llm/providers/local/index.d.ts +0 -11
- package/dist/llm/providers/openai/index.d.ts +0 -10
- package/dist/llm/providers/openaiCompatible.d.ts +0 -15
- package/dist/llm/providers/openrouter/index.d.ts +0 -9
- package/dist/llm/registry.d.ts +0 -8
- package/dist/llm/setup/getAvailableModels.d.ts +0 -5
- package/dist/llm/setup/selectModel.d.ts +0 -5
- package/dist/llm/setup/types.d.ts +0 -13
- package/dist/llm/setup/types.js +0 -1
- package/dist/llm/tokenCount.d.ts +0 -3
- 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,172 +1,193 @@
|
|
|
1
|
-
import ora from "ora";
|
|
2
1
|
import { getProvider } from "../../llm/registry.js";
|
|
3
|
-
import { countTokens } from "../../llm/tokenCount.js";
|
|
4
|
-
import { summarySystemPrompt, summaryUserPrompt } from "./context/summaryPrompt.js";
|
|
5
2
|
import { systemPrompt } from "./context/systemPrompt.js";
|
|
6
3
|
import { userPrompt } from "./context/userPrompt.js";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
+
}
|
|
20
51
|
];
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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";
|
|
27
58
|
};
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return truncated;
|
|
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;
|
|
39
69
|
};
|
|
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
|
-
|
|
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;
|
|
65
95
|
};
|
|
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
|
-
|
|
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;
|
|
93
125
|
};
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
return message.trim();
|
|
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();
|
|
102
133
|
};
|
|
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
|
-
if (finalPromptTokens(framed()) > fitBudget) {
|
|
162
|
-
const headerTokens = countTokens(`${systemPrompt}\n\n${userPrompt(`${header}\n`)}`);
|
|
163
|
-
combined = truncateToBudget("summary", combined, fitBudget - headerTokens);
|
|
164
|
-
}
|
|
165
|
-
spinner.succeed(`Summarized ${blocks.length} file(s): ~${totalTokens} → ~${countTokens(framed())} tokens`);
|
|
166
|
-
return framed();
|
|
167
|
-
}
|
|
168
|
-
catch (error) {
|
|
169
|
-
spinner.fail("Failed to summarize diff");
|
|
170
|
-
throw error;
|
|
171
|
-
}
|
|
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
|
+
}
|
|
172
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 };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { git } from "../../services/git/index.js";
|
|
2
|
+
import { generateCommitMessage } from "../commit/generateCommitMessage.js";
|
|
3
|
+
import { prepareCommitContext } from "../commit/summarizeDiff.js";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import { spawnSync } from "child_process";
|
|
6
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
7
|
+
import { join } from "path";
|
|
8
|
+
//#region src/commands/hook/index.ts
|
|
9
|
+
var HOOK_MARKER = "gitpt-hook";
|
|
10
|
+
var HOOK_SCRIPT = `#!/bin/sh
|
|
11
|
+
# ${HOOK_MARKER} (prepare-commit-msg) — managed by GitPT. Remove: gitpt hook uninstall
|
|
12
|
+
command -v gitpt >/dev/null 2>&1 && gitpt hook run "$1" "$2" || true
|
|
13
|
+
`;
|
|
14
|
+
var resolveHooksDir = () => {
|
|
15
|
+
const result = spawnSync("git", [
|
|
16
|
+
"rev-parse",
|
|
17
|
+
"--git-path",
|
|
18
|
+
"hooks"
|
|
19
|
+
], { encoding: "utf-8" });
|
|
20
|
+
if (result.status !== 0) throw new Error("Not a git repository.");
|
|
21
|
+
return result.stdout.trim();
|
|
22
|
+
};
|
|
23
|
+
var hookInstallCommand = async (options = {}) => {
|
|
24
|
+
const hookPath = join(resolveHooksDir(), "prepare-commit-msg");
|
|
25
|
+
if (existsSync(hookPath)) {
|
|
26
|
+
if (!readFileSync(hookPath, "utf-8").includes(HOOK_MARKER) && !options.force) {
|
|
27
|
+
console.error(chalk.red(`A prepare-commit-msg hook already exists at ${hookPath}.`));
|
|
28
|
+
console.error(chalk.gray("Remove it, or re-run with --force, to install the GitPT hook."));
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
mkdirSync(join(hookPath, ".."), { recursive: true });
|
|
33
|
+
writeFileSync(hookPath, HOOK_SCRIPT, { mode: 493 });
|
|
34
|
+
console.log(chalk.green(`✓ Installed GitPT hook at ${hookPath}.`));
|
|
35
|
+
console.log(chalk.gray("`git commit` (without -m) now prefills an AI-generated message."));
|
|
36
|
+
};
|
|
37
|
+
var hookUninstallCommand = async () => {
|
|
38
|
+
const hookPath = join(resolveHooksDir(), "prepare-commit-msg");
|
|
39
|
+
if (!existsSync(hookPath)) {
|
|
40
|
+
console.log(chalk.gray("No prepare-commit-msg hook to remove."));
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (!readFileSync(hookPath, "utf-8").includes(HOOK_MARKER)) {
|
|
44
|
+
console.error(chalk.red("The prepare-commit-msg hook isn't managed by GitPT — leaving it untouched."));
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
rmSync(hookPath);
|
|
48
|
+
console.log(chalk.green("✓ Removed GitPT prepare-commit-msg hook."));
|
|
49
|
+
};
|
|
50
|
+
var SKIP_SOURCES = /* @__PURE__ */ new Set([
|
|
51
|
+
"message",
|
|
52
|
+
"merge",
|
|
53
|
+
"squash",
|
|
54
|
+
"commit"
|
|
55
|
+
]);
|
|
56
|
+
var hookRunCommand = async (msgFile, source) => {
|
|
57
|
+
try {
|
|
58
|
+
if (!msgFile) return;
|
|
59
|
+
if (source && SKIP_SOURCES.has(source)) return;
|
|
60
|
+
const diff = git.getStagedChanges();
|
|
61
|
+
if (!diff || !diff.trim()) return;
|
|
62
|
+
const message = (await generateCommitMessage(await prepareCommitContext(diff))).trim();
|
|
63
|
+
if (!message) return;
|
|
64
|
+
writeFileSync(msgFile, `${message}\n\n${existsSync(msgFile) ? readFileSync(msgFile, "utf-8") : ""}`);
|
|
65
|
+
} catch {}
|
|
66
|
+
};
|
|
67
|
+
//#endregion
|
|
68
|
+
export { hookInstallCommand, hookRunCommand, hookUninstallCommand };
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
+
import { gh } from "../../../services/gh/index.js";
|
|
1
2
|
import chalk from "chalk";
|
|
2
3
|
import { execSync } from "child_process";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
console.log(chalk.yellow("Warning: Could not verify GitHub CLI authentication."));
|
|
21
|
-
console.log(chalk.yellow("If PR creation fails, please run `gh auth login` first."));
|
|
22
|
-
}
|
|
4
|
+
//#region src/commands/middleware/capabilitiesMiddleware/ghCapability.ts
|
|
5
|
+
var ghCapability = () => {
|
|
6
|
+
if (!gh.isAvailable()) {
|
|
7
|
+
console.error(chalk.red("GitHub CLI (gh) is not installed or available in PATH"));
|
|
8
|
+
console.log(chalk.yellow("Please install GitHub CLI from https://cli.github.com/"));
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
try {
|
|
12
|
+
if (execSync("gh auth status -h github.com 2>&1 || true").toString().includes("not logged")) {
|
|
13
|
+
console.error(chalk.red("Error: You are not authenticated with GitHub CLI."));
|
|
14
|
+
console.log(chalk.yellow("Please run `gh auth login` to authenticate."));
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
} catch (error) {
|
|
18
|
+
console.log(chalk.yellow("Warning: Could not verify GitHub CLI authentication."));
|
|
19
|
+
console.log(chalk.yellow("If PR creation fails, please run `gh auth login` first."));
|
|
20
|
+
}
|
|
23
21
|
};
|
|
22
|
+
//#endregion
|
|
23
|
+
export { ghCapability };
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import chalk from "chalk";
|
|
2
1
|
import { git } from "../../../services/git/index.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
//#region src/commands/middleware/capabilitiesMiddleware/gitCapability.ts
|
|
4
|
+
var gitCapability = () => {
|
|
5
|
+
if (!git.isAvailable()) {
|
|
6
|
+
console.error(chalk.red("Git command is not available, install it from https://git-scm.com/"));
|
|
7
|
+
process.exit(1);
|
|
8
|
+
}
|
|
9
|
+
if (!git.isGitRepository()) {
|
|
10
|
+
console.error(chalk.red("Not a git repository"));
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
12
13
|
};
|
|
14
|
+
//#endregion
|
|
15
|
+
export { gitCapability };
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { ghCapability } from "./ghCapability.js";
|
|
2
2
|
import { gitCapability } from "./gitCapability.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
//#region src/commands/middleware/capabilitiesMiddleware/index.ts
|
|
4
|
+
var capabilities = {
|
|
5
|
+
git: gitCapability,
|
|
6
|
+
gh: ghCapability
|
|
6
7
|
};
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
resolver();
|
|
14
|
-
});
|
|
8
|
+
var capabilitiesMiddleware = (requiredCapabilities) => {
|
|
9
|
+
requiredCapabilities.forEach((capabilityKey) => {
|
|
10
|
+
const resolver = capabilities[capabilityKey];
|
|
11
|
+
if (!resolver) throw new Error(`Capability ${capabilityKey} not found`);
|
|
12
|
+
resolver();
|
|
13
|
+
});
|
|
15
14
|
};
|
|
15
|
+
//#endregion
|
|
16
|
+
export { capabilitiesMiddleware };
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import chalk from "chalk";
|
|
2
1
|
import { git } from "../../services/git/index.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
//#region src/commands/middleware/hasStagedChangesMiddleware.ts
|
|
4
|
+
var hasStagedChangesMiddleware = () => {
|
|
5
|
+
if (!git.hasStagedChanges()) {
|
|
6
|
+
console.error(chalk.red("Error: No staged changes"));
|
|
7
|
+
process.exit(1);
|
|
8
|
+
}
|
|
8
9
|
};
|
|
10
|
+
//#endregion
|
|
11
|
+
export { hasStagedChangesMiddleware };
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
import { AppleProvider } from "../../../llm/providers/apple/index.js";
|
|
2
1
|
import { isAppleModelAvailable } from "../../../llm/providers/apple/models.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
2
|
+
import { AppleProvider } from "../../../llm/providers/apple/index.js";
|
|
3
|
+
//#region src/commands/middleware/setupMiddleware/defaultModels.ts
|
|
4
|
+
var DEFAULT_MODELS = [{
|
|
5
|
+
id: "apple-foundation-models",
|
|
6
|
+
label: "Apple Foundation Models (on-device)",
|
|
7
|
+
isAvailable: () => AppleProvider.isAvailable() && isAppleModelAvailable("system"),
|
|
8
|
+
config: {
|
|
9
|
+
provider: "apple",
|
|
10
|
+
model: "system"
|
|
11
|
+
}
|
|
12
|
+
}];
|
|
13
|
+
var resolveDefaultModel = () => DEFAULT_MODELS.find((model) => model.isAvailable());
|
|
14
|
+
//#endregion
|
|
15
|
+
export { resolveDefaultModel };
|