@kud/ai-conventional-commit-cli 0.2.1 → 0.4.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
@@ -11,8 +11,8 @@ Manual commit messages are noisy, inconsistent, and often miss context. ai-conve
11
11
  - Style fingerprinting (average title length, scope usage ratio, gitmoji ratio, top prefixes)
12
12
  - Single (`ai-conventional-commit` / `ai-conventional-commit generate`) or multi-commit planning (`ai-conventional-commit split`)
13
13
  - Refinement workflow (`ai-conventional-commit refine`) to iteratively tweak a prior result
14
- - Gitmoji modes: `--gitmoji` (emoji + type) and `--gitmoji-pure` (emoji only)
15
- - Reasoning depth control (`--reasoning low|medium|high`) influences explanation verbosity
14
+ - Gitmoji modes: `--gitmoji[-pure]` (plain adds emoji + type, pure removes type)
15
+
16
16
  - Privacy tiers governing diff detail sent to model
17
17
  - Title normalization + guardrails (length, conventional schema, secret heuristic)
18
18
  - Plugin system (transform + validate phases)
@@ -64,12 +64,61 @@ ai-conventional-commit split
64
64
  ai-conventional-commit --gitmoji
65
65
  # Pure gitmoji (emoji: subject)
66
66
  ai-conventional-commit --gitmoji-pure
67
- # Increase reasoning verbosity
68
- ai-conventional-commit --reasoning=high
67
+
68
+
69
69
  # Refine previous session’s first commit making it shorter
70
70
  ai-conventional-commit refine --shorter
