@feng0531/dsh-code-review 0.44.0 → 0.45.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,13 @@
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.45.0 — 必要调用者绑定
6
+
7
+ 已捕获的调用方文件会附带导入行、本地别名、调用行与变更文件中唯一声明行的语法绑定,仅在两侧正文都已批准时生成,不新增读取。未批准调用方、歧义声明与历史审查不产生绑定;不证明可达、分派或完整调用覆盖。
8
+
5
9
  ## 0.44.0 — 默认导入的定义位置
6
10
 
7
- 默认导入可指向已批准正文中唯一的 default 导出,匿名导出只标行号;混合默认与命名导入同时给出两类位置。多个 default、重导出、注释、模板与续行字符串不猜测,命名空间导入与 import type 保持原样。不新增读取权限,也不表示完整语义绑定。
11
+ 默认导入可指向已批准正文中唯一的 default 导出,匿名导出只标行号;混合默认与命名导入同时给出两类位置。多个 default、重导出、注释、模板与续行字符串不猜测,命名空间导入与 import type 保持原样。不新增读取权限,也不表示完整语义绑定。已正式发布;407 项回归、实际宿主、独立安装及四组 CI 通过,公开安装包与原包一致。
8
12
 
9
13
  ## 0.43.1 — 已批准正文中的唯一定义位置
10
14
 
@@ -23,22 +23,23 @@
23
23
 
24
24
  | 优先级 | 未完成项 | 验收重点 |
25
25
  |---|---|---|
26
- | 1 | 必要语义调用者与实例上下文 | 命名与默认导入的唯一定义位置已接入;调用者与实例仍待验收 |
26
+ | 1 | 实例方法与成员级上下文 | 命名、默认导入与调用方绑定已交付;实例与成员级仍待验收 |
27
27
  | 2 | 剩余历史来源与旧侧组合 | Git 历史与 SVN 三层链已交付;其他旧侧来源仍待验收 |
28
28
  | 3 | 其他旧侧独有来源 | 来源、版本、权限与删除场景保持一致 |
29
29
  | 4 | 剩余语言与路径专用指导 | 只提示具体缺陷和反证;不照搬风格禁令 |
30
30
  | 5 | 剩余预算组合与端到端矩阵 | 分组、窗口、检索、多次审查的终态与覆盖一致 |
31
31
  | 后置 | 真实模型质量评测 | 保存真实输出,人工裁决,分开统计失败和未完成 |
32
32
 
33
- Git 工作区三层字面依赖链已实现并通过测试。这解决了“导入转发文件后,未取得后续实现正文”的部分问题,但仍不是完整定义绑定。本批还补齐了手动上下文作为链入口,真实 Git、服务与独立安装四项测试通过,388 项全量回归和实际宿主检查通过,已随 v0.40.0 正式交付。Git 历史三层链本批已实现,5 项独立安装验收及 393 项回归、实际宿主检查通过,已随 v0.41.0 正式交付;SVN 安全三层链本批已实现,6 项独立安装验收及节点属性变化专项通过,400 项完整回归与实际宿主检查通过,已随 v0.42.0 正式交付。唯一定义位置与默认导入绑定本批已接入审查输入,原候选 8 项独立安装验收及 403 项回归通过;发布前复核发现默认导出误匹配,已修正并增加反例,v0.43.1 已完成完整复验与正式发布。
33
+ Git 工作区三层字面依赖链已实现并通过测试。这解决了“导入转发文件后,未取得后续实现正文”的部分问题,但仍不是完整定义绑定。本批还补齐了手动上下文作为链入口,真实 Git、服务与独立安装四项测试通过,388 项全量回归和实际宿主检查通过,已随 v0.40.0 正式交付。Git 历史三层链本批已实现,5 项独立安装验收及 393 项回归、实际宿主检查通过,已随 v0.41.0 正式交付;SVN 安全三层链本批已实现,6 项独立安装验收及节点属性变化专项通过,400 项完整回归与实际宿主检查通过,已随 v0.42.0 正式交付。唯一定义位置与默认导入绑定本批已接入审查输入,原候选 8 项独立安装验收及 403 项回归通过;发布前复核发现默认导出误匹配,已修正并增加反例,v0.43.1 已正式发布;默认导入的定义位置已随 v0.44.0 正式交付;调用方绑定本批已实现,待完整复验与发布。
34
34
 
35
35
  ## 当前验证结论
36
36
 
37
- - 最近完整回归:406 项通过。
37
+ - 最近完整回归:407 项通过。
38
38
  - 实际 DSH 工具运行时检查通过;模型响应受控,不是质量成绩。
39
39
  - v0.39.0 独立安装包的真实 Git、服务与预算测试:5 项通过。
40
40
  - v0.39.1 已正式交付,包含 Git 工作区三层依赖能力。四组 CI 通过;npm、Release 与保存原包的 91,502 字节完全一致,SHA-256、SHA-512 和校验清单通过。v0.39.0 不单独计作已核验交付。
