fly-buddy
README.md
# fly-buddy — 果蝇 TUI 伴侣
[](LICENSE)
[](data/DATA_LICENSE.md)
> dsh-fly 果蝇桌宠的 TUI 重写(T64):GUI 退场,终端里养果蝇。
> T65 灵魂补全:**本体与显形分离**——headless daemon 是身体,TUI/MCP/CLI 只是显形,
> 关掉终端它还活着。
> 基于 [fiorastudio/buddy](https://github.com/fiorastudio/buddy) 二开(vendor pin
> `64c8061`,见 [VENDOR.md](VENDOR.md)),灵魂机制保留:**喂 token 长大**。
```
┌── 赛博果蝇 · 幼虫(Lv.2) ──────────┐┌── 状态 ───────────────────┐
│ ( o ) ││ 颤翅 · Fruit Fly │
│ ( ~ )-. ││ 视觉 ███████▓ 91 │
│ `---------' ││ 急脾气 █████▓░░ 73 │
└─────────────────────────────────────┘└────────────────────────────┘
┌── 成长(喂 token 长大:1 XP / 万 tokens) ─────────────────────────┐
│ [██░░░░░░░░░░░░░░░░░░] 幼虫 → 成蝇 28/200 XP │
└────────────────────────────────────────────────────────────────────┘
```
## dsh 果蝇生态
fly-buddy 是 **dsh 果蝇生态**第三名成员(v0.2.1 品牌归队):
- **JoeyTrribbiani/dsh-fly** —— Electron GUI 桌宠,生态的**脑**:行为状态机参数
出处,也是连接组数据源(`data/` 两份真实连接组从它原样拷贝)。
- **JoeyTrribbiani/cyber-fly-tui** —— Rust TUI 消费者,本仓 TUI 显形的 **PoC
前身**(三形态精灵帧由此移植)。
- **JoeyTrribbiani/fly-buddy**(本仓)—— buddy 底座上的 **headless 无形态生命**:
身体 = 账本 + 守护进程,TUI/MCP/CLI 只是显形。
数据流:dsh-fly 的连接组数据与喂食契约(feed-ration 三源 token 识别)→
fly-buddy 的 SQLite 账本(同一只蝇,账本即身体)→ 各显形消费。
<!-- TODO(T66):dsh-fly 与 cyber-fly-tui 两仓公开推送后,把上文仓库名补成链接
(暂只文字提及,防死链) -->
## 快速开始
```sh
npm install && npm run build
node dist/src/cli/main.js daemon # 起飞本体(headless 守护进程,脱钩终端)
node dist/src/cli/main.js tui # 终端养蝇(本体活着自动 linked;q 退出)
node dist/src/cli/main.js feed --in 250000 --out 30000 --source zcode
node dist/src/cli/main.js daemon status # 本体诊断(PID/快照摘要)
node dist/src/cli/main.js daemon stop # 优雅降落(账本无损)
node dist/src/cli/main.js status # 状态卡(首次自动孵化)
node dist/src/cli/main.js events # 账本流水
node dist/src/cli/main.js serve # MCP server(stdio)
python3 scripts/smoke.py # 真终端冒烟
npm test # 108 单测/集成
```
## 本体与显形(T65 哲学)
果蝇不以固定形态存在:**身体 = SQLite 账本 + headless 守护进程**,TUI/MCP/CLI
都是显形。
- **本体**(`fly-buddy daemon`):detached 脱钩终端(PPID=1),行为状态机常驻
500ms tick(只动内存,XP 无新增不空转写库);IPC 事件入口(UDS
`~/.fly-buddy/daemon.sock`,feed/emit 毫秒级进账);周期原子写显形快照
`~/.fly-buddy/growth-state.json`(行为变化/进账/60s 心跳)。幂等:重复起飞拒
绝并报 PID;`daemon stop` 优雅降落;launchd 开机常驻模板在 `docs/launchd/`。
- **显形**:TUI 启动探测本体——活着 = linked(读快照 + 订阅 IPC 推送,纯显形不
自 tick,标题标 `linked`,断连自动降级 solo);没跑 = solo 模式(本地自 tick,
`--solo` 强制)。MCP `fly_status`、CLI `status` 同样优先读本体。
- **降级契约**:daemon 没跑时 feed/emit 自动退化 solo 直写库(输出标注 solo,
exit 0——喂食器钩子静默友好语义不变);任何 IPC 失败也退 solo——账本正确性
优先于路由。
- **并发安全**:hatch/feed/emit 全部 IMMEDIATE 事务(事务内重读行)——daemon 进
账、solo 直写、MCP 喂食三方并发不丢账(WAL + busy_timeout=5000;并发冒烟测
锁在测试集里)。
## 架构(一段话)
**数据层**在 `src/flydata/`(加载 `data/` 下从 dsh-fly 拷贝的两份真实连接组并压成
电路度量)→ **个性派生**在 `src/personality/derive.ts`(五维 stats 从度量确定性
算出:视觉←LC4/LPLC2 占比、急脾气←兴奋/抑制边平衡、胆魄←命令通路密度、韧劲←
运动端占比、好奇←本体感受种类数;真实数据下 91/73/72/71/70,视觉最强、好奇垫底)
→ **伴侣核**在 `src/core/`(SQLite WAL 账本 `~/.fly-buddy/fly.db`,companions/
xp_events/evolution_history 表语义对齐 buddy;growth.ts 移植 dsh-fly 的 XP 表、
三阶段阈值与技能 gate;写路径 IMMEDIATE 事务)→ **本体**在 `src/daemon/`
(daemon.ts 生命周期 / life.ts 生命核+显形快照 / ipc.ts UDS+TCP 通道 /
protocol.ts NDJSON 协议 / paths.ts 住址)→ **喂养链路**在 `src/feed/tokens.ts`
+ CLI `feed`(三源 token 识别:--in/--out > 环境变量 > stdin JSON 四组字段名,
dsh-fly feed-ration 移植;1 XP/万 tokens 余数滚存,每笔进 xp_events 账本)
→ **TUI** 在 `src/tui/`(三形态精灵=成长阶段、成长条、事件流、Tab 观察面板;
500ms tick 暗色低存在感,fail 风暴才转红;行为状态机在 `src/behavior/`——昼夜
节律/变温节律/习惯化梯子/惊退起飞阈值全沿 dsh-fly 参数,resize=loom;linked/
solo 双形态)→ **MCP server** 在 `src/server/mcp.ts`(fly_status/fly_hatch/
fly_feed,可接 zcode/Claude Code)。
## 喂 token 长大(灵魂机制)
| 来源 | 优先级 | 示例 |
|---|---|---|
| `--in/--out` 显式 | 1 | `fly-buddy feed --in 250000 --out 30000` |
| 环境变量 | 2 | `FLY_BUDDY_TOKENS_IN=250000 FLY_BUDDY_TOKENS_OUT=30000 fly-buddy feed` |
| stdin JSON | 3 | `echo '{"usage":{"input_tokens":250000,"output_tokens":30000}}' \| fly-buddy feed` |
stdin 识别四组字段名:`usage.input_tokens/output_tokens`、
`usage.prompt_tokens/completion_tokens`、顶层 `input_tokens/output_tokens`、顶层
`tokens_in/tokens_out`(只认数字字段,字符串数字不猜)。换算:**1 XP / 万 tokens**
(in+out 合计,向下取整余数滚存)。阶段:幼虫 0-199 → 成蝇 200-999 → 老蝇
1000+(只升不降);成蝇解锁 perch 停靠与事件性理毛。
## 个性从数据来(不是随机数)
同一份连接组永远孵出同一性格。派生公式与锚点见
`src/personality/derive.ts` 头注释;真实数据的黄金值由单测锁定
(`src/__tests__/derive.test.ts`)——换数据=换性格,改公式必须过测试。
## vendor 纪律
`vendor/buddy/` 是上游原样快照(commit `64c8061`),**绝不修改**;二开只发生在
`src/`,经 import 复用其纯逻辑(leveling 曲线 / seededIndex+statBar / ANSI 常量)。
核验完整性与升级流程见 [VENDOR.md](VENDOR.md)。
## 数据许可
`data/` 两份连接组衍生数据从 dsh-fly 原样拷贝,来源与许可见
`data/DATA_LICENSE.md`、`data/LOCOMOTOR_PROVENANCE.md`(FlyWire Codex FAFB v783 /
MaleCNS v1.0)。
## 环境变量
| 变量 | 用途 |
|---|---|
| `FLY_BUDDY_HOME` | 果蝇的家(缺省 `~/.fly-buddy`——DB/sock/pid/log/快照都住这里;测试全套隔离用) |
| `FLY_BUDDY_DB` | 账本 DB 路径(缺省 `$FLY_BUDDY_HOME/fly.db`;单独挪账本用) |
| `FLY_BUDDY_DATA` | 数据目录(缺省仓内 `data/`) |
| `FLY_BUDDY_TOKENS_IN/OUT` | feed 的第二优先级 token 来源 |
| `FLY_BUDDY_NO_IPC` | `=1` 强制 solo 直写库(跳过 daemon IPC,调试/冒烟用) |
| `FLY_BUDDY_TCP` / `FLY_BUDDY_TCP_PORT` | `=1` 时 daemon 加监听 TCP 127.0.0.1(默认端口 39187);client UDS 失败补试 TCP |
| `FLY_BUDDY_DEBUG` | serve 模式打 stderr 心跳 |
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues