@mcarvin/smart-diff 1.1.0 → 2.1.0
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 +123 -21
- package/dist/index.cjs +447 -134
- package/dist/index.cjs.map +1 -1
- package/dist/index.min.cjs +1 -1
- package/dist/index.min.cjs.map +1 -1
- package/dist/index.min.mjs +1 -1
- package/dist/index.min.mjs.map +1 -1
- package/dist/index.min.umd.js +1 -1
- package/dist/index.min.umd.js.map +1 -1
- package/dist/index.mjs +441 -131
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +450 -138
- package/dist/index.umd.js.map +1 -1
- package/dist/typings/ai/aiTypes.d.ts +5 -3
- package/dist/typings/ai/llmProviders.d.ts +12 -0
- package/dist/typings/git/diffShaping.d.ts +9 -0
- package/dist/typings/git/diffTypes.d.ts +2 -0
- package/dist/typings/git/gitDiff.d.ts +2 -0
- package/dist/typings/index.d.ts +14 -7
- package/package.json +34 -7
- package/dist/typings/ai/openAIConfig.d.ts +0 -21
package/dist/index.umd.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
(function (global, factory) {
|
|
2
|
-
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('node:path'), require('simple-git')) :
|
|
3
|
-
typeof define === 'function' && define.amd ? define(['exports', 'node:path', 'simple-git'], factory) :
|
|
4
|
-
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.jsTemplate = {}, global.node_path, global.simpleGit));
|
|
5
|
-
})(this, (function (exports, node_path, simpleGit) { 'use strict';
|
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('ai'), require('node:path'), require('simple-git')) :
|
|
3
|
+
typeof define === 'function' && define.amd ? define(['exports', 'ai', 'node:path', 'simple-git'], factory) :
|
|
4
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.jsTemplate = {}, global.ai, global.node_path, global.simpleGit));
|
|
5
|
+
})(this, (function (exports, ai, node_path, simpleGit) { 'use strict';
|
|
6
6
|
|
|
7
7
|
/******************************************************************************
|
|
8
8
|
Copyright (c) Microsoft Corporation.
|
|
@@ -36,9 +36,55 @@
|
|
|
36
36
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
+
const DEFAULT_LLM_MAX_DIFF_CHARS = 120000;
|
|
40
|
+
const DEFAULT_GIT_DIFF_SYSTEM_PROMPT = `You are a senior software engineer helping developers understand code and configuration changes from the git context they supplied.
|
|
41
|
+
You receive: commit subject lines (when available), changed file paths, and unified git patch(es)—either one range diff or concatenated per-commit patches, depending on how the diff was produced. Patches may be truncated mid-section with an explicit marker—do not infer changes beyond visible lines.
|
|
42
|
+
Explain what changed in terms of behavior, APIs, data, configuration, security, and operational risk. Tie claims to the patch when possible.
|
|
43
|
+
Produce a concise, developer-focused summary in Markdown.
|
|
44
|
+
Use sections that fit the change (for example: Highlights, Breaking or risky changes, API / contract changes, Data & schema, Configuration & infra, Security & auth, Tests & quality). Omit empty sections.
|
|
45
|
+
Group related changes; do not list every individual file. When multiple commits appear in the context, briefly separate notable themes by commit when helpful.
|
|
46
|
+
If the user message includes a Team line, use that exact team name in the summary title (for example: "## <Team> – Change summary" or similar).`;
|
|
47
|
+
const LLM_GATEWAY_REQUIRED_MESSAGE = "No LLM provider configured. Set LLM_PROVIDER (openai | openai-compatible | anthropic | google | bedrock | mistral | cohere | groq | xai | deepseek), " +
|
|
48
|
+
"or a provider API key (OPENAI_API_KEY, LLM_API_KEY, ANTHROPIC_API_KEY, GOOGLE_GENERATIVE_AI_API_KEY, MISTRAL_API_KEY, COHERE_API_KEY, GROQ_API_KEY, XAI_API_KEY, DEEPSEEK_API_KEY), " +
|
|
49
|
+
"or LLM_BASE_URL / OPENAI_BASE_URL for an OpenAI-compatible gateway, " +
|
|
50
|
+
"or JSON in OPENAI_DEFAULT_HEADERS / LLM_DEFAULT_HEADERS. " +
|
|
51
|
+
"Alternatively pass llmModelProvider or openAiClientProvider to generateSummary or summarizeGitDiff.";
|
|
52
|
+
|
|
53
|
+
const DEFAULT_MODEL_BY_PROVIDER = {
|
|
54
|
+
openai: "gpt-4o-mini",
|
|
55
|
+
"openai-compatible": "gpt-4o-mini",
|
|
56
|
+
anthropic: "claude-3-5-haiku-latest",
|
|
57
|
+
google: "gemini-2.0-flash",
|
|
58
|
+
bedrock: "anthropic.claude-3-5-haiku-20241022-v1:0",
|
|
59
|
+
mistral: "mistral-small-latest",
|
|
60
|
+
cohere: "command-r-08-2024",
|
|
61
|
+
groq: "llama-3.1-8b-instant",
|
|
62
|
+
xai: "grok-2-latest",
|
|
63
|
+
deepseek: "deepseek-chat",
|
|
64
|
+
};
|
|
65
|
+
const VALID_PROVIDERS = new Set([
|
|
66
|
+
"openai",
|
|
67
|
+
"openai-compatible",
|
|
68
|
+
"anthropic",
|
|
69
|
+
"google",
|
|
70
|
+
"bedrock",
|
|
71
|
+
"mistral",
|
|
72
|
+
"cohere",
|
|
73
|
+
"groq",
|
|
74
|
+
"xai",
|
|
75
|
+
"deepseek",
|
|
76
|
+
]);
|
|
77
|
+
function readEnv(name) {
|
|
78
|
+
var _a;
|
|
79
|
+
const value = (_a = process.env[name]) === null || _a === void 0 ? void 0 : _a.trim();
|
|
80
|
+
return value && value.length > 0 ? value : undefined;
|
|
81
|
+
}
|
|
82
|
+
function isValidProviderId(value) {
|
|
83
|
+
return VALID_PROVIDERS.has(value);
|
|
84
|
+
}
|
|
39
85
|
function resolveLlmBaseUrl() {
|
|
40
|
-
var _a
|
|
41
|
-
return (
|
|
86
|
+
var _a;
|
|
87
|
+
return (_a = readEnv("LLM_BASE_URL")) !== null && _a !== void 0 ? _a : readEnv("OPENAI_BASE_URL");
|
|
42
88
|
}
|
|
43
89
|
function parseHeaderJsonObject(raw) {
|
|
44
90
|
const trimmed = raw === null || raw === void 0 ? void 0 : raw.trim();
|
|
@@ -69,87 +115,187 @@
|
|
|
69
115
|
const merged = Object.assign(Object.assign({}, base), override);
|
|
70
116
|
return Object.keys(merged).length > 0 ? merged : undefined;
|
|
71
117
|
}
|
|
72
|
-
function
|
|
73
|
-
return Object.keys(headers).find((k) => k.toLowerCase() === "authorization");
|
|
74
|
-
}
|
|
75
|
-
function stripBearerPrefix(value) {
|
|
118
|
+
function resolveOpenAiApiKey() {
|
|
76
119
|
var _a;
|
|
77
|
-
|
|
78
|
-
const match = /^Bearer\s+(\S+)/i.exec(trimmed);
|
|
79
|
-
return (_a = match === null || match === void 0 ? void 0 : match[1]) !== null && _a !== void 0 ? _a : trimmed;
|
|
80
|
-
}
|
|
81
|
-
function splitPromotableAuthorizationFromHeaders(headers) {
|
|
82
|
-
const authName = findAuthorizationHeaderName(headers);
|
|
83
|
-
if (!authName) {
|
|
84
|
-
return { defaultHeaders: headers };
|
|
85
|
-
}
|
|
86
|
-
const raw = headers[authName];
|
|
87
|
-
if (!raw) {
|
|
88
|
-
return { defaultHeaders: headers };
|
|
89
|
-
}
|
|
90
|
-
const token = stripBearerPrefix(raw);
|
|
91
|
-
const looksBearer = /^Bearer\s+\S+/i.test(raw.trim());
|
|
92
|
-
const looksOpenAiKey = /^sk-/i.test(token);
|
|
93
|
-
if (!looksBearer && !looksOpenAiKey) {
|
|
94
|
-
return { defaultHeaders: headers };
|
|
95
|
-
}
|
|
96
|
-
const next = Object.assign({}, headers);
|
|
97
|
-
delete next[authName];
|
|
98
|
-
return { defaultHeaders: next, apiKeyFromAuthHeader: token };
|
|
99
|
-
}
|
|
100
|
-
function shouldUseLlmGateway() {
|
|
101
|
-
var _a, _b, _c;
|
|
102
|
-
const apiKey = (_b = (_a = process.env.LLM_API_KEY) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : (_c = process.env.OPENAI_API_KEY) === null || _c === void 0 ? void 0 : _c.trim();
|
|
103
|
-
if (apiKey)
|
|
104
|
-
return true;
|
|
105
|
-
if (resolveLlmBaseUrl())
|
|
106
|
-
return true;
|
|
107
|
-
const jsonHeaders = parseLlmDefaultHeadersFromEnv();
|
|
108
|
-
if (jsonHeaders && Object.keys(jsonHeaders).length > 0)
|
|
109
|
-
return true;
|
|
110
|
-
return false;
|
|
120
|
+
return (_a = readEnv("LLM_API_KEY")) !== null && _a !== void 0 ? _a : readEnv("OPENAI_API_KEY");
|
|
111
121
|
}
|
|
112
|
-
function
|
|
113
|
-
var _a, _b
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
let defaultHeaders;
|
|
118
|
-
let apiKey = envApiKey;
|
|
119
|
-
if (apiKey.length === 0) {
|
|
120
|
-
const split = splitPromotableAuthorizationFromHeaders(mergedHeaders);
|
|
121
|
-
if (split.apiKeyFromAuthHeader) {
|
|
122
|
-
apiKey = split.apiKeyFromAuthHeader;
|
|
123
|
-
}
|
|
124
|
-
defaultHeaders =
|
|
125
|
-
Object.keys(split.defaultHeaders).length > 0
|
|
126
|
-
? split.defaultHeaders
|
|
127
|
-
: undefined;
|
|
122
|
+
function detectLlmProvider() {
|
|
123
|
+
var _a, _b;
|
|
124
|
+
const explicit = (_a = readEnv("LLM_PROVIDER")) === null || _a === void 0 ? void 0 : _a.toLowerCase();
|
|
125
|
+
if (explicit && isValidProviderId(explicit)) {
|
|
126
|
+
return explicit;
|
|
128
127
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
Object.keys(mergedHeaders).length > 0 ? mergedHeaders : undefined;
|
|
128
|
+
if (resolveLlmBaseUrl()) {
|
|
129
|
+
return "openai-compatible";
|
|
132
130
|
}
|
|
133
|
-
|
|
131
|
+
if (resolveOpenAiApiKey()) {
|
|
132
|
+
return "openai";
|
|
133
|
+
}
|
|
134
|
+
if (readEnv("ANTHROPIC_API_KEY"))
|
|
135
|
+
return "anthropic";
|
|
136
|
+
if ((_b = readEnv("GOOGLE_GENERATIVE_AI_API_KEY")) !== null && _b !== void 0 ? _b : readEnv("GOOGLE_API_KEY"))
|
|
137
|
+
return "google";
|
|
138
|
+
if (readEnv("MISTRAL_API_KEY"))
|
|
139
|
+
return "mistral";
|
|
140
|
+
if (readEnv("COHERE_API_KEY"))
|
|
141
|
+
return "cohere";
|
|
142
|
+
if (readEnv("GROQ_API_KEY"))
|
|
143
|
+
return "groq";
|
|
144
|
+
if (readEnv("XAI_API_KEY"))
|
|
145
|
+
return "xai";
|
|
146
|
+
if (readEnv("DEEPSEEK_API_KEY"))
|
|
147
|
+
return "deepseek";
|
|
148
|
+
if (parseLlmDefaultHeadersFromEnv())
|
|
149
|
+
return "openai";
|
|
150
|
+
return undefined;
|
|
151
|
+
}
|
|
152
|
+
function isLlmProviderConfigured() {
|
|
153
|
+
return detectLlmProvider() !== undefined;
|
|
134
154
|
}
|
|
135
|
-
function
|
|
155
|
+
function defaultModelForProvider(provider) {
|
|
156
|
+
return DEFAULT_MODEL_BY_PROVIDER[provider];
|
|
157
|
+
}
|
|
158
|
+
function createOpenAiModel(modelId) {
|
|
136
159
|
return __awaiter(this, void 0, void 0, function* () {
|
|
137
|
-
const {
|
|
138
|
-
|
|
160
|
+
const { createOpenAI } = yield import('@ai-sdk/openai');
|
|
161
|
+
const apiKey = resolveOpenAiApiKey();
|
|
162
|
+
const headers = parseLlmDefaultHeadersFromEnv();
|
|
163
|
+
const provider = createOpenAI(Object.assign(Object.assign({}, (apiKey ? { apiKey } : {})), (headers ? { headers } : {})));
|
|
164
|
+
return provider(modelId);
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
function createOpenAiCompatibleModel(modelId) {
|
|
168
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
169
|
+
var _a;
|
|
170
|
+
const { createOpenAICompatible } = yield import('@ai-sdk/openai-compatible');
|
|
171
|
+
const baseURL = resolveLlmBaseUrl();
|
|
172
|
+
if (!baseURL) {
|
|
173
|
+
throw new Error("openai-compatible provider requires LLM_BASE_URL or OPENAI_BASE_URL to be set.");
|
|
174
|
+
}
|
|
175
|
+
const apiKey = resolveOpenAiApiKey();
|
|
176
|
+
const headers = parseLlmDefaultHeadersFromEnv();
|
|
177
|
+
const provider = createOpenAICompatible(Object.assign(Object.assign({ name: (_a = readEnv("LLM_PROVIDER_NAME")) !== null && _a !== void 0 ? _a : "openai-compatible", baseURL }, (apiKey ? { apiKey } : {})), (headers ? { headers } : {})));
|
|
178
|
+
return provider(modelId);
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
function wrapMissingPeer(failure) {
|
|
182
|
+
const err = new Error(`Failed to load optional provider package "${failure.pkg}" for LLM_PROVIDER="${failure.provider}". ` +
|
|
183
|
+
`Install it with \`npm install ${failure.pkg}\`.`);
|
|
184
|
+
err.cause = failure.cause;
|
|
185
|
+
return err;
|
|
186
|
+
}
|
|
187
|
+
function importOptional(provider, pkg, loader) {
|
|
188
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
189
|
+
try {
|
|
190
|
+
return yield loader();
|
|
191
|
+
}
|
|
192
|
+
catch (cause) {
|
|
193
|
+
throw wrapMissingPeer({ provider, pkg, cause });
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
function createAnthropicModel(modelId) {
|
|
198
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
199
|
+
const mod = yield importOptional("anthropic", "@ai-sdk/anthropic", () => import('@ai-sdk/anthropic'));
|
|
200
|
+
const apiKey = readEnv("ANTHROPIC_API_KEY");
|
|
201
|
+
const provider = mod.createAnthropic(apiKey ? { apiKey } : undefined);
|
|
202
|
+
return provider(modelId);
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
function createGoogleModel(modelId) {
|
|
206
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
207
|
+
var _a;
|
|
208
|
+
const mod = yield importOptional("google", "@ai-sdk/google", () => import('@ai-sdk/google'));
|
|
209
|
+
const apiKey = (_a = readEnv("GOOGLE_GENERATIVE_AI_API_KEY")) !== null && _a !== void 0 ? _a : readEnv("GOOGLE_API_KEY");
|
|
210
|
+
const provider = mod.createGoogleGenerativeAI(apiKey ? { apiKey } : undefined);
|
|
211
|
+
return provider(modelId);
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
function createBedrockModel(modelId) {
|
|
215
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
216
|
+
const mod = yield importOptional("bedrock", "@ai-sdk/amazon-bedrock", () => import('@ai-sdk/amazon-bedrock'));
|
|
217
|
+
const provider = mod.createAmazonBedrock();
|
|
218
|
+
return provider(modelId);
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
function createMistralModel(modelId) {
|
|
222
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
223
|
+
const mod = yield importOptional("mistral", "@ai-sdk/mistral", () => import('@ai-sdk/mistral'));
|
|
224
|
+
const apiKey = readEnv("MISTRAL_API_KEY");
|
|
225
|
+
const provider = mod.createMistral(apiKey ? { apiKey } : undefined);
|
|
226
|
+
return provider(modelId);
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
function createCohereModel(modelId) {
|
|
230
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
231
|
+
const mod = yield importOptional("cohere", "@ai-sdk/cohere", () => import('@ai-sdk/cohere'));
|
|
232
|
+
const apiKey = readEnv("COHERE_API_KEY");
|
|
233
|
+
const provider = mod.createCohere(apiKey ? { apiKey } : undefined);
|
|
234
|
+
return provider(modelId);
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
function createGroqModel(modelId) {
|
|
238
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
239
|
+
const mod = yield importOptional("groq", "@ai-sdk/groq", () => import('@ai-sdk/groq'));
|
|
240
|
+
const apiKey = readEnv("GROQ_API_KEY");
|
|
241
|
+
const provider = mod.createGroq(apiKey ? { apiKey } : undefined);
|
|
242
|
+
return provider(modelId);
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
function createXaiModel(modelId) {
|
|
246
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
247
|
+
const mod = yield importOptional("xai", "@ai-sdk/xai", () => import('@ai-sdk/xai'));
|
|
248
|
+
const apiKey = readEnv("XAI_API_KEY");
|
|
249
|
+
const provider = mod.createXai(apiKey ? { apiKey } : undefined);
|
|
250
|
+
return provider(modelId);
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
function createDeepseekModel(modelId) {
|
|
254
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
255
|
+
const mod = yield importOptional("deepseek", "@ai-sdk/deepseek", () => import('@ai-sdk/deepseek'));
|
|
256
|
+
const apiKey = readEnv("DEEPSEEK_API_KEY");
|
|
257
|
+
const provider = mod.createDeepSeek(apiKey ? { apiKey } : undefined);
|
|
258
|
+
return provider(modelId);
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
function resolveLanguageModel() {
|
|
262
|
+
return __awaiter(this, arguments, void 0, function* (options = {}) {
|
|
263
|
+
var _a, _b, _c;
|
|
264
|
+
const provider = (_a = options.provider) !== null && _a !== void 0 ? _a : detectLlmProvider();
|
|
265
|
+
if (!provider) {
|
|
266
|
+
throw new Error("No LLM provider could be resolved. Set LLM_PROVIDER or a provider API key " +
|
|
267
|
+
"(OPENAI_API_KEY, ANTHROPIC_API_KEY, GOOGLE_GENERATIVE_AI_API_KEY, MISTRAL_API_KEY, " +
|
|
268
|
+
"COHERE_API_KEY, GROQ_API_KEY, XAI_API_KEY, DEEPSEEK_API_KEY), or LLM_BASE_URL for an OpenAI-compatible gateway.");
|
|
269
|
+
}
|
|
270
|
+
const modelId = (_c = (_b = options.model) !== null && _b !== void 0 ? _b : readEnv("LLM_MODEL")) !== null && _c !== void 0 ? _c : defaultModelForProvider(provider);
|
|
271
|
+
switch (provider) {
|
|
272
|
+
case "openai":
|
|
273
|
+
return createOpenAiModel(modelId);
|
|
274
|
+
case "openai-compatible":
|
|
275
|
+
return createOpenAiCompatibleModel(modelId);
|
|
276
|
+
case "anthropic":
|
|
277
|
+
return createAnthropicModel(modelId);
|
|
278
|
+
case "google":
|
|
279
|
+
return createGoogleModel(modelId);
|
|
280
|
+
case "bedrock":
|
|
281
|
+
return createBedrockModel(modelId);
|
|
282
|
+
case "mistral":
|
|
283
|
+
return createMistralModel(modelId);
|
|
284
|
+
case "cohere":
|
|
285
|
+
return createCohereModel(modelId);
|
|
286
|
+
case "groq":
|
|
287
|
+
return createGroqModel(modelId);
|
|
288
|
+
case "xai":
|
|
289
|
+
return createXaiModel(modelId);
|
|
290
|
+
case "deepseek":
|
|
291
|
+
return createDeepseekModel(modelId);
|
|
292
|
+
default: {
|
|
293
|
+
const _exhaustive = provider;
|
|
294
|
+
throw new Error(`Unhandled LLM provider: ${String(_exhaustive)}`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
139
297
|
});
|
|
140
298
|
}
|
|
141
|
-
|
|
142
|
-
const DEFAULT_LLM_MAX_DIFF_CHARS = 120000;
|
|
143
|
-
const DEFAULT_GIT_DIFF_SYSTEM_PROMPT = `You are a senior software engineer helping developers understand code and configuration changes from the git context they supplied.
|
|
144
|
-
You receive: commit subject lines (when available), changed file paths, and unified git patch(es)—either one range diff or concatenated per-commit patches, depending on how the diff was produced. Patches may be truncated mid-section with an explicit marker—do not infer changes beyond visible lines.
|
|
145
|
-
Explain what changed in terms of behavior, APIs, data, configuration, security, and operational risk. Tie claims to the patch when possible.
|
|
146
|
-
Produce a concise, developer-focused summary in Markdown.
|
|
147
|
-
Use sections that fit the change (for example: Highlights, Breaking or risky changes, API / contract changes, Data & schema, Configuration & infra, Security & auth, Tests & quality). Omit empty sections.
|
|
148
|
-
Group related changes; do not list every individual file. When multiple commits appear in the context, briefly separate notable themes by commit when helpful.
|
|
149
|
-
If the user message includes a Team line, use that exact team name in the summary title (for example: "## <Team> – Change summary" or similar).`;
|
|
150
|
-
const LLM_GATEWAY_REQUIRED_MESSAGE = "No LLM gateway configured. Set OPENAI_API_KEY or LLM_API_KEY, and/or LLM_BASE_URL or OPENAI_BASE_URL, " +
|
|
151
|
-
"and/or JSON in OPENAI_DEFAULT_HEADERS or LLM_DEFAULT_HEADERS. " +
|
|
152
|
-
"Alternatively pass openAiClientProvider to generateSummary or summarizeGitDiff.";
|
|
153
299
|
|
|
154
300
|
function resolveLlmMaxDiffChars(cliOverride) {
|
|
155
301
|
var _a;
|
|
@@ -177,18 +323,26 @@ If the user message includes a Team line, use that exact team name in the summar
|
|
|
177
323
|
function markdownDiffTruncationNotice(originalChars, maxChars) {
|
|
178
324
|
return `> **Truncated diff:** The unified diff was ${originalChars} characters; only the first ${maxChars} were sent to the model. The summary may not reflect the full change set. Narrow the ref range, adjust path filters, or raise \`maxDiffChars\` / \`LLM_MAX_DIFF_CHARS\`—often together with switching to a model whose context window can fit a larger prompt.\n\n`;
|
|
179
325
|
}
|
|
326
|
+
function resolveMaxOutputTokens() {
|
|
327
|
+
var _a;
|
|
328
|
+
const raw = (_a = process.env.LLM_MAX_TOKENS) !== null && _a !== void 0 ? _a : process.env.OPENAI_MAX_TOKENS;
|
|
329
|
+
const parsed = raw !== undefined ? Number.parseInt(raw, 10) : 4000;
|
|
330
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : 4000;
|
|
331
|
+
}
|
|
180
332
|
function generateSummary(input) {
|
|
181
333
|
return __awaiter(this, void 0, void 0, function* () {
|
|
182
|
-
var _a
|
|
183
|
-
const { diffText, fileNames, commits, flags,
|
|
184
|
-
if (!
|
|
334
|
+
var _a;
|
|
335
|
+
const { diffText, fileNames, commits, flags, llmModelProvider, diffSummary, } = input;
|
|
336
|
+
if (!llmModelProvider && !isLlmProviderConfigured()) {
|
|
185
337
|
throw new Error(LLM_GATEWAY_REQUIRED_MESSAGE);
|
|
186
338
|
}
|
|
187
339
|
const maxDiffChars = resolveLlmMaxDiffChars(flags.maxDiffChars);
|
|
188
340
|
const diffTruncated = diffText.length > maxDiffChars;
|
|
189
341
|
const diffForLlm = truncateUnifiedDiffForLlm(diffText, maxDiffChars);
|
|
190
|
-
const userContent =
|
|
191
|
-
const
|
|
342
|
+
const userContent = buildUserContent(flags, commits, fileNames, diffForLlm, diffSummary);
|
|
343
|
+
const systemPrompt = (_a = flags.systemPrompt) !== null && _a !== void 0 ? _a : DEFAULT_GIT_DIFF_SYSTEM_PROMPT;
|
|
344
|
+
const maxOutputTokens = resolveMaxOutputTokens();
|
|
345
|
+
const summary = yield callLlm(userContent, systemPrompt, maxOutputTokens, llmModelProvider, flags);
|
|
192
346
|
if (!diffTruncated) {
|
|
193
347
|
return summary;
|
|
194
348
|
}
|
|
@@ -215,7 +369,7 @@ If the user message includes a Team line, use that exact team name in the summar
|
|
|
215
369
|
return (`${incLine}${excLine}` +
|
|
216
370
|
"Git context shape: concatenated per-commit unified patches for commits that pass the message filters.\n");
|
|
217
371
|
}
|
|
218
|
-
function
|
|
372
|
+
function buildUserContent(flags, commits, fileNames, diffText, diffSummary) {
|
|
219
373
|
var _a, _b;
|
|
220
374
|
const from = flags.from;
|
|
221
375
|
const to = (_a = flags.to) !== null && _a !== void 0 ? _a : "HEAD";
|
|
@@ -245,31 +399,20 @@ If the user message includes a Team line, use that exact team name in the summar
|
|
|
245
399
|
"=== Git context (unified diff(s); patches may be truncated with an explicit marker) ===\n" +
|
|
246
400
|
diffText);
|
|
247
401
|
}
|
|
248
|
-
function
|
|
402
|
+
function callLlm(userContent, systemPrompt, maxOutputTokens, llmModelProvider, flags) {
|
|
249
403
|
return __awaiter(this, void 0, void 0, function* () {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
const
|
|
254
|
-
const maxTokens = Number.isFinite(parsed) && parsed > 0 ? parsed : 4000;
|
|
255
|
-
const response = yield client.chat.completions.create({
|
|
404
|
+
const model = llmModelProvider
|
|
405
|
+
? yield llmModelProvider()
|
|
406
|
+
: yield resolveLanguageModel(Object.assign(Object.assign({}, (flags.provider ? { provider: flags.provider } : {})), (flags.model ? { model: flags.model } : {})));
|
|
407
|
+
const result = yield ai.generateText({
|
|
256
408
|
model,
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
role: "system",
|
|
260
|
-
content: systemPrompt,
|
|
261
|
-
},
|
|
262
|
-
{
|
|
263
|
-
role: "user",
|
|
264
|
-
content: userContent,
|
|
265
|
-
},
|
|
266
|
-
],
|
|
409
|
+
system: systemPrompt,
|
|
410
|
+
prompt: userContent,
|
|
267
411
|
temperature: 0.2,
|
|
268
|
-
|
|
412
|
+
maxOutputTokens,
|
|
269
413
|
});
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
return text.length > 0 ? text : "No summary generated by OpenAI.";
|
|
414
|
+
const text = result.text.trim();
|
|
415
|
+
return text.length > 0 ? text : "No summary generated by the model.";
|
|
273
416
|
});
|
|
274
417
|
}
|
|
275
418
|
|
|
@@ -351,6 +494,130 @@ If the user message includes a Team line, use that exact team name in the summar
|
|
|
351
494
|
return commits.filter((c) => commitMessagePassesFilters(c.message, includeRes, excludeRes));
|
|
352
495
|
}
|
|
353
496
|
|
|
497
|
+
const DEFAULT_NOISE_EXCLUDES = [
|
|
498
|
+
"package-lock.json",
|
|
499
|
+
"yarn.lock",
|
|
500
|
+
"pnpm-lock.yaml",
|
|
501
|
+
"npm-shrinkwrap.json",
|
|
502
|
+
"bun.lockb",
|
|
503
|
+
"go.sum",
|
|
504
|
+
"Cargo.lock",
|
|
505
|
+
"Gemfile.lock",
|
|
506
|
+
"composer.lock",
|
|
507
|
+
"Pipfile.lock",
|
|
508
|
+
"poetry.lock",
|
|
509
|
+
"uv.lock",
|
|
510
|
+
"Podfile.lock",
|
|
511
|
+
"node_modules",
|
|
512
|
+
"dist",
|
|
513
|
+
"build",
|
|
514
|
+
"out",
|
|
515
|
+
"coverage",
|
|
516
|
+
"__snapshots__",
|
|
517
|
+
];
|
|
518
|
+
function normalizeContextLines(raw) {
|
|
519
|
+
if (!Number.isFinite(raw) || raw < 0)
|
|
520
|
+
return 0;
|
|
521
|
+
return Math.trunc(raw);
|
|
522
|
+
}
|
|
523
|
+
function buildDiffShapingGitArgs(shaping) {
|
|
524
|
+
const args = [];
|
|
525
|
+
if ((shaping === null || shaping === void 0 ? void 0 : shaping.contextLines) !== undefined) {
|
|
526
|
+
args.push(`-U${normalizeContextLines(shaping.contextLines)}`);
|
|
527
|
+
}
|
|
528
|
+
if (shaping === null || shaping === void 0 ? void 0 : shaping.ignoreWhitespace) {
|
|
529
|
+
args.push("-w");
|
|
530
|
+
}
|
|
531
|
+
return args;
|
|
532
|
+
}
|
|
533
|
+
const PREAMBLE_NOISE_PREFIXES = [
|
|
534
|
+
"diff --git ",
|
|
535
|
+
"index ",
|
|
536
|
+
"new file mode ",
|
|
537
|
+
"deleted file mode ",
|
|
538
|
+
"old mode ",
|
|
539
|
+
"new mode ",
|
|
540
|
+
"similarity index ",
|
|
541
|
+
"dissimilarity index ",
|
|
542
|
+
"rename from ",
|
|
543
|
+
"rename to ",
|
|
544
|
+
"copy from ",
|
|
545
|
+
"copy to ",
|
|
546
|
+
];
|
|
547
|
+
function isPreambleNoiseLine(line) {
|
|
548
|
+
for (const prefix of PREAMBLE_NOISE_PREFIXES) {
|
|
549
|
+
if (line.startsWith(prefix))
|
|
550
|
+
return true;
|
|
551
|
+
}
|
|
552
|
+
return false;
|
|
553
|
+
}
|
|
554
|
+
function stripPreambleLines(text) {
|
|
555
|
+
return text
|
|
556
|
+
.split(/\r?\n/)
|
|
557
|
+
.filter((line) => !isPreambleNoiseLine(line))
|
|
558
|
+
.join("\n");
|
|
559
|
+
}
|
|
560
|
+
function isFileHeaderLine(line) {
|
|
561
|
+
return (/^--- (a\/|b\/|"a\/|"b\/|\/dev\/null)/.test(line) ||
|
|
562
|
+
/^\+\+\+ (a\/|b\/|"a\/|"b\/|\/dev\/null)/.test(line));
|
|
563
|
+
}
|
|
564
|
+
function elideLargeHunks(text, maxHunkLines) {
|
|
565
|
+
const limit = normalizeContextLines(maxHunkLines);
|
|
566
|
+
const lines = text.split(/\r?\n/);
|
|
567
|
+
const out = [];
|
|
568
|
+
let inHunk = false;
|
|
569
|
+
let hunkBuf = [];
|
|
570
|
+
const flushHunk = () => {
|
|
571
|
+
if (hunkBuf.length > limit) {
|
|
572
|
+
const elided = hunkBuf.length - limit;
|
|
573
|
+
out.push(...hunkBuf.slice(0, limit));
|
|
574
|
+
out.push(`[... ${elided} diff line${elided === 1 ? "" : "s"} elided ...]`);
|
|
575
|
+
}
|
|
576
|
+
else {
|
|
577
|
+
out.push(...hunkBuf);
|
|
578
|
+
}
|
|
579
|
+
hunkBuf = [];
|
|
580
|
+
inHunk = false;
|
|
581
|
+
};
|
|
582
|
+
for (const line of lines) {
|
|
583
|
+
if (line.startsWith("@@")) {
|
|
584
|
+
if (inHunk)
|
|
585
|
+
flushHunk();
|
|
586
|
+
out.push(line);
|
|
587
|
+
inHunk = true;
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
590
|
+
if (line.startsWith("diff --git ") || isFileHeaderLine(line)) {
|
|
591
|
+
if (inHunk)
|
|
592
|
+
flushHunk();
|
|
593
|
+
out.push(line);
|
|
594
|
+
continue;
|
|
595
|
+
}
|
|
596
|
+
if (inHunk) {
|
|
597
|
+
hunkBuf.push(line);
|
|
598
|
+
}
|
|
599
|
+
else {
|
|
600
|
+
out.push(line);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
if (inHunk)
|
|
604
|
+
flushHunk();
|
|
605
|
+
return out.join("\n");
|
|
606
|
+
}
|
|
607
|
+
function shapeUnifiedDiff(text, shaping) {
|
|
608
|
+
if (!(shaping === null || shaping === void 0 ? void 0 : shaping.stripDiffPreamble) && (shaping === null || shaping === void 0 ? void 0 : shaping.maxHunkLines) === undefined) {
|
|
609
|
+
return text;
|
|
610
|
+
}
|
|
611
|
+
let out = text;
|
|
612
|
+
if (shaping.stripDiffPreamble) {
|
|
613
|
+
out = stripPreambleLines(out);
|
|
614
|
+
}
|
|
615
|
+
if (shaping.maxHunkLines !== undefined) {
|
|
616
|
+
out = elideLargeHunks(out, shaping.maxHunkLines);
|
|
617
|
+
}
|
|
618
|
+
return out;
|
|
619
|
+
}
|
|
620
|
+
|
|
354
621
|
const GIT_STATUS_BY_FIRST_CHAR = {
|
|
355
622
|
A: "added",
|
|
356
623
|
D: "deleted",
|
|
@@ -381,25 +648,17 @@ If the user message includes a Team line, use that exact team name in the summar
|
|
|
381
648
|
}
|
|
382
649
|
|
|
383
650
|
function parseNameStatusLine(line) {
|
|
384
|
-
var _a;
|
|
385
651
|
const parts = line.split("\t");
|
|
386
652
|
let entry = null;
|
|
387
653
|
if (parts.length >= 2) {
|
|
388
|
-
const statusToken =
|
|
654
|
+
const statusToken = parts[0];
|
|
389
655
|
const status = mapGitStatus(statusToken);
|
|
390
656
|
const isRenameOrCopy = statusToken.startsWith("R") || statusToken.startsWith("C");
|
|
391
657
|
if (isRenameOrCopy && parts.length >= 3) {
|
|
392
|
-
|
|
393
|
-
const newPath = parts[2];
|
|
394
|
-
if (oldPath !== undefined && newPath !== undefined) {
|
|
395
|
-
entry = { path: newPath, status, oldPath };
|
|
396
|
-
}
|
|
658
|
+
entry = { path: parts[2], status, oldPath: parts[1] };
|
|
397
659
|
}
|
|
398
660
|
else if (!isRenameOrCopy) {
|
|
399
|
-
|
|
400
|
-
if (pathOnly !== undefined) {
|
|
401
|
-
entry = { path: pathOnly, status };
|
|
402
|
-
}
|
|
661
|
+
entry = { path: parts[1], status };
|
|
403
662
|
}
|
|
404
663
|
}
|
|
405
664
|
return entry;
|
|
@@ -444,12 +703,11 @@ If the user message includes a Team line, use that exact team name in the summar
|
|
|
444
703
|
return `${dirRaw}${toSeg}`;
|
|
445
704
|
}
|
|
446
705
|
function parseNumStatLine(line) {
|
|
447
|
-
var _a, _b;
|
|
448
706
|
const parts = line.split("\t");
|
|
449
707
|
if (parts.length < 3)
|
|
450
708
|
return null;
|
|
451
|
-
const addStr =
|
|
452
|
-
const delStr =
|
|
709
|
+
const addStr = parts[0];
|
|
710
|
+
const delStr = parts[1];
|
|
453
711
|
const pathField = parts.slice(2).join("\t");
|
|
454
712
|
const additions = addStr !== "-" ? Number.parseInt(addStr, 10) || 0 : 0;
|
|
455
713
|
const deletions = delStr !== "-" ? Number.parseInt(delStr, 10) || 0 : 0;
|
|
@@ -474,11 +732,10 @@ If the user message includes a Team line, use that exact team name in the summar
|
|
|
474
732
|
}
|
|
475
733
|
|
|
476
734
|
function parseTabDiffSummaryLine(line) {
|
|
477
|
-
var _a;
|
|
478
735
|
const parts = line.split("\t");
|
|
479
736
|
if (parts.length < 3)
|
|
480
737
|
return null;
|
|
481
|
-
const statusToken =
|
|
738
|
+
const statusToken = parts.shift();
|
|
482
739
|
const status = mapGitStatus(statusToken);
|
|
483
740
|
const add0 = parts[0];
|
|
484
741
|
const del0 = parts[1];
|
|
@@ -596,31 +853,48 @@ If the user message includes a Team line, use that exact team name in the summar
|
|
|
596
853
|
}
|
|
597
854
|
function getDiff(git, query) {
|
|
598
855
|
return __awaiter(this, void 0, void 0, function* () {
|
|
599
|
-
const { from, to, commits, filterByCommits, pathFilter, repoRootOverride } = query;
|
|
856
|
+
const { from, to, commits, filterByCommits, pathFilter, repoRootOverride, shaping, } = query;
|
|
600
857
|
const { specs } = yield getDiffPathContext(git, pathFilter, repoRootOverride);
|
|
858
|
+
const shapingArgs = buildDiffShapingGitArgs(shaping);
|
|
601
859
|
if (!filterByCommits) {
|
|
602
|
-
|
|
860
|
+
const raw = yield git.diff([
|
|
861
|
+
...shapingArgs,
|
|
862
|
+
`${from}..${to}`,
|
|
863
|
+
"--",
|
|
864
|
+
...specs,
|
|
865
|
+
]);
|
|
866
|
+
return shapeUnifiedDiff(raw, shaping);
|
|
603
867
|
}
|
|
604
|
-
const patches = yield Promise.all(commits.map((c) => git.diff([`${c.hash}^!`, "--", ...specs])));
|
|
605
|
-
return patches
|
|
868
|
+
const patches = yield Promise.all(commits.map((c) => git.diff([...shapingArgs, `${c.hash}^!`, "--", ...specs])));
|
|
869
|
+
return patches
|
|
870
|
+
.map((p) => shapeUnifiedDiff(p, shaping))
|
|
871
|
+
.filter(Boolean)
|
|
872
|
+
.join("\n");
|
|
606
873
|
});
|
|
607
874
|
}
|
|
608
875
|
function getDiffSummary(git, query) {
|
|
609
876
|
return __awaiter(this, void 0, void 0, function* () {
|
|
610
|
-
const { from, to, commits, filterByCommits, pathFilter, repoRootOverride } = query;
|
|
877
|
+
const { from, to, commits, filterByCommits, pathFilter, repoRootOverride, shaping, } = query;
|
|
611
878
|
const { specs } = yield getDiffPathContext(git, pathFilter, repoRootOverride);
|
|
879
|
+
const whitespaceArgs = (shaping === null || shaping === void 0 ? void 0 : shaping.ignoreWhitespace) ? ["-w"] : [];
|
|
612
880
|
if (!filterByCommits) {
|
|
613
881
|
const [numOutput, nameOutput] = yield Promise.all([
|
|
614
|
-
git.diff(["--numstat", `${from}..${to}`, "--", ...specs]),
|
|
615
|
-
git.diff([
|
|
882
|
+
git.diff([...whitespaceArgs, "--numstat", `${from}..${to}`, "--", ...specs]),
|
|
883
|
+
git.diff([
|
|
884
|
+
...whitespaceArgs,
|
|
885
|
+
"--name-status",
|
|
886
|
+
`${from}..${to}`,
|
|
887
|
+
"--",
|
|
888
|
+
...specs,
|
|
889
|
+
]),
|
|
616
890
|
]);
|
|
617
891
|
return buildDiffSummaryFromGitOutputs(nameOutput, numOutput);
|
|
618
892
|
}
|
|
619
893
|
const pairs = yield Promise.all(commits.map((c) => __awaiter(this, void 0, void 0, function* () {
|
|
620
894
|
const range = `${c.hash}^!`;
|
|
621
895
|
const [numOutput, nameOutput] = yield Promise.all([
|
|
622
|
-
git.diff(["--numstat", range, "--", ...specs]),
|
|
623
|
-
git.diff(["--name-status", range, "--", ...specs]),
|
|
896
|
+
git.diff([...whitespaceArgs, "--numstat", range, "--", ...specs]),
|
|
897
|
+
git.diff([...whitespaceArgs, "--name-status", range, "--", ...specs]),
|
|
624
898
|
]);
|
|
625
899
|
return { numOutput, nameOutput };
|
|
626
900
|
})));
|
|
@@ -670,6 +944,37 @@ If the user message includes a Team line, use that exact team name in the summar
|
|
|
670
944
|
});
|
|
671
945
|
}
|
|
672
946
|
|
|
947
|
+
function buildShapingFromOptions(options) {
|
|
948
|
+
const shaping = {};
|
|
949
|
+
if (options.contextLines !== undefined) {
|
|
950
|
+
shaping.contextLines = options.contextLines;
|
|
951
|
+
}
|
|
952
|
+
if (options.ignoreWhitespace)
|
|
953
|
+
shaping.ignoreWhitespace = true;
|
|
954
|
+
if (options.stripDiffPreamble)
|
|
955
|
+
shaping.stripDiffPreamble = true;
|
|
956
|
+
if (options.maxHunkLines !== undefined) {
|
|
957
|
+
shaping.maxHunkLines = options.maxHunkLines;
|
|
958
|
+
}
|
|
959
|
+
return Object.keys(shaping).length > 0 ? shaping : undefined;
|
|
960
|
+
}
|
|
961
|
+
function buildEffectiveExcludeFolders(options) {
|
|
962
|
+
var _a;
|
|
963
|
+
const userExcludes = (_a = options.excludeFolders) !== null && _a !== void 0 ? _a : [];
|
|
964
|
+
if (!options.excludeDefaultNoise) {
|
|
965
|
+
return userExcludes.length > 0 ? userExcludes : undefined;
|
|
966
|
+
}
|
|
967
|
+
const seen = new Set();
|
|
968
|
+
const merged = [];
|
|
969
|
+
for (const p of [...DEFAULT_NOISE_EXCLUDES, ...userExcludes]) {
|
|
970
|
+
const key = p.trim();
|
|
971
|
+
if (!key || seen.has(key))
|
|
972
|
+
continue;
|
|
973
|
+
seen.add(key);
|
|
974
|
+
merged.push(p);
|
|
975
|
+
}
|
|
976
|
+
return merged;
|
|
977
|
+
}
|
|
673
978
|
function hasNonEmptyTrimmed(arr) {
|
|
674
979
|
return (arr !== null && arr !== void 0 ? arr : []).some((s) => s.trim().length > 0);
|
|
675
980
|
}
|
|
@@ -686,22 +991,25 @@ If the user message includes a Team line, use that exact team name in the summar
|
|
|
686
991
|
const git = (_a = options.git) !== null && _a !== void 0 ? _a : createGitClient(options.cwd);
|
|
687
992
|
const from = options.from;
|
|
688
993
|
const to = (_b = options.to) !== null && _b !== void 0 ? _b : "HEAD";
|
|
994
|
+
const effectiveExcludeFolders = buildEffectiveExcludeFolders(options);
|
|
689
995
|
const pathFilter = hasNonEmptyTrimmed(options.includeFolders) ||
|
|
690
|
-
hasNonEmptyTrimmed(
|
|
996
|
+
hasNonEmptyTrimmed(effectiveExcludeFolders)
|
|
691
997
|
? {
|
|
692
998
|
includeFolders: options.includeFolders,
|
|
693
|
-
excludeFolders:
|
|
999
|
+
excludeFolders: effectiveExcludeFolders,
|
|
694
1000
|
}
|
|
695
1001
|
: undefined;
|
|
696
1002
|
const allCommits = yield getCommits(git, from, to);
|
|
697
1003
|
const filteredCommits = filterCommitsByMessageRegexes(allCommits, options.commitMessageIncludeRegexes, options.commitMessageExcludeRegexes);
|
|
698
1004
|
const filterByCommits = shouldFilterByCommits(allCommits, filteredCommits, options);
|
|
1005
|
+
const shaping = buildShapingFromOptions(options);
|
|
699
1006
|
const rangeQuery = {
|
|
700
1007
|
from,
|
|
701
1008
|
to,
|
|
702
1009
|
commits: filteredCommits,
|
|
703
1010
|
filterByCommits,
|
|
704
1011
|
pathFilter,
|
|
1012
|
+
shaping,
|
|
705
1013
|
};
|
|
706
1014
|
const [diffText, fileNames, diffSummary] = yield Promise.all([
|
|
707
1015
|
getDiff(git, rangeQuery),
|
|
@@ -713,6 +1021,7 @@ If the user message includes a Team line, use that exact team name in the summar
|
|
|
713
1021
|
to,
|
|
714
1022
|
team: options.teamName,
|
|
715
1023
|
model: options.model,
|
|
1024
|
+
provider: options.provider,
|
|
716
1025
|
maxDiffChars: options.maxDiffChars,
|
|
717
1026
|
systemPrompt: options.systemPrompt,
|
|
718
1027
|
commitMessageIncludeRegexes: options.commitMessageIncludeRegexes,
|
|
@@ -723,17 +1032,20 @@ If the user message includes a Team line, use that exact team name in the summar
|
|
|
723
1032
|
fileNames,
|
|
724
1033
|
commits: filteredCommits,
|
|
725
1034
|
flags: summarizeFlags,
|
|
726
|
-
|
|
1035
|
+
llmModelProvider: options.llmModelProvider,
|
|
727
1036
|
diffSummary,
|
|
728
1037
|
});
|
|
729
1038
|
});
|
|
730
1039
|
}
|
|
731
1040
|
|
|
732
1041
|
exports.DEFAULT_GIT_DIFF_SYSTEM_PROMPT = DEFAULT_GIT_DIFF_SYSTEM_PROMPT;
|
|
1042
|
+
exports.DEFAULT_NOISE_EXCLUDES = DEFAULT_NOISE_EXCLUDES;
|
|
733
1043
|
exports.LLM_GATEWAY_REQUIRED_MESSAGE = LLM_GATEWAY_REQUIRED_MESSAGE;
|
|
734
1044
|
exports.buildDiffPathspecs = buildDiffPathspecs;
|
|
1045
|
+
exports.buildDiffShapingGitArgs = buildDiffShapingGitArgs;
|
|
735
1046
|
exports.createGitClient = createGitClient;
|
|
736
|
-
exports.
|
|
1047
|
+
exports.defaultModelForProvider = defaultModelForProvider;
|
|
1048
|
+
exports.detectLlmProvider = detectLlmProvider;
|
|
737
1049
|
exports.filterCommitsByMessageRegexes = filterCommitsByMessageRegexes;
|
|
738
1050
|
exports.generateSummary = generateSummary;
|
|
739
1051
|
exports.getChangedFiles = getChangedFiles;
|
|
@@ -741,12 +1053,12 @@ If the user message includes a Team line, use that exact team name in the summar
|
|
|
741
1053
|
exports.getDiff = getDiff;
|
|
742
1054
|
exports.getDiffSummary = getDiffSummary;
|
|
743
1055
|
exports.getRepoRoot = getRepoRoot;
|
|
1056
|
+
exports.isLlmProviderConfigured = isLlmProviderConfigured;
|
|
744
1057
|
exports.parseLlmDefaultHeadersFromEnv = parseLlmDefaultHeadersFromEnv;
|
|
1058
|
+
exports.resolveLanguageModel = resolveLanguageModel;
|
|
745
1059
|
exports.resolveLlmBaseUrl = resolveLlmBaseUrl;
|
|
746
1060
|
exports.resolveLlmMaxDiffChars = resolveLlmMaxDiffChars;
|
|
747
|
-
exports.
|
|
748
|
-
exports.shouldUseLlmGateway = shouldUseLlmGateway;
|
|
749
|
-
exports.splitPromotableAuthorizationFromHeaders = splitPromotableAuthorizationFromHeaders;
|
|
1061
|
+
exports.shapeUnifiedDiff = shapeUnifiedDiff;
|
|
750
1062
|
exports.summarizeGitDiff = summarizeGitDiff;
|
|
751
1063
|
exports.truncateUnifiedDiffForLlm = truncateUnifiedDiffForLlm;
|
|
752
1064
|
|