@icode-js/icode 3.2.0 → 3.4.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 (75) hide show
  1. package/README.md +287 -24
  2. package/bin/icode.js +10 -0
  3. package/package.json +3 -3
  4. package/src/cli.js +175 -8
  5. package/src/commands/checkout.js +73 -6
  6. package/src/commands/clean.js +47 -7
  7. package/src/commands/config-ux.js +292 -0
  8. package/src/commands/config.js +199 -48
  9. package/src/commands/continue.js +104 -0
  10. package/src/commands/doctor.js +15 -29
  11. package/src/commands/help.js +54 -34
  12. package/src/commands/history.js +147 -0
  13. package/src/commands/info.js +39 -4
  14. package/src/commands/log.js +142 -0
  15. package/src/commands/migrate.js +19 -12
  16. package/src/commands/open.js +138 -0
  17. package/src/commands/pr.js +219 -0
  18. package/src/commands/push.js +62 -22
  19. package/src/commands/ready.js +18 -48
  20. package/src/commands/release.js +112 -0
  21. package/src/commands/rescue.js +164 -0
  22. package/src/commands/save.js +52 -11
  23. package/src/commands/stash.js +210 -0
  24. package/src/commands/sw.js +89 -0
  25. package/src/commands/sync.js +19 -2
  26. package/src/commands/tag.js +40 -1
  27. package/src/commands/undo.js +9 -3
  28. package/src/core/ai/diff-range.js +1 -1
  29. package/src/core/cli/completion.js +237 -1
  30. package/src/core/cli/output.js +46 -0
  31. package/src/core/cli/render.js +121 -0
  32. package/src/core/commands/shell.js +9 -1
  33. package/src/core/config/repo-config.js +93 -0
  34. package/src/core/config/store.js +60 -10
  35. package/src/core/git/autostash.js +46 -0
  36. package/src/core/git/backup.js +170 -0
  37. package/src/core/git/conflicts.js +58 -0
  38. package/src/core/git/context.js +72 -5
  39. package/src/core/git/file-guard.js +193 -0
  40. package/src/core/git/operation-state.js +63 -0
  41. package/src/core/git/operations.js +32 -0
  42. package/src/core/git/semver.js +194 -0
  43. package/src/core/git/service.js +454 -24
  44. package/src/core/platform/http.js +53 -0
  45. package/src/core/platform/index.js +165 -0
  46. package/src/core/platform/providers.js +454 -0
  47. package/src/core/platform/remote-url.js +92 -0
  48. package/src/core/remote-merge/client.js +33 -3
  49. package/src/core/tools/browser.js +28 -0
  50. package/src/core/tools/commit-prompt.js +137 -0
  51. package/src/core/tools/interactive.js +272 -40
  52. package/src/core/tools/logger.js +33 -23
  53. package/src/core/tools/theme.js +228 -0
  54. package/src/workflows/ai-commit-workflow.js +52 -20
  55. package/src/workflows/checkout-workflow.js +24 -10
  56. package/src/workflows/clean-workflow.js +135 -83
  57. package/src/workflows/continue-workflow.js +233 -0
  58. package/src/workflows/doctor-workflow.js +10 -7
  59. package/src/workflows/history-workflow.js +211 -0
  60. package/src/workflows/migrate-workflow.js +39 -14
  61. package/src/workflows/pr-workflow.js +252 -0
  62. package/src/workflows/push/commit-step.js +123 -0
  63. package/src/workflows/push/local-merge-step.js +213 -0
  64. package/src/workflows/push/plan.js +63 -0
  65. package/src/workflows/push/remote-merge-step.js +224 -0
  66. package/src/workflows/push-workflow.js +185 -492
  67. package/src/workflows/ready-workflow.js +14 -10
  68. package/src/workflows/release-workflow.js +189 -0
  69. package/src/workflows/rescue-workflow.js +137 -0
  70. package/src/workflows/save-workflow.js +106 -41
  71. package/src/workflows/switch-workflow.js +171 -0
  72. package/src/workflows/sync-workflow.js +143 -44
  73. package/src/workflows/tag-workflow.js +103 -17
  74. package/src/workflows/undo-workflow.js +113 -34
  75. package/src/workflows/rollback-workflow.js +0 -99
@@ -1,9 +1,11 @@
1
1
  import { IcodeError } from '../core/errors.js'
2
+ import { shortenHome } from '../core/tools/theme.js'
3
+ import { createBackup } from '../core/git/backup.js'
4
+ import { runAbortWorkflow, runContinueWorkflow } from './continue-workflow.js'
2
5
  import { resolveGitContext } from '../core/git/context.js'
3
6
  import { GitService } from '../core/git/service.js'
4
- import { chooseOne } from '../core/tools/interactive.js'
7
+ import { chooseOne, confirm } from '../core/tools/interactive.js'
5
8
  import { logger } from '../core/tools/logger.js'
6
- import { runRollbackWorkflow } from './rollback-workflow.js'
7
9
 
