@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 +55 -17
- package/dist/index.cjs +223 -52
- package/dist/index.js +506 -160
- package/package.json +3 -3
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
|
|
15
|
-
|
|
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
|
-
|
|
68
|
-
|
|
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`
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
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 = " " +
|
|
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: " " +
|
|
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
|
-
|
|
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)
|
|
641
|
-
else
|
|
699
|
+
if (line.trim().length === 0) resultLines.push("");
|
|
700
|
+
else resultLines.push(import_chalk2.default.white(line));
|
|
642
701
|
});
|
|
643
702
|
}
|
|
644
|
-
|
|
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
|
-
|
|
653
|
-
|
|
654
|
-
|
|
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(
|
|
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: "
|
|
678
|
-
{ name: "
|
|
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
|
|
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(
|
|
864
|
+
console.log(import_chalk3.default.cyan("\nProposed commits:"));
|
|
787
865
|
candidates.forEach((c) => {
|
|
788
|
-
console.log(
|
|
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 +
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
914
|
-
console.log(
|
|
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 +
|
|
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(
|
|
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`], [
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
|
|
986
|
-
|
|
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.
|
|
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
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
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.
|
|
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:
|
|
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,
|