@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,90 +1,90 @@
1
- const assert = require('assert');
2
- const fs = require('fs');
3
- const os = require('os');
4
- const path = require('path');
5
- const { SCHEMAS } = require('../lib/contracts');
6
- const { StorageLayout } = require('../lib/storage-layout');
7
- const { ProjectRegistryStore } = require('../lib/project-registry-store');
8
- const { ProjectStore } = require('../lib/project-store');
9
-
10
- (async () => {
11
- const root = fs.mkdtempSync(path.join(os.tmpdir(), 'pk-project-store-'));
12
- const layout = new StorageLayout({ dataDir: root });
13
- const registry = new ProjectRegistryStore({ layout });
14
- const projects = new ProjectStore({ layout });
15
- await registry.initialize();
16
-
17
- const create = id => projects.create(id, {
18
- displayName: id,
19
- storageName: `${id}-stable`,
20
- repoPath: path.join(root, 'repos', id),
21
- knowledgePath: path.join(root, 'knowledge', id),
22
- }, { trackingStartCommit: 'base' });
23
- await Promise.all([create('project-a'), create('project-b')]);
24
- await Promise.all([
25
- registry.add('project-a', { displayName: 'A' }),
26
- registry.add('project-b', { displayName: 'B' }),
27
- ]);
28
- assert.deepStrictEqual(new Set(registry.listIds()), new Set(['project-a', 'project-b']));
29
-
30
- await Promise.all([
31
- projects.updateState('project-a', state => { state.lastAnalyzedCommit = 'commit-a'; }),
32
- projects.updateState('project-b', state => { state.lastAnalyzedCommit = 'commit-b'; }),
33
- ]);
34
- assert.strictEqual(projects.readState('project-a').lastAnalyzedCommit, 'commit-a');
35
- assert.strictEqual(projects.readState('project-b').lastAnalyzedCommit, 'commit-b');
36
-
37
- const order = [];
38
- await Promise.all([
39
- projects.updateState('project-a', async state => {
40
- order.push('first-start');
41
- await new Promise(resolve => setTimeout(resolve, 40));
42
- order.push('first-end');
43
- state.lastAnalyzedCommit = 'commit-a-2';
44
- }),
45
- projects.updateState('project-a', state => {
46
- order.push('second');
47
- state.lastAnalyzedCommit = 'commit-a-3';
48
- }),
49
- ]);
50
- assert.deepStrictEqual(order, ['first-start', 'first-end', 'second']);
51
- assert.strictEqual(projects.readState('project-a').lastAnalyzedCommit, 'commit-a-3');
52
- const revision = projects.readState('project-a').revision;
53
- await assert.rejects(
54
- projects.updateState('project-a', state => state, { expectedRevision: revision - 1 }),
55
- error => error.code === 'PROJECT_BUSY',
56
- );
57
-
58
- const configBefore = projects.readConfig('project-a');
59
- const renamed = await projects.updateConfig('project-a', { displayName: 'Renamed A' });
60
- assert.strictEqual(renamed.projectId, configBefore.projectId);
61
- assert.strictEqual(renamed.storageName, configBefore.storageName);
62
- assert.strictEqual(renamed.knowledgePath, configBefore.knowledgePath);
63
- await assert.rejects(projects.updateConfig('project-a', { repoPath: path.join(root, 'unverified-move') }), error => error.code === 'INVALID_ARGUMENT');
64
- const moved = await projects.updateConfig('project-a', { repoPath: path.join(root, 'verified-move') }, { allowRepoPath: true });
65
- assert.strictEqual(moved.repoPath, path.resolve(root, 'verified-move'));
66
- await assert.rejects(projects.updateConfig('project-a', { knowledgePath: path.join(root, 'other') }), error => error.code === 'IMMUTABLE_FIELD');
67
-
68
- const requirementPath = layout.getProjectRequirementsPath('project-a');
69
- assert.strictEqual(fs.existsSync(requirementPath), false);
70
- await Promise.all(Array.from({ length: 20 }, (_, index) => projects.appendRequirement('project-a', {
71
- schema: SCHEMAS.requirement,
72
- id: `req-${index}`,
73
- ts: new Date().toISOString(),
74
- projectId: 'project-a',
75
- requirement: `requirement ${index}`,
76
- })));
77
- assert.strictEqual(projects.readRequirements('project-a').length, 20);
78
- assert.strictEqual(fs.existsSync(layout.getProjectRequirementsPath('project-b')), false);
79
-
80
- fs.writeFileSync(layout.getProjectStatePath('project-b'), '{broken', 'utf8');
81
- assert.throws(() => projects.readState('project-b'), error => error.code === 'DATA_CORRUPT');
82
-
83
- const registryData = registry.read();
84
- assert.deepStrictEqual(Object.keys(registryData.projects).sort(), ['project-a', 'project-b']);
85
- assert.strictEqual(Object.prototype.hasOwnProperty.call(registryData.projects['project-a'], 'lastAnalyzedCommit'), false);
86
- console.log('project-store-test PASS');
87
- })().catch(error => {
88
- console.error(error.stack || error.message);
89
- process.exitCode = 1;
90
- });
1
+ const assert = require('assert');
2
+ const fs = require('fs');
3
+ const os = require('os');
4
+ const path = require('path');
5
+ const { SCHEMAS } = require('../lib/contracts');
6
+ const { StorageLayout } = require('../lib/storage-layout');
7
+ const { ProjectRegistryStore } = require('../lib/project-registry-store');
8
+ const { ProjectStore } = require('../lib/project-store');
9
+
10
+ (async () => {
11
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'pk-project-store-'));
12
+ const layout = new StorageLayout({ dataDir: root });
13
+ const registry = new ProjectRegistryStore({ layout });
14
+ const projects = new ProjectStore({ layout });
15
+ await registry.initialize();
16
+
17
+ const create = id => projects.create(id, {
18
+ displayName: id,
19
+ storageName: `${id}-stable`,
20
+ repoPath: path.join(root, 'repos', id),
21
+ knowledgePath: path.join(root, 'knowledge', id),
22
+ }, { trackingStartCommit: 'base' });
23
+ await Promise.all([create('project-a'), create('project-b')]);
24
+ await Promise.all([
25
+ registry.add('project-a', { displayName: 'A' }),
26
+ registry.add('project-b', { displayName: 'B' }),
27
+ ]);
28
+ assert.deepStrictEqual(new Set(registry.listIds()), new Set(['project-a', 'project-b']));
29
+
30
+ await Promise.all([
31
+ projects.updateState('project-a', state => { state.lastAnalyzedCommit = 'commit-a'; }),
32
+ projects.updateState('project-b', state => { state.lastAnalyzedCommit = 'commit-b'; }),
33
+ ]);
34
+ assert.strictEqual(projects.readState('project-a').lastAnalyzedCommit, 'commit-a');
35
+ assert.strictEqual(projects.readState('project-b').lastAnalyzedCommit, 'commit-b');
36
+
37
+ const order = [];
38
+ await Promise.all([
39
+ projects.updateState('project-a', async state => {
40
+ order.push('first-start');
41
+ await new Promise(resolve => setTimeout(resolve, 40));
42
+ order.push('first-end');
43
+ state.lastAnalyzedCommit = 'commit-a-2';
44
+ }),
45
+ projects.updateState('project-a', state => {
46
+ order.push('second');
47
+ state.lastAnalyzedCommit = 'commit-a-3';
48
+ }),
49
+ ]);
50
+ assert.deepStrictEqual(order, ['first-start', 'first-end', 'second']);
51
+ assert.strictEqual(projects.readState('project-a').lastAnalyzedCommit, 'commit-a-3');
52
+ const revision = projects.readState('project-a').revision;
53
+ await assert.rejects(
54
+ projects.updateState('project-a', state => state, { expectedRevision: revision - 1 }),
55
+ error => error.code === 'PROJECT_BUSY',
56
+ );
57
+
58
+ const configBefore = projects.readConfig('project-a');
59
+ const renamed = await projects.updateConfig('project-a', { displayName: 'Renamed A' });
60
+ assert.strictEqual(renamed.projectId, configBefore.projectId);
61
+ assert.strictEqual(renamed.storageName, configBefore.storageName);
62
+ assert.strictEqual(renamed.knowledgePath, configBefore.knowledgePath);
63
+ await assert.rejects(projects.updateConfig('project-a', { repoPath: path.join(root, 'unverified-move') }), error => error.code === 'INVALID_ARGUMENT');
64
+ const moved = await projects.updateConfig('project-a', { repoPath: path.join(root, 'verified-move') }, { allowRepoPath: true });
65
+ assert.strictEqual(moved.repoPath, path.resolve(root, 'verified-move'));
66
+ await assert.rejects(projects.updateConfig('project-a', { knowledgePath: path.join(root, 'other') }), error => error.code === 'IMMUTABLE_FIELD');
67
+
68
+ const requirementPath = layout.getProjectRequirementsPath('project-a');
69
+ assert.strictEqual(fs.existsSync(requirementPath), false);
70
+ await Promise.all(Array.from({ length: 20 }, (_, index) => projects.appendRequirement('project-a', {
71
+ schema: SCHEMAS.requirement,
72
+ id: `req-${index}`,
73
+ ts: new Date().toISOString(),
74
+ projectId: 'project-a',
75
+ requirement: `requirement ${index}`,
76
+ })));
77
+ assert.strictEqual(projects.readRequirements('project-a').length, 20);
78
+ assert.strictEqual(fs.existsSync(layout.getProjectRequirementsPath('project-b')), false);
79
+
80
+ fs.writeFileSync(layout.getProjectStatePath('project-b'), '{broken', 'utf8');
81
+ assert.throws(() => projects.readState('project-b'), error => error.code === 'DATA_CORRUPT');
82
+
83
+ const registryData = registry.read();
84
+ assert.deepStrictEqual(Object.keys(registryData.projects).sort(), ['project-a', 'project-b']);
85
+ assert.strictEqual(Object.prototype.hasOwnProperty.call(registryData.projects['project-a'], 'lastAnalyzedCommit'), false);
86
+ console.log('project-store-test PASS');
87
+ })().catch(error => {
88
+ console.error(error.stack || error.message);
89
+ process.exitCode = 1;
90
+ });
@@ -0,0 +1,38 @@
1
+ 'use strict';
2
+ const assert = require('node:assert/strict');
3
+ const http = require('node:http');
4
+ const { RegistryEvents } = require('../lib/registry-events');
5
+ const { createRequestHandler } = require('../lib/server-app');
6
+
7
+ (async () => {
8
+ const events = new RegistryEvents(), received = [];
9
+ const off = events.subscribe(event => received.push(event));
10
+ assert.equal(received[0].type, 'registry-snapshot');
11
+ const runtime = { registryEvents: events, logger: { debug: async () => {}, info: async () => {} },
12
+ moduleBridge: { syncEngineConfiguration: async () => ({}) },
13
+ settingsStore: { read: () => ({ brain: { id: 'brain-default', name: 'before' }, knowledge: { rootPath: '' } }), updatePatch: async patch => ({ brain: patch.brain }) } };
14
+ // Exercise the real HTTP completion hook independently of business stores.
15
+ const handler = createRequestHandler(runtime, { host: '127.0.0.1', port: 0 });
16
+ const server = http.createServer(handler);
17
+ await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
18
+ try {
19
+ const base = `http://127.0.0.1:${server.address().port}`;
20
+ const reader = await fetch(`${base}/api/registry/events`);
21
+ const stream = reader.body.getReader();
22
+ const first = new TextDecoder().decode((await stream.read()).value);
23
+ assert.match(first, /registry-snapshot/);
24
+ const mutation = await fetch(`${base}/api/brain`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'after' }) });
25
+ assert.equal(mutation.status, 200);
26
+ const change = new TextDecoder().decode((await stream.read()).value);
27
+ assert.match(change, /registry-changed/);
28
+ assert.equal(received[1].revision, 1);
29
+ const reconnect = []; events.subscribe(event => reconnect.push(event))();
30
+ assert.equal(reconnect[0].revision, 1);
31
+ assert.equal(reconnect[0].instanceId, received[0].instanceId);
32
+ await stream.cancel();
33
+ off(); events.publish(['brain']);
34
+ assert.equal(received.length, 2, 'unsubscribed observers receive no more updates');
35
+ assert.notEqual(new RegistryEvents().instanceId, events.instanceId, 'restart makes old revisions distinguishable');
36
+ console.log('registry-events-test PASS (real SSE, reconnect identity and unsubscribe)');
37
+ } finally { server.closeAllConnections(); await new Promise(resolve => server.close(resolve)); }
38
+ })().catch(error => { console.error(error); process.exitCode = 1; });
@@ -1,115 +1,115 @@
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
-
7
- const ROOT = path.resolve(__dirname, '..', '..');
8
- const SCRIPT = path.join(ROOT, '_site', 'scripts', 'sync-release-version.js');
9
- const TEMP = fs.mkdtempSync(path.join(os.tmpdir(), `project-knowledge-release-version-${process.pid}-`));
10
- const FILES = [
11
- 'package.json',
12
- 'package-lock.json',
13
- 'desktop/package.json',
14
- 'desktop/package-lock.json',
15
- 'plugins/project-knowledge/.claude-plugin/plugin.json',
16
- 'plugins/project-knowledge/.codex-plugin/plugin.json',
17
- '.claude-plugin/marketplace.json',
18
- 'plugins/project-knowledge/.mcp.json',
19
- ];
20
-
21
- function copyFixture() {
22
- for (const relative of FILES) {
23
- const destination = path.join(TEMP, relative);
24
- fs.mkdirSync(path.dirname(destination), { recursive: true });
25
- fs.copyFileSync(path.join(ROOT, relative), destination);
26
- }
27
- }
28
-
29
- function run(args) {
30
- return spawnSync(process.execPath, [SCRIPT, '--root', TEMP, ...args], {
31
- cwd: ROOT,
32
- encoding: 'utf8',
33
- windowsHide: true,
34
- });
35
- }
36
-
37
- function git(args) {
38
- const result = spawnSync('git', args, { cwd: TEMP, encoding: 'utf8', windowsHide: true });
39
- assert.strictEqual(result.status, 0, result.stderr || result.stdout);
40
- return result;
41
- }
42
-
43
- try {
44
- copyFixture();
45
- git(['init', '-q']);
46
- git(['config', 'user.email', 'release-test@example.local']);
47
- git(['config', 'user.name', 'Release Test']);
48
- git(['add', '.']);
49
- git(['commit', '-q', '-m', 'fixture']);
50
- const rootPackage = path.join(TEMP, 'package.json');
51
- const value = JSON.parse(fs.readFileSync(rootPackage, 'utf8'));
52
- value.version = '9.8.7';
53
- fs.writeFileSync(rootPackage, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
54
-
55
- const before = run(['--check']);
56
- assert.notStrictEqual(before.status, 0, 'check must fail before derived version files are synchronized');
57
- assert.match(before.stderr, /out of sync/);
58
-
59
- const sync = run(['--stage']);
60
- assert.strictEqual(sync.status, 0, sync.stderr || sync.stdout);
61
- const check = run(['--check']);
62
- assert.strictEqual(check.status, 0, check.stderr || check.stdout);
63
- const matchingTag = run(['--check', '--tag', 'v9.8.7']);
64
- assert.strictEqual(matchingTag.status, 0, matchingTag.stderr || matchingTag.stdout);
65
- const mismatchedTag = run(['--check', '--tag', 'v9.8.8']);
66
- assert.notStrictEqual(mismatchedTag.status, 0, 'check must reject a tag that does not match package.json');
67
- assert.match(mismatchedTag.stderr, /must match package version/);
68
-
69
- const claude = JSON.parse(fs.readFileSync(path.join(TEMP, 'plugins/project-knowledge/.claude-plugin/plugin.json'), 'utf8'));
70
- const codex = JSON.parse(fs.readFileSync(path.join(TEMP, 'plugins/project-knowledge/.codex-plugin/plugin.json'), 'utf8'));
71
- const marketplace = JSON.parse(fs.readFileSync(path.join(TEMP, '.claude-plugin/marketplace.json'), 'utf8'));
72
- const mcp = JSON.parse(fs.readFileSync(path.join(TEMP, 'plugins/project-knowledge/.mcp.json'), 'utf8'));
73
- const desktopLock = JSON.parse(fs.readFileSync(path.join(TEMP, 'desktop/package-lock.json'), 'utf8'));
74
- assert.strictEqual(claude.version, '9.8.7');
75
- assert.strictEqual(codex.version, '9.8.7');
76
- assert.strictEqual(marketplace.plugins[0].version, '9.8.7');
77
- const server = mcp.mcpServers['project-knowledge'];
78
- const serialized = JSON.stringify(server);
79
- assert.strictEqual(server.command, 'node', 'the MCP server must bootstrap through the local node runtime');
80
- assert.strictEqual(server.args[0], '-e', 'the MCP server must use the node -e bootstrap chain');
81
- assert(server.args[1].includes('mcp-launcher.cjs'), 'the bootstrap must load the bundled local launcher');
82
- assert(!/npx/i.test(serialized) && !/@sanqianx\/project-knowledge@/.test(serialized), 'the pinned npx package form must stay rejected');
83
- assert(!/https?:\/\//.test(serialized), 'the MCP bootstrap must not reference network sources');
84
- assert.strictEqual(server.cwd, undefined, 'the MCP server must not pin a cwd');
85
- assert(!server.args[1].includes('process.cwd'), 'the MCP bootstrap must never fall back to cwd source resolution');
86
- assert(server.env && server.env.KB_PLUGIN_VERSION === '9.8.7', 'env.KB_PLUGIN_VERSION must follow the release version');
87
- assert.strictEqual(desktopLock.version, '9.8.7');
88
- assert.strictEqual(desktopLock.packages[''].version, '9.8.7');
89
- const staged = git(['diff', '--cached', '--name-only']);
90
- const stagedFiles = staged.stdout.split(/\r?\n/).filter(Boolean).sort();
91
- assert.deepStrictEqual(stagedFiles, [
92
- '.claude-plugin/marketplace.json',
93
- 'desktop/package-lock.json',
94
- 'desktop/package.json',
95
- 'package-lock.json',
96
- 'plugins/project-knowledge/.claude-plugin/plugin.json',
97
- 'plugins/project-knowledge/.codex-plugin/plugin.json',
98
- 'plugins/project-knowledge/.mcp.json',
99
- ]);
100
- const scripts = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf8')).scripts;
101
- assert.strictEqual(scripts.preversion, 'npm run release:verify');
102
- assert.strictEqual(scripts.version, 'node _site/scripts/sync-release-version.js --stage --web-only');
103
- assert.strictEqual(scripts.postversion, 'npm run release:verify');
104
- const desktopBytes = FILES.filter(file => file.startsWith('desktop/')).map(file => fs.readFileSync(path.join(TEMP, file)));
105
- value.version = '9.8.8-rc.1'; fs.writeFileSync(rootPackage, JSON.stringify(value, null, 2) + '\n');
106
- assert.strictEqual(run(['--web-only']).status, 0);
107
- assert.strictEqual(run(['--check', '--web-only']).status, 0);
108
- const resyncedMcp = JSON.parse(fs.readFileSync(path.join(TEMP, 'plugins/project-knowledge/.mcp.json'), 'utf8'));
109
- assert.strictEqual(resyncedMcp.mcpServers['project-knowledge'].env.KB_PLUGIN_VERSION, '9.8.8-rc.1', 'KB_PLUGIN_VERSION must follow every version bump');
110
- assert.notStrictEqual(run(['--check']).status, 0, 'desktop still needs explicit synchronization for desktop releases');
111
- FILES.filter(file => file.startsWith('desktop/')).forEach((file, i) => assert.deepStrictEqual(fs.readFileSync(path.join(TEMP, file)), desktopBytes[i]));
112
- console.log('release-version-sync-test PASS');
113
- } finally {
114
- fs.rmSync(TEMP, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
115
- }
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
+
7
+ const ROOT = path.resolve(__dirname, '..', '..');
8
+ const SCRIPT = path.join(ROOT, '_site', 'scripts', 'sync-release-version.js');
9
+ const TEMP = fs.mkdtempSync(path.join(os.tmpdir(), `project-knowledge-release-version-${process.pid}-`));
10
+ const FILES = [
11
+ 'package.json',
12
+ 'package-lock.json',
13
+ 'desktop/package.json',
14
+ 'desktop/package-lock.json',
15
+ 'plugins/project-knowledge/.claude-plugin/plugin.json',
16
+ 'plugins/project-knowledge/.codex-plugin/plugin.json',
17
+ '.claude-plugin/marketplace.json',
18
+ 'plugins/project-knowledge/.mcp.json',
19
+ ];
20
+
21
+ function copyFixture() {
22
+ for (const relative of FILES) {
23
+ const destination = path.join(TEMP, relative);
24
+ fs.mkdirSync(path.dirname(destination), { recursive: true });
25
+ fs.copyFileSync(path.join(ROOT, relative), destination);
26
+ }
27
+ }
28
+
29
+ function run(args) {
30
+ return spawnSync(process.execPath, [SCRIPT, '--root', TEMP, ...args], {
31
+ cwd: ROOT,
32
+ encoding: 'utf8',
33
+ windowsHide: true,
34
+ });
35
+ }
36
+
37
+ function git(args) {
38
+ const result = spawnSync('git', args, { cwd: TEMP, encoding: 'utf8', windowsHide: true });
39
+ assert.strictEqual(result.status, 0, result.stderr || result.stdout);
40
+ return result;
41
+ }
42
+
43
+ try {
44
+ copyFixture();
45
+ git(['init', '-q']);
46
+ git(['config', 'user.email', 'release-test@example.local']);
47
+ git(['config', 'user.name', 'Release Test']);
48
+ git(['add', '.']);
49
+ git(['commit', '-q', '-m', 'fixture']);
50
+ const rootPackage = path.join(TEMP, 'package.json');
51
+ const value = JSON.parse(fs.readFileSync(rootPackage, 'utf8'));
52
+ value.version = '9.8.7';
53
+ fs.writeFileSync(rootPackage, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
54
+
55
+ const before = run(['--check']);
56
+ assert.notStrictEqual(before.status, 0, 'check must fail before derived version files are synchronized');
57
+ assert.match(before.stderr, /out of sync/);
58
+
59
+ const sync = run(['--stage']);
60
+ assert.strictEqual(sync.status, 0, sync.stderr || sync.stdout);
61
+ const check = run(['--check']);
62
+ assert.strictEqual(check.status, 0, check.stderr || check.stdout);
63
+ const matchingTag = run(['--check', '--tag', 'v9.8.7']);
64
+ assert.strictEqual(matchingTag.status, 0, matchingTag.stderr || matchingTag.stdout);
65
+ const mismatchedTag = run(['--check', '--tag', 'v9.8.8']);
66
+ assert.notStrictEqual(mismatchedTag.status, 0, 'check must reject a tag that does not match package.json');
67
+ assert.match(mismatchedTag.stderr, /must match package version/);
68
+
69
+ const claude = JSON.parse(fs.readFileSync(path.join(TEMP, 'plugins/project-knowledge/.claude-plugin/plugin.json'), 'utf8'));
70
+ const codex = JSON.parse(fs.readFileSync(path.join(TEMP, 'plugins/project-knowledge/.codex-plugin/plugin.json'), 'utf8'));
71
+ const marketplace = JSON.parse(fs.readFileSync(path.join(TEMP, '.claude-plugin/marketplace.json'), 'utf8'));
72
+ const mcp = JSON.parse(fs.readFileSync(path.join(TEMP, 'plugins/project-knowledge/.mcp.json'), 'utf8'));
73
+ const desktopLock = JSON.parse(fs.readFileSync(path.join(TEMP, 'desktop/package-lock.json'), 'utf8'));
74
+ assert.strictEqual(claude.version, '9.8.7');
75
+ assert.strictEqual(codex.version, '9.8.7');
76
+ assert.strictEqual(marketplace.plugins[0].version, '9.8.7');
77
+ const server = mcp.mcpServers['project-knowledge'];
78
+ const serialized = JSON.stringify(server);
79
+ assert.strictEqual(server.command, 'node', 'the MCP server must bootstrap through the local node runtime');
80
+ assert.strictEqual(server.args[0], '-e', 'the MCP server must use the node -e bootstrap chain');
81
+ assert(server.args[1].includes('mcp-launcher.cjs'), 'the bootstrap must load the bundled local launcher');
82
+ assert(!/npx/i.test(serialized) && !/@sanqianx\/project-knowledge@/.test(serialized), 'the pinned npx package form must stay rejected');
83
+ assert(!/https?:\/\//.test(serialized), 'the MCP bootstrap must not reference network sources');
84
+ assert.strictEqual(server.cwd, undefined, 'the MCP server must not pin a cwd');
85
+ assert(!server.args[1].includes('process.cwd'), 'the MCP bootstrap must never fall back to cwd source resolution');
86
+ assert(server.env && server.env.KB_PLUGIN_VERSION === '9.8.7', 'env.KB_PLUGIN_VERSION must follow the release version');
87
+ assert.strictEqual(desktopLock.version, '9.8.7');
88
+ assert.strictEqual(desktopLock.packages[''].version, '9.8.7');
89
+ const staged = git(['diff', '--cached', '--name-only']);
90
+ const stagedFiles = staged.stdout.split(/\r?\n/).filter(Boolean).sort();
91
+ assert.deepStrictEqual(stagedFiles, [
92
+ '.claude-plugin/marketplace.json',
93
+ 'desktop/package-lock.json',
94
+ 'desktop/package.json',
95
+ 'package-lock.json',
96
+ 'plugins/project-knowledge/.claude-plugin/plugin.json',
97
+ 'plugins/project-knowledge/.codex-plugin/plugin.json',
98
+ 'plugins/project-knowledge/.mcp.json',
99
+ ]);
100
+ const scripts = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf8')).scripts;
101
+ assert.strictEqual(scripts.preversion, 'npm run release:verify');
102
+ assert.strictEqual(scripts.version, 'node _site/scripts/sync-release-version.js --stage --web-only');
103
+ assert.strictEqual(scripts.postversion, 'npm run release:verify');
104
+ const desktopBytes = FILES.filter(file => file.startsWith('desktop/')).map(file => fs.readFileSync(path.join(TEMP, file)));
105
+ value.version = '9.8.8-rc.1'; fs.writeFileSync(rootPackage, JSON.stringify(value, null, 2) + '\n');
106
+ assert.strictEqual(run(['--web-only']).status, 0);
107
+ assert.strictEqual(run(['--check', '--web-only']).status, 0);
108
+ const resyncedMcp = JSON.parse(fs.readFileSync(path.join(TEMP, 'plugins/project-knowledge/.mcp.json'), 'utf8'));
109
+ assert.strictEqual(resyncedMcp.mcpServers['project-knowledge'].env.KB_PLUGIN_VERSION, '9.8.8-rc.1', 'KB_PLUGIN_VERSION must follow every version bump');
110
+ assert.notStrictEqual(run(['--check']).status, 0, 'desktop still needs explicit synchronization for desktop releases');
111
+ FILES.filter(file => file.startsWith('desktop/')).forEach((file, i) => assert.deepStrictEqual(fs.readFileSync(path.join(TEMP, file)), desktopBytes[i]));
112
+ console.log('release-version-sync-test PASS');
113
+ } finally {
114
+ fs.rmSync(TEMP, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
115
+ }