(request: JsonRpcRequest)
| 161 | } |
| 162 | |
| 163 | private async handleInitialize(request: JsonRpcRequest): Promise<void> { |
| 164 | const params = request.params as { |
| 165 | rootUri?: string; |
| 166 | workspaceFolders?: Array<{ uri: string; name: string }>; |
| 167 | capabilities?: { roots?: unknown }; |
| 168 | clientInfo?: { name?: unknown; version?: unknown }; |
| 169 | } | undefined; |
| 170 | |
| 171 | this.clientSupportsRoots = !!params?.capabilities?.roots; |
| 172 | if (params?.clientInfo) { |
| 173 | this.clientInfo = { |
| 174 | name: typeof params.clientInfo.name === 'string' ? params.clientInfo.name : undefined, |
| 175 | version: typeof params.clientInfo.version === 'string' ? params.clientInfo.version : undefined, |
| 176 | }; |
| 177 | } |
| 178 | |
| 179 | // Explicit project signal, strongest first: client-provided rootUri / |
| 180 | // workspaceFolders (LSP-style), else the --path the server was launched |
| 181 | // with. cwd is NOT used here — we defer it so a roots/list answer can |
| 182 | // win over it. See issue #196. |
| 183 | let explicitPath: string | null = null; |
| 184 | if (params?.rootUri) { |
| 185 | explicitPath = fileUriToPath(params.rootUri); |
| 186 | } else if (params?.workspaceFolders?.[0]?.uri) { |
| 187 | explicitPath = fileUriToPath(params.workspaceFolders[0].uri); |
| 188 | } else if (this.explicitProjectPath) { |
| 189 | explicitPath = this.explicitProjectPath; |
| 190 | } |
| 191 | |
| 192 | // Pick the instructions variant by the root's index state — a cheap |
| 193 | // synchronous walk-up (existsSync loop only, no DB open, so the #172 |
| 194 | // respond-fast contract holds). When the root IS indexed, send the full |
| 195 | // single-project playbook. When it ISN'T, send the per-project variant |
| 196 | // (tools are still exposed — see handleToolsList): it tells the agent there |
| 197 | // is no default project and to pass `projectPath` to any project that has a |
| 198 | // `.codegraph/`. Gating tool AVAILABILITY on whether `./` is indexed was the |
| 199 | // #964 bug — it broke monorepos (only sub-projects indexed) and never |
| 200 | // surfaced the tools after a mid-session `codegraph init`. When no explicit |
| 201 | // path is known yet (roots/list dance pending), cwd is the best predictor of |
| 202 | // where the default project will resolve. |
| 203 | const indexed = findNearestCodeGraphRoot(explicitPath ?? process.cwd()) !== null; |
| 204 | |
| 205 | // Respond to the handshake BEFORE doing any heavy init — see issue #172. |
| 206 | this.transport.sendResult(request.id, { |
| 207 | protocolVersion: PROTOCOL_VERSION, |
| 208 | capabilities: { tools: {} }, |
| 209 | serverInfo: SERVER_INFO, |
| 210 | instructions: indexed ? SERVER_INSTRUCTIONS : SERVER_INSTRUCTIONS_NO_ROOT_INDEX, |
| 211 | }); |
| 212 | |
| 213 | if (explicitPath) { |
| 214 | // Kick off engine init in the background. If another session in the |
| 215 | // same daemon already opened the project, `ensureInitialized` is a |
| 216 | // ~free no-op — N concurrent clients pay exactly one open. |
| 217 | this.resolvePromise = this.engine.ensureInitialized(explicitPath); |
| 218 | } |
| 219 | } |
| 220 |
no test coverage detected