# 📋 Butler 项目待办事项清单

**最后更新**: 2026-01-23
**参考文档**: `docs/REVIEW_AND_ROADMAP.md` (完整审查报告)

---

## 🎯 快速开始指南

回来后执行以下命令:

```bash
# 1. 拉取最新代码
cd /Users/lili/workspace/bulter
git status

# 2. 激活虚拟环境
source venv/bin/activate

# 3. 开始第一周的任务
# 参考下方 "Week 1" 部分
```

---

## ⚡ 优先级说明

- **P0** - 🔴 紧急且重要 (安全、可靠性)
- **P1** - 🟡 重要但不紧急 (架构改进、文档)
- **P2** - 🟢 重要性较低 (性能优化、新功能)

---

## 📅 Week 1: 安全和稳定性 (6小时)

### Day 1-2: 安全修复 (3小时)

#### [ ] Task 1.1: 修复敏感信息日志泄露 (1h) - P0 🔴
**文件**: `slack_bot/dispatcher.py:342`

**问题**:
```python
# 当前代码 (Line 342)
token_masked = f"{token[:5]}...{token[-5:]}" if token else "None"
logger.info(f"{prefix} Attempting download from {url} with token {token_masked}")
```

**修复方案**:
```python
# 选项 A: 完全移除 token 日志
logger.info(f"{prefix} Attempting download from {url}")

# 选项 B: 仅在 DEBUG 模式下输出
if os.getenv("DEBUG") == "true":
    token_masked = f"{token[:5]}...{token[-5:]}"
    logger.debug(f"Using token {token_masked}")
```

**验证**:
```bash
# 测试 Slack 图片下载功能
# 检查日志文件确认无 token 泄露
grep -i "token" logs/*.log
```

---

#### [ ] Task 1.2: 添加环境变量验证 (1h) - P0 🔴
**新建文件**: `config/settings.py`

**实现**:
```python
from pydantic_settings import BaseSettings
from typing import Optional

class GarminSettings(BaseSettings):
    email: str
    password: str

    class Config:
        env_prefix = "GARMIN_"

class SlackSettings(BaseSettings):
    bot_token: str
    app_token: str

    class Config:
        env_prefix = "SLACK_"

class LLMSettings(BaseSettings):
    api_key: str
    base_url: str = "http://127.0.0.1:8045"
    model: str = "gemini-2.0-flash-exp"

    class Config:
        env_prefix = "GEMINI_"

class AppSettings(BaseSettings):
    garmin: GarminSettings
    slack: SlackSettings
    llm: LLMSettings

    class Config:
        env_file = ".env"

# 全局实例
settings = AppSettings()
```

**迁移步骤**:
1. 创建 `config/settings.py`
2. 安装依赖: `pip install pydantic-settings`
3. 更新 `requirements.txt`
4. 逐步替换所有 `os.getenv()` 调用
5. 测试启动时的验证

**验证**:
```bash
# 测试缺失变量时的错误提示
mv .env .env.backup
python -c "from config.settings import settings"
# 应该看到清晰的错误信息
mv .env.backup .env
```

---

#### [ ] Task 1.3: Shell 命令黑名单 (1h) - P0 🔴
**文件**: `slack_bot/tools/shell_tool.py`

**实现**:
```python
# 在文件开头添加
DANGEROUS_COMMANDS = [
    "rm -rf /",
    "rm -rf /*",
    "mkfs",
    "dd if=/dev/zero",
    "> /dev/sda",
    "fork bomb",
    ":(){ :|:& };:",
]

DANGEROUS_PATTERNS = [
    r"rm\s+-rf\s+/",
    r"sudo\s+rm",
    r"chmod\s+-R\s+777",
    r"chown\s+-R",
]

def is_safe_command(command: str) -> tuple[bool, str]:
    """Check if command is safe to execute.

    Returns:
        (is_safe, reason)
    """
    import re

    # Check exact matches
    for dangerous in DANGEROUS_COMMANDS:
        if dangerous in command.lower():
            return False, f"Blocked dangerous command pattern: {dangerous}"

    # Check regex patterns
    for pattern in DANGEROUS_PATTERNS:
        if re.search(pattern, command):
            return False, f"Blocked dangerous pattern: {pattern}"

    return True, ""

def execute_shell(command: str, channel_id: str, timeout: float = 2.0) -> str:
    """Execute shell command with safety checks."""

    # Safety check
    is_safe, reason = is_safe_command(command)
    if not is_safe:
        return f"❌ BLOCKED: {reason}"

    # ... 原有逻辑
```

