From a46add05b7f3373be43e23e58752f6f1f1a58c6e Mon Sep 17 00:00:00 2001 From: rob Date: Sat, 17 Jan 2026 02:42:46 -0400 Subject: [PATCH] Add prefix matching to registry search Search terms now automatically get a * suffix for prefix matching. E.g., searching "summ" matches "summarize", "summary", etc. Co-Authored-By: Claude Opus 4.5 --- CHANGELOG.md | 1 + src/cmdforge/registry/app.py | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f19dbd2..c107f55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,7 @@ All notable changes to CmdForge will be documented in this file. - Logs when AI review is skipped with reason ### Fixed +- **Registry search partial matching**: Search now supports prefix matching (e.g., "summ" matches "summarize") - VERSION_EXISTS error showing after successful publish (made endpoint idempotent by checking config_hash) - My Tools page listing every version separately (now consolidated by tool name) - GUI not refreshing after publish dialog closes diff --git a/src/cmdforge/registry/app.py b/src/cmdforge/registry/app.py index a42fffd..86639fe 100644 --- a/src/cmdforge/registry/app.py +++ b/src/cmdforge/registry/app.py @@ -677,10 +677,15 @@ def create_app() -> Flask: # Sanitize query for FTS5 - escape special characters that cause syntax errors # FTS5 special chars: * " ( ) : ^ - NOT AND OR NEAR # For safety, we'll quote the entire query if it contains special chars - fts5_special = set('*"():^-') + fts5_special = set('"():^-') if any(c in fts5_special for c in query_text) or query_text.upper() in ('NOT', 'AND', 'OR', 'NEAR'): # Escape double quotes and wrap in quotes for literal search query_text = '"' + query_text.replace('"', '""') + '"' + else: + # Add prefix matching - append * to each word for partial matches + # e.g., "summ text" becomes "summ* text*" to match "summarize", "text", "texting" + words = query_text.split() + query_text = " ".join(word + "*" if not word.endswith("*") else word for word in words) page, per_page, sort, order, error = parse_pagination("/tools/search", "downloads") if error: