Files
english/.opencode/skills/docs-seeker/scripts/detect-topic.js
2026-04-12 01:06:31 +07:00

173 lines
4.4 KiB
JavaScript
Executable File

#!/usr/bin/env node
/**
* Topic Detection Script
* Analyzes user queries to extract library name and topic keywords
* Returns null for general queries, topic info for specific queries
*/
const { loadEnv } = require('./utils/env-loader');
// Load environment
const env = loadEnv();
const DEBUG = env.DEBUG === 'true';
/**
* Topic-specific query patterns
*/
const TOPIC_PATTERNS = [
// "How do I use X in Y?"
/how (?:do i|to|can i) (?:use|implement|add|setup|configure) (?:the )?(.+?) (?:in|with|for) (.+)/i,
// "Y X strategies/patterns" - e.g., "Next.js caching strategies"
/(.+?) (.+?) (?:strategies|patterns|techniques|methods|approaches)/i,
// "X Y documentation" or "Y X docs"
/(.+?) (.+?) (?:documentation|docs|guide|tutorial)/i,
// "Using X with Y"
/using (.+?) (?:with|in|for) (.+)/i,
// "Y X guide/implementation/setup"
/(.+?) (.+?) (?:guide|implementation|setup|configuration)/i,
// "Implement X in Y"
/implement(?:ing)? (.+?) (?:in|with|for|using) (.+)/i,
];
/**
* General library query patterns (non-topic specific)
*/
const GENERAL_PATTERNS = [
/(?:documentation|docs) for (.+)/i,
/(.+?) (?:getting started|quick ?start|introduction)/i,
/(?:how to use|learn) (.+)/i,
/(.+?) (?:api reference|overview|basics)/i,
];
/**
* Normalize topic keyword
* @param {string} topic - Raw topic string
* @returns {string} Normalized topic keyword
*/
function normalizeTopic(topic) {
return topic
.toLowerCase()
.trim()
.replace(/[^a-z0-9\s-]/g, '') // Remove special chars
.replace(/\s+/g, '-') // Replace spaces with hyphens
.split('-')[0] // Take first word for multi-word topics
.slice(0, 20); // Limit length
}
/**
* Normalize library name
* @param {string} library - Raw library string
* @returns {string} Normalized library name
*/
function normalizeLibrary(library) {
return library
.toLowerCase()
.trim()
.replace(/[^a-z0-9\s\-\/\.]/g, '')
.replace(/\s+/g, '-');
}
/**
* Detect if query is topic-specific or general
* @param {string} query - User query
* @returns {Object|null} Topic info or null for general query
*/
function detectTopic(query) {
if (!query || typeof query !== 'string') {
return null;
}
const trimmedQuery = query.trim();
// Check general patterns first
for (const pattern of GENERAL_PATTERNS) {
const match = trimmedQuery.match(pattern);
if (match) {
if (DEBUG) console.error('[DEBUG] Matched general pattern, no topic');
return null;
}
}
// Check topic-specific patterns
for (let i = 0; i < TOPIC_PATTERNS.length; i++) {
const pattern = TOPIC_PATTERNS[i];
const match = trimmedQuery.match(pattern);
if (match) {
const [, term1, term2] = match;
// Determine which is library and which is topic based on pattern
let topic, library;
// Pattern 0: "How do I use X in Y?" -> X is topic, Y is library
// Pattern 1: "Y X strategies" -> X is topic, Y is library
// Pattern 2-5: X is topic, Y is library in most cases
// For pattern 1 (strategies/patterns), term1 is library, term2 is topic
if (i === 1) {
topic = normalizeTopic(term2);
library = normalizeLibrary(term1);
} else {
// For other patterns, term1 is topic, term2 is library
topic = normalizeTopic(term1);
library = normalizeLibrary(term2);
}
if (DEBUG) {
console.error('[DEBUG] Matched topic pattern');
console.error('[DEBUG] Topic:', topic);
console.error('[DEBUG] Library:', library);
}
return {
query: trimmedQuery,
topic,
library,
isTopicSpecific: true,
};
}
}
if (DEBUG) console.error('[DEBUG] No pattern matched, treating as general');
return null;
}
/**
* CLI entry point
*/
function main() {
const args = process.argv.slice(2);
if (args.length === 0) {
console.error('Usage: node detect-topic.js "<user query>"');
process.exit(1);
}
const query = args.join(' ');
const result = detectTopic(query);
if (result) {
console.log(JSON.stringify(result, null, 2));
process.exit(0);
} else {
console.log(JSON.stringify({ isTopicSpecific: false }, null, 2));
process.exit(0);
}
}
// Run if called directly
if (require.main === module) {
main();
}
module.exports = {
detectTopic,
normalizeTopic,
normalizeLibrary,
};