Skip to main content
Glama
README.md
# 星隐录 · Veiled Stars

**一本和自家 agent 一起维护的私人塔罗手帐**——可检索、可追加、可回望。
由一个 MCP server 和一套手机优先的网页界面组成,全部数据留在你自己的机器上。

它不是牌意字典,而是**你自己的经验库**:记录一张牌在当时的问题里如何说话,
也记录事情发生之后,你怎样重新理解那张牌和当时的自己。

## 开箱就有

脏活都跑完了。clone 下来就是一套能直接过日子的成品:

- **78 张牌面牌背已经备好。** 1909 年公版 RWS「Roses & Lilies」全套,
  已下载、核对校验和、转成 WebP 打包在仓库里。前端直接渲染——
  不用满网找图、不用配 CDN、不用担心版权。
- **自带一部中文小词典。** 78 张牌的正逆位牌意,原创中文,随包只读分发,
  抽完牌轻触即看。不合口味就跑 `tools/tarot_meaning_builder` 那条流水线换成
  你自己的体系(来源对齐 → 生成 → 验证 → 人工逐张接受 → 构建,每步 sha256 封印)。
- **解牌 = 词典 + AI 自己的知识。** 星隐录只端事实:这张牌、这个牌位、这个正逆位、
  词典条目、以及你以前在什么处境下抽到过它。怎么解交给正在陪你聊的那个 AI,
  用它自身的知识结合你的历史来说话。你也可以把自己收藏的塔罗资料丢给 AI 一起琢磨——
  那是 AI 那一侧的自由,星隐录不插手也不拦。
- **能塞进 Telegram 小程序,随聊随抽。** 前端已经做好门牌前缀自举和身份钥匙转发,
  挂进你自己 bot 的小程序里,手机上点开就抽,抽完直接存档。
  配方见 [`docs/DEPLOYMENT.md`](docs/DEPLOYMENT.md)。
- **零依赖,一条命令起飞。** 核心与 Web 只用 Python 3.12 标准库:不用 Docker、
  不用 Node 构建、不用数据库服务,页面也不加载任何第三方脚本、字体或统计。
- **数据就是你自己的一个文件。** 一个本地 SQLite,随时拷走。不上云、不联网、
  不自动记录——只有你明确说「存下来」才写库。
- **解读不会被当成证据。** 原始经历、当次解读、经验结晶是隔离的三层:AI 说的话
  永远只算待验证假设,不能反过来充当新的现实样本。这道墙防的是「AI 解读自己引用自己、
  越滚越信」,也是这套档案敢长期用下去的底气。

## 它解决什么

抽了两年日运,散落在聊天记录、备忘录和照片里。想问「宝剑女王上次出现是什么时候,
那件事后来怎么样了」,翻不到。

星隐录把这件事变成一次查询:输入一张牌,返回它在你个人历史里的全部语境——
出现日期、正逆位、当时的问题、所在牌位、当时的解读、后来追加的回顾。

而因为它是 MCP server,你可以直接在 Claude Code、Claude Desktop、Codex 这类客户端里
对 AI 说「帮我看看星币七这张牌以前都在什么处境下出现」,AI 会替你查库并结合上下文
解读。**MCP 只返回历史事实,不生成牌意总结**,归纳交给正在陪你聊的那个 AI。

## 细看能力

- **五种牌阵**:单张日运、三张时间流、四季牌阵(大牌+四元素五组分抽)、二选一、自定义三张。
- **系统代抽或实体抽牌**:代抽走操作系统安全随机源,每张牌独立生成正逆位;
  也可以把你手抽实体牌的结果原样录进来。
- **多人解读时间线**:同一次牌阵可以保存多段解读,各自署名来源;回顾只追加不覆盖,
  三个月后回头补一句「后来应验了」,旧内容一个字不会被盖掉。
- **经验结晶**:反复验证过的私人牌意可以沉淀成一条「结晶」,按正逆位分开、
  记录成熟度、挂上支撑它的原始记录;成熟度变更与归档必须显式确认,只归档不删除。
- **中文检索**:SQLite FTS5 `trigram` 支持三字以上子串命中,一两字查询回退到转义 `LIKE`;
  牌名先经别名表归一(「宝剑女王」和「宝剑皇后」是同一张)。
- **网页牌桌**:78 张牌背扇形铺开手选,两段确认,抽满统一翻面;全程零牌意干扰,
  翻开后可轻触单张查看词典小卡。
- **写入耐操**:每次写入带幂等键,客户端重试不会写出第二条;单一事务,失败整笔回滚。

## 界面

- **首页**:两个入口,抽牌 / 档案。
- **星迹索引**:四行检索(问题 / 单牌 / 牌阵 / 日期区间)+ 按月成历的星迹列表,
  每行带真牌面缩略图,逆位倒放。
- **单次详情**:牌、牌位、正逆位、多人解读时间线与后续回顾。
- **牌桌**:全屏一幕的手选抽牌。

设计语言为「晨光刻纸」:暖米白纸底、单一淡金细线、衬线体、大留白,移动端优先。
首页背景图需自备,见 `src/veiled_stars/web/assets/README.md`;不放也能正常使用。

## 安装

需要 Python 3.12+。

```bash
git clone https://github.com/SeithAsync/veiled-stars.git veiled-stars
cd veiled-stars
python3 -m pip install -e '.[mcp]'   # 只用网页界面的话,装 -e . 即可
```

### 启动网页界面

```bash
VEILED_STARS_DB=/绝对路径/veiled-stars.db veiled-stars-web --port 8765
```

