@sanqianx/project-knowledge 5.0.12-rc.2 → 5.0.12-rc.3
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/README.md +2 -0
- package/_modules/knowledge-engine/package.json +1 -1
- package/_modules/knowledge-engine/src/retrieval.js +10 -1
- package/_site/_test/native-context-integrations-test.js +16 -0
- package/_site/lib/zcode-context-integration.js +15 -6
- 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.12-rc.3] - 2026-10-12
|
|
4
|
+
|
|
5
|
+
- 修复点名源文件的需求被相似历史向量结果挤出的情况;已有文件标识先匹配知识依据,新文件名仍可检索相似背景。
|
|
6
|
+
- 修复 ZCode 安装目录变化时残留旧上下文入口;只替换明确属于本产品的脚本,去除重复入口,保留第三方 Hook、采集及模型。
|
|
7
|
+
- 保留真实模型首轮失败和再次验证记录,未完成 Codex 原生信任审核前不提升 latest。
|
|
8
|
+
|
|
3
9
|
## [5.0.12-rc.2] - 2026-10-12
|
|
4
10
|
|
|
5
11
|
- 修正自动上下文的当前记忆与历史事实边界,保留编辑时间及项目、领域、全局的适用优先级。
|
|
@@ -77,6 +77,8 @@ node src/bin.js serve [--port 5790] [--host 127.0.0.1] [--data DIR]
|
|
|
77
77
|
|
|
78
78
|
上下文明确区分当前已确认记忆与历史事实:项目约定优先于领域,领域优先于全局;同范围冲突参考记忆的更新时间。历史提交中的旧实现、偏好描述不能覆盖本次下发的现行记忆;记忆也不扩大当前用户授权或替代安全限制。保留旧事实及其提交依据,不删除历史来制造新记忆生效的结果。
|
|
79
79
|
|
|
80
|
+
检索中明确命名且当前知识已有依据的源文件与 commit 标识优先做身份匹配,再进行语义排序,避免重复历史范例挤出指定文件。尚无知识依据的新文件名仍允许检索相似背景;不把没有范围授权的项目或原始会话当作匹配来源。
|
|
81
|
+
|
|
80
82
|
- journal 游标按来源隔离;证据投影及可恢复任务先落盘,再 ACK。不等待模型才能消费下一条事件。
|
|
81
83
|
- 会话来源接受 bridge 的四客户端契约(Claude Code、Codex、OpenCode、ZCode)。单个 journal 的非法证据不被跳过或 ACK,但不阻塞其他项目;失败位置和错误码写入日志,健康接口提供 `journal.failures`,重复轮询不刷屏,恢复后续接原游标。
|
|
82
84
|
- 不同项目最多并发 2 个分析,同项目按提交顺序执行。未晋升的失败提交会阻挡本项目后续提交,不阻挡其他项目。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanqianx/ai-coding-knowledge-engine",
|
|
3
|
-
"version": "0.2.5-rc.
|
|
3
|
+
"version": "0.2.5-rc.3",
|
|
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",
|
|
@@ -4,6 +4,8 @@ const path = require('node:path');
|
|
|
4
4
|
const { KnowledgeRetrievalService } = require('./runtime/knowledge-retrieval-service');
|
|
5
5
|
const { listMarkdownFiles, isExcludedKnowledgePath } = require('./runtime/markdown-knowledge-indexer');
|
|
6
6
|
const { DomainError } = require('./runtime/contracts');
|
|
7
|
+
const fileNames = query => [...new Set((String(query).match(/[\p{L}\p{N}_./\\-]+\.(?:cjs|mjs|js|jsx|ts|tsx|py|cs|cpp|c|h|hpp|rs|go|html|css|scss|json|ya?ml|toml|md|txt|sh|ps1)\b/giu) || []).map(value => path.posix.basename(value.replace(/\\/g, '/')).toLowerCase()))].slice(0, 20);
|
|
8
|
+
const mentionsFile = (candidate, name) => new RegExp(`(?<![\\p{L}\\p{N}_-])${name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(?![\\p{L}\\p{N}_.-])`, 'iu').test([candidate.entryId, candidate.title, candidate.chunkText, ...(candidate.metadata?.sourcePaths || [])].join('\n'));
|
|
7
9
|
|
|
8
10
|
class EngineRetrieval extends KnowledgeRetrievalService {
|
|
9
11
|
constructor(options) { super(options); this.store = options.store; this.memories = options.memories; this.vectorUrl = options.vectorUrl || 'http://127.0.0.1:8787'; }
|
|
@@ -24,7 +26,10 @@ class EngineRetrieval extends KnowledgeRetrievalService {
|
|
|
24
26
|
const result = super.score(candidate, signals);
|
|
25
27
|
return { score: Math.max(1, result.score), selectionSignals: [...result.selectionSignals, 'exact-commit'] };
|
|
26
28
|
}
|
|
27
|
-
return
|
|
29
|
+
if (candidate.knownFileQuery && !candidate.fileReferences.length) return { score: 0, selectionSignals: [] };
|
|
30
|
+
const result = super.score(candidate, signals);
|
|
31
|
+
if (candidate.fileReferences?.length) return { score: Math.max(1, result.score), selectionSignals: [...result.selectionSignals, ...candidate.fileReferences.map(name => `exact-source-file:${name}`)] };
|
|
32
|
+
return result;
|
|
28
33
|
}
|
|
29
34
|
resolve(input = {}) {
|
|
30
35
|
let project = this.store.getProject(input.projectId || input.projectSlug || input.project);
|
|
@@ -68,6 +73,10 @@ class EngineRetrieval extends KnowledgeRetrievalService {
|
|
|
68
73
|
}
|
|
69
74
|
async recallIndex(scopes, query, current, health) {
|
|
70
75
|
if (!query) { health.index = 'unavailable'; return; }
|
|
76
|
+
// File identifiers, like commit IDs, select current facts before semantic
|
|
77
|
+
// ranking. A not-yet-known filename still permits analogous background.
|
|
78
|
+
const named = fileNames(query), known = named.filter(name => [...current.values()].some(candidate => mentionsFile(candidate, name)));
|
|
79
|
+
for (const candidate of current.values()) { candidate.knownFileQuery = known.length > 0; candidate.fileReferences = known.filter(name => mentionsFile(candidate, name)); }
|
|
71
80
|
const mappings = scopes.map(s => ({ scope: s, registration: this.store.getProject(s.projectId) || (() => { const shared = this.store.state.sharedScopes.find(p => p.id === s.projectId); return shared ? { vectorProjectId: `scope-${shared.kind}-${shared.id}` } : null; })() })).filter(m => m.registration);
|
|
72
81
|
try {
|
|
73
82
|
const url = new URL('/api/search', this.vectorUrl);
|
|
@@ -45,6 +45,22 @@ async function main() {
|
|
|
45
45
|
current.hooks.enabled = false; fs.writeFileSync(configFile, JSON.stringify(current)); assert.equal(manager.statusZCode().state, 'disabled'); assert.throws(() => manager.installZCode(), /禁用/);
|
|
46
46
|
current.hooks.enabled = true; fs.writeFileSync(configFile, JSON.stringify(current)); manager.uninstallZCode();
|
|
47
47
|
assert.deepEqual(JSON.parse(fs.readFileSync(configFile)), before, 'remove only the owned context entry, not capture or user config');
|
|
48
|
+
const oldScript = path.join(root, 'old-prefix/node_modules/@sanqianx/project-knowledge/plugins/project-knowledge/scripts/context-hook.cjs');
|
|
49
|
+
const oldOwned = { type: 'process', command: 'node', args: [oldScript, '--client', 'zcode'], timeoutMs: 12000 };
|
|
50
|
+
const similarThirdParty = { type: 'process', command: 'node', args: [path.join(root, 'custom/project-knowledge/scripts/context-hook.cjs'), '--client', 'zcode'] };
|
|
51
|
+
fs.writeFileSync(configFile, JSON.stringify({ ...before, hooks: { ...before.hooks, events: { ...before.hooks.events, UserPromptSubmit: [{ note: 'preserve group metadata', hooks: [thirdParty, oldOwned, similarThirdParty] }, { hooks: [oldOwned] }] } } }));
|
|
52
|
+
assert.equal(manager.statusZCode().installed, false, 'an old prefix is not current readiness');
|
|
53
|
+
manager.installZCode(); manager.installZCode();
|
|
54
|
+
const rewired = JSON.parse(fs.readFileSync(configFile));
|
|
55
|
+
const entries = rewired.hooks.events.UserPromptSubmit.flatMap(g => g.hooks);
|
|
56
|
+
assert.ok(!entries.some(h => h.args?.[0] === oldScript), 'an old managed npm path must not survive a new-prefix install');
|
|
57
|
+
assert.equal(entries.filter(h => h.args?.[0] === path.join(manager.rootDir, 'plugins/project-knowledge/scripts/context-hook.cjs')).length, 1, 'old duplicate managed entries collapse to one');
|
|
58
|
+
assert.ok(entries.some(h => JSON.stringify(h) === JSON.stringify(similarThirdParty)), 'similar third-party scripts are not owned');
|
|
59
|
+
assert.equal(rewired.hooks.events.UserPromptSubmit[0].note, 'preserve group metadata');
|
|
60
|
+
assert.deepEqual(rewired.hooks.events.Stop, before.hooks.events.Stop);
|
|
61
|
+
assert.equal(rewired.model, before.model); assert.deepEqual(rewired.env, before.env);
|
|
62
|
+
manager.uninstallZCode();
|
|
63
|
+
assert.deepEqual(JSON.parse(fs.readFileSync(configFile)).hooks.events.UserPromptSubmit[0].hooks, [thirdParty, similarThirdParty]);
|
|
48
64
|
fs.writeFileSync(configFile, '{invalid'); assert.throws(() => manager.installZCode(), /JSON/); assert.equal(fs.readFileSync(configFile, 'utf8'), '{invalid');
|
|
49
65
|
const endpoint = { schema: 'project-knowledge/runtime-endpoint/v1', host: '127.0.0.1', pid: process.pid, port: 12345, startedAt: 'test' }; fs.writeFileSync(path.join(root, 'runtime-endpoint.json'), JSON.stringify(endpoint));
|
|
50
66
|
for (const client of ['codex', 'zcode']) {
|
|
@@ -12,13 +12,18 @@ function configuration(file) {
|
|
|
12
12
|
const scriptFor = root => path.join(root, 'plugins/project-knowledge/scripts/context-hook.cjs');
|
|
13
13
|
const normalize = file => { const value = path.resolve(file).replace(/\\/g, '/'); return process.platform === 'win32' ? value.toLowerCase() : value; };
|
|
14
14
|
function owned(hook, root) {
|
|
15
|
-
|
|
15
|
+
if (hook?.type !== 'process' || hook.command !== 'node' || !Array.isArray(hook.args) || hook.args.length !== 3 || typeof hook.args[0] !== 'string' || !path.isAbsolute(hook.args[0]) || hook.args[1] !== '--client' || hook.args[2] !== 'zcode') return false;
|
|
16
|
+
const file = normalize(hook.args[0]);
|
|
17
|
+
// Only the exact current script or a canonical managed npm installation is
|
|
18
|
+
// ours. An unrelated script with a similar basename remains untouched.
|
|
19
|
+
return file === normalize(scriptFor(root)) || /\/node_modules\/(?:@sanqianx\/)?project-knowledge\/plugins\/project-knowledge\/scripts\/context-hook\.cjs$/i.test(file);
|
|
16
20
|
}
|
|
17
21
|
function status({ configFile, rootDir }) {
|
|
18
22
|
const config = configuration(configFile);
|
|
19
|
-
const
|
|
23
|
+
const entries = (config.hooks?.events?.UserPromptSubmit || []).flatMap(g => Array.isArray(g?.hooks) ? g.hooks.filter(h => owned(h, rootDir)) : []);
|
|
24
|
+
const installed = entries.length === 1 && normalize(entries[0].args[0]) === normalize(scriptFor(rootDir)) && fs.existsSync(scriptFor(rootDir));
|
|
20
25
|
const disabled = config.hooks?.enabled === false;
|
|
21
|
-
return { state: disabled ? 'disabled' : installed && config.hooks?.enabled === true ? 'installed' : 'not-installed', supported: true, installed: installed && !disabled && config.hooks?.enabled === true, detail: disabled ? 'ZCode 配置 Hook 已被用户禁用,未擅自启用。' : installed ? '原生 UserPromptSubmit 只读上下文入口已配置;MCP 插件状态不作已安装承诺。' : '开发前知识读取尚未接入;与会话采集独立。' };
|
|
26
|
+
return { state: disabled ? 'disabled' : installed && config.hooks?.enabled === true ? 'installed' : entries.length ? 'needs-repair' : 'not-installed', supported: true, installed: installed && !disabled && config.hooks?.enabled === true, detail: disabled ? 'ZCode 配置 Hook 已被用户禁用,未擅自启用。' : installed ? '原生 UserPromptSubmit 只读上下文入口已配置;MCP 插件状态不作已安装承诺。' : entries.length ? '知识读取入口仍指向旧安装或重复配置,请接入/修复;未误报就绪。' : '开发前知识读取尚未接入;与会话采集独立。' };
|
|
22
27
|
}
|
|
23
28
|
function install({ configFile, rootDir, dryRun }) {
|
|
24
29
|
const config = configuration(configFile);
|
|
@@ -32,9 +37,13 @@ function install({ configFile, rootDir, dryRun }) {
|
|
|
32
37
|
if (!Array.isArray(groups)) throw new Error('ZCode UserPromptSubmit 配置格式无效,未覆盖。');
|
|
33
38
|
const hook = { type: 'process', command: 'node', args: [scriptFor(rootDir), '--client', 'zcode'], timeoutMs: 12000 };
|
|
34
39
|
let found = false;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
const updated = groups.map(group => {
|
|
41
|
+
if (!Array.isArray(group?.hooks)) return group;
|
|
42
|
+
const hooks = group.hooks.flatMap(h => { if (!owned(h, rootDir)) return [h]; if (found) return []; found = true; return [hook]; });
|
|
43
|
+
return hooks.length || !group.hooks.length ? { ...group, hooks } : null;
|
|
44
|
+
}).filter(Boolean);
|
|
45
|
+
if (!found) updated.push({ hooks: [hook] });
|
|
46
|
+
config.hooks.events.UserPromptSubmit = updated; config.hooks.enabled = true;
|
|
38
47
|
AtomicFile.writeJsonAtomic(configFile, config);
|
|
39
48
|
}
|
|
40
49
|
return { state: dryRun ? 'planned' : 'installed', supported: true, installed: !dryRun, detail: 'ZCode 原生前置读取已配置,保留原模型、第三方 Hook 与采集入口;请重新打开会话。' };
|
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
"ui/favicon.svg": "c48be946b224112ebb0238b44263f7a61bda31e531afa895c2e9801c0ff24f32",
|
|
8
8
|
"plugins/project-knowledge/hooks/claude.json": "d61510327e3e1facbb4c645228e9f50cad05a88b33d80c4c22e5ba9f8b9f5359",
|
|
9
9
|
"plugins/project-knowledge/scripts/context-hook.cjs": "b138fab6d8fcb35f8101ccfda565ccec694acb888f94347c5864c804ebb5e1a0",
|
|
10
|
-
"plugins/project-knowledge/.claude-plugin/plugin.json": "
|
|
11
|
-
"plugins/project-knowledge/.mcp.json": "
|
|
10
|
+
"plugins/project-knowledge/.claude-plugin/plugin.json": "2c832fe322ccd16e6086e972df5044dd31551c6f8cb85523d9ca3242d2472f00",
|
|
11
|
+
"plugins/project-knowledge/.mcp.json": "d0d898b3e9d89d29ed141b9161c2875d1d474d8bd35edeee3380687969f3b757",
|
|
12
12
|
"plugins/project-knowledge/hooks/codex.json": "216e03c07295606ef6ebb2c4f669ad0d790b5e7c4f3f34600488033db75e4239",
|
|
13
|
-
"plugins/project-knowledge/.codex-plugin/plugin.json": "
|
|
13
|
+
"plugins/project-knowledge/.codex-plugin/plugin.json": "bf1ae88a4f595bea78d651294575d951e79dc49b2935a5a79dadd76830b73967"
|
|
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.12-rc.
|
|
3
|
+
"version": "5.0.12-rc.3",
|
|
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.12-rc.
|
|
5
|
+
"version": "5.0.12-rc.3",
|
|
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.5-rc.
|
|
6
|
+
"version": "0.2.5-rc.3",
|
|
7
7
|
"root": "_modules/knowledge-engine",
|
|
8
8
|
"files": {
|
|
9
|
-
"README.md": "
|
|
10
|
-
"package.json": "
|
|
9
|
+
"README.md": "62f620325d7776952fdd3d0c0a7ca9e4f6b95719533a83ca635077c0d9ed32d5",
|
|
10
|
+
"package.json": "5355d1f9f9703601241b29eabf16072e4295cc860668cc2730d6a7f0591637fa",
|
|
11
11
|
"src/analysis-contract.js": "913a63b63fb4a4a9df071f95bbeb8f8c40ef87e2f8445ab0742086ced70ffbf7",
|
|
12
12
|
"src/analysis-output.js": "95a3c19dcc9ab85d1dba7fee5b9aec80f9c000dfba89da3fd0be25a81e2fb680",
|
|
13
13
|
"src/analysis-worker.js": "a105cabe6e1573b2d986b1e7a5aaf615e2fb53524156fc8e4e2c0a71f2c97e55",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"src/index-request.js": "78f26b872299005499fff4f2d1f9e8f986784f2ff36a5cf2be2b1e7b7c7d9378",
|
|
21
21
|
"src/knowledge-targets.js": "aa44ec285d16d7b1c0b63bf084058a1d6f867fa9929c753b6b0cbb24f7b20ee2",
|
|
22
22
|
"src/memories.js": "6623fd27d45e297384082d078844aebd55bd0326bd657baae5919e85f9802d28",
|
|
23
|
-
"src/retrieval.js": "
|
|
23
|
+
"src/retrieval.js": "2049f9a4d4ce2d8fb4ccf72a4420ec842b94a5a45936ec83a0a3091b92d9ef13",
|
|
24
24
|
"src/runtime/README.md": "9be676eca76a7857b34d7357194e6f0ee733c22e61d962d53984715dbec7f673",
|
|
25
25
|
"src/runtime/atomic-file.js": "9846b4b69db5ded811acf19a1181ef93615d93efaa542b858e552a7f3124ad23",
|
|
26
26
|
"src/runtime/automation-config.js": "6498c5397be12b155829740595a1553d4dcd6eaa7db6ced3ef88fdc60218f00e",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"ui/app.js": "33a2ba38f0dafbbb2e30a1eb3fc93476bfcaf528e124e1a9b2edd2fb69221e9e",
|
|
46
46
|
"ui/index.html": "ce3565cfefd4838b11d37a91a906ef26ca5a1834844fe3f7c897b56851a3c418"
|
|
47
47
|
},
|
|
48
|
-
"sourceCommit": "
|
|
48
|
+
"sourceCommit": "c0d2a46d9a985c4093e23e080789afe012ed526e"
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
}
|