副屏状态灯(status-light)
把一台旧手机(或任意一个浏览器标签页)变成 WorkBuddy 工作状态的指示灯。本技能内置一个零第三方依赖的本地 Node 服务与一套三色灯全屏页面;手机浏览器打开后实时跟随状态变色。同时本技能约定了在任务关键节点切灯的规范,让助理在黄 / 红 / 绿之间按约定切换。
何时使用
- 用户希望用一台旧手机 / 浏览器实时看到 WorkBuddy 是否在忙、是否需要他介入、是否已完成。
- 需要为某个长任务提供一块"物理状态牌",减少来回切窗口确认的成本。
- 触发词(可作为提示):副屏状态灯、状态灯、手机副屏、红绿灯状态。
内置组件(随技能打包,路径相对于技能根目录)
server.js— 零依赖本地 HTTP 服务:托管页面 + 状态上报 / 查询 + SSE 广播,监听0.0.0.0:8765。index.html— 三色灯全屏单文件页面(EventSource 收状态、Screen Wake Lock 常亮、全屏 / 退出全屏按钮、原生语音播报与静音开关),无任何外部资源、无框架。package.json—npm start等价node server.js,无 dependencies。set-state.ps1— Windows 一键切灯脚本(接收yellow|green|red|idle)。scripts/setup.mjs— 安装器:将运行时文件复制到专用运行目录(默认~/.workbuddy/status-light-runtime/),并打印启动命令。qa-test.mjs/qa-ui-check.mjs— 内置自动化测试(见"测试"一节)。
首次部署(一次性)
两种方式任选其一:
方式 A — 专用运行目录(推荐)
1. 安装运行时(在任意终端):
bash
node <技能目录>/scripts/setup.mjs
该脚本把 server.js / index.html / package.json / set-state.ps1 复制到 ~/.workbuddy/status-light-runtime/(已存在则覆盖,幂等)。
2. 启动服务:
bash
node ~/.workbuddy/status-light-runtime/server.js
方式 B — 直接从技能目录运行
bash
node <技能目录>/server.js
<技能目录> 通常为 ~/.workbuddy/skills/status-light/。
启动后控制台打印本机地址与手机访问地址(局域网 IP)。
防火墙:若手机打不开页面,在 Windows 防火墙放行 node 程序入站(TCP 端口 8765)。
手机端使用
- 手机与电脑连同一 Wi-Fi,浏览器打开控制台显示的局域网地址(如
http://10.x.x.x:8765)。 - 页面自动尝试全屏 + 屏幕常亮(Screen Wake Lock API);被系统拦截时点页面上的「进入全屏」按钮,并在手机设置关闭自动锁屏。
- 之后页面实时跟随状态变色。
- 状态变化会用手机原生语音朗读(如「工作中」「需要你介入」);底部「语音开 / 语音关」可随时静音。iOS 需在页面上先点一下任意按钮以解锁语音(系统限制)。
切换灯(在助理侧通过 Bash 执行)
curl -s "http://localhost:8765/api/state?state=yellow" # 工作中
curl -s "http://localhost:8765/api/state?state=red" # 需要你介入
curl -s "http://localhost:8765/api/state?state=green" # 完成
curl -s "http://localhost:8765/api/state?state=idle" # 待机
> 若当前环境 curl 不可用,改用 PowerShell:
> powershell -ExecutionPolicy Bypass -File <运行目录>/set-state.ps1 yellow
打灯时机约定(核心)
优先级铁律:收到用户消息 是所有状态切换的最高优先级触发器——每条用户新消息到达,无条件先打黄(我开始思考 / 处理),再按后续规则决定灯色。任何"问用户前打红"的约束都不得覆盖这一条。
- 接到用户消息 / 指令的那一刻、开始思考即打黄——早于真正调用工具;分析、澄清、规划也算"工作中"。适用每一条用户消息,包括"需要我反问 / 确认"的消息。
- 若分析后判定需要问用户(AskUserQuestion / 等确认 / 等补充)→ 从当前状态切红。此时通常为 黄 → 红(收到消息已先黄,再转红),这是合法且预期的顺序,并非"红一闪就黄"。
- 任务交付前 → 打绿。
- 长时间空闲 → 可选打灰 / idle。
> ⚠️ "红必须先于黄"的原始约束仅指:不要在没有新用户消息的情况下,自发地先黄后红(例如自己主动干活中途莫名其妙黄了又红)。它不适用于"用户消息触发黄、随后因要问用户转红"的场景——后者黄先红后,语义清晰(从主动处理转为等你),必须允许。
红灯 → 黄灯的边界(避免"红一闪就黄")
红灯 = 被用户 block、在等待。只要用户还没答完、助理还没真正继续干活,红灯必须保持常亮,绝不允许中途提前切黄。
- 收到用户消息 → 先黄(铁律,无条件)。
- 分析后这轮要问用户 → 黄切红(不要黄红并存,直接转红)。
- 问题展示期间、用户正在选 / 正在打字 → 保持红,不要动灯。
- 用户回答回来 → 红灯继续保留,不要立刻切黄(此刻只是"收到答案",还没开工)。
- 真正开始继续工作那一刻(即准备调用工具 / 开始分析 / 撰写正式回复的实质性内容之前)→ 才切回黄。
- 一句话记忆:红撑到"用户答完 + 我开始干"的交界点,黄从"我开始干"那一刻才亮。 绝不在用户尚未回答、或刚回答完我还没起步的间隙提前切黄。
其他需人工介入的场景(同理)
- 等用户补充信息、确认方案、上传文件等 → 收到消息先黄,再切红并保持,直到用户给齐、助理重新开始工作才转黄。
- 若一轮里先打黄在分析、中途才发现问题要问用户,应把黄切到红(不要黄红同时),问完回到黄继续。
容错(重要)
- 若
curl连接失败(状态服务未启动)——静默忽略,不要向用户报错、不要重试、不要因此中断主任务。这只是辅助显示,不是主流程依赖。 - 一次失败即跳过本次打灯。
接口速查
| 接口 | 说明 |
|---|---|
POST/GET /api/state?state=yellow\|green\|red\|idle |
设置状态(非法值返回 400) |
GET /state |
读取当前状态 JSON |
GET /stream |
SSE 实时广播状态变化 |
GET / 或 /index.html |
返回三色灯页面 |
测试(内置)
在技能目录或运行目录执行:
node qa-test.mjs # 后端接口 + SSE 广播(6 用例)
node qa-ui-check.mjs # 前端布局 / 媒体查询静态校验(24 项)
两项均零依赖、可重复运行、干净退出子进程。前端灯效 / 常亮 / 全屏属真机视觉行为,需人工在手机确认。
工作原理(务必先读,避免"别的任务没亮"的困惑)
本技能由两部分组成,理解它才能知道灯为什么不亮:
- 状态服务(server.js):一个机器级 Node 进程,监听
0.0.0.0:8765,只认 curl 命令,自己不会感知 WorkBuddy。只要进程活着,任意任务 / 终端都能通过localhost:8765切灯——它是跨任务共享的"灯控总线"。 - 打灯约定(本文件上面的时机规则):一份给"当前对话的助理"看的行为规范——收到消息打黄、问前打红、交付打绿。灯色变化完全依赖该对话的助理按约定主动 curl。
⚠️ 因此本技能不是后台守护进程,也不会自动监听你所有任务。 它做不到"全局自动触发"——WorkBuddy 尚未开放"任务状态变化"的事件接口给第三方脚本。任何一个新开的任务,只有当"那个任务的助理"加载并遵循这份约定时,灯才会亮。
在任意任务启用(三选一,按稳妥程度排序)
- 任务开头说一句话(最稳、即时):在想用状态灯的任务里,开头说「用副屏状态灯」或「加载 status-light 技能」。这会加载技能,并提示该任务的助理按约定打灯。
- 长期记忆规则(推荐长期使用):在跨项目长期记忆(
~/.workbuddy/MEMORY.md)加一条,例如:
> 需要状态灯时主动Skill status-light加载,并按约定切灯:收到用户消息→黄、准备问用户→红、交付→绿、空闲→idle。
这样本机所有对话的助理读到的都会自觉接管。注意:是否真正执行仍取决于该对话的助理是否遵循长期记忆,属"软约束",不保证 100% 触发。 - 手动切灯(调试 / 演示,不依赖助理):直接用上面的 curl /
set-state.ps1命令即可,适合验证服务是否连通。
> 某次没亮时先排查两步:① 状态服务是否在跑——curl -s localhost:8765/state 能返回 JSON 即正常,否则去启动 server;② 该任务是否真的加载并遵循了约定(见上面三种启用方式)。