@icode-js/icode 3.1.0 → 3.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.
Files changed (39) hide show
  1. package/README.md +105 -5
  2. package/package.json +1 -1
  3. package/src/cli.js +6 -0
  4. package/src/commands/clean.js +4 -1
  5. package/src/commands/codereview.js +4 -1
  6. package/src/commands/completion.js +9 -6
  7. package/src/commands/config.js +9 -6
  8. package/src/commands/doctor.js +80 -0
  9. package/src/commands/explain.js +4 -1
  10. package/src/commands/help.js +10 -0
  11. package/src/commands/info.js +4 -1
  12. package/src/commands/migrate.js +38 -1
  13. package/src/commands/push.js +45 -18
  14. package/src/commands/ready.js +110 -0
  15. package/src/commands/save.js +98 -0
  16. package/src/commands/undo.js +13 -1
  17. package/src/core/ai/diff-prompt.js +204 -0
  18. package/src/core/cli/args.js +23 -2
  19. package/src/core/cli/completion.js +33 -0
  20. package/src/core/config/store.js +2 -4
  21. package/src/core/git/context.js +14 -15
  22. package/src/core/git/preconditions.js +88 -0
  23. package/src/core/git/service.js +151 -1
  24. package/src/core/remote-merge/providers/gitlab.js +149 -90
  25. package/src/core/tools/interactive.js +209 -34
  26. package/src/core/tools/loading.js +5 -1
  27. package/src/core/tools/logger.js +5 -0
  28. package/src/workflows/ai-codereview-workflow.js +14 -2
  29. package/src/workflows/ai-commit-workflow.js +14 -6
  30. package/src/workflows/checkout-workflow.js +14 -2
  31. package/src/workflows/clean-workflow.js +87 -70
  32. package/src/workflows/doctor-workflow.js +131 -0
  33. package/src/workflows/migrate-workflow.js +265 -58
  34. package/src/workflows/push-workflow.js +232 -50
  35. package/src/workflows/ready-workflow.js +278 -0
  36. package/src/workflows/rollback-workflow.js +20 -5
  37. package/src/workflows/save-workflow.js +152 -0
  38. package/src/workflows/sync-workflow.js +18 -2
  39. package/src/workflows/undo-workflow.js +25 -7
package/README.md CHANGED
@@ -7,6 +7,7 @@
7
7
  - 更稳定地处理复杂 Git 场景
8
8
  - 更好支持 `husky/git hooks` 校验链路
9
9
  - 默认识别“继承父级 Git 仓库”的目录结构
10
+ - 提供面向人类和 AI agent 的安全场景入口,而不是复刻 Git 子命令
10
11
  - 采用更现代的 Node 20+ ESM 架构(无运行时第三方依赖)
11
12
 
12
13
  ## 安装
@@ -20,6 +21,9 @@ npm i -g @icode-js/icode
20
21
  ```bash
21
22
  icode help # 查看命令总览
22
23
  icode info
24
+ icode ready
25
+ icode save -m "feat: add login form"
26
+ icode doctor
23
27
  icode config ai list
24
28
  ```
25
29
 
@@ -50,7 +54,7 @@ source <(icode completion zsh)
50
54
 
51
55
  ## AI 能力配置
52
56
 
53
- 先配置一个 AI profile(支持 OpenAI/Anthropic/Ollama 四种接口格式):
57
+ 先配置一个 AI profile(支持 OpenAI/Anthropic/Ollama 三种接口格式):
54
58
 
55
59
  ```bash
56
60
  icode config ai set openai \
@@ -99,8 +103,75 @@ icode config ai options set explain --json '{"profile":"ollama","base":"origin/m
99
103
  icode config ai options set push --json '{"aiProfile":"ollama","aiCommitLang":"zh"}'
100
104
  ```
101
105
 
106
+ ## 健康检查
107
+
108
+ ```bash
109
+ icode doctor
110
+ icode doctor --json
111
+ ```
112
+
113
+ `doctor` 会检查 Git 命令、`origin`、hooks、AI profile 与远程合并配置,并给出修复建议。建议新成员初始化环境后先执行一次。
114
+
115
+ 参数说明:
116
+
117
+ - `--repo-mode auto|strict`:仓库模式(自动继承父仓库/禁止继承)
118
+ - `--json`:输出机器可读 JSON,适合脚本或 CI 读取
119
+ - `-h, --help`:查看帮助
120
+
102
121
  ## 核心命令
103
122
 
