@icode-js/icode 3.0.2

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 (46) hide show
  1. package/README.md +346 -0
  2. package/bin/icode.js +6 -0
  3. package/package.json +34 -0
  4. package/src/cli.js +131 -0
  5. package/src/commands/ai.js +287 -0
  6. package/src/commands/checkout.js +59 -0
  7. package/src/commands/clean.js +65 -0
  8. package/src/commands/codereview.js +52 -0
  9. package/src/commands/config.js +513 -0
  10. package/src/commands/explain.js +80 -0
  11. package/src/commands/help.js +49 -0
  12. package/src/commands/info.js +57 -0
  13. package/src/commands/migrate.js +86 -0
  14. package/src/commands/push.js +125 -0
  15. package/src/commands/sync.js +74 -0
  16. package/src/commands/tag.js +53 -0
  17. package/src/commands/undo.js +66 -0
  18. package/src/core/ai-client.js +1125 -0
  19. package/src/core/ai-commit-summary.js +18 -0
  20. package/src/core/ai-config.js +342 -0
  21. package/src/core/ai-diff-range.js +117 -0
  22. package/src/core/args.js +47 -0
  23. package/src/core/commit-conventions.js +169 -0
  24. package/src/core/config-store.js +194 -0
  25. package/src/core/errors.js +25 -0
  26. package/src/core/git-context.js +105 -0
  27. package/src/core/git-service.js +428 -0
  28. package/src/core/hook-diagnostics.js +23 -0
  29. package/src/core/loading.js +36 -0
  30. package/src/core/logger.js +55 -0
  31. package/src/core/prompts.js +152 -0
  32. package/src/core/shell.js +77 -0
  33. package/src/workflows/ai-codereview-workflow.js +126 -0
  34. package/src/workflows/ai-commit-workflow.js +128 -0
  35. package/src/workflows/ai-conflict-workflow.js +102 -0
  36. package/src/workflows/ai-explain-workflow.js +116 -0
  37. package/src/workflows/ai-risk-review-workflow.js +49 -0
  38. package/src/workflows/checkout-workflow.js +85 -0
  39. package/src/workflows/clean-workflow.js +131 -0
  40. package/src/workflows/info-workflow.js +30 -0
  41. package/src/workflows/migrate-workflow.js +449 -0
  42. package/src/workflows/push-workflow.js +276 -0
  43. package/src/workflows/rollback-workflow.js +84 -0
  44. package/src/workflows/sync-workflow.js +141 -0
  45. package/src/workflows/tag-workflow.js +64 -0
  46. package/src/workflows/undo-workflow.js +328 -0
