()
| 37 | } |
| 38 | |
| 39 | export async function loadServerConfig(): Promise<ServerConfig | null> { |
| 40 | const configPath = |
| 41 | process.env.CODEBASE_CONTEXT_CONFIG_PATH ?? |
| 42 | path.join(os.homedir(), '.codebase-context', 'config.json'); |
| 43 | |
| 44 | let raw: string; |
| 45 | try { |
| 46 | raw = await fs.readFile(configPath, 'utf8'); |
| 47 | } catch (err) { |
| 48 | if ((err as NodeJS.ErrnoException).code === 'ENOENT') { |
| 49 | return null; |
| 50 | } |
| 51 | console.error(`[config] Failed to load config: ${(err as Error).message}`); |
| 52 | return null; |
| 53 | } |
| 54 | |
| 55 | let parsed: unknown; |
| 56 | try { |
| 57 | parsed = JSON.parse(raw); |
| 58 | } catch (err) { |
| 59 | console.error(`[config] Failed to load config: ${(err as Error).message}`); |
| 60 | return null; |
| 61 | } |
| 62 | |
| 63 | if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) { |
| 64 | return null; |
| 65 | } |
| 66 | |
| 67 | const config = parsed as Record<string, unknown>; |
| 68 | const result: ServerConfig = {}; |
| 69 | |
| 70 | if (Array.isArray(config.projects)) { |
| 71 | result.projects = (config.projects as unknown[]) |
| 72 | .filter( |
| 73 | (project): project is Record<string, unknown> => |
| 74 | typeof project === 'object' && project !== null |
| 75 | ) |
| 76 | .map((project) => { |
| 77 | const rawRoot = typeof project.root === 'string' ? project.root.trim() : ''; |
| 78 | if (!rawRoot) { |
| 79 | console.error('[config] Skipping project entry with missing or empty root'); |
| 80 | return null; |
| 81 | } |
| 82 | |
| 83 | const resolvedRoot = path.resolve(expandTilde(rawRoot)); |
| 84 | const parsedProject: ProjectConfig = { root: resolvedRoot }; |
| 85 | const excludePatterns = parseStringArray(project.excludePatterns); |
| 86 | if (excludePatterns) { |
| 87 | parsedProject.excludePatterns = excludePatterns; |
| 88 | } |
| 89 | |
| 90 | if ( |
| 91 | typeof project.analyzerHints === 'object' && |
| 92 | project.analyzerHints !== null && |
| 93 | !Array.isArray(project.analyzerHints) |
| 94 | ) { |
| 95 | const analyzerHints = project.analyzerHints as Record<string, unknown>; |
| 96 | const parsedHints: NonNullable<ProjectConfig['analyzerHints']> = {}; |
no test coverage detected