@ranger1/dx 0.1.76 → 0.1.78
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 +92 -31
- package/bin/dx.js +3 -3
- package/lib/cli/commands/deploy.js +2 -1
- package/lib/cli/commands/stack.js +198 -237
- package/lib/cli/commands/start.js +0 -6
- package/lib/cli/dx-cli.js +10 -1
- package/lib/cli/help.js +8 -7
- package/lib/{opencode-initial.js → codex-initial.js} +3 -82
- package/lib/vercel-deploy.js +14 -27
- package/package.json +1 -2
- package/@opencode/agents/__pycache__/gh_review_harvest.cpython-314.pyc +0 -0
- package/@opencode/agents/__pycache__/pr_context.cpython-314.pyc +0 -0
- package/@opencode/agents/__pycache__/pr_precheck.cpython-314.pyc +0 -0
- package/@opencode/agents/__pycache__/pr_review_aggregate.cpython-314.pyc +0 -0
- package/@opencode/agents/__pycache__/test_pr_review_aggregate.cpython-314-pytest-9.0.2.pyc +0 -0
- package/@opencode/agents/__pycache__/test_pr_review_aggregate.cpython-314.pyc +0 -0
- package/@opencode/agents/claude-reviewer.md +0 -82
- package/@opencode/agents/codex-reviewer.md +0 -83
- package/@opencode/agents/gemini-reviewer.md +0 -82
- package/@opencode/agents/gh-thread-reviewer.md +0 -122
- package/@opencode/agents/gh_review_harvest.py +0 -292
- package/@opencode/agents/pr-context.md +0 -82
- package/@opencode/agents/pr-fix.md +0 -243
- package/@opencode/agents/pr-precheck.md +0 -89
- package/@opencode/agents/pr-review-aggregate.md +0 -151
- package/@opencode/agents/pr_context.py +0 -351
- package/@opencode/agents/pr_precheck.py +0 -505
- package/@opencode/agents/pr_review_aggregate.py +0 -868
- package/@opencode/agents/test_pr_review_aggregate.py +0 -701
- package/@opencode/commands/doctor.md +0 -271
- package/@opencode/commands/git-commit-and-pr.md +0 -282
- package/@opencode/commands/git-release.md +0 -642
- package/@opencode/commands/oh_attach.json +0 -92
- package/@opencode/commands/opencode_attach.json +0 -29
- package/@opencode/commands/opencode_attach.py +0 -142
- package/@opencode/commands/pr-review-loop.md +0 -211
package/lib/cli/help.js
CHANGED
|
@@ -11,8 +11,8 @@ export function showHelp() {
|
|
|
11
11
|
'',
|
|
12
12
|
'命令:',
|
|
13
13
|
' start [service] [环境标志] 启动/桥接服务',
|
|
14
|
-
' service:
|
|
15
|
-
' stack:
|
|
14
|
+
' service: 由 dx/config/commands.json 的 start.* 决定(默认: dev)',
|
|
15
|
+
' stack: 需在 commands.json 配置 start.stack(internal: pm2-stack)',
|
|
16
16
|
' 环境标志: --dev, --staging, --prod, --test, --e2e(支持别名 --development、--production 等)',
|
|
17
17
|
" 说明: 传入 --staging 时会加载 '.env.staging(.local)' 层,同时复用生产构建/启动流程",
|
|
18
18
|
'',
|
|
@@ -21,7 +21,7 @@ export function showHelp() {
|
|
|
21
21
|
' 环境标志: --dev, --staging, --prod, --test, --e2e(未指定时默认 --dev)',
|
|
22
22
|
'',
|
|
23
23
|
' deploy <target> [环境标志] 部署前端到 Vercel',
|
|
24
|
-
' target: front, admin, telegram-bot, all',
|
|
24
|
+
' target: front, admin, merchant, telegram-bot, all',
|
|
25
25
|
' 环境标志: --dev, --staging, --prod(默认 --staging)',
|
|
26
26
|
'',
|
|
27
27
|
' install 安装依赖(使用 frozen-lockfile 确保版本一致)',
|
|
@@ -68,7 +68,7 @@ export function showHelp() {
|
|
|
68
68
|
'',
|
|
69
69
|
' status 查看系统状态',
|
|
70
70
|
'',
|
|
71
|
-
' initial 初始化
|
|
71
|
+
' initial 初始化 codex 模板到 ~/.codex(覆盖同名文件)',
|
|
72
72
|
'',
|
|
73
73
|
'选项:',
|
|
74
74
|
' --dev, --development 使用开发环境',
|
|
@@ -82,7 +82,7 @@ export function showHelp() {
|
|
|
82
82
|
' -V, --version 显示版本号',
|
|
83
83
|
'',
|
|
84
84
|
'示例:',
|
|
85
|
-
' dx start stack # PM2
|
|
85
|
+
' dx start stack # PM2 交互式服务栈(需在 commands.json 配置 start.stack)',
|
|
86
86
|
' dx start backend --dev # 启动后端开发服务',
|
|
87
87
|
' dx start front --dev # 启动用户前端开发服务',
|
|
88
88
|
' dx start admin --dev # 启动管理后台开发服务',
|
|
@@ -188,7 +188,7 @@ script 子命令:
|
|
|
188
188
|
dx deploy <target> [环境标志] [选项]
|
|
189
189
|
|
|
190
190
|
参数说明:
|
|
191
|
-
target: front, admin, telegram-bot, all
|
|
191
|
+
target: front, admin, merchant, telegram-bot, all
|
|
192
192
|
环境标志: --dev、--staging、--prod(默认 --staging)
|
|
193
193
|
|
|
194
194
|
Telegram Webhook(仅 target=telegram-bot 生效):
|
|
@@ -200,11 +200,12 @@ script 子命令:
|
|
|
200
200
|
常见示例:
|
|
201
201
|
dx deploy front --staging # 部署用户前端(staging)
|
|
202
202
|
dx deploy admin --prod # 部署管理后台(生产)
|
|
203
|
+
dx deploy merchant --staging # 部署商家前端(staging)
|
|
203
204
|
dx deploy telegram-bot --staging # 部署 Telegram Bot + 自动配置 Webhook
|
|
204
205
|
dx deploy telegram-bot --staging --webhook-path /webhook # 使用短路径(rewrite 到 /api/webhook)
|
|
205
206
|
dx deploy telegram-bot --prod --webhook-dry-run # 仅打印,不实际调用 Telegram
|
|
206
207
|
dx deploy telegram-bot --dev --no-strict-webhook # 开发环境显式降级为仅告警
|
|
207
|
-
dx deploy all --staging # 串行部署 front + admin
|
|
208
|
+
dx deploy all --staging # 串行部署 front + admin + merchant
|
|
208
209
|
`)
|
|
209
210
|
return
|
|
210
211
|
|
|
@@ -4,41 +4,6 @@ import os from 'node:os'
|
|
|
4
4
|
|
|
5
5
|
import { logger } from './logger.js'
|
|
6
6
|
|
|
7
|
-
async function collectTemplateFiles(dir) {
|
|
8
|
-
const out = []
|
|
9
|
-
|
|
10
|
-
async function walk(current) {
|
|
11
|
-
let entries
|
|
12
|
-
try {
|
|
13
|
-
entries = await fs.readdir(current, { withFileTypes: true })
|
|
14
|
-
} catch {
|
|
15
|
-
return
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
for (const entry of entries) {
|
|
19
|
-
const full = join(current, entry.name)
|
|
20
|
-
if (entry.isDirectory()) {
|
|
21
|
-
// 跳过 __pycache__ 和其他缓存目录
|
|
22
|
-
if (entry.name === '__pycache__' || entry.name === '.pytest_cache') {
|
|
23
|
-
continue
|
|
24
|
-
}
|
|
25
|
-
await walk(full)
|
|
26
|
-
continue
|
|
27
|
-
}
|
|
28
|
-
if (!entry.isFile()) continue
|
|
29
|
-
const lowerName = entry.name.toLowerCase()
|
|
30
|
-
// 拷贝 .md / .py / .json 文件
|
|
31
|
-
if (!lowerName.endsWith('.md') && !lowerName.endsWith('.py') && !lowerName.endsWith('.json')) continue
|
|
32
|
-
// 跳过 Python 编译文件
|
|
33
|
-
if (lowerName.endsWith('.pyc') || lowerName.endsWith('.pyo') || lowerName.endsWith('.pyd')) continue
|
|
34
|
-
out.push(full)
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
await walk(dir)
|
|
39
|
-
return out
|
|
40
|
-
}
|
|
41
|
-
|
|
42
7
|
async function collectAllFiles(dir) {
|
|
43
8
|
const out = []
|
|
44
9
|
|
|
@@ -146,70 +111,26 @@ async function copySkillsDirectories({ srcSkillsDir, dstSkillsDir }) {
|
|
|
146
111
|
return { copiedDirs, copiedFiles }
|
|
147
112
|
}
|
|
148
113
|
|
|
149
|
-
function
|
|
150
|
-
return [join(packageRoot, '@opencode')]
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
export async function runOpenCodeInitial(options = {}) {
|
|
114
|
+
export async function runCodexInitial(options = {}) {
|
|
154
115
|
const packageRoot = options.packageRoot
|
|
155
|
-
if (!packageRoot) throw new Error('
|
|
116
|
+
if (!packageRoot) throw new Error('runCodexInitial: 缺少 packageRoot')
|
|
156
117
|
|
|
157
118
|
const homeDir = options.homeDir || os.homedir()
|
|
158
|
-
const dstRoot = join(homeDir, '.opencode')
|
|
159
|
-
|
|
160
|
-
const roots = resolveTemplateRoot(packageRoot)
|
|
161
|
-
const existingRoot = await (async () => {
|
|
162
|
-
for (const p of roots) {
|
|
163
|
-
try {
|
|
164
|
-
const st = await fs.stat(p)
|
|
165
|
-
if (st.isDirectory()) return p
|
|
166
|
-
} catch {
|
|
167
|
-
// ignore
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
return null
|
|
171
|
-
})()
|
|
172
|
-
|
|
173
|
-
if (!existingRoot) {
|
|
174
|
-
const tried = roots.map(p => `- ${p}`).join('\n')
|
|
175
|
-
throw new Error(`未找到 opencode 模板目录,已尝试路径:\n${tried}`)
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const srcOpenCodeAgents = join(existingRoot, 'agents')
|
|
179
|
-
const srcCommands = join(existingRoot, 'commands')
|
|
180
|
-
const dstOpenCodeAgents = join(dstRoot, 'agents')
|
|
181
|
-
const dstCommands = join(dstRoot, 'commands')
|
|
182
119
|
const srcSkills = join(packageRoot, 'codex', 'skills')
|
|
183
120
|
const dstSkills = join(homeDir, '.codex', 'skills')
|
|
184
121
|
const srcCodexAgents = join(packageRoot, 'codex', 'agents')
|
|
185
122
|
const dstCodexAgents = join(homeDir, '.codex', 'agents')
|
|
186
123
|
|
|
187
|
-
await assertDirExists(srcOpenCodeAgents, '模板目录 agents')
|
|
188
|
-
await assertDirExists(srcCommands, '模板目录 commands')
|
|
189
124
|
await assertDirExists(srcSkills, '模板目录 codex/skills')
|
|
190
125
|
await assertDirExists(srcCodexAgents, '模板目录 codex/agents')
|
|
191
126
|
|
|
192
|
-
await ensureDir(dstOpenCodeAgents)
|
|
193
|
-
await ensureDir(dstCommands)
|
|
194
127
|
await ensureDir(dstSkills)
|
|
195
128
|
await ensureDir(dstCodexAgents)
|
|
196
129
|
|
|
197
|
-
const agentsStats = await copyTemplateTree({ srcDir: srcOpenCodeAgents, dstDir: dstOpenCodeAgents })
|
|
198
|
-
const commandsStats = await copyTemplateTree({ srcDir: srcCommands, dstDir: dstCommands })
|
|
199
130
|
const skillsStats = await copySkillsDirectories({ srcSkillsDir: srcSkills, dstSkillsDir: dstSkills })
|
|
200
131
|
const codexAgentsStats = await copyDirMerge({ srcDir: srcCodexAgents, dstDir: dstCodexAgents })
|
|
201
132
|
|
|
202
|
-
logger.success(`已初始化
|
|
203
|
-
logger.info(
|
|
204
|
-
`agents: ${agentsStats.md} 个 .md 文件` +
|
|
205
|
-
`${agentsStats.py > 0 ? ` + ${agentsStats.py} 个 .py 文件` : ''}` +
|
|
206
|
-
`${agentsStats.json > 0 ? ` + ${agentsStats.json} 个 .json 文件` : ''}`
|
|
207
|
-
)
|
|
208
|
-
logger.info(
|
|
209
|
-
`commands: ${commandsStats.md} 个 .md 文件` +
|
|
210
|
-
`${commandsStats.py > 0 ? ` + ${commandsStats.py} 个 .py 文件` : ''}` +
|
|
211
|
-
`${commandsStats.json > 0 ? ` + ${commandsStats.json} 个 .json 文件` : ''}`
|
|
212
|
-
)
|
|
133
|
+
logger.success(`已初始化 Codex 模板到: ${join(homeDir, '.codex')}`)
|
|
213
134
|
logger.info(`skills: ${skillsStats.copiedDirs} 个目录,覆盖复制 ${skillsStats.copiedFiles} 个文件 -> ${dstSkills}`)
|
|
214
135
|
logger.info(`codex agents: 覆盖复制 ${codexAgentsStats.fileCount} 个文件 -> ${dstCodexAgents}`)
|
|
215
136
|
}
|
package/lib/vercel-deploy.js
CHANGED
|
@@ -5,7 +5,7 @@ import { envManager } from './env.js'
|
|
|
5
5
|
import { logger } from './logger.js'
|
|
6
6
|
|
|
7
7
|
const ALLOWED_ENVIRONMENTS = ['development', 'staging', 'production']
|
|
8
|
-
const VALID_TARGETS = ['front', 'admin', 'telegram-bot']
|
|
8
|
+
const VALID_TARGETS = ['front', 'admin', 'merchant', 'telegram-bot']
|
|
9
9
|
|
|
10
10
|
const TARGET_CONFIGS = {
|
|
11
11
|
front: {
|
|
@@ -22,6 +22,13 @@ const TARGET_CONFIGS = {
|
|
|
22
22
|
deployMode: 'prebuilt',
|
|
23
23
|
prebuiltCwd: '.'
|
|
24
24
|
},
|
|
25
|
+
merchant: {
|
|
26
|
+
configFile: 'vercel.merchant.json',
|
|
27
|
+
projectIdEnvVar: 'VERCEL_PROJECT_ID_MERCHANT',
|
|
28
|
+
deployCwd: '.',
|
|
29
|
+
deployMode: 'prebuilt',
|
|
30
|
+
prebuiltCwd: '.'
|
|
31
|
+
},
|
|
25
32
|
'telegram-bot': {
|
|
26
33
|
configFile: 'vercel.telegram-bot.json',
|
|
27
34
|
projectIdEnvVar: 'VERCEL_PROJECT_ID_TELEGRAM_BOT',
|
|
@@ -229,31 +236,10 @@ export async function deployPrebuiltWithFallback(options) {
|
|
|
229
236
|
baseArgs,
|
|
230
237
|
env,
|
|
231
238
|
cwd,
|
|
232
|
-
run = runVercel
|
|
233
|
-
cleanupArchiveParts = () => {
|
|
234
|
-
try {
|
|
235
|
-
execSync('rm -f .vercel/source.tgz.part*', { stdio: 'ignore', cwd: cwd || process.cwd() })
|
|
236
|
-
} catch {
|
|
237
|
-
// ignore
|
|
238
|
-
}
|
|
239
|
-
},
|
|
240
|
-
onMissingFiles = () => {
|
|
241
|
-
logger.warn('检测到 missing_files,自动使用 --archive=tgz 重试一次')
|
|
242
|
-
}
|
|
239
|
+
run = runVercel
|
|
243
240
|
} = options || {}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
const result = await run(baseArgs, { env, cwd })
|
|
247
|
-
return { usedArchive: false, result }
|
|
248
|
-
} catch (e) {
|
|
249
|
-
if (!isMissingFilesError(e)) throw e
|
|
250
|
-
onMissingFiles(e)
|
|
251
|
-
cleanupArchiveParts()
|
|
252
|
-
const archiveArgs = baseArgs.slice()
|
|
253
|
-
archiveArgs.splice(2, 0, '--archive=tgz')
|
|
254
|
-
const result = await run(archiveArgs, { env, cwd })
|
|
255
|
-
return { usedArchive: true, result }
|
|
256
|
-
}
|
|
241
|
+
const result = await run(baseArgs, { env, cwd })
|
|
242
|
+
return { usedArchive: false, result }
|
|
257
243
|
}
|
|
258
244
|
|
|
259
245
|
export async function deployToVercel(target, options = {}) {
|
|
@@ -274,14 +260,14 @@ export async function deployToVercel(target, options = {}) {
|
|
|
274
260
|
}
|
|
275
261
|
|
|
276
262
|
const normalizedTarget = String(target || '').toLowerCase()
|
|
277
|
-
const targets = normalizedTarget === 'all' ? ['front', 'admin'] : [normalizedTarget]
|
|
263
|
+
const targets = normalizedTarget === 'all' ? ['front', 'admin', 'merchant'] : [normalizedTarget]
|
|
278
264
|
const projectRoot = process.cwd()
|
|
279
265
|
|
|
280
266
|
// 校验目标有效性
|
|
281
267
|
for (const t of targets) {
|
|
282
268
|
if (!VALID_TARGETS.includes(t)) {
|
|
283
269
|
logger.error(`不支持的部署目标: ${t}`)
|
|
284
|
-
logger.info('可用目标: front, admin, telegram-bot, all')
|
|
270
|
+
logger.info('可用目标: front, admin, merchant, telegram-bot, all')
|
|
285
271
|
process.exitCode = 1
|
|
286
272
|
return
|
|
287
273
|
}
|
|
@@ -306,6 +292,7 @@ export async function deployToVercel(target, options = {}) {
|
|
|
306
292
|
logger.info(' VERCEL_ORG_ID=team_xxx')
|
|
307
293
|
logger.info(' VERCEL_PROJECT_ID_FRONT=prj_xxx')
|
|
308
294
|
logger.info(' VERCEL_PROJECT_ID_ADMIN=prj_xxx')
|
|
295
|
+
logger.info(' VERCEL_PROJECT_ID_MERCHANT=prj_xxx')
|
|
309
296
|
logger.info(' VERCEL_PROJECT_ID_TELEGRAM_BOT=prj_xxx')
|
|
310
297
|
logger.info('')
|
|
311
298
|
logger.info('获取方式:')
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: review (Claude)
|
|
3
|
-
mode: subagent
|
|
4
|
-
model: openai/gpt-5.2
|
|
5
|
-
tools:
|
|
6
|
-
write: true
|
|
7
|
-
edit: false
|
|
8
|
-
bash: true
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
# PR Reviewer (Claude)
|
|
12
|
-
|
|
13
|
-
## 输入(prompt 必须包含)
|
|
14
|
-
|
|
15
|
-
- `PR #<number>`
|
|
16
|
-
- `round: <number>`
|
|
17
|
-
- `runId: <string>`(必须透传,格式 `<PR>-<ROUND>-<HEAD_SHORT>`,禁止自行生成)
|
|
18
|
-
- `contextFile: <filename>`
|
|
19
|
-
|
|
20
|
-
## 输出(强制)
|
|
21
|
-
|
|
22
|
-
只输出一行:
|
|
23
|
-
|
|
24
|
-
`reviewFile: ./.cache/<file>.md`
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
## 规则
|
|
28
|
-
|
|
29
|
-
- 默认已在 PR head 分支(可直接读工作区代码)
|
|
30
|
-
- 可用 `git`/`gh` 只读命令获取 diff/上下文
|
|
31
|
-
- 写入 reviewFile:`./.cache/review-CLD-pr<PR_NUMBER>-r<ROUND>-<RUN_ID>.md`
|
|
32
|
-
- findings id 必须以 `CLD-` 开头
|
|
33
|
-
|
|
34
|
-
## 决策日志约束(强制)
|
|
35
|
-
|
|
36
|
-
如果 prompt 中提供了 `decisionLogFile`,必须先读取并遵守以下规则:
|
|
37
|
-
|
|
38
|
-
1. **已修复问题**:不再提出本质相同的问题
|
|
39
|
-
2. **已拒绝问题**:
|
|
40
|
-
- 若你的发现 priority 比原问题高 ≥2 级(如 P3→P1, P2→P0),可以升级质疑
|
|
41
|
-
- 否则不再提出
|
|
42
|
-
|
|
43
|
-
4. **文件一致性**:
|
|
44
|
-
- 匹配 Decision Log 时,**必须检查 `file` 字段是否与当前 finding 的文件一致**。
|
|
45
|
-
- 若 decision-log 中的 `file` 与当前文件不一致(包括重命名、移动、删除),则**视为不同问题**,不进行 essence 匹配(即作为新问题处理)。
|
|
46
|
-
- 若 decision-log 条目缺少 `file` 字段,也视为不匹配。
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
### 禁止事项
|
|
50
|
-
- ⛔ 不质疑已修复问题的实现方式(除非发现修复引入了新 bug)
|
|
51
|
-
- ⛔ 不重复提出已拒绝问题(除非满足升级质疑条件)
|
|
52
|
-
|
|
53
|
-
## Cache 约定(强制)
|
|
54
|
-
|
|
55
|
-
- 缓存目录固定为 `./.cache/`;交接一律传 `./.cache/<file>`(repo 相对路径),禁止 basename-only(如 `foo.md`)。
|
|
56
|
-
|
|
57
|
-
## reviewFile 格式(强制)
|
|
58
|
-
|
|
59
|
-
```md
|
|
60
|
-
# Review (CLD)
|
|
61
|
-
|
|
62
|
-
PR: <PR_NUMBER>
|
|
63
|
-
Round: <ROUND>
|
|
64
|
-
|
|
65
|
-
## Summary
|
|
66
|
-
|
|
67
|
-
P0: <n>
|
|
68
|
-
P1: <n>
|
|
69
|
-
P2: <n>
|
|
70
|
-
P3: <n>
|
|
71
|
-
|
|
72
|
-
## Findings
|
|
73
|
-
|
|
74
|
-
- id: CLD-001
|
|
75
|
-
priority: P1
|
|
76
|
-
category: quality|performance|security|architecture
|
|
77
|
-
file: <path>
|
|
78
|
-
line: <number|null>
|
|
79
|
-
title: <short>
|
|
80
|
-
description: <text>
|
|
81
|
-
suggestion: <text>
|
|
82
|
-
```
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: review (Codex)
|
|
3
|
-
mode: subagent
|
|
4
|
-
model: openai/gpt-5.3-codex
|
|
5
|
-
temperature: 0.1
|
|
6
|
-
tools:
|
|
7
|
-
write: true
|
|
8
|
-
edit: false
|
|
9
|
-
bash: true
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
# PR Reviewer (Codex)
|
|
13
|
-
|
|
14
|
-
## 输入(prompt 必须包含)
|
|
15
|
-
|
|
16
|
-
- `PR #<number>`
|
|
17
|
-
- `round: <number>`
|
|
18
|
-
- `runId: <string>`(必须透传,格式 `<PR>-<ROUND>-<HEAD_SHORT>`,禁止自行生成)
|
|
19
|
-
- `contextFile: <filename>`
|
|
20
|
-
|
|
21
|
-
## 输出(强制)
|
|
22
|
-
|
|
23
|
-
只输出一行:
|
|
24
|
-
|
|
25
|
-
`reviewFile: ./.cache/<file>.md`
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
## 规则
|
|
29
|
-
|
|
30
|
-
- 默认已在 PR head 分支(可直接读工作区代码)
|
|
31
|
-
- 可用 `git`/`gh` 只读命令获取 diff/上下文
|
|
32
|
-
- 写入 reviewFile:`./.cache/review-CDX-pr<PR_NUMBER>-r<ROUND>-<RUN_ID>.md`
|
|
33
|
-
- findings id 必须以 `CDX-` 开头
|
|
34
|
-
|
|
35
|
-
## 决策日志约束(强制)
|
|
36
|
-
|
|
37
|
-
如果 prompt 中提供了 `decisionLogFile`,必须先读取并遵守以下规则:
|
|
38
|
-
|
|
39
|
-
1. **已修复问题**:不再提出本质相同的问题
|
|
40
|
-
2. **已拒绝问题**:
|
|
41
|
-
- 若你的发现 priority 比原问题高 ≥2 级(如 P3→P1, P2→P0),可以升级质疑
|
|
42
|
-
- 否则不再提出
|
|
43
|
-
|
|
44
|
-
4. **文件一致性**:
|
|
45
|
-
- 匹配 Decision Log 时,**必须检查 `file` 字段是否与当前 finding 的文件一致**。
|
|
46
|
-
- 若 decision-log 中的 `file` 与当前文件不一致(包括重命名、移动、删除),则**视为不同问题**,不进行 essence 匹配(即作为新问题处理)。
|
|
47
|
-
- 若 decision-log 条目缺少 `file` 字段,也视为不匹配。
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
### 禁止事项
|
|
51
|
-
- ⛔ 不质疑已修复问题的实现方式(除非发现修复引入了新 bug)
|
|
52
|
-
- ⛔ 不重复提出已拒绝问题(除非满足升级质疑条件)
|
|
53
|
-
|
|
54
|
-
## Cache 约定(强制)
|
|
55
|
-
|
|
56
|
-
- 缓存目录固定为 `./.cache/`;交接一律传 `./.cache/<file>`(repo 相对路径),禁止 basename-only(如 `foo.md`)。
|
|
57
|
-
|
|
58
|
-
## reviewFile 格式(强制)
|
|
59
|
-
|
|
60
|
-
```md
|
|
61
|
-
# Review (CDX)
|
|
62
|
-
|
|
63
|
-
PR: <PR_NUMBER>
|
|
64
|
-
Round: <ROUND>
|
|
65
|
-
|
|
66
|
-
## Summary
|
|
67
|
-
|
|
68
|
-
P0: <n>
|
|
69
|
-
P1: <n>
|
|
70
|
-
P2: <n>
|
|
71
|
-
P3: <n>
|
|
72
|
-
|
|
73
|
-
## Findings
|
|
74
|
-
|
|
75
|
-
- id: CDX-001
|
|
76
|
-
priority: P1
|
|
77
|
-
category: quality|performance|security|architecture
|
|
78
|
-
file: <path>
|
|
79
|
-
line: <number|null>
|
|
80
|
-
title: <short>
|
|
81
|
-
description: <text>
|
|
82
|
-
suggestion: <text>
|
|
83
|
-
```
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: review (Gemini)
|
|
3
|
-
mode: subagent
|
|
4
|
-
model: openai/gpt-5.1-codex
|
|
5
|
-
tools:
|
|
6
|
-
write: true
|
|
7
|
-
edit: false
|
|
8
|
-
bash: true
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
# PR Reviewer (Gemini)
|
|
12
|
-
|
|
13
|
-
## 输入(prompt 必须包含)
|
|
14
|
-
|
|
15
|
-
- `PR #<number>`
|
|
16
|
-
- `round: <number>`
|
|
17
|
-
- `runId: <string>`(必须透传,格式 `<PR>-<ROUND>-<HEAD_SHORT>`,禁止自行生成)
|
|
18
|
-
- `contextFile: <filename>`
|
|
19
|
-
|
|
20
|
-
## 输出(强制)
|
|
21
|
-
|
|
22
|
-
只输出一行:
|
|
23
|
-
|
|
24
|
-
`reviewFile: ./.cache/<file>.md`
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
## 规则
|
|
28
|
-
|
|
29
|
-
- 默认已在 PR head 分支(可直接读工作区代码)
|
|
30
|
-
- 可用 `git`/`gh` 只读命令获取 diff/上下文
|
|
31
|
-
- 写入 reviewFile:`./.cache/review-GMN-pr<PR_NUMBER>-r<ROUND>-<RUN_ID>.md`
|
|
32
|
-
- findings id 必须以 `GMN-` 开头
|
|
33
|
-
|
|
34
|
-
## 决策日志约束(强制)
|
|
35
|
-
|
|
36
|
-
如果 prompt 中提供了 `decisionLogFile`,必须先读取并遵守以下规则:
|
|
37
|
-
|
|
38
|
-
1. **已修复问题**:不再提出本质相同的问题
|
|
39
|
-
2. **已拒绝问题**:
|
|
40
|
-
- 若你的发现 priority 比原问题高 ≥2 级(如 P3→P1, P2→P0),可以升级质疑
|
|
41
|
-
- 否则不再提出
|
|
42
|
-
|
|
43
|
-
4. **文件一致性**:
|
|
44
|
-
- 匹配 Decision Log 时,**必须检查 `file` 字段是否与当前 finding 的文件一致**。
|
|
45
|
-
- 若 decision-log 中的 `file` 与当前文件不一致(包括重命名、移动、删除),则**视为不同问题**,不进行 essence 匹配(即作为新问题处理)。
|
|
46
|
-
- 若 decision-log 条目缺少 `file` 字段,也视为不匹配。
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
### 禁止事项
|
|
50
|
-
- ⛔ 不质疑已修复问题的实现方式(除非发现修复引入了新 bug)
|
|
51
|
-
- ⛔ 不重复提出已拒绝问题(除非满足升级质疑条件)
|
|
52
|
-
|
|
53
|
-
## Cache 约定(强制)
|
|
54
|
-
|
|
55
|
-
- 缓存目录固定为 `./.cache/`;交接一律传 `./.cache/<file>`(repo 相对路径),禁止 basename-only(如 `foo.md`)。
|
|
56
|
-
|
|
57
|
-
## reviewFile 格式(强制)
|
|
58
|
-
|
|
59
|
-
```md
|
|
60
|
-
# Review (GMN)
|
|
61
|
-
|
|
62
|
-
PR: <PR_NUMBER>
|
|
63
|
-
Round: <ROUND>
|
|
64
|
-
|
|
65
|
-
## Summary
|
|
66
|
-
|
|
67
|
-
P0: <n>
|
|
68
|
-
P1: <n>
|
|
69
|
-
P2: <n>
|
|
70
|
-
P3: <n>
|
|
71
|
-
|
|
72
|
-
## Findings
|
|
73
|
-
|
|
74
|
-
- id: GMN-001
|
|
75
|
-
priority: P1
|
|
76
|
-
category: quality|performance|security|architecture
|
|
77
|
-
file: <path>
|
|
78
|
-
line: <number|null>
|
|
79
|
-
title: <short>
|
|
80
|
-
description: <text>
|
|
81
|
-
suggestion: <text>
|
|
82
|
-
```
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: review (GitHub Harvest)
|
|
3
|
-
mode: subagent
|
|
4
|
-
model: openai/gpt-5.3-codex
|
|
5
|
-
temperature: 0.2
|
|
6
|
-
tools:
|
|
7
|
-
write: true
|
|
8
|
-
edit: false
|
|
9
|
-
bash: true
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
# PR Reviewer (GitHub Harvest)
|
|
13
|
-
|
|
14
|
-
Harvest all GitHub PR review feedback (humans + bots, including Copilot) and normalize into a standard `reviewFile`.
|
|
15
|
-
|
|
16
|
-
## 输入(prompt 必须包含)
|
|
17
|
-
|
|
18
|
-
- `PR #<number>`
|
|
19
|
-
- `round: <number>`
|
|
20
|
-
- `runId: <string>`(必须透传,格式 `<PR>-<ROUND>-<HEAD_SHORT>`,禁止自行生成)
|
|
21
|
-
- `contextFile: <filename>`
|
|
22
|
-
|
|
23
|
-
## Cache 约定(强制)
|
|
24
|
-
|
|
25
|
-
- 缓存目录固定为 `./.cache/`;交接一律传 `./.cache/<file>`(repo 相对路径),禁止 basename-only(如 `foo.md`)。
|
|
26
|
-
|
|
27
|
-
## 输出(强制)
|
|
28
|
-
|
|
29
|
-
只输出一行:
|
|
30
|
-
|
|
31
|
-
`reviewFile: ./.cache/<file>.md`
|
|
32
|
-
|
|
33
|
-
## reviewFile 格式(强制)
|
|
34
|
-
|
|
35
|
-
```md
|
|
36
|
-
# Review (GHR)
|
|
37
|
-
|
|
38
|
-
PR: <PR_NUMBER>
|
|
39
|
-
Round: <ROUND>
|
|
40
|
-
|
|
41
|
-
## Summary
|
|
42
|
-
|
|
43
|
-
P0: <n>
|
|
44
|
-
P1: <n>
|
|
45
|
-
P2: <n>
|
|
46
|
-
P3: <n>
|
|
47
|
-
|
|
48
|
-
## Findings
|
|
49
|
-
|
|
50
|
-
- id: GHR-RC-2752827557
|
|
51
|
-
priority: P1
|
|
52
|
-
category: quality|performance|security|architecture
|
|
53
|
-
file: <path>
|
|
54
|
-
line: <number|null>
|
|
55
|
-
title: <short>
|
|
56
|
-
description: <single-line text>
|
|
57
|
-
suggestion: <single-line text>
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
## ID 规则(强制)
|
|
61
|
-
|
|
62
|
-
- Inline 评审(discussion_r...):`GHR-RC-<databaseId>`(databaseId 可映射到 `#discussion_r<databaseId>`)
|
|
63
|
-
- PR Review 总评:`GHR-RV-<reviewId>`
|
|
64
|
-
- PR 普通评论:`GHR-IC-<issueCommentId>`
|
|
65
|
-
|
|
66
|
-
## 执行步骤(强制)
|
|
67
|
-
|
|
68
|
-
1) Harvest(确定性)
|
|
69
|
-
|
|
70
|
-
- 调用脚本生成 raw JSON:
|
|
71
|
-
|
|
72
|
-
```bash
|
|
73
|
-
python3 ~/.opencode/agents/gh_review_harvest.py \
|
|
74
|
-
--pr <PR_NUMBER> \
|
|
75
|
-
--round <ROUND> \
|
|
76
|
-
--run-id <RUN_ID>
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
- 脚本 stdout 会输出一行 JSON:`{"rawFile":"./.cache/...json"}`,从中取 `rawFile`。
|
|
80
|
-
|
|
81
|
-
2) Normalize(LLM 分类)
|
|
82
|
-
|
|
83
|
-
- 读取 `rawFile`(JSON)后,提取“建议/问题”并生成 findings:
|
|
84
|
-
- 覆盖 humans + bots(不做作者白名单)。
|
|
85
|
-
- 忽略纯审批/无内容:如 `LGTM`、`Looks good`、`Approved` 等。
|
|
86
|
-
- 分类规则(大致):
|
|
87
|
-
- P0: 明确安全漏洞/数据泄漏/资金损失/远程执行
|
|
88
|
-
- P1: 逻辑 bug/权限绕过/会导致线上错误
|
|
89
|
-
- P2: 潜在 bug/鲁棒性/边界条件/可维护性重大问题
|
|
90
|
-
- P3: 风格/命名/小优化/可选建议
|
|
91
|
-
- `category` 只能取:quality|performance|security|architecture
|
|
92
|
-
|
|
93
|
-
3) 写入 reviewFile
|
|
94
|
-
|
|
95
|
-
- 文件名固定:`./.cache/review-GHR-pr<PR_NUMBER>-r<ROUND>-<RUN_ID>.md`
|
|
96
|
-
- 重要:`title/description/suggestion` 必须是单行;原文有换行时用 `\\n` 转义。
|
|
97
|
-
|
|
98
|
-
## 禁止事项(强制)
|
|
99
|
-
|
|
100
|
-
- ⛔ 不发布 GitHub 评论(不调用 `gh pr comment/review`)
|
|
101
|
-
- ⛔ 不修改代码(只输出 reviewFile)
|
|
102
|
-
- ⛔ 不生成/伪造 runId
|
|
103
|
-
|
|
104
|
-
## 决策日志约束(强制)
|
|
105
|
-
|
|
106
|
-
如果 prompt 中提供了 `decisionLogFile`,必须先读取并遵守以下规则:
|
|
107
|
-
|
|
108
|
-
1. **已修复问题**:不再提出本质相同的问题
|
|
109
|
-
2. **已拒绝问题**:
|
|
110
|
-
- 若你的发现 priority 比原问题高 ≥2 级(如 P3→P1, P2→P0),可以升级质疑
|
|
111
|
-
- 否则不再提出
|
|
112
|
-
|
|
113
|
-
3. **文件一致性**:
|
|
114
|
-
- 匹配 Decision Log 时,**必须检查 `file` 字段是否与当前 finding 的文件一致**。
|
|
115
|
-
- 若 decision-log 中的 `file` 与当前文件不一致(包括重命名、移动、删除),则**视为不同问题**,不进行 essence 匹配(即作为新问题处理)。
|
|
116
|
-
- 若 decision-log 条目缺少 `file` 字段,也视为不匹配。
|
|
117
|
-
|
|
118
|
-
判断"问题本质相同"时,比对 decision-log 中的 `essence` 字段与你发现的问题描述。
|
|
119
|
-
|
|
120
|
-
### 禁止事项
|
|
121
|
-
- ⛔ 不质疑已修复问题的实现方式(除非发现修复引入了新 bug)
|
|
122
|
-
- ⛔ 不重复提出已拒绝问题(除非满足升级质疑条件)
|