package/README.md ADDED
@@ -0,0 +1,346 @@
1
+ # icode-js v3
2
+
3
+ `icode-js` 是一个面向 Git 日常操作的命令行工具。
4
+
5
+ 这次版本是重写级重构,不复用旧的 `lerna + packages/*` 逻辑,目标是:
6
+
7
+ - 更稳定地处理复杂 Git 场景
8
+ - 更好支持 `husky/git hooks` 校验链路
9
+ - 默认识别“继承父级 Git 仓库”的目录结构
10
+ - 采用更现代的 Node 20+ ESM 架构(无运行时第三方依赖)
11
+
12
+ ## 安装
13
+
14
+ ```bash
15
+ npm i -g icode-js
16
+ ```
17
+
18
+ ## 快速开始
19
+
20
+ ```bash
21
+ icode help # 查看命令总览
22
+ icode info
23
+ icode config ai list
24
+ ```
25
+
26
+ ## 全局参数
27
+
28
+ - `-h, --help`:查看帮助(也可 `icode <command> -h` 查看子命令参数)
29
+ - `-d, --debug`:开启调试日志(输出更多细节)
30
+
31
+ ## AI 能力配置
32
+
33
+ 先配置一个 AI profile(支持 OpenAI/Anthropic/Ollama 四种接口格式):
34
+
35
+ ```bash
36
+ icode config ai set openai \
37
+ --format openai \
38
+ --base-url https://api.openai.com/v1 \
39
+ --api-key sk-xxx \
40
+ --model gpt-4o-mini \
41
+ --activate
42
+ ```
43
+
44
+ Anthropic 示例:
45
+
46
+ ```bash
47
+ icode config ai set claude \
48
+ --format anthropic \
49
+ --base-url https://api.anthropic.com/v1 \
50
+ --api-key xxx \
51
+ --model claude-3-5-sonnet-20241022 \
52
+ --activate
53
+ ```
54
+
55
+ Ollama 本地模型示例:
56
+
57
+ ```bash
58
+ icode config ai set ollama \
59
+ --format ollama \
60
+ --base-url http://127.0.0.1:11434 \
61
+ --model qwen2.5:7b \
62
+ --activate
63
+ ```
64
+
65
+ 常用命令:
66
+
67
+ ```bash
68
+ icode config ai list
69
+ icode config ai show
70
+ icode config ai use claude
71
+ icode config ai test
72
+ ```
73
+
74
+ 可选:为常用命令预设默认 options(命令行显式传参优先级更高):
75
+
76
+ ```bash
77
+ icode config ai options set commit --json '{"profile":"ollama","lang":"zh","yes":true}'
78
+ icode config ai options set explain --json '{"profile":"ollama","base":"origin/main"}'
79
+ icode config ai options set push --json '{"aiProfile":"ollama","aiCommitLang":"zh"}'
80
+ ```
81
+
82
+ ## 核心命令
83
+
84
+ ### checkout
85
+
86
+ ```bash
87
+ icode checkout <branch> [base] [--push-origin] [--pull-main] [--repo-mode auto|strict]
88
+ ```
89
+
90
+ - 本地存在分支: 直接切换
91
+ - 远程存在分支: 本地创建 tracking 分支并切换
92
+ - 都不存在: 从 `base` 或默认主分支创建
93
+
94
+ 参数说明:
95
+
96
+ - `<branch>`:目标分支名(必填)
97
+ - `[base]`:新建分支基线,默认主分支
98
+ - `--push-origin`:新建分支后立即推送到 `origin`
99
+ - `--pull-main`:切换后同步主分支到当前分支
100
+ - `--repo-mode auto|strict`:仓库模式(自动继承父仓库/禁止继承)
101
+ - `--no-verify`:跳过 hook/husky 校验
102
+ - `-y, --yes`:自动确认(跳过交互提示)
103
+ - `-h, --help`:查看帮助
104
+
105
+ ### push
106
+
107
+ ```bash
108
+ icode push [targetBranch...] [-m "commit message"] [--ai-commit] [--pull-main] [--not-push-current]
109
+ ```
110
+
111
+ 示例:
112
+
113
+ ```bash
114
+ icode push -m "feat: release" # 提交并推送当前分支
115
+ icode push release test -m "feat: batch publish" -y # 默认本地 merge 推送到多个分支
116
+ ```
117
+
118
+ - 自动 `add + commit + push`
119
+ - 支持 `--ai-commit` 在 push 前自动生成并应用 AI 提交信息
120
+ - 若仓库存在 husky/hooks/commitlint 规则,AI commit 会先扫描本地规范再生成提交信息
121
+ - 支持把当前分支合并到多个目标分支
122
+ - 默认使用本地 merge 模式(会生成 merge commit)
123
+ - 支持受保护分支策略(通过 `icode config protect ...` 管理)
124
+
125
+ 参数说明:
126
+
127
+ - `[targetBranch...]`:目标分支列表(可多个,空则默认当前分支)
128
+ - `-m, --message <msg>`:提交信息(未填会提示输入)
129
+ - `-y, --yes`:自动确认(跳过确认提示)
130
+ - `--local-merge`:使用本地 merge 模式(默认,会切换分支并生成 merge commit)
131
+ - `--ai-commit`:push 前自动执行 AI commit
132
+ - `--ai-profile <name>`:指定 AI profile(用于 `--ai-commit`)
133
+ - `--pull-main`:提交前同步主分支到当前分支
134
+ - `--not-push-current`:不推送当前分支,只处理目标分支
135
+ - `--force-protected`:强制处理配置里的受保护分支
136
+ - `--repo-mode auto|strict`:仓库模式(自动继承父仓库/禁止继承)
137
+ - `--no-verify`:跳过 hook/husky 校验
138
+ - `-h, --help`:查看帮助
139
+
140
+ 说明:
141
+ - `push` 的布尔开关(如 `--ai-commit`、`--pull-main`、`--no-verify`、`-y`)仅在命令行显式传入时生效。
142
+
143
+ ### ai
144
+
145
+ ```bash
146
+ icode ai commit [--apply]
147
+ icode ai conflict
148
+ icode ai codereview
149
+ icode codereview [--base origin/main --head HEAD]
150
+ ```
151
+
152
+ - `ai commit`: 基于 diff 生成 Conventional Commit 信息;若检测到本地 hook/commitlint 规则,会优先遵循项目规范
153
+ - `ai conflict`: 解析冲突块并生成合并建议
154
+ - `ai codereview`: 默认评审暂存区 + 工作区未提交改动;传 `--base/--head` 时可改为评审分支 diff
155
+
156
+ 参数说明:
157
+
158
+ - `ai commit` 参数
159
+ - `--apply`:直接使用 AI 信息执行 commit
160
+ - `--lang <zh|en>`:输出语言(默认 zh)
161
+ - `--profile <name>`:指定 AI profile
162
+ - `--repo-mode auto|strict`:仓库模式(自动继承父仓库/禁止继承)
163
+ - `--no-verify`:commit 时跳过 hook/husky 校验
164
+ - `-y, --yes`:自动确认(跳过确认提示)
165
+ - `-h, --help`:查看帮助
166
+ - `ai conflict` 参数
167
+ - `--profile <name>`:指定 AI profile
168
+ - `--repo-mode auto|strict`:仓库模式(自动继承父仓库/禁止继承)
169
+ - `-h, --help`:查看帮助
170
+ - `ai codereview` / `codereview` 参数
171
+ - `--base <ref>`:指定分支 diff 基线;未传时默认评审暂存区 + 工作区未提交改动
172
+ - `--head <ref>`:指定分支 diff 终点(默认 `HEAD`)
173
+ - `--focus <text>`:评审重点(安全/性能/测试等)
174
+ - `--profile <name>`:指定 AI profile
175
+ - `--repo-mode auto|strict`:仓库模式(自动继承父仓库/禁止继承)
176
+ - `--dump-response`:输出 AI 原始响应(调试数据格式)
177
+ - `-h, --help`:查看帮助
178
+
179
+ ### sync
180
+
181
+ ```bash
182
+ icode sync [branch...] [--all-local] [--merge-main] [--push]
183
+ ```
184
+
185
+ - 批量同步多个分支(fetch + pull)
186
+ - 可选自动同步全部本地分支(`--all-local`)
187
+ - 可选把主分支自动 merge 到目标分支(`--merge-main`)
188
+ - 可选同步后自动推送(`--push`)
189
+
190
+ 参数说明:
191
+
192
+ - `[branch...]`:需要同步的分支列表(可多个)
193
+ - `--all-local`:自动同步全部本地分支
194
+ - `--merge-main`:同步后把主分支 merge 到目标分支
195
+ - `--rebase`:pull 时使用 rebase
196
+ - `--push`:同步后自动 push
197
+ - `-y, --yes`:自动确认(跳过确认提示)
198
+ - `--repo-mode auto|strict`:仓库模式(自动继承父仓库/禁止继承)
199
+ - `--no-verify`:push 时跳过 hook/husky 校验
200
+ - `-h, --help`:查看帮助
201
+
202
+ ### clean
203
+
204
+ ```bash
205
+ icode clean [--merged-target <branch>] [--remote] [--force]
206
+ ```
207
+
208
+ - 安全清理“已合并”本地分支
209
+ - 默认保护当前分支/主分支/配置中的受保护分支
210
+ - 可选同步删除远程分支(`--remote`)
211
+
212
+ 参数说明:
213
+
214
+ - `--merged-target <branch>`:基于该分支判断“已合并”状态
215
+ - `--keep <branch|csv>`:额外保留分支,可重复或逗号分隔
216
+ - `--remote`:同时删除远程分支
217
+ - `--force`:强制删除本地分支(`-D`)
218
+ - `-y, --yes`:自动确认(跳过确认提示)
219
+ - `--repo-mode auto|strict`:仓库模式(自动继承父仓库/禁止继承)
220
+ - `-h, --help`:查看帮助
221
+
222
+ ### tag
223
+
224
+ ```bash
225
+ icode tag [--name <tag>] [--message <msg>] [--from <ref>]
226
+ ```
227
+
228
+ 默认 tag 规则:`vYYYYMMDD_NN`(例如 `v20260309_01`)
229
+
230
+ 参数说明:
231
+
232
+ - `-n, --name <tag>`:指定 tag 名(默认 `vYYYYMMDD_NN`)
233
+ - `-m, --message <msg>`:tag 备注
234
+ - `--from <ref>`:从指定分支/commit 创建 tag
235
+ - `--repo-mode auto|strict`:仓库模式(自动继承父仓库/禁止继承)
236
+ - `--no-verify`:跳过 hook/husky 校验
237
+ - `-h, --help`:查看帮助
238
+
239
+ ### undo
240
+
241
+ ```bash
242
+ icode undo [--mode revert|soft|mixed|hard] [--ref <ref>] [--recover continue|abort|keep]
243
+ ```
244
+
245
+ - 向导式撤销命令(交互选择回滚策略)
246
+ - 适合新人或低频 Git 操作场景
247
+ - 也支持非交互参数:`--mode` + `--ref`
248
+ - 检测到 revert/cherry-pick 冲突时,会提示继续或中止
249
+
250
+ 参数说明:
251
+
252
+ - `--mode <mode>`:回滚模式(`revert|soft|mixed|hard`)
253
+ - `--ref <ref>`:回滚目标,默认按 mode 自动给出
254
+ - `--recover <action>`:冲突恢复策略(`continue|abort|keep`)
255
+ - `-y, --yes`:自动确认(跳过确认提示)
256
+ - `--repo-mode auto|strict`:仓库模式(自动继承父仓库/禁止继承)
257
+ - `-h, --help`:查看帮助
258
+
259
+ ### migrate
260
+
261
+ ```bash
262
+ icode migrate <sourceBranch> <targetBranch> [--range <from..to>] [--push]
263
+ ```
264
+
265
+ - 将 source 分支的提交迁移到 target(底层是 `cherry-pick`)
266
+ - 默认迁移 `target..source` 的增量提交
267
+ - 可通过 `--range` 精确指定迁移范围
268
+
269
+ 参数说明:
270
+
271
+ - `<sourceBranch>`:迁移来源分支
272
+ - `<targetBranch>`:迁移目标分支
273
+ - `-i, --interactive`:交互模式(单选/多选 source/target 与迁移范围)
274
+ - `--range <from..to>`:指定提交范围,例如 `main..feature-x`
275
+ - `--push`:迁移后自动推送 target 分支
276
+ - `-y, --yes`:自动确认(跳过确认提示)
277
+ - `--repo-mode auto|strict`:仓库模式(自动继承父仓库/禁止继承)
278
+ - `--no-verify`:推送时跳过 hook/husky 校验
279
+ - `-h, --help`:查看帮助
280
+
281
+ ### config
282
+
283
+ ```bash
284
+ icode config list
285
+ icode config get defaults.repoMode
286
+ icode config set defaults.repoMode strict
287
+ icode config protect add main release
288
+ icode config protect list
289
+ ```
290
+
291
+ 参数说明:
292
+
293
+ - `list`:查看全部配置
294
+ - `get <path>`:读取指定配置项
295
+ - `set <path> <value>`:写入配置项(支持数字/布尔/JSON)
296
+ - `delete <path>`:删除配置项
297
+ - `protect list`:查看受保护分支
298
+ - `protect add <branch...>`:添加受保护分支
299
+ - `protect remove <branch...>`:移除受保护分支
300
+ - `ai <subcommand>`:AI profile 管理(见上文“AI 能力配置”)
301
+ - `--repo-mode auto|strict`:仓库模式(仅影响 protect)
302
+ - `-h, --help`:查看帮助
303
+
304
+ ### info
305
+
306
+ ```bash
307
+ icode info
308
+ ```
309
+
310
+ 输出 Git 版本、仓库根路径、当前分支、hook/husky 状态、受保护分支等信息。
311
+
312
+ ## 复杂场景支持
313
+
314
+ ### 1) Husky / Git Hook 校验
315
+
316
+ 当 `commit`/`push` 被 hooks 拦截时,会明确提示:
317
+
318
+ - 当前被 hook 拦截
319
+ - 可使用 `--no-verify` 进行跳过
320
+
321
+ 示例:
322
+
323
+ ```bash
324
+ icode push -m "chore: hotfix" --no-verify
325
+ ```
326
+
327
+ ### 2) 继承父级 Git 仓库
328
+
329
+ 当你在子目录执行命令,而 `.git` 在父目录:
330
+
331
+ - 默认 `--repo-mode auto`:自动定位到父仓库根目录执行
332
+ - `--repo-mode strict`:检测到继承时直接阻断,避免误操作
333
+
334
+ ## 开发
335
+
336
+ ```bash
337
+ npm test
338
+ node bin/icode.js help
339
+ node bin/icode.js undo
340
+ ```
341
+
342
+ ## 迁移说明(v2 -> v3)
343
+
344
+ - 移除了旧版远程平台 API 合并流程(GitHub/GitLab/Gitee Token 管理)
345
+ - 统一聚焦本地 Git 工作流自动化
346
+ - 部分旧参数仍做兼容映射(如 `-pm`)
package/bin/icode.js ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ import('../src/cli.js').catch((error) => {
3
+ const message = error?.message || String(error)
4
+ console.error(`\x1b[31m[icode] ${message}\x1b[0m`)
5
+ process.exit(error?.exitCode || 1)
6
+ })
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@icode-js/icode",
3
+ "version": "3.0.2",
4
+ "description": "Modern git workflow CLI with robust parent-repo and hook handling",
5
+ "type": "module",
6
+ "author": "C",
7
+ "bin": {
8
+ "icode": "./bin/icode.js"
9
+ },
10
+ "scripts": {
11
+ "test": "node --test",
12
+ "check": "node --check src/cli.js"
13
+ },
14
+ "keywords": [
15
+ "git",
16
+ "cli",
17
+ "workflow",
18
+ "husky"
19
+ ],
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "https://github.com/wandou-cc/icode-js.git"
23
+ },
24
+ "homepage": "https://github.com/wandou-cc/icode-js",
25
+ "license": "MIT",
26
+ "engines": {
27
+ "node": ">=20.10.0"
28
+ },
29
+ "files": [
30
+ "bin",
31
+ "src",
32
+ "README.md"
33
+ ]
34
+ }
package/src/cli.js ADDED
@@ -0,0 +1,131 @@
1
+ import { runAiCommand } from './commands/ai.js'
2
+ import { runCleanCommand } from './commands/clean.js'
3
+ import { runCodeReviewCommand } from './commands/codereview.js'
4
+ import { runCheckoutCommand } from './commands/checkout.js'
5
+ import { runConfigCommand } from './commands/config.js'
6
+ import { runExplainCommand } from './commands/explain.js'
7
+ import { printMainHelp } from './commands/help.js'
8
+ import { runInfoCommand } from './commands/info.js'
9
+ import { runMigrateCommand } from './commands/migrate.js'
10
+ import { runPushCommand } from './commands/push.js'
11
+ import { runSyncCommand } from './commands/sync.js'
12
+ import { runTagCommand } from './commands/tag.js'
13
+ import { runUndoCommand } from './commands/undo.js'
14
+ import { asIcodeError } from './core/errors.js'
15
+ import { logger } from './core/logger.js'
16
+ import { normalizeLegacyArgs } from './core/args.js'
17
+
18
+ function isTruthy(value) {
19
+ const normalized = String(value || '').trim().toLowerCase()
20
+ return ['1', 'true', 'yes', 'y', 'on'].includes(normalized)
21
+ }
22
+
23
+ function serializeErrorMeta(meta) {
24
+ try {
25
+ return JSON.stringify(meta, null, 2)
26
+ } catch {
27
+ return String(meta)
28
+ }
29
+ }
30
+
31
+ const COMMANDS = {
32
+ ai: runAiCommand,
33
+ codereview: runCodeReviewCommand,
34
+ checkout: runCheckoutCommand,
35
+ push: runPushCommand,
36
+ sync: runSyncCommand,
37
+ clean: runCleanCommand,
38
+ undo: runUndoCommand,
39
+ migrate: runMigrateCommand,
40
+ tag: runTagCommand,
41
+ config: runConfigCommand,
42
+ explain: runExplainCommand,
43
+ info: runInfoCommand,
44
+ help: async () => {
45
+ printMainHelp()
46
+ }
47
+ }
48
+
49
+ function parseEntryArgs(argv) {
50
+ const args = normalizeLegacyArgs(argv)
51
+ const firstCommandIndex = args.findIndex((arg) => !arg.startsWith('-'))
52
+
53
+ const globalArgs = firstCommandIndex === -1 ? args : args.slice(0, firstCommandIndex)
54
+ const commandName = firstCommandIndex === -1 ? null : args[firstCommandIndex]
55
+ const commandArgs = firstCommandIndex === -1 ? [] : args.slice(firstCommandIndex + 1)
56
+
57
+ return {
58
+ globalArgs,
59
+ commandName,
60
+ commandArgs
61
+ }
62
+ }
63
+
64
+ function applyGlobalFlags(globalArgs) {
65
+ for (const flag of globalArgs) {
66
+ if (flag === '-d' || flag === '--debug') {
67
+ logger.setVerbose(true)
68
+ process.env.ICODE_DEBUG = '1'
69
+ continue
70
+ }
71
+
72
+ if (flag === '-h' || flag === '--help') {
73
+ printMainHelp()
74
+ return {
75
+ shouldExit: true,
76
+ exitCode: 0
77
+ }
78
+ }
79
+
80
+ throw new Error(`未知全局参数: ${flag}`)
81
+ }
82
+
83
+ return {
84
+ shouldExit: false,
85
+ exitCode: 0
86
+ }
87
+ }
88
+
89
+ async function main() {
90
+ const { globalArgs, commandName, commandArgs } = parseEntryArgs(process.argv.slice(2))
91
+ const globalResult = applyGlobalFlags(globalArgs)
92
+
93
+ if (globalResult.shouldExit) {
94
+ process.exit(globalResult.exitCode)
95
+ }
96
+
97
+ if (!commandName) {
98
+ printMainHelp()
99
+ process.exit(0)
100
+ }
101
+
102
+ const command = COMMANDS[commandName]
103
+ if (!command) {
104
+ throw new Error(`未知命令: ${commandName}`)
105
+ }
106
+
107
+ await command(commandArgs)
108
+ }
109
+
110
+ main().catch((error) => {
111
+ const normalized = asIcodeError(error)
112
+ logger.error(normalized.message)
113
+
114
+ if (normalized.code === 'AI_EMPTY_RESPONSE') {
115
+ if (normalized.meta?.thinkingPreview) {
116
+ logger.warn('检测到模型返回了思考过程,但没有可展示的最终内容。默认不会输出思考过程。')
117
+ }
118
+ if (normalized.meta?.hint) {
119
+ logger.warn(normalized.meta.hint)
120
+ }
121
+ if (normalized.meta?.rawResponse) {
122
+ logger.warn('如需排查原始响应,请使用 `--dump-response` 或设置 `ICODE_AI_DUMP_RESPONSE=1`。')
123
+ }
124
+ }
125
+
126
+ if ((process.env.ICODE_DEBUG === '1' || isTruthy(process.env.ICODE_AI_DUMP_RESPONSE)) && normalized.meta && Object.keys(normalized.meta).length) {
127
+ process.stderr.write(`[icode:debug] error meta:\n${serializeErrorMeta(normalized.meta)}\n`)
128
+ }
129
+
130
+ process.exit(normalized.exitCode || 1)
131
+ })