@hifullmoon/aicommit 2.0.1 → 2.2.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.
@@ -1,19 +1,18 @@
1
1
  # Provider compatibility / Provider 兼容表
2
2
 
3
- Provider presets choose setup defaults; adapters own request/response dialects; the core owns Git state, user interaction, HTTPS enforcement, retry, timeout, authorization, and machine output. Adding a compatible preset or an extension adapter does not modify the core Git/interaction flow.
3
+ Built-in setup defaults choose an adapter and endpoint; adapters own request/response dialects; the core owns Git state, user interaction, HTTPS enforcement, retry, timeout, authorization, and machine output.
4
4
 
5
- Provider preset 只选择 setup 默认值;adapter 负责请求/响应方言;核心负责 Git 状态、用户交互、HTTPS、重试、超时、鉴权与机器输出。新增兼容 preset 或 extension adapter 无需修改核心 Git/交互流程。
5
+ 内置 setup 默认值负责选择 adapter 和端点;adapter 负责请求/响应方言;核心负责 Git 状态、用户交互、HTTPS、重试、超时、鉴权与机器输出。
6
6
 
7
- | Provider / adapter | Endpoint and auth / 端点与鉴权 | Streaming / 流式 | Reasoning / 推理 | Token and usage mapping / token 与 usage | Notes / 说明 |
8
- | ---------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
9
- | OpenAI / `openai` | Official HTTPS Chat Completions; Bearer key / 官方 HTTPS;Bearer key | SSE | Native for recognized `o*`/`gpt-5*`; model-dependent otherwise / 已识别推理模型原生支持 | `max_completion_tokens` for reasoning models, otherwise `max_tokens`; OpenAI usage | Unsupported effort is rejected locally for known model generations / 已知模型不支持的 effort 会本地拒绝 |
10
- | OpenRouter / `openrouter` | OpenRouter HTTPS; Bearer key; `X-Title: aicommit` | SSE | `reasoning.effort` | `max_tokens`; OpenAI-style usage | Model IDs commonly include vendor prefix / model 通常含厂商前缀 |
11
- | DeepSeek / `deepseek` | DeepSeek HTTPS; Bearer key | SSE | `thinking.type` plus normalized effort / thinking 与归一 effort | `max_tokens`; compatible usage | `medium`/`xhigh` map to supported high behavior where required / 必要时映射为 high |
12
- | MiniMax / `minimax` | MiniMax HTTPS; Bearer key | SSE | `reasoning_split` and thinking switch | `max_tokens`; compatible usage | Adapter removes conflicting switches before send / 发送前移除冲突开关 |
13
- | Kimi Code / `custom` | Kimi Code OpenAI-compatible HTTPS; Bearer key / Kimi Code OpenAI 兼容 HTTPS | SSE | Server default; no vendor fields injected / 使用服务端默认值,不注入厂商字段 | `max_tokens`; OpenAI-style usage | Bundled preset uses `kimi-for-coding`; membership and Platform keys are distinct / 会员与开放平台 key 不通用 |
14
- | Ollama native / `ollama` | Loopback `/api/chat` or `/api/generate`; normally keyless HTTP | Complete JSON in v1; native NDJSON streaming is not consumed / v1 使用完整 JSON | Native `think` boolean | `options.num_predict`; `prompt_eval_count` + `eval_count` | OpenAI-compatible `/v1/chat/completions` uses the compatible shape instead / `/v1` 使用兼容方言 |
15
- | Custom compatible / `custom` | HTTPS remote or loopback HTTP; optional core Bearer key | SSE when endpoint supports Chat Completions events | No vendor fields by default; explicit `enabledBody`/`disabledBody` / 默认不注入厂商字段 | `max_tokens`; common OpenAI/Anthropic/Ollama usage fields normalized | Validate the endpoint before trusting it with code or credentials / 发送代码前验证端点 |
16
- | Extension / `extension:<id>` | Core HTTPS/loopback transport and optional Bearer only / 核心传输与可选 Bearer | Declared conservative in API v1 / v1 保守声明 | Adapter operation can transform non-secret reasoning config | Adapter maps body and normalized response | No custom credential scheme or headers; credential-like body fields rejected / 不支持自定义鉴权或 header |
7
+ | Provider / adapter | Endpoint and auth / 端点与鉴权 | Streaming / 流式 | Reasoning / 推理 | Token and usage mapping / token 与 usage | Notes / 说明 |
8
+ | ---------------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
9
+ | OpenAI / `openai` | Official HTTPS Chat Completions; Bearer key / 官方 HTTPS;Bearer key | SSE | Native for recognized `o*`/`gpt-5*`; model-dependent otherwise / 已识别推理模型原生支持 | `max_completion_tokens` for reasoning models, otherwise `max_tokens`; OpenAI usage | Unsupported effort is rejected locally for known model generations / 已知模型不支持的 effort 会本地拒绝 |
10
+ | OpenRouter / `openrouter` | OpenRouter HTTPS; Bearer key; `X-Title: aicommit` | SSE | `reasoning.effort` | `max_tokens`; OpenAI-style usage | Model IDs commonly include vendor prefix / model 通常含厂商前缀 |
11
+ | DeepSeek / `deepseek` | DeepSeek HTTPS; Bearer key | SSE | `thinking.type` plus normalized effort / thinking 与归一 effort | `max_tokens`; compatible usage | `medium`/`xhigh` map to supported high behavior where required / 必要时映射为 high |
12
+ | MiniMax / `minimax` | MiniMax HTTPS; Bearer key | SSE | `reasoning_split` and thinking switch | `max_tokens`; compatible usage | Adapter removes conflicting switches before send / 发送前移除冲突开关 |
13
+ | Kimi Code / `custom` | Kimi Code OpenAI-compatible HTTPS; Bearer key / Kimi Code OpenAI 兼容 HTTPS | SSE | Server default; no vendor fields injected / 使用服务端默认值,不注入厂商字段 | `max_tokens`; OpenAI-style usage | Bundled preset uses `kimi-for-coding`; membership and Platform keys are distinct / 会员与开放平台 key 不通用 |
14
+ | Ollama native / `ollama` | Loopback `/api/chat` or `/api/generate`; normally keyless HTTP | Complete JSON in v1; native NDJSON streaming is not consumed / v1 使用完整 JSON | Native `think` boolean | `options.num_predict`; `prompt_eval_count` + `eval_count` | OpenAI-compatible `/v1/chat/completions` uses the compatible shape instead / `/v1` 使用兼容方言 |
15
+ | Custom compatible / `custom` | HTTPS remote or loopback HTTP; optional core Bearer key | SSE when endpoint supports Chat Completions events | No vendor fields by default; explicit `enabledBody`/`disabledBody` / 默认不注入厂商字段 | `max_tokens`; common OpenAI/Anthropic/Ollama usage fields normalized | Validate the endpoint before trusting it with code or credentials / 发送代码前验证端点 |
17
16
 
