@sanqianx/project-knowledge 5.0.18 → 5.0.20-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.
Files changed (209) hide show
  1. package/.agents/plugins/marketplace.json +3 -3
  2. package/.claude-plugin/marketplace.json +1 -1
  3. package/CHANGELOG.md +14 -0
  4. package/LICENSE +201 -201
  5. package/NOTICE +5 -5
  6. package/README.md +349 -349
  7. package/_modules/knowledge-engine/README.md +4 -0
  8. package/_modules/knowledge-engine/src/analysis-contract.js +4 -3
  9. package/_modules/knowledge-engine/src/analysis-failures.js +39 -0
  10. package/_modules/knowledge-engine/src/analysis-worker.js +6 -2
  11. package/_modules/knowledge-engine/src/coordinator.js +8 -0
  12. package/_modules/knowledge-engine/src/headless-analyzer.js +2 -1
  13. package/_modules/knowledge-engine/src/memories.js +10 -5
  14. package/_modules/knowledge-engine/src/memory-eligibility.js +74 -0
  15. package/_modules/knowledge-engine/src/retrieval-ranking.js +19 -1
  16. package/_modules/knowledge-engine/src/runtime/commit-conversation-binder.js +1 -0
  17. package/_modules/knowledge-engine/src/runtime/commit-prompt.js +12 -9
  18. package/_modules/knowledge-engine/src/runtime/conversation-store.js +2 -0
  19. package/_modules/knowledge-engine/src/server.js +1 -1
  20. package/_site/_test/agent-capture-mcp-ui-test.js +67 -67
  21. package/_site/_test/ai-profile-test.js +75 -75
  22. package/_site/_test/ai-vendor-presets-test.js +91 -91
  23. package/_site/_test/automatic-context-test.js +70 -70
  24. package/_site/_test/bin-cli-test.js +230 -230
  25. package/_site/_test/bridge-adapter-test.js +95 -95
  26. package/_site/_test/bridge-consumer-service-test.js +310 -310
  27. package/_site/_test/candidate-bin-content-test.js +35 -0
  28. package/_site/_test/candidate-product-assets-test.js +13 -0
  29. package/_site/_test/candidate-release-contract-test.js +14 -14
  30. package/_site/_test/capture-hook-dedup-test.js +158 -158
  31. package/_site/_test/claude-executable-discovery-test.js +28 -28
  32. package/_site/_test/claude-session-lifecycle-sweep-test.js +252 -252
  33. package/_site/_test/cli-instance-management-test.js +62 -62
  34. package/_site/_test/client-capture-restart-e2e-test.js +189 -189
  35. package/_site/_test/codex-conversation-projection-test.js +60 -60
  36. package/_site/_test/codex-marketplace-scoped-path-test.js +186 -186
  37. package/_site/_test/codex-notify-capture-e2e-test.js +101 -101
  38. package/_site/_test/codex-notify-fanout-test.js +134 -134
  39. package/_site/_test/codex-notify-upgrade-test.js +253 -253
  40. package/_site/_test/commit-boundary-freeze-test.js +147 -147
  41. package/_site/_test/commit-conversation-binding-test.js +4 -4
  42. package/_site/_test/configuration-generation-test.js +22 -22
  43. package/_site/_test/context-pack-test.js +200 -200
  44. package/_site/_test/conversation-api-test.js +10 -10
  45. package/_site/_test/conversation-origin-test.js +15 -0
  46. package/_site/_test/cross-repo-isolation-e2e-test.js +187 -187
  47. package/_site/_test/embedding-config-test.js +32 -32
  48. package/_site/_test/fixtures/fake-browser-exit.js +10 -10
  49. package/_site/_test/fixtures/fake-browser-hang.js +10 -10
  50. package/_site/_test/fixtures/integration-busy-worker.cjs +6 -6
  51. package/_site/_test/fixtures/make-git-repos.js +92 -92
  52. package/_site/_test/fixtures/retrieval-gold-queries.json +32 -0
  53. package/_site/_test/fresh-hook-reimport-test.js +53 -53
  54. package/_site/_test/fresh-product-data-test.js +56 -56
  55. package/_site/_test/git-path-compatibility-test.js +80 -80
  56. package/_site/_test/git-validation-test.js +81 -81
  57. package/_site/_test/hook-worktree-test.js +42 -42
  58. package/_site/_test/index-builder-test.js +41 -41
  59. package/_site/_test/index-error-display-contract-test.js +10 -10
  60. package/_site/_test/integration-adapters-test.js +67 -67
  61. package/_site/_test/integration-capture-setup-test.js +118 -118
  62. package/_site/_test/integration-runtime-binding-a2-test.js +566 -566
  63. package/_site/_test/integration-runtime-binding-a3-test.js +227 -227
  64. package/_site/_test/integration-runtime-binding-test.js +473 -473
  65. package/_site/_test/integration-status-scan-test.js +29 -29
  66. package/_site/_test/kb-framework-test.js +57 -57
  67. package/_site/_test/kbpath-follow-test.js +63 -63
  68. package/_site/_test/knowledge-db-test.js +144 -144
  69. package/_site/_test/knowledge-engine-client-test.js +109 -109
  70. package/_site/_test/knowledge-maintenance-test.js +40 -40
  71. package/_site/_test/knowledge-panel-ui-contract-test.js +112 -0
  72. package/_site/_test/knowledge-scopes-test.js +37 -37
  73. package/_site/_test/knowledge-storage-location-test.js +28 -28
  74. package/_site/_test/launcher-readiness-test.js +80 -80
  75. package/_site/_test/legacy-claude-session-alias-test.js +106 -106
  76. package/_site/_test/legacy-routes-removed-test.js +47 -47
  77. package/_site/_test/legacy-workbench-exclusion-test.js +92 -92
  78. package/_site/_test/live-gateway-test.js +48 -48
  79. package/_site/_test/local-marketplace-test.js +16 -16
  80. package/_site/_test/log-redaction-test.js +58 -58
  81. package/_site/_test/markdown-maintenance-test.js +11 -11
  82. package/_site/_test/model-context-windows-test.js +89 -89
  83. package/_site/_test/module-agent-start-proxy-test.js +65 -65
  84. package/_site/_test/module-artifact-integrity-test.js +27 -27
  85. package/_site/_test/module-boundary-test.js +30 -30
  86. package/_site/_test/module-bridge-eventbridge-test.js +202 -202
  87. package/_site/_test/module-dynamic-port-test.js +118 -118
  88. package/_site/_test/module-index-proxy-test.js +33 -0
  89. package/_site/_test/module-model-configuration-test.js +44 -44
  90. package/_site/_test/module-process-lifecycle-test.js +35 -35
  91. package/_site/_test/module-stream-proxy-test.js +34 -34
  92. package/_site/_test/module-terminal-restart-test.js +63 -0
  93. package/_site/_test/module-wiring-unbind-race-test.js +59 -59
  94. package/_site/_test/native-context-integrations-test.js +86 -86
  95. package/_site/_test/orphaned-claim-recovery-test.js +80 -80
  96. package/_site/_test/package-boundary-test.js +43 -42
  97. package/_site/_test/package-startup-test.js +56 -56
  98. package/_site/_test/packaged-ui-smoke-test.js +88 -88
  99. package/_site/_test/pr-consumer-contract-test.js +55 -55
  100. package/_site/_test/product-brand-contract-test.js +25 -25
  101. package/_site/_test/product-diagnostics-ui-test.js +93 -93
  102. package/_site/_test/product-import-ui-test.js +104 -104
  103. package/_site/_test/product-progress-status-test.js +12 -1
  104. package/_site/_test/project-remove-running-guard-test.js +79 -79
  105. package/_site/_test/project-store-test.js +90 -90
  106. package/_site/_test/registry-events-test.js +38 -0
  107. package/_site/_test/release-version-sync-test.js +115 -115
  108. package/_site/_test/requirement-binding-test.js +113 -113
  109. package/_site/_test/runtime-endpoint-test.js +64 -64
  110. package/_site/_test/scanner-test.js +83 -83
  111. package/_site/_test/server-security-test.js +96 -96
  112. package/_site/_test/sessions-stream-test.js +93 -93
  113. package/_site/_test/shared-scope-root-binding-test.js +67 -67
  114. package/_site/_test/simple-import-test.js +98 -98
  115. package/_site/_test/startup-analysis-disabled-test.js +6 -6
  116. package/_site/_test/team-knowledge-store-sparse-checkout-test.js +196 -196
  117. package/_site/_test/team-knowledge-store-test.js +132 -132
  118. package/_site/_test/test-server-cleanup-test.js +35 -35
  119. package/_site/_test/test-server-module-isolation-test.js +37 -37
  120. package/_site/_test/tracking-start-test.js +35 -35
  121. package/_site/_test/workbench-context-receipts-test.js +22 -0
  122. package/_site/_test/workspace-ui-contract-test.js +31 -31
  123. package/_site/_test/zcode-capture-integration-test.js +108 -108
  124. package/_site/lib/ai-vendor-presets.js +83 -83
  125. package/_site/lib/ai-workspace.js +3 -3
  126. package/_site/lib/atomic-file.js +204 -204
  127. package/_site/lib/bridge-adapter.js +260 -260
  128. package/_site/lib/capture-hook-config.js +108 -108
  129. package/_site/lib/claude-cli-runner.js +89 -89
  130. package/_site/lib/cli-instances.js +43 -43
  131. package/_site/lib/codex-context-status.js +17 -17
  132. package/_site/lib/codex-notify-config.js +193 -193
  133. package/_site/lib/commit-conversation-binder.js +13 -12
  134. package/_site/lib/commit-reconciler.js +38 -38
  135. package/_site/lib/context-pack-builder.js +299 -299
  136. package/_site/lib/conversation-exclusions.js +204 -204
  137. package/_site/lib/conversation-query-service.js +17 -17
  138. package/_site/lib/conversation-store.js +2 -0
  139. package/_site/lib/data-dir.js +13 -13
  140. package/_site/lib/embedding-service.js +85 -85
  141. package/_site/lib/engine-connection.js +93 -90
  142. package/_site/lib/engine-knowledge-client.js +72 -72
  143. package/_site/lib/folder-picker-output.js +3 -3
  144. package/_site/lib/git-path.js +14 -14
  145. package/_site/lib/github-team-store.js +3 -3
  146. package/_site/lib/index-builder.js +157 -157
  147. package/_site/lib/integration-status-scan.js +99 -99
  148. package/_site/lib/integration-status-worker.js +14 -14
  149. package/_site/lib/kb-framework.js +15 -15
  150. package/_site/lib/knowledge-db.js +363 -363
  151. package/_site/lib/knowledge-schema.js +121 -121
  152. package/_site/lib/live-gateway.js +78 -76
  153. package/_site/lib/llm-client.js +12 -12
  154. package/_site/lib/model-context-windows.js +82 -82
  155. package/_site/lib/module-bridge.js +874 -865
  156. package/_site/lib/module-port-resolver.js +57 -57
  157. package/_site/lib/post-commit-automation.js +54 -54
  158. package/_site/lib/product-data.js +35 -35
  159. package/_site/lib/project-lifecycle-service.js +14 -14
  160. package/_site/lib/project-registry-store.js +129 -129
  161. package/_site/lib/prompt-registry.js +148 -148
  162. package/_site/lib/registry-events.js +16 -0
  163. package/_site/lib/requirement-adapters.js +10 -10
  164. package/_site/lib/requirement-binder.js +112 -112
  165. package/_site/lib/runtime-control.js +89 -89
  166. package/_site/lib/runtime-endpoint.js +5 -5
  167. package/_site/lib/server-app.js +10 -1
  168. package/_site/lib/zcode-context-integration.js +60 -60
  169. package/_site/scripts/codex-notify-fanout.cjs +95 -95
  170. package/_site/scripts/install-module-candidates.js +69 -69
  171. package/_site/scripts/pack-module-candidates.js +89 -72
  172. package/_site/scripts/sync-product-content.js +28 -28
  173. package/_site/scripts/sync-release-version.js +164 -164
  174. package/_site/scripts/vendor-modules.js +49 -49
  175. package/_site/scripts/verify-module-runtime.js +50 -50
  176. package/_site/scripts/verify-product-runtime.js +36 -36
  177. package/_site/scripts/verify-retrieval-gold.js +83 -0
  178. package/_site/server.js +15 -15
  179. package/_site/start.bat +26 -26
  180. package/_site/stop.bat +11 -11
  181. package/bin/project-knowledge-integrations.js +254 -254
  182. package/bin/project-knowledge-kb.js +67 -67
  183. package/bin/project-knowledge-mcp.js +8 -8
  184. package/bin/project-knowledge.js +170 -170
  185. package/claude-prompts.json +21 -21
  186. package/docs/README.zh-CN.md +205 -205
  187. package/docs/assets/architecture.svg +181 -181
  188. package/docs/assets/logo.svg +49 -49
  189. package/docs/fresh-v5-start.md +42 -42
  190. package/docs/leader-glm-workbench-playbook.md +122 -119
  191. package/docs/project-registry-schema.md +11 -11
  192. package/docs/release-5.0.16-acceptance.md +176 -176
  193. package/docs/release-5.0.17-acceptance.md +117 -117
  194. package/docs/release-5.0.19-acceptance.md +39 -0
  195. package/docs/team-knowledge-mode-a-plan.md +73 -73
  196. package/module-runtime-manifest.json +126 -104
  197. package/package.json +110 -108
  198. package/plugins/project-knowledge/.claude-plugin/plugin.json +1 -1
  199. package/plugins/project-knowledge/.codex-plugin/plugin.json +1 -1
  200. package/plugins/project-knowledge/.mcp.json +1 -1
  201. package/plugins/project-knowledge/opencode/project-knowledge.md +3 -3
  202. package/plugins/project-knowledge/skills/project-knowledge/SKILL.md +28 -28
  203. package/templates/change.md +30 -30
  204. package/templates/project-readme.md +18 -18
  205. package/ui/index.html +1 -1
  206. package/ui/knowledge-panel.html +118 -0
  207. package/ui/product.css +8 -0
  208. package/ui/product.js +175 -8
  209. package/vendor-manifest.json +55 -53
