@feng0531/dsh-code-review 0.70.0 → 0.72.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/CHANGELOG.md CHANGED
@@ -2,9 +2,17 @@
2
2
 
3
3
  本文件介绍对使用者有意义的变化。安装与参数见 [README](README.md),本次发布正文见 [发布说明](RELEASE-NOTES.md)。历史提交和安装包保留在 [GitHub Releases](https://github.com/f-e-n-g-0531/dsh-code-review/releases);本文不把开发日志当作功能介绍。
4
4
 
5
+ ## 0.72.0 — 使用指南与 SVN 固定历史换行元数据
6
+
7
+ README 改为可直接操作的五步流程,补齐 Git/SVN 场景、确认执行、报告阅读与排错示例。固定 SVN 历史变更文件新增逐侧 EOL 元数据,使用原版本正文和身份,不新增读取命令;缺失/目录侧明确 null。历史上下文映射及其他 SVN 缺口仍未闭环。
8
+
9
+ ## 0.71.0 — SVN 工作副本换行映射元数据
10
+
11
+ 有 svn:eol-style 的变更文件在预览和模型输入中披露双侧换行计数、样式和 LF 视图哈希,原文与原始字节证据保持不变。CRLF/LF 保持 LF 证据行号,单 CR 明确不是规范化 LF 行号的 identity 映射。混合换行不被静默改写,哈希相同不证明运行时等价。非法样式或 SVN 自身不可读取时明确失败。固定历史模式和上下文规范化不在本批范围,全面 SVN 目标尚未完成。507项串行全量回归、65项SVN专项、实际宿主与隔离安装、四矩阵CI通过;原始Actions/npm/Release 139,804字节逐字节一致,双哈希、清单与冻结发布说明核验通过。
12
+
5
13
  ## 0.70.0 — SVN 工作副本标准关键字原文审查
6
14
 
7
- 标准 svn:keywords 文件支持行保持映射核验,BASE 收缩与未展开仓库正文比较,原正文/原hash不替换。预览披露映射元数据,模型仍使用原始展开源码、原行号和明确提示,不以关键字变化直接推定缺陷或行为等价。自定义、固定宽度与未闭合展开继续阻断;关键字上下文导航及换行规范化仍待实现。
15
+ 标准 svn:keywords 文件支持行保持映射核验,BASE 收缩与未展开仓库正文比较,原正文/原hash不替换。预览披露映射元数据,模型仍使用原始展开源码、原行号和明确提示,不以关键字变化直接推定缺陷或行为等价。自定义、固定宽度与未闭合展开继续阻断;关键字上下文导航及换行规范化仍待实现。504 项串行回归、宿主、隔离安装和四矩阵 CI 通过;原产物/npm/Release 137,678 字节逐字节一致,双哈希、清单与冻结发布说明通过。
8
16
 
9
17
  ## 0.69.0 — SVN 固定历史限定调用方发现
10
18
 
@@ -25,14 +25,16 @@
25
25
 
26
26
  - 继承、原型链与动态分派:需要语言解析器或类型检查器做语义绑定,超出「只读批准快照、不放宽预算」的保守边界。
27
27
  - 完整语义调用方、别名与跨目录实例上下文:需要语义解析器;保守的命名/默认/命名空间成员/类实例方法导航已交付,其余不猜测。
28
- - SVN 全面支持扩展:用户已要求将工作副本特殊状态与固定 revision 历史审查纳入新目标。当前历史/旧侧三层链、oldContextPaths 和删除旧正文复用仅是 Git 已交付能力,不能算作 SVN 支持。SVN 当前缺口、批次与验收见 [SVN 支持矩阵](SVN-SUPPORT-MATRIX.md)。
28
+ - SVN 全面支持扩展:用户已要求将工作副本特殊状态与固定 revision 历史审查纳入新目标。固定 SVN 历史、oldContextPaths、三层依赖与限定调用方已分批交付;不能因此推断特殊工作副本状态、跨 URL 或有效继承 mergeinfo 已支持。SVN 当前缺口、批次与验收见 [SVN 支持矩阵](SVN-SUPPORT-MATRIX.md)。
29
29
  - 真实模型质量评测:目标明确后置,保存真实输出、人工裁决、分开统计,不作为对齐前提。
30
30
 
31
31
  各版交付结论见「当前验证结论」与 [版本变化](CHANGELOG.md)。
32
32
 
33
33
  ## 当前验证结论
34
34
 
35
- - 最近完整回归:502 项通过。
35
+ - 最近完整回归:507 项通过;冻结 v0.71.0 SVN 专项65项通过。
36
+ - v0.71.0 已发布工作副本 EOL 纯元数据映射,原文/原字节证据保持,单CR明确不套用规范化行号。实际宿主、隔离安装和四矩阵CI通过;原始Actions/npm/Release 139,804字节逐字节一致,双哈希、清单和冻结正文核验通过。历史EOL元数据、关键字上下文、未提交替换和跨URL等继续待闭环。
37
+ - v0.70.0 已发布标准 SVN 工作副本关键字原文审查,BASE收缩独立核验、原正文/hash/行号不替换,预览披露映射。504 项串行回归、宿主、隔离安装和四矩阵 CI 通过;原产物/npm/Release 137,678 字节逐字节一致,双哈希、清单与冻结发布说明通过。自定义/固定宽度关键字、换行规范化、未提交替换、跨URL分支等缺口仍未闭环。
36
38
  - v0.69.0 已发布固定 SVN 历史限定调用方与autoContext组合,扫描两侧候选、仅命中侧正文捕获,同侧声明导航,统一采集/来源/探测预算;字面发现仍明确不完整。502 项串行回归、宿主、隔离安装和四矩阵 CI 通过;原产物/npm/Release 136,030 字节逐字节一致,双哈希、清单与冻结发布说明通过。全面 SVN 目标仍未完成。
37
39
  - v0.68.0 已交付固定 SVN 历史三层 autoContext,逐侧正文/来源固定、父目录有界清单确认精确节点,不吞访问失败,不读工作树。496 项串行回归、宿主、隔离安装和四矩阵 CI 通过;原产物/npm/Release 134,056 字节逐字节一致,双哈希、清单与冻结发布说明通过。历史调用方和全面 SVN 其他缺口继续。
38
40
  - v0.67.0 已交付 SVN 工作副本 BASE/工作侧 mergeinfo 的有界显式导航、预览与报告披露,不扩大来源读取或将导航视为审查证明。489 项串行回归、宿主、隔离安装和四矩阵 CI 通过;原产物/npm/Release 130,866 字节逐字节一致,双哈希、清单与冻结发布说明通过。全面 SVN 目标仍未完成。
package/README.md CHANGED
@@ -1,121 +1,252 @@
1
1
  # DSH Code Review
2
2
 
3
- DSH 的只读代码审查插件。它比较 Git 或 SVN 中的代码变更,使用当前 Agent 的模型分析问题,并返回带文件位置、证据和复核结果的中文报告。
3
+ 在 **DeepSeek Harness(DSH)的 Agent 对话里**审查 Git / SVN 代码变更。插件会先列出审查范围,得到确认后,再用当前 Agent 选择的模型生成中文问题报告。
4
4
 
5
- **不会修改代码、自动修复、提交、回滚或发布业务项目。模型报告仍需人工判断。**
5
+ **只读:不修改代码,不自动修复,不提交、回滚或更新你的业务工作副本。**
6
6
 
7
- ## 安装
7
+ ## 快速开始:第一次怎么用
8
8
 
9
- 环境要求:Node.js 22 或更新版本,以及目标仓库使用的 Git 或 SVN 命令行工具。插件依赖 DSH 的工具和模型服务,不能脱离 DSH 单独运行。
9
+ ### 1. 安装并启用插件
10
10
 
11
- 在 DSH 中通过插件管理器安装并启用,或使用:
11
+ 在 DSH 插件管理器中安装并启用本项目,或在终端执行:
12
12
 
13
- dsh plugin add https://github.com/f-e-n-g-0531/dsh-code-review.git
13
+ ```sh
14
+ dsh plugin add https://github.com/f-e-n-g-0531/dsh-code-review.git
15
+ ```
14
16
 
15
- 升级后确认当前 DSH 进程已经加载新版本。仓库发布新版本不会自动更新正在运行的 DSH;本项目也不会自动修改已有 profile 或重启宿主。
17
+ 需要 Node.js 22+,以及已安装的 Git 或 SVN 命令行工具。插件依赖 DSH,**不是独立命令行审查程序**,不能用 `npm test` 或直接运行源码来发起审查。
16
18
 
17
- > 许可限制:本项目采用非商业源码公开许可证。公司内部商业项目、收费服务及商业交付需要另行取得书面授权。详见 [LICENSE](LICENSE)。
19
+ 安装或升级后,确认当前 DSH 已加载插件。仓库发布新版本不会自动升级正在运行的插件;如当前会话仍未获得新工具,按 DSH 的插件加载提示重新加载或新建会话。
18
20
 
19
- ## 第一次使用
21
+ ### 2. 在目标代码仓库创建 Agent 会话
20
22
 
21
- 在目标仓库的 DSH Agent 会话中,可以这样提出请求:
23
+ 将会话的工作目录设为要审查的 **Git 仓库根目录或 SVN 工作副本根目录**,并在 DSH 中选好模型。不需要另给本插件配置 API Key。
22
24
 
23
- > 审查 src/api.ts 和 src/client.ts 的改动。先展示审查范围和模型目的地,确认后再执行。不要修改代码。
25
+ 例如,你的 SVN 工作副本是 `D:/work/my-project`,就以该目录作为会话工作目录。不要把网页地址或仓库远端 URL 当作本地工作目录。
24
26
 
25
- SVN 工作副本的标准 `svn:keywords` 展开可审查:BASE 收缩须与仓库未展开正文一致,预览披露逐行 identity 映射;模型与证据始终使用原始展开正文和行号,不把规范化结果当行为等价证明。自定义配置、固定宽度和未闭合展开仍拒绝,关键字上下文导航未因此放开。
27
+ 如果会话工作区中有多个子仓库,见下方“多个仓库”示例。
26
28
 
27
- 插件提供两个工具:
29
+ ### 3. 把这段话发给 Agent
28
30
 
29
- 1. **code_review_preview**:读取本地变更,列出审查文件、额外上下文、排除原因和模型目的地。此步骤不调用模型。
30
- 2. **code_review_execute**:接收预览返回的 previewId 和 confirmed: true。执行前重新检查文件;只有预览仍有效且宿主允许执行时,才把批准内容发送给当前 Agent 模型。
31
+ ```text
32
+ 使用 code_review_preview 预览当前仓库所有已跟踪文件的改动。
33
+ 先告诉我:审查哪个仓库、哪些文件、额外读取哪些上下文、发送给哪个模型,以及哪些文件不能审查。
34
+ 先不要调用 code_review_execute,等我确认。全程不要修改代码。
35
+ ```
31
36
 
32
- 调用示例:
37
+ 这是发在 **DSH 对话输入框**里的请求,不是终端命令。Git 默认比较 HEAD 与当前工作区的净变化;SVN 默认比较本地 BASE(工作副本记录的基础版本)与当前文件。
33
38
 
34
- {"selectedPaths":["src/api.ts","src/client.ts"],"autoContext":true}
39
+ > 预览不调用审查模型,但 Agent 对话本身仍可能使用模型。工作副本模式默认不读 SVN 远端;SVN 历史模式会读取仓库版本,必须另行明确允许远端访问。
35
40
 
36
- 执行参数为:
41
+ ### 4. 检查预览,再确认执行
37
42
 
38
- {"previewId":"预览返回的ID","confirmed":true}
43
+ 检查 Agent 展示的:
39
44
 
40
- 执行返回中文 Markdown 报告,以及 JSON 格式的结构化报告。代码、会话目录或模型路由变化后,需要重新预览。
45
+ - **仓库和文件**是否选对,有没有遗漏或意外包含的文件。
46
+ - **额外上下文**是什么;上下文是辅助理解的文件,也可能随执行发送给模型。
47
+ - **模型提供方和模型名称**是否符合你的数据使用要求。
48
+ - **阻断、排除和预算限制**;不能审查的文件不能当作已通过。
41
49
 
42
- ## 可以审查什么
50
+ 确认无误后,在同一会话回复:
43
51
 
44
- | 场景 | 支持情况 |
45
- |---|---|
46
- | Git 工作区 | HEAD 与工作区的净变化,包含暂存和未暂存改动;没有单独的 staged/unstaged 模式 |
47
- | Git 单个提交 | 使用 commit 参数;与唯一父提交比较,根提交与空旧版本比较。合并提交请使用区间模式 |
48
- | Git 两个版本 | 使用 baseRevision 和 targetRevision;比较两个版本的文件树,不自动计算合并基点 |
49
- | SVN 工作副本 | 本地 BASE 与工作副本比较,默认不连接远端 |
50
- | SVN 固定历史版本(初始模式) | svnRevision 对比 r-1/r,或 svnBaseRevision/svnTargetRevision 固定树端点;从工作副本根的仓库 URL 读取,不使用脏工作树。远端必须显式 svnAllowNetwork:true |
51
- | 多个子仓库 | 用 repositoryPath 指定会话工作区中的一个仓库根目录;每个仓库分别预览和执行 |
52
+ ```text
53
+ 确认刚才展示的预览范围和模型,使用该 previewId 执行只读审查。
54
+ ```
52
55
 
53
- 未跟踪文件必须通过 selectedPaths 显式选入。省略 selectedPaths 时,审查范围是仓库根目录的变更,不只是当前子目录。
56
+ 插件使用预览返回的 ID 执行。默认预览有效期约 **5 分钟**;文件、模型或会话目录变化后,需要重新预览和确认。宿主权限不允许时,确认也不能绕过限制。
54
57
 
55
- ## 常用参数
58
+ ### 5. 查看结果
56
59
 
57
- 所有文件路径都相对于本次选定的仓库,使用正斜杠;不要使用绝对路径、上级路径或链接目录。
60
+ 执行工具会在对话中返回 **中文 Markdown 报告**,同时返回结构化 JSON。先看“覆盖情况和限制”,再看问题列表,不要只看“发现了几个问题”。
58
61
 
59
- | 参数 | 用途 | 示例 |
60
- |---|---|---|
61
- | selectedPaths | 只审查选中的变更文件 | ["src/api.ts"] |
62
- | repositoryPath | 指定工作区内的子仓库 | "project-a" |
63
- | contextPaths | 手动加入理解代码所需的文件 | ["src/types.ts"] |
64
- | autoContext | 从变更及手动上下文寻找依赖;Git 和 SVN 最多沿三层依赖链读取 | true |
65
- | callerScopePaths | 在指定目录中寻找引用变更文件的代码;已捕获调用方会标出调用行与唯一声明行 | ["src/clients"] |
66
- | oldContextPaths | Git/SVN 固定历史审查中,只加入旧版本的上下文;共享上下文名额 | ["src/removed-helper.ts"] |
67
- | rulePaths | 加入项目审查规则,最多四个 UTF-8 Markdown 或文本文件 | ["review-rules.md"] |
68
- | businessRequirement | 提供业务要求;不是缺陷证据,也不会增加读取权限 | "请求失败时必须释放连接" |
69
- | reviewRounds | 独立审查次数,1~3,默认 1;会增加模型调用量 | 2 |
62
+ 如需保存报告,可以另行告诉 Agent:“将本次报告保存到我指定的位置”。插件本身不会自动在业务仓库创建报告文件。
63
+
64
+ ## 按场景直接使用
65
+
66
+ 下面的自然语言可以直接发给 Agent。每个场景都应先预览、展示范围,再确认执行。
67
+
68
+ ### 只审查几个文件(Git / SVN 工作副本)
69
+
70
+ ```text
71
+ 使用代码审查插件,只预览 src/api.ts 和 src/client.ts 的当前改动。
72
+ 自动补充理解它们所需的依赖文件。先展示范围和模型,等我确认再执行,不修改代码。
73
+ ```
74
+
75
+ 对应 `code_review_preview` 参数(**工具参数,不是 shell 命令**):
76
+
77
+ ```json
78
+ {"selectedPaths":["src/api.ts","src/client.ts"],"autoContext":true}
79
+ ```
80
+
81
+ 文件名要替换成仓库中实际存在的变更路径。未跟踪文件不会默认纳入,必须用 `selectedPaths` 显式选择。省略该参数时,范围是整个仓库的变更,不限于当前子目录。
82
+
83
+ ### 审查 Git 单个提交
84
+
85
+ ```text
86
+ 使用代码审查插件预览 Git 提交 abc1234,先展示范围,等我确认后再执行。
87
+ ```
88
+
89
+ ```json
90
+ {"commit":"abc1234"}
91
+ ```
92
+
93
+ 将 `abc1234` 换成实际提交号。与该提交的唯一父提交比较;根提交与空旧版本比较。**合并提交请改用两个版本比较**,明确选择旧、新端点。
94
+
95
+ ### 审查 Git 两个版本之间的变化
96
+
97
+ ```text
98
+ 预览 Git 的 v1.0.0 到 v1.1.0 之间的代码变化,自动读取必要依赖。先展示范围,不直接执行。
99
+ ```
100
+
101
+ ```json
102
+ {"baseRevision":"v1.0.0","targetRevision":"v1.1.0","autoContext":true}
103
+ ```
104
+
105
+ 比较两个版本的文件树,不自动计算合并基点,也不会把工作区中的未提交正文当作历史正文。`commit` 不能与这两个参数混用。
106
+
107
+ ### 审查 SVN 单个 revision
108
+
109
+ 例如审查 r7315 引入的净变化,即 **r7314 → r7315**:
110
+
111
+ ```text
112
+ 使用代码审查插件预览 SVN r7315(与 r7314 比较)。
113
+ 允许为这次预览读取当前工作副本对应的远端仓库固定版本。
114
+ 先展示来源、范围和模型,不发送源码给审查模型,等我确认再执行。不要更新工作副本。
115
+ ```
70
116
 
71
- commit 与 baseRevision/targetRevision 不能同时使用。oldContextPaths 只能用于 Git/SVN 固定历史审查,不适用于工作副本模式。
117
+ ```json
118
+ {"svnRevision":"7315","svnAllowNetwork":true}
119
+ ```
72
120
 
73
- SVN 历史模式使用独立参数,不可与 Git 参数混用。示例:`{"svnRevision":"7315","svnAllowNetwork":true}`;或 `{"svnBaseRevision":"7314","svnTargetRevision":"7315","svnAllowNetwork":true}`。只接受固定十进制 revision,不接受 HEAD、日期或相对别名。仅选择工作副本根当前仓库 URL,不支持任意远端 URL;预览会读取明确端点的仓库元数据与源码(远端需显式许可),但不发送模型。历史模式支持显式 contextPaths 与 rulePaths:上下文按目标版和已核验的旧版正文分别保存,规则只读取目标版;共享 20 上下文名额与原采集预算。oldContextPaths 可显式选择仅旧版正文,没有目标占位,选中旧变更复用原身份;不能恢复已排除/阻断/无旧侧的变更。autoContext 可沿旧/新固定 revision 独立捕获最多三层字面依赖;精确子节点通过固定版父目录 immediates 清单确认,可能读取有界同目录元数据但不读取无关正文。共享 512 精确探测、512 清单条目、20 候选/上下文名额、1MiB依赖来源及原历史命令/字节/时间预算,缺失与访问失败不混淆。历史 callerScopePaths 支持限定目录的旧/新固定版字面引用发现:预览扫描两侧候选正文,包括非匹配文件,只有命中侧上下文发送模型;共享256索引条目、128扫描文件、1MiB导航来源和512精确探测及原采集预算,可与autoContext组合。不能回退到当前工作副本;声明导航只用同版本目标正文,不证明完整调用覆盖。目录新增/删除按后代逐项覆盖,凭据、链接/特殊节点与超限继续拒绝。
121
+ 必须在 SVN 工作副本根目录运行。插件从该工作副本对应的仓库 URL 读取固定版本,不要求先把工作副本更新到 r7315,也不会执行 update。远端认证不可用时会明确报错,不会改用本地文件冒充历史版本。
74
122
 
75
- 自动上下文和引用方发现是保守的文本导航,不是编译器分析。动态导入、宏、重载、继承和实例方法可能无法解析;找到引用不等于证明函数实际被调用。历史调用方的命名导入线索逐侧对应相同版本声明,标明 side/revision;缺少该版本的批准正文不回退另一侧。Git 历史可沿修改前后两侧分别捕获最多三层依赖;SVN 工作副本模式可沿安全的当前节点取得最多三层依赖;固定 revision 历史模式沿旧/新侧分别最多三层依赖,不把一侧正文或工作树替代另一侧。已批准正文中的唯一命名声明和唯一 default 导出会标出位置;同名、重导出或跨侧内容不猜测。命名空间导入后的独立成员调用可导航至唯一命名声明;写入、逃逸或间接调用不推定绑定,不是实例类型分析。
123
+ ### 审查 SVN revision 区间
76
124
 
77
- ## 如何读报告
125
+ ```text
126
+ 预览 SVN r7300 到 r7315 两个版本端点的净变化。
127
+ 允许读取当前工作副本对应远端仓库的固定版本,自动补充同版本依赖。
128
+ 先展示范围和来源,等我确认后执行,不修改或更新工作副本。
129
+ ```
78
130
 
79
- 先看**覆盖情况和限制**,再看问题列表:
131
+ ```json
132
+ {"svnBaseRevision":"7300","svnTargetRevision":"7315","svnAllowNetwork":true,"autoContext":true}
133
+ ```
80
134
 
81
- - completed:相应阶段完成,不表示代码绝对正确。
82
- - partial:只完成部分工作;检查未完成文件、证据不足或预算限制。
83
- - failed:审查执行失败,不是“审核通过”。
84
- - cancelled:审查被取消。
135
+ 这是两个版本端点的比较,**不是逐一审查区间内的每个提交**。revision 使用带引号的十进制数字,不写 `r7315`、`HEAD` 或日期;旧版本必须小于新版本。`svnRevision` 与区间参数不能同时使用,也不能混用 Git 版本参数。
85
136
 
86
- 每个候选问题会保留位置、触发条件、影响和复核结果。复核可能支持、反驳、无法确定或未完成;被反驳的候选不会悄悄删除。程序会核对引用的位置和原文,但这不能代替人工判断缺陷是否成立。
137
+ `svnAllowNetwork:true` 只允许读取本次工作副本对应仓库的固定版本,不授权更新、合并或任意远端 URL。本地 `file:` 仓库通常无需此参数。
87
138
 
88
- 额外上下文被读取,不等于它已作为主文件完成审查。没有发现问题,也不证明代码没有缺陷。
139
+ ### 加入业务要求、规则和调用方
89
140
 
90
- ## 数据与安全
141
+ ```text
142
+ 只预览 src/api.ts 的改动;业务要求是“请求失败时必须释放连接”。
143
+ 把 src/types.ts 作为辅助上下文,使用 review-rules.md 的项目规则,
144
+ 并在 src/clients 目录中寻找引用该变更文件的代码。先展示读取范围,等我确认。
145
+ ```
91
146
 
92
- - 预览会在本地读取选定变更和上下文;确认执行后,这些内容可能发送给当前模型提供方。发送前请检查范围。
93
- - 插件沿用 DSH 正常工具权限,不额外发起一套审批;confirmed 只确认预览,不能绕过宿主权限。
94
- - 不执行项目代码、构建脚本或模型生成的 shell 命令。模型只能在批准的快照中查找、搜索和读取。
95
- - 常见凭据路径会被排除,但普通源码也可能含秘密。路径过滤不能代替人工检查。
96
- - 二进制、不能解码的文件、链接和超限内容会被阻断,不会自动截断后假装完成。
97
- - SVN 同仓库复制新增文件以空目标旧侧审查,copyfrom URL/revision/UUID 在预览中披露,仅作来源信息,不自动读取来源正文,也不认定为已验证重命名。文件移动的新增/删除两侧分别覆盖。普通目录新增/删除会逐项列出后代;选择目录不会自动批准后代,未全选或子项阻断会标明覆盖不足。同仓库目录复制会显式展开后代,将继承 copied 的正常/修改节点作为空目标旧侧的新增审查,来源身份逐项披露。替换、切换目录、外部工作副本、冲突及部分属性状态仍未支持;含 svn:keywords 的文件仍阻断。全面扩展进度见 [SVN 支持矩阵](SVN-SUPPORT-MATRIX.md)。
147
+ ```json
148
+ {
149
+ "selectedPaths": ["src/api.ts"],
150
+ "contextPaths": ["src/types.ts"],
151
+ "rulePaths": ["review-rules.md"],
152
+ "callerScopePaths": ["src/clients"],
153
+ "businessRequirement": "请求失败时必须释放连接"
154
+ }
155
+ ```
98
156
 
99
- ## 默认规模限制
157
+ 这些文件和目录需要实际存在,路径相对于所选仓库:
158
+
159
+ - `contextPaths`:手动加入辅助文件,不代表该文件本身完成了主审。
160
+ - `autoContext:true`:自动补充最多三层文本依赖,不是全仓扫描或编译器分析。
161
+ - `callerScopePaths`:在明确指定的非根目录中寻找字面引用。预览可能扫描该目录内不匹配的候选正文;只有捕获的匹配上下文随执行发送模型。不保证找到全部调用方。
162
+ - `rulePaths`:最多四个 UTF-8 Markdown 或文本规则文件,不能同时列为上下文。
163
+ - `businessRequirement`:帮助确定审查目标,本身不是缺陷证据。
164
+
165
+ Git / SVN 历史审查也能使用这些参数;依赖和调用方按对应版本读取,不回退到当前工作区。只想提供旧版辅助文件时,用 `oldContextPaths`,例如 `{"svnRevision":"7315","svnAllowNetwork":true,"oldContextPaths":["src/old-helper.ts"]}`。它不能用于工作副本模式。
166
+
167
+ ### 一个工作区中有多个仓库
168
+
169
+ 假设会话工作目录下有 `project-a`、`project-b` 两个仓库:
170
+
171
+ ```text
172
+ 只预览工作区中 project-a 仓库的 src/api.ts 改动,不读取 project-b。先展示范围再等我确认。
173
+ ```
174
+
175
+ ```json
176
+ {"repositoryPath":"project-a","selectedPaths":["src/api.ts"]}
177
+ ```
178
+
179
+ `repositoryPath` 相对于会话工作目录,必须指向一个实际仓库根目录;其余文件路径再相对于该仓库。每个仓库分别预览和执行,不使用绝对路径、`..` 或链接目录。
180
+
181
+ ## 工具调用流程(给需要精确指定参数的人)
182
+
183
+ 普通使用者只需上面的对话请求,不必手工构造 ID。需要精确控制时,让 Agent 按下列顺序调用:
184
+
185
+ 1. 调用 `code_review_preview`,参数可以是 `{}` 或上面的 JSON 示例。
186
+ 2. 展示返回的仓库、模型、文件与限制,保留返回的 `previewId`。**此时还没有完成审查。**
187
+ 3. 用户确认后,调用 `code_review_execute`:
188
+
189
+ ```json
190
+ {"previewId":"替换为本次预览真实返回的ID","confirmed":true}
191
+ ```
192
+
193
+ 不要把示例 ID 原样执行,也不要在 execute 中重新传文件和版本参数。要改范围,重新 preview。`reviewRounds` 可在预览时指定为 1~3(默认 1),表示独立审查次数,会增加模型调用量,不是模型的思考强度设置。
194
+
195
+ ## 如何判断报告是否完成
196
+
197
+ | 状态 | 含义 | 你应该做什么 |
198
+ |---|---|---|
199
+ | `completed` | 相应阶段完成,不表示代码绝对正确 | 阅读问题、复核结果和限制 |
200
+ | `partial` | 只完成部分工作 | 检查未完成文件、证据不足或预算限制 |
201
+ | `failed` | 执行失败,不是审核通过 | 根据诊断处理后重新预览 |
202
+ | `cancelled` | 审查被取消 | 需要时重新发起 |
203
+
204
+ 每个候选问题会保留位置、触发条件、影响和复核结果。复核可能支持、反驳、无法确定或未完成;引用原文匹配也不等于缺陷一定成立。**没有发现问题不证明没有缺陷,读取过上下文也不等于审查了整个项目。**
205
+
206
+ ## 常见问题
207
+
208
+ | 遇到的问题 | 处理方法 |
209
+ |---|---|
210
+ | Agent 没有调用代码审查工具,只给了普通文字分析 | 明确要求调用 `code_review_preview`;若工具不可用,检查插件是否启用并已加载到当前会话 |
211
+ | 找不到仓库 / 选错仓库 | 检查会话工作目录;多仓库时用 `repositoryPath` 指定子仓库根目录 |
212
+ | 新文件没有出现 | 用 `selectedPaths` 显式选择未跟踪文件;路径相对于仓库 |
213
+ | 预览过期、文件已变化、模型已变化 | 重新预览,核对新范围后再确认,不复用旧 ID |
214
+ | SVN 历史提示需要网络许可 | 确认可以访问仓库后,预览参数增加 `svnAllowNetwork:true` |
215
+ | SVN 登录、权限或版本不存在 | 检查 SVN 本身的访问权限和版本号;不要把密码写进对话或仓库 URL |
216
+ | 文件被阻断或报告 partial | 阅读具体原因;缩小变更批次、补充允许的上下文,或处理真实冲突后重新预览。不能把排除文件视为通过 |
217
+ | 想审查 Git 暂存区,排除未暂存改动 | 当前没有单独 staged / unstaged 模式;默认是 HEAD 到工作区的净变化 |
218
+
219
+ ## 支持范围与安全边界
220
+
221
+ - 执行会把批准范围内的代码、上下文、所选规则及业务要求发送给当前模型提供方;敏感项目请先检查预览和模型。常见凭据路径过滤不能保证源码没有秘密。
222
+ - 插件遵循 DSH 工具权限;确认预览不等于绕过宿主权限。不执行项目代码、构建脚本或模型生成的 shell 命令。
223
+ - Git 支持工作区、单提交、两版本比较。SVN 支持工作副本和固定历史端点;**尚未完整支持所有 SVN 状态**。
224
+ - SVN 根目录属性可预览;同仓库复制新增以空目标旧侧审查,复制来源只作信息;移动的新增/删除分别覆盖。目录后代须逐项纳入,选择父目录不自动批准所有后代。
225
+ - SVN 标准关键字展开,以及工作副本/固定历史变更文件的换行元数据保留原文证据;不以规范化结果证明行为等价。自定义/固定宽度关键字、未提交替换、冲突、特殊节点、切换/外部工作副本及跨 URL 比较等仍有限制。已提交固定历史替换与未提交替换的支持不同,详见 [SVN 支持矩阵](SVN-SUPPORT-MATRIX.md)。
226
+ - 二进制、不能解码的文件、链接和超限内容会明确阻断,不静默截断后声称完成。文本导航不证明语义绑定、执行可达或完整调用覆盖。
227
+
228
+ ## 规模限制
100
229
 
101
230
  | 项目 | 默认上限 |
102
231
  |---|---|
103
232
  | 变更文件 | 200 个 |
104
233
  | 单文件 | 256 KiB |
105
234
  | 全部快照 | 4 MiB |
106
- | 额外上下文 | 共 20 个文件,手动与自动来源共享 |
235
+ | 额外上下文 | 手动与自动来源共 20 个文件 |
107
236
  | 单次模型输入 | 约 96 KiB |
108
- | 一次审查的模型调用 | 100 次,多个审查阶段共享 |
237
+ | 一次审查的模型调用 | 所有阶段共享 100 次 |
238
+
239
+ 大变更可按保留原行号的差异窗口分批分析;无法容纳的部分会明确标为未完成。建议先按功能或模块选择一小批文件。详细读取和时间预算见 [开发与发布指南](DEVELOPMENT.md)。
109
240
 
110
- 大变更可以按保留原始行号的差异窗口分批分析;不能容纳的部分会明确标为未完成。详细读取、时间和发现预算见 [开发与发布指南](DEVELOPMENT.md)。
241
+ ## 版本、许可与其他文档
111
242
 
112
- ## 文档导航
243
+ 最新安装包见 [npm](https://www.npmjs.com/package/@feng0531/dsh-code-review) 和 [GitHub Releases](https://github.com/f-e-n-g-0531/dsh-code-review/releases)。发布或安装成功不代表真实模型的误报率、漏报率已得到证明。
113
244
 
114
- - [版本变化](CHANGELOG.md):各版对使用者有何影响。
115
- - [本次发布说明](RELEASE-NOTES.md):新版本内容、升级注意事项及限制。
116
- - [开发与发布指南](DEVELOPMENT.md):测试、打包、发布和失败恢复。
117
- - [核心能力对照](CORE-ALIGNMENT-TASKS.md):已具备能力与尚未完成的差距。
118
- - [参考源码对照](CORE-SOURCE-INVENTORY.md):维护者使用的固定参考来源。
119
- - [评测说明](https://github.com/f-e-n-g-0531/dsh-code-review/blob/main/evaluation/README.md):样本、试跑观察及其证据限制。
245
+ **许可:非商业源码公开许可证。公司内部商业项目、收费服务和商业交付需要另行取得书面授权。** 详见 [LICENSE](LICENSE)。
120
246
 
121
- 最新安装包与正式版本以 [npm](https://www.npmjs.com/package/@feng0531/dsh-code-review) 和 [GitHub Releases](https://github.com/f-e-n-g-0531/dsh-code-review/releases) 为准。发布与安装成功不代表真实模型的误报率或漏报率已经得到证明。
247
+ - [版本变化](CHANGELOG.md):各版本带来的变化。
248
+ - [本次发布说明](RELEASE-NOTES.md):本版功能与限制。
249
+ - [SVN 支持矩阵](SVN-SUPPORT-MATRIX.md):SVN 具体状态、限制和验证证据。
250
+ - [开发与发布指南](DEVELOPMENT.md):维护者使用的测试、打包与发布流程。
251
+ - [核心能力对照](CORE-ALIGNMENT-TASKS.md) / [参考源码对照](CORE-SOURCE-INVENTORY.md):维护计划与参考来源。
252
+ - [评测说明](evaluation/README.md):样本、试跑观察及其限制。
package/RELEASE-NOTES.md CHANGED
@@ -1,14 +1,18 @@
1
- # v0.70.0 发布说明
1
+ # v0.72.0 发布说明
2
2
 
3
- ## SVN 工作副本标准关键字原文审查
3
+ ## 可以照着操作的使用指南
4
4
 
5
- 允许具有标准 svn:keywords 展开的工作副本文件参与审查,前提是映射可验证且逐行不变。
5
+ README 已按实际操作重写:安装启用、选择仓库与模型、发送预览请求、核对并确认、查看结果。补充可复制的 Git 工作区/单提交/区间、SVN 单 revision/区间、多仓库和辅助上下文示例,并区分对话请求、工具 JSON 与终端命令。增加预览过期、权限、未跟踪文件及审查未完成的处理方式。
6
6
 
7
- - BASE 关键字收缩结果须与 `svn cat --ignore-keywords` 仓库正文一致。
8
- - 原正文、原字节哈希和行号保留,模型与缺陷证据使用原始展开源码;收缩视图仅作映射核验和元数据,不替换证据。
9
- - 预览披露双侧原正文/视图哈希、收缩数量和 identity 行映射;提示关键字值变化本身不等于代码缺陷。
10
- - 自定义配置、固定宽度和未闭合展开继续阻断,不能声称所有关键字状态已支持。
7
+ ## SVN 固定历史版本的换行元数据
11
8
 
12
- 关键字上下文导航、换行规范化、未提交替换、跨 URL 分支比较和其他全面 SVN 项仍见 [支持矩阵](SVN-SUPPORT-MATRIX.md),目标尚未完成。
9
+ 审查固定 revision 的变更文件时,预览和模型输入现在披露各文件侧的换行样式、计数及 LF 视图哈希。
13
10
 
14
- 使用见 [README](README.md)。
11
+ - 使用对应仓库版本正文,不读取当前工作副本正文。原始字节哈希、UUID、URL 和版本身份保持不变。
12
+ - 新增/删除缺少的一侧及目录侧标记为 null,不冒充空文件的换行映射。
13
+ - CRLF/LF 保持原 LF 证据行号;单 CR 明确不能套用规范化后的行号。不以视图哈希相同证明行为等价。
14
+ - 不新增 SVN 命令或远端权限,原共享预算和预览确认流程不变。
15
+
16
+ 本批未新增历史上下文换行映射,也未解决未提交替换、跨 URL 分支比较或所有特殊工作副本状态。剩余范围见 [SVN 支持矩阵](SVN-SUPPORT-MATRIX.md)。
17
+
18
+ 使用方式见 [README](README.md)。
@@ -21,16 +21,56 @@
21
21
  | 普通新增/删除目录及后代 | v0.60.0 已发布:补齐默认 status 隐藏的删除后代,离线递归枚举与末尾清单复核;普通结构目录只在后代全选且可审查时开放,凭据/阻断子项导致目录覆盖不足。461 项串行回归、宿主、隔离安装与四矩阵 CI 通过;原产物/npm/Release 118,284 字节逐字节一致,双哈希/清单/冻结发布说明通过 | 第二批普通目录子批 |
22
22
  | 替换、删除再新增、文件/目录类型变化 | 普通与复制替换分别用 SVN_REPLACEMENT/SVN_COPIED_REPLACEMENT 诊断;离线探测确认前者 BASE 无 pristine,后者可能返回复制来源,禁止伪造旧侧。已新增未接入采集的 candidate-only 完整 diff 重建组件及真实离线测试:匹配完整工作侧,拒绝片段、错侧、截断与超限。diff 仍不能独立证明旧节点/编码/旧属性,因此未提交工作副本真实双侧替换仍未支持,不能把辅助组件算作支持完成。v0.64.1 已发布已提交固定历史版本的同路径 deleted/added 合并,真实目标旧/新正文与属性通过测试;481 项串行回归、宿主、隔离安装、四矩阵 CI 与原包/npm/Release 128,279 字节、双哈希/清单/发布说明通过。v0.65.0 已发布已提交固定历史文件/目录类型替换:分侧 kind/正文/属性、预览披露和目录后代覆盖;482 项串行回归、宿主、隔离安装、四矩阵 CI 与原包/npm/Release 128,792 字节、双哈希/清单/发布说明通过。未提交替换仍拒绝 | 第二批:区分 BASE 旧节点与目标节点 |
23
23
  | 缺失、文本/属性/树冲突、阻塞 | v0.60.1 已发布稳定状态诊断:SVN_MISSING_NODE/OBSTRUCTED_NODE/INCOMPLETE_NODE/TEXT_CONFLICT/PROPERTY_CONFLICT/TREE_CONFLICT 等,预览与报告透传 blockCode。463 项串行回归、宿主、隔离安装、四矩阵 CI 与原包/npm/Release 119,462 字节、双哈希/清单/发布说明通过。诊断不算真实双侧支持,仍需后续实现 | 第三批:明确状态诊断;不能合成不存在的工作侧或声称审核通过 |
24
- | svn:keywords、svn:eol-style | 本地已接入标准关键字行保持映射,BASE收缩与cat --ignore-keywords核验、原正文及hash保留、预览披露与模型原行号证据通过;不以规范化正文代替证据或证明行为等价。自定义/固定宽度/未闭合展开仍阻断;504项串行全量回归通过,尚需宿主/隔离安装和发布核验,尚未发布。换行保留原文但无规范化映射 | 第三批:双侧可验证映射,保留原字节和定位 |
24
+ | svn:keywords、svn:eol-style | v0.70.0 已发布标准关键字行保持映射,BASE收缩与cat --ignore-keywords核验、原正文及hash保留、预览披露与模型原行号证据通过;不以规范化正文代替证据或证明行为等价。自定义/固定宽度/未闭合展开仍阻断;504项串行全量回归、宿主/隔离安装、四矩阵CI通过;原包/npm/Release 137,678字节、双哈希/清单/发布说明核验通过。本地新增 EOL 元数据映射:保留原文/原hash与LF视图hash、CRLF/LF/CR计数,CRLF/LF行号identity,单CR明确不等同LF证据行号;预览与模型原文集成测试通过,已补充 UTF-16 字节/文本哈希区分、关键字组合、属性移除、非法样式明确失败与状态不变验证;专项3项、根目录预览/关键字/EOL组合7项、实际ASAR宿主及隔离安装通过,v0.71.0 已发布:507项串行全量回归与冻结版本65项SVN专项通过(0失败/跳过),四矩阵标签CI及发布CI通过,原始Actions/npm/Release 139,804字节逐字节一致,双哈希/清单/冻结发布说明核验通过。不以LF视图替换证据或证明行为等价 | 第三批:双侧可验证映射,保留原字节和定位 |
25
25
  | svn:special/链接 | 当前阻断,不跟随链接取正文 | 第三批:如支持只审链接目标数据;不沿链接读取文件 |
26
26
  | switched、externals、稀疏/混合 revision | 多数拒绝;调用方/依赖链要求祖先 normal | 第四批:明确仓库边界与独立来源,禁止隐式跨仓读取 |
27
- | 固定 revision 单提交 | v0.62.0 已发布 svnRevision:r-1/r 固定对象、UUID/URL/peg/operative revision 逐侧绑定,正文不读工作树;远端显式 svnAllowNetwork。472 项串行回归、宿主、隔离安装、四矩阵 CI 及原包/npm/Release 125,486 字节、双哈希/清单/发布说明通过。历史上下文/规则/调用方尚未支持 | 第五批:新增明确 SVN 参数与冻结身份 |
28
- | 固定 revision 区间 | v0.62.0 已发布 svnBaseRevision/svnTargetRevision,固定树端点 summary+逐侧 cat/proplist,目录后代与属性纳入预算,不建临时工作树;同上述完整发布验证。分支间不同 URL 和历史上下文尚未支持 | 第五批:固定树端点与逐路径差异 |
27
+ | 固定 revision 单提交 | v0.62.0 已发布 svnRevision:r-1/r 固定对象、UUID/URL/peg/operative revision 逐侧绑定,正文不读工作树;远端显式 svnAllowNetwork。472 项串行回归、宿主、隔离安装、四矩阵 CI 及原包/npm/Release 125,486 字节、双哈希/清单/发布说明通过。历史上下文/规则/调用方已由下方 v0.63–0.69 行跟踪,不再沿用 v0.62 当时缺口 | 第五批:新增明确 SVN 参数与冻结身份 |
28
+ | 固定 revision 区间 | v0.62.0 已发布 svnBaseRevision/svnTargetRevision,固定树端点 summary+逐侧 cat/proplist,目录后代与属性纳入预算,不建临时工作树;同上述完整发布验证。历史上下文见下方 v0.63–0.69;分支间不同 URL 仍未支持 | 第五批:固定树端点与逐路径差异 |
29
29
  | 分支复制、分支间比较、合并来源 | v0.66.0 已发布批准历史 mergeinfo 属性的来源路径/区间导航,保留非继承标记、有界不展开 revision、不读取分支源码;invalid/limited 明确且不裁剪。485 项串行回归、宿主、隔离安装、四矩阵 CI 与原包/npm/Release 129,866 字节、双哈希/清单/发布说明通过;v0.67.0 已发布工作副本 BASE/工作侧属性的同一有界导航及预览/报告披露;489 项串行回归、宿主、隔离安装、四矩阵 CI 与原包/npm/Release 130,866 字节、双哈希/清单/发布说明通过。有效继承 mergeinfo、跨 URL 比较、源码归属/合并提交覆盖仍未支持 | 第六批:URL/peg/operative revision 与 mergeinfo 导航披露 |
30
30
  | 当前三层依赖与限定目录调用方 | v0.66.1 已发布根目录仅 mergeinfo 变化下的自动依赖/限定调用方修复:根目录不作源码候选,祖先双侧属性仅 mergeinfo 差异可通过且入指纹;其他属性/unsafe 状态仍拒绝。488 项串行回归、宿主、隔离安装、四矩阵 CI 与原包/npm/Release 130,642 字节、双哈希/清单/发布说明通过。其他祖先/混合状态继续待验证 | 第四批补祖先/混合状态组合 |
31
31
  | 历史两侧上下文与调用方 | v0.63.0 已发布显式 contextPaths 固定双侧来源及目标 rulePaths,UUID/URL/peg/operative revision/原字节与正文 hash 验证,禁止脏工作树与排除项回流。477 项串行回归、宿主、隔离安装、四矩阵 CI 与原包/npm/Release 127,355 字节、双哈希/清单/发布说明通过。v0.64.0 已发布 oldContextPaths 旧侧独有正文、SVN 来源专用校验、预览披露与选中删除身份复用;479 项串行回归、宿主、隔离安装、四矩阵 CI 与原包/npm/Release 127,879 字节、双哈希/清单/发布说明通过;本地开发了注入固定版本 lookup/read 能力的三层历史依赖导航核心,逐侧共享512探测/20候选/1MiB来源与上下文名额;排除项、旧侧限定和规则隔离测试通过。v0.68.0 已发布三层逐侧历史 autoContext,固定父目录有界清单 lookup,清单成功证明缺失、访问失败直接传播,真实旧/新版本链和脏工作树隔离通过。496 项串行回归、宿主、隔离安装、四矩阵 CI 与原包/npm/Release 134,056 字节、双哈希/清单/发布说明通过;本地新增逐侧固定 revision 调用方核心能力接口:共享256索引/128扫描/1MiB正文/512侧探测,命中侧才捕获正文,规则/变更排除与old-only限制保留,同侧命名调用声明导航测试通过;本地已接入固定 SVN 显式目录递归清单/同侧正文、统一30秒/512命令/元数据/正文预算,caller 与依赖共享精确探测及来源上限,真实集成专项13项通过;v0.69.0 已发布历史限定调用方和autoContext组合;502项串行全量回归、组合模型输入隔离、宿主/隔离安装、四矩阵 CI 通过。原包/npm/Release 136,030 字节、双哈希/清单/发布说明通过;字面发现仍不证明完整调用覆盖 | 第六批:逐侧固定版本,不能回退至工作副本 |
32
32
  | 真正 r7314→r7315 样本 | 用户未提供隔离副本路径;本地类似复现不是该样本验收 | 获得路径后只读生成预览,不自动发送模型 |
33
33
 
34
+ ## 下一批:固定历史 EOL 与使用指南(v0.72.0 待发布)
35
+
36
+ - `src/svn-history.mjs` 对已有固定逐侧正文和属性计算 EOL 元数据,不新增 SVN 命令,不改变 UUID/URL/peg/operative revision 与原始字节哈希。
37
+ - 缺失侧与目录侧明确 null;真实 SVN 测试覆盖 CRLF→LF、CR、新增/删除、文件→目录替换、单revision/区间一致、脏工作树隔离、预览0发送、模型原文及status不变。
38
+ - `test/svn-history-eol.test.mjs` 与 EOL/历史替换组合7项通过;完整串行回归509项通过,历史导航/EOL/替换组合12项通过,实际宿主及隔离安装通过,尚未发布。历史上下文 EOL 元数据仍未新增。
39
+ - README 重写为实际使用指南;`test/readme-usage.test.mjs` 验证 JSON 示例参数与真实工具定义一致,文档链接测试通过。
40
+
41
+ ## 实现与复现入口索引
42
+
43
+ 以下入口便于逐项复核,测试存在不等于所有状态已支持;发布验收仍以对应批次实跑结果为准。
44
+
45
+ | 矩阵项 | 源码入口 | 复现测试入口 |
46
+ |---|---|---|
47
+ | 常规文件、子目录属性、新增/删除目录 | `src/svn.mjs` | `test/svn.test.mjs` |
48
+ | 根目录属性预览 | `src/file-relations.mjs`、`src/service.mjs` | `test/svn-root-preview.test.mjs` |
49
+ | 文件/目录复制、移动来源 | `src/svn.mjs` 的 structuralEntries 与 copySource | `test/svn.test.mjs` 离线复制/移动及复制目录案例 |
50
+ | 未提交替换辅助重建(未接入) | `src/svn-state.mjs`、`src/svn-replacement-text.mjs` | `test/svn-replacement-text.test.mjs`、`test/svn-state.test.mjs` |
51
+ | 已提交同路径与类型替换 | `src/svn-history.mjs` 的 mergeSvnHistoryRecord 与逐侧 kind | `test/svn-history-replacement.test.mjs` |
52
+ | 缺失/冲突/阻塞诊断 | `src/svn-state.mjs` | `test/svn-state.test.mjs`;真实缺失在 `test/svn.test.mjs` |
53
+ | 标准关键字与换行 | `src/svn-keyword-mapping.mjs`、`src/svn-eol-mapping.mjs` | `test/svn-keyword-mapping.test.mjs`、`test/svn-eol-mapping.test.mjs` |
54
+ | switched/外部根的拒绝 | `src/svn-caller-safety.mjs`、`src/svn-caller-node.mjs` | `test/svn-caller-special-roots.test.mjs`、`test/svn-caller-index.test.mjs` |
55
+ | 固定单 revision/区间及身份 | `src/svn-history.mjs` | `test/svn-history.test.mjs` |
56
+ | 显式 mergeinfo 来源导航 | `src/svn-mergeinfo.mjs` | `test/svn-mergeinfo.test.mjs` |
57
+ | 本地依赖/调用方及 mergeinfo 祖先 | `src/svn-dependency-chain.mjs`、`src/svn-caller-capture.mjs` | `test/svn-dependency-chain.test.mjs`、`test/svn-caller-capture.test.mjs`、`test/svn-mergeinfo-ancestor.test.mjs` |
58
+ | 历史两侧上下文/依赖/调用方 | `src/svn-history.mjs`、`src/svn-history-dependencies.mjs`、`src/svn-history-callers.mjs` | `test/svn-history.test.mjs`、`test/svn-history-dependencies.test.mjs`、`test/svn-history-callers-integration.test.mjs` |
59
+ | special、稀疏/混合状态、有效继承 mergeinfo、跨 URL | 上表限制仍有效 | 尚无完整支持验收,不能由其他案例推断完成 |
60
+
61
+ ## EOL 子批验收索引(v0.71.0 已发布)
62
+
63
+ 冻结提交 `9e9ffc8db9c9467a54366c17b6d6a6449524e795`;标签CI运行 `38690842208`,唯一发布运行 `38691445215`,原始工件 `11952831232`。包139,804字节;SHA-256 `2e99d5c73ef194be0dc542d19b5a69bfc850e548a1347f6d5ef6aec124ec0f96`。原始Actions包、npm、Release逐字节一致,SHA-512、SHA256SUMS及冻结发布正文通过。此验证不代表自动升级当前宿主或完整SVN支持。
64
+
65
+ | 项目 | 实现/复现证据 | 当前结论 |
66
+ |---|---|---|
67
+ | CRLF/LF/CR 与混合计数、视图哈希 | `src/svn-eol-mapping.mjs`;`test/svn-eol-mapping.test.mjs` 前两项 | 专项通过;原文/原字节哈希不替换;单 CR 不保证 LF 行映射 |
68
+ | 编码身份 | 同上 UTF-16 BOM 案例 | 文本 hash 与 source bytes hash 明确区分,不能互换 |
69
+ | 预览/模型输入及只读 | `src/svn.mjs`、`src/service.mjs`;真实 SVN EOL 集成案例 | 预览不发送;受控模型读取原文;文件与解析后 status 不变 |
70
+ | 组合与失败 | 同一集成案例中标准关键字+CRLF、移除属性、强制非法样式 | 组合通过;非法样式被 SVN 在 status 阶段拒绝,采集明确失败 |
71
+ | 根目录优先修复持续回归 | `test/svn-root-preview.test.mjs` 默认/指定三文件预览 | 本批重新实跑通过,0模型发送,状态不变;原 v0.58.1 交付不重发 |
72
+ | 历史、上下文、特殊状态 | 不在本子批新增范围 | 仍按上表未完成项推进,不由元数据功能推断支持 |
73
+
34
74
  ## 每批发布门禁
35
75
 
36
76
  1. 独立测试副本复现,确认 capture→关系推断→预览→执行的覆盖和拒绝路径。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feng0531/dsh-code-review",
3
- "version": "0.70.0",
3
+ "version": "0.72.0",
4
4
  "private": false,
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" },
package/src/service.mjs CHANGED
@@ -49,7 +49,7 @@ export function createReviewService(llm, { capture = captureSnapshot, now = Date
49
49
  while (previews.size >= maxPreviews) previews.delete(previews.keys().next().value);
50
50
  const previewId = randomUUID();
51
51
  previews.set(previewId, { ...current, target, options, rounds, snapshotId: snapshot.id, expires: now() + ttlMs });
52
- return { ...(options.businessRequirement!==undefined?{businessRequirement:{text:options.businessRequirement,hash:hash(options.businessRequirement),bytes:Buffer.byteLength(options.businessRequirement)}}:{}), previewId, plan, snapshotId: snapshot.id, repositoryRoot: snapshot.root, vcs: snapshot.vcs, ...(snapshot.history ? { history: snapshot.history } : {}), ...(snapshot.mergeSources ? {mergeSources:snapshot.mergeSources} : {}), model: current.route, modelSendingEnabled: allowModelSending, files: snapshot.files.map(f => ({ path: f.path, eligibility: f.eligibility, reason: f.reason, ...(f.keywordMapping?{keywordMapping:f.keywordMapping,textNotice:f.textNotice}:{}), ...(f.rawStatus==='replaced'&&snapshot.history?.vcs==='svn'?{oldKind:f.oldKind,newKind:f.newKind,nodeNotice:f.nodeNotice}:{}), ...(f.blockCode ? {blockCode:f.blockCode} : {}), ...(f.copySource ? {copySource:f.copySource,copyNotice:f.copyNotice} : {}) })), contextPaths: snapshot.context.map(c => c.path), ...(options.oldContextPaths !== undefined ? {oldContextPaths:options.oldContextPaths,oldContextSources:snapshot.context.filter(c=>c.oldOnly).map(c=>({path:c.path,side:'context-old',revision:c.oldRevision,...(c.oldSvnSource?{svnSource:c.oldSvnSource}:{blobOid:c.oldBlobOid}),hash:c.oldHash}))} : {}), ...(snapshot.callerDiscovery ? {callerDiscovery:snapshot.callerDiscovery} : {}), ...(snapshot.autoContext ? { autoContext: snapshot.autoContext } : {}), rules: (snapshot.rules ?? []).map(({ path, hash }) => ({ path, hash })), notice: (snapshot.history?.vcs==='svn'&&options.autoContext===true?'SVN历史autoContext为精确依赖定位读取固定版本父目录有界清单,包含同目录非匹配名字元数据;仅匹配捕获正文发送模型,不扫描全仓,不用工作树替代历史版本。':'') + (options.oldContextPaths !== undefined ? 'oldContextPaths仅为baseline旧侧正文,无目标侧替代;选中变更复用原old身份,旧侧不能证明新侧行为。' : '') + (snapshot.callerDiscovery ? 'callerScopePaths已授权预览读取限定目录内tracked候选正文,包括不匹配文件;非匹配正文不发送模型,匹配捕获上下文会发送。仅字面引用导航,不是函数调用或完整调用覆盖。' : '') + (enableRiskPlanning ? '执行按plan阈值决定风险假设规划,小变更可跳过独立规划但仍主审并复核;计划不是证据,所有阶段共享原预算。' : '') + '执行会向上述模型提供方发送可审查文件两侧内容、预览列出的显式/自动上下文、所选项目规则全文及预览业务需求文本。模型可在该快照范围内多轮只读检索,不读取范围外文件;候选生成后会进行证据与反证复核,两阶段各最多3轮检索,共享每文件120秒和总模型调用100次预算;复核不能证明缺陷成立。plan.reviewRounds为独立审查工作量,非模型reasoning effort;后续轮不带首轮风险计划,所有轮共享原超时与预算。plan仅预检初始输入,ready不代表已审查;minimumCalls不含后续检索/复核/动态业务综合;多文件可先进行业务分组,分组失败保留原组并记录限制,动态综合数执行前未知,实际预算可能不足。请先向用户展示范围,获得确认后执行。' };
52
+ return { ...(options.businessRequirement!==undefined?{businessRequirement:{text:options.businessRequirement,hash:hash(options.businessRequirement),bytes:Buffer.byteLength(options.businessRequirement)}}:{}), previewId, plan, snapshotId: snapshot.id, repositoryRoot: snapshot.root, vcs: snapshot.vcs, ...(snapshot.history ? { history: snapshot.history } : {}), ...(snapshot.mergeSources ? {mergeSources:snapshot.mergeSources} : {}), model: current.route, modelSendingEnabled: allowModelSending, files: snapshot.files.map(f => ({ path: f.path, eligibility: f.eligibility, reason: f.reason, ...(f.eolMapping?{eolMapping:f.eolMapping,textNotice:f.textNotice}:{}), ...(f.keywordMapping?{keywordMapping:f.keywordMapping,textNotice:f.textNotice}:{}), ...(f.rawStatus==='replaced'&&snapshot.history?.vcs==='svn'?{oldKind:f.oldKind,newKind:f.newKind,nodeNotice:f.nodeNotice}:{}), ...(f.blockCode ? {blockCode:f.blockCode} : {}), ...(f.copySource ? {copySource:f.copySource,copyNotice:f.copyNotice} : {}) })), contextPaths: snapshot.context.map(c => c.path), ...(options.oldContextPaths !== undefined ? {oldContextPaths:options.oldContextPaths,oldContextSources:snapshot.context.filter(c=>c.oldOnly).map(c=>({path:c.path,side:'context-old',revision:c.oldRevision,...(c.oldSvnSource?{svnSource:c.oldSvnSource}:{blobOid:c.oldBlobOid}),hash:c.oldHash}))} : {}), ...(snapshot.callerDiscovery ? {callerDiscovery:snapshot.callerDiscovery} : {}), ...(snapshot.autoContext ? { autoContext: snapshot.autoContext } : {}), rules: (snapshot.rules ?? []).map(({ path, hash }) => ({ path, hash })), notice: (snapshot.history?.vcs==='svn'&&options.autoContext===true?'SVN历史autoContext为精确依赖定位读取固定版本父目录有界清单,包含同目录非匹配名字元数据;仅匹配捕获正文发送模型,不扫描全仓,不用工作树替代历史版本。':'') + (options.oldContextPaths !== undefined ? 'oldContextPaths仅为baseline旧侧正文,无目标侧替代;选中变更复用原old身份,旧侧不能证明新侧行为。' : '') + (snapshot.callerDiscovery ? 'callerScopePaths已授权预览读取限定目录内tracked候选正文,包括不匹配文件;非匹配正文不发送模型,匹配捕获上下文会发送。仅字面引用导航,不是函数调用或完整调用覆盖。' : '') + (enableRiskPlanning ? '执行按plan阈值决定风险假设规划,小变更可跳过独立规划但仍主审并复核;计划不是证据,所有阶段共享原预算。' : '') + '执行会向上述模型提供方发送可审查文件两侧内容、预览列出的显式/自动上下文、所选项目规则全文及预览业务需求文本。模型可在该快照范围内多轮只读检索,不读取范围外文件;候选生成后会进行证据与反证复核,两阶段各最多3轮检索,共享每文件120秒和总模型调用100次预算;复核不能证明缺陷成立。plan.reviewRounds为独立审查工作量,非模型reasoning effort;后续轮不带首轮风险计划,所有轮共享原超时与预算。plan仅预检初始输入,ready不代表已审查;minimumCalls不含后续检索/复核/动态业务综合;多文件可先进行业务分组,分组失败保留原组并记录限制,动态综合数执行前未知,实际预算可能不足。请先向用户展示范围,获得确认后执行。' };
53
53
  } finally { busy = false; }
54
54
  },
55
55
  async execute(args, exec) {
@@ -0,0 +1,13 @@
1
+ import {hash} from './content.mjs';
2
+
3
+ // Keep raw text as evidence. LF view is metadata only, never a source replacement.
4
+ export function svnEolMapping(text,style=null){
5
+ if(typeof text!=='string'||Buffer.byteLength(text)>256*1024||!(style===null||['native','LF','CRLF','CR'].includes(style)))throw new Error('Unsupported SVN EOL mapping input or style');
6
+ let crlf=0,lf=0,cr=0;
7
+ for(let i=0;i<text.length;i++){
8
+ if(text[i]==='\r'){if(text[i+1]==='\n'){crlf++;i++;}else cr++;}
9
+ else if(text[i]==='\n')lf++;
10
+ }
11
+ const normalized=text.replace(/\r\n|\r/g,'\n');
12
+ return {rawTextHash:hash(text),lfViewHash:hash(normalized),style,counts:{crlf,lf,cr},mixed:[crlf,lf,cr].filter(Boolean).length>1,lineMapping:cr?'not-identity-for-LF-evidence':'identity',notice:'Hashes describe decoded text encoded as UTF-8, not original source bytes. LF-view hash only; raw text and raw evidence line numbering remain authoritative. Lone CR changes LF line boundaries; do not use normalized line numbers. Newline-only differences do not independently prove a defect or runtime equivalence.'};
13
+ }
@@ -5,6 +5,7 @@ import {hash,decode,relativePath} from './content.mjs';
5
5
  import {isSecretPath,assertNonSecretPath} from './secret-path.mjs';
6
6
  import {captureProjectRules} from './project-rules.mjs';
7
7
  import {snapshotMergeSources} from './svn-mergeinfo.mjs';
8
+ import {svnEolMapping} from './svn-eol-mapping.mjs';
8
9
  import {fixedSvnLookup} from './svn-history-lookup.mjs';
9
10
  import {svnHistoryDependencies} from './svn-history-dependencies.mjs';
10
11
  import {svnHistoryCallers} from './svn-history-callers.mjs';
@@ -130,6 +131,11 @@ export async function captureSvnHistory(cwd,options={}) {
130
131
  for(const key of [...new Set([...Object.keys(left.props),...Object.keys(right.props)])].sort())if(left.props[key]!==right.props[key])item.properties.push({name:key,old:left.props[key]??null,new:right.props[key]??null});
131
132
  item.propertyIdentity=hash(JSON.stringify([left.props,right.props]));
132
133
  item.textNotice='Fixed revision repository text; keywords unexpanded by svn cat --ignore-keywords. No working-copy text or keyword line remapping.';
134
+ if(Object.hasOwn(left.props,'svn:eol-style')||Object.hasOwn(right.props,'svn:eol-style')){
135
+ const mapping=side=>side.kind==='file'?svnEolMapping(side.content.text,side.props['svn:eol-style']??null):null;
136
+ item.eolMapping={old:mapping(left),new:mapping(right)};
137
+ item.textNotice+=' EOL metadata describes each existing file side only; null means absent or directory, not an empty file. Original fixed source bytes and LF evidence lines remain authoritative; lone CR has no normalized LF line identity.';
138
+ }
133
139
  }catch(error){signal.throwIfAborted();if(error.code==='SVN_HISTORY_BUDGET')throw error;item.eligibility='blocked';item.reason=error.message;delete item.left;delete item.right;}
134
140
  if(Buffer.byteLength(JSON.stringify(files))>maxSnapshotBytes)throw new Error('SVN historical snapshot limit exceeded');
135
141
  }
package/src/svn.mjs CHANGED
@@ -2,6 +2,7 @@ import { isSecretPath } from './secret-path.mjs';
2
2
  import { svnStateIssue } from './svn-state.mjs';
3
3
  import { snapshotMergeSources } from './svn-mergeinfo.mjs';
4
4
  import { svnKeywordMapping } from './svn-keyword-mapping.mjs';
5
+ import { svnEolMapping } from './svn-eol-mapping.mjs';
5
6
  import { realpath, lstat } from 'node:fs/promises';
6
7
  import path from 'node:path';
7
8
  import { isDeepStrictEqual } from 'node:util';
@@ -128,6 +129,10 @@ export async function captureSvn(cwd, options = {}) {
128
129
  } else {
129
130
  item.left = decode(hasBase ? await svn(root, ['cat', '-r', 'BASE', '--', target(name)], { ...options, maxBytes: maxFileBytes }) : Buffer.alloc(0));
130
131
  item.right = status === 'deleted' ? decode(Buffer.alloc(0)) : await readLocal(root, name, options);
132
+ if(Object.hasOwn(leftProps,'svn:eol-style')||Object.hasOwn(rightProps,'svn:eol-style')){
133
+ item.eolMapping={old:svnEolMapping(item.left.text,leftProps['svn:eol-style']??null),new:svnEolMapping(item.right.text,rightProps['svn:eol-style']??null)};
134
+ item.textNotice+=' EOL mapping is hash-only metadata; raw text and LF evidence lines are authoritative. Lone CR has no identity mapping to normalized LF lines.';
135
+ }
131
136
  if(keywordText){
132
137
  const old=svnKeywordMapping(item.left.text,leftProps['svn:keywords']??''),current=svnKeywordMapping(item.right.text,rightProps['svn:keywords']??'');
133
138
  if(hasBase){