18
17
  ## Compatibility contract / 兼容契约
19
18
 
@@ -21,17 +20,6 @@ All built-in adapters return `content`, optional `reasoning`, normalized usage,
21
20
 
22
21
  所有内置 adapter 返回 `content`、可选 `reasoning`、标准 usage、finish reason、raw response、capability、attempts 与 latency。仅 429、部分 5xx、网络/响应中断会重试;鉴权、参数与安全错误不会重试。
23
22
 
24
- Preset compatibility is declared by core version range plus `adapterContract: 1`. Run:
23
+ Use `aicommit doctor -p provider-name` to verify the selected adapter and live endpoint. Reuse a built-in adapter when only setup defaults change, and use `custom` for an OpenAI-compatible endpoint with optional body switches. A protocol requiring a different transport, streaming parser, or credential scheme needs core support.
25
24
 
26
- Preset 兼容性由核心版本范围与 `adapterContract: 1` 共同声明:
27
-
28
- ```bash
29
- aicommit preset show
30
- aicommit preset validate --file=provider-presets.json
31
- aicommit preset install --file=provider-presets.json
32
- aicommit doctor -p provider-name
33
- ```
34
-
35
- Use an existing built-in adapter when only setup defaults change. Use `custom` for an OpenAI-compatible endpoint with optional body switches. Use a reviewed `providerAdapter` extension only when the JSON request/response dialect differs and core Bearer authorization is sufficient. A protocol requiring different transport, streaming parser, or credential scheme is not compatible with extension API v1 and must not be disguised as a preset.
36
-
37
- 若只改变 setup 默认值,应复用内置 adapter;OpenAI-compatible endpoint 及少量 body 开关使用 `custom`;仅当 JSON 请求/响应方言不同且核心 Bearer 鉴权足够时,才使用已审查的 `providerAdapter` 扩展。若协议需要不同传输、流解析或鉴权方案,则不兼容扩展 API v1,不能伪装成 preset。
25
+ 使用 `aicommit doctor -p provider-name` 校验所选 adapter 与在线端点。若只改变 setup 默认值,应复用内置 adapter;OpenAI-compatible endpoint 及少量 body 开关使用 `custom`。需要不同传输、流解析或鉴权方案的协议必须由核心直接支持。
@@ -46,7 +46,7 @@ Both paths call the same validator. `--output=json` returns the effective policy
46
46
 
47
47
  1. Move Conventional Commit types, scope rules, subject length, body rules, breaking-change handling, and language out of free-form `prompt` text and into `.aicommit.policy.json`.
48
48
  2. Declare every field instead of relying on personal defaults. Start with optional scopes and narrow the values only after current history has been sampled.
49
- 3. Keep provider credentials in `~/.aicommit.config.json` or environment variables; never copy them into the repository policy.
49
+ 3. Keep provider credentials in `~/.aicommit/config.json` or environment variables; never copy them into the repository policy.
50
50
  4. If commitlint already defines `type-enum`, `scope-enum`, `subject-max-length`, or `header-max-length`, keep that file committed. AICommit reads recognized scalar values as data and never executes the config.
51
51
  5. Run the local hook and CI example on the same known-good and known-bad messages. Their `policyFingerprint` and issue codes must match before making the gate required.
52
52
 
