code-index-mcp
by johnhuang316
- code-index-mcp
- src
- code_index_mcp
"""
Project Settings Management
This module provides functionality for managing project settings and persistent data
for the Code Index MCP server.
"""
import os
import json
import shutil
import pickle
from datetime import datetime
class ProjectSettings:
"""管理專案設定和索引資料的類"""
SETTINGS_DIR = ".code_indexer"
CONFIG_FILE = "config.json"
INDEX_FILE = "file_index.pickle"
CACHE_FILE = "content_cache.pickle"
def __init__(self, base_path):
"""初始化專案設定
Args:
base_path (str): 專案的基本路徑
"""
self.base_path = base_path
self.settings_path = os.path.join(base_path, self.SETTINGS_DIR)
self.ensure_settings_dir()
def ensure_settings_dir(self):
"""確保設定目錄存在"""
if not os.path.exists(self.settings_path):
os.makedirs(self.settings_path)
# 創建一個 README.md 文件,說明該目錄的用途
readme_path = os.path.join(self.settings_path, "README.md")
with open(readme_path, 'w') as f:
f.write("# Code Indexer Cache Directory\n\nThis directory contains cached data for the Code Index MCP tool:\n\n- `config.json`: Project configuration\n- `file_index.pickle`: Index of project files\n- `content_cache.pickle`: Cached file contents\n\nThese files are automatically generated and should not be committed to version control.\n")
def get_config_path(self):
"""獲取配置文件的路徑"""
return os.path.join(self.settings_path, self.CONFIG_FILE)
def get_index_path(self):
"""獲取索引文件的路徑"""
return os.path.join(self.settings_path, self.INDEX_FILE)
def get_cache_path(self):
"""獲取緩存文件的路徑"""
return os.path.join(self.settings_path, self.CACHE_FILE)
def _get_timestamp(self):
"""獲取當前時間戳"""
return datetime.now().isoformat()
def save_config(self, config):
"""保存配置數據
Args:
config (dict): 配置數據
"""
config_path = self.get_config_path()
# 添加時間戳
config['last_updated'] = self._get_timestamp()
with open(config_path, 'w', encoding='utf-8') as f:
json.dump(config, f, indent=2, ensure_ascii=False)
return config
def load_config(self):
"""加載配置數據
Returns:
dict: 配置數據,如果文件不存在則返回空字典
"""
config_path = self.get_config_path()
if os.path.exists(config_path):
try:
with open(config_path, 'r', encoding='utf-8') as f:
return json.load(f)
except (json.JSONDecodeError, UnicodeDecodeError):
# 如果文件損壞,返回空字典
return {}
return {}
def save_index(self, file_index):
"""保存文件索引
Args:
file_index (dict): 文件索引數據
"""
index_path = self.get_index_path()
with open(index_path, 'wb') as f:
pickle.dump(file_index, f)
def load_index(self):
"""加載文件索引
Returns:
dict: 文件索引數據,如果文件不存在則返回空字典
"""
index_path = self.get_index_path()
if os.path.exists(index_path):
try:
with open(index_path, 'rb') as f:
return pickle.load(f)
except (pickle.PickleError, EOFError):
# 如果文件損壞,返回空字典
return {}
return {}
def save_cache(self, content_cache):
"""保存內容緩存
Args:
content_cache (dict): 內容緩存數據
"""
cache_path = self.get_cache_path()
with open(cache_path, 'wb') as f:
pickle.dump(content_cache, f)
def load_cache(self):
"""加載內容緩存
Returns:
dict: 內容緩存數據,如果文件不存在則返回空字典
"""
cache_path = self.get_cache_path()
if os.path.exists(cache_path):
try:
with open(cache_path, 'rb') as f:
return pickle.load(f)
except (pickle.PickleError, EOFError):
# 如果文件損壞,返回空字典
return {}
return {}
def clear(self):
"""清除所有設定和緩存文件"""
if os.path.exists(self.settings_path):
# 保留 .gitignore 文件
gitignore_path = os.path.join(self.settings_path, ".gitignore")
has_gitignore = os.path.exists(gitignore_path)
gitignore_content = None
if has_gitignore:
with open(gitignore_path, 'r') as f:
gitignore_content = f.read()
# 刪除資料夾中的所有文件
for filename in os.listdir(self.settings_path):
file_path = os.path.join(self.settings_path, filename)
if os.path.isfile(file_path):
os.unlink(file_path)
elif os.path.isdir(file_path):
shutil.rmtree(file_path)
# 恢復 .gitignore 文件
if has_gitignore:
with open(gitignore_path, 'w') as f:
f.write(gitignore_content)
def get_stats(self):
"""獲取設定目錄的統計信息
Returns:
dict: 包含文件大小和更新時間的字典
"""
stats = {
'exists': os.path.exists(self.settings_path),
'files': {}
}
if stats['exists']:
for filename in [self.CONFIG_FILE, self.INDEX_FILE, self.CACHE_FILE]:
file_path = os.path.join(self.settings_path, filename)
if os.path.exists(file_path):
file_stats = os.stat(file_path)
stats['files'][filename] = {
'size_bytes': file_stats.st_size,
'last_modified': datetime.fromtimestamp(file_stats.st_mtime).isoformat()
}
return stats