@sanqianx/project-knowledge 5.0.15-rc.1 → 5.0.15-rc.2
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/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +6 -0
- package/_modules/knowledge-engine/package.json +1 -1
- package/_modules/knowledge-engine/src/retrieval.js +17 -12
- package/_modules/knowledge-engine/src/runtime/layout.js +3 -1
- package/_site/_test/knowledge-engine-client-test.js +5 -1
- package/_site/lib/contracts.js +1 -0
- package/module-runtime-manifest.json +3 -3
- package/package.json +1 -1
- package/plugins/project-knowledge/.claude-plugin/plugin.json +1 -1
- package/plugins/project-knowledge/.codex-plugin/plugin.json +1 -1
- package/plugins/project-knowledge/.mcp.json +1 -1
- package/vendor-manifest.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [5.0.15-rc.2] - 2026-10-13
|
|
4
|
+
|
|
5
|
+
- Windows 路径边界校验改用原生规范化,保留中文/空格、缺失末级目录和目录联接越界检查。
|
|
6
|
+
- 保留 rc.1 的全部检索及离线接线修复;性能验收明确区分真实向量召回与最终选择词法结果,不把词法结果标为向量命中。
|
|
7
|
+
- 候选版仍不提升 latest,源码、npm 安装版、真实客户端及另一台设备的反馈分别记录。
|
|
8
|
+
|
|
3
9
|
## [5.0.15-rc.1] - 2026-10-13
|
|
4
10
|
|
|
5
11
|
- 候选版检索采用当前 Markdown 分块上的中文 BM25 与可验证向量片段融合;每篇最多两个互补片段,不将文档向量分数扩散到全部章节。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanqianx/ai-coding-knowledge-engine",
|
|
3
|
-
"version": "0.2.7-rc.
|
|
3
|
+
"version": "0.2.7-rc.2",
|
|
4
4
|
"description": "Knowledge engine module for project-knowledge: commit-boundary triggered knowledge analysis/generation (trigger plane) and retrieval for development assistants (retrieval plane).",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "src/server.js",
|
|
@@ -30,19 +30,8 @@ class EngineRetrieval extends KnowledgeRetrievalService {
|
|
|
30
30
|
if (!['greeting', 'followup'].includes(ranking.intent(signals.indexQuery))) await this.recallIndex(scopes, signals.indexQuery, scanned.candidates, health);
|
|
31
31
|
else health.index = 'not-requested';
|
|
32
32
|
if (stamp !== scopeStamp(this.store.getProject(project.projectId))) throw new DomainError('PROJECT_SCOPE_CHANGED', '项目归属或绑定已变化,请重新检索。', { status: 409 });
|
|
33
|
-
const checked = new Map();
|
|
34
33
|
for (const [id, c] of scanned.candidates) {
|
|
35
34
|
if (stamps.get(c.projectId) !== scopeStamp(this.store.getProject(c.projectId) || this.store.state.sharedScopes.find(s => s.id === c.projectId))) { scanned.candidates.delete(id); continue; }
|
|
36
|
-
const key = `${c.spaceId}:${c.entryId}`;
|
|
37
|
-
if (!checked.has(key)) {
|
|
38
|
-
try {
|
|
39
|
-
const file = path.join(c.knowledgePath, c.entryId);
|
|
40
|
-
const valid = this.layout.isPathInside(c.knowledgePath, file, { realpath: true }) && !fs.lstatSync(file).isSymbolicLink()
|
|
41
|
-
&& crypto.createHash('sha256').update(`chunker:${CHUNKER_VERSION}\n${fs.readFileSync(file, 'utf8')}`).digest('hex') === c.documentHash;
|
|
42
|
-
checked.set(key, valid);
|
|
43
|
-
} catch { checked.set(key, false); }
|
|
44
|
-
}
|
|
45
|
-
if (!checked.get(key)) scanned.candidates.delete(id);
|
|
46
35
|
if (c.entryId.startsWith('memories/')) {
|
|
47
36
|
const shared = this.store.state.sharedScopes.find(s => s.id === c.projectId);
|
|
48
37
|
if (!this.memories?.readableEntry(shared, c.entryId, registrationBefore) || !this.memories.memoryForChunk(shared, c.entryId, c.headingPath)) scanned.candidates.delete(id);
|
|
@@ -53,7 +42,23 @@ class EngineRetrieval extends KnowledgeRetrievalService {
|
|
|
53
42
|
return { ...scored, excluded: Boolean(signals.commitPrefixes?.length || c.knownFileQuery) && scored.score <= 0 };
|
|
54
43
|
});
|
|
55
44
|
const ordered = input.policy === 'project-first' ? [...ranked.filter(c => c.scopeReason === 'primary'), ...ranked.filter(c => c.scopeReason !== 'primary')] : ranked;
|
|
56
|
-
|
|
45
|
+
const checked = new Map(), candidates = [];
|
|
46
|
+
// Revalidate every returned document after the asynchronous vector call,
|
|
47
|
+
// not every unrelated file a second time. Changed/deleted/escaped facts
|
|
48
|
+
// are discarded before leaving this service, then the next rank is tried.
|
|
49
|
+
for (const c of ordered) {
|
|
50
|
+
const key = `${c.spaceId}:${c.entryId}`;
|
|
51
|
+
if (!checked.has(key)) {
|
|
52
|
+
try {
|
|
53
|
+
const file = path.join(c.knowledgePath, c.entryId);
|
|
54
|
+
checked.set(key, this.layout.isPathInside(c.knowledgePath, file, { realpath: true }) && !fs.lstatSync(file).isSymbolicLink()
|
|
55
|
+
&& crypto.createHash('sha256').update(`chunker:${CHUNKER_VERSION}\n${fs.readFileSync(file, 'utf8')}`).digest('hex') === c.documentHash);
|
|
56
|
+
} catch { checked.set(key, false); }
|
|
57
|
+
}
|
|
58
|
+
if (checked.get(key)) candidates.push(c);
|
|
59
|
+
if (candidates.length >= this.candidateLimit) break;
|
|
60
|
+
}
|
|
61
|
+
return { project, scopes, signals, health, candidates, totalCurrentChunks: scanned.candidates.size, intent: ranking.intent(signals.indexQuery) };
|
|
57
62
|
}
|
|
58
63
|
signalSet(input = {}) {
|
|
59
64
|
const signals = super.signalSet(input);
|
|
@@ -5,7 +5,9 @@ const { DomainError, validateProjectId } = require('./contracts');
|
|
|
5
5
|
|
|
6
6
|
function realpathWithMissingLeaf(value) {
|
|
7
7
|
const absolute = path.resolve(value);
|
|
8
|
-
|
|
8
|
+
// Native canonicalization preserves junction/symlink boundaries without the
|
|
9
|
+
// JavaScript implementation's repeated ancestor stat calls on Windows.
|
|
10
|
+
if (fs.existsSync(absolute)) return (fs.realpathSync.native || fs.realpathSync)(absolute);
|
|
9
11
|
const parent = path.dirname(absolute);
|
|
10
12
|
return parent === absolute ? absolute : path.join(realpathWithMissingLeaf(parent), path.basename(absolute));
|
|
11
13
|
}
|
|
@@ -25,7 +25,7 @@ const { EngineKnowledgeClient } = require('../lib/engine-knowledge-client');
|
|
|
25
25
|
await projects.create(projectId, { repoPath, knowledgePath }, { modules: { engine: { status: 'registered', projectId } } });
|
|
26
26
|
await projects.updateState(projectId, state => { state.modules = { engine: { status: 'registered', projectId } }; });
|
|
27
27
|
await registry.add(projectId, { displayNameSnapshot: 'Test' });
|
|
28
|
-
let invalidIdentity = false, malformed = false, businessError = false, oversized = false, wrongBinding = false;
|
|
28
|
+
let invalidIdentity = false, malformed = false, businessError = false, oversized = false, wrongBinding = false, scopeChanged = false;
|
|
29
29
|
const calls = [];
|
|
30
30
|
const reference = { scope_project_id: projectId, entry_id: 'changes/aaaaaaaaaaaa.md', commitSha: 'a'.repeat(40), chunk_text: 'Engine authoritative knowledge' };
|
|
31
31
|
const payload = { ok: true, projectId, backend: 'hybrid+markdown-truth', results: [reference], answer: reference.chunk_text, citations: [{ ...reference, entryId: reference.entry_id }], chunks: [{ chunk_text: reference.chunk_text }] };
|
|
@@ -37,6 +37,7 @@ const { EngineKnowledgeClient } = require('../lib/engine-knowledge-client');
|
|
|
37
37
|
let text = ''; for await (const bytes of req) text += bytes;
|
|
38
38
|
calls.push({ route: req.url, input: JSON.parse(text || '{}') });
|
|
39
39
|
if (oversized) return res.end(JSON.stringify({ ok: true, text: 'x'.repeat(2 * 1024 * 1024) }));
|
|
40
|
+
if (scopeChanged) { res.statusCode = 409; return res.end(JSON.stringify({ ok: false, error: { code: 'PROJECT_SCOPE_CHANGED' } })); }
|
|
40
41
|
if (businessError) { res.statusCode = 403; return res.end(JSON.stringify({ ok: false, error: { code: 'PATH_OUTSIDE_ROOT', message: 'private-test-secret' } })); }
|
|
41
42
|
res.end(malformed ? '[]' : JSON.stringify(payload));
|
|
42
43
|
});
|
|
@@ -86,6 +87,9 @@ const { EngineKnowledgeClient } = require('../lib/engine-knowledge-client');
|
|
|
86
87
|
businessError = false; oversized = true;
|
|
87
88
|
await assert.rejects(runtime.search({ projectId, query: 'knowledge' }), e => e.code === 'ENGINE_INVALID_RESPONSE');
|
|
88
89
|
oversized = false;
|
|
90
|
+
scopeChanged = true;
|
|
91
|
+
await assert.rejects(runtime.search({ projectId, query: 'knowledge' }), e => e.code === 'PROJECT_SCOPE_CHANGED' && e.status === 409);
|
|
92
|
+
scopeChanged = false;
|
|
89
93
|
const direct = new EngineKnowledgeClient({ layout, engineUrl: `http://127.0.0.1:${server.address().port}` });
|
|
90
94
|
assert.deepEqual(await direct.request('search', {}, runtime.resolveProject({ projectId })), payload);
|
|
91
95
|
assert.equal(calls.at(-1).route, '/api/search');
|
package/_site/lib/contracts.js
CHANGED
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
"plugins/project-knowledge/hooks/claude.json": "d61510327e3e1facbb4c645228e9f50cad05a88b33d80c4c22e5ba9f8b9f5359",
|
|
9
9
|
"plugins/project-knowledge/hooks/codex.json": "216e03c07295606ef6ebb2c4f669ad0d790b5e7c4f3f34600488033db75e4239",
|
|
10
10
|
"plugins/project-knowledge/scripts/context-hook.cjs": "da666153ad5723592cce42d25337ae649f7d93e43fab76b3c6d1263933de063f",
|
|
11
|
-
"plugins/project-knowledge/.claude-plugin/plugin.json": "
|
|
12
|
-
"plugins/project-knowledge/.codex-plugin/plugin.json": "
|
|
13
|
-
"plugins/project-knowledge/.mcp.json": "
|
|
11
|
+
"plugins/project-knowledge/.claude-plugin/plugin.json": "5e92b5474674fcfc9935f9e931b02ba057980821ef2618fbed05e5d2d5be3b5e",
|
|
12
|
+
"plugins/project-knowledge/.codex-plugin/plugin.json": "c21616b137cfe1335d0c07e8e08cf84814492d67e8d1a75ac6044a14d9403c9f",
|
|
13
|
+
"plugins/project-knowledge/.mcp.json": "2ab447acbc3a9dbc629f4f859f0b8dc8903ebe9ca00fc1c45e4aa148d4eb8d79"
|
|
14
14
|
},
|
|
15
15
|
"modules": {
|
|
16
16
|
"terminal": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanqianx/project-knowledge",
|
|
3
|
-
"version": "5.0.15-rc.
|
|
3
|
+
"version": "5.0.15-rc.2",
|
|
4
4
|
"description": "Knowledge base manager with Git integration, AI-driven analysis, and bilingual (zh-CN/en-US) knowledge output",
|
|
5
5
|
"main": "_site/server.js",
|
|
6
6
|
"scripts": {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
3
|
"name": "project-knowledge",
|
|
4
4
|
"hooks": "./hooks/claude.json",
|
|
5
|
-
"version": "5.0.15-rc.
|
|
5
|
+
"version": "5.0.15-rc.2",
|
|
6
6
|
"description": "Read durable project decisions and implementation history from Project Knowledge.",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "SanQianX",
|
package/vendor-manifest.json
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
"modules": {
|
|
4
4
|
"knowledge-engine": {
|
|
5
5
|
"name": "@sanqianx/ai-coding-knowledge-engine",
|
|
6
|
-
"version": "0.2.7-rc.
|
|
6
|
+
"version": "0.2.7-rc.2",
|
|
7
7
|
"root": "_modules/knowledge-engine",
|
|
8
8
|
"files": {
|
|
9
9
|
"README.md": "eae7e1a59416c623064f68dbf33d13d41e21096bf67701e41464e0a86c84abdd",
|
|
10
|
-
"package.json": "
|
|
10
|
+
"package.json": "c4047ea4206a90f1f0455c281e2ca42f5682d915477ad4f5035c9e14656ac13b",
|
|
11
11
|
"src/analysis-contract.js": "913a63b63fb4a4a9df071f95bbeb8f8c40ef87e2f8445ab0742086ced70ffbf7",
|
|
12
12
|
"src/analysis-output.js": "95a3c19dcc9ab85d1dba7fee5b9aec80f9c000dfba89da3fd0be25a81e2fb680",
|
|
13
13
|
"src/analysis-worker.js": "a105cabe6e1573b2d986b1e7a5aaf615e2fb53524156fc8e4e2c0a71f2c97e55",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"src/memories.js": "bba8e19380e906a80fd3e5d2a7f809d940466924578c8187d1f376f906f10f2b",
|
|
23
23
|
"src/retrieval-policy.json": "3205fb4eb114a839a74f902e6fae6a61e1460958e5a9d7e29add32b60ba4e05a",
|
|
24
24
|
"src/retrieval-ranking.js": "422c068ea995d4f8c7e1ffb64b4b72921017862add74da8996b1827c6c64c23d",
|
|
25
|
-
"src/retrieval.js": "
|
|
25
|
+
"src/retrieval.js": "db9ca99d2dfd184795543819215d2121cfd9f509413490a5f840367c6a071a25",
|
|
26
26
|
"src/runtime/README.md": "9be676eca76a7857b34d7357194e6f0ee733c22e61d962d53984715dbec7f673",
|
|
27
27
|
"src/runtime/atomic-file.js": "9846b4b69db5ded811acf19a1181ef93615d93efaa542b858e552a7f3124ad23",
|
|
28
28
|
"src/runtime/automation-config.js": "6498c5397be12b155829740595a1553d4dcd6eaa7db6ced3ef88fdc60218f00e",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"src/runtime/git-runner.js": "b74fe197bd4dcf3e70635ef7b1f2615edf965c1d9a3ffe5406aabd41b76d74a9",
|
|
39
39
|
"src/runtime/knowledge-promotion.js": "892a227921ca399e80ea7b3991bfd13de0ffc221c7a0562393f1e5d33c2d00fa",
|
|
40
40
|
"src/runtime/knowledge-retrieval-service.js": "75444f495a9ad0720af0e57b8d27d7ac1325c78a768f4f68ebd86ebc2524945e",
|
|
41
|
-
"src/runtime/layout.js": "
|
|
41
|
+
"src/runtime/layout.js": "e47fe54c605218493ce46f6289798d9db2a3ecec95089d288ed03d3b75cf871a",
|
|
42
42
|
"src/runtime/markdown-knowledge-indexer.js": "4d6b5b955849d274ca0614ef030c94526da7c39ab0874f02c168d0681f5b1406",
|
|
43
43
|
"src/runtime/scanner.js": "d99fa8d74a6fbbaae75ca49f62127e14cf9e48b1404e4b4d597ef39df5441410",
|
|
44
44
|
"src/server.js": "3aa7b6b3aec141f3d1fde63fbbd4ee8b3c95a92c851726a56717c3537fe4954b",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"ui/app.js": "33a2ba38f0dafbbb2e30a1eb3fc93476bfcaf528e124e1a9b2edd2fb69221e9e",
|
|
48
48
|
"ui/index.html": "ce3565cfefd4838b11d37a91a906ef26ca5a1834844fe3f7c897b56851a3c418"
|
|
49
49
|
},
|
|
50
|
-
"sourceCommit": "
|
|
50
|
+
"sourceCommit": "5219472b76a6bef8ede9ef9e0ee556cfda4b8551"
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
}
|