Add default config system with ~/.claude/mcp-browser/ directory
- Created comprehensive default configuration system - Config directory at ~/.claude/mcp-browser/ with: - config.yaml with example MCP server configurations - onboarding.md with detailed usage documentation - patterns/ directory for system and custom patterns - logs/ and backups/ directories - Added checksum system to detect unmodified config files - Enhanced CLI with new options: - --list-servers to show available servers - --show-config to display configuration - --test to test server connections - --debug for detailed output - Improved help text with examples and better documentation - Updated ConfigLoader to use new config directory structure - First run automatically creates default configuration The system now provides a user-friendly experience similar to claude-composer with proper configuration management. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
27d0f610ba
commit
33ff1c33f5
|
|
@ -8,13 +8,20 @@ import asyncio
|
|||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
import yaml
|
||||
|
||||
from .proxy import MCPBrowser
|
||||
from .config import ConfigLoader
|
||||
from .default_configs import ConfigManager
|
||||
|
||||
|
||||
async def interactive_mode(browser: MCPBrowser):
|
||||
"""Run MCP Browser in interactive mode."""
|
||||
print("MCP Browser Interactive Mode")
|
||||
print("=" * 50)
|
||||
print(f"Server: {browser._server_name or browser.config.default_server}")
|
||||
print(f"Sparse mode: {'enabled' if browser.config.sparse_mode else 'disabled'}")
|
||||
print("Type 'help' for commands, 'exit' to quit\n")
|
||||
|
||||
while True:
|
||||
|
|
@ -28,12 +35,20 @@ async def interactive_mode(browser: MCPBrowser):
|
|||
print(" list - List available tools (sparse mode)")
|
||||
print(" discover <path> - Discover tools using JSONPath")
|
||||
print(" call <json> - Execute JSON-RPC call")
|
||||
print(" onboard <id> - Get onboarding for identity")
|
||||
print(" onboard [<id>] - Show/set onboarding for identity")
|
||||
print(" reload - Reload configuration")
|
||||
print(" status - Show connection status")
|
||||
print(" exit - Exit interactive mode")
|
||||
print("\nExamples:")
|
||||
print(' discover $.tools[*].name')
|
||||
print(' call {"method": "tools/list"}')
|
||||
print(' onboard MyProject')
|
||||
print(' discover $.tools[*].name # Get all tool names')
|
||||
print(' discover $.tools[0].inputSchema # Get first tool schema')
|
||||
print(' call {"method": "tools/list"} # Raw JSON-RPC call')
|
||||
print(' onboard MyProject # Get project onboarding')
|
||||
print('\nJSONPath syntax:')
|
||||
print(' $ - Root object')
|
||||
print(' .tools[*] - All tools')
|
||||
print(' .tools[0] - First tool')
|
||||
print(' .tools[*].name - All tool names')
|
||||
continue
|
||||
|
||||
elif command.startswith("discover "):
|
||||
|
|
@ -81,6 +96,71 @@ async def interactive_mode(browser: MCPBrowser):
|
|||
print(f"Error: {e}")
|
||||
|
||||
|
||||
def show_available_servers(config_path: Optional[str] = None):
|
||||
"""Show list of available MCP servers from configuration."""
|
||||
loader = ConfigLoader(Path(config_path) if config_path else None)
|
||||
config = loader.load()
|
||||
|
||||
print("Available MCP Servers:")
|
||||
print("=" * 50)
|
||||
|
||||
for name, server in config.servers.items():
|
||||
print(f"\n{name}:")
|
||||
print(f" Description: {server.description or 'No description'}")
|
||||
print(f" Command: {' '.join(server.command) if server.command else 'Built-in only'}")
|
||||
if server.env:
|
||||
print(f" Environment: {', '.join(server.env.keys())}")
|
||||
|
||||
print(f"\nDefault server: {config.default_server}")
|
||||
print(f"Config location: {loader.config_path}")
|
||||
|
||||
|
||||
def show_configuration(config_path: Optional[str] = None):
|
||||
"""Show current configuration file path and content."""
|
||||
loader = ConfigLoader(Path(config_path) if config_path else None)
|
||||
|
||||
print(f"Configuration file: {loader.config_path}")
|
||||
print("=" * 50)
|
||||
|
||||
if loader.config_path.exists():
|
||||
with open(loader.config_path) as f:
|
||||
print(f.read())
|
||||
else:
|
||||
print("Configuration file not found. Will be created on first run.")
|
||||
|
||||
|
||||
async def test_server_connection(browser: MCPBrowser, server_name: Optional[str] = None):
|
||||
"""Test connection to specified MCP server."""
|
||||
print(f"Testing connection to server: {server_name or 'default'}")
|
||||
print("=" * 50)
|
||||
|
||||
try:
|
||||
await browser.initialize()
|
||||
print("✓ Successfully connected to server")
|
||||
|
||||
# Try to list tools
|
||||
response = await browser.call({
|
||||
"jsonrpc": "2.0",
|
||||
"method": "tools/list"
|
||||
})
|
||||
|
||||
if "result" in response:
|
||||
tools = response["result"]["tools"]
|
||||
print(f"✓ Server provides {len(tools)} tools")
|
||||
if browser.config.sparse_mode:
|
||||
print(" (Showing sparse tools only)")
|
||||
else:
|
||||
print("✗ Failed to list tools")
|
||||
|
||||
except Exception as e:
|
||||
print(f"✗ Connection failed: {e}")
|
||||
return
|
||||
finally:
|
||||
await browser.close()
|
||||
|
||||
print("\nConnection test completed successfully!")
|
||||
|
||||
|
||||
async def run_server_mode(browser: MCPBrowser):
|
||||
"""Run MCP Browser as an MCP server (stdin/stdout)."""
|
||||
import sys
|
||||
|
|
@ -115,18 +195,54 @@ async def run_server_mode(browser: MCPBrowser):
|
|||
|
||||
def main():
|
||||
"""Main entry point."""
|
||||
parser = argparse.ArgumentParser(description="MCP Browser - Generic MCP interface")
|
||||
parser.add_argument("--server", "-s", help="Target MCP server name")
|
||||
parser.add_argument("--config", "-c", help="Configuration file path")
|
||||
parser = argparse.ArgumentParser(
|
||||
description="MCP Browser - Universal Model Context Protocol Interface",
|
||||
epilog="""
|
||||
Examples:
|
||||
mcp-browser # Start interactive mode with default server
|
||||
mcp-browser --server brave-search # Use Brave Search server
|
||||
mcp-browser --list-servers # List configured servers
|
||||
mcp-browser --show-config # Show current configuration
|
||||
mcp-browser --mode server # Run as MCP server (stdin/stdout)
|
||||
|
||||
Configuration:
|
||||
Default config: ~/.claude/mcp-browser/config.yaml
|
||||
First run creates default configuration with examples
|
||||
|
||||
Environment:
|
||||
Set API keys as needed: BRAVE_API_KEY, GITHUB_TOKEN, etc.
|
||||
Or source from: source ~/.secrets/api-keys.sh
|
||||
""",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter
|
||||
)
|
||||
parser.add_argument("--server", "-s", help="Target MCP server name (see --list-servers)")
|
||||
parser.add_argument("--config", "-c", help="Custom configuration file path")
|
||||
parser.add_argument("--mode", choices=["interactive", "server"],
|
||||
default="interactive", help="Operating mode")
|
||||
default="interactive", help="Operating mode (default: interactive)")
|
||||
parser.add_argument("--no-sparse", action="store_true",
|
||||
help="Disable sparse mode")
|
||||
help="Disable sparse mode (show all tools)")
|
||||
parser.add_argument("--no-builtin", action="store_true",
|
||||
help="Disable built-in servers")
|
||||
help="Disable built-in servers (screen, memory, patterns)")
|
||||
parser.add_argument("--list-servers", action="store_true",
|
||||
help="List available MCP servers from config")
|
||||
parser.add_argument("--show-config", action="store_true",
|
||||
help="Show current configuration path and content")
|
||||
parser.add_argument("--test", action="store_true",
|
||||
help="Test connection to specified server")
|
||||
parser.add_argument("--debug", action="store_true",
|
||||
help="Enable debug output")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Handle special commands first
|
||||
if args.list_servers:
|
||||
show_available_servers(args.config)
|
||||
return
|
||||
|
||||
if args.show_config:
|
||||
show_configuration(args.config)
|
||||
return
|
||||
|
||||
# Create browser
|
||||
config_path = Path(args.config) if args.config else None
|
||||
browser = MCPBrowser(
|
||||
|
|
@ -135,6 +251,11 @@ def main():
|
|||
enable_builtin_servers=not args.no_builtin
|
||||
)
|
||||
|
||||
# Handle test mode
|
||||
if args.test:
|
||||
asyncio.run(test_server_connection(browser, args.server))
|
||||
return
|
||||
|
||||
# Run in appropriate mode
|
||||
if args.mode == "server":
|
||||
asyncio.run(run_server_mode(browser))
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ from typing import Dict, Any, Optional, List
|
|||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
|
||||
from .default_configs import ConfigManager
|
||||
|
||||
|
||||
@dataclass
|
||||
class MCPServerConfig:
|
||||
|
|
@ -52,23 +54,18 @@ class ConfigLoader:
|
|||
}
|
||||
|
||||
def __init__(self, config_path: Optional[Path] = None):
|
||||
self.config_path = config_path or self._find_config_file()
|
||||
self.config_manager = ConfigManager()
|
||||
|
||||
# Use provided path or default config location
|
||||
if config_path:
|
||||
self.config_path = config_path
|
||||
else:
|
||||
# Ensure default config exists
|
||||
self.config_manager.ensure_config_directory()
|
||||
self.config_path = self.config_manager.get_config_path()
|
||||
|
||||
self._config: Optional[MCPBrowserConfig] = None
|
||||
|
||||
def _find_config_file(self) -> Optional[Path]:
|
||||
"""Find configuration file in standard locations."""
|
||||
locations = [
|
||||
Path.cwd() / "mcp-browser.yaml",
|
||||
Path.cwd() / ".mcp-browser" / "config.yaml",
|
||||
Path.home() / ".mcp-browser" / "config.yaml",
|
||||
Path(__file__).parent.parent / "config" / "default.yaml"
|
||||
]
|
||||
|
||||
for loc in locations:
|
||||
if loc.exists():
|
||||
return loc
|
||||
|
||||
return None
|
||||
|
||||
def load(self) -> MCPBrowserConfig:
|
||||
"""Load configuration from file or use defaults."""
|
||||
|
|
|
|||
|
|
@ -0,0 +1,462 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Default configuration files for MCP Browser.
|
||||
|
||||
Creates and manages default config directory at ~/.claude/mcp-browser/
|
||||
with all necessary files including config, onboarding, patterns, etc.
|
||||
"""
|
||||
|
||||
import os
|
||||
import hashlib
|
||||
from pathlib import Path
|
||||
from typing import Dict, Optional
|
||||
import yaml
|
||||
import json
|
||||
|
||||
|
||||
# Default configurations with their checksums
|
||||
# This allows us to detect unmodified files and update them
|
||||
DEFAULT_CONFIGS = {
|
||||
"config.yaml": {
|
||||
"checksum": "3f4e8b9c2a1d5678ef90ab12cd34567890abcdef1234567890abcdef12345678",
|
||||
"content": """# MCP Browser Configuration
|
||||
# This file is automatically generated and will be overwritten if unmodified
|
||||
# To customize, modify any value and the file will be preserved
|
||||
|
||||
# Server definitions - Add your MCP servers here
|
||||
servers:
|
||||
# Example: Brave Search (requires BRAVE_API_KEY environment variable)
|
||||
brave-search:
|
||||
command: ["npx", "-y", "@modelcontextprotocol/server-brave-search"]
|
||||
name: "brave-search"
|
||||
description: "Brave Search API access"
|
||||
env: {} # Environment variables are inherited, including BRAVE_API_KEY
|
||||
|
||||
# Example: Filesystem access
|
||||
filesystem:
|
||||
command: ["npx", "-y", "@modelcontextprotocol/server-filesystem", "/home"]
|
||||
name: "filesystem"
|
||||
description: "File system access (read-only by default)"
|
||||
args: ["--read-only"]
|
||||
|
||||
# Example: GitHub API (requires GITHUB_TOKEN)
|
||||
github:
|
||||
command: ["npx", "-y", "@modelcontextprotocol/server-github"]
|
||||
name: "github"
|
||||
description: "GitHub API access"
|
||||
env: {} # Inherits GITHUB_TOKEN from environment
|
||||
|
||||
# Example: Memory/Notes server
|
||||
memory:
|
||||
command: ["npx", "-y", "@modelcontextprotocol/server-memory"]
|
||||
name: "memory"
|
||||
description: "Persistent memory and notes"
|
||||
|
||||
# Example: Postgres database (requires connection string)
|
||||
# postgres:
|
||||
# command: ["npx", "-y", "@modelcontextprotocol/server-postgres", "postgresql://localhost/mydb"]
|
||||
# name: "postgres"
|
||||
# description: "PostgreSQL database access"
|
||||
|
||||
# Built-in servers only mode (no external server)
|
||||
builtin-only:
|
||||
command: null
|
||||
name: "builtin-only"
|
||||
description: "Use only built-in Python servers"
|
||||
|
||||
# Default server to use (change to your preferred server)
|
||||
default_server: "builtin-only"
|
||||
|
||||
# Enable sparse mode - Shows only essential tools initially
|
||||
# This saves context tokens by hiding tool descriptions
|
||||
sparse_mode: true
|
||||
|
||||
# Enable built-in servers (screen, memory, patterns, onboarding)
|
||||
# These provide core functionality without external dependencies
|
||||
enable_builtin_servers: true
|
||||
|
||||
# Debug mode - Shows detailed MCP communication
|
||||
debug: false
|
||||
|
||||
# Connection settings
|
||||
buffer_size: 65536
|
||||
timeout: 30.0
|
||||
|
||||
# Tool filtering in sparse mode
|
||||
# Only these tools are shown initially, others available via mcp_discover
|
||||
sparse_tools:
|
||||
- mcp_discover
|
||||
- mcp_call
|
||||
- onboarding
|
||||
"""
|
||||
},
|
||||
|
||||
"onboarding.md": {
|
||||
"checksum": "9f8e7d6c5b4a3210fedcba9876543210abcdef1234567890abcdef12345678",
|
||||
"content": """# MCP Browser - Model Context Protocol Browser
|
||||
|
||||
Welcome to MCP Browser! This tool provides a unified interface to interact with any MCP (Model Context Protocol) server while optimizing context usage for AI systems.
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### First Time Setup
|
||||
1. **Check available MCP servers:**
|
||||
```bash
|
||||
# List configured servers
|
||||
mcp-browser --list-servers
|
||||
|
||||
# Test connection to a server
|
||||
mcp-browser --server brave-search --test
|
||||
```
|
||||
|
||||
2. **Set up environment variables for servers:**
|
||||
```bash
|
||||
# For Brave Search
|
||||
export BRAVE_API_KEY="your-api-key"
|
||||
|
||||
# For GitHub
|
||||
export GITHUB_TOKEN="your-token"
|
||||
|
||||
# Or source from a secrets file
|
||||
source ~/.secrets/api-keys.sh
|
||||
```
|
||||
|
||||
### Basic Usage
|
||||
|
||||
1. **Interactive Mode (default):**
|
||||
```bash
|
||||
mcp-browser
|
||||
# or with a specific server
|
||||
mcp-browser --server brave-search
|
||||
```
|
||||
|
||||
2. **Available commands in interactive mode:**
|
||||
- `list` - Show available tools (sparse mode)
|
||||
- `discover <jsonpath>` - Discover tools using JSONPath
|
||||
- `call <json>` - Execute any JSON-RPC call
|
||||
- `onboard <identity>` - Get/set identity-specific instructions
|
||||
- `help` - Show command help
|
||||
- `exit` - Exit interactive mode
|
||||
|
||||
3. **Example workflows:**
|
||||
```bash
|
||||
# Discover all available tools
|
||||
> discover $.tools[*].name
|
||||
|
||||
# Search the web using Brave
|
||||
> call {"method": "tools/call", "params": {"name": "brave_web_search", "arguments": {"query": "MCP protocol"}}}
|
||||
|
||||
# Get onboarding for your project
|
||||
> onboard MyProject
|
||||
```
|
||||
|
||||
## 📊 Context Optimization Features
|
||||
|
||||
### Sparse Mode (Default)
|
||||
- Only shows 3 essential meta-tools initially
|
||||
- Use `mcp_discover` to find all available tools
|
||||
- Saves significant context tokens
|
||||
|
||||
### Virtual Tools
|
||||
1. **mcp_discover** - Discover tools using JSONPath queries
|
||||
2. **mcp_call** - Execute any MCP server tool
|
||||
3. **onboarding** - Identity-aware instruction management
|
||||
|
||||
### JSONPath Discovery Examples
|
||||
```javascript
|
||||
// Get all tool names
|
||||
$.tools[*].name
|
||||
|
||||
// Find tools with "search" in name
|
||||
$.tools[?(@.name =~ /search/i)].name
|
||||
|
||||
// Get input schemas for all tools
|
||||
$.tools[*].inputSchema
|
||||
|
||||
// Find specific tool details
|
||||
$.tools[?(@.name == 'brave_web_search')]
|
||||
```
|
||||
|
||||
## 🛠 Available MCP Servers
|
||||
|
||||
### Built-in Python Servers (always available)
|
||||
- **screen** - GNU screen session management
|
||||
- **memory** - Persistent memory and context
|
||||
- **patterns** - Auto-response patterns
|
||||
- **onboarding** - Identity-specific instructions
|
||||
|
||||
### External Servers (configure in config.yaml)
|
||||
- **brave-search** - Web search via Brave API
|
||||
- **filesystem** - Local file system access
|
||||
- **github** - GitHub API operations
|
||||
- **postgres** - PostgreSQL database access
|
||||
- **memory** - Persistent notes and memory
|
||||
- **slack** - Slack workspace access
|
||||
- **google-drive** - Google Drive operations
|
||||
|
||||
## 💡 Best Practices
|
||||
|
||||
1. **For AI/LLM Usage:**
|
||||
- Keep sparse mode enabled
|
||||
- Use `mcp_discover` to explore available tools
|
||||
- Batch multiple operations when possible
|
||||
- Store frequently used patterns in onboarding
|
||||
|
||||
2. **For Development:**
|
||||
- Test with `--server builtin-only` first
|
||||
- Use `--debug` to see MCP communication
|
||||
- Check server logs for troubleshooting
|
||||
|
||||
3. **For Context Efficiency:**
|
||||
- Use JSONPath to get only needed information
|
||||
- Cache tool names and reuse them
|
||||
- Leverage onboarding for project-specific patterns
|
||||
|
||||
## 🔧 Configuration
|
||||
|
||||
Configuration file: `~/.claude/mcp-browser/config.yaml`
|
||||
|
||||
Key settings:
|
||||
- `default_server` - Server to use by default
|
||||
- `sparse_mode` - Enable/disable sparse mode
|
||||
- `enable_builtin_servers` - Use built-in Python servers
|
||||
- `debug` - Show detailed debugging information
|
||||
|
||||
## 📝 Advanced Usage
|
||||
|
||||
### Server Mode (for integration)
|
||||
```bash
|
||||
# Run as MCP server (stdin/stdout)
|
||||
mcp-browser --mode server
|
||||
|
||||
# With specific configuration
|
||||
mcp-browser --config ~/myconfig.yaml --mode server
|
||||
```
|
||||
|
||||
### Custom Configurations
|
||||
1. Edit `~/.claude/mcp-browser/config.yaml`
|
||||
2. Add your MCP servers
|
||||
3. Set environment variables as needed
|
||||
4. Restart mcp-browser
|
||||
|
||||
### Identity-Specific Onboarding
|
||||
```bash
|
||||
# Set instructions for a specific identity
|
||||
> onboard ProjectX "You are working on ProjectX. Focus on API endpoints and testing."
|
||||
|
||||
# Retrieve instructions
|
||||
> onboard ProjectX
|
||||
```
|
||||
|
||||
## 🐛 Troubleshooting
|
||||
|
||||
1. **Server won't start:**
|
||||
- Check if required environment variables are set
|
||||
- Verify server command is correct
|
||||
- Use `--debug` flag for detailed output
|
||||
|
||||
2. **Tools not appearing:**
|
||||
- Ensure server is properly initialized
|
||||
- Try `discover $.tools[*]` to see raw data
|
||||
- Check if sparse mode is hiding tools
|
||||
|
||||
3. **Connection issues:**
|
||||
- Verify network connectivity
|
||||
- Check API keys and tokens
|
||||
- Look at debug output for errors
|
||||
|
||||
## 📚 Learn More
|
||||
|
||||
- MCP Specification: https://modelcontextprotocol.io
|
||||
- Available Servers: https://github.com/modelcontextprotocol/servers
|
||||
- Report Issues: https://github.com/Xilope0/mcp-browser
|
||||
|
||||
Remember: This tool is optimized for AI context efficiency. When in doubt, use `mcp_discover` to explore!
|
||||
"""
|
||||
},
|
||||
|
||||
"patterns/system-patterns.json": {
|
||||
"checksum": "1a2b3c4d5e6f7890abcdef1234567890abcdef1234567890abcdef12345678",
|
||||
"content": """{
|
||||
"patterns": [
|
||||
{
|
||||
"id": "list_tools_sparse",
|
||||
"pattern": ["list tools", "show tools", "what tools"],
|
||||
"replacement": "__SPARSE_TOOLS__",
|
||||
"description": "Show sparse tools when requested",
|
||||
"type": "system"
|
||||
},
|
||||
{
|
||||
"id": "discover_all_tools",
|
||||
"pattern": ["show all tools", "list all tools", "discover tools"],
|
||||
"replacement": "__DISCOVER_$.tools[*].name__",
|
||||
"description": "Discover all available tool names",
|
||||
"type": "system"
|
||||
},
|
||||
{
|
||||
"id": "help_jsonpath",
|
||||
"pattern": ["jsonpath help", "jsonpath examples", "how to use jsonpath"],
|
||||
"replacement": "__HELP_JSONPATH__",
|
||||
"description": "Show JSONPath query examples",
|
||||
"type": "system"
|
||||
}
|
||||
]
|
||||
}
|
||||
"""
|
||||
},
|
||||
|
||||
"patterns/custom-patterns.json": {
|
||||
"checksum": "2b3c4d5e6f7890abcdef1234567890abcdef1234567890abcdef12345678",
|
||||
"content": """{
|
||||
"patterns": []
|
||||
}
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ConfigManager:
|
||||
"""Manages MCP Browser configuration directory."""
|
||||
|
||||
def __init__(self, config_dir: Optional[Path] = None):
|
||||
"""Initialize config manager.
|
||||
|
||||
Args:
|
||||
config_dir: Override default config directory
|
||||
"""
|
||||
if config_dir:
|
||||
self.config_dir = Path(config_dir)
|
||||
else:
|
||||
self.config_dir = Path.home() / ".claude" / "mcp-browser"
|
||||
|
||||
def ensure_config_directory(self) -> None:
|
||||
"""Ensure config directory exists with all default files."""
|
||||
# Create directory structure
|
||||
self.config_dir.mkdir(parents=True, exist_ok=True)
|
||||
(self.config_dir / "patterns").mkdir(exist_ok=True)
|
||||
(self.config_dir / "logs").mkdir(exist_ok=True)
|
||||
(self.config_dir / "backups").mkdir(exist_ok=True)
|
||||
|
||||
# Create or update each config file
|
||||
for filename, config_data in DEFAULT_CONFIGS.items():
|
||||
self._ensure_config_file(filename, config_data)
|
||||
|
||||
def _ensure_config_file(self, filename: str, config_data: Dict[str, str]) -> None:
|
||||
"""Ensure a config file exists and is up to date.
|
||||
|
||||
Args:
|
||||
filename: Relative path to file within config dir
|
||||
config_data: Dict with 'checksum' and 'content'
|
||||
"""
|
||||
filepath = self.config_dir / filename
|
||||
|
||||
# Create parent directories if needed
|
||||
filepath.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Check if file exists and is unmodified
|
||||
if filepath.exists():
|
||||
current_checksum = self._calculate_checksum(filepath)
|
||||
if current_checksum != config_data["checksum"]:
|
||||
# File has been modified, don't overwrite
|
||||
return
|
||||
|
||||
# Write default content
|
||||
filepath.write_text(config_data["content"])
|
||||
|
||||
# Update checksum in our records
|
||||
config_data["checksum"] = self._calculate_checksum(filepath)
|
||||
|
||||
def _calculate_checksum(self, filepath: Path) -> str:
|
||||
"""Calculate SHA256 checksum of a file.
|
||||
|
||||
Args:
|
||||
filepath: Path to file
|
||||
|
||||
Returns:
|
||||
Hex string of checksum
|
||||
"""
|
||||
content = filepath.read_bytes()
|
||||
return hashlib.sha256(content).hexdigest()
|
||||
|
||||
def get_config_path(self) -> Path:
|
||||
"""Get path to main config file."""
|
||||
return self.config_dir / "config.yaml"
|
||||
|
||||
def get_onboarding_path(self) -> Path:
|
||||
"""Get path to onboarding file."""
|
||||
return self.config_dir / "onboarding.md"
|
||||
|
||||
def get_patterns_dir(self) -> Path:
|
||||
"""Get path to patterns directory."""
|
||||
return self.config_dir / "patterns"
|
||||
|
||||
def load_config(self) -> dict:
|
||||
"""Load the main configuration file.
|
||||
|
||||
Returns:
|
||||
Configuration dictionary
|
||||
"""
|
||||
config_path = self.get_config_path()
|
||||
if not config_path.exists():
|
||||
self.ensure_config_directory()
|
||||
|
||||
with open(config_path, 'r') as f:
|
||||
return yaml.safe_load(f)
|
||||
|
||||
def save_config(self, config: dict) -> None:
|
||||
"""Save configuration to file.
|
||||
|
||||
Args:
|
||||
config: Configuration dictionary
|
||||
"""
|
||||
config_path = self.get_config_path()
|
||||
with open(config_path, 'w') as f:
|
||||
yaml.dump(config, f, default_flow_style=False, sort_keys=False)
|
||||
|
||||
def get_onboarding_text(self, identity: Optional[str] = None) -> str:
|
||||
"""Get onboarding text, optionally for specific identity.
|
||||
|
||||
Args:
|
||||
identity: Optional identity for custom onboarding
|
||||
|
||||
Returns:
|
||||
Onboarding text
|
||||
"""
|
||||
onboarding_path = self.get_onboarding_path()
|
||||
if not onboarding_path.exists():
|
||||
self.ensure_config_directory()
|
||||
|
||||
base_text = onboarding_path.read_text()
|
||||
|
||||
# Check for identity-specific onboarding
|
||||
if identity:
|
||||
identity_file = self.config_dir / f"onboarding-{identity}.md"
|
||||
if identity_file.exists():
|
||||
return identity_file.read_text()
|
||||
|
||||
return base_text
|
||||
|
||||
def set_onboarding_text(self, text: str, identity: Optional[str] = None) -> None:
|
||||
"""Set onboarding text.
|
||||
|
||||
Args:
|
||||
text: Onboarding text to save
|
||||
identity: Optional identity for custom onboarding
|
||||
"""
|
||||
if identity:
|
||||
identity_file = self.config_dir / f"onboarding-{identity}.md"
|
||||
identity_file.write_text(text)
|
||||
else:
|
||||
self.get_onboarding_path().write_text(text)
|
||||
|
||||
|
||||
# Update checksums with actual values
|
||||
def update_checksums():
|
||||
"""Update the checksums in DEFAULT_CONFIGS with actual calculated values."""
|
||||
for filename, config_data in DEFAULT_CONFIGS.items():
|
||||
content_bytes = config_data["content"].encode('utf-8')
|
||||
config_data["checksum"] = hashlib.sha256(content_bytes).hexdigest()
|
||||
|
||||
|
||||
# Initialize checksums
|
||||
update_checksums()
|
||||
Loading…
Reference in New Issue