@kud/ai-conventional-commit-cli 1.0.1 → 1.1.0

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/README.md CHANGED
@@ -98,6 +98,9 @@ git add .
98
98
  # Generate a single commit suggestion
99
99
  ai-conventional-commit
100
100
 
101
+ # Auto-confirm without prompting (useful for automation)
102
+ ai-conventional-commit --yes
103
+
101
104
  # Propose multiple commits (interactive confirm + real selective staging)
102
105
  ai-conventional-commit split
103
106
 
@@ -160,6 +163,7 @@ Helpful flags:
160
163
 
161
164
  - `--style <standard|gitmoji|gitmoji-pure>`
162
165
  - `--model <provider/name>` (override)
166
+ - `-y, --yes` (auto-confirm without prompting)
163
167
  - `--scope <scope>` (refine)
164
168
  - `--shorter` / `--longer`
165
169
 
@@ -274,7 +278,7 @@ ai-conventional-commit models --interactive --save # pick + persist globally
274
278
  ai-conventional-commit models --current # show active model + source
275
279
  ```
276
280
 
277
- `MODEL`, `PRIVACY`, `STYLE`, `STYLE_SAMPLES`, `MAX_TOKENS`, `MAX_FILE_LINES`, `VERBOSE`, `MODEL_TIMEOUT_MS`, `DEBUG`, `PRINT_LOGS`, `DEBUG_PROVIDER=mock`.
281
+ `MODEL`, `PRIVACY`, `STYLE`, `STYLE_SAMPLES`, `MAX_TOKENS`, `MAX_FILE_LINES`, `VERBOSE`, `YES`, `MODEL_TIMEOUT_MS`, `DEBUG`, `PRINT_LOGS`, `DEBUG_PROVIDER=mock`.
278
282
 
279
283
  **Note:** `skipFilePatterns` cannot be set via environment variable - use config file or accept defaults.
280
284
 
@@ -0,0 +1,122 @@
1
+ // src/config.ts
2
+ import { cosmiconfig } from "cosmiconfig";
3
+ import { resolve, dirname, join } from "path";
4
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
5
+ import { homedir } from "os";
6
+ var DEFAULTS = {
7
+ model: process.env.AICC_MODEL || "github-copilot/gpt-4.1",
8
+ privacy: process.env.AICC_PRIVACY || "low",
9
+ style: process.env.AICC_STYLE || "standard",
10
+ styleSamples: parseInt(process.env.AICC_STYLE_SAMPLES || "120", 10),
11
+ maxTokens: parseInt(process.env.AICC_MAX_TOKENS || "512", 10),
12
+ maxFileLines: parseInt(process.env.AICC_MAX_FILE_LINES || "1000", 10),
13
+ skipFilePatterns: [
14
+ "**/package-lock.json",
15
+ "**/yarn.lock",
16
+ "**/pnpm-lock.yaml",
17
+ "**/bun.lockb",
18
+ "**/composer.lock",
19
+ "**/Gemfile.lock",
20
+ "**/Cargo.lock",
21
+ "**/poetry.lock",
22
+ "**/*.d.ts",
23
+ "**/dist/**",
24
+ "**/build/**",
25
+ "**/.next/**",
26
+ "**/out/**",
27
+ "**/coverage/**",
28
+ "**/*.min.js",
29
+ "**/*.min.css",
30
+ "**/*.map"
31
+ ],
32
+ cacheDir: ".git/.aicc-cache",
33
+ plugins: [],
34
+ verbose: process.env.AICC_VERBOSE === "true",
35
+ yes: process.env.AICC_YES === "true"
36
+ };
37
+ function getGlobalConfigPath() {
38
+ const base = process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
39
+ return resolve(base, "ai-conventional-commit-cli", "aicc.json");
40
+ }
41
+ function saveGlobalConfig(partial) {
42
+ const filePath = getGlobalConfigPath();
43
+ const dir = dirname(filePath);
44
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
45
+ let existing = {};
46
+ if (existsSync(filePath)) {
47
+ try {
48
+ existing = JSON.parse(readFileSync(filePath, "utf8")) || {};
49
+ } catch (e) {
50
+ if (process.env.AICC_VERBOSE === "true") {
51
+ console.error("[ai-cc] Failed to parse existing global config, overwriting.");
52
+ }
53
+ }
54
+ }
55
+ const merged = { ...existing, ...partial };
56
+ writeFileSync(filePath, JSON.stringify(merged, null, 2) + "\n", "utf8");
57
+ return filePath;
58
+ }
59
+ async function loadConfig(cwd = process.cwd()) {
60
+ return (await loadConfigDetailed(cwd)).config;
61
+ }
62
+ async function loadConfigDetailed(cwd = process.cwd()) {
63
+ let globalCfg = {};
64
+ const globalPath = getGlobalConfigPath();
65
+ if (existsSync(globalPath)) {
66
+ try {
67
+ globalCfg = JSON.parse(readFileSync(globalPath, "utf8")) || {};
68
+ } catch (e) {
69
+ if (process.env.AICC_VERBOSE === "true") {
70
+ console.error("[ai-cc] Failed to parse global config, ignoring.");
71
+ }
72
+ }
73
+ }
74
+ const explorer = cosmiconfig("aicc");
75
+ const result = await explorer.search(cwd);
76
+ const projectCfg = result?.config || {};
77
+ const envCfg = {};
78
+ if (process.env.AICC_MODEL) envCfg.model = process.env.AICC_MODEL;
79
+ if (process.env.AICC_PRIVACY) envCfg.privacy = process.env.AICC_PRIVACY;
80
+ if (process.env.AICC_STYLE) envCfg.style = process.env.AICC_STYLE;
81
+ if (process.env.AICC_STYLE_SAMPLES)
82
+ envCfg.styleSamples = parseInt(process.env.AICC_STYLE_SAMPLES, 10);
83
+ if (process.env.AICC_MAX_TOKENS) envCfg.maxTokens = parseInt(process.env.AICC_MAX_TOKENS, 10);
84
+ if (process.env.AICC_MAX_FILE_LINES)
85
+ envCfg.maxFileLines = parseInt(process.env.AICC_MAX_FILE_LINES, 10);
86
+ if (process.env.AICC_VERBOSE) envCfg.verbose = process.env.AICC_VERBOSE === "true";
87
+ if (process.env.AICC_YES) envCfg.yes = process.env.AICC_YES === "true";
88
+ const merged = {
89
+ ...DEFAULTS,
90
+ ...globalCfg,
91
+ ...projectCfg,
92
+ ...envCfg
93
+ };
94
+ merged.plugins = (merged.plugins || []).filter((p) => {
95
+ const abs = resolve(cwd, p);
96
+ return existsSync(abs);
97
+ });
98
+ if (!merged.skipFilePatterns) {
99
+ merged.skipFilePatterns = DEFAULTS.skipFilePatterns;
100
+ }
101
+ const sources = Object.keys(merged).reduce((acc, key) => {
102
+ const k = key;
103
+ let src = "default";
104
+ if (k in globalCfg) src = "global";
105
+ if (k in projectCfg) src = "project";
106
+ if (k in envCfg) src = "env";
107
+ acc[k] = src;
108
+ return acc;
109
+ }, {});
110
+ const withMeta = Object.assign(merged, { _sources: sources });
111
+ return {
112
+ config: withMeta,
113
+ raw: { defaults: DEFAULTS, global: globalCfg, project: projectCfg, env: envCfg }
114
+ };
115
+ }
116
+
117
+ export {
118
+ getGlobalConfigPath,
119
+ saveGlobalConfig,
120
+ loadConfig,
121
+ loadConfigDetailed
122
+ };
@@ -0,0 +1,12 @@
1
+ import {
2
+ getGlobalConfigPath,
3
+ loadConfig,
4
+ loadConfigDetailed,
5
+ saveGlobalConfig
6
+ } from "./chunk-F3BOAVBY.js";
7
+ export {
8
+ getGlobalConfigPath,
9
+ loadConfig,
10
+ loadConfigDetailed,
11
+ saveGlobalConfig
12
+ };
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  loadConfig
4
- } from "./chunk-HJR5M6U7.js";
4
+ } from "./chunk-F3BOAVBY.js";
5
5
  import {
6
6
  OpenCodeProvider,
7
7
  abortMessage,
@@ -323,7 +323,7 @@ async function runGenerate(config) {
323
323
  errorLines.forEach((l) => borderLine(l));
324
324
  }
325
325
  borderLine();
326
- const yn = await selectYesNo();
326
+ const yn = config.yes || await selectYesNo();
327
327
  if (!yn) {
328
328
  borderLine();
329
329
  abortMessage();
@@ -506,7 +506,7 @@ async function runSplit(config, desired) {
506
506
  }
507
507
  });
508
508
  borderLine();
509
- const ok = await select2({
509
+ const ok = config.yes || await select2({
510
510
  message: "Use the commits?",
511
511
  choices: [
512
512
  { name: "Yes", value: true },
@@ -656,7 +656,7 @@ async function runRefine(config, options) {
656
656
  titleColor: (s) => chalk3.yellow(s)
657
657
  });
658
658
  borderLine();
659
- const { ok } = await inquirer.prompt([
659
+ const ok = config.yes || (await inquirer.prompt([
660
660
  {
661
661
  type: "list",
662
662
  name: "ok",
@@ -667,7 +667,7 @@ async function runRefine(config, options) {
667
667
  ],
668
668
  default: 0
669
669
  }
670
- ]);
670
+ ])).ok;
671
671
  if (!ok) {
672
672
  borderLine();
673
673
  abortMessage();
@@ -732,10 +732,14 @@ Refine Options:
732
732
  required: false,
733
733
  description: "Model provider/name (e.g. github-copilot/gpt-4.1)"
734
734
  });
735
+ yes = Option.Boolean("-y,--yes", false, {
736
+ description: "Auto-confirm commit without prompting"
737
+ });
735
738
  async execute() {
736
739
  const config = await loadConfig();
737
740
  if (this.style) config.style = this.style;
738
741
  if (this.model) config.model = this.model;
742
+ if (this.yes) config.yes = this.yes;
739
743
  await runGenerate(config);
740
744
  }
741
745
  };
@@ -761,12 +765,16 @@ var GenerateCommand = class extends Command {
761
765
  required: false,
762
766
  description: "Model provider/name (e.g. github-copilot/gpt-4.1)"
763
767
  });
768
+ yes = Option.Boolean("-y,--yes", false, {
769
+ description: "Auto-confirm commit without prompting"
770
+ });
764
771
  async execute() {
765
772
  const config = await loadConfig();
766
773
  if (this.style) {
767
774
  config.style = this.style;
768
775
  }
769
776
  if (this.model) config.model = this.model;
777
+ if (this.yes) config.yes = this.yes;
770
778
  await runGenerate(config);
771
779
  }
772
780
  };
@@ -792,10 +800,14 @@ var SplitCommand = class extends Command {
792
800
  required: false,
793
801
  description: "Model provider/name override"
794
802
  });
803
+ yes = Option.Boolean("-y,--yes", false, {
804
+ description: "Auto-confirm commit without prompting"
805
+ });
795
806
  async execute() {
796
807
  const config = await loadConfig();
797
808
  if (this.style) config.style = this.style;
798
809
  if (this.model) config.model = this.model;
810
+ if (this.yes) config.yes = this.yes;
799
811
  await runSplit(config, this.max ? parseInt(this.max, 10) : void 0);
800
812
  }
801
813
  };
@@ -823,10 +835,14 @@ var RefineCommand = class extends Command {
823
835
  required: false,
824
836
  description: "Model provider/name override"
825
837
  });
838
+ yes = Option.Boolean("-y,--yes", false, {
839
+ description: "Auto-confirm commit without prompting"
840
+ });
826
841
  async execute() {
827
842
  const config = await loadConfig();
828
843
  if (this.style) config.style = this.style;
829
844
  if (this.model) config.model = this.model;
845
+ if (this.yes) config.yes = this.yes;
830
846
  await runRefine(config, {
831
847
  shorter: this.shorter,
832
848
  longer: this.longer,
@@ -857,7 +873,7 @@ var ModelsCommand = class extends Command {
857
873
  });
858
874
  async execute() {
859
875
  if (this.current) {
860
- const { loadConfigDetailed } = await import("./config-C3S4LWLD.js");
876
+ const { loadConfigDetailed } = await import("./config-RHGCFLHQ.js");
861
877
  const { config } = await loadConfigDetailed();
862
878
  this.context.stdout.write(`${config.model} (source: ${config._sources.model})
863
879
  `);
@@ -901,7 +917,7 @@ var ModelsCommand = class extends Command {
901
917
  this.context.stdout.write(model + "\n");
902
918
  if (this.save) {
903
919
  try {
904
- const { saveGlobalConfig } = await import("./config-C3S4LWLD.js");
920
+ const { saveGlobalConfig } = await import("./config-RHGCFLHQ.js");
905
921
  const path = saveGlobalConfig({ model });
906
922
  this.context.stdout.write(`Saved as default model in ${path}
907
923
  `);
@@ -936,7 +952,7 @@ var ConfigShowCommand = class extends Command {
936
952
  });
937
953
  json = Option.Boolean("--json", false, { description: "Output JSON including _sources" });
938
954
  async execute() {
939
- const { loadConfigDetailed } = await import("./config-C3S4LWLD.js");
955
+ const { loadConfigDetailed } = await import("./config-RHGCFLHQ.js");
940
956
  const { config, raw } = await loadConfigDetailed();
941
957
  if (this.json) {
942
958
  this.context.stdout.write(JSON.stringify({ config, raw }, null, 2) + "\n");
@@ -961,7 +977,7 @@ var ConfigGetCommand = class extends Command {
961
977
  key = Option.String();
962
978
  withSource = Option.Boolean("--with-source", false, { description: "Append source label" });
963
979
  async execute() {
964
- const { loadConfigDetailed } = await import("./config-C3S4LWLD.js");
980
+ const { loadConfigDetailed } = await import("./config-RHGCFLHQ.js");
965
981
  const { config } = await loadConfigDetailed();
966
982
  const key = this.key;
967
983
  if (!(key in config)) {
@@ -983,17 +999,18 @@ var ConfigSetCommand = class extends Command {
983
999
  static paths = [[`config`, `set`]];
984
1000
  static usage = Command.Usage({
985
1001
  description: "Set and persist a global configuration key.",
986
- details: "Writes to the global aicc.json (XDG config). Accepts JSON for complex values. Only allowed keys: model, style, privacy, styleSamples, maxTokens, verbose.",
1002
+ details: "Writes to the global aicc.json (XDG config). Accepts JSON for complex values. Only allowed keys: model, style, privacy, styleSamples, maxTokens, verbose, yes.",
987
1003
  examples: [
988
1004
  ["Set default model", "ai-conventional-commit config set model github-copilot/gpt-4.1"],
989
1005
  ["Set style to gitmoji", "ai-conventional-commit config set style gitmoji"],
990
- ["Enable verbose mode", "ai-conventional-commit config set verbose true"]
1006
+ ["Enable verbose mode", "ai-conventional-commit config set verbose true"],
1007
+ ["Auto-confirm commits", "ai-conventional-commit config set yes true"]
991
1008
  ]
992
1009
  });
993
1010
  key = Option.String();
994
1011
  value = Option.String();
995
1012
  async execute() {
996
- const allowed = /* @__PURE__ */ new Set(["model", "style", "privacy", "styleSamples", "maxTokens", "verbose"]);
1013
+ const allowed = /* @__PURE__ */ new Set(["model", "style", "privacy", "styleSamples", "maxTokens", "verbose", "yes"]);
997
1014
  if (!allowed.has(this.key)) {
998
1015
  this.context.stderr.write(`Cannot set key: ${this.key}
999
1016
  `);
@@ -1009,7 +1026,7 @@ var ConfigSetCommand = class extends Command {
1009
1026
  } catch {
1010
1027
  }
1011
1028
  }
1012
- const { saveGlobalConfig } = await import("./config-C3S4LWLD.js");
1029
+ const { saveGlobalConfig } = await import("./config-RHGCFLHQ.js");
1013
1030
  const path = saveGlobalConfig({ [this.key]: parsed });
1014
1031
  this.context.stdout.write(`Saved ${this.key} to ${path}
1015
1032
  `);
@@ -1029,11 +1046,15 @@ var RewordCommand = class extends Command {
1029
1046
  hash = Option.String({ required: false });
1030
1047
  style = Option.String("--style", { required: false, description: "Title style override" });
1031
1048
  model = Option.String("-m,--model", { required: false, description: "Model override" });
1049
+ yes = Option.Boolean("-y,--yes", false, {
1050
+ description: "Auto-confirm commit without prompting"
1051
+ });
1032
1052
  async execute() {
1033
- const { runReword } = await import("./reword-Q7MES34W.js");
1053
+ const { runReword } = await import("./reword-7GG233AE.js");
1034
1054
  const config = await loadConfig();
1035
1055
  if (this.style) config.style = this.style;
1036
1056
  if (this.model) config.model = this.model;
1057
+ if (this.yes) config.yes = this.yes;
1037
1058
  let target = this.hash;
1038
1059
  if (!target) {
1039
1060
  try {
@@ -0,0 +1,212 @@
1
+ import {
2
+ OpenCodeProvider,
3
+ abortMessage,
4
+ animateHeaderBase,
5
+ borderLine,
6
+ buildRefineMessages,
7
+ createPhasedSpinner,
8
+ extractJSON,
9
+ finalSuccess,
10
+ formatCommitTitle,
11
+ renderCommitBlock,
12
+ sectionTitle
13
+ } from "./chunk-WW3N76NL.js";
14
+
15
+ // src/workflow/reword.ts
16
+ import chalk from "chalk";
17
+ import ora from "ora";
18
+ import inquirer from "inquirer";
19
+ import { simpleGit } from "simple-git";
20
+ var git = simpleGit();
21
+ async function getCommitMessage(hash) {
22
+ try {
23
+ const raw = await git.show([`${hash}`, "--quiet", "--format=%P%n%B"]);
24
+ const lines = raw.split("\n");
25
+ const parentsLine = lines.shift() || "";
26
+ const parents = parentsLine.trim().length ? parentsLine.trim().split(/\s+/) : [];
27
+ const message = lines.join("\n").trim();
28
+ if (!message) return null;
29
+ const [first, ...rest] = message.split("\n");
30
+ const body = rest.join("\n").trim() || void 0;
31
+ return { title: first, body, parents };
32
+ } catch {
33
+ return null;
34
+ }
35
+ }
36
+ async function isAncestor(ancestor, head) {
37
+ try {
38
+ const mb = (await git.raw(["merge-base", ancestor, head])).trim();
39
+ const anc = (await git.revparse([ancestor])).trim();
40
+ return mb === anc;
41
+ } catch {
42
+ return false;
43
+ }
44
+ }
45
+ async function runReword(config, hash) {
46
+ const startedAt = Date.now();
47
+ const commit = await getCommitMessage(hash);
48
+ if (!commit) {
49
+ console.log(`Commit not found: ${hash}`);
50
+ return;
51
+ }
52
+ if (commit.parents.length > 1) {
53
+ console.log("Refusing to reword a merge commit (multiple parents).");
54
+ return;
55
+ }
56
+ if (process.stdout.isTTY) {
57
+ await animateHeaderBase("ai-conventional-commit", config.model);
58
+ borderLine();
59
+ }
60
+ sectionTitle("Original commit");
61
+ borderLine(chalk.yellow(commit.title));
62
+ if (commit.body) {
63
+ commit.body.split("\n").forEach((l) => l.trim().length ? borderLine(l) : borderLine());
64
+ }
65
+ borderLine();
66
+ const instructions = [
67
+ "Improve clarity & conformity to Conventional Commits while preserving meaning."
68
+ ];
69
+ const syntheticPlan = {
70
+ commits: [
71
+ {
72
+ title: commit.title,
73
+ body: commit.body,
74
+ score: 0,
75
+ reasons: []
76
+ }
77
+ ]
78
+ };
79
+ const provider = new OpenCodeProvider(config.model);
80
+ const phased = createPhasedSpinner(ora);
81
+ let refined = null;
82
+ try {
83
+ phased.phase("Preparing prompt");
84
+ const messages = buildRefineMessages({
85
+ originalPlan: syntheticPlan,
86
+ index: 0,
87
+ instructions,
88
+ config
89
+ });
90
+ phased.phase("Calling model");
91
+ const raw = await provider.chat(messages, { maxTokens: config.maxTokens });
92
+ phased.phase("Parsing response");
93
+ refined = await extractJSON(raw);
94
+ } catch (e) {
95
+ phased.spinner.fail("Reword failed: " + (e?.message || e));
96
+ return;
97
+ }
98
+ phased.stop();
99
+ if (!refined || !refined.commits.length) {
100
+ console.log("No refined commit produced.");
101
+ return;
102
+ }
103
+ const candidate = refined.commits[0];
104
+ candidate.title = formatCommitTitle(candidate.title, {
105
+ allowGitmoji: config.style === "gitmoji" || config.style === "gitmoji-pure",
106
+ mode: config.style
107
+ });
108
+ sectionTitle("Proposed commit");
109
+ renderCommitBlock({
110
+ title: chalk.yellow(candidate.title),
111
+ body: candidate.body,
112
+ hideMessageLabel: true
113
+ });
114
+ borderLine();
115
+ const resolvedHash = (await git.revparse([hash])).trim();
116
+ const headHash = (await git.revparse(["HEAD"])).trim();
117
+ const isHead = headHash === resolvedHash || headHash.startsWith(resolvedHash);
118
+ const ok = config.yes || (await inquirer.prompt([
119
+ {
120
+ type: "list",
121
+ name: "ok",
122
+ message: isHead ? "Amend HEAD with this message?" : "Apply rewrite (history will change)?",
123
+ choices: [
124
+ { name: "Yes", value: true },
125
+ { name: "No", value: false }
126
+ ],
127
+ default: 0
128
+ }
129
+ ])).ok;
130
+ if (!ok) {
131
+ borderLine();
132
+ abortMessage();
133
+ return;
134
+ }
135
+ const full = candidate.body ? `${candidate.title}
136
+
137
+ ${candidate.body}` : candidate.title;
138
+ if (isHead) {
139
+ try {
140
+ await git.commit(full, { "--amend": null });
141
+ } catch (e) {
142
+ borderLine("Failed to amend HEAD: " + (e?.message || e));
143
+ borderLine();
144
+ abortMessage();
145
+ return;
146
+ }
147
+ borderLine();
148
+ finalSuccess({ count: 1, startedAt });
149
+ return;
150
+ }
151
+ const ancestorOk = await isAncestor(resolvedHash, headHash);
152
+ if (!ancestorOk) {
153
+ borderLine("Selected commit is not an ancestor of HEAD.");
154
+ borderLine("Cannot safely rewrite automatically.");
155
+ borderLine();
156
+ abortMessage();
157
+ return;
158
+ }
159
+ let mergesRange = "";
160
+ try {
161
+ mergesRange = (await git.raw(["rev-list", "--merges", `${resolvedHash}..HEAD`])).trim();
162
+ } catch {
163
+ }
164
+ if (mergesRange) {
165
+ sectionTitle("Unsafe automatic rewrite");
166
+ borderLine("Merge commits detected between target and HEAD.");
167
+ borderLine("Falling back to manual instructions (preserving previous behavior).");
168
+ borderLine();
169
+ sectionTitle("Apply manually");
170
+ borderLine(`1. git rebase -i ${resolvedHash}~1 --reword`);
171
+ borderLine("2. Mark the line as reword if needed.");
172
+ borderLine("3. Replace the message with:");
173
+ borderLine();
174
+ borderLine(candidate.title);
175
+ if (candidate.body) candidate.body.split("\n").forEach((l) => borderLine(l || void 0));
176
+ borderLine();
177
+ abortMessage();
178
+ return;
179
+ }
180
+ try {
181
+ const tree = (await git.raw(["show", "-s", "--format=%T", resolvedHash])).trim();
182
+ const parent = commit.parents[0];
183
+ const args = ["commit-tree", tree];
184
+ if (parent) args.push("-p", parent);
185
+ args.push("-m", full);
186
+ const newHash = (await git.raw(args)).trim();
187
+ const currentBranch = (await git.revparse(["--abbrev-ref", "HEAD"])).trim();
188
+ const rebaseTarget = currentBranch === "HEAD" ? "HEAD" : currentBranch;
189
+ await git.raw(["rebase", "--onto", newHash, resolvedHash, rebaseTarget]);
190
+ const afterBranch = (await git.revparse(["--abbrev-ref", "HEAD"])).trim();
191
+ if (afterBranch === "HEAD" && rebaseTarget !== "HEAD") {
192
+ try {
193
+ await git.checkout([rebaseTarget]);
194
+ } catch {
195
+ }
196
+ }
197
+ sectionTitle("Updated commit");
198
+ borderLine(`Rewrote ${resolvedHash.slice(0, 7)} \u2192 ${newHash.slice(0, 7)}`);
199
+ renderCommitBlock({ title: candidate.title, body: candidate.body, hideMessageLabel: true });
200
+ borderLine();
201
+ finalSuccess({ count: 1, startedAt });
202
+ } catch (e) {
203
+ borderLine("Automatic rewrite failed: " + (e?.message || e));
204
+ borderLine("If a rebase is in progress, resolve conflicts then run: git rebase --continue");
205
+ borderLine("Or abort with: git rebase --abort");
206
+ borderLine();
207
+ abortMessage();
208
+ }
209
+ }
210
+ export {
211
+ runReword
212
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kud/ai-conventional-commit-cli",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
5
  "description": "Opinionated, style-aware AI assistant for crafting and splitting git commits (opencode-based, provider-agnostic).",
6
6
  "bin": {