@feng0531/dsh-code-review 0.39.1 → 0.40.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 +5 -1
- package/CORE-ALIGNMENT-TASKS.md +4 -4
- package/DEVELOPMENT.md +1 -1
- package/README.md +1 -1
- package/RELEASE-NOTES.md +14 -17
- package/index.mjs +1 -1
- package/package.json +1 -1
- package/src/dependency-chain.mjs +15 -4
- package/src/snapshot.mjs +9 -4
package/CHANGELOG.md
CHANGED
|
@@ -2,12 +2,16 @@
|
|
|
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.40.0 — 手动上下文的依赖导航
|
|
6
|
+
|
|
7
|
+
Git 工作区启用 autoContext 后,也沿 contextPaths 指定文件的静态依赖取得必要实现。手动与自动来源共享数量、正文和时间限制;不改变未启用时的行为,不把排除变更或未跟踪依赖重新纳入。历史与 SVN 仍仅直接导航。
|
|
8
|
+
|
|
5
9
|
## 0.39.1 — 文档整理
|
|
6
10
|
|
|
7
11
|
- 重写安装、使用、参数和报告说明,去掉开发流水账与未解释术语。
|
|
8
12
|
- 分开用户说明、开发发布流程、能力差距和评测观察。
|
|
9
13
|
- GitHub Release 使用专门编写的发布说明,不再只展示自动生成的提交列表。
|
|
10
|
-
-
|
|
14
|
+
- 不改变审查工具参数或运行行为。已正式发布;四组 CI 通过,npm 与 GitHub Release 的实际安装包和保存原包一致,发布正文为新版用户说明。
|
|
11
15
|
|
|
12
16
|
## 0.39.0 — Git 工作区依赖上下文
|
|
13
17
|
|
package/CORE-ALIGNMENT-TASKS.md
CHANGED
|
@@ -30,15 +30,15 @@
|
|
|
30
30
|
| 5 | 剩余预算组合与端到端矩阵 | 分组、窗口、检索、多次审查的终态与覆盖一致 |
|
|
31
31
|
| 后置 | 真实模型质量评测 | 保存真实输出,人工裁决,分开统计失败和未完成 |
|
|
32
32
|
|
|
33
|
-
Git
|
|
33
|
+
Git 工作区三层字面依赖链已实现并通过测试。这解决了“导入转发文件后,未取得后续实现正文”的部分问题,但仍不是完整定义绑定。本批还补齐了手动上下文作为链入口,真实 Git、服务与独立安装四项测试通过,388 项全量回归和实际宿主检查通过,待正式发布。历史与 SVN 不具备同样的链式能力。
|
|
34
34
|
|
|
35
35
|
## 当前验证结论
|
|
36
36
|
|
|
37
|
-
- 最近完整回归:
|
|
37
|
+
- 最近完整回归:388 项通过。
|
|
38
38
|
- 实际 DSH 工具运行时检查通过;模型响应受控,不是质量成绩。
|
|
39
39
|
- v0.39.0 独立安装包的真实 Git、服务与预算测试:5 项通过。
|
|
40
|
-
-
|
|
41
|
-
-
|
|
40
|
+
- v0.39.1 已正式交付,包含 Git 工作区三层依赖能力。四组 CI 通过;npm、Release 与保存原包的 91,502 字节完全一致,SHA-256、SHA-512 和校验清单通过。v0.39.0 不单独计作已核验交付。
|
|
41
|
+
- 文档重构与正式发布已完成。新增文档一致性检查通过;本表中的核心差距仍未完成。
|
|
42
42
|
|
|
43
43
|
## 不在范围内
|
|
44
44
|
|
package/DEVELOPMENT.md
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
- 每个主文件的模型与检索阶段共享默认 120 秒;关联综合沿用相关主文件的生命周期,不因切换阶段重新计时。
|
|
20
20
|
- 每阶段最多三轮检索;全局最多 50 次读取请求,返回内容累计 256 KiB,单次最多 64 KiB,读取最多 200 行。
|
|
21
|
-
- Git 工作区依赖链最多三层、512 个精确路径查询、20
|
|
21
|
+
- Git 工作区依赖链最多三层、512 个精确路径查询、20 个候选、手动入口与自动依赖共享 1 MiB 正文;文件读取与最终复核共享 30 秒。
|
|
22
22
|
- 引用方发现最多四个不重叠的非根目录;128 个候选正文共 1 MiB,目录索引最多 256 条/64 KiB,竞争路径最多 512 个。发现与复核共享 30 秒。
|
|
23
23
|
|
|
24
24
|
达到上限必须阻断或说明未完成,不能静默扩大范围、丢弃证据或重新计时。
|
package/README.md
CHANGED
|
@@ -58,7 +58,7 @@ DSH 的只读代码审查插件。它比较 Git 或 SVN 中的代码变更,使
|
|
|
58
58
|
| selectedPaths | 只审查选中的变更文件 | ["src/api.ts"] |
|
|
59
59
|
| repositoryPath | 指定工作区内的子仓库 | "project-a" |
|
|
60
60
|
| contextPaths | 手动加入理解代码所需的文件 | ["src/types.ts"] |
|
|
61
|
-
| autoContext |
|
|
61
|
+
| autoContext | 从变更及手动上下文寻找依赖;Git 工作区最多沿三层依赖链读取 | true |
|
|
62
62
|
| callerScopePaths | 在指定目录中寻找引用变更文件的代码 | ["src/clients"] |
|
|
63
63
|
| oldContextPaths | Git 历史审查中,只加入旧版本的上下文 | ["src/removed-helper.ts"] |
|
|
64
64
|
| rulePaths | 加入项目审查规则,最多四个 UTF-8 Markdown 或文本文件 | ["review-rules.md"] |
|
package/RELEASE-NOTES.md
CHANGED
|
@@ -1,26 +1,23 @@
|
|
|
1
|
-
# v0.
|
|
1
|
+
# v0.40.0 发布说明
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 新增:沿手动上下文取得必要实现
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
在 Git 工作区同时设置 contextPaths 和 autoContext: true 时,自动上下文现在也从手动选定的文件出发。若该文件导入或转发其他实现,插件最多沿三层静态依赖取得正文,帮助理解变更所需的类型与实现。
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
不启用 autoContext 时,手动上下文仍只包含指定文件。无需新增参数。
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
- 发布说明与变更记录使用面向用户的描述,不再把流水线编号和校验日志混入功能介绍。
|
|
11
|
-
- 检查评测与能力对照文档,区分已完成、部分实现和未验证,删除过时的过程叙述。
|
|
12
|
-
- 发布流程使用本说明作为 GitHub Release 正文,避免自动生成的提交列表代替产品说明。
|
|
9
|
+
## 范围与限制
|
|
13
10
|
|
|
14
|
-
|
|
11
|
+
- 手动文件和自动依赖共享 20 个上下文名额。
|
|
12
|
+
- 手动入口正文和自动依赖共享 1 MiB 来源上限;单文件不超过 256 KiB。
|
|
13
|
+
- 依赖查询、入口检查及执行前重新采集沿用有限范围和时间上限。
|
|
14
|
+
- 自动依赖必须来自 Git 跟踪路径;不会重新纳入排除的变更。
|
|
15
|
+
- 文件在预览后发生变化时,必须重新预览,不能沿旧快照发送。
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
这是保守的文本导航,不是完整符号解析。Git 历史和 SVN 的链式上下文尚未完成,动态调用和实例绑定仍可能无法解析。
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
## 验证
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
真实 Git 和受控模型测试覆盖手动入口、启用开关、上下文数量、正文预算、未跟踪依赖、排除变更和过期拒绝。正式发布还须通过全量回归、独立安装及四组 CI。测试证明流程边界,不证明模型检出率。
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
这些验证证明插件流程和边界符合测试预期,不证明真实模型的检出率或误报率。完整语义调用者、历史与 SVN 依赖链及部分语言指导仍未完成。
|
|
25
|
-
|
|
26
|
-
安装方法见 [README](README.md)。开发和安装包一致性验证方法见 [开发与发布指南](DEVELOPMENT.md)。
|
|
23
|
+
使用方法见 [README](README.md),发布步骤见 [开发指南](DEVELOPMENT.md)。
|
package/index.mjs
CHANGED
|
@@ -12,7 +12,7 @@ export function apply(ctx, config = {}) {
|
|
|
12
12
|
};
|
|
13
13
|
ctx.tools.register({
|
|
14
14
|
name: 'code_review_preview', description: 'Preview Git/SVN working-copy or Git commit/range review scope and model destination without sending source to a model. repositoryPath selects an explicit child repository relative to session workspace; file paths are repository-relative. Preview each repository separately. Show the preview before execution.',
|
|
15
|
-
parameters: { type: 'object', additionalProperties: false, properties: { reviewRounds: { type: 'integer', minimum: 1, maximum: 3, description: 'Explicit independent review passes, default 1. Preview discloses extra sends; later passes omit the first risk plan. Shares existing timeout, call and retrieval budgets, not provider reasoning effort. Bound to this preview; execute cannot override.' }, oldContextPaths: { type: 'array', maxItems: 20, items: { type: 'string' }, description: 'Explicit Git historical baseline-only blob context paths. Shares 20 context slots. No target text is inferred; selected old changes reuse their original identity, excluded changes cannot reenter. Unsupported on working reviews and SVN.' }, callerScopePaths: { type: 'array', minItems: 1, maxItems: 4, items: { type: 'string' }, description: 'Explicit non-root repository-relative directories for bounded tracked literal reverse import/include discovery. Git/SVN working review or Git fixed commit/range review. SVN uses offline normal nodes, rejects unsafe ancestor states and special/keyword properties. Historical sides share scan budgets and use object blobs only. Preview reads candidate bodies, including nonmatches; only captured matching context is sent. Navigation only, not complete caller coverage.' }, businessRequirement: { type: 'string', maxLength: 16384, description: 'Optional business constraint text up to 16KiB UTF8; untrusted data, not evidence or permission. Bound to preview and sent in review phases.' }, commit: { type: 'string', description: 'Git commit versus sole parent; root empty, merge requires range.' }, baseRevision: { type: 'string', description: 'Git old endpoint; requires targetRevision, mutually exclusive with commit.' }, targetRevision: { type: 'string', description: 'Git new endpoint, exact tree endpoints not merge-base. Explicit context uses target tree with available baseline provenance; oldContextPaths is baseline-only. Historical caller discovery scans fixed sides; explicit rules use target-tree blobs.' }, repositoryPath: { type: 'string', description: 'Workspace-relative explicit child Git/SVN repository root, forward slashes; omit for current repository.' }, selectedPaths: { type: 'array', items: { type: 'string' }, maxItems: 200 }, autoContext: { type: 'boolean', description: 'Opt in to tracked literal import/include context shown in preview. Working Git follows at most 3 dependency layers under shared 512 exact probes, 20 candidates/context slots, 1MiB source and 30s verification budget. SVN and Git history remain direct navigation. Not semantic binding or complete definitions.' }, contextPaths: { type: 'array', items: { type: 'string' }, maxItems: 20 }, rulePaths: { type: 'array', items: { type: 'string' }, maxItems: 4 } } },
|
|
15
|
+
parameters: { type: 'object', additionalProperties: false, properties: { reviewRounds: { type: 'integer', minimum: 1, maximum: 3, description: 'Explicit independent review passes, default 1. Preview discloses extra sends; later passes omit the first risk plan. Shares existing timeout, call and retrieval budgets, not provider reasoning effort. Bound to this preview; execute cannot override.' }, oldContextPaths: { type: 'array', maxItems: 20, items: { type: 'string' }, description: 'Explicit Git historical baseline-only blob context paths. Shares 20 context slots. No target text is inferred; selected old changes reuse their original identity, excluded changes cannot reenter. Unsupported on working reviews and SVN.' }, callerScopePaths: { type: 'array', minItems: 1, maxItems: 4, items: { type: 'string' }, description: 'Explicit non-root repository-relative directories for bounded tracked literal reverse import/include discovery. Git/SVN working review or Git fixed commit/range review. SVN uses offline normal nodes, rejects unsafe ancestor states and special/keyword properties. Historical sides share scan budgets and use object blobs only. Preview reads candidate bodies, including nonmatches; only captured matching context is sent. Navigation only, not complete caller coverage.' }, businessRequirement: { type: 'string', maxLength: 16384, description: 'Optional business constraint text up to 16KiB UTF8; untrusted data, not evidence or permission. Bound to preview and sent in review phases.' }, commit: { type: 'string', description: 'Git commit versus sole parent; root empty, merge requires range.' }, baseRevision: { type: 'string', description: 'Git old endpoint; requires targetRevision, mutually exclusive with commit.' }, targetRevision: { type: 'string', description: 'Git new endpoint, exact tree endpoints not merge-base. Explicit context uses target tree with available baseline provenance; oldContextPaths is baseline-only. Historical caller discovery scans fixed sides; explicit rules use target-tree blobs.' }, repositoryPath: { type: 'string', description: 'Workspace-relative explicit child Git/SVN repository root, forward slashes; omit for current repository.' }, selectedPaths: { type: 'array', items: { type: 'string' }, maxItems: 200 }, autoContext: { type: 'boolean', description: 'Opt in to tracked literal import/include context shown in preview. Working Git follows changed and explicitly selected context sources at most 3 dependency layers under shared 512 exact probes, 20 candidates/context slots, 1MiB source and 30s verification budget. SVN and Git history remain direct navigation. Not semantic binding or complete definitions.' }, contextPaths: { type: 'array', items: { type: 'string' }, maxItems: 20 }, rulePaths: { type: 'array', items: { type: 'string' }, maxItems: 4 } } },
|
|
16
16
|
output,
|
|
17
17
|
async execute(args, exec) { const preview = await service.preview(args, exec); return { json: JSON.stringify(preview), markdown: '' }; },
|
|
18
18
|
});
|
package/package.json
CHANGED
package/src/dependency-chain.mjs
CHANGED
|
@@ -6,12 +6,23 @@ export async function captureDependencyChain(root,files,options={}){
|
|
|
6
6
|
const signal=AbortSignal.any([AbortSignal.timeout(30000),...(options.signal?[options.signal]:[])]);
|
|
7
7
|
const scoped={...options,maxFileBytes:Math.min(options.maxFileBytes??256*1024,256*1024),signal},probes=new Set(),visited=new Set(files.map(f=>f.path)),context=[],candidates=[];
|
|
8
8
|
const reserved=new Set([...(options.contextPaths??[]),...(options.rulePaths??[])]),limit=Math.min(options.maxContextFiles??20,20);
|
|
9
|
-
let frontier=files,index,bytes=0,truncated=false,depth=0;
|
|
9
|
+
let frontier=[...files],index,bytes=0,truncated=false,depth=0;
|
|
10
|
+
const explicitSeeds=[];
|
|
11
|
+
if(new Set(options.contextPaths??[]).size>limit)throw new Error('Context file limit exceeded');
|
|
12
|
+
for(const name of [...new Set(options.contextPaths??[])].sort()){
|
|
13
|
+
const changed=files.find(f=>f.path===name);
|
|
14
|
+
if(changed){if(changed.eligibility!=='reviewable'||changed.rightExists===false)throw new Error('Context path is an excluded blocked or deleted change');continue;}
|
|
15
|
+
const item={path:name,...await readLocal(root,name,scoped)};
|
|
16
|
+
bytes+=item.bytes;if(bytes>1024*1024)throw new Error('Dependency source byte limit exceeded');
|
|
17
|
+
explicitSeeds.push(item);visited.add(name);frontier.push({path:name,eligibility:'reviewable',right:item});
|
|
18
|
+
}
|
|
19
|
+
// Seeds are already explicitly selected, but descendants must still pass exact tracked probes.
|
|
20
|
+
|
|
10
21
|
const onProbe=paths=>{for(const p of paths)probes.add(p);if(probes.size>512)throw new Error('Git context lookup limit exceeded');};
|
|
11
22
|
for(depth=1;depth<=3&&frontier.length;depth++){
|
|
12
|
-
signal.throwIfAborted();contextCandidates(frontier,[],{onProbe});
|
|
23
|
+
signal.throwIfAborted();for(let i=0;i<frontier.length;i+=200)contextCandidates(frontier.slice(i,i+200),[],{onProbe});
|
|
13
24
|
index=await gitContextIndex(root,{...scoped,probePaths:[...probes]});
|
|
14
|
-
const
|
|
25
|
+
const plans=[];for(let i=0;i<frontier.length;i+=200)plans.push(contextCandidates(frontier.slice(i,i+200),index.paths));const plan={candidates:plans.flatMap(p=>p.candidates),truncated:plans.some(p=>p.truncated)};truncated ||= plan.truncated;
|
|
15
26
|
const next=[];
|
|
16
27
|
for(const entry of plan.candidates){
|
|
17
28
|
if(visited.has(entry.path)||reserved.has(entry.path))continue;
|
|
@@ -29,5 +40,5 @@ export async function captureDependencyChain(root,files,options={}){
|
|
|
29
40
|
}
|
|
30
41
|
if(frontier.length)truncated=true;
|
|
31
42
|
const indexOptions={...scoped,probePaths:[...probes]};
|
|
32
|
-
return {context,index,indexOptions,autoContext:{candidates,capturedPaths:context.map(c=>c.path),truncated,maxDepth:3,probeCount:probes.size,sourceBytes:bytes,notice:'Bounded current-side literal dependency navigation, depth <=3, 512 shared exact probes, 20 candidates/context slots, 1MiB source bytes and 30s through verification. Not symbol binding, caller coverage or complete definitions.'}};
|
|
43
|
+
return {context,index,indexOptions,explicitSeeds,autoContext:{candidates,capturedPaths:context.map(c=>c.path),truncated,maxDepth:3,probeCount:probes.size,sourceBytes:bytes,notice:'Bounded current-side literal dependency navigation from changed and explicit context sources, depth <=3, 512 shared exact probes, 20 candidates/context slots, 1MiB source bytes and 30s through verification. Not symbol binding, caller coverage or complete definitions.'}};
|
|
33
44
|
}
|
package/src/snapshot.mjs
CHANGED
|
@@ -49,10 +49,10 @@ export async function captureSnapshot(cwd, options = {}) {
|
|
|
49
49
|
if (!Array.isArray(rulePaths) || rulePaths.some(name => contextPaths.includes(name))) throw new Error('Rule paths must be separate from context paths');
|
|
50
50
|
const rules = await captureProjectRules(snapshot.root, rulePaths, { signal: options.signal, files: snapshot.files });
|
|
51
51
|
const context = [];
|
|
52
|
-
let autoContext, index, indexOptions = options;
|
|
52
|
+
let autoContext, index, explicitSeeds = [], indexOptions = options;
|
|
53
53
|
if (options.autoContext === true && type==='git') {
|
|
54
54
|
const chain=await captureDependencyChain(snapshot.root,snapshot.files,options);
|
|
55
|
-
context.push(...chain.context);autoContext=chain.autoContext;index=chain.index;indexOptions=chain.indexOptions;
|
|
55
|
+
explicitSeeds=chain.explicitSeeds;context.push(...chain.context);autoContext=chain.autoContext;index=chain.index;indexOptions=chain.indexOptions;
|
|
56
56
|
} else if (options.autoContext === true) {
|
|
57
57
|
if(type==='git'){const probes=new Set();contextCandidates(snapshot.files,[],{onProbe:paths=>{for(const p of paths)probes.add(p);if(probes.size>512)throw new Error('Git context lookup limit exceeded');}});indexOptions={...options,probePaths:[...probes]};}
|
|
58
58
|
index = await (type === 'git' ? gitContextIndex : svnContextIndex)(snapshot.root, indexOptions);
|
|
@@ -72,7 +72,12 @@ export async function captureSnapshot(cwd, options = {}) {
|
|
|
72
72
|
if (changed.eligibility !== 'reviewable') throw new Error('Context path is an excluded or blocked change');
|
|
73
73
|
if (changed.rightExists === false) throw new Error('Deleted path cannot be working context');
|
|
74
74
|
context.push({ path: name, ...changed.right });
|
|
75
|
-
} else
|
|
75
|
+
} else {
|
|
76
|
+
const item={path:name,...await readLocal(snapshot.root,name,indexOptions)};
|
|
77
|
+
const seed=explicitSeeds.find(c=>c.path===name);
|
|
78
|
+
if(seed&&seed.hash!==item.hash)throw new Error('Explicit dependency seed changed during capture');
|
|
79
|
+
context.push(item);
|
|
80
|
+
}
|
|
76
81
|
}
|
|
77
82
|
let callerDiscovery, verifyCallers;
|
|
78
83
|
if (options.callerScopePaths !== undefined) {
|
|
@@ -93,7 +98,7 @@ export async function captureSnapshot(cwd, options = {}) {
|
|
|
93
98
|
}
|
|
94
99
|
}
|
|
95
100
|
// Re-capture verifies both selected sides while collecting explicit context.
|
|
96
|
-
if ((await capture(cwd,
|
|
101
|
+
if ((await capture(cwd, indexOptions)).id !== snapshot.id) throw new Error('Snapshot changed while collecting context');
|
|
97
102
|
for (const item of context) if ((await readLocal(snapshot.root, item.path, indexOptions)).hash !== item.hash) throw new Error('Context changed during capture');
|
|
98
103
|
if (index && (await (type === 'git' ? gitContextIndex : svnContextIndex)(snapshot.root, indexOptions)).fingerprint !== index.fingerprint) throw new Error('Tracked context index changed during capture');
|
|
99
104
|
const verifiedRules = await captureProjectRules(snapshot.root, rulePaths, { signal: options.signal, files: snapshot.files });
|