feat: add cli_memory core modules
- __init__.py: Version and package info - cli.py: Main CLI entry point with commands - config.py: Configuration management
This commit is contained in:
161
cli_memory/config.py
Normal file
161
cli_memory/config.py
Normal file
@@ -0,0 +1,161 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
import yaml
|
||||
from dotenv import load_dotenv
|
||||
|
||||
|
||||
class Config:
|
||||
def __init__(self, config_path: Optional[str] = None):
|
||||
self.config_path = config_path or self._find_config_file()
|
||||
self._config: Dict[str, Any] = {}
|
||||
self._load_config()
|
||||
|
||||
def _find_config_file(self) -> str:
|
||||
env_path = os.environ.get("CLI_MEMORY_CONFIG")
|
||||
if env_path and os.path.exists(env_path):
|
||||
return env_path
|
||||
home_config = os.path.expanduser("~/.cli_memory/config.yaml")
|
||||
if os.path.exists(home_config):
|
||||
return home_config
|
||||
local_config = os.path.join(os.getcwd(), "config.yaml")
|
||||
if os.path.exists(local_config):
|
||||
return local_config
|
||||
return home_config
|
||||
|
||||
def _load_config(self) -> None:
|
||||
load_dotenv()
|
||||
self._config = {
|
||||
"database": self._load_database_config(),
|
||||
"recording": self._load_recording_config(),
|
||||
"project": self._load_project_config(),
|
||||
"search": self._load_search_config(),
|
||||
"suggestions": self._load_suggestions_config(),
|
||||
"patterns": self._load_patterns_config(),
|
||||
"script": self._load_script_config(),
|
||||
"playback": self._load_playback_config(),
|
||||
"shell": self._load_shell_config(),
|
||||
"logging": self._load_logging_config(),
|
||||
}
|
||||
self._apply_env_overrides()
|
||||
|
||||
def _load_database_config(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"path": os.environ.get("DATABASE_PATH", "~/.cli_memory/database.db"),
|
||||
"wal_mode": True,
|
||||
"timeout": 30.0,
|
||||
}
|
||||
|
||||
def _load_recording_config(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"max_commands_per_workflow": int(os.environ.get("MAX_WORKFLOW_COMMANDS", 100)),
|
||||
"min_commands_for_workflow": 3,
|
||||
"auto_save_interval": 10,
|
||||
"capture_exit_code": True,
|
||||
"capture_duration": True,
|
||||
}
|
||||
|
||||
def _load_project_config(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"auto_detect_git": True,
|
||||
"cache_ttl": 3600,
|
||||
"excluded_dirs": ["node_modules", "__pycache__", ".git", "venv", ".venv"],
|
||||
}
|
||||
|
||||
def _load_search_config(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"max_results": 50,
|
||||
"default_limit": 20,
|
||||
"enable_fuzzy": True,
|
||||
"fuzzy_threshold": 0.6,
|
||||
}
|
||||
|
||||
def _load_suggestions_config(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"max_suggestions": int(os.environ.get("MAX_SUGGESTIONS", 10)),
|
||||
"min_confidence": 0.3,
|
||||
"decay_factor": 0.95,
|
||||
"recency_weight": 0.3,
|
||||
"frequency_weight": 0.4,
|
||||
"context_weight": 0.3,
|
||||
}
|
||||
|
||||
def _load_patterns_config(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"min_sequence_length": 3,
|
||||
"min_occurrences": 2,
|
||||
"max_pattern_length": 10,
|
||||
"similarity_threshold": 0.8,
|
||||
"timeout": 30.0,
|
||||
}
|
||||
|
||||
def _load_script_config(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"output_dir": os.path.expanduser("~/.cli_memory/scripts"),
|
||||
"include_error_handling": True,
|
||||
"include_logging": True,
|
||||
"dry_run_default": False,
|
||||
}
|
||||
|
||||
def _load_playback_config(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"default_speed": 1.0,
|
||||
"confirm_each": False,
|
||||
"show_progress": True,
|
||||
"pause_on_error": True,
|
||||
}
|
||||
|
||||
def _load_shell_config(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"enable_autocomplete": os.environ.get("ENABLE_AUTOCOMPLETE", "true").lower() == "true",
|
||||
"prompt_command": "cm-prompt",
|
||||
"history_file": os.path.expanduser("~/.cli_memory/shell_history"),
|
||||
}
|
||||
|
||||
def _load_logging_config(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"level": os.environ.get("LOG_LEVEL", "info"),
|
||||
"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
||||
"file": os.path.expanduser("~/.cli_memory/logs/app.log"),
|
||||
}
|
||||
|
||||
def _apply_env_overrides(self) -> None:
|
||||
if os.environ.get("CLI_MEMORY_HOME"):
|
||||
home = os.path.expanduser(os.environ["CLI_MEMORY_HOME"])
|
||||
self._config["database"]["path"] = os.path.join(home, "database.db")
|
||||
self._config["script"]["output_dir"] = os.path.join(home, "scripts")
|
||||
self._config["shell"]["history_file"] = os.path.join(home, "shell_history")
|
||||
self._config["logging"]["file"] = os.path.join(home, "logs", "app.log")
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
keys = key.split(".")
|
||||
value = self._config
|
||||
for k in keys:
|
||||
if isinstance(value, dict) and k in value:
|
||||
value = value[k]
|
||||
else:
|
||||
return default
|
||||
return value
|
||||
|
||||
def set(self, key: str, value: Any) -> None:
|
||||
keys = key.split(".")
|
||||
config = self._config
|
||||
for k in keys[:-1]:
|
||||
if k not in config:
|
||||
config[k] = {}
|
||||
config = config[k]
|
||||
config[keys[-1]] = value
|
||||
|
||||
def reload(self) -> None:
|
||||
self._load_config()
|
||||
|
||||
def get_home_dir(self) -> str:
|
||||
home = os.environ.get("CLI_MEMORY_HOME", "~/.cli_memory")
|
||||
return os.path.expanduser(home)
|
||||
|
||||
def ensure_directories(self) -> None:
|
||||
home = self.get_home_dir()
|
||||
for subdir in ["scripts", "logs"]:
|
||||
path = os.path.join(home, subdir)
|
||||
os.makedirs(path, exist_ok=True)
|
||||
Reference in New Issue
Block a user