123
+ ### ready
124
+
125
+ ```bash
126
+ icode ready
127
+ icode ready --json
128
+ ```
129
+
130
+ `ready` 是继续开发、保存或推送前的场景化检查入口,不会修改 Git 状态。它会检查:
131
+
132
+ - 当前是否在明确分支上
133
+ - 是否存在未完成的 `merge/rebase/cherry-pick/revert`
134
+ - 是否存在未提交改动或冲突文件
135
+ - 当前分支与 upstream 的 ahead/behind 关系
136
+ - AI profile 与远程合并配置是否可用
137
+
138
+ 参数说明:
139
+
140
+ - `--repo-mode auto|strict`:仓库模式(自动继承父仓库/禁止继承)
141
+ - `--json`:输出机器可读 JSON,适合 AI agent 或脚本判断下一步
142
+ - `-h, --help`:查看帮助
143
+
144
+ ### save
145
+
146
+ ```bash
147
+ icode save -m "feat: add login form"
148
+ icode save --ai-commit -y
149
+ icode save -m "fix: adjust copy" --dry-run
150
+ icode save --ai-commit --dry-run --json
151
+ ```
152
+
153
+ `save` 是安全快速提交场景:检查状态 -> 暂存全部改动 -> 创建本地 commit。它不会 push,也不会隐式生成提交信息。
154
+
155
+ - 无改动会直接失败
156
+ - 存在冲突会直接失败
157
+ - 存在未完成 Git 操作会直接失败
158
+ - `--message` 和 `--ai-commit` 必须二选一
159
+ - `--dry-run` 只检查并展示计划,不执行 `git add` 或 `git commit`
160
+ - `--ai-commit` 必须使用已配置的 AI profile,不做静默降级
161
+
162
+ 参数说明:
163
+
164
+ - `-m, --message <msg>`:指定提交信息
165
+ - `--ai-commit`:使用 AI 根据全部未提交改动生成提交信息
166
+ - `--profile <name>`:指定 AI profile(仅 `--ai-commit` 使用)
167
+ - `--lang <zh|en>`:AI 提交信息语言,默认 `zh`
168
+ - `--dry-run`:只检查并展示将提交的信息,不执行 add/commit
169
+ - `--json`:输出机器可读 JSON
170
+ - `--repo-mode auto|strict`:仓库模式(自动继承父仓库/禁止继承)
171
+ - `--no-verify`:commit 时跳过 hook/husky 校验
172
+ - `-y, --yes`:自动确认 AI 提交
173
+ - `-h, --help`:查看帮助
174
+
104
175
  ### checkout
105
176
 
106
177
  ```bash
@@ -110,6 +181,7 @@ icode checkout <branch> [base] [--push-origin] [--pull-main] [--repo-mode auto|s
110
181
  - 本地存在分支: 直接切换
111
182
  - 远程存在分支: 本地创建 tracking 分支并切换
112
183
  - 都不存在: 从 `base` 或默认主分支创建
184
+ - 执行前要求工作区干净,且不存在未完成的 merge/rebase/cherry-pick/revert
113
185
 
114
186
  参数说明:
115
187
 
@@ -125,7 +197,7 @@ icode checkout <branch> [base] [--push-origin] [--pull-main] [--repo-mode auto|s
125
197
  ### push
126
198
 
127
199
  ```bash
128
- icode push [targetBranch...] [-m "commit message"] [--ai-commit] [--pull-main] [--not-push-current] [--remote-merge]
200
+ icode push [targetBranch...] [-m "commit message"] [--ai-commit] [--pull-main] [--not-push-current] [-r|--remote-merge] [--dry-run]
129
201
  ```
130
202
 
131
203
  示例:
@@ -133,9 +205,13 @@ icode push [targetBranch...] [-m "commit message"] [--ai-commit] [--pull-main] [
133
205
  ```bash
134
206
  icode push -m "feat: release" # 提交并推送当前分支
135
207
  icode push release test -m "feat: batch publish" -y # 默认本地 merge 推送到多个分支
