( nearestRoot: string, globalDataDir?: string )
| 289 | } |
| 290 | |
| 291 | async function resolveNearestOrDeclaredRoot( |
| 292 | nearestRoot: string, |
| 293 | globalDataDir?: string |
| 294 | ): Promise<ResolvedOpenSpecRoot> { |
| 295 | const { hasPlanningShape, pointer } = classifyOpenSpecDir(nearestRoot); |
| 296 | |
| 297 | if (hasPlanningShape) { |
| 298 | if (pointer.value !== undefined) { |
| 299 | console.error( |
| 300 | `Warning: ${pointer.filePath} declares store '${pointer.value}', but this directory is a real OpenSpec root; the declaration is ignored.` |
| 301 | ); |
| 302 | } |
| 303 | return makeRoot(nearestRoot, 'nearest'); |
| 304 | } |
| 305 | |
| 306 | if (pointer.malformed) { |
| 307 | const problem = storePointerProblem(pointer.malformed); |
| 308 | throw new RootSelectionError( |
| 309 | `Invalid store declaration in ${pointer.filePath}: ${problem}.`, |
| 310 | 'invalid_store_pointer', |
| 311 | { |
| 312 | target: 'store.pointer', |
| 313 | fix: |
| 314 | pointer.malformed === 'unparseable' |
| 315 | ? `Fix the YAML syntax in ${pointer.filePath}.` |
| 316 | : `Edit ${pointer.filePath} so the store key is a registered store id, or remove it.`, |
| 317 | } |
| 318 | ); |
| 319 | } |
| 320 | |
| 321 | if (pointer.value === undefined) { |
| 322 | return makeRoot(nearestRoot, 'nearest'); |
| 323 | } |
| 324 | |
| 325 | try { |
| 326 | return await resolveStoreRoot(pointer.value, globalDataDir, 'declared'); |
| 327 | } catch (error) { |
| 328 | if (error instanceof RootSelectionError) { |
| 329 | // Rewrap with the declaration origin. The unknown-store fix is |
| 330 | // reshaped for the actual mistake: the user declared a pointer, |
| 331 | // they did not pass --store. |
| 332 | const declarationFix = |
| 333 | error.diagnostic.code === 'unknown_store' |
| 334 | ? `Register the store (openspec store register <path> --id ${pointer.value}) or edit ${pointer.filePath} to name a registered store.` |
| 335 | : error.diagnostic.fix; |
| 336 | throw new RootSelectionError( |
| 337 | `Declared in ${pointer.filePath}: ${error.message}`, |
| 338 | error.diagnostic.code, |
| 339 | { |
| 340 | ...(error.diagnostic.target ? { target: error.diagnostic.target } : {}), |
| 341 | ...(declarationFix ? { fix: declarationFix } : {}), |
| 342 | } |
| 343 | ); |
| 344 | } |
| 345 | throw error; |
| 346 | } |
| 347 | } |
| 348 |
no test coverage detected