From 0756636bde2388c75ebb0de976d90716a11b3334 Mon Sep 17 00:00:00 2001 From: Colby McHenry Date: Wed, 1 Apr 2026 13:50:12 -0500 Subject: [PATCH 1/2] feat: Improve search tokenization with camelCase splitting and code-aware stop words MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit extractSearchTerms now splits camelCase, PascalCase, snake_case, and dot.notation into individual tokens (e.g. "getUserName" → ["user", "name"]). Stop words expanded with code-specific noise words (code, file, function, method, class, type, etc.) to improve search precision. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/search/query-utils.ts | 40 ++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/src/search/query-utils.ts b/src/search/query-utils.ts index 8f16340..0e89f20 100644 --- a/src/search/query-utils.ts +++ b/src/search/query-utils.ts @@ -8,9 +8,11 @@ import * as path from 'path'; import { Node } from '../types'; /** - * Common stop words to filter from search queries + * Common stop words to filter from search queries. + * Includes generic English + code-specific noise words. */ export const STOP_WORDS = new Set([ + // English 'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with', 'by', 'from', 'is', 'it', 'that', 'this', 'are', 'was', 'be', 'has', 'had', 'have', 'do', 'does', 'did', 'will', 'would', 'could', @@ -18,17 +20,41 @@ export const STOP_WORDS = new Set([ 'every', 'how', 'what', 'where', 'when', 'who', 'which', 'why', 'i', 'me', 'my', 'we', 'our', 'you', 'your', 'he', 'she', 'they', 'find', 'show', 'get', 'list', 'give', 'tell', + 'been', 'done', 'made', 'used', 'using', 'work', 'works', 'found', + 'also', 'into', 'then', 'than', 'just', 'more', 'some', 'such', + 'over', 'only', 'new', 'out', 'its', 'so', 'up', 'as', 'if', + // Code-specific noise + 'code', 'file', 'files', 'function', 'method', 'class', 'type', + 'build', 'run', 'test', 'fix', 'bug', 'call', 'called', 'set', 'add', ]); /** - * Extract meaningful search terms from a natural language query + * Extract meaningful search terms from a natural language query. + * Splits camelCase, PascalCase, snake_case, SCREAMING_SNAKE, and dot.notation + * into individual tokens before filtering. */ export function extractSearchTerms(query: string): string[] { - return query - .toLowerCase() - .replace(/[^\w\s-]/g, ' ') - .split(/\s+/) - .filter(term => term.length > 1 && !STOP_WORDS.has(term)); + const tokens = new Set(); + + // Split camelCase / PascalCase: "getUserName" → "get User Name" + const camelSplit = query + .replace(/([a-z])([A-Z])/g, '$1 $2') + .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2'); + + // Replace underscores and dots with spaces (snake_case, dot.notation) + const normalised = camelSplit.replace(/[_.]+/g, ' '); + + // Split on any non-alphanumeric character + const words = normalised.split(/[^a-zA-Z0-9]+/).filter(Boolean); + + for (const word of words) { + const lower = word.toLowerCase(); + if (lower.length < 3) continue; + if (STOP_WORDS.has(lower)) continue; + tokens.add(lower); + } + + return [...tokens]; } /** From 12b0414900e4acfe7d1a0fbb5f131d3ccf6964ac Mon Sep 17 00:00:00 2001 From: Colby McHenry Date: Wed, 1 Apr 2026 14:10:01 -0500 Subject: [PATCH 2/2] fix: Prompt before global npm install during installer The installer previously ran `npm install -g` silently without user consent. Now it asks for confirmation first, explains why the global install is needed (hooks & MCP server), and gracefully skips if declined. README updated to document this step. Closes #69 Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 14 ++++++++------ src/installer/index.ts | 31 ++++++++++++++++++++----------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index ee7df9c..27930a5 100644 --- a/README.md +++ b/README.md @@ -162,6 +162,7 @@ npx @colbymchenry/codegraph ``` The interactive installer will: +- Prompt to install `codegraph` globally (needed for hooks & MCP server to work) - Configure the MCP server in `~/.claude.json` - Set up auto-allow permissions for CodeGraph tools - Add global instructions to `~/.claude/CLAUDE.md` (teaches Claude how to use CodeGraph) @@ -306,12 +307,13 @@ npx @colbymchenry/codegraph # Run via npx (no global install needed) ``` The installer will: -1. Ask for installation location (global `~/.claude` or local `./.claude`) -2. Configure the MCP server in `claude.json` -3. Optionally set up auto-allow permissions -4. Add global instructions to `~/.claude/CLAUDE.md` (teaches Claude how to use CodeGraph) -5. Install Claude Code hooks for automatic index syncing -6. For local installs: initialize and index the current project +1. Prompt to install `codegraph` globally (needed for hooks & MCP server) +2. Ask for installation location (global `~/.claude` or local `./.claude`) +3. Configure the MCP server in `claude.json` +4. Optionally set up auto-allow permissions +5. Add global instructions to `~/.claude/CLAUDE.md` (teaches Claude how to use CodeGraph) +6. Install Claude Code hooks for automatic index syncing +7. For local installs: initialize and index the current project ### `codegraph init [path]` diff --git a/src/installer/index.ts b/src/installer/index.ts index c381489..bea2e62 100644 --- a/src/installer/index.ts +++ b/src/installer/index.ts @@ -7,7 +7,7 @@ import { execSync } from 'child_process'; import { showBanner, showNextSteps, success, error, info, chalk } from './banner'; -import { promptInstallLocation, promptAutoAllow, InstallLocation } from './prompts'; +import { promptInstallLocation, promptAutoAllow, promptConfirm, InstallLocation } from './prompts'; import { writeMcpConfig, writePermissions, writeClaudeMd, writeHooks, hasMcpConfig, hasPermissions, hasHooks } from './config-writer'; /** @@ -25,16 +25,25 @@ export async function runInstaller(): Promise { showBanner(); try { - // Step 1: Install codegraph globally. - // Always run npm install -g — we can't use `command -v codegraph` to check - // because npx puts a temporary binary in PATH that vanishes when npx exits. - console.log(chalk.dim(' Installing codegraph globally...')); - try { - execSync('npm install -g @colbymchenry/codegraph', { stdio: 'pipe' }); - success('Installed codegraph command globally'); - } catch { - info('Could not install globally (permission denied)'); - info('Try: sudo npm install -g @colbymchenry/codegraph'); + // Step 1: Install codegraph globally (with user consent). + // The global install is needed because Claude Code hooks and the MCP server + // invoke `codegraph` by name — the temporary npx binary vanishes when npx exits. + console.log(chalk.bold(' Install codegraph globally?') + chalk.dim(' (Required for hooks & MCP server)')); + console.log(); + const shouldInstallGlobally = await promptConfirm('Install globally via npm', true); + + if (shouldInstallGlobally) { + console.log(chalk.dim(' Installing codegraph globally...')); + try { + execSync('npm install -g @colbymchenry/codegraph', { stdio: 'pipe' }); + success('Installed codegraph command globally'); + } catch { + info('Could not install globally (permission denied)'); + info('Try: sudo npm install -g @colbymchenry/codegraph'); + } + } else { + info('Skipped global install — hooks and MCP server may not work without it'); + info('You can install later: npm install -g @colbymchenry/codegraph'); } console.log();