@kud/ai-conventional-commit-cli 0.3.0 → 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,10 +567,36 @@ 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
573
596
  async function animateHeader() {
574
597
  const text = "ai-conventional-commit";
575
598
  if (!process.stdout.isTTY || process.env.AICC_NO_ANIMATION) {
576
- console.log("\n\u250C " + import_chalk.default.bold(text));
599
+ console.log("\n\u250C " + import_chalk2.default.bold(text));
577
600
  return;
578
601
  }
579
602
  const palette = [
@@ -590,7 +613,7 @@ async function animateHeader() {
590
613
  ];
591
614
  process.stdout.write("\n");
592
615
  for (const color of palette) {
593
- const frame = import_chalk.default.bold.hex(color)(text);
616
+ const frame = import_chalk2.default.bold.hex(color)(text);
594
617
  process.stdout.write("\r\u250C " + frame);
595
618
  await new Promise((r) => setTimeout(r, 60));
596
619
  }
@@ -606,16 +629,25 @@ async function runGenerate(config) {
606
629
  console.log("No diff content detected after staging. Aborting.");
607
630
  return;
608
631
  }
609
- await animateHeader();
610
- console.log("\u2502");
611
- console.log(
612
- `\u25C6 ${import_chalk.default.bold(`Detected ${files.length} staged ${files.length === 1 ? "file" : "files"}:`)}`
613
- );
614
- for (const f of files) console.log(" \u2022 " + f.file);
615
- 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
+ }
616
648
  const spinner = (0, import_ora.default)({ text: " Starting", spinner: "dots" }).start();
617
649
  function setPhase(label) {
618
- spinner.text = " " + import_chalk.default.bold(label);
650
+ spinner.text = " " + import_chalk2.default.bold(label);
619
651
  }
620
652
  async function runStep(label, fn) {
621
653
  setPhase(label);
@@ -652,7 +684,7 @@ async function runGenerate(config) {
652
684
  async () => applyTransforms(plan.commits, plugins, { cwd: process.cwd(), env: process.env })
653
685
  );
654
686
  setPhase("Result found");
655
- spinner.stopAndPersist({ symbol: "\u25C6", text: " " + import_chalk.default.bold("Result found:") });
687
+ spinner.stopAndPersist({ symbol: "\u25C6", text: " " + import_chalk2.default.bold("Result found:") });
656
688
  candidates = candidates.map((c) => ({
657
689
  ...c,
658
690
  title: formatCommitTitle(c.title, {
@@ -661,16 +693,24 @@ async function runGenerate(config) {
661
693
  })
662
694
  }));
663
695
  const chosen = candidates[0];
664
- console.log(" " + import_chalk.default.white(chosen.title));
696
+ const resultLines = [import_chalk2.default.white(chosen.title)];
665
697
  if (chosen.body) {
666
- const indent = " ";
667
- console.log(indent);
668
698
  chosen.body.split("\n").forEach((line) => {
669
- if (line.trim().length === 0) console.log(indent);
670
- else console.log(indent + import_chalk.default.white(line));
699
+ if (line.trim().length === 0) resultLines.push("");
700
+ else resultLines.push(import_chalk2.default.white(line));
671
701
  });
672
702
  }
673
- 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
+ }
674
714
  const pluginErrors = await runValidations(chosen, plugins, {
675
715
  cwd: process.cwd(),
676
716
  env: process.env
@@ -678,9 +718,18 @@ async function runGenerate(config) {
678
718
  const guardErrors = checkCandidate(chosen);
679
719
  const errors = [...pluginErrors, ...guardErrors];
680
720
  if (errors.length) {
681
- console.log(import_chalk.default.red("! Validation issues:"));
682
- errors.forEach((e) => console.log(" -", e));
683
- 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
+ }
684
733
  }
685
734
  const yn = await selectYesNo();
686
735
  if (!yn) {
@@ -689,7 +738,7 @@ async function runGenerate(config) {
689
738
  }
690
739
  await createCommit(chosen.title, chosen.body);
691
740
  saveSession({ plan, chosen, mode: "single" });
692
- console.log(import_chalk.default.green("Commit created."));
741
+ console.log(import_chalk2.default.green("Commit created."));
693
742
  }
694
743
  function saveSession(data) {
695
744
  const dir = ".git/.aicc-cache";
@@ -713,7 +762,7 @@ async function selectYesNo() {
713
762
  }
714
763
 
715
764
  // src/workflow/split.ts
716
- var import_chalk2 = __toESM(require("chalk"), 1);
765
+ var import_chalk3 = __toESM(require("chalk"), 1);
717
766
 
718
767
  // src/cluster.ts
719
768
  var topLevel = (file) => file.split("/")[0] || file;
@@ -812,14 +861,14 @@ async function runSplit(config, desired) {
812
861
  mode: config.gitmojiMode || "standard"
813
862
  })
814
863
  }));
815
- console.log(import_chalk2.default.cyan("\nProposed commits:"));
864
+ console.log(import_chalk3.default.cyan("\nProposed commits:"));
816
865
  candidates.forEach((c) => {
817
- console.log(import_chalk2.default.yellow(`\u2022 ${c.title}`));
866
+ console.log(import_chalk3.default.yellow(`\u2022 ${c.title}`));
818
867
  if (c.body) {
819
868
  const indent = " ";
820
869
  c.body.split("\n").forEach((line) => {
821
870
  if (line.trim().length === 0) console.log(indent);
822
- else console.log(indent + import_chalk2.default.gray(line));
871
+ else console.log(indent + import_chalk3.default.gray(line));
823
872
  });
824
873
  }
825
874
  });
@@ -847,12 +896,12 @@ async function runSplit(config, desired) {
847
896
  const guardErrors = checkCandidate(candidate);
848
897
  const errors = [...pluginErrors, ...guardErrors];
849
898
  if (errors.length) {
850
- 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);
851
900
  errors.forEach((e) => console.log(" -", e));
852
901
  continue;
853
902
  }
854
903
  await createCommit(candidate.title, candidate.body);
855
- console.log(import_chalk2.default.green("Committed: ") + candidate.title);
904
+ console.log(import_chalk3.default.green("Committed: ") + candidate.title);
856
905
  }
