feat: config loader with validation and tilde expansion
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,20 @@
|
|||||||
|
import os
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
CONFIG_PATH = os.path.expanduser('~/.claude/autoresearch.yaml')
|
||||||
|
DEFAULTS = {'time_limit_minutes': 10, 'benchmarks': []}
|
||||||
|
|
||||||
|
class ConfigError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def load_config(path=CONFIG_PATH):
|
||||||
|
with open(path) as f:
|
||||||
|
cfg = yaml.safe_load(f)
|
||||||
|
if not cfg or 'projects' not in cfg or not cfg['projects']:
|
||||||
|
raise ConfigError(f"'projects' list is required in {path}")
|
||||||
|
for p in cfg['projects']:
|
||||||
|
p.setdefault('benchmarks', DEFAULTS['benchmarks'])
|
||||||
|
p.setdefault('time_limit_minutes', DEFAULTS['time_limit_minutes'])
|
||||||
|
p['path'] = os.path.expanduser(p['path'])
|
||||||
|
cfg.setdefault('token_threshold', {'context_remaining_pct': 60, 'api_budget_usd': 5.0})
|
||||||
|
return cfg
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import sys
|
||||||
|
sys.path.insert(0, 'bin')
|
||||||
|
from config import load_config, ConfigError
|
||||||
|
import pytest, tempfile, os, yaml
|
||||||
|
|
||||||
|
def test_load_valid_config():
|
||||||
|
data = {
|
||||||
|
'projects': [{'path': '/tmp', 'benchmarks': ['echo ok'], 'time_limit_minutes': 5}],
|
||||||
|
'token_threshold': {'context_remaining_pct': 60, 'api_budget_usd': 5.0}
|
||||||
|
}
|
||||||
|
with tempfile.NamedTemporaryFile('w', suffix='.yaml', delete=False) as f:
|
||||||
|
yaml.dump(data, f)
|
||||||
|
name = f.name
|
||||||
|
cfg = load_config(name)
|
||||||
|
assert cfg['projects'][0]['time_limit_minutes'] == 5
|
||||||
|
os.unlink(name)
|
||||||
|
|
||||||
|
def test_missing_projects_raises():
|
||||||
|
with tempfile.NamedTemporaryFile('w', suffix='.yaml', delete=False) as f:
|
||||||
|
yaml.dump({}, f)
|
||||||
|
name = f.name
|
||||||
|
with pytest.raises(ConfigError):
|
||||||
|
load_config(name)
|
||||||
|
os.unlink(name)
|
||||||
|
|
||||||
|
def test_expands_tilde_in_path():
|
||||||
|
data = {
|
||||||
|
'projects': [{'path': '~/.claude', 'benchmarks': [], 'time_limit_minutes': 5}],
|
||||||
|
'token_threshold': {'context_remaining_pct': 60, 'api_budget_usd': 5.0}
|
||||||
|
}
|
||||||
|
with tempfile.NamedTemporaryFile('w', suffix='.yaml', delete=False) as f:
|
||||||
|
yaml.dump(data, f)
|
||||||
|
name = f.name
|
||||||
|
cfg = load_config(name)
|
||||||
|
assert '~' not in cfg['projects'][0]['path']
|
||||||
|
os.unlink(name)
|
||||||
Reference in New Issue
Block a user