# omdsh-status [English](README.md) | 中文 哪个项目、哪个分支、动了多少 —— 一眼的事。这个插件把当前会话所在项目的名称和 git 状态,放进 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 网页界面 Work 窗口的会话顶栏,就在 Session log 导出按钮的左边: ``` omdsh-plugins ⎇ main ↑1 +2 ~3 ?1 ⬇ 导出 ``` 项目名取自会话所在的 workspace(就是侧边栏里那个组的标题);旁边是当前分支, 加上领先/落后、已暂存、已修改、未跟踪、冲突这几项里非零的计数。工作区干净的 时候就只有分支名;有冲突的时候,只有冲突那一个数字是红的 —— 一行里每个符号 都在喊,等于什么都没说。悬停提示里有完整目录、每一项计数的文字解释,以及刷 新失败时数字变旧的原因。 还没开始的会话根本没有顶栏 —— harness 把它整个藏了起来 —— 所以同一份读数改到 框架的浮层上,占住那一行自己的角落:还是那些像素,顶栏一回来就还回去。 ``` demo-project ⎇ main ~1 ?1 ✦ Files Terminal Into the Unknown 📁 demo-project ⌄ ⚙ Standard mode ⌄ ┌──────────────────────────────────────────────┐ │ Describe what you want to build │ ``` ## 它提供什么 | 界面 | 从哪来 | |---|---| | 会话顶栏里 Session log 按钮左边的读数 | `conversation.session.header.utilities`,ui-conversation 的尾部操作行 —— `order: -1` 让它排到默认顺序的 Session log 导出按钮左边 | | 新会话页上占住同一个角的那份读数 | `shell.overlay`,ui-layout 那层覆盖整个框架的浮层 —— 替补,给 harness 根本没画顶栏的那些会话用 | | `GET /omdsh-status/status` | `webServer`,背后是和 `/api` 网关同一道浏览器信任检查 | 没有设置项。这里没有一件事需要人来做决定 —— 项目名是 workspace 自己的标题, git 计数是工作目录自己的状态,轮询节奏是固定的。 ## 读数是怎么来的 两个半边,各自去问已经知道答案的人: - **项目名是浏览器自己推出来的。** 页面本来就持有 workspace 列表和会话列表: 当前会话属于哪个 workspace,哪个的标题就是项目名。不猜、不存,列表动了读 数就跟着动。 - **git 计数是 host 跑出来的。** 浏览器读不了 `.git`,所以 host 半边按会话 id 解析出它的工作目录,跑一次 `git --no-optional-locks -C <目录> status --porcelain=v1 --branch`,把解析 好的计数用 JSON 送回。**没有路径清单、没有 diff、没有分支图** —— 浏览器只 拿到它要画的那几个数字,路由背后则是和 `/api` 一模一样的信任检查。 - **新会话拿到同一个答案,位置也是同一个。** 会话还空白的时候,harness 会把 整个顶栏藏起来 —— 而那恰恰是最该知道分支的时刻:你正要说在这个项目里做什 么。所以第二份入口从框架的浮层替那一行站着:会话列的右上角,缩进量直接量 顶栏自己的 padding,行高也一样 —— 顶栏来了又走,读数不会跟着挪窝。顶栏那 一份一宣告自己在场,替补就退场,两边永远不会同时出现。这一点是从顶栏那一 份自己的挂载状态读出来的,而不是去猜"会话是不是空白" —— 后者是 ui-conversation 自己的规则,不该在这里抄一份。 - **那个角是共用的,没有登记处也排得好。** `omdsh-sidepanel` 的开关占着最外 端,`omdsh-sidechat` 的召唤图标贴着它们往里排;这份读数是顶栏那一行最左 边的一项,所以它的替补去量"已经缩得最靠里的那一个",再排到它里面。邻居量 的是相反的一端,依赖只往一个方向走,一趟就排定。全程只量框的位置,不读别 家插件的代码、状态、身份;角是空的就按空的量 —— 读数直接拿顶栏自己的 padding。 - **chat 不是项目。** profile 里装了模式系统时(`@omdsh-plugins/omdsh-basemode` 加 `@omdsh-plugins/omdsh-chatmode`),被它判定为 chat 的会话既不画读数也不 轮询:它的 workspace 是 harness 自己 home 底下那个托管目录 —— harness 留 的,不是谁选的项目。规则从 `sessionModes` 读,不自己再推一遍 —— 这样读数 和模式开关不可能互相打架。 开销压得很低:同一个目录的 git 结果缓存两秒,所有打开的标签页共用;可见的 页面每四秒轮询一次,后台标签页不轮询;冷会话的工作目录走持久化列表解析,那 份列表同样带缓存。 ## 少了几样东西时 - **目录不在 git 仓库里、机器上没装 git,或者 `git status` 超时。** 读数只 剩项目名,tooltip 写明"不是 git 仓库"。这些都不算错误 —— "这里没有 git" 就是诚实的答案。 - **没有选中会话,或者会话不属于任何 workspace。** 两个入口都不画 —— 这种状 态本来就没有项目可报。 - **Chat 模式。** 读数不画,轮询也停 —— chat 报不出项目。切回 Work,两样都 回来。 - **profile 里没有模式系统。** 那就没有任何会话算 chat,什么都不藏,行为和 这条规则出现之前一模一样。`sessionModes` 是在受限 fiber 里读的,从不进 `inject`,所以缺了它只丢这一条行为,别的一样不少。 - **Code 模式把会话列换成了终端。** 顶栏跟着一起没了,替补也不去占那个角 —— 那时那里是终端自己的横幅。宁可不画,也不画到别人的控件上;回到 Work 模式它就回来。 ## 安装 ```sh npx @omdsh-plugins/omdsh-plughub add omdsh-status ``` 这就是[插件中心](https://github.com/omdsh-plugins/omdsh-plughub)的安装器, 只是入口从按钮换成了 argv。它从这套集合的 [registry](https://github.com/omdsh-plugins/registry) 里解析出这个插件,从它 的 GitHub 仓库装好,并把那条 pnpm 构建白名单写好 —— 裸的 `dsh plugin add github:…` 会把这一步留给你;而那条记录里带的是 pnpm 实际解 析到的 commit,只能从报错里抄出来,事先写不出来。 `dsh plugin --profile web add @omdsh-plugins/omdsh-status` 现在**还不是**那 条命令:这个包不在 npm 上,pnpm 会回 `ERR_PNPM_FETCH_404`。同一次安装也可 以点按钮完成 —— 只要 profile 里已经有插件中心,按钮就在**设置 → 插件 → 插件 中心**里这个插件的卡片上。 或者从 checkout 装 —— 还没发布的构建需要用这种形式: ```sh pnpm install && pnpm run build dsh plugin --profile web add "$PWD" ``` 卸掉也走同一条路: ```sh dsh plugin --profile web remove @omdsh-plugins/omdsh-status ``` **这里没有任何插件是前提。** 两个半边 inject 的服务 —— host 侧是 `webServer`、 `webRuntime`、`sessions`,页面侧是 `slots`、`sessions`、`workspaces`、`locale` —— 全是 harness 自己的,两个落脚点也都走 `slots.inject`。唯一要读的别家服 务 `sessionModes`,是在 `apply` 内部的受限 fiber 里取的,所以没有模式系统的 profile 照常启动,只是什么都不藏。只装了它的 profile 照样能启动:没有 git 仓库就只显示项目名,没有会话就什么都不显示。卸掉它,读数、路由、轮询一起消 失,harness 一个字节都没改。 ## 命令 ```sh pnpm install pnpm run build # tsdown 打包宿主半边和浏览器半边 pnpm run typecheck pnpm run test pnpm run harness:local # 对着本地 harness checkout 开发时 pnpm run harness:npm # 提交前切回 registry pin pnpm run check:harness-pin # 还有 harness 依赖在 link 状态就失败 ``` 纯逻辑(porcelain 解析、项目名推导、chat 判定、轮询规则、路由行为,还有替补 给自己定位的那套角落算术)用 node 就能测;浏览器侧的插槽注册对着已发布的 harness pin 跑不了,因为发布包不带源码 —— 那部分走 `harness:local`。 `tests/git.run.spec.ts` 是唯一真的拉起 git 进程的 spec,仓库是它自己在临时 目录里建的。这几毫秒花得值:解析器的 spec 问不出"git 认不认这条命令",而发 布出去的那版参数把 `--no-optional-locks`(`git` 自己的选项,不是 `git status` 的)放在了子命令后面,于是每次读取都以 129 退出,每个仓库读出来都是"没有 git"。 ## 已知限制 - **两个座位都是借来的。** 读数挂在会话顶栏的尾部操作行上;那一行不在的时 候,就挂在它本该占的那些像素上。Code 模式把整列换成终端,两个座位一起带 走 —— 那种状态下读数干脆不画:退化成"什么都不显示",而不是"显示错的东 西"。 - **替补对齐的是这一行现在真有的东西,不是顶栏画出来时会有的东西。** 顶栏 在的时候,Session log 导出按钮夹在这份读数和它的邻居之间,而那个按钮没有 自己的替补 —— 新会话还没有东西可导出。所以在新会话页上,读数比顶栏回来 后的位置偏右一个按钮的宽度。给一个没人画的控件预留位置是猜,占住这一行的 高度和角是量。 - **计数是轮询,不是流。** 改动要等下一轮轮询才出现,所以读数最多落后索引 几秒;某一轮失败就保留上一组数字并调暗,直到有一轮成功。 - **"没有 git"是一张很宽的网。** 没装 git、git 拒绝读取的仓库(dubious ownership)、`git status` 超时,读出来都是"不是 git 仓库" —— tooltip 不 区分这几种情况。