@mcarvin/smart-diff 1.0.3 → 1.0.5

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.
Files changed (38) hide show
  1. package/README.md +13 -10
  2. package/dist/index.cjs +372 -281
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.min.cjs +1 -1
  5. package/dist/index.min.cjs.map +1 -1
  6. package/dist/index.min.mjs +1 -1
  7. package/dist/index.min.mjs.map +1 -1
  8. package/dist/index.min.umd.js +1 -1
  9. package/dist/index.min.umd.js.map +1 -1
  10. package/dist/index.mjs +372 -281
  11. package/dist/index.mjs.map +1 -1
  12. package/dist/index.umd.js +372 -281
  13. package/dist/index.umd.js.map +1 -1
  14. package/dist/typings/{src/ai/aiSummary.d.ts → ai/aiConstants.d.ts} +1 -17
  15. package/dist/typings/ai/aiSummary.d.ts +2 -17
  16. package/dist/typings/ai/aiTypes.d.ts +21 -0
  17. package/dist/typings/git/commitMessageFilter.d.ts +2 -0
  18. package/dist/typings/git/diffGitStatus.d.ts +3 -0
  19. package/dist/typings/git/diffNameStatusParse.d.ts +8 -0
  20. package/dist/typings/git/diffNumstatParse.d.ts +4 -0
  21. package/dist/typings/git/diffPathspecs.d.ts +2 -0
  22. package/dist/typings/git/diffSummaryBuild.d.ts +2 -0
  23. package/dist/typings/git/diffSummaryParse.d.ts +2 -0
  24. package/dist/typings/git/diffTypes.d.ts +31 -0
  25. package/dist/typings/git/gitDiff.d.ts +5 -33
  26. package/dist/typings/git/gitDiffOps.d.ts +8 -0
  27. package/dist/typings/index.d.ts +9 -8
  28. package/package.json +1 -1
  29. package/dist/typings/src/ai/openAIConfig.d.ts +0 -21
  30. package/dist/typings/src/git/gitDiff.d.ts +0 -33
  31. package/dist/typings/src/index.d.ts +0 -24
  32. package/dist/typings/test/aiSummary.spec.d.ts +0 -1
  33. package/dist/typings/test/gitDiff.async.spec.d.ts +0 -1
  34. package/dist/typings/test/gitDiff.spec.d.ts +0 -1
  35. package/dist/typings/test/index.spec.d.ts +0 -1
  36. package/dist/typings/test/openAIConfig.spec.d.ts +0 -1
  37. package/dist/typings/test/openAiSdk.spec.d.ts +0 -1
  38. package/dist/typings/test/summarizeGitDiff.spec.d.ts +0 -1
package/dist/index.mjs CHANGED
@@ -35,7 +35,7 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
35
35
 