41
41
  - v0.40.0 已正式交付手动上下文依赖导航。四组 CI 与唯一发布通过,npm、Release 与保存原包的 91,901 字节完全一致,双哈希及校验清单通过。准确语义绑定和剩余旧侧来源等差距仍保留。
42
+ - v0.44.0 已正式交付默认导入的定义位置。407 项回归、实际宿主、独立安装与四组 CI 通过;npm、Release 与原包的 95,672 字节一致,双哈希及校验清单通过。
42
43
  - v0.43.1 已正式交付已批准正文中的唯一定义位置。406 项回归、实际宿主、独立安装与四组 CI 通过;npm、Release 与原包的 95,203 字节一致,双哈希及校验清单通过。v0.43.0 候选在发布前复核被替代,不计作交付。
43
44
  - v0.42.0 已正式交付 SVN 安全三层链。400 项回归、实际宿主、独立安装与四组 CI 通过;npm、Release 与原包的 94,404 字节一致,双哈希及校验清单通过。
44
45
  - v0.41.0 已正式交付 Git 历史两侧三层链。393 项回归、实际宿主、独立安装与四组 CI 通过;npm、Release 与原包的 93,202 字节一致,双哈希及校验清单通过。
package/README.md CHANGED
@@ -59,7 +59,7 @@ DSH 的只读代码审查插件。它比较 Git 或 SVN 中的代码变更,使
59
59
  | repositoryPath | 指定工作区内的子仓库 | "project-a" |
60
60
  | contextPaths | 手动加入理解代码所需的文件 | ["src/types.ts"] |
61
61
  | autoContext | 从变更及手动上下文寻找依赖;Git 和 SVN 最多沿三层依赖链读取 | true |
62
- | callerScopePaths | 在指定目录中寻找引用变更文件的代码 | ["src/clients"] |
62
+ | callerScopePaths | 在指定目录中寻找引用变更文件的代码;已捕获调用方会标出调用行与唯一声明行 | ["src/clients"] |
63
63
  | oldContextPaths | Git 历史审查中,只加入旧版本的上下文 | ["src/removed-helper.ts"] |
64
64
  | rulePaths | 加入项目审查规则,最多四个 UTF-8 Markdown 或文本文件 | ["review-rules.md"] |
65
65
  | businessRequirement | 提供业务要求;不是缺陷证据,也不会增加读取权限 | "请求失败时必须释放连接" |
package/RELEASE-NOTES.md CHANGED
@@ -1,20 +1,19 @@
1
- # v0.44.0 发布说明
1
+ # v0.45.0 发布说明
2
2
 
3
- ## 新增:默认导入的定义位置
3
+ ## 新增:必要调用者绑定
4
4
 
5
- 默认导入现在可以在已批准正文中指向唯一的 default 导出,包括类、函数和表达式。匿名默认导出只标出所在行,不虚构名称。
5
+ 启用 callerScopePaths 后,已捕获的调用方文件会附带语法绑定:导入行、本地别名、调用行,以及变更文件中唯一声明的所在行。这回答“谁调用了你改的函数”,而不只是“谁提到了这个文件”。
6
6
 
7
- 混合写法 `import Gate, { run } from "./target.js"` 会同时给出默认和命名两种定义位置。
7
+ 绑定只在调用方正文与变更文件正文都已批准时生成,不新增文件读取。
8
8
 
9
9
  ## 限制
10
10
 
11
- - 出现多个 default 导出、`export { default }` 重导出、注释、模板或续行字符串时,不给出位置。
12
- - 命名空间导入、`import type` 和非默认写法的命名导入保持原样,不猜测。
13
- - 只检查已批准的变更或上下文正文,不新增文件读取。
14
- - 这些位置仍是语法线索,不证明语义绑定、调用可达或因果。
11
+ - 调用方正文未获批(例如超出上下文名额)或变更声明有歧义时不给出绑定。
12
+ - 历史审查不产生这类调用方绑定,避免把当前侧调用行当作旧版本证据。
13
+ - 绑定不证明执行可达、动态分派、重载选择或完整调用覆盖,也不代表全部调用方。
15
14
 
16
15
  ## 验证
17
16
 
18
- 测试覆盖默认导出的三种形态、复数 default、重导出拒绝、混合导入、旧侧隔离以及进入审查输入。全量回归、实际宿主、独立安装和四组 CI 是正式发布条件,不是模型效果成绩。
17
+ 测试覆盖真实 Git 调用方绑定、未批准调用方拒绝、歧义拒绝、审查输入投递与历史排除。全量回归、实际宿主、独立安装和四组 CI 是正式发布条件,不是模型效果成绩。
19
18
 
20
19
  使用方法见 [README](README.md),维护步骤见 [开发指南](DEVELOPMENT.md)。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feng0531/dsh-code-review",
