@sanqianx/project-knowledge 5.0.11 → 5.0.12-rc.1

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.
@@ -9,7 +9,7 @@
9
9
  "plugins": [
10
10
  {
11
11
  "name": "project-knowledge",
12
- "version": "5.0.11",
12
+ "version": "5.0.12-rc.1",
13
13
  "source": "./plugins/project-knowledge",
14
14
  "description": "Search durable project decisions and implementation history through MCP."
15
15
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [5.0.12-rc.1] - 2026-10-12
4
+
5
+ - 候选版新增 Codex、ZCode 原生需求提交前的知识读取,复用引擎同一份事实、记忆与引用。
6
+ - 修复 Codex 旧插件存在时跳过升级;区分安装、定义完整、启用和原生信任状态,未信任不显示已就绪。
7
+ - ZCode 使用用户配置的 process Hook,保留第三方 Hook、模型及既有采集;被禁用的 Hook 不静默重启。
8
+ - 工作台提供上下文和原始会话采集时,仅抑制自身原生重复入口;候选实测未通过前不替换 latest。
9
+
3
10
  ## [5.0.11] - 2026-10-12
4
11
 
5
12
  - Claude Code 的普通开发需求通过原生前置 Hook 自动取得项目/领域知识和适用的已确认长期记忆;MCP 保留用于深入读取。
package/README.md CHANGED
@@ -142,7 +142,7 @@ The application does not create, edit, refresh, or delete `CLAUDE.md`.
142
142
 
143
143
  Claude Code's bundled plugin has a synchronous `UserPromptSubmit` hook. It verifies the current local product instance and asks `/api/engine/context` for project/domain facts plus applicable confirmed project, domain and global memories before the model processes a normal request. No reminder to use MCP is required. The complete context is bounded to 8,000 characters; memories have an independent 3,000-character budget. MCP and the Skill remain available for deeper reads. Workbench uses the same engine endpoint and suppresses duplicate native injection.
144
144
 
145
- Knowledge Integration, Automatic Context and Development Capture are separate capabilities in System Status. An old or disabled plugin is not automatic-context readiness: npm upgrades do not silently upgrade third-party clients, and the existing integration repair action updates stale product plugins. Reopen external sessions after updating. This release adds Claude Code first; Codex and ZCode automatic context are not yet accepted, and OpenCode keeps its existing tool integration.
145
+ Knowledge Integration, Automatic Context and Development Capture are separate capabilities in System Status. An old or disabled plugin is not automatic-context readiness: npm upgrades do not silently upgrade third-party clients, and the existing integration repair action updates stale product plugins. Reopen external sessions after updating. The native-client candidate adds Codex plugin `UserPromptSubmit` and ZCode user-configuration process hooks. Codex requires native `/hooks` review and trust of the current definition; installation never writes trust or uses a bypass flag. Hook trust is read from the native app-server and checked for the selected repository. ZCode context and conversation capture are separate entries, preserving model and third-party configuration. OpenCode keeps its existing tool integration; automatic injection is not claimed for it.
146
146
 
147
147
  Context reading has a ten-second total client deadline and fails open with a visible warning. A vector failure is reported as file fallback, not vector success; an unknown project is ignored rather than borrowing a different project's context. `knowledge.context.prepared` and `agent.context.output` logs contain only metadata and citations, not the request or knowledge body. An output log does not itself prove model delivery; native-client acceptance must verify that separately. Internal analysis and Workbench-owned context suppress repeated client injection.
148
148
 
@@ -113,6 +113,8 @@ async function main() {
113
113
  assert(claudeCalls.some(call => call.args.join(' ') === 'plugin install project-knowledge@project-knowledge --scope user'), 'Claude Code should install the native plugin');
114
114
 
115
115
  const codexCalls = [];
116
+ const codexCache = path.join(TEMP, 'codex-plugin-cache');
117
+ fs.mkdirSync(codexCache, { recursive: true }); fs.cpSync(pluginRoot, codexCache, { recursive: true });
116
118
  const codexManager = new IntegrationManager({
117
119
  rootDir: ROOT,
118
120
  commandResolver: name => name,
@@ -124,7 +126,7 @@ async function main() {
124
126
  if (args.join(' ') === 'plugin list --json') {
125
127
  return {
126
128
  status: 0,
127
- stdout: JSON.stringify({ installed: [{ pluginId: 'project-knowledge@project-knowledge' }] }),
129
+ stdout: JSON.stringify({ installed: [{ pluginId: 'project-knowledge@project-knowledge', version: packageInfo.version, enabled: true, source: { path: codexCache } }] }),
128
130
  stderr: '',
129
131
  };
130
132
  }
@@ -32,9 +32,13 @@ function makeManager() {
32
32
  const cache = path.join(homeDir, '.claude/plugins/cache/project-knowledge/project-knowledge', version);
33
33
  if (command === 'claude' && args.slice(0, 2).join(' ') === 'plugin install') { fs.mkdirSync(cache, { recursive: true }); fs.cpSync(path.join(ROOT, 'plugins/project-knowledge'), cache, { recursive: true }); }
34
34
  if (command === 'claude' && args.join(' ') === 'plugin list --json') return { status: 0, stdout: JSON.stringify(fs.existsSync(cache) ? [{ id: 'project-knowledge@project-knowledge', version, enabled: true }] : []), stderr: '' };
35
+ const codexCache = path.join(homeDir, 'codex-plugin-cache');
36
+ if (command === 'codex' && args.slice(0, 2).join(' ') === 'plugin add') { fs.mkdirSync(codexCache, { recursive: true }); fs.cpSync(path.join(ROOT, 'plugins/project-knowledge'), codexCache, { recursive: true }); }
37
+ if (command === 'codex' && args.join(' ') === 'plugin list --json') return { status: 0, stdout: JSON.stringify({ installed: fs.existsSync(codexCache) ? [{ pluginId: 'project-knowledge@project-knowledge', version, enabled: true, source: { path: codexCache } }] : [] }), stderr: '' };
35
38
  return { status: 0, stdout: '', stderr: '' };
36
39
  },
37
40
  bridgeHomeDir: bridgeHome,
41
+ codexHookProbe: async () => ({ data: [] }),
38
42
  claudeSettingsFile: claudeSettings,
39
43
  codexConfigFile: codexConfig,
40
44
  openCodePluginsDir: openCodePlugins,
@@ -0,0 +1,61 @@
1
+ 'use strict';
2
+ const assert = require('node:assert/strict');
3
+ const fs = require('node:fs');
4
+ const os = require('node:os');
5
+ const path = require('node:path');
6
+ const { IntegrationManager } = require('../lib/integration-installer');
7
+ const { run } = require('../../plugins/project-knowledge/scripts/context-hook.cjs');
8
+
9
+ async function main() {
10
+ const plugin = path.resolve(__dirname, '../../plugins/project-knowledge');
11
+ assert.equal(JSON.parse(fs.readFileSync(path.join(plugin, '.codex-plugin/plugin.json'))).hooks, './hooks/codex.json');
12
+ const hook = JSON.parse(fs.readFileSync(path.join(plugin, 'hooks/codex.json'))).hooks.UserPromptSubmit[0].hooks[0];
13
+ assert.equal(hook.additionalContextLimit, 0); assert.equal(hook.async, undefined); assert.match(hook.command, /--client codex$/);
14
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'native-context-integration-'));
15
+ try {
16
+ const configFile = path.join(root, '.zcode/cli/config.json'); fs.mkdirSync(path.dirname(configFile), { recursive: true });
17
+ const thirdParty = { type: 'process', command: 'node', args: ['third-party-hook.js'], timeoutMs: 4000 };
18
+ const before = { model: 'user-model', env: { USER_VALUE: 'keep' }, hooks: { enabled: true, events: { UserPromptSubmit: [{ hooks: [thirdParty] }], Stop: [{ hooks: [thirdParty] }] } } };
19
+ fs.writeFileSync(configFile, JSON.stringify(before));
20
+ let version = '5.0.9', enabled = true, trusted = false;
21
+ const calls = [], cache = path.join(root, 'codex-cache');
22
+ const manager = new IntegrationManager({ homeDir: root, zcodeConfigFile: configFile, commandResolver: name => name,
23
+ codexHookProbe: async () => ({ data: [{ cwd: root, errors: [], warnings: [], hooks: [{ eventName: 'userPromptSubmit', pluginId: 'project-knowledge@project-knowledge', command: hook.command, sourcePath: path.join(cache, 'hooks/codex.json'), enabled: true, trustStatus: trusted ? 'trusted' : 'untrusted' }] }] }),
24
+ runner: (command, args) => {
25
+ calls.push(args.join(' '));
26
+ if (args.join(' ') === 'plugin marketplace list') return { status: 0, stdout: 'project-knowledge marketplace' };
27
+ if (args.join(' ') === 'plugin list --json') return { status: 0, stdout: JSON.stringify({ installed: [{ pluginId: 'project-knowledge@project-knowledge', version, enabled, source: { path: cache } }] }) };
28
+ if (args.slice(0, 2).join(' ') === 'plugin add') { version = manager.version; fs.mkdirSync(cache, { recursive: true }); fs.cpSync(plugin, cache, { recursive: true }); }
29
+ return { status: 0, stdout: '' };
30
+ }
31
+ });
32
+ assert.equal(manager.statusCodex().state, 'needs-update'); manager.installCodex();
33
+ assert.ok(calls.includes('plugin marketplace upgrade project-knowledge')); assert.equal(manager.statusCodex().state, 'installed');
34
+ let automatic = await manager.automaticCodexContextStatus(manager.statusCodex(), root);
35
+ assert.equal(automatic.state, 'waiting-trust'); assert.equal(automatic.configured, false);
36
+ trusted = true; automatic = await manager.automaticCodexContextStatus(manager.statusCodex(), root); assert.equal(automatic.configured, true);
37
+ const count = calls.filter(c => c.startsWith('plugin add')).length; manager.installCodex(); assert.equal(calls.filter(c => c.startsWith('plugin add')).length, count);
38
+ enabled = false; assert.throws(() => manager.installCodex(), /禁用/); assert.equal(manager.statusCodex().state, 'disabled'); enabled = true;
39
+ assert.ok(calls.every(c => !/trust|bypass/.test(c)), 'installer never writes native trust or bypasses review');
40
+ const installed = manager.installZCode(); assert.equal(installed.state, 'installed'); manager.installZCode();
41
+ const current = JSON.parse(fs.readFileSync(configFile));
42
+ assert.equal(current.model, before.model); assert.deepEqual(current.env, before.env); assert.deepEqual(current.hooks.events.Stop, before.hooks.events.Stop);
43
+ assert.equal(current.hooks.events.UserPromptSubmit.length, 2); assert.deepEqual(current.hooks.events.UserPromptSubmit[0].hooks[0], thirdParty);
44
+ assert.equal(manager.statusZCode().installed, true); assert.equal(manager.automaticContextStatus('zcode', manager.statusZCode()).configured, true);
45
+ current.hooks.enabled = false; fs.writeFileSync(configFile, JSON.stringify(current)); assert.equal(manager.statusZCode().state, 'disabled'); assert.throws(() => manager.installZCode(), /禁用/);
46
+ current.hooks.enabled = true; fs.writeFileSync(configFile, JSON.stringify(current)); manager.uninstallZCode();
47
+ assert.deepEqual(JSON.parse(fs.readFileSync(configFile)), before, 'remove only the owned context entry, not capture or user config');
48
+ fs.writeFileSync(configFile, '{invalid'); assert.throws(() => manager.installZCode(), /JSON/); assert.equal(fs.readFileSync(configFile, 'utf8'), '{invalid');
49
+ 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
+ for (const client of ['codex', 'zcode']) {
51
+ let input;
52
+ const result = await run({ cwd: root, session_id: 'session-' + client, hook_event_name: 'UserPromptSubmit', prompt: '请开发导出功能' }, { argv: ['--client', client], env: { KB_DATA_DIR: root }, fetchImpl: async (url, opts) => {
53
+ if (String(url).endsWith('/identity')) return Response.json({ ...endpoint, schema: 'project-knowledge/runtime-control/v1', name: 'project-knowledge', dataDir: root });
54
+ input = JSON.parse(opts.body); return Response.json({ ok: true, requestId: input.requestId, projectId: 'project-test', status: 'ready', prompt: '<knowledge_context>facts and memories</knowledge_context>', references: [{}], facts: [{}], memories: [{}] });
55
+ } });
56
+ assert.equal(input.client, client); assert.equal(result.hookSpecificOutput.hookEventName, 'UserPromptSubmit'); assert.match(result.hookSpecificOutput.additionalContext, /facts and memories/);
57
+ }
58
+ console.log('Native Codex/ZCode context integration, trust honesty, compatibility and preservation passed.');
59
+ } finally { fs.rmSync(root, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 }); }
60
+ }
61
+ main().catch(error => { console.error(error); process.exitCode = 1; });
@@ -50,7 +50,8 @@ const { runCli } = require('../../bin/project-knowledge-integrations');
50
50
  const status = await manager.statusAll({ clients: ['zcode'] });
51
51
  assert(status.clients[0].configFound && status.clients[0].developmentCapture.installed);
52
52
  assert.equal(status.clients[0].knowledgeIntegration.installed, false);
53
- assert.equal(status.clients[0].knowledgeIntegration.supported, false, 'capture never claims an unverified retrieval plugin');
53
+ assert.equal(status.clients[0].knowledgeIntegration.supported, true, 'native context is supported but capture alone does not install it');
54
+ assert.equal(status.clients[0].automaticContext.configured, false, 'capture alone never implies automatic knowledge readiness');
54
55
  assert.equal(await runCli(['update', '--ide', 'zcode', '--capture-only', '--json'], { manager }), 0);
55
56
  const dry = new IntegrationManager({ ...options, dryRun: true });
56
57
  const untouched = fs.readFileSync(configFile, 'utf8');
@@ -0,0 +1,17 @@
1
+ 'use strict';
2
+
3
+ // Native read-only discovery, never trust-file edits or bypass flags. Reuse
4
+ // the module's verified stdio transport rather than a second RPC lifecycle.
5
+ async function readCodexHooks({ command, env, cwd }) {
6
+ const { createAppServerBridge } = require('claude-ai-workbench/packages/server/lib/bridges/app-server-bridge');
7
+ const { resolveCliInvocation } = require('claude-ai-workbench/packages/server/lib/drivers/cli-json-runner');
8
+ const bridge = createAppServerBridge({ command, args: ['app-server'], env, cwd, resolveInvocation: resolveCliInvocation, maxRestarts: 0, idleTimeoutMs: 0, requestTimeoutMs: 10000 });
9
+ try {
10
+ await bridge.start();
11
+ await bridge.request('initialize', { clientInfo: { name: 'project-knowledge-hook-status', version: '1' }, capabilities: { experimentalApi: true } });
12
+ await bridge.notify('initialized');
13
+ return await bridge.request('hooks/list', { cwds: [cwd] });
14
+ } finally { await bridge.close(); }
15
+ }
16
+
17
+ module.exports = { readCodexHooks };
@@ -183,6 +183,7 @@ class IntegrationManager {
183
183
  this.marketplaceSource = options.marketplaceSource || DEFAULT_MARKETPLACE_SOURCE;
184
184
  this.runner = options.runner || defaultRunner;
185
185
  this.commandResolver = options.commandResolver || defaultCommandResolver;
186
+ this.codexHookProbe = options.codexHookProbe || require('./codex-context-status').readCodexHooks;
186
187
  this.dryRun = Boolean(options.dryRun);
187
188
  this.logger = options.logger || null;
188
189
  this.operationId = options.operationId || '';
@@ -386,7 +387,9 @@ class IntegrationManager {
386
387
  executable(client) {
387
388
  const name = client === 'claude' ? 'claude' : client;
388
389
  if (this.dryRun) return process.platform === 'win32' ? `${name}.cmd` : name;
389
- return this.commandResolver(name, this.env);
390
+ const command = this.commandResolver(name, this.env);
391
+ if (command || client !== 'zcode' || this.commandResolver !== defaultCommandResolver) return command;
392
+ try { return require('claude-ai-workbench/packages/server/lib/drivers/zcode-driver').discoverZCodeCli({ env: this.env })?.path || ''; } catch { return ''; }
390
393
  }
391
394
 
392
395
  detectClients() {
@@ -398,12 +401,11 @@ class IntegrationManager {
398
401
  try { const stat = fs.statSync(target); return client === 'opencode' ? stat.isDirectory() : stat.isFile(); } catch { return false; }
399
402
  }
400
403
 
401
- // Capture support does not imply a verified MCP/plugin installer. Never
402
- // present ZCode knowledge retrieval as installed merely because hooks exist.
403
- statusZCode() { return { state: 'not-supported', supported: false, installed: false, detail: 'ZCode 当前支持会话采集;知识检索插件未接入,不会自动安装。' }; }
404
- installZCode() { return this.statusZCode(); }
405
- updateZCode() { return this.statusZCode(); }
406
- uninstallZCode() { return this.statusZCode(); }
404
+ zcodeContextOptions() { return { configFile: this.zcodeConfigFile, rootDir: this.rootDir, dryRun: this.dryRun }; }
405
+ statusZCode() { return require('./zcode-context-integration').status(this.zcodeContextOptions()); }
406
+ installZCode() { return require('./zcode-context-integration').install(this.zcodeContextOptions()); }
407
+ updateZCode() { return this.installZCode(); }
408
+ uninstallZCode() { return require('./zcode-context-integration').uninstall(this.zcodeContextOptions()); }
407
409
 
408
410
  execute(client, args, options = {}) {
409
411
  const startedAt = Date.now();
@@ -548,21 +550,53 @@ class IntegrationManager {
548
550
 
549
551
  installCodex() {
550
552
  this.ensureCodexMarketplace();
551
- if (!this.dryRun && this.codexPlugin()) {
553
+ const plugin = this.dryRun ? null : this.codexPlugin();
554
+ if (plugin?.enabled === false) throw new Error('知识库插件已被用户禁用,请在 Codex 中明确启用,未静默修改。');
555
+ if (plugin && (plugin.version !== this.version || !this.codexContextFilesMatch(plugin))) return this.updateCodex();
556
+ if (plugin) {
552
557
  return { state: 'installed', detail: 'plugin already installed' };
553
558
  }
554
559
  this.execute('codex', ['plugin', 'add', `${INTEGRATION_NAME}@${MARKETPLACE_NAME}`]);
555
- return { state: 'installed', detail: 'plugin, MCP, and Skill installed' };
560
+ if (!this.dryRun) this.verifyCodexInstallation();
561
+ return { state: 'installed', detail: '插件已安装;在原生 /hooks 中审查并信任前置读取 Hook 后才会运行。' };
556
562
  }
557
563
 
558
564
  updateCodex() {
565
+ if (!this.dryRun && this.codexPlugin()?.enabled === false) throw new Error('知识库插件已被用户禁用,请在 Codex 中明确启用,未静默修改。');
559
566
  this.ensureCodexMarketplace();
560
567
  this.execute('codex', ['plugin', 'marketplace', 'upgrade', MARKETPLACE_NAME]);
561
568
  if (this.dryRun || this.codexPlugin()) {
562
569
  this.execute('codex', ['plugin', 'remove', `${INTEGRATION_NAME}@${MARKETPLACE_NAME}`]);
563
570
  }
564
571
  this.execute('codex', ['plugin', 'add', `${INTEGRATION_NAME}@${MARKETPLACE_NAME}`]);
565
- return { state: 'updated', detail: 'marketplace refreshed and plugin reinstalled' };
572
+ if (!this.dryRun) this.verifyCodexInstallation();
573
+ return { state: 'updated', detail: '知识插件已更新;新定义须在原生 /hooks 中审查并信任,未写入信任或跳过审查。' };
574
+ }
575
+
576
+ codexContextFilesMatch(plugin) {
577
+ const directory = plugin?.source?.path;
578
+ if (typeof directory !== 'string' || !path.isAbsolute(directory)) return false;
579
+ return ['hooks/codex.json', 'scripts/context-hook.cjs', '.mcp.json', '.codex-plugin/plugin.json'].every(file => {
580
+ try { const read = root => new TextDecoder('utf-8', { fatal: true }).decode(fs.readFileSync(path.join(root, file))).replace(/\r\n/g, '\n'); return read(directory) === read(path.join(this.rootDir, 'plugins', INTEGRATION_NAME)); } catch { return false; }
581
+ });
582
+ }
583
+
584
+ verifyCodexInstallation() {
585
+ const plugin = this.codexPlugin();
586
+ if (!plugin || plugin.version !== this.version || plugin.enabled === false || !this.codexContextFilesMatch(plugin)) throw new Error(`Codex 插件或前置读取文件未更新到 ${this.version},未把旧插件标为成功。`);
587
+ }
588
+
589
+ async automaticCodexContextStatus(knowledge, cwd = this.homeDir) {
590
+ const basic = this.automaticContextStatus('codex', knowledge);
591
+ if (!basic.filesVerified) return basic;
592
+ try {
593
+ const snapshot = await this.codexHookProbe({ command: this.executable('codex'), env: { ...this.env, CODEX_HOME: this.env.CODEX_HOME || path.dirname(this.codexConfigFile) }, cwd });
594
+ const entries = snapshot?.data || [];
595
+ const hooks = entries.flatMap(entry => entry.hooks || []).filter(hook => hook.eventName === 'userPromptSubmit' && hook.pluginId === `${INTEGRATION_NAME}@${MARKETPLACE_NAME}` && /context-hook\.cjs/.test(hook.command || ''));
596
+ const configured = hooks.length === 1 && hooks[0].enabled === true && ['trusted', 'managed'].includes(hooks[0].trustStatus) && entries.every(entry => !entry.errors?.length);
597
+ const disabled = hooks.some(h => h.enabled === false);
598
+ return { ...basic, configured, state: configured ? 'configured' : disabled ? 'disabled' : hooks.length ? 'waiting-trust' : 'needs-repair', trustStatus: hooks[0]?.trustStatus || 'unknown', detail: configured ? '原生前置读取定义已启用并信任;实际读取见知识上下文日志。' : disabled ? 'Codex 前置读取 Hook 已禁用,未擅自启用。' : hooks.length ? '待信任:请在 Codex 原生 /hooks 中审查 Project Knowledge 的 UserPromptSubmit 定义;更新后需重新审查。' : 'Codex 未加载前置读取 Hook,请检查客户端版本、Hook 开关及插件来源。' };
599
+ } catch { return { ...basic, state: 'check-failed', configured: false, detail: '无法验证原生 Hook 的信任状态;未将已安装插件当作自动读取已就绪。请在 Codex 中打开 /hooks 检查。' }; }
566
600
  }
567
601
 
568
602
  uninstallCodex() {
@@ -580,10 +614,12 @@ class IntegrationManager {
580
614
  if (!command) return { state: 'unavailable', installed: false, detail: 'Codex CLI not found' };
581
615
  const plugin = this.codexPlugin();
582
616
  return {
583
- state: plugin ? 'installed' : 'not-installed',
617
+ state: plugin?.enabled === false ? 'disabled' : plugin && plugin.version !== this.version ? 'needs-update' : plugin ? 'installed' : 'not-installed',
584
618
  installed: Boolean(plugin),
585
619
  version: plugin?.version,
586
- detail: plugin ? `plugin enabled: ${plugin.enabled !== false}` : 'plugin not installed',
620
+ expectedVersion: this.version,
621
+ source: plugin?.source,
622
+ detail: plugin?.enabled === false ? '插件已禁用,未自动启用。' : plugin && plugin.version !== this.version ? '知识插件版本不匹配,请更新后重新审查原生 Hook。' : plugin ? '知识插件已安装;原生 Hook 信任状态单独验证。' : 'plugin not installed',
587
623
  };
588
624
  }
589
625
 
@@ -764,7 +800,7 @@ class IntegrationManager {
764
800
  } catch (error) {
765
801
  entry.knowledgeIntegration = { ok: false, state: 'failed', detail: error.message };
766
802
  }
767
- entry.automaticContext = this.automaticContextStatus(client, entry.knowledgeIntegration);
803
+ entry.automaticContext = client === 'codex' ? await this.automaticCodexContextStatus(entry.knowledgeIntegration, options.cwd) : this.automaticContextStatus(client, entry.knowledgeIntegration);
768
804
  }
769
805
  entry.developmentCapture = await this.captureOperation('status', client);
770
806
  summary.clients.push(entry);
@@ -773,6 +809,11 @@ class IntegrationManager {
773
809
  }
774
810
 
775
811
  automaticContextStatus(client, knowledge) {
812
+ if (client === 'zcode') { const available = Boolean(this.executable('zcode')); return { supported: true, configured: available && knowledge?.state === 'installed', state: available ? knowledge?.state || 'not-installed' : 'unavailable', detail: available ? knowledge?.detail || '前置读取尚未接入。' : '未找到可运行的 ZCode;配置存在不等于原生入口可用。' }; }
813
+ if (client === 'codex') {
814
+ const filesVerified = knowledge?.state === 'installed' && this.codexContextFilesMatch({ source: knowledge.source });
815
+ return { supported: true, configured: false, filesVerified, state: filesVerified ? 'waiting-trust' : knowledge?.state === 'installed' ? 'needs-repair' : knowledge?.state || 'not-installed', expectedVersion: this.version, detail: filesVerified ? '已安装,等待验证原生 Hook 的信任状态。' : '插件版本、启用或前置读取文件尚未通过验证,请接入/修复。' };
816
+ }
776
817
  if (client !== 'claude') return { supported: false, state: 'not-supported', configured: false, detail: '当前版本尚未验收此客户端的自动前置读取;知识工具与采集状态单独显示。' };
777
818
  const state = knowledge?.state || 'not-installed';
778
819
  try { if (JSON.parse(fs.readFileSync(this.claudeSettingsFile, 'utf8')).disableAllHooks === true) return { supported: true, state: 'disabled', configured: false, detail: 'Claude Code 的 Hook 已被用户禁用,未擅自修改。' }; } catch {}
@@ -1300,7 +1300,10 @@ function createRequestHandler(runtime, options = {}) {
1300
1300
  // Integration and Development Capture per client, with separate status.
1301
1301
  if (method === 'GET' && pathname === '/api/integrations/status') {
1302
1302
  const manager = new IntegrationManager({ ...options.integrationOptions, rootDir: runtime.rootDir, logger: runtime.logger, operationId: requestOperationId, bridgeHomeDir: runtime.bridgeAdapter.hookBinding().bridgeHomeDir });
1303
- const summary = await manager.statusAll({ captureOnly: url.searchParams.get('captureOnly') === '1' });
1303
+ const projectId = url.searchParams.get('projectId');
1304
+ if (projectId && !runtime.registryStore.listIds().includes(projectId)) throw new DomainError('PROJECT_NOT_FOUND', '当前项目尚未登记。', { status: 404 });
1305
+ const cwd = projectId ? runtime.projectStore.readConfig(projectId).repoPath : undefined;
1306
+ const summary = await manager.statusAll({ captureOnly: url.searchParams.get('captureOnly') === '1', cwd });
1304
1307
  return send(res, 200, { ok: true, ...summary });
1305
1308
  }
1306
1309
  if (method === 'POST' && pathname === '/api/integrations/install') {
@@ -0,0 +1,51 @@
1
+ 'use strict';
2
+ const fs = require('node:fs');
3
+ const path = require('node:path');
4
+ const AtomicFile = require('./atomic-file');
5
+
6
+ function configuration(file) {
7
+ if (!fs.existsSync(file)) return {};
8
+ let value; try { value = JSON.parse(fs.readFileSync(file, 'utf8')); } catch { throw new Error('ZCode 配置不是有效 JSON,未覆盖原文件。'); }
9
+ if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('ZCode 配置必须是 JSON 对象,未覆盖原文件。');
10
+ return value;
11
+ }
12
+ const scriptFor = root => path.join(root, 'plugins/project-knowledge/scripts/context-hook.cjs');
13
+ const normalize = file => { const value = path.resolve(file).replace(/\\/g, '/'); return process.platform === 'win32' ? value.toLowerCase() : value; };
14
+ function owned(hook, root) {
15
+ return hook?.type === 'process' && hook.command === 'node' && Array.isArray(hook.args) && hook.args.length === 3 && typeof hook.args[0] === 'string' && normalize(hook.args[0]) === normalize(scriptFor(root)) && hook.args[1] === '--client' && hook.args[2] === 'zcode';
16
+ }
17
+ function status({ configFile, rootDir }) {
18
+ const config = configuration(configFile);
19
+ const installed = (config.hooks?.events?.UserPromptSubmit || []).some(g => Array.isArray(g?.hooks) && g.hooks.some(h => owned(h, rootDir))) && fs.existsSync(scriptFor(rootDir));
20
+ 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 插件状态不作已安装承诺。' : '开发前知识读取尚未接入;与会话采集独立。' };
22
+ }
23
+ function install({ configFile, rootDir, dryRun }) {
24
+ const config = configuration(configFile);
25
+ if (config.hooks?.enabled === false) throw new Error('ZCode Hook 已被用户禁用,请在客户端明确启用后重试;未覆盖配置。');
26
+ if (!fs.existsSync(scriptFor(rootDir))) throw new Error('缺少已安装的知识读取脚本。');
27
+ if (!dryRun) {
28
+ if (config.hooks && (typeof config.hooks !== 'object' || Array.isArray(config.hooks))) throw new Error('ZCode Hook 配置格式无效,未覆盖。');
29
+ config.hooks ||= {}; config.hooks.events ||= {};
30
+ if (typeof config.hooks.events !== 'object' || Array.isArray(config.hooks.events)) throw new Error('ZCode Hook 事件配置格式无效,未覆盖。');
31
+ const groups = config.hooks.events.UserPromptSubmit || [];
32
+ if (!Array.isArray(groups)) throw new Error('ZCode UserPromptSubmit 配置格式无效,未覆盖。');
33
+ const hook = { type: 'process', command: 'node', args: [scriptFor(rootDir), '--client', 'zcode'], timeoutMs: 12000 };
34
+ let found = false;
35
+ for (const group of groups) if (Array.isArray(group?.hooks)) group.hooks = group.hooks.map(h => { if (!owned(h, rootDir)) return h; found = true; return hook; });
36
+ if (!found) groups.push({ hooks: [hook] });
37
+ config.hooks.events.UserPromptSubmit = groups; config.hooks.enabled = true;
38
+ AtomicFile.writeJsonAtomic(configFile, config);
39
+ }
40
+ return { state: dryRun ? 'planned' : 'installed', supported: true, installed: !dryRun, detail: 'ZCode 原生前置读取已配置,保留原模型、第三方 Hook 与采集入口;请重新打开会话。' };
41
+ }
42
+ function uninstall({ configFile, rootDir, dryRun }) {
43
+ const config = configuration(configFile), groups = config.hooks?.events?.UserPromptSubmit;
44
+ if (!dryRun && Array.isArray(groups)) {
45
+ const kept = groups.map(group => { if (!Array.isArray(group?.hooks)) return group; const hooks = group.hooks.filter(h => !owned(h, rootDir)); return hooks.length ? { ...group, hooks } : null; }).filter(Boolean);
46
+ if (kept.length) config.hooks.events.UserPromptSubmit = kept; else delete config.hooks.events.UserPromptSubmit;
47
+ AtomicFile.writeJsonAtomic(configFile, config);
48
+ }
49
+ return { state: dryRun ? 'planned' : 'removed', supported: true, installed: false, detail: '已移除知识上下文入口,采集、模型和第三方设置保留。' };
50
+ }
51
+ module.exports = { install, status, uninstall };
@@ -20,6 +20,6 @@ for (const module of Object.values(manifest.modules)) {
20
20
  module.version = installed.version;
21
21
  for (const relative of Object.keys(module.files)) { if (!safeRelative(relative)) throw new Error('Unsafe content path'); const target = path.join(directory, relative); if (fs.lstatSync(target).isSymbolicLink()) throw new Error('Unsafe content symlink'); module.files[relative] = digest(target); }
22
22
  }
23
- for (const relative of ['ui/index.html', 'ui/product.js', 'ui/product.css', 'ui/favicon.svg', 'plugins/project-knowledge/hooks/claude.json', 'plugins/project-knowledge/scripts/context-hook.cjs', 'plugins/project-knowledge/.claude-plugin/plugin.json', 'plugins/project-knowledge/.mcp.json']) manifest.productFiles[relative] = digest(path.join(root, relative));
23
+ for (const relative of ['ui/index.html', 'ui/product.js', 'ui/product.css', 'ui/favicon.svg', 'plugins/project-knowledge/hooks/claude.json', 'plugins/project-knowledge/hooks/codex.json', 'plugins/project-knowledge/scripts/context-hook.cjs', 'plugins/project-knowledge/.claude-plugin/plugin.json', 'plugins/project-knowledge/.codex-plugin/plugin.json', 'plugins/project-knowledge/.mcp.json']) manifest.productFiles[relative] = digest(path.join(root, relative));
24
24
  AtomicFile.writeJsonAtomic(file, manifest);
25
25
  console.log('Updated content hashes from exact installed modules and reviewed product assets; this is not release acceptance.');
@@ -7,7 +7,7 @@ const { safeRelative } = require('./pack-module-candidates');
7
7
  function verifyProductRuntime(coreRoot, resolveFrom = path.join(coreRoot, 'package.json')) {
8
8
  const manifest = JSON.parse(fs.readFileSync(path.join(coreRoot, 'module-runtime-manifest.json')));
9
9
  if (manifest.schema !== 'project-knowledge/module-content/v1' || Object.keys(manifest.modules).length !== 3) throw new Error('Incomplete module content manifest');
10
- for (const file of ['ui/index.html', 'ui/product.js', 'ui/product.css', 'ui/favicon.svg', 'plugins/project-knowledge/hooks/claude.json', 'plugins/project-knowledge/scripts/context-hook.cjs', 'plugins/project-knowledge/.claude-plugin/plugin.json', 'plugins/project-knowledge/.mcp.json']) if (!manifest.productFiles?.[file]) throw new Error(`Product asset missing from content manifest: ${file}`);
10
+ for (const file of ['ui/index.html', 'ui/product.js', 'ui/product.css', 'ui/favicon.svg', 'plugins/project-knowledge/hooks/claude.json', 'plugins/project-knowledge/hooks/codex.json', 'plugins/project-knowledge/scripts/context-hook.cjs', 'plugins/project-knowledge/.claude-plugin/plugin.json', 'plugins/project-knowledge/.codex-plugin/plugin.json', 'plugins/project-knowledge/.mcp.json']) if (!manifest.productFiles?.[file]) throw new Error(`Product asset missing from content manifest: ${file}`);
11
11
  const resolve = createRequire(resolveFrom);
12
12
  for (const [relative, expected] of Object.entries(manifest.productFiles || {})) {
13
13
  if (!safeRelative(relative) || crypto.createHash('sha256').update(fs.readFileSync(path.join(coreRoot, relative))).digest('hex') !== expected) throw new Error(`Product UI content mismatch: ${relative}`);
@@ -25,7 +25,7 @@ Clients:
25
25
  claude Claude Code plugin (MCP + Skill) + managed Bridge hooks
26
26
  opencode OpenCode MCP + global Skill + managed Bridge plugin
27
27
  codex Codex plugin (MCP + Skill) + managed Bridge notify
28
- zcode ZCode managed Bridge hooks (capture only; no verified MCP installer)
28
+ zcode ZCode native context + managed Bridge capture (not an MCP-plugin claim)
29
29
  all All supported clients
30
30
 
31
31
  Options:
@@ -39,7 +39,8 @@ Options:
39
39
  -h, --help Show this help
40
40
 
41
41
  Without --ide, installed clients are detected automatically. One install
42
- action configures both capabilities; no client UI needs to be opened.`);
42
+ action configures both capabilities. Codex automatic context additionally
43
+ requires native /hooks review and trust; installation never bypasses it.`);
43
44
  }
44
45
 
45
46
  function parseArgs(argv) {
@@ -2,19 +2,21 @@
2
2
  "schema": "project-knowledge/module-content/v1",
3
3
  "productFiles": {
4
4
  "ui/index.html": "2f05ea669e27cba542f80c700a4a0b59cc8db3f15f7a83604fc6049ac816c53d",
5
- "ui/product.js": "9b285fc6ebc35d5dc86f2793a0f80158510dd8eebaa939a415583c57fe25f6be",
5
+ "ui/product.js": "7b1495a5ead94d908c3f2380dd5fd1c2842c28e62a287b8aa6eaad1be22dfbf9",
6
6
  "ui/product.css": "0c31d4937780b3a277689b4619208ad0e5dc59a84d7e27fc16b5f1815d59b47c",
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": "b04275a69c2790c7706c480615afc57211612d6ca8ce2865aa2ba9a742c27f14",
11
- "plugins/project-knowledge/.mcp.json": "0d371611f442631f1f03520d00d146242d23c584b50efccbe29d032aad68c35c"
10
+ "plugins/project-knowledge/.claude-plugin/plugin.json": "9007b7f727356dbfd3a7a92ae07ffc7dd122e69c16bbcf112762481230dba2b8",
11
+ "plugins/project-knowledge/.mcp.json": "45f33decfb148a86ae4ac5ed58f8d06a117bf49068daabbf1a11c75d1a40a813",
12
+ "plugins/project-knowledge/hooks/codex.json": "216e03c07295606ef6ebb2c4f669ad0d790b5e7c4f3f34600488033db75e4239",
13
+ "plugins/project-knowledge/.codex-plugin/plugin.json": "e3307ed802b063f4c2e808ed7cd925e8872df53f2387eb64d39f8951d453929d"
12
14
  },
13
15
  "modules": {
14
16
  "terminal": {
15
17
  "name": "@sanqianx/claude-ai-workbench",
16
18
  "installName": "claude-ai-workbench",
17
- "version": "0.13.2-rc.8",
19
+ "version": "0.13.2-rc.9",
18
20
  "files": {
19
21
  "LICENSE": "c1b9df1275e769f3dbab000d1e457a2d4b0f28eb5da6c77e48dc37eeba202ed7",
20
22
  "README.md": "5a382c531372deaa63b3e014f4dbd077d41ce02efee40347973463081848cb86",
@@ -31,26 +33,26 @@
31
33
  "apps/minimal-test-host/README.md": "fa73136f4ee3865c2d91eb97b2e8c03679aa4688e44c1e1c8ec6e27d604b0de5",
32
34
  "docs/sdk/FILE_LAYOUT.md": "17882f96505c8161bfb66876684b9d2d3f50021a111b8b844502d3cb4e97870a",
33
35
  "docs/sdk/INTEGRATION.md": "d4c0c80d2d2fc610205102d703a429bfaa20f9f3a48f1718f5bab02ee90138df",
34
- "docs/sdk/openapi.yaml": "bbd4e3e4e91cc04b9048d1f778e14009f83cc635d2ed9bb2c3200538a94b53b5",
36
+ "docs/sdk/openapi.yaml": "f93a0c93247267603838448814ec3f3be7d9e6827360024267cd1a3f1584721b",
35
37
  "docs/sdk/README.md": "7ab591d39c36a3b84cfdf487ff567040ec453975f3ef0fba86dc822094667b61",
36
- "package.json": "06a25664a3ce87f588a7d52ad6c5cf6d5c569f791c59f87a58ae7a0d74bc4f71",
38
+ "package.json": "01da288b2ade8f920695aac2f4687af3c686e153473565ac5def9ed15c1d86c1",
37
39
  "packages/client/index.d.ts": "fb66b7b21ac936cddcb7f74543cf4f3bb7e80c169534ec249ab8ef2356224162",
38
40
  "packages/client/index.js": "92fe5ddd32289d729f336f669e47bd9601e562f5a4309ee4fa7f7f15b567edc2",
39
- "packages/client/package.json": "24c0907cf9e620bc1e5bdce324553b6f8e2e32dd7c272bcfcb455f499a49b7e2",
41
+ "packages/client/package.json": "563c7296c103fda4ab1c606579e0c605ed44369753baec22a6152301836dbc5f",
40
42
  "packages/client/README.md": "342dff9b1dcbc85fccc9b980ed3e4e455aa71133af1d0805b8431ea2ac564ef8",
41
43
  "packages/contracts/index.d.ts": "edc717067bfb864c6c95df0c408a799fbf90b7552519131e6dc35af3f9eb9006",
42
44
  "packages/contracts/index.js": "565b4a88190dbb8994cd0a9309b30b00f64305d65505fff9fa360d7ce69dfb58",
43
- "packages/contracts/package.json": "3c4f62b87f034065491e6ae911216d9e04b090b6934428aa21fd8c97a7db0691",
45
+ "packages/contracts/package.json": "5f0d12f470196be581f0d57bdfb25d1f4e01b4672d63d5ad509ddd7241ca76f2",
44
46
  "packages/contracts/README.md": "44d83a3cc508cec6c5c20dfa1f2c08d5ea03e3afb2853639fdc0a9cc8eb5b9c8",
45
47
  "packages/core/index.d.ts": "c95b24a13f8aeeb128497833cf298790563baca6036ea5d29a9d8daff53199d7",
46
48
  "packages/core/index.js": "5579ceb1a63d3f962f5656c75f82cf60cd834fd270b4aa95e581bf38e4432448",
47
- "packages/core/package.json": "7e12d76c3c0698b15262ab02b445d429dd4643db8b32fae373e4fb77adde1db1",
49
+ "packages/core/package.json": "b272aceffc541023730d4d39b7464de64e08347af9d28ee182135beea4f7e2eb",
48
50
  "packages/core/README.md": "e222c546104a168f1038d95c6c4efb8e5603edd6145ab2cab79cab977fb710e5",
49
51
  "packages/server/bin/agent-terminal-server.js": "043dff4d76e3fd213b666a55e581ed1e72f9e5b8de6cb798fbf42bd39ed26861",
50
52
  "packages/server/bin/claude-workbench-server.js": "b03c730b6d86faf6974afea26c74109d59b1bbc1ecc656c1d6270258708694ac",
51
53
  "packages/server/bin/claude-workbench.js": "98dbd550d4d657056a75a190e7ee0dffd9f760fba58783f3ffb8d389ad7a32de",
52
54
  "packages/server/index.d.ts": "ad39d5091056df5ec94e031a0de47c2c4ff75128ec3edadc3d3a364fca1b912b",
53
- "packages/server/index.js": "7264099d498a67c81a63ec7ea71f7f2de0c2c7e959a7a50eccab5db63f118d6c",
55
+ "packages/server/index.js": "77556a727cbe0e43dedfa87273a069010cad48ae46fb57a0e42a14908ab01b91",
54
56
  "packages/server/lib/agent-discovery.js": "7fa15684329fd287cd0869183808311bf975263255425562f2896bc2c1a69e7c",
55
57
  "packages/server/lib/agent-registry.js": "fbf4f3e96542502a41e7fdf77037d63c3b0444d26708ad9766bec90fbb6b38bb",
56
58
  "packages/server/lib/attachment-store.js": "5c92c45c7ec5a8b0ee9c14edb42eeda5fce93d8e654718dcd75c12e3f0bfa6e7",
@@ -62,9 +64,9 @@
62
64
  "packages/server/lib/conversation-capture.js": "9f3ee29003eaea4778dc1e4b7e23e868f2ba0268bd43db07317acba62f48a094",
63
65
  "packages/server/lib/diagnostics.js": "b45687fcc07f8baeb33ffd08efdc875494d4e3205887514fdfd7e1ce6370753b",
64
66
  "packages/server/lib/drivers/cli-json-runner.js": "d3ad5dc67481d69abb3593ca58e7ca703bf1c438042d634d83642803849d797f",
65
- "packages/server/lib/drivers/codex-driver.js": "d472a6229aab8e3e5a568fa5f6c63de77afcf2b84c7a930101dd94e8b11a640e",
67
+ "packages/server/lib/drivers/codex-driver.js": "01d65b05cf8ba6fedf5fd7f5ce4c634f2ca69461175556286448c667031424b6",
66
68
  "packages/server/lib/drivers/opencode-driver.js": "322043d2d1912204c17b8c46f14d19bf132c096365cdacfdb98cfc0855e88c73",
67
- "packages/server/lib/drivers/zcode-driver.js": "89c99e3f40c8064260d83153116c2c12acfec9485df2e495207ba3a64bb95764",
69
+ "packages/server/lib/drivers/zcode-driver.js": "9b5e6fb461c2df5b2fe530e38da9d782f10fcdbf9d187933d5d74d4e1b4bb763",
68
70
  "packages/server/lib/drivers/zcode-profile-runtime.js": "77c5ac83551cc424375918863d4a31039984492c3a1c987a615a072ac7cb1f14",
69
71
  "packages/server/lib/event-stream.js": "b0f20bac19933a1a428c22938ae2666a1231b74a4330619b885171662133f48b",
70
72
  "packages/server/lib/folder-picker.js": "70f55df999262b90433f2b4251aa69dd70a23b55a84756b5f1d24dbeacfbda31",
@@ -78,7 +80,7 @@
78
80
  "packages/server/lib/system-doctor.js": "2b0e858d69d8fb268eaed950c9883473ee9850684b532bc2db8e14674a974919",
79
81
  "packages/server/lib/ui-preferences-store.js": "e108cb22e7cc1f3438704913d5401e01de2c0e717eb2abd3bdc4727209393819",
80
82
  "packages/server/lib/workbench-service.js": "206e50b8d4d9cbe18c79ffce145b4f37f711d4e18a1222010d723dfcc13f5ec4",
81
- "packages/server/package.json": "08d41bc24839f2b8aeb5cba7594267ec2a7031e682eae48deac66be0fd559c07",
83
+ "packages/server/package.json": "c443d9b9427fc10799fd0f5f4288e9db328ae2d3a898e39fce28c2dacc0d2388",
82
84
  "packages/server/README.md": "a34cfaa4f4e859008b44e2be8e1aafb56e97fe83e6129c968a8562945fa843f3",
83
85
  "packages/ui/ai-profile-settings.d.ts": "0551b6c6eda6d18ea89a976daddf01b2161195f4874fe2d67a57cd9ecc6ce643",
84
86
  "packages/ui/ai-profile-settings.js": "e92bbac67a1199d473e4e44b32d1cc76c5b6c79306d4fef9d9067a1bb6ec42ca",
@@ -86,7 +88,7 @@
86
88
  "packages/ui/claude-terminal.js": "96bd217a4a7e4792ae3fbaaf46249ecee13b0f4e01fc6d25a1b8b22d8f8fe00c",
87
89
  "packages/ui/index.d.ts": "e547ab39640fe7519fb367b78442dc8a2c51b2efd17b66f8b0a003b154b36ead",
88
90
  "packages/ui/index.js": "f4394aabd395938f5c3fe587e430471d4033e83945a9b9c7e0149ad4040982c9",
89
- "packages/ui/package.json": "4dda2e99dd95ca7f79569999c3a5e7eabfd7607deb7be91f04707f856c9d6837",
91
+ "packages/ui/package.json": "9290a2406dbef5448fd6ef20a5ecb20b8a276547d4f656e69055953347cc074b",
90
92
  "packages/ui/README.md": "793c4a9c286fee23f39062a1976ca3afe9d2816dcdb55e156ba940e1ef514f89",
91
93
  "packages/ui/theme.css": "8f03a081272a7db11b196f7a70d0fafb1f8d954778f329f1ba53d110d9d5fd98",
92
94
  "src/assets/claude-prompts.json": "cb111f2e0a45afe8a31c251a504d1ab5375966ed61291660e61fd2efd77f6d9f",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanqianx/project-knowledge",
3
- "version": "5.0.11",
3
+ "version": "5.0.12-rc.1",
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": {
@@ -84,7 +84,7 @@
84
84
  "@sanqianx/vector-hub": "1.0.8-rc.3",
85
85
  "buffer": "^6.0.3",
86
86
  "cheerio": "^1.2.0",
87
- "claude-ai-workbench": "npm:@sanqianx/claude-ai-workbench@0.13.2-rc.8",
87
+ "claude-ai-workbench": "npm:@sanqianx/claude-ai-workbench@0.13.2-rc.9",
88
88
  "cross-spawn": "7.0.6",
89
89
  "dotenv": "^17.4.2",
90
90
  "gpt-tokenizer": "^3.4.0",
@@ -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.11",
5
+ "version": "5.0.12-rc.1",
6
6
  "description": "Read durable project decisions and implementation history from Project Knowledge.",
7
7
  "author": {
8
8
  "name": "SanQianX",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "project-knowledge",
3
- "version": "5.0.11",
3
+ "version": "5.0.12-rc.1",
4
4
  "description": "Read durable project decisions and implementation history from Project Knowledge.",
5
5
  "author": {
6
6
  "name": "SanQianX",
@@ -16,6 +16,7 @@
16
16
  "project-history"
17
17
  ],
18
18
  "skills": "./skills/",
19
+ "hooks": "./hooks/codex.json",
19
20
  "mcpServers": "./.mcp.json",
20
21
  "interface": {
21
22
  "displayName": "Project Knowledge",
@@ -6,7 +6,7 @@
6
6
  "args": [
7
7
  "-y",
8
8
  "--package",
9
- "@sanqianx/project-knowledge@5.0.11",
9
+ "@sanqianx/project-knowledge@5.0.12-rc.1",
10
10
  "project-knowledge-mcp"
11
11
  ]
12
12
  }
@@ -0,0 +1,17 @@
1
+ {
2
+ "hooks": {
3
+ "UserPromptSubmit": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "node \"${PLUGIN_ROOT}/scripts/context-hook.cjs\" --client codex",
9
+ "timeout": 12,
10
+ "additionalContextLimit": 0,
11
+ "statusMessage": "正在读取项目知识与适用记忆"
12
+ }
13
+ ]
14
+ }
15
+ ]
16
+ }
17
+ }
package/ui/product.js CHANGED
@@ -63,6 +63,7 @@
63
63
  state.projectId = id; state.epoch++;
64
64
  referenceEpoch++; $('referenceDialog').close();
65
65
  if ($('logsDialog').open) loadLogs().catch(error => toast(error.message));
66
+ if (!$('systemDrawer').hidden) loadAgentCapture().catch(error => toast(error.message));
66
67
  localStorage.setItem('kb.project', id);
67
68
  $('projectSelect').value = id;
68
69
  Object.keys(frames).forEach(frameContext);
@@ -231,7 +232,9 @@
231
232
  async function loadAgentCapture() {
232
233
  const epoch = ++captureEpoch;
233
234
  try {
234
- const result = await api('/api/integrations/status');
235
+ const projectId = state.projectId;
236
+ const result = await api('/api/integrations/status' + (projectId ? '?projectId=' + encodeURIComponent(projectId) : ''));
237
+ if (projectId !== state.projectId) return;
235
238
  if (epoch !== captureEpoch) return;
236
239
  $('agentCaptureList').innerHTML = result.clients.map(agent => {
237
240
  const capture = agent.developmentCapture;