**验证**:
```bash
# 测试危险命令拦截
python -c "
from slack_bot.tools.shell_tool import execute_shell
print(execute_shell('rm -rf /', 'test'))
print(execute_shell('ls -la', 'test'))
"
```

---

### Day 3-4: 可靠性增强 (3小时)

#### [ ] Task 1.4: 添加 Garmin API 重试机制 (2h) - P0 🔴
**文件**: `health/services/data_sync.py`

**安装依赖**:
```bash
pip install tenacity
echo "tenacity>=8.0.0" >> requirements.txt
```

**实现**:
```python
from tenacity import (
    retry,
    stop_after_attempt,
    wait_exponential,
    retry_if_exception_type
)
from health.utils.exceptions import GarminAPIError

# 在 HealthDataSync 类中修改
@retry(
    stop=stop_after_attempt(3),
    wait=wait_exponential(multiplier=1, min=4, max=10),
    retry=retry_if_exception_type(GarminAPIError),
    reraise=True
)
def sync_daily_metric(
    self, metric_type: str, target_date: date, force: bool = False
) -> bool:
    """Sync a single daily metric with retry logic."""
    # 原有逻辑保持不变
    ...
```

**验证**:
```bash
# 临时断网测试重试
# 或者修改 garmin_client.py 模拟失败
python -m health.cli.sync sync --days 1
# 观察日志中的重试行为
```

---

#### [ ] Task 1.5: SQLite WAL 模式优化 (1h) - P0 🔴
**文件**: `health/db/schema.py`

**实现**:
```python
def init_database(db_path: Optional[Path] = None) -> None:
    """Initialize database with performance optimizations."""
    path = db_path or DB_PATH
    logger.info(f"Initializing database at {path}")

    with get_connection(path) as conn:
        cursor = conn.cursor()

        # Performance optimizations
        cursor.execute("PRAGMA journal_mode=WAL;")
        cursor.execute("PRAGMA synchronous=NORMAL;")
        cursor.execute("PRAGMA cache_size=10000;")
        cursor.execute("PRAGMA temp_store=MEMORY;")

        # Create tables
        cursor.execute(CREATE_SYNC_RECORDS_TABLE)
        # ... 其余保持不变
```

**验证**:
```bash
# 检查 WAL 模式是否启用
sqlite3 data/health/health.db "PRAGMA journal_mode;"
# 应该输出: wal

# 性能测试 (可选)
python -c "
from health.db.repository import HealthRepository
import time
repo = HealthRepository()
start = time.time()
for i in range(1000):
    repo.get_last_sync_date('sleep')
print(f'1000 queries: {time.time()-start:.2f}s')
"
```

---

## 📅 Week 2: 文档和测试 (8小时)

### Day 1-2: 核心文档 (4小时)

#### [ ] Task 2.1: 编写 ARCHITECTURE.md (2h) - P0 🔴
**新建文件**: `docs/ARCHITECTURE.md`

**内容大纲**:
```markdown
# Butler 架构文档

## 1. 系统概览
- 架构图
- 核心模块
- 技术栈

## 2. 模块详解
### 2.1 Health Module
- 数据流
- 同步逻辑
- 存储策略

### 2.2 Slack Bot
- 消息处理流程
- 工具系统
- 上下文管理

### 2.3 Obsidian Integration
- 索引机制
- 生成器

## 3. 设计决策
- 为什么选择 SQLite + JSON?
- 为什么使用双 Bot 架构?
- 为什么本地优先?

## 4. 扩展指南
- 如何添加新的健康指标
- 如何添加新工具
- 如何集成新数据源
```

