Files
agent-aide/aide-program/aide/core/config.py

129 lines
3.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""配置管理:生成默认配置、读取/写入配置、维护 .aide 目录与 .gitignore。"""
from __future__ import annotations
from pathlib import Path
from typing import Any
import tomllib
from tomli_w import dumps as toml_dumps
from aide.core import output
DEFAULT_CONFIG = """# Aide 默认配置(由 aide init 生成)
# runtime: aide 自身运行要求
# task: 任务文档路径
# env: 环境模块配置
# flow: 环节名称列表,供流程校验使用
[runtime]
python_min = "3.11"
use_uv = true
[task]
source = "task-now.md"
spec = "task-spec.md"
[env]
# 启用的模块列表
modules = ["python", "uv", "venv", "requirements"]
# Python 版本要求(可选,默认使用 runtime.python_min
# [env.python]
# min_version = "3.11"
# 虚拟环境配置类型B模块必须配置
[env.venv]
path = ".venv"
# 依赖文件配置类型B模块必须配置
[env.requirements]
path = "requirements.txt"
[flow]
phases = ["task-optimize", "flow-design", "impl", "verify", "docs", "finish"]
[decide]
# HTTP 服务起始端口
port = 3721
# 超时时间0 表示不超时
timeout = 0
"""
class ConfigManager:
def __init__(self, root: Path):
self.root = root
self.aide_dir = self.root / ".aide"
self.config_path = self.aide_dir / "config.toml"
self.decisions_dir = self.aide_dir / "decisions"
self.logs_dir = self.aide_dir / "logs"
def ensure_base_dirs(self) -> None:
self.aide_dir.mkdir(parents=True, exist_ok=True)
self.decisions_dir.mkdir(parents=True, exist_ok=True)
self.logs_dir.mkdir(parents=True, exist_ok=True)
def ensure_gitignore(self) -> None:
gitignore_path = self.root / ".gitignore"
marker = ".aide/"
if gitignore_path.exists():
content = gitignore_path.read_text(encoding="utf-8").splitlines()
if any(line.strip() == marker for line in content):
return
content.append(marker)
gitignore_path.write_text("\n".join(content) + "\n", encoding="utf-8")
else:
gitignore_path.write_text(f"{marker}\n", encoding="utf-8")
def ensure_config(self) -> dict[str, Any]:
self.ensure_base_dirs()
if not self.config_path.exists():
self.config_path.write_text(DEFAULT_CONFIG, encoding="utf-8")
output.ok("已创建默认配置 .aide/config.toml")
return self.load_config()
def load_config(self) -> dict[str, Any]:
if not self.config_path.exists():
return {}
try:
with self.config_path.open("rb") as f:
return tomllib.load(f)
except Exception as exc: # pragma: no cover - 兼容性输出
output.err(f"读取配置失败: {exc}")
return {}
def get_value(self, key: str) -> Any:
data = self.load_config()
return self._walk_get(data, key)
def set_value(self, key: str, value: Any) -> None:
data = self.ensure_config()
self._walk_set(data, key, value)
self._write_config(data)
output.ok(f"已更新 {key} = {value!r}")
def _write_config(self, data: dict[str, Any]) -> None:
self.config_path.write_text(toml_dumps(data), encoding="utf-8")
@staticmethod
def _walk_get(data: dict[str, Any], dotted_key: str) -> Any:
current: Any = data
for part in dotted_key.split("."):
if not isinstance(current, dict):
return None
if part not in current:
return None
current = current[part]
return current
@staticmethod
def _walk_set(data: dict[str, Any], dotted_key: str, value: Any) -> None:
parts = dotted_key.split(".")
current = data
for part in parts[:-1]:
if part not in current or not isinstance(current[part], dict):
current[part] = {}
current = current[part]
current[parts[-1]] = value