UNPKG

@tosin2013/mcp-shrimp-task-manager

Version:

Enhanced MCP Shrimp Task Manager with comprehensive LLM integration. A task management tool built for AI Agents, emphasizing chain-of-thought, reflection, and style consistency. Features real GPT-4 ↔ MCP tools communication, comprehensive testing pipeline

227 lines 8.61 kB
/** * Project Rules Integrator for the Idea Honing Tool * * This component integrates with the project rules system to apply relevant rules * to specifications and generate rule suggestions. */ import fsPromises from 'fs/promises'; import path from 'path'; import { fileURLToPath } from 'url'; // Get the base directory for project rules const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const PROJECT_ROOT = path.resolve(__dirname, '../../../..'); const DATA_DIR = process.env.DATA_DIR || path.join(PROJECT_ROOT, 'data'); const RULES_DIR = path.join(DATA_DIR, 'project-rules'); /** * Retrieves all project rules * * @returns Promise that resolves with an array of project rules */ export async function getAllProjectRules() { try { // Check if the rules directory exists try { await fsPromises.access(RULES_DIR); } catch { console.warn(`Project rules directory not found: ${RULES_DIR}`); return []; } // Read the rules file const rulesPath = path.join(RULES_DIR, 'rules.json'); try { await fsPromises.access(rulesPath); } catch { console.warn(`Project rules file not found: ${rulesPath}`); return []; } // Read and parse the rules file const rulesContent = await fsPromises.readFile(rulesPath, 'utf-8'); const rules = JSON.parse(rulesContent); return Array.isArray(rules) ? rules : []; } catch (error) { console.error(`Error retrieving project rules: ${error}`); return []; } } /** * Determines which rules are relevant to a specification * * @param specification - Specification document * @param rules - Project rules * @returns Array of relevant rules */ export function determineRelevantRules(specification, rules) { const relevantRules = []; // Extract keywords from the specification const specKeywords = extractKeywordsFromSpecification(specification); // Process each rule for (const rule of rules) { // Check if any rule keywords match the specification keywords const matchingKeywords = rule.keywords.filter(keyword => specKeywords.some(specKeyword => specKeyword.toLowerCase().includes(keyword.toLowerCase()) || keyword.toLowerCase().includes(specKeyword.toLowerCase()))); if (matchingKeywords.length > 0) { // Determine which section this rule is most relevant to const sectionId = determineMostRelevantSection(rule, specification.sections); // Create the relevant rule const relevantRule = { id: rule.id, description: rule.description, relevance: `Matched keywords: ${matchingKeywords.join(', ')}`, sectionId }; relevantRules.push(relevantRule); } } // Sort rules by priority return relevantRules.sort((a, b) => { const ruleA = rules.find(r => r.id === a.id); const ruleB = rules.find(r => r.id === b.id); if (!ruleA || !ruleB) return 0; return ruleA.priority - ruleB.priority; }); } /** * Extracts keywords from a specification * * @param specification - Specification document * @returns Array of keywords */ function extractKeywordsFromSpecification(specification) { const keywords = []; // Add keywords from the title keywords.push(...extractKeywordsFromText(specification.title)); // Add keywords from each section for (const section of specification.sections) { keywords.push(...extractKeywordsFromText(section.content)); } // Add keywords from metadata tags if (specification.metadata.tags) { keywords.push(...specification.metadata.tags); } // Remove duplicates return [...new Set(keywords)]; } /** * Extracts keywords from text * * @param text - Text to extract keywords from * @returns Array of keywords */ function extractKeywordsFromText(text) { // Split the text into words const words = text.toLowerCase() .replace(/[^\w\s]/g, ' ') // Replace punctuation with spaces .split(/\s+/) // Split on whitespace .filter(word => word.length > 3); // Filter out short words // Remove common words const commonWords = ['the', 'and', 'for', 'with', 'that', 'this', 'from', 'will', 'have', 'been']; const filteredWords = words.filter(word => !commonWords.includes(word)); // Get unique words return [...new Set(filteredWords)]; } /** * Determines the most relevant section for a rule * * @param rule - Project rule * @param sections - Specification sections * @returns ID of the most relevant section */ function determineMostRelevantSection(rule, sections) { // Map rule categories to section titles const categoryToSectionMap = { 'code': ['Technical Design', 'Architecture', 'Components', 'Data Flow', 'API Design'], 'documentation': ['Overview', 'Documentation'], 'testing': ['Acceptance Criteria', 'Testing'], 'security': ['Non-Functional Requirements', 'Security'], 'performance': ['Non-Functional Requirements', 'Performance'], 'accessibility': ['Non-Functional Requirements', 'Accessibility'], 'ux': ['Functional Requirements', 'User Experience'], 'general': ['Implementation Constraints'] }; // Get potential section titles for this rule category const potentialSectionTitles = categoryToSectionMap[rule.category] || categoryToSectionMap['general']; // Find matching sections const matchingSections = sections.filter(section => potentialSectionTitles.some(title => section.title.toLowerCase().includes(title.toLowerCase()))); // Return the first matching section ID, or the first section ID if no matches return matchingSections.length > 0 ? matchingSections[0].id : (sections[0]?.id || 'overview'); } /** * Applies rules to a specification * * @param specification - Specification document * @param relevantRules - Relevant rules * @returns Updated specification with rule suggestions */ export function applyRulesToSpecification(specification, relevantRules) { // Clone the specification const updatedSpecification = { ...specification }; // Group rules by section const rulesBySection = {}; for (const rule of relevantRules) { if (!rulesBySection[rule.sectionId]) { rulesBySection[rule.sectionId] = []; } rulesBySection[rule.sectionId].push(rule); } // Update each section with rule suggestions updatedSpecification.sections = specification.sections.map(section => { const sectionRules = rulesBySection[section.id] || []; if (sectionRules.length === 0) { return section; } // Clone the section const updatedSection = { ...section }; // Add rule suggestions to the section content updatedSection.content = addRuleSuggestionsToContent(section.content, sectionRules); return updatedSection; }); return updatedSpecification; } /** * Adds rule suggestions to section content * * @param content - Section content * @param rules - Relevant rules for the section * @returns Updated content with rule suggestions */ function addRuleSuggestionsToContent(content, rules) { if (rules.length === 0) { return content; } // Add a rule suggestions section let updatedContent = content; // Check if the content already has a rule suggestions section if (!updatedContent.includes('### Rule Suggestions')) { updatedContent += '\n\n### Rule Suggestions\n\n'; // Add each rule suggestion for (const rule of rules) { updatedContent += `- **${rule.id}**: ${rule.description} (${rule.relevance})\n`; } } return updatedContent; } /** * Generates rule suggestions based on a specification * * @param specification - Specification document * @returns Promise that resolves with an array of rule suggestions */ export async function generateRuleSuggestions(specification) { try { // Get all project rules const rules = await getAllProjectRules(); // Determine relevant rules const relevantRules = determineRelevantRules(specification, rules); return relevantRules; } catch (error) { console.error(`Error generating rule suggestions: ${error}`); return []; } } //# sourceMappingURL=project-rules-integrator.js.map