npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

nx-disklocation

v2.0.0

Published

Intelligent file and folder discovery SDK for Node.js and TypeScript. Smart upward/sibling search, multi-extension support, and path troubleshooting for monorepos and complex projects.

Readme

nx-disklocation

npm version License: MIT TypeScript Node

The intelligent file and folder discovery SDK for Node.js and TypeScript projects.

nx-disklocation is a powerful, batteries-included SDK that makes finding files and folders across complex directory structures effortless. Perfect for monorepos, build tools, CLI applications, and any project that needs intelligent file system navigation.

📚 Table of Contents


🌟 Why nx-disklocation?

The Problem: Finding files in modern JavaScript/TypeScript projects is hard. You might be deep in a nested component, working in a monorepo with multiple packages, or building a CLI tool that needs to locate project roots. Traditional approaches require manual path traversal, brittle relative paths, or complex glob patterns.

The Solution: nx-disklocation intelligently searches your file system with a multi-tier strategy system that automatically tries fallback strategies when primary locations fail. Perfect for chain-of-responsibility patterns where you need graceful degradation (e.g., content files → skills files → project root).

// Instead of this brittle manual fallback logic:
const primaryPath = path.join(__dirname, '../../content/test.md');
if (!fs.existsSync(primaryPath)) {
  const fallbackPath = path.join(__dirname, '../../skills/test.md');
  if (!fs.existsSync(fallbackPath)) {
    // More fallback logic...
  }
}

// Just define strategies and let nx-disklocation handle fallbacks:
const result = finder.smartFetch('test', {
  strategies: [
    { name: 'primary', searchPaths: ['.metadata/content'], priority: 1 },
    { name: 'fallback', rootFolders: ['.metadata'], searchSiblings: true, priority: 2 }
  ],
  warnOnFallback: true
});
console.log(result.filePath); // Automatically found with smart fallbacks!

✨ Features

🎯 Smart Search Strategies

  • Upward Traversal: Automatically searches parent directories to find project roots
  • Sibling Discovery: Falls back to sibling directories (perfect for monorepos!)
  • Recursive Descent: Searches all subdirectories within found roots
  • Multi-Extension Support: Try multiple file extensions in priority order
  • 🆕 Smart Fetch System: Define multiple search strategies with automatic fallback
  • 🆕 Strategy Presets: Pre-built strategies for content, instructions, and config files
  • 🆕 Chain of Responsibility: Automatic fallback when primary locations fail
  • 🆕 Metadata Construction: Context-aware results for content management systems
  • 🆕 Migration Guidance: Suggests moving files to standard locations

🔧 Intelligent Troubleshooting

  • Path Analysis: Analyze any path to understand what's there
  • Method Recommendations: Get told exactly which SDK methods to use
  • Configuration Suggestions: Receive optimal configuration for your project structure
  • Priority Ranking: HIGH/MEDIUM/LOW priority recommendations

📊 Rich Feedback

  • Search Strategy Reporting: Know if a file was found in parent or sibling directories
  • Location Descriptions: Human-readable explanations of where things were found
  • Console Logging: Beautiful, emoji-enhanced output for debugging
  • Detailed Results: Comprehensive result objects with all the information you need

🛠️ Developer Experience

  • Full TypeScript Support: Complete type definitions included
  • Zero Dependencies: Uses only Node.js built-ins
  • Flexible Configuration: Case-sensitive/insensitive, custom extensions, depth limits
  • 🆕 Strategy-Based Search: Define complex search patterns with automatic fallbacks
  • 🆕 Preset Strategies: Ready-to-use strategies for common use cases
  • 🆕 Content Management Integration: Metadata-aware results for AI/content systems
  • 🆕 Migration Assistance: Automatic suggestions for file organization
  • Extensive Documentation: README, guides, and 20+ examples

📦 Installation

npm install nx-disklocation
yarn add nx-disklocation
pnpm add nx-disklocation

🚀 Quick Start

Basic Usage

import { FileFinder } from 'nx-disklocation';

// Create a finder instance
const finder = new FileFinder({
  extensions: ['.yaml', '.json', '.toml'],
  caseInsensitive: false,
  maxDepth: 10
});

// Find a config file (traditional approach)
const result = finder.findFile('config', 'src');
if (result.found) {
  console.log(`Found: ${result.filePath}`);
}

🆕 Smart Fetch with Automatic Fallbacks (Recommended)

// Define strategies and let nx-disklocation handle fallbacks
const result = finder.smartFetch('config', {
  strategies: [
    { name: 'local', searchPaths: ['.'], priority: 1 },
    { name: 'parent', searchPaths: ['../'], priority: 2 },
    { name: 'sibling', rootFolders: ['config'], searchSiblings: true, priority: 3 }
  ],
  warnOnFallback: true
});

if (result.found) {
  console.log(`✅ Found at: ${result.filePath}`);
  console.log(`🔍 Strategy used: ${result.strategy}`);
  console.log(`⚠️ Fallback used: ${result.fallbackUsed}`);
}

🆕 Preset Strategies (Even Easier)

