@kyubiware/commit-mint 0.6.3 → 0.6.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.
- package/dist/cli.mjs +49 -30
- package/dist/cli.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -28,7 +28,7 @@ var __exportAll = (all, no_symbols) => {
|
|
|
28
28
|
//#region package.json
|
|
29
29
|
var package_default = {
|
|
30
30
|
name: "@kyubiware/commit-mint",
|
|
31
|
-
version: "0.6.
|
|
31
|
+
version: "0.6.5",
|
|
32
32
|
description: "🌿 A commit tool that actually handles hook failures",
|
|
33
33
|
type: "module",
|
|
34
34
|
bin: { "cmint": "./dist/cli.mjs" },
|
|
@@ -995,12 +995,28 @@ async function stageAll() {
|
|
|
995
995
|
await execa("git", ["add", "-A"]);
|
|
996
996
|
}
|
|
997
997
|
async function resetStaging() {
|
|
998
|
-
|
|
999
|
-
|
|
998
|
+
try {
|
|
999
|
+
debug("resetStaging: git reset HEAD");
|
|
1000
|
+
await execa("git", ["reset", "HEAD"]);
|
|
1001
|
+
} catch {
|
|
1002
|
+
debug("resetStaging: HEAD missing, falling back to git rm --cached");
|
|
1003
|
+
await execa("git", [
|
|
1004
|
+
"rm",
|
|
1005
|
+
"-r",
|
|
1006
|
+
"--cached",
|
|
1007
|
+
"--quiet",
|
|
1008
|
+
"."
|
|
1009
|
+
]);
|
|
1010
|
+
}
|
|
1000
1011
|
}
|
|
1001
1012
|
async function getHead() {
|
|
1002
|
-
|
|
1003
|
-
|
|
1013
|
+
try {
|
|
1014
|
+
const { stdout } = await execa("git", ["rev-parse", "HEAD"]);
|
|
1015
|
+
return stdout.trim();
|
|
1016
|
+
} catch {
|
|
1017
|
+
debug("getHead: HEAD does not exist (fresh repo)");
|
|
1018
|
+
return null;
|
|
1019
|
+
}
|
|
1004
1020
|
}
|
|
1005
1021
|
async function getStatusShort() {
|
|
1006
1022
|
const { stdout } = await execa("git", ["status", "--short"]);
|
|
@@ -1138,6 +1154,7 @@ function parseGroupingResponse(content) {
|
|
|
1138
1154
|
if (start !== -1 && end !== -1 && end > start) try {
|
|
1139
1155
|
const parsed = JSON.parse(cleaned.slice(start, end + 1));
|
|
1140
1156
|
if (Array.isArray(parsed)) {
|
|
1157
|
+
if (parsed.length === 0) return [];
|
|
1141
1158
|
const groups = parsed.map(coerceGroup).filter((g) => g !== null);
|
|
1142
1159
|
if (groups.length > 0) return groups;
|
|
1143
1160
|
}
|
|
@@ -1278,7 +1295,7 @@ async function generateGroups(files, apiKey, model, timeout, provider, proxy) {
|
|
|
1278
1295
|
debug("generateGroups: parsed %d raw groups", rawGroups.length);
|
|
1279
1296
|
let validated = validateGroups(rawGroups, included);
|
|
1280
1297
|
debug("generateGroups: %d validated groups", validated.length);
|
|
1281
|
-
if (isLowQualityGrouping(
|
|
1298
|
+
if (isLowQualityGrouping(rawGroups, included)) {
|
|
1282
1299
|
debug("generateGroups: low quality result, retrying with stricter prompt");
|
|
1283
1300
|
rawGroups = await callGroupingAI(client, resolvedModel, buildRetryGroupingPrompt(), userPrompt);
|
|
1284
1301
|
debug("generateGroups retry: parsed %d raw groups", rawGroups.length);
|
|
@@ -1317,7 +1334,8 @@ async function callGroupingAI(client, model, systemPrompt, userPrompt) {
|
|
|
1317
1334
|
/** Minimum file count where a single-group result is considered low quality */
|
|
1318
1335
|
const MIN_FILES_FOR_QUALITY_CHECK = 5;
|
|
1319
1336
|
function isLowQualityGrouping(groups, allFiles) {
|
|
1320
|
-
if (
|
|
1337
|
+
if (allFiles.length === 0) return false;
|
|
1338
|
+
if (groups.length === 0) return true;
|
|
1321
1339
|
if (allFiles.length < MIN_FILES_FOR_QUALITY_CHECK) return false;
|
|
1322
1340
|
return groups.length === 1;
|
|
1323
1341
|
}
|
|
@@ -2017,18 +2035,6 @@ function buildExcludedFilesMessage(files) {
|
|
|
2017
2035
|
//#endregion
|
|
2018
2036
|
//#region src/commands/agent.ts
|
|
2019
2037
|
/**
|
|
2020
|
-
* Wrapper around getHead() that returns "" on fresh repos with no commits.
|
|
2021
|
-
* `git rev-parse HEAD` fails with exit 128 on a brand-new repo, which would
|
|
2022
|
-
* crash the agent flow before the first commit can be made.
|
|
2023
|
-
*/
|
|
2024
|
-
async function safeGetHead() {
|
|
2025
|
-
try {
|
|
2026
|
-
return await getHead();
|
|
2027
|
-
} catch {
|
|
2028
|
-
return "";
|
|
2029
|
-
}
|
|
2030
|
-
}
|
|
2031
|
-
/**
|
|
2032
2038
|
* Headless agent command — orchestrates the entire commit flow without any TUI
|
|
2033
2039
|
* interaction. Emits structured JSON results to stdout, one per line. Returns
|
|
2034
2040
|
* control to the caller with `process.exitCode` set to one of the 7 documented
|
|
@@ -2081,16 +2087,16 @@ async function agentCommand(flags) {
|
|
|
2081
2087
|
if ("excludedFiles" in diffResult) {
|
|
2082
2088
|
debug("All staged files are excluded:", diffResult.excludedFiles);
|
|
2083
2089
|
const message = buildExcludedFilesMessage(diffResult.excludedFiles);
|
|
2084
|
-
const headBefore = await
|
|
2090
|
+
const headBefore = await getHead();
|
|
2085
2091
|
const result = await attemptCommit(message);
|
|
2086
|
-
const headAfter = await
|
|
2092
|
+
const headAfter = await getHead();
|
|
2087
2093
|
if (result.ok || headBefore !== headAfter) {
|
|
2088
2094
|
process.exitCode = EXIT_CODES.SUCCESS;
|
|
2089
2095
|
writeAgentResult({
|
|
2090
2096
|
status: "success",
|
|
2091
2097
|
commits: [{
|
|
2092
2098
|
message,
|
|
2093
|
-
hash: headAfter,
|
|
2099
|
+
hash: headAfter ?? "",
|
|
2094
2100
|
files: diffResult.excludedFiles
|
|
2095
2101
|
}]
|
|
2096
2102
|
});
|
|
@@ -2106,16 +2112,16 @@ async function agentCommand(flags) {
|
|
|
2106
2112
|
}
|
|
2107
2113
|
if (flags.message) {
|
|
2108
2114
|
debug("Using provided message:", flags.message);
|
|
2109
|
-
const headBefore = await
|
|
2115
|
+
const headBefore = await getHead();
|
|
2110
2116
|
const result = await attemptCommit(flags.message);
|
|
2111
|
-
const headAfter = await
|
|
2117
|
+
const headAfter = await getHead();
|
|
2112
2118
|
if (result.ok || headBefore !== headAfter) {
|
|
2113
2119
|
process.exitCode = EXIT_CODES.SUCCESS;
|
|
2114
2120
|
writeAgentResult({
|
|
2115
2121
|
status: "success",
|
|
2116
2122
|
commits: [{
|
|
2117
2123
|
message: flags.message,
|
|
2118
|
-
hash: headAfter,
|
|
2124
|
+
hash: headAfter ?? "",
|
|
2119
2125
|
files: diffResult.files
|
|
2120
2126
|
}]
|
|
2121
2127
|
});
|
|
@@ -2155,9 +2161,9 @@ async function agentCommand(flags) {
|
|
|
2155
2161
|
debug("Committing %d excluded files:", excluded.length, excluded);
|
|
2156
2162
|
await resetStaging();
|
|
2157
2163
|
await stageFiles(excluded);
|
|
2158
|
-
const headBefore = await
|
|
2164
|
+
const headBefore = await getHead();
|
|
2159
2165
|
const result = await attemptCommit(message);
|
|
2160
|
-
const headAfter = await
|
|
2166
|
+
const headAfter = await getHead();
|
|
2161
2167
|
if (!result.ok && headBefore === headAfter) debug("Excluded files commit failed, continuing without them");
|
|
2162
2168
|
}
|
|
2163
2169
|
if (included.length === 0) {
|
|
@@ -2227,13 +2233,13 @@ async function agentCommand(flags) {
|
|
|
2227
2233
|
return;
|
|
2228
2234
|
}
|
|
2229
2235
|
await saveCachedCommit(await getRepoRoot(), message);
|
|
2230
|
-
const headBefore = await
|
|
2236
|
+
const headBefore = await getHead();
|
|
2231
2237
|
const result = await attemptCommit(message);
|
|
2232
|
-
const headAfter = await
|
|
2238
|
+
const headAfter = await getHead();
|
|
2233
2239
|
if (result.ok || headBefore !== headAfter) {
|
|
2234
2240
|
commits.push({
|
|
2235
2241
|
message,
|
|
2236
|
-
hash: headAfter,
|
|
2242
|
+
hash: headAfter ?? "",
|
|
2237
2243
|
files: group.files,
|
|
2238
2244
|
groupName: group.name
|
|
2239
2245
|
});
|
|
@@ -2672,6 +2678,19 @@ async function runPreCommitChecks(changedFiles, noCheck) {
|
|
|
2672
2678
|
}
|
|
2673
2679
|
break;
|
|
2674
2680
|
}
|
|
2681
|
+
await restageFormatterModifications(stagedFileList);
|
|
2682
|
+
}
|
|
2683
|
+
/**
|
|
2684
|
+
* Re-stage staged files whose working-tree content diverged from the index after checks ran.
|
|
2685
|
+
* Signal: a file with both index and working-tree modifications has git status "MM".
|
|
2686
|
+
*/
|
|
2687
|
+
async function restageFormatterModifications(stagedFileList) {
|
|
2688
|
+
const checkedSet = new Set(stagedFileList);
|
|
2689
|
+
const modifiedByChecks = (await getChangedFiles()).filter((f) => checkedSet.has(f.path) && f.staged && f.status === "MM").map((f) => f.path);
|
|
2690
|
+
if (modifiedByChecks.length === 0) return;
|
|
2691
|
+
debug("Re-staging %d file(s) modified by checks", modifiedByChecks.length);
|
|
2692
|
+
await stageFiles(modifiedByChecks);
|
|
2693
|
+
log.info(`Re-staged ${modifiedByChecks.length} file${modifiedByChecks.length !== 1 ? "s" : ""} modified by checks`);
|
|
2675
2694
|
}
|
|
2676
2695
|
//#endregion
|
|
2677
2696
|
//#region src/commands/commit.ts
|