迁移 Claude/Codex/Cursor 开发环境与追溯资产
Co-Authored-By: OpenAI Codex <codex@openai.com> Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
45
.cursor/hooks.json
Normal file
45
.cursor/hooks.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"version": 1,
|
||||
"hooks": {
|
||||
"preToolUse": [
|
||||
{
|
||||
"command": ".venv\\Scripts\\python.exe .cursor\\hooks\\ai_env_guard.py preToolUse",
|
||||
"matcher": "Read|Glob|Edit|Write|ApplyPatch",
|
||||
"timeout": 5,
|
||||
"failClosed": true
|
||||
}
|
||||
],
|
||||
"beforeReadFile": [
|
||||
{
|
||||
"command": ".venv\\Scripts\\python.exe .cursor\\hooks\\ai_env_guard.py beforeReadFile",
|
||||
"matcher": "_archived",
|
||||
"timeout": 5,
|
||||
"failClosed": true
|
||||
}
|
||||
],
|
||||
"beforeMCPExecution": [
|
||||
{
|
||||
"command": ".venv\\Scripts\\python.exe .cursor\\hooks\\ai_env_guard.py beforeMCPExecution",
|
||||
"matcher": "pg-etl|pg-app",
|
||||
"timeout": 5,
|
||||
"failClosed": false
|
||||
}
|
||||
],
|
||||
"beforeShellExecution": [
|
||||
{
|
||||
"command": ".venv\\Scripts\\python.exe .cursor\\hooks\\ai_env_guard.py beforeShellExecution",
|
||||
"matcher": "git\\s+(reset|checkout)|--no-verify|psql|mcp-postgres|PG_DSN|APP_DB_DSN",
|
||||
"timeout": 5,
|
||||
"failClosed": false
|
||||
}
|
||||
],
|
||||
"postToolUse": [
|
||||
{
|
||||
"command": ".venv\\Scripts\\python.exe .cursor\\hooks\\ai_env_guard.py postToolUse",
|
||||
"matcher": "ApplyPatch|Edit|Write",
|
||||
"timeout": 5,
|
||||
"failClosed": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
121
.cursor/hooks/ai_env_guard.py
Normal file
121
.cursor/hooks/ai_env_guard.py
Normal file
@@ -0,0 +1,121 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ARCHIVED_RE = re.compile(r"(^|[/\\])_archived([/\\]|$)", re.IGNORECASE)
|
||||
PROD_MCP_RE = re.compile(r"\b(pg-etl|pg-app)\b", re.IGNORECASE)
|
||||
TEST_MCP_RE = re.compile(r"\b(pg-etl-test|pg-app-test)\b", re.IGNORECASE)
|
||||
WRITE_SQL_RE = re.compile(
|
||||
r"\b(insert|update|delete|truncate|drop|alter|create|grant|revoke|merge|copy|call|vacuum|reindex)\b",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
|
||||
|
||||
def load_input() -> dict:
|
||||
try:
|
||||
return json.load(sys.stdin)
|
||||
except Exception:
|
||||
return {}
|
||||
|
||||
|
||||
def target_text(data: dict) -> str:
|
||||
chunks = [json.dumps(data, ensure_ascii=False)]
|
||||
for key in ("command", "file_path", "path"):
|
||||
value = data.get(key)
|
||||
if isinstance(value, str):
|
||||
chunks.append(value)
|
||||
tool_input = data.get("tool_input")
|
||||
if isinstance(tool_input, dict):
|
||||
chunks.append(json.dumps(tool_input, ensure_ascii=False))
|
||||
return "\n".join(chunks)
|
||||
|
||||
|
||||
def allow() -> None:
|
||||
print(json.dumps({"permission": "allow"}, ensure_ascii=False))
|
||||
|
||||
|
||||
def before_shell(data: dict) -> None:
|
||||
text = target_text(data)
|
||||
dangerous = [
|
||||
r"git\s+reset\s+--hard",
|
||||
r"git\s+checkout\s+--",
|
||||
r"--no-verify",
|
||||
r"\bPG_DSN\b",
|
||||
r"\bAPP_DB_DSN\b",
|
||||
r"\bpsql\b",
|
||||
]
|
||||
if any(re.search(pattern, text, re.IGNORECASE) for pattern in dangerous):
|
||||
print(json.dumps({
|
||||
"permission": "ask",
|
||||
"user_message": "命令命中 NeoZQYY 高风险规则:请确认是否需要执行。",
|
||||
"agent_message": "执行前说明风险、目标库/文件、回滚方式;生产库默认不执行。",
|
||||
}, ensure_ascii=False))
|
||||
return
|
||||
allow()
|
||||
|
||||
|
||||
def guard_archived(data: dict) -> None:
|
||||
text = target_text(data)
|
||||
if ARCHIVED_RE.search(text):
|
||||
print(json.dumps({
|
||||
"permission": "deny",
|
||||
"user_message": "已阻断:`_archived/` 是废弃归档目录,禁止读取、搜索或作为实现参考。",
|
||||
"agent_message": "改用当前版本文件;如确需考古,请让用户明确授权并说明目的。",
|
||||
}, ensure_ascii=False))
|
||||
return
|
||||
allow()
|
||||
|
||||
|
||||
def before_mcp(data: dict) -> None:
|
||||
text = target_text(data)
|
||||
if not PROD_MCP_RE.search(text) or TEST_MCP_RE.search(text):
|
||||
allow()
|
||||
return
|
||||
if WRITE_SQL_RE.search(text):
|
||||
print(json.dumps({
|
||||
"permission": "deny",
|
||||
"user_message": "已阻断:生产库 MCP 写入/DDL 类操作默认禁止。请改用测试库验证,或让用户给出单次明确授权。",
|
||||
"agent_message": "生产库仅允许人工确认后的只读排查;DDL/写入需走单独变更流程。",
|
||||
}, ensure_ascii=False))
|
||||
return
|
||||
print(json.dumps({
|
||||
"permission": "ask",
|
||||
"user_message": "检测到生产库 MCP 只读调用。请确认本次查询目的、SQL 是否只读、是否会暴露敏感数据。",
|
||||
"agent_message": "说明查询目的、目标库、SQL 摘要和风险后等待用户确认。",
|
||||
}, ensure_ascii=False))
|
||||
|
||||
|
||||
def post_tool_use(data: dict) -> None:
|
||||
text = target_text(data).replace("\\", "/")
|
||||
hints = []
|
||||
if "/_archived/" in text or text.endswith("/_archived"):
|
||||
hints.append("检测到 `_archived/` 路径:该目录内容已废弃,禁止作为实现参考。")
|
||||
if "apps/demo-miniprogram/" in text:
|
||||
hints.append("检测到 demo-miniprogram:这是 MOCK 标杆目录,修改前需确认目的。")
|
||||
if re.search(r"(db/|docs/database/|migrations/|schemas/)", text):
|
||||
hints.append("检测到数据库相关改动:如影响 schema,需同步 `docs/database/` 并提供 3 条验证 SQL。")
|
||||
if re.search(r"(apps/backend/|apps/etl/|app/ai/|prompts/)", text):
|
||||
hints.append("检测到后端/ETL/AI 逻辑改动:完成后需运行相关测试并写审计记录。")
|
||||
if hints:
|
||||
print(json.dumps({"additional_context": "\n".join(f"[NeoZQYY] {hint}" for hint in hints)}, ensure_ascii=False))
|
||||
|
||||
|
||||
def main() -> None:
|
||||
mode = sys.argv[1] if len(sys.argv) > 1 else ""
|
||||
data = load_input()
|
||||
if mode in {"preToolUse", "beforeReadFile"}:
|
||||
guard_archived(data)
|
||||
elif mode == "beforeMCPExecution":
|
||||
before_mcp(data)
|
||||
elif mode == "beforeShellExecution":
|
||||
before_shell(data)
|
||||
elif mode == "postToolUse":
|
||||
post_tool_use(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
12
.cursor/rules/admin-web.mdc
Normal file
12
.cursor/rules/admin-web.mdc
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
description: admin-web 规则:React/Vite/AntD、AI 管理套件与前端验证。
|
||||
globs: apps/admin-web/**
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# admin-web 规则
|
||||
|
||||
- `admin-web` 是开发/运维后台,不是租户后台。
|
||||
- 遵循 React + Vite + Ant Design 现有页面和 API 封装风格。
|
||||
- AI 管理相关改动先查 2026-04-21、2026-04-30 审计记录。
|
||||
- 前端逻辑改动后优先运行 `pnpm test` / `pnpm lint`,无法运行需说明原因。
|
||||
11
.cursor/rules/audit-history.mdc
Normal file
11
.cursor/rules/audit-history.mdc
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
description: 历史追溯规则:优先使用精简索引、审计记录和当前代码。
|
||||
globs: docs/**
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# 历史追溯规则
|
||||
|
||||
- 日常追溯先查 `docs/ai-env-history/README.md`、`docs/claude-history/`、`docs/audit/changes/`。
|
||||
- 历史摘要只解释来龙去脉,编码前仍以当前文件、当前 diff、当前测试为准。
|
||||
- 原始 JSONL 可用于追查细节,但不要把密钥、DSN、token 原文写入仓库文档。
|
||||
14
.cursor/rules/backend-fastapi.mdc
Normal file
14
.cursor/rules/backend-fastapi.mdc
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
description: FastAPI 后端规则:响应包装、认证、AI 集成、RLS 与测试库。
|
||||
globs: apps/backend/**
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# 后端规则
|
||||
|
||||
- 2xx 响应经 `ResponseWrapperMiddleware` 包装为 `{ "code": 0, "data": <payload> }`。
|
||||
- 后端内部使用 snake_case,JSON 输出通过 `CamelModel` 转 camelCase。
|
||||
- admin、miniapp、tenant-admin 三类 JWT aud 不可混用。
|
||||
- 访问 ETL FDW/RLS 视图前必须设置 `app.current_site_id`。
|
||||
- AI 集成涉及 DashScope、熔断、限流、预算、缓存和运行日志,改动后必须查审计历史。
|
||||
- 后端验证默认在 `apps/backend` 下运行,使用测试库,禁止连正式库。
|
||||
13
.cursor/rules/database.mdc
Normal file
13
.cursor/rules/database.mdc
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
description: 数据库规则:schema 变更、RLS 双 schema、文档同步和验证 SQL。
|
||||
globs: db/**,docs/database/**
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# 数据库规则
|
||||
|
||||
- 任何 PostgreSQL schema/迁移/DDL/ORM 结构变更必须同步 `docs/database/`。
|
||||
- 新建 DWS/DWD RLS 视图必须同时创建原 schema 和 `app` schema 视图。
|
||||
- 回滚需逆序 DROP/ALTER,并提供至少 3 条验证 SQL。
|
||||
- 默认使用 `TEST_DB_DSN` / `TEST_APP_DB_DSN`,禁止连正式库。
|
||||
- DDL 基线变更后运行 `tools/db/gen_consolidated_ddl.py` 并同步权威 DDL。
|
||||
11
.cursor/rules/demo-miniprogram-protect.mdc
Normal file
11
.cursor/rules/demo-miniprogram-protect.mdc
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
description: demo-miniprogram 保护规则:假数据标杆,不删除不迁移到 _DEL。
|
||||
globs: apps/demo-miniprogram/**
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# demo-miniprogram 保护
|
||||
|
||||
- 本目录是假数据 MOCK 版小程序,用于页面样式和展示格式标杆校对。
|
||||
- 禁止删除、移入 `_DEL/` 或改造成真实 API 驱动。
|
||||
- 只有在用户明确要求校正 demo 标杆时才修改。
|
||||
14
.cursor/rules/etl-feiqiu.mdc
Normal file
14
.cursor/rules/etl-feiqiu.mdc
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
description: 飞球 ETL 规则:DWD-DOC 优先、金额口径、DWS 优先、禁止归档目录。
|
||||
globs: apps/etl/connectors/feiqiu/**
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# ETL 飞球规则
|
||||
|
||||
- 金额、支付、消费链路、字段语义优先参考 `apps/etl/connectors/feiqiu/docs/reports/DWD-DOC/`。
|
||||
- `consume_money` 禁止直接用于计算,使用 `items_sum` 拆分字段。
|
||||
- 助教费用必须区分 `assistant_pd_money` 和 `assistant_cx_money`。
|
||||
- 正向结算使用 `settle_type IN (1, 3)`,禁止随意读取 ODS 做业务计算。
|
||||
- DWS/DWD 汇总默认保持幂等,禁止 `TRUNCATE`。
|
||||
- 所有 `_archived/` 目录禁止读取或参考。
|
||||
12
.cursor/rules/miniprogram.mdc
Normal file
12
.cursor/rules/miniprogram.mdc
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
description: 微信小程序规则:生产小程序、Donut/TDesign、demo 标杆对齐。
|
||||
globs: apps/miniprogram/**
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# 小程序规则
|
||||
|
||||
- `apps/miniprogram` 是生产小程序,数据来自后端 API。
|
||||
- UI 样式和展示格式需要参考 `apps/demo-miniprogram` 的 MOCK 标杆。
|
||||
- 改动关键交互、鉴权、API 字段或页面跳转后必须说明验证路径。
|
||||
- 涉及微信开发者工具时优先使用 `weixin-devtools-mcp` 或记录手工验证步骤。
|
||||
18
.cursor/rules/neozqyy-core.mdc
Normal file
18
.cursor/rules/neozqyy-core.mdc
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
description: NeoZQYY 核心工作规范:中文、调研、验证、审计、dirty tree 保护。
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# NeoZQYY 核心规范
|
||||
|
||||
- 始终使用中文交流、解释、审计和文档;命令、API 字段、变量名保持原文。
|
||||
- 以 `AGENTS.md` 为权威规则;历史 `CLAUDE.md` 已并入 `AGENTS.md`,需考古时查 git 历史或 `docs/ai-env-history/`。
|
||||
- 逻辑改动前先做需求审问和前置调研;用户明确跳过时除外。
|
||||
- 逻辑改动后运行相关验证,输出 diff 摘要和未覆盖风险。
|
||||
- 不回滚用户已有改动,不使用破坏性 git 命令,除非用户明确要求。
|
||||
- 审计记录统一写入 `docs/audit/changes/`,`docs/audit/audit_dashboard.md` 只由脚本生成。
|
||||
- 历史追溯优先查 `docs/ai-env-history/`、`docs/claude-history/`、`docs/audit/`,再查原始对话。
|
||||
- 用户偏好模型为 GPT 5.5 与 Claude 4.7;模型选择由 Cursor UI/会话设置控制,规则只保留偏好。
|
||||
- CLI / Shell 中文处理必须优先确保 UTF-8:Python 用 `encoding="utf-8"` / `PYTHONUTF8=1`,CSV 给 Excel 用 `utf-8-sig`,PowerShell/Node 避免依赖系统默认 ANSI 编码。
|
||||
- 遇到中文乱码时,不要把乱码输出当作事实;先调整编码重跑,或明确说明终端编码异常并转述可确认的信息。
|
||||
- Shell 路径和参数含中文、空格或特殊字符时必须正确加引号;复杂中文输出优先用脚本或结构化 API,避免手写脆弱转义。
|
||||
110
.cursor/skills/audit/SKILL.md
Normal file
110
.cursor/skills/audit/SKILL.md
Normal file
@@ -0,0 +1,110 @@
|
||||
---
|
||||
name: audit
|
||||
description: /audit — 变更审计。从 Claude Code 命令迁移为 Cursor project skill;用户要求执行 audit、/audit 或相关流程时使用。
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# /audit — 变更审计
|
||||
|
||||
回顾本次会话中你所做的所有文件变更,结合自动预扫描结果,执行审计落盘。
|
||||
|
||||
## 执行步骤
|
||||
|
||||
### 第 1 步:运行预扫描脚本(Python,零 token)
|
||||
|
||||
运行:
|
||||
```bash
|
||||
python scripts/audit/prescan.py
|
||||
```
|
||||
|
||||
该脚本自动完成:
|
||||
- 从 git status 获取所有变更文件
|
||||
- 分类高风险文件 + 生成 risk_tags
|
||||
- 合规检查:代码→文档映射、迁移 SQL 检测、DDL 基线检查
|
||||
|
||||
读取输出的 JSON。如果 `audit_required: false`,告知用户"无需审计"并结束。
|
||||
|
||||
**备选**:如果 git status 包含大量非本次会话的历史变更,可以用 `--files` 参数只传入本次会话的文件:
|
||||
```bash
|
||||
python scripts/audit/prescan.py --files "file1.py,file2.sql,..."
|
||||
```
|
||||
文件列表从你的对话记忆(本次会话的 Edit/Write 工具调用)中提取。
|
||||
|
||||
### 第 2 步:补充语义上下文
|
||||
|
||||
预扫描脚本能告诉你"哪些文件变了、是否高风险、文档是否缺失",但它不知道**为什么改**。
|
||||
|
||||
从对话记忆中补充:
|
||||
- 每个变更文件的修改原因(用户的需求是什么)
|
||||
- 改动的技术思路和设计决策
|
||||
- 与其他模块的关联影响
|
||||
|
||||
将预扫描 JSON + 语义上下文合并,作为第 3 步的输入。
|
||||
|
||||
### 第 3 步:委托子代理写审计记录
|
||||
|
||||
用 Agent 工具启动子代理,传入:
|
||||
1. 预扫描 JSON 结果(完整)
|
||||
2. 每个变更的原因和内容概要(你补充的语义上下文)
|
||||
|
||||
子代理的任务指令:
|
||||
|
||||
> 在 `docs/audit/changes/` 目录下创建审计记录文件,文件名格式 `<YYYY-MM-DD>__<英文短标识>.md`。
|
||||
>
|
||||
> 使用以下格式:
|
||||
>
|
||||
> ```markdown
|
||||
> # 变更审计记录:<中文标题>
|
||||
>
|
||||
> | 字段 | 值 |
|
||||
> |------|-----|
|
||||
> | 日期 | YYYY-MM-DD HH:MM:SS |
|
||||
>
|
||||
> ## 操作摘要
|
||||
> <1-3 段,说清楚做了什么、为什么做>
|
||||
>
|
||||
> ## 变更文件
|
||||
> 按新增/修改/删除分组,每个文件一行,简要说明改动内容。
|
||||
>
|
||||
> ## 改动注解
|
||||
> 对每个变更文件写注解:
|
||||
> - 高风险文件(ETL 任务/后端路由/数据库迁移/金额相关):写详细注解(变更类型、原因、思路、结果)
|
||||
> - 普通文件:一行简要说明
|
||||
> - 删除的文件:只记录删除原因
|
||||
>
|
||||
> ## 数据库变更(如有)
|
||||
> 列出新建/修改/删除的表、字段、约束、索引。标注迁移执行状态。
|
||||
>
|
||||
> ## 风险与回滚
|
||||
> - 风险点(标注高/中/低)
|
||||
> - 回滚要点
|
||||
>
|
||||
> ## 验证
|
||||
> - 至少 1 条可执行的验证方式(测试命令 / SQL / 联调步骤)
|
||||
>
|
||||
> ## 合规检查
|
||||
> - 列出文档同步状态(已同步 / 待补齐 / 不适用)
|
||||
> ```
|
||||
>
|
||||
> 当前北京时间通过 `python -c "from datetime import datetime, timezone, timedelta; print(datetime.now(timezone(timedelta(hours=8))).strftime('%Y-%m-%d %H:%M:%S'))"` 获取。
|
||||
>
|
||||
> 审计记录语言使用简体中文。
|
||||
>
|
||||
> 完成后运行 `python scripts/audit/gen_audit_dashboard.py` 刷新审计一览表。
|
||||
>
|
||||
> 最终只返回:done / files_written / next_step。
|
||||
|
||||
### 第 4 步:补齐缺失的文档同步
|
||||
|
||||
根据预扫描 JSON 中 `code_without_docs` 列出的不合规项,逐项补齐:
|
||||
- 读取对应代码文件当前内容
|
||||
- 更新对应文档
|
||||
|
||||
如果补齐工作量大(>3 个文档),委托子代理处理。
|
||||
|
||||
### 第 5 步:向用户报告
|
||||
|
||||
简短回执:
|
||||
- 审计记录文件路径
|
||||
- 合规检查结果(全部通过 / N 项已补齐 / N 项待用户处理)
|
||||
- 下一步建议(如 "commit when ready")
|
||||
69
.cursor/skills/db-docs/SKILL.md
Normal file
69
.cursor/skills/db-docs/SKILL.md
Normal file
@@ -0,0 +1,69 @@
|
||||
---
|
||||
name: db-docs
|
||||
description: /db-docs — 数据库文档同步。从 Claude Code 命令迁移为 Cursor project skill;用户要求执行 db-docs、/db-docs 或相关流程时使用。
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# /db-docs — 数据库文档同步
|
||||
|
||||
当 PostgreSQL schema/表结构发生变化时,将变更以审计友好的方式落盘到 `docs/database/`。
|
||||
|
||||
## 触发条件
|
||||
|
||||
- 迁移脚本/DDL 修改(新增/删除/改表、字段、类型、默认值、非空、约束、索引、外键)
|
||||
- 手工执行了 DDL
|
||||
|
||||
## 执行步骤
|
||||
|
||||
### 第 1 步:识别结构性变化
|
||||
|
||||
从本次会话的改动中,列出新增/修改/删除的对象:
|
||||
- schema / table / column / index / constraint / foreign key
|
||||
- 明确变更前后差异(before/after)
|
||||
|
||||
### 第 2 步:更新表结构文档
|
||||
|
||||
对每张受影响的表,更新 `docs/database/` 下对应的文档:
|
||||
- 如果文档已存在:更新字段列表、约束、索引等
|
||||
- 如果文档不存在:基于以下模板创建
|
||||
|
||||
模板:
|
||||
```markdown
|
||||
# <schema>.<table_name>
|
||||
|
||||
## 概述
|
||||
<表的用途说明>
|
||||
|
||||
## 字段
|
||||
|
||||
| 字段名 | 类型 | 可空 | 默认值 | 说明 |
|
||||
|--------|------|------|--------|------|
|
||||
| ... | ... | ... | ... | ... |
|
||||
|
||||
## 约束与索引
|
||||
- PRIMARY KEY: ...
|
||||
- UNIQUE: ...
|
||||
- INDEX: ...
|
||||
|
||||
## 关联
|
||||
- 上游:<数据来源>
|
||||
- 下游:<被哪些模块/表消费>
|
||||
```
|
||||
|
||||
特别注意金额类字段:标注精度、币种、舍入规则。
|
||||
|
||||
### 第 3 步:回滚与验证
|
||||
|
||||
写入审计友好的回滚和验证信息:
|
||||
- DDL 回滚路径(必要时提供反向迁移 SQL)
|
||||
- 至少 3 条验证 SQL(含约束/索引/关键字段检查)
|
||||
|
||||
### 第 4 步:DDL 基线检查
|
||||
|
||||
检查 `docs/database/ddl/` 下的基线文件是否需要合并更新。如需要,更新基线。
|
||||
|
||||
### 第 5 步:输出摘要
|
||||
|
||||
- 更新/创建了哪些文档
|
||||
- 迁移脚本执行状态(已执行 / 待执行)
|
||||
- DDL 基线状态(已合并 / 待合并)
|
||||
61
.cursor/skills/doc-sync/SKILL.md
Normal file
61
.cursor/skills/doc-sync/SKILL.md
Normal file
@@ -0,0 +1,61 @@
|
||||
---
|
||||
name: doc-sync
|
||||
description: /doc-sync — 逻辑改动后文档同步。从 Claude Code 命令迁移为 Cursor project skill;用户要求执行 doc-sync、/doc-sync 或相关流程时使用。
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# /doc-sync — 逻辑改动后文档同步
|
||||
|
||||
检查本次会话中的逻辑改动是否需要同步更新文档,并执行同步。
|
||||
|
||||
## 触发条件
|
||||
|
||||
修改了以下任一类内容时应执行:
|
||||
- 业务规则/计算口径/资金处理(精度、舍入、阈值)
|
||||
- ETL/SQL 清洗聚合映射逻辑
|
||||
- API 行为(返回结构、错误码、鉴权/权限)
|
||||
- 小程序关键交互流程
|
||||
- 数据库表结构
|
||||
|
||||
## 执行步骤
|
||||
|
||||
### 第 1 步:分类
|
||||
|
||||
判断本次会话的改动是否属于"逻辑改动"。如果只是纯格式化/拼写修正/注释调整,告知用户"无逻辑改动,无需文档同步"并结束。
|
||||
|
||||
### 第 2 步:逐项评估需要更新的文档
|
||||
|
||||
根据变更涉及的模块,评估以下文档是否需要更新:
|
||||
|
||||
**各级 README.md**(只更新与本次变更相关的):
|
||||
- `README.md`(根目录):项目总览、快速开始、环境变量、架构概述
|
||||
- `apps/backend/README.md`:后端 API 路由、配置、运行方式
|
||||
- `apps/etl/connectors/feiqiu/README.md`:ETL 任务清单、开发约定
|
||||
- `apps/miniprogram/README.md`:小程序页面结构
|
||||
- `apps/admin-web/README.md`:管理后台功能说明
|
||||
- `apps/tenant-admin/README.md`:租户管理后台功能说明
|
||||
- `packages/shared/README.md`:共享包说明
|
||||
- `db/README.md`:Schema 约定、迁移规范
|
||||
|
||||
规则:如果"对读者理解系统行为有帮助"就应更新。若某个 README 尚不存在但变更涉及该模块,应创建。
|
||||
|
||||
### 第 3 步:执行更新
|
||||
|
||||
对每个需要更新的文档:
|
||||
1. 读取当前内容
|
||||
2. 根据本次变更更新相关段落
|
||||
3. 写入更新后的内容
|
||||
|
||||
如果更新工作量大(>3 个文档),委托子代理处理。
|
||||
|
||||
### 第 4 步:联动检查
|
||||
|
||||
- 如果涉及 DB schema 变化:提醒用户执行 `/db-docs`
|
||||
- 如果涉及 API 变化:检查 `apps/backend/docs/API-REFERENCE.md` 是否已更新
|
||||
|
||||
### 第 5 步:输出摘要
|
||||
|
||||
- Changed:改了哪些文档
|
||||
- Why:原始原因 + 直接原因
|
||||
- Risk:风险点与回归范围
|
||||
- Verify:建议的验证步骤
|
||||
71
.cursor/skills/pre-change/SKILL.md
Normal file
71
.cursor/skills/pre-change/SKILL.md
Normal file
@@ -0,0 +1,71 @@
|
||||
---
|
||||
name: pre-change
|
||||
description: /pre-change — 逻辑改动前置调研。从 Claude Code 命令迁移为 Cursor project skill;用户要求执行 pre-change、/pre-change 或相关流程时使用。
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# /pre-change — 逻辑改动前置调研
|
||||
|
||||
对即将修改的模块进行全面调研,输出上下文摘要供用户确认后再动手。
|
||||
|
||||
## 适用场景
|
||||
|
||||
任何逻辑改动(ETL/业务规则/API/数据模型/前端交互),写代码前执行。
|
||||
|
||||
## 执行步骤
|
||||
|
||||
### 第 1 步:识别改动范围
|
||||
|
||||
从用户需求中提取:
|
||||
- 要修改的模块和文件
|
||||
- 涉及的数据表/API/页面
|
||||
- 预期的行为变化
|
||||
|
||||
### 第 2 步:委托 Explore 子代理调研
|
||||
|
||||
启动 Explore 子代理(thoroughness: very thorough),调研以下内容:
|
||||
|
||||
1. **目标模块文件**:读取要修改的文件及其直接依赖
|
||||
2. **历史审计**:搜索 `docs/audit/changes/` 中相关模块的历史变更记录
|
||||
3. **相关文档**:README、PRD(`docs/prd/`)、BD 手册(`docs/database/`)、API 参考
|
||||
4. **调用关系**:要修改文件的调用方和被调用方
|
||||
5. **数据流向**:上游(数据从哪来)→ 当前模块 → 下游(数据到哪去)
|
||||
6. **影响范围**:哪些模块/页面/任务可能受影响
|
||||
|
||||
### 第 3 步:输出「改动前上下文摘要」
|
||||
|
||||
格式:
|
||||
|
||||
```
|
||||
## 改动前上下文摘要
|
||||
|
||||
### 模块职责
|
||||
<模块做什么,在系统中的角色>
|
||||
|
||||
### 历史变更
|
||||
<近期审计记录中的相关改动,特别是踩坑记录>
|
||||
|
||||
### 数据流向
|
||||
上游: <数据来源>
|
||||
当前: <本模块处理>
|
||||
下游: <消费方>
|
||||
|
||||
### 影响范围
|
||||
- <受影响的模块/页面/任务列表>
|
||||
|
||||
### 风险点
|
||||
- <可能的副作用、边界条件、兼容性问题>
|
||||
|
||||
### 建议方案
|
||||
<基于调研结果的实施建议>
|
||||
```
|
||||
|
||||
### 第 4 步:等待用户确认
|
||||
|
||||
输出摘要后,等待用户确认或调整方向,确认后再进入编码实施。
|
||||
|
||||
## 例外(无需执行此流程)
|
||||
|
||||
- 纯格式调整、注释/文档纯文字修改
|
||||
- 用户明确说"直接改/跳过调研"
|
||||
- 新建文件且不涉及已有逻辑
|
||||
69
.cursor/skills/spec-close/SKILL.md
Normal file
69
.cursor/skills/spec-close/SKILL.md
Normal file
@@ -0,0 +1,69 @@
|
||||
---
|
||||
name: spec-close
|
||||
description: /spec-close — Spec 收尾通用流程。从 Claude Code 命令迁移为 Cursor project skill;用户要求执行 spec-close、/spec-close 或相关流程时使用。
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# /spec-close — Spec 收尾通用流程
|
||||
|
||||
当一个功能 spec 开发完成时,执行此收尾检查清单确保质量闭环。
|
||||
|
||||
## 执行步骤
|
||||
|
||||
### 步骤 1:最终测试检查点(必选)
|
||||
|
||||
- 运行 Monorepo 属性测试:`cd /c/NeoZQYY && pytest tests/ -v`
|
||||
- 运行模块单元测试:`cd <模块路径> && pytest tests/ -v`
|
||||
- 确保所有测试通过,有问题询问用户
|
||||
|
||||
### 步骤 2:前后端联调验证(涉及 API + 前端时必选)
|
||||
|
||||
- 启动后端服务,使用测试库验证各端点完整请求-响应链路
|
||||
- 验证 JSON 响应结构与 Schema 定义一致(camelCase 序列化)
|
||||
- 验证权限校验和数据隔离(`SET LOCAL app.current_site_id`)在真实请求中生效
|
||||
- 前端联调验证:确认前端页面能正确调用 API 并渲染数据
|
||||
- 验证空数据/降级场景下前端不崩溃
|
||||
|
||||
### 步骤 3:数据库变更审计与 DDL 合并(涉及 DB 改动时必选)
|
||||
|
||||
- 审计本次实现中对数据库的所有改动(新建表、新增字段、新增索引、FDW 映射变更等)
|
||||
- **必须通过 pg MCP 工具实际执行迁移 SQL**(禁止仅标记完成而不执行)
|
||||
- 执行后用查询验证表/字段/索引已正确创建
|
||||
- RLS 视图双 schema:后端查询 `app.v_*` 视图,新建 DWS RLS 视图时必须同时在原 schema 和 `app` schema 下创建
|
||||
- 合并到主 DDL 基线文件(ETL → `docs/database/ddl/etl_feiqiu__<schema>.sql`,业务 → `docs/database/ddl/zqyy_app__<schema>.sql`)
|
||||
- 编写回滚脚本(逆序 DROP/ALTER)
|
||||
|
||||
### 步骤 4:BD 手册更新(涉及 DB 改动时必选)
|
||||
|
||||
- 业务库 → `docs/database/BD_manual_*.md`
|
||||
- ETL 库 → `apps/etl/connectors/feiqiu/docs/database/<层级>/main/BD_manual_*.md`
|
||||
- FDW → `docs/database/BD_manual_fdw*.md`
|
||||
- 每份手册必须包含:字段明细、约束与索引、验证 SQL(≥3 条)、兼容性影响、回滚策略
|
||||
|
||||
### 步骤 5:项目文档同步更新(按涉及范围裁剪)
|
||||
|
||||
根据改动类型选择需要更新的文档:
|
||||
|
||||
| 文档 | 更新条件 |
|
||||
|------|----------|
|
||||
| 模块 README | 模块内部结构变更时 |
|
||||
| `apps/backend/docs/API-REFERENCE.md` | 新增/修改后端路由时 |
|
||||
| `docs/contracts/openapi/backend-api.json` | 新增/修改 API 端点时 |
|
||||
| `docs/DOCUMENTATION-MAP.md` | 新增任何文档条目时 |
|
||||
|
||||
### 步骤 6:变更审计收口(涉及高风险路径时必选)
|
||||
|
||||
执行 `/audit` 命令完成审计流程。
|
||||
|
||||
### 步骤 7:服务清理(启动了运行时服务时必选)
|
||||
|
||||
- 关闭浏览器实例、停止后端和前端服务、清理资源
|
||||
|
||||
## 按 Spec 类型裁剪
|
||||
|
||||
| 类型 | 必选步骤 |
|
||||
|------|---------|
|
||||
| ETL 类(ODS/DWD/DWS) | 1, 3, 4, 5, 6 |
|
||||
| 后端 API 类 | 1, 2, 5, 6 |
|
||||
| 全栈类(前后端 + DB) | 1, 2, 3, 4, 5, 6 |
|
||||
| 重构类 | 1, 5, 6 |
|
||||
46
.cursorignore
Normal file
46
.cursorignore
Normal file
@@ -0,0 +1,46 @@
|
||||
# Cursor 索引/上下文屏蔽列表
|
||||
# 目的:减少每次对话注入到 system prompt 的无关文件,降低 token 开销
|
||||
# 不影响本地文件浏览和手动 Read
|
||||
|
||||
# ===== AI 历史会话归档(数百个 .md 文件,正常工作不需要 AI 看到) =====
|
||||
docs/ai-env-history/
|
||||
docs/claude-history/
|
||||
|
||||
# ===== 审计变更记录(按需手动 Read,不需要默认索引) =====
|
||||
docs/audit/changes/
|
||||
docs/audit/audit_dashboard.md
|
||||
|
||||
# ===== 临时产物与归档 =====
|
||||
tmp/
|
||||
_DEL/
|
||||
.Deleted/
|
||||
export/
|
||||
reports/
|
||||
scripts/logs/
|
||||
.playwright-mcp/
|
||||
|
||||
# ===== 大型二进制/构建产物 =====
|
||||
node_modules/
|
||||
.venv/
|
||||
venv/
|
||||
dist/
|
||||
build/
|
||||
*.egg-info/
|
||||
htmlcov/
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# ===== 锁文件(很大且不需要 AI 阅读) =====
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
uv.lock
|
||||
|
||||
# ===== 小程序打包产物 =====
|
||||
apps/*.zip
|
||||
apps/miniprogram/.font_patch_tmp/
|
||||
|
||||
# ===== IDE/系统杂项 =====
|
||||
.idea/
|
||||
.vscode/
|
||||
*.lnk
|
||||
*.swp
|
||||
20
.gitignore
vendored
20
.gitignore
vendored
@@ -32,18 +32,18 @@ scripts/logs/
|
||||
|
||||
|
||||
# ===== 环境配置(保留模板) =====
|
||||
.env
|
||||
.env.local
|
||||
!.env.template
|
||||
.env
|
||||
.env.local
|
||||
!.env.template
|
||||
|
||||
# ===== Node =====
|
||||
node_modules/
|
||||
|
||||
# ===== Python 虚拟环境 =====
|
||||
.venv/
|
||||
venv/
|
||||
ENV/
|
||||
env/
|
||||
.venv/
|
||||
venv/
|
||||
ENV/
|
||||
env/
|
||||
|
||||
# ===== Python 构建产物 =====
|
||||
.Python
|
||||
@@ -86,6 +86,12 @@ infra/**/*.secret
|
||||
# ===== Claude Code 本地配置 =====
|
||||
.claude/settings.local.json
|
||||
|
||||
# ===== AI 历史会话归档(个人本地,不入库) =====
|
||||
docs/ai-env-history/sessions/
|
||||
docs/ai-env-history/conversation_index.csv
|
||||
docs/ai-env-history/file_impact_index.csv
|
||||
docs/claude-history/
|
||||
|
||||
# ===== Windows 杂项 =====
|
||||
*.lnk
|
||||
.Deleted/
|
||||
|
||||
53
.ignore
Normal file
53
.ignore
Normal file
@@ -0,0 +1,53 @@
|
||||
# ripgrep / fd / 终端搜索屏蔽列表
|
||||
# 目的:避免全仓 grep 扫描历史归档、缓存、构建产物和大锁文件。
|
||||
|
||||
# AI 历史与审计归档
|
||||
docs/ai-env-history/
|
||||
docs/claude-history/
|
||||
docs/audit/changes/
|
||||
docs/audit/audit_dashboard.md
|
||||
|
||||
# 临时产物与运行日志
|
||||
tmp/
|
||||
_DEL/
|
||||
.Deleted/
|
||||
export/
|
||||
reports/
|
||||
logs/
|
||||
scripts/logs/
|
||||
.playwright-mcp/
|
||||
*.log
|
||||
*.jsonl
|
||||
|
||||
# 依赖、虚拟环境、缓存和构建产物
|
||||
node_modules/
|
||||
.venv/
|
||||
venv/
|
||||
ENV/
|
||||
env/
|
||||
dist/
|
||||
build/
|
||||
.vite/
|
||||
*.egg-info/
|
||||
htmlcov/
|
||||
.coverage
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
pytest-cache-files-*/
|
||||
|
||||
# 大锁文件
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
uv.lock
|
||||
|
||||
# 小程序打包产物
|
||||
apps/*.zip
|
||||
apps/miniprogram/.font_patch_tmp/
|
||||
|
||||
# IDE / 系统杂项
|
||||
.idea/
|
||||
.vscode/
|
||||
*.lnk
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
47
.mcp.json
47
.mcp.json
@@ -1,55 +1,48 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"pg-etl": {
|
||||
"command": "uvx",
|
||||
"args": ["postgres-mcp", "--access-mode=unrestricted"],
|
||||
"env": {
|
||||
"DATABASE_URI": "postgresql://local-Python:Neo-local-1991125@100.64.0.4:5432/etl_feiqiu"
|
||||
},
|
||||
"command": "powershell.exe",
|
||||
"args": ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "C:\\Project\\NeoZQYY\\tools\\codex\\mcp-postgres.ps1", "PG_DSN"],
|
||||
"disabled": true
|
||||
},
|
||||
"pg-etl-test": {
|
||||
"command": "uvx",
|
||||
"args": ["postgres-mcp", "--access-mode=unrestricted"],
|
||||
"env": {
|
||||
"DATABASE_URI": "postgresql://local-Python:Neo-local-1991125@100.64.0.4:5432/test_etl_feiqiu"
|
||||
},
|
||||
"command": "powershell.exe",
|
||||
"args": ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "C:\\Project\\NeoZQYY\\tools\\codex\\mcp-postgres.ps1", "TEST_DB_DSN"],
|
||||
"disabled": false
|
||||
},
|
||||
"pg-app": {
|
||||
"command": "uvx",
|
||||
"args": ["postgres-mcp", "--access-mode=unrestricted"],
|
||||
"env": {
|
||||
"DATABASE_URI": "postgresql://local-Python:Neo-local-1991125@100.64.0.4:5432/zqyy_app"
|
||||
},
|
||||
"command": "powershell.exe",
|
||||
"args": ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "C:\\Project\\NeoZQYY\\tools\\codex\\mcp-postgres.ps1", "APP_DB_DSN"],
|
||||
"disabled": true
|
||||
},
|
||||
"pg-app-test": {
|
||||
"command": "uvx",
|
||||
"args": ["postgres-mcp", "--access-mode=unrestricted"],
|
||||
"env": {
|
||||
"DATABASE_URI": "postgresql://local-Python:Neo-local-1991125@100.64.0.4:5432/test_zqyy_app"
|
||||
},
|
||||
"command": "powershell.exe",
|
||||
"args": ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "C:\\Project\\NeoZQYY\\tools\\codex\\mcp-postgres.ps1", "TEST_APP_DB_DSN"],
|
||||
"disabled": false
|
||||
},
|
||||
"weixin-devtools-mcp": {
|
||||
"command": "cmd",
|
||||
"args": ["/c", "npx", "-y", "weixin-devtools-mcp", "--tools-profile=full", "--ws-endpoint=ws://127.0.0.1:9420"],
|
||||
"command": "C:\\nvm4w\\nodejs\\npx.cmd",
|
||||
"args": ["-y", "weixin-devtools-mcp", "--tools-profile=full", "--ws-endpoint=ws://127.0.0.1:9420"],
|
||||
"env": {
|
||||
"WECHAT_DEVTOOLS_CLI": "C:\\dev\\WechatDevtools\\cli.bat",
|
||||
"WECHAT_DEVTOOLS_PROJECT": "C:\\Project\\NeoZQYY\\apps\\miniprogram"
|
||||
"PATH": "C:\\nvm4w\\nodejs;C:\\Windows\\System32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0",
|
||||
"WECHAT_DEVTOOLS_CLI": "C:\\dev\\wechat-devtools-cli.bat",
|
||||
"WECHAT_DEVTOOLS_PROJECT": "C:\\Project\\NeoZQYY\\apps\\miniprogram\\miniprogram"
|
||||
},
|
||||
"disabled": false
|
||||
},
|
||||
"playwright": {
|
||||
"command": "cmd",
|
||||
"args": ["/c", "npx", "@playwright/mcp@latest"],
|
||||
"command": "C:\\nvm4w\\nodejs\\npx.cmd",
|
||||
"args": ["@playwright/mcp@latest"],
|
||||
"env": {
|
||||
"PATH": "C:\\nvm4w\\nodejs;C:\\Windows\\System32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0"
|
||||
},
|
||||
"disabled": false
|
||||
},
|
||||
"openapi": {
|
||||
"command": "uv",
|
||||
"command": "C:\\Dev\\miniconda3\\Scripts\\uv.exe",
|
||||
"args": [
|
||||
"tool", "run",
|
||||
"--python", "3.12",
|
||||
"--from", "awslabs.openapi-mcp-server@latest",
|
||||
"--with", "fastmcp>=2.14.0,<3.0.0",
|
||||
"awslabs.openapi-mcp-server.exe",
|
||||
|
||||
130
AGENTS.md
Normal file
130
AGENTS.md
Normal file
@@ -0,0 +1,130 @@
|
||||
# AGENTS.md
|
||||
|
||||
NeoZQYY Monorepo 顶层规则。子模块详细规范见各自 `apps/*/AGENTS.md`、`docs/database/`、`apps/etl/connectors/feiqiu/AGENTS.md`。
|
||||
|
||||
## 语言(强制)
|
||||
|
||||
始终中文:对话、解释、代码注释、commit message(中文描述 + 英文 Co-Authored-By 签名)、PR、审计、文档、错误日志。
|
||||
保持原文:变量/函数/类名、第三方 API 字段名、CLI 命令、技术术语。
|
||||
禁止英文段落或英文标题。
|
||||
|
||||
## CLI / Shell 中文处理
|
||||
|
||||
- Python `encoding="utf-8"` / `PYTHONUTF8=1`;CSV 给 Excel 用 `utf-8-sig`;PowerShell/Node 不依赖系统 ANSI。
|
||||
- 终端中文乱码时,不当作事实;先调编码重跑,或说明"终端编码异常,结果需复核"。
|
||||
- Shell 路径含中文/空格/特殊字符必须加引号;复杂中文输出走脚本或结构化 API。
|
||||
|
||||
## 项目概览
|
||||
|
||||
NeoZQYY = 台球门店全栈数据平台。多门店隔离(`site_id` + RLS),中文领域语言,CNY,金额 `numeric(2)`。
|
||||
|
||||
| 目录 | 用途 |
|
||||
|------|------|
|
||||
| `apps/etl/connectors/feiqiu/` | 飞球 ETL:API → ODS → DWD → DWS |
|
||||
| `apps/backend/` | FastAPI(JWT 双认证 + WebSocket + AI) |
|
||||
| `apps/miniprogram/` | 微信小程序 C 端(Donut + TDesign) |
|
||||
| `apps/admin-web/` | 系统管理后台(开发/运维视角,操作 ETL 库) |
|
||||
| `apps/tenant-admin/` | 租户管理后台(门店管理员视角,操作业务库) |
|
||||
| `apps/demo-miniprogram/` | MOCK 标杆小程序(样式校对,禁改) |
|
||||
| `apps/mcp-server/` | MCP Server(PostgreSQL 只读) |
|
||||
| `packages/shared/` | 跨项目共享包 |
|
||||
| `db/` | DDL(`schemas/`)+ 迁移 + FDW |
|
||||
| `tools/` | 通用工具 |
|
||||
| `scripts/ops/` | 日常运维脚本 |
|
||||
|
||||
技术栈:Python 3.10+ uv workspace、React+Vite+AntD、PostgreSQL 四库(`etl_feiqiu`/`test_etl_feiqiu`/`zqyy_app`/`test_zqyy_app`,DSN `PG_DSN`/`APP_DB_DSN`)。配置分层 `.env` < `.env.local` < env < CLI。
|
||||
|
||||
## 数据库
|
||||
|
||||
- 六层 Schema:`meta` → `ods` → `dwd` → `core` → `dws` → `app`(RLS 视图)
|
||||
- 跨库:`zqyy_app` 通过 FDW 只读映射 `etl_feiqiu.app`
|
||||
- RLS:`site_id` + `app.current_site_id` 会话变量
|
||||
- **RLS 双 Schema 踩坑**:DWS/DWD 表的 RLS 视图必须同时建在 `dws` 和 `app` schema,后端走 `app.v_*`。只建 `dws` 会让后端查询失败。回滚需逆序 DROP。
|
||||
|
||||
## 飞球数据规范(速查)
|
||||
|
||||
- `consume_money` 禁止直接计算 → 用 `items_sum` 拆分字段
|
||||
- 取数优先级:DWS > DWD > 禁止 ODS
|
||||
- 参考优先级:DWD-DOC > DWS 权威规范 > BD 手册 > ETL 任务文档 > DDL 注释
|
||||
- `_archived/` 目录禁止读取或参考
|
||||
- 完整规则见 `apps/etl/connectors/feiqiu/AGENTS.md`
|
||||
|
||||
## 文件归属
|
||||
|
||||
| 类型 | 位置 |
|
||||
|------|------|
|
||||
| 模块内文档/测试/脚本 | 模块内 `docs/`、`tests/`、`scripts/` |
|
||||
| 跨模块文档 | 根 `docs/` |
|
||||
| Monorepo 守护测试 | 根 `tests/` |
|
||||
| 日常运维脚本 | `scripts/ops/` |
|
||||
| 通用工具 | `tools/`(按类型分子目录) |
|
||||
| 审计记录 | 根 `docs/audit/changes/<YYYY-MM-DD>__<slug>.md`(禁止写子模块) |
|
||||
| 数据库文档 | 根 `docs/database/` |
|
||||
| 归档/待删 | `_DEL/`(保持原路径,定期清理) |
|
||||
|
||||
`docs/audit/audit_dashboard.md` 由 `scripts/audit/gen_audit_dashboard.py` 生成,勿手动编辑。
|
||||
|
||||
## 编码前需求审问(强制)
|
||||
|
||||
新建功能/接口、重构、多模块联动、需求模糊时:
|
||||
1. 不立即动手,先提问循环(每轮 3-5 个问题)
|
||||
2. 必问:用户角色、核心操作、数据写入/展示/来源、错误/成功反馈、认证权限、存储(哪库/新表)、终端适配、边界(并发/幂等/超时)
|
||||
3. 输出「需求确认摘要」,用户确认后实施
|
||||
|
||||
例外:用户说"直接改/跳过审问"、Bug 有明确复现步骤、纯格式调整、已有 spec
|
||||
|
||||
## 逻辑改动前置调研(强制)
|
||||
|
||||
任何逻辑改动(ETL/业务规则/API/数据模型/前端交互):
|
||||
1. 委托 Explore 子代理调研:目标模块、`docs/audit/changes/` 历史、调用方/被调用方、数据流向、影响范围
|
||||
2. 输出「改动前上下文摘要」,用户确认后实施
|
||||
|
||||
流程:需求审问 → 确认 → 前置调研 → 确认 → 编码
|
||||
|
||||
例外:纯格式/注释/文档修改、用户说"直接改/跳过调研"、新建独立文件
|
||||
|
||||
## 改动后验证(强制)
|
||||
|
||||
1. 运行相关测试(单元/集成/lint),不能运行需说明原因
|
||||
2. 输出 diff 摘要(文件清单 + 每个改动要点)
|
||||
3. 列出未覆盖风险点(未测路径、副作用、需人工验证场景)
|
||||
|
||||
例外:纯格式/文档/注释、用户说"跳过验证"
|
||||
|
||||
## 数据库 Schema 变更
|
||||
|
||||
修改 PostgreSQL schema(迁移/DDL/表定义/ORM)时,必须同步 `docs/database/`:变更说明、兼容性影响、回滚策略、≥3 条校验 SQL。
|
||||
|
||||
## 测试环境
|
||||
|
||||
1. `load_dotenv` 加根 `.env`;必需变量缺失立即报错,禁止静默回退空串
|
||||
2. cwd 与正式一致:ETL → `apps/etl/connectors/feiqiu/`、后端 → `apps/backend/`
|
||||
3. 配置走 `AppConfig.load()`,不得为测试构造简化配置
|
||||
4. 用测试库(`TEST_DB_DSN`),禁止连正式库
|
||||
5. 属性测试分组执行(`-k` 筛选),禁止一次性全跑;hypothesis 默认 `max_examples=100`
|
||||
|
||||
例外:用户指定简化环境、纯单元测试用 FakeDB/FakeAPI、`--dry-run` CLI 验证
|
||||
|
||||
## 脚本规范
|
||||
|
||||
- 复杂操作写 Python 脚本,避免复杂 shell
|
||||
- 一次性运维 → `scripts/ops/`;模块专属 → 模块内 `scripts/`
|
||||
- `scripts/ops/` 不在 uv workspace 内,导入 ETL 纯函数用 `importlib.util` + stub(参考 `scripts/ops/backfill_finance_area_daily.py`)
|
||||
|
||||
## 子代理
|
||||
|
||||
- 委托:批量读 ≥3 文件、大范围搜索、不熟悉模块探索、多步 shell
|
||||
- 主流程直接处理:单文件、单命令、小范围精确搜索
|
||||
|
||||
## 审计
|
||||
|
||||
任何逻辑改动必须可追溯、可验证、可回滚。
|
||||
|
||||
完成一轮后用 `/audit`:
|
||||
1. `python scripts/audit/prescan.py` 预扫描(自动识别+分类+合规,零 token)
|
||||
2. 补充语义上下文(从对话提取每变更原因)
|
||||
3. 委托子代理写 `docs/audit/changes/`
|
||||
4. 补齐文档同步
|
||||
5. `python scripts/audit/gen_audit_dashboard.py` 刷新一览表
|
||||
|
||||
`prescan.py --files` 参数:git status 含大量历史未提交时,只传本次会话文件列表。
|
||||
52
apps/backend/AGENTS.md
Normal file
52
apps/backend/AGENTS.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# AGENTS.md - Backend (FastAPI)
|
||||
|
||||
进入本目录时自动加载。
|
||||
|
||||
## 架构模式
|
||||
|
||||
### 全局响应包装
|
||||
|
||||
`ResponseWrapperMiddleware` 把所有 2xx 响应包为 `{ "code": 0, "data": <payload> }`。
|
||||
非 2xx 响应保持原样。前端统一通过 `response.data` 解包。
|
||||
|
||||
### 序列化
|
||||
|
||||
`CamelModel` 基类:snake_case -> camelCase 自动转换(小程序 API 用)。
|
||||
后端代码始终用 snake_case,JSON 输出自动转驼峰。
|
||||
|
||||
### JWT 双认证
|
||||
|
||||
| 认证方式 | 用途 | 表 | JWT aud |
|
||||
|---------|------|-----|---------|
|
||||
| 用户名+密码 | admin-web 登录 | `auth.admin_users` | `admin` |
|
||||
| 微信 code | 小程序登录 | `auth.users` | `miniapp` |
|
||||
| 用户名+密码 | tenant-admin 登录 | `auth.tenant_admins` | `tenant-admin` |
|
||||
|
||||
待审核用户有 limited token(仅可访问审核状态接口)。
|
||||
|
||||
### AI 集成
|
||||
|
||||
8 个千问应用通过 DashScope SDK:
|
||||
chat / finance / clue / analysis / tactics / note / customer / consolidate
|
||||
|
||||
特性:熔断(连续失败自动断路)、限流(每分钟/每日)、预算追踪、对话缓存。
|
||||
|
||||
### 后台服务(lifespan)
|
||||
|
||||
- `TaskQueue`:按 site_id 消费,FIFO 队列
|
||||
- `Scheduler`:读 `meta.scheduled_tasks` 自动入队
|
||||
- 4 个触发器:日结/月结/工资/关系指数
|
||||
|
||||
### 数据库访问
|
||||
|
||||
- 业务库通过 `APP_DB_DSN` 直连 `zqyy_app`
|
||||
- ETL 数据通过 FDW 映射的 `app.v_*` RLS 视图访问
|
||||
- 查询前必须 `SET LOCAL app.current_site_id = :site_id`
|
||||
|
||||
## 测试
|
||||
|
||||
```bash
|
||||
cd apps/backend && pytest tests/ -v
|
||||
```
|
||||
|
||||
使用测试库(`TEST_APP_DB_DSN`),禁止连正式库。
|
||||
18
apps/demo-miniprogram/AGENTS.md
Normal file
18
apps/demo-miniprogram/AGENTS.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# AGENTS.md - demo-miniprogram
|
||||
|
||||
**禁止删除或移入 `_DEL/`。**
|
||||
|
||||
本目录是假数据 MOCK 版小程序,使用硬编码数据驱动所有页面,不连接后端 API。
|
||||
|
||||
## 用途
|
||||
|
||||
- 页面样式和展示格式的**标杆校对**:开发 `apps/miniprogram/` 时,以本目录的 UI 效果为参考基准
|
||||
- 快速预览各页面在不同数据状态下的渲染效果,无需启动后端服务
|
||||
|
||||
## 与 miniprogram 的关系
|
||||
|
||||
| | `apps/miniprogram/` | `apps/demo-miniprogram/` |
|
||||
|--|---------------------|--------------------------|
|
||||
| 数据来源 | 后端 API | 硬编码假数据 |
|
||||
| 用途 | 生产代码 | 样式参考 / UI 校对 |
|
||||
| 可独立运行 | 需后端 | 可独立运行 |
|
||||
81
apps/etl/connectors/feiqiu/AGENTS.md
Normal file
81
apps/etl/connectors/feiqiu/AGENTS.md
Normal file
@@ -0,0 +1,81 @@
|
||||
# AGENTS.md - ETL Feiqiu Connector
|
||||
|
||||
进入本目录时自动加载。包含 DWD 和 DWS 层的强制业务规则。
|
||||
|
||||
## DWD-DOC 标杆文档(权威数据源)
|
||||
|
||||
`docs/reports/DWD-DOC/` 是业务模型与财务数据的权威标杆文档。所有涉及金额口径、支付渠道、消费链路、账务公式、字段语义的开发工作,必须以此目录为第一参考源。
|
||||
|
||||
### 文档清单
|
||||
|
||||
| 文件 | 内容 | 关键规则 |
|
||||
|------|------|----------|
|
||||
| `01-business-panorama.md` | 消费链路 + 优惠机制 + 消费场景 | settle_type 枚举、助教费用拆分、团购券三层价格 |
|
||||
| `02-accounting-panorama.md` | 支付渠道 + 对账公式 + consume_money 口径 | 支付渠道恒等式、F2 三期公式 |
|
||||
| `03-financial-panorama.md` | 收入构成 + 储值卡资金流 + 对账矩阵 | 平台结算互斥关系 |
|
||||
| `04-dimension-panorama.md` | 维度表与主数据全景 | SCD2 维度取值规则 |
|
||||
| `05-f2-balance-audit.md` | F2 收支平衡公式专项 | 三期公式 + 139 笔失败根因 |
|
||||
| `06-calibration-checklist.md` | 校准清单 + 验证 SQL | 全部验证公式集中 |
|
||||
| `consume/consume-money-caliber.md` | consume_money 口径变化时间线 | 三种口径(A/B/C)定义与切换时间点 |
|
||||
|
||||
### DWD 强制规则(12 条)
|
||||
|
||||
1. **consume_money 禁止直接用于计算**:存在三种历史口径(A/B/C)混合,DWS 层及下游统一使用 `items_sum = table_charge_money + goods_money + assistant_pd_money + assistant_cx_money + electricity_money`
|
||||
2. **助教费用必须拆分**:使用 `assistant_pd_money`(陪打)和 `assistant_cx_money`(超休),禁止使用 `service_fee` / `ASSISTANT_BASE` / `ASSISTANT_BONUS`(`service_fee` 仅在平台结算表中表示"平台服务费",语义不同)
|
||||
3. **支付渠道恒等式**:`balance_amount = recharge_card_amount + gift_card_amount`(100% 成立),三者不可重复计算
|
||||
4. **settle_type 过滤**:正向交易取 `IN (1, 3)`,本表无 `is_delete` 字段
|
||||
5. **电费未启用**:`electricity_money` 全为 0,`gross_amount` 不含电费是正确的
|
||||
6. **折扣互斥**:`discount_manual`(大客户优惠)与 `discount_other` 互斥,两者之和 = `adjust_amount`
|
||||
7. **现金流互斥**:`cash_inflow_total` 中 `platform_settlement_amount` 和 `groupbuy_pay_amount` 互斥
|
||||
8. **废单判断**:使用 `dwd_assistant_service_log_ex.is_trash`,`dwd_assistant_trash_event` 已废弃
|
||||
9. **储值卡字段命名**:DWS 层使用 `balance_pay`/`recharge_card_pay`/`gift_card_pay`;财务日报用 `recharge_card_consume`
|
||||
10. **会员字段断档(DQ-6)**:`settlement_head.member_phone/member_name` 自 2025-12 起全为 NULL -> 通过 `member_id` LEFT JOIN `dwd.dim_member`(`scd2_is_current=1`)
|
||||
11. **会员卡字段断档(DQ-7)**:`settlement_head.member_card_type_name` 自 2025-07-21 起全为 NULL -> 通过 `member_id` LEFT JOIN `dwd.dim_member_card_account`(`scd2_is_current=1`)。通用规则:结算单上所有会员冗余字段均不可靠
|
||||
12. **支付方式拆分(DQ-8)**:`dwd_settlement_head_ex.cash_amount`/`online_amount` 不可靠。正确来源是 `dwd_payment` 表:`payment_method=2` 现金,`payment_method=4` 扫码。通过 `relate_type=2` + `relate_id` 关联结算单
|
||||
|
||||
## DWS 层权威规范
|
||||
|
||||
> DWD 12 条在 DWS 层同样生效。冲突时以 DWD-DOC 为准。
|
||||
|
||||
### 幂等更新策略
|
||||
|
||||
- 汇总表默认 delete-before-insert(按日期范围 + site_id 先删后插)
|
||||
- 库存表使用 upsert(`ON CONFLICT DO UPDATE`)
|
||||
- 禁止 TRUNCATE
|
||||
|
||||
### 课程类型与定价
|
||||
|
||||
- 课程类型通过 `cfg_skill_type` 映射(`skill_id` -> `course_type_code`:BASE/BONUS/ROOM),禁止硬编码
|
||||
- 定价通过 `cfg_assistant_level_price` 按 SCD2 生效期 as-of join,禁止硬编码价格
|
||||
- 包厢课统一 138 元/小时(`dws.salary.room_course_price`)
|
||||
|
||||
### 绩效档位与工资
|
||||
|
||||
- 绩效档位通过 `cfg_performance_tier` 按有效业绩小时数匹配 `[min_hours, max_hours)` 区间
|
||||
- 新入职折算:入职日期在当月 1 日后按日均 × 30 定档;> 25 日最高 T2
|
||||
- 奖金通过 `cfg_bonus_rules`:SPRINT 不累计取最高档,TOP_RANK 按排名(1000/600/400 元)
|
||||
- 排名使用 `calculate_rank_with_ties()`,相同业绩并列
|
||||
|
||||
### 会员与散客
|
||||
|
||||
- 散客:`member_id <= 0`,不计入会员统计(但计入助教业绩)
|
||||
- 客户分层:高价值(90 天 >= 3 次且 >= 1000 元)-> 中等 -> 低活跃 -> 流失
|
||||
- 会员信息一律通过 ID 关联维度表
|
||||
|
||||
### 时间窗口与调度
|
||||
|
||||
- 滚动窗口标准集:7/10/15/30/60/90 天
|
||||
- 月度任务宽限期:月初前 5 天可处理上月数据
|
||||
- 工资计算周期:月初前 5 天运行
|
||||
|
||||
### 指数参数
|
||||
|
||||
- 所有权重和阈值通过 `cfg_index_parameters` 按 `index_type`(WBI/NCI/RS/OS/MS/ML/SPI)加载,禁止硬编码
|
||||
|
||||
### 台桌分类
|
||||
|
||||
- `cfg_area_category` 仅精确匹配 + 兜底:BILLIARD/SNOOKER/OTHER。`BILLIARD_VIP` 已废弃
|
||||
|
||||
### 参考优先级
|
||||
|
||||
DWD-DOC > DWS 权威规范 > BD 手册 > ETL 任务文档 > 业务规则文档 > DDL 注释
|
||||
60
db/AGENTS.md
Normal file
60
db/AGENTS.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# AGENTS.md - Database (DDL / Migrations / Seeds)
|
||||
|
||||
进入本目录时自动加载。
|
||||
|
||||
## Schema 变更规则
|
||||
|
||||
修改任何影响 PostgreSQL schema 的内容(迁移脚本/DDL/表定义)时,必须同步更新 `docs/database/`:
|
||||
|
||||
1. **变更说明**:新增/修改/删除的表、字段、约束、索引
|
||||
2. **兼容性**:对 ETL、后端 API、小程序字段映射的影响
|
||||
3. **回滚策略**:如何撤销(DDL 回滚 / 数据回填)
|
||||
4. **验证步骤**:至少 3 条校验 SQL
|
||||
|
||||
## RLS 视图双 Schema 规则
|
||||
|
||||
新建 DWS/DWD 表的 RLS 视图必须同时在原 schema(如 `dws`)和 `app` schema 创建:
|
||||
|
||||
```sql
|
||||
-- 1. 原 schema
|
||||
CREATE VIEW dws.v_xxx AS SELECT ... WHERE site_id = current_setting('app.current_site_id')::int;
|
||||
|
||||
-- 2. app schema(后端通过此路径访问)
|
||||
CREATE VIEW app.v_xxx AS SELECT ... WHERE site_id = current_setting('app.current_site_id')::int;
|
||||
```
|
||||
|
||||
回滚需逆序 DROP 两个 schema 的视图。只在原 schema 创建会导致后端查询失败。
|
||||
|
||||
## 目录结构
|
||||
|
||||
```text
|
||||
db/
|
||||
├── etl_feiqiu/
|
||||
│ ├── schemas/ # 权威 DDL — 六层完整定义(meta/ods/dwd/core/dws/app)
|
||||
│ ├── migrations/ # 未来增量迁移(v1 已全部归档)
|
||||
│ ├── ods/ # ODS 补充脚本
|
||||
│ └── scripts/ # 测试数据库脚本
|
||||
├── zqyy_app/
|
||||
│ ├── schemas/ # 权威 DDL — 三层完整定义(public/auth/biz)
|
||||
│ ├── migrations/ # 未来增量迁移(v1 已全部归档)
|
||||
│ └── scripts/ # 测试数据库脚本
|
||||
├── fdw/ # FDW 跨库只读映射(正向 + 反向 + 测试环境)
|
||||
└── _archived/ # 归档(v1 迁移 39 个、旧基线)
|
||||
```
|
||||
|
||||
v1 阶段种子数据已合并进 `schemas/` 对应 DDL 文件末尾,不再单独维护。
|
||||
|
||||
## DDL 刷新
|
||||
|
||||
修改 schema 后,重新生成完整 DDL:
|
||||
|
||||
```bash
|
||||
PYTHONUTF8=1 python tools/db/gen_consolidated_ddl.py
|
||||
```
|
||||
|
||||
输出到 `docs/database/ddl/`,然后复制到 `db/*/schemas/` 保持同步。
|
||||
|
||||
## 测试规范
|
||||
|
||||
- 数据库操作使用测试库(`TEST_DB_DSN` / `TEST_APP_DB_DSN`),禁止连正式库
|
||||
- 迁移脚本在测试库执行后需验证表结构
|
||||
@@ -0,0 +1,60 @@
|
||||
# 变更审计记录:Codex 深度迁移与 Claude 历史摘要归档
|
||||
|
||||
| 字段 | 值 |
|
||||
|------|-----|
|
||||
| 日期 | 2026-04-29 03:57:55 |
|
||||
|
||||
## 操作摘要
|
||||
|
||||
本轮将 Claude Code 的项目规则、用户习惯、skills、agents、rules 和 NeoZQYY 项目历史会话迁移到 Codex 可读取的结构。迁移目标是让 Codex 尊重用户此前的使用习惯,并在后续修改代码时能追溯“哪次 Claude 会话改了什么、影响了什么”。
|
||||
|
||||
迁移没有把 Claude 原始对话全文注入 Prompt,而是生成脱敏摘要、会话索引和文件反向索引,避免过时信息和敏感信息污染日常上下文。
|
||||
|
||||
## 变更文件
|
||||
|
||||
### 新增
|
||||
|
||||
- `AGENTS.md`:根项目 Codex 工作规范。
|
||||
- `apps/backend/AGENTS.md`:后端模块 Codex 规则。
|
||||
- `apps/etl/connectors/feiqiu/AGENTS.md`:ETL 飞球模块 Codex 规则。
|
||||
- `apps/demo-miniprogram/AGENTS.md`:demo 小程序模块保护规则。
|
||||
- `db/AGENTS.md`:数据库目录 Codex 规则。
|
||||
- `tools/codex/mcp-postgres.ps1`:Codex PostgreSQL MCP 启动脚本,按变量名读取 `.env` / `.env.local` / 当前环境。
|
||||
- `tools/codex/migrate_claude_assets.py`:Claude 资产迁移脚本。
|
||||
- `docs/codex_migration.md`:Codex 迁移状态说明。
|
||||
- `docs/claude-history/`:Claude 会话摘要归档、会话索引和文件反向索引。
|
||||
|
||||
## 改动注解
|
||||
|
||||
- `tools/codex/migrate_claude_assets.py`:实现可重复迁移流程,转换 Claude skills、agents、rules,生成全局 Codex 习惯文件,并对 Claude JSONL 历史做脱敏摘要和索引。
|
||||
- `tools/codex/mcp-postgres.ps1`:避免把数据库 DSN 明文写入 Codex 配置,支持 `-ValidateOnly` 做环境巡检。
|
||||
- `docs/claude-history/session_index.csv`:记录 94 个 Claude 会话的时间范围、影响范围、风险标签和摘要文件。
|
||||
- `docs/claude-history/file_index.csv`:记录 246 个被编辑文件与 Claude 会话的关联,支持按文件追溯。
|
||||
- `docs/codex_migration.md`:同步迁移结果、验证方式和追溯入口。
|
||||
- `db/AGENTS.md`:高风险目录规则文件,仅迁移原 Claude DB 指令,没有修改 DDL 或迁移 SQL。
|
||||
|
||||
## 数据库变更
|
||||
|
||||
无数据库 schema 变更。未新增迁移 SQL,未修改 DDL。
|
||||
|
||||
## 风险与回滚
|
||||
|
||||
- 中:Claude 历史摘要为脚本自动提取,可能遗漏自然语言中描述的影响。回溯关键问题时仍需查看原始 JSONL、git diff、审计记录和测试结果。
|
||||
- 中:Claude skill 中部分内容包含 Claude Code 专属命令或 agent 概念,已迁移为 Codex skill/reference,但使用时需要按 Codex 工具等价替换。
|
||||
- 低:Codex hooks 未默认启用,原 Claude hooks 的强校验不是一比一恢复;当前由 AGENTS 规则和审计流程承接。
|
||||
- 回滚:删除新增的 `AGENTS.md` / `docs/claude-history/` / `docs/codex_migration.md` / `tools/codex/*`,并恢复 `C:\Users\Administrator\.codex` 下自动生成前的备份。
|
||||
|
||||
## 验证
|
||||
|
||||
- `python scripts/audit/prescan.py --files "<本次迁移文件列表>"`:审计预扫描通过,未发现缺失文档同步。
|
||||
- `python -m py_compile tools/codex/migrate_claude_assets.py`:迁移脚本语法通过。
|
||||
- `powershell.exe -NoProfile -ExecutionPolicy Bypass -File tools\codex\mcp-postgres.ps1 TEST_DB_DSN -ValidateOnly`:PostgreSQL MCP 启动配置检查通过。
|
||||
- Codex skill 校验:12/12 个迁移后的 skill 通过 `quick_validate.py`。
|
||||
- 历史归档校验:生成 94 个会话摘要、94 条会话索引、246 个文件反向索引。
|
||||
- 敏感信息扫描:生成的历史摘要、迁移说明和 Codex skills 未发现常见 DSN/API key/密码形态。
|
||||
|
||||
## 合规检查
|
||||
|
||||
- 文档同步:已同步到 `docs/codex_migration.md` 和 `docs/claude-history/README.md`。
|
||||
- 数据库文档:不适用,本轮无 schema 变更。
|
||||
- 审计记录:已落盘。
|
||||
47
docs/audit/changes/2026-05-01__cursor_migration.md
Normal file
47
docs/audit/changes/2026-05-01__cursor_migration.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# 变更审计记录:Cursor AI 开发环境迁移
|
||||
|
||||
| 字段 | 值 |
|
||||
|------|-----|
|
||||
| 日期 | 2026-05-02 00:05:37 |
|
||||
|
||||
## 操作摘要
|
||||
|
||||
本轮将 Claude Code 与 Codex 中已经沉淀的 AI 开发习惯迁移到 Cursor 原生结构。迁移目标是让 Cursor 继承中文沟通、前置调研、验证、审计、子代理审查、MCP 测试库优先和历史追溯习惯,同时避免把长规则和原始对话全文堆入常规上下文。
|
||||
|
||||
## 变更文件
|
||||
|
||||
### 新增/修改
|
||||
|
||||
- `tools/cursor/migrate_ai_environment.py`:可重复迁移脚本。
|
||||
- `.cursor/rules/`:Cursor 项目级短规则。
|
||||
- `.cursor/skills/`:审计、前置调研、文档同步和 spec 收尾流程技能。
|
||||
- `.cursor/hooks.json`、`.cursor/hooks/ai_env_guard.py`:轻量提醒/保护 hooks。
|
||||
- `docs/cursor_migration.md`:Cursor 迁移说明。
|
||||
- `docs/ai-env-history/`:Claude/Codex/Cursor 原始对话精简索引和逐会话摘要。
|
||||
- `AGENTS.md` / `CLAUDE.md`:补充 CLI / Shell 中文编码处理规则,要求 UTF-8、乱码复核、中文路径正确加引号。
|
||||
- `C:\Users\Administrator\.cursor\skills`:用户级技能迁移。
|
||||
- `C:\Users\Administrator\.cursor\agents`:用户级子代理迁移。
|
||||
|
||||
## 数据库变更
|
||||
|
||||
无数据库 schema 变更。未执行 DDL,未修改迁移 SQL。
|
||||
|
||||
## 风险与回滚
|
||||
|
||||
- 中:Cursor hooks 与 Claude hooks 事件字段不完全一致,本轮只启用轻量提醒和 ask,不启用强阻断。
|
||||
- 中:历史摘要从原始 JSONL 自动分析,仍可能遗漏自然语言里的隐含决策;关键判断需回看原文和当前代码。
|
||||
- 低:用户级 Cursor 资产覆盖前已备份,可从 manifest 恢复。
|
||||
- 低:Windows 终端中文输出可能受代码页影响;已在根规则和 Cursor 核心规则中要求 UTF-8、乱码不采信、必要时重跑。
|
||||
|
||||
## 验证
|
||||
|
||||
- 运行 `python tools/cursor/migrate_ai_environment.py --check` 校验生成结构。
|
||||
- 运行 `python scripts/audit/prescan.py --files "<本轮迁移文件>"` 做审计预扫描。
|
||||
- 解析 `.mcp.json`、`.cursor/hooks.json`、skill/subagent frontmatter。
|
||||
- 运行 `.venv\Scripts\python.exe scripts\audit\prescan.py --files "AGENTS.md,CLAUDE.md,.cursor/rules/neozqyy-core.mdc,tools/cursor/migrate_ai_environment.py"` 校验中文规则增量。
|
||||
|
||||
## 合规检查
|
||||
|
||||
- 文档同步:已新增 `docs/cursor_migration.md` 和 `docs/ai-env-history/README.md`。
|
||||
- 数据库文档:不适用,本轮无 schema 变更。
|
||||
- 审计记录:已落盘。
|
||||
108
docs/codex_migration.md
Normal file
108
docs/codex_migration.md
Normal file
@@ -0,0 +1,108 @@
|
||||
# Codex 迁移配置说明
|
||||
|
||||
本仓库已从 Claude Code 的项目配置迁移到 Codex 可识别的配置结构。
|
||||
|
||||
## 已迁移内容
|
||||
|
||||
| Claude Code | Codex | 状态 |
|
||||
|-------------|-------|------|
|
||||
| 根目录 `CLAUDE.md` | 根目录 `AGENTS.md` | 已迁移 |
|
||||
| 子目录 `CLAUDE.md` | 子目录 `AGENTS.md` | 已迁移 |
|
||||
| `.mcp.json` | `C:\Users\Administrator\.codex\config.toml` | 已迁移 |
|
||||
| PostgreSQL MCP 明文 DSN | `tools/codex/mcp-postgres.ps1` 读取 `.env` / `.env.local` | 已改为间接读取 |
|
||||
| `.claude/settings.local.json` 中的项目环境变量 | `shell_environment_policy.set` | 已迁移核心变量 |
|
||||
|
||||
## Codex 全局配置
|
||||
|
||||
Codex 当前读取:
|
||||
|
||||
```text
|
||||
C:\Users\Administrator\.codex\config.toml
|
||||
```
|
||||
|
||||
已配置的 MCP server:
|
||||
|
||||
- `pg-etl`:ETL 正式库,默认禁用
|
||||
- `pg-etl-test`:ETL 测试库,默认启用
|
||||
- `pg-app`:业务正式库,默认禁用
|
||||
- `pg-app-test`:业务测试库,默认启用
|
||||
- `weixin-devtools-mcp`:微信开发者工具 MCP
|
||||
- `playwright`:Playwright MCP
|
||||
- `openapi`:后端 OpenAPI MCP
|
||||
|
||||
已配置的 shell 环境变量:
|
||||
|
||||
- `NEOZQYY_ROOT=C:\Project\NeoZQYY`
|
||||
- `VIRTUAL_ENV=C:\Project\NeoZQYY\.venv`
|
||||
|
||||
未覆盖全局 `PATH`,避免影响 Windows、Node.js、uv 等系统命令解析。
|
||||
|
||||
数据库 MCP 的 DSN 不直接写入 Codex 全局配置,启动时由 `tools/codex/mcp-postgres.ps1` 按以下优先级解析:
|
||||
|
||||
```text
|
||||
.env < .env.local < 当前进程环境变量
|
||||
```
|
||||
|
||||
## 验证命令
|
||||
|
||||
```powershell
|
||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File tools\codex\mcp-postgres.ps1 TEST_DB_DSN -ValidateOnly
|
||||
powershell.exe -NoProfile -ExecutionPolicy Bypass -File tools\codex\mcp-postgres.ps1 TEST_APP_DB_DSN -ValidateOnly
|
||||
```
|
||||
|
||||
TOML 静态校验:
|
||||
|
||||
```powershell
|
||||
@'
|
||||
import pathlib, tomllib
|
||||
path = pathlib.Path.home().joinpath(".codex/config.toml")
|
||||
data = tomllib.loads(path.read_text(encoding="utf-8-sig"))
|
||||
print(sorted(data.get("mcp_servers", {}).keys()))
|
||||
'@ | .venv\Scripts\python.exe -
|
||||
```
|
||||
|
||||
## 尚未一比一迁移的内容
|
||||
|
||||
Claude Code 的 `.claude/commands/*.md` 与 `.claude/hooks/*.py` 不完全等价于 Codex 当前稳定能力:
|
||||
|
||||
- `/audit` 等自定义命令已经沉淀在根目录 `AGENTS.md` 的审计流程中,可直接要求 Codex “执行审计流程”。
|
||||
- Claude 的 `PreToolUse` / `PostToolUse` 钩子在 Codex 上仍属于实验能力,且 Windows 兼容性需要按 Codex 版本再验证;本次未默认启用,避免影响日常编辑。
|
||||
- 如需恢复钩子式强校验,优先迁移 `Stop` 类审计/验证提醒,再评估文件读写前置拦截。
|
||||
|
||||
## 用户级 Claude 资产迁移状态
|
||||
|
||||
以下原始内容仍保留在 `C:\Users\Administrator\.claude` 作为备份和源材料,同时已经完成 Codex 侧迁移:
|
||||
|
||||
- `skills/`:10 个 Claude skill 已迁移到 `C:\Users\Administrator\.codex\skills`。
|
||||
- `agents/`:8 个 Claude agent 已迁移为 Codex skill `claude-agent-roles`。
|
||||
- `rules/`:`python`、`typescript`、`web`、`zh` 四组规则已迁移为 Codex skill `claude-rules-reference`,并提炼进 `C:\Users\Administrator\.codex\AGENTS.md`。
|
||||
- `projects/C--Project-NeoZQYY/`:NeoZQYY 项目历史会话 94 个 JSONL 文件已摘要归档到 `docs/claude-history/`。
|
||||
- `history.jsonl`、`plans/`、`file-history/` 等 Claude 本地历史与辅助状态。
|
||||
|
||||
Codex 不会自动学习 Claude Code 的原始历史对话,也不会自动加载 `C:\Users\Administrator\.claude\skills`。当前 Codex 可稳定读取的是:
|
||||
|
||||
- 仓库内 `AGENTS.md`
|
||||
- `C:\Users\Administrator\.codex\config.toml`
|
||||
- `C:\Users\Administrator\.codex\skills`
|
||||
- 当前会话上下文与 Codex 自己生成的 memory
|
||||
- `docs/claude-history/` 中的历史摘要索引
|
||||
|
||||
历史会话没有全文注入 Prompt,只保留脱敏摘要和索引。需要追溯时先查索引,再按需打开对应摘要;只有必要时才读取原始 JSONL。
|
||||
|
||||
## 使用提醒
|
||||
|
||||
修改 `C:\Users\Administrator\.codex\config.toml` 后,需要重启 Codex 会话或重新打开项目,MCP server 才会被重新发现。
|
||||
|
||||
## 本次深度迁移结果
|
||||
|
||||
- 用户全局习惯已写入 `C:\Users\Administrator\.codex\AGENTS.md`。
|
||||
- Claude skills 已迁移 10 个到 `C:\Users\Administrator\.codex\skills`:agent-introspection-debugging, claude-api, code-tour, codebase-onboarding, repo-scan, rules-distill, search-first, security-review, strategic-compact, tdd-workflow。
|
||||
- Claude agents 已迁移为 Codex skill `claude-agent-roles`,包含 8 个角色参考。
|
||||
- Claude rules 已迁移为 Codex skill `claude-rules-reference`,包含 28 个规则文件。
|
||||
- NeoZQYY Claude 会话历史已摘要归档到 `docs/claude-history/`:94 个会话,246 个被编辑文件索引。
|
||||
|
||||
### 追溯入口
|
||||
|
||||
- 会话索引:`docs/claude-history/session_index.csv`
|
||||
- 文件索引:`docs/claude-history/file_index.csv`
|
||||
- 会话摘要:`docs/claude-history/sessions/`
|
||||
207
docs/codex_migration_status_report_2026-04-29.md
Normal file
207
docs/codex_migration_status_report_2026-04-29.md
Normal file
@@ -0,0 +1,207 @@
|
||||
# Codex 迁移阶段状态报告
|
||||
|
||||
| 字段 | 内容 |
|
||||
|------|------|
|
||||
| 生成时间 | 2026-04-29 04:01:26 |
|
||||
| 范围 | 从 Claude Code 迁移至 Codex 的环境、配置、使用习惯、skills、agents、rules 与历史追溯资产 |
|
||||
| 当前结论 | 已进入“深度迁移完成,等待 Codex 重启生效与后续验收”的阶段 |
|
||||
|
||||
## 一、按时间线看当前进行到哪一步
|
||||
|
||||
| 时间 | 阶段 | 状态 | 说明 |
|
||||
|------|------|------|------|
|
||||
| 2026-04-20 至 2026-04-22 | Claude Code 主要业务开发期 | 已归档 | Claude 历史显示这段时间有大量跨后端、admin-web、小程序、数据库、文档和脚本的修改。 |
|
||||
| 2026-04-28 | Claude Code 尾声会话 | 已归档 | 最新 Claude 项目会话 `67f30f8e-2db7-467f-82e0-5395f9ed855f` 未检测到文件写入,偏向过渡/查询类会话。 |
|
||||
| 2026-04-29 03:30 左右 | Codex 基础迁移 | 已完成 | 已迁移根/子目录 `AGENTS.md`、MCP 配置、PostgreSQL MCP 启动脚本、环境变量。 |
|
||||
| 2026-04-29 03:54 左右 | Codex 深度迁移 | 已完成 | 已迁移 Claude skills、agents、rules、用户全局习惯,并生成 Claude 历史摘要归档。 |
|
||||
| 2026-04-29 03:57 左右 | 审计固化 | 已完成 | 已生成审计记录并刷新审计面板。 |
|
||||
| 当前 | 验收与生效阶段 | 进行中 | 需要重启 Codex 或重新打开项目,让新的全局规则、skills 和 MCP 配置完整加载。 |
|
||||
|
||||
## 二、已经完成的迁移
|
||||
|
||||
### 1. 项目级规则
|
||||
|
||||
- 根目录 `CLAUDE.md` 已迁移为 `AGENTS.md`。
|
||||
- 子模块规则已补齐:
|
||||
- `apps/backend/AGENTS.md`
|
||||
- `apps/etl/connectors/feiqiu/AGENTS.md`
|
||||
- `apps/demo-miniprogram/AGENTS.md`
|
||||
- `db/AGENTS.md`
|
||||
|
||||
### 2. Codex 全局配置
|
||||
|
||||
- `C:\Users\Administrator\.codex\config.toml` 已配置 NeoZQYY 项目信任、MCP server 和 shell 环境变量。
|
||||
- PostgreSQL MCP 已改为通过 `tools/codex/mcp-postgres.ps1` 间接读取 `.env` / `.env.local` / 当前环境变量,避免把 DSN 明文写入 Codex 配置。
|
||||
- 已配置 MCP:
|
||||
- `pg-etl`:正式 ETL 库,默认禁用
|
||||
- `pg-etl-test`:测试 ETL 库,默认启用
|
||||
- `pg-app`:正式业务库,默认禁用
|
||||
- `pg-app-test`:测试业务库,默认启用
|
||||
- `weixin-devtools-mcp`
|
||||
- `playwright`
|
||||
- `openapi`
|
||||
|
||||
### 3. 用户习惯、skills、agents、rules
|
||||
|
||||
- 用户全局习惯已写入 `C:\Users\Administrator\.codex\AGENTS.md`。
|
||||
- Claude skills 已迁移 10 个到 `C:\Users\Administrator\.codex\skills`。
|
||||
- Claude agents 已迁移为 Codex skill:`claude-agent-roles`,包含 8 个角色参考。
|
||||
- Claude rules/steering/pre-prompt 已迁移为 Codex skill:`claude-rules-reference`。
|
||||
- Codex 当前可发现的用户级 skill 数量:12 个。
|
||||
|
||||
### 4. Claude 对话历史追溯
|
||||
|
||||
- 已生成 `docs/claude-history/`。
|
||||
- 已归档 Claude 项目会话摘要:94 个。
|
||||
- 已建立文件反向索引:246 个被编辑文件。
|
||||
- 追溯入口:
|
||||
- `docs/claude-history/session_index.csv`
|
||||
- `docs/claude-history/file_index.csv`
|
||||
- `docs/claude-history/sessions/`
|
||||
|
||||
### 5. 审计与文档
|
||||
|
||||
- 迁移说明:`docs/codex_migration.md`
|
||||
- 审计记录:`docs/audit/changes/2026-04-29__codex_migration_and_claude_history_archive.md`
|
||||
- 审计面板:`docs/audit/audit_dashboard.md`
|
||||
- 可重复迁移脚本:`tools/codex/migrate_claude_assets.py`
|
||||
|
||||
## 三、已经完成的验证
|
||||
|
||||
| 验证项 | 结果 |
|
||||
|--------|------|
|
||||
| Codex skill 校验 | 12/12 通过 |
|
||||
| Claude 会话摘要数量 | 94 个 |
|
||||
| 文件反向索引数量 | 246 个唯一文件 |
|
||||
| PostgreSQL MCP 测试库启动配置 | `TEST_DB_DSN -ValidateOnly` 通过 |
|
||||
| PowerShell MCP 脚本语法 | 通过 |
|
||||
| Python 迁移脚本语法 | 通过 |
|
||||
| 迁移产物敏感信息扫描 | 未发现常见 DSN/API key/密码形态 |
|
||||
| 审计预扫描 | 已执行,需审计,已补审计记录 |
|
||||
|
||||
## 四、还没有解决或需要注意的遗留问题
|
||||
|
||||
### 1. Codex 需要重启生效
|
||||
|
||||
`config.toml`、`C:\Users\Administrator\.codex\AGENTS.md` 和新迁移的 skills 需要重启 Codex 或重新打开项目后才能稳定加载。
|
||||
|
||||
### 2. MCP 只完成静态与启动配置验证
|
||||
|
||||
当前已验证 TOML、PostgreSQL MCP 启动脚本和环境变量解析,但还没有在重启后的 Codex 会话里逐个调用 MCP 工具确认可用性。尤其需要后续确认:
|
||||
|
||||
- PostgreSQL 测试库 MCP 是否能列 schema / 执行只读 SQL。
|
||||
- WeChat DevTools MCP 是否能连接当前微信开发者工具。
|
||||
- Playwright MCP 是否能正常打开本地页面。
|
||||
- OpenAPI MCP 是否能在后端服务启动后读取接口。
|
||||
|
||||
### 3. Claude hooks 没有一比一启用
|
||||
|
||||
Claude Code 的 `PreToolUse`、`PostToolUse`、`Stop` hooks 没有在 Codex 里默认启用。当前替代方式是:
|
||||
|
||||
- 把规则写入 `AGENTS.md`。
|
||||
- 把审计流程写入项目规范。
|
||||
- 保留迁移说明中关于 hooks 的差异。
|
||||
|
||||
遗留风险是:原来由 hooks 自动拦截的行为,现在主要依赖 Codex 遵循规则和人工/脚本验证。
|
||||
|
||||
### 4. Claude 历史是摘要,不是全文记忆
|
||||
|
||||
历史归档是脱敏摘要和索引,不是原始对话全文导入。它能回答“哪个会话可能改了哪个文件、用了哪些命令、有什么 SQL/风险线索”,但不能保证完整还原每个自然语言决策。
|
||||
|
||||
关键问题追溯时仍需组合使用:
|
||||
|
||||
- `docs/claude-history/file_index.csv`
|
||||
- 对应 `sessions/<session_id>.md`
|
||||
- 原始 Claude JSONL
|
||||
- git diff / git blame
|
||||
- `docs/audit/changes/`
|
||||
- 当前代码与测试结果
|
||||
|
||||
### 5. 部分 Claude skill 带有 Claude 专属语境
|
||||
|
||||
如 `claude-api`、部分 agent/skill 文案仍可能包含 Claude Code 专属术语。它们已经可被 Codex 发现,但实际使用时需要按 Codex 工具能力做等价替换。
|
||||
|
||||
### 6. 工作区仍有大量既有未提交业务改动
|
||||
|
||||
本轮迁移没有处理此前已有的业务代码改动。当前迁移相关文件仍未提交,工作区还存在其他历史未提交内容。后续提交时需要隔离本次迁移文件,避免混入业务改动。
|
||||
|
||||
## 五、当前仓库内本轮新增/修改文件
|
||||
|
||||
### 迁移配置与规则
|
||||
|
||||
- `AGENTS.md`
|
||||
- `apps/backend/AGENTS.md`
|
||||
- `apps/demo-miniprogram/AGENTS.md`
|
||||
- `apps/etl/connectors/feiqiu/AGENTS.md`
|
||||
- `db/AGENTS.md`
|
||||
|
||||
### 工具脚本
|
||||
|
||||
- `tools/codex/mcp-postgres.ps1`
|
||||
- `tools/codex/migrate_claude_assets.py`
|
||||
|
||||
### 迁移与历史文档
|
||||
|
||||
- `docs/codex_migration.md`
|
||||
- `docs/claude-history/`
|
||||
- `docs/codex_migration_status_report_2026-04-29.md`
|
||||
|
||||
### 审计
|
||||
|
||||
- `docs/audit/changes/2026-04-29__codex_migration_and_claude_history_archive.md`
|
||||
- `docs/audit/audit_dashboard.md`
|
||||
|
||||
## 六、未来工作安排建议
|
||||
|
||||
### 第一阶段:生效验收
|
||||
|
||||
1. 重启 Codex 或重新打开 NeoZQYY 项目。
|
||||
2. 确认 Codex 能看到新增 skills:
|
||||
- `claude-agent-roles`
|
||||
- `claude-rules-reference`
|
||||
- `tdd-workflow`
|
||||
- `security-review`
|
||||
- `search-first`
|
||||
3. 逐个验证 MCP:
|
||||
- `pg-etl-test`
|
||||
- `pg-app-test`
|
||||
- `weixin-devtools-mcp`
|
||||
- `playwright`
|
||||
- `openapi`
|
||||
|
||||
### 第二阶段:迁移产物固化
|
||||
|
||||
1. 单独提交 Codex 迁移相关文件。
|
||||
2. 不混入既有业务代码改动。
|
||||
3. 提交信息使用中文描述,并按项目规范保留 Co-Authored-By 签名行。
|
||||
|
||||
### 第三阶段:历史追溯增强
|
||||
|
||||
1. 按业务模块补充历史摘要标签,例如 AI、财务看板、小程序聊天、触发器、数据库迁移。
|
||||
2. 对 2026-04-20 至 2026-04-22 的高影响会话做人工二次摘要。
|
||||
3. 将关键“设计决策”从历史摘要中提炼到 `docs/audit/` 或对应模块文档。
|
||||
|
||||
### 第四阶段:恢复强校验能力
|
||||
|
||||
1. 评估 Codex hooks 当前版本在 Windows 上的稳定性。
|
||||
2. 优先恢复 Stop 类检查:
|
||||
- 是否需要审计
|
||||
- 是否需要测试验证
|
||||
- 是否涉及数据库文档同步
|
||||
3. 再评估 PreToolUse / PostToolUse 类拦截。
|
||||
|
||||
### 第五阶段:继续业务开发前的固定流程
|
||||
|
||||
后续任何逻辑改动前,建议固定执行:
|
||||
|
||||
1. 查 `docs/claude-history/file_index.csv`,确认目标文件历史会话。
|
||||
2. 查 `docs/audit/changes/`,确认最近审计记录。
|
||||
3. 读取当前文件和调用链。
|
||||
4. 输出改动前上下文摘要。
|
||||
5. 实施后运行测试、输出 diff 摘要和风险清单。
|
||||
|
||||
## 七、阶段性结论
|
||||
|
||||
当前不是“刚开始迁移”,也不是“已经可以无感继续业务开发”。准确位置是:
|
||||
|
||||
> Codex 深度迁移已经完成,历史追溯体系已经建立;下一步是重启 Codex 让配置生效,并做 MCP/skills 的实际运行验收。验收完成后,再把迁移文件单独提交,之后才能稳定进入新的业务开发阶段。
|
||||
108
docs/cursor_migration.md
Normal file
108
docs/cursor_migration.md
Normal file
@@ -0,0 +1,108 @@
|
||||
# Cursor AI 开发环境迁移说明
|
||||
|
||||
## 状态
|
||||
|
||||
本轮把 NeoZQYY 的 AI 开发环境从 Claude Code / Codex 迁移到 Cursor 原生资产:
|
||||
|
||||
- 项目规则:`.cursor/rules/`
|
||||
- 项目流程技能:`.cursor/skills/`
|
||||
- 用户技能:`C:\Users\Administrator\.cursor\skills`
|
||||
- 用户子代理:`C:\Users\Administrator\.cursor\agents`
|
||||
- 轻量 hooks:`.cursor/hooks.json` 与 `.cursor/hooks/`
|
||||
- 历史索引:`docs/ai-env-history/`
|
||||
|
||||
## 去重原则
|
||||
|
||||
1. 当前仓库 `AGENTS.md` 是项目权威规则。
|
||||
2. `CLAUDE.md` 保留为历史兼容来源。
|
||||
3. `.cursor/rules` 只放短规则和入口,不复制长文。
|
||||
4. skills 使用“短入口 + reference”,避免每次会话加载过多旧内容。
|
||||
5. 原始对话逐一分析为摘要和索引,不把原始 JSONL 全文写入仓库。
|
||||
|
||||
## 模型偏好
|
||||
|
||||
用户主要使用 GPT 5.5 与 Claude 4.7。Cursor 的实际模型选择由当前会话或 UI 控制;本迁移只把偏好写入规则和说明,不强写不可控的内部模型字段。
|
||||
|
||||
## 内置流程与规则边界
|
||||
|
||||
Cursor、GPT 5.5、Claude 4.7 都具备调研、审计、验证、子代理和工具调用能力,但它们不知道 NeoZQYY 的项目事实、历史踩坑、数据库口径、RLS 双 schema、demo 标杆保护和审计路径。因此仍需要项目规则和技能作为触发器与约束。
|
||||
|
||||
规则不重复实现模型能力,只固化三类内容:
|
||||
|
||||
- 项目事实:目录职责、数据库、ETL、后端、前端、小程序约定。
|
||||
- 用户习惯:中文、先调研、再改动、再验证、再审计。
|
||||
- 风险边界:生产库禁用、敏感信息不扩散、危险 git/SQL/归档目录保护。
|
||||
|
||||
## 历史入口
|
||||
|
||||
- 新索引:`docs/ai-env-history/README.md`
|
||||
- Claude 摘要:`docs/claude-history/README.md`
|
||||
- 审计一览:`docs/audit/audit_dashboard.md`
|
||||
- Codex 迁移说明:`docs/codex_migration.md`
|
||||
|
||||
## 用户级资产触发策略
|
||||
|
||||
### 保留自然触发
|
||||
|
||||
- `neozqyy-cursor-migration`:NeoZQYY 迁移和历史追溯入口。
|
||||
- `strategic-compact`:长会话上下文压缩建议。
|
||||
- `claude-api`:仅在 Anthropic / Claude API 场景触发。
|
||||
|
||||
### 改为显式触发
|
||||
|
||||
- `claude-agent-roles`:旧 Claude agents 参考;日常使用 Cursor subagents。
|
||||
- `claude-rules-reference`:旧 Claude rules 档案;当前权威规则是 `AGENTS.md` 与 `.cursor/rules/`。
|
||||
- `tdd-workflow`:详细 TDD 参考;日常入口是 `tdd-guide` subagent。
|
||||
- `security-review`:详细安全 checklist;日常入口是 `security-reviewer` subagent。
|
||||
- `search-first`:通用搜索优先流程;NeoZQYY 内优先使用 `.cursor/skills/pre-change`。
|
||||
- `rules-distill`、`repo-scan`、`codebase-onboarding`、`code-tour`、`agent-introspection-debugging`:低频或重型工具,按需显式使用。
|
||||
|
||||
保留全部 8 个用户级 subagents:`planner`、`architect`、`code-reviewer`、`security-reviewer`、`database-reviewer`、`python-reviewer`、`tdd-guide`、`refactor-cleaner`。
|
||||
|
||||
## Hooks 强度判断
|
||||
|
||||
当前 hooks 保持轻量提醒和 ask,不默认强阻断。建议:
|
||||
|
||||
- `_archived/` 读取/搜索:已升级为 `failClosed` 强阻断。依据:项目规则明确禁止参考归档目录,误用概率高。
|
||||
- 危险 git 命令:保持 ask,不建议 `failClosed`。依据:极少数维护场景可能需要,必须由用户明确确认。
|
||||
- 生产库 MCP / 生产 DSN:只读调用走 ask;写入/DDL 类调用 deny。依据:测试库足够覆盖日常验证,生产库写操作风险高。
|
||||
- demo-miniprogram:保持提醒,不建议强阻断。依据:它是标杆目录,偶尔仍可能需要按用户明确要求修改。
|
||||
- DB schema 改动提醒:保持提醒。依据:是否需要文档同步要结合实际变更判断。
|
||||
|
||||
## MCP 策略
|
||||
|
||||
延续既有用途:
|
||||
|
||||
- `pg-etl-test`、`pg-app-test`:测试库,默认可用。
|
||||
- `pg-etl`、`pg-app`:生产库,默认禁用或 ask,不自动执行。
|
||||
- `weixin-devtools-mcp`:小程序调试和截图验证。
|
||||
- `playwright` / `cursor-ide-browser`:Web 前端验证、截图、交互检查。
|
||||
- `openapi`:后端 API 合同查询。
|
||||
|
||||
## VSCode Insiders 迁移
|
||||
|
||||
- 用户数据目录:`C:\Users\Administrator\AppData\Roaming\Code - Insiders\User`
|
||||
- 扩展清单:`C:\Users\Administrator\.vscode-insiders\extensions\extensions.json`
|
||||
- Cursor 扩展对照表:`docs/ai-env-history/vscode_insiders_extensions.csv`
|
||||
- 补装脚本:`tools/cursor/install_vscode_insiders_extensions.ps1`
|
||||
- 旧环境 disabled 状态已记录;Cursor 没有稳定 CLI 持久禁用接口,补装后需要在 Cursor UI 中按清单确认禁用状态。
|
||||
|
||||
## 主题时间线
|
||||
|
||||
- 主题时间线:`docs/ai-env-history/topic_timeline.md`
|
||||
- 主题 CSV:`docs/ai-env-history/topic_timeline.csv`
|
||||
|
||||
## 验证
|
||||
|
||||
```powershell
|
||||
.venv\Scripts\python.exe tools\cursor\migrate_ai_environment.py --check
|
||||
.venv\Scripts\python.exe scripts\audit\prescan.py --files "tools/cursor/migrate_ai_environment.py,docs/cursor_migration.md"
|
||||
```
|
||||
|
||||
## 回滚
|
||||
|
||||
迁移脚本会把覆盖前的 Cursor 用户资产和项目 `.cursor` 资产备份到:
|
||||
|
||||
`C:\Users\Administrator\.cursor\backups\neozqyy-cursor-migration\<timestamp>`
|
||||
|
||||
如需回滚,按 manifest 中的源/目标路径恢复即可。
|
||||
68
tools/codex/mcp-postgres.ps1
Normal file
68
tools/codex/mcp-postgres.ps1
Normal file
@@ -0,0 +1,68 @@
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$DsnVariable,
|
||||
|
||||
[switch]$ValidateOnly
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$repoRoot = Resolve-Path (Join-Path $scriptDir "..\..")
|
||||
$fileVars = @{}
|
||||
|
||||
foreach ($envFile in @(".env", ".env.local")) {
|
||||
$envPath = Join-Path $repoRoot $envFile
|
||||
if (-not (Test-Path $envPath)) {
|
||||
continue
|
||||
}
|
||||
|
||||
foreach ($line in Get-Content $envPath) {
|
||||
$trimmed = $line.Trim()
|
||||
if ($trimmed.Length -eq 0 -or $trimmed.StartsWith("#")) {
|
||||
continue
|
||||
}
|
||||
|
||||
$index = $trimmed.IndexOf("=")
|
||||
if ($index -lt 1) {
|
||||
continue
|
||||
}
|
||||
|
||||
$key = $trimmed.Substring(0, $index).Trim()
|
||||
$rawValue = $trimmed.Substring($index + 1).Trim()
|
||||
if (($rawValue.StartsWith('"') -and $rawValue.EndsWith('"')) -or ($rawValue.StartsWith("'") -and $rawValue.EndsWith("'"))) {
|
||||
$rawValue = $rawValue.Substring(1, $rawValue.Length - 2)
|
||||
}
|
||||
|
||||
$fileVars[$key] = $rawValue
|
||||
}
|
||||
}
|
||||
|
||||
$dsn = [Environment]::GetEnvironmentVariable($DsnVariable, "Process")
|
||||
if ([string]::IsNullOrWhiteSpace($dsn) -and $fileVars.ContainsKey($DsnVariable)) {
|
||||
$dsn = $fileVars[$DsnVariable]
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($dsn)) {
|
||||
Write-Error "缺少环境变量:$DsnVariable"
|
||||
exit 2
|
||||
}
|
||||
|
||||
$uvx = [Environment]::GetEnvironmentVariable("UVX_EXE", "Process")
|
||||
if ([string]::IsNullOrWhiteSpace($uvx)) {
|
||||
$uvx = "C:\Dev\miniconda3\Scripts\uvx.exe"
|
||||
}
|
||||
|
||||
if (-not (Test-Path $uvx)) {
|
||||
Write-Error "未找到 uvx:$uvx"
|
||||
exit 2
|
||||
}
|
||||
|
||||
if ($ValidateOnly) {
|
||||
Write-Output "MCP PostgreSQL 启动配置检查通过:$DsnVariable"
|
||||
exit 0
|
||||
}
|
||||
|
||||
$env:DATABASE_URI = $dsn
|
||||
& $uvx --python "3.12" "postgres-mcp" "--access-mode=unrestricted"
|
||||
exit $LASTEXITCODE
|
||||
647
tools/codex/migrate_claude_assets.py
Normal file
647
tools/codex/migrate_claude_assets.py
Normal file
@@ -0,0 +1,647 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import csv
|
||||
import datetime as dt
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
from collections import Counter, defaultdict
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[2]
|
||||
HOME = Path.home()
|
||||
CLAUDE_HOME = HOME / ".claude"
|
||||
CODEX_HOME = HOME / ".codex"
|
||||
CODEX_SKILLS = CODEX_HOME / "skills"
|
||||
HISTORY_ROOT = REPO_ROOT / "docs" / "claude-history"
|
||||
|
||||
SECRET_PATTERNS = [
|
||||
re.compile(r"sk-[A-Za-z0-9_-]{12,}"),
|
||||
re.compile(r"sk-proj-[A-Za-z0-9_-]{12,}"),
|
||||
re.compile(r"(?i)(password|passwd|pwd|token|secret|api[_-]?key)\s*[:=]\s*['\"]?[^'\"\s,;]+"),
|
||||
re.compile(r"postgresql://[^\s'\"`]+"),
|
||||
re.compile(r"mysql://[^\s'\"`]+"),
|
||||
re.compile(r"mongodb(?:\+srv)?://[^\s'\"`]+"),
|
||||
]
|
||||
|
||||
|
||||
def redact_preserve(text: str) -> str:
|
||||
value = text.replace("sk-proj-xxxxx", "[示例密钥已脱敏]")
|
||||
for pattern in SECRET_PATTERNS:
|
||||
value = pattern.sub("[已脱敏]", value)
|
||||
return value
|
||||
|
||||
|
||||
def redact(text: str, limit: int | None = None) -> str:
|
||||
value = redact_preserve(text)
|
||||
value = re.sub(r"\s+", " ", value).strip()
|
||||
if limit is not None and len(value) > limit:
|
||||
return value[: limit - 1].rstrip() + "…"
|
||||
return value
|
||||
|
||||
|
||||
def backup(path: Path) -> None:
|
||||
if not path.exists():
|
||||
return
|
||||
stamp = dt.datetime.now().strftime("%Y%m%d-%H%M%S")
|
||||
backup_root = CODEX_HOME / "backups" / "claude-migration"
|
||||
backup_root.mkdir(parents=True, exist_ok=True)
|
||||
safe_name = re.sub(r"[^A-Za-z0-9_.-]+", "_", str(path).replace(":", ""))
|
||||
target = backup_root / f"{safe_name}.backup-{stamp}"
|
||||
if path.is_dir():
|
||||
shutil.copytree(path, target)
|
||||
else:
|
||||
shutil.copy2(path, target)
|
||||
|
||||
|
||||
def parse_frontmatter(text: str) -> tuple[dict[str, str], str]:
|
||||
if not text.startswith("---"):
|
||||
return {}, text
|
||||
end = text.find("\n---", 3)
|
||||
if end == -1:
|
||||
return {}, text
|
||||
raw = text[3:end].strip()
|
||||
body = text[end + len("\n---") :].lstrip("\n")
|
||||
meta: dict[str, str] = {}
|
||||
for line in raw.splitlines():
|
||||
if ":" not in line:
|
||||
continue
|
||||
key, value = line.split(":", 1)
|
||||
meta[key.strip()] = value.strip().strip('"').strip("'")
|
||||
return meta, body
|
||||
|
||||
|
||||
def title_case_slug(name: str) -> str:
|
||||
return " ".join(part.capitalize() for part in name.replace("_", "-").split("-"))
|
||||
|
||||
|
||||
def write_text(path: Path, text: str) -> None:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(text, encoding="utf-8")
|
||||
|
||||
|
||||
def copytree_contents(src: Path, dst: Path) -> None:
|
||||
if dst.exists():
|
||||
shutil.rmtree(dst)
|
||||
dst.mkdir(parents=True, exist_ok=True)
|
||||
for item in src.iterdir():
|
||||
target = dst / item.name
|
||||
if item.is_dir():
|
||||
shutil.copytree(item, target)
|
||||
else:
|
||||
shutil.copy2(item, target)
|
||||
|
||||
|
||||
def migrate_skills() -> list[str]:
|
||||
src_root = CLAUDE_HOME / "skills"
|
||||
migrated: list[str] = []
|
||||
if not src_root.exists():
|
||||
return migrated
|
||||
|
||||
for src in sorted(p for p in src_root.iterdir() if p.is_dir()):
|
||||
skill_md = src / "SKILL.md"
|
||||
if not skill_md.exists():
|
||||
continue
|
||||
|
||||
dst = CODEX_SKILLS / src.name
|
||||
if dst.exists():
|
||||
backup(dst)
|
||||
copytree_contents(src, dst)
|
||||
|
||||
original = skill_md.read_text(encoding="utf-8", errors="replace")
|
||||
meta, body = parse_frontmatter(original)
|
||||
name = re.sub(r"[^a-z0-9-]", "-", meta.get("name", src.name).lower()).strip("-") or src.name
|
||||
description = meta.get("description") or f"从 Claude Code 迁移的 {src.name} 工作流。"
|
||||
body = redact_preserve(body)
|
||||
new_text = (
|
||||
"---\n"
|
||||
f"name: {name}\n"
|
||||
f"description: {description} 从 Claude Code 迁移;当用户提到 ${name}、{src.name}、原 Claude skill,或需要该工作流时使用。\n"
|
||||
"---\n\n"
|
||||
f"> 迁移说明:本 skill 从 `C:\\Users\\Administrator\\.claude\\skills\\{src.name}` 转换而来。"
|
||||
"如内容包含 Claude Code 专属命令,请按 Codex 当前工具等价替换。\n\n"
|
||||
+ body
|
||||
)
|
||||
write_text(dst / "SKILL.md", new_text)
|
||||
|
||||
agents_dir = dst / "agents"
|
||||
agents_dir.mkdir(exist_ok=True)
|
||||
short = redact(description, 120).replace('"', "'")
|
||||
openai_yaml = (
|
||||
f'display_name: "{title_case_slug(name)}"\n'
|
||||
f'short_description: "{short}"\n'
|
||||
f'default_prompt: "使用 {name} 处理当前任务,遵循从 Claude Code 迁移来的工作流。"\n'
|
||||
)
|
||||
write_text(agents_dir / "openai.yaml", openai_yaml)
|
||||
migrated.append(name)
|
||||
return migrated
|
||||
|
||||
|
||||
def migrate_agents() -> list[str]:
|
||||
src_root = CLAUDE_HOME / "agents"
|
||||
if not src_root.exists():
|
||||
return []
|
||||
|
||||
skill_dir = CODEX_SKILLS / "claude-agent-roles"
|
||||
if skill_dir.exists():
|
||||
backup(skill_dir)
|
||||
shutil.rmtree(skill_dir)
|
||||
refs = skill_dir / "references"
|
||||
refs.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
rows: list[tuple[str, str]] = []
|
||||
for src in sorted(src_root.glob("*.md")):
|
||||
text = src.read_text(encoding="utf-8", errors="replace")
|
||||
meta, body = parse_frontmatter(text)
|
||||
name = meta.get("name", src.stem)
|
||||
description = meta.get("description", "")
|
||||
rows.append((name, description))
|
||||
write_text(refs / f"{src.stem}.md", redact_preserve(body))
|
||||
|
||||
table = "\n".join(
|
||||
f"| `{name}` | {redact(description, 160)} | `references/{name}.md` |" for name, description in rows
|
||||
)
|
||||
skill_md = f"""---
|
||||
name: claude-agent-roles
|
||||
description: 从 Claude Code 迁移的自定义 agent 角色参考。Use when 用户提到 planner、architect、code-reviewer、security-reviewer、database-reviewer、python-reviewer、tdd-guide、refactor-cleaner,或要求沿用 Claude Code agent/角色/多视角审查习惯时使用。
|
||||
---
|
||||
|
||||
# Claude Agent Roles
|
||||
|
||||
本 skill 保存原 Claude Code 自定义 agent 的角色提示词。Codex 当前不能一比一注册这些 Claude agent;使用时读取对应 reference,把它当作角色视角、检查清单或审查框架。
|
||||
|
||||
## 角色映射
|
||||
|
||||
| 角色 | 用途 | 参考文件 |
|
||||
|------|------|----------|
|
||||
{table}
|
||||
|
||||
## 使用规则
|
||||
|
||||
1. 用户明确点名某个角色时,读取对应 `references/*.md`。
|
||||
2. 复杂功能、架构调整、重大重构时优先参考 `planner` 与 `architect`。
|
||||
3. 代码修改后优先参考 `code-reviewer`;涉及认证、权限、数据库、密钥、用户输入时叠加 `security-reviewer` 或 `database-reviewer`。
|
||||
4. Bug 修复和新功能需要测试设计时参考 `tdd-guide`。
|
||||
5. 不要声称已经启动 Claude agent;用“按迁移角色检查/规划”描述即可。
|
||||
"""
|
||||
write_text(skill_dir / "SKILL.md", skill_md)
|
||||
write_text(
|
||||
skill_dir / "agents" / "openai.yaml",
|
||||
'display_name: "Claude Agent Roles"\n'
|
||||
'short_description: "迁移自 Claude Code 的 planner、architect、reviewer 等角色参考。"\n'
|
||||
'default_prompt: "按迁移自 Claude Code 的角色习惯,对当前任务进行规划、审查或安全检查。"\n',
|
||||
)
|
||||
return [name for name, _ in rows]
|
||||
|
||||
|
||||
def migrate_rules() -> list[str]:
|
||||
src_root = CLAUDE_HOME / "rules"
|
||||
if not src_root.exists():
|
||||
return []
|
||||
|
||||
skill_dir = CODEX_SKILLS / "claude-rules-reference"
|
||||
if skill_dir.exists():
|
||||
backup(skill_dir)
|
||||
shutil.rmtree(skill_dir)
|
||||
refs = skill_dir / "references"
|
||||
refs.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
copied: list[str] = []
|
||||
for src in sorted(src_root.rglob("*.md")):
|
||||
rel = src.relative_to(src_root)
|
||||
target = refs / rel
|
||||
write_text(target, redact_preserve(src.read_text(encoding="utf-8", errors="replace")))
|
||||
copied.append(rel.as_posix())
|
||||
|
||||
list_text = "\n".join(f"- `references/{path}`" for path in copied)
|
||||
skill_md = f"""---
|
||||
name: claude-rules-reference
|
||||
description: 从 Claude Code 迁移的个人工程规则、中文工作流、Python/TypeScript/Web 编码规范、安全、测试、审查和性能偏好。Use when 需要沿用用户之前的 Claude Code 使用习惯、steering/rules/pre-prompt,或处理代码风格、测试、安全、评审、Web 设计质量要求时使用。
|
||||
---
|
||||
|
||||
# Claude Rules Reference
|
||||
|
||||
本 skill 保存原 `C:\\Users\\Administrator\\.claude\\rules`。优先使用当前仓库 `AGENTS.md`,当用户要求沿用旧习惯、或任务涉及代码风格/测试/安全/审查/Web 体验时,再读取相关 reference。
|
||||
|
||||
## 可用参考
|
||||
|
||||
{list_text}
|
||||
|
||||
## 读取建议
|
||||
|
||||
- 中文通用习惯:读取 `references/zh/README.md` 及同目录相关主题。
|
||||
- Python:读取 `references/python/*.md`。
|
||||
- TypeScript/前端:读取 `references/typescript/*.md` 与 `references/web/*.md`。
|
||||
- 安全、测试、代码审查:按主题读取对应文件,不要一次性加载全部。
|
||||
"""
|
||||
write_text(skill_dir / "SKILL.md", skill_md)
|
||||
write_text(
|
||||
skill_dir / "agents" / "openai.yaml",
|
||||
'display_name: "Claude Rules Reference"\n'
|
||||
'short_description: "迁移自 Claude Code 的个人规则、steering 和工程偏好。"\n'
|
||||
'default_prompt: "沿用用户从 Claude Code 迁移来的工程规则和审查习惯处理当前任务。"\n',
|
||||
)
|
||||
return copied
|
||||
|
||||
|
||||
def migrate_global_agents() -> None:
|
||||
target = CODEX_HOME / "AGENTS.md"
|
||||
backup(target)
|
||||
text = """# 用户全局习惯(由 Claude Code 迁移)
|
||||
|
||||
## 语言与沟通
|
||||
|
||||
- 默认使用简体中文回复、解释、状态更新和审计记录。
|
||||
- 技术术语、命令、API 字段、变量名保持原文。
|
||||
- 先读上下文再动手;不确定时提出关键问题,但对低风险配置/文档迁移可直接执行。
|
||||
- 回复要高信号、少套话;给出实际结果、验证状态和剩余风险。
|
||||
|
||||
## 工作方式
|
||||
|
||||
- 尊重既有代码风格和项目约定,优先复用现有模式。
|
||||
- 每一处改动都应能追溯到用户请求;不要顺手做无关重构。
|
||||
- 小步实施,保持可验证、可回滚。
|
||||
- 复杂功能、重构、多模块改动前先做规划和影响分析。
|
||||
- Bug 修复和新功能优先考虑测试驱动:先确认复现或 RED,再实现,再验证 GREEN。
|
||||
- 修改代码后要说明改了哪些文件、为什么改、怎么验证、哪些风险未覆盖。
|
||||
|
||||
## 审查与安全偏好
|
||||
|
||||
- 代码修改后进行代码审查视角检查。
|
||||
- 涉及认证、授权、数据库、文件系统、用户输入、外部 API、密钥、支付/财务时,必须叠加安全审查。
|
||||
- 禁止硬编码密钥、令牌、密码和生产 DSN;日志和文档中避免暴露敏感信息。
|
||||
- 数据库查询优先参数化,Schema 变更必须同步文档和回滚/验证步骤。
|
||||
|
||||
## 角色与 skill 迁移
|
||||
|
||||
- 原 Claude Code agents 已迁移为 Codex skill:`claude-agent-roles`。
|
||||
- 原 Claude Code rules 已迁移为 Codex skill:`claude-rules-reference`。
|
||||
- 原 Claude Code skills 已迁移到 `C:\\Users\\Administrator\\.codex\\skills`。
|
||||
- 当用户提到旧角色或旧 skill 时,优先读取对应 Codex skill/reference,而不是重新发明流程。
|
||||
|
||||
## 历史追溯
|
||||
|
||||
- Claude Code 历史摘要归档在仓库 `docs/claude-history/`。
|
||||
- 需要追踪“哪次对话改了什么、影响什么”时,先查 `session_index.csv` 和 `file_index.csv`,再读对应 `sessions/*.md`。
|
||||
- 历史摘要是追溯材料,不是当前事实来源;真正编码前仍需读取当前文件、git diff、审计记录和测试结果。
|
||||
"""
|
||||
write_text(target, text)
|
||||
|
||||
|
||||
def safe_rel(path: str) -> str:
|
||||
value = path.replace("\\", "/")
|
||||
normalized = value.lower()
|
||||
markers = [
|
||||
"c:/project/neozqyy/",
|
||||
"/c/project/neozqyy/",
|
||||
"c:/neozqyy/",
|
||||
"/c/neozqyy/",
|
||||
]
|
||||
for marker in markers:
|
||||
if normalized.startswith(marker):
|
||||
return value[len(marker) :]
|
||||
return value
|
||||
|
||||
|
||||
def classify_area(path: str) -> str:
|
||||
normalized = safe_rel(path)
|
||||
if normalized.startswith("apps/backend/"):
|
||||
return "后端"
|
||||
if normalized.startswith("apps/etl/"):
|
||||
return "ETL"
|
||||
if normalized.startswith("apps/miniprogram/"):
|
||||
return "小程序"
|
||||
if normalized.startswith("apps/admin-web/"):
|
||||
return "admin-web"
|
||||
if normalized.startswith("apps/tenant-admin/"):
|
||||
return "tenant-admin"
|
||||
if normalized.startswith("db/") or normalized.endswith(".sql"):
|
||||
return "数据库"
|
||||
if normalized.startswith("docs/"):
|
||||
return "文档"
|
||||
if normalized.startswith("scripts/") or normalized.startswith("tools/"):
|
||||
return "脚本/工具"
|
||||
return "其他"
|
||||
|
||||
|
||||
def extract_text_from_content(content: Any) -> str:
|
||||
if isinstance(content, str):
|
||||
return content
|
||||
if isinstance(content, list):
|
||||
parts: list[str] = []
|
||||
for item in content:
|
||||
if isinstance(item, dict) and item.get("type") == "text":
|
||||
parts.append(str(item.get("text", "")))
|
||||
return "\n".join(parts)
|
||||
return ""
|
||||
|
||||
|
||||
def first_sql_summary(sql: str) -> str:
|
||||
clean = redact(sql, 240)
|
||||
command = re.match(r"\s*(select|insert|update|delete|create|alter|drop|with|explain|truncate)\b", sql, re.I)
|
||||
verb = command.group(1).upper() if command else "SQL"
|
||||
tables = sorted(set(re.findall(r"\b(?:from|join|into|update|table|view)\s+([a-zA-Z_][\w.]*)(?:\s|$)", sql, re.I)))
|
||||
if tables:
|
||||
return f"{verb}: {', '.join(tables[:8])}"
|
||||
return f"{verb}: {clean}"
|
||||
|
||||
|
||||
def summarize_session(path: Path) -> dict[str, Any]:
|
||||
session_id = path.stem
|
||||
timestamps: list[str] = []
|
||||
branches: set[str] = set()
|
||||
cwds: set[str] = set()
|
||||
user_prompts: list[str] = []
|
||||
assistant_notes: list[str] = []
|
||||
touched: Counter[str] = Counter()
|
||||
read_files: Counter[str] = Counter()
|
||||
commands: Counter[str] = Counter()
|
||||
sql_ops: Counter[str] = Counter()
|
||||
tools: Counter[str] = Counter()
|
||||
mcp_tools: Counter[str] = Counter()
|
||||
agents: Counter[str] = Counter()
|
||||
risk_flags: set[str] = set()
|
||||
line_count = 0
|
||||
|
||||
with path.open("r", encoding="utf-8", errors="replace") as f:
|
||||
for line in f:
|
||||
line_count += 1
|
||||
try:
|
||||
obj = json.loads(line)
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
ts = obj.get("timestamp")
|
||||
if isinstance(ts, str):
|
||||
timestamps.append(ts)
|
||||
branch = obj.get("gitBranch")
|
||||
if isinstance(branch, str) and branch:
|
||||
branches.add(branch)
|
||||
cwd = obj.get("cwd")
|
||||
if isinstance(cwd, str) and cwd:
|
||||
cwds.add(cwd)
|
||||
msg = obj.get("message")
|
||||
if not isinstance(msg, dict):
|
||||
continue
|
||||
role = msg.get("role")
|
||||
content = msg.get("content")
|
||||
if role == "user":
|
||||
text = extract_text_from_content(content)
|
||||
if text and not obj.get("isMeta") and not obj.get("isCompactSummary"):
|
||||
user_prompts.append(redact(text, 260))
|
||||
elif role == "assistant":
|
||||
text = extract_text_from_content(content)
|
||||
if text:
|
||||
assistant_notes.append(redact(text, 220))
|
||||
|
||||
if isinstance(content, list):
|
||||
for item in content:
|
||||
if not isinstance(item, dict) or item.get("type") != "tool_use":
|
||||
continue
|
||||
name = str(item.get("name", "unknown"))
|
||||
tools[name] += 1
|
||||
if name.startswith("mcp__"):
|
||||
mcp_tools[name] += 1
|
||||
inp = item.get("input") if isinstance(item.get("input"), dict) else {}
|
||||
|
||||
file_path = inp.get("file_path")
|
||||
if isinstance(file_path, str):
|
||||
rel = safe_rel(file_path)
|
||||
if name in {"Edit", "Write", "MultiEdit"}:
|
||||
touched[rel] += 1
|
||||
elif name == "Read":
|
||||
read_files[rel] += 1
|
||||
path_value = inp.get("path")
|
||||
if isinstance(path_value, str) and name in {"Write", "Edit", "mcp__weixin-devtools-mcp__screenshot"}:
|
||||
touched[safe_rel(path_value)] += 1
|
||||
command = inp.get("command")
|
||||
if isinstance(command, str):
|
||||
cmd = redact(command, 180)
|
||||
commands[cmd] += 1
|
||||
lowered = command.lower()
|
||||
if "git reset --hard" in lowered or "git clean" in lowered:
|
||||
risk_flags.add("包含高风险 git 清理命令")
|
||||
if "drop table" in lowered or "truncate" in lowered:
|
||||
risk_flags.add("包含高风险数据库命令")
|
||||
if ".env" in lowered:
|
||||
risk_flags.add("命令涉及环境文件")
|
||||
sql = inp.get("sql")
|
||||
if isinstance(sql, str):
|
||||
summary = first_sql_summary(sql)
|
||||
sql_ops[summary] += 1
|
||||
lowered_sql = sql.lower()
|
||||
if re.search(r"\b(drop|truncate|delete)\b", lowered_sql):
|
||||
risk_flags.add("包含删除/回滚类 SQL")
|
||||
subagent = inp.get("subagent_type") or inp.get("description")
|
||||
if isinstance(subagent, str) and name == "Agent":
|
||||
agents[redact(subagent, 120)] += 1
|
||||
|
||||
areas = Counter(classify_area(p) for p in touched)
|
||||
first_ts = min(timestamps) if timestamps else ""
|
||||
last_ts = max(timestamps) if timestamps else ""
|
||||
return {
|
||||
"session_id": session_id,
|
||||
"source_path": str(path),
|
||||
"bytes": path.stat().st_size,
|
||||
"lines": line_count,
|
||||
"first_ts": first_ts,
|
||||
"last_ts": last_ts,
|
||||
"branches": sorted(branches),
|
||||
"cwds": sorted(cwds),
|
||||
"user_prompts": user_prompts[:12],
|
||||
"assistant_notes": assistant_notes[:8],
|
||||
"touched_files": touched.most_common(),
|
||||
"read_files": read_files.most_common(30),
|
||||
"commands": commands.most_common(40),
|
||||
"sql_ops": sql_ops.most_common(30),
|
||||
"tools": tools.most_common(30),
|
||||
"mcp_tools": mcp_tools.most_common(20),
|
||||
"agents": agents.most_common(20),
|
||||
"areas": areas.most_common(),
|
||||
"risk_flags": sorted(risk_flags),
|
||||
}
|
||||
|
||||
|
||||
def session_markdown(summary: dict[str, Any]) -> str:
|
||||
touched = summary["touched_files"]
|
||||
areas = ", ".join(f"{area}({count})" for area, count in summary["areas"]) or "未识别"
|
||||
goals = "\n".join(f"- {p}" for p in summary["user_prompts"]) or "- 未提取到用户目标"
|
||||
files = "\n".join(f"- `{path}`:{count} 次写入/编辑" for path, count in touched[:80]) or "- 未检测到写入/编辑工具"
|
||||
commands = "\n".join(f"- `{cmd}`:{count} 次" for cmd, count in summary["commands"][:30]) or "- 未检测到 Bash 命令"
|
||||
sql_ops = "\n".join(f"- {op}:{count} 次" for op, count in summary["sql_ops"][:30]) or "- 未检测到 SQL 工具调用"
|
||||
tools = "\n".join(f"- `{tool}`:{count} 次" for tool, count in summary["tools"][:20]) or "- 无"
|
||||
agents = "\n".join(f"- {agent}:{count} 次" for agent, count in summary["agents"]) or "- 未检测到 Claude Agent 调用"
|
||||
risks = "\n".join(f"- {flag}" for flag in summary["risk_flags"]) or "- 未从工具调用中检测到显式高风险信号"
|
||||
notes = "\n".join(f"- {note}" for note in summary["assistant_notes"][:8]) or "- 未提取"
|
||||
return f"""# Claude 会话摘要:{summary['session_id']}
|
||||
|
||||
| 字段 | 值 |
|
||||
|------|----|
|
||||
| 时间范围 | {summary['first_ts']} -> {summary['last_ts']} |
|
||||
| 原始记录 | `{summary['source_path']}` |
|
||||
| 大小 | {summary['bytes']} bytes / {summary['lines']} lines |
|
||||
| 分支 | {', '.join(summary['branches']) or '未记录'} |
|
||||
| 目录 | {', '.join(summary['cwds']) or '未记录'} |
|
||||
| 影响范围 | {areas} |
|
||||
|
||||
## 用户目标摘录(已脱敏)
|
||||
|
||||
{goals}
|
||||
|
||||
## 可能修改的文件
|
||||
|
||||
{files}
|
||||
|
||||
## 运行过的命令(已脱敏)
|
||||
|
||||
{commands}
|
||||
|
||||
## 数据库/SQL 操作摘要
|
||||
|
||||
{sql_ops}
|
||||
|
||||
## 工具调用概览
|
||||
|
||||
{tools}
|
||||
|
||||
## Agent/子任务线索
|
||||
|
||||
{agents}
|
||||
|
||||
## 助手过程摘要摘录(已脱敏)
|
||||
|
||||
{notes}
|
||||
|
||||
## 风险与追溯提示
|
||||
|
||||
{risks}
|
||||
|
||||
> 本摘要由脚本从 Claude JSONL 工具调用和消息元数据中生成,不替代 `git diff`、审计记录、测试结果和当前代码事实。需要深挖时再读取原始 JSONL,并继续做脱敏处理。
|
||||
"""
|
||||
|
||||
|
||||
def migrate_history() -> dict[str, int]:
|
||||
source = CLAUDE_HOME / "projects" / "C--Project-NeoZQYY"
|
||||
if not source.exists():
|
||||
return {"sessions": 0, "files": 0}
|
||||
|
||||
if HISTORY_ROOT.exists():
|
||||
backup(HISTORY_ROOT)
|
||||
shutil.rmtree(HISTORY_ROOT)
|
||||
sessions_dir = HISTORY_ROOT / "sessions"
|
||||
sessions_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
summaries = [summarize_session(path) for path in sorted(source.glob("*.jsonl"), key=lambda p: p.stat().st_mtime)]
|
||||
summaries.sort(key=lambda item: item["last_ts"] or item["first_ts"])
|
||||
|
||||
file_index: dict[str, list[dict[str, Any]]] = defaultdict(list)
|
||||
with (HISTORY_ROOT / "session_index.csv").open("w", newline="", encoding="utf-8-sig") as f:
|
||||
writer = csv.writer(f)
|
||||
writer.writerow(["session_id", "first_ts", "last_ts", "bytes", "lines", "branches", "areas", "touched_count", "risk_flags", "summary_file", "source_path"])
|
||||
for summary in summaries:
|
||||
summary_file = sessions_dir / f"{summary['session_id']}.md"
|
||||
write_text(summary_file, session_markdown(summary))
|
||||
areas = "; ".join(f"{area}:{count}" for area, count in summary["areas"])
|
||||
writer.writerow([
|
||||
summary["session_id"],
|
||||
summary["first_ts"],
|
||||
summary["last_ts"],
|
||||
summary["bytes"],
|
||||
summary["lines"],
|
||||
"; ".join(summary["branches"]),
|
||||
areas,
|
||||
len(summary["touched_files"]),
|
||||
"; ".join(summary["risk_flags"]),
|
||||
str(summary_file.relative_to(REPO_ROOT)),
|
||||
summary["source_path"],
|
||||
])
|
||||
for path, count in summary["touched_files"]:
|
||||
file_index[path].append({"session_id": summary["session_id"], "count": count, "last_ts": summary["last_ts"]})
|
||||
|
||||
with (HISTORY_ROOT / "file_index.csv").open("w", newline="", encoding="utf-8-sig") as f:
|
||||
writer = csv.writer(f)
|
||||
writer.writerow(["file_path", "session_id", "last_ts", "edit_count", "session_summary"])
|
||||
for file_path in sorted(file_index):
|
||||
for row in sorted(file_index[file_path], key=lambda item: item["last_ts"]):
|
||||
writer.writerow([
|
||||
file_path,
|
||||
row["session_id"],
|
||||
row["last_ts"],
|
||||
row["count"],
|
||||
f"docs/claude-history/sessions/{row['session_id']}.md",
|
||||
])
|
||||
|
||||
recent_lines = "\n".join(
|
||||
f"- `{s['session_id']}`:{s['first_ts']} -> {s['last_ts']},修改 {len(s['touched_files'])} 个文件,范围 {', '.join(a for a, _ in s['areas']) or '未识别'}"
|
||||
for s in summaries[-20:]
|
||||
)
|
||||
readme = f"""# Claude Code 历史摘要归档
|
||||
|
||||
本目录由 `tools/codex/migrate_claude_assets.py` 从 `C:\\Users\\Administrator\\.claude\\projects\\C--Project-NeoZQYY` 同名项目历史生成,用于迁移到 Codex 后的追本溯源。
|
||||
|
||||
## 文件说明
|
||||
|
||||
- `session_index.csv`:会话级索引,按 session 记录时间范围、影响范围、风险标签、摘要文件。
|
||||
- `file_index.csv`:文件反向索引,回答“哪个会话改过这个文件”。
|
||||
- `sessions/*.md`:每个 Claude JSONL 会话的脱敏摘要。
|
||||
|
||||
## 最近 20 个会话
|
||||
|
||||
{recent_lines}
|
||||
|
||||
## 使用方式
|
||||
|
||||
1. 查某个文件历史:在 `file_index.csv` 搜索文件路径。
|
||||
2. 查某次会话影响:打开对应 `sessions/<session_id>.md`。
|
||||
3. 需要完整细节时,再回到原始 JSONL;读取前注意脱敏。
|
||||
|
||||
## 注意
|
||||
|
||||
摘要基于工具调用和消息元数据自动生成,不能替代当前代码、审计文档和测试结果。编码前仍需读取当前文件和 `git diff`。
|
||||
"""
|
||||
write_text(HISTORY_ROOT / "README.md", readme)
|
||||
return {"sessions": len(summaries), "files": len(file_index)}
|
||||
|
||||
|
||||
def update_migration_doc(result: dict[str, Any]) -> None:
|
||||
doc = REPO_ROOT / "docs" / "codex_migration.md"
|
||||
existing = doc.read_text(encoding="utf-8", errors="replace") if doc.exists() else "# Codex 迁移配置说明\n"
|
||||
marker = "## 本次深度迁移结果"
|
||||
existing = existing.split(marker)[0].rstrip()
|
||||
section = f"""
|
||||
|
||||
{marker}
|
||||
|
||||
- 用户全局习惯已写入 `C:\\Users\\Administrator\\.codex\\AGENTS.md`。
|
||||
- Claude skills 已迁移 {len(result['skills'])} 个到 `C:\\Users\\Administrator\\.codex\\skills`:{', '.join(result['skills'])}。
|
||||
- Claude agents 已迁移为 Codex skill `claude-agent-roles`,包含 {len(result['agents'])} 个角色参考。
|
||||
- Claude rules 已迁移为 Codex skill `claude-rules-reference`,包含 {len(result['rules'])} 个规则文件。
|
||||
- NeoZQYY Claude 会话历史已摘要归档到 `docs/claude-history/`:{result['history']['sessions']} 个会话,{result['history']['files']} 个被编辑文件索引。
|
||||
|
||||
### 追溯入口
|
||||
|
||||
- 会话索引:`docs/claude-history/session_index.csv`
|
||||
- 文件索引:`docs/claude-history/file_index.csv`
|
||||
- 会话摘要:`docs/claude-history/sessions/`
|
||||
"""
|
||||
write_text(doc, existing + section)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
CODEX_HOME.mkdir(exist_ok=True)
|
||||
CODEX_SKILLS.mkdir(exist_ok=True)
|
||||
result = {
|
||||
"skills": migrate_skills(),
|
||||
"agents": migrate_agents(),
|
||||
"rules": migrate_rules(),
|
||||
"history": migrate_history(),
|
||||
}
|
||||
migrate_global_agents()
|
||||
update_migration_doc(result)
|
||||
print(json.dumps(result, ensure_ascii=False, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
36
tools/cursor/install_vscode_insiders_extensions.ps1
Normal file
36
tools/cursor/install_vscode_insiders_extensions.ps1
Normal file
@@ -0,0 +1,36 @@
|
||||
# 从 VSCode Insiders 迁移到 Cursor 的扩展补装脚本
|
||||
# 自动生成;安装旧 VSCode Insiders 中已安装但 Cursor 当前缺失的扩展。
|
||||
# 旧环境 disabled 状态见 docs/ai-env-history/vscode_insiders_extensions.csv;Cursor 无稳定 CLI 持久禁用接口,需在 UI 中按清单确认。
|
||||
$ErrorActionPreference = 'Continue'
|
||||
$cursor = 'cursor'
|
||||
$timeoutSec = 90
|
||||
$extensions = @(
|
||||
"leizongmin.node-module-intellisense"
|
||||
"meezilla.json"
|
||||
"ms-dotnettools.csdevkit"
|
||||
"ms-dotnettools.csharp"
|
||||
"ms-python.vscode-pylance"
|
||||
"ms-vscode-remote.remote-ssh"
|
||||
"ms-vscode-remote.remote-ssh-edit"
|
||||
"ms-vscode.remote-explorer"
|
||||
)
|
||||
foreach ($ext in $extensions) {
|
||||
Write-Host "Installing $ext ..."
|
||||
$p = Start-Process -FilePath $cursor -ArgumentList @('--install-extension', $ext) -NoNewWindow -PassThru -Wait:$false
|
||||
if (-not $p.WaitForExit($timeoutSec * 1000)) {
|
||||
Write-Warning "Timeout installing $ext; killing process and continuing."
|
||||
try { Stop-Process -Id $p.Id -Force } catch {}
|
||||
continue
|
||||
}
|
||||
Write-Host "Finished $ext exit=$($p.ExitCode)"
|
||||
}
|
||||
|
||||
|
||||
# 以下扩展在 VSCode Insiders 同步状态中为 disabled,安装后建议在 Cursor UI 中禁用:
|
||||
# disabled-before: leizongmin.node-module-intellisense
|
||||
# disabled-before: ms-dotnettools.csdevkit
|
||||
# disabled-before: ms-dotnettools.csharp
|
||||
# disabled-before: ms-python.vscode-pylance
|
||||
# disabled-before: ms-vscode-remote.remote-ssh
|
||||
# disabled-before: ms-vscode-remote.remote-ssh-edit
|
||||
# disabled-before: ms-vscode.remote-explorer
|
||||
1329
tools/cursor/migrate_ai_environment.py
Normal file
1329
tools/cursor/migrate_ai_environment.py
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user