@hifullmoon/aicommit 2.6.6 → 2.6.7
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/CHANGELOG.md +12 -1
- package/docs/distribution.md +1 -1
- package/package.json +1 -1
- package/src/local-analysis.js +50 -6
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,16 @@ This file lists notable user-facing changes. Internal refactors, test-only chang
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [2.6.7] - 2026-10-05
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Large-change local analysis now selects bounded excerpts across change hunks, prioritizing behavior changes over imports and comments.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- Local change summaries now fit the complete commit request input budget, including system policy, repository context, and JSON envelopes.
|
|
16
|
+
|
|
7
17
|
## [2.6.6] - 2026-09-21
|
|
8
18
|
|
|
9
19
|
### Fixed
|
|
@@ -273,7 +283,8 @@ This file lists notable user-facing changes. Internal refactors, test-only chang
|
|
|
273
283
|
- Added file-level split planning and execution with Git-state concurrency checks.
|
|
274
284
|
- Added provider presets and user/project configuration boundaries.
|
|
275
285
|
|
|
276
|
-
[Unreleased]: https://github.com/hi-fullmoon/AICommit/compare/v2.6.
|
|
286
|
+
[Unreleased]: https://github.com/hi-fullmoon/AICommit/compare/v2.6.7...HEAD
|
|
287
|
+
[2.6.7]: https://github.com/hi-fullmoon/AICommit/releases/tag/v2.6.7
|
|
277
288
|
[2.6.6]: https://github.com/hi-fullmoon/AICommit/releases/tag/v2.6.6
|
|
278
289
|
[2.6.5]: https://github.com/hi-fullmoon/AICommit/releases/tag/v2.6.5
|
|
279
290
|
[2.6.4]: https://github.com/hi-fullmoon/AICommit/releases/tag/v2.6.4
|
package/docs/distribution.md
CHANGED
|
@@ -33,7 +33,7 @@ The release workflow uses npm Trusted Publishing without a long-lived `NPM_TOKEN
|
|
|
33
33
|
```bash
|
|
34
34
|
workdir=$(mktemp -d)
|
|
35
35
|
cd "$workdir"
|
|
36
|
-
npm install --package-lock-only @hifullmoon/aicommit@2.6.
|
|
36
|
+
npm install --package-lock-only @hifullmoon/aicommit@2.6.7
|
|
37
37
|
npm audit signatures
|
|
38
38
|
```
|
|
39
39
|
|
package/package.json
CHANGED
package/src/local-analysis.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { isLockFile, matchStripPattern } from './git.js';
|
|
3
3
|
import { ERROR_CATEGORIES, fail } from './errors.js';
|
|
4
|
+
import { buildCommitMessages } from './api.js';
|
|
5
|
+
import { estimateTokens } from './analysis-budget.js';
|
|
4
6
|
|
|
5
7
|
// These are evidence-selection rules, not exclusions from Git or secret scans.
|
|
6
8
|
export function isGeneratedFile(path) {
|
|
@@ -117,6 +119,16 @@ export function localOverview(config, facts) {
|
|
|
117
119
|
samples: [],
|
|
118
120
|
};
|
|
119
121
|
const cap = localInputBytes(config);
|
|
122
|
+
const fits = () => {
|
|
123
|
+
const text = JSON.stringify(data);
|
|
124
|
+
return (
|
|
125
|
+
Buffer.byteLength(text) <= cap &&
|
|
126
|
+
estimateTokens(JSON.stringify(buildCommitMessages(config, text).messages)) <=
|
|
127
|
+
(config.analysisBudget?.limits.chunkInputTokens ||
|
|
128
|
+
config.largeChange?.chunkInputTokens ||
|
|
129
|
+
12000)
|
|
130
|
+
);
|
|
131
|
+
};
|
|
120
132
|
let sampledFiles = 0;
|
|
121
133
|
const seen = new Set();
|
|
122
134
|
const ordered = ranked(facts);
|
|
@@ -145,17 +157,18 @@ export function localOverview(config, facts) {
|
|
|
145
157
|
if (data.samples.length >= 16) break;
|
|
146
158
|
const sample = summaryOf(fact);
|
|
147
159
|
data.samples.push(sample);
|
|
148
|
-
|
|
160
|
+
data.omittedGroups--;
|
|
161
|
+
if (!fits()) {
|
|
149
162
|
data.samples.pop();
|
|
163
|
+
data.omittedGroups++;
|
|
150
164
|
continue;
|
|
151
165
|
}
|
|
152
166
|
if (fact.evidence) sampledFiles++;
|
|
153
|
-
data.omittedGroups--;
|
|
154
167
|
}
|
|
155
168
|
// A tiny configured budget may not even fit aggregate metadata. Fail before
|
|
156
169
|
// sending rather than silently slicing JSON or dropping the uncertainty notice.
|
|
157
170
|
const text = JSON.stringify(data);
|
|
158
|
-
if (
|
|
171
|
+
if (!fits())
|
|
159
172
|
throw fail(
|
|
160
173
|
ERROR_CATEGORIES.CONFIG,
|
|
161
174
|
'Local change inventory does not fit the input budget. Increase largeChange.chunkInputTokens or maxDiffChars.',
|
|
@@ -172,30 +185,61 @@ export function analyzeLocally(config, capture, protect, previews) {
|
|
|
172
185
|
hash: createHash('sha256'),
|
|
173
186
|
evidence: '',
|
|
174
187
|
textual: false,
|
|
188
|
+
excerpts: [],
|
|
189
|
+
excerpt: '',
|
|
190
|
+
score: 0,
|
|
175
191
|
},
|
|
176
192
|
]),
|
|
177
193
|
);
|
|
194
|
+
const finishExcerpt = (record) => {
|
|
195
|
+
if (!record.excerpt) return;
|
|
196
|
+
record.excerpts.push({ text: record.excerpt, score: record.score });
|
|
197
|
+
record.excerpts.sort((a, b) => b.score - a.score);
|
|
198
|
+
record.excerpts.length = Math.min(record.excerpts.length, 8);
|
|
199
|
+
record.excerpt = '';
|
|
200
|
+
record.score = 0;
|
|
201
|
+
};
|
|
178
202
|
for (const unit of capture.units(protect, previews)) {
|
|
179
203
|
if (unit.metadataOnly) continue;
|
|
180
204
|
const record = records.get(unit.fileId);
|
|
181
205
|
// Tracked fragments carry Git headers; untracked fragments are plain text.
|
|
182
206
|
const section = unit.id.match(/S\d+(?=P\d+$)/)?.[0];
|
|
183
207
|
if (section && section !== record.section) {
|
|
208
|
+
finishExcerpt(record);
|
|
184
209
|
record.section = section;
|
|
185
210
|
record.inHunk = false;
|
|
186
211
|
}
|
|
187
212
|
for (const line of unit.text.split(/(?<=\n)/)) {
|
|
188
213
|
if (section) {
|
|
189
|
-
if (line.startsWith('@@'))
|
|
214
|
+
if (line.startsWith('@@')) {
|
|
215
|
+
finishExcerpt(record);
|
|
216
|
+
record.inHunk = true;
|
|
217
|
+
}
|
|
190
218
|
if (!record.inHunk || !/^[+-]/.test(line)) continue;
|
|
191
219
|
}
|
|
192
220
|
record.hash.update(line);
|
|
193
221
|
record.textual = true;
|
|
194
|
-
|
|
195
|
-
|
|
222
|
+
// Keep bounded candidates across hunks instead of spending the entire
|
|
223
|
+
// evidence allowance on imports at the beginning of a file.
|
|
224
|
+
const remaining = 450 - Buffer.byteLength(record.excerpt);
|
|
225
|
+
if (remaining > 3) {
|
|
226
|
+
record.excerpt += clip(line, remaining);
|
|
227
|
+
record.score = Math.max(
|
|
228
|
+
record.score,
|
|
229
|
+
/^[+-]\s*(?:import\b|export\s+\{|\/\/|\*|$)/.test(line) ? 1 : 4,
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
if (Buffer.byteLength(record.excerpt) >= 447) finishExcerpt(record);
|
|
196
233
|
}
|
|
197
234
|
}
|
|
198
235
|
const grouped = new Map();
|
|
236
|
+
for (const record of records.values()) {
|
|
237
|
+
finishExcerpt(record);
|
|
238
|
+
record.evidence = record.excerpts
|
|
239
|
+
.slice(0, 3)
|
|
240
|
+
.map((excerpt) => clip(excerpt.text, 300))
|
|
241
|
+
.join('');
|
|
242
|
+
}
|
|
199
243
|
for (const { file, hash, evidence, textual } of records.values()) {
|
|
200
244
|
const kind = category(file, config);
|
|
201
245
|
const module = moduleOf(file.addPaths?.at(-1) || file.path);
|