Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .cursor/rules/enforce-dev-standards.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
description: 强制在任何代码改动前加载 Timeflow 开发规范 skill
alwaysApply: true
---

# 开发规范强制加载

在本仓库编写、修改、重构任何代码之前,必须先用 Read 工具阅读
`.cursor/skills/timeflow-dev-standards/SKILL.md` 并严格遵守其中全部约束。

- 改完代码必须运行 `bash scripts/check-all.sh`(或带 backend/frontend 参数),全绿才算完成
- 禁止操作 git(init/commit/push/hooks),git 一律由用户执行
- 未经用户同意禁止新增依赖
- 与 SKILL.md 冲突的实现一律不允许产出
133 changes: 133 additions & 0 deletions .cursor/skills/timeflow-dev-standards/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
---
name: timeflow-dev-standards
description: Timeflow 项目的开发规范与硬性约束。在本仓库中编写、修改、重构任何代码(backend FastAPI 或 frontend Expo RN)之前必须先阅读并遵守。涵盖架构分层、API 设计、数据库、前端结构、命名、注释语言、测试与安全底线,以及 AI 禁止行为清单。
---

# Timeflow 开发规范

本文件是本仓库的最高开发约束。与本文件冲突的实现一律不允许产出。
改完代码后必须运行检查(见「完工门禁」),检查不过不算完成。

## 项目概览

- `backend/`:FastAPI + SQLAlchemy + Alembic,Python ≥ 3.11,包管理用 **uv**(禁止 pip install / poetry)
- `frontend/`:Expo React Native + TypeScript strict,**目标平台为 Android**,包管理用 **npm**(禁止 yarn / pnpm),本地运行用 `npm run android`
- 后端包名 `timeapp`,src 布局:`backend/src/timeapp/`

## 完工门禁(每次改完代码必须执行)

```bash
# 只改了后端
bash scripts/check-all.sh backend
# 只改了前端
bash scripts/check-all.sh frontend
# 都改了
bash scripts/check-all.sh
```

门禁内容:后端 `ruff check` + `ruff format --check` + `mypy`(strict)+ `pytest` + `alembic history`;若本机 PostgreSQL 可达则再跑 `alembic upgrade head` + `alembic check`(CI 始终跑这两项);前端 `eslint` + `prettier --check` + `tsc --noEmit` + `npm audit --audit-level=moderate`。
任何一项失败都必须修复后重跑,直到全绿。禁止用 `# noqa`、`# type: ignore`、`eslint-disable` 掩盖问题(确有必要时必须写明原因并在回复中向用户说明)。

## 后端架构(强制分层)

后端按 Agent 边界组织,禁止把业务写进 `api/`、`core/` 或 `main.py`:

```text
src/timeapp/
├── agents/ # 主 Agent 与专项 Agent
│ ├── main_agent/ # 唯一 Agent HTTP 入口:router / schemas / dispatcher
│ └── <special>_agent/ # 内部能力包:schemas / service / prompts(禁止 router.py)
├── basic/ # 非 Agent 产品边界(手动业务、事项展示、OCR/ASR 等)
│ └── <domain>/ # router / schemas / service / models(按需)
├── common/ # 跨 Agent 契约与共享能力
│ ├── contracts/ # 统一响应等契约
│ ├── data/ # 公共事实数据读写(专项 Agent 禁止直接调用)
│ ├── confirmation/ / questioning/ / llm/ / task_profile/ / context/
├── api/ # 路由聚合、health、dependencies
└── core/ # Settings、DB engine / session / Base
```

- 只有 `main_agent` 暴露 Agent HTTP 入口;专项 Agent 是进程内能力包,不直接读写数据库、不追问、不执行 `db_action`
- 对外 HTTP 路由(main_agent / basic)必须在 `api/router.py` 中注册:`api_router.include_router(...)`
- 横切能力(认证、DB 会话)统一放 `api/dependencies.py`,业务包内禁止自建
- 配置只能通过 `core/config.py` 的 `Settings` 读取,禁止在业务代码里直接 `os.environ`
- `basic/<domain>/` 内部分层:`router` 只做 HTTP 编排,`service` 禁止出现 FastAPI 对象;包之间只允许调用对方 `service`,禁止跨包 import `router` / `models`
- ORM 模型放 `basic/<domain>/models.py` 或经 `common/data/` 统一出口;专项 Agent 目录禁止出现 `models.py` 与数据库导入

## API 设计