857
906
  saveSession2({ plan, chosen: candidates, mode: "split" });
858
907
  }
@@ -863,7 +912,7 @@ function saveSession2(data) {
863
912
  }
864
913
 
865
914
  // src/workflow/refine.ts
866
- var import_chalk3 = __toESM(require("chalk"), 1);
915
+ var import_chalk4 = __toESM(require("chalk"), 1);
867
916
  var import_ora2 = __toESM(require("ora"), 1);
868
917
  var import_node_fs3 = require("fs");
869
918
  var import_node_path4 = require("path");
@@ -939,19 +988,19 @@ async function runRefine(config, options) {
939
988
  mode: config.gitmojiMode || "standard"
940
989
  });
941
990
  }
942
- console.log(import_chalk3.default.cyan("\nRefined candidate:"));
943
- 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));
944
993
  if (refined.commits[0].body) {
945
994
  const indent = " ";
946
995
  refined.commits[0].body.split("\n").forEach((line) => {
947
996
  if (line.trim().length === 0) console.log(indent);
948
- else console.log(indent + import_chalk3.default.gray(line));
997
+ else console.log(indent + import_chalk4.default.gray(line));
949
998
  });
950
999
  }
951
1000
  const accept = await prompt("Accept refined version? (Y/n) ", "y");
952
1001
  if (!/^n/i.test(accept)) {
953
1002
  plan.commits[index] = refined.commits[0];
954
- console.log(import_chalk3.default.green("Refinement stored (not retro-committed)."));
1003
+ console.log(import_chalk4.default.green("Refinement stored (not retro-committed)."));
955
1004
  } else {
956
1005
  console.log("Refinement discarded.");
957
1006
  }
@@ -970,8 +1019,7 @@ var DEFAULTS = {
970
1019
  maxTokens: parseInt(process.env.AICC_MAX_TOKENS || "512", 10),
971
1020
  cacheDir: ".git/.aicc-cache",
972
1021
  plugins: [],
973
- verbose: process.env.AICC_VERBOSE === "true",
974
- reasoning: process.env.AICC_REASONING || "low"
1022
+ verbose: process.env.AICC_VERBOSE === "true"
975
1023
  };