208
+ icode push release test --dry-run # 只查看计划,不执行任何 Git/远程合并副作用
209
+ icode push release test -r -y # 使用远程 MR 模式合并到多个目标分支
136
210
  ```
137
211
 
138
212
  - 自动 `add + commit + push`
213
+ - 存在冲突或未完成 Git 操作时会直接停止
214
+ - 本地 merge 冲突会暂停在目标分支现场;解决后执行 `icode undo --recover continue`,或执行 `icode undo --recover abort` 中止
139
215
  - 支持 `--ai-commit` 在 push 前自动生成并应用 AI 提交信息
140
216
  - 若仓库存在 husky/hooks/commitlint 规则,AI commit 会先扫描本地规范再生成提交信息
141
217
  - 支持把当前分支合并到多个目标分支
@@ -149,7 +225,8 @@ icode push release test -m "feat: batch publish" -y # 默认本地 merge 推送
149
225
  - `-m, --message <msg>`:提交信息(未填会提示输入)
150
226
  - `-y, --yes`:自动确认(跳过确认提示)
151
227
  - `--local-merge`:显式声明使用本地 merge 模式(当前为默认行为,会切换分支并生成 merge commit)
152
- - `--remote-merge`:改为远程 PR/MR 模式;会先推送源分支,再通过 API 创建合并请求;若发生冲突会暂停,并输出明确失败原因
228
+ - `-r, --remote-merge`:改为远程 PR/MR 模式;会先推送源分支,再通过 API 创建合并请求;若发生冲突会暂停,并输出明确失败原因
229
+ - `--dry-run`:只展示计划,不执行 commit/fetch/pull/push/merge/MR
153
230
  - `--ai-commit`:push 前自动执行 AI commit
154
231
  - `--ai-profile <name>`:指定 AI profile(用于 `--ai-commit`)
155
232
  - `--pull-main`:提交前同步主分支到当前分支
@@ -160,6 +237,7 @@ icode push release test -m "feat: batch publish" -y # 默认本地 merge 推送
160
237
  - `-h, --help`:查看帮助
161
238
 
162
239
  说明:
240
+
163
241
  - `push` 的布尔开关(如 `--ai-commit`、`--pull-main`、`--no-verify`、`-y`)仅在命令行显式传入时生效。
164
242
 
165
243
  远程合并配置示例:
@@ -169,10 +247,13 @@ icode config platform remote-merge set --provider gitlab --api-key rm_xxx
169
247
  ```
170
248
 
171
249
  建议:
250
+
172
251
  - 密钥按平台维度管理,例如 GitLab / GitHub / 自建平台。
173
252
  - GitLab API 地址固定为仓库 `origin` 对应站点的 `/api/v4`,无需再单独配置服务地址。
174
253
  - 项目/仓库默认启用远程合并;如个别仓库不希望启用,可显式设置 `repositories.<repo>.remoteMerge.enabled=false`。
175
- - 远程合并会直接根据 `origin` 自动提取 GitLab 项目地址;若无法识别,会提示你修正仓库 remote 配置。
254
+ - 远程合并始终根据 `origin` 自动提取 GitLab 项目地址;若无法识别,会提示你修正仓库 remote 配置。
255
+ - GitLab 审批规则由 merge API 统一判断;若审批不足,会在 merge 阶段输出明确失败原因。
256
+ - GitLab 合并检查会展示等待进度,并在长时间 pending 后超时退出,避免命令无限卡住。
176
257
  - 这样更符合密钥复用与安全边界,避免多个仓库重复维护同一平台 API 地址。
177
258
 
178
259
  ### ai
@@ -218,6 +299,7 @@ icode sync [branch...] [--all-local] [--merge-main] [--push]
218
299
  ```
219
300
 
220
301
  - 批量同步多个分支(fetch + pull)
302
+ - 执行前要求当前处于普通分支、工作区干净,且不存在未完成 Git 操作
221
303
  - 可选自动同步全部本地分支(`--all-local`)
222
304
  - 可选把主分支自动 merge 到目标分支(`--merge-main`)
223
305
  - 可选同步后自动推送(`--push`)
@@ -241,6 +323,7 @@ icode clean [--merged-target <branch>] [--remote] [--force]
241
323
  ```
242
324
 
243
325
  - 安全清理“已合并”本地分支
326
+ - 执行前要求当前处于普通分支、工作区干净,且不存在未完成 Git 操作
244
327
  - 默认保护当前分支/主分支/配置中的受保护分支
245
328
  - 可选同步删除远程分支(`--remote`)
246
329
 
@@ -275,13 +358,15 @@ icode tag [--name <tag>] [--message <msg>] [--from <ref>]
275
358
 
276
359
  ```bash
277
360
  icode undo [ref] [--mode revert|soft|mixed|hard] [--ref <ref>] [--hash <hash>] [--recover continue|abort|keep]
361
+ icode undo --mode revert --ref HEAD --json
278
362
  ```
279
363
 
280
364
  - 向导式撤销命令(交互选择回滚策略)
281
365
  - 适合新人或低频 Git 操作场景
282
366
  - 也支持非交互参数:`--mode` + `--ref` / `--hash`
283
367
  - 支持用位置参数指定 commit hash,例如:`icode undo a1b2c3d --mode revert -y`
284
- - 检测到 revert/cherry-pick 冲突时,会提示继续或中止
368
+ - 检测到 merge/revert/cherry-pick 冲突时,会提示继续或中止
369
+ - 支持 `--json` 输出结构化结果;适合 AI agent 判断是否已恢复现场
285
370
 
