# AGENTS.md — AI 协作代理工作规范 > 本文件面向在此仓库工作的 AI 编码代理(Claude Code / DSH Agent / 其他)。 > 人类读者请先看 `README.md`(项目全貌与演进史),本文件只讲**怎么正确地改这个项目**。 ## 项目一句话定位 `@shuaihaov/dsh-theme-wandering-earth-2` — DeepSeek Harness (DSH) Web GUI 的《流浪地球2》电影风格主题插件包:行星发动机插图沉底、会话运行状态点火联动(burn/idle)、MOSS 化发送/停止按钮(三态:灰/蓝光/蓝光呼吸)、星场 HUD 氛围层。经 `~/.dsh/profiles/web`(`dsh plugin add` 安装 + patch insert)持久挂载,DSH 重启自动加载。 ## 协作基础规范 - 🔴 **必须**:思考、推理、输出全部使用中文 - 修改文件后说明关键变更点,不啰嗦 - 不主动 `git commit` / `push`、不安装全局依赖 - 临时文件一律放 `.tmp/` 目录(已 gitignore,永不提交) ## 架构速览(改代码前必读) ``` ┌─ Host 半 src/index.js ──────────── 直接加载(无需构建) │ inject: ['webServer'] │ 注册 /twe2-assets/* 前缀路由 → serve assets/ 内 PNG/JPG │ ├─ Client 半 src/client/index.js ──── 源码(人改这里) │ inject: ['theme', 'slots'] │ overrideTokens 配色 + HUD CSS + EngineBeacon 状态桥 │ + MOSS 化发送按钮(纯 CSS 换肤 shell 主按钮,详见 README v7.9 节) │ │ │ ▼ pnpm run bundle(scripts/build.mjs,零依赖) │ lib/client.js ────────────────── 构建产物(机生成,勿手改) │ │ │ ▼ window.__ModuleLoader__.load({ id, factory }) │ 浏览器加载执行 │ └─ assets/ 插图 ← .tmp/ 生成管线产出(genimg*.py 生成 + cutout*.py 去背) ``` **部署链路**:本仓库经 `link:` 挂载进 `~/.dsh/profiles/web/package.json`,`~/.dsh/profiles/web/cordis.patch.yml` 中 `theme-wandering-earth-2` insert 行使其随 DSH 启动加载。改本仓库代码 **不自动生效**,见下方工作流。 ## 硬性约束(🔴 违反即产出错误结果) 1. **改 Client 半源码后必须 `pnpm run bundle`**——浏览器只加载 `lib/client.js`,不读 `src/client/index.js`。bundle 内自动注入构建 rev 做 cache-bust,改完 bundle 刷新页面即可。 2. **改 assets/ 图片后必须 bump `TWE2_ASSET_VER`**(`src/client/index.js` 内的 `?v=` 版本号)再 `pnpm run bundle`,否则浏览器继续用旧缓存图。 3. **两半的 `inject` 声明不可删**: - Host 半 `export const inject = ['webServer']` - Client 半 `var inject = ['theme', 'slots']` 原因:本插件行位于组合树尾部,fiber 激活早于目标服务注册;用 `ctx.get` 会竞态落空——Host 半表现为插图 404 落 SPA 首页,Client 半表现为主题静默不注册。此坑已在 v7.1/v7.2 修复,勿回退。 4. **配色只走 `theme.overrideTokens`,不走主题注册路线**。DSH 外观偏好经 zod 枚举只持久化 light/dark/system 三选,第三方主题 `setTheme` 只改内存不落盘,刷新即回落。overrideTokens 叠加层压在任何当前主题之上,切换/刷新均不失效(v7.4 定论,勿改回)。 5. **overrideTokens 的每个 token 必须给 `{ light, dark }` 双值对象**——运行时校验,裸字符串直接抛错。 6. **`.tmp/genimg*.py` 含明文 API key,`.tmp/raw/` 为大批量原图——两者都已 gitignore,永不提交、永不复制进文档或回复**。 7. **`lib/client.js` 是构建产物**——只改 `src/client/index.js`,产物靠 `pnpm run bundle` 再生;`scripts/build.mjs` 会把源码整体缩进包进 factory,手改产物下次构建即被覆盖。 8. **Host 半资产白名单是安全面**:`ASSET_NAME` 正则(小写 slug + png/jpg 扩展名)防路径穿越。放宽校验前先想清楚 serve 的是包内 `assets/` 目录。 9. **模块顶层语句的执行顺序即依赖顺序**:新增顶层常量/赋值前,先核对它引用的变量是否已在更早的行完成**初始化**(`var` 只提升声明不提升赋值)。factory 顶层一炸就是整个 client bundle 加载失败、dsh 崩溃——参考 `--twe2-moss-url` 必须位于 `TWE2_IMG` 定义之后(v7.9.1 教训)。 ## 分场景修改工作流 | 场景 | 步骤 | | --- | --- | | 改 Client 半样式/逻辑 | 编辑 `src/client/index.js` → `pnpm run bundle` → 刷新页面 | | 改 Host 半路由 | 编辑 `src/index.js` → **重启 DSH**(Host 半无构建,但走进程加载) | | 换 assets/ 插图 | 生成/处理新图放入 `assets/` → bump `TWE2_ASSET_VER` → `pnpm run bundle` → 刷新 | | 调整配色 | 改 `TWE2_TOKENS_DARK` / `TWE2_TOKENS_LIGHT`(两槽位都要动)→ bundle → 刷新 | | 彻底停用主题 | 编辑 `~/.dsh/profiles/web/cordis.patch.yml` 删除 `theme-wandering-earth-2` insert 段 → 重启 DSH | 验证插图路由:`curl -sI http://127.0.0.1:3080/twe2-assets/moss-eye.jpg` 应返回 200 + `image/jpeg`。 ## 插图生成管线(.tmp/) - **生成**:`.tmp/genimg*.py` 调 Right Code 平台 `nano-banana-2`(gemini-3.1-flash-image),原图落 `.tmp/raw/`(engine13-burn / engine14-idle 为当前深色版基准,engine15/16 为浅色版) - **去背**: - 深色底图用 `.tmp/cutout.py` —— 黑底按亮度映射 alpha(`max_size=1400` 保全分辨率,避免放大模糊) - 浅色底图用 `.tmp/cutout_light.py` —— 按**与背景色的欧氏色差**映射 alpha(亮度法会误伤亮蓝光柱) - **单一参考链原则**:idle 版必须以同代 burn 版为**唯一参考**生成(仅熄灭光柱)。历史上双参考链导致机位/色调漂移(engine12 两张横版各自扩竖版色调不匹配,已废弃) - **分辨率**:去背 `max_size` 不低于 1400(CSS 以 `auto 100vh` 渲染,低分辨率会被放大 1.2~1.8 倍而模糊——v7.6 教训) ## 踩坑档案(历史教训,勿重蹈) | # | 坑 | 结论 | | --- | --- | --- | | 1 | 扫描线滤镜晃眼 | v1 方案被否决,removed | | 2 | 氛围层挂业务容器背景 | 被容器不透明背景遮盖,改挂 `body::before/after` | | 3 | 第三方主题无法持久化选中 | 外观偏好 zod 枚举只收内置三选 → 改 overrideTokens 常驻叠加(v7.4) | | 4 | fiber 激活早于服务注册 | `ctx.get` 竞态落空 → 必须声明 `inject` 硬依赖(v7.1/v7.2) | | 5 | 插图 URL 无 cache-bust | 浏览器持旧缓存 → `?v=TWE2_ASSET_VER`(v6.3) | | 6 | 低分辨率去背图被 CSS 放大 | 模糊根因是 `max_size=800` → 提至 1400(v7.6) | | 7 | 黑底亮度去背用于浅色图 | 亮蓝光柱被误判为背景 → 浅色图专用色差法(v7.7) | | 8 | 双参考链生成 idle | 机位/色调漂移 → 单一参考链(v6.3 定论) | | 9 | shell 主按钮 hover 用 `background` 简写 | 特异性高于换肤 base 规则,背景图被重置为 none → hover 态必须重申 `background-image`(v7.9) | | 10 | 用 `ctx.get` 读 theme/slots/webServer | 组合树尾部 fiber 竞态 → 必须 `inject` 声明(同 #4,重申以防回退) | | 11 | 顶层引用先于初始化:`TWE2_TOKEN_OVERRIDES['--twe2-moss-url']` 在 `TWE2_IMG` 定义前读 `.moss` | `var` 只提升声明不提升赋值,执行时 undefined → `Cannot read properties of undefined` → factory 顶层炸、**整个 client bundle 加载失败、dsh 崩溃**。修复:赋值移到 `TWE2_IMG` 之后(源码有顺序约束注释,勿挪回 token 段)。**新增顶层常量时先核对被依赖物的定义顺序**(v7.9.1) | ## 版本约定 - 主题演进版本 vX.Y 记录在 README「演进记录」节,每次功能性变更追加一条并 `git commit`(message 格式:`vX.Y: 摘要`) - `package.json` 的 `version` 字段是包管理版本线(当前 0.0.1),与演进版本独立,不必强制同步 - 文档变更单独 commit(`docs: ...`) ## 快速命令 ```bash pnpm run bundle # 重建 lib/client.js curl -sI http://127.0.0.1:3080/twe2-assets/moss-eye.jpg # 验证 Host 路由 git log --oneline -10 # 查看演进版本线 ```