3
- "version": "0.44.0",
3
+ "version": "0.45.0",
4
4
  "private": false,
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" },
@@ -1,6 +1,25 @@
1
1
  import { contextCandidates } from './context-candidates.mjs';
2
+ import { directImportBindings } from './import-bindings.mjs';
3
+ import { importedCallSites, indexStandaloneCalls } from './import-call-sites.mjs';
2
4
  import { relativePath } from './content.mjs';
3
5
  import { isSecretPath } from './secret-path.mjs';
6
+ import path from 'node:path';
7
+ const importExtensions=['.mjs','.cjs','.js','.jsx','.ts','.tsx'];
8
+ // Approved caller body plus approved target text only; no additional reads.
9
+ function callerBindings(fromPath, targetPath, scannedText, targetText) {
10
+ if (typeof scannedText !== 'string' || typeof targetText !== 'string') return [];
11
+ const lines = scannedText.split(String.fromCharCode(10)), callIndex = indexStandaloneCalls(lines), result = [];
12
+ for (let i = 0; i < lines.length; i++) {
13
+ const match = /^\s*(?:import\s+(?:(?:[^'";]+)\s+from\s+)?|export\s+(?:[^'";]+)\s+from\s+)(['"])([^'"\\]+)\1\s*;?\s*(?:\/\/.*)?$/.exec(lines[i]);
14
+ if (!match || !(match[2].startsWith('./') || match[2].startsWith('../'))) continue;
15
+ const target = path.posix.normalize(path.posix.join(path.posix.dirname(fromPath), match[2]));
16
+ try { relativePath(target); } catch { continue; }
17
+ const candidates = path.posix.extname(target) ? [target] : [target, ...importExtensions.map(ext => target + ext), ...importExtensions.map(ext => target + '/index' + ext)];
18
+ if (!candidates.includes(targetPath)) continue;
19
+ result.push(...directImportBindings(lines, i + 1, importedCallSites(lines, i + 1, callIndex), targetText));
20
+ }
21
+ return result.slice(0, 20);
22
+ }
4
23
 
5
24
  // Pure reverse navigation; resolutionPaths must contain every competitor.
6
25
  export function callerCandidates(files, scanned, resolutionPaths) {
@@ -25,7 +44,7 @@ export function callerCandidates(files, scanned, resolutionPaths) {
25
44
  if (probes.size > 512) throw new Error('Caller resolution probe limit exceeded');
26
45
  }});
27
46
  if (plan.truncated) throw new Error('Caller navigation result limit exceeded');
28
- for (const target of plan.candidates) if (targets.has(target.path)) candidates.push({fromPath:source.path,targetPath:target.path,reason:target.reasons[0],confidence:'navigation-only'});
47
+ for (const target of plan.candidates) if (targets.has(target.path)) { const bindings=callerBindings(source.path,target.path,source.text,files.find(f=>f.path===target.path)?.right?.text); candidates.push({fromPath:source.path,targetPath:target.path,reason:target.reasons[0],confidence:'navigation-only',...(bindings.length?{bindings}:{})}); }
29
48
  }
30
49
  const compare=(a,b)=>a<b?-1:a>b?1:0;
31
50
  return {candidates:candidates.sort((a,b)=>compare(a.fromPath,b.fromPath)||compare(a.targetPath,b.targetPath)),probePaths:[...probes].sort(),notice:'Literal module/include references only; not function calls, reachability, semantic binding or complete caller coverage.'};
@@ -26,6 +26,11 @@ export function preparePrimaryInput(input, file, { instructions, scope, grouping
26
26
  }
27
27
  const csharpHints = inferCsharpContext(input, file);
28
28
  if (csharpHints.length) { base.csharpContextHints = csharpHints; base.csharpContextNotice = '仅批准context的C#静态类型/方法语法位置;历史输入按contextSide/contextVersion区分正文,无旧正文不回退目标侧。不证明语义绑定或执行可达。歧义不推定,线索不增加审查覆盖。'; }
29
+ if (!input.history && input.callerDiscovery) {
30
+ const approved = new Set((input.context ?? []).filter(c => typeof c.text === 'string').map(c => c.path));
31
+ const callerBindings = input.callerDiscovery.candidates.filter(c => c.targetPath === file.path && c.bindings?.length && approved.has(c.fromPath)).map(({ fromPath, bindings }) => ({ fromPath, bindings }));
32
+ if (callerBindings.length) { base.callerBindings = callerBindings; base.callerBindingNotice = '调用方文件的导入行、调用行与目标声明行仅为语法位置线索,来源限于已批准上下文;不证明执行可达、动态分派、重载选择或完整调用覆盖,也不代表全部调用方。'; }
33
+ }
29
34
  let payload = JSON.stringify(base);
30
35
  const measure = value => initialInputBudget({ instructions, input: value }, scope, maxInputBytes);
31
36
  const metadata = {};