71
71
  ```
72
72
 
73
+ ## Timed Output
74
+
75
+ Final success lines now include count + duration, e.g.:
76
+
77
+ ```
78
+ └ ✨ commit created in 850ms.
79
+ └ ✨ 3 commits created in 2.4s.
80
+ ```
81
+
82
+ Durations under 100ms show raw milliseconds; otherwise one decimal second precision.
83
+
84
+ ## Commands
85
+
86
+ | Command | Purpose |
87
+ | --------------------------------- | ------------------------------------------- |
88
+ | `ai-conventional-commit` | Generate single commit suggestion (default) |
89
+ | `ai-conventional-commit generate` | Same as root (explicit) |
90
+ | `ai-conventional-commit split` | Propose multiple commits (plan) |
91
+ | `ai-conventional-commit refine` | Refine last session result |
92
+
93
+ ### Help Output
94
+
95
+ ```text
96
+ $ ai-conventional-commit --help
97
+ ai-conventional-commit vX.Y.Z
98
+
99
+ Usage:
100
+ ai-conventional-commit [generate] [options] Generate a commit (default)
101
+ ai-conventional-commit split [options] Propose multiple commits
102
+ ai-conventional-commit refine [options] Refine last or indexed commit
103
+
104
+ Global Options:
105
+ -m, --model <provider/name> Override model provider/name
106
+ --gitmoji[-pure] Gitmoji modes (emoji + type / pure emoji only)
107
+ -h, --help Show this help
108
+ -V, --version Show version
109
+
110
+ Refine Options:
111
+ --shorter / --longer Adjust message length
112
+ --scope <scope> Add or replace scope
113
+ --emoji Add suitable gitmoji
114
+ --index <n> Select commit index
115
+
116
+ Examples:
117
+ ai-conventional-commit --gitmoji
118
+ ai-conventional-commit split --max 3
119
+ ai-conventional-commit refine --scope api --emoji
120
+ ```
121
+
73
122
  ## Gitmoji Modes
74
123
 
75
124
  | Mode | Example | Notes |
@@ -78,17 +127,7 @@ ai-conventional-commit refine --shorter
78
127
  | gitmoji | `✨ feat: add search box` | Emoji + conventional type retained |
79
128
  | gitmoji-pure | `✨: add search box` | Type removed, emoji acts as category |
80
129
 
81
- Enable via CLI flags (`--gitmoji` / `--gitmoji-pure`) or config (`gitmoji: true`, `gitmojiMode`).
82
-
83
- ## Reasoning Depth
84
-
85
- Controls verbosity of reasons array in the JSON returned by the model:
86
-
87
- - low: minimal
88
- - medium: balanced
89
- - high: detailed, more hunk-specific references
90
-
91
- Configured with `--reasoning` or in config (`reasoning`).
130
+ Enable via CLI flags (`--gitmoji` or `--gitmoji-pure`, shorthand `--gitmoji[-pure]`) or config (`gitmoji: true`, `gitmojiMode`).
92
131
 
93
132
  ## Privacy Modes
94
133
 
@@ -108,7 +147,7 @@ Uses cosmiconfig; supports JSON, YAML, etc. Example:
108
147
  "privacy": "low",
109
148
  "gitmoji": true,
110
149
  "gitmojiMode": "gitmoji",
111
- "reasoning": "low",
150
+
112
151
  "styleSamples": 120,
113
152
  "plugins": ["./src/sample-plugin/example-plugin.ts"],
114
153
  "maxTokens": 512
@@ -129,7 +168,6 @@ Uses cosmiconfig; supports JSON, YAML, etc. Example:
129
168
  - `AICC_DEBUG` (provider debug logs)
130
169
  - `AICC_PRINT_LOGS` (stream model raw output)
131
170
  - `AICC_DEBUG_PROVIDER=mock` (deterministic JSON response)
132
- - `AICC_REASONING` (low|medium|high)
133
171
 
134
172
  ## Conventional Commits Enforcement
135
173
 
package/dist/index.cjs CHANGED
@@ -27,7 +27,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
27
  var import_clipanion = require("clipanion");
28
28
 
29
29
  // src/workflow/generate.ts
30
- var import_chalk = __toESM(require("chalk"), 1);
30
+ var import_chalk2 = __toESM(require("chalk"), 1);
31
31
  var import_ora = __toESM(require("ora"), 1);
32
32
 
33
33
  // src/git.ts
@@ -218,9 +218,6 @@ var buildGenerationMessages = (opts) => {
218
218
  specLines.push(
219
219
  "Provide reasons array citing concrete diff elements: filenames, functions, tests, metrics."
220
220
  );
221
- specLines.push(
222
- `Reasoning Depth: ${config.reasoning || "low"} (low=minimal concise reasons, medium=balanced detail, high=very detailed). Adjust reasons verbosity accordingly.`
223
- );
224
221
  specLines.push("Return ONLY the JSON object. No surrounding text or markdown.");
225
222
  specLines.push("Do not add fields not listed in schema.");
226
223
  specLines.push("Never fabricate content not present or implied by the diff.");
@@ -570,6 +567,58 @@ var formatCommitTitle = (raw, opts) => {
570
567
  var import_node_fs = require("fs");
571
568
  var import_node_path2 = require("path");
572
569
  var import_inquirer = __toESM(require("inquirer"), 1);
570
+
571
+ // src/workflow/panel.ts
572
+ var import_chalk = __toESM(require("chalk"), 1);
573
+ function buildPanel(opts) {
574
+ const termWidth = process.stdout.columns || 80;
575
+ const contentLines = opts.lines && opts.lines.length ? opts.lines : [];
576
+ const maxContent = Math.max(
577
+ opts.title ? stripAnsi(opts.title).length : 0,
578
+ ...contentLines.map((l) => stripAnsi(l).length)
579
+ );
580
+ const innerWidth = Math.min(opts.width || maxContent, termWidth - 4);
581
+ const pad = (s) => {
582
+ const visible = stripAnsi(s).length;
583
+ const needed = innerWidth - visible;
584
+ return s + " ".repeat(Math.max(0, needed));
585
+ };
586
+ const top = "\u250C " + pad(opts.title ? import_chalk.default.bold(opts.title) : "") + " \u2510";
587
+ const body = contentLines.map((l) => "\u2502 " + pad(l) + " \u2502");
588
+ const bottom = "\u2514" + "\u2500".repeat(innerWidth + 2) + "\u2518";
589
+ return [top, ...body, bottom].join("\n");
590
+ }
591
+ function stripAnsi(str) {
592
+ return str.replace(/\u001B\[[0-9;]*m/g, "");
593
+ }
594
+
595
+ // src/workflow/generate.ts
596
+ async function animateHeader() {
597
+ const text = "ai-conventional-commit";
598
+ if (!process.stdout.isTTY || process.env.AICC_NO_ANIMATION) {
599
+ console.log("\n\u250C " + import_chalk2.default.bold(text));
600
+ return;
601
+ }
602
+ const palette = [
603
+ "#3a0d6d",
604
+ "#5a1ea3",
605
+ "#7a32d6",
606
+ "#9a4dff",
607
+ "#b267ff",
608
+ "#c37dff",
609
+ "#b267ff",
610
+ "#9a4dff",
611
+ "#7a32d6",
612
+ "#5a1ea3"
613
+ ];
614
+ process.stdout.write("\n");
615
+ for (const color of palette) {
616
+ const frame = import_chalk2.default.bold.hex(color)(text);
617
+ process.stdout.write("\r\u250C " + frame);
618
+ await new Promise((r) => setTimeout(r, 60));
619
+ }
620
+ process.stdout.write("\n");
621
+ }
573
622
  async function runGenerate(config) {
574
623
  if (!await ensureStagedChanges()) {
575
624
  console.log("No staged changes.");
@@ -580,14 +629,25 @@ async function runGenerate(config) {
580
629
  console.log("No diff content detected after staging. Aborting.");
581
630
  return;
582
631
  }
583
- console.log("\n\u250C " + import_chalk.default.bold("ai-conventional-commit"));
584
- console.log("\u2502");
585
- console.log(`\u25C6 ${import_chalk.default.bold(`Detected ${files.length} staged files:`)}`);
586
- for (const f of files) console.log(" " + f.file);
587
- console.log("\u2502");
632
+ const fileLines = [
633
+ import_chalk2.default.dim(`Detected ${files.length} staged ${files.length === 1 ? "file" : "files"}:`),
634
+ ...files.map((f) => "\u2022 " + f.file)
635
+ ];
636
+ if (process.stdout.isTTY) {
637
+ await animateHeader();
638
+ console.log(
639
+ buildPanel({
640
+ title: "Files",
641
+ lines: fileLines
642
+ })
643
+ );
644
+ } else {
645
+ console.log("\nFiles:");
646
+ fileLines.forEach((l) => console.log(" " + l));
647
+ }
588
648
  const spinner = (0, import_ora.default)({ text: " Starting", spinner: "dots" }).start();
589
649
  function setPhase(label) {
590
- spinner.text = " " + import_chalk.default.bold(label);
650
+ spinner.text = " " + import_chalk2.default.bold(label);
591
651
  }
592
652
  async function runStep(label, fn) {
593
653
  setPhase(label);
@@ -624,7 +684,7 @@ async function runGenerate(config) {
624
684
  async () => applyTransforms(plan.commits, plugins, { cwd: process.cwd(), env: process.env })
625
685
  );
626
686
  setPhase("Result found");
627
- spinner.stopAndPersist({ symbol: "\u25C6", text: " " + import_chalk.default.bold("Result found:") });
687
+ spinner.stopAndPersist({ symbol: "\u25C6", text: " " + import_chalk2.default.bold("Result found:") });
628
688
  candidates = candidates.map((c) => ({
629
689
  ...c,
630
690
  title: formatCommitTitle(c.title, {
@@ -633,15 +693,24 @@ async function runGenerate(config) {
633
693
  })
634
694
  }));
635
695
  const chosen = candidates[0];
636
- console.log(" " + import_chalk.default.yellow(chosen.title));
696
+ const resultLines = [import_chalk2.default.white(chosen.title)];
637
697
  if (chosen.body) {
638
- const indent = " ";
639
698
  chosen.body.split("\n").forEach((line) => {
640
- if (line.trim().length === 0) console.log(indent);
641
- else console.log(indent + import_chalk.default.gray(line));
699
+ if (line.trim().length === 0) resultLines.push("");
700
+ else resultLines.push(import_chalk2.default.white(line));
642
701
  });
643
702
  }
644
- console.log("\u2502");
703
+ if (process.stdout.isTTY) {
704
+ console.log(
705
+ buildPanel({
706
+ title: "Result",
707
+ lines: resultLines
708
+ })
709
+ );
710
+ } else {
711
+ console.log("\nResult:");
712
+ resultLines.forEach((l) => console.log(" " + l));
713
+ }
645
714
  const pluginErrors = await runValidations(chosen, plugins, {
646
715
  cwd: process.cwd(),
647
716
  env: process.env
@@ -649,9 +718,18 @@ async function runGenerate(config) {
649
718
  const guardErrors = checkCandidate(chosen);
650
719
  const errors = [...pluginErrors, ...guardErrors];
651
720
  if (errors.length) {
652
- console.log(import_chalk.default.red("! Validation issues:"));
653
- errors.forEach((e) => console.log(" -", e));
654
- console.log("\u2502");
721
+ const errorLines = ["Validation issues:", ...errors.map((e) => import_chalk2.default.red("\u2022 " + e))];
722
+ if (process.stdout.isTTY) {
723
+ console.log(
724
+ buildPanel({
725
+ title: "Checks",
726
+ lines: errorLines
727
+ })
728
+ );
729
+ } else {
730
+ console.log("\nValidation issues:");
731
+ errorLines.slice(1).forEach((l) => console.log(" " + l));
732
+ }
655
733
  }
656
734
  const yn = await selectYesNo();
657
735
  if (!yn) {
@@ -660,7 +738,7 @@ async function runGenerate(config) {
660
738
  }
661
739
  await createCommit(chosen.title, chosen.body);
662
740
  saveSession({ plan, chosen, mode: "single" });
663
- console.log(import_chalk.default.green("Commit created."));
741
+ console.log(import_chalk2.default.green("Commit created."));
664
742
  }
665
743
  function saveSession(data) {
666
744
  const dir = ".git/.aicc-cache";
@@ -674,8 +752,8 @@ async function selectYesNo() {
674
752
  name: "choice",
675
753
  message: " Use this commit message?",
676
754
  choices: [
677
- { name: "\u25CF Yes", value: true },
678
- { name: "\u25CB No", value: false }
755
+ { name: "Yes", value: true },
756
+ { name: "No", value: false }
679
757
  ],
680
758
  default: 0
681
759
  }
@@ -684,7 +762,7 @@ async function selectYesNo() {
684
762
  }
685
763
 
686
764
  // src/workflow/split.ts
687
- var import_chalk2 = __toESM(require("chalk"), 1);
765
+ var import_chalk3 = __toESM(require("chalk"), 1);
688
766
 
689
767
  // src/cluster.ts
690
768
  var topLevel = (file) => file.split("/")[0] || file;
@@ -783,14 +861,14 @@ async function runSplit(config, desired) {
783
861
  mode: config.gitmojiMode || "standard"
784
862
  })
785
863
  }));
786
- console.log(import_chalk2.default.cyan("\nProposed commits:"));
864
+ console.log(import_chalk3.default.cyan("\nProposed commits:"));
787
865
  candidates.forEach((c) => {
788
- console.log(import_chalk2.default.yellow(`\u2022 ${c.title}`));
866
+ console.log(import_chalk3.default.yellow(`\u2022 ${c.title}`));
789
867
  if (c.body) {
790
868
  const indent = " ";
791
869
  c.body.split("\n").forEach((line) => {
792
870
  if (line.trim().length === 0) console.log(indent);
793
- else console.log(indent + import_chalk2.default.gray(line));
871
+ else console.log(indent + import_chalk3.default.gray(line));
794
872
  });
795
873
  }
796
874
  });
@@ -818,12 +896,12 @@ async function runSplit(config, desired) {
818
896
  const guardErrors = checkCandidate(candidate);
819
897
  const errors = [...pluginErrors, ...guardErrors];
820
898
  if (errors.length) {
821
- console.log(import_chalk2.default.red("Skipping commit due to errors:"), candidate.title);
899
+ console.log(import_chalk3.default.red("Skipping commit due to errors:"), candidate.title);
822
900
  errors.forEach((e) => console.log(" -", e));
823
901
  continue;
824
902
  }
825
903
  await createCommit(candidate.title, candidate.body);
826
- console.log(import_chalk2.default.green("Committed: ") + candidate.title);
904
+ console.log(import_chalk3.default.green("Committed: ") + candidate.title);
827
905
  }
828
906
  saveSession2({ plan, chosen: candidates, mode: "split" });
829
907
  }
@@ -834,7 +912,7 @@ function saveSession2(data) {
834
912
  }
835
913
 
836
914
  // src/workflow/refine.ts
837
- var import_chalk3 = __toESM(require("chalk"), 1);
915
+ var import_chalk4 = __toESM(require("chalk"), 1);
838
916
  var import_ora2 = __toESM(require("ora"), 1);
839
917
  var import_node_fs3 = require("fs");
840
918
  var import_node_path4 = require("path");
@@ -910,19 +988,19 @@ async function runRefine(config, options) {
910
988
  mode: config.gitmojiMode || "standard"
911
989
  });
912
990
  }
913
- console.log(import_chalk3.default.cyan("\nRefined candidate:"));
914
- console.log(import_chalk3.default.yellow(refined.commits[0].title));
991
+ console.log(import_chalk4.default.cyan("\nRefined candidate:"));
992
+ console.log(import_chalk4.default.yellow(refined.commits[0].title));
915
993
  if (refined.commits[0].body) {
916
994
  const indent = " ";
917
995
  refined.commits[0].body.split("\n").forEach((line) => {
918
996
  if (line.trim().length === 0) console.log(indent);
919
- else console.log(indent + import_chalk3.default.gray(line));
997
+ else console.log(indent + import_chalk4.default.gray(line));
920
998
  });
921
999
  }
922
1000
  const accept = await prompt("Accept refined version? (Y/n) ", "y");
923
1001
  if (!/^n/i.test(accept)) {
924
1002
  plan.commits[index] = refined.commits[0];
925
- console.log(import_chalk3.default.green("Refinement stored (not retro-committed)."));
1003
+ console.log(import_chalk4.default.green("Refinement stored (not retro-committed)."));
926
1004
  } else {
927
1005
  console.log("Refinement discarded.");
928
1006
  }
@@ -941,8 +1019,7 @@ var DEFAULTS = {
941
1019
  maxTokens: parseInt(process.env.AICC_MAX_TOKENS || "512", 10),
942
1020
  cacheDir: ".git/.aicc-cache",
943
1021
  plugins: [],
944
- verbose: process.env.AICC_VERBOSE === "true",
945
- reasoning: process.env.AICC_REASONING || "low"
1022
+ verbose: process.env.AICC_VERBOSE === "true"
946
1023
  };
947
1024
  async function loadConfig(cwd = process.cwd()) {
948
1025
  const explorer = (0, import_cosmiconfig.cosmiconfig)("aicc");
@@ -959,52 +1036,103 @@ async function loadConfig(cwd = process.cwd()) {
959
1036
  }
960
1037
 
961
1038
  // src/index.ts
1039
+ var import_module = require("module");
1040
+ var import_meta = {};
1041
+ var require2 = (0, import_module.createRequire)(import_meta.url);
1042
+ var pkgVersion = "0.0.0";
1043
+ try {
1044
+ const pkg = require2("../package.json");
1045
+ pkgVersion = pkg.version || pkgVersion;
1046
+ } catch {
1047
+ }
962
1048
  var GenerateCommand = class extends import_clipanion.Command {
963
- static paths = [[`generate`], [`run`], [`commit`], []];
1049
+ static paths = [[`generate`], []];
1050
+ static usage = import_clipanion.Command.Usage({
1051
+ description: "Generate a conventional commit message for staged changes (aliases: run, commit).",
1052
+ details: `Generates a single commit message using AI with style + guardrails.
1053
+ Add --gitmoji or --gitmoji-pure to enable emoji styles.`,
1054
+ examples: [
1055
+ ["Generate a commit with gitmoji style", "ai-conventional-commit generate --gitmoji"]
1056
+ ]
1057
+ });
964
1058
  gitmoji = import_clipanion.Option.Boolean("--gitmoji", false, {
965
- description: "Gitmoji mode: emoji acts as type (emoji: subject)"
1059
+ description: "Gitmoji mode (vs --gitmoji-pure): emoji + type (emoji: subject)"
966
1060
  });
967
1061
  gitmojiPure = import_clipanion.Option.Boolean("--gitmoji-pure", false, {
968
- description: "Pure gitmoji mode: emoji: subject (no type)"
1062
+ description: "Pure gitmoji mode (vs --gitmoji): emoji only (emoji: subject)"
1063
+ });
1064
+ model = import_clipanion.Option.String("-m,--model", {
1065
+ required: false,
1066
+ description: "Model provider/name (e.g. github-copilot/gpt-5)"
969
1067
  });
970
- reasoning = import_clipanion.Option.String("--reasoning", { required: false });
971
1068
  async execute() {
972
1069
  const config = await loadConfig();
973
1070
  if (this.gitmoji || this.gitmojiPure) {
974
1071
  config.gitmoji = true;
975
1072
  config.gitmojiMode = this.gitmojiPure ? "gitmoji-pure" : "gitmoji";
976
1073
  }
977
- if (this.reasoning) config.reasoning = this.reasoning;
1074
+ if (this.model) config.model = this.model;
978
1075
  await runGenerate(config);
979
1076
  }
980
1077
  };
981
1078
  var SplitCommand = class extends import_clipanion.Command {
982
1079
  static paths = [[`split`]];
1080
+ static usage = import_clipanion.Command.Usage({
1081
+ description: "Propose multiple smaller conventional commits for current staged diff.",
1082
+ details: `Analyzes staged changes, groups them logically and suggests multiple commit messages.
1083
+ Use --max to limit the number of proposals.`,
1084
+ examples: [
1085
+ [
1086
+ "Split into at most 3 commits with gitmoji",
1087
+ "ai-conventional-commit split --max 3 --gitmoji"
1088
+ ]
1089
+ ]
1090
+ });
983
1091
  max = import_clipanion.Option.String("--max", { description: "Max proposed commits", required: false });
984
- gitmoji = import_clipanion.Option.Boolean("--gitmoji", false);
985
- gitmojiPure = import_clipanion.Option.Boolean("--gitmoji-pure", false);
986
- reasoning = import_clipanion.Option.String("--reasoning", { required: false });
1092
+ gitmoji = import_clipanion.Option.Boolean("--gitmoji", false, {
1093
+ description: "Gitmoji mode (vs --gitmoji-pure): emoji + type"
1094
+ });
1095
+ gitmojiPure = import_clipanion.Option.Boolean("--gitmoji-pure", false, {
1096
+ description: "Pure gitmoji mode (vs --gitmoji): emoji only"
1097
+ });
1098
+ model = import_clipanion.Option.String("-m,--model", {
1099
+ required: false,
1100
+ description: "Model provider/name override"
1101
+ });
987
1102
  async execute() {
988
1103
  const config = await loadConfig();
989
1104
  if (this.gitmoji || this.gitmojiPure) {
990
1105
  config.gitmoji = true;
991
1106
  config.gitmojiMode = this.gitmojiPure ? "gitmoji-pure" : "gitmoji";
992
1107
  }
993
- if (this.reasoning) config.reasoning = this.reasoning;
1108
+ if (this.model) config.model = this.model;
994
1109
  await runSplit(config, this.max ? parseInt(this.max, 10) : void 0);
995
1110
  }
996
1111
  };
997
1112
  var RefineCommand = class extends import_clipanion.Command {
998
1113
  static paths = [[`refine`]];
999
- shorter = import_clipanion.Option.Boolean("--shorter", false);
1000
- longer = import_clipanion.Option.Boolean("--longer", false);
1001
- scope = import_clipanion.Option.String("--scope");
1002
- emoji = import_clipanion.Option.Boolean("--emoji", false);
1003
- index = import_clipanion.Option.String("--index");
1004
- reasoning = import_clipanion.Option.String("--reasoning", { required: false });
1114
+ static usage = import_clipanion.Command.Usage({
1115
+ description: "Refine the last (or chosen) commit message with style rules.",
1116
+ details: `Allows targeted improvements: shorter/longer length, inject scope, add emoji, or select a specific index when multiple commits were generated earlier.`,
1117
+ examples: [
1118
+ ["Shorten the last commit message", "ai-conventional-commit refine --shorter"],
1119
+ ["Add a scope to the last commit", "ai-conventional-commit refine --scope ui"]
1120
+ ]
1121
+ });
1122
+ shorter = import_clipanion.Option.Boolean("--shorter", false, { description: "Make message more concise" });
1123
+ longer = import_clipanion.Option.Boolean("--longer", false, { description: "Expand message with detail" });
1124
+ scope = import_clipanion.Option.String("--scope", { description: "Override/add scope (e.g. ui, api)" });
1125
+ emoji = import_clipanion.Option.Boolean("--emoji", false, { description: "Add appropriate gitmoji (non-pure)" });
1126
+ index = import_clipanion.Option.String("--index", {
1127
+ description: "Select commit index if multiple were generated"
1128
+ });
1129
+ model = import_clipanion.Option.String("-m,--model", {
1130
+ required: false,
1131
+ description: "Model provider/name override"
1132
+ });
1005
1133
  async execute() {
1006
1134
  const config = await loadConfig();
1007
- if (this.reasoning) config.reasoning = this.reasoning;
1135
+ if (this.model) config.model = this.model;
1008
1136
  await runRefine(config, {
1009
1137
  shorter: this.shorter,
1010
1138
  longer: this.longer,
@@ -1014,14 +1142,57 @@ var RefineCommand = class extends import_clipanion.Command {
1014
1142
  });
1015
1143
  }
1016
1144
  };
1145
+ var HelpCommand = class extends import_clipanion.Command {
1146
+ static paths = [[`--help`], [`-h`]];
1147
+ // capture explicit help
1148
+ async execute() {
1149
+ this.context.stdout.write(globalHelp() + "\n");
1150
+ }
1151
+ };
1152
+ function globalHelp() {
1153
+ return `ai-conventional-commit v${pkgVersion}
1154
+
1155
+ Usage:
1156
+ ai-conventional-commit [generate] [options] Generate a commit (default)
1157
+ ai-conventional-commit split [options] Propose multiple commits
1158
+ ai-conventional-commit refine [options] Refine last or indexed commit
1159
+
1160
+ Global Options:
1161
+ -m, --model <provider/name> Override model provider/name
1162
+ --gitmoji Gitmoji mode (emoji + type)
1163
+ --gitmoji-pure Gitmoji pure mode (emoji only)
1164
+ -h, --help Show this help
1165
+ -V, --version Show version
1166
+
1167
+ Refine Options:
1168
+ --shorter / --longer Adjust message length
1169
+ --scope <scope> Add or replace scope
1170
+ --emoji Add suitable gitmoji
1171
+ --index <n> Select commit index
1172
+
1173
+ Examples:
1174
+ ai-conventional-commit --gitmoji
1175
+ ai-conventional-commit split --max 3
1176
+ ai-conventional-commit refine --scope api --emoji
1177
+ `;
1178
+ }
1179
+ var VersionCommand = class extends import_clipanion.Command {
1180
+ static paths = [[`--version`], [`-V`]];
1181
+ async execute() {
1182
+ this.context.stdout.write(`${pkgVersion}
1183
+ `);
1184
+ }
1185
+ };
1017
1186
  var cli = new import_clipanion.Cli({
1018
1187
  binaryLabel: "ai-conventional-commit",
1019
1188
  binaryName: "ai-conventional-commit",
1020
- binaryVersion: "0.1.0"
1189
+ binaryVersion: pkgVersion
1021
1190
  });
1022
1191
  cli.register(GenerateCommand);
1023
1192
  cli.register(SplitCommand);
1024
1193
  cli.register(RefineCommand);
1194
+ cli.register(HelpCommand);
1195
+ cli.register(VersionCommand);
1025
1196
  cli.runExit(process.argv.slice(2), {
1026
1197
  stdin: process.stdin,
1027
1198
  stdout: process.stdout,