- 路径用 kebab-case(`/main-agent`、`/unified-items`),与现有前缀保持一致;资源用复数名词,禁止动词路径(`POST /todos`,不是 `/create-todo`)
- 请求/响应必须定义 Pydantic 模型并声明 `response_model`,禁止返回裸 dict
- 状态码:创建 201、删除 204、404/409 等错误用 `HTTPException` 抛出,`detail` 用英文
- 分页列表统一 query 参数 `limit`(默认 20,上限 100)+ `offset`

## 数据库

- 公共 `Base`、engine、session 放 `core/db.py`;迁移脚本放 `backend/alembic/versions/`
- 表名 snake_case 复数(`todos`、`goal_plans`);所有表必须有 `id`、`created_at`、`updated_at`
- 任何模型变更必须生成 Alembic 迁移(`cd backend && uv run alembic revision --autogenerate -m "..."`),禁止手改历史迁移、禁止 `Base.metadata.create_all` 用于生产路径
- 应用迁移:`cd backend && uv run alembic upgrade head`
- 查询写在 service / `common/data` 层,禁止在 router 里直接操作 session

## 前端结构(Expo RN)

新代码按以下目录组织(目录不存在时按需创建):

```text
frontend/src/
├── screens/ # 页面级组件,PascalCase:HomeScreen.tsx
├── components/ # 可复用组件,PascalCase:TodoCard.tsx
├── hooks/ # 自定义 hook,camelCase:useTodos.ts
├── api/ # 后端 API 封装,统一 fetch 客户端
├── types/ # 共享 TS 类型
└── constants/ # 颜色、间距等设计常量
```

- 一律函数组件 + hooks,禁止 class 组件;组件 props 必须有显式 TS 类型
- 样式用 `StyleSheet.create`,禁止大段内联样式对象;颜色/间距取自 `constants/`
- 禁止 `any`(确需未知类型用 `unknown` 再收窄);tsconfig strict 不得关闭
- 调用后端必须经过 `src/api/` 封装层,组件内禁止直接写 fetch/URL

### Android 适配(目标平台)

- 一切 UI 与交互以 Android 为准验收,禁止引入 iOS-only API(如 `ActionSheetIOS`)
- 阴影用 `elevation`,禁止只写 iOS 的 `shadow*` 系列样式
- 必须处理 Android 物理返回键的页面退出逻辑(`BackHandler` 或导航库默认行为)
- 系统权限(通知、麦克风、相册等)在 `app.json` 的 `android.permissions` 中声明,并在代码中运行时请求
- 刘海屏/状态栏适配用 `SafeAreaView`/safe-area 方案,不写死状态栏高度

## 命名与语言

- Python:模块/函数/变量 snake_case,类 PascalCase;TS:变量/函数 camelCase,组件/类型 PascalCase
- 注释和 docstring 用**中文**;标识符、日志、错误信息、commit scope 用**英文**
- 只写解释「为什么」的注释,禁止复述代码行为的废话注释

## 测试(硬性要求)

- 新增或修改业务逻辑(service 层、非空壳 router、工具函数)必须同步补/改 pytest 测试,放 `backend/tests/test_<模块>.py`
- API 测试用 `fastapi.testclient.TestClient`,覆盖正常路径 + 至少一个错误路径
- 改完必须实际运行 `uv run pytest` 并通过;禁止提交只为凑数、无断言的测试

## 安全底线

- 密钥、token、连接串只能走环境变量(`TIMEAPP_` 前缀)+ `.env`(已 gitignore),新增变量必须同步更新 `.env.example`(放占位值)
- 禁止把 `.env`、真实密钥、用户数据写进代码、测试、文档
- SQL 只能通过 ORM / 绑定参数,禁止字符串拼接 SQL
- 禁止 `print` 调试(ruff T20 会拦截);日志不得输出密码、token、个人敏感信息
- 后端禁止吞异常(裸 `except: pass`);对外错误信息不暴露堆栈和内部路径

## AI 禁止行为清单

1. 禁止未经用户同意引入新依赖、新框架、新服务(改 pyproject.toml / package.json 依赖前必须先说明理由并征得同意)
2. 禁止修改与当前任务无关的代码、重排无关 import、顺手重构
3. 禁止留 TODO 空壳函数、`pass` 占位实现交差;做不完就明确告诉用户哪部分没做
4. 禁止降低门禁:不得删除/放宽 ruff、mypy、eslint、tsconfig 的现有配置
5. 禁止操作 git:不 init、不 commit、不 push,git 操作一律由用户自己执行。唯一例外:用户明确要求启用 hooks 时,按 `timeflow-git-hooks` skill 写 `.git/hooks/` 下的三个文件
6. 禁止编造不存在的 API、库用法;不确定就先查证
7. 禁止跳过「完工门禁」就宣称任务完成

