aicp-cli 0.7.0__tar.gz → 0.8.0__tar.gz
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.
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/CHANGELOG.md +14 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/PKG-INFO +100 -45
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/README.md +99 -44
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/pyproject.toml +1 -1
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/src/aicp/cli.py +10 -2
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/src/aicp/menu.py +9 -5
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/uv.lock +2 -2
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/.github/workflows/ci.yml +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/.github/workflows/release.yml +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/.gitignore +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/CLAUDE.md +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/LICENSE +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/TODO.md +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/config.example.json +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/docs/images/aicp-run.png +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/skills/commit.md +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/skills/safe-git-push/SKILL.md +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/skills/safe-git-push/scripts/safe_push.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/src/aicp/__init__.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/src/aicp/_keyreader.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/src/aicp/_utils.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/src/aicp/agentcfg.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/src/aicp/agents.json +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/src/aicp/agents.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/src/aicp/budget.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/src/aicp/config.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/src/aicp/contracts.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/src/aicp/gitflow.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/src/aicp/i18n.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/src/aicp/notify.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/src/aicp/present.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/src/aicp/quota.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/src/aicp/runner.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/src/aicp/secrets.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/src/aicp/skills.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/src/aicp/timing.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/tests/conftest.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/tests/test_agentcfg.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/tests/test_agents.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/tests/test_budget.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/tests/test_cli.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/tests/test_config.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/tests/test_cross_platform.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/tests/test_gitflow.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/tests/test_i18n.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/tests/test_menu.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/tests/test_menu_skills.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/tests/test_notify.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/tests/test_present.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/tests/test_quota.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/tests/test_runner.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/tests/test_secrets.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/tests/test_skills.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/tests/test_timing.py +0 -0
- {aicp_cli-0.7.0 → aicp_cli-0.8.0}/tests/test_undo.py +0 -0
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
## [0.8.0] - 2026-09-18
|
|
2
|
+
|
|
3
|
+
### 🐛 Bug Fixes
|
|
4
|
+
|
|
5
|
+
- **agents:** Keep agent listing widths stable across toggles
|
|
6
|
+
- **cli:** Parenthesize concatenated f-string for ruff ISC004
|
|
7
|
+
|
|
8
|
+
### 📚 Documentation
|
|
9
|
+
|
|
10
|
+
- **readme:** Document configurable agent chain and skills field
|
|
1
11
|
## [0.7.0] - 2026-09-17
|
|
2
12
|
|
|
3
13
|
### 🚀 Features
|
|
@@ -7,6 +17,10 @@
|
|
|
7
17
|
### 🐛 Bug Fixes
|
|
8
18
|
|
|
9
19
|
- **menu:** Size panels to their own content
|
|
20
|
+
|
|
21
|
+
### 📚 Documentation
|
|
22
|
+
|
|
23
|
+
- **changelog:** Release v0.7.0
|
|
10
24
|
## [0.6.0] - 2026-09-17
|
|
11
25
|
|
|
12
26
|
### 📚 Documentation
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: aicp-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: AI commit + push, with a git-verified result summary
|
|
5
5
|
Project-URL: Repository, https://github.com/weskao/aicp
|
|
6
6
|
Project-URL: Issues, https://github.com/weskao/aicp/issues
|
|
@@ -14,11 +14,19 @@ Description-Content-Type: text/markdown
|
|
|
14
14
|
|
|
15
15
|
AI commit + push, with a git-verified result summary.
|
|
16
16
|
|
|
17
|
-
`aicp` runs an AI coding CLI to write your commit message(s) and push,
|
|
18
|
-
a fallback chain
|
|
19
|
-
`vibe` → `grok` — until one exits 0; anything not installed is skipped. It
|
|
17
|
+
`aicp` runs an AI coding CLI to write your commit message(s) and push, walking
|
|
18
|
+
a fallback chain until one exits 0; anything not installed is skipped. It
|
|
20
19
|
sends that CLI two literal prompts, `/commit` then `/safe-git-push`.
|
|
21
20
|
|
|
21
|
+
**The chain is yours to arrange, and it isn't a fixed set.** The built-in
|
|
22
|
+
registry ships six CLIs — `copilot`, `agy`, `codex`, `claude`, `vibe`,
|
|
23
|
+
`grok` — but that's only the default *order*, not a hardcoded pipeline. Move
|
|
24
|
+
any one of them to the front with `aicp --swap-ai`, set the whole order from
|
|
25
|
+
`aicp --config` or `AICP_CLI_ORDER`, and add your own CLIs (or drop built-in
|
|
26
|
+
ones) with `aicp --agents`. Every agent in the resolved registry is a
|
|
27
|
+
first-class link in the chain — there is no privileged first CLI and no cap
|
|
28
|
+
on how many you run with.
|
|
29
|
+
|
|
22
30
|
The part that matters: **aicp never trusts the CLI's own account of what
|
|
23
31
|
happened.** An AI CLI can print "pushed!" and exit 0 while `/safe-git-push`
|
|
24
32
|
quietly aborted inside it. So every number in the result summary — new
|
|
@@ -28,17 +36,36 @@ failed `git fetch` is never read as "already in sync" either: a stale
|
|
|
28
36
|
remote-tracking ref resolves just fine and would otherwise report a clean
|
|
29
37
|
push that never reached the remote.
|
|
30
38
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
39
|
+
## Contents
|
|
40
|
+
|
|
41
|
+
**Getting started**
|
|
42
|
+
|
|
43
|
+
- [Requirements](#requirements)
|
|
44
|
+
- [Install](#install)
|
|
45
|
+
- [Set up skills](#set-up-skills)
|
|
46
|
+
- [Run it](#run-it)
|
|
47
|
+
|
|
48
|
+
**Using it**
|
|
49
|
+
|
|
50
|
+
- [Fallback order](#fallback-order) — reorder the chain, `--swap-ai`
|
|
51
|
+
- [Fallback results and quota limits](#fallback-results-and-quota-limits)
|
|
52
|
+
- [`--undo`](#--undo) — take back the last commit
|
|
53
|
+
- [Automation / CI](#automation--ci) — the non-interactive flags
|
|
54
|
+
|
|
55
|
+
**Reference**
|
|
56
|
+
|
|
57
|
+
- [Configuration](#configuration) — every setting and env var
|
|
58
|
+
- [Custom agents](#custom-agents) — add your own AI CLI
|
|
59
|
+
- [Safety](#safety) — the pre-commit secret scan
|
|
60
|
+
- [Platform support](#platform-support) — macOS, Linux, Windows
|
|
35
61
|
|
|
36
62
|
## Requirements
|
|
37
63
|
|
|
38
64
|
- Python 3.10 or newer
|
|
39
65
|
- [`uv`](https://docs.astral.sh/uv/) — the install path below
|
|
40
66
|
- `git`
|
|
41
|
-
- At least one
|
|
67
|
+
- At least one AI CLI from the chain on `PATH` (a built-in one, or your own
|
|
68
|
+
via [Custom agents](#custom-agents))
|
|
42
69
|
|
|
43
70
|
The Python package itself has no runtime dependencies.
|
|
44
71
|
|
|
@@ -79,44 +106,26 @@ Targets follow each CLI's own config directory, not its binary name — `agy`
|
|
|
79
106
|
| CLI | Config dir | `/commit` installed? | `/safe-git-push` installed? |
|
|
80
107
|
| --- | --- | --- | --- |
|
|
81
108
|
| `claude` | `~/.claude` | No — keeps your existing `commands/commit.md` | Yes |
|
|
82
|
-
| `codex` | `~/.codex` |
|
|
83
|
-
| `copilot` | `~/.copilot` |
|
|
84
|
-
| `agy` (Gemini CLI) | `~/.gemini` |
|
|
85
|
-
| `vibe` | `~/.vibe` |
|
|
86
|
-
| `grok` | `$GROK_HOME` when set, otherwise `~/.grok` |
|
|
87
|
-
|
|
88
|
-
`/commit` is
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
109
|
+
| `codex` | `~/.codex` | Yes | Yes |
|
|
110
|
+
| `copilot` | `~/.copilot` | Yes | Yes |
|
|
111
|
+
| `agy` (Gemini CLI) | `~/.gemini` | Yes | Yes |
|
|
112
|
+
| `vibe` | `~/.vibe` | Yes | Yes |
|
|
113
|
+
| `grok` | `$GROK_HOME` when set, otherwise `~/.grok` | Yes | Yes |
|
|
114
|
+
|
|
115
|
+
Every configured CLI gets `/safe-git-push`. `/commit` is installed everywhere
|
|
116
|
+
*except* `claude`, which already resolves `/commit` from
|
|
117
|
+
`commands/commit.md` — a second definition under `skills/` would compete with
|
|
118
|
+
it. Which skills an agent gets is data, not a branch: it's the `skills` field
|
|
119
|
+
of that agent's registry entry, so a [custom agent](#custom-agents) declares
|
|
120
|
+
its own list the same way.
|
|
121
|
+
|
|
122
|
+
The vendored text is rewritten per CLI on install — the shipped copy talks
|
|
123
|
+
about `.claude/` and `CLAUDE.md`, which mean nothing to `codex` or `agy`, so
|
|
124
|
+
each install substitutes that agent's own `config_dir` name and `memory_file`.
|
|
92
125
|
|
|
93
126
|
A CLI whose config directory doesn't exist at all is skipped, never created —
|
|
94
127
|
aicp only ever installs into a CLI you've actually set up.
|
|
95
128
|
|
|
96
|
-
### Fallback results and quota limits
|
|
97
|
-
|
|
98
|
-
The opening run panel prints the resolved chain, and the commit panel and
|
|
99
|
-
final RESULT table name the CLI that handled each step (`—` when skipped).
|
|
100
|
-
When a CLI emits a supported, exact quota/rate-limit signal, aicp records the
|
|
101
|
-
outcome as `quota`, notifies through the usual notification path, and excludes
|
|
102
|
-
that CLI from the rest of that one commit/push flow. The exclusion also
|
|
103
|
-
persists: the CLI stays skipped for `AICP_QUOTA_COOLDOWN` seconds (default one
|
|
104
|
-
hour, state in `~/.aicp/quota.json`), because a token or rate-limit wall
|
|
105
|
-
normally stands for hours and every run inside that window would otherwise burn
|
|
106
|
-
a full budget per step on a CLI that cannot succeed. A skipped CLI prints how
|
|
107
|
-
long is left; `AICP_QUOTA_COOLDOWN=0` switches the cooldown off entirely, and
|
|
108
|
-
deleting the file clears it.
|
|
109
|
-
|
|
110
|
-
Only the `quota` outcome starts a cooldown. A timeout does not — a CLI that
|
|
111
|
-
hangs on its rate limit instead of exiting is indistinguishable from one merely
|
|
112
|
-
running long, and sidelining it for an hour on that guess costs more than the
|
|
113
|
-
retry does.
|
|
114
|
-
|
|
115
|
-
Exact detection is intentionally narrow. It is supported for Codex, Claude,
|
|
116
|
-
Vibe, and Grok only; Copilot and `agy` have no verified quota signature, so a
|
|
117
|
-
nonzero exit from either remains an ordinary failure and is still eligible for
|
|
118
|
-
the next step.
|
|
119
|
-
|
|
120
129
|
## Run it
|
|
121
130
|
|
|
122
131
|
The whole command surface is two things to remember:
|
|
@@ -141,6 +150,52 @@ same as `aicp --config`.
|
|
|
141
150
|
right now": which steps run, message language, fallback CLI order, the
|
|
142
151
|
skills install/upgrade view, and a health check.
|
|
143
152
|
|
|
153
|
+
### Fallback order
|
|
154
|
+
|
|
155
|
+
The order is a saved preference, not a property of the tool. Three ways to
|
|
156
|
+
set it, all writing the same `aicp_cli_order` key:
|
|
157
|
+
|
|
158
|
+
```sh
|
|
159
|
+
aicp --swap-ai # pick a CLI, it trades places with whoever holds #1
|
|
160
|
+
aicp --config # the AI CLI order row, arrow keys
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
```sh
|
|
164
|
+
# ~/.aicp/config.json — or AICP_CLI_ORDER="claude codex" aicp for one run
|
|
165
|
+
{ "aicp_cli_order": "claude codex copilot agy vibe grok" }
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
`--swap-ai` is a rotation, not a destructive set: the CLI you pick moves to
|
|
169
|
+
`#1` and the old `#1` takes its place, so nothing falls out of the chain. It
|
|
170
|
+
lists every agent in the registry, including ones whose binary isn't on
|
|
171
|
+
`PATH` — a missing binary must not hide a choice you're about to install.
|
|
172
|
+
Naming only the first few is enough; everything you leave out is appended
|
|
173
|
+
behind them in registry order.
|
|
174
|
+
|
|
175
|
+
### Fallback results and quota limits
|
|
176
|
+
|
|
177
|
+
The opening run panel prints the resolved chain, and the commit panel and
|
|
178
|
+
final RESULT table name the CLI that handled each step (`—` when skipped).
|
|
179
|
+
When a CLI emits a supported, exact quota/rate-limit signal, aicp records the
|
|
180
|
+
outcome as `quota`, notifies through the usual notification path, and excludes
|
|
181
|
+
that CLI from the rest of that one commit/push flow. The exclusion also
|
|
182
|
+
persists: the CLI stays skipped for `AICP_QUOTA_COOLDOWN` seconds (default one
|
|
183
|
+
hour, state in `~/.aicp/quota.json`), because a token or rate-limit wall
|
|
184
|
+
normally stands for hours and every run inside that window would otherwise burn
|
|
185
|
+
a full budget per step on a CLI that cannot succeed. A skipped CLI prints how
|
|
186
|
+
long is left; `AICP_QUOTA_COOLDOWN=0` switches the cooldown off entirely, and
|
|
187
|
+
deleting the file clears it.
|
|
188
|
+
|
|
189
|
+
Only the `quota` outcome starts a cooldown. A timeout does not — a CLI that
|
|
190
|
+
hangs on its rate limit instead of exiting is indistinguishable from one merely
|
|
191
|
+
running long, and sidelining it for an hour on that guess costs more than the
|
|
192
|
+
retry does.
|
|
193
|
+
|
|
194
|
+
Exact detection is intentionally narrow. It is supported for Codex, Claude,
|
|
195
|
+
Vibe, and Grok only; Copilot, `agy` and any agent you add yourself have no
|
|
196
|
+
verified quota signature, so a nonzero exit from those remains an ordinary
|
|
197
|
+
failure and is still eligible for the next step.
|
|
198
|
+
|
|
144
199
|
### `--undo`
|
|
145
200
|
|
|
146
201
|
```sh
|
|
@@ -223,7 +278,7 @@ integration tests and installed-wheel checks on all three operating systems.
|
|
|
223
278
|
| `config_dir` | Absolute path or `~/`-relative directory; home is resolved when used. |
|
|
224
279
|
| `config_dir_env` | Optional environment variable overriding that directory (currently `GROK_HOME`). |
|
|
225
280
|
| `skills_dir` | Relative directory inside `config_dir` where skills are installed. |
|
|
226
|
-
| `skills` |
|
|
281
|
+
| `skills` | Which vendored skills to install for this agent — any of `safe-git-push`, `commit`. Omitting `commit` is how `claude` keeps its own `commands/commit.md`. |
|
|
227
282
|
| `memory_file` | Instruction filename used when adapting vendored skill text. Project directory references use the basename of `config_dir`. |
|
|
228
283
|
| `args` | Argument array containing exactly one standalone `{prompt}`, replaced with the complete prompt as one argument. No shell evaluation. |
|
|
229
284
|
|
|
@@ -351,7 +406,7 @@ See [`config.example.json`](config.example.json) for a ready-to-copy template.
|
|
|
351
406
|
| `AICP_DO_COMMIT` | `1` | Run the `/commit` step. `0` = only push what's already committed. |
|
|
352
407
|
| `AICP_DO_PUSH` | `1` | Run the `/safe-git-push` step. `0` = commit and stop. |
|
|
353
408
|
| `AICP_LANG` | `en` | Message language: `en` or `zh-TW`, everywhere including notifications. |
|
|
354
|
-
| `AICP_CLI_ORDER` | `copilot agy codex claude vibe grok` | Fallback order. A prefix is enough — any
|
|
409
|
+
| `AICP_CLI_ORDER` | registry order (`copilot agy codex claude vibe grok` out of the box) | Fallback order, space-separated. A prefix is enough — any agent left out is appended after it, in registry order, so adding an agent never invalidates an order you already saved. An unknown or repeated name is refused outright and the default order is used. |
|
|
355
410
|
| `AICP_TZ` | `Asia/Taipei` | IANA zone name used to render commit timestamps. Anything else falls back to the default. |
|
|
356
411
|
| `AICP_TZ_LABEL` | `UTC+8` | Cosmetic label shown beside those timestamps; not validated. |
|
|
357
412
|
| `AICP_STEP_TIMEOUT` | *(unset)* | Pins every CLI's per-step budget in seconds, skipping the formula and history below entirely. |
|
|
@@ -2,11 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
AI commit + push, with a git-verified result summary.
|
|
4
4
|
|
|
5
|
-
`aicp` runs an AI coding CLI to write your commit message(s) and push,
|
|
6
|
-
a fallback chain
|
|
7
|
-
`vibe` → `grok` — until one exits 0; anything not installed is skipped. It
|
|
5
|
+
`aicp` runs an AI coding CLI to write your commit message(s) and push, walking
|
|
6
|
+
a fallback chain until one exits 0; anything not installed is skipped. It
|
|
8
7
|
sends that CLI two literal prompts, `/commit` then `/safe-git-push`.
|
|
9
8
|
|
|
9
|
+
**The chain is yours to arrange, and it isn't a fixed set.** The built-in
|
|
10
|
+
registry ships six CLIs — `copilot`, `agy`, `codex`, `claude`, `vibe`,
|
|
11
|
+
`grok` — but that's only the default *order*, not a hardcoded pipeline. Move
|
|
12
|
+
any one of them to the front with `aicp --swap-ai`, set the whole order from
|
|
13
|
+
`aicp --config` or `AICP_CLI_ORDER`, and add your own CLIs (or drop built-in
|
|
14
|
+
ones) with `aicp --agents`. Every agent in the resolved registry is a
|
|
15
|
+
first-class link in the chain — there is no privileged first CLI and no cap
|
|
16
|
+
on how many you run with.
|
|
17
|
+
|
|
10
18
|
The part that matters: **aicp never trusts the CLI's own account of what
|
|
11
19
|
happened.** An AI CLI can print "pushed!" and exit 0 while `/safe-git-push`
|
|
12
20
|
quietly aborted inside it. So every number in the result summary — new
|
|
@@ -16,17 +24,36 @@ failed `git fetch` is never read as "already in sync" either: a stale
|
|
|
16
24
|
remote-tracking ref resolves just fine and would otherwise report a clean
|
|
17
25
|
push that never reached the remote.
|
|
18
26
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
27
|
+
## Contents
|
|
28
|
+
|
|
29
|
+
**Getting started**
|
|
30
|
+
|
|
31
|
+
- [Requirements](#requirements)
|
|
32
|
+
- [Install](#install)
|
|
33
|
+
- [Set up skills](#set-up-skills)
|
|
34
|
+
- [Run it](#run-it)
|
|
35
|
+
|
|
36
|
+
**Using it**
|
|
37
|
+
|
|
38
|
+
- [Fallback order](#fallback-order) — reorder the chain, `--swap-ai`
|
|
39
|
+
- [Fallback results and quota limits](#fallback-results-and-quota-limits)
|
|
40
|
+
- [`--undo`](#--undo) — take back the last commit
|
|
41
|
+
- [Automation / CI](#automation--ci) — the non-interactive flags
|
|
42
|
+
|
|
43
|
+
**Reference**
|
|
44
|
+
|
|
45
|
+
- [Configuration](#configuration) — every setting and env var
|
|
46
|
+
- [Custom agents](#custom-agents) — add your own AI CLI
|
|
47
|
+
- [Safety](#safety) — the pre-commit secret scan
|
|
48
|
+
- [Platform support](#platform-support) — macOS, Linux, Windows
|
|
23
49
|
|
|
24
50
|
## Requirements
|
|
25
51
|
|
|
26
52
|
- Python 3.10 or newer
|
|
27
53
|
- [`uv`](https://docs.astral.sh/uv/) — the install path below
|
|
28
54
|
- `git`
|
|
29
|
-
- At least one
|
|
55
|
+
- At least one AI CLI from the chain on `PATH` (a built-in one, or your own
|
|
56
|
+
via [Custom agents](#custom-agents))
|
|
30
57
|
|
|
31
58
|
The Python package itself has no runtime dependencies.
|
|
32
59
|
|
|
@@ -67,44 +94,26 @@ Targets follow each CLI's own config directory, not its binary name — `agy`
|
|
|
67
94
|
| CLI | Config dir | `/commit` installed? | `/safe-git-push` installed? |
|
|
68
95
|
| --- | --- | --- | --- |
|
|
69
96
|
| `claude` | `~/.claude` | No — keeps your existing `commands/commit.md` | Yes |
|
|
70
|
-
| `codex` | `~/.codex` |
|
|
71
|
-
| `copilot` | `~/.copilot` |
|
|
72
|
-
| `agy` (Gemini CLI) | `~/.gemini` |
|
|
73
|
-
| `vibe` | `~/.vibe` |
|
|
74
|
-
| `grok` | `$GROK_HOME` when set, otherwise `~/.grok` |
|
|
75
|
-
|
|
76
|
-
`/commit` is
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
97
|
+
| `codex` | `~/.codex` | Yes | Yes |
|
|
98
|
+
| `copilot` | `~/.copilot` | Yes | Yes |
|
|
99
|
+
| `agy` (Gemini CLI) | `~/.gemini` | Yes | Yes |
|
|
100
|
+
| `vibe` | `~/.vibe` | Yes | Yes |
|
|
101
|
+
| `grok` | `$GROK_HOME` when set, otherwise `~/.grok` | Yes | Yes |
|
|
102
|
+
|
|
103
|
+
Every configured CLI gets `/safe-git-push`. `/commit` is installed everywhere
|
|
104
|
+
*except* `claude`, which already resolves `/commit` from
|
|
105
|
+
`commands/commit.md` — a second definition under `skills/` would compete with
|
|
106
|
+
it. Which skills an agent gets is data, not a branch: it's the `skills` field
|
|
107
|
+
of that agent's registry entry, so a [custom agent](#custom-agents) declares
|
|
108
|
+
its own list the same way.
|
|
109
|
+
|
|
110
|
+
The vendored text is rewritten per CLI on install — the shipped copy talks
|
|
111
|
+
about `.claude/` and `CLAUDE.md`, which mean nothing to `codex` or `agy`, so
|
|
112
|
+
each install substitutes that agent's own `config_dir` name and `memory_file`.
|
|
80
113
|
|
|
81
114
|
A CLI whose config directory doesn't exist at all is skipped, never created —
|
|
82
115
|
aicp only ever installs into a CLI you've actually set up.
|
|
83
116
|
|
|
84
|
-
### Fallback results and quota limits
|
|
85
|
-
|
|
86
|
-
The opening run panel prints the resolved chain, and the commit panel and
|
|
87
|
-
final RESULT table name the CLI that handled each step (`—` when skipped).
|
|
88
|
-
When a CLI emits a supported, exact quota/rate-limit signal, aicp records the
|
|
89
|
-
outcome as `quota`, notifies through the usual notification path, and excludes
|
|
90
|
-
that CLI from the rest of that one commit/push flow. The exclusion also
|
|
91
|
-
persists: the CLI stays skipped for `AICP_QUOTA_COOLDOWN` seconds (default one
|
|
92
|
-
hour, state in `~/.aicp/quota.json`), because a token or rate-limit wall
|
|
93
|
-
normally stands for hours and every run inside that window would otherwise burn
|
|
94
|
-
a full budget per step on a CLI that cannot succeed. A skipped CLI prints how
|
|
95
|
-
long is left; `AICP_QUOTA_COOLDOWN=0` switches the cooldown off entirely, and
|
|
96
|
-
deleting the file clears it.
|
|
97
|
-
|
|
98
|
-
Only the `quota` outcome starts a cooldown. A timeout does not — a CLI that
|
|
99
|
-
hangs on its rate limit instead of exiting is indistinguishable from one merely
|
|
100
|
-
running long, and sidelining it for an hour on that guess costs more than the
|
|
101
|
-
retry does.
|
|
102
|
-
|
|
103
|
-
Exact detection is intentionally narrow. It is supported for Codex, Claude,
|
|
104
|
-
Vibe, and Grok only; Copilot and `agy` have no verified quota signature, so a
|
|
105
|
-
nonzero exit from either remains an ordinary failure and is still eligible for
|
|
106
|
-
the next step.
|
|
107
|
-
|
|
108
117
|
## Run it
|
|
109
118
|
|
|
110
119
|
The whole command surface is two things to remember:
|
|
@@ -129,6 +138,52 @@ same as `aicp --config`.
|
|
|
129
138
|
right now": which steps run, message language, fallback CLI order, the
|
|
130
139
|
skills install/upgrade view, and a health check.
|
|
131
140
|
|
|
141
|
+
### Fallback order
|
|
142
|
+
|
|
143
|
+
The order is a saved preference, not a property of the tool. Three ways to
|
|
144
|
+
set it, all writing the same `aicp_cli_order` key:
|
|
145
|
+
|
|
146
|
+
```sh
|
|
147
|
+
aicp --swap-ai # pick a CLI, it trades places with whoever holds #1
|
|
148
|
+
aicp --config # the AI CLI order row, arrow keys
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
```sh
|
|
152
|
+
# ~/.aicp/config.json — or AICP_CLI_ORDER="claude codex" aicp for one run
|
|
153
|
+
{ "aicp_cli_order": "claude codex copilot agy vibe grok" }
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
`--swap-ai` is a rotation, not a destructive set: the CLI you pick moves to
|
|
157
|
+
`#1` and the old `#1` takes its place, so nothing falls out of the chain. It
|
|
158
|
+
lists every agent in the registry, including ones whose binary isn't on
|
|
159
|
+
`PATH` — a missing binary must not hide a choice you're about to install.
|
|
160
|
+
Naming only the first few is enough; everything you leave out is appended
|
|
161
|
+
behind them in registry order.
|
|
162
|
+
|
|
163
|
+
### Fallback results and quota limits
|
|
164
|
+
|
|
165
|
+
The opening run panel prints the resolved chain, and the commit panel and
|
|
166
|
+
final RESULT table name the CLI that handled each step (`—` when skipped).
|
|
167
|
+
When a CLI emits a supported, exact quota/rate-limit signal, aicp records the
|
|
168
|
+
outcome as `quota`, notifies through the usual notification path, and excludes
|
|
169
|
+
that CLI from the rest of that one commit/push flow. The exclusion also
|
|
170
|
+
persists: the CLI stays skipped for `AICP_QUOTA_COOLDOWN` seconds (default one
|
|
171
|
+
hour, state in `~/.aicp/quota.json`), because a token or rate-limit wall
|
|
172
|
+
normally stands for hours and every run inside that window would otherwise burn
|
|
173
|
+
a full budget per step on a CLI that cannot succeed. A skipped CLI prints how
|
|
174
|
+
long is left; `AICP_QUOTA_COOLDOWN=0` switches the cooldown off entirely, and
|
|
175
|
+
deleting the file clears it.
|
|
176
|
+
|
|
177
|
+
Only the `quota` outcome starts a cooldown. A timeout does not — a CLI that
|
|
178
|
+
hangs on its rate limit instead of exiting is indistinguishable from one merely
|
|
179
|
+
running long, and sidelining it for an hour on that guess costs more than the
|
|
180
|
+
retry does.
|
|
181
|
+
|
|
182
|
+
Exact detection is intentionally narrow. It is supported for Codex, Claude,
|
|
183
|
+
Vibe, and Grok only; Copilot, `agy` and any agent you add yourself have no
|
|
184
|
+
verified quota signature, so a nonzero exit from those remains an ordinary
|
|
185
|
+
failure and is still eligible for the next step.
|
|
186
|
+
|
|
132
187
|
### `--undo`
|
|
133
188
|
|
|
134
189
|
```sh
|
|
@@ -211,7 +266,7 @@ integration tests and installed-wheel checks on all three operating systems.
|
|
|
211
266
|
| `config_dir` | Absolute path or `~/`-relative directory; home is resolved when used. |
|
|
212
267
|
| `config_dir_env` | Optional environment variable overriding that directory (currently `GROK_HOME`). |
|
|
213
268
|
| `skills_dir` | Relative directory inside `config_dir` where skills are installed. |
|
|
214
|
-
| `skills` |
|
|
269
|
+
| `skills` | Which vendored skills to install for this agent — any of `safe-git-push`, `commit`. Omitting `commit` is how `claude` keeps its own `commands/commit.md`. |
|
|
215
270
|
| `memory_file` | Instruction filename used when adapting vendored skill text. Project directory references use the basename of `config_dir`. |
|
|
216
271
|
| `args` | Argument array containing exactly one standalone `{prompt}`, replaced with the complete prompt as one argument. No shell evaluation. |
|
|
217
272
|
|
|
@@ -339,7 +394,7 @@ See [`config.example.json`](config.example.json) for a ready-to-copy template.
|
|
|
339
394
|
| `AICP_DO_COMMIT` | `1` | Run the `/commit` step. `0` = only push what's already committed. |
|
|
340
395
|
| `AICP_DO_PUSH` | `1` | Run the `/safe-git-push` step. `0` = commit and stop. |
|
|
341
396
|
| `AICP_LANG` | `en` | Message language: `en` or `zh-TW`, everywhere including notifications. |
|
|
342
|
-
| `AICP_CLI_ORDER` | `copilot agy codex claude vibe grok` | Fallback order. A prefix is enough — any
|
|
397
|
+
| `AICP_CLI_ORDER` | registry order (`copilot agy codex claude vibe grok` out of the box) | Fallback order, space-separated. A prefix is enough — any agent left out is appended after it, in registry order, so adding an agent never invalidates an order you already saved. An unknown or repeated name is refused outright and the default order is used. |
|
|
343
398
|
| `AICP_TZ` | `Asia/Taipei` | IANA zone name used to render commit timestamps. Anything else falls back to the default. |
|
|
344
399
|
| `AICP_TZ_LABEL` | `UTC+8` | Cosmetic label shown beside those timestamps; not validated. |
|
|
345
400
|
| `AICP_STEP_TIMEOUT` | *(unset)* | Pins every CLI's per-step budget in seconds, skipping the formula and history below entirely. |
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# PyPI distribution name only — the `aicp` name was taken in 2021 by an
|
|
3
3
|
# unrelated project. The command, import path and repo stay `aicp`.
|
|
4
4
|
name = "aicp-cli"
|
|
5
|
-
version = "0.
|
|
5
|
+
version = "0.8.0"
|
|
6
6
|
description = "AI commit + push, with a git-verified result summary"
|
|
7
7
|
readme = "README.md"
|
|
8
8
|
requires-python = ">=3.10"
|
|
@@ -305,9 +305,17 @@ def _doctor(*, as_json: bool) -> int:
|
|
|
305
305
|
|
|
306
306
|
|
|
307
307
|
def _agents_list() -> int:
|
|
308
|
+
inventory = agents.inventory()
|
|
309
|
+
exec_w = max((present.width(row.executable) for row in inventory), default=0)
|
|
308
310
|
rows = [
|
|
309
|
-
(
|
|
310
|
-
|
|
311
|
+
(
|
|
312
|
+
row.name,
|
|
313
|
+
(
|
|
314
|
+
f"{row.executable}{' ' * (exec_w - present.width(row.executable))}"
|
|
315
|
+
f" {DIM}· {t(*agentcfg.STATE_LABELS[row.state])}{RESET}"
|
|
316
|
+
),
|
|
317
|
+
)
|
|
318
|
+
for row in inventory
|
|
311
319
|
]
|
|
312
320
|
_echo(present.render(rows, t("agents_title", "AGENTS")))
|
|
313
321
|
_dim(t("agents_hint", " aicp --agents set <name> executable=… · disable <name> · reset <name>"))
|
|
@@ -442,13 +442,16 @@ def _agents_lines(state: MenuState, selected: int, message: str | None = None) -
|
|
|
442
442
|
than read off a printed list afterwards. Sized to its own content (a
|
|
443
443
|
short roster, not the terminal width) and stable across keypresses: the
|
|
444
444
|
row text never changes length as the cursor moves, only which one is
|
|
445
|
-
bold. The executable column is padded to its own widest entry
|
|
446
|
-
state label
|
|
447
|
-
|
|
448
|
-
|
|
445
|
+
bold. The executable column is padded to its own widest entry, and the
|
|
446
|
+
state label to the widest of all *possible* states (not just the ones
|
|
447
|
+
currently in play) — so toggling a row's state can never widen or narrow
|
|
448
|
+
the panel that toggle is being watched in. The fixed-width-column trick
|
|
449
|
+
:func:`render_table` gets for free and a two-column ``(label, value)``
|
|
450
|
+
panel does not.
|
|
449
451
|
"""
|
|
450
452
|
rows = _agent_rows(state)
|
|
451
453
|
exec_w = max((width(row.executable) for row in rows), default=0)
|
|
454
|
+
state_w = max(width(_t(state.lang, *msg)) for msg in agentcfg.STATE_LABELS.values())
|
|
452
455
|
body: list[tuple[str, str]] = []
|
|
453
456
|
for i, row in enumerate(rows, start=1):
|
|
454
457
|
marker = "›" if selected == i else " "
|
|
@@ -458,7 +461,8 @@ def _agents_lines(state: MenuState, selected: int, message: str | None = None) -
|
|
|
458
461
|
label = f"{RESET}{BOLD}{label}{RESET}"
|
|
459
462
|
state_label = _t(state.lang, *agentcfg.STATE_LABELS[row.state])
|
|
460
463
|
pad = " " * (exec_w - width(row.executable))
|
|
461
|
-
|
|
464
|
+
state_pad = " " * (state_w - width(state_label))
|
|
465
|
+
value = f"{color}{mark}{RESET} {row.executable}{pad} {DIM}{state_label}{state_pad}{RESET}"
|
|
462
466
|
body.append((label, value))
|
|
463
467
|
notes = [message] if message else []
|
|
464
468
|
notes.append(
|
|
@@ -4,7 +4,7 @@ requires-python = ">=3.10"
|
|
|
4
4
|
|
|
5
5
|
[[package]]
|
|
6
6
|
name = "aicp-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.8.0"
|
|
8
8
|
source = { editable = "." }
|
|
9
9
|
|
|
10
10
|
[package.dev-dependencies]
|
|
@@ -49,7 +49,7 @@ name = "exceptiongroup"
|
|
|
49
49
|
version = "1.3.1"
|
|
50
50
|
source = { registry = "https://pypi.org/simple" }
|
|
51
51
|
dependencies = [
|
|
52
|
-
{ name = "typing-extensions" },
|
|
52
|
+
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
|
53
53
|
]
|
|
54
54
|
sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
|
|
55
55
|
wheels = [
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|