# Directory Structure ``` ├── .gitignore ├── package-lock.json ├── package.json ├── README.md ├── src │ ├── index.ts │ ├── mcp │ │ └── memoryBankMcp.ts │ ├── templates │ │ ├── .byterules │ │ ├── activeContext.md │ │ ├── productContext.md │ │ ├── progress.md │ │ ├── projectbrief.md │ │ ├── systemPatterns.md │ │ └── techContext.md │ └── utils │ ├── cursorRulesGenerator.ts │ ├── fileManager.ts │ └── gemini.ts └── tsconfig.json ``` # Files -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- ``` 1 | # Node.js 2 | node_modules/ 3 | npm-debug.log 4 | yarn-debug.log 5 | yarn-error.log 6 | 7 | # Derleme ve dağıtım 8 | dist/ 9 | build/ 10 | *.tsbuildinfo 11 | 12 | # Çevre değişkenleri 13 | .env 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | # Memory Bank verileri 20 | memory-bank/ 21 | memory-bank-export/ 22 | memory-bank-export.json 23 | 24 | # Editör ve IDE 25 | .vscode/ 26 | .idea/ 27 | *.swp 28 | *.swo 29 | 30 | # İşletim sistemi 31 | .DS_Store 32 | Thumbs.db 33 | 34 | # Log dosyaları 35 | logs/ 36 | *.log 37 | npm-debug.log* 38 | yarn-debug.log* 39 | yarn-error.log* ``` -------------------------------------------------------------------------------- /src/templates/.byterules: -------------------------------------------------------------------------------- ``` 1 | # Memory Bank Document Orchestration Rules 2 | 3 | ## Document Types and Purposes 4 | 5 | ### 1. Project Brief (projectbrief.md) 6 | - **Purpose**: Acts as the project's foundation document defining key objectives 7 | - **When to Update**: At project initiation and when scope changes 8 | - **Structure**: 9 | - Project Vision & Goals 10 | - Key Stakeholders 11 | - Success Metrics 12 | - Constraints & Limitations 13 | - Timeline Overview 14 | - **Commands**: 15 | - `update_document projectbrief` - Update project brief details 16 | - `query_memory_bank "project goals"` - Find project goal information 17 | 18 | ### 2. Product Context (productContext.md) 19 | - **Purpose**: Details product functionality, user experience, and market position 20 | - **When to Update**: When features change or market requirements shift 21 | - **Structure**: 22 | - User Personas 23 | - Feature List & Priorities 24 | - User Stories 25 | - Competitive Analysis 26 | - Product Roadmap 27 | - **Commands**: 28 | - `update_document productContext` - Update product information 29 | - `query_memory_bank "features"` - Find feature-related information 30 | 31 | ### 3. System Patterns (systemPatterns.md) 32 | - **Purpose**: Documents system architecture and design decisions 33 | - **When to Update**: When architectural decisions are made or changed 34 | - **Structure**: 35 | - System Architecture 36 | - Component Diagrams 37 | - Design Patterns Used 38 | - Integration Points 39 | - Data Flow 40 | - **Commands**: 41 | - `update_document systemPatterns` - Update system architecture information 42 | - `query_memory_bank "architecture"` - Find architecture information 43 | 44 | ### 4. Tech Context (techContext.md) 45 | - **Purpose**: Technical details, stack choices, and tooling decisions 46 | - **When to Update**: When technology decisions are made or changed 47 | - **Structure**: 48 | - Technology Stack 49 | - Development Environment 50 | - Deployment Process 51 | - Performance Considerations 52 | - Technical Debt 53 | - **Commands**: 54 | - `update_document techContext` - Update technology information 55 | - `query_memory_bank "stack"` - Find technology stack information 56 | 57 | ### 5. Active Context (activeContext.md) 58 | - **Purpose**: Current tasks, open questions, and active development 59 | - **When to Update**: Daily or when switching focus areas 60 | - **Structure**: 61 | - Current Sprint Goals 62 | - Active Tasks 63 | - Blockers & Challenges 64 | - Decisions Needed 65 | - Next Actions 66 | - **Commands**: 67 | - `update_document activeContext` - Update current work information 68 | - `query_memory_bank "current tasks"` - Find information about active work 69 | 70 | ### 6. Progress (progress.md) 71 | - **Purpose**: Progress tracking and milestone documentation 72 | - **When to Update**: After completing tasks or reaching milestones 73 | - **Structure**: 74 | - Milestones Achieved 75 | - Current Progress Status 76 | - Sprint/Cycle History 77 | - Learnings & Adjustments 78 | - Next Milestones 79 | - **Commands**: 80 | - `update_document progress` - Update project progress information 81 | - `query_memory_bank "milestone"` - Find milestone information 82 | 83 | ## Document Workflow Processes 84 | 85 | ### Project Initialization 86 | 1. Create project brief with clear goals 87 | 2. Define product context based on brief 88 | 3. Establish initial system patterns 89 | 4. Document technology decisions 90 | 5. Set up initial active context and progress tracking 91 | 92 | ### Feature Development Cycle 93 | 1. Update active context with new feature details 94 | 2. Reference system patterns for implementation guidance 95 | 3. Document technical decisions in tech context 96 | 4. Update progress when feature is completed 97 | 5. Ensure product context reflects new capabilities 98 | 99 | ### Project Review Process 100 | 1. Review progress against project brief goals 101 | 2. Validate system patterns match implementation 102 | 3. Update product context with feedback/learnings 103 | 4. Document technical challenges in tech context 104 | 5. Set new goals in active context 105 | 106 | ## Best Practices 107 | 108 | ### Document Maintenance 109 | - Keep documents concise and focused 110 | - Use markdown formatting for readability 111 | - Include diagrams where appropriate (store in resources/) 112 | - Link between documents when referencing related content 113 | - Update documents regularly based on the workflow process 114 | 115 | ### Collaboration Guidelines 116 | - Review document changes with team members 117 | - Hold regular sync meetings to update active context 118 | - Use version control for tracking document history 119 | - Maintain changelog entries in progress.md 120 | - Cross-reference documents to maintain consistency 121 | 122 | ## Command Reference 123 | - `initialize_memory_bank` - Create a new Memory Bank structure 124 | - `update_document <docType>` - Update specific document content 125 | - `query_memory_bank <query>` - Search across all documents 126 | - `export_memory_bank` - Export current Memory Bank state 127 | 128 | ## Document Integration Flow 129 | Project Brief → Product Context → System Patterns → Tech Context → Active Context → Progress 130 | 131 | Follow this integration flow to ensure proper document orchestration and maintain project coherence. 132 | ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- ```markdown 1 | # Memory Bank MCP 2 | 3 | <div align="center"> 4 | <img src="https://github.com/tuncer-byte/byte/blob/main/media/icons/icon-white.png" height="128"> 5 | <h1>Memory Bank MCP</h1> 6 | <p> 7 | <b>Structured project knowledge management for LLMs via Model Context Protocol (MCP)</b> 8 | </p> 9 | </div> 10 | 11 | <a href="https://glama.ai/mcp/servers/@tuncer-byte/memory-bank-MCP"> 12 | <img width="380" height="200" src="https://glama.ai/mcp/servers/@tuncer-byte/memory-bank-MCP/badge" alt="Memory Bank MCP server" /> 13 | </a> 14 | 15 | --- 16 | 17 | > **Note:** This is not a traditional Node.js application. Memory Bank MCP is an **MCP server**—a component in the [Model Context Protocol](https://modelcontextprotocol.io/introduction) ecosystem. It exposes project knowledge to LLM-powered agents and tools using a standardized protocol, enabling seamless integration with AI clients (e.g., Claude Desktop, IDEs, or custom LLM agents). 18 | 19 | --- 20 | 21 | ## What is Model Context Protocol (MCP)? 22 | 23 | MCP is an open protocol that standardizes how applications provide context to LLMs. Think of MCP like a USB-C port for AI: it provides a universal way to connect AI models to data sources and tools, both locally and remotely. MCP enables: 24 | 25 | - **Plug-and-play integrations** between LLMs, data, and tools 26 | - **Switching between LLM providers** with minimal friction 27 | - **Secure, modular architecture** for building AI workflows 28 | 29 | Learn more: [MCP Introduction](https://modelcontextprotocol.io/introduction) 30 | 31 | ## About Memory Bank MCP 32 | 33 | Memory Bank MCP is an **MCP server** that helps teams create, manage, and access structured project documentation. It generates and maintains interconnected Markdown documents capturing all aspects of project knowledge, from high-level goals to technical details and daily progress. It is designed to be accessed by MCP-compatible clients and LLM agents. 34 | 35 | ## Features 36 | 37 | - **AI-Generated Documentation**: Uses Gemini API to generate and update project documentation 38 | - **Structured Knowledge System**: Maintains six core document types in a hierarchical structure 39 | - **MCP Server**: Implements the Model Context Protocol for integration with LLM agents and tools 40 | - **Customizable Storage**: Choose where your Memory Bank directory is created 41 | - **Document Templates**: Pre-defined templates for project brief, product context, system patterns, etc. 42 | - **AI-Assisted Updates**: Update documents manually or regenerate them with AI 43 | - **Advanced Querying**: Search across all documents with context-aware relevance ranking 44 | 45 | ## Installation 46 | 47 | ```bash 48 | # Clone the repository 49 | git clone https://github.com/tuncer-byte/memory-bank-mcp.git 50 | cd memory-bank-mcp 51 | 52 | # Install dependencies 53 | npm install 54 | 55 | # (Optional) Create .env file with your Gemini API key 56 | echo "GEMINI_API_KEY=your_api_key_here" > .env 57 | ``` 58 | 59 | ## Usage 60 | 61 | > **Note:** Memory Bank MCP is intended to be run as an MCP server, not as a standalone app. You typically launch it as part of an MCP workflow, and connect to it from an MCP-compatible client (such as Claude Desktop or your own LLM agent). 62 | 63 | ### Development Mode 64 | 65 | ```bash 66 | npm run dev 67 | ``` 68 | 69 | ### Production Mode 70 | 71 | ```bash 72 | npm run build 73 | npm run start 74 | ``` 75 | 76 | ### MCP Integration 77 | 78 | To connect Memory Bank MCP to your MCP client, add the following to your `mcp.json` configuration: 79 | 80 | ```json 81 | { 82 | "memoryBank": { 83 | "command": "node", 84 | "args": ["/path/to/memory-bank-mcp/dist/index.js"], 85 | "env": { 86 | "GEMINI_API_KEY": "your_gemini_api_key_here" 87 | } 88 | } 89 | } 90 | ``` 91 | 92 | Replace `/path/to/memory-bank-mcp/dist/index.js` with the absolute path to your built file, and add your Gemini API key if needed. 93 | 94 | --- 95 | 96 | ## MCP Tools Exposed by Memory Bank 97 | 98 | Memory Bank MCP provides the following tools via the Model Context Protocol: 99 | 100 | ### `initialize_memory_bank` 101 | 102 | Creates a new Memory Bank structure with all document templates. 103 | 104 | **Parameters:** 105 | - `goal` (string): Project goal description (min 10 characters) 106 | - `geminiApiKey` (string, optional): Gemini API key for document generation 107 | - `location` (string, optional): Absolute path where memory-bank folder will be created 108 | 109 | **Example:** 110 | ```javascript 111 | await callTool({ 112 | name: "initialize_memory_bank", 113 | arguments: { 114 | goal: "Building a self-documenting AI-powered software development assistant", 115 | location: "/Users/username/Documents/projects/ai-assistant" 116 | } 117 | }); 118 | ``` 119 | 120 | ### `update_document` 121 | 122 | Updates a specific document in the Memory Bank. 123 | 124 | **Parameters:** 125 | - `documentType` (enum): One of: `projectbrief`, `productContext`, `systemPatterns`, `techContext`, `activeContext`, `progress` 126 | - `content` (string, optional): New content for the document 127 | - `regenerate` (boolean, default: false): Whether to regenerate the document using AI 128 | 129 | **Example:** 130 | ```javascript 131 | await callTool({ 132 | name: "update_document", 133 | arguments: { 134 | documentType: "projectbrief", 135 | content: "# Project Brief\n\n## Purpose\nTo develop an advanced and user-friendly AI..." 136 | } 137 | }); 138 | ``` 139 | 140 | ### `query_memory_bank` 141 | 142 | Searches across all documents with context-aware relevance ranking. 143 | 144 | **Parameters:** 145 | - `query` (string): Search query (min 5 characters) 146 | 147 | **Example:** 148 | ```javascript 149 | await callTool({ 150 | name: "query_memory_bank", 151 | arguments: { 152 | query: "system architecture components" 153 | } 154 | }); 155 | ``` 156 | 157 | ### `export_memory_bank` 158 | 159 | Exports all Memory Bank documents. 160 | 161 | **Parameters:** 162 | - `format` (enum, default: "folder"): Export format, either "json" or "folder" 163 | - `outputPath` (string, optional): Custom output path for the export 164 | 165 | **Example:** 166 | ```javascript 167 | await callTool({ 168 | name: "export_memory_bank", 169 | arguments: { 170 | format: "json", 171 | outputPath: "/Users/username/Documents/exports" 172 | } 173 | }); 174 | ``` 175 | 176 | ## Document Types 177 | 178 | Memory Bank organizes project knowledge into six core document types: 179 | 180 | 1. **Project Brief** (`projectbrief.md`): Core document defining project objectives, scope, and vision 181 | 2. **Product Context** (`productContext.md`): Documents product functionality from a user perspective 182 | 3. **System Patterns** (`systemPatterns.md`): Establishes system architecture and component relationships 183 | 4. **Tech Context** (`techContext.md`): Specifies technology stack and implementation details 184 | 5. **Active Context** (`activeContext.md`): Tracks current tasks, open issues, and development focus 185 | 6. **Progress** (`progress.md`): Documents completed work, milestones, and project history 186 | 187 | ## License 188 | 189 | MIT ``` -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "module": "NodeNext", 5 | "moduleResolution": "NodeNext", 6 | "esModuleInterop": true, 7 | "outDir": "./dist", 8 | "rootDir": "./src", 9 | "strict": true, 10 | "declaration": true, 11 | "resolveJsonModule": true 12 | }, 13 | "include": ["src/**/*"], 14 | "exclude": ["node_modules", "**/*.test.ts"] 15 | } ``` -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { startServer } from './mcp/memoryBankMcp.js'; 2 | 3 | // Ana fonksiyon 4 | async function main() { 5 | console.log('Memory Bank MCP uygulaması başlatılıyor...'); 6 | 7 | try { 8 | // MCP sunucusunu başlat 9 | await startServer(); 10 | } catch (error) { 11 | console.error('Hata:', error); 12 | process.exit(1); 13 | } 14 | } 15 | 16 | // Uygulamayı başlat 17 | main().catch(error => { 18 | console.error('Kritik hata:', error); 19 | process.exit(1); 20 | }); ``` -------------------------------------------------------------------------------- /src/templates/projectbrief.md: -------------------------------------------------------------------------------- ```markdown 1 | # Project Brief 2 | 3 | ## Project Purpose 4 | {{projectPurpose}} 5 | 6 | ## Core Objectives 7 | {{projectGoals}} 8 | 9 | ## Target Audience 10 | {{targetAudience}} 11 | 12 | ## Key Features 13 | {{keyFeatures}} 14 | 15 | ## Success Criteria 16 | {{successCriteria}} 17 | 18 | ## Timeline 19 | {{timeline}} 20 | 21 | ## Project Scope 22 | {{projectScope}} 23 | 24 | ## Stakeholders 25 | {{stakeholders}} 26 | 27 | ## Dependencies 28 | {{dependencies}} 29 | 30 | ## Constraints 31 | {{constraints}} 32 | 33 | --- 34 | *This document was created by AI on {{date}} and serves as the foundation for all Memory Bank documentation.* ``` -------------------------------------------------------------------------------- /src/templates/productContext.md: -------------------------------------------------------------------------------- ```markdown 1 | # Product Context 2 | 3 | ## Market Analysis 4 | {{marketAnalysis}} 5 | 6 | ## Competitive Landscape 7 | {{competitiveAnalysis}} 8 | 9 | ## User Stories 10 | {{userStories}} 11 | 12 | ## Requirements 13 | {{requirements}} 14 | 15 | ## Workflows 16 | {{workflows}} 17 | 18 | ## Product Roadmap 19 | {{roadmap}} 20 | 21 | ## Problem Statement 22 | {{problemStatement}} 23 | 24 | ## Solution Overview 25 | {{solutionOverview}} 26 | 27 | ## User Experience Goals 28 | {{uxGoals}} 29 | 30 | ## Business Model 31 | {{businessModel}} 32 | 33 | --- 34 | *This document was created by AI on {{date}} and outlines why this project exists and how it should work.* ``` -------------------------------------------------------------------------------- /src/templates/activeContext.md: -------------------------------------------------------------------------------- ```markdown 1 | # Active Context 2 | 3 | ## Current Sprint 4 | {{currentSprint}} 5 | 6 | ## Ongoing Tasks 7 | {{ongoingTasks}} 8 | 9 | ## Known Issues 10 | {{knownIssues}} 11 | 12 | ## Priorities 13 | {{priorities}} 14 | 15 | ## Next Steps 16 | {{nextSteps}} 17 | 18 | ## Meeting Notes 19 | {{meetingNotes}} 20 | 21 | ## Recent Decisions 22 | {{recentDecisions}} 23 | 24 | ## Blockers 25 | {{blockers}} 26 | 27 | ## Resource Allocation 28 | {{resourceAllocation}} 29 | 30 | ## Risk Assessment 31 | {{riskAssessment}} 32 | 33 | ## Current Focus Areas 34 | {{currentFocusAreas}} 35 | 36 | --- 37 | *This document was created by AI on {{date}} and is updated regularly to reflect the current state of the project.* ``` -------------------------------------------------------------------------------- /src/templates/progress.md: -------------------------------------------------------------------------------- ```markdown 1 | # Progress Report 2 | 3 | ## Completed Tasks 4 | {{completedTasks}} 5 | 6 | ## Milestones 7 | {{milestones}} 8 | 9 | ## Test Results 10 | {{testResults}} 11 | 12 | ## Performance Metrics 13 | {{performanceMetrics}} 14 | 15 | ## Feedback 16 | {{feedback}} 17 | 18 | ## Changelog 19 | {{changelog}} 20 | 21 | ## Quality Assurance Results 22 | {{qaResults}} 23 | 24 | ## User Adoption Metrics 25 | {{userAdoption}} 26 | 27 | ## Lessons Learned 28 | {{lessonsLearned}} 29 | 30 | ## Time Tracking 31 | {{timeTracking}} 32 | 33 | ## Achievement Highlights 34 | {{achievementHighlights}} 35 | 36 | --- 37 | *This document was created by AI on {{date}} and is updated regularly to track the project's progress and outcomes.* ``` -------------------------------------------------------------------------------- /src/templates/systemPatterns.md: -------------------------------------------------------------------------------- ```markdown 1 | # System Patterns 2 | 3 | ## Architectural Design 4 | {{architectureDesign}} 5 | 6 | ## Data Models 7 | {{dataModels}} 8 | 9 | ## API Definitions 10 | {{apiDefinitions}} 11 | 12 | ## Component Structure 13 | {{componentStructure}} 14 | 15 | ## Integration Points 16 | {{integrationPoints}} 17 | 18 | ## Scalability Strategy 19 | {{scalabilityStrategy}} 20 | 21 | ## Security Architecture 22 | {{securityArchitecture}} 23 | 24 | ## Design Patterns 25 | {{designPatterns}} 26 | 27 | ## Technical Debt 28 | {{technicalDebt}} 29 | 30 | ## System Constraints 31 | {{systemConstraints}} 32 | 33 | ## Performance Considerations 34 | {{performanceConsiderations}} 35 | 36 | --- 37 | *This document was created by AI on {{date}} and documents the system architecture and key technical decisions.* ``` -------------------------------------------------------------------------------- /src/templates/techContext.md: -------------------------------------------------------------------------------- ```markdown 1 | # Technology Context 2 | 3 | ## Technologies Used 4 | {{technologiesUsed}} 5 | 6 | ## Development Tools 7 | {{developmentTools}} 8 | 9 | ## Development Environment 10 | {{developmentEnvironment}} 11 | 12 | ## Testing Strategy 13 | {{testingStrategy}} 14 | 15 | ## Deployment Process 16 | {{deploymentProcess}} 17 | 18 | ## Continuous Integration 19 | {{continuousIntegration}} 20 | 21 | ## Infrastructure 22 | {{infrastructure}} 23 | 24 | ## Third-Party Dependencies 25 | {{thirdPartyDependencies}} 26 | 27 | ## Configuration Management 28 | {{configManagement}} 29 | 30 | ## Monitoring & Logging 31 | {{monitoringLogging}} 32 | 33 | ## Backup & Recovery 34 | {{backupRecovery}} 35 | 36 | --- 37 | *This document was created by AI on {{date}} and covers the technical foundation of the project.* ``` -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "name": "memory-bank-mcp", 3 | "version": "1.0.0", 4 | "description": "MCP tabanlı Memory Bank: metin tabanlı bilgileri depolama ve erişim aracı", 5 | "main": "dist/index.js", 6 | "type": "module", 7 | "scripts": { 8 | "build": "tsc", 9 | "start": "node dist/index.js", 10 | "dev": "ts-node src/index.ts" 11 | }, 12 | "dependencies": { 13 | "@google/generative-ai": "^0.2.0", 14 | "@modelcontextprotocol/sdk": "^1.8.0", 15 | "dotenv": "^16.3.1", 16 | "zod": "^3.22.4", 17 | "fs-extra": "^11.2.0" 18 | }, 19 | "devDependencies": { 20 | "@types/fs-extra": "^11.0.4", 21 | "@types/node": "^20.10.5", 22 | "ts-node": "^10.9.2", 23 | "typescript": "^5.3.3" 24 | }, 25 | "keywords": ["mcp", "memory-bank", "gemini", "ai"], 26 | "author": "", 27 | "license": "MIT" 28 | } ``` -------------------------------------------------------------------------------- /src/utils/gemini.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { GoogleGenerativeAI, HarmBlockThreshold, HarmCategory } from '@google/generative-ai'; 2 | import dotenv from 'dotenv'; 3 | import fs from 'fs-extra'; 4 | import path from 'path'; 5 | 6 | 7 | // Load environment variables 8 | dotenv.config(); 9 | 10 | // Default API key placeholder - replace with your actual API key 11 | const DEFAULT_API_KEY = 'YOUR_DEFAULT_API_KEY'; 12 | 13 | // Initialize API key 14 | let apiKey = process.env.GEMINI_API_KEY; 15 | 16 | console.log('Checking for Gemini API key...'); 17 | if (!apiKey) { 18 | console.error('GEMINI_API_KEY environment variable is not defined.'); 19 | 20 | try { 21 | const envPath = path.resolve(process.cwd(), '.env'); 22 | if (!fs.existsSync(envPath)) { 23 | fs.writeFileSync(envPath, `GEMINI_API_KEY=${DEFAULT_API_KEY}`, 'utf-8'); 24 | console.log('Created .env file with default API key.'); 25 | } 26 | } catch (err) { 27 | console.error('Failed to create .env file:', err); 28 | } 29 | 30 | apiKey = DEFAULT_API_KEY; 31 | console.log('Using default API key.'); 32 | } 33 | 34 | if (apiKey === 'your_gemini_api_key_here' || apiKey === 'YOUR_DEFAULT_API_KEY') { 35 | console.warn('GEMINI_API_KEY is set to the example value. Please set a valid API key in your .env file.'); 36 | throw new Error('Invalid API key. Please set a valid GEMINI_API_KEY in your .env file.'); 37 | } 38 | 39 | console.log('Gemini API key found.'); 40 | 41 | // Initialize Gemini client 42 | let genAI: GoogleGenerativeAI; 43 | try { 44 | genAI = new GoogleGenerativeAI(apiKey); 45 | console.log('Gemini client created successfully.'); 46 | } catch (error) { 47 | console.error('Failed to create Gemini client:', error); 48 | throw new Error(`Gemini client creation failed: ${error}`); 49 | } 50 | 51 | const safetySettings = [ 52 | { 53 | category: HarmCategory.HARM_CATEGORY_HARASSMENT, 54 | threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE, 55 | }, 56 | { 57 | category: HarmCategory.HARM_CATEGORY_HATE_SPEECH, 58 | threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE, 59 | }, 60 | { 61 | category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, 62 | threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE, 63 | }, 64 | { 65 | category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, 66 | threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE, 67 | }, 68 | ]; 69 | 70 | export async function generateContent(prompt: string): Promise<string> { 71 | try { 72 | const model = genAI.getGenerativeModel({ model: 'gemini-2.0-flash' }); 73 | 74 | const result = await model.generateContent({ 75 | contents: [{ role: 'user', parts: [{ text: prompt }] }], 76 | safetySettings, 77 | generationConfig: { 78 | temperature: 0.7, 79 | topK: 40, 80 | topP: 0.95, 81 | maxOutputTokens: 8192, 82 | }, 83 | }); 84 | 85 | const response = result.response; 86 | return response.text(); 87 | } catch (error) { 88 | console.error('Gemini API error:', error); 89 | throw new Error(`Error generating document content: ${error}`); 90 | } 91 | } 92 | 93 | export async function fillTemplate(templatePath: string, values: Record<string, string>): Promise<string> { 94 | try { 95 | let templateContent = await fs.readFile(templatePath, 'utf-8'); 96 | 97 | Object.entries(values).forEach(([key, value]) => { 98 | const regex = new RegExp(`{{${key}}}`, 'g'); 99 | templateContent = templateContent.replace(regex, value); 100 | }); 101 | 102 | return templateContent; 103 | } catch (error) { 104 | console.error('Template filling error:', error); 105 | throw new Error(`Error filling template: ${error}`); 106 | } 107 | } 108 | 109 | export async function generateAllDocuments(goal: string): Promise<Record<string, string>> { 110 | const currentDate = new Date().toLocaleDateString('tr-TR'); 111 | 112 | const basePrompt = ` 113 | You are a project documentation expert. You will create comprehensive documentation for the following project: 114 | 115 | PROJECT PURPOSE: ${goal} 116 | 117 | Create the following documents for this project: 118 | `; 119 | 120 | const documentTypes = { 121 | projectbrief: ` 122 | 1. Project Brief (projectbrief.md): 123 | - Explain the general purpose and vision of the project 124 | - List the main objectives 125 | - Define the target audience 126 | - Specify key features 127 | - Determine success criteria 128 | - Present a realistic timeline`, 129 | 130 | productContext: ` 131 | 2. Product Context (productContext.md): 132 | - Conduct market analysis 133 | - Evaluate competitive landscape 134 | - Write user stories 135 | - List requirements 136 | - Explain workflows 137 | - Define product roadmap`, 138 | 139 | systemPatterns: ` 140 | 3. System Patterns (systemPatterns.md): 141 | - Explain architectural design 142 | - Define data models 143 | - Specify API definitions 144 | - Show component structure 145 | - List integration points 146 | - Explain scalability strategy`, 147 | 148 | techContext: ` 149 | 4. Technology Context (techContext.md): 150 | - List technologies used 151 | - Specify software development tools 152 | - Define development environment 153 | - Explain testing strategy 154 | - Define deployment process 155 | - Explain continuous integration approach`, 156 | 157 | activeContext: ` 158 | 5. Active Context (activeContext.md): 159 | - Explain current sprint goals 160 | - List ongoing tasks 161 | - Specify known issues 162 | - Define priorities 163 | - Explain next steps 164 | - Add meeting notes`, 165 | 166 | progress: ` 167 | 6. Progress Report (progress.md): 168 | - List completed tasks 169 | - Specify milestones 170 | - Report test results 171 | - Show performance metrics 172 | - Summarize feedback 173 | - Maintain a changelog` 174 | }; 175 | 176 | 177 | const results: Record<string, string> = {}; 178 | 179 | for (const [docType, docPrompt] of Object.entries(documentTypes)) { 180 | console.log(`Creating ${docType} document...`); 181 | 182 | const fullPrompt = `${basePrompt}${docPrompt}\n\nPlease create content only for the "${docType}" document. Use Markdown format with section headers marked by ##. At the end of the document, add the note "Created on ${currentDate}".`; 183 | 184 | const content = await generateContent(fullPrompt); 185 | results[docType] = content; 186 | } 187 | 188 | return results; 189 | } ``` -------------------------------------------------------------------------------- /src/utils/cursorRulesGenerator.ts: -------------------------------------------------------------------------------- ```typescript 1 | /** 2 | * Utility functions for generating Cursor rules 3 | * 4 | * Note: This module dynamically imports the gemini.js module when necessary 5 | * to generate AI-powered content for cursor rules. 6 | */ 7 | 8 | /** 9 | * Generates cursor rules content based on project purpose 10 | * @param purpose Project purpose description 11 | * @returns Generated cursor rules content 12 | */ 13 | export async function generateCursorRules(purpose: string): Promise<string> { 14 | // Format current date in English locale 15 | const currentDate = new Date().toLocaleDateString("en-US"); 16 | 17 | // Project type detection based on user's purpose 18 | const projectType = detectProjectType(purpose); 19 | 20 | // AI ile içerik oluştur 21 | try { 22 | console.log("Attempting to generate cursor rules with AI..."); 23 | const mainRulesContent = await generateMainCursorRulesWithAI( 24 | purpose, 25 | currentDate, 26 | projectType 27 | ); 28 | console.log("Successfully generated cursor rules with AI"); 29 | return mainRulesContent; 30 | } catch (error) { 31 | console.error("Error generating cursor rules with AI:", error); 32 | 33 | // Hata detayı için log ekleyelim 34 | if (error instanceof Error) { 35 | console.error("Error details:", error.message); 36 | if (error.stack) { 37 | console.error("Error stack:", error.stack); 38 | } 39 | 40 | // Kullanıcıya daha açıklayıcı hata mesajı göster 41 | throw new Error(`AI ile içerik oluşturulamadı: ${error.message}`); 42 | } 43 | 44 | // Genel hata durumunda 45 | throw new Error("AI ile içerik oluşturulamadı. Lütfen daha sonra tekrar deneyin."); 46 | } 47 | } 48 | 49 | /** 50 | * Detects project type based on user's purpose 51 | */ 52 | function detectProjectType(purpose: string): string { 53 | const purposeLower = purpose.toLowerCase(); 54 | 55 | if ( 56 | purposeLower.includes("frontend") || 57 | purposeLower.includes("web") || 58 | purposeLower.includes("site") || 59 | purposeLower.includes("ui") 60 | ) { 61 | return "frontend"; 62 | } 63 | 64 | if ( 65 | purposeLower.includes("backend") || 66 | purposeLower.includes("api") || 67 | purposeLower.includes("service") 68 | ) { 69 | return "backend"; 70 | } 71 | 72 | if ( 73 | purposeLower.includes("mobile") || 74 | purposeLower.includes("android") || 75 | purposeLower.includes("ios") 76 | ) { 77 | return "mobile"; 78 | } 79 | 80 | if ( 81 | purposeLower.includes("fullstack") || 82 | purposeLower.includes("full-stack") 83 | ) { 84 | return "fullstack"; 85 | } 86 | 87 | if ( 88 | purposeLower.includes("data") || 89 | purposeLower.includes("analytics") || 90 | purposeLower.includes("ml") || 91 | purposeLower.includes("ai") 92 | ) { 93 | return "data"; 94 | } 95 | 96 | if ( 97 | purposeLower.includes("devops") || 98 | purposeLower.includes("infrastructure") || 99 | purposeLower.includes("cloud") 100 | ) { 101 | return "devops"; 102 | } 103 | 104 | return "general"; 105 | } 106 | 107 | /** 108 | * Generates the main cursor rules content using Gemini API 109 | */ 110 | async function generateMainCursorRulesWithAI( 111 | purpose: string, 112 | currentDate: string, 113 | projectType: string 114 | ): Promise<string> { 115 | const frontmatter = `--- 116 | description: Main development guidelines for the ${purpose} project 117 | globs: **/* 118 | alwaysApply: true 119 | ---`; 120 | 121 | try { 122 | console.log("Dynamically importing gemini.js module..."); 123 | const { generateContent } = await import("./gemini.js"); 124 | console.log("Successfully imported gemini.js module"); 125 | 126 | const prompt = ` 127 | As a software development expert, you are creating Cursor rules for the ${purpose} project. 128 | 129 | PROJECT DETAILS: 130 | - PURPOSE: ${purpose} 131 | - TYPE: ${projectType} 132 | - DATE: ${currentDate} 133 | 134 | FORMAT REQUIREMENTS: 135 | 1. Start with a clear and concise main title 136 | 2. Use hierarchical markdown headings (## for main sections, ### for subsections) 137 | 3. Use numbered lists for step-by-step instructions 138 | 4. Use bullet points for important notes and guidelines 139 | 5. Include language-specific code blocks for all examples 140 | 6. Provide good and bad examples with explanatory comments 141 | 7. Use bold and italic formatting to emphasize important points 142 | 8. Include a footer with "Powered by tuncer-byte" and GitHub reference 143 | 144 | CONTENT REQUIREMENTS: 145 | 1. PROJECT OVERVIEW: 146 | - Detailed project purpose and objectives 147 | - Technical goals and success criteria 148 | - Recommended technology stack with version numbers 149 | - Architectural patterns and design decisions 150 | 151 | 2. CODE STRUCTURE AND ORGANIZATION: 152 | - Detailed file/folder structure for ${projectType} projects 153 | - Comprehensive naming conventions with examples 154 | - Module organization and dependency management 155 | - State management patterns (if applicable) 156 | 157 | 3. CODING STANDARDS: 158 | - Language-specific best practices 159 | - Error handling and logging strategies 160 | - Performance optimization techniques 161 | - Security implementation guidelines 162 | - Code review checklist 163 | 164 | 4. DEVELOPMENT WORKFLOW: 165 | - Git workflow with branch naming rules 166 | - Commit message format with examples 167 | - PR template and review process 168 | - CI/CD pipeline configuration 169 | - Environment management 170 | 171 | 5. TESTING REQUIREMENTS: 172 | - Test pyramid implementation 173 | - Framework setup and configuration 174 | - Test coverage goals and metrics 175 | - Mocking and test data strategies 176 | - E2E testing approach 177 | 178 | 6. DOCUMENTATION STANDARDS: 179 | - Code documentation templates 180 | - API documentation format 181 | - README structure and content 182 | - Architectural decision records 183 | - Deployment documentation 184 | 185 | 7. QUALITY ASSURANCE: 186 | - Code quality metrics 187 | - Static analysis tools 188 | - Performance monitoring 189 | - Security scanning 190 | - Accessibility guidelines 191 | 192 | 8. FILE ORGANIZATION: 193 | - Explain the purpose of each directory 194 | - Provide examples of correct file placement 195 | 196 | 9. ONBOARDING PROCESS: 197 | - Step-by-step guide for new developers 198 | - Required development environment setup 199 | - Access management and permissions 200 | - Communication channels and protocols 201 | 202 | 10. DEPLOYMENT STRATEGY: 203 | - Environment configuration 204 | - Release process 205 | - Rollback procedures 206 | - Monitoring and alerting setup 207 | 208 | Include specific, practical examples that directly apply to ${projectType} development. 209 | Each guideline should be actionable and specific. 210 | End with a footer containing "Powered by tuncer-byte" and GitHub reference. 211 | `; 212 | 213 | console.log("Sending request to Gemini API..."); 214 | const aiGeneratedContent = await generateContent(prompt); 215 | console.log("Successfully received response from Gemini API"); 216 | 217 | return `${frontmatter} 218 | 219 | ${aiGeneratedContent} 220 | 221 | --- 222 | *Powered by tuncer-byte* 223 | *GitHub: @tuncer-byte*`; 224 | } catch (error) { 225 | console.error("Error generating cursor rules with AI:", error); 226 | 227 | // Daha açıklayıcı hata mesajı 228 | if (error instanceof Error) { 229 | if (error.message.includes("GEMINI_API_KEY")) { 230 | throw new Error( 231 | "Gemini API anahtarı bulunamadı. Lütfen .env dosyasında GEMINI_API_KEY değişkenini tanımlayın." 232 | ); 233 | } else if ( 234 | error.message.includes("network") || 235 | error.message.includes("fetch") 236 | ) { 237 | throw new Error( 238 | "Gemini API ile iletişim kurulamadı. Lütfen internet bağlantınızı kontrol edin." 239 | ); 240 | } 241 | } 242 | 243 | // Orijinal hatayı yeniden fırlat 244 | throw error; 245 | } 246 | } 247 | ``` -------------------------------------------------------------------------------- /src/utils/fileManager.ts: -------------------------------------------------------------------------------- ```typescript 1 | import fs from 'fs-extra'; 2 | import path from 'path'; 3 | 4 | /** 5 | * Creates the Memory Bank directory structure 6 | * @param outputDir Output directory (must already exist) 7 | */ 8 | export async function createMemoryBankStructure(outputDir: string): Promise<void> { 9 | try { 10 | console.log(`Creating Memory Bank structure in existing directory: ${outputDir}`); 11 | 12 | // Verify directory exists 13 | if (!await fs.pathExists(outputDir)) { 14 | console.warn(`Directory does not exist: ${outputDir}, will create it`); 15 | await fs.ensureDir(outputDir); 16 | } 17 | 18 | // No subdirectories needed - using a flat structure for simplicity 19 | console.log(`Using flat structure for Memory Bank in "${outputDir}"`); 20 | 21 | // Create a README.md file with component descriptions 22 | const readmePath = path.join(outputDir, 'README.md'); 23 | const readmeContent = `# Memory Bank 24 | 25 | This directory serves as a structured repository for your project information and notes. 26 | 27 | ## Directory Structure 28 | - **resources**: Images, diagrams, and other resources 29 | - **temp**: Temporary files and drafts 30 | - **archive**: Archived documents 31 | - **references**: Reference materials and documentation 32 | 33 | ## Core Documents 34 | - **projectbrief.md**: Project goals, scope, and vision 35 | - **productContext.md**: Product features, user stories, and market context 36 | - **systemPatterns.md**: System architecture, design patterns, and component structure 37 | - **techContext.md**: Technology stack, frameworks, and technical specifications 38 | - **activeContext.md**: Active tasks, current sprint, and in-progress work 39 | - **progress.md**: Progress tracking, milestones, and project history 40 | 41 | ## Document Management 42 | This Memory Bank uses a structured approach to organize project knowledge. Each document serves a specific purpose in the project lifecycle and should be maintained according to the rules specified in the \`.byterules\` file. 43 | 44 | See the \`.byterules\` file for detailed guidelines on how to maintain and update these documents. 45 | `; 46 | try { 47 | await fs.writeFile(readmePath, readmeContent, 'utf-8'); 48 | console.log(`README file created at: ${readmePath}`); 49 | } catch (error) { 50 | const err = error as any; 51 | console.error(`Error creating README file: ${err.code} - ${err.message}`); 52 | // Continue without README 53 | } 54 | 55 | console.log(`Memory Bank structure successfully created in "${outputDir}".`); 56 | } catch (error) { 57 | console.error(`Error creating directory structure at ${outputDir}:`, error); 58 | if (error instanceof Error) { 59 | throw new Error(`Failed to create Memory Bank structure: ${error.message} (Code: ${(error as any).code || 'UNKNOWN'})`); 60 | } else { 61 | throw new Error(`Failed to create Memory Bank structure: Unknown error`); 62 | } 63 | } 64 | } 65 | 66 | /** 67 | * Saves document content to a specific file 68 | * @param content File content 69 | * @param filePath File path 70 | */ 71 | export async function saveDocument(content: string, filePath: string): Promise<void> { 72 | try { 73 | // Ensure directory exists 74 | await fs.ensureDir(path.dirname(filePath)); 75 | 76 | // Write file 77 | await fs.writeFile(filePath, content, 'utf-8'); 78 | 79 | console.log(`Document saved: ${filePath}`); 80 | } catch (error) { 81 | console.error('Error saving document:', error); 82 | throw new Error(`Failed to save document: ${error}`); 83 | } 84 | } 85 | 86 | /** 87 | * Reads document content 88 | * @param filePath File path 89 | * @returns File content 90 | */ 91 | export async function readDocument(filePath: string): Promise<string> { 92 | try { 93 | // Check if file exists 94 | if (!await fs.pathExists(filePath)) { 95 | throw new Error(`Document not found: ${filePath}`); 96 | } 97 | 98 | // Read file 99 | const content = await fs.readFile(filePath, 'utf-8'); 100 | 101 | return content; 102 | } catch (error) { 103 | console.error('Error reading document:', error); 104 | throw new Error(`Failed to read document: ${error}`); 105 | } 106 | } 107 | 108 | /** 109 | * Reads all documents from a directory 110 | * @param directoryPath Directory path 111 | * @returns Object containing file paths and contents 112 | */ 113 | export async function readAllDocuments(directoryPath: string): Promise<Record<string, string>> { 114 | try { 115 | // Check if directory exists 116 | if (!await fs.pathExists(directoryPath)) { 117 | throw new Error(`Directory not found: ${directoryPath}`); 118 | } 119 | 120 | // List all files 121 | const files = await fs.readdir(directoryPath); 122 | 123 | // Filter only markdown files 124 | const markdownFiles = files.filter(file => file.endsWith('.md')); 125 | 126 | // Read each file 127 | const results: Record<string, string> = {}; 128 | 129 | for (const file of markdownFiles) { 130 | const filePath = path.join(directoryPath, file); 131 | const content = await fs.readFile(filePath, 'utf-8'); 132 | 133 | // Use filename as key (without extension) 134 | const fileName = path.basename(file, path.extname(file)); 135 | results[fileName] = content; 136 | } 137 | 138 | return results; 139 | } catch (error) { 140 | console.error('Error reading documents:', error); 141 | throw new Error(`Failed to read documents: ${error}`); 142 | } 143 | } 144 | 145 | /** 146 | * Exports Memory Bank documents 147 | * @param sourceDir Source directory 148 | * @param format Export format ('folder' or 'json') 149 | * @param outputPath Output file path 150 | * @returns Path to the exported content 151 | */ 152 | export async function exportMemoryBank(sourceDir: string, format: string = 'folder', outputPath: string): Promise<string> { 153 | try { 154 | // Check if source directory exists 155 | if (!await fs.pathExists(sourceDir)) { 156 | throw new Error(`Source directory not found: ${sourceDir}`); 157 | } 158 | 159 | const exportDir = path.dirname(outputPath); 160 | await fs.ensureDir(exportDir); 161 | 162 | if (format === 'folder') { 163 | // Export as folder (copy entire directory structure) 164 | const exportFolderPath = path.join(exportDir, path.basename(sourceDir)); 165 | await fs.copy(sourceDir, exportFolderPath); 166 | console.log(`Memory Bank folder exported to "${exportFolderPath}".`); 167 | return exportFolderPath; 168 | } else if (format === 'json') { 169 | // Export as JSON 170 | const documents = await readAllDocuments(sourceDir); 171 | 172 | // Add metadata 173 | const exportData = { 174 | exportDate: new Date().toISOString(), 175 | memoryBank: documents 176 | }; 177 | 178 | const jsonFilePath = outputPath.endsWith('.json') ? outputPath : `${outputPath}.json`; 179 | await fs.writeFile(jsonFilePath, JSON.stringify(exportData, null, 2), 'utf-8'); 180 | console.log(`Memory Bank data exported to "${jsonFilePath}" in JSON format.`); 181 | return jsonFilePath; 182 | } else { 183 | throw new Error(`Unsupported format: ${format}. Use 'folder' or 'json'.`); 184 | } 185 | } catch (error) { 186 | console.error('Error exporting:', error); 187 | throw new Error(`Failed to export Memory Bank: ${error}`); 188 | } 189 | } 190 | 191 | /** 192 | * Reads the .byterules file and returns its content 193 | * @param directory Directory where .byterules file is located 194 | * @returns Content of .byterules file 195 | */ 196 | export async function readByteRules(directory: string): Promise<string> { 197 | try { 198 | const byteRulesPath = path.join(directory, '.byterules'); 199 | 200 | // Check if file exists 201 | if (!await fs.pathExists(byteRulesPath)) { 202 | throw new Error('ByteRules file not found. Memory Bank may not be properly initialized.'); 203 | } 204 | 205 | // Read file 206 | const content = await fs.readFile(byteRulesPath, 'utf-8'); 207 | 208 | return content; 209 | } catch (error) { 210 | console.error('Error reading ByteRules:', error); 211 | throw new Error(`Failed to read ByteRules: ${error}`); 212 | } 213 | } 214 | 215 | /** 216 | * Gets document workflow information based on document type 217 | * @param directory Directory where .byterules file is located 218 | * @param documentType Type of document to get workflow for 219 | * @returns Workflow information for the document 220 | */ 221 | export async function getDocumentWorkflow(directory: string, documentType: string): Promise<{ 222 | purpose: string; 223 | updateTiming: string; 224 | structure: string[]; 225 | commands: string[]; 226 | }> { 227 | try { 228 | // Get byterules content 229 | const byteRulesContent = await readByteRules(directory); 230 | 231 | // Extract section for the specific document type 232 | const regex = new RegExp(`###\\s*\\d+\\.\\s*${documentType.replace(/Context/g, ' Context')}\\s*\\([\\w\\.]+\\)[\\s\\S]*?(?=###\\s*\\d+\\.\\s*|##\\s*|$)`, 'i'); 233 | const match = byteRulesContent.match(regex); 234 | 235 | if (!match) { 236 | return { 237 | purpose: `Information about ${documentType} document`, 238 | updateTiming: 'As needed', 239 | structure: ['No specific structure defined'], 240 | commands: [`update_document ${documentType.toLowerCase()}`] 241 | }; 242 | } 243 | 244 | // Parse section content 245 | const sectionContent = match[0]; 246 | 247 | // Extract purpose 248 | const purposeMatch = sectionContent.match(/\*\*Purpose\*\*:\s*(.*?)(?=\n)/); 249 | const purpose = purposeMatch ? purposeMatch[1].trim() : `Information about ${documentType}`; 250 | 251 | // Extract when to update 252 | const updateMatch = sectionContent.match(/\*\*When to Update\*\*:\s*(.*?)(?=\n)/); 253 | const updateTiming = updateMatch ? updateMatch[1].trim() : 'As needed'; 254 | 255 | // Extract structure 256 | const structureMatch = sectionContent.match(/\*\*Structure\*\*:[\s\S]*?(?=\*\*|$)/); 257 | const structure = structureMatch 258 | ? structureMatch[0] 259 | .replace(/\*\*Structure\*\*:\s*/, '') 260 | .trim() 261 | .split('\n') 262 | .map(line => line.replace(/^\s*-\s*/, '').trim()) 263 | .filter(line => line.length > 0) 264 | : ['No specific structure defined']; 265 | 266 | // Extract commands 267 | const commandsMatch = sectionContent.match(/\*\*Commands\*\*:[\s\S]*?(?=\*\*|$)/); 268 | const commands = commandsMatch 269 | ? commandsMatch[0] 270 | .replace(/\*\*Commands\*\*:\s*/, '') 271 | .trim() 272 | .split('\n') 273 | .map(line => line.replace(/^\s*-\s*`(.*?)`.*/, '$1').trim()) 274 | .filter(line => line.length > 0) 275 | : [`update_document ${documentType.toLowerCase()}`]; 276 | 277 | return { 278 | purpose, 279 | updateTiming, 280 | structure, 281 | commands 282 | }; 283 | } catch (error) { 284 | console.error('Error getting document workflow:', error); 285 | return { 286 | purpose: `Information about ${documentType} document`, 287 | updateTiming: 'As needed', 288 | structure: ['No specific structure defined'], 289 | commands: [`update_document ${documentType.toLowerCase()}`] 290 | }; 291 | } 292 | } 293 | 294 | /** 295 | * Creates a structured template for a document based on ByteRules 296 | * @param directory Directory where .byterules file is located 297 | * @param documentType Type of document to create template for 298 | * @returns Structured template content 299 | */ 300 | export async function createDocumentTemplate(directory: string, documentType: string): Promise<string> { 301 | try { 302 | // Get workflow info 303 | const workflow = await getDocumentWorkflow(directory, documentType); 304 | 305 | // Build template 306 | let template = `# ${documentType.replace(/([A-Z])/g, ' $1').trim()}\n\n`; 307 | template += `> ${workflow.purpose}\n\n`; 308 | template += `> Last Updated: ${new Date().toISOString().split('T')[0]}\n\n`; 309 | 310 | // Add sections based on structure 311 | for (const section of workflow.structure) { 312 | template += `## ${section}\n\n_Add content here_\n\n`; 313 | } 314 | 315 | // Add reference to update timing 316 | template += `---\n\n**Note:** This document should be updated ${workflow.updateTiming.toLowerCase()}.\n`; 317 | 318 | return template; 319 | } catch (error) { 320 | console.error('Error creating document template:', error); 321 | 322 | // Return basic template on error 323 | return `# ${documentType.replace(/([A-Z])/g, ' $1').trim()}\n\nLast Updated: ${new Date().toISOString().split('T')[0]}\n\n## Content\n\n_Add content here_\n`; 324 | } 325 | } 326 | 327 | /** 328 | * Analyzes document consistency with ByteRules guidelines 329 | * @param directory Directory where documents are located 330 | * @returns Analysis results with recommendations 331 | */ 332 | export async function analyzeDocumentConsistency(directory: string): Promise<{ 333 | documentType: string; 334 | status: 'good' | 'needs-update'; 335 | recommendation: string; 336 | }[]> { 337 | try { 338 | // Get all documents 339 | const documents = await readAllDocuments(directory); 340 | const results = []; 341 | 342 | // Analyze each document 343 | for (const [docName, content] of Object.entries(documents)) { 344 | // Skip non-standard documents 345 | if (!['projectbrief', 'productContext', 'systemPatterns', 'techContext', 'activeContext', 'progress'].includes(docName)) { 346 | continue; 347 | } 348 | 349 | // Get workflow info 350 | const workflow = await getDocumentWorkflow(directory, docName); 351 | 352 | // Check for required sections 353 | const missingSections = []; 354 | for (const section of workflow.structure) { 355 | const sectionRegex = new RegExp(`##\\s*${section}`, 'i'); 356 | if (!sectionRegex.test(content)) { 357 | missingSections.push(section); 358 | } 359 | } 360 | 361 | // Check if document was updated recently (within last 30 days) 362 | const lastUpdatedMatch = content.match(/Last Updated:\s*(\d{4}-\d{2}-\d{2})/); 363 | let needsUpdate = false; 364 | 365 | if (lastUpdatedMatch) { 366 | const lastUpdated = new Date(lastUpdatedMatch[1]); 367 | const thirtyDaysAgo = new Date(); 368 | thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30); 369 | needsUpdate = lastUpdated < thirtyDaysAgo; 370 | } else { 371 | needsUpdate = true; // No update date found 372 | } 373 | 374 | let recommendation = ''; 375 | let status: 'good' | 'needs-update' = 'good'; 376 | 377 | if (missingSections.length > 0) { 378 | recommendation = `Missing sections: ${missingSections.join(', ')}`; 379 | status = 'needs-update'; 380 | } else if (needsUpdate) { 381 | recommendation = 'Document may need updating (last update over 30 days ago)'; 382 | status = 'needs-update'; 383 | } else { 384 | recommendation = 'Document follows the structure defined in ByteRules'; 385 | } 386 | 387 | results.push({ 388 | documentType: docName, 389 | status, 390 | recommendation 391 | }); 392 | } 393 | 394 | return results; 395 | } catch (error) { 396 | console.error('Error analyzing documents:', error); 397 | throw new Error(`Failed to analyze documents: ${error}`); 398 | } 399 | } ``` -------------------------------------------------------------------------------- /src/mcp/memoryBankMcp.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; 2 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; 3 | import { z } from 'zod'; 4 | import path from 'path'; 5 | import fs from 'fs-extra'; 6 | import os from 'os'; 7 | import { generateAllDocuments } from '../utils/gemini.js'; 8 | import { 9 | createMemoryBankStructure, 10 | saveDocument, 11 | readDocument, 12 | readAllDocuments, 13 | exportMemoryBank 14 | } from '../utils/fileManager.js'; 15 | import { generateCursorRules } from '../utils/cursorRulesGenerator.js'; 16 | 17 | // Create MCP server 18 | const server = new McpServer({ 19 | name: 'Memory Bank MCP', 20 | version: '1.0.0' 21 | }); 22 | 23 | // Import URL and fileURLToPath for ESM compatible __dirname alternative 24 | import { fileURLToPath } from 'url'; 25 | import { dirname } from 'path'; 26 | 27 | // Helper function to get the workspace root directory 28 | const getWorkspaceRootDir = () => { 29 | // Try to get VS Code workspace folder from environment variables 30 | // This is more reliable than process.cwd() in VS Code environment 31 | if (process.env.VSCODE_WORKSPACE_FOLDER) { 32 | console.log(`Using VS Code workspace folder: ${process.env.VSCODE_WORKSPACE_FOLDER}`); 33 | return process.env.VSCODE_WORKSPACE_FOLDER; 34 | } 35 | 36 | // If not in VS Code or env var not available, try to determine from current file path 37 | // ESM compatible version of __dirname 38 | const __filename = fileURLToPath(import.meta.url); 39 | const __dirname = dirname(__filename); 40 | 41 | const currentFilePath = __dirname; 42 | console.log(`Current file directory: ${currentFilePath}`); 43 | 44 | // Try to find the workspace root by looking for package.json 45 | let dir = currentFilePath; 46 | while (dir !== path.parse(dir).root) { 47 | if (fs.existsSync(path.join(dir, 'package.json'))) { 48 | console.log(`Found workspace root at: ${dir}`); 49 | return dir; 50 | } 51 | dir = path.dirname(dir); 52 | } 53 | 54 | // Fallback to current working directory with warning 55 | console.warn(`Could not determine workspace root, falling back to CWD: ${process.cwd()}`); 56 | return process.cwd(); 57 | }; 58 | 59 | // Default document directory path - initialize to null, will be set during initialization 60 | let MEMORY_BANK_DIR: string | null = null; 61 | 62 | // Initialize Memory Bank - create new document structure 63 | server.tool( 64 | 'initialize_memory_bank', 65 | { 66 | goal: z.string().min(10, 'Project goal must be at least 10 characters'), 67 | geminiApiKey: z.string().optional().describe('Gemini API key (optional)'), 68 | location: z.string().describe('Absolute path where memory-bank folder will be created') 69 | }, 70 | async ({ goal, geminiApiKey, location }) => { 71 | try { 72 | // Diagnostics: Log environment info 73 | console.log(`Current working directory: ${process.cwd()}`); 74 | console.log(`Node version: ${process.version}`); 75 | console.log(`Platform: ${process.platform}`); 76 | 77 | // Determine where to create the memory-bank directory 78 | let baseDir; 79 | let memoryBankDir; 80 | 81 | if (location) { 82 | // Use user-specified location as the base directory 83 | if (path.isAbsolute(location)) { 84 | // If absolute path is provided, use it directly as base directory 85 | baseDir = location; 86 | } else { 87 | // If relative path is provided, resolve against current working directory 88 | baseDir = path.resolve(process.cwd(), location); 89 | } 90 | console.log(`Using user specified base location: ${baseDir}`); 91 | } else { 92 | // If no location provided, use current working directory as base 93 | baseDir = process.cwd(); 94 | console.log(`No location specified, using current directory as base: ${baseDir}`); 95 | } 96 | 97 | // Create memory-bank directory inside the base directory 98 | memoryBankDir = path.join(baseDir, 'memory-bank'); 99 | console.log(`Will create Memory Bank structure at: ${memoryBankDir}`); 100 | 101 | // Ensure parent directory exists if needed 102 | const parentDir = path.dirname(memoryBankDir); 103 | try { 104 | await fs.ensureDir(parentDir); 105 | console.log(`Ensured parent directory exists: ${parentDir}`); 106 | } catch (error) { 107 | console.error(`Error ensuring parent directory: ${error}`); 108 | throw new Error(`Cannot create or access parent directory: ${error}`); 109 | } 110 | 111 | // Set global memory bank directory 112 | MEMORY_BANK_DIR = memoryBankDir; 113 | 114 | console.log(`Will create Memory Bank at: ${MEMORY_BANK_DIR}`); 115 | 116 | // Ensure memory-bank directory exists before passing to createMemoryBankStructure 117 | try { 118 | await fs.ensureDir(MEMORY_BANK_DIR); 119 | console.log(`Created Memory Bank root directory: ${MEMORY_BANK_DIR}`); 120 | } catch (error) { 121 | console.error(`Error creating Memory Bank directory: ${error}`); 122 | throw new Error(`Cannot create Memory Bank directory: ${error}`); 123 | } 124 | 125 | // Temporarily set the API key if provided 126 | if (geminiApiKey) { 127 | process.env.GEMINI_API_KEY = geminiApiKey; 128 | } 129 | 130 | // First, set up the .byterules file before creating other files 131 | // This ensures the byterules file is in place before other operations 132 | const byterulesDest = path.join(MEMORY_BANK_DIR, '.byterules'); 133 | 134 | try { 135 | // Debug: List all search paths we're going to try 136 | console.log('Searching for .byterules template file...'); 137 | 138 | // Get the ESM compatible dirname 139 | const __filename = fileURLToPath(import.meta.url); 140 | const __dirname = dirname(__filename); 141 | console.log(`Current file directory: ${__dirname}`); 142 | 143 | // Try multiple possible locations for the .byterules file 144 | const possiblePaths = [ 145 | path.join(process.cwd(), 'src', 'templates', '.byterules'), // From current working dir 146 | path.join(__dirname, '..', 'templates', '.byterules'), // From mcp dir to templates 147 | path.join(__dirname, '..', '..', 'src', 'templates', '.byterules'), // From mcp dir up two levels 148 | path.join(process.cwd(), 'templates', '.byterules'), // Direct templates folder 149 | path.join(process.cwd(), '.byterules') // Root of project 150 | ]; 151 | 152 | // Manually create .byterules content as fallback 153 | const defaultByterules = `# Memory Bank Document Orchestration Standard 154 | 155 | ## Directory Validation 156 | 157 | Before any operation (create/update/reference/review), ensure you are in the correct project root directory. Specifically: 158 | 159 | - A valid Memory Bank system **must contain** this \`.byterules\` file at its root. 160 | - If this file is missing, halt operations and **navigate to the correct directory** using: 161 | 162 | \`\`\`bash 163 | cd /your/project/root 164 | \`\`\` 165 | 166 | Failing to validate the directory can lead to misplaced or inconsistent documentation. 167 | 168 | --- 169 | 170 | ## System Overview 171 | 172 | Memory Bank is a structured documentation system designed to maintain project knowledge in an organized, accessible format. This \`.byterules\` file serves as the standard guide for how the system works across all projects. 173 | 174 | ## Standard Document Types 175 | 176 | ### 1. Project Brief (projectbrief.md) 177 | - **Purpose**: Core document that defines project objectives, scope, and vision 178 | - **When to Use**: Reference when making any major project decisions 179 | - **Workflow Step**: Start here; all other documents derive from this foundation 180 | - **Critical For**: Maintaining alignment with business goals throughout development 181 | 182 | ### 2. Product Context (productContext.md) 183 | - **Purpose**: Documents product functionality from a user perspective 184 | - **When to Use**: When designing features and establishing requirements 185 | - **Workflow Step**: Second document in sequence, expands on project brief goals 186 | - **Critical For**: Ensuring user needs drive technical decisions 187 | 188 | ### 3. System Patterns (systemPatterns.md) 189 | - **Purpose**: Establishes system architecture and component relationships 190 | - **When to Use**: During system design and when making integration decisions 191 | - **Workflow Step**: Third document, translates product needs to technical design 192 | - **Critical For**: Maintaining a coherent and scalable technical architecture 193 | 194 | ### 4. Tech Context (techContext.md) 195 | - **Purpose**: Specifies technology stack and implementation details 196 | - **When to Use**: During development and when onboarding technical team members 197 | - **Workflow Step**: Fourth document, makes concrete technology choices 198 | - **Critical For**: Technical consistency and efficient development 199 | 200 | ### 5. Active Context (activeContext.md) 201 | - **Purpose**: Tracks current tasks, open issues, and development focus 202 | - **When to Use**: Daily, during planning sessions, and when switching tasks 203 | - **Workflow Step**: Fifth document, operationalizes the technical approach 204 | - **Critical For**: Day-to-day execution and short-term planning 205 | 206 | ### 6. Progress (progress.md) 207 | - **Purpose**: Documents completed work, milestones, and project history 208 | - **When to Use**: After completing significant work or during reviews 209 | - **Workflow Step**: Ongoing document that records the project journey 210 | - **Critical For**: Tracking accomplishments and learning from experience 211 | 212 | ## Standard Workflows 213 | 214 | ### Documentation Sequence 215 | Always follow this sequence for document creation and reference: 216 | 1. **Project Brief** → Foundation of all project decisions 217 | 2. **Product Context** → User-focused requirements and features 218 | 3. **System Patterns** → Architecture and component design 219 | 4. **Tech Context** → Technology choices and implementation guidelines 220 | 5. **Active Context** → Current work and immediate focus 221 | 6. **Progress** → Historical record and milestone tracking 222 | 223 | ### Document Lifecycle Management 224 | Each document follows a standard lifecycle: 225 | 1. **Creation**: Establish initial content based on project needs 226 | 2. **Reference**: Use document for planning and decision-making 227 | 3. **Update**: Revise when relevant factors change 228 | 4. **Review**: Periodically validate for accuracy and completeness 229 | 5. **Archive**: Maintain as historical reference when superseded 230 | 231 | ## Best Practices 232 | 233 | ### Document Quality Standards 234 | - **Clarity**: Write in clear, concise language 235 | - **Completeness**: Include all relevant information 236 | - **Consistency**: Use consistent terminology across documents 237 | - **Structure**: Follow standardized document formats 238 | - **Granularity**: Balance detail with readability 239 | - **Traceability**: Link related concepts across documents 240 | 241 | ### Document Integration Principles 242 | - **Vertical Traceability**: Ensure business goals trace to technical implementation 243 | - **Horizontal Consistency**: Maintain alignment across documents at the same level 244 | - **Change Impact Analysis**: Update related documents when one changes 245 | - **Decision Recording**: Document the reasoning behind significant decisions 246 | 247 | 248 | `; 249 | 250 | // Try each path and use the first one that exists 251 | let bytesRulesFound = false; 252 | 253 | for (const testPath of possiblePaths) { 254 | console.log(`Checking path: ${testPath}`); 255 | 256 | if (await fs.pathExists(testPath)) { 257 | console.log(`✓ Found .byterules at: ${testPath}`); 258 | await fs.copy(testPath, byterulesDest); 259 | console.log(`Standard .byterules file copied to: ${byterulesDest}`); 260 | bytesRulesFound = true; 261 | break; 262 | } else { 263 | console.log(`✗ Not found at: ${testPath}`); 264 | } 265 | } 266 | 267 | // If no .byterules file found, create one with the default content 268 | if (!bytesRulesFound) { 269 | console.log('No .byterules template found, creating default'); 270 | await fs.writeFile(byterulesDest, defaultByterules, 'utf-8'); 271 | console.log(`Default .byterules file created at: ${byterulesDest}`); 272 | } 273 | 274 | } catch (error) { 275 | console.error(`Error setting up .byterules file: ${error}`); 276 | throw new Error(`Failed to set up .byterules file: ${error}`); 277 | } 278 | 279 | // Now create the full structure 280 | await createMemoryBankStructure(MEMORY_BANK_DIR); 281 | 282 | // Generate document contents 283 | const documentContents = await generateAllDocuments(goal); 284 | 285 | // Save each document 286 | for (const [docType, content] of Object.entries(documentContents)) { 287 | const filePath = path.join(MEMORY_BANK_DIR, `${docType}.md`); 288 | await saveDocument(content, filePath); 289 | } 290 | 291 | return { 292 | content: [ 293 | { 294 | type: 'text', 295 | text: `✅ Memory Bank successfully created!\n\nLocation: ${MEMORY_BANK_DIR}\n\nGenerated Documents:\n- projectbrief.md\n- productContext.md\n- systemPatterns.md\n- techContext.md\n- activeContext.md\n- progress.md\n- .byterules` 296 | } 297 | ] 298 | }; 299 | } catch (error) { 300 | console.error('Error creating Memory Bank:', error); 301 | return { 302 | content: [{ type: 'text', text: `❌ Error: ${error instanceof Error ? error.message : String(error)}` }], 303 | isError: true 304 | }; 305 | } 306 | } 307 | ); 308 | 309 | // Update document 310 | server.tool( 311 | 'update_document', 312 | { 313 | documentType: z.enum(['projectbrief', 'productContext', 'systemPatterns', 'techContext', 'activeContext', 'progress']), 314 | content: z.string().optional(), 315 | regenerate: z.boolean().default(false) 316 | }, 317 | async ({ documentType, content, regenerate }) => { 318 | try { 319 | // Check if Memory Bank directory is initialized 320 | if (!MEMORY_BANK_DIR) { 321 | throw new Error('Memory Bank not initialized. Please use initialize_memory_bank tool first.'); 322 | } 323 | 324 | const filePath = path.join(MEMORY_BANK_DIR, `${documentType}.md`); 325 | 326 | // Check if file exists 327 | if (!await fs.pathExists(filePath)) { 328 | // Create file if it doesn't exist 329 | await fs.ensureFile(filePath); 330 | await fs.writeFile(filePath, `# ${documentType}\n\n`, 'utf-8'); 331 | } 332 | 333 | if (regenerate) { 334 | // Read existing document 335 | const currentContent = await readDocument(filePath); 336 | 337 | // Always use en-US locale for date formatting to ensure English output 338 | const dateOptions = { year: 'numeric', month: 'long', day: 'numeric' }; 339 | const englishDate = new Date().toLocaleDateString('en-US'); 340 | 341 | // TODO: Generate new content with Gemini (example for now) 342 | const newContent = `${currentContent}\n\n## Update\nThis document was regenerated on ${englishDate}.`; 343 | 344 | // Save document 345 | await saveDocument(newContent, filePath); 346 | } else if (content) { 347 | // Save provided content 348 | await saveDocument(content, filePath); 349 | } else { 350 | throw new Error('Content must be provided or regenerate=true'); 351 | } 352 | 353 | // Always use English for all response messages 354 | return { 355 | content: [{ 356 | type: 'text', 357 | text: `✅ "${documentType}.md" document successfully updated!` 358 | }] 359 | }; 360 | } catch (error) { 361 | console.error('Error updating document:', error); 362 | // Ensure error messages are also in English 363 | const errorMessage = error instanceof Error ? error.message : String(error); 364 | return { 365 | content: [{ type: 'text', text: `❌ Error: ${errorMessage}` }], 366 | isError: true 367 | }; 368 | } 369 | } 370 | ); 371 | 372 | // Query Memory Bank 373 | server.tool( 374 | 'query_memory_bank', 375 | { 376 | query: z.string().min(5, 'Query must be at least 5 characters') 377 | }, 378 | async ({ query }) => { 379 | try { 380 | // Check if Memory Bank has been initialized 381 | if (!MEMORY_BANK_DIR) { 382 | return { 383 | content: [{ type: 'text', text: `ℹ️ Memory Bank not initialized. Please use 'initialize_memory_bank' tool first.` }] 384 | }; 385 | } 386 | 387 | // Check if Memory Bank directory exists on disk 388 | if (!await fs.pathExists(MEMORY_BANK_DIR)) { 389 | return { 390 | content: [{ type: 'text', text: `ℹ️ Memory Bank directory (${MEMORY_BANK_DIR}) not found on disk. Please use 'initialize_memory_bank' tool first.` }] 391 | }; 392 | } 393 | 394 | // Read all documents 395 | const documents = await readAllDocuments(MEMORY_BANK_DIR); 396 | 397 | // Advanced search function 398 | const searchResults = performAdvancedSearch(query, documents); 399 | 400 | if (searchResults.length === 0) { 401 | return { 402 | content: [{ type: 'text', text: `ℹ️ No results found for query "${query}".` }] 403 | }; 404 | } 405 | 406 | // Format results 407 | const formattedResults = searchResults.map(result => { 408 | return `📄 **${result.documentType}**:\n${result.snippet}\n`; 409 | }).join('\n'); 410 | 411 | return { 412 | content: [{ 413 | type: 'text', 414 | text: `🔍 Results for query "${query}":\n\n${formattedResults}` 415 | }] 416 | }; 417 | } catch (error) { 418 | console.error('Error querying Memory Bank:', error); 419 | return { 420 | content: [{ type: 'text', text: `❌ Error: ${error instanceof Error ? error.message : String(error)}` }], 421 | isError: true 422 | }; 423 | } 424 | } 425 | ); 426 | 427 | // Advanced search functionality 428 | interface SearchResult { 429 | documentType: string; 430 | relevanceScore: number; 431 | snippet: string; 432 | } 433 | 434 | function performAdvancedSearch(query: string, documents: Record<string, string>): SearchResult[] { 435 | const results: SearchResult[] = []; 436 | const queryTerms = query.toLowerCase().split(/\s+/).filter(term => term.length > 2); 437 | 438 | // Search in each document 439 | for (const [docType, content] of Object.entries(documents)) { 440 | // Split document into sections and paragraphs 441 | const sections = content.split(/\n#{2,3}\s+/).filter(Boolean); 442 | 443 | for (const section of sections) { 444 | // Extract title and content 445 | const titleMatch = section.match(/^([^\n]+)/); 446 | const title = titleMatch ? titleMatch[1].trim() : ''; 447 | 448 | // Evaluate each paragraph 449 | const paragraphs = section.split(/\n\n+/); 450 | 451 | for (const paragraph of paragraphs) { 452 | // Calculate relevance score 453 | const relevanceScore = calculateRelevanceScore(query, queryTerms, paragraph); 454 | 455 | // Add results above threshold 456 | if (relevanceScore > 0.3) { 457 | // Extract relevant snippet 458 | const snippet = extractRelevantSnippet(paragraph, queryTerms, title); 459 | 460 | results.push({ 461 | documentType: docType, 462 | relevanceScore, 463 | snippet 464 | }); 465 | } 466 | } 467 | } 468 | } 469 | 470 | // Sort results by relevance and return top 5 471 | return results 472 | .sort((a, b) => b.relevanceScore - a.relevanceScore) 473 | .slice(0, 5); 474 | } 475 | 476 | function calculateRelevanceScore(query: string, queryTerms: string[], text: string): number { 477 | const lowerText = text.toLowerCase(); 478 | 479 | // Exact match check (highest score) 480 | if (lowerText.includes(query.toLowerCase())) { 481 | return 1.0; 482 | } 483 | 484 | // Term-based matching 485 | let matchCount = 0; 486 | for (const term of queryTerms) { 487 | if (lowerText.includes(term)) { 488 | matchCount++; 489 | } 490 | } 491 | 492 | // Term match ratio 493 | const termMatchRatio = queryTerms.length > 0 ? matchCount / queryTerms.length : 0; 494 | 495 | // Proximity factor calculation 496 | let proximityFactor = 0; 497 | if (matchCount >= 2) { 498 | // Calculate proximity between matching terms 499 | // (This is a simplified approach) 500 | proximityFactor = 0.2; 501 | } 502 | 503 | return termMatchRatio * 0.8 + proximityFactor; 504 | } 505 | 506 | function extractRelevantSnippet(text: string, queryTerms: string[], sectionTitle: string): string { 507 | const lowerText = text.toLowerCase(); 508 | const MAX_SNIPPET_LENGTH = 150; 509 | 510 | // Find best match 511 | let bestPosition = 0; 512 | let bestTermCount = 0; 513 | 514 | // Query for each character in the document 515 | for (let i = 0; i < lowerText.length; i++) { 516 | let termCount = 0; 517 | for (const term of queryTerms) { 518 | if (lowerText.substring(i, i + 100).includes(term)) { 519 | termCount++; 520 | } 521 | } 522 | 523 | if (termCount > bestTermCount) { 524 | bestTermCount = termCount; 525 | bestPosition = i; 526 | } 527 | } 528 | 529 | // Create snippet around best match 530 | let startPos = Math.max(0, bestPosition - 30); 531 | let endPos = Math.min(text.length, bestPosition + MAX_SNIPPET_LENGTH - 30); 532 | 533 | // Adjust to word boundaries 534 | while (startPos > 0 && text[startPos] !== ' ' && text[startPos] !== '\n') { 535 | startPos--; 536 | } 537 | 538 | while (endPos < text.length && text[endPos] !== ' ' && text[endPos] !== '\n') { 539 | endPos++; 540 | } 541 | 542 | let snippet = text.substring(startPos, endPos).trim(); 543 | 544 | // Add ellipsis to indicate truncation 545 | if (startPos > 0) { 546 | snippet = '...' + snippet; 547 | } 548 | 549 | if (endPos < text.length) { 550 | snippet = snippet + '...'; 551 | } 552 | 553 | // Add title 554 | if (sectionTitle) { 555 | return `**${sectionTitle}**: ${snippet}`; 556 | } 557 | 558 | return snippet; 559 | } 560 | 561 | // Export Memory Bank 562 | server.tool( 563 | 'export_memory_bank', 564 | { 565 | format: z.enum(['json', 'folder']).default('folder').describe('Export format'), 566 | outputPath: z.string().optional() 567 | }, 568 | async ({ format, outputPath }) => { 569 | try { 570 | // Check if Memory Bank has been initialized 571 | if (!MEMORY_BANK_DIR) { 572 | return { 573 | content: [{ type: 'text', text: `ℹ️ Memory Bank not initialized. Please use 'initialize_memory_bank' tool first.` }] 574 | }; 575 | } 576 | 577 | // Check if Memory Bank directory exists on disk 578 | if (!await fs.pathExists(MEMORY_BANK_DIR)) { 579 | return { 580 | content: [{ type: 'text', text: `ℹ️ Memory Bank directory (${MEMORY_BANK_DIR}) not found on disk. Please use 'initialize_memory_bank' tool first.` }] 581 | }; 582 | } 583 | 584 | // Ensure we have an absolute path for the output 585 | const defaultOutputPath = path.resolve(path.join(process.cwd(), 'memory-bank-export')); 586 | const targetOutputPath = outputPath ? path.resolve(outputPath) : defaultOutputPath; 587 | 588 | console.log(`Exporting Memory Bank from ${MEMORY_BANK_DIR} to ${targetOutputPath}`); 589 | 590 | // Call exportMemoryBank function 591 | const exportResult = await exportMemoryBank(MEMORY_BANK_DIR, format, targetOutputPath); 592 | 593 | // Create message based on format type 594 | const formatMessage = format === 'json' ? 'JSON file' : 'folder'; 595 | 596 | return { 597 | content: [{ 598 | type: 'text', 599 | text: `✅ Memory Bank successfully exported as ${formatMessage}: ${exportResult}` 600 | }] 601 | }; 602 | } catch (error) { 603 | console.error('Error exporting Memory Bank:', error); 604 | return { 605 | content: [{ type: 'text', text: `❌ Error: ${error instanceof Error ? error.message : String(error)}` }], 606 | isError: true 607 | }; 608 | } 609 | } 610 | ); 611 | 612 | // Create Cursor Rules 613 | server.tool( 614 | 'create_cursor_rules', 615 | { 616 | projectPurpose: z.string() 617 | .min(10, 'Proje amacı en az 10 karakter olmalıdır') 618 | .describe('Proje amacını detaylı bir şekilde açıklayan bir metin giriniz. Bu metin projenin temel hedeflerini ve kapsamını belirleyecektir.'), 619 | location: z.string() 620 | .describe('Absolute path where cursor-rules will be created') 621 | }, 622 | async ({ projectPurpose, location }) => { 623 | try { 624 | // Diagnostics: Log environment info 625 | console.log(`Current working directory: ${process.cwd()}`); 626 | console.log(`Node version: ${process.version}`); 627 | console.log(`Platform: ${process.platform}`); 628 | 629 | // Determine where to create the .cursor directory 630 | let baseDir; 631 | 632 | if (location) { 633 | // Use user-specified location as the base directory 634 | if (path.isAbsolute(location)) { 635 | // If absolute path is provided, use it directly as base directory 636 | baseDir = location; 637 | } else { 638 | // If relative path is provided, resolve against current working directory 639 | baseDir = path.resolve(process.cwd(), location); 640 | } 641 | console.log(`Using user specified base location: ${baseDir}`); 642 | } else { 643 | // If no location provided, use current working directory as base 644 | baseDir = process.cwd(); 645 | console.log(`No location specified, using current directory as base: ${baseDir}`); 646 | } 647 | 648 | // Create .cursor directory in the base directory 649 | const cursorDir = path.join(baseDir, '.cursor'); 650 | console.log(`Will create Cursor Rules at: ${cursorDir}`); 651 | 652 | // Ensure parent directory exists if needed 653 | const parentDir = path.dirname(cursorDir); 654 | try { 655 | await fs.ensureDir(parentDir); 656 | console.log(`Ensured parent directory exists: ${parentDir}`); 657 | } catch (error) { 658 | console.error(`Error ensuring parent directory: ${error}`); 659 | throw new Error(`Cannot create or access parent directory: ${error}`); 660 | } 661 | 662 | // Ensure .cursor directory exists 663 | try { 664 | await fs.ensureDir(cursorDir); 665 | console.log(`Created .cursor directory: ${cursorDir}`); 666 | } catch (error) { 667 | console.error(`Error creating .cursor directory: ${error}`); 668 | throw new Error(`Cannot create .cursor directory: ${error}`); 669 | } 670 | 671 | // Create the cursor-rules.mdc file 672 | const cursorRulesPath = path.join(cursorDir, 'cursor-rules.mdc'); 673 | console.log(`Will create cursor-rules.mdc at: ${cursorRulesPath}`); 674 | 675 | // Generate content for the rules file based on project purpose 676 | console.log(`Generating cursor rules content for purpose: ${projectPurpose}`); 677 | try { 678 | const cursorRulesContent = await generateCursorRules(projectPurpose); 679 | 680 | // Save the file 681 | try { 682 | await fs.writeFile(cursorRulesPath, cursorRulesContent, 'utf-8'); 683 | console.log(`Created cursor-rules.mdc at: ${cursorRulesPath}`); 684 | } catch (error) { 685 | console.error(`Error creating cursor-rules.mdc file: ${error}`); 686 | throw new Error(`Cannot create cursor-rules.mdc file: ${error}`); 687 | } 688 | 689 | return { 690 | content: [{ 691 | type: 'text', 692 | text: `✅ Cursor Rules successfully created!\n\nLocation: ${cursorRulesPath}` 693 | }] 694 | }; 695 | } catch (ruleGenError) { 696 | console.error(`Error generating cursor rules content: ${ruleGenError}`); 697 | 698 | // Detaylı hata mesajı oluştur 699 | let errorMessage = 'Error generating Cursor Rules content: '; 700 | if (ruleGenError instanceof Error) { 701 | errorMessage += ruleGenError.message; 702 | 703 | // API key ile ilgili hata mesajlarını daha açıklayıcı hale getir 704 | if (ruleGenError.message.includes('GEMINI_API_KEY') || ruleGenError.message.includes('API key')) { 705 | errorMessage += '\n\nÖnemli: Bu özellik Gemini API kullanıyor. Lütfen .env dosyasında geçerli bir GEMINI_API_KEY tanımladığınızdan emin olun.'; 706 | } 707 | } else { 708 | errorMessage += String(ruleGenError); 709 | } 710 | 711 | throw new Error(errorMessage); 712 | } 713 | } catch (error) { 714 | console.error('Error creating Cursor Rules:', error); 715 | return { 716 | content: [{ type: 'text', text: `❌ Error: ${error instanceof Error ? error.message : String(error)}` }], 717 | isError: true 718 | }; 719 | } 720 | } 721 | ); 722 | 723 | // Read document contents - provides as resource 724 | server.resource( 725 | 'memory_bank_document', 726 | 'memory-bank://{documentType}', 727 | async (uri) => { 728 | try { 729 | // First check if Memory Bank has been initialized 730 | if (!MEMORY_BANK_DIR) { 731 | throw new Error('Memory Bank not initialized. Please use initialize_memory_bank tool first.'); 732 | } 733 | 734 | const documentType = uri.pathname.split('/').pop(); 735 | const validDocumentTypes = ['projectbrief', 'productContext', 'systemPatterns', 'techContext', 'activeContext', 'progress']; 736 | 737 | if (!documentType || !validDocumentTypes.includes(documentType)) { 738 | throw new Error(`Invalid document type: ${documentType}`); 739 | } 740 | 741 | const filePath = path.join(MEMORY_BANK_DIR, `${documentType}.md`); 742 | 743 | // Check if file exists 744 | if (!await fs.pathExists(filePath)) { 745 | // Create file if it doesn't exist 746 | await fs.ensureFile(filePath); 747 | await fs.writeFile(filePath, `# ${documentType}\n\nThis document has not been created yet.`, 'utf-8'); 748 | } 749 | 750 | const content = await readDocument(filePath); 751 | 752 | return { 753 | contents: [{ 754 | uri: uri.href, 755 | text: content 756 | }] 757 | }; 758 | } catch (error) { 759 | console.error('Error reading document:', error); 760 | throw error; 761 | } 762 | } 763 | ); 764 | 765 | // Export MCP server 766 | export default server; 767 | 768 | // Direct execution function 769 | export async function startServer(): Promise<void> { 770 | const transport = new StdioServerTransport(); 771 | 772 | try { 773 | console.log('Starting Memory Bank MCP server...'); 774 | await server.connect(transport); 775 | console.log('Memory Bank MCP server successfully started!'); 776 | } catch (error) { 777 | console.error('Error starting server:', error); 778 | process.exit(1); 779 | } 780 | } ```