@feng0531/dsh-code-review 0.26.2 → 0.27.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 +10 -0
- package/README.md +3 -1
- package/package.json +1 -1
- package/src/context-candidates.mjs +4 -0
- package/src/git-history.mjs +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# 变更记录
|
|
2
2
|
|
|
3
|
+
## 0.27.0
|
|
4
|
+
|
|
5
|
+
- C#同目录静态零参调用Type.cs必要上下文有界探针,沿用批准路径/changed排除及历史对象查询。
|
|
6
|
+
- 无using/dynamic/同名其他引用且file-scoped namespace窄语法,捕获不证明绑定;275测试通过。
|
|
7
|
+
|
|
8
|
+
## 0.26.3
|
|
9
|
+
|
|
10
|
+
- 历史依赖链接/目录/子模块参与路径歧义,不先过滤后误判唯一普通文件;仍禁止捕获非普通对象正文。
|
|
11
|
+
- 重叠目录与index探针分别字面查询,避免Git合并路径展开;真实对象竞争及大树回归通过。
|
|
12
|
+
|
|
3
13
|
## 0.26.2
|
|
4
14
|
|
|
5
15
|
- 跨文件规划/生成输入附批准上下文import/include/caller语法绑定线索,带primaryFileId及历史contextSide/contextVersion。
|
package/README.md
CHANGED
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
## 当前状态
|
|
6
6
|
|
|
7
|
-
当前代码版本为 0.
|
|
7
|
+
当前代码版本为 0.27.0
|
|
8
|
+
|
|
9
|
+
C#自动上下文支持窄语法同目录Type.cs静态零参调用探针,需file-scoped namespace且无using/dynamic/注释字符串/同名其他引用;捕获后定义提示再校验命名空间。不是编译器解析,不全仓寻找类型。
|
|
8
10
|
|
|
9
11
|
历史捕获用diff-tree精确端点变更元数据,不前置完整枚举文件树;显式上下文/规则和直接导入依赖按字面非递归路径查询。自动依赖最多512个待查路径,仍保留200变更/20上下文/4MiB快照限制,大树小提交不因无关文件元数据超限。
|
|
10
12
|
|
package/package.json
CHANGED
|
@@ -10,6 +10,10 @@ export function contextCandidates(files, trackedPaths, {maxCandidates=20,onProbe
|
|
|
10
10
|
relativePath(f.path);if(f.eligibility!=='reviewable'||f.rightExists===false)continue;
|
|
11
11
|
const text=f.right?.text;if(typeof text!=='string'||Buffer.byteLength(text)>256*1024)continue;
|
|
12
12
|
if(text.includes('/*')||text.includes(String.fromCharCode(96))||text.includes('R"')||/\\\r?\n/.test(text))continue;
|
|
13
|
+
if(f.path.endsWith('.cs')&&!/["'\x60#]|\/\/|\busing\b|\bdynamic\b/.test(text)){
|
|
14
|
+
const namespaces=[...text.matchAll(/^\s*namespace\s+([A-Za-z_][A-Za-z0-9_.]*)\s*;\s*$/gm)];
|
|
15
|
+
if(namespaces.length===1){const lines=text.split('\n');for(let i=0;i<lines.length;i++){const call=/^\s*(?:await\s+)?([A-Za-z_][A-Za-z0-9_]*)\.([A-Za-z_][A-Za-z0-9_]*)\s*\(\s*\)\s*;\s*$/.exec(lines[i]);if(!call)continue;const token=new RegExp('\\b'+call[1]+'\\b');if(lines.some((line,j)=>j!==i&&token.test(line)))continue;const candidate=path.posix.join(path.posix.dirname(f.path),call[1]+'.cs');onProbe?.([candidate]);add(candidate,f.path,'same-directory-csharp-static-call');}}
|
|
16
|
+
}
|
|
13
17
|
const cpp=/\.(c|cc|cpp|cxx|h|hh|hpp|hxx|shader|hlsl|glsl|cginc|hlsli|compute)$/i.test(f.path),js=/\.(mjs|cjs|js|jsx|ts|tsx)$/i.test(f.path);
|
|
14
18
|
if(cpp&&/^\s*#\s*(if|ifdef|ifndef|elif|else|define|undef)\b/m.test(text))continue;
|
|
15
19
|
for(const line of text.split('\n')){
|
package/src/git-history.mjs
CHANGED
|
@@ -23,7 +23,7 @@ export async function captureGitHistory(cwd, options={}) {
|
|
|
23
23
|
for(let i=0;i<raw.length;i+=2){const m=/^:(\d{6}) (\d{6}) ([a-f0-9]{40}|[a-f0-9]{64}) ([a-f0-9]{40}|[a-f0-9]{64}) ([AMDT])$/.exec(raw[i]);if(!m)throw new Error('Invalid historical diff metadata');const name=relativePath(raw[i+1]);if(names.includes(name))throw new Error('Duplicate historical change');names.push(name);for(const [map,mode,oid] of [[left,m[1],m[3]],[right,m[2],m[4]]])if(mode!=='000000')map.set(name,{mode,type:mode==='160000'?'commit':'blob',oid});}
|
|
24
24
|
names.sort();
|
|
25
25
|
// Literal, nonrecursive lookups cannot enumerate a supplied directory or wildcard.
|
|
26
|
-
const lookup=async (oid,map,requested)=>{if(!oid)return;const pending=[...new Set(requested)].filter(p=>!map.has(p));for(let i=0;i<pending.length;i
|
|
26
|
+
const lookup=async (oid,map,requested)=>{if(!oid)return;const pending=[...new Set(requested)].filter(p=>!map.has(p));for(let i=0;i<pending.length;i++){const paths=pending.slice(i,i+1);paths.forEach(relativePath);const records=utf8(await git(root,['--literal-pathspecs','ls-tree','-z','--full-tree',oid,'--',...paths])).split('\0').filter(Boolean);for(const r of records){const m=/^(\d{6}) (blob|commit|tree) ([a-f0-9]+)\t([\s\S]+)$/.exec(r);if(!m||!paths.includes(m[4]))throw new Error('Invalid historical path lookup');map.set(m[4],{mode:m[1],type:m[2],oid:m[3]});}}};
|
|
27
27
|
if(names.length>maxFiles)throw new Error('Historical change count limit exceeded');if(selectedPaths?.some(p=>!names.includes(p)))throw new Error('Selected path is not a historical change');
|
|
28
28
|
const files=[];for(const name of names){signal?.throwIfAborted();const l=left.get(name),r=right.get(name),item={id:hash(name),path:name,rawStatus:!l?'A':!r?'D':'M',eligibility:'reviewable',properties:[]};files.push(item);if(selectedPaths&&!selectedPaths.includes(name)){item.eligibility='excluded';item.reason='Not explicitly selected';continue;}try{for(const e of [l,r].filter(Boolean))if(e.type!=='blob'||!['100644','100755'].includes(e.mode))throw new Error('Historical link or submodule excluded');const read=async e=>decode(e?await git(root,['cat-file','blob',e.oid],maxFileBytes):Buffer.alloc(0));item.left=await read(l);item.right=await read(r);item.rightExists=!!r;if(l&&r&&item.left.hash===item.right.hash){item.eligibility='excluded';item.reason='Mode-only historical change not analyzed';}}catch(error){signal?.throwIfAborted();item.eligibility='blocked';item.reason=error.message;delete item.left;delete item.right;}if(Buffer.byteLength(JSON.stringify(files))>maxSnapshotBytes)throw new Error('Historical snapshot limit exceeded');}
|
|
29
29
|
const rulePaths=options.rulePaths ?? [];
|
|
@@ -37,6 +37,6 @@ export async function captureGitHistory(cwd, options={}) {
|
|
|
37
37
|
for(const name of [...new Set(contextPaths)].sort())context.push(await captureContext(name));
|
|
38
38
|
const rules=await captureProjectRules(root,rulePaths,{signal,files,read:async (_root,name,{maxFileBytes:limit})=>{const entry=right.get(name);if(!regular(entry))throw new Error('Historical rule not a target regular blob');return decode(await git(root,['cat-file','blob',entry.oid],limit));}});
|
|
39
39
|
let autoContext;
|
|
40
|
-
if(options.autoContext===true){const probes=new Set();const oldFiles=files.map(f=>f.eligibility==='reviewable'?{...f,right:f.left,rightExists:true}:f);const onProbe=paths=>{for(const p of paths)probes.add(p);if(probes.size>512)throw new Error('Historical context lookup limit exceeded');};contextCandidates(files,[],{onProbe});contextCandidates(oldFiles,[],{onProbe});await lookup(target,right,[...probes]);await lookup(base,left,[...probes]);const plan=contextCandidates(files,[...right
|
|
40
|
+
if(options.autoContext===true){const probes=new Set();const oldFiles=files.map(f=>f.eligibility==='reviewable'?{...f,right:f.left,rightExists:true}:f);const onProbe=paths=>{for(const p of paths)probes.add(p);if(probes.size>512)throw new Error('Historical context lookup limit exceeded');};contextCandidates(files,[],{onProbe});contextCandidates(oldFiles,[],{onProbe});await lookup(target,right,[...probes]);await lookup(base,left,[...probes]);const plan=contextCandidates(files,[...right.keys()]);const oldPlan=contextCandidates(files.map(f=>f.eligibility==='reviewable'?{...f,right:f.left,rightExists:true}:f),[...left.keys()]);const combined=new Map(plan.candidates.map(c=>[c.path,{...c,sourceSides:['new']}]));for(const c of oldPlan.candidates){if(combined.has(c.path))combined.get(c.path).sourceSides.push('old');else combined.set(c.path,{...c,sourceSides:['old']});}plan.candidates=[...combined.values()].sort((a,b)=>a.path<b.path?-1:a.path>b.path?1:0);plan.truncated ||= oldPlan.truncated;autoContext={...plan,notice:'Direct baseline/target-tree import/include navigation only; no caller coverage, binding or working-tree reads.',target,base,candidates:plan.candidates.filter(c=>!contextPaths.includes(c.path)&&!rulePaths.includes(c.path)),capturedPaths:[]};for(const c of autoContext.candidates){signal?.throwIfAborted();if(context.length>=maxContextFiles){c.status='blocked';c.reason='Context file limit exceeded';continue;}try{context.push(await captureContext(c.path));c.status='captured';autoContext.capturedPaths.push(c.path);}catch(error){signal?.throwIfAborted();c.status='blocked';c.reason=error.message;}}}
|
|
41
41
|
const snapshot={context,...(rules.length?{rules}:{}),...(autoContext?{autoContext}:{}),schemaVersion:1,vcs:'git',root,baseline:base,history:{mode:commit!==undefined?'commit':'range',base,target,semantics:'exact-tree-endpoints; renames represented as add/delete'},files};const serialized=JSON.stringify(snapshot);if(Buffer.byteLength(serialized)>maxSnapshotBytes)throw new Error('Historical snapshot limit exceeded');return {...snapshot,id:hash(serialized)};
|
|
42
42
|
}
|