@sanqianx/project-knowledge 5.0.19 → 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 (200) hide show
  1. package/.agents/plugins/marketplace.json +3 -3
  2. package/.claude-plugin/marketplace.json +1 -1
  3. package/CHANGELOG.md +5 -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-failures.js +39 -0
  9. package/_modules/knowledge-engine/src/analysis-worker.js +6 -2
  10. package/_modules/knowledge-engine/src/coordinator.js +7 -0
  11. package/_modules/knowledge-engine/src/headless-analyzer.js +2 -1
  12. package/_modules/knowledge-engine/src/server.js +1 -1
  13. package/_site/_test/agent-capture-mcp-ui-test.js +67 -67
  14. package/_site/_test/ai-profile-test.js +75 -75
  15. package/_site/_test/ai-vendor-presets-test.js +91 -91
  16. package/_site/_test/automatic-context-test.js +70 -70
  17. package/_site/_test/bin-cli-test.js +230 -230
  18. package/_site/_test/bridge-adapter-test.js +95 -95
  19. package/_site/_test/bridge-consumer-service-test.js +310 -310
  20. package/_site/_test/candidate-bin-content-test.js +35 -35
  21. package/_site/_test/candidate-product-assets-test.js +13 -13
  22. package/_site/_test/candidate-release-contract-test.js +14 -14
  23. package/_site/_test/capture-hook-dedup-test.js +158 -158
  24. package/_site/_test/claude-executable-discovery-test.js +28 -28
  25. package/_site/_test/claude-session-lifecycle-sweep-test.js +252 -252
  26. package/_site/_test/cli-instance-management-test.js +62 -62
  27. package/_site/_test/client-capture-restart-e2e-test.js +189 -189
  28. package/_site/_test/codex-conversation-projection-test.js +60 -60
  29. package/_site/_test/codex-marketplace-scoped-path-test.js +186 -186
  30. package/_site/_test/codex-notify-capture-e2e-test.js +101 -101
  31. package/_site/_test/codex-notify-fanout-test.js +134 -134
  32. package/_site/_test/codex-notify-upgrade-test.js +253 -253
  33. package/_site/_test/commit-boundary-freeze-test.js +147 -147
  34. package/_site/_test/commit-conversation-binding-test.js +4 -4
  35. package/_site/_test/configuration-generation-test.js +22 -22
  36. package/_site/_test/context-pack-test.js +200 -200
  37. package/_site/_test/conversation-api-test.js +10 -10
  38. package/_site/_test/conversation-origin-test.js +15 -15
  39. package/_site/_test/cross-repo-isolation-e2e-test.js +187 -187
  40. package/_site/_test/embedding-config-test.js +32 -32
  41. package/_site/_test/fixtures/fake-browser-exit.js +10 -10
  42. package/_site/_test/fixtures/fake-browser-hang.js +10 -10
  43. package/_site/_test/fixtures/integration-busy-worker.cjs +6 -6
  44. package/_site/_test/fixtures/make-git-repos.js +92 -92
  45. package/_site/_test/fixtures/retrieval-gold-queries.json +32 -32
  46. package/_site/_test/fresh-hook-reimport-test.js +53 -53
  47. package/_site/_test/fresh-product-data-test.js +56 -56
  48. package/_site/_test/git-path-compatibility-test.js +80 -80
  49. package/_site/_test/git-validation-test.js +81 -81
  50. package/_site/_test/hook-worktree-test.js +42 -42
  51. package/_site/_test/index-builder-test.js +41 -41
  52. package/_site/_test/index-error-display-contract-test.js +10 -10
  53. package/_site/_test/integration-adapters-test.js +67 -67
  54. package/_site/_test/integration-capture-setup-test.js +118 -118
  55. package/_site/_test/integration-runtime-binding-a2-test.js +566 -566
  56. package/_site/_test/integration-runtime-binding-a3-test.js +227 -227
  57. package/_site/_test/integration-runtime-binding-test.js +473 -473
  58. package/_site/_test/integration-status-scan-test.js +29 -29
  59. package/_site/_test/kb-framework-test.js +57 -57
  60. package/_site/_test/kbpath-follow-test.js +63 -63
  61. package/_site/_test/knowledge-db-test.js +144 -144
  62. package/_site/_test/knowledge-engine-client-test.js +109 -109
  63. package/_site/_test/knowledge-maintenance-test.js +40 -40
  64. package/_site/_test/knowledge-panel-ui-contract-test.js +112 -0
  65. package/_site/_test/knowledge-scopes-test.js +37 -37
  66. package/_site/_test/knowledge-storage-location-test.js +28 -28
  67. package/_site/_test/launcher-readiness-test.js +80 -80
  68. package/_site/_test/legacy-claude-session-alias-test.js +106 -106
  69. package/_site/_test/legacy-routes-removed-test.js +47 -47
  70. package/_site/_test/legacy-workbench-exclusion-test.js +92 -92
  71. package/_site/_test/live-gateway-test.js +48 -48
  72. package/_site/_test/local-marketplace-test.js +16 -16
  73. package/_site/_test/log-redaction-test.js +58 -58
  74. package/_site/_test/markdown-maintenance-test.js +11 -11
  75. package/_site/_test/model-context-windows-test.js +89 -89
  76. package/_site/_test/module-agent-start-proxy-test.js +65 -65
  77. package/_site/_test/module-artifact-integrity-test.js +27 -27
  78. package/_site/_test/module-boundary-test.js +30 -30
  79. package/_site/_test/module-bridge-eventbridge-test.js +202 -202
  80. package/_site/_test/module-dynamic-port-test.js +118 -118
  81. package/_site/_test/module-index-proxy-test.js +33 -33
  82. package/_site/_test/module-model-configuration-test.js +44 -44
  83. package/_site/_test/module-process-lifecycle-test.js +35 -35
  84. package/_site/_test/module-stream-proxy-test.js +34 -34
  85. package/_site/_test/module-terminal-restart-test.js +63 -63
  86. package/_site/_test/module-wiring-unbind-race-test.js +59 -59
  87. package/_site/_test/native-context-integrations-test.js +86 -86
  88. package/_site/_test/orphaned-claim-recovery-test.js +80 -80
  89. package/_site/_test/package-boundary-test.js +43 -42
  90. package/_site/_test/package-startup-test.js +56 -56
  91. package/_site/_test/packaged-ui-smoke-test.js +88 -88
  92. package/_site/_test/pr-consumer-contract-test.js +55 -55
  93. package/_site/_test/product-brand-contract-test.js +25 -25
  94. package/_site/_test/product-diagnostics-ui-test.js +93 -93
  95. package/_site/_test/product-import-ui-test.js +104 -104
  96. package/_site/_test/product-progress-status-test.js +12 -1
  97. package/_site/_test/project-remove-running-guard-test.js +79 -79
  98. package/_site/_test/project-store-test.js +90 -90
  99. package/_site/_test/registry-events-test.js +38 -38
  100. package/_site/_test/release-version-sync-test.js +115 -115
  101. package/_site/_test/requirement-binding-test.js +113 -113
  102. package/_site/_test/runtime-endpoint-test.js +64 -64
  103. package/_site/_test/scanner-test.js +83 -83
  104. package/_site/_test/server-security-test.js +96 -96
  105. package/_site/_test/sessions-stream-test.js +93 -93
  106. package/_site/_test/shared-scope-root-binding-test.js +67 -67
  107. package/_site/_test/simple-import-test.js +98 -98
  108. package/_site/_test/startup-analysis-disabled-test.js +6 -6
  109. package/_site/_test/team-knowledge-store-sparse-checkout-test.js +196 -196
  110. package/_site/_test/team-knowledge-store-test.js +132 -132
  111. package/_site/_test/test-server-cleanup-test.js +35 -35
  112. package/_site/_test/test-server-module-isolation-test.js +37 -37
  113. package/_site/_test/tracking-start-test.js +35 -35
  114. package/_site/_test/workbench-context-receipts-test.js +22 -22
  115. package/_site/_test/workspace-ui-contract-test.js +31 -31
  116. package/_site/_test/zcode-capture-integration-test.js +108 -108
  117. package/_site/lib/ai-vendor-presets.js +83 -83
  118. package/_site/lib/ai-workspace.js +3 -3
  119. package/_site/lib/atomic-file.js +204 -204
  120. package/_site/lib/bridge-adapter.js +260 -260
  121. package/_site/lib/capture-hook-config.js +108 -108
  122. package/_site/lib/claude-cli-runner.js +89 -89
  123. package/_site/lib/cli-instances.js +43 -43
  124. package/_site/lib/codex-context-status.js +17 -17
  125. package/_site/lib/codex-notify-config.js +193 -193
  126. package/_site/lib/commit-conversation-binder.js +12 -12
  127. package/_site/lib/commit-reconciler.js +38 -38
  128. package/_site/lib/context-pack-builder.js +299 -299
  129. package/_site/lib/conversation-exclusions.js +204 -204
  130. package/_site/lib/conversation-query-service.js +17 -17
  131. package/_site/lib/data-dir.js +13 -13
  132. package/_site/lib/embedding-service.js +85 -85
  133. package/_site/lib/engine-connection.js +93 -93
  134. package/_site/lib/engine-knowledge-client.js +72 -72
  135. package/_site/lib/folder-picker-output.js +3 -3
  136. package/_site/lib/git-path.js +14 -14
  137. package/_site/lib/github-team-store.js +3 -3
  138. package/_site/lib/index-builder.js +157 -157
  139. package/_site/lib/integration-status-scan.js +99 -99
  140. package/_site/lib/integration-status-worker.js +14 -14
  141. package/_site/lib/kb-framework.js +15 -15
  142. package/_site/lib/knowledge-db.js +363 -363
  143. package/_site/lib/knowledge-schema.js +121 -121
  144. package/_site/lib/live-gateway.js +78 -78
  145. package/_site/lib/llm-client.js +12 -12
  146. package/_site/lib/model-context-windows.js +82 -82
  147. package/_site/lib/module-bridge.js +874 -874
  148. package/_site/lib/module-port-resolver.js +57 -57
  149. package/_site/lib/post-commit-automation.js +54 -54
  150. package/_site/lib/product-data.js +35 -35
  151. package/_site/lib/project-lifecycle-service.js +14 -14
  152. package/_site/lib/project-registry-store.js +129 -129
  153. package/_site/lib/prompt-registry.js +148 -148
  154. package/_site/lib/registry-events.js +16 -16
  155. package/_site/lib/requirement-adapters.js +10 -10
  156. package/_site/lib/requirement-binder.js +112 -112
  157. package/_site/lib/runtime-control.js +89 -89
  158. package/_site/lib/runtime-endpoint.js +5 -5
  159. package/_site/lib/zcode-context-integration.js +60 -60
  160. package/_site/scripts/codex-notify-fanout.cjs +95 -95
  161. package/_site/scripts/install-module-candidates.js +69 -69
  162. package/_site/scripts/pack-module-candidates.js +89 -89
  163. package/_site/scripts/sync-product-content.js +28 -28
  164. package/_site/scripts/sync-release-version.js +164 -164
  165. package/_site/scripts/vendor-modules.js +49 -49
  166. package/_site/scripts/verify-module-runtime.js +50 -50
  167. package/_site/scripts/verify-product-runtime.js +36 -36
  168. package/_site/scripts/verify-retrieval-gold.js +83 -83
  169. package/_site/server.js +15 -15
  170. package/_site/start.bat +26 -26
  171. package/_site/stop.bat +11 -11
  172. package/bin/project-knowledge-integrations.js +254 -254
  173. package/bin/project-knowledge-kb.js +67 -67
  174. package/bin/project-knowledge-mcp.js +8 -8
  175. package/bin/project-knowledge.js +170 -170
  176. package/claude-prompts.json +21 -21
  177. package/docs/README.zh-CN.md +205 -205
  178. package/docs/assets/architecture.svg +181 -181
  179. package/docs/assets/logo.svg +49 -49
  180. package/docs/fresh-v5-start.md +42 -42
  181. package/docs/leader-glm-workbench-playbook.md +122 -122
  182. package/docs/project-registry-schema.md +11 -11
  183. package/docs/release-5.0.16-acceptance.md +176 -176
  184. package/docs/release-5.0.17-acceptance.md +117 -117
  185. package/docs/release-5.0.19-acceptance.md +39 -25
  186. package/docs/team-knowledge-mode-a-plan.md +73 -73
  187. package/module-runtime-manifest.json +53 -33
  188. package/package.json +3 -2
  189. package/plugins/project-knowledge/.claude-plugin/plugin.json +1 -1
  190. package/plugins/project-knowledge/.codex-plugin/plugin.json +1 -1
  191. package/plugins/project-knowledge/.mcp.json +1 -1
  192. package/plugins/project-knowledge/opencode/project-knowledge.md +3 -3
  193. package/plugins/project-knowledge/skills/project-knowledge/SKILL.md +28 -28
  194. package/templates/change.md +30 -30
  195. package/templates/project-readme.md +18 -18
  196. package/ui/index.html +1 -1
  197. package/ui/knowledge-panel.html +118 -0
  198. package/ui/product.css +8 -0
  199. package/ui/product.js +146 -4
  200. package/vendor-manifest.json +55 -54