286
371
  参数说明:
287
372
 
@@ -289,6 +374,7 @@ icode undo [ref] [--mode revert|soft|mixed|hard] [--ref <ref>] [--hash <hash>] [
289
374
  - `--ref <ref>`:回滚目标,默认按 mode 自动给出
290
375
  - `--hash <hash>`:按 commit hash 指定回滚目标(等同 `--ref`)
291
376
  - `--recover <action>`:冲突恢复策略(`continue|abort|keep`)
377
+ - `--json`:输出机器可读 JSON
292
378
  - `-y, --yes`:自动确认(跳过确认提示)
293
379
  - `--repo-mode auto|strict`:仓库模式(自动继承父仓库/禁止继承)
294
380
  - `-h, --help`:查看帮助
@@ -297,11 +383,16 @@ icode undo [ref] [--mode revert|soft|mixed|hard] [--ref <ref>] [--hash <hash>] [
297
383
 
298
384
  ```bash
299
385
  icode migrate <sourceBranch> <targetBranch> [--range <from..to>] [--push]
386
+ icode migrate <sourceBranch> <targetBranch> --dry-run --json
300
387
  ```
301
388
 
302
389
  - 将 source 分支的提交迁移到 target(底层是 `cherry-pick`)
303
390
  - 默认迁移 `target..source` 的增量提交
304
391
  - 可通过 `--range` 精确指定迁移范围
392
+ - 执行前要求工作区干净,且不能存在未完成的 merge/rebase/cherry-pick/revert
393
+ - 支持 `--dry-run` 输出 checkout/pull/cherry-pick/push 计划,不产生 Git 副作用
394
+ - 支持 `--json` 输出结构化计划或执行结果,适合 AI agent 消费
395
+ - 如果迁移冲突,可使用 `icode undo --recover continue|abort|keep` 处理现场
305
396
 
306
397
  参数说明:
307
398
 
@@ -309,6 +400,8 @@ icode migrate <sourceBranch> <targetBranch> [--range <from..to>] [--push]
309
400
  - `<targetBranch>`:迁移目标分支
310
401
  - `-i, --interactive`:交互模式(单选/多选 source/target 与迁移范围)
311
402
  - `--range <from..to>`:指定提交范围,例如 `main..feature-x`
403
+ - `--dry-run`:只展示迁移计划,不 checkout/pull/cherry-pick/push
404
+ - `--json`:输出机器可读 JSON
312
405
  - `--push`:迁移后自动推送 target 分支
313
406
  - `-y, --yes`:自动确认(跳过确认提示)
314
407
  - `--repo-mode auto|strict`:仓库模式(自动继承父仓库/禁止继承)
@@ -323,6 +416,7 @@ icode config get defaults.repoMode
323
416
  icode config set defaults.repoMode strict
324
417
  icode config protect add main release
325
418
  icode config protect list
419
+ icode config platform remote-merge set --provider gitlab --api-key rm_xxx
326
420
  ```
327
421
 
328
422
  参数说明:
@@ -335,6 +429,8 @@ icode config protect list
335
429
  - `protect add <branch...>`:添加受保护分支
336
430
  - `protect remove <branch...>`:移除受保护分支
337
431
  - `ai <subcommand>`:AI profile 管理(见上文“AI 能力配置”)
432
+ - `platform remote-merge show`:查看远程合并平台配置(密钥脱敏)
433
+ - `platform remote-merge set --provider gitlab --api-key <key>`:写入 GitLab 远程合并密钥
338
434
  - `--repo-mode auto|strict`:仓库模式(仅影响 protect)
339
435
  - `-h, --help`:查看帮助
340
436
 
@@ -368,6 +464,10 @@ icode push -m "chore: hotfix" --no-verify
368
464
  - 默认 `--repo-mode auto`:自动定位到父仓库根目录执行
369
465
  - `--repo-mode strict`:检测到继承时直接阻断,避免误操作
370
466
 
467
+ ### 3) 非 Git 目录
468
+
469
+ `icode` 不会在非 Git 目录中隐式执行 `git init`。如果需要初始化仓库,请先手动执行 `git init`,再运行 `icode` 命令。
470
+
371
471
  ## 开发
372
472
 
373
473
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@icode-js/icode",
3
- "version": "3.1.0",
3
+ "version": "3.2.0",
4
4
  "description": "Modern git workflow CLI with robust parent-repo and hook handling",
5
5
  "type": "module",
6
6
  "author": "C",
package/src/cli.js CHANGED
@@ -4,11 +4,14 @@ import { runCodeReviewCommand } from './commands/codereview.js'
4
4
  import { runCompletionCommand, runHiddenCompletionCommand } from './commands/completion.js'
5
5
  import { runCheckoutCommand } from './commands/checkout.js'
6
6
  import { runConfigCommand } from './commands/config.js'
7
+ import { runDoctorCommand } from './commands/doctor.js'
7
8
  import { runExplainCommand } from './commands/explain.js'
8
9
  import { printMainHelp } from './commands/help.js'
9
10
  import { runInfoCommand } from './commands/info.js'
10
11
  import { runMigrateCommand } from './commands/migrate.js'
11
12
  import { runPushCommand } from './commands/push.js'
13
+ import { runReadyCommand } from './commands/ready.js'
14
+ import { runSaveCommand } from './commands/save.js'
12
15
  import { runSyncCommand } from './commands/sync.js'
13
16
  import { runTagCommand } from './commands/tag.js'
14
17
  import { runUndoCommand } from './commands/undo.js'
@@ -35,6 +38,8 @@ const COMMANDS = {
35
38
  codereview: runCodeReviewCommand,
36
39
  completion: runCompletionCommand,
37
40
  checkout: runCheckoutCommand,
41
+ ready: runReadyCommand,
42
+ save: runSaveCommand,
38
43
  push: runPushCommand,
39
44
  sync: runSyncCommand,
40
45
  clean: runCleanCommand,
@@ -42,6 +47,7 @@ const COMMANDS = {
42
47
  migrate: runMigrateCommand,
43
48
  tag: runTagCommand,
44
49
  config: runConfigCommand,
50
+ doctor: runDoctorCommand,
45
51
  explain: runExplainCommand,
46
52
  info: runInfoCommand,
47
53
  help: async () => {
@@ -1,4 +1,5 @@
1
1
  import { parseArgs } from 'node:util'
2
+ import { normalizeLegacyArgs } from '../core/cli/args.js'
2
3
  import { logger } from '../core/tools/logger.js'
3
4
  import { runCleanWorkflow } from '../workflows/clean-workflow.js'
4
5
 
@@ -26,9 +27,11 @@ Examples:
26
27
  `)
27
28
  }
