Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Project Overview

`aic` is an AI-powered git commit message generator (Rust CLI, edition 2024). It produces hunk-level atomic Conventional Commits: nothing staged → the LLM plans unstaged hunks into logical Batches → one commit per Batch; something staged → one commit. Two LLM backends: 12 API providers via `rig` (openai, anthropic, gemini, deepseek, groq, xai, mistral, openrouter, perplexity, together, ollama, openai-compatible) or external CLI agents (claude/codex/pi/opencode) run headless. Subcommands: `resolve` (merge-conflict resolution), `setup`, `use`, `list`, `update` (zipsign-verified self-update), `completion`.
`aic` is an AI-powered git commit message generator (Rust CLI, edition 2024). It produces hunk-level atomic Conventional Commits: nothing staged → the LLM plans unstaged hunks into logical Batches → one commit per Batch; something staged → one commit. Two LLM backends: 12 API providers via `rig` (openai, anthropic, gemini, deepseek, groq, xai, mistral, openrouter, perplexity, together, ollama, openai-compatible) or external CLI agents (claude, codex, pi, opencode, omp, gemini, cursor, windsurf, copilot, trae, qwen) run headless. Subcommands: `resolve` (merge-conflict resolution), `setup`, `use`, `list`, `update` (zipsign-verified self-update), `completion`.

## Architecture & Data Flow

Expand Down
28 changes: 21 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ aic
# → 3 atomic commits from your working tree
```

> 💡 **No API key?** Skip the provider setup — use [Claude Code, Codex, pi, or opencode](#-no-api-key-use-your-ai-agent) instead. aic reuses their auth.
> 💡 **No API key?** Skip the provider setup — use [Claude Code, Codex, pi, opencode, and 7 more CLI agents](#-no-api-key-use-your-ai-agent) instead. aic reuses their auth.

> **Windows (PowerShell):** `irm https://github.com/CaicoLeung/aic/releases/latest/download/aic-installer.ps1 | iex`

Expand All @@ -63,6 +63,8 @@ aic

