@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
@@ -0,0 +1,287 @@
1
+ import { parseArgs } from 'node:util'
2
+ import { normalizeLegacyArgs } from '../core/args.js'
3
+ import { getAiCommandOptions } from '../core/ai-config.js'
4
+ import { formatAiCommitSummary } from '../core/ai-commit-summary.js'
5
+ import { IcodeError } from '../core/errors.js'
6
+ import { logger } from '../core/logger.js'
7
+ import { runAiCodeReviewWorkflow } from '../workflows/ai-codereview-workflow.js'
8
+ import { runAiCommitWorkflow } from '../workflows/ai-commit-workflow.js'
9
+ import { runAiConflictWorkflow } from '../workflows/ai-conflict-workflow.js'
10
+ import { runAiExplainWorkflow } from '../workflows/ai-explain-workflow.js'
11
+
12
+ function printMainHelp() {
13
+ process.stdout.write(`
14
+ Usage:
15
+ icode ai <subcommand> [options]
16
+
17
+ Subcommands:
18
+ commit AI 生成提交信息(会参考本地 hook/commitlint 规范)
19
+ conflict AI 冲突解决建议
20
+ codereview AI 代码评审
21
+ explain AI 解释 Git diff
22
+
23
+ Tips:
24
+ icode ai <subcommand> -h 查看子命令参数说明
25
+
26
+ Examples:
27
+ icode ai commit --apply -y
28
+ icode ai conflict
29
+ icode ai codereview --base origin/main --head HEAD
30
+ icode ai explain --base origin/main --head HEAD
31
+ `)
32
+ }
33
+
34
+ function printCommitHelp() {
35
+ process.stdout.write(`
36
+ Usage:
37
+ icode ai commit [options]
38
+
39
+ Options:
40
+ --apply 直接使用 AI 信息执行 commit
41
+ --lang <zh|en> 输出语言,默认 zh
42
+ --profile <name> 指定 AI profile
43
+ --repo-mode <mode> 仓库模式: auto(自动继承父仓库) | strict(禁止继承)
44
+ --no-verify commit 时跳过 hook/husky 校验
45
+ -y, --yes 自动确认(跳过确认提示)
46
+ -h, --help 查看帮助
47
+ `)
48
+ }
49
+
50
+ function printConflictHelp() {
51
+ process.stdout.write(`
52
+ Usage:
53
+ icode ai conflict [options]
54
+
55
+ Options:
56
+ --profile <name> 指定 AI profile
57
+ --repo-mode <mode> 仓库模式: auto(自动继承父仓库) | strict(禁止继承)
58
+ -h, --help 查看帮助
59
+ `)
60
+ }
61
+
62
+ function printCodeReviewHelp() {
63
+ process.stdout.write(`
64
+ Usage:
65
+ icode ai codereview [options]
66
+
67
+ Options:
68
+ --base <ref> 指定分支 diff 基线;未传时默认评审暂存区 + 工作区改动
69
+ --head <ref> 指定分支 diff 终点,默认 HEAD
70
+ --focus <text> 评审重点(安全/性能/测试等)
71
+ --profile <name> 指定 AI profile
72
+ --repo-mode <mode> 仓库模式: auto(自动继承父仓库) | strict(禁止继承)
73
+ --dump-response 输出 AI 原始响应(调试数据格式)
74
+ -h, --help 查看帮助
75
+ `)
76
+ }
77
+
78
+ function printExplainHelp() {
79
+ process.stdout.write(`
80
+ Usage:
81
+ icode ai explain [options]
82
+
83
+ Options:
84
+ --base <ref> diff 基线,默认 origin/<defaultBranch>;不可用时回退到本地默认分支
85
+ --head <ref> diff 终点,默认 HEAD
86
+ --profile <name> 指定 AI profile
87
+ --repo-mode <mode> 仓库模式: auto(自动继承父仓库) | strict(禁止继承)
88
+ --dump-response 输出 AI 原始响应(调试数据格式)
89
+ -h, --help 查看帮助
90
+ `)
91
+ }
92
+
93
+ function resolveBooleanOption(cliValue, configValue, fallback = false) {
94
+ if (typeof cliValue === 'boolean') {
95
+ return cliValue
96
+ }
97
+ if (typeof configValue === 'boolean') {
98
+ return configValue
99
+ }
100
+ if (typeof configValue === 'string') {
101
+ const normalized = configValue.trim().toLowerCase()
102
+ if (['true', '1', 'yes', 'y', 'on'].includes(normalized)) {
103
+ return true
104
+ }
105
+ if (['false', '0', 'no', 'n', 'off'].includes(normalized)) {
106
+ return false
107
+ }
108
+ }
109
+ return fallback
110
+ }
111
+
112
+ function resolveStringOption(cliValue, configValue, fallback = '') {
113
+ if (typeof cliValue === 'string' && cliValue.trim()) {
114
+ return cliValue
115
+ }
116
+ if (typeof configValue === 'string' && configValue.trim()) {
117
+ return configValue
118
+ }
119
+ return fallback
120
+ }
121
+
122
+ // Execute `icode ai commit` and surface the full applied commit message in logs.
123
+ async function runCommitSubcommand(rawArgs) {
124
+ const scopedOptions = getAiCommandOptions('commit')
125
+ const parsed = parseArgs({
126
+ args: rawArgs,
127
+ allowPositionals: true,
128
+ options: {
129
+ apply: { type: 'boolean' },
130
+ lang: { type: 'string' },
131
+ profile: { type: 'string' },
132
+ 'repo-mode': { type: 'string' },
133
+ 'no-verify': { type: 'boolean' },
134
+ yes: { type: 'boolean', short: 'y' },
135
+ help: { type: 'boolean', short: 'h' }
136
+ }
137
+ })
138
+
139
+ if (parsed.values.help) {
140
+ printCommitHelp()
141
+ return
142
+ }
143
+
144
+ const result = await runAiCommitWorkflow({
145
+ apply: resolveBooleanOption(parsed.values.apply, scopedOptions.apply, false),
146
+ lang: resolveStringOption(parsed.values.lang, scopedOptions.lang, 'zh'),
147
+ profile: resolveStringOption(parsed.values.profile, scopedOptions.profile, ''),
148
+ repoMode: resolveStringOption(parsed.values['repo-mode'], scopedOptions.repoMode, 'auto'),
149
+ noVerify: resolveBooleanOption(parsed.values['no-verify'], scopedOptions.noVerify, false),
150
+ yes: resolveBooleanOption(parsed.values.yes, scopedOptions.yes, false)
151
+ })
152
+
153
+ if (result.canceled) {
154
+ return
155
+ }
156
+
157
+ if (result.applied) {
158
+ logger.success(`AI commit 已应用:\n${formatAiCommitSummary(result.commitId, result.commitMessage)}`)
159
+ }
160
+ }
161
+
162
+ async function runConflictSubcommand(rawArgs) {
163
+ const scopedOptions = getAiCommandOptions('conflict')
164
+ const parsed = parseArgs({
165
+ args: rawArgs,
166
+ allowPositionals: true,
167
+ options: {
168
+ profile: { type: 'string' },
169
+ 'repo-mode': { type: 'string' },
170
+ help: { type: 'boolean', short: 'h' }
171
+ }
172
+ })
173
+
174
+ if (parsed.values.help) {
175
+ printConflictHelp()
176
+ return
177
+ }
178
+
179
+ const result = await runAiConflictWorkflow({
180
+ profile: resolveStringOption(parsed.values.profile, scopedOptions.profile, ''),
181
+ repoMode: resolveStringOption(parsed.values['repo-mode'], scopedOptions.repoMode, 'auto')
182
+ })
183
+
184
+ logger.info(`冲突文件: ${result.conflictedFiles.join(', ')}`)
185
+ process.stdout.write(`\n${result.suggestion}\n`)
186
+ }
187
+
188
+ async function runCodeReviewSubcommand(rawArgs) {
189
+ const parsed = parseArgs({
190
+ args: rawArgs,
191
+ allowPositionals: true,
192
+ options: {
193
+ base: { type: 'string' },
194
+ head: { type: 'string' },
195
+ focus: { type: 'string' },
196
+ profile: { type: 'string' },
197
+ 'repo-mode': { type: 'string' },
198
+ 'dump-response': { type: 'boolean' },
199
+ help: { type: 'boolean', short: 'h' }
200
+ }
201
+ })
202
+
203
+ if (parsed.values.help) {
204
+ printCodeReviewHelp()
205
+ return
206
+ }
207
+
208
+ const result = await runAiCodeReviewWorkflow({
209
+ baseRef: resolveStringOption(parsed.values.base, '', ''),
210
+ headRef: resolveStringOption(parsed.values.head, '', ''),
211
+ focus: resolveStringOption(parsed.values.focus, '', ''),
212
+ profile: resolveStringOption(parsed.values.profile, '', ''),
213
+ repoMode: resolveStringOption(parsed.values['repo-mode'], '', 'auto'),
214
+ dumpResponse: resolveBooleanOption(parsed.values['dump-response'], undefined, false)
215
+ })
216
+
217
+ logger.info(`Code Review 范围: ${result.rangeSpec}`)
218
+ process.stdout.write(`\n${result.review}\n`)
219
+ }
220
+
221
+ async function runExplainSubcommand(rawArgs) {
222
+ const scopedOptions = getAiCommandOptions('explain')
223
+ const parsed = parseArgs({
224
+ args: rawArgs,
225
+ allowPositionals: true,
226
+ options: {
227
+ base: { type: 'string' },
228
+ head: { type: 'string' },
229
+ profile: { type: 'string' },
230
+ 'repo-mode': { type: 'string' },
231
+ 'dump-response': { type: 'boolean' },
232
+ help: { type: 'boolean', short: 'h' }
233
+ }
234
+ })
235
+
236
+ if (parsed.values.help) {
237
+ printExplainHelp()
238
+ return
239
+ }
240
+
241
+ const result = await runAiExplainWorkflow({
242
+ baseRef: resolveStringOption(parsed.values.base, scopedOptions.base, ''),
243
+ headRef: resolveStringOption(parsed.values.head, scopedOptions.head, ''),
244
+ profile: resolveStringOption(parsed.values.profile, scopedOptions.profile, ''),
245
+ repoMode: resolveStringOption(parsed.values['repo-mode'], scopedOptions.repoMode, 'auto'),
246
+ dumpResponse: resolveBooleanOption(parsed.values['dump-response'], scopedOptions.dumpResponse, false)
247
+ })
248
+
249
+ logger.info(`Explain 范围: ${result.rangeSpec}`)
250
+ process.stdout.write(`\n${result.explanation}\n`)
251
+ }
252
+
253
+ export async function runAiCommand(rawArgs) {
254
+ const args = normalizeLegacyArgs(rawArgs)
255
+
256
+ if (!args.length || args[0] === '--help' || args[0] === '-h') {
257
+ printMainHelp()
258
+ return
259
+ }
260
+
261
+ const [subcommand, ...subcommandArgs] = args
262
+
263
+ if (subcommand === 'commit') {
264
+ await runCommitSubcommand(subcommandArgs)
265
+ return
266
+ }
267
+
268
+ if (subcommand === 'conflict') {
269
+ await runConflictSubcommand(subcommandArgs)
270
+ return
271
+ }
272
+
273
+ if (subcommand === 'codereview' || subcommand === 'review') {
274
+ await runCodeReviewSubcommand(subcommandArgs)
275
+ return
276
+ }
277
+
278
+ if (subcommand === 'explain') {
279
+ await runExplainSubcommand(subcommandArgs)
280
+ return
281
+ }
282
+
283
+ throw new IcodeError(`未知 ai 子命令: ${subcommand}`, {
284
+ code: 'AI_SUBCOMMAND_UNKNOWN',
285
+ exitCode: 2
286
+ })
287
+ }
@@ -0,0 +1,59 @@
1
+ import { parseArgs } from 'node:util'
2
+ import { normalizeLegacyArgs } from '../core/args.js'
3
+ import { logger } from '../core/logger.js'
4
+ import { runCheckoutWorkflow } from '../workflows/checkout-workflow.js'
5
+
6
+ function printHelp() {
7
+ process.stdout.write(`
8
+ Usage:
9
+ icode checkout <branch> [base] [options]
10
+
11
+ Arguments:
12
+ <branch> 目标分支名(必填)
13
+ [base] 新建分支基线,默认主分支
14
+
15
+ Options:
16
+ -y, --yes 自动确认(跳过交互提示)
17
+ --push-origin 新建分支后立即推送到 origin
18
+ --pull-main 切换后同步主分支到当前分支
19
+ --repo-mode <mode> 仓库模式: auto(自动继承父仓库) | strict(禁止继承)
20
+ --no-verify 跳过 hook/husky 校验
21
+ -h, --help 查看帮助
22
+
23
+ Notes:
24
+ 若本地与远程都不存在,会从基线分支创建。
25
+ `)
26
+ }
27
+
28
+ export async function runCheckoutCommand(rawArgs) {
29
+ const args = normalizeLegacyArgs(rawArgs)
30
+ const parsed = parseArgs({
31
+ args,
32
+ allowPositionals: true,
33
+ options: {
34
+ yes: { type: 'boolean', short: 'y', default: false },
35
+ help: { type: 'boolean', short: 'h', default: false },
36
+ 'push-origin': { type: 'boolean', default: false },
37
+ 'pull-main': { type: 'boolean', default: false },
38
+ 'repo-mode': { type: 'string', default: 'auto' },
39
+ 'no-verify': { type: 'boolean', default: false }
40
+ }
41
+ })
42
+
43
+ if (parsed.values.help || parsed.positionals.length < 1) {
44
+ printHelp()
45
+ return
46
+ }
47
+
48
+ const [branchName, baseBranchName] = parsed.positionals
49
+ const result = await runCheckoutWorkflow({
50
+ branchName,
51
+ baseBranchName,
52
+ pushOrigin: parsed.values['push-origin'],
53
+ pullMain: parsed.values['pull-main'],
54
+ repoMode: parsed.values['repo-mode'],
55
+ noVerify: parsed.values['no-verify']
56
+ })
57
+
58
+ logger.success(`checkout 完成: ${result.branchName}`)
59
+ }
@@ -0,0 +1,65 @@
1
+ import { parseArgs } from 'node:util'
2
+ import { logger } from '../core/logger.js'
3
+ import { runCleanWorkflow } from '../workflows/clean-workflow.js'
4
+
5
+ function printHelp() {
6
+ process.stdout.write(`
7
+ Usage:
8
+ icode clean [options]
9
+
10
+ Options:
11
+ --merged-target <branch> 基于该分支判断“已合并”状态,默认主分支
12
+ --keep <branch|csv> 额外保留分支,可重复使用或逗号分隔
13
+ --remote 同时删除远程分支
14
+ --force 强制删除本地分支(-D)
15
+ -y, --yes 自动确认(跳过确认提示)
16
+ --repo-mode <mode> 仓库模式: auto(自动继承父仓库) | strict(禁止继承)
17
+ -h, --help 查看帮助
18
+
19
+ Notes:
20
+ 默认保护当前分支/主分支/受保护分支。
21
+
22
+ Examples:
23
+ icode clean
24
+ icode clean --merged-target main --keep release,hotfix
25
+ icode clean --remote --force -y
26
+ `)
27
+ }
28
+
29
+ export async function runCleanCommand(rawArgs) {
30
+ const parsed = parseArgs({
31
+ args: rawArgs,
32
+ allowPositionals: true,
33
+ options: {
34
+ 'merged-target': { type: 'string' },
35
+ keep: { type: 'string', multiple: true, default: [] },
36
+ remote: { type: 'boolean', default: false },
37
+ force: { type: 'boolean', default: false },
38
+ yes: { type: 'boolean', short: 'y', default: false },
39
+ 'repo-mode': { type: 'string', default: 'auto' },
40
+ help: { type: 'boolean', short: 'h', default: false }
41
+ }
42
+ })
43
+
44
+ if (parsed.values.help) {
45
+ printHelp()
46
+ return
47
+ }
48
+
49
+ const result = await runCleanWorkflow({
50
+ mergedTarget: parsed.values['merged-target'],
51
+ keep: parsed.values.keep,
52
+ remote: parsed.values.remote,
53
+ force: parsed.values.force,
54
+ yes: parsed.values.yes,
55
+ repoMode: parsed.values['repo-mode']
56
+ })
57
+
58
+ if (result.canceled) {
59
+ return
60
+ }
61
+
62
+ logger.success(
63
+ `clean 完成: 本地删除 ${result.deletedLocal.length} 个, 远程删除 ${result.deletedRemote.length} 个`
64
+ )
65
+ }
@@ -0,0 +1,52 @@
1
+ import { parseArgs } from 'node:util'
2
+ import { logger } from '../core/logger.js'
3
+ import { runAiCodeReviewWorkflow } from '../workflows/ai-codereview-workflow.js'
4
+
5
+ function printHelp() {
6
+ process.stdout.write(`
7
+ Usage:
8
+ icode codereview [options]
9
+
10
+ Options:
11
+ --base <ref> 指定分支 diff 基线;未传时默认评审暂存区 + 工作区改动
12
+ --head <ref> 指定分支 diff 终点,默认 HEAD
13
+ --focus <text> 评审重点(安全/性能/测试等)
14
+ --profile <name> 指定 AI profile
15
+ --repo-mode <mode> 仓库模式: auto(自动继承父仓库) | strict(禁止继承)
16
+ --dump-response 输出 AI 原始响应(调试数据格式)
17
+ -h, --help 查看帮助
18
+ `)
19
+ }
20
+
21
+ export async function runCodeReviewCommand(rawArgs) {
22
+ const parsed = parseArgs({
23
+ args: rawArgs,
24
+ allowPositionals: true,
25
+ options: {
26
+ base: { type: 'string' },
27
+ head: { type: 'string' },
28
+ focus: { type: 'string' },
29
+ profile: { type: 'string' },
30
+ 'repo-mode': { type: 'string' },
31
+ 'dump-response': { type: 'boolean' },
32
+ help: { type: 'boolean', short: 'h' }
33
+ }
34
+ })
35
+
36
+ if (parsed.values.help) {
37
+ printHelp()
38
+ return
39
+ }
40
+
41
+ const result = await runAiCodeReviewWorkflow({
42
+ baseRef: typeof parsed.values.base === 'string' ? parsed.values.base : '',
43
+ headRef: typeof parsed.values.head === 'string' ? parsed.values.head : '',
44
+ focus: typeof parsed.values.focus === 'string' ? parsed.values.focus : '',
45
+ profile: typeof parsed.values.profile === 'string' ? parsed.values.profile : '',
46
+ repoMode: typeof parsed.values['repo-mode'] === 'string' ? parsed.values['repo-mode'] : 'auto',
47
+ dumpResponse: parsed.values['dump-response'] === true
48
+ })
49
+
50
+ logger.info(`Code Review 范围: ${result.rangeSpec}`)
51
+ process.stdout.write(`\n${result.review}\n`)
52
+ }