@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.
- package/README.md +287 -24
- package/bin/icode.js +10 -0
- package/package.json +3 -3
- package/src/cli.js +175 -8
- package/src/commands/checkout.js +73 -6
- package/src/commands/clean.js +47 -7
- package/src/commands/config-ux.js +292 -0
- package/src/commands/config.js +199 -48
- package/src/commands/continue.js +104 -0
- package/src/commands/doctor.js +15 -29
- package/src/commands/help.js +54 -34
- package/src/commands/history.js +147 -0
- package/src/commands/info.js +39 -4
- package/src/commands/log.js +142 -0
- package/src/commands/migrate.js +19 -12
- package/src/commands/open.js +138 -0
- package/src/commands/pr.js +219 -0
- package/src/commands/push.js +62 -22
- package/src/commands/ready.js +18 -48
- package/src/commands/release.js +112 -0
- package/src/commands/rescue.js +164 -0
- package/src/commands/save.js +52 -11
- package/src/commands/stash.js +210 -0
- package/src/commands/sw.js +89 -0
- package/src/commands/sync.js +19 -2
- package/src/commands/tag.js +40 -1
- package/src/commands/undo.js +9 -3
- package/src/core/ai/diff-range.js +1 -1
- package/src/core/cli/completion.js +237 -1
- package/src/core/cli/output.js +46 -0
- package/src/core/cli/render.js +121 -0
- package/src/core/commands/shell.js +9 -1
- package/src/core/config/repo-config.js +93 -0
- package/src/core/config/store.js +60 -10
- package/src/core/git/autostash.js +46 -0
- package/src/core/git/backup.js +170 -0
- package/src/core/git/conflicts.js +58 -0
- package/src/core/git/context.js +72 -5
- package/src/core/git/file-guard.js +193 -0
- package/src/core/git/operation-state.js +63 -0
- package/src/core/git/operations.js +32 -0
- package/src/core/git/semver.js +194 -0
- package/src/core/git/service.js +454 -24
- package/src/core/platform/http.js +53 -0
- package/src/core/platform/index.js +165 -0
- package/src/core/platform/providers.js +454 -0
- package/src/core/platform/remote-url.js +92 -0
- package/src/core/remote-merge/client.js +33 -3
- package/src/core/tools/browser.js +28 -0
- package/src/core/tools/commit-prompt.js +137 -0
- package/src/core/tools/interactive.js +272 -40
- package/src/core/tools/logger.js +33 -23
- package/src/core/tools/theme.js +228 -0
- package/src/workflows/ai-commit-workflow.js +52 -20
- package/src/workflows/checkout-workflow.js +24 -10
- package/src/workflows/clean-workflow.js +135 -83
- package/src/workflows/continue-workflow.js +233 -0
- package/src/workflows/doctor-workflow.js +10 -7
- package/src/workflows/history-workflow.js +211 -0
- package/src/workflows/migrate-workflow.js +39 -14
- package/src/workflows/pr-workflow.js +252 -0
- package/src/workflows/push/commit-step.js +123 -0
- package/src/workflows/push/local-merge-step.js +213 -0
- package/src/workflows/push/plan.js +63 -0
- package/src/workflows/push/remote-merge-step.js +224 -0
- package/src/workflows/push-workflow.js +185 -492
- package/src/workflows/ready-workflow.js +14 -10
- package/src/workflows/release-workflow.js +189 -0
- package/src/workflows/rescue-workflow.js +137 -0
- package/src/workflows/save-workflow.js +106 -41
- package/src/workflows/switch-workflow.js +171 -0
- package/src/workflows/sync-workflow.js +143 -44
- package/src/workflows/tag-workflow.js +103 -17
- package/src/workflows/undo-workflow.js +113 -34
- package/src/workflows/rollback-workflow.js +0 -99
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { parseCommandArgs, printJson } from '../core/cli/output.js'
|
|
2
|
+
import { c, print, table, ui } from '../core/cli/render.js'
|
|
3
|
+
import { resolveGitContext } from '../core/git/context.js'
|
|
4
|
+
import { GitService } from '../core/git/service.js'
|
|
5
|
+
import { logger } from '../core/tools/logger.js'
|
|
6
|
+
import { listSwitchCandidates, runSwitchWorkflow } from '../workflows/switch-workflow.js'
|
|
7
|
+
|
|
8
|
+
function printHelp() {
|
|
9
|
+
process.stdout.write(`
|
|
10
|
+
Usage:
|
|
11
|
+
icode sw [query] [options]
|
|
12
|
+
|
|
13
|
+
Arguments:
|
|
14
|
+
[query] 分支名或关键字(支持模糊匹配);"-" 切回上一个分支;不传则交互选择
|
|
15
|
+
|
|
16
|
+
Options:
|
|
17
|
+
-c, --create 新建分支并切换(基于当前分支,或 --from 指定)
|
|
18
|
+
--from <ref> 新建分支的基线
|
|
19
|
+
-a, --all 候选里包含仅存在于远程的分支(自动创建 tracking 分支)
|
|
20
|
+
--autostash 有冲突的未提交改动时自动 stash,切换后恢复
|
|
21
|
+
-l, --list 只列出分支(最近使用在前,含 ahead/behind)
|
|
22
|
+
--json 输出机器可读 JSON
|
|
23
|
+
-y, --yes 非交互:多个候选时直接报错
|
|
24
|
+
--repo-mode <mode> 仓库模式: auto | strict
|
|
25
|
+
-h, --help 查看帮助
|
|
26
|
+
|
|
27
|
+
Examples:
|
|
28
|
+
icode sw # 交互选择
|
|
29
|
+
icode sw login # 模糊匹配 feature/login
|
|
30
|
+
icode sw - # 切回上一个分支
|
|
31
|
+
icode sw -c fix/typo # 新建
|
|
32
|
+
icode sw -l
|
|
33
|
+
`)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function printList(list, currentBranch) {
|
|
37
|
+
print(table(list, [
|
|
38
|
+
{ key: 'name', label: '分支', format: (name, item) => `${name === currentBranch ? c.green('●') : ' '} ${name === currentBranch ? c.bold(ui.branch(name)) : ui.branch(name)}` },
|
|
39
|
+
{ key: 'ahead', label: '同步', format: (_, item) => [ui.sync(item.ahead, item.behind), item.gone ? c.red('远程已删') : '', item.kind === 'remote' ? c.blue('仅远程') : ''].filter(Boolean).join(' ') },
|
|
40
|
+
{ key: 'relativeDate', label: '最近提交', format: (value) => ui.time(value || '') },
|
|
41
|
+
{ key: 'subject', label: '', maxWidth: 60, format: (value) => ui.subject(value || '') }
|
|
42
|
+
]))
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export async function runSwCommand(rawArgs) {
|
|
46
|
+
const parsed = parseCommandArgs(rawArgs, {
|
|
47
|
+
create: { type: 'boolean', short: 'c', default: false },
|
|
48
|
+
from: { type: 'string' },
|
|
49
|
+
all: { type: 'boolean', short: 'a', default: false },
|
|
50
|
+
autostash: { type: 'boolean', default: false },
|
|
51
|
+
list: { type: 'boolean', short: 'l', default: false }
|
|
52
|
+
})
|
|
53
|
+
const { values, positionals } = parsed
|
|
54
|
+
|
|
55
|
+
if (values.help) {
|
|
56
|
+
printHelp()
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (values.list || (values.json && !positionals.length && !values.create)) {
|
|
61
|
+
const context = await resolveGitContext({ repoMode: values['repo-mode'] })
|
|
62
|
+
const git = new GitService(context)
|
|
63
|
+
const list = await listSwitchCandidates(git, { all: values.all })
|
|
64
|
+
if (values.json) {
|
|
65
|
+
printJson({ currentBranch: context.currentBranch, branches: list })
|
|
66
|
+
return list
|
|
67
|
+
}
|
|
68
|
+
printList(list, context.currentBranch)
|
|
69
|
+
return list
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const result = await runSwitchWorkflow({
|
|
73
|
+
query: positionals[0],
|
|
74
|
+
create: values.create,
|
|
75
|
+
from: values.from,
|
|
76
|
+
all: values.all,
|
|
77
|
+
autostash: values.autostash,
|
|
78
|
+
yes: values.yes,
|
|
79
|
+
repoMode: values['repo-mode']
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
if (values.json) {
|
|
83
|
+
printJson(result)
|
|
84
|
+
return result
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
logger.success(`已切换到 ${c.bold(ui.branch(result.branch))}${result.previousBranch ? c.gray(`(来自 ${result.previousBranch})`) : ''}`)
|
|
88
|
+
return result
|
|
89
|
+
}
|
package/src/commands/sync.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { parseArgs } from 'node:util'
|
|
2
2
|
import { normalizeLegacyArgs } from '../core/cli/args.js'
|
|
3
|
+
import { print, renderBranchResults } from '../core/cli/render.js'
|
|
3
4
|
import { logger } from '../core/tools/logger.js'
|
|
4
5
|
import { runSyncWorkflow } from '../workflows/sync-workflow.js'
|
|
5
6
|
|
|
@@ -16,6 +17,8 @@ Options:
|
|
|
16
17
|
--merge-main 同步后把主分支 merge 到目标分支
|
|
17
18
|
--rebase pull 时使用 rebase
|
|
18
19
|
--push 同步后自动 push
|
|
20
|
+
--autostash 有未提交改动时自动 stash,结束后恢复
|
|
21
|
+
--json 输出机器可读 JSON
|
|
19
22
|
-y, --yes 自动确认(跳过确认提示)
|
|
20
23
|
--repo-mode <mode> 仓库模式: auto(自动继承父仓库) | strict(禁止继承)
|
|
21
24
|
--no-verify push 时跳过 hook/husky 校验
|
|
@@ -42,6 +45,8 @@ export async function runSyncCommand(rawArgs) {
|
|
|
42
45
|
'merge-main': { type: 'boolean', default: false },
|
|
43
46
|
rebase: { type: 'boolean', default: false },
|
|
44
47
|
push: { type: 'boolean', default: false },
|
|
48
|
+
autostash: { type: 'boolean', default: false },
|
|
49
|
+
json: { type: 'boolean', default: false },
|
|
45
50
|
yes: { type: 'boolean', short: 'y', default: false },
|
|
46
51
|
'repo-mode': { type: 'string', default: 'auto' },
|
|
47
52
|
'no-verify': { type: 'boolean', default: false },
|
|
@@ -60,15 +65,27 @@ export async function runSyncCommand(rawArgs) {
|
|
|
60
65
|
mergeMain: parsed.values['merge-main'],
|
|
61
66
|
rebase: parsed.values.rebase,
|
|
62
67
|
push: parsed.values.push,
|
|
68
|
+
autostash: parsed.values.autostash,
|
|
63
69
|
yes: parsed.values.yes,
|
|
64
70
|
repoMode: parsed.values['repo-mode'],
|
|
65
71
|
noVerify: parsed.values['no-verify']
|
|
66
72
|
})
|
|
67
73
|
|
|
74
|
+
if (parsed.values.json) {
|
|
75
|
+
process.stdout.write(`${JSON.stringify({ ok: !result.canceled, ...result }, null, 2)}\n`)
|
|
76
|
+
return result
|
|
77
|
+
}
|
|
78
|
+
|
|
68
79
|
if (result.canceled) {
|
|
69
80
|
return
|
|
70
81
|
}
|
|
71
82
|
|
|
72
|
-
|
|
73
|
-
|
|
83
|
+
print('')
|
|
84
|
+
renderBranchResults(result.summary, {
|
|
85
|
+
synced: '已同步',
|
|
86
|
+
'synced-and-pushed': '已同步并推送',
|
|
87
|
+
missing: '本地和远程都不存在'
|
|
88
|
+
})
|
|
89
|
+
print('')
|
|
90
|
+
logger.success(`sync 完成,共 ${result.summary.length} 个分支`)
|
|
74
91
|
}
|
package/src/commands/tag.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { parseArgs } from 'node:util'
|
|
2
2
|
import { normalizeLegacyArgs } from '../core/cli/args.js'
|
|
3
|
+
import { printJson } from '../core/cli/output.js'
|
|
4
|
+
import { c, ui } from '../core/cli/render.js'
|
|
3
5
|
import { logger } from '../core/tools/logger.js'
|
|
4
6
|
import { runTagWorkflow } from '../workflows/tag-workflow.js'
|
|
5
7
|
|
|
@@ -10,14 +12,28 @@ Usage:
|
|
|
10
12
|
|
|
11
13
|
Options:
|
|
12
14
|
-n, --name <tag> 指定 tag 名(默认 vYYYYMMDD_NN)
|
|
15
|
+
-b, --bump <kind> 按语义化版本递增: auto | patch | minor | major | prerelease
|
|
16
|
+
--preid <id> 预发布标识,例如 beta、rc(配合 --bump)
|
|
13
17
|
-m, --message <msg> tag 备注
|
|
14
18
|
--from <ref> 从指定分支/commit 创建 tag
|
|
19
|
+
--no-push 只创建本地 tag,不推送
|
|
20
|
+
--dry-run 只计算 tag 名,不创建
|
|
21
|
+
--json 输出机器可读 JSON
|
|
22
|
+
-y, --yes 不再询问 tag 名
|
|
15
23
|
--repo-mode <mode> 仓库模式: auto(自动继承父仓库) | strict(禁止继承)
|
|
16
24
|
--no-verify 跳过 hook/husky 校验
|
|
17
25
|
-h, --help 查看帮助
|
|
18
26
|
|
|
19
27
|
Notes:
|
|
20
28
|
默认 tag 规则: vYYYYMMDD_NN(例如 v20260309_01)。
|
|
29
|
+
--bump auto 会根据上个版本以来的提交推断:破坏性变更 -> major,feat -> minor,其它 -> patch。
|
|
30
|
+
需要同时生成 changelog 和平台 Release,请用 icode release。
|
|
31
|
+
|
|
32
|
+
Examples:
|
|
33
|
+
icode tag
|
|
34
|
+
icode tag --bump auto --dry-run
|
|
35
|
+
icode tag --bump minor -y
|
|
36
|
+
icode tag --bump prerelease --preid rc
|
|
21
37
|
`)
|
|
22
38
|
}
|
|
23
39
|
|
|
@@ -28,8 +44,14 @@ export async function runTagCommand(rawArgs) {
|
|
|
28
44
|
allowPositionals: true,
|
|
29
45
|
options: {
|
|
30
46
|
name: { type: 'string', short: 'n' },
|
|
47
|
+
bump: { type: 'string', short: 'b' },
|
|
48
|
+
preid: { type: 'string' },
|
|
31
49
|
message: { type: 'string', short: 'm' },
|
|
32
50
|
from: { type: 'string' },
|
|
51
|
+
'no-push': { type: 'boolean', default: false },
|
|
52
|
+
'dry-run': { type: 'boolean', default: false },
|
|
53
|
+
json: { type: 'boolean', default: false },
|
|
54
|
+
yes: { type: 'boolean', short: 'y', default: false },
|
|
33
55
|
'repo-mode': { type: 'string', default: 'auto' },
|
|
34
56
|
'no-verify': { type: 'boolean', default: false },
|
|
35
57
|
help: { type: 'boolean', short: 'h', default: false }
|
|
@@ -43,11 +65,28 @@ export async function runTagCommand(rawArgs) {
|
|
|
43
65
|
|
|
44
66
|
const result = await runTagWorkflow({
|
|
45
67
|
tagName: parsed.values.name,
|
|
68
|
+
bump: parsed.values.bump,
|
|
69
|
+
preid: parsed.values.preid,
|
|
46
70
|
message: parsed.values.message,
|
|
47
71
|
fromRef: parsed.values.from,
|
|
72
|
+
noPush: parsed.values['no-push'],
|
|
73
|
+
dryRun: parsed.values['dry-run'],
|
|
74
|
+
yes: parsed.values.yes || parsed.values.json,
|
|
75
|
+
silentLog: parsed.values.json,
|
|
48
76
|
repoMode: parsed.values['repo-mode'],
|
|
49
77
|
noVerify: parsed.values['no-verify']
|
|
50
78
|
})
|
|
51
79
|
|
|
52
|
-
|
|
80
|
+
if (parsed.values.json) {
|
|
81
|
+
printJson(result)
|
|
82
|
+
return result
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (result.dryRun) {
|
|
86
|
+
logger.info(`下一个 tag: ${c.bold(ui.tag(result.tagName))}${result.previousTag ? c.gray(`(上一个 ${result.previousTag} · ${result.bump})`) : ''} ${c.gray('dry-run')}`)
|
|
87
|
+
return result
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
logger.success(`已创建 tag ${c.bold(ui.tag(result.tagName))}${result.pushed ? c.gray(' 并已推送') : c.yellow('(未推送)')}`)
|
|
91
|
+
return result
|
|
53
92
|
}
|
package/src/commands/undo.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { parseArgs } from 'node:util'
|
|
2
2
|
import { normalizeLegacyArgs } from '../core/cli/args.js'
|
|
3
3
|
import { IcodeError } from '../core/errors.js'
|
|
4
|
+
import { c } from '../core/cli/render.js'
|
|
4
5
|
import { logger } from '../core/tools/logger.js'
|
|
5
6
|
import { runUndoWorkflow } from '../workflows/undo-workflow.js'
|
|
6
7
|
|
|
@@ -17,7 +18,7 @@ Options:
|
|
|
17
18
|
--mode <mode> 回滚模式: revert | soft | mixed | hard
|
|
18
19
|
--ref <ref> 回滚目标,默认按 mode 自动给出
|
|
19
20
|
--hash <hash> 按 commit hash 指定回滚目标(等同 --ref)
|
|
20
|
-
--recover <action> 冲突恢复策略: continue | abort | keep
|
|
21
|
+
--recover <action> 冲突恢复策略: continue | abort | keep(推荐直接用 icode continue / icode abort)
|
|
21
22
|
--json 输出机器可读 JSON,适合 AI agent 或脚本消费
|
|
22
23
|
-y, --yes 自动确认(跳过确认提示)
|
|
23
24
|
--repo-mode <mode> 仓库模式: auto(自动继承父仓库) | strict(禁止继承)
|
|
@@ -121,9 +122,14 @@ export async function runUndoCommand(rawArgs) {
|
|
|
121
122
|
}
|
|
122
123
|
|
|
123
124
|
if (result.resolvedOperation) {
|
|
124
|
-
|
|
125
|
+
const action = result.recoverAction === 'continue' ? '已继续完成' : '已中止'
|
|
126
|
+
logger.success(`${action}未完成的 ${c.bold(result.resolvedOperation)}`)
|
|
125
127
|
return
|
|
126
128
|
}
|
|
127
129
|
|
|
128
|
-
|
|
130
|
+
const modeLabel = { revert: '生成反向提交 (revert)', soft: '撤销提交,保留暂存区', mixed: '撤销提交,保留工作区改动', hard: '撤销提交并丢弃改动' }[result.mode] || result.mode
|
|
131
|
+
logger.success(`${modeLabel} ${c.gray(`ref=${result.ref}`)}`)
|
|
132
|
+
if (result.backupId) {
|
|
133
|
+
logger.hint(`已创建恢复点 ${result.backupId},后悔了可以 icode rescue 找回`)
|
|
134
|
+
}
|
|
129
135
|
}
|
|
@@ -25,7 +25,7 @@ function summarizeGitError(error) {
|
|
|
25
25
|
|
|
26
26
|
export async function resolveAiDiffRange({ git, context, baseRef = '', headRef = 'HEAD', label = 'AI', explicitHead = false }) {
|
|
27
27
|
const explicitBase = Boolean((baseRef || '').trim())
|
|
28
|
-
const remoteDefaultBase =
|
|
28
|
+
const remoteDefaultBase = `${context.remote || 'origin'}/${context.defaultBranch}`
|
|
29
29
|
const localDefaultBase = context.defaultBranch || ''
|
|
30
30
|
const candidates = explicitBase
|
|
31
31
|
? [(baseRef || '').trim()]
|
|
@@ -8,9 +8,21 @@ const AI_COMMIT_LANG_VALUES = ['zh', 'en']
|
|
|
8
8
|
const UNDO_MODE_VALUES = ['revert', 'soft', 'mixed', 'hard']
|
|
9
9
|
const UNDO_RECOVER_VALUES = ['continue', 'abort', 'keep']
|
|
10
10
|
const SHELL_VALUES = ['bash', 'zsh']
|
|
11
|
+
const BUMP_VALUES = ['auto', 'patch', 'minor', 'major', 'prerelease']
|
|
12
|
+
const PLATFORM_VALUES = ['github', 'gitlab', 'gitee', 'gitea']
|
|
13
|
+
const PR_STATE_VALUES = ['open', 'closed', 'merged', 'all']
|
|
14
|
+
const PR_MERGE_METHOD_VALUES = ['merge', 'squash', 'rebase']
|
|
15
|
+
const OPEN_TARGET_VALUES = ['repo', 'pr', 'prs', 'commit', 'compare', 'ci', 'releases']
|
|
11
16
|
const CONFIG_PATH_VALUES = [
|
|
17
|
+
'remote',
|
|
18
|
+
'main',
|
|
19
|
+
'repo-mode',
|
|
20
|
+
'ai-profile',
|
|
12
21
|
'defaults.repoMode',
|
|
13
22
|
'defaults.defaultMainBranches',
|
|
23
|
+
'defaults.remote',
|
|
24
|
+
'platforms.tokens',
|
|
25
|
+
'platforms.hosts',
|
|
14
26
|
'ai.activeProfile',
|
|
15
27
|
'ai.profiles',
|
|
16
28
|
'ai.options',
|
|
@@ -40,8 +52,44 @@ const EXPLAIN_NODE = {
|
|
|
40
52
|
]
|
|
41
53
|
}
|
|
42
54
|
|
|
55
|
+
const JSON_OPTION = { long: '--json' }
|
|
56
|
+
const YES_OPTION = { long: '--yes', short: '-y' }
|
|
57
|
+
const HELP_OPTION = { long: '--help', short: '-h' }
|
|
58
|
+
const REPO_MODE_OPTION = { long: '--repo-mode', valueSource: 'repo-mode' }
|
|
59
|
+
const BASIC_OPTIONS = [JSON_OPTION, YES_OPTION, REPO_MODE_OPTION, HELP_OPTION]
|
|
60
|
+
|
|
61
|
+
const PR_OPTIONS = [
|
|
62
|
+
{ long: '--base', short: '-b', valueSource: 'branch' },
|
|
63
|
+
{ long: '--title', short: '-t', valueSource: 'text' },
|
|
64
|
+
{ long: '--body', valueSource: 'text' },
|
|
65
|
+
{ long: '--draft' },
|
|
66
|
+
{ long: '--ai' },
|
|
67
|
+
{ long: '--profile', valueSource: 'ai-profile' },
|
|
68
|
+
{ long: '--lang', valueSource: 'ai-commit-lang' },
|
|
69
|
+
{ long: '--no-push' },
|
|
70
|
+
{ long: '--web' },
|
|
71
|
+
{ long: '--state', valueSource: 'pr-state' },
|
|
72
|
+
{ long: '--method', valueSource: 'pr-merge-method' },
|
|
73
|
+
{ long: '--branch', valueSource: 'text' },
|
|
74
|
+
{ long: '--provider', valueSource: 'platform' },
|
|
75
|
+
...BASIC_OPTIONS
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
const SW_NODE = {
|
|
79
|
+
positionals: [{ valueSource: 'branch' }],
|
|
80
|
+
options: [
|
|
81
|
+
{ long: '--create', short: '-c' },
|
|
82
|
+
{ long: '--from', valueSource: 'ref' },
|
|
83
|
+
{ long: '--all', short: '-a' },
|
|
84
|
+
{ long: '--autostash' },
|
|
85
|
+
{ long: '--list', short: '-l' },
|
|
86
|
+
...BASIC_OPTIONS
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
|
|
43
90
|
const ROOT_COMPLETION_SPEC = {
|
|
44
91
|
options: [
|
|
92
|
+
{ long: '--remote', short: '-R', valueSource: 'remote' },
|
|
45
93
|
{ long: '--debug', short: '-d' },
|
|
46
94
|
{ long: '--help', short: '-h' }
|
|
47
95
|
],
|
|
@@ -82,6 +130,8 @@ const ROOT_COMPLETION_SPEC = {
|
|
|
82
130
|
{ long: '--yes', short: '-y' },
|
|
83
131
|
{ long: '--push-origin' },
|
|
84
132
|
{ long: '--pull-main' },
|
|
133
|
+
{ long: '--autostash' },
|
|
134
|
+
{ long: '--json' },
|
|
85
135
|
{ long: '--repo-mode', valueSource: 'repo-mode' },
|
|
86
136
|
{ long: '--no-verify' },
|
|
87
137
|
{ long: '--help', short: '-h' }
|
|
@@ -100,6 +150,8 @@ const ROOT_COMPLETION_SPEC = {
|
|
|
100
150
|
{ long: '--ai-commit' },
|
|
101
151
|
{ long: '--profile', valueSource: 'ai-profile' },
|
|
102
152
|
{ long: '--lang', valueSource: 'ai-commit-lang' },
|
|
153
|
+
{ long: '--staged-only' },
|
|
154
|
+
{ long: '--allow-risky' },
|
|
103
155
|
{ long: '--dry-run' },
|
|
104
156
|
{ long: '--json' },
|
|
105
157
|
{ long: '--repo-mode', valueSource: 'repo-mode' },
|
|
@@ -122,6 +174,10 @@ const ROOT_COMPLETION_SPEC = {
|
|
|
122
174
|
{ long: '--pull-main' },
|
|
123
175
|
{ long: '--not-push-current' },
|
|
124
176
|
{ long: '--force-protected' },
|
|
177
|
+
{ long: '--force', short: '-f' },
|
|
178
|
+
{ long: '--create-target' },
|
|
179
|
+
{ long: '--allow-risky' },
|
|
180
|
+
{ long: '--json' },
|
|
125
181
|
{ long: '--repo-mode', valueSource: 'repo-mode' },
|
|
126
182
|
{ long: '--no-verify' }
|
|
127
183
|
]
|
|
@@ -133,6 +189,8 @@ const ROOT_COMPLETION_SPEC = {
|
|
|
133
189
|
{ long: '--merge-main' },
|
|
134
190
|
{ long: '--rebase' },
|
|
135
191
|
{ long: '--push' },
|
|
192
|
+
{ long: '--autostash' },
|
|
193
|
+
{ long: '--json' },
|
|
136
194
|
{ long: '--yes', short: '-y' },
|
|
137
195
|
{ long: '--repo-mode', valueSource: 'repo-mode' },
|
|
138
196
|
{ long: '--no-verify' },
|
|
@@ -142,9 +200,14 @@ const ROOT_COMPLETION_SPEC = {
|
|
|
142
200
|
clean: {
|
|
143
201
|
options: [
|
|
144
202
|
{ long: '--merged-target', valueSource: 'branch' },
|
|
203
|
+
{ long: '--merged' },
|
|
204
|
+
{ long: '--gone' },
|
|
205
|
+
{ long: '--stale', valueSource: 'text' },
|
|
145
206
|
{ long: '--keep', valueSource: 'branch' },
|
|
146
207
|
{ long: '--remote' },
|
|
147
208
|
{ long: '--force' },
|
|
209
|
+
{ long: '--dry-run' },
|
|
210
|
+
{ long: '--json' },
|
|
148
211
|
{ long: '--yes', short: '-y' },
|
|
149
212
|
{ long: '--repo-mode', valueSource: 'repo-mode' },
|
|
150
213
|
{ long: '--help', short: '-h' }
|
|
@@ -183,8 +246,14 @@ const ROOT_COMPLETION_SPEC = {
|
|
|
183
246
|
tag: {
|
|
184
247
|
options: [
|
|
185
248
|
{ long: '--name', short: '-n', valueSource: 'text' },
|
|
249
|
+
{ long: '--bump', short: '-b', valueSource: 'bump' },
|
|
250
|
+
{ long: '--preid', valueSource: 'text' },
|
|
186
251
|
{ long: '--message', short: '-m', valueSource: 'text' },
|
|
187
252
|
{ long: '--from', valueSource: 'ref' },
|
|
253
|
+
{ long: '--no-push' },
|
|
254
|
+
{ long: '--dry-run' },
|
|
255
|
+
{ long: '--json' },
|
|
256
|
+
{ long: '--yes', short: '-y' },
|
|
188
257
|
{ long: '--repo-mode', valueSource: 'repo-mode' },
|
|
189
258
|
{ long: '--no-verify' },
|
|
190
259
|
{ long: '--help', short: '-h' }
|
|
@@ -196,7 +265,34 @@ const ROOT_COMPLETION_SPEC = {
|
|
|
196
265
|
{ long: '--help', short: '-h' }
|
|
197
266
|
],
|
|
198
267
|
subcommands: {
|
|
199
|
-
list: {
|
|
268
|
+
list: {
|
|
269
|
+
options: [{ long: '--show-secrets' }]
|
|
270
|
+
},
|
|
271
|
+
path: {},
|
|
272
|
+
edit: {},
|
|
273
|
+
init: {
|
|
274
|
+
options: [{ long: '--force' }, { long: '--yes', short: '-y' }]
|
|
275
|
+
},
|
|
276
|
+
platform: {
|
|
277
|
+
subcommands: {
|
|
278
|
+
token: {
|
|
279
|
+
subcommands: {
|
|
280
|
+
show: {},
|
|
281
|
+
set: { positionals: [{ valueSource: 'platform' }] },
|
|
282
|
+
remove: { positionals: [{ valueSource: 'platform' }] }
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
host: {
|
|
286
|
+
subcommands: {
|
|
287
|
+
set: { positionals: [{ valueSource: 'text' }, { valueSource: 'platform' }] },
|
|
288
|
+
remove: {}
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
'remote-merge': {
|
|
292
|
+
subcommands: { show: {}, set: { options: [{ long: '--provider', valueSource: 'platform' }, { long: '--api-key', valueSource: 'text' }] } }
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
},
|
|
200
296
|
get: {
|
|
201
297
|
positionals: [{ valueSource: 'config-path' }]
|
|
202
298
|
},
|
|
@@ -285,9 +381,116 @@ const ROOT_COMPLETION_SPEC = {
|
|
|
285
381
|
info: {
|
|
286
382
|
options: [
|
|
287
383
|
{ long: '--repo-mode', valueSource: 'repo-mode' },
|
|
384
|
+
{ long: '--json' },
|
|
288
385
|
{ long: '--help', short: '-h' }
|
|
289
386
|
]
|
|
290
387
|
},
|
|
388
|
+
continue: { options: [JSON_OPTION, REPO_MODE_OPTION, HELP_OPTION] },
|
|
389
|
+
abort: { options: BASIC_OPTIONS },
|
|
390
|
+
sw: SW_NODE,
|
|
391
|
+
switch: SW_NODE,
|
|
392
|
+
stash: {
|
|
393
|
+
options: [
|
|
394
|
+
{ long: '--message', short: '-m', valueSource: 'text' },
|
|
395
|
+
{ long: '--staged' },
|
|
396
|
+
{ long: '--no-untracked' },
|
|
397
|
+
{ long: '--index' },
|
|
398
|
+
...BASIC_OPTIONS
|
|
399
|
+
],
|
|
400
|
+
subcommands: {
|
|
401
|
+
list: {},
|
|
402
|
+
save: {},
|
|
403
|
+
push: {},
|
|
404
|
+
apply: {},
|
|
405
|
+
pop: {},
|
|
406
|
+
drop: {},
|
|
407
|
+
show: {}
|
|
408
|
+
}
|
|
409
|
+
},
|
|
410
|
+
log: {
|
|
411
|
+
positionals: [{ valueSource: 'ref' }],
|
|
412
|
+
options: [
|
|
413
|
+
{ long: '--limit', short: '-n', valueSource: 'text' },
|
|
414
|
+
{ long: '--mine' },
|
|
415
|
+
{ long: '--author', valueSource: 'text' },
|
|
416
|
+
{ long: '--since', valueSource: 'text' },
|
|
417
|
+
{ long: '--grep', valueSource: 'text' },
|
|
418
|
+
{ long: '--file', valueSource: 'text' },
|
|
419
|
+
{ long: '--unpushed' },
|
|
420
|
+
{ long: '--no-merges' },
|
|
421
|
+
{ long: '--graph' },
|
|
422
|
+
...BASIC_OPTIONS
|
|
423
|
+
]
|
|
424
|
+
},
|
|
425
|
+
amend: {
|
|
426
|
+
options: [
|
|
427
|
+
{ long: '--message', short: '-m', valueSource: 'text' },
|
|
428
|
+
{ long: '--all', short: '-a' },
|
|
429
|
+
{ long: '--allow-risky' },
|
|
430
|
+
{ long: '--no-verify' },
|
|
431
|
+
...BASIC_OPTIONS
|
|
432
|
+
]
|
|
433
|
+
},
|
|
434
|
+
fixup: {
|
|
435
|
+
positionals: [{ valueSource: 'ref' }],
|
|
436
|
+
options: [
|
|
437
|
+
{ long: '--all', short: '-a' },
|
|
438
|
+
{ long: '--no-rebase' },
|
|
439
|
+
{ long: '--allow-risky' },
|
|
440
|
+
{ long: '--no-verify' },
|
|
441
|
+
...BASIC_OPTIONS
|
|
442
|
+
]
|
|
443
|
+
},
|
|
444
|
+
rescue: {
|
|
445
|
+
options: [
|
|
446
|
+
{ long: '--branch', valueSource: 'text' },
|
|
447
|
+
{ long: '--reset' },
|
|
448
|
+
{ long: '--limit', valueSource: 'text' },
|
|
449
|
+
{ long: '--keep', valueSource: 'text' },
|
|
450
|
+
...BASIC_OPTIONS
|
|
451
|
+
],
|
|
452
|
+
subcommands: {
|
|
453
|
+
list: {},
|
|
454
|
+
restore: {},
|
|
455
|
+
prune: {}
|
|
456
|
+
}
|
|
457
|
+
},
|
|
458
|
+
pr: {
|
|
459
|
+
options: PR_OPTIONS,
|
|
460
|
+
subcommands: {
|
|
461
|
+
create: { options: PR_OPTIONS },
|
|
462
|
+
list: { options: PR_OPTIONS },
|
|
463
|
+
view: { options: PR_OPTIONS },
|
|
464
|
+
checkout: { options: PR_OPTIONS },
|
|
465
|
+
merge: { options: PR_OPTIONS },
|
|
466
|
+
open: { options: PR_OPTIONS }
|
|
467
|
+
}
|
|
468
|
+
},
|
|
469
|
+
open: {
|
|
470
|
+
positionals: [{ valueSource: 'open-target' }],
|
|
471
|
+
options: [
|
|
472
|
+
{ long: '--print', short: '-p' },
|
|
473
|
+
{ long: '--branch', valueSource: 'branch' },
|
|
474
|
+
...BASIC_OPTIONS
|
|
475
|
+
]
|
|
476
|
+
},
|
|
477
|
+
release: {
|
|
478
|
+
options: [
|
|
479
|
+
{ long: '--bump', short: '-b', valueSource: 'bump' },
|
|
480
|
+
{ long: '--preid', valueSource: 'text' },
|
|
481
|
+
{ long: '--name', short: '-n', valueSource: 'text' },
|
|
482
|
+
{ long: '--write-changelog' },
|
|
483
|
+
{ long: '--changelog-file', valueSource: 'text' },
|
|
484
|
+
{ long: '--update-package' },
|
|
485
|
+
{ long: '--no-platform' },
|
|
486
|
+
{ long: '--draft' },
|
|
487
|
+
{ long: '--prerelease' },
|
|
488
|
+
{ long: '--no-push' },
|
|
489
|
+
{ long: '--dry-run' },
|
|
490
|
+
{ long: '--no-verify' },
|
|
491
|
+
...BASIC_OPTIONS
|
|
492
|
+
]
|
|
493
|
+
},
|
|
291
494
|
help: {},
|
|
292
495
|
completion: {
|
|
293
496
|
positionals: [{ valueSource: 'shell' }],
|
|
@@ -562,6 +765,30 @@ function resolveValueCandidates(valueSource, providers) {
|
|
|
562
765
|
return CONFIG_PATH_VALUES
|
|
563
766
|
}
|
|
564
767
|
|
|
768
|
+
if (valueSource === 'bump') {
|
|
769
|
+
return BUMP_VALUES
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
if (valueSource === 'platform') {
|
|
773
|
+
return PLATFORM_VALUES
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
if (valueSource === 'pr-state') {
|
|
777
|
+
return PR_STATE_VALUES
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
if (valueSource === 'pr-merge-method') {
|
|
781
|
+
return PR_MERGE_METHOD_VALUES
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
if (valueSource === 'open-target') {
|
|
785
|
+
return OPEN_TARGET_VALUES
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
if (valueSource === 'remote') {
|
|
789
|
+
return listRemoteCandidates()
|
|
790
|
+
}
|
|
791
|
+
|
|
565
792
|
if (valueSource === 'branch') {
|
|
566
793
|
return providers.listBranchCandidates()
|
|
567
794
|
}
|
|
@@ -603,6 +830,15 @@ function filterCandidates(candidates, currentWord) {
|
|
|
603
830
|
* 读取当前仓库的分支候选项,失败时静默返回空数组。
|
|
604
831
|
* 输入为空,输出为本地与远程分支名数组。
|
|
605
832
|
*/
|
|
833
|
+
function listRemoteCandidates() {
|
|
834
|
+
try {
|
|
835
|
+
const result = spawnSync('git', ['remote'], { encoding: 'utf8' })
|
|
836
|
+
return result.status === 0 ? result.stdout.split('\n').map((item) => item.trim()).filter(Boolean) : []
|
|
837
|
+
} catch {
|
|
838
|
+
return []
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
|
|
606
842
|
function listBranchCandidates() {
|
|
607
843
|
return listGitCandidates(['refs/heads', 'refs/remotes'])
|
|
608
844
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { parseArgs } from 'node:util'
|
|
2
|
+
import { normalizeLegacyArgs } from './args.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 输出 JSON 结果;统一带上 ok 字段,方便脚本和 AI agent 判断。
|
|
6
|
+
* 输入为结果对象,输出为空。
|
|
7
|
+
*/
|
|
8
|
+
export function printJson(result, ok = true) {
|
|
9
|
+
const payload = result && typeof result === 'object' && !Array.isArray(result)
|
|
10
|
+
? { ok: result.canceled ? false : ok, ...result }
|
|
11
|
+
: { ok, data: result }
|
|
12
|
+
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// 所有命令共用的参数。
|
|
16
|
+
export const COMMON_OPTIONS = {
|
|
17
|
+
'repo-mode': { type: 'string', default: 'auto' },
|
|
18
|
+
json: { type: 'boolean', default: false },
|
|
19
|
+
yes: { type: 'boolean', short: 'y', default: false },
|
|
20
|
+
help: { type: 'boolean', short: 'h', default: false }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 解析子命令参数:历史别名归一化 + 合并公共参数。
|
|
25
|
+
* 输入为原始参数与命令自有参数定义,输出 parseArgs 结果。
|
|
26
|
+
*/
|
|
27
|
+
export function parseCommandArgs(rawArgs, options = {}) {
|
|
28
|
+
return parseArgs({
|
|
29
|
+
args: normalizeLegacyArgs(rawArgs),
|
|
30
|
+
allowPositionals: true,
|
|
31
|
+
options: {
|
|
32
|
+
...COMMON_OPTIONS,
|
|
33
|
+
...options
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 左对齐补空格(按显示宽度粗略处理中文)。
|
|
39
|
+
export function padDisplay(text, width) {
|
|
40
|
+
const value = String(text ?? '')
|
|
41
|
+
let displayWidth = 0
|
|
42
|
+
for (const char of value) {
|
|
43
|
+
displayWidth += /[ᄀ-]/.test(char) ? 2 : 1
|
|
44
|
+
}
|
|
45
|
+
return displayWidth >= width ? value : `${value}${' '.repeat(width - displayWidth)}`
|
|
46
|
+
}
|