36
36
  function resolveLlmBaseUrl() {
37
37
  var _a, _b, _c;
38
- return (_b = (_a = process.env.LLM_BASE_URL) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : (_c = process.env.OPENAI_BASE_URL) === null || _c === void 0 ? void 0 : _c.trim();
38
+ return ((_b = (_a = process.env.LLM_BASE_URL) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : (_c = process.env.OPENAI_BASE_URL) === null || _c === void 0 ? void 0 : _c.trim());
39
39
  }
40
40
  function parseHeaderJsonObject(raw) {
41
41
  const trimmed = raw === null || raw === void 0 ? void 0 : raw.trim();
@@ -43,12 +43,14 @@ function parseHeaderJsonObject(raw) {
43
43
  return {};
44
44
  try {
45
45
  const parsed = JSON.parse(trimmed);
46
- if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
46
+ if (typeof parsed !== "object" ||
47
+ parsed === null ||
48
+ Array.isArray(parsed)) {
47
49
  return {};
48
50
  }
49
51
  const out = {};
50
52
  for (const [key, value] of Object.entries(parsed)) {
51
- if (typeof value === 'string' && value.length > 0) {
53
+ if (typeof value === "string" && value.length > 0) {
52
54
  out[key] = value;
53
55
  }
54
56
  }
@@ -65,7 +67,7 @@ function parseLlmDefaultHeadersFromEnv() {
65
67
  return Object.keys(merged).length > 0 ? merged : undefined;
66
68
  }
67
69
  function findAuthorizationHeaderName(headers) {
68
- return Object.keys(headers).find((k) => k.toLowerCase() === 'authorization');
70
+ return Object.keys(headers).find((k) => k.toLowerCase() === "authorization");
69
71
  }
70
72
  function stripBearerPrefix(value) {
71
73
  var _a;
@@ -108,7 +110,7 @@ function resolveOpenAiLikeClientInit() {
108
110
  var _a, _b, _c, _d, _e;
109
111
  const baseURL = resolveLlmBaseUrl();
110
112
  const mergedHeaders = (_a = parseLlmDefaultHeadersFromEnv()) !== null && _a !== void 0 ? _a : {};
111
- const envApiKey = (_e = (_c = (_b = process.env.LLM_API_KEY) === null || _b === void 0 ? void 0 : _b.trim()) !== null && _c !== void 0 ? _c : (_d = process.env.OPENAI_API_KEY) === null || _d === void 0 ? void 0 : _d.trim()) !== null && _e !== void 0 ? _e : '';
113
+ const envApiKey = (_e = (_c = (_b = process.env.LLM_API_KEY) === null || _b === void 0 ? void 0 : _b.trim()) !== null && _c !== void 0 ? _c : (_d = process.env.OPENAI_API_KEY) === null || _d === void 0 ? void 0 : _d.trim()) !== null && _e !== void 0 ? _e : "";
112
114
  let defaultHeaders;
113
115
  let apiKey = envApiKey;
114
116
  if (apiKey.length === 0) {
@@ -116,12 +118,16 @@ function resolveOpenAiLikeClientInit() {
116
118
  if (split.apiKeyFromAuthHeader) {
117
119
  apiKey = split.apiKeyFromAuthHeader;
118
120
  }
119
- defaultHeaders = Object.keys(split.defaultHeaders).length > 0 ? split.defaultHeaders : undefined;
121
+ defaultHeaders =
122
+ Object.keys(split.defaultHeaders).length > 0
123
+ ? split.defaultHeaders
124
+ : undefined;
120
125
  }
121
126
  else {
122
- defaultHeaders = Object.keys(mergedHeaders).length > 0 ? mergedHeaders : undefined;
127
+ defaultHeaders =
128
+ Object.keys(mergedHeaders).length > 0 ? mergedHeaders : undefined;
123
129
  }
124
- return Object.assign(Object.assign({ apiKey: apiKey.length > 0 ? apiKey : 'unused' }, (baseURL ? { baseURL } : {})), (defaultHeaders ? { defaultHeaders } : {}));
130
+ return Object.assign(Object.assign({ apiKey: apiKey.length > 0 ? apiKey : "unused" }, (baseURL ? { baseURL } : {})), (defaultHeaders ? { defaultHeaders } : {}));
125
131
  }
126
132
  function createOpenAiLikeClient() {
127
133
  return __awaiter(this, void 0, void 0, function* () {
@@ -131,9 +137,22 @@ function createOpenAiLikeClient() {
131
137
  }
132
138
 
133
139
  const DEFAULT_LLM_MAX_DIFF_CHARS = 120000;
140
+ 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.
141
+ 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.
142
+ Explain what changed in terms of behavior, APIs, data, configuration, security, and operational risk. Tie claims to the patch when possible.
143
+ Produce a concise, developer-focused summary in Markdown.
144
+ 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.
145
+ Group related changes; do not list every individual file. When multiple commits appear in the context, briefly separate notable themes by commit when helpful.
146
+ If the user message includes a Team line, use that exact team name in the summary title (for example: "## <Team> – Change summary" or similar).`;
147
+ 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, " +
148
+ "and/or JSON in OPENAI_DEFAULT_HEADERS or LLM_DEFAULT_HEADERS. " +
149
+ "Alternatively pass openAiClientProvider to generateSummary or summarizeGitDiff.";
150
+
134
151
  function resolveLlmMaxDiffChars(cliOverride) {
135
152
  var _a;
136
- if (cliOverride !== undefined && Number.isFinite(cliOverride) && cliOverride > 0) {
153
+ if (cliOverride !== undefined &&
154
+ Number.isFinite(cliOverride) &&
155
+ cliOverride > 0) {
137
156
  return Math.trunc(cliOverride);
138
157
  }
139
158
  const raw = (_a = process.env.LLM_MAX_DIFF_CHARS) === null || _a === void 0 ? void 0 : _a.trim();
@@ -152,70 +171,67 @@ function truncateUnifiedDiffForLlm(diffText, maxChars) {
152
171
  const marker = `\n\n--- TRUNCATED: unified diff was ${diffText.length} characters; only the first ${maxChars} were sent. Narrow the ref range, adjust commit/path filters, or raise maxDiffChars / LLM_MAX_DIFF_CHARS only if your model context allows. ---\n`;
153
172
  return diffText.slice(0, maxChars) + marker;
154
173
  }
155
- 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.
156
- 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.
157
- Explain what changed in terms of behavior, APIs, data, configuration, security, and operational risk. Tie claims to the patch when possible.
158
- Produce a concise, developer-focused summary in Markdown.
159
- 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.
160
- Group related changes; do not list every individual file. When multiple commits appear in the context, briefly separate notable themes by commit when helpful.
161
- If the user message includes a Team line, use that exact team name in the summary title (for example: "## <Team> – Change summary" or similar).`;
162
- 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, ' +
163
- 'and/or JSON in OPENAI_DEFAULT_HEADERS or LLM_DEFAULT_HEADERS. ' +
164
- 'Alternatively pass openAiClientProvider to generateSummary or summarizeGitDiff.';
165
- function generateSummary(diffText, fileNames, commits, flags, openAiClientProvider, diffSummary) {
174
+ function generateSummary(input) {
166
175
  return __awaiter(this, void 0, void 0, function* () {
167
176
  var _a, _b;
177
+ const { diffText, fileNames, commits, flags, openAiClientProvider, diffSummary, } = input;
168
178
  if (!shouldUseLlmGateway() && openAiClientProvider === undefined) {
169
179
  throw new Error(LLM_GATEWAY_REQUIRED_MESSAGE);
170
180
  }
171
181
  const maxDiffChars = resolveLlmMaxDiffChars(flags.maxDiffChars);
172
182
  const diffForLlm = truncateUnifiedDiffForLlm(diffText, maxDiffChars);
173
183
  const userContent = buildOpenAiUserContent(flags, commits, fileNames, diffForLlm, diffSummary);
174
- return callOpenAi(userContent, (_a = flags.model) !== null && _a !== void 0 ? _a : 'gpt-4o-mini', (_b = flags.systemPrompt) !== null && _b !== void 0 ? _b : DEFAULT_GIT_DIFF_SYSTEM_PROMPT, openAiClientProvider !== null && openAiClientProvider !== void 0 ? openAiClientProvider : (() => __awaiter(this, void 0, void 0, function* () { return createOpenAiLikeClient(); })));
184
+ return callOpenAi(userContent, (_a = flags.model) !== null && _a !== void 0 ? _a : "gpt-4o-mini", (_b = flags.systemPrompt) !== null && _b !== void 0 ? _b : DEFAULT_GIT_DIFF_SYSTEM_PROMPT, openAiClientProvider !== null && openAiClientProvider !== void 0 ? openAiClientProvider : (() => __awaiter(this, void 0, void 0, function* () { return createOpenAiLikeClient(); })));
175
185
  });
176
186
  }
177
187
  function formatRegexFilterLines(flags) {
178
188
  var _a, _b;
179
- const includes = ((_a = flags.commitMessageIncludeRegexes) !== null && _a !== void 0 ? _a : []).map((s) => s.trim()).filter(Boolean);
180
- const excludes = ((_b = flags.commitMessageExcludeRegexes) !== null && _b !== void 0 ? _b : []).map((s) => s.trim()).filter(Boolean);
189
+ const includes = ((_a = flags.commitMessageIncludeRegexes) !== null && _a !== void 0 ? _a : [])
190
+ .map((s) => s.trim())
191
+ .filter(Boolean);
192
+ const excludes = ((_b = flags.commitMessageExcludeRegexes) !== null && _b !== void 0 ? _b : [])
193
+ .map((s) => s.trim())
194
+ .filter(Boolean);
181
195
  const incLine = includes.length > 0
182
- ? `Commit message include regexes (OR): ${includes.map((r) => JSON.stringify(r)).join(', ')}\n`
183
- : '';
196
+ ? `Commit message include regexes (OR): ${includes.map((r) => JSON.stringify(r)).join(", ")}\n`
197
+ : "";
184
198
  const excLine = excludes.length > 0
185
- ? `Commit message exclude regexes: ${excludes.map((r) => JSON.stringify(r)).join(', ')}\n`
186
- : '';
199
+ ? `Commit message exclude regexes: ${excludes.map((r) => JSON.stringify(r)).join(", ")}\n`
200
+ : "";
187
201
  if (!incLine && !excLine) {
188
- return 'Commit message filters: none.\nGit context shape: single unified diff for the full ref range.\n';
202
+ return "Commit message filters: none.\nGit context shape: single unified diff for the full ref range.\n";
189
203
  }
190
204
  return (`${incLine}${excLine}` +
191
- 'Git context shape: concatenated per-commit unified patches for commits that pass the message filters.\n');
205
+ "Git context shape: concatenated per-commit unified patches for commits that pass the message filters.\n");
192
206
  }
193
207
  function buildOpenAiUserContent(flags, commits, fileNames, diffText, diffSummary) {
194
208
  var _a, _b;
195
209
  const from = flags.from;
196
- const to = (_a = flags.to) !== null && _a !== void 0 ? _a : 'HEAD';
210
+ const to = (_a = flags.to) !== null && _a !== void 0 ? _a : "HEAD";
197
211
  const team = (_b = flags.team) === null || _b === void 0 ? void 0 : _b.trim();
198
212
  const ts = new Date().toISOString();
199
- const teamLine = team ? `Team: ${team}\n` : '';
213
+ const teamLine = team ? `Team: ${team}\n` : "";
200
214
  const filterBlock = formatRegexFilterLines(flags);
201
215
  const commitBlock = commits.length > 0
202
- ? commits.map((c) => `- ${c.hash.slice(0, 7)} ${c.message.replace(/\r?\n/g, ' ')}`).join('\n')
203
- : '- (no commits in range after filtering)';
204
- const pathsBlock = fileNames.length > 0 ? fileNames.join('\n') : '(no paths in diff scope)';
216
+ ? commits
217
+ .map((c) => `- ${c.hash.slice(0, 7)} ${c.message.replace(/\r?\n/g, " ")}`)
218
+ .join("\n")
219
+ : "- (no commits in range after filtering)";
220
+ const pathsBlock = fileNames.length > 0 ? fileNames.join("\n") : "(no paths in diff scope)";
205
221
  const structuredDiffSection = diffSummary
206
222
  ? `=== Structured git context (JSON summary) ===\n${JSON.stringify(diffSummary, null, 2)}\n\n`
207
- : '';
223
+ : "";
208
224
  return (`${teamLine}` +
209
225
  `Date: ${ts}\n\n` +
210
226
  `Git refs: ${from}..${to}\n` +
211
227
  filterBlock +
212
- '\n' +
213
- '=== Included commits (subject lines) ===\n' +
228
+ "\n" +
229
+ "=== Included commits (subject lines) ===\n" +
214
230
  `${commitBlock}\n\n` +
215
- '=== Changed paths ===\n' +
231
+ "=== Changed paths ===\n" +
216
232
  `${pathsBlock}\n\n` +
217
233
  structuredDiffSection +
218
- '=== Git context (unified diff(s); patches may be truncated with an explicit marker) ===\n' +
234
+ "=== Git context (unified diff(s); patches may be truncated with an explicit marker) ===\n" +
219
235
  diffText);
220
236
  }
221
237
  function callOpenAi(userContent, model, systemPrompt, openAiClientProvider) {
@@ -229,11 +245,11 @@ function callOpenAi(userContent, model, systemPrompt, openAiClientProvider) {
229
245
  model,
230
246
  messages: [
231
247
  {
232
- role: 'system',
248
+ role: "system",
233
249
  content: systemPrompt,
234
250
  },
235
251
  {
236
- role: 'user',
252
+ role: "user",
237
253
  content: userContent,
238
254
  },
239
255
  ],
@@ -241,63 +257,25 @@ function callOpenAi(userContent, model, systemPrompt, openAiClientProvider) {
241
257
  max_tokens: maxTokens,
242
258
  });
243
259
  const typedResponse = response;
244
- const text = (_f = (_e = (_d = (_c = (_b = typedResponse.choices) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.message) === null || _d === void 0 ? void 0 : _d.content) === null || _e === void 0 ? void 0 : _e.trim()) !== null && _f !== void 0 ? _f : '';
245
- return text.length > 0 ? text : 'No summary generated by OpenAI.';
260
+ const text = (_f = (_e = (_d = (_c = (_b = typedResponse.choices) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.message) === null || _d === void 0 ? void 0 : _d.content) === null || _e === void 0 ? void 0 : _e.trim()) !== null && _f !== void 0 ? _f : "";
261
+ return text.length > 0 ? text : "No summary generated by OpenAI.";
246
262
  });
247
263
  }
248
264
 
249
- function createGitClient(cwd = process.cwd()) {
250
- return simpleGit(cwd);
251
- }
252
- function getCommits(git, from, to) {
253
- return __awaiter(this, void 0, void 0, function* () {
254
- const logResult = yield git.log({ from, to });
255
- return logResult.all;
256
- });
257
- }
258
- function compileRegex(pattern, label) {
259
- try {
260
- return new RegExp(pattern, 'i');
261
- }
262
- catch (_a) {
263
- throw new Error(`Invalid ${label} regular expression: ${JSON.stringify(pattern)}`);
264
- }
265
- }
266
- function filterCommitsByMessageRegexes(commits, includePatterns, excludePatterns) {
267
- const includes = (includePatterns !== null && includePatterns !== void 0 ? includePatterns : []).map((p) => p.trim()).filter((p) => p.length > 0);
268
- const excludes = (excludePatterns !== null && excludePatterns !== void 0 ? excludePatterns : []).map((p) => p.trim()).filter((p) => p.length > 0);
269
- const includeRes = includes.map((p, i) => compileRegex(p, `commit message include pattern[${i}]`));
270
- const excludeRes = excludes.map((p, i) => compileRegex(p, `commit message exclude pattern[${i}]`));
271
- return commits.filter((c) => {
272
- for (const ex of excludeRes) {
273
- if (ex.test(c.message))
274
- return false;
275
- }
276
- if (includeRes.length > 0 && !includeRes.some((r) => r.test(c.message)))
277
- return false;
278
- return true;
279
- });
280
- }
281
- function getRepoRoot(git) {
282
- return __awaiter(this, void 0, void 0, function* () {
283
- const root = yield git.revparse(['--show-toplevel']);
284
- return root.trim();
285
- });
286
- }
287
265
  function normalizeRepoRelativePath(p) {
288
- const trimmed = p.trim().replace(/\\/g, '/');
289
- const noLeading = trimmed.replace(/^\/+/, '');
290
- const noTrailingSlash = noLeading.replace(/\/+$/, '');
291
- return noTrailingSlash.length > 0 ? noTrailingSlash : '.';
266
+ const trimmed = p.trim().replace(/\\/g, "/");
267
+ const noLeading = trimmed.replace(/^\/+/, "");
268
+ const noTrailingSlash = noLeading.replace(/\/+$/, "");
269
+ return noTrailingSlash.length > 0 ? noTrailingSlash : ".";
292
270
  }
293
271
  function assertPathUnderRepo(repoRoot, userPath) {
294
272
  const abs = resolve(repoRoot, userPath);
295
273
  const rel = relative(repoRoot, abs);
296
- if (rel === '..') {
274
+ if (rel === "..") {
297
275
  throw new Error(`Path escapes repository root: ${JSON.stringify(userPath)}`);
298
276
  }
299
277
  const segments = rel.split(/[/\\]/);
300
- if (segments.includes('..')) {
278
+ if (segments.includes("..")) {
301
279
  throw new Error(`Path escapes repository root: ${JSON.stringify(userPath)}`);
302
280
  }
303
281
  }
@@ -305,9 +283,13 @@ function buildDiffPathspecs(repoRoot, pathFilter) {
305
283
  var _a, _b, _c, _d;
306
284
  const includeRaw = (_b = (_a = pathFilter === null || pathFilter === void 0 ? void 0 : pathFilter.includeFolders) === null || _a === void 0 ? void 0 : _a.filter((p) => p.trim().length > 0)) !== null && _b !== void 0 ? _b : [];
307
285
  const excludeRaw = (_d = (_c = pathFilter === null || pathFilter === void 0 ? void 0 : pathFilter.excludeFolders) === null || _c === void 0 ? void 0 : _c.filter((p) => p.trim().length > 0)) !== null && _d !== void 0 ? _d : [];
308
- const includes = includeRaw.map(normalizeRepoRelativePath).filter((p) => p !== '.' && p !== '');
309
- const excludes = excludeRaw.map(normalizeRepoRelativePath).filter((p) => p !== '.' && p !== '');
310
- const toValidate = includes.length > 0 ? includes : ['.'];
286
+ const includes = includeRaw
287
+ .map(normalizeRepoRelativePath)
288
+ .filter((p) => p !== "." && p !== "");
289
+ const excludes = excludeRaw
290
+ .map(normalizeRepoRelativePath)
291
+ .filter((p) => p !== "." && p !== "");
292
+ const toValidate = includes.length > 0 ? includes : ["."];
311
293
  for (const inc of toValidate) {
312
294
  assertPathUnderRepo(repoRoot, inc);
313
295
  }
@@ -316,7 +298,7 @@ function buildDiffPathspecs(repoRoot, pathFilter) {
316
298
  }
317
299
  const specs = [];
318
300
  if (includes.length === 0) {
319
- specs.push('.');
301
+ specs.push(".");
320
302
  }
321
303
  else {
322
304
  for (const inc of includes) {
@@ -328,123 +310,99 @@ function buildDiffPathspecs(repoRoot, pathFilter) {
328
310
  }
329
311
  return specs;
330
312
  }
331
- function getDiffPathContext(git, pathFilter, repoRootOverride) {
332
- return __awaiter(this, void 0, void 0, function* () {
333
- const repoRoot = repoRootOverride !== null && repoRootOverride !== void 0 ? repoRootOverride : (yield getRepoRoot(git));
334
- const specs = buildDiffPathspecs(repoRoot, pathFilter);
335
- return { repoRoot, specs };
336
- });
337
- }
338
- function getDiff(git, from, to, commits, filterByCommits, pathFilter, repoRootOverride) {
339
- return __awaiter(this, void 0, void 0, function* () {
340
- const { specs } = yield getDiffPathContext(git, pathFilter, repoRootOverride);
341
- if (!filterByCommits) {
342
- return git.diff([`${from}..${to}`, '--', ...specs]);
343
- }
344
- const patches = yield Promise.all(commits.map((c) => git.diff([`${c.hash}^!`, '--', ...specs])));
345
- return patches.filter(Boolean).join('\n');
346
- });
313
+
314
+ function compileRegex(pattern, label) {
315
+ try {
316
+ return new RegExp(pattern, "i");
317
+ }
318
+ catch (_a) {
319
+ throw new Error(`Invalid ${label} regular expression: ${JSON.stringify(pattern)}`);
320
+ }
347
321
  }
348
- function getDiffSummary(git, from, to, commits, filterByCommits, pathFilter, repoRootOverride) {
349
- return __awaiter(this, void 0, void 0, function* () {
350
- const { specs } = yield getDiffPathContext(git, pathFilter, repoRootOverride);
351
- if (!filterByCommits) {
352
- const [numOutput, nameOutput] = yield Promise.all([
353
- git.diff(['--numstat', `${from}..${to}`, '--', ...specs]),
354
- git.diff(['--name-status', `${from}..${to}`, '--', ...specs]),
355
- ]);
356
- return buildDiffSummaryFromGitOutputs(nameOutput, numOutput);
357
- }
358
- const pairs = yield Promise.all(commits.map((c) => __awaiter(this, void 0, void 0, function* () {
359
- const range = `${c.hash}^!`;
360
- const [numOutput, nameOutput] = yield Promise.all([
361
- git.diff(['--numstat', range, '--', ...specs]),
362
- git.diff(['--name-status', range, '--', ...specs]),
363
- ]);
364
- return { numOutput, nameOutput };
365
- })));
366
- const nameJoined = pairs
367
- .map((p) => p.nameOutput)
368
- .filter(Boolean)
369
- .join('\n');
370
- const numJoined = pairs
371
- .map((p) => p.numOutput)
372
- .filter(Boolean)
373
- .join('\n');
374
- return buildDiffSummaryFromGitOutputs(nameJoined, numJoined);
375
- });
322
+ function commitMessagePassesFilters(message, includeRes, excludeRes) {
323
+ for (const ex of excludeRes) {
324
+ if (ex.test(message))
325
+ return false;
326
+ }
327
+ if (includeRes.length > 0 && !includeRes.some((r) => r.test(message)))
328
+ return false;
329
+ return true;
376
330
  }
377
- function getChangedFiles(git, from, to, commits, filterByCommits, pathFilter, repoRootOverride) {
378
- return __awaiter(this, void 0, void 0, function* () {
379
- const { specs } = yield getDiffPathContext(git, pathFilter, repoRootOverride);
380
- if (!filterByCommits) {
381
- const output = yield git.diff(['--name-only', `${from}..${to}`, '--', ...specs]);
382
- return output
383
- .split(/\r?\n/)
384
- .map((f) => f.trim())
385
- .filter(Boolean);
386
- }
387
- const fileSet = new Set();
388
- yield Promise.all(commits.map((c) => __awaiter(this, void 0, void 0, function* () {
389
- const output = yield git.show(['--name-only', '--pretty=format:', c.hash, '--', ...specs]);
390
- output
391
- .split(/\r?\n/)
392
- .map((f) => f.trim())
393
- .filter(Boolean)
394
- .forEach((f) => fileSet.add(f));
395
- })));
396
- return Array.from(fileSet);
397
- });
331
+ function filterCommitsByMessageRegexes(commits, includePatterns, excludePatterns) {
332
+ const includes = (includePatterns !== null && includePatterns !== void 0 ? includePatterns : [])
333
+ .map((p) => p.trim())
334
+ .filter((p) => p.length > 0);
335
+ const excludes = (excludePatterns !== null && excludePatterns !== void 0 ? excludePatterns : [])
336
+ .map((p) => p.trim())
337
+ .filter((p) => p.length > 0);
338
+ const includeRes = includes.map((p, i) => compileRegex(p, `commit message include pattern[${i}]`));
339
+ const excludeRes = excludes.map((p, i) => compileRegex(p, `commit message exclude pattern[${i}]`));
340
+ return commits.filter((c) => commitMessagePassesFilters(c.message, includeRes, excludeRes));
398
341
  }
342
+
343
+ const GIT_STATUS_BY_FIRST_CHAR = {
344
+ A: "added",
345
+ D: "deleted",
346
+ R: "renamed",
347
+ C: "copied",
348
+ T: "type-changed",
349
+ M: "modified",
350
+ };
399
351
  function mapGitStatus(statusCode) {
400
- if (statusCode.startsWith('A'))
401
- return 'added';
402
- if (statusCode.startsWith('D'))
403
- return 'deleted';
404
- if (statusCode.startsWith('R'))
405
- return 'renamed';
406
- if (statusCode.startsWith('C'))
407
- return 'copied';
408
- if (statusCode.startsWith('T'))
409
- return 'type-changed';
410
- if (statusCode.startsWith('M'))
411
- return 'modified';
412
- return 'unknown';
352
+ var _a;
353
+ return (_a = GIT_STATUS_BY_FIRST_CHAR[statusCode.charAt(0)]) !== null && _a !== void 0 ? _a : "unknown";
413
354
  }
414
355
  function mergeStatus(existing, next) {
415
356
  if (existing === next)
416
357
  return existing;
417
- const precedence = ['deleted', 'added', 'renamed', 'copied', 'type-changed', 'modified', 'unknown'];
418
- return precedence.indexOf(existing) <= precedence.indexOf(next) ? existing : next;
358
+ const precedence = [
359
+ "deleted",
360
+ "added",
361
+ "renamed",
362
+ "copied",
363
+ "type-changed",
364
+ "modified",
365
+ "unknown",
366
+ ];
367
+ return precedence.indexOf(existing) <= precedence.indexOf(next)
368
+ ? existing
369
+ : next;
419
370
  }
420
- function parseNameStatusLines(nameStatusOutput) {
371
+
372
+ function parseNameStatusLine(line) {
421
373
  var _a;
422
- const entries = [];
423
- for (const rawLine of nameStatusOutput.split(/\r?\n/)) {
424
- const line = rawLine.trim();
425
- if (!line)
426
- continue;
427
- const parts = line.split('\t');
428
- if (parts.length < 2)
429
- continue;
430
- const statusToken = (_a = parts[0]) !== null && _a !== void 0 ? _a : '';
374
+ const parts = line.split("\t");
375
+ let entry = null;
376
+ if (parts.length >= 2) {
377
+ const statusToken = (_a = parts[0]) !== null && _a !== void 0 ? _a : "";
431
378
  const status = mapGitStatus(statusToken);
432
- if (statusToken.startsWith('R') || statusToken.startsWith('C')) {
433
- if (parts.length < 3)
434
- continue;
379
+ const isRenameOrCopy = statusToken.startsWith("R") || statusToken.startsWith("C");
380
+ if (isRenameOrCopy && parts.length >= 3) {
435
381
  const oldPath = parts[1];
436
382
  const newPath = parts[2];
437
- if (oldPath === undefined || newPath === undefined)
438
- continue;
439
- entries.push({ path: newPath, status, oldPath });
383
+ if (oldPath !== undefined && newPath !== undefined) {
384
+ entry = { path: newPath, status, oldPath };
385
+ }
440
386
  }
441
- else {
387
+ else if (!isRenameOrCopy) {
442
388
  const pathOnly = parts[1];
443
- if (pathOnly === undefined)
444
- continue;
445
- entries.push({ path: pathOnly, status });
389
+ if (pathOnly !== undefined) {
390
+ entry = { path: pathOnly, status };
391
+ }
446
392
  }
447
393
  }
394
+ return entry;
395
+ }
396
+ function parseNameStatusLines(nameStatusOutput) {
397
+ const entries = [];
398
+ for (const rawLine of nameStatusOutput.split(/\r?\n/)) {
399
+ const line = rawLine.trim();
400
+ if (!line)
401
+ continue;
402
+ const entry = parseNameStatusLine(line);
403
+ if (entry)
404
+ entries.push(entry);
405
+ }
448
406
  return entries;
449
407
  }
450
408
  function mergeNameEntriesByPath(entries) {
@@ -464,6 +422,7 @@ function mergeNameEntriesByPath(entries) {
464
422
  }
465
423
  return byPath;
466
424
  }
425
+
467
426
  function numStatPathToLookupKey(pathField) {
468
427
  const brace = /^(.*)\{(.+) => (.+)\}$/.exec(pathField);
469
428
  if (!brace) {
@@ -473,107 +432,93 @@ function numStatPathToLookupKey(pathField) {
473
432
  const toSeg = brace[3].trim();
474
433
  return `${dirRaw}${toSeg}`;
475
434
  }
435
+ function parseNumStatLine(line) {
436
+ var _a, _b;
437
+ const parts = line.split("\t");
438
+ if (parts.length < 3)
439
+ return null;
440
+ const addStr = (_a = parts[0]) !== null && _a !== void 0 ? _a : "";
441
+ const delStr = (_b = parts[1]) !== null && _b !== void 0 ? _b : "";
442
+ const pathField = parts.slice(2).join("\t");
443
+ const additions = addStr !== "-" ? Number.parseInt(addStr, 10) || 0 : 0;
444
+ const deletions = delStr !== "-" ? Number.parseInt(delStr, 10) || 0 : 0;
445
+ const key = numStatPathToLookupKey(pathField);
446
+ return { key, additions, deletions };
447
+ }
476
448
  function accumulateNumStat(numStatOutput, into) {
477
- var _a, _b, _c;
449
+ var _a;
478
450
  for (const rawLine of numStatOutput.split(/\r?\n/)) {
479
451
  const line = rawLine.trim();
480
452
  if (!line)
481
453
  continue;
482
- const parts = line.split('\t');
483
- if (parts.length < 3)
454
+ const parsed = parseNumStatLine(line);
455
+ if (!parsed)
484
456
  continue;
485
- const addStr = (_a = parts[0]) !== null && _a !== void 0 ? _a : '';
486
- const delStr = (_b = parts[1]) !== null && _b !== void 0 ? _b : '';
487
- const pathField = parts.slice(2).join('\t');
488
- const additions = addStr !== '-' ? Number.parseInt(addStr, 10) || 0 : 0;
489
- const deletions = delStr !== '-' ? Number.parseInt(delStr, 10) || 0 : 0;
490
- const key = numStatPathToLookupKey(pathField);
491
- const prev = (_c = into.get(key)) !== null && _c !== void 0 ? _c : { additions: 0, deletions: 0 };
492
- into.set(key, { additions: prev.additions + additions, deletions: prev.deletions + deletions });
457
+ const prev = (_a = into.get(parsed.key)) !== null && _a !== void 0 ? _a : { additions: 0, deletions: 0 };
458
+ into.set(parsed.key, {
459
+ additions: prev.additions + parsed.additions,
460
+ deletions: prev.deletions + parsed.deletions,
461
+ });
493
462
  }
494
463
  }
495
- function diffStatusToSyntheticPrefix(status) {
496
- switch (status) {
497
- case 'added':
498
- return 'A';
499
- case 'deleted':
500
- return 'D';
501
- case 'renamed':
502
- return 'R100';
503
- case 'copied':
504
- return 'C100';
505
- case 'type-changed':
506
- return 'T';
507
- case 'modified':
508
- return 'M';
509
- default:
510
- return 'X';
464
+
465
+ function parseTabDiffSummaryLine(line) {
466
+ var _a;
467
+ const parts = line.split("\t");
468
+ if (parts.length < 3)
469
+ return null;
470
+ const statusToken = (_a = parts.shift()) !== null && _a !== void 0 ? _a : "";
471
+ const status = mapGitStatus(statusToken);
472
+ const add0 = parts[0];
473
+ const del0 = parts[1];
474
+ const additions = add0 && add0 !== "-" ? Number.parseInt(add0, 10) || 0 : 0;
475
+ const deletions = del0 && del0 !== "-" ? Number.parseInt(del0, 10) || 0 : 0;
476
+ if (parts.length === 3) {
477
+ return { status, additions, deletions, newPath: parts[2] };
478
+ }
479
+ if (parts.length === 4) {
480
+ return {
481
+ status,
482
+ additions,
483
+ deletions,
484
+ oldPath: parts[2],
485
+ newPath: parts[3],
486
+ };
511
487
  }
488
+ return null;
512
489
  }
513
- function buildDiffSummaryFromGitOutputs(nameStatusOutput, numStatOutput) {
514
- var _a;
515
- const numMap = new Map();
516
- accumulateNumStat(numStatOutput, numMap);
517
- const mergedName = mergeNameEntriesByPath(parseNameStatusLines(nameStatusOutput));
518
- const syntheticLines = [];
519
- for (const [path, meta] of mergedName) {
520
- const counts = (_a = numMap.get(path)) !== null && _a !== void 0 ? _a : { additions: 0, deletions: 0 };
521
- const prefix = diffStatusToSyntheticPrefix(meta.status);
522
- if (meta.oldPath) {
523
- syntheticLines.push(`${prefix}\t${counts.additions}\t${counts.deletions}\t${meta.oldPath}\t${path}`);
524
- }
525
- else {
526
- syntheticLines.push(`${prefix}\t${counts.additions}\t${counts.deletions}\t${path}`);
527
- }
490
+ function mergeParsedDiffSummaryLine(fileMap, p) {
491
+ var _a, _b;
492
+ const { newPath, status, additions, deletions, oldPath } = p;
493
+ const existing = fileMap.get(newPath);
494
+ if (existing) {
495
+ existing.additions += additions;
496
+ existing.deletions += deletions;
497
+ existing.status = mergeStatus(existing.status, status);
498
+ if (oldPath)
499
+ existing.oldPath = (_a = existing.oldPath) !== null && _a !== void 0 ? _a : oldPath;
500
+ existing.newPath = (_b = existing.newPath) !== null && _b !== void 0 ? _b : newPath;
501
+ }
502
+ else {
503
+ fileMap.set(newPath, {
504
+ path: newPath,
505
+ status,
506
+ additions,
507
+ deletions,
508
+ oldPath,
509
+ newPath: oldPath ? newPath : undefined,
510
+ });
528
511
  }
529
- return parseDiffSummary(syntheticLines.join('\n'));
530
512
  }
531
513
  function parseDiffSummary(diffOutput) {
532
- var _a, _b, _c;
533
514
  const fileMap = new Map();
534
515
  for (const rawLine of diffOutput.split(/\r?\n/)) {
535
516
  const line = rawLine.trim();
536
517
  if (!line)
537
518
  continue;
538
- const parts = line.split('\t');
539
- if (parts.length < 3)
540
- continue;
541
- const statusToken = (_a = parts.shift()) !== null && _a !== void 0 ? _a : '';
542
- const status = mapGitStatus(statusToken);
543
- const additions = parts[0] && parts[0] !== '-' ? Number.parseInt(parts[0], 10) || 0 : 0;
544
- const deletions = parts[1] && parts[1] !== '-' ? Number.parseInt(parts[1], 10) || 0 : 0;
545
- let oldPath;
546
- let newPath;
547
- if (parts.length === 3) {
548
- newPath = parts[2];
549
- }
550
- else if (parts.length === 4) {
551
- oldPath = parts[2];
552
- newPath = parts[3];
553
- }
554
- else {
555
- continue;
556
- }
557
- const path = newPath;
558
- const existing = fileMap.get(path);
559
- if (existing) {
560
- existing.additions += additions;
561
- existing.deletions += deletions;
562
- existing.status = mergeStatus(existing.status, status);
563
- if (oldPath)
564
- existing.oldPath = (_b = existing.oldPath) !== null && _b !== void 0 ? _b : oldPath;
565
- existing.newPath = (_c = existing.newPath) !== null && _c !== void 0 ? _c : newPath;
566
- }
567
- else {
568
- fileMap.set(path, {
569
- path,
570
- status,
571
- additions,
572
- deletions,
573
- oldPath,
574
- newPath: oldPath ? newPath : undefined,
575
- });
576
- }
519
+ const parsed = parseTabDiffSummaryLine(line);
520
+ if (parsed)
521
+ mergeParsedDiffSummaryLine(fileMap, parsed);
577
522
  }
578
523
  const files = Array.from(fileMap.values());
579
524
  return {
@@ -584,11 +529,142 @@ function parseDiffSummary(diffOutput) {
584
529
  };
585
530
  }
586
531
 
532
+ const STATUS_TO_SYNTHETIC_PREFIX = {
533
+ added: "A",
534
+ deleted: "D",
535
+ renamed: "R100",
536
+ copied: "C100",
537
+ "type-changed": "T",
538
+ modified: "M",
539
+ unknown: "X",
540
+ };
541
+ function diffStatusToSyntheticPrefix(status) {
542
+ return STATUS_TO_SYNTHETIC_PREFIX[status];
543
+ }
544
+ function buildSyntheticDiffLine(meta, counts) {
545
+ const prefix = diffStatusToSyntheticPrefix(meta.status);
546
+ if (meta.oldPath) {
547
+ return `${prefix}\t${counts.additions}\t${counts.deletions}\t${meta.oldPath}\t${meta.path}`;
548
+ }
549
+ return `${prefix}\t${counts.additions}\t${counts.deletions}\t${meta.path}`;
550
+ }
551
+ function buildDiffSummaryFromGitOutputs(nameStatusOutput, numStatOutput) {
552
+ var _a;
553
+ const numMap = new Map();
554
+ accumulateNumStat(numStatOutput, numMap);
555
+ const mergedName = mergeNameEntriesByPath(parseNameStatusLines(nameStatusOutput));
556
+ const syntheticLines = [];
557
+ for (const [path, meta] of mergedName) {
558
+ const counts = (_a = numMap.get(path)) !== null && _a !== void 0 ? _a : { additions: 0, deletions: 0 };
559
+ syntheticLines.push(buildSyntheticDiffLine(meta, counts));
560
+ }
561
+ return parseDiffSummary(syntheticLines.join("\n"));
562
+ }
563
+
564
+ function createGitClient(cwd = process.cwd()) {
565
+ return simpleGit(cwd);
566
+ }
567
+ function getCommits(git, from, to) {
568
+ return __awaiter(this, void 0, void 0, function* () {
569
+ const logResult = yield git.log({ from, to });
570
+ return logResult.all;
571
+ });
572
+ }
573
+ function getRepoRoot(git) {
574
+ return __awaiter(this, void 0, void 0, function* () {
575
+ const root = yield git.revparse(["--show-toplevel"]);
576
+ return root.trim();
577
+ });
578
+ }
579
+ function getDiffPathContext(git, pathFilter, repoRootOverride) {
580
+ return __awaiter(this, void 0, void 0, function* () {
581
+ const repoRoot = repoRootOverride !== null && repoRootOverride !== void 0 ? repoRootOverride : (yield getRepoRoot(git));
582
+ const specs = buildDiffPathspecs(repoRoot, pathFilter);
583
+ return { repoRoot, specs };
584
+ });
585
+ }
586
+ function getDiff(git, query) {
587
+ return __awaiter(this, void 0, void 0, function* () {
588
+ const { from, to, commits, filterByCommits, pathFilter, repoRootOverride } = query;
589
+ const { specs } = yield getDiffPathContext(git, pathFilter, repoRootOverride);
590
+ if (!filterByCommits) {
591
+ return git.diff([`${from}..${to}`, "--", ...specs]);
592
+ }
593
+ const patches = yield Promise.all(commits.map((c) => git.diff([`${c.hash}^!`, "--", ...specs])));
594
+ return patches.filter(Boolean).join("\n");
595
+ });
596
+ }
597
+ function getDiffSummary(git, query) {
598
+ return __awaiter(this, void 0, void 0, function* () {
599
+ const { from, to, commits, filterByCommits, pathFilter, repoRootOverride } = query;
600
+ const { specs } = yield getDiffPathContext(git, pathFilter, repoRootOverride);
601
+ if (!filterByCommits) {
602
+ const [numOutput, nameOutput] = yield Promise.all([
603
+ git.diff(["--numstat", `${from}..${to}`, "--", ...specs]),
604
+ git.diff(["--name-status", `${from}..${to}`, "--", ...specs]),
605
+ ]);
606
+ return buildDiffSummaryFromGitOutputs(nameOutput, numOutput);
607
+ }
608
+ const pairs = yield Promise.all(commits.map((c) => __awaiter(this, void 0, void 0, function* () {
609
+ const range = `${c.hash}^!`;
610
+ const [numOutput, nameOutput] = yield Promise.all([
611
+ git.diff(["--numstat", range, "--", ...specs]),
612
+ git.diff(["--name-status", range, "--", ...specs]),
613
+ ]);
614
+ return { numOutput, nameOutput };
615
+ })));
616
+ const nameJoined = pairs
617
+ .map((p) => p.nameOutput)
618
+ .filter(Boolean)
619
+ .join("\n");
620
+ const numJoined = pairs
621
+ .map((p) => p.numOutput)
622
+ .filter(Boolean)
623
+ .join("\n");
624
+ return buildDiffSummaryFromGitOutputs(nameJoined, numJoined);
625
+ });
626
+ }
627
+ function getChangedFiles(git, query) {
628
+ return __awaiter(this, void 0, void 0, function* () {
629
+ const { from, to, commits, filterByCommits, pathFilter, repoRootOverride } = query;
630
+ const { specs } = yield getDiffPathContext(git, pathFilter, repoRootOverride);
631
+ if (!filterByCommits) {
632
+ const output = yield git.diff([
633
+ "--name-only",
634
+ `${from}..${to}`,
635
+ "--",
636
+ ...specs,
637
+ ]);
638
+ return output
639
+ .split(/\r?\n/)
640
+ .map((f) => f.trim())
641
+ .filter(Boolean);
642
+ }
643
+ const fileSet = new Set();
644
+ yield Promise.all(commits.map((c) => __awaiter(this, void 0, void 0, function* () {
645
+ const output = yield git.show([
646
+ "--name-only",
647
+ "--pretty=format:",
648
+ c.hash,
649
+ "--",
650
+ ...specs,
651
+ ]);
652
+ output
653
+ .split(/\r?\n/)
654
+ .map((f) => f.trim())
655
+ .filter(Boolean)
656
+ .forEach((f) => fileSet.add(f));
657
+ })));
658
+ return Array.from(fileSet);
659
+ });
660
+ }
661
+
587
662
  function hasNonEmptyTrimmed(arr) {
588
663
  return (arr !== null && arr !== void 0 ? arr : []).some((s) => s.trim().length > 0);
589
664
  }
590
665
  function shouldFilterByCommits(allCommits, filtered, opts) {
591
- if (hasNonEmptyTrimmed(opts.commitMessageIncludeRegexes) || hasNonEmptyTrimmed(opts.commitMessageExcludeRegexes)) {
666
+ if (hasNonEmptyTrimmed(opts.commitMessageIncludeRegexes) ||
667
+ hasNonEmptyTrimmed(opts.commitMessageExcludeRegexes)) {
592
668
  return true;
593
669
  }
594
670
  return filtered.length !== allCommits.length;
@@ -598,8 +674,9 @@ function summarizeGitDiff(options) {
598
674
  var _a, _b;
599
675
  const git = (_a = options.git) !== null && _a !== void 0 ? _a : createGitClient(options.cwd);
600
676
  const from = options.from;
601
- const to = (_b = options.to) !== null && _b !== void 0 ? _b : 'HEAD';
602
- const pathFilter = hasNonEmptyTrimmed(options.includeFolders) || hasNonEmptyTrimmed(options.excludeFolders)
677
+ const to = (_b = options.to) !== null && _b !== void 0 ? _b : "HEAD";
678
+ const pathFilter = hasNonEmptyTrimmed(options.includeFolders) ||
679
+ hasNonEmptyTrimmed(options.excludeFolders)
603
680
  ? {
604
681
  includeFolders: options.includeFolders,
605
682
  excludeFolders: options.excludeFolders,
@@ -608,10 +685,17 @@ function summarizeGitDiff(options) {
608
685
  const allCommits = yield getCommits(git, from, to);
609
686
  const filteredCommits = filterCommitsByMessageRegexes(allCommits, options.commitMessageIncludeRegexes, options.commitMessageExcludeRegexes);
610
687
  const filterByCommits = shouldFilterByCommits(allCommits, filteredCommits, options);
688
+ const rangeQuery = {
689
+ from,
690
+ to,
691
+ commits: filteredCommits,
692
+ filterByCommits,
693
+ pathFilter,
694
+ };
611
695
  const [diffText, fileNames, diffSummary] = yield Promise.all([
612
- getDiff(git, from, to, filteredCommits, filterByCommits, pathFilter),
613
- getChangedFiles(git, from, to, filteredCommits, filterByCommits, pathFilter),
614
- getDiffSummary(git, from, to, filteredCommits, filterByCommits, pathFilter),
696
+ getDiff(git, rangeQuery),
697
+ getChangedFiles(git, rangeQuery),
698
+ getDiffSummary(git, rangeQuery),
615
699
  ]);
616
700
  const summarizeFlags = {
617
701
  from,
@@ -623,7 +707,14 @@ function summarizeGitDiff(options) {
623
707
  commitMessageIncludeRegexes: options.commitMessageIncludeRegexes,
624
708
  commitMessageExcludeRegexes: options.commitMessageExcludeRegexes,
625
709
  };
626
- return generateSummary(diffText, fileNames, filteredCommits, summarizeFlags, options.openAiClientProvider, diffSummary);
710
+ return generateSummary({
711
+ diffText,
712
+ fileNames,
713
+ commits: filteredCommits,
714
+ flags: summarizeFlags,
715
+ openAiClientProvider: options.openAiClientProvider,
716
+ diffSummary,
717
+ });
627
718
  });
628
719
  }
629
720