## Commit 规范(供用户参考,hooks 强制)

提交信息请遵循 Conventional Commits 规范,详见 <https://www.conventionalcommits.org/zh-hans/v1.0.0/>。
格式 `type(scope): 描述`,type 限 feat/fix/docs/style/refactor/perf/test/build/ci/chore/revert,scope 用英文模块名,描述可中文,标题 ≤ 72 字符。示例:`feat(scheduling): 新增待办创建接口`。
启用拦截:用户提出需求后,按 `.cursor/skills/timeflow-git-hooks/SKILL.md` 创建 hooks(pre-commit 检查改动端、commit-msg 校验格式、pre-push 全量检查)。
134 changes: 134 additions & 0 deletions .cursor/skills/timeflow-git-hooks/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
---
name: timeflow-git-hooks
description: 按模板为 Timeflow 仓库创建并启用 git hooks(pre-commit / commit-msg / pre-push 质量与提交信息拦截)。当用户要求安装、启用、创建、更新或修复 git hooks、提交拦截、push 拦截时使用。AI 依据本文件将 hook 脚本写入 .git/hooks/ 并验证。
---

# Timeflow Git Hooks 创建

按本文件模板把三个 hook 写入 `.git/hooks/`,实现:提交前跑质量检查、提交信息强制 `type(scope): 描述`、推送前全量检查。
提交信息遵循 Conventional Commits 规范,详见 <https://www.conventionalcommits.org/zh-hans/v1.0.0/>。

## 前置条件

1. 仓库根目录必须存在 `.git/`。不存在时**停止**并提示用户先自行 `git init`,禁止代替用户执行。
2. `scripts/check-all.sh` 必须存在且可执行(hooks 依赖它)。缺失时先修复此依赖再继续。
3. 仅在用户明确要求启用/更新 hooks 时执行本流程;这是「禁止操作 git」约束的唯一例外,且例外范围仅限写 `.git/hooks/` 下的这三个文件。

## 创建步骤

用 Write 工具将下面三个模板**原样**写入对应路径(已存在同名 hook 时先向用户确认再覆盖),然后:

```bash
chmod +x .git/hooks/pre-commit .git/hooks/commit-msg .git/hooks/pre-push
```

### 模板 1:`.git/hooks/pre-commit`

```bash
#!/usr/bin/env bash
# 提交前拦截:只检查本次提交涉及的端,检查不通过则禁止提交。
set -uo pipefail

ROOT="$(git rev-parse --show-toplevel)"
STAGED="$(git diff --cached --name-only --diff-filter=ACMR)"

[[ -z "$STAGED" ]] && exit 0

if echo "$STAGED" | grep -q '^backend/'; then
"$ROOT/scripts/check-all.sh" backend || {
echo ""
echo "[pre-commit] 后端检查未通过,提交已被拦截。"
exit 1
}
fi

if echo "$STAGED" | grep -q '^frontend/'; then
"$ROOT/scripts/check-all.sh" frontend || {
echo ""
echo "[pre-commit] 前端检查未通过,提交已被拦截。"
exit 1
}
fi

exit 0
```

### 模板 2:`.git/hooks/commit-msg`

```bash
#!/usr/bin/env bash
# 提交信息拦截:必须符合 Conventional Commits 格式。
# 格式: type(scope)?: 描述 例如: feat(scheduling): 新增待办创建接口
set -uo pipefail

MSG_FILE="$1"
SUBJECT="$(head -n 1 "$MSG_FILE")"

# merge/revert 等 git 自动生成的信息直接放行
if echo "$SUBJECT" | grep -qE '^(Merge|Revert|fixup!|squash!)'; then
exit 0
fi

PATTERN='^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\([a-z0-9_-]+\))?(!)?: .+'

if ! echo "$SUBJECT" | grep -qE "$PATTERN"; then
echo "[commit-msg] 提交信息不符合规范,已被拦截。"
echo ""
echo " 要求格式: type(scope): 描述"
echo " 允许类型: feat fix docs style refactor perf test build ci chore revert"
echo " 示例: feat(scheduling): 新增待办创建接口"
echo " fix(reminders): 修复提醒时间时区错误"
echo ""
echo " 当前信息: $SUBJECT"
exit 1
fi

if [[ "${#SUBJECT}" -gt 72 ]]; then
echo "[commit-msg] 标题超过 72 字符(当前 ${#SUBJECT}),请精简后重试。"
exit 1
fi

exit 0
```

