From e38e56ba3eb30ae99147a53aef608c58975555d2 Mon Sep 17 00:00:00 2001 From: manpengan Date: Sun, 22 Mar 2026 08:48:25 +0800 Subject: [PATCH] docs: add codex-hud design spec and original plan Design spec covers dual-mode architecture (tmux pane + inline fallback), SQLite/JSONL data layer, HUD layout, installation, and error handling. Co-Authored-By: Claude Opus 4.6 --- .../2026-03-21-codex-hud-global-wrapper.md | 502 ++++++++++++++++++ .../specs/2026-03-22-codex-hud-design.md | 354 ++++++++++++ 2 files changed, 856 insertions(+) create mode 100644 docs/superpowers/plans/2026-03-21-codex-hud-global-wrapper.md create mode 100644 docs/superpowers/specs/2026-03-22-codex-hud-design.md diff --git a/docs/superpowers/plans/2026-03-21-codex-hud-global-wrapper.md b/docs/superpowers/plans/2026-03-21-codex-hud-global-wrapper.md new file mode 100644 index 0000000..2e67800 --- /dev/null +++ b/docs/superpowers/plans/2026-03-21-codex-hud-global-wrapper.md @@ -0,0 +1,502 @@ +# Codex HUD Global Wrapper Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Build a global `codex` HUD wrapper that keeps the existing `codex` command unchanged for the user while rendering a dense, always-visible terminal HUD with model, path, git, tool activity, agent activity, todo progress, and best-effort context/usage signals. + +**Architecture:** Implement a user-level Node.js TUI wrapper that launches the real Codex binary through a PTY, observes available runtime signals, normalizes them into HUD state, and draws a fixed bottom status area in the same terminal. Install it globally by placing a wrapper `codex` ahead of the real binary and preserve a safe bypass and uninstall path. + +**Tech Stack:** Node.js 20+ · TypeScript · PTY process bridge · terminal UI rendering · user-level install scripts · zsh/macOS target first + +--- + +## File Structure + +``` +/Users/manpengan/pro/codex-hud/ +├── package.json +├── tsconfig.json +├── README.md +├── bin/ +│ ├── codex-hud +│ └── codex-hud-install +├── scripts/ +│ ├── install-global.sh +│ ├── uninstall-global.sh +│ └── doctor.sh +├── src/ +│ ├── cli.ts # entrypoint for hud launcher +│ ├── install.ts # install/uninstall/doctor commands +│ ├── config.ts # local config + defaults +│ ├── terminal/ +│ │ ├── renderer.ts # bottom HUD drawing +│ │ ├── layout.ts # dense/compact layout rules +│ │ └── tty.ts # resize, raw mode, cleanup +│ ├── launcher/ +│ │ ├── resolve-codex.ts # find real codex binary +│ │ ├── spawn-codex.ts # launch codex in PTY +│ │ └── argv.ts # passthrough args, --no-hud handling +│ ├── state/ +│ │ ├── store.ts # in-memory normalized HUD state +│ │ ├── model.ts # model/session metadata +│ │ ├── git.ts # cwd + branch + dirty status +│ │ ├── timer.ts # session elapsed time +│ │ ├── tools.ts # tool activity tracking +│ │ ├── agents.ts # agent activity tracking +│ │ ├── todos.ts # todo progress extraction +│ │ ├── context.ts # context/usage native-or-estimated signals +│ │ └── status-labels.ts # "native", "estimated", "n/a" labels +│ ├── observers/ +│ │ ├── stdout-parser.ts # best-effort parsing from visible output +│ │ ├── app-server.ts # optional official signal source if available +│ │ ├── session-files.ts # inspect readable local Codex state when present +│ │ └── process-tree.ts # codex child process lifecycle observation +│ └── types/ +│ └── hud.ts # shared event/state types +└── docs/ + └── superpowers/ + └── plans/ + └── 2026-03-21-codex-hud-global-wrapper.md +``` + +## Product Rules + +- The user keeps typing `codex`; no new daily command should be required. +- The wrapper must support `codex --no-hud` to bypass the HUD immediately. +- The wrapper must not destroy terminal state on crash; cleanup and cursor restore are mandatory. +- Dense mode is the default. +- Any metric that is not guaranteed by Codex must be marked `estimated` or `n/a`; never invent precision. +- Install must be reversible in one command. + +## Task 1: Scaffold the project and baseline CLI + +**Files:** +- Create: `/Users/manpengan/pro/codex-hud/package.json` +- Create: `/Users/manpengan/pro/codex-hud/tsconfig.json` +- Create: `/Users/manpengan/pro/codex-hud/README.md` +- Create: `/Users/manpengan/pro/codex-hud/src/cli.ts` +- Create: `/Users/manpengan/pro/codex-hud/src/launcher/argv.ts` + +- [ ] **Step 1: Create package manifest with executable bins** + + Add package metadata, `type`, build scripts, and bins for `codex-hud` and installer commands. + +- [ ] **Step 2: Create TypeScript config** + + Set Node-targeted compilation, strict mode, and `dist/` output. + +- [ ] **Step 3: Create CLI entrypoint** + + Parse argv, recognize `--no-hud`, and route either to passthrough mode or HUD mode. + +- [ ] **Step 4: Add placeholder README** + + Document scope, supported platform, install model, and safety notes. + +- [ ] **Step 5: Verify compile shape** + + Run: `npm run build` + Expected: TypeScript builds with stub files present. + +- [ ] **Step 6: Commit** + + ```bash + cd /Users/manpengan/pro/codex-hud + git add . + git commit -m "feat: scaffold codex hud project" + ``` + +## Task 2: Resolve and launch the real Codex binary safely + +**Files:** +- Create: `/Users/manpengan/pro/codex-hud/src/launcher/resolve-codex.ts` +- Create: `/Users/manpengan/pro/codex-hud/src/launcher/spawn-codex.ts` +- Modify: `/Users/manpengan/pro/codex-hud/src/cli.ts` + +- [ ] **Step 1: Implement real-binary resolution** + + Resolution order: + 1. explicit env override (`CODEX_REAL_BIN`) + 2. stored install manifest path + 3. fallback PATH search excluding wrapper path + +- [ ] **Step 2: Define failure behavior** + + If real Codex is not found, print a direct recovery message and exit non-zero. + +- [ ] **Step 3: Launch Codex through a PTY** + + Use a PTY-backed child so Codex keeps its normal interactive behavior. + +- [ ] **Step 4: Forward signals** + + Handle `SIGINT`, `SIGTERM`, and child exit propagation correctly. + +- [ ] **Step 5: Verify passthrough mode** + + Run: `node dist/cli.js --no-hud --help` + Expected: forwards to real `codex --help`. + +- [ ] **Step 6: Commit** + + ```bash + git add src/launcher src/cli.ts + git commit -m "feat: launch real codex via wrapper" + ``` + +## Task 3: Build terminal lifecycle management and fixed bottom HUD rendering + +**Files:** +- Create: `/Users/manpengan/pro/codex-hud/src/terminal/tty.ts` +- Create: `/Users/manpengan/pro/codex-hud/src/terminal/layout.ts` +- Create: `/Users/manpengan/pro/codex-hud/src/terminal/renderer.ts` +- Create: `/Users/manpengan/pro/codex-hud/src/types/hud.ts` + +- [ ] **Step 1: Define HUD state shape** + + Include model, cwd, git, elapsed, context, usage, tools, agents, todo, and display flags. + +- [ ] **Step 2: Implement dense layout** + + Four-line default: + 1. model | path | git | elapsed + 2. context | usage + 3. tools + 4. agents | todo + +- [ ] **Step 3: Implement compact fallback** + + Auto-switch when terminal height is too small. + +- [ ] **Step 4: Implement draw/erase cycle** + + Reserve bottom lines, redraw on state change, and restore cursor position cleanly. + +- [ ] **Step 5: Handle resize and exit cleanup** + + On resize, recompute visible HUD lines. On exit, clear HUD area and restore terminal modes. + +- [ ] **Step 6: Verify manual rendering** + + Run a local stub state loop and confirm no cursor corruption after exit. + +- [ ] **Step 7: Commit** + + ```bash + git add src/terminal src/types + git commit -m "feat: add terminal hud renderer" + ``` + +## Task 4: Implement stable state store and always-correct signals + +**Files:** +- Create: `/Users/manpengan/pro/codex-hud/src/state/store.ts` +- Create: `/Users/manpengan/pro/codex-hud/src/state/model.ts` +- Create: `/Users/manpengan/pro/codex-hud/src/state/git.ts` +- Create: `/Users/manpengan/pro/codex-hud/src/state/timer.ts` +- Create: `/Users/manpengan/pro/codex-hud/src/state/status-labels.ts` + +- [ ] **Step 1: Add store with subscribe/update semantics** + + State changes must be incremental and cheap to redraw. + +- [ ] **Step 2: Implement cwd + path shortening** + + Show project path with configurable depth, defaulting to a dense but readable path. + +- [ ] **Step 3: Implement git branch + dirty signal** + + Detect branch and working tree changes on an interval and on cwd changes. + +- [ ] **Step 4: Implement elapsed session timer** + + Start from wrapper launch, refresh at 1-second granularity. + +- [ ] **Step 5: Implement model source** + + Derive from argv/config when available; otherwise show `unknown`. + +- [ ] **Step 6: Verify baseline HUD data** + + Run wrapper against a real repo and confirm path/git/timer update correctly. + +- [ ] **Step 7: Commit** + + ```bash + git add src/state + git commit -m "feat: add base hud state sources" + ``` + +## Task 5: Implement activity extraction for tools, agents, and todo progress + +**Files:** +- Create: `/Users/manpengan/pro/codex-hud/src/state/tools.ts` +- Create: `/Users/manpengan/pro/codex-hud/src/state/agents.ts` +- Create: `/Users/manpengan/pro/codex-hud/src/state/todos.ts` +- Create: `/Users/manpengan/pro/codex-hud/src/observers/stdout-parser.ts` +- Create: `/Users/manpengan/pro/codex-hud/src/observers/process-tree.ts` + +- [ ] **Step 1: Define normalized activity events** + + Event types: + - tool-start / tool-end + - agent-start / agent-update / agent-end + - todo-progress + +- [ ] **Step 2: Parse visible Codex output conservatively** + + Only promote to structured activity when patterns are high-confidence. Ignore ambiguous text. + +- [ ] **Step 3: Track recent tool activity** + + Show short rolling summary with counts and active item. + +- [ ] **Step 4: Track agent activity** + + Show active worker name, duration, and simple state. + +- [ ] **Step 5: Track todo progress** + + Extract `n/m` style progress when visible; otherwise keep hidden or unknown. + +- [ ] **Step 6: Add staleness handling** + + Expire old activity after configurable time so the HUD does not freeze on ancient work. + +- [ ] **Step 7: Verify with recorded output samples** + + Build fixtures from representative Codex sessions and confirm parser output. + +- [ ] **Step 8: Commit** + + ```bash + git add src/state src/observers + git commit -m "feat: track tool agent and todo activity" + ``` + +## Task 6: Implement context and usage with native-first, estimate-second strategy + +**Files:** +- Create: `/Users/manpengan/pro/codex-hud/src/state/context.ts` +- Create: `/Users/manpengan/pro/codex-hud/src/observers/app-server.ts` +- Modify: `/Users/manpengan/pro/codex-hud/src/state/status-labels.ts` + +- [ ] **Step 1: Probe official signal sources** + + Inspect whether Codex app-server or readable session artifacts expose native context or usage values. + +- [ ] **Step 2: Define source precedence** + + 1. native + 2. derived + 3. estimated + 4. n/a + +- [ ] **Step 3: Implement context bar rendering** + + Show percentage with source label when known; otherwise `Context n/a`. + +- [ ] **Step 4: Implement usage display** + + Prefer native rate-limit or session usage data. If absent, mark as `estimated` or hide value. + +- [ ] **Step 5: Refuse false precision** + + Never display fake percentages without provenance. + +- [ ] **Step 6: Verify degradation** + + Test three cases: + - native values available + - only estimated values available + - nothing available + +- [ ] **Step 7: Commit** + + ```bash + git add src/state/context.ts src/observers/app-server.ts src/state/status-labels.ts + git commit -m "feat: add native-first context and usage indicators" + ``` + +## Task 7: Add installer, global wrapper swap, and rollback safety + +**Files:** +- Create: `/Users/manpengan/pro/codex-hud/src/install.ts` +- Create: `/Users/manpengan/pro/codex-hud/scripts/install-global.sh` +- Create: `/Users/manpengan/pro/codex-hud/scripts/uninstall-global.sh` +- Create: `/Users/manpengan/pro/codex-hud/scripts/doctor.sh` +- Create: `/Users/manpengan/pro/codex-hud/bin/codex-hud` +- Create: `/Users/manpengan/pro/codex-hud/bin/codex-hud-install` + +- [ ] **Step 1: Define install manifest** + + Persist: + - real Codex path + - wrapper path + - install timestamp + - version + +- [ ] **Step 2: Implement global install** + + Place wrapper in a user-controlled bin directory that is before the real Codex in PATH, or create a symlink strategy if safer. + +- [ ] **Step 3: Implement bypass** + + `codex --no-hud` must dispatch directly to the real binary. + +- [ ] **Step 4: Implement uninstall** + + Remove wrapper and restore original command behavior without manual cleanup. + +- [ ] **Step 5: Implement doctor** + + Verify: + - real binary reachable + - wrapper is first on PATH + - config writable + - PTY dependency available + +- [ ] **Step 6: Verify full install/uninstall cycle** + + Run install, open a fresh shell, check `which codex`, then uninstall and re-check. + +- [ ] **Step 7: Commit** + + ```bash + git add src/install.ts scripts bin + git commit -m "feat: add global install and rollback workflow" + ``` + +## Task 8: Add configuration, user overrides, and polished dense defaults + +**Files:** +- Create: `/Users/manpengan/pro/codex-hud/src/config.ts` +- Modify: `/Users/manpengan/pro/codex-hud/src/terminal/layout.ts` +- Modify: `/Users/manpengan/pro/codex-hud/README.md` + +- [ ] **Step 1: Add config file support** + + Store user config under `~/.codex-hud/config.json`. + +- [ ] **Step 2: Expose dense layout toggles** + + Allow hiding: + - git + - context + - usage + - tools + - agents + - todo + +- [ ] **Step 3: Add path depth and color settings** + + Keep defaults opinionated but overridable. + +- [ ] **Step 4: Add compact fallback rules** + + Compact mode must still preserve the most important signals first. + +- [ ] **Step 5: Update README with configuration examples** + + Show practical examples, not a full config dump. + +- [ ] **Step 6: Commit** + + ```bash + git add src/config.ts src/terminal/layout.ts README.md + git commit -m "feat: add hud configuration and dense defaults" + ``` + +## Task 9: Add verification fixtures and end-to-end smoke tests + +**Files:** +- Create: `/Users/manpengan/pro/codex-hud/tests/fixtures/` +- Create: `/Users/manpengan/pro/codex-hud/tests/stdout-parser.test.ts` +- Create: `/Users/manpengan/pro/codex-hud/tests/layout.test.ts` +- Create: `/Users/manpengan/pro/codex-hud/tests/install-smoke.sh` + +- [ ] **Step 1: Add parser fixtures** + + Capture representative Codex output for: + - tool activity + - agent activity + - todo progress + - unknown/noisy output + +- [ ] **Step 2: Add unit tests for parser normalization** + + Ensure high-confidence extraction only. + +- [ ] **Step 3: Add layout snapshot tests** + + Verify dense and compact string rendering from known state. + +- [ ] **Step 4: Add install smoke test** + + Non-destructive script that checks wrapper resolution and `--no-hud`. + +- [ ] **Step 5: Run full test suite** + + Run: `npm test` + Expected: all tests pass. + +- [ ] **Step 6: Commit** + + ```bash + git add tests + git commit -m "test: add codex hud parser and install smoke coverage" + ``` + +## Task 10: Final docs, release workflow, and manual verification + +**Files:** +- Modify: `/Users/manpengan/pro/codex-hud/README.md` +- Create: `/Users/manpengan/pro/codex-hud/docs/manual-test-checklist.md` + +- [ ] **Step 1: Document install and uninstall** + + Include: + - first install + - upgrade + - bypass + - uninstall + +- [ ] **Step 2: Document metric provenance** + + Explain which HUD fields are native, derived, estimated, or optional. + +- [ ] **Step 3: Create manual verification checklist** + + Cover: + - launch in repo and non-repo directories + - resize terminal + - Ctrl-C / exit cleanup + - agent-heavy session + - todo-heavy session + - `--no-hud` + - uninstall/reinstall + +- [ ] **Step 4: Run final verification** + + Run: + ```bash + npm run build + npm test + ./scripts/doctor.sh + ``` + Expected: build/test/doctor all succeed. + +- [ ] **Step 5: Commit** + + ```bash + git add README.md docs/manual-test-checklist.md + git commit -m "docs: add codex hud install and verification guide" + ``` + +## Notes for Execution + +- macOS + zsh is the primary target for the first pass. +- Do not switch to destructive binary replacement first; prefer a user-bin wrapper that wins by PATH order. +- If Codex exposes better structured signals during implementation, replace any heuristic parser that the official source makes unnecessary. +- If a signal cannot be made trustworthy, surface that explicitly instead of smoothing it over. + diff --git a/docs/superpowers/specs/2026-03-22-codex-hud-design.md b/docs/superpowers/specs/2026-03-22-codex-hud-design.md new file mode 100644 index 0000000..89f7973 --- /dev/null +++ b/docs/superpowers/specs/2026-03-22-codex-hud-design.md @@ -0,0 +1,354 @@ +# Codex HUD — Design Spec + +> Date: 2026-03-22 +> Status: Approved +> Author: manpengan + Claude + +--- + +## 1. Problem + +Codex CLI(OpenAI)运行时没有实时状态面板。用户看不到当前 session 的 token 消耗、tool 活动、task 进度等信息,只能在 TUI 内滚动查看零散输出。 + +claude-hud 为 Claude Code 解决了这个问题,但它依赖 Claude Code 的原生 statusline plugin API。Codex 没有这个 API。 + +## 2. Key Discovery + +Codex 在本地写入了结构化数据,足以支撑 HUD: + +| 数据源 | 路径 | 内容 | +|--------|------|------| +| SQLite | `~/.codex/state_5.sqlite` | threads, jobs, agent_jobs, agent_job_items, logs 等表 | +| Session JSONL | `~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl` | 结构化事件(session_meta, event_msg, response_item, task_started) | +| History | `~/.codex/history.jsonl` | 全局历史 | +| TUI log | `~/.codex/log/codex-tui.log` | TUI 日志 | + +其中 `threads` 表直接包含:`rollout_path`, `cwd`, `model`, `tokens_used`, `git_branch`, `updated_at`。 + +**结论:主数据源放在 SQLite + Session JSONL,不走 stdout 启发式解析。** + +## 3. Architecture + +### 3.1 Mode Selection + +启动时按优先级降序选择渲染模式: + +``` +codex-hud 启动 + | + 探测 pane 能力(不只看 $TMUX/$ZELLIJ 环境变量) + |-- tmux: `display-message` 验证 client/session 可操作 --> pane mode + |-- zellij: `action` 能力探测 --> pane mode + |-- 探测失败 --> inline mode (inject --no-alt-screen) + |-- inline 也失败(TTY 不可用 / 渲染初始化失败 / 被禁用)--> passthrough mode(纯透传,无 HUD) +``` + +探测逻辑: +- 有环境变量不代表可安全 split(可能是嵌套 session、只读 client、shell 残留) +- tmux: 执行 `tmux display-message -p '#{session_id}'` 验证可操作性 +- zellij: 执行对应 action 探测 +- 探测失败才降级 + +### 3.2 Pane Mode (preferred) + +``` ++--------------------------------------+ +| Codex full-screen TUI (main) | +| 原生体验,参数不做任何修改 | ++--------------------------------------+ +| claude-4o | ~/pro | main* | 2m | <-- HUD pane (4 lines) +| tokens 12,340 | Write 12s | 纯 SQLite 轮询 +| shell(3) read(7) write(2) | 无 PTY +| task 3/5 | rollout-2026-03-22 | ++--------------------------------------+ +``` + +- `tmux split-window -l 4` 开底部 pane +- HUD 作为 wrapper 内子模块运行在 pane 中(不是独立进程) +- 主 pane 启动真实 codex,参数原样透传 +- Codex 退出时自动关闭 HUD pane + +### 3.3 Inline Mode (fallback) + +``` +| Codex 滚动输出 (--no-alt-screen) | +| > Read file: src/index.ts | ++--------------------------------------+ +| claude-4o | ~/pro | main* | 2m | <-- ANSI scroll region +| tokens 12,340 | Write 12s | PTY wrapper 维护 +| shell(3) read(7) write(2) | +| task 3/5 | rollout-2026-03-22 | ++--------------------------------------+ +``` + +- PTY wrapper 注入 `--no-alt-screen`,预留底部 4 行 +- ANSI scroll region + cursor save/restore +- Codex 以滚动模式运行(牺牲全屏 TUI 体验) + +### 3.4 Passthrough Mode (last resort) + +- `child_process.spawn` 真实 codex,完全透传 +- wrapper 退出码跟随 codex +- 触发条件:不在 tmux/zellij + inline 注入失败/被显式禁用 + TTY 不可用 + 渲染初始化失败 + +## 4. Data Layer + +两种渲染模式共用同一套数据层。 + +### 4.1 Observers + +``` +sqlite observer -- 500-1000ms 低频轮询 --> 稳定状态 (model, tokens, branch, jobs) +jsonl observer -- fs.watch 唤醒 + offset 增量 tail + 低频轮询兜底 --> 实时事件 + | + state/store.ts + 收到事件 --> 立即重绘 + 空闲时 --> 1s timer 兜底刷新(elapsed 计时用) +``` + +**SQLite observer:** +- 每 500-1000ms 轮询 `~/.codex/state_5.sqlite` +- 读取最新 `threads` 行:model, cwd, git_branch, tokens_used, updated_at +- 可选读 `jobs`, `agent_jobs` 表 + +**JSONL observer:** +- `fs.watch` 只负责唤醒(macOS 会丢边缘事件) +- 真正读取靠"记住 offset 后增量 tail" +- 额外加低频轮询兜底(防止 fs.watch 丢事件) +- 事件标准化:`normalize(rawEvent) -> HudEvent | null` + - 不硬编码事件字段名,先 normalize 再处理 + - 无法识别的事件返回 null,静默跳过 + +### 4.2 HudState + +```typescript +type HudState = { + // from SQLite threads table + model: string | null + cwd: string + gitBranch: string | null + tokensUsedTotal: number | null // thread 累计 tokens_used,不是 context 窗口占用率 + sessionPath: string | null + + // from JSONL events (via normalize) + activeTool: { name: string; startedAt: number } | null + toolCounts: Record + taskProgress: { done: number; total: number } | null + + // from timer + elapsedSec: number + + // render meta + renderMode: RenderMode // "pane" | "inline" | "passthrough" + termWidth: number + termHeight: number +} + +type RenderMode = "pane" | "inline" | "passthrough" + +type HudEvent = + | { type: "task_progress"; done: number; total: number } + | { type: "tool_start"; tool: string } + | { type: "tool_end"; tool: string } + | { type: "session_meta"; model: string } + // ... normalize layer handles mapping raw event names to these +``` + +**语义约束:** +- `tokensUsedTotal` 是累计消耗值,不是 context window 占用百分比,不渲染成百分比条 +- `activeTool` 带时间戳,用于显示持续时长和超时回收 +- 无法可靠获取的字段显示 `n/a` 或 `~estimated` + +## 5. Components & File Structure + +``` +src/ + cli.ts # 入口:解析 argv,运行模式检测,路由 + + launcher/ + resolve-codex.ts # 找真实 codex binary + argv.ts # passthrough args, --no-hud, inline 模式注入 --no-alt-screen + pane.ts # tmux/zellij pane 生命周期 (open, resize, close) + + detect/ + tmux.ts # display-message 探测,返回 { capable, sessionId } + zellij.ts # action 探测,返回 { capable } + + observers/ + sqlite.ts # 轮询 state_5.sqlite,产出 SqliteSnapshot + jsonl.ts # tail 最新 session JSONL,产出 HudEvent stream + + state/ + store.ts # 合并 SqliteSnapshot + HudEvent -> HudState, emit change + timer.ts # session elapsed (wrapper 启动时间起算) + + terminal/ + pane-renderer.ts # pane 模式:clearScreen 循环,写入 stdout + inline-renderer.ts # inline 模式:ANSI scroll region + cursor save/restore + layout.ts # dense(4) / compact(3) 布局,输入 HudState 输出 string[] + tty.ts # resize 监听,cleanup (scroll region 还原, cursor 还原) + + types/ + hud.ts # HudState, HudEvent, SqliteSnapshot, RenderMode + + config.ts # ~/.codex-hud/config.json, defaults + install.ts # install / uninstall / doctor +``` + +**相比原计划删除的部分:** +- `observers/stdout-parser.ts` — 删除(不做 stdout 启发式解析) +- `observers/app-server.ts` — 删除 +- `observers/process-tree.ts` — 删除 +- `state/tools.ts` / `state/agents.ts` 启发式推断 — 删除 + +## 6. HUD Layout + +### 6.1 Dense Mode (4 lines, terminal height >= 24) + +``` +Line 1: {model} | {cwd_short} | {branch}{dirty} | {elapsed} +Line 2: tokens {tokensUsedTotal} | {activeTool.name} {duration} +Line 3: {toolCounts summary} +Line 4: task {done}/{total} | {sessionLabel} +``` + +### 6.2 Compact Mode (3 lines, terminal height < 24) + +``` +Line 1: {model} | {branch} | {elapsed} +Line 2: tokens {tokensUsedTotal} | {activeTool.name} +Line 3: {toolCounts compact} | task {done}/{total} +``` + +### 6.3 Width Degradation + +| Width | 丢弃 | +|-------|------| +| < 100 | sessionLabel | +| < 80 | toolDuration, dirty 标记 | +| < 60 | toolCounts,只保留 activeTool | +| < 40 | 只保留 model + elapsed | + +### 6.4 Field Provenance + +- 来自 SQLite / JSONL normalize(可信): 正常白色 +- 推断/估算: 暗色 + `~` 前缀 +- 不可用: `n/a`(暗色) + +## 7. Installation + +### 7.1 Install + +```bash +codex-hud install +``` + +执行: +1. 写 wrapper 到 `~/.codex-hud/bin/codex`(薄 shell 脚本 -> 固定绝对路径 `dist/cli.js`) +2. 在 shell rc 文件中用标记块前置 PATH: + ```bash + # >>> codex-hud >>> + export PATH="$HOME/.codex-hud/bin:$PATH" + # <<< codex-hud <<< + ``` +3. 写 manifest.json + +### 7.2 Manifest + +```json +{ + "realBin": "/usr/local/bin/codex", + "wrapperPath": "~/.codex-hud/bin/codex", + "installedAt": "2026-03-22T...", + "version": "0.1.0", + "shellRcFilesModified": ["~/.zshrc"], + "pathEntry": "$HOME/.codex-hud/bin" +} +``` + +### 7.3 Real Binary Resolution + +1. `CODEX_REAL_BIN` 环境变量(显式 override) +2. `manifest.json` 记录的路径 +3. PATH 中排除 `~/.codex-hud/bin` 后的第一个 `codex` + +### 7.4 Bypass + +```bash +codex --no-hud [args...] +``` + +直接 `execvp` 真实 binary,wrapper 进程替换,零开销。 + +### 7.5 Uninstall + +```bash +codex-hud uninstall +``` + +- 删除 `~/.codex-hud/bin/codex` +- 删除 shell rc 中的标记块 +- 删除 `manifest.json` +- 保留 `config.json`(用户数据) + +### 7.6 Doctor + +```bash +codex-hud doctor +``` + +检查: +- real binary 可达 +- wrapper 在 PATH 中优先 +- `codex --no-hud --help` 真实可执行 +- `~/.codex/state_5.sqlite` 可读 +- tmux / zellij 能力检测 +- config 可写 + +## 8. Error Handling & Degradation + +### 8.1 Observer Failure + +| 场景 | 行为 | +|------|------| +| SQLite 不可读 | tokensUsedTotal: null,显示 n/a,每 5s 重试 | +| 最新 JSONL 找不到 | toolCounts 清空,activeTool: null,不报错 | +| JSONL normalize 返回 null | 静默跳过,不更新 state | +| fs.watch 失败 | 降回纯轮询 (1s),不影响 HUD 显示 | + +### 8.2 Render Failure Cascade + +``` +pane 初始化失败 --> 自动降级到 inline mode +inline 初始化失败 --> 自动降级到 passthrough mode +任何模式崩溃 --> tty.ts cleanup --> 退出 +``` + +### 8.3 Process Exit Cleanup (mandatory, all paths) + +``` +1. inline mode: 还原 scroll region,清除 HUD 行,显示光标 +2. pane mode: 关闭 HUD pane +3. PTY: 等待子进程退出或超时 kill +4. 退出码跟随真实 codex +``` + +处理 `exit`, `SIGINT`, `SIGTERM`, `uncaughtException`。任何 crash 先跑 cleanup,再打印简短错误,以真实 codex 退出码退出。绝不让用户终端停在残留 scroll region 或隐藏光标状态。 + +## 9. Product Rules + +- pane 模式不修改传给 codex 的任何参数 +- inline 模式才注入 `--no-alt-screen` +- `--no-alt-screen` 不应自动强推,它是 fallback 而非平级体验 +- 无法可靠获取的指标显示 `n/a` 或 `~estimated`,绝不虚构精度 +- wrapper 必须支持 `codex --no-hud` 立即 bypass +- crash 后必须恢复 terminal 状态 +- install 必须可逆(one command uninstall) +- shell rc 修改使用标记块,安装幂等 + +## 10. Tech Stack + +- Node.js 20+ / TypeScript +- `node-pty` (inline mode PTY) +- `better-sqlite3` (SQLite 读取) +- macOS + zsh 为首选目标