Files
agent-aide/aide-program/docs/02-aide-init设计.md
2025-12-13 04:37:41 +08:00

546 lines
12 KiB
Markdown
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 init 设计
## 一、命令概述
### 1.1 功能定位
`aide init` 命令用于初始化项目的 aide 工作环境,创建必要的目录和配置文件。
### 1.2 执行时机
- 首次在项目中使用 aide 时
- 配置文件丢失需要重新创建时
- 作为 `/aide:init` 命令的一部分
### 1.3 命令格式
```bash
aide init [options]
```
**选项**
- 无(当前版本不需要选项)
---
## 二、功能需求
### 2.1 核心功能
1. **创建 .aide 目录**
- 检查目录是否存在
- 不存在则创建
- 已存在则跳过(幂等性)
2. **生成配置文件**
- 创建 `config.toml`
- 包含详细注释
- 使用合理的默认值
3. **更新 .gitignore**
- 检查 `.gitignore` 是否存在
- 添加 `.aide/` 到忽略列表
- 避免重复添加
### 2.2 输出要求
**首次初始化**
```
✓ 已创建 .aide/ 目录
✓ 已生成默认配置
✓ 已添加 .aide/ 到 .gitignore
```
**重复初始化**
```
⚠ .aide/ 目录已存在
⚠ 配置文件已存在,跳过生成
```
**部分已存在**
```
⚠ .aide/ 目录已存在
✓ 已生成默认配置
✓ 已添加 .aide/ 到 .gitignore
```
---
## 三、实现设计
### 3.1 函数接口
```python
def cmd_init(args: list[str]) -> int:
"""aide init 命令处理
Args:
args: 命令参数(当前版本为空)
Returns:
退出码0 表示成功)
"""
pass
```
### 3.2 实现流程
```python
from pathlib import Path
from core.output import ok, warn, err
from core.config import generate_default_config
def cmd_init(args: list[str]) -> int:
"""aide init 命令实现"""
# 1. 获取项目根目录(当前工作目录)
project_root = Path.cwd()
aide_dir = project_root / ".aide"
config_path = aide_dir / "config.toml"
gitignore_path = project_root / ".gitignore"
# 2. 创建 .aide 目录
if aide_dir.exists():
warn(".aide/ 目录已存在")
else:
aide_dir.mkdir(parents=True, exist_ok=True)
ok("已创建 .aide/ 目录")
# 3. 生成配置文件
if config_path.exists():
warn("配置文件已存在,跳过生成")
else:
config_content = generate_default_config()
config_path.write_text(config_content, encoding="utf-8")
ok("已生成默认配置")
# 4. 更新 .gitignore
gitignore_updated = update_gitignore(gitignore_path)
if gitignore_updated:
ok("已添加 .aide/ 到 .gitignore")
return 0
```
### 3.3 辅助函数
#### 3.3.1 更新 .gitignore
```python
def update_gitignore(gitignore_path: Path) -> bool:
"""更新 .gitignore 文件
Args:
gitignore_path: .gitignore 文件路径
Returns:
是否进行了更新
"""
aide_ignore = ".aide/"
# 读取现有内容
if gitignore_path.exists():
content = gitignore_path.read_text(encoding="utf-8")
lines = content.splitlines()
else:
lines = []
# 检查是否已存在
if aide_ignore in lines or ".aide" in lines:
return False
# 添加到末尾
if lines and not lines[-1].strip():
# 最后一行是空行,直接添加
lines.append(aide_ignore)
else:
# 添加空行和 .aide/
lines.extend(["", aide_ignore])
# 写回文件
gitignore_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
return True
```
#### 3.3.2 生成默认配置
```python
def generate_default_config() -> str:
"""生成默认配置文件内容
Returns:
TOML 格式的配置内容
"""
return '''# Aide 项目配置文件
# 由 aide init 自动生成
# 文档: https://github.com/your-org/aide
[task]
# 任务原文档路径prep 阶段使用)
# 可通过 /aide:prep [路径] 覆盖
source = "task-now.md"
# 任务细则文档路径exec 阶段使用)
# 可通过 /aide:exec [路径] 覆盖
spec = "task-spec.md"
[env]
# 环境配置
[env.python]
# Python 版本要求(语义化版本)
# 格式: ">=3.10" 或 ">=3.10,<4.0"
version = ">=3.10"
# 虚拟环境路径(相对于项目根目录)
venv = ".venv"
[env.tools]
# 可选工具配置
# 是否需要 uvPython 包管理器)
uv = false
# 是否需要 git
git = true
[flow]
# 流程追踪配置aide flow 命令使用)
# 环节列表(不建议修改)
phases = ["flow-design", "impl", "verify", "docs", "finish"]
# PlantUML 流程图目录
flowchart_dir = "program_flowchart"
[decide]
# 待定项确认配置aide decide 命令使用)
# Web 服务端口
port = 3721
# 决策记录保存目录
decisions_dir = ".aide/decisions"
[output]
# 输出配置
# 是否启用颜色输出
# 可通过环境变量 NO_COLOR 禁用
color = true
# 输出语言(当前仅支持 zh-CN
language = "zh-CN"
'''
```
---
## 四、错误处理
### 4.1 可能的错误
| 错误类型 | 处理方式 | 退出码 |
|---------|---------|--------|
| 无写入权限 | 显示错误信息和建议 | 1 |
| 磁盘空间不足 | 显示错误信息 | 1 |
| 配置文件格式错误 | 不应该发生(生成的内容固定) | - |
### 4.2 错误处理示例
```python
def cmd_init(args: list[str]) -> int:
"""aide init 命令实现(带错误处理)"""
try:
project_root = Path.cwd()
aide_dir = project_root / ".aide"
# 创建目录
try:
aide_dir.mkdir(parents=True, exist_ok=True)
except PermissionError:
err(
"无法创建 .aide/ 目录",
[
"原因: 权限不足",
f"位置: {aide_dir}",
"建议: 检查当前目录的写入权限"
]
)
return 1
except OSError as e:
err(
"无法创建 .aide/ 目录",
[
f"原因: {str(e)}",
f"位置: {aide_dir}"
]
)
return 1
# 生成配置文件
config_path = aide_dir / "config.toml"
if not config_path.exists():
try:
config_content = generate_default_config()
config_path.write_text(config_content, encoding="utf-8")
ok("已生成默认配置")
except OSError as e:
err(
"无法创建配置文件",
[
f"原因: {str(e)}",
f"位置: {config_path}"
]
)
return 1
else:
warn("配置文件已存在,跳过生成")
# 更新 .gitignore
gitignore_path = project_root / ".gitignore"
try:
if update_gitignore(gitignore_path):
ok("已添加 .aide/ 到 .gitignore")
except OSError as e:
# .gitignore 更新失败不是致命错误
warn(f"无法更新 .gitignore: {str(e)}")
return 0
except Exception as e:
err(
"初始化失败",
[
f"原因: {str(e)}",
"建议: 检查目录权限和磁盘空间"
]
)
return 1
```
---
## 五、幂等性设计
### 5.1 幂等性要求
多次执行 `aide init` 应该:
1. 不破坏已有的配置
2. 不重复添加 .gitignore 条目
3. 给出清晰的提示信息
### 5.2 幂等性测试
```python
def test_init_idempotent():
"""测试 aide init 的幂等性"""
# 第一次执行
result1 = cmd_init([])
assert result1 == 0
# 验证文件已创建
assert Path(".aide").exists()
assert Path(".aide/config.toml").exists()
# 第二次执行
result2 = cmd_init([])
assert result2 == 0
# 验证配置文件内容未改变
config1 = Path(".aide/config.toml").read_text()
# 第三次执行
result3 = cmd_init([])
assert result3 == 0
config2 = Path(".aide/config.toml").read_text()
assert config1 == config2
```
---
## 六、测试用例
### 6.1 正常场景
```python
def test_init_success(tmp_path, monkeypatch):
"""测试正常初始化"""
# 切换到临时目录
monkeypatch.chdir(tmp_path)
# 执行初始化
result = cmd_init([])
# 验证退出码
assert result == 0
# 验证目录创建
assert (tmp_path / ".aide").exists()
assert (tmp_path / ".aide").is_dir()
# 验证配置文件创建
config_path = tmp_path / ".aide" / "config.toml"
assert config_path.exists()
assert config_path.is_file()
# 验证配置文件内容
content = config_path.read_text()
assert "[task]" in content
assert "[env]" in content
assert "source = " in content
# 验证 .gitignore 更新
gitignore_path = tmp_path / ".gitignore"
assert gitignore_path.exists()
gitignore_content = gitignore_path.read_text()
assert ".aide/" in gitignore_content
```
### 6.2 重复初始化
```python
def test_init_already_exists(tmp_path, monkeypatch, capsys):
"""测试重复初始化"""
monkeypatch.chdir(tmp_path)
# 第一次初始化
cmd_init([])
# 第二次初始化
result = cmd_init([])
# 验证退出码
assert result == 0
# 验证输出包含警告
captured = capsys.readouterr()
assert "已存在" in captured.out
```
### 6.3 权限错误
```python
def test_init_permission_error(tmp_path, monkeypatch, capsys):
"""测试权限错误"""
monkeypatch.chdir(tmp_path)
# 创建只读目录
tmp_path.chmod(0o444)
# 执行初始化
result = cmd_init([])
# 恢复权限
tmp_path.chmod(0o755)
# 验证退出码
assert result == 1
# 验证错误信息
captured = capsys.readouterr()
assert "权限" in captured.out
```
### 6.4 .gitignore 已存在
```python
def test_init_gitignore_exists(tmp_path, monkeypatch):
"""测试 .gitignore 已存在的情况"""
monkeypatch.chdir(tmp_path)
# 创建已有的 .gitignore
gitignore_path = tmp_path / ".gitignore"
gitignore_path.write_text("*.pyc\n__pycache__/\n")
# 执行初始化
result = cmd_init([])
# 验证退出码
assert result == 0
# 验证 .gitignore 内容
content = gitignore_path.read_text()
assert "*.pyc" in content
assert ".aide/" in content
# 验证不重复添加
lines = content.splitlines()
aide_count = sum(1 for line in lines if ".aide" in line)
assert aide_count == 1
```
---
## 七、集成测试
### 7.1 完整工作流测试
```python
def test_init_workflow(tmp_path, monkeypatch):
"""测试完整的初始化工作流"""
monkeypatch.chdir(tmp_path)
# 1. 执行初始化
result = cmd_init([])
assert result == 0
# 2. 验证可以读取配置
from core.config import Config
config = Config(tmp_path)
config.load()
# 3. 验证配置值
assert config.get("task.source") == "task-now.md"
assert config.get("task.spec") == "task-spec.md"
assert config.get("env.python.version") == ">=3.10"
# 4. 验证可以修改配置
config.set("task.source", "new-task.md")
assert config.get("task.source") == "new-task.md"
```
---
## 八、性能要求
### 8.1 执行时间
- 正常情况:< 100ms
- 包含 .gitignore 更新< 200ms
### 8.2 资源占用
- 内存< 10MB
- 磁盘空间< 10KB配置文件
---
## 九、总结
### 9.1 核心要点
1. 创建 .aide 目录和配置文件
2. 自动更新 .gitignore
3. 幂等性设计可重复执行
4. 完善的错误处理
### 9.2 实现检查清单
- [ ] 实现 cmd_init 函数
- [ ] 实现 generate_default_config 函数
- [ ] 实现 update_gitignore 函数
- [ ] 实现错误处理
- [ ] 编写单元测试
- [ ] 编写集成测试
- [ ] 验证幂等性
- [ ] 性能测试
---
**版本**v1.0
**更新日期**2025-12-13