@sanqianx/project-knowledge 4.6.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.
- package/.agents/plugins/marketplace.json +20 -0
- package/.claude-plugin/marketplace.json +17 -0
- package/CHANGELOG.md +780 -0
- package/LICENSE +201 -0
- package/NOTICE +5 -0
- package/README.md +266 -0
- package/_site/README.md +30 -0
- package/_site/_test/ai-profile-resolver-test.js +137 -0
- package/_site/_test/ai-profile-test.js +75 -0
- package/_site/_test/ai-vendor-presets-test.js +91 -0
- package/_site/_test/automation-queue-test.js +14 -0
- package/_site/_test/automation-ui-test.js +40 -0
- package/_site/_test/background-task-registry-test.js +43 -0
- package/_site/_test/baseline-schema-test.js +91 -0
- package/_site/_test/bin-cli-test.js +218 -0
- package/_site/_test/bridge-adapter-test.js +95 -0
- package/_site/_test/bridge-consumer-service-test.js +310 -0
- package/_site/_test/cdp-browser-failure-test.js +100 -0
- package/_site/_test/chat-claudecodeui-match-test.js +92 -0
- package/_site/_test/claude-executable-discovery-test.js +90 -0
- package/_site/_test/claude-session-lifecycle-sweep-test.js +252 -0
- package/_site/_test/claude-workbench-test.js +170 -0
- package/_site/_test/client-capture-restart-e2e-test.js +189 -0
- package/_site/_test/codex-conversation-projection-test.js +59 -0
- package/_site/_test/codex-notify-fanout-test.js +45 -0
- package/_site/_test/commit-boundary-freeze-test.js +147 -0
- package/_site/_test/commit-conversation-binding-test.js +99 -0
- package/_site/_test/commit-evidence-test.js +110 -0
- package/_site/_test/context-pack-test.js +198 -0
- package/_site/_test/conversation-api-test.js +119 -0
- package/_site/_test/conversation-store-test.js +136 -0
- package/_site/_test/cross-repo-isolation-e2e-test.js +187 -0
- package/_site/_test/data-dir-migration-test.js +77 -0
- package/_site/_test/desktop-browser-compat-test.js +28 -0
- package/_site/_test/desktop-hook-runtime-regression-test.js +204 -0
- package/_site/_test/embedding-config-test.js +32 -0
- package/_site/_test/explicit-commit-processor-test.js +37 -0
- package/_site/_test/fixtures/fake-browser-exit.js +10 -0
- package/_site/_test/fixtures/fake-browser-hang.js +10 -0
- package/_site/_test/fixtures/make-git-repos.js +89 -0
- package/_site/_test/folder-picker-output-test.js +19 -0
- package/_site/_test/full-integration-e2e-test.js +165 -0
- package/_site/_test/git-validation-test.js +81 -0
- package/_site/_test/hook-runtime-endpoint-test.js +88 -0
- package/_site/_test/hook-status-repair-api-test.js +152 -0
- package/_site/_test/hook-trigger-test.js +85 -0
- package/_site/_test/hook-worktree-test.js +42 -0
- package/_site/_test/import-preflight-api-test.js +233 -0
- package/_site/_test/import-ui-flow-test.js +171 -0
- package/_site/_test/index-builder-test.js +41 -0
- package/_site/_test/index-writer-concurrency-test.js +135 -0
- package/_site/_test/integration-adapters-test.js +243 -0
- package/_site/_test/integration-capture-setup-test.js +108 -0
- package/_site/_test/integration-surface-coverage-test.js +165 -0
- package/_site/_test/kb-framework-test.js +57 -0
- package/_site/_test/kbpath-follow-test.js +63 -0
- package/_site/_test/knowledge-db-test.js +144 -0
- package/_site/_test/knowledge-language-control-test.js +170 -0
- package/_site/_test/knowledge-maintenance-test.js +40 -0
- package/_site/_test/knowledge-migration-test.js +75 -0
- package/_site/_test/knowledge-promotion-recovery-test.js +260 -0
- package/_site/_test/knowledge-query-test.js +54 -0
- package/_site/_test/knowledge-retrieval-service-test.js +77 -0
- package/_site/_test/knowledge-scopes-test.js +37 -0
- package/_site/_test/knowledge-storage-location-test.js +28 -0
- package/_site/_test/knowledge-storage-startup-test.js +52 -0
- package/_site/_test/knowledge-store-logs-supervision-test.js +89 -0
- package/_site/_test/legacy-claude-session-alias-test.js +106 -0
- package/_site/_test/legacy-forward-compat-test.js +257 -0
- package/_site/_test/legacy-project-upgrade-e2e-test.js +363 -0
- package/_site/_test/legacy-routes-removed-test.js +47 -0
- package/_site/_test/legacy-workbench-exclusion-test.js +92 -0
- package/_site/_test/log-redaction-test.js +58 -0
- package/_site/_test/logging-api-test.js +89 -0
- package/_site/_test/logging-sse-no-gap-test.js +88 -0
- package/_site/_test/logging-ui-test.js +104 -0
- package/_site/_test/markdown-delta-overlay-test.js +85 -0
- package/_site/_test/markdown-maintenance-api-test.js +75 -0
- package/_site/_test/markdown-maintenance-test.js +11 -0
- package/_site/_test/mcp-server-test.js +149 -0
- package/_site/_test/model-context-windows-test.js +89 -0
- package/_site/_test/module-bridge-eventbridge-test.js +193 -0
- package/_site/_test/non-release-ci-test.js +34 -0
- package/_site/_test/offline-boundary-isolation-test.js +32 -0
- package/_site/_test/orphaned-claim-recovery-test.js +80 -0
- package/_site/_test/p0-data-migration-characterization-test.js +37 -0
- package/_site/_test/p0-e2e-gate-test.js +347 -0
- package/_site/_test/package-boundary-test.js +42 -0
- package/_site/_test/package-startup-test.js +56 -0
- package/_site/_test/packaged-ui-smoke-test.js +79 -0
- package/_site/_test/path-consistency-test.js +151 -0
- package/_site/_test/pending-sweep-test.js +9 -0
- package/_site/_test/post-commit-automation-test.js +87 -0
- package/_site/_test/pr-consumer-contract-test.js +55 -0
- package/_site/_test/project-control-panel-task14-test.js +63 -0
- package/_site/_test/project-delete-recovery-test.js +64 -0
- package/_site/_test/project-goal-editor-test.js +145 -0
- package/_site/_test/project-layout-v2-migration-test.js +127 -0
- package/_site/_test/project-lifecycle-transaction-test.js +106 -0
- package/_site/_test/project-remove-running-guard-test.js +79 -0
- package/_site/_test/project-store-test.js +90 -0
- package/_site/_test/prompt-settings-test.js +115 -0
- package/_site/_test/protected-architecture-gate-test.js +123 -0
- package/_site/_test/refactor-characterization-test.js +36 -0
- package/_site/_test/release-version-sync-test.js +98 -0
- package/_site/_test/requirement-binding-test.js +113 -0
- package/_site/_test/requirement-recorder-test.js +173 -0
- package/_site/_test/run-all-tests.js +156 -0
- package/_site/_test/runtime-endpoint-test.js +64 -0
- package/_site/_test/scanner-test.js +83 -0
- package/_site/_test/server-runtime-migration-safety-test.js +27 -0
- package/_site/_test/server-security-test.js +96 -0
- package/_site/_test/sessions-stream-test.js +92 -0
- package/_site/_test/shared-contracts-test.js +58 -0
- package/_site/_test/simple-import-test.js +97 -0
- package/_site/_test/startup-analysis-disabled-test.js +16 -0
- package/_site/_test/storage-foundation-test.js +78 -0
- package/_site/_test/structured-logger-test.js +101 -0
- package/_site/_test/task15-20-ui-flow-test.js +148 -0
- package/_site/_test/team-knowledge-store-sparse-checkout-test.js +196 -0
- package/_site/_test/team-knowledge-store-test.js +131 -0
- package/_site/_test/tracking-start-test.js +143 -0
- package/_site/_test/ui-i18n-toggle-test.js +114 -0
- package/_site/_test/ui-smoke-test.js +73 -0
- package/_site/_test/v4122-upgrade-data-contract-test.js +56 -0
- package/_site/_test/workbench-permission-test.js +118 -0
- package/_site/_test/workspace-ui-contract-test.js +43 -0
- package/_site/lib/ai-profile-resolver.js +78 -0
- package/_site/lib/ai-vendor-presets.js +83 -0
- package/_site/lib/ai-workspace.js +104 -0
- package/_site/lib/atomic-file.js +204 -0
- package/_site/lib/automation-config.js +140 -0
- package/_site/lib/bridge-adapter.js +256 -0
- package/_site/lib/bridge-consumer-service.js +402 -0
- package/_site/lib/claude-cli-runner.js +1599 -0
- package/_site/lib/commit-conversation-binder.js +246 -0
- package/_site/lib/commit-processing-ledger.js +27 -0
- package/_site/lib/commit-prompt.js +202 -0
- package/_site/lib/commit-reconciler.js +444 -0
- package/_site/lib/context-pack-builder.js +299 -0
- package/_site/lib/contracts.js +279 -0
- package/_site/lib/conversation-exclusions.js +204 -0
- package/_site/lib/conversation-query-service.js +161 -0
- package/_site/lib/conversation-store.js +385 -0
- package/_site/lib/data-dir.js +81 -0
- package/_site/lib/data-state-classifier.js +40 -0
- package/_site/lib/embedding-service.js +85 -0
- package/_site/lib/evidence-bundle.js +228 -0
- package/_site/lib/folder-picker-output.js +24 -0
- package/_site/lib/git-runner.js +62 -0
- package/_site/lib/github-team-store.js +1175 -0
- package/_site/lib/hook-manager.js +202 -0
- package/_site/lib/index-builder.js +157 -0
- package/_site/lib/index-service.js +173 -0
- package/_site/lib/integration-installer.js +907 -0
- package/_site/lib/kb-framework.js +191 -0
- package/_site/lib/kb-validator.js +124 -0
- package/_site/lib/knowledge-db.js +363 -0
- package/_site/lib/knowledge-promotion.js +435 -0
- package/_site/lib/knowledge-retrieval-service.js +401 -0
- package/_site/lib/knowledge-schema.js +121 -0
- package/_site/lib/knowledge-tool-runtime.js +359 -0
- package/_site/lib/legacy-data-manifest.js +26 -0
- package/_site/lib/llm-client.js +173 -0
- package/_site/lib/markdown-knowledge-indexer.js +230 -0
- package/_site/lib/migration-service.js +442 -0
- package/_site/lib/model-context-windows.js +82 -0
- package/_site/lib/module-bridge.js +496 -0
- package/_site/lib/post-commit-automation.js +141 -0
- package/_site/lib/project-lifecycle-service.js +511 -0
- package/_site/lib/project-registry-store.js +129 -0
- package/_site/lib/project-store.js +262 -0
- package/_site/lib/prompt-registry.js +148 -0
- package/_site/lib/requirement-adapters.js +10 -0
- package/_site/lib/requirement-binder.js +112 -0
- package/_site/lib/requirement-recorder.js +278 -0
- package/_site/lib/runtime-endpoint.js +160 -0
- package/_site/lib/scanner.js +298 -0
- package/_site/lib/server-app.js +1520 -0
- package/_site/lib/settings-store.js +113 -0
- package/_site/lib/storage-layout.js +162 -0
- package/_site/lib/structured-logger.js +574 -0
- package/_site/scripts/codex-notify-fanout.cjs +51 -0
- package/_site/scripts/folder-picker.ps1 +156 -0
- package/_site/scripts/hook-trigger.js +156 -0
- package/_site/scripts/sync-release-version.js +158 -0
- package/_site/server.js +12 -0
- package/_site/start.bat +26 -0
- package/_site/stop.bat +11 -0
- package/bin/project-knowledge-integrations.js +241 -0
- package/bin/project-knowledge-kb.js +65 -0
- package/bin/project-knowledge-mcp.js +195 -0
- package/bin/project-knowledge.js +439 -0
- package/claude-prompts.json +21 -0
- package/docs/README.zh-CN.md +175 -0
- package/docs/assets/architecture.svg +182 -0
- package/docs/assets/dashboard.png +0 -0
- package/docs/assets/draft-review.png +0 -0
- package/docs/assets/logo.svg +50 -0
- package/docs/assets/runs-drafts.png +0 -0
- package/docs/assets/settings.png +0 -0
- package/docs/project-registry-schema.md +33 -0
- package/docs/team-knowledge-mode-a-plan.md +73 -0
- package/docs/testing-strategy.md +33 -0
- package/package.json +100 -0
- package/plugins/project-knowledge/.claude-plugin/plugin.json +19 -0
- package/plugins/project-knowledge/.codex-plugin/plugin.json +38 -0
- package/plugins/project-knowledge/.mcp.json +14 -0
- package/plugins/project-knowledge/opencode/project-knowledge.md +3 -0
- package/plugins/project-knowledge/skills/project-knowledge/SKILL.md +28 -0
- package/plugins/project-knowledge/skills/project-knowledge/agents/openai.yaml +11 -0
- package/templates/change.md +30 -0
- package/templates/project-readme.md +52 -0
- package/ui/app.css +58 -0
- package/ui/app.js +607 -0
- package/ui/favicon.svg +38 -0
- package/ui/i18n.js +146 -0
- package/ui/index.html +162 -0
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// project-knowledge CLI — start in background by default (auto-opens browser).
|
|
4
|
+
// Pattern modeled on tokmeter's bin.js: detached child + PID file + port fallback.
|
|
5
|
+
// Run `project-knowledge --help` for usage.
|
|
6
|
+
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const { spawn, spawnSync, exec, execSync } = require('child_process');
|
|
9
|
+
const { existsSync, readFileSync, writeFileSync, unlinkSync, mkdirSync, openSync } = require('fs');
|
|
10
|
+
const net = require('net');
|
|
11
|
+
const os = require('os');
|
|
12
|
+
const { getDataDir } = require('../_site/lib/data-dir');
|
|
13
|
+
const runtimeEndpoint = require('../_site/lib/runtime-endpoint');
|
|
14
|
+
|
|
15
|
+
const pkg = require('../package.json');
|
|
16
|
+
const DEFAULT_PORT = parseInt(process.env.KB_SITE_PORT || '5757', 10);
|
|
17
|
+
const PORT_RANGE = 20;
|
|
18
|
+
const PID_FILE = path.join(os.tmpdir(), '.project-knowledge.pid');
|
|
19
|
+
const DATA_DIR = getDataDir();
|
|
20
|
+
|
|
21
|
+
function readPid() {
|
|
22
|
+
try {
|
|
23
|
+
if (!existsSync(PID_FILE)) return null;
|
|
24
|
+
const lines = readFileSync(PID_FILE, 'utf8').trim().split('\n');
|
|
25
|
+
return parseInt(lines[0], 10);
|
|
26
|
+
} catch { return null; }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function readPort() {
|
|
30
|
+
try {
|
|
31
|
+
if (!existsSync(PID_FILE)) return null;
|
|
32
|
+
const lines = readFileSync(PID_FILE, 'utf8').trim().split('\n');
|
|
33
|
+
return lines[1] ? parseInt(lines[1], 10) : null;
|
|
34
|
+
} catch { return null; }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function writePid(pid, port) {
|
|
38
|
+
try {
|
|
39
|
+
writeFileSync(PID_FILE, `${pid}\n${port}`, 'utf8');
|
|
40
|
+
} catch (err) {
|
|
41
|
+
console.error(`Warning: failed to write PID file at ${PID_FILE}: ${err.message}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function removePid(expectedPid) {
|
|
46
|
+
try {
|
|
47
|
+
if (expectedPid != null && readPid() !== Number(expectedPid)) return;
|
|
48
|
+
unlinkSync(PID_FILE);
|
|
49
|
+
} catch { /* ignore */ }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function isProcessAlive(pid) {
|
|
53
|
+
if (!pid || Number.isNaN(pid)) return false;
|
|
54
|
+
try {
|
|
55
|
+
process.kill(pid, 0);
|
|
56
|
+
return true;
|
|
57
|
+
} catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function openBrowser(url) {
|
|
63
|
+
let cmd;
|
|
64
|
+
if (process.platform === 'win32') {
|
|
65
|
+
cmd = `start "" "${url}"`;
|
|
66
|
+
} else if (process.platform === 'darwin') {
|
|
67
|
+
cmd = `open "${url}"`;
|
|
68
|
+
} else {
|
|
69
|
+
cmd = `xdg-open "${url}"`;
|
|
70
|
+
}
|
|
71
|
+
try { exec(cmd); } catch { /* best-effort */ }
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function isPortFree(port, host = '127.0.0.1') {
|
|
75
|
+
return new Promise((resolve) => {
|
|
76
|
+
const tester = net.createServer();
|
|
77
|
+
let settled = false;
|
|
78
|
+
const finish = (free) => {
|
|
79
|
+
if (settled) return;
|
|
80
|
+
settled = true;
|
|
81
|
+
tester.removeAllListeners();
|
|
82
|
+
tester.close(() => resolve(free));
|
|
83
|
+
};
|
|
84
|
+
tester.once('error', () => finish(false));
|
|
85
|
+
tester.once('listening', () => finish(true));
|
|
86
|
+
tester.listen(port, host);
|
|
87
|
+
// Under AV scan / cold cache a listen() can be slower than the probe
|
|
88
|
+
// timeout; a false "busy" cascades into "No free port found". 3s is slow
|
|
89
|
+
// enough for one probe while still bounding the 20-port scan.
|
|
90
|
+
setTimeout(() => finish(false), 3000);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function findFreePort(start, host = '127.0.0.1') {
|
|
95
|
+
for (let offset = 0; offset < PORT_RANGE; offset++) {
|
|
96
|
+
const port = start + offset;
|
|
97
|
+
if (await isPortFree(port, host)) return port;
|
|
98
|
+
}
|
|
99
|
+
throw new Error(`No free port found in range ${start}-${start + PORT_RANGE - 1}`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Find PIDs whose TCP socket is LISTENING on `port`. Cross-platform wrapper
|
|
103
|
+
// around netstat / lsof / ss. Returns a deduped list, possibly empty.
|
|
104
|
+
function findListeningPids(port) {
|
|
105
|
+
try {
|
|
106
|
+
let cmd;
|
|
107
|
+
if (process.platform === 'win32') {
|
|
108
|
+
cmd = `netstat -ano | findstr ":${port} " | findstr "LISTENING"`;
|
|
109
|
+
} else if (process.platform === 'darwin') {
|
|
110
|
+
cmd = `lsof -nP -iTCP:${port} -sTCP:LISTEN -t 2>/dev/null`;
|
|
111
|
+
} else {
|
|
112
|
+
cmd = `ss -tlnpH 'sport = :${port}' 2>/dev/null | grep -oP 'pid=\\K[0-9]+'`;
|
|
113
|
+
}
|
|
114
|
+
const stdout = execSync(cmd, { windowsHide: true, encoding: 'utf8', timeout: 3000 });
|
|
115
|
+
const pids = stdout.split(/\r?\n/).map((line) => {
|
|
116
|
+
const nums = line.match(/\d+/g);
|
|
117
|
+
return nums && nums.length ? parseInt(nums[nums.length - 1], 10) : null;
|
|
118
|
+
}).filter(Boolean);
|
|
119
|
+
return Array.from(new Set(pids));
|
|
120
|
+
} catch {
|
|
121
|
+
return [];
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Read another process's command line so we can verify it's ours before
|
|
126
|
+
// killing it. Returns '' on any failure (permissions, process gone, etc).
|
|
127
|
+
function getProcessCommandLine(pid) {
|
|
128
|
+
try {
|
|
129
|
+
let cmd, stdout;
|
|
130
|
+
if (process.platform === 'win32') {
|
|
131
|
+
stdout = execSync(`wmic process where "ProcessId=${pid}" get CommandLine /value`,
|
|
132
|
+
{ windowsHide: true, encoding: 'utf8', timeout: 3000 });
|
|
133
|
+
const m = stdout.match(/CommandLine=(.+)/);
|
|
134
|
+
return m ? m[1].trim() : '';
|
|
135
|
+
}
|
|
136
|
+
stdout = execSync(`ps -p ${pid} -o args= 2>/dev/null`,
|
|
137
|
+
{ encoding: 'utf8', timeout: 3000 });
|
|
138
|
+
return stdout.trim();
|
|
139
|
+
} catch {
|
|
140
|
+
return '';
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Scan DEFAULT_PORT..+PORT_RANGE for a LISTENING PID whose command line looks
|
|
145
|
+
// like our server. Used as a fallback when the PID file is missing/stale so the
|
|
146
|
+
// CLI isn't blind to long-running orphans.
|
|
147
|
+
function findOrphanProcess() {
|
|
148
|
+
const startPort = readPort() || DEFAULT_PORT;
|
|
149
|
+
for (let offset = 0; offset < PORT_RANGE; offset++) {
|
|
150
|
+
const port = startPort + offset;
|
|
151
|
+
for (const candidate of findListeningPids(port)) {
|
|
152
|
+
const cmdline = getProcessCommandLine(candidate).toLowerCase();
|
|
153
|
+
if (cmdline.includes('project-knowledge') && cmdline.includes('node')) {
|
|
154
|
+
return { pid: candidate, port };
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// ── Subcommands ──
|
|
162
|
+
function cmdStop() {
|
|
163
|
+
const endpoint = runtimeEndpoint.readLiveEndpoint(DATA_DIR);
|
|
164
|
+
if (endpoint) {
|
|
165
|
+
try {
|
|
166
|
+
process.kill(endpoint.pid);
|
|
167
|
+
runtimeEndpoint.clearEndpoint(DATA_DIR, { pid: endpoint.pid });
|
|
168
|
+
if (readPid() === endpoint.pid) removePid(endpoint.pid);
|
|
169
|
+
console.log(`project-knowledge stopped (PID ${endpoint.pid}).`);
|
|
170
|
+
} catch {
|
|
171
|
+
console.error(`Failed to stop process ${endpoint.pid}`);
|
|
172
|
+
process.exit(1);
|
|
173
|
+
}
|
|
174
|
+
process.exit(0);
|
|
175
|
+
}
|
|
176
|
+
const pid = readPid();
|
|
177
|
+
if (pid && isProcessAlive(pid)) {
|
|
178
|
+
try {
|
|
179
|
+
process.kill(pid);
|
|
180
|
+
removePid();
|
|
181
|
+
console.log(`project-knowledge stopped (PID ${pid}).`);
|
|
182
|
+
} catch {
|
|
183
|
+
console.error(`Failed to stop process ${pid}`);
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
186
|
+
process.exit(0);
|
|
187
|
+
}
|
|
188
|
+
if (pid) {
|
|
189
|
+
// Stale PID file — record pointed at a dead process. Drop it so the
|
|
190
|
+
// orphan-scan below can take over without being misled.
|
|
191
|
+
removePid();
|
|
192
|
+
console.log('Process already stopped.');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Fallback: the PID file can disappear (manual cleanup, antivirus, OS temp
|
|
196
|
+
// cleanup) while the server keeps running. Without this scan the CLI has no
|
|
197
|
+
// way to stop an orphan whose PID it never recorded.
|
|
198
|
+
const orphan = findOrphanProcess();
|
|
199
|
+
if (orphan) {
|
|
200
|
+
try {
|
|
201
|
+
process.kill(orphan.pid);
|
|
202
|
+
console.log(`Stopped orphan project-knowledge (PID ${orphan.pid}) on port ${orphan.port}.`);
|
|
203
|
+
process.exit(0);
|
|
204
|
+
} catch {
|
|
205
|
+
console.error(`Found PID ${orphan.pid} on port ${orphan.port} but failed to stop it.`);
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
console.log('No background process found.');
|
|
210
|
+
process.exit(0);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function cmdStatus() {
|
|
214
|
+
const endpoint = runtimeEndpoint.readLiveEndpoint(DATA_DIR);
|
|
215
|
+
if (endpoint) {
|
|
216
|
+
console.log(`project-knowledge is running (PID ${endpoint.pid}) at http://${endpoint.host}:${endpoint.port} [${endpoint.mode}]`);
|
|
217
|
+
process.exit(0);
|
|
218
|
+
}
|
|
219
|
+
const pid = readPid();
|
|
220
|
+
if (pid && isProcessAlive(pid)) {
|
|
221
|
+
const port = readPort() || DEFAULT_PORT;
|
|
222
|
+
console.log(`project-knowledge is running (PID ${pid}) at http://localhost:${port}`);
|
|
223
|
+
process.exit(0);
|
|
224
|
+
}
|
|
225
|
+
if (pid) removePid();
|
|
226
|
+
|
|
227
|
+
// PID file is gone or stale — check the port directly.
|
|
228
|
+
const orphan = findOrphanProcess();
|
|
229
|
+
if (orphan) {
|
|
230
|
+
console.log(`project-knowledge is running (orphan, PID ${orphan.pid}) at http://localhost:${orphan.port}`);
|
|
231
|
+
console.log('(No PID file on disk — recovered via port scan. Run "project-knowledge stop" to clean up.)');
|
|
232
|
+
process.exit(0);
|
|
233
|
+
}
|
|
234
|
+
console.log('project-knowledge is not running.');
|
|
235
|
+
process.exit(0);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function printHelp() {
|
|
239
|
+
console.log(`project-knowledge ${pkg.version}
|
|
240
|
+
|
|
241
|
+
Local knowledge-base dashboard manager.
|
|
242
|
+
|
|
243
|
+
Usage:
|
|
244
|
+
project-knowledge Start in background (default), auto-open browser
|
|
245
|
+
project-knowledge --fg Start in foreground (Ctrl+C to stop)
|
|
246
|
+
project-knowledge stop Stop the background process
|
|
247
|
+
project-knowledge status Check if running
|
|
248
|
+
project-knowledge install Install coding-agent integrations
|
|
249
|
+
project-knowledge update Update coding-agent integrations
|
|
250
|
+
project-knowledge uninstall Remove coding-agent integrations
|
|
251
|
+
project-knowledge integrations status
|
|
252
|
+
Check Claude Code, OpenCode, and Codex integrations
|
|
253
|
+
|
|
254
|
+
Options:
|
|
255
|
+
-p, --port <port> Port to run on (default: ${DEFAULT_PORT}, auto-fallback ±${PORT_RANGE})
|
|
256
|
+
--host <host> Host to bind on (default: 127.0.0.1)
|
|
257
|
+
--no-open Don't auto-open browser
|
|
258
|
+
--fg Run in foreground
|
|
259
|
+
-v, --version Print version and exit
|
|
260
|
+
-h, --help Show this help message
|
|
261
|
+
|
|
262
|
+
Runtime data lives next to the npm global root; PID file at ${PID_FILE}.
|
|
263
|
+
`);
|
|
264
|
+
process.exit(0);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// ── Parse args ──
|
|
268
|
+
const args = process.argv.slice(2);
|
|
269
|
+
|
|
270
|
+
const integrationCommands = new Set(['install', 'update', 'uninstall', 'integrations']);
|
|
271
|
+
if (integrationCommands.has(args[0])) {
|
|
272
|
+
const forwarded = args[0] === 'integrations'
|
|
273
|
+
? (args.slice(1).length ? args.slice(1) : ['status'])
|
|
274
|
+
: args;
|
|
275
|
+
const result = spawnSync(
|
|
276
|
+
process.execPath,
|
|
277
|
+
[path.join(__dirname, 'project-knowledge-integrations.js'), ...forwarded],
|
|
278
|
+
{ stdio: 'inherit', windowsHide: true }
|
|
279
|
+
);
|
|
280
|
+
process.exit(result.status == null ? 1 : result.status);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Subcommands first
|
|
284
|
+
if (args[0] === 'stop') cmdStop();
|
|
285
|
+
if (args[0] === 'status') cmdStatus();
|
|
286
|
+
|
|
287
|
+
let port = DEFAULT_PORT;
|
|
288
|
+
let host = '127.0.0.1';
|
|
289
|
+
let shouldOpen = true;
|
|
290
|
+
let foreground = false;
|
|
291
|
+
let portExplicit = false;
|
|
292
|
+
let hostExplicit = false;
|
|
293
|
+
|
|
294
|
+
for (let i = 0; i < args.length; i++) {
|
|
295
|
+
const arg = args[i];
|
|
296
|
+
if (arg === '--help' || arg === '-h') {
|
|
297
|
+
printHelp();
|
|
298
|
+
} else if (arg === '--version' || arg === '-v') {
|
|
299
|
+
console.log(pkg.version);
|
|
300
|
+
process.exit(0);
|
|
301
|
+
} else if ((arg === '--port' || arg === '-p') && args[i + 1]) {
|
|
302
|
+
const parsed = parseInt(args[i + 1], 10);
|
|
303
|
+
if (Number.isNaN(parsed)) {
|
|
304
|
+
console.error('Error: --port must be a number');
|
|
305
|
+
process.exit(1);
|
|
306
|
+
}
|
|
307
|
+
port = parsed;
|
|
308
|
+
portExplicit = true;
|
|
309
|
+
i++;
|
|
310
|
+
} else if (arg === '--host' && args[i + 1]) {
|
|
311
|
+
host = args[i + 1];
|
|
312
|
+
hostExplicit = true;
|
|
313
|
+
i++;
|
|
314
|
+
} else if (arg === '--no-open') {
|
|
315
|
+
shouldOpen = false;
|
|
316
|
+
} else if (arg === '--fg') {
|
|
317
|
+
foreground = true;
|
|
318
|
+
} else if (arg.startsWith('-')) {
|
|
319
|
+
console.error(`Unknown option: ${arg}\nRun \`project-knowledge --help\` for usage.`);
|
|
320
|
+
process.exit(1);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// ── Background launch ──
|
|
325
|
+
if (!foreground) {
|
|
326
|
+
const endpoint = runtimeEndpoint.readLiveEndpoint(DATA_DIR);
|
|
327
|
+
if (endpoint) {
|
|
328
|
+
const url = `http://${endpoint.host}:${endpoint.port}`;
|
|
329
|
+
console.log(`Already running (PID ${endpoint.pid}) at ${url}`);
|
|
330
|
+
if (shouldOpen) openBrowser(url);
|
|
331
|
+
process.exit(0);
|
|
332
|
+
}
|
|
333
|
+
const existingPid = readPid();
|
|
334
|
+
if (existingPid && isProcessAlive(existingPid)) {
|
|
335
|
+
const actualPort = readPort() || DEFAULT_PORT;
|
|
336
|
+
const url = `http://localhost:${actualPort}`;
|
|
337
|
+
console.log(`Already running (PID ${existingPid}) at ${url}`);
|
|
338
|
+
if (shouldOpen) openBrowser(url);
|
|
339
|
+
process.exit(0);
|
|
340
|
+
}
|
|
341
|
+
removePid();
|
|
342
|
+
|
|
343
|
+
// Re-spawn self with --fg so the child owns the PID file
|
|
344
|
+
const forwarded = [];
|
|
345
|
+
if (portExplicit) forwarded.push('--port', String(port));
|
|
346
|
+
if (hostExplicit) forwarded.push('--host', host);
|
|
347
|
+
if (!shouldOpen) forwarded.push('--no-open');
|
|
348
|
+
|
|
349
|
+
// The parent doesn't know the final port (the child may fall back when the
|
|
350
|
+
// default is busy), so it must not print a URL or open the browser — the
|
|
351
|
+
// child does both once it is actually listening. The child's output goes to
|
|
352
|
+
// a log file: a detached child with stdio:'ignore' is undiagnosable when
|
|
353
|
+
// startup fails or the port falls back.
|
|
354
|
+
const LAUNCH_LOG = path.join(DATA_DIR, 'launcher.log');
|
|
355
|
+
let logFd;
|
|
356
|
+
try {
|
|
357
|
+
mkdirSync(path.dirname(LAUNCH_LOG), { recursive: true });
|
|
358
|
+
logFd = openSync(LAUNCH_LOG, 'a');
|
|
359
|
+
} catch { /* best-effort: fall back to discarding output */ }
|
|
360
|
+
const stdio = logFd == null ? 'ignore' : ['ignore', logFd, logFd];
|
|
361
|
+
|
|
362
|
+
const child = spawn(
|
|
363
|
+
process.execPath,
|
|
364
|
+
[...process.argv.slice(1), '--fg', ...forwarded],
|
|
365
|
+
{ detached: true, stdio, windowsHide: true }
|
|
366
|
+
);
|
|
367
|
+
child.unref();
|
|
368
|
+
|
|
369
|
+
console.log('project-knowledge starting in background');
|
|
370
|
+
console.log(`Diagnostics: ${LAUNCH_LOG}`);
|
|
371
|
+
console.log(`Use "project-knowledge status" to check, "project-knowledge stop" to stop.`);
|
|
372
|
+
process.exit(0);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// ── Foreground: start server ──
|
|
376
|
+
process.env.KB_SITE_HOST = host;
|
|
377
|
+
|
|
378
|
+
async function main() {
|
|
379
|
+
const existingEndpoint = runtimeEndpoint.readLiveEndpoint(DATA_DIR);
|
|
380
|
+
if (existingEndpoint && existingEndpoint.pid !== process.pid) {
|
|
381
|
+
console.log(`Already running (PID ${existingEndpoint.pid}) at http://${existingEndpoint.host}:${existingEndpoint.port}`);
|
|
382
|
+
process.exit(0);
|
|
383
|
+
}
|
|
384
|
+
const actualPort = portExplicit ? port : await findFreePort(port, host);
|
|
385
|
+
process.env.KB_SITE_PORT = String(actualPort);
|
|
386
|
+
|
|
387
|
+
const claim = runtimeEndpoint.claimEndpoint(DATA_DIR, {
|
|
388
|
+
pid: process.pid,
|
|
389
|
+
host,
|
|
390
|
+
port: actualPort,
|
|
391
|
+
mode: process.env.KB_RUNTIME_MODE || 'cli',
|
|
392
|
+
});
|
|
393
|
+
if (!claim.claimed) {
|
|
394
|
+
const active = claim.endpoint;
|
|
395
|
+
if (active) {
|
|
396
|
+
console.log(`Already running (PID ${active.pid}) at http://${active.host}:${active.port}`);
|
|
397
|
+
} else {
|
|
398
|
+
console.error('Another project-knowledge process is starting. Please try again in a moment.');
|
|
399
|
+
}
|
|
400
|
+
process.exit(active ? 0 : 1);
|
|
401
|
+
}
|
|
402
|
+
writePid(process.pid, actualPort);
|
|
403
|
+
const cleanup = () => {
|
|
404
|
+
removePid(process.pid);
|
|
405
|
+
runtimeEndpoint.clearEndpoint(DATA_DIR, { pid: process.pid });
|
|
406
|
+
};
|
|
407
|
+
process.on('exit', cleanup);
|
|
408
|
+
process.on('SIGINT', () => { cleanup(); process.exit(0); });
|
|
409
|
+
process.on('SIGTERM', () => { cleanup(); process.exit(0); });
|
|
410
|
+
// Windows console-close sends SIGBREAK; Node doesn't run exit handlers after
|
|
411
|
+
// a forceful TerminateProcess, but SIGBREAK does fire before the process is
|
|
412
|
+
// torn down, so we can still clean up here.
|
|
413
|
+
if (process.platform === 'win32') {
|
|
414
|
+
process.on('SIGBREAK', () => { cleanup(); process.exit(0); });
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const url = `http://localhost:${actualPort}`;
|
|
418
|
+
if (actualPort !== port) {
|
|
419
|
+
console.log(`(Port ${port} was busy, using ${actualPort} instead)`);
|
|
420
|
+
}
|
|
421
|
+
console.log(`project-knowledge ${pkg.version}`);
|
|
422
|
+
console.log(`Listening at ${url}`);
|
|
423
|
+
// Resolve data dir the same way server.js will, so we can show the user
|
|
424
|
+
// where their config and KB files live BEFORE the server boots.
|
|
425
|
+
try {
|
|
426
|
+
console.log(`Data dir: ${getDataDir()}`);
|
|
427
|
+
} catch {}
|
|
428
|
+
|
|
429
|
+
require(path.join(__dirname, '..', '_site', 'server.js'));
|
|
430
|
+
|
|
431
|
+
if (shouldOpen) setTimeout(() => openBrowser(url), 500);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
main().catch((err) => {
|
|
435
|
+
console.error(err.message);
|
|
436
|
+
removePid(process.pid);
|
|
437
|
+
runtimeEndpoint.clearEndpoint(DATA_DIR, { pid: process.pid });
|
|
438
|
+
process.exit(1);
|
|
439
|
+
});
|
|
@@ -0,0 +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
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# project-knowledge
|
|
2
|
+
|
|
3
|
+
本地、由 Git Commit 驱动的项目知识服务。Markdown 是知识事实源;LanceDB
|
|
4
|
+
只是位于内部数据目录、可随时重建的派生索引。
|
|
5
|
+
|
|
6
|
+
## 安装与运行
|
|
7
|
+
|
|
8
|
+
新机器完整安装只需三条命令:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install -g project-knowledge
|
|
12
|
+
project-knowledge-integrations install
|
|
13
|
+
project-knowledge
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
1. `npm install -g project-knowledge` 安装知识库 CLI,内置
|
|
17
|
+
`ai-coding-event-bridge` 依赖,无需再装其它包。
|
|
18
|
+
2. `project-knowledge-integrations install` 是一次性的 Integration Setup,
|
|
19
|
+
对检测到的每个客户端(Claude Code / Codex / OpenCode)同时安装两类
|
|
20
|
+
相互独立、分别上报状态的能力:
|
|
21
|
+
- **Knowledge Integration**:插件 / MCP / Skill,让外部 Agent 能查询知识库;
|
|
22
|
+
- **Development Capture**:托管的 Bridge hooks / notify / plugin,把真实
|
|
23
|
+
Git 工作区里的 Agent 对话捕捉进已导入项目的「开发对话」。
|
|
24
|
+
同时注册 host 级 `project-knowledge` Bridge consumer。全程无需打开任何
|
|
25
|
+
客户端 UI;第三方 hooks 与配置完整保留;Codex notify 冲突只上报不覆盖。
|
|
26
|
+
3. `project-knowledge` 启动本地服务。旧版本导入的项目会在第一次 drain 时
|
|
27
|
+
自动升级(补写 canonical workspace 身份 + 建立对话 baseline),无需手工迁移。
|
|
28
|
+
|
|
29
|
+
注意事项:
|
|
30
|
+
|
|
31
|
+
- 装完 Development Capture 后,已在运行的 Claude Code 会话需重启一次
|
|
32
|
+
(hooks 在会话启动时加载)。只捕捉配置之后的对话,历史不回填。
|
|
33
|
+
- 按需收窄安装范围:`--ide claude` / `--capture-only` / `--knowledge-only`;
|
|
34
|
+
按客户端移除捕捉:`uninstall --capture-only`;全局禁用捕捉(含注销 Bridge
|
|
35
|
+
consumer):`disable-capture`。
|
|
36
|
+
|
|
37
|
+
默认只监听 `127.0.0.1:5757`。CLI 会记录实际 loopback endpoint,托管 Git
|
|
38
|
+
Hook 不依赖写死端口。
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
project-knowledge
|
|
42
|
+
project-knowledge --fg
|
|
43
|
+
project-knowledge status
|
|
44
|
+
project-knowledge stop
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
需要 Node.js 18+ 与 `PATH` 中的 Git。Windows 桌面安装包与 npm CLI 共享同一
|
|
48
|
+
数据目录和 backend owner 记录,不会同时启动两个 LanceDB writer。
|
|
49
|
+
|
|
50
|
+
## 唯一分析流程
|
|
51
|
+
|
|
52
|
+
公开分析入口只有两个:
|
|
53
|
+
|
|
54
|
+
```text
|
|
55
|
+
post-commit Hook ----+
|
|
56
|
+
+--> reconcileProjectCommits(projectId, trigger)
|
|
57
|
+
程序 startup --------+ trigger: git-hook | startup
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
导入只建立 Git tracking baseline、创建项目元数据并安装/验证 Hook;不运行 AI、
|
|
61
|
+
不扫描全仓推测需求、不生成 TODO 知识。空仓库导入后的第一个 Commit会被处理。
|
|
62
|
+
|
|
63
|
+
Hook 只向本地主程序发送 `hook-event/v2` 通知。主程序离线时 Hook 仍返回成功,
|
|
64
|
+
不阻塞 `git commit`,也不写离线任务 spool;下次启动从当前 Git 历史补查可达
|
|
65
|
+
Commit。同项目严格串行、失败即停;不同项目可并行;Hook 与 startup 重叠时复用
|
|
66
|
+
同一个 in-flight reconciliation。
|
|
67
|
+
|
|
68
|
+
## 知识与索引安全
|
|
69
|
+
|
|
70
|
+
每个 Commit 的 claim 会冻结 Commit SHA、真实 patch、绑定的 requirement IDs、
|
|
71
|
+
prompt hash 与固定 knowledgePath。AI 只能写内部 per-run staging,不能写源码或
|
|
72
|
+
final knowledgePath。产物 manifest 必须通过路径、UTF-8、Markdown、hash、证据与
|
|
73
|
+
操作类型验证。
|
|
74
|
+
|
|
75
|
+
Promotion 使用 backup、hash 与 durable journal。只有 Markdown promotion 验证
|
|
76
|
+
成功后才推进 lastAnalyzedCommit 并把 index 标为 dirty。索引失败不会回滚真实
|
|
77
|
+
Markdown,也不会重跑 AI;启动和维护流程会重试 dirty index。
|
|
78
|
+
|
|
79
|
+
`IndexService` 是唯一生产 LanceDB writer。增量更新和 full rebuild 共用一个全局
|
|
80
|
+
FIFO。完整重建先生成独立临时 DB,验证后原子替换,并在 recovery 中保留旧索引。
|
|
81
|
+
|
|
82
|
+
应用不会创建、修改、刷新或删除 `CLAUDE.md`。
|
|
83
|
+
|
|
84
|
+
## 需求记录与查询
|
|
85
|
+
|
|
86
|
+
Claude Code、Codex、OpenCode 集成可在编码前追加用户真实需求元数据。记录写入
|
|
87
|
+
项目 `requirements.jsonl`,不会触发分析或写知识。
|
|
88
|
+
|
|
89
|
+
MCP 提供只读 `resolve/search/ask/get/history`,以及唯一写能力
|
|
90
|
+
`record_requirement`。CLI 示例:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
project-knowledge-kb search --project <projectId> --query "令牌轮换" --json
|
|
94
|
+
project-knowledge-kb ask --project <projectId> --query "登录方案如何决定?"
|
|
95
|
+
project-knowledge-kb get --project <projectId> --entry modules/auth.md --json
|
|
96
|
+
project-knowledge-kb history --project <projectId> --json
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
只读查询不会创建或修改配置。索引缺失、dirty 或不可用时,会明确回退到当前项目
|
|
100
|
+
以及显式 related projects 的 Markdown。
|
|
101
|
+
|
|
102
|
+
## 存储合同
|
|
103
|
+
|
|
104
|
+
内部数据默认在 `~/.project-knowledge/`,可用 `KB_DATA_DIR` 修改。用户必须先设置
|
|
105
|
+
global knowledge root,再导入项目;它只影响未来导入。每个项目导入时固化绝对
|
|
106
|
+
knowledgePath,修改 global root 不会移动已有项目。
|
|
107
|
+
|
|
108
|
+
```text
|
|
109
|
+
~/.project-knowledge/
|
|
110
|
+
├── settings.json
|
|
111
|
+
├── projects.json # 仅 ID、顺序、最小显示快照
|
|
112
|
+
├── projects/<projectId>/
|
|
113
|
+
│ ├── config.json # 固定 repoPath/knowledgePath
|
|
114
|
+
│ ├── state.json # tracking/claim/index/Hook 状态
|
|
115
|
+
│ └── requirements.jsonl # 按需创建
|
|
116
|
+
├── index/knowledge.lancedb # 唯一派生索引
|
|
117
|
+
├── cache/
|
|
118
|
+
├── runtime/
|
|
119
|
+
├── logs/{app,projects,hooks}/
|
|
120
|
+
└── recovery/
|
|
121
|
+
|
|
122
|
+
<用户选择的知识根>/
|
|
123
|
+
└── <项目 storage name>/
|
|
124
|
+
├── README.md
|
|
125
|
+
├── GOAL.md
|
|
126
|
+
├── ARCHITECTURE.md
|
|
127
|
+
├── modules/*.md
|
|
128
|
+
└── changes/*.md
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Team knowledge 是显式例外:项目可绑定已经 checkout 的 team store 子目录。路径
|
|
132
|
+
仍必须位于所选 store 内并固化到 config;删除项目注册时不会删除这类外部知识。
|
|
133
|
+
|
|
134
|
+
## 迁移
|
|
135
|
+
|
|
136
|
+
`layout-v2` 迁移先只读发现旧资产,创建 journal 和集中 recovery backup,再 staging
|
|
137
|
+
settings、per-project config/state、最小 registry 与旧 index。验证路径、secret、
|
|
138
|
+
Commit pointer、日志和索引后才激活,completion marker 最后写。任何阶段中断都
|
|
139
|
+
保留旧 reader、用户知识、历史日志、配置与 backup,允许安全重试。
|
|
140
|
+
|
|
141
|
+
## 日志 UI
|
|
142
|
+
|
|
143
|
+
生产 UI 只保留一套结构化日志控制台,支持:
|
|
144
|
+
|
|
145
|
+
- trace/debug/info/warn/error/fatal 六级;
|
|
146
|
+
- 本地日期、项目、component、event、operation、Commit、全文过滤;
|
|
147
|
+
- newest-first cursor 分页、暂停/自动刷新、按当前条件导出;
|
|
148
|
+
- operation flow、结构化 error/stack、logger degraded 状态;
|
|
149
|
+
- Hook/index/项目只读状态、浅色/深色与窄屏布局。
|
|
150
|
+
|
|
151
|
+
日志使用 `log/v2` JSONL,按天和 50 MiB segment 轮转;默认保留 365 天,`0`
|
|
152
|
+
表示不按时间删除,并受总容量策略约束。写入、查询、error 与 export 全部递归脱敏。
|
|
153
|
+
日志根固定在内部数据目录,不能由 API 修改。
|
|
154
|
+
|
|
155
|
+
## API 安全
|
|
156
|
+
|
|
157
|
+
默认 loopback + same-origin,不使用 wildcard CORS。非回环绑定必须配置
|
|
158
|
+
`KB_SITE_AUTH_TOKEN` 并通过 Origin 校验。AI profile GET 只返回 masked metadata;
|
|
159
|
+
通用错误不返回 stack;不存在 raw-file、通用项目覆盖、手动 Hook 或手动分析接口。
|
|
160
|
+
|
|
161
|
+
## 验证
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
npm ci
|
|
165
|
+
npm test -- --no-report
|
|
166
|
+
npm test --prefix desktop
|
|
167
|
+
npm pack --dry-run --json
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Windows E2E 覆盖真实 Git Hook、空格/非 ASCII 路径、在线 Commit、停服期间多个
|
|
171
|
+
Commit、重启顺序补查、crash lock 恢复、promotion/index、查询与完整日志链路。
|
|
172
|
+
|
|
173
|
+
## 许可证
|
|
174
|
+
|
|
175
|
+
[Apache-2.0](../LICENSE),另见 [NOTICE](../NOTICE)。
|