📃 docs: 部分完成
This commit is contained in:
545
aide-program/docs/02-aide-init设计.md
Normal file
545
aide-program/docs/02-aide-init设计.md
Normal file
@@ -0,0 +1,545 @@
|
||||
# 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]
|
||||
# 可选工具配置
|
||||
|
||||
# 是否需要 uv(Python 包管理器)
|
||||
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
|
||||
Reference in New Issue
Block a user