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.
Maintainers
Keywords
Readme
nx-disklocation
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?
- ✨ Features
- 📦 Installation
- 🚀 Quick Start
- 📖 Core Concepts
- 🎯 API Reference
- 📚 Usage Examples
- 🏗️ Real-World Use Cases
- 🎓 Advanced Topics
- 🤝 Contributing
- 🗺️ Roadmap
🌟 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-disklocationyarn add nx-disklocationpnpm 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:
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
Search Within Root
- Recursively searches all subdirectories
- Respects configured extension priorities
- Returns first match or all matches
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:
- Priority 1: Direct path search in
.metadata/content/ - Priority 2: Find
.metadatafolder, then search sibling folders - 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 extensionrootFolderName(string): Root folder to find firststartPath(string, optional): Starting directory (defaults toprocess.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 findstartPath(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 withinstartPath(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 analyzestartPath(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 optionsstartPath(string, optional): Starting directory (defaults toprocess.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 findpreset('contentRegistry' | 'instructions' | 'config' | 'contentRegistryWithFallback'): Preset typecontentType(string, optional): Content type/root path for registry presetscategories(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 → rootStrategyPresets.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/ → rootStrategyPresets.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 .jsxExample 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/ → rootExample 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
Parent Search (Higher Priority)
- Searches upward from current location
- Checks each parent directory level
- Stops at first match or max depth
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:
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
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.mdOptimizing 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
- Priority Ordering: Lower numbers = higher priority
- Descriptive Names: Use clear strategy names for debugging
- Fallback Warnings: Enable
warnOnFallbackin development - Stop on First: Use
stopOnFirst: truefor performance - 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 testRunning Tests
npm run testCode 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
