MCPcopy Create free account
hub / github.com/backnotprop/plannotator / resolveMarkdownFileCore

Function resolveMarkdownFileCore

packages/shared/resolve-file.ts:376–435  ·  view source on GitHub ↗

* Resolve a markdown file path within a project root. * * @param input - User-provided path (absolute, relative, or bare filename) * @param projectRoot - Project root directory to search within

(
	input: string,
	projectRoot: string,
)

Source from the content-addressed store, hash-verified

374 * @param projectRoot - Project root directory to search within
375 */
376function resolveMarkdownFileCore(
377 input: string,
378 projectRoot: string,
379): ResolveResult {
380 const normalizedInput = normalizeUserPathInput(input);
381 const searchInput = normalizeSeparators(normalizedInput);
382 const isBareFilename = !searchInput.includes("/");
383 const targetLookupKey = getLookupKey(searchInput, isBareFilename);
384
385 // Restrict to markdown files
386 if (!isSearchableMarkdownPath(normalizedInput)) {
387 return { kind: "not_found", input };
388 }
389
390 // 1. Absolute path — use as-is (no project root restriction;
391 // the user explicitly typed the full path)
392 if (isAbsoluteNormalizedUserPath(normalizedInput)) {
393 const absolutePath = resolveAbsolutePath(normalizedInput);
394 if (fileExists(absolutePath)) {
395 return { kind: "found", path: absolutePath };
396 }
397 return { kind: "not_found", input };
398 }
399
400 // 2. Exact relative path from project root
401 const fromRoot = resolve(projectRoot, searchInput);
402 if (isWithinProjectRoot(fromRoot, projectRoot) && fileExists(fromRoot)) {
403 return { kind: "found", path: fromRoot };
404 }
405
406 // 3. Case-insensitive search (only scan markdown files)
407 const allFiles: string[] = [];
408 walkMarkdownFiles(projectRoot, projectRoot, allFiles, IGNORED_DIRS);
409 const matches: string[] = [];
410
411 for (const match of allFiles) {
412 const normalizedMatch = normalizeSeparators(match);
413 const matchLookupKey = getLookupKey(normalizedMatch, isBareFilename);
414
415 if (matchLookupKey === targetLookupKey) {
416 const full = resolve(projectRoot, normalizedMatch);
417 if (isWithinProjectRoot(full, projectRoot)) {
418 matches.push(full);
419 }
420 }
421 }
422
423 if (matches.length === 1) {
424 return { kind: "found", path: matches[0] };
425 }
426 if (matches.length > 1) {
427 const projectRootPrefix = `${normalizeComparablePath(projectRoot)}/`;
428 const relative = matches.map((match) =>
429 normalizeComparablePath(match).replace(projectRootPrefix, ""),
430 );
431 return { kind: "ambiguous", input, matches: relative };
432 }
433

Callers 1

resolveMarkdownFileFunction · 0.85

Calls 13

normalizeUserPathInputFunction · 0.85
normalizeSeparatorsFunction · 0.85
getLookupKeyFunction · 0.85
isSearchableMarkdownPathFunction · 0.85
resolveAbsolutePathFunction · 0.85
isWithinProjectRootFunction · 0.85
normalizeComparablePathFunction · 0.85
replaceMethod · 0.80
fileExistsFunction · 0.70
resolveFunction · 0.70
walkMarkdownFilesFunction · 0.70

Tested by

no test coverage detected