@hifullmoon/aicommit 2.6.3 → 2.6.4
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 +8 -1
- package/docs/distribution.md +1 -1
- package/package.json +1 -1
- package/src/change-analysis.js +31 -8
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@ This file lists notable user-facing changes. Internal refactors, test-only chang
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [2.6.4] - 2026-09-19
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Large split planning now sizes batches for the model response limit and preserves valid file groupings when the provider omits a group summary.
|
|
12
|
+
|
|
7
13
|
## [2.6.3] - 2026-09-15
|
|
8
14
|
|
|
9
15
|
### Changed
|
|
@@ -255,7 +261,8 @@ This file lists notable user-facing changes. Internal refactors, test-only chang
|
|
|
255
261
|
- Added file-level split planning and execution with Git-state concurrency checks.
|
|
256
262
|
- Added provider presets and user/project configuration boundaries.
|
|
257
263
|
|
|
258
|
-
[Unreleased]: https://github.com/hi-fullmoon/AICommit/compare/v2.6.
|
|
264
|
+
[Unreleased]: https://github.com/hi-fullmoon/AICommit/compare/v2.6.4...HEAD
|
|
265
|
+
[2.6.4]: https://github.com/hi-fullmoon/AICommit/releases/tag/v2.6.4
|
|
259
266
|
[2.6.3]: https://github.com/hi-fullmoon/AICommit/releases/tag/v2.6.3
|
|
260
267
|
[2.6.2]: https://github.com/hi-fullmoon/AICommit/releases/tag/v2.6.2
|
|
261
268
|
[2.6.1]: https://github.com/hi-fullmoon/AICommit/releases/tag/v2.6.1
|
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.4
|
|
37
37
|
npm audit signatures
|
|
38
38
|
```
|
|
39
39
|
|
package/package.json
CHANGED
package/src/change-analysis.js
CHANGED
|
@@ -351,17 +351,17 @@ async function jsonCall(config, instruction, items, validate = null, stream = nu
|
|
|
351
351
|
}
|
|
352
352
|
}
|
|
353
353
|
|
|
354
|
-
export function validatePartition(groups, ids) {
|
|
354
|
+
export function validatePartition(groups, ids, requireSummary = true) {
|
|
355
355
|
const remaining = new Set(ids);
|
|
356
356
|
if (!Array.isArray(groups) || !groups.length)
|
|
357
357
|
throw fail(ERROR_CATEGORIES.RESPONSE_FORMAT, 'Analysis returned an empty partition.');
|
|
358
358
|
for (const group of groups) {
|
|
359
359
|
if (
|
|
360
|
+
!group ||
|
|
360
361
|
!Array.isArray(group.ids) ||
|
|
361
362
|
!group.ids.length ||
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
group.summary.length > 2000
|
|
363
|
+
(requireSummary &&
|
|
364
|
+
(typeof group.summary !== 'string' || !group.summary.trim() || group.summary.length > 2000))
|
|
365
365
|
)
|
|
366
366
|
throw fail(ERROR_CATEGORIES.RESPONSE_FORMAT, 'Analysis returned an invalid group.');
|
|
367
367
|
for (const id of group.ids)
|
|
@@ -729,15 +729,23 @@ export async function planAnalyzedChanges(config, facts, coverage = null, stream
|
|
|
729
729
|
config.splitMaxDiffChars || 16000,
|
|
730
730
|
Math.floor(config.analysisBudget.limits.chunkInputTokens * 0.6),
|
|
731
731
|
);
|
|
732
|
+
// A model may keep every input as a separate commit. Limit candidate count
|
|
733
|
+
// by the response budget too, leaving room for a complete JSON plan.
|
|
734
|
+
const outputTokens = Math.min(2048, config.maxTokens || 1024);
|
|
735
|
+
const maxPlanItems = Math.max(
|
|
736
|
+
1,
|
|
737
|
+
Math.min(config.splitMaxPlanFiles || 100, Math.floor((outputTokens - 224) / 80)),
|
|
738
|
+
);
|
|
739
|
+
const batchConfig = { ...config, splitMaxPlanFiles: maxPlanItems };
|
|
732
740
|
for (let level = 0; level < 8; level++) {
|
|
733
741
|
const byId = new Map(candidates.map((x) => [x.id, x]));
|
|
734
742
|
const batches =
|
|
735
743
|
!deep && level === 0
|
|
736
|
-
? localPlanBatches(
|
|
744
|
+
? localPlanBatches(batchConfig, candidates, cap)
|
|
737
745
|
: packItems(
|
|
738
746
|
candidates.map(({ id, path, summary }) => ({ id, path, summary })),
|
|
739
747
|
cap,
|
|
740
|
-
|
|
748
|
+
maxPlanItems,
|
|
741
749
|
);
|
|
742
750
|
if (!deep && level === 0 && coverage) {
|
|
743
751
|
coverage.sampledFiles = batches.flat().filter((item) => item.representativeExcerpt).length;
|
|
@@ -769,10 +777,10 @@ export async function planAnalyzedChanges(config, facts, coverage = null, stream
|
|
|
769
777
|
};
|
|
770
778
|
const groups = await jsonCall(
|
|
771
779
|
config,
|
|
772
|
-
`Each commit message must follow this policy: ${JSON.stringify(policy)}.\nGroup related changes into logical commits, including related implementation and tests across directories. Return [{"ids":[input IDs],"summary":"factual combined change summary","subject":"commit subject","body":"optional commit body"}]. Assign every input ID exactly once. Do not merge unrelated changes just to reduce group count.${!deep && level === 0 ? ' Inputs are a compact local inventory, not full semantic summaries. One ID may represent multiple files and must remain atomic; use kind, module, status, fileCount, examples, and any representativeExcerpt conservatively.' : ''}`,
|
|
780
|
+
`Each commit message must follow this policy: ${JSON.stringify(policy)}.\nGroup related changes into logical commits, including related implementation and tests across directories. Return [{"ids":[input IDs],"summary":"factual combined change summary","subject":"commit subject","body":"optional commit body"}]. Assign every input ID exactly once. Do not merge unrelated changes just to reduce group count.${finalPlan ? ' The summary field is optional in this final plan.' : ''}${!deep && level === 0 ? ' Inputs are a compact local inventory, not full semantic summaries. One ID may represent multiple files and must remain atomic; use kind, module, status, fileCount, examples, and any representativeExcerpt conservatively.' : ''}`,
|
|
773
781
|
batch,
|
|
774
782
|
(candidate) => {
|
|
775
|
-
validatePartition(candidate, ids);
|
|
783
|
+
validatePartition(candidate, ids, false);
|
|
776
784
|
if (candidate.some((group) => typeof group.subject !== 'string' || !group.subject.trim()))
|
|
777
785
|
throw fail(
|
|
778
786
|
ERROR_CATEGORIES.RESPONSE_FORMAT,
|
|
@@ -782,8 +790,23 @@ export async function planAnalyzedChanges(config, facts, coverage = null, stream
|
|
|
782
790
|
batchStream,
|
|
783
791
|
);
|
|
784
792
|
for (const group of groups) {
|
|
793
|
+
const sourceSummary = group.ids
|
|
794
|
+
.map((id) => {
|
|
795
|
+
const fact = byId.get(id);
|
|
796
|
+
return (
|
|
797
|
+
fact.summary ||
|
|
798
|
+
`${fact.status || 'Changed'} ${fact.kind || 'files'} in ${fact.module || fact.path || 'repository'} (${fact.files.length} files)`
|
|
799
|
+
).slice(0, 160);
|
|
800
|
+
})
|
|
801
|
+
.join('; ');
|
|
785
802
|
next.push({
|
|
786
803
|
...group,
|
|
804
|
+
summary:
|
|
805
|
+
typeof group.summary === 'string' &&
|
|
806
|
+
group.summary.trim() &&
|
|
807
|
+
group.summary.length <= 2000
|
|
808
|
+
? group.summary
|
|
809
|
+
: sourceSummary.slice(0, 2000),
|
|
787
810
|
id: `L${level}G${next.length}`,
|
|
788
811
|
files: group.ids.flatMap((id) => byId.get(id).files),
|
|
789
812
|
});
|