// Use pre-built strategies for common patterns
const contentResult = finder.fetchWithPreset('test', 'contentRegistry');
// Automatically tries: .metadata/content/ → .metadata/skills/ → ./

const configResult = finder.fetchWithPreset('database', 'config');
// Automatically tries: config/ → ./

const aiResult = finder.fetchWithPreset('prompt', 'instructions');
// Automatically tries: .metadata/instruction/ → .metadata/prompt/ → .metadata/skills/

🆕 Content Registry Integration (Advanced)

// Perfect for content management systems with /content/ subfolder handling
const result = finder.fetchWithPreset('test', 'contentRegistryWithFallback', '.metadata', ['instruction']);
// Solves the /content/ subfolder issue! Provides metadata construction context.
// Content-registry can build full metadata contracts even for legacy file locations.

import { FileFinder } from 'nx-disklocation';

// Create a finder instance
const finder = new FileFinder({
  extensions: ['.yaml', '.json', '.toml'],
  caseInsensitive: false,
  maxDepth: 10
});

// 🆕 Smart Fetch with automatic fallbacks (recommended)
const result = finder.smartFetch('config', {
  strategies: [
    { name: 'local', searchPaths: ['.'], priority: 1 },
    { name: 'parent', searchPaths: ['../'], priority: 2 },
    { name: 'sibling', rootFolders: ['config'], searchSiblings: true, priority: 3 }
  ],
  warnOnFallback: true
});

if (result.found) {
  console.log(`✅ Found at: ${result.filePath}`);
  console.log(`🔍 Strategy used: ${result.strategy}`);
  console.log(`⚠️ Fallback used: ${result.fallbackUsed}`);
}

// Traditional single-strategy approach still works
const traditionalResult = finder.findFile('config', 'src');

if (traditionalResult.found) {
  console.log(`✅ Found at: ${traditionalResult.filePath}`);
  console.log(`📂 Root folder: ${traditionalResult.rootPath}`);
  console.log(`🔍 Search method: ${traditionalResult.searchStrategy}`);
}

📖 Core Concepts

How It Works

nx-disklocation uses a three-step process to find files:

  1. Find the Root Folder

    • Searches upward through parent directories (higher priority)
    • Falls back to sibling directories if not found (lower priority)
    • Provides feedback on where and how the root was found
  2. Search Within Root

    • Recursively searches all subdirectories
    • Respects configured extension priorities
    • Returns first match or all matches
  3. Return Rich Results

    • Full paths and metadata
    • Search strategy used
    • Location descriptions

Search Priority Example

monorepo/
├── packages/
│   ├── app-a/          ← You are here
│   │   └── src/
│   │       └── component.tsx
│   ├── app-b/
│   │   └── src/        ← Sibling (lower priority)
│   └── shared/
│       └── src/        ← Sibling (lower priority)
└── src/                ← Parent (higher priority) ✅ CHOSEN

Priority 1: Searches upward (monorepo/packages/app-a/src → monorepo/src)
Priority 2: Searches siblings (app-b/src, shared/src)

🆕 Smart Fetch: Multi-Strategy Search System

The Problem: Sometimes you need complex fallback logic. Looking for .metadata/content/test.md but it should fall back to .metadata/skills/test.md if not found.

The Solution: Define multiple search strategies with priorities:

// Define strategies for content → skills fallback
const strategies = [
  {
    name: 'primary-content',
    description: 'Look in .metadata/content/',
    searchPaths: ['.metadata/content'],
    extensions: ['.md', '.json'],
    priority: 1  // Try first
  },
  {
    name: 'fallback-skills',
    description: 'Fallback to .metadata/skills/',
    rootFolders: ['.metadata'],  // Find .metadata folder first
    searchSiblings: true,         // Then search its siblings
    extensions: ['.md', '.json'],
    priority: 2  // Try if primary fails
  }
];

// Execute with automatic fallback
const result = finder.smartFetch('test', {
  strategies,
  warnOnFallback: true,    // Log warnings when using fallback
  stopOnFirst: true        // Stop at first successful match
});

Strategy Priority Flow:

  1. Priority 1: Direct path search in .metadata/content/
  2. Priority 2: Find .metadata folder, then search sibling folders
  3. Automatic Logging: Warns when fallback strategies are used

🎯 API Reference

Constructor

new FileFinder(config?: FileFinderConfig)

Configuration Options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | extensions | string[] | ['.md', '.txt', '.yaml', '.yml', '.json'] | File extensions to try | | maxDepth | number | 10 | Maximum levels to search upward | | caseInsensitive | boolean | false | Enable case-insensitive matching |

Key Methods Overview

| Method | Purpose | Use Case | |--------|---------|----------| | findFile() | Simple file search in root folder | Basic file discovery | | smartFetch() | 🆕 Multi-strategy search with fallbacks | Chain of responsibility patterns | | fetchWithPreset() | 🆕 Pre-built strategies | Common patterns (content, config, AI) | | findRootFolderEnhanced() | Enhanced folder search | Complex directory structures | | locationTroubleshooting() | Analyze paths and get recommendations | Debugging and optimization |

