@hifullmoon/aicommit 1.4.0 → 2.0.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/.aicommit.config.example.json +30 -10
- package/CHANGELOG.md +75 -74
- package/README.md +71 -49
- package/README.zh-CN.md +109 -87
- package/SECURITY.md +1 -1
- package/docs/distribution.md +13 -76
- package/docs/privacy.md +8 -8
- package/docs/provider-presets.md +22 -9
- package/docs/troubleshooting.md +0 -1
- package/package.json +6 -6
- package/presets/provider-presets.json +48 -15
- package/schemas/aicommit-provider-presets.schema.json +24 -4
- package/src/cli.js +57 -17
- package/src/completion.js +4 -1
- package/src/config-command.js +7 -5
- package/src/config.js +198 -26
- package/src/doctor.js +5 -3
- package/src/main.js +29 -6
- package/src/provider-presets.js +43 -15
- package/src/setup.js +66 -37
- package/src/split.js +9 -2
- package/src/ui.js +66 -13
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"kind": "aicommit-provider-presets",
|
|
3
|
-
"schemaVersion":
|
|
4
|
-
"version": "
|
|
3
|
+
"schemaVersion": 2,
|
|
4
|
+
"version": "2.0.0",
|
|
5
5
|
"compatibility": {
|
|
6
|
-
"coreMinimum": "1.
|
|
7
|
-
"coreMaximumExclusive": "
|
|
6
|
+
"coreMinimum": "1.5.1",
|
|
7
|
+
"coreMaximumExclusive": "3.0.0",
|
|
8
8
|
"adapterContract": 1
|
|
9
9
|
},
|
|
10
10
|
"providers": [
|
|
@@ -13,33 +13,55 @@
|
|
|
13
13
|
"label": "OpenAI",
|
|
14
14
|
"adapter": "openai",
|
|
15
15
|
"apiUrl": "https://api.openai.com/v1/chat/completions",
|
|
16
|
-
"
|
|
16
|
+
"defaultModel": "gpt-4o",
|
|
17
|
+
"models": {
|
|
18
|
+
"gpt-4o": {
|
|
19
|
+
"label": "GPT-4o",
|
|
20
|
+
"modelId": "gpt-4o"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
17
23
|
},
|
|
18
24
|
{
|
|
19
25
|
"id": "deepseek",
|
|
20
26
|
"label": "DeepSeek",
|
|
21
27
|
"adapter": "deepseek",
|
|
22
28
|
"apiUrl": "https://api.deepseek.com/v1/chat/completions",
|
|
23
|
-
"
|
|
29
|
+
"defaultModel": "chat",
|
|
30
|
+
"models": {
|
|
31
|
+
"chat": {
|
|
32
|
+
"label": "DeepSeek Chat",
|
|
33
|
+
"modelId": "deepseek-v4-flash"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
24
36
|
},
|
|
25
37
|
{
|
|
26
38
|
"id": "openrouter",
|
|
27
39
|
"label": "OpenRouter",
|
|
28
40
|
"adapter": "openrouter",
|
|
29
41
|
"apiUrl": "https://openrouter.ai/api/v1/chat/completions",
|
|
30
|
-
"
|
|
42
|
+
"defaultModel": "default",
|
|
43
|
+
"models": {
|
|
44
|
+
"default": {
|
|
45
|
+
"modelId": "openai/gpt-4o-mini"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
31
48
|
},
|
|
32
49
|
{
|
|
33
50
|
"id": "minimax",
|
|
34
51
|
"label": "MiniMax",
|
|
35
52
|
"adapter": "minimax",
|
|
36
53
|
"apiUrl": "https://api.minimaxi.com/v1/chat/completions",
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
|
|
42
|
-
|
|
54
|
+
"defaultModel": "default",
|
|
55
|
+
"models": {
|
|
56
|
+
"default": {
|
|
57
|
+
"modelId": "MiniMax-M3",
|
|
58
|
+
"extraBody": {
|
|
59
|
+
"thinking": {
|
|
60
|
+
"type": "disabled"
|
|
61
|
+
},
|
|
62
|
+
"reasoning_split": true
|
|
63
|
+
}
|
|
64
|
+
}
|
|
43
65
|
}
|
|
44
66
|
},
|
|
45
67
|
{
|
|
@@ -47,14 +69,25 @@
|
|
|
47
69
|
"label": "Kimi Code",
|
|
48
70
|
"adapter": "custom",
|
|
49
71
|
"apiUrl": "https://api.kimi.com/coding/v1/chat/completions",
|
|
50
|
-
"
|
|
72
|
+
"defaultModel": "default",
|
|
73
|
+
"models": {
|
|
74
|
+
"default": {
|
|
75
|
+
"modelId": "kimi-for-coding"
|
|
76
|
+
}
|
|
77
|
+
}
|
|
51
78
|
},
|
|
52
79
|
{
|
|
53
80
|
"id": "ollama",
|
|
54
81
|
"label": "Ollama (local)",
|
|
55
82
|
"adapter": "ollama",
|
|
56
83
|
"apiUrl": "http://127.0.0.1:11434/api/chat",
|
|
57
|
-
"
|
|
84
|
+
"defaultModel": "qwen",
|
|
85
|
+
"models": {
|
|
86
|
+
"qwen": {
|
|
87
|
+
"label": "Qwen 3 8B",
|
|
88
|
+
"modelId": "qwen3:8b"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
58
91
|
}
|
|
59
92
|
]
|
|
60
93
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"required": ["kind", "schemaVersion", "version", "compatibility", "providers"],
|
|
8
8
|
"properties": {
|
|
9
9
|
"kind": { "const": "aicommit-provider-presets" },
|
|
10
|
-
"schemaVersion": { "const":
|
|
10
|
+
"schemaVersion": { "const": 2 },
|
|
11
11
|
"version": {
|
|
12
12
|
"type": "string",
|
|
13
13
|
"pattern": "^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$"
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"items": {
|
|
36
36
|
"type": "object",
|
|
37
37
|
"additionalProperties": false,
|
|
38
|
-
"required": ["id", "label", "adapter", "apiUrl", "
|
|
38
|
+
"required": ["id", "label", "adapter", "apiUrl", "defaultModel", "models"],
|
|
39
39
|
"properties": {
|
|
40
40
|
"id": {
|
|
41
41
|
"type": "string",
|
|
@@ -47,8 +47,28 @@
|
|
|
47
47
|
"enum": ["openai", "openrouter", "deepseek", "minimax", "ollama", "custom"]
|
|
48
48
|
},
|
|
49
49
|
"apiUrl": { "type": "string", "minLength": 1, "maxLength": 2048 },
|
|
50
|
-
"
|
|
51
|
-
|
|
50
|
+
"defaultModel": {
|
|
51
|
+
"type": "string",
|
|
52
|
+
"pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"
|
|
53
|
+
},
|
|
54
|
+
"models": {
|
|
55
|
+
"type": "object",
|
|
56
|
+
"minProperties": 1,
|
|
57
|
+
"maxProperties": 100,
|
|
58
|
+
"propertyNames": {
|
|
59
|
+
"pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"
|
|
60
|
+
},
|
|
61
|
+
"additionalProperties": {
|
|
62
|
+
"type": "object",
|
|
63
|
+
"additionalProperties": false,
|
|
64
|
+
"required": ["modelId"],
|
|
65
|
+
"properties": {
|
|
66
|
+
"label": { "type": "string", "minLength": 1, "maxLength": 80 },
|
|
67
|
+
"modelId": { "type": "string", "minLength": 1, "maxLength": 256 },
|
|
68
|
+
"extraBody": { "type": "object" }
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
52
72
|
}
|
|
53
73
|
}
|
|
54
74
|
}
|
package/src/cli.js
CHANGED
|
@@ -18,7 +18,7 @@ function showHelp() {
|
|
|
18
18
|
${chalk.dim('$')} aicommit policy <template|check> [options]
|
|
19
19
|
${chalk.dim('$')} aicommit preset <show|validate|path|install|rollback> [options]
|
|
20
20
|
${chalk.dim('$')} aicommit completion <bash|zsh|fish>
|
|
21
|
-
${chalk.dim('$')} aicommit split
|
|
21
|
+
${chalk.dim('$')} aicommit split [run|plan|apply|resume|abort] [options]
|
|
22
22
|
|
|
23
23
|
${chalk.bold('Commands:')}
|
|
24
24
|
setup Interactive configuration wizard
|
|
@@ -30,7 +30,7 @@ function showHelp() {
|
|
|
30
30
|
policy check Validate a message file or Git range with the effective team policy
|
|
31
31
|
preset Inspect, validate, install, or roll back provider preset manifests
|
|
32
32
|
completion Generate Bash, Zsh, or Fish completion on stdout
|
|
33
|
-
split run
|
|
33
|
+
split, split run Plan and create logical commits
|
|
34
34
|
split plan Generate and export a fingerprinted JSON plan
|
|
35
35
|
split apply Validate and apply an exported JSON plan
|
|
36
36
|
split resume Resume the repository's unfinished transaction
|
|
@@ -45,8 +45,9 @@ function showHelp() {
|
|
|
45
45
|
-v, --version Show version number
|
|
46
46
|
-l, --lang=<zh|en> Commit message language (default: zh)
|
|
47
47
|
-p, --provider=<name> Use the named provider from config "providers"
|
|
48
|
+
-m, --model=<name> Use a named model from the selected provider
|
|
48
49
|
--split-hunks Experimental same-file hunk planning (default: off)
|
|
49
|
-
--scope=<scope> Scope for "split
|
|
50
|
+
--scope=<scope> Scope for "split|split plan": staged, all
|
|
50
51
|
--file=<path> Split-plan artifact or commit-message file
|
|
51
52
|
--range=<revision> Git revision/range for "policy check" (default: HEAD)
|
|
52
53
|
--reasoning=<level> Set reasoning effort (enabled by default: medium)
|
|
@@ -72,11 +73,11 @@ function showHelp() {
|
|
|
72
73
|
aicommit stats Show local acceptance, quality, latency, and token trends
|
|
73
74
|
aicommit Commit changes in current directory (Chinese)
|
|
74
75
|
aicommit --lang=en Generate English commit message
|
|
75
|
-
aicommit -p deepseek
|
|
76
|
-
aicommit split
|
|
77
|
-
aicommit split
|
|
78
|
-
aicommit split
|
|
79
|
-
aicommit split
|
|
76
|
+
aicommit -p deepseek -m chat Select a configured provider and model
|
|
77
|
+
aicommit split Choose staged/all scope, then plan logical commits
|
|
78
|
+
aicommit split --scope=staged Split only the reviewed index snapshot
|
|
79
|
+
aicommit split --scope=all --yes Split the complete working tree non-interactively
|
|
80
|
+
aicommit split --scope=staged --split-hunks Split eligible text hunks
|
|
80
81
|
aicommit split plan --scope=staged --file=.aicommit-plan.json --yes
|
|
81
82
|
aicommit split apply --file=.aicommit-plan.json --yes
|
|
82
83
|
aicommit split resume --yes Resume pending groups from the checkpoint
|
|
@@ -109,6 +110,7 @@ function parsedDefaults(overrides = {}) {
|
|
|
109
110
|
targetPath: null,
|
|
110
111
|
cliLang: null,
|
|
111
112
|
cliProvider: null,
|
|
113
|
+
cliModel: null,
|
|
112
114
|
cliReasoning: null,
|
|
113
115
|
output: 'text',
|
|
114
116
|
debug: false,
|
|
@@ -203,14 +205,23 @@ export function parseArgs(args = process.argv.slice(2)) {
|
|
|
203
205
|
|
|
204
206
|
let splitCommand = null;
|
|
205
207
|
if (args[0] === 'split') {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
+
const requestedAction = args[1];
|
|
209
|
+
const actions = ['run', 'plan', 'apply', 'resume', 'abort'];
|
|
210
|
+
if (!requestedAction || requestedAction.startsWith('-')) {
|
|
211
|
+
// Keep the common path short: `aicommit split` is the interactive
|
|
212
|
+
// split flow, while explicit actions remain available for automation
|
|
213
|
+
// and recovery.
|
|
214
|
+
splitCommand = 'run';
|
|
215
|
+
args = args.slice(1);
|
|
216
|
+
} else if (actions.includes(requestedAction)) {
|
|
217
|
+
splitCommand = requestedAction;
|
|
218
|
+
args = args.slice(2);
|
|
219
|
+
} else {
|
|
208
220
|
throw fail(
|
|
209
221
|
ERROR_CATEGORIES.CONFIG,
|
|
210
222
|
'split requires one action: run, plan, apply, resume, or abort.',
|
|
211
223
|
);
|
|
212
224
|
}
|
|
213
|
-
args = args.slice(2);
|
|
214
225
|
}
|
|
215
226
|
|
|
216
227
|
const doctor = args[0] === 'doctor';
|
|
@@ -219,6 +230,7 @@ export function parseArgs(args = process.argv.slice(2)) {
|
|
|
219
230
|
let targetPath = null;
|
|
220
231
|
let cliLang = null;
|
|
221
232
|
let cliProvider = null;
|
|
233
|
+
let cliModel = null;
|
|
222
234
|
let cliReasoning = null;
|
|
223
235
|
let output = 'text';
|
|
224
236
|
let debug = false;
|
|
@@ -378,6 +390,23 @@ export function parseArgs(args = process.argv.slice(2)) {
|
|
|
378
390
|
continue;
|
|
379
391
|
}
|
|
380
392
|
|
|
393
|
+
if (arg === '-m' || arg === '--model') {
|
|
394
|
+
cliModel = takeValue(args, i, arg, 'name');
|
|
395
|
+
i++;
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (arg.startsWith('--model=')) {
|
|
400
|
+
cliModel = arg.slice('--model='.length);
|
|
401
|
+
if (!cliModel) throw fail(ERROR_CATEGORIES.CONFIG, 'Missing value for --model.');
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (arg.startsWith('-m') && arg.length > 2) {
|
|
406
|
+
cliModel = arg.slice(2);
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
|
|
381
410
|
if (!arg.startsWith('-')) {
|
|
382
411
|
if (targetPath) {
|
|
383
412
|
throw fail(
|
|
@@ -430,7 +459,7 @@ export function parseArgs(args = process.argv.slice(2)) {
|
|
|
430
459
|
'split apply reads its scope from the plan; do not pass --scope or --split-hunks.',
|
|
431
460
|
);
|
|
432
461
|
}
|
|
433
|
-
if (cliProvider || cliLang || cliReasoning || dryRun) {
|
|
462
|
+
if (cliProvider || cliModel || cliLang || cliReasoning || dryRun) {
|
|
434
463
|
throw fail(
|
|
435
464
|
ERROR_CATEGORIES.CONFIG,
|
|
436
465
|
'split apply accepts only --file, --yes, --output, and --debug.',
|
|
@@ -442,6 +471,7 @@ export function parseArgs(args = process.argv.slice(2)) {
|
|
|
442
471
|
split ||
|
|
443
472
|
splitHunks ||
|
|
444
473
|
cliProvider ||
|
|
474
|
+
cliModel ||
|
|
445
475
|
cliLang ||
|
|
446
476
|
cliReasoning ||
|
|
447
477
|
dryRun
|
|
@@ -475,15 +505,23 @@ export function parseArgs(args = process.argv.slice(2)) {
|
|
|
475
505
|
) {
|
|
476
506
|
throw fail(
|
|
477
507
|
ERROR_CATEGORIES.CONFIG,
|
|
478
|
-
'config accepts only an optional path, --provider, --output, and --debug.',
|
|
508
|
+
'config accepts only an optional path, --provider, --model, --output, and --debug.',
|
|
479
509
|
);
|
|
480
510
|
}
|
|
481
|
-
if (configAction === 'path' && cliProvider) {
|
|
482
|
-
throw fail(ERROR_CATEGORIES.CONFIG, 'config path does not accept --provider.');
|
|
511
|
+
if (configAction === 'path' && (cliProvider || cliModel)) {
|
|
512
|
+
throw fail(ERROR_CATEGORIES.CONFIG, 'config path does not accept --provider or --model.');
|
|
483
513
|
}
|
|
484
514
|
if (
|
|
485
515
|
policyAction &&
|
|
486
|
-
(cliProvider ||
|
|
516
|
+
(cliProvider ||
|
|
517
|
+
cliModel ||
|
|
518
|
+
cliLang ||
|
|
519
|
+
cliReasoning ||
|
|
520
|
+
split ||
|
|
521
|
+
splitHunks ||
|
|
522
|
+
splitCommand ||
|
|
523
|
+
dryRun ||
|
|
524
|
+
yes)
|
|
487
525
|
) {
|
|
488
526
|
throw fail(
|
|
489
527
|
ERROR_CATEGORIES.CONFIG,
|
|
@@ -500,6 +538,7 @@ export function parseArgs(args = process.argv.slice(2)) {
|
|
|
500
538
|
presetAction &&
|
|
501
539
|
(targetPath ||
|
|
502
540
|
cliProvider ||
|
|
541
|
+
cliModel ||
|
|
503
542
|
cliLang ||
|
|
504
543
|
cliReasoning ||
|
|
505
544
|
split ||
|
|
@@ -525,7 +564,7 @@ export function parseArgs(args = process.argv.slice(2)) {
|
|
|
525
564
|
) {
|
|
526
565
|
throw fail(
|
|
527
566
|
ERROR_CATEGORIES.CONFIG,
|
|
528
|
-
'doctor accepts only --provider, --output, and --debug options.',
|
|
567
|
+
'doctor accepts only --provider, --model, --output, and --debug options.',
|
|
529
568
|
);
|
|
530
569
|
}
|
|
531
570
|
|
|
@@ -533,6 +572,7 @@ export function parseArgs(args = process.argv.slice(2)) {
|
|
|
533
572
|
targetPath,
|
|
534
573
|
cliLang,
|
|
535
574
|
cliProvider,
|
|
575
|
+
cliModel,
|
|
536
576
|
cliReasoning,
|
|
537
577
|
output,
|
|
538
578
|
debug,
|
package/src/completion.js
CHANGED
|
@@ -11,6 +11,7 @@ const TOP_LEVEL = [
|
|
|
11
11
|
'--version',
|
|
12
12
|
'--lang',
|
|
13
13
|
'--provider',
|
|
14
|
+
'--model',
|
|
14
15
|
'--split-hunks',
|
|
15
16
|
'--scope',
|
|
16
17
|
'--reasoning',
|
|
@@ -30,7 +31,7 @@ _aicommit() {
|
|
|
30
31
|
command="\${COMP_WORDS[1]}"
|
|
31
32
|
|
|
32
33
|
case "$command" in
|
|
33
|
-
config) COMPREPLY=( $(compgen -W "show validate path --provider --output --debug" -- "$cur") ); return ;;
|
|
34
|
+
config) COMPREPLY=( $(compgen -W "show validate path --provider --model --output --debug" -- "$cur") ); return ;;
|
|
34
35
|
policy) COMPREPLY=( $(compgen -W "template check --file --range --output --debug" -- "$cur") ); return ;;
|
|
35
36
|
preset) COMPREPLY=( $(compgen -W "show validate path install rollback --file --output --debug" -- "$cur") ); return ;;
|
|
36
37
|
completion) COMPREPLY=( $(compgen -W "bash zsh fish" -- "$cur") ); return ;;
|
|
@@ -66,6 +67,7 @@ _aicommit() {
|
|
|
66
67
|
'--version[show version]'
|
|
67
68
|
'--lang=[commit language]:language:(zh en)'
|
|
68
69
|
'--provider=[provider name]:provider:'
|
|
70
|
+
'--model=[model name]:model:'
|
|
69
71
|
'--split-hunks[experimental same-file hunk splitting]'
|
|
70
72
|
'--scope=[split scope]:scope:(staged all)'
|
|
71
73
|
'--file=[split plan or commit-message file]:path:_files'
|
|
@@ -115,6 +117,7 @@ complete -c aicommit -s h -l help -d 'Show help'
|
|
|
115
117
|
complete -c aicommit -s v -l version -d 'Show version'
|
|
116
118
|
complete -c aicommit -s l -l lang -x -a 'zh en' -d 'Commit language'
|
|
117
119
|
complete -c aicommit -s p -l provider -x -d 'Provider name'
|
|
120
|
+
complete -c aicommit -s m -l model -x -d 'Model name'
|
|
118
121
|
complete -c aicommit -l split-hunks -d 'Experimental same-file hunk splitting'
|
|
119
122
|
complete -c aicommit -l scope -x -a 'staged all' -d 'Split plan scope'
|
|
120
123
|
complete -c aicommit -l file -r -d 'Split plan file'
|
package/src/config-command.js
CHANGED
|
@@ -28,7 +28,10 @@ function printPaths(paths) {
|
|
|
28
28
|
);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
export async function runConfigCommand(
|
|
31
|
+
export async function runConfigCommand(
|
|
32
|
+
action,
|
|
33
|
+
{ provider = null, model = null, machineOutput = false } = {},
|
|
34
|
+
) {
|
|
32
35
|
const projectRoot = getProjectRoot();
|
|
33
36
|
const paths = await inspectConfigPaths(projectRoot);
|
|
34
37
|
if (action === 'path') {
|
|
@@ -36,18 +39,17 @@ export async function runConfigCommand(action, { provider = null, machineOutput
|
|
|
36
39
|
return { exitReason: 'config_path', data: { projectRoot, paths } };
|
|
37
40
|
}
|
|
38
41
|
|
|
39
|
-
const loaded = await loadConfig(provider, { resolveCredentials: false });
|
|
42
|
+
const loaded = await loadConfig(provider, { model, resolveCredentials: false });
|
|
40
43
|
const data = {
|
|
41
44
|
projectRoot: loaded.projectRoot,
|
|
42
45
|
sources: loaded.loaded,
|
|
43
46
|
provider: loaded.providerName,
|
|
47
|
+
model: loaded.modelName,
|
|
44
48
|
paths,
|
|
45
49
|
};
|
|
46
50
|
if (action === 'validate') {
|
|
47
51
|
if (!machineOutput) {
|
|
48
|
-
console.log(
|
|
49
|
-
`Configuration valid${loaded.providerName ? ` for provider "${loaded.providerName}"` : ''}.`,
|
|
50
|
-
);
|
|
52
|
+
console.log(`Configuration valid for ${loaded.providerName}/${loaded.modelName}.`);
|
|
51
53
|
printPaths(paths);
|
|
52
54
|
}
|
|
53
55
|
return { exitReason: 'config_valid', data };
|