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 <noreply@anthropic.com>
This commit is contained in:
502
docs/superpowers/plans/2026-03-21-codex-hud-global-wrapper.md
Normal file
502
docs/superpowers/plans/2026-03-21-codex-hud-global-wrapper.md
Normal file
@@ -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.
|
||||
|
||||
354
docs/superpowers/specs/2026-03-22-codex-hud-design.md
Normal file
354
docs/superpowers/specs/2026-03-22-codex-hud-design.md
Normal file
@@ -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<string, number>
|
||||
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 为首选目标
|
||||
Reference in New Issue
Block a user