**参考**:
- 查看 `REVIEW_AND_ROADMAP.md` 的架构章节
- 使用 Mermaid 绘制流程图

---

#### [ ] Task 2.2: 编写 TOOLS_REFERENCE.md (2h) - P0 🔴
**新建文件**: `docs/TOOLS_REFERENCE.md`

**内容大纲**:
```markdown
# Tools Reference

## 概览
Butler Bot 提供 14 个工具，分为 5 类:
- 读取工具 (4个)
- 写入工具 (5个)
- 同步工具 (2个)
- 分析工具 (2个)
- 系统工具 (2个)

## 详细文档

### 1. get_daily_detailed_stats
**用途**: 获取某一天的完整健康摘要

**参数**:
- `target_date` (必填): YYYY-MM-DD

**使用场景**:
- "昨天我睡得怎么样?"
- "2024-01-15 的数据"

**示例**:
```json
{
  "target_date": "2024-01-22"
}
```

**返回示例**:
```json
{
  "date": "2024-01-22",
  "sleep": {...},
  "steps": 8234,
  "activities": [...],
  "manual_logs": {
    "diet": 3,
    "supplements": 2
  }
}
```

... (为每个工具重复)
```

---

### Day 3-5: 测试覆盖 (4小时)

#### [ ] Task 2.3: 配置测试覆盖率工具 (2h) - P1 🟡

**安装依赖**:
```bash
pip install pytest-cov pytest-mock
echo "pytest-cov>=4.0.0" >> requirements.txt
echo "pytest-mock>=3.10.0" >> requirements.txt
```

**配置文件**: `pytest.ini` (新建)
```ini
[pytest]
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*

# Coverage settings
addopts =
    --cov=health
    --cov=slack_bot
    --cov-report=html
    --cov-report=term-missing
    --cov-fail-under=60

# Ignore warnings
filterwarnings =
    ignore::DeprecationWarning
```

**运行测试**:
```bash
pytest
# 查看覆盖率报告
open htmlcov/index.html
```

**分析覆盖率**:
1. 找出覆盖率 <50% 的模块
2. 优先补充核心路径测试
3. 目标: 整体覆盖率 ≥60%

---

#### [ ] Task 2.4: 添加关键路径集成测试 (2h) - P1 🟡

**新建文件**: `tests/integration/test_sync_flow.py`

```python
import pytest
from datetime import date, timedelta
from health.services.data_sync import HealthDataSync

@pytest.fixture
def sync_service():
    """Create sync service for testing."""
    return HealthDataSync()

def test_incremental_sync_flow(sync_service):
    """Test complete incremental sync flow."""
    # 1. Authenticate
    sync_service.authenticate()

    # 2. Get current status
    status = sync_service.get_sync_status()
    assert isinstance(status, dict)

    # 3. Perform incremental sync
    yesterday = date.today() - timedelta(days=1)
    results = sync_service.sync_incremental(
        until_date=yesterday
    )

    # 4. Verify results
    assert "sleep" in results
    assert results["sleep"]["synced"] >= 0
    assert results["sleep"]["errors"] == 0

def test_manual_log_flow():
    """Test manual logging flow."""
    from health.services.manual_log_storage import ManualLogStorage

    storage = ManualLogStorage()

    # Log diet
    storage.log_diet(
        target_date=date.today(),
        description="Test meal",
        meal_type="lunch"
    )

    # Query back
    logs = storage.get_logs(
        start_date=date.today(),
        end_date=date.today(),
        category="diet"
    )

    assert len(logs) > 0
    assert logs[-1]["description"] == "Test meal"
```

**新建文件**: `tests/integration/test_slack_bot.py`

```python
import pytest
from slack_bot.dispatcher import MessageDispatcher

@pytest.fixture
def dispatcher():
    """Create dispatcher with test config."""
    return MessageDispatcher()

def test_message_dispatch_simple(dispatcher, mocker):
    """Test simple message dispatch."""
    # Mock Slack client
    mock_client = mocker.patch.object(dispatcher, 'client')

    # Mock LLM response
    mocker.patch.object(
        dispatcher.llm,
        'generate_response',
        return_value=("Hello! I'm Butler.", [])
    )

    # Dispatch message
    dispatcher.dispatch(
        message_text="你好",
        channel_id="C12345",
        user_id="U12345"
    )

    # Verify Slack was called
    assert mock_client.chat_postMessage.called
```