Already have [Claude Code](https://docs.anthropic.com/claude/docs/claude-code), [OpenAI Codex](https://github.com/openai/codex), [pi](https://pi.dev), or [opencode](https://opencode.ai) installed and authenticated? aic can drive it in **headless mode** — no API key needed.

Also supported: **oh-my-pi** (`omp`), **Gemini** (`gemini`), **Cursor** (`cursor-agent`), **Windsurf** (`devin` — Windsurf was renamed Devin Desktop), **GitHub Copilot** (`copilot`), **Trae** (`traecli`), and **Qwen Code** (`qwen`).

```sh
aic setup # → select "CLI agent" → pick your tool → done
```
Expand All @@ -75,20 +77,20 @@ command = "claude"
args = ["-p", "{prompt}", "--output-format", "stream-json", "--include-partial-messages"]
```

aic sends one prompt and reads the answer — it never runs the agent in tool-use mode. Each preset pins itself to read-only or text-only, so an injected instruction can't touch your working tree. Both backends' fields can coexist in the config; `backend_kind` selects the active one.
aic sends one prompt and reads the answer — it never runs the agent in tool-use mode. Presets pin explicit flags where needed (codex's read-only sandbox, pi's `--no-tools`); the rest rely on headless print mode, where approval-gated tools can't run without a TTY — so an injected instruction can't touch your working tree. Both backends' fields can coexist in the config; `backend_kind` selects the active one.

See [CLI-agent presets](#cli-agent-presets) for Codex, pi, and opencode.
See [CLI-agent presets](#cli-agent-presets) for the full preset list.

---

## Features

- **Hunk-level splitting** — one file, many concerns? Splits per-hunk into atomic commits, fully non-interactive
- **Two backends** — API provider (12+ supported) or CLI agent (Claude Code, Codex, pi, opencode — no API key)
- **Two backends** — API provider (12+ supported) or CLI agent (11 presets: claude, codex, pi, opencode, omp, gemini, cursor, windsurf, copilot, trae, qwen — no API key)
- **Merge conflict resolution** — `aic resolve` proposes per-file resolutions you review, then finalizes the merge
- **Live reasoning** — watch the model think as it decides the split
- **Conventional Commits** — messages follow the [v1.0.0 spec](https://www.conventionalcommits.org/)
- **Interactive setup** — `aic setup` is menu-driven; `aic use` switches between saved provider profiles and CLI agents (claude, codex, pi, opencode)
- **Interactive setup** — `aic setup` is menu-driven; `aic use` switches between saved provider profiles and CLI agents (claude, codex, pi, opencode, omp, gemini, cursor, windsurf, copilot, trae, qwen)

## Installation

Expand All @@ -107,7 +109,7 @@ Shell completions: `aic completion` (bash, fish, zsh, nushell).
| `aic` | Commit staged files. If nothing is staged, auto-split all unstaged changes into hunk-level atomic commits. |
| `aic resolve` | Resolve git merge conflicts via the LLM. Review each file, then finalize. |
| `aic setup` | Menu-driven config: API provider, CLI agent, or pre-commit confirmation. |
| `aic use <name>` | Switch to a provider already configured via `aic setup`, or to a CLI agent (claude, codex, pi, opencode). |
| `aic use <name>` | Switch to a provider already configured via `aic setup`, or to a CLI agent (claude, codex, pi, opencode, omp, gemini, cursor, windsurf, copilot, trae, qwen). |
| `aic list` | Show resolved config and where each value comes from. |
| `aic update` | Update to the latest release. |
| `aic completion` | Install shell completions. |
Expand Down Expand Up @@ -169,7 +171,7 @@ OpenRouter and the OpenAI-compatible provider have no default model — set `mod

### CLI-agent presets

Each preset ships a dedicated decoder for its CLI's stdout envelope, so aic can stream reasoning where the CLI exposes it and cleanly extract the answer.
Presets with a decodable stdout envelope (claude, codex, pi, opencode, omp) get a decoder — omp reuses pi's, being a pi fork — so aic streams reasoning where the CLI exposes it and cleanly extracts the answer; the rest use plain print mode — stdout IS the answer.

```toml
# OpenAI Codex — exec --json, read-only sandbox
Expand All @@ -192,6 +194,18 @@ command = "opencode"
args = ["run", "--format", "json", "{prompt}"]
```

Additional presets — all single-shot print mode (`-p`), answer on stdout:

| preset | command | notes |
|--------|---------|-------|
| `omp` | `omp --mode json {prompt}` | pi fork; pi-shaped NDJSON, reasoning feed |
| `gemini` | `gemini -p {prompt}` | shadows the `gemini` provider name in `aic use` — the Google API stays reachable via `aic use google` |
| `cursor` | `cursor-agent -p {prompt}` | no `--trust` → runs untrusted (writes disabled) |
| `windsurf` | `devin -p {prompt}` | Windsurf renamed to Devin Desktop; auth via `devin auth login` |
| `copilot` | `copilot -p {prompt}` | tool use needs interactive approval headless mode can't give |
| `trae` | `traecli -p {prompt}` | non-read tools gated behind a permission prompt |
| `qwen` | `qwen -p {prompt}` | Qwen Code, gemini-cli lineage |

The CLI must already be installed and logged in — aic does not install or authenticate it.

See [ADR 0010](docs/adr/0010-cli-agent-backend.md) for the backend design and [ADR 0011](docs/adr/0011-explicit-backend-discriminator.md) for the `backend_kind` discriminator.
Expand Down
28 changes: 21 additions & 7 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ aic
# → 从你的工作区生成 3 个原子提交
```

> 💡 **没有 API key?** 跳过 provider 配置 —— 直接使用 [Claude Code、Codex、pi 或 opencode](#-没有-api-key复用你的-ai-agent),aic 会复用它们的登录认证。
> 💡 **没有 API key?** 跳过 provider 配置 —— 直接使用 [Claude Code、Codex、pi、opencode 以及另外 7 个 CLI agent](#-没有-api-key复用你的-ai-agent),aic 会复用它们的登录认证。

> **Windows (PowerShell):** `irm https://github.com/CaicoLeung/aic/releases/latest/download/aic-installer.ps1 | iex`

Expand All @@ -63,6 +63,8 @@ aic

已经安装并登录了 [Claude Code](https://docs.anthropic.com/claude/docs/claude-code)、[OpenAI Codex](https://github.com/openai/codex)、[pi](https://pi.dev) 或 [opencode](https://opencode.ai)?aic 可以在 **headless 模式** 下驱动它 —— 无需 API key。

同样支持:**oh-my-pi**(`omp`)、**Gemini**(`gemini`)、**Cursor**(`cursor-agent`)、**Windsurf**(`devin` —— Windsurf 已更名为 Devin Desktop)、**GitHub Copilot**(`copilot`)、**Trae**(`traecli`)和 **Qwen Code**(`qwen`)。

```sh
aic setup # → 选择 "CLI agent" → 选你的工具 → 完成
```
Expand All @@ -75,20 +77,20 @@ command = "claude"
args = ["-p", "{prompt}", "--output-format", "stream-json", "--include-partial-messages"]
```

aic 只发送一条 prompt 并读取回答 —— 绝不在 tool-use 模式下运行 agent。每个预设都锁定为只读或纯文本,因此注入的指令无法触碰你的工作区。两个 backend 的字段可以共存于配置文件中;`backend_kind` 决定哪个生效。
aic 只发送一条 prompt 并读取回答 —— 绝不在 tool-use 模式下运行 agent。需要时预设会显式锁定权限(codex 的只读沙箱、pi 的 `--no-tools`);其余预设依赖无头 print 模式 —— 没有 TTY,需审批的工具无法运行 —— 因此注入的指令无法触碰你的工作区。两个 backend 的字段可以共存于配置文件中;`backend_kind` 决定哪个生效。

其他预设(Codex、pi、opencode)见 [CLI-agent 预设](#cli-agent-预设)。
其他预设见 [CLI-agent 预设](#cli-agent-预设)。

---

## 功能特性

- **Hunk 级别拆分** —— 一个文件、多种 concern?按 hunk 拆成多个原子提交,完全非交互
- **两种 backend** —— API provider(支持 12+ 家)或 CLI agent(Claude Code、Codex、pi、opencode —— 无需 API key)
- **两种 backend** —— API provider(支持 12+ 家)或 CLI agent(11 个预设:claude、codex、pi、opencode、omp、gemini、cursor、windsurf、copilot、trae、qwen —— 无需 API key)
- **Merge 冲突解决** —— `aic resolve` 逐文件给出方案供你审核,然后完成 merge
- **实时推理** —— 观看模型思考拆分方案的全过程
- **Conventional Commits** —— message 遵循 [v1.0.0 规范](https://www.conventionalcommits.org/)
- **交互式配置** —— `aic setup` 菜单驱动;`aic use` 在已保存的 provider 与 CLI agent(claude、codex、pi、opencode)之间切换
- **交互式配置** —— `aic setup` 菜单驱动;`aic use` 在已保存的 provider 与 CLI agent(claude、codex、pi、opencode、omp、gemini、cursor、windsurf、copilot、trae、qwen)之间切换

## 安装

Expand All @@ -107,7 +109,7 @@ Shell 补全:`aic completion`(bash、fish、zsh、nushell)。
| `aic` | 提交已 stage 的文件。若无 stage 内容,自动将所有未暂存改动拆分为 hunk 级别的原子提交。 |
| `aic resolve` | 通过 LLM 解决 git merge 冲突。逐文件审核后完成 merge。 |
| `aic setup` | 菜单驱动配置:API provider、CLI agent、或提交前确认。 |
| `aic use <name>` | 切换到已通过 `aic setup` 配置过的 provider,或切换到 CLI agent(claude、codex、pi、opencode)。 |
| `aic use <name>` | 切换到已通过 `aic setup` 配置过的 provider,或切换到 CLI agent(claude、codex、pi、opencode、omp、gemini、cursor、windsurf、copilot、trae、qwen)。 |
| `aic list` | 展示已 resolve 的 config 及每个值的来源。 |
| `aic update` | 更新到最新版本。 |
| `aic completion` | 安装 shell 补全。 |
Expand Down Expand Up @@ -169,7 +171,7 @@ OpenRouter 和 OpenAI-compatible provider 没有默认 model —— 在 config

### CLI-agent 预设

每个预配有专用的解码器来处理对应 CLI 的 stdout 格式,因此 aic 能在 CLI 支持的情况下流式输出推理过程,并干净地提取回答
带可解码 stdout 封装的预设(claude、codex、pi、opencode、omp)配有解码器 —— omp 作为 pi 的复刻直接复用 pi 的解码器 —— aic 能在 CLI 支持的情况下流式输出推理过程并干净地提取回答;其余预设为纯打印模式 —— stdout 即回答

```toml
# OpenAI Codex — exec --json,只读沙箱
Expand All @@ -192,6 +194,18 @@ command = "opencode"
args = ["run", "--format", "json", "{prompt}"]
```

其余预设 —— 均为单次打印模式(`-p`),回答输出到 stdout:

| 预设 | 命令 | 说明 |
|------|------|------|
| `omp` | `omp --mode json {prompt}` | pi 分支;pi 同构 NDJSON,带推理流 |
| `gemini` | `gemini -p {prompt}` | 在 `aic use` 中遮蔽 `gemini` provider 名 —— Google API 仍可通过 `aic use google` 使用 |
| `cursor` | `cursor-agent -p {prompt}` | 不带 `--trust` → 以未信任模式运行(禁用写入) |
| `windsurf` | `devin -p {prompt}` | Windsurf 已更名 Devin Desktop;`devin auth login` 登录 |
| `copilot` | `copilot -p {prompt}` | 工具调用需要 headless 无法给出的交互式审批 |
| `trae` | `traecli -p {prompt}` | 非只读工具被权限提示门控 |
| `qwen` | `qwen -p {prompt}` | Qwen Code,gemini-cli 血统 |

该 CLI 必须已安装并登录 —— aic 不负责安装或认证。

详见 [ADR 0010](docs/adr/0010-cli-agent-backend.md)(backend 设计)和 [ADR 0011](docs/adr/0011-explicit-backend-discriminator.md)(`backend_kind` 判别字段)。
Expand Down
46 changes: 30 additions & 16 deletions docs/adr/0010-cli-agent-backend.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,13 @@ placeholder), `timeout_secs`. All optional, so existing configs are unchanged
### Generic command template, not one adapter per CLI

The backend is a single `CliSpec { command, args, timeout_secs }` carrying an
argv template with a literal `{prompt}` placeholder. Three presets
(`cli_preset("claude" | "codex" | "pi")`) are offered as snippets by `aic setup`
and the docs; any other CLI (Opencode, Copilot, OhMyPi, future ones) is covered
by a custom `command`/`args` with **zero new code**. This deliberately rejects
the "one hardcoded adapter per CLI" alternative — six adapters doing the same
text-in/text-out job is a maintenance treadmill.
argv template with a literal `{prompt}` placeholder. Presets are data, not
adapters: the preset names live in one registry ([`PRESETS`], resolved by
`cli_preset`), and adding one is a single match arm plus an `Encoding` choice —
omp needed zero new decoder code because it reuses pi's. A CLI without a
preset is still covered by a custom `command`/`args` with **zero new code**.
This deliberately rejects the "one hardcoded adapter per CLI" alternative —
many adapters doing the same text-in/text-out job is a maintenance treadmill.

### Dispatch: an enum, not `Box<dyn>`

Expand All @@ -76,23 +77,36 @@ single prompt and reads stdout. No tool loop is ever allowed: an agent that can
run tools could act on prompt-injected instructions against the working tree.
Print mode removes that entire class of risk.

**Least-permission presets.** The promise above is enforced by the invocation
itself, not by trusting each CLI's default — every preset pins itself to a
text-only / read-only stance, because defaults differ and one (pi) is unsafe:
**Least-permission presets.** The promise above is enforced by the invocation,
not by tool-use mode: every preset runs single-shot print mode, where no TTY
exists to answer an approval prompt. On top of that shared guarantee, presets
pin explicit flags where the CLI's defaults need it — defaults differ and one
(pi) is unsafe even in print mode:

| Preset | Pinned flags | Why |
| --- | --- | --- |
| `claude` | `-p` + `--output-format stream-json --include-partial-messages` | `-p` is print mode (cannot prompt). The `stream-json` flags surface claude's `thinking_delta` as a live reasoning stream — without them, plain `-p` returns only the final answer and the batch-plan reasoning window stays empty. claude has no reliable `--no-tools` flag (`--allowedTools` is variadic and greedily consumes the prompt), so print mode's conservative default (it cannot prompt → no privileged tool auto-executes; `--dangerously-skip-permissions` stays opt-in) is the lever. The NDJSON envelope is decoded centrally, so the typed paths still receive plain JSON text. |
| `codex` | `exec -s read-only` | `exec` runs non-interactively; the sandbox is pinned to `read-only` so model-generated shell commands cannot write, even if a global config widens the default. `--dangerously-bypass-approvals-and-sandbox` is opt-in. |
| `pi` | `--no-tools -p` | **Required.** pi enables `read/bash/edit/write` tools by default; in print mode on a *trusted* project it can auto-run them (it cannot prompt) — effectively yolo. `--no-tools` disables all tools so print mode is genuinely text-only. |

Custom `command`/`args` backends are the user's responsibility to harden. The
`aic setup` wizard offers **only the four presets** (claude / codex / pi /
opencode) — each ships a dedicated decoder for its CLI's stdout envelope, so
aic can stream reasoning where the CLI exposes it and cleanly extract the
answer. A hand-edited custom `command`/`args` still runs, but in plain-text mode
with no reasoning feed and no envelope decoding; it is the config-edit escape
hatch for a CLI without a preset, not a wizard option.
Custom `command`/`args` backends are the user's responsibility to harden.

The remaining presets pin **no** flags — deliberately. They rely on headless
print mode itself: with no TTY, approval-gated tools cannot run (gemini,
copilot, trae, qwen); cursor runs untrusted (writes disabled) because
`--trust` is omitted; opencode and omp expose no equivalent of pi's
`--no-tools`, so like claude their text-only stance rests on print mode's
conservative default. This weakening of "pin flags everywhere" is accepted
here on purpose: per-CLI permission flags drift between CLI versions, while
the headless default is shared by all eleven and cannot regress per CLI.

The `aic setup` wizard offers **only the presets** ([`PRESETS`]). The five
with a decodable stdout envelope (claude, codex, pi, opencode — and omp, which
reuses pi's decoder) get reasoning streamed or extracted where the CLI exposes
it; the rest are plain print mode (stdout IS the answer). A hand-edited custom
`command`/`args` still runs, but in plain-text mode with no reasoning feed and
no envelope decoding; it is the config-edit escape hatch for a CLI without a
preset, not a wizard option.

### Preset auto-migration

Expand Down
Loading
Loading