@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 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.3",
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
- debug("resetStaging: git reset HEAD");
999
- await execa("git", ["reset", "HEAD"]);
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
- const { stdout } = await execa("git", ["rev-parse", "HEAD"]);
1003
- return stdout.trim();
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(validated, included)) {
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 (groups.length === 0) return false;
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 safeGetHead();
2090
+ const headBefore = await getHead();
2085
2091
  const result = await attemptCommit(message);
2086
- const headAfter = await safeGetHead();
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 safeGetHead();
2115
+ const headBefore = await getHead();
2110
2116
  const result = await attemptCommit(flags.message);
2111
- const headAfter = await safeGetHead();
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 safeGetHead();
2164
+ const headBefore = await getHead();
2159
2165
  const result = await attemptCommit(message);
2160
- const headAfter = await safeGetHead();
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 safeGetHead();
2236
+ const headBefore = await getHead();
2231
2237
  const result = await attemptCommit(message);
2232
- const headAfter = await safeGetHead();
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