---

## 📅 Week 3+: 功能增强 (按需选择)

### 高价值功能推荐

#### [ ] Feature 3.1: 健康洞察自动化报告 (1天) - P0 🔴
**新建文件**: `health/analytics/weekly_report.py`

**功能**:
- 每周日晚生成健康周报
- 自动分析本周数据
- 发送到 Slack

**Cron 配置**:
```bash
# 每周日 20:00 运行
0 20 * * 0 cd /Users/lili/workspace/bulter && source venv/bin/activate && python -m health.analytics.weekly_report
```

---

#### [ ] Feature 3.2: Streamlit 仪表板 (3天) - P1 🟡
**新建目录**: `apps/dashboard/`

**安装**:
```bash
pip install streamlit plotly
```

**实现**:
```python
# apps/dashboard/app.py
import streamlit as st
import plotly.express as px
from health.services.query import HealthDataQuery

st.title("🏥 Butler Health Dashboard")

# 日期选择
date_range = st.date_input("选择日期范围", ...)

# 睡眠趋势图
sleep_data = query.get_metric_history("sleep", ...)
fig = px.line(sleep_data, x="date", y="sleep_score")
st.plotly_chart(fig)
```

**运行**:
```bash
streamlit run apps/dashboard/app.py
```

---

#### [ ] Feature 3.3: 数据导出功能 (2天) - P1 🟡
**新建文件**: `health/services/export.py`

**功能**:
- 导出为 CSV
- 导出为 PDF 报告
- 支持日期范围过滤

**CLI**:
```bash
python -m health.cli.export csv --start 2024-01-01 --end 2024-01-31
python -m health.cli.export pdf --start 2024-01-01 --end 2024-01-31
```

---

## 🔧 配置和依赖更新

### 需要添加的依赖

```txt
# 安全和稳定性
tenacity>=8.0.0

# 配置管理
pydantic-settings>=2.0.0

# 测试
pytest-cov>=4.0.0
pytest-mock>=3.10.0

# 数据可视化 (可选)
streamlit>=1.20.0
plotly>=5.14.0

# 数据导出 (可选)
reportlab>=4.0.0  # PDF 生成
```

### 安装命令

```bash
# 安装核心优化依赖
pip install tenacity pydantic-settings pytest-cov pytest-mock

# 更新 requirements.txt
pip freeze > requirements_new.txt
# 手动合并到 requirements.txt
```

---

## 📊 进度跟踪

### Week 1 进度 (0/5)
- [ ] 1.1 修复敏感信息日志泄露
- [ ] 1.2 添加环境变量验证
- [ ] 1.3 Shell 命令黑名单
- [ ] 1.4 Garmin API 重试机制
- [ ] 1.5 SQLite WAL 模式优化

### Week 2 进度 (0/4)
- [ ] 2.1 编写 ARCHITECTURE.md
- [ ] 2.2 编写 TOOLS_REFERENCE.md
- [ ] 2.3 配置测试覆盖率工具
- [ ] 2.4 添加集成测试

### Week 3+ (可选)
- [ ] 3.1 健康洞察自动化报告
- [ ] 3.2 Streamlit 仪表板
- [ ] 3.3 数据导出功能

---

## 🎯 回来后的第一条命令

```bash
# 提醒 Claude 继续工作
我回来了，请阅读 docs/REVIEW_AND_ROADMAP.md 和 docs/TODO.md，
让我们开始执行 Week 1 的任务，从 Task 1.1 开始。
```

---

## 📝 备注

- 所有任务都是**非破坏性**的，不会影响现有功能
- 优先级 P0 的任务建议优先完成
- 每个任务完成后建议 git commit
- 测试通过后再 push

**Good luck! 🚀**
