@ranger1/dx 0.1.15 → 0.1.17
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/@opencode/agents/claude-reviewer.md +57 -0
- package/@opencode/agents/codex-reviewer.md +58 -0
- package/@opencode/agents/gemini-reviewer.md +57 -0
- package/@opencode/agents/pr-context.md +195 -0
- package/@opencode/agents/pr-fix.md +173 -0
- package/@opencode/agents/pr-precheck.md +57 -0
- package/@opencode/agents/pr-review-aggregate.md +118 -0
- package/@opencode/commands/doctor.md +235 -0
- package/@opencode/commands/git-commit-and-pr.md +276 -0
- package/@opencode/commands/git-release.md +642 -0
- package/@opencode/commands/pr-review-loop.md +74 -0
- package/bin/dx.js +31 -0
- package/lib/cli/dx-cli.js +1 -4
- package/lib/cli/help.js +1 -37
- package/lib/opencode-initial.js +108 -0
- package/opencode/agents/claude-reviewer.md +57 -0
- package/opencode/agents/codex-reviewer.md +58 -0
- package/opencode/agents/gemini-reviewer.md +57 -0
- package/opencode/agents/pr-context.md +195 -0
- package/opencode/agents/pr-fix.md +173 -0
- package/opencode/agents/pr-precheck.md +57 -0
- package/opencode/agents/pr-review-aggregate.md +118 -0
- package/opencode/commands/doctor.md +235 -0
- package/opencode/commands/git-commit-and-pr.md +276 -0
- package/opencode/commands/git-release.md +642 -0
- package/opencode/commands/pr-review-loop.md +74 -0
- package/package.json +3 -1
- package/lib/cli/commands/ai.js +0 -215
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ranger1/dx",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.17",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
"files": [
|
|
19
19
|
"bin/",
|
|
20
20
|
"lib/",
|
|
21
|
+
"@opencode/",
|
|
22
|
+
"opencode/",
|
|
21
23
|
"LICENSE",
|
|
22
24
|
"README.md",
|
|
23
25
|
"package.json"
|
package/lib/cli/commands/ai.js
DELETED
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from 'node:fs'
|
|
2
|
-
import { resolve, join, isAbsolute } from 'node:path'
|
|
3
|
-
import { homedir } from 'node:os'
|
|
4
|
-
import { spawn, spawnSync } from 'node:child_process'
|
|
5
|
-
import { logger } from '../../logger.js'
|
|
6
|
-
import { confirmManager } from '../../confirm.js'
|
|
7
|
-
import { getPassthroughArgs } from '../args.js'
|
|
8
|
-
|
|
9
|
-
function ensureOpencodeAvailable() {
|
|
10
|
-
const result = spawnSync('opencode', ['--version'], { stdio: 'ignore' })
|
|
11
|
-
if (result?.error?.code === 'ENOENT') {
|
|
12
|
-
return { ok: false, reason: 'missing' }
|
|
13
|
-
}
|
|
14
|
-
return { ok: true }
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function resolveAiConfig(cli, name) {
|
|
18
|
-
const raw = cli.commands?.ai?.[name]
|
|
19
|
-
if (!raw) return null
|
|
20
|
-
|
|
21
|
-
const environment = cli.determineEnvironment()
|
|
22
|
-
const envKey = cli.normalizeEnvKey(environment)
|
|
23
|
-
let config = raw
|
|
24
|
-
|
|
25
|
-
// 允许按环境分支(保持与 build/start/export 一致)
|
|
26
|
-
if (typeof config === 'object' && config && !config.promptFile && !config.command) {
|
|
27
|
-
if (config[envKey]) config = config[envKey]
|
|
28
|
-
else if (envKey === 'staging' && config.prod) config = config.prod
|
|
29
|
-
else config = config.dev || config
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return config
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function expandHomePath(input) {
|
|
36
|
-
const raw = String(input ?? '')
|
|
37
|
-
if (raw === '~') return homedir()
|
|
38
|
-
if (raw.startsWith('~/') || raw.startsWith('~\\')) {
|
|
39
|
-
return resolve(homedir(), raw.slice(2))
|
|
40
|
-
}
|
|
41
|
-
return raw
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function isBareFilename(input) {
|
|
45
|
-
const raw = String(input ?? '')
|
|
46
|
-
if (!raw) return false
|
|
47
|
-
if (raw.startsWith('.')) return false
|
|
48
|
-
return !raw.includes('/') && !raw.includes('\\')
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function resolvePromptPath(promptFile) {
|
|
52
|
-
const projectRoot = process.env.DX_PROJECT_ROOT || process.cwd()
|
|
53
|
-
const expanded = expandHomePath(promptFile)
|
|
54
|
-
|
|
55
|
-
if (isAbsolute(expanded)) return [expanded]
|
|
56
|
-
|
|
57
|
-
const dxDir = join(projectRoot, 'dx')
|
|
58
|
-
const candidates = []
|
|
59
|
-
|
|
60
|
-
// 仅文件名:优先在 dx/prompts 下找(不破坏历史:仍保留 project root fallback)
|
|
61
|
-
if (isBareFilename(expanded)) {
|
|
62
|
-
candidates.push(join(dxDir, 'prompts', expanded))
|
|
63
|
-
candidates.push(resolve(projectRoot, expanded))
|
|
64
|
-
candidates.push(resolve(dxDir, expanded))
|
|
65
|
-
return candidates
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// 相对路径:优先保持与历史一致(相对 project root),找不到再 fallback 到 dx/ 下
|
|
69
|
-
candidates.push(resolve(projectRoot, expanded))
|
|
70
|
-
candidates.push(resolve(dxDir, expanded))
|
|
71
|
-
return candidates
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export async function handleAi(cli, args = []) {
|
|
75
|
-
const name = args[0]
|
|
76
|
-
if (!name) {
|
|
77
|
-
const names = Object.keys(cli.commands?.ai || {})
|
|
78
|
-
logger.error('请指定 ai 任务名称')
|
|
79
|
-
logger.info(`用法: ${cli.invocation} ai <name> [-- <opencode flags>...]`)
|
|
80
|
-
if (names.length > 0) {
|
|
81
|
-
logger.info(`可用任务: ${names.join(', ')}`)
|
|
82
|
-
}
|
|
83
|
-
process.exitCode = 1
|
|
84
|
-
return
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const config = resolveAiConfig(cli, name)
|
|
88
|
-
if (!config) {
|
|
89
|
-
const names = Object.keys(cli.commands?.ai || {})
|
|
90
|
-
logger.error(`未找到 ai 任务配置: ${name}`)
|
|
91
|
-
if (names.length > 0) {
|
|
92
|
-
logger.info(`可用任务: ${names.join(', ')}`)
|
|
93
|
-
}
|
|
94
|
-
process.exitCode = 1
|
|
95
|
-
return
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const availability = ensureOpencodeAvailable()
|
|
99
|
-
if (!availability.ok) {
|
|
100
|
-
logger.error('未找到 opencode 可执行文件(PATH 中不存在 opencode)')
|
|
101
|
-
logger.info('请先安装并确保 `opencode --version` 可用')
|
|
102
|
-
process.exitCode = 1
|
|
103
|
-
return
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const promptFile = config?.promptFile
|
|
107
|
-
if (!promptFile) {
|
|
108
|
-
logger.error(`ai.${name} 缺少 promptFile 配置(指向一个 .md 文件)`)
|
|
109
|
-
process.exitCode = 1
|
|
110
|
-
return
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const promptCandidates = resolvePromptPath(promptFile)
|
|
114
|
-
const promptPath = promptCandidates.find(p => existsSync(p)) || promptCandidates[0]
|
|
115
|
-
if (!promptPath || !existsSync(promptPath)) {
|
|
116
|
-
logger.error(`未找到提示词文件: ${promptFile}`)
|
|
117
|
-
if (promptCandidates.length <= 1) {
|
|
118
|
-
logger.info(`解析后的路径: ${promptPath}`)
|
|
119
|
-
} else {
|
|
120
|
-
logger.info('解析后的候选路径:')
|
|
121
|
-
for (const p of promptCandidates) logger.info(`- ${p}`)
|
|
122
|
-
}
|
|
123
|
-
process.exitCode = 1
|
|
124
|
-
return
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
let promptText = ''
|
|
128
|
-
try {
|
|
129
|
-
promptText = readFileSync(promptPath, 'utf8')
|
|
130
|
-
} catch (error) {
|
|
131
|
-
logger.error(`读取提示词文件失败: ${promptFile}`)
|
|
132
|
-
logger.error(error?.message || String(error))
|
|
133
|
-
process.exitCode = 1
|
|
134
|
-
return
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// 固定全权限:这会让 opencode 在当前目录拥有 bash/edit 等工具的自动执行权
|
|
138
|
-
if (!cli.flags.Y) {
|
|
139
|
-
const confirmed = await confirmManager.confirmDangerous(
|
|
140
|
-
`ai.${name}(将以 OPENCODE_PERMISSION="allow" 运行,全权限)`,
|
|
141
|
-
'当前目录',
|
|
142
|
-
false,
|
|
143
|
-
)
|
|
144
|
-
if (!confirmed) {
|
|
145
|
-
logger.info('操作已取消')
|
|
146
|
-
return
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const model = config?.model ? String(config.model) : null
|
|
151
|
-
const agent = config?.agent ? String(config.agent) : null
|
|
152
|
-
const format = config?.format ? String(config.format) : null
|
|
153
|
-
const attach = config?.attach ? String(config.attach) : null
|
|
154
|
-
|
|
155
|
-
const passthrough = getPassthroughArgs(cli.args)
|
|
156
|
-
const configPassthrough = Array.isArray(config?.passthrough)
|
|
157
|
-
? config.passthrough.map(v => String(v))
|
|
158
|
-
: []
|
|
159
|
-
|
|
160
|
-
const opencodeArgs = ['run']
|
|
161
|
-
if (model) opencodeArgs.push('--model', model)
|
|
162
|
-
if (agent) opencodeArgs.push('--agent', agent)
|
|
163
|
-
if (format) opencodeArgs.push('--format', format)
|
|
164
|
-
if (attach) opencodeArgs.push('--attach', attach)
|
|
165
|
-
if (configPassthrough.length > 0) opencodeArgs.push(...configPassthrough)
|
|
166
|
-
if (Array.isArray(passthrough) && passthrough.length > 0) opencodeArgs.push(...passthrough)
|
|
167
|
-
// Protect prompt content from being parsed as CLI flags (e.g. markdown frontmatter starts with '---').
|
|
168
|
-
opencodeArgs.push('--', promptText)
|
|
169
|
-
|
|
170
|
-
logger.step(`ai ${name}`)
|
|
171
|
-
logger.command(`opencode ${opencodeArgs.filter(a => a !== promptText).join(' ')} <prompt-from-file>`)
|
|
172
|
-
|
|
173
|
-
await new Promise(resolvePromise => {
|
|
174
|
-
const child = spawn('opencode', opencodeArgs, {
|
|
175
|
-
cwd: process.cwd(),
|
|
176
|
-
stdio: 'inherit',
|
|
177
|
-
env: {
|
|
178
|
-
...process.env,
|
|
179
|
-
// OpenCode expects OPENCODE_PERMISSION to be JSON (it JSON.parse's the value).
|
|
180
|
-
OPENCODE_PERMISSION: '"allow"',
|
|
181
|
-
},
|
|
182
|
-
})
|
|
183
|
-
|
|
184
|
-
const forwardSignal = signal => {
|
|
185
|
-
try {
|
|
186
|
-
child.kill(signal)
|
|
187
|
-
} catch {}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const onSigint = () => forwardSignal('SIGINT')
|
|
191
|
-
const onSigterm = () => forwardSignal('SIGTERM')
|
|
192
|
-
process.on('SIGINT', onSigint)
|
|
193
|
-
process.on('SIGTERM', onSigterm)
|
|
194
|
-
|
|
195
|
-
const cleanup = () => {
|
|
196
|
-
process.off('SIGINT', onSigint)
|
|
197
|
-
process.off('SIGTERM', onSigterm)
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
child.on('error', error => {
|
|
201
|
-
cleanup()
|
|
202
|
-
logger.error(error?.message || String(error))
|
|
203
|
-
process.exitCode = 1
|
|
204
|
-
resolvePromise()
|
|
205
|
-
})
|
|
206
|
-
|
|
207
|
-
child.on('exit', code => {
|
|
208
|
-
cleanup()
|
|
209
|
-
if (typeof code === 'number' && code !== 0) {
|
|
210
|
-
process.exitCode = code
|
|
211
|
-
}
|
|
212
|
-
resolvePromise()
|
|
213
|
-
})
|
|
214
|
-
})
|
|
215
|
-
}
|