aicp-cli 0.3.0__py3-none-any.whl
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/__init__.py +18 -0
- aicp/_keyreader.py +210 -0
- aicp/_skills_data/commit.md +90 -0
- aicp/_skills_data/safe-git-push/SKILL.md +55 -0
- aicp/_skills_data/safe-git-push/scripts/safe_push.py +248 -0
- aicp/_utils.py +183 -0
- aicp/budget.py +158 -0
- aicp/cli.py +595 -0
- aicp/config.py +434 -0
- aicp/contracts.py +144 -0
- aicp/gitflow.py +456 -0
- aicp/i18n.py +228 -0
- aicp/menu.py +1013 -0
- aicp/notify.py +87 -0
- aicp/present.py +303 -0
- aicp/quota.py +115 -0
- aicp/runner.py +560 -0
- aicp/secrets.py +291 -0
- aicp/skills.py +679 -0
- aicp/timing.py +116 -0
- aicp_cli-0.3.0.dist-info/METADATA +298 -0
- aicp_cli-0.3.0.dist-info/RECORD +25 -0
- aicp_cli-0.3.0.dist-info/WHEEL +4 -0
- aicp_cli-0.3.0.dist-info/entry_points.txt +2 -0
- aicp_cli-0.3.0.dist-info/licenses/LICENSE +20 -0
aicp/i18n.py
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"""Messages: the mechanism, and the zh-TW catalogue.
|
|
2
|
+
|
|
3
|
+
Shape copied from ``~/scripts/bin/aicp``'s ``_aicp_t``/``_AICP_ZH`` (itself
|
|
4
|
+
copied from ai-accounts' ``i18n.py``): the English text stays at the call
|
|
5
|
+
site as ``t()``'s second argument (so the printed wording stays readable
|
|
6
|
+
where it's used, and English output never depends on a lookup that could
|
|
7
|
+
silently miss); only the zh-TW translation lives in a catalogue dict here. A
|
|
8
|
+
missing msgid, or one with no zh-TW entry yet, degrades to the English text
|
|
9
|
+
passed in — never a blank line, never a crash.
|
|
10
|
+
|
|
11
|
+
``AICP_LANG`` is resolved once, at import time (same posture as ai-accounts'
|
|
12
|
+
language resolution) — ``en`` or ``zh-TW`` only; anything else is a value the
|
|
13
|
+
writer got wrong, so it is announced to stderr and treated as ``en``, never
|
|
14
|
+
silently coerced or guessed at.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import os
|
|
20
|
+
import sys
|
|
21
|
+
|
|
22
|
+
VALID_LANGUAGES = ("en", "zh-TW")
|
|
23
|
+
DEFAULT_LANGUAGE = "en"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _resolve_language() -> str:
|
|
27
|
+
raw = os.environ.get("AICP_LANG", DEFAULT_LANGUAGE)
|
|
28
|
+
if raw not in VALID_LANGUAGES:
|
|
29
|
+
print(
|
|
30
|
+
f"aicp: ignoring AICP_LANG={raw} (expected en or zh-TW) — using en",
|
|
31
|
+
file=sys.stderr,
|
|
32
|
+
)
|
|
33
|
+
return DEFAULT_LANGUAGE
|
|
34
|
+
return raw
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# Resolved once, at import — mirrors ai-accounts' i18n posture. A test that
|
|
38
|
+
# needs a different language must re-import this module (or call
|
|
39
|
+
# `_resolve_language()` directly) after patching the environment.
|
|
40
|
+
LANGUAGE = _resolve_language()
|
|
41
|
+
|
|
42
|
+
# Catalogue: msgid -> zh-TW translation. Wording is carried over from
|
|
43
|
+
# bin/aicp's `_AICP_ZH` wherever that script had the same message, so the port
|
|
44
|
+
# reads identically to the tool it replaces; only what is new here (the Skills
|
|
45
|
+
# and Doctor rows, the installer prompts, `--help`) is freshly written.
|
|
46
|
+
#
|
|
47
|
+
# House style (from ai-accounts / bin/aicp): fullwidth :,。(), a single
|
|
48
|
+
# half-width space around embedded Latin/number runs, product and command
|
|
49
|
+
# nouns left untranslated (commit, push, branch, remote, git fetch, skill,
|
|
50
|
+
# timeout, /safe-git-push), the same emoji per status in both languages, "·"
|
|
51
|
+
# as separator, " — " to attach a trailing clause.
|
|
52
|
+
#
|
|
53
|
+
# tests/test_i18n.py re-derives the msgid set from src/ on every run: an entry
|
|
54
|
+
# missing here, an entry nothing uses, and a %s count that drifts from the
|
|
55
|
+
# English default are all failures, not silent fallbacks.
|
|
56
|
+
CATALOG: dict[str, str] = {
|
|
57
|
+
# ── notifier ────────────────────────────────────────────────────────────
|
|
58
|
+
"telegram_unavailable": "(telegram 無法使用 — 訊息未送出)",
|
|
59
|
+
# ── the run: budget, fallback chain, interruption ───────────────────────
|
|
60
|
+
# bin/aicp carried the ⚠ inside this string; here the call site prints the
|
|
61
|
+
# marker, so it would be doubled.
|
|
62
|
+
"interrupted": "已中斷(Ctrl+C)— 中止執行,後續步驟都不會跑",
|
|
63
|
+
"step_signal_note": "訊號 %s",
|
|
64
|
+
"step_timeout_note": "逾時(超過 %ss)",
|
|
65
|
+
"step_exit_note": "結束碼 %s",
|
|
66
|
+
"budget_hint": "預算 %ss · %s",
|
|
67
|
+
"budget_note_plain": "%s 個檔案 · %s 行",
|
|
68
|
+
"budget_note_history": "%s 個檔案 · %s 行 · 歷史紀錄 %ss",
|
|
69
|
+
"budget_note_fixed": "固定值",
|
|
70
|
+
"budget_note_bad_step": "AICP_STEP_TIMEOUT 值無效 — 改用下限",
|
|
71
|
+
"budget_note_bad_env": "AICP_TIMEOUT_* 值無效 — 改用下限",
|
|
72
|
+
"no_cli_for_step": " ✗ 沒有任何 AI CLI 能執行這個步驟",
|
|
73
|
+
"no_cli_found": "✗ PATH 上找不到任何 AI CLI",
|
|
74
|
+
"no_cli_looked_for": " 已尋找:%s",
|
|
75
|
+
"no_cli_install": " 請安裝其中一個,或在 %s 設定 AICP_CLI_ORDER",
|
|
76
|
+
# ── secret scan ─────────────────────────────────────────────────────────
|
|
77
|
+
"scan_skipped": "▸ 已略過機密掃描(AICP_SKIP_SECRET_SCAN=1)",
|
|
78
|
+
"scan_binary_warn": "⚠ 機密掃描:%s 個檔案內容為二進位 — 未掃描,請自行確認:",
|
|
79
|
+
"scan_found": "✗ 機密掃描發現 %s 筆疑似機密 — 在 /commit 前中止,未呼叫任何 AI CLI",
|
|
80
|
+
"scan_advise": " 請移除或遮蔽該字串後重跑;若為誤判,可用 AICP_SKIP_SECRET_SCAN=1 單次略過。",
|
|
81
|
+
# ── git preconditions and --undo ────────────────────────────────────────
|
|
82
|
+
"not_on_branch": "✗ 不在任何分支上(detached HEAD,或不是 git repo)",
|
|
83
|
+
"undo_no_parent": "✗ 沒有可還原的 commit — HEAD 沒有上一層 commit",
|
|
84
|
+
"undo_unverifiable": "✗ 拒絕 --undo:無法驗證 %s/%s(不可達,或從未推送過)— 有可能正在改寫已推送的歷史",
|
|
85
|
+
"undo_already_pushed": "✗ 拒絕 --undo:最後一個 commit 已經在 %s/%s 上 — undo 會改寫已推送的歷史",
|
|
86
|
+
"undo_reset_failed": "✗ git reset --soft HEAD^ 失敗",
|
|
87
|
+
"undo_done": "✓ 已還原:",
|
|
88
|
+
"undo_note": " 變更已回到 index — 沒有遺失,也沒有推送出去。",
|
|
89
|
+
# ── --swap-ai ───────────────────────────────────────────────────────────
|
|
90
|
+
"swap_current_order": "目前的 fallback 順序:",
|
|
91
|
+
"swap_current_first": " ← 目前的 #1",
|
|
92
|
+
"swap_prompt": "請選擇要移到 #1 的 CLI(1-%s):",
|
|
93
|
+
"swap_invalid": "✗ 無效的選擇:%s",
|
|
94
|
+
"swap_already_first": "▸ %s 已經是 #1 — 不需變更",
|
|
95
|
+
"swap_new_order": "✓ 新的順序:",
|
|
96
|
+
"swap_saved": " 已儲存到 %s — 下一次 aicp 就會採用",
|
|
97
|
+
"persist_failed": "✗ 寫入 %s 失敗",
|
|
98
|
+
# ── the flow: spinners and the two step-skip notices ────────────────────
|
|
99
|
+
"spin_precheck": "正在檢查工作目錄…",
|
|
100
|
+
"spin_fetch": "正在 git fetch %s/%s…",
|
|
101
|
+
"active_chain": "▸ 順序:%s",
|
|
102
|
+
"skip_commit_clean": "▸ 沒有未提交的變更 — 略過 /commit",
|
|
103
|
+
"skip_commit_config": "▸ /commit 已在設定中關閉(AICP_DO_COMMIT=0)— 略過",
|
|
104
|
+
"skip_push_synced": "▸ %s/%s 已經同步 — 略過 /safe-git-push",
|
|
105
|
+
"skip_push_config": "▸ /safe-git-push 已在設定中關閉(AICP_DO_PUSH=0)— 略過",
|
|
106
|
+
# ── the RESULT table ────────────────────────────────────────────────────
|
|
107
|
+
"fetch_note": "⚠ git fetch %s %s 失敗 — 下方的 Ahead/Behind 讀自上一次已知的遠端狀態,不是剛更新的",
|
|
108
|
+
"state_in_sync": "✅ 已同步",
|
|
109
|
+
"state_not_pushed": "⚠️ 未推送",
|
|
110
|
+
"state_remote_missing": "✗ 找不到 %s/%s",
|
|
111
|
+
"result_title": "結果",
|
|
112
|
+
"result_repo": "Repo",
|
|
113
|
+
"result_branch": "分支",
|
|
114
|
+
"result_remote": "遠端",
|
|
115
|
+
"result_new_commits": "新增 commit",
|
|
116
|
+
"result_ahead": "領先",
|
|
117
|
+
"result_behind": "落後",
|
|
118
|
+
"result_status": "狀態",
|
|
119
|
+
"commit_handler": "Commit 處理 CLI",
|
|
120
|
+
"push_handler": "Push 處理 CLI",
|
|
121
|
+
"panel_value_header": "值",
|
|
122
|
+
"panel_repo": "repo",
|
|
123
|
+
"panel_branch": "分支",
|
|
124
|
+
"commits_panel_title": "新增的 COMMIT",
|
|
125
|
+
"cannot_verify": "無法驗證這次 push:%s/%s 解析不出來。",
|
|
126
|
+
"cannot_verify_note": " 遠端可能不可達,或這個分支從未推送過。",
|
|
127
|
+
"local_not_pushed": "本地未推送:",
|
|
128
|
+
"remote_not_merged": "遠端未合入:",
|
|
129
|
+
# ── Telegram ────────────────────────────────────────────────────────────
|
|
130
|
+
"tg_timeout": "⏱️ aicp:%s 執行「%s」時 %s 逾時(超過 %ss 預算,%s),已切換下一個 CLI。",
|
|
131
|
+
"step_quota_note": "配額/速率限制已耗盡 — 本次執行略過",
|
|
132
|
+
"step_quota_cooling": "配額冷卻中 — 尚餘 %s 分鐘,略過",
|
|
133
|
+
"tg_quota": "⚠️ aicp:%s 執行「%s」時 %s 的配額/速率限制已耗盡 — 本次執行後續略過。",
|
|
134
|
+
"usage_limit_reached": "usage_limit_reached",
|
|
135
|
+
"tg_unverifiable": "⚠️ aicp:分支 %s(%s)無法驗證 push —— %s/%s 無法解析(遠端不可達,或這個分支從未推送過)。新增 commit %s 個。",
|
|
136
|
+
"tg_not_synced": "⚠️ aicp:分支 %s(%s)未與 %s/%s 同步",
|
|
137
|
+
"tg_tbl_item": "項目",
|
|
138
|
+
"tg_tbl_value": "值",
|
|
139
|
+
# ── --config menu: rows, help lines, the typed-choice surface ───────────
|
|
140
|
+
"config_title": "aicp 設定",
|
|
141
|
+
"config_group_steps": "執行階段",
|
|
142
|
+
"config_group_general": "一般",
|
|
143
|
+
"config_group_tools": "工具",
|
|
144
|
+
"config_do_commit": "執行 /commit 階段",
|
|
145
|
+
"config_do_push": "執行 /safe-git-push 階段",
|
|
146
|
+
"config_language": "語言",
|
|
147
|
+
"config_cli_order": "AI CLI 順序",
|
|
148
|
+
# The feature is called Skills in every AI CLI that has it; translating it
|
|
149
|
+
# would only make the row harder to match to what the vendors call it.
|
|
150
|
+
"config_skills": "Skills",
|
|
151
|
+
"config_doctor": "健康檢查",
|
|
152
|
+
"config_on": "開啟",
|
|
153
|
+
"config_off": "關閉",
|
|
154
|
+
"config_help_commit": "關閉時不 stage 也不 commit,只推送已經 commit 的內容。",
|
|
155
|
+
"config_help_push": "關閉時只做 commit 就停下,分支會領先遠端。",
|
|
156
|
+
"config_help_lang": "所有訊息的語言,包含 Telegram 通知。",
|
|
157
|
+
"config_help_cli": "完整的 fallback 順序,由左到右依序嘗試;←/→ 可輪替順序,未安裝的 CLI 會自動跳過。",
|
|
158
|
+
"config_help_skills": "把 aicp 的 /commit 與 /safe-git-push 裝進每個 AI CLI,你自己的同名檔案一律保留。",
|
|
159
|
+
"config_help_doctor": "平常不會出聲的項目:timeout、被略過的 config.json 設定、skills、git remote。",
|
|
160
|
+
"config_keys_plain": "1-%s 切換 · q 離開 · 自動儲存",
|
|
161
|
+
"config_keys_tui": "↑↓ 選擇 · ←→ 切換 · ⏎ 切換/執行 · q/Ctrl-C 離開 · 自動儲存",
|
|
162
|
+
"config_prompt": "請選擇要修改的項目(1-%s,q 離開):",
|
|
163
|
+
"config_bad_number": "⚠ 請輸入上面列出的項目編號。",
|
|
164
|
+
# ── Skills: the row's inline status, the report, the installer prompts ──
|
|
165
|
+
"skills_none": "沒有已設定的 CLI",
|
|
166
|
+
"skills_missing": "⚠ %s 缺少",
|
|
167
|
+
"skills_older": "⚠ %s 需更新",
|
|
168
|
+
"skills_yours": "✓ 保留你自己的",
|
|
169
|
+
"skills_ok": "✓ 已安裝",
|
|
170
|
+
"skills_title": "SKILLS",
|
|
171
|
+
"skills_skipped": "這裡沒有安裝:%s",
|
|
172
|
+
"skills_state_missing": "尚未安裝",
|
|
173
|
+
"skills_state_current": "已安裝(%s)",
|
|
174
|
+
"skills_state_older": "來自舊版 aicp(%s)— 可以更新",
|
|
175
|
+
"skills_state_foreign": "你自己的檔案 — aicp 會直接沿用",
|
|
176
|
+
"skills_install_q": "要安裝/更新 %s 個 skill 嗎?[y/N]:",
|
|
177
|
+
"skills_keep_note": "保留你自己的 %s 個 skill — aicp 會直接沿用,這不是錯誤。",
|
|
178
|
+
"skills_force_q": "要改用 aicp 的版本嗎?你的檔案會先備份 [y/N]:",
|
|
179
|
+
"skills_installed": "%s 已安裝到 %s",
|
|
180
|
+
"skills_upgraded": "%s 已為 %s 更新",
|
|
181
|
+
"skills_overwritten": "%s 已為 %s 換成 aicp 的版本 — 你的原檔在 %s",
|
|
182
|
+
"skills_kept": "%s:保留你自己的版本 — aicp 會直接沿用",
|
|
183
|
+
"skills_nothing": "沒有需要處理的項目。",
|
|
184
|
+
"install_needs_yes": " 要實際寫入請加上 --install-skills --yes",
|
|
185
|
+
"skill_nudge": "▸ %s 缺少 %s skill — 執行 aicp --config 安裝",
|
|
186
|
+
# ── Doctor: the row's inline status and the health report ───────────────
|
|
187
|
+
"doctor_clear": "✓ 一切正常",
|
|
188
|
+
"doctor_one": "⚠ 1 項提醒",
|
|
189
|
+
"doctor_many": "⚠ %s 項提醒",
|
|
190
|
+
"health_footer": "提醒只是讓你知道,不是失敗 — aicp 照樣可以跑。",
|
|
191
|
+
"health_timeout_ok": "每次 CLI 呼叫的 timeout 用 %s",
|
|
192
|
+
"health_timeout_missing": "PATH 上沒有 timeout/gtimeout — 每次 AI CLI 呼叫都沒有時間上限",
|
|
193
|
+
"health_no_config": "還沒有 %s — 全部採用內建預設值",
|
|
194
|
+
"health_config_ok": "%s:讀到 %s 個設定",
|
|
195
|
+
"health_env_only": "%s 在 %s 裡會被忽略 — 這個項目只能從環境變數/PATH 取得",
|
|
196
|
+
"health_dropped": "%s 被 %s 略過 — 值裡有不允許的字元",
|
|
197
|
+
"health_skills_none": "找不到任何 AI CLI 的設定目錄 — 沒有地方可以安裝 skills",
|
|
198
|
+
"health_skill_missing": "%s:%s skill 尚未安裝 — 用 Skills 那一列安裝",
|
|
199
|
+
"health_skill_older": "%s:%s skill 來自舊版 aicp — 用 Skills 那一列更新",
|
|
200
|
+
"health_skill_foreign": "%s:%s skill 是你自己的 — aicp 會直接沿用",
|
|
201
|
+
"health_skills_ok": "所有已設定的 CLI 都有 skills",
|
|
202
|
+
"health_no_branch": "目前不在分支上(detached HEAD,或不是 git repo)— aicp 需要分支才能 push",
|
|
203
|
+
"health_no_remote": "這個 repo 沒有 git remote — 可以 commit,但沒有地方 push",
|
|
204
|
+
"health_git_ok": "分支 %s → remote %s",
|
|
205
|
+
# ── --help (argparse's own furniture stays English; see cli.py) ─────────
|
|
206
|
+
"help_description": "AI commit + push,附一份 git 驗證過的結果摘要。",
|
|
207
|
+
"help_verbose": "即時輸出每個 CLI 的內容,不用轉圈動畫",
|
|
208
|
+
"help_undo": "還原最後一個 commit(git reset --soft HEAD^);已經在遠端上就拒絕。不呼叫任何 AI CLI",
|
|
209
|
+
"help_swap_ai": "把某個 CLI 移到 fallback 順序的 #1,並存進 config.json",
|
|
210
|
+
"help_config": "設定選單:兩個執行階段、語言、CLI 順序",
|
|
211
|
+
"help_doctor": "列出每個 CLI 的內建 skill 各自處於什麼狀態",
|
|
212
|
+
"help_json": "--doctor 改輸出 JSON",
|
|
213
|
+
"help_install_skills": "把內建的 skills 安裝到每個已設定的 CLI",
|
|
214
|
+
"help_yes": "--install-skills:實際寫入(不加就只是預覽)",
|
|
215
|
+
"help_force": "--install-skills:覆蓋別人的同名檔案,並留下 .bak",
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def t(msgid: str, english: str, *args: object) -> str:
|
|
220
|
+
"""Look up *msgid*; zh-TW translation if resolved and present, else
|
|
221
|
+
*english*. With no *args* the text is returned verbatim (never run
|
|
222
|
+
through ``%``-formatting), so a literal ``%`` in a message can never be
|
|
223
|
+
misread as a format specifier.
|
|
224
|
+
"""
|
|
225
|
+
text = CATALOG.get(msgid, english) if LANGUAGE != "en" else english
|
|
226
|
+
if args:
|
|
227
|
+
return text % args
|
|
228
|
+
return text
|