@@ -1,56 +1,56 @@
1
- const assert = require('assert');
2
- const fs = require('fs');
3
- const os = require('os');
4
- const path = require('path');
5
- const { spawn } = require('child_process');
6
-
7
- const ROOT = path.resolve(__dirname, '..', '..');
8
- const BIN = path.join(ROOT, 'bin', 'project-knowledge.js');
9
- const PORT = 7825;
10
- const BASE = `http://127.0.0.1:${PORT}`;
11
- const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pkb-package-v2-'));
12
-
13
- async function waitForServer() {
14
- for (let attempt = 0; attempt < 150; attempt += 1) {
15
- try { if ((await fetch(`${BASE}/api/health`)).ok) return; } catch {}
16
- await new Promise(resolve => setTimeout(resolve, 100));
17
- }
18
- throw new Error('packaged CLI server did not start');
19
- }
20
-
21
- (async () => {
22
- const child = spawn(process.execPath, [BIN, '--fg', '--port', String(PORT), '--host', '127.0.0.1', '--no-open'], {
23
- cwd: ROOT,
24
- env: { ...process.env, KB_DATA_DIR: dataDir, KB_SKIP_MIGRATION: '1' },
25
- stdio: ['ignore', 'pipe', 'pipe'],
26
- windowsHide: true,
27
- });
28
- let output = '';
29
- child.stdout.on('data', chunk => { output += chunk; });
30
- child.stderr.on('data', chunk => { output += chunk; });
31
- try {
32
- await waitForServer();
33
- const stateResponse = await fetch(`${BASE}/api/state`);
34
- const state = await stateResponse.json();
35
- assert.strictEqual(state.schema, 'server-state/v2');
36
- assert.deepStrictEqual(state.projects, []);
37
-
38
- const registry = JSON.parse(fs.readFileSync(path.join(dataDir, 'projects.json'), 'utf8'));
39
- assert.strictEqual(registry.schema, 'project-registry/v2');
40
- assert.deepStrictEqual(registry.projectOrder, []);
41
- const endpoint = JSON.parse(fs.readFileSync(path.join(dataDir, 'runtime-endpoint.json'), 'utf8'));
42
- assert.strictEqual(endpoint.schema, 'project-knowledge/runtime-endpoint/v1');
43
- assert.strictEqual(endpoint.port, PORT);
44
-
45
- const removed = await fetch(`${BASE}/api/projects/example/hook-install`, { method: 'POST' });
46
- assert.strictEqual(removed.status, 404);
47
- console.log('package-startup-test PASS');
48
- } catch (error) {
49
- console.error(output);
50
- throw error;
51
- } finally {
52
- child.kill('SIGTERM');
53
- await new Promise(resolve => child.once('exit', resolve));
54
- fs.rmSync(dataDir, { recursive: true, force: true });
55
- }
56
- })().catch(error => { console.error(error); process.exitCode = 1; });
1
+ const assert = require('assert');
2
+ const fs = require('fs');
3
+ const os = require('os');
4
+ const path = require('path');
5
+ const { spawn } = require('child_process');
6
+
7
+ const ROOT = path.resolve(__dirname, '..', '..');
8
+ const BIN = path.join(ROOT, 'bin', 'project-knowledge.js');
9
+ const PORT = 7825;
10
+ const BASE = `http://127.0.0.1:${PORT}`;
11
+ const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pkb-package-v2-'));
12
+
13
+ async function waitForServer() {
14
+ for (let attempt = 0; attempt < 150; attempt += 1) {
15
+ try { if ((await fetch(`${BASE}/api/health`)).ok) return; } catch {}
16
+ await new Promise(resolve => setTimeout(resolve, 100));
17
+ }
18
+ throw new Error('packaged CLI server did not start');
19
+ }
20
+
21
+ (async () => {
22
+ const child = spawn(process.execPath, [BIN, '--fg', '--port', String(PORT), '--host', '127.0.0.1', '--no-open'], {
23
+ cwd: ROOT,
24
+ env: { ...process.env, KB_DATA_DIR: dataDir, KB_SKIP_MIGRATION: '1' },
25
+ stdio: ['ignore', 'pipe', 'pipe'],
26
+ windowsHide: true,
27
+ });
28
+ let output = '';
29
+ child.stdout.on('data', chunk => { output += chunk; });
30
+ child.stderr.on('data', chunk => { output += chunk; });
31
+ try {
32
+ await waitForServer();
33
+ const stateResponse = await fetch(`${BASE}/api/state`);
34
+ const state = await stateResponse.json();
35
+ assert.strictEqual(state.schema, 'server-state/v2');
36
+ assert.deepStrictEqual(state.projects, []);
37
+
38
+ const registry = JSON.parse(fs.readFileSync(path.join(dataDir, 'projects.json'), 'utf8'));
39
+ assert.strictEqual(registry.schema, 'project-registry/v2');
40
+ assert.deepStrictEqual(registry.projectOrder, []);
41
+ const endpoint = JSON.parse(fs.readFileSync(path.join(dataDir, 'runtime-endpoint.json'), 'utf8'));
42
+ assert.strictEqual(endpoint.schema, 'project-knowledge/runtime-endpoint/v1');
43
+ assert.strictEqual(endpoint.port, PORT);
44
+
45
+ const removed = await fetch(`${BASE}/api/projects/example/hook-install`, { method: 'POST' });
46
+ assert.strictEqual(removed.status, 404);
47
+ console.log('package-startup-test PASS');
48
+ } catch (error) {
49
+ console.error(output);
50
+ throw error;
51
+ } finally {
52
+ child.kill('SIGTERM');
53
+ await new Promise(resolve => child.once('exit', resolve));
54
+ fs.rmSync(dataDir, { recursive: true, force: true });
55
+ }
56
+ })().catch(error => { console.error(error); process.exitCode = 1; });
@@ -1,88 +1,88 @@
1
- const assert = require('assert');
2
- const fs = require('fs');
3
- const os = require('os');
4
- const path = require('path');
5
- const { spawnSync } = require('child_process');
6
- const { spawnServer } = require('./helpers/spawn-server');
7
- const { findChrome } = require('./helpers/find-chrome');
8
- const { launchCdpBrowser, waitFor } = require('./helpers/cdp-browser');
9
-
10
- const ROOT = path.resolve(__dirname, '..', '..');
11
- const PORT = 8250 + (process.pid % 500);
12
- const BASE = `http://127.0.0.1:${PORT}`;
13
- const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), `pk-packaged-ui-${process.pid}-`));
14
- const packDir = path.join(tempRoot, 'pack');
15
- const installDir = path.join(tempRoot, 'install');
16
- const profileDir = path.join(tempRoot, 'chrome-profile');
17
- fs.mkdirSync(packDir, { recursive: true });
18
- fs.mkdirSync(installDir, { recursive: true });
19
-
20
- function npmCliPath() {
21
- return process.env.npm_execpath
22
- || path.join(path.dirname(process.execPath), 'node_modules', 'npm', 'bin', 'npm-cli.js');
23
- }
24
-
25
- function buildAndInstallPackage() {
26
- const packed = spawnSync(process.execPath, [npmCliPath(), 'pack', '--json', '--pack-destination', packDir], {
27
- cwd: ROOT,
28
- encoding: 'utf8',
29
- windowsHide: true,
30
- });
31
- assert.strictEqual(packed.status, 0, packed.stderr || packed.stdout);
32
- const report = JSON.parse(packed.stdout);
33
- const tarball = path.join(packDir, report[0].filename);
34
- assert(fs.existsSync(tarball), `npm pack did not create ${tarball}`);
35
-
36
- const installed = spawnSync(process.execPath, [npmCliPath(), 'install', '--ignore-scripts', '--no-audit', '--no-fund', '--prefix', installDir, tarball], {
37
- encoding: 'utf8',
38
- windowsHide: true,
39
- });
40
- assert.strictEqual(installed.status, 0, installed.stderr || installed.stdout);
41
- // Resolve the install path from the package's actual name (moved to
42
- // @sanqianx/project-knowledge in 4.7.0; prior releases used the unscoped
43
- // 'project-knowledge' which would live at node_modules/project-knowledge).
44
- const pkg = require(path.join(ROOT, 'package.json'));
45
- const installPath = pkg.name.startsWith('@')
46
- ? path.join(installDir, 'node_modules', pkg.name.split('/')[0], pkg.name.split('/')[1])
47
- : path.join(installDir, 'node_modules', pkg.name);
48
- return installPath;
49
- }
50
-
51
- (async () => {
52
- let server;
53
- let browser;
54
- try {
55
- const packagedRoot = buildAndInstallPackage();
56
- for (const file of ['index.html', 'product.js', 'product.css']) assert(fs.existsSync(path.join(packagedRoot, 'ui', file)), `packed UI must include ${file}`);
57
- for (const file of ['app.js', 'app.css', 'i18n.js']) assert(!fs.existsSync(path.join(packagedRoot, 'ui', file)), `obsolete shell resource must not be shipped: ${file}`);
58
-
59
- server = spawnServer({ root: packagedRoot, port: PORT, dataDir: path.join(tempRoot, 'data'), tag: 'packaged-ui', analysisOwner: 'engine', stdio: 'ignore' });
60
- await waitFor(async () => {
61
- try { return (await fetch(`${BASE}/api/health`)).ok ? { ok: true } : null; }
62
- catch { return null; }
63
- }, 'packaged server', 20000);
64
-
65
- const productResponse = await fetch(`${BASE}/product.js`);
66
- assert.strictEqual(productResponse.status, 200, 'packaged server must serve /product.js');
67
- assert.match(String(productResponse.headers.get('content-type') || ''), /javascript/i);
68
-
69
- browser = await launchCdpBrowser({ chrome: findChrome(), profileDir, url: `${BASE}/`, width: 1920, height: 1080 });
70
- await browser.setViewport(1920, 1080);
71
- await waitFor(async () => {
72
- const ready = await browser.evaluate("document.getElementById('homeContent').textContent.includes('第一个项目')");
73
- return ready ? { ok: true } : null;
74
- }, 'packaged UI bootstrap', 20000);
75
-
76
- assert.strictEqual(await browser.evaluate("document.getElementById('projectSelect').disabled"), true);
77
- assert.strictEqual(await browser.evaluate("document.querySelectorAll('nav [data-view]').length"), 6);
78
- assert.strictEqual(browser.exceptions.length, 0, `packaged UI runtime exceptions: ${JSON.stringify(browser.exceptions)}`);
79
- console.log('packaged-ui-smoke-test PASS');
80
- } finally {
81
- if (browser) await browser.close();
82
- if (server) await server.cleanup();
83
- try { fs.rmSync(tempRoot, { recursive: true, force: true }); } catch {}
84
- }
85
- })().catch(error => {
86
- console.error(error.stack || error.message);
87
- process.exitCode = 1;
88
- });
1
+ const assert = require('assert');
2
+ const fs = require('fs');
3
+ const os = require('os');
4
+ const path = require('path');
5
+ const { spawnSync } = require('child_process');
6
+ const { spawnServer } = require('./helpers/spawn-server');
7
+ const { findChrome } = require('./helpers/find-chrome');
8
+ const { launchCdpBrowser, waitFor } = require('./helpers/cdp-browser');
9
+
10
+ const ROOT = path.resolve(__dirname, '..', '..');
11
+ const PORT = 8250 + (process.pid % 500);
12
+ const BASE = `http://127.0.0.1:${PORT}`;
13
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), `pk-packaged-ui-${process.pid}-`));
14
+ const packDir = path.join(tempRoot, 'pack');
15
+ const installDir = path.join(tempRoot, 'install');
16
+ const profileDir = path.join(tempRoot, 'chrome-profile');
17
+ fs.mkdirSync(packDir, { recursive: true });
18
+ fs.mkdirSync(installDir, { recursive: true });
19
+
20
+ function npmCliPath() {
21
+ return process.env.npm_execpath
22
+ || path.join(path.dirname(process.execPath), 'node_modules', 'npm', 'bin', 'npm-cli.js');
23
+ }
24
+
25
+ function buildAndInstallPackage() {
26
+ const packed = spawnSync(process.execPath, [npmCliPath(), 'pack', '--json', '--pack-destination', packDir], {
27
+ cwd: ROOT,
28
+ encoding: 'utf8',
29
+ windowsHide: true,
30
+ });
31
+ assert.strictEqual(packed.status, 0, packed.stderr || packed.stdout);
32
+ const report = JSON.parse(packed.stdout);
33
+ const tarball = path.join(packDir, report[0].filename);
34
+ assert(fs.existsSync(tarball), `npm pack did not create ${tarball}`);
35
+
36
+ const installed = spawnSync(process.execPath, [npmCliPath(), 'install', '--ignore-scripts', '--no-audit', '--no-fund', '--prefix', installDir, tarball], {
37
+ encoding: 'utf8',
38
+ windowsHide: true,
39
+ });
40
+ assert.strictEqual(installed.status, 0, installed.stderr || installed.stdout);
41
+ // Resolve the install path from the package's actual name (moved to
42
+ // @sanqianx/project-knowledge in 4.7.0; prior releases used the unscoped
43
+ // 'project-knowledge' which would live at node_modules/project-knowledge).
44
+ const pkg = require(path.join(ROOT, 'package.json'));
45
+ const installPath = pkg.name.startsWith('@')
46
+ ? path.join(installDir, 'node_modules', pkg.name.split('/')[0], pkg.name.split('/')[1])
47
+ : path.join(installDir, 'node_modules', pkg.name);
48
+ return installPath;
49
+ }
50
+
51
+ (async () => {
52
+ let server;
53
+ let browser;
54
+ try {
55
+ const packagedRoot = buildAndInstallPackage();
56
+ for (const file of ['index.html', 'knowledge-panel.html', 'product.js', 'product.css']) assert(fs.existsSync(path.join(packagedRoot, 'ui', file)), `packed UI must include ${file}`);
57
+ for (const file of ['app.js', 'app.css', 'i18n.js']) assert(!fs.existsSync(path.join(packagedRoot, 'ui', file)), `obsolete shell resource must not be shipped: ${file}`);
58
+
59
+ server = spawnServer({ root: packagedRoot, port: PORT, dataDir: path.join(tempRoot, 'data'), tag: 'packaged-ui', analysisOwner: 'engine', stdio: 'ignore' });
60
+ await waitFor(async () => {
61
+ try { return (await fetch(`${BASE}/api/health`)).ok ? { ok: true } : null; }
62
+ catch { return null; }
63
+ }, 'packaged server', 20000);
64
+
65
+ const productResponse = await fetch(`${BASE}/product.js`);
66
+ assert.strictEqual(productResponse.status, 200, 'packaged server must serve /product.js');
67
+ assert.match(String(productResponse.headers.get('content-type') || ''), /javascript/i);
68
+
69
+ browser = await launchCdpBrowser({ chrome: findChrome(), profileDir, url: `${BASE}/`, width: 1920, height: 1080 });
70
+ await browser.setViewport(1920, 1080);
71
+ await waitFor(async () => {
72
+ const ready = await browser.evaluate("document.getElementById('homeContent').textContent.includes('第一个项目')");
73
+ return ready ? { ok: true } : null;
74
+ }, 'packaged UI bootstrap', 20000);
75
+
76
+ assert.strictEqual(await browser.evaluate("document.getElementById('projectSelect').disabled"), true);
77
+ assert.strictEqual(await browser.evaluate("document.querySelectorAll('nav [data-view]').length"), 6);
78
+ assert.strictEqual(browser.exceptions.length, 0, `packaged UI runtime exceptions: ${JSON.stringify(browser.exceptions)}`);
79
+ console.log('packaged-ui-smoke-test PASS');
80
+ } finally {
81
+ if (browser) await browser.close();
82
+ if (server) await server.cleanup();
83
+ try { fs.rmSync(tempRoot, { recursive: true, force: true }); } catch {}
84
+ }
85
+ })().catch(error => {
86
+ console.error(error.stack || error.message);
87
+ process.exitCode = 1;
88
+ });
@@ -1,55 +1,55 @@
1
- const assert = require('assert');
2
- const fs = require('fs');
3
- const os = require('os');
4
- const path = require('path');
5
- const { StorageLayout } = require('../lib/storage-layout');
6
- const { SettingsStore } = require('../lib/settings-store');
7
- const { ProjectRegistryStore } = require('../lib/project-registry-store');
8
- const { ProjectStore } = require('../lib/project-store');
9
- const { KnowledgeToolRuntime } = require('../lib/knowledge-tool-runtime');
10
- const { validateKb, buildPrContextPack } = require('../lib/kb-validator');
11
- const { regenerateIndexes } = require('../lib/index-builder');
12
-
13
- const temp = fs.mkdtempSync(path.join(os.tmpdir(), 'pkb-pr-v2-'));
14
- const knowledgePath = path.join(temp, 'knowledge');
15
-
16
- function writeKnowledge() {
17
- fs.mkdirSync(path.join(knowledgePath, 'modules'), { recursive: true });
18
- fs.mkdirSync(path.join(knowledgePath, 'changes'), { recursive: true });
19
- fs.writeFileSync(path.join(knowledgePath, 'README.md'), '# Test KB\n');
20
- fs.writeFileSync(path.join(knowledgePath, 'GOAL.md'), '# Goal\n');
21
- fs.writeFileSync(path.join(knowledgePath, 'ARCHITECTURE.md'), '# Architecture\n');
22
- fs.writeFileSync(path.join(knowledgePath, 'modules', 'api.md'), '---\ntags: [api]\nsourcePaths: [src/api.js]\n---\n# API\n');
23
- fs.writeFileSync(path.join(knowledgePath, 'changes', 'api-change.md'), '---\ntags: [api]\naffectedModules: [api]\ndevelopmentIntent: Add API memory.\n---\n# API change\n\n## Development Intent\nAdd API memory.\n\n## Implementation Result\nDone.\n\n## Evidence\n- src/api.js\n');
24
- regenerateIndexes(knowledgePath);
25
- }
26
-
27
- (async () => {
28
- try {
29
- writeKnowledge();
30
- const validation = validateKb(knowledgePath);
31
- assert(validation.ok, JSON.stringify(validation.errors));
32
- const pack = buildPrContextPack(knowledgePath);
33
- assert(pack.ok);
34
- assert.strictEqual(pack.pack.schema, 'pr-context-pack/v1');
35
- assert(pack.pack.trustedKnowledge.some(item => item.path === 'modules/api.md'));
36
- assert(!JSON.stringify(pack.pack).includes('_ai/'));
37
-
38
- const layout = new StorageLayout({ dataDir: path.join(temp, 'data') });
39
- const settingsStore = new SettingsStore({ layout });
40
- const registryStore = new ProjectRegistryStore({ layout });
41
- const projectStore = new ProjectStore({ layout });
42
- await settingsStore.initialize({ knowledge: { rootPath: path.join(temp, 'future') } });
43
- await registryStore.initialize();
44
- await projectStore.create('project-consumer', { displayName: 'Consumer', storageName: 'consumer', repoPath: temp, knowledgePath }, { index: { dirty: true, generation: 1 } });
45
- await registryStore.add('project-consumer', { displayNameSnapshot: 'Consumer' });
46
- const runtime = new KnowledgeToolRuntime({ layout, settingsStore, registryStore, projectStore, cwd: temp });
47
- const entry = await runtime.get({ projectId: 'project-consumer', entry: 'modules/api.md' });
48
- assert.match(entry.chunks[0].chunk_text, /# API/);
49
- await assert.rejects(runtime.get({ projectId: 'project-consumer', entry: '../settings.json' }), error => error.code === 'PATH_OUTSIDE_ROOT');
50
- await runtime.close();
51
- console.log('pr-consumer-contract-test PASS');
52
- } finally {
53
- fs.rmSync(temp, { recursive: true, force: true });
54
- }
55
- })().catch(error => { console.error(error); process.exitCode = 1; });
1
+ const assert = require('assert');
2
+ const fs = require('fs');
3
+ const os = require('os');
4
+ const path = require('path');
5
+ const { StorageLayout } = require('../lib/storage-layout');
6
+ const { SettingsStore } = require('../lib/settings-store');
7
+ const { ProjectRegistryStore } = require('../lib/project-registry-store');
8
+ const { ProjectStore } = require('../lib/project-store');
9
+ const { KnowledgeToolRuntime } = require('../lib/knowledge-tool-runtime');
10
+ const { validateKb, buildPrContextPack } = require('../lib/kb-validator');
11
+ const { regenerateIndexes } = require('../lib/index-builder');
12
+
13
+ const temp = fs.mkdtempSync(path.join(os.tmpdir(), 'pkb-pr-v2-'));
14
+ const knowledgePath = path.join(temp, 'knowledge');
15
+
16
+ function writeKnowledge() {
17
+ fs.mkdirSync(path.join(knowledgePath, 'modules'), { recursive: true });
18
+ fs.mkdirSync(path.join(knowledgePath, 'changes'), { recursive: true });
19
+ fs.writeFileSync(path.join(knowledgePath, 'README.md'), '# Test KB\n');
20
+ fs.writeFileSync(path.join(knowledgePath, 'GOAL.md'), '# Goal\n');
21
+ fs.writeFileSync(path.join(knowledgePath, 'ARCHITECTURE.md'), '# Architecture\n');
22
+ fs.writeFileSync(path.join(knowledgePath, 'modules', 'api.md'), '---\ntags: [api]\nsourcePaths: [src/api.js]\n---\n# API\n');
23
+ fs.writeFileSync(path.join(knowledgePath, 'changes', 'api-change.md'), '---\ntags: [api]\naffectedModules: [api]\ndevelopmentIntent: Add API memory.\n---\n# API change\n\n## Development Intent\nAdd API memory.\n\n## Implementation Result\nDone.\n\n## Evidence\n- src/api.js\n');
24
+ regenerateIndexes(knowledgePath);
25
+ }
26
+
27
+ (async () => {
28
+ try {
29
+ writeKnowledge();
30
+ const validation = validateKb(knowledgePath);
31
+ assert(validation.ok, JSON.stringify(validation.errors));
32
+ const pack = buildPrContextPack(knowledgePath);
33
+ assert(pack.ok);
34
+ assert.strictEqual(pack.pack.schema, 'pr-context-pack/v1');
35
+ assert(pack.pack.trustedKnowledge.some(item => item.path === 'modules/api.md'));
36
+ assert(!JSON.stringify(pack.pack).includes('_ai/'));
37
+
38
+ const layout = new StorageLayout({ dataDir: path.join(temp, 'data') });
39
+ const settingsStore = new SettingsStore({ layout });
40
+ const registryStore = new ProjectRegistryStore({ layout });
41
+ const projectStore = new ProjectStore({ layout });
42
+ await settingsStore.initialize({ knowledge: { rootPath: path.join(temp, 'future') } });
43
+ await registryStore.initialize();
44
+ await projectStore.create('project-consumer', { displayName: 'Consumer', storageName: 'consumer', repoPath: temp, knowledgePath }, { index: { dirty: true, generation: 1 } });
45
+ await registryStore.add('project-consumer', { displayNameSnapshot: 'Consumer' });
46
+ const runtime = new KnowledgeToolRuntime({ layout, settingsStore, registryStore, projectStore, cwd: temp });
47
+ const entry = await runtime.get({ projectId: 'project-consumer', entry: 'modules/api.md' });
48
+ assert.match(entry.chunks[0].chunk_text, /# API/);
49
+ await assert.rejects(runtime.get({ projectId: 'project-consumer', entry: '../settings.json' }), error => error.code === 'PATH_OUTSIDE_ROOT');
50
+ await runtime.close();
51
+ console.log('pr-consumer-contract-test PASS');
52
+ } finally {
53
+ fs.rmSync(temp, { recursive: true, force: true });
54
+ }
55
+ })().catch(error => { console.error(error); process.exitCode = 1; });
@@ -1,25 +1,25 @@
1
- 'use strict';
2
-
3
- const assert = require('node:assert/strict');
4
- const fs = require('node:fs');
5
- const path = require('node:path');
6
-
7
- const ui = path.resolve(__dirname, '../../ui');
8
- const html = fs.readFileSync(path.join(ui, 'index.html'), 'utf8');
9
- const css = fs.readFileSync(path.join(ui, 'product.css'), 'utf8');
10
- const mark = fs.readFileSync(path.join(ui, 'favicon.svg'), 'utf8');
11
- const favicon = html.match(/<link\b(?=[^>]*\brel="icon")[^>]*\bhref="([^"]+)"/);
12
- const sidebar = html.match(/<img\b(?=[^>]*\bclass="brand-mark")[^>]*\bsrc="([^"]+)"/);
13
-
14
- assert(favicon, 'Product needs an SVG favicon.');
15
- assert(sidebar, 'Sidebar must use the product mark, not a second drawn icon.');
16
- assert.equal(sidebar[1], favicon[1], 'Favicon and sidebar must share the exact asset URL.');
17
- assert.equal(favicon[1].split('?')[0], '/favicon.svg');
18
- assert.match(favicon[1], /\?v=/, 'Brand changes need a new browser cache identity.');
19
- assert.match(mark, /viewBox="0 0 64 64"/);
20
- assert.match(mark, /fill="#516f89"/);
21
- assert.equal((mark.match(/<path\b/g) || []).length, 1, 'Keep one geometric silhouette.');
22
- assert.doesNotMatch(mark, /<(?:script|image|text|filter|linearGradient|radialGradient)\b/i);
23
- assert.doesNotMatch(html, /<span class="brand-mark">知<\/span>/);
24
- assert.match(css, /\.brand-mark\{[^}]*width:32px;[^}]*height:32px;[^}]*flex:none/);
25
- console.log('PASS product brand: shared SVG, cache identity, fixed-size layout, no decorative dependencies');
1
+ 'use strict';
2
+
3
+ const assert = require('node:assert/strict');
4
+ const fs = require('node:fs');
5
+ const path = require('node:path');
6
+
7
+ const ui = path.resolve(__dirname, '../../ui');
8
+ const html = fs.readFileSync(path.join(ui, 'index.html'), 'utf8');
9
+ const css = fs.readFileSync(path.join(ui, 'product.css'), 'utf8');
10
+ const mark = fs.readFileSync(path.join(ui, 'favicon.svg'), 'utf8');
11
+ const favicon = html.match(/<link\b(?=[^>]*\brel="icon")[^>]*\bhref="([^"]+)"/);
12
+ const sidebar = html.match(/<img\b(?=[^>]*\bclass="brand-mark")[^>]*\bsrc="([^"]+)"/);
13
+
14
+ assert(favicon, 'Product needs an SVG favicon.');
15
+ assert(sidebar, 'Sidebar must use the product mark, not a second drawn icon.');
16
+ assert.equal(sidebar[1], favicon[1], 'Favicon and sidebar must share the exact asset URL.');
17
+ assert.equal(favicon[1].split('?')[0], '/favicon.svg');
18
+ assert.match(favicon[1], /\?v=/, 'Brand changes need a new browser cache identity.');
19
+ assert.match(mark, /viewBox="0 0 64 64"/);
20
+ assert.match(mark, /fill="#516f89"/);
21
+ assert.equal((mark.match(/<path\b/g) || []).length, 1, 'Keep one geometric silhouette.');
22
+ assert.doesNotMatch(mark, /<(?:script|image|text|filter|linearGradient|radialGradient)\b/i);
23
+ assert.doesNotMatch(html, /<span class="brand-mark">知<\/span>/);
24
+ assert.match(css, /\.brand-mark\{[^}]*width:32px;[^}]*height:32px;[^}]*flex:none/);
25
+ console.log('PASS product brand: shared SVG, cache identity, fixed-size layout, no decorative dependencies');