How to Choose the Right Method

// 🟢 SIMPLE: Just find a file in a known root folder
const result = finder.findFile('config', 'src');

// 🟡 ADVANCED: Need fallbacks when primary location fails
const result = finder.smartFetch('config', {
  strategies: [
    { name: 'local', searchPaths: ['.'], priority: 1 },
    { name: 'fallback', searchPaths: ['../'], priority: 2 }
  ]
});

// 🟠 PRESET: Common patterns (content registry, AI, config)
const result = finder.fetchWithPreset('test', 'contentRegistryWithFallback');
// Handles /content/ subfolder issues automatically!

// 🔴 COMPLEX: Multiple files or detailed analysis
const results = finder.findAllFiles('config', 'src');
const analysis = finder.locationTroubleshooting('./src');

Core Methods

findFile(fileName, rootFolderName, startPath?, extensions?)

Find a file by first locating a root folder, then searching within it.

Parameters:

  • fileName (string): File name with or without extension
  • rootFolderName (string): Root folder to find first
  • startPath (string, optional): Starting directory (defaults to process.cwd())
  • extensions (string[], optional): Extensions to try (overrides config)

Returns: FileSearchResult

interface FileSearchResult {
  found: boolean;
  filePath?: string;
  rootPath?: string;
  matchedExtension?: string;
  searchStrategy?: 'parent' | 'sibling' | 'not-found';
  locationDescription?: string;
  error?: string;
}

Example:

const result = finder.findFile('tsconfig', 'config', process.cwd(), ['.json']);

if (result.found) {
  console.log(result.filePath);           // '/path/to/config/tsconfig.json'
  console.log(result.searchStrategy);     // 'parent' or 'sibling'
  console.log(result.locationDescription); // 'In parent directory (2 levels up)'
}

findRootFolder(folderName, startPath?)

Find a folder by searching upward through parent directories, then siblings.

Parameters:

  • folderName (string): Name of the folder to find
  • startPath (string, optional): Starting directory

Returns: string | null - Absolute path to the folder, or null if not found

Example:

const srcPath = finder.findRootFolder('src');
if (srcPath) {
  console.log(`Found src at: ${srcPath}`);
}

findRootFolderEnhanced(folderName, startPath?)

Enhanced version with detailed search strategy information.

Returns: RootFolderResult

interface RootFolderResult {
  found: boolean;
  path?: string;
  strategy?: 'parent' | 'sibling';
  description?: string;
  depth?: number;
}

findAllFiles(fileName, rootFolderName, startPath?)

Find all files matching a name (with any extension) within a root folder.

Parameters:

  • fileName (string): Base file name (without extension)
  • rootFolderName (string): Root folder to search within
  • startPath (string, optional): Starting directory

Returns: string[] - Array of all matching file paths

Example:

const allConfigs = finder.findAllFiles('config', 'src');
// Returns: ['/path/to/src/config.yaml', '/path/to/src/sub/config.json', ...]

locationTroubleshooting(relativePath, startPath?)

🆕 Analyze a path and get intelligent SDK recommendations!

Parameters:

  • relativePath (string): Path to analyze
  • startPath (string, optional): Starting directory

Returns: TroubleshootingResult

interface TroubleshootingResult {
  analyzedPath: string;
  absolutePath: string;
  exists: boolean;
  type?: 'file' | 'directory' | 'unknown';
  findings: StrategyFinding[];
  recommendations: MethodRecommendation[];
  suggestions: string[];
  issues: string[];
  summary: string;
}

Example:

const analysis = finder.locationTroubleshooting('./src/config');

console.log(analysis.summary);
// "✅ Location is a DIRECTORY with contents. Recommended methods: findFile(), listFiles()."

// Get the best method to use
const topRec = analysis.recommendations.find(r => r.priority === 'high');
console.log(topRec.method);   // 'findFile()'
console.log(topRec.example);  // 'const result = finder.findFile("config", "src");'

smartFetch(fileName, options, startPath?)

🆕 Execute multiple search strategies with automatic fallback!

Parameters:

  • fileName (string): File to find (with or without extension)
  • options (SmartFetchOptions): Configuration with strategies and options
  • startPath (string, optional): Starting directory (defaults to process.cwd())

Returns: SmartFetchResult

interface SmartFetchOptions {
  strategies: SearchStrategy[];     // Strategies to try (sorted by priority)
  warnOnFallback?: boolean;         // Log warnings when using fallback strategies
  stopOnFirst?: boolean;            // Stop at first successful match
  returnAllMatches?: boolean;       // Return all matches from all strategies
}

interface SmartFetchResult {
  found: boolean;                   // Whether any file was found
  filePath?: string;                // Path to the file (if found)
  strategy?: string;                // Strategy that succeeded
  attemptedStrategies: string[];    // All strategies that were attempted
  warnings: string[];               // Warnings generated during search
  fallbackUsed: boolean;            // True if not the first strategy
  allMatches?: string[];            // All files found (if returnAllMatches)

