@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,80 +1,80 @@
1
- 'use strict';
2
- const assert = require('node:assert/strict');
3
- const fs = require('node:fs');
4
- const path = require('node:path');
5
- const os = require('node:os');
6
- const net = require('node:net');
7
- const { spawn } = require('node:child_process');
8
- const endpoint = require('../lib/runtime-endpoint');
9
-
10
- const packageRoot = process.env.KB_LAUNCHER_TEST_PACKAGE || path.resolve(__dirname, '../..');
11
- const bin = path.join(packageRoot, 'bin/project-knowledge.js');
12
- const root = fs.mkdtempSync(path.join(os.tmpdir(), 'kb-launcher-readiness-'));
13
- const data = path.join(root, '.project-knowledge-v5');
14
- const record = path.join(data, 'runtime/claude-sessions/project-a/session.json');
15
- const recordBytes = JSON.stringify({ schema: 'claude-workbench-session/v1', sessionId: 'old-test', projectSlug: 'project-a', state: 'running', metadata: { automation: true } });
16
- fs.mkdirSync(path.dirname(record), { recursive: true });
17
- fs.writeFileSync(record, recordBytes);
18
- const env = { ...process.env, KB_DATA_DIR: data, KB_MODULES_AUTOSTART: '0', AI_CODING_EVENT_BRIDGE_HOME: path.join(root, 'bridge') };
19
- let serverPid;
20
-
21
- function run(args, overrides = {}) {
22
- return new Promise((resolve, reject) => {
23
- const child = spawn(process.execPath, [bin, ...args], { cwd: root, env: { ...env, ...overrides }, stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true });
24
- let stdout = '', stderr = '';
25
- const timer = setTimeout(() => { child.kill(); reject(new Error(`CLI timed out: ${stdout} ${stderr}`)); }, 25000);
26
- child.stdout.on('data', chunk => { stdout += chunk; });
27
- child.stderr.on('data', chunk => { stderr += chunk; });
28
- child.once('error', reject);
29
- child.once('exit', code => { clearTimeout(timer); resolve({ code, stdout, stderr }); });
30
- });
31
- }
32
- async function freePort() {
33
- const server = net.createServer();
34
- await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
35
- const port = server.address().port;
36
- await new Promise(resolve => server.close(resolve));
37
- return port;
38
- }
39
-
40
- (async () => {
41
- const port = await freePort();
42
- const launched = await run(['--no-open'], { KB_SITE_PORT: String(port) });
43
- serverPid = endpoint.readEndpoint(data)?.pid;
44
- assert.equal(launched.code, 0, launched.stderr);
45
- assert.match(launched.stdout, new RegExp(`is ready at http://127.0.0.1:${port}`));
46
- assert(serverPid && endpoint.isProcessAlive(serverPid));
47
- const response = await fetch(`http://127.0.0.1:${port}/api/state`);
48
- assert.equal(response.status, 200, 'ready must mean HTTP is usable, not only a PID exists');
49
- const state = await response.json();
50
- assert.deepEqual(state.projects, [], 'abandoned sessions must not import projects or enqueue analysis');
51
- assert.equal(fs.readFileSync(record, 'utf8'), recordBytes);
52
- const again = await run(['--no-open']);
53
- assert.equal(again.code, 0);
54
- assert.match(again.stdout, /Already running/);
55
- const stopped = await run(['stop']);
56
- assert.equal(stopped.code, 0);
57
- for (let attempt = 0; attempt < 50 && endpoint.isProcessAlive(serverPid); attempt++) await new Promise(resolve => setTimeout(resolve, 100));
58
- assert.equal(endpoint.isProcessAlive(serverPid), false);
59
- serverPid = null;
60
-
61
- const oldData = path.join(root, 'actual-old-data');
62
- fs.mkdirSync(oldData);
63
- const original = JSON.stringify({ projectOrder: ['old-project'] });
64
- fs.writeFileSync(path.join(oldData, 'projects.json'), original);
65
- const failed = await run(['--no-open', '--port', String(port)], { KB_DATA_DIR: oldData });
66
- assert.equal(failed.code, 1, 'background failures must return nonzero to the invoking terminal');
67
- assert.match(failed.stderr, /启动失败/);
68
- assert.match(failed.stderr, /projects\.json/);
69
- assert(!/is ready|Listening at/.test(failed.stdout));
70
- assert.equal(fs.readFileSync(path.join(oldData, 'projects.json'), 'utf8'), original);
71
- assert.equal(fs.existsSync(path.join(oldData, 'product-data.json')), false);
72
- const log = fs.readFileSync(path.join(oldData, 'launcher.log'), 'utf8');
73
- assert.match(log, /startup failed/);
74
- assert(!/Listening at/.test(log), 'never advertise a listener before initialization succeeds');
75
- console.log('launcher readiness PASS: unrelated cwd, abandoned records, HTTP readiness, stop, terminal failure, legacy preservation');
76
- })().catch(error => { console.error(error); process.exitCode = 1; }).finally(() => {
77
- if (serverPid && endpoint.readEndpoint(data)?.pid === serverPid) {
78
- try { process.kill(serverPid); } catch {}
79
- }
80
- });
1
+ 'use strict';
2
+ const assert = require('node:assert/strict');
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+ const os = require('node:os');
6
+ const net = require('node:net');
7
+ const { spawn } = require('node:child_process');
8
+ const endpoint = require('../lib/runtime-endpoint');
9
+
10
+ const packageRoot = process.env.KB_LAUNCHER_TEST_PACKAGE || path.resolve(__dirname, '../..');
11
+ const bin = path.join(packageRoot, 'bin/project-knowledge.js');
12
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'kb-launcher-readiness-'));
13
+ const data = path.join(root, '.project-knowledge-v5');
14
+ const record = path.join(data, 'runtime/claude-sessions/project-a/session.json');
15
+ const recordBytes = JSON.stringify({ schema: 'claude-workbench-session/v1', sessionId: 'old-test', projectSlug: 'project-a', state: 'running', metadata: { automation: true } });
16
+ fs.mkdirSync(path.dirname(record), { recursive: true });
17
+ fs.writeFileSync(record, recordBytes);
18
+ const env = { ...process.env, KB_DATA_DIR: data, KB_MODULES_AUTOSTART: '0', AI_CODING_EVENT_BRIDGE_HOME: path.join(root, 'bridge') };
19
+ let serverPid;
20
+
21
+ function run(args, overrides = {}) {
22
+ return new Promise((resolve, reject) => {
23
+ const child = spawn(process.execPath, [bin, ...args], { cwd: root, env: { ...env, ...overrides }, stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true });
24
+ let stdout = '', stderr = '';
25
+ const timer = setTimeout(() => { child.kill(); reject(new Error(`CLI timed out: ${stdout} ${stderr}`)); }, 25000);
26
+ child.stdout.on('data', chunk => { stdout += chunk; });
27
+ child.stderr.on('data', chunk => { stderr += chunk; });
28
+ child.once('error', reject);
29
+ child.once('exit', code => { clearTimeout(timer); resolve({ code, stdout, stderr }); });
30
+ });
31
+ }
32
+ async function freePort() {
33
+ const server = net.createServer();
34
+ await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
35
+ const port = server.address().port;
36
+ await new Promise(resolve => server.close(resolve));
37
+ return port;
38
+ }
39
+
40
+ (async () => {
41
+ const port = await freePort();
42
+ const launched = await run(['--no-open'], { KB_SITE_PORT: String(port) });
43
+ serverPid = endpoint.readEndpoint(data)?.pid;
44
+ assert.equal(launched.code, 0, launched.stderr);
45
+ assert.match(launched.stdout, new RegExp(`is ready at http://127.0.0.1:${port}`));
46
+ assert(serverPid && endpoint.isProcessAlive(serverPid));
47
+ const response = await fetch(`http://127.0.0.1:${port}/api/state`);
48
+ assert.equal(response.status, 200, 'ready must mean HTTP is usable, not only a PID exists');
49
+ const state = await response.json();
50
+ assert.deepEqual(state.projects, [], 'abandoned sessions must not import projects or enqueue analysis');
51
+ assert.equal(fs.readFileSync(record, 'utf8'), recordBytes);
52
+ const again = await run(['--no-open']);
53
+ assert.equal(again.code, 0);
54
+ assert.match(again.stdout, /Already running/);
55
+ const stopped = await run(['stop']);
56
+ assert.equal(stopped.code, 0);
57
+ for (let attempt = 0; attempt < 50 && endpoint.isProcessAlive(serverPid); attempt++) await new Promise(resolve => setTimeout(resolve, 100));
58
+ assert.equal(endpoint.isProcessAlive(serverPid), false);
59
+ serverPid = null;
60
+
61
+ const oldData = path.join(root, 'actual-old-data');
62
+ fs.mkdirSync(oldData);
63
+ const original = JSON.stringify({ projectOrder: ['old-project'] });
64
+ fs.writeFileSync(path.join(oldData, 'projects.json'), original);
65
+ const failed = await run(['--no-open', '--port', String(port)], { KB_DATA_DIR: oldData });
66
+ assert.equal(failed.code, 1, 'background failures must return nonzero to the invoking terminal');
67
+ assert.match(failed.stderr, /启动失败/);
68
+ assert.match(failed.stderr, /projects\.json/);
69
+ assert(!/is ready|Listening at/.test(failed.stdout));
70
+ assert.equal(fs.readFileSync(path.join(oldData, 'projects.json'), 'utf8'), original);
71
+ assert.equal(fs.existsSync(path.join(oldData, 'product-data.json')), false);
72
+ const log = fs.readFileSync(path.join(oldData, 'launcher.log'), 'utf8');
73
+ assert.match(log, /startup failed/);
74
+ assert(!/Listening at/.test(log), 'never advertise a listener before initialization succeeds');
75
+ console.log('launcher readiness PASS: unrelated cwd, abandoned records, HTTP readiness, stop, terminal failure, legacy preservation');
76
+ })().catch(error => { console.error(error); process.exitCode = 1; }).finally(() => {
77
+ if (serverPid && endpoint.readEndpoint(data)?.pid === serverPid) {
78
+ try { process.kill(serverPid); } catch {}
79
+ }
80
+ });
@@ -1,106 +1,106 @@
1
- // Run: node _site/_test/legacy-claude-session-alias-test.js
2
-
3
- const assert = require('assert');
4
- const fs = require('fs');
5
- const os = require('os');
6
- const path = require('path');
7
-
8
- const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), `kb-legacy-session-${process.pid}-`));
9
- const projectId = 'project-stable-id';
10
- const legacySlug = 'legacy-project-slug';
11
- const knowledgePath = path.join(dataDir, 'knowledge', legacySlug);
12
- const metadataPath = path.join(dataDir, 'projects', projectId);
13
- const now = new Date().toISOString();
14
- process.env.KB_DATA_DIR = dataDir;
15
- require('../lib/data-dir')._resetCache();
16
-
17
- const { defaultProjectConfig, defaultProjectState } = require('../lib/project-store');
18
-
19
- function writeRecord(root, record) {
20
- fs.mkdirSync(root, { recursive: true });
21
- fs.writeFileSync(path.join(root, `${record.sessionId}.json`), `${JSON.stringify({
22
- schema: 'claude-workbench-session/v1',
23
- projectPath: dataDir,
24
- kbPath: knowledgePath,
25
- promptKey: 'post-commit-automation',
26
- runner: 'sdk',
27
- state: 'idle',
28
- model: 'test-model',
29
- startedAt: now,
30
- endedAt: now,
31
- exitCode: 0,
32
- turns: 1,
33
- pendingPermission: null,
34
- updatedAt: now,
35
- ...record,
36
- }, null, 2)}\n`, 'utf8');
37
- }
38
-
39
- try {
40
- fs.mkdirSync(metadataPath, { recursive: true });
41
- fs.mkdirSync(knowledgePath, { recursive: true });
42
- fs.writeFileSync(path.join(metadataPath, 'config.json'), `${JSON.stringify(defaultProjectConfig(projectId, {
43
- displayName: 'Legacy project',
44
- storageName: 'legacy-project',
45
- repoPath: dataDir,
46
- knowledgePath,
47
- legacyExtensions: { slug: legacySlug, sourceSchema: 'legacy-v1' },
48
- }), null, 2)}\n`, 'utf8');
49
- fs.writeFileSync(path.join(metadataPath, 'state.json'), `${JSON.stringify(defaultProjectState(), null, 2)}\n`, 'utf8');
50
- fs.writeFileSync(path.join(dataDir, 'projects.json'), `${JSON.stringify({
51
- schema: 'project-registry/v2',
52
- schemaVersion: 2,
53
- projectOrder: [projectId],
54
- projects: { [projectId]: { createdAt: now, displayNameSnapshot: 'Legacy project' } },
55
- revision: 1,
56
- updatedAt: now,
57
- }, null, 2)}\n`, 'utf8');
58
-
59
- const legacyRoot = path.join(dataDir, '_ai', legacySlug, 'claude-workbench');
60
- const currentRoot = path.join(dataDir, 'runtime', 'claude-sessions', projectId);
61
- writeRecord(legacyRoot, {
62
- sessionId: 'sess-legacy-only',
63
- projectSlug: legacySlug,
64
- source: 'git-hook',
65
- automation: true,
66
- events: [
67
- { type: 'claude/user-prompt', text: 'historical hook prompt' },
68
- { type: 'claude/result', result: 'historical analysis result' },
69
- ],
70
- });
71
- writeRecord(legacyRoot, {
72
- sessionId: 'sess-duplicate',
73
- projectSlug: legacySlug,
74
- source: 'legacy-copy',
75
- events: [{ type: 'claude/result', result: 'legacy duplicate' }],
76
- });
77
- writeRecord(currentRoot, {
78
- sessionId: 'sess-duplicate',
79
- projectSlug: projectId,
80
- source: 'current-runtime',
81
- events: [{ type: 'claude/result', result: 'current duplicate' }],
82
- });
83
-
84
- const runner = require('../lib/claude-cli-runner');
85
- const listed = runner.listSessions({ projectSlug: projectId });
86
- assert.strictEqual(listed.length, 2);
87
- const legacy = listed.find(session => session.sessionId === 'sess-legacy-only');
88
- assert(legacy, 'legacy alias session must be discoverable under the stable projectId');
89
- assert.strictEqual(legacy.projectSlug, projectId);
90
- assert.strictEqual(legacy.legacyProjectSlug, legacySlug);
91
- assert.strictEqual(legacy.legacySource, true);
92
- assert.strictEqual(listed.find(session => session.sessionId === 'sess-duplicate').source, 'current-runtime', 'canonical runtime record must win duplicate IDs');
93
-
94
- const events = [];
95
- const unsubscribe = runner.subscribe('sess-legacy-only', event => events.push(event));
96
- unsubscribe();
97
- assert(events.some(event => event.type === 'claude/user-prompt' && event.text === 'historical hook prompt'));
98
- assert(events.some(event => event.type === 'claude/result' && event.result === 'historical analysis result'));
99
- assert.strictEqual(runner.getState('sess-legacy-only').projectSlug, projectId);
100
- console.log('legacy-claude-session-alias-test PASS');
101
- } catch (error) {
102
- console.error(error.stack || error.message);
103
- process.exitCode = 1;
104
- } finally {
105
- fs.rmSync(dataDir, { recursive: true, force: true });
106
- }
1
+ // Run: node _site/_test/legacy-claude-session-alias-test.js
2
+
3
+ const assert = require('assert');
4
+ const fs = require('fs');
5
+ const os = require('os');
6
+ const path = require('path');
7
+
8
+ const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), `kb-legacy-session-${process.pid}-`));
9
+ const projectId = 'project-stable-id';
10
+ const legacySlug = 'legacy-project-slug';
11
+ const knowledgePath = path.join(dataDir, 'knowledge', legacySlug);
12
+ const metadataPath = path.join(dataDir, 'projects', projectId);
13
+ const now = new Date().toISOString();
14
+ process.env.KB_DATA_DIR = dataDir;
15
+ require('../lib/data-dir')._resetCache();
16
+
17
+ const { defaultProjectConfig, defaultProjectState } = require('../lib/project-store');
18
+
19
+ function writeRecord(root, record) {
20
+ fs.mkdirSync(root, { recursive: true });
21
+ fs.writeFileSync(path.join(root, `${record.sessionId}.json`), `${JSON.stringify({
22
+ schema: 'claude-workbench-session/v1',
23
+ projectPath: dataDir,
24
+ kbPath: knowledgePath,
25
+ promptKey: 'post-commit-automation',
26
+ runner: 'sdk',
27
+ state: 'idle',
28
+ model: 'test-model',
29
+ startedAt: now,
30
+ endedAt: now,
31
+ exitCode: 0,
32
+ turns: 1,
33
+ pendingPermission: null,
34
+ updatedAt: now,
35
+ ...record,
36
+ }, null, 2)}\n`, 'utf8');
37
+ }
38
+
39
+ try {
40
+ fs.mkdirSync(metadataPath, { recursive: true });
41
+ fs.mkdirSync(knowledgePath, { recursive: true });
42
+ fs.writeFileSync(path.join(metadataPath, 'config.json'), `${JSON.stringify(defaultProjectConfig(projectId, {
43
+ displayName: 'Legacy project',
44
+ storageName: 'legacy-project',
45
+ repoPath: dataDir,
46
+ knowledgePath,
47
+ legacyExtensions: { slug: legacySlug, sourceSchema: 'legacy-v1' },
48
+ }), null, 2)}\n`, 'utf8');
49
+ fs.writeFileSync(path.join(metadataPath, 'state.json'), `${JSON.stringify(defaultProjectState(), null, 2)}\n`, 'utf8');
50
+ fs.writeFileSync(path.join(dataDir, 'projects.json'), `${JSON.stringify({
51
+ schema: 'project-registry/v2',
52
+ schemaVersion: 2,
53
+ projectOrder: [projectId],
54
+ projects: { [projectId]: { createdAt: now, displayNameSnapshot: 'Legacy project' } },
55
+ revision: 1,
56
+ updatedAt: now,
57
+ }, null, 2)}\n`, 'utf8');
58
+
59
+ const legacyRoot = path.join(dataDir, '_ai', legacySlug, 'claude-workbench');
60
+ const currentRoot = path.join(dataDir, 'runtime', 'claude-sessions', projectId);
61
+ writeRecord(legacyRoot, {
62
+ sessionId: 'sess-legacy-only',
63
+ projectSlug: legacySlug,
64
+ source: 'git-hook',
65
+ automation: true,
66
+ events: [
67
+ { type: 'claude/user-prompt', text: 'historical hook prompt' },
68
+ { type: 'claude/result', result: 'historical analysis result' },
69
+ ],
70
+ });
71
+ writeRecord(legacyRoot, {
72
+ sessionId: 'sess-duplicate',
73
+ projectSlug: legacySlug,
74
+ source: 'legacy-copy',
75
+ events: [{ type: 'claude/result', result: 'legacy duplicate' }],
76
+ });
77
+ writeRecord(currentRoot, {
78
+ sessionId: 'sess-duplicate',
79
+ projectSlug: projectId,
80
+ source: 'current-runtime',
81
+ events: [{ type: 'claude/result', result: 'current duplicate' }],
82
+ });
83
+
84
+ const runner = require('../lib/claude-cli-runner');
85
+ const listed = runner.listSessions({ projectSlug: projectId });
86
+ assert.strictEqual(listed.length, 2);
87
+ const legacy = listed.find(session => session.sessionId === 'sess-legacy-only');
88
+ assert(legacy, 'legacy alias session must be discoverable under the stable projectId');
89
+ assert.strictEqual(legacy.projectSlug, projectId);
90
+ assert.strictEqual(legacy.legacyProjectSlug, legacySlug);
91
+ assert.strictEqual(legacy.legacySource, true);
92
+ assert.strictEqual(listed.find(session => session.sessionId === 'sess-duplicate').source, 'current-runtime', 'canonical runtime record must win duplicate IDs');
93
+
94
+ const events = [];
95
+ const unsubscribe = runner.subscribe('sess-legacy-only', event => events.push(event));
96
+ unsubscribe();
97
+ assert(events.some(event => event.type === 'claude/user-prompt' && event.text === 'historical hook prompt'));
98
+ assert(events.some(event => event.type === 'claude/result' && event.result === 'historical analysis result'));
99
+ assert.strictEqual(runner.getState('sess-legacy-only').projectSlug, projectId);
100
+ console.log('legacy-claude-session-alias-test PASS');
101
+ } catch (error) {
102
+ console.error(error.stack || error.message);
103
+ process.exitCode = 1;
104
+ } finally {
105
+ fs.rmSync(dataDir, { recursive: true, force: true });
106
+ }
@@ -1,47 +1,47 @@
1
- const assert = require('assert');
2
- const fs = require('fs');
3
- const os = require('os');
4
- const path = require('path');
5
- const { spawnServer } = require('./helpers/spawn-server');
6
-
7
- const ROOT = path.resolve(__dirname, '..', '..');
8
- const PORT = 7833;
9
- const BASE = `http://127.0.0.1:${PORT}`;
10
- const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pkb-routes-v2-'));
11
-
12
- async function waitForServer() {
13
- for (let attempt = 0; attempt < 100; attempt += 1) {
14
- try { if ((await fetch(`${BASE}/api/health`)).ok) return; } catch {}
15
- await new Promise(resolve => setTimeout(resolve, 100));
16
- }
17
- throw new Error('server did not start');
18
- }
19
-
20
- (async () => {
21
- const source = `${fs.readFileSync(path.join(ROOT, '_site', 'server.js'), 'utf8')}\n${fs.readFileSync(path.join(ROOT, '_site', 'lib', 'server-app.js'), 'utf8')}`;
22
- for (const removedSymbol of ['automation/simulate', 'automation/init', 'hook-install', 'hook-uninstall', 'dispatchProjectInit', 'DEFAULT_INIT_PROMPT_TEMPLATE', 'renderProjectInitPrompt', '/api/raw']) {
23
- assert(!source.includes(removedSymbol), `removed production symbol remains: ${removedSymbol}`);
24
- }
25
-
26
- const spawned = spawnServer({ root: ROOT, port: PORT, dataDir, tag: 'routes-v2', extraEnv: { KB_SKIP_MIGRATION: '1' } });
27
- try {
28
- await waitForServer();
29
- const routes = [
30
- ['PUT', '/api/projects'],
31
- ['POST', '/api/projects/project-example/init'],
32
- ['POST', '/api/projects/project-example/automation/simulate'],
33
- ['POST', '/api/projects/project-example/automation/init'],
34
- ['GET', '/api/projects/project-example/hook-status'],
35
- ['POST', '/api/projects/project-example/hook-install'],
36
- ['POST', '/api/projects/project-example/hook-uninstall'],
37
- ['GET', '/api/raw?path=README.md'],
38
- ];
39
- for (const [method, pathname] of routes) {
40
- const response = await fetch(`${BASE}${pathname}`, { method });
41
- assert.strictEqual(response.status, 404, `${method} ${pathname} should be removed`);
42
- }
43
- console.log('legacy-routes-removed-test PASS');
44
- } finally {
45
- await spawned.cleanup();
46
- }
47
- })().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 { spawnServer } = require('./helpers/spawn-server');
6
+
7
+ const ROOT = path.resolve(__dirname, '..', '..');
8
+ const PORT = 7833;
9
+ const BASE = `http://127.0.0.1:${PORT}`;
10
+ const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pkb-routes-v2-'));
11
+
12
+ async function waitForServer() {
13
+ for (let attempt = 0; attempt < 100; attempt += 1) {
14
+ try { if ((await fetch(`${BASE}/api/health`)).ok) return; } catch {}
15
+ await new Promise(resolve => setTimeout(resolve, 100));
16
+ }
17
+ throw new Error('server did not start');
18
+ }
19
+
20
+ (async () => {
21
+ const source = `${fs.readFileSync(path.join(ROOT, '_site', 'server.js'), 'utf8')}\n${fs.readFileSync(path.join(ROOT, '_site', 'lib', 'server-app.js'), 'utf8')}`;
22
+ for (const removedSymbol of ['automation/simulate', 'automation/init', 'hook-install', 'hook-uninstall', 'dispatchProjectInit', 'DEFAULT_INIT_PROMPT_TEMPLATE', 'renderProjectInitPrompt', '/api/raw']) {
23
+ assert(!source.includes(removedSymbol), `removed production symbol remains: ${removedSymbol}`);
24
+ }
25
+
26
+ const spawned = spawnServer({ root: ROOT, port: PORT, dataDir, tag: 'routes-v2', extraEnv: { KB_SKIP_MIGRATION: '1' } });
27
+ try {
28
+ await waitForServer();
29
+ const routes = [
30
+ ['PUT', '/api/projects'],
31
+ ['POST', '/api/projects/project-example/init'],
32
+ ['POST', '/api/projects/project-example/automation/simulate'],
33
+ ['POST', '/api/projects/project-example/automation/init'],
34
+ ['GET', '/api/projects/project-example/hook-status'],
35
+ ['POST', '/api/projects/project-example/hook-install'],
36
+ ['POST', '/api/projects/project-example/hook-uninstall'],
37
+ ['GET', '/api/raw?path=README.md'],
38
+ ];
39
+ for (const [method, pathname] of routes) {
40
+ const response = await fetch(`${BASE}${pathname}`, { method });
41
+ assert.strictEqual(response.status, 404, `${method} ${pathname} should be removed`);
42
+ }
43
+ console.log('legacy-routes-removed-test PASS');
44
+ } finally {
45
+ await spawned.cleanup();
46
+ }
47
+ })().catch(error => { console.error(error); process.exitCode = 1; });