8
10
  const UNDO_OPTIONS = [
9
11
  {
@@ -42,6 +44,105 @@ function logWarn(options, message) {
42
44
  }
43
45
  }
44
46
 
47
+ // 静默模式下输出 info 日志,输入执行选项和日志文本,输出为空。
48
+ function logInfo(options, message) {
49
+ if (!options.silentLog) {
50
+ logger.info(message)
51
+ }
52
+ }
53
+
54
+ function isRevertConflict(error) {
55
+ const output = `${error?.meta?.stdout || ''}\n${error?.meta?.stderr || ''}\n${error?.message || ''}`
56
+ return /revert is already in progress|could not revert|after resolving the conflicts|CONFLICT \(/i.test(output)
57
+ }
58
+
59
+ /**
60
+ * 执行底层回滚:revert 生成新提交;soft/mixed/hard 移动 HEAD。
61
+ * reset 前会自动创建恢复点(hard 模式还会备份未提交改动),可用 icode rescue 找回。
62
+ */
63
+ async function performRollback(options) {
64
+ const mode = options.mode || 'revert'
65
+ const ref = options.ref || (mode === 'revert' ? 'HEAD' : 'HEAD~1')
66
+
67
+ const context = await resolveGitContext({
68
+ cwd: options.cwd,
69
+ repoMode: options.repoMode
70
+ })
71
+
72
+ const git = new GitService(context)
73
+
74
+ logInfo(options, `仓库 ${shortenHome(context.topLevelPath)}`)
75
+ if (context.inheritedFromParent) {
76
+ logWarn(options, '当前目录继承了父级 Git 仓库,命令将基于父仓库根目录执行。')
77
+ }
78
+
79
+ if (mode === 'revert') {
80
+ logInfo(options, `执行回滚(revert): ${ref}`)
81
+ try {
82
+ await git.revert(ref)
83
+ } catch (error) {
84
+ if (isRevertConflict(error)) {
85
+ throw new IcodeError(
86
+ 'revert 发生冲突。解决冲突后执行 `icode continue`,或执行 `icode abort` 直接中止。',
87
+ {
88
+ code: 'REVERT_CONFLICT',
89
+ cause: error,
90
+ meta: error.meta
91
+ }
92
+ )
93
+ }
94
+ throw error
95
+ }
96
+ return {
97
+ mode,
98
+ ref,
99
+ repoRoot: context.topLevelPath
100
+ }
101
+ }
102
+
103
+ if (!['soft', 'mixed', 'hard'].includes(mode)) {
104
+ throw new IcodeError('mode 仅支持: revert | soft | mixed | hard', {
105
+ code: 'ROLLBACK_MODE_INVALID',
106
+ exitCode: 2
107
+ })
108
+ }
109
+
110
+ if (mode === 'hard' && !options.yes) {
111
+ // hard reset 会直接丢弃工作区改动,这里强制确认一次降低误操作风险。
112
+ const accepted = await confirm(
113
+ `你将执行 git reset --hard ${ref},这会丢弃未提交改动(已自动备份已跟踪文件的改动),是否继续?`,
114
+ false
115
+ )
116
+ if (!accepted) {
117
+ logWarn(options, '已取消 hard 回滚。')
118
+ return {
119
+ canceled: true,
120
+ mode,
121
+ ref,
122
+ repoRoot: context.topLevelPath
123
+ }
124
+ }
125
+ }
126
+
127
+ const backup = await createBackup(git, {
128
+ reason: `undo reset --${mode} ${ref}`,
129
+ includeWorktree: mode === 'hard',
130
+ silent: options.silentLog
131
+ })
132
+
133
+ logInfo(options, `执行回滚(reset --${mode}): ${ref}`)
134
+ await git.reset(mode, ref)
135
+
136
+ return {
137
+ mode,
138
+ ref,
139
+ backupId: backup.entries.length ? backup.id : '',
140
+ repoRoot: context.topLevelPath
141
+ }
142
+ }
143
+
144
+
145
+
45
146
  // 解析交互菜单选择,输入菜单 value,输出 mode/ref 或 canceled。
46
147
  function parseSelection(selection) {
47
148
  if (!selection || selection === 'cancel') {
@@ -172,43 +273,21 @@ async function resolvePendingOperation(options) {
172
273
  }
173
274
  }
174
275
 
276
+ // 与 icode continue / icode abort 走同一套流程:自动暂存已解决文件,并续做被打断的 push/migrate/sync。
175
277
  if (action === 'continue') {
176
- try {
177
- if (pending === 'revert') {
178
- await git.revertContinue()
179
- } else if (pending === 'merge') {
180
- await git.mergeContinue()
181
- } else {
182
- await git.cherryPickContinue()
183
- }
184
-
185
- return {
186
- resolvedOperation: pending,
187
- recoverAction: 'continue'
188
- }
189
- } catch (error) {
190
- throw new IcodeError(
191
- `${pending} --continue 失败,请先解决冲突后重试,或使用 --recover abort。`,
192
- {
193
- code: 'UNDO_CONTINUE_FAILED',
194
- cause: error,
195
- meta: error.meta
196
- }
197
- )
278
+ const result = await runContinueWorkflow({ cwd: options.cwd, repoMode: options.repoMode })
279
+ return {
280
+ resolvedOperation: pending,
281
+ recoverAction: 'continue',
282
+ ...(result.resumed ? { resumed: result.resumed, summary: result.summary } : {})
198
283
  }
199
284
  }
200
285
 
201
- if (pending === 'revert') {
202
- await git.revertAbort()
203
- } else if (pending === 'merge') {
204
- await git.mergeAbort()
205
- } else {
206
- await git.abortCherryPick()
207
- }
208
-
286
+ const result = await runAbortWorkflow({ cwd: options.cwd, repoMode: options.repoMode })
209
287
  return {
210
288
  resolvedOperation: pending,
211
- recoverAction: 'abort'
289
+ recoverAction: 'abort',
290
+ ...(result.aborted ? { aborted: result.aborted, returnedTo: result.returnedTo } : {})
212
291
  }
213
292
  }
214
293
 
@@ -334,7 +413,7 @@ export async function runUndoWorkflow(options) {
334
413
  }
335
414
 
336
415
  try {
337
- return await runRollbackWorkflow({
416
+ return await performRollback({
338
417
  ref,
339
418
  mode,
340
419
  yes: options.yes,
@@ -1,99 +0,0 @@
1
- import { IcodeError } from '../core/errors.js'
2
- import { resolveGitContext } from '../core/git/context.js'
3
- import { GitService } from '../core/git/service.js'
4
- import { confirm } from '../core/tools/interactive.js'
5
- import { logger } from '../core/tools/logger.js'
6
-
7
- function isRevertConflict(error) {
8
- const output = `${error?.meta?.stdout || ''}\n${error?.meta?.stderr || ''}\n${error?.message || ''}`
9
- return /revert is already in progress|could not revert|after resolving the conflicts|CONFLICT \(/i.test(output)
10
- }
11
-
12
- // 静默模式下输出 info 日志,输入执行选项和日志文本,输出为空。
13
- function logInfo(options, message) {
14
- if (!options.silentLog) {
15
- logger.info(message)
16
- }
17
- }
18
-
19
- // 静默模式下输出 warn 日志,输入执行选项和日志文本,输出为空。
20
- function logWarn(options, message) {
21
- if (!options.silentLog) {
22
- logger.warn(message)
23
- }
24
- }
25
-
26
- // 执行底层回滚操作,输入 mode/ref/仓库选项,输出执行结果。
27
- export async function runRollbackWorkflow(options) {
28
- const mode = options.mode || 'revert'
29
- const ref = options.ref || (mode === 'revert' ? 'HEAD' : 'HEAD~1')
30
-
31
- const context = await resolveGitContext({
32
- cwd: options.cwd,
33
- repoMode: options.repoMode
34
- })
35
-
36
- const git = new GitService(context)
37
-
38
- logInfo(options, `仓库根目录: ${context.topLevelPath}`)
39
- if (context.inheritedFromParent) {
40
- logWarn(options, '当前目录继承了父级 Git 仓库,命令将基于父仓库根目录执行。')
41
- }
42
-
43
- if (mode === 'revert') {
44
- logInfo(options, `执行回滚(revert): ${ref}`)
45
- try {
46
- await git.revert(ref)
47
- } catch (error) {
48
- if (isRevertConflict(error)) {
49
- throw new IcodeError(
50
- 'revert 发生冲突。请解决冲突后执行 `git revert --continue`,或使用 `icode undo --recover abort` 直接中止。',
51
- {
52
- code: 'REVERT_CONFLICT',
53
- cause: error,
54
- meta: error.meta
55
- }
56
- )
57
- }
58
- throw error
59
- }
60
- return {
61
- mode,
62
- ref,
63
- repoRoot: context.topLevelPath
64
- }
65
- }
66
-
67
- if (!['soft', 'mixed', 'hard'].includes(mode)) {
68
- throw new IcodeError('mode 仅支持: revert | soft | mixed | hard', {
69
- code: 'ROLLBACK_MODE_INVALID',
70
- exitCode: 2
71
- })
72
- }
73
-
74
- if (mode === 'hard' && !options.yes) {
75
- // hard reset 会直接丢弃工作区改动,这里强制确认一次降低误操作风险。
76
- const accepted = await confirm(
77
- `你将执行 git reset --hard ${ref},这会丢失未提交改动,是否继续?`,
78
- false
79
- )
80
- if (!accepted) {
81
- logWarn(options, '已取消 hard 回滚。')
82
- return {
83
- canceled: true,
84
- mode,
85
- ref,
86
- repoRoot: context.topLevelPath
87
- }
88
- }
89
- }
90
-
91
- logInfo(options, `执行回滚(reset --${mode}): ${ref}`)
92
- await git.reset(mode, ref)
93
-
94
- return {
95
- mode,
96
- ref,
97
- repoRoot: context.topLevelPath
98
- }
99
- }