Skip to main content
The SearchEngine class provides powerful search capabilities across your repository.

Import

import { Repository, SearchEngine } from 'wit';

Constructor

const repo = Repository.open('.');
const search = new SearchEngine(repo);

Methods

General-purpose search.
async search(query: string, options?: SearchOptions): Promise<SearchResults>
Parameters:
NameTypeDescription
querystringSearch query
optionsSearchOptionsSearch options
Options:
interface SearchOptions {
  type?: 'all' | 'commits' | 'files' | 'content';
  limit?: number;
  caseSensitive?: boolean;
  regex?: boolean;
  path?: string;  // Limit to path
}
Returns:
interface SearchResults {
  commits: CommitMatch[];
  files: FileMatch[];
  content: ContentMatch[];
  totalCount: number;
}
Example:
const search = new SearchEngine(repo);

// Search everything
const results = await search.search('authentication');

// Search commits only
const commits = await search.search('fix bug', { type: 'commits' });

// Search with regex
const todos = await search.search('TODO:', { regex: true });

searchCommits()

Search commit messages.
async searchCommits(query: string, options?: CommitSearchOptions): Promise<CommitMatch[]>
Options:
interface CommitSearchOptions {
  limit?: number;
  since?: Date;
  until?: Date;
  author?: string;
  caseSensitive?: boolean;
}
Returns:
interface CommitMatch {
  commit: Commit;
  matches: TextMatch[];
}

interface TextMatch {
  text: string;      // Matched text
  start: number;     // Start position
  end: number;       // End position
  context: string;   // Surrounding text
}
Example:
// Find commits about authentication
const commits = await search.searchCommits('auth');

// Find commits by specific author
const aliceCommits = await search.searchCommits('', { 
  author: 'alice' 
});

// Recent commits mentioning 'bug'
const bugs = await search.searchCommits('bug', {
  since: new Date('2024-01-01'),
  limit: 20
});

searchFiles()

Search for files by name.
async searchFiles(pattern: string, options?: FileSearchOptions): Promise<FileMatch[]>
Options:
interface FileSearchOptions {
  limit?: number;
  glob?: boolean;    // Treat pattern as glob
  extension?: string; // Filter by extension
}
Returns:
interface FileMatch {
  path: string;
  name: string;
  extension: string;
  size: number;
  lastModified: Date;
}
Example:
// Find files named 'index'
const indexFiles = await search.searchFiles('index');

// Find all TypeScript files
const tsFiles = await search.searchFiles('*.ts', { glob: true });

// Find test files
const tests = await search.searchFiles('test', { 
  extension: '.ts' 
});

searchContent()

Search file contents.
async searchContent(query: string, options?: ContentSearchOptions): Promise<ContentMatch[]>
Options:
interface ContentSearchOptions {
  limit?: number;
  caseSensitive?: boolean;
  regex?: boolean;
  path?: string;       // Limit to path
  extension?: string;  // Limit to extension
  contextLines?: number; // Lines of context
}
Returns:
interface ContentMatch {
  path: string;
  matches: LineMatch[];
}

interface LineMatch {
  line: number;
  content: string;
  matchStart: number;
  matchEnd: number;
  context: {
    before: string[];
    after: string[];
  };
}
Example:
// Find TODO comments
const todos = await search.searchContent('TODO', {
  contextLines: 2
});

// Find function definitions
const functions = await search.searchContent(
  'function\\s+\\w+',
  { regex: true, extension: '.ts' }
);

// Find in specific directory
const apiCalls = await search.searchContent('fetch', {
  path: 'src/api/'
});

searchBlame()

Find who last modified specific lines.
async searchBlame(path: string, query: string): Promise<BlameMatch[]>
Returns:
interface BlameMatch {
  line: number;
  content: string;
  commit: Commit;
  author: string;
}
Example:
// Find who wrote TODO comments
const todos = await search.searchBlame('src/index.ts', 'TODO');

for (const match of todos) {
  console.log(`Line ${match.line}: ${match.content}`);
  console.log(`  by ${match.author} in ${match.commit.hash}`);
}

Advanced Examples

Search and Replace Across Repository

async function findAndReplace(
  search: SearchEngine,
  repo: Repository,
  find: string,
  replace: string
) {
  const matches = await search.searchContent(find);
  
  const affectedFiles = new Set<string>();
  
  for (const file of matches) {
    console.log(`Found ${file.matches.length} matches in ${file.path}`);
    affectedFiles.add(file.path);
  }
  
  return Array.from(affectedFiles);
}

Find Dead Code

async function findUnusedExports(search: SearchEngine) {
  // Find all exports
  const exports = await search.searchContent(
    'export (const|function|class) (\\w+)',
    { regex: true }
  );
  
  const unusedExports = [];
  
  for (const file of exports) {
    for (const match of file.matches) {
      const exportName = match.content.match(/export \w+ (\w+)/)?.[1];
      if (!exportName) continue;
      
      // Search for usage
      const usage = await search.searchContent(exportName);
      
      // If only found in the export file, might be unused
      if (usage.length === 1 && usage[0].path === file.path) {
        unusedExports.push({ file: file.path, export: exportName });
      }
    }
  }
  
  return unusedExports;
}

Commit History Analysis

async function analyzeCommitPatterns(search: SearchEngine) {
  const bugFixes = await search.searchCommits('fix', { limit: 1000 });
  const features = await search.searchCommits('add|feat', { 
    regex: true, 
    limit: 1000 
  });
  
  console.log(`Bug fixes: ${bugFixes.length}`);
  console.log(`Features: ${features.length}`);
  
  // Analyze by author
  const authorStats: Record<string, number> = {};
  for (const match of bugFixes) {
    const author = match.commit.author;
    authorStats[author] = (authorStats[author] || 0) + 1;
  }
  
  console.log('Bug fixes by author:', authorStats);
}

Find Sensitive Information

async function findSecrets(search: SearchEngine) {
  const patterns = [
    { name: 'API Key', pattern: 'api[_-]?key[\\s]*[=:][\\s]*["\']?[\\w-]+' },
    { name: 'Password', pattern: 'password[\\s]*[=:][\\s]*["\'][^"\']+' },
    { name: 'Token', pattern: 'token[\\s]*[=:][\\s]*["\']?[\\w-]+' },
  ];
  
  const findings = [];
  
  for (const { name, pattern } of patterns) {
    const matches = await search.searchContent(pattern, {
      regex: true,
      caseSensitive: false
    });
    
    for (const file of matches) {
      findings.push({
        type: name,
        file: file.path,
        count: file.matches.length
      });
    }
  }
  
  return findings;
}

Performance Tips

Use specific search typesIf you only need commits, use searchCommits() instead of search() with type: 'commits'. Direct methods are faster.
Limit resultsAlways set a reasonable limit for large repositories:
const results = await search.search('query', { limit: 100 });
Use path filtersNarrow searches to specific directories:
const results = await search.searchContent('TODO', {
  path: 'src/components/'
});
Cache the SearchEngineCreate one instance and reuse it:
// Good
const search = new SearchEngine(repo);
await search.search('one');
await search.search('two');

// Avoid
await new SearchEngine(repo).search('one');
await new SearchEngine(repo).search('two');