(loc: Location)
| 187 | } |
| 188 | |
| 189 | function writeMcpEntry(loc: Location): WriteResult['files'][number] { |
| 190 | const file = configPath(loc); |
| 191 | const existed = fs.existsSync(file); |
| 192 | let text = readConfigText(file); |
| 193 | |
| 194 | // Seed a minimal opencode config when the file is brand-new so |
| 195 | // the result is a complete, schema-tagged file (not just a bare |
| 196 | // `{ "mcp": {...} }`). |
| 197 | if (!text.trim()) { |
| 198 | text = '{\n "$schema": "https://opencode.ai/config.json"\n}\n'; |
| 199 | } |
| 200 | |
| 201 | const config = parseConfig(text); |
| 202 | const before = config.mcp?.codegraph; |
| 203 | const after = getOpencodeServerEntry(); |
| 204 | |
| 205 | if (jsonDeepEqual(before, after)) { |
| 206 | return { path: file, action: 'unchanged' }; |
| 207 | } |
| 208 | |
| 209 | // Add $schema if the user's existing file is missing it. |
| 210 | if (!config.$schema) { |
| 211 | const schemaEdits = modify(text, ['$schema'], 'https://opencode.ai/config.json', { |
| 212 | formattingOptions: FORMATTING, |
| 213 | }); |
| 214 | text = applyEdits(text, schemaEdits); |
| 215 | } |
| 216 | |
| 217 | // Surgical edit — preserves comments, formatting, and order of |
| 218 | // every key we don't touch. |
| 219 | const edits = modify(text, ['mcp', 'codegraph'], after, { |
| 220 | formattingOptions: FORMATTING, |
| 221 | }); |
| 222 | const updated = applyEdits(text, edits); |
| 223 | atomicWriteFileSync(file, updated); |
| 224 | |
| 225 | return { path: file, action: existed ? 'updated' : 'created' }; |
| 226 | } |
| 227 | |
| 228 | /** |
| 229 | * Surgically drop `mcp.codegraph` from one config file. Leaves sibling |
no test coverage detected