.claude/skills/in-memory-search-registry/SKILL.md
Use when building a searchable in-memory catalog or registry for documentation sites, admin panels, or type/API browsers where you need keyword matching, fuzzy search, and ranked results without an external search engine or database. Covers RegistryService with weighted scoring across name, description, keywords, and method names; Levenshtein fuzzy matching; synonym expansion; category and subcategory filtering; and singleton DI registration for datasets of hundreds to low thousands of items. Domain: Search, Data Access Patterns. Level: Intermediate. Tags: search, registry, fuzzy-matching, in-memory, catalog, filtering.
npx skillsauth add klod68/littlerae in-memory-search-registryInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Your application needs a searchable catalog of items (types, products, docs, features) with:
Build a RegistryService that holds searchable items in memory, populated at startup, and queried via a scoring-based search algorithm.
/// <summary>
/// Represents an item in the searchable registry.
/// </summary>
public sealed record SearchableItem
{
public required string Name { get; init; }
public required string FullName { get; init; }
public required string Description { get; init; }
public required string Category { get; init; }
public string? Subcategory { get; init; }
public string[] Keywords { get; init; } = [];
public string[] MethodNames { get; init; } = [];
public string? Icon { get; init; }
public string? Url { get; init; }
}
/// <summary>
/// In-memory registry with search, filtering, and categorization.
/// </summary>
public sealed class RegistryService
{
private readonly List<SearchableItem> _items = [];
public void Register(SearchableItem item)
{
ArgumentNullException.ThrowIfNull(item);
_items.Add(item);
}
public void RegisterRange(IEnumerable<SearchableItem> items)
{
foreach (var item in items)
Register(item);
}
public IReadOnlyList<SearchableItem> GetAll() => _items.AsReadOnly();
public IEnumerable<string> GetCategories() =>
_items.Select(t => t.Category).Distinct().OrderBy(c => c);
public IEnumerable<string> GetSubcategories(string category) =>
_items.Where(t => t.Category == category && t.Subcategory is not null)
.Select(t => t.Subcategory!)
.Distinct()
.OrderBy(s => s);
}
public sealed record SearchResult(SearchableItem Item, double Score);
// Add to RegistryService:
public IEnumerable<SearchResult> Search(
string query,
int maxResults = 20,
string? category = null,
bool useFuzzyMatching = false,
bool useSynonyms = false)
{
if (string.IsNullOrWhiteSpace(query))
return [];
var terms = query.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
var candidates = category is null ? _items : _items.Where(t => t.Category == category);
return candidates
.Select(item => new SearchResult(item, CalculateScore(item, terms, useFuzzyMatching, useSynonyms)))
.Where(r => r.Score > 0)
.OrderByDescending(r => r.Score)
.Take(maxResults);
}
private static double CalculateScore(SearchableItem item, string[] terms, bool fuzzy, bool synonyms)
{
double score = 0;
foreach (var term in terms)
{
// Exact name match (highest weight)
if (item.Name.Contains(term, StringComparison.OrdinalIgnoreCase))
score += 10;
// Description match
if (item.Description.Contains(term, StringComparison.OrdinalIgnoreCase))
score += 3;
// Keyword match
if (item.Keywords.Any(k => k.Contains(term, StringComparison.OrdinalIgnoreCase)))
score += 5;
// Method name match
if (item.MethodNames.Any(m => m.Contains(term, StringComparison.OrdinalIgnoreCase)))
score += 4;
// Fuzzy matching (edit distance)
if (fuzzy && score == 0)
{
if (IsFuzzyMatch(item.Name, term))
score += 2;
}
// Synonym expansion
if (synonyms)
{
var expanded = GetSynonyms(term);
foreach (var syn in expanded)
{
if (item.Name.Contains(syn, StringComparison.OrdinalIgnoreCase))
score += 4;
if (item.Keywords.Any(k => k.Contains(syn, StringComparison.OrdinalIgnoreCase)))
score += 2;
}
}
}
return score;
}
private static bool IsFuzzyMatch(string source, string target, int maxDistance = 2)
{
if (Math.Abs(source.Length - target.Length) > maxDistance)
return false;
var distance = LevenshteinDistance(source.ToLowerInvariant(), target.ToLowerInvariant());
return distance <= maxDistance;
}
private static int LevenshteinDistance(string s, string t)
{
var n = s.Length;
var m = t.Length;
var d = new int[n + 1, m + 1];
for (var i = 0; i <= n; i++) d[i, 0] = i;
for (var j = 0; j <= m; j++) d[0, j] = j;
for (var i = 1; i <= n; i++)
for (var j = 1; j <= m; j++)
{
var cost = s[i - 1] == t[j - 1] ? 0 : 1;
d[i, j] = Math.Min(
Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1),
d[i - 1, j - 1] + cost);
}
return d[n, m];
}
private static readonly Dictionary<string, string[]> _synonyms = new(StringComparer.OrdinalIgnoreCase)
{
["cache"] = ["caching", "memory", "store", "redis"],
["validate"] = ["validation", "check", "verify", "guard"],
["page"] = ["pagination", "paging", "paged"],
["encrypt"] = ["encryption", "decrypt", "hash", "security"],
["date"] = ["datetime", "time", "calendar", "timestamp"],
["log"] = ["logging", "logger", "trace", "audit"],
["retry"] = ["resilience", "backoff", "fault-tolerance"],
};
private static string[] GetSynonyms(string term)
{
if (_synonyms.TryGetValue(term, out var synonyms))
return synonyms;
// Reverse lookup: if the term is a synonym value, find the key
return _synonyms
.Where(kvp => kvp.Value.Any(v => v.Equals(term, StringComparison.OrdinalIgnoreCase)))
.Select(kvp => kvp.Key)
.ToArray();
}
public static class RegistryServiceExtensions
{
public static IServiceCollection AddSearchRegistry(this IServiceCollection services)
{
var registry = new RegistryService();
// Register items from any source: reflection, config, hardcoded
registry.RegisterRange(LoadItemsFromReflection());
// registry.RegisterRange(LoadItemsFromConfig(config));
services.AddSingleton(registry);
return services;
}
}
List<T> is fine if populated once at startup and read-only afterward. If items are added at runtime, switch to ConcurrentBag<T> or use locking.StringComparison.OrdinalIgnoreCase for search comparisons.tools
Use when cross-cutting concerns (logging, metrics, validation, authorization) are tangled into command handlers or service methods, when building database command pipelines with reorderable concerns, or when HTTP client pipelines or message handlers need composable, independently-replaceable processing stages. Covers ICommandInterceptor interface, InterceptorPipeline with reverse-chain construction, zero-cost Empty sentinel to skip overhead when no interceptors are registered, and ConfigureAwait(false) discipline for library code. Domain: Architecture, Cross-Cutting Concerns. Level: Intermediate. Tags: interceptor, pipeline, middleware, decorator, cross-cutting-concerns.
development
Use when writing integration tests for Razor Pages, MVC, or Minimal API applications to validate routing, middleware, page rendering, and HTTP behavior without a browser or live server, or when adding fast smoke tests to a CI pipeline. Covers WebApplicationFactory<Program> setup with public partial class Program, in-memory test server, AngleSharp HTML parsing, CSS selector assertions, redirect and status code testing, and a shared static fixture pattern for minimal per-test startup overhead. Domain: Testing, ASP.NET Core. Level: Intermediate. Tags: integration-testing, webapplicationfactory, razor-pages, anglesharp, http-testing.
development
Use when designing indexes for new tables, diagnosing slow queries that are not using indexes efficiently, reviewing index fragmentation and maintenance, or when the current indexing strategy results in key lookups, table scans, or missing index warnings. Covers clustered index key selection (narrow, unique, ever-increasing), non-clustered index design for query patterns, covering indexes with INCLUDE columns, filtered indexes for subset queries, composite index column ordering, DMV-based monitoring for missing and unused indexes, and rebuild vs reorganize maintenance thresholds. Domain: Database, Performance. Level: Intermediate. Tags: index, sql-server, covering-index, filtered-index, performance, dmv, maintenance.
tools
Use when adding database persistence to a Truenorth.Components.* library, migrating legacy persistence code to the standard pattern, or adding new entities to an existing component following the stored-procedure-only architecture with EXECUTE-only permissions. Covers the 8-artifact pattern: embedded settings, language constants, static repository, component factory, DbMappings, Insert/Update/Delete commands, SelectAll/SelectById/Exists queries, and a CrudPersistenceHelper-based persistence service with IList/IListCollectable/ITableCollectable output variants. Domain: Data Access / Persistence. Level: Advanced. Tags: persistence, stored-procedure, CRUD, ADO.NET, provider-agnostic.