@@ -1,42 +1,43 @@
1
- const assert = require('assert');
2
- const fs = require('fs');
3
- const path = require('path');
4
- const { spawnSync } = require('child_process');
5
-
6
- const root = path.resolve(__dirname, '..', '..');
7
- const npmCli = process.env.npm_execpath
8
- || path.join(path.dirname(process.execPath), 'node_modules', 'npm', 'bin', 'npm-cli.js');
9
- const result = spawnSync(process.execPath, [npmCli, 'pack', '--dry-run', '--json'], {
10
- cwd: root,
11
- encoding: 'utf8',
12
- windowsHide: true,
13
- });
14
-
15
- assert.strictEqual(result.status, 0, result.stderr || result.stdout);
16
- const report = JSON.parse(result.stdout);
17
- const files = report[0].files.map(entry => entry.path.replace(/\\/g, '/'));
18
- const forbidden = [
19
- 'docs/project-knowledge-base-review-deliverables.zip',
20
- 'docs/project-knowledge-base-review-deliverables/',
21
- 'docs/project-knowledge-base-pro-review-prompt.md',
22
- 'docs/knowledge-base-trigger-refactor-plan.md',
23
- 'docs/log-ui-comparison.html',
24
- 'ui/claude-workspace-preview.html',
25
- ];
26
- for (const candidate of forbidden) {
27
- assert(!files.some(file => candidate.endsWith('/') ? file.startsWith(candidate) : file === candidate), `local review artifact leaked into npm package: ${candidate}`);
28
- }
29
- assert(files.includes('docs/README.zh-CN.md'));
30
- assert(files.includes('ui/index.html'));
31
-
32
- // Every local asset referenced by the packaged HTML must be present in the
33
- // tarball. This is deliberately derived from index.html so adding a future
34
- // script, stylesheet, or icon cannot silently produce a broken npm install.
35
- const html = fs.readFileSync(path.join(root, 'ui', 'index.html'), 'utf8');
36
- const referencedAssets = [...html.matchAll(/<(?:script|link)\b[^>]*(?:src|href)="(\/[^"?#]+)(?:[?#][^"]*)?"/gi)]
37
- .map(match => `ui/${match[1].replace(/^\//, '')}`);
38
- assert(referencedAssets.length > 0, 'ui/index.html must reference packaged assets');
39
- for (const asset of referencedAssets) {
40
- assert(files.includes(asset), `HTML references an asset missing from the npm package: ${asset}`);
41
- }
42
- console.log('package-boundary-test PASS');
1
+ const assert = require('assert');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const { spawnSync } = require('child_process');
5
+
6
+ const root = path.resolve(__dirname, '..', '..');
7
+ const npmCli = process.env.npm_execpath
8
+ || path.join(path.dirname(process.execPath), 'node_modules', 'npm', 'bin', 'npm-cli.js');
9
+ const result = spawnSync(process.execPath, [npmCli, 'pack', '--dry-run', '--json'], {
10
+ cwd: root,
11
+ encoding: 'utf8',
12
+ windowsHide: true,
13
+ });
14
+
15
+ assert.strictEqual(result.status, 0, result.stderr || result.stdout);
16
+ const report = JSON.parse(result.stdout);
17
+ const files = report[0].files.map(entry => entry.path.replace(/\\/g, '/'));
18
+ const forbidden = [
19
+ 'docs/project-knowledge-base-review-deliverables.zip',
20
+ 'docs/project-knowledge-base-review-deliverables/',
21
+ 'docs/project-knowledge-base-pro-review-prompt.md',
22
+ 'docs/knowledge-base-trigger-refactor-plan.md',
23
+ 'docs/log-ui-comparison.html',
24
+ 'ui/claude-workspace-preview.html',
25
+ ];
26
+ for (const candidate of forbidden) {
27
+ assert(!files.some(file => candidate.endsWith('/') ? file.startsWith(candidate) : file === candidate), `local review artifact leaked into npm package: ${candidate}`);
28
+ }
29
+ assert(files.includes('docs/README.zh-CN.md'));
30
+ assert(files.includes('ui/index.html'));
31
+ assert(files.includes('ui/knowledge-panel.html'), 'K1 knowledge panel page must ship in the npm package');
32
+
33
+ // Every local asset referenced by the packaged HTML must be present in the
34
+ // tarball. This is deliberately derived from index.html so adding a future
35
+ // script, stylesheet, or icon cannot silently produce a broken npm install.
36
+ const html = fs.readFileSync(path.join(root, 'ui', 'index.html'), 'utf8');
37
+ const referencedAssets = [...html.matchAll(/<(?:script|link)\b[^>]*(?:src|href)="(\/[^"?#]+)(?:[?#][^"]*)?"/gi)]
38
+ .map(match => `ui/${match[1].replace(/^\//, '')}`);
39
+ assert(referencedAssets.length > 0, 'ui/index.html must reference packaged assets');
40
+ for (const asset of referencedAssets) {
41
+ assert(files.includes(asset), `HTML references an asset missing from the npm package: ${asset}`);
42
+ }
43
+ console.log('package-boundary-test PASS');
@@ -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');