28
29
 
30
+ // 执行 clean 子命令,输入原始参数,输出清理结果;参数会先做历史别名归一化。
29
31
  export async function runCleanCommand(rawArgs) {
32
+ const args = normalizeLegacyArgs(rawArgs)
30
33
  const parsed = parseArgs({
31
- args: rawArgs,
34
+ args,
32
35
  allowPositionals: true,
33
36
  options: {
34
37
  'merged-target': { type: 'string' },
@@ -1,4 +1,5 @@
1
1
  import { parseArgs } from 'node:util'
2
+ import { normalizeLegacyArgs } from '../core/cli/args.js'
2
3
  import { logger } from '../core/tools/logger.js'
3
4
  import { runAiCodeReviewWorkflow } from '../workflows/ai-codereview-workflow.js'
4
5
 
@@ -18,9 +19,11 @@ Options:
18
19
  `)
19
20
  }
20
21
 
22
+ // 执行 codereview 子命令,输入原始参数,输出 AI 代码评审结果;参数会先归一化。
21
23
  export async function runCodeReviewCommand(rawArgs) {
24
+ const args = normalizeLegacyArgs(rawArgs)
22
25
  const parsed = parseArgs({
23
- args: rawArgs,
26
+ args,
24
27
  allowPositionals: true,
25
28
  options: {
26
29
  base: { type: 'string' },
@@ -1,4 +1,5 @@
1
1
  import { parseArgs } from 'node:util'
2
+ import { normalizeLegacyArgs } from '../core/cli/args.js'
2
3
  import { IcodeError } from '../core/errors.js'
3
4
  import { getCompletionCandidates, renderCompletionScript } from '../core/cli/completion.js'
4
5
 
@@ -25,24 +26,25 @@ Examples:
25
26
  * 输入为隐藏命令的原始参数数组,输出为 current 和 previousWords。
26
27
  */
27
28
  function parseHiddenCompletionArgs(rawArgs) {
29
+ const args = normalizeLegacyArgs(rawArgs)
28
30
  let current = ''
29
- let separatorIndex = rawArgs.indexOf('--')
31
+ let separatorIndex = args.indexOf('--')
30
32
 
31
33
  if (separatorIndex === -1) {
32
- separatorIndex = rawArgs.length
34
+ separatorIndex = args.length
33
35
  }
34
36
 
35
37
  for (let index = 0; index < separatorIndex; index += 1) {
36
- const token = rawArgs[index]
38
+ const token = args[index]
37
39
  if (token === '--current') {
38
- current = rawArgs[index + 1] || ''
40
+ current = args[index + 1] || ''
39
41
  index += 1
40
42
  }
41
43
  }
42
44
 
43
45
  return {
44
46
  current,
45
- previousWords: rawArgs.slice(separatorIndex + 1)
47
+ previousWords: args.slice(separatorIndex + 1)
46
48
  }
47
49
  }
48
50
 
@@ -51,8 +53,9 @@ function parseHiddenCompletionArgs(rawArgs) {
51
53
  * 输入为命令行参数数组,输出为脚本文本到标准输出。
52
54
  */
53
55
  export async function runCompletionCommand(rawArgs) {
56
+ const args = normalizeLegacyArgs(rawArgs)
54
57
  const parsed = parseArgs({
55
- args: rawArgs,
58
+ args,
56
59
  allowPositionals: true,
57
60
  options: {
58
61
  help: { type: 'boolean', short: 'h', default: false }
@@ -23,7 +23,7 @@ import {
23
23
  setRepoPolicy,
24
24
  setValue
25
25
  } from '../core/config/store.js'
26
- import { parseConfigValue } from '../core/cli/args.js'
26
+ import { normalizeLegacyArgs, parseConfigValue } from '../core/cli/args.js'
27
27
  import { IcodeError } from '../core/errors.js'
28
28
  import { resolveGitContext } from '../core/git/context.js'
29
29
  import { logger } from '../core/tools/logger.js'
@@ -485,19 +485,22 @@ async function resolvePolicyRoot(repoMode) {
485
485
  }
486
486
  }
487
487
 
488
+ // 执行 config 子命令,输入原始参数,输出配置读写结果;参数会先归一化。
488
489
  export async function runConfigCommand(rawArgs) {
489
- if (rawArgs[0] === 'ai') {
490
- await runAiConfigCommand(rawArgs.slice(1))
490
+ const args = normalizeLegacyArgs(rawArgs)
491
+
492
+ if (args[0] === 'ai') {
493
+ await runAiConfigCommand(args.slice(1))
491
494
  return
492
495
  }
493
496
 
494
- if (rawArgs[0] === 'platform') {
495
- await runPlatformConfigCommand(rawArgs.slice(1))
497
+ if (args[0] === 'platform') {
498
+ await runPlatformConfigCommand(args.slice(1))
496
499
  return
497
500
  }
498
501
 
499
502
  const parsed = parseArgs({
500
- args: rawArgs,
503
+ args,
501
504
  allowPositionals: true,
502
505
  options: {
503
506
  help: { type: 'boolean', short: 'h', default: false },
@@ -0,0 +1,80 @@
1
+ import { parseArgs } from 'node:util'
2
+ import { normalizeLegacyArgs } from '../core/cli/args.js'
3
+ import { logger } from '../core/tools/logger.js'
4
+ import { runDoctorWorkflow } from '../workflows/doctor-workflow.js'
5
+
6
+ // 输出 doctor 子命令帮助,输入为空,输出标准帮助文本。
7
+ function printHelp() {
8
+ process.stdout.write(`
9
+ Usage:
10
+ icode doctor [options]
11
+
12
+ Options:
13
+ --repo-mode <mode> 仓库模式: auto(自动继承父仓库) | strict(禁止继承)
14
+ --json 输出机器可读 JSON
15
+ -h, --help 查看帮助
16
+
17
+ Notes:
18
+ 检查 Git、origin、hooks、AI profile 与远程合并配置,并给出修复建议。
19
+ `)
20
+ }
21
+
22
+ // 根据检查状态选择输出函数,输入状态,输出 logger 方法。
23
+ function resolveLogMethod(status) {
24
+ if (status === 'fail') {
25
+ return logger.error
26
+ }
27
+ if (status === 'warn') {
28
+ return logger.warn
29
+ }
30
+ return logger.success
31
+ }
32
+
33
+ // 输出单条 doctor 检查结果,输入检查项,输出彩色日志。
34
+ function printCheck(check) {
35
+ const log = resolveLogMethod(check.status).bind(logger)
36
+ const statusLabel = check.status.toUpperCase()
37
+ log(`[${statusLabel}] ${check.title}: ${check.detail}`)
38
+ if (check.suggestion) {
39
+ logger.info(`建议: ${check.suggestion}`)
40
+ }
41
+ }
42
+
43
+ // 执行 doctor 子命令,输入原始参数,输出健康检查结果。
44
+ // 执行 doctor 子命令,输入原始参数,输出健康检查结果;参数会先归一化。
45
+ export async function runDoctorCommand(rawArgs) {
46
+ const args = normalizeLegacyArgs(rawArgs)
47
+ const parsed = parseArgs({
48
+ args,
49
+ allowPositionals: true,
50
+ options: {
51
+ 'repo-mode': { type: 'string', default: 'auto' },
52
+ json: { type: 'boolean', default: false },
53
+ help: { type: 'boolean', short: 'h', default: false }
54
+ }
55
+ })
56
+
57
+ if (parsed.values.help) {
58
+ printHelp()
59
+ return
60
+ }
61
+
62
+ const result = await runDoctorWorkflow({
63
+ repoMode: parsed.values['repo-mode']
64
+ })
65
+
66
+ if (parsed.values.json) {
67
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`)
68
+ return result
69
+ }
70
+
71
+ logger.info(`仓库根目录: ${result.context.topLevelPath}`)
72
+ logger.info(`配置文件: ${result.configPath}`)
73
+ result.checks.forEach((check) => {
74
+ printCheck(check)
75
+ })
76
+
77
+ const summary = result.summary
78
+ logger.info(`doctor 完成: pass=${summary.pass}, warn=${summary.warn}, fail=${summary.fail}`)
79
+ return result
80
+ }
@@ -1,5 +1,6 @@
1
1
  import { parseArgs } from 'node:util'
2
2
  import { getAiCommandOptions } from '../core/ai/config.js'
3
+ import { normalizeLegacyArgs } from '../core/cli/args.js'
3
4
  import { logger } from '../core/tools/logger.js'
4
5
  import { runAiExplainWorkflow } from '../workflows/ai-explain-workflow.js'
5
6
 
@@ -47,10 +48,12 @@ function resolveStringOption(cliValue, configValue, fallback = '') {
47
48
  return fallback
48
49
  }
49
50
 
51
+ // 执行 explain 子命令,输入原始参数,输出 AI 对 diff 的解释;参数会先归一化。
50
52
  export async function runExplainCommand(rawArgs) {
51
53
  const scopedOptions = getAiCommandOptions('explain')
54
+ const args = normalizeLegacyArgs(rawArgs)
52
55
  const parsed = parseArgs({
53
- args: rawArgs,
56
+ args,
54
57
  allowPositionals: true,
55
58
  options: {
56
59
  base: { type: 'string' },
@@ -13,6 +13,8 @@ Commands:
13
13
  ai AI 助手能力(提交信息/冲突建议/代码评审)
14
14
  codereview AI 代码评审(不带 ai 前缀)
15
15
  checkout 切换/创建分支(本地/远程自动识别)
16
+ ready 检查仓库是否适合继续 save/push(适合 AI agent 读取)
17
+ save 安全快速提交本地改动(检查 + add + commit)
16
18
  push 提交并推送,可合并到多个目标分支
17
19
  sync 批量同步分支(fetch + pull)
18
20
  clean 清理已合并分支(可删远程)
@@ -20,6 +22,7 @@ Commands:
20
22
  migrate 迁移分支提交(cherry-pick)
21
23
  tag 创建并推送 tag(支持自动命名)
22
24
  config 查看和修改本地配置(含 AI profile)
25
+ doctor 检查 Git/AI/远程合并配置健康状态
23
26
  explain AI 解释 Git diff(自然语言)
24
27
  info 查看当前 git 与配置环境
25
28
  completion 生成 bash/zsh tab 补全脚本
@@ -34,6 +37,11 @@ Tips:
34
37
 
35
38
  Examples:
36
39
  icode checkout feature/login main --push-origin
40
+ icode ready
41
+ icode ready --json
42
+ icode save -m "feat: add login form"
43
+ icode save --ai-commit -y
44
+ icode save -m "fix: adjust copy" --dry-run
37
45
  icode ai commit --apply -y
38
46
  icode ai codereview
39
47
  icode codereview --base origin/main --head HEAD
@@ -41,6 +49,7 @@ Examples:
41
49
  icode push release test -m "feat: batch publish" -y
42
50
  icode push release test --ai-commit -y
43
51
  icode push release test -m "feat: keep merge commit" --local-merge -y
52
+ icode push release test --dry-run
44
53
  icode sync --all-local --merge-main
45
54
  icode clean --remote --force -y
46
55
  icode undo a1b2c3d --mode revert -y
@@ -50,6 +59,7 @@ Examples:
50
59
  icode migrate --interactive
51
60
  icode push --no-verify -m "chore: bypass hooks"
52
61
  icode config protect add main release
62
+ icode doctor
53
63
  source <(icode completion zsh)
54
64
  icode info
55
65
  `)
@@ -1,4 +1,5 @@
1
1
  import { parseArgs } from 'node:util'
2
+ import { normalizeLegacyArgs } from '../core/cli/args.js'
2
3
  import { logger } from '../core/tools/logger.js'
3
4
  import { runInfoWorkflow } from '../workflows/info-workflow.js'
4
5
 
@@ -16,9 +17,11 @@ Notes:
16
17
  `)
17
18
  }
18
19
 
20
+ // 执行 info 子命令,输入原始参数,输出仓库与配置概览;参数会先归一化。
19
21
  export async function runInfoCommand(rawArgs) {
22
+ const args = normalizeLegacyArgs(rawArgs)
20
23
  const parsed = parseArgs({
21
- args: rawArgs,
24
+ args,
22
25
  allowPositionals: true,
23
26
  options: {
24
27
  'repo-mode': { type: 'string', default: 'auto' },
@@ -17,6 +17,8 @@ Arguments:
17
17
  Options:
18
18
  -i, --interactive 交互模式(单选/多选 source/target 与迁移范围)
19
19
  --range <spec> 指定提交范围,例如 main..feature-x(交互模式下若提供则直接使用)
20
+ --dry-run 只展示迁移计划,不 checkout/pull/cherry-pick/push
21
+ --json 输出机器可读 JSON,适合 AI agent 或脚本消费
20
22
  --push 迁移后自动推送 target 分支
21
23
  -y, --yes 自动确认(跳过确认提示)
22
24
  --repo-mode <mode> 仓库模式: auto(自动继承父仓库) | strict(禁止继承)
@@ -29,11 +31,32 @@ Notes:
29
31
  Examples:
30
32
  icode migrate feature/login release
31
33
  icode migrate --interactive
34
+ icode migrate feature/login release --dry-run --json
32
35
  icode migrate feature/login release --range HEAD~3..feature/login
33
36
  icode migrate feature/login release --push -y
34
37
  `)
35
38
  }
36
39
 
40
+ // 输出 migrate dry-run 计划,输入 workflow 结果,输出人类可读步骤。
41
+ function printDryRunPlan(result) {
42
+ const previewLimit = 10
43
+ const previewCommits = result.commits.slice(0, previewLimit)
44
+
45
+ logger.info(`[dry-run] source: ${result.sourceBranch} (${result.sourceRef})`)
46
+ logger.info(`[dry-run] target: ${result.targetBranch} (${result.targetRef})`)
47
+ logger.info(`[dry-run] range: ${result.rangeSpec}`)
48
+ logger.info(`[dry-run] commits: ${result.migratedCommits}`)
49
+ previewCommits.forEach((commit, index) => {
50
+ logger.info(`[dry-run] commit ${index + 1}: ${commit.summary || commit.hash}`)
51
+ })
52
+ if (result.commits.length > previewLimit) {
53
+ logger.info(`[dry-run] ... 还有 ${result.commits.length - previewLimit} 个提交未显示,可用 --json 查看完整计划`)
54
+ }
55
+ result.steps.forEach((step, index) => {
56
+ logger.info(`[dry-run] ${index + 1}. ${step.action}: ${step.detail}`)
57
+ })
58
+ }
59
+
37
60
  export async function runMigrateCommand(rawArgs) {
38
61
  const args = normalizeLegacyArgs(rawArgs)
39
62
  const parsed = parseArgs({
@@ -42,6 +65,8 @@ export async function runMigrateCommand(rawArgs) {
42
65
  options: {
43
66
  interactive: { type: 'boolean', short: 'i', default: false },
44
67
  range: { type: 'string' },
68
+ 'dry-run': { type: 'boolean', default: false },
69
+ json: { type: 'boolean', default: false },
45
70
  push: { type: 'boolean', default: false },
46
71
  yes: { type: 'boolean', short: 'y', default: false },
47
72
  'repo-mode': { type: 'string', default: 'auto' },
@@ -70,16 +95,28 @@ export async function runMigrateCommand(rawArgs) {
70
95
  targetBranch,
71
96
  interactive,
72
97
  range: parsed.values.range,
98
+ dryRun: parsed.values['dry-run'],
73
99
  push: parsed.values.push,
74
100
  yes: parsed.values.yes,
75
101
  repoMode: parsed.values['repo-mode'],
76
- noVerify: parsed.values['no-verify']
102
+ noVerify: parsed.values['no-verify'],
103
+ silentLog: parsed.values.json
77
104
  })
78
105
 
106
+ if (parsed.values.json) {
107
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`)
108
+ return result
109
+ }
110
+
79
111
  if (result.canceled) {
80
112
  return
81
113
  }
82
114
 
115
+ if (result.dryRun) {
116
+ printDryRunPlan(result)
117
+ return result
118
+ }
119
+
83
120
  logger.success(
84
121
  `migrate 完成: ${result.sourceBranch} -> ${result.targetBranch}, commits=${result.migratedCommits}, mode=${result.rangeMode || 'range'}`
85
122
  )