  // 🆕 Metadata context for content management
  strategyMetadata?: any;           // Strategy metadata (legacy path flags, etc.)
  resolvedCategory?: string;        // Detected category (instruction/prompt/skills)
  isPrimaryPath: boolean;           // True if found via primary strategy
  needsMetadataConstruction: boolean; // True if metadata needs construction
}

Example:

const result = finder.smartFetch('config', {
  strategies: [
    { name: 'local', searchPaths: ['.'], priority: 1 },
    { name: 'parent', searchPaths: ['../'], priority: 2 }
  ],
  warnOnFallback: true
});

if (result.found) {
  console.log(`Found: ${result.filePath}`);
  console.log(`Strategy: ${result.strategy}`);
  if (result.fallbackUsed) {
    console.log('⚠️ Used fallback strategy');
  }
}

fetchWithPreset(fileName, preset, contentType?, categories?)

🆕 Quick method using predefined strategy presets!

Parameters:

  • fileName (string): File to find
  • preset ('contentRegistry' | 'instructions' | 'config' | 'contentRegistryWithFallback'): Preset type
  • contentType (string, optional): Content type/root path for registry presets
  • categories (string[], optional): Categories for contentRegistryWithFallback

Returns: SmartFetchResult

Example:

// Content registry pattern (your use case!)
const result = finder.fetchWithPreset('test', 'contentRegistry');
// Automatically tries: .metadata/content/ → .metadata/skills/ → ./

// 🆕 ADVANCED: Content registry with /content/ fallback (THE SOLUTION!)
const advancedResult = finder.fetchWithPreset('test', 'contentRegistryWithFallback', '.metadata', ['instruction']);
// Solves the /content/ subfolder problem! Provides metadata construction context.

// Instructions pattern for AI/LLM systems
const promptResult = finder.fetchWithPreset('system-prompt', 'instructions');
// Tries: .metadata/instruction/ → .metadata/prompt/ → .metadata/skills/

// Config file pattern
const configResult = finder.fetchWithPreset('database', 'config');
// Tries: config/ → ./

Strategy Presets

Predefined strategy collections for common use cases:

StrategyPresets.contentRegistry(contentType?)

Perfect for content management systems with fallback to skills/metadata:

const strategies = StrategyPresets.contentRegistry('content');
// Creates strategies for .metadata/content/ → siblings → root

StrategyPresets.instructions()

For AI/LLM instruction files:

const strategies = StrategyPresets.instructions();
// Creates strategies for instruction/ → prompt/ → skills/

StrategyPresets.config()

For configuration files:

const strategies = StrategyPresets.config();
// Creates strategies for config/ → root

StrategyPresets.contentRegistryWithFallback(rootPath?, categories?)

🆕 Advanced preset for content management systems (like content-registry)!

Solves the critical issue where content systems expect files in standard paths (.metadata/content/{category}/) but files might exist in legacy locations (.metadata/{category}/ without /content/).

Parameters:

  • rootPath (string, optional): Root metadata path (defaults to '.metadata')
  • categories (string[], optional): Categories to search (defaults to ['instruction', 'prompt', 'skills'])

Features:

  • Priority 1: Standard paths with /content/ subfolder (primary)
  • Priority 2: Legacy paths without /content/ subfolder (fallback)
  • Priority 3: Sibling directory search
  • Priority 4: Direct root search (last resort)

Provides metadata context for:

  • Category detection (instruction, prompt, skills)
  • Primary vs fallback path identification
  • Metadata construction requirements
  • Migration suggestions

Example:

// For content-registry: solves the /content/ subfolder problem
const strategies = StrategyPresets.contentRegistryWithFallback('.metadata', ['instruction']);

/*
Creates strategies:
1. primary-instruction: .metadata/content/instruction/ (priority 1.0)
2. no-content-instruction: .metadata/instruction/ (priority 2.0) - THE FIX!
3. sibling-folders: siblings of .metadata (priority 3)
4. root-fallback: .metadata/ directly (priority 4)
*/

Result provides context for content-registry:

const result = finder.smartFetch('test', { strategies });

if (result.needsMetadataConstruction) {
  // Content-registry knows to build metadata contract
  // Even though file was found in non-standard location
  console.log(`Category: ${result.resolvedCategory}`); // 'instruction'
  console.log(`Legacy path used: ${result.strategyMetadata?.isLegacyPath}`); // true
}

Utility Methods

| Method | Description | |--------|-------------| | checkPath(path) | Check if a path exists and get info | | fileExists(path) | Check if a file exists | | directoryExistsPublic(path) | Check if a directory exists | | listFiles(path) | List all files in a directory | | listDirectories(path) | List all subdirectories | | getFileInfo(path) | Get file statistics (fs.Stats) |


📚 Usage Examples

Example 1: Find Configuration Files

import { FileFinder } from 'nx-disklocation';

const finder = new FileFinder({
  extensions: ['.yaml', '.yml', '.json', '.toml']
});

const configResult = finder.findFile('config', 'config');

