@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.
Files changed (36) hide show
  1. package/README.md +92 -31
  2. package/bin/dx.js +3 -3
  3. package/lib/cli/commands/deploy.js +2 -1
  4. package/lib/cli/commands/stack.js +198 -237
  5. package/lib/cli/commands/start.js +0 -6
  6. package/lib/cli/dx-cli.js +10 -1
  7. package/lib/cli/help.js +8 -7
  8. package/lib/{opencode-initial.js → codex-initial.js} +3 -82
  9. package/lib/vercel-deploy.js +14 -27
  10. package/package.json +1 -2
  11. package/@opencode/agents/__pycache__/gh_review_harvest.cpython-314.pyc +0 -0
  12. package/@opencode/agents/__pycache__/pr_context.cpython-314.pyc +0 -0
  13. package/@opencode/agents/__pycache__/pr_precheck.cpython-314.pyc +0 -0
  14. package/@opencode/agents/__pycache__/pr_review_aggregate.cpython-314.pyc +0 -0
  15. package/@opencode/agents/__pycache__/test_pr_review_aggregate.cpython-314-pytest-9.0.2.pyc +0 -0
  16. package/@opencode/agents/__pycache__/test_pr_review_aggregate.cpython-314.pyc +0 -0
  17. package/@opencode/agents/claude-reviewer.md +0 -82
  18. package/@opencode/agents/codex-reviewer.md +0 -83
  19. package/@opencode/agents/gemini-reviewer.md +0 -82
  20. package/@opencode/agents/gh-thread-reviewer.md +0 -122
  21. package/@opencode/agents/gh_review_harvest.py +0 -292
  22. package/@opencode/agents/pr-context.md +0 -82
  23. package/@opencode/agents/pr-fix.md +0 -243
  24. package/@opencode/agents/pr-precheck.md +0 -89
  25. package/@opencode/agents/pr-review-aggregate.md +0 -151
  26. package/@opencode/agents/pr_context.py +0 -351
  27. package/@opencode/agents/pr_precheck.py +0 -505
  28. package/@opencode/agents/pr_review_aggregate.py +0 -868
  29. package/@opencode/agents/test_pr_review_aggregate.py +0 -701
  30. package/@opencode/commands/doctor.md +0 -271
  31. package/@opencode/commands/git-commit-and-pr.md +0 -282
  32. package/@opencode/commands/git-release.md +0 -642
  33. package/@opencode/commands/oh_attach.json +0 -92
  34. package/@opencode/commands/opencode_attach.json +0 -29
  35. package/@opencode/commands/opencode_attach.py +0 -142
  36. 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: backend, front, admin, all, dev, stack, stagewise-front, stagewise-admin (默认: dev)',
15
- ' stack: PM2 交互式服务栈管理(推荐)- 同时启动三个服务并提供交互式命令',
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 初始化 OpenCode 模板到 ~/.opencode,并同步 codex 模板到 ~/.codex(覆盖同名文件)',
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 resolveTemplateRoot(packageRoot) {
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('runOpenCodeInitial: 缺少 packageRoot')
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(`已初始化 OpenCode 模板到: ${dstRoot}`)
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
  }
@@ -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
- try {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ranger1/dx",
3
- "version": "0.1.76",
3
+ "version": "0.1.78",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -18,7 +18,6 @@
18
18
  "files": [
19
19
  "bin/",
20
20
  "lib/",
21
- "@opencode/",
22
21
  "codex/",
23
22
  "LICENSE",
24
23
  "README.md",
@@ -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
- - ⛔ 不重复提出已拒绝问题(除非满足升级质疑条件)