@kyubiware/commit-mint 0.7.4 → 0.7.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
@@ -29,7 +29,7 @@ var __exportAll = (all, no_symbols) => {
29
29
  //#region package.json
30
30
  var package_default = {
31
31
  name: "@kyubiware/commit-mint",
32
- version: "0.7.4",
32
+ version: "0.7.5",
33
33
  description: "🌿 AI-powered git commit tool — auto-group changed files, generate messages, run pre-commit checks",
34
34
  type: "module",
35
35
  bin: { "cmint": "./dist/cli.mjs" },
@@ -1866,25 +1866,65 @@ async function showRecoveryMenu(errors, onRetry, onSkipHooks, onRestage, message
1866
1866
  }
1867
1867
  //#endregion
1868
1868
  //#region src/ui/review-message.ts
1869
- async function reviewCommitMessage(message) {
1870
- const { select, text } = await import("@clack/prompts");
1869
+ async function handleEdit(message) {
1870
+ const { text } = await import("@clack/prompts");
1871
+ const edited = await text({
1872
+ message: "Edit commit message:",
1873
+ initialValue: message,
1874
+ validate: (v) => v?.trim() ? void 0 : "Message cannot be empty"
1875
+ });
1876
+ if (isCancel(edited)) {
1877
+ debug("User cancelled edit, returning to review menu");
1878
+ return null;
1879
+ }
1880
+ const newMessage = String(edited).trim();
1881
+ debug("Edited message:", newMessage);
1882
+ return newMessage;
1883
+ }
1884
+ async function handleRegenerate(regenerate) {
1885
+ const { log, text } = await import("@clack/prompts");
1886
+ const hint = await text({
1887
+ message: "Describe what this commit is about to guide regeneration:",
1888
+ validate: (v) => v?.trim() ? void 0 : "Hint cannot be empty"
1889
+ });
1890
+ if (isCancel(hint)) {
1891
+ debug("User cancelled hint entry, returning to review menu");
1892
+ return null;
1893
+ }
1894
+ const hintValue = String(hint).trim();
1895
+ debug("Regenerating with hint:", hintValue);
1896
+ try {
1897
+ const newMessage = await regenerate(hintValue);
1898
+ debug("Regenerated message:", newMessage);
1899
+ return newMessage;
1900
+ } catch (err) {
1901
+ const errMsg = err instanceof Error ? err.message : String(err);
1902
+ debug("Regeneration failed:", errMsg);
1903
+ log.warn(red(`Regeneration failed: ${errMsg}`));
1904
+ return null;
1905
+ }
1906
+ }
1907
+ async function reviewCommitMessage(message, options) {
1908
+ const { select } = await import("@clack/prompts");
1871
1909
  while (true) {
1910
+ const reviewOptions = [{
1911
+ label: "Use as-is",
1912
+ value: "use"
1913
+ }, {
1914
+ label: "Edit",
1915
+ value: "edit"
1916
+ }];
1917
+ if (options?.regenerate) reviewOptions.push({
1918
+ label: "Regenerate with hint",
1919
+ value: "regenerate"
1920
+ });
1921
+ reviewOptions.push({
1922
+ label: "Cancel",
1923
+ value: "cancel"
1924
+ });
1872
1925
  const review = await select({
1873
1926
  message: `Review commit message:\n\n ${bold(message)}\n`,
1874
- options: [
1875
- {
1876
- label: "Use as-is",
1877
- value: "use"
1878
- },
1879
- {
1880
- label: "Edit",
1881
- value: "edit"
1882
- },
1883
- {
1884
- label: "Cancel",
1885
- value: "cancel"
1886
- }
1887
- ]
1927
+ options: reviewOptions
1888
1928
  });
1889
1929
  if (isCancel(review) || review === "cancel") {
1890
1930
  debug("User cancelled at review step");
@@ -1894,17 +1934,8 @@ async function reviewCommitMessage(message) {
1894
1934
  debug("User accepted message");
1895
1935
  return message;
1896
1936
  }
1897
- if (review === "edit") {
1898
- debug("User chose to edit message");
1899
- const edited = await text({
1900
- message: "Edit commit message:",
1901
- initialValue: message,
1902
- validate: (v) => v?.trim() ? void 0 : "Message cannot be empty"
1903
- });
1904
- if (isCancel(edited)) continue;
1905
- message = String(edited).trim();
1906
- debug("Edited message:", message);
1907
- }
1937
+ if (review === "edit") message = await handleEdit(message) ?? message;
1938
+ if (review === "regenerate" && options?.regenerate) message = await handleRegenerate(options.regenerate) ?? message;
1908
1939
  }
1909
1940
  }
1910
1941
  //#endregion
@@ -2019,7 +2050,19 @@ async function runAutoGroupFlow(changedFiles, flags) {
2019
2050
  log.info(dim(message));
2020
2051
  if (flags.auto) debug("Auto mode: accepting generated message");
2021
2052
  else {
2022
- const reviewed = await reviewCommitMessage(message);
2053
+ const reviewed = await reviewCommitMessage(message, { regenerate: async (hint) => {
2054
+ const combinedHint = flags.hint ? `${flags.hint}\n${hint}` : hint;
2055
+ debug("Regenerating with combined hint:", combinedHint);
2056
+ s.start("Regenerating commit message...");
2057
+ try {
2058
+ const newMessage = await generateMessage(diffResult.diff, combinedHint);
2059
+ s.stop("Message regenerated");
2060
+ return newMessage;
2061
+ } catch (err) {
2062
+ s.stop(red("Regeneration failed"));
2063
+ throw err;
2064
+ }
2065
+ } });
2023
2066
  if (reviewed === null) {
2024
2067
  outro(dim("Cancelled."));
2025
2068
  return "cancelled";
@@ -3009,11 +3052,13 @@ async function runPreCommitChecks(changedFiles, noCheck) {
3009
3052
  }
3010
3053
  /**
3011
3054
  * Re-stage staged files whose working-tree content diverged from the index after checks ran.
3012
- * Signal: a file with both index and working-tree modifications has git status "MM".
3055
+ * Signals (git status --short, 2-char XY code):
3056
+ * "MM" — tracked file staged-modified, then reformatted on disk
3057
+ * "AM" — newly-added file staged, then reformatted on disk
3013
3058
  */
3014
3059
  async function restageFormatterModifications(stagedFileList) {
3015
3060
  const checkedSet = new Set(stagedFileList);
3016
- const modifiedByChecks = (await getChangedFiles()).filter((f) => checkedSet.has(f.path) && f.staged && f.status === "MM").map((f) => f.path);
3061
+ const modifiedByChecks = (await getChangedFiles()).filter((f) => checkedSet.has(f.path) && f.staged && (f.status === "MM" || f.status === "AM")).map((f) => f.path);
3017
3062
  if (modifiedByChecks.length === 0) return;
3018
3063
  debug("Re-staging %d file(s) modified by checks", modifiedByChecks.length);
3019
3064
  await stageFiles(modifiedByChecks);
@@ -3127,7 +3172,19 @@ async function commitCommand(flags, version) {
3127
3172
  }
3128
3173
  s.stop("Message generated");
3129
3174
  }
3130
- const reviewed = await reviewCommitMessage(message);
3175
+ const reviewed = await reviewCommitMessage(message, { regenerate: async (hint) => {
3176
+ const combinedHint = flags.hint ? `${flags.hint}\n${hint}` : hint;
3177
+ debug("Regenerating with combined hint:", combinedHint);
3178
+ s.start("Regenerating commit message...");
3179
+ try {
3180
+ const newMessage = await generateMessage(diffResult.diff, combinedHint);
3181
+ s.stop("Message regenerated");
3182
+ return newMessage;
3183
+ } catch (err) {
3184
+ s.stop(red("Regeneration failed"));
3185
+ throw err;
3186
+ }
3187
+ } });
3131
3188
  if (reviewed === null) {
3132
3189
  outro(dim("Cancelled."));
3133
3190
  return;