@ranger1/dx 0.1.30 → 0.1.32

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.
@@ -33,3 +33,14 @@ tools:
33
33
  ```bash
34
34
  python3 ~/.opencode/agents/pr_context.py --pr <PR_NUMBER> --round <ROUND>
35
35
  ```
36
+
37
+ ## 脚本输出处理(强制)
38
+
39
+ - 脚本 stdout 只会输出**单一一行 JSON**(可 `JSON.parse()`)。
40
+ - **成功时**:你的最终输出必须是**脚本 stdout 的那一行 JSON 原样内容**。
41
+ - 禁止:解释/分析/补充文字
42
+ - 禁止:代码块(```)
43
+ - 禁止:前后空行
44
+ - **失败/异常时**:
45
+ - 若脚本 stdout 已输出合法 JSON(包含 `error` 或其他字段)→ 仍然**原样返回该 JSON**。
46
+ - 若脚本未输出合法 JSON / 退出异常 → 仅输出一行 JSON:`{"error":"PR_CONTEXT_AGENT_FAILED"}`(必要时可加 `detail` 字段)。
@@ -26,6 +26,18 @@ tools:
26
26
  python3 ~/.opencode/agents/pr_precheck.py <PR_NUMBER>
27
27
  ```
28
28
 
29
+ ## 脚本输出处理(强制)
30
+
31
+ - 脚本 stdout 只会输出**单一一行 JSON**(可 `JSON.parse()`)。
32
+ - **成功时**:你的最终输出必须是**脚本 stdout 的那一行 JSON 原样内容**。
33
+ - 典型返回:`{"ok":true}` 或 `{"ok":false,"fixFile":"..."}`
34
+ - 禁止:解释/分析/补充文字
35
+ - 禁止:代码块(```)
36
+ - 禁止:前后空行
37
+ - **失败/异常时**:
38
+ - 若脚本 stdout 已输出合法 JSON(包含 `error` 或其他字段)→ 仍然**原样返回该 JSON**。
39
+ - 若脚本未输出合法 JSON / 退出异常 → 仅输出一行 JSON:`{"error":"PR_PRECHECK_AGENT_FAILED"}`(必要时可加 `detail` 字段)。
40
+
29
41
  ## 仅当出现 merge 冲突时怎么处理
30
42
 
31
43
  当脚本输出 `{"error":"PR_MERGE_CONFLICTS_UNRESOLVED"}` 时:
@@ -46,11 +46,16 @@ reviewFile: review-GMN-pr123-r1-abcdef123456.md
46
46
 
47
47
  所有确定性工作(解析/聚合/发评论/生成 fixFile/输出 JSON)都由 `~/.opencode/agents/pr_review_aggregate.py` 完成。
48
48
 
49
- 你只做一件事:在模式 A 里用大模型判断哪些 finding 是重复的,并把重复分组作为参数传给脚本(不落盘)。
49
+ 你只做两件事:
50
50
 
51
- ## 重复分组(给大模型输出)
51
+ 1) 在模式 A 里用大模型判断哪些 finding 是重复的,并把重复分组作为参数传给脚本(不落盘)。
52
+ 2) 调用脚本后,把脚本 stdout 的 JSON **原样返回**给调用者(不做解释/分析)。
52
53
 
53
- 大模型只输出一行 JSON(不要代码块、不要解释文字、不要换行):
54
+ ## 重复分组(仅作为脚本入参)
55
+
56
+ 你需要基于 3 份 `reviewFile` 内容判断重复 finding 分组,生成**一行 JSON**(不要代码块、不要解释文字、不要换行)。
57
+
58
+ 注意:这行 JSON **不是你的最终输出**,它只用于生成 `--duplicate-groups-b64` 传给脚本。
54
59
 
55
60
  ```json
56
61
  {"duplicateGroups":[["CDX-001","CLD-003"],["GMN-002","CLD-005","CDX-004"]]}
@@ -81,3 +86,15 @@ python3 ~/.opencode/agents/pr_review_aggregate.py \
81
86
  --run-id <RUN_ID> \
82
87
  --fix-report-file <FIX_REPORT_FILE>
83
88
  ```
89
+
90
+ ## 脚本输出处理(强制)
91
+
92
+ - 脚本 stdout 只会输出**单一一行 JSON**(可 `JSON.parse()`)。
93
+ - **成功时**:你的最终输出必须是**脚本 stdout 的那一行 JSON 原样内容**。
94
+ - 典型返回:`{"stop":true}` 或 `{"stop":false,"fixFile":"..."}` 或 `{"ok":true}`
95
+ - 禁止:解释/分析/补充文字
96
+ - 禁止:代码块(```)
97
+ - 禁止:前后空行
98
+ - **失败/异常时**:
99
+ - 若脚本 stdout 已输出合法 JSON(包含 `error` 或其他字段)→ 仍然**原样返回该 JSON**。
100
+ - 若脚本未输出合法 JSON / 退出异常 → 仅输出一行 JSON:`{"error":"PR_REVIEW_AGGREGATE_AGENT_FAILED"}`(必要时可加 `detail` 字段)。
@@ -4,7 +4,7 @@ import os from 'node:os'
4
4
 
5
5
  import { logger } from './logger.js'
6
6
 
7
- async function collectMarkdownFiles(dir) {
7
+ async function collectTemplateFiles(dir) {
8
8
  const out = []
9
9
 
10
10
  async function walk(current) {
@@ -18,11 +18,19 @@ async function collectMarkdownFiles(dir) {
18
18
  for (const entry of entries) {
19
19
  const full = join(current, entry.name)
20
20
  if (entry.isDirectory()) {
21
+ // 跳过 __pycache__ 和其他缓存目录
22
+ if (entry.name === '__pycache__' || entry.name === '.pytest_cache') {
23
+ continue
24
+ }
21
25
  await walk(full)
22
26
  continue
23
27
  }
24
28
  if (!entry.isFile()) continue
25
- if (!entry.name.toLowerCase().endsWith('.md')) continue
29
+ const lowerName = entry.name.toLowerCase()
30
+ // 拷贝 .md 和 .py 文件
31
+ if (!lowerName.endsWith('.md') && !lowerName.endsWith('.py')) continue
32
+ // 跳过 Python 编译文件
33
+ if (lowerName.endsWith('.pyc') || lowerName.endsWith('.pyo') || lowerName.endsWith('.pyd')) continue
26
34
  out.push(full)
27
35
  }
28
36
  }
@@ -47,15 +55,30 @@ async function assertDirExists(path, label) {
47
55
  }
48
56
  }
49
57
 
50
- async function copyMarkdownTree({ srcDir, dstDir }) {
51
- const files = await collectMarkdownFiles(srcDir)
58
+ async function copyTemplateTree({ srcDir, dstDir }) {
59
+ const files = await collectTemplateFiles(srcDir)
60
+ let mdCount = 0
61
+ let pyCount = 0
62
+
52
63
  for (const file of files) {
53
64
  const rel = relative(srcDir, file)
54
65
  const target = join(dstDir, rel)
55
66
  await ensureDir(dirname(target))
56
67
  await fs.copyFile(file, target)
68
+
69
+ if (file.endsWith('.md')) {
70
+ mdCount++
71
+ } else if (file.endsWith('.py')) {
72
+ pyCount++
73
+ try {
74
+ await fs.chmod(target, 0o755)
75
+ } catch {
76
+ // 忽略权限设置失败
77
+ }
78
+ }
57
79
  }
58
- return files.length
80
+
81
+ return { total: files.length, md: mdCount, py: pyCount }
59
82
  }
60
83
 
61
84
  function resolveTemplateRoot(packageRoot) {
@@ -98,10 +121,10 @@ export async function runOpenCodeInitial(options = {}) {
98
121
  await ensureDir(dstAgents)
99
122
  await ensureDir(dstCommands)
100
123
 
101
- const agentsCount = await copyMarkdownTree({ srcDir: srcAgents, dstDir: dstAgents })
102
- const commandsCount = await copyMarkdownTree({ srcDir: srcCommands, dstDir: dstCommands })
124
+ const agentsStats = await copyTemplateTree({ srcDir: srcAgents, dstDir: dstAgents })
125
+ const commandsStats = await copyTemplateTree({ srcDir: srcCommands, dstDir: dstCommands })
103
126
 
104
127
  logger.success(`已初始化 OpenCode 模板到: ${dstRoot}`)
105
- logger.info(`agents: ${agentsCount} 个 .md 文件`)
106
- logger.info(`commands: ${commandsCount} 个 .md 文件`)
128
+ logger.info(`agents: ${agentsStats.md} 个 .md 文件${agentsStats.py > 0 ? ` + ${agentsStats.py} 个 .py 文件` : ''}`)
129
+ logger.info(`commands: ${commandsStats.md} 个 .md 文件${commandsStats.py > 0 ? ` + ${commandsStats.py} 个 .py 文件` : ''}`)
107
130
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ranger1/dx",
3
- "version": "0.1.30",
3
+ "version": "0.1.32",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {