This guide provides comprehensive documentation for developers who want to contribute to or extend the Code Assistant Manager project.
- Project Overview
- Architecture
- Code Structure
- Adding New Tools
- Configuration System
- Testing
- Security Considerations
- Performance Optimization
- Contributing
Code Assistant Manager is a Python-based command-line interface that provides unified access to various AI providers (Claude, Codex, Qwen, etc.) with interactive model selection and endpoint configuration. The project aims to provide a consistent and secure way to interact with different AI services through a single interface.
Key features:
- Interactive model selection with dynamic filtering
- Centralized configuration management
- Secure API key handling
- Extensible tool framework
- Comprehensive test suite
The project follows a modular architecture with the following key components:
-
CLI Interface (
code_assistant_manager/cli/)- Main Entry Point (
app.py): Typer-based CLI with sub-apps for different functionality - Command Classes (
base_commands.py): Standardized base classes for consistent CLI behaviorBaseCommand: Common functionality, error handling, loggingAppAwareCommand: Commands that work with AI appsPluginCommand: Plugin-specific operationsPromptCommand: Prompt management operations
- Sub-Apps: Modular command groups (agents, plugins, prompts, skills, MCP)
- Options & Utils: Shared CLI utilities and option definitions
- Main Entry Point (
-
Configuration Management (
code_assistant_manager/config.py)- JSON-based configuration parsing
- Endpoint configuration management
- Environment variable integration
- Configuration validation
-
MCP System (
code_assistant_manager/mcp/)- Base Classes: Common MCP functionality and client implementations
- Client Implementations: Tool-specific MCP clients (Claude, Copilot, etc.)
- Registry System: MCP server registry and management
- Installation System: Automated MCP server installation and management
-
Prompts System (
code_assistant_manager/prompts.py)- Centralized prompt storage and management
- Multi-level support (user, project, Copilot-specific)
- Sync functionality across different AI assistants
- Template system for reusable prompt components
-
Plugin System (
code_assistant_manager/plugins.py)- Marketplace-based plugin distribution
- Repository management for plugin sources
- Installation and management across AI assistants
- Version compatibility checking
# New standardized pattern using base classes
class MyCommand(BaseCommand):
def execute(self) -> int:
self.log_command_start("my_command")
try:
# Command logic here
self.show_success("Operation completed")
return 0
except Exception as e:
self.handle_error(e, "Command failed")
return 1- Consistent error messages with color coding
- Structured logging for debugging
- User-friendly error descriptions
- Automatic cleanup on failures
- Input validation with descriptive error messages
- Type checking and constraint validation
- Choice validation for enumerated options
- File/path existence checking
- Complexity Monitoring: CI pipeline checks cyclomatic complexity and maintainability index
- File Size Limits: Automated checks ensure files stay under 500 lines
- Security Audits: Regular scanning for command injection vulnerabilities
- Test Coverage: Comprehensive test suite with helper functions for CLI testing
- Monolithic Function Breakdown:
show_live_prompt()split into focused helper functions - MCP Client Consolidation: Reduced duplication in client implementations
- Base Command Classes: Standardized CLI command patterns
- Helper Function Extraction: Common functionality moved to reusable utilities
code-assistant-manager/
├── code_assistant_manager/
│ ├── cli/
│ │ ├── __init__.py
│ │ ├── app.py # Main Typer app with sub-apps
│ │ ├── base_commands.py # Standardized command base classes
│ │ ├── commands.py # Legacy command definitions
│ │ ├── option_utils.py # Shared CLI utilities
│ │ ├── options.py # Common CLI option definitions
│ │ ├── prompts_commands.py # Prompt management commands
│ │ ├── plugin_commands.py # Plugin command orchestration
│ │ ├── plugins/ # Plugin subcommand modules
│ │ │ ├── plugin_discovery_commands.py
│ │ │ ├── plugin_install_commands.py
│ │ │ └── plugin_marketplace_commands.py
│ │ └── skills_commands.py # Skills management commands
│ ├── mcp/
│ │ ├── base.py # MCP base classes and utilities
│ │ ├── base_client.py # MCP client base class
│ │ ├── clients.py # Individual MCP client implementations
│ │ └── [tool]_client.py # Tool-specific MCP clients
│ ├── config.py # Configuration management
│ ├── endpoints.py # Endpoint handling
│ ├── prompts.py # Prompt storage and sync logic
│ ├── plugins.py # Plugin system core
│ └── tools.py # Tool implementations
├── docs/ # Documentation
├── scripts/ # Utility scripts (file size checks, etc.)
├── tests/ # Comprehensive test suite
└── .github/workflows/ # CI/CD pipelines with quality checks
To add a new CLI command, follow the standardized patterns using the base command classes:
from code_assistant_manager.cli.base_commands import BaseCommand, AppAwareCommand
class MyNewCommand(AppAwareCommand):
"""Example command using the standardized base class."""
def execute(self) -> int:
"""Execute the command with proper error handling and logging."""
self.log_command_start("my_new_command")
# Validate inputs
app_type = self.resolve_app_type(self.app_type_option)
self.validate_app_installed(app_type)
try:
# Command logic here
result = self.perform_operation(app_type)
self.show_success(f"Operation completed for {app_type}")
return 0
except Exception as e:
self.handle_error(e, f"Failed to execute command for {app_type}")
return 1
def perform_operation(self, app_type: str) -> bool:
"""Actual command implementation."""
# Implementation here
passAdd the command to the appropriate CLI module and register it in the app:
# In your command module
from code_assistant_manager.cli.base_commands import create_command_handler
@prompt_app.command("my-command")
def my_command_handler(
param1: str = typer.Option(..., help="Parameter description"),
app_type: str = typer.Option("claude", help="App type"),
) -> None:
"""Command docstring."""
command = MyNewCommand()
exit_code = command.execute(param1=param1, app_type=app_type)
raise typer.Exit(exit_code)- Use
self.handle_error()for exceptions that should terminate the command - Use
self.show_warning()for non-critical issues that should be displayed but allow continuation - Use
self.show_success()for successful operations - All logging is automatically handled by the base classes
To add a new tool to Code Assistant Manager, follow these steps:
Extend the CLITool base class in code_assistant_manager/tools.py:
class NewTool(CLITool):
"""Description of the new tool."""
command_name = "newtool"
tool_key = "newtool-key"
install_description = "New Tool Description"
def run(self, args: List[str] = []) -> int:
"""Run the new tool."""
# Implementation here
passThe tool is automatically registered through class inheritance. The get_registered_tools() function will discover it.
Add the entry point function in code_assistant_manager/cli.py:
def newtool_main():
"""Entry point for 'newtool' command."""
sys.argv.insert(1, 'newtool')
sys.exit(main())Add the console script entry point in setup.py:
entry_points={
'console_scripts': [
# ... existing entries
"newtool=code_assistant_manager.cli:newtool_main",
]
}Add the tool configuration to tools.yaml:
newtool-key:
enabled: true # Set to false to hide from menus
install_cmd: npm install -g newtool
cli_command: newtool
description: "New Tool description"
env:
exported:
NEWTOOL_API_KEY: "Resolved API key"
configuration:
required:
endpoint: "Base URL for the API"
list_models_cmd: "Command to list models"Tools can be shown or hidden from the interactive menu using the enabled key in tools.yaml:
enabled: true(default) - Tool appears in menus and can be launchedenabled: false- Tool is hidden from menus (useful for tools under development)
If the enabled key is not specified, it defaults to true for backward compatibility.
Example - disabling a tool:
my-experimental-tool:
enabled: false # Hidden from menu
install_cmd: npm install -g my-tool
cli_command: mytool
description: "Experimental tool - not ready yet"The configuration system uses JSON format with two main sections:
Global settings that apply to all endpoints:
{
"common": {
"http_proxy": "http://proxy.example.com:3128/",
"https_proxy": "http://proxy.example.com:3128/",
"no_proxy": "localhost,127.0.0.1",
"cache_ttl_seconds": 3600
}
}Individual endpoint configurations:
{
"endpoints": {
"endpoint-name": {
"endpoint": "https://api.example.com",
"api_key": "your-api-key",
"api_key_env": "API_KEY_NAME",
"list_models_cmd": "echo model1 model2",
"keep_proxy_config": false,
"use_proxy": true,
"description": "Endpoint description",
"supported_client": "tool1,tool2"
}
}
}The project uses pytest for testing with the following structure:
tests/test_cli.py: CLI interface teststests/test_config.py: Configuration management teststests/test_endpoints.py: Endpoint handling teststests/test_tools.py: Tool implementation teststests/test_ui.py: UI component teststests/test_integration.py: Integration tests
# Run all tests
python -m pytest tests/
# Run specific test file
python -m pytest tests/test_config.py
# Run with coverage
python -m pytest --cov=code_assistant_manager tests/Follow these guidelines when writing tests:
- Use pytest fixtures for test data
- Mock external dependencies
- Test both success and error cases
- Include edge case testing
- Use descriptive test names
Example test:
def test_config_reload_updates_data(self, temp_config):
"""Test that reloading config updates the data."""
config = ConfigManager(temp_config)
# Test initial state
sections_before = config.get_sections()
assert 'test-endpoint' in sections_before
# Modify config
# ... modification code ...
# Reload and verify
config.reload()
sections_after = config.get_sections()
# ... assertions ...The validate_command function in config.py provides comprehensive validation of shell commands:
- Dangerous pattern detection
- File path validation
- Executable whitelisting
- Argument sanitization
- Environment variable precedence
- Secure masking in output
- Multiple resolution methods
- File permission checks
All user inputs are validated:
- URL format validation
- API key format validation
- Model ID validation
- Boolean value validation
Model lists are cached with TTL to reduce API calls:
- Cache location:
${XDG_CACHE_HOME:-$HOME/.cache}/code-assistant-manager - Configurable TTL in seconds
- Atomic cache file operations
- Cache validation and refresh options
- Efficient data structures
- Proper resource cleanup
- Minimal memory footprint
- Lazy loading where appropriate
- Clone the repository
- Create a virtual environment
- Install dependencies
- Run tests to verify setup
git clone https://github.com/your-org/code-assistant-manager.git
cd code-assistant-manager
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
pip install -e .
python -m pytest tests/Follow these guidelines:
- Use type hints for all functions
- Write docstrings for all public functions
- Follow PEP 8 style guide
- Keep functions focused and small
- Use meaningful variable names
- Fork the repository
- Create a feature branch
- Implement your changes
- Add tests for new functionality
- Update documentation
- Run all tests
- Submit pull request
When reporting issues, include:
- Clear description of the problem
- Steps to reproduce
- Expected vs actual behavior
- Environment information
- Relevant configuration
- Error messages or logs