@@ -54,7 +54,7 @@ Both paths call the same validator. `--output=json` returns the effective policy
54
54
 
55
55
  1. 将 Conventional Commit 类型、scope 规则、标题长度、正文规则、破坏性变更和语言要求从自由文本 `prompt` 迁移到 `.aicommit.policy.json`。
56
56
  2. 明确声明所有字段,不依赖个人默认值。可先保留可选 scope,再根据现有提交历史逐步收紧取值。
57
- 3. provider 凭据继续放在 `~/.aicommit.config.json` 或环境变量中,绝不要复制到仓库策略。
57
+ 3. provider 凭据继续放在 `~/.aicommit/config.json` 或环境变量中,绝不要复制到仓库策略。
58
58
  4. 如果 commitlint 已定义 `type-enum`、`scope-enum`、`subject-max-length` 或 `header-max-length`,继续提交该配置。AICommit 只按数据读取识别出的标量规则,不执行配置文件。
59
59
  5. 用同一组已知正确/错误消息分别运行本地 hook 与 CI 示例;在强制启用门禁前,确认两者的 `policyFingerprint` 和问题代码一致。
60
60
 
@@ -15,22 +15,19 @@ JSON mode keeps one machine object on stdout and diagnostics on stderr. The `err
15
15
 
16
16
  JSON 模式保证 stdout 只有一个机器对象,诊断进入 stderr。`error.category` 与进程退出码可稳定用于自动化。
17
17
 
18
- | Symptom / 症状 | Category / exit | Likely cause / 常见原因 | Check and recovery / 检查与恢复 |
19
- | ------------------------------------------------------------------------------- | ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
20
- | Config fails before Git/provider access / 在 Git/provider 前配置失败 | `config` / `2` | Malformed JSON, invalid URL, unsupported option, missing credential / JSON 损坏、URL 或选项无效、凭据缺失 | `aicommit config path`; `aicommit config validate`; fix only the user-owned file shown |
21
- | Project settings are ignored / 项目设置被忽略 | warning | Repository config tried to set endpoint, credential, extension, reasoning, metrics, or raise a ceiling / 仓库配置尝试设置连接、凭据、扩展或提高预算 | Move trusted connection settings to `~/.aicommit.config.json`; keep team rules in `.aicommit.policy.json` |
22
- | Not a Git repository, conflict, empty index / 非仓库、冲突或 index 为空 | `git_state` / `3` | Wrong directory or unsafe Git state / 目录错误或 Git 状态不安全 | `git status`; `aicommit /absolute/repo/path`; resolve conflicts before retrying |
23
- | DNS, connection, or timeout / DNS、连接或超时 | `network` / `4` | Endpoint unavailable, proxy/TLS issue, budget too short / endpoint 不可达、代理/TLS、超时过短 | `aicommit doctor -p NAME`; verify HTTPS URL; raise user-owned `timeoutMs` only if expected |
24
- | HTTP authentication/rate/parameter failure / 鉴权、限流或参数失败 | `provider` / `5` | Wrong key/model/body; non-retryable 4xx / key、model、body 错误 | Check `apiKeyEnv`, model and compatibility table; authentication is never retried automatically |
25
- | Empty or malformed model reply / 空或畸形回复 | `response_format` / `6` | Unsupported response dialect, token limit, policy/extension validator failure / 响应方言、token 限制、policy/扩展校验失败 | Raise `maxTokens`, choose the matching adapter, inspect validator issue code; one correction is already attempted |
26
- | `split run --scope=all --yes` stops before API call / split 非交互在 API 前停止 | `sensitive_data` / `7` | Complete untracked scan found sensitive-looking data / 完整未跟踪扫描发现疑似敏感数据 | Review/stage intended files explicitly; do not bypass without checking the actual content |
27
- | Commit aborts after generation / 生成后提交中止 | `concurrent_modification` / `8` | Index/worktree changed during the protected window / 受保护窗口中 index/worktree 被修改 | Review `git status`, restore the intended snapshot, and generate again |
28
- | Split stopped after one or more commits / split 部分提交后停止 | reported Git failure | Hook, crash, SIGINT, or concurrent pending edit / hook、崩溃、中断或待处理文件变化 | Run `aicommit split resume`; if another Git workflow already replaced the transaction, use `aicommit split abort`(只删除恢复元数据,不改提交或工作区) |
29
- | Preset install/rollback fails / preset 安装或回滚失败 | `config` / `2` | Schema/core range/adapter contract mismatch or no valid backup / schema、核心范围、adapter contract 不匹配或无备份 | `aicommit preset validate --file=...`; `aicommit preset show`; follow the compatibility guide |
30
- | Extension requires Node 20+ / 扩展要求 Node 20+ | `config` / `2` | Node 18 intentionally has no executable-extension fallback / Node 18 有意不降级运行扩展 | Upgrade Node for extensions or remove user manifest entries; core CLI still supports Node 18 |
31
- | Extension timeout or permission error / 扩展超时或权限错误 | warning or `response_format` | Extension exceeded its limit or tried denied filesystem/process access / 扩展超时或访问被拒资源 | Review the extension, keep it single-file, raise `extensions.timeoutMs` cautiously; validator failures are fail-closed |
32
- | npm provenance is absent or invalid / npm provenance 缺失或失败 | npm audit failure | Old npm CLI, non-trusted release, or wrong version / npm 过旧、非可信发布或版本错误 | Upgrade npm; run `npm audit signatures`; install only a version linked to the official workflow |
33
-
34
- If a failure remains, capture `aicommit doctor --output=json`, Node/Git versions, the error category, and redacted config sources. Never attach a diff, commit message, config file, API key, reasoning trace, extension source containing secrets, or credential-helper output to a public issue.
35
-
36
- 若问题仍未解决,请记录 `aicommit doctor --output=json`、Node/Git 版本、错误分类与脱敏后的配置来源。不要在公开 issue 中附加 diff、commit message、配置文件、API key、reasoning、含秘密的扩展源码或 credential-helper 输出。
18
+ | Symptom / 症状 | Category / exit | Likely cause / 常见原因 | Check and recovery / 检查与恢复 |
19
+ | ------------------------------------------------------------------------------- | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
20
+ | Config fails before Git/provider access / 在 Git/provider 前配置失败 | `config` / `2` | Malformed JSON, invalid URL, unsupported option, missing credential / JSON 损坏、URL 或选项无效、凭据缺失 | `aicommit config path`; `aicommit config validate`; fix only the user-owned file shown |
21
+ | Project settings are ignored / 项目设置被忽略 | warning | Repository config tried to set endpoint, credential, reasoning, or raise a ceiling / 仓库配置尝试设置连接、凭据、推理或提高预算 | Move trusted connection settings to `~/.aicommit/config.json`; keep team rules in `.aicommit.policy.json` |
22
+ | Not a Git repository, conflict, empty index / 非仓库、冲突或 index 为空 | `git_state` / `3` | Wrong directory or unsafe Git state / 目录错误或 Git 状态不安全 | `git status`; `aicommit /absolute/repo/path`; resolve conflicts before retrying |
23
+ | DNS, connection, or timeout / DNS、连接或超时 | `network` / `4` | Endpoint unavailable, proxy/TLS issue, budget too short / endpoint 不可达、代理/TLS、超时过短 | `aicommit doctor -p NAME`; verify HTTPS URL; raise user-owned `timeoutMs` only if expected |
24
+ | HTTP authentication/rate/parameter failure / 鉴权、限流或参数失败 | `provider` / `5` | Wrong key/model/body; non-retryable 4xx / key、model、body 错误 | Check `apiKeyEnv`, model and compatibility table; authentication is never retried automatically |
25
+ | Empty or malformed model reply / 空或畸形回复 | `response_format` / `6` | Unsupported response dialect, token limit, or policy validation failure / 响应方言、token 限制或 policy 校验失败 | Raise `maxTokens`, choose the matching adapter, inspect validator issue code; one correction is already attempted |
26
+ | `split run --scope=all --yes` stops before API call / split 非交互在 API 前停止 | `sensitive_data` / `7` | Complete untracked scan found sensitive-looking data / 完整未跟踪扫描发现疑似敏感数据 | Review/stage intended files explicitly; do not bypass without checking the actual content |
27
+ | Commit aborts after generation / 生成后提交中止 | `concurrent_modification` / `8` | Index/worktree changed during the protected window / 受保护窗口中 index/worktree 被修改 | Review `git status`, restore the intended snapshot, and generate again |
28
+ | Split stopped after one or more commits / split 部分提交后停止 | reported Git failure | Hook, crash, SIGINT, or concurrent pending edit / hook、崩溃、中断或待处理文件变化 | Run `aicommit split resume`; if another Git workflow already replaced the transaction, use `aicommit split abort`(只删除恢复元数据,不改提交或工作区) |
29
+ | npm provenance is absent or invalid / npm provenance 缺失或失败 | npm audit failure | Old npm CLI, non-trusted release, or wrong version / npm 过旧、非可信发布或版本错误 | Upgrade npm; run `npm audit signatures`; install only a version linked to the official workflow |
30
+
31
+ If a failure remains, capture `aicommit doctor --output=json`, Node/Git versions, the error category, and redacted config sources. Never attach a diff, commit message, config file, API key, reasoning trace, or credential-helper output to a public issue.
32
+
33
+ 若问题仍未解决,请记录 `aicommit doctor --output=json`、Node/Git 版本、错误分类与脱敏后的配置来源。不要在公开 issue 中附加 diff、commit message、配置文件、API key、reasoning 或 credential-helper 输出。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hifullmoon/aicommit",
3
- "version": "2.0.1",
3
+ "version": "2.2.0",
4
4
  "description": "Safe, local-first AI commit message generator for Git workflows",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "kind": "aicommit-provider-presets",
3
3
  "schemaVersion": 2,
4
- "version": "2.0.0",
4
+ "version": "2.1.0",
5
5
  "compatibility": {
6
6
  "coreMinimum": "1.5.1",
7
7
  "coreMaximumExclusive": "3.0.0",
@@ -18,6 +18,18 @@
18
18
  "gpt-4o": {
19
19
  "label": "GPT-4o",
20
20
  "modelId": "gpt-4o"
21
+ },
22
+ "gpt-5.6-sol": {
23
+ "label": "GPT-5.6 Sol",
24
+ "modelId": "gpt-5.6-sol"
25
+ },
26
+ "gpt-5.6-terra": {
27
+ "label": "GPT-5.6 Terra",
28
+ "modelId": "gpt-5.6-terra"
29
+ },
30
+ "gpt-5.6-luna": {
31
+ "label": "GPT-5.6 Luna",
32
+ "modelId": "gpt-5.6-luna"
21
33
  }
22
34
  }
23
35
  },
@@ -31,6 +43,10 @@
31
43
  "chat": {
32
44
  "label": "DeepSeek Chat",
33
45
  "modelId": "deepseek-v4-flash"
46
+ },
47
+ "pro": {
48
+ "label": "DeepSeek V4 Pro",
49
+ "modelId": "deepseek-v4-pro"
34
50
  }
35
51
  }
36
52
  },
@@ -43,6 +59,42 @@
43
59
  "models": {
44
60
  "default": {
45
61
  "modelId": "openai/gpt-4o-mini"
62
+ },
63
+ "auto": {
64
+ "label": "OpenRouter Auto",
65
+ "modelId": "openrouter/auto"
66
+ },
67
+ "gpt-5.6-terra": {
68
+ "label": "GPT-5.6 Terra",
69
+ "modelId": "openai/gpt-5.6-terra"
70
+ },
71
+ "claude-sonnet-5": {
72
+ "label": "Claude Sonnet 5",
73
+ "modelId": "anthropic/claude-sonnet-5"
74
+ },
75
+ "gemini-3.7-flash": {
76
+ "label": "Gemini 3.7 Flash",
77
+ "modelId": "google/gemini-3.7-flash"
78
+ },
79
+ "deepseek-v4-pro": {
80
+ "label": "DeepSeek V4 Pro",
81
+ "modelId": "deepseek/deepseek-v4-pro"
82
+ },
83
+ "qwen3.8-max": {
84
+ "label": "Qwen3.8 Max",
85
+ "modelId": "qwen/qwen3.8-max"
86
+ },
87
+ "glm-5.3": {
88
+ "label": "GLM-5.3",
89
+ "modelId": "z-ai/glm-5.3"
90
+ },
91
+ "kimi-k3": {
92
+ "label": "Kimi K3",
93
+ "modelId": "moonshotai/kimi-k3"
94
+ },
95
+ "grok-4.6": {
96
+ "label": "Grok 4.6",
97
+ "modelId": "x-ai/grok-4.6"
46
98
  }
47
99
  }
48
100
  },
@@ -86,6 +138,14 @@
86
138
  "qwen": {
87
139
  "label": "Qwen 3 8B",
88
140
  "modelId": "qwen3:8b"
141
+ },
142
+ "deepseek": {
143
+ "label": "DeepSeek R1 8B",
144
+ "modelId": "deepseek-r1:8b"
145
+ },
146
+ "gpt-oss": {
147
+ "label": "GPT-OSS 20B",
148
+ "modelId": "gpt-oss:20b"
89
149
  }
90
150
  }
91
151
  }
package/src/api.js CHANGED
@@ -8,7 +8,6 @@ import {
8
8
  validateCommitCandidate,
9
9
  } from './policy.js';
10
10
  import { encodeUntrustedData } from './trust.js';
11
- import { extensionHostFor, resolveProviderAdapter } from './extensions.js';
12
11
 
13
12
  // Default per-request timeout; overridable via the "timeoutMs" config key.
14
13
  const DEFAULT_TIMEOUT_MS = 120_000;
@@ -167,7 +166,7 @@ async function fetchWithRetry(apiUrl, init, timeout, configuredPolicy, consume)
167
166
  export async function requestGeneration(config, request) {
168
167
  secureEndpoint(config.apiUrl);
169
168
  const timeout = config.timeoutMs || DEFAULT_TIMEOUT_MS;
170
- const adapter = await resolveProviderAdapter(config, getProviderAdapter);
169
+ const adapter = getProviderAdapter(config);
171
170
  const payload = await adapter.buildRequest({
172
171
  messages: request.messages,
173
172
  temperature: request.temperature,
@@ -370,7 +369,7 @@ function hitTokenLimit(data) {
370
369
  // happened. Disable it only for providers where we know the switch is valid;
371
370
  // unknown compatible endpoints keep their configured behavior.
372
371
  async function reasoningForFollowUp(config) {
373
- const adapter = await resolveProviderAdapter(config, getProviderAdapter);
372
+ const adapter = getProviderAdapter(config);
374
373
  return adapter.reasoningForFollowUp(config.reasoning);
375
374
  }
376
375
 
@@ -660,23 +659,7 @@ export async function generateCommitMessage(
660
659
  // Validate against the versioned policy and give the provider exactly one
661
660
  // cheap correction attempt. The diff is never re-sent: the prior reply plus
662
661
  // concrete violations are sufficient to repair formatting and constraints.
663
- const validate = async (candidate) => {
664
- const builtIn = validateCommitCandidate(candidate, { policy, diff });
665
- const host = extensionHostFor(config);
666
- if (!host) return builtIn;
667
- const extensionIssues = await host.validateMessage(candidate, policy);
668
- const issues = [...builtIn.issues, ...extensionIssues];
669
- const errors = issues.filter((item) => item.severity === 'error');
670
- const warnings = issues.filter((item) => item.severity === 'warning');
671
- return {
672
- ...builtIn,
673
- valid: errors.length === 0,
674
- needsCorrection: errors.length > 0,
675
- issues,
676
- errors,
677
- warnings,
678
- };
679
- };
662
+ const validate = (candidate) => validateCommitCandidate(candidate, { policy, diff });
680
663
 
681
664
  let validation = await validate(message);
682
665
  if (message.trim() && validation.needsCorrection) {
package/src/cli.js CHANGED
@@ -14,28 +14,24 @@ function showHelp() {
14
14
  ${chalk.bold('Usage:')}
15
15
  ${chalk.dim('$')} aicommit [path] [options]
16
16
  ${chalk.dim('$')} aicommit setup
17
- ${chalk.dim('$')} aicommit config <show|validate|path> [options]
18
- ${chalk.dim('$')} aicommit policy <template|check> [options]
19
- ${chalk.dim('$')} aicommit preset <show|validate|path|install|rollback> [options]
20
- ${chalk.dim('$')} aicommit completion <bash|zsh|fish>
21
17
  ${chalk.dim('$')} aicommit split [run|plan|apply|resume|abort] [options]
22
18
 
23
- ${chalk.bold('Commands:')}
19
+ ${chalk.bold('Everyday commands:')}
24
20
  setup Interactive configuration wizard
25
21
  doctor Diagnose runtime, config, credentials, and connectivity
22
+ split Plan and create file-level logical commits
23
+
24
+ ${chalk.bold('Advanced commands:')}
26
25
  config show Print the effective configuration with secrets redacted
27
26
  config validate Parse, merge, and validate configuration without reading credentials
28
27
  config path Print user, project, and team-policy paths
29
28
  policy template Print a safe repository team-policy template
30
29
  policy check Validate a message file or Git range with the effective team policy
31
- preset Inspect, validate, install, or roll back provider preset manifests
32
30
  completion Generate Bash, Zsh, or Fish completion on stdout
33
- split, split run Plan and create logical commits
34
31
  split plan Generate and export a fingerprinted JSON plan
35
32
  split apply Validate and apply an exported JSON plan
36
33
  split resume Resume the repository's unfinished transaction
37
34
  split abort Discard recovery metadata; keep commits and changes
38
- stats [action] Show quality/cost trends; show, clear, enable, disable
39
35
 
40
36
  ${chalk.bold('Arguments:')}
41
37
  path Target directory (default: current directory)
@@ -46,7 +42,6 @@ function showHelp() {
46
42
  -l, --lang=<zh|en> Commit message language (default: zh)
47
43
  -p, --provider=<name> Use the named provider from config "providers"
48
44
  -m, --model=<name> Use a named model from the selected provider
49
- --split-hunks Experimental same-file hunk planning (default: off)
50
45
  --scope=<scope> Scope for "split|split plan": staged, all
51
46
  --file=<path> Split-plan artifact or commit-message file
52
47
  --range=<revision> Git revision/range for "policy check" (default: HEAD)
@@ -58,35 +53,11 @@ function showHelp() {
58
53
  --debug Print debug info (parsed args, final config, etc.)
59
54
 
60
55
  ${chalk.bold('Examples:')}
61
- aicommit setup Run the interactive configuration wizard
62
- aicommit doctor Check runtime, config, credentials, and connectivity
63
- aicommit config show Show effective redacted configuration
64
- aicommit config validate --output=json Validate configuration for CI
65
- aicommit config path Show user and project config locations
66
- aicommit policy template > .aicommit.policy.json Create a committable policy
67
- aicommit policy check --file=.git/COMMIT_EDITMSG Validate a local commit message
68
- aicommit policy check --range=origin/main..HEAD --output=json Validate a CI range
69
- aicommit preset show Show the active versioned provider preset manifest
70
- aicommit preset install --file=provider-presets.json Install a compatible manifest
71
- aicommit preset rollback Restore the previously installed manifest
72
- aicommit completion zsh > _aicommit Generate Zsh completion
73
- aicommit stats Show local acceptance, quality, latency, and token trends
74
56
  aicommit Commit changes in current directory (Chinese)
75
57
  aicommit --lang=en Generate English commit message
76
- aicommit -p deepseek -m chat Select a configured provider and model
77
- aicommit split Choose staged/all scope, then plan logical commits
78
58
  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
81
- aicommit split plan --scope=staged --file=.aicommit-plan.json --yes
82
- aicommit split apply --file=.aicommit-plan.json --yes
83
- aicommit split resume --yes Resume pending groups from the checkpoint
84
- aicommit split abort --yes Discard a stale checkpoint without changing Git state
85
- aicommit --reasoning=low Stream reasoning; Ctrl+O expands/collapses it
86
59
  aicommit --dry-run Review a generated message without committing
87
60
  aicommit --yes Commit already staged changes without prompts
88
- aicommit --yes --output=json Emit one machine-readable result on stdout
89
- aicommit /path/to Commit changes in the specified directory
90
61
  `);
