@endlessblink/like-i-said-v2
Version:
Task Management & Memory for Claude - Track tasks, remember context, and maintain continuity across sessions with 27 powerful tools. Works with Claude Desktop and Claude Code.
565 lines (485 loc) • 18.6 kB
JavaScript
/**
* Query Intelligence System
* Inspired by claude-historian's approach to smart query classification
* Classifies queries to optimize search strategy and improve result relevance
*/
export class QueryIntelligence {
constructor() {
// Query type patterns for classification (claude-historian inspired)
this.patterns = {
error: {
keywords: ['error', 'bug', 'issue', 'problem', 'fail', 'failed', 'exception', 'crash', 'broken', 'fix', 'fixed'],
patterns: [/error\s*:/i, /exception\s*:/i, /failed\s+to/i, /cannot\s+\w+/i, /undefined\s+is\s+not/i],
weight: 1.5
},
code: {
keywords: ['function', 'class', 'method', 'variable', 'import', 'export', 'const', 'let', 'var', 'async', 'await'],
patterns: [/\w+\(\)/, /\w+\.\w+/, /import\s+.*\s+from/, /class\s+\w+/, /function\s+\w+/],
weight: 1.3
},
file: {
keywords: ['file', 'path', 'directory', 'folder', '.js', '.ts', '.tsx', '.json', '.md'],
patterns: [/\w+\.\w+$/, /\/\w+\//, /\\\w+\\/, /src\//, /lib\//, /components\//],
weight: 1.2
},
concept: {
keywords: ['how', 'what', 'why', 'when', 'where', 'concept', 'explain', 'understand', 'implement', 'approach'],
patterns: [/how\s+to/i, /what\s+is/i, /why\s+does/i, /when\s+should/i],
weight: 1.0
},
recent: {
keywords: ['recent', 'latest', 'last', 'today', 'yesterday', 'previous', 'just', 'now'],
patterns: [/last\s+\d+/, /recent\s+\w+/, /latest\s+\w+/],
weight: 1.4
},
command: {
keywords: ['npm', 'yarn', 'git', 'bash', 'run', 'build', 'test', 'install', 'deploy'],
patterns: [/npm\s+\w+/, /yarn\s+\w+/, /git\s+\w+/, /\$\s*\w+/],
weight: 1.1
}
};
// Boost factors for different scenarios
this.boostFactors = {
questionMark: 1.2,
quoted: 1.3,
multipleKeywords: 1.4,
exact: 2.0
};
// Domain-specific synonyms and related terms
this.synonymMap = {
// MCP/Development terms
'mcp': ['model context protocol', 'mcp server', 'mcp client'],
'stdio': ['stdin stdout', 'standard io', 'standard input output'],
'jsonrpc': ['json-rpc', 'json rpc', 'jsonrpc2', 'json-rpc 2.0'],
'fastmcp': ['fast mcp', 'fast-mcp', 'fastmcp framework'],
// Common programming terms
'config': ['configuration', 'settings', 'setup', 'conf'],
'repo': ['repository', 'git repo', 'codebase'],
'env': ['environment', 'environment variable', 'env var'],
'auth': ['authentication', 'authorization', 'login'],
'db': ['database', 'data store', 'storage'],
'api': ['endpoint', 'rest api', 'service'],
'ui': ['user interface', 'frontend', 'gui', 'interface'],
'bug': ['error', 'issue', 'problem', 'defect'],
'fix': ['solution', 'resolve', 'patch', 'workaround'],
// Action terms
'working': ['works', 'functional', 'successful', 'functioning'],
'broken': ['failing', 'not working', 'error', 'failed'],
'debug': ['troubleshoot', 'investigate', 'diagnose'],
'implement': ['build', 'create', 'develop', 'code'],
// State terms
'disconnected': ['connection lost', 'not connected', 'connection error'],
'connected': ['connection established', 'online', 'linked'],
'crashed': ['segfault', 'core dump', 'terminated', 'died'],
'hanging': ['frozen', 'stuck', 'unresponsive', 'deadlock']
};
// Common acronyms and their expansions
this.acronyms = {
'api': 'application programming interface',
'cli': 'command line interface',
'gui': 'graphical user interface',
'sdk': 'software development kit',
'ide': 'integrated development environment',
'ci': 'continuous integration',
'cd': 'continuous deployment',
'pr': 'pull request',
'poc': 'proof of concept',
'mvp': 'minimum viable product',
'orm': 'object relational mapping',
'crud': 'create read update delete',
'rest': 'representational state transfer',
'jwt': 'json web token',
'oauth': 'open authorization',
'ssl': 'secure sockets layer',
'tls': 'transport layer security',
'npm': 'node package manager',
'pip': 'pip installs packages'
};
// Common typos and corrections
this.typoCorrections = {
'disconneted': 'disconnected',
'conection': 'connection',
'recieve': 'receive',
'seperate': 'separate',
'occured': 'occurred',
'successfull': 'successful',
'paramter': 'parameter',
'arguement': 'argument',
'fucntion': 'function',
'retrun': 'return',
'flase': 'false',
'ture': 'true',
'cosnt': 'const',
'lgo': 'log',
'consoel': 'console',
'proint': 'print',
'debgu': 'debug',
'eroor': 'error',
'mehtod': 'method',
'clinet': 'client',
'sever': 'server',
'josn': 'json',
'pythno': 'python',
'javscript': 'javascript'
};
// Contextual expansions (phrases that should be expanded)
this.contextualExpansions = {
'not working': ['broken', 'failing', 'error', 'issue'],
'works now': ['fixed', 'resolved', 'solution', 'working'],
'found solution': ['solved', 'fixed', 'resolved', 'workaround'],
'still broken': ['not fixed', 'ongoing issue', 'unresolved'],
'works with': ['compatible', 'functions with', 'successful using']
};
}
/**
* Expand a query with synonyms, related terms, and corrections
*/
expandQuery(query) {
const originalTerms = this.tokenize(query);
const expandedTerms = new Set(originalTerms);
// Add original query
expandedTerms.add(query.toLowerCase());
// Process each term
originalTerms.forEach(term => {
const lowerTerm = term.toLowerCase();
// Add typo corrections
if (this.typoCorrections[lowerTerm]) {
expandedTerms.add(this.typoCorrections[lowerTerm]);
}
// Add synonyms
if (this.synonymMap[lowerTerm]) {
this.synonymMap[lowerTerm].forEach(synonym => expandedTerms.add(synonym));
}
// Check if term is in any synonym list (reverse lookup)
Object.entries(this.synonymMap).forEach(([key, synonyms]) => {
if (synonyms.includes(lowerTerm)) {
expandedTerms.add(key);
synonyms.forEach(s => expandedTerms.add(s));
}
});
// Expand acronyms
if (this.acronyms[lowerTerm]) {
expandedTerms.add(this.acronyms[lowerTerm]);
}
// Check if term is an expansion of an acronym
Object.entries(this.acronyms).forEach(([acronym, expansion]) => {
if (expansion.includes(lowerTerm)) {
expandedTerms.add(acronym);
}
});
});
// Check for contextual phrases
Object.entries(this.contextualExpansions).forEach(([phrase, expansions]) => {
if (query.toLowerCase().includes(phrase)) {
expansions.forEach(expansion => expandedTerms.add(expansion));
}
});
// Generate variations
this.generateVariations(query).forEach(variation => expandedTerms.add(variation));
return Array.from(expandedTerms);
}
/**
* Tokenize query into meaningful terms
*/
tokenize(query) {
// Split by spaces and common delimiters, filter out small words
return query.toLowerCase()
.split(/[\s\-_.,;:!?()[\]{}'"]+/)
.filter(term => term.length > 2 || ['ui', 'ci', 'cd', 'pr', 'db'].includes(term));
}
/**
* Generate variations of the query
*/
generateVariations(query) {
const variations = [];
const lower = query.toLowerCase();
// Hyphenated vs non-hyphenated
if (lower.includes('-')) {
variations.push(lower.replace(/-/g, ' '));
variations.push(lower.replace(/-/g, ''));
} else if (lower.includes(' ')) {
variations.push(lower.replace(/ /g, '-'));
variations.push(lower.replace(/ /g, ''));
}
// Underscore variations
if (lower.includes('_')) {
variations.push(lower.replace(/_/g, ' '));
variations.push(lower.replace(/_/g, '-'));
}
// CamelCase to space separated
const camelCaseWords = query.match(/[A-Z][a-z]+/g);
if (camelCaseWords && camelCaseWords.length > 1) {
variations.push(camelCaseWords.join(' ').toLowerCase());
variations.push(camelCaseWords.join('-').toLowerCase());
}
return variations;
}
/**
* Calculate similarity between two strings (for fuzzy matching)
*/
calculateSimilarity(str1, str2) {
const longer = str1.length > str2.length ? str1 : str2;
const shorter = str1.length > str2.length ? str2 : str1;
if (longer.length === 0) return 1.0;
const editDistance = this.levenshteinDistance(longer, shorter);
return (longer.length - editDistance) / longer.length;
}
/**
* Levenshtein distance for fuzzy matching
*/
levenshteinDistance(str1, str2) {
const matrix = [];
for (let i = 0; i <= str2.length; i++) {
matrix[i] = [i];
}
for (let j = 0; j <= str1.length; j++) {
matrix[0][j] = j;
}
for (let i = 1; i <= str2.length; i++) {
for (let j = 1; j <= str1.length; j++) {
if (str2.charAt(i - 1) === str1.charAt(j - 1)) {
matrix[i][j] = matrix[i - 1][j - 1];
} else {
matrix[i][j] = Math.min(
matrix[i - 1][j - 1] + 1, // substitution
matrix[i][j - 1] + 1, // insertion
matrix[i - 1][j] + 1 // deletion
);
}
}
}
return matrix[str2.length][str1.length];
}
/**
* Get search suggestions based on query
*/
getSuggestions(query, availableTerms = []) {
const suggestions = new Set();
const queryLower = query.toLowerCase();
// Direct matches from available terms
availableTerms.forEach(term => {
if (term.toLowerCase().includes(queryLower) ||
this.calculateSimilarity(term.toLowerCase(), queryLower) > 0.8) {
suggestions.add(term);
}
});
// Add corrections
if (this.typoCorrections[queryLower]) {
suggestions.add(this.typoCorrections[queryLower]);
}
// Add synonym suggestions
if (this.synonymMap[queryLower]) {
this.synonymMap[queryLower].forEach(s => suggestions.add(s));
}
return Array.from(suggestions).slice(0, 5); // Top 5 suggestions
}
/**
* Analyze query intent
*/
analyzeIntent(query) {
const lower = query.toLowerCase();
// Determine if looking for problems or solutions
const problemIndicators = ['error', 'failing', 'broken', 'issue', 'problem', 'crash', 'not working'];
const solutionIndicators = ['working', 'fixed', 'solution', 'resolved', 'successful', 'works'];
const isProblemSearch = problemIndicators.some(indicator => lower.includes(indicator));
const isSolutionSearch = solutionIndicators.some(indicator => lower.includes(indicator));
// Determine technical context
const contexts = [];
if (lower.includes('dxt') || lower.includes('desktop')) contexts.push('dxt');
if (lower.includes('mcp') || lower.includes('protocol')) contexts.push('mcp');
if (lower.includes('json') || lower.includes('rpc')) contexts.push('jsonrpc');
if (lower.includes('python') || lower.includes('py')) contexts.push('python');
if (lower.includes('node') || lower.includes('npm')) contexts.push('nodejs');
return {
type: isProblemSearch ? 'problem' : isSolutionSearch ? 'solution' : 'general',
contexts,
confidence: (isProblemSearch || isSolutionSearch) ? 0.8 : 0.5
};
}
/**
* Rank search results based on query relevance
*/
rankResults(results, originalQuery, expandedQueries) {
return results.map(result => {
let score = 0;
const content = result.content.toLowerCase();
// Exact match bonus
if (content.includes(originalQuery.toLowerCase())) {
score += 10;
}
// Expanded query matches
expandedQueries.forEach(expanded => {
if (content.includes(expanded.toLowerCase())) {
score += 5;
}
});
// Title/summary match bonus
if (result.title && result.title.toLowerCase().includes(originalQuery.toLowerCase())) {
score += 8;
}
// Recent items bonus
const daysSinceCreated = (Date.now() - new Date(result.created).getTime()) / (1000 * 60 * 60 * 24);
if (daysSinceCreated < 7) score += 3;
if (daysSinceCreated < 30) score += 1;
// High priority bonus
if (result.priority === 'high') score += 2;
return { ...result, relevanceScore: score };
}).sort((a, b) => b.relevanceScore - a.relevanceScore);
}
/**
* Classify a query into one or more types with confidence scores
* Claude-historian inspired classification system
*/
classify(query) {
if (!query || typeof query !== 'string') {
return {
types: ['general'],
primary: 'general',
confidence: 0.5,
features: {}
};
}
const normalizedQuery = query.toLowerCase();
const scores = {};
const features = this.extractQueryFeatures(query);
// Score each query type
for (const [type, config] of Object.entries(this.patterns)) {
scores[type] = this.scoreQueryType(normalizedQuery, config, features);
}
// Normalize scores
const totalScore = Object.values(scores).reduce((sum, score) => sum + score, 0);
const normalizedScores = {};
for (const [type, score] of Object.entries(scores)) {
normalizedScores[type] = totalScore > 0 ? score / totalScore : 0;
}
// Get primary type and confidence
const sortedTypes = Object.entries(normalizedScores)
.sort(([, a], [, b]) => b - a)
.filter(([, score]) => score > 0.1);
const primary = sortedTypes[0]?.[0] || 'general';
const confidence = sortedTypes[0]?.[1] || 0.5;
return {
types: sortedTypes.map(([type]) => type),
primary,
confidence,
scores: normalizedScores,
features,
suggestions: this.generateSearchSuggestions(primary, features),
searchParameters: this.getSearchParameters(primary)
};
}
/**
* Extract features from the query for classification
*/
extractQueryFeatures(query) {
return {
hasQuestionMark: query.includes('?'),
hasQuotes: /"[^"]*"|'[^']*'/.test(query),
isCommand: query.startsWith('$') || query.startsWith('>'),
wordCount: query.split(/\s+/).length,
hasPath: /[\/\\]/.test(query),
hasFileExtension: /\.\w{2,4}($|\s)/.test(query),
hasErrorIndicator: /error|exception|fail/i.test(query),
isRecent: /recent|latest|last|today|yesterday/i.test(query)
};
}
/**
* Score a query for a specific type
*/
scoreQueryType(query, config, features) {
let score = 0;
// Check keywords
for (const keyword of config.keywords) {
if (query.includes(keyword)) {
score += config.weight;
}
}
// Check patterns
for (const pattern of config.patterns) {
if (pattern.test(query)) {
score += config.weight * 1.5; // Patterns are stronger indicators
}
}
// Apply feature boosts
if (features.hasQuestionMark && ['concept', 'error'].includes(config.type)) {
score *= this.boostFactors.questionMark;
}
if (features.hasQuotes) {
score *= this.boostFactors.quoted;
}
return score;
}
/**
* Generate search suggestions based on query classification
*/
generateSearchSuggestions(primaryType, features) {
const suggestions = {
filters: [],
sortBy: 'relevance',
includeRelated: true,
searchStrategy: 'standard'
};
// Use features to enhance suggestions
if (features.hasQuotes) {
suggestions.exactMatch = true;
}
if (features.isRecent) {
suggestions.sortBy = 'timestamp';
}
switch (primaryType) {
case 'error':
suggestions.filters.push({ field: 'category', value: 'code' });
suggestions.filters.push({ field: 'content', contains: 'solution' });
suggestions.sortBy = 'relevance';
suggestions.searchStrategy = 'problem-solution';
break;
case 'recent':
suggestions.sortBy = 'timestamp';
suggestions.filters.push({ field: 'timestamp', range: 'last7days' });
suggestions.searchStrategy = 'chronological';
break;
case 'code':
suggestions.filters.push({ field: 'content_type', value: 'code' });
suggestions.includeRelated = true;
suggestions.searchStrategy = 'technical';
break;
case 'file':
suggestions.filters.push({ field: 'content', pattern: 'path' });
suggestions.searchStrategy = 'exact-match';
break;
case 'concept':
suggestions.includeRelated = true;
suggestions.searchStrategy = 'semantic';
break;
}
return suggestions;
}
/**
* Get search parameters optimized for the query type
*/
getSearchParameters(primaryType) {
const params = {
limit: 20,
offset: 0,
includeMetadata: true,
scoreThreshold: 0.3
};
// Adjust parameters based on query type
switch (primaryType) {
case 'error':
params.limit = 10; // Fewer but more relevant results
params.scoreThreshold = 0.5; // Higher threshold for relevance
break;
case 'recent':
params.limit = 30; // More results for browsing
params.scoreThreshold = 0.2; // Lower threshold to include more
break;
case 'code':
params.includeCodeContext = true;
params.limit = 15;
break;
}
return params;
}
}
export default QueryIntelligence;