if (configResult.found) {
  console.log(`Config file: ${configResult.filePath}`);
  console.log(`Extension: ${configResult.matchedExtension}`);
} else {
  console.error(`Config not found: ${configResult.error}`);
}

Example 2: Monorepo Package Discovery

import { FileFinder } from 'nx-disklocation';

const finder = new FileFinder();

// Find shared utilities across packages
const utilsResult = finder.findFile('index', 'shared');

if (utilsResult.searchStrategy === 'sibling') {
  console.log('✅ Found in sibling package (shared configuration)');
  console.log(`Location: ${utilsResult.locationDescription}`);
}

Example 3: CLI Tool - Find Project Root

#!/usr/bin/env node
import { FileFinder } from 'nx-disklocation';

const finder = new FileFinder();

// Find package.json to locate project root
const pkgResult = finder.findFile('package.json', 'my-app');

if (!pkgResult.found) {
  console.error('❌ Not in a valid project directory');
  process.exit(1);
}

console.log(`✅ Project root: ${pkgResult.rootPath}`);
// Continue with your CLI tool logic...

Example 4: Build Tool - TypeScript Config Discovery

import { FileFinder } from 'nx-disklocation';

const finder = new FileFinder({
  extensions: ['.json']
});

// Find all TypeScript configs in project
const allTsConfigs = finder.findAllFiles('tsconfig', 'src');

allTsConfigs.forEach(configPath => {
  console.log(`Building with config: ${configPath}`);
  // Run TypeScript compiler for each config
});

Example 5: Smart Path Analysis

import { FileFinder } from 'nx-disklocation';

const finder = new FileFinder();

// Not sure which method to use? Ask the troubleshooter!
const analysis = finder.locationTroubleshooting('./src/components');

if (!analysis.exists) {
  console.log('Path does not exist. Suggestions:');
  analysis.suggestions.forEach(s => console.log(`  - ${s}`));
}

// Get high-priority recommendations
const highPriority = analysis.recommendations.filter(r => r.priority === 'high');

highPriority.forEach(rec => {
  console.log(`\n📌 ${rec.method}`);
  console.log(`   Reason: ${rec.reason}`);
  console.log(`   Example:\n${rec.example}`);
});

Example 6: Custom Extension Priority

import { FileFinder } from 'nx-disklocation';

// For a TypeScript project, prioritize .ts files
const finder = new FileFinder({
  extensions: ['.ts', '.tsx', '.js', '.jsx']
});

const indexFile = finder.findFile('index', 'src');
// Tries index.ts first, then .tsx, then .js, then .jsx

Example 7: Case-Insensitive Search (Cross-Platform)

import { FileFinder } from 'nx-disklocation';

// Works across Windows, macOS, Linux
const finder = new FileFinder({
  caseInsensitive: true
});

const readme = finder.findFile('readme', 'docs');
// Finds README.md, readme.md, ReadMe.MD, etc.

Example 8: 🆕 Smart Fetch with Automatic Fallback

import { FileFinder, StrategyPresets } from 'nx-disklocation';

const finder = new FileFinder();

// Content registry pattern - your exact use case!
// Looks for content, falls back to skills, then project root
const result = finder.fetchWithPreset('test', 'contentRegistry');

if (result.found) {
  console.log(`✅ Found: ${result.filePath}`);
  console.log(`Strategy: ${result.strategy}`);
  if (result.fallbackUsed) {
    console.log('⚠️ Used fallback - primary location not found');
  }
}

Example 9: 🆕 Custom Multi-Strategy Search

import { FileFinder } from 'nx-disklocation';

const finder = new FileFinder();

// Define custom strategies for complex scenarios
const strategies = [
  {
    name: 'workspace-config',
    description: 'Look in workspace root config',
    searchPaths: ['config'],
    extensions: ['.yaml', '.json'],
    priority: 1,
  },
  {
    name: 'package-config',
    description: 'Look in current package config',
    searchPaths: ['packages/current/config', 'src/config'],
    extensions: ['.yaml', '.json'],
    priority: 2,
  },
  {
    name: 'shared-config',
    description: 'Look in shared packages',
    rootFolders: ['packages'],
    searchSiblings: true,
    extensions: ['.yaml', '.json'],
    priority: 3,
  },
];

const result = finder.smartFetch('app-config', {
  strategies,
  warnOnFallback: true,    // Log when using fallbacks
  stopOnFirst: true,       // Stop at first match
});

console.log(`Strategies attempted: ${result.attemptedStrategies.join(' → ')}`);

Example 10: 🆕 AI Content Management System

import { FileFinder } from 'nx-disklocation';

class AIContentManager {
  private finder: FileFinder;

  constructor() {
    this.finder = new FileFinder({
      extensions: ['.md', '.txt', '.json']
    });
  }

