MCPcopy Index your code
hub / github.com/Fission-AI/OpenSpec / readProjectConfig

Function readProjectConfig

src/core/project-config.ts:151–262  ·  view source on GitHub ↗
(projectRoot: string)

Source from the content-addressed store, hash-verified

149 * @returns Parsed config or null if file doesn't exist
150 */
151export function readProjectConfig(projectRoot: string): ProjectConfig | null {
152 const configPath = resolveConfigFilePath(projectRoot);
153 if (configPath === null) {
154 return null; // No config is OK
155 }
156
157 try {
158 const content = readFileSync(configPath, 'utf-8');
159 const raw = parseYaml(content);
160
161 if (!raw || typeof raw !== 'object') {
162 console.warn(`openspec/config.yaml is not a valid YAML object`);
163 return null;
164 }
165
166 const config: Partial<ProjectConfig> = {};
167
168 // Parse schema field using Zod
169 const schemaField = z.string().min(1);
170 const schemaResult = schemaField.safeParse(raw.schema);
171 if (schemaResult.success) {
172 config.schema = schemaResult.data;
173 } else if (raw.schema !== undefined) {
174 console.warn(`Invalid 'schema' field in config (must be non-empty string)`);
175 }
176
177 // Parse context field with size limit
178 if (raw.context !== undefined) {
179 const contextField = z.string();
180 const contextResult = contextField.safeParse(raw.context);
181
182 if (contextResult.success) {
183 const contextSize = Buffer.byteLength(contextResult.data, 'utf-8');
184 if (contextSize > MAX_CONTEXT_SIZE) {
185 console.warn(
186 `Context too large (${(contextSize / 1024).toFixed(1)}KB, limit: ${MAX_CONTEXT_SIZE / 1024}KB)`
187 );
188 console.warn(`Ignoring context field`);
189 } else {
190 config.context = contextResult.data;
191 }
192 } else {
193 console.warn(`Invalid 'context' field in config (must be string)`);
194 }
195 }
196
197 // Parse rules field using Zod
198 if (raw.rules !== undefined) {
199 const rulesField = z.record(z.string(), z.array(z.string()));
200
201 // First check if it's an object structure (guard against null since typeof null === 'object')
202 if (typeof raw.rules === 'object' && raw.rules !== null && !Array.isArray(raw.rules)) {
203 const parsedRules: Record<string, string[]> = {};
204 let hasValidRules = false;
205
206 for (const [artifactId, rules] of Object.entries(raw.rules)) {
207 const rulesArrayResult = z.array(z.string()).safeParse(rules);
208

Callers 7

resolveSchemaForChangeFunction · 0.85
createChangeFunction · 0.85
gatherHealthFunction · 0.85
gatherRelationshipDataFunction · 0.85
loadRootConfigContextFunction · 0.85
generateInstructionsFunction · 0.85

Calls 3

resolveConfigFilePathFunction · 0.85
parseDeclarationListFunction · 0.85
configPathForWarningsFunction · 0.85

Tested by

no test coverage detected