91
62
  }
92
63
 
@@ -115,7 +86,6 @@ function parsedDefaults(overrides = {}) {
115
86
  output: 'text',
116
87
  debug: false,
117
88
  split: null,
118
- splitHunks: false,
119
89
  splitCommand: null,
120
90
  splitPlanFile: null,
121
91
  dryRun: false,
@@ -126,10 +96,7 @@ function parsedDefaults(overrides = {}) {
126
96
  policyAction: null,
127
97
  policyMessageFile: null,
128
98
  policyRange: null,
129
- presetAction: null,
130
- presetFile: null,
131
99
  completionShell: null,
132
- statsAction: null,
133
100
  help: false,
134
101
  version: false,
135
102
  ...overrides,
@@ -137,6 +104,13 @@ function parsedDefaults(overrides = {}) {
137
104
  }
138
105
 
139
106
  export function parseArgs(args = process.argv.slice(2)) {
107
+ if (args[0] === 'stats' || args[0] === 'preset') {
108
+ throw fail(
109
+ ERROR_CATEGORIES.CONFIG,
110
+ `The "${args[0]}" command was removed to keep aicommit focused on commit generation.`,
111
+ );
112
+ }
113
+
140
114
  // "setup" is a standalone subcommand — it doesn't combine with the
141
115
  // commit-flow options, so short-circuit before parsing them.
142
116
  if (args[0] === 'setup') {
@@ -149,15 +123,6 @@ export function parseArgs(args = process.argv.slice(2)) {
149
123
  return parsedDefaults({ setup: true });
150
124
  }
151
125
 
152
- if (args[0] === 'stats') {
153
- const action = args[1] || 'show';
154
- const allowed = ['show', 'clear', 'enable', 'disable'];
155
- if (args.length > 2 || !allowed.includes(action)) {
156
- throw fail(ERROR_CATEGORIES.CONFIG, `stats accepts one action: ${allowed.join(', ')}.`);
157
- }
158
- return parsedDefaults({ statsAction: action });
159
- }
160
-
161
126
  if (args[0] === 'completion') {
162
127
  const shell = args[1];
163
128
  if (args.length !== 2 || !['bash', 'zsh', 'fish'].includes(shell)) {
@@ -182,18 +147,6 @@ export function parseArgs(args = process.argv.slice(2)) {
182
147
  args = args.slice(2);
183
148
  }
184
149
 
185
- let presetAction = null;
186
- if (args[0] === 'preset') {
187
- presetAction = args[1];
188
- if (!['show', 'validate', 'path', 'install', 'rollback'].includes(presetAction)) {
189
- throw fail(
190
- ERROR_CATEGORIES.CONFIG,
191
- 'preset requires one action: show, validate, path, install, or rollback.',
192
- );
193
- }
194
- args = args.slice(2);
195
- }
196
-
197
150
  let configAction = null;
198
151
  if (args[0] === 'config') {
199
152
  configAction = args[1];
@@ -235,11 +188,9 @@ export function parseArgs(args = process.argv.slice(2)) {
235
188
  let output = 'text';
236
189
  let debug = false;
237
190
  let split = null;
238
- let splitHunks = false;
239
191
  let splitPlanFile = null;
240
192
  let policyMessageFile = null;
241
193
  let policyRange = null;
242
- let presetFile = null;
243
194
  let splitScopeOption = false;
244
195
  let dryRun = false;
245
196
  let yes = false;
@@ -275,11 +226,6 @@ export function parseArgs(args = process.argv.slice(2)) {
275
226
  continue;
276
227
  }
277
228
 
278
- if (arg === '--split-hunks') {
279
- splitHunks = true;
280
- continue;
281
- }
282
-
283
229
  if (arg === '--scope') {
284
230
  splitScopeOption = true;
285
231
  split = takeValue(args, i, arg, 'staged|all');
@@ -302,7 +248,6 @@ export function parseArgs(args = process.argv.slice(2)) {
302
248
  if (arg === '--file') {
303
249
  const value = takeValue(args, i, arg, 'path');
304
250
  if (policyAction) policyMessageFile = value;
305
- else if (presetAction) presetFile = value;
306
251
  else splitPlanFile = value;
307
252
  i++;
308
253
  continue;
@@ -312,7 +257,6 @@ export function parseArgs(args = process.argv.slice(2)) {
312
257
  const value = arg.slice('--file='.length);
313
258
  if (!value) throw fail(ERROR_CATEGORIES.CONFIG, 'Missing value for --file.');
314
259
  if (policyAction) policyMessageFile = value;
315
- else if (presetAction) presetFile = value;
316
260
  else splitPlanFile = value;
317
261
  continue;
318
262
  }
@@ -430,12 +374,6 @@ export function parseArgs(args = process.argv.slice(2)) {
430
374
  if (!['text', 'json'].includes(output)) {
431
375
  throw fail(ERROR_CATEGORIES.CONFIG, `Invalid output mode: "${output}". Use text or json.`);
432
376
  }
433
- if (splitHunks && !['run', 'plan'].includes(splitCommand)) {
434
- throw fail(
435
- ERROR_CATEGORIES.CONFIG,
436
- '--split-hunks is only valid with "aicommit split run" or "aicommit split plan".',
437
- );
438
- }
439
377
  if (splitCommand) {
440
378
  if (['plan', 'apply'].includes(splitCommand) && !splitPlanFile) {
441
379
  throw fail(ERROR_CATEGORIES.CONFIG, `split ${splitCommand} requires --file=<path>.`);
@@ -453,10 +391,10 @@ export function parseArgs(args = process.argv.slice(2)) {
453
391
  }
454
392
  if (splitCommand === 'plan') dryRun = true;
455
393
  } else if (splitCommand === 'apply') {
456
- if (split || splitHunks) {
394
+ if (split) {
457
395
  throw fail(
458
396
  ERROR_CATEGORIES.CONFIG,
459
- 'split apply reads its scope from the plan; do not pass --scope or --split-hunks.',
397
+ 'split apply reads its scope from the plan; do not pass --scope.',
460
398
  );
461
399
  }
462
400
  if (cliProvider || cliModel || cliLang || cliReasoning || dryRun) {
@@ -466,16 +404,7 @@ export function parseArgs(args = process.argv.slice(2)) {
466
404
  );
467
405
  }
468
406
  } else {
469
- if (
470
- splitPlanFile ||
471
- split ||
472
- splitHunks ||
473
- cliProvider ||
474
- cliModel ||
475
- cliLang ||
476
- cliReasoning ||
477
- dryRun
478
- ) {
407
+ if (splitPlanFile || split || cliProvider || cliModel || cliLang || cliReasoning || dryRun) {
479
408
  throw fail(
480
409
  ERROR_CATEGORIES.CONFIG,
481
410
  `split ${splitCommand} accepts only an optional path, --yes, --output, and --debug.`,
@@ -494,14 +423,7 @@ export function parseArgs(args = process.argv.slice(2)) {
494
423
  }
495
424
  if (
496
425
  configAction &&
497
- (cliLang ||
498
- cliReasoning ||
499
- split ||
500
- splitHunks ||
501
- splitCommand ||
502
- splitPlanFile ||
503
- dryRun ||
504
- yes)
426
+ (cliLang || cliReasoning || split || splitCommand || splitPlanFile || dryRun || yes)
505
427
  ) {
506
428
  throw fail(
507
429
  ERROR_CATEGORIES.CONFIG,
@@ -513,15 +435,7 @@ export function parseArgs(args = process.argv.slice(2)) {
513
435
  }
514
436
  if (
515
437
  policyAction &&
516
- (cliProvider ||
517
- cliModel ||
518
- cliLang ||
519
- cliReasoning ||
520
- split ||
521
- splitHunks ||
522
- splitCommand ||
523
- dryRun ||
524
- yes)
438
+ (cliProvider || cliModel || cliLang || cliReasoning || split || splitCommand || dryRun || yes)
525
439
  ) {
526
440
  throw fail(
527
441
  ERROR_CATEGORIES.CONFIG,
@@ -534,30 +448,6 @@ export function parseArgs(args = process.argv.slice(2)) {
534
448
  if (policyMessageFile && policyRange) {
535
449
  throw fail(ERROR_CATEGORIES.CONFIG, 'policy check accepts either --file or --range, not both.');
536
450
  }
537
- if (
538
- presetAction &&
539
- (targetPath ||
540
- cliProvider ||
541
- cliModel ||
542
- cliLang ||
543
- cliReasoning ||
544
- split ||
545
- splitHunks ||
546
- splitCommand ||
547
- dryRun ||
548
- yes)
549
- ) {
550
- throw fail(
551
- ERROR_CATEGORIES.CONFIG,
552
- 'preset accepts only --file (validate/install), --output, and --debug.',
553
- );
554
- }
555
- if (presetAction === 'install' && !presetFile) {
556
- throw fail(ERROR_CATEGORIES.CONFIG, 'preset install requires --file=<manifest>.');
557
- }
558
- if (presetFile && !['validate', 'install'].includes(presetAction)) {
559
- throw fail(ERROR_CATEGORIES.CONFIG, '--file is only valid with preset validate or install.');
560
- }
561
451
  if (
562
452
  doctor &&
563
453
  (targetPath || cliLang || cliReasoning || split || splitPlanFile || dryRun || yes)
@@ -577,7 +467,6 @@ export function parseArgs(args = process.argv.slice(2)) {
577
467
  output,
578
468
  debug,
579
469
  split,
580
- splitHunks,
581
470
  splitCommand,
582
471
  splitPlanFile,
583
472
  dryRun,
@@ -588,10 +477,7 @@ export function parseArgs(args = process.argv.slice(2)) {
588
477
  policyAction,
589
478
  policyMessageFile,
590
479
  policyRange,
591
- presetAction,
592
- presetFile,
593
480
  completionShell: null,
594
- statsAction: null,
595
481
  help: false,
596
482
  version: false,
597
483
  };