Config Service
The Config Service provides comprehensive TOML-based configuration management for SpecifyX projects. It handles project-specific and global configuration files, branch naming pattern validation, and cross-platform compatibility.
Overview
The service is built around two main components:
- ConfigService (abstract interface) - Defines the contract for configuration operations
- TomlConfigService (concrete implementation) - Implements TOML-based configuration management
Key Features
Configuration Management
- Project-specific configuration in
.specify/config.toml
- Global user configuration in
~/.specify/config.toml
- Hierarchical configuration merging (defaults → global → project)
- Configuration backup and restore capabilities
Branch Naming System
- Flexible branch naming patterns with variable substitution
- Comprehensive pattern validation with detailed error messages
- Support for multiple patterns per project
- Special placeholder expansion (date, number sequences, etc.)
Cross-Platform Support
- Platform-aware path normalization
- Windows/Unix path separator handling
- Cross-platform configuration portability
Core Classes
ConfigService (Abstract)
class ConfigService(ABC):
def load_project_config(self, project_path: Path) -> Optional[ProjectConfig]
def save_project_config(self, project_path: Path, config: ProjectConfig) -> bool
def validate_branch_pattern(self, pattern: str) -> Tuple[bool, Optional[str]]
def generate_branch_name(self, pattern: str, context: Dict[str, str], validation_rules: List[str]) -> Tuple[str, bool, Optional[str]]
TomlConfigService (Implementation)
service = TomlConfigService()
config = service.load_project_config(Path("/path/to/project"))
merged = service.get_merged_config(Path("/path/to/project"))
is_valid, error = service.validate_branch_pattern("feature/{feature-name}")
Configuration Structure
Project Configuration (.specify/config.toml
)
[project]
name = "my-project"
version = "1.0.0"
[branch_naming]
patterns = [
"feature/{feature-name}",
"hotfix/{bug-id}",
"main",
"development"
]
validation_rules = [
"max_length_60",
"lowercase_only",
"alphanumeric_dash_slash_only",
"no_double_dashes",
"valid_git_branch"
]
[template_settings]
ai_assistant = "claude"
config_directory = ".claude"
custom_templates_dir = "custom/templates"
template_cache_enabled = true
Global Configuration (~/.specify/config.toml
)
[branch_naming]
patterns = ["feature/{feature-name}"]
validation_rules = ["max_length_60", "lowercase_only"]
[template_settings]
ai_assistant = "claude"
template_cache_enabled = true
Branch Naming Patterns
Pattern Syntax
Branch patterns support variable substitution with curly braces:
feature/{feature-name} # feature/user-authentication
hotfix/{bug-id} # hotfix/fix-login-bug
release/{version} # release/v1.2.0
{team}/{feature-name} # backend/api-refactor
Special Placeholders
{number-3} # 001, 002, 003 (3-digit sequence)
{date} # 2025-09-11 (current date)
{datetime} # 2025-09-11-143022 (current datetime)
{timestamp} # 1694456202 (unix timestamp)
{spec-id} # 001 (3-digit spec identifier)
Validation Rules
max_length_X # Maximum length limit
lowercase_only # Only lowercase characters
no_spaces # No space characters
alphanumeric_dash_only # Only a-z, 0-9, and -
alphanumeric_dash_slash_only # Only a-z, 0-9, -, and /
no_leading_trailing_dashes # No dashes at start/end
no_double_dashes # No consecutive dashes
no_dots # No dot characters
valid_git_branch # Valid git branch name
Branch Name Generation
Basic Generation
pattern = "feature/{feature-name}"
context = {"feature-name": "user-auth"}
rules = ["max_length_60", "lowercase_only"]
branch_name, is_valid, error = service.generate_branch_name(pattern, context, rules)
# Result: "feature/user-auth", True, None
Pattern Validation
is_valid, error = service.validate_branch_pattern("feature/{feature-name}")
# Result: True, None
is_valid, error = service.validate_branch_pattern("feature/{}")
# Result: False, "Empty variable name in braces"
Branch Name Validation
is_valid, error = service.validate_branch_name_matches_pattern(
"feature/user-auth",
"feature/{feature-name}"
)
# Result: True, None
Configuration Operations
Loading Configuration
# Load project configuration
project_config = service.load_project_config(Path("/path/to/project"))
# Load global configuration
global_config = service.load_global_config()
# Get merged configuration (global + project)
merged_config = service.get_merged_config(Path("/path/to/project"))
Saving Configuration
config = ProjectConfig.create_default("my-project")
success = service.save_project_config(Path("/path/to/project"), config)
# Cross-platform save
success = service.save_project_config_cross_platform(
Path("/path/to/project"), config, "windows"
)
Configuration Backup
# Create backup
backup_path = service.backup_config(Path("/path/to/project"))
# Restore from backup
success = service.restore_config(Path("/path/to/project"), backup_path)
Error Handling
The service provides detailed error messages for various scenarios:
Pattern Validation Errors
- Empty or whitespace-only patterns
- Mismatched braces
- Invalid characters in patterns
- Invalid variable names
Branch Name Generation Errors
- Unfilled placeholders in generated names
- Validation rule violations
- Pattern-name mismatches
Configuration Errors
- TOML parsing errors
- File permission issues
- Invalid configuration structures
Usage Examples
Project Setup
from specify_cli.services.config_service import TomlConfigService
from specify_cli.models.config import BranchNamingConfig
service = TomlConfigService()
# Ensure project has valid configuration
branch_config = BranchNamingConfig(
patterns=["feature/{feature-name}", "hotfix/{bug-id}"],
validation_rules=["max_length_60", "lowercase_only"]
)
config = service.ensure_project_config(
project_path=Path("/path/to/project"),
ai_assistant="claude",
branch_naming_config=branch_config
)
Branch Name Workflow
# Validate and generate branch name
pattern = "feature/{feature-name}"
context = {"feature-name": "user-authentication"}
rules = ["max_length_60", "lowercase_only", "valid_git_branch"]
branch_name, is_valid, error = service.generate_branch_name(pattern, context, rules)
if is_valid:
print(f"Generated branch: {branch_name}")
else:
print(f"Error: {error}")
Configuration Management
# Load and modify configuration
config = service.load_project_config(Path("/path/to/project"))
if config:
# Update AI assistant
config.template_settings.ai_assistant = "gemini"
service.save_project_config(Path("/path/to/project"), config)
Integration Points
The Config Service integrates with:
- Project Manager: For project initialization and configuration setup
- Template Service: For AI assistant and template settings
- Git Service: For branch naming and repository operations
File Locations
- Project Config:
{project}/.specify/config.toml
- Global Config:
~/.specify/config.toml
- Config Backups:
{project}/.specify/backups/config_backup_{timestamp}.toml
Performance Considerations
- Configuration files are loaded on-demand
- Validation results are computed fresh (no caching for accuracy)
- File operations use atomic writes where possible
- Cross-platform path normalization is applied at save/load time