服务**强制绑定回环地址**,打开 http://127.0.0.1:8765 即可。要在手机上用,
先读 [`docs/DEPLOYMENT.md`](docs/DEPLOYMENT.md)——**这个应用自身没有任何身份验证**,
必须由外层网关把门。

### 挂上 MCP 客户端

以 stdio 启动,例如 Claude Code:

```bash
claude mcp add veiled-stars \
  --env VEILED_STARS_DB=/绝对路径/veiled-stars.db \
  -- veiled-stars-mcp
```

或在客户端配置文件里写:

```json
{
  "mcpServers": {
    "veiled-stars": {
      "command": "veiled-stars-mcp",
      "env": { "VEILED_STARS_DB": "/绝对路径/veiled-stars.db" }
    }
  }
}
```

首次连接前,建议先用一个临时数据库路径跑一遍工具列表验收,再换成真实档案。

> Codex CLI 用户注意:0.144 版本会对未声明只读注解的 MCP 工具逐次弹审批,
> 桥接或无人值守环境下无人应答即自动取消。在 `~/.codex/config.toml` 的
> `[mcp_servers.veiled-stars]` 段落加 `default_tools_approval_mode = "approve"` 可解。

## MCP 工具

| 工具 | 作用 |
|---|---|
| `list_spreads` | 读取五种牌阵及牌位规则 |
| `draw_spread` | 代抽并返回结果,明确标记为未保存 |
| `get_base_meaning` | 按牌名或别名读取只读基础牌意 |
| `write_reading` | 唯一写入口,处理牌阵、解读、回顾与结晶的显式写入 |
| `search_readings` | 按关键词、牌、正逆位、牌阵、日期组合检索 |
| `list_pending_readings` | 分页列出已保存但还没有解读的牌阵 |
| `card_history` | 单牌个人历史,分区返回原始证据与经验假设 |
| `get_reading` | 读取一次完整牌阵及全部解读、回顾与关联结晶 |

`write_reading` 的参数是 `action`、`idempotency_key` 和对应 `payload`。
基础动作为 `create`、`add_interpretation`、`add_reflection`、`update`;
结晶动作为 `create_insight`、`link_insight_evidence`、`revise_insight`、
`set_insight_maturity`、`archive_insight`。后两者必须传 `owner_confirmed: true`。

**代抽不会暗中调用写入。** 同一幂等键重试只会产生一笔记录。

## 数据边界

- 数据库路径由 `VEILED_STARS_DB` 指定,仓库里不带任何真实数据。
- 所有时间戳以 UTC 存储,前端按本地时区显示;「日运属于哪一天」单独作为业务日期存储。
- SQLite 启用外键、WAL 与忙等待;每次写入在单一事务中完成,失败整笔回滚。
- 网页写接口仅四个 `/api/draw-sessions` 同源 JSON POST 路由,要求专用 intent header、
  请求体上限 8 KiB、严格校验字段、不开放 CORS;其余档案接口只读。
- 抽牌会话存活在服务进程内存里:服务器预先洗牌并封存正逆位,浏览器只拿到不透明
  牌背 token,抽满前拿不到牌面,未点「保存牌阵」不写库。默认 30 分钟过期,
  刷新可恢复,重启即失效。

表结构见 [`docs/schema.md`](docs/schema.md)。

## 基础牌意从哪来

`src/veiled_stars/data/base_meanings.production-v2.json` 里的 78 张中文牌意是本项目
原创文本,由 `tools/tarot_meaning_builder/` 这条离线流水线产出:来源对齐 → 生成 →
验证 → **人工逐张接受** → 确定性构建,每一步产物都带 sha256 封印,`build.py` 不调用
模型也不会隐式生成。牌意与运行时数据库完全隔离,只读。

想换成自己的牌意,照 `tools/tarot_meaning_builder/README.md` 重跑这条流水线即可。

牌面图片是 1909 年 Pamela Colman Smith 绘制的 Rider–Waite–Smith「Roses & Lilies」版,
公有领域,权利依据与校验信息见 `src/veiled_stars/web/cards/rws/LICENSE`。

## 开发

无需安装依赖即可跑全部测试:

```bash
PYTHONPATH=src python3 -m unittest discover -s tests -v
PYTHONPATH=src python3 -m unittest discover -s tools/tarot_meaning_builder/tests -v
python3 -m compileall -q src tests tools/tarot_meaning_builder
node --check src/veiled_stars/web/app.js
```

测试只创建临时数据库,不读写任何真实档案。

### 代码地图

| 文件 | 职责 |
|---|---|
| `catalog.py` | 78 张牌与牌名别名 |
| `base_meanings.py` | 严格校验并只读加载基础牌意 |
| `spreads.py` | 五种牌阵及牌位约束 |
| `drawing.py` | 安全随机抽牌 |
| `database.py` | SQLite 连接设置与版本化迁移 |
| `repository.py` | 事务写入与完整记录读取 |
| `services.py` | 组合检索、分页与单牌历史 |
| `mcp_tools.py` | 八个 MCP 工具的参数与返回合同 |
| `mcp_server.py` | 可选 FastMCP stdio 入口 |
| `draw_sessions.py` | 手选会话、抽满揭示与幂等保存 |
| `card_assets.py` | 牌面静态资源清单 |
| `web_api.py` | 只读浏览接口与受控抽牌 POST API |
| `web_server.py` | 强制回环的本地服务入口 |
| `web/` | 首页、星迹索引、详情与牌桌界面 |

## 许可

代码与随附中文牌意文本采用 MIT 许可,见 [`LICENSE`](LICENSE)。
牌面图片为公有领域素材,另附权利说明。