🦄 refactor: 删去部分,重新实现aide
This commit is contained in:
@@ -1,346 +0,0 @@
|
||||
# Aide 程序系统开发文档
|
||||
|
||||
## 一、项目概述
|
||||
|
||||
### 1.1 项目定位
|
||||
|
||||
Aide 是一套命令行工具集,用于支持 AI 辅助开发工作流。本项目实现 aide 程序的核心基础功能,包括:
|
||||
|
||||
- 项目初始化(aide init)
|
||||
- 环境管理(aide env)
|
||||
- 配置管理(aide config)
|
||||
|
||||
**注意**:本阶段不包含 `aide flow`(进度追踪)和 `aide decide`(待定项确认)的实现,这两个功能将在后续阶段开发。
|
||||
|
||||
### 1.2 技术栈要求
|
||||
|
||||
- **Python 3.10+**:主要编程语言
|
||||
- **Shell 脚本**:跨平台入口封装(aide.sh / aide.bat)
|
||||
- **TOML**:配置文件格式
|
||||
- **标准库优先**:尽量使用 Python 标准库,减少外部依赖
|
||||
|
||||
### 1.3 设计原则
|
||||
|
||||
1. **确定性**:相同输入产生相同输出,避免不确定性
|
||||
2. **精简输出**:成功时输出极简,失败时输出详细
|
||||
3. **幂等性**:重复执行不产生副作用
|
||||
4. **自文档化**:配置文件包含详细注释
|
||||
5. **跨平台**:支持 Linux、macOS、Windows
|
||||
|
||||
---
|
||||
|
||||
## 二、目录结构
|
||||
|
||||
```
|
||||
aide-program/
|
||||
├── README.md # 本文件:项目总览
|
||||
├── docs/ # 详细设计文档
|
||||
│ ├── 01-入口脚本设计.md # aide.sh / aide.bat 设计
|
||||
│ ├── 02-aide-init设计.md # aide init 命令设计
|
||||
│ ├── 03-aide-env设计.md # aide env 命令设计
|
||||
│ ├── 04-aide-config设计.md # aide config 命令设计
|
||||
│ ├── 05-配置文件规范.md # config.toml 格式规范
|
||||
│ ├── 06-输出格式规范.md # 统一输出格式规范
|
||||
│ └── 07-测试规范.md # 测试要求和用例
|
||||
├── src/ # 源代码目录(开发时创建)
|
||||
│ ├── aide.sh # Linux/macOS 入口
|
||||
│ ├── aide.bat # Windows 入口
|
||||
│ ├── main.py # Python 主入口
|
||||
│ ├── core/ # 核心模块
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── config.py # 配置读写
|
||||
│ │ └── output.py # 输出格式化
|
||||
│ ├── commands/ # 命令实现
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── init.py # aide init
|
||||
│ │ ├── env.py # aide env
|
||||
│ │ └── config_cmd.py # aide config
|
||||
│ └── utils/ # 工具函数
|
||||
│ ├── __init__.py
|
||||
│ └── validators.py # 验证函数
|
||||
└── tests/ # 测试目录(开发时创建)
|
||||
├── test_init.py
|
||||
├── test_env.py
|
||||
└── test_config.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、命令清单
|
||||
|
||||
### 3.1 本阶段实现的命令
|
||||
|
||||
| 命令 | 功能 | 优先级 |
|
||||
|------|------|--------|
|
||||
| `aide init` | 初始化 .aide 目录和配置文件 | P0 |
|
||||
| `aide env ensure` | 检测并修复项目开发环境 | P0 |
|
||||
| `aide env ensure --runtime` | 检测 aide 运行时环境 | P0 |
|
||||
| `aide config get <key>` | 获取配置值 | P1 |
|
||||
| `aide config set <key> <value>` | 设置配置值 | P1 |
|
||||
|
||||
### 3.2 后续阶段实现的命令
|
||||
|
||||
| 命令 | 功能 | 说明 |
|
||||
|------|------|------|
|
||||
| `aide flow ...` | 进度追踪和 git 集成 | 后续实现 |
|
||||
| `aide decide ...` | 待定项确认 Web 服务 | 后续实现 |
|
||||
|
||||
---
|
||||
|
||||
## 四、开发流程
|
||||
|
||||
### 4.1 阅读顺序
|
||||
|
||||
建议按以下顺序阅读文档:
|
||||
|
||||
1. **README.md**(本文件)- 了解项目全貌
|
||||
2. **docs/06-输出格式规范.md** - 理解统一输出格式
|
||||
3. **docs/05-配置文件规范.md** - 理解配置文件结构
|
||||
4. **docs/01-入口脚本设计.md** - 理解命令调用流程
|
||||
5. **docs/02-aide-init设计.md** - 实现 aide init
|
||||
6. **docs/03-aide-env设计.md** - 实现 aide env
|
||||
7. **docs/04-aide-config设计.md** - 实现 aide config
|
||||
8. **docs/07-测试规范.md** - 编写测试用例
|
||||
|
||||
### 4.2 开发步骤
|
||||
|
||||
1. **搭建基础框架**
|
||||
- 创建目录结构
|
||||
- 实现入口脚本(aide.sh / aide.bat)
|
||||
- 实现 main.py 命令分发
|
||||
- 实现 core/output.py 输出格式化
|
||||
|
||||
2. **实现核心模块**
|
||||
- 实现 core/config.py 配置读写
|
||||
- 实现 utils/validators.py 验证函数
|
||||
|
||||
3. **实现命令**
|
||||
- 实现 aide init
|
||||
- 实现 aide env ensure --runtime
|
||||
- 实现 aide env ensure
|
||||
- 实现 aide config get/set
|
||||
|
||||
4. **编写测试**
|
||||
- 单元测试
|
||||
- 集成测试
|
||||
- 跨平台测试
|
||||
|
||||
5. **文档和打包**
|
||||
- 编写用户文档
|
||||
- 准备分发包
|
||||
|
||||
### 4.3 质量要求
|
||||
|
||||
1. **代码质量**
|
||||
- 遵循 PEP 8 代码规范
|
||||
- 函数和类必须有文档字符串
|
||||
- 关键逻辑必须有注释
|
||||
|
||||
2. **测试覆盖**
|
||||
- 核心功能测试覆盖率 ≥ 80%
|
||||
- 所有错误路径必须有测试用例
|
||||
- 跨平台兼容性测试
|
||||
|
||||
3. **用户体验**
|
||||
- 输出信息清晰易懂
|
||||
- 错误提示包含解决建议
|
||||
- 命令执行速度快(< 1秒)
|
||||
|
||||
---
|
||||
|
||||
## 五、核心概念
|
||||
|
||||
### 5.1 输出格式
|
||||
|
||||
所有 aide 命令遵循统一的输出格式:
|
||||
|
||||
| 前缀 | 含义 | 使用场景 |
|
||||
|------|------|---------|
|
||||
| `✓` | 成功 | 操作成功完成 |
|
||||
| `⚠` | 警告 | 有问题但可继续 |
|
||||
| `✗` | 错误 | 操作失败 |
|
||||
| `→` | 信息 | 进行中或提示信息 |
|
||||
|
||||
**静默原则**:无输出 = 正常完成(适用于幂等操作)
|
||||
|
||||
### 5.2 配置文件
|
||||
|
||||
- **位置**:`.aide/config.toml`
|
||||
- **格式**:TOML
|
||||
- **特点**:自文档化,包含详细注释
|
||||
- **访问**:通过 `aide config` 命令或 core/config.py 模块
|
||||
|
||||
### 5.3 数据存储
|
||||
|
||||
所有 aide 数据统一存放在项目根目录的 `.aide/` 下:
|
||||
|
||||
```
|
||||
.aide/
|
||||
├── config.toml # 项目配置
|
||||
├── flow-status.json # 任务进度(后续实现)
|
||||
├── decisions/ # 待定项记录(后续实现)
|
||||
└── logs/ # 操作日志(可选)
|
||||
```
|
||||
|
||||
### 5.4 错误处理
|
||||
|
||||
1. **预期错误**:返回明确的错误信息和建议
|
||||
2. **非预期错误**:记录详细日志,返回简化错误信息
|
||||
3. **退出码**:
|
||||
- 0:成功
|
||||
- 1:一般错误
|
||||
- 2:参数错误
|
||||
- 3:环境错误
|
||||
|
||||
---
|
||||
|
||||
## 六、依赖管理
|
||||
|
||||
### 6.1 Python 依赖
|
||||
|
||||
**核心依赖**(必需):
|
||||
- Python 3.10+
|
||||
- 标准库:os, sys, pathlib, subprocess, json, configparser
|
||||
|
||||
**可选依赖**:
|
||||
- tomli / tomllib(Python 3.11+ 内置):TOML 解析
|
||||
- tomli-w:TOML 写入
|
||||
|
||||
### 6.2 系统依赖
|
||||
|
||||
**必需**:
|
||||
- Python 3.10+
|
||||
|
||||
**可选**(用于项目环境检测):
|
||||
- git
|
||||
- uv / pip
|
||||
|
||||
---
|
||||
|
||||
## 七、交付物
|
||||
|
||||
### 7.1 必需交付物
|
||||
|
||||
1. **源代码**
|
||||
- 完整的 Python 代码
|
||||
- 入口脚本(aide.sh / aide.bat)
|
||||
- 所有必需的模块和工具函数
|
||||
|
||||
2. **测试代码**
|
||||
- 单元测试
|
||||
- 集成测试
|
||||
- 测试数据和 fixtures
|
||||
|
||||
3. **文档**
|
||||
- 用户使用文档
|
||||
- 开发者文档(如有特殊设计)
|
||||
- CHANGELOG
|
||||
|
||||
### 7.2 可选交付物
|
||||
|
||||
1. **打包脚本**
|
||||
- 用于生成分发包的脚本
|
||||
- 安装说明
|
||||
|
||||
2. **CI/CD 配置**
|
||||
- GitHub Actions 或其他 CI 配置
|
||||
- 自动化测试流程
|
||||
|
||||
---
|
||||
|
||||
## 八、注意事项
|
||||
|
||||
### 8.1 设计约束
|
||||
|
||||
1. **不要实现 aide flow**:进度追踪功能后续实现
|
||||
2. **不要实现 aide decide**:待定项确认功能后续实现
|
||||
3. **不要硬编码路径**:所有路径通过配置或参数传入
|
||||
4. **不要假设环境**:所有环境依赖必须检测和验证
|
||||
|
||||
### 8.2 兼容性要求
|
||||
|
||||
1. **Python 版本**:支持 3.10+
|
||||
2. **操作系统**:Linux、macOS、Windows
|
||||
3. **路径分隔符**:使用 pathlib 处理跨平台路径
|
||||
4. **编码**:统一使用 UTF-8
|
||||
|
||||
### 8.3 安全考虑
|
||||
|
||||
1. **配置文件权限**:不存储敏感信息
|
||||
2. **命令注入**:所有外部命令调用必须参数化
|
||||
3. **路径遍历**:验证所有文件路径
|
||||
4. **输入验证**:验证所有用户输入
|
||||
|
||||
---
|
||||
|
||||
## 九、参考资料
|
||||
|
||||
### 9.1 项目文档
|
||||
|
||||
- `../aide-requirements.md`:Aide 系统需求规格
|
||||
- `../aide-marketplace/aide-plugin/`:Commands 和 Skills 定义
|
||||
- `../discuss/`:设计讨论和决策记录
|
||||
|
||||
### 9.2 外部资源
|
||||
|
||||
- [TOML 规范](https://toml.io/)
|
||||
- [PEP 8 代码规范](https://pep8.org/)
|
||||
- [Python pathlib 文档](https://docs.python.org/3/library/pathlib.html)
|
||||
|
||||
---
|
||||
|
||||
## 十、联系方式
|
||||
|
||||
如有疑问或需要澄清,请:
|
||||
|
||||
1. 查阅 `docs/` 目录下的详细设计文档
|
||||
2. 参考 `../aide-requirements.md` 了解整体设计
|
||||
3. 查看 `../discuss/` 目录了解设计决策
|
||||
|
||||
---
|
||||
|
||||
## 附录:快速开始
|
||||
|
||||
### A.1 验证环境
|
||||
|
||||
```bash
|
||||
# 检查 Python 版本
|
||||
python3 --version # 应该 >= 3.10
|
||||
|
||||
# 检查必要的库
|
||||
python3 -c "import tomllib" # Python 3.11+
|
||||
# 或
|
||||
python3 -c "import tomli" # Python 3.10
|
||||
```
|
||||
|
||||
### A.2 创建开发环境
|
||||
|
||||
```bash
|
||||
# 创建虚拟环境
|
||||
cd aide-program
|
||||
python3 -m venv .venv
|
||||
|
||||
# 激活虚拟环境
|
||||
source .venv/bin/activate # Linux/macOS
|
||||
# 或
|
||||
.venv\Scripts\activate # Windows
|
||||
|
||||
# 安装依赖(如需要)
|
||||
pip install tomli tomli-w
|
||||
```
|
||||
|
||||
### A.3 运行测试
|
||||
|
||||
```bash
|
||||
# 运行所有测试
|
||||
python3 -m pytest tests/
|
||||
|
||||
# 运行特定测试
|
||||
python3 -m pytest tests/test_init.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**版本**:v1.0
|
||||
**更新日期**:2025-12-13
|
||||
**状态**:待开发
|
||||
@@ -1,554 +0,0 @@
|
||||
# 入口脚本设计
|
||||
|
||||
## 一、概述
|
||||
|
||||
### 1.1 设计目标
|
||||
|
||||
提供跨平台的统一命令行入口,使用户可以通过 `aide <command>` 的方式调用所有功能。
|
||||
|
||||
### 1.2 入口脚本清单
|
||||
|
||||
| 脚本 | 平台 | 说明 |
|
||||
|------|------|------|
|
||||
| `aide.sh` | Linux/macOS | Shell 脚本入口 |
|
||||
| `aide.bat` | Windows | 批处理脚本入口 |
|
||||
| `main.py` | 所有平台 | Python 主程序 |
|
||||
|
||||
---
|
||||
|
||||
## 二、aide.sh 设计(Linux/macOS)
|
||||
|
||||
### 2.1 功能要求
|
||||
|
||||
1. **定位 Python**:查找可用的 Python 3.10+ 解释器
|
||||
2. **定位脚本目录**:确定 main.py 的位置
|
||||
3. **传递参数**:将所有命令行参数传递给 Python 程序
|
||||
4. **处理错误**:Python 未找到时给出明确提示
|
||||
|
||||
### 2.2 实现示例
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
# Aide 命令行工具入口脚本(Linux/macOS)
|
||||
# 版本: 1.0
|
||||
|
||||
set -e # 遇到错误立即退出
|
||||
|
||||
# 获取脚本所在目录
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
# Python 主程序路径
|
||||
MAIN_PY="${SCRIPT_DIR}/main.py"
|
||||
|
||||
# 查找 Python 解释器
|
||||
find_python() {
|
||||
# 尝试查找 Python 3.10+
|
||||
for cmd in python3.12 python3.11 python3.10 python3 python; do
|
||||
if command -v "$cmd" &> /dev/null; then
|
||||
# 检查版本
|
||||
version=$("$cmd" -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')
|
||||
major=$(echo "$version" | cut -d. -f1)
|
||||
minor=$(echo "$version" | cut -d. -f2)
|
||||
|
||||
if [ "$major" -eq 3 ] && [ "$minor" -ge 10 ]; then
|
||||
echo "$cmd"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# 查找 Python
|
||||
PYTHON=$(find_python)
|
||||
|
||||
if [ -z "$PYTHON" ]; then
|
||||
echo "✗ Python 3.10+ 未找到" >&2
|
||||
echo " 建议: 安装 Python 3.10 或更高版本" >&2
|
||||
echo " 文档: https://www.python.org/downloads/" >&2
|
||||
exit 3
|
||||
fi
|
||||
|
||||
# 检查 main.py 是否存在
|
||||
if [ ! -f "$MAIN_PY" ]; then
|
||||
echo "✗ 找不到 main.py" >&2
|
||||
echo " 位置: $MAIN_PY" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 执行 Python 程序
|
||||
exec "$PYTHON" "$MAIN_PY" "$@"
|
||||
```
|
||||
|
||||
### 2.3 安装方式
|
||||
|
||||
```bash
|
||||
# 方式1:添加到 PATH
|
||||
export PATH="/path/to/aide:$PATH"
|
||||
|
||||
# 方式2:创建符号链接
|
||||
ln -s /path/to/aide/aide.sh /usr/local/bin/aide
|
||||
|
||||
# 方式3:添加别名
|
||||
alias aide='/path/to/aide/aide.sh'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、aide.bat 设计(Windows)
|
||||
|
||||
### 3.1 功能要求
|
||||
|
||||
1. **定位 Python**:查找可用的 Python 3.10+ 解释器
|
||||
2. **定位脚本目录**:确定 main.py 的位置
|
||||
3. **传递参数**:将所有命令行参数传递给 Python 程序
|
||||
4. **处理错误**:Python 未找到时给出明确提示
|
||||
|
||||
### 3.2 实现示例
|
||||
|
||||
```batch
|
||||
@echo off
|
||||
REM Aide 命令行工具入口脚本(Windows)
|
||||
REM 版本: 1.0
|
||||
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
REM 获取脚本所在目录
|
||||
set "SCRIPT_DIR=%~dp0"
|
||||
|
||||
REM Python 主程序路径
|
||||
set "MAIN_PY=%SCRIPT_DIR%main.py"
|
||||
|
||||
REM 查找 Python 解释器
|
||||
set "PYTHON="
|
||||
for %%p in (python3.12 python3.11 python3.10 python3 python py) do (
|
||||
where %%p >nul 2>&1
|
||||
if !errorlevel! equ 0 (
|
||||
REM 检查版本
|
||||
for /f "delims=" %%v in ('%%p -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')"') do (
|
||||
set "version=%%v"
|
||||
)
|
||||
|
||||
REM 解析版本号
|
||||
for /f "tokens=1,2 delims=." %%a in ("!version!") do (
|
||||
set "major=%%a"
|
||||
set "minor=%%b"
|
||||
)
|
||||
|
||||
REM 检查是否满足要求(3.10+)
|
||||
if !major! equ 3 if !minor! geq 10 (
|
||||
set "PYTHON=%%p"
|
||||
goto :found
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
:found
|
||||
if "%PYTHON%"=="" (
|
||||
echo ✗ Python 3.10+ 未找到 >&2
|
||||
echo 建议: 安装 Python 3.10 或更高版本 >&2
|
||||
echo 文档: https://www.python.org/downloads/ >&2
|
||||
exit /b 3
|
||||
)
|
||||
|
||||
REM 检查 main.py 是否存在
|
||||
if not exist "%MAIN_PY%" (
|
||||
echo ✗ 找不到 main.py >&2
|
||||
echo 位置: %MAIN_PY% >&2
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
REM 执行 Python 程序
|
||||
"%PYTHON%" "%MAIN_PY%" %*
|
||||
exit /b %errorlevel%
|
||||
```
|
||||
|
||||
### 3.3 安装方式
|
||||
|
||||
```batch
|
||||
REM 方式1:添加到 PATH
|
||||
set PATH=C:\path\to\aide;%PATH%
|
||||
|
||||
REM 方式2:创建批处理文件到系统目录
|
||||
copy aide.bat C:\Windows\System32\aide.bat
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、main.py 设计
|
||||
|
||||
### 4.1 功能要求
|
||||
|
||||
1. **命令分发**:根据第一个参数分发到对应的命令处理函数
|
||||
2. **参数解析**:解析命令行参数
|
||||
3. **错误处理**:统一的错误处理和退出码
|
||||
4. **帮助信息**:提供 `--help` 和 `--version` 支持
|
||||
|
||||
### 4.2 实现框架
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Aide 命令行工具主程序
|
||||
版本: 1.0
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# 添加 src 目录到 Python 路径
|
||||
SCRIPT_DIR = Path(__file__).parent
|
||||
sys.path.insert(0, str(SCRIPT_DIR))
|
||||
|
||||
from core.output import ok, warn, err, info
|
||||
from commands.init import cmd_init
|
||||
from commands.env import cmd_env
|
||||
from commands.config_cmd import cmd_config
|
||||
|
||||
|
||||
VERSION = "1.0.0"
|
||||
|
||||
|
||||
def show_help():
|
||||
"""显示帮助信息"""
|
||||
help_text = """Aide - AI 辅助开发工作流工具
|
||||
|
||||
用法:
|
||||
aide <command> [options]
|
||||
|
||||
命令:
|
||||
init 初始化 .aide 目录和配置文件
|
||||
env ensure 检测并修复项目开发环境
|
||||
env ensure --runtime 检测 aide 运行时环境
|
||||
config get <key> 获取配置值
|
||||
config set <key> <value> 设置配置值
|
||||
|
||||
选项:
|
||||
-h, --help 显示帮助信息
|
||||
-v, --version 显示版本信息
|
||||
|
||||
示例:
|
||||
aide init
|
||||
aide env ensure
|
||||
aide config get task.source
|
||||
aide config set task.source "new-task.md"
|
||||
|
||||
文档: https://github.com/your-org/aide
|
||||
"""
|
||||
print(help_text)
|
||||
|
||||
|
||||
def show_version():
|
||||
"""显示版本信息"""
|
||||
print(f"Aide v{VERSION}")
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
# 解析参数
|
||||
args = sys.argv[1:]
|
||||
|
||||
# 无参数或帮助
|
||||
if not args or args[0] in ["-h", "--help", "help"]:
|
||||
show_help()
|
||||
return 0
|
||||
|
||||
# 版本信息
|
||||
if args[0] in ["-v", "--version", "version"]:
|
||||
show_version()
|
||||
return 0
|
||||
|
||||
# 获取命令
|
||||
command = args[0]
|
||||
|
||||
try:
|
||||
# 命令分发
|
||||
if command == "init":
|
||||
return cmd_init(args[1:])
|
||||
elif command == "env":
|
||||
return cmd_env(args[1:])
|
||||
elif command == "config":
|
||||
return cmd_config(args[1:])
|
||||
else:
|
||||
err(
|
||||
f"未知命令: {command}",
|
||||
[
|
||||
"可用命令: init, env, config",
|
||||
"使用 'aide --help' 查看帮助"
|
||||
]
|
||||
)
|
||||
return 2
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\n")
|
||||
info("操作已取消")
|
||||
return 130 # 128 + SIGINT(2)
|
||||
|
||||
except Exception as e:
|
||||
err(
|
||||
"命令执行失败",
|
||||
[
|
||||
f"原因: {str(e)}",
|
||||
"使用 'aide --help' 查看帮助"
|
||||
]
|
||||
)
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
```
|
||||
|
||||
### 4.3 命令处理函数接口
|
||||
|
||||
每个命令处理函数应该遵循以下接口:
|
||||
|
||||
```python
|
||||
def cmd_<command>(args: list[str]) -> int:
|
||||
"""命令处理函数
|
||||
|
||||
Args:
|
||||
args: 命令参数列表(不包含命令本身)
|
||||
|
||||
Returns:
|
||||
退出码(0 表示成功)
|
||||
"""
|
||||
pass
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、参数解析
|
||||
|
||||
### 5.1 简单参数解析
|
||||
|
||||
对于简单的命令,可以手动解析参数:
|
||||
|
||||
```python
|
||||
def cmd_env(args: list[str]) -> int:
|
||||
"""env 命令处理"""
|
||||
if not args:
|
||||
err("缺少子命令", ["使用 'aide env ensure' 检测环境"])
|
||||
return 2
|
||||
|
||||
subcommand = args[0]
|
||||
|
||||
if subcommand == "ensure":
|
||||
# 检查 --runtime 参数
|
||||
runtime_only = "--runtime" in args
|
||||
return env_ensure(runtime_only=runtime_only)
|
||||
else:
|
||||
err(f"未知子命令: {subcommand}", ["可用子命令: ensure"])
|
||||
return 2
|
||||
```
|
||||
|
||||
### 5.2 使用 argparse(可选)
|
||||
|
||||
对于复杂的命令,可以使用 argparse:
|
||||
|
||||
```python
|
||||
import argparse
|
||||
|
||||
def cmd_config(args: list[str]) -> int:
|
||||
"""config 命令处理"""
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="aide config",
|
||||
description="配置管理"
|
||||
)
|
||||
|
||||
subparsers = parser.add_subparsers(dest="subcommand")
|
||||
|
||||
# get 子命令
|
||||
parser_get = subparsers.add_parser("get", help="获取配置值")
|
||||
parser_get.add_argument("key", help="配置键")
|
||||
|
||||
# set 子命令
|
||||
parser_set = subparsers.add_parser("set", help="设置配置值")
|
||||
parser_set.add_argument("key", help="配置键")
|
||||
parser_set.add_argument("value", help="配置值")
|
||||
|
||||
try:
|
||||
parsed = parser.parse_args(args)
|
||||
except SystemExit:
|
||||
return 2
|
||||
|
||||
if parsed.subcommand == "get":
|
||||
return config_get(parsed.key)
|
||||
elif parsed.subcommand == "set":
|
||||
return config_set(parsed.key, parsed.value)
|
||||
else:
|
||||
parser.print_help()
|
||||
return 2
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、错误处理
|
||||
|
||||
### 6.1 异常捕获
|
||||
|
||||
```python
|
||||
def main():
|
||||
"""主函数"""
|
||||
try:
|
||||
# 命令执行
|
||||
return execute_command()
|
||||
|
||||
except KeyboardInterrupt:
|
||||
# Ctrl+C 中断
|
||||
print("\n")
|
||||
info("操作已取消")
|
||||
return 130
|
||||
|
||||
except FileNotFoundError as e:
|
||||
# 文件不存在
|
||||
err("文件不存在", [f"路径: {e.filename}"])
|
||||
return 5
|
||||
|
||||
except PermissionError as e:
|
||||
# 权限错误
|
||||
err("权限不足", [f"路径: {e.filename}"])
|
||||
return 1
|
||||
|
||||
except Exception as e:
|
||||
# 其他错误
|
||||
err("命令执行失败", [f"原因: {str(e)}"])
|
||||
return 1
|
||||
```
|
||||
|
||||
### 6.2 退出码规范
|
||||
|
||||
| 退出码 | 含义 |
|
||||
|-------|------|
|
||||
| 0 | 成功 |
|
||||
| 1 | 一般错误 |
|
||||
| 2 | 参数错误 |
|
||||
| 3 | 环境错误 |
|
||||
| 4 | 配置错误 |
|
||||
| 5 | 文件错误 |
|
||||
| 130 | 用户中断(Ctrl+C) |
|
||||
|
||||
---
|
||||
|
||||
## 七、测试
|
||||
|
||||
### 7.1 入口脚本测试
|
||||
|
||||
```bash
|
||||
# 测试 aide.sh
|
||||
./aide.sh --version
|
||||
./aide.sh --help
|
||||
./aide.sh init
|
||||
./aide.sh env ensure
|
||||
|
||||
# 测试 aide.bat(Windows)
|
||||
aide.bat --version
|
||||
aide.bat --help
|
||||
aide.bat init
|
||||
aide.bat env ensure
|
||||
```
|
||||
|
||||
### 7.2 main.py 测试
|
||||
|
||||
```python
|
||||
def test_main_help(capsys):
|
||||
"""测试帮助信息"""
|
||||
sys.argv = ["aide", "--help"]
|
||||
result = main()
|
||||
|
||||
assert result == 0
|
||||
captured = capsys.readouterr()
|
||||
assert "用法:" in captured.out
|
||||
|
||||
def test_main_version(capsys):
|
||||
"""测试版本信息"""
|
||||
sys.argv = ["aide", "--version"]
|
||||
result = main()
|
||||
|
||||
assert result == 0
|
||||
captured = capsys.readouterr()
|
||||
assert "Aide v" in captured.out
|
||||
|
||||
def test_main_unknown_command(capsys):
|
||||
"""测试未知命令"""
|
||||
sys.argv = ["aide", "unknown"]
|
||||
result = main()
|
||||
|
||||
assert result == 2
|
||||
captured = capsys.readouterr()
|
||||
assert "未知命令" in captured.out
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 八、安装和分发
|
||||
|
||||
### 8.1 目录结构
|
||||
|
||||
```
|
||||
aide/
|
||||
├── aide.sh # Linux/macOS 入口
|
||||
├── aide.bat # Windows 入口
|
||||
├── main.py # Python 主程序
|
||||
├── core/ # 核心模块
|
||||
├── commands/ # 命令实现
|
||||
└── utils/ # 工具函数
|
||||
```
|
||||
|
||||
### 8.2 安装脚本(可选)
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
# install.sh - Aide 安装脚本
|
||||
|
||||
set -e
|
||||
|
||||
INSTALL_DIR="${HOME}/.local/bin"
|
||||
|
||||
# 创建安装目录
|
||||
mkdir -p "$INSTALL_DIR"
|
||||
|
||||
# 复制文件
|
||||
cp -r aide "$INSTALL_DIR/"
|
||||
|
||||
# 创建符号链接
|
||||
ln -sf "$INSTALL_DIR/aide/aide.sh" "$INSTALL_DIR/aide"
|
||||
|
||||
# 添加到 PATH
|
||||
if ! echo "$PATH" | grep -q "$INSTALL_DIR"; then
|
||||
echo "export PATH=\"$INSTALL_DIR:\$PATH\"" >> ~/.bashrc
|
||||
echo "✓ 已添加到 PATH"
|
||||
echo " 请运行: source ~/.bashrc"
|
||||
fi
|
||||
|
||||
echo "✓ Aide 安装完成"
|
||||
echo " 使用 'aide --help' 查看帮助"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 九、总结
|
||||
|
||||
### 9.1 核心要点
|
||||
|
||||
1. 提供跨平台的统一入口
|
||||
2. 自动查找合适的 Python 解释器
|
||||
3. 统一的命令分发和错误处理
|
||||
4. 清晰的帮助和版本信息
|
||||
|
||||
### 9.2 实现检查清单
|
||||
|
||||
- [ ] 实现 aide.sh(Linux/macOS)
|
||||
- [ ] 实现 aide.bat(Windows)
|
||||
- [ ] 实现 main.py 命令分发
|
||||
- [ ] 实现帮助和版本信息
|
||||
- [ ] 实现错误处理和退出码
|
||||
- [ ] 编写入口脚本测试
|
||||
- [ ] 验证跨平台兼容性
|
||||
- [ ] 编写安装脚本(可选)
|
||||
|
||||
---
|
||||
|
||||
**版本**:v1.0
|
||||
**更新日期**:2025-12-13
|
||||
@@ -1,545 +0,0 @@
|
||||
# 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
|
||||
@@ -1,599 +0,0 @@
|
||||
# aide env 设计
|
||||
|
||||
## 一、命令概述
|
||||
|
||||
### 1.1 功能定位
|
||||
|
||||
`aide env` 命令用于检测和修复项目开发环境,确保所有必需的工具和依赖都已正确安装。
|
||||
|
||||
### 1.2 执行时机
|
||||
|
||||
- `/aide:init` 命令中调用(两次)
|
||||
- 用户手动检查环境时
|
||||
- 环境配置变更后
|
||||
|
||||
### 1.3 命令格式
|
||||
|
||||
```bash
|
||||
aide env ensure [--runtime]
|
||||
```
|
||||
|
||||
**选项**:
|
||||
- `--runtime`:仅检查 aide 运行时环境(不依赖配置文件)
|
||||
|
||||
---
|
||||
|
||||
## 二、功能需求
|
||||
|
||||
### 2.1 aide env ensure --runtime
|
||||
|
||||
**用途**:检查 aide 程序自身运行所需的环境
|
||||
|
||||
**检查项**:
|
||||
1. Python 版本(>= 3.10)
|
||||
2. 必需的 Python 库(tomli/tomllib, tomli-w)
|
||||
|
||||
**特点**:
|
||||
- 不读取项目配置文件
|
||||
- 在 `aide init` 之前执行
|
||||
- 失败时给出明确的安装建议
|
||||
|
||||
**输出示例**:
|
||||
|
||||
成功:
|
||||
```
|
||||
✓ 环境就绪 (python:3.12)
|
||||
```
|
||||
|
||||
失败:
|
||||
```
|
||||
✗ Python 版本不满足要求 (需要 >=3.10, 当前 3.8)
|
||||
建议: 安装 Python 3.10+ 或使用 pyenv 管理版本
|
||||
文档: https://www.python.org/downloads/
|
||||
```
|
||||
|
||||
### 2.2 aide env ensure
|
||||
|
||||
**用途**:检查项目开发环境
|
||||
|
||||
**检查项**:
|
||||
1. Python 版本(根据配置文件)
|
||||
2. 虚拟环境(根据配置文件)
|
||||
3. 可选工具(git, uv 等,根据配置文件)
|
||||
|
||||
**特点**:
|
||||
- 读取 `.aide/config.toml`
|
||||
- 在 `aide init` 之后执行
|
||||
- 可以自动修复部分问题
|
||||
|
||||
**输出示例**:
|
||||
|
||||
成功(无问题):
|
||||
```
|
||||
✓ 环境就绪 (python:3.12, git:2.40.0)
|
||||
```
|
||||
|
||||
成功(自动修复):
|
||||
```
|
||||
⚠ 已修复: 创建虚拟环境 .venv
|
||||
✓ 环境就绪 (python:3.12)
|
||||
```
|
||||
|
||||
失败(无法修复):
|
||||
```
|
||||
✗ Python 版本不满足要求 (需要 >=3.11, 当前 3.10)
|
||||
建议: 升级 Python 到 3.11+ 或修改配置文件
|
||||
配置: .aide/config.toml (env.python.version)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、实现设计
|
||||
|
||||
### 3.1 函数接口
|
||||
|
||||
```python
|
||||
def cmd_env(args: list[str]) -> int:
|
||||
"""aide env 命令处理
|
||||
|
||||
Args:
|
||||
args: 命令参数
|
||||
|
||||
Returns:
|
||||
退出码(0 表示成功)
|
||||
"""
|
||||
pass
|
||||
|
||||
def env_ensure(runtime_only: bool = False) -> int:
|
||||
"""环境检测和修复
|
||||
|
||||
Args:
|
||||
runtime_only: 是否仅检查运行时环境
|
||||
|
||||
Returns:
|
||||
退出码(0 表示成功)
|
||||
"""
|
||||
pass
|
||||
```
|
||||
|
||||
### 3.2 实现流程
|
||||
|
||||
#### 3.2.1 aide env ensure --runtime
|
||||
|
||||
```python
|
||||
import sys
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from core.output import ok, err
|
||||
|
||||
def env_ensure_runtime() -> int:
|
||||
"""检查 aide 运行时环境"""
|
||||
|
||||
# 1. 检查 Python 版本
|
||||
python_version = sys.version_info
|
||||
if python_version.major < 3 or (python_version.major == 3 and python_version.minor < 10):
|
||||
err(
|
||||
f"Python 版本不满足要求 (需要 >=3.10, 当前 {python_version.major}.{python_version.minor})",
|
||||
[
|
||||
"建议: 安装 Python 3.10+ 或使用 pyenv 管理版本",
|
||||
"文档: https://www.python.org/downloads/"
|
||||
]
|
||||
)
|
||||
return 3
|
||||
|
||||
# 2. 检查必需的库
|
||||
try:
|
||||
# Python 3.11+ 内置 tomllib
|
||||
if python_version.minor >= 11:
|
||||
import tomllib
|
||||
else:
|
||||
import tomli as tomllib
|
||||
|
||||
import tomli_w
|
||||
except ImportError as e:
|
||||
err(
|
||||
f"缺少必需的 Python 库: {e.name}",
|
||||
[
|
||||
"建议: 运行 'pip install tomli tomli-w'",
|
||||
"或使用 uv: 'uv pip install tomli tomli-w'"
|
||||
]
|
||||
)
|
||||
return 3
|
||||
|
||||
# 3. 输出成功信息
|
||||
version_str = f"{python_version.major}.{python_version.minor}.{python_version.micro}"
|
||||
ok(f"环境就绪 (python:{version_str})")
|
||||
|
||||
return 0
|
||||
```
|
||||
|
||||
#### 3.2.2 aide env ensure
|
||||
|
||||
```python
|
||||
from core.config import Config
|
||||
from core.output import ok, warn, err
|
||||
|
||||
def env_ensure_project() -> int:
|
||||
"""检查项目开发环境"""
|
||||
|
||||
# 1. 加载配置
|
||||
try:
|
||||
config = Config(Path.cwd())
|
||||
config.load()
|
||||
except FileNotFoundError:
|
||||
err(
|
||||
"配置文件不存在",
|
||||
[
|
||||
"位置: .aide/config.toml",
|
||||
"建议: 运行 'aide init' 创建配置文件"
|
||||
]
|
||||
)
|
||||
return 4
|
||||
except Exception as e:
|
||||
err(
|
||||
"配置文件读取失败",
|
||||
[
|
||||
f"原因: {str(e)}",
|
||||
"建议: 检查配置文件格式"
|
||||
]
|
||||
)
|
||||
return 4
|
||||
|
||||
# 2. 检查 Python 版本
|
||||
required_version = config.get("env.python.version", ">=3.10")
|
||||
if not check_python_version(required_version):
|
||||
err(
|
||||
f"Python 版本不满足要求 (需要 {required_version}, 当前 {get_python_version()})",
|
||||
[
|
||||
"建议: 升级 Python 或修改配置文件",
|
||||
"配置: .aide/config.toml (env.python.version)"
|
||||
]
|
||||
)
|
||||
return 3
|
||||
|
||||
# 3. 检查虚拟环境
|
||||
venv_path = config.get("env.python.venv", ".venv")
|
||||
venv_result = check_venv(venv_path)
|
||||
|
||||
if venv_result == "missing":
|
||||
# 尝试创建虚拟环境
|
||||
if create_venv(venv_path):
|
||||
warn(f"已修复: 创建虚拟环境 {venv_path}")
|
||||
else:
|
||||
err(
|
||||
f"虚拟环境不存在且无法创建 ({venv_path})",
|
||||
[
|
||||
"建议: 手动创建虚拟环境",
|
||||
f"命令: python3 -m venv {venv_path}"
|
||||
]
|
||||
)
|
||||
return 3
|
||||
|
||||
# 4. 检查可选工具
|
||||
tools_info = []
|
||||
|
||||
# 检查 git
|
||||
if config.get("env.tools.git", True):
|
||||
git_version = get_tool_version("git")
|
||||
if git_version:
|
||||
tools_info.append(f"git:{git_version}")
|
||||
else:
|
||||
warn("git 未安装(可选)")
|
||||
|
||||
# 检查 uv
|
||||
if config.get("env.tools.uv", False):
|
||||
uv_version = get_tool_version("uv")
|
||||
if uv_version:
|
||||
tools_info.append(f"uv:{uv_version}")
|
||||
else:
|
||||
warn("uv 未安装(可选)")
|
||||
|
||||
# 5. 输出成功信息
|
||||
python_version = get_python_version()
|
||||
info_parts = [f"python:{python_version}"] + tools_info
|
||||
ok(f"环境就绪 ({', '.join(info_parts)})")
|
||||
|
||||
return 0
|
||||
```
|
||||
|
||||
### 3.3 辅助函数
|
||||
|
||||
#### 3.3.1 版本检查
|
||||
|
||||
```python
|
||||
import re
|
||||
from packaging import version
|
||||
|
||||
def check_python_version(requirement: str) -> bool:
|
||||
"""检查 Python 版本是否满足要求
|
||||
|
||||
Args:
|
||||
requirement: 版本要求(如 ">=3.10")
|
||||
|
||||
Returns:
|
||||
是否满足要求
|
||||
"""
|
||||
current = get_python_version()
|
||||
|
||||
# 解析要求
|
||||
match = re.match(r'(>=|<=|>|<|==)?(\d+\.\d+(?:\.\d+)?)', requirement)
|
||||
if not match:
|
||||
return True # 无法解析,假设满足
|
||||
|
||||
operator, required = match.groups()
|
||||
operator = operator or "=="
|
||||
|
||||
# 比较版本
|
||||
try:
|
||||
current_ver = version.parse(current)
|
||||
required_ver = version.parse(required)
|
||||
|
||||
if operator == ">=":
|
||||
return current_ver >= required_ver
|
||||
elif operator == "<=":
|
||||
return current_ver <= required_ver
|
||||
elif operator == ">":
|
||||
return current_ver > required_ver
|
||||
elif operator == "<":
|
||||
return current_ver < required_ver
|
||||
elif operator == "==":
|
||||
return current_ver == required_ver
|
||||
except Exception:
|
||||
return True
|
||||
|
||||
return True
|
||||
|
||||
def get_python_version() -> str:
|
||||
"""获取当前 Python 版本
|
||||
|
||||
Returns:
|
||||
版本字符串(如 "3.12.0")
|
||||
"""
|
||||
v = sys.version_info
|
||||
return f"{v.major}.{v.minor}.{v.micro}"
|
||||
```
|
||||
|
||||
#### 3.3.2 虚拟环境检查
|
||||
|
||||
```python
|
||||
import subprocess
|
||||
|
||||
def check_venv(venv_path: str) -> str:
|
||||
"""检查虚拟环境状态
|
||||
|
||||
Args:
|
||||
venv_path: 虚拟环境路径
|
||||
|
||||
Returns:
|
||||
状态:'ok', 'missing', 'invalid'
|
||||
"""
|
||||
venv_dir = Path(venv_path)
|
||||
|
||||
if not venv_dir.exists():
|
||||
return "missing"
|
||||
|
||||
# 检查是否是有效的虚拟环境
|
||||
if sys.platform == "win32":
|
||||
python_exe = venv_dir / "Scripts" / "python.exe"
|
||||
else:
|
||||
python_exe = venv_dir / "bin" / "python"
|
||||
|
||||
if python_exe.exists():
|
||||
return "ok"
|
||||
else:
|
||||
return "invalid"
|
||||
|
||||
def create_venv(venv_path: str) -> bool:
|
||||
"""创建虚拟环境
|
||||
|
||||
Args:
|
||||
venv_path: 虚拟环境路径
|
||||
|
||||
Returns:
|
||||
是否创建成功
|
||||
"""
|
||||
try:
|
||||
subprocess.run(
|
||||
[sys.executable, "-m", "venv", venv_path],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
return True
|
||||
except subprocess.CalledProcessError:
|
||||
return False
|
||||
except Exception:
|
||||
return False
|
||||
```
|
||||
|
||||
#### 3.3.3 工具版本检查
|
||||
|
||||
```python
|
||||
def get_tool_version(tool: str) -> str | None:
|
||||
"""获取工具版本
|
||||
|
||||
Args:
|
||||
tool: 工具名称(如 "git", "uv")
|
||||
|
||||
Returns:
|
||||
版本字符串,未安装则返回 None
|
||||
"""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
[tool, "--version"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=5
|
||||
)
|
||||
|
||||
if result.returncode == 0:
|
||||
# 解析版本号
|
||||
output = result.stdout.strip()
|
||||
match = re.search(r'(\d+\.\d+\.\d+)', output)
|
||||
if match:
|
||||
return match.group(1)
|
||||
return "unknown"
|
||||
except (subprocess.TimeoutExpired, FileNotFoundError):
|
||||
pass
|
||||
|
||||
return None
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、错误处理
|
||||
|
||||
### 4.1 错误分类
|
||||
|
||||
| 错误类型 | 退出码 | 处理方式 |
|
||||
|---------|--------|---------|
|
||||
| Python 版本不满足 | 3 | 显示当前版本和要求,给出升级建议 |
|
||||
| 配置文件不存在 | 4 | 提示运行 aide init |
|
||||
| 配置文件格式错误 | 4 | 显示错误位置和原因 |
|
||||
| 虚拟环境无法创建 | 3 | 显示创建命令 |
|
||||
| 必需工具未安装 | 3 | 显示安装建议 |
|
||||
|
||||
### 4.2 错误恢复
|
||||
|
||||
```python
|
||||
def env_ensure_with_retry(runtime_only: bool = False, max_retries: int = 3) -> int:
|
||||
"""带重试的环境检测
|
||||
|
||||
Args:
|
||||
runtime_only: 是否仅检查运行时
|
||||
max_retries: 最大重试次数
|
||||
|
||||
Returns:
|
||||
退出码
|
||||
"""
|
||||
for attempt in range(max_retries):
|
||||
result = env_ensure(runtime_only)
|
||||
|
||||
if result == 0:
|
||||
return 0
|
||||
|
||||
if attempt < max_retries - 1:
|
||||
info(f"重试 ({attempt + 1}/{max_retries})...")
|
||||
|
||||
return result
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、测试用例
|
||||
|
||||
### 5.1 runtime 模式测试
|
||||
|
||||
```python
|
||||
def test_env_ensure_runtime_success():
|
||||
"""测试运行时环境检测成功"""
|
||||
result = env_ensure_runtime()
|
||||
assert result == 0
|
||||
|
||||
def test_env_ensure_runtime_old_python(monkeypatch):
|
||||
"""测试 Python 版本过低"""
|
||||
# 模拟 Python 3.8
|
||||
monkeypatch.setattr(sys, "version_info", (3, 8, 0, "final", 0))
|
||||
|
||||
result = env_ensure_runtime()
|
||||
assert result == 3
|
||||
|
||||
def test_env_ensure_runtime_missing_lib(monkeypatch):
|
||||
"""测试缺少必需库"""
|
||||
# 模拟 import 失败
|
||||
def mock_import(name, *args, **kwargs):
|
||||
if name in ["tomli", "tomllib"]:
|
||||
raise ImportError(f"No module named '{name}'")
|
||||
return __import__(name, *args, **kwargs)
|
||||
|
||||
monkeypatch.setattr("builtins.__import__", mock_import)
|
||||
|
||||
result = env_ensure_runtime()
|
||||
assert result == 3
|
||||
```
|
||||
|
||||
### 5.2 项目环境测试
|
||||
|
||||
```python
|
||||
def test_env_ensure_project_success(tmp_path, monkeypatch):
|
||||
"""测试项目环境检测成功"""
|
||||
monkeypatch.chdir(tmp_path)
|
||||
|
||||
# 创建配置文件
|
||||
cmd_init([])
|
||||
|
||||
# 检测环境
|
||||
result = env_ensure_project()
|
||||
assert result == 0
|
||||
|
||||
def test_env_ensure_project_no_config(tmp_path, monkeypatch):
|
||||
"""测试配置文件不存在"""
|
||||
monkeypatch.chdir(tmp_path)
|
||||
|
||||
result = env_ensure_project()
|
||||
assert result == 4
|
||||
|
||||
def test_env_ensure_project_create_venv(tmp_path, monkeypatch):
|
||||
"""测试自动创建虚拟环境"""
|
||||
monkeypatch.chdir(tmp_path)
|
||||
|
||||
# 创建配置文件
|
||||
cmd_init([])
|
||||
|
||||
# 确保虚拟环境不存在
|
||||
venv_path = tmp_path / ".venv"
|
||||
if venv_path.exists():
|
||||
shutil.rmtree(venv_path)
|
||||
|
||||
# 检测环境(应该自动创建虚拟环境)
|
||||
result = env_ensure_project()
|
||||
assert result == 0
|
||||
assert venv_path.exists()
|
||||
```
|
||||
|
||||
### 5.3 版本检查测试
|
||||
|
||||
```python
|
||||
def test_check_python_version():
|
||||
"""测试 Python 版本检查"""
|
||||
# 测试各种版本要求
|
||||
assert check_python_version(">=3.10") == True # 假设当前是 3.12
|
||||
assert check_python_version(">=3.15") == False
|
||||
assert check_python_version("==3.12") == True
|
||||
assert check_python_version("<4.0") == True
|
||||
|
||||
def test_get_python_version():
|
||||
"""测试获取 Python 版本"""
|
||||
version = get_python_version()
|
||||
assert re.match(r'\d+\.\d+\.\d+', version)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、性能要求
|
||||
|
||||
### 6.1 执行时间
|
||||
|
||||
- `--runtime` 模式:< 100ms
|
||||
- 项目环境检测(无修复):< 500ms
|
||||
- 项目环境检测(创建虚拟环境):< 10s
|
||||
|
||||
### 6.2 资源占用
|
||||
|
||||
- 内存:< 50MB
|
||||
- CPU:低(主要是 I/O 操作)
|
||||
|
||||
---
|
||||
|
||||
## 七、集成测试
|
||||
|
||||
### 7.1 完整工作流
|
||||
|
||||
```python
|
||||
def test_env_workflow(tmp_path, monkeypatch):
|
||||
"""测试完整的环境检测工作流"""
|
||||
monkeypatch.chdir(tmp_path)
|
||||
|
||||
# 1. 检查运行时环境
|
||||
result = env_ensure_runtime()
|
||||
assert result == 0
|
||||
|
||||
# 2. 初始化项目
|
||||
result = cmd_init([])
|
||||
assert result == 0
|
||||
|
||||
# 3. 检查项目环境
|
||||
result = env_ensure_project()
|
||||
assert result == 0
|
||||
|
||||
# 4. 验证虚拟环境已创建
|
||||
assert (tmp_path / ".venv").exists()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 八、总结
|
||||
|
||||
### 8.1 核心要点
|
||||
|
||||
1. 两种模式:runtime 和 project
|
||||
2. 自动修复部分问题(如创建虚拟环境)
|
||||
3. 清晰的错误信息和建议
|
||||
4. 完善的版本检查逻辑
|
||||
|
||||
### 8.2 实现检查清单
|
||||
|
||||
- [ ] 实现 cmd_env 函数
|
||||
- [ ] 实现 env_ensure_runtime 函数
|
||||
- [ ] 实现 env_ensure_project 函数
|
||||
- [ ] 实现版本检查函数
|
||||
- [ ] 实现虚拟环境检查和创建
|
||||
- [ ] 实现工具版本检查
|
||||
- [ ] 编写单元测试
|
||||
- [ ] 编写集成测试
|
||||
- [ ] 性能测试
|
||||
|
||||
---
|
||||
|
||||
**版本**:v1.0
|
||||
**更新日期**:2025-12-13
|
||||
@@ -1,745 +0,0 @@
|
||||
# aide config 设计
|
||||
|
||||
## 一、命令概述
|
||||
|
||||
### 1.1 功能定位
|
||||
|
||||
`aide config` 命令用于读取和修改项目配置文件,提供命令行方式访问配置。
|
||||
|
||||
### 1.2 执行时机
|
||||
|
||||
- 用户需要查看配置值时
|
||||
- 用户需要修改配置值时
|
||||
- Commands 中需要获取配置时(如 prep/exec 的默认文档路径)
|
||||
|
||||
### 1.3 命令格式
|
||||
|
||||
```bash
|
||||
aide config get <key>
|
||||
aide config set <key> <value>
|
||||
```
|
||||
|
||||
**参数**:
|
||||
- `<key>`:配置键,支持点号分隔(如 `task.source`)
|
||||
- `<value>`:配置值(仅 set 命令需要)
|
||||
|
||||
---
|
||||
|
||||
## 二、功能需求
|
||||
|
||||
### 2.1 aide config get
|
||||
|
||||
**功能**:获取配置值
|
||||
|
||||
**输出格式**:
|
||||
|
||||
单个值:
|
||||
```
|
||||
task.source = "task-now.md"
|
||||
```
|
||||
|
||||
数组值:
|
||||
```
|
||||
flow.phases = ["flow-design", "impl", "verify", "docs", "finish"]
|
||||
```
|
||||
|
||||
布尔值:
|
||||
```
|
||||
env.tools.git = true
|
||||
```
|
||||
|
||||
**错误处理**:
|
||||
|
||||
配置键不存在:
|
||||
```
|
||||
✗ 配置键不存在: invalid.key
|
||||
可用的配置键: task.source, task.spec, env.python.version
|
||||
```
|
||||
|
||||
配置文件不存在:
|
||||
```
|
||||
✗ 配置文件不存在
|
||||
位置: .aide/config.toml
|
||||
建议: 运行 'aide init' 创建配置文件
|
||||
```
|
||||
|
||||
### 2.2 aide config set
|
||||
|
||||
**功能**:设置配置值
|
||||
|
||||
**输出**:
|
||||
|
||||
成功时无输出(静默原则)
|
||||
|
||||
**错误处理**:
|
||||
|
||||
配置键不存在:
|
||||
```
|
||||
✗ 配置键不存在: invalid.key
|
||||
可用的配置键: task.source, task.spec, env.python.version
|
||||
```
|
||||
|
||||
配置值类型错误:
|
||||
```
|
||||
✗ 配置值类型错误
|
||||
键: env.tools.git
|
||||
期望类型: boolean
|
||||
实际值: "yes"
|
||||
建议: 使用 true 或 false
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、实现设计
|
||||
|
||||
### 3.1 函数接口
|
||||
|
||||
```python
|
||||
def cmd_config(args: list[str]) -> int:
|
||||
"""aide config 命令处理
|
||||
|
||||
Args:
|
||||
args: 命令参数
|
||||
|
||||
Returns:
|
||||
退出码(0 表示成功)
|
||||
"""
|
||||
pass
|
||||
|
||||
def config_get(key: str) -> int:
|
||||
"""获取配置值
|
||||
|
||||
Args:
|
||||
key: 配置键
|
||||
|
||||
Returns:
|
||||
退出码
|
||||
"""
|
||||
pass
|
||||
|
||||
def config_set(key: str, value: str) -> int:
|
||||
"""设置配置值
|
||||
|
||||
Args:
|
||||
key: 配置键
|
||||
value: 配置值(字符串形式)
|
||||
|
||||
Returns:
|
||||
退出码
|
||||
"""
|
||||
pass
|
||||
```
|
||||
|
||||
### 3.2 实现流程
|
||||
|
||||
#### 3.2.1 cmd_config
|
||||
|
||||
```python
|
||||
from core.output import err
|
||||
|
||||
def cmd_config(args: list[str]) -> int:
|
||||
"""aide config 命令处理"""
|
||||
|
||||
if not args:
|
||||
err(
|
||||
"缺少子命令",
|
||||
[
|
||||
"用法: aide config get <key>",
|
||||
" aide config set <key> <value>"
|
||||
]
|
||||
)
|
||||
return 2
|
||||
|
||||
subcommand = args[0]
|
||||
|
||||
if subcommand == "get":
|
||||
if len(args) < 2:
|
||||
err("缺少参数: key")
|
||||
return 2
|
||||
return config_get(args[1])
|
||||
|
||||
elif subcommand == "set":
|
||||
if len(args) < 3:
|
||||
err("缺少参数: key 或 value")
|
||||
return 2
|
||||
return config_set(args[1], args[2])
|
||||
|
||||
else:
|
||||
err(
|
||||
f"未知子命令: {subcommand}",
|
||||
["可用子命令: get, set"]
|
||||
)
|
||||
return 2
|
||||
```
|
||||
|
||||
#### 3.2.2 config_get
|
||||
|
||||
```python
|
||||
from pathlib import Path
|
||||
from core.config import Config
|
||||
from core.output import err
|
||||
|
||||
def config_get(key: str) -> int:
|
||||
"""获取配置值"""
|
||||
|
||||
try:
|
||||
# 加载配置
|
||||
config = Config(Path.cwd())
|
||||
config.load()
|
||||
|
||||
# 获取值
|
||||
value = config.get(key)
|
||||
|
||||
if value is None:
|
||||
# 配置键不存在
|
||||
available_keys = get_available_keys(config._config)
|
||||
err(
|
||||
f"配置键不存在: {key}",
|
||||
[f"可用的配置键: {', '.join(available_keys)}"]
|
||||
)
|
||||
return 4
|
||||
|
||||
# 格式化输出
|
||||
formatted_value = format_config_value(value)
|
||||
print(f"{key} = {formatted_value}")
|
||||
|
||||
return 0
|
||||
|
||||
except FileNotFoundError:
|
||||
err(
|
||||
"配置文件不存在",
|
||||
[
|
||||
"位置: .aide/config.toml",
|
||||
"建议: 运行 'aide init' 创建配置文件"
|
||||
]
|
||||
)
|
||||
return 4
|
||||
|
||||
except Exception as e:
|
||||
err(
|
||||
"配置读取失败",
|
||||
[f"原因: {str(e)}"]
|
||||
)
|
||||
return 4
|
||||
```
|
||||
|
||||
#### 3.2.3 config_set
|
||||
|
||||
```python
|
||||
def config_set(key: str, value_str: str) -> int:
|
||||
"""设置配置值"""
|
||||
|
||||
try:
|
||||
# 加载配置
|
||||
config = Config(Path.cwd())
|
||||
config.load()
|
||||
|
||||
# 验证配置键是否存在
|
||||
if not is_valid_config_key(key):
|
||||
available_keys = get_available_keys(config._config)
|
||||
err(
|
||||
f"配置键不存在: {key}",
|
||||
[f"可用的配置键: {', '.join(available_keys)}"]
|
||||
)
|
||||
return 4
|
||||
|
||||
# 解析值
|
||||
try:
|
||||
value = parse_config_value(key, value_str, config._config)
|
||||
except ValueError as e:
|
||||
err(
|
||||
"配置值类型错误",
|
||||
[
|
||||
f"键: {key}",
|
||||
f"原因: {str(e)}"
|
||||
]
|
||||
)
|
||||
return 4
|
||||
|
||||
# 设置值
|
||||
config.set(key, value)
|
||||
|
||||
# 成功时无输出(静默原则)
|
||||
return 0
|
||||
|
||||
except FileNotFoundError:
|
||||
err(
|
||||
"配置文件不存在",
|
||||
[
|
||||
"位置: .aide/config.toml",
|
||||
"建议: 运行 'aide init' 创建配置文件"
|
||||
]
|
||||
)
|
||||
return 4
|
||||
|
||||
except Exception as e:
|
||||
err(
|
||||
"配置写入失败",
|
||||
[f"原因: {str(e)}"]
|
||||
)
|
||||
return 4
|
||||
```
|
||||
|
||||
### 3.3 辅助函数
|
||||
|
||||
#### 3.3.1 格式化配置值
|
||||
|
||||
```python
|
||||
import json
|
||||
|
||||
def format_config_value(value) -> str:
|
||||
"""格式化配置值用于输出
|
||||
|
||||
Args:
|
||||
value: 配置值
|
||||
|
||||
Returns:
|
||||
格式化后的字符串
|
||||
"""
|
||||
if isinstance(value, str):
|
||||
return f'"{value}"'
|
||||
elif isinstance(value, bool):
|
||||
return "true" if value else "false"
|
||||
elif isinstance(value, (list, dict)):
|
||||
return json.dumps(value, ensure_ascii=False)
|
||||
else:
|
||||
return str(value)
|
||||
```
|
||||
|
||||
#### 3.3.2 解析配置值
|
||||
|
||||
```python
|
||||
def parse_config_value(key: str, value_str: str, config: dict):
|
||||
"""解析配置值
|
||||
|
||||
Args:
|
||||
key: 配置键
|
||||
value_str: 值字符串
|
||||
config: 当前配置(用于推断类型)
|
||||
|
||||
Returns:
|
||||
解析后的值
|
||||
|
||||
Raises:
|
||||
ValueError: 值类型错误
|
||||
"""
|
||||
# 获取当前值以推断类型
|
||||
current_value = get_config_value(config, key)
|
||||
|
||||
if current_value is None:
|
||||
# 新键,尝试自动推断类型
|
||||
return auto_parse_value(value_str)
|
||||
|
||||
# 根据当前值的类型解析
|
||||
if isinstance(current_value, bool):
|
||||
return parse_bool(value_str)
|
||||
elif isinstance(current_value, int):
|
||||
return parse_int(value_str)
|
||||
elif isinstance(current_value, float):
|
||||
return parse_float(value_str)
|
||||
elif isinstance(current_value, list):
|
||||
return parse_list(value_str)
|
||||
elif isinstance(current_value, dict):
|
||||
return parse_dict(value_str)
|
||||
else:
|
||||
# 字符串
|
||||
return value_str
|
||||
|
||||
def parse_bool(value_str: str) -> bool:
|
||||
"""解析布尔值"""
|
||||
lower = value_str.lower()
|
||||
if lower in ["true", "yes", "1"]:
|
||||
return True
|
||||
elif lower in ["false", "no", "0"]:
|
||||
return False
|
||||
else:
|
||||
raise ValueError(f"无效的布尔值: {value_str},使用 true 或 false")
|
||||
|
||||
def parse_int(value_str: str) -> int:
|
||||
"""解析整数"""
|
||||
try:
|
||||
return int(value_str)
|
||||
except ValueError:
|
||||
raise ValueError(f"无效的整数: {value_str}")
|
||||
|
||||
def parse_float(value_str: str) -> float:
|
||||
"""解析浮点数"""
|
||||
try:
|
||||
return float(value_str)
|
||||
except ValueError:
|
||||
raise ValueError(f"无效的浮点数: {value_str}")
|
||||
|
||||
def parse_list(value_str: str) -> list:
|
||||
"""解析列表(JSON 格式)"""
|
||||
try:
|
||||
value = json.loads(value_str)
|
||||
if not isinstance(value, list):
|
||||
raise ValueError("不是列表")
|
||||
return value
|
||||
except json.JSONDecodeError:
|
||||
raise ValueError(f"无效的列表格式: {value_str},使用 JSON 格式")
|
||||
|
||||
def parse_dict(value_str: str) -> dict:
|
||||
"""解析字典(JSON 格式)"""
|
||||
try:
|
||||
value = json.loads(value_str)
|
||||
if not isinstance(value, dict):
|
||||
raise ValueError("不是字典")
|
||||
return value
|
||||
except json.JSONDecodeError:
|
||||
raise ValueError(f"无效的字典格式: {value_str},使用 JSON 格式")
|
||||
|
||||
def auto_parse_value(value_str: str):
|
||||
"""自动推断并解析值"""
|
||||
# 尝试 JSON
|
||||
try:
|
||||
return json.loads(value_str)
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
|
||||
# 尝试布尔值
|
||||
if value_str.lower() in ["true", "false", "yes", "no"]:
|
||||
return parse_bool(value_str)
|
||||
|
||||
# 尝试数字
|
||||
try:
|
||||
if "." in value_str:
|
||||
return float(value_str)
|
||||
else:
|
||||
return int(value_str)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# 默认为字符串
|
||||
return value_str
|
||||
```
|
||||
|
||||
#### 3.3.3 获取可用配置键
|
||||
|
||||
```python
|
||||
def get_available_keys(config: dict, prefix: str = "") -> list[str]:
|
||||
"""获取所有可用的配置键
|
||||
|
||||
Args:
|
||||
config: 配置字典
|
||||
prefix: 键前缀
|
||||
|
||||
Returns:
|
||||
配置键列表
|
||||
"""
|
||||
keys = []
|
||||
|
||||
for key, value in config.items():
|
||||
full_key = f"{prefix}{key}" if prefix else key
|
||||
|
||||
if isinstance(value, dict):
|
||||
# 递归获取嵌套键
|
||||
keys.extend(get_available_keys(value, f"{full_key}."))
|
||||
else:
|
||||
keys.append(full_key)
|
||||
|
||||
return sorted(keys)
|
||||
|
||||
def is_valid_config_key(key: str) -> bool:
|
||||
"""验证配置键是否有效
|
||||
|
||||
Args:
|
||||
key: 配置键
|
||||
|
||||
Returns:
|
||||
是否有效
|
||||
"""
|
||||
# 定义允许的配置键
|
||||
valid_keys = [
|
||||
"task.source",
|
||||
"task.spec",
|
||||
"env.python.version",
|
||||
"env.python.venv",
|
||||
"env.tools.uv",
|
||||
"env.tools.git",
|
||||
"flow.phases",
|
||||
"flow.flowchart_dir",
|
||||
"decide.port",
|
||||
"decide.decisions_dir",
|
||||
"output.color",
|
||||
"output.language"
|
||||
]
|
||||
|
||||
return key in valid_keys
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、错误处理
|
||||
|
||||
### 4.1 错误分类
|
||||
|
||||
| 错误类型 | 退出码 | 处理方式 |
|
||||
|---------|--------|---------|
|
||||
| 缺少参数 | 2 | 显示用法 |
|
||||
| 配置文件不存在 | 4 | 提示运行 aide init |
|
||||
| 配置键不存在 | 4 | 显示可用的键 |
|
||||
| 配置值类型错误 | 4 | 显示期望类型和建议 |
|
||||
| 配置文件格式错误 | 4 | 显示错误位置 |
|
||||
|
||||
### 4.2 类型验证
|
||||
|
||||
```python
|
||||
def validate_config_value(key: str, value) -> list[str]:
|
||||
"""验证配置值
|
||||
|
||||
Args:
|
||||
key: 配置键
|
||||
value: 配置值
|
||||
|
||||
Returns:
|
||||
错误列表(空列表表示无错误)
|
||||
"""
|
||||
errors = []
|
||||
|
||||
# 验证特定键的值
|
||||
if key == "env.python.version":
|
||||
if not isinstance(value, str):
|
||||
errors.append("env.python.version 必须是字符串")
|
||||
elif not is_valid_version_spec(value):
|
||||
errors.append(f"无效的版本格式: {value}")
|
||||
|
||||
elif key == "env.python.venv":
|
||||
if not isinstance(value, str):
|
||||
errors.append("env.python.venv 必须是字符串")
|
||||
|
||||
elif key in ["env.tools.uv", "env.tools.git", "output.color"]:
|
||||
if not isinstance(value, bool):
|
||||
errors.append(f"{key} 必须是布尔值")
|
||||
|
||||
elif key == "flow.phases":
|
||||
if not isinstance(value, list):
|
||||
errors.append("flow.phases 必须是数组")
|
||||
elif not all(isinstance(p, str) for p in value):
|
||||
errors.append("flow.phases 的元素必须是字符串")
|
||||
|
||||
elif key == "decide.port":
|
||||
if not isinstance(value, int):
|
||||
errors.append("decide.port 必须是整数")
|
||||
elif not (1024 <= value <= 65535):
|
||||
errors.append("decide.port 必须在 1024-65535 之间")
|
||||
|
||||
return errors
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、测试用例
|
||||
|
||||
### 5.1 config get 测试
|
||||
|
||||
```python
|
||||
def test_config_get_success(tmp_path, monkeypatch, capsys):
|
||||
"""测试获取配置成功"""
|
||||
monkeypatch.chdir(tmp_path)
|
||||
|
||||
# 初始化配置
|
||||
cmd_init([])
|
||||
|
||||
# 获取配置
|
||||
result = config_get("task.source")
|
||||
|
||||
# 验证退出码
|
||||
assert result == 0
|
||||
|
||||
# 验证输出
|
||||
captured = capsys.readouterr()
|
||||
assert 'task.source = "task-now.md"' in captured.out
|
||||
|
||||
def test_config_get_not_found(tmp_path, monkeypatch, capsys):
|
||||
"""测试配置键不存在"""
|
||||
monkeypatch.chdir(tmp_path)
|
||||
|
||||
# 初始化配置
|
||||
cmd_init([])
|
||||
|
||||
# 获取不存在的配置
|
||||
result = config_get("invalid.key")
|
||||
|
||||
# 验证退出码
|
||||
assert result == 4
|
||||
|
||||
# 验证输出
|
||||
captured = capsys.readouterr()
|
||||
assert "配置键不存在" in captured.out
|
||||
|
||||
def test_config_get_no_config(tmp_path, monkeypatch, capsys):
|
||||
"""测试配置文件不存在"""
|
||||
monkeypatch.chdir(tmp_path)
|
||||
|
||||
# 获取配置(未初始化)
|
||||
result = config_get("task.source")
|
||||
|
||||
# 验证退出码
|
||||
assert result == 4
|
||||
|
||||
# 验证输出
|
||||
captured = capsys.readouterr()
|
||||
assert "配置文件不存在" in captured.out
|
||||
```
|
||||
|
||||
### 5.2 config set 测试
|
||||
|
||||
```python
|
||||
def test_config_set_success(tmp_path, monkeypatch):
|
||||
"""测试设置配置成功"""
|
||||
monkeypatch.chdir(tmp_path)
|
||||
|
||||
# 初始化配置
|
||||
cmd_init([])
|
||||
|
||||
# 设置配置
|
||||
result = config_set("task.source", "new-task.md")
|
||||
|
||||
# 验证退出码
|
||||
assert result == 0
|
||||
|
||||
# 验证配置已更新
|
||||
config = Config(tmp_path)
|
||||
config.load()
|
||||
assert config.get("task.source") == "new-task.md"
|
||||
|
||||
def test_config_set_bool(tmp_path, monkeypatch):
|
||||
"""测试设置布尔值"""
|
||||
monkeypatch.chdir(tmp_path)
|
||||
|
||||
# 初始化配置
|
||||
cmd_init([])
|
||||
|
||||
# 设置布尔值
|
||||
result = config_set("env.tools.uv", "true")
|
||||
assert result == 0
|
||||
|
||||
# 验证
|
||||
config = Config(tmp_path)
|
||||
config.load()
|
||||
assert config.get("env.tools.uv") == True
|
||||
|
||||
def test_config_set_invalid_type(tmp_path, monkeypatch, capsys):
|
||||
"""测试设置错误类型"""
|
||||
monkeypatch.chdir(tmp_path)
|
||||
|
||||
# 初始化配置
|
||||
cmd_init([])
|
||||
|
||||
# 设置错误类型
|
||||
result = config_set("env.tools.git", "yes")
|
||||
|
||||
# 验证退出码
|
||||
assert result == 4
|
||||
|
||||
# 验证输出
|
||||
captured = capsys.readouterr()
|
||||
assert "类型错误" in captured.out
|
||||
```
|
||||
|
||||
### 5.3 值解析测试
|
||||
|
||||
```python
|
||||
def test_parse_bool():
|
||||
"""测试布尔值解析"""
|
||||
assert parse_bool("true") == True
|
||||
assert parse_bool("false") == False
|
||||
assert parse_bool("yes") == True
|
||||
assert parse_bool("no") == False
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
parse_bool("invalid")
|
||||
|
||||
def test_parse_list():
|
||||
"""测试列表解析"""
|
||||
result = parse_list('["a", "b", "c"]')
|
||||
assert result == ["a", "b", "c"]
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
parse_list("not a list")
|
||||
|
||||
def test_auto_parse_value():
|
||||
"""测试自动解析"""
|
||||
assert auto_parse_value("true") == True
|
||||
assert auto_parse_value("123") == 123
|
||||
assert auto_parse_value("3.14") == 3.14
|
||||
assert auto_parse_value('["a"]') == ["a"]
|
||||
assert auto_parse_value("text") == "text"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、使用示例
|
||||
|
||||
### 6.1 查看配置
|
||||
|
||||
```bash
|
||||
# 查看任务文档路径
|
||||
aide config get task.source
|
||||
|
||||
# 查看 Python 版本要求
|
||||
aide config get env.python.version
|
||||
|
||||
# 查看流程环节列表
|
||||
aide config get flow.phases
|
||||
```
|
||||
|
||||
### 6.2 修改配置
|
||||
|
||||
```bash
|
||||
# 修改任务文档路径
|
||||
aide config set task.source "my-task.md"
|
||||
|
||||
# 修改 Python 版本要求
|
||||
aide config set env.python.version ">=3.11"
|
||||
|
||||
# 启用 uv
|
||||
aide config set env.tools.uv true
|
||||
|
||||
# 修改端口
|
||||
aide config set decide.port 8080
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 七、性能要求
|
||||
|
||||
### 7.1 执行时间
|
||||
|
||||
- config get:< 100ms
|
||||
- config set:< 200ms
|
||||
|
||||
### 7.2 资源占用
|
||||
|
||||
- 内存:< 20MB
|
||||
- 磁盘 I/O:最小化
|
||||
|
||||
---
|
||||
|
||||
## 八、总结
|
||||
|
||||
### 8.1 核心要点
|
||||
|
||||
1. 支持 get 和 set 两个子命令
|
||||
2. 点号分隔的键访问
|
||||
3. 自动类型推断和验证
|
||||
4. 静默原则(set 成功时无输出)
|
||||
5. 清晰的错误信息
|
||||
|
||||
### 8.2 实现检查清单
|
||||
|
||||
- [ ] 实现 cmd_config 函数
|
||||
- [ ] 实现 config_get 函数
|
||||
- [ ] 实现 config_set 函数
|
||||
- [ ] 实现值格式化函数
|
||||
- [ ] 实现值解析函数
|
||||
- [ ] 实现类型验证
|
||||
- [ ] 编写单元测试
|
||||
- [ ] 编写集成测试
|
||||
- [ ] 性能测试
|
||||
|
||||
---
|
||||
|
||||
**版本**:v1.0
|
||||
**更新日期**:2025-12-13
|
||||
@@ -1,698 +0,0 @@
|
||||
# 配置文件规范
|
||||
|
||||
## 一、概述
|
||||
|
||||
### 1.1 配置文件定位
|
||||
|
||||
- **文件路径**:`.aide/config.toml`
|
||||
- **格式**:TOML(Tom's Obvious, Minimal Language)
|
||||
- **特点**:自文档化,包含详细注释
|
||||
- **创建时机**:`aide init` 命令执行时
|
||||
|
||||
### 1.2 设计原则
|
||||
|
||||
1. **自文档化**:每个配置项都有注释说明
|
||||
2. **合理默认值**:开箱即用,无需修改
|
||||
3. **类型安全**:明确的数据类型
|
||||
4. **向后兼容**:新版本保持旧配置可用
|
||||
|
||||
---
|
||||
|
||||
## 二、配置文件结构
|
||||
|
||||
### 2.1 完整示例
|
||||
|
||||
```toml
|
||||
# Aide 项目配置文件
|
||||
# 由 aide init 自动生成
|
||||
# 版本: 1.0
|
||||
|
||||
[task]
|
||||
# 任务原文档路径(prep 阶段使用)
|
||||
source = "task-now.md"
|
||||
|
||||
# 任务细则文档路径(exec 阶段使用)
|
||||
spec = "task-spec.md"
|
||||
|
||||
[env]
|
||||
# 环境配置
|
||||
|
||||
[env.python]
|
||||
# Python 版本要求(语义化版本)
|
||||
version = ">=3.10"
|
||||
|
||||
# 虚拟环境路径(相对于项目根目录)
|
||||
venv = ".venv"
|
||||
|
||||
[env.tools]
|
||||
# 可选工具配置
|
||||
|
||||
# 是否需要 uv(Python 包管理器)
|
||||
uv = false
|
||||
|
||||
# 是否需要 git
|
||||
git = true
|
||||
|
||||
[flow]
|
||||
# 流程追踪配置(后续实现)
|
||||
|
||||
# 环节列表
|
||||
phases = ["flow-design", "impl", "verify", "docs", "finish"]
|
||||
|
||||
# PlantUML 流程图目录
|
||||
flowchart_dir = "program_flowchart"
|
||||
|
||||
[decide]
|
||||
# 待定项确认配置(后续实现)
|
||||
|
||||
# Web 服务端口
|
||||
port = 3721
|
||||
|
||||
# 决策记录保存目录
|
||||
decisions_dir = ".aide/decisions"
|
||||
|
||||
[output]
|
||||
# 输出配置
|
||||
|
||||
# 是否启用颜色输出
|
||||
color = true
|
||||
|
||||
# 输出语言(当前仅支持 zh-CN)
|
||||
language = "zh-CN"
|
||||
```
|
||||
|
||||
### 2.2 配置项说明
|
||||
|
||||
#### 2.2.1 [task] 任务配置
|
||||
|
||||
| 键 | 类型 | 默认值 | 说明 |
|
||||
|---|------|--------|------|
|
||||
| `source` | string | `"task-now.md"` | 任务原文档路径 |
|
||||
| `spec` | string | `"task-spec.md"` | 任务细则文档路径 |
|
||||
|
||||
**使用场景**:
|
||||
- `prep` 命令未传入参数时,使用 `task.source`
|
||||
- `exec` 命令未传入参数时,使用 `task.spec`
|
||||
|
||||
#### 2.2.2 [env.python] Python 环境配置
|
||||
|
||||
| 键 | 类型 | 默认值 | 说明 |
|
||||
|---|------|--------|------|
|
||||
| `version` | string | `">=3.10"` | Python 版本要求 |
|
||||
| `venv` | string | `".venv"` | 虚拟环境路径 |
|
||||
|
||||
**版本格式**:
|
||||
- `">=3.10"` - 大于等于 3.10
|
||||
- `">=3.10,<4.0"` - 3.10 到 4.0 之间
|
||||
- `"3.12"` - 精确匹配 3.12
|
||||
|
||||
#### 2.2.3 [env.tools] 工具配置
|
||||
|
||||
| 键 | 类型 | 默认值 | 说明 |
|
||||
|---|------|--------|------|
|
||||
| `uv` | boolean | `false` | 是否需要 uv |
|
||||
| `git` | boolean | `true` | 是否需要 git |
|
||||
|
||||
#### 2.2.4 [flow] 流程配置(后续实现)
|
||||
|
||||
| 键 | 类型 | 默认值 | 说明 |
|
||||
|---|------|--------|------|
|
||||
| `phases` | array | `["flow-design", "impl", "verify", "docs", "finish"]` | 环节列表 |
|
||||
| `flowchart_dir` | string | `"program_flowchart"` | 流程图目录 |
|
||||
|
||||
#### 2.2.5 [decide] 待定项配置(后续实现)
|
||||
|
||||
| 键 | 类型 | 默认值 | 说明 |
|
||||
|---|------|--------|------|
|
||||
| `port` | integer | `3721` | Web 服务端口 |
|
||||
| `decisions_dir` | string | `".aide/decisions"` | 决策记录目录 |
|
||||
|
||||
#### 2.2.6 [output] 输出配置
|
||||
|
||||
| 键 | 类型 | 默认值 | 说明 |
|
||||
|---|------|--------|------|
|
||||
| `color` | boolean | `true` | 是否启用颜色 |
|
||||
| `language` | string | `"zh-CN"` | 输出语言 |
|
||||
|
||||
---
|
||||
|
||||
## 三、配置文件操作
|
||||
|
||||
### 3.1 读取配置
|
||||
|
||||
**Python 实现示例**:
|
||||
|
||||
```python
|
||||
import tomllib # Python 3.11+
|
||||
# 或
|
||||
import tomli # Python 3.10
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
def load_config(project_root: Path) -> dict:
|
||||
"""加载配置文件
|
||||
|
||||
Args:
|
||||
project_root: 项目根目录
|
||||
|
||||
Returns:
|
||||
配置字典
|
||||
|
||||
Raises:
|
||||
FileNotFoundError: 配置文件不存在
|
||||
tomllib.TOMLDecodeError: 配置文件格式错误
|
||||
"""
|
||||
config_path = project_root / ".aide" / "config.toml"
|
||||
|
||||
if not config_path.exists():
|
||||
raise FileNotFoundError(f"配置文件不存在: {config_path}")
|
||||
|
||||
with open(config_path, "rb") as f:
|
||||
return tomllib.load(f)
|
||||
|
||||
def get_config_value(config: dict, key: str, default=None):
|
||||
"""获取配置值(支持点号分隔的键)
|
||||
|
||||
Args:
|
||||
config: 配置字典
|
||||
key: 配置键(如 "task.source")
|
||||
default: 默认值
|
||||
|
||||
Returns:
|
||||
配置值
|
||||
|
||||
Example:
|
||||
>>> config = {"task": {"source": "task-now.md"}}
|
||||
>>> get_config_value(config, "task.source")
|
||||
'task-now.md'
|
||||
"""
|
||||
keys = key.split(".")
|
||||
value = config
|
||||
|
||||
for k in keys:
|
||||
if isinstance(value, dict) and k in value:
|
||||
value = value[k]
|
||||
else:
|
||||
return default
|
||||
|
||||
return value
|
||||
```
|
||||
|
||||
### 3.2 写入配置
|
||||
|
||||
**Python 实现示例**:
|
||||
|
||||
```python
|
||||
import tomli_w
|
||||
from pathlib import Path
|
||||
|
||||
def save_config(project_root: Path, config: dict) -> None:
|
||||
"""保存配置文件
|
||||
|
||||
Args:
|
||||
project_root: 项目根目录
|
||||
config: 配置字典
|
||||
"""
|
||||
config_path = project_root / ".aide" / "config.toml"
|
||||
|
||||
with open(config_path, "wb") as f:
|
||||
tomli_w.dump(config, f)
|
||||
|
||||
def set_config_value(config: dict, key: str, value) -> dict:
|
||||
"""设置配置值(支持点号分隔的键)
|
||||
|
||||
Args:
|
||||
config: 配置字典
|
||||
key: 配置键(如 "task.source")
|
||||
value: 配置值
|
||||
|
||||
Returns:
|
||||
更新后的配置字典
|
||||
|
||||
Example:
|
||||
>>> config = {"task": {"source": "task-now.md"}}
|
||||
>>> set_config_value(config, "task.source", "new-task.md")
|
||||
{'task': {'source': 'new-task.md'}}
|
||||
"""
|
||||
keys = key.split(".")
|
||||
current = config
|
||||
|
||||
# 导航到倒数第二层
|
||||
for k in keys[:-1]:
|
||||
if k not in current:
|
||||
current[k] = {}
|
||||
current = current[k]
|
||||
|
||||
# 设置最后一层的值
|
||||
current[keys[-1]] = value
|
||||
|
||||
return config
|
||||
```
|
||||
|
||||
### 3.3 验证配置
|
||||
|
||||
**验证规则**:
|
||||
|
||||
```python
|
||||
from typing import Any
|
||||
|
||||
def validate_config(config: dict) -> list[str]:
|
||||
"""验证配置文件
|
||||
|
||||
Args:
|
||||
config: 配置字典
|
||||
|
||||
Returns:
|
||||
错误列表(空列表表示无错误)
|
||||
"""
|
||||
errors = []
|
||||
|
||||
# 验证必需的顶层键
|
||||
required_sections = ["task", "env", "output"]
|
||||
for section in required_sections:
|
||||
if section not in config:
|
||||
errors.append(f"缺少必需的配置节: [{section}]")
|
||||
|
||||
# 验证 task 配置
|
||||
if "task" in config:
|
||||
if "source" not in config["task"]:
|
||||
errors.append("缺少配置项: task.source")
|
||||
if "spec" not in config["task"]:
|
||||
errors.append("缺少配置项: task.spec")
|
||||
|
||||
# 验证 env.python 配置
|
||||
if "env" in config and "python" in config["env"]:
|
||||
python_config = config["env"]["python"]
|
||||
if "version" not in python_config:
|
||||
errors.append("缺少配置项: env.python.version")
|
||||
else:
|
||||
# 验证版本格式
|
||||
version = python_config["version"]
|
||||
if not is_valid_version_spec(version):
|
||||
errors.append(f"无效的版本格式: {version}")
|
||||
|
||||
# 验证 output 配置
|
||||
if "output" in config:
|
||||
output_config = config["output"]
|
||||
if "language" in output_config:
|
||||
lang = output_config["language"]
|
||||
if lang not in ["zh-CN", "en-US"]:
|
||||
errors.append(f"不支持的语言: {lang}")
|
||||
|
||||
return errors
|
||||
|
||||
def is_valid_version_spec(spec: str) -> bool:
|
||||
"""验证版本规格字符串
|
||||
|
||||
Args:
|
||||
spec: 版本规格(如 ">=3.10")
|
||||
|
||||
Returns:
|
||||
是否有效
|
||||
"""
|
||||
import re
|
||||
# 简化的版本规格验证
|
||||
pattern = r'^(>=|<=|>|<|==)?\d+\.\d+(\.\d+)?$'
|
||||
return bool(re.match(pattern, spec))
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、默认配置生成
|
||||
|
||||
### 4.1 生成逻辑
|
||||
|
||||
`aide init` 命令应该生成包含注释的默认配置:
|
||||
|
||||
```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.2 配置文件创建流程
|
||||
|
||||
```python
|
||||
from pathlib import Path
|
||||
|
||||
def create_config_file(project_root: Path) -> bool:
|
||||
"""创建配置文件
|
||||
|
||||
Args:
|
||||
project_root: 项目根目录
|
||||
|
||||
Returns:
|
||||
是否创建成功(False 表示文件已存在)
|
||||
"""
|
||||
aide_dir = project_root / ".aide"
|
||||
config_path = aide_dir / "config.toml"
|
||||
|
||||
# 检查是否已存在
|
||||
if config_path.exists():
|
||||
return False
|
||||
|
||||
# 确保 .aide 目录存在
|
||||
aide_dir.mkdir(exist_ok=True)
|
||||
|
||||
# 写入默认配置
|
||||
config_content = generate_default_config()
|
||||
config_path.write_text(config_content, encoding="utf-8")
|
||||
|
||||
return True
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、配置访问接口
|
||||
|
||||
### 5.1 Config 类设计
|
||||
|
||||
```python
|
||||
from pathlib import Path
|
||||
from typing import Any, Optional
|
||||
|
||||
class Config:
|
||||
"""配置管理类"""
|
||||
|
||||
def __init__(self, project_root: Path):
|
||||
"""初始化配置
|
||||
|
||||
Args:
|
||||
project_root: 项目根目录
|
||||
"""
|
||||
self.project_root = project_root
|
||||
self.config_path = project_root / ".aide" / "config.toml"
|
||||
self._config: Optional[dict] = None
|
||||
|
||||
def load(self) -> None:
|
||||
"""加载配置文件"""
|
||||
if not self.config_path.exists():
|
||||
raise FileNotFoundError(f"配置文件不存在: {self.config_path}")
|
||||
|
||||
with open(self.config_path, "rb") as f:
|
||||
self._config = tomllib.load(f)
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
"""获取配置值
|
||||
|
||||
Args:
|
||||
key: 配置键(支持点号分隔)
|
||||
default: 默认值
|
||||
|
||||
Returns:
|
||||
配置值
|
||||
"""
|
||||
if self._config is None:
|
||||
self.load()
|
||||
|
||||
return get_config_value(self._config, key, default)
|
||||
|
||||
def set(self, key: str, value: Any) -> None:
|
||||
"""设置配置值
|
||||
|
||||
Args:
|
||||
key: 配置键(支持点号分隔)
|
||||
value: 配置值
|
||||
"""
|
||||
if self._config is None:
|
||||
self.load()
|
||||
|
||||
set_config_value(self._config, key, value)
|
||||
self.save()
|
||||
|
||||
def save(self) -> None:
|
||||
"""保存配置文件"""
|
||||
if self._config is None:
|
||||
return
|
||||
|
||||
with open(self.config_path, "wb") as f:
|
||||
tomli_w.dump(self._config, f)
|
||||
|
||||
def validate(self) -> list[str]:
|
||||
"""验证配置
|
||||
|
||||
Returns:
|
||||
错误列表
|
||||
"""
|
||||
if self._config is None:
|
||||
self.load()
|
||||
|
||||
return validate_config(self._config)
|
||||
```
|
||||
|
||||
### 5.2 使用示例
|
||||
|
||||
```python
|
||||
# 初始化配置
|
||||
config = Config(Path.cwd())
|
||||
|
||||
# 获取配置值
|
||||
task_source = config.get("task.source", "task-now.md")
|
||||
python_version = config.get("env.python.version", ">=3.10")
|
||||
|
||||
# 设置配置值
|
||||
config.set("task.source", "new-task.md")
|
||||
config.set("env.python.version", ">=3.11")
|
||||
|
||||
# 验证配置
|
||||
errors = config.validate()
|
||||
if errors:
|
||||
for error in errors:
|
||||
print(f"✗ 配置错误: {error}")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、配置迁移
|
||||
|
||||
### 6.1 版本兼容性
|
||||
|
||||
当配置文件格式升级时,需要提供迁移机制:
|
||||
|
||||
```python
|
||||
def migrate_config(config: dict, from_version: str, to_version: str) -> dict:
|
||||
"""迁移配置文件
|
||||
|
||||
Args:
|
||||
config: 旧配置
|
||||
from_version: 源版本
|
||||
to_version: 目标版本
|
||||
|
||||
Returns:
|
||||
新配置
|
||||
"""
|
||||
if from_version == "1.0" and to_version == "1.1":
|
||||
# 示例:添加新的配置项
|
||||
if "output" not in config:
|
||||
config["output"] = {
|
||||
"color": True,
|
||||
"language": "zh-CN"
|
||||
}
|
||||
|
||||
return config
|
||||
```
|
||||
|
||||
### 6.2 配置备份
|
||||
|
||||
修改配置前应该备份:
|
||||
|
||||
```python
|
||||
import shutil
|
||||
from datetime import datetime
|
||||
|
||||
def backup_config(config_path: Path) -> Path:
|
||||
"""备份配置文件
|
||||
|
||||
Args:
|
||||
config_path: 配置文件路径
|
||||
|
||||
Returns:
|
||||
备份文件路径
|
||||
"""
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
backup_path = config_path.with_suffix(f".toml.backup.{timestamp}")
|
||||
shutil.copy2(config_path, backup_path)
|
||||
return backup_path
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 七、错误处理
|
||||
|
||||
### 7.1 常见错误
|
||||
|
||||
| 错误类型 | 处理方式 |
|
||||
|---------|---------|
|
||||
| 配置文件不存在 | 提示使用 `aide init` 创建 |
|
||||
| 配置文件格式错误 | 显示具体错误位置和原因 |
|
||||
| 配置项缺失 | 使用默认值并警告 |
|
||||
| 配置值类型错误 | 显示期望类型和实际类型 |
|
||||
|
||||
### 7.2 错误信息示例
|
||||
|
||||
```python
|
||||
def handle_config_error(error: Exception, config_path: Path) -> None:
|
||||
"""处理配置错误
|
||||
|
||||
Args:
|
||||
error: 异常对象
|
||||
config_path: 配置文件路径
|
||||
"""
|
||||
if isinstance(error, FileNotFoundError):
|
||||
err(
|
||||
"配置文件不存在",
|
||||
[
|
||||
f"位置: {config_path}",
|
||||
"建议: 运行 'aide init' 创建配置文件"
|
||||
]
|
||||
)
|
||||
elif isinstance(error, tomllib.TOMLDecodeError):
|
||||
err(
|
||||
f"配置文件格式错误 (第{error.lineno}行)",
|
||||
[
|
||||
f"位置: {config_path}:{error.lineno}",
|
||||
f"原因: {error.msg}",
|
||||
"建议: 检查 TOML 语法,确保格式正确"
|
||||
]
|
||||
)
|
||||
else:
|
||||
err(
|
||||
"配置文件读取失败",
|
||||
[
|
||||
f"位置: {config_path}",
|
||||
f"原因: {str(error)}"
|
||||
]
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 八、测试要求
|
||||
|
||||
### 8.1 测试用例
|
||||
|
||||
```python
|
||||
def test_load_config():
|
||||
"""测试加载配置"""
|
||||
# 创建测试配置
|
||||
# 加载配置
|
||||
# 验证配置内容
|
||||
|
||||
def test_get_config_value():
|
||||
"""测试获取配置值"""
|
||||
# 测试简单键
|
||||
# 测试嵌套键
|
||||
# 测试不存在的键
|
||||
# 测试默认值
|
||||
|
||||
def test_set_config_value():
|
||||
"""测试设置配置值"""
|
||||
# 测试设置简单键
|
||||
# 测试设置嵌套键
|
||||
# 测试创建新键
|
||||
|
||||
def test_validate_config():
|
||||
"""测试配置验证"""
|
||||
# 测试有效配置
|
||||
# 测试缺少必需项
|
||||
# 测试无效值类型
|
||||
# 测试无效版本格式
|
||||
|
||||
def test_config_migration():
|
||||
"""测试配置迁移"""
|
||||
# 测试版本升级
|
||||
# 测试向后兼容
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 九、总结
|
||||
|
||||
### 9.1 核心要点
|
||||
|
||||
1. 使用 TOML 格式,自文档化
|
||||
2. 提供合理的默认值
|
||||
3. 支持点号分隔的键访问
|
||||
4. 完善的错误处理和验证
|
||||
5. 配置迁移和备份机制
|
||||
|
||||
### 9.2 实现检查清单
|
||||
|
||||
- [ ] 实现配置文件读取(load_config)
|
||||
- [ ] 实现配置文件写入(save_config)
|
||||
- [ ] 实现配置值获取(get_config_value)
|
||||
- [ ] 实现配置值设置(set_config_value)
|
||||
- [ ] 实现配置验证(validate_config)
|
||||
- [ ] 实现默认配置生成(generate_default_config)
|
||||
- [ ] 实现 Config 类
|
||||
- [ ] 编写配置测试用例
|
||||
- [ ] 验证 TOML 格式正确性
|
||||
|
||||
---
|
||||
|
||||
**版本**:v1.0
|
||||
**更新日期**:2025-12-13
|
||||
@@ -1,428 +0,0 @@
|
||||
# 输出格式规范
|
||||
|
||||
## 一、设计原则
|
||||
|
||||
### 1.1 核心原则
|
||||
|
||||
1. **精简优先**:成功时输出极简,失败时输出详细
|
||||
2. **一致性**:所有命令使用统一的输出格式
|
||||
3. **可解析性**:输出格式便于程序解析(如需要)
|
||||
4. **用户友好**:错误信息包含解决建议
|
||||
|
||||
### 1.2 静默原则
|
||||
|
||||
**无输出 = 正常完成**
|
||||
|
||||
适用于幂等操作,例如:
|
||||
- 配置项已经是目标值时,`aide config set` 不输出
|
||||
- 目录已存在时,创建目录操作不输出
|
||||
|
||||
---
|
||||
|
||||
## 二、输出前缀
|
||||
|
||||
### 2.1 前缀定义
|
||||
|
||||
所有输出行必须以以下前缀之一开头:
|
||||
|
||||
| 前缀 | Unicode | 含义 | 颜色(可选) | 退出码 |
|
||||
|------|---------|------|-------------|--------|
|
||||
| `✓` | U+2713 | 成功 | 绿色 | 0 |
|
||||
| `⚠` | U+26A0 | 警告(可继续) | 黄色 | 0 |
|
||||
| `✗` | U+2717 | 错误(失败) | 红色 | 非0 |
|
||||
| `→` | U+2192 | 信息/进行中 | 蓝色 | - |
|
||||
|
||||
### 2.2 使用场景
|
||||
|
||||
**✓ 成功**
|
||||
```
|
||||
✓ 环境就绪 (python:3.12)
|
||||
✓ 已创建 .aide/ 目录
|
||||
✓ 已生成默认配置
|
||||
```
|
||||
|
||||
**⚠ 警告**
|
||||
```
|
||||
⚠ 已修复: 创建虚拟环境 .venv
|
||||
⚠ 配置文件不存在,使用默认配置
|
||||
⚠ Python 版本较旧 (3.10),建议升级到 3.11+
|
||||
```
|
||||
|
||||
**✗ 错误**
|
||||
```
|
||||
✗ Python 版本不满足要求 (需要 >=3.10, 当前 3.8)
|
||||
建议: 安装 Python 3.10+ 或使用 pyenv 管理版本
|
||||
✗ 配置文件格式错误: 第5行缺少引号
|
||||
位置: .aide/config.toml:5
|
||||
```
|
||||
|
||||
**→ 信息**
|
||||
```
|
||||
→ 正在检测环境...
|
||||
→ 正在创建配置文件...
|
||||
→ 配置值: task.source = "task-now.md"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、输出格式
|
||||
|
||||
### 3.1 单行输出
|
||||
|
||||
**格式**:`<前缀> <消息>`
|
||||
|
||||
**示例**:
|
||||
```
|
||||
✓ 环境就绪 (python:3.12)
|
||||
⚠ 已修复: 创建虚拟环境 .venv
|
||||
✗ Python 未安装
|
||||
```
|
||||
|
||||
### 3.2 多行输出
|
||||
|
||||
**格式**:
|
||||
```
|
||||
<前缀> <主消息>
|
||||
<详细信息行1>
|
||||
<详细信息行2>
|
||||
```
|
||||
|
||||
**缩进规则**:
|
||||
- 主消息行:前缀 + 空格 + 消息
|
||||
- 详细信息行:2个空格缩进
|
||||
|
||||
**示例**:
|
||||
```
|
||||
✗ Python 版本不满足要求 (需要 >=3.10, 当前 3.8)
|
||||
建议: 安装 Python 3.10+ 或使用 pyenv 管理版本
|
||||
文档: https://docs.python.org/3/using/index.html
|
||||
```
|
||||
|
||||
### 3.3 列表输出
|
||||
|
||||
**格式**:
|
||||
```
|
||||
<前缀> <标题>
|
||||
- <项目1>
|
||||
- <项目2>
|
||||
```
|
||||
|
||||
**示例**:
|
||||
```
|
||||
✓ 环境就绪
|
||||
- python: 3.12.0
|
||||
- uv: 0.4.0
|
||||
- git: 2.40.0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、特殊场景
|
||||
|
||||
### 4.1 配置输出
|
||||
|
||||
**aide config get**
|
||||
|
||||
单个值:
|
||||
```
|
||||
task.source = "task-now.md"
|
||||
```
|
||||
|
||||
多个值(如果支持):
|
||||
```
|
||||
task.source = "task-now.md"
|
||||
task.spec = "task-spec.md"
|
||||
```
|
||||
|
||||
数组值:
|
||||
```
|
||||
flow.phases = ["flow-design", "impl", "verify", "docs", "finish"]
|
||||
```
|
||||
|
||||
**aide config set**
|
||||
|
||||
成功时无输出(静默原则)
|
||||
|
||||
失败时:
|
||||
```
|
||||
✗ 配置键不存在: invalid.key
|
||||
可用的配置键: task.source, task.spec, env.python.version
|
||||
```
|
||||
|
||||
### 4.2 环境检测输出
|
||||
|
||||
**成功(无问题)**:
|
||||
```
|
||||
✓ 环境就绪 (python:3.12)
|
||||
```
|
||||
|
||||
**成功(自动修复)**:
|
||||
```
|
||||
⚠ 已修复: 创建虚拟环境 .venv
|
||||
✓ 环境就绪 (python:3.12)
|
||||
```
|
||||
|
||||
**失败(无法修复)**:
|
||||
```
|
||||
✗ Python 版本不满足要求 (需要 >=3.10, 当前 3.8)
|
||||
建议: 安装 Python 3.10+ 或使用 pyenv 管理版本
|
||||
```
|
||||
|
||||
### 4.3 初始化输出
|
||||
|
||||
**首次初始化**:
|
||||
```
|
||||
✓ 已创建 .aide/ 目录
|
||||
✓ 已生成默认配置
|
||||
✓ 已添加 .aide/ 到 .gitignore
|
||||
```
|
||||
|
||||
**重复初始化**:
|
||||
```
|
||||
⚠ .aide/ 目录已存在
|
||||
⚠ 配置文件已存在,跳过生成
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、实现规范
|
||||
|
||||
### 5.1 输出函数接口
|
||||
|
||||
建议在 `core/output.py` 中实现以下函数:
|
||||
|
||||
```python
|
||||
def ok(message: str, details: list[str] | None = None) -> None:
|
||||
"""输出成功信息
|
||||
|
||||
Args:
|
||||
message: 主消息
|
||||
details: 详细信息列表(可选)
|
||||
"""
|
||||
pass
|
||||
|
||||
def warn(message: str, details: list[str] | None = None) -> None:
|
||||
"""输出警告信息
|
||||
|
||||
Args:
|
||||
message: 主消息
|
||||
details: 详细信息列表(可选)
|
||||
"""
|
||||
pass
|
||||
|
||||
def err(message: str, details: list[str] | None = None) -> None:
|
||||
"""输出错误信息
|
||||
|
||||
Args:
|
||||
message: 主消息
|
||||
details: 详细信息列表(可选)
|
||||
"""
|
||||
pass
|
||||
|
||||
def info(message: str, details: list[str] | None = None) -> None:
|
||||
"""输出信息
|
||||
|
||||
Args:
|
||||
message: 主消息
|
||||
details: 详细信息列表(可选)
|
||||
"""
|
||||
pass
|
||||
```
|
||||
|
||||
### 5.2 使用示例
|
||||
|
||||
```python
|
||||
from core.output import ok, warn, err, info
|
||||
|
||||
# 简单成功
|
||||
ok("环境就绪 (python:3.12)")
|
||||
|
||||
# 带详细信息的警告
|
||||
warn("已修复: 创建虚拟环境 .venv")
|
||||
|
||||
# 带建议的错误
|
||||
err(
|
||||
"Python 版本不满足要求 (需要 >=3.10, 当前 3.8)",
|
||||
[
|
||||
"建议: 安装 Python 3.10+ 或使用 pyenv 管理版本",
|
||||
"文档: https://docs.python.org/3/using/index.html"
|
||||
]
|
||||
)
|
||||
|
||||
# 进行中信息
|
||||
info("正在检测环境...")
|
||||
```
|
||||
|
||||
### 5.3 颜色支持(可选)
|
||||
|
||||
如果实现颜色输出,必须:
|
||||
|
||||
1. **检测终端支持**:使用 `sys.stdout.isatty()` 检测
|
||||
2. **提供禁用选项**:通过环境变量 `NO_COLOR` 或 `--no-color` 参数
|
||||
3. **跨平台兼容**:Windows 需要特殊处理(colorama 或 ANSI 转义)
|
||||
|
||||
**颜色映射**:
|
||||
- ✓ 成功:绿色(`\033[32m`)
|
||||
- ⚠ 警告:黄色(`\033[33m`)
|
||||
- ✗ 错误:红色(`\033[31m`)
|
||||
- → 信息:蓝色(`\033[34m`)
|
||||
|
||||
---
|
||||
|
||||
## 六、错误信息规范
|
||||
|
||||
### 6.1 错误信息结构
|
||||
|
||||
```
|
||||
✗ <问题描述> (<具体原因>)
|
||||
<解决建议>
|
||||
<相关信息>
|
||||
```
|
||||
|
||||
### 6.2 错误信息要素
|
||||
|
||||
1. **问题描述**:清晰说明出了什么问题
|
||||
2. **具体原因**:括号内说明具体原因(如版本号、路径等)
|
||||
3. **解决建议**:以"建议:"开头,提供可行的解决方案
|
||||
4. **相关信息**:文档链接、配置位置等
|
||||
|
||||
### 6.3 错误信息示例
|
||||
|
||||
**环境错误**:
|
||||
```
|
||||
✗ Python 版本不满足要求 (需要 >=3.10, 当前 3.8)
|
||||
建议: 安装 Python 3.10+ 或使用 pyenv 管理版本
|
||||
```
|
||||
|
||||
**配置错误**:
|
||||
```
|
||||
✗ 配置文件格式错误 (第5行: 缺少引号)
|
||||
位置: .aide/config.toml:5
|
||||
建议: 检查 TOML 语法,确保字符串值使用引号
|
||||
```
|
||||
|
||||
**文件错误**:
|
||||
```
|
||||
✗ 任务文档不存在 (task-now.md)
|
||||
建议: 创建任务文档或使用 --file 参数指定路径
|
||||
```
|
||||
|
||||
**权限错误**:
|
||||
```
|
||||
✗ 无法创建目录 (.aide/)
|
||||
原因: 权限不足
|
||||
建议: 检查当前目录的写入权限
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 七、退出码规范
|
||||
|
||||
### 7.1 退出码定义
|
||||
|
||||
| 退出码 | 含义 | 使用场景 |
|
||||
|-------|------|---------|
|
||||
| 0 | 成功 | 操作成功完成 |
|
||||
| 1 | 一般错误 | 操作失败,原因不明确 |
|
||||
| 2 | 参数错误 | 命令参数错误或缺失 |
|
||||
| 3 | 环境错误 | 环境不满足要求 |
|
||||
| 4 | 配置错误 | 配置文件错误 |
|
||||
| 5 | 文件错误 | 文件不存在或无法访问 |
|
||||
|
||||
### 7.2 退出码使用
|
||||
|
||||
```python
|
||||
import sys
|
||||
|
||||
# 成功
|
||||
sys.exit(0)
|
||||
|
||||
# 参数错误
|
||||
sys.exit(2)
|
||||
|
||||
# 环境错误
|
||||
sys.exit(3)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 八、测试要求
|
||||
|
||||
### 8.1 输出测试
|
||||
|
||||
每个命令必须测试:
|
||||
|
||||
1. **成功场景**:验证输出格式正确
|
||||
2. **警告场景**:验证警告信息正确
|
||||
3. **错误场景**:验证错误信息和退出码
|
||||
4. **静默场景**:验证幂等操作无输出
|
||||
|
||||
### 8.2 测试示例
|
||||
|
||||
```python
|
||||
def test_env_ensure_success(capsys):
|
||||
"""测试环境检测成功输出"""
|
||||
# 执行命令
|
||||
result = env_ensure()
|
||||
|
||||
# 验证退出码
|
||||
assert result == 0
|
||||
|
||||
# 验证输出
|
||||
captured = capsys.readouterr()
|
||||
assert "✓ 环境就绪" in captured.out
|
||||
assert "python:" in captured.out
|
||||
|
||||
def test_env_ensure_failure(capsys):
|
||||
"""测试环境检测失败输出"""
|
||||
# 模拟 Python 版本过低
|
||||
with mock_python_version("3.8"):
|
||||
result = env_ensure()
|
||||
|
||||
# 验证退出码
|
||||
assert result == 3
|
||||
|
||||
# 验证输出
|
||||
captured = capsys.readouterr()
|
||||
assert "✗ Python 版本不满足要求" in captured.out
|
||||
assert "建议:" in captured.out
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 九、国际化考虑(可选)
|
||||
|
||||
如果需要支持多语言:
|
||||
|
||||
1. **消息模板**:将所有消息定义为模板
|
||||
2. **语言检测**:通过环境变量 `LANG` 或 `--lang` 参数
|
||||
3. **回退机制**:不支持的语言回退到英文
|
||||
|
||||
**当前阶段**:仅支持简体中文,国际化可在后续版本实现。
|
||||
|
||||
---
|
||||
|
||||
## 十、总结
|
||||
|
||||
### 10.1 核心要点
|
||||
|
||||
1. 使用统一的前缀(✓ ⚠ ✗ →)
|
||||
2. 成功时输出极简,失败时输出详细
|
||||
3. 错误信息包含解决建议
|
||||
4. 遵循静默原则(幂等操作)
|
||||
5. 使用明确的退出码
|
||||
|
||||
### 10.2 实现检查清单
|
||||
|
||||
- [ ] 实现 `core/output.py` 模块
|
||||
- [ ] 定义 ok/warn/err/info 函数
|
||||
- [ ] 支持单行和多行输出
|
||||
- [ ] 实现颜色支持(可选)
|
||||
- [ ] 定义退出码常量
|
||||
- [ ] 编写输出测试用例
|
||||
- [ ] 验证跨平台兼容性
|
||||
|
||||
---
|
||||
|
||||
**版本**:v1.0
|
||||
**更新日期**:2025-12-13
|
||||
@@ -1,708 +0,0 @@
|
||||
# 测试规范
|
||||
|
||||
## 一、测试概述
|
||||
|
||||
### 1.1 测试目标
|
||||
|
||||
确保 aide 程序的所有功能正确、稳定、可靠,满足设计要求。
|
||||
|
||||
### 1.2 测试范围
|
||||
|
||||
- 单元测试:核心模块和函数
|
||||
- 集成测试:命令完整流程
|
||||
- 跨平台测试:Linux、macOS、Windows
|
||||
- 性能测试:执行时间和资源占用
|
||||
|
||||
### 1.3 测试工具
|
||||
|
||||
- **pytest**:测试框架
|
||||
- **pytest-cov**:代码覆盖率
|
||||
- **pytest-mock**:模拟和打桩
|
||||
- **pytest-timeout**:超时控制
|
||||
|
||||
---
|
||||
|
||||
## 二、测试结构
|
||||
|
||||
### 2.1 目录结构
|
||||
|
||||
```
|
||||
tests/
|
||||
├── __init__.py
|
||||
├── conftest.py # pytest 配置和 fixtures
|
||||
├── unit/ # 单元测试
|
||||
│ ├── __init__.py
|
||||
│ ├── test_output.py # 输出模块测试
|
||||
│ ├── test_config.py # 配置模块测试
|
||||
│ └── test_validators.py # 验证函数测试
|
||||
├── integration/ # 集成测试
|
||||
│ ├── __init__.py
|
||||
│ ├── test_init.py # aide init 测试
|
||||
│ ├── test_env.py # aide env 测试
|
||||
│ └── test_config_cmd.py # aide config 测试
|
||||
└── fixtures/ # 测试数据
|
||||
├── sample_config.toml
|
||||
└── sample_gitignore
|
||||
```
|
||||
|
||||
### 2.2 命名规范
|
||||
|
||||
- 测试文件:`test_<module>.py`
|
||||
- 测试类:`Test<Feature>`
|
||||
- 测试函数:`test_<scenario>`
|
||||
|
||||
**示例**:
|
||||
```python
|
||||
# test_config.py
|
||||
class TestConfig:
|
||||
def test_load_success(self):
|
||||
"""测试成功加载配置"""
|
||||
pass
|
||||
|
||||
def test_load_file_not_found(self):
|
||||
"""测试配置文件不存在"""
|
||||
pass
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、单元测试
|
||||
|
||||
### 3.1 core/output.py 测试
|
||||
|
||||
```python
|
||||
# tests/unit/test_output.py
|
||||
import pytest
|
||||
from core.output import ok, warn, err, info
|
||||
|
||||
def test_ok_simple(capsys):
|
||||
"""测试简单成功输出"""
|
||||
ok("操作成功")
|
||||
|
||||
captured = capsys.readouterr()
|
||||
assert "✓ 操作成功" in captured.out
|
||||
|
||||
def test_ok_with_details(capsys):
|
||||
"""测试带详细信息的成功输出"""
|
||||
ok("操作成功", ["详细信息1", "详细信息2"])
|
||||
|
||||
captured = capsys.readouterr()
|
||||
assert "✓ 操作成功" in captured.out
|
||||
assert " 详细信息1" in captured.out
|
||||
assert " 详细信息2" in captured.out
|
||||
|
||||
def test_warn(capsys):
|
||||
"""测试警告输出"""
|
||||
warn("这是警告")
|
||||
|
||||
captured = capsys.readouterr()
|
||||
assert "⚠ 这是警告" in captured.out
|
||||
|
||||
def test_err(capsys):
|
||||
"""测试错误输出"""
|
||||
err("这是错误", ["建议: 检查配置"])
|
||||
|
||||
captured = capsys.readouterr()
|
||||
assert "✗ 这是错误" in captured.out
|
||||
assert " 建议: 检查配置" in captured.out
|
||||
|
||||
def test_info(capsys):
|
||||
"""测试信息输出"""
|
||||
info("正在处理...")
|
||||
|
||||
captured = capsys.readouterr()
|
||||
assert "→ 正在处理..." in captured.out
|
||||
```
|
||||
|
||||
### 3.2 core/config.py 测试
|
||||
|
||||
```python
|
||||
# tests/unit/test_config.py
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
from core.config import Config, get_config_value, set_config_value
|
||||
|
||||
def test_get_config_value():
|
||||
"""测试获取配置值"""
|
||||
config = {
|
||||
"task": {
|
||||
"source": "task-now.md"
|
||||
}
|
||||
}
|
||||
|
||||
assert get_config_value(config, "task.source") == "task-now.md"
|
||||
assert get_config_value(config, "task.spec", "default") == "default"
|
||||
|
||||
def test_set_config_value():
|
||||
"""测试设置配置值"""
|
||||
config = {"task": {}}
|
||||
|
||||
set_config_value(config, "task.source", "new-task.md")
|
||||
assert config["task"]["source"] == "new-task.md"
|
||||
|
||||
def test_config_load(tmp_path):
|
||||
"""测试加载配置"""
|
||||
# 创建测试配置文件
|
||||
config_dir = tmp_path / ".aide"
|
||||
config_dir.mkdir()
|
||||
config_path = config_dir / "config.toml"
|
||||
config_path.write_text('[task]\nsource = "test.md"\n')
|
||||
|
||||
# 加载配置
|
||||
config = Config(tmp_path)
|
||||
config.load()
|
||||
|
||||
assert config.get("task.source") == "test.md"
|
||||
|
||||
def test_config_load_not_found(tmp_path):
|
||||
"""测试配置文件不存在"""
|
||||
config = Config(tmp_path)
|
||||
|
||||
with pytest.raises(FileNotFoundError):
|
||||
config.load()
|
||||
|
||||
def test_config_save(tmp_path):
|
||||
"""测试保存配置"""
|
||||
# 创建配置
|
||||
config_dir = tmp_path / ".aide"
|
||||
config_dir.mkdir()
|
||||
|
||||
config = Config(tmp_path)
|
||||
config._config = {"task": {"source": "test.md"}}
|
||||
config.save()
|
||||
|
||||
# 验证文件已创建
|
||||
config_path = tmp_path / ".aide" / "config.toml"
|
||||
assert config_path.exists()
|
||||
|
||||
# 验证内容
|
||||
content = config_path.read_text()
|
||||
assert "task" in content
|
||||
assert "test.md" in content
|
||||
```
|
||||
|
||||
### 3.3 utils/validators.py 测试
|
||||
|
||||
```python
|
||||
# tests/unit/test_validators.py
|
||||
import pytest
|
||||
from utils.validators import is_valid_version_spec, validate_config
|
||||
|
||||
def test_is_valid_version_spec():
|
||||
"""测试版本规格验证"""
|
||||
assert is_valid_version_spec(">=3.10") == True
|
||||
assert is_valid_version_spec("3.12") == True
|
||||
assert is_valid_version_spec(">=3.10,<4.0") == True
|
||||
assert is_valid_version_spec("invalid") == False
|
||||
|
||||
def test_validate_config():
|
||||
"""测试配置验证"""
|
||||
# 有效配置
|
||||
valid_config = {
|
||||
"task": {"source": "task.md", "spec": "spec.md"},
|
||||
"env": {"python": {"version": ">=3.10"}},
|
||||
"output": {"language": "zh-CN"}
|
||||
}
|
||||
errors = validate_config(valid_config)
|
||||
assert len(errors) == 0
|
||||
|
||||
# 缺少必需项
|
||||
invalid_config = {"task": {}}
|
||||
errors = validate_config(invalid_config)
|
||||
assert len(errors) > 0
|
||||
assert any("task.source" in e for e in errors)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、集成测试
|
||||
|
||||
### 4.1 aide init 测试
|
||||
|
||||
```python
|
||||
# tests/integration/test_init.py
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
from commands.init import cmd_init
|
||||
|
||||
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" / "config.toml").exists()
|
||||
assert (tmp_path / ".gitignore").exists()
|
||||
|
||||
def test_init_idempotent(tmp_path, monkeypatch):
|
||||
"""测试幂等性"""
|
||||
monkeypatch.chdir(tmp_path)
|
||||
|
||||
# 第一次初始化
|
||||
result1 = cmd_init([])
|
||||
assert result1 == 0
|
||||
|
||||
# 读取配置内容
|
||||
config_path = tmp_path / ".aide" / "config.toml"
|
||||
content1 = config_path.read_text()
|
||||
|
||||
# 第二次初始化
|
||||
result2 = cmd_init([])
|
||||
assert result2 == 0
|
||||
|
||||
# 验证配置未改变
|
||||
content2 = config_path.read_text()
|
||||
assert content1 == content2
|
||||
|
||||
def test_init_permission_error(tmp_path, monkeypatch):
|
||||
"""测试权限错误"""
|
||||
monkeypatch.chdir(tmp_path)
|
||||
|
||||
# 设置只读权限
|
||||
tmp_path.chmod(0o444)
|
||||
|
||||
result = cmd_init([])
|
||||
|
||||
# 恢复权限
|
||||
tmp_path.chmod(0o755)
|
||||
|
||||
assert result != 0
|
||||
```
|
||||
|
||||
### 4.2 aide env 测试
|
||||
|
||||
```python
|
||||
# tests/integration/test_env.py
|
||||
import pytest
|
||||
from commands.env import cmd_env, env_ensure_runtime, env_ensure_project
|
||||
|
||||
def test_env_ensure_runtime_success():
|
||||
"""测试运行时环境检测成功"""
|
||||
result = env_ensure_runtime()
|
||||
assert result == 0
|
||||
|
||||
def test_env_ensure_project_success(tmp_path, monkeypatch):
|
||||
"""测试项目环境检测成功"""
|
||||
monkeypatch.chdir(tmp_path)
|
||||
|
||||
# 初始化项目
|
||||
from commands.init import cmd_init
|
||||
cmd_init([])
|
||||
|
||||
# 检测环境
|
||||
result = env_ensure_project()
|
||||
assert result == 0
|
||||
|
||||
def test_env_ensure_project_no_config(tmp_path, monkeypatch):
|
||||
"""测试配置文件不存在"""
|
||||
monkeypatch.chdir(tmp_path)
|
||||
|
||||
result = env_ensure_project()
|
||||
assert result == 4
|
||||
|
||||
def test_env_ensure_create_venv(tmp_path, monkeypatch):
|
||||
"""测试自动创建虚拟环境"""
|
||||
monkeypatch.chdir(tmp_path)
|
||||
|
||||
# 初始化项目
|
||||
from commands.init import cmd_init
|
||||
cmd_init([])
|
||||
|
||||
# 检测环境(应该自动创建虚拟环境)
|
||||
result = env_ensure_project()
|
||||
assert result == 0
|
||||
|
||||
# 验证虚拟环境已创建
|
||||
assert (tmp_path / ".venv").exists()
|
||||
```
|
||||
|
||||
### 4.3 aide config 测试
|
||||
|
||||
```python
|
||||
# tests/integration/test_config_cmd.py
|
||||
import pytest
|
||||
from commands.config_cmd import cmd_config, config_get, config_set
|
||||
|
||||
def test_config_get_success(tmp_path, monkeypatch, capsys):
|
||||
"""测试获取配置成功"""
|
||||
monkeypatch.chdir(tmp_path)
|
||||
|
||||
# 初始化
|
||||
from commands.init import cmd_init
|
||||
cmd_init([])
|
||||
|
||||
# 获取配置
|
||||
result = config_get("task.source")
|
||||
assert result == 0
|
||||
|
||||
captured = capsys.readouterr()
|
||||
assert "task.source" in captured.out
|
||||
assert "task-now.md" in captured.out
|
||||
|
||||
def test_config_set_success(tmp_path, monkeypatch):
|
||||
"""测试设置配置成功"""
|
||||
monkeypatch.chdir(tmp_path)
|
||||
|
||||
# 初始化
|
||||
from commands.init import cmd_init
|
||||
cmd_init([])
|
||||
|
||||
# 设置配置
|
||||
result = config_set("task.source", "new-task.md")
|
||||
assert result == 0
|
||||
|
||||
# 验证配置已更新
|
||||
result = config_get("task.source")
|
||||
assert result == 0
|
||||
|
||||
def test_config_cmd_no_args(capsys):
|
||||
"""测试无参数"""
|
||||
result = cmd_config([])
|
||||
assert result == 2
|
||||
|
||||
captured = capsys.readouterr()
|
||||
assert "缺少子命令" in captured.out
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、跨平台测试
|
||||
|
||||
### 5.1 路径处理测试
|
||||
|
||||
```python
|
||||
def test_path_handling_cross_platform(tmp_path):
|
||||
"""测试跨平台路径处理"""
|
||||
from pathlib import Path
|
||||
|
||||
# 使用 pathlib 确保跨平台兼容
|
||||
aide_dir = tmp_path / ".aide"
|
||||
config_path = aide_dir / "config.toml"
|
||||
|
||||
aide_dir.mkdir()
|
||||
config_path.write_text("test")
|
||||
|
||||
assert config_path.exists()
|
||||
assert config_path.is_file()
|
||||
```
|
||||
|
||||
### 5.2 命令执行测试
|
||||
|
||||
```python
|
||||
@pytest.mark.skipif(sys.platform == "win32", reason="Unix only")
|
||||
def test_unix_specific():
|
||||
"""Unix 特定测试"""
|
||||
pass
|
||||
|
||||
@pytest.mark.skipif(sys.platform != "win32", reason="Windows only")
|
||||
def test_windows_specific():
|
||||
"""Windows 特定测试"""
|
||||
pass
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、性能测试
|
||||
|
||||
### 6.1 执行时间测试
|
||||
|
||||
```python
|
||||
import time
|
||||
|
||||
def test_init_performance(tmp_path, monkeypatch):
|
||||
"""测试初始化性能"""
|
||||
monkeypatch.chdir(tmp_path)
|
||||
|
||||
start = time.time()
|
||||
result = cmd_init([])
|
||||
elapsed = time.time() - start
|
||||
|
||||
assert result == 0
|
||||
assert elapsed < 0.2 # 应该在 200ms 内完成
|
||||
|
||||
def test_config_get_performance(tmp_path, monkeypatch):
|
||||
"""测试配置读取性能"""
|
||||
monkeypatch.chdir(tmp_path)
|
||||
|
||||
# 初始化
|
||||
cmd_init([])
|
||||
|
||||
# 测试性能
|
||||
start = time.time()
|
||||
result = config_get("task.source")
|
||||
elapsed = time.time() - start
|
||||
|
||||
assert result == 0
|
||||
assert elapsed < 0.1 # 应该在 100ms 内完成
|
||||
```
|
||||
|
||||
### 6.2 内存占用测试
|
||||
|
||||
```python
|
||||
import psutil
|
||||
import os
|
||||
|
||||
def test_memory_usage():
|
||||
"""测试内存占用"""
|
||||
process = psutil.Process(os.getpid())
|
||||
mem_before = process.memory_info().rss / 1024 / 1024 # MB
|
||||
|
||||
# 执行操作
|
||||
cmd_init([])
|
||||
|
||||
mem_after = process.memory_info().rss / 1024 / 1024 # MB
|
||||
mem_used = mem_after - mem_before
|
||||
|
||||
assert mem_used < 50 # 应该小于 50MB
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 七、测试覆盖率
|
||||
|
||||
### 7.1 覆盖率要求
|
||||
|
||||
- **核心模块**:≥ 80%
|
||||
- **命令模块**:≥ 80%
|
||||
- **工具模块**:≥ 70%
|
||||
- **整体**:≥ 75%
|
||||
|
||||
### 7.2 运行覆盖率测试
|
||||
|
||||
```bash
|
||||
# 运行测试并生成覆盖率报告
|
||||
pytest --cov=src --cov-report=html --cov-report=term
|
||||
|
||||
# 查看覆盖率报告
|
||||
open htmlcov/index.html
|
||||
```
|
||||
|
||||
### 7.3 覆盖率配置
|
||||
|
||||
```ini
|
||||
# .coveragerc
|
||||
[run]
|
||||
source = src
|
||||
omit =
|
||||
*/tests/*
|
||||
*/venv/*
|
||||
*/__pycache__/*
|
||||
|
||||
[report]
|
||||
exclude_lines =
|
||||
pragma: no cover
|
||||
def __repr__
|
||||
raise AssertionError
|
||||
raise NotImplementedError
|
||||
if __name__ == .__main__.:
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 八、测试 Fixtures
|
||||
|
||||
### 8.1 conftest.py
|
||||
|
||||
```python
|
||||
# tests/conftest.py
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
|
||||
@pytest.fixture
|
||||
def temp_project(tmp_path):
|
||||
"""创建临时项目目录"""
|
||||
project_dir = tmp_path / "test_project"
|
||||
project_dir.mkdir()
|
||||
return project_dir
|
||||
|
||||
@pytest.fixture
|
||||
def initialized_project(temp_project, monkeypatch):
|
||||
"""创建已初始化的项目"""
|
||||
monkeypatch.chdir(temp_project)
|
||||
from commands.init import cmd_init
|
||||
cmd_init([])
|
||||
return temp_project
|
||||
|
||||
@pytest.fixture
|
||||
def sample_config():
|
||||
"""示例配置"""
|
||||
return {
|
||||
"task": {
|
||||
"source": "task-now.md",
|
||||
"spec": "task-spec.md"
|
||||
},
|
||||
"env": {
|
||||
"python": {
|
||||
"version": ">=3.10",
|
||||
"venv": ".venv"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 九、持续集成
|
||||
|
||||
### 9.1 GitHub Actions 配置
|
||||
|
||||
```yaml
|
||||
# .github/workflows/test.yml
|
||||
name: Tests
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
python-version: ['3.10', '3.11', '3.12']
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install pytest pytest-cov pytest-mock
|
||||
pip install tomli tomli-w
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
pytest --cov=src --cov-report=xml
|
||||
|
||||
- name: Upload coverage
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
file: ./coverage.xml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十、测试最佳实践
|
||||
|
||||
### 10.1 测试原则
|
||||
|
||||
1. **独立性**:每个测试独立运行,不依赖其他测试
|
||||
2. **可重复性**:多次运行结果一致
|
||||
3. **清晰性**:测试意图明确,易于理解
|
||||
4. **快速性**:测试执行快速,及时反馈
|
||||
|
||||
### 10.2 测试模式
|
||||
|
||||
**AAA 模式**(Arrange-Act-Assert):
|
||||
|
||||
```python
|
||||
def test_example():
|
||||
# Arrange:准备测试数据
|
||||
config = {"task": {"source": "test.md"}}
|
||||
|
||||
# Act:执行操作
|
||||
result = get_config_value(config, "task.source")
|
||||
|
||||
# Assert:验证结果
|
||||
assert result == "test.md"
|
||||
```
|
||||
|
||||
### 10.3 Mock 使用
|
||||
|
||||
```python
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
def test_with_mock(monkeypatch):
|
||||
"""使用 mock 测试"""
|
||||
# Mock 函数
|
||||
mock_func = Mock(return_value=True)
|
||||
monkeypatch.setattr("module.function", mock_func)
|
||||
|
||||
# 执行测试
|
||||
result = some_function()
|
||||
|
||||
# 验证 mock 被调用
|
||||
mock_func.assert_called_once()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十一、测试检查清单
|
||||
|
||||
### 11.1 单元测试
|
||||
|
||||
- [ ] core/output.py 所有函数
|
||||
- [ ] core/config.py 所有函数
|
||||
- [ ] utils/validators.py 所有函数
|
||||
- [ ] 边界条件测试
|
||||
- [ ] 错误路径测试
|
||||
|
||||
### 11.2 集成测试
|
||||
|
||||
- [ ] aide init 完整流程
|
||||
- [ ] aide env ensure --runtime
|
||||
- [ ] aide env ensure
|
||||
- [ ] aide config get
|
||||
- [ ] aide config set
|
||||
- [ ] 命令组合测试
|
||||
|
||||
### 11.3 跨平台测试
|
||||
|
||||
- [ ] Linux 测试通过
|
||||
- [ ] macOS 测试通过
|
||||
- [ ] Windows 测试通过
|
||||
- [ ] 路径处理正确
|
||||
|
||||
### 11.4 性能测试
|
||||
|
||||
- [ ] 执行时间符合要求
|
||||
- [ ] 内存占用符合要求
|
||||
- [ ] 无内存泄漏
|
||||
|
||||
### 11.5 覆盖率
|
||||
|
||||
- [ ] 核心模块 ≥ 80%
|
||||
- [ ] 命令模块 ≥ 80%
|
||||
- [ ] 整体 ≥ 75%
|
||||
|
||||
---
|
||||
|
||||
## 十二、总结
|
||||
|
||||
### 12.1 核心要点
|
||||
|
||||
1. 完整的测试覆盖(单元、集成、跨平台)
|
||||
2. 明确的覆盖率要求
|
||||
3. 持续集成自动化
|
||||
4. 遵循测试最佳实践
|
||||
|
||||
### 12.2 运行测试
|
||||
|
||||
```bash
|
||||
# 运行所有测试
|
||||
pytest
|
||||
|
||||
# 运行特定测试
|
||||
pytest tests/unit/test_config.py
|
||||
|
||||
# 运行带覆盖率的测试
|
||||
pytest --cov=src
|
||||
|
||||
# 运行性能测试
|
||||
pytest -m performance
|
||||
|
||||
# 运行跨平台测试
|
||||
pytest -m cross_platform
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**版本**:v1.0
|
||||
**更新日期**:2025-12-13
|
||||
@@ -16,7 +16,7 @@ claude code正在辅助我完成一项任务
|
||||
|
||||
## 细节补充
|
||||
|
||||
前面已经根据 @aide-requirements.md 产出了 discuss/ 目录下的信息,当前完成了01报告中的Phase 1和Phase 2,产出了 aide-marketplace/ 和02报告,然后我提出了 @reply/re-03.md 中所述的意见,现在re-03已经完成了commands部分的内容,还有skills部分没有开始调整,
|
||||
前面已经根据 @aide-requirements.md 产出了 discuss/ 目录下的信息,当前完成了01报告中的Phase 1和Phase 2,产出了 aide-marketplace/ 和02报告,然后我提出了 @reply/re-03.md 中所述的意见,现在re-03已经完成,
|
||||
|
||||
# 要求
|
||||
|
||||
@@ -24,4 +24,4 @@ claude code正在辅助我完成一项任务
|
||||
|
||||
你必须亲自完整仔细的阅读所有提及的文件、目录及其子目录下所包含的所有文件内容(除了anthropic-agent-skills/仅要按需学习即可),必须一行不漏的完全审阅了所有文件,然后继续完成任务,
|
||||
|
||||
根据re-03的意见调整skills内容,然后根据re-03中意见的语义,更新 @aide-requirements.md
|
||||
在aide-program/目录下,实现aide程序系统(入口脚本封装+python实现,但不要考虑、不要实现aide flow和aide decide,它们留到后面单独讨论细节),
|
||||
@@ -1,5 +0,0 @@
|
||||
在aide-program/目录下,编写一套详细准确的,可用于开始开发aide体系程序的文档(但不包含aide flow和aide decide),
|
||||
|
||||
注意:这些文档必须足够详细足够准确,但是不允许直接给出具体的代码片段(可以定义细节、逻辑、接口、数据结构,但不允许在设计这一步给定固定死的代码实现)
|
||||
|
||||
然后我将会把aide-program/目录单独打包交给开发人员,完成好程序的开发(封装脚本+python程序)和测试后,再回来我们继续谈flow和decide的具体细节
|
||||
Reference in New Issue
Block a user