From 10a05ba55aa1c21bde9f2c66973b4fe8115a57bb Mon Sep 17 00:00:00 2001 From: "thomas.kopp" Date: Sat, 4 Apr 2026 14:16:49 +0200 Subject: [PATCH] feat: config loader with validation and tilde expansion Co-Authored-By: Claude Sonnet 4.6 --- bin/config.py | 20 ++++++++++++++++++++ tests/test_config.py | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 bin/config.py create mode 100644 tests/test_config.py diff --git a/bin/config.py b/bin/config.py new file mode 100644 index 0000000..d65b29d --- /dev/null +++ b/bin/config.py @@ -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 diff --git a/tests/test_config.py b/tests/test_config.py new file mode 100644 index 0000000..cd4a75a --- /dev/null +++ b/tests/test_config.py @@ -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)