### 模板 3:`.git/hooks/pre-push`

```bash
#!/usr/bin/env bash
# 推送前拦截:全量检查(后端 + 前端)通过后才允许 push。
set -uo pipefail

ROOT="$(git rev-parse --show-toplevel)"

"$ROOT/scripts/check-all.sh" || {
echo ""
echo "[pre-push] 检查未通过,推送已被拦截。修复后重新 push。"
exit 1
}

exit 0
```

## 验证步骤(创建后必须执行)

不实际执行 commit/push,只做无副作用验证:

```bash
# 1. 语法检查
bash -n .git/hooks/pre-commit && bash -n .git/hooks/commit-msg && bash -n .git/hooks/pre-push

# 2. commit-msg 逻辑:合法信息应放行、非法信息应拦截
T=$(mktemp)
echo "feat(scheduling): 新增待办创建接口" > "$T" && bash .git/hooks/commit-msg "$T" # 应通过
echo "随便改改" > "$T" && bash .git/hooks/commit-msg "$T" # 应拦截(退出码 1)
rm -f "$T"

# 3. 确认可执行权限
ls -l .git/hooks/pre-commit .git/hooks/commit-msg .git/hooks/pre-push
```

三项全部符合预期后,向用户报告 hooks 已启用;任何一项异常必须修复后重新验证。

## 停用方式(告知用户即可,不主动执行)

删除对应文件即可:`rm .git/hooks/pre-commit .git/hooks/commit-msg .git/hooks/pre-push`
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
20.20.2
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.11.15
77 changes: 76 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,76 @@
# timeflow
# Timeflow

Timeflow 是一个单仓库项目,包含 FastAPI 后端和 Expo React Native 移动端。

## 固定版本

- Node.js `20.20.2` / npm `10.8.2`,见 `.nvmrc` 和 `package.json`。
- Python `3.11.15` / uv `0.11.28`,见 `.python-version`、`backend/pyproject.toml` 和 `backend/uv.lock`。
- PostgreSQL `16.4`,由根目录 Docker Compose 提供。

依赖使用规范见 [docs/dependency-management.md](docs/dependency-management.md),
完整目录设计见 [docs/project-structure.md](docs/project-structure.md)。

## 首次安装

```bash
cp .env.example .env
cp backend/.env.example backend/.env
cp frontend/.env.example frontend/.env
npm run install:frontend
npm run install:backend
```

本地直接跑 API(不用 Compose)时,先确保 PostgreSQL 可用,再应用迁移:

```bash
cd backend && uv run alembic upgrade head
```

## 启动

启动 PostgreSQL 和 API(容器入口会先执行 `alembic upgrade head`,再启动 uvicorn):

```bash
docker compose up --build
```

需要 Docker Desktop 4+(内含 Docker Compose v2)。
Android 模拟器使用 `frontend/.env.example` 中的 `10.0.2.2` 访问宿主机 API;
真机调试时,将 `EXPO_PUBLIC_API_URL` 改为开发机器的局域网 IP。

只启动移动端:

```bash
npm run dev:frontend
```

本地只启动 API(需已执行过迁移):

```bash
npm run dev:backend
```

API 健康检查:<http://127.0.0.1:8000/api/v1/health>

## 质量检查

官方门禁(改完代码后优先使用;后端在 PostgreSQL 可达时会跑 Alembic upgrade/check):

```bash
bash scripts/check-all.sh
# 或:bash scripts/check-all.sh backend
# 或:bash scripts/check-all.sh frontend
```

等价的分项命令:

```bash
npm run check:frontend
npm run check:backend
npm --prefix frontend audit --audit-level=moderate
```

后端使用 `uv` 管理依赖,前端使用 `npm` 管理依赖。两种语言使用各自原生的锁文件,根目录脚本只负责统一入口。

后端按 Agent 边界组织:`agents/` 放主 Agent 和专项 Agent,`common/` 放公共数据、确认、反问、LLM、任务级画像、上下文和统一响应契约,`basic/` 放手动业务与 OCR/ASR。只有主 Agent 暴露 Agent HTTP 入口;专项 Agent 是内部能力包,不直接读写数据库,统一响应契约见 `backend/src/timeapp/common/contracts/agent_response.py`。
Loading