@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,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  const packageInfo = require('../package.json');
4
4
  const { Server } = require('@modelcontextprotocol/sdk/server/index.js');
@@ -11,7 +11,7 @@ const { KnowledgeToolRuntime } = require('../_site/lib/knowledge-tool-runtime');
11
11
  const { serializeErrorEnvelope, createId } = require('../_site/lib/contracts');
12
12
 
13
13
  const SERVER_NAME = 'project-knowledge';
14
- const SCOPE = { type: 'string', enum: ['project', 'domain', 'brain'], description: 'Knowledge range: current project (default), its domain, or its brain.' };
14
+ const SCOPE = { type: 'string', enum: ['project', 'domain', 'brain'], description: 'Knowledge range: current project (default), its domain, or its brain.' };
15
15
  const SERVER_INSTRUCTIONS = `Project Knowledge is the durable source of prior project decisions and implementation history. Knowledge content is read-only; the only write operation appends an explicit supplemental Prompt event to the shared conversation truth.
16
16
 
17
17
  At the start of work in a registered Git repository, and before answering questions about prior work or implementing a non-trivial change:
@@ -57,7 +57,7 @@ const TOOLS = [
57
57
  },
58
58
  {
59
59
  name: 'project_knowledge_search',
60
- description: 'Search the same durable knowledge and commit citations as the product UI, within the selected project, domain or brain. This tool is read-only.',
60
+ description: 'Search the same durable knowledge and commit citations as the product UI, within the selected project, domain or brain. This tool is read-only.',
61
61
  inputSchema: {
62
62
  type: 'object',
63
63
  required: ['query'],
@@ -67,7 +67,7 @@ const TOOLS = [
67
67
  projectId: { type: 'string', description: 'Stable projectId returned by project_knowledge_resolve.' },
68
68
  project: { type: 'string', description: 'Optional registered project slug.' },
69
69
  limit: { type: 'integer', minimum: 1, maximum: 30, default: 8 },
70
- scope: SCOPE,
70
+ scope: SCOPE,
71
71
  },
72
72
  additionalProperties: false,
73
73
  },
@@ -84,7 +84,7 @@ const TOOLS = [
84
84
  projectId: { type: 'string' },
85
85
  project: { type: 'string' },
86
86
  limit: { type: 'integer', minimum: 1, maximum: 30, default: 8 },
87
- scope: SCOPE,
87
+ scope: SCOPE,
88
88
  },
89
89
  additionalProperties: false,
90
90
  },
@@ -98,8 +98,8 @@ const TOOLS = [
98
98
  properties: {
99
99
  entry: { type: 'string', minLength: 1, description: 'Entry identifier returned by search.' },
100
100
  space: { type: 'string', description: 'Optional scoped knowledge space identifier.' },
101
- spaceId: { type: 'string', description: 'Knowledge space identifier returned by a citation; alias of space.' },
102
- scope: SCOPE,
101
+ spaceId: { type: 'string', description: 'Knowledge space identifier returned by a citation; alias of space.' },
102
+ scope: SCOPE,
103
103
  repoPath: { type: 'string' },
104
104
  projectId: { type: 'string' },
105
105
  project: { type: 'string' },
@@ -117,7 +117,7 @@ const TOOLS = [
117
117
  projectId: { type: 'string' },
118
118
  project: { type: 'string' },
119
119
  limit: { type: 'integer', minimum: 1, maximum: 100, default: 20 },
120
- scope: SCOPE,
120
+ scope: SCOPE,
121
121
  },
122
122
  additionalProperties: false,
123
123
  },
@@ -1,170 +1,170 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
- const path = require('node:path');
4
- const fs = require('node:fs');
5
- const net = require('node:net');
6
- const { spawn, spawnSync, exec } = require('node:child_process');
7
- const { resolveDataDirPath } = require('../_site/lib/data-dir');
8
- const endpoint = require('../_site/lib/runtime-endpoint');
9
- const instances = require('../_site/lib/cli-instances');
10
- const pkg = require('../package.json');
11
-
12
- function help() {
13
- console.log(`project-knowledge ${pkg.version}
14
-
15
- Usage:
16
- project-knowledge [start] Start in background and open the UI
17
- project-knowledge --fg Start in foreground
18
- project-knowledge stop Stop this data directory and its owned modules
19
- project-knowledge status Show this data directory's instance
20
- project-knowledge status --all List controllable local knowledge instances
21
- project-knowledge stop --port 5804
22
- Stop the verified instance on this port
23
- project-knowledge install|update|uninstall|integrations
24
- Manage coding-agent integrations
25
-
26
- Options:
27
- -p, --port <port> Port (default: 5757, startup auto-fallback +19)
28
- --data <dir> Select data directory (default: ~/.project-knowledge-v5)
29
- --host <host> Bind host (default: 127.0.0.1)
30
- --no-open Do not open browser
31
- --fg Run in foreground
32
- -v, --version Print version
33
- -h, --help Show help
34
-
35
- Commands are case-insensitive. Unknown commands never start a service.`);
36
- }
37
- function parse(args) {
38
- const commands = new Set(['start', 'stop', 'status', 'install', 'update', 'uninstall', 'integrations']);
39
- let command = 'start';
40
- if (args[0] && !args[0].startsWith('-')) {
41
- command = args.shift().toLowerCase();
42
- if (!commands.has(command)) throw new Error(`Unknown command: ${command}. Run project-knowledge --help.`);
43
- }
44
- if (['install', 'update', 'uninstall', 'integrations'].includes(command)) return { command, forwarded: args };
45
- const options = { command, port: Number(process.env.KB_SITE_PORT || 5757), host: '127.0.0.1', open: true };
46
- for (let i = 0; i < args.length; i++) {
47
- const arg = args[i];
48
- if (arg === '--help' || arg === '-h') options.help = true;
49
- else if (arg === '--version' || arg === '-v') options.version = true;
50
- else if (['--port', '-p', '--host', '--data'].includes(arg)) {
51
- const value = args[++i];
52
- if (!value || value.startsWith('-')) throw new Error(`Missing value for ${arg}.`);
53
- if (arg === '--port' || arg === '-p') { options.port = Number(value); options.portExplicit = true; }
54
- else if (arg === '--data') options.dataDir = value;
55
- else options.host = value;
56
- } else if (arg === '--no-open') options.open = false;
57
- else if (arg === '--fg') options.foreground = true;
58
- else if (arg === '--all') options.all = true;
59
- else throw new Error(`${arg.startsWith('-') ? 'Unknown option' : 'Unexpected argument'}: ${arg}.`);
60
- }
61
- if (!Number.isInteger(options.port) || options.port < 1 || options.port > 65535) throw new Error('Port must be an integer from 1 to 65535.');
62
- if (options.all && (command !== 'status' || options.portExplicit)) throw new Error('--all is only supported by status, without --port.');
63
- if (command !== 'start' && (options.foreground || !options.open || options.host !== '127.0.0.1')) throw new Error('Startup options cannot be used with stop/status.');
64
- return options;
65
- }
66
- function openBrowser(url) {
67
- const command = process.platform === 'win32' ? `start "" "${url}"` : process.platform === 'darwin' ? `open "${url}"` : `xdg-open "${url}"`;
68
- exec(command, () => {});
69
- }
70
- function notify(message) { if (process.connected) process.send(message, () => {}); }
71
- function waitForStartup(child, log) {
72
- return new Promise((resolve, reject) => {
73
- const finish = (error, value) => {
74
- clearTimeout(timer); child.removeAllListeners('message'); child.removeAllListeners('exit');
75
- if (child.connected) child.disconnect();
76
- if (error) reject(error); else resolve(value);
77
- };
78
- const timer = setTimeout(() => finish(new Error(`启动尚未完成,请运行 project-knowledge status 并查看 ${log}`)), 45000);
79
- child.on('message', message => {
80
- if (message?.type === 'kb:launcher-ready') finish(null, message);
81
- else if (message?.type === 'kb:launcher-failed') finish(new Error(message.message));
82
- });
83
- child.once('error', error => finish(error));
84
- child.once('exit', code => finish(new Error(`后台进程在启动完成前退出(${code}),请查看 ${log}`)));
85
- });
86
- }
87
- function portFree(port, host) {
88
- return new Promise(resolve => {
89
- const tester = net.createServer();
90
- let done = false;
91
- const timer = setTimeout(() => finish(false), 3000);
92
- function finish(free) {
93
- if (done) return; done = true; clearTimeout(timer); tester.close(() => resolve(free));
94
- }
95
- tester.once('error', () => finish(false)); tester.once('listening', () => finish(true)); tester.listen(port, host);
96
- });
97
- }
98
- async function freePort(port, host) {
99
- for (let offset = 0; offset < 20 && port + offset <= 65535; offset++) if (await portFree(port + offset, host)) return port + offset;
100
- throw new Error(`No free port found near ${port}.`);
101
- }
102
- function show(info) { console.log(`project-knowledge is ${info.status === 'stopping' ? 'stopping' : 'running'} (PID ${info.pid}) at ${instances.url(info)} [${info.version}]\nData dir: ${info.dataDir}`); }
103
-
104
- async function main() {
105
- const options = parse(process.argv.slice(2));
106
- if (options.help) { help(); return; }
107
- if (options.version) { console.log(pkg.version); return; }
108
- if (options.forwarded) {
109
- const args = options.command === 'integrations' ? (options.forwarded.length ? options.forwarded : ['status']) : [options.command, ...options.forwarded];
110
- const result = spawnSync(process.execPath, [path.join(__dirname, 'project-knowledge-integrations.js'), ...args], { stdio: 'inherit', windowsHide: true });
111
- process.exitCode = result.status ?? 1; return;
112
- }
113
- const dataDir = resolveDataDirPath({ dataDir: options.dataDir });
114
- process.env.KB_DATA_DIR = dataDir;
115
- if (options.all) {
116
- const found = await instances.all();
117
- if (!found.length) console.log('No controllable project-knowledge instances found.');
118
- for (const info of found) show(info);
119
- return;
120
- }
121
- const existing = options.command !== 'start' && options.portExplicit ? await instances.atPort(options.port) : await instances.current(dataDir);
122
- const unverified = !existing && endpoint.readLiveEndpoint(dataDir, { removeStale: false });
123
- if (unverified) throw new Error(`数据目录已有实例记录(PID ${unverified.pid},端口 ${unverified.port}),但控制通道不可用。请先确认该实例,不会重复启动、误停止或报告已停止。`);
124
- if (existing && options.dataDir && options.command !== 'start' && path.resolve(existing.dataDir).toLowerCase() !== dataDir.toLowerCase()) throw new Error('指定端口不属于指定数据目录,已拒绝控制。');
125
- if (options.command === 'status') {
126
- if (existing) show(existing);
127
- else console.log(`project-knowledge is not running for ${dataDir}.\nUse "project-knowledge status --all" to inspect other data directories.`);
128
- return;
129
- }
130
- if (options.command === 'stop') {
131
- if (!existing) { console.log(`No background process found for ${dataDir}.`); return; }
132
- await instances.stop(existing);
133
- console.log(`project-knowledge stopped (PID ${existing.pid}) at ${instances.url(existing)}.\nData dir: ${existing.dataDir}`);
134
- return;
135
- }
136
- if (existing) {
137
- if (existing.status === 'stopping') throw new Error('实例正在停止,请稍后再启动。');
138
- const url = instances.url(existing); console.log(`Already running (PID ${existing.pid}) at ${url}\nData dir: ${existing.dataDir}`);
139
- notify({ type: 'kb:launcher-ready', url }); if (options.open) openBrowser(url); return;
140
- }
141
- // A live claim with no control channel is starting, legacy, or unverified.
142
- // Never trust a reused/shared PID as proof or kill it to make room.
143
- if (!options.foreground) {
144
- fs.mkdirSync(dataDir, { recursive: true });
145
- const log = path.join(dataDir, 'launcher.log'), fd = fs.openSync(log, 'a');
146
- const child = spawn(process.execPath, [...process.argv.slice(1), '--fg'], { detached: true, stdio: ['ignore', fd, fd, 'ipc'], windowsHide: true });
147
- fs.closeSync(fd); child.unref();
148
- console.log(`project-knowledge starting in background\nDiagnostics: ${log}`);
149
- const ready = await waitForStartup(child, log);
150
- console.log(`project-knowledge is ready at ${ready.url}\nUse "project-knowledge status" to check, "project-knowledge stop" to stop.`);
151
- return;
152
- }
153
- const port = options.portExplicit ? options.port : await freePort(options.port, options.host);
154
- process.env.KB_SITE_HOST = options.host; process.env.KB_SITE_PORT = String(port);
155
- console.log(`project-knowledge ${pkg.version}\nData dir: ${dataDir}`);
156
- if (port !== options.port) console.log(`(Port ${options.port} was busy, using ${port} instead)`);
157
- const { startServer, installProcessHandlers } = require('../_site/lib/server-app');
158
- const promise = startServer(); installProcessHandlers(promise);
159
- const instance = await promise;
160
- if (process.platform === 'win32') process.once('SIGBREAK', () => instance.stop('SIGBREAK'));
161
- const url = instances.url({ host: options.host, port });
162
- console.log(`Listening at ${url}`); notify({ type: 'kb:launcher-ready', url });
163
- if (options.open) setTimeout(() => openBrowser(url), 500);
164
- }
165
- main().catch(error => {
166
- const control = ['stop', 'status'].includes(process.argv[2]?.toLowerCase());
167
- console.error(process.argv.includes('--fg') ? `[project-knowledge] startup failed: ${error.message}` : `${control ? '控制失败' : '启动失败'}:${error.message}`);
168
- notify({ type: 'kb:launcher-failed', message: error.message });
169
- process.exitCode = 1;
170
- });
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ const path = require('node:path');
4
+ const fs = require('node:fs');
5
+ const net = require('node:net');
6
+ const { spawn, spawnSync, exec } = require('node:child_process');
7
+ const { resolveDataDirPath } = require('../_site/lib/data-dir');
8
+ const endpoint = require('../_site/lib/runtime-endpoint');
9
+ const instances = require('../_site/lib/cli-instances');
10
+ const pkg = require('../package.json');
11
+
12
+ function help() {
13
+ console.log(`project-knowledge ${pkg.version}
14
+
15
+ Usage:
16
+ project-knowledge [start] Start in background and open the UI
17
+ project-knowledge --fg Start in foreground
18
+ project-knowledge stop Stop this data directory and its owned modules
19
+ project-knowledge status Show this data directory's instance
20
+ project-knowledge status --all List controllable local knowledge instances
21
+ project-knowledge stop --port 5804
22
+ Stop the verified instance on this port
23
+ project-knowledge install|update|uninstall|integrations
24
+ Manage coding-agent integrations
25
+
26
+ Options:
27
+ -p, --port <port> Port (default: 5757, startup auto-fallback +19)
28
+ --data <dir> Select data directory (default: ~/.project-knowledge-v5)
29
+ --host <host> Bind host (default: 127.0.0.1)
30
+ --no-open Do not open browser
31
+ --fg Run in foreground
32
+ -v, --version Print version
33
+ -h, --help Show help
34
+
35
+ Commands are case-insensitive. Unknown commands never start a service.`);
36
+ }
37
+ function parse(args) {
38
+ const commands = new Set(['start', 'stop', 'status', 'install', 'update', 'uninstall', 'integrations']);
39
+ let command = 'start';
40
+ if (args[0] && !args[0].startsWith('-')) {
41
+ command = args.shift().toLowerCase();
42
+ if (!commands.has(command)) throw new Error(`Unknown command: ${command}. Run project-knowledge --help.`);
43
+ }
44
+ if (['install', 'update', 'uninstall', 'integrations'].includes(command)) return { command, forwarded: args };
45
+ const options = { command, port: Number(process.env.KB_SITE_PORT || 5757), host: '127.0.0.1', open: true };
46
+ for (let i = 0; i < args.length; i++) {
47
+ const arg = args[i];
48
+ if (arg === '--help' || arg === '-h') options.help = true;
49
+ else if (arg === '--version' || arg === '-v') options.version = true;
50
+ else if (['--port', '-p', '--host', '--data'].includes(arg)) {
51
+ const value = args[++i];
52
+ if (!value || value.startsWith('-')) throw new Error(`Missing value for ${arg}.`);
53
+ if (arg === '--port' || arg === '-p') { options.port = Number(value); options.portExplicit = true; }
54
+ else if (arg === '--data') options.dataDir = value;
55
+ else options.host = value;
56
+ } else if (arg === '--no-open') options.open = false;
57
+ else if (arg === '--fg') options.foreground = true;
58
+ else if (arg === '--all') options.all = true;
59
+ else throw new Error(`${arg.startsWith('-') ? 'Unknown option' : 'Unexpected argument'}: ${arg}.`);
60
+ }
61
+ if (!Number.isInteger(options.port) || options.port < 1 || options.port > 65535) throw new Error('Port must be an integer from 1 to 65535.');
62
+ if (options.all && (command !== 'status' || options.portExplicit)) throw new Error('--all is only supported by status, without --port.');
63
+ if (command !== 'start' && (options.foreground || !options.open || options.host !== '127.0.0.1')) throw new Error('Startup options cannot be used with stop/status.');
64
+ return options;
65
+ }
66
+ function openBrowser(url) {
67
+ const command = process.platform === 'win32' ? `start "" "${url}"` : process.platform === 'darwin' ? `open "${url}"` : `xdg-open "${url}"`;
68
+ exec(command, () => {});
69
+ }
70
+ function notify(message) { if (process.connected) process.send(message, () => {}); }
71
+ function waitForStartup(child, log) {
72
+ return new Promise((resolve, reject) => {
73
+ const finish = (error, value) => {
74
+ clearTimeout(timer); child.removeAllListeners('message'); child.removeAllListeners('exit');
75
+ if (child.connected) child.disconnect();
76
+ if (error) reject(error); else resolve(value);
77
+ };
78
+ const timer = setTimeout(() => finish(new Error(`启动尚未完成,请运行 project-knowledge status 并查看 ${log}`)), 45000);
79
+ child.on('message', message => {
80
+ if (message?.type === 'kb:launcher-ready') finish(null, message);
81
+ else if (message?.type === 'kb:launcher-failed') finish(new Error(message.message));
82
+ });
83
+ child.once('error', error => finish(error));
84
+ child.once('exit', code => finish(new Error(`后台进程在启动完成前退出(${code}),请查看 ${log}`)));
85
+ });
86
+ }
87
+ function portFree(port, host) {
88
+ return new Promise(resolve => {
89
+ const tester = net.createServer();
90
+ let done = false;
91
+ const timer = setTimeout(() => finish(false), 3000);
92
+ function finish(free) {
93
+ if (done) return; done = true; clearTimeout(timer); tester.close(() => resolve(free));
94
+ }
95
+ tester.once('error', () => finish(false)); tester.once('listening', () => finish(true)); tester.listen(port, host);
96
+ });
97
+ }
98
+ async function freePort(port, host) {
99
+ for (let offset = 0; offset < 20 && port + offset <= 65535; offset++) if (await portFree(port + offset, host)) return port + offset;
100
+ throw new Error(`No free port found near ${port}.`);
101
+ }
102
+ function show(info) { console.log(`project-knowledge is ${info.status === 'stopping' ? 'stopping' : 'running'} (PID ${info.pid}) at ${instances.url(info)} [${info.version}]\nData dir: ${info.dataDir}`); }
103
+
104
+ async function main() {
105
+ const options = parse(process.argv.slice(2));
106
+ if (options.help) { help(); return; }
107
+ if (options.version) { console.log(pkg.version); return; }
108
+ if (options.forwarded) {
109
+ const args = options.command === 'integrations' ? (options.forwarded.length ? options.forwarded : ['status']) : [options.command, ...options.forwarded];
110
+ const result = spawnSync(process.execPath, [path.join(__dirname, 'project-knowledge-integrations.js'), ...args], { stdio: 'inherit', windowsHide: true });
111
+ process.exitCode = result.status ?? 1; return;
112
+ }
113
+ const dataDir = resolveDataDirPath({ dataDir: options.dataDir });
114
+ process.env.KB_DATA_DIR = dataDir;
115
+ if (options.all) {
116
+ const found = await instances.all();
117
+ if (!found.length) console.log('No controllable project-knowledge instances found.');
118
+ for (const info of found) show(info);
119
+ return;
120
+ }
121
+ const existing = options.command !== 'start' && options.portExplicit ? await instances.atPort(options.port) : await instances.current(dataDir);
122
+ const unverified = !existing && endpoint.readLiveEndpoint(dataDir, { removeStale: false });
123
+ if (unverified) throw new Error(`数据目录已有实例记录(PID ${unverified.pid},端口 ${unverified.port}),但控制通道不可用。请先确认该实例,不会重复启动、误停止或报告已停止。`);
124
+ if (existing && options.dataDir && options.command !== 'start' && path.resolve(existing.dataDir).toLowerCase() !== dataDir.toLowerCase()) throw new Error('指定端口不属于指定数据目录,已拒绝控制。');
125
+ if (options.command === 'status') {
126
+ if (existing) show(existing);
127
+ else console.log(`project-knowledge is not running for ${dataDir}.\nUse "project-knowledge status --all" to inspect other data directories.`);
128
+ return;
129
+ }
130
+ if (options.command === 'stop') {
131
+ if (!existing) { console.log(`No background process found for ${dataDir}.`); return; }
132
+ await instances.stop(existing);
133
+ console.log(`project-knowledge stopped (PID ${existing.pid}) at ${instances.url(existing)}.\nData dir: ${existing.dataDir}`);
134
+ return;
135
+ }
136
+ if (existing) {
137
+ if (existing.status === 'stopping') throw new Error('实例正在停止,请稍后再启动。');
138
+ const url = instances.url(existing); console.log(`Already running (PID ${existing.pid}) at ${url}\nData dir: ${existing.dataDir}`);
139
+ notify({ type: 'kb:launcher-ready', url }); if (options.open) openBrowser(url); return;
140
+ }
141
+ // A live claim with no control channel is starting, legacy, or unverified.
142
+ // Never trust a reused/shared PID as proof or kill it to make room.
143
+ if (!options.foreground) {
144
+ fs.mkdirSync(dataDir, { recursive: true });
145
+ const log = path.join(dataDir, 'launcher.log'), fd = fs.openSync(log, 'a');
146
+ const child = spawn(process.execPath, [...process.argv.slice(1), '--fg'], { detached: true, stdio: ['ignore', fd, fd, 'ipc'], windowsHide: true });
147
+ fs.closeSync(fd); child.unref();
148
+ console.log(`project-knowledge starting in background\nDiagnostics: ${log}`);
149
+ const ready = await waitForStartup(child, log);
150
+ console.log(`project-knowledge is ready at ${ready.url}\nUse "project-knowledge status" to check, "project-knowledge stop" to stop.`);
151
+ return;
152
+ }
153
+ const port = options.portExplicit ? options.port : await freePort(options.port, options.host);
154
+ process.env.KB_SITE_HOST = options.host; process.env.KB_SITE_PORT = String(port);
155
+ console.log(`project-knowledge ${pkg.version}\nData dir: ${dataDir}`);
156
+ if (port !== options.port) console.log(`(Port ${options.port} was busy, using ${port} instead)`);
157
+ const { startServer, installProcessHandlers } = require('../_site/lib/server-app');
158
+ const promise = startServer(); installProcessHandlers(promise);
159
+ const instance = await promise;
160
+ if (process.platform === 'win32') process.once('SIGBREAK', () => instance.stop('SIGBREAK'));
161
+ const url = instances.url({ host: options.host, port });
162
+ console.log(`Listening at ${url}`); notify({ type: 'kb:launcher-ready', url });
163
+ if (options.open) setTimeout(() => openBrowser(url), 500);
164
+ }
165
+ main().catch(error => {
166
+ const control = ['stop', 'status'].includes(process.argv[2]?.toLowerCase());
167
+ console.error(process.argv.includes('--fg') ? `[project-knowledge] startup failed: ${error.message}` : `${control ? '控制失败' : '启动失败'}:${error.message}`);
168
+ notify({ type: 'kb:launcher-failed', message: error.message });
169
+ process.exitCode = 1;
170
+ });
@@ -1,21 +1,21 @@
1
- {
2
- "schema": "claude-prompts/v1",
3
- "prompts": {
4
- "initial-analysis": {
5
- "description": "Initial project analysis. Claude runs from the KB workspace and reads the source project via PROJECT_PATH.",
6
- "model": "sonnet",
7
- "permissionMode": "bypassPermissions",
8
- "allowedTools": ["Read", "Grep", "Glob"],
9
- "systemPrompt": "You are an expert project analyst building a knowledge base. The current working directory is the knowledge-base workspace, not the source project. Read source code from PROJECT_PATH and treat KB_PATH as the write target for knowledge-base drafts and updates. Output STRICT JSON only: no prose and no markdown fences. The JSON must match this shape:\n\n{\n \"goalDraft\": {\n \"oneLine\": \"one-sentence description of what this project is\",\n \"positioning\": \"paragraph describing the project's role and core value\",\n \"coreValue\": [\"bullet points of core value to developers / teams\"],\n \"painPoints\": [{\"pain\": \"...\", \"legacyApproach\": \"...\", \"projectApproach\": \"...\"}]\n },\n \"analysisDraft\": {\n \"framework\": \"primary framework / language / stack\",\n \"modules\": [{\"name\": \"...\", \"responsibility\": \"...\", \"keyFiles\": [\"path/to/file\"]}],\n \"architecture\": \"paragraph describing the architecture and data flow\",\n \"evidence\": [{\"claim\": \"...\", \"file\": \"path:line\"}]\n },\n \"features\": [{\"slug\": \"kebab-case-slug\", \"name\": \"...\", \"summary\": \"...\"}]\n}\n\nUse the Read, Grep, and Glob tools to investigate PROJECT_PATH. Cite real source file paths in evidence. Do not invent modules that do not exist. Do not modify the source project unless the user explicitly asks for source changes.",
10
- "userPrompt": "Analyze the source project at PROJECT_PATH. The current working directory is KB_PATH.\n\nProject context:\n- Slug: {{SLUG}}\n- Source project path: {{PROJECT_PATH}}\n- Knowledge-base path: {{KB_PATH}}\n- Primary language: {{PRIMARY_LANGUAGE}}\n- Knowledge language: {{KNOWLEDGE_LANGUAGE}}\n\nTasks:\n1. Identify the framework, main tech stack, and language distribution.\n2. List the core modules / top-level directories and their responsibilities.\n3. Describe the architecture and data flow at a high level.\n4. Identify 3-6 user-facing features.\n5. Capture 4-8 evidence-backed claims with file:line citations.\n\nWrite the knowledge-base content in {{KNOWLEDGE_LANGUAGE}} where natural, but keep code identifiers, file paths, and JSON keys in English.\n\nReturn ONLY the JSON object described in the system prompt."
11
- },
12
- "commit-analysis": {
13
- "description": "Batch analysis of one or more commits. Claude runs from the KB workspace and reads the source project via PROJECT_PATH.",
14
- "model": "sonnet",
15
- "permissionMode": "bypassPermissions",
16
- "allowedTools": ["Read", "Grep", "Glob", "Bash"],
17
- "systemPrompt": "You are a commit analyst building a knowledge base. The current working directory is the knowledge-base workspace, not the source project. Read source code from PROJECT_PATH and treat KB_PATH as the write target for knowledge-base drafts and updates. For each commit provided, output a structured change description. STRICT JSON only: no prose.\n\nOutput shape:\n{\n \"changes\": [\n {\n \"hash\": \"<full-or-short-hash>\",\n \"subject\": \"<original commit subject>\",\n \"type\": \"feat|fix|refactor|docs|test|chore|infra\",\n \"classification\": \"feature|bugfix|infrastructure|documentation|test\",\n \"summary\": \"one-paragraph description of what changed and why\",\n \"files\": [\"paths/touched\"],\n \"evidence\": [{\"claim\": \"...\", \"file\": \"path:line\"}]\n }\n ]\n}\n\nUse git commands against PROJECT_PATH and Read for inspecting changed files. Cite real source file paths. Do not modify the source project unless the user explicitly asks for source changes.",
18
- "userPrompt": "Analyze the following commits in the source project at PROJECT_PATH. The current working directory is KB_PATH.\n\nProject: {{SLUG}} ({{PROJECT_PATH}})\nKnowledge-base path: {{KB_PATH}}\nCommit range: {{COMMIT_RANGE}}\nCommits to analyze:\n{{COMMIT_HASHES}}\n\nFor each commit, produce one entry in the changes array. Use git commands against {{PROJECT_PATH}} and Read for inspecting changed source files. Summarize in {{KNOWLEDGE_LANGUAGE}}; keep identifiers and paths in English.\n\nReturn ONLY the JSON object."
19
- }
20
- }
21
- }
1
+ {
2
+ "schema": "claude-prompts/v1",
3
+ "prompts": {
4
+ "initial-analysis": {
5
+ "description": "Initial project analysis. Claude runs from the KB workspace and reads the source project via PROJECT_PATH.",
6
+ "model": "sonnet",
7
+ "permissionMode": "bypassPermissions",
8
+ "allowedTools": ["Read", "Grep", "Glob"],
9
+ "systemPrompt": "You are an expert project analyst building a knowledge base. The current working directory is the knowledge-base workspace, not the source project. Read source code from PROJECT_PATH and treat KB_PATH as the write target for knowledge-base drafts and updates. Output STRICT JSON only: no prose and no markdown fences. The JSON must match this shape:\n\n{\n \"goalDraft\": {\n \"oneLine\": \"one-sentence description of what this project is\",\n \"positioning\": \"paragraph describing the project's role and core value\",\n \"coreValue\": [\"bullet points of core value to developers / teams\"],\n \"painPoints\": [{\"pain\": \"...\", \"legacyApproach\": \"...\", \"projectApproach\": \"...\"}]\n },\n \"analysisDraft\": {\n \"framework\": \"primary framework / language / stack\",\n \"modules\": [{\"name\": \"...\", \"responsibility\": \"...\", \"keyFiles\": [\"path/to/file\"]}],\n \"architecture\": \"paragraph describing the architecture and data flow\",\n \"evidence\": [{\"claim\": \"...\", \"file\": \"path:line\"}]\n },\n \"features\": [{\"slug\": \"kebab-case-slug\", \"name\": \"...\", \"summary\": \"...\"}]\n}\n\nUse the Read, Grep, and Glob tools to investigate PROJECT_PATH. Cite real source file paths in evidence. Do not invent modules that do not exist. Do not modify the source project unless the user explicitly asks for source changes.",
10
+ "userPrompt": "Analyze the source project at PROJECT_PATH. The current working directory is KB_PATH.\n\nProject context:\n- Slug: {{SLUG}}\n- Source project path: {{PROJECT_PATH}}\n- Knowledge-base path: {{KB_PATH}}\n- Primary language: {{PRIMARY_LANGUAGE}}\n- Knowledge language: {{KNOWLEDGE_LANGUAGE}}\n\nTasks:\n1. Identify the framework, main tech stack, and language distribution.\n2. List the core modules / top-level directories and their responsibilities.\n3. Describe the architecture and data flow at a high level.\n4. Identify 3-6 user-facing features.\n5. Capture 4-8 evidence-backed claims with file:line citations.\n\nWrite the knowledge-base content in {{KNOWLEDGE_LANGUAGE}} where natural, but keep code identifiers, file paths, and JSON keys in English.\n\nReturn ONLY the JSON object described in the system prompt."
11
+ },
12
+ "commit-analysis": {
13
+ "description": "Batch analysis of one or more commits. Claude runs from the KB workspace and reads the source project via PROJECT_PATH.",
14
+ "model": "sonnet",
15
+ "permissionMode": "bypassPermissions",
16
+ "allowedTools": ["Read", "Grep", "Glob", "Bash"],
17
+ "systemPrompt": "You are a commit analyst building a knowledge base. The current working directory is the knowledge-base workspace, not the source project. Read source code from PROJECT_PATH and treat KB_PATH as the write target for knowledge-base drafts and updates. For each commit provided, output a structured change description. STRICT JSON only: no prose.\n\nOutput shape:\n{\n \"changes\": [\n {\n \"hash\": \"<full-or-short-hash>\",\n \"subject\": \"<original commit subject>\",\n \"type\": \"feat|fix|refactor|docs|test|chore|infra\",\n \"classification\": \"feature|bugfix|infrastructure|documentation|test\",\n \"summary\": \"one-paragraph description of what changed and why\",\n \"files\": [\"paths/touched\"],\n \"evidence\": [{\"claim\": \"...\", \"file\": \"path:line\"}]\n }\n ]\n}\n\nUse git commands against PROJECT_PATH and Read for inspecting changed files. Cite real source file paths. Do not modify the source project unless the user explicitly asks for source changes.",
18
+ "userPrompt": "Analyze the following commits in the source project at PROJECT_PATH. The current working directory is KB_PATH.\n\nProject: {{SLUG}} ({{PROJECT_PATH}})\nKnowledge-base path: {{KB_PATH}}\nCommit range: {{COMMIT_RANGE}}\nCommits to analyze:\n{{COMMIT_HASHES}}\n\nFor each commit, produce one entry in the changes array. Use git commands against {{PROJECT_PATH}} and Read for inspecting changed source files. Summarize in {{KNOWLEDGE_LANGUAGE}}; keep identifiers and paths in English.\n\nReturn ONLY the JSON object."
19
+ }
20
+ }
21
+ }