  // Find content with automatic fallback chain
  findContent(contentName: string): string | null {
    const strategies = [
      {
        name: 'primary-content',
        description: 'Primary content repository',
        searchPaths: ['.metadata/content'],
        priority: 1,
      },
      {
        name: 'skills-fallback',
        description: 'Fallback to skills repository',
        rootFolders: ['.metadata'],
        searchSiblings: true,
        priority: 2,
      },
      {
        name: 'project-fallback',
        description: 'Project root fallback',
        searchPaths: ['.'],
        priority: 3,
      },
    ];

    const result = this.finder.smartFetch(contentName, {
      strategies,
      warnOnFallback: true,
    });

    return result.found ? result.filePath! : null;
  }
}

// Usage in AI pipeline
const manager = new AIContentManager();
const contentPath = manager.findContent('user-preferences');
// Automatically tries: content/ → skills/ → root

Example 11: 🆕 Content Registry Integration (The Complete Solution)

import { FileFinder } from 'nx-disklocation';

class ContentRegistry {
  private finder: FileFinder;

  constructor() {
    this.finder = new FileFinder({
      extensions: ['.md', '.txt', '.json', '.yaml']
    });
  }

  // 🆕 THE SOLUTION: Use contentRegistryWithFallback preset
  async fetch(key: string, category?: string): Promise<ContentResult> {
    // Use the advanced preset that handles /content/ subfolder issues
    const result = this.finder.fetchWithPreset(
      key,
      'contentRegistryWithFallback',
      '.metadata',  // root path
      category ? [category] : ['instruction', 'prompt', 'skills']
    );

    if (!result.found) {
      throw new Error(`Content not found: ${key}`);
    }

    // 🆕 CRITICAL: Build metadata contract for fallback paths
    const metadata = result.isPrimaryPath
      ? await this.loadMetadataFromPrimaryPath(result.filePath!)
      : await this.constructMetadataForFallbackPath(result);

    // Log warnings when using non-standard paths
    if (result.fallbackUsed) {
      console.warn(`⚠️ Content loaded from non-standard location`);
      console.warn(`   Expected: .metadata/content/${category || 'instruction'}/${key}`);
      console.warn(`   Actual: ${result.filePath}`);
      console.warn(`   Strategy: ${result.strategy}`);

      // Suggest migration for legacy paths
      if (result.strategyMetadata?.isLegacyPath) {
        console.warn(`💡 MIGRATION SUGGESTED:`);
        console.warn(`   Move: ${result.filePath}`);
        console.warn(`   To: .metadata/content/${result.resolvedCategory}/${key}.md`);
        console.warn(`   Reason: Primary path supports full metadata contract`);
      }
    }

    return {
      content: await fs.readFile(result.filePath!, 'utf-8'),
      metadata,
      filePath: result.filePath!,
      strategy: result.strategy!,
      category: result.resolvedCategory,
      isPrimaryPath: result.isPrimaryPath,
      fallbackUsed: result.fallbackUsed,
    };
  }

  // 🆕 Build metadata for fallback paths (solves auto-extraction!)
  private async constructMetadataForFallbackPath(result: SmartFetchResult): Promise<ContentMetadata> {
    const content = await fs.readFile(result.filePath!, 'utf-8');

    // Extract metadata from file content (YAML front-matter, etc.)
    const extracted = this.extractMetadataFromContent(content);

    return {
      key: path.basename(result.filePath!, path.extname(result.filePath!)),
      category: result.resolvedCategory || 'instruction',
      version: extracted.version || '1.0.0',
      format: extracted.format || 'flex-md',
      source: 'local-fallback',
      resolvedAt: new Date().toISOString(),
      strategyUsed: result.strategy,
      ...extracted,
    };
  }

  private extractMetadataFromContent(content: string): Partial<ContentMetadata> {
    // Simplified - would parse YAML front-matter, etc.
    return {};
  }
}

// Usage - now works even with legacy file locations!
const registry = new ContentRegistry();
const result = await registry.fetch('test', 'instruction');
// ✅ Finds file even if in .metadata/instruction/ instead of .metadata/content/instruction/
// ✅ Constructs full metadata contract for auto-extraction
// ✅ Provides migration suggestions
// ✅ Logs warnings for transparency

🏗️ Real-World Use Cases

Monorepo Configuration Management

import { FileFinder } from 'nx-disklocation';

class MonorepoConfigManager {
  private finder: FileFinder;

  constructor() {
    this.finder = new FileFinder({
      extensions: ['.json', '.yaml']
    });
  }

  getSharedConfig(configName: string): any {
    // Finds config in shared package even from deep nested locations
    const result = this.finder.findFile(configName, 'shared');
    
    if (result.found) {
      return require(result.filePath);
    }
    
    throw new Error(`Config ${configName} not found`);
  }

  getAllPackageConfigs(): string[] {
    return this.finder.findAllFiles('package.json', 'packages');
  }
}

Build Tool Plugin

import { FileFinder } from 'nx-disklocation';

export class BuildPlugin {
  private finder: FileFinder;

  constructor() {
    this.finder = new FileFinder({
      extensions: ['.ts', '.tsx', '.js', '.jsx']
    });
  }

  findEntryPoints(): string[] {
    // Find all index files as entry points
    return this.finder.findAllFiles('index', 'src');
  }