976
1024
  async function loadConfig(cwd = process.cwd()) {
977
1025
  const explorer = (0, import_cosmiconfig.cosmiconfig)("aicc");
@@ -988,52 +1036,103 @@ async function loadConfig(cwd = process.cwd()) {
988
1036
  }
989
1037
 
990
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
+ }
991
1048
  var GenerateCommand = class extends import_clipanion.Command {
992
- 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
+ });
993
1058
  gitmoji = import_clipanion.Option.Boolean("--gitmoji", false, {
994
- description: "Gitmoji mode: emoji acts as type (emoji: subject)"
1059
+ description: "Gitmoji mode (vs --gitmoji-pure): emoji + type (emoji: subject)"
995
1060
  });
996
1061
  gitmojiPure = import_clipanion.Option.Boolean("--gitmoji-pure", false, {
997
- 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)"
998
1067
  });
999
- reasoning = import_clipanion.Option.String("--reasoning", { required: false });
1000
1068
  async execute() {
1001
1069
  const config = await loadConfig();
1002
1070
  if (this.gitmoji || this.gitmojiPure) {
1003
1071
  config.gitmoji = true;
1004
1072
  config.gitmojiMode = this.gitmojiPure ? "gitmoji-pure" : "gitmoji";
1005
1073
  }
1006
- if (this.reasoning) config.reasoning = this.reasoning;
1074
+ if (this.model) config.model = this.model;
1007
1075
  await runGenerate(config);
1008
1076
  }
1009
1077
  };
1010
1078
  var SplitCommand = class extends import_clipanion.Command {
1011
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
+ });
1012
1091
  max = import_clipanion.Option.String("--max", { description: "Max proposed commits", required: false });
1013
- gitmoji = import_clipanion.Option.Boolean("--gitmoji", false);
1014
- gitmojiPure = import_clipanion.Option.Boolean("--gitmoji-pure", false);
1015
- 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
+ });
1016
1102
  async execute() {
1017
1103
  const config = await loadConfig();
1018
1104
  if (this.gitmoji || this.gitmojiPure) {
1019
1105
  config.gitmoji = true;
1020
1106
  config.gitmojiMode = this.gitmojiPure ? "gitmoji-pure" : "gitmoji";
1021
1107
  }
1022
- if (this.reasoning) config.reasoning = this.reasoning;
1108
+ if (this.model) config.model = this.model;
1023
1109
  await runSplit(config, this.max ? parseInt(this.max, 10) : void 0);
1024
1110
  }
1025
1111
  };
1026
1112
  var RefineCommand = class extends import_clipanion.Command {
1027
1113
  static paths = [[`refine`]];
1028
- shorter = import_clipanion.Option.Boolean("--shorter", false);
1029
- longer = import_clipanion.Option.Boolean("--longer", false);
1030
- scope = import_clipanion.Option.String("--scope");
1031
- emoji = import_clipanion.Option.Boolean("--emoji", false);
1032
- index = import_clipanion.Option.String("--index");
1033
- 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
+ });
1034
1133
  async execute() {
1035
1134
  const config = await loadConfig();
1036
- if (this.reasoning) config.reasoning = this.reasoning;
1135
+ if (this.model) config.model = this.model;
1037
1136
  await runRefine(config, {
1038
1137
  shorter: this.shorter,
1039
1138
  longer: this.longer,
@@ -1043,14 +1142,57 @@ var RefineCommand = class extends import_clipanion.Command {
1043
1142
  });
1044
1143
  }
1045
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
+ };
1046
1186
  var cli = new import_clipanion.Cli({
1047
1187
  binaryLabel: "ai-conventional-commit",
1048
1188
  binaryName: "ai-conventional-commit",
1049
- binaryVersion: "0.1.0"
1189
+ binaryVersion: pkgVersion
1050
1190
  });
1051
1191
  cli.register(GenerateCommand);
1052
1192
  cli.register(SplitCommand);
1053
1193
  cli.register(RefineCommand);
1194
+ cli.register(HelpCommand);
1195
+ cli.register(VersionCommand);
1054
1196
  cli.runExit(process.argv.slice(2), {
1055
1197
  stdin: process.stdin,
1056
1198
  stdout: process.stdout,