  locateConfig(): string {
    const result = this.finder.findFile('build.config', 'config');
    
    if (!result.found) {
      throw new Error('Build config not found');
    }
    
    return result.filePath!;
  }
}

Documentation Generator

import { FileFinder } from 'nx-disklocation';

class DocGenerator {
  private finder: FileFinder;

  constructor() {
    this.finder = new FileFinder({
      extensions: ['.md']
    });
  }

  generateIndex(): void {
    const docsRoot = this.finder.findRootFolder('docs');
    
    if (!docsRoot) {
      console.error('Docs folder not found');
      return;
    }

    const allDocs = this.finder.findAllFiles('', 'docs')
      .filter(f => f.endsWith('.md'));

    // Generate index from all markdown files
    this.createIndex(allDocs);
  }
}

🆕 AI Content Management System

import { FileFinder, StrategyPresets } from 'nx-disklocation';

class AIContentRegistry {
  private finder: FileFinder;

  constructor() {
    this.finder = new FileFinder({
      extensions: ['.md', '.txt', '.json', '.yaml']
    });
  }

  /**
   * Find content with intelligent fallback chain
   * Chain: content/ → skills/ → project root
   */
  findContent(contentId: string): ContentResult {
    const result = this.finder.fetchWithPreset(contentId, 'contentRegistry');

    return {
      found: result.found,
      path: result.filePath,
      strategy: result.strategy,
      fallbackUsed: result.fallbackUsed,
      warnings: result.warnings,
    };
  }

  /**
   * Find AI instructions with multiple fallback strategies
   * Chain: instruction/ → prompt/ → skills/
   */
  findInstructions(instructionName: string): string | null {
    const result = this.finder.fetchWithPreset(instructionName, 'instructions');
    return result.found ? result.filePath! : null;
  }

  /**
   * Batch find all content in registry
   */
  findAllContent(): string[] {
    return this.finder.findAllFiles('', '.metadata');
  }
}

interface ContentResult {
  found: boolean;
  path?: string;
  strategy?: string;
  fallbackUsed: boolean;
  warnings: string[];
}

🆕 Content Registry Integration

import { FileFinder, StrategyPresets } from 'nx-disklocation';

class ContentRegistryIntegration {
  private finder: FileFinder;

  constructor(localRoot: string = '.metadata') {
    this.finder = new FileFinder({
      extensions: ['.md', '.txt', '.json', '.yaml']
    });
  }

  /**
   * Fetch content with automatic fallback and metadata construction
   * Solves the "/content/" subfolder problem completely
   */
  async fetchContent(key: string, category?: string): Promise<ContentFetchResult> {
    // Use the specialized preset for content registry
    const result = this.finder.fetchWithPreset(
      key,
      'contentRegistryWithFallback',
      '.metadata',
      category ? [category] : ['instruction', 'prompt', 'skills']
    );

    if (!result.found) {
      throw new Error(`Content not found: ${key}`);
    }

    // Build complete metadata contract
    const metadata = result.needsMetadataConstruction
      ? await this.constructMetadata(result)
      : await this.loadStandardMetadata(result.filePath!);

    // Log helpful information
    if (result.fallbackUsed) {
      console.warn(`⚠️ Content loaded from fallback location: ${result.strategy}`);
      if (result.strategyMetadata?.isLegacyPath) {
        console.warn(`💡 Consider migrating to standard path for better performance`);
      }
    }

    const content = await fs.readFile(result.filePath!, 'utf-8');

    return {
      content,
      metadata,
      filePath: result.filePath!,
      resolvedCategory: result.resolvedCategory,
      strategyUsed: result.strategy,
      isPrimaryPath: result.isPrimaryPath,
      needsMetadataConstruction: result.needsMetadataConstruction,
    };
  }

  private async constructMetadata(result: SmartFetchResult): Promise<ContentMetadata> {
    // Extract metadata from file content and result context
    const content = await fs.readFile(result.filePath!, 'utf-8');
    const extracted = this.extractFrontMatter(content);

    return {
      key: path.basename(result.filePath!, path.extname(result.filePath!)),
      category: result.resolvedCategory || 'instruction',
      version: extracted.version || '1.0.0',
      format: extracted.format || 'flex-md',
      source: result.isPrimaryPath ? 'local-primary' : 'local-fallback',
      resolvedAt: new Date().toISOString(),
      strategyUsed: result.strategy,
      ...extracted,
    };
  }

  private extractFrontMatter(content: string): Partial<ContentMetadata> {
    // Parse YAML front-matter, detect output formats, etc.
    return {};
  }
}

// Now content-registry works seamlessly regardless of file location!
const integration = new ContentRegistryIntegration();
const content = await integration.fetchContent('test');
// ✅ Always works - even with legacy file locations
// ✅ Always provides complete metadata contract
// ✅ Always enables auto-extraction

🎓 Advanced Topics

Understanding Search Strategies

nx-disklocation supports multiple search paradigms:

Traditional Two-Tier Strategy

  1. Parent Search (Higher Priority)

    • Searches upward from current location
    • Checks each parent directory level
    • Stops at first match or max depth
  2. Sibling Search (Lower Priority)

    • Searches sibling directories if parent search fails
    • Perfect for monorepo structures
    • Finds shared packages automatically

🆕 Smart Fetch Multi-Strategy System

Define complex search patterns with automatic fallback chains:

  1. Strategy Definition

    • Each strategy has a priority (lower number = higher priority)
    • Strategies can search direct paths or find folders first
    • Automatic logging when fallbacks are used
  2. Execution Flow

    • Strategies executed in priority order
    • Stops at first successful match (unless returnAllMatches)
    • Rich feedback about which strategy succeeded

Smart Fetch Example:

🔍 Trying strategy: primary-content (Look in .metadata/content/)
❌ Strategy "primary-content" failed
🔍 Trying strategy: sibling-folders (Look in sibling folders of .metadata)
   🔍 Searching sibling directories (lower priority)...
✅ Found file using strategy "sibling-folders": /path/to/.metadata/skills/test.md
⚠️ File found using fallback strategy "sibling-folders" (not primary strategy)
   Primary: primary-content
   Used: sibling-folders
   File: /path/to/.metadata/skills/test.md

Optimizing for Your Project Structure

Use locationTroubleshooting() to get optimal configuration:

const finder = new FileFinder();

// Analyze your project structure
const analysis = finder.locationTroubleshooting('./src');

// Extract recommended configuration
const configRec = analysis.recommendations.find(r => r.config);

if (configRec) {
  // Create an optimized finder
  const optimizedFinder = new FileFinder(configRec.config);
}

Error Handling Best Practices

const result = finder.findFile('config', 'src');

if (!result.found) {
  // Check if root folder was found
  if (!result.rootPath) {
    console.error('Root folder "src" not found');
    console.error(`Error: ${result.error}`);
  } else {
    console.error('File not found in root folder');
    // Root exists but file doesn't
  }
  
  // Troubleshoot for recommendations
  const analysis = finder.locationTroubleshooting('./src');
  console.log('Suggestions:', analysis.suggestions);
}

🆕 Smart Fetch Strategy Patterns

Content Registry Pattern (Your Use Case)

// Perfect for AI systems with content → skills fallback
const contentStrategies = StrategyPresets.contentRegistry('content');
/*
Creates:
1. Primary: .metadata/content/ (priority 1)
2. Fallback: .metadata siblings (priority 2)
3. Last resort: project root (priority 3)
*/

Chain of Responsibility Pattern

const strategies = [
  { name: 'local', searchPaths: ['.'], priority: 1 },
  { name: 'user', searchPaths: ['~/.config'], priority: 2 },
  { name: 'system', searchPaths: ['/etc'], priority: 3 },
  { name: 'default', searchPaths: ['./defaults'], priority: 4 },
];

Monorepo Package Discovery

const strategies = [
  {
    name: 'local-package',
    searchPaths: ['src', 'lib'],
    priority: 1,
  },
  {
    name: 'shared-packages',
    rootFolders: ['packages'],
    searchSiblings: true,
    priority: 2,
  },
];

Configuration Hierarchy

const configStrategies = StrategyPresets.config();
/*
Creates:
1. Config folder: config/ (priority 1)
2. Project root: ./ (priority 2)
*/

Best Practices for Smart Fetch

  1. Priority Ordering: Lower numbers = higher priority
  2. Descriptive Names: Use clear strategy names for debugging
  3. Fallback Warnings: Enable warnOnFallback in development
  4. Stop on First: Use stopOnFirst: true for performance
  5. Strategy Reuse: Define strategies once, reuse across calls

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Development Setup

# Clone the repository
git clone https://github.com/yourusername/nx-disklocation.git

# Install dependencies
cd nx-disklocation
npm install

# Build
npm run build

# Run examples
npm test

Running Tests

npm run test

Code Style

This project uses TypeScript strict mode and follows standard TypeScript conventions.


📄 License

MIT © [Your Name]


🙏 Acknowledgments

  • Inspired by the challenges of working with complex monorepo structures
  • Built with ❤️ for the Node.js and TypeScript community

📞 Support


🗺️ Roadmap

  • [x] ~~Smart Fetch multi-strategy system~~ ✅ COMPLETED
  • [x] ~~Strategy presets for common patterns~~ ✅ COMPLETED
  • [x] ~~Chain of responsibility patterns~~ ✅ COMPLETED
  • [x] ~~Content Registry Integration~~ ✅ COMPLETED
  • [x] ~~Metadata construction for fallback paths~~ ✅ COMPLETED
  • [x] ~~Migration guidance system~~ ✅ COMPLETED
  • [ ] Glob pattern matching support
  • [ ] Watch mode for file changes
  • [ ] Parallel directory search for large trees
  • [ ] Plugin system for custom search strategies
  • [ ] Integration examples with popular build tools
  • [ ] Performance benchmarks
  • [ ] Caching mechanism for repeated searches

Made with ❤️ for developers who need intelligent file discovery

⬆ back to top